pycontrails 0.54.2__cp313-cp313-win_amd64.whl → 0.54.4__cp313-cp313-win_amd64.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.

Potentially problematic release.


This version of pycontrails might be problematic. Click here for more details.

Files changed (68) hide show
  1. pycontrails/__init__.py +2 -2
  2. pycontrails/_version.py +2 -2
  3. pycontrails/core/__init__.py +1 -1
  4. pycontrails/core/aircraft_performance.py +75 -61
  5. pycontrails/core/cache.py +7 -7
  6. pycontrails/core/fleet.py +25 -21
  7. pycontrails/core/flight.py +215 -301
  8. pycontrails/core/interpolation.py +56 -56
  9. pycontrails/core/met.py +48 -39
  10. pycontrails/core/models.py +25 -11
  11. pycontrails/core/polygon.py +15 -15
  12. pycontrails/core/rgi_cython.cp313-win_amd64.pyd +0 -0
  13. pycontrails/core/vector.py +22 -22
  14. pycontrails/datalib/_met_utils/metsource.py +8 -5
  15. pycontrails/datalib/ecmwf/__init__.py +14 -14
  16. pycontrails/datalib/ecmwf/common.py +1 -1
  17. pycontrails/datalib/ecmwf/era5.py +7 -7
  18. pycontrails/datalib/ecmwf/hres.py +3 -3
  19. pycontrails/datalib/ecmwf/ifs.py +1 -1
  20. pycontrails/datalib/ecmwf/variables.py +1 -0
  21. pycontrails/datalib/gfs/__init__.py +6 -6
  22. pycontrails/datalib/gfs/gfs.py +2 -2
  23. pycontrails/datalib/goes.py +5 -5
  24. pycontrails/datalib/landsat.py +5 -8
  25. pycontrails/datalib/sentinel.py +7 -11
  26. pycontrails/ext/bada.py +3 -2
  27. pycontrails/ext/empirical_grid.py +1 -1
  28. pycontrails/ext/synthetic_flight.py +3 -2
  29. pycontrails/models/accf.py +40 -19
  30. pycontrails/models/apcemm/apcemm.py +5 -4
  31. pycontrails/models/cocip/__init__.py +2 -2
  32. pycontrails/models/cocip/cocip.py +16 -17
  33. pycontrails/models/cocip/cocip_params.py +2 -11
  34. pycontrails/models/cocip/cocip_uncertainty.py +24 -18
  35. pycontrails/models/cocip/contrail_properties.py +331 -316
  36. pycontrails/models/cocip/output_formats.py +53 -53
  37. pycontrails/models/cocip/radiative_forcing.py +135 -131
  38. pycontrails/models/cocip/radiative_heating.py +135 -135
  39. pycontrails/models/cocip/unterstrasser_wake_vortex.py +90 -87
  40. pycontrails/models/cocip/wake_vortex.py +92 -92
  41. pycontrails/models/cocip/wind_shear.py +8 -8
  42. pycontrails/models/cocipgrid/cocip_grid.py +118 -107
  43. pycontrails/models/dry_advection.py +59 -58
  44. pycontrails/models/emissions/__init__.py +2 -2
  45. pycontrails/models/emissions/black_carbon.py +108 -108
  46. pycontrails/models/emissions/emissions.py +85 -85
  47. pycontrails/models/emissions/ffm2.py +35 -35
  48. pycontrails/models/humidity_scaling/humidity_scaling.py +23 -23
  49. pycontrails/models/ps_model/__init__.py +3 -2
  50. pycontrails/models/ps_model/ps_aircraft_params.py +11 -6
  51. pycontrails/models/ps_model/ps_grid.py +256 -60
  52. pycontrails/models/ps_model/ps_model.py +18 -21
  53. pycontrails/models/ps_model/ps_operational_limits.py +58 -69
  54. pycontrails/models/tau_cirrus.py +8 -1
  55. pycontrails/physics/geo.py +216 -67
  56. pycontrails/physics/jet.py +220 -90
  57. pycontrails/physics/static/iata-cargo-load-factors-20241115.csv +71 -0
  58. pycontrails/physics/static/iata-passenger-load-factors-20241115.csv +71 -0
  59. pycontrails/physics/units.py +14 -14
  60. pycontrails/utils/json.py +1 -2
  61. pycontrails/utils/types.py +12 -7
  62. {pycontrails-0.54.2.dist-info → pycontrails-0.54.4.dist-info}/METADATA +10 -10
  63. {pycontrails-0.54.2.dist-info → pycontrails-0.54.4.dist-info}/NOTICE +1 -1
  64. pycontrails-0.54.4.dist-info/RECORD +111 -0
  65. {pycontrails-0.54.2.dist-info → pycontrails-0.54.4.dist-info}/WHEEL +1 -1
  66. pycontrails-0.54.2.dist-info/RECORD +0 -109
  67. {pycontrails-0.54.2.dist-info → pycontrails-0.54.4.dist-info}/LICENSE +0 -0
  68. {pycontrails-0.54.2.dist-info → pycontrails-0.54.4.dist-info}/top_level.txt +0 -0
