diff --git a/ewatercycle/config/ewatercycle.yaml b/ewatercycle/config/ewatercycle.yaml index 42e7f92a..c54121f3 100644 --- a/ewatercycle/config/ewatercycle.yaml +++ b/ewatercycle/config/ewatercycle.yaml @@ -3,5 +3,3 @@ grdc_location: null container_engine: null singularity_dir: null output_dir: null -pcrglobwb.singularity_image: null -pcrglobwb.docker_image: null diff --git a/ewatercycle/forcing/_pcrglobwb.py b/ewatercycle/forcing/_pcrglobwb.py index 4801aecc..dd4e4aab 100644 --- a/ewatercycle/forcing/_pcrglobwb.py +++ b/ewatercycle/forcing/_pcrglobwb.py @@ -12,18 +12,19 @@ class PCRGlobWBForcing(DefaultForcing): """Container for pcrglobwb forcing data.""" + def __init__( self, start_time: str, end_time: str, directory: str, shape: Optional[str] = None, - precipitationNC: Optional[str] = 'precipitation.nc', - temperatureNC: Optional[str] = 'temperature.nc', + precipitationNC: Optional[str] = "precipitation.nc", + temperatureNC: Optional[str] = "temperature.nc", ): """ - precipitationNC (str): Input file for precipitation data. - temperatureNC (str): Input file for temperature data. + precipitationNC (str): Input file for precipitation data. + temperatureNC (str): Input file for temperature data. """ super().__init__(start_time, end_time, directory, shape) self.precipitationNC = precipitationNC @@ -37,59 +38,66 @@ def generate( # type: ignore end_time: str, shape: str, start_time_climatology: str, # TODO make optional, default to start_time - end_time_climatology: - str, # TODO make optional, defaults to start_time + 1 year + end_time_climatology: str, # TODO make optional, defaults to start_time + 1 year extract_region: dict = None, - ) -> 'PCRGlobWBForcing': + ) -> "PCRGlobWBForcing": """ - start_time_climatology (str): Start time for the climatology data - end_time_climatology (str): End time for the climatology data - extract_region (dict): Region specification, dictionary must - contain `start_longitude`, `end_longitude`, `start_latitude`, - `end_latitude` + start_time_climatology (str): Start time for the climatology data + end_time_climatology (str): End time for the climatology data + extract_region (dict): Region specification, dictionary must + contain `start_longitude`, `end_longitude`, `start_latitude`, + `end_latitude` """ # load the ESMValTool recipe recipe_name = "hydrology/recipe_pcrglobwb.yml" recipe = get_recipe(recipe_name) # model-specific updates to the recipe - preproc_names = ('crop_basin', 'preproc_pr', 'preproc_tas', - 'preproc_pr_clim', 'preproc_tas_clim') + preproc_names = ( + "crop_basin", + "preproc_pr", + "preproc_tas", + "preproc_pr_clim", + "preproc_tas_clim", + ) if dataset is not None: - recipe.data['diagnostics']['diagnostic_daily'][ - 'additional_datasets'] = [DATASETS[dataset]] + recipe.data["diagnostics"]["diagnostic_daily"][ + "additional_datasets" + ] = [DATASETS[dataset]] basin = Path(shape).stem - recipe.data['diagnostics']['diagnostic_daily']['scripts']['script'][ - 'basin'] = basin + recipe.data["diagnostics"]["diagnostic_daily"]["scripts"]["script"][ + "basin" + ] = basin if extract_region is None: extract_region = get_extents(shape) for preproc_name in preproc_names: - recipe.data['preprocessors'][preproc_name][ - 'extract_region'] = extract_region + recipe.data["preprocessors"][preproc_name][ + "extract_region" + ] = extract_region - variables = recipe.data['diagnostics']['diagnostic_daily']['variables'] - var_names = 'tas', 'pr' + variables = recipe.data["diagnostics"]["diagnostic_daily"]["variables"] + var_names = "tas", "pr" startyear = get_time(start_time).year for var_name in var_names: - variables[var_name]['start_year'] = startyear + variables[var_name]["start_year"] = startyear endyear = get_time(end_time).year for var_name in var_names: - variables[var_name]['end_year'] = endyear + variables[var_name]["end_year"] = endyear - var_names_climatology = 'pr_climatology', 'tas_climatology' + var_names_climatology = "pr_climatology", "tas_climatology" startyear_climatology = get_time(start_time_climatology) for var_name in var_names_climatology: - variables[var_name]['start_year'] = startyear_climatology + variables[var_name]["start_year"] = startyear_climatology endyear_climatology = get_time(end_time_climatology) for var_name in var_names_climatology: - variables[var_name]['end_year'] = endyear_climatology + variables[var_name]["end_year"] = endyear_climatology # generate forcing data and retrieve useful information recipe_output = recipe.run() @@ -97,11 +105,29 @@ def generate( # type: ignore directory, forcing_files = data_files_from_recipe_output(recipe_output) # instantiate forcing object based on generated data - return PCRGlobWBForcing(directory=directory, - start_time=start_time, - end_time=end_time, - precipitationNC=forcing_files['pr'], - temperatureNC=forcing_files['tas']) + return PCRGlobWBForcing( + directory=directory, + start_time=start_time, + end_time=end_time, + precipitationNC=forcing_files["pr"], + temperatureNC=forcing_files["tas"], + ) def plot(self): - raise NotImplementedError('Dont know how to plot') + raise NotImplementedError("Dont know how to plot") + + def __str__(self): + """Nice formatting of the forcing data object.""" + return "\n".join( + [ + "Forcing data for PCRGlobWB", + "--------------------------", + f"Directory: {self.directory}", + f"Start time: {self.start_time}", + f"End time: {self.end_time}", + f"Shapefile: {self.shape}", + f"Additional information for model config:", + f" - temperatureNC: {self.temperatureNC}", + f" - precipitationNC: {self.precipitationNC}", + ] + ) diff --git a/ewatercycle/models/pcrglobwb.py b/ewatercycle/models/pcrglobwb.py index 867da0bf..3f9a474e 100644 --- a/ewatercycle/models/pcrglobwb.py +++ b/ewatercycle/models/pcrglobwb.py @@ -1,7 +1,8 @@ import time +from dataclasses import dataclass from os import PathLike from pathlib import Path -from typing import Any, Iterable, Tuple +from typing import Any, Iterable, Tuple, Union import numpy as np import xarray as xr @@ -10,109 +11,226 @@ from grpc4bmi.bmi_client_singularity import BmiClientSingularity from ewatercycle import CFG +from ewatercycle.forcing._pcrglobwb import PCRGlobWBForcing from ewatercycle.models.abstract import AbstractModel from ewatercycle.parametersetdb.config import CaseConfigParser +from ewatercycle.util import get_time +from grpc import FutureTimeoutError + + +@dataclass +class PCRGlobWBParameterSet: + """Parameter set for the PCRGlobWB model class. + + A valid pcrglobwb parameter set consists of a folder with input data files + and should always include a default configuration file. + """ + + input_dir: Union[str, PathLike] + """Input folder path.""" + default_config: Union[str, PathLike] + """Path to (default) model configuration file consistent with `input_data`.""" + + def __setattr__(self, name: str, value: Union[str, PathLike]): + self.__dict__[name] = Path(value).expanduser().resolve() + + def __str__(self): + """Nice formatting of the parameterset object.""" + return "\n".join( + [ + "Wflow parameter set", + "-------------------", + f"Directory: {self.input_dir}", + f"Default configuration file: {self.default_config}", + ] + ) class PCRGlobWB(AbstractModel): """eWaterCycle implementation of PCRGlobWB hydrological model. - Attributes + Args: + + version: pick a version from :py:attr:`~available_versions` + parameter_set: instance of :py:class:`~PCRGlobWBParameterSet`. + forcing: ewatercycle forcing container; + see :py:mod:`ewatercycle.forcing`. + + Attributes: + bmi (Bmi): GRPC4BMI Basic Modeling Interface object """ - def setup( # type: ignore - self, - input_dir: PathLike, - cfg_file: PathLike, - additional_input_dirs: Iterable[PathLike] = [], - **kwargs) -> Tuple[PathLike, PathLike]: - """Start model inside container and return config file and work dir. - Args: - input_dir: main input directory. Relative paths in the cfg_file - should start from this directory. - cfg_file: path to a valid pcrglobwb configuration file, - typically somethig like `setup.ini`. - additional_input_dirs: one or more additional data directories - that the model will have access to. - **kwargs (optional, dict): any settings in the cfg_file that you - want to overwrite programmatically. Should be passed as a dict, - e.g. `meteoOptions = {"temperatureNC": "era5_tas_1990_2000.nc"}` - where meteoOptions is the section in which the temperatureNC option - may be found. - - Returns: - Path to config file and work dir - """ + available_versions = ("setters",) + + def __init__( + self, + version: str, + parameter_set: PCRGlobWBParameterSet, + forcing: PCRGlobWBForcing, + ): + super().__init__() + + self.version = version + self.parameter_set = parameter_set + self.forcing = forcing + + self._set_docker_image() + self._setup_work_dir() - self._setup_config(cfg_file, input_dir, **kwargs) - self._start_container(input_dir, additional_input_dirs) + self._setup_default_config() - return self.cfg_file, self.work_dir, + def _set_docker_image(self): + images = { + "setters": "ewatercycle/pcrg-grpc4bmi:setters", + } + self.docker_image = images[self.version] def _setup_work_dir(self): + # Must exist before setting up default config timestamp = time.strftime("%Y%m%d_%H%M%S") - work_dir = Path(CFG["output_dir"]) / f'pcrglobwb_{timestamp}' + work_dir = Path(CFG["output_dir"]) / f"pcrglobwb_{timestamp}" work_dir.mkdir() - self.work_dir = work_dir.resolve() - print(f"Created working directory: {work_dir}") + self.work_dir = work_dir.expanduser().resolve() + + def _setup_default_config(self): + config_file = self.parameter_set.default_config + input_dir = self.parameter_set.input_dir - def _setup_config(self, cfg_file: PathLike, input_dir: PathLike, **kwargs): cfg = CaseConfigParser() - cfg.read(cfg_file) - self.cfg = cfg + cfg.read(config_file) + cfg.set("globalOptions", "inputDir", str(input_dir)) + cfg.set("globalOptions", "outputDir", str(self.work_dir)) + cfg.set( + "globalOptions", + "startTime", + get_time(self.forcing.start_time).strftime("%Y-%m-%d"), + ) + cfg.set( + "globalOptions", + "endTime", + get_time(self.forcing.start_time).strftime("%Y-%m-%d"), + ) + cfg.set( + "meteoOptions", + "temperatureNC", + str( + (Path(self.forcing.directory) / self.forcing.temperatureNC) + .expanduser() + .resolve() + ), + ) + cfg.set( + "meteoOptions", + "precipitationNC", + str( + (Path(self.forcing.directory) / self.forcing.precipitationNC) + .expanduser() + .resolve() + ), + ) - full_input_path = Path(input_dir).resolve() - cfg.set('globalOptions', 'inputDir', str(full_input_path)) - cfg.set('globalOptions', 'outputDir', str(self.work_dir)) + self.config = cfg - for section, options in kwargs.items(): - for option, value in options.items(): - cfg.set(section, option, value) + def setup(self, **kwargs) -> Tuple[PathLike, PathLike]: # type: ignore + """Start model inside container and return config file and work dir. + Args: + **kwargs: Use :py:meth:`parameters` to see the current values + configurable options for this model, + + Returns: Path to config file and work dir + """ + self._update_config(**kwargs) + + cfg_file = self._export_config() + work_dir = self.work_dir + + try: + self._start_container() + except FutureTimeoutError: + # https://github.com/eWaterCycle/grpc4bmi/issues/95 + # https://github.com/eWaterCycle/grpc4bmi/issues/100 + raise ValueError( + "Couldn't spawn container within allocated time limit " + "(15 seconds). You may try pulling the docker image with" + f" `docker pull {self.docker_image}` or call `singularity " + f"exec docker://{self.docker_image} run-bmi-server -h`" + "if you're using singularity, and then try again." + ) + + return cfg_file, work_dir + + def _update_config(self, **kwargs): + cfg = self.config + + if "start_time" in kwargs: + cfg.set( + "globalOptions", + "startTime", + get_time(kwargs["start_time"]).strftime("%Y-%m-%d"), + ) + + if "end_time" in kwargs: + cfg.set( + "globalOptions", + "endTime", + get_time(kwargs["end_time"]).strftime("%Y-%m-%d"), + ) + + if "routing_method" in kwargs: + cfg.set( + "routingOptions", "routingMethod", kwargs["routing_method"] + ) + + if "dynamic_flood_plain" in kwargs: + cfg.set( + "routingOptions", + "dynamicFloodPlain", + kwargs["dynamic_flood_plain"], + ) + + if "max_spinups_in_years" in kwargs: + cfg.set( + "globalOptions", + "maxSpinUpsInYears", + str(kwargs["max_spinups_in_years"]), + ) + + def _export_config(self) -> PathLike: new_cfg_file = Path(self.work_dir) / "pcrglobwb_ewatercycle.ini" with new_cfg_file.open("w") as filename: - cfg.write(filename) + self.config.write(filename) - self.cfg_file = new_cfg_file.resolve() - print(f"Created config file {self.cfg_file} with inputDir " - f"{full_input_path} and outputDir {self.work_dir}.") + self.cfg_file = new_cfg_file.expanduser().resolve() + return self.cfg_file - def _start_container(self, - input_dir: PathLike, - additional_input_dirs: Iterable[PathLike] = []): - input_dirs = [input_dir] + list(additional_input_dirs) + def _start_container(self): + additional_input_dirs = [ + str(self.parameter_set.input_dir), + str(self.forcing.directory), + ] if CFG["container_engine"] == "docker": self.bmi = BmiClientDocker( - image=CFG["pcrglobwb.docker_image"], + image=self.docker_image, image_port=55555, work_dir=str(self.work_dir), - input_dirs=[str(input_dir) for input_dir in input_dirs], - timeout=10, + input_dirs=additional_input_dirs, + timeout=15, ) elif CFG["container_engine"] == "singularity": - image = CFG["pcrglobwb.singularity_image"] - - message = f"No singularity image found at {image}" - assert Path(image).exists(), message - self.bmi = BmiClientSingularity( - image=image, + image=f"docker://{self.docker_image}", work_dir=str(self.work_dir), - input_dirs=[str(input_path) for input_path in input_dirs], - timeout=10, + input_dirs=additional_input_dirs, + timeout=15, ) else: raise ValueError( f"Unknown container technology in CFG: {CFG['container_engine']}" ) - inputs = "\n".join([str(Path(p).resolve()) for p in input_dirs]) - print( - f"Started model container with working directory {self.work_dir} " - f"and access to the following input directories:\n{inputs}.") - def get_value_as_xarray(self, name: str) -> xr.DataArray: """Return the value as xarray object.""" # Get time information @@ -126,7 +244,7 @@ def get_value_as_xarray(self, name: str) -> xr.DataArray: coords={ "longitude": self.bmi.get_grid_y(grid), "latitude": self.bmi.get_grid_x(grid), - "time": num2date(self.bmi.get_current_time(), time_units) + "time": num2date(self.bmi.get_current_time(), time_units), }, dims=["latitude", "longitude"], name=name, @@ -138,11 +256,17 @@ def get_value_as_xarray(self, name: str) -> xr.DataArray: @property def parameters(self) -> Iterable[Tuple[str, Any]]: """List the configurable parameters for this model.""" - if not hasattr(self, "cfg"): - raise NotImplementedError( - "No default parameters available for pcrglobwb. To see the " - "parameters, first run setup with a valid .ini file.") - - return [(f"{section}.{option}", f"{self.cfg.get(section, option)}") - for section in self.cfg.sections() - for option in self.cfg.options(section)] + # An opiniated list of configurable parameters. + cfg = self.config + return [ + ( + "start_time", + f"{cfg.get('globalOptions', 'startTime')}T00:00:00Z", + ), + ("end_time", f"{cfg.get('globalOptions', 'endTime')}T00:00:00Z"), + ("routing_method", cfg.get("routingOptions", "routingMethod")), + ( + "max_spinups_in_years", + cfg.get("globalOptions", "maxSpinUpsInYears"), + ), + ] diff --git a/examples/pcrglobwb.ipynb b/examples/pcrglobwb.ipynb index 612d823b..7ea9e1e6 100644 --- a/examples/pcrglobwb.ipynb +++ b/examples/pcrglobwb.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "928c4579", + "id": "181daf63", "metadata": {}, "source": [ "# Running PCRGlobWB using the eWaterCycle system" @@ -10,20 +10,20 @@ }, { "cell_type": "markdown", - "id": "33da94f8", + "id": "1490c138", "metadata": { "tags": [] }, "source": [ "This notebook assumes you have a working model configuration available on your system. We will use the example data from https://github.com/UU-Hydro/PCR-GLOBWB_input_example and we will user `ewatercycle.parametersetdb` to copy it to our local directory.\n", "\n", - "The default ini file contains a number of paths. Under `globalOptions`, the `input_dir` and `output_dir` will be overwritten on setup. All other paths must be relative to the `input_dir`, or they can be specified as absolute paths. If absolute paths are used in the config file, these directories must be passed during setup using the `additional_input_dirs` argument." + "We will combine them into one `PCRGloBWBParameterSet` object, which we can then pass to the model when we first create it." ] }, { "cell_type": "code", "execution_count": 1, - "id": "0a90d720", + "id": "30450976", "metadata": {}, "outputs": [], "source": [ @@ -39,364 +39,184 @@ ] }, { - "cell_type": "markdown", - "id": "5fe6a14f", + "cell_type": "code", + "execution_count": 1, + "id": "becb0108", "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/peter/miniconda3/envs/ewatercycle/lib/python3.9/site-packages/esmvalcore/experimental/_warnings.py:18: UserWarning: \n", + " Thank you for trying out the new ESMValCore API.\n", + " Note that this API is experimental and may be subject to change.\n", + " More info: https://github.com/ESMValGroup/ESMValCore/issues/498\n", + "/home/peter/miniconda3/envs/ewatercycle/lib/python3.9/site-packages/esmvalcore/experimental/config/_config_validators.py:254: ESMValToolDeprecationWarning: `write_plots` will be removed in 2.4.0.\n", + "/home/peter/miniconda3/envs/ewatercycle/lib/python3.9/site-packages/esmvalcore/experimental/config/_config_validators.py:255: ESMValToolDeprecationWarning: `write_netcdf` will be removed in 2.4.0.\n" + ] + } + ], "source": [ - "This notebook uses the following docker and/or singularity images. Make sure these are present on the system:\n", - "\n", - "- Docker: `ewatercycle/pcrg-grpc4bmi:setters`\n", - "- Singularity: `ewatercycle-pcrg-grpc4bmi-setters.sif`\n" + "import ewatercycle\n", + "import ewatercycle.models\n", + "import ewatercycle.forcing" ] }, { "cell_type": "code", "execution_count": 2, - "id": "bb69a0c9", + "id": "b8825726", "metadata": {}, "outputs": [], "source": [ - "from ewatercycle import CFG\n", - "from ewatercycle.models import PCRGlobWB" + "ewatercycle.CFG['container_engine'] = 'singularity' # or 'singularity'\n", + "ewatercycle.CFG['singularity_dir'] = './'\n", + "ewatercycle.CFG['output_dir'] = './'" ] }, { "cell_type": "code", "execution_count": 3, - "id": "364e4a53", + "id": "8b863a7b", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Wflow parameter set\n", + "-------------------\n", + "Directory: /home/peter/ewatercycle/ewatercycle/examples/pcrglobwb_example_case\n", + "Default configuration file: /home/peter/ewatercycle/ewatercycle/examples/pcrglobwb_example_case/setup.ini\n" + ] + } + ], "source": [ - "# My default is set to docker. To test with singularity:\n", - "# CFG['container_engine'] = 'singularity'" + "parameterset = ewatercycle.models.pcrglobwb.PCRGlobWBParameterSet(\n", + " input_dir = './pcrglobwb_example_case', \n", + " default_config = './pcrglobwb_example_case/setup.ini'\n", + ")\n", + "print(parameterset)" ] }, { "cell_type": "code", "execution_count": 4, - "id": "5ac75efe", + "id": "f57a6f04", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Forcing data for PCRGlobWB\n", + "--------------------------\n", + "Directory: ./pcrglobwb_example_case/forcing\n", + "Start time: 2001-01-01T00:00:00Z\n", + "End time: 2010-12-31T00:00:00Z\n", + "Shapefile: None\n", + "Additional information for model config:\n", + " - temperatureNC: temperature_2001to2010.nc\n", + " - precipitationNC: precipitation_2001to2010.nc\n" + ] + } + ], "source": [ - "pcrglob = PCRGlobWB()" + "forcing = ewatercycle.forcing.load_foreign(\n", + " target_model = \"pcrglobwb\",\n", + " start_time = \"2001-01-01T00:00:00Z\",\n", + " end_time = \"2010-12-31T00:00:00Z\",\n", + " directory = \"./pcrglobwb_example_case/forcing\",\n", + " shape = None,\n", + " forcing_info = dict(\n", + " precipitationNC = \"precipitation_2001to2010.nc\",\n", + " temperatureNC = \"temperature_2001to2010.nc\"\n", + "))\n", + "print(forcing)" ] }, { "cell_type": "code", "execution_count": 5, - "id": "ee3cce7c", + "id": "ad624243", "metadata": {}, - "outputs": [ - { - "ename": "NotImplementedError", - "evalue": "No default parameters available for pcrglobwb. To see the parameters, first run setup with a valid .ini file.", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mNotImplementedError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mpcrglob\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mparameters\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m~/ewatercycle/ewatercycle/ewatercycle/models/pcrglobwb.py\u001b[0m in \u001b[0;36mparameters\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 143\u001b[0m \u001b[0;34m\"\"\"List the configurable parameters for this model.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 144\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mhasattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"cfg\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 145\u001b[0;31m raise NotImplementedError(\n\u001b[0m\u001b[1;32m 146\u001b[0m \u001b[0;34m\"No default parameters available for pcrglobwb. To see the \"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 147\u001b[0m \"parameters, first run setup with a valid .ini file.\")\n", - "\u001b[0;31mNotImplementedError\u001b[0m: No default parameters available for pcrglobwb. To see the parameters, first run setup with a valid .ini file." - ] - } - ], + "outputs": [], "source": [ - "pcrglob.parameters" + "pcrglob = ewatercycle.models.PCRGlobWB(version=\"setters\", parameter_set=parameterset, forcing=forcing)" ] }, { "cell_type": "code", "execution_count": 6, - "id": "9351f9a2", + "id": "e5d681b5", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "\u001b[0;31mSignature:\u001b[0m\n", - "\u001b[0mpcrglob\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msetup\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0minput_dir\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mos\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mPathLike\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mcfg_file\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mos\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mPathLike\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0madditional_input_dirs\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mIterable\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mos\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mPathLike\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0mTuple\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mos\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mPathLike\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mos\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mPathLike\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mDocstring:\u001b[0m\n", - "Start model inside container and return config file and work dir.\n", - "\n", - "Args:\n", - "\n", - " - input_dir: main input directory. Relative paths in the cfg_file\n", - " should start from this directory.\n", - "\n", - " - cfg_file: path to a valid pcrglobwb configuration file,\n", - " typically somethig like `setup.ini`.\n", - "\n", - " - additional_input_dirs: one or more additional data directories\n", - " that the model will have access to.\n", - "\n", - " - **kwargs (optional, dict): any settings in the cfg_file that you\n", - " want to overwrite programmatically. Should be passed as a dict,\n", - " e.g. `meteoOptions = {\"temperatureNC\": \"era5_tas_1990_2000.nc\"}`\n", - " where meteoOptions is the section in which the temperatureNC option\n", - " may be found.\n", - "\n", - "Returns: Path to config file and work dir\n", - "\u001b[0;31mFile:\u001b[0m ~/ewatercycle/ewatercycle/ewatercycle/models/pcrglobwb.py\n", - "\u001b[0;31mType:\u001b[0m method\n" + "[('start_time', '2001-01-01T00:00:00Z'),\n", + " ('end_time', '2001-01-01T00:00:00Z'),\n", + " ('routing_method', 'accuTravelTime'),\n", + " ('max_spinups_in_years', '20')]" ] }, + "execution_count": 6, "metadata": {}, - "output_type": "display_data" + "output_type": "execute_result" } ], "source": [ - "?pcrglob.setup" + "pcrglob.parameters" ] }, { "cell_type": "code", - "execution_count": 8, - "id": "7a4a71a7", + "execution_count": 7, + "id": "328e0ef2", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Created working directory: /home/peter/ewatercycle/ewatercycle/examples/pcrglobwb_20210419_120714\n", - "Created config file /home/peter/ewatercycle/ewatercycle/examples/pcrglobwb_20210419_120714/pcrglobwb_ewatercycle.ini with inputDir /home/peter/ewatercycle/ewatercycle/examples/pcrglobwb_example_case and outputDir /home/peter/ewatercycle/ewatercycle/examples/pcrglobwb_20210419_120714.\n", - "Started model container with working directory /home/peter/ewatercycle/ewatercycle/examples/pcrglobwb_20210419_120714 and access to the following input directories:\n", - "/home/peter/ewatercycle/ewatercycle/examples/pcrglobwb_example_case.\n" + "Running docker://ewatercycle/pcrg-grpc4bmi:setters singularity container on port 59387\n" ] }, { "data": { "text/plain": [ - "(PosixPath('/home/peter/ewatercycle/ewatercycle/examples/pcrglobwb_20210419_120714/pcrglobwb_ewatercycle.ini'),\n", - " PosixPath('/home/peter/ewatercycle/ewatercycle/examples/pcrglobwb_20210419_120714'))" + "(PosixPath('/home/peter/ewatercycle/ewatercycle/examples/pcrglobwb_20210618_160443/pcrglobwb_ewatercycle.ini'),\n", + " PosixPath('/home/peter/ewatercycle/ewatercycle/examples/pcrglobwb_20210618_160443'))" ] }, - "execution_count": 8, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "cfg_file, cfg_dir = pcrglob.setup(input_dir=\"pcrglobwb_example_case/\", cfg_file=\"pcrglobwb_example_case/setup.ini\")\n", + "# We can modify all settings above during the setup. Here's how to use a different forcing file\n", + "cfg_file, cfg_dir = pcrglob.setup(end_time='2001-02-28T00:00:00Z', max_spinups_in_years=5)\n", "cfg_file, cfg_dir" ] }, { "cell_type": "code", - "execution_count": 9, - "id": "efd5af6e", + "execution_count": 8, + "id": "1f0a2404", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "[('globalOptions.inputDir',\n", - " '/home/peter/ewatercycle/ewatercycle/examples/pcrglobwb_example_case'),\n", - " ('globalOptions.cloneMap', 'cloneMaps/RhineMeuse30min.map'),\n", - " ('globalOptions.landmask', 'None'),\n", - " ('globalOptions.outputDir',\n", - " '/home/peter/ewatercycle/ewatercycle/examples/pcrglobwb_20210419_120714'),\n", - " ('globalOptions.spinUpOutputDir', 'True'),\n", - " ('globalOptions.institution',\n", - " 'Department of Physical Geography, Utrecht University'),\n", - " ('globalOptions.title', 'PCR-GLOBWB output'),\n", - " ('globalOptions.description', 'test version (by Edwin H. Sutanudjaja)'),\n", - " ('globalOptions.startTime', '2001-01-01'),\n", - " ('globalOptions.endTime', '2010-12-31'),\n", - " ('globalOptions.maxSpinUpsInYears', '20'),\n", - " ('globalOptions.minConvForSoilSto', '0.5'),\n", - " ('globalOptions.minConvForGwatSto', '0.5'),\n", - " ('globalOptions.minConvForChanSto', '0.5'),\n", - " ('globalOptions.minConvForTotlSto', '0.5'),\n", - " ('meteoOptions.precipitationNC', 'forcing/precipitation_2001to2010.nc'),\n", - " ('meteoOptions.temperatureNC', 'forcing/temperature_2001to2010.nc'),\n", - " ('meteoOptions.referenceETPotMethod', 'Hamon'),\n", - " ('meteoOptions.refETPotFileNC',\n", - " 'forcing/CRU-TS3.21/merged_1958_to_2010/CRU-TS3.21_ERA-40_ERA-Interim_daily_referencePotET_1958_to_2010.nc'),\n", - " ('landSurfaceOptions.debugWaterBalance', 'True'),\n", - " ('landSurfaceOptions.numberOfUpperSoilLayers', '2'),\n", - " ('landSurfaceOptions.topographyNC',\n", - " 'landSurface/topoPropertiesRhineMeuse30min.nc'),\n", - " ('landSurfaceOptions.soilPropertiesNC',\n", - " 'landSurface/soilPropertiesRhineMeuse30min.nc'),\n", - " ('landSurfaceOptions.includeIrrigation', 'False'),\n", - " ('landSurfaceOptions.includeDomesticWaterDemand', 'False'),\n", - " ('landSurfaceOptions.includeIndustryWaterDemand', 'False'),\n", - " ('landSurfaceOptions.includeLivestockWaterDemand', 'False'),\n", - " ('forestOptions.name', 'forest'),\n", - " ('forestOptions.debugWaterBalance', 'True'),\n", - " ('forestOptions.snowModuleType', 'Simple'),\n", - " ('forestOptions.freezingT', '0.0'),\n", - " ('forestOptions.degreeDayFactor', '0.0025'),\n", - " ('forestOptions.snowWaterHoldingCap', '0.1'),\n", - " ('forestOptions.refreezingCoeff', '0.05'),\n", - " ('forestOptions.minTopWaterLayer', '0.0'),\n", - " ('forestOptions.minCropKC', '0.2'),\n", - " ('forestOptions.minInterceptCap', '0.0002'),\n", - " ('forestOptions.landCoverMapsNC',\n", - " 'landCover/forest/forestPropertiesRhineMeuse.nc'),\n", - " ('forestOptions.arnoBeta', 'None'),\n", - " ('forestOptions.cropCoefficientNC',\n", - " 'landCover/forest/cropKC_forest_daily366.nc'),\n", - " ('forestOptions.interceptCapNC',\n", - " 'landCover/forest/interceptCap_forest_daily366.nc'),\n", - " ('forestOptions.coverFractionNC',\n", - " 'landCover/forest/coverFraction_forest_daily366.nc'),\n", - " ('forestOptions.interceptStorIni', '0.0'),\n", - " ('forestOptions.snowCoverSWEIni', '0.0'),\n", - " ('forestOptions.snowFreeWaterIni', '0.0'),\n", - " ('forestOptions.topWaterLayerIni', '0.0'),\n", - " ('forestOptions.storUppIni', '0.0'),\n", - " ('forestOptions.storLowIni', '0.0'),\n", - " ('forestOptions.interflowIni', '0.0'),\n", - " ('grasslandOptions.name', 'grassland'),\n", - " ('grasslandOptions.debugWaterBalance', 'True'),\n", - " ('grasslandOptions.snowModuleType', 'Simple'),\n", - " ('grasslandOptions.freezingT', '0.0'),\n", - " ('grasslandOptions.degreeDayFactor', '0.0025'),\n", - " ('grasslandOptions.snowWaterHoldingCap', '0.1'),\n", - " ('grasslandOptions.refreezingCoeff', '0.05'),\n", - " ('grasslandOptions.minTopWaterLayer', '0.0'),\n", - " ('grasslandOptions.minCropKC', '0.2'),\n", - " ('grasslandOptions.minInterceptCap', '0.0002'),\n", - " ('grasslandOptions.landCoverMapsNC',\n", - " 'landCover/grassland/grasslandPropertiesRhineMeuse.nc'),\n", - " ('grasslandOptions.arnoBeta', 'None'),\n", - " ('grasslandOptions.cropCoefficientNC',\n", - " 'landCover/grassland/cropKC_grassland_daily366.nc'),\n", - " ('grasslandOptions.interceptCapNC',\n", - " 'landCover/grassland/interceptCap_grassland_daily366.nc'),\n", - " ('grasslandOptions.coverFractionNC',\n", - " 'landCover/grassland/coverFraction_grassland_daily366.nc'),\n", - " ('grasslandOptions.interceptStorIni', '0.0'),\n", - " ('grasslandOptions.snowCoverSWEIni', '0.0'),\n", - " ('grasslandOptions.snowFreeWaterIni', '0.0'),\n", - " ('grasslandOptions.topWaterLayerIni', '0.0'),\n", - " ('grasslandOptions.storUppIni', '0.0'),\n", - " ('grasslandOptions.storLowIni', '0.0'),\n", - " ('grasslandOptions.interflowIni', '0.0'),\n", - " ('irrPaddyOptions.name', 'irrPaddy'),\n", - " ('irrPaddyOptions.debugWaterBalance', 'True'),\n", - " ('irrPaddyOptions.snowModuleType', 'Simple'),\n", - " ('irrPaddyOptions.freezingT', '0.0'),\n", - " ('irrPaddyOptions.degreeDayFactor', '0.0025'),\n", - " ('irrPaddyOptions.snowWaterHoldingCap', '0.1'),\n", - " ('irrPaddyOptions.refreezingCoeff', '0.05'),\n", - " ('irrPaddyOptions.usingSpecificSoilTopo', 'True'),\n", - " ('irrPaddyOptions.topographyNC',\n", - " 'dfguu/data/hydroworld/PCRGLOBWB20/input30min/landSurface/topo/topoProperties.nc'),\n", - " ('irrPaddyOptions.soilPropertiesNC',\n", - " 'dfguu/data/hydroworld/PCRGLOBWB20/input30min/landSurface/soil/soilProperties.nc'),\n", - " ('irrPaddyOptions.landCoverMapsNC',\n", - " 'PCRGLOBWB20/input30min/landCover/irrPaddy/paddyProperties.nc'),\n", - " ('irrPaddyOptions.arnoBeta', 'None'),\n", - " ('irrPaddyOptions.minTopWaterLayer', '0.05'),\n", - " ('irrPaddyOptions.minCropKC', '0.2'),\n", - " ('irrPaddyOptions.minInterceptCap', '0.0002'),\n", - " ('irrPaddyOptions.cropDeplFactor', '0.2'),\n", - " ('irrPaddyOptions.cropCoefficientNC',\n", - " 'PCRGLOBWB20/input30min/landCover/irrPaddy/Global_CropCoefficientKc-IrrPaddy_30min.nc'),\n", - " ('irrPaddyOptions.interceptStorIni',\n", - " '/scratch/edwin/30min_27_april_2015/non-natural/states/interceptStor_irrPaddy_1958-12-31.map'),\n", - " ('irrPaddyOptions.snowCoverSWEIni',\n", - " '/scratch/edwin/30min_27_april_2015/non-natural/states/snowCoverSWE_irrPaddy_1958-12-31.map'),\n", - " ('irrPaddyOptions.snowFreeWaterIni',\n", - " '/scratch/edwin/30min_27_april_2015/non-natural/states/snowFreeWater_irrPaddy_1958-12-31.map'),\n", - " ('irrPaddyOptions.topWaterLayerIni',\n", - " '/scratch/edwin/30min_27_april_2015/non-natural/states/topWaterLayer_irrPaddy_1958-12-31.map'),\n", - " ('irrPaddyOptions.storUppIni',\n", - " '/scratch/edwin/30min_27_april_2015/non-natural/states/storUpp_irrPaddy_1958-12-31.map'),\n", - " ('irrPaddyOptions.storLowIni',\n", - " '/scratch/edwin/30min_27_april_2015/non-natural/states/storLow_irrPaddy_1958-12-31.map'),\n", - " ('irrPaddyOptions.interflowIni',\n", - " '/scratch/edwin/30min_27_april_2015/non-natural/states/interflow_irrPaddy_1958-12-31.map'),\n", - " ('irrNonPaddyOptions.name', 'irrNonPaddy'),\n", - " ('irrNonPaddyOptions.debugWaterBalance', 'True'),\n", - " ('irrNonPaddyOptions.snowModuleType', 'Simple'),\n", - " ('irrNonPaddyOptions.freezingT', '0.0'),\n", - " ('irrNonPaddyOptions.degreeDayFactor', '0.0025'),\n", - " ('irrNonPaddyOptions.snowWaterHoldingCap', '0.1'),\n", - " ('irrNonPaddyOptions.refreezingCoeff', '0.05'),\n", - " ('irrNonPaddyOptions.usingSpecificSoilTopo', 'True'),\n", - " ('irrNonPaddyOptions.topographyNC',\n", - " 'dfguu/data/hydroworld/PCRGLOBWB20/input30min/landSurface/topo/topoProperties.nc'),\n", - " ('irrNonPaddyOptions.soilPropertiesNC',\n", - " 'dfguu/data/hydroworld/PCRGLOBWB20/input30min/landSurface/soil/soilProperties.nc'),\n", - " ('irrNonPaddyOptions.landCoverMapsNC',\n", - " 'PCRGLOBWB20/input30min/landCover/irrNonPaddy/nonPaddyProperties.nc'),\n", - " ('irrNonPaddyOptions.arnoBeta', 'None'),\n", - " ('irrNonPaddyOptions.minTopWaterLayer', '0.0'),\n", - " ('irrNonPaddyOptions.minCropKC', '0.2'),\n", - " ('irrNonPaddyOptions.minInterceptCap', '0.0002'),\n", - " ('irrNonPaddyOptions.cropDeplFactor', '0.5'),\n", - " ('irrNonPaddyOptions.cropCoefficientNC',\n", - " 'PCRGLOBWB20/input30min/landCover/irrNonPaddy/Global_CropCoefficientKc-IrrNonPaddy_30min.nc'),\n", - " ('irrNonPaddyOptions.interceptStorIni',\n", - " '/scratch/edwin/30min_27_april_2015/non-natural/states/interceptStor_irrNonPaddy_1958-12-31.map'),\n", - " ('irrNonPaddyOptions.snowCoverSWEIni',\n", - " '/scratch/edwin/30min_27_april_2015/non-natural/states/snowCoverSWE_irrNonPaddy_1958-12-31.map'),\n", - " ('irrNonPaddyOptions.snowFreeWaterIni',\n", - " '/scratch/edwin/30min_27_april_2015/non-natural/states/snowFreeWater_irrNonPaddy_1958-12-31.map'),\n", - " ('irrNonPaddyOptions.topWaterLayerIni',\n", - " '/scratch/edwin/30min_27_april_2015/non-natural/states/topWaterLayer_irrNonPaddy_1958-12-31.map'),\n", - " ('irrNonPaddyOptions.storUppIni',\n", - " '/scratch/edwin/30min_27_april_2015/non-natural/states/storUpp_irrNonPaddy_1958-12-31.map'),\n", - " ('irrNonPaddyOptions.storLowIni',\n", - " '/scratch/edwin/30min_27_april_2015/non-natural/states/storLow_irrNonPaddy_1958-12-31.map'),\n", - " ('irrNonPaddyOptions.interflowIni',\n", - " '/scratch/edwin/30min_27_april_2015/non-natural/states/interflow_irrNonPaddy_1958-12-31.map'),\n", - " ('groundwaterOptions.debugWaterBalance', 'True'),\n", - " ('groundwaterOptions.groundwaterPropertiesNC',\n", - " 'groundwater/groundwaterProperties.nc'),\n", - " ('groundwaterOptions.storGroundwaterIni',\n", - " 'initialConditions/storGroundwaterIni_2001-12-31.map'),\n", - " ('routingOptions.debugWaterBalance', 'True'),\n", - " ('routingOptions.lddMap', 'routing/RhineMeuse_lddsound_30min.map'),\n", - " ('routingOptions.cellAreaMap', 'routing/RhineMeuse_cellarea30min.map'),\n", - " ('routingOptions.gradient',\n", - " 'routing/RhineMeuse_ChannelGradient-RVB_30min.map'),\n", - " ('routingOptions.manningsN', '0.04'),\n", - " ('routingOptions.routingMethod', 'accuTravelTime'),\n", - " ('routingOptions.waterBodyInputNC',\n", - " 'routing/waterBodiesRhineMeuse2001To2010.nc'),\n", - " ('routingOptions.onlyNaturalWaterBodies', 'True'),\n", - " ('routingOptions.cropCoefficientWaterNC',\n", - " 'routing/cropCoefficientForOpenWaterRhineMeuse.nc'),\n", - " ('routingOptions.minCropWaterKC', '0.20'),\n", - " ('routingOptions.timestepsToAvgDischargeIni', '0.0'),\n", - " ('routingOptions.waterBodyStorageIni', '0.0'),\n", - " ('routingOptions.channelStorageIni', '0.0'),\n", - " ('routingOptions.readAvlChannelStorageIni', '0.0'),\n", - " ('routingOptions.avgDischargeLongIni', '0.0'),\n", - " ('routingOptions.avgDischargeShortIni', '0.0'),\n", - " ('routingOptions.m2tDischargeLongIni', '0.0'),\n", - " ('routingOptions.avgBaseflowLongIni', '0.0'),\n", - " ('routingOptions.riverbedExchangeIni', '0.0'),\n", - " ('routingOptions.avgLakeReservoirInflowShortIni', '0.0'),\n", - " ('routingOptions.avgLakeReservoirOutflowLongIni', '0.0'),\n", - " ('reportingOptions.outDailyTotNC', 'discharge'),\n", - " ('reportingOptions.outMonthTotNC',\n", - " 'gwRecharge,totalGroundwaterAbstraction,groundwaterAbsReturnFlow,totalRunoff,baseflow,totalEvaporation,desalinationAbstraction,surfaceWaterAbstraction,nonFossilGroundwaterAbstraction,fossilGroundwaterAbstraction,irrGrossDemand,nonIrrGrossDemand,totalGrossDemand,nonIrrWaterConsumption,nonIrrReturnFlow,runoff,actualET'),\n", - " ('reportingOptions.outMonthAvgNC',\n", - " 'accuBaseflow,discharge,gwRecharge,totalGroundwaterAbstraction,groundwaterAbsReturnFlow,totalRunoff,snowCoverSWE,fractionTotalEvaporation,fracSurfaceWaterAllocation,storGroundwater,storGroundwaterFossil,totalWaterStorageThickness,satDegUpp,satDegLow,interceptStor,snowFreeWater,snowCoverSWE,topWaterLayer,storUppTotal,storLowTotal'),\n", - " ('reportingOptions.outMonthEndNC',\n", - " 'storGroundwater,storGroundwaterFossil,totalActiveStorageThickness,totalWaterStorageThickness'),\n", - " ('reportingOptions.outAnnuaTotNC',\n", - " 'gwRecharge,totalRunoff,baseflow,totalEvaporation,desalinationAbstraction,surfaceWaterAbstraction,nonFossilGroundwaterAbstraction,fossilGroundwaterAbstraction,totalGroundwaterAbstraction,totalAbstraction,irrGrossDemand,nonIrrGrossDemand,totalGrossDemand,nonIrrWaterConsumption,nonIrrReturnFlow,runoff,actualET'),\n", - " ('reportingOptions.outAnnuaAvgNC',\n", - " 'accuBaseflow,storGroundwater,storGroundwaterFossil,storGroundwaterTotal,totalActiveStorageThickness,totalWaterStorageThickness,discharge,fractionTotalEvaporation'),\n", - " ('reportingOptions.outAnnuaEndNC',\n", - " 'storGroundwater,storGroundwaterFossil,storGroundwaterTotal,totalActiveStorageThickness,totalWaterStorageThickness'),\n", - " ('reportingOptions.formatNetCDF', 'NETCDF4'),\n", - " ('reportingOptions.zlib', 'True')]" + "[('start_time', '2001-01-01T00:00:00Z'),\n", + " ('end_time', '2001-02-28T00:00:00Z'),\n", + " ('routing_method', 'accuTravelTime'),\n", + " ('max_spinups_in_years', '5')]" ] }, - "execution_count": 9, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -407,8 +227,8 @@ }, { "cell_type": "code", - "execution_count": 10, - "id": "7e72ac14", + "execution_count": 9, + "id": "d468b126", "metadata": {}, "outputs": [], "source": [ @@ -417,8 +237,8 @@ }, { "cell_type": "code", - "execution_count": 11, - "id": "ba81a736", + "execution_count": 10, + "id": "241c5817", "metadata": {}, "outputs": [ { @@ -523,7 +343,7 @@ " 'surface_water_abstraction_volume')" ] }, - "execution_count": 11, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -534,8 +354,8 @@ }, { "cell_type": "code", - "execution_count": 12, - "id": "516f42a6", + "execution_count": 11, + "id": "709a9b83", "metadata": {}, "outputs": [ { @@ -560,7 +380,7 @@ " 0., nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan])" ] }, - "execution_count": 12, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } @@ -571,8 +391,8 @@ }, { "cell_type": "code", - "execution_count": 13, - "id": "5ae98537", + "execution_count": 12, + "id": "329b7d77", "metadata": {}, "outputs": [ { @@ -961,7 +781,7 @@ " * latitude (latitude) float64 46.25 46.75 47.25 47.75 ... 51.25 51.75 52.25\n", " time object 2000-12-31 00:00:00\n", "Attributes:\n", - " units: m3.s-1
    • longitude
      (longitude)
      float64
      3.75 4.25 4.75 ... 11.25 11.75
      array([ 3.75,  4.25,  4.75,  5.25,  5.75,  6.25,  6.75,  7.25,  7.75,  8.25,\n",
      +       "        8.75,  9.25,  9.75, 10.25, 10.75, 11.25, 11.75])
    • latitude
      (latitude)
      float64
      46.25 46.75 47.25 ... 51.75 52.25
      array([46.25, 46.75, 47.25, 47.75, 48.25, 48.75, 49.25, 49.75, 50.25, 50.75,\n",
      +       "       51.25, 51.75, 52.25])
    • time
      ()
      object
      2000-12-31 00:00:00
      array(cftime.DatetimeGregorian(2000, 12, 31, 0, 0, 0, 0), dtype=object)
  • units :
    m3.s-1
  • " ], "text/plain": [ "\n", @@ -1026,7 +846,7 @@ " units: m3.s-1" ] }, - "execution_count": 13, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } @@ -1037,8 +857,8 @@ }, { "cell_type": "code", - "execution_count": 14, - "id": "f312e771", + "execution_count": 13, + "id": "cbb4a75d", "metadata": {}, "outputs": [ { @@ -1068,17 +888,17 @@ }, { "cell_type": "code", - "execution_count": 15, - "id": "b71188aa", + "execution_count": 14, + "id": "d9b57cf2", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 15, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" }, @@ -1100,10 +920,21 @@ "da.plot(robust=True, cmap='GnBu')" ] }, + { + "cell_type": "code", + "execution_count": 16, + "id": "a835ef4f", + "metadata": {}, + "outputs": [], + "source": [ + "pcrglob.finalize()\n", + "del pcrglob" + ] + }, { "cell_type": "code", "execution_count": null, - "id": "503002fa", + "id": "97fc0864", "metadata": {}, "outputs": [], "source": []