pycontrails 0.54.6__tar.gz → 0.54.8__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 (281) hide show
  1. {pycontrails-0.54.6 → pycontrails-0.54.8}/.github/workflows/release.yaml +1 -1
  2. {pycontrails-0.54.6 → pycontrails-0.54.8}/.github/workflows/scorecard.yaml +1 -1
  3. {pycontrails-0.54.6 → pycontrails-0.54.8}/CHANGELOG.md +43 -0
  4. {pycontrails-0.54.6 → pycontrails-0.54.8}/NOTICE +1 -1
  5. {pycontrails-0.54.6/pycontrails.egg-info → pycontrails-0.54.8}/PKG-INFO +5 -4
  6. {pycontrails-0.54.6 → pycontrails-0.54.8}/RELEASE.md +1 -1
  7. pycontrails-0.54.8/docs/_static/img/favicon.png +0 -0
  8. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/conf.py +4 -2
  9. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/index.rst +1 -1
  10. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/__init__.py +1 -1
  11. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/_version.py +9 -4
  12. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/core/aircraft_performance.py +12 -30
  13. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/core/airports.py +4 -1
  14. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/core/cache.py +4 -0
  15. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/core/flight.py +4 -4
  16. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/core/flightplan.py +10 -2
  17. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/core/met.py +53 -40
  18. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/core/met_var.py +18 -0
  19. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/core/models.py +79 -3
  20. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/core/rgi_cython.c +195 -195
  21. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/core/vector.py +74 -0
  22. pycontrails-0.54.8/pycontrails/datalib/spire/__init__.py +5 -0
  23. pycontrails-0.54.8/pycontrails/datalib/spire/exceptions.py +62 -0
  24. pycontrails-0.54.8/pycontrails/datalib/spire/spire.py +604 -0
  25. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/accf.py +4 -4
  26. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/cocip/cocip.py +52 -6
  27. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/cocip/cocip_params.py +10 -1
  28. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/cocip/contrail_properties.py +4 -6
  29. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/cocip/output_formats.py +12 -4
  30. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/cocip/radiative_forcing.py +2 -8
  31. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/cocip/unterstrasser_wake_vortex.py +132 -30
  32. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/cocipgrid/cocip_grid.py +14 -11
  33. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/emissions/black_carbon.py +19 -14
  34. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/emissions/emissions.py +8 -8
  35. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/humidity_scaling/humidity_scaling.py +49 -4
  36. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/ps_model/ps_aircraft_params.py +1 -1
  37. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/ps_model/ps_grid.py +22 -22
  38. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/ps_model/ps_model.py +4 -7
  39. pycontrails-0.54.6/pycontrails/models/ps_model/static/ps-aircraft-params-20240524.csv → pycontrails-0.54.8/pycontrails/models/ps_model/static/ps-aircraft-params-20250328.csv +58 -57
  40. pycontrails-0.54.6/pycontrails/models/ps_model/static/ps-synonym-list-20240524.csv → pycontrails-0.54.8/pycontrails/models/ps_model/static/ps-synonym-list-20250328.csv +1 -0
  41. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/tau_cirrus.py +1 -0
  42. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/physics/constants.py +2 -1
  43. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/physics/jet.py +5 -4
  44. pycontrails-0.54.6/pycontrails/physics/static/iata-cargo-load-factors-20241115.csv → pycontrails-0.54.8/pycontrails/physics/static/iata-cargo-load-factors-20250221.csv +3 -0
  45. pycontrails-0.54.6/pycontrails/physics/static/iata-passenger-load-factors-20241115.csv → pycontrails-0.54.8/pycontrails/physics/static/iata-passenger-load-factors-20250221.csv +3 -0
  46. {pycontrails-0.54.6 → pycontrails-0.54.8/pycontrails.egg-info}/PKG-INFO +5 -4
  47. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails.egg-info/SOURCES.txt +7 -5
  48. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails.egg-info/requires.txt +1 -1
  49. {pycontrails-0.54.6 → pycontrails-0.54.8}/pyproject.toml +3 -3
  50. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_apcemm.py +1 -2
  51. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_cocip.py +62 -2
  52. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_cocip_grid.py +19 -1
  53. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_flight.py +1 -1
  54. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_flightplan.py +15 -5
  55. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_geo.py +1 -2
  56. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_grid_to_netcdf.py +2 -1
  57. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_humidity_scaling.py +53 -5
  58. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_met.py +1 -1
  59. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_models.py +22 -0
  60. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_ps_model.py +8 -9
  61. pycontrails-0.54.8/tests/unit/test_spire.py +200 -0
  62. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_unterstrasser_wake_vortex.py +42 -26
  63. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_vector.py +1 -2
  64. pycontrails-0.54.6/docs/_static/img/favicon.png +0 -0
  65. pycontrails-0.54.6/pycontrails/datalib/spire.py +0 -739
  66. pycontrails-0.54.6/tests/unit/test_spire.py +0 -93
  67. {pycontrails-0.54.6 → pycontrails-0.54.8}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  68. {pycontrails-0.54.6 → pycontrails-0.54.8}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  69. {pycontrails-0.54.6 → pycontrails-0.54.8}/.github/dependabot.yaml +0 -0
  70. {pycontrails-0.54.6 → pycontrails-0.54.8}/.github/pull_request_template.md +0 -0
  71. {pycontrails-0.54.6 → pycontrails-0.54.8}/.github/workflows/docs.yaml +0 -0
  72. {pycontrails-0.54.6 → pycontrails-0.54.8}/.github/workflows/doctest.yaml +0 -0
  73. {pycontrails-0.54.6 → pycontrails-0.54.8}/.github/workflows/test.yaml +0 -0
  74. {pycontrails-0.54.6 → pycontrails-0.54.8}/.gitignore +0 -0
  75. {pycontrails-0.54.6 → pycontrails-0.54.8}/.pre-commit-config.yaml +0 -0
  76. {pycontrails-0.54.6 → pycontrails-0.54.8}/.zenodo.json +0 -0
  77. {pycontrails-0.54.6 → pycontrails-0.54.8}/CONTRIBUTING.md +0 -0
  78. {pycontrails-0.54.6 → pycontrails-0.54.8}/LICENSE +0 -0
  79. {pycontrails-0.54.6 → pycontrails-0.54.8}/Makefile +0 -0
  80. {pycontrails-0.54.6 → pycontrails-0.54.8}/README.md +0 -0
  81. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/_static/css/style.css +0 -0
  82. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/_static/img/colab.png +0 -0
  83. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/_static/img/logo-dark.png +0 -0
  84. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/_static/img/logo.png +0 -0
  85. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/_static/pycontrails.bib +0 -0
  86. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/api.rst +0 -0
  87. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/changelog.rst +0 -0
  88. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/contributing.rst +0 -0
  89. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/develop.rst +0 -0
  90. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/flight.rst +0 -0
  91. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/install.rst +0 -0
  92. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/integrations/ACCF.ipynb +0 -0
  93. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/integrations/APCEMM.ipynb +0 -0
  94. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/literature.rst +0 -0
  95. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/meteorology.rst +0 -0
  96. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/models.rst +0 -0
  97. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks/ARCO-ERA5.ipynb +0 -0
  98. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks/AircraftPerformance.ipynb +0 -0
  99. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks/Cache.ipynb +0 -0
  100. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks/CoCiP.ipynb +0 -0
  101. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks/CoCiPGrid.ipynb +0 -0
  102. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks/ECMWF.ipynb +0 -0
  103. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks/Flight.ipynb +0 -0
  104. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks/GFS.ipynb +0 -0
  105. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks/GOES.ipynb +0 -0
  106. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks/ISSR.ipynb +0 -0
  107. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks/Landsat.ipynb +0 -0
  108. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks/Meteorology.ipynb +0 -0
  109. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks/SAC.ipynb +0 -0
  110. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks/Sentinel.ipynb +0 -0
  111. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks/advection.ipynb +0 -0
  112. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks/airports.ipynb +0 -0
  113. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks/data/.gitignore +0 -0
  114. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks/data/flight-ap.csv +0 -0
  115. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks/data/flight-cocip.csv +0 -0
  116. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks/data/flight-fdr.csv +0 -0
  117. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks/data/flight-noisy.csv +0 -0
  118. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks/data/flight.csv +0 -0
  119. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks/data/iagos-flight-landsat.csv +0 -0
  120. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks/data/iagos-flight-sentinel.csv +0 -0
  121. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks/flightplan.ipynb +0 -0
  122. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks/model-levels.ipynb +0 -0
  123. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks/run-cocip-on-flight.ipynb +0 -0
  124. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks/run-cocip-with-fdr.ipynb +0 -0
  125. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks/specific-humidity-interpolation.ipynb +0 -0
  126. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/notebooks.rst +0 -0
  127. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/observations.rst +0 -0
  128. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/tutorials.rst +0 -0
  129. {pycontrails-0.54.6 → pycontrails-0.54.8}/docs/utilities.rst +0 -0
  130. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/core/__init__.py +0 -0
  131. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/core/coordinates.py +0 -0
  132. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/core/fleet.py +0 -0
  133. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/core/fuel.py +0 -0
  134. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/core/interpolation.py +0 -0
  135. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/core/polygon.py +0 -0
  136. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/core/rgi_cython.pyx +0 -0
  137. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/datalib/__init__.py +0 -0
  138. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/datalib/_leo_utils/search.py +0 -0
  139. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/datalib/_leo_utils/static/bq_roi_query.sql +0 -0
  140. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/datalib/_leo_utils/vis.py +0 -0
  141. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/datalib/_met_utils/metsource.py +0 -0
  142. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/datalib/ecmwf/__init__.py +0 -0
  143. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/datalib/ecmwf/arco_era5.py +0 -0
  144. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/datalib/ecmwf/common.py +0 -0
  145. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/datalib/ecmwf/era5.py +0 -0
  146. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/datalib/ecmwf/era5_model_level.py +0 -0
  147. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/datalib/ecmwf/hres.py +0 -0
  148. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/datalib/ecmwf/hres_model_level.py +0 -0
  149. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/datalib/ecmwf/ifs.py +0 -0
  150. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/datalib/ecmwf/model_levels.py +0 -0
  151. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/datalib/ecmwf/static/model_level_dataframe_v20240418.csv +0 -0
  152. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/datalib/ecmwf/variables.py +0 -0
  153. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/datalib/gfs/__init__.py +0 -0
  154. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/datalib/gfs/gfs.py +0 -0
  155. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/datalib/gfs/variables.py +0 -0
  156. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/datalib/goes.py +0 -0
  157. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/datalib/landsat.py +0 -0
  158. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/datalib/sentinel.py +0 -0
  159. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/ext/bada.py +0 -0
  160. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/ext/cirium.py +0 -0
  161. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/ext/empirical_grid.py +0 -0
  162. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/ext/synthetic_flight.py +0 -0
  163. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/__init__.py +0 -0
  164. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/apcemm/__init__.py +0 -0
  165. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/apcemm/apcemm.py +0 -0
  166. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/apcemm/inputs.py +0 -0
  167. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/apcemm/static/apcemm_yaml_template.yaml +0 -0
  168. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/apcemm/utils.py +0 -0
  169. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/cocip/__init__.py +0 -0
  170. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/cocip/cocip_uncertainty.py +0 -0
  171. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/cocip/radiative_heating.py +0 -0
  172. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/cocip/wake_vortex.py +0 -0
  173. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/cocip/wind_shear.py +0 -0
  174. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/cocipgrid/__init__.py +0 -0
  175. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/cocipgrid/cocip_grid_params.py +0 -0
  176. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/dry_advection.py +0 -0
  177. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/emissions/__init__.py +0 -0
  178. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/emissions/ffm2.py +0 -0
  179. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/emissions/static/default-engine-uids.csv +0 -0
  180. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/emissions/static/edb-gaseous-v29b-engines.csv +0 -0
  181. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/emissions/static/edb-nvpm-v29b-engines.csv +0 -0
  182. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/humidity_scaling/__init__.py +0 -0
  183. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/humidity_scaling/quantiles/era5-model-level-quantiles.pq +0 -0
  184. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/humidity_scaling/quantiles/era5-pressure-level-quantiles.pq +0 -0
  185. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/issr.py +0 -0
  186. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/pcc.py +0 -0
  187. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/pcr.py +0 -0
  188. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/ps_model/__init__.py +0 -0
  189. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/ps_model/ps_operational_limits.py +0 -0
  190. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/models/sac.py +0 -0
  191. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/physics/__init__.py +0 -0
  192. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/physics/geo.py +0 -0
  193. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/physics/thermo.py +0 -0
  194. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/physics/units.py +0 -0
  195. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/py.typed +0 -0
  196. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/utils/__init__.py +0 -0
  197. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/utils/dependencies.py +0 -0
  198. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/utils/iteration.py +0 -0
  199. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/utils/json.py +0 -0
  200. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/utils/temp.py +0 -0
  201. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails/utils/types.py +0 -0
  202. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails.egg-info/dependency_links.txt +0 -0
  203. {pycontrails-0.54.6 → pycontrails-0.54.8}/pycontrails.egg-info/top_level.txt +0 -0
  204. {pycontrails-0.54.6 → pycontrails-0.54.8}/setup.cfg +0 -0
  205. {pycontrails-0.54.6 → pycontrails-0.54.8}/setup.py +0 -0
  206. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/__init__.py +0 -0
  207. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/_deprecated.py +0 -0
  208. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/benchmark/cocip/Makefile +0 -0
  209. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/benchmark/cocip/README.md +0 -0
  210. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/benchmark/cocip/benchmark.py +0 -0
  211. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/benchmark/cocip/compare.py +0 -0
  212. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/benchmark/cocip/data.md +0 -0
  213. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/benchmark/cocip/output.py +0 -0
  214. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/benchmark/cocip/review.ipynb +0 -0
  215. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/benchmark/cocip-fortran/README.md +0 -0
  216. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/benchmark/north-atlantic-study/.gcloudignore +0 -0
  217. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/benchmark/north-atlantic-study/README.md +0 -0
  218. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/benchmark/north-atlantic-study/support.py +0 -0
  219. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/benchmark/north-atlantic-study/validate.py +0 -0
  220. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/fixtures/cocip-met.py +0 -0
  221. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/fixtures/cocip-met2.py +0 -0
  222. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/fixtures/ecmwf-met.py +0 -0
  223. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/fixtures/gfs-met.py +0 -0
  224. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/__init__.py +0 -0
  225. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/conftest.py +0 -0
  226. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/static/NOAA_Solar_Calculations_day.csv +0 -0
  227. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/static/cocip-contrail-output.json +0 -0
  228. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/static/cocip-contrail-output2.json +0 -0
  229. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/static/cocip-flight-output.json +0 -0
  230. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/static/cocip-flight-output2.json +0 -0
  231. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/static/cocip-output-contrail-edges.json +0 -0
  232. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/static/cocip-output-flts-20190101-eu.pq +0 -0
  233. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/static/flight-cocip2.csv +0 -0
  234. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/static/flight-meridian.csv +0 -0
  235. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/static/flight-metadata.json +0 -0
  236. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/static/flight-spire-data-cleaning.pq +0 -0
  237. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/static/flight.csv +0 -0
  238. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/static/flt-wypts-20190101-eu.pq +0 -0
  239. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/static/met-20190101-eu.nc +0 -0
  240. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/static/met-accf-pl.nc +0 -0
  241. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/static/met-accf-sl.nc +0 -0
  242. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/static/met-ecmwf-lnsp.nc +0 -0
  243. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/static/met-ecmwf-ml.nc +0 -0
  244. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/static/met-ecmwf-pl.nc +0 -0
  245. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/static/met-ecmwf-sl.nc +0 -0
  246. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/static/met-era5-cocip1.nc +0 -0
  247. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/static/met-era5-cocip2.nc +0 -0
  248. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/static/met-gfs.nc +0 -0
  249. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/static/polygon-bug.nc +0 -0
  250. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/static/rad-20190101-eu.nc +0 -0
  251. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/static/rad-era5-cocip1.nc +0 -0
  252. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/static/rad-era5-cocip2.nc +0 -0
  253. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/static/rad-gfs.nc +0 -0
  254. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_accf.py +0 -0
  255. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_airports.py +0 -0
  256. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_cache.py +0 -0
  257. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_cocip_grid_parity.py +0 -0
  258. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_cocip_radiative_forcing.py +0 -0
  259. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_cocip_uncertainty.py +0 -0
  260. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_coordinates.py +0 -0
  261. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_datalib_metsource.py +0 -0
  262. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_dry_advection.py +0 -0
  263. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_dtypes.py +0 -0
  264. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_ecmwf.py +0 -0
  265. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_emissions.py +0 -0
  266. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_fleet.py +0 -0
  267. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_fuel.py +0 -0
  268. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_gfs.py +0 -0
  269. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_goes.py +0 -0
  270. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_init.py +0 -0
  271. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_interpolation.py +0 -0
  272. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_leo.py +0 -0
  273. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_met_cache.py +0 -0
  274. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_pcc.py +0 -0
  275. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_polygons.py +0 -0
  276. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_sac_issr.py +0 -0
  277. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_tau_cirrus.py +0 -0
  278. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_thermo_sac.py +0 -0
  279. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_units.py +0 -0
  280. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_utils.py +0 -0
  281. {pycontrails-0.54.6 → pycontrails-0.54.8}/tests/unit/test_zarr.py +0 -0
@@ -40,7 +40,7 @@ jobs:
40
40
 
41
41
  # https://cibuildwheel.readthedocs.io/en/stable/options/#testing
42
42
  - name: Build wheels
43
- uses: pypa/cibuildwheel@v2.22
43
+ uses: pypa/cibuildwheel@v2.23
44
44
  env:
45
45
  CIBW_BUILD: cp310-* cp311-* cp312-* cp313-*
46
46
  CIBW_SKIP: '*-win32 *-manylinux_i686 *-musllinux*'
@@ -30,7 +30,7 @@ jobs:
30
30
  - uses: actions/checkout@v4
31
31
 
32
32
  - name: Run OpenSSF Security Scorecard
33
- uses: ossf/scorecard-action@v2.4.0
33
+ uses: ossf/scorecard-action@v2.4.1
34
34
  with:
35
35
  results_file: results.sarif
36
36
  results_format: sarif
@@ -1,5 +1,48 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.54.8
4
+
5
+ ### Features
6
+
7
+ - Update the Poll-Schumann Aircraft Performance model:
8
+ - Support the Support Boeing 737 MAX 10 aircraft type.
9
+ - Update aircraft maximum landing weight, maximum zero fuel weight, operating empty weight, and maximum payload within the static CSVs for existing aircraft types as recommended by Ian Poll.
10
+ - Add `ExponentialBoostLatitudeCorrectionHumidityScaling` calibrated for model-level ERA5 data.
11
+
12
+ ### Internals
13
+
14
+ - Update the `pycontrails.physics.static` CSV files to include newly released global and regional passenger and cargo load factor data from IATA (Oct-2024 to Dec-2024).
15
+ - Attach `n_ice_per_m_0` and `f_surv` to the downwash flight computed in the `Cocip` runtime. This data is now saved as part of the `Cocip` output.
16
+ - Rename and modify `contrail_properties.ice_particle_number` to `contrail_properties.initial_ice_particle_number`.
17
+ - Rename `ValidateTrajectoryHandler.CRUISE_ROCD_THRESHOLD_FPS` -> `ValidateTrajectoryHandler.ROCD_THRESHOLD_FPS`. Update its value from 4.2 ft/sec -> 83.25 ft/sec.
18
+ - Remove the altitude filter on the `ValidateTrajectoryHandler` ROCD check. Now all waypoints are checked for ROCD violations.
19
+ - Correctly parse "DOF" (departure date) field from flight plan in the `flightplan` module.
20
+
21
+ ## v0.54.7
22
+
23
+ ### Features
24
+
25
+ - Add helper `classmethods` to `Model`, `Cocip`, and `CocipGrid` for generating lists of required variables from specific data sources.
26
+ - Add a `ValidateTrajectoryHandler` to the `spire` module to validate spire ADS-B data. This work is experimental and will be improved in future releases.
27
+ - Update Unterstrasser (2016)'s parameterised model of the contrail ice crystal survival fraction to the latest version (Lottermoser & Unterstrasser, 2025). This update:
28
+ - improves the goodness of fit between the parameterised model and LES, and
29
+ - 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.
30
+
31
+ ### Breaking changes
32
+
33
+ - 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)`.
34
+
35
+ ### Fixes
36
+
37
+ - 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.
38
+ - 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.
39
+
40
+ ### Internals
41
+
42
+ - Make `pycontrails` compatible with `pandas 2.0` and `pandas 2.1`.
43
+ - Avoid auto-promotion of float32 to float64 within the `Emissions` model run-time.
44
+ - Add convenience `VectorDataset.get_constant` method.
45
+
3
46
  ## v0.54.6
4
47
 
5
48
  ### 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.2
1
+ Metadata-Version: 2.4
2
2
  Name: pycontrails
3
- Version: 0.54.6
3
+ Version: 0.54.8
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
@@ -111,6 +111,7 @@ Provides-Extra: zarr
111
111
  Requires-Dist: fsspec>=2022.7.1; extra == "zarr"
112
112
  Requires-Dist: gcsfs>=2022.7.1; extra == "zarr"
113
113
  Requires-Dist: zarr>=2.12; extra == "zarr"
114
+ Dynamic: license-file
114
115
 
115
116
  # pycontrails
116
117
 
@@ -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
 
@@ -4,7 +4,7 @@ pycontrails
4
4
 
5
5
  *Python library for modeling contrails and other aviation climate impacts.*
6
6
 
7
- Learn more on `contrails.org <https://contrails.org>`_.
7
+ Learn more at `Contrails.org <https://contrails.org>`_.
8
8
 
9
9
  .. list-table::
10
10
  :widths: 30 70
@@ -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.
@@ -1,8 +1,13 @@
1
- # file generated by setuptools_scm
1
+ # file generated by setuptools-scm
2
2
  # don't change, don't track in version control
3
+
4
+ __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
5
+
3
6
  TYPE_CHECKING = False
4
7
  if TYPE_CHECKING:
5
- from typing import Tuple, Union
8
+ from typing import Tuple
9
+ from typing import Union
10
+
6
11
  VERSION_TUPLE = Tuple[Union[int, str], ...]
7
12
  else:
8
13
  VERSION_TUPLE = object
@@ -12,5 +17,5 @@ __version__: str
12
17
  __version_tuple__: VERSION_TUPLE
13
18
  version_tuple: VERSION_TUPLE
14
19
 
15
- __version__ = version = '0.54.6'
16
- __version_tuple__ = version_tuple = (0, 54, 6)
20
+ __version__ = version = '0.54.8'
21
+ __version_tuple__ = version_tuple = (0, 54, 8)
@@ -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
 
@@ -509,7 +511,8 @@ class AircraftPerformance(Model):
509
511
  tas[cond] = self.source.segment_groundspeed()[cond]
510
512
  return tas
511
513
 
512
- 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 (
513
516
  self.met is not None and "eastward_wind" in self.met and "northward_wind" in self.met
514
517
  )
515
518
 
@@ -526,12 +529,16 @@ class AircraftPerformance(Model):
526
529
  )
527
530
  raise ValueError(msg)
528
531
 
529
- u = interpolate_met(self.met, self.source, "eastward_wind", **self.interp_kwargs)
530
- 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)
531
534
 
532
535
  if fill_with_groundspeed:
533
- met_level_max = self.met.data["level"][-1].item() # type: ignore[union-attr]
534
- 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
+
535
542
  # We DON'T overwrite the original u and v arrays already attached to the source
536
543
  u = np.where(cond, 0.0, u)
537
544
  v = np.where(cond, 0.0, v)
@@ -657,28 +664,3 @@ def _fill_low_altitude_with_isa_temperature(vector: GeoVectorDataset, met_level_
657
664
 
658
665
  t_isa = vector.T_isa()
659
666
  air_temperature[cond] = t_isa[cond]
660
-
661
-
662
- def _fill_low_altitude_tas_with_true_groundspeed(fl: Flight, met_level_max: float) -> None:
663
- """Fill low-altitude NaN values in ``true_airspeed`` with ground speed.
664
-
665
- The ``true_airspeed`` param is assumed to have been computed by
666
- interpolating against a gridded wind field that did not necessarily
667
- extend to the surface. This function fills points below the lowest
668
- altitude in the gridded data with ground speed values.
669
-
670
- This function operates in-place and modifies the ``true_airspeed`` field.
671
-
672
- Parameters
673
- ----------
674
- fl : Flight
675
- Flight instance associated with the ``true_airspeed`` data.
676
- met_level_max : float
677
- The maximum level in the met data, [:math:`hPa`].
678
- """
679
- tas = fl["true_airspeed"]
680
- is_nan = np.isnan(tas)
681
- low_alt = fl.level > met_level_max
682
- cond = is_nan & low_alt
683
-
684
- 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")
@@ -266,6 +266,8 @@ class DiskCacheStore(CacheStore):
266
266
  >>> # put a file directly
267
267
  >>> disk_cache.put("README.md", "test/file.md")
268
268
  'test/file.md'
269
+
270
+ >>> disk_cache.clear() # cleanup
269
271
  """
270
272
 
271
273
  if not pathlib.Path(data_path).is_file():
@@ -312,6 +314,8 @@ class DiskCacheStore(CacheStore):
312
314
  >>> # returns a path
313
315
  >>> disk_cache.get("test/file.md")
314
316
  'cache/test/file.md'
317
+
318
+ >>> disk_cache.clear() # cleanup
315
319
  """
316
320
  return self.path(cache_path)
317
321
 
@@ -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
  -------
@@ -38,10 +38,13 @@ def to_atc_plan(plan: dict[str, Any]) -> str:
38
38
  if "second_alt_icao" in plan:
39
39
  ret += f" {plan['second_alt_icao']}"
40
40
  ret += "\n"
41
- ret += f"-{plan['other_info']})\n"
41
+ ret += f"-{plan['other_info']}"
42
42
  if "supplementary_info" in plan:
43
+ ret += "\n-"
43
44
  ret += " ".join([f"{i[0]}/{i[1]}" for i in plan["supplementary_info"].items()])
44
45
 
46
+ ret += ")"
47
+
45
48
  if ret[-1] == "\n":
46
49
  ret = ret[:-1]
47
50
 
@@ -194,7 +197,12 @@ def parse_atc_plan(atc_plan: str) -> dict[str, str]:
194
197
 
195
198
  # Other info
196
199
  if len(basic) > 8:
197
- flightplan["other_info"] = basic[8]
200
+ info = basic[8]
201
+ idx = info.find("DOF")
202
+ if idx != -1:
203
+ flightplan["departure_date"] = info[idx + 4 : idx + 10]
204
+
205
+ flightplan["other_info"] = info.strip()
198
206
 
199
207
  # Supl. Info
200
208
  if len(basic) > 9:
@@ -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(
@@ -2626,23 +2643,28 @@ def downselect(data: XArrayType, bbox: tuple[float, ...]) -> XArrayType:
2626
2643
  "or length 6 [west, south, min-level, east, north, max-level]"
2627
2644
  )
2628
2645
 
2646
+ if west <= east:
2647
+ # Return a view of the data
2648
+ # If data is lazy, this will not load the data
2649
+ return data.sel(
2650
+ longitude=slice(west, east),
2651
+ latitude=slice(south, north),
2652
+ level=slice(level_min, level_max),
2653
+ )
2654
+
2655
+ # In this case, the bbox spans the antimeridian
2656
+ # If data is lazy, this will load the data (data.where is not lazy AFAIK)
2629
2657
  cond = (
2630
2658
  (data["latitude"] >= south)
2631
2659
  & (data["latitude"] <= north)
2632
2660
  & (data["level"] >= level_min)
2633
2661
  & (data["level"] <= level_max)
2662
+ & ((data["longitude"] >= west) | (data["longitude"] <= east))
2634
2663
  )
2635
-
2636
- # wrapping longitude
2637
- if west <= east:
2638
- cond = cond & (data["longitude"] >= west) & (data["longitude"] <= east)
2639
- else:
2640
- cond = cond & ((data["longitude"] >= west) | (data["longitude"] <= east))
2641
-
2642
2664
  return data.where(cond, drop=True)
2643
2665
 
2644
2666
 
2645
- def standardize_variables(ds: DatasetType, variables: Iterable[MetVariable]) -> DatasetType:
2667
+ def standardize_variables(ds: xr.Dataset, variables: Iterable[MetVariable]) -> xr.Dataset:
2646
2668
  """Rename all variables in dataset from short name to standard name.
2647
2669
 
2648
2670
  This function does not change any variables in ``ds`` that are not found in ``variables``.
@@ -2652,8 +2674,7 @@ def standardize_variables(ds: DatasetType, variables: Iterable[MetVariable]) ->
2652
2674
  Parameters
2653
2675
  ----------
2654
2676
  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.
2677
+ An :class:`xr.Dataset`.
2657
2678
  variables : Iterable[MetVariable]
2658
2679
  Data source variables
2659
2680
 
@@ -2662,14 +2683,6 @@ def standardize_variables(ds: DatasetType, variables: Iterable[MetVariable]) ->
2662
2683
  DatasetType
2663
2684
  Dataset with variables renamed to standard names
2664
2685
  """
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
2686
  variables_dict: dict[Hashable, str] = {v.short_name: v.standard_name for v in variables}
2674
2687
  name_dict = {var: variables_dict[var] for var in ds.data_vars if var in variables_dict}
2675
2688
  return ds.rename(name_dict)
@@ -367,3 +367,21 @@ TOAOutgoingLongwaveFlux = MetVariable(
367
367
  '"flux" implies per unit area, called "flux density" in physics.'
368
368
  ),
369
369
  )
