@@ -1811,7 +1811,7 @@ def process_meteomatics_atmosphere( # pylint: disable=too-many-statements
18111811 If credentials are missing, if no launch date is set, or if the API
18121812 returns no usable data.
18131813 """
1814- model = model if isinstance ( model , str ) else "mix"
1814+ model = model or "mix"
18151815 username = username or os .environ .get ("METEOMATICS_USERNAME" )
18161816 password = password or os .environ .get ("METEOMATICS_PASSWORD" )
18171817 if not username or not password :
@@ -1821,10 +1821,16 @@ def process_meteomatics_atmosphere( # pylint: disable=too-many-statements
18211821 "or set the METEOMATICS_USERNAME and METEOMATICS_PASSWORD "
18221822 "environment variables."
18231823 )
1824- if getattr (self , "datetime_date" , None ) is None :
1825- raise ValueError (
1826- "A launch date is required to use the Meteomatics atmospheric "
1827- "model. Provide it when creating the Environment or via set_date()."
1824+ self .__validate_datetime ()
1825+
1826+ if self .elevation == 0 :
1827+ warnings .warn (
1828+ "The Environment elevation is 0 m (possibly unset), so Meteomatics "
1829+ "heights above ground level are being treated as heights above sea "
1830+ "level. Set the elevation before this call if the launch site is "
1831+ "not at sea level." ,
1832+ UserWarning ,
1833+ stacklevel = 2 ,
18281834 )
18291835
18301836 profiles = fetch_atmospheric_data_from_meteomatics (
@@ -1841,39 +1847,34 @@ def process_meteomatics_atmosphere( # pylint: disable=too-many-statements
18411847 query_limit = query_limit ,
18421848 )
18431849
1844- if self . elevation == 0 :
1845- warnings . warn (
1846- "The Environment elevation is 0 m (possibly unset), so "
1847- "Meteomatics heights above ground level are being treated as "
1848- "heights above sea level. If the launch site is not at sea "
1849- "level, set the elevation (e.g. Environment(elevation=...) or "
1850- "set_elevation('Open-Elevation')) before this call for an "
1851- "accurate profile." ,
1852- UserWarning ,
1853- stacklevel = 2 ,
1850+ def to_profile_array ( * names ) :
1851+ """Convert {AGL height: value} mappings into a sorted array whose
1852+ first column is the ASL height and the remaining ones the requested
1853+ profile values, keeping only the heights that carry a value in
1854+ every mapping."" "
1855+ common_heights = set . intersection ( * ( set ( profiles [ n ]) for n in names ))
1856+ heights = sorted (
1857+ h
1858+ for h in common_heights
1859+ if all ( profiles [ n ][ h ] is not None for n in names )
18541860 )
1855-
1856- def to_profile_array (profile ):
1857- """Convert an {AGL height: value} mapping into a sorted
1858- (ASL height, value) array, dropping any missing value."""
1859- heights = sorted (h for h , value in profile .items () if value is not None )
18601861 return np .array (
1861- [(h + self .elevation , profile [h ]) for h in heights ], dtype = float
1862+ [
1863+ (h + self .elevation , * (profiles [n ][h ] for n in names ))
1864+ for h in heights
1865+ ],
1866+ dtype = float ,
18621867 )
18631868
1864- pressure_array = to_profile_array (profiles ["pressure" ])
1865- temperature_array = to_profile_array (profiles ["temperature" ])
1866-
1869+ pressure_array = to_profile_array ("pressure" )
1870+ temperature_array = to_profile_array ("temperature" )
18671871 # Wind u and v share the same altitude grid; keep only common levels.
1868- wind_heights = sorted (
1869- h
1870- for h in set (profiles ["wind_u" ]) & set (profiles ["wind_v" ])
1871- if profiles ["wind_u" ][h ] is not None and profiles ["wind_v" ][h ] is not None
1872- )
1872+ wind_array = to_profile_array ("wind_u" , "wind_v" )
1873+
18731874 # Each profile needs at least two levels: a single-point Function cannot
18741875 # be evaluated at its own node (it raises IndexError downstream), so a
18751876 # collapsed grid must fail here with an actionable message instead.
1876- if min (len (pressure_array ), len (temperature_array ), len (wind_heights )) < 2 :
1877+ if min (len (pressure_array ), len (temperature_array ), len (wind_array )) < 2 :
18771878 raise ValueError (
18781879 "Meteomatics did not return enough usable atmospheric data: at "
18791880 "least two valid altitude levels are required for pressure, "
@@ -1883,11 +1884,11 @@ def to_profile_array(profile):
18831884 "and your account permissions."
18841885 )
18851886
1886- wind_u_values = np . array ([ profiles [ "wind_u" ][ h ] for h in wind_heights ])
1887- wind_v_values = np . array ([ profiles [ "wind_v" ][ h ] for h in wind_heights ])
1888- wind_asl_heights = np . array ( wind_heights , dtype = float ) + self . elevation
1889- wind_u_array = np . column_stack (( wind_asl_heights , wind_u_values ))
1890- wind_v_array = np . column_stack (( wind_asl_heights , wind_v_values ))
1887+ wind_asl_heights = wind_array [:, 0 ]
1888+ wind_u_values = wind_array [:, 1 ]
1889+ wind_v_values = wind_array [:, 2 ]
1890+ wind_u_array = wind_array [:, ( 0 , 1 )]
1891+ wind_v_array = wind_array [:, ( 0 , 2 )]
18911892
18921893 wind_speed_array = calculate_wind_speed (wind_u_values , wind_v_values )
18931894 wind_heading_array = calculate_wind_heading (wind_u_values , wind_v_values )
@@ -3181,8 +3182,11 @@ def from_dict(cls, data): # pylint: disable=too-many-statements
31813182 )
31823183 atmospheric_model = data ["atmospheric_model_type" ]
31833184 env .atmospheric_model_type = atmospheric_model
3185+ # set_atmospheric_model stores the type as the user spelled it (e.g.
3186+ # "Meteomatics"), so the dispatch below must be case-insensitive.
3187+ model_type = atmospheric_model .lower ()
31843188
3185- match atmospheric_model :
3189+ match model_type :
31863190 case "standard_atmosphere" :
31873191 env .set_atmospheric_model ("standard_atmosphere" )
31883192 case "custom_atmosphere" :
@@ -3204,13 +3208,7 @@ def from_dict(cls, data): # pylint: disable=too-many-statements
32043208 env .elevation = data ["elevation" ]
32053209 env .max_expected_height = data ["max_expected_height" ]
32063210
3207- if isinstance (atmospheric_model , str ) and atmospheric_model .lower () in (
3208- "windy" ,
3209- "meteomatics" ,
3210- "forecast" ,
3211- "reanalysis" ,
3212- "ensemble" ,
3213- ):
3211+ if model_type in ("windy" , "meteomatics" , "forecast" , "reanalysis" , "ensemble" ):
32143212 env .atmospheric_model_init_date = data ["atmospheric_model_init_date" ]
32153213 env .atmospheric_model_end_date = data ["atmospheric_model_end_date" ]
32163214 env .atmospheric_model_interval = data ["atmospheric_model_interval" ]
@@ -3219,7 +3217,7 @@ def from_dict(cls, data): # pylint: disable=too-many-statements
32193217 env .atmospheric_model_init_lon = data ["atmospheric_model_init_lon" ]
32203218 env .atmospheric_model_end_lon = data ["atmospheric_model_end_lon" ]
32213219
3222- if atmospheric_model == "ensemble" :
3220+ if model_type == "ensemble" :
32233221 env .level_ensemble = data ["level_ensemble" ]
32243222 env .height_ensemble = data ["height_ensemble" ]
32253223 env .temperature_ensemble = data ["temperature_ensemble" ]
0 commit comments