Running a case
The command is the orchestration with a shell in front of it: it reads a population and a keyword set, runs one case, and reports where the outputs are. It adds no modelling and no keyword the caller did not write.
python -m fresh_fvsbc population.json \
--directory run \
--species FD --tree-age 60 \
--keyword "STDIDENT=SYNTHETIC the command" \
--keyword STDINFO=CAR,IDFdk3/01,0,200,35,16 \
--keyword MANAGED=0,1 \
--keyword SITECODE=FD,100 \
--keyword INVYEAR=0 \
--keyword DESIGN=0,1,999,1,0,1,1 \
--keyword NUMCYCLE=4 \
--keyword RANNSEED=1 \
--keyword ECHOSUM \
--keyword PROCESS \
--keyword STOP
An installed package gets the same command as fresh-fvsbc. The recorded invocation, with its exit
status and its outputs, is in notes/reference/synthetic-run.md.
The population file
A JSON list of stem records, shaped like nemora’s output. The adapter reads stand_id,
dbh_cm, height_m and crown_ratio, and ignores the rest.
[
{"stand_id": "S1", "dbh_cm": 20.0, "height_m": 18.0, "crown_ratio": 0.5},
{"stand_id": "S1", "dbh_cm": 26.0, "height_m": 22.0, "crown_ratio": 0.6}
]
--species and --tree-age are required because nemora supplies neither, and the adapter fails
rather than defaulting. See nemora ingestion contract.
The keyword set
--keyword NAME=FIELD,..., repeated in the order FVS will read them. The fields are FVS’s own
parameter fields, so a blank field is written as a gap:
--keyword TIMEINT=,5
--block NAME=FIELDS|LINE|LINE writes a keyword that takes a block of lines: the fields come
before the first | and each | starts a line. This is how a caller asks for the database tables:
--block "DATABASE|DSNOut|FVSOut.db|Summary|ComputDB"
--keyword and --block keep their relative order, because a DATABASE block must precede the
stand setup — see Keyword-set contract. A --block for a keyword that takes no lines is refused
with exit 2.
The names are validated against FVS’s table, so a typo is refused rather than passed to Fortran. Two limits are worth knowing:
A parameter cannot contain a comma, because the comma separates the fields. The API has no such limit.
TREEFMTandTREEDATAare refused, because the run writes them. So are the block keywords (COMMENT,COMPUTE,IF,DATABASE), because the keyword model has no room for a block yet. See Keyword-set contract.
Exit codes
0The run completed and wrote its outputs.
1The run was attempted and failed: an error row, no database, a bad projection, or no extension. This is worth reporting.
2The input was wrong before any run: a missing or malformed population file, an unknown keyword, a parameter wider than its field, a species that does not resolve. This is worth fixing.
argparse already exits 2 for a usage error, so the third code is the convention rather than an
invention.
What the run writes
The directory named by --directory, created if absent, holds:
run.key, the keyword file, withTREEFMTandTREEDATAinserted beforePROCESS;run.tre, the tree list, ending with the-999line FVS reads to;FVSOut.db, the database, under the fixed namedbsblkd.fdeclares;run.outandrun.sum, FVS’s own report and summary;provenance.json, the record described in Provenance contract;summary.csvandvariants.json, what the child process that drove the projection wrote for the parent: the summary FVS produced, and the build’s variant list.
A run owns its directory. The database’s name is fixed, so two runs in one directory overwrite each other’s database. Each run drives its projection in a child process (ADR 0010), so the child owns the working directory and the Fortran state, and two runs in one process are independent: a run is thread-safe, and a second run cannot inherit the first one’s state.