Skip to main content

Civic-Digital-Twins Modeling Framework

Build Status codecov PyPI version Python Versions License

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 @define decorator to declare a Model subclass via a compute() method; @inputs, @outputs, and @expose decorators mark the contractual interface. Sub-models are wired via constructor arguments in compute(), producing a composable pipeline.
  • ModelVariant — selects among pre-constructed Model implementations sharing the same I/O contract. The active variant is resolved by a string key (static) or a CategoricalIndex/graph node (runtime dispatch).
  • Scenario — wraps a model with optional value overrides and parameter axes; the canonical first argument to Evaluation and 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 by Evaluation.
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):

  1. Make sure the version number in pyproject.toml is correct.

  2. Regenerate the lockfile to record the new version:

    uv lock
    
  3. Update CHANGELOG.md: promote the [Unreleased] heading to [<version>] - <date> and add the corresponding comparison link at the bottom.

  4. Check that documentation Last-Updated dates 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-Updated fields that are out of date.

  5. 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
    
  6. 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
    
  7. 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.

  8. 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

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

civic_digital_twins-0.10.1.tar.gz (398.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

civic_digital_twins-0.10.1-py3-none-any.whl (133.3 kB view details)

Uploaded Python 3

File details

Details for the file civic_digital_twins-0.10.1.tar.gz.

File metadata

  • Download URL: civic_digital_twins-0.10.1.tar.gz
  • Upload date:
  • Size: 398.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for civic_digital_twins-0.10.1.tar.gz
Algorithm Hash digest
SHA256 d4cbf9c4bdd855c94ed37cad592e9b785e8b85eae838007810a50a264b8d1796
MD5 9ce05b37382ea90257b63c39310fa94b
BLAKE2b-256 d6dd9b14c335c572ca0517fd75971e8e2fd65b6e56b859121c704f304a324391

See more details on using hashes here.

Provenance

The following attestation bundles were made for civic_digital_twins-0.10.1.tar.gz:

Publisher: publish.yml on fbk-most/civic-digital-twins

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file civic_digital_twins-0.10.1-py3-none-any.whl.

File metadata

File hashes

Hashes for civic_digital_twins-0.10.1-py3-none-any.whl
Algorithm Hash digest
SHA256 d9e42e0d9521db26b1f94f58bfd15ff3ec32d5a84002d67d6bb0f6f8ba6b5f97
MD5 d47e632c895a620fff07c10fe3c5e1ba
BLAKE2b-256 fcad44fb0d54af2602d5e3d7f06c12a2c765a536b9d933f46e7b5ba8a4f3525e

See more details on using hashes here.

Provenance

The following attestation bundles were made for civic_digital_twins-0.10.1-py3-none-any.whl:

Publisher: publish.yml on fbk-most/civic-digital-twins

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.11.1

2 files

0.11.0

2 files

This release

0.10.1 This release

2 files

0.10.0

2 files

0.9.0

2 files

0.8.1

2 files

0.8.0

2 files

0.7.0

2 files

0.6.0

2 files

0.5.0

2 files

0.4.0

2 files

0.3.0

2 files

0.2.0

2 files

0.1.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page