From 64b2984d16805c21ae3c814a53aee5abb503ea29 Mon Sep 17 00:00:00 2001 From: Rufai Omowunmi Balogun <rbalogun@eurac.edu> Date: Wed, 4 Oct 2023 12:46:52 +0200 Subject: [PATCH] rebase merge request --- ...st_climatology.cpython-38-pytest-7.1.2.pyc | Bin 0 -> 5507 bytes setup.cfg | 4 +- smodex/__init__.py | 8 + smodex/sm_downloader.py | 190 ++++++++++++++++++ tests/test_climatology.py | 3 + 5 files changed, 203 insertions(+), 2 deletions(-) create mode 100644 __pycache__/test_climatology.cpython-38-pytest-7.1.2.pyc create mode 100644 smodex/__init__.py create mode 100644 smodex/sm_downloader.py diff --git a/__pycache__/test_climatology.cpython-38-pytest-7.1.2.pyc b/__pycache__/test_climatology.cpython-38-pytest-7.1.2.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8ad705a75283e8926a9f14e25983d119a6d83e7d GIT binary patch literal 5507 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 literal 0 HcmV?d00001 diff --git a/setup.cfg b/setup.cfg index ac6b862..646d788 100644 --- a/setup.cfg +++ b/setup.cfg @@ -30,7 +30,7 @@ classifiers = [options] include_package_data = True package_dir = - = src + = smodex packages = find: python_requires = >=3.7 install_requires = @@ -49,7 +49,7 @@ docs = # smodex = smodex.__main__:main [options.packages.find] -where = src +where = smodex [options.package_data] * = *.yaml, *.ini diff --git a/smodex/__init__.py b/smodex/__init__.py new file mode 100644 index 0000000..45b4eb8 --- /dev/null +++ b/smodex/__init__.py @@ -0,0 +1,8 @@ +from smodex import sm_anomaly +from smodex import sm_climatology +from smodex import sm_downloader +from smodex import version +from smodex import visual_sma_ts + + +__all__ = ["sm_anomaly", "sm_climatology", "sm_downloader", "visual_sma_ts", "version"] diff --git a/smodex/sm_downloader.py b/smodex/sm_downloader.py new file mode 100644 index 0000000..139b100 --- /dev/null +++ b/smodex/sm_downloader.py @@ -0,0 +1,190 @@ +""" + Soil Moisture Downloader: Configured to download datasets from the Climate Data Store + Downloads hourly soil moisture datasets for full year +""" +import argparse +import json +import logging +import os +import sys + +import numpy as np +import pandas as pd + +import cdsapi + + +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) +formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s") +stream_handler = logging.StreamHandler(sys.stdout) +stream_handler.setFormatter(formatter) +logger.addHandler(stream_handler) + + +c = cdsapi.Client() + + +class SMDownload: + """Soil Moisture Downloader""" + + def __init__( + self, + start_date: str, + end_date: str, + api: str, + area: tuple = (50.775, 2.775, 42.275, 18.025), + depth: tuple = (1, 2, 3, 4), + download_path: str = "./sm_downloaded/", + ) -> None: + self.start_date = start_date + self.end_date = end_date + self.api = api + self.download_path = download_path + self.area = area + self.depth = depth + + def era5_sm_downloader(self, year): + """downloader from ERA5 API""" + + variables = [] + for dep in self.depth: + variables.append(f"volumetric_soil_water_layer_{dep}") + + c.retrieve( + "reanalysis-era5-single-levels", + { + "product_type": "reanalysis", + "variable": variables, + "year": int(year), + "month": [ + "01", + "02", + "03", + "04", + "05", + "06", + "07", + "08", + "09", + "10", + "11", + "12", + ], + "day": [ + "01", + "02", + "03", + "04", + "05", + "06", + "07", + "08", + "09", + "10", + "11", + "12", + "13", + "14", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + ], + "time": [ + "00:00", + "06:00", + "12:00", + "18:00", + ], + # 'time': [ + # '00:00', '01:00', '02:00', + # '03:00', '04:00', '05:00', + # '06:00', '07:00', '08:00', + # '09:00', '10:00', '11:00', + # '12:00', '13:00', '14:00', + # '15:00', '16:00', '17:00', + # '18:00', '19:00', '20:00', + # '21:00', '22:00', '23:00', + # ], + "area": self.area, + "format": "netcdf", + }, + self.download_path + f"ERA5_SM_{year}.nc", + ) + + def downloader(self): + """download""" + # TODO: Extend to other APIs. + + date_ranges = pd.date_range(start=self.start_date, end=self.end_date).year + years = np.unique(date_ranges) + + for yr in years: + if self.api == "era5": + logger.info(f"Initiating downloading of ERA5 Soil Moisture for {yr}") + if not os.path.exists(self.download_path): + os.makedirs(self.download_path) + self.era5_sm_downloader(year=yr) + logger.info(f"Downloaded ERA5 Soil Moisture for {yr}") + + +if __name__ == "__main__": + # command line option + parser = argparse.ArgumentParser( + description="Downloads soil moisture \ + datasets from start date to end date" + ) + parser.add_argument( + "start_date", + type=str, + help="initial date to start \ + downloading from e.g. 1990-01-01", + ) + parser.add_argument( + "end_date", + type=str, + help="end date to stop \ + downloading datasets from e.g. 2030-12-31", + ) + parser.add_argument("api", type=str, help="download portal API e.g. era5, lpdaac, etc.") + parser.add_argument( + "-a", + "--area", + type=json.loads, + help="bounding box area for downloading \ + datasets e.g. [50.775, 2.775, 42.275, 18.025]", + ) + parser.add_argument( + "-d", + "--depth", + type=json.loads, + help="volumetric \ + soil moisture depths e.g. [1, 2, 3, 4]", + ) + parser.add_argument( + "path", + type=str, + help="directory to save the \ + downloaded datasets e.g. /sm_downloaded/", + ) + + args = parser.parse_args() + + SMDownload = SMDownload( # type: ignore [misc, assignment] + args.start_date, args.end_date, args.api, args.area, args.depth, args.path + ) + SMDownload.downloader() # type: ignore [call-arg] diff --git a/tests/test_climatology.py b/tests/test_climatology.py index 3f07c47..1ee7efb 100644 --- a/tests/test_climatology.py +++ b/tests/test_climatology.py @@ -152,6 +152,9 @@ def test_get_climatology_stack(): # captured = capsys.readouterr() # assert "Soil Moisture Stack created" in captured.out # assert "Climatology computation complete" in captured.out +<<<<<<< HEAD if __name__ == "__main__": pytest.main() +======= +>>>>>>> e26b415 (rebase merge request) -- GitLab