|
3 | 3 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger |
4 | 4 | # GitHub: https://github.com/btschwertfeger |
5 | 5 | # |
| 6 | +# pylint: disable=consider-using-f-string,logging-not-lazy |
6 | 7 |
|
7 | 8 | r""" |
8 | 9 | Module providing the a method named "adjust" to apply different bias |
|
24 | 25 | _{m} = long-term monthly interval |
25 | 26 | """ |
26 | 27 |
|
27 | | -from cmethods.core import adjust |
| 28 | +from __future__ import annotations |
| 29 | + |
| 30 | +import logging |
| 31 | +import sys |
28 | 32 |
|
29 | | -__author__ = "Benjamin Thomas Schwertfeger" |
30 | | -__copyright__ = __author__ |
31 | | -__email__ = "contact@b-schwertfeger.de" |
32 | | -__link__ = "https://github.com/btschwertfeger" |
33 | | -__github__ = "https://github.com/btschwertfeger/python-cmethods" |
| 33 | +import cloup |
| 34 | +import xarray as xr |
| 35 | +from cloup import ( |
| 36 | + HelpFormatter, |
| 37 | + HelpTheme, |
| 38 | + Path, |
| 39 | + Style, |
| 40 | + command, |
| 41 | + option, |
| 42 | + option_group, |
| 43 | + version_option, |
| 44 | +) |
| 45 | +from cloup.constraints import Equal, If, require_all |
| 46 | + |
| 47 | +from cmethods.core import adjust |
34 | 48 |
|
35 | 49 | __all__ = ["adjust"] |
| 50 | + |
| 51 | + |
| 52 | +@command( |
| 53 | + context_settings={ |
| 54 | + "auto_envvar_prefix": "CMETHODS", |
| 55 | + "help_option_names": ["-h", "--help"], |
| 56 | + }, |
| 57 | + formatter_settings=HelpFormatter.settings( |
| 58 | + theme=HelpTheme( |
| 59 | + invoked_command=Style(fg="bright_yellow"), |
| 60 | + heading=Style(fg="bright_white", bold=True), |
| 61 | + constraint=Style(fg="magenta"), |
| 62 | + col1=Style(fg="bright_yellow"), |
| 63 | + ), |
| 64 | + ), |
| 65 | +) |
| 66 | +@version_option(message="%version%") |
| 67 | +@option( |
| 68 | + "--obs", |
| 69 | + "--observations", |
| 70 | + required=True, |
| 71 | + type=Path(exists=True), |
| 72 | + help="Reference data set (control period)", |
| 73 | +) |
| 74 | +@option( |
| 75 | + "--simh", |
| 76 | + "--simulated-historical", |
| 77 | + required=True, |
| 78 | + type=Path(exists=True), |
| 79 | + help="Modeled data set (control period)", |
| 80 | +) |
| 81 | +@option( |
| 82 | + "--simp", |
| 83 | + "--simulated-scenario", |
| 84 | + required=True, |
| 85 | + type=Path(exists=True), |
| 86 | + help="Modeled data set (scenario period)", |
| 87 | +) |
| 88 | +@option( |
| 89 | + "--method", |
| 90 | + required=True, |
| 91 | + type=cloup.Choice( |
| 92 | + [ |
| 93 | + "linear_scaling", |
| 94 | + "variance_scaling", |
| 95 | + "delta_method", |
| 96 | + "quantile_mapping", |
| 97 | + "quantile_delta_mapping", |
| 98 | + ], |
| 99 | + case_sensitive=False, |
| 100 | + ), |
| 101 | + help="Bias adjustment method to apply", |
| 102 | +) |
| 103 | +@option( |
| 104 | + "--kind", |
| 105 | + required=True, |
| 106 | + type=cloup.Choice(["+", "add", "*", "mult"]), |
| 107 | + help="Kind of adjustment", |
| 108 | +) |
| 109 | +@option( |
| 110 | + "--variable", |
| 111 | + required=True, |
| 112 | + type=str, |
| 113 | + help="Variable of interest", |
| 114 | +) |
| 115 | +@option( |
| 116 | + "-o", |
| 117 | + "--output", |
| 118 | + required=True, |
| 119 | + type=str, |
| 120 | + callback=lambda _, __, value: (value if value.endswith(".nc") else f"{value}.nc"), |
| 121 | + help="Output file name", |
| 122 | +) |
| 123 | +@option_group( |
| 124 | + "Scaling-Based Adjustment Options", |
| 125 | + option( |
| 126 | + "--group", |
| 127 | + type=str, |
| 128 | + help="Temporal grouping", |
| 129 | + ), |
| 130 | + constraint=If( |
| 131 | + Equal("method", "linear_scaling") |
| 132 | + & Equal("method", "variance_scaling") |
| 133 | + & Equal("method", "delta_method"), |
| 134 | + then=require_all, |
| 135 | + ), |
| 136 | +) |
| 137 | +@option_group( |
| 138 | + "Distribution-Based Adjustment Options", |
| 139 | + option( |
| 140 | + "--quantiles", |
| 141 | + type=int, |
| 142 | + help="Quantiles to respect", |
| 143 | + ), |
| 144 | + constraint=If( |
| 145 | + Equal("method", "quantile_mapping") & Equal("method", "quantile_delta_mapping"), |
| 146 | + then=require_all, |
| 147 | + ), |
| 148 | +) |
| 149 | +def cli(**kwargs) -> None: |
| 150 | + """ |
| 151 | + Command-line tool to apply bias correction procedures to climate data. |
| 152 | +
|
| 153 | + Copyright (C) 2023 Benjamin Thomas Schwertfeger\n |
| 154 | + GitHub: https://github.com/btschwertfeger/python-cmethods |
| 155 | + """ |
| 156 | + |
| 157 | + logging.basicConfig( |
| 158 | + format="%(asctime)s %(levelname)8s | %(message)s", |
| 159 | + datefmt="%Y/%m/%d %H:%M:%S", |
| 160 | + level=logging.INFO, |
| 161 | + ) |
| 162 | + |
| 163 | + logging.info("Loading data sets ...") |
| 164 | + try: |
| 165 | + for key, message in zip( |
| 166 | + ("obs", "simh", "simp"), |
| 167 | + ( |
| 168 | + "observation data set", |
| 169 | + "modeled data set of the control period", |
| 170 | + "modeled data set of the scenario period", |
| 171 | + ), |
| 172 | + ): |
| 173 | + kwargs[key] = xr.open_dataset(kwargs[key]) |
| 174 | + if not isinstance(kwargs[key], xr.Dataset): |
| 175 | + raise TypeError("The data sets must be type xarray.Dataset") |
| 176 | + |
| 177 | + if kwargs["variable"] not in kwargs[key]: |
| 178 | + raise KeyError( |
| 179 | + f"Variable '{kwargs['variable']}' is missing in the {message}", |
| 180 | + ) |
| 181 | + kwargs[key] = kwargs[key][kwargs["variable"]] |
| 182 | + except (TypeError, KeyError) as exc: |
| 183 | + logging.error(exc) |
| 184 | + sys.exit(1) |
| 185 | + |
| 186 | + logging.info("Data sets loaded ...") |
| 187 | + kwargs["n_quantiles"] = kwargs["quantiles"] |
| 188 | + del kwargs["quantiles"] |
| 189 | + |
| 190 | + logging.info("Applying %s ..." % kwargs["method"]) |
| 191 | + result = adjust(**kwargs) |
| 192 | + |
| 193 | + logging.info("Saving result to %s ..." % kwargs["output"]) |
| 194 | + result.to_netcdf(kwargs["output"]) |
0 commit comments