370
+
371
+ PRESSURE_LEVEL_VARIABLES = [
372
+ AirTemperature,
373
+ SpecificHumidity,
374
+ RelativeHumidity,
375
+ Geopotential,
376
+ GeopotentialHeight,
377
+ EastwardWind,
378
+ NorthwardWind,
379
+ VerticalVelocity,
380
+ MassFractionOfCloudLiquidWaterInAir,
381
+ MassFractionOfCloudIceInAir,
382
+ CloudAreaFractionInAtmosphereLayer,
383
+ ]
384
+
385
+ SINGLE_LEVEL_VARIABLES = [SurfacePressure, TOANetDownwardShortwaveFlux, TOAOutgoingLongwaveFlux]
386
+
387
+ MET_VARIABLES = PRESSURE_LEVEL_VARIABLES + SINGLE_LEVEL_VARIABLES
@@ -22,8 +22,10 @@ import xarray as xr
22
22
  from pycontrails.core.fleet import Fleet
23
23
  from pycontrails.core.flight import Flight
24
24
  from pycontrails.core.met import MetDataArray, MetDataset, MetVariable, originates_from_ecmwf
25
- from pycontrails.core.met_var import SpecificHumidity
25
+ from pycontrails.core.met_var import MET_VARIABLES, SpecificHumidity
26
26
  from pycontrails.core.vector import GeoVectorDataset
