Skip to content

Nodes-filtered network cannot be matched — time coordinate degrades to object dtype #676

Description

@jpalm3r

Summary

A network created with nodes=[...] filtering (topology-only nodes for the rest) cannot be matched. ms.match() fails with:

AssertionError: time must be datetime

The underlying data values are correct — only the time coordinate's dtype is wrong.

Root cause

Network._build_dataframe (src/modelskill/network.py:497-508) drops nodes with no data using if v["data"] is not None (line 500), then concatenates the rest:

data_in_nodes = {
    k: v["data"] for k, v in g.nodes.items() if v["data"] is not None
}
...
df = pd.concat(data_in_nodes, axis=1)

But topology-only nodes never store None. Res1DNode.__init__ and GridPoint.__init__ (src/modelskill/model/adapters/_res1d.py:45,66) both default missing data to an empty pd.DataFrame(), so the is not None check never filters anything out.

Concatenating an empty (default RangeIndex) DataFrame alongside real DatetimeIndex-indexed DataFrames degrades the result's index to object dtype:

>>> pd.concat({'real': real_df, 'topo': pd.DataFrame()}, axis=1).index.dtype
dtype('O')

This flows through Network.to_dataset() (network.py:532-547) into the model result dataset, and eventually fails the assertion in _validate_dataset (src/modelskill/timeseries/_timeseries.py:79), raised inside NodeModelResult.__init__ — far from where the dtype actually broke.

Reproduction

network = Network.from_res1d("network.res1d", nodes=["108", "101"], reaches=[])
network._df.index.dtype  # object, should be datetime64[ns]

tests/test_network.py already has tests covering this exact nodes=[...], reaches=[] combination (test_from_res1d_nodes_filter_only_selected_have_data, test_dataframe_from_partial_network, lines 542-584), but none of them check the time dtype, so this passes CI today.

Workaround

mr.data = mr.data.assign_coords(time=pd.DatetimeIndex(mr.data.time.to_index()))

Suggested fixes

Any of these would work:

  • Store None (not an empty DataFrame) for topology-only nodes, and keep the existing is not None filter.
  • Filter out empty DataFrames before the pd.concat in _build_dataframe.
  • Coerce the index to DatetimeIndex explicitly after building the dataset.

Found by a test user evaluating the network functionality ahead of a 1.4.0 release.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions