frame v0.5.3 engine to the
developframe engine (frame_r's active branch, which ships a newer Scala
fire jar). developframe is the reference point going forward.frame_scala
commit 89e39c0 (a = 0 when flameLength or dlen ≈ 0, instead of the
old jar's -Inf/NaN plume). This is the sole behavioural change and the one
that makes framecpp match the developframe engine: on the four golden
communities only tuart's canopy differed under the old jar, and the guard
brings it into line (canopy flame length 18.58 m → 17.87 m, plus four extra
canopy ignition-path segments). The other three communities were already
identical across the two engines.inst/extdata/ref_*.rds, data-raw/ref_*.db) re-captured
from the developframe engine using identical inputs. framecpp is bit-exact
against the developframe engine on all seven tables of all four communities
(worst rel err ~1e-14); full test suite (1033) green.amadou to framecpp, naming it as the C++/Rcpp
member of the FRaME model family alongside frame_r and frame_scala. This
touches the package name, library()/::/namespace, the compiled
framecpp_* bindings and framecpp_version(), file names, and all docs. No
behaviour changes; the golden-reference parity and the full test suite (1033
tests) pass unchanged. The GitHub repository moves to
traitecoevo/framecpp (the old URL redirects).scenario_from_plant() — the direct plant -> framecpp front door: a
population of plant cohorts (crown geometry + relative abundance) meets the
engine at fire_scenario with no frame bridge and no k-means. It is a thin
adapter over scenario_from_population() (Tier B): it maps the cohort table
onto the population contract and delegates, so strata are derived by the same
deterministic geometric height-gap segmentation and a cleanly layered set of
cohorts reduces to scenario_from_community() bit-for-bit (the §3.2
equivalence contract, covered by tests). Packing/moisture terms plant does
not produce (live_moisture, openness, clump) are supplied per cohort or
from a reviewable packing fallback (FRaME buildTraitsP conventions); framecpp
does not infer them.scenario_from_survey() is not provided in framecpp, by design. Real survey
transects are typically non-layered (e.g. tuart_survey collapses to a
single connected vertical band), so resolving field strata needs frame's
stochastic k-means stratifier — a domain model that, per
dev/package-boundary.md, stays a front door in frame (framecpp's core is
deterministic, and framecpp must not depend on frame). Survey ingestion emits a
scenario from the frame side; framecpp's deterministic path handles already-
layered populations.run_fire_model() now returns a first-class fire_result object exposing
the complete run — all seven frame result tables' worth as tidy data
frames in $tables (Sites, Runs, Strata, FlameSummaries,
SurfaceResults, IgnitionPaths, ROS), plus a nested per-run convenience
view ($run1, $run2) and the scalars $canopyROS / $hasSecondRun. This
is the output half of the engine interface contract (dev/package-boundary.md)
and the prerequisite for the Tier C adapters (Phase 4) and the frame refactor
(Phase 6). It replaces the earlier subset (canopyROS, per-run surfaceROS,
flame summaries, ROS map) that skipped the ignition-path detail, the full
surface flame record, and the combined-flame block.parse_fire_result() is promoted to build the full fire_result from the new
compiled framecpp_fireResult() output; it still accepts the flat numeric vector
from framecpp_runFireModel() and returns the legacy subset for the parity
harnesses and low-level tests.run_fire_model() now also accepts a fire_scenario directly, not only a
paramBuilder parameter table.speciesId (build
order) and a speciesRank within each (level, pathType) group — not by
name. Species names are a data-source concern on the consumer side of the
boundary (frame resolves ids to names).SurfaceResults (both runs) and IgnitionPaths
geometry — matches the captured frame DB across all four communities (worst
relative error ~3e-15). New tests in test-fire-result.R; make parity and
the Getting started vignette extended to cover the full object.scenario_from_population() — build a scenario from a population of
plant elements (crown geometry + leaf traits, no stratum labels). Strata are
derived internally by a deterministic, geometric height-gap segmentation
of the vertical foliage profile (no k-means, no RNG): a stratum is a maximal
band of vertically connected crowns, modes mapped to the supplied levels by
height rank. It reuses the Tier A species/site assembly, so a cleanly layered
population reduces to scenario_from_community() bit-for-bit (the plan §3.2
equivalence contract, covered by tests). Non-layered profiles are rejected
with a clear message rather than resolved arbitrarily (research case, plan §6
P3). Stratum properties (plant_separation, overlaps) remain supplied per
level — no new physics.Building.R species/site derivations now live in one shared place
(.derive_species_block() / .assemble_scenario()) used by both the
community and population front doors.fire_scenario() — a typed, validated constructor for the one object the
engine runs (site scalars + strata + overlaps). scenario_from_params() now
returns a fire_scenario. All values are model-native SI.scenario_from_community() — build a scenario directly from a described
layered community (tidy site/strata/species/traits/overlaps data
frames), with no frame dependency and no parameter-table round trip. It
reproduces FRaME's Building.R derivations bit-for-bit (crown NA-fill,
clump geometry, composition-weighted plant separation, trait join). Accepts
model-native SI by default, or units = "frame" (km/h, deg, t/ha).paramBuilder bridge (~1e-15) and stays
bit-exact against the captured frame reference. Tier A source fixtures ship
in inst/extdata/community_*.rds (generated by data-raw/community_fixtures.R).First versioned milestone: the classic, discrete-strata FRaME fire model is
fully ported to C++/Rcpp and validated bit-exact against frame v0.5.3, in a
self-contained package.
-ffp-contract=off) so results match the JVM's rounding.frame model runs (inst/extdata/):
mild and extreme (the shipped layered community under control/severe
weather), alt (a round/flat-leaf community), and tuart (a
survey-derived, genuinely non-layered community). Worst relative error across
the corpus ~3e-15 (floating-point noise).frame jars stage-by-stage
(tools/parity/); testthat suite mirrors the ScalaTest specs (884 checks).In-process execution removes the per-run JVM start-up + SQLite round-trip that
the frame R interface pays on every call. On the mild community (single
single-site run):
| | per run | vs frame |
|---|--:|--:|
| run_fire_model() (R entry point) | ~1 ms | ~500–900× faster |
| compiled core only | ~0.2–0.8 ms | ~800–4000× faster |
| frame Java CSVRunner | ~630 ms | baseline |
Reproduce with make bench (tools/benchmark.R).
run_fire_model() — run the whole single-site model from a frame
paramBuilder parameter table.scenario_from_params() / parse_fire_result() — the param-table → scenario
bridge and result parser.crown_polygon(),
crown_ray_intersection(), surface_fire(), flame_angle(),
species_derived().framecpp_version().vignettes/articles/.