Connecting Fortran to openLCA: Batch LCA Calculations

Fortran remains widely used in climate modelling, computational fluid dynamics, chemical process simulation, and structural engineering. Researchers running these simulations sometimes need life cycle assessment (LCA) results as part of their pipeline: the environmental impact of a production scenario, scaled by outputs from a process model, fed into an optimisation loop. openLCA is a free, open-source LCA platform with a built-in IPC server that exposes its calculation engine over HTTP. Calling it from within existing Fortran code means the results sit in native arrays, ready for computation.

The program described here connects to openLCA’s IPC server, runs every product system in the database against a chosen impact method, and collects the results into a 2D Fortran array (systems x impact categories). It writes the matrix to CSV and leaves the arrays available for downstream use.

The code is on GitHub and the full IPC protocol reference is in the openLCA IPC Protocol article.

What the program does

The program runs through the standard IPC calculation lifecycle documented in the protocol article:

  1. Connects to the IPC server and lists all product systems and impact methods.
  2. Calculates the first product system to discover how many impact categories the chosen method produces.
  3. Allocates a results matrix sized to fit: real(dp) :: results(n_systems, n_categories).
  4. Loops through the remaining product systems, calculating each one and storing the impacts into the matrix.
  5. Writes the full matrix to olca_results.csv with category names and units as column headers.

The output CSV has one row per product system and one column per impact category. The matrix stays in memory after the CSV is written, so downstream Fortran code can operate on it directly:

gwp_column = findloc(category_names, 'Global Warming', dim=1)
total_gwp  = sum(results(:, gwp_column) * production_volumes)

Calling the module

The IPC client module exposes a ipc_client type with five procedures. A minimal example:

use olca_ipc
type(ipc_client)    :: client
type(descriptor)    :: systems(100), methods(50)
type(impact_result) :: impacts(50)
integer             :: n_sys, n_met, n_imp
character(len=64)   :: result_id
logical             :: ok

! List product systems and impact methods
call client%get_descriptors('ProductSystem', systems, n_sys, ok)
call client%get_descriptors('ImpactMethod', methods, n_met, ok)

! Run a calculation
call client%calculate(systems(1)%id, methods(1)%id, result_id, ok)
call client%wait_until_ready(result_id)
call client%get_total_impacts(result_id, impacts, n_imp, ok)

! Use the results
print *, impacts(1)%category_name, impacts(1)%amount, impacts(1)%unit

! Free server memory
call client%dispose(result_id)

Example output

=== openLCA Fortran Batch Calculator ===

Found 4 product systems:
    1  Production of composite part - US
    2  4) biodiesel Production - US
    3  Product manufacture
    4  Product manufacture

Found 8 impact methods:
    1  ReCiPe 2016 - Endpoint/H
    2  ReCiPe 2016 - Midpoint/I
    ...

Using method: ReCiPe 2016 - Endpoint/H
Running initial calculation (Production of composite part - US)...
  22 impact categories detected.

Calculating system 2/4 (4) biodiesel Production - US)...
Calculating system 3/4 (Product manufacture)...
Calculating system 4/4 (Product manufacture)...

All calculations complete.

Results written to: olca_results.csv
------------------------------------------------------------
Product systems calculated: 4
Impact categories:          22
Results matrix dimensions:  4 x 22
Output:                     olca_results.csv
------------------------------------------------------------

Architecture

The program has two source files:

src/olca_ipc.f90 is the IPC client module. It provides three public types (ipc_client, descriptor, impact_result) and five type-bound procedures (get_descriptors, calculate, wait_until_ready, get_total_impacts, dispose). HTTP requests go through system curl via execute_command_line. JSON parsing uses the json-fortran library.

app/main.f90 is the batch calculator. It uses the module to run calculations and collect results.

The split follows the fpm project convention: reusable library code in src/, application code in app/.

Building and running

The program uses fpm (Fortran Package Manager) and requires gfortran and curl. On Windows, MSYS2 provides all three:

pacman -S mingw-w64-ucrt-x86_64-gcc-fortran git

curl is included with Windows 10 and later, and with MSYS2. On Linux and macOS, both gfortran and curl are available through the system package manager.

Clone the repository, build, and run:

git clone https://github.com/Below280/openLCA-IPC-tools-fortran.git
cd openLCA-IPC-tools-fortran
fpm build
fpm run

The first build pulls json-fortran from GitHub automatically. Subsequent builds recompile only changed files. Once built, the compiled binary runs independently:

fpm run          # build if needed, then run
olca-fortran     # or run the binary directly after fpm install

Before running, start the IPC server in openLCA: Tools > Developer Tools > IPC Server, port 8080, green play button.

How the IPC client works

The module talks to openLCA using the JSON-RPC protocol described in the IPC Protocol article. Each method call builds a JSON request string, writes it to a temporary file, calls curl via execute_command_line, and reads the response from curl’s output file. The temp files are deleted after each call.

Two internal subroutines handle communication:

send_request is used by get_descriptors and get_total_impacts. It makes the HTTP call and parses the response into a json_file object for structured extraction of names, IDs, and amounts.

send_raw is used by calculate, wait_until_ready, and dispose. It makes the HTTP call and returns the raw response string. These methods extract what they need with simple string matching on the JSON text.

The split exists because of a practical issue with json-fortran’s path syntax. openLCA’s JSON uses @id and @type as field names. json-fortran treats @ as a JSONPath operator in path strings, so result.@id returns the array index instead of the UUID. The get_descriptors and get_total_impacts subroutines work around this by navigating to parent objects first, then retrieving @id children by literal name. The calculate and wait_until_ready subroutines work with raw strings directly: the result UUID always follows "@id":" in the response, and readiness is indicated by the presence of "isReady":true.

Practical notes

json-fortran and the @ character. The @ prefix on field names like @id and @type comes from JSON-LD conventions that openLCA follows. json-fortran’s json_file%get method interprets @ as part of its JSONPath-like path syntax. The workaround used throughout the module is json_core%get_child(parent, '@id', child, found), which matches the child name literally. This would apply to any JSON library that supports JSONPath.

System curl vs HTTP libraries. The module uses system curl for HTTP requests. During development, the fortran-lang http-client library produced segmentation faults after multiple sequential HTTP calls on Windows. System curl is reliable across platforms and ships with Windows 10+, macOS, and Linux. The trade-off is that each request writes two temporary files (request body and response), which adds a small I/O overhead.

Portable sleep. The wait_until_ready subroutine polls the server in a loop with a one-second delay between polls. The delay uses POSIX sleep() via iso_c_binding. This works on Linux, macOS, and Windows under MSYS2. A native Windows build would need Sleep from the Windows API.

Impact method selection. The program currently uses the first impact method in the database. Extending this to accept a method name as a command-line argument is straightforward with Fortran’s get_command_argument intrinsic.

Further reading

The openLCA IPC Protocol article documents the JSON-RPC methods, request/response formats, and curl commands for debugging.

The Python examples in the openLCA scripting knowledge base cover the same calculation workflows using the olca-ipc package, which wraps the protocol in a Python-native interface.

The source code is on GitHub.