Civic-Digital-Twins Modeling Framework
Project description
Civic-Digital-Twins Modeling Framework
This repository contains a Python package implementing a Civic-Digital-Twins modeling framework. The framework is designed to support defining digital twins models and evaluating them in simulated environments with varying contextual conditions. We develop this package at @fbk-most, a research unit at Fondazione Bruno Kessler.
Note: this package is currently in an early development stage.
Conceptual Overview
The framework is organised in three layers.
Engine layer
The engine (civic_digital_twins.dt_model.engine) is an embedded DSL
compiler. The programmer builds a computation graph (DAG) by composing
typed nodes — constants, placeholders, and operations — using ordinary Python
expressions. The graph is then linearised by topological sorting and
evaluated by a NumPy-based interpreter that maps each node to the
corresponding numpy operation.
import numpy as np
from civic_digital_twins.dt_model.engine.frontend import graph, linearize
from civic_digital_twins.dt_model.engine.numpybackend import executor
a = graph.placeholder("a")
b = graph.placeholder("b")
c = a * 2 + b
state = executor.State(values={a: np.asarray(3.0), b: np.asarray(1.0)})
executor.evaluate_nodes(state, *linearize.forest(c))
print(state.get_node_value(c)) # 7.0
See docs/design/dd-cdt-engine.md for a full description of the engine.
Model / simulation layer
The model layer (civic_digital_twins.dt_model) provides higher-level
abstractions built on top of the engine:
Index/TimeseriesIndex— named wrappers around graph nodes. An index can be a constant, a distribution (sampled at evaluation time), or a formula.Model— a typed computation unit. Use the@definedecorator to declare aModelsubclass via acompute()method;@inputs,@outputs, and@exposedecorators mark the contractual interface. Sub-models are wired via constructor arguments incompute(), producing a composable pipeline.ModelVariant— selects among pre-constructedModelimplementations sharing the same I/O contract. The active variant is resolved by a string key (static) or aCategoricalIndex/graph node (runtime dispatch).Scenario— wraps a model with optional value overrides and parameter axes; the canonical first argument toEvaluationand all ensemble classes.Evaluation— evaluates a model over a sequence of weighted scenarios, each of which maps every abstract index to a concrete value.Ensemble/WeightedScenario— a protocol and type alias that define the scenario contract consumed byEvaluation.
from scipy import stats
from civic_digital_twins.dt_model import DistributionIndex, Index, Model, define, inputs, outputs
@define("example")
class ExampleModel(Model):
@inputs
class Inputs:
x: DistributionIndex
y: DistributionIndex
@outputs
class Outputs:
result: Index
def compute(self, inputs: Inputs) -> Outputs:
result = Index("result", inputs.x + inputs.y)
return ExampleModel.Outputs(result=result)
model = ExampleModel(inputs=ExampleModel.Inputs(
x=DistributionIndex("x", stats.uniform, {"loc": 0.0, "scale": 1.0}),
y=DistributionIndex("y", stats.uniform, {"loc": 0.0, "scale": 1.0}),
))
See docs/design/dd-cdt-model.md for the full
reference: index types, Model API, ModelVariant, Evaluation, and the
domain modeling pattern.
Usage patterns
The examples/ directory contains two illustrative examples, distinguished
by whether the model has external categorical context. Both use the
@define/compute() API (@inputs, @outputs, @expose, ModelVariant) —
see docs/design/dd-cdt-modularity.md.
Direct pattern (examples/mobility_bologna/) — no context variables.
Uncertainty enters only through DistributionIndex parameters.
DistributionEnsemble draws S Monte-Carlo samples to produce weighted
scenarios; Evaluation.evaluate() runs the engine and returns an
EvaluationResult.
Context-variable pattern (examples/overtourism_molveno/) — the model
has categorical scenario factors outside the modeller's control (season,
weather, …), expressed as CategoricalIndex, and quantities whose
distribution depends on context, expressed as
ConditionalDistributionIndex. CrossProductEnsemble enumerates the
context combinations into weighted scenarios; presence quantities are
swept over a multi-dimensional grid via
Evaluation.evaluate(parameters={pv: array, …}).
Installation
The package name is civic-digital-twins on PyPi. Install
using pip:
pip install civic-digital-twins
or, using uv:
uv add civic-digital-twins
The main package name is civic_digital_twins:
import civic_digital_twins
or
from civic_digital_twins import dt_model
Minimum Python Version
Python 3.12. Tested against Python 3.12, 3.13, and 3.14.
API Stability Guarantees
The package is currently in an early development stage. We do not anticipate breaking APIs without a good reason to do so, yet, breaking changes may occur from time to time. We generally expect subpackages within the top-level package to change more frequently.
Development Setup
We use uv for managing the development environment.
To get started, run:
git clone https://github.com/fbk-most/civic-digital-twins
cd civic-digital-twins
uv venv
source .venv/bin/activate
uv sync --dev
We use pytest for testing. To run tests use this command (from inside the virtual environment):
pytest
Each pull request is automatically tested using GitHub Actions. The workflow
is defined in .github/workflows/test.yml.
Updating Dependencies
uv self update
uv sync --upgrade
Releasing
Per-release checklist (eight manual steps):
-
Make sure the version number in
pyproject.tomlis correct. -
Regenerate the lockfile to record the new version:
uv lock -
Update
CHANGELOG.md: promote the[Unreleased]heading to[<version>] - <date>and add the corresponding comparison link at the bottom. -
Check that documentation
Last-Updateddates are in sync with actual commit dates:git log -1 --format="%ai" -- docs/design/dd-cdt-engine.md git log -1 --format="%ai" -- docs/design/dd-cdt-model.md git log -1 --format="%ai" -- docs/design/dd-cdt-modularity.md git log -1 --format="%ai" -- docs/design/dd-cdt-simulation.md git log -1 --format="%ai" -- docs/getting-started.md
Update any
Last-Updatedfields that are out of date. -
Verify that the runnable doc scripts in
examples/doc/are in sync with the code snippets in the corresponding documentation files, and that they all execute without errors:uv run python examples/doc/doc_engine.py uv run python examples/doc/doc_model.py uv run python examples/doc/doc_modularity.py uv run python examples/doc/doc_simulation.py uv run python examples/doc/doc_getting_started.py uv run python examples/doc/doc_overtourism_getting_started.py uv run python examples/doc/doc_readme.py
-
Verify that the full domain examples run end-to-end without errors (output images are written to
examples/*/output/):uv run python examples/mobility_bologna/mobility_bologna.py uv run python examples/overtourism_molveno/overtourism_molveno.py
-
Verify that every tracked Python and Markdown file carries an SPDX header:
# Python files — should print nothing (no files missing the header) git ls-files '*.py' | xargs grep -rL "SPDX-License-Identifier" # Markdown files — should print nothing git ls-files '*.md' | xargs grep -rL "SPDX-License-Identifier"
Add
# SPDX-License-Identifier: Apache-2.0(Python) or<!-- SPDX-License-Identifier: Apache-2.0 -->(Markdown) to any file that is missing the header. -
Commit the changes above, then create and push a version tag:
git add pyproject.toml uv.lock CHANGELOG.md docs/ git commit -m "chore: prepare v<version> release" git tag v<version> && git push origin main v<version>
After the tag is pushed, go to the repository's Releases page, review the
auto-created draft, write release notes, and click Publish release. This
triggers the publish.yml workflow, which
builds the sdist + wheel, runs twine check, and publishes to PyPI via OIDC —
no manual build or upload step is needed.
Precondition: PyPI's Trusted Publisher must be configured for this repository before the first release. See the PyPI Trusted Publishers documentation for setup instructions.
Documentation
| Document | Description |
|---|---|
| Getting Started | Step-by-step guide: define a model with @define/compute(), sample with DistributionEnsemble, evaluate with Evaluation. |
| dd-cdt-engine.md | DSL compiler engine — graph nodes, topological sorting, NumPy executor. |
| dd-cdt-model.md | Model layer reference — index types, @define/compute(), Model, Evaluation, EvaluationResult, and the domain modeling pattern. |
| dd-cdt-modularity.md | Model modularity concept guide — @define/compute(), ModelVariant, decomposition patterns, and Bologna worked example. |
| dd-cdt-simulation.md | Simulation guide — Scenario, CrossProductEnsemble, EvaluationHandle, incremental evaluation, ModelEvaluator. |
License
SPDX-License-Identifier: Apache-2.0
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file civic_digital_twins-0.10.0.tar.gz.
File metadata
- Download URL: civic_digital_twins-0.10.0.tar.gz
- Upload date:
- Size: 395.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1141155c35d3d21fb92a2358bdb5867a154f12e82979cbe9285b520e17baf1ef
|
|
| MD5 |
89d994c9c032c872a047e980058d1f94
|
|
| BLAKE2b-256 |
cc89b531a4aa305bb7f341914a6a62ecbd093e563756f92f82b8e03bc2065118
|
Provenance
The following attestation bundles were made for civic_digital_twins-0.10.0.tar.gz:
Publisher:
publish.yml on fbk-most/civic-digital-twins
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
civic_digital_twins-0.10.0.tar.gz -
Subject digest:
1141155c35d3d21fb92a2358bdb5867a154f12e82979cbe9285b520e17baf1ef - Sigstore transparency entry: 1902042413
- Sigstore integration time:
-
Permalink:
fbk-most/civic-digital-twins@b8c65405b4b842c3513cfbcd56252e7e5dd62de3 -
Branch / Tag:
refs/tags/v0.10.0 - Owner: https://github.com/fbk-most
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b8c65405b4b842c3513cfbcd56252e7e5dd62de3 -
Trigger Event:
release
-
Statement type:
File details
Details for the file civic_digital_twins-0.10.0-py3-none-any.whl.
File metadata
- Download URL: civic_digital_twins-0.10.0-py3-none-any.whl
- Upload date:
- Size: 132.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
beffda5b0f833385f4d25e2c6b1b8ceb1a9f33be5bcafc586c87225de2f3a643
|
|
| MD5 |
1a409a2321913511e72fd5b8f7a6f647
|
|
| BLAKE2b-256 |
ea952138cb2ceb9b06933182b0bbbd62d50d30c61262b44e398d82682257ab19
|
Provenance
The following attestation bundles were made for civic_digital_twins-0.10.0-py3-none-any.whl:
Publisher:
publish.yml on fbk-most/civic-digital-twins
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
civic_digital_twins-0.10.0-py3-none-any.whl -
Subject digest:
beffda5b0f833385f4d25e2c6b1b8ceb1a9f33be5bcafc586c87225de2f3a643 - Sigstore transparency entry: 1902042493
- Sigstore integration time:
-
Permalink:
fbk-most/civic-digital-twins@b8c65405b4b842c3513cfbcd56252e7e5dd62de3 -
Branch / Tag:
refs/tags/v0.10.0 - Owner: https://github.com/fbk-most
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b8c65405b4b842c3513cfbcd56252e7e5dd62de3 -
Trigger Event:
release
-
Statement type: