metrust 0.4.0__tar.gz → 0.4.2__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.
Files changed (225) hide show
  1. metrust-0.4.2/BENCHMARKS.md +236 -0
  2. {metrust-0.4.0 → metrust-0.4.2}/Cargo.lock +1 -1
  3. {metrust-0.4.0 → metrust-0.4.2}/Cargo.toml +2 -2
  4. {metrust-0.4.0 → metrust-0.4.2}/PKG-INFO +9 -7
  5. {metrust-0.4.0 → metrust-0.4.2}/README.md +7 -5
  6. {metrust-0.4.0 → metrust-0.4.2}/crates/metrust/Cargo.toml +1 -1
  7. {metrust-0.4.0 → metrust-0.4.2}/crates/metrust/src/calc/atmo.rs +7 -4
  8. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-math/src/thermo.rs +15 -6
  9. {metrust-0.4.0 → metrust-0.4.2}/docs/compatibility.md +5 -1
  10. {metrust-0.4.0 → metrust-0.4.2}/docs/guides/migration.md +1 -1
  11. {metrust-0.4.0 → metrust-0.4.2}/docs/index.md +6 -6
  12. {metrust-0.4.0 → metrust-0.4.2}/docs/methodology/index.md +1 -1
  13. {metrust-0.4.0 → metrust-0.4.2}/mkdocs.yml +1 -1
  14. {metrust-0.4.0 → metrust-0.4.2}/pyproject.toml +2 -2
  15. {metrust-0.4.0 → metrust-0.4.2}/python/metrust/calc/__init__.py +3320 -1091
  16. metrust-0.4.2/tests/benchmarks/bench_01_hrrr_severe.py +1075 -0
  17. metrust-0.4.2/tests/benchmarks/bench_02_gfs_upper_air.py +831 -0
  18. metrust-0.4.2/tests/benchmarks/bench_03_rap_warm_front.py +900 -0
  19. metrust-0.4.2/tests/benchmarks/bench_04_nam_winter.py +846 -0
  20. metrust-0.4.2/tests/benchmarks/bench_05_hrrr_supercell.py +1152 -0
  21. metrust-0.4.2/tests/benchmarks/bench_06_gfs_jet.py +740 -0
  22. metrust-0.4.2/tests/benchmarks/bench_07_hrrr_fire.py +802 -0
  23. metrust-0.4.2/tests/benchmarks/bench_08_era5_tropical.py +1031 -0
  24. metrust-0.4.2/tests/benchmarks/bench_09_rap_aviation.py +1099 -0
  25. metrust-0.4.2/tests/benchmarks/bench_10_hrrr_squall.py +1375 -0
  26. metrust-0.4.2/tests/benchmarks/bench_11_gfs_cold_air.py +920 -0
  27. metrust-0.4.2/tests/benchmarks/bench_12_hrrr_pbl.py +1350 -0
  28. {metrust-0.4.0 → metrust-0.4.2}/tests/test_metpy_dropin_compat.py +165 -2
  29. metrust-0.4.2/tests/test_runtime_parity.py +442 -0
  30. metrust-0.4.2/tests/test_runtime_parity_interp_dataset.py +242 -0
  31. metrust-0.4.2/tests/test_runtime_parity_kinematics_extra.py +248 -0
  32. metrust-0.4.2/tests/test_runtime_parity_remaining.py +93 -0
  33. metrust-0.4.2/tests/test_runtime_parity_sounding_core.py +204 -0
  34. metrust-0.4.2/tests/test_runtime_parity_thermo_layers.py +251 -0
  35. metrust-0.4.2/tests/test_runtime_parity_utils_misc.py +226 -0
  36. metrust-0.4.2/tests/test_runtime_parity_wind_profiles.py +277 -0
  37. metrust-0.4.2/tests/test_signature_parity.py +53 -0
  38. {metrust-0.4.0 → metrust-0.4.2}/.github/workflows/ci.yml +0 -0
  39. {metrust-0.4.0 → metrust-0.4.2}/.github/workflows/docs.yml +0 -0
  40. {metrust-0.4.0 → metrust-0.4.2}/.github/workflows/release.yml +0 -0
  41. {metrust-0.4.0 → metrust-0.4.2}/.gitignore +0 -0
  42. {metrust-0.4.0 → metrust-0.4.2}/VERIFICATION.md +0 -0
  43. {metrust-0.4.0 → metrust-0.4.2}/benches/bench_hrrr.py +0 -0
  44. {metrust-0.4.0 → metrust-0.4.2}/benches/bench_hrrr_vs_metpy.py +0 -0
  45. {metrust-0.4.0 → metrust-0.4.2}/benches/bench_python.py +0 -0
  46. {metrust-0.4.0 → metrust-0.4.2}/crates/metrust/benches/calc_bench.rs +0 -0
  47. {metrust-0.4.0 → metrust-0.4.2}/crates/metrust/src/calc/kinematics.rs +0 -0
  48. {metrust-0.4.0 → metrust-0.4.2}/crates/metrust/src/calc/mod.rs +0 -0
  49. {metrust-0.4.0 → metrust-0.4.2}/crates/metrust/src/calc/severe.rs +0 -0
  50. {metrust-0.4.0 → metrust-0.4.2}/crates/metrust/src/calc/smooth.rs +0 -0
  51. {metrust-0.4.0 → metrust-0.4.2}/crates/metrust/src/calc/thermo.rs +0 -0
  52. {metrust-0.4.0 → metrust-0.4.2}/crates/metrust/src/calc/utils.rs +0 -0
  53. {metrust-0.4.0 → metrust-0.4.2}/crates/metrust/src/calc/wind.rs +0 -0
  54. {metrust-0.4.0 → metrust-0.4.2}/crates/metrust/src/constants.rs +0 -0
  55. {metrust-0.4.0 → metrust-0.4.2}/crates/metrust/src/interpolate/mod.rs +0 -0
  56. {metrust-0.4.0 → metrust-0.4.2}/crates/metrust/src/io/gempak.rs +0 -0
  57. {metrust-0.4.0 → metrust-0.4.2}/crates/metrust/src/io/gempak_dm.rs +0 -0
  58. {metrust-0.4.0 → metrust-0.4.2}/crates/metrust/src/io/gempak_sounding.rs +0 -0
  59. {metrust-0.4.0 → metrust-0.4.2}/crates/metrust/src/io/gempak_surface.rs +0 -0
  60. {metrust-0.4.0 → metrust-0.4.2}/crates/metrust/src/io/gini.rs +0 -0
  61. {metrust-0.4.0 → metrust-0.4.2}/crates/metrust/src/io/level3.rs +0 -0
  62. {metrust-0.4.0 → metrust-0.4.2}/crates/metrust/src/io/metar.rs +0 -0
  63. {metrust-0.4.0 → metrust-0.4.2}/crates/metrust/src/io/mod.rs +0 -0
  64. {metrust-0.4.0 → metrust-0.4.2}/crates/metrust/src/io/station.rs +0 -0
  65. {metrust-0.4.0 → metrust-0.4.2}/crates/metrust/src/io/wpc.rs +0 -0
  66. {metrust-0.4.0 → metrust-0.4.2}/crates/metrust/src/lib.rs +0 -0
  67. {metrust-0.4.0 → metrust-0.4.2}/crates/metrust/src/plots/mod.rs +0 -0
  68. {metrust-0.4.0 → metrust-0.4.2}/crates/metrust/src/projections.rs +0 -0
  69. {metrust-0.4.0 → metrust-0.4.2}/crates/metrust/src/units.rs +0 -0
  70. {metrust-0.4.0 → metrust-0.4.2}/crates/metrust/tests/test_gempak.rs +0 -0
  71. {metrust-0.4.0 → metrust-0.4.2}/crates/metrust/tests/test_new_functions.rs +0 -0
  72. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/Cargo.toml +0 -0
  73. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/composite.rs +0 -0
  74. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/download/cache.rs +0 -0
  75. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/download/catalog.rs +0 -0
  76. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/download/client.rs +0 -0
  77. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/download/fallback.rs +0 -0
  78. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/download/idx.rs +0 -0
  79. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/download/mod.rs +0 -0
  80. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/download/sources.rs +0 -0
  81. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/download/streaming.rs +0 -0
  82. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/dynamics.rs +0 -0
  83. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/error.rs +0 -0
  84. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/grib2/grid.rs +0 -0
  85. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/grib2/mod.rs +0 -0
  86. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/grib2/ops.rs +0 -0
  87. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/grib2/parser.rs +0 -0
  88. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/grib2/search.rs +0 -0
  89. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/grib2/streaming.rs +0 -0
  90. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/grib2/tables.rs +0 -0
  91. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/grib2/tests.rs +0 -0
  92. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/grib2/unpack.rs +0 -0
  93. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/grib2/writer.rs +0 -0
  94. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/gridmath.rs +0 -0
  95. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/lib.rs +0 -0
  96. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/metfuncs.rs +0 -0
  97. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/models/cfs.rs +0 -0
  98. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/models/ecmwf.rs +0 -0
  99. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/models/era5.rs +0 -0
  100. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/models/gefs.rs +0 -0
  101. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/models/gfs.rs +0 -0
  102. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/models/href.rs +0 -0
  103. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/models/hrrr.rs +0 -0
  104. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/models/hrrr_ak.rs +0 -0
  105. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/models/latest.rs +0 -0
  106. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/models/mod.rs +0 -0
  107. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/models/mrms.rs +0 -0
  108. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/models/nam.rs +0 -0
  109. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/models/nbm.rs +0 -0
  110. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/models/rap.rs +0 -0
  111. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/models/rrfs.rs +0 -0
  112. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/models/rtma.rs +0 -0
  113. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/models/sref.rs +0 -0
  114. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/models/urma.rs +0 -0
  115. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/models/wpc.rs +0 -0
  116. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/products.rs +0 -0
  117. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/projection.rs +0 -0
  118. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/regrid.rs +0 -0
  119. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/render/ansi.rs +0 -0
  120. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/render/colormap.rs +0 -0
  121. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/render/contour.rs +0 -0
  122. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/render/cross_section.rs +0 -0
  123. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/render/encode.rs +0 -0
  124. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/render/filled_contour.rs +0 -0
  125. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/render/hodograph.rs +0 -0
  126. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/render/mod.rs +0 -0
  127. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/render/overlay.rs +0 -0
  128. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/render/raster.rs +0 -0
  129. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/render/skewt.rs +0 -0
  130. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-core/src/render/station.rs +0 -0
  131. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-field/Cargo.toml +0 -0
  132. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-field/src/error.rs +0 -0
  133. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-field/src/field.rs +0 -0
  134. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-field/src/lib.rs +0 -0
  135. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-field/src/meta.rs +0 -0
  136. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-field/src/projection.rs +0 -0
  137. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-field/src/radial.rs +0 -0
  138. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-field/src/site.rs +0 -0
  139. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-field/src/sounding.rs +0 -0
  140. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-field/src/time.rs +0 -0
  141. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-math/Cargo.toml +0 -0
  142. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-math/src/composite.rs +0 -0
  143. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-math/src/dynamics.rs +0 -0
  144. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-math/src/gridmath.rs +0 -0
  145. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-math/src/interpolate.rs +0 -0
  146. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-math/src/lib.rs +0 -0
  147. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-math/src/regrid.rs +0 -0
  148. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-radar/Cargo.toml +0 -0
  149. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-radar/src/cells.rs +0 -0
  150. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-radar/src/color_table.rs +0 -0
  151. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-radar/src/derived.rs +0 -0
  152. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-radar/src/detection.rs +0 -0
  153. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-radar/src/level2.rs +0 -0
  154. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-radar/src/lib.rs +0 -0
  155. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-radar/src/products.rs +0 -0
  156. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-radar/src/render.rs +0 -0
  157. {metrust-0.4.0 → metrust-0.4.2}/crates/wx-radar/src/sites.rs +0 -0
  158. {metrust-0.4.0 → metrust-0.4.2}/demos/hrrr_diffusion_training.md +0 -0
  159. {metrust-0.4.0 → metrust-0.4.2}/docs/api/atmospheric.md +0 -0
  160. {metrust-0.4.0 → metrust-0.4.2}/docs/api/grid-composites.md +0 -0
  161. {metrust-0.4.0 → metrust-0.4.2}/docs/api/io.md +0 -0
  162. {metrust-0.4.0 → metrust-0.4.2}/docs/api/kinematics.md +0 -0
  163. {metrust-0.4.0 → metrust-0.4.2}/docs/api/moisture.md +0 -0
  164. {metrust-0.4.0 → metrust-0.4.2}/docs/api/severe.md +0 -0
  165. {metrust-0.4.0 → metrust-0.4.2}/docs/api/smoothing.md +0 -0
  166. {metrust-0.4.0 → metrust-0.4.2}/docs/api/thermodynamics.md +0 -0
  167. {metrust-0.4.0 → metrust-0.4.2}/docs/api/units.md +0 -0
  168. {metrust-0.4.0 → metrust-0.4.2}/docs/api/wind.md +0 -0
  169. {metrust-0.4.0 → metrust-0.4.2}/docs/guides/arrays.md +0 -0
  170. {metrust-0.4.0 → metrust-0.4.2}/docs/guides/installation.md +0 -0
  171. {metrust-0.4.0 → metrust-0.4.2}/docs/methodology/architecture.md +0 -0
  172. {metrust-0.4.0 → metrust-0.4.2}/docs/methodology/grid-kinematics.md +0 -0
  173. {metrust-0.4.0 → metrust-0.4.2}/docs/methodology/interpolation.md +0 -0
  174. {metrust-0.4.0 → metrust-0.4.2}/docs/methodology/io-formats.md +0 -0
  175. {metrust-0.4.0 → metrust-0.4.2}/docs/methodology/metpy-compatibility.md +0 -0
  176. {metrust-0.4.0 → metrust-0.4.2}/docs/methodology/moisture.md +0 -0
  177. {metrust-0.4.0 → metrust-0.4.2}/docs/methodology/parallelism.md +0 -0
  178. {metrust-0.4.0 → metrust-0.4.2}/docs/methodology/severe-weather.md +0 -0
  179. {metrust-0.4.0 → metrust-0.4.2}/docs/methodology/smoothing.md +0 -0
  180. {metrust-0.4.0 → metrust-0.4.2}/docs/methodology/thermodynamics.md +0 -0
  181. {metrust-0.4.0 → metrust-0.4.2}/docs/methodology/units-and-pint.md +0 -0
  182. {metrust-0.4.0 → metrust-0.4.2}/docs/methodology/wind.md +0 -0
  183. {metrust-0.4.0 → metrust-0.4.2}/docs/performance.md +0 -0
  184. {metrust-0.4.0 → metrust-0.4.2}/docs/tutorials/first-grid.md +0 -0
  185. {metrust-0.4.0 → metrust-0.4.2}/docs/tutorials/first-sounding.md +0 -0
  186. {metrust-0.4.0 → metrust-0.4.2}/docs/tutorials/reading-the-numbers.md +0 -0
  187. {metrust-0.4.0 → metrust-0.4.2}/docs/tutorials/recipes.md +0 -0
  188. {metrust-0.4.0 → metrust-0.4.2}/docs/tutorials/weather-101.md +0 -0
  189. {metrust-0.4.0 → metrust-0.4.2}/examples/cookbook_500hpa_grid.py +0 -0
  190. {metrust-0.4.0 → metrust-0.4.2}/examples/cookbook_sounding.py +0 -0
  191. {metrust-0.4.0 → metrust-0.4.2}/examples/sounderpy_dropin.py +0 -0
  192. {metrust-0.4.0 → metrust-0.4.2}/python/metrust/__init__.py +0 -0
  193. {metrust-0.4.0 → metrust-0.4.2}/python/metrust/constants/__init__.py +0 -0
  194. {metrust-0.4.0 → metrust-0.4.2}/python/metrust/interpolate/__init__.py +0 -0
  195. {metrust-0.4.0 → metrust-0.4.2}/python/metrust/io/__init__.py +0 -0
  196. {metrust-0.4.0 → metrust-0.4.2}/python/metrust/plots/__init__.py +0 -0
  197. {metrust-0.4.0 → metrust-0.4.2}/python/metrust/units.py +0 -0
  198. {metrust-0.4.0 → metrust-0.4.2}/python/metrust/xarray.py +0 -0
  199. {metrust-0.4.0 → metrust-0.4.2}/src/lib.rs +0 -0
  200. {metrust-0.4.0 → metrust-0.4.2}/src/py_atmo.rs +0 -0
  201. {metrust-0.4.0 → metrust-0.4.2}/src/py_constants.rs +0 -0
  202. {metrust-0.4.0 → metrust-0.4.2}/src/py_interpolate.rs +0 -0
  203. {metrust-0.4.0 → metrust-0.4.2}/src/py_io.rs +0 -0
  204. {metrust-0.4.0 → metrust-0.4.2}/src/py_kinematics.rs +0 -0
  205. {metrust-0.4.0 → metrust-0.4.2}/src/py_severe.rs +0 -0
  206. {metrust-0.4.0 → metrust-0.4.2}/src/py_smooth.rs +0 -0
  207. {metrust-0.4.0 → metrust-0.4.2}/src/py_thermo.rs +0 -0
  208. {metrust-0.4.0 → metrust-0.4.2}/src/py_utils.rs +0 -0
  209. {metrust-0.4.0 → metrust-0.4.2}/src/py_wind.rs +0 -0
  210. {metrust-0.4.0 → metrust-0.4.2}/tests/api_audit_calc.md +0 -0
  211. {metrust-0.4.0 → metrust-0.4.2}/tests/api_audit_other.md +0 -0
  212. {metrust-0.4.0 → metrust-0.4.2}/tests/benchmark.py +0 -0
  213. {metrust-0.4.0 → metrust-0.4.2}/tests/benchmark_gpu.py +0 -0
  214. {metrust-0.4.0 → metrust-0.4.2}/tests/sounding_3way.py +0 -0
  215. {metrust-0.4.0 → metrust-0.4.2}/tests/sounding_comparison.png +0 -0
  216. {metrust-0.4.0 → metrust-0.4.2}/tests/test_gpu_backend.py +0 -0
  217. {metrust-0.4.0 → metrust-0.4.2}/tests/test_python_compat.py +0 -0
  218. {metrust-0.4.0 → metrust-0.4.2}/tests/verify_constants.py +0 -0
  219. {metrust-0.4.0 → metrust-0.4.2}/tests/verify_edge_cases.py +0 -0
  220. {metrust-0.4.0 → metrust-0.4.2}/tests/verify_kinematics.py +0 -0
  221. {metrust-0.4.0 → metrust-0.4.2}/tests/verify_severe_atmo.py +0 -0
  222. {metrust-0.4.0 → metrust-0.4.2}/tests/verify_smooth_interp.py +0 -0
  223. {metrust-0.4.0 → metrust-0.4.2}/tests/verify_thermo.py +0 -0
  224. {metrust-0.4.0 → metrust-0.4.2}/tests/verify_units.py +0 -0
  225. {metrust-0.4.0 → metrust-0.4.2}/tests/verify_wind.py +0 -0
@@ -0,0 +1,236 @@
1
+ # Benchmarks
2
+
3
+ All results on **NVIDIA GeForce RTX 5090** (34.2 GB VRAM, Blackwell, 21,760 CUDA cores) with CuPy 14.0.1, met-cu 0.2.1, metrust 0.4.0. CPU is a 32-core AMD Ryzen with rayon parallelism.
4
+
5
+ ## Three-Way Comparison: MetPy vs Rust vs CUDA
6
+
7
+ Real HRRR model output (40 isobaric levels, 1059 × 1799 grid, ~1.9M points per level). `python tests/benchmark_gpu.py`
8
+
9
+ ### Scalar Thermodynamics (2D: 1059×1799)
10
+
11
+ | Function | MetPy | Rust | CUDA | Rust/MetPy | CUDA/Rust |
12
+ |---|---:|---:|---:|---:|---:|
13
+ | potential_temperature ★ | 11.2 ms | 13.0 ms | 7.9 ms | 0.9x | 1.6x |
14
+ | equiv_potential_temperature ★ | 303.1 ms | 16.5 ms | 9.1 ms | **18x** | 1.8x |
15
+ | dewpoint ★ | 33.6 ms | 10.8 ms | 8.8 ms | 3.1x | 1.2x |
16
+ | saturation_vapor_pressure | 66.7 ms | 8.2 ms | — | 8.1x | — |
17
+ | saturation_mixing_ratio | 78.6 ms | 13.7 ms | — | 5.7x | — |
18
+ | dewpoint_from_rh | 110.3 ms | 7.6 ms | — | **14x** | — |
19
+ | rh_from_dewpoint | 138.7 ms | 10.9 ms | — | **13x** | — |
20
+ | virtual_temperature | 31.1 ms | 19.7 ms | — | 1.6x | — |
21
+ | mixing_ratio | 11.6 ms | 8.5 ms | — | 1.4x | — |
22
+ | wet_bulb_temperature | >10 min | 26.9 ms | — | **>22,000x** | — |
23
+
24
+ ### Grid Kinematics (2D: 1059×1799)
25
+
26
+ | Function | MetPy | Rust | CUDA | Rust/MetPy | CUDA/Rust |
27
+ |---|---:|---:|---:|---:|---:|
28
+ | vorticity ★ | 98.3 ms | 92.8 ms | 9.3 ms | 1.1x | **10x** |
29
+ | divergence | 96.1 ms | 91.2 ms | — | 1.1x | — |
30
+ | frontogenesis ★ | 733.0 ms | 339.4 ms | 12.2 ms | 2.2x | **28x** |
31
+ | q_vector ★ | 390.3 ms | 310.1 ms | 10.8 ms | 1.3x | **29x** |
32
+ | advection | 161.8 ms | 87.7 ms | — | 1.8x | — |
33
+
34
+ ### 1D Sounding (40 levels, single column)
35
+
36
+ | Function | MetPy | Rust | Rust/MetPy |
37
+ |---|---:|---:|---:|
38
+ | parcel_profile | 5.5 ms | 0.074 ms | **74x** |
39
+ | cape_cin | 1.4 ms | 0.254 ms | 5.4x |
40
+ | lcl | 0.118 ms | 0.065 ms | 1.8x |
41
+ | lfc | 6.7 ms | 0.107 ms | **62x** |
42
+ | el | 6.6 ms | 0.112 ms | **59x** |
43
+ | precipitable_water | 2.1 ms | 0.057 ms | **36x** |
44
+
45
+ ### Grid Composites (3D: 40×1059×1799 → 2D)
46
+
47
+ MetPy has no grid-level equivalents for these functions.
48
+
49
+ | Function | Rust | CUDA | CUDA/Rust |
50
+ |---|---:|---:|---:|
51
+ | compute_cape_cin ★ | 2.96 s | 674.5 ms | **4.4x** |
52
+ | compute_srh ★ | 223.5 ms | 135.8 ms | 1.6x |
53
+ | compute_shear ★ | 190.4 ms | 166.5 ms | 1.1x |
54
+ | compute_pw ★ | 191.6 ms | 107.9 ms | 1.8x |
55
+ | composite_refl_hydrometeors ★ | 154.2 ms | 232.2 ms | 0.7x |
56
+
57
+ ### Summary
58
+
59
+ | Category | MetPy | Rust | CUDA |
60
+ |---|---:|---:|---:|
61
+ | Scalar thermo (×10) | 785 ms | 136 ms | 26 ms |
62
+ | Grid kinematics (×5) | 1.48 s | 921 ms | 32 ms |
63
+ | 1D sounding (×6) | 22 ms | 0.67 ms | — |
64
+ | Grid composites (×5) | — | 3.72 s | 1.32 s |
65
+ | **★ GPU-eligible total** | — | **4.50 s** | **1.37 s (3.3x)** |
66
+
67
+ ★ = dispatches to CUDA when `set_backend("gpu")`.
68
+
69
+ ---
70
+
71
+ ## Real-Data Verification Benchmarks
72
+
73
+ 12 independent scenarios using actual HRRR and GFS GRIB2 data, verifying correctness across all 4 backends (MetPy, metrust CPU, met-cu GPU, metrust GPU) with ~230 deep statistical checks.
74
+
75
+ ### Timing Highlights on Real Atmospheric Data
76
+
77
+ | Function | Grid | MetPy | Rust | met-cu GPU | vs MetPy | vs Rust |
78
+ |---|---|---:|---:|---:|---:|---:|
79
+ | frontogenesis | 721×1440 | 388 ms | 154 ms | 3.8 ms | **101x** | **40x** |
80
+ | equiv_potential_temp | 1059×1799 | 13.7 s | 464 ms | 163 ms | **84x** | **2.8x** |
81
+ | compute_srh | 200×200 | — | 124 ms | 2.7 ms | — | **46x** |
82
+ | q_vector | 1059×1799 | — | 115 ms | 4.6 ms | — | **25x** |
83
+ | compute_cape_cin | 200×200 | — | 317 ms | 13 ms | — | **24x** |
84
+ | vorticity | 721×1440 | 41.6 ms | 41.1 ms | 1.7 ms | **24x** | **24x** |
85
+ | potential_temp | 721×1440 | 28.4 ms | 5.5 ms | 1.7 ms | **17x** | **3.2x** |
86
+
87
+ ### Full Pipeline (bench 10: HRRR squall line, 1059×1799×40)
88
+
89
+ | | Rust CPU | met-cu GPU | Speedup |
90
+ |---|---:|---:|---:|
91
+ | Total (6 functions) | 3.58 s | 762 ms | **4.7x** |
92
+ | CAPE alone | 2.84 s | 622 ms | **4.6x** |
93
+ | Kinematics alone | 230 ms | 10 ms | **23x** |
94
+
95
+ ### 100 Real Soundings (bench 09: HRRR columns across CONUS)
96
+
97
+ | | MetPy | metrust | Speedup |
98
+ |---|---:|---:|---:|
99
+ | 100 soundings total | 676 ms | 72 ms | **9.4x** |
100
+ | Per sounding | 6.8 ms | 0.72 ms | 9.4x |
101
+
102
+ ### Correctness Summary
103
+
104
+ All 12 benchmarks pass deep verification against MetPy as ground truth:
105
+
106
+ | Benchmark | Data | Checks | RMSE | Pearson r |
107
+ |---|---|---:|---|---|
108
+ | 01 HRRR Severe | 1059×1799×40 | 20/20 | ~1e-13 | 1.0000 |
109
+ | 02 GFS Upper Air | 721×1440 | 19/21 | ~1e-14 | 1.0000 |
110
+ | 03 HRRR Warm Front | 1059×1799 | All | ~1e-18 | 1.0000 |
111
+ | 04 HRRR Precip Water | 1059×1799×40 | 14/14 | ~1e-13 | 1.0000 |
112
+ | 05 HRRR Supercell | 1059×1799×40 | 16/20 | ~1e-13 | 0.99999 |
113
+ | 06 GFS Jet Stream | 721×1440 | 16/16 | ~1e-15 | 1.0000 |
114
+ | 07 HRRR Fire Weather | 1059×1799 | 16/16 | ~1e-14 | 1.0000 |
115
+ | 08 GFS Tropical | 121×201×33 | 15/15 | ~1e-15 | 1.0000 |
116
+ | 09 HRRR Soundings | 100×40 | 25/25 | ~12 J/kg (CAPE) | 0.9999 |
117
+ | 10 HRRR Squall Line | 1059×1799×40 | All | ~1e-13 | 1.0000 |
118
+ | 11 GFS Cold Air | 161×1440 | 19/19 | ~1e-14 | 1.0000 |
119
+ | 12 HRRR Boundary Layer | 1059×1799×40 | 35/35 | ~1e-13 | 1.0000 |
120
+
121
+ Non-passing checks are documented edge cases: frontogenesis boundary NaN (0.05% of points), CIN/LFC sentinel values on marginal soundings.
122
+
123
+ ---
124
+
125
+ ## met-cu Comprehensive Benchmark (202 Functions)
126
+
127
+ All 202 met-cu functions benchmarked against metrust CPU on 1,905,141 points (HRRR grid).
128
+
129
+ ### Category Averages
130
+
131
+ | Category | Functions | Avg Speedup | Min | Max |
132
+ |---|---:|---:|---:|---:|
133
+ | Per-element thermo | 52 | **238x** | 1.0x | 971x |
134
+ | Wind per-element | 3 | **7.5x** | 6.3x | 9.3x |
135
+ | Grid stencil | 28 | **39x** | 3.7x | 108x |
136
+ | Column/sounding | 72 | **3.7x** | 0.0x | 85x |
137
+
138
+ ### Top GPU Speedups
139
+
140
+ | Function | GPU | CPU | Speedup |
141
+ |---|---:|---:|---:|
142
+ | height_to_geopotential | 0.94 ms | 912 ms | **971x** |
143
+ | moist_air_specific_heat_pressure | 0.91 ms | 819 ms | **902x** |
144
+ | coriolis_parameter | 0.94 ms | 830 ms | **883x** |
145
+ | water_latent_heat_melting | 0.98 ms | 828 ms | **849x** |
146
+ | scale_height | 1.08 ms | 916 ms | **847x** |
147
+ | geopotential_to_height | 1.13 ms | 921 ms | **815x** |
148
+ | water_latent_heat_vaporization | 1.02 ms | 808 ms | **794x** |
149
+ | water_latent_heat_sublimation | 1.09 ms | 816 ms | **746x** |
150
+ | moist_air_gas_constant | 1.13 ms | 820 ms | **723x** |
151
+ | heat_index | 2.00 ms | 1035 ms | **517x** |
152
+
153
+ Note: The extreme speedups (>100x) are for functions where the metrust CPU path falls back to a Python scalar loop (`_vec_call`). The GPU kernel runs natively on the full array. These represent real user-facing speedups.
154
+
155
+ ### Grid Stencil Functions (1059×1799)
156
+
157
+ | Function | GPU | CPU | Speedup |
158
+ |---|---:|---:|---:|
159
+ | shear_vorticity | 2.05 ms | 221 ms | **108x** |
160
+ | curvature_vorticity | 1.90 ms | 160 ms | **84x** |
161
+ | smooth_rectangular | 0.98 ms | 78 ms | **80x** |
162
+ | total_deformation | 2.26 ms | 144 ms | **64x** |
163
+ | vector_derivative | 2.41 ms | 153 ms | **63x** |
164
+ | geostrophic_wind | 2.24 ms | 117 ms | **52x** |
165
+ | q_vector | 5.37 ms | 240 ms | **45x** |
166
+ | frontogenesis | 5.86 ms | 257 ms | **44x** |
167
+ | vorticity | 2.02 ms | 75 ms | **37x** |
168
+ | smooth_n_point (5) | 0.95 ms | 32 ms | **33x** |
169
+
170
+ ### Where GPU Is Slower
171
+
172
+ Single-column sounding functions are faster on CPU due to kernel launch overhead:
173
+
174
+ | Function | GPU | CPU | Ratio |
175
+ |---|---:|---:|---:|
176
+ | parcel_profile | 3.25 ms | 0.16 ms | 0.05x |
177
+ | moist_lapse | 3.15 ms | 0.13 ms | 0.04x |
178
+ | cape_cin (1 sounding) | 3.42 ms | 0.34 ms | 0.1x |
179
+
180
+ For single soundings, use CPU. For grid-level computation (1000+ columns), GPU wins.
181
+
182
+ ---
183
+
184
+ ## Workflow-Level Speedups
185
+
186
+ metrust vs MetPy on real-world analysis workflows:
187
+
188
+ | Workflow | Speedup | Notes |
189
+ |---|---:|---|
190
+ | SounderPy compute-heavy subset | **29.7x** | Thermo + wind + severe params |
191
+ | MetPy Cookbook sounding analysis | **6.0x** | Full severe weather stack |
192
+ | MetPy Cookbook 500 hPa grid | **6.1x** | Vorticity, smoothing, advection |
193
+ | MetPy Cookbook Q-vectors | **6.1x** | Q-vector divergence |
194
+ | MetPy isentropic example | **2.3x** | Isentropic interpolation + Montgomery |
195
+ | Vorticity/divergence (global grid) | **2.3x** | Spherical corrections on 721×1440 |
196
+
197
+ ---
198
+
199
+ ## Rust Array Throughput
200
+
201
+ 1M elements, 32-core Ryzen, rayon parallel:
202
+
203
+ | Function | Time | Throughput |
204
+ |---|---:|---:|
205
+ | potential_temperature | 1.8 ms | 550 M elem/s |
206
+ | wet_bulb_temperature | 7.3 ms | 137 M elem/s |
207
+ | wind_speed | 1.5 ms | 660 M elem/s |
208
+
209
+ ---
210
+
211
+ ## Running the Benchmarks
212
+
213
+ ```bash
214
+ # Three-way comparison (requires real HRRR data in data/)
215
+ python tests/benchmark_gpu.py # MetPy + Rust + CUDA
216
+ python tests/benchmark_gpu.py --no-metpy # Rust + CUDA only (faster)
217
+
218
+ # Real-data verification suite (requires HRRR + GFS GRIB files in data/)
219
+ python tests/benchmarks/bench_01_hrrr_severe.py
220
+ python tests/benchmarks/bench_02_gfs_upper_air.py
221
+ # ... through bench_12
222
+
223
+ # Run all verification benchmarks
224
+ for f in tests/benchmarks/bench_*.py; do python "$f"; done
225
+ ```
226
+
227
+ Data files needed in `data/`:
228
+ - `hrrr_prs.grib2` — HRRR pressure levels (~405 MB)
229
+ - `hrrr_sfc.grib2` — HRRR surface fields (~148 MB)
230
+ - `gfs_0p25.grib2` — GFS 0.25° analysis (~490 MB)
231
+
232
+ Download from NOAA AWS:
233
+ ```bash
234
+ curl -Lo data/hrrr_prs.grib2 "https://noaa-hrrr-bdp-pds.s3.amazonaws.com/hrrr.20260328/conus/hrrr.t00z.wrfprsf00.grib2"
235
+ curl -Lo data/gfs_0p25.grib2 "https://noaa-gfs-bdp-pds.s3.amazonaws.com/gfs.20260328/00/atmos/gfs.t00z.pgrb2.0p25.f000"
236
+ ```
@@ -802,7 +802,7 @@ dependencies = [
802
802
 
803
803
  [[package]]
804
804
  name = "metrust-py"
805
- version = "0.4.0"
805
+ version = "0.4.2"
806
806
  dependencies = [
807
807
  "metrust",
808
808
  "numpy",
@@ -8,9 +8,9 @@ license = "MIT"
8
8
 
9
9
  [package]
10
10
  name = "metrust-py"
11
- version = "0.4.0"
11
+ version = "0.4.2"
12
12
  edition.workspace = true
13
- description = "Python bindings for metrust a drop-in replacement for MetPy"
13
+ description = "Python bindings for metrust, a Rust-powered MetPy-compatible calculation layer"
14
14
  readme = "README.md"
15
15
 
16
16
  [lib]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: metrust
3
- Version: 0.4.0
3
+ Version: 0.4.2
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Intended Audience :: Science/Research
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -11,7 +11,7 @@ Requires-Dist: numpy>=1.20
11
11
  Requires-Dist: pint>=0.20
12
12
  Requires-Dist: met-cu>=0.2.1 ; extra == 'gpu'
13
13
  Provides-Extra: gpu
14
- Summary: Rust-powered drop-in replacement for MetPy -- 150/150 calc functions plus 36 extras, 10-93000x faster
14
+ Summary: Rust-powered MetPy-compatible calculation layer with optional GPU acceleration
15
15
  Keywords: meteorology,weather,rust,metpy,atmospheric-science,nwp,cape,sounding
16
16
  Author: Fahrenheit Research
17
17
  License: MIT
@@ -24,9 +24,9 @@ Project-URL: Repository, https://github.com/FahrenheitResearch/metrust-py
24
24
 
25
25
  # metrust
26
26
 
27
- **Drop-in replacement for MetPy's calculation layer, powered by Rust.**
27
+ **MetPy-compatible calculation layer, powered by Rust.**
28
28
 
29
- 150/150 `metpy.calc` functions implemented natively, plus 36 extras. 6-30x faster on real-world workflows. Verified against MetPy on SounderPy, MetPy Cookbook examples, and synthetic global grids.
29
+ 150/150 `metpy.calc` functions implemented natively, plus 36 extras. Often faster on real-world workflows. Verified against MetPy on SounderPy, MetPy Cookbook examples, and synthetic global grids.
30
30
 
31
31
  ```python
32
32
  # The only change: swap the import
@@ -54,7 +54,7 @@ pip install metrust metpy
54
54
 
55
55
  ## What It Does
56
56
 
57
- metrust implements every function in `metpy.calc` with a Rust backend compiled via PyO3. The Python API matches MetPy's signatures, units, and return types:
57
+ metrust implements every function in `metpy.calc` with a Rust backend compiled via PyO3. The Python API now matches MetPy's public `metpy.calc` signatures and is designed for MetPy-compatible units, return types, and runtime behavior on the shared calculation surface:
58
58
 
59
59
  ```python
60
60
  import numpy as np
@@ -108,6 +108,8 @@ The GPU backend currently targets the overlap where `met-cu` is already strong a
108
108
  - `compute_pw`
109
109
  - `composite_reflectivity_from_hydrometeors`
110
110
 
111
+ Eligible dispatch currently focuses on scalar thermo plus uniform 2-D Cartesian grid workloads. Latitude/longitude-derived spacing, map-scale corrections, and other projection-aware cases fall back to the Rust CPU backend.
112
+
111
113
  `metrust` still returns the same Pint/NumPy-facing API surface. Unsupported cases automatically stay on the Rust CPU path.
112
114
 
113
115
  ## Speed
@@ -229,7 +231,7 @@ These forward to MetPy when installed:
229
231
  - `metrust.xarray` (xarray accessor)
230
232
  - `metrust.io.Level2File` (NEXRAD Level II)
231
233
 
232
- Core `metrust.calc` is native Rust by default with no MetPy dependency. The optional `met-cu` backend is an explicit accelerator, not a requirement.
234
+ Core `metrust.calc` stays native Rust by default with no required MetPy dependency. A small parity-sensitive subset may delegate to MetPy when it is installed; otherwise those paths still fall back to local metrust implementations. The optional `met-cu` backend is an explicit accelerator, not a requirement.
233
235
 
234
236
  ## Examples
235
237
 
@@ -243,7 +245,7 @@ See `examples/` for complete drop-in demos:
243
245
 
244
246
  ```bash
245
247
  cargo test --workspace # 1,186 Rust tests
246
- python -m pytest tests/ -q # 20 Python tests (including MetPy drop-in regression)
248
+ python -m pytest tests/ -q # 30 Python tests (including MetPy compatibility regression)
247
249
  ```
248
250
 
249
251
  ## Documentation
@@ -1,8 +1,8 @@
1
1
  # metrust
2
2
 
3
- **Drop-in replacement for MetPy's calculation layer, powered by Rust.**
3
+ **MetPy-compatible calculation layer, powered by Rust.**
4
4
 
5
- 150/150 `metpy.calc` functions implemented natively, plus 36 extras. 6-30x faster on real-world workflows. Verified against MetPy on SounderPy, MetPy Cookbook examples, and synthetic global grids.
5
+ 150/150 `metpy.calc` functions implemented natively, plus 36 extras. Often faster on real-world workflows. Verified against MetPy on SounderPy, MetPy Cookbook examples, and synthetic global grids.
6
6
 
7
7
  ```python
8
8
  # The only change: swap the import
@@ -30,7 +30,7 @@ pip install metrust metpy
30
30
 
31
31
  ## What It Does
32
32
 
33
- metrust implements every function in `metpy.calc` with a Rust backend compiled via PyO3. The Python API matches MetPy's signatures, units, and return types:
33
+ metrust implements every function in `metpy.calc` with a Rust backend compiled via PyO3. The Python API now matches MetPy's public `metpy.calc` signatures and is designed for MetPy-compatible units, return types, and runtime behavior on the shared calculation surface:
34
34
 
35
35
  ```python
36
36
  import numpy as np
@@ -84,6 +84,8 @@ The GPU backend currently targets the overlap where `met-cu` is already strong a
84
84
  - `compute_pw`
85
85
  - `composite_reflectivity_from_hydrometeors`
86
86
 
87
+ Eligible dispatch currently focuses on scalar thermo plus uniform 2-D Cartesian grid workloads. Latitude/longitude-derived spacing, map-scale corrections, and other projection-aware cases fall back to the Rust CPU backend.
88
+
87
89
  `metrust` still returns the same Pint/NumPy-facing API surface. Unsupported cases automatically stay on the Rust CPU path.
88
90
 
89
91
  ## Speed
@@ -205,7 +207,7 @@ These forward to MetPy when installed:
205
207
  - `metrust.xarray` (xarray accessor)
206
208
  - `metrust.io.Level2File` (NEXRAD Level II)
207
209
 
208
- Core `metrust.calc` is native Rust by default with no MetPy dependency. The optional `met-cu` backend is an explicit accelerator, not a requirement.
210
+ Core `metrust.calc` stays native Rust by default with no required MetPy dependency. A small parity-sensitive subset may delegate to MetPy when it is installed; otherwise those paths still fall back to local metrust implementations. The optional `met-cu` backend is an explicit accelerator, not a requirement.
209
211
 
210
212
  ## Examples
211
213
 
@@ -219,7 +221,7 @@ See `examples/` for complete drop-in demos:
219
221
 
220
222
  ```bash
221
223
  cargo test --workspace # 1,186 Rust tests
222
- python -m pytest tests/ -q # 20 Python tests (including MetPy drop-in regression)
224
+ python -m pytest tests/ -q # 30 Python tests (including MetPy compatibility regression)
223
225
  ```
224
226
 
225
227
  ## Documentation
@@ -2,7 +2,7 @@
2
2
  name = "metrust"
3
3
  version = "0.3.9"
4
4
  edition = "2021"
5
- description = "A pure-Rust drop-in replacement for MetPy meteorological calculations, data I/O, and visualization"
5
+ description = "A pure-Rust meteorology toolkit with a MetPy-compatible calculation layer"
6
6
  license = "MIT"
7
7
 
8
8
  [dependencies]
@@ -225,10 +225,13 @@ pub fn heat_index(temperature_c: f64, relative_humidity_pct: f64) -> f64 {
225
225
  let t_f = temperature_c * 9.0 / 5.0 + 32.0;
226
226
  let rh = relative_humidity_pct;
227
227
 
228
- // Below 80 F, use the simpler Steadman formula
229
- if t_f < 80.0 {
230
- let hi_f = 0.5 * (t_f + 61.0 + (t_f - 68.0) * 1.2 + rh * 0.094);
231
- return (hi_f - 32.0) * 5.0 / 9.0;
228
+ // NWS two-step: compute Steadman, average with T, then decide
229
+ let steadman = 0.5 * (t_f + 61.0 + (t_f - 68.0) * 1.2 + rh * 0.094);
230
+ let hi_avg = (steadman + t_f) / 2.0;
231
+
232
+ if hi_avg < 80.0 {
233
+ // Below threshold, return the averaged Steadman result
234
+ return (hi_avg - 32.0) * 5.0 / 9.0;
232
235
  }
233
236
 
234
237
  // Rothfusz regression
@@ -456,7 +456,12 @@ pub fn cape_cin_core(
456
456
  let tv_parc_prev = virtual_temp(t_parc_prev, p_prev, t_parc_prev);
457
457
  let buoy_prev = tv_parc_prev - tv_env_prev;
458
458
 
459
- if buoyancy != buoy_prev {
459
+ if buoy_prev >= 0.0 {
460
+ // Previous level is also buoyant — no real crossing.
461
+ // The parcel is buoyant from the LCL (or surface)
462
+ // upward, so the LFC is at the LCL pressure.
463
+ p_lcl
464
+ } else if buoyancy != buoy_prev {
460
465
  let frac = (0.0 - buoy_prev) / (buoyancy - buoy_prev);
461
466
  p_prev + frac * (p_curr - p_prev)
462
467
  } else {
@@ -1423,9 +1428,12 @@ pub fn sigma_to_pressure(sigma: f64, p_sfc: f64, p_top: f64) -> f64 {
1423
1428
  /// t_f: temperature (Fahrenheit), rh: relative humidity (%).
1424
1429
  /// Returns heat index in Fahrenheit.
1425
1430
  pub fn heat_index(t_f: f64, rh: f64) -> f64 {
1426
- // Below 80F, use simple formula
1427
- if t_f < 80.0 {
1428
- return 0.5 * (t_f + 61.0 + (t_f - 68.0) * 1.2 + rh * 0.094);
1431
+ // NWS two-step: compute Steadman, average with T, then decide
1432
+ let steadman = 0.5 * (t_f + 61.0 + (t_f - 68.0) * 1.2 + rh * 0.094);
1433
+ let hi_avg = (steadman + t_f) / 2.0;
1434
+
1435
+ if hi_avg < 80.0 {
1436
+ return hi_avg;
1429
1437
  }
1430
1438
 
1431
1439
  // Rothfusz regression
@@ -3176,9 +3184,10 @@ mod tests {
3176
3184
 
3177
3185
  #[test]
3178
3186
  fn test_heat_index_below_80f() {
3179
- // Below 80F, uses simple formula: 0.5*(T + 61 + (T-68)*1.2 + RH*0.094)
3187
+ // NWS two-step: Steadman averaged with T when avg < 80F
3180
3188
  let hi = heat_index(70.0, 50.0);
3181
- let expected = 0.5 * (70.0 + 61.0 + (70.0 - 68.0) * 1.2 + 50.0 * 0.094);
3189
+ let steadman = 0.5 * (70.0 + 61.0 + (70.0 - 68.0) * 1.2 + 50.0 * 0.094);
3190
+ let expected = (steadman + 70.0) / 2.0;
3182
3191
  assert!(
3183
3192
  (hi - expected).abs() < 1e-10,
3184
3193
  "Heat index at 70F/50%: got={hi}, expected={expected}"
@@ -7,9 +7,13 @@ in Rust versus what it delegates to MetPy.
7
7
 
8
8
  ## 1. Compatibility Model
9
9
 
10
- metrust is a drop-in replacement for `metpy.calc` backed by a compiled Rust
10
+ metrust is a MetPy-compatible calculation layer for `metpy.calc`, backed by a compiled Rust
11
11
  engine (`_metrust`). The compatibility model has three tiers:
12
12
 
13
+ Function coverage in the tables below is a functionality matrix. Public `metpy.calc` signatures now
14
+ match MetPy, and the compatibility tests cover explicit runtime regression cases on the shared
15
+ calculation surface.
16
+
13
17
  | Tier | Description |
14
18
  |------|-------------|
15
19
  | **Native Rust** | Function is implemented entirely in Rust. The Python wrapper strips Pint units, calls the Rust function, and re-attaches units. No MetPy dependency. |
@@ -19,7 +19,7 @@ from metrust.units import units
19
19
 
20
20
  That is the entire change for most code. Every function in `metrust.calc`
21
21
  accepts the same Pint Quantity inputs and returns Pint Quantity outputs, using
22
- the same function names and the same positional/keyword signatures as MetPy.
22
+ the same function names and the same public positional/keyword signatures as MetPy.
23
23
 
24
24
  A project-wide find-and-replace from `metpy.` to `metrust.` is usually
25
25
  sufficient. The table below shows the namespace mapping:
@@ -2,8 +2,8 @@
2
2
 
3
3
  ## Meteorological Computation at the Speed of Rust
4
4
 
5
- **metrust** is a Rust-powered, drop-in replacement for [MetPy](https://unidata.github.io/MetPy/)'s calculation layer.
6
- Change one import line. Keep your existing code. Get 10--90x faster results.
5
+ **metrust** is a Rust-powered, MetPy-compatible calculation layer for [MetPy](https://unidata.github.io/MetPy/)'s calculation workflows.
6
+ For many `metpy.calc` workflows, changing one import line is enough. Many thermo and grid workloads run substantially faster.
7
7
 
8
8
  ---
9
9
 
@@ -53,7 +53,7 @@ The surface is identical. Under the hood, every calculation runs through compile
53
53
 
54
54
  ### 150/150 MetPy Calc Functions (Plus 36 Extras)
55
55
 
56
- Every function in `metpy.calc` has a metrust equivalent -- 100% API coverage. Plus 36 additional functions MetPy doesn't have (grid composites, fire weather indices, and more). The entire `metrust.calc` module is backed by Rust -- no MetPy dependency, no Python fallback.
56
+ Every function in `metpy.calc` has a metrust equivalent -- 100% API coverage. Plus 36 additional functions MetPy doesn't have (grid composites, fire weather indices, and more). The calculation layer is Rust-backed by default, with a small parity-sensitive subset optionally delegating to MetPy when it is installed.
57
57
  Coverage spans the core of operational meteorology:
58
58
 
59
59
  - **Thermodynamics** -- potential temperature, equivalent potential temperature, virtual temperature, wet-bulb temperature, LCL, LFC, EL, CAPE/CIN, parcel profiles, precipitable water, thickness hydrostatic, stability indices
@@ -149,8 +149,8 @@ All `metrust.calc` functions compile to native machine code through the Rust too
149
149
 
150
150
  ### No MetPy Dependency for Calculations
151
151
 
152
- `pip install metrust` pulls in **only** NumPy and Pint. The entire calculation layer is self-contained.
153
- MetPy is never imported, loaded, or called for any `metrust.calc` function.
152
+ `pip install metrust` pulls in **only** NumPy and Pint. The calculation layer stays self-contained by default.
153
+ A small parity-sensitive subset of `metrust.calc` can delegate to MetPy when MetPy is installed, but the default path remains the Rust backend.
154
154
 
155
155
  ### Optional MetPy for Plots, xarray, and Level 2
156
156
 
@@ -158,7 +158,7 @@ A handful of surfaces intentionally delegate to MetPy when it is installed:
158
158
 
159
159
  | Surface | Behavior |
160
160
  |---|---|
161
- | `metrust.calc` | 100% native Rust. No MetPy fallback. |
161
+ | `metrust.calc` | Native Rust by default, with limited optional MetPy delegation on a few parity-sensitive paths. |
162
162
  | `metrust.io.Level2File` | Forwards to MetPy's Level 2 reader when available. |
163
163
  | `metrust.plots` | Forwards to `metpy.plots`. |
164
164
  | `metrust.xarray` | Forwards to `metpy.xarray`. |
@@ -17,7 +17,7 @@ metrust started with SHARPpy-heritage algorithms — Wobus polynomial approximat
17
17
 
18
18
  Over a series of systematic replacements, metrust moved to MetPy-exact algorithms:
19
19
 
20
- - **Moist lapse rate** replaced with a fourth-order Runge-Kutta (RK4) integration of the full moist-adiabatic ODE, matching MetPy's `moist_lapse` to machine precision.
20
+ - **Moist lapse rate** replaced with a fourth-order Runge-Kutta (RK4) integration of the full moist-adiabatic ODE, matching MetPy closely on the supported reference-pressure paths.
21
21
  - **CAPE integration** switched from a simplified buoyancy sum to MetPy's exact formulation: `g * dTv / Tv * dz`, integrated over each layer.
22
22
  - **Bunkers storm motion** moved to a pressure-weighted mean wind, matching the Bunkers et al. (2000) method that MetPy implements.
23
23
  - **Isentropic interpolation** adopted a Newton solver on the exact theta equation rather than a lookup-table approach.
@@ -1,5 +1,5 @@
1
1
  site_name: metrust
2
- site_description: Rust-powered meteorology toolkit drop-in replacement for MetPy
2
+ site_description: Rust-powered meteorology toolkit with a MetPy-compatible calculation layer
3
3
  site_url: https://fahrenheitresearch.github.io/metrust-py/
4
4
  repo_url: https://github.com/FahrenheitResearch/metrust-py
5
5
  repo_name: FahrenheitResearch/metrust-py
@@ -4,8 +4,8 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "metrust"
7
- version = "0.4.0"
8
- description = "Rust-powered drop-in replacement for MetPy -- 150/150 calc functions plus 36 extras, 10-93000x faster"
7
+ version = "0.4.2"
8
+ description = "Rust-powered MetPy-compatible calculation layer with optional GPU acceleration"
9
9
  requires-python = ">=3.9"
10
10
  license = {text = "MIT"}
11
11
  authors = [{name = "Fahrenheit Research"}]