1 Call stacks

Code
using CairoMakie
using Colors
using Graphs, MetaGraphsNext
using JuliaInterpreter, OrderedCollections

include("scripts/trace_call.jl")
include("scripts/plot_trace.jl")

using Ribasim
using Random

Random.seed!(1);

The plots below show the call stack within the Julia core for several important entrypoints. The function names are colored by the script in which they are defined, and the lines between the function names have random colors to be able to differentiate between them. Solid lines refer to calls to functions defined in the same script, dashed ones to functions defined in a different script. The plots are of high resolution so zooming in to particular parts is encouraged.

Note that these graphs are obtained by dynamic analysis, i.e. by running parts of the code with specific inputs. This means that there can be unshown paths through the code that are not reached for these particular inputs.

1.1 Parameter initialization

Parameter initialization is the process of reading the parameter values from the input files and storing them in data structures for easy access in the core. Most notable here is the convergence of many paths to load_structvector and parse_static_and_time, as these are needed for parameter initialization for most node types.

Code
using SQLite
toml_path = normpath(@__DIR__, "../../generated_testmodels/basic_transient/ribasim.toml")
config = Ribasim.Config(toml_path)
db_path = Ribasim.database_path(config)
db = SQLite.DB(db_path)

graph, verts = tracecall((Ribasim,), Ribasim.Parameters, (db, config))
close(db)

plot_graph(
    graph;
    squash_methods = [
        :n_neighbor_bounds_flow,
        :n_neighbor_bounds_control,
        :sort_by_function,
        :neighbortypes,
    ],
)

1.2 water_balance!

water_balance! is the right hand side function of the system of ODEs that is solved by the Ribasim core (for more details see here). The various formulate_flow! methods are for flow rates as determined by different node types.

Code
using OrdinaryDiffEqCore: get_du
model = Ribasim.Model(toml_path)
du = get_du(model.integrator)
(; u, p, t) = model.integrator
graph, verts = tracecall((Ribasim,), Ribasim.water_balance!, (du, u, p, t))
plot_graph(graph; max_depth = 4)

1.3 Allocation initialization

In this part of the code the data structures for allocation are set up. Most endpoints in allocation_init.jl set up data structures as defined in JuMP.jl.

Code
toml_path = normpath(
    @__DIR__,
    "../../generated_testmodels/main_network_with_subnetworks/ribasim.toml",
)
config = Ribasim.Config(toml_path)
db_path = Ribasim.database_path(config)
db = SQLite.DB(db_path)
p = Ribasim.Parameters(db, config)
empty!(p.allocation.subnetwork_ids)
empty!(p.allocation.main_network_connections)
graph, verts = tracecall((Ribasim,), Ribasim.initialize_allocation!, (p, config))
plot_graph(graph)

1.4 Allocation run

Running the allocation algorithm consists of running the optimization itself (which is handled in JuMP.jl), and all Ribasim functions around it are used for communicating data between the optimization problem and the physical layer, as well as gathering output data. Fore more information on the allocation algorithm see here.

Code
model = Ribasim.Model(toml_path)
graph, verts = tracecall((Ribasim,), Ribasim.update_allocation!, (model.integrator,))
plot_graph(graph)

1.5 Discrete control

Discrete control works by a FunctionCallingCallback, changing parameters when a change in control state is detected (see also here).

Code
toml_path =
    normpath(@__DIR__, "../../generated_testmodels/pump_discrete_control/ribasim.toml")
model = Ribasim.Model(toml_path)
(; u, t) = model.integrator
model.integrator.p.basin.storage0 .= [0.1, 100.0]
graph, verts =
    tracecall((Ribasim,), Ribasim.apply_discrete_control!, (u, t, model.integrator))
plot_graph(graph; prune_from = [:water_balance!], max_depth = 3)

1.6 Writing output

Writing output (currently) happens only after the full simulation is finished. For more information on the different output tables see here.

Code
toml_path = normpath(@__DIR__, "../../generated_testmodels/basic_transient/ribasim.toml")
model = Ribasim.Model(toml_path)
graph, verts = tracecall((Ribasim,), Ribasim.write_results, (model,))
plot_graph(graph; max_depth = 4)