This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
- Windows-only (depends on a local MIKE+ install via pythonnet/CLR).
- Requires a valid MIKE+ license to run most functionality.
- At import time,
mikeplus/__init__.pyloads the .NET runtime (coreclr) and resolves MIKE+ assemblies fromC:/Program Files (x86)/DHI/MIKE+/<year>by default. Override with env varMIKEPLUSPY_INSTALL_ROOTpointing at the MIKE+ install root (parent ofbin). major_assembly_versioninmikeplus/__init__.pymust match the installed MIKE+ year (23 = 2025, 24 = 2026, …). The packageversioninpyproject.tomlis intentionally aligned with the MIKE+ year (e.g.2026.0.0).- Known conflicts (enforced at import via
mikeplus/conflicts.py):- Import order must be
mikeio→modelskill→mikeplus→mikeio1d. - Importing
mikeio1dbeforemikeplusraisesImportError. mikeioin the same process warns. SetMIKEPLUSPY_DISABLE_CONFLICT_CHECKS=trueto silence.
- Import order must be
Install dev deps (uses uv + hatchling):
uv pip install -e ".[dev]" # or .[test] / .[docs]Tests (pytest) — note that pyproject.toml sets addopts = ["-m not slow", "-s", ...], so by default slow tests are skipped:
pytest # fast suite only
pytest -m "slow" # just the slow tests
pytest -m "not license_required and not slow" # CI selector
pytest tests/database/test_tables.py::TestFoo::test_bar # single testAdditional markers defined in pyproject.toml: slow, license_required, timeout, skip_ci.
Lint / format / typecheck:
ruff check .
ruff format .
mypy mikeplusRuff is configured (in pyproject.toml) to only include pyproject.toml and mikeplus/**/*.py, and to skip mikeplus/tables/auto_generated/** and notebooks. Mypy also ignores the auto-generated tables package and DHI/.NET modules.
Regenerate auto-generated table classes (after a MIKE+ version bump or schema change):
python scripts/generate_tables.pyDocs build (requires quarto installed separately):
uv run docs/generate_table_docs.py # regenerate table docs sections
cd docs && uv run quartodoc build && uv run quarto renderTop-level entry points live on the mikeplus package:
mikeplus.open(path)/mikeplus.create(path, ...)(mikeplus/shortcuts.py) construct aDatabase.mikeplus.Database(mikeplus/database.py) wraps the .NETBaseDataSource+DataTableContainerfromDHI.Amelia.DataModule. It owns:tables→TableCollection(auto-generated, see below).scenarios→ScenarioCollectionbacked by a lazily-created .NETScenarioManager.alternative_groups→AlternativeGroupCollection._runner→SimulationRunner(mikeplus/simulation_runner.py), exposed viadb.run(...)for CS / EPANET / SWMM engines viaDHI.Amelia.Tools.EngineTool.
The database layer is a thin Python veneer over .NET objects:
mikeplus/tables/base_table.pywraps a .NETIMuTable. One concrete subclass per MIKE+ table is generated intomikeplus/tables/auto_generated/(≈330 files). Do not hand-edit auto-generated code — regenerate viascripts/generate_tables.py, which uses Jinja templates inscripts/table_templates/and introspects a live (or temporary) MIKE+ database to discover columns.mikeplus/queries.pyimplements a fluentSelectQuery / InsertQuery / UpdateQuery / DeleteQueryAPI (table.select().where(...).to_dataframe(), etc.) that builds and executes SQL against the .NET data source.mikeplus/dotnet.pycentralizes .NET interop helpers (type conversion, unwrapping proxy objects). Useget_implementation()/DotNetConverterrather than touching .NET types directly.mikeplus/tools/wraps individual MIKE+ GUI tools (ImportTool, TopoRepairTool, InterpolationTool, ConnectionRepairTool, CathSlopeLengthProcess). Each loads its ownDHI.Amelia.Tools.*assembly on import.mikeplus/scenarios/models MIKE+ scenarios and alternative groups; construction is lazy because the underlyingScenarioManagerrequires the DB to be open.
When adding new functionality, prefer extending the existing Python wrappers around the .NET objects (_net_table, _data_source, etc.) rather than introducing parallel abstractions.
tests/conftest.py defines a ladder of DB fixtures at session / module / class / function scope for each test database in tests/testdata/Db/. Use the most coarse-grained scope that is safe:
session_*_db/module_*_db/class_*_db: read-only, copied once per scope — fast, shared across tests. Modifications leak between tests in that scope.- Plain
sirius_db/epanet_demo_db/swmm_db/ etc.: function-scoped fresh copy — use whenever the test mutates the DB.
Available DBs: sirius, epanet_demo, swmm, flood, repair_tool, interpolate, connection_repair, catch_slope_len, import, river_junction_couple. Any test that actually runs a MIKE+ simulation or calls licensed APIs should be marked @pytest.mark.license_required and @pytest.mark.slow as appropriate.
See DEVELOPMENT.md for the full checklist. Short version: set MIKEPLUSPY_INSTALL_ROOT, regenerate tables (python scripts/generate_tables.py), review the diff under mikeplus/tables/auto_generated/, bump the assembly version in mikeplus/__init__.py, bump project.version in pyproject.toml to the new MIKE+ year, regenerate docs/_table_generated_sections.yml, and run the full test suite + notebooks.
MIKE+Py writes directly to .sqlite / .mupp files with no undo. When working with example or user-provided databases, operate on a copy (the test fixtures already do this).