fonsim.core package

Submodules

fonsim.core.component module

Class Component

2020, July 21

class fonsim.core.component.Component(label)

Bases: object

Components to build a system with.

TODO

States and variables The state data (e.g. amount of fluid inside actuator) is saved as 2D-array in the components themselves (and the solver always refers to these) while the argument data (e.g. pressure, flow in/out) is saved as a list of _references_ to nameless 1D-arrays created by the solver. The component object provides functionality for the solver to allocate memory for the states, but not for allocating the variables. The solver takes care of calling these functions necessary. The object property state_history holds the 2D-array and object property argument_history holds a list with references to the 1D-arrays.

Parameters:

label – Component name.

auto(func)
auto_state(func)
evaluate(values, jacobian_state, jacobian_arguments, state, arguments, elapsed_time)

Evaluates the component internal equations. This method should be static.

Note: only evaluate left-hand side (LH) of equation, equation should be structured such that RH is always zero.

Parameters:
  • values – array where the equation residuals will be stored.

  • jacobian_state – array where the jacobian to the state will be stored.

  • jacobian_arguments – array where the jacobian to the arguments will be stored.

  • state – numerical values belonging to the state Variables.

  • arguments – numerical values belonging to the Variables.

  • elapsed_time – ? TODO.

Returns:

None

get(variable_key, terminal_label=None)

Same as method self.get_all, but returns only the first return value.

get_all(variable_key, terminal_label=None)

Get simulation results. Supports ‘smart matching’ by comparing string distances.

Parameters:
  • variable_key – key of variable, e.g. ‘pressure’

  • terminal_label – label of terminal, e.g. ‘a’

Returns:

Numpy ndarray object and Terminal object

get_state(label)

Get simulation results. Supports ‘smart matching’ by comparing string distances.

Parameters:

label – state label, e.g. ‘volume’

Returns:

Numpy ndarray object

get_terminal(terminallabel=None)

Returns Terminal object. If no label given, returns the first unconnected terminal. If label given, returns terminal with that label. If no terminal found, returns None.

Parameters:

terminallabel – Label of terminal

Returns:

Terminal object

set_arguments(*arguments)

Overwrite component arguments list with the provided Variable objects.

Parameters:

arguments – one or more Variable objects

Returns:

None

set_states(*states)

Overwrite component states list with the provided Variable objects.

Parameters:

states – one or more Variable objects

Returns:

None

set_terminals(*terminals)

Overwrite component terminals list with the provided Terminal objects and attach those terminals to the component.

Parameters:

terminals – one or more Terminal objects

Returns:

None

update_state(state_new, jacobian, state, arguments, dt)

Evaluates the update to the component state. This method should be static.

Parameters:
  • state_new – array where the new state values will be stored.

  • jacobian – array where the jacobian to the arguments will be stored.

  • state – numerical values belonging to the state Variables.

  • arguments – numerical values belonging to the Variables.

  • dt – time discretization timestep.

Returns:

None

fonsim.core.node module

Class Node

2021, January 14

class fonsim.core.node.Node(*terminals)

Bases: object

Connection between multiple component terminals

add_terminals(*terminals)

Connect terminals to the node

Parameters:

terminals – Terminal object. Multiple can be provided

Returns:

None

contains_terminal(terminal)

Check if the node contains the requested terminal

Parameters:

terminal – Terminal object

Returns:

Boolean

get_variables(orientation=None, key=None)

Get a list of all variables with the provided orientation and/or key associated with the node.

Parameters:
  • orientation – optional string specifying requested variable orientation

  • key – optional string specifying requested variable key

Returns:

list of Variable objects

merge_node(node)

Connect all terminals from another node to this node

Parameters:

node – Node object

Returns:

None

fonsim.core.setnumpythreads module

https://gitlab.com/abaeyens/fonsim/-/issues/19

2022, June 04

fonsim.core.setnumpythreads.setnumpythreads(nb_threads=1)

fonsim.core.simulation module

Class Simulation

2020, July 22

class fonsim.core.simulation.Simulation(system_to_simulate, duration=10, step=None, step_min=None, step_max=None, max_steps=0, verbose=0)

Bases: object

Class to convert network information (how the components are connected to each other) and component information (equations for each component) to a single non-linear system of equations (function vector + Jacobian) describing the whole system.

Solving this system is to be done by a solver. This object contains a solver object in the property self.solver. (Future functionality: possibility to select a solver manually etc.)

The Class Solver interacts heavily with the System class and expects the following methods to be available:

  • evaluate_equations()

  • update_state()

  • equation_to_string()

