Python SDK
cfdl_sdk compiles and runs CFDL models from Python with pandas accessors
over the results. The compiler and engine are embedded in-process as a Rust
extension module — no separate binary or server, and results are
byte-identical to the CLI's.
Install
See Install for Python. Short version:
pip install cfdl-sdk.
Quickstart
import cfdl_sdk
results = cfdl_sdk.run(
"examples/cre_developer",
packs_dir="packs",
pack="cre",
config="examples/cre_developer/run.base.json",
)
results.cashflows() # wide DataFrame: one column per stream, PeriodIndex
results.metrics() # flat Series of core + domain metrics
results.metrics_frame() # metric / value / currency / source(core|domain:<pack>)
results.scenarios() # one row per scenario (when the run declares them)
results.monte_carlo() # per-metric summary stats (mean/stdev/percentiles)
results.annual() # annual rollup, when presentThe package's surface, with the signatures as the package defines them:
cfdl_sdk.run(model_dir, *, packs_dir=None, config=None, rate=None, as_of=None, pack=None) -> Results
cfdl_sdk.compile(model_dir, *, packs_dir=None) -> Model
Model.run(*, config=None, rate=None, as_of=None, pack=None, packs_dir=None) -> Results
Model.ir # property: the compiled IR, a dict
Results.from_json(results_json: str) -> Results
Results.cashflows(*, wide: bool = True) -> pandas.DataFrame
Results.metrics() -> pandas.Series
Results.metrics_frame() -> pandas.DataFrame
Results.annual() -> pandas.DataFrame
Results.scenarios() -> pandas.DataFrame
Results.monte_carlo() -> pandas.DataFrame
Results.scenario(name: str) -> Results
Results.compare(other=None, *, scenario=None, baseline=None) -> dict
Results.explain(series: str, period: int) -> dict
Results.explain_statement(statement: str, row: int, period: int, through: int | None = None) -> dict
Results.trace(kind=None, actor=None) -> pandas.DataFrame
Results.journal() -> pandas.DataFrame
Results.to_dict() -> dict
Results.status, Results.warnings, Results.model_hash, Results.ledger_hash, Results.run_config, Results.results_json, Results.scenario_names # properties
cfdl_sdk.package(dir, *, root="model.cfdl", run="run.json", results="results.json", ir=None) -> dict
cfdl_sdk.verify(dir) -> dict
cfdl_sdk.reproduce(dir) -> dict
Results.plot.cashflows(series=None, *, ax=None) # needs the [viz] extra
Results.plot.cumulative(series="model.net_cash_flow", *, ax=None)
Results.plot.mc_distribution(metric, *, ax=None)explainandexplain_statementsay what made a number: the agreement, the source line, the folds, the cell'sreasonand its owner'slifecycleevaluation that period, and the acts.rowandperiodare 0-based as published;throughis the last column of a cell that spans several, such as a year of a monthly statement.compare(scenario="downside")says what differs between the base run and a scenario, and why;baseline=names another scenario to compare from, andotheranotherResults.scenario("downside")is a scenario's run as results of its own. The answer'sdecisionslist the first period each actor decided differently, lifecycles first, before the cash.trace()says why every period is what it is: one row per run of one reason, for each lifecycle (moved, held or set, the state, the guards tested), event, option, stream and field;kindandactorfilter it. Itsentryis a position injournal(), which lists every act with its position and parent.statusis the deterministic run's status,warningswhat it noticed,model_hashwhich model produced it,ledger_hashwhat came out, andrun_configthe run configuration as the run used it.package,verifyandreproducemake and check a package folder (Packaging and verifying runs).- The review page,
inputs.assumptions, has no accessor of its own: read it fromto_dict().
config accepts a dict, a path to a run-config JSON file, or a raw JSON
string. pack names the pack whose domain metrics the run computes; it is
auto-detected from a pack file in the model directory when one exists, and
examples/cre_developer has none, so the quickstart passes it. The four domain
packs are built into the package, so a model that says use pack needs no
packs_dir; pass one to read the packs from a directory instead. compile() and
Model.run() are available separately when you want to reuse compiled IR.
Compile problems raise CompileError with structured .diagnostics
(code, message, span); runtime problems raise RunError.
Notebooks
Four notebooks, one per domain, built on the benchmark models. Each is published here with the outputs and chart it produced, so you can read the SDK's DataFrame surface on a real model before installing anything:
Each page is produced by executing its notebook against the released packs and engine, so the outputs and charts are real. The notebooks read benchmark models that are not distributed, so they are published as rendered pages rather than as files to run.