cloudnetpy 1.76.1__py3-none-any.whl → 1.77.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.
@@ -3,6 +3,7 @@ from .bowtie import bowtie2nc
3
3
  from .ceilo import ceilo2nc
4
4
  from .copernicus import copernicus2nc
5
5
  from .disdrometer import parsivel2nc, thies2nc
6
+ from .fd12p import fd12p2nc
6
7
  from .galileo import galileo2nc
7
8
  from .hatpro import hatpro2l1c, hatpro2nc
8
9
  from .instruments import Instrument
@@ -109,17 +109,22 @@ class CSVFile(CloudnetInstrument):
109
109
  parsed = (
110
110
  utils.datetime2decimal_hours(value)
111
111
  if key == "time"
112
- else ma.array(value)
112
+ else ma.masked_invalid(value)
113
113
  )
114
114
  self.data[key] = CloudnetArray(parsed, key)
115
115
 
116
- def normalize_rainfall_amount(self) -> None:
117
- if "rainfall_amount" in self.data:
118
- amount = self.data["rainfall_amount"][:]
119
- offset = 0
120
- for i in range(1, len(amount)):
121
- if amount[i] + offset < amount[i - 1]:
122
- offset += amount[i - 1]
123
- amount[i] += offset
124
- amount -= amount[0]
125
- self.data["rainfall_amount"].data = amount
116
+ def normalize_cumulative_amount(self, key: str) -> None:
117
+ if key not in self.data:
118
+ return
119
+ amount = self.data[key][:]
120
+ offset = 0
121
+ last_valid = 0
122
+ for i in range(1, len(amount)):
123
+ if amount[i] is ma.masked:
124
+ continue
125
+ if amount[i] + offset < amount[last_valid]:
126
+ offset += amount[last_valid]
127
+ amount[i] += offset
128
+ last_valid = i
129
+ amount -= amount[0]
130
+ self.data[key].data = amount
@@ -0,0 +1,173 @@
1
+ import datetime
2
+ import math
3
+ from os import PathLike
4
+ from uuid import UUID
5
+
6
+ import numpy as np
7
+ from numpy import ma
8
+
9
+ from cloudnetpy import output
10
+ from cloudnetpy.exceptions import ValidTimeStampError
11
+ from cloudnetpy.instruments import instruments
12
+ from cloudnetpy.instruments.cloudnet_instrument import CSVFile
13
+ from cloudnetpy.metadata import MetaData
14
+
15
+
16
+ def fd12p2nc(
17
+ input_file: str | PathLike,
18
+ output_file: str | PathLike,
19
+ site_meta: dict,
20
+ uuid: str | UUID | None = None,
21
+ date: str | datetime.date | None = None,
22
+ ):
23
+ """Converts Vaisala FD12P into Cloudnet Level 1b netCDF file.
24
+
25
+ Args:
26
+ input_file: Filename of input file.
27
+ output_file: Output filename.
28
+ site_meta: Dictionary containing information about the site. Required key
29
+ is `name`.
30
+ uuid: Set specific UUID for the file.
31
+ date: Expected date of the measurements as YYYY-MM-DD or datetime.date object.
32
+
33
+ Returns:
34
+ UUID of the generated file.
35
+
36
+ Raises:
37
+ ValidTimeStampError: No valid timestamps found.
38
+ """
39
+ if isinstance(date, str):
40
+ date = datetime.date.fromisoformat(date)
41
+ if isinstance(uuid, str):
42
+ uuid = UUID(uuid)
43
+ fd12p = FD12P(site_meta)
44
+ fd12p.parse_input_file(input_file, date)
45
+ fd12p.add_data()
46
+ fd12p.add_date()
47
+ fd12p.screen_all_masked()
48
+ fd12p.sort_timestamps()
49
+ fd12p.remove_duplicate_timestamps()
50
+ fd12p.convert_units()
51
+ fd12p.normalize_cumulative_amount("precipitation_amount")
52
+ fd12p.normalize_cumulative_amount("snowfall_amount")
53
+ fd12p.add_site_geolocation()
54
+ attributes = output.add_time_attribute(ATTRIBUTES, fd12p.date)
55
+ output.update_attributes(fd12p.data, attributes)
56
+ return output.save_level1b(fd12p, output_file, uuid)
57
+
58
+
59
+ class FD12P(CSVFile):
60
+ def __init__(self, site_meta: dict):
61
+ super().__init__(site_meta)
62
+ self.instrument = instruments.FD12P
63
+ self._data = {
64
+ key: []
65
+ for key in (
66
+ "time",
67
+ "visibility",
68
+ "synop_WaWa",
69
+ "precipitation_rate",
70
+ "precipitation_amount",
71
+ "snowfall_amount",
72
+ )
73
+ }
74
+
75
+ def parse_input_file(
76
+ self, filename: str | PathLike, expected_date: datetime.date | None = None
77
+ ):
78
+ # In Lindenberg, format is date and time followed by Message 2 without
79
+ # non-printable characters.
80
+ with open(filename) as file:
81
+ for line in file:
82
+ columns = line.split()
83
+ date = _parse_date(columns[0])
84
+ time = _parse_time(columns[1])
85
+ self._data["time"].append(datetime.datetime.combine(date, time))
86
+ self._data["visibility"].append(_parse_int(columns[4]))
87
+ self._data["synop_WaWa"].append(_parse_int(columns[7]))
88
+ self._data["precipitation_rate"].append(
89
+ _parse_float(columns[10])
90
+ ) # mm/h
91
+ self._data["precipitation_amount"].append(
92
+ _parse_float(columns[11])
93
+ ) # mm
94
+ self._data["snowfall_amount"].append(_parse_int(columns[12])) # mm
95
+ for key in ("visibility", "synop_WaWa", "snowfall_amount"):
96
+ values = np.array(
97
+ [0 if x is math.nan else x for x in self._data[key]], dtype=np.int32
98
+ )
99
+ mask = np.array([x is math.nan for x in self._data[key]])
100
+ self._data[key] = ma.array(values, mask=mask)
101
+ self._data["snowfall_amount"] = self._data["snowfall_amount"].astype(np.float32)
102
+ if expected_date:
103
+ self._data["time"] = [
104
+ d for d in self._data["time"] if d.date() == expected_date
105
+ ]
106
+ if not self._data["time"]:
107
+ raise ValidTimeStampError
108
+
109
+ def convert_units(self) -> None:
110
+ precipitation_rate = self.data["precipitation_rate"][:]
111
+ self.data["precipitation_rate"].data = (
112
+ precipitation_rate / 3600 / 1000
113
+ ) # mm/h -> m/s
114
+ for key in ("precipitation_amount", "snowfall_amount"):
115
+ self.data[key].data = self.data[key][:] / 1000 # mm -> m
116
+
117
+ def screen_all_masked(self) -> None:
118
+ is_valid = np.ones_like(self.data["time"][:], dtype=np.bool)
119
+ for key in self.data:
120
+ if key == "time":
121
+ continue
122
+ is_valid &= ma.getmaskarray(self.data[key][:])
123
+ self.screen_time_indices(~is_valid)
124
+
125
+
126
+ def _parse_date(date: str) -> datetime.date:
127
+ day, month, year = map(int, date.split("."))
128
+ return datetime.date(year, month, day)
129
+
130
+
131
+ def _parse_time(time: str) -> datetime.time:
132
+ hour, minute, *rest = [int(x) for x in time.split(":")]
133
+ second = rest[0] if rest else 0
134
+ return datetime.time(hour, minute, second)
135
+
136
+
137
+ def _parse_int(value: str) -> float:
138
+ if "/" in value:
139
+ return math.nan
140
+ return int(value)
141
+
142
+
143
+ def _parse_float(value: str) -> float:
144
+ if "/" in value:
145
+ return math.nan
146
+ return float(value)
147
+
148
+
149
+ ATTRIBUTES = {
150
+ "visibility": MetaData(
151
+ long_name="Meteorological optical range (MOR) visibility",
152
+ units="m",
153
+ standard_name="visibility_in_air",
154
+ ),
155
+ "precipitation_rate": MetaData(
156
+ long_name="Precipitation rate",
157
+ standard_name="lwe_precipitation_rate",
158
+ units="m s-1",
159
+ ),
160
+ "precipitation_amount": MetaData(
161
+ long_name="Precipitation amount",
162
+ standard_name="lwe_thickness_of_precipitation_amount",
163
+ units="m",
164
+ comment="Cumulated precipitation since 00:00 UTC",
165
+ ),
166
+ "snowfall_amount": MetaData(
167
+ long_name="Snowfall amount",
168
+ units="m",
169
+ standard_name="thickness_of_snowfall_amount",
170
+ comment="Cumulated snow since 00:00 UTC",
171
+ ),
172
+ "synop_WaWa": MetaData(long_name="Synop code WaWa", units="1"),
173
+ }
@@ -246,3 +246,10 @@ MRR_PRO = Instrument(
246
246
  model="MRR-PRO",
247
247
  frequency=24.23,
248
248
  )
