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(times=range(3))
             x
time
0     1.000000
1     0.367879
2     0.135335

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}, times=range(3))
             x
time
0     2.000000
1     0.735759
2     0.270671

which reuses the previously compiled model in the Simulator instance.

  1. Instantiating the model with other values:
>>> Simulator(Model(x=2)).solve(times=range(3))
             x
time
0     2.000000
1     0.735759
2     0.270671

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(times=range(3))
             x        2x
time
0     1.000000  2.000000
1     0.367879  0.735759
2     0.135335  0.270671

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(times=range(3))
             x         v
time
0     1.000000  0.000000
1     0.540302 -0.841471
2    -0.416147 -0.909297

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(times=range(3))
             x         y
time
0     1.000000  2.000000
1     0.367879  0.735759
2     0.135335  0.270671

Now, we can vary their initial conditions jointly:

>>> Simulator(Model(c=2)).solve(times=range(3))
             x         y
time
0     2.000000  4.000000
1     0.735759  1.471518
2     0.270671  0.541341

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

>>> Simulator(Model(c=2, y=2)).solve(times=range(3))
             x         y
time
0     2.000000  2.000000
1     0.735759  0.735759
2     0.270671  0.270671

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(times=range(3))
             x
time
0     1.000000
1     0.367879
2     0.135335

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(times=range(3))
             x
time
0     1.000000
1     1.956295
2     2.655911

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(times=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.540302      -0.841471
2    -0.416147      -0.909297

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.1.0.tar.gz (30.1 kB view details)

Uploaded Source

Built Distribution

poincare-0.1.0-py3-none-any.whl (31.2 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for poincare-0.1.0.tar.gz
Algorithm Hash digest
SHA256 7f9eb315680816065395ec1826c19a4b2ba9596afd575bf62d563593221076d6
MD5 565a818019f0ee83530fb0b0f9e28145
BLAKE2b-256 84f2fd2cfa70b7339faf312501328898d9bb51895912538a8753d3b53a268b6d

See more details on using hashes here.

File details

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

File metadata

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

File hashes

Hashes for poincare-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f03f2f3c743c64f73161187e11957600458063fdb283310acd9b6de4ade6ebf4
MD5 6f21f12349a6cbe6e65a07b6d5d31c15
BLAKE2b-256 044623fa50d5a449f6f0e32f61663e779587d6f3721c59a48438d5bafe6ca164

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