as well as the following properties:

  • system

  • phi, H and A

  • arguments

  • nb_arguments and nb_network_equations

  • times and duration and verbose

Parameters:
  • system_to_simulate – System object with components and how they are connected

  • duration – amount of time the system will be simulated for

  • step – initial time increment

  • step_min – minimal time increment during the simulation

  • step_max – maximal time increment during the simulation

  • max_steps – maximum amount of time increments before the simulation is terminated prematurely. A value of 0 disables this behavior (default)

  • verbose

    level of information printed in the console during the simulation. All messages belonging to a level with a number lower than or equal to the provided parameter will be displayed, with the possible levels being:

    • -1: simulation start and end messages

    • 0 (default): simulation progress in % steps

    • 1: system matrices on iterations with bad convergence

check_issolvable()

Warns when number of equations doesn’t equal number of unknowns. :return: None

equation_to_string(equation_index)

Return a string describing the equation with the provided index in a human-readable format. For a network equation, this string contains the involved variables and their coefficients. For a component equation, this string mentions the component label and the index of the equation in the list of equations of that particular component.

Parameters:

equation_index – index of the row in the simulation equation matrix corresponding to the desired equation

Return eq_str:

string representing the equation

evaluate_equations(simstep, g, H, elapsed_time, dt)

Evaluate component equations to obtain evaluated function vector (equation residuals) and jacobian. This method will call the method Component.evaluate on each component. This method will also call the method Component.update_state on each component.

Note: This function does not evaluate (or update) the network equations (upper part of the jacobian ‘H’)!

Parameters:
  • simstep – simulation timestep index to start from

  • g – numpy ndarray for the evaluated function vector

  • H – numpy ndarray for the evaluated jacobian

  • elapsed_time – time elapsed

  • dt – timestep (0 for explicit Euler, dt for implicit Euler)

Returns:

None

extend_memory(nb_extra_steps)

Increase the size of the simulation memory without overwriting previous results. This deals with the same entities as described in the documentation of initialize_memory

Parameters:

nb_extra_steps – amount of time steps by which to increase the memory

Returns:

None

fill_network_matrix(A)

Fill the network matrix. The network matrix is constant over the simulation, at least supposing the network configuration does not change. It is thus sufficient to calculate it a single time.

There are two types of network equations, one for across variables and one for through variables. At each node, all across variables have to be equal. Thus for each node n-1 equations, n being the number of components attached to that node. Concerning the through variables, the sum of all through variables should be zero at each node, thus one equation for each node.

Parameters:

A – system Jacobian matrix, numpy ndarray n x n, n=len(phi)

Returns:

None

init_matrixconstruction()

Create some LUT-style lists and dicts so data can be moved around quickly in the simulation loop.

Returns:

None

initialize_memory(nb_steps)

Initialize all arrays that will hold the simulation results through time. This includes - The vector with time values - Component argument and state histories (in Component class) - The simulation phi matrix with all arguments over time All previously stored results are overwritten. This method initializes the arrays with zeros and therefore does not use the initial_value attribute of the Variable objects.

Parameters:

nb_steps – estimated amount of time steps in the simulation

Returns:

None

map_phi_to_components(phi)

Send addresses of arguments over time to components, so one can get the data from the component without passing by the Simulation object. Furthermore, it avoids duplicating the data.

Parameters:

phi – numpy ndarray m x n with the argument vector over time (m = nb timesteps and n = nb arguments)

Returns:

None

map_state_to_components(state)

Send addresses of state over time to components, so one can get the data from the component without passing by the Simulation object. Furthermore, it avoids duplicating the data.

Parameters:

state – numpy ndarray m x n with the state vector over time (m = nb timesteps and n = nb states)

Returns:

None

print_equations()

Print a human-readable representation of the full system of equations to the console.

Returns:

None

run()

Run simulation, with the parameters specified previously.

Returns:

None

set_initial_values_phi(step=0)

Takes the initial values from the component argument Variable objects and writes them to the simulation memory (matrix ‘phi’).

Parameters:

step – simulation step at which to write the initial value

Returns:

None

set_initial_values_state(step=0)

Takes the initial values from the component state Variable objects and writes them to the simulation memory (matrix ‘state’).

Parameters:

step – simulation step at which to write the initial value

Returns:

None

slice_memory(start_step, end_step)

Decrease the size of the simulation memory by taking a slice out of it. This deals with the same entities as described in the documentation of initialize_memory

# TODO update such that takes slice as argument

Parameters:
  • start_step – index of the first time step in the range to maintain

  • end_step – index of the first time step outside the range to maintain

