Patchworks Export Contract
femic export patchworks writes two artifacts:
forestmodel.xmlfragments/fragments.shp(plus shapefile sidecars)
Terminology:
A fragment shapefile row is one stand-fragment record.
In this exporter,
BLOCKis one-to-one with fragment rows (one fragment per block id).
Patchworks semantics guardrail
Before reading the rest of this page, keep this separation fixed:
managed/unmanagedmeans Patchworks treatment eligibility;natural/treated(or equivalent origin labels) means curve provenance; andretention is an orthogonal area-reallocation surface.
Do not use curve-family availability, hasfg, or any first-growth versus
plantation shortcut as a proxy for IFM. If a model needs both IFM and origin,
it should publish both explicitly. The compact repo-wide statement of this
contract lives at Patchworks Model Semantics.
ForestModel XML requirements
The exporter now enforces these required structure elements before writing XML:
Root tag:
ForestModelRoot attributes:
horizon,year,match<input>node with attributes:block,area,age<output>node<define field="AU" column="AU"><define field="IFM" column="IFM"><define field="ORIGIN" column="ORIGIN"><define field="RETENTION" column="Number(column('RETENTION'))"><define field="treatment">A
unitycurve with at least one pointAt least one
<treatment label="CC" ...>Every
<attribute><curve idref="...">must reference an existing<curve id="...">
Species-wise yield curves
For each AU/IFM species proportion curve, FEMIC now also emits a derived species-yield curve:
unmanaged:
feature.Yield.unmanaged.<SPP>managed:
feature.Yield.managed.<SPP>andproduct.Yield.managed.<SPP>
For CC treatment consequences, managed product attributes now also include:
total harvested volume:
product.HarvestedVolume.managed.Total.CCspecies harvested volume:
product.HarvestedVolume.managed.<SPP>.CC
Derived species-yield points are computed as:
TotalVolume(age) * SpeciesProportion(age)
where species proportions are evaluated at each total-curve age using constant or piecewise-linear interpolation of the source species-proportion curve.
To reduce XML size/noise, serializer output trims redundant far-left and far-right points when a curve starts/ends with repeated y-values; Patchworks extends terminal points horizontally by default.
Curve IDs are emitted as readable tokens (for example
managed_total_<au_token>_<id>, managed_prop_<SPP>_<au_token>_<id>,
au_<au_token>_managed_yield_<SPP>) while remaining unique within the XML
file.
In these readable surfaces, <au_token> means the deterministic
Patchworks-safe AU token derived from stratum_code + si_level (for
example CWHvm_HW_FDC_H). Operator characters such as + and - are
sanitized because Patchworks parses account labels as expressions rather than
free-text strings. When the same readable AU token would otherwise collide
across FMU/code targets, FEMIC prefixes the case code to keep the label
unique. The underlying compatibility field/normalizer still uses legacy
tsa naming in parts of the runtime.
CC treatment minimum age is now resolved per AU as:
CMAI(managed_total_curve) - 20
where CMAI is the age with maximum mean annual increment
(managed_volume(age) / age) on the managed total-yield curve. The result is
clamped to [0, --cc-max-age].
Seral-stage attributes (optional)
When --seral-stage-config is provided, the exporter emits per-AU binary
seral curves and binds these attributes:
feature.Seral.regeneratingfeature.Seral.youngfeature.Seral.immaturefeature.Seral.maturefeature.Seral.overmaturefeature.Seral.<au_token>.regeneratingfeature.Seral.<au_token>.youngfeature.Seral.<au_token>.immaturefeature.Seral.<au_token>.maturefeature.Seral.<au_token>.overmatureCC-treatment consequence area accounts by stage/AU:
product.Seral.area.<stage>.<au_token>.CC
The global feature.Seral.<stage> labels remain in place for compatibility
and summary surfaces. The AU-specific feature.Seral.<au_token>.<stage>
labels are the per-AU inventory-state surface.
Default boundaries are derived per AU from managed total-yield CMAI and peak yield age:
regenerating:
0-5young:
6-25immature:
26-CMAI(CMAI floor of 25 applied for ordering stability)mature:
CMAI+1tomin(peak_yield_age, 200)overmature:
mature_upper+1and older
YAML supports optional per-AU stage overrides:
default:
mature:
max_age: min_peak_or_200
au_overrides:
"985501000":
mature:
max_age: 170
overmature:
min_age: 171
Recognized token values for min_age/max_age are:
cmai, cmai_plus_1, peak_yield_age, min_peak_or_200,
mature_plus_1.
Retention modulator (optional per fragment)
The exporter now supports a per-fragment retention scalar:
RETENTION = 0.0: no retained areaRETENTION = 1.0: fully retained fragment areaintermediate values: partial retained area
Current export behavior:
fragments carry a numeric
RETENTIONfieldmanaged select statements emit:
<retention factor="RETENTION">the retained share is reassigned to
IFM='unmanaged'ORIGINis left unchanged on the retained portion
This keeps retention orthogonal to both:
IFMas the managed/unmanaged regime field, andORIGINas the natural/planted composition field
This is intentional. Retention may change treatment eligibility without changing curve provenance.
When RETENTION = 0.0 everywhere, retention wiring is present but behavior is
unchanged relative to the pre-retention model.
Point formatting policy:
x: integer age strings when integral (default case)yfor volume-yield curves (managed_total_*,unmanaged_total_*,au_*_..._yield_*): rounded to 1 decimal placeyfor normalized/proportion curves: rounded to at most 5 decimals
Fragments shapefile requirements
The exporter validates these required fields before writing:
BLOCK: integer block ID (non-negative). A block may have one row (one stand-fragment per block)AREA_HA: numeric area in hectares (strictly positive)F_AGE: numeric forest age (non-negative)AU: numeric analysis-unit ID (non-negative)IFM: management mode, one ofmanagedorunmanagedORIGIN: stand origin state, one ofnaturalorplantedRETENTION: retention factor in[0.0, 1.0]TSA: legacy case/FMU code label field retained for compatibilitygeometry: non-null, non-empty geometry
Managed/unmanaged assignment:
Exporter now supports two managed/unmanaged assignment modes:
proportional(default): interprets the THLB signal as a continuous managed-area share and carries the complementary unmanaged share throughRETENTION;legacy_binary: preserves the older threshold/share-based stand snap.
In
proportionalmode, exporter prefers continuous THLB sources in this order:thlb_fact, thenthlb_raw, thenthlb_area, thenthlb.In
legacy_binarymode, exporter preserves the historical priority order:thlb(0/1), thenthlb_fact(>0), thenthlb_area(>0), thenthlb_raw(>0).If no THLB signal is present, exporter defaults to
managed.In proportional mode, percent-style THLB signals greater than
1.0are interpreted as0..100percentages and normalized to0..1.You can override the source column with
--ifm-source-col.--ifm-mode proportionalis now the default and keeps continuous THLB share rather than snapping stands immediately to{0,1}.--ifm-threshold <value>marks stands as managed when source value exceeds the threshold inlegacy_binarymode.--ifm-target-managed-share <share>marks top-N stands as managed to hit the requested stand-count share inlegacy_binarymode.--ifm-thresholdand--ifm-target-managed-shareare mutually exclusive, and both are only valid when--ifm-mode legacy_binaryis in effect.
Important semantic boundary:
IFM assignment chooses the managed versus unmanaged lane only.
It does not, by itself, choose the natural versus treated curve family.
Natural/treated origin should come from the case’s reviewed origin contract, not from THLB or other IFM signals.
The fragments dataset must also carry a CRS.
CLI usage
Basic usage:
PYTHONPATH=src python -m femic export patchworks --tsa k3z
Useful overrides:
--bundle-dir: alternate bundle source (au_table.csv,curve_table.csv,curve_points_table.csv)--checkpoint: alternate stand checkpoint feather (must include geometry, legacyTSAcase code field, AU, and age)--output-dir: export destination--start-year,--horizon-years,--cc-min-age,--cc-max-age,--cc-transition-ifm,--fragments-crs,--seral-stage-config--ifm-mode,--ifm-source-col,--ifm-threshold,--ifm-target-managed-share
Transition note:
By default, CC tracks do not write an IFM transition assignment.
If
--cc-transition-ifm unmanagedis provided, CC treatment writes<transition><assign field="IFM" value="'unmanaged'"/></transition>.--cc-transition-ifm managedis accepted but omitted from XML because it is redundant within managed-only select statements.