Metadata-Version: 2.4
Name: sysdyn
Version: 0.1.0
Summary: System Dynamics modelling, simulation and diagramming
Author-email: Balbir Thomas <balbir.thomas@gmail.com>
Maintainer-email: Balbir Thomas <balbir.thomas@gmail.com>
License-Expression: GPL-3.0-or-later
Project-URL: Homepage, https://github.com/balbirthomas/sysdyn
Project-URL: Repository, https://github.com/balbirthomas/sysdyn
Project-URL: Issues, https://github.com/balbirthomas/sysdyn/issues
Keywords: system dynamics,simulation,stock and flow,modelling
Classifier: Development Status :: 2 - Pre-Alpha
Classifier: Environment :: X11 Applications
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Mathematics
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.24
Requires-Dist: scipy>=1.10
Requires-Dist: matplotlib>=3.6
Provides-Extra: documentation
Requires-Dist: sphinx>=5.3; extra == "documentation"
Dynamic: license-file

# Sysdyn

System Dynamics modelling, simulation and diagramming in Python.

## What is System Dynamics?

System Dynamics is a way of understanding how something changes over time by
describing it in terms of what accumulates and the rates at which those
accumulations fill and drain.

A population is the standard example. It grows because births add to it and
shrinks because deaths remove from it. The population itself is a *stock*, a
quantity that accumulates. Births and deaths are *flows*, the rates at which
the stock changes. What makes such a system interesting is that the flows
depend on the stock: the more people there are, the more births there are, so
the population feeds back into its own growth.

That feedback is why these systems are worth simulating rather than reasoning
about in your head. A handful of stocks and flows connected in a loop can
behave in ways nobody predicts from looking at the parts, and the behaviour
often turns out to come from the structure of the connections rather than from
any single number in the model.

## What is Sysdyn?

Two things that work together.

A **Python package** for building and simulating System Dynamics models. A
flow is written as an ordinary Python function, and its argument names say what
it depends on:

```python
def births(population, birth_rate):
    """People born per unit of time."""
    return population * birth_rate
```

Sysdyn reads those argument names to work out how the parts of a model connect,
so nothing is written down twice. There is no separate equation language, and
each function can be called and tested on its own like any other Python
function.

A **graphical application** in which a model is built by drawing it, as a Stock
and Flow diagram on a canvas, then checked, simulated and plotted without
writing any Python.

## Describing a model, checking it, and running it

```python
from sysdyn import Flow, Model, Parameter, Stock, simulate

def births(population, birth_rate):
    """People born per year."""
    return population * birth_rate

model = Model("Population", time_units="year")
model.add(Stock("population", 1000.0, units="people"))
model.add(Parameter("birth_rate", 0.03, units="1/year"))
model.add(Flow("births", births, to_stock="population",
               units="people/year"))

print(model.problems())        # [] — nothing to object to

results = simulate(model, start_time=0.0, end_time=100.0, step_size=0.25)
print(results.values("population")[-1])
```

An equation may equally be written as text — `Flow("births", "population *
birth_rate", ...)` — and only text can be read, which is what lets Sysdyn check
that the units of an equation are what it says they are.

Results come back as NumPy arrays, so they go straight into Matplotlib and
SciPy. The tutorial (`docs/tutorial.rst` in the source tree) works through a
complete example and explains how to tell whether an answer can be trusted.

## What is in it

- stocks, flows, auxiliary variables and parameters, with mistakes reported
  before anything runs
- three fixed-step methods and six adaptive solvers, including ones for stiff
  models, and any solver of your own
- `step`, `pulse` and `ramp`, lookup tables, delays and smoothing, repeatable
  randomness, and locating the exact moment a threshold is crossed
- conveyors, ovens and queues, for material that does not mix
- dimensions, matched by name rather than by position
- **units, checked before a run**: a flow against what it fills, and the
  arithmetic of an equation against what it claims to be
- a model built out of models somebody else wrote
- the whole visual language in the application, with undo, plots in their own
  windows, the numbers out as CSV and the model out as plain Python

## The package does not need the graphical application

This matters enough to say on its own. The Python package is complete by
itself: it needs no display and no graphical toolkit, so models can be written
in plain Python files, kept under version control, and used alongside NumPy,
SciPy and Matplotlib in the ordinary way.

The relationship runs one way only. The graphical application is built on top
of the package's public interface, exactly as a user writing Python would use
it, and holds no modelling or simulation logic of its own. So anything the
application can do can also be done from Python, and choosing to draw a model
never limits what can be done with it afterwards.

## Requirements

- Python 3.11 or later
- NumPy 1.24 or later
- SciPy 1.10 or later
- Matplotlib 3.6 or later

The graphical application additionally needs `tkinter`, the standard library's
interface to the Tk toolkit. This cannot be installed from a package index
because it is supplied by the operating system. On Debian and its derivatives:

```
sudo apt install python3-tk
```

The Python package itself does not need it, and works on a machine with no
graphical display at all.

## Running the tests

From the top of the source tree:

```
python3 -m pytest
```

No installation and no virtual environment are needed. The test configuration
makes the package importable from `src/` directly, so a freshly cloned copy of
the repository is ready to test as it stands.

## Building the documentation

From the top of the source tree:

```
./docs/build.sh
```

The finished documentation is written to `docs/_build/html`, and the page to
open is `docs/_build/html/index.html`.

Sphinx 5.3 or later is all that is needed. Warnings are treated as errors, so a
reference to something that does not exist fails the build rather than quietly
producing a page with a broken link.

Equations are written as LaTeX in the documentation source and rendered in the
reader's browser, so nothing needs to be installed to typeset them. By default
the renderer is fetched from a content delivery network, which means viewing
the equations needs network access. To use a local copy instead — which is what
a distribution package wants — pass its path through:

```
./docs/build.sh -D mathjax_path=/usr/share/javascript/mathjax/MathJax.js
```

Anything given to `build.sh` is passed on to `sphinx-build`, so any other
setting can be overridden the same way.

## Licence

GNU General Public License, version 3 or later. The full text is in
[LICENSE](LICENSE).

## Author

The Sysdyn Developers.

The maintainer's contact address is recorded in the package metadata, which is
the one place it is kept. `pip show sysdyn` prints it for an installed copy,
and `pyproject.toml` holds it in the source.
