Simulation log¶
CoSApp simulation logger is built on top of the Python built-in package logging
. To set the simulation log, you will use the helper function set_log
.
[1]:
from cosapp.utils import LogLevel, set_log
help(set_log)
Help on function set_log in module cosapp.utils.logging:
set_log(filename: Union[str, pathlib.Path, NoneType] = 'cosapp_trace.log', stream: Optional[io.TextIOBase] = <object object at 0x7f95cee2ec60>, level: int = <LogLevel.INFO: 20>, context: Optional[str] = None, start_time: Optional[numbers.Number] = None, format: str = '%(message)s', encoding: Optional[str] = None, backupCount: int = 5) -> None
Set the CoSApp simulation log behavior.
If `backupCount` is nonzero, at most `backupCount` files will be kept, and if more
would be created when rollover occurs, the oldest one is deleted.
The system will save old log files by appending extensions to the filename. The
extensions are date-and-time based, using the strftime format `%Y-%m-%d_%H-%M-%S`.
By default the log messages are written to a file (specified by its `filename`) and
to a stream. Set either `filename` or `stream` to deactivate the corresponding log
handler.
Parameters
----------
filename : str or Path or None, optional
Log filename; default "cosapp_trace.log"
stream : io.TextIOBase or None, optional
Log stream; default ``sys.stdout``
level : int or LogLevel, optional
Log level; default LogLevel.INFO
context : str or None, optional
Context on which to focus the log message; default None
start_time : Number or None, optional
Time from which debug log will be recorded; default all time steps
format : str, optional
Log record format; default "%(message)s" - for the available attributes (see https://docs.python.org/3/library/logging.html#logrecord-attributes)
encoding : str, optional
File encoding to be enforced
backupCount : int, optional
Number of backup log files; default 5
Example¶
The following cell creates the two tanks problem (see Transient simulations tutorial for more information). The usage of set_log
will be demonstrated on it.
[2]:
import numpy as np
from cosapp.base import System, Port
from cosapp.drivers import EulerExplicit, NonLinearSolver
class FloatPort(Port):
def setup(self):
self.add_variable('value', 0.0)
class Tank(System):
def setup(self, rho=1e3):
self.add_inward('area', 1.0, desc='Cross-section area')
self.add_inward('rho', abs(rho), desc='Fluid density')
self.add_input(FloatPort, 'flowrate')
self.add_output(FloatPort, 'p_bottom')
self.add_transient('height', der='flowrate.value / area')
def compute(self):
g = 9.81
self.p_bottom.value = self.rho * g * self.height
class Pipe(System):
"""Poiseuille flow in a cylindrical pipe"""
def setup(self):
self.add_inward('D', 0.1, desc="Diameter")
self.add_inward('L', 2.0, desc="Length")
self.add_inward('mu', 1e-3, desc="Fluid dynamic viscosity")
self.add_input(FloatPort, 'p1')
self.add_input(FloatPort, 'p2')
self.add_output(FloatPort, 'Q1')
self.add_output(FloatPort, 'Q2')
self.add_outward('k', desc='Pressure loss coefficient')
def compute(self):
"""Computes the volumetric flowrate from the pressure drop"""
self.k = np.pi * self.D**4 / (256 * self.mu * self.L)
self.Q1.value = self.k * (self.p2.value - self.p1.value)
self.Q2.value = -self.Q1.value
class CoupledTanks(System):
"""System describing two tanks connected by a pipe (viscous limit)"""
def setup(self, rho=1e3):
self.add_child(Tank('tank1', rho=rho))
self.add_child(Tank('tank2', rho=rho))
self.add_child(Pipe('pipe'))
self.connect(self.tank1.p_bottom, self.pipe.p1)
self.connect(self.tank2.p_bottom, self.pipe.p2)
self.connect(self.tank1.flowrate, self.pipe.Q1)
self.connect(self.tank2.flowrate, self.pipe.Q2)
set_log
usage¶
Default usage¶
In its default usage, the simulation log will be stored in a file with the default name in the current folder. Only messages of level LogLevel.INFO
or above will be displayed.
[3]:
set_log()
system = CoupledTanks("coupledTanks", rho=1e3)
driver = system.add_driver(EulerExplicit(dt=0.1, time_interval=[0, 0.1]))
solver = driver.add_child(NonLinearSolver("solver", factor=1.0))
h1_0, h2_0 = (3, 1)
driver.set_scenario(
name="run",
init={"tank1.height": h1_0, "tank2.height": h2_0,}, # initial conditions
values={"pipe.D": 0.07, "pipe.L": 2.5, "tank1.area": 2,}, # fixed values
)
system.run_drivers()
Creation of new time-dependent inward 'height' within system 'tank1'
Creation of new time-dependent inward 'height' within system 'tank2'
------------------------------------------------------------
# Starting driver 'Euler' on 'coupledTanks'
Time = 0 s
solver : solver -> Converged (0.0000e+00) in 1 iterations, 1 complete, 0 partial Jacobian and 0 Broyden evaluation(s) (tol = 0.0e+00)
Time = 0.1 s
solver : solver -> Converged (0.0000e+00) in 1 iterations, 0 complete, 0 partial Jacobian and 0 Broyden evaluation(s) (tol = 0.0e+00)
# Ending driver 'Euler' on 'coupledTanks' in 0.014 seconds
Set log level¶
If you want to have more of less information, you could set the level of message to be recorded according to the following scale:
[4]:
LogLevel?
[5]:
set_log(level=LogLevel.WARNING) # less information (none in this case)
system = CoupledTanks("coupledTanks", rho=1e3)
driver = system.add_driver(EulerExplicit(dt=0.1, time_interval=[0, 0.1]))
solver = driver.add_child(NonLinearSolver("solver", factor=1.0))
h1_0, h2_0 = (3, 1)
driver.set_scenario(
name="run",
init={"tank1.height": h1_0, "tank2.height": h2_0,}, # initial conditions
values={"pipe.D": 0.07, "pipe.L": 2.5, "tank1.area": 2,}, # fixed values
)
system.run_drivers() # Should no emit any log message
[6]:
set_log(level=LogLevel.DEBUG) # more information
system = CoupledTanks("coupledTanks", rho=1e3)
driver = system.add_driver(EulerExplicit(dt=0.1, time_interval=[0, 0.1]))
solver = driver.add_child(NonLinearSolver("solver", factor=1.0))
h1_0, h2_0 = (3, 1)
driver.set_scenario(
name="run",
init={"tank1.height": h1_0, "tank2.height": h2_0,}, # initial conditions
values={"pipe.D": 0.07, "pipe.L": 2.5, "tank1.area": 2,}, # fixed values
)
system.run_drivers()
Creation of new time-dependent inward 'height' within system 'tank1'
Creation of new time-dependent inward 'height' within system 'tank2'
System <coupledTanks - CoupledTanks> is the execution master.
Call coupledTanks.open_loops
Call tank1.open_loops
Call tank2.open_loops
Call pipe.open_loops
Connector Connector(tank1.flowrate <- pipe.Q1, ['value']) to be opened.
Connector Connector(tank2.flowrate <- pipe.Q2, ['value']) to be opened.
Start setup_run recursive calls.
Call Euler.setup_run
Call solver.setup_run
****************************************
*
* Assemble mathematical problem
*
****************************************
Mathematical problem:
Unknowns [2]
tank1.flowrate.value = 0.0
tank2.flowrate.value = 0.0
Equations [2]
tank1.flowrate.value == pipe.Q1.value (loop) := 0.0
tank2.flowrate.value == pipe.Q2.value (loop) := 0.0
Transient variables: {'tank1.height': height := 0.0, 'tank2.height': height := 0.0}
Rate variables: OrderedDict()
Reset time to 0
Call coupledTanks.setup_run
Call tank1.setup_run
Call tank2.setup_run
Call pipe.setup_run
Exec order for coupledTanks: ['tank1', 'tank2', 'pipe']
Call driver Euler.run_once on coupledTanks
------------------------------------------------------------
# Starting driver 'Euler' on 'coupledTanks'
Call Euler.compute
Reset time to 0
Apply initial conditions
tank1.area = 2
pipe.L = 2.5
pipe.D = 0.07
tank2.height = 1
tank1.height = 3
Reset rates
Time = 0 s
Call solver.run_once()
Call solver.compute_before()
Set unknowns initial values: [0. 0.]
Call solver.compute()
NR - Reference call
Call fresidues with x = array([0., 0.])
Call coupledTanks.compute_before
Call coupledTanks.tank1.run_once()
Call tank1.compute_before
Call coupledTanks.tank2.run_once()
Call tank2.compute_before
Call coupledTanks.pipe.run_once()
Call pipe.compute_before
Residues: array([ 2.31238989, -2.31238989])
Initial residue: 2.3123898893765573
Iteration 0
Perturb unknown 0
Call fresidues with x = array([1.52587891e-05, 0.00000000e+00])
Skip coupledTanks.compute_before - Clean inputs
Call coupledTanks.tank1.run_once()
Call tank1.compute_before
Call coupledTanks.tank2.run_once()
Skip tank2.compute_before - Clean inputs
Skip tank2.compute - Clean inputs
Call coupledTanks.pipe.run_once()
Call pipe.compute_before
Residues: array([ 2.31240515, -2.31238989])
Perturb unknown 1
Call fresidues with x = array([0.00000000e+00, 1.52587891e-05])
Skip coupledTanks.compute_before - Clean inputs
Call coupledTanks.tank1.run_once()
Call tank1.compute_before
Call coupledTanks.tank2.run_once()
Call tank2.compute_before
Call coupledTanks.pipe.run_once()
Call pipe.compute_before
Residues: array([ 2.31238989, -2.31237463])
Jacobian matrix: full update
Call fresidues with x = array([-2.31238989, 2.31238989])
Skip coupledTanks.compute_before - Clean inputs
Call coupledTanks.tank1.run_once()
Call tank1.compute_before
Call coupledTanks.tank2.run_once()
Call tank2.compute_before
Call coupledTanks.pipe.run_once()
Call pipe.compute_before
Residues: array([0., 0.])
iter #1 tol = 0.00e+00 |R| = 0.0 x = [-2.31238989 2.31238989]
solver : solver -> Converged (0.0000e+00) in 1 iterations, 1 complete, 0 partial Jacobian and 0 Broyden evaluation(s) (tol = 0.0e+00)
Time = 0.1 s
Call solver.run_once()
Call solver.compute_before()
Set unknowns initial values: [0. 0.]
Call solver.compute()
NR - Reference call
Call fresidues with x = array([0., 0.])
Skip coupledTanks.compute_before - Clean inputs
Call coupledTanks.tank1.run_once()
Call tank1.compute_before
Call coupledTanks.tank2.run_once()
Call tank2.compute_before
Call coupledTanks.pipe.run_once()
Call pipe.compute_before
Residues: array([ 1.91135386, -1.91135386])
Initial residue: 1.9113538643397376
Reuse of previous Jacobian matrix
Iteration 0
Call fresidues with x = array([-1.91135386, 1.91135386])
Skip coupledTanks.compute_before - Clean inputs
Call coupledTanks.tank1.run_once()
Call tank1.compute_before
Call coupledTanks.tank2.run_once()
Call tank2.compute_before
Call coupledTanks.pipe.run_once()
Call pipe.compute_before
Residues: array([0., 0.])
iter #1 tol = 0.00e+00 |R| = 0.0 x = [-1.91135386 1.91135386]
solver : solver -> Converged (0.0000e+00) in 1 iterations, 0 complete, 0 partial Jacobian and 0 Broyden evaluation(s) (tol = 0.0e+00)
# Ending driver 'Euler' on 'coupledTanks' in 0.065 seconds
Start clean_run recursive calls.
Call coupledTanks.clean_run
Call tank1.clean_run
Compute calls for coupledTanks.tank1: 6
Call tank2.clean_run
Compute calls for coupledTanks.tank2: 5
Call pipe.clean_run
Compute calls for coupledTanks.pipe: 6
Compute calls for coupledTanks: 6
Call Euler.clean_run
Call solver.clean_run
Compute calls for Euler.solver: 2
Compute calls for Euler: 1
Call coupledTanks.close_loops
Add time filter¶
With level lower than DEBUG
, the number of message recorded may become large. Usually, though, when debugging, you may be interested in debug messages emitted after a given simulation time, say. You can achieve that by specifying the start_time
keyword:
[7]:
set_log(level=LogLevel.DEBUG, start_time=0.1) # Be more verbose for time greater than 0.1s
system = CoupledTanks("coupledTanks", rho=1e3)
driver = system.add_driver(EulerExplicit(dt=0.1, time_interval=[0, 0.1]))
solver = driver.add_child(NonLinearSolver("solver", factor=1.0))
h1_0, h2_0 = (3, 1)
driver.set_scenario(
name="run",
init={"tank1.height": h1_0, "tank2.height": h2_0,}, # initial conditions
values={"pipe.D": 0.07, "pipe.L": 2.5, "tank1.area": 2,}, # fixed values
)
system.run_drivers()
Creation of new time-dependent inward 'height' within system 'tank1'
Creation of new time-dependent inward 'height' within system 'tank2'
System <coupledTanks - CoupledTanks> is the execution master.
Call coupledTanks.open_loops
Call tank1.open_loops
Call tank2.open_loops
Call pipe.open_loops
Connector Connector(tank1.flowrate <- pipe.Q1, ['value']) to be opened.
Connector Connector(tank2.flowrate <- pipe.Q2, ['value']) to be opened.
Start setup_run recursive calls.
Call Euler.setup_run
Call solver.setup_run
****************************************
*
* Assemble mathematical problem
*
****************************************
Mathematical problem:
Unknowns [2]
tank1.flowrate.value = 0.0
tank2.flowrate.value = 0.0
Equations [2]
tank1.flowrate.value == pipe.Q1.value (loop) := 0.0
tank2.flowrate.value == pipe.Q2.value (loop) := 0.0
Transient variables: {'tank1.height': height := 0.0, 'tank2.height': height := 0.0}
Rate variables: OrderedDict()
Reset time to 0
------------------------------------------------------------
# Starting driver 'Euler' on 'coupledTanks'
Time = 0 s
solver : solver -> Converged (0.0000e+00) in 1 iterations, 1 complete, 0 partial Jacobian and 0 Broyden evaluation(s) (tol = 0.0e+00)
Time = 0.1 s
Skip coupledTanks.compute_before - Clean inputs
Call coupledTanks.tank1.run_once()
Call tank1.compute_before
Call coupledTanks.tank2.run_once()
Call tank2.compute_before
Call coupledTanks.pipe.run_once()
Call pipe.compute_before
Residues: array([ 1.91135386, -1.91135386])
Initial residue: 1.9113538643397376
Reuse of previous Jacobian matrix
Iteration 0
Call fresidues with x = array([-1.91135386, 1.91135386])
Skip coupledTanks.compute_before - Clean inputs
Call coupledTanks.tank1.run_once()
Call tank1.compute_before
Call coupledTanks.tank2.run_once()
Call tank2.compute_before
Call coupledTanks.pipe.run_once()
Call pipe.compute_before
Residues: array([0., 0.])
iter #1 tol = 0.00e+00 |R| = 0.0 x = [-1.91135386 1.91135386]
solver : solver -> Converged (0.0000e+00) in 1 iterations, 0 complete, 0 partial Jacobian and 0 Broyden evaluation(s) (tol = 0.0e+00)
# Ending driver 'Euler' on 'coupledTanks' in 0.032 seconds
Start clean_run recursive calls.
Call coupledTanks.clean_run
Call tank1.clean_run
Compute calls for coupledTanks.tank1: 6
Call tank2.clean_run
Compute calls for coupledTanks.tank2: 5
Call pipe.clean_run
Compute calls for coupledTanks.pipe: 6
Compute calls for coupledTanks: 6
Call Euler.clean_run
Call solver.clean_run
Compute calls for Euler.solver: 2
Compute calls for Euler: 1
Call coupledTanks.close_loops
Add context filter¶
Another possibility would be to filter by context, i.e. by systems or by driver.
When filtering by system, the prescribed system and all its children will be part of the more verbose log. See for example the log for the dummy subsystem
subtank1
in the following example.
[8]:
set_log(level=LogLevel.DEBUG, context="tank1") # Filter on a System
system = CoupledTanks("coupledTanks", rho=1e3)
driver = system.add_driver(EulerExplicit(dt=0.1, time_interval=[0, 0.1]))
system.tank1.add_child(System("subtank1"))
solver = driver.add_child(NonLinearSolver("solver", factor=1.0))
h1_0, h2_0 = (3, 1)
driver.set_scenario(
name="run",
init={"tank1.height": h1_0, "tank2.height": h2_0,}, # initial conditions
values={"pipe.D": 0.07, "pipe.L": 2.5, "tank1.area": 2,}, # fixed values
)
system.run_drivers()
Creation of new time-dependent inward 'height' within system 'tank1'
Creation of new time-dependent inward 'height' within system 'tank2'
Call tank1.setup_run
Call subtank1.setup_run
------------------------------------------------------------
# Starting driver 'Euler' on 'coupledTanks'
Time = 0 s
Call tank1.compute_before
Call tank1.subtank1.run_once()
Call subtank1.compute_before
Call tank1.compute_before
Call tank1.subtank1.run_once()
Skip subtank1.compute_before - Clean inputs
Skip subtank1.compute - Clean inputs
Call tank1.compute_before
Call tank1.subtank1.run_once()
Skip subtank1.compute_before - Clean inputs
Skip subtank1.compute - Clean inputs
Call tank1.compute_before
Call tank1.subtank1.run_once()
Skip subtank1.compute_before - Clean inputs
Skip subtank1.compute - Clean inputs
solver : solver -> Converged (0.0000e+00) in 1 iterations, 1 complete, 0 partial Jacobian and 0 Broyden evaluation(s) (tol = 0.0e+00)
Time = 0.1 s
Call tank1.compute_before
Call tank1.subtank1.run_once()
Skip subtank1.compute_before - Clean inputs
Skip subtank1.compute - Clean inputs
Call tank1.compute_before
Call tank1.subtank1.run_once()
Skip subtank1.compute_before - Clean inputs
Skip subtank1.compute - Clean inputs
solver : solver -> Converged (0.0000e+00) in 1 iterations, 0 complete, 0 partial Jacobian and 0 Broyden evaluation(s) (tol = 0.0e+00)
# Ending driver 'Euler' on 'coupledTanks' in 0.037 seconds
Call tank1.clean_run
Call subtank1.clean_run
Compute calls for coupledTanks.tank1.subtank1: 1
Compute calls for coupledTanks.tank1: 6
[9]:
set_log(level=LogLevel.DEBUG, context="solver") # Filter on a Driver
system = CoupledTanks("coupledTanks", rho=1e3)
driver = system.add_driver(EulerExplicit(dt=0.1, time_interval=[0, 0.1]))
solver = driver.add_child(NonLinearSolver("solver", factor=1.0))
h1_0, h2_0 = (3, 1)
driver.set_scenario(
name="run",
init={"tank1.height": h1_0, "tank2.height": h2_0,}, # initial conditions
values={"pipe.D": 0.07, "pipe.L": 2.5, "tank1.area": 2,}, # fixed values
)
system.run_drivers()
Creation of new time-dependent inward 'height' within system 'tank1'
Creation of new time-dependent inward 'height' within system 'tank2'
Call solver.setup_run
****************************************
*
* Assemble mathematical problem
*
****************************************
Mathematical problem:
Unknowns [2]
tank1.flowrate.value = 0.0
tank2.flowrate.value = 0.0
Equations [2]
tank1.flowrate.value == pipe.Q1.value (loop) := 0.0
tank2.flowrate.value == pipe.Q2.value (loop) := 0.0
------------------------------------------------------------
# Starting driver 'Euler' on 'coupledTanks'
Time = 0 s
Call solver.compute_before()
Set unknowns initial values: [0. 0.]
Call solver.compute()
NR - Reference call
Call fresidues with x = array([0., 0.])
solver : solver -> Converged (0.0000e+00) in 1 iterations, 1 complete, 0 partial Jacobian and 0 Broyden evaluation(s) (tol = 0.0e+00)
Time = 0.1 s
Call solver.compute_before()
Set unknowns initial values: [0. 0.]
Call solver.compute()
NR - Reference call
Call fresidues with x = array([0., 0.])
solver : solver -> Converged (0.0000e+00) in 1 iterations, 0 complete, 0 partial Jacobian and 0 Broyden evaluation(s) (tol = 0.0e+00)
# Ending driver 'Euler' on 'coupledTanks' in 0.025 seconds
Call solver.clean_run
Compute calls for Euler.solver: 2
FULL_DEBUG
mode¶
The DEBUG
mode provides a detailed trace of calls done by the solver within the simulation system. When bad comes to worse, though, nothing is more valuable than values to analyze a simulation. This is the purpose of the FULL_DEBUG
mode. Its behavior will depend on the object. What you could expect for now is:
For all
System
objects: Record the values of inputs (including inwards) and outputs (including outwards)For
NonLinearSolver
: Record the computed Jacobian matrix and the evolution of the unknowns and the residues. These data are printed as comma-separated tables to ease their post-processing, such as plotting a graph of their evolution, e.g.For
TimeDriver
objects: Record actual simulation time steps.
Records for System
¶
[10]:
set_log(level=LogLevel.FULL_DEBUG, context="tank1", start_time=0.1)
system = CoupledTanks("coupledTanks", rho=1e3)
driver = system.add_driver(EulerExplicit(dt=0.1, time_interval=[0, 0.1]))
solver = driver.add_child(NonLinearSolver("solver", factor=1.0))
h1_0, h2_0 = (3, 1)
driver.set_scenario(
name="run",
init={"tank1.height": h1_0, "tank2.height": h2_0,}, # initial conditions
values={"pipe.D": 0.07, "pipe.L": 2.5, "tank1.area": 2,}, # fixed values
)
system.run_drivers()
Creation of new time-dependent inward 'height' within system 'tank1'
Creation of new time-dependent inward 'height' within system 'tank2'
------------------------------------------------------------
# Starting driver 'Euler' on 'coupledTanks'
Time = 0 s
solver : solver -> Converged (0.0000e+00) in 1 iterations, 1 complete, 0 partial Jacobian and 0 Broyden evaluation(s) (tol = 0.0e+00)
Time = 0.1 s
#### tank1 - Tank - Inputs
- inwards:
- area = 2
- rho = 1000
- height = 2.8844
- flowrate:
- value = 0
Call tank1.compute_before
#### tank1 - Tank - Outputs
- p_bottom:
- value = 28296
#### tank1 - Tank - Inputs
- inwards:
- area = 2
- rho = 1000
- height = 2.8844
- flowrate:
- value = -1.9114
Call tank1.compute_before
#### tank1 - Tank - Outputs
- p_bottom:
- value = 28296
solver : solver -> Converged (0.0000e+00) in 1 iterations, 0 complete, 0 partial Jacobian and 0 Broyden evaluation(s) (tol = 0.0e+00)
# Ending driver 'Euler' on 'coupledTanks' in 0.024 seconds
Call tank1.clean_run
Compute calls for coupledTanks.tank1: 6
Records for NonLinearSolver
¶
[11]:
set_log(level=LogLevel.FULL_DEBUG, context="solver", start_time=0.1)
system = CoupledTanks("coupledTanks", rho=1e3)
driver = system.add_driver(EulerExplicit(dt=0.1, time_interval=[0, 0.1]))
solver = driver.add_child(NonLinearSolver("solver", factor=1.0))
h1_0, h2_0 = (3, 1)
driver.set_scenario(
name="run",
init={"tank1.height": h1_0, "tank2.height": h2_0,}, # initial conditions
values={"pipe.D": 0.07, "pipe.L": 2.5, "tank1.area": 2,}, # fixed values
)
system.run_drivers()
Creation of new time-dependent inward 'height' within system 'tank1'
Creation of new time-dependent inward 'height' within system 'tank2'
Call solver.setup_run
****************************************
*
* Assemble mathematical problem
*
****************************************
Mathematical problem:
Unknowns [2]
tank1.flowrate.value = 0.0
tank2.flowrate.value = 0.0
Equations [2]
tank1.flowrate.value == pipe.Q1.value (loop) := 0.0
tank2.flowrate.value == pipe.Q2.value (loop) := 0.0
------------------------------------------------------------
# Starting driver 'Euler' on 'coupledTanks'
Time = 0 s
solver : solver -> Converged (0.0000e+00) in 1 iterations, 1 complete, 0 partial Jacobian and 0 Broyden evaluation(s) (tol = 0.0e+00)
Time = 0.1 s
solver : solver -> Converged (0.0000e+00) in 1 iterations, 0 complete, 0 partial Jacobian and 0 Broyden evaluation(s) (tol = 0.0e+00)
# Ending driver 'Euler' on 'coupledTanks' in 0.019 seconds
Call solver.clean_run
Compute calls for Euler.solver: 2
Records for TimeDriver
¶
[12]:
set_log(level=LogLevel.FULL_DEBUG, context="euler")
system = CoupledTanks("coupledTanks", rho=1e3)
driver = system.add_driver(EulerExplicit("euler", dt=0.1, time_interval=[0, 0.2]))
solver = driver.add_child(NonLinearSolver("solver", factor=1.0))
h1_0, h2_0 = (3, 1)
driver.set_scenario(
name="run",
init={"tank1.height": h1_0, "tank2.height": h2_0,}, # initial conditions
values={"pipe.D": 0.07, "pipe.L": 2.5, "tank1.area": 2,}, # fixed values
)
system.run_drivers()
Creation of new time-dependent inward 'height' within system 'tank1'
Creation of new time-dependent inward 'height' within system 'tank2'
Call euler.setup_run
Call solver.setup_run
****************************************
*
* Assemble mathematical problem
*
****************************************
Mathematical problem:
Unknowns [2]
tank1.flowrate.value = 0.0
tank2.flowrate.value = 0.0
Equations [2]
tank1.flowrate.value == pipe.Q1.value (loop) := 0.0
tank2.flowrate.value == pipe.Q2.value (loop) := 0.0
Transient variables: {'tank1.height': height := 0.0, 'tank2.height': height := 0.0}
Rate variables: OrderedDict()
Reset time to 0
------------------------------------------------------------
# Starting driver 'euler' on 'coupledTanks'
Call euler.compute
Reset time to 0
Apply initial conditions
tank1.area = 2
pipe.L = 2.5
pipe.D = 0.07
tank2.height = 1
tank1.height = 3
Reset rates
Time = 0 s
Call solver.run_once()
Call solver.compute_before()
Set unknowns initial values: [0. 0.]
Call solver.compute()
NR - Reference call
Call fresidues with x = array([0., 0.])
solver : solver -> Converged (0.0000e+00) in 1 iterations, 1 complete, 0 partial Jacobian and 0 Broyden evaluation(s) (tol = 0.0e+00)
Time = 0.1 s
Call solver.run_once()
Call solver.compute_before()
Set unknowns initial values: [0. 0.]
Call solver.compute()
NR - Reference call
Call fresidues with x = array([0., 0.])
solver : solver -> Converged (0.0000e+00) in 1 iterations, 0 complete, 0 partial Jacobian and 0 Broyden evaluation(s) (tol = 0.0e+00)
Time = 0.2 s
Call solver.run_once()
Call solver.compute_before()
Set unknowns initial values: [0. 0.]
Call solver.compute()
NR - Reference call
Call fresidues with x = array([0., 0.])
solver : solver -> Converged (0.0000e+00) in 1 iterations, 0 complete, 0 partial Jacobian and 0 Broyden evaluation(s) (tol = 0.0e+00)
# Ending driver 'euler' on 'coupledTanks' in 0.045 seconds
Time steps:
1.000000000000000056e-01
1.000000000000000056e-01
Call euler.clean_run
Call solver.clean_run
Compute calls for euler.solver: 3
Compute calls for euler: 1