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.2-py3-none-win_amd64.whl (1.0 MB view details)

Uploaded Python 3Windows x86-64

lpjguess_runner-1.0.2-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.2-py3-none-win_amd64.whl.

File metadata

File hashes

Hashes for lpjguess_runner-1.0.2-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 72b85d4b946143f7d5d500c42a1644ca8b84deeca88935ff205925fecd73e288
MD5 3401efa36e3f6fc71841326010006f29
BLAKE2b-256 5df31b6a5b33120c4c4be65ed8e337c579a2ff6f99c46664f7f46d14d50eafd4

See more details on using hashes here.

Provenance

The following attestation bundles were made for lpjguess_runner-1.0.2-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.2-py3-none-manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for lpjguess_runner-1.0.2-py3-none-manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 c4452ff844b2e3c25e66e38383311deff4277950a3122eb011fb96c1d0ee8d84
MD5 0aa1f652552c0bd9ae25e177489740a6
BLAKE2b-256 91e3e73cdbec06cb6cf29b4bbd39dc88f803db310c709925beff0f1d4eaac754

See more details on using hashes here.

Provenance

The following attestation bundles were made for lpjguess_runner-1.0.2-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.2-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for lpjguess_runner-1.0.2-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 cede37712384bd5a4b0cb29b6c9049f642aea841849b971b96c93a9d57d4f785
MD5 bedf628c96646f75e3e7908bab473588
BLAKE2b-256 ce4d9268735f78683f4c559b2e5f8eea339220e066819f910f80ed8122cfb34f

See more details on using hashes here.

Provenance

The following attestation bundles were made for lpjguess_runner-1.0.2-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