diff --git a/docs/source/using_the_ve/data/data.md b/docs/source/using_the_ve/data/data.md index 2ca2ebb69..09534cbcd 100644 --- a/docs/source/using_the_ve/data/data.md +++ b/docs/source/using_the_ve/data/data.md @@ -105,12 +105,16 @@ two methods: {class}`~virtual_ecosystem.core.data.Data` instance just using the standard dictionary assignment: ``data['var_name'] = data_array``. The Virtual Ecosystem {mod}`~virtual_ecosystem.core.readers` module provides the - function {func}`~virtual_ecosystem.core.readers.load_to_dataarray` to read data into - a DataArray from supported file formats. This can then be added directly to a Data - instance: + function {func}`~virtual_ecosystem.core.readers.load_to_dataarray` to read a list of + variables in a file into DataArrays from supported file formats. The returned value + is a dictionary of DataArrays keyed by the variable names and can then be added + directly to a Data instance: ```{code-block} ipython3 -data["var_name"] = load_to_dataarray("path/to/file.nc", var_name="temperature") +loaded_data = load_to_dataarray("path/to/file.nc", var_names=["temperature"]) +# iterate over the dictionary of variable names and arrays +for var_name, data_array in loaded_data.items(): + data[var_name] = data_array ``` 1. The {meth}`~virtual_ecosystem.core.data.Data.load_data_config` method takes a @@ -186,7 +190,11 @@ configured grid. ```{code-cell} ipython3 # Load data from a file file_path = Path("../../data/xy_dim.nc") -data["temp"] = load_to_dataarray(file_path, var_name="temp") +loaded_data = load_to_dataarray(file_path, var_names=["temp"]) + +# iterate over the dictionary of variable names and arrays +for var_name, data_array in loaded_data.items(): + data[var_name] = data_array ``` ```{code-cell} ipython3 diff --git a/tests/core/test_data.py b/tests/core/test_data.py index dd7472c8a..0c214e220 100644 --- a/tests/core/test_data.py +++ b/tests/core/test_data.py @@ -229,27 +229,27 @@ def test_Data_contains(fixture_data, var_name, expected): @pytest.mark.parametrize( - argnames=["name", "exp_log"], + argnames=["var_names", "exp_log"], argvalues=[ pytest.param( - "temp", + ["temp"], ( - (INFO, "Loading variable 'temp' from file:"), + (INFO, "Loading variables from file"), (INFO, "Adding data array for 'temp'"), ), id="simple_load", ), pytest.param( - "elev", + ["elev"], ( - (INFO, "Loading variable 'elev' from file:"), + (INFO, "Loading variables from file"), (INFO, "Replacing data array for 'elev'"), ), id="load_and_replace", ), ], ) -def test_Data_load_to_dataarray_naming(caplog, shared_datadir, name, exp_log): +def test_Data_load_to_dataarray_naming(caplog, shared_datadir, var_names, exp_log): """Test the coding of the name handling and replacement.""" # Setup a Data instance to match the example files generated in tests/core/data @@ -258,6 +258,8 @@ def test_Data_load_to_dataarray_naming(caplog, shared_datadir, name, exp_log): from virtual_ecosystem.core.grid import Grid from virtual_ecosystem.core.readers import load_to_dataarray + caplog.clear() + grid = Grid( grid_type="square", cell_nx=10, @@ -275,11 +277,14 @@ def test_Data_load_to_dataarray_naming(caplog, shared_datadir, name, exp_log): # Load the data from file datafile = shared_datadir / "cellid_coords.nc" - data[name] = load_to_dataarray(file=datafile, var_name=name) + results = load_to_dataarray(file=datafile, var_names=var_names) + for ky, val in results.items(): + data[ky] = val - # Check the naming has worked and the data are loaded - assert name in data - assert data[name].sum() == (20 * 100) + for name in var_names: + # Check the naming has worked and the data are loaded + assert name in data + assert data[name].sum() == (20 * 100) # Check the error reports log_check(caplog, exp_log) @@ -328,7 +333,7 @@ def fixture_load_data_grids(request): does_not_raise(), None, ( - (INFO, "Loading variable 'temp' from file:"), + (INFO, "Loading variables from file"), (INFO, "Adding data array for 'temp'"), ), 20 * 100, @@ -340,7 +345,7 @@ def fixture_load_data_grids(request): pytest.raises(ValueError), "Grid defines 100 cells, data provides 60", ( - (INFO, "Loading variable 'temp' from file:"), + (INFO, "Loading variables from file"), (INFO, "Adding data array for 'temp'"), (CRITICAL, "Grid defines 100 cells, data provides 60"), ), @@ -353,7 +358,7 @@ def fixture_load_data_grids(request): pytest.raises(ValueError), "Grid defines 100 cells, data provides 200", ( - (INFO, "Loading variable 'temp' from file:"), + (INFO, "Loading variables from file"), (INFO, "Adding data array for 'temp'"), (CRITICAL, "Grid defines 100 cells, data provides 200"), ), @@ -366,7 +371,7 @@ def fixture_load_data_grids(request): does_not_raise(), None, ( - (INFO, "Loading variable 'temp' from file:"), + (INFO, "Loading variables from file"), (INFO, "Adding data array for 'temp'"), ), 20 * 100, @@ -378,7 +383,7 @@ def fixture_load_data_grids(request): pytest.raises(ValueError), "The data cell ids do not provide a one-to-one map onto grid cell ids.", ( - (INFO, "Loading variable 'temp' from file:"), + (INFO, "Loading variables from file"), (INFO, "Adding data array for 'temp'"), ( CRITICAL, @@ -395,7 +400,7 @@ def fixture_load_data_grids(request): pytest.raises(ValueError), "The data cell ids do not provide a one-to-one map onto grid cell ids.", ( - (INFO, "Loading variable 'temp' from file:"), + (INFO, "Loading variables from file"), (INFO, "Adding data array for 'temp'"), ( CRITICAL, @@ -412,7 +417,7 @@ def fixture_load_data_grids(request): does_not_raise(), None, ( - (INFO, "Loading variable 'temp' from file:"), + (INFO, "Loading variables from file"), (INFO, "Adding data array for 'temp'"), ), 20 * 100, @@ -424,7 +429,7 @@ def fixture_load_data_grids(request): pytest.raises(ValueError), "Data XY dimensions do not match square grid", ( - (INFO, "Loading variable 'temp' from file:"), + (INFO, "Loading variables from file"), (INFO, "Adding data array for 'temp'"), (CRITICAL, "Data XY dimensions do not match square grid"), ), @@ -437,7 +442,7 @@ def fixture_load_data_grids(request): does_not_raise(), None, ( - (INFO, "Loading variable 'temp' from file:"), + (INFO, "Loading variables from file"), (INFO, "Adding data array for 'temp'"), ), 20 * 100, @@ -449,7 +454,7 @@ def fixture_load_data_grids(request): pytest.raises(ValueError), "Mapped points do not cover all cells.", ( - (INFO, "Loading variable 'temp' from file:"), + (INFO, "Loading variables from file"), (INFO, "Adding data array for 'temp'"), (CRITICAL, "Mapped points do not cover all cells."), ), @@ -462,7 +467,7 @@ def fixture_load_data_grids(request): pytest.raises(ValueError), "Mapped points fall outside grid.", ( - (INFO, "Loading variable 'temp' from file:"), + (INFO, "Loading variables from file"), (INFO, "Adding data array for 'temp'"), (CRITICAL, "Mapped points fall outside grid."), ), @@ -499,6 +504,8 @@ def test_Data_load_to_dataarray_data_handling( from virtual_ecosystem.core.data import Data from virtual_ecosystem.core.readers import load_to_dataarray + caplog.clear() + # Skip combinations where validator does not supported this grid if not ( ("__any__" in supported_grids) @@ -510,7 +517,8 @@ def test_Data_load_to_dataarray_data_handling( datafile = shared_datadir / filename with exp_error as err: - data["temp"] = load_to_dataarray(file=datafile, var_name="temp") + results = load_to_dataarray(file=datafile, var_names=["temp"]) + data["temp"] = results["temp"] # Check the data is in fact loaded and that a simple sum of values matches assert "temp" in data @@ -521,8 +529,6 @@ def test_Data_load_to_dataarray_data_handling( log_check(caplog, exp_log) - return - @pytest.mark.parametrize( argnames=["cfg_strings", "exp_error", "exp_msg", "exp_log"], @@ -546,13 +552,10 @@ def test_Data_load_to_dataarray_data_handling( None, ( (INFO, "Loading data from configuration"), - (INFO, "Loading variable 'temp' from file:"), + (INFO, "Loading variables from file"), (INFO, "Adding data array for 'temp'"), - (INFO, "Loading variable 'prec' from file:"), (INFO, "Adding data array for 'prec'"), - (INFO, "Loading variable 'elev' from file:"), (INFO, "Adding data array for 'elev'"), - (INFO, "Loading variable 'vapd' from file:"), (INFO, "Adding data array for 'vapd'"), ), id="valid config", @@ -587,14 +590,10 @@ def test_Data_load_to_dataarray_data_handling( ( (INFO, "Loading data from configuration"), (ERROR, "Duplicate variable names in data configuration"), - (INFO, "Loading variable 'temp' from file:"), + (INFO, "Loading variables from file"), (INFO, "Adding data array for 'temp'"), - (INFO, "Loading variable 'prec' from file:"), (INFO, "Adding data array for 'prec'"), - (INFO, "Loading variable 'elev' from file:"), (INFO, "Adding data array for 'elev'"), - (INFO, "Loading variable 'elev' from file:"), - (INFO, "Replacing data array for 'elev'"), (CRITICAL, "Data configuration did not load cleanly - check log"), ), id="repeated names", diff --git a/tests/core/test_readers.py b/tests/core/test_readers.py index b4bc9364e..f7e382d7c 100644 --- a/tests/core/test_readers.py +++ b/tests/core/test_readers.py @@ -39,11 +39,7 @@ ], ) def test_file_format_loader(caplog, file_types, expected_log): - """Tests the file format loader decorator. - - TODO - Note that the test here is actually changing the live DATA_LOADER_REGISTRY, - so that the tests are not independent of one another. - """ + """Tests the file format loader decorator.""" # Import register_data_loader - this triggers the registration of existing data # loaders so need to clear those log messages before trying new ones @@ -66,138 +62,195 @@ def test_func(): @pytest.mark.parametrize( - argnames=["file", "file_var", "exp_err", "expected_log"], + argnames=["file", "file_vars", "exp_err", "expected_log"], argvalues=[ - ( + pytest.param( "not_there.nc", - "irrelevant", + ["irrelevant"], pytest.raises(FileNotFoundError), ((CRITICAL, "Data file not found"),), + id="file_missing", ), - ( + pytest.param( "garbage.nc", - "irrelevant", + ["irrelevant"], pytest.raises(ValueError), ((CRITICAL, "Could not load data from"),), + id="file_misformatted", ), - ( + pytest.param( "xy_dim.nc", - "missing", + ["missing"], pytest.raises(KeyError), - ((CRITICAL, "Variable missing not found in"),), + ((CRITICAL, "Data variables not found in"),), + id="missing_var", ), - ( + pytest.param( "xy_dim.nc", - "temp", + ["temp"], + does_not_raise(), + (), + id="all_good_single_var", + ), + pytest.param( + "cellid_coords.nc", + ["temp", "prec", "elev"], does_not_raise(), (), + id="all_good_multi_var", + ), + pytest.param( + "cellid_coords.nc", + ["temp", "prec", "elves"], + pytest.raises(KeyError), + ((CRITICAL, "Data variables not found in"),), + id="missing_multi_var", ), ], ) -def test_load_netcdf(shared_datadir, caplog, file, file_var, exp_err, expected_log): - """Test the netdcf variable loader. - - The tests here are dependent on the test_file_format_loader, so cannot be run - individually. - """ +def test_load_netcdf(shared_datadir, caplog, file, file_vars, exp_err, expected_log): + """Test the netdcf variable loader.""" from virtual_ecosystem.core.readers import load_netcdf + caplog.clear() + with exp_err: - darray = load_netcdf(shared_datadir / file, file_var) - assert isinstance(darray, DataArray) + result = load_netcdf(shared_datadir / file, file_vars) + assert len(result) == len(file_vars) + assert list(result.keys()) == file_vars + for ky, val in result.items(): + assert isinstance(val, DataArray) # Check the error reports log_check(caplog, expected_log) @pytest.mark.parametrize( - argnames=["file", "file_var", "exp_err", "expected_log"], + argnames=["file", "file_vars", "exp_err", "expected_log"], argvalues=[ - ( + pytest.param( "not_there.csv", - "irrelevant", + ["irrelevant"], pytest.raises(FileNotFoundError), ((CRITICAL, "Data file not found"),), + id="file_missing", ), - ( + pytest.param( "garbage.csv", - "irrelevant", + ["irrelevant"], pytest.raises(ParserError), ((CRITICAL, "Could not load data from"),), + id="file_malformatted", ), - ( + pytest.param( "reader_test.csv", - "missing", + ["missing"], pytest.raises(KeyError), - ((CRITICAL, "Variable missing not found in"),), + ((CRITICAL, "Data variables not found in"),), + id="missing_var", ), - ( + pytest.param( "reader_test.csv", - "var1", + ["var1"], does_not_raise(), (), + id="all_good_single_var", + ), + pytest.param( + "reader_test.csv", + ["var1", "var2"], + does_not_raise(), + (), + id="all_good_multi_var", + ), + pytest.param( + "reader_test.csv", + ["var1", "var22"], + pytest.raises(KeyError), + ((CRITICAL, "Data variables not found in"),), + id="missing_multi_var", ), ], ) -def test_load_csv(shared_datadir, caplog, file, file_var, exp_err, expected_log): - """Test the netdcf variable loader. - - The tests here are dependent on the test_file_format_loader, so cannot be run - individually. - """ +def test_load_csv(shared_datadir, caplog, file, file_vars, exp_err, expected_log): + """Test the netdcf variable loader.""" from virtual_ecosystem.core.readers import load_csv + caplog.clear() + with exp_err: - darray = load_csv(shared_datadir / file, file_var) - assert isinstance(darray, DataArray) + result = load_csv(shared_datadir / file, file_vars) + assert len(result) == len(file_vars) + assert list(result.keys()) == file_vars + for ky, val in result.items(): + assert isinstance(val, DataArray) # Check the error reports log_check(caplog, expected_log) @pytest.mark.parametrize( - argnames=["file", "file_var", "exp_err", "expected_log"], + argnames=["file", "file_vars", "exp_err", "expected_log"], argvalues=[ - ( + pytest.param( "not_there.xlsx", - "irrelevant", + ["irrelevant"], pytest.raises(FileNotFoundError), ((CRITICAL, "Data file not found"),), + id="file_missing", ), - ( + pytest.param( "garbage.xlsx", - "irrelevant", + ["irrelevant"], pytest.raises(BadZipFile), ((CRITICAL, "Could not load data from"),), + id="file_malformatted", ), - ( + pytest.param( "reader_test.xlsx", - "missing", + ["missing"], pytest.raises(KeyError), - ((CRITICAL, "Variable missing not found in"),), + ((CRITICAL, "Data variables not found in"),), + id="missing_var", ), - ( + pytest.param( + "reader_test.xlsx", + ["var1"], + does_not_raise(), + (), + id="all_good_single_var", + ), + pytest.param( "reader_test.xlsx", - "var1", + ["var1", "var2"], does_not_raise(), (), + id="all_good_multi_var", + ), + pytest.param( + "reader_test.xlsx", + ["var1", "var22"], + pytest.raises(KeyError), + ((CRITICAL, "Data variables not found in"),), + id="missing_multi_var", ), ], ) -def test_load_excel(shared_datadir, caplog, file, file_var, exp_err, expected_log): - """Test the netdcf variable loader. - - The tests here are dependent on the test_file_format_loader, so cannot be run - individually. - """ +def test_load_excel(shared_datadir, caplog, file, file_vars, exp_err, expected_log): + """Test the netdcf variable loader.""" from virtual_ecosystem.core.readers import load_excel + caplog.clear() + with exp_err: - darray = load_excel(shared_datadir / file, file_var) - assert isinstance(darray, DataArray) + result = load_excel(shared_datadir / file, file_vars) + assert len(result) == len(file_vars) + assert list(result.keys()) == file_vars + for ky, val in result.items(): + assert isinstance(val, DataArray) # Check the error reports log_check(caplog, expected_log) @@ -224,7 +277,7 @@ def test_load_excel(shared_datadir, caplog, file, file_var, exp_err, expected_lo "cellid_dims.nc", does_not_raise(), None, - ((INFO, "Loading variable 'temp' from file:"),), + ((INFO, "Loading variables from file"),), 20 * 100, id="valid_netcdf", ), @@ -252,10 +305,11 @@ def test_load_to_dataarray( datafile = shared_datadir / filename with exp_error as err: - dataarray = load_to_dataarray(file=datafile, var_name="temp") + results = load_to_dataarray(file=datafile, var_names=["temp"]) # Check the data is in fact loaded and that a simple sum of values matches - dataarray.sum() == exp_sum_val + assert "temp" in results + assert results["temp"].sum() == exp_sum_val if err: assert str(err.value) == exp_msg diff --git a/virtual_ecosystem/core/data.py b/virtual_ecosystem/core/data.py index f6c88c227..c200f8968 100644 --- a/virtual_ecosystem/core/data.py +++ b/virtual_ecosystem/core/data.py @@ -70,7 +70,8 @@ The general solution for programmatically adding data from a file is to: * manually open a data file using an appropriate reader packages for the format, -* coerce the data into a properly structured :class:`~xarray.DataArray` object, and then +* coerce data from named variables into properly structured :class:`~xarray.DataArray` + objects, and then * use the :meth:`~virtual_ecosystem.core.data.Data.__setitem__` method to validate and add it to a :class:`~virtual_ecosystem.core.data.Data` instance. @@ -83,9 +84,10 @@ # Load temperature data from a supported file from virtual_ecosystem.core.readers import load_to_dataarray - data['temp'] = load_to_dataarray( - '/path/to/supported/format.nc', var_name='temperature' + results = load_to_dataarray( + '/path/to/supported/format.nc', var_names=['temperature'] ) + data['temperature'] = results['temperature'] Using a data configuration -------------------------- @@ -120,6 +122,7 @@ """ # noqa: D205 +from itertools import groupby from pathlib import Path from typing import Any @@ -336,19 +339,29 @@ def load_data_config(self, config: Config) -> None: LOGGER.error("Duplicate variable names in data configuration.") clean_load = False + # Group variables by file + variables = data_config["variable"] + variables.sort(key=lambda v: v["file"]) + file_groups = groupby(variables, key=lambda v: v["file"]) + # Load data from each data source - for each_var in data_config["variable"]: + for file, file_vars in file_groups: # Attempt to load the file, trapping exceptions as critical logger # messages and defer failure until the whole configuration has been # processed + try: - self[each_var["var_name"]] = load_to_dataarray( - file=Path(each_var["file"]), - var_name=each_var["var_name"], + loaded_data = load_to_dataarray( + file=Path(file), + var_names=[var["var_name"] for var in file_vars], ) + except Exception as err: LOGGER.error(str(err)) clean_load = False + else: + for var_name, data_array in loaded_data.items(): + self[var_name] = data_array if "constant" in data_config: msg = "Data config for constants not yet implemented." diff --git a/virtual_ecosystem/core/readers.py b/virtual_ecosystem/core/readers.py index ff057efbb..3d955bd75 100644 --- a/virtual_ecosystem/core/readers.py +++ b/virtual_ecosystem/core/readers.py @@ -1,8 +1,9 @@ """The :mod:`~virtual_ecosystem.core.readers` module provides the function -:func:`~virtual_ecosystem.core.readers.load_to_dataarray`, which is used to load data -from a file and convert it into a :class:`~xarray.DataArray` object. The ``DataArray`` -can then be added to a :class:`~virtual_ecosystem.core.data.Data` instance for use in a -Virtual Ecosystem simulation. +:func:`~virtual_ecosystem.core.readers.load_to_dataarray`, which is used to load a set +of data variables from a file and convert them into a dictionary of +:class:`~xarray.DataArray` objects. The ``DataArray`` values can then be added to a +:class:`~virtual_ecosystem.core.data.Data` instance for use in a Virtual Ecosystem +simulation. The module also supports the registration of different reader functions, used to convert files in different storage formats into a ``DataArray``. The @@ -53,7 +54,7 @@ def new_function_to_load_tif_data(...): .. code-block:: python - func(file: Path, var_name: str) -> DataArray + func(file: Path, var_names: str) -> dict[str, DataArray] """ @@ -98,12 +99,13 @@ def decorator_file_format_loader(func: Callable) -> Callable: @register_file_format_loader(file_types=(".nc",)) -def load_netcdf(file: Path, var_name: str) -> DataArray: +def load_netcdf(file: Path, var_names: list[str]) -> dict[str, DataArray]: """Loads a DataArray from a NetCDF file. Args: file: A Path for a NetCDF file containing the variable to load. - var_name: A string providing the name of the variable in the file. + var_names: A list of strings providing the names of the variables to be loaded + from the file. Raises: FileNotFoundError: with bad file path names. @@ -128,22 +130,26 @@ def load_netcdf(file: Path, var_name: str) -> DataArray: LOGGER.critical(to_raise) raise to_raise - # Check if file var is in the dataset - if var_name not in dataset: - to_raise = KeyError(f"Variable {var_name} not found in {file}") + # Check if file vars are in the dataset + missing_vars = set(var_names).difference(dataset.data_vars) + if missing_vars: + to_raise = KeyError( + f"Data variables not found in {file}: {', '.join(missing_vars)}" + ) LOGGER.critical(to_raise) raise to_raise - return dataset[var_name] + return {var: dataset[var] for var in var_names} @register_file_format_loader(file_types=(".csv",)) -def load_csv(file: Path, var_name: str) -> DataArray: +def load_csv(file: Path, var_names: list[str]) -> dict[str, DataArray]: """Loads a DataArray from a csv file. Args: file: A Path for a csv or excel file containing the variable to load. - var_name: A string providing the name of the variables in this file. + var_names: A list of strings providing the names of the variables to be loaded + from the file. Raises: FileNotFoundError: with bad file path names. @@ -165,31 +171,36 @@ def load_csv(file: Path, var_name: str) -> DataArray: raise to_raise # Check if file var is in the dataset - if var_name not in dataset.columns: - to_raise = KeyError(f"Variable {var_name} not found in {file}") + missing_vars = set(var_names).difference(dataset.columns) + if missing_vars: + to_raise = KeyError( + f"Data variables not found in {file}: {', '.join(missing_vars)}" + ) LOGGER.critical(to_raise) raise to_raise - return dataset[var_name].to_xarray() + return {var: dataset[var].to_xarray() for var in var_names} @register_file_format_loader(file_types=(".xlsx",)) -def load_excel(file: Path, var_name: str) -> DataArray: +def load_excel(file: Path, var_names: list[str]) -> dict[str, DataArray]: """Loads a DataArray from an excel file. Args: file: A Path for a csv or excel file containing the variable to load. - var_name: A string providing the name of the variables in this file. + var_names: A list of strings providing the names of the variables to be loaded + from the file. Raises: FileNotFoundError: with bad file path names. BadZipFile: if the excel file is corrupted. Exception: catches other exceptions from openpyxl. - Note: BadZipFile is the most common error thrown by openpyxl for corrupted excel - files, which is based on their internal processing files as zips. The general - exception is included to cover other possible issues from openpyxl, as it has - various other potential failure modes. + Note: + BadZipFile is the most common error thrown by openpyxl for corrupted excel + files, which is based on their internal processing files as zips. The general + exception is included to cover other possible issues from openpyxl, as it has + various other potential failure modes. """ to_raise: Exception @@ -211,29 +222,32 @@ def load_excel(file: Path, var_name: str) -> DataArray: raise to_raise # Check if file var is in the dataset - if var_name not in dataset.columns: - to_raise = KeyError(f"Variable {var_name} not found in {file}") + missing_vars = set(var_names).difference(dataset.columns) + if missing_vars: + to_raise = KeyError( + f"Data variables not found in {file}: {', '.join(missing_vars)}" + ) LOGGER.critical(to_raise) raise to_raise - return dataset[var_name].to_xarray() + return {var: dataset[var].to_xarray() for var in var_names} def load_to_dataarray( file: Path, - var_name: str, -) -> DataArray: + var_names: list[str], +) -> dict[str, DataArray]: """Loads data from a file into a DataArray. The function takes a path to a file format supported in the - :attr:`~virtual_ecosystem.core.readers.FILE_FORMAT_REGISTRY` and uses the - appropriate data loader function to load the data and convert it to a - {class}`~xarray.DataArray`, ready for insertion into a - :attr:`~virtual_ecosystem.core.data.Data` instance. + :attr:`~virtual_ecosystem.core.readers.FILE_FORMAT_REGISTRY` and a list of variable + names that are asserted to be stored in the file. It uses the appropriate data + loader function to load the data and convert it to a {class}`~xarray.DataArray`, + ready for insertion into a :attr:`~virtual_ecosystem.core.data.Data` instance. Args: file: A Path for the file containing the variable to load. - var_name: A string providing the name of the variable in the file. + var_names: A list of strings providing the names of variables in the file. Raises: ValueError: if there is no loader provided for the file format. @@ -249,8 +263,8 @@ def load_to_dataarray( raise to_raise # If so, load the data - LOGGER.info("Loading variable '%s' from file: %s", var_name, file) + LOGGER.info("Loading variables from file %s: %s", file, ", ".join(var_names)) loader = FILE_FORMAT_REGISTRY[file_type] - value = loader(file, var_name) + value = loader(file, var_names) return value diff --git a/virtual_ecosystem/models/soil/pools.py b/virtual_ecosystem/models/soil/pools.py index f6be84dd6..1acb1a4de 100644 --- a/virtual_ecosystem/models/soil/pools.py +++ b/virtual_ecosystem/models/soil/pools.py @@ -227,10 +227,10 @@ class PoolData: """ soil_n_pool_ammonium: NDArray[np.float32] - """Soil ammonium (:math:`\ce{NH4+}`) pool [kg N m^-3].""" + r"""Soil ammonium (:math:`\ce{NH4+}`) pool [kg N m^-3].""" soil_n_pool_nitrate: NDArray[np.float32] - """Soil nitrate (:math:`\ce{NO3-}`) pool [kg N m^-3].""" + r"""Soil nitrate (:math:`\ce{NO3-}`) pool [kg N m^-3].""" soil_p_pool_dop: NDArray[np.float32] """Organic phosphorus content of the low molecular weight carbon pool [kg P m^-3].