Skip to main content

Simulation of dynamical systems.

Project description

Package CodeStyle License PyVersion CI pre-commit.ci status

Poincaré: simulation of dynamical systems

Poincaré allows to define and simulate dynamical systems in Python.

Definition

To define the system

$$ \frac{dx}{dt} = -x \quad \text{with} \quad x(0) = 1 $$

we write can:

>>> from poincare import Variable, System, initial
>>> class Model(System):
...   # Define a variable with name `x` with an initial value (t=0) of `1``.
...   x: Variable = initial(default=1)
...   # The rate of change of `x` (i.e. velocity) is assigned (<<) to `-x`.
...   # This relation is assigned to a Python variable (`eq`)
...   eq = x.derive() << -x
...

Simulation

To simulate that system, we do:

>>> from poincare import Simulator
>>> sim = Simulator(Model)
>>> sim.solve(save_at=range(3))
             x
time
0     1.000000
1     0.368139
2     0.135501

The output is a pandas.DataFrame, which can be plotted with .plot().

Changing initial conditions

To change the initial condition, we have two options.

  1. Passing a dictionary to the `solve`` method:
>>> sim.solve(values={Model.x: 2}, save_at=range(3))
             x
time
0     2.000000
1     0.736278
2     0.271002

which reuses the previously compiled model in the Simulator instance.

  1. Instantiating the model with other values:
>>> Simulator(Model(x=2)).solve(save_at=range(3))
             x
time
0     2.000000
1     0.736278
2     0.271002

This second option allows to compose systems into bigger systems. See the example in examples/oscillators.py.

Transforming the output

We can compute transformations of the output by passing a dictionary of expressions:

>>> Simulator(Model, transform={"x": Model.x, "2x": 2 * Model.x}).solve(save_at=range(3))
             x        2x
time
0     1.000000  2.000000
1     0.368139  0.736278
2     0.135501  0.271002

Higher-order systems

To define a higher-order system, we have to assign an initial condition to the derivative of a variable:

>>> from poincare import Derivative
>>> class Oscillator(System):
...   x: Variable = initial(default=1)
...   v: Derivative = x.derive(initial=0)
...   eq = v.derive() << -x
...
>>> Simulator(Oscillator).solve(save_at=range(3))
             x         v
time
0     1.000000  0.000000
1     0.540366 -0.841561
2    -0.416308 -0.909791

Constants, Parameters, and functions

Besides variables, we can define parameters and constants, and use functions from symbolite.

Constants

Constants allow to define common initial conditions for Variables and Derivatives:

>>> from poincare import assign, Constant
>>> class Model(System):
...     c: Constant = assign(default=1, constant=True)
...     x: Variable = initial(default=c)
...     y: Variable = initial(default=2 * c)
...     eq_x = x.derive() << -x
...     eq_y = y.derive() << -y
...
>>> Simulator(Model).solve(save_at=range(3))
             x         y
time
0     1.000000  2.000000
1     0.368139  0.736278
2     0.135501  0.271002

Now, we can vary their initial conditions jointly:

>>> Simulator(Model(c=2)).solve(save_at=range(3))
             x         y
time
0     2.000000  4.000000
1     0.736278  1.472556
2     0.271001  0.542003

But we can break that connection by passing y initial value directly:

>>> Simulator(Model(c=2, y=2)).solve(save_at=range(3))
             x         y
time
0     2.000000  2.000000
1     0.736278  0.736278
2     0.271002  0.271002

Parameters

Parameters are like Variables, but their time evolution is given directly as a function of time, Variables, Constants and other Parameters:

>>> from poincare import Parameter
>>> class Model(System):
...     p: Parameter = assign(default=1)
...     x: Variable = initial(default=1)
...     eq = x.derive() << -p * x
...
>>> Simulator(Model).solve(save_at=range(3))
             x
time
0     1.000000
1     0.368139
2     0.135501

Functions

Symbolite functions are accessible from the symbolite.scalar module:

>>> from symbolite import scalar
>>> class Model(System):
...     x: Variable = initial(default=1)
...     eq = x.derive() << scalar.sin(x)
...
>>> Simulator(Model).solve(save_at=range(3))
             x
time
0     1.000000
1     1.951464
2     2.654572

Units

poincaré also supports functions through pint and pint-pandas.

>>> import pint
>>> unit = pint.get_application_registry()
>>> class Model(System):
...     x: Variable = initial(default=1 * unit.m)
...     v: Derivative = x.derive(initial=0 * unit.m/unit.s)
...     w: Parameter = assign(default=1 * unit.Hz)
...     eq = v.derive() << -w**2 * x
...
>>> result = Simulator(Model).solve(save_at=range(3))

The columns have units of m and m/s, respectively. pint raises a DimensionalityError if we try to add them:

>>> result["x"] + result["v"]
Traceback (most recent call last):
...
pint.errors.DimensionalityError: Cannot convert from 'meter' ([length]) to 'meter / second' ([length] / [time])

We can remove the units and set them as string metadata with:

>>> result.pint.dequantify()
             x              v
unit     meter meter / second
time
0     1.000000       0.000000
1     0.540366      -0.841561
2    -0.416308      -0.909791

which allows to plot the DataFrame with .plot().

Installation

pip install -U poincare

Project details


Download files

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

Source Distribution

poincare-0.3.1.tar.gz (35.1 kB view details)

Uploaded Source

Built Distribution

poincare-0.3.1-py3-none-any.whl (36.5 kB view details)

Uploaded Python 3

File details

Details for the file poincare-0.3.1.tar.gz.

File metadata

  • Download URL: poincare-0.3.1.tar.gz
  • Upload date:
  • Size: 35.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/4.0.2 CPython/3.11.6

File hashes

Hashes for poincare-0.3.1.tar.gz
Algorithm Hash digest
SHA256 9b048cb429ef4f058a55ddab10d2499b7e994da3429d7d5d0dd8440e2ad1941d
MD5 a8f2b63c713bd71b87b531fc7fb3f5f5
BLAKE2b-256 9dc49051713ed9c7ddfd1662bfa1c7ea76be8bc3290486c0e5ece138ed6a62fe

See more details on using hashes here.

File details

Details for the file poincare-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: poincare-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 36.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/4.0.2 CPython/3.11.6

File hashes

Hashes for poincare-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 0eaee5642910e78eb24c82936b875ad391053086cb0954a7abefea108718da09
MD5 9290a4df431d6a7b431323fc164b7f1f
BLAKE2b-256 c60a388b9ffd579bb8cfb568b102e5ac540b25ae3bbb3032f8310cd13eb6c420

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page