Introduction

openLCA provides an API for inter-process communication (IPC) that can be used by any application written in any programming language (e.g. Python, JavaScript/TypeScript, .Net, Go, etc.)1. This IPC protocol is provided by an openLCA server which can be a running instance of the openLCA desktop application or a web-server with an openLCA back-end that exposes this protocol. An application can connect to such an IPC server to call functions in openLCA:


        +--------+                  +--------------------+
        | Client | <--------------> | openLCA IPC Server |
        +--------+                  +--------------------+
                    IPC - Protocol
                    * data management
                    * calculations
                    * result details
                    * ...

Starting an IPC server

In the openLCA desktop application, you can start an IPC server for the currently active database from the tools menu: Tools > Developer tools > IPC Server. This brings up the following dialog where you can start and stop the server:

Starting an IPC server in openLCA

For headless stand-alone servers, please see the next chapter.

Available protocols

In the dialog, you can select to start a standard IPC server or a gRPC server. The standard IPC server is based on the JSON-RPC protocol provided over HTTP. This protocol is easy to implement as it is just based on JSON and HTTP. For example, it can be directly used from a web-browser using the Fetch API.

The gRPC protocol is another option, especially when you are already familiar with it or when your platform has good support for this option. In this case, you can just generate the client side interface from the openLCA service declaration.

A third option is the REST API provided by openLCA web-services. This protocol is also just based on JSON and HTTP and especially useful when integrating the openLCA back-end into web-applications.

In this documentation we try to cover all these protocols as they just provide an interface to the same service back-end of the openLCA kernel. Also, all of these protocols are based on the openLCA Schema as the data exchange format. Thus, parameter and return types of the this documentations often link to their respective description in the openLCA schema documentation.

Client libraries and example applications

The table below lists some client libraries and demo applications based on the openLCA IPC protocol:

LanguageTypeProtocol
olca-opc.pyPythonClient libraryJSON-RPC, REST
olca-ipc.tsJavaScript/TypeScriptClient libraryJSON-RPC, REST
ProtoLCA-DemoC#DemogRPC
protolca-js-exampleJavaScript/NodeClient librarygRPC
olcarpc.pyPythonClient librarygRPC

1 openLCA is a Java application and if your application is written in a language that also runs on the Java virtual machine (like Java, Kotlin, Scala, Clojure etc.) it is recommended to directly use the openLCA Java API instead of an IPC server. The kernel of openLCA can be used independently from the user interface and can be integrated as a set of standard Java libraries in JVM based applications. For example, the integrated Python editor in openLCA is in fact a Python implementation for the JVM (Jython), with which you can call the Java API of openLCA.

Stand-alone servers

As shown in the section before, it is easy to start an IPC server in openLCA from the user interface but it is of course also possible to run these servers as stand-alone servers. The JSON-RPC and gRPC servers are part of the openLCA modules, the gdt-server provides an implementation of the Rest API of the openLCA IPC protocol. All these servers provide a common command line interface with which a server can be configured and started. The parameters of the configuration are listed below. All parameters are optional. By default a server uses the default openLCA workspace folder as used by the desktop application (~/openLCA-data-1.4) and connects to the database with the name database in that folder:


-data <path to data folder>

  The path to the data folder that contains the database and possible
  libraries. The folder structure need to follow the openLCA workspace
  structure, means the sub-folder `databases` of that folder contains the
  database and the sub-folder `libraries` possible data libraries to which
  the database is linked. If this parameter is not provided, the default
  openLCA workspace (currently `~/openLCA-data-1.4`) is taken as data folder.

-db <database>
  The name of the database in the data folder (only the name, not a full
  file path, must be provided); defaults to 'database'.

-port <port>
  The port of the server; defaults to 8080.

-native <path to native library folder>
  The path to the folder from which the native libraries should be
  loaded; defaults to the data folder.

-threads <number of calculation threads>
 	The number of parallel threads that can be used for calculations. Make sure
 	that the server has enough resources if you provide a larger number than 1
 	here; defaults to 1.

-timeout <minutes after which results are disposed>
 	The time in minutes after which results are cleaned up if they were not
 	disposed by the user. A value of <=0 means that no timeout should be
 	applied; defaults to 0.

--readonly <true | false>?
  If this flag is set, the server will run in readonly mode and modifying the
  database will not be possible.

-static <path to folder with static files>
  A path to a folder with static files that should be hosted by the server.
  This only has an effect if the server supports hosting of static files.

Startup scripts in openLCA

In the bin folder of the openLCA application, there are two scripts for starting a headless IPC server (without user interface involved):

  • ipc-server: for JSON-RPC based IPC servers
  • grpc-server: for gRPC based IPC servers

These scripts just take the name of a database in the default openLCA workspace as argument and will start a server at port 8080. For example, for Windows starting a headless IPC server for the database ecoinvent39 looks like this:

cd openLCA\bin
.\ipc-server.cmd ecoinvent39

A running server can be quit with Ctr+c. Note that you cannot connect to a database that is opened via the openLCA desktop application with a headless server. You can of course modify these scripts to your needs or re-package a server application under the respective license conditions.

Running with Docker

An IPC server can be of course also packaged as a Docker container and the gdt-server is especially useful for this. There are basically two types of gdt-server images: images with packaged LCA models (model images) and images without a model (service images).

Model images

A model image is an easy way to distribute and share one or more computable LCA models. Such models are often parameterized and can be calculated for a given set of parameter values on demand. Model images are typically shared as tar archives which can be loaded into a local image repository via the load command:

docker load -i gdt-server-{identifier}.tar

With docker image ls you should see the image then. A model image typically does not need any configuration (timeouts, number of threads etc.), you just run it and map the 8080 port of openLCA service in the container (note that -d runs the container in detached mode, --rm will delete it when it is stopped):

docker run -p 3000:8080 -d --rm gdt-server-{identifier}

In the example above, it maps the port 8080 in the container to the port 3000, the URL http://localhost:3000/api/version should then respond with the API version of the gdt-server.

Service images

A service image contains a gdt-server and can be configured via start parameters of a container. The components of such a service image are freely available in the Github container registry and can be composed via this Dockerfile, e.g.:

cd <workdir>
curl https://raw.githubusercontent.com/GreenDelta/gdt-server/main/Dockerfile \
  > Dockerfile \
  && docker build -t gdt-server .

A container can be started from such an image in the following way:

docker run \
  -p 3000:8080 \
  -v $HOME/openLCA-data-1.4:/app/data \
  --rm -d gdt-server \
  -db example --readonly

As above, it first maps the internal port to the port 3000 of the host. Also, a data folder needs to be mounted under the /app/data folder of the container. In this example, it maps the default openLCA workspace to that folder. More configuration parameters can be passed to the server via the start command of the container.

Examples

Python IPC - From scratch

In this section we will go through a complete example using the openLCA IPC interface from the olca-ipc.py Python package. As we will create everything from scratch, we first create an empty database and start an IPC server for that database:

In the Python code, we first import the required packages that we will use in our example. The olca-schema packages comes is a dependency of the olca-ipc package and contains the data type definitions of the openLCA model and some utility methods. We will use Pandas for formatting our data output and NumPy, which is a dependency of Pandas, for checking the calculation. Also, we will add type annotations in our code, compatible with Python 3.11.

import olca_ipc as ipc
import olca_schema as o
import pandas as pd
import numpy as np

from typing import Callable

A historic example

Our example was taken from Heijungs 19941 and extended a bit. First, we define the technosphere of our system which are 4 processes connected by 4 products:

technosphere = pd.DataFrame(
    data=[
        [1.0, -50.0, -1.0, 0.0],
        [-0.01, 1.0, -1.0, 0.0],
        [0.0, 0.0, 1.0, -1.0],
        [0.0, 0.0, 0.0, 100],
    ],
    columns=[
        "electricity production",
        "aluminium production",
        "aluminium foil production",
        "sandwitch package production",
    ],
    index=[
        "electricity [MJ]",
        "aluminium [kg]",
        "aluminium foil [kg]",
        "sandwitch package [Item(s)]",
    ],
)
print(technosphere)

When we print this data frame, we get the following table:

electricity productionaluminium productionaluminium foil productionsandwitch package production
electricity [MJ]1.00-50.0-1.00.0
aluminium [kg]-0.011.0-1.00.0
aluminium foil [kg]0.000.01.0-1.0
sandwitch package [Item(s)]0.000.00.0100.0

In the rows, we have our products, in the columns the processes. Inputs have negative and outputs positive values. Thus, for 100 sandwitch packages, we would need 1 kg of aluminium foil (this is how sandwitches were packed in the 90s)2.

Next, we define the interventions of these processes with the environment:

interventions = pd.DataFrame(
    data=[
        [0.0, -5.0, 0.0, 0.0],
        [-0.5, 0.0, 0.0, 0.0],
        [3.0, 0.0, 0.0, 0.0],
        [2.0, 10.0, 0.0, 1.0],
    ],
    columns=technosphere.columns,
    index=[
        "bauxite [kg]",
        "crude oil [kg]",
        "CO2 [kg]",
        "solid waste [kg]",
    ],
)
print(interventions)
electricity productionaluminium productionaluminium foil productionsandwitch package production
bauxite [kg]0.0-5.00.00.0
crude oil [kg]-0.50.00.00.0
CO2 [kg]3.00.00.00.0
solid waste [kg]2.010.00.01.0

In the paper, the inventory is calculated for 10 sandwitch packages as the final demand \(f\) of the system, which we can quickly do with NumPy now:

f = [
    0.0,
    0.0,
    0.0,
    10,
]
s = np.linalg.solve(technosphere.to_numpy(), f)
g = interventions.to_numpy() @ s
print(pd.DataFrame(g, index=interventions.index))

This gives the expected result:

bauxite [kg]-1.01
crude oil [kg]-5.10
CO2 [kg]30.60
solid waste [kg]22.52

Inventory calculations

Now we do the same in openLCA via the IPC interface. First, we create an IPC client that holds our connection data:

client = ipc.Client(8080)

As we have nothing in our database, we first need to create the units and flow properties (quantity kinds) in which the flows of the examples are measured:

mass_units = o.new_unit_group("Mass units", "kg")
energy_units = o.new_unit_group("Energy units", "MJ")
counting_units = o.new_unit_group("Counting units", "Item(s)")
mass = o.new_flow_property("Mass", mass_units)
energy = o.new_flow_property("Energy", energy_units)
count = o.new_flow_property("Number of items", counting_units)

client.put_all(
    mass_units,
    energy_units,
    counting_units,
    mass,
    energy,
    count,
)

While IPC server is running, you can also continue to use the openLCA user interface, just do not close the dialog of the server. When you refresh the navigation, you will see the newly created unit groups and flow properties:

However, typically you will not create units and flow properties but use the reference data from openLCA. For example, we can get the flow property Mass by its name:

print(client.get(o.FlowProperty, name="Mass").to_json())

This will print the JSON serialization of that flow property which is the internal communication format of the IPC interface (and also the standard openLCA data exchange format in general):

