Skip to content

Latest commit

 

History

113 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Basic Model Interface Build/Test

bmi-example-fortran

An example of implementing the Fortran bindings for the CSDMS Basic Model Interface (BMI).

Deltares 'fork' building dynamic libraries (dll / so)

This is a forked / detached version of the bmi-fortran-example adapter at:

https://github.com/csdms/bmi-example-fortran

The sole purpose of this repos was to build dynamic libraries for both Windows and (RHEL8) Linux, and to create a JNA interop for Java with some help of Claude Code.

Note

Files not relevant to the Deltares BMI application have been removed, and the github fork was detached as this will not serve to create a pull requet upstream.

The bmi_main.f90 and test/ folder are preserved from the upstream CSDMS bmi-example-fortran repository as Fortran usage examples. They require CMake/fpm to build and are not part of the Java interop build pipeline.

Overview

This is an example of implementing a BMI for a simple model that solves the diffusion equation on a uniform rectangular plate with Dirichlet boundary conditions. Tests and examples of using the BMI are provided. The model is written in Fortran 90. The BMI is written in Fortran 2003.

This repository is organized with the following directories:

heat
Holds the model and a sample main program
bmi_heat
Holds the BMI for the model and a main program to run the model through its BMI
test
Unit tests for the BMI-ed model
example
Examples of controlling the model through its BMI

Build/Install

This repos contains a github workflow, build.yml to use the Github actions platform for building the binaries.

Deltares FEWS / Java interop

This fork adds a C/Java interoperability layer on top of the standard BMI FORTRAN implementation, following the NOAA-OWP NextGen iso_c_fortran_bmi pattern. This allows the model to be called from Java via JNA (Java Native Access) inside Deltares FEWS.

Architecture

Java (FEWS BMI adapter)
    │  JNA – interop/FortranModelJnaLibrary.java
    ▼
libbmi_heat.so / .dll
    ├── register_bmi.f90          model-specific factory function
    ├── iso_c_bmif_2_0.f90        generic C-interop layer (all 50+ BMI functions)
    │       uses ↓
    ├── bmif_2_0_iso.f90          BMI abstract type with ISO C integer kinds
    │       extends ↓
    ├── bmi.f90                   CSDMS BMI v2.0 abstract spec
    │
    └── bmi_heat.f90              concrete heat-model BMI implementation
            uses ↓
        heat.f90                  2D heat equation physics

Opaque handle pattern

register_bmi(void** handle) allocates a bmi_heat instance, wraps it in a thin Fortran box type, and returns c_loc(box) as an opaque void*. Every other BMI function takes this handle, recovers the Fortran object with c_f_pointer, and dispatches polymorphically.

finalize(void** handle) both runs the BMI finalize method and deallocates the model. There is no separate bmi_destroy() — do not use the handle after calling finalize.

ABI note: every function except register_bmi receives the handle as type(c_ptr), intent(in) without the Fortran VALUE attribute. Without VALUE, Fortran bind(C) passes by reference, so the C ABI is void** throughout. In JNA this maps to PointerByReference — do not unwrap with .getValue() before calling BMI methods.

FEWS build (GitHub Actions)

Platform Container / runner Compiler Output
Linux AlmaLinux 8 Docker (docker/Dockerfile) Intel ifx 2025.2 libbmi_heat.so (statically linked Intel runtime)
Windows windows-latest Intel ifx 2025.2 libbmi_heat.dll + 4 Intel runtime DLLs

Java usage (JNA)

import bmi.model.FortranModelJnaLibrary;
import bmi.model.FortranString;
import com.sun.jna.Native;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.PointerByReference;

// Load the shared library
FortranModelJnaLibrary lib =
    Native.load("bmi_heat", FortranModelJnaLibrary.class);

// Allocate the model
PointerByReference handleRef = new PointerByReference();
lib.register_bmi(handleRef);
// All methods take handleRef directly — do NOT call handleRef.getValue()

// Initialize
lib.initialize(handleRef, new FortranString("/path/to/config.cfg").toBytes());

// Find grid size for a variable
IntByReference gridRef = new IntByReference();
lib.get_var_grid(handleRef, new FortranString("plate_surface__temperature").toBytes(), gridRef);
IntByReference sizeRef = new IntByReference();
lib.get_grid_size(handleRef, gridRef, sizeRef);

// Run one step and retrieve results
lib.update(handleRef);
float[] dest = new float[sizeRef.getValue()];
lib.get_value_float(handleRef, new FortranString("plate_surface__temperature").toBytes(), dest);

// Finalize (also frees memory — do not use handleRef after this)
lib.finalize(handleRef);

Interop files

File Purpose
interop/bmi.h C header with exact exported symbol signatures and ABI notes (DIFF 1–8)
interop/FortranModelJnaLibrary.java JNA interface (bmi.model package)
interop/FortranString.java Helper: converts Java String ↔ Fortran fixed-size 2048-byte buffer
interop/bmi_from_spec.h Reference header derived from the abstract Fortran spec (not the actual ABI)

See interop/bmi.h for important ABI differences from the abstract spec, including known stub implementations and a bug in get_grid_edge_nodes.

Known limitations

  • get_value_ptr_* — not implemented; always returns BMI_FAILURE.
  • get/set_value_at_indices_* — not implemented; always returns BMI_FAILURE.
  • get_grid_edge_nodes — contains a logic bug in iso_c_bmif_2_0.f90 (line 893) that may cause incorrect array sizing. See interop/bmi.h (BUG 1).

Credits

  • NOAA-OWP iso_c_fortran_bmi interop layer: Nels Frazier, NOAA OWP (Apache 2.0)

About

An example of wrapping a model written in Fortran with a BMI

Resources

Code of conduct

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages