conspire/physics/molecular/single_chain/efrc/
mod.rs1#[cfg(test)]
2mod test;
3
4use crate::{
5 math::{Scalar, Tensor, TensorArray, random_uniform, random_x2_normal},
6 mechanics::CurrentCoordinate,
7 physics::{
8 BOLTZMANN_CONSTANT,
9 molecular::single_chain::{
10 Configuration, Ensemble, Extensible, Isometric, Isotensional, Legendre, MonteCarlo,
11 SingleChain, SingleChainError, Thermodynamics,
12 },
13 },
14};
15use std::f64::consts::TAU;
16
17#[derive(Clone, Debug)]
19pub struct ExtensibleFreelyRotatingChain {
20 pub link_angle: Scalar,
22 pub link_length: Scalar,
24 pub link_stiffness: Scalar,
26 pub number_of_links: u8,
28 pub ensemble: Ensemble,
30}
31
32impl ExtensibleFreelyRotatingChain {
33 fn nondimensional_link_stiffness(&self) -> Scalar {
34 self.link_stiffness * self.link_length().powi(2) / BOLTZMANN_CONSTANT / self.temperature()
35 }
36}
37
38impl SingleChain for ExtensibleFreelyRotatingChain {
39 fn link_length(&self) -> Scalar {
40 self.link_length
41 }
42 fn number_of_links(&self) -> u8 {
43 self.number_of_links
44 }
45}
46
47impl Extensible for ExtensibleFreelyRotatingChain {}
48
49impl Thermodynamics for ExtensibleFreelyRotatingChain {
50 fn ensemble(&self) -> Ensemble {
51 self.ensemble
52 }
53}
54
55impl Isometric for ExtensibleFreelyRotatingChain {
56 fn nondimensional_helmholtz_free_energy(
57 &self,
58 _nondimensional_extension: Scalar,
59 ) -> Result<Scalar, SingleChainError> {
60 unimplemented!()
61 }
62 fn nondimensional_force(
63 &self,
64 _nondimensional_extension: Scalar,
65 ) -> Result<Scalar, SingleChainError> {
66 unimplemented!()
67 }
68 fn nondimensional_stiffness(
69 &self,
70 _nondimensional_extension: Scalar,
71 ) -> Result<Scalar, SingleChainError> {
72 unimplemented!()
73 }
74 fn nondimensional_spherical_distribution(
75 &self,
76 _nondimensional_extension: Scalar,
77 ) -> Result<Scalar, SingleChainError> {
78 unimplemented!()
79 }
80}
81
82impl Isotensional for ExtensibleFreelyRotatingChain {
83 fn nondimensional_gibbs_free_energy_per_link(
84 &self,
85 _nondimensional_force: Scalar,
86 ) -> Result<Scalar, SingleChainError> {
87 unimplemented!()
88 }
89 fn nondimensional_extension(
90 &self,
91 _nondimensional_force: Scalar,
92 ) -> Result<Scalar, SingleChainError> {
93 unimplemented!()
94 }
95 fn nondimensional_compliance(
96 &self,
97 _nondimensional_force: Scalar,
98 ) -> Result<Scalar, SingleChainError> {
99 unimplemented!()
100 }
101}
102
103impl Legendre for ExtensibleFreelyRotatingChain {
104 fn nondimensional_spherical_distribution(
105 &self,
106 _nondimensional_extension: Scalar,
107 ) -> Result<Scalar, SingleChainError> {
108 unimplemented!()
109 }
110}
111
112impl MonteCarlo for ExtensibleFreelyRotatingChain {
113 fn random_configuration(&self) -> Configuration {
114 let std = 1.0 / self.nondimensional_link_stiffness().sqrt();
115 let cos_theta = 2.0 * random_uniform() - 1.0;
116 let sin_theta = (1.0 - cos_theta * cos_theta).sqrt();
117 let phi = TAU * random_uniform();
118 let (sin_phi, cos_phi) = phi.sin_cos();
119 const AY: CurrentCoordinate = CurrentCoordinate::const_from([0.0, 1.0, 0.0]);
120 const AZ: CurrentCoordinate = CurrentCoordinate::const_from([0.0, 0.0, 1.0]);
121 let mut a = AY;
122 let mut b =
123 CurrentCoordinate::const_from([sin_theta * cos_phi, sin_theta * sin_phi, cos_theta]);
124 let mut position = CurrentCoordinate::zero();
125 let (sin_theta, cos_theta) = self.link_angle.sin_cos();
126 (0..self.number_of_links())
127 .map(|link| {
128 if link > 0 {
129 a = if b[1].abs() < 0.9 { AY } else { AZ };
130 let u = a.cross(&b).normalized();
131 let v = b.cross(&u);
132 let phi = TAU * random_uniform();
133 let (sin_phi, cos_phi) = phi.sin_cos();
134 b = &b * cos_theta + (&u * cos_phi + &v * sin_phi) * sin_theta;
135 }
136 position += &b * random_x2_normal(1.0, std);
137 position.clone()
138 })
139 .collect()
140 }
141}