Returns:

None

update_state(simstep, dt)

Update the state variables in all components using the arguments in self.phi at step n + 1 (n = ‘simstep’). This method will call the method Component.update_state on each component.

Parameters:
  • simstep – simulation timestep index to start from

  • dt – timestep

Returns:

None

fonsim.core.solver module

Classes with available solvers

A solver takes care of solving the (non-linear) system of equations generated by the Simulation object. This object can interact with the Simulation object.

The Simulation class expects the following methods from the solver object: - get_nb_steps_estimate() - run_step(simulation_step_index)

Current solvers:
  1. ImplicitEulerNewtonConstantTimeStep

  2. ImplicitEulerNewtonAdaptiveTimeStep

2020, July 23

class fonsim.core.solver.ImplicitEulerNewton(simulation)

Bases: object

apply_solver_bias(bias=1e-12, step=0)

Give elements in the solution vector a small positive value, to ease starting the simulation

Parameters:
  • bias – bias value added to the solution vector entries

  • step – simulation step for which this bias is applied

Returns:

None

get_all_variables(simstep)
Parameters:

simstep – simulation step at which variables are queried

Returns:

np array with the values of all arguments and component states at the desired simulation step

get_residual(simstep, res=None)

Evaluate the simulation system of equations at the provided timestep to get the residual vector

Parameters:
  • simstep – index of simulation timestep at which the system of equations is evaluated

  • res – optional initialized residual vector that will be overwritten by this function in place

Returns:

evaluated residual vector

newton_solver(step, iterations=100, alpha=1.0)

Newton method for solving the system equations and storing the solution in the simulation phi vector at an time step

Parameters:
  • step – step index for which the system will be solved and the solution will be stored. The initialization for the solver can be done externally by setting self.sim.phi[step] to the initial guess

  • iterations – maximum number of newton solver iterations. 100 (default) gives good results although it often converges in one step and otherwise mostly in less than ten

  • alpha – correction constant for damped Newton method

Returns:

flag indicating exit status of the solver for this step: * 0: maximum amount of iterations reached without convergence * 1: solver converged quickly * 2: solver converged slowly

print_report(simstep)

Print a report on the solver status at a certain simulation time step.

Parameters:

simstep – index of simulation timestep for which the report is generated

Returns:

None

class fonsim.core.solver.ImplicitEulerNewtonAdaptiveTimeStep(simulation, step, step_min, step_max, max_attempts=200)

Bases: ImplicitEulerNewton

delta_in_range(delta)

Check whether a change in simulation variables (both arguments and states) during a step is within an acceptable range compared to the other simulation step results or it is abnormally large

Parameters:

delta – vector with change in all the arguments between consecutive simulation steps

Return in_range:

True if delta is of an acceptable magnitude and False if it represents a change which is abnormally large

get_nb_steps_estimate()
Returns:

number of timesteps the solver thinks it will need to finish the simulation

run_step(simstep)

Run a single step of the solver. Note: Calling it at step n (‘simstep’ = n) results in it writing to the next step, aka step n+1

Parameters:

simstep – index of timestep with the last simulated results

Returns:

list with as elements: * 0: boolean showing solver convergence for the simulation step * 1: string giving more information about the exit status

update_delta_range(nb_points, delta)

Update the average and variance of the changes of simulation variables (both arguments and states) over all simulation steps with the change at the current step

Parameters:
  • nb_points – amount of steps included in the last metrics

  • delta – new change in simulation arguments between consecutive steps to include in the metrics

Returns:

None

class fonsim.core.solver.ImplicitEulerNewtonConstantTimeStep(simulation, step)

Bases: ImplicitEulerNewton

get_nb_steps_estimate()
Returns:

number of timesteps the solver needs

run_step(simstep)

Run a single step of the solver. Note: Calling it at step n (‘simstep’ = n) results in it writing to the next step, aka step n+1

Parameters:

simstep – index of simulation timestep with the last results

Returns:

None

fonsim.core.system module

Class System

2020, July 22

class fonsim.core.system.System(label=None)

Bases: object

A System is a collection of interconnected components. It contains the Component objects and keeps track of how they are connected to each other. A System with components is created by first creating the System object, then adding components to this object and finally connecting these components to each other.

Parameters:

label – Label of the system, optional and currently not important.

add(*components)

Add components to the system.

Parameters:

components – Component object(s) to be added to the system

Returns:

None

connect(*args)

Connect two or more components together by connecting their terminals. In case terminals are not specified directly, the Component objects decide on which of their terminals to connect. The connections are made in sequential pairs using the method System.connect_two_components which in turn uses the method self.connect_two_terminals.

