femic.fmg.patchworks Module
The femic.fmg.patchworks module is FEMIC’s Patchworks export synthesis
layer. It takes the compiled bundle/checkpoint surfaces produced upstream and
turns them into a Patchworks package: forestmodel.xml plus the fragments
shapefile payload that Matrix Builder and the interactive model consume later.
If you are debugging why FEMIC exported the wrong ForestModel curves, why a fragments dataset fails structural validation, or why retention/seral/silviculture semantics are not showing up in the Patchworks package, this is the first module to read. In practice it owns:
Patchworks ForestModel XML construction from bundle model context
fragments GeoDataFrame construction and shapefile writing
managed/unmanaged IFM assignment and origin/silviculture state wiring
derived yield/species/seral/old-growth curve generation
export-time validation of XML structure and fragments field/value contracts
Start Here If…
Use this page first if you are trying to:
understand how FEMIC bundle tables become
forestmodel.xmlandfragments/fragments.shpinspect how AU-level managed/unmanaged tracks are mapped into Patchworks feature attributes and treatments
debug seral-stage, retention, CT/PCT/fertilization, or origin-state behavior in exported models
work out whether a failure belongs in export synthesis here or later in
femic.patchworks_runtimevalidate whether a problem is in the source bundle/checkpoint surfaces versus the Patchworks-specific export layer
Typical maintenance path:
Start with
export_patchworks_package()to understand the top-level export contract and artifacts.Move to
build_patchworks_forestmodel_definition()andbuild_forestmodel_xml_tree_from_context()if the issue is visible in ForestModel XML.Read
build_fragments_geodataframe()if the issue is visible in fragments field values, IFM assignment, geometry, or retention state.Finish with
validate_forestmodel_xml_tree()andvalidate_fragments_geodataframe()if the export is failing fast on contract checks before runtime launch.
Typical Usage
The common operator-facing path is to export from already-built bundle tables rather than rerunning upstream stages from inside the exporter:
femic export patchworks --instance-root external/femic-k3z-instance --run-config config/run_profile.k3z.yaml --tsa k3z
At the Python level, maintainers usually enter through the top-level export
helpers after bundle tables already exist under data/model_input_bundle/.
The CLI flag remains --tsa for compatibility, but in generic FEMIC usage
that selector should be read as an FMU/code target rather than only a BC
Timber Supply Area.
How This Fits Into The Pipeline
This module sits after Stage 01b and bundle assembly. It does not run Patchworks itself. Instead, it defines the export-time contract that later runtime helpers consume.
At a high level, the owning sequence is:
read bundle/checkpoint/model-context inputs
derive Patchworks curves, attributes, treatments, and selects
write
forestmodel.xmlbuild and validate the fragments dataset
hand the resulting package off to downstream runtime tooling
That distinction matters when debugging. If the package content itself is wrong, this module is the likely owner. If the package is correct but Patchworks fails to launch or Matrix Builder fails later, the problem usually belongs in the runtime layer instead.
Key Entry Surfaces
The highest-value entrypoints in this module are:
export_patchworks_package()Top-level package export entrypoint returning the final artifact paths/counts.build_patchworks_forestmodel_definition()Build the in-memory Patchworks definition, including selects, treatments, and curve bindings.build_forestmodel_xml_tree()build_forestmodel_xml_tree_from_context()Build XML output from bundle tables or a prepared model context.build_fragments_geodataframe()Build the fragments dataset from FEMIC checkpoint output.validate_forestmodel_xml_tree()Enforce Patchworks XML structure expectations before writing.validate_fragments_geodataframe()Enforce required fragments field/value/geometry contracts.
The main result payload is also worth reading:
PatchworksExportResult
Main Contract Surfaces
The most important export contracts in this module are:
forestmodel.xmlmust contain the expected Patchworks structure, required select/input/treatment surfaces, and valid curve referencesfragments must carry the required columns from
REQUIRED_FRAGMENT_COLUMNS, includingIFM,ORIGIN,SILV_STATE,RETENTION, and geometrymanaged/unmanaged assignment can come from explicit IFM signal columns or target-share heuristics
retention is modeled as a separate scalar factor and should stay orthogonal to IFM and silviculture state
optional seral-stage and silviculture configs can change which attributes, states, and treatments are emitted
export-time validation should fail fast before the package reaches the proprietary runtime boundary
This is the code-level owner of the Patchworks export contract documented in Patchworks Export Contract.
Curve And Attribute Synthesis
One reason this module is large is that it does much more than serialize existing curves. It also derives export-specific surfaces, including:
readable deterministic curve ids for source and derived curves
species-yield curves from total-yield plus species-proportion curves
seral-stage binary curves
old-growth indicator curves
treatment-state variants for CT, PCT, and fertilization paths
feature/product/account bindings for managed and unmanaged tracks
That means a bug in exported Patchworks semantics often does not come from a single raw source table. It may come from how this module derives and rebinds curves during export.
One current high-value example is managed QMD. When the optional BTC
stand-structure-basic bank is present, this module now prefers richer
BTC-native managed diameter evidence in this order:
direct
DBHg000curve pointsQMD reconstructed from
BasalArea000plusSPH000/StemCount000the older volume/height/stems approximation
That keeps the newer K3Z proving-ground QMD surfaces coherent with the richer BTC-managed stand-structure outputs without forcing every non-bank surface to carry the same dependency.
Another current example is the log-grade compile-recipe seam. The shipped
log-grades recipe now treats the explicit grades
D/F/H/I/J/U/X/Y as the additive family and excludes Logs_Grade_All by
default because that BTC field behaves as a separate scaled-log metric rather
than a true additive parent. At export time this module:
reads the shipped reference recipe from
src/femic/resources/patchworks/btc_indicator_bank_compile_recipes.yaml;merges optional user overlays from
~/.femic/recipe-overlays/btc_indicator_bank_compile_recipes.yaml;applies optional treatment-specific ratio overrides;
can also apply exact
treatment + SILV_STATEratio overrides for narrow cases such as post-CTCCwithout changing baselineCC;normalizes the explicit grades against harvested-volume totals so the emitted grade family sums to
product.HarvestedVolume.*instead of raw BTC merchantable yield.
The current K3Z teaching contract uses that seam to bias CT harvested
volume toward lower-grade J/U/X/Y material. This is a deliberate bridge
between upstream forest-growth signals and downstream product-sector teaching
accounts, not a claim that BTC directly observed CT-grade outcomes.
Another exporter-owned contract to keep in mind is Patchworks succession
wiring. Patchworks’ own DTD and sample ForestModel XMLs define succession
as a select-scoped element, not a treatment transition block. FEMIC now
uses that same contract for K3Z by attaching a default pass-through
<succession breakup="1000" renew="1000" /> to the state-bearing selects
that also carry the live track surface. That default form deliberately makes no
field assignments and therefore behaves as a null “keep the current state”
succession path while still satisfying the explicit-successions XML contract.
The same recipe seam now also supports a second teaching bridge layer for K3Z:
additive
AU x species x gradeharvested-volume products built from the explicit grade family plus AU-level species weights;matching value products built from shipped coast-market price matrices; and
user-owned override seams under
~/.femic/recipe-overlaysfor both the compile recipe and the price matrices/proxy mappings.
This bridge should be read as a modeled classroom surface, not as a claim that BTC directly observed species-by-grade outturn. FEMIC uses the explicit grade totals as one margin, combines them with AU/species weights, and emits the full matrix so students can move between forest-growth accounting and products-sector accounting inside the same Patchworks model.
Succession Defaults
Patchworks treats succession as a select-scoped XML element, not a
treatment-scoped one. FEMIC now models that contract directly and emits a
default pass-through succession on state-bearing selects:
<succession breakup="1000" renew="1000" />
No assign children are emitted for this default. The intent is simply to
ensure every compiled state path has an explicit valid succession contract
without changing the modeled state. This behavior was anchored against the
installed Patchworks ForestModel.dtd and corroborated with the newer
reference/ForestModel.xsd plus the shipped sample
ForestModel_C5_lookup.xml.
For the K3Z family, broad Matrix Builder revalidation after this change
collapsed the old succession rows out of every rebuilt tracks*/messages.csv
surface, and no substantive warning/error text remained in stderr. That means
the explicit succession defaults achieved the practical 0-warning goal directly
without needing an extra warning-policy layer in the current slice.
Fragments And State Wiring
The fragments path in this module is responsible for:
coercing geometry out of checkpoint payloads
assigning deterministic fragment/block ids
resolving IFM from configured signal columns or managed-share heuristics
writing
ORIGINandSILV_STATEvalues expected by the XML definitionapplying full-retention overrides where configured
preserving a valid CRS for geometry-derived area processing
This is also the main seam where exported model semantics become spatial. If the XML looks reasonable but Patchworks behavior is still wrong, inspect the fragments dataset generated here before assuming the runtime is at fault.
Failure Seams To Watch
The common failure boundaries in this module are:
invalid fragments payloads missing columns, null/empty geometry, invalid CRS, bad value domains, or duplicate fragment/block identifiers fail validation here
XML structure drift missing required treatments, invalid define fields, or bad curve references fail export before runtime launch
IFM assignment surprises changing signal columns, thresholds, or target-managed-share logic can silently reclassify a large portion of the fragments set
seral/silviculture config misuse malformed YAML or invalid config objects can change or block which treatment states are emitted
retention confusion retention is intended to be an explicit scalar overlay, not a synonym for IFM or unmanaged state, so bugs around that distinction often surface here
Cross-References
Guides and references that pair especially closely with this module:
Related API pages:
Patchworks export helpers (ForestModel XML + fragments shapefile).
- class femic.fmg.patchworks.PatchworksExportResult(forestmodel_xml_path, fragments_shapefile_path, tsa_list, au_count, fragment_count, curve_count)[source]
Bases:
objectPaths and counts from a Patchworks package export.
- Parameters:
forestmodel_xml_path (Path)
fragments_shapefile_path (Path)
tsa_list (list[str])
au_count (int)
fragment_count (int)
curve_count (int)
- au_count: int
- curve_count: int
- forestmodel_xml_path: Path
- fragment_count: int
- fragments_shapefile_path: Path
- tsa_list: list[str]
- femic.fmg.patchworks.build_forestmodel_xml_tree(*, au_table, curve_table, curve_points_table, forestmodel_description='FEMIC Patchworks export', input_attributes=None, start_year=2026, horizon_years=300, cc_min_age=0, cc_max_age=1000, cc_transition_ifm=None, seral_stage_config=None, silviculture_config=None)[source]
Build a Patchworks ForestModel XML tree from FEMIC bundle tables.
- Parameters:
au_table (DataFrame)
curve_table (DataFrame)
curve_points_table (DataFrame)
forestmodel_description (str)
input_attributes (dict[str, str] | None)
start_year (int)
horizon_years (int)
cc_min_age (int)
cc_max_age (int)
cc_transition_ifm (str | None)
seral_stage_config (dict[str, Any] | None)
silviculture_config (dict[str, Any] | None)
- Return type:
Element
- femic.fmg.patchworks.build_forestmodel_xml_tree_from_context(*, context, forestmodel_description='FEMIC Patchworks export', input_attributes=None, start_year=2026, horizon_years=300, cc_min_age=0, cc_max_age=1000, cc_transition_ifm=None, seral_stage_config=None, silviculture_config=None)[source]
Build a Patchworks ForestModel XML tree from shared FMG context.
- Parameters:
context (BundleModelContext)
forestmodel_description (str)
input_attributes (dict[str, str] | None)
start_year (int)
horizon_years (int)
cc_min_age (int)
cc_max_age (int)
cc_transition_ifm (str | None)
seral_stage_config (dict[str, Any] | None)
silviculture_config (dict[str, Any] | None)
- Return type:
Element
- femic.fmg.patchworks.build_fragments_geodataframe(*, checkpoint_path, au_table, tsa_list, fragments_crs='EPSG:3005', ifm_mode='proportional', ifm_source_col=None, ifm_threshold=None, ifm_target_managed_share=None, silviculture_config=None, legacy_input_variables_config=None)[source]
Build Patchworks fragments GeoDataFrame from FEMIC checkpoint output.
- Parameters:
checkpoint_path (Path)
au_table (DataFrame)
tsa_list (Iterable[str])
fragments_crs (str)
ifm_mode (str)
ifm_source_col (str | None)
ifm_threshold (float | None)
ifm_target_managed_share (float | None)
silviculture_config (dict[str, Any] | None)
legacy_input_variables_config (dict[str, Any] | None)
- Return type:
Any
- femic.fmg.patchworks.build_patchworks_forestmodel_definition(*, context, forestmodel_description='FEMIC Patchworks export', input_attributes=None, start_year=2026, horizon_years=300, cc_min_age=0, cc_max_age=1000, cc_transition_ifm=None, seral_stage_config=None, silviculture_config=None)[source]
Build Patchworks ForestModel core definition from shared context.
- Parameters:
context (BundleModelContext)
forestmodel_description (str)
input_attributes (dict[str, str] | None)
start_year (int)
horizon_years (int)
cc_min_age (int)
cc_max_age (int)
cc_transition_ifm (str | None)
seral_stage_config (dict[str, Any] | None)
silviculture_config (dict[str, Any] | None)
- Return type:
ForestModelDefinition
- femic.fmg.patchworks.export_patchworks_package(*, bundle_dir, checkpoint_path, output_dir, tsa_list, forestmodel_description='FEMIC Patchworks export', start_year=2026, horizon_years=300, cc_min_age=0, cc_max_age=1000, cc_transition_ifm=None, fragments_crs='EPSG:3005', ifm_mode='proportional', ifm_source_col=None, ifm_threshold=None, ifm_target_managed_share=None, seral_stage_config_path=None, silviculture_config_path=None, legacy_input_variables_config_path=None)[source]
Export Patchworks package artifacts from FEMIC outputs.
- Parameters:
bundle_dir (Path)
checkpoint_path (Path)
output_dir (Path)
tsa_list (Iterable[str])
forestmodel_description (str)
start_year (int)
horizon_years (int)
cc_min_age (int)
cc_max_age (int)
cc_transition_ifm (str | None)
fragments_crs (str)
ifm_mode (str)
ifm_source_col (str | None)
ifm_threshold (float | None)
ifm_target_managed_share (float | None)
seral_stage_config_path (Path | None)
silviculture_config_path (Path | None)
legacy_input_variables_config_path (Path | None)
- Return type:
PatchworksExportResult
- femic.fmg.patchworks.forestmodel_definition_to_xml_tree(*, definition)[source]
Serialize ForestModel core definition to XML tree.
- Parameters:
definition (ForestModelDefinition)
- Return type:
Element
- femic.fmg.patchworks.validate_forestmodel_xml_tree(*, root, required_define_fields=None, required_curve_ids=('unity',), require_cc_treatment=True)[source]
Validate required ForestModel structure and curve references.
- Parameters:
root (Element)
required_define_fields (Iterable[str] | None)
required_curve_ids (Iterable[str] | None)
require_cc_treatment (bool)
- Return type:
None
- femic.fmg.patchworks.validate_fragments_geodataframe(*, fragments_gdf)[source]
Validate required Patchworks fragments fields and value domains.
- Parameters:
fragments_gdf (Any)
- Return type:
None
- femic.fmg.patchworks.write_forestmodel_xml(*, root, path)[source]
Write ForestModel XML tree with Patchworks XSD model hint.
- Parameters:
root (Element)
path (Path)
- Return type:
None
- femic.fmg.patchworks.write_fragments_shapefile(*, fragments_gdf, path)[source]
Write fragments shapefile (directory + sidecar files).
- Parameters:
fragments_gdf (Any)
path (Path)
- Return type:
None