Skip to content

Commit a833749

Browse files
authored
Add export_to_netcdf and rename export method
Refactor export methods to support CSV and NetCDF formats.
1 parent f999ab7 commit a833749

1 file changed

Lines changed: 84 additions & 2 deletions

File tree

src/output.py

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
import warnings
66
import numbers
77

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
910

1011
import numpy as np
1112
import pandas as pd
13+
import xarray as xr
1214

1315
class Output():
1416
"""
@@ -301,7 +303,7 @@ def microbes_tradeoff(self, ecosystem, year, day):
301303
self.Growth_yield = pd.concat([self.Growth_yield,GY_grid],axis=1,sort=False)
302304

303305

304-
def export(self, base_path: Path | str) -> None:
306+
def export_to_csv(self, base_path: Path | str) -> None:
305307
"""Export contents of the output file to a directory.
306308
307309
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:
348350

349351
# Print numbers
350352
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

Comments
 (0)