Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/full_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.9", "3.13"]
python-version: ["3.10", "3.14"]

steps:
- uses: actions/checkout@v4
Expand Down
13 changes: 8 additions & 5 deletions .github/workflows/full_test_self_hosted.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ name: Full Test (self-hosted)
on:
workflow_dispatch:

push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
# Disabled for now: the organization self-hosted runner this workflow's
# `test` job depends on was removed, and MIKE+ CI/CD is being reworked.
# Re-enable these triggers once a runner is available again.
# push:
# branches: [ "main" ]
# pull_request:
# branches: [ "main" ]

jobs:
lint:
Expand Down Expand Up @@ -40,7 +43,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.13"]
python-version: ["3.10", "3.14"]

steps:
- uses: actions/checkout@v4
Expand Down
40 changes: 40 additions & 0 deletions .github/workflows/pr_checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: PR Checks

# Lightweight checks that run on every PR (and pushes to main) without needing
# the MIKE+ self-hosted runner: linting and type checking only, no tests. These
# mirror the lint/typecheck jobs in python_publish.yml so PRs get the same
# baseline coverage while MIKE+ CI/CD is being reworked.

on:
workflow_dispatch:

push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Ruff
uses: chartboost/ruff-action@v1
with:
src: "." # Adjust the source directory as needed

typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install test dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -e .[test]
- name: Run mypy
run: |
mypy mikeplus/
2 changes: 1 addition & 1 deletion .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
runs-on: windows-latest
strategy:
matrix:
python-version: ['3.9', '3.12']
python-version: ['3.10', '3.14']

steps:
- uses: actions/checkout@v4
Expand Down
67 changes: 36 additions & 31 deletions .github/workflows/python_publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,37 +35,42 @@ jobs:
run: |
mypy mikeplus/

test:
runs-on: self-hosted
needs: [lint, typecheck]
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.13"]

steps:
- uses: actions/checkout@v4

- name: Set up UV and Python ${{ matrix.python-version }}
id: setup-uv
uses: astral-sh/setup-uv@v5
with:
python-version: ${{ matrix.python-version }}
enable-cache: true
cache-dependency-glob: |
**/requirements*.txt
**/setup.py
**/pyproject.toml

- name: Install project dependencies and test requirements
run: |
uv pip install -e .[test]

- name: Test with pytest
run: |
uv run pytest -v -m "not skip_ci"
# Disabled for now: this `test` job ran on the organization self-hosted
# runner, which was removed while MIKE+ CI/CD is being reworked. Publishing
# still runs (lint + typecheck below), it just no longer runs the tests.
# Re-enable this job (and add `test` back to `deploy.needs`) once a runner
# is available again.
# test:
# runs-on: self-hosted
# needs: [lint, typecheck]
# strategy:
# fail-fast: false
# matrix:
# python-version: ["3.10", "3.14"]
#
# steps:
# - uses: actions/checkout@v4
#
# - name: Set up UV and Python ${{ matrix.python-version }}
# id: setup-uv
# uses: astral-sh/setup-uv@v5
# with:
# python-version: ${{ matrix.python-version }}
# enable-cache: true
# cache-dependency-glob: |
# **/requirements*.txt
# **/setup.py
# **/pyproject.toml
#
# - name: Install project dependencies and test requirements
# run: |
# uv pip install -e .[test]
#
# - name: Test with pytest
# run: |
# uv run pytest -v -m "not skip_ci"
deploy:
needs: [lint, typecheck, test]
needs: [lint, typecheck]
runs-on: windows-latest

permissions:
Expand All @@ -77,7 +82,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.13'
python-version: '3.14'
- name: Set up .NET
uses: actions/setup-dotnet@v3
with:
Expand Down
28 changes: 22 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,38 @@

### Added

- Support for MIKE+ 2026.
- Improved developer documentation for releases.

### Changed

### Deprecated

### Removed

- DataTableAccess: this was previously marked for deprecation and is now replaced by Database.
- Engine classes (FloodEngine, EPANET, MIKE1D): these were previously marked for deprecation and are now replaced by SimulationRunner.

### Fixed

### Security

## [2026.1.0] - 2026-06-23

### Added

- Support for MIKE+ 2026 Update 1.

### Fixed

- Inserting a row with no field values on MIKE+ 2026 Update 1.

## [2026.0.0] - 2026-01-29

### Added

- Support for MIKE+ 2026.
- Improved developer documentation for releases.

### Removed

- DataTableAccess: this was previously marked for deprecation and is now replaced by Database.
- Engine classes (FloodEngine, EPANET, MIKE1D): these were previously marked for deprecation and are now replaced by SimulationRunner.

## [2025.6.0] - 2025-12-12

### Added
Expand Down
14 changes: 14 additions & 0 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@

This is a space for documenting standard development and maintenance processes.

