Metadata-Version: 2.4
Name: civic-digital-twins
Version: 0.8.0
Summary: Civic-Digital-Twins Modeling Framework
Project-URL: Homepage, https://github.com/fbk-most/civic-digital-twins
Author-email: Fondazione Bruno Kessler <most@fbk.eu>, Marco Pistore <pistore@fbk.eu>, Simone Basso <sibasso@fbk.eu>
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: civic,digital twin,modeling,simulation,sustainability,urban
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Python: >=3.11.0
Requires-Dist: numpy>=2.2.0
Requires-Dist: pandas>=2.2.0
Requires-Dist: scipy>=1.17.1
Description-Content-Type: text/markdown

# Civic-Digital-Twins Modeling Framework

[![Build Status](https://github.com/fbk-most/civic-digital-twins/actions/workflows/test.yml/badge.svg)](https://github.com/fbk-most/civic-digital-twins/actions) [![codecov](https://codecov.io/gh/fbk-most/civic-digital-twins/branch/main/graph/badge.svg)](https://codecov.io/gh/fbk-most/civic-digital-twins) [![PyPI version](https://img.shields.io/pypi/v/civic-digital-twins.svg)](https://pypi.org/project/civic-digital-twins/) [![Python Versions](https://img.shields.io/pypi/pyversions/civic-digital-twins.svg)](https://pypi.org/project/civic-digital-twins/) [![License](https://img.shields.io/pypi/l/civic-digital-twins.svg)](https://pypi.org/project/civic-digital-twins/)

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](
https://github.com/fbk-most), a research unit at [Fondazione Bruno Kessler](
https://www.fbk.eu/en/).

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

```python
from civic_digital_twins.dt_model.engine.frontend import graph, linearize
from civic_digital_twins.dt_model.engine.numpybackend import executor
import numpy as np

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](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 named collection of `Index` objects.  Declare `Inputs`,
  `Outputs`, and `Expose` as inner dataclasses to make the inter-model
  interface explicit.  Sub-models are wired via constructor arguments,
  producing a composable pipeline.
- **`ModelVariant`** — selects among pre-constructed `Model` implementations
  sharing the same I/O contract.  The active variant is resolved at
  construction time via a string key.
- **`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`.

```python
from civic_digital_twins.dt_model import Evaluation, Model, DistributionIndex
from civic_digital_twins.dt_model.model.index import Index
from scipy import stats

# Two distribution-backed indexes
x = DistributionIndex("x", stats.uniform, {"loc": 0.0, "scale": 1.0})
y = DistributionIndex("y", stats.uniform, {"loc": 0.0, "scale": 1.0})
result = Index("result", x + y)

model = Model("example", [x, y, result])
```

See [docs/design/dd-cdt-model.md](docs/design/dd-cdt-model.md) for the full
reference: index types, `Model` API, `ModelVariant`, `Evaluation`, and the
vertical extension pattern.

### Usage patterns

Three concrete usage patterns are illustrated in the `examples/` directory.

**Direct pattern** (`examples/mobility_bologna/`) — the model consists
entirely of `Index` and `TimeseriesIndex` objects;
`DistributionEnsemble` samples each distribution-backed index to produce
weighted scenarios; `Evaluation.evaluate()` runs the engine and returns an
`EvaluationResult`.

**Vertical extension pattern** (`examples/overtourism_molveno/`) — the
domain-specific layer introduces `ContextVariable`, `PresenceVariable`,
`Constraint`, and `OvertourismModel` on top of the core types.
`OvertourismEnsemble` samples context variables and produces weighted
scenarios.  `Evaluation.evaluate(axes={pv: array, …})` evaluates the model
on a multi-dimensional grid, returning arrays of shape `(N₀, …, Nₖ, S)`
where `S` is the number of scenarios and each `Nᵢ` corresponds to one
presence axis.

**Model modularity** — for larger models, the dataclass-based I/O API
(`Inputs`, `Outputs`, `Expose`) makes inter-model wiring explicit and
machine-checkable.  `ModelVariant` enables swapping between alternative
implementations at construction time.  Both the Bologna and Molveno examples
are rewritten using this API.  See
[docs/design/dd-cdt-modularity.md](docs/design/dd-cdt-modularity.md)
for the full guide.

## Installation

The package name is `civic-digital-twins` on [PyPi](
https://pypi.org/project/civic-digital-twins/). Install
using `pip`:

```bash
pip install civic-digital-twins
```

or, using `uv`:

```bash
uv add civic-digital-twins
```

The main package name is `civic_digital_twins`:

```Python
import civic_digital_twins
```

or

```Python
from civic_digital_twins import dt_model
```

## Minimum Python Version

Python 3.11. Tested against Python 3.11, 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](https://astral.sh/uv) for managing the development environment.

To get started, run:

```bash
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](https://docs.pytest.org/en/stable/) for testing. To run
tests use this command (from inside the virtual environment):

```bash
pytest
```

Each pull request is automatically tested using GitHub Actions. The workflow
is defined in [`.github/workflows/test.yml`](.github/workflows/test.yml).

## Updating Dependencies

```bash
uv self update
uv sync --upgrade
```

## Releasing

Per-release checklist (five manual steps):

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

2. Regenerate the lockfile to record the new version:
   ```bash
   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:
   ```bash
   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/getting-started.md
   ```
   Update any `Last-Updated` fields that are out of date.

5. Commit the changes above, then create and push a version tag:
   ```bash
   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`](.github/workflows/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](https://docs.pypi.org/trusted-publishers/)
> for setup instructions.

## Documentation

| Document | Description |
| -------- | ----------- |
| [Getting Started](docs/getting-started.md) | Step-by-step guide covering the direct and vertical extension usage patterns. |
| [dd-cdt-engine.md](docs/design/dd-cdt-engine.md) | DSL compiler engine — graph nodes, topological sorting, NumPy executor. |
| [dd-cdt-model.md](docs/design/dd-cdt-model.md) | Model / simulation layer — `Model`, `Evaluation`, `WeightedScenario`, and the vertical extension pattern. |
| [dd-cdt-modularity.md](docs/design/dd-cdt-modularity.md) | Model modularity concept guide — dataclass I/O API, `ModelVariant`, decomposition patterns, and Bologna worked example. |

## License

```
SPDX-License-Identifier: Apache-2.0
```