{
  "@type": "FlowProperty",
  "@id": "b24a123b-f5a1-40fb-a481-afeeb50f6159",
  "lastChange": "2023-01-26T13:36:37.954Z",
  "name": "Mass",
  "unitGroup": {
    "@type": "UnitGroup",
    "@id": "3e912f50-9490-473c-89fc-1393ed2eea03",
    "name": "Mass units"
  },
  "version": "01.00.000"
}

Next, we create the flows of the example. In the snippet below, it iterates over the rows of the data frames and creates a product or elementary flow for each row, extracting the unit from the row label and mapping the corresponding flow property:

def create_flow(
    row_label: str, fn: Callable[[str, o.FlowProperty], o.Flow]
) -> o.Flow:
    parts = row_label.split("[")
    name = parts[0].strip()
    unit = parts[1][0:-1].strip()
    match unit:
        case "kg":
            prop = mass
        case "MJ":
            prop = energy
        case "Item(s)":
            prop = count
    flow = fn(name, prop)
    client.put(flow)
    return flow


tech_flows = [create_flow(label, o.new_product) for label in technosphere.index]
envi_flows = [
    create_flow(label, o.new_elementary_flow) for label in interventions.index
]

Then we iterate over the columns of the data frames and create the corrsponding processes with their inputs and outputs of the flows we just created. One the diagonal of the technosphere matrix, the reference products of the respective processes are located and we set the these exchanges as the quantitative reference of the corresponding process:

def create_process(index: int, name: str) -> o.Process:
    process = o.new_process(name)

    def exchange(flow: o.Flow, value: float) -> o.Exchange | None:
        if value == 0:
            return None
        if value < 0:
            return o.new_input(process, flow, abs(value))
        else:
            return o.new_output(process, flow, value)

    for (i, tech_flow) in enumerate(tech_flows):
        value = technosphere.iat[i, index]
        e = exchange(tech_flow, value)
        if e and i == index:
            e.is_quantitative_reference = True

    for (i, envi_flow) in enumerate(envi_flows):
        value = interventions.iat[i, index]
        exchange(envi_flow, value)

    client.put(process)
    return process


processes = [
    create_process(index, name)
    for (index, name) in enumerate(technosphere.columns)
]

When you refresh the navigation in openLCA again, you should now see these new processes and flows:

Now we can calculate the inventory of this system. We create a calculation setup for the sandwitch packaging process as calculation target. We do not need to set the unit in the setup as it would take the unit of the quantitative reference of the process by default, but we need to set the amount as we want the result for 10 sandwitches but the process has 100 as quantitative reference. The calculation immediately returns a result object but this is maybe not ready yet, so we wait for the calculation to be finished via the wait_until_ready method:

setup = o.CalculationSetup(
    target=o.Ref(ref_type=o.RefType.Process, id=processes[3].id),
    unit=count.unit_group.ref_unit,  # "Item(s)"
    amount=10,
)
result = client.calculate(setup)
result.wait_until_ready()

When the result is ready, we can query the inventory from it:

inventory = result.get_total_flows()
print(
    pd.DataFrame(
        data=[
            (
                i.envi_flow.flow.name,
                i.envi_flow.is_input,
                i.amount,
                i.envi_flow.flow.ref_unit,
            )
            for i in inventory
        ],
        columns=["Flow", "Is input?", "Amount", "Unit"],
    )
)

This prints the following expected values:

          Flow  Is input?  Amount Unit
0          CO2      False   30.60   kg
1    crude oil       True    5.10   kg
2  solid waste      False   22.52   kg
3      bauxite       True    1.01   kg

Finally, when we do not need the result anymore, we need to dispose it so that allocated resources can be freed on the openLCA side:

result.dispose()

Full workbook

Below is the full example. Note that you can run it as a note-book, cell by cell, in VS Code:

# %%
# ANCHOR: imports
import olca_ipc as ipc
import olca_schema as o
import pandas as pd
import numpy as np

from typing import Callable

# ANCHOR_END: imports


# %%
# ANCHOR: techsphere
technosphere = pd.DataFrame(
    data=[
        [1.0, -50.0, -1.0, 0.0],
        [-0.01, 1.0, -1.0, 0.0],
        [0.0, 0.0, 1.0, -1.0],
        [0.0, 0.0, 0.0, 100],
    ],
    columns=[
        "electricity production",
        "aluminium production",
        "aluminium foil production",
        "sandwitch package production",
    ],
    index=[
        "electricity [MJ]",
        "aluminium [kg]",
        "aluminium foil [kg]",
        "sandwitch package [Item(s)]",
    ],
)
print(technosphere)
# ANCHOR_END: techsphere


# %%
# ANCHOR: envisphere
interventions = pd.DataFrame(
    data=[
        [0.0, -5.0, 0.0, 0.0],
        [-0.5, 0.0, 0.0, 0.0],
        [3.0, 0.0, 0.0, 0.0],
        [2.0, 10.0, 0.0, 1.0],
    ],
    columns=technosphere.columns,
    index=[
        "bauxite [kg]",
        "crude oil [kg]",
        "CO2 [kg]",
        "solid waste [kg]",
    ],
)
print(interventions)
# ANCHOR_END: envisphere


# %%
# ANCHOR: numsol
f = [
    0.0,
    0.0,
    0.0,
    10,
]
s = np.linalg.solve(technosphere.to_numpy(), f)
g = interventions.to_numpy() @ s
print(pd.DataFrame(g, index=interventions.index))
# ANCHOR_END: numsol


# %%
# ANCHOR: mkclient
client = ipc.Client(8080)
# ANCHOR_END: mkclient


# %%
# ANCHOR: units
mass_units = o.new_unit_group("Mass units", "kg")
energy_units = o.new_unit_group("Energy units", "MJ")
counting_units = o.new_unit_group("Counting units", "Item(s)")
mass = o.new_flow_property("Mass", mass_units)
energy = o.new_flow_property("Energy", energy_units)
count = o.new_flow_property("Number of items", counting_units)

client.put_all(
    mass_units,
    energy_units,
    counting_units,
    mass,
    energy,
    count,
)
# ANCHOR_END: units


# %%
# ANCHOR: mass
print(client.get(o.FlowProperty, name="Mass").to_json())
# ANCHOR_END: mass


# %%
# ANCHOR: flows
def create_flow(
    row_label: str, fn: Callable[[str, o.FlowProperty], o.Flow]
) -> o.Flow:
    parts = row_label.split("[")
    name = parts[0].strip()
    unit = parts[1][0:-1].strip()
    match unit:
        case "kg":
            prop = mass
        case "MJ":
            prop = energy
        case "Item(s)":
            prop = count
    flow = fn(name, prop)
    client.put(flow)
    return flow


tech_flows = [create_flow(label, o.new_product) for label in technosphere.index]
envi_flows = [
    create_flow(label, o.new_elementary_flow) for label in interventions.index
]
# ANCHOR_END: flows


# %%
# ANCHOR: processes
def create_process(index: int, name: str) -> o.Process:
    process = o.new_process(name)

    def exchange(flow: o.Flow, value: float) -> o.Exchange | None:
        if value == 0:
            return None
        if value < 0:
            return o.new_input(process, flow, abs(value))
        else:
            return o.new_output(process, flow, value)

    for (i, tech_flow) in enumerate(tech_flows):
        value = technosphere.iat[i, index]
        e = exchange(tech_flow, value)
        if e and i == index:
            e.is_quantitative_reference = True

    for (i, envi_flow) in enumerate(envi_flows):
        value = interventions.iat[i, index]
        exchange(envi_flow, value)

    client.put(process)
    return process


processes = [
    create_process(index, name)
    for (index, name) in enumerate(technosphere.columns)
]
# ANCHOR_END: processes


# %%
# ANCHOR: calc
setup = o.CalculationSetup(
    target=o.Ref(ref_type=o.RefType.Process, id=processes[3].id),
    unit=count.unit_group.ref_unit,  # "Item(s)"
    amount=10,
)
result = client.calculate(setup)
result.wait_until_ready()
# ANCHOR_END: calc


# %%
# ANCHOR: inventory
inventory = result.get_total_flows()
print(
    pd.DataFrame(
        data=[
            (
                i.envi_flow.flow.name,
                i.envi_flow.is_input,
                i.amount,
                i.envi_flow.flow.ref_unit,
            )
            for i in inventory
        ],
        columns=["Flow", "Is input?", "Amount", "Unit"],
    )
)
# ANCHOR_END: inventory


# %%
# ANCHOR: free-inventory
result.dispose()
# ANCHOR_END: free-inventory

# %%

1

Reinout Heijungs: A generic method for the identification of options for cleaner products. Ecological Economics, Volume 10, Issue 1, 1994, Pages 69-81, ISSN 0921-8009, https://doi.org/10.1016/0921-8009(94)90038-8.

2

it is of course just an illustrative example and not real data

Calculation parameters

This example shows the calculation of an LCA model with different parameter values. We will use the openLCA Python IPC module and the REST API in this example. First, we need to make sure that the current olca-ipc package is installed. Currently the latest version for openLCA 2 is 2.0.0a4:

pip install olca-ipc==2.0.0a4

We connect to a REST API in this example and assume that it is running at http://192.168.142.136:3000

import olca_schema as o
import olca_ipc.rest as rest
import json

client = rest.RestClient("http://192.168.142.136:3000")

To check that we can access the server, we can also just call this URL in a browser: http://192.168.142.136:3000/api/version, which should then return the API version of that server.

Next, we check which models (product systems) are available and select the first model for our calculations:

models = client.get_descriptors(o.ProductSystem)
for model in models:
    print(f"{model.name} :: {model.id}")
model = models[0]

This should print something like this depending on the available models:

battery model without EoL :: 59327d11-9a0d-477b-9cc0-060ca74b6327
battery model with EoL :: eb31db8a-8102-453d-a549-be989e83c592

Again, we could just query an URL for this, for the example: http://192.168.142.136:3000/data/product-systems

