pycontrails 0.54.3__cp313-cp313-macosx_10_13_x86_64.whl → 0.54.5__cp313-cp313-macosx_10_13_x86_64.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 (62) 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 +58 -58
  5. pycontrails/core/cache.py +7 -7
  6. pycontrails/core/fleet.py +54 -29
  7. pycontrails/core/flight.py +218 -301
  8. pycontrails/core/interpolation.py +63 -60
  9. pycontrails/core/met.py +193 -125
  10. pycontrails/core/models.py +27 -13
  11. pycontrails/core/polygon.py +15 -15
  12. pycontrails/core/rgi_cython.cpython-313-darwin.so +0 -0
  13. pycontrails/core/vector.py +119 -96
  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/gfs/__init__.py +6 -6
  21. pycontrails/datalib/gfs/gfs.py +2 -2
  22. pycontrails/datalib/goes.py +5 -5
  23. pycontrails/ext/empirical_grid.py +1 -1
  24. pycontrails/models/apcemm/apcemm.py +5 -5
  25. pycontrails/models/apcemm/utils.py +1 -1
  26. pycontrails/models/cocip/__init__.py +2 -2
  27. pycontrails/models/cocip/cocip.py +23 -24
  28. pycontrails/models/cocip/cocip_params.py +2 -11
  29. pycontrails/models/cocip/cocip_uncertainty.py +24 -18
  30. pycontrails/models/cocip/contrail_properties.py +331 -316
  31. pycontrails/models/cocip/output_formats.py +53 -53
  32. pycontrails/models/cocip/radiative_forcing.py +135 -131
  33. pycontrails/models/cocip/radiative_heating.py +135 -135
  34. pycontrails/models/cocip/unterstrasser_wake_vortex.py +90 -87
  35. pycontrails/models/cocip/wake_vortex.py +92 -92
  36. pycontrails/models/cocip/wind_shear.py +8 -8
  37. pycontrails/models/cocipgrid/cocip_grid.py +37 -96
  38. pycontrails/models/dry_advection.py +60 -19
  39. pycontrails/models/emissions/__init__.py +2 -2
  40. pycontrails/models/emissions/black_carbon.py +108 -108
  41. pycontrails/models/emissions/emissions.py +87 -87
  42. pycontrails/models/emissions/ffm2.py +35 -35
  43. pycontrails/models/humidity_scaling/humidity_scaling.py +23 -23
  44. pycontrails/models/issr.py +2 -2
  45. pycontrails/models/ps_model/__init__.py +1 -1
  46. pycontrails/models/ps_model/ps_aircraft_params.py +8 -4
  47. pycontrails/models/ps_model/ps_grid.py +76 -66
  48. pycontrails/models/ps_model/ps_model.py +16 -16
  49. pycontrails/models/ps_model/ps_operational_limits.py +20 -18
  50. pycontrails/models/tau_cirrus.py +8 -1
  51. pycontrails/physics/geo.py +67 -67
  52. pycontrails/physics/jet.py +79 -79
  53. pycontrails/physics/units.py +14 -14
  54. pycontrails/utils/json.py +1 -2
  55. pycontrails/utils/types.py +12 -7
  56. {pycontrails-0.54.3.dist-info → pycontrails-0.54.5.dist-info}/METADATA +2 -2
  57. {pycontrails-0.54.3.dist-info → pycontrails-0.54.5.dist-info}/NOTICE +1 -1
  58. pycontrails-0.54.5.dist-info/RECORD +111 -0
  59. pycontrails-0.54.3.dist-info/RECORD +0 -111
  60. {pycontrails-0.54.3.dist-info → pycontrails-0.54.5.dist-info}/LICENSE +0 -0
  61. {pycontrails-0.54.3.dist-info → pycontrails-0.54.5.dist-info}/WHEEL +0 -0
  62. {pycontrails-0.54.3.dist-info → pycontrails-0.54.5.dist-info}/top_level.txt +0 -0