The components and/or terminals to connect should be as specified in the method System.get_component_and_terminal.

For instead connecting all components to a common Node, use the method System.connect_common.

Parameters:

args – component terminals

Returns:

None

connect_common(*args)

Connect two or more component terminals together. In case terminals are not specified directly, the Component objects decide on which of their terminals to connect. All Terminals are connected to each other, aka to a common Node. Making the connection is handled with the method self.connect_two_terminals.

The components and/or terminals to connect should be as specified in the method System.get_component_and_terminal.

For instead making sequential connections, use the method System.connect.

Parameters:

args – component terminals

Returns:

None

connect_two_components(component_a, component_b)

Connect two Components to each other. The connection is made using the method self.connect_two_terminals.

The component arguments component_a and component_b can be as specified in the method System.get_component_and_terminal.

Parameters:
  • component_a – First component, see description

  • component_b – Second component, see description

Returns:

None

connect_two_terminals(terminal_a, terminal_b)

Connect two system terminals together in a Node. These nodes exist for the workings of the solver and have nothing to do with any nodes in the fluidic networks being simulated.

Parameters:
  • terminal_a – Terminal object

  • terminal_b – Terminal object

Returns:

None

get(component_label)
Parameters:

component_label – Label of the desired component.

Returns:

Component object with the given label.

get_component_and_terminal(arg)

Get a pair of a Component and a Terminal given an argument arg. This argument can be:

  • a string specifying a component label present in the system

  • a Component object

  • a Terminal object

  • a Tuple with first the component label and then the terminal label as strings

If multiple choices are available, this method may make an undefined choice.

Parameters:

arg – see desription

Returns:

Component object, Terminal object

get_connectivity_message()

Get a message describing the connectivity of the system in case not every component is connected together.

Returns:

message string

fonsim.core.terminal module

Class Terminal

2020, July 22

class fonsim.core.terminal.Terminal(label, variables, variable_labels={})

Bases: object

Component connection point with local through and across variables.

Note: In any particular Terminal object, there cannot be more than one variable with the same key.

Parameters:
  • label – Label to refer to the Terminal later on. Free to choose.

  • variables – Variable objects that will belong to the Terminal.

get_variable(key)

Return the variable object attached to the terminal with the provided key, for example ‘pressure’. If there is no variable with the requested key, None is returned.

Parameters:

key – key of the variable to return

Return variable:

attached variable with the matching key

get_variables(orientation=None)

Get list of all terminal variables with the given orientation. The orientation can be either “through” or “across”. If not provided or None, all variables regardless of orientation are returned

Parameters:

orientation – optional string specifying desired variable orientation

Returns:

list of Variable objects

fonsim.core.variable module

Class Variable

2020, July 21

class fonsim.core.variable.Variable(key, orientation, initial_value=0.0, terminal=None, label='None')

Bases: object

A Variable object is used to denote the presence of a yet unknown numerical value. For each Variable object, the solver will search for the optimal numerical values over time. The solver does so by solving the system of equations that connect these variables together. The variables are connected by each other by connecting the Terminal objects that contain the values to each other.

The parameter ‘key’ indicates the type label. Only Variable objects with the same type label can exchange information.

The parameter ‘orientation’ should have value ‘across’ or ‘through’ or ‘free’. ‘across’ indicates that the value of the Variable will be shared with the Variable belonging to the other Terminal while ‘through’ indicates that its negative will be shared. The former is typically used with nondirectional values, such as pressure, while the latter is typically used with directional values, such as a massflow. ‘local’ indicates that it will not be shared (feature WIP).

Parameters:
  • key – type label, e.g. ‘pressure’, ‘massflow’

  • orientation – ‘across’, ‘through’ or ‘free’.

  • initial_value – Initial value, default: 0

  • terminal – Terminal object to which Variable object get connected, default: None

copy_and_attach(terminal)

Return a copy of the variable object attached to a given terminal. The returned variable has the same key, orientation and initial value but is otherwise unrelated to the Variable object it is called upon.

Parameters:

terminal – Terminal object to attach the variable copy to

Return variable:

attached copy of the variable object

short_str(nb_var_chars=1)

Return a short string describing the variable more as a symbol than in words. This string contains the first n letters of the variable name as well as (if applicable) the port and component it is attached to.

Parameters:

nb_var_chars – number of characters with which the variable key is abbreviated. Set to 0 to avoid abbreviation.

Return var_str:

short string representing the variable

Module contents

2020, September 17