We do the same for the available impact assessment methods ( http://192.168.142.136:3000/data/methods):

methods = client.get_descriptors(o.ImpactMethod)
for method in methods:
    print(f"{method.name} :: {method.id}")
method = methods[0]

This should print something like this:

EF 3.0 Method (adapted) :: b4571628-4b7b-3e4f-81b1-9a8cca6cb3f8
...

With the reference to a product system and LCIA method, we can create a calculation setup:

setup = o.CalculationSetup(
    target=model,
    impact_method=method,
)
print(json.dumps(setup.to_dict(), indent=2))

The objects of the openLCA schema can be easily translated to JSON as done in the snippet above. This is the payload that is posted to the server in a calculation request:

{
  "impactMethod": {
    "@type": "ImpactMethod",
    "@id": "b4571628-4b7b-3e4f-81b1-9a8cca6cb3f8",
    "category": "openLCA LCIA methods 2_1_2",
    "name": "EF 3.0 Method (adapted)"
  },
  "target": {
    "@type": "ProductSystem",
    "@id": "59327d11-9a0d-477b-9cc0-060ca74b6327",
    "name": "battery model without EoL"
  }
}

We can calculate the setup like this:

result = client.calculate(setup)
result.wait_until_ready()
impacts = result.get_total_impacts()
for i in impacts:
    assert i.impact_category
    print(f"{i.impact_category.name} {i.amount} {i.impact_category.ref_unit}")
result.dispose()

As shown above, we should dispose a result when we do not need it anymore. The snippet prints something like:

...
Climate change 0.7891638059816873 kg CO2 eq
...

For parameterized models, the default parameter values are applied in a calculation but we can change them in each calculation. We can query the parameters of a model like this:

parameters = client.get_parameters(o.ProductSystem, model.id)
for param in parameters:
    print(f"parameter: {param.name} = {param.value}")
param = parameters[0]

For our example, we just have a single parameter, number_of_recharges with a default value of 1.0:

parameter: number_of_recharges = 1.0

The corresponding URL is: http://192.168.142.136:3000/data/product-systems/59327d11-9a0d-477b-9cc0-060ca74b6327/parameters

Finally, we run a set of calculations over a parameter range:

for x in range(0, 55, 5):
    setup = o.CalculationSetup(
        target=model,
        impact_method=method,
        parameters=[
            o.ParameterRedef(name=param.name, value=x, context=param.context)
        ],
    )
    result = client.calculate(setup)
    assert result
    result.wait_until_ready()
    impacts = result.get_total_impacts()
    for i in impacts:
        if i.impact_category.name == "Climate change":
            print(f"{param.name}: {x} => {i.amount : .3f} kg CO2 eq")
    result.dispose()

For the example, it prints something like this:

number_of_recharges: 0 =>  0.788 kg CO2 eq
number_of_recharges: 5 =>  0.792 kg CO2 eq
number_of_recharges: 10 =>  0.796 kg CO2 eq
number_of_recharges: 15 =>  0.800 kg CO2 eq
number_of_recharges: 20 =>  0.803 kg CO2 eq
number_of_recharges: 25 =>  0.807 kg CO2 eq
number_of_recharges: 30 =>  0.811 kg CO2 eq
number_of_recharges: 35 =>  0.814 kg CO2 eq
number_of_recharges: 40 =>  0.818 kg CO2 eq
number_of_recharges: 45 =>  0.822 kg CO2 eq
number_of_recharges: 50 =>  0.826 kg CO2 eq

Data management

The openLCA IPC protocol is based on the openLCA Schema as data exchange format. Thus, parameter and return types of this documentation often link to their respective description in the openLCA schema documentation. The openLCA schema is a typed data format with the following principles:

  • There are stand-alone entities like processes or flows, called root entities, and composition entities that can only exist within another entity, like the inputs and outputs (called exchanges) of a process.
  • There is a uniform reference concept in the format: if an entity A references an entity B it only stores a reference to B that is of type Ref. This reference contains the type (if it cannot be inferred from field in A), the ID, and some optional meta-data of B.
    +---+   Ref   +---+
    | A |  ---->  | B |
    +---+         +---+

For example, an output in a process is of type Exchange and an exchange contains a reference to a flow. In addition, instances of the Ref type are often used as data set descriptors: instead of loading the full data set it is often enough to just display some meta-data of an entity (for example in search results).

Many of the data management functions are the same for all root entity types. Thus, the respective type is often just an additional parameter of a method call. The table below shows the root entity types and their parameter value in the Rest API (multiple values can map to the same type for convenience):

Root entity typeRest parameter
Actoractor, actors
Categorycategory, categories
Currencycurrency, currencies
DQSystemdq-system, dq-systems
Epdepd, epds
Flowflow, flows
FlowPropertyflow-property, flow-properties
ImpactCategoryimpact-category, impact-categories
ImpactMethodimpact-method, method, impact-methods, methods
Locationlocation, locations
Parameterparameter, parameters
Processprocess, processes
ProductSystemproduct-system, product-systems, model, models
Projectproject, projects
Resultresult, results
SocialIndicatorsocial-indicator, social-indicators
Sourcesource, sources
UnitGroupunit-group, unit-groups

Get descriptors

This function is useful for exploring the content of a database. It returns a list of data set descriptors for a given data set type. A descriptor contains just a few information, like the name or category path, to understand the content of a data set. Descriptors are valid data set references; they can be used to reference a data set in a given context (e.g. referencing a process as calculation target in a calculation setup).

Rest APIGET /data/{type}
JSON-RPCdata/get/descriptors
Python/IPCClient.get_descriptors
Return typeList[Ref]

Examples

Python IPC

import olca_ipc as ipc
import olca_schema as o

client = ipc.Client()
refs = client.get_descriptors(o.FlowProperty)
for ref in refs:
    print(ref.name)
# Area
# Area*time
# Duration
# Energy

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "data/get/descriptors",
      params: {
        "@type": "FlowProperty"
      }
    })
  });
  let descriptors = await resp.json();
  console.log(descriptors);
  // {
  //   jsonrpc: "2.0",
  //   id: 1,
  //   result: [
  //     {
  //       "@type": "FlowProperty",
  //       "@id": "838aaa20-0117-11db-92e3-0800200c9a66",
  //       name: "Goods transport (mass*distance)",
  //       category: "Technical flow properties",
  //       refUnit: "t*km"
  //     },

Get a full data set by ID

This method returns the full data set of a given type and ID.

Rest APIGET /data/{type}/{id}
JSON-RPCdata/get
Python/IPCClient.get
Return typeE > RootEntity

Examples

Python IPC

import olca_ipc as ipc
import olca_schema as o