@@ -62,20 +62,20 @@ def haversine(lons0: ArrayLike, lats0: ArrayLike, lons1: ArrayLike, lats1: Array
62
62
 
63
63
 
64
64
  def segment_haversine(
65
- longitude: npt.NDArray[np.float64], latitude: npt.NDArray[np.float64]
66
- ) -> npt.NDArray[np.float64]:
65
+ longitude: npt.NDArray[np.floating], latitude: npt.NDArray[np.floating]
66
+ ) -> npt.NDArray[np.floating]:
67
67
  r"""Calculate haversine distance between consecutive points along path.
68
68
 
69
69
  Parameters
70
70
  ----------
71
- longitude : npt.NDArray[np.float64]
71
+ longitude : npt.NDArray[np.floating]
72
72
  1D Longitude values with index corresponding to latitude inputs, [:math:`\deg`]
73
- latitude : npt.NDArray[np.float64]
73
+ latitude : npt.NDArray[np.floating]
74
74
  1D Latitude values with index corresponding to longitude inputs, [:math:`\deg`]
75
75
 
76
76
  Returns
77
77
  -------
78
- npt.NDArray[np.float64]
78
+ npt.NDArray[np.floating]
79
79
  Haversine distance between (lat_i, lon_i) and (lat_i+1, lon_i+1), [:math:`m`]
80
80
  The final entry of the output is set to nan.
81
81
 
@@ -97,8 +97,8 @@ def segment_haversine(
97
97
 
98
98
 
99
99
  def azimuth_to_direction(
100
- azimuth_: npt.NDArray[np.float64], latitude: npt.NDArray[np.float64]
101
- ) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]:
100
+ azimuth_: npt.NDArray[np.floating], latitude: npt.NDArray[np.floating]
101
+ ) -> tuple[npt.NDArray[np.floating], npt.NDArray[np.floating]]:
102
102
  r"""Calculate rectangular direction from spherical azimuth.
103
103
 
104
104
  This implementation uses the equation
@@ -109,14 +109,14 @@ def azimuth_to_direction(
109
109
 
110
110
  Parameters
111
111
  ----------
112
- azimuth_ : npt.NDArray[np.float64]
112
+ azimuth_ : npt.NDArray[np.floating]
113
113
  Angle measured clockwise from true north, [:math:`\deg`]
114
- latitude : npt.NDArray[np.float64]
114
+ latitude : npt.NDArray[np.floating]
115
115
  Latitude value of the point, [:math:`\deg`]
116
116
 
117
117
  Returns
118
118
  -------
119
- tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]
119
+ tuple[npt.NDArray[np.floating], npt.NDArray[np.floating]]
120
120
  A tuple of sine and cosine values.
121
121
  """
122
122
  cos_lat = np.cos(units.degrees_to_radians(latitude))
@@ -138,22 +138,22 @@ def azimuth_to_direction(
138
138
 
139
139
 
140
140
  def azimuth(
141
- lons0: npt.NDArray[np.float64],
142
- lats0: npt.NDArray[np.float64],
143
- lons1: npt.NDArray[np.float64],
144
- lats1: npt.NDArray[np.float64],
145
- ) -> npt.NDArray[np.float64]:
141
+ lons0: npt.NDArray[np.floating],
142
+ lats0: npt.NDArray[np.floating],
143
+ lons1: npt.NDArray[np.floating],
144
+ lats1: npt.NDArray[np.floating],
145
+ ) -> npt.NDArray[np.floating]:
146
146
  r"""Calculate angle relative to true north for set of coordinates.