pycontrails/__init__.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """
2
2
  ``pycontrails`` public API.
3
3
 
4
- Copyright 2021-2023 Breakthrough Energy
4
+ Copyright 2021-present Breakthrough Energy
5
5
 
6
6
  Licensed under the Apache License, Version 2.0 (the "License");
7
7
  you may not use this file except in compliance with the License.
@@ -60,8 +60,8 @@ __all__ = [
60
60
  "HydrogenFuel",
61
61
  "JetA",
62
62
  "MetDataArray",
63
- "MetDataset",
64
63
  "MetDataSource",
64
+ "MetDataset",
65
65
  "MetVariable",
66
66
  "Model",
67
67
  "ModelParams",
pycontrails/_version.py CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.54.3'
16
- __version_tuple__ = version_tuple = (0, 54, 3)
15
+ __version__ = version = '0.54.5'
16
+ __version_tuple__ = version_tuple = (0, 54, 5)
@@ -20,8 +20,8 @@ __all__ = [
20
20
  "HydrogenFuel",
21
21
  "JetA",
22
22
  "MetDataArray",
23
- "MetDataset",
24
23
  "MetDataSource",
24
+ "MetDataset",
25
25
  "MetVariable",
26
26
  "Model",
27
27
  "ModelParams",
@@ -163,14 +163,14 @@ class AircraftPerformance(Model):
163
163
  self,
164
164
  *,
165
165
  aircraft_type: str,
166
- altitude_ft: npt.NDArray[np.float64],
166
+ altitude_ft: npt.NDArray[np.floating],
167
167
  time: npt.NDArray[np.datetime64],
168
- true_airspeed: npt.NDArray[np.float64],
169
- air_temperature: npt.NDArray[np.float64],
170
- aircraft_mass: npt.NDArray[np.float64] | float | None,
171
- thrust: npt.NDArray[np.float64] | float | None,
172
- engine_efficiency: npt.NDArray[np.float64] | float | None,
173
- fuel_flow: npt.NDArray[np.float64] | float | None,
168
+ true_airspeed: npt.NDArray[np.floating],
169
+ air_temperature: npt.NDArray[np.floating],
170
+ aircraft_mass: npt.NDArray[np.floating] | float | None,
171
+ thrust: npt.NDArray[np.floating] | float | None,
172
+ engine_efficiency: npt.NDArray[np.floating] | float | None,
173
+ fuel_flow: npt.NDArray[np.floating] | float | None,
174
174
  q_fuel: float,
175
175
  n_iter: int,
176
176
  amass_oew: float,
@@ -192,21 +192,21 @@ class AircraftPerformance(Model):
192
192
  ----------
193
193
  aircraft_type: str
194
194
  Aircraft type designator used to query the underlying model database.
195
- altitude_ft: npt.NDArray[np.float64]
195
+ altitude_ft: npt.NDArray[np.floating]
196
196
  Altitude at each waypoint, [:math:`ft`]
197
197
  time: npt.NDArray[np.datetime64]
198
198
  Waypoint time in ``np.datetime64`` format.
199
- true_airspeed: npt.NDArray[np.float64]
199
+ true_airspeed: npt.NDArray[np.floating]
200
200
  True airspeed for each waypoint, [:math:`m s^{-1}`]
201
- air_temperature : npt.NDArray[np.float64]
201
+ air_temperature : npt.NDArray[np.floating]
202
202
  Ambient temperature for each waypoint, [:math:`K`]
203
- aircraft_mass : npt.NDArray[np.float64] | float | None
203
+ aircraft_mass : npt.NDArray[np.floating] | float | None
204
204
  Override the aircraft_mass at each waypoint, [:math:`kg`].
205
- thrust : npt.NDArray[np.float64] | float | None
205
+ thrust : npt.NDArray[np.floating] | float | None
206
206
  Override the thrust setting at each waypoint, [:math: `N`].
207
- engine_efficiency : npt.NDArray[np.float64] | float | None
207
+ engine_efficiency : npt.NDArray[np.floating] | float | None
208
208
  Override the engine efficiency at each waypoint.
209
- fuel_flow : npt.NDArray[np.float64] | float | None
209
+ fuel_flow : npt.NDArray[np.floating] | float | None
210
210
  Override the fuel flow at each waypoint, [:math:`kg s^{-1}`].
211
211
  q_fuel : float
212
212
  Lower calorific value (LCV) of fuel, [:math:`J \ kg_{fuel}^{-1}`].
@@ -280,14 +280,14 @@ class AircraftPerformance(Model):
280
280
  self,
281
281
  *,
282
282
  aircraft_type: str,
283
- altitude_ft: npt.NDArray[np.float64],
283
+ altitude_ft: npt.NDArray[np.floating],
284
284
  time: npt.NDArray[np.datetime64],
285
- true_airspeed: npt.NDArray[np.float64],
286
- air_temperature: npt.NDArray[np.float64],
287
- aircraft_mass: npt.NDArray[np.float64] | float,
288
- thrust: npt.NDArray[np.float64] | float | None,
289
- engine_efficiency: npt.NDArray[np.float64] | float | None,
290
- fuel_flow: npt.NDArray[np.float64] | float | None,
285
+ true_airspeed: npt.NDArray[np.floating],
286
+ air_temperature: npt.NDArray[np.floating],
287
+ aircraft_mass: npt.NDArray[np.floating] | float,
288
+ thrust: npt.NDArray[np.floating] | float | None,
289
+ engine_efficiency: npt.NDArray[np.floating] | float | None,
290
+ fuel_flow: npt.NDArray[np.floating] | float | None,
291
291
  q_fuel: float,
292
292
  **kwargs: Any,
293
293
  ) -> AircraftPerformanceData:
@@ -332,13 +332,13 @@ class AircraftPerformance(Model):
332
332
  self,
333
333
  *,
334
334
  aircraft_type: str,
335
- altitude_ft: npt.NDArray[np.float64],
335
+ altitude_ft: npt.NDArray[np.floating],
336
336
  time: npt.NDArray[np.datetime64],
337
- true_airspeed: npt.NDArray[np.float64],
338
- air_temperature: npt.NDArray[np.float64],
339
- thrust: npt.NDArray[np.float64] | float | None,
340
- engine_efficiency: npt.NDArray[np.float64] | float | None,
341
- fuel_flow: npt.NDArray[np.float64] | float | None,
337
+ true_airspeed: npt.NDArray[np.floating],
338
+ air_temperature: npt.NDArray[np.floating],
339
+ thrust: npt.NDArray[np.floating] | float | None,
340
+ engine_efficiency: npt.NDArray[np.floating] | float | None,
341
+ fuel_flow: npt.NDArray[np.floating] | float | None,
342
342
  q_fuel: float,
343
343
  n_iter: int,
344
344
  amass_oew: float,
@@ -351,7 +351,7 @@ class AircraftPerformance(Model):
351
351
  # Variable aircraft_mass will change dynamically after each iteration
352
352
  # Set the initial aircraft mass depending on a possible load factor
353
353
 
354
- aircraft_mass: npt.NDArray[np.float64] | float
354
+ aircraft_mass: npt.NDArray[np.floating] | float
355
355
  if takeoff_mass is not None:
356
356
  aircraft_mass = takeoff_mass
357
357
  else:
@@ -411,14 +411,14 @@ class AircraftPerformance(Model):
411
411
  self,
412
412
  *,
413
413
  aircraft_type: str,
414
- altitude_ft: npt.NDArray[np.float64],
415
- air_temperature: npt.NDArray[np.float64],
414
+ altitude_ft: npt.NDArray[np.floating],
415
+ air_temperature: npt.NDArray[np.floating],
416
416
  time: npt.NDArray[np.datetime64] | None,
417
- true_airspeed: npt.NDArray[np.float64] | float | None,
418
- aircraft_mass: npt.NDArray[np.float64] | float,
419
- engine_efficiency: npt.NDArray[np.float64] | float | None,
420
- fuel_flow: npt.NDArray[np.float64] | float | None,
421
- thrust: npt.NDArray[np.float64] | float | None,
417
+ true_airspeed: npt.NDArray[np.floating] | float | None,
418
+ aircraft_mass: npt.NDArray[np.floating] | float,
419
+ engine_efficiency: npt.NDArray[np.floating] | float | None,
420
+ fuel_flow: npt.NDArray[np.floating] | float | None,
421
+ thrust: npt.NDArray[np.floating] | float | None,
422
422
  q_fuel: float,
423
423
  **kwargs: Any,
424
424
  ) -> AircraftPerformanceData:
@@ -441,24 +441,24 @@ class AircraftPerformance(Model):
441
441
  ----------
442
442
  aircraft_type : str
443
443
  Used to query the underlying model database for aircraft engine parameters.
444
- altitude_ft : npt.NDArray[np.float64]
444
+ altitude_ft : npt.NDArray[np.floating]
445
445
  Altitude at each waypoint, [:math:`ft`]
446
- air_temperature : npt.NDArray[np.float64]
446
+ air_temperature : npt.NDArray[np.floating]
447
447
  Ambient temperature for each waypoint, [:math:`K`]
448
448
  time: npt.NDArray[np.datetime64] | None
449
449
  Waypoint time in ``np.datetime64`` format. If None, only drag force
450
450
  will is used in thrust calculations (ie, no vertical change and constant
451
451
  horizontal change). In addition, aircraft is assumed to be in cruise.
452
- true_airspeed : npt.NDArray[np.float64] | float | None
452
+ true_airspeed : npt.NDArray[np.floating] | float | None
453
453
  True airspeed for each waypoint, [:math:`m s^{-1}`].
454
454
  If None, a nominal value is used.
455
- aircraft_mass : npt.NDArray[np.float64] | float
455
+ aircraft_mass : npt.NDArray[np.floating] | float
456
456
  Aircraft mass for each waypoint, [:math:`kg`].
457
- engine_efficiency : npt.NDArray[np.float64] | float | None
457
+ engine_efficiency : npt.NDArray[np.floating] | float | None
458
458
  Override the engine efficiency at each waypoint.
459
- fuel_flow : npt.NDArray[np.float64] | float | None
459
+ fuel_flow : npt.NDArray[np.floating] | float | None
460
460
  Override the fuel flow at each waypoint, [:math:`kg s^{-1}`].
461
- thrust : npt.NDArray[np.float64] | float | None
461
+ thrust : npt.NDArray[np.floating] | float | None
462
462
  Override the thrust setting at each waypoint, [:math: `N`].
463
463
  q_fuel : float
464
464
  Lower calorific value (LCV) of fuel, [:math:`J \ kg_{fuel}^{-1}`].
@@ -471,12 +471,12 @@ class AircraftPerformance(Model):
471
471
  Derived performance metrics at each waypoint.
472
472
  """
