Skip to main content

Python wrapper for LPJ-Guess experiment runner (via pythonnet + .NET 9)

Project description

lpjguess-runner

CI

Python wrapper for LPJ-Guess experiment runner (via pythonnet + .NET 9)

Build

Prerequisites:

  • .NET 9 SDK
  • Python 3.11 (if building wheel)
# Clone the repository with submodules
git clone --recurse-submodules git@github.com:hie-dave/lpjg-gui.git
make wheel

Install

  • Prerequisites: .NET 9 runtime

From PyPI

pip install lpjguess-runner

From Source

make install                 # install globally
make install-venv VENV=.venv # install into a local virtual environment (default: .venv)
make install-user            # install for the current user

Concepts and Workflow

This package is a thin Python wrapper around a .NET job runner for LPJ-Guess. A typical workflow configures a run, defines which simulations to execute, and optionally customises how progress and model output are handled.

  • Run settings: A RunSettings object specifies the LPJ-Guess executable path, output directory, input module, CPU count, and job name.

  • Simulations: A list of Simulation objects describes variations to apply to base instruction files (for example changing top-level parameters or PFT block parameters). The helper simulation(name, factors) creates these objects.

    factors should be a list of IFactor objects, which each represent a change to an instruction file parameter. This may be either a top-level parameter (e.g. npatch, wateruptake, etc) or a block parameter (e.g. sla within a PFT block, etc). These factors may be constructed via TopLevelParameter(name, value) and BlockParameter(block_type, block_name, parameter_name, parameter_value) respectively. See the examples below for details.

    Parameter typing note: All parameter values passed to TopLevelParameter(...) and BlockParameter(...) must be strings. For example, use TopLevelParameter("nindiv_max", "1") rather than TopLevelParameter("nindiv_max", 1). When constructing simulations programmatically, convert values explicitly with str(value).

  • Instruction files and PFTs: A list of .ins files and PFT names to run. Each instruction file will be run once for each defined simulation.

  • Progress and output handling (optional): Objects can be supplied to receive progress updates and model stdout/stderr. For convenience, this package exports Python base classes that already implement the required .NET interfaces, so only the required methods need to be overridden:

    • CustomProgressReporter.ReportProgress(percent, elapsed, ncomplete, njob)
    • CustomOutputHelper.ReportOutput(jobName, output) / ReportError(...)

    See the "Customising Progress and Output" section below for more details.

  • Outputs: Files are written under the output directory specified in RunSettings. The structure depends on the LPJ-Guess configuration and runner settings.

  • Result: run_simulations(...) returns an ExperimentResult with summary counts (TotalJobs, SuccessfulJobs, FailedJobs) and Error if present.

Notes for Python users:

  • When using custom progress/output handlers, the overriden hooks may be called from background threads; handlers should therefore be fast and thread-safe.

  • If only console output is required, use ConsoleProgressReporter() and ConsoleOutputHelper() and skip writing custom classes.

  • To customise behavior, subclass the provided bases rather than implementing .NET interfaces directly.

  • Parameter values for TopLevelParameter and BlockParameter are string typed. Convert numeric or boolean values with str(...) when building simulation lists programmatically.

Example Usage

from lpjguess_runner import *

run_settings = RunSettings.Local(
    "/path/to/guess/executable",
    "/path/to/output/directory",
    "nc",   # input module
    4,      # cpu count
    "job_name",
    True)   # Whether to allow context switching of LPJ-Guess processes between CPUs (recommended: True)

simulations = [
    simulation("nindiv_max_0_sla_26", [               # Run all .ins files with:
        TopLevelParameter("wateruptake", "wcont"),    # - wateruptake = wcont
        BlockParameter("pft", "MRS", "sla", "26")     # - SLA of "MRS" pft set to 26
    ]),
    simulation("nindiv_max_1_sla_39", [               # Run all .ins files with:
        TopLevelParameter("wateruptake", "rootdist"), # - wateruptake = rootdist
        BlockParameter("pft", "MRS", "sla", "39")     # - SLA of "MRS" pft set to 39
    ]),
]

ins = ["/path/to/file1.ins", "/path/to/file2.ins"]
pfts = ["MRS"]

result = run_simulations(run_settings,
                         simulations,
                         ins,
                         pfts,
                         ConsoleProgressReporter(),   # Write progress messages to stdout
                         ConsoleOutputHelper())       # Propagate subprocess output to stdout

print(f"Total jobs: {result.TotalJobs}")
print(f"Successful jobs: {result.SuccessfulJobs}")
print(f"Failed jobs: {result.FailedJobs}")
print(f"Error: {result.Error}")

Customising Progress and Output

The default helpers ConsoleProgressReporter() and ConsoleOutputHelper() propagate progress and model stdout/stderr to stdout. For custom behavior, subclass the Python base classes exported by this package:

  • CustomProgressReporter (implements .NET IProgressReporter).
  • CustomOutputHelper (implements .NET IOutputHelper).

These hide pythonnet interop details so only the necessary methods must be overridden in subclasses.

Example: capture stdout/stderr and print compact progress

from lpjguess_runner import *
import threading

class MyOutput(CustomOutputHelper):
    def __init__(self):
        super().__init__()
        self.stdout = {}
        self.stderr = {}
        self._lock = threading.Lock()

    def ReportOutput(self, jobName, output):
        with self._lock:
            self.stdout.setdefault(jobName, []).append(output)

    def ReportError(self, jobName, output):
        with self._lock:
            self.stderr.setdefault(jobName, []).append(output)

class MyProgress(CustomProgressReporter):
    def ReportProgress(self, percent, elapsed, ncomplete, njob):
        print(f"[{elapsed}] {ncomplete}/{njob} ({percent:.1f}%)")

out = MyOutput()
pr = MyProgress()

result = run_simulations(run_settings, simulations, ins, pfts, pr, out)

Programmatic construction of simulations

Many workflows define a small grid of parameter values and generate one simulation per combination. The following example shows a minimal pattern using itertools.product. Note the explicit conversion of parameter values to strings.

from itertools import product
from lpjguess_runner import simulation, TopLevelParameter, BlockParameter

param_grid = {
    "wateruptake": ["wcont", "rootdist"],
    "npatch": range(1, 50, 10)
}

sims = []
for (wu, npatch) in product(param_grid["wateruptake"], param_grid["npatch"]):
    factors = [
        TopLevelParameter("wateruptake", str(wu)),
        TopLevelParameter("npatch", str(npatch))
    ]
    name = f"wu_{wu}_npatch_{npatch}"
    sims.append(simulation(name, factors))
# sims now contains one Simulation per combination.

for sim in sims:
    print(f"# {sim.Name}")
    print("\n".join([f"- {c.GetName()}" for c in sim.Changes]))
    print()

Notes

  • Methods may be called from background threads; keep handlers fast and thread-safe.
  • Output can be frequent; consider buffering or filtering.
  • Advanced: if implementing the .NET interfaces directly instead of subclassing these bases, the class must inherit from System.Object, call Object.__init__, and set a valid __namespace__ (for example "LpjGuess.Runner.Python"). Using the provided base classes is recommended.

Results and Outputs

run_simulations(...) returns an ExperimentResult with at least:

  • TotalJobs
  • SuccessfulJobs
  • FailedJobs
  • Error

Files are written under the output directory provided to RunSettings.Local. If jobs produce per-run subdirectories, those will appear under that directory.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

lpjguess_runner-1.0.1-py3-none-win_amd64.whl (1.0 MB view details)

Uploaded Python 3Windows x86-64

lpjguess_runner-1.0.1-py3-none-macosx_11_0_arm64.whl (1.0 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

File details

Details for the file lpjguess_runner-1.0.1-py3-none-win_amd64.whl.

File metadata

File hashes

Hashes for lpjguess_runner-1.0.1-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 82c21acc6e032127c2e39f8396d30db6e03664f15198f5bd6e6a81c9cf25fab1
MD5 56afaa5f9abab124ae9edc2139a5c91b
BLAKE2b-256 3da7af9f0dafd951b47a3c7458ed6821116affa3003807e1514846230cd6e0a8

See more details on using hashes here.

Provenance

The following attestation bundles were made for lpjguess_runner-1.0.1-py3-none-win_amd64.whl:

Publisher: ci.yml on hie-dave/lpjg-gui

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file lpjguess_runner-1.0.1-py3-none-manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for lpjguess_runner-1.0.1-py3-none-manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 1f55efdb51e88a9df633f50615d03eebe1d8662fedf799ebfa5df7ac102868ff
MD5 1468312e401cf59d537adbaf65906f8b
BLAKE2b-256 50b3b0337d3f6746b6319f8003b91dd7013b889d36a20698ba9a1abea9d08753

See more details on using hashes here.

Provenance

The following attestation bundles were made for lpjguess_runner-1.0.1-py3-none-manylinux2014_x86_64.whl:

Publisher: ci.yml on hie-dave/lpjg-gui

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file lpjguess_runner-1.0.1-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for lpjguess_runner-1.0.1-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 6d4a7aff5a924a24130525e55b9e45809c7e1d3077a4c01c7061f01f2bda77b8
MD5 a9e1bf4112ceb749b8edb3b94297affd
BLAKE2b-256 5741e3bd25dc9b2244c0282ecb93ba2a869e4d9c7ab675627d4c7687020f5a67

See more details on using hashes here.

Provenance

The following attestation bundles were made for lpjguess_runner-1.0.1-py3-none-macosx_11_0_arm64.whl:

Publisher: ci.yml on hie-dave/lpjg-gui

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page