25592fa6c2
ci / test (push) Successful in 6m14s
History-free snapshot of the private development repository at 77a82eb. Code, docs and the public catalogue only: no plant or job knowledge layers, no client, job or producer names (tools/scrub.py clean against the widened term list; a second word-level grep clean). The intro deck is left out because its slide images cannot be checked for job drawings.
131 lines
6.6 KiB
Python
131 lines
6.6 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""The reference tool's (Eriksson Wall) §1.3 developed-strand-at-opening rule:
|
|
a strand a blockout SAW-CUT re-develops over the DEVELOPMENT length from that
|
|
edge, while a strand's transfer from the PIECE END is unchanged.
|
|
|
|
docs/eriksson-wall-algorithms.md sec.1.3 / sec.9. ADDITIVE and OPT-IN: it fires
|
|
only when a caller passes `ld_ft` (the handling STRESS path,
|
|
`capacity.handling_stress_at`). `prestress.state_at` with no `ld_ft` -- which is
|
|
what Panel Lift Check's `checks.stress_profile` uses -- keeps the physically
|
|
correct transfer-length force model, so a clean solid panel is byte-for-byte
|
|
what it was (validated against Eriksson to ~1%).
|
|
"""
|
|
import pytest
|
|
|
|
from panellift import capacity, prestress
|
|
from panellift.model import Blockout, Panel, Prestress, Stage, Strand
|
|
|
|
|
|
def _panel_with_one_opening():
|
|
"""20 ft panel, a full-depth opening x = 8..12 across the strand's y band.
|
|
|
|
The single strand at y = 2 is cut by it, so it has two bonded runs,
|
|
[0, 8] and [12, 20]. l_t = 2 ft (stated), and the tests pass l_d = 5 ft.
|
|
"""
|
|
strand = Strand(y=2.0, z_in=1.5, area_in2=0.153, initial_pull_lb=30000.0)
|
|
pre = Prestress(strands=[strand], loss_pct=0.0, diameter_in=0.5,
|
|
fpu_ksi=270.0, transfer_length_in=24.0) # l_t = 2 ft
|
|
panel = Panel(mark='O', length_ft=20.0, width_ft=4.0, thickness_in=8.0,
|
|
unit_weight_pcf=150.0, fci_psi=3500.0, fc_psi=6000.0,
|
|
prestress=pre)
|
|
panel.blockouts = [Blockout(name='O1', x0=8.0, y0=0.0, length=4.0,
|
|
width=4.0)]
|
|
return panel, strand
|
|
|
|
|
|
def test_developed_uses_development_length_from_an_opening_edge():
|
|
"""At x = 6, 2 ft short of the opening edge at x = 8, over l_d = 5:
|
|
fraction = (8 - 6)/5 = 0.4 -- against 1.0 with transfer only.
|
|
The piece-end side (x = 6, l_t = 2) is already full, so the opening edge
|
|
governs.
|
|
"""
|
|
panel, strand = _panel_with_one_opening()
|
|
# transfer only: (8-6)/2 = 1.0 on the opening side -> full
|
|
assert prestress.developed(strand, panel, 6.0, lt_ft=2.0) == pytest.approx(1.0)
|
|
# development length from the opening edge: (8-6)/5 = 0.4
|
|
assert prestress.developed(strand, panel, 6.0, lt_ft=2.0,
|
|
ld_ft=5.0) == pytest.approx(0.4)
|
|
|
|
|
|
def test_developed_keeps_transfer_length_from_the_piece_end():
|
|
"""At x = 1, near the piece end (x = 0), the transfer length still governs
|
|
even when l_d is supplied: (1 - 0)/2 = 0.5, the same both ways. Releasing
|
|
at the bed transfers f_se over l_t; only a saw-cut rebuilds over l_d."""
|
|
panel, strand = _panel_with_one_opening()
|
|
assert prestress.developed(strand, panel, 1.0, lt_ft=2.0) == pytest.approx(0.5)
|
|
assert prestress.developed(strand, panel, 1.0, lt_ft=2.0,
|
|
ld_ft=5.0) == pytest.approx(0.5)
|
|
|
|
|
|
def test_developed_on_the_far_side_of_the_opening():
|
|
"""In the second run [12, 20], the near edge x = 12 is an OPENING edge, so
|
|
at x = 14: (14 - 12)/5 = 0.4 with l_d, 1.0 with transfer only."""
|
|
panel, strand = _panel_with_one_opening()
|
|
assert prestress.developed(strand, panel, 14.0, lt_ft=2.0) == pytest.approx(1.0)
|
|
assert prestress.developed(strand, panel, 14.0, lt_ft=2.0,
|
|
ld_ft=5.0) == pytest.approx(0.4)
|
|
|
|
|
|
def test_state_at_ld_reduces_pi_near_an_opening():
|
|
"""`state_at(..., ld_ft=5)` credits less prestress force approaching an
|
|
opening than the transfer-only default. Pi = 30000 * developed."""
|
|
panel, _ = _panel_with_one_opening()
|
|
s_transfer = prestress.state_at(panel, 6.0)
|
|
s_dev = prestress.state_at(panel, 6.0, ld_ft=5.0)
|
|
assert s_transfer.force_lb == pytest.approx(30000.0) # full
|
|
assert s_dev.force_lb == pytest.approx(30000.0 * 0.4) # 12000
|
|
|
|
|
|
def test_clean_panel_is_untouched_by_the_ld_rule():
|
|
"""A panel with NO openings has only piece-end boundaries, so passing l_d
|
|
changes nothing -- the WPA2.0 clean-panel match is preserved. Checked at
|
|
several stations, near end and midspan alike."""
|
|
strand = Strand(y=2.0, z_in=1.5, area_in2=0.153, initial_pull_lb=30000.0)
|
|
pre = Prestress(strands=[strand], loss_pct=0.0, diameter_in=0.5,
|
|
fpu_ksi=270.0, transfer_length_in=24.0)
|
|
panel = Panel(mark='C', length_ft=20.0, width_ft=4.0, thickness_in=8.0,
|
|
unit_weight_pcf=150.0, fci_psi=3500.0, fc_psi=6000.0,
|
|
prestress=pre) # no blockouts
|
|
for x in (0.5, 1.5, 5.0, 10.0, 18.5, 19.5):
|
|
a = prestress.state_at(panel, x)
|
|
b = prestress.state_at(panel, x, ld_ft=5.0)
|
|
assert a.force_lb == pytest.approx(b.force_lb)
|
|
assert a.cy_in == pytest.approx(b.cy_in)
|
|
|
|
|
|
def test_stress_development_length_is_built_on_the_transfer_length():
|
|
"""The representative l_d handling_stress_at uses is l_t + (f_ps - f_se) d_b
|
|
-- the ACI 25.4.8.1 form on the SAME transfer length the force model uses,
|
|
so it is always at least the transfer length."""
|
|
panel, _ = _panel_with_one_opening()
|
|
stage = Stage(name='strip', multiplier=1.4, fc_psi=3500.0)
|
|
ld = capacity.stress_development_length_ft(panel, stage)
|
|
lt = prestress.transfer_length_in(panel.prestress) / 12.0
|
|
assert ld >= lt
|
|
assert ld == pytest.approx(2.0, abs=1e-9) or ld > 2.0 # l_t was 2.0 ft
|
|
|
|
|
|
def test_handling_stress_solid_panel_unchanged_by_the_rule():
|
|
"""On a solid panel the §1.3 rule is a no-op, so the extreme-fibre stress
|
|
is exactly the transfer-model value -- the guard that the clean-panel
|
|
parity match cannot regress."""
|
|
strand_b = Strand(y=2.0, z_in=1.5, area_in2=0.153, initial_pull_lb=30000.0)
|
|
strand_t = Strand(y=2.0, z_in=6.5, area_in2=0.153, initial_pull_lb=30000.0)
|
|
pre = Prestress(strands=[strand_b, strand_t], loss_pct=0.0, diameter_in=0.5,
|
|
fpu_ksi=270.0)
|
|
panel = Panel(mark='C', length_ft=20.0, width_ft=4.0, thickness_in=8.0,
|
|
unit_weight_pcf=150.0, fci_psi=3500.0, fc_psi=6000.0,
|
|
prestress=pre)
|
|
stage = Stage(name='strip', multiplier=1.4, fc_psi=3500.0,
|
|
tension_limit_sqrt_fc=7.5, compression_limit_ratio=0.6)
|
|
sp = capacity.handling_stress_at(panel, stage, 8.0, 12000.0)
|
|
# rebuild with the plain transfer-model state, no ld gating
|
|
from panellift import section
|
|
net = section.NetSection(panel, 8.0)
|
|
st = prestress.state_at(panel, 8.0) # no ld_ft
|
|
f_form, f_far = capacity.extreme_fibre_stresses(
|
|
st.force_lb, net.area_in2, 0.0, panel.thickness_in / 2.0,
|
|
net.iz_in4, 12000.0)
|
|
assert sp.f_form_psi == pytest.approx(f_form)
|
|
assert sp.f_far_psi == pytest.approx(f_far)
|