147
147
 
148
148
  Parameters
149
149
  ----------
150
- lons0 : npt.NDArray[np.float64]
150
+ lons0 : npt.NDArray[np.floating]
151
151
  Longitude values of initial endpoints, [:math:`\deg`].
152
- lats0 : npt.NDArray[np.float64]
152
+ lats0 : npt.NDArray[np.floating]
153
153
  Latitude values of initial endpoints, [:math:`\deg`].
154
- lons1 : npt.NDArray[np.float64]
154
+ lons1 : npt.NDArray[np.floating]
155
155
  Longitude values of terminal endpoints, [:math:`\deg`].
156
- lats1 : npt.NDArray[np.float64]
156
+ lats1 : npt.NDArray[np.floating]
157
157
  Latitude values of terminal endpoints, [:math:`\deg`].
158
158
 
159
159
  References
@@ -162,7 +162,7 @@ def azimuth(
162
162
 
163
163
  Returns
164
164
  -------
165
- npt.NDArray[np.float64]
165
+ npt.NDArray[np.floating]
166
166
  Azimuth relative to true north (:math:`0\deg`), [:math:`\deg`]
167
167
 
168
168
  See Also
@@ -186,22 +186,22 @@ def azimuth(
186
186
 
187
187
 
188
188
  def segment_azimuth(
189
- longitude: npt.NDArray[np.float64], latitude: npt.NDArray[np.float64]
190
- ) -> npt.NDArray[np.float64]:
189
+ longitude: npt.NDArray[np.floating], latitude: npt.NDArray[np.floating]
190
+ ) -> npt.NDArray[np.floating]:
191
191
  r"""Calculate the angle between coordinate segments and true north.
192
192
 
193
193
  `np.nan` is added to the final value so the length of the output is the same as the inputs.
194
194
 
195
195
  Parameters
196
196
  ----------
197
- longitude : npt.NDArray[np.float64]
197
+ longitude : npt.NDArray[np.floating]
198
198
  Longitude values, [:math:`\deg`]
199
- latitude : npt.NDArray[np.float64]
199
+ latitude : npt.NDArray[np.floating]
200
200
  Latitude values, [:math:`\deg`]
201
201
 
202
202
  Returns
203
203
  -------
204
- npt.NDArray[np.float64]
204
+ npt.NDArray[np.floating]
205
205
  Azimuth relative to true north (:math:`0\deg`), [:math:`\deg`]
206
206
  Final entry of each array is set to `np.nan`.
207
207
 
@@ -227,22 +227,22 @@ def segment_azimuth(
227
227
 
228
228
 
229
229
  def longitudinal_angle(
230
- lons0: npt.NDArray[np.float64],
231
- lats0: npt.NDArray[np.float64],
232
- lons1: npt.NDArray[np.float64],
233
- lats1: npt.NDArray[np.float64],
234
- ) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]:
230
+ lons0: npt.NDArray[np.floating],
231
+ lats0: npt.NDArray[np.floating],
232
+ lons1: npt.NDArray[np.floating],
233
+ lats1: npt.NDArray[np.floating],
234
+ ) -> tuple[npt.NDArray[np.floating], npt.NDArray[np.floating]]:
235
235
  r"""Calculate angle with longitudinal axis for sequence of segments.
236
236
 
237
237
  Parameters
238
238
  ----------
239
- lons0 : npt.NDArray[np.float64]
239
+ lons0 : npt.NDArray[np.floating]
240
240
  Longitude values of initial endpoints, [:math:`\deg`].
241
- lats0 : npt.NDArray[np.float64]
241
+ lats0 : npt.NDArray[np.floating]
242
242
  Latitude values of initial endpoints, [:math:`\deg`].
243
- lons1 : npt.NDArray[np.float64]
243
+ lons1 : npt.NDArray[np.floating]
244
244
  Longitude values of terminal endpoints, [:math:`\deg`].
245
- lats1 : npt.NDArray[np.float64]
245
+ lats1 : npt.NDArray[np.floating]
246
246
  Latitude values of terminal endpoints, [:math:`\deg`].
247
247
 
248
248
  References
@@ -251,9 +251,9 @@ def longitudinal_angle(
251
251
 
252
252
  Returns
253
253
  -------
254
- sin_a : npt.NDArray[np.float64]
254
+ sin_a : npt.NDArray[np.floating]
255
255
  Sine values.
256
- cos_a : npt.NDArray[np.float64]
256
+ cos_a : npt.NDArray[np.floating]
257
257
  Cosine values.
258
258
  """
259
259
  lons0 = units.degrees_to_radians(lons0)
@@ -275,22 +275,22 @@ def longitudinal_angle(
275
275
 
276
276
 
277
277
  def segment_angle(
278
- longitude: npt.NDArray[np.float64], latitude: npt.NDArray[np.float64]
279
- ) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]:
278
+ longitude: npt.NDArray[np.floating], latitude: npt.NDArray[np.floating]
279
+ ) -> tuple[npt.NDArray[np.floating], npt.NDArray[np.floating]]:
280
280
  r"""Calculate the angle between coordinate segments and the longitudinal axis.
281
281
 
282
282
  `np.nan` is added to the final value so the length of the output is the same as the inputs.
283
283
 
284
284
  Parameters
285
285
  ----------
286
- longitude : npt.NDArray[np.float64]
286
+ longitude : npt.NDArray[np.floating]
287
287
  Longitude values, [:math:`\deg`]
288
- latitude : npt.NDArray[np.float64]
288
+ latitude : npt.NDArray[np.floating]
289
289
  Latitude values, [:math:`\deg`]
290
290
 
291
291
  Returns
292
292
  -------
293
- tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]
293
+ tuple[npt.NDArray[np.floating], npt.NDArray[np.floating]]
294
294
  sin(a), cos(a), where ``a`` is the angle between the segment and the longitudinal axis.
295
295
  Final entry of each array is set to `np.nan`.
296
296
 
@@ -332,10 +332,10 @@ def segment_angle(
332
332
 
333
333
 
334
334
  def segment_length(
335
- longitude: npt.NDArray[np.float64],
336
- latitude: npt.NDArray[np.float64],
337
- altitude: npt.NDArray[np.float64],
338
- ) -> npt.NDArray[np.float64]:
335
+ longitude: npt.NDArray[np.floating],
336
+ latitude: npt.NDArray[np.floating],
337
+ altitude: npt.NDArray[np.floating],
338
+ ) -> npt.NDArray[np.floating]:
339
339
  r"""Calculate the segment length between coordinates by assuming a great circle distance.
340
340
 
341
341
  Requires coordinates to be in EPSG:4326.
@@ -345,16 +345,16 @@ def segment_length(
345
345
 
346
346
  Parameters
347
347
  ----------
348
- longitude : npt.NDArray[np.float64]
348
+ longitude : npt.NDArray[np.floating]
349
349
  Longitude values, [:math:`\deg`]
350
- latitude : npt.NDArray[np.float64]
350
+ latitude : npt.NDArray[np.floating]
351
351
  Latitude values, [:math:`\deg`]
352
- altitude : npt.NDArray[np.float64]
352
+ altitude : npt.NDArray[np.floating]
353
353
  Altitude values, [:math:`m`]
354
354
 
355
355
  Returns
356
356
  -------
357
- npt.NDArray[np.float64]
357
+ npt.NDArray[np.floating]
358
358
  Array of distances in [:math:`m`] between coordinates.
359
359
  Final entry of each array is set to `np.nan`.
360
360
 
@@ -371,11 +371,11 @@ def segment_length(
371
371
 
372
372
 
373
373
  def forward_azimuth(
374
- lons: npt.NDArray[np.float64],
375
- lats: npt.NDArray[np.float64],
376
- az: npt.NDArray[np.float64] | float,
377
- dist: npt.NDArray[np.float64] | float,
378
- ) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]:
374
+ lons: npt.NDArray[np.floating],
375
+ lats: npt.NDArray[np.floating],
376
+ az: npt.NDArray[np.floating] | float,
377
+ dist: npt.NDArray[np.floating] | float,
378
+ ) -> tuple[npt.NDArray[np.floating], npt.NDArray[np.floating]]:
379
379
  r"""Calculate coordinates along forward azimuth.
380
380
 
381
381
  This function is identical to the `pyproj.Geod.fwd` method when working on
@@ -384,19 +384,19 @@ def forward_azimuth(
384
384
 
385
385
  Parameters
386
386
  ----------
387
- lons : npt.NDArray[np.float64]
387
+ lons : npt.NDArray[np.floating]
388
388
  Array of longitude values.
389
- lats : npt.NDArray[np.float64]
389
+ lats : npt.NDArray[np.floating]
390
390
  Array of latitude values.
391
- az : npt.NDArray[np.float64] | float
391
+ az : npt.NDArray[np.floating] | float
392
392
  Azimuth, measured in [:math:`\deg`].
393
- dist : npt.NDArray[np.float64] | float
393
+ dist : npt.NDArray[np.floating] | float
394
394
  Distance [:math:`m`] between initial longitude latitude values and
395
395
  point to be computed.
396
396
 
397
397
  Returns
398
398
  -------
399
- tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]
399
+ tuple[npt.NDArray[np.floating], npt.NDArray[np.floating]]
400
400
  Tuple of longitude latitude arrays.
401
401
 
402
402
  See Also
@@ -855,13 +855,162 @@ def advect_level(
855
855
  return (level * 100.0 + (dt_s * dp_dt)) / 100.0
856
856
 
857
857
 
858
+ def advect_longitude_and_latitude_near_poles(
859
+ longitude: npt.NDArray[np.floating],
860
+ latitude: npt.NDArray[np.floating],
861
+ u_wind: npt.NDArray[np.floating],
862
+ v_wind: npt.NDArray[np.floating],
863
+ dt: npt.NDArray[np.timedelta64] | np.timedelta64,
864
+ ) -> tuple[npt.NDArray[np.floating], npt.NDArray[np.floating]]:
865
+ r"""Advect a particle near the poles.
866
+
867
+ This function calculates the longitude and latitude of a particle after time ``dt``
868
+ caused by advection due to wind near the poles (above 80 degrees North and South).
869
+
870
+ Automatically wrap over the antimeridian if necessary.
871
+
872
+ Parameters
873
+ ----------
874
+ longitude : npt.NDArray[np.floating]
875
+ Original longitude, [:math:`\deg`]
876
+ latitude : npt.NDArray[np.floating]
877
+ Original latitude, [:math:`\deg`]
878
+ u_wind : npt.NDArray[np.floating]
879
+ Wind speed in the longitudinal direction, [:math:`m s^{-1}`]
880
+ v_wind : npt.NDArray[np.floating]
881
+ Wind speed in the latitudinal direction, [:math:`m s^{-1}`]
882
+ dt : npt.NDArray[np.timedelta64] | np.timedelta64
883
+ Advection timestep
884
+
885
+ Returns
886
+ -------
887
+ tuple[npt.NDArray[np.floating], npt.NDArray[np.floating]]
888
+ New longitude and latitude values, [:math:`\deg`]
889
+
890
+ Notes
891
+ -----
892
+ Near the poles, the longitude and latitude is converted to a 2-D Cartesian-like coordinate
893
+ system to avoid numerical instabilities and singularities caused by convergence of meridians.
894
+
895
+ See Also
896
+ --------
897
+ advect_longitude
898
+ advect_latitude
899
+ advect_horizontal
900
+ """
901
+ # Determine hemisphere sign (1 for Northern Hemisphere, -1 for Southern Hemisphere)
902
+ hemisphere_sign = np.where(latitude > 0.0, 1.0, -1.0)
903
+
904
+ # Convert longitude and latitude to radians
905
+ sin_lon_rad = np.sin(units.degrees_to_radians(longitude))
906
+ cos_lon_rad = np.cos(units.degrees_to_radians(longitude))
907
+
908
+ # Convert longitude and latitude to 2-D Cartesian-like coordinate system, [:math:`\deg`]
909
+ polar_radius = 90.0 - np.abs(latitude)
910
+ x_cartesian = sin_lon_rad * polar_radius
911
+ y_cartesian = -cos_lon_rad * polar_radius * hemisphere_sign
912
+
913
+ # Convert winds from eastward and northward direction (u, v) to (X, Y), [:math:`\deg s^{-1}`]
914
+ x_wind = units.radians_to_degrees(
915
+ (u_wind * cos_lon_rad - v_wind * sin_lon_rad * hemisphere_sign) / constants.radius_earth
916
+ )
917
+ y_wind = units.radians_to_degrees(
918
+ (u_wind * sin_lon_rad * hemisphere_sign + v_wind * cos_lon_rad) / constants.radius_earth
919
+ )
920
+
921
+ # Advect contrails in 2-D Cartesian-like plane, [:math:`\deg`]
922
+ dtype = np.result_type(latitude, v_wind)
923
+ dt_s = units.dt_to_seconds(dt, dtype)
924
+ x_cartesian_new = x_cartesian + dt_s * x_wind
925
+ y_cartesian_new = y_cartesian + dt_s * y_wind
926
+
927
+ # Convert `y_cartesian_new` back to `latitude`, [:math:`\deg`]
928
+ dist_squared = x_cartesian_new**2 + y_cartesian_new**2
929
+ new_latitude = (90.0 - np.sqrt(dist_squared)) * hemisphere_sign
930
+
931
+ # Convert `x_cartesian_new` back to `longitude`, [:math:`\deg`]
932
+ new_lon_rad = np.arctan2(y_cartesian_new, x_cartesian_new)
933
+
934
+ new_longitude = np.where(
935
+ (x_wind == 0.0) & (y_wind == 0.0),
936
+ longitude,
937
+ 90.0 + units.radians_to_degrees(new_lon_rad) * hemisphere_sign,
938
+ )
939
+ # new_longitude = 90.0 + units.radians_to_degrees(new_lon_rad) * hemisphere_sign
940
+ new_longitude = (new_longitude + 180.0) % 360.0 - 180.0 # wrap antimeridian
941
+ return new_longitude, new_latitude
942
+
943
+
944
+ def advect_horizontal(
945
+ longitude: npt.NDArray[np.floating],
946
+ latitude: npt.NDArray[np.floating],
947
+ u_wind: npt.NDArray[np.floating],
948
+ v_wind: npt.NDArray[np.floating],
949
+ dt: npt.NDArray[np.timedelta64] | np.timedelta64,
950
+ ) -> tuple[npt.NDArray[np.floating], npt.NDArray[np.floating]]:
951
+ r"""Advect a particle in the horizontal plane.
952
+
953
+ This function calls :func:`advect_longitude` and :func:`advect_latitude` when
954
+ the position is far from the poles (<= 80.0 degrees). When the position is near
955
+ the poles (> 80.0 degrees), :func:`advect_longitude_and_latitude_near_poles`
956
+ is used instead.
957
+
958
+ Parameters
959
+ ----------
960
+ longitude : npt.NDArray[np.floating]
961
+ Original longitude, [:math:`\deg`]
962
+ latitude : npt.NDArray[np.floating]
963
+ Original latitude, [:math:`\deg`]
964
+ u_wind : npt.NDArray[np.floating]
965
+ Wind speed in the longitudinal direction, [:math:`m s^{-1}`]
966
+ v_wind : npt.NDArray[np.floating]
967
+ Wind speed in the latitudinal direction, [:math:`m s^{-1}`]
968
+ dt : npt.NDArray[np.timedelta64] | np.timedelta64
969
+ Advection timestep
970
+
971
+ Returns
972
+ -------
973
+ tuple[npt.NDArray[np.floating], npt.NDArray[np.floating]]
974
+ New longitude and latitude values, [:math:`\deg`]
975
+ """
976
+ near_poles = np.abs(latitude) > 80.0
977
+
978
+ longitude_out = np.empty_like(longitude)
979
+ latitude_out = np.empty_like(latitude)
980
+
981
+ # Use simple spherical advection if position is far from the poles (<= 80.0 degrees)
982
+ cond = ~near_poles
983
+ lon_cond = longitude[cond]
984
+ lat_cond = latitude[cond]
985
+ u_wind_cond = u_wind[cond]
986
+ v_wind_cond = v_wind[cond]
987
+ dt_cond = dt if isinstance(dt, np.timedelta64) else dt[cond]
988
+ longitude_out[cond] = advect_longitude(lon_cond, lat_cond, u_wind_cond, dt_cond)
989
+ latitude_out[cond] = advect_latitude(lat_cond, v_wind_cond, dt_cond)
990
+
991
+ # And use Cartesian-like advection if position is near the poles (> 80.0 degrees)
992
+ cond = near_poles
993
+ lon_cond = longitude[cond]
994
+ lat_cond = latitude[cond]
995
+ u_wind_cond = u_wind[cond]
996
+ v_wind_cond = v_wind[cond]
997
+ dt_cond = dt if isinstance(dt, np.timedelta64) else dt[cond]
998
+ lon_out_cond, lat_out_cond = advect_longitude_and_latitude_near_poles(
999
+ lon_cond, lat_cond, u_wind_cond, v_wind_cond, dt_cond
1000
+ )
1001
+ longitude_out[cond] = lon_out_cond
1002
+ latitude_out[cond] = lat_out_cond
1003
+
1004
+ return longitude_out, latitude_out
1005
+
1006
+
858
1007
  # ---------------
859
1008
  # Grid properties
860
1009
  # ---------------
861
1010
 
862
1011
 
863
1012
  def spatial_bounding_box(
864
- longitude: npt.NDArray[np.float64], latitude: npt.NDArray[np.float64], buffer: float = 1.0
1013
+ longitude: npt.NDArray[np.floating], latitude: npt.NDArray[np.floating], buffer: float = 1.0
865
1014
  ) -> tuple[float, float, float, float]:
866
1015
  r"""
867
1016
  Construct rectangular spatial bounding box from a set of waypoints.
@@ -924,17 +1073,17 @@ def domain_surface_area(
924
1073
 
925
1074
 
926
1075
  def grid_surface_area(
927
- longitude: npt.NDArray[np.float64], latitude: npt.NDArray[np.float64]
1076
+ longitude: npt.NDArray[np.floating], latitude: npt.NDArray[np.floating]
928
1077
  ) -> xr.DataArray:
929
1078
  r"""
930
1079
  Calculate surface area that is covered by each pixel in a longitude-latitude grid.
931
1080
 
932
1081
  Parameters
933
1082
  ----------
934
- longitude: npt.NDArray[np.float64]
1083
+ longitude: npt.NDArray[np.floating]
935
1084
  Longitude coordinates in a longitude-latitude grid, [:math:`\deg`].
936
1085
  Must be in ascending order.
937
- latitude: npt.NDArray[np.float64]
1086
+ latitude: npt.NDArray[np.floating]
938
1087
  Latitude coordinates in a longitude-latitude grid, [:math:`\deg`].
939
1088
  Must be in ascending order.
940
1089
 
@@ -970,19 +1119,19 @@ def grid_surface_area(
970
1119
 
971
1120
 
972
1121
  def _area_between_latitude_and_north_pole(
973
- latitude: npt.NDArray[np.float64],
974
- ) -> npt.NDArray[np.float64]:
1122
+ latitude: npt.NDArray[np.floating],
1123
+ ) -> npt.NDArray[np.floating]:
975
1124
  r"""
976
1125
  Calculate surface area from the provided latitude to the North Pole.
977
1126
 
978
1127
  Parameters
979
1128
  ----------
980
- latitude: npt.NDArray[np.float64]
1129
+ latitude: npt.NDArray[np.floating]
981
1130
  1D Latitude values with index corresponding to latitude inputs, [:math:`\deg`]
982
1131
 
983
1132
  Returns
984
1133
  -------
985
- npt.NDArray[np.float64]
1134
+ npt.NDArray[np.floating]
986
1135
  Surface area from latitude to North Pole, [:math:`m^{2}`]
987
1136
  """
988
1137
  lat_radians = units.degrees_to_radians(latitude)