chemistrykit.polymer#
chemistrykit.polymer: polymer chemistry.
Ideal random-walk chain statistics (end-to-end distance, radius of
gyration) and the Flory exponent for real chains under different solvent
conditions; molecular-weight-distribution statistics (Mn, Mw, PDI) and
the Flory-Schulz (most-probable) distribution derived from step-growth
polymerization statistics; the Carothers equation for step-growth
kinetics; and chain-growth/free-radical polymerization kinetics built on
chemistrykit.kinetics’s reaction-network engine, with its
steady-state-approximation closed form as a cross-check. Also Kuhn’s
freely jointed chain and the Kratky-Porod worm-like chain; Flory-Huggins
solution thermodynamics; Staudinger/Mark-Houwink solution viscosity;
Debye light scattering vs. osmometry; Flory-Stockmayer gelation; the
Mayo-Lewis copolymer equation; Poisson (living) chain-length
distributions; and Bernoullian tacticity statistics.
- class chemistrykit.polymer.IdealChain[source]#
Bases:
PolymerChainModelAn ideal (theta-solvent) polymer chain: an exact random walk of n segments.
Examples
The mean-square end-to-end distance is exactly \(nb^2\) (Rubinstein & Colby, Polymer Physics, 2003, eq. 2.44):
>>> chain = IdealChain() >>> chain.mean_square_end_to_end(n=100, b=0.5) 25.0
The radius of gyration is exactly \(R/\sqrt{6}\):
>>> import numpy as np >>> n, b = 200, 0.6 >>> ratio = chain.mean_square_end_to_end(n, b) / chain.mean_square_radius_of_gyration(n, b) >>> round(float(ratio), 6) 6.0
Doubling n exactly doubles \(\langle R^2\rangle\) (linear scaling, \(\nu=1/2\) since \(R^2\sim n^{2\nu}=n\)):
>>> chain.mean_square_end_to_end(n=400, b=0.5) / chain.mean_square_end_to_end(n=200, b=0.5) 2.0
- class chemistrykit.polymer.MolecularWeightDistribution(Mn, Mw, PDI)[source]#
Bases:
objectSummary statistics (Mn, Mw, PDI) of a molar-mass distribution.
- classmethod from_counts(N_i, M_i)[source]#
Build from raw (count, molar mass) fraction data.
- Parameters:
N_i (array-like of float) – As in
number_average_molar_mass().M_i (array-like of float) – As in
number_average_molar_mass().
- Return type:
- Returns:
MolecularWeightDistribution
Examples
>>> mwd = MolecularWeightDistribution.from_counts(N_i=[10.0, 5.0], M_i=[1000.0, 2000.0]) >>> round(mwd.Mn, 2), round(mwd.Mw, 2) (1333.33, 1500.0) >>> round(mwd.PDI, 4) 1.125
- class chemistrykit.polymer.PolymerChainModel[source]#
Bases:
ABCCommon interface for a polymer chain’s characteristic size as a function of chain length.
Concrete subclasses (
chemistrykit.polymer.systems.chain_statistics.IdealChain,chemistrykit.polymer.systems.chain_statistics.RealChain) implementmean_square_end_to_end()andmean_square_radius_of_gyration();end_to_end_distance()andradius_of_gyration()(their square roots) are then available for every subclass for free.- abstractmethod mean_square_end_to_end(n, b)[source]#
Return \(\langle R^2\rangle\) for a chain of n segments of length b.
- class chemistrykit.polymer.RealChain(nu)[source]#
Bases:
PolymerChainModelA real (excluded-volume) polymer chain: \(R = bn^\nu\) with Flory exponent nu.
- Parameters:
nu (
float) – Flory scaling exponent. Usetheta_solvent(),good_solvent(), orpoor_solvent()for the standard named values rather than passing nu directly, unless modeling a nonstandard solvent quality.
Examples
A real chain in a good solvent is more swollen (larger) than an ideal chain of the same n:
>>> ideal = IdealChain() >>> good = RealChain.good_solvent() >>> n, b = 1000, 0.5 >>> bool(good.end_to_end_distance(n, b) > ideal.end_to_end_distance(n, b)) True
A real chain in a poor solvent is more collapsed (smaller):
>>> poor = RealChain.poor_solvent() >>> bool(poor.end_to_end_distance(n, b) < ideal.end_to_end_distance(n, b)) True
At the theta point,
RealChainreduces exactly to the ideal chain’s end-to-end distance (though not, by construction here, to its exact radius-of-gyration prefactor – see the module docstring):>>> theta = RealChain.theta_solvent() >>> round(float(theta.end_to_end_distance(n, b)), 6) == round(float(ideal.end_to_end_distance(n, b)), 6) True
- classmethod good_solvent()[source]#
Build a
RealChainwith Flory’s mean-field good-solvent exponent (nu=3/5).See
good_solvent_renormalization_group()for the more precise renormalization-group value.- Return type:
- classmethod good_solvent_renormalization_group()[source]#
Build a
RealChainwith the renormalization-group good-solvent exponent (nu ~= 0.588).This is de Gennes’ (1979) more precise value, refining Flory’s original mean-field estimate (
good_solvent(), nu=3/5) by about 2% – see the module docstring.- Return type:
Examples
>>> flory = RealChain.good_solvent() >>> de_gennes = RealChain.good_solvent_renormalization_group() >>> relative_difference = abs(flory.nu - de_gennes.nu) / flory.nu >>> round(float(relative_difference) * 100, 2) 2.0
- mean_square_end_to_end(n, b)[source]#
Return \(\langle R^2\rangle\) for a chain of n segments of length b.
- mean_square_radius_of_gyration(n, b)[source]#
See the module docstring: the 1/6 prefactor is an ideal-chain approximation, not exact for real chains.
- chemistrykit.polymer.azeotropic_feed_composition(r1, r2)[source]#
Azeotropic feed \(f_1^*=(1-r_2)/(2-r_1-r_2)\) at which \(F_1=f_1\).
- Parameters:
- Return type:
- Returns:
float
Examples
Styrene (1) / methyl methacrylate (2), \(r_1\approx0.52\), \(r_2\approx0.46\):
>>> fstar = azeotropic_feed_composition(0.52, 0.46) >>> round(fstar, 4) 0.5294 >>> round(float(mayo_lewis_copolymer_composition(fstar, 0.52, 0.46)), 4) 0.5294
- chemistrykit.polymer.bernoullian_triad_fractions(Pm)[source]#
Triad fractions \(([mm],[mr],[rr])\) for Bernoullian placement with meso probability Pm.
- Parameters:
Pm (float or array-like of float) – Probability of a meso placement.
- Return type:
- Returns:
mm, mr, rr (float or ndarray) – They sum to 1.
Examples
Atactic (\(P_m=1/2\)) gives the 1:2:1 ratio:
>>> tuple(round(float(v), 3) for v in bernoullian_triad_fractions(0.5)) (0.25, 0.5, 0.25)
- chemistrykit.polymer.branching_number_average_DP(p, f)[source]#
Number-average degree of polymerization of \(A_f\) self-condensation, \(1/(1-fp/2)\).
- Parameters:
- Returns:
float or ndarray
Examples
Finite at the Flory-Stockmayer gel point, \(2(f-1)/(f-2)\):
>>> round(float(branching_number_average_DP(0.5, f=3)), 6) 4.0
- chemistrykit.polymer.branching_weight_average_DP(p, f)[source]#
Weight-average degree of polymerization of \(A_f\) self-condensation, \((1+p)/(1-(f-1)p)\).
- Parameters:
- Returns:
float or ndarray
Examples
With
f=2this is the Flory-Schulz \((1+p)/(1-p)\):>>> round(float(branching_weight_average_DP(0.9, f=2)), 6) 19.0
- chemistrykit.polymer.carothers_gel_point(f_avg)[source]#
Carothers gel point, \(p_c=2/f_\text{avg}\) (where \(\bar X_n\to\infty\)).
Examples
>>> round(carothers_gel_point(3.0), 6) 0.666667
- chemistrykit.polymer.debye_Kc_over_R(c, Mw, A2=0.0)[source]#
Debye relation \(Kc/R_0=1/M_w+2A_2c\) (zero scattering angle).
- Parameters:
- Returns:
float or ndarray
Examples
>>> float(debye_Kc_over_R(0.0, Mw=1e5, A2=1e-4)) 1e-05
- chemistrykit.polymer.degree_of_polymerization(p)[source]#
The Carothers equation: \(\bar{X}_n=1/(1-p)\).
- Parameters:
p (
float) – Extent of reaction (fraction of functional groups reacted), \(0\le p<1\).- Return type:
- Returns:
float
Examples
At 90% conversion the average chain is 10 repeat units long:
>>> round(degree_of_polymerization(0.9), 6) 10.0
A “high” molecular weight polymer genuinely requires extreme conversion – reaching \(\bar X_n=100\) needs 99% conversion, not 90%:
>>> round(degree_of_polymerization(0.99), 1) 100.0
- chemistrykit.polymer.degree_of_polymerization_stoichiometric_imbalance(p, r)[source]#
Carothers equation generalized to a stoichiometric imbalance of the two functional groups.
When the two reactive functional groups (e.g. -OH and -COOH) are not present in exactly equal number – either a deliberate stoichiometric imbalance, or the deliberate addition of a monofunctional “chain stopper” – the maximum attainable degree of polymerization is capped even at \(p=1\) (Odian, Principles of Polymerization, 4th ed., Ch. 2.2, eq. 2-70):
\[\bar{X}_n = \frac{1+r}{1+r-2rp}\]where \(r\le1\) is the ratio of the minority to majority functional-group count (or, with a monofunctional chain stopper of concentration \(N_B'\), \(r=N_A/(N_B+2N_B')\); see Odian for the full derivation). Setting \(r=1\) (perfect stoichiometric balance) recovers the ordinary
degree_of_polymerization()exactly.- Parameters:
- Return type:
- Returns:
float
Examples
With
r=1this reduces exactly to the ordinary Carothers equation:>>> round(degree_of_polymerization_stoichiometric_imbalance(p=0.9, r=1.0), 10) 10.0
A stoichiometric imbalance caps the attainable degree of polymerization even at complete conversion of the limiting group (\(p=1\)):
>>> round(degree_of_polymerization_stoichiometric_imbalance(p=1.0, r=0.98), 4) 99.0
- chemistrykit.polymer.extent_of_reaction_for_DP(Xn)[source]#
Invert the Carothers equation: the extent of reaction needed for a target \(\bar X_n\).
- Parameters:
Xn (
float) – Target number-average degree of polymerization, \(\ge1\).- Return type:
- Returns:
float – Required extent of reaction p.
Examples
Exactly inverts
degree_of_polymerization():>>> round(extent_of_reaction_for_DP(degree_of_polymerization(0.95)), 10) 0.95
- chemistrykit.polymer.fit_mark_houwink(M, eta)[source]#
Fit \([\eta]=KM^a\) by least squares on \(\log[\eta]\) vs \(\log M\).
- Parameters:
- Return type:
- Returns:
K, a (float)
Examples
>>> M = np.array([1e4, 1e5, 1e6]) >>> K, a = fit_mark_houwink(M, 0.02 * M**0.7) >>> round(K, 6), round(a, 6) (0.02, 0.7)
- chemistrykit.polymer.flory_exponent(solvent)[source]#
Return the standard Flory scaling exponent \(\nu\) for a named solvent quality.
- Parameters:
solvent (
str) – Solvent quality (see the module docstring).- Return type:
- Returns:
float
Examples
>>> flory_exponent("theta") 0.5 >>> round(flory_exponent("good"), 3) 0.6 >>> round(flory_exponent("poor"), 4) 0.3333
- chemistrykit.polymer.flory_huggins_critical_point(N)[source]#
Critical point \((\phi_c,\chi_c)\) of a polymer solution.
- Parameters:
N (
float) – Degree of polymerization.- Return type:
- Returns:
phi_c, chi_c (float) – \(\phi_c=1/(1+\sqrt N)\) and \(\chi_c=\tfrac12(1+1/\sqrt N)^2\).
Examples
>>> phi_c, chi_c = flory_huggins_critical_point(100) >>> round(phi_c, 6), round(chi_c, 6) (0.090909, 0.605)
For very long chains \(\chi_c\) approaches the theta value 1/2:
>>> round(flory_huggins_critical_point(1e12)[1], 5) 0.5
- chemistrykit.polymer.flory_huggins_free_energy(phi, N, chi)[source]#
Flory-Huggins free energy of mixing per lattice site, in units of \(k_BT\).
- Parameters:
- Returns:
float or ndarray
Examples
With
N=1andchi=0this is the ideal mixing entropy of a symmetric binary mixture, \(\ln\frac12\) at \(\phi=1/2\):>>> round(float(flory_huggins_free_energy(0.5, N=1, chi=0.0)), 6) -0.693147
- chemistrykit.polymer.flory_huggins_spinodal_chi(phi, N)[source]#
Interaction parameter on the spinodal, \(\chi_s=\frac12[1/(N\phi)+1/(1-\phi)]\).
- Parameters:
- Returns:
float or ndarray
Examples
For a symmetric blend (
N=1) the spinodal at \(\phi=1/2\) is \(\chi=2\):>>> float(flory_huggins_spinodal_chi(0.5, N=1)) 2.0
- chemistrykit.polymer.flory_schulz_number_average_DP(p)[source]#
Flory-Schulz number-average degree of polymerization \(\bar X_n=1/(1-p)\).
Identical to the Carothers equation,
chemistrykit.polymer.systems.step_growth.degree_of_polymerization()– this is the same quantity derived from the chain-length distribution’s first moment rather than from a mass-balance argument.Examples
>>> round(flory_schulz_number_average_DP(0.9), 6) 10.0
- chemistrykit.polymer.flory_schulz_number_fraction(x, p)[source]#
Flory-Schulz number fraction of chains of length x: \(N_x=(1-p)p^{x-1}\).
- Parameters:
- Returns:
float or ndarray
Examples
The number fractions over all chain lengths sum to exactly 1 (a proper probability distribution):
>>> import numpy as np >>> x = np.arange(1, 5000) >>> round(float(np.sum(flory_schulz_number_fraction(x, p=0.98))), 6) 1.0
- chemistrykit.polymer.flory_schulz_pdi(p)[source]#
Flory-Schulz polydispersity index \(\text{\dj}=\bar X_w/\bar X_n=1+p\).
- Parameters:
p (
float) – Extent of reaction, \(0\le p\le1\).- Return type:
- Returns:
float – In \([1, 2]\).
Examples
The PDI is exactly 1 with no reaction (every “chain” is a lone monomer – trivially monodisperse):
>>> flory_schulz_pdi(0.0) 1.0
The PDI approaches exactly 2 in the high-conversion limit (\(p\to1\)) – the textbook result that an ideal step-growth polymerization’s molecular-weight distribution can never exceed a polydispersity of 2, however far the reaction is driven:
>>> flory_schulz_pdi(1.0) 2.0
- chemistrykit.polymer.flory_schulz_weight_average_DP(p)[source]#
Flory-Schulz weight-average degree of polymerization \(\bar X_w=(1+p)/(1-p)\).
Examples
>>> round(flory_schulz_weight_average_DP(0.9), 6) 19.0
- chemistrykit.polymer.flory_schulz_weight_fraction(x, p)[source]#
Flory-Schulz weight fraction of chains of length x: \(w_x=x(1-p)^2p^{x-1}\).
Examples
The weight fractions over all chain lengths sum to exactly 1:
>>> import numpy as np >>> x = np.arange(1, 5000) >>> round(float(np.sum(flory_schulz_weight_fraction(x, p=0.98))), 6) 1.0
- chemistrykit.polymer.flory_stockmayer_gel_point(f)[source]#
Flory-Stockmayer gel point for \(A_f\) self-condensation, \(p_c=1/(f-1)\).
- Parameters:
f (
float) – Monomer functionality (> 2 for gelation at \(p<1\)).- Return type:
- Returns:
float
Examples
>>> flory_stockmayer_gel_point(3) 0.5
- chemistrykit.polymer.free_radical_network(kd, f, kp, kt, I0, M0, mode='combination')[source]#
Build the lumped initiation/propagation/termination free-radical polymerization network.
Species, in order:
I(initiator),M(monomer),R(total radical concentration, lumped over all chain lengths),D(dead polymer chains formed by termination – a count of chains, not monomer units incorporated). The three reactions:I -> 2Rat rate \((fk_d)[I]\) (so the net radical production rate is \(2fk_d[I]=R_i\), matching the standard definition above).R + M -> Rat rate \(k_p[R][M]\) (propagation: consumes a monomer, leaves the radical count unchanged – the radical that reacted is regenerated one unit longer).2R -> D(mode=”combination”, one dead chain per termination event) or2R -> 2D(mode=”disproportionation”, two dead chains per event) at rate \(k_t[R]^2\).
- Parameters:
kd (
float) – Initiator decomposition rate constant.f (
float) – Initiator efficiency, \(0<f\le1\).kp (
float) – Propagation rate constant.kt (
float) – Termination rate constant.I0 (
float) – Initial initiator and monomer concentrations.M0 (
float) – Initial initiator and monomer concentrations.mode (
str) – Termination mechanism (affects only theDstoichiometry, not the radical/monomer dynamics).
- Return type:
- Returns:
StoichiometricNetwork – Species
("I", "M", "R", "D").
Examples
Radical concentration quickly rises from zero and plateaus near the steady-state approximation’s prediction (see
steady_state_radical_concentration()), while initiator and monomer are consumed only slowly on that timescale:>>> kd, f, kp, kt = 1.0e-5, 0.5, 1.0e3, 1.0e7 >>> I0, M0 = 0.01, 5.0 >>> net = free_radical_network(kd, f, kp, kt, I0, M0) >>> result = net.integrate((0.0, 50.0), dt=1e-2, method="rk4") >>> R_ss = steady_state_radical_concentration(kd, f, I0, kt) >>> bool(np.isclose(result.concentration("R")[-1], R_ss, rtol=0.05)) True >>> bool(result.concentration("I")[-1] > 0.99 * I0) True
- chemistrykit.polymer.freely_jointed_chain(n, b, n_chains=1, rng=None)[source]#
Sample 3D conformations of Kuhn’s freely jointed chain: n bonds of length b in uniformly random directions.
Each bond vector is drawn independently and isotropically on the sphere of radius b (W. Kuhn, Kolloid-Z. 68, 2 (1934)); the chain starts at the origin. Averaged over many samples, the squared end-to-end distance converges to the exact ideal-chain result \(\langle R^2\rangle=nb^2\) (
IdealChain), since the cross terms \(\langle\mathbf{b}_i\cdot\mathbf{b}_j\rangle\) vanish for independent bonds.- Parameters:
n (
int) – Number of bonds (Kuhn segments).b (
float) – Bond (Kuhn) length.n_chains (
int) – Number of independent conformations to sample.rng (int, numpy.random.Generator, or None, optional) – Seed or generator for reproducibility.
- Return type:
- Returns:
ndarray, shape (n_chains, n + 1, 3) – Bead positions of every sampled chain.
Examples
Every bond has exactly length b:
>>> X = freely_jointed_chain(50, 0.5, n_chains=3, rng=0) >>> X.shape (3, 51, 3) >>> bool(np.allclose(np.linalg.norm(np.diff(X, axis=1), axis=-1), 0.5)) True
The sample mean of \(R^2\) approaches \(nb^2=100\cdot1^2\):
>>> X = freely_jointed_chain(100, 1.0, n_chains=20000, rng=1) >>> R2 = np.sum(X[:, -1] ** 2, axis=-1) >>> bool(abs(R2.mean() / 100.0 - 1.0) < 0.03) True
- chemistrykit.polymer.kinetic_chain_length(kd, f, kp, kt, I, M)[source]#
Kinetic chain length \(\nu=R_p/R_i\): monomers consumed per radical generated.
\[\nu = \frac{R_p}{R_i} = \frac{k_p[M]}{2\sqrt{fk_dk_t[I]}}\]directly related to the number-average degree of polymerization of the resulting dead polymer: \(\bar X_n=\nu\) for termination by disproportionation (each radical becomes one dead chain) or \(\bar X_n=2\nu\) for termination by combination (two radicals fuse into one dead chain) – see Odian, Principles of Polymerization, 4th ed., Ch. 3.3.2.
- Parameters:
kd (
float) – Rate constants and initiator efficiency, as infree_radical_network().f (
float) – Rate constants and initiator efficiency, as infree_radical_network().kp (
float) – Rate constants and initiator efficiency, as infree_radical_network().kt (
float) – Rate constants and initiator efficiency, as infree_radical_network().I (float or array-like of float) – Initiator and monomer concentrations.
M (float or array-like of float) – Initiator and monomer concentrations.
- Returns:
float or ndarray
Examples
>>> nu = kinetic_chain_length(kd=1.0e-5, f=0.5, kp=1.0e3, kt=1.0e7, I=0.01, M=5.0) >>> Ri = 2.0 * 0.5 * 1.0e-5 * 0.01 >>> Rp = steady_state_rate_of_polymerization(kd=1.0e-5, f=0.5, kp=1.0e3, kt=1.0e7, I=0.01, M=5.0) >>> round(float(nu), 6) == round(float(Rp / Ri), 6) True
- chemistrykit.polymer.mark_houwink_exponent_from_flory(nu)[source]#
Mark-Houwink exponent implied by a Flory exponent via Flory-Fox, \(a=3\nu-1\).
Examples
>>> mark_houwink_exponent_from_flory(0.5) 0.5 >>> round(mark_houwink_exponent_from_flory(0.6), 6) 0.8
- chemistrykit.polymer.mark_houwink_intrinsic_viscosity(M, K, a)[source]#
Mark-Houwink equation, \([\eta]=KM^a\).
- Parameters:
- Returns:
float or ndarray
Examples
>>> round(float(mark_houwink_intrinsic_viscosity(1e4, K=0.01, a=0.5)), 6) 1.0
- chemistrykit.polymer.mayo_lewis_copolymer_composition(f1, r1, r2)[source]#
Instantaneous copolymer composition \(F_1\) from the Mayo-Lewis equation.
- Parameters:
- Returns:
float or ndarray
Examples
An ideal random copolymerization (\(r_1=r_2=1\)) has \(F_1=f_1\):
>>> float(mayo_lewis_copolymer_composition(0.3, 1.0, 1.0)) 0.3
A perfectly alternating system (\(r_1=r_2=0\)) gives \(F_1=1/2\) at any feed:
>>> float(mayo_lewis_copolymer_composition(0.9, 0.0, 0.0)) 0.5
- chemistrykit.polymer.mean_isotactic_run_length(Pm)[source]#
Mean length of a run of consecutive meso dyads, \(1/(1-P_m)\) (Bernoullian).
Examples
>>> round(mean_isotactic_run_length(0.99), 6) 100.0
- chemistrykit.polymer.number_average_molar_mass(N_i, M_i)[source]#
Number-average molar mass \(M_n=\sum N_iM_i/\sum N_i\).
- Parameters:
- Return type:
- Returns:
float
Examples
>>> number_average_molar_mass(N_i=[10.0, 5.0], M_i=[1000.0, 2000.0]) 1333.3333333333333
- chemistrykit.polymer.osmotic_pressure_dilute_mixture(c_i, M_i, T, R=8.314462618)[source]#
Van ‘t Hoff osmotic pressure of an ideal dilute mixture, \(\Pi=RT\sum c_i/M_i\).
- Parameters:
- Return type:
- Returns:
float – Osmotic pressure (Pa).
Examples
The apparent molar mass \(RTc/\Pi\) is the number average:
>>> Pi = osmotic_pressure_dilute_mixture([1.0, 1.0], [10.0, 100.0], T=300.0) >>> round(8.314462618 * 300.0 * 2.0 / Pi, 6) 18.181818
- chemistrykit.polymer.poisson_number_average_DP(nu)[source]#
Number-average degree of polymerization, \(1+\nu\).
Examples
>>> poisson_number_average_DP(99.0) 100.0
- chemistrykit.polymer.poisson_number_fraction(x, nu)[source]#
Poisson number fraction \(N_x=e^{-\nu}\nu^{x-1}/(x-1)!\) of chains with x units.
- Parameters:
- Returns:
float or ndarray
Examples
>>> import numpy as np >>> round(float(np.sum(poisson_number_fraction(np.arange(1, 400), 50.0))), 10) 1.0
- chemistrykit.polymer.poisson_pdi(nu)[source]#
Polydispersity index of the Poisson distribution, \(1+\nu/(1+\nu)^2\).
Examples
>>> round(poisson_pdi(99.0), 6) 1.0099
- chemistrykit.polymer.poisson_weight_average_DP(nu)[source]#
Weight-average degree of polymerization, \((\nu^2+3\nu+1)/(\nu+1)\).
Examples
>>> round(poisson_weight_average_DP(1.0), 6) 2.5
- chemistrykit.polymer.polydispersity_index(Mn, Mw)[source]#
Polydispersity index \(\text{\dj} = M_w/M_n\).
- Parameters:
- Return type:
- Returns:
float – \(\ge 1\), with 1 for a perfectly monodisperse sample.
Examples
>>> polydispersity_index(Mn=1000.0, Mw=1000.0) 1.0
- chemistrykit.polymer.rayleigh_ratio_dilute_mixture(c_i, M_i, K=1.0)[source]#
Zero-angle excess Rayleigh ratio of an ideal dilute mixture, \(R_0=K\sum c_iM_i\).
- Parameters:
- Return type:
- Returns:
float
Examples
The apparent molar mass \(R_0/(Kc)\) is the weight average:
>>> round(rayleigh_ratio_dilute_mixture([1.0, 1.0], [1e4, 1e5]) / 2.0, 6) 55000.0
- chemistrykit.polymer.sample_dyad_sequence(n_dyads, Pm, rng=None)[source]#
Sample a Bernoullian dyad sequence:
Truefor meso,Falsefor racemo.- Parameters:
n_dyads (
int)Pm (
float) – Meso placement probability.rng (int, numpy.random.Generator, or None, optional)
- Return type:
- Returns:
ndarray of bool, shape (n_dyads,)
Examples
>>> s = sample_dyad_sequence(100000, 0.9, rng=0) >>> bool(abs(s.mean() - 0.9) < 0.01) True
- chemistrykit.polymer.simulate_living_polymerization(n_chains, n_monomers, rng=None)[source]#
Stochastically grow n_chains living chains by adding n_monomers one at a time to random chains.
Each monomer attaches to a uniformly chosen active chain (equal reactivity, no termination), so chain lengths follow a multinomial distribution that tends to the Poisson distribution (
poisson_number_fraction()) with \(\nu=\) n_monomers / n_chains.- Parameters:
n_chains (
int) – Number of initiated chains (each starts with one unit).n_monomers (
int) – Number of monomers added in total.rng (int, numpy.random.Generator, or None, optional) – Seed or generator.
- Return type:
- Returns:
ndarray of int, shape (n_chains,) – Final chain lengths \(x\) (units per chain, including the first).
Examples
>>> x = simulate_living_polymerization(1000, 50000, rng=0) >>> int(x.sum()) 51000
- chemistrykit.polymer.staudinger_specific_viscosity(c, M, Km)[source]#
Staudinger’s viscosity rule, \(\eta_\text{sp}=K_mcM\).
- Parameters:
- Returns:
float or ndarray
Examples
Doubling the chain length doubles the specific viscosity:
>>> float(staudinger_specific_viscosity(0.01, 2e5, 1e-4) / staudinger_specific_viscosity(0.01, 1e5, 1e-4)) 2.0
- chemistrykit.polymer.steady_state_radical_concentration(kd, f, I, kt)[source]#
Steady-state-approximation (SSA) radical concentration \([M^\bullet]_{ss}=\sqrt{fk_d[I]/k_t}\).
Setting the radical production rate \(R_i=2fk_d[I]\) equal to the termination (radical-consumption) rate \(R_t=2k_t[M^\bullet]^2\) and solving for \([M^\bullet]\) gives this result (Odian, Principles of Polymerization, 4th ed., Ch. 3.3.1, eq. 3-21) – valid whenever radical production and consumption equilibrate on a timescale much shorter than the initiator/monomer are consumed (\(k_t[M^\bullet]\gg k_d\), essentially always true in practice since termination is diffusion-controlled while initiator decomposition is not).
- Parameters:
- Returns:
float or ndarray
Examples
>>> round(float(steady_state_radical_concentration(kd=1.0e-5, f=0.5, I=0.01, kt=1.0e7)), 10) 7.07e-08
- chemistrykit.polymer.steady_state_rate_of_polymerization(kd, f, kp, kt, I, M)[source]#
Steady-state rate of polymerization \(R_p=k_p[M]\sqrt{fk_d[I]/k_t}\).
The rate at which monomer is consumed by propagation, evaluated at the steady-state radical concentration (
steady_state_radical_concentration()) – the classic result that \(R_p\) scales as the square root of initiator concentration (Odian, Principles of Polymerization, 4th ed., Ch. 3.3.1, eq. 3-22), a distinctive experimental signature of free-radical (as opposed to, e.g., ionic) chain polymerization.- Parameters:
kd (
float) – Rate constants and initiator efficiency, as infree_radical_network().f (
float) – Rate constants and initiator efficiency, as infree_radical_network().kp (
float) – Rate constants and initiator efficiency, as infree_radical_network().kt (
float) – Rate constants and initiator efficiency, as infree_radical_network().I (float or array-like of float) – Initiator and monomer concentrations.
M (float or array-like of float) – Initiator and monomer concentrations.
- Returns:
float or ndarray
Examples
Doubling the initiator concentration increases the rate by only \(\sqrt2\), not 2 – the square-root dependence:
>>> kd, f, kp, kt, M = 1.0e-5, 0.5, 1.0e3, 1.0e7, 5.0 >>> Rp1 = steady_state_rate_of_polymerization(kd, f, kp, kt, I=0.01, M=M) >>> Rp2 = steady_state_rate_of_polymerization(kd, f, kp, kt, I=0.02, M=M) >>> round(float(Rp2 / Rp1), 6) 1.414214
- chemistrykit.polymer.weight_average_molar_mass(N_i, M_i)[source]#
Weight-average molar mass \(M_w=\sum N_iM_i^2/\sum N_iM_i\).
- Parameters:
N_i (array-like of float) – As in
number_average_molar_mass().M_i (array-like of float) – As in
number_average_molar_mass().
- Return type:
- Returns:
float
Examples
\(M_w \ge M_n\) always (Cauchy-Schwarz), with equality only for a monodisperse sample:
>>> N_i, M_i = [10.0, 5.0], [1000.0, 2000.0] >>> weight_average_molar_mass(N_i, M_i) >= number_average_molar_mass(N_i, M_i) True
- chemistrykit.polymer.worm_like_chain_mean_square_end_to_end(L, P)[source]#
Kratky-Porod worm-like chain mean-square end-to-end distance.
For a semiflexible chain of contour length \(L\) whose tangent correlations decay as \(e^{-s/P}\) along the contour (persistence length \(P\)), Kratky and Porod (Recl. Trav. Chim. Pays-Bas 68, 1106 (1949)) obtained
\[\langle R^2\rangle = 2PL\left[1-\frac{P}{L}\left(1-e^{-L/P}\right)\right]\]which interpolates between a rigid rod, \(\langle R^2\rangle\to L^2\) for \(L\ll P\), and an ideal random coil, \(\langle R^2\rangle\to2PL\) for \(L\gg P\) – i.e. a freely jointed chain with Kuhn length \(b=2P\).
- Parameters:
- Returns:
float or ndarray
Examples
A very short chain is a rigid rod, \(R^2\approx L^2\):
>>> round(float(worm_like_chain_mean_square_end_to_end(1e-3, 50.0) / 1e-6), 4) 1.0
A very long chain is an ideal coil with Kuhn length \(2P\):
>>> round(float(worm_like_chain_mean_square_end_to_end(1e6, 50.0) / (2 * 50.0 * 1e6)), 4) 1.0