pycontrails 0.54.5__tar.gz → 0.54.7__tar.gz

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 (280) hide show
  1. {pycontrails-0.54.5 → pycontrails-0.54.7}/CHANGELOG.md +46 -0
  2. {pycontrails-0.54.5 → pycontrails-0.54.7}/NOTICE +1 -1
  3. {pycontrails-0.54.5/pycontrails.egg-info → pycontrails-0.54.7}/PKG-INFO +5 -6
  4. {pycontrails-0.54.5 → pycontrails-0.54.7}/RELEASE.md +1 -1
  5. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/conf.py +4 -2
  6. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks/ECMWF.ipynb +7 -7
  7. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/__init__.py +1 -1
  8. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/_version.py +2 -2
  9. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/core/aircraft_performance.py +46 -46
  10. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/core/airports.py +7 -5
  11. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/core/flight.py +6 -8
  12. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/core/flightplan.py +11 -11
  13. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/core/met.py +41 -33
  14. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/core/met_var.py +80 -0
  15. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/core/models.py +80 -3
  16. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/core/rgi_cython.c +217 -217
  17. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/core/vector.py +66 -0
  18. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/datalib/_met_utils/metsource.py +1 -1
  19. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/datalib/ecmwf/era5.py +5 -6
  20. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/datalib/ecmwf/era5_model_level.py +4 -5
  21. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/datalib/ecmwf/ifs.py +1 -3
  22. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/datalib/gfs/gfs.py +1 -3
  23. pycontrails-0.54.7/pycontrails/datalib/spire/__init__.py +5 -0
  24. pycontrails-0.54.7/pycontrails/datalib/spire/exceptions.py +62 -0
  25. pycontrails-0.54.7/pycontrails/datalib/spire/spire.py +606 -0
  26. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/accf.py +4 -4
  27. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/cocip/cocip.py +116 -19
  28. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/cocip/cocip_params.py +10 -1
  29. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/cocip/output_formats.py +1 -0
  30. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/cocip/unterstrasser_wake_vortex.py +132 -30
  31. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/cocipgrid/cocip_grid.py +3 -0
  32. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/dry_advection.py +51 -19
  33. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/emissions/black_carbon.py +19 -14
  34. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/emissions/emissions.py +8 -8
  35. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/humidity_scaling/humidity_scaling.py +1 -1
  36. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/pcc.py +1 -2
  37. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/ps_model/ps_model.py +3 -31
  38. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/ps_model/ps_operational_limits.py +2 -6
  39. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/tau_cirrus.py +13 -6
  40. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/physics/constants.py +2 -1
  41. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/physics/geo.py +3 -3
  42. {pycontrails-0.54.5 → pycontrails-0.54.7/pycontrails.egg-info}/PKG-INFO +5 -6
  43. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails.egg-info/SOURCES.txt +3 -1
  44. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails.egg-info/requires.txt +2 -3
  45. {pycontrails-0.54.5 → pycontrails-0.54.7}/pyproject.toml +5 -6
  46. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/benchmark/cocip/benchmark.py +2 -6
  47. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/benchmark/north-atlantic-study/validate.py +3 -3
  48. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/conftest.py +61 -0
  49. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_apcemm.py +1 -2
  50. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_cache.py +1 -1
  51. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_cocip.py +170 -2
  52. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_cocip_grid.py +71 -1
  53. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_cocip_grid_parity.py +1 -1
  54. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_dry_advection.py +38 -0
  55. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_geo.py +1 -2
  56. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_met.py +1 -1
  57. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_models.py +22 -0
  58. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_ps_model.py +1 -2
  59. pycontrails-0.54.7/tests/unit/test_spire.py +200 -0
  60. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_unterstrasser_wake_vortex.py +42 -26
  61. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_vector.py +1 -2
  62. pycontrails-0.54.5/pycontrails/datalib/spire.py +0 -739
  63. pycontrails-0.54.5/tests/unit/test_spire.py +0 -93
  64. {pycontrails-0.54.5 → pycontrails-0.54.7}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  65. {pycontrails-0.54.5 → pycontrails-0.54.7}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  66. {pycontrails-0.54.5 → pycontrails-0.54.7}/.github/dependabot.yaml +0 -0
  67. {pycontrails-0.54.5 → pycontrails-0.54.7}/.github/pull_request_template.md +0 -0
  68. {pycontrails-0.54.5 → pycontrails-0.54.7}/.github/workflows/docs.yaml +0 -0
  69. {pycontrails-0.54.5 → pycontrails-0.54.7}/.github/workflows/doctest.yaml +0 -0
  70. {pycontrails-0.54.5 → pycontrails-0.54.7}/.github/workflows/release.yaml +0 -0
  71. {pycontrails-0.54.5 → pycontrails-0.54.7}/.github/workflows/scorecard.yaml +0 -0
  72. {pycontrails-0.54.5 → pycontrails-0.54.7}/.github/workflows/test.yaml +0 -0
  73. {pycontrails-0.54.5 → pycontrails-0.54.7}/.gitignore +0 -0
  74. {pycontrails-0.54.5 → pycontrails-0.54.7}/.pre-commit-config.yaml +0 -0
  75. {pycontrails-0.54.5 → pycontrails-0.54.7}/.zenodo.json +0 -0
  76. {pycontrails-0.54.5 → pycontrails-0.54.7}/CONTRIBUTING.md +0 -0
  77. {pycontrails-0.54.5 → pycontrails-0.54.7}/LICENSE +0 -0
  78. {pycontrails-0.54.5 → pycontrails-0.54.7}/Makefile +0 -0
  79. {pycontrails-0.54.5 → pycontrails-0.54.7}/README.md +0 -0
  80. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/_static/css/style.css +0 -0
  81. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/_static/img/colab.png +0 -0
  82. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/_static/img/favicon.png +0 -0
  83. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/_static/img/logo-dark.png +0 -0
  84. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/_static/img/logo.png +0 -0
  85. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/_static/pycontrails.bib +0 -0
  86. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/api.rst +0 -0
  87. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/changelog.rst +0 -0
  88. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/contributing.rst +0 -0
  89. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/develop.rst +0 -0
  90. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/flight.rst +0 -0
  91. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/index.rst +0 -0
  92. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/install.rst +0 -0
  93. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/integrations/ACCF.ipynb +0 -0
  94. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/integrations/APCEMM.ipynb +0 -0
  95. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/literature.rst +0 -0
  96. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/meteorology.rst +0 -0
  97. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/models.rst +0 -0
  98. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks/ARCO-ERA5.ipynb +0 -0
  99. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks/AircraftPerformance.ipynb +0 -0
  100. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks/Cache.ipynb +0 -0
  101. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks/CoCiP.ipynb +0 -0
  102. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks/CoCiPGrid.ipynb +0 -0
  103. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks/Flight.ipynb +0 -0
  104. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks/GFS.ipynb +0 -0
  105. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks/GOES.ipynb +0 -0
  106. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks/ISSR.ipynb +0 -0
  107. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks/Landsat.ipynb +0 -0
  108. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks/Meteorology.ipynb +0 -0
  109. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks/SAC.ipynb +0 -0
  110. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks/Sentinel.ipynb +0 -0
  111. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks/advection.ipynb +0 -0
  112. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks/airports.ipynb +0 -0
  113. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks/data/.gitignore +0 -0
  114. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks/data/flight-ap.csv +0 -0
  115. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks/data/flight-cocip.csv +0 -0
  116. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks/data/flight-fdr.csv +0 -0
  117. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks/data/flight-noisy.csv +0 -0
  118. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks/data/flight.csv +0 -0
  119. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks/data/iagos-flight-landsat.csv +0 -0
  120. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks/data/iagos-flight-sentinel.csv +0 -0
  121. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks/flightplan.ipynb +0 -0
  122. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks/model-levels.ipynb +0 -0
  123. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks/run-cocip-on-flight.ipynb +0 -0
  124. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks/run-cocip-with-fdr.ipynb +0 -0
  125. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks/specific-humidity-interpolation.ipynb +0 -0
  126. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/notebooks.rst +0 -0
  127. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/observations.rst +0 -0
  128. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/tutorials.rst +0 -0
  129. {pycontrails-0.54.5 → pycontrails-0.54.7}/docs/utilities.rst +0 -0
  130. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/core/__init__.py +0 -0
  131. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/core/cache.py +0 -0
  132. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/core/coordinates.py +0 -0
  133. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/core/fleet.py +0 -0
  134. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/core/fuel.py +0 -0
  135. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/core/interpolation.py +0 -0
  136. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/core/polygon.py +0 -0
  137. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/core/rgi_cython.pyx +0 -0
  138. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/datalib/__init__.py +0 -0
  139. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/datalib/_leo_utils/search.py +0 -0
  140. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/datalib/_leo_utils/static/bq_roi_query.sql +0 -0
  141. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/datalib/_leo_utils/vis.py +0 -0
  142. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/datalib/ecmwf/__init__.py +0 -0
  143. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/datalib/ecmwf/arco_era5.py +0 -0
  144. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/datalib/ecmwf/common.py +0 -0
  145. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/datalib/ecmwf/hres.py +0 -0
  146. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/datalib/ecmwf/hres_model_level.py +0 -0
  147. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/datalib/ecmwf/model_levels.py +0 -0
  148. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/datalib/ecmwf/static/model_level_dataframe_v20240418.csv +0 -0
  149. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/datalib/ecmwf/variables.py +0 -0
  150. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/datalib/gfs/__init__.py +0 -0
  151. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/datalib/gfs/variables.py +0 -0
  152. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/datalib/goes.py +0 -0
  153. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/datalib/landsat.py +0 -0
  154. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/datalib/sentinel.py +0 -0
  155. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/ext/bada.py +0 -0
  156. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/ext/cirium.py +0 -0
  157. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/ext/empirical_grid.py +0 -0
  158. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/ext/synthetic_flight.py +0 -0
  159. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/__init__.py +0 -0
  160. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/apcemm/__init__.py +0 -0
  161. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/apcemm/apcemm.py +0 -0
  162. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/apcemm/inputs.py +0 -0
  163. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/apcemm/static/apcemm_yaml_template.yaml +0 -0
  164. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/apcemm/utils.py +0 -0
  165. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/cocip/__init__.py +0 -0
  166. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/cocip/cocip_uncertainty.py +0 -0
  167. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/cocip/contrail_properties.py +0 -0
  168. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/cocip/radiative_forcing.py +0 -0
  169. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/cocip/radiative_heating.py +0 -0
  170. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/cocip/wake_vortex.py +0 -0
  171. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/cocip/wind_shear.py +0 -0
  172. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/cocipgrid/__init__.py +0 -0
  173. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/cocipgrid/cocip_grid_params.py +0 -0
  174. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/emissions/__init__.py +0 -0
  175. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/emissions/ffm2.py +0 -0
  176. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/emissions/static/default-engine-uids.csv +0 -0
  177. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/emissions/static/edb-gaseous-v29b-engines.csv +0 -0
  178. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/emissions/static/edb-nvpm-v29b-engines.csv +0 -0
  179. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/humidity_scaling/__init__.py +0 -0
  180. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/humidity_scaling/quantiles/era5-model-level-quantiles.pq +0 -0
  181. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/humidity_scaling/quantiles/era5-pressure-level-quantiles.pq +0 -0
  182. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/issr.py +0 -0
  183. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/pcr.py +0 -0
  184. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/ps_model/__init__.py +0 -0
  185. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/ps_model/ps_aircraft_params.py +0 -0
  186. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/ps_model/ps_grid.py +0 -0
  187. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/ps_model/static/ps-aircraft-params-20240524.csv +0 -0
  188. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/ps_model/static/ps-synonym-list-20240524.csv +0 -0
  189. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/models/sac.py +0 -0
  190. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/physics/__init__.py +0 -0
  191. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/physics/jet.py +0 -0
  192. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/physics/static/iata-cargo-load-factors-20241115.csv +0 -0
  193. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/physics/static/iata-passenger-load-factors-20241115.csv +0 -0
  194. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/physics/thermo.py +0 -0
  195. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/physics/units.py +0 -0
  196. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/py.typed +0 -0
  197. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/utils/__init__.py +0 -0
  198. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/utils/dependencies.py +0 -0
  199. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/utils/iteration.py +0 -0
  200. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/utils/json.py +0 -0
  201. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/utils/temp.py +0 -0
  202. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails/utils/types.py +0 -0
  203. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails.egg-info/dependency_links.txt +0 -0
  204. {pycontrails-0.54.5 → pycontrails-0.54.7}/pycontrails.egg-info/top_level.txt +0 -0
  205. {pycontrails-0.54.5 → pycontrails-0.54.7}/setup.cfg +0 -0
  206. {pycontrails-0.54.5 → pycontrails-0.54.7}/setup.py +0 -0
  207. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/__init__.py +0 -0
  208. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/_deprecated.py +0 -0
  209. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/benchmark/cocip/Makefile +0 -0
  210. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/benchmark/cocip/README.md +0 -0
  211. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/benchmark/cocip/compare.py +0 -0
  212. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/benchmark/cocip/data.md +0 -0
  213. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/benchmark/cocip/output.py +0 -0
  214. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/benchmark/cocip/review.ipynb +0 -0
  215. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/benchmark/cocip-fortran/README.md +0 -0
  216. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/benchmark/north-atlantic-study/.gcloudignore +0 -0
  217. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/benchmark/north-atlantic-study/README.md +0 -0
  218. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/benchmark/north-atlantic-study/support.py +0 -0
  219. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/fixtures/cocip-met.py +0 -0
  220. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/fixtures/cocip-met2.py +0 -0
  221. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/fixtures/ecmwf-met.py +0 -0
  222. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/fixtures/gfs-met.py +0 -0
  223. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/__init__.py +0 -0
  224. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/static/NOAA_Solar_Calculations_day.csv +0 -0
  225. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/static/cocip-contrail-output.json +0 -0
  226. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/static/cocip-contrail-output2.json +0 -0
  227. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/static/cocip-flight-output.json +0 -0
  228. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/static/cocip-flight-output2.json +0 -0
  229. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/static/cocip-output-contrail-edges.json +0 -0
  230. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/static/cocip-output-flts-20190101-eu.pq +0 -0
  231. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/static/flight-cocip2.csv +0 -0
  232. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/static/flight-meridian.csv +0 -0
  233. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/static/flight-metadata.json +0 -0
  234. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/static/flight-spire-data-cleaning.pq +0 -0
  235. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/static/flight.csv +0 -0
  236. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/static/flt-wypts-20190101-eu.pq +0 -0
  237. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/static/met-20190101-eu.nc +0 -0
  238. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/static/met-accf-pl.nc +0 -0
  239. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/static/met-accf-sl.nc +0 -0
  240. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/static/met-ecmwf-lnsp.nc +0 -0
  241. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/static/met-ecmwf-ml.nc +0 -0
  242. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/static/met-ecmwf-pl.nc +0 -0
  243. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/static/met-ecmwf-sl.nc +0 -0
  244. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/static/met-era5-cocip1.nc +0 -0
  245. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/static/met-era5-cocip2.nc +0 -0
  246. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/static/met-gfs.nc +0 -0
  247. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/static/polygon-bug.nc +0 -0
  248. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/static/rad-20190101-eu.nc +0 -0
  249. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/static/rad-era5-cocip1.nc +0 -0
  250. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/static/rad-era5-cocip2.nc +0 -0
  251. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/static/rad-gfs.nc +0 -0
  252. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_accf.py +0 -0
  253. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_airports.py +0 -0
  254. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_cocip_radiative_forcing.py +0 -0
  255. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_cocip_uncertainty.py +0 -0
  256. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_coordinates.py +0 -0
  257. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_datalib_metsource.py +0 -0
  258. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_dtypes.py +0 -0
  259. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_ecmwf.py +0 -0
  260. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_emissions.py +0 -0
  261. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_fleet.py +0 -0
  262. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_flight.py +0 -0
  263. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_flightplan.py +0 -0
  264. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_fuel.py +0 -0
  265. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_gfs.py +0 -0
  266. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_goes.py +0 -0
  267. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_grid_to_netcdf.py +0 -0
  268. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_humidity_scaling.py +0 -0
  269. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_init.py +0 -0
  270. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_interpolation.py +0 -0
  271. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_leo.py +0 -0
  272. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_met_cache.py +0 -0
  273. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_pcc.py +0 -0
  274. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_polygons.py +0 -0
  275. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_sac_issr.py +0 -0
  276. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_tau_cirrus.py +0 -0
  277. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_thermo_sac.py +0 -0
  278. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_units.py +0 -0
  279. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_utils.py +0 -0
  280. {pycontrails-0.54.5 → pycontrails-0.54.7}/tests/unit/test_zarr.py +0 -0
@@ -1,5 +1,51 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.54.7
4
+
5
+ ### Features
6
+
7
+ - Add helper `classmethods` to `Model`, `Cocip`, and `CocipGrid` for generating lists of required variables from specific data sources.
8
+ - Add a `ValidateTrajectoryHandler` to the `spire` module to validate spire ADS-B data. This work is experimental and will be improved in future releases.
9
+ - Update Unterstrasser (2016)'s parameterised model of the contrail ice crystal survival fraction to the latest version (Lottermoser & Unterstrasser, 2025). This update:
10
+ - improves the goodness of fit between the parameterised model and LES, and
11
+ - expands the parameter space for application to very low and very high nvPM inputs, different fuel types (where the EI H2Os are different), and higher ambient temperatures (up to 235 K) to accommodate for contrails formed by liquid hydrogen aircraft.
12
+
13
+ ### Breaking changes
14
+
15
+ - The `MetDataset.standardize_variables` method now returns a new `MetDataset` rather than modifying the existing dataset in place. To retain the previous behavior, use `MetDataset.standardize_variables(..., inplace=True)`.
16
+
17
+ ### Fixes
18
+
19
+ - Change naming convention for eastward and northward wind fields in `AircraftPerformance` models for consistency with the `Cocip` and `DryAdvection` models. Fields on the `source` are now named `u_wind` and `v_wind` instead of `eastward_wind` and `northward_wind`. Under some paths of computation, this avoids a redundant interpolation.
20
+ - Fix the `AircraftPerformance.ensure_true_airspeed_on_source` method in the case when the `met` attr is None and the `fill_with_groundspeed` parameter is enabled.
21
+
22
+ ### Internals
23
+
24
+ - Make `pycontrails` compatible with `pandas 2.0` and `pandas 2.1`.
25
+ - Avoid auto-promotion of float32 to float64 within the `Emissions` model run-time.
26
+ - Add convenience `VectorDataset.get_constant` method.
27
+
28
+ ## v0.54.6
29
+
30
+ ### Features
31
+
32
+ - Add support for generic (model-agnostic) meteorology data to `Cocip` and `CocipGrid`.
33
+
34
+ - Add two new parameters to the `DryAdvection` model.
35
+ - If the `verbose_outputs` parameter is enabled, additional wind-shear data is included in the output.
36
+ - If the `include_source_in_output` parameter is enabled, the source data with any of the intermediate artifacts (e.g., interpolated met data, wind-shear data, etc.) is included in the output.
37
+
38
+ Both parameters are disabled by default.
39
+
40
+ ### Fixes
41
+
42
+ - Update the CDS URL referenced throughout pycontrails from ``https://cds-beta.climate.copernicus.eu`` to ``https://cds.climate.copernicus.eu``.
43
+
44
+ ### Internals
45
+
46
+ - Suppress mypy `return-value` errors for functions in `geo.py` where mypy fails to correctly infer return types of numpy ufuncs applied to xarray objects.
47
+ - Change `AircraftPerformance` and downstream implementations for better support in running over `Fleet` sources. The runtime of `PSFlight` remains the same.
48
+
3
49
  ## v0.54.5
4
50
 
5
51
  ### Features
@@ -1,4 +1,4 @@
1
- Copyright (c) 2021-present Breakthrough Energy
1
+ Copyright (c) 2021-present Contrails.org and the Breakthrough Energy Foundation
2
2
 
3
3
 
4
4
  Attribution
@@ -1,8 +1,8 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: pycontrails
3
- Version: 0.54.5
3
+ Version: 0.54.7
4
4
  Summary: Python library for modeling aviation climate impacts
5
- Author-email: Breakthrough Energy <py@contrails.org>
5
+ Author-email: "Contrails.org" <py@contrails.org>
6
6
  License: Apache-2.0
7
7
  Project-URL: Changelog, https://py.contrails.org/changelog.html
8
8
  Project-URL: Documentation, https://py.contrails.org
@@ -29,7 +29,7 @@ License-File: LICENSE
29
29
  License-File: NOTICE
30
30
  Requires-Dist: dask>=2022.3
31
31
  Requires-Dist: numpy>=1.22
32
- Requires-Dist: pandas>=2.2
32
+ Requires-Dist: pandas>=2.0
33
33
  Requires-Dist: scipy>=1.10
34
34
  Requires-Dist: typing-extensions>=4.5; python_version < "3.12"
35
35
  Requires-Dist: xarray>=2022.3
@@ -49,8 +49,7 @@ Requires-Dist: pyarrow>=5.0; extra == "dev"
49
49
  Requires-Dist: pytest>=8.2; extra == "dev"
50
50
  Requires-Dist: pytest-cov>=2.11; extra == "dev"
51
51
  Requires-Dist: requests>=2.25; extra == "dev"
52
- Requires-Dist: ruff==0.8.0; extra == "dev"
53
- Requires-Dist: setuptools; extra == "dev"
52
+ Requires-Dist: ruff>=0.9.0; extra == "dev"
54
53
  Provides-Extra: docs
55
54
  Requires-Dist: doc8>=1.1; extra == "docs"
56
55
  Requires-Dist: furo>=2023.3; extra == "docs"
@@ -11,7 +11,7 @@
11
11
  Double check that the release tag is consistent and consecutive with
12
12
  the previous release tag following [PEP 440](https://peps.python.org/pep-0440/#version-scheme).
13
13
  This often but not always agrees with [Semantic Versioning](https://semver.org/).
14
- 1. Copy the CHANGELOG content for the release into the *Description*.
14
+ 1. Copy the [CHANGELOG](https://raw.githubusercontent.com/contrailcirrus/pycontrails/refs/heads/main/CHANGELOG.md) content for the release into the *Description*.
15
15
  1. Publish the new release.
16
16
  This will create a tag on `main` and trigger the [Release Action](https://github.com/contrailcirrus/pycontrails/actions/workflows/release.yaml).
17
17
  1. Confirm that the release is successfully deployed to [PyPI](https://pypi.org/project/pycontrails/).
@@ -15,9 +15,11 @@ import pycontrails
15
15
  # -- Project information -----------------------------------------------------
16
16
 
17
17
  project = "pycontrails"
18
- copyright = f"2021-{datetime.datetime.now().year}, Breakthrough Energy"
18
+ copyright = (
19
+ f"2021-{datetime.datetime.now().year}, Contrails.org and the Breakthrough Energy Foundation"
20
+ )
19
21
 
20
- author = "Breakthrough Energy"
22
+ author = "Contrails.org"
21
23
  version = pycontrails.__version__
22
24
  release = pycontrails.__version__
23
25
 
@@ -16,7 +16,7 @@
16
16
  "\n",
17
17
  "Support provided for:\n",
18
18
  "\n",
19
- "- [ERA5](https://www.ecmwf.int/en/forecasts/dataset/ecmwf-reanalysis-v5) via the [Copernicus Data Store (CDS-Beta)](https://cds-beta.climate.copernicus.eu/) using [cdsapi](https://github.com/ecmwf/cdsapi) or user provided files\n",
19
+ "- [ERA5](https://www.ecmwf.int/en/forecasts/dataset/ecmwf-reanalysis-v5) via the [Copernicus Data Store](https://cds.climate.copernicus.eu/) using [cdsapi](https://github.com/ecmwf/cdsapi) or user provided files\n",
20
20
  "- [HRES](https://confluence.ecmwf.int/display/FUG/Section+2.1.1.2+Rationale+for+High+Resolution) and [ENS](https://confluence.ecmwf.int/display/FUG/Section+2.1.2.1+Medium+Range+Ensemble+forecasts) via [MARS](https://confluence.ecmwf.int/display/UDOC/MARS+user+documentation) using [ecmwf-api-client](https://github.com/ecmwf/ecmwf-api-client) or user provided files.\n",
21
21
  "\n",
22
22
  "For both ERA5 and HRES, we provide interfaces for accessing \"pressure-level data\" (fields pre-interpolated to a fixed set of pressure levels) or \"model-level data\" (fields retrieved on the native vertical grid and [interpolated after retrieval to an arbitrary set of pressure levels](model-levels.ipynb)). We recommend using model-level data when possible, as the resolution of pressure-level data is coarse relative to the vertical scale of ice-supersaturated regions.\n",
@@ -32,8 +32,8 @@
32
32
  "\n",
33
33
  "### Access\n",
34
34
  "\n",
35
- "- Requires account with [Copernicus Data Portal](https://cds-beta.climate.copernicus.eu/)\n",
36
- "- Provide `url` and `key` credentials on input, or refer to the [CDS API Documentation](https://cds-beta.climate.copernicus.eu/how-to-api) for how to create `~/.cdsapirc` file to configure access.\n",
35
+ "- Requires account with [Copernicus Data Portal](https://cds.climate.copernicus.eu/)\n",
36
+ "- Provide `url` and `key` credentials on input, or refer to the [CDS API Documentation](https://cds.climate.copernicus.eu/how-to-api) for how to create `~/.cdsapirc` file to configure access.\n",
37
37
  "\n",
38
38
  "### Reference\n",
39
39
  "\n",
@@ -87,7 +87,7 @@
87
87
  " time=\"2022-03-01 00:00:00\",\n",
88
88
  " variables=[\"t\", \"q\", \"u\", \"v\", \"w\", \"ciwc\", \"z\", \"cc\"], # supports CF name or short names\n",
89
89
  " pressure_levels=[200, 250, 300],\n",
90
- " # url=\"https://cds-beta.climate.copernicus.eu/api\",\n",
90
+ " # url=\"https://cds.climate.copernicus.eu/api\",\n",
91
91
  " # key=\"<key>\"\n",
92
92
  ")\n",
93
93
  "era5"
@@ -132,7 +132,7 @@
132
132
  " \"cc\",\n",
133
133
  " ], # supports CF name or short names\n",
134
134
  " pressure_levels=[300, 250, 200],\n",
135
- " # url=\"https://cds-beta.climate.copernicus.eu/api\",\n",
135
+ " # url=\"https://cds.climate.copernicus.eu/api\",\n",
136
136
  " # key=\"<key>\"\n",
137
137
  ")\n",
138
138
  "era5"
@@ -1429,7 +1429,7 @@
1429
1429
  "era5 = ERA5(\n",
1430
1430
  " time=(\"2022-03-01 00:00:00\", \"2022-03-01 03:00:00\"),\n",
1431
1431
  " variables=[\"tsr\", \"ttr\"],\n",
1432
- " # url=\"https://cds-beta.climate.copernicus.eu/api\",\n",
1432
+ " # url=\"https://cds.climate.copernicus.eu/api\",\n",
1433
1433
  " # key=\"<key>\"\n",
1434
1434
  ")\n",
1435
1435
  "era5"
@@ -6071,7 +6071,7 @@
6071
6071
  " variables=variables,\n",
6072
6072
  " pressure_levels=[300, 250, 150],\n",
6073
6073
  " cachestore=gcp,\n",
6074
- " # url=\"https://cds-beta.climate.copernicus.eu/api\",\n",
6074
+ " # url=\"https://cds.climate.copernicus.eu/api\",\n",
6075
6075
  " # key=\"<key>\"\n",
6076
6076
  ")"
6077
6077
  ]
@@ -1,7 +1,7 @@
1
1
  """
2
2
  ``pycontrails`` public API.
3
3
 
4
- Copyright 2021-present Breakthrough Energy
4
+ Copyright 2021-present Contrails.org and the Breakthrough Energy Foundation
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.
@@ -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.5'
16
- __version_tuple__ = version_tuple = (0, 54, 5)
15
+ __version__ = version = '0.54.7'
16
+ __version_tuple__ = version_tuple = (0, 54, 7)
@@ -71,6 +71,8 @@ class AircraftPerformanceParams(ModelParams, CommonAircraftPerformanceParams):
71
71
  #: level with zero wind when computing true airspeed. In other words,
72
72
  #: approximate low-altitude true airspeed with the ground speed. Enabling
73
73
  #: this does NOT remove any NaN values in the ``met`` data itself.
74
+ #: In the case that ``met`` is not provided, any missing values are
75
+ #: filled with zero wind.
74
76
  fill_low_altitude_with_zero_wind: bool = False
75
77
 
76
78
 
@@ -96,22 +98,52 @@ class AircraftPerformance(Model):
96
98
 
97
99
  source: Flight
98
100
 
99
- @abc.abstractmethod
100
101
  @overload
101
102
  def eval(self, source: Fleet, **params: Any) -> Fleet: ...
102
103
 
103
- @abc.abstractmethod
104
104
  @overload
105
105
  def eval(self, source: Flight, **params: Any) -> Flight: ...
106
106
 
107
- @abc.abstractmethod
108
107
  @overload
109
108
  def eval(self, source: None = ..., **params: Any) -> NoReturn: ...
110
109
 
111
- @abc.abstractmethod
112
110
  def eval(self, source: Flight | None = None, **params: Any) -> Flight:
113
111
  """Evaluate the aircraft performance model.
114
112
 
113
+ Parameters
114
+ ----------
115
+ source : Flight
116
+ Flight trajectory to evaluate. Can be a :class:`Flight` or :class:`Fleet`.
117
+ params : Any
118
+ Override :attr:`params` with keyword arguments.
119
+
120
+ Returns
121
+ -------
122
+ Flight
123
+ Flight trajectory with aircraft performance data.
124
+ """
125
+ self.update_params(params)
126
+ self.set_source(source)
127
+ self.source = self.require_source_type(Flight)
128
+ self.downselect_met()
129
+ self.set_source_met()
130
+ self._cleanup_indices()
131
+
132
+ # Calculate true airspeed if not included on source
133
+ self.ensure_true_airspeed_on_source()
134
+
135
+ if isinstance(self.source, Fleet):
136
+ fls = [self.eval_flight(fl) for fl in self.source.to_flight_list()]
137
+ self.source = Fleet.from_seq(fls, attrs=self.source.attrs, broadcast_numeric=False)
138
+ return self.source
139
+
140
+ self.source = self.eval_flight(self.source)
141
+ return self.source
142
+
143
+ @abc.abstractmethod
144
+ def eval_flight(self, fl: Flight) -> Flight:
145
+ """Evaluate the aircraft performance model on a single flight trajectory.
146
+
115
147
  The implementing model adds the following fields to the source flight:
116
148
 
117
149
  - ``aircraft_mass``: aircraft mass at each waypoint, [:math:`kg`]
@@ -128,18 +160,6 @@ class AircraftPerformance(Model):
128
160
  - ``max_mach``: maximum Mach number
129
161
  - ``max_altitude``: maximum altitude, [:math:`m`]
130
162
  - ``total_fuel_burn``: total fuel burn, [:math:`kg`]
131
-
132
- Parameters
133
- ----------
134
- source : Flight
135
- Flight trajectory to evaluate.
136
- params : Any
137
- Override :attr:`params` with keyword arguments.
138
-
139
- Returns
140
- -------
141
- Flight
142
- Flight trajectory with aircraft performance data.
143
163
  """
144
164
 
145
165
  @override
@@ -491,7 +511,8 @@ class AircraftPerformance(Model):
491
511
  tas[cond] = self.source.segment_groundspeed()[cond]
492
512
  return tas
493
513
 
494
- wind_available = ("eastward_wind" in self.source and "northward_wind" in self.source) or (
514
+ # Use current cocip convention: eastward_wind on met, u_wind on source
515
+ wind_available = ("u_wind" in self.source and "v_wind" in self.source) or (
495
516
  self.met is not None and "eastward_wind" in self.met and "northward_wind" in self.met
496
517
  )
497
518
 
@@ -508,12 +529,16 @@ class AircraftPerformance(Model):
508
529
  )
509
530
  raise ValueError(msg)
510
531
 
511
- u = interpolate_met(self.met, self.source, "eastward_wind", **self.interp_kwargs)
512
- v = interpolate_met(self.met, self.source, "northward_wind", **self.interp_kwargs)
532
+ u = interpolate_met(self.met, self.source, "eastward_wind", "u_wind", **self.interp_kwargs)
533
+ v = interpolate_met(self.met, self.source, "northward_wind", "v_wind", **self.interp_kwargs)
513
534
 
514
535
  if fill_with_groundspeed:
515
- met_level_max = self.met.data["level"][-1].item() # type: ignore[union-attr]
516
- cond = self.source.level > met_level_max
536
+ if self.met is None:
537
+ cond = np.isnan(u) & np.isnan(v)
538
+ else:
539
+ met_level_max = self.met.data["level"][-1].item() # type: ignore[union-attr]
540
+ cond = self.source.level > met_level_max
541
+
517
542
  # We DON'T overwrite the original u and v arrays already attached to the source
518
543
  u = np.where(cond, 0.0, u)
519
544
  v = np.where(cond, 0.0, v)
@@ -639,28 +664,3 @@ def _fill_low_altitude_with_isa_temperature(vector: GeoVectorDataset, met_level_
639
664
 
640
665
  t_isa = vector.T_isa()
641
666
  air_temperature[cond] = t_isa[cond]
642
-
643
-
644
- def _fill_low_altitude_tas_with_true_groundspeed(fl: Flight, met_level_max: float) -> None:
645
- """Fill low-altitude NaN values in ``true_airspeed`` with ground speed.
646
-
647
- The ``true_airspeed`` param is assumed to have been computed by
648
- interpolating against a gridded wind field that did not necessarily
649
- extend to the surface. This function fills points below the lowest
650
- altitude in the gridded data with ground speed values.
651
-
652
- This function operates in-place and modifies the ``true_airspeed`` field.
653
-
654
- Parameters
655
- ----------
656
- fl : Flight
657
- Flight instance associated with the ``true_airspeed`` data.
658
- met_level_max : float
659
- The maximum level in the met data, [:math:`hPa`].
660
- """
661
- tas = fl["true_airspeed"]
662
- is_nan = np.isnan(tas)
663
- low_alt = fl.level > met_level_max
664
- cond = is_nan & low_alt
665
-
666
- tas[cond] = fl.segment_groundspeed()[cond]
@@ -2,6 +2,8 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import functools
6
+
5
7
  import numpy as np
6
8
  import pandas as pd
7
9
 
@@ -35,6 +37,7 @@ def _download_ourairports_csv() -> pd.DataFrame:
35
37
  )
36
38
 
37
39
 
40
+ @functools.cache
38
41
  def global_airport_database(
39
42
  cachestore: cache.CacheStore | None = None, update_cache: bool = False
40
43
  ) -> pd.DataFrame:
@@ -91,7 +94,7 @@ def global_airport_database(
91
94
  airports = airports.rename(
92
95
  columns={"latitude_deg": "latitude", "longitude_deg": "longitude", "gps_code": "icao_code"},
93
96
  )
94
- airports.fillna({"elevation_ft": 0}, inplace=True)
97
+ airports.fillna({"elevation_ft": 0.0}, inplace=True)
95
98
 
96
99
  # Keep specific airport types used by commercial aviation
97
100
  subset = ("large_airport", "medium_airport", "small_airport", "heliport")
@@ -162,7 +165,7 @@ def find_nearest_airport(
162
165
  ) & airports["latitude"].between((latitude - bbox), (latitude + bbox))
163
166
 
164
167
  # Find the nearest airport from largest to smallest airport type
165
- search_priority = ["large_airport", "medium_airport", "small_airport"]
168
+ search_priority = ("large_airport", "medium_airport", "small_airport")
166
169
 
167
170
  for airport_type in search_priority:
168
171
  is_airport_type = airports["type"] == airport_type
@@ -171,7 +174,7 @@ def find_nearest_airport(
171
174
  if len(nearest_airports) == 1:
172
175
  return nearest_airports["icao_code"].values[0]
173
176
 
174
- elif len(nearest_airports) > 1:
177
+ if len(nearest_airports) > 1:
175
178
  distance = distance_to_airports(
176
179
  nearest_airports,
177
180
  longitude,
@@ -181,8 +184,7 @@ def find_nearest_airport(
181
184
  i_nearest = np.argmin(distance)
182
185
  return nearest_airports["icao_code"].values[i_nearest]
183
186
 
184
- else:
185
- continue
187
+ continue
186
188
 
187
189
  return None
188
190
 
@@ -891,7 +891,7 @@ class Flight(GeoVectorDataset):
891
891
  """
892
892
  methods = "geodesic", "linear"
893
893
  if fill_method not in methods:
894
- raise ValueError(f'Unknown `fill_method`. Supported methods: {", ".join(methods)}')
894
+ raise ValueError(f"Unknown `fill_method`. Supported methods: {', '.join(methods)}")
895
895
 
896
896
  # STEP 0: If self is empty, return an empty flight
897
897
  if not self:
@@ -1388,7 +1388,7 @@ class Flight(GeoVectorDataset):
1388
1388
 
1389
1389
  jump_indices = _antimeridian_index(pd.Series(self["longitude"]))
1390
1390
 
1391
- def _group_to_feature(group: pd.DataFrame) -> dict[str, str | dict[str, Any]]:
1391
+ def _group_to_feature(name: str, group: pd.DataFrame) -> dict[str, str | dict[str, Any]]:
1392
1392
  # assigns a different value to each group of consecutive indices
1393
1393
  subgrouping = group.index.to_series().diff().ne(1).cumsum()
1394
1394
 
@@ -1405,7 +1405,7 @@ class Flight(GeoVectorDataset):
1405
1405
  geometry = {"type": "MultiLineString", "coordinates": multi_ls}
1406
1406
 
1407
1407
  # adding in static properties
1408
- properties: dict[str, Any] = {key: group.name} if key is not None else {}
1408
+ properties: dict[str, Any] = {key: name} if key is not None else {}
1409
1409
  properties.update(self.constants)
1410
1410
  return {"type": "Feature", "geometry": geometry, "properties": properties}
1411
1411
 
@@ -1415,11 +1415,11 @@ class Flight(GeoVectorDataset):
1415
1415
  # create a single group containing all rows of dataframe
1416
1416
  groups = self.dataframe.groupby(lambda _: 0)
1417
1417
 
1418
- features = groups.apply(_group_to_feature, include_groups=False).values.tolist()
1418
+ features = [_group_to_feature(*name_group) for name_group in groups]
1419
1419
  return {"type": "FeatureCollection", "features": features}
1420
1420
 
1421
1421
  def to_traffic(self) -> traffic.core.Flight:
1422
- """Convert to :class:`traffic.core.Flight`instance.
1422
+ """Convert to :class:`traffic.core.Flight` instance.
1423
1423
 
1424
1424
  Returns
1425
1425
  -------
@@ -1982,9 +1982,7 @@ def filter_altitude(
1982
1982
  result[i0:i1] = altitude_filt[i0:i1]
1983
1983
 
1984
1984
  # reapply Savitzky-Golay filter to smooth climb and descent
1985
- result = _sg_filter(result, window_length=kernel_size)
1986
-
1987
- return result
1985
+ return _sg_filter(result, window_length=kernel_size)
1988
1986
 
1989
1987
 
1990
1988
  def segment_duration(
@@ -21,24 +21,24 @@ def to_atc_plan(plan: dict[str, Any]) -> str:
21
21
  --------
22
22
  :func:`parse_atc_plan`
23
23
  """
24
- ret = f'(FPL-{plan["callsign"]}-{plan["flight_rules"]}'
25
- ret += f'{plan["type_of_flight"]}\n'
24
+ ret = f"(FPL-{plan['callsign']}-{plan['flight_rules']}"
25
+ ret += f"{plan['type_of_flight']}\n"
26
26
  ret += "-"
27
27
  if "number_aircraft" in plan and plan["number_aircraft"] <= 10:
28
28
  ret += plan["number_aircraft"]
29
- ret += f'{plan["type_of_aircraft"]}/{plan["wake_category"]}-'
30
- ret += f'{plan["equipment"]}/{plan["transponder"]}\n'
31
- ret += f'-{plan["departure_icao"]}{plan["time"]}\n'
32
- ret += f'-{plan["speed_type"]}{plan["speed"]}{plan["level_type"]}'
33
- ret += f'{plan["level"]} {plan["route"]}\n'
29
+ ret += f"{plan['type_of_aircraft']}/{plan['wake_category']}-"
30
+ ret += f"{plan['equipment']}/{plan['transponder']}\n"
31
+ ret += f"-{plan['departure_icao']}{plan['time']}\n"
32
+ ret += f"-{plan['speed_type']}{plan['speed']}{plan['level_type']}"
33
+ ret += f"{plan['level']} {plan['route']}\n"
34
34
  if "destination_icao" in plan and "duration" in plan:
35
- ret += f'-{plan["destination_icao"]}{plan["duration"]}'
35
+ ret += f"-{plan['destination_icao']}{plan['duration']}"
36
36
  if "alt_icao" in plan:
37
- ret += f' {plan["alt_icao"]}'
37
+ ret += f" {plan['alt_icao']}"
38
38
  if "second_alt_icao" in plan:
39
- ret += f' {plan["second_alt_icao"]}'
39
+ ret += f" {plan['second_alt_icao']}"
40
40
  ret += "\n"
41
- ret += f'-{plan["other_info"]})\n'
41
+ ret += f"-{plan['other_info']})\n"
42
42
  if "supplementary_info" in plan:
43
43
  ret += " ".join([f"{i[0]}/{i[1]}" for i in plan["supplementary_info"].items()])
44
44
 
@@ -150,11 +150,8 @@ class MetBase(ABC, Generic[XArrayType]):
150
150
  """
151
151
  longitude = self.indexes["longitude"].to_numpy()
152
152
  if longitude.dtype != COORD_DTYPE:
153
- raise ValueError(
154
- "Longitude values must be of type float64. "
155
- "Instantiate with 'copy=True' to convert to float64. "
156
- "Instantiate with 'validate=False' to skip validation."
157
- )
153
+ msg = f"Longitude values must have dtype {COORD_DTYPE}. Instantiate with 'copy=True'."
154
+ raise ValueError(msg)
158
155
 
159
156
  if self.is_wrapped:
160
157
  # Relax verification if the longitude has already been processed and wrapped
@@ -194,11 +191,8 @@ class MetBase(ABC, Generic[XArrayType]):
194
191
  """
195
192
  latitude = self.indexes["latitude"].to_numpy()
196
193
  if latitude.dtype != COORD_DTYPE:
197
- raise ValueError(
198
- "Latitude values must be of type float64. "
199
- "Instantiate with 'copy=True' to convert to float64. "
200
- "Instantiate with 'validate=False' to skip validation."
201
- )
194
+ msg = f"Latitude values must have dtype {COORD_DTYPE}. Instantiate with 'copy=True'."
195
+ raise ValueError(msg)
202
196
 
203
197
  if latitude[0] < -90.0:
204
198
  raise ValueError(
@@ -233,8 +227,8 @@ class MetBase(ABC, Generic[XArrayType]):
233
227
  if da.dims != self.dim_order:
234
228
  if key is not None:
235
229
  msg = (
236
- f"Data dimension not transposed on variable '{key}'. Instantiate with"
237
- " 'copy=True'."
230
+ f"Data dimension not transposed on variable '{key}'. "
231
+ "Instantiate with 'copy=True'."
238
232
  )
239
233
  else:
240
234
  msg = "Data dimension not transposed. Instantiate with 'copy=True'."
@@ -258,11 +252,8 @@ class MetBase(ABC, Generic[XArrayType]):
258
252
  self._validate_latitude()
259
253
  self._validate_transpose()
260
254
  if self.data["level"].dtype != COORD_DTYPE:
261
- raise ValueError(
262
- "Level values must be of type float64. "
263
- "Instantiate with 'copy=True' to convert to float64. "
264
- "Instantiate with 'validate=False' to skip validation."
265
- )
255
+ msg = f"Level values must have dtype {COORD_DTYPE}. Instantiate with 'copy=True'."
256
+ raise ValueError(msg)
266
257
 
267
258
  def _preprocess_dims(self, wrap_longitude: bool) -> None:
268
259
  """Confirm DataArray or Dataset include required dimension in a consistent format.
@@ -435,7 +426,7 @@ class MetBase(ABC, Generic[XArrayType]):
435
426
  Assumes the longitude dimension is sorted (this is established by the
436
427
  :class:`MetDataset` or :class:`MetDataArray` constructor).
437
428
 
438
- .. versionchanged 0.26.0::
429
+ .. versionchanged:: 0.26.0
439
430
 
440
431
  The previous implementation checked for the minimum and maximum longitude
441
432
  dimension values to be duplicated. The current implementation only checks for
@@ -492,7 +483,7 @@ class MetBase(ABC, Generic[XArrayType]):
492
483
 
493
484
  Does not yet save in parallel.
494
485
 
495
- .. versionchanged::0.34.1
486
+ .. versionchanged:: 0.34.1
496
487
 
497
488
  If :attr:`cachestore` is None, this method assigns it
498
489
  to new :class:`DiskCacheStore`.
@@ -1178,19 +1169,45 @@ class MetDataset(MetBase):
1178
1169
  }
1179
1170
  return self._get_pycontrails_attr_template("product", supported, examples)
1180
1171
 
1181
- def standardize_variables(self, variables: Iterable[MetVariable]) -> None:
1182
- """Standardize variables **in-place**.
1172
+ @overload
1173
+ def standardize_variables(
1174
+ self, variables: Iterable[MetVariable], inplace: Literal[False] = ...
1175
+ ) -> Self: ...
1176
+
1177
+ @overload
1178
+ def standardize_variables(
1179
+ self, variables: Iterable[MetVariable], inplace: Literal[True]
1180
+ ) -> None: ...
1181
+
1182
+ def standardize_variables(
1183
+ self, variables: Iterable[MetVariable], inplace: bool = False
1184
+ ) -> Self | None:
1185
+ """Standardize variable names.
1186
+
1187
+ .. versionchanged:: 0.54.7
1188
+
1189
+ By default, this method returns a new :class:`MetDataset` instead
1190
+ of renaming in place. To retain the old behavior, set ``inplace=True``.
1183
1191
 
1184
1192
  Parameters
1185
1193
  ----------
1186
1194
  variables : Iterable[MetVariable]
1187
1195
  Data source variables
1196
+ inplace : bool, optional
1197
+ If True, rename variables in place. Otherwise, return a new
1198
+ :class:`MetDataset` with renamed variables.
1188
1199
 
1189
1200
  See Also
1190
1201
  --------
1191
1202
  :func:`standardize_variables`
1192
1203
  """
1193
- standardize_variables(self, variables)
1204
+ data_renamed = standardize_variables(self.data, variables)
1205
+
1206
+ if inplace:
1207
+ self.data = data_renamed
1208
+ return None
1209
+
1210
+ return type(self)._from_fastpath(data_renamed, cachestore=self.cachestore)
1194
1211
 
1195
1212
  @classmethod
1196
1213
  def from_coords(
@@ -2642,7 +2659,7 @@ def downselect(data: XArrayType, bbox: tuple[float, ...]) -> XArrayType:
2642
2659
  return data.where(cond, drop=True)
2643
2660
 
2644
2661
 
2645
- def standardize_variables(ds: DatasetType, variables: Iterable[MetVariable]) -> DatasetType:
2662
+ def standardize_variables(ds: xr.Dataset, variables: Iterable[MetVariable]) -> xr.Dataset:
2646
2663
  """Rename all variables in dataset from short name to standard name.
2647
2664
 
2648
2665
  This function does not change any variables in ``ds`` that are not found in ``variables``.
@@ -2652,8 +2669,7 @@ def standardize_variables(ds: DatasetType, variables: Iterable[MetVariable]) ->
2652
2669
  Parameters
2653
2670
  ----------
2654
2671
  ds : DatasetType
2655
- An :class:`xr.Dataset` or :class:`MetDataset`. When a :class:`MetDataset` is
2656
- passed, the underlying :class:`xr.Dataset` is modified in place.
2672
+ An :class:`xr.Dataset`.
2657
2673
  variables : Iterable[MetVariable]
2658
2674
  Data source variables
2659
2675
 
@@ -2662,14 +2678,6 @@ def standardize_variables(ds: DatasetType, variables: Iterable[MetVariable]) ->
2662
2678
  DatasetType
2663
2679
  Dataset with variables renamed to standard names
2664
2680
  """
2665
- if isinstance(ds, xr.Dataset):
2666
- return _standardize_variables(ds, variables)
2667
-
2668
- ds.data = _standardize_variables(ds.data, variables)
2669
- return ds
2670
-
2671
-
2672
- def _standardize_variables(ds: xr.Dataset, variables: Iterable[MetVariable]) -> xr.Dataset:
2673
2681
  variables_dict: dict[Hashable, str] = {v.short_name: v.standard_name for v in variables}
2674
2682
  name_dict = {var: variables_dict[var] for var in ds.data_vars if var in variables_dict}
2675
2683
  return ds.rename(name_dict)