metrust 0.3.7__tar.gz → 0.3.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.
Files changed (201) hide show
  1. {metrust-0.3.7 → metrust-0.3.8}/Cargo.lock +3 -3
  2. {metrust-0.3.7 → metrust-0.3.8}/Cargo.toml +1 -1
  3. {metrust-0.3.7 → metrust-0.3.8}/PKG-INFO +1 -1
  4. {metrust-0.3.7 → metrust-0.3.8}/crates/metrust/Cargo.toml +1 -1
  5. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/Cargo.toml +1 -1
  6. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-math/src/thermo.rs +28 -3
  7. {metrust-0.3.7 → metrust-0.3.8}/pyproject.toml +1 -1
  8. {metrust-0.3.7 → metrust-0.3.8}/python/metrust/calc/__init__.py +49 -5
  9. metrust-0.3.8/tests/sounding_3way.py +207 -0
  10. metrust-0.3.8/tests/sounding_comparison.png +0 -0
  11. {metrust-0.3.7 → metrust-0.3.8}/.github/workflows/ci.yml +0 -0
  12. {metrust-0.3.7 → metrust-0.3.8}/.github/workflows/docs.yml +0 -0
  13. {metrust-0.3.7 → metrust-0.3.8}/.github/workflows/release.yml +0 -0
  14. {metrust-0.3.7 → metrust-0.3.8}/.gitignore +0 -0
  15. {metrust-0.3.7 → metrust-0.3.8}/README.md +0 -0
  16. {metrust-0.3.7 → metrust-0.3.8}/VERIFICATION.md +0 -0
  17. {metrust-0.3.7 → metrust-0.3.8}/benches/bench_hrrr.py +0 -0
  18. {metrust-0.3.7 → metrust-0.3.8}/benches/bench_hrrr_vs_metpy.py +0 -0
  19. {metrust-0.3.7 → metrust-0.3.8}/benches/bench_python.py +0 -0
  20. {metrust-0.3.7 → metrust-0.3.8}/crates/metrust/benches/calc_bench.rs +0 -0
  21. {metrust-0.3.7 → metrust-0.3.8}/crates/metrust/src/calc/atmo.rs +0 -0
  22. {metrust-0.3.7 → metrust-0.3.8}/crates/metrust/src/calc/kinematics.rs +0 -0
  23. {metrust-0.3.7 → metrust-0.3.8}/crates/metrust/src/calc/mod.rs +0 -0
  24. {metrust-0.3.7 → metrust-0.3.8}/crates/metrust/src/calc/severe.rs +0 -0
  25. {metrust-0.3.7 → metrust-0.3.8}/crates/metrust/src/calc/smooth.rs +0 -0
  26. {metrust-0.3.7 → metrust-0.3.8}/crates/metrust/src/calc/thermo.rs +0 -0
  27. {metrust-0.3.7 → metrust-0.3.8}/crates/metrust/src/calc/utils.rs +0 -0
  28. {metrust-0.3.7 → metrust-0.3.8}/crates/metrust/src/calc/wind.rs +0 -0
  29. {metrust-0.3.7 → metrust-0.3.8}/crates/metrust/src/constants.rs +0 -0
  30. {metrust-0.3.7 → metrust-0.3.8}/crates/metrust/src/interpolate/mod.rs +0 -0
  31. {metrust-0.3.7 → metrust-0.3.8}/crates/metrust/src/io/gempak.rs +0 -0
  32. {metrust-0.3.7 → metrust-0.3.8}/crates/metrust/src/io/gempak_dm.rs +0 -0
  33. {metrust-0.3.7 → metrust-0.3.8}/crates/metrust/src/io/gempak_sounding.rs +0 -0
  34. {metrust-0.3.7 → metrust-0.3.8}/crates/metrust/src/io/gempak_surface.rs +0 -0
  35. {metrust-0.3.7 → metrust-0.3.8}/crates/metrust/src/io/gini.rs +0 -0
  36. {metrust-0.3.7 → metrust-0.3.8}/crates/metrust/src/io/level3.rs +0 -0
  37. {metrust-0.3.7 → metrust-0.3.8}/crates/metrust/src/io/metar.rs +0 -0
  38. {metrust-0.3.7 → metrust-0.3.8}/crates/metrust/src/io/mod.rs +0 -0
  39. {metrust-0.3.7 → metrust-0.3.8}/crates/metrust/src/io/station.rs +0 -0
  40. {metrust-0.3.7 → metrust-0.3.8}/crates/metrust/src/io/wpc.rs +0 -0
  41. {metrust-0.3.7 → metrust-0.3.8}/crates/metrust/src/lib.rs +0 -0
  42. {metrust-0.3.7 → metrust-0.3.8}/crates/metrust/src/plots/mod.rs +0 -0
  43. {metrust-0.3.7 → metrust-0.3.8}/crates/metrust/src/projections.rs +0 -0
  44. {metrust-0.3.7 → metrust-0.3.8}/crates/metrust/src/units.rs +0 -0
  45. {metrust-0.3.7 → metrust-0.3.8}/crates/metrust/tests/test_gempak.rs +0 -0
  46. {metrust-0.3.7 → metrust-0.3.8}/crates/metrust/tests/test_new_functions.rs +0 -0
  47. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/composite.rs +0 -0
  48. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/download/cache.rs +0 -0
  49. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/download/catalog.rs +0 -0
  50. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/download/client.rs +0 -0
  51. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/download/fallback.rs +0 -0
  52. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/download/idx.rs +0 -0
  53. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/download/mod.rs +0 -0
  54. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/download/sources.rs +0 -0
  55. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/download/streaming.rs +0 -0
  56. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/dynamics.rs +0 -0
  57. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/error.rs +0 -0
  58. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/grib2/grid.rs +0 -0
  59. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/grib2/mod.rs +0 -0
  60. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/grib2/ops.rs +0 -0
  61. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/grib2/parser.rs +0 -0
  62. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/grib2/search.rs +0 -0
  63. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/grib2/streaming.rs +0 -0
  64. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/grib2/tables.rs +0 -0
  65. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/grib2/tests.rs +0 -0
  66. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/grib2/unpack.rs +0 -0
  67. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/grib2/writer.rs +0 -0
  68. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/gridmath.rs +0 -0
  69. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/lib.rs +0 -0
  70. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/metfuncs.rs +0 -0
  71. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/models/cfs.rs +0 -0
  72. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/models/ecmwf.rs +0 -0
  73. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/models/era5.rs +0 -0
  74. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/models/gefs.rs +0 -0
  75. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/models/gfs.rs +0 -0
  76. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/models/href.rs +0 -0
  77. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/models/hrrr.rs +0 -0
  78. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/models/hrrr_ak.rs +0 -0
  79. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/models/latest.rs +0 -0
  80. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/models/mod.rs +0 -0
  81. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/models/mrms.rs +0 -0
  82. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/models/nam.rs +0 -0
  83. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/models/nbm.rs +0 -0
  84. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/models/rap.rs +0 -0
  85. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/models/rrfs.rs +0 -0
  86. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/models/rtma.rs +0 -0
  87. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/models/sref.rs +0 -0
  88. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/models/urma.rs +0 -0
  89. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/models/wpc.rs +0 -0
  90. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/products.rs +0 -0
  91. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/projection.rs +0 -0
  92. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/regrid.rs +0 -0
  93. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/render/ansi.rs +0 -0
  94. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/render/colormap.rs +0 -0
  95. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/render/contour.rs +0 -0
  96. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/render/cross_section.rs +0 -0
  97. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/render/encode.rs +0 -0
  98. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/render/filled_contour.rs +0 -0
  99. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/render/hodograph.rs +0 -0
  100. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/render/mod.rs +0 -0
  101. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/render/overlay.rs +0 -0
  102. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/render/raster.rs +0 -0
  103. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/render/skewt.rs +0 -0
  104. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-core/src/render/station.rs +0 -0
  105. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-field/Cargo.toml +0 -0
  106. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-field/src/error.rs +0 -0
  107. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-field/src/field.rs +0 -0
  108. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-field/src/lib.rs +0 -0
  109. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-field/src/meta.rs +0 -0
  110. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-field/src/projection.rs +0 -0
  111. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-field/src/radial.rs +0 -0
  112. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-field/src/site.rs +0 -0
  113. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-field/src/sounding.rs +0 -0
  114. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-field/src/time.rs +0 -0
  115. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-math/Cargo.toml +0 -0
  116. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-math/src/composite.rs +0 -0
  117. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-math/src/dynamics.rs +0 -0
  118. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-math/src/gridmath.rs +0 -0
  119. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-math/src/interpolate.rs +0 -0
  120. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-math/src/lib.rs +0 -0
  121. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-math/src/regrid.rs +0 -0
  122. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-radar/Cargo.toml +0 -0
  123. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-radar/src/cells.rs +0 -0
  124. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-radar/src/color_table.rs +0 -0
  125. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-radar/src/derived.rs +0 -0
  126. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-radar/src/detection.rs +0 -0
  127. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-radar/src/level2.rs +0 -0
  128. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-radar/src/lib.rs +0 -0
  129. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-radar/src/products.rs +0 -0
  130. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-radar/src/render.rs +0 -0
  131. {metrust-0.3.7 → metrust-0.3.8}/crates/wx-radar/src/sites.rs +0 -0
  132. {metrust-0.3.7 → metrust-0.3.8}/demos/hrrr_diffusion_training.md +0 -0
  133. {metrust-0.3.7 → metrust-0.3.8}/docs/api/atmospheric.md +0 -0
  134. {metrust-0.3.7 → metrust-0.3.8}/docs/api/grid-composites.md +0 -0
  135. {metrust-0.3.7 → metrust-0.3.8}/docs/api/io.md +0 -0
  136. {metrust-0.3.7 → metrust-0.3.8}/docs/api/kinematics.md +0 -0
  137. {metrust-0.3.7 → metrust-0.3.8}/docs/api/moisture.md +0 -0
  138. {metrust-0.3.7 → metrust-0.3.8}/docs/api/severe.md +0 -0
  139. {metrust-0.3.7 → metrust-0.3.8}/docs/api/smoothing.md +0 -0
  140. {metrust-0.3.7 → metrust-0.3.8}/docs/api/thermodynamics.md +0 -0
  141. {metrust-0.3.7 → metrust-0.3.8}/docs/api/units.md +0 -0
  142. {metrust-0.3.7 → metrust-0.3.8}/docs/api/wind.md +0 -0
  143. {metrust-0.3.7 → metrust-0.3.8}/docs/compatibility.md +0 -0
  144. {metrust-0.3.7 → metrust-0.3.8}/docs/guides/arrays.md +0 -0
  145. {metrust-0.3.7 → metrust-0.3.8}/docs/guides/installation.md +0 -0
  146. {metrust-0.3.7 → metrust-0.3.8}/docs/guides/migration.md +0 -0
  147. {metrust-0.3.7 → metrust-0.3.8}/docs/index.md +0 -0
  148. {metrust-0.3.7 → metrust-0.3.8}/docs/methodology/architecture.md +0 -0
  149. {metrust-0.3.7 → metrust-0.3.8}/docs/methodology/grid-kinematics.md +0 -0
  150. {metrust-0.3.7 → metrust-0.3.8}/docs/methodology/index.md +0 -0
  151. {metrust-0.3.7 → metrust-0.3.8}/docs/methodology/interpolation.md +0 -0
  152. {metrust-0.3.7 → metrust-0.3.8}/docs/methodology/io-formats.md +0 -0
  153. {metrust-0.3.7 → metrust-0.3.8}/docs/methodology/metpy-compatibility.md +0 -0
  154. {metrust-0.3.7 → metrust-0.3.8}/docs/methodology/moisture.md +0 -0
  155. {metrust-0.3.7 → metrust-0.3.8}/docs/methodology/parallelism.md +0 -0
  156. {metrust-0.3.7 → metrust-0.3.8}/docs/methodology/severe-weather.md +0 -0
  157. {metrust-0.3.7 → metrust-0.3.8}/docs/methodology/smoothing.md +0 -0
  158. {metrust-0.3.7 → metrust-0.3.8}/docs/methodology/thermodynamics.md +0 -0
  159. {metrust-0.3.7 → metrust-0.3.8}/docs/methodology/units-and-pint.md +0 -0
  160. {metrust-0.3.7 → metrust-0.3.8}/docs/methodology/wind.md +0 -0
  161. {metrust-0.3.7 → metrust-0.3.8}/docs/performance.md +0 -0
  162. {metrust-0.3.7 → metrust-0.3.8}/docs/tutorials/first-grid.md +0 -0
  163. {metrust-0.3.7 → metrust-0.3.8}/docs/tutorials/first-sounding.md +0 -0
  164. {metrust-0.3.7 → metrust-0.3.8}/docs/tutorials/reading-the-numbers.md +0 -0
  165. {metrust-0.3.7 → metrust-0.3.8}/docs/tutorials/recipes.md +0 -0
  166. {metrust-0.3.7 → metrust-0.3.8}/docs/tutorials/weather-101.md +0 -0
  167. {metrust-0.3.7 → metrust-0.3.8}/examples/cookbook_500hpa_grid.py +0 -0
  168. {metrust-0.3.7 → metrust-0.3.8}/examples/cookbook_sounding.py +0 -0
  169. {metrust-0.3.7 → metrust-0.3.8}/examples/sounderpy_dropin.py +0 -0
  170. {metrust-0.3.7 → metrust-0.3.8}/mkdocs.yml +0 -0
  171. {metrust-0.3.7 → metrust-0.3.8}/python/metrust/__init__.py +0 -0
  172. {metrust-0.3.7 → metrust-0.3.8}/python/metrust/constants/__init__.py +0 -0
  173. {metrust-0.3.7 → metrust-0.3.8}/python/metrust/interpolate/__init__.py +0 -0
  174. {metrust-0.3.7 → metrust-0.3.8}/python/metrust/io/__init__.py +0 -0
  175. {metrust-0.3.7 → metrust-0.3.8}/python/metrust/plots/__init__.py +0 -0
  176. {metrust-0.3.7 → metrust-0.3.8}/python/metrust/units.py +0 -0
  177. {metrust-0.3.7 → metrust-0.3.8}/python/metrust/xarray.py +0 -0
  178. {metrust-0.3.7 → metrust-0.3.8}/src/lib.rs +0 -0
  179. {metrust-0.3.7 → metrust-0.3.8}/src/py_atmo.rs +0 -0
  180. {metrust-0.3.7 → metrust-0.3.8}/src/py_constants.rs +0 -0
  181. {metrust-0.3.7 → metrust-0.3.8}/src/py_interpolate.rs +0 -0
  182. {metrust-0.3.7 → metrust-0.3.8}/src/py_io.rs +0 -0
  183. {metrust-0.3.7 → metrust-0.3.8}/src/py_kinematics.rs +0 -0
  184. {metrust-0.3.7 → metrust-0.3.8}/src/py_severe.rs +0 -0
  185. {metrust-0.3.7 → metrust-0.3.8}/src/py_smooth.rs +0 -0
  186. {metrust-0.3.7 → metrust-0.3.8}/src/py_thermo.rs +0 -0
  187. {metrust-0.3.7 → metrust-0.3.8}/src/py_utils.rs +0 -0
  188. {metrust-0.3.7 → metrust-0.3.8}/src/py_wind.rs +0 -0
  189. {metrust-0.3.7 → metrust-0.3.8}/tests/api_audit_calc.md +0 -0
  190. {metrust-0.3.7 → metrust-0.3.8}/tests/api_audit_other.md +0 -0
  191. {metrust-0.3.7 → metrust-0.3.8}/tests/benchmark.py +0 -0
  192. {metrust-0.3.7 → metrust-0.3.8}/tests/test_metpy_dropin_compat.py +0 -0
  193. {metrust-0.3.7 → metrust-0.3.8}/tests/test_python_compat.py +0 -0
  194. {metrust-0.3.7 → metrust-0.3.8}/tests/verify_constants.py +0 -0
  195. {metrust-0.3.7 → metrust-0.3.8}/tests/verify_edge_cases.py +0 -0
  196. {metrust-0.3.7 → metrust-0.3.8}/tests/verify_kinematics.py +0 -0
  197. {metrust-0.3.7 → metrust-0.3.8}/tests/verify_severe_atmo.py +0 -0
  198. {metrust-0.3.7 → metrust-0.3.8}/tests/verify_smooth_interp.py +0 -0
  199. {metrust-0.3.7 → metrust-0.3.8}/tests/verify_thermo.py +0 -0
  200. {metrust-0.3.7 → metrust-0.3.8}/tests/verify_units.py +0 -0
  201. {metrust-0.3.7 → metrust-0.3.8}/tests/verify_wind.py +0 -0
