Laminate Hygrothermal Response (structures/composites/laminate-hygrothermal-response)
Use when the task is the hygrothermal response of a composite laminate:
equilibrium moisture content from the ambient relative humidity,
stiffness-weighted laminate thermal and moisture expansion coefficients
by classical lamination theory (CLT), the hygrothermal strain from a
temperature change plus a moisture change, and the residual strain from
the cure-cooldown temperature drop. This leaf implements the exact CLT
free-expansion solution in pure Python, stdlib only, deterministic and
offline. Material properties (ply stiffness, ply expansion
coefficients, saturation moisture content) are inputs with documented
typical bounds; the code path is exact CLT arithmetic with no empirical
fits. It pairs with structures/composites/laminate-stiffness, whose
Qbar assembly this leaf reuses for the thermal and moisture weighting.
Domain quick reference
- Linear isotherm: equilibrium moisture content M = m_sat * rh, where
m_sat is the saturation mass fraction (default 0.015, published CFRP
magnitudes 0.01-0.02) and rh the ambient relative humidity fraction.
- Plane-stress reduced stiffness: q11 = e1/d, q22 = e2/d,
q12 = nu12e2/d = nu21e1/d, q66 = g12, with nu21 = nu12e2/e1 and
d = 1 - nu12nu21.
- Qbar rotation with m = cos(theta), n = sin(theta):
qbar11 = q11m^4 + 2(q12 + 2q66)m^2n^2 + q22n^4,
qbar22 = q11n^4 + 2(q12 + 2q66)m^2n^2 + q22m^4,
qbar12 = (q11 + q22 - 4q66)m^2n^2 + q12(m^4 + n^4),
qbar66 = (q11 + q22 - 2q12 - 2q66)m^2n^2 + q66*(m^4 + n^4),
qbar16 = (q11 - q12 - 2q66)m^3n + (q12 - q22 + 2q66)mn^3,
qbar26 = (q11 - q12 - 2q66)mn^3 + (q12 - q22 + 2q66)m^3n.
- Free-expansion CLT for a symmetric balanced laminate: assemble the 2x2
in-plane stiffness A = sum_k Qbar_k * t_k (rows qbar11, qbar12;
qbar12, qbar22; shear row dropped) and the thermal force resultant per
unit temperature Nth = sum_k Qbar_k * [alpha_x_k, alpha_y_k]^T * t_k,
then solve A * [alpha_x, alpha_y]^T = Nth by the 2x2 determinant
inversion. The moisture vector uses [beta_x_k, beta_y_k] the same way
with the moisture force resultant Nm.
- The exact 2x2 inversion is REQUIRED: the simplified stiffness-weighted
scalar ratio (Nth_x/A11 alone) returns 1.65 ppm for the worked
[0/90]s, a wrong value, and fails the unidirectional identity. Only
the full inversion returns alpha_1 exactly for a 0-deg unidirectional
laminate.
- Hygrothermal strain: eps = alphadelta_t + betadelta_m per laminate
axis. Cure-cooldown strain: alpha_x * (t_rt - t_cure), negative for a
cooldown with positive alpha.
- Coefficients are raw SI in the module (alpha in 1/K, beta per unit
moisture mass fraction); cte_ppm(alpha) = alpha * 1e6 is the
reporting helper.
- Transport aeroplane structural context sits in FAR-25 (reference only;
the relations above are standard mechanics, summary-only).
Workflow
- Gather the ambient relative humidity and the ply material properties:
e1, e2 (Pa), nu12, g12 (Pa), alpha_1 and alpha_2 (1/K), beta_1 and
beta_2 (per unit moisture fraction), ply angle theta_deg and ply
thickness t (m). Use equilibrium_moisture_content for M at the
given rh with m_sat (default 0.015).
- Build the plane-stress stiffness of one ply with plane_stress_q and
rotate it to the laminate axes with qbar.
- Assemble the symmetric balanced laminate and solve for the laminate
coefficients with laminate_cte_cme, which returns alpha_x, alpha_y,
beta_x and beta_y by the exact 2x2 CLT inversion.
- Apply the temperature and moisture changes with hygrothermal_strain,
or report the residual cure strain with cure_cooldown_strain from
the cure temperature and room temperature (21 C default).
- For a one-call assessment run laminate_hygrothermal_response with
the ply list, rh_fraction and delta_t_k; delta_m defaults to the
equilibrium moisture content and t_cure_c adds the cure branch.
- Report coefficients with cte_ppm where parts per million read better
than raw 1/K, then confirm the deterministic checks with the
contract test.
Worked example
T300/5208-style carbon/epoxy [0/90]s symmetric balanced laminate, ply
t = 0.125e-3 m each: e1 = 181e9 Pa, e2 = 10.3e9 Pa, nu12 = 0.28,
g12 = 7.17e9 Pa, alpha_1 = -0.3e-6/K, alpha_2 = 28.1e-6/K, beta_1 = 0,
beta_2 = 0.6 per unit moisture fraction. Ambient rh = 0.6, m_sat =
0.015, cure 177 C, room 21 C. Real module outputs:
- equilibrium_moisture_content(0.6) = 0.009 (0.015 * 0.6).
- plane_stress_q: q11 = 181.81 GPa, q22 = 10.35 GPa, q12 = 2.90 GPa,
q66 = 7.17 GPa.
- laminate_cte_cme: alpha_x = alpha_y = 1.59998e-6/K (1.60 ppm, inside
the 1.55-1.70 ppm band); beta_x = beta_y = 0.04014 per unit moisture
fraction (the CLT moisture solution for beta_1 = 0, beta_2 = 0.6).
The simplified q11-only scalar ratio gives 1.65 ppm, outside the
exact CLT result, and must not be used.
- Cure-cooldown strain: alpha_x * (21 - 177) = -2.496e-4 (about
-2.50e-4) after the -156 K cooldown.
- Moisture branch: hygrothermal_strain at delta_m = 0.009 gives
beta_x * 0.009 = 3.613e-4 (about 3.6e-4); published CFRP swelling
strain at saturation is roughly 0.1-0.7% when beta_2 ~ 0.1-0.6 and
m_sat ~ 1-2%.
- Combined hygrothermal strain at delta_t = -156 K and delta_m = 0.009:
alpha_x * delta_t + beta_x * delta_m = -2.496e-4 + 3.613e-4 =
+1.117e-4. In this laminate the moisture swelling dominates the
cooldown contraction.
- Identity check: a 0-deg unidirectional laminate of the same ply
returns alpha_x = alpha_1 = -0.3e-6/K exactly (float-precision
difference 0.0), which only the full 2x2 inversion achieves.
Pitfalls
- Using the simplified scalar ratio instead of the exact inversion:
the q11-only stiffness-weighted average returns 1.65 ppm for the
worked [0/90]s where the exact 2x2 CLT inversion gives 1.60 ppm,
and only the full inversion returns alpha_1 exactly for a 0-deg
unidirectional laminate.
- Confusing the moisture and thermal branches: the hygrothermal
strain is alpha * delta_t + beta * delta_m per axis, and in the
worked laminate the moisture swelling (+3.6e-4) dominates the cure
cooldown contraction (-2.5e-4); summing them with the wrong sign
misses the net +1.12e-4.
- Assuming every laminate is 0-deg: the ply angles enter through the
Qbar rotation, so a 45-deg unidirectional ply must return the
rotated material coefficient; an angle outside [-90, 90] raises
ValueError.
- Quoting raw SI where ppm reads better: cte_ppm(alpha) = alpha *
1e6 is the reporting helper, and mixing 1/K values with ppm values
in one comparison misreads the coefficient by 1e6.
- Forgetting the moisture content endpoint domain: the isotherm is
M = m_sat * rh with rh in [0, 1] and m_sat > 0; rh outside the
unit interval or a non-positive saturation raises ValueError.
- Feeding non-physical ply properties: non-positive moduli,
nu12*nu21 >= 1, empty ply lists, and non-positive thickness all
raise ValueError; the code path is exact CLT arithmetic, so the
material inputs carry the physics.
Verification
- Confirm laminate_cte_cme on the worked [0/90]s returns alpha_x
inside 1.55-1.70e-6/K and beta_x inside 0.035-0.045.
- Confirm a 0-deg unidirectional layup returns alpha_1 and beta_1 to
float precision, and that a 45-deg unidirectional ply returns the
rotated material coefficient.
- Confirm equilibrium_moisture_content maps rh = 0 to 0 and rh = 1 to
m_sat, and rejects rh outside [0, 1] and m_sat <= 0 with ValueError.
- Confirm the 1D stiffness-weighted average formula matches a direct
ply-by-ply sum for the [0/90]s at about 1.23e-6/K (inside 0-5 ppm),
distinct from the exact 1.60 ppm inversion.
- Confirm the cure-strain sign: cooling (delta_t < 0) with positive
alpha gives a negative strain.
- Confirm plane_stress_q, qbar and laminate_cte_cme reject non-physical
inputs (non-positive moduli, nu12*nu21 >= 1, empty ply list,
non-positive thickness, angle outside [-90, 90]) with ValueError.
- Confirm determinism: the module has no RNG and returns identical
floats run to run.
- Run the contract test offline: python3
scripts/test_laminate_hygrothermal_response.py (32 tests,
deterministic, exits 0).
Related leaves
- structures/composites/laminate-stiffness: the mechanical CLT sibling
whose Qbar assembly this leaf reuses for the thermal and moisture
weighting; it owns the elastic A matrix, this leaf owns expansion.
- structures/composites/cmh17-allowables: hot/wet statistical strength
knockdown factors that consume the moisture content this leaf
computes; this leaf computes expansion strain, not strength.
- structures/thermal-structures/thermal-stress-analysis: isotropic
constrained-member alpha*dT stress and bimetallic strips, the
homogeneous-material counterpart of this laminate expansion leaf.
- structures/composites/sandwich-panels: honeycomb core selection
context where core moisture is a caveat, separate from laminate
hygrothermal strain.
Behavior contract (gate 3)
Run the deterministic contract test (stdlib unittest, offline):
python3 scripts/test_laminate_hygrothermal_response.py
The test covers the worked [0/90]s example (q11 ~ 181.8 GPa, q22 ~
10.35 GPa, alpha_x ~ 1.60e-6/K, beta_x ~ 0.040, equilibrium moisture
0.009, cure strain ~ -2.50e-4, moisture strain ~ 3.6e-4), the exact 2x2
inversion identity for a 0-deg unidirectional laminate, Qbar rotation
invariants and coupling-term symmetry, equilibrium moisture isotherm
endpoints, cure-cooldown sign, ValueError rejection of non-physical
inputs, determinism, and the one-call convenience dict keys and values.
Compliance
- Standards referenced, not reproduced: FAR-25 is US government work
(public domain); the CLT hygrothermal relations above are standard
mechanics methodology, summary-only per standards-map.yaml.
- compliance: STANDARDS-REF, gated: false.
1---2name: laminate-hygrothermal-response3description: Use when you must compute the hygrothermal response of a composite laminate: equilibrium moisture content from relative humidity with a linear isotherm, stiffness-weighted laminate CTE and CME assembled by classical lamination theory from ply-level properties, hygrothermal laminate strain from temperature and moisture changes, and residual strain from the cure-cooldown drop. Produces the moisture content, laminate CTE and CME in raw SI with a parts-per-million helper, and the hygrothermal and cure-cooldown strains that gate a laminate hygrothermal assessment. Trigger: hygrothermal response, laminate cte, laminate cme, moisture swelling, equilibrium moisture content, hygral strain, cure cooldown strain, laminate moisture content.4license: Apache-2.05---67# Laminate Hygrothermal Response (structures/composites/laminate-hygrothermal-response)89Use when the task is the hygrothermal response of a composite laminate:10equilibrium moisture content from the ambient relative humidity,11stiffness-weighted laminate thermal and moisture expansion coefficients12by classical lamination theory (CLT), the hygrothermal strain from a13temperature change plus a moisture change, and the residual strain from14the cure-cooldown temperature drop. This leaf implements the exact CLT15free-expansion solution in pure Python, stdlib only, deterministic and16offline. Material properties (ply stiffness, ply expansion17coefficients, saturation moisture content) are inputs with documented18typical bounds; the code path is exact CLT arithmetic with no empirical19fits. It pairs with structures/composites/laminate-stiffness, whose20Qbar assembly this leaf reuses for the thermal and moisture weighting.2122## Domain quick reference2324- Linear isotherm: equilibrium moisture content M = m_sat * rh, where25 m_sat is the saturation mass fraction (default 0.015, published CFRP26 magnitudes 0.01-0.02) and rh the ambient relative humidity fraction.27- Plane-stress reduced stiffness: q11 = e1/d, q22 = e2/d,28 q12 = nu12*e2/d = nu21*e1/d, q66 = g12, with nu21 = nu12*e2/e1 and29 d = 1 - nu12*nu21.30- Qbar rotation with m = cos(theta), n = sin(theta):31 qbar11 = q11*m^4 + 2*(q12 + 2*q66)*m^2*n^2 + q22*n^4,32 qbar22 = q11*n^4 + 2*(q12 + 2*q66)*m^2*n^2 + q22*m^4,33 qbar12 = (q11 + q22 - 4*q66)*m^2*n^2 + q12*(m^4 + n^4),34 qbar66 = (q11 + q22 - 2*q12 - 2*q66)*m^2*n^2 + q66*(m^4 + n^4),35 qbar16 = (q11 - q12 - 2*q66)*m^3*n + (q12 - q22 + 2*q66)*m*n^3,36 qbar26 = (q11 - q12 - 2*q66)*m*n^3 + (q12 - q22 + 2*q66)*m^3*n.37- Free-expansion CLT for a symmetric balanced laminate: assemble the 2x238 in-plane stiffness A = sum_k Qbar_k * t_k (rows qbar11, qbar12;39 qbar12, qbar22; shear row dropped) and the thermal force resultant per40 unit temperature Nth = sum_k Qbar_k * [alpha_x_k, alpha_y_k]^T * t_k,41 then solve A * [alpha_x, alpha_y]^T = Nth by the 2x2 determinant42 inversion. The moisture vector uses [beta_x_k, beta_y_k] the same way43 with the moisture force resultant Nm.44- The exact 2x2 inversion is REQUIRED: the simplified stiffness-weighted45 scalar ratio (Nth_x/A11 alone) returns 1.65 ppm for the worked46 [0/90]s, a wrong value, and fails the unidirectional identity. Only47 the full inversion returns alpha_1 exactly for a 0-deg unidirectional48 laminate.49- Hygrothermal strain: eps = alpha*delta_t + beta*delta_m per laminate50 axis. Cure-cooldown strain: alpha_x * (t_rt - t_cure), negative for a51 cooldown with positive alpha.52- Coefficients are raw SI in the module (alpha in 1/K, beta per unit53 moisture mass fraction); cte_ppm(alpha) = alpha * 1e6 is the54 reporting helper.55- Transport aeroplane structural context sits in FAR-25 (reference only;56 the relations above are standard mechanics, summary-only).5758## Workflow59601. Gather the ambient relative humidity and the ply material properties:61 e1, e2 (Pa), nu12, g12 (Pa), alpha_1 and alpha_2 (1/K), beta_1 and62 beta_2 (per unit moisture fraction), ply angle theta_deg and ply63 thickness t (m). Use equilibrium_moisture_content for M at the64 given rh with m_sat (default 0.015).652. Build the plane-stress stiffness of one ply with plane_stress_q and66 rotate it to the laminate axes with qbar.673. Assemble the symmetric balanced laminate and solve for the laminate68 coefficients with laminate_cte_cme, which returns alpha_x, alpha_y,69 beta_x and beta_y by the exact 2x2 CLT inversion.704. Apply the temperature and moisture changes with hygrothermal_strain,71 or report the residual cure strain with cure_cooldown_strain from72 the cure temperature and room temperature (21 C default).735. For a one-call assessment run laminate_hygrothermal_response with74 the ply list, rh_fraction and delta_t_k; delta_m defaults to the75 equilibrium moisture content and t_cure_c adds the cure branch.766. Report coefficients with cte_ppm where parts per million read better77 than raw 1/K, then confirm the deterministic checks with the78 contract test.7980## Worked example8182T300/5208-style carbon/epoxy [0/90]s symmetric balanced laminate, ply83t = 0.125e-3 m each: e1 = 181e9 Pa, e2 = 10.3e9 Pa, nu12 = 0.28,84g12 = 7.17e9 Pa, alpha_1 = -0.3e-6/K, alpha_2 = 28.1e-6/K, beta_1 = 0,85beta_2 = 0.6 per unit moisture fraction. Ambient rh = 0.6, m_sat =860.015, cure 177 C, room 21 C. Real module outputs:8788- equilibrium_moisture_content(0.6) = 0.009 (0.015 * 0.6).89- plane_stress_q: q11 = 181.81 GPa, q22 = 10.35 GPa, q12 = 2.90 GPa,90 q66 = 7.17 GPa.91- laminate_cte_cme: alpha_x = alpha_y = 1.59998e-6/K (1.60 ppm, inside92 the 1.55-1.70 ppm band); beta_x = beta_y = 0.04014 per unit moisture93 fraction (the CLT moisture solution for beta_1 = 0, beta_2 = 0.6).94 The simplified q11-only scalar ratio gives 1.65 ppm, outside the95 exact CLT result, and must not be used.96- Cure-cooldown strain: alpha_x * (21 - 177) = -2.496e-4 (about97 -2.50e-4) after the -156 K cooldown.98- Moisture branch: hygrothermal_strain at delta_m = 0.009 gives99 beta_x * 0.009 = 3.613e-4 (about 3.6e-4); published CFRP swelling100 strain at saturation is roughly 0.1-0.7% when beta_2 ~ 0.1-0.6 and101 m_sat ~ 1-2%.102- Combined hygrothermal strain at delta_t = -156 K and delta_m = 0.009:103 alpha_x * delta_t + beta_x * delta_m = -2.496e-4 + 3.613e-4 =104 +1.117e-4. In this laminate the moisture swelling dominates the105 cooldown contraction.106- Identity check: a 0-deg unidirectional laminate of the same ply107 returns alpha_x = alpha_1 = -0.3e-6/K exactly (float-precision108 difference 0.0), which only the full 2x2 inversion achieves.109110111## Pitfalls112113- Using the simplified scalar ratio instead of the exact inversion:114 the q11-only stiffness-weighted average returns 1.65 ppm for the115 worked [0/90]s where the exact 2x2 CLT inversion gives 1.60 ppm,116 and only the full inversion returns alpha_1 exactly for a 0-deg117 unidirectional laminate.118- Confusing the moisture and thermal branches: the hygrothermal119 strain is alpha * delta_t + beta * delta_m per axis, and in the120 worked laminate the moisture swelling (+3.6e-4) dominates the cure121 cooldown contraction (-2.5e-4); summing them with the wrong sign122 misses the net +1.12e-4.123- Assuming every laminate is 0-deg: the ply angles enter through the124 Qbar rotation, so a 45-deg unidirectional ply must return the125 rotated material coefficient; an angle outside [-90, 90] raises126 ValueError.127- Quoting raw SI where ppm reads better: cte_ppm(alpha) = alpha *128 1e6 is the reporting helper, and mixing 1/K values with ppm values129 in one comparison misreads the coefficient by 1e6.130- Forgetting the moisture content endpoint domain: the isotherm is131 M = m_sat * rh with rh in [0, 1] and m_sat > 0; rh outside the132 unit interval or a non-positive saturation raises ValueError.133- Feeding non-physical ply properties: non-positive moduli,134 nu12*nu21 >= 1, empty ply lists, and non-positive thickness all135 raise ValueError; the code path is exact CLT arithmetic, so the136 material inputs carry the physics.137## Verification138139- Confirm laminate_cte_cme on the worked [0/90]s returns alpha_x140 inside 1.55-1.70e-6/K and beta_x inside 0.035-0.045.141- Confirm a 0-deg unidirectional layup returns alpha_1 and beta_1 to142 float precision, and that a 45-deg unidirectional ply returns the143 rotated material coefficient.144- Confirm equilibrium_moisture_content maps rh = 0 to 0 and rh = 1 to145 m_sat, and rejects rh outside [0, 1] and m_sat <= 0 with ValueError.146- Confirm the 1D stiffness-weighted average formula matches a direct147 ply-by-ply sum for the [0/90]s at about 1.23e-6/K (inside 0-5 ppm),148 distinct from the exact 1.60 ppm inversion.149- Confirm the cure-strain sign: cooling (delta_t < 0) with positive150 alpha gives a negative strain.151- Confirm plane_stress_q, qbar and laminate_cte_cme reject non-physical152 inputs (non-positive moduli, nu12*nu21 >= 1, empty ply list,153 non-positive thickness, angle outside [-90, 90]) with ValueError.154- Confirm determinism: the module has no RNG and returns identical155 floats run to run.156- Run the contract test offline: python3157 scripts/test_laminate_hygrothermal_response.py (32 tests,158 deterministic, exits 0).159160## Related leaves161162- structures/composites/laminate-stiffness: the mechanical CLT sibling163 whose Qbar assembly this leaf reuses for the thermal and moisture164 weighting; it owns the elastic A matrix, this leaf owns expansion.165- structures/composites/cmh17-allowables: hot/wet statistical strength166 knockdown factors that consume the moisture content this leaf167 computes; this leaf computes expansion strain, not strength.168- structures/thermal-structures/thermal-stress-analysis: isotropic169 constrained-member alpha*dT stress and bimetallic strips, the170 homogeneous-material counterpart of this laminate expansion leaf.171- structures/composites/sandwich-panels: honeycomb core selection172 context where core moisture is a caveat, separate from laminate173 hygrothermal strain.174175## Behavior contract (gate 3)176177Run the deterministic contract test (stdlib unittest, offline):178179 python3 scripts/test_laminate_hygrothermal_response.py180181The test covers the worked [0/90]s example (q11 ~ 181.8 GPa, q22 ~18210.35 GPa, alpha_x ~ 1.60e-6/K, beta_x ~ 0.040, equilibrium moisture1830.009, cure strain ~ -2.50e-4, moisture strain ~ 3.6e-4), the exact 2x2184inversion identity for a 0-deg unidirectional laminate, Qbar rotation185invariants and coupling-term symmetry, equilibrium moisture isotherm186endpoints, cure-cooldown sign, ValueError rejection of non-physical187inputs, determinism, and the one-call convenience dict keys and values.188189## Compliance190191- Standards referenced, not reproduced: FAR-25 is US government work192 (public domain); the CLT hygrothermal relations above are standard193 mechanics methodology, summary-only per standards-map.yaml.194- compliance: STANDARDS-REF, gated: false.