client = ipc.Client()
process = client.get(o.Process, "eacc4872-6f4e-4ff1-946e-c1bddeda24be")
print(process)
# Process(
#   id='eacc4872-6f4e-4ff1-946e-c1bddeda24be',
#   exchanges=[Exchange(amount=1.0, flow=Ref(id='b254bbdf- ...

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "data/get",
      params: {
        "@type": "Process",
        "@id": "eacc4872-6f4e-4ff1-946e-c1bddeda24be"
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  // {
  //   jsonrpc: "2.0",
  //   result: {
  //     "@type": "Process",
  //     "@id": "eacc4872-6f4e-4ff1-946e-c1bddeda24be",
  //     name: "blast furnace gas, Recycled Content cut-off",
  //     processType: "LCI_RESULT",

Curl

endpoint="http://localhost:8080"
process_id="eacc4872-6f4e-4ff1-946e-c1bddeda24be"

curl "$endpoint/data/process/$process_id"
# { "@type":"Process",
#   "@id":"eacc4872-6f4e-4ff1-946e-c1bddeda24be",
#   "name":"blast furnace gas, Recycled Content cut-off

Get a full data set by name

This method returns a full data set for the given type and name. Note that the name does not have to be unique in an openLCA database, and in this case, it will just return the first entity from the database with the given name.

Rest APIGET /data/{type}/name/{name}
JSON-RPCdata/get
Python/IPCClient.get
Return type:E > RootEntity

Examples

Python IPC

import olca_ipc as ipc
import olca_schema as o

client = ipc.Client()
mass = client.get(o.FlowProperty, name="Mass")
print(mass)
# FlowProperty(
#   name='Mass',
#   id='93a60a56-a3c8-11da-a746-0800200b9a66',
#   unit_group=Ref(id='93a60a57-a4c8-11da-a746-0800200c9a66', ...

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "data/get",
      params: {
        "@type": "FlowProperty",
        "name": "Mass",
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  // {
  //   jsonrpc: "2.0",
  //   result: {
  //     "@type": "FlowProperty",
  //     "@id": "93a60a56-a3c8-11da-a746-0800200b9a66",
  //     name: "Mass",
  //     category: "Technical flow properties",
  //     version: "00.00.000",
  //     flowPropertyType: "PHYSICAL_QUANTITY",

Get the descriptor of a data set

Returns the descriptor of the data set with the given type and ID.

Rest APIGET /data/{type}/{id}/info
JSON-RPCdata/get/descriptor
Python/IPCClient.get_descriptor
Return type:Ref

Examples

Python IPC

import olca_ipc as ipc
import olca_schema as o

client = ipc.Client()
mass_ref = client.get_descriptor(o.FlowProperty, name="Mass")
print(mass_ref)
# Ref(
#   id='93a60a56-a3c8-11da-a746-0800200b9a66',
#   category='Technical flow properties',
#   name='Mass',
# ...

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "data/get/descriptor",
      params: {
        "@type": "Process",
        "@id": "eacc4872-6f4e-4ff1-946e-c1bddeda24be",
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  // {
  //   jsonrpc: "2.0",
  //   id: 1,
  //   result: {
  //     "@type": "Process",
  //     "@id": "eacc4872-6f4e-4ff1-946e-c1bddeda24be",
  //     name: "blast furnace gas, Recycled Content cut-off",
  //     processType: "LCI_RESULT",
  //     flowType: "PRODUCT_FLOW"
  //   }
  // }

Get all data sets of a given type

Returns all data sets of a given type from a database. This is not a practical method for all types of data sets and may is not available in a specific context. For example, it is fine to get all unit groups but a server would go down when you query for all processes in a large database. Typically, you just want to query all descriptors of a data set type.

Rest APIGET /data/{type}/all
JSON-RPCdata/get/all
Python IPCClient.get_all
Return type:List[E > RootEntity]

Examples

Python IPC

import olca_ipc as ipc
import olca_schema as o

client = ipc.Client()
for group in client.get_all(o.UnitGroup):
    print(group.name)
# Units of area
# Units of energy
# Units of length
# Units of mass
# ...

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "data/get/all",
      params: {
        "@type": "UnitGroup"
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  // {
  //   jsonrpc: "2.0",
  //   result: [
  //     {
  //       "@type": "UnitGroup",
  //       "@id": "838aaa21-0117-11db-92e3-0800200c9a66",
  //       name: "Units of mass*length",
  //       category: "Technical unit groups",
  //       version: "00.00.000",
  //       defaultFlowProperty: {

Get the parameters of a data set

Returns the (local) parameters of the specified data set. In case of processes and impact categories, a list of parameters is returned. For product systems, the respective parameter redefinitions are returned.

Rest APIGET /data/{type}/{id}/parameters
JSON-RPCdata/get/parameters
Python/IPCClient.get_parameters
Return type*

*Return type:

Examples

Python IPC

import olca_ipc as ipc
import olca_schema as o

client = ipc.Client()
params = client.get_parameters(
    o.ProductSystem, "0db1eda6-a34e-4c82-b06b-19f27c92495a"
)
for p in params:
    print(f"{p.name}: {p.value}")
# param1: 42
# param2: 21
# ...

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "data/get/parameters",
      params: {
        "@type": "ProductSystem",
        "@id": "0db1eda6-a34e-4c82-b06b-19f27c92495a",
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  // {
  //   jsonrpc: "2.0",
  //   result: [
  //     { name: "param_a", value: 994, isProtected: false },
  //     { name: "param_b", value: 0.7237, isProtected: false },
  //     { name: "param_c", value: 0, isProtected: false },

Get providers of technosphere flows

Technosphere flows are products or waste flows that can be linked in a product system model. For products, the providers are processes that produce this product as output. For waste flows, providers are waste treatment processes with an input of that waste flow. These provider and flow pairs can be linked to product inputs and waste outputs of other processes in a product system.

Rest APIGET /data/providers
JSON-RPCdata/get/providers
Python IPCClient.get_providers
Return type:List[TechFlow]

Examples

Python IPC

import olca_ipc as ipc

client = ipc.Client()
for p in client.get_providers()[:5]:
    print(p)
# TechFlow(
#   provider=Ref(id='71dab7ae-4d58-4f73-8449-5967c296bcde', ...
#   flow=Ref(id='7f6bb533-2d3c-43a6-ac60-6eef299d7c52', ...

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "data/get/providers",
    })
  });
  let v = await resp.json();
  console.log(v);
  // {
  //   jsonrpc: "2.0",
  //   result: [
  //     {
  //       provider: {
  //         "@type": "Process",
  //         "@id": "ba98a130-d92c-4c40-8fe9-c8cab5b956ab",
  //         name: "electricity production, wind, 1-3MW turbine, onshore",
  //         processType: "LCI_RESULT",
  //         flowType: "PRODUCT_FLOW"
  //       },

Get providers for a flow

This method returns the processes that produce a given product (as output) or provide the treatment of a given waste flow (as input). Thus, the flow parameter of this method needs to be a product or waste flow (or an ID of that flow). The returned providers can be linked to respective product inputs or waste outputs of other processes in a product system.

Rest APIGET /data/providers/{flow-id}
JSON-RPCdata/get/providers
Python IPCClient.get_providers
Return type:List[TechFlow]

Examples

Python IPC

import olca_ipc as ipc
import olca_schema as o

client = ipc.Client()
providers = client.get_providers(
    flow=o.Ref(id="7f6bb533-2d3c-43a6-ac60-6eef299d7c52")
)
for p in providers:
    print(p)
# TechFlow(
#   provider=Ref(id='71dab7ae-4d58-4f73-8449-5967c296bcde', ...
#   flow=Ref(id='7f6bb533-2d3c-43a6-ac60-6eef299d7c52', ...

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "data/get/providers",
      params: {
        // only the ID is required here
        "@type": "Flow",
        "@id": "66c93e71-f32b-4591-901c-55395db5c132",
        name: "electricity, high voltage"
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  // {
  //   jsonrpc: "2.0",
  //   result: [
  //     {
  //       provider: {
  //         "@type": "Process",
  //         "@id": "ba98a130-d92c-4c40-8fe9-c8cab5b956ab",
  //         name: "electricity production, wind, 1-3MW turbine, onshore",
  //         processType: "LCI_RESULT",
  //         flowType: "PRODUCT_FLOW"
  //       },

Insert or update a data set

Inserts or updates a provided data set in the database. This method is not available when a server runs in read-only mode.

Rest APIPUT /data/{type}
JSON-RPCdata/put
Python/IPCclient.put
Request bodyE > RootEntity
Return type:Ref

Examples

Python IPC

import olca_ipc as ipc
import olca_schema as o

client = ipc.Client()
ref = client.put(o.Source(name="Inter-process communication with openLCA"))
print(ref)
# Ref(
#  id="16cba9b2-2987-4735-9f3e-96fedf8449dd",
#  name="Inter-process communication with openLCA",
# ...

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "data/put",
      params: {
        "@type": "Source",
        "@id": "91bbc461-4ce1-4f83-b95d-de0909575174",
        "name": "Inter-process communication with openLCA",
        // ...
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  // {
  //   jsonrpc: "2.0",
  //   id: 1,
  //   result: {
  //     "@type": "Source",
  //     "@id": "91bbc461-4ce1-4f83-b95d-de0909575174",
  //     name: "Inter-process communication with openLCA"
  //   }
  // }

Create a product system

This method creates a product system for a given process. It recursively links the processes of that system according to the given linking configuration.

Rest APIGET /data/{type}/all
JSON-RPCdata/get/all
Python IPCClient.get_all
ParameterRef[Process]
ParameterLinkingConfig
Return type:Ref[ProductSystem]

Examples

Python IPC

import olca_ipc as ipc
import olca_schema as o

client = ipc.Client()
process_ref = client.find(o.Process, "aluminium foil production")
config = o.LinkingConfig(
    prefer_unit_processes=True,
    provider_linking=o.ProviderLinking.PREFER_DEFAULTS,
)
system_ref = client.create_product_system(process_ref, config)
print(f"created product system {system_ref.name}, id={system_ref.id}")
# created product system aluminium foil production, id=fbc33e47-ee...

Delete a data set

Deletes the specified data set from the database. This method is not available when the server runs in read-only mode.

Rest APIDELETE /data/{type}/{id}
JSON-RPCdata/delete
Python/IPCClient.delete
Return type:Ref

Examples

Python IPC

import olca_ipc as ipc
import olca_schema as o

client = ipc.Client()
ref = client.delete(
    o.Ref(ref_type=o.RefType.Source, id="16cba9b2-2987-4735-9f3e-96fedf8449dd")
)
print(ref)
# Ref(
#     id="16cba9b2-2987-4735-9f3e-96fedf8449dd",
#     name="Inter-process communication with openLCA",
# ...

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "data/delete",
      params: {
        "@type": "Source",
        "@id": "91bbc461-4ce1-4f83-b95d-de0909575174",
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  // {
  //   jsonrpc: "2.0",
  //   result: {
  //     "@type": "Source",
  //     "@id": "91bbc461-4ce1-4f83-b95d-de0909575174",

Calculation and results

When starting a calculation, it is first scheduled in a calculation queue. This is because multiple calculations could be started at the same time and calculations may need to wait for other calculations first to be finished. Thus, a calculation directly returns a ResultState object with a unique identifier. With this identifier, the state of the calculation can be retrieved from the server. It is then also the ID of the corresponding result when the calculation is finished. When a result is not needed anymore, the dispose method should be called so that the allocated resources of the result can be released.

The idea of the result interface is not to provide some ready-to-use charts and tables but to provide all possible building blocks with which such higher level result views can be created (charts, tables, upstream trees, Sankey diagrams). Thus, the result interface has many methods that often look quite similar but they have their purpose for efficiently creating higher level result views.

Result elements

Depending on the calculation setup and the calculated model, results can be retrieved for different elements, these are:

  • Technosphere flows, tech-flows: the product and waste flows of the product system with their corresponding providers.
  • Intervention flows, envi-flows: typically elementary flows but also unconnected product or waste inputs or outputs of the processes in the system. These flows cross the boundary to the environment of the system and form the inventory result of the system. In case of a regionalized calculation, an intervention flow is a pair of flow and location.
  • Impact categories, impact-categories: the impact categories of the impact assessment method selected in the calculation setup.
  • costs: life cycle costs

Technosphere flows are always present. All other result elements are only available if the calculated model provides these elements and/or if the corresponding options are set in the calculation setup.

Mathematical relations

Where possible, a short formula for calculating a respective result is provided in the following documentation of the respective functions. These formulas are based on standard matrix algebra for LCA computations. However, this does not mean that a respective result is calculated by exactly using this formula.

Calculate results

This method schedules a new calculation for a given setup. It directly returns a state object that contains the result ID (field @id). With this ID the the result state can be queried.

RESTPOST result/calculate
IPCresult/calculate
Python IPCClient.calculate
Return typeResultState
Parameter typeCalculationSetup

Examples

Python IPC

import olca_ipc as ipc
import olca_schema as o

# create a calculation setup
setup = o.CalculationSetup(
    target=o.Ref(
        ref_type=o.RefType.ProductSystem,
        id="0db1eda6-a34e-4c82-b06b-19f27c92495a",
    ),
    impact_method=o.Ref(id="b4571628-4b7b-3e4f-81b1-9a8cca6cb3f8"),
    nw_set=o.Ref(id="867fe119-0b5c-38a0-a3e6-1d845ffaedd5"),
)

# run a calculation
client = ipc.Client()
result: ipc.Result = client.calculate(setup)

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "result/calculate",
      params: {
        target: {
          "@type": "ProductSystem",
          "@id": "0db1eda6-a34e-4c82-b06b-19f27c92495a"
        },
        impactMethod: {
          "@id": "b4571628-4b7b-3e4f-81b1-9a8cca6cb3f8",
        },
        nwSet: {
          "@id": "867fe119-0b5c-38a0-a3e6-1d845ffaedd5",
        },
        withCosts: true,
        amount: 1.0
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  // {
  //   jsonrpc: "2.0",
  //   result: {
  //     "@id": "e316a369-bf5b-4c25-a61f-3492cfca9535",
  //     isReady: false,
  //     isScheduled: true,
  //     time: 1671187585187
  //   },
  //   id: 1
  // }

Get state

This method returns the result state of a calculation. When a calculation is started, it is scheduled in a calculation queue first. The calculation directly returns a result state with a unique ID (field @id) of the result. Only when the state of a result is ready, calculation results can be retrieved.

APIMethod
RESTGET result/{result-id}/state
IPCresult/state
Python IPCResult.get_state
Return typeResultState

Examples

Python IPC

state = result.get_state()
if state.error:
    print(f"calculation failed: {state.error}")
    exit(-1)

# actively waiting for a result
import time

while not result.get_state().is_ready:
    time.sleep(1)
    print("waiting ...")

# or better do this:
state = result.wait_until_ready()
print(f"result id: {state.id}")

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "result/state",
      params: {
        "@id": "e316a369-bf5b-4c25-a61f-3492cfca9535",
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  // {
  //   jsonrpc: "2.0",
  //   result: {
  //     "@id": "e316a369-bf5b-4c25-a61f-3492cfca9535",
  //     isReady: true,
  //     isScheduled: false,
  //     time: 1671187586993
  //   },
  //   id: 1
  // }

Dispose result

This method should be always called when a result is not needed anymore. It disposes the result and releases allocated resources of that result.

APIMethod
RESTPOST result/{result-id}/dispose
IPCresult/dispose
Python IPCResult.dispose
Return typeResultState

Examples

Python IPC

# it is important to dispose a result when it is not needed anymore
result.dispose()

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "result/dispose",
      params: {
        "@id": "e316a369-bf5b-4c25-a61f-3492cfca9535",
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  // {
  //    jsonrpc: "2.0",
  //    result: {
  //      "@id": "e316a369-bf5b-4c25-a61f-3492cfca9535"
  //    },
  //    id: 1
  //  }

Monte Carlo Simulations

Running a Monte Carlo Simulation is similar to a normal calculation. You first call the simulate method with a calculation setup. This schedules a first simulation run. When the calculation is ready, the result can be queried like any other result. For each subsequent iteration you call the next method on that result which schedules a new iteration, that again returns a result that can be queried, and so on. Old iteration results are disposed automatically when a new iteration is started but you should dispose the last result when it is not needed anymore.

The API method for the first iteration is:

RESTPOST result/simulate
IPCresult/simulate
Python IPCClient.simulate
Return typeResultState
Parameter typeCalculationSetup

For each subsequent calculation, the API method is:

RESTPOST result/{result-id}/simulate/next
IPCresult/simulate/next
Python IPCResult.simulate_next
Return typeResultState
Parameter typeresult id

Examples

Python IPC

import random as rand
import statistics as stats

import olca_ipc as ipc
import olca_schema as o

client = ipc.Client()

# schedule a first iteration
print("run iteration 1")
result = client.simulate(
    o.CalculationSetup(
        target=o.Ref(
            ref_type=o.RefType.ProductSystem,
            id="7d1cbce0-b5b3-47ba-95b5-014ab3c7f569",
        ),
        impact_method=o.Ref(id="99b9d86b-ec6f-4610-ba9f-68ebfe5691dd"),
    )
)
result.wait_until_ready()

# get the result for some indicator from the first iteration
indicator = rand.choice(result.get_impact_categories())
val = lambda: result.get_total_impact_value_of(indicator).amount
xs: list[float] = [val()]

# collect the values from 99 more iterations
for i in range(0, 99):
    print(f"run iteration {i+2}")
    result.simulate_next()
    result.wait_until_ready()
    xs.append(val())

result.dispose()

# plot the results in a simple histogram
bucket_count = 15
quantiles = stats.quantiles(xs, n=bucket_count + 1, method="inclusive")
buckets = [0] * bucket_count
for x in xs:
    bucket = 0
    for i in range(1, bucket_count):
        if x <= quantiles[i]:
            bucket += 1
    buckets[bucket] += 1
for i in buckets:
    print(f"|{'#' * i}")

# ...
# run iteration 98
# run iteration 99
# run iteration 100
#
# |#######
# |######
# |######
# |######
# |######
# |#######
# |######
# |######
# |######
# |######
# |#######
# |######
# |######
# |######
# |#############

Technosphere flows

This method returns the \(n\) technosphere flows of a result. These are the flows by which the processes of the calculated system are linked. Each technosphere flow is a pair of a product or waste flow and a provider where the provider is typically a process but can also be a product system (a sub-system) or even another result. The technosphere matrix \(A\) is symmetrically indexed by these technosphere flows:

$$ A \in \mathbb{R}^{n \times n} $$

RESTresult/{result-id}/tech-flows
IPCresult/tech-flows
Python IPCResult.get_tech_flows
Return typeList[TechFlow]

Examples

Python IPC

tech_flows = result.get_tech_flows()
print(
    pd.DataFrame(
        [
            (tf.provider.name, tf.flow.name, tf.flow.ref_unit)
            for tf in tech_flows
        ],
        columns=["Provider", "Flow", "Unit"],
    ).head()
)
#                               Provider                      Flow  Unit
# 0  Fresh wheat, corn, rice, and oth...  Fresh wheat, corn, ric...  USD
# 1       Synthetic dyes and pigments...       Synthetic dyes an...  USD
# 2              Cardboard containers...              Cardboard ...  USD
# 3  Synthetic rubber and artificial ...  Synthetic rubber and a...  USD
# 4                  Packaged poultry...                  Packag...  USD

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "result/tech-flows",
      params: {
        "@id": "77ccaffa-9d79-4f38-8da5-4413469b8a7b",
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  // {
  //   jsonrpc: "2.0",
  //   id: 1,
  //   result: [
  //     {
  //        provider: {
  //          "@type": "Process",
  //          "@id": "7c619276-7b15-472a-b261-0110d461755a",
  //          name: "market for natural gas, high pressure",
  //          processType: "LCI_RESULT",
  //          flowType: "PRODUCT_FLOW"
  //        },
  //        flow: {
  //          "@type": "Flow",
  //          "@id": "a9007f10-7e39-4d50-8f4a-d6d03ce3d673",
  //          name: "natural gas, high pressure",

The final demand

In openLCA, a product system is calculated for a single demand value for a technosphere flow: a product output or waste input of the system. It is the quantitative reference of the system. In the general case, a system can have multiple demand values organized in a final demand vector \(f\) which is indexed in the same way as the technology matrix (Note that an multi-demand system can be transformed into a single demand system by simply adding an additional process column to the the technology matrix). In the result calculation, the technosphere matrix \(A\) is scaled by a scaling vector \(s\) to fulfill the final demand.

$$ f = \sum_j{A[:, j] * s_j} = A * s $$

RESTGET result/{result-id}/demand
IPCresult/demand
Python IPCResult.get_demand
Return typeTechFlowValue

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "result/demand",
      params: {
        "@id": "841c7110-4106-49d3-8447-38acc0805ca5",
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  // {
  //   jsonrpc: "2.0",
  //   id: 1,
  //   result: {
  //     amount: 1,
  //     techFlow: {
  //       provider: {
  //         "@type": "Process",
  //         "@id": "4446fcf6-7b87-4eb6-9529-775f3fe0c016",
  // ...

Total requirements

This method returns the total requirements of technosphere flows that are needed to fulfill the demand of the calculated product system. Mathematically, this is the diagonal of the technosphere matrix \(A\) scaled by the scaling \(s\):

$$ t = diag(s) * diag(A) $$

RESTGET result/{result-id}/total-requirements
IPCresult/total-requirements
Python IPCResult.get_total_requirements
Return typeList[TechFlowValue]

It is also possible to get just the total requirements value of a single technosphere flow \(j\):

$$ t_j = s_j * A[j, j] $$

RESTGET result/{result-id}/total-requirements-of/{tech-flow}
IPCresult/total-requirements-of
Python IPCResult.get_total_requirements_of
Return typeTechFlowValue
Parameter typeTechFlow

Examples

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "result/total-requirements",
      params: {
        "@id": "fa78990c-3f88-455a-9f8f-bd9e536bac28",
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  // {
  //   jsonrpc: "2.0",
  //   result: [
  //     { techFlow: { provider: [Object], flow: [Object] }, amount: 1 },
  //     { techFlow: { provider: [Object], flow: [Object] }, amount: 1 },
  //     { techFlow: {
  //         provider: [Object],
  //         flow: [Object] },
  //          amount: 15.151515151515152 }, ..
})();

Direct requirements

The direct requirements of a process \(j\) are the scaled inputs and outputs of the linked product and waste flows of that process related to the final demand of the product system. Mathematically, it is the scaled column \(j\) of the technology matrix \(A\):

$$ s_j * A[:,j] $$

The returned values are negative for inputs and positive for outputs of the respective flows. Also, the returned values contain the total requirements of the technosphere flow \(j\) to which the other requirements are related.

RESTGET result/{result-id}/direct-requirements-of/{tech-flow}
IPCresult/direct-requirements-of
Python IPCResult.get_direct_requirements_of
Return typeList[TechFlowValue]
Parameter typeTechFlow

Examples

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "result/direct-requirements-of",
      params: {
        "@id": "77ccaffa-9d79-4f38-8da5-4413469b8a7b",
        techFlow: {
          provider: {
            "@type": "Process",
            "@id": "ff6b1c80-03b2-4433-adaf-66c51063b078",
          },
          flow: {
            "@type": "Flow",
            "@id": "759b89bd-3aa6-42ad-b767-5bb9ef5d331d",
          }
        }
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  // {
  //   jsonrpc: "2.0",
  //   result: [
  //     {
  //       techFlow: { provider: [Object], flow: [Object] },
  //       amount: 0.0029162004774116275
  //     },
  //     { techFlow: ...
})();

Scaling factors

The scaling vector \(s\) contains for each process \(j\) a factor \(s_j\) by which the process needs to be scaled to fulfill the demand of the product system. Mathematically, it can be calculating by solving the following equation by \(s\) where \(A\) is the technology matrix and \(f\) the final demand vector of the system:

$$ A * s = f $$

RESTGET result/{result-id}/scaling-factors
IPCresult/scaling-factors
Python IPCResult.get_scaling_factors
Return typeList[TechFlowValue]

Examples

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "result/scaling-factors",
      params: {
        "@id": "fa78990c-3f88-455a-9f8f-bd9e536bac28",
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  // {
  //   jsonrpc: "2.0",
  //   result: [
  //     {
  //       techFlow: { provider: [Object], flow: [Object] },
  //       amount: 0.0016129032258064518
  //     },
  //     { techFlow: ...
})();

Totality factors

The concept of totality factors is a bit complicated, but they are very useful for visualizations like Sankey diagrams. In short, they scale an intensity result to a total result. An intensity result is related to one unit of product output (waste input) of a technosphere flow \(j\) including the direct, upstream, and downstream contributions. Multiplying such an intensity with the totality factor \(tf_j\) gives the total result related to the total requirements of product (waste) \(j\). Directly multiplying the intensity with the total requirements would double count possible loops.

Mathematically, the totality factor of a technosphere flow \(j\) is calculated by multiplying the total requirements \(t_j\) of that flow by a loop factor \(\lambda_j\):

$$ tf_j = \lambda_j * t_j $$

Where the loop factor is calculated via:

$$ \lambda_j = \frac{1}{A[j, j] * A^{-1}[j, j]} $$

RESTGET result/{result-id}/totality-factors
IPCresult/totality-factors
Python IPCResult.get_totality_factors
Return typeList[TechFlowValue]

As the calculation of a totality factor \(tf_j\) requires to solve the complete system for one unit of techosphere flow \(j\), it is almost always a better idea to only query the API for totality factors that are really needed:

RESTGET result/{result-id}/totality-factor-of/{tech-flow}
IPCresult/totality-factor-of
Python IPCResult.get_totality_factor_of
Return typeTechFlowValue
Parameter typeTechFlow

Unscaled requirements

The unscaled requirements of a process \(j\) are the direct requirements of the process related to the quantitative reference of that process without applying a scaling factor. Mathematically, it is just the column \(j\) of the technosphere matrix \(A\):

$$ A[:, j] $$

RESTGET result/{result-id}/unscaled-requirements-of/{tech-flow}
IPCresult/unscaled-requirements-of
Python IPCResult.get_unscaled_requirements_of
Return typeList[TechFlowValue]
Parameter typeTechFlow

Examples

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "result/unscaled-requirements-of",
      params: {
        "@id": "66dcc7d0-ce47-46bf-b77a-ada4b4c95169",
        "techFlow": {
          provider: {
            "@type": "Process",
            "@id": "ff6b1c80-03b2-4433-adaf-66c51063b078",
          },
          flow: {
            "@type": "Flow",
            "@id": "759b89bd-3aa6-42ad-b767-5bb9ef5d331d",
          }
        }
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  // {
  //   jsonrpc: "2.0",
  //   result: [
  //     { techFlow: { provider: [Object], flow: [Object] }, amount: 3.6 },
  //     { techFlow: { ...

Intervention flows

This method returns the \(m\) intervention flows of a result. These are the flows that cross the boundary with the environment of the calculated system (this is why the short name is EnviFlow in the API). In regionalized calculations these flows can be pairs of flows and locations, the same flow can occur in different locations (with possibly different characterisation factors). The rows of the intervention matrix are indexed by these \(m\) intervention flows (and the columns by the \(n\) technosphere flows):

$$ B \in \mathbb{R}^{m \times n} $$

RESTresult/{result-id}/envi-flows
IPCresult/envi-flows
Python IPCResult.get_envi_flows
Return typeList[EnviFlow]

Examples

Python IPC

envi_flows = result.get_envi_flows()
print(
    pd.DataFrame(
        [
            (ef.is_input, ef.flow.name, ef.flow.category, ef.flow.ref_unit)
            for ef in envi_flows
        ],
        columns=["Is input?", "Flow", "Category", "Unit"],
    ).head()
)

#    Is input?          Flow                                     Category Unit
# 0      False   1,4-dioxane             Elementary Flows/air/unspecified   kg
# 1      False      oryzalin            Elementary Flows/soil/groundwater   kg
# 2      False    Sethoxydim            Elementary Flows/soil/groundwater   kg
# 3      False  Chlorpyrifos  Elementary Flows/air/low population density   kg
# 4      False          lead             Elementary Flows/air/unspecified   kg

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "result/envi-flows",
      params: {
        "@id": "66dcc7d0-ce47-46bf-b77a-ada4b4c95169",
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  // {
  //   jsonrpc: "2.0",
  //   result: [
  //     {
  //       flow: {
  //         "@type": "Flow",
  //         "@id": "60ea7a31-8f27-46af-bfe5-66417f00088b",
  //         name: "Barite",
  //         category: "Elementary flows/Emission to water/ocean",
  //         flowType: "ELEMENTARY_FLOW",
  //         refUnit: "kg"
  //       },
  //       isInput: false
  //     },
  //     {
  //       flow: {
  //         "@type": "Flow",

Inventory result

This method returns the inventory result \(g\) of the calculated product system. This is the amount \(g_i\) of each intervention flow \(i\) that crosses the boundary to the environment related to the final demand of product system. It can be calculated by multiplying the intervention matrix \(B\) with the scaling vector \(s\):

$$ g = B * s $$

RESTGET result/{result-id}/total-flows
IPCresult/total-flows
Python IPCResult.get_total_flows
Return typeList[EnviFlowValue]

It is also possible to get the inventory result \(g_i\) of a single flow \(i\):

RESTGET result/{result-id}/total-flow-value-of/{envi-flow}
IPCresult/total-flow-value-of
Python IPCResult.get_total_flow_value_of
Return typeEnviFlowValue
Parameter typeEnviFlow

Examples

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "result/total-flows",
      params: {
        "@id": "66dcc7d0-ce47-46bf-b77a-ada4b4c95169",
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  // {
  //   jsonrpc: "2.0",
  //   result: [
  //     { enviFlow: { flow: [Object], isInput: false }, amount: 5.316452367058923e-12 },
  //     { enviFlow: { flow: [Object], isInput: false }, amount: 0.0000020503441278781976 },
  //     { enviFlow: { flow: [Object], isInput: false }, amount: 2.2757672132330865e-10 },
  //     { enviFlow: { flow: [Object], isInput: false }, amount: 5.003477809709274e-7 },
  //     { enviFlow: { flow: [Object], isInput: true }, amount: 0.0013196599415652454 },

Direct contributions

This method returns the direct contribution of each process \(j\) in the system to the inventory result of a flow \(i\). Mathematically, it is the row \(G[i, :]\) of the scaled intervention matrix \(G\):

$$ G = B * diag(s) $$

RESTGET result/{result-id}/direct-flow-values-of/{envi-flow}
IPCresult/direct-flow-values-of
Python IPCResult.get_direct_flow_values_of
Return typeList[TechFlowValue]
Parameter typeEnviFlow

Examples

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "result/direct-flow-values-of",
      params: {
        "@id": "66dcc7d0-ce47-46bf-b77a-ada4b4c95169",
        enviFlow: {
          flow: {
            "@type": "Flow",
            "@id": "5f7aad3d-566c-4d0d-ad59-e765f971aa0f",
            name: "Methane, fossil",
          },
        }
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  // {
  //   jsonrpc: "2.0",
  //   result: [
  //     {
  //       techFlow: { provider: [Object], flow: [Object] },
  //       amount: 1.5185665316633185e-8
  //     },
  //     {
  //       techFlow: { provider: [Object], flow: [Object] },
  //       amount: 0.0018045126002407595
  //     }, ...

Total values

This method returns for each process \(j\) in the product system the total inventory result for a flow \(j\) at this point in the supply chain including the direct, upstream, and downstream (related to waste treatment) contributions. Mathematically, this is the row \(i\) of the intensity matrix \(M\) scaled by the totality factors \(tf\):

$$ M[i,:] * diag(tf) $$

RESTGET result/{result-id}/total-flow-values-of/{envi-flow}
IPCresult/total-flow-values-of
Python IPCResult.get_total_flow_values_of
Return typeList[TechFlowValue]
Parameter typeEnviFlow

Examples

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "result/total-flow-values-of",
      params: {
        "@id": "66dcc7d0-ce47-46bf-b77a-ada4b4c95169",
        enviFlow: {
          flow: {
            "@type": "Flow",
            "@id": "5f7aad3d-566c-4d0d-ad59-e765f971aa0f",
            name: "Methane, fossil",
          },
        }
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  // {
  //   jsonrpc: "2.0",
  //   result: [
  //     {
  //       techFlow: { provider: [Object], flow: [Object] },
  //       amount: 0.0037571867032375776
  //     },
  //     {
  //       techFlow: { provider: [Object], flow: [Object] },
  //       amount: 0.000006654150590167164
  //     }, ...

Direct process results

This method returns the direct intervention flows related to the production of a product (or treatment of a waste) of a process \(j\) in order to fulfill the demand of the product system. Mathematically, it is the column \(j\) of the scaled intervention matrix \(G\):

$$ G = B * diag(s) $$

RESTGET result/{result-id}/direct-flows-of/{tech-flow}
IPCresult/direct-flows-of
Python IPCResult.get_direct_flows_of
Return typeList[EnviFlowValue]
Parameter typeTechFlow

It is also possible to get the direct result of a single flow \(i\):

RESTGET result/{result-id}/direct-flow-of/{envi-flow}/{tech-flow}
IPCresult/direct-flow-of
Python IPCResult.get_direct_flow_of
Return typeEnviFlowValue
Parameter typeEnviFlow
Parameter typeTechFlow

Examples

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "result/direct-flows-of",
      params: {
        "@id": "66dcc7d0-ce47-46bf-b77a-ada4b4c95169",
        techFlow: {
          provider: {
            "@type": "Process",
            "@id": "ff6b1c80-03b2-4433-adaf-66c51063b078",
          },
          flow: {
            "@type": "Flow",
            "@id": "759b89bd-3aa6-42ad-b767-5bb9ef5d331d",
          }
        }
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  // {
  //   jsonrpc: "2.0",
  //   result: [
  //     { enviFlow: { flow: [Object], isInput: false },
  //       amount: 2.4884640352310415e-19 },
  //     { enviFlow: { flow: [Object], isInput: false },
  //        amount: 1.0147225898077544e-12 }, ...

Total process results

This method returns the total results related to the total requirements $t_j$ of a technosphere flow \(j\)) in the calculated product system. This includes the direct, upstream, and downstream contributions related to $t_j$. Mathematically, it is the column \(j\) of the intensity matrix \(M\) scaled by the totality factor \(tf_j\):

$$ M[:,j] * tf_j $$

RESTGET result/{result-id}/total-flows-of/{tech-flow}
IPCresult/total-flows-of
Python IPCResult.get_total_flows_of
Return typeList[EnviFlowValue]
Parameter typeTechFlow

It is also possible to get the total result value for a single flow:

RESTGET result/{result-id}/total-flow-of/{envi-flow}/{tech-flow}
IPCresult/total-flow-of
Python IPCResult.get_total_flow_of
Return typeEnviFlowValue
Parameter typeEnviFlow
Parameter typeTechFlow

Intensities

This method returns the total interventions related to one unit of product output or waste input of a technosphere flow \(j\) in the supply chain. This includes direct, upstream, and downstream contributions related to one unit of this flow. Mathematically, it is the column \(M[:, j]\) of the intensity matrix \(M\):

$$ M = B * A^{-1} $$

RESTGET result/{result-id}/total-flows-of-one/{tech-flow}
IPCresult/total-flows-of-one
Python IPCResult.get_total_flows_of_one
Return typeList[EnviFlowValue]
Parameter typeTechFlow

It is also possible to get the intensity result of a single flow:

RESTGET result/{result-id}/total-flow-of-one/{envi-flow}/{tech-flow}
IPCresult/total-flow-of-one
Python IPCResult.get_total_flow_of_one
Return typeEnviFlowValue
Parameter typeEnviFlow
Parameter typeTechFlow

Unscaled flows

This method returns the unscaled direct interventions related to the quantitative reference of a process \(j\). Mathematically, this is just the column \(j\) of the intervention matrix \(B\)):

$$ B[:, j] $$

RESTGET result/{result-id}/unscaled-flows-of/{tech-flow}
IPCresult/unscaled-flows-of
Python IPCResult.get_unscaled_flows_of
Return typeList[EnviFlowValue]
Parameter typeTechFlow

Upstream results

This method returns the upstream results for a given path in an upstream contribution tree related to an intervention flow. If the path is empty, the root of that tree is returned.

RESTPOST result/{result-id}/upstream-interventions-of/{envi-flow}
IPCresult/upstream-interventions-of
Python IPCResult.get_upstream_interventions_of
Return typeList[UpstreamNode]
ParameterEnviFlow
Parameterthe upstream path encoded as string

Upstream trees

This method returns the result nodes of a given intervention flow for a given path in an upstream contribution tree. The path is a sequence of technosphere-flows that describe the path to the parent node of the returned nodes. If the path is empty, the root of the tree is returned. Note that such an upstream tree can be infinitely deep when the calculated system has cycles.

Rest APIPOST result/{result-id}/upstream-interventions-of/{envi-flow}
JSON-RPCresult/upstream-interventions-of
Snake caseresult.get_upstream_interventions_of
Camel caseresult.getUpstreamInterventionsOf
Return typeList[UpstreamNode]
Parameter 1EnviFlow
Parameter 2List[TechFlow]

Examples

olca-ipc.ts

The TypeScript example below uses the olca-ipc.ts client API:

async function main() {

  // connect to a local server running on port 8080
  const client = o.IpcClient.on(8080);
  const setup = o.CalculationSetup.of({
    target: o.Ref.of({
      id: "d3a9a9b2-ec3e-4811-8617-ae853573b50b",
      refType: o.RefType.ProductSystem,
    }),
  });
  const result = await client.calculate(setup);
  await result.untilReady();

  // select the first best inventory flow and expand a tree for it
  const flow = (await result.getEnviFlows())[0];
  console.log(`upstream tree for ${flow.flow?.name} (${flow.flow?.category})`);
  await expand(result, flow, []);

  // as always, dispose the result
  result.dispose();
}

async function expand(r: o.IpcResult, flow: o.EnviFlow, path: o.TechFlow[]) {
  const level = path.length;
  const indent = "  ".repeat(level);
  const unit = flow.flow?.refUnit;
  const nodes = await r.getUpstreamInterventionsOf(flow, path);
  for (const node of nodes) {
    if (node.result === 0) {
      continue;
    }
    const name = node.techFlow?.provider?.name;
    const value = node.result?.toExponential(2);
    console.log(`${indent}- ${value} ${unit} :: ${name}`);

    // we stop the expansion after 3 levels; you can set other cut-offs like
    // result contributions etc.
    if (level < 3) {
      const next = path.slice();
      next.push(node.techFlow!);
      await expand(r, flow, next);
    }
  }
}

main();

/*
upstream tree for COD, Chemical Oxygen Demand (Elementary flows/Emission to water/ground water)
- 1.73e-9 t :: ...
  - 1.73e-9 t :: ...
    - 7.21e-10 t :: ...
      - 3.45e-10 t :: ...
      - 2.04e-10 t :: ...
      - ...
    - 5.77e-10 t :: ...
      - 4.40e-10 t :: ...
      - 7.53e-11 t :: ...
      - 3.21e-11 t :: ...
      - ...
*/

olca-ipc.py

The Python example below uses the olca-ipc.py client API:

import olca_ipc as ipc
import olca_schema as o


def main():

    # calculate a result
    client = ipc.Client(8080)
    setup = o.CalculationSetup(
        target=o.Ref(
            id="d3a9a9b2-ec3e-4811-8617-ae853573b50b",
            ref_type=o.RefType.ProductSystem,
        )
    )
    result = client.calculate(setup)
    result.wait_until_ready()

    # select the first best inventory flow and expand a tree for it
    flow = result.get_envi_flows()[0]
    print(f"upstream tree for {flow.flow.name} ({flow.flow.category})")
    expand(result, flow, [])

    # as always, dispose the result
    result.dispose()


def expand(r: ipc.IpcResult, flow: o.EnviFlow, path: list[o.TechFlow]):
    level = len(path)
    indent = "  " * level
    unit = flow.flow.ref_unit
    nodes = r.get_upstream_interventions_of(flow, path)
    for node in nodes:
        if node.result == 0 or not node.tech_flow:
            continue
        name = node.tech_flow.provider.name
        value = node.result
        print(f"{indent}- {value:.2E} {unit} :: {name}")

        # we stop the expansion after 3 levels; you can set other cut-offs like
        # result contributions etc.
        if level < 3:
            expand(r, flow, path + [node.tech_flow])
    pass


if __name__ == "__main__":
    main()

Impact categories

This method returns the \(l\) impact categories of a result. The rows of the impact matrix \(C\) are indexed by these impact categories and the columns by the \(m\) intervention flows of the system. \(C\) contains the respective characterisation factors of the intervention flows.

$$ C \in \mathbb{R}^{l \times m} $$

RESTresult/{result-id}/impact-categories
IPCresult/impact-categories
Python IPCResult.get_impact_categories
Return typeList[Ref[ImpactCategory]]

Examples

Python IPC

impact_categories = result.get_impact_categories()
print(
    pd.DataFrame(
        [(i.name, i.ref_unit) for i in impact_categories],
        columns=["Impact category", "Unit"],
    )
)

#   Impact category         Unit
# 0            OZON  kg CFC11-eq
# 1            EUTR      kg N eq
# 2            SMOG     kg O3 eq
# 3             HNC         CTUh
# 4            ETOX         CTUe
# 5              HC         CTUh
# 6            HRSP  kg PM2.5 eq
# 7             GCC    kg CO2 eq
# 8            HTOX         CTUh
# 9            ACID    kg SO2-eq

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "result/impact-categories",
      params: {
        "@id": "4780f66c-0b77-49ca-9226-c7ce4447ea2d",
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  // {
  //   jsonrpc: "2.0",
  //   result: [
  //     {
  //       "@type": "ImpactCategory",
  //       "@id": "2dddb0a4-2f97-3fef-a4e6-708f0b4c2554",
  //       name: "Human toxicity, cancer - inorganics",
  //       category: "EF 3.0 Method (adapted)",
  //       refUnit: "CTUh"
  //     },
  //     {
  //       "@type": "ImpactCategory",
  //       "@id": "248a3f3f-933e-3c45-a799-fad9f956e03c",
  //       name: "Photochemical ozone formation",
  //       category: "EF 3.0 Method (adapted)",
  //       refUnit: "kg NMVOC eq"
  //     },

Impact assessment result

This method returns the impact assessment result of the calculated product system. It can be calculated by multiplying the characterisation matrix \(C\) with the inventory result \(g\):

$$ h = C * g $$

RESTGET result/{result-id}/total-impacts
IPCresult/total-impacts
Python IPCResult.get_total_impacts
Return typeList[ImpactValue]

It is also possible to get the impact result \(h_k\) for a single impact category \(k\):

RESTGET result/{result-id}/total-impact-value-of/{impact-category}
IPCresult/total-impact-value-of
Python IPCResult.get_total_impact_value_of
Return typeImpactValue
Parameter typeRef[ImpactCategory]

Examples

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "result/total-impacts",
      params: {
        "@id": "86b36d3b-1dba-4804-bd61-dc5bcaf7c86c",
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  // {
  //   jsonrpc: "2.0",
  //   result: [
  //     {
  //       impactCategory: {
  //         "@type": "ImpactCategory",
  //         "@id": "3bc1c67f-d3e3-3891-9fea-4512107d88ef",
  //         name: "Climate change",
  //         category: "EF 3.0 Method (adapted)",
  //         refUnit: "kg CO2 eq"
  //       },
  //       amount: 0.6387478227404646
  //     },
  // ...

Normalized results

This method returns the normalized impact assessment result of the calculated product system. Mathematically, it is the impact assessment result \(h\) devided by the respective normalization values \(nv\):

$$ diag(nv)^{-1} * h $$

RESTGET result/{result-id}/total-impacts/normalized
IPCresult/total-impacts/normalized
Python IPCResult.get_total_impacts_normalized
Return typeList[ImpactValue]

Examples

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "result/total-impacts/normalized",
      params: {
        "@id": "15432ac4-7752-4066-a62d-31270f6a0dbd",
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  //{
  //  jsonrpc: "2.0",
  //  result: [
  //    {
  //      impactCategory: {
  //        "@type": "ImpactCategory",
  //        "@id": "248a3f3f-933e-3c45-a799-fad9f956e03c",
  //        name: "Photochemical ozone formation",
  //        category: "EF 3.0 Method (adapted)",
  //        refUnit: "kg NMVOC eq"
  //      },
  //      amount: 0.0010466725460636012
  //    },

Weighted results

This method returns the weighted impact assessment result of the calculated product system. Mathematically, it is the impact assessment result \(h\) devided by the respective normalization values \(nv\) and multiplied with the respective weighting factors \(w\):

$$ diag(w) * diag(nv)^{-1} * h $$

RESTGET result/{result-id}/total-impacts/weighted
IPCresult/total-impacts/weighted
Python IPCResult.get_total_impacts_weighted
Return typeList[ImpactValue]

Examples

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "result/total-impacts/weighted",
      params: {
        "@id": "546a1d13-3cae-4c9b-82d0-a18204b0c6eb",
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  //{
  //  jsonrpc: "2.0",
  //  result: [
  //    {
  //      {
  //        impactCategory: {
  //          "@type": "ImpactCategory",
  //          "@id": "3bc1c67f-d3e3-3891-9fea-4512107d88ef",
  //          name: "Climate change",
  //          category: "EF 3.0 Method (adapted)",
  //          refUnit: "kg CO2 eq"
  //        },
  //        amount: 2.038186234380937
  //      },
  //    },

Direct contributions

This method returns the direct contribution of each process \(j\) in the system to the impact assessment result of an impact category \(k\). Mathematically, this is the row \(H[k, :]\) of the direct impact matrix which can be calculated in the following way:

$$ H = C * B * diag(s) $$

RESTGET result/{result-id}/direct-impact-values-of/{impact-category}
IPCresult/direct-impact-values-of
Python IPCResult.get_direct_impact_values_of
Return typeList[TechFlowValue]
Parameter typeRef[ImpactCategory]

Examples

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "result/direct-impact-values-of",
      params: {
        "@id": "86b36d3b-1dba-4804-bd61-dc5bcaf7c86c",
        impactCategory: {
          "@id": "dbdd01d5-2be4-3ba4-8127-de89f065fda1",
        },
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  //  {
  //     jsonrpc: "2.0",
  //     result: [
  //       {
  //         techFlow: { provider: [Object], flow: [Object] },
  //         amount: 0.00008582892489209728
  //       },
  //       { techFlow: { provider: [Object], flow: [Object] },
  //         amount: 2.023594297933216 },
  //    ...

Direct process result

This method returns the direct impacts of a process \(j\) in the calculated product system. Mathematically, it is the column \(H[:, j]\) of the direct impact matrix:

$$ H = C * B * diag(s) $$

RESTGET result/{result-id}/direct-impacts-of/{tech-flow}
IPCresult/direct-impacts-of
Python IPCResult.get_direct_impacts_of
Return typeList[ImpactValue]
Parameter typeTechFlow

It is also possible to get the direct process result \(H[k, j]\) for a single impact category \(k\):

RESTGET result/{result-id}/direct-impact-of/{impact-category}/{tech-flow}
IPCresult/direct-impact-of
Python IPCResult.get_direct_impact_of
Return typeImpactValue
Parameter typeRef[ImpactCategory]
Parameter typeTechFlow

Examples

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "result/direct-impacts-of",
      params: {
        "@id": "86b36d3b-1dba-4804-bd61-dc5bcaf7c86c",
        techFlow: {
          provider: { "@id": "37eb4bf5-9fbc-4caf-a24a-0e0b71b997dd" },
          flow: { "@id": "817c3650-4fed-4ef2-b9b6-404a198834e6" }
        }
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  // {
  //   jsonrpc: "2.0",
  //   result: [
  //     {
  //       impactCategory: {
  //         "@type": "ImpactCategory",
  //         "@id": "3bc1c67f-d3e3-3891-9fea-4512107d88ef",
  //         name: "Climate change",
  //         category: "EF 3.0 Method (adapted)",
  //         refUnit: "kg CO2 eq"
  //       },
  //       amount: 0.0008854099635082675
  //     },
  //  ...

Total process result

This method returns the total impact result related to the total requirements \(t_j\) of a technosphere flow \(j\) in the calculated product system. This includes the direct, upstream, and downstream contributions related to $t_j$. Mathematically, it is the column \(j\) of the impact intensity matrix \(N\) scaled by the totality factor \(tf_j\):

$$ M[:,j] * tf_j $$

RESTGET result/{result-id}/total-impacts-of/{tech-flow}
IPCresult/total-impacts-of
Python IPCResult.get_total_impacts_of
Return typeList[ImpactValue]
Parameter typeTechFlow

It is also possible to get the total impact result for a single impact category:

RESTGET result/{result-id}/total-impact-of/{impact-category}/{tech-flow}
IPCresult/total-impact-of
Python IPCResult.get_total_impact_of
Return typeImpactValue
Parameter typeRef[ImpactCategory]
Parameter typeTechFlow

Examples

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "result/total-impacts-of",
      params: {
        "@id": "86b36d3b-1dba-4804-bd61-dc5bcaf7c86c",
        techFlow: {
          provider: { "@id": "37eb4bf5-9fbc-4caf-a24a-0e0b71b997dd" },
          flow: { "@id": "817c3650-4fed-4ef2-b9b6-404a198834e6" }
        }
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  // {
  //   jsonrpc: "2.0",
  //   result: [
  //     {
  //       impactCategory: {
  //         "@type": "ImpactCategory",
  //         "@id": "3bc1c67f-d3e3-3891-9fea-4512107d88ef",
  //         name: "Climate change",
  //         category: "EF 3.0 Method (adapted)",
  //         refUnit: "kg CO2 eq"
  //       },
  //       amount: 0.020981112466506135
  //     },
  //  ...

Intensities

This method returns the total impacts related to one unit of product output or waste input of a technosphere flow \(j\) in the supply chain. This includes direct, upstream, and downstream contributions related to one unit of this flow. Mathematically, it is the column \(N[:, j]\) of the impact intensity matrix \(N\):

$$ N = C * B * A^{-1} = C * M $$

RESTGET result/{result-id}/total-impacts-of-one/{tech-flow}
IPCresult/total-impacts-of-one
Python IPCResult.get_total_impacts_of_one
Return typeList[ImpactValue]
Parameter typeTechFlow

It is also possible to get the intensity \(N[k, j]\) of a single impact category \(k\):

RESTGET result/{result-id}/total-impact-of-one/{impact-category}/{tech-flow}
IPCresult/total-impact-of-one
Python IPCResult.get_total_impact_of_one
Return typeImpactValue
Parameter typeRef[ImpactCategory]
Parameter typeTechFlow

Examples

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "result/total-impacts-of-one",
      params: {
        "@id": "86b36d3b-1dba-4804-bd61-dc5bcaf7c86c",
        techFlow: {
          provider: { "@id": "37eb4bf5-9fbc-4caf-a24a-0e0b71b997dd" },
          flow: { "@id": "817c3650-4fed-4ef2-b9b6-404a198834e6" }
        }
      }
    })
  });
  let v = await resp.json();
  console.log(v);
  // {
  //   jsonrpc: "2.0",
  //   result: [
  //     {
  //       impactCategory: {
  //         "@type": "ImpactCategory",
  //         "@id": "3bc1c67f-d3e3-3891-9fea-4512107d88ef",
  //         name: "Climate change",
  //         category: "EF 3.0 Method (adapted)",
  //         refUnit: "kg CO2 eq"
  //       },
  //       amount: 1.3687522905554603
  //     },
  // ...

Upstream results

This method returns the upstream results for a given path in an upstream contribution tree related to an impact category. If the path is empty, the root of that tree is returned.

RESTPOST result/{result-id}/upstream-impacts-of/{impact-category}
IPCresult/upstream-impacts-of
Python IPCResult.get_upstream_impacts_of
Return typeList[UpstreamNode]
ParameterRef[ImpactCategory]
Parameterthe upstream path encoded as string

Upstream trees

This method returns the result nodes of a given impact category for a given path in an upstream contribution tree. The path is a sequence of technosphere-flows that describe the path to the parent node of the returned nodes. If the path is empty, the root of the tree is returned. Note that such an upstream tree can be infinitely deep when the calculated system has cycles.

Rest APIPOST result/{result-id}/upstream-impacts-of/{impact-category}
JSON-RPCresult/upstream-impacts-of
Snake caseresult.get_upstream_impacts_of
Camel caseresult.getUpstreamImpactsOf
Return typeList[UpstreamNode]
Parameter 1Ref[ImpactCategory]
Parameter 2List[TechFlow]

Examples

olca-ipc.ts

The TypeScript example below uses the olca-ipc.ts client API:

async function main() {

  // connect to a local server running on port 8080
  const client = o.IpcClient.on(8080);
  const setup = o.CalculationSetup.of({
    target: o.Ref.of({
      id: "d3a9a9b2-ec3e-4811-8617-ae853573b50b",
      refType: o.RefType.ProductSystem,
    }),
    impactMethod: o.Ref.of({
      id: "07370e48-dde9-4248-9a9b-7255f701da89",
    })
  });
  const result = await client.calculate(setup);
  await result.untilReady();

  // select the first best impact category and expand a tree for it
  const indicator = (await result.getImpactCategories())[0];
  console.log(`upstream tree for ${indicator.name}`);
  await expand(result, indicator, []);

  // as always, dispose the result
  result.dispose();
}

async function expand(r: o.IpcResult, indicator: o.Ref, path: o.TechFlow[]) {
  const level = path.length;
  const indent = "  ".repeat(level);
  const unit = indicator.refUnit;
  const nodes = await r.getUpstreamImpactsOf(indicator, path);
  for (const node of nodes) {
    if (node.result === 0) {
      continue;
    }
    const name = node.techFlow?.provider?.name;
    const value = node.result?.toExponential(2);
    console.log(`${indent}- ${value} ${unit} :: ${name}`);

    // we stop the expansion after 3 levels; you can set other cut-offs like
    // result contributions etc.
    if (level < 3) {
      const next = path.slice();
      next.push(node.techFlow!);
      await expand(r, indicator, next);
    }
  }
}

main();

/*
upstream tree for Photochemical ozone formation
- 2.66e-2 kg NMVOC eq ...
  - 2.66e-2 kg NMVOC eq ...
    - 1.13e-2 kg NMVOC eq ...
      - 4.69e-3 kg NMVOC eq ...
      - 4.53e-3 kg NMVOC eq ...
      - 1.41e-3 kg NMVOC eq ...

    - 8.72e-3 kg NMVOC eq ...
      - 4.18e-3 kg NMVOC eq ...
      - 4.18e-3 kg NMVOC eq ...
      - 1.01e-4 kg NMVOC eq ...
*/

olca-ipc.py

The Python example below uses the olca-ipc.py client API:

import olca_ipc as ipc
import olca_schema as o


def main():

    # calculate a result
    client = ipc.Client(8080)
    setup = o.CalculationSetup(
        target=o.Ref(
            id="d3a9a9b2-ec3e-4811-8617-ae853573b50b",
            ref_type=o.RefType.ProductSystem,
        ),
        impact_method=o.Ref(id="07370e48-dde9-4248-9a9b-7255f701da89"),
    )
    result = client.calculate(setup)
    result.wait_until_ready()

    # select the first best impact category and expand a tree for it
    indicator = result.get_impact_categories()[0]
    print(f"upstream tree for {indicator.name}")
    expand(result, indicator, [])

    # as always, dispose the result
    result.dispose()


def expand(r: ipc.IpcResult, indicator: o.Ref, path: list[o.TechFlow]):
    level = len(path)
    indent = "  " * level
    unit = indicator.ref_unit
    nodes = r.get_upstream_impacts_of(indicator, path)
    for node in nodes:
        if node.result == 0 or not node.tech_flow:
            continue
        name = node.tech_flow.provider.name
        value = node.result
        print(f"{indent}- {value:.2E} {unit} :: {name}")

        # we stop the expansion after 3 levels; you can set other cut-offs like
        # result contributions etc.
        if level < 3:
            expand(r, indicator, path + [node.tech_flow])
    pass


if __name__ == "__main__":
    main()

The currency of cost results

This method returns the currency of cost results if available. This is typically the reference currency of the underlying database.

RESTGET result/{result-id}/currency
IPCresult/currency
Python IPCResult.get_currency
Return typeRef[Currency]

Examples

Python IPC

JSON-RPC via Fetch API

The example below shows the usage of this method using the JSON-RPC protocol via the Fetch API which is available in modern web-browsers or platforms like Deno.

  let resp = await fetch("http://localhost:8080", {
    method: "POST",
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "result/currency",
      params: {
        "@id": "66dcc7d0-ce47-46bf-b77a-ada4b4c95169",
      }
    })
  });
  let v = await resp.json();
  console.log(v);

Life cycle costing result

This method retursn the total life cycle costing (LCC) result.

$$ q^T * s $$

Rest APIGET result/{result-id}/total-costs
JSON-RPCresult/total-costs
Snake caseresult.get_total_costs
Camel caseresult.getTotalCosts
Return typeCostValue

Examples

TypeScript IPC

  const totals = await result.getTotalCosts();
  const code = totals.currency?.refUnit;
  console.log(`total costs: ${totals.amount} ${code}`);
  // total costs: 60 USD

Direct contributions

This method returns the direct contribution of each process in the system to the total cost result.

$$ q^T * diagm(s) $$

Rest APIGET result/{result-id}/cost-contributions
JSON-RPCresult/cost-contributions
Snake caseresult.get_cost_contributions
Camel caseresult.getCostContributions
Return typeList[TechFlowValue]

Examples

TypeScript IPC

  const contributions = await result.getCostContributions();
  for (const c of contributions) {
    console.log(`${c.techFlow?.provider?.name}: ${c.amount} ${code}`);
  }
  // Q: 15 USD
  // P: 45 USD

Total cost values

$$ q^T * A^{-1}[:,j] * tf[j] $$

Rest: /total-costs-of/{tech-flow}

Rest: /total-cost-values

$$ q^T * A^{-1} * diagm(tf) $$

Intensities

$$ q^T * A^{-1}[:,j] $$

Rest: /total-costs-of-one/{tech-flow}

Unscaled costs

$$ q^T $$

Rest: /unscaled-costs

Upstream results

This method returns the upstream cost results for a given path in an upstream contribution tree. If the path is empty, the root of that tree is returned.

RESTPOST result/{result-id}/upstream-costs-of
IPCresult/upstream-costs-of
Python IPCResult.get_upstream_costs_of
Return typeList[UpstreamNode]
Parameterthe upstream path encoded as string

Upstream trees

Sankey Graphs

This method returns a graph data structure with which Sankey diagrams can be constructed.

RESTPOST result/{result-id}/sankey
IPCresult/sankey
Python IPCResult.get_sankey_graph
Parameter typeSankeyRequest
Return typeSankeyGraph

Examples

Python IPC

import olca_ipc.rest as ipc
import olca_schema as o

# in this example we use the REST client
client = ipc.RestClient("http://localhost:8080")

# calculate a result for the default quantitative reference of a product system
result = client.calculate(
    o.CalculationSetup(
        target=o.Ref(
            ref_type=o.RefType.ProductSystem,
            id="7d1cbce0-b5b3-47ba-95b5-014ab3c7f569",
        ),
        impact_method=o.Ref(id="99b9d86b-ec6f-4610-ba9f-68ebfe5691dd"),
    )
)
result.wait_until_ready()

# get the Sankey graph for an impact category
g = result.get_sankey_graph(
    o.SankeyRequest(
        impact_category=o.Ref(id="b8658d7c-9c6e-4361-acbf-3bd6d9fef8c9"),
        max_nodes=10,
    )
)
print(f"loaded a graph with {len(g.nodes)} nodes and {len(g.edges)} edges")
# prints something like: loaded a graph with 10 nodes and 14 edges

# finally, dispose the result
result.dispose()

TypeScript IPC

// we use it as Deno module here; but it is the same for npm and friends
// with Deno, run it like this: deno run --allow-net sankey.ts
import * as o from "https://raw.githubusercontent.com/GreenDelta/olca-ipc.ts/main/mod.ts";

async function main() {
  const client = o.RestClient.on("http://localhost:8080");

  // calculate the result
  const result = await client.calculate(o.CalculationSetup.of({
    target: o.Ref.of({
      refType: o.RefType.ProductSystem,
      id: "7d1cbce0-b5b3-47ba-95b5-014ab3c7f569",
    }),
    impactMethod: o.Ref.of({ id: "99b9d86b-ec6f-4610-ba9f-68ebfe5691dd" }),
  }));
  await result.untilReady();

  const g = await result.getSankeyGraph(o.SankeyRequest.of({
    impactCategory: o.Ref.of({ id: "b8658d7c-9c6e-4361-acbf-3bd6d9fef8c9" }),
    maxNodes: 10,
  }));
  console.log(
    `loaded a graph with ${g.nodes?.length} nodes and ${g.edges?.length} edges`,
  );
  result.dispose();
}

main();