diff --git a/Cargo.toml b/Cargo.toml index 51f08011..860d2c22 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polymers" -version = "0.3.4" +version = "0.3.5" edition = "2021" description = "Polymers Modeling Library" license = "BSD-3-Clause" diff --git a/Project.toml b/Project.toml index dec6f096..33c5cf09 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Polymers" uuid = "8aef037c-a721-4e8a-9d81-eb7093daef2c" authors = ["mrbuche "] -version = "0.3.4" +version = "0.3.5" [deps] DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" diff --git a/README.md b/README.md index 53225238..05d06624 100644 --- a/README.md +++ b/README.md @@ -17,16 +17,6 @@ The library can be installed as a Python package: pip install polymers ``` -If Rust is installed, the latest edition of the library can be installed from the GitHub repository: - -```shell -git clone git@github.com:sandialabs/Polymers.git -cd Polymers/ -pip install maturin -maturin build --features python -pip install target/wheels/*.whl -``` - ## Julia [![docs (stable)](https://raw.githubusercontent.com/sandialabs/Polymers/main/pages/assets/images/julia-docs-stable.svg)](https://sandialabs.github.io/Polymers/julia/docs/stable) @@ -40,30 +30,16 @@ using Pkg Pkg.add("Polymers") ``` -If Rust is installed, the latest edition of the library can be installed from the GitHub repository: - -```julia -using Pkg -Pkg.add(url="https://github.com/sandialabs/Polymers") -``` - ## Rust [![docs (stable)](https://img.shields.io/badge/Docs-stable-e57300?logo=rust&logoColor=000000)](https://docs.rs/crate/polymers) [![docs (latest)](https://img.shields.io/badge/Docs-latest-e57300?logo=rust&logoColor=000000)](https://sandialabs.github.io/Polymers/rust/docs/latest) [![crates](https://img.shields.io/crates/v/polymers?logo=rust&logoColor=000000&label=Crates&color=32592f)](https://crates.io/crates/polymers) -The library can be used in an existing Rust project by adding the `polymers` crate to Cargo.toml: +The library can be added to an existing Rust project: -```toml -[dependencies] -polymers = "*" -``` -To use the latest edition of the library, add the GitHub repository to Cargo.toml: - -```toml -[dependencies] -regex = { git = "https://github.com/sandialabs/polymers" } +```shell +cargo add polymers ``` ## Citation diff --git a/docs/source/index.rst b/docs/source/index.rst index 439274f6..bfb00d55 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -16,16 +16,6 @@ The library can be installed as a Python package: pip install polymers -If Rust is installed, the latest edition of the library can be installed from the GitHub repository: - -.. code-block:: sh - - git clone git@github.com:sandialabs/Polymers.git - cd Polymers/ - pip install maturin - maturin build --features python - pip install target/wheels/*.whl - Citation -------- diff --git a/docs/source/physics/single_chain/wlc/thermodynamics.rst b/docs/source/physics/single_chain/wlc/thermodynamics.rst index ab1ee11a..e320d1c3 100644 --- a/docs/source/physics/single_chain/wlc/thermodynamics.rst +++ b/docs/source/physics/single_chain/wlc/thermodynamics.rst @@ -5,6 +5,7 @@ WLC model thermodynamics :maxdepth: 1 Isometric + Isometric .. autoclass:: polymers.physics.single_chain.wlc.thermodynamics::WLC(number_of_links, link_length, hinge_mass, persistance_length) @@ -13,3 +14,4 @@ WLC model thermodynamics .. autoattribute:: hinge_mass .. autoattribute:: persistance_length .. autoattribute:: isometric + .. autoattribute:: isotensional diff --git a/docs/source/physics/single_chain/wlc/thermodynamics/isotensional.rst b/docs/source/physics/single_chain/wlc/thermodynamics/isotensional.rst new file mode 100644 index 00000000..c38a6710 --- /dev/null +++ b/docs/source/physics/single_chain/wlc/thermodynamics/isotensional.rst @@ -0,0 +1,34 @@ +WLC model thermodynamics (isotensional) +======================================= + +.. toctree:: + :maxdepth: 1 + + Legendre + +.. autoclass:: polymers.physics.single_chain.wlc.thermodynamics.isotensional::WLC(number_of_links, link_length, hinge_mass, persistance_length) + + .. autoattribute:: number_of_links + .. autoattribute:: link_length + .. autoattribute:: hinge_mass + .. autoattribute:: persistance_length + .. autoattribute:: legendre + .. automethod:: end_to_end_length(force, temperature) + .. automethod:: end_to_end_length_per_link(force, temperature) + .. automethod:: nondimensional_end_to_end_length(nondimensional_force) + .. automethod:: nondimensional_end_to_end_length_per_link(nondimensional_force) + .. automethod:: gibbs_free_energy(force, temperature) + .. automethod:: gibbs_free_energy_per_link(force, temperature) + .. automethod:: relative_gibbs_free_energy(force, temperature) + .. automethod:: relative_gibbs_free_energy_per_link(force, temperature) + .. automethod:: nondimensional_gibbs_free_energy(nondimensional_force, temperature) + .. automethod:: nondimensional_gibbs_free_energy_per_link(nondimensional_force, temperature) + .. automethod:: nondimensional_relative_gibbs_free_energy(nondimensional_force) + .. automethod:: nondimensional_relative_gibbs_free_energy_per_link(nondimensional_force) + +.. raw:: + html + +
+ +.. footbibliography:: diff --git a/docs/source/physics/single_chain/wlc/thermodynamics/isotensional/legendre.rst b/docs/source/physics/single_chain/wlc/thermodynamics/isotensional/legendre.rst new file mode 100644 index 00000000..8171fd96 --- /dev/null +++ b/docs/source/physics/single_chain/wlc/thermodynamics/isotensional/legendre.rst @@ -0,0 +1,17 @@ +WLC model thermodynamics (isotensional/legendre) +================================================ + +.. autoclass:: polymers.physics.single_chain.wlc.thermodynamics.isotensional.legendre::WLC(number_of_links, link_length, hinge_mass, persistance_length) + + .. autoattribute:: number_of_links + .. autoattribute:: link_length + .. autoattribute:: hinge_mass + .. autoattribute:: persistance_length + .. automethod:: helmholtz_free_energy(force, temperature) + .. automethod:: helmholtz_free_energy_per_link(force, temperature) + .. automethod:: relative_helmholtz_free_energy(force, temperature) + .. automethod:: relative_helmholtz_free_energy_per_link(force, temperature) + .. automethod:: nondimensional_helmholtz_free_energy(nondimensional_force, temperature) + .. automethod:: nondimensional_helmholtz_free_energy_per_link(nondimensional_force, temperature) + .. automethod:: nondimensional_relative_helmholtz_free_energy(nondimensional_force) + .. automethod:: nondimensional_relative_helmholtz_free_energy_per_link(nondimensional_force) diff --git a/docs/src/index.md b/docs/src/index.md index cbaa48a5..2cb3ba42 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -16,13 +16,6 @@ using Pkg Pkg.add("Polymers") ``` -If Rust is installed, the latest edition of the library can be installed from the GitHub repository: - -```julia -using Pkg -Pkg.add(url="https://github.com/sandialabs/Polymers") -``` - ## Citation [![doi](https://img.shields.io/badge/Zenodo-10.5281%2Fzenodo.7041983-blue)](https://doi.org/10.5281/zenodo.7041983) diff --git a/docs/src/physics/single_chain/wlc.md b/docs/src/physics/single_chain/wlc.md index 03707932..d9101bc1 100644 --- a/docs/src/physics/single_chain/wlc.md +++ b/docs/src/physics/single_chain/wlc.md @@ -3,5 +3,5 @@ * [WLC model thermodynamics](../../thermodynamics) ```@autodocs -Modules = [Polymers.Physics.SingleChain.WLC] +Modules = [Polymers.Physics.SingleChain.Wlc] ``` diff --git a/docs/src/physics/single_chain/wlc/thermodynamics.md b/docs/src/physics/single_chain/wlc/thermodynamics.md index 36e7b4ae..c2ad1a4d 100644 --- a/docs/src/physics/single_chain/wlc/thermodynamics.md +++ b/docs/src/physics/single_chain/wlc/thermodynamics.md @@ -1,7 +1,8 @@ # WLC model thermodynamics * [WLC model thermodynamics (isometric)](../../../isometric) + * [WLC model thermodynamics (isotensional)](../../../isotensional) ```@autodocs -Modules = [Polymers.Physics.SingleChain.WLC.Thermodynamics] +Modules = [Polymers.Physics.SingleChain.Wlc.Thermodynamics] ``` diff --git a/docs/src/physics/single_chain/wlc/thermodynamics/isometric.md b/docs/src/physics/single_chain/wlc/thermodynamics/isometric.md index f2be6e4d..637b719a 100644 --- a/docs/src/physics/single_chain/wlc/thermodynamics/isometric.md +++ b/docs/src/physics/single_chain/wlc/thermodynamics/isometric.md @@ -3,5 +3,5 @@ * [WLC model thermodynamics (isometric/legendre)](../../../../legendre) ```@autodocs -Modules = [Polymers.Physics.SingleChain.WLC.Thermodynamics.Isometric] +Modules = [Polymers.Physics.SingleChain.Wlc.Thermodynamics.Isometric] ``` diff --git a/docs/src/physics/single_chain/wlc/thermodynamics/isometric/legendre.md b/docs/src/physics/single_chain/wlc/thermodynamics/isometric/legendre.md index df84b9ce..e6c3ef1c 100644 --- a/docs/src/physics/single_chain/wlc/thermodynamics/isometric/legendre.md +++ b/docs/src/physics/single_chain/wlc/thermodynamics/isometric/legendre.md @@ -1,5 +1,5 @@ # WLC model thermodynamics (isometric/legendre) ```@autodocs -Modules = [Polymers.Physics.SingleChain.WLC.Thermodynamics.Isometric.Legendre] +Modules = [Polymers.Physics.SingleChain.Wlc.Thermodynamics.Isometric.Legendre] ``` diff --git a/docs/src/physics/single_chain/wlc/thermodynamics/isotensional.md b/docs/src/physics/single_chain/wlc/thermodynamics/isotensional.md new file mode 100644 index 00000000..5ef8f83e --- /dev/null +++ b/docs/src/physics/single_chain/wlc/thermodynamics/isotensional.md @@ -0,0 +1,7 @@ +# WLC model thermodynamics (isotensional) + + * [WLC model thermodynamics (isotensional/legendre)](../../../../legendre) + +```@autodocs +Modules = [Polymers.Physics.SingleChain.Wlc.Thermodynamics.Isotensional] +``` diff --git a/docs/src/physics/single_chain/wlc/thermodynamics/isotensional/legendre.md b/docs/src/physics/single_chain/wlc/thermodynamics/isotensional/legendre.md new file mode 100644 index 00000000..b7472781 --- /dev/null +++ b/docs/src/physics/single_chain/wlc/thermodynamics/isotensional/legendre.md @@ -0,0 +1,5 @@ +# WLC model thermodynamics (isotensional/legendre) + +```@autodocs +Modules = [Polymers.Physics.SingleChain.Wlc.Thermodynamics.Isotensional.Legendre] +``` diff --git a/src/physics/single_chain/mod.jl b/src/physics/single_chain/mod.jl index 86beee54..d1349eba 100644 --- a/src/physics/single_chain/mod.jl +++ b/src/physics/single_chain/mod.jl @@ -36,6 +36,7 @@ struct Parameters link_stiffness_scale::Float64 link_energy_reference::Float64 link_energy_scale::Float64 + nondimensional_persistance_length_small::Float64 nondimensional_link_stiffness_large::Float64 nondimensional_link_stiffness_big::Float64 nondimensional_link_stiffness_medium::Float64 @@ -80,6 +81,7 @@ parameters = Parameters( 99e4, 5e4, 99e3, + 2e-2, 1e4, 1e3, 1e1, diff --git a/src/physics/single_chain/test.py b/src/physics/single_chain/test.py index 415c522b..24d62bc2 100644 --- a/src/physics/single_chain/test.py +++ b/src/physics/single_chain/test.py @@ -39,6 +39,7 @@ def __init__(self): self.link_stiffness_scale = 99e4 self.link_energy_reference = 5e4 self.link_energy_scale = 99e3 + self.nondimensional_persistance_length_small = 2e-2 self.nondimensional_link_stiffness_large = 1e4 self.nondimensional_link_stiffness_big = 1e3 self.nondimensional_link_stiffness_medium = 1e1 diff --git a/src/physics/single_chain/test.rs b/src/physics/single_chain/test.rs index dd02f2bd..31ac9b9c 100644 --- a/src/physics/single_chain/test.rs +++ b/src/physics/single_chain/test.rs @@ -26,6 +26,7 @@ pub struct Parameters pub link_stiffness_scale: f64, pub link_energy_reference: f64, pub link_energy_scale: f64, + pub nondimensional_persistance_length_small: f64, pub nondimensional_link_stiffness_large: f64, pub nondimensional_link_stiffness_big: f64, pub nondimensional_link_stiffness_medium: f64, @@ -74,6 +75,7 @@ impl Default for Parameters link_stiffness_scale: 99e4, link_energy_reference: 5e4, link_energy_scale: 99e3, + nondimensional_persistance_length_small: 2e-2, nondimensional_link_stiffness_large: 1e4, nondimensional_link_stiffness_big: 1e3, nondimensional_link_stiffness_medium: 1e1, diff --git a/src/physics/single_chain/wlc/thermodynamics/isometric/legendre/mod.jl b/src/physics/single_chain/wlc/thermodynamics/isometric/legendre/mod.jl index 5eb53628..6e5fe9e4 100644 --- a/src/physics/single_chain/wlc/thermodynamics/isometric/legendre/mod.jl +++ b/src/physics/single_chain/wlc/thermodynamics/isometric/legendre/mod.jl @@ -64,7 +64,7 @@ end """ The Gibbs free energy ``\\varphi`` as a function of the applied end-to-end length ``\\xi`` and temperature ``T``, -parameterized by the number of links ``N_b``, link length ``\\ell_b``, and hinge mass ``m``, +parameterized by the number of links ``N_b``, link length ``\\ell_b``, hinge mass ``m``, and persistance length ``\\ell_p``, ```math \\varphi(\\xi, T) \\sim \\psi(\\xi, T) - \\xi f(\\xi, T) \\quad \\text{for } N_b\\gg 1, @@ -115,7 +115,7 @@ end """ The Gibbs free energy per link ``\\varphi/N_b`` as a function of the applied end-to-end length ``\\xi`` and temperature ``T``, -parameterized by the number of links ``N_b``, link length ``\\ell_b``, and hinge mass ``m``. +parameterized by the number of links ``N_b``, link length ``\\ell_b``, hinge mass ``m``, and persistance length ``\\ell_p``. $(TYPEDSIGNATURES) """ @@ -160,7 +160,7 @@ end """ The relative Helmholtz free energy ``\\Delta\\varphi\\equiv\\varphi(\\xi,T)-\\varphi(0,T)`` as a function of the applied end-to-end length ``\\xi`` and temperature ``T``, -parameterized by the number of links ``N_b`` and link length ``\\ell_b``. +parameterized by the number of links ``N_b``, link length ``\\ell_b``, and persistance length ``\\ell_p``. $(TYPEDSIGNATURES) """ @@ -201,7 +201,7 @@ end """ The relative Gibbs free energy per link ``\\Delta\\varphi/N_b`` as a function of the applied end-to-end length ``\\xi`` and temperature ``T``, -parameterized by the number of links ``N_b`` and link length ``\\ell_b``. +parameterized by the number of links ``N_b``, link length ``\\ell_b``, and persistance length ``\\ell_p``. $(TYPEDSIGNATURES) """ @@ -242,7 +242,7 @@ end """ The nondimensional Gibbs free energy ``N_b\\varrho=\\beta\\varphi`` as a function of the applied nondimensional end-to-end length per link ``\\gamma`` and temperature ``T``, -parameterized by the number of links ``N_b``, link length ``\\ell_b``, and hinge mass ``m``. +parameterized by the number of links ``N_b``, link length ``\\ell_b``, hinge mass ``m``, and nondimensional persistance length ``\\zeta``. $(TYPEDSIGNATURES) """ @@ -287,7 +287,7 @@ end """ The nondimensional Gibbs free energy per link ``\\varrho\\equiv\\beta\\varphi/N_b`` as a function of the applied nondimensional end-to-end length per link ``\\gamma`` and temperature ``T``, -parameterized by the number of links ``N_b``, link length ``\\ell_b``, and hinge mass ``m``. +parameterized by the number of links ``N_b``, link length ``\\ell_b``, hinge mass ``m``, and nondimensional persistance length ``\\zeta``. $(TYPEDSIGNATURES) """ @@ -332,7 +332,7 @@ end """ The nondimensional relative Gibbs free energy ``N_b\\Delta\\varrho=\\beta\\Delta\\varphi`` as a function of the applied nondimensional end-to-end length per link ``\\gamma`` and temperature ``T``, -parameterized by the number of links ``N_b``. +parameterized by the number of links ``N_b`` and nondimensional persistance length ``\\zeta``. $(TYPEDSIGNATURES) """ @@ -365,7 +365,7 @@ end """ The nondimensional relative Helmholtz free energy per link ``\\Delta\\varrho\\equiv\\beta\\Delta\\varphi/N_b`` as a function of the applied nondimensional end-to-end length per link ``\\gamma`` and temperature ``T``, -parameterized by the number of links ``N_b``. +parameterized by the number of links ``N_b`` and nondimensional persistance length ``\\zeta``. $(TYPEDSIGNATURES) """ diff --git a/src/physics/single_chain/wlc/thermodynamics/isometric/mod.jl b/src/physics/single_chain/wlc/thermodynamics/isometric/mod.jl index 48eb64da..67677a70 100644 --- a/src/physics/single_chain/wlc/thermodynamics/isometric/mod.jl +++ b/src/physics/single_chain/wlc/thermodynamics/isometric/mod.jl @@ -97,7 +97,7 @@ end """ The expected force ``f`` as a function of the applied end-to-end length ``\\xi`` and temperature ``T``, -parameterized by the number of links ``N_b`` and link length ``\\ell_b``, +parameterized by the number of links ``N_b``, link length ``\\ell_b``, and persistance length ``\\ell_p``, ```math f(\\xi, T) = \\frac{\\partial \\psi}{\\partial\\xi}. @@ -142,7 +142,7 @@ end """ The expected nondimensional force ``\\eta`` as a function of the applied nondimensional end-to-end length per link ``\\gamma``, -parameterized by the number of links ``N_b``, +parameterized by the number of links ``N_b`` and nondimensional persistance length ``\\zeta``, ```math \\eta(\\gamma) = \\frac{\\partial\\vartheta}{\\partial\\gamma}. @@ -179,7 +179,7 @@ end """ The Helmholtz free energy ``\\psi`` as a function of the applied end-to-end length ``\\xi`` and temperature ``T``, -parameterized by the number of links ``N_b``, link length ``\\ell_b``, and hinge mass ``m``, +parameterized by the number of links ``N_b``, link length ``\\ell_b``, hinge mass ``m``, and persistance length ``\\ell_p``, ```math \\psi(\\xi, T) = -kT\\ln Q(\\xi, T). @@ -228,7 +228,7 @@ end """ The Helmholtz free energy per link ``\\psi/N_b`` as a function of the applied end-to-end length ``\\xi`` and temperature ``T``, -parameterized by the number of links ``N_b``, link length ``\\ell_b``, and hinge mass ``m``. +parameterized by the number of links ``N_b``, link length ``\\ell_b``, hinge mass ``m``, and persistance length ``\\ell_p``. $(TYPEDSIGNATURES) """ @@ -273,7 +273,7 @@ end """ The relative Helmholtz free energy ``\\Delta\\psi\\equiv\\psi(\\xi,T)-\\psi(0,T)`` as a function of the applied end-to-end length ``\\xi`` and temperature ``T``, -parameterized by the number of links ``N_b`` and link length ``\\ell_b``, +parameterized by the number of links ``N_b`` link length ``\\ell_b``, and persistance length ``\\ell_p``, ```math \\Delta\\psi(\\xi, T) = kT\\ln\\left[\\frac{P_\\mathrm{eq}(0)}{P_\\mathrm{eq}(\\xi)}\\right]. @@ -318,7 +318,7 @@ end """ The relative Helmholtz free energy per link ``\\Delta\\psi/N_b`` as a function of the applied end-to-end length ``\\xi`` and temperature ``T``, -parameterized by the number of links ``N_b`` and link length ``\\ell_b``. +parameterized by the number of links ``N_b`` link length ``\\ell_b``, and persistance length ``\\ell_p``. $(TYPEDSIGNATURES) """ @@ -359,7 +359,7 @@ end """ The nondimensional Helmholtz free energy ``N_b\\vartheta=\\beta\\psi`` as a function of the applied nondimensional end-to-end length per link ``\\gamma`` and temperature ``T``, -parameterized by the number of links ``N_b``, link length ``\\ell_b``, and hinge mass ``m``. +parameterized by the number of links ``N_b``, link length ``\\ell_b``, hinge mass ``m``, and nondimensional persistance length ``\\zeta``. $(TYPEDSIGNATURES) """ @@ -404,7 +404,7 @@ end """ The nondimensional Helmholtz free energy per link ``\\vartheta\\equiv\\beta\\psi/N_b`` as a function of the applied nondimensional end-to-end length per link ``\\gamma`` and temperature ``T``, -parameterized by the number of links ``N_b``, link length ``\\ell_b``, and hinge mass ``m``. +parameterized by the number of links ``N_b``, link length ``\\ell_b``, hinge mass ``m``, and nondimensional persistance length ``\\zeta``. $(TYPEDSIGNATURES) """ @@ -449,7 +449,7 @@ end """ The nondimensional relative Helmholtz free energy ``N_b\\Delta\\vartheta=\\beta\\Delta\\psi`` as a function of the applied nondimensional end-to-end length per link ``\\gamma``, -parameterized by the number of links ``N_b``, +parameterized by the nondimensional persistance length ``\\zeta``, ```math \\beta\\Delta\\psi(\\gamma) = \\ln\\left[\\frac{\\mathscr{P}_\\mathrm{eq}(0)}{\\mathscr{P}_\\mathrm{eq}(\\gamma)}\\right]. @@ -482,7 +482,7 @@ end """ The nondimensional relative Helmholtz free energy per link ``\\Delta\\vartheta\\equiv\\beta\\Delta\\psi/N_b`` as a function of the applied nondimensional end-to-end length per link ``\\gamma``, -parameterized by the number of links ``N_b``, +parameterized by the number of links ``N_b`` and nondimensional persistance length ``\\zeta``, ```math \\Delta\\vartheta(\\gamma) = \\ln\\left[\\frac{\\mathscr{P}_\\mathrm{eq}(0)}{\\mathscr{P}_\\mathrm{eq}(\\gamma)}\\right]^{1/N_b}. @@ -519,7 +519,7 @@ end """ The equilibrium probability density of end-to-end vectors ``P_\\mathrm{eq}`` as a function of the end-to-end length ``\\xi``, -parameterized by the number of links ``N_b`` and link length ``\\ell_b``, +parameterized by the number of links ``N_b``, link length ``\\ell_b``, and persistance length ``\\ell_p``, ```math P_\\mathrm{eq}(\\xi) = \\frac{e^{-\\beta\\psi(\\xi, T)}}{4\\pi\\int e^{-\\beta\\psi(\\xi', T)} \\,{\\xi'}{}^2 d\\xi'}, @@ -558,7 +558,7 @@ end """ The nondimensional equilibrium probability density of nondimensional end-to-end vectors per link ``\\mathscr{P}_\\mathrm{eq}`` as a function of the nondimensional end-to-end length per link ``\\gamma``, -parameterized by the number of links ``N_b``, +parameterized by the number of links ``N_b`` and nondimensional persistance length ``\\zeta``, ```math \\mathscr{P}_\\mathrm{eq}(\\gamma) = \\frac{e^{-\\Delta\\vartheta(\\gamma)}}{4\\pi\\int e^{-\\Delta\\vartheta(\\gamma')} \\,{\\gamma'}{}^2 d\\gamma'}. @@ -597,7 +597,7 @@ end """ The equilibrium probability density of end-to-end lengths ``g_\\mathrm{eq}`` as a function of the end-to-end length ``\\xi``, -parameterized by the number of links ``N_b`` and link length ``\\ell_b``, +parameterized by the number of links ``N_b``, link length ``\\ell_b``, and persistance length ``\\ell_p``, ```math g_\\mathrm{eq}(\\xi) = 4\\pi\\xi^2 P_\\mathrm{eq}(\\xi). @@ -636,7 +636,7 @@ end """ The nondimensional equilibrium probability density of nondimensional end-to-end lenghts per link ``\\mathscr{g}_\\mathrm{eq}`` as a function of the nondimensional end-to-end length per link ``\\gamma``, -parameterized by the number of links ``N_b``, +parameterized by the number of links ``N_b`` and nondimensional persistance length ``\\zeta``, ```math \\mathscr{g}_\\mathrm{eq}(\\gamma) = 4\\pi\\gamma^2 \\mathscr{P}_\\mathrm{eq}(\\gamma). diff --git a/src/physics/single_chain/wlc/thermodynamics/isometric/mod.rs b/src/physics/single_chain/wlc/thermodynamics/isometric/mod.rs index 38cd517e..72dd95c3 100644 --- a/src/physics/single_chain/wlc/thermodynamics/isometric/mod.rs +++ b/src/physics/single_chain/wlc/thermodynamics/isometric/mod.rs @@ -116,10 +116,33 @@ pub fn relative_helmholtz_free_energy_per_link(number_of_links: &u8, link_length pub fn nondimensional_helmholtz_free_energy(number_of_links: &u8, link_length: &f64, hinge_mass: &f64, nondimensional_persistance_length: &f64, nondimensional_end_to_end_length_per_link: &f64, temperature: &f64) -> f64 { // - // not exactly correct unless P_eq is already normalized (see the &1.0) + // Note: not exactly correct unless P_eq is already normalized. // - let contour_length = (*number_of_links as f64)*link_length; - -(equilibrium_distribution(number_of_links, link_length, &(contour_length*nondimensional_persistance_length), &1.0, &(contour_length*nondimensional_end_to_end_length_per_link))).ln() - ((*number_of_links as f64) - 1.0)*(4.0*(-1.0/nondimensional_persistance_length).exp().acos().sin()*PI.powi(2)*hinge_mass*link_length.powi(2)*BOLTZMANN_CONSTANT*temperature/PLANCK_CONSTANT.powi(2)).ln() + let g2 = nondimensional_end_to_end_length_per_link.powi(2); + let a: f64 = 14.054; + let b: f64 = 0.473; + let c = vec![ + vec![-0.75, 0.359375, -0.109375], + vec![-0.5, 1.0625, -0.5625] + ]; + let c0: f64 = 1.0 - (1.0 + (0.38/nondimensional_persistance_length.powf(0.95)).powi(-5)).powf(-0.2); + let d: f64; + let e: f64; + if nondimensional_persistance_length < &0.125 + { + d = 1.0; + e = (0.75/PI/nondimensional_persistance_length).powf(1.5)*(1.0 - 1.25*nondimensional_persistance_length); + } + else + { + d = 1.0 - 1.0/(0.177/(nondimensional_persistance_length - 0.111) + 6.40*(nondimensional_persistance_length - 0.111).powf(0.783)); + e = 112.04*nondimensional_persistance_length.powi(2)*(0.246/nondimensional_persistance_length - a*nondimensional_persistance_length).exp(); + } + let f = (1.0 - c0*g2)/(1.0 - g2); + let h = nondimensional_end_to_end_length_per_link/(1.0 - b.powi(2)*g2); + let arg = -d*nondimensional_persistance_length*a*(1.0 + b)*h; + let sum = (0..2).collect::>().iter().map(|i| (1..4).collect::>().iter().map(|j| c[*i][j - 1]*(nondimensional_persistance_length.powi(*i as i32 - 1)*g2.powi((*j).try_into().unwrap()))).sum::()).sum::(); + -e.ln() - 2.5*f.ln() - sum/(1.0 - g2) - arg*b*nondimensional_end_to_end_length_per_link - bessel_i(&0, &arg).ln() - ((*number_of_links as f64) - 1.0)*(4.0*(-1.0/nondimensional_persistance_length).exp().acos().sin()*PI.powi(2)*hinge_mass*link_length.powi(2)*BOLTZMANN_CONSTANT*temperature/PLANCK_CONSTANT.powi(2)).ln() } /// The nondimensional Helmholtz free energy per link as a function of the applied nondimensional end-to-end length per link and temperature, parameterized by the number of links, link length, and hinge mass, and nondimensional persistance length. @@ -131,7 +154,7 @@ pub fn nondimensional_helmholtz_free_energy_per_link(number_of_links: &u8, link_ /// The nondimensional relative Helmholtz free energy as a function of the applied nondimensional end-to-end length per link, parameterized by the nondimensional persistance length. pub fn nondimensional_relative_helmholtz_free_energy(nondimensional_persistance_length: &f64, nondimensional_end_to_end_length_per_link: &f64) -> f64 { - (nondimensional_equilibrium_distribution(nondimensional_persistance_length, &1.0, &ZERO)/nondimensional_equilibrium_distribution(nondimensional_persistance_length, &1.0, nondimensional_end_to_end_length_per_link)).ln() + nondimensional_helmholtz_free_energy(&8, &1.0, &1.0, nondimensional_persistance_length, nondimensional_end_to_end_length_per_link, &300.0) - nondimensional_helmholtz_free_energy(&8, &1.0, &1.0, nondimensional_persistance_length, &ZERO, &300.0) } /// The nondimensional relative Helmholtz free energy per link as a function of the applied nondimensional end-to-end length per link, parameterized by the number of links and nondimensional persistance length. diff --git a/src/physics/single_chain/wlc/thermodynamics/isotensional/__init__.py b/src/physics/single_chain/wlc/thermodynamics/isotensional/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/physics/single_chain/wlc/thermodynamics/isotensional/ex.rs b/src/physics/single_chain/wlc/thermodynamics/isotensional/ex.rs new file mode 100644 index 00000000..738b24c4 --- /dev/null +++ b/src/physics/single_chain/wlc/thermodynamics/isotensional/ex.rs @@ -0,0 +1,60 @@ +#[no_mangle] +pub extern fn physics_single_chain_wlc_thermodynamics_isotensional_end_to_end_length(number_of_links: u8, link_length: f64, persistance_length: f64, force: f64, temperature: f64) -> f64 +{ + super::end_to_end_length(&number_of_links, &link_length, &persistance_length, &force, &temperature) +} +#[no_mangle] +pub extern fn physics_single_chain_wlc_thermodynamics_isotensional_end_to_end_length_per_link(number_of_links: u8, link_length: f64, persistance_length: f64, force: f64, temperature: f64) -> f64 +{ + super::end_to_end_length_per_link(&number_of_links, &link_length, &persistance_length, &force, &temperature) +} +#[no_mangle] +pub extern fn physics_single_chain_wlc_thermodynamics_isotensional_nondimensional_end_to_end_length(number_of_links: u8, nondimensional_persistance_length: f64, nondimensional_force: f64) -> f64 +{ + super::nondimensional_end_to_end_length(&number_of_links, &nondimensional_persistance_length, &nondimensional_force) +} +#[no_mangle] +pub extern fn physics_single_chain_wlc_thermodynamics_isotensional_nondimensional_end_to_end_length_per_link(number_of_links: u8, nondimensional_persistance_length: f64, nondimensional_force: f64) -> f64 +{ + super::nondimensional_end_to_end_length_per_link(&number_of_links, &nondimensional_persistance_length, &nondimensional_force) +} +#[no_mangle] +pub extern fn physics_single_chain_wlc_thermodynamics_isotensional_gibbs_free_energy(number_of_links: u8, link_length: f64, hinge_mass: f64, persistance_length: f64, force: f64, temperature: f64) -> f64 +{ + super::gibbs_free_energy(&number_of_links, &link_length, &hinge_mass, &persistance_length, &force, &temperature) +} +#[no_mangle] +pub extern fn physics_single_chain_wlc_thermodynamics_isotensional_gibbs_free_energy_per_link(number_of_links: u8, link_length: f64, hinge_mass: f64, persistance_length: f64, force: f64, temperature: f64) -> f64 +{ + super::gibbs_free_energy_per_link(&number_of_links, &link_length, &hinge_mass, &persistance_length, &force, &temperature) +} +#[no_mangle] +pub extern fn physics_single_chain_wlc_thermodynamics_isotensional_relative_gibbs_free_energy(number_of_links: u8, link_length: f64, persistance_length: f64, force: f64, temperature: f64) -> f64 +{ + super::relative_gibbs_free_energy(&number_of_links, &link_length, &persistance_length, &force, &temperature) +} +#[no_mangle] +pub extern fn physics_single_chain_wlc_thermodynamics_isotensional_relative_gibbs_free_energy_per_link(number_of_links: u8, link_length: f64, persistance_length: f64, force: f64, temperature: f64) -> f64 +{ + super::relative_gibbs_free_energy_per_link(&number_of_links, &link_length, &persistance_length, &force, &temperature) +} +#[no_mangle] +pub extern fn physics_single_chain_wlc_thermodynamics_isotensional_nondimensional_gibbs_free_energy(number_of_links: u8, link_length: f64, hinge_mass: f64, nondimensional_persistance_length: f64, nondimensional_force: f64, temperature: f64) -> f64 +{ + super::nondimensional_gibbs_free_energy(&number_of_links, &link_length, &hinge_mass, &nondimensional_persistance_length, &nondimensional_force, &temperature) +} +#[no_mangle] +pub extern fn physics_single_chain_wlc_thermodynamics_isotensional_nondimensional_gibbs_free_energy_per_link(number_of_links: u8, link_length: f64, hinge_mass: f64, nondimensional_persistance_length: f64, nondimensional_force: f64, temperature: f64) -> f64 +{ + super::nondimensional_gibbs_free_energy_per_link(&number_of_links, &link_length, &hinge_mass, &nondimensional_persistance_length, &nondimensional_force, &temperature) +} +#[no_mangle] +pub extern fn physics_single_chain_wlc_thermodynamics_isotensional_nondimensional_relative_gibbs_free_energy(number_of_links: u8, nondimensional_persistance_length: f64, nondimensional_force: f64) -> f64 +{ + super::nondimensional_relative_gibbs_free_energy(&number_of_links, &nondimensional_persistance_length, &nondimensional_force) +} +#[no_mangle] +pub extern fn physics_single_chain_wlc_thermodynamics_isotensional_nondimensional_relative_gibbs_free_energy_per_link(number_of_links: u8, nondimensional_persistance_length: f64, nondimensional_force: f64) -> f64 +{ + super::nondimensional_relative_gibbs_free_energy_per_link(&number_of_links, &nondimensional_persistance_length, &nondimensional_force) +} \ No newline at end of file diff --git a/src/physics/single_chain/wlc/thermodynamics/isotensional/legendre/__init__.py b/src/physics/single_chain/wlc/thermodynamics/isotensional/legendre/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/physics/single_chain/wlc/thermodynamics/isotensional/legendre/ex.rs b/src/physics/single_chain/wlc/thermodynamics/isotensional/legendre/ex.rs new file mode 100644 index 00000000..dd8c0079 --- /dev/null +++ b/src/physics/single_chain/wlc/thermodynamics/isotensional/legendre/ex.rs @@ -0,0 +1,40 @@ +#[no_mangle] +pub extern fn physics_single_chain_wlc_thermodynamics_isotensional_legendre_helmholtz_free_energy(number_of_links: u8, link_length: f64, hinge_mass: f64, persistance_length: f64, force: f64, temperature: f64) -> f64 +{ + super::helmholtz_free_energy(&number_of_links, &link_length, &hinge_mass, &persistance_length, &force, &temperature) +} +#[no_mangle] +pub extern fn physics_single_chain_wlc_thermodynamics_isotensional_legendre_helmholtz_free_energy_per_link(number_of_links: u8, link_length: f64, hinge_mass: f64, persistance_length: f64, force: f64, temperature: f64) -> f64 +{ + super::helmholtz_free_energy_per_link(&number_of_links, &link_length, &hinge_mass, &persistance_length, &force, &temperature) +} +#[no_mangle] +pub extern fn physics_single_chain_wlc_thermodynamics_isotensional_legendre_relative_helmholtz_free_energy(number_of_links: u8, link_length: f64, persistance_length: f64, force: f64, temperature: f64) -> f64 +{ + super::relative_helmholtz_free_energy(&number_of_links, &link_length, &persistance_length, &force, &temperature) +} +#[no_mangle] +pub extern fn physics_single_chain_wlc_thermodynamics_isotensional_legendre_relative_helmholtz_free_energy_per_link(number_of_links: u8, link_length: f64, persistance_length: f64, force: f64, temperature: f64) -> f64 +{ + super::relative_helmholtz_free_energy_per_link(&number_of_links, &link_length, &persistance_length, &force, &temperature) +} +#[no_mangle] +pub extern fn physics_single_chain_wlc_thermodynamics_isotensional_legendre_nondimensional_helmholtz_free_energy(number_of_links: u8, link_length: f64, hinge_mass: f64, nondimensional_persistance_length: f64, nondimensional_force: f64, temperature: f64) -> f64 +{ + super::nondimensional_helmholtz_free_energy(&number_of_links, &link_length, &hinge_mass, &nondimensional_persistance_length, &nondimensional_force, &temperature) +} +#[no_mangle] +pub extern fn physics_single_chain_wlc_thermodynamics_isotensional_legendre_nondimensional_helmholtz_free_energy_per_link(number_of_links: u8, link_length: f64, hinge_mass: f64, nondimensional_persistance_length: f64, nondimensional_force: f64, temperature: f64) -> f64 +{ + super::nondimensional_helmholtz_free_energy_per_link(&number_of_links, &link_length, &hinge_mass, &nondimensional_persistance_length, &nondimensional_force, &temperature) +} +#[no_mangle] +pub extern fn physics_single_chain_wlc_thermodynamics_isotensional_legendre_nondimensional_relative_helmholtz_free_energy(number_of_links: u8, nondimensional_persistance_length: f64, nondimensional_force: f64) -> f64 +{ + super::nondimensional_relative_helmholtz_free_energy(&number_of_links, &nondimensional_persistance_length, &nondimensional_force) +} +#[no_mangle] +pub extern fn physics_single_chain_wlc_thermodynamics_isotensional_legendre_nondimensional_relative_helmholtz_free_energy_per_link(number_of_links: u8, nondimensional_persistance_length: f64, nondimensional_force: f64) -> f64 +{ + super::nondimensional_relative_helmholtz_free_energy_per_link(&number_of_links, &nondimensional_persistance_length, &nondimensional_force) +} \ No newline at end of file diff --git a/src/physics/single_chain/wlc/thermodynamics/isotensional/legendre/mod.jl b/src/physics/single_chain/wlc/thermodynamics/isotensional/legendre/mod.jl new file mode 100644 index 00000000..71920471 --- /dev/null +++ b/src/physics/single_chain/wlc/thermodynamics/isotensional/legendre/mod.jl @@ -0,0 +1,459 @@ +""" +The worm-like chain (WLC) model thermodynamics in the isotensional ensemble approximated using a Legendre transformation. +""" +module Legendre + +using DocStringExtensions +using .......Polymers: PROJECT_ROOT + +""" +The structure of the thermodynamics of the WLC model in the isotensional ensemble approximated using a Legendre transformation. +$(FIELDS) +""" +struct WLC + """ + The number of links in the chain ``N_b``. + """ + number_of_links::UInt8 + """ + The length of each link in the chain ``\\ell_b`` in units of nm. + """ + link_length::Float64 + """ + The mass of each hinge in the chain ``m`` in units of kg/mol. + """ + hinge_mass::Float64 + """ + The persistance length of the chain in units of nm. + """ + persistance_length::Float64 + nondimensional_persistance_length::Float64 + """ + The Helmholtz free energy ``\\psi`` as a function of the applied force ``f`` and temperature ``T``. + """ + helmholtz_free_energy::Function + """ + The Helmholtz free energy per link ``\\psi/N_b`` as a function of the applied force ``f`` and temperature ``T``. + """ + helmholtz_free_energy_per_link::Function + """ + The relative helmholtz free energy ``\\Delta\\psi\\equiv\\psi(f,T)-\\psi(0,T)`` as a function of the applied force ``f`` and temperature ``T``. + """ + relative_helmholtz_free_energy::Function + """ + The relative helmholtz free energy per link ``\\Delta\\psi/N_b`` as a function of the applied force ``f`` and temperature ``T``. + """ + relative_helmholtz_free_energy_per_link::Function + """ + The nondimensional helmholtz free energy ``N_b\\vartheta=\\beta\\psi`` as a function of the applied nondimensional force ``\\eta`` and temperature ``T``. + """ + nondimensional_helmholtz_free_energy::Function + """ + The nondimensional helmholtz free energy per link ``\\vartheta\\equiv\\beta\\psi/N_b`` as a function of the applied nondimensional force ``\\eta`` and temperature ``T``. + """ + nondimensional_helmholtz_free_energy_per_link::Function + """ + The nondimensional relative helmholtz free energy ``N_b\\Delta\\vartheta=\\beta\\Delta\\psi`` as a function of the applied nondimensional force ``\\eta``. + """ + nondimensional_relative_helmholtz_free_energy::Function + """ + The nondimensional relative helmholtz free energy per link ``\\Delta\\vartheta\\equiv\\beta\\Delta\\psi/N_b`` as a function of the applied nondimensional force ``\\eta``. + """ + nondimensional_relative_helmholtz_free_energy_per_link::Function +end + +""" +The Helmholtz free energy ``\\psi`` as a function of the applied force ``f`` and temperature ``T``, +parameterized by the number of links ``N_b``, link length ``\\ell_b``, hinge mass ``m``, and persistance length ``\\ell_p``. + +```math +\\psi(f, T) \\sim \\varphi(f, T) + f \\xi(f, T) \\quad \\text{for } N_b\\gg 1. +``` + +$(TYPEDSIGNATURES) +""" +function helmholtz_free_energy( + number_of_links::Union{UInt8,Vector,Matrix,Array}, + link_length::Union{Float64,Vector,Matrix,Array}, + hinge_mass::Union{Float64,Vector,Matrix,Array}, + persistance_length::Union{Float64,Vector,Matrix,Array}, + force::Union{Float64,Vector,Matrix,Array}, + temperature::Union{Float64,Vector,Matrix,Array}, +)::Union{Float64,Vector,Matrix,Array} + return broadcast( + ( + number_of_links_i, + link_length_i, + hinge_mass_i, + persistance_length_i, + force_i, + temperature_i, + ) -> ccall( + ( + :physics_single_chain_wlc_thermodynamics_isotensional_legendre_helmholtz_free_energy, + string(PROJECT_ROOT, "target/debug/libpolymers"), + ), + Float64, + (UInt8, Float64, Float64, Float64, Float64, Float64), + number_of_links_i, + link_length_i, + hinge_mass_i, + persistance_length_i, + force_i, + temperature_i, + ), + number_of_links, + link_length, + hinge_mass, + persistance_length, + force, + temperature, + ) +end + +""" +The Helmholtz free energy per link ``\\psi/N_b`` as a function of the applied force ``f`` and temperature ``T``, +parameterized by the number of links ``N_b``, link length ``\\ell_b``, hinge mass ``m``, and persistance length ``\\ell_p``. + +$(TYPEDSIGNATURES) +""" +function helmholtz_free_energy_per_link( + number_of_links::Union{UInt8,Vector,Matrix,Array}, + link_length::Union{Float64,Vector,Matrix,Array}, + hinge_mass::Union{Float64,Vector,Matrix,Array}, + persistance_length::Union{Float64,Vector,Matrix,Array}, + force::Union{Float64,Vector,Matrix,Array}, + temperature::Union{Float64,Vector,Matrix,Array}, +)::Union{Float64,Vector,Matrix,Array} + return broadcast( + ( + number_of_links_i, + link_length_i, + hinge_mass_i, + persistance_length_i, + force_i, + temperature_i, + ) -> ccall( + ( + :physics_single_chain_wlc_thermodynamics_isotensional_legendre_helmholtz_free_energy_per_link, + string(PROJECT_ROOT, "target/debug/libpolymers"), + ), + Float64, + (UInt8, Float64, Float64, Float64, Float64, Float64), + number_of_links_i, + link_length_i, + hinge_mass_i, + persistance_length_i, + force_i, + temperature_i, + ), + number_of_links, + link_length, + hinge_mass, + persistance_length, + force, + temperature, + ) +end + +""" +The relative Helmholtz free energy ``\\Delta\\psi\\equiv\\psi(f,T)-\\psi(0,T)`` as a function of the applied force ``f`` and temperature ``T``, +parameterized by the number of links ``N_b``, link length ``\\ell_b``, and persistance length ``\\ell_p``. + +$(TYPEDSIGNATURES) +""" +function relative_helmholtz_free_energy( + number_of_links::Union{UInt8,Vector,Matrix,Array}, + link_length::Union{Float64,Vector,Matrix,Array}, + persistance_length::Union{Float64,Vector,Matrix,Array}, + force::Union{Float64,Vector,Matrix,Array}, + temperature::Union{Float64,Vector,Matrix,Array}, +)::Union{Float64,Vector,Matrix,Array} + return broadcast( + (number_of_links_i, link_length_i, persistance_length_i, force_i, temperature_i) -> + ccall( + ( + :physics_single_chain_wlc_thermodynamics_isotensional_legendre_relative_helmholtz_free_energy, + string(PROJECT_ROOT, "target/debug/libpolymers"), + ), + Float64, + (UInt8, Float64, Float64, Float64, Float64), + number_of_links_i, + link_length_i, + persistance_length_i, + force_i, + temperature_i, + ), + number_of_links, + link_length, + persistance_length, + force, + temperature, + ) +end + +""" +The relative Helmholtz free energy per link ``\\Delta\\psi/N_b`` as a function of the applied force ``f`` and temperature ``T``, +parameterized by the number of links ``N_b``, link length ``\\ell_b``, and persistance length ``\\ell_p``. + +$(TYPEDSIGNATURES) +""" +function relative_helmholtz_free_energy_per_link( + number_of_links::Union{UInt8,Vector,Matrix,Array}, + link_length::Union{Float64,Vector,Matrix,Array}, + persistance_length::Union{Float64,Vector,Matrix,Array}, + force::Union{Float64,Vector,Matrix,Array}, + temperature::Union{Float64,Vector,Matrix,Array}, +)::Union{Float64,Vector,Matrix,Array} + return broadcast( + (number_of_links_i, link_length_i, persistance_length_i, force_i, temperature_i) -> + ccall( + ( + :physics_single_chain_wlc_thermodynamics_isotensional_legendre_relative_helmholtz_free_energy_per_link, + string(PROJECT_ROOT, "target/debug/libpolymers"), + ), + Float64, + (UInt8, Float64, Float64, Float64, Float64), + number_of_links_i, + link_length_i, + persistance_length_i, + force_i, + temperature_i, + ), + number_of_links, + link_length, + persistance_length, + force, + temperature, + ) +end + +""" +The nondimensional Helmholtz free energy ``N_b\\vartheta=\\beta\\psi`` as a function of the applied nondimensional force ``\\eta`` and temperature ``T``, +parameterized by the number of links ``N_b``, link length ``\\ell_b``, hinge mass ``m``, and nondimensional persistance length ``\\zeta``. + +$(TYPEDSIGNATURES) +""" +function nondimensional_helmholtz_free_energy( + number_of_links::Union{UInt8,Vector,Matrix,Array}, + link_length::Union{Float64,Vector,Matrix,Array}, + hinge_mass::Union{Float64,Vector,Matrix,Array}, + nondimensional_persistance_length::Union{Float64,Vector,Matrix,Array}, + nondimensional_force::Union{Float64,Vector,Matrix,Array}, + temperature::Union{Float64,Vector,Matrix,Array}, +)::Union{Float64,Vector,Matrix,Array} + return broadcast( + ( + number_of_links_i, + link_length_i, + hinge_mass_i, + nondimensional_persistance_length_i, + nondimensional_force_i, + temperature_i, + ) -> ccall( + ( + :physics_single_chain_wlc_thermodynamics_isotensional_legendre_nondimensional_helmholtz_free_energy, + string(PROJECT_ROOT, "target/debug/libpolymers"), + ), + Float64, + (UInt8, Float64, Float64, Float64, Float64, Float64), + number_of_links_i, + link_length_i, + hinge_mass_i, + nondimensional_persistance_length_i, + nondimensional_force_i, + temperature_i, + ), + number_of_links, + link_length, + hinge_mass, + nondimensional_persistance_length, + nondimensional_force, + temperature, + ) +end + +""" +The nondimensional Helmholtz free energy per link ``\\vartheta\\equiv\\beta\\psi/N_b`` as a function of the applied nondimensional force ``\\eta`` and temperature ``T``, +parameterized by the number of links ``N_b``, link length ``\\ell_b``, hinge mass ``m``, and nondimensional persistance length ``\\zeta``. + +$(TYPEDSIGNATURES) +""" +function nondimensional_helmholtz_free_energy_per_link( + number_of_links::Union{UInt8,Vector,Matrix,Array}, + link_length::Union{Float64,Vector,Matrix,Array}, + hinge_mass::Union{Float64,Vector,Matrix,Array}, + nondimensional_persistance_length::Union{Float64,Vector,Matrix,Array}, + nondimensional_force::Union{Float64,Vector,Matrix,Array}, + temperature::Union{Float64,Vector,Matrix,Array}, +)::Union{Float64,Vector,Matrix,Array} + return broadcast( + ( + number_of_links_i, + link_length_i, + hinge_mass_i, + nondimensional_persistance_length_i, + nondimensional_force_i, + temperature_i, + ) -> ccall( + ( + :physics_single_chain_wlc_thermodynamics_isotensional_legendre_nondimensional_helmholtz_free_energy_per_link, + string(PROJECT_ROOT, "target/debug/libpolymers"), + ), + Float64, + (UInt8, Float64, Float64, Float64, Float64, Float64), + number_of_links_i, + link_length_i, + hinge_mass_i, + nondimensional_persistance_length_i, + nondimensional_force_i, + temperature_i, + ), + number_of_links, + link_length, + hinge_mass, + nondimensional_persistance_length, + nondimensional_force, + temperature, + ) +end + +""" +The nondimensional relative Helmholtz free energy ``N_b\\Delta\\vartheta=\\beta\\Delta\\psi`` as a function of the applied nondimensional force ``\\eta``, +parameterized by the number of links ``N_b`` and nondimensional persistance length ``\\zeta``. + +$(TYPEDSIGNATURES) +""" +function nondimensional_relative_helmholtz_free_energy( + number_of_links::Union{UInt8,Vector,Matrix,Array}, + nondimensional_persistance_length::Union{Float64,Vector,Matrix,Array}, + nondimensional_force::Union{Float64,Vector,Matrix,Array}, +)::Union{Float64,Vector,Matrix,Array} + return broadcast( + (number_of_links_i, nondimensional_persistance_length_i, nondimensional_force_i) -> + ccall( + ( + :physics_single_chain_wlc_thermodynamics_isotensional_legendre_nondimensional_relative_helmholtz_free_energy, + string(PROJECT_ROOT, "target/debug/libpolymers"), + ), + Float64, + (UInt8, Float64, Float64), + number_of_links_i, + nondimensional_persistance_length_i, + nondimensional_force_i, + ), + number_of_links, + nondimensional_persistance_length, + nondimensional_force, + ) +end + +""" +The nondimensional relative Helmholtz free energy per link ``\\Delta\\vartheta\\equiv\\beta\\Delta\\psi/N_b`` as a function of the applied nondimensional force ``\\eta``, +parameterized by the number of links ``N_b`` and nondimensional persistance length ``\\zeta``. + +$(TYPEDSIGNATURES) +""" +function nondimensional_relative_helmholtz_free_energy_per_link( + number_of_links::Union{UInt8,Vector,Matrix,Array}, + nondimensional_persistance_length::Union{Float64,Vector,Matrix,Array}, + nondimensional_force::Union{Float64,Vector,Matrix,Array}, +)::Union{Float64,Vector,Matrix,Array} + return broadcast( + (number_of_links_i, nondimensional_persistance_length_i, nondimensional_force_i) -> + ccall( + ( + :physics_single_chain_wlc_thermodynamics_isotensional_legendre_nondimensional_relative_helmholtz_free_energy_per_link, + string(PROJECT_ROOT, "target/debug/libpolymers"), + ), + Float64, + (UInt8, Float64, Float64), + number_of_links_i, + nondimensional_persistance_length_i, + nondimensional_force_i, + ), + number_of_links, + nondimensional_persistance_length, + nondimensional_force, + ) +end + +""" +Initializes and returns an instance of the thermodynamics of the WLC model in the isotensional ensemble approximated using a Legendre transformation. + +$(TYPEDSIGNATURES) +""" +function WLC( + number_of_links::UInt8, + link_length::Float64, + hinge_mass::Float64, + persistance_length::Float64, +) + nondimensional_persistance_length = persistance_length / number_of_links / link_length + return WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length, + nondimensional_persistance_length, + (force, temperature) -> helmholtz_free_energy( + number_of_links, + link_length, + hinge_mass, + persistance_length, + force, + temperature, + ), + (force, temperature) -> helmholtz_free_energy_per_link( + number_of_links, + link_length, + hinge_mass, + persistance_length, + force, + temperature, + ), + (force, temperature) -> relative_helmholtz_free_energy( + number_of_links, + link_length, + persistance_length, + force, + temperature, + ), + (force, temperature) -> relative_helmholtz_free_energy_per_link( + number_of_links, + link_length, + persistance_length, + force, + temperature, + ), + (nondimensional_force, temperature) -> nondimensional_helmholtz_free_energy( + number_of_links, + link_length, + hinge_mass, + nondimensional_persistance_length, + nondimensional_force, + temperature, + ), + (nondimensional_force, temperature) -> + nondimensional_helmholtz_free_energy_per_link( + number_of_links, + link_length, + hinge_mass, + nondimensional_persistance_length, + nondimensional_force, + temperature, + ), + (nondimensional_force) -> nondimensional_relative_helmholtz_free_energy( + number_of_links, + nondimensional_persistance_length, + nondimensional_force, + ), + (nondimensional_force) -> nondimensional_relative_helmholtz_free_energy_per_link( + number_of_links, + nondimensional_persistance_length, + nondimensional_force, + ), + ) +end + +end diff --git a/src/physics/single_chain/wlc/thermodynamics/isotensional/legendre/mod.rs b/src/physics/single_chain/wlc/thermodynamics/isotensional/legendre/mod.rs new file mode 100644 index 00000000..e3778079 --- /dev/null +++ b/src/physics/single_chain/wlc/thermodynamics/isotensional/legendre/mod.rs @@ -0,0 +1,141 @@ +#[cfg(feature = "extern")] +pub mod ex; + +#[cfg(feature = "python")] +pub mod py; + +mod test; + +use crate::physics:: +{ + BOLTZMANN_CONSTANT, + single_chain::ZERO +}; +use super:: +{ + nondimensional_end_to_end_length, + nondimensional_gibbs_free_energy +}; + +/// The structure of the thermodynamics of the WLC model in the isotensional ensemble approximated using a Legendre transformation. +pub struct WLC +{ + /// The mass of each hinge in the chain in units of kg/mol. + pub hinge_mass: f64, + + /// The length of each link in the chain in units of nm. + pub link_length: f64, + + /// The number of links in the chain. + pub number_of_links: u8, + + /// The persistance length of the chain in units of nm. + pub persistance_length: f64, + + nondimensional_persistance_length: f64 +} + +/// The Helmholtz free energy as a function of the applied force and temperature, parameterized by the number of links, link length, hinge mass, and persistance length. +pub fn helmholtz_free_energy(number_of_links: &u8, link_length: &f64, hinge_mass: &f64, persistance_length: &f64, force: &f64, temperature: &f64) -> f64 +{ + nondimensional_helmholtz_free_energy(number_of_links, link_length, hinge_mass, &(persistance_length/((*number_of_links as f64)*link_length)), &(force*link_length/BOLTZMANN_CONSTANT/temperature), temperature)*BOLTZMANN_CONSTANT*temperature +} + +/// The Helmholtz free energy per link as a function of the applied force and temperature, parameterized by the number of links, link length, hinge mass, and persistance length. +pub fn helmholtz_free_energy_per_link(number_of_links: &u8, link_length: &f64, hinge_mass: &f64, persistance_length: &f64, force: &f64, temperature: &f64) -> f64 +{ + nondimensional_helmholtz_free_energy_per_link(number_of_links, link_length, hinge_mass, &(persistance_length/((*number_of_links as f64)*link_length)), &(force*link_length/BOLTZMANN_CONSTANT/temperature), temperature)*BOLTZMANN_CONSTANT*temperature +} + +/// The relative Helmholtz free energy as a function of the applied force and temperature, parameterized by the number of links, link length, and persistance length. +pub fn relative_helmholtz_free_energy(number_of_links: &u8, link_length: &f64, persistance_length: &f64, force: &f64, temperature: &f64) -> f64 +{ + nondimensional_relative_helmholtz_free_energy(number_of_links, &(persistance_length/((*number_of_links as f64)*link_length)), &(force*link_length/BOLTZMANN_CONSTANT/temperature))*BOLTZMANN_CONSTANT*temperature +} + +/// The relative Helmholtz free energy per link as a function of the applied force and temperature, parameterized by the number of links, link length, and persistance length. +pub fn relative_helmholtz_free_energy_per_link(number_of_links: &u8, link_length: &f64, persistance_length: &f64, force: &f64, temperature: &f64) -> f64 +{ + nondimensional_relative_helmholtz_free_energy_per_link(number_of_links, &(persistance_length/((*number_of_links as f64)*link_length)), &(force*link_length/BOLTZMANN_CONSTANT/temperature))*BOLTZMANN_CONSTANT*temperature +} + +/// The nondimensional Helmholtz free energy as a function of the applied nondimensional force and temperature, parameterized by the number of links, link length, hinge mass, and nondimensional persistance length. +pub fn nondimensional_helmholtz_free_energy(number_of_links: &u8, link_length: &f64, hinge_mass: &f64, nondimensional_persistance_length: &f64, nondimensional_force: &f64, temperature: &f64) -> f64 +{ + nondimensional_gibbs_free_energy(number_of_links, link_length, hinge_mass, nondimensional_persistance_length, nondimensional_force, temperature) + nondimensional_force*nondimensional_end_to_end_length(number_of_links, nondimensional_persistance_length, nondimensional_force) +} + +/// The nondimensional Helmholtz free energy per link as a function of the applied nondimensional force and temperature, parameterized by the number of links, link length hinge mass, and nondimensional persistance length. +pub fn nondimensional_helmholtz_free_energy_per_link(number_of_links: &u8, link_length: &f64, hinge_mass: &f64, nondimensional_persistance_length: &f64, nondimensional_force: &f64, temperature: &f64) -> f64 +{ + nondimensional_helmholtz_free_energy(number_of_links, link_length, hinge_mass, nondimensional_persistance_length, nondimensional_force, temperature)/(*number_of_links as f64) +} + +/// The nondimensional relative Helmholtz free energy as a function of the applied nondimensional force, parameterized by the number of links and nondimensional persistance length. +pub fn nondimensional_relative_helmholtz_free_energy(number_of_links: &u8, nondimensional_persistance_length: &f64, nondimensional_force: &f64) -> f64 +{ + nondimensional_helmholtz_free_energy(number_of_links, &1.0, &1.0, nondimensional_persistance_length, nondimensional_force, &300.0) - nondimensional_helmholtz_free_energy(number_of_links, &1.0, &1.0, nondimensional_persistance_length, &ZERO, &300.0) +} + +/// The nondimensional relative Helmholtz free energy per link as a function of the applied nondimensional force, parameterized by the number of links and nondimensional persistance length. +pub fn nondimensional_relative_helmholtz_free_energy_per_link(number_of_links: &u8, nondimensional_persistance_length: &f64, nondimensional_force: &f64) -> f64 +{ + nondimensional_helmholtz_free_energy_per_link(number_of_links, &1.0, &1.0, nondimensional_persistance_length, nondimensional_force, &300.0) - nondimensional_helmholtz_free_energy_per_link(number_of_links, &1.0, &1.0, nondimensional_persistance_length, &ZERO, &300.0) +} + +/// The implemented functionality of the thermodynamics of the WLC model in the isotensional ensemble approximated using a Legendre transformation. +impl WLC +{ + /// Initializes and returns an instance of the thermodynamics of the WLC model in the isotensional ensemble approximated using a Legendre transformation. + pub fn init(number_of_links: u8, link_length: f64, hinge_mass: f64, persistance_length: f64) -> Self + { + WLC + { + hinge_mass, + link_length, + number_of_links, + persistance_length, + nondimensional_persistance_length: persistance_length/(number_of_links as f64)/link_length + } + } + /// The Helmholtz free energy as a function of the applied force and temperature. + pub fn helmholtz_free_energy(&self, force: &f64, temperature: &f64) -> f64 + { + helmholtz_free_energy(&self.number_of_links, &self.link_length, &self.hinge_mass, &self.persistance_length, force, temperature) + } + /// The Helmholtz free energy per link as a function of the applied force and temperature. + pub fn helmholtz_free_energy_per_link(&self, force: &f64, temperature: &f64) -> f64 + { + helmholtz_free_energy_per_link(&self.number_of_links, &self.link_length, &self.hinge_mass, &self.persistance_length, force, temperature) + } + /// The relative Helmholtz free energy as a function of the applied force and temperature. + pub fn relative_helmholtz_free_energy(&self, force: &f64, temperature: &f64) -> f64 + { + relative_helmholtz_free_energy(&self.number_of_links, &self.link_length, &self.persistance_length, force, temperature) + } + /// The relative Helmholtz free energy per link as a function of the applied force and temperature. + pub fn relative_helmholtz_free_energy_per_link(&self, force: &f64, temperature: &f64) -> f64 + { + relative_helmholtz_free_energy_per_link(&self.number_of_links, &self.link_length, &self.persistance_length, force, temperature) + } + /// The nondimensional Helmholtz free energy as a function of the applied nondimensional force and temperature. + pub fn nondimensional_helmholtz_free_energy(&self, nondimensional_force: &f64, temperature: &f64) -> f64 + { + nondimensional_helmholtz_free_energy(&self.number_of_links, &self.link_length, &self.hinge_mass, &self.nondimensional_persistance_length, nondimensional_force, temperature) + } + /// The nondimensional Helmholtz free energy per link as a function of the applied nondimensional force and temperature. + pub fn nondimensional_helmholtz_free_energy_per_link(&self, nondimensional_force: &f64, temperature: &f64) -> f64 + { + nondimensional_helmholtz_free_energy_per_link(&self.number_of_links, &self.link_length, &self.hinge_mass, &self.nondimensional_persistance_length, nondimensional_force, temperature) + } + /// The nondimensional relative Helmholtz free energy as a function of the applied nondimensional force. + pub fn nondimensional_relative_helmholtz_free_energy(&self, nondimensional_force: &f64) -> f64 + { + nondimensional_relative_helmholtz_free_energy(&self.number_of_links, &self.nondimensional_persistance_length, nondimensional_force) + } + /// The nondimensional relative Helmholtz free energy per link as a function of the applied nondimensional force. + pub fn nondimensional_relative_helmholtz_free_energy_per_link(&self, nondimensional_force: &f64) -> f64 + { + nondimensional_relative_helmholtz_free_energy_per_link(&self.number_of_links, &self.nondimensional_persistance_length, nondimensional_force) + } +} diff --git a/src/physics/single_chain/wlc/thermodynamics/isotensional/legendre/py.rs b/src/physics/single_chain/wlc/thermodynamics/isotensional/legendre/py.rs new file mode 100644 index 00000000..cb406bd4 --- /dev/null +++ b/src/physics/single_chain/wlc/thermodynamics/isotensional/legendre/py.rs @@ -0,0 +1,161 @@ +use pyo3::prelude::*; +use numpy:: +{ + IntoPyArray, + PyArrayDyn, + PyReadonlyArrayDyn +}; + +pub fn register_module(py: Python<'_>, parent_module: &PyModule) -> PyResult<()> +{ + let legendre = PyModule::new(py, "legendre")?; + parent_module.add_submodule(legendre)?; + legendre.add_class::()?; + Ok(()) +} + +/// The worm-like chain (WLC) model thermodynamics in the isotensional ensemble approximated using a Legendre transformation. +#[pyclass] +#[derive(Copy, Clone)] +pub struct WLC +{ + /// The mass of each hinge in the chain in units of kg/mol. + #[pyo3(get)] + pub hinge_mass: f64, + + /// The length of each link in the chain in units of nm. + #[pyo3(get)] + pub link_length: f64, + + /// The number of links in the chain. + #[pyo3(get)] + pub number_of_links: u8, + + /// The persistance length of the chain in units of nm. + #[pyo3(get)] + pub persistance_length: f64, + + nondimensional_persistance_length: f64 +} + +#[pymethods] +impl WLC +{ + #[new] + pub fn init(number_of_links: u8, link_length: f64, hinge_mass: f64, persistance_length: f64) -> Self + { + WLC + { + hinge_mass, + link_length, + number_of_links, + persistance_length, + nondimensional_persistance_length: persistance_length/(number_of_links as f64)/link_length + } + } + /// The Helmholtz free energy as a function of the applied force and temperature, + /// + /// .. math:: + /// \psi(f, T) \sim \varphi(f, T) + f \xi(f, T) \quad \text{for } N_b\gg 1. + /// + /// Args: + /// force (numpy.ndarray): The force :math:`f`. + /// temperature (float): The temperature :math:`T`. + /// + /// Returns: + /// numpy.ndarray: The Helmholtz free energy :math:`\psi`. + /// + pub fn helmholtz_free_energy<'py>(&self, py: Python<'py>, force: PyReadonlyArrayDyn, temperature: f64) -> &'py PyArrayDyn + { + force.as_array().mapv(|force: f64| super::helmholtz_free_energy(&self.number_of_links, &self.link_length, &self.hinge_mass, &self.persistance_length, &force, &temperature)).into_pyarray(py) + } + /// The Helmholtz free energy per link as a function of the applied force and temperature. + /// + /// Args: + /// force (numpy.ndarray): The force :math:`f`. + /// temperature (float): The temperature :math:`T`. + /// + /// Returns: + /// numpy.ndarray: The Helmholtz free energy per link :math:`\psi/N_b`. + /// + pub fn helmholtz_free_energy_per_link<'py>(&self, py: Python<'py>, force: PyReadonlyArrayDyn, temperature: f64) -> &'py PyArrayDyn + { + force.as_array().mapv(|force: f64| super::helmholtz_free_energy_per_link(&self.number_of_links, &self.link_length, &self.hinge_mass, &self.persistance_length, &force, &temperature)).into_pyarray(py) + } + /// The relative Helmholtz free energy as a function of the applied force and temperature. + /// + /// Args: + /// force (numpy.ndarray): The force :math:`f`. + /// temperature (float): The temperature :math:`T`. + /// + /// Returns: + /// numpy.ndarray: The relative Helmholtz free energy :math:`\Delta\psi\equiv\psi(f,T)-\psi(0,T)`. + /// + pub fn relative_helmholtz_free_energy<'py>(&self, py: Python<'py>, force: PyReadonlyArrayDyn, temperature: f64) -> &'py PyArrayDyn + { + force.as_array().mapv(|force: f64| super::relative_helmholtz_free_energy(&self.number_of_links, &self.link_length, &self.persistance_length, &force, &temperature)).into_pyarray(py) + } + /// The relative Helmholtz free energy per link as a function of the applied force and temperature. + /// + /// Args: + /// force (numpy.ndarray): The force :math:`f`. + /// temperature (float): The temperature :math:`T`. + /// + /// Returns: + /// numpy.ndarray: The relative Helmholtz free energy per link :math:`\Delta\psi/N_b`. + /// + pub fn relative_helmholtz_free_energy_per_link<'py>(&self, py: Python<'py>, force: PyReadonlyArrayDyn, temperature: f64) -> &'py PyArrayDyn + { + force.as_array().mapv(|force: f64| super::relative_helmholtz_free_energy_per_link(&self.number_of_links, &self.link_length, &self.persistance_length, &force, &temperature)).into_pyarray(py) + } + /// The nondimensional Helmholtz free energy as a function of the applied nondimensional force and temperature. + /// + /// Args: + /// nondimensional_force (numpy.ndarray): The nondimensional force :math:`\eta\equiv\beta f\ell_b`. + /// temperature (float): The temperature :math:`T`. + /// + /// Returns: + /// numpy.ndarray: The nondimensional Helmholtz free energy :math:`\beta\psi=N_b\vartheta`. + /// + pub fn nondimensional_helmholtz_free_energy<'py>(&self, py: Python<'py>, nondimensional_force: PyReadonlyArrayDyn, temperature: f64) -> &'py PyArrayDyn + { + nondimensional_force.as_array().mapv(|nondimensional_force: f64| super::nondimensional_helmholtz_free_energy(&self.number_of_links, &self.link_length, &self.hinge_mass, &self.nondimensional_persistance_length, &nondimensional_force, &temperature)).into_pyarray(py) + } + /// The nondimensional Helmholtz free energy per link as a function of the applied nondimensional force and temperature. + /// + /// Args: + /// nondimensional_force (numpy.ndarray): The nondimensional force :math:`\eta\equiv\beta f\ell_b`. + /// temperature (float): The temperature :math:`T`. + /// + /// Returns: + /// numpy.ndarray: The nondimensional Helmholtz free energy per link :math:`\vartheta\equiv\beta\psi/N_b`. + /// + pub fn nondimensional_helmholtz_free_energy_per_link<'py>(&self, py: Python<'py>, nondimensional_force: PyReadonlyArrayDyn, temperature: f64) -> &'py PyArrayDyn + { + nondimensional_force.as_array().mapv(|nondimensional_force: f64| super::nondimensional_helmholtz_free_energy_per_link(&self.number_of_links, &self.link_length, &self.hinge_mass, &self.nondimensional_persistance_length, &nondimensional_force, &temperature)).into_pyarray(py) + } + /// The nondimensional relative Helmholtz free energy as a function of the applied nondimensional force. + /// + /// Args: + /// nondimensional_force (numpy.ndarray): The nondimensional force :math:`\eta\equiv\beta f\ell_b`. + /// + /// Returns: + /// numpy.ndarray: The nondimensional relative Helmholtz free energy :math:`\beta\Delta\psi=N_b\Delta\vartheta`. + /// + pub fn nondimensional_relative_helmholtz_free_energy<'py>(&self, py: Python<'py>, nondimensional_force: PyReadonlyArrayDyn) -> &'py PyArrayDyn + { + nondimensional_force.as_array().mapv(|nondimensional_force: f64| super::nondimensional_relative_helmholtz_free_energy(&self.number_of_links, &self.nondimensional_persistance_length, &nondimensional_force)).into_pyarray(py) + } + /// The nondimensional relative Helmholtz free energy per link as a function of the applied nondimensional force. + /// + /// Args: + /// nondimensional_force (numpy.ndarray): The nondimensional force :math:`\eta\equiv\beta f\ell_b`. + /// + /// Returns: + /// numpy.ndarray: The nondimensional relative Helmholtz free energy per link :math:`\Delta\vartheta\equiv\beta\Delta\psi/N_b`. + /// + pub fn nondimensional_relative_helmholtz_free_energy_per_link<'py>(&self, py: Python<'py>, nondimensional_force: PyReadonlyArrayDyn) -> &'py PyArrayDyn + { + nondimensional_force.as_array().mapv(|nondimensional_force: f64| super::nondimensional_relative_helmholtz_free_energy_per_link(&self.number_of_links, &self.nondimensional_persistance_length, &nondimensional_force)).into_pyarray(py) + } +} diff --git a/src/physics/single_chain/wlc/thermodynamics/isotensional/legendre/test.jl b/src/physics/single_chain/wlc/thermodynamics/isotensional/legendre/test.jl new file mode 100644 index 00000000..5bbceba4 --- /dev/null +++ b/src/physics/single_chain/wlc/thermodynamics/isotensional/legendre/test.jl @@ -0,0 +1,578 @@ +module Test + +using Test +using Polymers.Physics: BOLTZMANN_CONSTANT +using Polymers.Physics.SingleChain: ZERO, parameters +using Polymers.Physics.SingleChain.Wlc.Thermodynamics.Isotensional.Legendre: WLC + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::legendre::test::base::init" begin + @test isa( + WLC( + parameters.number_of_links_minimum, + parameters.link_length_reference, + parameters.hinge_mass_reference, + parameters.persistance_length_reference, + ), + Any, + ) +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::legendre::test::base::number_of_links" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + @test WLC( + number_of_links, + parameters.link_length_reference, + parameters.hinge_mass_reference, + parameters.persistance_length_reference, + ).number_of_links == number_of_links + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::legendre::test::base::link_length" begin + for _ = 1:parameters.number_of_loops + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + @test WLC( + parameters.number_of_links_minimum, + link_length, + parameters.hinge_mass_reference, + parameters.persistance_length_reference, + ).link_length == link_length + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::legendre::test::base::hinge_mass" begin + for _ = 1:parameters.number_of_loops + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + @test WLC( + parameters.number_of_links_minimum, + parameters.link_length_reference, + hinge_mass, + parameters.persistance_length_reference, + ).hinge_mass == hinge_mass + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::legendre::test::base::persistance_length" begin + for _ = 1:parameters.number_of_loops + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + @test WLC( + parameters.number_of_links_minimum, + parameters.link_length_reference, + parameters.hinge_mass_reference, + persistance_length, + ).persistance_length == persistance_length + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::legendre::test::base::all_parameters" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + @test all( + WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length, + ).number_of_links == number_of_links && + WLC(number_of_links, link_length, hinge_mass, persistance_length).link_length == + link_length && + WLC(number_of_links, link_length, hinge_mass, persistance_length).hinge_mass == + hinge_mass && + WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length, + ).persistance_length == persistance_length, + ) + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::legendre::test::nondimensional::helmholtz_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + nondimensional_helmholtz_free_energy = + model.nondimensional_helmholtz_free_energy(nondimensional_force, temperature) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + helmholtz_free_energy = model.helmholtz_free_energy(force, temperature) + residual_abs = + helmholtz_free_energy / BOLTZMANN_CONSTANT / temperature - + nondimensional_helmholtz_free_energy + residual_rel = residual_abs / nondimensional_helmholtz_free_energy + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::legendre::test::nondimensional::helmholtz_free_energy_per_link" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + nondimensional_helmholtz_free_energy_per_link = + model.nondimensional_helmholtz_free_energy_per_link( + nondimensional_force, + temperature, + ) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + helmholtz_free_energy_per_link = + model.helmholtz_free_energy_per_link(force, temperature) + residual_abs = + helmholtz_free_energy_per_link / BOLTZMANN_CONSTANT / temperature - + nondimensional_helmholtz_free_energy_per_link + residual_rel = residual_abs / nondimensional_helmholtz_free_energy_per_link + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::legendre::test::nondimensional::relative_helmholtz_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + nondimensional_relative_helmholtz_free_energy = + model.nondimensional_relative_helmholtz_free_energy(nondimensional_force) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + relative_helmholtz_free_energy = + model.relative_helmholtz_free_energy(force, temperature) + residual_abs = + relative_helmholtz_free_energy / BOLTZMANN_CONSTANT / temperature - + nondimensional_relative_helmholtz_free_energy + residual_rel = residual_abs / nondimensional_relative_helmholtz_free_energy + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::legendre::test::nondimensional::relative_helmholtz_free_energy_per_link" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + nondimensional_relative_helmholtz_free_energy_per_link = + model.nondimensional_relative_helmholtz_free_energy_per_link( + nondimensional_force, + ) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + relative_helmholtz_free_energy_per_link = + model.relative_helmholtz_free_energy_per_link(force, temperature) + residual_abs = + relative_helmholtz_free_energy_per_link / BOLTZMANN_CONSTANT / temperature - + nondimensional_relative_helmholtz_free_energy_per_link + residual_rel = residual_abs / nondimensional_relative_helmholtz_free_energy_per_link + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::legendre::test::per_link::helmholtz_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + helmholtz_free_energy = model.helmholtz_free_energy(force, temperature) + helmholtz_free_energy_per_link = + model.helmholtz_free_energy_per_link(force, temperature) + residual_abs = + helmholtz_free_energy / number_of_links - helmholtz_free_energy_per_link + residual_rel = residual_abs / helmholtz_free_energy_per_link + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::legendre::test::per_link::relative_helmholtz_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + relative_helmholtz_free_energy = + model.relative_helmholtz_free_energy(force, temperature) + relative_helmholtz_free_energy_per_link = + model.relative_helmholtz_free_energy_per_link(force, temperature) + residual_abs = + relative_helmholtz_free_energy / number_of_links - + relative_helmholtz_free_energy_per_link + residual_rel = residual_abs / relative_helmholtz_free_energy_per_link + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::legendre::test::per_link::nondimensional_helmholtz_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + nondimensional_helmholtz_free_energy = + model.nondimensional_helmholtz_free_energy(nondimensional_force, temperature) + nondimensional_helmholtz_free_energy_per_link = + model.nondimensional_helmholtz_free_energy_per_link( + nondimensional_force, + temperature, + ) + residual_abs = + nondimensional_helmholtz_free_energy / number_of_links - + nondimensional_helmholtz_free_energy_per_link + residual_rel = residual_abs / nondimensional_helmholtz_free_energy_per_link + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::legendre::test::per_link::nondimensional_relative_helmholtz_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + nondimensional_relative_helmholtz_free_energy = + model.nondimensional_relative_helmholtz_free_energy(nondimensional_force) + nondimensional_relative_helmholtz_free_energy_per_link = + model.nondimensional_relative_helmholtz_free_energy_per_link( + nondimensional_force, + ) + residual_abs = + nondimensional_relative_helmholtz_free_energy / number_of_links - + nondimensional_relative_helmholtz_free_energy_per_link + residual_rel = residual_abs / nondimensional_relative_helmholtz_free_energy_per_link + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::legendre::test::relative::helmholtz_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + helmholtz_free_energy = model.helmholtz_free_energy(force, temperature) + helmholtz_free_energy_0 = model.helmholtz_free_energy( + ZERO * BOLTZMANN_CONSTANT * temperature / link_length, + temperature, + ) + relative_helmholtz_free_energy = + model.relative_helmholtz_free_energy(force, temperature) + residual_abs = + helmholtz_free_energy - helmholtz_free_energy_0 - relative_helmholtz_free_energy + residual_rel = residual_abs / helmholtz_free_energy_0 + @test abs(residual_abs) <= + BOLTZMANN_CONSTANT * temperature * number_of_links * parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::legendre::test::relative::helmholtz_free_energy_per_link" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + helmholtz_free_energy_per_link = + model.helmholtz_free_energy_per_link(force, temperature) + helmholtz_free_energy_per_link_0 = model.helmholtz_free_energy_per_link( + ZERO * BOLTZMANN_CONSTANT * temperature / link_length, + temperature, + ) + relative_helmholtz_free_energy_per_link = + model.relative_helmholtz_free_energy_per_link(force, temperature) + residual_abs = + helmholtz_free_energy_per_link - helmholtz_free_energy_per_link_0 - + relative_helmholtz_free_energy_per_link + residual_rel = residual_abs / helmholtz_free_energy_per_link_0 + @test abs(residual_abs) <= BOLTZMANN_CONSTANT * temperature * parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::legendre::test::relative::nondimensional_helmholtz_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + nondimensional_helmholtz_free_energy = + model.nondimensional_helmholtz_free_energy(nondimensional_force, temperature) + nondimensional_helmholtz_free_energy_0 = + model.nondimensional_helmholtz_free_energy(ZERO, temperature) + nondimensional_relative_helmholtz_free_energy = + model.nondimensional_relative_helmholtz_free_energy(nondimensional_force) + residual_abs = + nondimensional_helmholtz_free_energy - nondimensional_helmholtz_free_energy_0 - + nondimensional_relative_helmholtz_free_energy + residual_rel = residual_abs / nondimensional_helmholtz_free_energy_0 + @test abs(residual_abs) <= number_of_links * parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::legendre::test::relative::nondimensional_helmholtz_free_energy_per_link" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + nondimensional_helmholtz_free_energy_per_link = + model.nondimensional_helmholtz_free_energy_per_link( + nondimensional_force, + temperature, + ) + nondimensional_helmholtz_free_energy_per_link_0 = + model.nondimensional_helmholtz_free_energy_per_link(ZERO, temperature) + nondimensional_relative_helmholtz_free_energy_per_link = + model.nondimensional_relative_helmholtz_free_energy_per_link( + nondimensional_force, + ) + residual_abs = + nondimensional_helmholtz_free_energy_per_link - + nondimensional_helmholtz_free_energy_per_link_0 - + nondimensional_relative_helmholtz_free_energy_per_link + residual_rel = residual_abs / nondimensional_helmholtz_free_energy_per_link_0 + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::legendre::test::zero::relative_helmholtz_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + relative_helmholtz_free_energy_0 = model.relative_helmholtz_free_energy( + ZERO * BOLTZMANN_CONSTANT * temperature / link_length, + temperature, + ) + @test abs(relative_helmholtz_free_energy_0) <= + ZERO * BOLTZMANN_CONSTANT * temperature * number_of_links + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::legendre::test::zero::relative_helmholtz_free_energy_per_link" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + relative_helmholtz_free_energy_per_link_0 = + model.relative_helmholtz_free_energy_per_link( + ZERO * BOLTZMANN_CONSTANT * temperature / link_length, + temperature, + ) + @test abs(relative_helmholtz_free_energy_per_link_0) <= + ZERO * BOLTZMANN_CONSTANT * temperature + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::legendre::test::zero::nondimensional_relative_helmholtz_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_relative_helmholtz_free_energy_0 = + model.nondimensional_relative_helmholtz_free_energy(ZERO) + @test abs(nondimensional_relative_helmholtz_free_energy_0) <= ZERO * number_of_links + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::legendre::test::zero::nondimensional_relative_helmholtz_free_energy_per_link" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_relative_helmholtz_free_energy_per_link_0 = + model.nondimensional_relative_helmholtz_free_energy_per_link(ZERO) + @test abs(nondimensional_relative_helmholtz_free_energy_per_link_0) <= ZERO + end +end + +end diff --git a/src/physics/single_chain/wlc/thermodynamics/isotensional/legendre/test.py b/src/physics/single_chain/wlc/thermodynamics/isotensional/legendre/test.py new file mode 100644 index 00000000..5ddc4f47 --- /dev/null +++ b/src/physics/single_chain/wlc/thermodynamics/isotensional/legendre/test.py @@ -0,0 +1,1051 @@ +"""Module to test the local module. + +""" +import unittest +import numpy as np +from polymers import physics +from ..test import Parameters + +parameters = Parameters() +WLC = physics.single_chain.wlc.thermodynamics.isotensional.legendre.WLC + + +class Base(unittest.TestCase): + """Class for basic tests. + + """ + def test_init(self): + """Function to test instantiation. + + """ + for _ in range(parameters.number_of_loops): + _ = WLC( + parameters.number_of_links_minimum, + parameters.link_length_reference, + parameters.hinge_mass_reference, + parameters.persistance_length_reference + ) + + def test_number_of_links(self): + """Function to test the number of links during instantiation. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + self.assertEqual( + number_of_links, + WLC( + number_of_links, + parameters.link_length_reference, + parameters.hinge_mass_reference, + parameters.persistance_length_reference + ).number_of_links + ) + + def test_link_length(self): + """Function to test the link length during instantiation. + + """ + for _ in range(parameters.number_of_loops): + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + self.assertEqual( + link_length, + WLC( + parameters.number_of_links_minimum, + link_length, + parameters.hinge_mass_reference, + parameters.persistance_length_reference + ).link_length + ) + + def test_hinge_mass(self): + """Function to test the hinge mass during instantiation. + + """ + for _ in range(parameters.number_of_loops): + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + self.assertEqual( + hinge_mass, + WLC( + parameters.number_of_links_minimum, + parameters.link_length_reference, + hinge_mass, + parameters.persistance_length_reference + ).hinge_mass + ) + + def test_persistance_length(self): + """Function to test the persistance length during instantiation. + + """ + for _ in range(parameters.number_of_loops): + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + self.assertEqual( + persistance_length, + WLC( + parameters.number_of_links_minimum, + parameters.link_length_reference, + parameters.hinge_mass_reference, + persistance_length + ).persistance_length + ) + + def test_all_parameters(self): + """Function to test all parameters during instantiation. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + self.assertEqual( + number_of_links, + model.number_of_links + ) + self.assertEqual( + link_length, + model.link_length + ) + self.assertEqual( + hinge_mass, + model.hinge_mass, + persistance_length + ) + self.assertEqual( + persistance_length, + model.persistance_length + ) + + +class Nondimensional(unittest.TestCase): + """Class for nondimensionalization tests. + + """ + def test_helmholtz_free_energy(self): + """Function to test the nondimensionalization + of the Helmholtz free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + nondimensional_helmholtz_free_energy = \ + model.nondimensional_helmholtz_free_energy( + np.array(nondimensional_force), + temperature + ) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + helmholtz_free_energy = \ + model.helmholtz_free_energy( + np.array(force), + temperature + ) + residual_abs = \ + helmholtz_free_energy / \ + parameters.boltzmann_constant/temperature \ + - nondimensional_helmholtz_free_energy + residual_rel = \ + residual_abs / \ + nondimensional_helmholtz_free_energy + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + def test_helmholtz_free_energy_per_link(self): + """Function to test the nondimensionalization + of the Helmholtz free energy per link. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + nondimensional_helmholtz_free_energy = \ + model.nondimensional_helmholtz_free_energy_per_link( + np.array(nondimensional_force), + temperature + ) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + helmholtz_free_energy = \ + model.helmholtz_free_energy_per_link( + np.array(force), + temperature + ) + residual_abs = \ + helmholtz_free_energy / \ + parameters.boltzmann_constant/temperature \ + - nondimensional_helmholtz_free_energy + residual_rel = \ + residual_abs / \ + nondimensional_helmholtz_free_energy + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + def test_relative_helmholtz_free_energy(self): + """Function to test the nondimensionalization + of the relative Helmholtz free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + nondimensional_helmholtz_free_energy = \ + model.nondimensional_relative_helmholtz_free_energy( + np.array(nondimensional_force) + ) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + helmholtz_free_energy = \ + model.relative_helmholtz_free_energy( + np.array(force), + temperature + ) + residual_abs = \ + helmholtz_free_energy / \ + parameters.boltzmann_constant/temperature \ + - nondimensional_helmholtz_free_energy + residual_rel = \ + residual_abs / \ + nondimensional_helmholtz_free_energy + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + def test_relative_helmholtz_free_energy_per_link(self): + """Function to test the nondimensionalization + of the relative Helmholtz free energy per link. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + nondimensional_helmholtz_free_energy = \ + model.nondimensional_relative_helmholtz_free_energy_per_link( + np.array(nondimensional_force) + ) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + helmholtz_free_energy = \ + model.relative_helmholtz_free_energy_per_link( + np.array(force), + temperature + ) + residual_abs = \ + helmholtz_free_energy / \ + parameters.boltzmann_constant/temperature \ + - nondimensional_helmholtz_free_energy + residual_rel = \ + residual_abs / \ + nondimensional_helmholtz_free_energy + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + +class PerLink(unittest.TestCase): + """Class for per-linkness tests. + + """ + def test_helmholtz_free_energy(self): + """Function to test the per-linkness + of the Helmholtz free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + helmholtz_free_energy = \ + model.helmholtz_free_energy( + np.array(force), + temperature + ) + helmholtz_free_energy_per_link = \ + model.helmholtz_free_energy_per_link( + np.array(force), + temperature + ) + residual_abs = \ + helmholtz_free_energy / \ + number_of_links \ + - helmholtz_free_energy_per_link + residual_rel = \ + residual_abs / \ + helmholtz_free_energy_per_link + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + def test_relative_helmholtz_free_energy(self): + """Function to test the per-linkness + of the relative Helmholtz free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + relative_helmholtz_free_energy = \ + model.relative_helmholtz_free_energy( + np.array(force), + temperature + ) + relative_helmholtz_free_energy_per_link = \ + model.relative_helmholtz_free_energy_per_link( + np.array(force), + temperature + ) + residual_abs = \ + relative_helmholtz_free_energy / \ + number_of_links \ + - relative_helmholtz_free_energy_per_link + residual_rel = \ + residual_abs / \ + relative_helmholtz_free_energy_per_link + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + def test_nondimensional_helmholtz_free_energy(self): + """Function to test the per-linkness + of the nondimensional Helmholtz free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + nondimensional_helmholtz_free_energy = \ + model.nondimensional_helmholtz_free_energy( + np.array(nondimensional_force), + temperature + ) + nondimensional_helmholtz_free_energy_per_link = \ + model.nondimensional_helmholtz_free_energy_per_link( + np.array(nondimensional_force), + temperature + ) + residual_abs = \ + nondimensional_helmholtz_free_energy / \ + number_of_links \ + - nondimensional_helmholtz_free_energy_per_link + residual_rel = \ + residual_abs / \ + nondimensional_helmholtz_free_energy_per_link + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + def test_nondimensional_relative_helmholtz_free_energy(self): + """Function to test the per-linkness + of the nondimensional relative Helmholtz free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + nondimensional_relative_helmholtz_free_energy = \ + model.nondimensional_relative_helmholtz_free_energy( + np.array(nondimensional_force) + ) + nondimensional_relative_helmholtz_free_energy_per_link = \ + model.nondimensional_relative_helmholtz_free_energy_per_link( + np.array(nondimensional_force) + ) + residual_abs = \ + nondimensional_relative_helmholtz_free_energy / \ + number_of_links \ + - nondimensional_relative_helmholtz_free_energy_per_link + residual_rel = \ + residual_abs / \ + nondimensional_relative_helmholtz_free_energy_per_link + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + +class Relative(unittest.TestCase): + """Class for relativeness tests. + + """ + def test_helmholtz_free_energy(self): + """Function to test the relativeness + of the Helmholtz free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + helmholtz_free_energy = \ + model.helmholtz_free_energy( + np.array(force), + temperature + ) + helmholtz_free_energy_0 = \ + model.helmholtz_free_energy( + np.array( + parameters.zero * + parameters.boltzmann_constant*temperature/link_length + ), + temperature + ) + relative_helmholtz_free_energy = \ + model.relative_helmholtz_free_energy( + np.array(force), + temperature + ) + residual_abs = \ + helmholtz_free_energy \ + - helmholtz_free_energy_0 \ + - relative_helmholtz_free_energy + residual_rel = \ + residual_abs / \ + helmholtz_free_energy_0 + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol * + parameters.boltzmann_constant*temperature/link_length + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + def test_helmholtz_free_energy_per_link(self): + """Function to test the relativeness + of the Helmholtz free energy per link. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + helmholtz_free_energy_per_link = \ + model.helmholtz_free_energy_per_link( + np.array(force), + temperature + ) + helmholtz_free_energy_per_link_0 = \ + model.helmholtz_free_energy_per_link( + np.array( + parameters.zero * + parameters.boltzmann_constant*temperature/link_length + ), + temperature + ) + relative_helmholtz_free_energy_per_link = \ + model.relative_helmholtz_free_energy_per_link( + np.array(force), + temperature + ) + residual_abs = \ + helmholtz_free_energy_per_link \ + - helmholtz_free_energy_per_link_0 \ + - relative_helmholtz_free_energy_per_link + residual_rel = \ + residual_abs / \ + helmholtz_free_energy_per_link_0 + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol * + parameters.boltzmann_constant*temperature/link_length + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + def test_nondimensional_helmholtz_free_energy(self): + """Function to test the relativeness + of the nondimensional Helmholtz free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + nondimensional_helmholtz_free_energy = \ + model.nondimensional_helmholtz_free_energy( + np.array(nondimensional_force), + temperature + ) + nondimensional_helmholtz_free_energy_0 = \ + model.nondimensional_helmholtz_free_energy( + np.array(parameters.zero), + temperature + ) + nondimensional_relative_helmholtz_free_energy = \ + model.nondimensional_relative_helmholtz_free_energy( + np.array(nondimensional_force) + ) + residual_abs = \ + nondimensional_helmholtz_free_energy \ + - nondimensional_helmholtz_free_energy_0 \ + - nondimensional_relative_helmholtz_free_energy + residual_rel = \ + residual_abs / \ + nondimensional_helmholtz_free_energy_0 + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + def test_nondimensional_helmholtz_free_energy_per_link(self): + """Function to test the relativeness + of the nondimensional Helmholtz free energy per link. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + nondimensional_helmholtz_free_energy_per_link = \ + model.nondimensional_helmholtz_free_energy_per_link( + np.array(nondimensional_force), + temperature + ) + nondimensional_helmholtz_free_energy_per_link_0 = \ + model.nondimensional_helmholtz_free_energy_per_link( + np.array(parameters.zero), + temperature + ) + nondimensional_relative_helmholtz_free_energy_per_link = \ + model.nondimensional_relative_helmholtz_free_energy_per_link( + np.array(nondimensional_force) + ) + residual_abs = \ + nondimensional_helmholtz_free_energy_per_link \ + - nondimensional_helmholtz_free_energy_per_link_0 \ + - nondimensional_relative_helmholtz_free_energy_per_link + residual_rel = \ + residual_abs / \ + nondimensional_helmholtz_free_energy_per_link_0 + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + +class Zero(unittest.TestCase): + """Class for zero tests. + + """ + def test_relative_helmholtz_free_energy(self): + """Function to test the zero + of the relative Helmholtz free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + relative_helmholtz_free_energy_0 = \ + model.relative_helmholtz_free_energy( + np.array( + parameters.zero * + parameters.boltzmann_constant*temperature/link_length + ), + temperature + ) + self.assertLessEqual( + relative_helmholtz_free_energy_0, + parameters.abs_tol * + parameters.boltzmann_constant*temperature + ) + + def test_relative_helmholtz_free_energy_per_link(self): + """Function to test the zero + of the relative Helmholtz free energy per link. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + relative_helmholtz_free_energy_per_link_0 = \ + model.relative_helmholtz_free_energy_per_link( + np.array( + parameters.zero * + parameters.boltzmann_constant*temperature/link_length + ), + temperature + ) + self.assertLessEqual( + relative_helmholtz_free_energy_per_link_0, + parameters.abs_tol * + parameters.boltzmann_constant*temperature + ) + + def test_nondimensional_relative_helmholtz_free_energy(self): + """Function to test the zero + of the relative Helmholtz free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_relative_helmholtz_free_energy_0 = \ + model.nondimensional_relative_helmholtz_free_energy( + np.array(parameters.zero) + ) + self.assertLessEqual( + nondimensional_relative_helmholtz_free_energy_0, + parameters.abs_tol + ) + + def test_nondimensional_relative_helmholtz_free_energy_per_link(self): + """Function to test the zero + of the nondimensional relative Helmholtz free energy per link. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_relative_helmholtz_free_energy_per_link_0 = \ + model.nondimensional_relative_helmholtz_free_energy_per_link( + np.array(parameters.zero) + ) + self.assertLessEqual( + nondimensional_relative_helmholtz_free_energy_per_link_0, + parameters.abs_tol + ) diff --git a/src/physics/single_chain/wlc/thermodynamics/isotensional/legendre/test.rs b/src/physics/single_chain/wlc/thermodynamics/isotensional/legendre/test.rs new file mode 100644 index 00000000..11349107 --- /dev/null +++ b/src/physics/single_chain/wlc/thermodynamics/isotensional/legendre/test.rs @@ -0,0 +1,439 @@ +#![cfg(test)] +use super::*; +use crate::physics::single_chain::test::Parameters; +mod base +{ + use super::*; + use rand::Rng; + #[test] + fn init() + { + let parameters = Parameters::default(); + let _ = WLC::init(parameters.number_of_links_minimum, parameters.link_length_reference, parameters.hinge_mass_reference, parameters.persistance_length_reference); + } + #[test] + fn number_of_links() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + assert_eq!(number_of_links, WLC::init(number_of_links, parameters.link_length_reference, parameters.hinge_mass_reference, parameters.persistance_length_reference).number_of_links); + } + } + #[test] + fn link_length() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + assert_eq!(link_length, WLC::init(parameters.number_of_links_minimum, link_length, parameters.hinge_mass_reference, parameters.persistance_length_reference).link_length); + } + } + #[test] + fn hinge_mass() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + assert_eq!(hinge_mass, WLC::init(parameters.number_of_links_minimum, parameters.link_length_reference, hinge_mass, parameters.persistance_length_reference).hinge_mass); + } + } + #[test] + fn persistance_length() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + assert_eq!(persistance_length, WLC::init(parameters.number_of_links_minimum, parameters.link_length_reference, parameters.hinge_mass_reference, persistance_length).persistance_length); + } + } + #[test] + fn all_parameters() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + assert_eq!(number_of_links, model.number_of_links); + assert_eq!(link_length, model.link_length); + assert_eq!(hinge_mass, model.hinge_mass); + assert_eq!(persistance_length, model.persistance_length); + } + } +} +mod nondimensional +{ + use super::*; + use rand::Rng; + #[test] + fn helmholtz_free_energy() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let nondimensional_helmholtz_free_energy = model.nondimensional_helmholtz_free_energy(&nondimensional_force, &temperature); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let helmholtz_free_energy = model.helmholtz_free_energy(&force, &temperature); + let residual_abs = &helmholtz_free_energy/BOLTZMANN_CONSTANT/temperature - &nondimensional_helmholtz_free_energy; + let residual_rel = &residual_abs/&nondimensional_helmholtz_free_energy; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } + #[test] + fn helmholtz_free_energy_per_link() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let nondimensional_helmholtz_free_energy_per_link = model.nondimensional_helmholtz_free_energy_per_link(&nondimensional_force, &temperature); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let helmholtz_free_energy_per_link = model.helmholtz_free_energy_per_link(&force, &temperature); + let residual_abs = &helmholtz_free_energy_per_link/BOLTZMANN_CONSTANT/temperature - &nondimensional_helmholtz_free_energy_per_link; + let residual_rel = &residual_abs/&nondimensional_helmholtz_free_energy_per_link; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } + #[test] + fn relative_helmholtz_free_energy() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let nondimensional_relative_helmholtz_free_energy = model.nondimensional_relative_helmholtz_free_energy(&nondimensional_force); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let relative_helmholtz_free_energy = model.relative_helmholtz_free_energy(&force, &temperature); + let residual_abs = &relative_helmholtz_free_energy/BOLTZMANN_CONSTANT/temperature - &nondimensional_relative_helmholtz_free_energy; + let residual_rel = &residual_abs/&nondimensional_relative_helmholtz_free_energy; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } + #[test] + fn relative_helmholtz_free_energy_per_link() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let nondimensional_relative_helmholtz_free_energy_per_link = model.nondimensional_relative_helmholtz_free_energy_per_link(&nondimensional_force); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let relative_helmholtz_free_energy_per_link = model.relative_helmholtz_free_energy_per_link(&force, &temperature); + let residual_abs = &relative_helmholtz_free_energy_per_link/BOLTZMANN_CONSTANT/temperature - &nondimensional_relative_helmholtz_free_energy_per_link; + let residual_rel = &residual_abs/&nondimensional_relative_helmholtz_free_energy_per_link; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } +} +mod per_link +{ + use super::*; + use rand::Rng; + #[test] + fn helmholtz_free_energy() + { + let parameters = Parameters::default(); + let mut rng = rand::thread_rng(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let helmholtz_free_energy = model.helmholtz_free_energy(&force, &temperature); + let helmholtz_free_energy_per_link = model.helmholtz_free_energy_per_link(&force, &temperature); + let residual_abs = &helmholtz_free_energy/(number_of_links as f64) - &helmholtz_free_energy_per_link; + let residual_rel = &residual_abs/&helmholtz_free_energy_per_link; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } + #[test] + fn relative_helmholtz_free_energy() + { + let parameters = Parameters::default(); + let mut rng = rand::thread_rng(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let relative_helmholtz_free_energy = model.relative_helmholtz_free_energy(&force, &temperature); + let relative_helmholtz_free_energy_per_link = model.relative_helmholtz_free_energy_per_link(&force, &temperature); + let residual_abs = &relative_helmholtz_free_energy/(number_of_links as f64) - &relative_helmholtz_free_energy_per_link; + let residual_rel = &residual_abs/&relative_helmholtz_free_energy_per_link; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } + #[test] + fn nondimensional_helmholtz_free_energy() + { + let parameters = Parameters::default(); + let mut rng = rand::thread_rng(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let nondimensional_helmholtz_free_energy = model.nondimensional_helmholtz_free_energy(&nondimensional_force, &temperature); + let nondimensional_helmholtz_free_energy_per_link = model.nondimensional_helmholtz_free_energy_per_link(&nondimensional_force, &temperature); + let residual_abs = &nondimensional_helmholtz_free_energy/(number_of_links as f64) - &nondimensional_helmholtz_free_energy_per_link; + let residual_rel = &residual_abs/&nondimensional_helmholtz_free_energy_per_link; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } + #[test] + fn nondimensional_relative_helmholtz_free_energy() + { + let parameters = Parameters::default(); + let mut rng = rand::thread_rng(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let nondimensional_relative_helmholtz_free_energy = model.nondimensional_relative_helmholtz_free_energy(&nondimensional_force); + let nondimensional_relative_helmholtz_free_energy_per_link = model.nondimensional_relative_helmholtz_free_energy_per_link(&nondimensional_force); + let residual_abs = &nondimensional_relative_helmholtz_free_energy/(number_of_links as f64) - &nondimensional_relative_helmholtz_free_energy_per_link; + let residual_rel = &residual_abs/&nondimensional_relative_helmholtz_free_energy_per_link; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } +} +mod relative +{ + use super::*; + use crate::physics::single_chain::ZERO; + use rand::Rng; + #[test] + fn helmholtz_free_energy() + { + let parameters = Parameters::default(); + let mut rng = rand::thread_rng(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let helmholtz_free_energy = model.helmholtz_free_energy(&force, &temperature); + let helmholtz_free_energy_0 = model.helmholtz_free_energy(&(ZERO*BOLTZMANN_CONSTANT*temperature/link_length), &temperature); + let relative_helmholtz_free_energy = model.relative_helmholtz_free_energy(&force, &temperature); + let residual_abs = &helmholtz_free_energy - &helmholtz_free_energy_0 - &relative_helmholtz_free_energy; + let residual_rel = &residual_abs/&helmholtz_free_energy_0; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } + #[test] + fn helmholtz_free_energy_per_link() + { + let parameters = Parameters::default(); + let mut rng = rand::thread_rng(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let helmholtz_free_energy_per_link = model.helmholtz_free_energy_per_link(&force, &temperature); + let helmholtz_free_energy_per_link_0 = model.helmholtz_free_energy_per_link(&(ZERO*BOLTZMANN_CONSTANT*temperature/link_length), &temperature); + let relative_helmholtz_free_energy_per_link = model.relative_helmholtz_free_energy_per_link(&force, &temperature); + let residual_abs = &helmholtz_free_energy_per_link - &helmholtz_free_energy_per_link_0 - &relative_helmholtz_free_energy_per_link; + let residual_rel = &residual_abs/&helmholtz_free_energy_per_link_0; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } + #[test] + fn nondimensional_helmholtz_free_energy() + { + let parameters = Parameters::default(); + let mut rng = rand::thread_rng(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let nondimensional_helmholtz_free_energy = model.nondimensional_helmholtz_free_energy(&nondimensional_force, &temperature); + let nondimensional_helmholtz_free_energy_0 = model.nondimensional_helmholtz_free_energy(&ZERO, &temperature); + let nondimensional_relative_helmholtz_free_energy = model.nondimensional_relative_helmholtz_free_energy(&nondimensional_force); + let residual_abs = &nondimensional_helmholtz_free_energy - &nondimensional_helmholtz_free_energy_0 - &nondimensional_relative_helmholtz_free_energy; + let residual_rel = &residual_abs/&nondimensional_helmholtz_free_energy_0; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } + #[test] + fn nondimensional_helmholtz_free_energy_per_link() + { + let parameters = Parameters::default(); + let mut rng = rand::thread_rng(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let nondimensional_helmholtz_free_energy_per_link = model.nondimensional_helmholtz_free_energy_per_link(&nondimensional_force, &temperature); + let nondimensional_helmholtz_free_energy_per_link_0 = model.nondimensional_helmholtz_free_energy_per_link(&ZERO, &temperature); + let nondimensional_relative_helmholtz_free_energy_per_link = model.nondimensional_relative_helmholtz_free_energy_per_link(&nondimensional_force); + let residual_abs = &nondimensional_helmholtz_free_energy_per_link - &nondimensional_helmholtz_free_energy_per_link_0 - &nondimensional_relative_helmholtz_free_energy_per_link; + let residual_rel = &residual_abs/&nondimensional_helmholtz_free_energy_per_link_0; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } +} +mod zero +{ + use super::*; + use crate::physics::single_chain::ZERO; + use rand::Rng; + #[test] + fn relative_helmholtz_free_energy() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let relative_helmholtz_free_energy_0 = model.relative_helmholtz_free_energy(&ZERO, &temperature); + assert!(relative_helmholtz_free_energy_0.abs() <= (number_of_links as f64)*BOLTZMANN_CONSTANT*temperature*ZERO); + } + } + #[test] + fn relative_helmholtz_free_energy_per_link() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let relative_helmholtz_free_energy_per_link_0 = model.relative_helmholtz_free_energy_per_link(&ZERO, &temperature); + assert!(relative_helmholtz_free_energy_per_link_0.abs() <= BOLTZMANN_CONSTANT*temperature*ZERO); + } + } + #[test] + fn nondimensional_relative_helmholtz_free_energy() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_relative_helmholtz_free_energy_0 = model.nondimensional_relative_helmholtz_free_energy(&ZERO); + assert!(nondimensional_relative_helmholtz_free_energy_0.abs() <= ZERO); + } + } + #[test] + fn nondimensional_relative_helmholtz_free_energy_per_link() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_relative_helmholtz_free_energy_per_link_0 = model.nondimensional_relative_helmholtz_free_energy_per_link(&ZERO); + assert!(nondimensional_relative_helmholtz_free_energy_per_link_0.abs() <= ZERO); + } + } +} \ No newline at end of file diff --git a/src/physics/single_chain/wlc/thermodynamics/isotensional/mod.jl b/src/physics/single_chain/wlc/thermodynamics/isotensional/mod.jl new file mode 100644 index 00000000..1061a353 --- /dev/null +++ b/src/physics/single_chain/wlc/thermodynamics/isotensional/mod.jl @@ -0,0 +1,651 @@ +""" +The worm-like chain (WLC) model thermodynamics in the isotensional ensemble. +""" +module Isotensional + +using DocStringExtensions +using ......Polymers: PROJECT_ROOT + +include("legendre/mod.jl") + +""" +The structure of the thermodynamics of the WLC model in the isotensional ensemble. + +$(FIELDS) +""" +struct WLC + """ + The number of links in the chain ``N_b``. + """ + number_of_links::UInt8 + """ + The length of each link in the chain ``\\ell_b`` in units of nm. + """ + link_length::Float64 + """ + The mass of each hinge in the chain ``m`` in units of kg/mol. + """ + hinge_mass::Float64 + """ + The persistance length of the chain in units of nm. + """ + persistance_length::Float64 + nondimensional_persistance_length::Float64 + """ + The thermodynamic functions of the model in the isotensional ensemble approximated using a Legendre transformation. + """ + legendre::Any + """ + The expected force ``f`` as a function of the applied force ``f`` and temperature ``T``. + """ + end_to_end_length::Function + """ + The expected end-to-end length per link ``\\xi/N_b=\\ell_b\\gamma`` as a function of the applied force ``f`` and temperature ``T``. + """ + end_to_end_length_per_link::Function + """ + The expected nondimensional end-to-end length ``N_b\\gamma=\\xi/\\ell_b`` as a function of the applied nondimensional force ``\\eta``. + """ + nondimensional_end_to_end_length::Function + """ + The expected nondimensional end-to-end length per link ``\\gamma\\equiv\\xi/N_b\\ell_b`` as a function of the applied nondimensional force ``\\eta``. + """ + nondimensional_end_to_end_length_per_link::Function + """ + The Gibbs free energy ``\\varphi`` as a function of the applied force ``f`` and temperature ``T``. + """ + gibbs_free_energy::Function + """ + The Gibbs free energy per link ``\\varphi/N_b`` as a function of the applied force ``f`` and temperature ``T``. + """ + gibbs_free_energy_per_link::Function + """ + The relative Gibbs free energy ``\\Delta\\varphi\\equiv\\varphi(f,T)-\\varphi(0,T)`` as a function of the applied force ``f`` and temperature ``T``. + """ + relative_gibbs_free_energy::Function + """ + The relative Gibbs free energy per link ``\\Delta\\varphi/N_b`` as a function of the applied force ``f`` and temperature ``T``. + """ + relative_gibbs_free_energy_per_link::Function + """ + The nondimensional Gibbs free energy ``N_b\\varrho=\\beta\\varphi`` as a function of the applied nondimensional force ``\\eta`` and temperature ``T``. + """ + nondimensional_gibbs_free_energy::Function + """ + The nondimensional Gibbs free energy per link ``\\varrho\\equiv\\beta\\varphi/N_b`` as a function of the applied nondimensional force ``\\eta`` and temperature ``T``. + """ + nondimensional_gibbs_free_energy_per_link::Function + """ + The nondimensional relative Gibbs free energy ``N_b\\Delta\\varrho=\\beta\\Delta\\varphi`` as a function of the applied nondimensional force ``\\eta``. + """ + nondimensional_relative_gibbs_free_energy::Function + """ + The nondimensional relative Gibbs free energy per link ``\\Delta\\varrho\\equiv\\beta\\Delta\\varphi/N_b`` as a function of the applied nondimensional force ``\\eta``. + """ + nondimensional_relative_gibbs_free_energy_per_link::Function +end + +""" +The expected force ``f`` as a function of the applied force ``f`` and temperature ``T``, +parameterized by the number of links ``N_b``, link length ``\\ell_b``, and persistance length ``\\ell_p``. + +$(TYPEDSIGNATURES) +""" +function end_to_end_length( + number_of_links::Union{UInt8,Vector,Matrix,Array}, + link_length::Union{Float64,Vector,Matrix,Array}, + persistance_length::Union{Float64,Vector,Matrix,Array}, + force::Union{Float64,Vector,Matrix,Array}, + temperature::Union{Float64,Vector,Matrix,Array}, +)::Union{Float64,Vector,Matrix,Array} + return broadcast( + (number_of_links_i, link_length_i, persistance_length_i, force_i, temperature_i) -> + ccall( + ( + :physics_single_chain_wlc_thermodynamics_isotensional_end_to_end_length, + string(PROJECT_ROOT, "target/debug/libpolymers"), + ), + Float64, + (UInt8, Float64, Float64, Float64, Float64), + number_of_links_i, + link_length_i, + persistance_length_i, + force_i, + temperature_i, + ), + number_of_links, + link_length, + persistance_length, + force, + temperature, + ) +end + +""" +The expected end-to-end length per link ``\\xi/N_b=\\ell_b\\gamma`` as a function of the applied force ``f`` and temperature ``T``, +parameterized by the number of links ``N_b``, link length ``\\ell_b``, and persistance length ``\\ell_p``. + +$(TYPEDSIGNATURES) +""" +function end_to_end_length_per_link( + number_of_links::Union{UInt8,Vector,Matrix,Array}, + link_length::Union{Float64,Vector,Matrix,Array}, + persistance_length::Union{Float64,Vector,Matrix,Array}, + force::Union{Float64,Vector,Matrix,Array}, + temperature::Union{Float64,Vector,Matrix,Array}, +)::Union{Float64,Vector,Matrix,Array} + return broadcast( + (number_of_links_i, link_length_i, persistance_length_i, force_i, temperature_i) -> + ccall( + ( + :physics_single_chain_wlc_thermodynamics_isotensional_end_to_end_length_per_link, + string(PROJECT_ROOT, "target/debug/libpolymers"), + ), + Float64, + (UInt8, Float64, Float64, Float64, Float64), + number_of_links_i, + link_length_i, + persistance_length_i, + force_i, + temperature_i, + ), + number_of_links, + link_length, + persistance_length, + force, + temperature, + ) +end + +""" +The expected nondimensional end-to-end length ``N_b\\gamma\\equiv\\xi/\\ell_b`` as a function of the applied nondimensional force ``\\eta``, +parameterized by the number of links ``N_b`` and nondimensional persistance length ``\\zeta``. + +$(TYPEDSIGNATURES) +""" +function nondimensional_end_to_end_length( + number_of_links::Union{UInt8,Vector,Matrix,Array}, + nondimensional_persistance_length::Union{Float64,Vector,Matrix,Array}, + nondimensional_force::Union{Float64,Vector,Matrix,Array}, +)::Union{Float64,Vector,Matrix,Array} + return broadcast( + (number_of_links_i, nondimensional_persistance_length_i, nondimensional_force_i) -> + ccall( + ( + :physics_single_chain_wlc_thermodynamics_isotensional_nondimensional_end_to_end_length, + string(PROJECT_ROOT, "target/debug/libpolymers"), + ), + Float64, + (UInt8, Float64, Float64), + number_of_links_i, + nondimensional_persistance_length_i, + nondimensional_force_i, + ), + number_of_links, + nondimensional_persistance_length, + nondimensional_force, + ) +end + +""" +The expected nondimensional end-to-end length per link ``\\gamma\\equiv\\xi/N_b\\ell_b`` as a function of the applied nondimensional force ``\\eta``, +parameterized by the number of links ``N_b`` and nondimensional persistance length ``\\zeta``. + +$(TYPEDSIGNATURES) +""" +function nondimensional_end_to_end_length_per_link( + number_of_links::Union{UInt8,Vector,Matrix,Array}, + nondimensional_persistance_length::Union{Float64,Vector,Matrix,Array}, + nondimensional_force::Union{Float64,Vector,Matrix,Array}, +)::Union{Float64,Vector,Matrix,Array} + return broadcast( + (number_of_links_i, nondimensional_persistance_length_i, nondimensional_force_i) -> + ccall( + ( + :physics_single_chain_wlc_thermodynamics_isotensional_nondimensional_end_to_end_length_per_link, + string(PROJECT_ROOT, "target/debug/libpolymers"), + ), + Float64, + (UInt8, Float64, Float64), + number_of_links_i, + nondimensional_persistance_length_i, + nondimensional_force_i, + ), + number_of_links, + nondimensional_persistance_length, + nondimensional_force, + ) +end + +""" +The Gibbs free energy ``\\varphi`` as a function of the applied force ``f`` and temperature ``T``, +parameterized by the number of links ``N_b``, link length ``\\ell_b``, hinge mass ``m``, and persistance length ``\\ell_p``. + +$(TYPEDSIGNATURES) +""" +function gibbs_free_energy( + number_of_links::Union{UInt8,Vector,Matrix,Array}, + link_length::Union{Float64,Vector,Matrix,Array}, + hinge_mass::Union{Float64,Vector,Matrix,Array}, + persistance_length::Union{Float64,Vector,Matrix,Array}, + end_to_end_length::Union{Float64,Vector,Matrix,Array}, + temperature::Union{Float64,Vector,Matrix,Array}, +)::Union{Float64,Vector,Matrix,Array} + return broadcast( + ( + number_of_links_i, + link_length_i, + hinge_mass_i, + persistance_length_i, + end_to_end_length_i, + temperature_i, + ) -> ccall( + ( + :physics_single_chain_wlc_thermodynamics_isotensional_gibbs_free_energy, + string(PROJECT_ROOT, "target/debug/libpolymers"), + ), + Float64, + (UInt8, Float64, Float64, Float64, Float64, Float64), + number_of_links_i, + link_length_i, + hinge_mass_i, + persistance_length_i, + end_to_end_length_i, + temperature_i, + ), + number_of_links, + link_length, + hinge_mass, + persistance_length, + end_to_end_length, + temperature, + ) +end + +""" +The Gibbs free energy per link ``\\varphi/N_b`` as a function of the applied force ``f`` and temperature ``T``, +parameterized by the number of links ``N_b``, link length ``\\ell_b``, hinge mass ``m``, and persistance length ``\\ell_p``. + +$(TYPEDSIGNATURES) +""" +function gibbs_free_energy_per_link( + number_of_links::Union{UInt8,Vector,Matrix,Array}, + link_length::Union{Float64,Vector,Matrix,Array}, + hinge_mass::Union{Float64,Vector,Matrix,Array}, + persistance_length::Union{Float64,Vector,Matrix,Array}, + end_to_end_length::Union{Float64,Vector,Matrix,Array}, + temperature::Union{Float64,Vector,Matrix,Array}, +)::Union{Float64,Vector,Matrix,Array} + return broadcast( + ( + number_of_links_i, + link_length_i, + hinge_mass_i, + persistance_length_i, + end_to_end_length_i, + temperature_i, + ) -> ccall( + ( + :physics_single_chain_wlc_thermodynamics_isotensional_gibbs_free_energy_per_link, + string(PROJECT_ROOT, "target/debug/libpolymers"), + ), + Float64, + (UInt8, Float64, Float64, Float64, Float64, Float64), + number_of_links_i, + link_length_i, + hinge_mass_i, + persistance_length_i, + end_to_end_length_i, + temperature_i, + ), + number_of_links, + link_length, + hinge_mass, + persistance_length, + end_to_end_length, + temperature, + ) +end + +""" +The relative Gibbs free energy ``\\Delta\\varphi\\equiv\\varphi(f,T)-\\varphi(0,T)`` as a function of the applied force ``f`` and temperature ``T``, +parameterized by the number of links ``N_b`` link length ``\\ell_b``, and persistance length ``\\ell_p``. + +$(TYPEDSIGNATURES) +""" +function relative_gibbs_free_energy( + number_of_links::Union{UInt8,Vector,Matrix,Array}, + link_length::Union{Float64,Vector,Matrix,Array}, + persistance_length::Union{Float64,Vector,Matrix,Array}, + end_to_end_length::Union{Float64,Vector,Matrix,Array}, + temperature::Union{Float64,Vector,Matrix,Array}, +)::Union{Float64,Vector,Matrix,Array} + return broadcast( + ( + number_of_links_i, + link_length_i, + persistance_length_i, + end_to_end_length_i, + temperature_i, + ) -> ccall( + ( + :physics_single_chain_wlc_thermodynamics_isotensional_relative_gibbs_free_energy, + string(PROJECT_ROOT, "target/debug/libpolymers"), + ), + Float64, + (UInt8, Float64, Float64, Float64, Float64), + number_of_links_i, + link_length_i, + persistance_length_i, + end_to_end_length_i, + temperature_i, + ), + number_of_links, + link_length, + persistance_length, + end_to_end_length, + temperature, + ) +end + +""" +The relative Gibbs free energy per link ``\\Delta\\varphi/N_b`` as a function of the applied force ``f`` and temperature ``T``, +parameterized by the number of links ``N_b`` link length ``\\ell_b``, and persistance length ``\\ell_p``. + +$(TYPEDSIGNATURES) +""" +function relative_gibbs_free_energy_per_link( + number_of_links::Union{UInt8,Vector,Matrix,Array}, + link_length::Union{Float64,Vector,Matrix,Array}, + persistance_length::Union{Float64,Vector,Matrix,Array}, + end_to_end_length::Union{Float64,Vector,Matrix,Array}, + temperature::Union{Float64,Vector,Matrix,Array}, +)::Union{Float64,Vector,Matrix,Array} + return broadcast( + ( + number_of_links_i, + link_length_i, + persistance_length_i, + end_to_end_length_i, + temperature_i, + ) -> ccall( + ( + :physics_single_chain_wlc_thermodynamics_isotensional_relative_gibbs_free_energy_per_link, + string(PROJECT_ROOT, "target/debug/libpolymers"), + ), + Float64, + (UInt8, Float64, Float64, Float64, Float64), + number_of_links_i, + link_length_i, + persistance_length_i, + end_to_end_length_i, + temperature_i, + ), + number_of_links, + link_length, + persistance_length, + end_to_end_length, + temperature, + ) +end + +""" +The nondimensional Gibbs free energy ``N_b\\varrho=\\beta\\varphi`` as a function of the applied nondimensional force ``\\eta`` and temperature ``T``, +parameterized by the number of links ``N_b``, link length ``\\ell_b``, hinge mass ``m``, and nondimensional persistance length ``\\zeta``. + +$(TYPEDSIGNATURES) +""" +function nondimensional_gibbs_free_energy( + number_of_links::Union{UInt8,Vector,Matrix,Array}, + link_length::Union{Float64,Vector,Matrix,Array}, + hinge_mass::Union{Float64,Vector,Matrix,Array}, + nondimensional_persistance_length::Union{Float64,Vector,Matrix,Array}, + nondimensional_end_to_end_length_per_link::Union{Float64,Vector,Matrix,Array}, + temperature::Union{Float64,Vector,Matrix,Array}, +)::Union{Float64,Vector,Matrix,Array} + return broadcast( + ( + number_of_links_i, + link_length_i, + hinge_mass_i, + nondimensional_persistance_length_i, + nondimensional_end_to_end_length_per_link_i, + temperature_i, + ) -> ccall( + ( + :physics_single_chain_wlc_thermodynamics_isotensional_nondimensional_gibbs_free_energy, + string(PROJECT_ROOT, "target/debug/libpolymers"), + ), + Float64, + (UInt8, Float64, Float64, Float64, Float64, Float64), + number_of_links_i, + link_length_i, + hinge_mass_i, + nondimensional_persistance_length_i, + nondimensional_end_to_end_length_per_link_i, + temperature_i, + ), + number_of_links, + link_length, + hinge_mass, + nondimensional_persistance_length, + nondimensional_end_to_end_length_per_link, + temperature, + ) +end + +""" +The nondimensional Gibbs free energy per link ``\\varrho\\equiv\\beta\\varphi/N_b`` as a function of the applied nondimensional force ``\\eta`` and temperature ``T``, +parameterized by the number of links ``N_b``, link length ``\\ell_b``, hinge mass ``m``, and nondimensional persistance length ``\\zeta``. + +$(TYPEDSIGNATURES) +""" +function nondimensional_gibbs_free_energy_per_link( + number_of_links::Union{UInt8,Vector,Matrix,Array}, + link_length::Union{Float64,Vector,Matrix,Array}, + hinge_mass::Union{Float64,Vector,Matrix,Array}, + nondimensional_persistance_length::Union{Float64,Vector,Matrix,Array}, + nondimensional_end_to_end_length_per_link::Union{Float64,Vector,Matrix,Array}, + temperature::Union{Float64,Vector,Matrix,Array}, +)::Union{Float64,Vector,Matrix,Array} + return broadcast( + ( + number_of_links_i, + link_length_i, + hinge_mass_i, + nondimensional_persistance_length_i, + nondimensional_end_to_end_length_per_link_i, + temperature_i, + ) -> ccall( + ( + :physics_single_chain_wlc_thermodynamics_isotensional_nondimensional_gibbs_free_energy_per_link, + string(PROJECT_ROOT, "target/debug/libpolymers"), + ), + Float64, + (UInt8, Float64, Float64, Float64, Float64, Float64), + number_of_links_i, + link_length_i, + hinge_mass_i, + nondimensional_persistance_length_i, + nondimensional_end_to_end_length_per_link_i, + temperature_i, + ), + number_of_links, + link_length, + hinge_mass, + nondimensional_persistance_length, + nondimensional_end_to_end_length_per_link, + temperature, + ) +end + +""" +The nondimensional relative Gibbs free energy ``N_b\\Delta\\varrho=\\beta\\Delta\\varphi`` as a function of the applied nondimensional force ``\\eta``, +parameterized by the nondimensional persistance length ``\\zeta``. +``` + +$(TYPEDSIGNATURES) +""" +function nondimensional_relative_gibbs_free_energy( + number_of_links::Union{UInt8,Vector,Matrix,Array}, + nondimensional_persistance_length::Union{Float64,Vector,Matrix,Array}, + nondimensional_end_to_end_length_per_link::Union{Float64,Vector,Matrix,Array}, +)::Union{Float64,Vector,Matrix,Array} + return broadcast( + ( + number_of_links_i, + nondimensional_persistance_length_i, + nondimensional_end_to_end_length_per_link_i, + ) -> ccall( + ( + :physics_single_chain_wlc_thermodynamics_isotensional_nondimensional_relative_gibbs_free_energy, + string(PROJECT_ROOT, "target/debug/libpolymers"), + ), + Float64, + (UInt8, Float64, Float64), + number_of_links_i, + nondimensional_persistance_length_i, + nondimensional_end_to_end_length_per_link_i, + ), + number_of_links, + nondimensional_persistance_length, + nondimensional_end_to_end_length_per_link, + ) +end + +""" +The nondimensional relative Gibbs free energy per link ``\\Delta\\varrho\\equiv\\beta\\Delta\\varphi/N_b`` as a function of the applied nondimensional force ``\\eta``, +parameterized by the number of links ``N_b`` and nondimensional persistance length ``\\zeta``. + +$(TYPEDSIGNATURES) +""" +function nondimensional_relative_gibbs_free_energy_per_link( + number_of_links::Union{UInt8,Vector,Matrix,Array}, + nondimensional_persistance_length::Union{Float64,Vector,Matrix,Array}, + nondimensional_end_to_end_length_per_link::Union{Float64,Vector,Matrix,Array}, +)::Union{Float64,Vector,Matrix,Array} + return broadcast( + ( + number_of_links_i, + nondimensional_persistance_length_i, + nondimensional_end_to_end_length_per_link_i, + ) -> ccall( + ( + :physics_single_chain_wlc_thermodynamics_isotensional_nondimensional_relative_gibbs_free_energy_per_link, + string(PROJECT_ROOT, "target/debug/libpolymers"), + ), + Float64, + (UInt8, Float64, Float64), + number_of_links_i, + nondimensional_persistance_length_i, + nondimensional_end_to_end_length_per_link_i, + ), + number_of_links, + nondimensional_persistance_length, + nondimensional_end_to_end_length_per_link, + ) +end + +""" +Initializes and returns an instance of the thermodynamics of the WLC model in the isotensional ensemble. + +$(TYPEDSIGNATURES) +""" +function WLC( + number_of_links::UInt8, + link_length::Float64, + hinge_mass::Float64, + persistance_length::Float64, +) + nondimensional_persistance_length = persistance_length / number_of_links / link_length + return WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length, + nondimensional_persistance_length, + Legendre.WLC(number_of_links, link_length, hinge_mass, persistance_length), + (force, temperature) -> end_to_end_length( + number_of_links, + link_length, + persistance_length, + force, + temperature, + ), + (force, temperature) -> end_to_end_length_per_link( + number_of_links, + link_length, + persistance_length, + force, + temperature, + ), + (nondimensional_force) -> nondimensional_end_to_end_length( + number_of_links, + nondimensional_persistance_length, + nondimensional_force, + ), + (nondimensional_force) -> nondimensional_end_to_end_length_per_link( + number_of_links, + nondimensional_persistance_length, + nondimensional_force, + ), + (force, temperature) -> gibbs_free_energy( + number_of_links, + link_length, + hinge_mass, + persistance_length, + force, + temperature, + ), + (force, temperature) -> gibbs_free_energy_per_link( + number_of_links, + link_length, + hinge_mass, + persistance_length, + force, + temperature, + ), + (force, temperature) -> relative_gibbs_free_energy( + number_of_links, + link_length, + persistance_length, + force, + temperature, + ), + (force, temperature) -> relative_gibbs_free_energy_per_link( + number_of_links, + link_length, + persistance_length, + force, + temperature, + ), + (nondimensional_force, temperature) -> nondimensional_gibbs_free_energy( + number_of_links, + link_length, + hinge_mass, + nondimensional_persistance_length, + nondimensional_force, + temperature, + ), + (nondimensional_force, temperature) -> nondimensional_gibbs_free_energy_per_link( + number_of_links, + link_length, + hinge_mass, + nondimensional_persistance_length, + nondimensional_force, + temperature, + ), + (nondimensional_force) -> nondimensional_relative_gibbs_free_energy( + number_of_links, + nondimensional_persistance_length, + nondimensional_force, + ), + (nondimensional_force) -> nondimensional_relative_gibbs_free_energy_per_link( + number_of_links, + nondimensional_persistance_length, + nondimensional_force, + ), + ) +end + +end diff --git a/src/physics/single_chain/wlc/thermodynamics/isotensional/mod.rs b/src/physics/single_chain/wlc/thermodynamics/isotensional/mod.rs new file mode 100644 index 00000000..023ca74b --- /dev/null +++ b/src/physics/single_chain/wlc/thermodynamics/isotensional/mod.rs @@ -0,0 +1,247 @@ +#[cfg(feature = "extern")] +pub mod ex; + +#[cfg(feature = "python")] +pub mod py; + +mod test; + +/// The worm-like chain (WLC) model thermodynamics in the isotensional ensemble approximated using a Legendre transformation. +pub mod legendre; + +use std::f64::consts::PI; +use crate::math:: +{ + inverse_newton_raphson_powered, + integrate_1d +}; +use crate::physics:: +{ + PLANCK_CONSTANT, + BOLTZMANN_CONSTANT +}; +use crate::physics::single_chain:: +{ + ONE, + ZERO, + POINTS +}; +use super::isometric::nondimensional_force as isometric_nondimensional_force; +use super::isometric::nondimensional_helmholtz_free_energy as isometric_nondimensional_helmholtz_free_energy; +use super::isometric::nondimensional_relative_helmholtz_free_energy as isometric_nondimensional_relative_helmholtz_free_energy; + +/// The structure of the thermodynamics of the WLC model in the isotensional ensemble. +pub struct WLC +{ + /// The mass of each hinge in the chain in units of kg/mol. + pub hinge_mass: f64, + + /// The length of each link in the chain in units of nm. + pub link_length: f64, + + /// The number of links in the chain. + pub number_of_links: u8, + + /// The persistance length of the chain in units of nm. + pub persistance_length: f64, + + nondimensional_persistance_length: f64, + + /// The thermodynamic functions of the model in the isotensional ensemble approximated using a Legendre transformation. + pub legendre: self::legendre::WLC +} + +/// The expected end-to-end length as a function of the applied force and temperature, parameterized by the number of links, link length, and persistance length. +pub fn end_to_end_length(number_of_links: &u8, link_length: &f64, persistance_length: &f64, force: &f64, temperature: &f64) -> f64 +{ + link_length*nondimensional_end_to_end_length(number_of_links, &(persistance_length/((*number_of_links as f64)*link_length)), &(force*link_length/BOLTZMANN_CONSTANT/temperature)) +} + +/// The expected end-to-end length per link as a function of the applied force and temperature, parameterized by the number of links, link length, and persistance length. +pub fn end_to_end_length_per_link(number_of_links: &u8, link_length: &f64, persistance_length: &f64, force: &f64, temperature: &f64) -> f64 +{ + link_length*nondimensional_end_to_end_length_per_link(number_of_links, &(persistance_length/((*number_of_links as f64)*link_length)), &(force*link_length/BOLTZMANN_CONSTANT/temperature)) +} + +/// The expected nondimensional end-to-end length as a function of the applied nondimensional force, parameterized by the number of links and nondimensional persistance length. +pub fn nondimensional_end_to_end_length(number_of_links: &u8, nondimensional_persistance_length: &f64, nondimensional_force: &f64) -> f64 +{ + let f = &|nondimensional_end_to_end_length_per_link: &f64| isometric_nondimensional_force(number_of_links, nondimensional_persistance_length, nondimensional_end_to_end_length_per_link); + let fp = &|nondimensional_end_to_end_length_per_link: &f64| (isometric_nondimensional_force(number_of_links, nondimensional_persistance_length, &(nondimensional_end_to_end_length_per_link + 1e-8)) - isometric_nondimensional_force(number_of_links, nondimensional_persistance_length, &(nondimensional_end_to_end_length_per_link - 1e-8)))/2e-8; + let gamma_hat = inverse_newton_raphson_powered(nondimensional_force, f, fp, &0.9999, &1e-5, &100, 2); + if !(0.0..=1.0).contains(&gamma_hat) + { + panic!("Root finding failed!"); + } + let scale = (*number_of_links as f64)*nondimensional_force*gamma_hat - isometric_nondimensional_relative_helmholtz_free_energy(nondimensional_persistance_length, &gamma_hat); + let integrand_denominator = |nondimensional_end_to_end_length_per_link: &f64| + { + let beta_delta_psi = isometric_nondimensional_relative_helmholtz_free_energy(nondimensional_persistance_length, nondimensional_end_to_end_length_per_link); + let beta_f_xi = (*number_of_links as f64)*nondimensional_force*nondimensional_end_to_end_length_per_link; + ((beta_f_xi - scale - beta_delta_psi).exp() - (-beta_f_xi - scale - beta_delta_psi).exp())*nondimensional_end_to_end_length_per_link/nondimensional_force + }; + let integrand_numerator = |nondimensional_end_to_end_length_per_link: &f64| + { + let beta_delta_psi = isometric_nondimensional_relative_helmholtz_free_energy(nondimensional_persistance_length, nondimensional_end_to_end_length_per_link); + let beta_f_xi = (*number_of_links as f64)*nondimensional_force*nondimensional_end_to_end_length_per_link; + let exp_1 = (beta_f_xi - scale - beta_delta_psi).exp(); + let exp_2 = (-beta_f_xi - scale - beta_delta_psi).exp(); + ((exp_1 + exp_2)*(*number_of_links as f64)*nondimensional_end_to_end_length_per_link - (exp_1 - exp_2)/nondimensional_force)*nondimensional_end_to_end_length_per_link/nondimensional_force + }; + integrate_1d(&integrand_numerator, &ZERO, &ONE, &POINTS)/integrate_1d(&integrand_denominator, &ZERO, &ONE, &POINTS) +} + +/// The expected nondimensional end-to-end length per link as a function of the applied nondimensional force, parameterized by the number of links and nondimensional persistance length. +pub fn nondimensional_end_to_end_length_per_link(number_of_links: &u8, nondimensional_persistance_length: &f64, nondimensional_force: &f64) -> f64 +{ + nondimensional_end_to_end_length(number_of_links, nondimensional_persistance_length, nondimensional_force)/(*number_of_links as f64) +} + +/// The Gibbs free energy as a function of the applied force and temperature, parameterized by the number of links, link length, hinge mass, and persistance length. +pub fn gibbs_free_energy(number_of_links: &u8, link_length: &f64, hinge_mass: &f64, persistance_length: &f64, force: &f64, temperature: &f64) -> f64 +{ + nondimensional_gibbs_free_energy(number_of_links, link_length, hinge_mass, &(persistance_length/((*number_of_links as f64)*link_length)), &(force*link_length/BOLTZMANN_CONSTANT/temperature), temperature)*BOLTZMANN_CONSTANT*temperature +} + +/// The Gibbs free energy per link as a function of the applied force and temperature, parameterized by the number of links, link length, hinge mass, and persistance length. +pub fn gibbs_free_energy_per_link(number_of_links: &u8, link_length: &f64, hinge_mass: &f64, persistance_length: &f64, force: &f64, temperature: &f64) -> f64 +{ + nondimensional_gibbs_free_energy_per_link(number_of_links, link_length, hinge_mass, &(persistance_length/((*number_of_links as f64)*link_length)), &(force*link_length/BOLTZMANN_CONSTANT/temperature), temperature)*BOLTZMANN_CONSTANT*temperature +} + +/// The relative Gibbs free energy as a function of the applied force and temperature, parameterized by the number of links, link length, and persistance length. +pub fn relative_gibbs_free_energy(number_of_links: &u8, link_length: &f64, persistance_length: &f64, force: &f64, temperature: &f64) -> f64 +{ + nondimensional_relative_gibbs_free_energy(number_of_links, &(persistance_length/((*number_of_links as f64)*link_length)), &(force*link_length/BOLTZMANN_CONSTANT/temperature))*BOLTZMANN_CONSTANT*temperature +} + +/// The relative Gibbs free energy per link as a function of the applied force and temperature, parameterized by the number of links, link length, and persistance length. +pub fn relative_gibbs_free_energy_per_link(number_of_links: &u8, link_length: &f64, persistance_length: &f64, force: &f64, temperature: &f64) -> f64 +{ + nondimensional_relative_gibbs_free_energy_per_link(number_of_links, &(persistance_length/((*number_of_links as f64)*link_length)), &(force*link_length/BOLTZMANN_CONSTANT/temperature))*BOLTZMANN_CONSTANT*temperature +} + +/// The nondimensional Gibbs free energy as a function of the applied nondimensional force and temperature, parameterized by the number of links, link length, hinge mass, and nondimensional persistance length. +pub fn nondimensional_gibbs_free_energy(number_of_links: &u8, link_length: &f64, hinge_mass: &f64, nondimensional_persistance_length: &f64, nondimensional_force: &f64, temperature: &f64) -> f64 +{ + let f = &|nondimensional_end_to_end_length_per_link: &f64| isometric_nondimensional_force(number_of_links, nondimensional_persistance_length, nondimensional_end_to_end_length_per_link); + let fp = &|nondimensional_end_to_end_length_per_link: &f64| (isometric_nondimensional_force(number_of_links, nondimensional_persistance_length, &(nondimensional_end_to_end_length_per_link + 1e-8)) - isometric_nondimensional_force(number_of_links, nondimensional_persistance_length, &(nondimensional_end_to_end_length_per_link - 1e-8)))/2e-8; + let fpp = &|x: &f64| (fp(&(x + 1e-8)) - fp(&(x - 1e-8)))/2e-8; + let gamma_hat = inverse_newton_raphson_powered(nondimensional_force, f, fp, &0.9999, &1e-5, &100, 2); + if !(0.0..=1.0).contains(&gamma_hat) + { + panic!("Root finding failed!"); + } + let scale = (*number_of_links as f64)*nondimensional_force*gamma_hat - isometric_nondimensional_helmholtz_free_energy(number_of_links, link_length, hinge_mass, nondimensional_persistance_length, &gamma_hat, temperature); + let log_integral = if nondimensional_force > &100.0 + { + -((8.0*PI.powi(2)/fpp(&gamma_hat)).sqrt()).ln() - (0.5*(*number_of_links as f64)*gamma_hat/nondimensional_force).ln() + } + else + { + let integrand = |nondimensional_end_to_end_length_per_link: &f64| + { + let beta_psi = isometric_nondimensional_helmholtz_free_energy(number_of_links, link_length, hinge_mass, nondimensional_persistance_length, nondimensional_end_to_end_length_per_link, temperature); + let beta_f_xi = (*number_of_links as f64)*nondimensional_force*nondimensional_end_to_end_length_per_link; + 0.5*((beta_f_xi - scale - beta_psi).exp() - (-beta_f_xi - scale - beta_psi).exp())*(*number_of_links as f64)*nondimensional_end_to_end_length_per_link/nondimensional_force + }; + -(4.0*PI*integrate_1d(&integrand, &ZERO, &ONE, &POINTS)).ln() + }; + log_integral - scale - (4.0*(-1.0/nondimensional_persistance_length).exp().acos().sin()*PI.powi(2)*hinge_mass*link_length.powi(2)*BOLTZMANN_CONSTANT*temperature/PLANCK_CONSTANT.powi(2)).ln() +} + +/// The nondimensional Gibbs free energy per link as a function of the applied nondimensional force and temperature, parameterized by the number of links, link length, and hinge mass, and nondimensional persistance length. +pub fn nondimensional_gibbs_free_energy_per_link(number_of_links: &u8, link_length: &f64, hinge_mass: &f64, nondimensional_persistance_length: &f64, nondimensional_force: &f64, temperature: &f64) -> f64 +{ + nondimensional_gibbs_free_energy(number_of_links, link_length, hinge_mass, nondimensional_persistance_length, nondimensional_force, temperature)/(*number_of_links as f64) +} + +/// The nondimensional relative Gibbs free energy as a function of the applied nondimensional force, parameterized by the number of links and nondimensional persistance length. +pub fn nondimensional_relative_gibbs_free_energy(number_of_links: &u8, nondimensional_persistance_length: &f64, nondimensional_force: &f64) -> f64 +{ + nondimensional_gibbs_free_energy(number_of_links, &1.0, &1.0, nondimensional_persistance_length, nondimensional_force, &300.0) - nondimensional_gibbs_free_energy(number_of_links, &1.0, &1.0, nondimensional_persistance_length, &ZERO, &300.0) +} + +/// The nondimensional relative Gibbs free energy per link as a function of the applied nondimensional force, parameterized by the number of links and nondimensional persistance length. +pub fn nondimensional_relative_gibbs_free_energy_per_link(number_of_links: &u8, nondimensional_persistance_length: &f64, nondimensional_force: &f64) -> f64 +{ + nondimensional_gibbs_free_energy_per_link(number_of_links, &1.0, &1.0, nondimensional_persistance_length, nondimensional_force, &300.0) - nondimensional_gibbs_free_energy_per_link(number_of_links, &1.0, &1.0, nondimensional_persistance_length, &ZERO, &300.0) +} + +/// The implemented functionality of the thermodynamics of the WLC model in the isotensional ensemble. +impl WLC +{ + /// Initializes and returns an instance of the thermodynamics of the WLC model in the isotensional ensemble. + pub fn init(number_of_links: u8, link_length: f64, hinge_mass: f64, persistance_length: f64) -> Self + { + WLC + { + hinge_mass, + link_length, + number_of_links, + persistance_length, + nondimensional_persistance_length: persistance_length/(number_of_links as f64)/link_length, + legendre: self::legendre::WLC::init(number_of_links, link_length, hinge_mass, persistance_length) + } + } + /// The expected end-to-end length as a function of the applied force and temperature. + pub fn end_to_end_length(&self, force: &f64, temperature: &f64) -> f64 + { + end_to_end_length(&self.number_of_links, &self.link_length, &self.persistance_length, force, temperature) + } + /// The expected end-to-end length per link as a function of the applied force and temperature. + pub fn end_to_end_length_per_link(&self, force: &f64, temperature: &f64) -> f64 + { + end_to_end_length_per_link(&self.number_of_links, &self.link_length, &self.persistance_length, force, temperature) + } + /// The expected nondimensional end-to-end length as a function of the applied nondimensional force. + pub fn nondimensional_end_to_end_length(&self, nondimensional_force: &f64) -> f64 + { + nondimensional_end_to_end_length(&self.number_of_links, &self.nondimensional_persistance_length, nondimensional_force) + } + /// The expected nondimensional end-to-end length per link as a function of the applied nondimensional force. + pub fn nondimensional_end_to_end_length_per_link(&self, nondimensional_force: &f64) -> f64 + { + nondimensional_end_to_end_length_per_link(&self.number_of_links, &self.nondimensional_persistance_length, nondimensional_force) + } + /// The Gibbs free energy as a function of the applied force and temperature. + pub fn gibbs_free_energy(&self, force: &f64, temperature: &f64) -> f64 + { + gibbs_free_energy(&self.number_of_links, &self.link_length, &self.hinge_mass, &self.persistance_length, force, temperature) + } + /// The Gibbs free energy per link as a function of the applied force and temperature. + pub fn gibbs_free_energy_per_link(&self, force: &f64, temperature: &f64) -> f64 + { + gibbs_free_energy_per_link(&self.number_of_links, &self.link_length, &self.hinge_mass, &self.persistance_length, force, temperature) + } + /// The relative Gibbs free energy as a function of the applied force and temperature. + pub fn relative_gibbs_free_energy(&self, force: &f64, temperature: &f64) -> f64 + { + relative_gibbs_free_energy(&self.number_of_links, &self.link_length, &self.persistance_length, force, temperature) + } + /// The relative Gibbs free energy per link as a function of the applied force and temperature. + pub fn relative_gibbs_free_energy_per_link(&self, force: &f64, temperature: &f64) -> f64 + { + relative_gibbs_free_energy_per_link(&self.number_of_links, &self.link_length, &self.persistance_length, force, temperature) + } + /// The nondimensional Gibbs free energy as a function of the applied nondimensional force and temperature. + pub fn nondimensional_gibbs_free_energy(&self, nondimensional_force: &f64, temperature: &f64) -> f64 + { + nondimensional_gibbs_free_energy(&self.number_of_links, &self.link_length, &self.hinge_mass, &self.nondimensional_persistance_length, nondimensional_force, temperature) + } + /// The nondimensional Gibbs free energy per link as a function of the applied nondimensional force and temperature. + pub fn nondimensional_gibbs_free_energy_per_link(&self, nondimensional_force: &f64, temperature: &f64) -> f64 + { + nondimensional_gibbs_free_energy_per_link(&self.number_of_links, &self.link_length, &self.hinge_mass, &self.nondimensional_persistance_length, nondimensional_force, temperature) + } + /// The nondimensional relative Gibbs free energy as a function of the applied nondimensional force. + pub fn nondimensional_relative_gibbs_free_energy(&self, nondimensional_force: &f64) -> f64 + { + nondimensional_relative_gibbs_free_energy(&self.number_of_links, &self.nondimensional_persistance_length, nondimensional_force) + } + /// The nondimensional relative Gibbs free energy per link as a function of the applied nondimensional force. + pub fn nondimensional_relative_gibbs_free_energy_per_link(&self, nondimensional_force: &f64) -> f64 + { + nondimensional_relative_gibbs_free_energy_per_link(&self.number_of_links, &self.nondimensional_persistance_length, nondimensional_force) + } +} diff --git a/src/physics/single_chain/wlc/thermodynamics/isotensional/py.rs b/src/physics/single_chain/wlc/thermodynamics/isotensional/py.rs new file mode 100644 index 00000000..6eb6e301 --- /dev/null +++ b/src/physics/single_chain/wlc/thermodynamics/isotensional/py.rs @@ -0,0 +1,226 @@ +use pyo3::prelude::*; +use numpy:: +{ + IntoPyArray, + PyArrayDyn, + PyReadonlyArrayDyn +}; + +pub fn register_module(py: Python<'_>, parent_module: &PyModule) -> PyResult<()> +{ + let isotensional = PyModule::new(py, "isotensional")?; + super::legendre::py::register_module(py, isotensional)?; + parent_module.add_submodule(isotensional)?; + isotensional.add_class::()?; + Ok(()) +} + +/// The worm-like chain (WLC) model thermodynamics in the isotensional ensemble. +#[pyclass] +#[derive(Copy, Clone)] +pub struct WLC +{ + /// The mass of each hinge in the chain in units of kg/mol. + #[pyo3(get)] + pub hinge_mass: f64, + + /// The length of each link in the chain in units of nm. + #[pyo3(get)] + pub link_length: f64, + + /// The number of links in the chain. + #[pyo3(get)] + pub number_of_links: u8, + + /// The persistance length of the chain in units of nm. + #[pyo3(get)] + pub persistance_length: f64, + + nondimensional_persistance_length: f64, + + /// The thermodynamic functions of the model in the isotensional ensemble approximated using a Legendre transformation. + #[pyo3(get)] + pub legendre: super::legendre::py::WLC +} + +#[pymethods] +impl WLC +{ + #[new] + pub fn init(number_of_links: u8, link_length: f64, hinge_mass: f64, persistance_length: f64) -> Self + { + WLC + { + hinge_mass, + link_length, + number_of_links, + persistance_length, + nondimensional_persistance_length: persistance_length/(number_of_links as f64)/link_length, + legendre: super::legendre::py::WLC::init(number_of_links, link_length, hinge_mass, persistance_length) + } + } + /// The expected end-to-end length as a function of the applied force and temperature. + /// + /// Args: + /// force (numpy.ndarray): The force :math:`f`. + /// temperature (float): The temperature :math:`T`. + /// + /// Returns: + /// numpy.ndarray: The end-to-end length :math:`\xi`. + /// + pub fn end_to_end_length<'py>(&self, py: Python<'py>, force: PyReadonlyArrayDyn, temperature: f64) -> &'py PyArrayDyn + { + force.as_array().mapv(|force: f64| super::end_to_end_length(&self.number_of_links, &self.link_length, &self.persistance_length, &force, &temperature)).into_pyarray(py) + } + /// The expected end-to-end length per link as a function of the applied force and temperature. + /// + /// Args: + /// force (numpy.ndarray): The force :math:`f`. + /// temperature (float): The temperature :math:`T`. + /// + /// Returns: + /// numpy.ndarray: The end-to-end length per link :math:`\xi/N_b=\ell_b\gamma`. + /// + pub fn end_to_end_length_per_link<'py>(&self, py: Python<'py>, force: PyReadonlyArrayDyn, temperature: f64) -> &'py PyArrayDyn + { + force.as_array().mapv(|force: f64| super::end_to_end_length_per_link(&self.number_of_links, &self.link_length, &self.persistance_length, &force, &temperature)).into_pyarray(py) + } + /// The expected nondimensional end-to-end length as a function of the applied nondimensional force. + /// + /// Args: + /// nondimensional_force (numpy.ndarray): The nondimensional force :math:`\eta\equiv\beta f\ell_b`. + /// + /// Returns: + /// numpy.ndarray: The nondimensional end-to-end length :math:`N_b\gamma=\xi/\ell_b`. + /// + pub fn nondimensional_end_to_end_length<'py>(&self, py: Python<'py>, nondimensional_force: PyReadonlyArrayDyn) -> &'py PyArrayDyn + { + nondimensional_force.as_array().mapv(|nondimensional_force: f64| super::nondimensional_end_to_end_length(&self.number_of_links, &self.nondimensional_persistance_length, &nondimensional_force)).into_pyarray(py) + } + /// The expected nondimensional end-to-end length per link as a function of the applied nondimensional force. + /// + /// Args: + /// nondimensional_force (numpy.ndarray): The nondimensional force :math:`\eta\equiv\beta f\ell_b`. + /// + /// Returns: + /// numpy.ndarray: The nondimensional end-to-end length per link :math:`\gamma\equiv \xi/N_b\ell_b`. + /// + pub fn nondimensional_end_to_end_length_per_link<'py>(&self, py: Python<'py>, nondimensional_force: PyReadonlyArrayDyn) -> &'py PyArrayDyn + { + nondimensional_force.as_array().mapv(|nondimensional_force: f64| super::nondimensional_end_to_end_length_per_link(&self.number_of_links, &self.nondimensional_persistance_length, &nondimensional_force)).into_pyarray(py) + } + /// The Gibbs free energy as a function of the applied force and temperature, + /// + /// .. math:: + /// \psi(\xi, T) = -kT\ln Q(\xi, T). + /// + /// Args: + /// force (numpy.ndarray): The force :math:`f`. + /// temperature (float): The temperature :math:`T`. + /// + /// Returns: + /// numpy.ndarray: The Gibbs free energy :math:`\psi`. + /// + pub fn gibbs_free_energy<'py>(&self, py: Python<'py>, force: PyReadonlyArrayDyn, temperature: f64) -> &'py PyArrayDyn + { + force.as_array().mapv(|force: f64| super::gibbs_free_energy(&self.number_of_links, &self.link_length, &self.hinge_mass, &self.persistance_length, &force, &temperature)).into_pyarray(py) + } + /// The Gibbs free energy per link as a function of the applied force and temperature. + /// + /// Args: + /// force (numpy.ndarray): The force :math:`f`. + /// temperature (float): The temperature :math:`T`. + /// + /// Returns: + /// numpy.ndarray: The Gibbs free energy per link :math:`\psi/N_b`. + /// + pub fn gibbs_free_energy_per_link<'py>(&self, py: Python<'py>, force: PyReadonlyArrayDyn, temperature: f64) -> &'py PyArrayDyn + { + force.as_array().mapv(|force: f64| super::gibbs_free_energy_per_link(&self.number_of_links, &self.link_length, &self.hinge_mass, &self.persistance_length, &force, &temperature)).into_pyarray(py) + } + /// The relative Gibbs free energy as a function of the applied force and temperature, + /// + /// .. math:: + /// \Delta\psi(\xi, T) = kT\ln\left[\frac{P_\mathrm{eq}(0)}{P_\mathrm{eq}(\xi)}\right]. + /// + /// Args: + /// force (numpy.ndarray): The force :math:`f`. + /// temperature (float): The temperature :math:`T`. + /// + /// Returns: + /// numpy.ndarray: The relative Gibbs free energy :math:`\Delta\psi\equiv\psi(\xi,T)-\psi(0,T)`. + /// + pub fn relative_gibbs_free_energy<'py>(&self, py: Python<'py>, force: PyReadonlyArrayDyn, temperature: f64) -> &'py PyArrayDyn + { + force.as_array().mapv(|force: f64| super::relative_gibbs_free_energy(&self.number_of_links, &self.link_length, &self.persistance_length, &force, &temperature)).into_pyarray(py) + } + /// The relative Gibbs free energy per link as a function of the applied force and temperature. + /// + /// Args: + /// force (numpy.ndarray): The force :math:`f`. + /// temperature (float): The temperature :math:`T`. + /// + /// Returns: + /// numpy.ndarray: The relative Gibbs free energy per link :math:`\Delta\psi/N_b`. + /// + pub fn relative_gibbs_free_energy_per_link<'py>(&self, py: Python<'py>, force: PyReadonlyArrayDyn, temperature: f64) -> &'py PyArrayDyn + { + force.as_array().mapv(|force: f64| super::relative_gibbs_free_energy_per_link(&self.number_of_links, &self.link_length, &self.persistance_length, &force, &temperature)).into_pyarray(py) + } + /// The nondimensional Gibbs free energy as a function of the applied nondimensional force and temperature. + /// + /// Args: + /// nondimensional_force (numpy.ndarray): The nondimensional force :math:`\eta\equiv\beta f\ell_b`. + /// temperature (float): The temperature :math:`T`. + /// + /// Returns: + /// numpy.ndarray: The nondimensional Gibbs free energy :math:`N_b\vartheta=\beta\psi`. + /// + pub fn nondimensional_gibbs_free_energy<'py>(&self, py: Python<'py>, nondimensional_force: PyReadonlyArrayDyn, temperature: f64) -> &'py PyArrayDyn + { + nondimensional_force.as_array().mapv(|nondimensional_force: f64| super::nondimensional_gibbs_free_energy(&self.number_of_links, &self.link_length, &self.hinge_mass, &self.nondimensional_persistance_length, &nondimensional_force, &temperature)).into_pyarray(py) + } + /// The nondimensional Gibbs free energy per link as a function of the applied nondimensional force and temperature. + /// + /// Args: + /// nondimensional_force (numpy.ndarray): The nondimensional force :math:`\eta\equiv\beta f\ell_b`. + /// temperature (float): The temperature :math:`T`. + /// + /// Returns: + /// numpy.ndarray: The nondimensional Gibbs free energy per link :math:`\vartheta\equiv\beta\psi/N_b`. + /// + pub fn nondimensional_gibbs_free_energy_per_link<'py>(&self, py: Python<'py>, nondimensional_force: PyReadonlyArrayDyn, temperature: f64) -> &'py PyArrayDyn + { + nondimensional_force.as_array().mapv(|nondimensional_force: f64| super::nondimensional_gibbs_free_energy_per_link(&self.number_of_links, &self.link_length, &self.hinge_mass, &self.nondimensional_persistance_length, &nondimensional_force, &temperature)).into_pyarray(py) + } + /// The nondimensional relative Gibbs free energy as a function of the applied nondimensional force, + /// + /// .. math:: + /// \beta\Delta\psi(\gamma) = \ln\left[\frac{\mathscr{P}_\mathrm{eq}(0)}{\mathscr{P}_\mathrm{eq}(\gamma)}\right]. + /// + /// Args: + /// nondimensional_force (numpy.ndarray): The nondimensional force :math:`\eta\equiv\beta f\ell_b`. + /// + /// Returns: + /// numpy.ndarray: The nondimensional relative Gibbs free energy :math:`N_b\Delta\vartheta=\beta\Delta\psi`. + /// + pub fn nondimensional_relative_gibbs_free_energy<'py>(&self, py: Python<'py>, nondimensional_force: PyReadonlyArrayDyn) -> &'py PyArrayDyn + { + nondimensional_force.as_array().mapv(|nondimensional_force: f64| super::nondimensional_relative_gibbs_free_energy(&self.number_of_links, &self.nondimensional_persistance_length, &nondimensional_force)).into_pyarray(py) + } + /// The nondimensional relative Gibbs free energy per link as a function of the applied nondimensional force, + /// + /// .. math:: + /// \Delta\vartheta(\gamma) = \ln\left[\frac{\mathscr{P}_\mathrm{eq}(0)}{\mathscr{P}_\mathrm{eq}(\gamma)}\right]^{1/N_b}. + /// + /// Args: + /// nondimensional_force (numpy.ndarray): The nondimensional force :math:`\eta\equiv\beta f\ell_b`. + /// + /// Returns: + /// numpy.ndarray: The nondimensional relative Gibbs free energy per link :math:`\Delta\vartheta\equiv\beta\Delta\psi/N_b`. + /// + pub fn nondimensional_relative_gibbs_free_energy_per_link<'py>(&self, py: Python<'py>, nondimensional_force: PyReadonlyArrayDyn) -> &'py PyArrayDyn + { + nondimensional_force.as_array().mapv(|nondimensional_force: f64| super::nondimensional_relative_gibbs_free_energy_per_link(&self.number_of_links, &self.nondimensional_persistance_length, &nondimensional_force)).into_pyarray(py) + } +} diff --git a/src/physics/single_chain/wlc/thermodynamics/isotensional/test.jl b/src/physics/single_chain/wlc/thermodynamics/isotensional/test.jl new file mode 100644 index 00000000..67d7214e --- /dev/null +++ b/src/physics/single_chain/wlc/thermodynamics/isotensional/test.jl @@ -0,0 +1,1154 @@ +module Test + +using Test +using Polymers.Physics: BOLTZMANN_CONSTANT +using Polymers.Physics.SingleChain: ZERO, parameters +using Polymers.Physics.SingleChain.Wlc.Thermodynamics.Isotensional: WLC + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::base::init" begin + @test isa( + WLC( + parameters.number_of_links_minimum, + parameters.link_length_reference, + parameters.hinge_mass_reference, + parameters.persistance_length_reference, + ), + Any, + ) +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::base::number_of_links" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + @test WLC( + number_of_links, + parameters.link_length_reference, + parameters.hinge_mass_reference, + parameters.persistance_length_reference, + ).number_of_links == number_of_links + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::base::link_length" begin + for _ = 1:parameters.number_of_loops + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + @test WLC( + parameters.number_of_links_minimum, + link_length, + parameters.hinge_mass_reference, + parameters.persistance_length_reference, + ).link_length == link_length + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::base::hinge_mass" begin + for _ = 1:parameters.number_of_loops + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + @test WLC( + parameters.number_of_links_minimum, + parameters.link_length_reference, + hinge_mass, + parameters.persistance_length_reference, + ).hinge_mass == hinge_mass + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::base::persistance_length" begin + for _ = 1:parameters.number_of_loops + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + @test WLC( + parameters.number_of_links_minimum, + parameters.link_length_reference, + parameters.hinge_mass_reference, + persistance_length, + ).persistance_length == persistance_length + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::base::all_parameters" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + @test all( + WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length, + ).number_of_links == number_of_links && + WLC(number_of_links, link_length, hinge_mass, persistance_length).link_length == + link_length && + WLC(number_of_links, link_length, hinge_mass, persistance_length).hinge_mass == + hinge_mass && + WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length, + ).persistance_length == persistance_length, + ) + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::nondimensional::end_to_end_length" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + nondimensional_end_to_end_length = + model.nondimensional_end_to_end_length(nondimensional_force) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + end_to_end_length = model.end_to_end_length(force, temperature) + residual_abs = end_to_end_length / link_length - nondimensional_end_to_end_length + residual_rel = residual_abs / nondimensional_end_to_end_length + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::nondimensional::end_to_end_length_per_link" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + nondimensional_end_to_end_length_per_link = + model.nondimensional_end_to_end_length_per_link(nondimensional_force) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + end_to_end_length_per_link = model.end_to_end_length_per_link(force, temperature) + residual_abs = + end_to_end_length_per_link / link_length - + nondimensional_end_to_end_length_per_link + residual_rel = residual_abs / nondimensional_end_to_end_length_per_link + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::nondimensional::gibbs_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + nondimensional_gibbs_free_energy = + model.nondimensional_gibbs_free_energy(nondimensional_force, temperature) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + gibbs_free_energy = model.gibbs_free_energy(force, temperature) + residual_abs = + gibbs_free_energy / BOLTZMANN_CONSTANT / temperature - + nondimensional_gibbs_free_energy + residual_rel = residual_abs / nondimensional_gibbs_free_energy + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::nondimensional::gibbs_free_energy_per_link" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + nondimensional_gibbs_free_energy_per_link = + model.nondimensional_gibbs_free_energy_per_link( + nondimensional_force, + temperature, + ) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + gibbs_free_energy_per_link = model.gibbs_free_energy_per_link(force, temperature) + residual_abs = + gibbs_free_energy_per_link / BOLTZMANN_CONSTANT / temperature - + nondimensional_gibbs_free_energy_per_link + residual_rel = residual_abs / nondimensional_gibbs_free_energy_per_link + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::nondimensional::relative_gibbs_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + nondimensional_relative_gibbs_free_energy = + model.nondimensional_relative_gibbs_free_energy(nondimensional_force) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + relative_gibbs_free_energy = model.relative_gibbs_free_energy(force, temperature) + residual_abs = + relative_gibbs_free_energy / BOLTZMANN_CONSTANT / temperature - + nondimensional_relative_gibbs_free_energy + residual_rel = residual_abs / nondimensional_relative_gibbs_free_energy + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::nondimensional::relative_gibbs_free_energy_per_link" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + nondimensional_relative_gibbs_free_energy_per_link = + model.nondimensional_relative_gibbs_free_energy_per_link(nondimensional_force) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + relative_gibbs_free_energy_per_link = + model.relative_gibbs_free_energy_per_link(force, temperature) + residual_abs = + relative_gibbs_free_energy_per_link / BOLTZMANN_CONSTANT / temperature - + nondimensional_relative_gibbs_free_energy_per_link + residual_rel = residual_abs / nondimensional_relative_gibbs_free_energy_per_link + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::per_link::end_to_end_length" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + end_to_end_length = model.end_to_end_length(force, temperature) + end_to_end_length_per_link = model.end_to_end_length_per_link(force, temperature) + residual_abs = end_to_end_length / number_of_links - end_to_end_length_per_link + residual_rel = residual_abs / end_to_end_length_per_link + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::per_link::nondimensional_end_to_end_length" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + nondimensional_end_to_end_length = + model.nondimensional_end_to_end_length(nondimensional_force) + nondimensional_end_to_end_length_per_link = + model.nondimensional_end_to_end_length_per_link(nondimensional_force) + residual_abs = + nondimensional_end_to_end_length / number_of_links - + nondimensional_end_to_end_length_per_link + residual_rel = residual_abs / nondimensional_end_to_end_length_per_link + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::per_link::gibbs_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + gibbs_free_energy = model.gibbs_free_energy(force, temperature) + gibbs_free_energy_per_link = model.gibbs_free_energy_per_link(force, temperature) + residual_abs = gibbs_free_energy / number_of_links - gibbs_free_energy_per_link + residual_rel = residual_abs / gibbs_free_energy_per_link + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::per_link::relative_gibbs_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + relative_gibbs_free_energy = model.relative_gibbs_free_energy(force, temperature) + relative_gibbs_free_energy_per_link = + model.relative_gibbs_free_energy_per_link(force, temperature) + residual_abs = + relative_gibbs_free_energy / number_of_links - + relative_gibbs_free_energy_per_link + residual_rel = residual_abs / relative_gibbs_free_energy_per_link + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::per_link::nondimensional_gibbs_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + nondimensional_gibbs_free_energy = + model.nondimensional_gibbs_free_energy(nondimensional_force, temperature) + nondimensional_gibbs_free_energy_per_link = + model.nondimensional_gibbs_free_energy_per_link( + nondimensional_force, + temperature, + ) + residual_abs = + nondimensional_gibbs_free_energy / number_of_links - + nondimensional_gibbs_free_energy_per_link + residual_rel = residual_abs / nondimensional_gibbs_free_energy_per_link + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::per_link::nondimensional_relative_gibbs_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + nondimensional_relative_gibbs_free_energy = + model.nondimensional_relative_gibbs_free_energy(nondimensional_force) + nondimensional_relative_gibbs_free_energy_per_link = + model.nondimensional_relative_gibbs_free_energy_per_link(nondimensional_force) + residual_abs = + nondimensional_relative_gibbs_free_energy / number_of_links - + nondimensional_relative_gibbs_free_energy_per_link + residual_rel = residual_abs / nondimensional_relative_gibbs_free_energy_per_link + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::relative::gibbs_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + gibbs_free_energy = model.gibbs_free_energy(force, temperature) + gibbs_free_energy_0 = model.gibbs_free_energy( + ZERO * BOLTZMANN_CONSTANT * temperature / link_length, + temperature, + ) + relative_gibbs_free_energy = model.relative_gibbs_free_energy(force, temperature) + residual_abs = gibbs_free_energy - gibbs_free_energy_0 - relative_gibbs_free_energy + residual_rel = residual_abs / gibbs_free_energy_0 + @test abs(residual_abs) <= + BOLTZMANN_CONSTANT * temperature * number_of_links * parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::relative::gibbs_free_energy_per_link" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + gibbs_free_energy_per_link = model.gibbs_free_energy_per_link(force, temperature) + gibbs_free_energy_per_link_0 = model.gibbs_free_energy_per_link( + ZERO * BOLTZMANN_CONSTANT * temperature / link_length, + temperature, + ) + relative_gibbs_free_energy_per_link = + model.relative_gibbs_free_energy_per_link(force, temperature) + residual_abs = + gibbs_free_energy_per_link - gibbs_free_energy_per_link_0 - + relative_gibbs_free_energy_per_link + residual_rel = residual_abs / gibbs_free_energy_per_link_0 + @test abs(residual_abs) <= BOLTZMANN_CONSTANT * temperature * parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::relative::nondimensional_gibbs_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + nondimensional_gibbs_free_energy = + model.nondimensional_gibbs_free_energy(nondimensional_force, temperature) + nondimensional_gibbs_free_energy_0 = + model.nondimensional_gibbs_free_energy(ZERO, temperature) + nondimensional_relative_gibbs_free_energy = + model.nondimensional_relative_gibbs_free_energy(nondimensional_force) + residual_abs = + nondimensional_gibbs_free_energy - nondimensional_gibbs_free_energy_0 - + nondimensional_relative_gibbs_free_energy + residual_rel = residual_abs / nondimensional_gibbs_free_energy_0 + @test abs(residual_abs) <= number_of_links * parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::relative::nondimensional_gibbs_free_energy_per_link" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + nondimensional_gibbs_free_energy_per_link = + model.nondimensional_gibbs_free_energy_per_link( + nondimensional_force, + temperature, + ) + nondimensional_gibbs_free_energy_per_link_0 = + model.nondimensional_gibbs_free_energy_per_link(ZERO, temperature) + nondimensional_relative_gibbs_free_energy_per_link = + model.nondimensional_relative_gibbs_free_energy_per_link(nondimensional_force) + residual_abs = + nondimensional_gibbs_free_energy_per_link - + nondimensional_gibbs_free_energy_per_link_0 - + nondimensional_relative_gibbs_free_energy_per_link + residual_rel = residual_abs / nondimensional_gibbs_free_energy_per_link_0 + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::zero::relative_gibbs_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + relative_gibbs_free_energy_0 = model.relative_gibbs_free_energy( + ZERO * BOLTZMANN_CONSTANT * temperature / link_length, + temperature, + ) + @test abs(relative_gibbs_free_energy_0) <= + ZERO * BOLTZMANN_CONSTANT * temperature * number_of_links + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::zero::relative_gibbs_free_energy_per_link" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + relative_gibbs_free_energy_per_link_0 = model.relative_gibbs_free_energy_per_link( + ZERO * BOLTZMANN_CONSTANT * temperature / link_length, + temperature, + ) + @test abs(relative_gibbs_free_energy_per_link_0) <= + ZERO * BOLTZMANN_CONSTANT * temperature + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::zero::nondimensional_relative_gibbs_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_relative_gibbs_free_energy_0 = + model.nondimensional_relative_gibbs_free_energy(ZERO) + @test abs(nondimensional_relative_gibbs_free_energy_0) <= ZERO * number_of_links + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::zero::nondimensional_relative_gibbs_free_energy_per_link" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_relative_gibbs_free_energy_per_link_0 = + model.nondimensional_relative_gibbs_free_energy_per_link(ZERO) + @test abs(nondimensional_relative_gibbs_free_energy_per_link_0) <= ZERO + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::connection::end_to_end_length" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + end_to_end_length = model.end_to_end_length(force, temperature) + h = parameters.rel_tol * BOLTZMANN_CONSTANT * temperature / link_length + end_to_end_length_from_derivative = + -( + model.relative_gibbs_free_energy(force + 0.5 * h, temperature) - + model.relative_gibbs_free_energy(force - 0.5 * h, temperature) + ) / h + residual_abs = end_to_end_length - end_to_end_length_from_derivative + residual_rel = residual_abs / end_to_end_length + @test abs(residual_rel) <= h + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::connection::end_to_end_length_per_link" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + end_to_end_length_per_link = model.end_to_end_length_per_link(force, temperature) + h = parameters.rel_tol * BOLTZMANN_CONSTANT * temperature / link_length + end_to_end_length_per_link_from_derivative = + -( + model.relative_gibbs_free_energy_per_link(force + 0.5 * h, temperature) - + model.relative_gibbs_free_energy_per_link(force - 0.5 * h, temperature) + ) / h + residual_abs = + end_to_end_length_per_link - end_to_end_length_per_link_from_derivative + residual_rel = residual_abs / end_to_end_length_per_link + @test abs(residual_rel) <= h + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::connection::nondimensional_end_to_end_length" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + nondimensional_end_to_end_length = + model.nondimensional_end_to_end_length(nondimensional_force) + h = parameters.rel_tol + nondimensional_end_to_end_length_from_derivative = + -( + model.nondimensional_relative_gibbs_free_energy( + nondimensional_force + 0.5 * h, + ) - model.nondimensional_relative_gibbs_free_energy( + nondimensional_force - 0.5 * h, + ) + ) / h + residual_abs = + nondimensional_end_to_end_length - + nondimensional_end_to_end_length_from_derivative + residual_rel = residual_abs / nondimensional_end_to_end_length + @test abs(residual_rel) <= h + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::connection::nondimensional_end_to_end_length_per_link" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + nondimensional_end_to_end_length_per_link = + model.nondimensional_end_to_end_length_per_link(nondimensional_force) + h = parameters.rel_tol + nondimensional_end_to_end_length_per_link_from_derivative = + -( + model.nondimensional_relative_gibbs_free_energy_per_link( + nondimensional_force + 0.5 * h, + ) - model.nondimensional_relative_gibbs_free_energy_per_link( + nondimensional_force - 0.5 * h, + ) + ) / h + residual_abs = + nondimensional_end_to_end_length_per_link - + nondimensional_end_to_end_length_per_link_from_derivative + residual_rel = residual_abs / nondimensional_end_to_end_length_per_link + @test abs(residual_rel) <= h + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::legendre::gibbs_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + end_to_end_length = model.end_to_end_length(force, temperature) + gibbs_free_energy = model.gibbs_free_energy(force, temperature) + gibbs_free_energy_legendre = + model.legendre.helmholtz_free_energy(force, temperature) - + force * end_to_end_length + residual_abs = gibbs_free_energy - gibbs_free_energy_legendre + residual_rel = residual_abs / gibbs_free_energy + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::legendre::gibbs_free_energy_per_link" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + end_to_end_length_per_link = model.end_to_end_length_per_link(force, temperature) + gibbs_free_energy_per_link = model.gibbs_free_energy_per_link(force, temperature) + gibbs_free_energy_per_link_legendre = + model.legendre.helmholtz_free_energy_per_link(force, temperature) - + force * end_to_end_length_per_link + residual_abs = gibbs_free_energy_per_link - gibbs_free_energy_per_link_legendre + residual_rel = residual_abs / gibbs_free_energy_per_link + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::legendre::relative_gibbs_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + end_to_end_length = model.end_to_end_length(force, temperature) + end_to_end_length_0 = model.end_to_end_length( + ZERO * BOLTZMANN_CONSTANT * temperature / link_length, + temperature, + ) + relative_gibbs_free_energy = model.relative_gibbs_free_energy(force, temperature) + relative_gibbs_free_energy_legendre = + model.legendre.relative_helmholtz_free_energy(force, temperature) - + force * end_to_end_length + + ZERO * BOLTZMANN_CONSTANT * temperature / link_length * end_to_end_length_0 + residual_abs = relative_gibbs_free_energy - relative_gibbs_free_energy_legendre + residual_rel = residual_abs / relative_gibbs_free_energy + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::legendre::relative_gibbs_free_energy_per_link" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + end_to_end_length_per_link = model.end_to_end_length_per_link(force, temperature) + end_to_end_length_per_link_0 = model.end_to_end_length_per_link( + ZERO * BOLTZMANN_CONSTANT * temperature / link_length, + temperature, + ) + relative_gibbs_free_energy_per_link = + model.relative_gibbs_free_energy_per_link(force, temperature) + relative_gibbs_free_energy_per_link_legendre = + model.legendre.relative_helmholtz_free_energy_per_link(force, temperature) - + force * end_to_end_length_per_link + + ZERO * BOLTZMANN_CONSTANT * temperature / link_length * + end_to_end_length_per_link_0 + residual_abs = + relative_gibbs_free_energy_per_link - + relative_gibbs_free_energy_per_link_legendre + residual_rel = residual_abs / relative_gibbs_free_energy_per_link + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::legendre::nondimensional_gibbs_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_end_to_end_length = + model.nondimensional_end_to_end_length(nondimensional_force) + nondimensional_gibbs_free_energy = + model.nondimensional_gibbs_free_energy(nondimensional_force, temperature) + nondimensional_gibbs_free_energy_legendre = + model.legendre.nondimensional_helmholtz_free_energy( + nondimensional_force, + temperature, + ) - nondimensional_force * nondimensional_end_to_end_length + residual_abs = + nondimensional_gibbs_free_energy - nondimensional_gibbs_free_energy_legendre + residual_rel = residual_abs / nondimensional_gibbs_free_energy + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::legendre::nondimensional_gibbs_free_energy_per_link" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_end_to_end_length_per_link = + model.nondimensional_end_to_end_length_per_link(nondimensional_force) + nondimensional_gibbs_free_energy_per_link = + model.nondimensional_gibbs_free_energy_per_link( + nondimensional_force, + temperature, + ) + nondimensional_gibbs_free_energy_per_link_legendre = + model.legendre.nondimensional_helmholtz_free_energy_per_link( + nondimensional_force, + temperature, + ) - nondimensional_force * nondimensional_end_to_end_length_per_link + residual_abs = + nondimensional_gibbs_free_energy_per_link - + nondimensional_gibbs_free_energy_per_link_legendre + residual_rel = residual_abs / nondimensional_gibbs_free_energy_per_link + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::legendre::nondimensional_relative_gibbs_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + nondimensional_end_to_end_length = + model.nondimensional_end_to_end_length(nondimensional_force) + nondimensional_end_to_end_length_0 = model.nondimensional_end_to_end_length(ZERO) + nondimensional_relative_gibbs_free_energy = + model.nondimensional_relative_gibbs_free_energy(nondimensional_force) + nondimensional_relative_gibbs_free_energy_legendre = + model.legendre.nondimensional_relative_helmholtz_free_energy( + nondimensional_force, + ) - nondimensional_force * nondimensional_end_to_end_length + + ZERO * nondimensional_end_to_end_length_0 + residual_abs = + nondimensional_relative_gibbs_free_energy - + nondimensional_relative_gibbs_free_energy_legendre + residual_rel = residual_abs / nondimensional_relative_gibbs_free_energy + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::legendre::nondimensional_relative_gibbs_free_energy_per_link" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + nondimensional_end_to_end_length_per_link = + model.nondimensional_end_to_end_length_per_link(nondimensional_force) + nondimensional_end_to_end_length_per_link_0 = + model.nondimensional_end_to_end_length_per_link(ZERO) + nondimensional_relative_gibbs_free_energy_per_link = + model.nondimensional_relative_gibbs_free_energy_per_link(nondimensional_force) + nondimensional_relative_gibbs_free_energy_per_link_legendre = + model.legendre.nondimensional_relative_helmholtz_free_energy_per_link( + nondimensional_force, + ) - nondimensional_force * nondimensional_end_to_end_length_per_link + + ZERO * nondimensional_end_to_end_length_per_link_0 + residual_abs = + nondimensional_relative_gibbs_free_energy_per_link - + nondimensional_relative_gibbs_free_energy_per_link_legendre + residual_rel = residual_abs / nondimensional_relative_gibbs_free_energy_per_link + @test abs(residual_abs) <= parameters.abs_tol && + abs(residual_rel) <= parameters.rel_tol + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::legendre_connection::force" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + h = parameters.rel_tol * BOLTZMANN_CONSTANT * temperature / link_length + force_from_derivative = + ( + model.legendre.relative_helmholtz_free_energy( + force + 0.5 * h, + temperature, + ) - + model.legendre.relative_helmholtz_free_energy(force - 0.5 * h, temperature) + ) / ( + model.end_to_end_length(force + 0.5 * h, temperature) - + model.end_to_end_length(force - 0.5 * h, temperature) + ) + residual_abs = force - force_from_derivative + residual_rel = residual_abs / force + @test abs(residual_rel) <= h + end +end + +@testset "physics::single_chain::wlc::thermodynamics::isotensional::test::legendre_connection::nondimensional_force" begin + for _ = 1:parameters.number_of_loops + number_of_links = + rand(parameters.number_of_links_minimum:parameters.number_of_links_maximum) + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.persistance_length_reference + + parameters.persistance_length_scale * (0.5 - rand()) + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + h = parameters.rel_tol + nondimensional_force_from_derivative = + ( + model.legendre.nondimensional_relative_helmholtz_free_energy_per_link( + nondimensional_force + 0.5 * h, + ) - model.legendre.nondimensional_relative_helmholtz_free_energy_per_link( + nondimensional_force - 0.5 * h, + ) + ) / ( + model.nondimensional_end_to_end_length_per_link( + nondimensional_force + 0.5 * h, + ) - model.nondimensional_end_to_end_length_per_link( + nondimensional_force - 0.5 * h, + ) + ) + residual_abs = nondimensional_force - nondimensional_force_from_derivative + residual_rel = residual_abs / nondimensional_force + @test abs(residual_rel) <= h + end +end + +end diff --git a/src/physics/single_chain/wlc/thermodynamics/isotensional/test.py b/src/physics/single_chain/wlc/thermodynamics/isotensional/test.py new file mode 100644 index 00000000..8e4db3b4 --- /dev/null +++ b/src/physics/single_chain/wlc/thermodynamics/isotensional/test.py @@ -0,0 +1,2188 @@ +"""Module to test the local module. + +""" +import unittest +import numpy as np +from polymers import physics +from ..test import Parameters + +parameters = Parameters() +WLC = physics.single_chain.wlc.thermodynamics.isotensional.WLC + + +class Base(unittest.TestCase): + """Class for basic tests. + + """ + def test_init(self): + """Function to test instantiation. + + """ + for _ in range(parameters.number_of_loops): + _ = WLC( + parameters.number_of_links_minimum, + parameters.link_length_reference, + parameters.hinge_mass_reference, + parameters.persistance_length_reference + ) + + def test_number_of_links(self): + """Function to test the number of links during instantiation. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + self.assertEqual( + number_of_links, + WLC( + number_of_links, + parameters.link_length_reference, + parameters.hinge_mass_reference, + parameters.persistance_length_reference + ).number_of_links + ) + + def test_link_length(self): + """Function to test the link length during instantiation. + + """ + for _ in range(parameters.number_of_loops): + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + self.assertEqual( + link_length, + WLC( + parameters.number_of_links_minimum, + link_length, + parameters.hinge_mass_reference, + parameters.persistance_length_reference + ).link_length + ) + + def test_hinge_mass(self): + """Function to test the hinge mass during instantiation. + + """ + for _ in range(parameters.number_of_loops): + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + self.assertEqual( + hinge_mass, + WLC( + parameters.number_of_links_minimum, + parameters.link_length_reference, + hinge_mass, + parameters.persistance_length_reference + ).hinge_mass + ) + + def test_persistance_length(self): + """Function to test the persistance length during instantiation. + + """ + for _ in range(parameters.number_of_loops): + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + self.assertEqual( + persistance_length, + WLC( + parameters.number_of_links_minimum, + parameters.link_length_reference, + parameters.hinge_mass_reference, + persistance_length + ).persistance_length + ) + + def test_all_parameters(self): + """Function to test all parameters during instantiation. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + self.assertEqual( + number_of_links, + model.number_of_links + ) + self.assertEqual( + link_length, + model.link_length + ) + self.assertEqual( + hinge_mass, + model.hinge_mass, + persistance_length + ) + self.assertEqual( + persistance_length, + model.persistance_length + ) + + +class Nondimensional(unittest.TestCase): + """Class for nondimensionalization tests. + + """ + def test_end_to_end_length(self): + """Function to test the nondimensionalization + of the end-to-end length. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + nondimensional_end_to_end_length = \ + model.nondimensional_end_to_end_length( + np.array(nondimensional_force) + ) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + end_to_end_length = \ + model.end_to_end_length( + np.array(force), + temperature + ) + residual_abs = \ + end_to_end_length \ + / link_length \ + - nondimensional_end_to_end_length + residual_rel = \ + residual_abs / \ + nondimensional_end_to_end_length + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + def test_end_to_end_length_per_link(self): + """Function to test the nondimensionalization + of the end-to-end length per link. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + nondimensional_end_to_end_length_per_link = \ + model.nondimensional_end_to_end_length_per_link( + np.array(nondimensional_force) + ) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + end_to_end_length_per_link = \ + model.end_to_end_length_per_link( + np.array(force), + temperature + ) + residual_abs = \ + end_to_end_length_per_link \ + / link_length \ + - nondimensional_end_to_end_length_per_link + residual_rel = \ + residual_abs / \ + nondimensional_end_to_end_length_per_link + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + def test_gibbs_free_energy(self): + """Function to test the nondimensionalization + of the Gibbs free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + nondimensional_gibbs_free_energy = \ + model.nondimensional_gibbs_free_energy( + np.array(nondimensional_force), + temperature + ) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + gibbs_free_energy = \ + model.gibbs_free_energy( + np.array(force), + temperature + ) + residual_abs = \ + gibbs_free_energy / \ + parameters.boltzmann_constant/temperature \ + - nondimensional_gibbs_free_energy + residual_rel = \ + residual_abs / \ + nondimensional_gibbs_free_energy + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + def test_gibbs_free_energy_per_link(self): + """Function to test the nondimensionalization + of the Gibbs free energy per link. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + nondimensional_gibbs_free_energy = \ + model.nondimensional_gibbs_free_energy_per_link( + np.array(nondimensional_force), + temperature + ) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + gibbs_free_energy = \ + model.gibbs_free_energy_per_link( + np.array(force), + temperature + ) + residual_abs = \ + gibbs_free_energy / \ + parameters.boltzmann_constant/temperature \ + - nondimensional_gibbs_free_energy + residual_rel = \ + residual_abs / \ + nondimensional_gibbs_free_energy + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + def test_relative_gibbs_free_energy(self): + """Function to test the nondimensionalization + of the relative Gibbs free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + nondimensional_gibbs_free_energy = \ + model.nondimensional_relative_gibbs_free_energy( + np.array(nondimensional_force) + ) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + gibbs_free_energy = \ + model.relative_gibbs_free_energy( + np.array(force), + temperature + ) + residual_abs = \ + gibbs_free_energy / \ + parameters.boltzmann_constant/temperature \ + - nondimensional_gibbs_free_energy + residual_rel = \ + residual_abs / \ + nondimensional_gibbs_free_energy + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + def test_relative_gibbs_free_energy_per_link(self): + """Function to test the nondimensionalization + of the relative Gibbs free energy per link. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + nondimensional_gibbs_free_energy = \ + model.nondimensional_relative_gibbs_free_energy_per_link( + np.array(nondimensional_force) + ) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + gibbs_free_energy = \ + model.relative_gibbs_free_energy_per_link( + np.array(force), + temperature + ) + residual_abs = \ + gibbs_free_energy / \ + parameters.boltzmann_constant/temperature \ + - nondimensional_gibbs_free_energy + residual_rel = \ + residual_abs / \ + nondimensional_gibbs_free_energy + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + +class PerLink(unittest.TestCase): + """Class for per-linkness tests. + + """ + def test_end_to_end_length(self): + """Function to test the per-linkness + of the end-to-end length. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + end_to_end_length = \ + model.end_to_end_length( + np.array(force), + temperature + ) + end_to_end_length_per_link = \ + model.end_to_end_length_per_link( + np.array(force), + temperature + ) + residual_abs = \ + end_to_end_length / \ + number_of_links \ + - end_to_end_length_per_link + residual_rel = \ + residual_abs / \ + end_to_end_length_per_link + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + def test_nondimensional_end_to_end_length(self): + """Function to test the per-linkness + of the nondimensional end-to-end length. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + nondimensional_end_to_end_length = \ + model.nondimensional_end_to_end_length( + np.array(nondimensional_force) + ) + nondimensional_end_to_end_length_per_link = \ + model.nondimensional_end_to_end_length_per_link( + np.array(nondimensional_force) + ) + residual_abs = \ + nondimensional_end_to_end_length / \ + number_of_links \ + - nondimensional_end_to_end_length_per_link + residual_rel = \ + residual_abs / \ + nondimensional_end_to_end_length_per_link + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + def test_gibbs_free_energy(self): + """Function to test the per-linkness + of the Gibbs free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + gibbs_free_energy = \ + model.gibbs_free_energy( + np.array(force), + temperature + ) + gibbs_free_energy_per_link = \ + model.gibbs_free_energy_per_link( + np.array(force), + temperature + ) + residual_abs = \ + gibbs_free_energy / \ + number_of_links \ + - gibbs_free_energy_per_link + residual_rel = \ + residual_abs / \ + gibbs_free_energy_per_link + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + def test_relative_gibbs_free_energy(self): + """Function to test the per-linkness + of the relative Gibbs free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + relative_gibbs_free_energy = \ + model.relative_gibbs_free_energy( + np.array(force), + temperature + ) + relative_gibbs_free_energy_per_link = \ + model.relative_gibbs_free_energy_per_link( + np.array(force), + temperature + ) + residual_abs = \ + relative_gibbs_free_energy / \ + number_of_links \ + - relative_gibbs_free_energy_per_link + residual_rel = \ + residual_abs / \ + relative_gibbs_free_energy_per_link + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + def test_nondimensional_gibbs_free_energy(self): + """Function to test the per-linkness + of the nondimensional Gibbs free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + nondimensional_gibbs_free_energy = \ + model.nondimensional_gibbs_free_energy( + np.array(nondimensional_force), + temperature + ) + nondimensional_gibbs_free_energy_per_link = \ + model.nondimensional_gibbs_free_energy_per_link( + np.array(nondimensional_force), + temperature + ) + residual_abs = \ + nondimensional_gibbs_free_energy / \ + number_of_links \ + - nondimensional_gibbs_free_energy_per_link + residual_rel = \ + residual_abs / \ + nondimensional_gibbs_free_energy_per_link + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + def test_nondimensional_relative_gibbs_free_energy(self): + """Function to test the per-linkness + of the nondimensional relative Gibbs free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + nondimensional_relative_gibbs_free_energy = \ + model.nondimensional_relative_gibbs_free_energy( + np.array(nondimensional_force) + ) + nondimensional_relative_gibbs_free_energy_per_link = \ + model.nondimensional_relative_gibbs_free_energy_per_link( + np.array(nondimensional_force) + ) + residual_abs = \ + nondimensional_relative_gibbs_free_energy / \ + number_of_links \ + - nondimensional_relative_gibbs_free_energy_per_link + residual_rel = \ + residual_abs / \ + nondimensional_relative_gibbs_free_energy_per_link + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + +class Relative(unittest.TestCase): + """Class for relativeness tests. + + """ + def test_gibbs_free_energy(self): + """Function to test the relativeness + of the Gibbs free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + gibbs_free_energy = \ + model.gibbs_free_energy( + np.array(force), + temperature + ) + gibbs_free_energy_0 = \ + model.gibbs_free_energy( + np.array( + parameters.zero * + parameters.boltzmann_constant*temperature/link_length + ), + temperature + ) + relative_gibbs_free_energy = \ + model.relative_gibbs_free_energy( + np.array(force), + temperature + ) + residual_abs = \ + gibbs_free_energy \ + - gibbs_free_energy_0 \ + - relative_gibbs_free_energy + residual_rel = \ + residual_abs / \ + gibbs_free_energy_0 + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol * + parameters.boltzmann_constant*temperature/link_length + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + def test_gibbs_free_energy_per_link(self): + """Function to test the relativeness + of the Gibbs free energy per link. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + gibbs_free_energy_per_link = \ + model.gibbs_free_energy_per_link( + np.array(force), + temperature + ) + gibbs_free_energy_per_link_0 = \ + model.gibbs_free_energy_per_link( + np.array( + parameters.zero * + parameters.boltzmann_constant*temperature/link_length + ), + temperature + ) + relative_gibbs_free_energy_per_link = \ + model.relative_gibbs_free_energy_per_link( + np.array(force), + temperature + ) + residual_abs = \ + gibbs_free_energy_per_link \ + - gibbs_free_energy_per_link_0 \ + - relative_gibbs_free_energy_per_link + residual_rel = \ + residual_abs / \ + gibbs_free_energy_per_link_0 + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol * + parameters.boltzmann_constant*temperature/link_length + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + def test_nondimensional_gibbs_free_energy(self): + """Function to test the relativeness + of the nondimensional Gibbs free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + nondimensional_gibbs_free_energy = \ + model.nondimensional_gibbs_free_energy( + np.array(nondimensional_force), + temperature + ) + nondimensional_gibbs_free_energy_0 = \ + model.nondimensional_gibbs_free_energy( + np.array(parameters.zero), + temperature + ) + nondimensional_relative_gibbs_free_energy = \ + model.nondimensional_relative_gibbs_free_energy( + np.array(nondimensional_force) + ) + residual_abs = \ + nondimensional_gibbs_free_energy \ + - nondimensional_gibbs_free_energy_0 \ + - nondimensional_relative_gibbs_free_energy + residual_rel = \ + residual_abs / \ + nondimensional_gibbs_free_energy_0 + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + def test_nondimensional_gibbs_free_energy_per_link(self): + """Function to test the relativeness + of the nondimensional Gibbs free energy per link. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + nondimensional_gibbs_free_energy_per_link = \ + model.nondimensional_gibbs_free_energy_per_link( + np.array(nondimensional_force), + temperature + ) + nondimensional_gibbs_free_energy_per_link_0 = \ + model.nondimensional_gibbs_free_energy_per_link( + np.array(parameters.zero), + temperature + ) + nondimensional_relative_gibbs_free_energy_per_link = \ + model.nondimensional_relative_gibbs_free_energy_per_link( + np.array(nondimensional_force) + ) + residual_abs = \ + nondimensional_gibbs_free_energy_per_link \ + - nondimensional_gibbs_free_energy_per_link_0 \ + - nondimensional_relative_gibbs_free_energy_per_link + residual_rel = \ + residual_abs / \ + nondimensional_gibbs_free_energy_per_link_0 + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + +class Zero(unittest.TestCase): + """Class for zero tests. + + """ + def test_relative_gibbs_free_energy(self): + """Function to test the zero + of the relative Gibbs free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + relative_gibbs_free_energy_0 = \ + model.relative_gibbs_free_energy( + np.array( + parameters.zero * + parameters.boltzmann_constant*temperature/link_length + ), + temperature + ) + self.assertLessEqual( + relative_gibbs_free_energy_0, + parameters.abs_tol * + parameters.boltzmann_constant*temperature + ) + + def test_relative_gibbs_free_energy_per_link(self): + """Function to test the zero + of the relative Gibbs free energy per link. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + relative_gibbs_free_energy_per_link_0 = \ + model.relative_gibbs_free_energy_per_link( + np.array( + parameters.zero * + parameters.boltzmann_constant*temperature/link_length + ), + temperature + ) + self.assertLessEqual( + relative_gibbs_free_energy_per_link_0, + parameters.abs_tol * + parameters.boltzmann_constant*temperature + ) + + def test_nondimensional_relative_gibbs_free_energy(self): + """Function to test the zero + of the relative Gibbs free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_relative_gibbs_free_energy_0 = \ + model.nondimensional_relative_gibbs_free_energy( + np.array(parameters.zero) + ) + self.assertLessEqual( + nondimensional_relative_gibbs_free_energy_0, + parameters.abs_tol + ) + + def test_nondimensional_relative_gibbs_free_energy_per_link(self): + """Function to test the zero + of the nondimensional relative Gibbs free energy per link. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_relative_gibbs_free_energy_per_link_0 = \ + model.nondimensional_relative_gibbs_free_energy_per_link( + np.array(parameters.zero) + ) + self.assertLessEqual( + nondimensional_relative_gibbs_free_energy_per_link_0, + parameters.abs_tol + ) + + +class Connection(unittest.TestCase): + """Class for connection tests. + + """ + def test_end_to_end_length(self): + """Function to test the connection + of the end-to-end length. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + end_to_end_length = \ + model.end_to_end_length( + np.array(force), + temperature + ) + h_step = parameters.rel_tol * \ + parameters.boltzmann_constant*temperature/link_length + end_to_end_length_from_derivative = -( + model.relative_gibbs_free_energy( + np.array(force + 0.5*h_step), + temperature + ) + - model.relative_gibbs_free_energy( + np.array(force - 0.5*h_step), + temperature + ))/h_step + residual_abs = \ + end_to_end_length \ + - end_to_end_length_from_derivative + self.assertLessEqual( + np.abs(residual_abs), h_step + ) + + def test_end_to_end_length_per_link(self): + """Function to test the connection + of the end-to-end length per link. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + end_to_end_length_per_link = \ + model.end_to_end_length_per_link( + np.array(force), + temperature + ) + h_step = parameters.rel_tol * \ + parameters.boltzmann_constant*temperature/link_length + end_to_end_length_per_link_from_derivative = -( + model.relative_gibbs_free_energy_per_link( + np.array(force + 0.5*h_step), + temperature + ) + - model.relative_gibbs_free_energy_per_link( + np.array(force - 0.5*h_step), + temperature + ))/h_step + residual_abs = \ + end_to_end_length_per_link \ + - end_to_end_length_per_link_from_derivative + self.assertLessEqual( + np.abs(residual_abs), h_step + ) + + def test_nondimensional_end_to_end_length(self): + """Function to test the connection + of the nondimensional end-to-end length. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + nondimensional_end_to_end_length = \ + model.nondimensional_end_to_end_length( + np.array(nondimensional_force) + ) + h_step = parameters.rel_tol + nondimensional_end_to_end_length_from_derivative = -( + model.nondimensional_relative_gibbs_free_energy( + np.array(nondimensional_force + 0.5*h_step) + ) + - model.nondimensional_relative_gibbs_free_energy( + np.array(nondimensional_force - 0.5*h_step) + ))/h_step + residual_abs = \ + nondimensional_end_to_end_length \ + - nondimensional_end_to_end_length_from_derivative + self.assertLessEqual( + np.abs(residual_abs), h_step + ) + + def test_nondimensional_end_to_end_length_per_link(self): + """Function to test the connection + of the nondimensional end-to-end length per link. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters.nondimensional_force_reference + \ + parameters.nondimensional_force_scale*(0.5 - np.random.rand()) + nondimensional_end_to_end_length_per_link = \ + model.nondimensional_end_to_end_length_per_link( + np.array(nondimensional_force) + ) + h_step = parameters.rel_tol + nondimensional_end_to_end_length_per_link_from_derivative = -( + model.nondimensional_relative_gibbs_free_energy_per_link( + np.array(nondimensional_force + 0.5*h_step) + ) + - model.nondimensional_relative_gibbs_free_energy_per_link( + np.array(nondimensional_force - 0.5*h_step) + ))/h_step + residual_abs = \ + nondimensional_end_to_end_length_per_link \ + - nondimensional_end_to_end_length_per_link_from_derivative + self.assertLessEqual( + np.abs(residual_abs), h_step + ) + + +class Legendre(unittest.TestCase): + """Class for Legendre tests. + + """ + def test_gibbs_free_energy(self): + """Function to test the Legendre transformation + of the Gibbs free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters. \ + nondimensional_force_reference + \ + parameters. \ + nondimensional_force_scale * \ + (0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + end_to_end_length = \ + model.end_to_end_length( + np.array(force), + temperature + ) + gibbs_free_energy = \ + model.gibbs_free_energy( + np.array(force), + temperature + ) + gibbs_free_energy_legendre = \ + model.legendre.helmholtz_free_energy( + np.array(force), + temperature + ) - force*end_to_end_length + residual_abs = \ + gibbs_free_energy \ + - gibbs_free_energy_legendre + residual_rel = \ + residual_abs / \ + gibbs_free_energy + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + def test_gibbs_free_energy_per_link(self): + """Function to test the Legendre transformation + of the Gibbs free energy per link. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters. \ + nondimensional_force_reference + \ + parameters. \ + nondimensional_force_scale * \ + (0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + end_to_end_length_per_link = \ + model.end_to_end_length_per_link( + np.array(force), + temperature + ) + gibbs_free_energy_per_link = \ + model.gibbs_free_energy_per_link( + np.array(force), + temperature + ) + gibbs_free_energy_per_link_legendre = \ + model.legendre.helmholtz_free_energy_per_link( + np.array(force), + temperature + ) - force*end_to_end_length_per_link + residual_abs = \ + gibbs_free_energy_per_link \ + - gibbs_free_energy_per_link_legendre + residual_rel = \ + residual_abs / \ + gibbs_free_energy_per_link + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + def test_relative_gibbs_free_energy(self): + """Function to test the Legendre transformation + of the relative Gibbs free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters. \ + nondimensional_force_reference + \ + parameters. \ + nondimensional_force_scale * \ + (0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + end_to_end_length = \ + model.end_to_end_length( + np.array(force), + temperature + ) + end_to_end_length_0 = \ + model.end_to_end_length( + np.array( + parameters.boltzmann_constant*temperature/link_length + * parameters.zero + ), + temperature + ) + relative_gibbs_free_energy = \ + model.relative_gibbs_free_energy( + np.array(force), + temperature + ) + relative_gibbs_free_energy_legendre = \ + model.legendre.relative_helmholtz_free_energy( + np.array(force), + temperature + ) - force*end_to_end_length \ + + end_to_end_length_0 \ + * parameters.boltzmann_constant*temperature/link_length \ + * parameters.zero + residual_abs = \ + relative_gibbs_free_energy \ + - relative_gibbs_free_energy_legendre + residual_rel = \ + residual_abs / \ + relative_gibbs_free_energy + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + def test_relative_gibbs_free_energy_per_link(self): + """Function to test the Legendre transformation + of the relative Gibbs free energy per link. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters. \ + nondimensional_force_reference + \ + parameters. \ + nondimensional_force_scale * \ + (0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + end_to_end_length_per_link = \ + model.end_to_end_length_per_link( + np.array(force), + temperature + ) + end_to_end_length_per_link_0 = \ + model.end_to_end_length_per_link( + np.array( + parameters.boltzmann_constant*temperature/link_length + * parameters.zero + ), + temperature + ) + relative_gibbs_free_energy_per_link = \ + model.relative_gibbs_free_energy_per_link( + np.array(force), + temperature + ) + relative_gibbs_free_energy_per_link_legendre = \ + model.legendre.relative_helmholtz_free_energy_per_link( + np.array(force), + temperature + ) - force*end_to_end_length_per_link \ + + end_to_end_length_per_link_0 \ + * parameters.boltzmann_constant*temperature/link_length \ + * parameters.zero + residual_abs = \ + relative_gibbs_free_energy_per_link \ + - relative_gibbs_free_energy_per_link_legendre + residual_rel = \ + residual_abs / \ + relative_gibbs_free_energy_per_link + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + def test_nondimensional_gibbs_free_energy(self): + """Function to test the Legendre transformation + of the nondimensional Gibbs free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters. \ + nondimensional_force_reference + \ + parameters. \ + nondimensional_force_scale * \ + (0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + nondimensional_end_to_end_length = \ + model.nondimensional_end_to_end_length( + np.array(nondimensional_force) + ) + nondimensional_gibbs_free_energy = \ + model.nondimensional_gibbs_free_energy( + np.array(nondimensional_force), + temperature + ) + nondimensional_gibbs_free_energy_legendre = \ + model.legendre.nondimensional_helmholtz_free_energy( + np.array(nondimensional_force), + temperature + ) - nondimensional_force*nondimensional_end_to_end_length + residual_abs = \ + nondimensional_gibbs_free_energy \ + - nondimensional_gibbs_free_energy_legendre + residual_rel = \ + residual_abs / \ + nondimensional_gibbs_free_energy + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + def test_nondimensional_gibbs_free_energy_per_link(self): + """Function to test the Legendre transformation + of the nondimensional Gibbs free energy per link. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters. \ + nondimensional_force_reference + \ + parameters. \ + nondimensional_force_scale * \ + (0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + nondimensional_end_to_end_length_per_link = \ + model.nondimensional_end_to_end_length_per_link( + np.array(nondimensional_force) + ) + nondimensional_gibbs_free_energy_per_link = \ + model.nondimensional_gibbs_free_energy_per_link( + np.array(nondimensional_force), + temperature + ) + nondimensional_gibbs_free_energy_per_link_legendre = \ + model.legendre.nondimensional_helmholtz_free_energy_per_link( + np.array(nondimensional_force), + temperature + ) - nondimensional_force * \ + nondimensional_end_to_end_length_per_link + residual_abs = \ + nondimensional_gibbs_free_energy_per_link \ + - nondimensional_gibbs_free_energy_per_link_legendre + residual_rel = \ + residual_abs / \ + nondimensional_gibbs_free_energy_per_link + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + def test_nondimensional_relative_gibbs_free_energy(self): + """Function to test the Legendre transformation + of the nondimensional relative Gibbs free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters. \ + nondimensional_force_reference + \ + parameters. \ + nondimensional_force_scale * \ + (0.5 - np.random.rand()) + nondimensional_end_to_end_length = \ + model.nondimensional_end_to_end_length( + np.array(nondimensional_force) + ) + nondimensional_end_to_end_length_0 = \ + model.nondimensional_end_to_end_length( + np.array(parameters.zero) + ) + nondimensional_relative_gibbs_free_energy = \ + model.nondimensional_relative_gibbs_free_energy( + np.array(nondimensional_force) + ) + nondimensional_relative_gibbs_free_energy_legendre = \ + model.legendre.nondimensional_relative_helmholtz_free_energy( + np.array(nondimensional_force) + ) - nondimensional_force*nondimensional_end_to_end_length \ + + parameters.zero*nondimensional_end_to_end_length_0 + residual_abs = \ + nondimensional_relative_gibbs_free_energy \ + - nondimensional_relative_gibbs_free_energy_legendre + residual_rel = \ + residual_abs / \ + nondimensional_relative_gibbs_free_energy + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + def test_nondimensional_relative_gibbs_free_energy_per_link(self): + """Function to test the Legendre transformation + of the nondimensional relative Gibbs free energy per link. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters. \ + nondimensional_force_reference + \ + parameters. \ + nondimensional_force_scale * \ + (0.5 - np.random.rand()) + nondimensional_end_to_end_length_per_link = \ + model.nondimensional_end_to_end_length_per_link( + np.array(nondimensional_force) + ) + nondimensional_end_to_end_length_per_link_0 = \ + model.nondimensional_end_to_end_length_per_link( + np.array(parameters.zero) + ) + nondimensional_relative_gibbs_free_energy_per_link = \ + model.nondimensional_relative_gibbs_free_energy_per_link( + np.array(nondimensional_force) + ) + nondimensional_relative_gibbs_free_energy_per_link_legendre = \ + model.legendre.\ + nondimensional_relative_helmholtz_free_energy_per_link( + np.array(nondimensional_force) + ) - nondimensional_force * \ + nondimensional_end_to_end_length_per_link \ + + parameters.zero*nondimensional_end_to_end_length_per_link_0 + residual_abs = \ + nondimensional_relative_gibbs_free_energy_per_link \ + - nondimensional_relative_gibbs_free_energy_per_link_legendre + residual_rel = \ + residual_abs / \ + nondimensional_relative_gibbs_free_energy_per_link + self.assertLessEqual( + np.abs(residual_abs), + parameters.abs_tol + ) + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol + ) + + +class LegendreConnection(unittest.TestCase): + """Class for Legendre connection tests. + + """ + def test_force(self): + """Function to test the Legendre transformation connection + of the force. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters. \ + nondimensional_force_reference + \ + parameters. \ + nondimensional_force_scale * \ + (0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + h_step = parameters.rel_tol * \ + parameters.boltzmann_constant*temperature/link_length + force_from_derivative = ( + model.legendre.relative_helmholtz_free_energy( + np.array( + force + 0.5*h_step + ), temperature + ) + - model.legendre.relative_helmholtz_free_energy( + np.array( + force - 0.5*h_step + ), temperature + ))/( + model.end_to_end_length( + np.array( + force + 0.5*h_step + ), temperature + ) + - model.end_to_end_length( + np.array( + force - 0.5*h_step + ), temperature + )) + residual_abs = \ + force \ + - force_from_derivative + residual_rel = residual_abs/force + self.assertLessEqual( + np.abs(residual_rel), h_step + ) + + def test_nondimensional_force(self): + """Function to test the Legendre transformation connection + of the nondimensional force. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = \ + np.random.randint( + parameters.number_of_links_minimum, + high=parameters.number_of_links_maximum + ) + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.persistance_length_reference + \ + parameters.persistance_length_scale*(0.5 - np.random.rand()) + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters. \ + nondimensional_force_reference + \ + parameters. \ + nondimensional_force_scale * \ + (0.5 - np.random.rand()) + h_step = parameters.rel_tol + nondimensional_force_from_derivative = ( + model.legendre. + nondimensional_relative_helmholtz_free_energy_per_link( + np.array( + nondimensional_force + 0.5*h_step + ) + ) + - model.legendre. + nondimensional_relative_helmholtz_free_energy_per_link( + np.array( + nondimensional_force - 0.5*h_step + ) + ))/( + model.nondimensional_end_to_end_length_per_link( + np.array( + nondimensional_force + 0.5*h_step + ) + ) + - model.nondimensional_end_to_end_length_per_link( + np.array( + nondimensional_force - 0.5*h_step + ) + )) + residual_abs = \ + nondimensional_force \ + - nondimensional_force_from_derivative + residual_rel = residual_abs/nondimensional_force + self.assertLessEqual( + np.abs(residual_rel), h_step + ) diff --git a/src/physics/single_chain/wlc/thermodynamics/isotensional/test.rs b/src/physics/single_chain/wlc/thermodynamics/isotensional/test.rs new file mode 100644 index 00000000..dcf33e48 --- /dev/null +++ b/src/physics/single_chain/wlc/thermodynamics/isotensional/test.rs @@ -0,0 +1,865 @@ +#![cfg(test)] +use super::*; +use crate::physics::single_chain::test::Parameters; +mod base +{ + use super::*; + use rand::Rng; + #[test] + fn init() + { + let parameters = Parameters::default(); + let _ = WLC::init(parameters.number_of_links_minimum, parameters.link_length_reference, parameters.hinge_mass_reference, parameters.persistance_length_reference); + } + #[test] + fn number_of_links() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + assert_eq!(number_of_links, WLC::init(number_of_links, parameters.link_length_reference, parameters.hinge_mass_reference, parameters.persistance_length_reference).number_of_links); + } + } + #[test] + fn link_length() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + assert_eq!(link_length, WLC::init(parameters.number_of_links_minimum, link_length, parameters.hinge_mass_reference, parameters.persistance_length_reference).link_length); + } + } + #[test] + fn hinge_mass() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + assert_eq!(hinge_mass, WLC::init(parameters.number_of_links_minimum, parameters.link_length_reference, hinge_mass, parameters.persistance_length_reference).hinge_mass); + } + } + #[test] + fn persistance_length() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + assert_eq!(persistance_length, WLC::init(parameters.number_of_links_minimum, parameters.link_length_reference, parameters.hinge_mass_reference, persistance_length).persistance_length); + } + } + #[test] + fn all_parameters() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + assert_eq!(number_of_links, model.number_of_links); + assert_eq!(link_length, model.link_length); + assert_eq!(hinge_mass, model.hinge_mass); + assert_eq!(persistance_length, model.persistance_length); + } + } +} +mod nondimensional +{ + use super::*; + use rand::Rng; + #[test] + fn end_to_end_length() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let nondimensional_end_to_end_length = model.nondimensional_end_to_end_length(&nondimensional_force); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let end_to_end_length = model.end_to_end_length(&force, &temperature); + let residual_abs = &end_to_end_length/link_length - &nondimensional_end_to_end_length; + let residual_rel = &residual_abs/&nondimensional_end_to_end_length; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } + #[test] + fn end_to_end_length_per_link() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let nondimensional_end_to_end_length_per_link = model.nondimensional_end_to_end_length_per_link(&nondimensional_force); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let end_to_end_length_per_link = model.end_to_end_length_per_link(&force, &temperature); + let residual_abs = &end_to_end_length_per_link/link_length - &nondimensional_end_to_end_length_per_link; + let residual_rel = &residual_abs/&nondimensional_end_to_end_length_per_link; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } + #[test] + fn gibbs_free_energy() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let nondimensional_gibbs_free_energy = model.nondimensional_gibbs_free_energy(&nondimensional_force, &temperature); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let gibbs_free_energy = model.gibbs_free_energy(&force, &temperature); + let residual_abs = &gibbs_free_energy/BOLTZMANN_CONSTANT/temperature - &nondimensional_gibbs_free_energy; + let residual_rel = &residual_abs/&nondimensional_gibbs_free_energy; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } + #[test] + fn gibbs_free_energy_per_link() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let nondimensional_gibbs_free_energy_per_link = model.nondimensional_gibbs_free_energy_per_link(&nondimensional_force, &temperature); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let gibbs_free_energy_per_link = model.gibbs_free_energy_per_link(&force, &temperature); + let residual_abs = &gibbs_free_energy_per_link/BOLTZMANN_CONSTANT/temperature - &nondimensional_gibbs_free_energy_per_link; + let residual_rel = &residual_abs/&nondimensional_gibbs_free_energy_per_link; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } + #[test] + fn relative_gibbs_free_energy() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let nondimensional_relative_gibbs_free_energy = model.nondimensional_relative_gibbs_free_energy(&nondimensional_force); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let relative_gibbs_free_energy = model.relative_gibbs_free_energy(&force, &temperature); + let residual_abs = &relative_gibbs_free_energy/BOLTZMANN_CONSTANT/temperature - &nondimensional_relative_gibbs_free_energy; + let residual_rel = &residual_abs/&nondimensional_relative_gibbs_free_energy; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } + #[test] + fn relative_gibbs_free_energy_per_link() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let nondimensional_relative_gibbs_free_energy_per_link = model.nondimensional_relative_gibbs_free_energy_per_link(&nondimensional_force); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let relative_gibbs_free_energy_per_link = model.relative_gibbs_free_energy_per_link(&force, &temperature); + let residual_abs = &relative_gibbs_free_energy_per_link/BOLTZMANN_CONSTANT/temperature - &nondimensional_relative_gibbs_free_energy_per_link; + let residual_rel = &residual_abs/&nondimensional_relative_gibbs_free_energy_per_link; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } +} +mod per_link +{ + use super::*; + use rand::Rng; + #[test] + fn end_to_end_length() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let end_to_end_length = model.end_to_end_length(&force, &temperature); + let end_to_end_length_per_link = model.end_to_end_length_per_link(&force, &temperature); + let residual_abs = &end_to_end_length/(number_of_links as f64) - &end_to_end_length_per_link; + let residual_rel = &residual_abs/&end_to_end_length_per_link; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } + #[test] + fn nondimensional_end_to_end_length() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let nondimensional_end_to_end_length = model.nondimensional_end_to_end_length(&nondimensional_force); + let nondimensional_end_to_end_length_per_link = model.nondimensional_end_to_end_length_per_link(&nondimensional_force); + let residual_abs = &nondimensional_end_to_end_length/(number_of_links as f64) - &nondimensional_end_to_end_length_per_link; + let residual_rel = &residual_abs/&nondimensional_end_to_end_length_per_link; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } + #[test] + fn gibbs_free_energy() + { + let parameters = Parameters::default(); + let mut rng = rand::thread_rng(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let gibbs_free_energy = model.gibbs_free_energy(&force, &temperature); + let gibbs_free_energy_per_link = model.gibbs_free_energy_per_link(&force, &temperature); + let residual_abs = &gibbs_free_energy/(number_of_links as f64) - &gibbs_free_energy_per_link; + let residual_rel = &residual_abs/&gibbs_free_energy_per_link; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } + #[test] + fn relative_gibbs_free_energy() + { + let parameters = Parameters::default(); + let mut rng = rand::thread_rng(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let relative_gibbs_free_energy = model.relative_gibbs_free_energy(&force, &temperature); + let relative_gibbs_free_energy_per_link = model.relative_gibbs_free_energy_per_link(&force, &temperature); + let residual_abs = &relative_gibbs_free_energy/(number_of_links as f64) - &relative_gibbs_free_energy_per_link; + let residual_rel = &residual_abs/&relative_gibbs_free_energy_per_link; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } + #[test] + fn nondimensional_gibbs_free_energy() + { + let parameters = Parameters::default(); + let mut rng = rand::thread_rng(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let nondimensional_gibbs_free_energy = model.nondimensional_gibbs_free_energy(&nondimensional_force, &temperature); + let nondimensional_gibbs_free_energy_per_link = model.nondimensional_gibbs_free_energy_per_link(&nondimensional_force, &temperature); + let residual_abs = &nondimensional_gibbs_free_energy/(number_of_links as f64) - &nondimensional_gibbs_free_energy_per_link; + let residual_rel = &residual_abs/&nondimensional_gibbs_free_energy_per_link; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } + #[test] + fn nondimensional_relative_gibbs_free_energy() + { + let parameters = Parameters::default(); + let mut rng = rand::thread_rng(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let nondimensional_relative_gibbs_free_energy = model.nondimensional_relative_gibbs_free_energy(&nondimensional_force); + let nondimensional_relative_gibbs_free_energy_per_link = model.nondimensional_relative_gibbs_free_energy_per_link(&nondimensional_force); + let residual_abs = &nondimensional_relative_gibbs_free_energy/(number_of_links as f64) - &nondimensional_relative_gibbs_free_energy_per_link; + let residual_rel = &residual_abs/&nondimensional_relative_gibbs_free_energy_per_link; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } +} +mod relative +{ + use super::*; + use crate::physics::single_chain::ZERO; + use rand::Rng; + #[test] + fn gibbs_free_energy() + { + let parameters = Parameters::default(); + let mut rng = rand::thread_rng(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let gibbs_free_energy = model.gibbs_free_energy(&force, &temperature); + let gibbs_free_energy_0 = model.gibbs_free_energy(&(ZERO*BOLTZMANN_CONSTANT*temperature/link_length), &temperature); + let relative_gibbs_free_energy = model.relative_gibbs_free_energy(&force, &temperature); + let residual_abs = &gibbs_free_energy - &gibbs_free_energy_0 - &relative_gibbs_free_energy; + let residual_rel = &residual_abs/&gibbs_free_energy_0; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } + #[test] + fn gibbs_free_energy_per_link() + { + let parameters = Parameters::default(); + let mut rng = rand::thread_rng(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let gibbs_free_energy_per_link = model.gibbs_free_energy_per_link(&force, &temperature); + let gibbs_free_energy_per_link_0 = model.gibbs_free_energy_per_link(&(ZERO*BOLTZMANN_CONSTANT*temperature/link_length), &temperature); + let relative_gibbs_free_energy_per_link = model.relative_gibbs_free_energy_per_link(&force, &temperature); + let residual_abs = &gibbs_free_energy_per_link - &gibbs_free_energy_per_link_0 - &relative_gibbs_free_energy_per_link; + let residual_rel = &residual_abs/&gibbs_free_energy_per_link_0; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } + #[test] + fn nondimensional_gibbs_free_energy() + { + let parameters = Parameters::default(); + let mut rng = rand::thread_rng(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let nondimensional_gibbs_free_energy = model.nondimensional_gibbs_free_energy(&nondimensional_force, &temperature); + let nondimensional_gibbs_free_energy_0 = model.nondimensional_gibbs_free_energy(&ZERO, &temperature); + let nondimensional_relative_gibbs_free_energy = model.nondimensional_relative_gibbs_free_energy(&nondimensional_force); + let residual_abs = &nondimensional_gibbs_free_energy - &nondimensional_gibbs_free_energy_0 - &nondimensional_relative_gibbs_free_energy; + let residual_rel = &residual_abs/&nondimensional_gibbs_free_energy_0; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } + #[test] + fn nondimensional_gibbs_free_energy_per_link() + { + let parameters = Parameters::default(); + let mut rng = rand::thread_rng(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let nondimensional_gibbs_free_energy_per_link = model.nondimensional_gibbs_free_energy_per_link(&nondimensional_force, &temperature); + let nondimensional_gibbs_free_energy_per_link_0 = model.nondimensional_gibbs_free_energy_per_link(&ZERO, &temperature); + let nondimensional_relative_gibbs_free_energy_per_link = model.nondimensional_relative_gibbs_free_energy_per_link(&nondimensional_force); + let residual_abs = &nondimensional_gibbs_free_energy_per_link - &nondimensional_gibbs_free_energy_per_link_0 - &nondimensional_relative_gibbs_free_energy_per_link; + let residual_rel = &residual_abs/&nondimensional_gibbs_free_energy_per_link_0; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } +} +mod zero +{ + use super::*; + use crate::physics::single_chain::ZERO; + use rand::Rng; + #[test] + fn relative_gibbs_free_energy() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let relative_gibbs_free_energy_0 = model.relative_gibbs_free_energy(&ZERO, &temperature); + assert!(relative_gibbs_free_energy_0.abs() <= (number_of_links as f64)*BOLTZMANN_CONSTANT*temperature*ZERO); + } + } + #[test] + fn relative_gibbs_free_energy_per_link() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let relative_gibbs_free_energy_per_link_0 = model.relative_gibbs_free_energy_per_link(&ZERO, &temperature); + assert!(relative_gibbs_free_energy_per_link_0.abs() <= BOLTZMANN_CONSTANT*temperature*ZERO); + } + } + #[test] + fn nondimensional_relative_gibbs_free_energy() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_relative_gibbs_free_energy_0 = model.nondimensional_relative_gibbs_free_energy(&ZERO); + assert!(nondimensional_relative_gibbs_free_energy_0.abs() <= ZERO); + } + } + #[test] + fn nondimensional_relative_gibbs_free_energy_per_link() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_relative_gibbs_free_energy_per_link_0 = model.nondimensional_relative_gibbs_free_energy_per_link(&ZERO); + assert!(nondimensional_relative_gibbs_free_energy_per_link_0.abs() <= ZERO); + } + } +} +mod connection +{ + use super::*; + use rand::Rng; + #[test] + fn end_to_end_length() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + 0.5*parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let end_to_end_length = model.end_to_end_length(&force, &temperature); + let h = parameters.rel_tol*BOLTZMANN_CONSTANT*temperature/link_length; + let end_to_end_length_from_derivative = -(model.relative_gibbs_free_energy(&(force + 0.5*h), &temperature) - model.relative_gibbs_free_energy(&(force - 0.5*h), &temperature))/h; + let residual_abs = &end_to_end_length - &end_to_end_length_from_derivative; + let residual_rel = &residual_abs/&end_to_end_length; + assert!(residual_rel.abs() <= h); + } + } + #[test] + fn end_to_end_length_per_link() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + 0.5*parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let end_to_end_length_per_link = model.end_to_end_length_per_link(&force, &temperature); + let h = parameters.rel_tol*BOLTZMANN_CONSTANT*temperature/link_length; + let end_to_end_length_per_link_from_derivative = -(model.relative_gibbs_free_energy_per_link(&(force + 0.5*h), &temperature) - model.relative_gibbs_free_energy_per_link(&(force - 0.5*h), &temperature))/h; + let residual_abs = &end_to_end_length_per_link - &end_to_end_length_per_link_from_derivative; + let residual_rel = &residual_abs/&end_to_end_length_per_link; + assert!(residual_rel.abs() <= h); + } + } + #[test] + fn nondimensional_end_to_end_length() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + 0.5*parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let nondimensional_end_to_end_length = model.nondimensional_end_to_end_length(&nondimensional_force); + let h = parameters.rel_tol; + let nondimensional_end_to_end_length_from_derivative = -(model.nondimensional_relative_gibbs_free_energy(&(nondimensional_force + 0.5*h)) - model.nondimensional_relative_gibbs_free_energy(&(nondimensional_force - 0.5*h)))/h; + let residual_abs = &nondimensional_end_to_end_length - &nondimensional_end_to_end_length_from_derivative; + let residual_rel = &residual_abs/&nondimensional_end_to_end_length; + assert!(residual_rel.abs() <= h); + } + } + #[test] + fn nondimensional_end_to_end_length_per_link() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + 0.5*parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let nondimensional_end_to_end_length_per_link = model.nondimensional_end_to_end_length_per_link(&nondimensional_force); + let h = parameters.rel_tol; + let nondimensional_end_to_end_length_per_link_from_derivative = -(model.nondimensional_relative_gibbs_free_energy_per_link(&(nondimensional_force + 0.5*h)) - model.nondimensional_relative_gibbs_free_energy_per_link(&(nondimensional_force - 0.5*h)))/h; + let residual_abs = &nondimensional_end_to_end_length_per_link - &nondimensional_end_to_end_length_per_link_from_derivative; + let residual_rel = &residual_abs/&nondimensional_end_to_end_length_per_link; + assert!(residual_rel.abs() <= h); + } + } +} +mod legendre +{ + use super::*; + use crate::physics::single_chain::ZERO; + use rand::Rng; + #[test] + fn gibbs_free_energy() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + 0.5*parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let end_to_end_length = model.end_to_end_length(&force, &temperature); + let gibbs_free_energy = model.gibbs_free_energy(&force, &temperature); + let gibbs_free_energy_legendre = model.legendre.helmholtz_free_energy(&force, &temperature) - force*end_to_end_length; + let residual_abs = &gibbs_free_energy - &gibbs_free_energy_legendre; + let residual_rel = &residual_abs/&gibbs_free_energy; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } + #[test] + fn gibbs_free_energy_per_link() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + 0.5*parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let end_to_end_length_per_link = model.end_to_end_length_per_link(&force, &temperature); + let gibbs_free_energy_per_link = model.gibbs_free_energy_per_link(&force, &temperature); + let gibbs_free_energy_per_link_legendre = model.legendre.helmholtz_free_energy_per_link(&force, &temperature) - force*end_to_end_length_per_link; + let residual_abs = &gibbs_free_energy_per_link - &gibbs_free_energy_per_link_legendre; + let residual_rel = &residual_abs/&gibbs_free_energy_per_link; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } + #[test] + fn relative_gibbs_free_energy() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + 0.5*parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let end_to_end_length = model.end_to_end_length(&force, &temperature); + let end_to_end_length_0 = model.end_to_end_length(&(ZERO*BOLTZMANN_CONSTANT*temperature/link_length), &temperature); + let relative_gibbs_free_energy = model.relative_gibbs_free_energy(&force, &temperature); + let relative_gibbs_free_energy_legendre = model.legendre.relative_helmholtz_free_energy(&force, &temperature) - force*end_to_end_length + ZERO*BOLTZMANN_CONSTANT*temperature/link_length*end_to_end_length_0; + let residual_abs = &relative_gibbs_free_energy - &relative_gibbs_free_energy_legendre; + let residual_rel = &residual_abs/&relative_gibbs_free_energy; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } + #[test] + fn relative_gibbs_free_energy_per_link() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + 0.5*parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let end_to_end_length_per_link = model.end_to_end_length_per_link(&force, &temperature); + let end_to_end_length_per_link_0 = model.end_to_end_length_per_link(&(ZERO*BOLTZMANN_CONSTANT*temperature/link_length), &temperature); + let relative_gibbs_free_energy_per_link = model.relative_gibbs_free_energy_per_link(&force, &temperature); + let relative_gibbs_free_energy_per_link_legendre = model.legendre.relative_helmholtz_free_energy_per_link(&force, &temperature) - force*end_to_end_length_per_link + ZERO*BOLTZMANN_CONSTANT*temperature/link_length*end_to_end_length_per_link_0; + let residual_abs = &relative_gibbs_free_energy_per_link - &relative_gibbs_free_energy_per_link_legendre; + let residual_rel = &residual_abs/&relative_gibbs_free_energy_per_link; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } + #[test] + fn nondimensional_gibbs_free_energy() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + 0.5*parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let nondimensional_end_to_end_length = model.nondimensional_end_to_end_length(&nondimensional_force); + let nondimensional_gibbs_free_energy = model.nondimensional_gibbs_free_energy(&nondimensional_force, &temperature); + let nondimensional_gibbs_free_energy_legendre = model.legendre.nondimensional_helmholtz_free_energy(&nondimensional_force, &temperature) - nondimensional_force*nondimensional_end_to_end_length; + let residual_abs = &nondimensional_gibbs_free_energy - &nondimensional_gibbs_free_energy_legendre; + let residual_rel = &residual_abs/&nondimensional_gibbs_free_energy; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } + #[test] + fn nondimensional_gibbs_free_energy_per_link() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + 0.5*parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let nondimensional_end_to_end_length_per_link = model.nondimensional_end_to_end_length_per_link(&nondimensional_force); + let nondimensional_gibbs_free_energy_per_link = model.nondimensional_gibbs_free_energy_per_link(&nondimensional_force, &temperature); + let nondimensional_gibbs_free_energy_per_link_legendre = model.legendre.nondimensional_helmholtz_free_energy_per_link(&nondimensional_force, &temperature) - nondimensional_force*nondimensional_end_to_end_length_per_link; + let residual_abs = &nondimensional_gibbs_free_energy_per_link - &nondimensional_gibbs_free_energy_per_link_legendre; + let residual_rel = &residual_abs/&nondimensional_gibbs_free_energy_per_link; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } + #[test] + fn nondimensional_relative_gibbs_free_energy() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + 0.5*parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let nondimensional_end_to_end_length = model.nondimensional_end_to_end_length(&nondimensional_force); + let nondimensional_end_to_end_length_0 = model.nondimensional_end_to_end_length(&ZERO); + let nondimensional_relative_gibbs_free_energy = model.nondimensional_relative_gibbs_free_energy(&nondimensional_force); + let nondimensional_relative_gibbs_free_energy_legendre = model.legendre.nondimensional_relative_helmholtz_free_energy(&nondimensional_force) - nondimensional_force*nondimensional_end_to_end_length + ZERO*nondimensional_end_to_end_length_0; + let residual_abs = &nondimensional_relative_gibbs_free_energy - &nondimensional_relative_gibbs_free_energy_legendre; + let residual_rel = &residual_abs/&nondimensional_relative_gibbs_free_energy; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } + #[test] + fn nondimensional_relative_gibbs_free_energy_per_link() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + 0.5*parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let nondimensional_end_to_end_length_per_link = model.nondimensional_end_to_end_length_per_link(&nondimensional_force); + let nondimensional_end_to_end_length_per_link_0 = model.nondimensional_end_to_end_length_per_link(&ZERO); + let nondimensional_relative_gibbs_free_energy_per_link = model.nondimensional_relative_gibbs_free_energy_per_link(&nondimensional_force); + let nondimensional_relative_gibbs_free_energy_per_link_legendre = model.legendre.nondimensional_relative_helmholtz_free_energy_per_link(&nondimensional_force) - nondimensional_force*nondimensional_end_to_end_length_per_link + ZERO*nondimensional_end_to_end_length_per_link_0; + let residual_abs = &nondimensional_relative_gibbs_free_energy_per_link - &nondimensional_relative_gibbs_free_energy_per_link_legendre; + let residual_rel = &residual_abs/&nondimensional_relative_gibbs_free_energy_per_link; + assert!(residual_abs.abs() <= parameters.abs_tol); + assert!(residual_rel.abs() <= parameters.rel_tol); + } + } +} +mod legendre_connection +{ + use super::*; + use rand::Rng; + #[test] + fn force() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + 0.5*parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let h = parameters.rel_tol*BOLTZMANN_CONSTANT*temperature/link_length; + let force_from_derivative = (model.legendre.relative_helmholtz_free_energy(&(force + 0.5*h), &temperature) - model.legendre.relative_helmholtz_free_energy(&(force - 0.5*h), &temperature))/(model.end_to_end_length(&(force + 0.5*h), &temperature) - model.end_to_end_length(&(force - 0.5*h), &temperature)); + let residual_abs = &force - &force_from_derivative; + let residual_rel = &residual_abs/&force; + assert!(residual_rel.abs() <= h); + } + } + #[test] + fn nondimensional_force() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = rng.gen_range(parameters.number_of_links_minimum..parameters.number_of_links_maximum); + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.persistance_length_reference + parameters.persistance_length_scale*(0.5 - rng.gen::()); + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + 0.5*parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let h = parameters.rel_tol; + let nondimensional_force_from_derivative = (model.legendre.nondimensional_relative_helmholtz_free_energy_per_link(&(nondimensional_force + 0.5*h)) - model.legendre.nondimensional_relative_helmholtz_free_energy_per_link(&(nondimensional_force - 0.5*h)))/(model.nondimensional_end_to_end_length_per_link(&(nondimensional_force + 0.5*h)) - model.nondimensional_end_to_end_length_per_link(&(nondimensional_force - 0.5*h))); + let residual_abs = &nondimensional_force - &nondimensional_force_from_derivative; + let residual_rel = &residual_abs/&nondimensional_force; + assert!(residual_rel.abs() <= h); + } + } +} \ No newline at end of file diff --git a/src/physics/single_chain/wlc/thermodynamics/mod.jl b/src/physics/single_chain/wlc/thermodynamics/mod.jl index 945dbfe3..0a5538f9 100644 --- a/src/physics/single_chain/wlc/thermodynamics/mod.jl +++ b/src/physics/single_chain/wlc/thermodynamics/mod.jl @@ -6,6 +6,7 @@ module Thermodynamics using DocStringExtensions include("isometric/mod.jl") +include("isotensional/mod.jl") """ The structure of the thermodynamics of the WLC model. @@ -33,6 +34,10 @@ struct WLC The thermodynamic functions of the model in the isometric ensemble. """ isometric::Any + """ + The thermodynamic functions of the model in the isotensional ensemble. + """ + isotensional::Any end """ @@ -52,6 +57,7 @@ function WLC( hinge_mass, persistance_length, Isometric.WLC(number_of_links, link_length, hinge_mass, persistance_length), + Isotensional.WLC(number_of_links, link_length, hinge_mass, persistance_length), ) end diff --git a/src/physics/single_chain/wlc/thermodynamics/mod.rs b/src/physics/single_chain/wlc/thermodynamics/mod.rs index 39a0375e..ecf72d43 100644 --- a/src/physics/single_chain/wlc/thermodynamics/mod.rs +++ b/src/physics/single_chain/wlc/thermodynamics/mod.rs @@ -6,6 +6,9 @@ mod test; /// The worm-like chain (WLC) model thermodynamics in the isometric ensemble. pub mod isometric; +/// The worm-like chain (WLC) model thermodynamics in the isotensional ensemble. +pub mod isotensional; + /// The structure of the thermodynamics of the WLC model. pub struct WLC { @@ -22,7 +25,10 @@ pub struct WLC pub persistance_length: f64, /// The thermodynamic functions of the model in the isometric ensemble. - pub isometric: self::isometric::WLC + pub isometric: self::isometric::WLC, + + /// The thermodynamic functions of the model in the isotensional ensemble. + pub isotensional: self::isotensional::WLC } /// The implemented functionality of the thermodynamics of the WLC model. @@ -37,7 +43,8 @@ impl WLC link_length, number_of_links, persistance_length, - isometric: self::isometric::WLC::init(number_of_links, link_length, hinge_mass, persistance_length) + isometric: self::isometric::WLC::init(number_of_links, link_length, hinge_mass, persistance_length), + isotensional: self::isotensional::WLC::init(number_of_links, link_length, hinge_mass, persistance_length) } } } diff --git a/src/physics/single_chain/wlc/thermodynamics/py.rs b/src/physics/single_chain/wlc/thermodynamics/py.rs index b5952f45..bb2fcc34 100644 --- a/src/physics/single_chain/wlc/thermodynamics/py.rs +++ b/src/physics/single_chain/wlc/thermodynamics/py.rs @@ -4,6 +4,7 @@ pub fn register_module(py: Python<'_>, parent_module: &PyModule) -> PyResult<()> { let thermodynamics = PyModule::new(py, "thermodynamics")?; super::isometric::py::register_module(py, thermodynamics)?; + super::isotensional::py::register_module(py, thermodynamics)?; parent_module.add_submodule(thermodynamics)?; thermodynamics.add_class::()?; Ok(()) @@ -32,7 +33,11 @@ pub struct WLC /// The thermodynamic functions of the model in the isometric ensemble. #[pyo3(get)] - pub isometric: super::isometric::py::WLC + pub isometric: super::isometric::py::WLC, + + /// The thermodynamic functions of the model in the isotensional ensemble. + #[pyo3(get)] + pub isotensional: super::isotensional::py::WLC } #[pymethods] @@ -47,7 +52,8 @@ impl WLC link_length, number_of_links, persistance_length, - isometric: super::isometric::py::WLC::init(number_of_links, link_length, hinge_mass, persistance_length) + isometric: super::isometric::py::WLC::init(number_of_links, link_length, hinge_mass, persistance_length), + isotensional: super::isotensional::py::WLC::init(number_of_links, link_length, hinge_mass, persistance_length) } } } diff --git a/src/physics/single_chain/wlc/thermodynamics/test.jl b/src/physics/single_chain/wlc/thermodynamics/test.jl index 2f26dcd1..e754113e 100644 --- a/src/physics/single_chain/wlc/thermodynamics/test.jl +++ b/src/physics/single_chain/wlc/thermodynamics/test.jl @@ -1,6 +1,7 @@ module Test using Test +using Polymers.Physics: BOLTZMANN_CONSTANT, PLANCK_CONSTANT using Polymers.Physics.SingleChain: parameters using Polymers.Physics.SingleChain.Wlc.Thermodynamics: WLC @@ -101,4 +102,719 @@ end end end +@testset "physics::single_chain::wlc::thermodynamics::test::thermodynamic_limit::end_to_end_length" begin + for _ = 1:parameters.number_of_loops + number_of_links = parameters.number_of_links_maximum + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.nondimensional_persistance_length_small * + number_of_links * + link_length + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_end_to_end_length_per_link = + parameters.nondimensional_end_to_end_length_per_link_reference + + parameters.nondimensional_end_to_end_length_per_link_scale * (0.5 - rand()) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + end_to_end_length = + nondimensional_end_to_end_length_per_link * number_of_links * link_length + force = model.isometric.force(end_to_end_length, temperature) + end_to_end_length_out = model.isotensional.end_to_end_length(force, temperature) + residual_abs = end_to_end_length - end_to_end_length_out + residual_rel = residual_abs / end_to_end_length + @test abs(residual_rel) <= parameters.rel_tol_thermodynamic_limit + end +end + +@testset "physics::single_chain::wlc::thermodynamics::test::thermodynamic_limit::end_to_end_length_per_link" begin + for _ = 1:parameters.number_of_loops + number_of_links = parameters.number_of_links_maximum + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.nondimensional_persistance_length_small * + number_of_links * + link_length + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_end_to_end_length_per_link = + parameters.nondimensional_end_to_end_length_per_link_reference + + parameters.nondimensional_end_to_end_length_per_link_scale * (0.5 - rand()) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + end_to_end_length = + nondimensional_end_to_end_length_per_link * number_of_links * link_length + force = model.isometric.force(end_to_end_length, temperature) + end_to_end_length_per_link = nondimensional_end_to_end_length_per_link * link_length + end_to_end_length_per_link_out = + model.isotensional.end_to_end_length_per_link(force, temperature) + residual_abs = end_to_end_length_per_link - end_to_end_length_per_link_out + residual_rel = residual_abs / end_to_end_length_per_link + @test abs(residual_rel) <= parameters.rel_tol_thermodynamic_limit + end +end + +@testset "physics::single_chain::wlc::thermodynamics::test::thermodynamic_limit::nondimensional_end_to_end_length" begin + for _ = 1:parameters.number_of_loops + number_of_links = parameters.number_of_links_maximum + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.nondimensional_persistance_length_small * + number_of_links * + link_length + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_end_to_end_length_per_link = + parameters.nondimensional_end_to_end_length_per_link_reference + + parameters.nondimensional_end_to_end_length_per_link_scale * (0.5 - rand()) + nondimensional_force = + model.isometric.nondimensional_force(nondimensional_end_to_end_length_per_link) + nondimensional_end_to_end_length = + nondimensional_end_to_end_length_per_link * number_of_links + nondimensional_end_to_end_length_out = + model.isotensional.nondimensional_end_to_end_length(nondimensional_force) + residual_abs = + nondimensional_end_to_end_length - nondimensional_end_to_end_length_out + residual_rel = residual_abs / nondimensional_end_to_end_length + @test abs(residual_rel) <= parameters.rel_tol_thermodynamic_limit + end +end + +@testset "physics::single_chain::wlc::thermodynamics::test::thermodynamic_limit::nondimensional_end_to_end_length_per_link" begin + for _ = 1:parameters.number_of_loops + number_of_links = parameters.number_of_links_maximum + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.nondimensional_persistance_length_small * + number_of_links * + link_length + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_end_to_end_length_per_link = + parameters.nondimensional_end_to_end_length_per_link_reference + + parameters.nondimensional_end_to_end_length_per_link_scale * (0.5 - rand()) + nondimensional_force = + model.isometric.nondimensional_force(nondimensional_end_to_end_length_per_link) + nondimensional_end_to_end_length_per_link_out = + model.isotensional.nondimensional_end_to_end_length_per_link( + nondimensional_force, + ) + residual_abs = + nondimensional_end_to_end_length_per_link - + nondimensional_end_to_end_length_per_link_out + residual_rel = residual_abs / nondimensional_end_to_end_length_per_link + @test abs(residual_rel) <= parameters.rel_tol_thermodynamic_limit + end +end + +@testset "physics::single_chain::wlc::thermodynamics::test::thermodynamic_limit::force" begin + for _ = 1:parameters.number_of_loops + number_of_links = parameters.number_of_links_maximum + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.nondimensional_persistance_length_small * + number_of_links * + link_length + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + end_to_end_length = model.isotensional.end_to_end_length(force, temperature) + force_out = model.isometric.force(end_to_end_length, temperature) + residual_abs = force - force_out + residual_rel = residual_abs / force + @test abs(residual_rel) <= parameters.rel_tol_thermodynamic_limit + end +end + +@testset "physics::single_chain::wlc::thermodynamics::test::thermodynamic_limit::nondimensional_force" begin + for _ = 1:parameters.number_of_loops + number_of_links = parameters.number_of_links_maximum + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.nondimensional_persistance_length_small * + number_of_links * + link_length + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + nondimensional_end_to_end_length_per_link = + model.isotensional.nondimensional_end_to_end_length_per_link( + nondimensional_force, + ) + nondimensional_force_out = + model.isometric.nondimensional_force(nondimensional_end_to_end_length_per_link) + residual_abs = nondimensional_force - nondimensional_force_out + residual_rel = residual_abs / nondimensional_force + @test abs(residual_rel) <= parameters.rel_tol_thermodynamic_limit + end +end + +@testset "physics::single_chain::wlc::thermodynamics::test::thermodynamic_limit::helmholtz_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = parameters.number_of_links_maximum + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.nondimensional_persistance_length_small * + number_of_links * + link_length + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_end_to_end_length_per_link = + parameters.nondimensional_end_to_end_length_per_link_reference + + parameters.nondimensional_end_to_end_length_per_link_scale * (0.5 - rand()) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + end_to_end_length = + nondimensional_end_to_end_length_per_link * number_of_links * link_length + force = model.isometric.force(end_to_end_length, temperature) + helmholtz_free_energy = + model.isometric.helmholtz_free_energy(end_to_end_length, temperature) + helmholtz_free_energy_out = + model.isotensional.gibbs_free_energy(force, temperature) + + force * end_to_end_length + residual_abs = helmholtz_free_energy - helmholtz_free_energy_out + residual_rel = residual_abs / helmholtz_free_energy + @test abs(residual_rel) <= parameters.rel_tol_thermodynamic_limit + end +end + +@testset "physics::single_chain::wlc::thermodynamics::test::thermodynamic_limit::helmholtz_free_energy_per_link" begin + for _ = 1:parameters.number_of_loops + number_of_links = parameters.number_of_links_maximum + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.nondimensional_persistance_length_small * + number_of_links * + link_length + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_end_to_end_length_per_link = + parameters.nondimensional_end_to_end_length_per_link_reference + + parameters.nondimensional_end_to_end_length_per_link_scale * (0.5 - rand()) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + end_to_end_length = + nondimensional_end_to_end_length_per_link * number_of_links * link_length + end_to_end_length_per_link = nondimensional_end_to_end_length_per_link * link_length + force = model.isometric.force(end_to_end_length, temperature) + helmholtz_free_energy_per_link = + model.isometric.helmholtz_free_energy_per_link(end_to_end_length, temperature) + helmholtz_free_energy_per_link_out = + model.isotensional.gibbs_free_energy_per_link(force, temperature) + + force * end_to_end_length_per_link + residual_abs = helmholtz_free_energy_per_link - helmholtz_free_energy_per_link_out + residual_rel = residual_abs / helmholtz_free_energy_per_link + @test abs(residual_rel) <= parameters.rel_tol_thermodynamic_limit + end +end + +@testset "physics::single_chain::wlc::thermodynamics::test::thermodynamic_limit::relative_helmholtz_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = parameters.number_of_links_maximum + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.nondimensional_persistance_length_small * + number_of_links * + link_length + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_end_to_end_length_per_link = + parameters.nondimensional_end_to_end_length_per_link_reference + + parameters.nondimensional_end_to_end_length_per_link_scale * (0.5 - rand()) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + end_to_end_length = + nondimensional_end_to_end_length_per_link * number_of_links * link_length + force = model.isometric.force(end_to_end_length, temperature) + relative_helmholtz_free_energy = + model.isometric.relative_helmholtz_free_energy(end_to_end_length, temperature) + relative_helmholtz_free_energy_out = + model.isotensional.relative_gibbs_free_energy(force, temperature) + + force * end_to_end_length + residual_abs = relative_helmholtz_free_energy - relative_helmholtz_free_energy_out + residual_rel = residual_abs / relative_helmholtz_free_energy + @test abs(residual_rel) <= parameters.rel_tol_thermodynamic_limit + end +end + +@testset "physics::single_chain::wlc::thermodynamics::test::thermodynamic_limit::relative_helmholtz_free_energy_per_link" begin + for _ = 1:parameters.number_of_loops + number_of_links = parameters.number_of_links_maximum + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.nondimensional_persistance_length_small * + number_of_links * + link_length + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_end_to_end_length_per_link = + parameters.nondimensional_end_to_end_length_per_link_reference + + parameters.nondimensional_end_to_end_length_per_link_scale * (0.5 - rand()) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + end_to_end_length = + nondimensional_end_to_end_length_per_link * number_of_links * link_length + end_to_end_length_per_link = nondimensional_end_to_end_length_per_link * link_length + force = model.isometric.force(end_to_end_length, temperature) + relative_helmholtz_free_energy_per_link = + model.isometric.relative_helmholtz_free_energy_per_link( + end_to_end_length, + temperature, + ) + relative_helmholtz_free_energy_per_link_out = + model.isotensional.relative_gibbs_free_energy_per_link(force, temperature) + + force * end_to_end_length_per_link + residual_abs = + relative_helmholtz_free_energy_per_link - + relative_helmholtz_free_energy_per_link_out + residual_rel = residual_abs / relative_helmholtz_free_energy_per_link + @test abs(residual_rel) <= parameters.rel_tol_thermodynamic_limit + end +end + +@testset "physics::single_chain::wlc::thermodynamics::test::thermodynamic_limit::nondimensional_helmholtz_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = parameters.number_of_links_maximum + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.nondimensional_persistance_length_small * + number_of_links * + link_length + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_end_to_end_length_per_link = + parameters.nondimensional_end_to_end_length_per_link_reference + + parameters.nondimensional_end_to_end_length_per_link_scale * (0.5 - rand()) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_end_to_end_length = + nondimensional_end_to_end_length_per_link * number_of_links + nondimensional_force = + model.isometric.nondimensional_force(nondimensional_end_to_end_length_per_link) + nondimensional_helmholtz_free_energy = + model.isometric.nondimensional_helmholtz_free_energy( + nondimensional_end_to_end_length_per_link, + temperature, + ) + nondimensional_helmholtz_free_energy_out = + model.isotensional.nondimensional_gibbs_free_energy( + nondimensional_force, + temperature, + ) + nondimensional_force * nondimensional_end_to_end_length + residual_abs = + nondimensional_helmholtz_free_energy - nondimensional_helmholtz_free_energy_out + residual_rel = residual_abs / nondimensional_helmholtz_free_energy + @test abs(residual_rel) <= parameters.rel_tol_thermodynamic_limit + end +end + +@testset "physics::single_chain::wlc::thermodynamics::test::thermodynamic_limit::nondimensional_helmholtz_free_energy_per_link" begin + for _ = 1:parameters.number_of_loops + number_of_links = parameters.number_of_links_maximum + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.nondimensional_persistance_length_small * + number_of_links * + link_length + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_end_to_end_length_per_link = + parameters.nondimensional_end_to_end_length_per_link_reference + + parameters.nondimensional_end_to_end_length_per_link_scale * (0.5 - rand()) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_force = + model.isometric.nondimensional_force(nondimensional_end_to_end_length_per_link) + nondimensional_helmholtz_free_energy_per_link = + model.isometric.nondimensional_helmholtz_free_energy_per_link( + nondimensional_end_to_end_length_per_link, + temperature, + ) + nondimensional_helmholtz_free_energy_per_link_out = + model.isotensional.nondimensional_gibbs_free_energy_per_link( + nondimensional_force, + temperature, + ) + nondimensional_force * nondimensional_end_to_end_length_per_link + residual_abs = + nondimensional_helmholtz_free_energy_per_link - + nondimensional_helmholtz_free_energy_per_link_out + residual_rel = residual_abs / nondimensional_helmholtz_free_energy_per_link + @test abs(residual_rel) <= parameters.rel_tol_thermodynamic_limit + end +end + +@testset "physics::single_chain::wlc::thermodynamics::test::thermodynamic_limit::nondimensional_relative_helmholtz_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = parameters.number_of_links_maximum + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.nondimensional_persistance_length_small * + number_of_links * + link_length + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_end_to_end_length_per_link = + parameters.nondimensional_end_to_end_length_per_link_reference + + parameters.nondimensional_end_to_end_length_per_link_scale * (0.5 - rand()) + nondimensional_end_to_end_length = + nondimensional_end_to_end_length_per_link * number_of_links + nondimensional_force = + model.isometric.nondimensional_force(nondimensional_end_to_end_length_per_link) + nondimensional_relative_helmholtz_free_energy = + model.isometric.nondimensional_relative_helmholtz_free_energy( + nondimensional_end_to_end_length_per_link, + ) + nondimensional_relative_helmholtz_free_energy_out = + model.isotensional.nondimensional_relative_gibbs_free_energy( + nondimensional_force, + ) + nondimensional_force * nondimensional_end_to_end_length + residual_abs = + nondimensional_relative_helmholtz_free_energy - + nondimensional_relative_helmholtz_free_energy_out + residual_rel = residual_abs / nondimensional_relative_helmholtz_free_energy + @test abs(residual_rel) <= parameters.rel_tol_thermodynamic_limit + end +end + +@testset "physics::single_chain::wlc::thermodynamics::test::thermodynamic_limit::nondimensional_relative_helmholtz_free_energy_per_link" begin + for _ = 1:parameters.number_of_loops + number_of_links = parameters.number_of_links_maximum + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.nondimensional_persistance_length_small * + number_of_links * + link_length + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_end_to_end_length_per_link = + parameters.nondimensional_end_to_end_length_per_link_reference + + parameters.nondimensional_end_to_end_length_per_link_scale * (0.5 - rand()) + nondimensional_force = + model.isometric.nondimensional_force(nondimensional_end_to_end_length_per_link) + nondimensional_relative_helmholtz_free_energy_per_link = + model.isometric.nondimensional_relative_helmholtz_free_energy_per_link( + nondimensional_end_to_end_length_per_link, + ) + nondimensional_relative_helmholtz_free_energy_per_link_out = + model.isotensional.nondimensional_relative_gibbs_free_energy_per_link( + nondimensional_force, + ) + nondimensional_force * nondimensional_end_to_end_length_per_link + residual_abs = + nondimensional_relative_helmholtz_free_energy_per_link - + nondimensional_relative_helmholtz_free_energy_per_link_out + residual_rel = residual_abs / nondimensional_relative_helmholtz_free_energy_per_link + @test abs(residual_rel) <= parameters.rel_tol_thermodynamic_limit + end +end + +@testset "physics::single_chain::wlc::thermodynamics::test::thermodynamic_limit::gibbs_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = parameters.number_of_links_maximum + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.nondimensional_persistance_length_small * + number_of_links * + link_length + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + end_to_end_length = model.isotensional.end_to_end_length(force, temperature) + gibbs_free_energy = model.isotensional.gibbs_free_energy(force, temperature) + gibbs_free_energy_out = + model.isometric.helmholtz_free_energy(end_to_end_length, temperature) - + force * end_to_end_length + residual_abs = gibbs_free_energy - gibbs_free_energy_out + residual_rel = residual_abs / gibbs_free_energy + @test abs(residual_rel) <= parameters.rel_tol_thermodynamic_limit + end +end + +@testset "physics::single_chain::wlc::thermodynamics::test::thermodynamic_limit::gibbs_free_energy_per_link" begin + for _ = 1:parameters.number_of_loops + number_of_links = parameters.number_of_links_maximum + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.nondimensional_persistance_length_small * + number_of_links * + link_length + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + end_to_end_length = model.isotensional.end_to_end_length(force, temperature) + end_to_end_length_per_link = end_to_end_length / number_of_links + gibbs_free_energy_per_link = + model.isotensional.gibbs_free_energy_per_link(force, temperature) + gibbs_free_energy_per_link_out = + model.isometric.helmholtz_free_energy_per_link(end_to_end_length, temperature) - + force * end_to_end_length_per_link + residual_abs = gibbs_free_energy_per_link - gibbs_free_energy_per_link_out + residual_rel = residual_abs / gibbs_free_energy_per_link + @test abs(residual_rel) <= parameters.rel_tol_thermodynamic_limit + end +end + +@testset "physics::single_chain::wlc::thermodynamics::test::thermodynamic_limit::relative_gibbs_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = parameters.number_of_links_maximum + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.nondimensional_persistance_length_small * + number_of_links * + link_length + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + end_to_end_length = model.isotensional.end_to_end_length(force, temperature) + relative_gibbs_free_energy = + model.isotensional.relative_gibbs_free_energy(force, temperature) + relative_gibbs_free_energy_out = + model.isometric.relative_helmholtz_free_energy(end_to_end_length, temperature) - + force * end_to_end_length + residual_abs = relative_gibbs_free_energy - relative_gibbs_free_energy_out + residual_rel = residual_abs / relative_gibbs_free_energy + @test abs(residual_rel) <= parameters.rel_tol_thermodynamic_limit + end +end + +@testset "physics::single_chain::wlc::thermodynamics::test::thermodynamic_limit::relative_gibbs_free_energy_per_link" begin + for _ = 1:parameters.number_of_loops + number_of_links = parameters.number_of_links_maximum + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.nondimensional_persistance_length_small * + number_of_links * + link_length + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + force = nondimensional_force * BOLTZMANN_CONSTANT * temperature / link_length + end_to_end_length = model.isotensional.end_to_end_length(force, temperature) + end_to_end_length_per_link = end_to_end_length / number_of_links + relative_gibbs_free_energy_per_link = + model.isotensional.relative_gibbs_free_energy_per_link(force, temperature) + relative_gibbs_free_energy_per_link_out = + model.isometric.relative_helmholtz_free_energy_per_link( + end_to_end_length, + temperature, + ) - force * end_to_end_length_per_link + residual_abs = + relative_gibbs_free_energy_per_link - relative_gibbs_free_energy_per_link_out + residual_rel = residual_abs / relative_gibbs_free_energy_per_link + @test abs(residual_rel) <= parameters.rel_tol_thermodynamic_limit + end +end + +@testset "physics::single_chain::wlc::thermodynamics::test::thermodynamic_limit::nondimensional_gibbs_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = parameters.number_of_links_maximum + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.nondimensional_persistance_length_small * + number_of_links * + link_length + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_end_to_end_length_per_link = + model.isotensional.nondimensional_end_to_end_length_per_link( + nondimensional_force, + ) + nondimensional_end_to_end_length = + nondimensional_end_to_end_length_per_link * number_of_links + nondimensional_gibbs_free_energy = + model.isotensional.nondimensional_gibbs_free_energy( + nondimensional_force, + temperature, + ) + nondimensional_gibbs_free_energy_out = + model.isometric.nondimensional_helmholtz_free_energy( + nondimensional_end_to_end_length_per_link, + temperature, + ) - nondimensional_force * nondimensional_end_to_end_length + residual_abs = + nondimensional_gibbs_free_energy - nondimensional_gibbs_free_energy_out + residual_rel = residual_abs / nondimensional_gibbs_free_energy + @test abs(residual_rel) <= parameters.rel_tol_thermodynamic_limit + end +end + +@testset "physics::single_chain::wlc::thermodynamics::test::thermodynamic_limit::nondimensional_gibbs_free_energy_per_link" begin + for _ = 1:parameters.number_of_loops + number_of_links = parameters.number_of_links_maximum + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.nondimensional_persistance_length_small * + number_of_links * + link_length + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + temperature = + parameters.temperature_reference + parameters.temperature_scale * (0.5 - rand()) + nondimensional_end_to_end_length_per_link = + model.isotensional.nondimensional_end_to_end_length_per_link( + nondimensional_force, + ) + nondimensional_gibbs_free_energy_per_link = + model.isotensional.nondimensional_gibbs_free_energy_per_link( + nondimensional_force, + temperature, + ) + nondimensional_gibbs_free_energy_per_link_out = + model.isometric.nondimensional_helmholtz_free_energy_per_link( + nondimensional_end_to_end_length_per_link, + temperature, + ) - nondimensional_force * nondimensional_end_to_end_length_per_link + residual_abs = + nondimensional_gibbs_free_energy_per_link - + nondimensional_gibbs_free_energy_per_link_out + residual_rel = residual_abs / nondimensional_gibbs_free_energy_per_link + @test abs(residual_rel) <= parameters.rel_tol_thermodynamic_limit + end +end + +@testset "physics::single_chain::wlc::thermodynamics::test::thermodynamic_limit::nondimensional_relative_gibbs_free_energy" begin + for _ = 1:parameters.number_of_loops + number_of_links = parameters.number_of_links_maximum + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.nondimensional_persistance_length_small * + number_of_links * + link_length + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + nondimensional_end_to_end_length_per_link = + model.isotensional.nondimensional_end_to_end_length_per_link( + nondimensional_force, + ) + nondimensional_end_to_end_length = + nondimensional_end_to_end_length_per_link * number_of_links + nondimensional_relative_gibbs_free_energy = + model.isotensional.nondimensional_relative_gibbs_free_energy( + nondimensional_force, + ) + nondimensional_relative_gibbs_free_energy_out = + model.isometric.nondimensional_relative_helmholtz_free_energy( + nondimensional_end_to_end_length_per_link, + ) - nondimensional_force * nondimensional_end_to_end_length + residual_abs = + nondimensional_relative_gibbs_free_energy - + nondimensional_relative_gibbs_free_energy_out + residual_rel = residual_abs / nondimensional_relative_gibbs_free_energy + @test abs(residual_rel) <= parameters.rel_tol_thermodynamic_limit + end +end + +@testset "physics::single_chain::wlc::thermodynamics::test::thermodynamic_limit::nondimensional_relative_gibbs_free_energy_per_link" begin + for _ = 1:parameters.number_of_loops + number_of_links = parameters.number_of_links_maximum + link_length = + parameters.link_length_reference + parameters.link_length_scale * (0.5 - rand()) + hinge_mass = + parameters.hinge_mass_reference + parameters.hinge_mass_scale * (0.5 - rand()) + persistance_length = + parameters.nondimensional_persistance_length_small * + number_of_links * + link_length + model = WLC(number_of_links, link_length, hinge_mass, persistance_length) + nondimensional_force = + parameters.nondimensional_force_reference + + parameters.nondimensional_force_scale * (0.5 - rand()) + nondimensional_end_to_end_length_per_link = + model.isotensional.nondimensional_end_to_end_length_per_link( + nondimensional_force, + ) + nondimensional_relative_gibbs_free_energy_per_link = + model.isotensional.nondimensional_relative_gibbs_free_energy_per_link( + nondimensional_force, + ) + nondimensional_relative_gibbs_free_energy_per_link_out = + model.isometric.nondimensional_relative_helmholtz_free_energy_per_link( + nondimensional_end_to_end_length_per_link, + ) - nondimensional_force * nondimensional_end_to_end_length_per_link + residual_abs = + nondimensional_relative_gibbs_free_energy_per_link - + nondimensional_relative_gibbs_free_energy_per_link_out + residual_rel = residual_abs / nondimensional_relative_gibbs_free_energy_per_link + @test abs(residual_rel) <= parameters.rel_tol_thermodynamic_limit + end +end + end diff --git a/src/physics/single_chain/wlc/thermodynamics/test.py b/src/physics/single_chain/wlc/thermodynamics/test.py index 9325ffe0..8ee4d6fa 100644 --- a/src/physics/single_chain/wlc/thermodynamics/test.py +++ b/src/physics/single_chain/wlc/thermodynamics/test.py @@ -20,7 +20,7 @@ def test_init(self): """ for _ in range(parameters.number_of_loops): _ = WLC( - parameters.number_of_links_minimum, + parameters.number_of_links_maximum, parameters.link_length_reference, parameters.hinge_mass_reference, parameters.persistance_length_reference @@ -57,7 +57,7 @@ def test_link_length(self): self.assertEqual( link_length, WLC( - parameters.number_of_links_minimum, + parameters.number_of_links_maximum, link_length, parameters.hinge_mass_reference, parameters.persistance_length_reference @@ -75,7 +75,7 @@ def test_hinge_mass(self): self.assertEqual( hinge_mass, WLC( - parameters.number_of_links_minimum, + parameters.number_of_links_maximum, parameters.link_length_reference, hinge_mass, parameters.persistance_length_reference @@ -93,7 +93,7 @@ def test_persistance_length(self): self.assertEqual( persistance_length, WLC( - parameters.number_of_links_minimum, + parameters.number_of_links_maximum, parameters.link_length_reference, parameters.hinge_mass_reference, persistance_length @@ -135,9 +135,1259 @@ def test_all_parameters(self): ) self.assertEqual( hinge_mass, - model.hinge_mass + model.hinge_mass, + persistance_length ) self.assertEqual( persistance_length, model.persistance_length ) + + +class ThermodynamicLimit(unittest.TestCase): + """Class for thermodynamic limit tests. + + """ + def test_end_to_end_length(self): + """Function to test the thermodynamic limit + of the end-to-end length. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = parameters.number_of_links_maximum + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.nondimensional_persistance_length_small * \ + number_of_links*link_length + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_end_to_end_length_per_link = \ + parameters. \ + nondimensional_end_to_end_length_per_link_reference + \ + parameters. \ + nondimensional_end_to_end_length_per_link_scale * \ + (0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + end_to_end_length = nondimensional_end_to_end_length_per_link * \ + number_of_links*link_length + force = \ + model.isometric.force( + np.array(end_to_end_length), + temperature + ) + end_to_end_length_out = \ + model.isotensional.end_to_end_length( + np.array(force), + temperature + ) + residual_abs = \ + end_to_end_length \ + - end_to_end_length_out + residual_rel = residual_abs/end_to_end_length + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol_thermodynamic_limit + ) + + def test_end_to_end_length_per_link(self): + """Function to test the thermodynamic limit + of the end-to-end length per link. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = parameters.number_of_links_maximum + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.nondimensional_persistance_length_small * \ + number_of_links*link_length + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_end_to_end_length_per_link = \ + parameters. \ + nondimensional_end_to_end_length_per_link_reference + \ + parameters. \ + nondimensional_end_to_end_length_per_link_scale * \ + (0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + end_to_end_length = nondimensional_end_to_end_length_per_link * \ + number_of_links*link_length + force = \ + model.isometric.force( + np.array(end_to_end_length), + temperature + ) + end_to_end_length_per_link = \ + nondimensional_end_to_end_length_per_link*link_length + end_to_end_length_per_link_out = \ + model.isotensional.end_to_end_length_per_link( + np.array(force), + temperature + ) + residual_abs = \ + end_to_end_length_per_link \ + - end_to_end_length_per_link_out + residual_rel = residual_abs/end_to_end_length_per_link + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol_thermodynamic_limit + ) + + def test_nondimensional_end_to_end_length(self): + """Function to test the thermodynamic limit + of the nondimensional end-to-end length. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = parameters.number_of_links_maximum + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.nondimensional_persistance_length_small * \ + number_of_links*link_length + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_end_to_end_length_per_link = \ + parameters. \ + nondimensional_end_to_end_length_per_link_reference + \ + parameters. \ + nondimensional_end_to_end_length_per_link_scale * \ + (0.5 - np.random.rand()) + nondimensional_force = \ + model.isometric.nondimensional_force( + np.array(nondimensional_end_to_end_length_per_link) + ) + nondimensional_end_to_end_length = \ + nondimensional_end_to_end_length_per_link*number_of_links + nondimensional_end_to_end_length_out = \ + model.isotensional.nondimensional_end_to_end_length( + np.array(nondimensional_force) + ) + residual_abs = \ + nondimensional_end_to_end_length \ + - nondimensional_end_to_end_length_out + residual_rel = residual_abs/nondimensional_end_to_end_length + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol_thermodynamic_limit + ) + + def test_nondimensional_end_to_end_length_per_link(self): + """Function to test the thermodynamic limit + of the nondimensional end-to-end length per link. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = parameters.number_of_links_maximum + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.nondimensional_persistance_length_small * \ + number_of_links*link_length + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_end_to_end_length_per_link = \ + parameters. \ + nondimensional_end_to_end_length_per_link_reference + \ + parameters. \ + nondimensional_end_to_end_length_per_link_scale * \ + (0.5 - np.random.rand()) + nondimensional_force = \ + model.isometric.nondimensional_force( + np.array(nondimensional_end_to_end_length_per_link) + ) + nondimensional_end_to_end_length_per_link_out = \ + model.isotensional.nondimensional_end_to_end_length_per_link( + np.array(nondimensional_force) + ) + residual_abs = \ + nondimensional_end_to_end_length_per_link \ + - nondimensional_end_to_end_length_per_link_out + residual_rel = residual_abs / \ + nondimensional_end_to_end_length_per_link + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol_thermodynamic_limit + ) + + def test_force(self): + """Function to test the thermodynamic limit + of the force. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = parameters.number_of_links_maximum + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.nondimensional_persistance_length_small * \ + number_of_links*link_length + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters. \ + nondimensional_force_reference + \ + parameters. \ + nondimensional_force_scale * \ + (0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + end_to_end_length = \ + model.isotensional.end_to_end_length( + np.array(force), + temperature + ) + force_out = \ + model.isometric.force( + np.array(end_to_end_length), + temperature + ) + residual_abs = \ + force \ + - force_out + residual_rel = residual_abs/force + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol_thermodynamic_limit + ) + + def test_nondimensional_force(self): + """Function to test the thermodynamic limit + of the nondimensional force. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = parameters.number_of_links_maximum + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.nondimensional_persistance_length_small * \ + number_of_links*link_length + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters. \ + nondimensional_force_reference + \ + parameters. \ + nondimensional_force_scale * \ + (0.5 - np.random.rand()) + nondimensional_end_to_end_length_per_link = \ + model.isotensional.nondimensional_end_to_end_length_per_link( + np.array(nondimensional_force) + ) + nondimensional_force_out = \ + model.isometric.nondimensional_force( + np.array(nondimensional_end_to_end_length_per_link) + ) + residual_abs = \ + nondimensional_force \ + - nondimensional_force_out + residual_rel = residual_abs/nondimensional_force + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol_thermodynamic_limit + ) + + def test_helmholtz_free_energy(self): + """Function to test the thermodynamic limit + of the Helmholtz free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = parameters.number_of_links_maximum + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.nondimensional_persistance_length_small * \ + number_of_links*link_length + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_end_to_end_length_per_link = \ + parameters. \ + nondimensional_end_to_end_length_per_link_reference + \ + parameters. \ + nondimensional_end_to_end_length_per_link_scale * \ + (0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + end_to_end_length = nondimensional_end_to_end_length_per_link * \ + number_of_links*link_length + force = \ + model.isometric.force( + np.array(end_to_end_length), + temperature + ) + helmholtz_free_energy = \ + model.isometric.helmholtz_free_energy( + np.array(end_to_end_length), + temperature + ) + helmholtz_free_energy_out = \ + model.isotensional.gibbs_free_energy( + np.array(force), + temperature + ) + force*end_to_end_length + residual_abs = \ + helmholtz_free_energy \ + - helmholtz_free_energy_out \ + - parameters.boltzmann_constant*temperature*np.log( + 4.0*np.sin(np.arccos(np.exp( + -number_of_links*link_length/persistance_length + )))*np.pi**2*hinge_mass*link_length**2 * + parameters.boltzmann_constant*temperature / + parameters.planck_constant**2 + ) + residual_rel = residual_abs/helmholtz_free_energy + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol_thermodynamic_limit + ) + + def test_helmholtz_free_energy_per_link(self): + """Function to test the thermodynamic limit + of the Helmholtz free energy per link. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = parameters.number_of_links_maximum + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.nondimensional_persistance_length_small * \ + number_of_links*link_length + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_end_to_end_length_per_link = \ + parameters. \ + nondimensional_end_to_end_length_per_link_reference + \ + parameters. \ + nondimensional_end_to_end_length_per_link_scale * \ + (0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + end_to_end_length = nondimensional_end_to_end_length_per_link * \ + number_of_links*link_length + force = \ + model.isometric.force( + np.array(end_to_end_length), + temperature + ) + helmholtz_free_energy_per_link = \ + model.isometric.helmholtz_free_energy_per_link( + np.array(end_to_end_length), + temperature + ) + helmholtz_free_energy_per_link_out = \ + model.isotensional.gibbs_free_energy_per_link( + np.array(force), + temperature + ) + force*end_to_end_length/number_of_links + residual_abs = \ + helmholtz_free_energy_per_link \ + - helmholtz_free_energy_per_link_out \ + - parameters.boltzmann_constant*temperature*np.log( + 4.0*np.sin(np.arccos(np.exp( + -number_of_links*link_length/persistance_length + )))*np.pi**2*hinge_mass*link_length**2 * + parameters.boltzmann_constant*temperature / + parameters.planck_constant**2 + )/number_of_links + residual_rel = residual_abs/helmholtz_free_energy_per_link + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol_thermodynamic_limit + ) + + def test_relative_helmholtz_free_energy(self): + """Function to test the thermodynamic limit + of the relative Helmholtz free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = parameters.number_of_links_maximum + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.nondimensional_persistance_length_small * \ + number_of_links*link_length + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_end_to_end_length_per_link = \ + parameters. \ + nondimensional_end_to_end_length_per_link_reference + \ + parameters. \ + nondimensional_end_to_end_length_per_link_scale * \ + (0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + end_to_end_length = nondimensional_end_to_end_length_per_link * \ + number_of_links*link_length + force = \ + model.isometric.force( + np.array(end_to_end_length), + temperature + ) + relative_helmholtz_free_energy = \ + model.isometric.relative_helmholtz_free_energy( + np.array(end_to_end_length), + temperature + ) + relative_helmholtz_free_energy_out = \ + model.isotensional.relative_gibbs_free_energy( + np.array(force), + temperature + ) + force*end_to_end_length + residual_abs = \ + relative_helmholtz_free_energy \ + - relative_helmholtz_free_energy_out + residual_rel = residual_abs/relative_helmholtz_free_energy + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol_thermodynamic_limit + ) + + def test_relative_helmholtz_free_energy_per_link(self): + """Function to test the thermodynamic limit + of the relative Helmholtz free energy per link. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = parameters.number_of_links_maximum + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.nondimensional_persistance_length_small * \ + number_of_links*link_length + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_end_to_end_length_per_link = \ + parameters. \ + nondimensional_end_to_end_length_per_link_reference + \ + parameters. \ + nondimensional_end_to_end_length_per_link_scale * \ + (0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + end_to_end_length = nondimensional_end_to_end_length_per_link * \ + number_of_links*link_length + force = \ + model.isometric.force( + np.array(end_to_end_length), + temperature + ) + relative_helmholtz_free_energy_per_link = \ + model.isometric.relative_helmholtz_free_energy_per_link( + np.array(end_to_end_length), + temperature + ) + relative_helmholtz_free_energy_per_link_out = \ + model.isotensional.relative_gibbs_free_energy_per_link( + np.array(force), + temperature + ) + force*end_to_end_length/number_of_links + residual_abs = \ + relative_helmholtz_free_energy_per_link \ + - relative_helmholtz_free_energy_per_link_out + residual_rel = residual_abs/relative_helmholtz_free_energy_per_link + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol_thermodynamic_limit + ) + + def test_nondimensional_helmholtz_free_energy(self): + """Function to test the thermodynamic limit + of the nondimensional Helmholtz free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = parameters.number_of_links_maximum + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.nondimensional_persistance_length_small * \ + number_of_links*link_length + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_end_to_end_length_per_link = \ + parameters. \ + nondimensional_end_to_end_length_per_link_reference + \ + parameters. \ + nondimensional_end_to_end_length_per_link_scale * \ + (0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + nondimensional_force = \ + model.isometric.nondimensional_force( + np.array(nondimensional_end_to_end_length_per_link) + ) + nondimensional_helmholtz_free_energy = \ + model.isometric.nondimensional_helmholtz_free_energy( + np.array(nondimensional_end_to_end_length_per_link), + temperature + ) + nondimensional_helmholtz_free_energy_out = \ + model.isotensional.nondimensional_gibbs_free_energy( + np.array(nondimensional_force), + temperature + ) + nondimensional_force * \ + nondimensional_end_to_end_length_per_link*number_of_links + residual_abs = \ + nondimensional_helmholtz_free_energy \ + - nondimensional_helmholtz_free_energy_out \ + - np.log( + 4.0*np.sin(np.arccos(np.exp( + -number_of_links*link_length/persistance_length + )))*np.pi**2*hinge_mass*link_length**2 * + parameters.boltzmann_constant*temperature / + parameters.planck_constant**2 + ) + residual_rel = residual_abs / \ + nondimensional_helmholtz_free_energy + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol_thermodynamic_limit + ) + + def test_nondimensional_helmholtz_free_energy_per_link(self): + """Function to test the thermodynamic limit + of the nondimensional Helmholtz free energy per link. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = parameters.number_of_links_maximum + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.nondimensional_persistance_length_small * \ + number_of_links*link_length + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_end_to_end_length_per_link = \ + parameters. \ + nondimensional_end_to_end_length_per_link_reference + \ + parameters. \ + nondimensional_end_to_end_length_per_link_scale * \ + (0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + nondimensional_force = \ + model.isometric.nondimensional_force( + np.array(nondimensional_end_to_end_length_per_link) + ) + nondimensional_helmholtz_free_energy_per_link = \ + model.isometric.nondimensional_helmholtz_free_energy_per_link( + np.array(nondimensional_end_to_end_length_per_link), + temperature + ) + nondimensional_helmholtz_free_energy_per_link_out = \ + model.isotensional.nondimensional_gibbs_free_energy_per_link( + np.array(nondimensional_force), + temperature + ) + nondimensional_force * \ + nondimensional_end_to_end_length_per_link + residual_abs = \ + nondimensional_helmholtz_free_energy_per_link \ + - nondimensional_helmholtz_free_energy_per_link_out \ + - np.log( + 4.0*np.sin(np.arccos(np.exp( + -number_of_links*link_length/persistance_length + )))*np.pi**2*hinge_mass*link_length**2 * + parameters.boltzmann_constant*temperature / + parameters.planck_constant**2 + )/number_of_links + residual_rel = residual_abs / \ + nondimensional_helmholtz_free_energy_per_link + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol_thermodynamic_limit + ) + + def test_nondimensional_relative_helmholtz_free_energy(self): + """Function to test the thermodynamic limit + of the nondimensional relative Helmholtz free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = parameters.number_of_links_maximum + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.nondimensional_persistance_length_small * \ + number_of_links*link_length + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_end_to_end_length_per_link = \ + parameters. \ + nondimensional_end_to_end_length_per_link_reference + \ + parameters. \ + nondimensional_end_to_end_length_per_link_scale * \ + (0.5 - np.random.rand()) + nondimensional_force = \ + model.isometric.nondimensional_force( + np.array(nondimensional_end_to_end_length_per_link) + ) + nondimensional_relative_helmholtz_free_energy = \ + model.isometric. \ + nondimensional_relative_helmholtz_free_energy( + np.array(nondimensional_end_to_end_length_per_link) + ) + nondimensional_relative_helmholtz_free_energy_out = \ + model.isotensional. \ + nondimensional_relative_gibbs_free_energy( + np.array(nondimensional_force) + ) + nondimensional_force * \ + nondimensional_end_to_end_length_per_link*number_of_links + residual_abs = \ + nondimensional_relative_helmholtz_free_energy \ + - nondimensional_relative_helmholtz_free_energy_out + residual_rel = residual_abs / \ + nondimensional_relative_helmholtz_free_energy + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol_thermodynamic_limit + ) + + def test_nondimensional_relative_helmholtz_free_energy_per_link(self): + """Function to test the thermodynamic limit + of the nondimensional relative Helmholtz free energy per link. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = parameters.number_of_links_maximum + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.nondimensional_persistance_length_small * \ + number_of_links*link_length + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_end_to_end_length_per_link = \ + parameters. \ + nondimensional_end_to_end_length_per_link_reference + \ + parameters. \ + nondimensional_end_to_end_length_per_link_scale * \ + (0.5 - np.random.rand()) + nondimensional_force = \ + model.isometric.nondimensional_force( + np.array(nondimensional_end_to_end_length_per_link) + ) + nondimensional_relative_helmholtz_free_energy_per_link = \ + model.isometric. \ + nondimensional_relative_helmholtz_free_energy_per_link( + np.array(nondimensional_end_to_end_length_per_link) + ) + nondimensional_relative_helmholtz_free_energy_per_link_out = \ + model.isotensional. \ + nondimensional_relative_gibbs_free_energy_per_link( + np.array(nondimensional_force) + ) + nondimensional_force * \ + nondimensional_end_to_end_length_per_link + residual_abs = \ + nondimensional_relative_helmholtz_free_energy_per_link \ + - nondimensional_relative_helmholtz_free_energy_per_link_out + residual_rel = residual_abs / \ + nondimensional_relative_helmholtz_free_energy_per_link + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol_thermodynamic_limit + ) + + def test_gibbs_free_energy(self): + """Function to test the thermodynamic limit + of the Gibbs free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = parameters.number_of_links_maximum + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.nondimensional_persistance_length_small * \ + number_of_links*link_length + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters. \ + nondimensional_force_reference + \ + parameters. \ + nondimensional_force_scale * \ + (0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + end_to_end_length = \ + model.isotensional.end_to_end_length( + np.array(force), + temperature + ) + gibbs_free_energy = \ + model.isotensional.gibbs_free_energy( + np.array(force), + temperature + ) + gibbs_free_energy_out = \ + model.isometric.helmholtz_free_energy( + np.array(end_to_end_length), + temperature + ) - force*end_to_end_length + residual_abs = \ + gibbs_free_energy \ + - gibbs_free_energy_out \ + + parameters.boltzmann_constant*temperature*np.log( + 4.0*np.sin(np.arccos(np.exp( + -number_of_links*link_length/persistance_length + )))*np.pi**2*hinge_mass*link_length**2 * + parameters.boltzmann_constant*temperature / + parameters.planck_constant**2 + ) + residual_rel = residual_abs/gibbs_free_energy + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol_thermodynamic_limit + ) + + def test_gibbs_free_energy_per_link(self): + """Function to test the thermodynamic limit + of the Gibbs free energy per link. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = parameters.number_of_links_maximum + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.nondimensional_persistance_length_small * \ + number_of_links*link_length + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters. \ + nondimensional_force_reference + \ + parameters. \ + nondimensional_force_scale * \ + (0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + end_to_end_length = \ + model.isotensional.end_to_end_length( + np.array(force), + temperature + ) + gibbs_free_energy_per_link = \ + model.isotensional.gibbs_free_energy_per_link( + np.array(force), + temperature + ) + gibbs_free_energy_per_link_out = \ + model.isometric.helmholtz_free_energy_per_link( + np.array(end_to_end_length), + temperature + ) - force*end_to_end_length/number_of_links + residual_abs = \ + gibbs_free_energy_per_link \ + - gibbs_free_energy_per_link_out \ + + parameters.boltzmann_constant*temperature*np.log( + 4.0*np.sin(np.arccos(np.exp( + -number_of_links*link_length/persistance_length + )))*np.pi**2*hinge_mass*link_length**2 * + parameters.boltzmann_constant*temperature / + parameters.planck_constant**2 + )/number_of_links + residual_rel = residual_abs/gibbs_free_energy_per_link + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol_thermodynamic_limit + ) + + def test_relative_gibbs_free_energy(self): + """Function to test the thermodynamic limit + of the relative Gibbs free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = parameters.number_of_links_maximum + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.nondimensional_persistance_length_small * \ + number_of_links*link_length + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters. \ + nondimensional_force_reference + \ + parameters. \ + nondimensional_force_scale * \ + (0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + end_to_end_length = \ + model.isotensional.end_to_end_length( + np.array(force), + temperature + ) + relative_gibbs_free_energy = \ + model.isotensional.relative_gibbs_free_energy( + np.array(force), + temperature + ) + relative_gibbs_free_energy_out = \ + model.isometric.relative_helmholtz_free_energy( + np.array(end_to_end_length), + temperature + ) - force*end_to_end_length + residual_abs = \ + relative_gibbs_free_energy \ + - relative_gibbs_free_energy_out + residual_rel = residual_abs/relative_gibbs_free_energy + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol_thermodynamic_limit + ) + + def test_relative_gibbs_free_energy_per_link(self): + """Function to test the thermodynamic limit + of the relative Gibbs free energy per link. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = parameters.number_of_links_maximum + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.nondimensional_persistance_length_small * \ + number_of_links*link_length + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters. \ + nondimensional_force_reference + \ + parameters. \ + nondimensional_force_scale * \ + (0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + force = nondimensional_force * \ + parameters.boltzmann_constant*temperature/link_length + end_to_end_length = \ + model.isotensional.end_to_end_length( + np.array(force), + temperature + ) + relative_gibbs_free_energy_per_link = \ + model.isotensional.relative_gibbs_free_energy_per_link( + np.array(force), + temperature + ) + relative_gibbs_free_energy_per_link_out = \ + model.isometric.relative_helmholtz_free_energy_per_link( + np.array(end_to_end_length), + temperature + ) - force*end_to_end_length/number_of_links + residual_abs = \ + relative_gibbs_free_energy_per_link \ + - relative_gibbs_free_energy_per_link_out + residual_rel = residual_abs/relative_gibbs_free_energy_per_link + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol_thermodynamic_limit + ) + + def test_nondimensional_gibbs_free_energy(self): + """Function to test the thermodynamic limit + of the relative Gibbs free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = parameters.number_of_links_maximum + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.nondimensional_persistance_length_small * \ + number_of_links*link_length + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters. \ + nondimensional_force_reference + \ + parameters. \ + nondimensional_force_scale * \ + (0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + nondimensional_end_to_end_length_per_link = \ + model.isotensional.nondimensional_end_to_end_length_per_link( + np.array(nondimensional_force) + ) + nondimensional_gibbs_free_energy = \ + model.isotensional.nondimensional_gibbs_free_energy( + np.array(nondimensional_force), + temperature + ) + nondimensional_gibbs_free_energy_out = \ + model.isometric.nondimensional_helmholtz_free_energy( + np.array(nondimensional_end_to_end_length_per_link), + temperature + ) - nondimensional_force * \ + nondimensional_end_to_end_length_per_link*number_of_links + residual_abs = \ + nondimensional_gibbs_free_energy \ + - nondimensional_gibbs_free_energy_out \ + + np.log( + 4.0*np.sin(np.arccos(np.exp( + -number_of_links*link_length/persistance_length + )))*np.pi**2*hinge_mass*link_length**2 * + parameters.boltzmann_constant*temperature / + parameters.planck_constant**2 + ) + residual_rel = residual_abs / \ + nondimensional_gibbs_free_energy + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol_thermodynamic_limit + ) + + def test_nondimensional_gibbs_free_energy_per_link(self): + """Function to test the thermodynamic limit + of the relative Gibbs free energy per link. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = parameters.number_of_links_maximum + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.nondimensional_persistance_length_small * \ + number_of_links*link_length + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters. \ + nondimensional_force_reference + \ + parameters. \ + nondimensional_force_scale * \ + (0.5 - np.random.rand()) + temperature = \ + parameters.temperature_reference + \ + parameters.temperature_scale*(0.5 - np.random.rand()) + nondimensional_end_to_end_length_per_link = \ + model.isotensional.nondimensional_end_to_end_length_per_link( + np.array(nondimensional_force) + ) + nondimensional_gibbs_free_energy_per_link = \ + model.isotensional.nondimensional_gibbs_free_energy_per_link( + np.array(nondimensional_force), + temperature + ) + nondimensional_gibbs_free_energy_per_link_out = \ + model.isometric.nondimensional_helmholtz_free_energy_per_link( + np.array(nondimensional_end_to_end_length_per_link), + temperature + ) - nondimensional_force * \ + nondimensional_end_to_end_length_per_link + residual_abs = \ + nondimensional_gibbs_free_energy_per_link \ + - nondimensional_gibbs_free_energy_per_link_out \ + + np.log( + 4.0*np.sin(np.arccos(np.exp( + -number_of_links*link_length/persistance_length + )))*np.pi**2*hinge_mass*link_length**2 * + parameters.boltzmann_constant*temperature / + parameters.planck_constant**2 + )/number_of_links + residual_rel = residual_abs / \ + nondimensional_gibbs_free_energy_per_link + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol_thermodynamic_limit + ) + + def test_nondimensional_relative_gibbs_free_energy(self): + """Function to test the thermodynamic limit + of the relative Gibbs free energy. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = parameters.number_of_links_maximum + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.nondimensional_persistance_length_small * \ + number_of_links*link_length + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters. \ + nondimensional_force_reference + \ + parameters. \ + nondimensional_force_scale * \ + (0.5 - np.random.rand()) + nondimensional_end_to_end_length_per_link = \ + model.isotensional.nondimensional_end_to_end_length_per_link( + np.array(nondimensional_force) + ) + nondimensional_relative_gibbs_free_energy = \ + model.isotensional. \ + nondimensional_relative_gibbs_free_energy( + np.array(nondimensional_force) + ) + nondimensional_relative_gibbs_free_energy_out = \ + model.isometric. \ + nondimensional_relative_helmholtz_free_energy( + np.array(nondimensional_end_to_end_length_per_link) + ) - nondimensional_force * \ + nondimensional_end_to_end_length_per_link*number_of_links + residual_abs = \ + nondimensional_relative_gibbs_free_energy \ + - nondimensional_relative_gibbs_free_energy_out + residual_rel = residual_abs / \ + nondimensional_relative_gibbs_free_energy + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol_thermodynamic_limit + ) + + def test_nondimensional_relative_gibbs_free_energy_per_link(self): + """Function to test the thermodynamic limit + of the relative Gibbs free energy per link. + + """ + for _ in range(parameters.number_of_loops): + number_of_links = parameters.number_of_links_maximum + link_length = \ + parameters.link_length_reference + \ + parameters.link_length_scale*(0.5 - np.random.rand()) + hinge_mass = \ + parameters.hinge_mass_reference + \ + parameters.hinge_mass_scale*(0.5 - np.random.rand()) + persistance_length = \ + parameters.nondimensional_persistance_length_small * \ + number_of_links*link_length + model = WLC( + number_of_links, + link_length, + hinge_mass, + persistance_length + ) + nondimensional_force = \ + parameters. \ + nondimensional_force_reference + \ + parameters. \ + nondimensional_force_scale * \ + (0.5 - np.random.rand()) + nondimensional_end_to_end_length_per_link = \ + model.isotensional.nondimensional_end_to_end_length_per_link( + np.array(nondimensional_force) + ) + nondimensional_relative_gibbs_free_energy_per_link = \ + model.isotensional. \ + nondimensional_relative_gibbs_free_energy_per_link( + np.array(nondimensional_force) + ) + nondimensional_relative_gibbs_free_energy_per_link_out = \ + model.isometric. \ + nondimensional_relative_helmholtz_free_energy_per_link( + np.array(nondimensional_end_to_end_length_per_link) + ) - nondimensional_force * \ + nondimensional_end_to_end_length_per_link + residual_abs = \ + nondimensional_relative_gibbs_free_energy_per_link \ + - nondimensional_relative_gibbs_free_energy_per_link_out + residual_rel = residual_abs / \ + nondimensional_relative_gibbs_free_energy_per_link + self.assertLessEqual( + np.abs(residual_rel), + parameters.rel_tol_thermodynamic_limit + ) diff --git a/src/physics/single_chain/wlc/thermodynamics/test.rs b/src/physics/single_chain/wlc/thermodynamics/test.rs index 45903223..f29dce13 100644 --- a/src/physics/single_chain/wlc/thermodynamics/test.rs +++ b/src/physics/single_chain/wlc/thermodynamics/test.rs @@ -1,6 +1,12 @@ #![cfg(test)] use super::*; +use crate::physics:: +{ + BOLTZMANN_CONSTANT, + PLANCK_CONSTANT +}; use crate::physics::single_chain::test::Parameters; +use std::f64::consts::PI; mod base { use super::*; @@ -73,4 +79,501 @@ mod base assert_eq!(persistance_length, model.persistance_length); } } +} +mod thermodynamic_limit +{ + use super::*; + use rand::Rng; + #[test] + fn end_to_end_length() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = parameters.number_of_links_maximum; + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.nondimensional_persistance_length_small*(number_of_links as f64)*link_length; + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_end_to_end_length_per_link = parameters.nondimensional_end_to_end_length_per_link_reference + parameters.nondimensional_end_to_end_length_per_link_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let end_to_end_length = nondimensional_end_to_end_length_per_link*(number_of_links as f64)*link_length; + let force = model.isometric.force(&end_to_end_length, &temperature); + let end_to_end_length_out = model.isotensional.end_to_end_length(&force, &temperature); + let residual_abs = &end_to_end_length - &end_to_end_length_out; + let residual_rel = &residual_abs/&end_to_end_length; + assert!(residual_rel.abs() <= parameters.rel_tol_thermodynamic_limit); + } + } + #[test] + fn end_to_end_length_per_link() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = parameters.number_of_links_maximum; + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.nondimensional_persistance_length_small*(number_of_links as f64)*link_length; + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_end_to_end_length_per_link = parameters.nondimensional_end_to_end_length_per_link_reference + parameters.nondimensional_end_to_end_length_per_link_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let end_to_end_length = nondimensional_end_to_end_length_per_link*(number_of_links as f64)*link_length; + let force = model.isometric.force(&end_to_end_length, &temperature); + let end_to_end_length_per_link = nondimensional_end_to_end_length_per_link*link_length; + let end_to_end_length_per_link_out = model.isotensional.end_to_end_length_per_link(&force, &temperature); + let residual_abs = &end_to_end_length_per_link - &end_to_end_length_per_link_out; + let residual_rel = &residual_abs/&end_to_end_length_per_link; + assert!(residual_rel.abs() <= parameters.rel_tol_thermodynamic_limit); + } + } + #[test] + fn nondimensional_end_to_end_length() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = parameters.number_of_links_maximum; + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.nondimensional_persistance_length_small*(number_of_links as f64)*link_length; + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_end_to_end_length_per_link = parameters.nondimensional_end_to_end_length_per_link_reference + parameters.nondimensional_end_to_end_length_per_link_scale*(0.5 - rng.gen::()); + let nondimensional_force = model.isometric.nondimensional_force(&nondimensional_end_to_end_length_per_link); + let nondimensional_end_to_end_length = nondimensional_end_to_end_length_per_link*(number_of_links as f64); + let nondimensional_end_to_end_length_out = model.isotensional.nondimensional_end_to_end_length(&nondimensional_force); + let residual_abs = &nondimensional_end_to_end_length - &nondimensional_end_to_end_length_out; + let residual_rel = &residual_abs/&nondimensional_end_to_end_length; + assert!(residual_rel.abs() <= parameters.rel_tol_thermodynamic_limit); + } + } + #[test] + fn nondimensional_end_to_end_length_per_link() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = parameters.number_of_links_maximum; + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.nondimensional_persistance_length_small*(number_of_links as f64)*link_length; + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_end_to_end_length_per_link = parameters.nondimensional_end_to_end_length_per_link_reference + parameters.nondimensional_end_to_end_length_per_link_scale*(0.5 - rng.gen::()); + let nondimensional_force = model.isometric.nondimensional_force(&nondimensional_end_to_end_length_per_link); + let nondimensional_end_to_end_length_per_link_out = model.isotensional.nondimensional_end_to_end_length_per_link(&nondimensional_force); + let residual_abs = &nondimensional_end_to_end_length_per_link - &nondimensional_end_to_end_length_per_link_out; + let residual_rel = &residual_abs/&nondimensional_end_to_end_length_per_link; + assert!(residual_rel.abs() <= parameters.rel_tol_thermodynamic_limit); + } + } + #[test] + fn force() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = parameters.number_of_links_maximum; + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.nondimensional_persistance_length_small*(number_of_links as f64)*link_length; + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let end_to_end_length = model.isotensional.end_to_end_length(&force, &temperature); + let force_out = model.isometric.force(&end_to_end_length, &temperature); + let residual_abs = &force - &force_out; + let residual_rel = &residual_abs/&force; + assert!(residual_rel.abs() <= parameters.rel_tol_thermodynamic_limit); + } + } + #[test] + fn nondimensional_force() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = parameters.number_of_links_maximum; + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.nondimensional_persistance_length_small*(number_of_links as f64)*link_length; + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let nondimensional_end_to_end_length_per_link = model.isotensional.nondimensional_end_to_end_length_per_link(&nondimensional_force); + let nondimensional_force_out = model.isometric.nondimensional_force(&nondimensional_end_to_end_length_per_link); + let residual_abs = &nondimensional_force - &nondimensional_force_out; + let residual_rel = &residual_abs/&nondimensional_force; + assert!(residual_rel.abs() <= parameters.rel_tol_thermodynamic_limit); + } + } + #[test] + fn helmholtz_free_energy() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = parameters.number_of_links_maximum; + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.nondimensional_persistance_length_small*(number_of_links as f64)*link_length; + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_end_to_end_length_per_link = parameters.nondimensional_end_to_end_length_per_link_reference + parameters.nondimensional_end_to_end_length_per_link_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let end_to_end_length = nondimensional_end_to_end_length_per_link*(number_of_links as f64)*link_length; + let force = model.isometric.force(&end_to_end_length, &temperature); + let helmholtz_free_energy = model.isometric.helmholtz_free_energy(&end_to_end_length, &temperature); + let helmholtz_free_energy_out = model.isotensional.gibbs_free_energy(&force, &temperature) + force*end_to_end_length; + let residual_abs = &helmholtz_free_energy - &helmholtz_free_energy_out - BOLTZMANN_CONSTANT*temperature*(4.0*(-(number_of_links as f64)*link_length/persistance_length).exp().acos().sin()*PI.powi(2)*hinge_mass*link_length.powi(2)*BOLTZMANN_CONSTANT*temperature/PLANCK_CONSTANT.powi(2)).ln(); + let residual_rel = &residual_abs/&helmholtz_free_energy; + assert!(residual_rel.abs() <= parameters.rel_tol_thermodynamic_limit); + } + } + #[test] + fn helmholtz_free_energy_per_link() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = parameters.number_of_links_maximum; + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.nondimensional_persistance_length_small*(number_of_links as f64)*link_length; + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_end_to_end_length_per_link = parameters.nondimensional_end_to_end_length_per_link_reference + parameters.nondimensional_end_to_end_length_per_link_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let end_to_end_length = nondimensional_end_to_end_length_per_link*(number_of_links as f64)*link_length; + let end_to_end_length_per_link = nondimensional_end_to_end_length_per_link*link_length; + let force = model.isometric.force(&end_to_end_length, &temperature); + let helmholtz_free_energy_per_link = model.isometric.helmholtz_free_energy_per_link(&end_to_end_length, &temperature); + let helmholtz_free_energy_per_link_out = model.isotensional.gibbs_free_energy_per_link(&force, &temperature) + force*end_to_end_length_per_link; + let residual_abs = &helmholtz_free_energy_per_link - &helmholtz_free_energy_per_link_out - BOLTZMANN_CONSTANT*temperature*(4.0*(-(number_of_links as f64)*link_length/persistance_length).exp().acos().sin()*PI.powi(2)*hinge_mass*link_length.powi(2)*BOLTZMANN_CONSTANT*temperature/PLANCK_CONSTANT.powi(2)).ln()/(number_of_links as f64); + let residual_rel = &residual_abs/&helmholtz_free_energy_per_link; + assert!(residual_rel.abs() <= parameters.rel_tol_thermodynamic_limit); + } + } + #[test] + fn relative_helmholtz_free_energy() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = parameters.number_of_links_maximum; + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.nondimensional_persistance_length_small*(number_of_links as f64)*link_length; + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_end_to_end_length_per_link = parameters.nondimensional_end_to_end_length_per_link_reference + parameters.nondimensional_end_to_end_length_per_link_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let end_to_end_length = nondimensional_end_to_end_length_per_link*(number_of_links as f64)*link_length; + let force = model.isometric.force(&end_to_end_length, &temperature); + let relative_helmholtz_free_energy = model.isometric.relative_helmholtz_free_energy(&end_to_end_length, &temperature); + let relative_helmholtz_free_energy_out = model.isotensional.relative_gibbs_free_energy(&force, &temperature) + force*end_to_end_length; + let residual_abs = &relative_helmholtz_free_energy - &relative_helmholtz_free_energy_out; + let residual_rel = &residual_abs/&relative_helmholtz_free_energy; + assert!(residual_rel.abs() <= parameters.rel_tol_thermodynamic_limit); + } + } + #[test] + fn relative_helmholtz_free_energy_per_link() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = parameters.number_of_links_maximum; + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.nondimensional_persistance_length_small*(number_of_links as f64)*link_length; + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_end_to_end_length_per_link = parameters.nondimensional_end_to_end_length_per_link_reference + parameters.nondimensional_end_to_end_length_per_link_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let end_to_end_length = nondimensional_end_to_end_length_per_link*(number_of_links as f64)*link_length; + let end_to_end_length_per_link = nondimensional_end_to_end_length_per_link*link_length; + let force = model.isometric.force(&end_to_end_length, &temperature); + let relative_helmholtz_free_energy_per_link = model.isometric.relative_helmholtz_free_energy_per_link(&end_to_end_length, &temperature); + let relative_helmholtz_free_energy_per_link_out = model.isotensional.relative_gibbs_free_energy_per_link(&force, &temperature) + force*end_to_end_length_per_link; + let residual_abs = &relative_helmholtz_free_energy_per_link - &relative_helmholtz_free_energy_per_link_out; + let residual_rel = &residual_abs/&relative_helmholtz_free_energy_per_link; + assert!(residual_rel.abs() <= parameters.rel_tol_thermodynamic_limit); + } + } + #[test] + fn nondimensional_helmholtz_free_energy() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = parameters.number_of_links_maximum; + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.nondimensional_persistance_length_small*(number_of_links as f64)*link_length; + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_end_to_end_length_per_link = parameters.nondimensional_end_to_end_length_per_link_reference + parameters.nondimensional_end_to_end_length_per_link_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let nondimensional_end_to_end_length = nondimensional_end_to_end_length_per_link*(number_of_links as f64); + let nondimensional_force = model.isometric.nondimensional_force(&nondimensional_end_to_end_length_per_link); + let nondimensional_helmholtz_free_energy = model.isometric.nondimensional_helmholtz_free_energy(&nondimensional_end_to_end_length_per_link, &temperature); + let nondimensional_helmholtz_free_energy_out = model.isotensional.nondimensional_gibbs_free_energy(&nondimensional_force, &temperature) + nondimensional_force*nondimensional_end_to_end_length; + let residual_abs = &nondimensional_helmholtz_free_energy - &nondimensional_helmholtz_free_energy_out - (4.0*(-(number_of_links as f64)*link_length/persistance_length).exp().acos().sin()*PI.powi(2)*hinge_mass*link_length.powi(2)*BOLTZMANN_CONSTANT*temperature/PLANCK_CONSTANT.powi(2)).ln(); + let residual_rel = &residual_abs/&nondimensional_helmholtz_free_energy; + assert!(residual_rel.abs() <= parameters.rel_tol_thermodynamic_limit); + } + } + #[test] + fn nondimensional_helmholtz_free_energy_per_link() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = parameters.number_of_links_maximum; + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.nondimensional_persistance_length_small*(number_of_links as f64)*link_length; + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_end_to_end_length_per_link = parameters.nondimensional_end_to_end_length_per_link_reference + parameters.nondimensional_end_to_end_length_per_link_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let nondimensional_force = model.isometric.nondimensional_force(&nondimensional_end_to_end_length_per_link); + let nondimensional_helmholtz_free_energy_per_link = model.isometric.nondimensional_helmholtz_free_energy_per_link(&nondimensional_end_to_end_length_per_link, &temperature); + let nondimensional_helmholtz_free_energy_per_link_out = model.isotensional.nondimensional_gibbs_free_energy_per_link(&nondimensional_force, &temperature) + nondimensional_force*nondimensional_end_to_end_length_per_link; + let residual_abs = &nondimensional_helmholtz_free_energy_per_link - &nondimensional_helmholtz_free_energy_per_link_out - (4.0*(-(number_of_links as f64)*link_length/persistance_length).exp().acos().sin()*PI.powi(2)*hinge_mass*link_length.powi(2)*BOLTZMANN_CONSTANT*temperature/PLANCK_CONSTANT.powi(2)).ln()/(number_of_links as f64); + let residual_rel = &residual_abs/&nondimensional_helmholtz_free_energy_per_link; + assert!(residual_rel.abs() <= parameters.rel_tol_thermodynamic_limit); + } + } + #[test] + fn nondimensional_relative_helmholtz_free_energy() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = parameters.number_of_links_maximum; + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.nondimensional_persistance_length_small*(number_of_links as f64)*link_length; + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_end_to_end_length_per_link = parameters.nondimensional_end_to_end_length_per_link_reference + parameters.nondimensional_end_to_end_length_per_link_scale*(0.5 - rng.gen::()); + let nondimensional_end_to_end_length = nondimensional_end_to_end_length_per_link*(number_of_links as f64); + let nondimensional_force = model.isometric.nondimensional_force(&nondimensional_end_to_end_length_per_link); + let nondimensional_relative_helmholtz_free_energy = model.isometric.nondimensional_relative_helmholtz_free_energy(&nondimensional_end_to_end_length_per_link); + let nondimensional_relative_helmholtz_free_energy_out = model.isotensional.nondimensional_relative_gibbs_free_energy(&nondimensional_force) + nondimensional_force*nondimensional_end_to_end_length; + let residual_abs = &nondimensional_relative_helmholtz_free_energy - &nondimensional_relative_helmholtz_free_energy_out; + let residual_rel = &residual_abs/&nondimensional_relative_helmholtz_free_energy; + assert!(residual_rel.abs() <= parameters.rel_tol_thermodynamic_limit); + } + } + #[test] + fn nondimensional_relative_helmholtz_free_energy_per_link() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = parameters.number_of_links_maximum; + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.nondimensional_persistance_length_small*(number_of_links as f64)*link_length; + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_end_to_end_length_per_link = parameters.nondimensional_end_to_end_length_per_link_reference + parameters.nondimensional_end_to_end_length_per_link_scale*(0.5 - rng.gen::()); + let nondimensional_force = model.isometric.nondimensional_force(&nondimensional_end_to_end_length_per_link); + let nondimensional_relative_helmholtz_free_energy_per_link = model.isometric.nondimensional_relative_helmholtz_free_energy_per_link(&nondimensional_end_to_end_length_per_link); + let nondimensional_relative_helmholtz_free_energy_per_link_out = model.isotensional.nondimensional_relative_gibbs_free_energy_per_link(&nondimensional_force) + nondimensional_force*nondimensional_end_to_end_length_per_link; + let residual_abs = &nondimensional_relative_helmholtz_free_energy_per_link - &nondimensional_relative_helmholtz_free_energy_per_link_out; + let residual_rel = &residual_abs/&nondimensional_relative_helmholtz_free_energy_per_link; + assert!(residual_rel.abs() <= parameters.rel_tol_thermodynamic_limit); + } + } + #[test] + fn gibbs_free_energy() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = parameters.number_of_links_maximum; + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.nondimensional_persistance_length_small*(number_of_links as f64)*link_length; + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let end_to_end_length = model.isotensional.end_to_end_length(&force, &temperature); + let gibbs_free_energy = model.isotensional.gibbs_free_energy(&force, &temperature); + let gibbs_free_energy_out = model.isometric.helmholtz_free_energy(&end_to_end_length, &temperature) - force*end_to_end_length; + let residual_abs = &gibbs_free_energy - &gibbs_free_energy_out + BOLTZMANN_CONSTANT*temperature*(4.0*(-(number_of_links as f64)*link_length/persistance_length).exp().acos().sin()*PI.powi(2)*hinge_mass*link_length.powi(2)*BOLTZMANN_CONSTANT*temperature/PLANCK_CONSTANT.powi(2)).ln(); + let residual_rel = &residual_abs/&gibbs_free_energy; + assert!(residual_rel.abs() <= parameters.rel_tol_thermodynamic_limit); + } + } + #[test] + fn gibbs_free_energy_per_link() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = parameters.number_of_links_maximum; + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.nondimensional_persistance_length_small*(number_of_links as f64)*link_length; + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let end_to_end_length = model.isotensional.end_to_end_length(&force, &temperature); + let end_to_end_length_per_link = end_to_end_length/(number_of_links as f64); + let gibbs_free_energy_per_link = model.isotensional.gibbs_free_energy_per_link(&force, &temperature); + let gibbs_free_energy_per_link_out = model.isometric.helmholtz_free_energy_per_link(&end_to_end_length, &temperature) - force*end_to_end_length_per_link; + let residual_abs = &gibbs_free_energy_per_link - &gibbs_free_energy_per_link_out + BOLTZMANN_CONSTANT*temperature*(4.0*(-(number_of_links as f64)*link_length/persistance_length).exp().acos().sin()*PI.powi(2)*hinge_mass*link_length.powi(2)*BOLTZMANN_CONSTANT*temperature/PLANCK_CONSTANT.powi(2)).ln()/(number_of_links as f64); + let residual_rel = &residual_abs/&gibbs_free_energy_per_link; + assert!(residual_rel.abs() <= parameters.rel_tol_thermodynamic_limit); + } + } + #[test] + fn relative_gibbs_free_energy() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = parameters.number_of_links_maximum; + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.nondimensional_persistance_length_small*(number_of_links as f64)*link_length; + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let end_to_end_length = model.isotensional.end_to_end_length(&force, &temperature); + let relative_gibbs_free_energy = model.isotensional.relative_gibbs_free_energy(&force, &temperature); + let relative_gibbs_free_energy_out = model.isometric.relative_helmholtz_free_energy(&end_to_end_length, &temperature) - force*end_to_end_length; + let residual_abs = &relative_gibbs_free_energy - &relative_gibbs_free_energy_out; + let residual_rel = &residual_abs/&relative_gibbs_free_energy; + assert!(residual_rel.abs() <= parameters.rel_tol_thermodynamic_limit); + } + } + #[test] + fn relative_gibbs_free_energy_per_link() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = parameters.number_of_links_maximum; + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.nondimensional_persistance_length_small*(number_of_links as f64)*link_length; + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let force = nondimensional_force*BOLTZMANN_CONSTANT*temperature/link_length; + let end_to_end_length = model.isotensional.end_to_end_length(&force, &temperature); + let end_to_end_length_per_link = end_to_end_length/(number_of_links as f64); + let relative_gibbs_free_energy_per_link = model.isotensional.relative_gibbs_free_energy_per_link(&force, &temperature); + let relative_gibbs_free_energy_per_link_out = model.isometric.relative_helmholtz_free_energy_per_link(&end_to_end_length, &temperature) - force*end_to_end_length_per_link; + let residual_abs = &relative_gibbs_free_energy_per_link - &relative_gibbs_free_energy_per_link_out; + let residual_rel = &residual_abs/&relative_gibbs_free_energy_per_link; + assert!(residual_rel.abs() <= parameters.rel_tol_thermodynamic_limit); + } + } + #[test] + fn nondimensional_gibbs_free_energy() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = parameters.number_of_links_maximum; + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.nondimensional_persistance_length_small*(number_of_links as f64)*link_length; + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let nondimensional_end_to_end_length = model.isotensional.nondimensional_end_to_end_length(&nondimensional_force); + let nondimensional_end_to_end_length_per_link = nondimensional_end_to_end_length/(number_of_links as f64); + let nondimensional_gibbs_free_energy = model.isotensional.nondimensional_gibbs_free_energy(&nondimensional_force, &temperature); + let nondimensional_gibbs_free_energy_out = model.isometric.nondimensional_helmholtz_free_energy(&nondimensional_end_to_end_length_per_link, &temperature) - nondimensional_force*nondimensional_end_to_end_length; + let residual_abs = &nondimensional_gibbs_free_energy - &nondimensional_gibbs_free_energy_out + (4.0*(-(number_of_links as f64)*link_length/persistance_length).exp().acos().sin()*PI.powi(2)*hinge_mass*link_length.powi(2)*BOLTZMANN_CONSTANT*temperature/PLANCK_CONSTANT.powi(2)).ln(); + let residual_rel = &residual_abs/&nondimensional_gibbs_free_energy; + assert!(residual_rel.abs() <= parameters.rel_tol_thermodynamic_limit); + } + } + #[test] + fn nondimensional_gibbs_free_energy_per_link() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = parameters.number_of_links_maximum; + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.nondimensional_persistance_length_small*(number_of_links as f64)*link_length; + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let temperature = parameters.temperature_reference + parameters.temperature_scale*(0.5 - rng.gen::()); + let nondimensional_end_to_end_length_per_link = model.isotensional.nondimensional_end_to_end_length_per_link(&nondimensional_force); + let nondimensional_gibbs_free_energy_per_link = model.isotensional.nondimensional_gibbs_free_energy_per_link(&nondimensional_force, &temperature); + let nondimensional_gibbs_free_energy_per_link_out = model.isometric.nondimensional_helmholtz_free_energy_per_link(&nondimensional_end_to_end_length_per_link, &temperature) - nondimensional_force*nondimensional_end_to_end_length_per_link; + let residual_abs = &nondimensional_gibbs_free_energy_per_link - &nondimensional_gibbs_free_energy_per_link_out + (4.0*(-(number_of_links as f64)*link_length/persistance_length).exp().acos().sin()*PI.powi(2)*hinge_mass*link_length.powi(2)*BOLTZMANN_CONSTANT*temperature/PLANCK_CONSTANT.powi(2)).ln()/(number_of_links as f64); + let residual_rel = &residual_abs/&nondimensional_gibbs_free_energy_per_link; + assert!(residual_rel.abs() <= parameters.rel_tol_thermodynamic_limit); + } + } + #[test] + fn nondimensional_relative_gibbs_free_energy() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = parameters.number_of_links_maximum; + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.nondimensional_persistance_length_small*(number_of_links as f64)*link_length; + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let nondimensional_end_to_end_length = model.isotensional.nondimensional_end_to_end_length(&nondimensional_force); + let nondimensional_end_to_end_length_per_link = nondimensional_end_to_end_length/(number_of_links as f64); + let nondimensional_relative_gibbs_free_energy = model.isotensional.nondimensional_relative_gibbs_free_energy(&nondimensional_force); + let nondimensional_relative_gibbs_free_energy_out = model.isometric.nondimensional_relative_helmholtz_free_energy(&nondimensional_end_to_end_length_per_link) - nondimensional_force*nondimensional_end_to_end_length; + let residual_abs = &nondimensional_relative_gibbs_free_energy - &nondimensional_relative_gibbs_free_energy_out; + let residual_rel = &residual_abs/&nondimensional_relative_gibbs_free_energy; + assert!(residual_rel.abs() <= parameters.rel_tol_thermodynamic_limit); + } + } + #[test] + fn nondimensional_relative_gibbs_free_energy_per_link() + { + let mut rng = rand::thread_rng(); + let parameters = Parameters::default(); + for _ in 0..parameters.number_of_loops + { + let number_of_links: u8 = parameters.number_of_links_maximum; + let link_length = parameters.link_length_reference + parameters.link_length_scale*(0.5 - rng.gen::()); + let hinge_mass = parameters.hinge_mass_reference + parameters.hinge_mass_scale*(0.5 - rng.gen::()); + let persistance_length = parameters.nondimensional_persistance_length_small*(number_of_links as f64)*link_length; + let model = WLC::init(number_of_links, link_length, hinge_mass, persistance_length); + let nondimensional_force = parameters.nondimensional_force_reference + parameters.nondimensional_force_scale*(0.5 - rng.gen::()); + let nondimensional_end_to_end_length_per_link = model.isotensional.nondimensional_end_to_end_length_per_link(&nondimensional_force); + let nondimensional_relative_gibbs_free_energy_per_link = model.isotensional.nondimensional_relative_gibbs_free_energy_per_link(&nondimensional_force); + let nondimensional_relative_gibbs_free_energy_per_link_out = model.isometric.nondimensional_relative_helmholtz_free_energy_per_link(&nondimensional_end_to_end_length_per_link) - nondimensional_force*nondimensional_end_to_end_length_per_link; + let residual_abs = &nondimensional_relative_gibbs_free_energy_per_link - &nondimensional_relative_gibbs_free_energy_per_link_out; + let residual_rel = &residual_abs/&nondimensional_relative_gibbs_free_energy_per_link; + assert!(residual_rel.abs() <= parameters.rel_tol_thermodynamic_limit); + } + } } \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index dec114fc..a1160404 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -165,3 +165,5 @@ include("../src/physics/single_chain/wlc/test.jl") include("../src/physics/single_chain/wlc/thermodynamics/test.jl") include("../src/physics/single_chain/wlc/thermodynamics/isometric/test.jl") include("../src/physics/single_chain/wlc/thermodynamics/isometric/legendre/test.jl") +include("../src/physics/single_chain/wlc/thermodynamics/isotensional/test.jl") +include("../src/physics/single_chain/wlc/thermodynamics/isotensional/legendre/test.jl") \ No newline at end of file