|
5 | 5 | import warnings |
6 | 6 | import numbers |
7 | 7 |
|
8 | | -from initialization import export_initialization_dict |
| 8 | +from initialization import export_initialization_dict_to_csv |
| 9 | +from initialization import export_initialization_dict_to_netcdf |
9 | 10 |
|
10 | 11 | import numpy as np |
11 | 12 | import pandas as pd |
| 13 | +import xarray as xr |
12 | 14 |
|
13 | 15 | class Output(): |
14 | 16 | """ |
@@ -301,7 +303,7 @@ def microbes_tradeoff(self, ecosystem, year, day): |
301 | 303 | self.Growth_yield = pd.concat([self.Growth_yield,GY_grid],axis=1,sort=False) |
302 | 304 |
|
303 | 305 |
|
304 | | - def export(self, base_path: Path | str) -> None: |
| 306 | + def export_to_csv(self, base_path: Path | str) -> None: |
305 | 307 | """Export contents of the output file to a directory. |
306 | 308 |
|
307 | 309 | Exports each class member of type pandas.DataFrame to a separate CSV file. |
@@ -348,3 +350,83 @@ def export(self, base_path: Path | str) -> None: |
348 | 350 |
|
349 | 351 | # Print numbers |
350 | 352 | pd.Series(scalar_numbers).to_csv(base_path / "scalars.csv") |
| 353 | + |
| 354 | + def export_to_netcdf(self, base_path: Path | str) -> None: |
| 355 | + """Export contents of the output file to a directory in NetCDF format. |
| 356 | + - Each pandas.DataFrame member is saved to a separate .nc file. |
| 357 | + - All pandas.Series members are combined and saved to a single 'series.nc' file. |
| 358 | + - All scalar numerical members are grouped and saved to 'scalars.nc'. |
| 359 | +
|
| 360 | + Parameters: |
| 361 | + base_path : Path |
| 362 | + A path that names the root directory where contents will be exported. |
| 363 | + If the directory does not exist it will be created. |
| 364 | + """ |
| 365 | + # Create space for output |
| 366 | + base_path = Path(base_path) |
| 367 | + base_path.mkdir(parents=True, exist_ok=True) |
| 368 | + |
| 369 | + # Collect all series and scalar data |
| 370 | + series_data = dict() |
| 371 | + scalar_numbers = dict() |
| 372 | + |
| 373 | + for name, member in vars(self).items(): |
| 374 | + # convert each DataFrame to an xarray Dataset and save to .nc |
| 375 | + if isinstance(member, pd.DataFrame): |
| 376 | + # Ensure column names are strings |
| 377 | + member.columns = member.columns.astype(str) |
| 378 | + if member.index.name is not None: |
| 379 | + member.index.name = str(member.index.name) |
| 380 | + fname = name + ".nc" # use the .nc extension |
| 381 | + try: |
| 382 | + xarray_member = xr.Dataset.from_dataframe(member) |
| 383 | + xarray_member.to_netcdf(base_path / fname) |
| 384 | + except Exception as e: |
| 385 | + warnings.warn( |
| 386 | + f"Could not export DataFrame '{name}' to NetCDF. Error: {e}" |
| 387 | + ) |
| 388 | + |
| 389 | + elif isinstance(member, pd.Series): |
| 390 | + series_data[name] = member |
| 391 | + |
| 392 | + elif isinstance(member, numbers.Number): |
| 393 | + scalar_numbers[name] = member |
| 394 | + |
| 395 | + elif name == "Initialization": |
| 396 | + # Special case - Initialization dictionary |
| 397 | + # Serialise it to a subfolder |
| 398 | + path = base_path / name |
| 399 | + export_initialization_dict_to_netcdf(path, member) |
| 400 | + elif isinstance(member, pd.Series): |
| 401 | + xrmember = xr.DataArray(member) |
| 402 | + fname = name + ".nc" |
| 403 | + xrmember.to_netcdf(base_path / fname) |
| 404 | + |
| 405 | + else: |
| 406 | + warnings.warn( |
| 407 | + f"Output member '{name}' has unsupported type '{type(member)}'. " |
| 408 | + f"It has not been exported to the output directory '{base_path}'." |
| 409 | + ) |
| 410 | + |
| 411 | + # process and save Series |
| 412 | + if series_data: |
| 413 | + try: |
| 414 | + # Combine all Series into a single DataFrame. |
| 415 | + combined_series_df = pd.concat(series_data, axis=1) |
| 416 | + # Convert the combined DataFrame to an xarray Dataset. |
| 417 | + series_dataset = xr.Dataset.from_dataframe(combined_series_df) |
| 418 | + # Save the Series Dataset to a single NetCDF file. |
| 419 | + series_dataset.to_netcdf(base_path / "series.nc") |
| 420 | + except ValueError as e: |
| 421 | + # This handles the "duplicate labels" error if it occurs. |
| 422 | + warnings.warn( |
| 423 | + f"Could not export combined series due to an error: {e}. " |
| 424 | + "Consider cleaning the index of your Series data first." |
| 425 | + ) |
| 426 | + |
| 427 | + if scalar_numbers: |
| 428 | + # Create an xarray Dataset directly from the dictionary of scalars. |
| 429 | + # Each key will become a variable in the NetCDF file. |
| 430 | + scalars_dataset = xr.Dataset(scalar_numbers) |
| 431 | + # Save the scalars Dataset to a NetCDF file. |
| 432 | + scalars_dataset.to_netcdf(base_path / "scalars.nc") |
0 commit comments