disdrodb 0.1.4__py3-none-any.whl → 0.2.0__py3-none-any.whl

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.
Files changed (135) hide show
  1. disdrodb/__init__.py +1 -5
  2. disdrodb/_version.py +2 -2
  3. disdrodb/accessor/methods.py +14 -3
  4. disdrodb/api/checks.py +10 -0
  5. disdrodb/api/create_directories.py +0 -2
  6. disdrodb/api/io.py +14 -17
  7. disdrodb/api/path.py +42 -77
  8. disdrodb/api/search.py +89 -23
  9. disdrodb/cli/disdrodb_create_summary.py +11 -1
  10. disdrodb/cli/disdrodb_create_summary_station.py +10 -0
  11. disdrodb/cli/disdrodb_run_l0.py +1 -1
  12. disdrodb/cli/disdrodb_run_l0a.py +1 -1
  13. disdrodb/cli/disdrodb_run_l0b.py +1 -1
  14. disdrodb/cli/disdrodb_run_l0c.py +1 -1
  15. disdrodb/cli/disdrodb_run_l1.py +1 -1
  16. disdrodb/cli/disdrodb_run_l2e.py +1 -1
  17. disdrodb/cli/disdrodb_run_l2m.py +1 -1
  18. disdrodb/configs.py +30 -83
  19. disdrodb/constants.py +4 -3
  20. disdrodb/data_transfer/download_data.py +4 -2
  21. disdrodb/docs.py +2 -2
  22. disdrodb/etc/products/L1/1MIN.yaml +13 -0
  23. disdrodb/etc/products/L1/LPM/1MIN.yaml +13 -0
  24. disdrodb/etc/products/L1/PARSIVEL/1MIN.yaml +13 -0
  25. disdrodb/etc/products/L1/PARSIVEL2/1MIN.yaml +13 -0
  26. disdrodb/etc/products/L1/PWS100/1MIN.yaml +13 -0
  27. disdrodb/etc/products/L1/RD80/1MIN.yaml +13 -0
  28. disdrodb/etc/products/L1/SWS250/1MIN.yaml +13 -0
  29. disdrodb/etc/products/L1/global.yaml +7 -1
  30. disdrodb/etc/products/L2E/10MIN.yaml +1 -12
  31. disdrodb/etc/products/L2E/5MIN.yaml +1 -0
  32. disdrodb/etc/products/L2E/global.yaml +1 -1
  33. disdrodb/etc/products/L2M/MODELS/GAMMA_GS_ND_MAE.yaml +6 -0
  34. disdrodb/etc/products/L2M/{GAMMA_ML.yaml → MODELS/GAMMA_ML.yaml} +1 -1
  35. disdrodb/etc/products/L2M/MODELS/LOGNORMAL_GS_LOG_ND_MAE.yaml +6 -0
  36. disdrodb/etc/products/L2M/MODELS/LOGNORMAL_GS_ND_MAE.yaml +6 -0
  37. disdrodb/etc/products/L2M/MODELS/LOGNORMAL_ML.yaml +8 -0
  38. disdrodb/etc/products/L2M/MODELS/NGAMMA_GS_R_MAE.yaml +6 -0
  39. disdrodb/etc/products/L2M/global.yaml +11 -3
  40. disdrodb/l0/check_configs.py +49 -16
  41. disdrodb/l0/configs/LPM/l0a_encodings.yml +2 -2
  42. disdrodb/l0/configs/LPM/l0b_cf_attrs.yml +2 -2
  43. disdrodb/l0/configs/LPM/l0b_encodings.yml +2 -2
  44. disdrodb/l0/configs/LPM/raw_data_format.yml +2 -2
  45. disdrodb/l0/configs/PARSIVEL/l0b_encodings.yml +1 -1
  46. disdrodb/l0/configs/PWS100/l0b_encodings.yml +1 -0
  47. disdrodb/l0/configs/SWS250/bins_diameter.yml +108 -0
  48. disdrodb/l0/configs/SWS250/bins_velocity.yml +83 -0
  49. disdrodb/l0/configs/SWS250/l0a_encodings.yml +18 -0
  50. disdrodb/l0/configs/SWS250/l0b_cf_attrs.yml +72 -0
  51. disdrodb/l0/configs/SWS250/l0b_encodings.yml +155 -0
  52. disdrodb/l0/configs/SWS250/raw_data_format.yml +148 -0
  53. disdrodb/l0/l0_reader.py +2 -2
  54. disdrodb/l0/l0b_processing.py +70 -15
  55. disdrodb/l0/l0c_processing.py +7 -3
  56. disdrodb/l0/readers/LPM/ARM/ARM_LPM.py +1 -1
  57. disdrodb/l0/readers/LPM/AUSTRALIA/MELBOURNE_2007_LPM.py +2 -2
  58. disdrodb/l0/readers/LPM/BELGIUM/ULIEGE.py +256 -0
  59. disdrodb/l0/readers/LPM/BRAZIL/CHUVA_LPM.py +2 -2
  60. disdrodb/l0/readers/LPM/BRAZIL/GOAMAZON_LPM.py +2 -2
  61. disdrodb/l0/readers/LPM/GERMANY/DWD.py +491 -0
  62. disdrodb/l0/readers/LPM/ITALY/GID_LPM.py +2 -2
  63. disdrodb/l0/readers/LPM/ITALY/GID_LPM_W.py +2 -2
  64. disdrodb/l0/readers/LPM/KIT/CHWALA.py +2 -2
  65. disdrodb/l0/readers/LPM/SLOVENIA/ARSO.py +107 -12
  66. disdrodb/l0/readers/LPM/SLOVENIA/UL.py +3 -3
  67. disdrodb/l0/readers/LPM/SWITZERLAND/INNERERIZ_LPM.py +2 -2
  68. disdrodb/l0/readers/PARSIVEL/BASQUECOUNTRY/EUSKALMET_OTT.py +227 -0
  69. disdrodb/l0/readers/PARSIVEL/{GPM → NASA}/LPVEX.py +1 -1
  70. disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2010.py +5 -14
  71. disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2010_UF.py +8 -17
  72. disdrodb/l0/readers/PARSIVEL/SLOVENIA/UL.py +117 -8
  73. disdrodb/l0/readers/PARSIVEL2/BASQUECOUNTRY/EUSKALMET_OTT2.py +232 -0
  74. disdrodb/l0/readers/PARSIVEL2/BRAZIL/CHUVA_PARSIVEL2.py +10 -14
  75. disdrodb/l0/readers/PARSIVEL2/BRAZIL/GOAMAZON_PARSIVEL2.py +10 -14
  76. disdrodb/l0/readers/PARSIVEL2/DENMARK/DTU.py +8 -14
  77. disdrodb/l0/readers/PARSIVEL2/DENMARK/EROSION_raw.py +382 -0
  78. disdrodb/l0/readers/PARSIVEL2/FINLAND/FMI_PARSIVEL2.py +4 -0
  79. disdrodb/l0/readers/PARSIVEL2/FRANCE/OSUG.py +1 -1
  80. disdrodb/l0/readers/PARSIVEL2/GREECE/NOA.py +127 -0
  81. disdrodb/l0/readers/PARSIVEL2/ITALY/HYDROX.py +239 -0
  82. disdrodb/l0/readers/PARSIVEL2/NCAR/FARM_PARSIVEL2.py +5 -11
  83. disdrodb/l0/readers/PARSIVEL2/NCAR/PERILS_MIPS.py +4 -17
  84. disdrodb/l0/readers/PARSIVEL2/NCAR/RELAMPAGO_PARSIVEL2.py +5 -14
  85. disdrodb/l0/readers/PARSIVEL2/NCAR/SNOWIE_PJ.py +10 -13
  86. disdrodb/l0/readers/PARSIVEL2/NCAR/SNOWIE_SB.py +10 -13
  87. disdrodb/l0/readers/PARSIVEL2/PHILIPPINES/PAGASA.py +232 -0
  88. disdrodb/l0/readers/PARSIVEL2/SPAIN/CENER.py +6 -18
  89. disdrodb/l0/readers/PARSIVEL2/{NASA/LPVEX.py → SPAIN/GRANADA.py} +46 -35
  90. disdrodb/l0/readers/PARSIVEL2/SWEDEN/SMHI.py +189 -0
  91. disdrodb/l0/readers/PARSIVEL2/USA/{C3WE.py → CW3E.py} +10 -28
  92. disdrodb/l0/readers/PWS100/AUSTRIA/HOAL.py +321 -0
  93. disdrodb/l0/readers/SW250/BELGIUM/KMI.py +239 -0
  94. disdrodb/l1/beard_model.py +31 -129
  95. disdrodb/l1/fall_velocity.py +136 -83
  96. disdrodb/l1/filters.py +25 -28
  97. disdrodb/l1/processing.py +16 -17
  98. disdrodb/l1/resampling.py +101 -38
  99. disdrodb/l1_env/routines.py +46 -17
  100. disdrodb/l2/empirical_dsd.py +6 -0
  101. disdrodb/l2/processing.py +6 -5
  102. disdrodb/metadata/geolocation.py +0 -2
  103. disdrodb/metadata/search.py +3 -4
  104. disdrodb/psd/fitting.py +16 -13
  105. disdrodb/routines/l0.py +2 -2
  106. disdrodb/routines/l1.py +173 -60
  107. disdrodb/routines/l2.py +148 -284
  108. disdrodb/routines/options.py +345 -0
  109. disdrodb/routines/wrappers.py +14 -1
  110. disdrodb/scattering/axis_ratio.py +90 -84
  111. disdrodb/scattering/permittivity.py +6 -0
  112. disdrodb/summary/routines.py +735 -670
  113. disdrodb/utils/archiving.py +51 -44
  114. disdrodb/utils/attrs.py +3 -1
  115. disdrodb/utils/dask.py +4 -4
  116. disdrodb/utils/dict.py +33 -0
  117. disdrodb/utils/encoding.py +6 -1
  118. disdrodb/utils/routines.py +9 -8
  119. disdrodb/utils/time.py +11 -3
  120. disdrodb/viz/__init__.py +0 -13
  121. disdrodb/viz/plots.py +231 -1
  122. {disdrodb-0.1.4.dist-info → disdrodb-0.2.0.dist-info}/METADATA +2 -1
  123. {disdrodb-0.1.4.dist-info → disdrodb-0.2.0.dist-info}/RECORD +135 -103
  124. /disdrodb/etc/products/L2M/{NGAMMA_GS_LOG_ND_MAE.yaml → MODELS/NGAMMA_GS_LOG_ND_MAE.yaml} +0 -0
  125. /disdrodb/etc/products/L2M/{NGAMMA_GS_ND_MAE.yaml → MODELS/NGAMMA_GS_ND_MAE.yaml} +0 -0
  126. /disdrodb/etc/products/L2M/{NGAMMA_GS_Z_MAE.yaml → MODELS/NGAMMA_GS_Z_MAE.yaml} +0 -0
  127. /disdrodb/l0/readers/PARSIVEL/{GPM → NASA}/IFLOODS.py +0 -0
  128. /disdrodb/l0/readers/PARSIVEL/{GPM → NASA}/MC3E.py +0 -0
  129. /disdrodb/l0/readers/PARSIVEL/{GPM → NASA}/PIERS.py +0 -0
  130. /disdrodb/l0/readers/PARSIVEL2/{GPM → NASA}/GCPEX.py +0 -0
  131. /disdrodb/l0/readers/PARSIVEL2/{GPM → NASA}/NSSTC.py +0 -0
  132. {disdrodb-0.1.4.dist-info → disdrodb-0.2.0.dist-info}/WHEEL +0 -0
  133. {disdrodb-0.1.4.dist-info → disdrodb-0.2.0.dist-info}/entry_points.txt +0 -0
  134. {disdrodb-0.1.4.dist-info → disdrodb-0.2.0.dist-info}/licenses/LICENSE +0 -0
  135. {disdrodb-0.1.4.dist-info → disdrodb-0.2.0.dist-info}/top_level.txt +0 -0
@@ -456,67 +456,67 @@ def get_raindrop_reynolds_number(diameter, temperature, air_density, water_densi
456
456
  return reynolds_number
457
457
 
458
458
 
459
- def get_fall_velocity_beard_1976(diameter, temperature, air_density, water_density, g):
459
+ def get_drag_coefficient(diameter, air_density, water_density, fall_velocity, g=9.81):
460
460
  """
461
- Computes the terminal fall velocity of a raindrop in still air.
462
-
463
- Reference: Beard 1976; Pruppacher & Klett 1978
461
+ Computes the drag coefficient for a raindrop.
464
462
 
465
463
  Parameters
466
464
  ----------
467
465
  diameter : float
468
466
  Diameter of the raindrop in meters.
469
- temperature : float
470
- Temperature in Kelvin.
471
467
  air_density : float
472
468
  Density of air in kg/m^3.
473
469
  water_density : float
474
470
  Density of water in kg/m^3.
471
+ fall_velocity : float
472
+ Terminal fall velocity of the raindrop in m/s.
475
473
  g : float
476
474
  Gravitational acceleration in m/s^2.
477
475
 
478
476
  Returns
479
477
  -------
480
478
  float
481
- Terminal fall velocity of the raindrop in m/s.
479
+ Drag coefficient of the raindrop.
482
480
  """
483
- air_viscosity = get_air_dynamic_viscosity(temperature)
484
- reynolds_number = get_raindrop_reynolds_number(
485
- diameter=diameter,
486
- temperature=temperature,
487
- air_density=air_density,
488
- water_density=water_density,
489
- g=g,
490
- )
491
- fall_velocity = air_viscosity * reynolds_number / (air_density * diameter)
492
- return fall_velocity
481
+ delta_density = water_density - air_density
482
+ drag_coefficient = 4 * delta_density * g * diameter / (3 * air_density * fall_velocity**2)
483
+ return drag_coefficient
493
484
 
494
485
 
495
- def get_drag_coefficient(diameter, air_density, water_density, fall_velocity, g=9.81):
486
+ def get_fall_velocity_beard_1976(diameter, temperature, air_density, water_density, g):
496
487
  """
497
- Computes the drag coefficient for a raindrop.
488
+ Computes the terminal fall velocity of a raindrop in still air.
489
+
490
+ Reference: Beard 1976; Pruppacher & Klett 1978
498
491
 
499
492
  Parameters
500
493
  ----------
501
494
  diameter : float
502
495
  Diameter of the raindrop in meters.
496
+ temperature : float
497
+ Temperature in Kelvin.
503
498
  air_density : float
504
499
  Density of air in kg/m^3.
505
500
  water_density : float
506
501
  Density of water in kg/m^3.
507
- fall_velocity : float
508
- Terminal fall velocity of the raindrop in m/s.
509
502
  g : float
510
503
  Gravitational acceleration in m/s^2.
511
504
 
512
505
  Returns
513
506
  -------
514
507
  float
515
- Drag coefficient of the raindrop.
508
+ Terminal fall velocity of the raindrop in m/s.
516
509
  """
517
- delta_density = water_density - air_density
518
- drag_coefficient = 4 * delta_density * g * diameter / (3 * air_density * fall_velocity**2)
519
- return drag_coefficient
510
+ air_viscosity = get_air_dynamic_viscosity(temperature)
511
+ reynolds_number = get_raindrop_reynolds_number(
512
+ diameter=diameter,
513
+ temperature=temperature,
514
+ air_density=air_density,
515
+ water_density=water_density,
516
+ g=g,
517
+ )
518
+ fall_velocity = air_viscosity * reynolds_number / (air_density * diameter)
519
+ return fall_velocity
520
520
 
521
521
 
522
522
  def retrieve_fall_velocity(
@@ -573,19 +573,24 @@ def retrieve_fall_velocity(
573
573
  gas_constant_dry_air=gas_constant_dry_air,
574
574
  )
575
575
 
576
+ # else
577
+ # --> Estimate sea_level_air_pressure from air_pressure ?
578
+
576
579
  # Retrieve vapour pressure (from relative humidity)
577
580
  vapor_pressure = get_vapor_actual_pressure(
578
581
  relative_humidity=relative_humidity,
579
582
  temperature=temperature,
580
583
  )
581
584
 
582
- # Retrieve air density and water density
585
+ # Retrieve air density
583
586
  air_density = get_air_density(
584
587
  temperature=temperature,
585
588
  air_pressure=air_pressure,
586
589
  vapor_pressure=vapor_pressure,
587
590
  gas_constant_dry_air=gas_constant_dry_air,
588
591
  )
592
+
593
+ # Retrieve water density
589
594
  water_density = get_water_density(
590
595
  temperature=temperature,
591
596
  air_pressure=air_pressure,
@@ -611,106 +616,3 @@ def retrieve_fall_velocity(
611
616
  # fall_velocity=fall_velocity)
612
617
 
613
618
  return fall_velocity
614
-
615
-
616
- ####-----------------------------------------------------------------------------------------
617
- #### OLD CODE
618
-
619
-
620
- # def get_fall_velocity_beard_1977(diameter):
621
- # """
622
- # Compute the fall velocity of raindrops using the Beard (1977) relationship.
623
-
624
- # Parameters
625
- # ----------
626
- # diameter : array-like
627
- # Diameter of the raindrops in millimeters.
628
- # Valid up to 7 mm (0.7 cm).
629
-
630
- # Returns
631
- # -------
632
- # fall_velocity : array-like
633
- # Fall velocities in meters per second.
634
-
635
- # Notes
636
- # -----
637
- # This method uses an exponential function based on the work of Beard (1977),
638
- # valid at sea level conditions (pressure = 1 atm, temperature = 20°C,
639
- # air density = 1.194 kg/m³).
640
-
641
- # References
642
- # ----------
643
- # Beard, K. V. (1977).
644
- # Terminal velocity adjustment for cloud and precipitation drops aloft.
645
- # Journal of the Atmospheric Sciences, 34(8), 1293-1298.
646
- # https://doi.org/10.1175/1520-0469(1977)034<1293:TVAFCA>2.0.CO;2
647
-
648
- # """
649
- # diameter_cm = diameter/1000
650
- # c = [7.06037, 1.74951, 4.86324, 6.60631, 4.84606, 2.14922, 0.58714, 0.096348, 0.00869209, 0.00033089]
651
- # log_diameter = np.log(diameter_cm)
652
- # y = c[0] + sum(c * log_diameter**i for i, c in enumerate(c[1:], start=1))
653
- # fall_velocity = np.exp(y)
654
- # return fall_velocity
655
-
656
-
657
- # def get_fall_velocity_beard_1977(diameter, temperature, air_pressure, gas_constant_dry_air=287.04):
658
- # """
659
- # Computes the terminal fall velocity of a raindrop in still air.
660
-
661
- # This function is based on the Table 4 coefficients of Kenneth V. Beard (1977),
662
- # "Terminal Velocity and Shape of Cloud and Precipitation Drops Aloft",
663
- # Journal of the Atmospheric Sciences, Vol. 34, pp. 1293-1298.
664
-
665
- # Note: This approximation is valid at sea level with conditions:
666
- # Pressure = 1 atm, Temperature = 20°C, (saturated) air density = 1.194 kg/m³.
667
-
668
- # Parameters
669
- # ----------
670
- # diameter : array-like
671
- # Array of equivolume drop diameters in meters.
672
-
673
- # Returns
674
- # -------
675
- # fall_velocity : array-like
676
- # Array of terminal fall velocity in meters per second (m/s).
677
- # For diameters greater than 7 mm, the function returns NaN.
678
-
679
- # """
680
- # # PROBLEMATIC
681
- # # Compute sea level velocity
682
- # c = [7.06037, 1.74951, 4.86324, 6.60631, 4.84606, 2.14922, 0.58714, 0.096348, 0.00869209, 0.00033089]
683
- # log_diameter = np.log(diameter / 1000 * 10)
684
- # y = c[0] + sum(c * log_diameter**i for i, c in enumerate(c[1:], start=1))
685
- # v0 = np.exp(y)
686
-
687
- # # Compute fall velocity
688
- # t_20 = 273.15 + 20
689
- # eps_s = get_air_dynamic_viscosity(t_20) / get_air_dynamic_viscosity(temperature) - 1
690
- # eps_c = -1 + (
691
- # np.sqrt(
692
- # get_air_density(
693
- # temperature=t_20,
694
- # air_pressure=101325,
695
- # vapor_pressure=0,
696
- # gas_constant_dry_air=gas_constant_dry_air,
697
- # )
698
- # / get_air_density(
699
- # temperature=temperature,
700
- # air_pressure=air_pressure,
701
- # vapor_pressure=0,
702
- # gas_constant_dry_air=gas_constant_dry_air,
703
- # ),
704
- # )
705
- # )
706
- # a = 1.104 * eps_s
707
- # b = (1.058 * eps_c - 1.104 * eps_s) / 5.01
708
- # x = np.log(diameter) + 5.52
709
- # f = (a + b * x) + 1
710
- # fall_velocity = v0 * f
711
- # # fall_velocity.plot()
712
-
713
- # eps = 1.104 * eps_s + (1.058 * eps_c - 1.104 * eps_s) * np.log(diameter / 1e-3) / 5.01
714
- # # eps = 1.104 * eps_s + (1.058 * eps_c - 1.104 * eps_s) * np.log(diameter / 4e-5) / 5.01
715
- # fall_velocity = 0.01 * v0 * (1 + eps)
716
- # return fall_velocity
@@ -14,10 +14,14 @@
14
14
  # You should have received a copy of the GNU General Public License
15
15
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
16
  # -----------------------------------------------------------------------------.
17
- """Theoretical models to estimate the drop fall velocity."""
18
-
19
-
17
+ """Theoretical models to estimate the raindrop fall velocity based on drop diameter in mm."""
20
18
  import numpy as np
19
+ import xarray as xr
20
+
21
+ from disdrodb.constants import DIAMETER_DIMENSION
22
+ from disdrodb.l0.l0b_processing import ensure_valid_geolocation
23
+ from disdrodb.l1_env.routines import load_env_dataset
24
+ from disdrodb.utils.warnings import suppress_warnings
21
25
 
22
26
 
23
27
  def get_fall_velocity_atlas_1973(diameter):
@@ -53,7 +57,7 @@ def get_fall_velocity_atlas_1973(diameter):
53
57
 
54
58
  """
55
59
  fall_velocity = 9.65 - 10.3 * np.exp(-0.6 * diameter) # clip to 0 !
56
- fall_velocity = np.clip(fall_velocity, 0, None)
60
+ fall_velocity = fall_velocity.clip(min=0, max=None)
57
61
  return fall_velocity
58
62
 
59
63
 
@@ -80,6 +84,7 @@ def get_fall_velocity_brandes_2002(diameter):
80
84
 
81
85
  """
82
86
  fall_velocity = -0.1021 + 4.932 * diameter - 0.9551 * diameter**2 + 0.07934 * diameter**3 - 0.002362 * diameter**4
87
+ fall_velocity = fall_velocity.clip(min=0, max=None)
83
88
  return fall_velocity
84
89
 
85
90
 
@@ -107,6 +112,7 @@ def get_fall_velocity_uplinger_1981(diameter):
107
112
  """
108
113
  # Valid between 0.1 and 7 mm
109
114
  fall_velocity = 4.874 * diameter * np.exp(-0.195 * diameter)
115
+ fall_velocity = fall_velocity.clip(min=0, max=None)
110
116
  return fall_velocity
111
117
 
112
118
 
@@ -133,6 +139,7 @@ def get_fall_velocity_van_dijk_2002(diameter):
133
139
 
134
140
  """
135
141
  fall_velocity = -0.254 + 5.03 * diameter - 0.912 * diameter**2 + 0.0561 * diameter**3
142
+ fall_velocity = fall_velocity.clip(min=0, max=None)
136
143
  return fall_velocity
137
144
 
138
145
 
@@ -147,9 +154,10 @@ def get_fall_velocity_beard_1976(diameter, ds_env):
147
154
  A dataset containing the following environmental variables:
148
155
  - 'altitude' : Altitude in meters (m).
149
156
  - 'latitude' : Latitude in degrees.
150
- - 'temperature' : Temperature in degrees Celsius (°C).
157
+ - 'temperature' : Temperature in degrees Kelvin (K).
151
158
  - 'relative_humidity' : Relative humidity in percentage (%).
152
159
  - 'sea_level_air_pressure' : Sea level air pressure in Pascals (Pa).
160
+ - 'air_pressure': Air pressure in Pascals (Pa).
153
161
  - 'lapse_rate' : Lapse rate in degrees Celsius per meter (°C/m).
154
162
 
155
163
  Returns
@@ -166,121 +174,171 @@ def get_fall_velocity_beard_1976(diameter, ds_env):
166
174
  latitude=ds_env["latitude"],
167
175
  temperature=ds_env["temperature"],
168
176
  relative_humidity=ds_env["relative_humidity"],
169
- # TODO: add air_pressure # TODO
177
+ air_pressure=ds_env.get("air_pressure", None),
170
178
  sea_level_air_pressure=ds_env["sea_level_air_pressure"],
171
179
  lapse_rate=ds_env["lapse_rate"],
172
180
  )
181
+ fall_velocity = fall_velocity.clip(min=0, max=None)
173
182
  return fall_velocity
174
183
 
175
184
 
176
- def ensure_valid_coordinates(ds, default_altitude=0, default_latitude=0, default_longitude=0):
177
- """Ensure dataset valid coordinates for altitude, latitude, and longitude.
185
+ RAINDROP_FALL_VELOCITY_MODELS = {
186
+ "Atlas1973": get_fall_velocity_atlas_1973,
187
+ "Beard1976": get_fall_velocity_beard_1976,
188
+ "Brandes2002": get_fall_velocity_brandes_2002,
189
+ "Uplinger1981": get_fall_velocity_uplinger_1981,
190
+ "VanDijk2002": get_fall_velocity_van_dijk_2002,
191
+ }
192
+
193
+
194
+ def available_raindrop_fall_velocity_models():
195
+ """Return a list of the available raindrop fall velocity models."""
196
+ return list(RAINDROP_FALL_VELOCITY_MODELS)
178
197
 
179
- Invalid values are np.nan and -9999.
198
+
199
+ def check_raindrop_fall_velocity_model(model):
200
+ """Check validity of the specified raindrop fall velocity model."""
201
+ available_models = available_raindrop_fall_velocity_models()
202
+ if model not in available_models:
203
+ raise ValueError(f"{model} is an invalid raindrop fall velocity model. Valid models: {available_models}.")
204
+ return model
205
+
206
+
207
+ def get_raindrop_fall_velocity_model(model):
208
+ """Return the specified raindrop fall velocity model.
180
209
 
181
210
  Parameters
182
211
  ----------
183
- ds : xarray.Dataset
184
- The dataset for which to ensure valid geolocation coordinates.
185
- default_altitude : float, optional
186
- The default value to use for invalid altitude values. Defaults to 0.
187
- default_latitude : float, optional
188
- The default value to use for invalid latitude values. Defaults to 0.
189
- default_longitude : float, optional
190
- The default value to use for invalid longitude values. Defaults to 0.
212
+ model : str
213
+ The model to use for calculating the rain drop fall velocity. Available models are:
214
+ 'Atlas1973', 'Beard1976', 'Brandes2002', 'Uplinger1981', 'VanDijk2002'.
191
215
 
192
216
  Returns
193
217
  -------
194
- xarray.Dataset
195
- The dataset with invalid coordinates replaced by default values.
218
+ callable
219
+ A function which compute the raindrop fall velocity model
220
+ given the rain drop diameter in mm.
196
221
 
222
+ Notes
223
+ -----
224
+ This function serves as a wrapper to various raindrop fall velocity models.
225
+ It returns the appropriate model based on the `model` parameter.
197
226
  """
198
- # TODO raise error if not present
199
- invalid_altitude = np.logical_or(np.isnan(ds["altitude"]), ds["altitude"] == -9999)
200
- ds["altitude"] = ds["altitude"].where(~invalid_altitude, default_altitude)
201
-
202
- invalid_lat = np.logical_or(np.isnan(ds["latitude"]), ds["latitude"] == -9999)
203
- ds["latitude"] = ds["latitude"].where(~invalid_lat, default_latitude)
227
+ model = check_raindrop_fall_velocity_model(model)
228
+ return RAINDROP_FALL_VELOCITY_MODELS[model]
204
229
 
205
- invalid_lon = np.logical_or(np.isnan(ds["longitude"]), ds["longitude"] == -9999)
206
- ds["longitude"] = ds["longitude"].where(~invalid_lon, default_longitude)
207
- return ds
208
230
 
209
-
210
- def get_raindrop_fall_velocity(diameter, method, ds_env=None):
231
+ def get_raindrop_fall_velocity(diameter, model, ds_env=None):
211
232
  """Calculate the fall velocity of raindrops based on their diameter.
212
233
 
213
234
  Parameters
214
235
  ----------
215
236
  diameter : array-like
216
237
  The diameter of the raindrops in millimeters.
217
- method : str
218
- The method to use for calculating the fall velocity. Must be one of the following:
238
+ model : str
239
+ The model to use for calculating the raindrop fall velocity. Must be one of the following:
219
240
  'Atlas1973', 'Beard1976', 'Brandes2002', 'Uplinger1981', 'VanDijk2002'.
220
241
  ds_env : xr.Dataset, optional
242
+ Only required if model is 'Beard1976'.
221
243
  A dataset containing the following environmental variables:
222
- - 'altitude' : Altitude in meters (m).
223
- - 'latitude' : Latitude in degrees.
224
- - 'temperature' : Temperature in degrees Celsius (°C).
244
+ - 'altitude' (m)
245
+ - 'latitude' (°)
246
+ - 'temperature' : Temperature in degrees Kelvin (K).
225
247
  - 'relative_humidity' : Relative humidity. A value between 0 and 1.
226
248
  - 'sea_level_air_pressure' : Sea level air pressure in Pascals (Pa).
227
249
  - 'lapse_rate' : Lapse rate in degrees Celsius per meter (°C/m).
228
- It is required for for the 'Beard1976' method.
250
+ If not specified, sensible default values are used.
229
251
 
230
252
  Returns
231
253
  -------
232
- fall_velocity : array-like
233
- The calculated fall velocities of the raindrops.
254
+ fall_velocity : xr.DataArray
255
+ The calculated raindrop fall velocities per diameter.
234
256
 
235
257
  Notes
236
258
  -----
237
- The 'Beard1976' method requires additional environmental parameters such as altitude and latitude.
238
- These parameters can be provided through the `ds_env` argument. If not provided, default values will be used.
259
+ The 'Beard1976' model requires additional environmental parameters.
260
+ These parameters can be provided through the `ds_env` argument.
261
+ If not provided, default values are be used.
262
+
263
+ For D < 0.12, Atlas1973 relationship results output V = 0 m/s !
264
+ For D < 0.05, VanDijk2002 relationship results output V = 0 m/s !
265
+ For D < 0.02, Brandes relationship results output V = 0 m/s !
266
+
239
267
  """
240
- # Input diameter in mm
241
- dict_methods = {
242
- "Atlas1973": get_fall_velocity_atlas_1973,
243
- "Beard1976": get_fall_velocity_beard_1976,
244
- "Brandes2002": get_fall_velocity_brandes_2002,
245
- "Uplinger1981": get_fall_velocity_uplinger_1981,
246
- "VanDijk2002": get_fall_velocity_van_dijk_2002,
247
- }
248
268
  # Check valid method
249
- available_methods = list(dict_methods)
250
- if method not in dict_methods:
251
- raise ValueError(f"{method} is an invalid fall velocity method. Valid methods: {available_methods}.")
252
- # Copy diameter
253
- diameter = diameter.copy()
254
- # Initialize ds_env if None
255
- # if ds_env is None:
256
- # ds_env = load_env_dataset(ds_env)
269
+ model = check_raindrop_fall_velocity_model(model)
257
270
 
258
- # TODO: wrapper for DISDRODB product !
271
+ # Copy diameter
272
+ if isinstance(diameter, xr.DataArray):
273
+ diameter = diameter.copy()
274
+ else:
275
+ diameter = np.atleast_1d(diameter)
276
+ diameter = xr.DataArray(diameter, dims=DIAMETER_DIMENSION, coords={DIAMETER_DIMENSION: diameter.copy()})
277
+
278
+ # Initialize ds_env if None and method == "Beard1976"
279
+ if model == "Beard1976":
280
+ if ds_env is None:
281
+ ds_env = load_env_dataset()
282
+
283
+ # Ensure valid altitude and geolocation
284
+ # - altitude required by Beard
285
+ # - latitude required for gravity
286
+ for coord in ["altitude", "latitude"]:
287
+ ds_env = ensure_valid_geolocation(ds_env, coord=coord, errors="raise")
259
288
 
260
- # Ensure valid altitude and geolocation (if missing set defaults)
261
- # - altitude required by Beard
262
- # - latitude required for gravity
263
- ds_env = ensure_valid_coordinates(ds_env)
264
289
  # Retrieve fall velocity
265
- func = dict_methods[method]
266
- fall_velocity = func(diameter, ds_env=ds_env) if method == "Beard1976" else func(diameter)
267
- return fall_velocity
290
+ func = get_raindrop_fall_velocity_model(model)
291
+ with suppress_warnings(): # e.g. when diameter = 0 for Beard1976
292
+ fall_velocity = func(diameter, ds_env=ds_env) if model == "Beard1976" else func(diameter)
293
+
294
+ # Set to NaN for diameter outside [0, 10)
295
+ fall_velocity = fall_velocity.where(diameter < 10).where(diameter > 0)
296
+ # Ensure fall velocity is > 0 to avoid division by zero
297
+ # - Some models, at small diameter, can return negative/zero fall velocity
298
+ fall_velocity = fall_velocity.where(fall_velocity > 0)
268
299
 
300
+ # Add attributes
301
+ fall_velocity.name = "fall_velocity"
302
+ fall_velocity.attrs["units"] = "m/s"
303
+ fall_velocity.attrs["model"] = model
304
+ return fall_velocity.squeeze()
269
305
 
270
- def get_dataset_fall_velocity(ds, method="Brandes2002"):
271
- """Compute the fall velocity and add it to the dataset.
306
+
307
+ def get_raindrop_fall_velocity_from_ds(ds, ds_env=None, model="Beard1976"):
308
+ """Compute the raindrop fall velocity.
272
309
 
273
310
  Parameters
274
311
  ----------
275
312
  ds : xarray.Dataset
276
- DISDRODB L0C dataset.
277
- method : str, optional
278
- Method to compute fall velocity. The default method is ``"Brandes2002"``.
313
+ DISDRODB dataset with the ``'diameter_bin_center'`` coordinate.
314
+ The ``'altitude'`` and ``'latitude'`` coordinates are used if ``model='Beard1976'``.
315
+ model : str, optional
316
+ Model to compute rain drop fall velocity.
317
+ The default model is ``"Beard1976"``.
318
+ ds_env : xr.Dataset, optional
319
+ Only required if model is 'Beard1976'.
320
+ A dataset containing the following environmental variables:
321
+ - 'temperature' : Temperature in degrees Kelvin (K).
322
+ - 'relative_humidity' : Relative humidity. A value between 0 and 1.
323
+ - 'sea_level_air_pressure' : Sea level air pressure in Pascals (Pa).
324
+ - 'lapse_rate' : Lapse rate in degrees Celsius per meter (°C/m).
325
+ If not specified, sensible default values are used.
279
326
 
280
327
  Returns
281
328
  -------
282
- xarray.Dataset
283
- DISDRODB L0C dataset with an additional variable 'fall_velocity'.
329
+ xarray.DataArray
330
+ Rain drop fall velocity DataArray.
331
+
332
+ Notes
333
+ -----
334
+ The 'Beard1976' model requires additional environmental parameters.
335
+ These parameters can be provided through the `ds_env` argument.
336
+ If not provided, default values are be used.
337
+
338
+ For D < 0.12, Atlas1973 relationship results output V = 0 m/s
339
+ For D < 0.05, VanDijk2002 relationship results output V = 0 m/s
340
+ For D < 0.02, Brandes relationship results output V = 0 m/s
341
+
284
342
  """
285
343
  from disdrodb.constants import DIAMETER_DIMENSION
286
344
  from disdrodb.l1_env.routines import load_env_dataset
@@ -289,18 +347,13 @@ def get_dataset_fall_velocity(ds, method="Brandes2002"):
289
347
  if DIAMETER_DIMENSION not in ds.dims:
290
348
  raise ValueError(f"Diameter dimension '{DIAMETER_DIMENSION}' not found in dataset dimensions.")
291
349
 
292
- # Retrieve diameter values (in mm)
293
- diameter_bin_center = ds["diameter_bin_center"]
294
-
295
- # Ensure valid altitude and geolocation (if missing set defaults)
296
- # TODO: MOBILE CASE !
297
- default_geolocation = {"altitude": 0, "latitude": 0, "longitude": 0}
298
- dataset_coords = {key: ds[key] for key in default_geolocation if key in ds}
299
- default_geolocation.update(dataset_coords)
300
- ds = ds.assign_coords(default_geolocation)
350
+ # Retrieve ENV dataset
351
+ # - It checks and includes default geolocation if missing
352
+ # - For mobile disdrometer, infill missing geolocation with backward and forward filling
353
+ if ds_env is None:
354
+ ds_env = load_env_dataset(ds)
301
355
 
302
- # TODO: deal with ENV dataset
303
- ds_env = load_env_dataset(ds)
356
+ # Compute raindrop fall velocity
357
+ fall_velocity = get_raindrop_fall_velocity(diameter=ds["diameter_bin_center"], model=model, ds_env=ds_env) # mn
304
358
 
305
- fall_velocity = get_raindrop_fall_velocity(diameter_bin_center, method=method, ds_env=ds_env)
306
359
  return fall_velocity
disdrodb/l1/filters.py CHANGED
@@ -57,17 +57,15 @@ def filter_diameter_bins(ds, minimum_diameter=None, maximum_diameter=None):
57
57
  ds["diameter_bin_upper"] > minimum_diameter,
58
58
  ds["diameter_bin_lower"] < maximum_diameter,
59
59
  )
60
-
61
- # Select bins with diameter values entirely inside the specified min/max values
62
- # valid_indices = np.logical_and(
63
- # ds["diameter_bin_lower"] >= minimum_diameter,
64
- # ds["diameter_bin_upper"] <= maximum_diameter,
65
- # )
66
60
  ds = ds.isel({DIAMETER_DIMENSION: valid_indices})
61
+
62
+ if ds.sizes[DIAMETER_DIMENSION] == 0:
63
+ msg = f"Filtering using {minimum_diameter=} removes all diameter bins."
64
+ raise ValueError(msg)
67
65
  return ds
68
66
 
69
67
 
70
- def filter_velocity_bins(ds, minimum_velocity=0, maximum_velocity=12):
68
+ def filter_velocity_bins(ds, minimum_velocity=None, maximum_velocity=None):
71
69
  """
72
70
  Filter the dataset to include only velocity bins within specified bounds.
73
71
 
@@ -77,10 +75,10 @@ def filter_velocity_bins(ds, minimum_velocity=0, maximum_velocity=12):
77
75
  The dataset containing velocity bin data.
78
76
  minimum_velocity : float, optional
79
77
  The minimum velocity to include in the filter, in meters per second.
80
- Defaults to 0 m/s.
78
+ Defaults to the minimum value in `ds["velocity_bin_lower"]`.
81
79
  maximum_velocity : float, optional
82
80
  The maximum velocity to include in the filter, in meters per second.
83
- Defaults to 12 m/s.
81
+ Defaults to the maximum value in `ds["velocity_bin_upper"]`.
84
82
 
85
83
  Returns
86
84
  -------
@@ -103,16 +101,14 @@ def filter_velocity_bins(ds, minimum_velocity=0, maximum_velocity=12):
103
101
  ds["velocity_bin_lower"] < maximum_velocity,
104
102
  )
105
103
 
106
- # Select bins with velocity values entirely inside the specified min/max values
107
- # valid_indices = np.logical_and(
108
- # ds["velocity_bin_lower"] >= minimum_velocity,
109
- # ds["velocity_bin_upper"] <= maximum_velocity,
110
- # )
111
104
  ds = ds.isel({VELOCITY_DIMENSION: valid_indices})
105
+ if ds.sizes[VELOCITY_DIMENSION] == 0:
106
+ msg = f"Filtering using {minimum_velocity=} removes all velocity bins."
107
+ raise ValueError(msg)
112
108
  return ds
113
109
 
114
110
 
115
- def define_spectrum_mask(
111
+ def define_raindrop_spectrum_mask(
116
112
  drop_number,
117
113
  fall_velocity,
118
114
  above_velocity_fraction=None,
@@ -130,29 +126,29 @@ def define_spectrum_mask(
130
126
  drop_number : xarray.DataArray
131
127
  Array of drop counts per diameter and velocity bins.
132
128
  fall_velocity : array-like
133
- The expected terminal fall velocities for drops of given sizes.
129
+ The expected terminal fall velocities for rain drops of given sizes.
134
130
  above_velocity_fraction : float, optional
135
- Fraction of terminal fall velocity above which drops are considered too fast.
131
+ Fraction of terminal fall velocity above which rain drops are considered too fast.
136
132
  Either specify ``above_velocity_fraction`` or ``above_velocity_tolerance``.
137
133
  above_velocity_tolerance : float, optional
138
- Absolute tolerance above which drops terminal fall velocities are considered too fast.
134
+ Absolute tolerance above which rain drops terminal fall velocities are considered too fast.
139
135
  Either specify ``above_velocity_fraction`` or ``above_velocity_tolerance``.
140
136
  below_velocity_fraction : float, optional
141
- Fraction of terminal fall velocity below which drops are considered too slow.
137
+ Fraction of terminal fall velocity below which rain drops are considered too slow.
142
138
  Either specify ``below_velocity_fraction`` or ``below_velocity_tolerance``.
143
139
  below_velocity_tolerance : float, optional
144
- Absolute tolerance below which drops terminal fall velocities are considered too slow.
140
+ Absolute tolerance below which rain drops terminal fall velocities are considered too slow.
145
141
  Either specify ``below_velocity_fraction`` or ``below_velocity_tolerance``.
146
142
  maintain_smallest : bool, optional
147
- If True, ensures that the small drops in the spectrum are retained in the mask.
148
- The smallest drops are characterized by ``small_diameter_threshold``
143
+ If True, ensures that the small rain drops in the spectrum are retained in the mask.
144
+ The smallest rain drops are characterized by ``small_diameter_threshold``
149
145
  and ``small_velocity_threshold`` arguments.
150
146
  Defaults to False.
151
147
  small_diameter_threshold : float, optional
152
- The diameter threshold to use for keeping the smallest drop.
148
+ The diameter threshold to use for keeping the smallest rain drop.
153
149
  Defaults to 1 mm.
154
150
  small_velocity_threshold : float, optional
155
- The fall velocity threshold to use for keeping the smallest drops.
151
+ The fall velocity threshold to use for keeping the smallest rain drops.
156
152
  Defaults to 2.5 m/s.
157
153
 
158
154
  Returns
@@ -178,6 +174,7 @@ def define_spectrum_mask(
178
174
  above_fall_velocity = fall_velocity + above_velocity_tolerance
179
175
  else:
180
176
  above_fall_velocity = np.inf
177
+
181
178
  if below_velocity_fraction is not None:
182
179
  below_fall_velocity = fall_velocity * (1 - below_velocity_fraction)
183
180
  elif below_velocity_tolerance is not None:
@@ -191,15 +188,15 @@ def define_spectrum_mask(
191
188
 
192
189
  # Define mask
193
190
  mask = np.logical_and(
194
- np.logical_or(velocity_lower >= below_fall_velocity, velocity_upper >= below_fall_velocity),
195
- np.logical_or(velocity_lower <= above_fall_velocity, velocity_upper <= above_fall_velocity),
191
+ velocity_upper > below_fall_velocity,
192
+ velocity_lower < above_fall_velocity,
196
193
  )
197
194
 
198
195
  # Maintant smallest drops
199
196
  if maintain_smallest_drops:
200
197
  mask_smallest = np.logical_and(
201
- drop_number["diameter_bin_upper"] < small_diameter_threshold,
202
- drop_number["velocity_bin_upper"] < small_velocity_threshold,
198
+ drop_number["diameter_bin_upper"] <= small_diameter_threshold,
199
+ drop_number["velocity_bin_upper"] <= small_velocity_threshold,
203
200
  )
204
201
  mask = np.logical_or(mask, mask_smallest)
205
202