@@ -788,7 +788,7 @@ dependencies = [
788
788
 
789
789
  [[package]]
790
790
  name = "metrust"
791
- version = "0.3.7"
791
+ version = "0.3.8"
792
792
  dependencies = [
793
793
  "chrono",
794
794
  "criterion",
@@ -802,7 +802,7 @@ dependencies = [
802
802
 
803
803
  [[package]]
804
804
  name = "metrust-py"
805
- version = "0.3.7"
805
+ version = "0.3.8"
806
806
  dependencies = [
807
807
  "metrust",
808
808
  "numpy",
@@ -1925,7 +1925,7 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
1925
1925
 
1926
1926
  [[package]]
1927
1927
  name = "wx-core"
1928
- version = "0.3.7"
1928
+ version = "0.3.8"
1929
1929
  dependencies = [
1930
1930
  "chrono",
1931
1931
  "flate2",
@@ -8,7 +8,7 @@ license = "MIT"
8
8
 
9
9
  [package]
10
10
  name = "metrust-py"
11
- version = "0.3.7"
11
+ version = "0.3.8"
12
12
  edition.workspace = true
13
13
  description = "Python bindings for metrust — a drop-in replacement for MetPy"
14
14
  readme = "README.md"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: metrust
3
- Version: 0.3.7
3
+ Version: 0.3.8
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Intended Audience :: Science/Research
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "metrust"
3
- version = "0.3.7"
3
+ version = "0.3.8"
4
4
  edition = "2021"
5
5
  description = "A pure-Rust drop-in replacement for MetPy — meteorological calculations, data I/O, and visualization"
6
6
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "wx-core"
3
- version = "0.3.7"
3
+ version = "0.3.8"
4
4
  edition = "2021"
5
5
  description = "Pure Rust GRIB2 processor and weather model data library — HRRR, GFS, NAM, RAP"
6
6
  license = "MIT"
@@ -581,6 +581,25 @@ pub fn cape_cin_core(
581
581
  // Only integrate between surface and top limit
582
582
  let p_top_actual = if p_top_limit > 0.0 { p_top_limit } else { p_prof[n - 1] };
583
583
 
584
+ // CIN is accumulated below the LFC. We track the *last*
585
+ // transition from negative to positive buoyancy (the true LFC).
586
+ // This handles superadiabatic surface layers correctly.
587
+
588
+ // First, find the index of the last neg→pos crossing (the true LFC)
589
+ let mut last_lfc_idx: Option<usize> = None;
590
+ for i in 1..n {
591
+ if tv_parc_arr[i].is_nan() || tv_parc_arr[i - 1].is_nan() { continue; }
592
+ let tv_e = tv_env_arr[i] + ZEROCNK;
593
+ let tv_p = tv_parc_arr[i] + ZEROCNK;
594
+ let tv_e_prev = tv_env_arr[i - 1] + ZEROCNK;
595
+ let tv_p_prev = tv_parc_arr[i - 1] + ZEROCNK;
596
+ let buoy = tv_p - tv_e;
597
+ let buoy_prev = tv_p_prev - tv_e_prev;
598
+ if buoy > 0.0 && buoy_prev <= 0.0 {
599
+ last_lfc_idx = Some(i);
600
+ }
601
+ }
602
+
584
603
  for i in 1..n {
585
604
  if p_prof[i] <= 0.0 || tv_parc_arr[i].is_nan() || tv_parc_arr[i - 1].is_nan() { continue; }
586
605
  if p_prof[i] < p_top_actual { continue; }
@@ -596,10 +615,16 @@ pub fn cape_cin_core(
596
615
  let buoy_hi = (tv_p_hi - tv_e_hi) / tv_e_hi;
597
616
  let val = G * (buoy_lo + buoy_hi) / 2.0 * dz;
598
617
 
599
- if val > 0.0 {
600
- total_cape += val;
618
+ if let Some(lfc_i) = last_lfc_idx {
619
+ if val > 0.0 && i >= lfc_i {
620
+ total_cape += val;
621
+ } else if val < 0.0 && i <= lfc_i {
622
+ total_cin += val;
623
+ }
601
624
  } else {
602
- total_cin += val;
625
+ // No LFC found — accumulate everything
626
+ if val > 0.0 { total_cape += val; }
627
+ else { total_cin += val; }
603
628
  }
604
629
  }
605
630
 
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "metrust"
7
- version = "0.3.7"
7
+ version = "0.3.8"
8
8
  description = "Rust-powered drop-in replacement for MetPy -- 150/150 calc functions plus 36 extras, 10-93000x faster"
9
9
  requires-python = ">=3.9"
10
10
  license = {text = "MIT"}
@@ -628,15 +628,54 @@ def cape_cin(pressure, temperature, dewpoint, parcel_profile_or_height=None,
628
628
  h_calc[i] = h_calc[i-1] + (287.04749 * tv_mean / 9.80665) * np.log(p[i-1] / p[i])
629
629
 
630
630
  # Integrate CAPE/CIN: trapezoidal rule
631
+ # Two-pass approach matching MetPy:
632
+ # Pass 1: Find all buoyancy values to locate LFC and EL
633
+ # Pass 2: Integrate CAPE between LFC-EL, CIN between LCL-LFC
634
+ #
635
+ # CIN is the negative area where the parcel is cooler than the
636
+ # environment. For surface-based parcels, the parcel may be
637
+ # positively buoyant near the surface (superadiabatic layer),
638
+ # then negatively buoyant (the cap/CIN), then positively buoyant
639
+ # again above the LFC. We need to capture that middle negative
640
+ # layer, not just stop at the first positive.
641
+
642
+ # Find LCL (where T_parcel ≈ Td, i.e. parcel becomes saturated)
643
+ lcl_idx = 0
644
+ for i in range(1, len(p)):
645
+ if t_parcel[i] <= t_parcel[0] - 1.0: # parcel has cooled — above LCL
646
+ lcl_idx = i
647
+ break
648
+
649
+ # Compute buoyancy at each level
650
+ buoyancy = np.zeros(len(p))
651
+ for i in range(len(p)):
652
+ if p[i] <= 0:
653
+ continue
654
+ tv_e = _calc.virtual_temp(t[i], p[i], td[i]) + 273.15
655
+ tv_p = _calc.virtual_temp(t_parcel[i], p[i], t_parcel[i]) + 273.15
656
+ if tv_e > 0:
657
+ buoyancy[i] = (tv_p - tv_e) / tv_e
658
+
659
+ # Find LFC: last crossing from negative to positive buoyancy
660
+ lfc_idx = None
661
+ for i in range(1, len(p)):
662
+ if buoyancy[i] > 0 and buoyancy[i-1] <= 0:
663
+ lfc_idx = i
664
+
665
+ # Find EL: last crossing from positive to negative after LFC
666
+ el_idx = len(p) - 1
667
+ if lfc_idx is not None:
668
+ for i in range(lfc_idx + 1, len(p)):
669
+ if buoyancy[i] <= 0 and buoyancy[i-1] > 0:
670
+ el_idx = i
671
+
631
672
  cape_val = 0.0
632
673
  cin_val = 0.0
633
674
  for i in range(1, len(p)):
634
675
  if p[i] <= 0:
635
676
  continue
636
- # Environment virtual temperature
637
677
  tv_e_lo = _calc.virtual_temp(t[i-1], p[i-1], td[i-1]) + 273.15
638
678
  tv_e_hi = _calc.virtual_temp(t[i], p[i], td[i]) + 273.15
639
- # Parcel virtual temperature (saturated above LCL, so Td_parcel ≈ T_parcel)
640
679
  tv_p_lo = _calc.virtual_temp(t_parcel[i-1], p[i-1], t_parcel[i-1]) + 273.15
641
680
  tv_p_hi = _calc.virtual_temp(t_parcel[i], p[i], t_parcel[i]) + 273.15
642
681
  dz = h_calc[i] - h_calc[i-1]
@@ -645,10 +684,15 @@ def cape_cin(pressure, temperature, dewpoint, parcel_profile_or_height=None,
645
684
  buoy_lo = (tv_p_lo - tv_e_lo) / tv_e_lo
646
685
  buoy_hi = (tv_p_hi - tv_e_hi) / tv_e_hi
647
686
  val = 9.80665 * (buoy_lo + buoy_hi) / 2.0 * dz
648
- if val > 0:
649
- cape_val += val
650
- else:
687
+
688
+ if lfc_idx is not None and i <= el_idx:
689
+ if val > 0 and i >= lfc_idx:
690
+ cape_val += val
691
+ elif val < 0 and i <= lfc_idx:
692
+ cin_val += val
693
+ elif val < 0:
651
694
  cin_val += val
695
+
652
696
  return cape_val * units("J/kg"), cin_val * units("J/kg")
653
697
  elif fourth is not None:
654
698
  h = _as_1d(_strip(fourth, "m"))
@@ -0,0 +1,207 @@
1
+ """Three-way sounding comparison: SharpJS vs MetPy vs metrust."""
2
+ import warnings; warnings.filterwarnings("ignore")
3
+ import numpy as np
4
+
5
+ from rusbie import Herbie
6
+
7
+ H = Herbie("2026-03-27 23:00", model="hrrr", product="prs", fxx=0, verbose=False)
8
+
9
+ ds_t = H.xarray(":TMP:.*mb", backend_kwargs={"filter_by_keys": {"typeOfLevel": "isobaricInhPa"}})
10
+ ds_d = H.xarray(":DPT:.*mb", backend_kwargs={"filter_by_keys": {"typeOfLevel": "isobaricInhPa"}})
11
+ ds_u = H.xarray(":UGRD:.*mb", backend_kwargs={"filter_by_keys": {"typeOfLevel": "isobaricInhPa"}})
12
+ ds_v = H.xarray(":VGRD:.*mb", backend_kwargs={"filter_by_keys": {"typeOfLevel": "isobaricInhPa"}})
13
+ ds_h = H.xarray(":HGT:.*mb", backend_kwargs={"filter_by_keys": {"typeOfLevel": "isobaricInhPa"}})
14
+
15
+ for name in ["ds_t", "ds_d", "ds_u", "ds_v", "ds_h"]:
16
+ obj = locals()[name]
17
+ if isinstance(obj, list):
18
+ locals()[name] = obj[0]
19
+
20
+ ds_t, ds_d, ds_u, ds_v, ds_h = [x[0] if isinstance(x, list) else x for x in [ds_t, ds_d, ds_u, ds_v, ds_h]]
21
+
22
+ lat_target, lon_target = 32.5462, -89.12
23
+ lon_360 = lon_target % 360
24
+ lat_vals = ds_t.latitude.values
25
+ lon_vals = ds_t.longitude.values
26
+ dist = (lat_vals - lat_target)**2 + (lon_vals - lon_360)**2
27
+ j, i = np.unravel_index(np.nanargmin(dist), dist.shape)
28
+
29
+ pres_coord = [c for c in ds_t.coords if "isobaric" in c.lower()][0]
30
+ plevs = ds_t[pres_coord].values
31
+ p_hpa = plevs / 100.0 if plevs.max() > 2000 else plevs
32
+
33
+ t_k = ds_t[list(ds_t.data_vars)[0]].values[:, j, i]
34
+ td_k = ds_d[list(ds_d.data_vars)[0]].values[:, j, i]
35
+ u_ms = ds_u[list(ds_u.data_vars)[0]].values[:, j, i]
36
+ v_ms = ds_v[list(ds_v.data_vars)[0]].values[:, j, i]
37
+ h_m = ds_h[list(ds_h.data_vars)[0]].values[:, j, i]
38
+
39
+ sort_idx = np.argsort(p_hpa)[::-1]
40
+ p = p_hpa[sort_idx].astype(np.float64)
41
+ t_c = (t_k[sort_idx] - 273.15).astype(np.float64)
42
+ td_c = (td_k[sort_idx] - 273.15).astype(np.float64)
43
+ u = u_ms[sort_idx].astype(np.float64)
44
+ v = v_ms[sort_idx].astype(np.float64)
45
+ h = h_m[sort_idx].astype(np.float64)
46
+
47
+ print(f"Nearest grid point: {lat_vals[j,i]:.4f}N {abs(lon_vals[j,i]-360):.2f}W")
48
+ print(f"Sfc: T={t_c[0]:.2f}C Td={td_c[0]:.2f}C P={p[0]:.1f}hPa Z={h[0]:.0f}m")
49
+ print(f"Levels: {len(p)}")
50
+ print()
51
+
52
+ # ─── MetPy ───
53
+ import metpy.calc as mpcalc
54
+ from metpy.units import units as mpu
55
+
56
+ p_mp = p * mpu.hPa
57
+ t_mp = t_c * mpu.degC
58
+ td_mp = td_c * mpu.degC
59
+ h_mp = h * mpu.meter
60
+ u_mp = u * mpu("m/s")
61
+ v_mp = v * mpu("m/s")
62
+
63
+ pp_sb = mpcalc.parcel_profile(p_mp, t_mp[0], td_mp[0])
64
+ mp_sbcape, mp_sbcin = mpcalc.cape_cin(p_mp, t_mp, td_mp, pp_sb)
65
+ mp_lcl_p, mp_lcl_t = mpcalc.lcl(p_mp[0], t_mp[0], td_mp[0])
66
+ mp_lfc_p, mp_lfc_t = mpcalc.lfc(p_mp, t_mp, td_mp, pp_sb)
67
+ mp_el_p, mp_el_t = mpcalc.el(p_mp, t_mp, td_mp, pp_sb)
68
+
69
+ ml_t, ml_td = mpcalc.mixed_layer(p_mp, t_mp, td_mp, depth=100 * mpu.hPa)
70
+ pp_ml = mpcalc.parcel_profile(p_mp, ml_t, ml_td)
71
+ mp_mlcape, mp_mlcin = mpcalc.cape_cin(p_mp, t_mp, td_mp, pp_ml)
72
+
73
+ mu_idx = np.argmax(mpcalc.equivalent_potential_temperature(p_mp[:20], t_mp[:20], td_mp[:20]).magnitude)
74
+ try:
75
+ pp_mu = mpcalc.parcel_profile(p_mp[mu_idx:], t_mp[mu_idx], td_mp[mu_idx])
76
+ pp_mu_arr = np.full(len(p), np.nan)
77
+ pp_mu_arr[mu_idx:] = pp_mu.magnitude
78
+ mp_mucape, mp_mucin = mpcalc.cape_cin(p_mp, t_mp, td_mp, pp_mu_arr * pp_mu.units)
79
+ except:
80
+ mp_mucape = mp_sbcape # fallback — MU is often same as SB
81
+ mp_mucin = mp_sbcin
82
+
83
+ mp_li = mpcalc.lifted_index(p_mp, t_mp, pp_sb)
84
+ mp_pw = mpcalc.precipitable_water(p_mp, td_mp)
85
+ mp_ki = mpcalc.k_index(p_mp, t_mp, td_mp)
86
+ try:
87
+ mp_tt = mpcalc.total_totals(p_mp, t_mp, td_mp)
88
+ except AttributeError:
89
+ mp_vt = mpcalc.vertical_totals(p_mp, t_mp)
90
+ mp_ct = mpcalc.cross_totals(p_mp, t_mp, td_mp)
91
+ mp_tt_val = mp_vt.magnitude + mp_ct.magnitude
92
+ mp_tt = mp_tt_val * mpu.delta_degC
93
+ else:
94
+ mp_ct = mpcalc.cross_totals(p_mp, t_mp, td_mp)
95
+ mp_vt = mpcalc.vertical_totals(p_mp, t_mp)
96
+
97
+ # Bunkers
98
+ rm, lm, mw = mpcalc.bunkers_storm_motion(p_mp, u_mp, v_mp, h_mp)
99
+ mp_rm_spd = np.sqrt(rm[0].magnitude**2 + rm[1].magnitude**2) * 1.944
100
+ mp_rm_dir = (270 - np.degrees(np.arctan2(rm[1].magnitude, rm[0].magnitude))) % 360
101
+
102
+ # Shear
103
+ def mp_shear(depth):
104
+ try:
105
+ bu, bv = mpcalc.bulk_shear(p_mp, u_mp, v_mp, height=h_mp, depth=depth * mpu.meter)
106
+ return np.sqrt(bu.magnitude**2 + bv.magnitude**2) * 1.944
107
+ except: return 0
108
+
109
+ # SRH
110
+ def mp_srh(depth):
111
+ try:
112
+ pos, neg, tot = mpcalc.storm_relative_helicity(h_mp, u_mp, v_mp, depth=depth * mpu.meter)
113
+ return tot.magnitude, pos.magnitude, neg.magnitude
114
+ except: return 0, 0, 0
115
+
116
+ # Lapse rates
117
+ def lr(pb, pt):
118
+ ib = np.argmin(np.abs(p - pb))
119
+ it = np.argmin(np.abs(p - pt))
120
+ dT = t_c[it] - t_c[ib]
121
+ dZ = (h[it] - h[ib]) / 1000.0
122
+ return -dT / dZ if dZ != 0 else 0
123
+
124
+ # ─── metrust ───
125
+ from metrust.calc import cape_cin as mr_cape_cin, parcel_profile as mr_parcel_profile
126
+ from metrust.calc import lcl as mr_lcl, precipitable_water as mr_pw
127
+ from metrust.calc import k_index as mr_ki, total_totals as mr_tt, lifted_index as mr_li
128
+ from metrust.units import units as mru
129
+
130
+ p_mr = p * mru.hPa
131
+ t_mr = t_c * mru.degC
132
+ td_mr = td_c * mru.degC
133
+
134
+ # Use the Rust cape_cin_core directly (not the Python parcel_profile wrapper)
135
+ from metrust._metrust import calc as _calc
136
+ _rust_result = _calc.cape_cin(p, t_c, td_c, h - h[0], float(p[0]), float(t_c[0]), float(td_c[0]), "sb", 100.0, 300.0, None)
137
+ mr_cape_val, mr_cin_val = _rust_result[0], _rust_result[1]
138
+ # Wrap in units for display
139
+ mr_cape = mr_cape_val * mru("J/kg")
140
+ mr_cin = mr_cin_val * mru("J/kg")
141
+ mr_lcl_p, mr_lcl_t = mr_lcl(p_mr[0], t_mr[0], td_mr[0])
142
+ mr_pwat = mr_pw(p_mr, td_mr)
143
+ mr_k = mr_ki(p_mr, t_mr, td_mr)
144
+ mr_t = mr_tt(p_mr, t_mr, td_mr)
145
+
146
+ # SharpJS reference values
147
+ sjs = {
148
+ "sbcape": 1254.83, "sbcin": -7.62, "sb_lcl": 905.92, "sb_lfc": 900.0, "sb_el": 200.0,
149
+ "mlcape": 782.51, "mlcin": -1.73, "ml_lcl": 884.0,
150
+ "mucape": 1254.83, "mucin": -7.62,
151
+ "li": -3.37, "pwat": 1.23, "ki": 17.88, "tt": 43.97, "ct": 20.40, "vt": 23.58,
152
+ "rm_dir": 337.3, "rm_spd": 24.80,
153
+ }
154
+
155
+ print("=" * 78)
156
+ print(" PARCELS SharpJS MetPy metrust")
157
+ print("-" * 78)
158
+ print(f" SBCAPE (J/kg) {sjs['sbcape']:9.1f} {mp_sbcape.magnitude:9.1f} {mr_cape.magnitude:9.1f}")
159
+ print(f" SBCIN (J/kg) {sjs['sbcin']:9.2f} {mp_sbcin.magnitude:9.2f} {mr_cin.magnitude:9.2f}")
160
+ print(f" SB LCL (hPa) {sjs['sb_lcl']:9.2f} {mp_lcl_p.magnitude:9.2f} {mr_lcl_p.magnitude:9.2f}")
161
+ try:
162
+ print(f" SB LFC (hPa) {sjs['sb_lfc']:9.2f} {mp_lfc_p.magnitude:9.2f}")
163
+ except: print(f" SB LFC (hPa) {sjs['sb_lfc']:9.2f} nan")
164
+ try:
165
+ print(f" SB EL (hPa) {sjs['sb_el']:9.2f} {mp_el_p.magnitude:9.2f}")
166
+ except: print(f" SB EL (hPa) {sjs['sb_el']:9.2f} nan")
167
+ print(f" MLCAPE (J/kg) {sjs['mlcape']:9.1f} {mp_mlcape.magnitude:9.1f}")
168
+ print(f" MLCIN (J/kg) {sjs['mlcin']:9.2f} {mp_mlcin.magnitude:9.2f}")
169
+ print(f" MUCAPE (J/kg) {sjs['mucape']:9.1f} {mp_mucape.magnitude:9.1f}")
170
+ print(f" LI {sjs['li']:9.2f} {mp_li.magnitude[0]:9.2f}")
171
+ print()
172
+
173
+ print(" INDICES SharpJS MetPy metrust")
174
+ print("-" * 78)
175
+ print(f" K-Index {sjs['ki']:9.2f} {mp_ki.magnitude:9.2f} {mr_k.magnitude:9.2f}")
176
+ print(f" Total Totals {sjs['tt']:9.2f} {mp_tt.magnitude:9.2f} {mr_t.magnitude:9.2f}")
177
+ print(f" Cross Totals {sjs['ct']:9.2f} {mp_ct.magnitude:9.2f}")
178
+ print(f" Vert Totals {sjs['vt']:9.2f} {mp_vt.magnitude:9.2f}")
179
+ print(f" PWAT (in) {sjs['pwat']:9.2f} {mp_pw.to('inch').magnitude:9.2f} {(mr_pwat.magnitude/25.4 if mr_pwat.magnitude > 5 else mr_pwat.magnitude):9.2f}")
180
+ print()
181
+
182
+ print(" SHEAR (kts) SharpJS MetPy")
183
+ print("-" * 78)
184
+ for depth, label, ref in [(500,"0-500m",5.54),(1000,"0-1km",5.83),(3000,"0-3km",12.38),(6000,"0-6km",18.35)]:
185
+ ms = mp_shear(depth)
186
+ print(f" {label:15s} {ref:9.2f} {ms:9.2f} ({ms-ref:+.2f})")
187
+ print()
188
+
189
+ print(" SRH (m2/s2) SharpJS MetPy")
190
+ print("-" * 78)
191
+ for depth, label, ref in [(500,"0-500m",3.73),(1000,"0-1km",3.70),(3000,"0-3km",33.04)]:
192
+ tot, pos, neg = mp_srh(depth)
193
+ print(f" {label:15s} {ref:9.2f} {tot:9.2f} ({tot-ref:+.2f})")
194
+ print()
195
+
196
+ print(" STORM MOTION SharpJS MetPy")
197
+ print("-" * 78)
198
+ print(f" Bunkers RM {sjs['rm_dir']:5.1f}/{sjs['rm_spd']:5.2f} {mp_rm_dir:5.1f}/{mp_rm_spd:5.2f}")
199
+ print()
200
+
201
+ print(" LAPSE RATES SharpJS MetPy")
202
+ print("-" * 78)
203
+ for (pb,pt), label, ref in [((1000,850),"1000-850",9.48),((925,700),"925-700",6.36),
204
+ ((850,500),"850-500",5.84),((700,500),"700-500",6.14),((500,300),"500-300",7.75)]:
205
+ v = lr(pb, pt)
206
+ print(f" {label:15s} {ref:9.2f} {v:9.2f} ({v-ref:+.2f})")
207
+ print("=" * 78)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes