diff --git a/CHANGELOG.md b/CHANGELOG.md index d25b488b..afa4a2a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # CHANGELOG +## v0.13.3. - 8 April 2026 + +- Enable Pandas v3. +- Replaces some instances of `pd.DataFrame.values` with `pd.DataFrame.to_numpy()` to avoid + compatibility issues in Pandas 2.3.3 and Pandas 3+. +- Cast the weather datetime index to "us" instead of "ns" to match updated Pandas 3 and Polars + defaults. +- Cost columns are forced to be float columns to ensure for Pandas dtypes compatibility. +- Fixes a bug in `Metrics.vessel_crew_hours_at_sea` where equipment-wise aggregations fail at + frequencies more granular than the project level. + ## v0.13.2 - 10 February 2026 - Temporarily prohibit Pandas 3.0 to resolve failing tests and integrations. diff --git a/pyproject.toml b/pyproject.toml index cba7541d..1f61e970 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ dependencies = [ "attrs>=24.1.0", "numpy>=1.21", "scipy>=1.8", - "pandas>=2,<3", + "pandas>=2", "polars>=1.33.1", "pyarrow>=10", "jupyterlab>=3", diff --git a/wombat/__init__.py b/wombat/__init__.py index f8541f3e..e4abecec 100644 --- a/wombat/__init__.py +++ b/wombat/__init__.py @@ -4,4 +4,4 @@ from wombat.core.library import create_library_structure, load_yaml -__version__ = "0.13.2" +__version__ = "0.13.3" diff --git a/wombat/core/library.py b/wombat/core/library.py index 46bbeef9..018957dd 100644 --- a/wombat/core/library.py +++ b/wombat/core/library.py @@ -330,7 +330,7 @@ def format_weather(weather: pd.DataFrame) -> pl.DataFrame: .with_row_index() .with_columns( [ - pl.col("datetime").cast(pl.Datetime).dt.cast_time_unit("ns"), + pl.col("datetime").cast(pl.Datetime).dt.cast_time_unit("us"), (pl.col("datetime").dt.hour()).alias("hour"), ] ) diff --git a/wombat/core/post_processor.py b/wombat/core/post_processor.py index 8d956c1a..1eefce7f 100644 --- a/wombat/core/post_processor.py +++ b/wombat/core/post_processor.py @@ -382,13 +382,16 @@ def _apply_inflation_rate(self, events: pd.DataFrame) -> pd.DataFrame: pd.DataFrame The events dataframe with costs adjusted for inflation. """ + events = events.astype(dict.fromkeys(self._cost_columns, "float")) + adjusted_inflation = deepcopy(self.inflation_rate) years = events.year.unique() years.sort() for year in years: - row_filter = events.year == year if year > years[0]: - events.loc[row_filter, self._cost_columns] *= adjusted_inflation + events.loc[events.year.eq(year), self._cost_columns] *= ( + adjusted_inflation + ) adjusted_inflation *= self.inflation_rate return events @@ -1022,15 +1025,15 @@ def vessel_crew_hours_at_sea( return pd.DataFrame(at_sea.sum()[["duration"]]).T.rename( columns={"duration": "Total Crew Hours at Sea"} ) - additional_cols = frequency.group_cols + time_cols = frequency.group_cols total_hours = ( total_hours.drop(columns=frequency.drop_cols) - .groupby(group_cols)[["N"]] + .groupby(time_cols)[["N"]] .sum() ) - columns = additional_cols + columns - group_cols.extend(additional_cols) + columns = time_cols + columns + group_cols.extend(time_cols) at_sea = at_sea[group_cols + ["duration"]].groupby(group_cols).sum() if by_equipment: total = [] @@ -1043,7 +1046,7 @@ def vessel_crew_hours_at_sea( total_hours = ( total_hours.reset_index() .rename(columns={"N": "Total Crew Hours at Sea"})[columns] - .set_index(additional_cols) + .set_index(time_cols) ) return total_hours diff --git a/wombat/windfarm/windfarm.py b/wombat/windfarm/windfarm.py index 9a64f8b0..210cf153 100644 --- a/wombat/windfarm/windfarm.py +++ b/wombat/windfarm/windfarm.py @@ -165,9 +165,9 @@ def _create_graph_layout(self, windfarm_layout: str | pd.DataFrame) -> None: windfarm, dict(layout[["id", "type"]].values), name="type" ) - self.turbine_id: np.ndarray = layout.loc[turbine_filter, "id"].values - self.substation_id = layout.loc[substation_filter, "id"].values - self.electrolyzer_id = layout.loc[electrolyzer_filter, "id"].values + self.turbine_id: np.ndarray = layout.loc[turbine_filter, "id"].to_numpy() + self.substation_id = layout.loc[substation_filter, "id"].to_numpy() + self.electrolyzer_id = layout.loc[electrolyzer_filter, "id"].to_numpy() for substation in self.substation_id: windfarm.nodes[substation]["connection"] = layout.loc[