|
14 | 14 | geodesic_to_utm, |
15 | 15 | get_final_date_from_time_array, |
16 | 16 | get_initial_date_from_time_array, |
| 17 | + get_pressure_levels_from_file, |
| 18 | + pressure_unit_to_factor, |
17 | 19 | utm_to_geodesic, |
18 | 20 | ) |
19 | 21 | from rocketpy.environment.weather_model_mapping import WeatherModelMapping |
@@ -378,14 +380,21 @@ def test_environment_to_dict_from_dict_round_trip_preserves_weather_metadata( |
378 | 380 | ensemble_metadata.update( |
379 | 381 | { |
380 | 382 | "level_ensemble": np.array([1000.0, 900.0]), |
381 | | - "height_ensemble": np.array([[0.0, 1000.0]]), |
382 | | - "temperature_ensemble": np.array([[288.15, 281.15]]), |
383 | | - "wind_u_ensemble": np.array([[2.0, 3.0]]), |
384 | | - "wind_v_ensemble": np.array([[4.0, 5.0]]), |
385 | | - "wind_heading_ensemble": np.array([[26.565051, 30.963757]]), |
386 | | - "wind_direction_ensemble": np.array([[206.565051, 210.963757]]), |
387 | | - "wind_speed_ensemble": np.array([[4.472136, 5.830952]]), |
388 | | - "num_ensemble_members": 1, |
| 383 | + "height_ensemble": np.array([[0.0, 1000.0], [0.0, 1000.0]]), |
| 384 | + "temperature_ensemble": np.array([[288.15, 281.15], [288.15, 281.15]]), |
| 385 | + "wind_u_ensemble": np.array([[2.0, 3.0], [2.0, 3.0]]), |
| 386 | + "wind_v_ensemble": np.array([[4.0, 5.0], [4.0, 5.0]]), |
| 387 | + "wind_heading_ensemble": np.array( |
| 388 | + [[26.565051, 30.963757], [26.565051, 30.963757]] |
| 389 | + ), |
| 390 | + "wind_direction_ensemble": np.array( |
| 391 | + [[206.565051, 210.963757], [206.565051, 210.963757]] |
| 392 | + ), |
| 393 | + "wind_speed_ensemble": np.array( |
| 394 | + [[4.472136, 5.830952], [4.472136, 5.830952]] |
| 395 | + ), |
| 396 | + "num_ensemble_members": 2, |
| 397 | + "ensemble_member": 1, |
389 | 398 | } |
390 | 399 | ) |
391 | 400 |
|
@@ -416,6 +425,7 @@ def test_environment_to_dict_from_dict_round_trip_preserves_weather_metadata( |
416 | 425 | npt.assert_allclose(restored_env.level_ensemble, env.level_ensemble) |
417 | 426 | npt.assert_allclose(restored_env.height_ensemble, env.height_ensemble) |
418 | 427 | assert restored_env.num_ensemble_members == env.num_ensemble_members |
| 428 | + assert restored_env.ensemble_member == env.ensemble_member == 1 |
419 | 429 |
|
420 | 430 |
|
421 | 431 | class _DummyDataset: |
@@ -818,3 +828,91 @@ def test_pressure_conversion_factor_autodetect_by_model( |
818 | 828 | None, None, model |
819 | 829 | ) |
820 | 830 | assert factor == expected_factor |
| 831 | + |
| 832 | + |
| 833 | +@pytest.mark.parametrize( |
| 834 | + "model, expected_factor", |
| 835 | + [("GEFS", 100), ("HIRESW", 100), ("GFS", 1), ("AIGFS", 1)], |
| 836 | +) |
| 837 | +def test_pressure_conversion_factor_autodetect_by_dictionary( |
| 838 | + example_plain_env, model, expected_factor |
| 839 | +): |
| 840 | + """Model shortcuts arriving via ``dictionary`` (the realistic download |
| 841 | + path) must map to the same factor as when they arrive via ``file``.""" |
| 842 | + factor = example_plain_env._Environment__determine_pressure_conversion_factor( |
| 843 | + None, model, None |
| 844 | + ) |
| 845 | + assert factor == expected_factor |
| 846 | + |
| 847 | + |
| 848 | +@pytest.mark.parametrize( |
| 849 | + "units, expected_levels", |
| 850 | + [ |
| 851 | + ("mb", [100000.0, 85000.0]), |
| 852 | + ("millibar", [100000.0, 85000.0]), |
| 853 | + ("millibars", [100000.0, 85000.0]), |
| 854 | + ("hPa", [100000.0, 85000.0]), |
| 855 | + ("mbar", [100000.0, 85000.0]), |
| 856 | + ("Pa", [1000.0, 850.0]), |
| 857 | + ], |
| 858 | +) |
| 859 | +def test_get_pressure_levels_from_file_unit_synonyms(units, expected_levels): |
| 860 | + """hPa/millibar unit synonyms auto-scale by 100; Pa by 1.""" |
| 861 | + |
| 862 | + class _Var: |
| 863 | + def __init__(self, values, units): |
| 864 | + self._values = np.asarray(values) |
| 865 | + self.units = units |
| 866 | + |
| 867 | + def __getitem__(self, key): |
| 868 | + return self._values[key] |
| 869 | + |
| 870 | + class _DS: |
| 871 | + def __init__(self, var): |
| 872 | + self.variables = {"lev": var} |
| 873 | + |
| 874 | + dataset = _DS(_Var([1000.0, 850.0], units)) |
| 875 | + levels = get_pressure_levels_from_file(dataset, {"level": "lev"}, None) |
| 876 | + npt.assert_allclose(levels, expected_levels) |
| 877 | + |
| 878 | + |
| 879 | +@pytest.mark.parametrize( |
| 880 | + "unit, expected", |
| 881 | + [ |
| 882 | + ("mbar", 100), |
| 883 | + ("mb", 100), |
| 884 | + ("hPa", 100), |
| 885 | + ("millibar", 100), |
| 886 | + ("millibars", 100), |
| 887 | + ("hectopascal", 100), |
| 888 | + ("Pa", 1), |
| 889 | + ("pascal", 1), |
| 890 | + ("parsecs", None), |
| 891 | + ("", None), |
| 892 | + ], |
| 893 | +) |
| 894 | +def test_pressure_unit_to_factor(unit, expected): |
| 895 | + """The shared unit->factor helper: hPa synonyms ->100, Pa ->1, else None.""" |
| 896 | + |
| 897 | + assert pressure_unit_to_factor(unit) == expected |
| 898 | + |
| 899 | + |
| 900 | +@pytest.mark.parametrize("unit, expected", [("mb", 100), ("millibar", 100), ("Pa", 1)]) |
| 901 | +def test_pressure_conversion_factor_explicit_unit_synonyms( |
| 902 | + example_plain_env, unit, expected |
| 903 | +): |
| 904 | + """An explicit string ``pressure_conversion_factor`` accepts the same unit |
| 905 | + synonyms as file auto-detection (Copilot review consistency fix).""" |
| 906 | + factor = example_plain_env._Environment__determine_pressure_conversion_factor( |
| 907 | + unit, None, None |
| 908 | + ) |
| 909 | + assert factor == expected |
| 910 | + |
| 911 | + |
| 912 | +def test_set_atmospheric_model_rejects_unknown_pressure_unit(example_plain_env): |
| 913 | + """An unrecognized ``pressure_conversion_factor`` unit is rejected during |
| 914 | + validation, before any file access.""" |
| 915 | + with pytest.raises(ValueError, match="pressure_conversion_factor"): |
| 916 | + example_plain_env.set_atmospheric_model( |
| 917 | + type="Forecast", file="dummy", pressure_conversion_factor="parsecs" |
| 918 | + ) |
0 commit comments