Source code for ufjc.swfjc

"""The core module for the SWFJC single-chain model.

This module contains the core class ``SWFJC`` which, upon instantiation,
becomes a SWFJC single-chain model instance with methods for computing
single-chain quantities in either thermodynamic ensemble.
The ``SWFJCIsometric`` and ``SWFJCIsotensional`` classes are also
contained within this module.
The SWFJC is the uFJC model with a square-well link potential,
which is a special case that is efficiently treated separately
as it can be solved exactly :cite:`swfjc-buche2022freely`.

Example:
    Import and create an instance of the model:

        >>> from ufjc import SWFJC
        >>> model = SWFJC()

"""

# Import internal modules
from .utility import BasicUtility

# Import external modules
import numpy as np


[docs]class SWFJCIsometric(BasicUtility): """The SWFJC model class for the isometric ensemble. Attributes: N (int): The number of links in the chain. varsigma (float): The nondimensional well width. """ def __init__(self, N_b=8, varsigma=3): """Initializes the ``SWFJCIsometric`` class. Initialize and inherit all attributes and methods from a ``BasicUtility`` class instance. Args: N_b (int, optional, default=8): The number of links in the chain. varsigma (float, optional, default=3): The nondimensional well width. """ super().__init__() self.N_b = N_b self.varsigma = varsigma
[docs]class SWFJCIsotensional(SWFJCIsometric): """The SWFJC model class for the isotensional ensemble. Attributes: N (int): The number of links in the chain. varsigma (float): The nondimensional well width. """ def __init__(self, N_b=8, varsigma=2): """Initializes the ``SWFJCIsotensional`` class. Initialize and inherit all attributes and methods from a ``SWFJCIsometric`` class instance. Args: N_b (int, optional, default=8): The number of links in the chain. varsigma (float, optional, default=3): One plus the nondimensional well width. """ super().__init__() self.N_b = N_b self.varsigma = varsigma
[docs] def z(self, eta): r"""The nondimensional single-link isotensional partition function as a function of the nondimensional force, .. math:: \mathfrak{z}(\eta) = \frac{1}{\eta^3}\left[ \varsigma\eta\cosh(\varsigma\eta) - \sinh(\varsigma\eta) - \eta\cosh(\eta) + \sinh(\eta) \right]. Args: v (array_like): The nondimensional force. Returns: numpy.ndarray: The nondimensional isotensional partition function. """ eta = self.np_array(eta) eta_zero = eta == 0 eta[eta_zero] = -1 z = ( np.sinh(eta) - eta*np.cosh(eta) - np.sinh(self.varsigma*eta) + self.varsigma*eta*np.cosh(self.varsigma*eta) )/eta**3 z[eta_zero] = (self.varsigma**3 - 1)/3 return z
[docs] def beta_varphi(self, eta): r"""The nondimensional isotensional free energy as a function of the nondimensional force, .. math:: \beta\varphi(\eta) = -\ln\mathfrak{z}(\eta). Note that this becomes the isotensional free energy of the FJC model as :math:`\varsigma` goes to zero, .. math:: \lim_{\varsigma\to 0}\beta\varphi(\eta) = \ln\left[\frac{\eta}{\sinh(\eta)}\right]. Args: v (array_like): The nondimensional force. Returns: numpy.ndarray: The nondimensional isotensional free energy. Example: Plot the nondimensional isotensional free energy as a function of the nondimensional force for varying :math:`\varsigma`: .. plot:: >>> import numpy as np >>> import matplotlib.pyplot as plt >>> from ufjc.swfjc import SWFJCIsotensional >>> eta = np.linspace(0, 10, 1000)[1:] >>> _ = plt.figure() >>> for varsigma in [2, 3, 5, 10]: ... model = SWFJCIsotensional(varsigma=varsigma) ... _ = plt.plot(eta, model.beta_varphi(eta), ... label=r'$\varsigma=$'+str(varsigma)) >>> _ = plt.plot(eta, np.log(eta/np.sinh(eta)), ... 'k--', label='FJC') >>> _ = plt.xlabel(r'$\eta$') >>> _ = plt.ylabel(r'$\beta\varphi$') >>> _ = plt.legend() >>> plt.show() """ return (np.log(self.z(0)) - np.log(self.z(eta)))/self.varsigma
[docs] def gamma_isotensional(self, eta): r"""The nondimensional end-to-end length as a function of the nondimensional force in the isotensional ensemble, .. math:: \gamma(\eta) = \frac{\partial}{\partial\eta}\,\ln\mathfrak{z}(\eta) = \frac{ \varsigma^2\eta\sinh(\varsigma\eta) - \eta\sinh(\eta) }{ \varsigma\eta\cosh(\varsigma\eta) - \sinh(\varsigma\eta) - \eta\cosh(\eta) + \sinh(\eta) } Note that this becomes the Langevin function of the FJC model as :math:`\varsigma` goes to zero, .. math:: \lim_{\varsigma\to 0}\gamma(\eta) = \coth(\eta) - \frac{1}{\eta} = \mathcal{L}(\eta). Args: v (array_like): The nondimensional force. Returns: numpy.ndarray: The nondimensional end-to-end length. Example: Plot the nondimensional single-chain mechanical response in the isotensional ensemble for varying :math:`\varsigma`: .. plot:: >>> import numpy as np >>> import matplotlib.pyplot as plt >>> from ufjc.swfjc import SWFJCIsotensional >>> eta = np.linspace(0, 10, 1000)[1:] >>> _ = plt.figure() >>> for varsigma in [2, 3, 5, 10]: ... model = SWFJCIsotensional(varsigma=varsigma) ... _ = plt.plot( ... model.gamma_isotensional(eta)/varsigma, ... eta, label=r'$\varsigma=$'+str(varsigma)) >>> _ = plt.plot(1/np.tanh(eta) - 1/eta, eta, ... 'k--', label='FJC') >>> _ = plt.xlabel(r'$\gamma/\varsigma$') >>> _ = plt.ylabel(r'$\eta$') >>> _ = plt.legend() >>> plt.show() """ return ( self.varsigma**2*eta*np.sinh(self.varsigma*eta) - eta*np.sinh(eta) )/( np.sinh(eta) - eta*np.cosh(eta) - np.sinh(self.varsigma*eta) + self.varsigma*eta*np.cosh(self.varsigma*eta) ) - 3/eta
[docs]class SWFJC(SWFJCIsotensional): """The SWFJC single-chain model class. Attributes: N (int): The number of links in the chain. varsigma (float): The nondimensional well width. """ def __init__(self): """Initializes the ``SWFJC`` class. Initialize and inherit all attributes and methods from a ``SWFJCIsotensional`` class instance. Args: N_b (int, optional, default=8): The number of links in the chain. varsigma (float, optional, default=3): The nondimensional well width. """ super().__init__()