## Compatibility principle: no breaking changes within a year line

Within a MIKE+ year (all `2026.x.x` releases), avoid changes that break users
regardless of which update (GA, U1, U2, …) of that year they have installed.
Save genuine breaks for the next year bump.

- Adding tables/columns is fine — they degrade gracefully against older
assemblies (`GetTable` returns `None`, `BaseTable.__init__` warns and
continues). Don't add code that hard-fails on a missing table/column. Avoid
removing or renaming mid-year.
- For changed .NET signatures, support both forms (try new, fall back to old)
rather than swapping. See `SimulationRunner.__init__`. Tag fallbacks with
`TODO(<next year>)` for cleanup at the year bump.

## Release process for new MIKE+ versions

When a new version of MIKE+ is released, the following needs to be done before releasing a corresponing Python vesrion:
Expand Down
4 changes: 4 additions & 0 deletions docs/_table_generated_sections.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ sections:
- m2d_ADPrecipitationArea
- m2d_ADPrecipitationD
- m2d_BndDistributedSource
- m2d_BndGlobalSetting
- m2d_BndQHRelation
- m2d_Boundary
- m2d_Coupling
Expand Down Expand Up @@ -54,6 +55,7 @@ sections:
- m_CustomConfig
- m_CustomUnit
- m_DefaultValue
- m_GenericSetting
- m_GlobalParameter
- m_Measurement
- m_Media
Expand Down Expand Up @@ -240,6 +242,7 @@ sections:
- mss_Inflow
- mss_InflowD
- mss_Inlet
- mss_InletConduitCon
- mss_LIDControl
- mss_LIDControlD
- mss_LIDusage
Expand Down Expand Up @@ -322,6 +325,7 @@ sections:
- mw_Valve
- mw_WDOAggSensors
- mw_WDOAggSensorsD
- mw_WDOAmi
- mw_WDOComparisons
- mw_WDOControls
- mw_WDODemandPredictions
Expand Down
2 changes: 1 addition & 1 deletion mikeplus/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""MIKE+Py package."""

__version__ = "2026.0.0"
__version__ = "2026.1.0"


from pathlib import Path
Expand Down
15 changes: 9 additions & 6 deletions mikeplus/dotnet.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import clr # noqa: F401
import datetime
from typing import Any, Dict, Optional
from typing import Any, Dict

import pandas as pd

Expand Down Expand Up @@ -119,25 +119,28 @@ def from_dotnet_value(value: Any) -> Any:
@staticmethod
def to_dotnet_dictionary(
py_dict: Dict[str, Any],
) -> Optional[Dictionary[String, Object]]:
) -> Dictionary[String, Object]:
"""Convert a Python dictionary to a .NET Dictionary.

An empty/falsy ``py_dict`` yields an empty .NET Dictionary (not ``None``);
some MIKE+ 2026 U1 insert APIs NullReference on a ``null`` fields argument.

Parameters
----------
py_dict : Dict[str, Any]
Python dictionary to convert

Returns
-------
Dictionary[String, Object] or None
Dictionary[String, Object]
.NET Dictionary with converted values

"""
if not py_dict:
return None

net_dict = Dictionary[String, Object]()

if not py_dict:
return net_dict

for key, value in py_dict.items():
net_dict[key] = DotNetConverter.to_dotnet_value(value)

Expand Down
6 changes: 1 addition & 5 deletions mikeplus/queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,11 +348,7 @@ def _execute_impl(self) -> str:
non_ud_values = {k: v for k, v in values.items() if k not in ud_columns}
ud_values = {k: v for k, v in values.items() if k in ud_columns}

net_values = (
DotNetConverter.to_dotnet_dictionary(non_ud_values)
if non_ud_values
else None
)
net_values = DotNetConverter.to_dotnet_dictionary(non_ud_values)

_, inserted_muid = net_table.InsertByCommand(
muid,
Expand Down
13 changes: 11 additions & 2 deletions mikeplus/simulation_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,17 @@ def __init__(self, database: Database):
>>> runner = SimulationRunner(db)
"""
self._database = database
self._engine_tool = EngineTool()
self._engine_tool.DataTables = self._database._data_table_container
# MIKE+ 2026 U1 changed the EngineTool constructor signature. To avoid a
# breaking change within the 2026 line, support both: try the U1+ form
# (container passed to the constructor), then fall back to the 2026 GA
# form (parameterless constructor + DataTables property). pythonnet raises
# TypeError when no constructor overload matches the given arguments.
# TODO(2027): drop the GA fallback once 2026 GA installs have aged out.
try:
self._engine_tool = EngineTool(self._database._data_table_container)
except TypeError:
self._engine_tool = EngineTool()
self._engine_tool.DataTables = self._database._data_table_container

def run(
self,
Expand Down
Loading
Loading