473
473
 
474
- def ensure_true_airspeed_on_source(self) -> npt.NDArray[np.float64]:
474
+ def ensure_true_airspeed_on_source(self) -> npt.NDArray[np.floating]:
475
475
  """Add ``true_airspeed`` field to :attr:`source` data if not already present.
476
476
 
477
477
  Returns
478
478
  -------
479
- npt.NDArray[np.float64]
479
+ npt.NDArray[np.floating]
480
480
  True airspeed, [:math:`m s^{-1}`]. If ``true_airspeed`` is already present
481
481
  on :attr:`source`, this is returned directly. Otherwise, it is calculated
482
482
  using :meth:`Flight.segment_true_airspeed`.
@@ -529,30 +529,30 @@ class AircraftPerformanceData:
529
529
 
530
530
  Parameters
531
531
  ----------
532
- fuel_flow : npt.NDArray[np.float64]
532
+ fuel_flow : npt.NDArray[np.floating]
533
533
  Fuel mass flow rate for each waypoint, [:math:`kg s^{-1}`]
534
- aircraft_mass : npt.NDArray[np.float64]
534
+ aircraft_mass : npt.NDArray[np.floating]
535
535
  Aircraft mass for each waypoint, [:math:`kg`]
536
- true_airspeed : npt.NDArray[np.float64]
536
+ true_airspeed : npt.NDArray[np.floating]
537
537
  True airspeed at each waypoint, [:math: `m s^{-1}`]
538
- fuel_burn: npt.NDArray[np.float64]
538
+ fuel_burn: npt.NDArray[np.floating]
539
539
  Fuel consumption for each waypoint, [:math:`kg`]. Set to an array of
540
540
  all nan values if it cannot be computed (ie, working with gridpoints).
541
- thrust: npt.NDArray[np.float64]
541
+ thrust: npt.NDArray[np.floating]
542
542
  Thrust force, [:math:`N`]
543
- engine_efficiency: npt.NDArray[np.float64]
543
+ engine_efficiency: npt.NDArray[np.floating]
544
544
  Overall propulsion efficiency for each waypoint
545
- rocd : npt.NDArray[np.float64]
545
+ rocd : npt.NDArray[np.floating]
546
546
  Rate of climb and descent, [:math:`ft min^{-1}`]
547
547
  """
