Source code for triflow.core.simulation

#!/usr/bin/env python
# coding=utf8

import inspect
import logging

from coolname import generate_slug

from triflow.plugins import schemes

logging.getLogger(__name__).addHandler(logging.NullHandler())
logging = logging.getLogger(__name__)


[docs]class Simulation(object): """High level container used to run simulation build on triflow Model. This object is an iterable which will yield every time step until the parameters 'tmax' is reached if provided. By default, the solver use a 6th order ROW solver, an implicit method with integrated time-stepping. Parameters ---------- model : triflow.Model Contain finite difference approximation and routine of the dynamical system t : float initial time fields : triflow.Fields triflow container filled with initial conditions physical_parameters : dict physical parameters of the simulation id : None, optional name of the simulation. A 2 word slug will be generated if not provided. hook : callable, optional any callable taking the actual time, fields and parameters and return modified fields and parameters. Will be called every internal time step and can be used to include time dependent or conditionnal parameters, boundary conditions... scheme : callable, optional, default triflow.schemes.RODASPR an callable object which take the simulation state and return the next step. Its signature is scheme.__call__(fields, t, dt, pars, hook) and it should return the next time and the updated fields. It take the model and extra positional and named arguments. *args, **kwargs extra arguments passed to the scheme. *args, **kwargs extra arguments passed to the scheme. Attributes ---------- dt : float output time step fields : triflow.Fields triflow container filled with actual data i : int actual iteration id : str name of the simulation model : triflow.Model triflow Model used in the simulation physical_parameters : dict physical parameters of the simulation status : str status of the simulation, one of the following one: ('created', 'running', 'finished', 'failed') t : float actual time tmax : float or None, default None stopping time of the simulation. Not stopping if set to None. Examples -------- >>> import numpy as np >>> import triflow >>> model = triflow.Model(["k1 * dxxU", ... "k2 * dxxV"], ... ["U", "V"], ... ["k1", "k2"]) >>> x = np.linspace(0, 100, 1000, endpoint=False) >>> U = np.cos(x * 2 * np.pi / 100) >>> V = np.sin(x * 2 * np.pi / 100) >>> fields = model.fields_template(x=x, U=U, V=V) >>> pars = {'k1': 1, 'k2': 1, 'periodic': True} >>> simulation = triflow.Simulation(model, 0, fields, ... pars, dt=5, tmax=50) >>> for t, fields in simulation: ... pass >>> print(t) 50 """ # noqa def __init__(self, model, t, fields, physical_parameters, dt, id=None, hook=lambda t, fields, pars: (fields, pars), scheme=schemes.RODASPR, tmax=None, **kwargs): def intersection_kwargs(kwargs, function): func_signature = inspect.signature(function) func_parameters = func_signature.parameters kwargs = {key: value for key, value in kwargs.items() if key in func_parameters} return kwargs self.id = generate_slug(2) if not id else id self.model = model self.physical_parameters = physical_parameters self.fields = fields self.t = t self.dt = dt self.tmax = tmax self.i = 0 self._scheme = scheme(model, **intersection_kwargs(kwargs, scheme.__init__)) self.status = 'created' self._hook = hook self._displays = [] self._iterator = self.compute()
[docs] def compute(self): """Generator which yield the actual state of the system every dt. Yields ------ tuple : t, fields Actual time and updated fields container. """ fields = self.fields t = self.t pars = self.physical_parameters for display in self._displays: display(t, fields) try: while True: fields, pars = self._hook(t, fields, pars) t, fields = self._scheme(t, fields, self.dt, pars, hook=self._hook) self.fields = fields self.t = t self.physical_parameters = pars if not self._takewhile(): return for display in self._displays: display(self.t, self.fields) yield self.t, self.fields except RuntimeError: self.status = 'failed' raise
[docs] def add_display(self, display, *display_args, **display_kwargs): """add a display for the simulation. Parameters ---------- display : callable a display as the one available in triflow.displays *display_args positional arguments for the display function (other than the simulation itself) **display_kwargs named arguments for the display function """ # noqa self._displays.append(display(self, *display_args, **display_kwargs))
def _takewhile(self): if self.tmax is None: return True if self.t <= self.tmax: return True self.status = 'finished' return False def __iter__(self): return self.compute() def __next__(self): return next(self._iterator)