Example 1. System with two processes, two parameters, one material.

ODYM example by Stefan Pauliuk, adapted for flodym

A simple MFA system with one material, a time horizon of 30 years (1980-2010), two processes, and a time-dependent parameter is analysed.

Diagram of the MFA system

The model equations are as follows:

  • \(a(t) = D(t)\) (exogenous input flow)

  • \(d(t) = \alpha (t)\cdot b(t)\) (recovery efficiency parameter)

  • \(a(t) + d(t) = b(t)\) (mass balance process 1)

  • \(b(t) = c(t) + d(t)\) (mass balance process 2)

From these equations the system solution follows:

  • \(c(t) = a(t) = D(t)\)

  • \(b(t) = \frac{1}{1-\alpha (t)}\cdot D(t)\)

  • \(c(t) = \frac{\alpha}{1-\alpha (t)}\cdot D(t)\)

1. Load flodym and other useful packages

[1]:
import numpy as np
import plotly.express as px

from flodym import (
    Dimension,
    DimensionSet,
    Process,
    Parameter,
    FlowDefinition,
    MFASystem,
    make_empty_flows,
)

2. Load data

Normally data would be loaded from a file / database, but since this is just a small example, the values are input directly into the code below.

[2]:
time = Dimension(name="Time", letter="t", items=list(range(1980, 2011)))
elements = Dimension(
    name="Elements",
    letter="e",
    items=[
        "single material",
    ],
)
dimensions = DimensionSet(dim_list=[time, elements])

parameters = {
    "D": Parameter(name="inflow", dims=dimensions, values=np.arange(0, 31).reshape(31, 1)),
    "alpha": Parameter(
        name="recovery rate",
        dims=dimensions,
        values=np.arange(2, 33).reshape(31, 1) / 34,
    ),
}

processes = {
    "sysenv": Process(name="sysenv", id=0),
    "process 1": Process(name="process 1", id=1),
    "process 2": Process(name="process 2", id=2),
}

3. Define flows and initialise them with zero values.

By defining them with the shape specified by the relevant dimensions, we can later ensure that when we update the values, these have the correct dimensions. Note that flows are automatically assigned their names based on the names of the processes they are connecting.

[3]:
flow_definitions = [
    FlowDefinition(
        from_process_name="sysenv", to_process_name="process 1", dim_letters=("t", "e")
    ),  # input
    FlowDefinition(
        from_process_name="process 1",
        to_process_name="process 2",
        dim_letters=("t", "e"),
    ),  # consumption
    FlowDefinition(
        from_process_name="process 2", to_process_name="sysenv", dim_letters=("t", "e")
    ),  # output
    FlowDefinition(
        from_process_name="process 2",
        to_process_name="process 1",
        dim_letters=("t", "e"),
    ),  # recovered material
]
flows = make_empty_flows(processes=processes, flow_definitions=flow_definitions, dims=dimensions)

4. Define the MFA System equations

We define a class with our system equations in the compute method. Afterwards we create an instance of this class, using the input data defined above. The class (system equations) can then easily be reused with different input data.

We just need to define the compute method with our system equations, as all the other things we need are inherited from the MFASystem class.

[4]:
class SimpleMFA(MFASystem):
    def compute(self):
        self.flows["sysenv => process 1"][...] = self.parameters[
            "D"
        ]  # the elipsis slice [...] ensures the dimensionality of the flow is not changed
        self.flows["process 1 => process 2"][...] = (
            1 / (1 - self.parameters["alpha"]) * self.parameters["D"]
        )
        self.flows["process 2 => sysenv"][...] = self.parameters["D"]
        self.flows["process 2 => process 1"][...] = (
            self.parameters["alpha"] / (1 - self.parameters["alpha"]) * self.parameters["D"]
        )
[5]:
mfa_example = SimpleMFA(
    dims=dimensions, processes=processes, parameters=parameters, flows=flows, stocks={}
)
mfa_example.compute()
[6]:
flow_a = mfa_example.flows["sysenv => process 1"].to_df()
flow_a = flow_a.reset_index(level="Elements", drop=True)
fig = px.line(flow_a, title="sysenv => process 1")
fig.show(renderer="notebook")
[7]:
flow_b = mfa_example.flows["process 1 => process 2"].to_df()
flow_b = flow_b.reset_index(level="Elements", drop=True)
fig = px.line(flow_b, title="process 1 => process 2")
fig.show(renderer="notebook")
[ ]: