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