cavapy 2.1.3__tar.gz → 2.1.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cavapy
3
- Version: 2.1.3
3
+ Version: 2.1.4
4
4
  Summary: CAVA Python package. Retrive climate data.
5
5
  License: Apache-2.0
6
6
  License-File: LICENSE
@@ -77,6 +77,12 @@ A single `get_climate_data()` call orchestrates a full pipeline:
77
77
  | **Bias correction** | ERA5 is automatically fetched as the reference; EQM is trained and applied — no external tools needed |
78
78
  | **Domain validation** | If your bounding box falls outside the chosen CORDEX domain, a corrected domain is suggested |
79
79
 
80
+ Unit conversion is enabled by default for backward compatibility. Pass
81
+ `convert_units=False` to keep the values and `units` metadata provided by the
82
+ source (for example, temperature in kelvin). This option cannot be combined
83
+ with `bias_correction=True`, because raw ERA5 and CORDEX units are not directly
84
+ compatible for every variable.
85
+
80
86
  ---
81
87
 
82
88
  ## Data Coverage
@@ -47,6 +47,12 @@ A single `get_climate_data()` call orchestrates a full pipeline:
47
47
  | **Bias correction** | ERA5 is automatically fetched as the reference; EQM is trained and applied — no external tools needed |
48
48
  | **Domain validation** | If your bounding box falls outside the chosen CORDEX domain, a corrected domain is suggested |
49
49
 
50
+ Unit conversion is enabled by default for backward compatibility. Pass
51
+ `convert_units=False` to keep the values and `units` metadata provided by the
52
+ source (for example, temperature in kelvin). This option cannot be combined
53
+ with `bias_correction=True`, because raw ERA5 and CORDEX units are not directly
54
+ compatible for every variable.
55
+
50
56
  ---
51
57
 
52
58
  ## Data Coverage
@@ -242,6 +242,7 @@ def _climate_data_for_variable(
242
242
  bias_correction: bool,
243
243
  historical: bool,
244
244
  remote: bool,
245
+ convert_units: bool = True,
245
246
  dataset: str = "CORDEX-CORE",
246
247
  retry_log_level: int = logging.WARNING,
247
248
  ) -> xr.DataArray:
@@ -262,6 +263,7 @@ def _climate_data_for_variable(
262
263
  gcm=gcm,
263
264
  rcm=rcm,
264
265
  rcp=rcp,
266
+ convert_units=convert_units,
265
267
  retry_log_level=retry_log_level,
266
268
  )
267
269
 
@@ -292,6 +294,7 @@ def _climate_data_for_variable(
292
294
  gcm=gcm,
293
295
  rcm=rcm,
294
296
  rcp=rcp,
297
+ convert_units=convert_units,
295
298
  retry_log_level=retry_log_level,
296
299
  )