548
548
 
549
- fuel_flow: npt.NDArray[np.float64]
550
- aircraft_mass: npt.NDArray[np.float64]
551
- true_airspeed: npt.NDArray[np.float64]
552
- fuel_burn: npt.NDArray[np.float64]
553
- thrust: npt.NDArray[np.float64]
554
- engine_efficiency: npt.NDArray[np.float64]
555
- rocd: npt.NDArray[np.float64]
549
+ fuel_flow: npt.NDArray[np.floating]
550
+ aircraft_mass: npt.NDArray[np.floating]
551
+ true_airspeed: npt.NDArray[np.floating]
552
+ fuel_burn: npt.NDArray[np.floating]
553
+ thrust: npt.NDArray[np.floating]
554
+ engine_efficiency: npt.NDArray[np.floating]
555
+ rocd: npt.NDArray[np.floating]
556
556
 
557
557
 
558
558
  # --------------------------------
pycontrails/core/cache.py CHANGED
@@ -43,7 +43,7 @@ def _get_user_cache_dir() -> str:
43
43
  class CacheStore(ABC):
44
44
  """Abstract cache storage class for storing staged and intermediate data."""
45
45
 
46
- __slots__ = ("cache_dir", "allow_clear")
46
+ __slots__ = ("allow_clear", "cache_dir")
47
47
  cache_dir: str
