#[cfg(test)]
mod test;
use crate::{
math::{Scalar, random_uniform},
mechanics::CurrentCoordinate,
physics::molecular::single_chain::{
Configuration, Ensemble, Inextensible, Isometric, Isotensional, Legendre, MonteCarlo,
SingleChain, SingleChainError, Thermodynamics,
},
};
use std::f64::consts::TAU;
#[derive(Clone, Debug)]
pub struct SquareWellFreelyJointedChain {
pub link_length: Scalar,
pub number_of_links: u8,
pub well_width: Scalar,
pub ensemble: Ensemble,
}
impl SingleChain for SquareWellFreelyJointedChain {
fn link_length(&self) -> Scalar {
self.link_length
}
fn number_of_links(&self) -> u8 {
self.number_of_links
}
}
impl Inextensible for SquareWellFreelyJointedChain {
fn maximum_nondimensional_extension(&self) -> Scalar {
1.0 + self.well_width / self.link_length
}
}
impl Thermodynamics for SquareWellFreelyJointedChain {
fn ensemble(&self) -> Ensemble {
self.ensemble
}
}
impl Isometric for SquareWellFreelyJointedChain {
fn nondimensional_helmholtz_free_energy(
&self,
_nondimensional_extension: Scalar,
) -> Result<Scalar, SingleChainError> {
unimplemented!()
}
fn nondimensional_force(
&self,
_nondimensional_extension: Scalar,
) -> Result<Scalar, SingleChainError> {
unimplemented!()
}
fn nondimensional_stiffness(
&self,
_nondimensional_extension: Scalar,
) -> Result<Scalar, SingleChainError> {
unimplemented!()
}
fn nondimensional_spherical_distribution(
&self,
_nondimensional_extension: Scalar,
) -> Result<Scalar, SingleChainError> {
unimplemented!()
}
}
impl Isotensional for SquareWellFreelyJointedChain {
fn nondimensional_gibbs_free_energy(
&self,
nondimensional_force: Scalar,
) -> Result<Scalar, SingleChainError> {
let varsigma = self.maximum_nondimensional_extension();
let varsigma_eta = varsigma * nondimensional_force;
Ok(self.number_of_links() as Scalar
* (nondimensional_force.powi(3)
/ (varsigma_eta * varsigma_eta.cosh()
- varsigma_eta.sinh()
- nondimensional_force * nondimensional_force.cosh()
+ nondimensional_force.sinh()))
.ln())
}
fn nondimensional_extension(
&self,
nondimensional_force: Scalar,
) -> Result<Scalar, SingleChainError> {
if nondimensional_force == 0.0 {
Ok(0.0)
} else {
let eta = nondimensional_force;
let eta_sinh = eta.sinh();
let varsigma = self.maximum_nondimensional_extension();
let varsigma_eta = varsigma * eta;
let varsigma_eta_sinh = varsigma_eta.sinh();
Ok(eta * (varsigma.powi(2) * varsigma_eta_sinh - eta_sinh)
/ (varsigma_eta * varsigma_eta.cosh() - varsigma_eta_sinh - eta * eta.cosh()
+ eta_sinh)
- 3.0 / eta)
}
}
fn nondimensional_compliance(
&self,
nondimensional_force: Scalar,
) -> Result<Scalar, SingleChainError> {
if nondimensional_force == 0.0 {
Ok(Scalar::NAN)
} else {
let eta = nondimensional_force;
let eta_sinh = eta.sinh();
let eta_cosh = eta.cosh();
let varsigma = self.maximum_nondimensional_extension();
let varsigma_eta = varsigma * nondimensional_force;
let varsigma_eta_sinh = varsigma_eta.sinh();
let varsigma_eta_cosh = varsigma_eta.cosh();
let a = eta * (varsigma * varsigma * varsigma_eta_sinh - eta_sinh);
let b =
varsigma_eta * varsigma_eta_cosh - varsigma_eta_sinh - eta * eta_cosh + eta_sinh;
let a_prime = (varsigma * varsigma * varsigma_eta_sinh - eta_sinh)
+ eta * (varsigma * varsigma * varsigma * varsigma_eta_cosh - eta_cosh);
Ok((a_prime * b - a * a) / (b * b) + 3.0 / (eta * eta))
}
}
}
impl Legendre for SquareWellFreelyJointedChain {
fn nondimensional_spherical_distribution(
&self,
_nondimensional_extension: Scalar,
) -> Result<Scalar, SingleChainError> {
unimplemented!()
}
}
impl MonteCarlo for SquareWellFreelyJointedChain {
fn random_nondimensional_link_vectors(&self, nondimensional_force: Scalar) -> Configuration {
let max_strain = self.maximum_nondimensional_extension() - 1.0;
(0..self.number_of_links())
.map(|_| {
let cos_theta = if nondimensional_force == 0.0 {
2.0 * random_uniform() - 1.0
} else {
todo!("Force biases the link stretch too.")
};
let sin_theta = (1.0 - cos_theta * cos_theta).sqrt();
let phi = TAU * random_uniform();
let (sin_phi, cos_phi) = phi.sin_cos();
let lambda = 1.0 + max_strain * random_uniform();
CurrentCoordinate::from([
lambda * sin_theta * cos_phi,
lambda * sin_theta * sin_phi,
lambda * cos_theta,
])
})
.collect()
}
}