249
+
250
+ FD12P = Instrument(
251
+ manufacturer="Vaisala",
252
+ domain="weather-station",
253
+ category="present weather sensor",
254
+ model="FD12P",
255
+ )
@@ -40,7 +40,7 @@ def rain_e_h32nc(
40
40
  rain.add_data()
41
41
  rain.add_date()
42
42
  rain.convert_units()
43
- rain.normalize_rainfall_amount()
43
+ rain.normalize_cumulative_amount("rainfall_amount")
44
44
  rain.add_site_geolocation()
45
45
  rain.sort_timestamps()
46
46
  rain.remove_duplicate_timestamps()
@@ -77,7 +77,7 @@ def ws2nc(
77
77
  ws.convert_pressure()
78
78
  ws.convert_rainfall_rate()
79
79
  ws.convert_rainfall_amount()
80
- ws.normalize_rainfall_amount()
80
+ ws.normalize_cumulative_amount("rainfall_amount")
81
81
  ws.calculate_rainfall_amount()
82
82
  attributes = output.add_time_attribute({}, ws.date)
83
83
  output.update_attributes(ws.data, attributes)
@@ -361,9 +361,12 @@ class Plot:
361
361
  units_conversion = {
362
362
  "rainfall_rate": (multiply, 3600000, "mm h$^{-1}$"),
363
363
  "snowfall_rate": (multiply, 3600000, "mm h$^{-1}$"),
364
+ "precipitation_rate": (multiply, 3600000, "mm h$^{-1}$"),
364
365
  "air_pressure": (multiply, 0.01, "hPa"),
365
366
  "relative_humidity": (multiply, 100, "%"),
366
367
  "rainfall_amount": (multiply, 1000, "mm"),
368
+ "snowfall_amount": (multiply, 1000, "mm"),
369
+ "precipitation_amount": (multiply, 1000, "mm"),
367
370
  "air_temperature": (add, -273.15, "\u00b0C"),
368
371
  "r_accum_RT": (multiply, 1000, "mm"),
369
372
  "r_accum_NRT": (multiply, 1000, "mm"),
cloudnetpy/version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  MAJOR = 1
2
- MINOR = 76
3
- PATCH = 1
2
+ MINOR = 77
3
+ PATCH = 0
4
4
  __version__ = f"{MAJOR}.{MINOR}.{PATCH}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudnetpy
3
- Version: 1.76.1
3
+ Version: 1.77.0
4
4
  Summary: Python package for Cloudnet processing
5
5
  Author: Simo Tukiainen
6
6
  License: MIT License
@@ -9,7 +9,7 @@ cloudnetpy/metadata.py,sha256=lO7BCbVAzFoH3Nq-VuezYX0f7MnbG1Zp11g5GSiuQwM,6189
9
9
  cloudnetpy/output.py,sha256=l0LoOhcGCBrg2EJ4NT1xZ7-UKWdV7X7yQ0fJmhkwJVc,15829
10
10
  cloudnetpy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  cloudnetpy/utils.py,sha256=SSZWk82c4nkAiTcLdOKGVvxt5ovETdAMn_TLxVeYpBY,33473
12
- cloudnetpy/version.py,sha256=h_CrP8sn9JN6-YKTFNNaJcVYnr_Y-PGPAYa6HOR0wGo,72
12
+ cloudnetpy/version.py,sha256=2wWI0Vrxs-3sRV0GweamYgxmMxkoJvam_z0qRWpiarc,72
13
13
  cloudnetpy/categorize/__init__.py,sha256=s-SJaysvVpVVo5kidiruWQO6p3gv2TXwY1wEHYO5D6I,44
14
14
  cloudnetpy/categorize/atmos_utils.py,sha256=RcmbKxm2COkE7WEya0mK3yX5rzUbrewRVh3ekm01RtM,10598
15
15
  cloudnetpy/categorize/attenuation.py,sha256=Y_-fzmQTltWTqIZTulJhovC7a6ifpMcaAazDJcnMIOc,990
@@ -32,17 +32,18 @@ cloudnetpy/categorize/attenuations/gas_attenuation.py,sha256=emr-RCxQT0i2N8k6eBN
32
32
  cloudnetpy/categorize/attenuations/liquid_attenuation.py,sha256=0p0G79BPkw1itCXHMwbvkNHtJGBocJzow3gNHAirChI,3036
33
33
  cloudnetpy/categorize/attenuations/melting_attenuation.py,sha256=9c9xoZHtGUbjFYJxkVc3UUDHLDy0UbNUZ32ITtnsj5w,2333
34
34
  cloudnetpy/categorize/attenuations/rain_attenuation.py,sha256=qazJzRyXf9vbjJhh4yiFmABI4L57j5W_6YZ-6qjRiBI,2839
35
- cloudnetpy/instruments/__init__.py,sha256=sbJZBYWynZbGAWb8VMMaT5qXuCyzG1LwEdHhVxFXVMk,500
35
+ cloudnetpy/instruments/__init__.py,sha256=PEgrrQNoiOuN_ctYilmt4LV2QCLg1likPjJdWtuGlLs,528
36
36
  cloudnetpy/instruments/basta.py,sha256=Lb_EhQTI93S5Bd9osDbCE_tC8gZreRsHz7D2_dFOjmE,3793
37
37
  cloudnetpy/instruments/bowtie.py,sha256=Hp4mzjGqvYw5bhgAy_LvScYrf3Xm3ULbtPjhG9GnAJ8,2977
38
38
  cloudnetpy/instruments/ceilo.py,sha256=qM3AkQKHUblhRCD42HsB6lr82giBH-0g_VzoWHZDgeA,9535
39
39
  cloudnetpy/instruments/ceilometer.py,sha256=ati9-fUQ54K9tvynIPB-nlBYwtvBVaQtUCjVCLNB67w,12059
40
40
  cloudnetpy/instruments/cl61d.py,sha256=g6DNBFju3wYhLFl32DKmC8pUup7y-EupXoUU0fuoGGA,1990
41
- cloudnetpy/instruments/cloudnet_instrument.py,sha256=3qJe8STIvsU8irj79xuElFUZa0jUsSSg2lq7Ozo1om4,4401
41
+ cloudnetpy/instruments/cloudnet_instrument.py,sha256=SGPsRYYoGPoRoDY7hHJcKUVX0A23X0Telc00Fu01PnY,4495
42
42
  cloudnetpy/instruments/copernicus.py,sha256=99idcn6-iKOSvSslNjwFRng3gwlTLFjKPiT1tnVytpQ,6613
43
+ cloudnetpy/instruments/fd12p.py,sha256=LNY_yYk9DshGkNJvGEhOsgDmfpHGZOFRRJAYkT_Qw0M,5868
43
44
  cloudnetpy/instruments/galileo.py,sha256=BjWE15_S3tTCOmAM5k--oicI3wghKaO0hv9EUBxtbl8,4830
44
45
  cloudnetpy/instruments/hatpro.py,sha256=G1fHsY9LTos4vHP5kFubjE5Wg2uTVFZpYDSD8VAo-zw,9590
45
- cloudnetpy/instruments/instruments.py,sha256=hdELBl4zw9gZghXLjmUBmC_XnyGvNuOyaYZzSl9M17k,4704
46
+ cloudnetpy/instruments/instruments.py,sha256=z8Osjww3iQRxKvzXdISl-5vV6gShtji8Db5k-ZzDQ-0,4843
46
47
  cloudnetpy/instruments/lufft.py,sha256=nIoEKuuFGKq2dLqkX7zW-HpAifefG472tZhKfXE1yoA,4212
47
48
  cloudnetpy/instruments/mira.py,sha256=IH88dnV5fdAQ-A04S23ROgNmT4GBAtzXQxCr_9fWj-Q,11634
48
49
  cloudnetpy/instruments/mrr.py,sha256=eeAzCp3CiHGauywjwvMUAFwZ4vBOZMcd3IlF8KsrLQo,5711
@@ -50,12 +51,12 @@ cloudnetpy/instruments/nc_lidar.py,sha256=5gQG9PApnNPrHmS9_zanl8HEYIQuGRpbnzC3wf
50
51
  cloudnetpy/instruments/nc_radar.py,sha256=HlaZeH5939R86ukF8K-P4Kfzb5-CpLB15LU2u94C5eI,7330
51
52
  cloudnetpy/instruments/pollyxt.py,sha256=U3g-ttmcs02LuLwVOydP3GjeNcmDyoYQroB-leIGdHY,10060
52
53
  cloudnetpy/instruments/radiometrics.py,sha256=_ZBHYiw3u4m0UDPaYRHnx-ofq2FS59Vdv3-fLiOzm9I,15471
53
- cloudnetpy/instruments/rain_e_h3.py,sha256=9TdpP4UzMBNIt2iE2GL6K9dFldzTHPLOrU8Q3tcosCU,5317
54
+ cloudnetpy/instruments/rain_e_h3.py,sha256=JEg4Ko7ZdfjAUJwJ1BWdTkm4K7r3s8WKrPb-HidTqpg,5336
54
55
  cloudnetpy/instruments/rpg.py,sha256=m3-xLJ-w2T7Ip7jBveWsGrts4tmNvdc-Lb4HebvHQjQ,17319
55
56
  cloudnetpy/instruments/rpg_reader.py,sha256=ThztFuVrWxhmWVAfZTfQDeUiKK1XMTbtv08IBe8GK98,11364
56
57
  cloudnetpy/instruments/toa5.py,sha256=CfmmBMv5iMGaWHIGBK01Rw24cuXC1R1RMNTXkmsm340,1760
57
58
  cloudnetpy/instruments/vaisala.py,sha256=RKAw_fVry4YOUF0i2_-2jLIc6_H85oL8USA4ji9rh0o,4583
58
- cloudnetpy/instruments/weather_station.py,sha256=Ok2i4RUkbEHdOuXuDPkMPSRp_vAiST7SQeg2amIavyI,24530
59
+ cloudnetpy/instruments/weather_station.py,sha256=pZK7I5bk1USDRoTeIhZoWzbka9ciea5ypA3oIzZX-7g,24549
59
60
  cloudnetpy/instruments/disdrometer/__init__.py,sha256=lyjwttWvFvuwYxEkusoAvgRcbBmglmOp5HJOpXUqLWo,93
60
61
  cloudnetpy/instruments/disdrometer/common.py,sha256=g52iK2aNp3Z88kovUmGVpC54NZomPa9D871gzO0AmQ4,9267
61
62
  cloudnetpy/instruments/disdrometer/parsivel.py,sha256=HJZrEysQkx9MiIVPDV25CYHpXi_SjgZlgO-otoaKK34,25640
@@ -102,7 +103,7 @@ cloudnetpy/model_evaluation/tests/unit/test_statistical_methods.py,sha256=Ra3r4V
102
103
  cloudnetpy/model_evaluation/tests/unit/test_tools.py,sha256=Ia_VrLdV2NstX5gbx_3AZTOAlrgLAy_xFZ8fHYVX0xI,3817
103
104
  cloudnetpy/plotting/__init__.py,sha256=lg9Smn4BI0dVBgnDLC3JVJ4GmwoSnO-qoSd4ApvwV6Y,107
104
105
  cloudnetpy/plotting/plot_meta.py,sha256=qfyZJNis937uM-NJseer8i4FO7I_v5jhQPyFl5Uszi8,17390
105
- cloudnetpy/plotting/plotting.py,sha256=ghdR-DW0N4wMju20LM0sNCPXkueR1WpsimQfiA-eQQQ,38422
106
+ cloudnetpy/plotting/plotting.py,sha256=nMdrXg7WEUUAHFQ0-_kxlETuXYd17-mzYNXbbsE0nVU,38607
106
107
  cloudnetpy/products/__init__.py,sha256=cBJdJBYltz5ZTKDqnRo-0StytAZK8gE3RYxxriFA4ak,295
107
108
  cloudnetpy/products/classification.py,sha256=KwAiBSgFwDqhM114NIgYiUjj8HoYc7gAlc8E1QgcSig,8207
108
109
  cloudnetpy/products/der.py,sha256=soypE7uSEP4uHUCCQVEhyXsKY6e9mzV9B_2S5GUizqk,12729
@@ -116,10 +117,10 @@ cloudnetpy/products/lwc.py,sha256=sl6Al2tuH3KkCBrPbWTmuz3jlD5UQJ4D6qBsn1tt2CQ,18
116
117
  cloudnetpy/products/mie_lu_tables.nc,sha256=It4fYpqJXlqOgL8jeZ-PxGzP08PMrELIDVe55y9ob58,16637951
117
118
  cloudnetpy/products/mwr_tools.py,sha256=8HPZpQMTojKZP1JS1S83IE0sxmbDE9bxlaWoqmGnUZE,6199
118
119
  cloudnetpy/products/product_tools.py,sha256=uu4l6reuGbPcW3TgttbaSrqIKbyYGhBVTdnC7opKvmg,11101
119
- cloudnetpy-1.76.1.dist-info/licenses/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
120
+ cloudnetpy-1.77.0.dist-info/licenses/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
120
121
  docs/source/conf.py,sha256=IKiFWw6xhUd8NrCg0q7l596Ck1d61XWeVjIFHVSG9Og,1490
121
- cloudnetpy-1.76.1.dist-info/METADATA,sha256=D879CAfuvaUbCF960Vvrb0Xiik6hijq1yiFREIFjZcE,5796
122
- cloudnetpy-1.76.1.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
123
- cloudnetpy-1.76.1.dist-info/entry_points.txt,sha256=HhY7LwCFk4qFgDlXx_Fy983ZTd831WlhtdPIzV-Y3dY,51
124
- cloudnetpy-1.76.1.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
125
- cloudnetpy-1.76.1.dist-info/RECORD,,
122
+ cloudnetpy-1.77.0.dist-info/METADATA,sha256=tEn0YbqanXZxITD44cJM-bw_30aA1s-lwhIZQEJjtzA,5796
123
+ cloudnetpy-1.77.0.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
124
+ cloudnetpy-1.77.0.dist-info/entry_points.txt,sha256=HhY7LwCFk4qFgDlXx_Fy983ZTd831WlhtdPIzV-Y3dY,51
125
+ cloudnetpy-1.77.0.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
126
+ cloudnetpy-1.77.0.dist-info/RECORD,,