48
48
  allow_clear: bool
49
49
 
@@ -426,15 +426,15 @@ class GCPCacheStore(CacheStore):
426
426
  """
427
427
 
428
428
  __slots__ = (
429
- "project",
429
+ "_bucket",
430
+ "_client",
431
+ "_disk_cache",
430
432
  "bucket",
433
+ "chunk_size",
434
+ "project",
431
435
  "read_only",
432
- "timeout",
433
436
  "show_progress",
434
- "chunk_size",
435
- "_disk_cache",
436
- "_client",
437
- "_bucket",
437
+ "timeout",
438
438
  )
439
439
  project: str | None
440
440
  bucket: str
pycontrails/core/fleet.py CHANGED
@@ -32,7 +32,7 @@ class Fleet(Flight):
32
32
  Flight waypoints are merged into a single :class:`Flight`-like object.
33
33
  """
34
34
 
35
- __slots__ = ("fl_attrs", "final_waypoints")
35
+ __slots__ = ("final_waypoints", "fl_attrs")
36
36
 
37
37
  def __init__(
38
38
  self,
@@ -132,19 +132,20 @@ class Fleet(Flight):
132
132
  return final_waypoints, fl_attrs
133
133
 
134
134
  @override
135
- def copy(self, **kwargs: Any) -> Fleet:
136
- kwargs.setdefault("fuel", self.fuel)
135
+ def copy(self, **kwargs: Any) -> Self:
137
136
  kwargs.setdefault("fl_attrs", self.fl_attrs)
137
+ kwargs.setdefault("final_waypoints", self.final_waypoints)
138
138
  return super().copy(**kwargs)
139
139
 
140
140
  @override
141
- def filter(self, mask: npt.NDArray[np.bool_], copy: bool = True, **kwargs: Any) -> Fleet:
142
- kwargs.setdefault("fuel", self.fuel)
143
-
141
+ def filter(self, mask: npt.NDArray[np.bool_], copy: bool = True, **kwargs: Any) -> Self:
144
142
  flight_ids = set(np.unique(self["flight_id"][mask]))
145
143
  fl_attrs = {k: v for k, v in self.fl_attrs.items() if k in flight_ids}
146
144
  kwargs.setdefault("fl_attrs", fl_attrs)
147
145
 
146
+ final_waypoints = np.array(self.final_waypoints[mask], copy=copy)
147
+ kwargs.setdefault("final_waypoints", final_waypoints)
148
+
148
149
  return super().filter(mask, copy=copy, **kwargs)
149
150
 
150
151
  @override
@@ -162,7 +163,6 @@ class Fleet(Flight):
162
163
  cls,
163
164
  seq: Iterable[Flight],
164
165
  broadcast_numeric: bool = True,
165
- copy: bool = True,
166
166
  attrs: dict[str, Any] | None = None,
167
167
  ) -> Self:
