Skip to content

Commit

Permalink
Updated BUFRVariables with scales and descriptions
Browse files Browse the repository at this point in the history
* Added detailed descriptions with references to the attributes in BUFRVariables
* Change the attribute order to align with the exported schema
* Changed variable roundings to align with the scales defined in the BUFR schemas:
  * Latitude and longitude is set to 5. Was 6
  * heightOfStationGroundAboveMeanSeaLevel is set to 1. Was 2
  * heightOfBarometerAboveMeanSeaLevel is set to to 1. Was 2
  * pressure is set to -1. Was 1. Note: The BUFRVariable unit is Pa and not hPA
  * airTemperature is set to 2. Was 1.
  * heightOfSensorAboveLocalGroundOrDeckOfMarinePlatformTempRH is set to 2. Was 4
  * heightOfSensorAboveLocalGroundOrDeckOfMarinePlatformWSPD is set to 2. Was 4
 * Added unit tests to test the roundings
* Updated existing unit tests to align with corrected precision
  • Loading branch information
ladsmund committed Jul 9, 2024
1 parent 9979847 commit 5fad06b
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 24 deletions.
81 changes: 68 additions & 13 deletions src/pypromice/postprocess/bufr_utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,28 +66,81 @@ class BUFRVariables:
"""

wmo_id: str
# Station type: "mobile" or "land"
# ===============================
# Fixed land station schema: https://vocabulary-manager.eumetsat.int/vocabularies/BUFR/WMO/32/TABLE_D/307080
# Mobile station schema: https://vocabulary-manager.eumetsat.int/vocabularies/BUFR/WMO/32/TABLE_D/307090

station_type: str

# WMO station identifier
# Land stations: https://vocabulary-manager.eumetsat.int/vocabularies/BUFR/WMO/32/TABLE_D/301090
# Mobile stations: https://vocabulary-manager.eumetsat.int/vocabularies/BUFR/WMO/32/TABLE_D/301092
# ======================================================================================================
wmo_id: str
timestamp: datetime.datetime
relativeHumidity: float = attrs.field(converter=round_converter(0))
airTemperature: float = attrs.field(converter=round_converter(1))
pressure: float = attrs.field(converter=round_converter(1))
windDirection: float = attrs.field(converter=round_converter(0))
windSpeed: float = attrs.field(converter=round_converter(1))
latitude: float = attrs.field(converter=round_converter(6))
longitude: float = attrs.field(converter=round_converter(6))

# https://vocabulary-manager.eumetsat.int/vocabularies/BUFR/WMO/32/TABLE_B/005001
# Scale: 5, unit: degrees
# TODO: Test if eccodes does the rounding as well. The rounding is was 6 which is larger that the scale.
latitude: float = attrs.field(converter=round_converter(5))
# https://vocabulary-manager.eumetsat.int/vocabularies/BUFR/WMO/32/TABLE_B/006001
# Scale: 5, unit: degrees
longitude: float = attrs.field(converter=round_converter(5))

# https://vocabulary-manager.eumetsat.int/vocabularies/BUFR/WMO/32/TABLE_B/007030
# Scale: 1, unit: m
heightOfStationGroundAboveMeanSeaLevel: float = attrs.field(
converter=round_converter(2)
converter=round_converter(1)
)
#
# https://vocabulary-manager.eumetsat.int/vocabularies/BUFR/WMO/32/TABLE_B/007031
# Scale: 1, unit: m
heightOfBarometerAboveMeanSeaLevel: float = attrs.field(
converter=round_converter(2),
converter=round_converter(1),
)

# Pressure information
# ====================
# Definition table: https://vocabulary-manager.eumetsat.int/vocabularies/BUFR/WMO/32/TABLE_D/302031
# https://vocabulary-manager.eumetsat.int/vocabularies/BUFR/WMO/32/TABLE_B/007004
# Scale: -1, unit: Pa
pressure: float = attrs.field(converter=round_converter(-1))
# There are two other pressure variables in the template: 302001 and 010062.

# Basic synoptic "instantaneous" data
# ===================================
# Definition table: https://vocabulary-manager.eumetsat.int/vocabularies/BUFR/WMO/32/TABLE_D/302035
# This section only include the temperature and humidity data (302032).
# Precipitation and cloud data are currently ignored.
# https://vocabulary-manager.eumetsat.int/vocabularies/BUFR/WMO/32/TABLE_B/007032
# Scale: 2, unit: m
# This is the first appearance of this variable id.
heightOfSensorAboveLocalGroundOrDeckOfMarinePlatformTempRH: float = attrs.field(
converter=round_converter(4),
converter=round_converter(2),
)
# https://vocabulary-manager.eumetsat.int/vocabularies/BUFR/WMO/32/TABLE_B/012101
# Scale: 2, unit: K
airTemperature: float = attrs.field(converter=round_converter(2))
# There is also a Dewpoint temperature in this template: 012103 which is currently unused.
# https://vocabulary-manager.eumetsat.int/vocabularies/BUFR/WMO/32/TABLE_B/012103
# Scale: 0, unit: %
relativeHumidity: float = attrs.field(converter=round_converter(0))

# Basic synoptic "period" data
# ============================
# Definition table: https://vocabulary-manager.eumetsat.int/vocabularies/BUFR/WMO/32/TABLE_D/302043
# Wind data: https://vocabulary-manager.eumetsat.int/vocabularies/BUFR/WMO/32/TABLE_D/302042
# Wind direction: https://vocabulary-manager.eumetsat.int/vocabularies/BUFR/WMO/32/TABLE_B/011001
# Scale: 0, unit: degrees
windDirection: float = attrs.field(converter=round_converter(0))
# Wind speed: https://vocabulary-manager.eumetsat.int/vocabularies/BUFR/WMO/32/TABLE_B/011002
# Scale: 1, unit: m/s
windSpeed: float = attrs.field(converter=round_converter(1))
# https://vocabulary-manager.eumetsat.int/vocabularies/BUFR/WMO/32/TABLE_B/007032
# Scale: 2, unit: m
# This is the 7th appearance of this variable id.
heightOfSensorAboveLocalGroundOrDeckOfMarinePlatformWSPD: float = attrs.field(
converter=round_converter(4)
converter=round_converter(2)
)

def as_series(self) -> pd.Series:
Expand Down Expand Up @@ -131,6 +184,7 @@ def __eq__(self, other: "BUFRVariables"):

BUFR_TEMPLATES = {
"mobile": {
# Template definition: https://vocabulary-manager.eumetsat.int/vocabularies/BUFR/WMO/32/TABLE_D/307090
"unexpandedDescriptors": (307090), # message template, "synopMobil"
"edition": 4, # latest edition
"masterTableNumber": 0,
Expand All @@ -146,6 +200,7 @@ def __eq__(self, other: "BUFRVariables"):
"compressedData": 0,
},
"land": {
# Template definition: https://vocabulary-manager.eumetsat.int/vocabularies/BUFR/WMO/32/TABLE_D/307080
"unexpandedDescriptors": (307080), # message template, "synopLand"
"edition": 4, # latest edition
"masterTableNumber": 0,
Expand Down
34 changes: 34 additions & 0 deletions tests/unit/bufr_export/test_bufr_utilitites.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,3 +181,37 @@ def test_nan_value_serialization(self):
variables_src,
variables_read,
)

def test_precision(self):
"""
Test if the BUFRVariable rounding configurations aligns with the BUFR format.
Use np.random.random() to generate high precision random values.
"""
variables_src = BUFRVariables(
wmo_id="04464",
station_type="mobile",
timestamp=datetime.datetime(2023, 12, 19, 10, 0),
relativeHumidity=np.random.random(),
airTemperature=np.random.random(),
pressure=1000*np.random.random(),
windDirection=np.random.random(),
windSpeed=np.random.random(),
latitude=np.random.random(),
longitude=np.random.random(),
heightOfStationGroundAboveMeanSeaLevel=np.random.random(),
heightOfBarometerAboveMeanSeaLevel=np.random.random(),
heightOfSensorAboveLocalGroundOrDeckOfMarinePlatformTempRH=np.random.random(),
heightOfSensorAboveLocalGroundOrDeckOfMarinePlatformWSPD=np.random.random(),
)
with tempfile.TemporaryFile("w+b") as fp:
write_bufr_message(variables=variables_src, file=fp)
fp.seek(0)
variables_read = read_bufr_message(
fp=fp,
)

self.assertEqual(
variables_src,
variables_read,
)
2 changes: 1 addition & 1 deletion tests/unit/bufr_export/test_create_bufr_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def tearDown(self):

def test_create_bufr_files(self):
"""
Teste the creation of bufr files and their output folder structure.
Test the creation of bufr files and their output folder structure.
It does not test the content of the bufr files.
"""
input_dir = self.temp_dir / "input"
Expand Down
8 changes: 4 additions & 4 deletions tests/unit/bufr_export/test_get_bufr.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def test_bufr_variables_gcnet(self):
stid=station_configuration.stid,
station_configuration=station_configuration,
relativeHumidity=69.0,
airTemperature=256.0,
airTemperature=255.95,
pressure=77300.0,
windDirection=149.0,
windSpeed=14.9,
Expand Down Expand Up @@ -84,7 +84,7 @@ def test_bufr_variables_promice_v2(self):
stid=station_configuration.stid,
station_configuration=station_configuration,
relativeHumidity=69.0,
airTemperature=256.0,
airTemperature=255.95,
pressure=77300.0,
windDirection=149.0,
windSpeed=14.9,
Expand Down Expand Up @@ -114,7 +114,7 @@ def test_bufr_variables_promice_v3(self):
stid=station_configuration.stid,
station_configuration=station_configuration,
relativeHumidity=69.0,
airTemperature=256.0,
airTemperature=255.95,
pressure=77300.0,
windDirection=149.0,
windSpeed=14.9,
Expand Down Expand Up @@ -165,7 +165,7 @@ def test_bufr_variables_static_gps_elevation(self):
station_type=station_config.station_type,
timestamp=timestamp,
relativeHumidity=1.0,
airTemperature=252.2, # Converted to kelvin
airTemperature=252.15, # Converted to kelvin
pressure=199300.0,
windDirection=32.0,
windSpeed=5.3,
Expand Down
12 changes: 6 additions & 6 deletions tests/unit/bufr_export/test_get_bufr_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def test_get_bufr_has_new_data(self):
# Newest measurement in tx_l3_test1.csv: 2023-12-07 23:00:00
timestamp=datetime.datetime(2023, 12, 7, 23, 00),
relativeHumidity=69,
airTemperature=256.0,
airTemperature=255.95,
pressure=77300.0,
windDirection=149,
windSpeed=14.9,
Expand Down Expand Up @@ -180,7 +180,7 @@ def test_get_bufr_has_new_data_dont_store_position(self):
# Newest measurement in tx_l3_test1.csv: 2023-12-07 23:00:00
timestamp=datetime.datetime(2023, 12, 7, 23, 00),
relativeHumidity=69,
airTemperature=256.0,
airTemperature=255.95,
pressure=77300.0,
windDirection=149,
windSpeed=14.9,
Expand Down Expand Up @@ -260,7 +260,7 @@ def test_get_bufr_includes_datasets_not_in_latests_timestamps(self):
# Newest measurement in tx_l3_test1.csv: 2023-12-07 23:00:00
timestamp=datetime.datetime(2023, 12, 7, 23, 00),
relativeHumidity=69,
airTemperature=256.0,
airTemperature=255.95,
pressure=77300.0,
windDirection=149,
windSpeed=14.9,
Expand Down Expand Up @@ -321,7 +321,7 @@ def test_invalid_value_at_last_index(self):
# Newest measurement in tx_l3_test1.csv: 2023-12-07 23:00:00
timestamp=datetime.datetime(2023, 12, 7, 23, 00),
relativeHumidity=69,
airTemperature=256.0,
airTemperature=255.95,
pressure=np.nan,
windDirection=149,
windSpeed=14.9,
Expand Down Expand Up @@ -459,7 +459,7 @@ def test_ignore_newer_data_than_now_input(self):
# Newest measurement in tx_l3_test1.csv: 2023-12-07 23:00:00 but now_timestamp is 2023-12-06
timestamp=datetime.datetime(2023, 12, 6, 0, 0),
relativeHumidity=82,
airTemperature=250.8,
airTemperature=250.85,
pressure=77370.0,
windDirection=153,
windSpeed=10.4,
Expand Down Expand Up @@ -500,7 +500,7 @@ def test_land_station_export(self):
# Newest measurement in tx_l3_test1.csv: 2023-12-07 23:00:00
timestamp=datetime.datetime(2023, 12, 7, 23, 00),
relativeHumidity=69,
airTemperature=256.0,
airTemperature=255.95,
pressure=77300.0,
windDirection=149,
windSpeed=14.9,
Expand Down

0 comments on commit 5fad06b

Please sign in to comment.