metrust 0.2.8__tar.gz → 0.2.9__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 (186) hide show
  1. {metrust-0.2.8 → metrust-0.2.9}/Cargo.lock +6 -6
  2. {metrust-0.2.8 → metrust-0.2.9}/Cargo.toml +1 -1
  3. {metrust-0.2.8 → metrust-0.2.9}/PKG-INFO +1 -1
  4. {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/Cargo.toml +1 -1
  5. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/Cargo.toml +1 -1
  6. {metrust-0.2.8 → metrust-0.2.9}/crates/wx-field/Cargo.toml +1 -1
  7. {metrust-0.2.8 → metrust-0.2.9}/crates/wx-math/Cargo.toml +1 -1
  8. {metrust-0.2.8 → metrust-0.2.9}/crates/wx-radar/Cargo.toml +1 -1
  9. {metrust-0.2.8 → metrust-0.2.9}/docs/index.md +1 -1
  10. {metrust-0.2.8 → metrust-0.2.9}/pyproject.toml +1 -1
  11. {metrust-0.2.8 → metrust-0.2.9}/python/metrust/calc/__init__.py +307 -9
  12. metrust-0.2.8/tmp_bunkers.txt +0 -106
  13. metrust-0.2.8/tmp_wca.txt +0 -59
  14. {metrust-0.2.8 → metrust-0.2.9}/.github/workflows/ci.yml +0 -0
  15. {metrust-0.2.8 → metrust-0.2.9}/.github/workflows/docs.yml +0 -0
  16. {metrust-0.2.8 → metrust-0.2.9}/.github/workflows/release.yml +0 -0
  17. {metrust-0.2.8 → metrust-0.2.9}/.gitignore +0 -0
  18. {metrust-0.2.8 → metrust-0.2.9}/README.md +0 -0
  19. {metrust-0.2.8 → metrust-0.2.9}/VERIFICATION.md +0 -0
  20. {metrust-0.2.8 → metrust-0.2.9}/benches/bench_hrrr.py +0 -0
  21. {metrust-0.2.8 → metrust-0.2.9}/benches/bench_hrrr_vs_metpy.py +0 -0
  22. {metrust-0.2.8 → metrust-0.2.9}/benches/bench_python.py +0 -0
  23. {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/benches/calc_bench.rs +0 -0
  24. {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/calc/atmo.rs +0 -0
  25. {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/calc/kinematics.rs +0 -0
  26. {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/calc/mod.rs +0 -0
  27. {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/calc/severe.rs +0 -0
  28. {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/calc/smooth.rs +0 -0
  29. {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/calc/thermo.rs +0 -0
  30. {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/calc/utils.rs +0 -0
  31. {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/calc/wind.rs +0 -0
  32. {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/constants.rs +0 -0
  33. {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/interpolate/mod.rs +0 -0
  34. {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/io/gempak.rs +0 -0
  35. {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/io/gempak_dm.rs +0 -0
  36. {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/io/gempak_sounding.rs +0 -0
  37. {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/io/gempak_surface.rs +0 -0
  38. {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/io/gini.rs +0 -0
  39. {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/io/level3.rs +0 -0
  40. {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/io/metar.rs +0 -0
  41. {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/io/mod.rs +0 -0
  42. {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/io/station.rs +0 -0
  43. {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/io/wpc.rs +0 -0
  44. {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/lib.rs +0 -0
  45. {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/plots/mod.rs +0 -0
  46. {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/projections.rs +0 -0
  47. {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/units.rs +0 -0
  48. {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/tests/test_gempak.rs +0 -0
  49. {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/tests/test_new_functions.rs +0 -0
  50. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/composite.rs +0 -0
  51. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/download/cache.rs +0 -0
  52. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/download/catalog.rs +0 -0
  53. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/download/client.rs +0 -0
  54. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/download/fallback.rs +0 -0
  55. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/download/idx.rs +0 -0
  56. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/download/mod.rs +0 -0
  57. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/download/sources.rs +0 -0
  58. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/download/streaming.rs +0 -0
  59. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/dynamics.rs +0 -0
  60. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/error.rs +0 -0
  61. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/grib2/grid.rs +0 -0
  62. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/grib2/mod.rs +0 -0
  63. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/grib2/ops.rs +0 -0
  64. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/grib2/parser.rs +0 -0
  65. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/grib2/search.rs +0 -0
  66. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/grib2/streaming.rs +0 -0
  67. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/grib2/tables.rs +0 -0
  68. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/grib2/tests.rs +0 -0
  69. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/grib2/unpack.rs +0 -0
  70. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/grib2/writer.rs +0 -0
  71. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/gridmath.rs +0 -0
  72. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/lib.rs +0 -0
  73. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/metfuncs.rs +0 -0
  74. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/cfs.rs +0 -0
  75. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/ecmwf.rs +0 -0
  76. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/era5.rs +0 -0
  77. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/gefs.rs +0 -0
  78. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/gfs.rs +0 -0
  79. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/href.rs +0 -0
  80. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/hrrr.rs +0 -0
  81. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/hrrr_ak.rs +0 -0
  82. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/latest.rs +0 -0
  83. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/mod.rs +0 -0
  84. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/mrms.rs +0 -0
  85. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/nam.rs +0 -0
  86. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/nbm.rs +0 -0
  87. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/rap.rs +0 -0
  88. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/rrfs.rs +0 -0
  89. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/rtma.rs +0 -0
  90. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/sref.rs +0 -0
  91. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/urma.rs +0 -0
  92. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/wpc.rs +0 -0
  93. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/products.rs +0 -0
  94. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/projection.rs +0 -0
  95. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/regrid.rs +0 -0
  96. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/render/ansi.rs +0 -0
  97. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/render/colormap.rs +0 -0
  98. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/render/contour.rs +0 -0
  99. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/render/cross_section.rs +0 -0
  100. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/render/encode.rs +0 -0
  101. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/render/filled_contour.rs +0 -0
  102. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/render/hodograph.rs +0 -0
  103. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/render/mod.rs +0 -0
  104. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/render/overlay.rs +0 -0
  105. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/render/raster.rs +0 -0
  106. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/render/skewt.rs +0 -0
  107. {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/render/station.rs +0 -0
  108. {metrust-0.2.8 → metrust-0.2.9}/crates/wx-field/src/error.rs +0 -0
  109. {metrust-0.2.8 → metrust-0.2.9}/crates/wx-field/src/field.rs +0 -0
  110. {metrust-0.2.8 → metrust-0.2.9}/crates/wx-field/src/lib.rs +0 -0
  111. {metrust-0.2.8 → metrust-0.2.9}/crates/wx-field/src/meta.rs +0 -0
  112. {metrust-0.2.8 → metrust-0.2.9}/crates/wx-field/src/projection.rs +0 -0
  113. {metrust-0.2.8 → metrust-0.2.9}/crates/wx-field/src/radial.rs +0 -0
  114. {metrust-0.2.8 → metrust-0.2.9}/crates/wx-field/src/site.rs +0 -0
  115. {metrust-0.2.8 → metrust-0.2.9}/crates/wx-field/src/sounding.rs +0 -0
  116. {metrust-0.2.8 → metrust-0.2.9}/crates/wx-field/src/time.rs +0 -0
  117. {metrust-0.2.8 → metrust-0.2.9}/crates/wx-math/src/composite.rs +0 -0
  118. {metrust-0.2.8 → metrust-0.2.9}/crates/wx-math/src/dynamics.rs +0 -0
  119. {metrust-0.2.8 → metrust-0.2.9}/crates/wx-math/src/gridmath.rs +0 -0
  120. {metrust-0.2.8 → metrust-0.2.9}/crates/wx-math/src/lib.rs +0 -0
  121. {metrust-0.2.8 → metrust-0.2.9}/crates/wx-math/src/regrid.rs +0 -0
  122. {metrust-0.2.8 → metrust-0.2.9}/crates/wx-math/src/thermo.rs +0 -0
  123. {metrust-0.2.8 → metrust-0.2.9}/crates/wx-radar/src/cells.rs +0 -0
  124. {metrust-0.2.8 → metrust-0.2.9}/crates/wx-radar/src/color_table.rs +0 -0
  125. {metrust-0.2.8 → metrust-0.2.9}/crates/wx-radar/src/derived.rs +0 -0
  126. {metrust-0.2.8 → metrust-0.2.9}/crates/wx-radar/src/detection.rs +0 -0
  127. {metrust-0.2.8 → metrust-0.2.9}/crates/wx-radar/src/level2.rs +0 -0
  128. {metrust-0.2.8 → metrust-0.2.9}/crates/wx-radar/src/lib.rs +0 -0
  129. {metrust-0.2.8 → metrust-0.2.9}/crates/wx-radar/src/products.rs +0 -0
  130. {metrust-0.2.8 → metrust-0.2.9}/crates/wx-radar/src/render.rs +0 -0
  131. {metrust-0.2.8 → metrust-0.2.9}/crates/wx-radar/src/sites.rs +0 -0
  132. {metrust-0.2.8 → metrust-0.2.9}/docs/api/atmospheric.md +0 -0
  133. {metrust-0.2.8 → metrust-0.2.9}/docs/api/grid-composites.md +0 -0
  134. {metrust-0.2.8 → metrust-0.2.9}/docs/api/io.md +0 -0
  135. {metrust-0.2.8 → metrust-0.2.9}/docs/api/kinematics.md +0 -0
  136. {metrust-0.2.8 → metrust-0.2.9}/docs/api/moisture.md +0 -0
  137. {metrust-0.2.8 → metrust-0.2.9}/docs/api/severe.md +0 -0
  138. {metrust-0.2.8 → metrust-0.2.9}/docs/api/smoothing.md +0 -0
  139. {metrust-0.2.8 → metrust-0.2.9}/docs/api/thermodynamics.md +0 -0
  140. {metrust-0.2.8 → metrust-0.2.9}/docs/api/units.md +0 -0
  141. {metrust-0.2.8 → metrust-0.2.9}/docs/api/wind.md +0 -0
  142. {metrust-0.2.8 → metrust-0.2.9}/docs/compatibility.md +0 -0
  143. {metrust-0.2.8 → metrust-0.2.9}/docs/guides/arrays.md +0 -0
  144. {metrust-0.2.8 → metrust-0.2.9}/docs/guides/installation.md +0 -0
  145. {metrust-0.2.8 → metrust-0.2.9}/docs/guides/migration.md +0 -0
  146. {metrust-0.2.8 → metrust-0.2.9}/docs/performance.md +0 -0
  147. {metrust-0.2.8 → metrust-0.2.9}/docs/tutorials/first-grid.md +0 -0
  148. {metrust-0.2.8 → metrust-0.2.9}/docs/tutorials/first-sounding.md +0 -0
  149. {metrust-0.2.8 → metrust-0.2.9}/docs/tutorials/reading-the-numbers.md +0 -0
  150. {metrust-0.2.8 → metrust-0.2.9}/docs/tutorials/recipes.md +0 -0
  151. {metrust-0.2.8 → metrust-0.2.9}/docs/tutorials/weather-101.md +0 -0
  152. {metrust-0.2.8 → metrust-0.2.9}/examples/cookbook_500hpa_grid.py +0 -0
  153. {metrust-0.2.8 → metrust-0.2.9}/examples/cookbook_sounding.py +0 -0
  154. {metrust-0.2.8 → metrust-0.2.9}/examples/sounderpy_dropin.py +0 -0
  155. {metrust-0.2.8 → metrust-0.2.9}/mkdocs.yml +0 -0
  156. {metrust-0.2.8 → metrust-0.2.9}/python/metrust/__init__.py +0 -0
  157. {metrust-0.2.8 → metrust-0.2.9}/python/metrust/constants/__init__.py +0 -0
  158. {metrust-0.2.8 → metrust-0.2.9}/python/metrust/interpolate/__init__.py +0 -0
  159. {metrust-0.2.8 → metrust-0.2.9}/python/metrust/io/__init__.py +0 -0
  160. {metrust-0.2.8 → metrust-0.2.9}/python/metrust/plots/__init__.py +0 -0
  161. {metrust-0.2.8 → metrust-0.2.9}/python/metrust/units.py +0 -0
  162. {metrust-0.2.8 → metrust-0.2.9}/python/metrust/xarray.py +0 -0
  163. {metrust-0.2.8 → metrust-0.2.9}/src/lib.rs +0 -0
  164. {metrust-0.2.8 → metrust-0.2.9}/src/py_atmo.rs +0 -0
  165. {metrust-0.2.8 → metrust-0.2.9}/src/py_constants.rs +0 -0
  166. {metrust-0.2.8 → metrust-0.2.9}/src/py_interpolate.rs +0 -0
  167. {metrust-0.2.8 → metrust-0.2.9}/src/py_io.rs +0 -0
  168. {metrust-0.2.8 → metrust-0.2.9}/src/py_kinematics.rs +0 -0
  169. {metrust-0.2.8 → metrust-0.2.9}/src/py_severe.rs +0 -0
  170. {metrust-0.2.8 → metrust-0.2.9}/src/py_smooth.rs +0 -0
  171. {metrust-0.2.8 → metrust-0.2.9}/src/py_thermo.rs +0 -0
  172. {metrust-0.2.8 → metrust-0.2.9}/src/py_utils.rs +0 -0
  173. {metrust-0.2.8 → metrust-0.2.9}/src/py_wind.rs +0 -0
  174. {metrust-0.2.8 → metrust-0.2.9}/tests/api_audit_calc.md +0 -0
  175. {metrust-0.2.8 → metrust-0.2.9}/tests/api_audit_other.md +0 -0
  176. {metrust-0.2.8 → metrust-0.2.9}/tests/benchmark.py +0 -0
  177. {metrust-0.2.8 → metrust-0.2.9}/tests/test_metpy_dropin_compat.py +0 -0
  178. {metrust-0.2.8 → metrust-0.2.9}/tests/test_python_compat.py +0 -0
  179. {metrust-0.2.8 → metrust-0.2.9}/tests/verify_constants.py +0 -0
  180. {metrust-0.2.8 → metrust-0.2.9}/tests/verify_edge_cases.py +0 -0
  181. {metrust-0.2.8 → metrust-0.2.9}/tests/verify_kinematics.py +0 -0
  182. {metrust-0.2.8 → metrust-0.2.9}/tests/verify_severe_atmo.py +0 -0
  183. {metrust-0.2.8 → metrust-0.2.9}/tests/verify_smooth_interp.py +0 -0
  184. {metrust-0.2.8 → metrust-0.2.9}/tests/verify_thermo.py +0 -0
  185. {metrust-0.2.8 → metrust-0.2.9}/tests/verify_units.py +0 -0
  186. {metrust-0.2.8 → metrust-0.2.9}/tests/verify_wind.py +0 -0
@@ -788,7 +788,7 @@ dependencies = [
788
788
 
789
789
  [[package]]
790
790
  name = "metrust"
791
- version = "0.2.8"
791
+ version = "0.2.9"
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.2.8"
805
+ version = "0.2.9"
806
806
  dependencies = [
807
807
  "metrust",
808
808
  "numpy",
@@ -1419,7 +1419,7 @@ dependencies = [
1419
1419
 
1420
1420
  [[package]]
1421
1421
  name = "rustmet-core"
1422
- version = "0.2.8"
1422
+ version = "0.2.9"
1423
1423
  dependencies = [
1424
1424
  "chrono",
1425
1425
  "flate2",
@@ -1934,14 +1934,14 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
1934
1934
 
1935
1935
  [[package]]
1936
1936
  name = "wx-field"
1937
- version = "0.2.8"
1937
+ version = "0.2.9"
1938
1938
  dependencies = [
1939
1939
  "chrono",
1940
1940
  ]
1941
1941
 
1942
1942
  [[package]]
1943
1943
  name = "wx-math"
1944
- version = "0.2.8"
1944
+ version = "0.2.9"
1945
1945
  dependencies = [
1946
1946
  "rayon",
1947
1947
  "wx-field",
@@ -1949,7 +1949,7 @@ dependencies = [
1949
1949
 
1950
1950
  [[package]]
1951
1951
  name = "wx-radar"
1952
- version = "0.2.8"
1952
+ version = "0.2.9"
1953
1953
  dependencies = [
1954
1954
  "byteorder",
1955
1955
  "bzip2",
@@ -8,7 +8,7 @@ license = "MIT"
8
8
 
9
9
  [package]
10
10
  name = "metrust-py"
11
- version = "0.2.8"
11
+ version = "0.2.9"
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.2.8
3
+ Version: 0.2.9
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.2.8"
3
+ version = "0.2.9"
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 = "rustmet-core"
3
- version = "0.2.8"
3
+ version = "0.2.9"
4
4
  edition = "2021"
5
5
  description = "Pure Rust GRIB2 processor and weather model data library — HRRR, GFS, NAM, RAP"
6
6
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "wx-field"
3
- version = "0.2.8"
3
+ version = "0.2.9"
4
4
  edition = "2021"
5
5
  description = "Shared type foundation for the unified atmospheric engine — fields, projections, metadata"
6
6
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "wx-math"
3
- version = "0.2.8"
3
+ version = "0.2.9"
4
4
  edition = "2021"
5
5
  description = "Meteorological computations and grid math"
6
6
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "wx-radar"
3
- version = "0.2.8"
3
+ version = "0.2.9"
4
4
  edition = "2021"
5
5
  description = "Radar data processing — NEXRAD Level-II parser, PPI renderer, color tables"
6
6
  license = "MIT"
@@ -233,4 +233,4 @@ cape, cin = cape_cin(p, T, Td, prof)
233
233
 
234
234
  ---
235
235
 
236
- *metrust v0.2.8 -- MIT License -- [GitHub](https://github.com/FahrenheitResearch/metrust-py)*
236
+ *metrust v0.2.9 -- MIT License -- [GitHub](https://github.com/FahrenheitResearch/metrust-py)*
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "metrust"
7
- version = "0.2.8"
7
+ version = "0.2.9"
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"}
@@ -2573,11 +2573,228 @@ def _mean_spacing(val, target_unit="m"):
2573
2573
  return float(arr.mean()) if arr.ndim > 0 and arr.size > 1 else float(arr)
2574
2574
 
2575
2575
 
2576
+ def _is_variable_spacing(val):
2577
+ """Check if dx/dy is a 2D array (variable spacing, e.g., lat/lon grid)."""
2578
+ if hasattr(val, "magnitude"):
2579
+ arr = np.asarray(val.magnitude)
2580
+ else:
2581
+ arr = np.asarray(val)
2582
+ if arr.ndim < 2:
2583
+ return False
2584
+ # Check if values vary significantly (>5% relative range)
2585
+ finite = arr[np.isfinite(arr)]
2586
+ if finite.size < 2:
2587
+ return False
2588
+ rng = finite.max() - finite.min()
2589
+ return rng > 0.05 * abs(finite.mean()) if abs(finite.mean()) > 1e-10 else rng > 1e-10
2590
+
2591
+
2592
+ def _gradient_2d_variable(field, dx_2d, dy_2d):
2593
+ """Compute ∂f/∂x and ∂f/∂y with variable 2D grid spacing.
2594
+
2595
+ Uses centered differences in the interior, one-sided at boundaries,
2596
+ matching MetPy's first_derivative behavior on variable-spaced grids.
2597
+ """
2598
+ arr = np.asarray(field, dtype=np.float64)
2599
+ ny, nx = arr.shape
2600
+ dx = np.asarray(dx_2d, dtype=np.float64)
2601
+ dy = np.asarray(dy_2d, dtype=np.float64)
2602
+
2603
+ # Pad dx/dy to match field shape if needed (MetPy grid_deltas returns (ny, nx-1) and (ny-1, nx))
2604
+ if dx.shape != (ny, nx):
2605
+ if dx.shape == (ny, nx - 1):
2606
+ # Average adjacent to get (ny, nx)
2607
+ dx_full = np.empty((ny, nx))
2608
+ dx_full[:, 0] = dx[:, 0]
2609
+ dx_full[:, -1] = dx[:, -1]
2610
+ dx_full[:, 1:-1] = (dx[:, :-1] + dx[:, 1:]) / 2.0
2611
+ dx = dx_full
2612
+ else:
2613
+ dx = np.broadcast_to(dx, (ny, nx)).copy()
2614
+ if dy.shape != (ny, nx):
2615
+ if dy.shape == (ny - 1, nx):
2616
+ dy_full = np.empty((ny, nx))
2617
+ dy_full[0, :] = dy[0, :]
2618
+ dy_full[-1, :] = dy[-1, :]
2619
+ dy_full[1:-1, :] = (dy[:-1, :] + dy[1:, :]) / 2.0
2620
+ dy = dy_full
2621
+ else:
2622
+ dy = np.broadcast_to(dy, (ny, nx)).copy()
2623
+
2624
+ # Replace zeros/near-zeros with NaN to avoid division by zero (poles)
2625
+ dx[np.abs(dx) < 1.0] = np.nan
2626
+ dy[np.abs(dy) < 1.0] = np.nan
2627
+
2628
+ # ∂f/∂x — centered differences along axis=1
2629
+ dfdx = np.full_like(arr, np.nan)
2630
+ dfdx[:, 1:-1] = (arr[:, 2:] - arr[:, :-2]) / (2.0 * dx[:, 1:-1])
2631
+ dfdx[:, 0] = (arr[:, 1] - arr[:, 0]) / dx[:, 0]
2632
+ dfdx[:, -1] = (arr[:, -1] - arr[:, -2]) / dx[:, -1]
2633
+
2634
+ # ∂f/∂y — centered differences along axis=0
2635
+ dfdy = np.full_like(arr, np.nan)
2636
+ dfdy[1:-1, :] = (arr[2:, :] - arr[:-2, :]) / (2.0 * dy[1:-1, :])
2637
+ dfdy[0, :] = (arr[1, :] - arr[0, :]) / dy[0, :]
2638
+ dfdy[-1, :] = (arr[-1, :] - arr[-2, :]) / dy[-1, :]
2639
+
2640
+ return dfdx, dfdy
2641
+
2642
+
2576
2643
  def _safe_unit_str(unit_obj):
2577
2644
  """Return a unit string usable with *our* registry, avoiding cross-registry ops."""
2578
2645
  return str(unit_obj)
2579
2646
 
2580
2647
 
2648
+ def _first_derivative_variable(field, delta, axis):
2649
+ """Compute first derivative with variable spacing along an axis.
2650
+
2651
+ Matches MetPy's first_derivative: centered differences in interior,
2652
+ one-sided at boundaries. *delta* is a 1-D or 2-D array of grid spacings
2653
+ (one fewer element than field along *axis*).
2654
+ """
2655
+ arr = np.asarray(field, dtype=np.float64)
2656
+ d = np.asarray(delta, dtype=np.float64).copy()
2657
+ # Replace near-zero spacings with NaN to avoid division by zero (e.g., at poles)
2658
+ d[np.abs(d) < 1.0] = np.nan
2659
+ n = arr.shape[axis]
2660
+
2661
+ # Expand delta to match field dimensions if needed
2662
+ if d.ndim == 1 and d.size == n - 1:
2663
+ # Standard case: spacing between adjacent levels
2664
+ pass
2665
+ elif d.ndim == 2 and d.shape[axis] == n - 1:
2666
+ pass
2667
+ elif d.ndim == 2 and d.shape[axis] == n:
2668
+ # Average adjacent to get n-1 spacings
2669
+ d = (np.take(d, range(d.shape[axis] - 1), axis=axis)
2670
+ + np.take(d, range(1, d.shape[axis]), axis=axis)) / 2.0
2671
+ elif d.size == 1:
2672
+ return np.gradient(arr, float(d.ravel()[0]), axis=axis)
2673
+ else:
2674
+ return np.gradient(arr, float(np.mean(d)), axis=axis)
2675
+
2676
+ result = np.empty_like(arr)
2677
+ # Interior: centered differences
2678
+ slc_c = [slice(None)] * arr.ndim
2679
+ slc_p = [slice(None)] * arr.ndim
2680
+ slc_m = [slice(None)] * arr.ndim
2681
+ slc_c[axis] = slice(1, -1)
2682
+ slc_p[axis] = slice(2, None)
2683
+ slc_m[axis] = slice(None, -2)
2684
+
2685
+ # d_fwd[i] = spacing from i to i+1, d_bwd[i] = spacing from i-1 to i
2686
+ slc_df = [slice(None)] * d.ndim
2687
+ slc_db = [slice(None)] * d.ndim
2688
+ slc_df[axis] = slice(1, None) # d[1:]
2689
+ slc_db[axis] = slice(None, -1) # d[:-1]
2690
+
2691
+ d_fwd = d[tuple(slc_df)]
2692
+ d_bwd = d[tuple(slc_db)]
2693
+
2694
+ # Broadcast delta to match field shape
2695
+ if d_fwd.ndim < arr.ndim:
2696
+ shape = [1] * arr.ndim
2697
+ shape[axis] = d_fwd.shape[0] if d_fwd.ndim > 0 else 1
2698
+ d_fwd = d_fwd.reshape(shape)
2699
+ d_bwd = d_bwd.reshape(shape)
2700
+
2701
+ result[tuple(slc_c)] = (arr[tuple(slc_p)] - arr[tuple(slc_m)]) / (d_fwd + d_bwd)
2702
+
2703
+ # Boundaries: forward/backward differences
2704
+ slc_0 = [slice(None)] * arr.ndim
2705
+ slc_1 = [slice(None)] * arr.ndim
2706
+ slc_0[axis] = 0
2707
+ slc_1[axis] = 1
2708
+ d0 = np.take(d, 0, axis=axis)
2709
+ if d0.ndim < arr[tuple(slc_0)].ndim:
2710
+ d0 = np.expand_dims(d0, axis=axis) if d0.ndim > 0 else d0
2711
+ result[tuple(slc_0)] = (arr[tuple(slc_1)] - arr[tuple(slc_0)]) / d0
2712
+
2713
+ slc_n1 = [slice(None)] * arr.ndim
2714
+ slc_n2 = [slice(None)] * arr.ndim
2715
+ slc_n1[axis] = -1
2716
+ slc_n2[axis] = -2
2717
+ dn = np.take(d, -1, axis=axis)
2718
+ if dn.ndim < arr[tuple(slc_n1)].ndim:
2719
+ dn = np.expand_dims(dn, axis=axis) if dn.ndim > 0 else dn
2720
+ result[tuple(slc_n1)] = (arr[tuple(slc_n1)] - arr[tuple(slc_n2)]) / dn
2721
+
2722
+ return result
2723
+
2724
+
2725
+ def _get_scale_factors(data):
2726
+ """Extract map projection scale factors from xarray CRS metadata.
2727
+
2728
+ Returns (parallel_scale, meridional_scale) as 2D arrays matching data shape,
2729
+ or (None, None) if no CRS is available.
2730
+ """
2731
+ if not hasattr(data, "metpy"):
2732
+ return None, None
2733
+ try:
2734
+ crs = data.metpy.cartopy_crs
2735
+ except Exception:
2736
+ return None, None
2737
+
2738
+ lat_arr, lon_arr = _infer_lat_lon(data)
2739
+ if lat_arr is None:
2740
+ return None, None
2741
+
2742
+ lat_2d = np.asarray(lat_arr.magnitude if hasattr(lat_arr, "magnitude") else lat_arr, dtype=np.float64)
2743
+ lon_2d = np.asarray(lon_arr.magnitude if hasattr(lon_arr, "magnitude") else lon_arr, dtype=np.float64)
2744
+
2745
+ ny, nx = data.shape[-2:]
2746
+ if lat_2d.ndim == 1:
2747
+ lat_2d = np.broadcast_to(lat_2d[:, None], (ny, nx))
2748
+ lon_2d = np.broadcast_to(lon_2d[None, :], (ny, nx))
2749
+
2750
+ try:
2751
+ from pyproj import Proj
2752
+ proj = Proj(crs)
2753
+ factors = proj.get_factors(lon_2d.ravel(), lat_2d.ravel())
2754
+ ps = np.asarray(factors.parallel_scale).reshape(ny, nx)
2755
+ ms = np.asarray(factors.meridional_scale).reshape(ny, nx)
2756
+ return ps, ms
2757
+ except Exception:
2758
+ # Fallback for lat/lon: parallel_scale = 1/cos(lat), meridional_scale = 1
2759
+ ps = 1.0 / np.cos(np.deg2rad(lat_2d))
2760
+ ms = np.ones_like(ps)
2761
+ return ps, ms
2762
+
2763
+
2764
+ def _vector_derivative_corrected(u_arr, v_arr, dx, dy, parallel_scale, meridional_scale):
2765
+ """Compute map-projection-corrected vector derivative components.
2766
+
2767
+ Returns (du_dy_corrected, dv_dx_corrected) for vorticity,
2768
+ or (du_dx_corrected, dv_dy_corrected) for divergence.
2769
+ """
2770
+ dx_m = np.asarray(dx.to("m").magnitude if hasattr(dx, "to") else dx, dtype=np.float64)
2771
+ dy_m = np.asarray(dy.to("m").magnitude if hasattr(dy, "to") else dy, dtype=np.float64)
2772
+
2773
+ # Cartesian derivatives with variable spacing
2774
+ du_dx = _first_derivative_variable(u_arr, dx_m, axis=-1)
2775
+ du_dy = _first_derivative_variable(u_arr, dy_m, axis=-2)
2776
+ dv_dx = _first_derivative_variable(v_arr, dx_m, axis=-1)
2777
+ dv_dy = _first_derivative_variable(v_arr, dy_m, axis=-2)
2778
+
2779
+ ps = np.asarray(parallel_scale, dtype=np.float64)
2780
+ ms = np.asarray(meridional_scale, dtype=np.float64)
2781
+
2782
+ # Scale factor derivatives
2783
+ dp_dy = _first_derivative_variable(ps, dy_m, axis=-2)
2784
+ dm_dx = _first_derivative_variable(ms, dx_m, axis=-1)
2785
+
2786
+ # Map factor corrections (MetPy vector_derivative formula)
2787
+ dx_correction = ms / ps * dp_dy
2788
+ dy_correction = ps / ms * dm_dx
2789
+
2790
+ du_dx_corr = ps * du_dx - v_arr * dx_correction
2791
+ du_dy_corr = ms * du_dy + v_arr * dy_correction
2792
+ dv_dx_corr = ps * dv_dx + u_arr * dx_correction
2793
+ dv_dy_corr = ms * dv_dy - u_arr * dy_correction
2794
+
2795
+ return du_dx_corr, du_dy_corr, dv_dx_corr, dv_dy_corr
2796
+
2797
+
2581
2798
  def divergence(u, v, dx=None, dy=None, x_dim=-1, y_dim=-2, parallel_scale=None,
2582
2799
  meridional_scale=None, latitude=None, longitude=None, crs=None):
2583
2800
  """Horizontal divergence on a 2-D grid.
@@ -2598,8 +2815,34 @@ def divergence(u, v, dx=None, dy=None, x_dim=-1, y_dim=-2, parallel_scale=None,
2598
2815
  raise TypeError("divergence requires dx/dy or inferable latitude/longitude coordinates")
2599
2816
  u_arr = np.asarray(_strip(u, "m/s"), dtype=np.float64)
2600
2817
  v_arr = np.asarray(_strip(v, "m/s"), dtype=np.float64)
2601
- dx_val = _mean_spacing(dx, "m")
2602
- dy_val = _mean_spacing(dy, "m")
2818
+
2819
+ # Check for map projection scale factors (spherical corrections)
2820
+ if parallel_scale is None and meridional_scale is None:
2821
+ ps, ms = _get_scale_factors(u)
2822
+ else:
2823
+ ps = np.asarray(parallel_scale, dtype=np.float64) if parallel_scale is not None else None
2824
+ ms = np.asarray(meridional_scale, dtype=np.float64) if meridional_scale is not None else None
2825
+
2826
+ dx_m = np.asarray(dx.to("m").magnitude if hasattr(dx, "to") else dx, dtype=np.float64)
2827
+ dy_m = np.asarray(dy.to("m").magnitude if hasattr(dy, "to") else dy, dtype=np.float64)
2828
+
2829
+ # If we have scale factors, use full vector derivative with metric corrections
2830
+ if ps is not None and ms is not None:
2831
+ du_dx_corr, _, _, dv_dy_corr = _vector_derivative_corrected(
2832
+ u_arr, v_arr, dx, dy, ps, ms)
2833
+ result = du_dx_corr + dv_dy_corr
2834
+ return _wrap_result_like(u, result, "1/s")
2835
+
2836
+ # Variable spacing without scale factors
2837
+ if _is_variable_spacing(dx) or _is_variable_spacing(dy) or dx_m.ndim >= 2:
2838
+ dudx = _first_derivative_variable(u_arr, dx_m, axis=-1)
2839
+ dvdy = _first_derivative_variable(v_arr, dy_m, axis=-2)
2840
+ result = dudx + dvdy
2841
+ return _wrap_result_like(u, result, "1/s")
2842
+
2843
+ # Uniform grid: fast Rust path
2844
+ dx_val = float(dx_m.mean()) if dx_m.ndim > 0 else float(dx_m)
2845
+ dy_val = float(dy_m.mean()) if dy_m.ndim > 0 else float(dy_m)
2603
2846
  if u_arr.ndim == 2:
2604
2847
  result = np.asarray(_calc.divergence(np.ascontiguousarray(u_arr), np.ascontiguousarray(v_arr), dx_val, dy_val))
2605
2848
  return _wrap_result_like(u, result, "1/s")
@@ -2632,11 +2875,37 @@ def vorticity(u, v, dx=None, dy=None, x_dim=-1, y_dim=-2,
2632
2875
  dx, dy = _resolve_dx_dy(u, dx=dx, dy=dy, latitude=latitude, longitude=longitude)
2633
2876
  if dx is None or dy is None:
2634
2877
  raise TypeError("vorticity requires dx/dy or inferable latitude/longitude coordinates")
2635
- u_f = _flat(u, "m/s")
2636
- v_f = _flat(v, "m/s")
2637
- dx_val = _mean_spacing(dx, "m")
2638
- dy_val = _mean_spacing(dy, "m")
2639
- result = np.asarray(_calc.vorticity(u_f, v_f, dx_val, dy_val))
2878
+ u_arr = np.asarray(_strip(u, "m/s"), dtype=np.float64)
2879
+ v_arr = np.asarray(_strip(v, "m/s"), dtype=np.float64)
2880
+
2881
+ # Check for map projection scale factors (spherical corrections)
2882
+ if parallel_scale is None and meridional_scale is None:
2883
+ ps, ms = _get_scale_factors(u)
2884
+ else:
2885
+ ps = np.asarray(parallel_scale, dtype=np.float64) if parallel_scale is not None else None
2886
+ ms = np.asarray(meridional_scale, dtype=np.float64) if meridional_scale is not None else None
2887
+
2888
+ dx_m = np.asarray(dx.to("m").magnitude if hasattr(dx, "to") else dx, dtype=np.float64)
2889
+ dy_m = np.asarray(dy.to("m").magnitude if hasattr(dy, "to") else dy, dtype=np.float64)
2890
+
2891
+ # If we have scale factors, use full vector derivative with metric corrections
2892
+ if ps is not None and ms is not None:
2893
+ _, du_dy_corr, dv_dx_corr, _ = _vector_derivative_corrected(
2894
+ u_arr, v_arr, dx, dy, ps, ms)
2895
+ result = dv_dx_corr - du_dy_corr
2896
+ return _wrap_result_like(u, result, "1/s")
2897
+
2898
+ # Variable spacing without scale factors (flat-Earth, variable grid)
2899
+ if _is_variable_spacing(dx) or _is_variable_spacing(dy) or dx_m.ndim >= 2:
2900
+ dvdx = _first_derivative_variable(v_arr, dx_m, axis=-1)
2901
+ dudy = _first_derivative_variable(u_arr, dy_m, axis=-2)
2902
+ result = dvdx - dudy
2903
+ return _wrap_result_like(u, result, "1/s")
2904
+
2905
+ # Uniform grid: fast Rust path
2906
+ dx_val = float(dx_m.mean()) if dx_m.ndim > 0 else float(dx_m)
2907
+ dy_val = float(dy_m.mean()) if dy_m.ndim > 0 else float(dy_m)
2908
+ result = np.asarray(_calc.vorticity(np.ascontiguousarray(u_arr), np.ascontiguousarray(v_arr), dx_val, dy_val))
2640
2909
  return _wrap_result_like(u, result, "1/s")
2641
2910
 
2642
2911
 
@@ -4056,8 +4325,37 @@ def lat_lon_grid_deltas(longitude, latitude, x_dim=-1, y_dim=-2, geod=None):
4056
4325
  lon_arr, lat_arr = lat_arr, lon_arr
4057
4326
  if lon_arr.ndim == 1 and lat_arr.ndim == 1:
4058
4327
  lon_arr, lat_arr = np.meshgrid(lon_arr, lat_arr)
4059
- dx, dy = _calc.lat_lon_grid_deltas(lat_arr, lon_arr)
4060
- return np.asarray(dx) * units.m, np.asarray(dy) * units.m
4328
+ dx_abs, dy_abs = _calc.lat_lon_grid_deltas(lat_arr, lon_arr)
4329
+ dx_out = np.asarray(dx_abs, dtype=np.float64)
4330
+ dy_out = np.asarray(dy_abs, dtype=np.float64)
4331
+
4332
+ # Apply sign based on coordinate direction (MetPy convention):
4333
+ # dx > 0 when longitude increases, dy > 0 when latitude increases
4334
+ ny, nx = lat_arr.shape
4335
+ # dx sign: based on longitude difference (column-wise)
4336
+ if nx > 1:
4337
+ lon_sign = np.sign(lon_arr[:, 1:] - lon_arr[:, :-1])
4338
+ # Pad to match dx shape (which may be ny x nx or ny x nx-1)
4339
+ if dx_out.shape[-1] == nx - 1:
4340
+ dx_out = dx_out * lon_sign
4341
+ elif dx_out.shape[-1] == nx:
4342
+ lon_sign_full = np.ones((ny, nx))
4343
+ lon_sign_full[:, 1:] = lon_sign
4344
+ lon_sign_full[:, 0] = lon_sign[:, 0]
4345
+ dx_out = dx_out * lon_sign_full
4346
+
4347
+ # dy sign: based on latitude difference (row-wise)
4348
+ if ny > 1:
4349
+ lat_sign = np.sign(lat_arr[1:, :] - lat_arr[:-1, :])
4350
+ if dy_out.shape[0] == ny - 1:
4351
+ dy_out = dy_out * lat_sign
4352
+ elif dy_out.shape[0] == ny:
4353
+ lat_sign_full = np.ones((ny, nx))
4354
+ lat_sign_full[1:, :] = lat_sign
4355
+ lat_sign_full[0, :] = lat_sign[0, :]
4356
+ dy_out = dy_out * lat_sign_full
4357
+
4358
+ return dx_out * units.m, dy_out * units.m
4061
4359
 
4062
4360
 
4063
4361
  # ============================================================================
@@ -1,106 +0,0 @@
1
- @exporter.export
2
- @preprocess_and_wrap()
3
- @check_units('[pressure]', '[speed]', '[speed]', '[length]')
4
- def bunkers_storm_motion(pressure, u, v, height):
5
- r"""Calculate right-mover and left-mover supercell storm motions using the Bunkers method.
6
-
7
- This is a physically based, shear-relative, and Galilean invariant method for predicting
8
- supercell motion. Full atmospheric profiles of wind components, as well as pressure and
9
- heights, need to be provided so that calculation can properly calculate the required
10
- surface to 6 km mean flow.
11
-
12
- The calculation in summary is (from [Bunkers2000]_):
13
-
14
- * surface to 6 km non-pressure-weighted mean wind
15
- * a deviation from the sfc to 6 km mean wind of 7.5 m s−1
16
- * a 5.5 to 6 km mean wind for the head of the vertical wind shear vector
17
- * a surface to 0.5 km mean wind for the tail of the vertical wind shear vector
18
-
19
- Parameters
20
- ----------
21
- pressure : `pint.Quantity`
22
- Pressure from full profile
23
-
24
- u : `pint.Quantity`
25
- Full profile of the U-component of the wind
26
-
27
- v : `pint.Quantity`
28
- Full profile of the V-component of the wind
29
-
30
- height : `pint.Quantity`
31
- Full profile of height
32
-
33
- Returns
34
- -------
35
- right_mover: (`pint.Quantity`, `pint.Quantity`)
36
- Scalar U- and V- components of Bunkers right-mover storm motion
37
-
38
- left_mover: (`pint.Quantity`, `pint.Quantity`)
39
- Scalar U- and V- components of Bunkers left-mover storm motion
40
-
41
- wind_mean: (`pint.Quantity`, `pint.Quantity`)
42
- Scalar U- and V- components of surface to 6 km mean flow
43
-
44
- Examples
45
- --------
46
- >>> from metpy.calc import bunkers_storm_motion, wind_components
47
- >>> from metpy.units import units
48
- >>> p = [1000, 925, 850, 700, 500, 400] * units.hPa
49
- >>> h = [250, 700, 1500, 3100, 5720, 7120] * units.meters
50
- >>> wdir = [165, 180, 190, 210, 220, 250] * units.degree
51
- >>> sped = [5, 15, 20, 30, 50, 60] * units.knots
52
- >>> u, v = wind_components(sped, wdir)
53
- >>> bunkers_storm_motion(p, u, v, h)
54
- (<Quantity([22.09618172 12.43406736], 'knot')>,
55
- <Quantity([ 6.02861839 36.76517865], 'knot')>,
56
- <Quantity([14.06240005 24.599623 ], 'knot')>)
57
-
58
- Notes
59
- -----
60
- Only functions on 1D profiles (not higher-dimension vertical cross sections or grids).
61
- Since this function returns scalar values when given a profile, this will return Pint
62
- Quantities even when given xarray DataArray profiles.
63
-
64
- .. versionchanged:: 1.0
65
- Renamed ``heights`` parameter to ``height``
66
-
67
- """
68
- # remove nans from input data
69
- pressure, u, v, height = _remove_nans(pressure, u, v, height)
70
-
71
- # mean wind from sfc-6km
72
- wind_mean = weighted_continuous_average(pressure, u, v, height=height,
73
- depth=units.Quantity(6000, 'meter'))
74
-
75
- wind_mean = units.Quantity.from_list(wind_mean)
76
-
77
- # mean wind from sfc-500m
78
- wind_500m = weighted_continuous_average(pressure, u, v, height=height,
79
- depth=units.Quantity(500, 'meter'))
80
-
81
- wind_500m = units.Quantity.from_list(wind_500m)
82
-
83
- # mean wind from 5.5-6km
84
- wind_5500m = weighted_continuous_average(
85
- pressure, u, v, height=height,
86
- depth=units.Quantity(500, 'meter'),
87
- bottom=height[0] + units.Quantity(5500, 'meter'))
88
-
89
- wind_5500m = units.Quantity.from_list(wind_5500m)
90
-
91
- # Calculate the shear vector from sfc-500m to 5.5-6km
92
- shear = wind_5500m - wind_500m
93
-
94
- # Take the cross product of the wind shear and k, and divide by the vector magnitude and
95
- # multiply by the deviation empirically calculated in Bunkers (2000) (7.5 m/s)
96
- shear_cross = concatenate([shear[1], -shear[0]])
97
- shear_mag = np.hypot(*shear)
98
- rdev = shear_cross * (units.Quantity(7.5, 'm/s').to(u.units) / shear_mag)
99
-
100
- # Add the deviations to the layer average wind to get the RM motion
101
- right_mover = wind_mean + rdev
102
-
103
- # Subtract the deviations to get the LM motion
104
- left_mover = wind_mean - rdev
105
-
106
- return right_mover, left_mover, wind_mean
metrust-0.2.8/tmp_wca.txt DELETED
@@ -1,59 +0,0 @@
1
- @exporter.export
2
- @preprocess_and_wrap()
3
- @check_units('[pressure]')
4
- def weighted_continuous_average(pressure, *args, height=None, bottom=None, depth=None):
5
- r"""Calculate weighted-continuous mean of an arbitrary variable through a layer.
6
-
7
- Layer top and bottom specified in height or pressure.
8
-
9
- Formula based on that from [Holton2004]_ pg. 76 and the NCL function ``wgt_vertical_n``
10
-
11
- .. math:: WCA = \frac{\int_{p_s}^{p_b} A dp}{\int_{p_s}^{p_b} dp}
12
-
13
- where:
14
-
15
- * :math:`WCA` is the weighted continuous average of a variable.
16
- * :math:`p_b` is the bottom pressure level.
17
- * :math:`p_s` is the top pressure level.
18
- * :math:`A` is the variable whose weighted continuous average is being calculated.
19
-
20
- Parameters
21
- ----------
22
- pressure : `pint.Quantity`
23
- Atmospheric pressure profile.
24
-
25
- args : `pint.Quantity`
26
- Parameters for which the weighted-continuous mean is to be calculated.
27
-
28
- height : `pint.Quantity`, optional
29
- Heights from sounding. Standard atmosphere heights assumed (if needed)
30
- if no heights are given.
31
-
32
- bottom: `pint.Quantity`, optional
33
- The bottom of the layer in either the provided height coordinate
34
- or in pressure. Don't provide in meters AGL unless the provided
35
- height coordinate is meters AGL. Default is the first observation,
36
- assumed to be the surface.
37
-
38
- depth: `pint.Quantity`, optional
39
- Depth of the layer in meters or hPa.
40
-
41
- Returns
42
- -------
43
- list of `pint.Quantity`
44
- list of layer mean value for each profile in args.
45
-
46
- Notes
47
- -----
48
- Only functions on 1D profiles (not higher-dimension vertical cross sections or grids).
49
- Since this function returns scalar values when given a profile, this will return Pint
50
- Quantities even when given xarray DataArray profiles.
51
-
52
- """
53
- # Split pressure profile from other variables to average
54
- pres_prof, *others = get_layer(
55
- pressure, *args, height=height, bottom=bottom, depth=depth
56
- )
57
-
58
- return [trapezoid(var_prof, x=pres_prof) / (pres_prof[-1] - pres_prof[0])
59
- for var_prof in others]
File without changes
File without changes
File without changes
File without changes
File without changes