cloudnetpy 1.82.3__tar.gz → 1.83.1__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.
- {cloudnetpy-1.82.3/cloudnetpy.egg-info → cloudnetpy-1.83.1}/PKG-INFO +1 -1
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/categorize/attenuations/melting_attenuation.py +7 -6
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/datasource.py +2 -2
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/instruments/disdrometer/parsivel.py +1 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/instruments/nc_radar.py +2 -2
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/instruments/weather_station.py +63 -29
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/products/observation_products.py +1 -1
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/plotting/plot_meta.py +24 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/plotting/plotting.py +68 -5
- cloudnetpy-1.83.1/cloudnetpy/products/classification.py +464 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/version.py +2 -2
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1/cloudnetpy.egg-info}/PKG-INFO +1 -1
- cloudnetpy-1.82.3/cloudnetpy/products/classification.py +0 -228
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/LICENSE +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/MANIFEST.in +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/README.md +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/__init__.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/categorize/__init__.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/categorize/atmos_utils.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/categorize/attenuation.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/categorize/attenuations/__init__.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/categorize/attenuations/gas_attenuation.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/categorize/attenuations/liquid_attenuation.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/categorize/attenuations/rain_attenuation.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/categorize/categorize.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/categorize/classify.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/categorize/containers.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/categorize/disdrometer.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/categorize/droplet.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/categorize/falling.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/categorize/freezing.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/categorize/insects.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/categorize/itu.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/categorize/lidar.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/categorize/melting.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/categorize/model.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/categorize/mwr.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/categorize/radar.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/cli.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/cloudnetarray.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/concat_lib.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/constants.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/exceptions.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/instruments/__init__.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/instruments/basta.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/instruments/bowtie.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/instruments/ceilo.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/instruments/ceilometer.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/instruments/cl61d.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/instruments/cloudnet_instrument.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/instruments/copernicus.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/instruments/disdrometer/__init__.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/instruments/disdrometer/common.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/instruments/disdrometer/thies.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/instruments/fd12p.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/instruments/galileo.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/instruments/hatpro.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/instruments/instruments.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/instruments/lufft.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/instruments/mira.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/instruments/mrr.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/instruments/nc_lidar.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/instruments/pollyxt.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/instruments/radiometrics.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/instruments/rain_e_h3.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/instruments/rpg.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/instruments/rpg_reader.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/instruments/toa5.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/instruments/vaisala.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/metadata.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/__init__.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/file_handler.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/metadata.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/model_metadata.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/plotting/__init__.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/plotting/plot_meta.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/plotting/plot_tools.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/plotting/plotting.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/products/__init__.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/products/advance_methods.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/products/grid_methods.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/products/model_products.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/products/product_resampling.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/products/tools.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/statistics/__init__.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/statistics/statistical_methods.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/tests/__init__.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/tests/e2e/__init__.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/tests/e2e/conftest.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/tests/e2e/process_cf/__init__.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/tests/e2e/process_cf/main.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/tests/e2e/process_cf/tests.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/tests/e2e/process_iwc/__init__.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/tests/e2e/process_iwc/main.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/tests/e2e/process_iwc/tests.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/tests/e2e/process_lwc/__init__.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/tests/e2e/process_lwc/main.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/tests/e2e/process_lwc/tests.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/tests/unit/__init__.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/tests/unit/conftest.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/tests/unit/test_advance_methods.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/tests/unit/test_grid_methods.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/tests/unit/test_model_products.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/tests/unit/test_observation_products.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/tests/unit/test_plot_tools.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/tests/unit/test_plotting.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/tests/unit/test_statistical_methods.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/tests/unit/test_tools.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/model_evaluation/utils.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/output.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/plotting/__init__.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/products/__init__.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/products/der.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/products/drizzle.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/products/drizzle_error.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/products/drizzle_tools.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/products/epsilon.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/products/ier.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/products/iwc.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/products/lwc.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/products/mie_lu_tables.nc +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/products/mwr_tools.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/products/product_tools.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/py.typed +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/utils.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy.egg-info/SOURCES.txt +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy.egg-info/dependency_links.txt +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy.egg-info/entry_points.txt +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy.egg-info/requires.txt +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy.egg-info/top_level.txt +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/docs/source/conf.py +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/pyproject.toml +0 -0
- {cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/setup.cfg +0 -0
{cloudnetpy-1.82.3 → cloudnetpy-1.83.1}/cloudnetpy/categorize/attenuations/melting_attenuation.py
RENAMED
@@ -13,14 +13,14 @@ def calc_melting_attenuation(
|
|
13
13
|
data: Observations, classification: ClassificationResult
|
14
14
|
) -> Attenuation:
|
15
15
|
shape = classification.category_bits.melting.shape
|
16
|
-
|
16
|
+
no_rain = classification.is_rain == 0
|
17
17
|
|
18
18
|
affected_region = classification.category_bits.freezing.copy()
|
19
19
|
|
20
20
|
if data.disdrometer is None:
|
21
|
-
affected_region[~is_rain, :] = False
|
22
21
|
above_melting = utils.ffill(classification.category_bits.melting)
|
23
22
|
affected_region[~above_melting] = False
|
23
|
+
affected_region[no_rain, :] = False
|
24
24
|
return Attenuation(
|
25
25
|
amount=ma.masked_all(shape),
|
26
26
|
error=ma.masked_all(shape),
|
@@ -29,22 +29,23 @@ def calc_melting_attenuation(
|
|
29
29
|
)
|
30
30
|
|
31
31
|
rainfall_rate = data.disdrometer.data["rainfall_rate"][:]
|
32
|
-
rainfall_rate
|
32
|
+
rainfall_rate = ma.where(no_rain, 0, rainfall_rate)
|
33
|
+
|
33
34
|
frequency = data.radar.radar_frequency
|
34
35
|
|
35
36
|
attenuation_array = _calc_melting_attenuation(rainfall_rate, frequency)
|
36
37
|
|
37
38
|
amount = affected_region * utils.transpose(attenuation_array)
|
38
39
|
|
39
|
-
|
40
|
+
no_attenuation = amount == 0
|
40
41
|
|
41
|
-
|
42
|
+
affected_region[no_attenuation] = False
|
43
|
+
amount[no_attenuation] = ma.masked
|
42
44
|
|
43
45
|
band = utils.get_wl_band(data.radar.radar_frequency)
|
44
46
|
error_factor = {"Ka": 0.2, "W": 0.1}[band]
|
45
47
|
|
46
48
|
error = amount * error_factor
|
47
|
-
error[~affected_region] = ma.masked
|
48
49
|
|
49
50
|
return Attenuation(
|
50
51
|
amount=amount,
|
@@ -74,14 +74,14 @@ class DataSource:
|
|
74
74
|
ndarray: The actual data.
|
75
75
|
|
76
76
|
Raises:
|
77
|
-
|
77
|
+
KeyError: The variable is not found.
|
78
78
|
|
79
79
|
"""
|
80
80
|
for arg in args:
|
81
81
|
if arg in self.dataset.variables:
|
82
82
|
return self.dataset.variables[arg][:]
|
83
83
|
msg = f"Missing variable {args[0]} in the input file."
|
84
|
-
raise
|
84
|
+
raise KeyError(msg)
|
85
85
|
|
86
86
|
def append_data(
|
87
87
|
self,
|
@@ -205,6 +205,7 @@ TOA5_HEADERS = {
|
|
205
205
|
"precipitation": "_rain_accum",
|
206
206
|
"rain accum [mm]": "_rain_accum",
|
207
207
|
"weatherCodeWaWa": "synop_WaWa",
|
208
|
+
"wawa": "synop_WaWa",
|
208
209
|
"weather_code_wawa": "synop_WaWa",
|
209
210
|
"radarReflectivity": "radar_reflectivity",
|
210
211
|
"radar_reflectivity": "radar_reflectivity",
|
@@ -41,7 +41,7 @@ class NcRadar(DataSource, CloudnetInstrument):
|
|
41
41
|
name = keymap[key]
|
42
42
|
try:
|
43
43
|
array = self.getvar(key)
|
44
|
-
except
|
44
|
+
except KeyError:
|
45
45
|
logging.warning("Can not find variable %s from the input file", key)
|
46
46
|
continue
|
47
47
|
array = np.array(array) if utils.isscalar(array) else array
|
@@ -158,7 +158,7 @@ class NcRadar(DataSource, CloudnetInstrument):
|
|
158
158
|
self.data[key] = CloudnetArray(np.median(np.array(data)), key)
|
159
159
|
if "NyquistVelocity" in self.data:
|
160
160
|
del self.data["NyquistVelocity"]
|
161
|
-
except
|
161
|
+
except KeyError:
|
162
162
|
logging.warning("Unable to find nyquist_velocity")
|
163
163
|
|
164
164
|
def test_if_all_masked(self) -> None:
|
@@ -70,6 +70,8 @@ def ws2nc(
|
|
70
70
|
ws = LimassolWS(weather_station_file, site_meta)
|
71
71
|
elif site_meta["name"] == "L'Aquila":
|
72
72
|
ws = LAquilaWS(weather_station_file, site_meta)
|
73
|
+
elif site_meta["name"] == "Maïdo Observatory":
|
74
|
+
ws = MaidoWS(weather_station_file, site_meta)
|
73
75
|
else:
|
74
76
|
msg = "Unsupported site"
|
75
77
|
raise ValueError(msg)
|
@@ -162,6 +164,26 @@ class WS(CSVFile):
|
|
162
164
|
|
163
165
|
|
164
166
|
class PalaiseauWS(WS):
|
167
|
+
expected_header_identifiers: tuple[str, ...] = (
|
168
|
+
"DateTime(yyyy-mm-ddThh:mm:ssZ)",
|
169
|
+
"Windspeed(m/s)",
|
170
|
+
"Winddirection(deg",
|
171
|
+
"Airtemperature",
|
172
|
+
"Relativehumidity(%)",
|
173
|
+
"Pressure(hPa)",
|
174
|
+
"Precipitationrate(mm/min)",
|
175
|
+
"precipitation",
|
176
|
+
)
|
177
|
+
keys: tuple[str, ...] = (
|
178
|
+
"wind_speed",
|
179
|
+
"wind_direction",
|
180
|
+
"air_temperature",
|
181
|
+
"relative_humidity",
|
182
|
+
"air_pressure",
|
183
|
+
"rainfall_rate",
|
184
|
+
"rainfall_amount",
|
185
|
+
)
|
186
|
+
|
165
187
|
def __init__(self, filenames: Sequence[str | PathLike], site_meta: dict) -> None:
|
166
188
|
super().__init__(site_meta)
|
167
189
|
self.filenames = filenames
|
@@ -175,16 +197,16 @@ class PalaiseauWS(WS):
|
|
175
197
|
for row in data:
|
176
198
|
if not (columns := row.split()):
|
177
199
|
continue
|
178
|
-
if
|
179
|
-
header_row = "".join(columns)
|
180
|
-
if header_row not in header:
|
181
|
-
header.append(header_row)
|
182
|
-
else:
|
200
|
+
if re.match(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z", columns[0]):
|
183
201
|
timestamp = datetime.datetime.strptime(
|
184
202
|
columns[0], "%Y-%m-%dT%H:%M:%SZ"
|
185
203
|
).replace(tzinfo=datetime.timezone.utc)
|
186
204
|
values.append([timestamp] + [float(x) for x in columns[1:]])
|
187
205
|
timestamps.append(timestamp)
|
206
|
+
else:
|
207
|
+
header_row = "".join(columns)
|
208
|
+
if header_row not in header:
|
209
|
+
header.append(header_row)
|
188
210
|
|
189
211
|
self._validate_header(header)
|
190
212
|
return {"time": timestamps, "values": values}
|
@@ -204,16 +226,9 @@ class PalaiseauWS(WS):
|
|
204
226
|
]
|
205
227
|
|
206
228
|
def add_data(self) -> None:
|
207
|
-
|
208
|
-
"
|
209
|
-
|
210
|
-
"air_temperature",
|
211
|
-
"relative_humidity",
|
212
|
-
"air_pressure",
|
213
|
-
"rainfall_rate",
|
214
|
-
"rainfall_amount",
|
215
|
-
)
|
216
|
-
for ind, key in enumerate(keys):
|
229
|
+
for ind, key in enumerate(self.keys):
|
230
|
+
if key.startswith("_"):
|
231
|
+
continue
|
217
232
|
array = [row[ind + 1] for row in self._data["values"]]
|
218
233
|
array_masked = ma.masked_invalid(array)
|
219
234
|
self.data[key] = CloudnetArray(array_masked, key)
|
@@ -223,27 +238,46 @@ class PalaiseauWS(WS):
|
|
223
238
|
self.data["rainfall_amount"][:] / 1000
|
224
239
|
) # mm -> m
|
225
240
|
|
226
|
-
|
227
|
-
def _validate_header(header: list[str]) -> None:
|
228
|
-
expected_identifiers = [
|
229
|
-
"DateTime(yyyy-mm-ddThh:mm:ssZ)",
|
230
|
-
"Windspeed(m/s)",
|
231
|
-
"Winddirection(deg",
|
232
|
-
"Airtemperature",
|
233
|
-
"Relativehumidity(%)",
|
234
|
-
"Pressure(hPa)",
|
235
|
-
"Precipitationrate(mm/min)",
|
236
|
-
"precipitation",
|
237
|
-
]
|
241
|
+
def _validate_header(self, header: list[str]) -> None:
|
238
242
|
column_titles = [row for row in header if "Col." in row]
|
239
243
|
error_msg = "Unexpected weather station file format"
|
240
|
-
if len(column_titles) != len(
|
244
|
+
if len(column_titles) != len(self.expected_header_identifiers):
|
241
245
|
raise ValueError(error_msg)
|
242
|
-
for title, identifier in zip(
|
246
|
+
for title, identifier in zip(
|
247
|
+
column_titles, self.expected_header_identifiers, strict=True
|
248
|
+
):
|
243
249
|
if identifier not in title:
|
244
250
|
raise ValueError(error_msg)
|
245
251
|
|
246
252
|
|
253
|
+
class MaidoWS(PalaiseauWS):
|
254
|
+
expected_header_identifiers = (
|
255
|
+
"DateTimeyyyy-mm-ddThh:mm:ssZ",
|
256
|
+
"Winddirection-average",
|
257
|
+
"Windspeed-maximumvalue(m/s)",
|
258
|
+
"Windspeed-average(m/s)",
|
259
|
+
"Pressure-average(hPa)",
|
260
|
+
"Relativehumidity-maximumvalue(%)",
|
261
|
+
"Relativehumidity-average(%)",
|
262
|
+
"Airtemperature-minimumvalue",
|
263
|
+
"Airtemperature-average",
|
264
|
+
)
|
265
|
+
|
266
|
+
keys = (
|
267
|
+
"wind_direction",
|
268
|
+
"_wind_speed_max",
|
269
|
+
"wind_speed",
|
270
|
+
"air_pressure",
|
271
|
+
"_relative_humidity_max",
|
272
|
+
"relative_humidity",
|
273
|
+
"_air_temperature_min",
|
274
|
+
"air_temperature",
|
275
|
+
)
|
276
|
+
|
277
|
+
def convert_rainfall_amount(self) -> None:
|
278
|
+
pass
|
279
|
+
|
280
|
+
|
247
281
|
class BucharestWS(PalaiseauWS):
|
248
282
|
def convert_rainfall_rate(self) -> None:
|
249
283
|
rainfall_rate = self.data["rainfall_rate"][:]
|
@@ -90,6 +90,12 @@ _CLABEL = {
|
|
90
90
|
("Clutter", _COLORS["shockred"]),
|
91
91
|
("_Lidar molecular scattering", _COLORS["pink"]),
|
92
92
|
),
|
93
|
+
"signal_source_status": (
|
94
|
+
("Clear sky", _COLORS["white"]),
|
95
|
+
("Radar & lidar", _COLORS["green"]),
|
96
|
+
("Radar only", _COLORS["lightsteel"]),
|
97
|
+
("Lidar only", _COLORS["yellow"]),
|
98
|
+
),
|
93
99
|
"ice_retrieval_status": (
|
94
100
|
("_No ice", _COLORS["white"]),
|
95
101
|
("Reliable", _COLORS["green"]),
|
@@ -124,6 +130,15 @@ _CLABEL = {
|
|
124
130
|
("Unfeasible", _COLORS["red"]),
|
125
131
|
("Surrounding ice", _COLORS["lightsteel"]),
|
126
132
|
),
|
133
|
+
"radar_attenuation_status": (
|
134
|
+
("_Clear sky", _COLORS["white"]),
|
135
|
+
("Negligible", _COLORS["green"]),
|
136
|
+
("Minor", _COLORS["lightgreen"]),
|
137
|
+
("Moderate", _COLORS["yellow"]),
|
138
|
+
("Severe", _COLORS["red"]),
|
139
|
+
("Unquantifiable", _COLORS["seaweed_roll"]),
|
140
|
+
("Undetected", _COLORS["skyblue"]),
|
141
|
+
),
|
127
142
|
}
|
128
143
|
|
129
144
|
|
@@ -213,6 +228,9 @@ ATTRIBUTES = {
|
|
213
228
|
),
|
214
229
|
},
|
215
230
|
"fallback": {
|
231
|
+
"cloud_top_height_agl": PlotMeta(
|
232
|
+
moving_average=False,
|
233
|
+
),
|
216
234
|
"nubf": PlotMeta(plot_range=(0, 5)),
|
217
235
|
"ze_sat": PlotMeta(
|
218
236
|
plot_range=(-40, 15),
|
@@ -281,6 +299,9 @@ ATTRIBUTES = {
|
|
281
299
|
"der_retrieval_status": PlotMeta(
|
282
300
|
clabel=_CLABEL["der_retrieval_status"],
|
283
301
|
),
|
302
|
+
"radar_attenuation_status": PlotMeta(
|
303
|
+
clabel=_CLABEL["radar_attenuation_status"],
|
304
|
+
),
|
284
305
|
"mu": PlotMeta(
|
285
306
|
plot_range=(0, 10),
|
286
307
|
),
|
@@ -554,6 +575,9 @@ ATTRIBUTES = {
|
|
554
575
|
"detection_status": PlotMeta(
|
555
576
|
clabel=_CLABEL["detection_status"],
|
556
577
|
),
|
578
|
+
"signal_source_status": PlotMeta(
|
579
|
+
clabel=_CLABEL["signal_source_status"],
|
580
|
+
),
|
557
581
|
"iwc": PlotMeta(
|
558
582
|
plot_range=(1e-7, 1e-3),
|
559
583
|
log_scale=True,
|
@@ -28,6 +28,7 @@ from cloudnetpy.categorize.atmos_utils import calc_altitude
|
|
28
28
|
from cloudnetpy.exceptions import PlottingError
|
29
29
|
from cloudnetpy.instruments.ceilometer import calc_sigma_units
|
30
30
|
from cloudnetpy.plotting.plot_meta import ATTRIBUTES, PlotMeta
|
31
|
+
from cloudnetpy.products.classification import TopStatus
|
31
32
|
|
32
33
|
EARTHCARE_MAX_X = 517.84
|
33
34
|
|
@@ -375,6 +376,7 @@ class Plot:
|
|
375
376
|
"air_temperature": (add, -273.15, "\u00b0C"),
|
376
377
|
"r_accum_RT": (multiply, 1000, "mm"),
|
377
378
|
"r_accum_NRT": (multiply, 1000, "mm"),
|
379
|
+
"cloud_top_height_agl": (multiply, con.M_TO_KM, "Height (km AGL)"),
|
378
380
|
}
|
379
381
|
conversion_method, conversion, units = units_conversion.get(
|
380
382
|
self.sub_plot.variable.name, (multiply, 1, None)
|
@@ -467,6 +469,12 @@ class Plot:
|
|
467
469
|
self._data = data_new
|
468
470
|
figure_data.time_including_gaps = time_new
|
469
471
|
|
472
|
+
def _read_cloud_top_flags(
|
473
|
+
self, figure_data: FigureData, flag_value: int | tuple[int, ...]
|
474
|
+
) -> ndarray:
|
475
|
+
status = figure_data.file.variables["cloud_top_height_status"][:]
|
476
|
+
return np.isin(status, flag_value)
|
477
|
+
|
470
478
|
def _read_flagged_data(self, figure_data: FigureData) -> ndarray:
|
471
479
|
flag_names = [
|
472
480
|
f"{self.sub_plot.variable.name}_quality_flag",
|
@@ -503,6 +511,32 @@ class Plot2D(Plot):
|
|
503
511
|
if figure_data.is_mwrpy_product():
|
504
512
|
self._fill_flagged_data(figure_data)
|
505
513
|
|
514
|
+
if figure_data.variables[0].name == "signal_source_status":
|
515
|
+
self._indicate_rainy_profiles(figure_data)
|
516
|
+
|
517
|
+
def _indicate_rainy_profiles(self, figure_data: FigureData) -> None:
|
518
|
+
if "rain_detected" not in figure_data.file.variables:
|
519
|
+
return
|
520
|
+
rain = figure_data.file.variables["rain_detected"][:]
|
521
|
+
is_rain: ma.MaskedArray = ma.masked_array(np.zeros_like(rain), mask=(rain == 0))
|
522
|
+
if is_rain.mask.all():
|
523
|
+
return
|
524
|
+
self._ax.plot(
|
525
|
+
figure_data.time,
|
526
|
+
is_rain,
|
527
|
+
color="red",
|
528
|
+
marker="|",
|
529
|
+
linestyle="None",
|
530
|
+
markersize=10,
|
531
|
+
zorder=-999,
|
532
|
+
label="Rain",
|
533
|
+
)
|
534
|
+
self._ax.legend(
|
535
|
+
markerscale=0.75,
|
536
|
+
numpoints=1,
|
537
|
+
frameon=False,
|
538
|
+
)
|
539
|
+
|
506
540
|
def _fill_flagged_data(self, figure_data: FigureData) -> None:
|
507
541
|
flags = self._read_flagged_data(figure_data)
|
508
542
|
batches = find_batches_of_ones(flags)
|
@@ -672,11 +706,35 @@ class Plot1D(Plot):
|
|
672
706
|
self.sub_plot.set_yax(ylabel=units, y_limits=self._get_y_limits())
|
673
707
|
pos = self._ax.get_position()
|
674
708
|
self._ax.set_position((pos.x0, pos.y0, pos.width * 0.965, pos.height))
|
709
|
+
self._plot_flags(figure_data)
|
710
|
+
|
711
|
+
def _plot_flags(self, figure_data: FigureData) -> None:
|
675
712
|
if figure_data.is_mwrpy_product():
|
676
713
|
flags = self._read_flagged_data(figure_data)
|
677
714
|
if np.any(flags):
|
678
715
|
self._plot_flag_data(figure_data.time[flags], self._data_orig[flags])
|
679
716
|
self._add_legend()
|
717
|
+
if (
|
718
|
+
figure_data.variables[0].name == "cloud_top_height_agl"
|
719
|
+
and "cloud_top_height_status" in figure_data.file.variables
|
720
|
+
):
|
721
|
+
legend: tuple = ()
|
722
|
+
flag_value = (TopStatus.MODERATE_ATT, TopStatus.UNCORR_ATT)
|
723
|
+
flags = self._read_cloud_top_flags(figure_data, flag_value)
|
724
|
+
if np.any(flags):
|
725
|
+
self._plot_flag_data(
|
726
|
+
figure_data.time[flags], self._data_orig[flags], color="orange"
|
727
|
+
)
|
728
|
+
legend += ("Suspicious",)
|
729
|
+
flag_value = (TopStatus.SEVERE_ATT, TopStatus.ABOVE_RANGE)
|
730
|
+
flags = self._read_cloud_top_flags(figure_data, flag_value)
|
731
|
+
if np.any(flags):
|
732
|
+
self._plot_flag_data(
|
733
|
+
figure_data.time[flags], self._data_orig[flags], color="red"
|
734
|
+
)
|
735
|
+
legend += ("Unreliable",)
|
736
|
+
if legend:
|
737
|
+
self._add_legend(name=legend)
|
680
738
|
|
681
739
|
def plot_tb(self, figure_data: FigureData, freq_ind: int) -> None:
|
682
740
|
if len(self._data.shape) != 2 or freq_ind >= self._data.shape[1]:
|
@@ -728,20 +786,22 @@ class Plot1D(Plot):
|
|
728
786
|
},
|
729
787
|
)
|
730
788
|
|
731
|
-
def _plot_flag_data(
|
789
|
+
def _plot_flag_data(
|
790
|
+
self, time: ndarray, values: ndarray, color: str = "salmon"
|
791
|
+
) -> None:
|
732
792
|
self._ax.plot(
|
733
793
|
time,
|
734
794
|
values,
|
735
|
-
color=
|
795
|
+
color=color,
|
736
796
|
marker=".",
|
737
797
|
lw=0,
|
738
798
|
markersize=3,
|
739
799
|
zorder=_get_zorder("flags"),
|
740
800
|
)
|
741
801
|
|
742
|
-
def _add_legend(self) -> None:
|
802
|
+
def _add_legend(self, name: str | tuple = ("Flagged data",)) -> None:
|
743
803
|
self._ax.legend(
|
744
|
-
|
804
|
+
name,
|
745
805
|
markerscale=3,
|
746
806
|
numpoints=1,
|
747
807
|
frameon=False,
|
@@ -772,7 +832,10 @@ class Plot1D(Plot):
|
|
772
832
|
custom_options = {
|
773
833
|
"tb": {
|
774
834
|
"color": "lightblue",
|
775
|
-
}
|
835
|
+
},
|
836
|
+
"cloud_top_height_agl": {
|
837
|
+
"color": "steelblue",
|
838
|
+
},
|
776
839
|
}
|
777
840
|
|
778
841
|
variable_name = self.sub_plot.variable.name
|