PyExtrusion integration guide — 0.19.0¶
New inputs should use trim_total_per_billet_m. The synthetic examples below use that canonical total. Legacy front_scrap_m remains per billet for p=1 and per sequential pull for p=2; migrate explicitly with a verified family. Canonical and non-null legacy inputs cannot be combined. See explicit migration and public event counters.
PyExtrusion exposes one deterministic direct-extrusion engine through Python objects, JSON files and CLI commands. The calculations use the current PyExtrusion engine.
1. Press configuration¶
table_length_m is mandatory. Other common press values can be explicit or inferred from nominal_size_in.
from pyextrusion import Press
press = Press(
name="Line A",
nominal_size_in=9,
billet_diameter_mm=228,
container_diameter_mm=236,
billet_min_length_mm=500,
billet_max_length_mm=1300,
table_length_m=64,
dead_time_sec=14,
target_net_productivity_kg_h=2250,
)
Real plant values always take priority over inferred defaults.
2. Study/cotization case¶
from pyextrusion import Profile, Production, StudyCase
case = StudyCase(
profile=Profile(1.35, exits=2, profile_type="solid"),
production=Production(
exit_speed_m_min=24,
cut_length_mm=7000,
bars_requested=1000,
trim_total_per_billet_m=2,
complexity="normal",
),
)
Calculate with:
Important structured outputs include:
result.status.supported
result.geometry.extrusion_ratio
result.geometry.billets_per_pull
result.billet.recommended_length_mm
result.production.bars_per_billet
result.production.n_pulls
result.production.final_saw_events
result.production.internal_billet_transitions
result.production.puller_saw_events
result.production.billet_saw_events
result.process.trim_total_per_billet_m
result.process.trim_per_pull_m
result.scrap.total_kg
result.productivity.real_net_kg_h
result.productivity.productivity_index
3. Billet-first and multi-billet semantics¶
PyExtrusion selects the longest feasible billet among valid candidates. For each candidate, grouping and physical table occupancy already include its trim and downstream kerfs. Do not reconstruct billets_per_pull by dividing table length by net commercial length alone; use the returned configuration.
It never reduces cuts merely to fit more billets on the table.
The multi-billet family is k_billets_1_profile; k is dynamic and may be greater than 3. The reverse case is deliberately limited: 1_billet_2_profiles is supported, but 3+ profiles per billet are reported as unsupported.
For positive-trim multibillet continuous pulls, each actual billet contribution requires cuts + 1 final-saw events, while the puller event is shared once per pull. A final partial pull uses its actual billet count: internal transitions are N_B - N_pulls, not nominal full-pull capacity. Zero trim retains one end-preparation allowance per actual pull plus the commercial positions. Kerf zero makes the corresponding length/mass loss zero without removing the event topology. Additional downstream kerf changes extrusion time, not a separate saw-operation time or new press dead-time event.
4. Planning API — quantity-free process architecture¶
A planning process should not contain a fictitious order quantity:
from pyextrusion import PlanningCase, Process, Profile
process_case = PlanningCase(
profile=Profile(1.35, exits=2, profile_type="solid"),
process=Process(
exit_speed_m_min=24,
cut_length_mm=7000,
trim_total_per_billet_m=2,
complexity="normal",
),
)
Evaluate the process independently from demand:
from pyextrusion import calculate_process
process = calculate_process(press, process_case)
print(process.billet_length_mm)
print(process.bars_per_billet)
print(process.billets_per_pull)
Then provide the operational request separately:
from pyextrusion import PlanningRequest, calculate_planning
plan = calculate_planning(press, process_case, PlanningRequest.bars(300))
calculate_planning(press, process_case, PlanningRequest.kg(2500))
calculate_planning(press, process_case, PlanningRequest.metres(1800))
calculate_planning(press, process_case, PlanningRequest.billets(20))
calculate_planning(press, process_case, PlanningRequest.hours(2))
For application integration, plan.process is the quantity-free ProcessResult; plan.calculation is the concrete order-level CalculationResult when at least one complete billet is planned.
StudyCase remains accepted by calculate_planning() only as a compatibility bridge and emits a deprecation warning.
5. Production-sequence API¶
For a user-defined list of orders:
from pyextrusion import ProductionOrder, calculate_production_sequence
orders = [
ProductionOrder("OF-001", process_case, PlanningRequest.bars(300)),
ProductionOrder("OF-002", process_case, PlanningRequest.billets(20)),
]
sequence = calculate_production_sequence(press, orders)
The list order is preserved exactly. Sequence time is the sum of the individual planning times; PyExtrusion inserts no setup, die-change, waiting or calendar assumptions. Optional start_at=<datetime> adds theoretical clock timestamps only.
Capacity-window requests (minutes/hours) remain standalone planning queries and are not accepted as production-sequence order quantities.
6. Multi-press comparison¶
The same technical definition can be evaluated independently on two or more presses:
from pyextrusion import compare_processes, compare_planning, compare_production_sequences
process_comparison = compare_processes([press_a, press_b, press_c], process_case)
planning_comparison = compare_planning(
[press_a, press_b, press_c],
process_case,
PlanningRequest.bars(300),
)
sequence_comparison = compare_production_sequences(
[press_a, press_b, press_c],
orders,
)
The press list order is preserved. Every press receives the same process/request, and sequence comparison gives every press the complete same order list in the same order. PyExtrusion does not rank presses, declare a winner, distribute orders or balance workload.
7. Immutable adjustments¶
faster = process_case.with_process(exit_speed_m_min=26)
three_exit = process_case.with_profile(exits=3)
Existing study cases retain:
case.with_production(exit_speed_m_min=26)
case.with_profile(exits=3)
case.replace(exits=3, exit_speed_m_min=22)
8. JSON¶
Schema 1.1 is the current write format. Schemas 0.2 through 1.0 remain readable.
from pyextrusion import (
save_case_json,
save_planning_case_json,
save_press_json,
)
save_press_json(press, "press.json")
save_case_json(case, "case.json")
save_planning_case_json(process_case, "planning_case.json")
Production-sequence JSON can be loaded/saved with:
from pyextrusion import load_production_sequence_json, save_production_sequence_json
save_production_sequence_json(press, orders, "production_sequence.json")
press2, orders2, start_at2 = load_production_sequence_json("production_sequence.json")
A planning-case JSON contains profile + process; no order quantity is required.
9. Model area distinction¶
Do not substitute billet area for container area:
- container bore area → extrusion ratio, ram speed and physical butt mass;
- effective incoming-billet kg/mm → billet length/mass and upstream billet-saw loss.
butt_mm is physical residual thickness in the container. Its mass is converted once to incoming-billet equivalent length. A billet_kg_per_mm_override changes that equivalent, not the butt mass, and does not redefine the geometric upsetting relation.
For canonical trim, total rejection mass is billet count × trim_total_per_billet_m × combined linear weight. Do not multiply the total by sequential pulls again. trim_per_pull_m is the sum for a full pull; partial orders use actual contributions. The process-level billet recommendation remains representative of a complete pull and can require a plant-specific last-billet adjustment.
10. Result discovery and errors¶
Use result.supported to distinguish an unsupported model scenario from a physically non-viable supported scenario.
11. Engineering and economics¶
The production/planning engine remains separate from the analytical
pyextrusion.engineering and deterministic pyextrusion.economics
subpackages. Use the dedicated Engineering API and
Economics API references for those interfaces and their model
boundaries.