168
168
  """Instantiate a :class:`Fleet` instance from an iterable of :class:`Flight`.
@@ -177,8 +177,6 @@ class Fleet(Flight):
177
177
  An iterable of :class:`Flight` instances.
178
178
  broadcast_numeric : bool, optional
179
179
  If True, broadcast numeric attributes to data variables.
180
- copy : bool, optional
181
- If True, make copy of each flight instance in ``seq``.
182
180
  attrs : dict[str, Any] | None, optional
183
181
  Global attribute to attach to instance.
184
182
 
@@ -190,15 +188,16 @@ class Fleet(Flight):
190
188
  in ``seq``.
191
189
  """
192
190
 
193
- def _maybe_copy(fl: Flight) -> Flight:
194
- return fl.copy() if copy else fl
191
+ # Create a shallow copy because we add additional keys in _validate_fl
192
+ def _shallow_copy(fl: Flight) -> Flight:
193
+ return Flight._from_fastpath(fl.data, fl.attrs, fuel=fl.fuel)
195
194
 
196
195
  def _maybe_warn(fl: Flight) -> Flight:
197
196
  if not fl:
198
197
  warnings.warn("Empty flight found in sequence. It will be filtered out.")
199
198
  return fl
200
199
 
201
- seq = tuple(_maybe_copy(fl) for fl in seq if _maybe_warn(fl))
200
+ seq = tuple(_shallow_copy(fl) for fl in seq if _maybe_warn(fl))
202
201
 
203
202
  if not seq:
204
203
  msg = "Cannot create Fleet from empty sequence."
@@ -220,7 +219,18 @@ class Fleet(Flight):
220
219
  )
221
220
 
222
221
  data = {var: np.concatenate([fl[var] for fl in seq]) for var in seq[0]}
223
- return cls(data=data, attrs=attrs, copy=False, fuel=fuel, fl_attrs=fl_attrs)
222
+
223
+ final_waypoints = np.zeros(data["time"].size, dtype=bool)
224
+ final_waypoint_indices = np.cumsum([fl.size for fl in seq]) - 1
225
+ final_waypoints[final_waypoint_indices] = True
226
+
227
+ return cls._from_fastpath(
228
+ data,
229
+ attrs,
230
+ fuel=fuel,
231
+ fl_attrs=fl_attrs,
232
+ final_waypoints=final_waypoints,
233
+ )
224
234
 
225
235
  @property
226
236
  def n_flights(self) -> int:
@@ -249,11 +259,19 @@ class Fleet(Flight):
249
259
  List of Flights in the same order as was passed into the ``Fleet`` instance.
250
260
  """
251
261
  indices = self.dataframe.groupby("flight_id", sort=False).indices
262
+ if copy:
263
+ return [
264
+ Flight._from_fastpath(
265
+ {k: v[idx] for k, v in self.data.items()},
266
+ self.fl_attrs[flight_id],
267
+ fuel=self.fuel,
268
+ ).copy()
269
+ for flight_id, idx in indices.items()
270
+ ]
252
271
  return [
253
- Flight(
254
- data=VectorDataDict({k: v[idx] for k, v in self.data.items()}),
255
- attrs=self.fl_attrs[flight_id],
256
- copy=copy,
272
+ Flight._from_fastpath(
273
+ {k: v[idx] for k, v in self.data.items()},
274
+ self.fl_attrs[flight_id],
257
275
  fuel=self.fuel,
258
276
  )
259
277
  for flight_id, idx in indices.items()
@@ -265,12 +283,12 @@ class Fleet(Flight):
265
283
 
266
284
  def segment_true_airspeed(
267
285
  self,
268
- u_wind: npt.NDArray[np.float64] | float = 0.0,
269
- v_wind: npt.NDArray[np.float64] | float = 0.0,
286
+ u_wind: npt.NDArray[np.floating] | float = 0.0,
287
+ v_wind: npt.NDArray[np.floating] | float = 0.0,
270
288
  smooth: bool = True,
271
289
  window_length: int = 7,
272
290
  polyorder: int = 1,
273
- ) -> npt.NDArray[np.float64]:
291
+ ) -> npt.NDArray[np.floating]:
274
292
  """Calculate the true airspeed [:math:`m / s`] from the ground speed and horizontal winds.