297
300
  downloaded_models = list(
@@ -412,6 +415,7 @@ def _download_data(
412
415
  gcm: str,
413
416
  rcm: str,
414
417
  rcp: str,
418
+ convert_units: bool = True,
415
419
  retry_log_level: int = logging.WARNING,
416
420
  ) -> xr.DataArray:
417
421
  """Download a dataset, subset it to the bbox, and perform unit/calendar handling."""
@@ -556,35 +560,36 @@ def _download_data(
556
560
  else:
557
561
  raise
558
562
 
559
- if obs:
560
- if var in ["t2mx", "t2mn", "t2m"]:
561
- ds_cropped -= 273.15 # Convert from Kelvin to Celsius
562
- ds_cropped.attrs["units"] = "°C"
563
- elif var == "tp":
564
- ds_cropped *= 1000 # Convert precipitation
565
- ds_cropped.attrs["units"] = "mm"
566
- elif var == "ssrd":
567
- ds_cropped /= 86400 # Convert from J/m^2 to W/m^2
568
- ds_cropped.attrs["units"] = "W m-2"
569
- elif var == "sfcwind":
570
- ds_cropped = ds_cropped * (
571
- 4.87 / np.log((67.8 * 10) - 5.42)
572
- ) # Convert wind speed from 10 m to 2 m
573
- ds_cropped.attrs["units"] = "m s-1"
574
- else:
575
- if variable in ["tas", "tasmax", "tasmin"]:
576
- ds_cropped -= 273.15 # Convert from Kelvin to Celsius
577
- ds_cropped.attrs["units"] = "°C"
578
- elif variable == "pr":
579
- ds_cropped *= 86400 # Convert from kg m^-2 s^-1 to mm/day
580
- ds_cropped.attrs["units"] = "mm"
581
- elif variable == "rsds":
582
- ds_cropped.attrs["units"] = "W m-2"
583
- elif variable == "sfcWind":
584
- ds_cropped = ds_cropped * (
585
- 4.87 / np.log((67.8 * 10) - 5.42)
586
- ) # Convert wind speed from 10 m to 2 m
587
- ds_cropped.attrs["units"] = "m s-1"
563
+ if convert_units:
564
+ if obs:
565
+ if var in ["t2mx", "t2mn", "t2m"]:
566
+ ds_cropped -= 273.15 # Convert from Kelvin to Celsius
567
+ ds_cropped.attrs["units"] = "°C"
568
+ elif var == "tp":
569
+ ds_cropped *= 1000 # Convert precipitation
570
+ ds_cropped.attrs["units"] = "mm"
571
+ elif var == "ssrd":
572
+ ds_cropped /= 86400 # Convert from J/m^2 to W/m^2
573
+ ds_cropped.attrs["units"] = "W m-2"
574
+ elif var == "sfcwind":
575
+ ds_cropped = ds_cropped * (
576
+ 4.87 / np.log((67.8 * 10) - 5.42)
577
+ ) # Convert wind speed from 10 m to 2 m
578
+ ds_cropped.attrs["units"] = "m s-1"
579
+ else:
580
+ if variable in ["tas", "tasmax", "tasmin"]:
581
+ ds_cropped -= 273.15 # Convert from Kelvin to Celsius
582
+ ds_cropped.attrs["units"] = "°C"
583
+ elif variable == "pr":
584
+ ds_cropped *= 86400 # Convert from kg m^-2 s^-1 to mm/day
585
+ ds_cropped.attrs["units"] = "mm"
586
+ elif variable == "rsds":
587
+ ds_cropped.attrs["units"] = "W m-2"
588
+ elif variable == "sfcWind":
589
+ ds_cropped = ds_cropped * (
590
+ 4.87 / np.log((67.8 * 10) - 5.42)
591
+ ) # Convert wind speed from 10 m to 2 m
592
+ ds_cropped.attrs["units"] = "m s-1"
588
593
 
589
594
  # Fetch only the requested years, inside the retry loop; downstream
590
595
  # time-core operations (interpolate_na, bias correction) need it in memory.
@@ -615,12 +620,14 @@ def _download_data(
615
620
  raise last_exc
616
621
 
617
622
  if obs:
623
+ unit_handling = "unit conversion" if convert_units else "source units retained"
618
624
  log.info(
619
- f"ERA5 data for {variable} has been processed: unit conversion ({ds_cropped.attrs.get('units', 'unknown units')}), time selection ({min(years)}-{max(years)})"
625
+ f"ERA5 data for {variable} has been processed: {unit_handling} ({ds_cropped.attrs.get('units', 'unknown units')}), time selection ({min(years)}-{max(years)})"
620
626
  )
621
627
  else:
628
+ unit_handling = "unit conversion" if convert_units else "source units retained"
622
629
  log.info(
623
- f"CORDEX data for {variable} has been processed: unit conversion ({ds_cropped.attrs.get('units', 'unknown units')}), calendar transformation (360-day to Gregorian), time selection ({years[0]}-{years[-1]})"
630
+ f"CORDEX data for {variable} has been processed: {unit_handling} ({ds_cropped.attrs.get('units', 'unknown units')}), calendar transformation (360-day to Gregorian), time selection ({years[0]}-{years[-1]})"
624
631
  )
625
632
 
626
633
  return ds_cropped
@@ -116,6 +116,7 @@ def _get_climate_data_single(
116
116
  xlim: tuple[float, float] | None = None,
117
117
  ylim: tuple[float, float] | None = None,
118
118
  remote: bool = True,
119
+ convert_units: bool = True,
119
120
  variables: list[str] | None = None,
120
121
  num_processes: int = len(VALID_VARIABLES),
121
122
  max_threads_per_process: int = 3,
@@ -186,6 +187,11 @@ def _get_climate_data_single(
186
187
  "Cannot apply bias_correction=True when using dataset='CORDEX-CORE-BC'. "
187
188
  "The CORDEX-CORE-BC dataset is already bias-corrected using ISIMIP methodology."
188
189
  )
190
+ if bias_correction and not convert_units:
191
+ raise ValueError(
192
+ "convert_units=False cannot be used with bias_correction=True because "
193
+ "ERA5 and CORDEX source units are not directly compatible."
194
+ )
189
195
 
190
196
  # Validate variables if provided
191
197
  if variables is not None:
@@ -234,6 +240,7 @@ def _get_climate_data_single(
234
240
  bias_correction=bias_correction,
235
241
  historical=historical,
236
242
  remote=remote,
243
+ convert_units=convert_units,
237
244
  dataset=dataset,
238
245
  )
239
246
  except Exception as exc:
@@ -262,6 +269,7 @@ def _get_climate_data_single(
262
269
  "bias_correction": bias_correction,
263
270
  "historical": historical,
264
271
  "remote": remote,
272
+ "convert_units": convert_units,
265
273
  "dataset": dataset,
266
274
  },
267
275
  )
@@ -350,6 +358,7 @@ def get_climate_data(
350
358
  xlim: tuple[float, float] | None = None,
351
359
  ylim: tuple[float, float] | None = None,
352
360
  remote: bool = True,
361
+ convert_units: bool = True,
353
362
  variables: list[str] | None = None,
354
363
  num_processes: int = len(VALID_VARIABLES),
355
364
  max_threads_per_process: int = 3,
@@ -391,6 +400,8 @@ def get_climate_data(
391
400
  xlim (tuple or None): Longitudinal bounds of the region of interest. Use only when country is None (default: None).
392
401
  ylim (tuple or None): Latitudinal bounds of the region of interest. Use only when country is None (default: None).
393
402
  remote (bool): Flag to work with remote data or not (default: True).
403
+ convert_units (bool): Whether to convert source data to cavapy's standard units.
404
+ Set to False to preserve the values and units stored by the source (default: True).
394
405
  variables (list[str] or None): List of variables to process. Must be a subset of {VALID_VARIABLES}. If None, all variables are processed. (default: None).
395
406
  num_processes (int): Number of processes to use, one per variable for a single combo.
396
407
  If num_processes <= 1 or only one variable is requested, variables run sequentially.
@@ -425,6 +436,7 @@ def get_climate_data(
425
436
  xlim=xlim,
426
437
  ylim=ylim,
427
438
  remote=remote,
439
+ convert_units=convert_units,
428
440
  variables=variables,
429
441
  num_processes=num_processes,
430
442
  max_threads_per_process=max_threads_per_process,
@@ -452,6 +464,11 @@ def get_climate_data(
452
464
  "Cannot apply bias_correction=True when using dataset='CORDEX-CORE-BC'. "
453
465
  "The CORDEX-CORE-BC dataset is already bias-corrected using ISIMIP methodology."
454
466
  )
467
+ if bias_correction and not convert_units:
468
+ raise ValueError(
469
+ "convert_units=False cannot be used with bias_correction=True because "
470
+ "ERA5 and CORDEX source units are not directly compatible."
471
+ )
455
472
 
456
473
  if years_up_to is None:
457
474
  raise ValueError("years_up_to is required when obs is False")
@@ -483,6 +500,7 @@ def get_climate_data(
483
500
  xlim=xlim,
484
501
  ylim=ylim,
485
502
  remote=remote,
503
+ convert_units=convert_units,
486
504
  variables=variables,
487
505
  num_processes=num_processes,
488
506
  max_threads_per_process=max_threads_per_process,
@@ -535,6 +553,7 @@ def get_climate_data(
535
553
  "bias_correction": bias_correction,
536
554
  "historical": historical,
537
555
  "remote": remote,
556
+ "convert_units": convert_units,
538
557
  "dataset": dataset,
539
558
  "retry_log_level": retry_log_level,
540
559
  }
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "cavapy"
3
- version = "2.1.3"
3
+ version = "2.1.4"
4
4
  description = "CAVA Python package. Retrive climate data."
5
5
  authors = ["Riccardo Soldan <riccardosoldan@hotmail.it>", "Oleh Lokshyn <olokshyn@gmail.com>"]
6
6
  readme = "README.md"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes