conspire/physics/molecular/single_chain/frc/
mod.rs1#[cfg(test)]
2mod test;
3
4use crate::{
5 math::{Scalar, Tensor, TensorArray, random_uniform},
6 mechanics::CurrentCoordinate,
7 physics::molecular::single_chain::{
8 Configuration, Ensemble, Inextensible, MonteCarlo, SingleChain,
9 },
10};
11use std::f64::consts::TAU;
12
13#[derive(Clone, Debug)]
15pub struct FreelyRotatingChain {
16 pub link_angle: Scalar,
18 pub link_length: Scalar,
20 pub number_of_links: u8,
22 pub ensemble: Ensemble,
24}
25
26impl SingleChain for FreelyRotatingChain {
27 fn link_length(&self) -> Scalar {
28 self.link_length
29 }
30 fn number_of_links(&self) -> u8 {
31 self.number_of_links
32 }
33}
34
35impl Inextensible for FreelyRotatingChain {
36 fn maximum_nondimensional_extension(&self) -> Scalar {
37 1.0
38 }
39}
40
41impl MonteCarlo for FreelyRotatingChain {
42 fn random_configuration(&self) -> Configuration {
43 let cos_theta = 2.0 * random_uniform() - 1.0;
44 let sin_theta = (1.0 - cos_theta * cos_theta).sqrt();
45 let phi = TAU * random_uniform();
46 let (sin_phi, cos_phi) = phi.sin_cos();
47 const AY: CurrentCoordinate = CurrentCoordinate::const_from([0.0, 1.0, 0.0]);
48 const AZ: CurrentCoordinate = CurrentCoordinate::const_from([0.0, 0.0, 1.0]);
49 let mut a = AY;
50 let mut b =
51 CurrentCoordinate::const_from([sin_theta * cos_phi, sin_theta * sin_phi, cos_theta]);
52 let mut position = CurrentCoordinate::zero();
53 let (sin_theta, cos_theta) = self.link_angle.sin_cos();
54 (0..self.number_of_links())
55 .map(|link| {
56 if link > 0 {
57 a = if b[1].abs() < 0.9 { AY } else { AZ };
58 let u = a.cross(&b).normalized();
59 let v = b.cross(&u);
60 let phi = TAU * random_uniform();
61 let (sin_phi, cos_phi) = phi.sin_cos();
62 b = &b * cos_theta + (&u * cos_phi + &v * sin_phi) * sin_theta;
63 }
64 position += &b;
65 position.clone()
66 })
67 .collect()
68 }
69}