275
293
 
276
294
  Because Flight.segment_true_airspeed uses a smoothing pattern, waypoints in :attr:`data`
@@ -302,7 +320,7 @@ class Fleet(Flight):
302
320
  self[key] = v_wind
303
321
 
304
322
  # Calculate TAS on each flight individually
305
- def calc_tas(fl: Flight) -> npt.NDArray[np.float64]:
323
+ def calc_tas(fl: Flight) -> npt.NDArray[np.floating]:
306
324
  u = fl.get("__u_wind", u_wind)
307
325
  v = fl.get("__v_wind", v_wind)
308
326
 
@@ -325,19 +343,27 @@ class Fleet(Flight):
325
343
  return np.concatenate(tas)
326
344
 
327
345
  @override
328
- def segment_groundspeed(self, *args: Any, **kwargs: Any) -> npt.NDArray[np.float64]:
346
+ def segment_groundspeed(self, *args: Any, **kwargs: Any) -> npt.NDArray[np.floating]:
329
347
  fls = self.to_flight_list(copy=False)
330
348
  gs = [fl.segment_groundspeed(*args, **kwargs) for fl in fls]
331
349
  return np.concatenate(gs)
332
350
 
333
351
  @override
334
- def resample_and_fill(self, *args: Any, **kwargs: Any) -> Fleet:
352
+ def resample_and_fill(self, *args: Any, **kwargs: Any) -> Self:
335
353
  flights = self.to_flight_list(copy=False)
354
+
355
+ # We need to ensure that each flight has an flight_id attrs field
356
+ # When we call fl.resample_and_fill, any flight_id data field
357
+ # will be lost, so the call to Fleet.from_seq will fail.
358
+ for fl in flights:
359
+ if "flight_id" not in fl.attrs:
360
+ fl.attrs["flight_id"] = _extract_flight_id(fl)
361
+
336
362
  flights = [fl.resample_and_fill(*args, **kwargs) for fl in flights]
337
- return type(self).from_seq(flights, copy=False, broadcast_numeric=False, attrs=self.attrs)
363
+ return type(self).from_seq(flights, broadcast_numeric=False, attrs=self.attrs)
338
364
 
339
365
  @override
340
- def segment_length(self) -> npt.NDArray[np.float64]:
366
+ def segment_length(self) -> npt.NDArray[np.floating]:
341
367
  return np.where(self.final_waypoints, np.nan, super().segment_length())
342
368
 
343
369
  @property
@@ -346,11 +372,11 @@ class Fleet(Flight):
346
372
  return np.nanmax(self.segment_length()).item()
347
373
 
348
374
  @override
349
- def segment_azimuth(self) -> npt.NDArray[np.float64]:
375
+ def segment_azimuth(self) -> npt.NDArray[np.floating]:
350
376
  return np.where(self.final_waypoints, np.nan, super().segment_azimuth())
351
377
 
352
378
  @override
353
- def segment_angle(self) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]:
379
+ def segment_angle(self) -> tuple[npt.NDArray[np.floating], npt.NDArray[np.floating]]:
354
380
  sin_a, cos_a = super().segment_angle()
355
381
  sin_a[self.final_waypoints] = np.nan
356
382
  cos_a[self.final_waypoints] = np.nan
@@ -368,8 +394,7 @@ class Fleet(Flight):
368
394
  force_filter: bool = False,
369
395
  drop: bool = True,
370
396
  keep_original_index: bool = False,
371
- climb_descend_at_end: bool = False,
372
- ) -> Flight:
397
+ ) -> NoReturn:
373
398
  msg = "Only implemented for Flight instances"
374
399
  raise NotImplementedError(msg)
375
400