From b308f193090ade21cecf63be093f65b45fd573a5 Mon Sep 17 00:00:00 2001 From: wpbonelli Date: Sat, 3 Oct 2026 04:58:32 -0700 Subject: [PATCH 1/2] fix(dfns): restore SFR ic's ncon lookup from IDM's deferred shape MODFLOW-ORG/modflow6#2923 changed gwf-sfr's connectiondata `ic` shape from `(ncon(ifno))` to IDM's deferred `(:)`, which the v2 schema rejects, so no DFN set including it validates. Map it back via _V1_SHAPE_FIXES. Co-Authored-By: Claude Opus 5.5 --- autotest/dfns/test_migrate.py | 11 +++++++++++ modflow_devtools/dfns/migrate_to_v2_0_0_dev2.py | 1 + 2 files changed, 12 insertions(+) diff --git a/autotest/dfns/test_migrate.py b/autotest/dfns/test_migrate.py index 6e9cb534..b44738a1 100644 --- a/autotest/dfns/test_migrate.py +++ b/autotest/dfns/test_migrate.py @@ -297,6 +297,17 @@ def test_migrate_ts_shapes(dfn_dir): assert ts.shape == ["time_series_names"] +@pytest.mark.parametrize("v1_shape", ["(ncon(ifno))", "(:)"]) +def test_migrate_sfr_ic_shape(dfn_dir, tmp_path, v1_shape): + for name in ("common", "gwf-sfr"): + text = (dfn_dir / f"{name}.dfn").read_text() + text = text.replace("shape (ncon(ifno))", f"shape {v1_shape}") + (tmp_path / f"{name}.dfn").write_text(text) + component = _migrate_dev3(tmp_path, "gwf-sfr") + conn = component.blocks["connectiondata"].fields["connectiondata"].item + assert conn.fields["ic"].shape == ["packagedata.ncon(ifno)"] + + def test_migrate_auxiliary_stays_self_sizing(dfn_dir): component = _migrate_dev3(dfn_dir, "gwf-chd") assert component.blocks["options"].fields["auxiliary"].shape == [] diff --git a/modflow_devtools/dfns/migrate_to_v2_0_0_dev2.py b/modflow_devtools/dfns/migrate_to_v2_0_0_dev2.py index ca8fe851..364ad3f4 100644 --- a/modflow_devtools/dfns/migrate_to_v2_0_0_dev2.py +++ b/modflow_devtools/dfns/migrate_to_v2_0_0_dev2.py @@ -19,6 +19,7 @@ # time series name, so the count is exactly `time_series_names`. _V1_SHAPE_FIXES: dict[tuple[str, str], str] = { ("utl-ts", "sfacval"): "(time_series_names)", + ("gwf-sfr", "ic"): "(ncon(ifno))", } From b046da62536aea54a887e6251b549c0ee798676f Mon Sep 17 00:00:00 2001 From: wpbonelli Date: Sat, 3 Oct 2026 05:12:45 -0700 Subject: [PATCH 2/2] fix(dfns): restore LAK connectiondata's sum(nlakeconn) shape MODFLOW-ORG/modflow6#3021 (LAK IDM integration) changes gwf-lak's connectiondata shape from `(sum(nlakeconn))` to `(nlakeconn)`, a packagedata column rather than a dim, which silently drops the list shape and the derived nlakeconn dim. Map it back via _V1_SHAPE_FIXES, now also applied to recarray shapes in _infer_list_shape_dims. Co-Authored-By: Claude Opus 5.5 --- autotest/dfns/test_migrate.py | 11 +++++++++++ modflow_devtools/dfns/migrate_to_v2_0_0_dev2.py | 6 ++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/autotest/dfns/test_migrate.py b/autotest/dfns/test_migrate.py index b44738a1..6f051121 100644 --- a/autotest/dfns/test_migrate.py +++ b/autotest/dfns/test_migrate.py @@ -308,6 +308,17 @@ def test_migrate_sfr_ic_shape(dfn_dir, tmp_path, v1_shape): assert conn.fields["ic"].shape == ["packagedata.ncon(ifno)"] +@pytest.mark.parametrize("v1_shape", ["(sum(nlakeconn))", "(nlakeconn)"]) +def test_migrate_lak_connectiondata_shape(dfn_dir, tmp_path, v1_shape): + for name in ("common", "gwf-lak"): + text = (dfn_dir / f"{name}.dfn").read_text() + text = text.replace("shape (sum(nlakeconn))", f"shape {v1_shape}") + (tmp_path / f"{name}.dfn").write_text(text) + component = _migrate_dev3(tmp_path, "gwf-lak") + assert component.blocks["connectiondata"].fields["connectiondata"].shape == ["nlakeconn"] + assert component.dims["nlakeconn"].value == "sum(packagedata.nlakeconn)" + + def test_migrate_auxiliary_stays_self_sizing(dfn_dir): component = _migrate_dev3(dfn_dir, "gwf-chd") assert component.blocks["options"].fields["auxiliary"].shape == [] diff --git a/modflow_devtools/dfns/migrate_to_v2_0_0_dev2.py b/modflow_devtools/dfns/migrate_to_v2_0_0_dev2.py index 364ad3f4..b651ad87 100644 --- a/modflow_devtools/dfns/migrate_to_v2_0_0_dev2.py +++ b/modflow_devtools/dfns/migrate_to_v2_0_0_dev2.py @@ -20,6 +20,7 @@ _V1_SHAPE_FIXES: dict[tuple[str, str], str] = { ("utl-ts", "sfacval"): "(time_series_names)", ("gwf-sfr", "ic"): "(ncon(ifno))", + ("gwf-lak", "connectiondata"): "(sum(nlakeconn))", } @@ -849,6 +850,7 @@ def visit_Call(self, node: ast.Call) -> ast.AST: def _infer_list_shape_dims( + name: str, blocks: dict[str, v2.Block], v1_fields: OMD, scope: "Literal['component', 'model', 'simulation']", @@ -867,7 +869,7 @@ def _infer_list_shape_dims( v1_shapes: dict[tuple[str, str], str] = {} for f in v1_fields.values(multi=True): if (f.get("type") or "").startswith("recarray"): - shape = (f.get("shape") or "").strip() + shape = _V1_SHAPE_FIXES.get((name, f["name"]), f.get("shape") or "").strip() if shape: v1_shapes[(f["block"], f["name"])] = shape @@ -1950,7 +1952,7 @@ def _lookup(rname: str) -> dict | None: blocks = _fill_period_list_shapes(blocks, explicit_dims) blocks = _fill_named_list_shapes(blocks, explicit_dims) blocks, derived_dims = _infer_list_shape_dims( - blocks, fields, _dims_scope(name, parent), known_dims + name, blocks, fields, _dims_scope(name, parent), known_dims ) blocks = _fix_list_shapes(name, blocks) blocks = _fix_ts_sfac(name, blocks)