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.
- {metrust-0.2.8 → metrust-0.2.9}/Cargo.lock +6 -6
- {metrust-0.2.8 → metrust-0.2.9}/Cargo.toml +1 -1
- {metrust-0.2.8 → metrust-0.2.9}/PKG-INFO +1 -1
- {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/Cargo.toml +1 -1
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/Cargo.toml +1 -1
- {metrust-0.2.8 → metrust-0.2.9}/crates/wx-field/Cargo.toml +1 -1
- {metrust-0.2.8 → metrust-0.2.9}/crates/wx-math/Cargo.toml +1 -1
- {metrust-0.2.8 → metrust-0.2.9}/crates/wx-radar/Cargo.toml +1 -1
- {metrust-0.2.8 → metrust-0.2.9}/docs/index.md +1 -1
- {metrust-0.2.8 → metrust-0.2.9}/pyproject.toml +1 -1
- {metrust-0.2.8 → metrust-0.2.9}/python/metrust/calc/__init__.py +307 -9
- metrust-0.2.8/tmp_bunkers.txt +0 -106
- metrust-0.2.8/tmp_wca.txt +0 -59
- {metrust-0.2.8 → metrust-0.2.9}/.github/workflows/ci.yml +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/.github/workflows/docs.yml +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/.github/workflows/release.yml +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/.gitignore +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/README.md +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/VERIFICATION.md +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/benches/bench_hrrr.py +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/benches/bench_hrrr_vs_metpy.py +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/benches/bench_python.py +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/benches/calc_bench.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/calc/atmo.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/calc/kinematics.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/calc/mod.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/calc/severe.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/calc/smooth.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/calc/thermo.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/calc/utils.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/calc/wind.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/constants.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/interpolate/mod.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/io/gempak.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/io/gempak_dm.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/io/gempak_sounding.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/io/gempak_surface.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/io/gini.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/io/level3.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/io/metar.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/io/mod.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/io/station.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/io/wpc.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/lib.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/plots/mod.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/projections.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/src/units.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/tests/test_gempak.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/metrust/tests/test_new_functions.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/composite.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/download/cache.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/download/catalog.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/download/client.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/download/fallback.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/download/idx.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/download/mod.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/download/sources.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/download/streaming.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/dynamics.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/error.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/grib2/grid.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/grib2/mod.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/grib2/ops.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/grib2/parser.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/grib2/search.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/grib2/streaming.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/grib2/tables.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/grib2/tests.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/grib2/unpack.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/grib2/writer.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/gridmath.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/lib.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/metfuncs.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/cfs.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/ecmwf.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/era5.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/gefs.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/gfs.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/href.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/hrrr.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/hrrr_ak.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/latest.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/mod.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/mrms.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/nam.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/nbm.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/rap.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/rrfs.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/rtma.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/sref.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/urma.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/models/wpc.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/products.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/projection.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/regrid.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/render/ansi.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/render/colormap.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/render/contour.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/render/cross_section.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/render/encode.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/render/filled_contour.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/render/hodograph.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/render/mod.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/render/overlay.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/render/raster.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/render/skewt.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/rustmet-core/src/render/station.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/wx-field/src/error.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/wx-field/src/field.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/wx-field/src/lib.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/wx-field/src/meta.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/wx-field/src/projection.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/wx-field/src/radial.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/wx-field/src/site.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/wx-field/src/sounding.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/wx-field/src/time.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/wx-math/src/composite.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/wx-math/src/dynamics.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/wx-math/src/gridmath.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/wx-math/src/lib.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/wx-math/src/regrid.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/wx-math/src/thermo.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/wx-radar/src/cells.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/wx-radar/src/color_table.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/wx-radar/src/derived.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/wx-radar/src/detection.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/wx-radar/src/level2.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/wx-radar/src/lib.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/wx-radar/src/products.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/wx-radar/src/render.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/crates/wx-radar/src/sites.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/docs/api/atmospheric.md +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/docs/api/grid-composites.md +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/docs/api/io.md +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/docs/api/kinematics.md +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/docs/api/moisture.md +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/docs/api/severe.md +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/docs/api/smoothing.md +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/docs/api/thermodynamics.md +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/docs/api/units.md +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/docs/api/wind.md +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/docs/compatibility.md +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/docs/guides/arrays.md +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/docs/guides/installation.md +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/docs/guides/migration.md +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/docs/performance.md +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/docs/tutorials/first-grid.md +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/docs/tutorials/first-sounding.md +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/docs/tutorials/reading-the-numbers.md +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/docs/tutorials/recipes.md +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/docs/tutorials/weather-101.md +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/examples/cookbook_500hpa_grid.py +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/examples/cookbook_sounding.py +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/examples/sounderpy_dropin.py +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/mkdocs.yml +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/python/metrust/__init__.py +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/python/metrust/constants/__init__.py +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/python/metrust/interpolate/__init__.py +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/python/metrust/io/__init__.py +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/python/metrust/plots/__init__.py +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/python/metrust/units.py +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/python/metrust/xarray.py +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/src/lib.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/src/py_atmo.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/src/py_constants.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/src/py_interpolate.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/src/py_io.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/src/py_kinematics.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/src/py_severe.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/src/py_smooth.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/src/py_thermo.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/src/py_utils.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/src/py_wind.rs +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/tests/api_audit_calc.md +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/tests/api_audit_other.md +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/tests/benchmark.py +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/tests/test_metpy_dropin_compat.py +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/tests/test_python_compat.py +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/tests/verify_constants.py +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/tests/verify_edge_cases.py +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/tests/verify_kinematics.py +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/tests/verify_severe_atmo.py +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/tests/verify_smooth_interp.py +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/tests/verify_thermo.py +0 -0
- {metrust-0.2.8 → metrust-0.2.9}/tests/verify_units.py +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
1952
|
+
version = "0.2.9"
|
|
1953
1953
|
dependencies = [
|
|
1954
1954
|
"byteorder",
|
|
1955
1955
|
"bzip2",
|
|
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "metrust"
|
|
7
|
-
version = "0.2.
|
|
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
|
-
|
|
2602
|
-
|
|
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
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
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
|
-
|
|
4060
|
-
|
|
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
|
# ============================================================================
|
metrust-0.2.8/tmp_bunkers.txt
DELETED
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|