Skip to content

Add BMI 2.0 interface and Python bindings#86

Draft
HendrikKok wants to merge 11 commits into
daisy-model:mainfrom
HendrikKok:add_bmi
Draft

Add BMI 2.0 interface and Python bindings#86
HendrikKok wants to merge 11 commits into
daisy-model:mainfrom
HendrikKok:add_bmi

Conversation

@HendrikKok

@HendrikKok HendrikKok commented Jun 25, 2026

Copy link
Copy Markdown

Overview

This PR adds a Basic Model Interface (BMI) 2.0 implementation for Daisy,
along with Python bindings that allow Daisy to be driven from Python
using the BMI standard.

Changes

C++ BMI interface

  • Add bmi.h / bmi.C: BMI 2.0 base interface
  • Add daisy_bmi.h / daisy_bmi.C: Daisy-specific BMI implementation
    wrapping the core Daisy and Column objects
  • Expose key variables (soil water, groundwater) via BMI get/set methods
  • Add DaisyPythonController for driving Daisy from Python

Python package (python/)

  • New daisy Python package with BMI bindings (bmi_bindings.cpp)
  • Moved DaisyPyFun plugin interface into the new package
  • Example scripts:
    1- daisy_bmi_example.py for a bmi driven simulation,
    2- updated daisy_py_fun_example.py to use the new python package
  • Updated pyproject.toml

Build

  • Updated CMakeLists.txt and build scripts for the new targets
  • Added sample_bmi.dai sample setup file

Status

  • [ x] Windows build tested
  • Linux build tested
  • More BMI variables to expose (work in progress)

Notes

  • This is a draft — feedback on the interface design and which variables
    to expose via BMI is welcome.
  • build.bat / build_exe.bat are included as a convenience for Windows builds
    and will be replaced by proper README documentation in a follow-up.

BMI state access: towards a central MemoryManager

The current BMI implementation pierces the full model hierarchy
(BMI → DaisyBMI → Daisy → Field → Column → SoilWater) with a separate
getter at every layer for each variable. Every new BMI variable requires
adding that method at every level, and every read copies a std::vector.

The root cause is that SoilWater and other components have private variables,
so every caller must go through a getter to reach them.

Proposed direction: global MemoryManager

Models register live pointers to their internal arrays once at
initialize() time. BMI reads from the manager by name — no per-variable
forwarding chain, no copies.

// SoilWater::initialize() registers its arrays:
store.register_view("soil_water__pressure_head", h_.data(),        h_.size());
store.register_view("soil_water__theta",         Theta_.data(),    Theta_.size());
store.register_view("soil_water__flux",          q_matrix_.data(), q_matrix_.size());

// BMI::get_value_ptr — zero copy, standard BMI 2.0:
void* BMI::get_value_ptr(const std::string& name) {
    return const_cast<double*>(memory_manager_.get(name).data);
}

// BMI::get_value — copies from the same pointer:
auto view = memory_manager_.get(name);
std::copy(view.data, view.data + view.size, dest);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant