|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Environment |
| 6 | + |
| 7 | +- Windows-only (depends on a local MIKE+ install via pythonnet/CLR). |
| 8 | +- Requires a valid MIKE+ license to run most functionality. |
| 9 | +- At import time, `mikeplus/__init__.py` loads the .NET runtime (`coreclr`) and resolves MIKE+ assemblies from `C:/Program Files (x86)/DHI/MIKE+/<year>` by default. Override with env var `MIKEPLUSPY_INSTALL_ROOT` pointing at the MIKE+ install root (parent of `bin`). |
| 10 | +- `major_assembly_version` in `mikeplus/__init__.py` must match the installed MIKE+ year (23 = 2025, 24 = 2026, …). The package `version` in `pyproject.toml` is intentionally aligned with the MIKE+ year (e.g. `2026.0.0`). |
| 11 | +- Known conflicts (enforced at import via `mikeplus/conflicts.py`): |
| 12 | + - Import order must be `mikeio` → `modelskill` → `mikeplus` → `mikeio1d`. |
| 13 | + - Importing `mikeio1d` before `mikeplus` raises `ImportError`. |
| 14 | + - `mikeio` in the same process warns. Set `MIKEPLUSPY_DISABLE_CONFLICT_CHECKS=true` to silence. |
| 15 | + |
| 16 | +## Common commands |
| 17 | + |
| 18 | +Install dev deps (uses `uv` + `hatchling`): |
| 19 | + |
| 20 | +```bash |
| 21 | +uv pip install -e ".[dev]" # or .[test] / .[docs] |
| 22 | +``` |
| 23 | + |
| 24 | +Tests (pytest) — note that `pyproject.toml` sets `addopts = ["-m not slow", "-s", ...]`, so by default slow tests are skipped: |
| 25 | + |
| 26 | +```bash |
| 27 | +pytest # fast suite only |
| 28 | +pytest -m "slow" # just the slow tests |
| 29 | +pytest -m "not license_required and not slow" # CI selector |
| 30 | +pytest tests/database/test_tables.py::TestFoo::test_bar # single test |
| 31 | +``` |
| 32 | + |
| 33 | +Additional markers defined in `pyproject.toml`: `slow`, `license_required`, `timeout`, `skip_ci`. |
| 34 | + |
| 35 | +Lint / format / typecheck: |
| 36 | + |
| 37 | +```bash |
| 38 | +ruff check . |
| 39 | +ruff format . |
| 40 | +mypy mikeplus |
| 41 | +``` |
| 42 | + |
| 43 | +Ruff 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. |
| 44 | + |
| 45 | +Regenerate auto-generated table classes (after a MIKE+ version bump or schema change): |
| 46 | + |
| 47 | +```bash |
| 48 | +python scripts/generate_tables.py |
| 49 | +``` |
| 50 | + |
| 51 | +Docs build (requires quarto installed separately): |
| 52 | + |
| 53 | +```bash |
| 54 | +uv run docs/generate_table_docs.py # regenerate table docs sections |
| 55 | +cd docs && uv run quartodoc build && uv run quarto render |
| 56 | +``` |
| 57 | + |
| 58 | +## Architecture |
| 59 | + |
| 60 | +Top-level entry points live on the `mikeplus` package: |
| 61 | + |
| 62 | +- `mikeplus.open(path)` / `mikeplus.create(path, ...)` (`mikeplus/shortcuts.py`) construct a `Database`. |
| 63 | +- `mikeplus.Database` (`mikeplus/database.py`) wraps the .NET `BaseDataSource` + `DataTableContainer` from `DHI.Amelia.DataModule`. It owns: |
| 64 | + - `tables` → `TableCollection` (auto-generated, see below). |
| 65 | + - `scenarios` → `ScenarioCollection` backed by a lazily-created .NET `ScenarioManager`. |
| 66 | + - `alternative_groups` → `AlternativeGroupCollection`. |
| 67 | + - `_runner` → `SimulationRunner` (`mikeplus/simulation_runner.py`), exposed via `db.run(...)` for CS / EPANET / SWMM engines via `DHI.Amelia.Tools.EngineTool`. |
| 68 | + |
| 69 | +The database layer is a thin Python veneer over .NET objects: |
| 70 | + |
| 71 | +- `mikeplus/tables/base_table.py` wraps a .NET `IMuTable`. One concrete subclass per MIKE+ table is generated into `mikeplus/tables/auto_generated/` (≈330 files). **Do not hand-edit auto-generated code** — regenerate via `scripts/generate_tables.py`, which uses Jinja templates in `scripts/table_templates/` and introspects a live (or temporary) MIKE+ database to discover columns. |
| 72 | +- `mikeplus/queries.py` implements a fluent `SelectQuery / InsertQuery / UpdateQuery / DeleteQuery` API (`table.select().where(...).to_dataframe()`, etc.) that builds and executes SQL against the .NET data source. |
| 73 | +- `mikeplus/dotnet.py` centralizes .NET interop helpers (type conversion, unwrapping proxy objects). Use `get_implementation()` / `DotNetConverter` rather than touching .NET types directly. |
| 74 | +- `mikeplus/tools/` wraps individual MIKE+ GUI tools (ImportTool, TopoRepairTool, InterpolationTool, ConnectionRepairTool, CathSlopeLengthProcess). Each loads its own `DHI.Amelia.Tools.*` assembly on import. |
| 75 | +- `mikeplus/scenarios/` models MIKE+ scenarios and alternative groups; construction is lazy because the underlying `ScenarioManager` requires the DB to be open. |
| 76 | + |
| 77 | +When adding new functionality, prefer extending the existing Python wrappers around the .NET objects (`_net_table`, `_data_source`, etc.) rather than introducing parallel abstractions. |
| 78 | + |
| 79 | +## Test fixtures |
| 80 | + |
| 81 | +`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: |
| 82 | + |
| 83 | +- `session_*_db` / `module_*_db` / `class_*_db`: **read-only**, copied once per scope — fast, shared across tests. Modifications leak between tests in that scope. |
| 84 | +- Plain `sirius_db` / `epanet_demo_db` / `swmm_db` / etc.: function-scoped fresh copy — use whenever the test mutates the DB. |
| 85 | + |
| 86 | +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. |
| 87 | + |
| 88 | +## Releasing for a new MIKE+ version |
| 89 | + |
| 90 | +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. |
| 91 | + |
| 92 | +## Caution |
| 93 | + |
| 94 | +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). |
0 commit comments