27
+ from pycontrails.datalib.ecmwf import ECMWF_VARIABLES
28
+ from pycontrails.datalib.gfs import GFS_VARIABLES
27
29
  from pycontrails.utils.json import NumpyEncoder
28
30
  from pycontrails.utils.types import type_guard
29
31
 
@@ -179,8 +181,10 @@ class Model(ABC):
179
181
 
180
182
  #: Required meteorology pressure level variables.
181
183
  #: Each element in the list is a :class:`MetVariable` or a ``tuple[MetVariable]``.
182
- #: If element is a ``tuple[MetVariable]``, the variable depends on the data source.
183
- #: Only one variable in the tuple is required.
184
+ #: If element is a ``tuple[MetVariable]``, the variable depends on the data source
185
+ #: and the tuple must include entries for a model-agnostic variable,
186
+ #: an ECMWF-specific variable, and a GFS-specific variable.
187
+ #: Only one of the three variable in the tuple is required for model evaluation.
184
188
  met_variables: tuple[MetVariable | tuple[MetVariable, ...], ...]
185
189
 
186
190
  #: Set of required parameters if processing already complete on ``met`` input.
@@ -276,6 +280,42 @@ class Model(ABC):
276
280
 
277
281
  return hashlib.sha1(bytes(_hash, "utf-8")).hexdigest()
278
282
 
283
+ @classmethod
284
+ def generic_met_variables(cls) -> tuple[MetVariable, ...]:
285
+ """Return a model-agnostic list of required meteorology variables.
286
+
287
+ Returns
288
+ -------
289
+ tuple[MetVariable]
290
+ List of model-agnostic variants of required variables
291
+ """
292
+ available = set(MET_VARIABLES)
293
+ return tuple(_find_match(required, available) for required in cls.met_variables)
294
+
295
+ @classmethod
296
+ def ecmwf_met_variables(cls) -> tuple[MetVariable, ...]:
297
+ """Return an ECMWF-specific list of required meteorology variables.
298
+
299
+ Returns
300
+ -------
301
+ tuple[MetVariable]
302
+ List of ECMWF-specific variants of required variables
303
+ """
304
+ available = set(ECMWF_VARIABLES)
305
+ return tuple(_find_match(required, available) for required in cls.met_variables)
306
+
307
+ @classmethod
308
+ def gfs_met_variables(cls) -> tuple[MetVariable, ...]:
309
+ """Return a GFS-specific list of required meteorology variables.
310
+
311
+ Returns
312
+ -------
313
+ tuple[MetVariable]
314
+ List of GFS-specific variants of required variables
315
+ """
316
+ available = set(GFS_VARIABLES)
317
+ return tuple(_find_match(required, available) for required in cls.met_variables)
318
+
279
319
  def _verify_met(self) -> None:
280
320
  """Verify integrity of :attr:`met`.
281
321
 
@@ -805,6 +845,42 @@ def _interp_grid_to_grid(
805
845
  raise NotImplementedError(msg)
806
846
 
807
847
 
848
+ def _find_match(
849
+ required: MetVariable | Sequence[MetVariable], available: set[MetVariable]
850
+ ) -> MetVariable:
851
+ """Find match for required met variable in list of data-source-specific met variables.
852
+
853
+ Parameters
854
+ ----------
855
+ required : MetVariable | Sequence[MetVariable]
856
+ Required met variable
857
+
858
+ available : Sequence[MetVariable]
859
+ Collection of data-source-specific met variables
860
+
861
+ Returns
862
+ -------
863
+ MetVariable
864
+ Match for required met variable in collection of data-source-specific met variables
865
+
866
+ Raises
867
+ ------
868
+ KeyError
869
+ Raised if not match is found
870
+ """
871
+ if isinstance(required, MetVariable):
872
+ return required
873
+
874
+ for var in required:
875
+ if var in available:
876
+ return var
877
+
878
+ required_keys = [v.standard_name for v in required]
879
+ available_keys = [v.standard_name for v in available]
880
+ msg = f"None of {required_keys} match variable in {available_keys}"
881
+ raise KeyError(msg)
882
+
883
+
808
884
  def _raise_missing_met_var(var: MetVariable | Sequence[MetVariable]) -> NoReturn:
809
885
  """Raise KeyError on missing met variable.
810
886