WillTools 2026-09-09 -- clean snapshot of the development repo for review
ci / test (push) Successful in 6m14s
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.
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""The handling compression limit, from the window down to the job file.
|
||||
|
||||
`handling.compression_limit_ratio` multiplies f'c (f'ci at stripping) to give
|
||||
the compression allowable in psi. Unlike the tensile limit next to it, there
|
||||
is no second school of thought to weigh -- the owner closed the question,
|
||||
2026-09-01:
|
||||
|
||||
"0.6保持可调" ("0.6 stays adjustable")
|
||||
|
||||
-- so 0.6 remains the shipped default (ACI 318-19 Table 24.5.3.1 / Sec.
|
||||
24.5.4.1) and becomes a per-job field the same way `tension_limit_sqrt_fc`
|
||||
already is, in the same Project Config module, following the exact same
|
||||
plumbing: `configmodel.SETTINGS` -> `configform.MODULE_OF` -> `liftexport`
|
||||
writes it onto every stage -> panellift reads it into `Stage`.
|
||||
|
||||
Nothing here implements the higher 0.70*f'ci ACI permits at member ends --
|
||||
the owner ruled that out the same night ("先不用实现0.7...别搞这么复杂") --
|
||||
so there is exactly one ratio, and this file never tests a location split.
|
||||
|
||||
Mirrors `test_stress_limit_setting.py` structurally; see that file's own
|
||||
docstring for why each half is tested where it is.
|
||||
"""
|
||||
from vibe import configform, configmodel, knowledge, liftexport
|
||||
|
||||
from test_liftexport import FACE_AND_EDGE_LIFTERS, make_job, make_know
|
||||
|
||||
|
||||
KEY = 'handling.compression_limit_ratio'
|
||||
|
||||
|
||||
# ------------------------------------------------------- it is in the window
|
||||
|
||||
def test_the_compression_limit_is_a_field_in_lifting_and_handling():
|
||||
assert configform.MODULE_OF[KEY] == configform.LIFTING
|
||||
assert KEY not in configform.NOT_SHOWN
|
||||
|
||||
|
||||
def test_it_is_a_box_you_type_a_number_into():
|
||||
field = [f for f in configform.settings_fields() if f['key'] == KEY][0]
|
||||
assert field['kind'] == 'number'
|
||||
ok, value, _why = configform.parse(field, '0.7')
|
||||
assert ok and value == 0.7
|
||||
|
||||
|
||||
def test_the_row_note_names_the_default_and_the_citation():
|
||||
"""A box that says only 'Compression limit' asks a person to trust a
|
||||
number with no way to check it. The note carries what it multiplies,
|
||||
the default, and the ACI table it comes from."""
|
||||
field = [f for f in configform.settings_fields() if f['key'] == KEY][0]
|
||||
note = field['note']
|
||||
assert '0.6' in note
|
||||
assert "f'c" in note
|
||||
assert '24.5' in note # the ACI section, either citation
|
||||
|
||||
|
||||
def test_the_note_says_the_higher_member_end_value_is_not_implemented():
|
||||
"""The owner's ruling against implementing ACI's 0.70*f'ci at member
|
||||
ends has to be visible at the control, not only in a comment nobody
|
||||
editing the job sees."""
|
||||
field = [f for f in configform.settings_fields() if f['key'] == KEY][0]
|
||||
assert '0.70' in field['note']
|
||||
assert 'conservative' in field['note'] or 'flat' in field['note']
|
||||
|
||||
|
||||
def test_the_full_view_says_where_it_is_edited_now():
|
||||
assert configform.placement()[KEY] == 'Lifting and handling'
|
||||
|
||||
|
||||
def test_nothing_fell_through_the_move():
|
||||
"""The guard that makes the not-shown pool honest: every editable
|
||||
setting is in a module or has a written reason for not being. Failing
|
||||
this here would mean the new field landed with no home."""
|
||||
assert configform.unmapped() == []
|
||||
|
||||
|
||||
# ------------------------------------------------------------ what it refuses
|
||||
|
||||
def test_a_zero_or_negative_ratio_is_refused():
|
||||
"""The exact mirror of the tensile limit's own refusal: a zero
|
||||
allowable fails every piece in compression at any stress, and a
|
||||
negative one inverts the comparison so every piece passes."""
|
||||
assert configmodel.validate(KEY, 0)
|
||||
assert configmodel.validate(KEY, -0.6)
|
||||
assert 'greater than zero' in ' '.join(configmodel.validate(KEY, 0))
|
||||
|
||||
|
||||
def test_a_garbage_value_is_refused_not_silently_reverted():
|
||||
assert configmodel.validate(KEY, 'static_lift')
|
||||
assert configmodel.validate(KEY, None) == [] # blank means "inherit"
|
||||
|
||||
|
||||
def test_ordinary_values_are_accepted_without_argument():
|
||||
"""0.6 is the shipped default, not a doctrine -- the validator does not
|
||||
get to refuse a job that wants a different positive ratio."""
|
||||
assert configmodel.validate(KEY, 0.6) == []
|
||||
assert configmodel.validate(KEY, 0.5) == []
|
||||
assert configmodel.validate(KEY, 0.7) == []
|
||||
|
||||
|
||||
# ------------------------------------------------- it reaches the calculation
|
||||
|
||||
def test_the_default_is_still_point_six():
|
||||
"""Byte-identity when nobody sets it. Every job configured today has no
|
||||
override, and this is the number they have all been running."""
|
||||
assert knowledge.get(knowledge.DEFAULTS, KEY) == 0.6
|
||||
|
||||
|
||||
def test_the_job_layer_value_lands_on_every_stage_of_the_job_file():
|
||||
know = make_know()
|
||||
know['handling']['compression_limit_ratio'] = 0.7
|
||||
stages, _rows, _problems = liftexport.default_stages(
|
||||
make_job(FACE_AND_EDGE_LIFTERS), know)
|
||||
assert stages
|
||||
assert all(s['compression_limit_ratio'] == 0.7 for s in stages)
|
||||
|
||||
|
||||
def test_an_unset_ratio_writes_the_point_six_it_always_wrote():
|
||||
stages, _rows, _problems = liftexport.default_stages(
|
||||
make_job(FACE_AND_EDGE_LIFTERS), make_know())
|
||||
assert all(s['compression_limit_ratio'] == 0.6 for s in stages)
|
||||
assert all(s['compression_limit_source'] == 'job' for s in stages)
|
||||
|
||||
|
||||
def test_a_stage_that_states_its_own_ratio_is_marked_as_the_override():
|
||||
know = make_know()
|
||||
know['handling']['compression_limit_ratio'] = 0.6
|
||||
know['handling']['stages'][0]['compression_limit_ratio'] = 0.7
|
||||
stages, _rows, _problems = liftexport.default_stages(
|
||||
make_job(FACE_AND_EDGE_LIFTERS), know)
|
||||
assert stages[0]['compression_limit_ratio'] == 0.7
|
||||
assert stages[0]['compression_limit_source'] == 'stage'
|
||||
assert stages[1]['compression_limit_ratio'] == 0.6
|
||||
assert stages[1]['compression_limit_source'] == 'job'
|
||||
|
||||
|
||||
def test_the_provenance_words_are_the_ones_panellift_reads():
|
||||
"""Written on one side, refused on the other if they disagree -- see
|
||||
`panellift.model.COMPRESSION_LIMIT_SOURCES`."""
|
||||
stages, _rows, _problems = liftexport.default_stages(
|
||||
make_job(FACE_AND_EDGE_LIFTERS), make_know())
|
||||
assert set(s['compression_limit_source'] for s in stages) <= \
|
||||
set(['job', 'stage', 'default'])
|
||||
|
||||
|
||||
def test_tension_and_compression_are_independent_fields():
|
||||
"""Setting one must never move the other -- they are two separate
|
||||
dotted keys, two separate stage entries, and this is the regression a
|
||||
copy-paste of the tension plumbing could introduce silently."""
|
||||
know = make_know()
|
||||
know['handling']['tension_limit_sqrt_fc'] = 7.5
|
||||
know['handling']['compression_limit_ratio'] = 0.7
|
||||
stages, _rows, _problems = liftexport.default_stages(
|
||||
make_job(FACE_AND_EDGE_LIFTERS), know)
|
||||
assert all(s['tension_limit_sqrt_fc'] == 7.5 for s in stages)
|
||||
assert all(s['compression_limit_ratio'] == 0.7 for s in stages)
|
||||
Reference in New Issue
Block a user