Calculation#
The Skyborn calculation module provides statistical, atmospheric, and mathematical functions for climate data analysis.
Atmospheric Physics Functions#
Tropopause Calculations#
- trop_wmo(temperature, pressure, xdim, ydim, levdim, timedim=None, pressure_unit='hPa', lapse_criterion=2.0, missing_value=-999.0, check_pressure_order=True)[source]#
Calculate WMO tropopause properties for multi-dimensional atmospheric data.
This function processes gridded atmospheric data to find the tropopause following the WMO definition (lapse rate < 2 K/km). Designed for isobaric (constant pressure level) data.
- Parameters:
temperature (array_like) – Atmospheric temperature data [K] on isobaric levels. Must be ordered to correspond with the pressure levels.
pressure (array_like) –
Atmospheric pressure data [hPa or Pa]. Can be either:
1D array: Pressure levels (recommended for isobaric data). Length must match the level dimension of temperature.
Multi-dimensional array: Same shape as temperature (legacy support).
CRITICAL: Pressure levels must be sorted in ascending order (from low pressure/high altitude to high pressure/low altitude). This is required by the underlying WMO tropopause algorithm.
xdim (int) – Longitude dimension index
ydim (int) – Latitude dimension index
levdim (int) – Vertical level dimension index
timedim (int, optional) – Time dimension index for 4D data
pressure_unit (str, default 'hPa') – Unit of pressure data (‘hPa’ or ‘Pa’)
lapse_criterion (float, default 2.0) – WMO lapse rate criterion [K/km] for tropopause definition
missing_value (float, default -999.0) – Value to use for missing data
check_pressure_order (bool, default True) – Whether to check and enforce ascending pressure order along the level dimension
- Returns:
Dictionary containing: - ‘pressure’: Tropopause pressure [hPa] - ‘height’: Tropopause height [m] - ‘level_index’: Tropopause level index (0-based) - ‘lapse_rate’: Tropopause lapse rate [K/km] - ‘success’: Success flag for each grid point
- Return type:
Examples
Calculate tropopause for 4D isobaric data (time, level, lat, lon):
>>> # Define isobaric pressure levels (ascending order: high altitude to surface) >>> pressure_levels = np.array([10, 20, 50, 100, 200, 300, 500, 700, 850, 1000]) # hPa >>> temperature = 300 - np.random.rand(12, 10, 180, 360) * 80 # K >>> result = trop_wmo(temperature, pressure_levels, ... xdim=3, ydim=2, levdim=1, timedim=0) >>> print(f"Pressure shape: {result['pressure'].shape}") # (12, 180, 360) >>> print(f"Height shape: {result['height'].shape}") # (12, 180, 360)
Calculate tropopause for 3D isobaric data (level, lat, lon):
>>> pressure_levels = np.array([50, 100, 200, 300, 500, 700, 850, 1000]) # hPa >>> temperature_3d = 300 - np.random.rand(8, 180, 360) * 80 # K >>> result = trop_wmo(temperature_3d, pressure_levels, ... xdim=2, ydim=1, levdim=0) >>> print(f"Pressure shape: {result['pressure'].shape}") # (180, 360)
Notes
This function is optimized for isobaric data (constant pressure levels).
For model level data, first interpolate to pressure levels.
Requires compiled Fortran extensions. Install with: pip install skyborn[fortran]
The underlying algorithm follows the WMO (1957) tropopause definition.
- trop_wmo_profile(temperature, pressure, pressure_unit='hPa', lapse_criterion=2.0, missing_value=-999.0)[source]#
Calculate WMO tropopause properties for a single vertical profile.
This function processes a single atmospheric vertical profile to find the tropopause following the WMO definition (lapse rate < 2 K/km). Optimized for profile data analysis with isobaric level data.
- Parameters:
temperature (array_like) – Atmospheric temperature profile [K] on isobaric levels. 1D array.
pressure (array_like) – Atmospheric pressure profile [hPa or Pa]. Must have same length as temperature. CRITICAL: Pressure levels must be sorted in ascending order (from low pressure/high altitude to high pressure/low altitude). This is required by the underlying WMO tropopause algorithm.
pressure_unit (str, default 'hPa') – Unit of pressure data (‘hPa’ or ‘Pa’)
lapse_criterion (float, default 2.0) – WMO lapse rate criterion [K/km] for tropopause definition
missing_value (float, default -999.0) – Value to use for missing data
- Returns:
Dictionary containing: - ‘pressure’: Tropopause pressure [hPa] (float) - ‘height’: Tropopause height [m] (float) - ‘level_index’: Tropopause level index (int, 0-based) - ‘lapse_rate’: Tropopause lapse rate [K/km] (float) - ‘success’: Success flag (bool)
- Return type:
Examples
Basic usage with isobaric profile data:
>>> import numpy as np >>> from skyborn.calc.troposphere.core import trop_wmo_profile >>> >>> # Create atmospheric profile on isobaric levels (ascending order) >>> pressure = np.array([10, 20, 50, 100, 200, 300, 500, 700, 850, 1000]) # hPa >>> temperature = np.array([190, 200, 210, 220, 230, 250, 270, 280, 285, 288]) # K >>> >>> result = trop_wmo_profile(temperature, pressure) >>> print(f"Tropopause pressure: {result['pressure']:.1f} hPa") >>> print(f"Tropopause height: {result['height']:.0f} m")
Notes
This function is optimized for isobaric data (constant pressure levels).
For model level data, first interpolate to pressure levels.
Uses dedicated Fortran routine for single profile analysis.
For gridded data, use trop_wmo() instead.
- trop_wmo(temperature, pressure=None, xdim=None, ydim=None, levdim=None, timedim=None, pressure_unit='hPa', lapse_criterion=2.0, missing_value=-999.0, keep_attrs=True, auto_sort_levels=True)[source]#
Calculate WMO tropopause properties for xarray DataArrays.
This function processes gridded atmospheric data to find the tropopause following the WMO definition (lapse rate < 2 K/km). It automatically detects coordinate dimensions and preserves all metadata.
* DESIGNED FOR ISOBARIC DATA *
- Parameters:
temperature (xarray.DataArray) – Atmospheric temperature data [K] on isobaric (constant pressure) levels. Can be a 1D profile, a 2D (level, lat) / (level, lon) cross-section, a 3D (level, lat, lon) grid or (time, level, lat) / (time, level, lon) time-varying cross-section, or a 4D (time, level, lat, lon) array. Must have level coordinate (for pressure generation) if pressure is not provided.
pressure (xarray.DataArray, optional) – Atmospheric pressure data [hPa or Pa] on isobaric levels. If None, will be automatically generated from temperature’s level coordinate. CRITICAL: Pressure levels MUST be sorted in ASCENDING order (from low pressure/high altitude to high pressure/low altitude). This is required by the underlying WMO tropopause algorithm.
xdim (int or str, optional) – Longitude dimension index/name. Auto-detected if None. Not required for 1D profiles. For (time, level, lat) cross-sections, the missing longitude axis may be left as None or passed explicitly as -1.
ydim (int or str, optional) – Latitude dimension index/name. Auto-detected if None. Not required for 1D profiles. For (time, level, lon) cross-sections, the missing latitude axis may be left as None or passed explicitly as -1.
levdim (int or str, optional) – Vertical level dimension index/name. Auto-detected if None.
timedim (int or str, optional) – Time dimension index/name. Auto-detected if None.
pressure_unit (str, default 'hPa') – Unit of pressure data (‘hPa’ or ‘Pa’)
lapse_criterion (float, default 2.0) – WMO lapse rate criterion [K/km] for tropopause definition
missing_value (float, default -999.0) – Value to use for missing data
keep_attrs (bool, default True) – Preserve input DataArray attributes in output
auto_sort_levels (bool, default True) – Automatically sort pressure levels in ascending order along the level dimension
- Returns:
Dataset containing tropopause properties:
For multi-dimensional data (2D, 3D, 4D): - ‘pressure’: Tropopause pressure [hPa] with spatial/temporal coordinates - ‘height’: Tropopause height [m] with spatial/temporal coordinates - ‘level_index’: Tropopause level index (0-based) - ‘lapse_rate’: Tropopause lapse rate [K/km] - ‘success’: Success flag for each grid point
For 1D profile data: - Same variables but as scalar values (0D DataArrays)
- Return type:
Examples
1D Profile Analysis (Isobaric Data):
>>> import xarray as xr >>> import numpy as np >>> from skyborn.calc.troposphere.xarray import trop_wmo >>> >>> # Create 1D isobaric profile (ascending pressure order) >>> temp_profile = xr.DataArray( ... [210, 230, 250, 270, 280, 288], # Temperature decreasing with altitude ... dims=['level'], ... coords={'level': [100, 300, 500, 700, 850, 1000]} # hPa - ASCENDING order ... ) >>> result = trop_wmo(temp_profile) >>> print(f"Tropopause: {float(result.pressure)} hPa, {float(result.height)} m")
Simplified Interface (Auto-pressure generation from isobaric levels):
>>> # Load ERA5 pressure level data (already isobaric) >>> ds = xr.open_dataset('era5_pressure_levels.nc') # Has 'level' coordinate in hPa >>> result = trop_wmo(ds.temperature) # Pressure auto-generated from level coordinate >>> print(f"Tropopause pressure shape: {result.pressure.shape}")
2D Spatial Analysis (Isobaric Cross-sections):
>>> # Analyze latitude or longitude cross-sections on isobaric levels >>> temp_2d = ds.temperature.isel(time=0, lon=0) # (level, lat) - isobaric levels >>> result = trop_wmo(temp_2d) >>> # Result shape: (lat,)
3D Time-Varying Cross-section (Isobaric Data):
>>> temp_time_lat = ds.temperature.isel(lon=0) # (time, level, lat) >>> result = trop_wmo(temp_time_lat) >>> # Result shape: (time, lat) >>> >>> # Explicitly mark the missing horizontal axis if desired >>> result = trop_wmo( ... temp_time_lat, ... xdim=None, # xdim=-1 is also accepted in the xarray wrapper ... ydim='lat', ... levdim='level', ... timedim='time', ... )
Advanced usage with explicit isobaric pressure:
>>> # Ensure pressure levels are in ascending order >>> result = trop_wmo( ... temperature_data, # On isobaric levels ... pressure=pressure_data, # Corresponding isobaric pressure levels ... xdim='longitude', ydim='latitude', levdim='level', ... lapse_criterion=2.5 # Custom WMO criterion ... )
4D Time Series (Isobaric Data):
>>> # Multi-year isobaric climate data >>> result = trop_wmo(temperature_4d) # (time, level, lat, lon) - isobaric levels >>> # Result preserves time dimension: (time, lat, lon) >>> seasonal_mean = result.height.groupby('time.season').mean()
Notes
This function is optimized for ISOBARIC data (constant pressure levels).
For model level data, first interpolate to pressure levels before using this function.
Requires compiled Fortran extensions. Install with: pip install skyborn[fortran]
The underlying algorithm follows the WMO (1957) tropopause definition.
The function automatically: - Detects spatial and temporal coordinates using metadata - Handles missing values (NaN or masked arrays) - Preserves all coordinate information and attributes - Works with multi-dimensional isobaric data
See also
skyborn.calc.troposphere.core.trop_wmoLower-level function for numpy arrays
Growth-rate Diagnostics#
- baroc_growth_rate(u, temperature, pressure, *, lat=None, lat_bounds=None, lon=None, wavenumber_mode=DEFAULT_WAVENUMBER_MODE, method='log', tropopause_pressure=None, solver_levels=DEFAULT_SOLVER_LEVELS, smooth_window=DEFAULT_SMOOTH_WINDOW)[source]#
Compute the maximum baroclinic normal-mode growth rate.
- Parameters:
u (
xarray.DataArray,numpy.ndarray) – One-dimensional zonal-wind profile inm s^-1or a two-dimensional stack of profiles that shares one vertical axis withpressure.temperature (
xarray.DataArray,numpy.ndarray) – One-dimensional temperature profile in Kelvin or a two-dimensional stack of profiles that shares one vertical axis withpressure.pressure (
xarray.DataArray,numpy.ndarray) – One-dimensional pressure coordinate in Pa or hPa. Values must be strictly monotonic and strictly positive.lat (scalar,
xarray.DataArray,numpy.ndarray, optional) –Latitude in degrees north. This may be a scalar or, for batched profile input, a one-dimensional vector matching the profile dimension. The compiled solver uses this latitude to build the local Coriolis parameter and meridional gradient of planetary vorticity, unless
lat_boundsis provided.When
lat_boundsis given and raw NumPy inputs still contain an explicit latitude axis,latmay instead be the one-dimensional latitude coordinate used to select and cosine-weight that latitude band before the remaining profile solve.lat_bounds (tuple of float, optional) –
Two-element latitude range in degrees north. When provided, the baroclinic solver uses the Chemke-style band-mean definitions
\[\sin_{\mathrm{avg}} = \frac{\sin(\phi_1) + \sin(\phi_2)}{2}, \qquad \cos_{\mathrm{avg}} = \frac{\cos(\phi_1) + \cos(\phi_2)}{2},\]together with
\[f = 2\Omega \sin_{\mathrm{avg}}, \qquad \beta = \frac{2\Omega\cos_{\mathrm{avg}}}{a},\]to match the latitude-band formulation used in the reference Chemke scripts.
If
uandtemperaturestill carry a latitude axis, this option also reduces that axis with cosine-latitude weighting before the vertical-profile solve. For xarray inputs, the latitude coordinate is taken from thelatorlatitudedimension. For NumPy inputs with three dimensions, pass the one-dimensional latitude coordinate throughlat=...so the requested latitude band can be selected explicitly. The recommended NumPy layout is(time, level, lat). More generally, the wrapper requires exactly one axis matching the pressure-coordinate length and one non-pressure axis matching the latitude-coordinate length.lon (
xarray.DataArray,numpy.ndarray, optional) – One-dimensional longitude coordinate in degrees used only whenwavenumber_mode="low". The coordinate must be strictly monotonic. If omitted in low-resolution mode, the wrapper uses the original Chemke Python-share defaultnp.arange(0.0, 361.0, 1.5).wavenumber_mode ({"high", "low"}, optional) –
Zonal-wavenumber grid used by the compiled instability solver.
"high"uses the fixed high-resolution Chemke/MATLAB-style gridk = 0, 1\times10^{-7}, \dots, 1.99\times10^{-5}\,\mathrm{m}^{-1}."low"uses the lower-resolution Chemke Python-share formula\[k = \frac{2\pi w}{\left|\lambda_0 - \lambda_1\right|\pi a \cos\phi / 180},\]where \(w = 0, 1, \dots, \mathrm{round}(N_{\lambda}/3)-1\). When
lonis omitted, the low-resolution mode uses the original Chemke Python-share longitude gridnp.arange(0.0, 361.0, 1.5). Defaults to"high".method ({"log", "linear"}, optional) – Vertical interpolation method used to remap the input profiles onto the solver pressure grid.
"log"means linear interpolation in log-pressure, not logarithmic interpolation of the field itself. Defaults to"log". The legacy alias"logp"is still accepted.tropopause_pressure (scalar,
xarray.DataArray,numpy.ndarray, optional) – Explicit tropopause pressure in Pa or hPa. If provided, the automatic WMO tropopause diagnosis is skipped and the solver grid is built from this pressure to the lower-tropospheric bound. For batched profile input, this may be a scalar climatology or a one-dimensional vector matching the profile dimension.solver_levels (int, optional) – Number of pressure levels used when the solver grid is built automatically from the diagnosed or explicit tropopause pressure. Defaults to
DEFAULT_SOLVER_LEVELS.smooth_window (int, optional) – Optional centered running-mean window applied to the growth-rate spectrum over zonal wavenumber inside the compiled Fortran backend before the final maximum-growth diagnostic is taken.
1disables smoothing. If given, this must be a positive odd integer.
- Returns:
Maximum baroclinic growth rate in
s^-1. One-dimensional input returns a scalar float. Batched profile input returns a one-dimensional NumPy array or DataArray over the non-pressure dimension. Missing or unusable profiles returnNaN.- Return type:
float,
numpy.ndarray,xarray.DataArray
Notes
This function diagnoses the WMO tropopause pressure by default, or uses an explicit
tropopause_pressurewhen provided, and then interpolatesuandtemperatureto a fixed pressure grid between that tropopause and the lower troposphere. The thermodynamic profiles passed to the compiled kernel are\[\theta = T\left(\frac{p_0}{p}\right)^{\kappa}, \qquad \rho = \frac{p}{R_d T}, \qquad N_z = -\frac{1}{\rho\theta}\frac{\partial \theta}{\partial p}.\]The compiled solver then forms the finite-difference generalized eigenproblem
\[A(k)\psi = \lambda B(k)\psi,\]where the vertical quasi-geostrophic operator is represented in pressure coordinates by
\[A(k) \sim k\beta - k^3 U + kUf^2 \partial_p \left(N_z^{-1}\partial_p\right) - kf^2 \left(\partial_p U\right) N_z^{-1}\partial_p,\]and
\[B(k) \sim f^2 \partial_p \left(N_z^{-1}\partial_p\right) - k^2,\]and the returned value is \(\max_k \operatorname{Im}(\lambda_k)\). If
smooth_windowis greater than 1, the compiled backend first applies a centered running mean along the discrete zonal-wavenumber spectrum and then evaluates the final Chemke-style turning-point maximum on that smoothed spectrum. Whenuortemperaturecontains multiple profiles, one-dimensional climatologies are broadcast across the profile dimension in Python, while the per-profile pressure interpolation and normal-mode solve loop run in the compiled Fortran backend.The default
DEFAULT_SOLVER_LEVELS = 45is a practical resolution choice, not a formal optimum. Increasingsolver_levelscan improve vertical-grid convergence up to a point, but it also increases the per-profile eigenvalue cost and does not guarantee a better diagnostic once the solution is already converged on the chosen pressure interval.References
- Chemke, R., and Ming, Y. (2020):
Large Atmospheric Waves Will Get Stronger, While Small Waves Will Get Weaker by the End of the 21st Century. Geophysical Research Letters, 47, e2020GL090441. https://doi.org/10.1029/2020GL090441
- Chemke, R., Zanna, L., Orbe, C., Sentman, L. T., and Polvani, L. M. (2022):
The Future Intensification of the North Atlantic Winter Storm Track: The Key Role of Dynamic Ocean Coupling. Journal of Climate, 35(8), 2407-2421. https://doi.org/10.1175/JCLI-D-21-0407.1
- Chemke, R. (2022):
The future poleward shift of Southern Hemisphere summer mid-latitude storm tracks stems from ocean coupling. Nature Communications, 13, 2531. https://doi.org/10.1038/s41467-022-29392-4
- Chemke, R., Ming, Y., and Yuval, J. (2022):
The intensification of winter mid-latitude storm tracks in the Southern Hemisphere. Nature Climate Change, 12, 553-557. https://doi.org/10.1038/s41558-022-01368-8
- barot_growth_rate(u_barotropic, lat, *, radius=6_371_000.0, omega=7.292e-5)[source]#
Compute the maximum barotropic normal-mode growth rate from
U(lat).- Parameters:
u_barotropic (
xarray.DataArray,numpy.ndarray) – One-dimensional vertically averaged zonal-wind profile inm s^-1.lat (
xarray.DataArray,numpy.ndarray) – One-dimensional latitude coordinate in degrees north. Values must be strictly monotonic.radius (float, optional) – Earth radius in meters. Only the default value is currently supported by the compiled kernel.
omega (float, optional) – Planetary rotation rate in
s^-1. Only the default value is currently supported by the compiled kernel.
- Returns:
Maximum barotropic growth rate in
s^-1. Missing or unusable profiles returnNaN.- Return type:
Notes
This diagnostic solves the finite-difference generalized eigenproblem
\[A(k)\psi = \lambda B(k)\psi\]for each zonal wavenumber \(k\), where the continuous barotropic operator is
\[\left[k\beta - k^3 U + kU\partial_{yy} - kU_{yy}\right]\psi = \lambda \left(\partial_{yy} - k^2\right)\psi,\]with
\[\beta = \frac{2\Omega\cos(\phi)}{a}.\]The returned value is \(\max_k \operatorname{Im}(\lambda_k)\).
References
- Chemke, R., Ming, Y., and Yuval, J. (2022):
The intensification of winter mid-latitude storm tracks in the Southern Hemisphere. Nature Climate Change, 12, 553-557. https://doi.org/10.1038/s41558-022-01368-8
Note
skyborn.calc.baroc_growth_rate uses method="log" by
default. Here "log" means interpolation that is linear in
log-pressure, not a logarithmic transform of the field values.
The public growth-rate wrappers return s^-1 directly. Missing or
unusable profiles return NaN instead of a user-supplied
missing-value marker.
By default, the compiled solver uses the fixed high-resolution Chemke/MATLAB-style zonal-wavenumber grid
If you want the lower-resolution Chemke Python-share behavior instead,
pass wavenumber_mode="low". When lon is omitted, Skyborn uses
the original Chemke Python-share longitude grid
np.arange(0.0, 361.0, 1.5) by default; you may still pass a custom
one-dimensional longitude coordinate lon if needed. In that mode the
solver uses
with \(w = 0, 1, \dots, \mathrm{round}(N_{\lambda}/3)-1\).
When a latitude band is more appropriate than a single representative latitude, pass the band endpoints and the compiled solver uses the same Chemke-style averages as the original reference scripts:
If the input fields still carry a latitude axis, lat_bounds can also
drive the latitude-band reduction step directly. Xarray inputs such as
(time, level, lat) are selected and cosine-weighted over that band
automatically before the compiled profile solver runs. For raw NumPy
arrays with an explicit latitude axis, provide the one-dimensional
latitude coordinate through lat=... together with lat_bounds so the
requested band can be selected unambiguously. The recommended NumPy layout
is (time, level, lat). More generally, the wrapper expects exactly one
axis matching the pressure-coordinate length and one non-pressure axis
matching the supplied latitude-coordinate length.
The function diagnoses the WMO tropopause pressure by default, or uses an
explicit tropopause_pressure when given, and then builds a fixed solver
grid with
solver_levels=45 by default between that tropopause and the lower
troposphere. This is a practical resolution choice for the compiled
eigenvalue problem, not a universal optimum: increasing solver_levels
can improve vertical-grid convergence up to a point, but it also raises
the per-profile cost and is not automatically better once the solution is
converged.
In the original Chemke-style analysis scripts, a parameter named
window_size is often used for a centered running mean applied to the
growth-rate spectrum over zonal wavenumber. In Skyborn this role is
represented by smooth_window in baroc_growth_rate. That smoothing
width is not the same thing as solver_levels. Even for a single
atmospheric column, the compiled solver still sweeps many zonal
wavenumbers and solves a generalized eigenvalue problem for each one, so
the dominant cost is the repeated linear-algebra solve rather than
tropopause diagnosis or pressure interpolation. The public default is
smooth_window=1; values greater than 1 apply the centered running mean
inside the Fortran backend before the final Chemke-style maximum-growth
diagnostic is taken.
References
The current growth-rate implementation is documented against the following Chemke publications that are directly related to baroclinic wave growth and storm-track changes:
Chemke, R., and Ming, Y. (2020): Large Atmospheric Waves Will Get Stronger, While Small Waves Will Get Weaker by the End of the 21st Century. Geophysical Research Letters, 47,
e2020GL090441. https://doi.org/10.1029/2020GL090441Chemke, R., Zanna, L., Orbe, C., Sentman, L. T., and Polvani, L. M. (2022): The Future Intensification of the North Atlantic Winter Storm Track: The Key Role of Dynamic Ocean Coupling. Journal of Climate, 35(8), 2407-2421. https://doi.org/10.1175/JCLI-D-21-0407.1
Chemke, R. (2022): The future poleward shift of Southern Hemisphere summer mid-latitude storm tracks stems from ocean coupling. Nature Communications, 13, 2531. https://doi.org/10.1038/s41467-022-29392-4
Chemke, R., Ming, Y., and Yuval, J. (2022): The intensification of winter mid-latitude storm tracks in the Southern Hemisphere. Nature Climate Change, 12, 553-557. https://doi.org/10.1038/s41558-022-01368-8
Geostrophic Wind Calculations#
- geostrophic_wind(z, glon, glat, dim_order, missing_value=-999.0)[source]#
Calculate geostrophic wind components from geopotential height.
This function can handle various input shapes by using windspharm’s prep_data and recover_data utilities to reshape data for batch processing.
- Parameters:
z (ndarray) – Geopotential height data [gpm]. Can be 2D, 3D, or 4D. Must contain latitude (‘y’) and longitude (‘x’) dimensions.
glon (ndarray, shape (nlon,)) – Longitude coordinates in degrees
glat (ndarray, shape (nlat,)) – Latitude coordinates in degrees (south to north)
dim_order (str) – String specifying dimension order using: - ‘x’ for longitude - ‘y’ for latitude - ‘t’ for time - ‘z’ for level Example: ‘tzyx’ for (time, level, lat, lon)
missing_value (float, optional) – Missing value identifier (default: -999.0)
- Returns:
ug (ndarray) – Zonal geostrophic wind component [m/s] (same shape as input z)
vg (ndarray) – Meridional geostrophic wind component [m/s] (same shape as input z)
- Return type:
Examples
# 2D case: single time/level >>> z2d = np.random.randn(73, 144) # (lat, lon) >>> ug, vg = geostrophic_wind(z2d, glon, glat, ‘yx’)
# 3D case: multiple times >>> z3d = np.random.randn(73, 144, 12) # (lat, lon, time) >>> ug, vg = geostrophic_wind(z3d, glon, glat, ‘yxt’)
# 4D case: multiple levels and times >>> z4d = np.random.randn(12, 17, 73, 144) # (time, level, lat, lon) >>> ug, vg = geostrophic_wind(z4d, glon, glat, ‘tzyx’)
# Alternative 4D ordering >>> z4d_alt = np.random.randn(73, 144, 17, 12) # (lat, lon, level, time) >>> ug, vg = geostrophic_wind(z4d_alt, glon, glat, ‘yxzt’)
- class GeostrophicWind(z, glon, glat, dim_order, missing_value=-999.0)[source]#
Class-based interface for geostrophic wind calculations.
This class provides a high-level interface similar to windspharm’s VectorWind, allowing for easy calculation of various geostrophic wind quantities.
- Parameters:
Examples
>>> # Create GeostrophicWind instance (longitude cyclicity auto-detected) >>> gw = GeostrophicWind(z, glon, glat, 'tzyx') >>> >>> # Get wind components >>> ug, vg = gw.uv_components() >>> >>> # Calculate derived quantities >>> speed = gw.speed() >>> >>> # Access original data >>> z_orig = gw.geopotential_height
- geostrophic_wind(z, missing_value=-999.0, keep_attrs=True)[source]#
Calculate geostrophic wind components for xarray DataArrays.
This function processes geopotential height data to calculate geostrophic wind components (ug, vg). It automatically detects coordinate dimensions and preserves all metadata.
- Parameters:
z (xarray.DataArray) – Geopotential height data [gpm]. Can be 2D, 3D, or 4D. Must contain longitude and latitude dimensions with coordinate information.
missing_value (float, optional) – Missing value identifier (default: -999.0)
keep_attrs (bool, optional) – Preserve input DataArray attributes in output (default: True)
- Returns:
Dataset containing geostrophic wind components: - ‘ug’: Zonal geostrophic wind component [m/s] with spatial/temporal coordinates - ‘vg’: Meridional geostrophic wind component [m/s] with spatial/temporal coordinates
- Return type:
Examples
2D Geopotential Height Analysis:
>>> import xarray as xr >>> import numpy as np >>> from skyborn.calc.geostrophic.xarray import geostrophic_wind >>> >>> # Load 500 hPa geopotential height >>> z = xr.open_dataarray('z500_era5.nc') # Shape: (lat, lon) >>> result = geostrophic_wind(z) >>> print(f"Wind components: ug{result.ug.shape}, vg{result.vg.shape}")
3D Time Series Analysis:
>>> # Multi-time geopotential height data >>> z_3d = xr.open_dataarray('z500_timeseries.nc') # Shape: (time, lat, lon) >>> result = geostrophic_wind(z_3d) >>> # Result preserves time dimension: (time, lat, lon) >>> monthly_mean = result.ug.groupby('time.month').mean()
4D Multi-level Analysis:
>>> # Multi-level, multi-time data >>> z_4d = xr.open_dataarray('z_multilevel.nc') # Shape: (time, level, lat, lon) >>> result = geostrophic_wind(z_4d) >>> # Result shape: (time, level, lat, lon) >>> surface_winds = result.sel(level=1000) # 1000 hPa level
Simplified Interface (No coordinate specification needed):
>>> # Automatic coordinate detection >>> result = geostrophic_wind(z_data) # Longitude cyclicity auto-detected >>> print(f"Longitude cyclicity auto-detected: {result.attrs['longitude_cyclic']}") >>> print(f"Latitude ordering: {result.attrs['latitude_ordering']}")
Notes
Longitude cyclicity is automatically detected from coordinate spacing
Latitude ordering is automatically ensured to be south-to-north as required
Requires compiled Fortran extensions for optimal performance
All coordinate information and attributes are preserved
The function automatically: - Detects longitude and latitude coordinates using metadata - Handles missing values (NaN or masked arrays) - Preserves all coordinate information and attributes - Works with multi-dimensional data of any supported shape
See also
skyborn.calc.geostrophic.core.geostrophic_windLower-level function for numpy arrays
GeostrophicWindClass-based interface for xarray DataArrays
- class GeostrophicWind(z, missing_value=-999.0, keep_attrs=True)[source]#
Class-based geostrophic wind analysis using xarray DataArrays.
This class provides a high-level interface for geostrophic wind calculations that preserves xarray coordinate information and metadata. It wraps the standard interface implementation while maintaining CF-compliant attributes.
- Parameters:
z (xarray.DataArray) – Geopotential height data [gpm]. Must contain longitude and latitude dimensions with appropriate coordinate information.
missing_value (float, optional) – Missing value identifier (default: -999.0)
keep_attrs (bool, optional) – Preserve input DataArray attributes in output (default: True)
- _z_original#
Original geopotential height data
- Type:
- _result#
Computed geostrophic wind components
- Type:
- _glon#
Longitude coordinates
- Type:
np.ndarray
- _glat#
Latitude coordinates
- Type:
np.ndarray
Examples
>>> import xarray as xr >>> from skyborn.calc.geostrophic.xarray import GeostrophicWind >>> >>> # Load geopotential height >>> z = xr.open_dataarray('z500.nc') >>> >>> # Create GeostrophicWind instance >>> gw = GeostrophicWind(z) >>> >>> # Get wind components with preserved metadata >>> ug, vg = gw.uv_components() >>> print(ug.attrs) # CF-compliant attributes >>> >>> # Calculate derived quantities >>> speed = gw.speed() >>> print(f"Max wind speed: {float(speed.max()):.1f} m/s") >>> >>> # Access original data >>> z_orig = gw.geopotential_height
- __init__(z, missing_value=-999.0, keep_attrs=True)[source]#
Initialize GeostrophicWind with xarray DataArray.
Genesis Potential Index (GPI) / Tropical Cyclone Potential Intensity#
- potential_intensity(sst, psl, pressure_levels, temperature, mixing_ratio, actual_levels=None)[source]#
Calculate tropical cyclone potential intensity with automatic dimension detection.
This is the primary NumPy entry point. It automatically detects whether the thermodynamic input describes a single profile, a 3D gridded field, or a 4D time series and dispatches to the compiled backend directly.
- Parameters:
sst (float or ndarray) – Sea surface temperature [K]
psl (float or ndarray) – Sea level pressure [Pa]
pressure_levels (ndarray) – Atmospheric pressure levels [mb]
temperature (ndarray) – Temperature data [K]. Supported shapes are
(level,),(level, y, x), and(time, level, y, x).mixing_ratio (ndarray) – Water vapor mixing ratio [kg/kg]
actual_levels (int, optional) – Number of valid vertical levels to use for a 1D profile input. Ignored for gridded 3D and 4D inputs.
- Returns:
min_pressure (float or ndarray) – Minimum central pressure [mb]
max_wind (float or ndarray) – Maximum sustained wind speed [m/s]
error_flag (int) – Error status (1 = success, other values indicate non-convergence or invalid input)
- Return type:
- log_decompose_pi(pi, sst, t0, CKCD=0.9, *, sst_units='K')[source]#
Log-decompose potential intensity into efficiency, disequilibrium, and Ck/Cd.
- Parameters:
- Returns:
(lnpi, lneff, lndiseq, lnCKCD)wherelnpi = ln(V^2).- Return type:
- pi_log_decomposition(sst, psl, pressure_levels, temperature, mixing_ratio, CKCD=0.9, *, outflow_source='cape_star')[source]#
Calculate PI plus the Wing et al. (2015) logarithmic decomposition.
- Parameters:
sst (Any) – Sea surface temperature [K].
psl (Any) – Sea level pressure [Pa].
pressure_levels (ndarray) – Atmospheric pressure levels [mb].
temperature (ndarray) – Thermodynamic input field. Supported shapes are
(level,),(level, y, x), and(time, level, y, x).mixing_ratio (ndarray) – Water vapor mixing ratio [kg/kg] with the same shape as
temperature.CKCD (float, default: 0.9) – Ratio of exchange coefficients.
outflow_source ({"cape_star", "cape_env"}, default: "cape_star") – Outflow branch used for the backend diagnostics.
- Returns:
max_wind,min_pressure,error_flag,t0,otl,lnpi,lneff,lndiseq, andlnCKCD.- Return type:
- potential_intensity(sst, psl, pressure_levels, temperature, mixing_ratio)[source]#
Calculate tropical cyclone potential intensity with automatic dimension detection.
This function automatically detects the input data dimensions and calls the appropriate specific function (profile, 3D, or 4D).
- Parameters:
sst (xr.DataArray) – Sea surface temperature [K]
psl (xr.DataArray) – Sea level pressure [Pa]
pressure_levels (xr.DataArray) – Atmospheric pressure levels [mb]
temperature (xr.DataArray) – Temperature data [K]
mixing_ratio (xr.DataArray) – Water vapor mixing ratio [kg/kg]
- Returns:
result – Dataset containing potential intensity results with appropriate dimensions.
- Return type:
xr.Dataset
Notes
The function determines the calculation type based on temperature array dimensions: - 1D (vertical only): Single profile calculation - 3D (vertical + 2 spatial): Gridded data calculation - 4D (time + vertical + 2 spatial): Time series calculation
- pi_log_decomposition(sst, psl, pressure_levels, temperature, mixing_ratio, CKCD=0.9, *, outflow_source='cape_star')[source]#
Return PI plus outflow and logarithmic decomposition diagnostics.
Note
error_flag == 1 indicates a successful PI solve.
potential_intensity(...) remains the pure PI entry point.
Use pi_log_decomposition(...) for supported 1D, 3D, and 4D inputs
when you need tcpyPI-style outflow diagnostics
(outflow_source={"cape_star", "cape_env"}, t0, otl) and the
Wing et al. (2015) logarithmic decomposition where lnpi = ln(V^2).
Statistical Functions#
- linear_regression(data, predictor)[source]#
Perform linear regression between a 3D data array and a predictor sequence. Handles both numpy arrays and xarray DataArrays with NaN handling.
- Parameters:
data (np.ndarray or xr.DataArray) – A 3D array of shape (n_samples, dim1, dim2) containing dependent variables. Missing values should be represented as NaN.
predictor (np.ndarray or xr.DataArray) – A 1D array of shape (n_samples,) containing the independent variable. Missing values should be represented as NaN.
- Returns:
A tuple containing: - regression_coefficients: The slope of the regression line with shape (dim1, dim2) - p_values: The p-values of the regression with shape (dim1, dim2)
- Return type:
Tuple[np.ndarray, np.ndarray]
- Raises:
ValueError – If the number of samples in data doesn’t match the length of the predictor.
- spatial_correlation(data, predictor)[source]#
Perform fast spatial correlation between a 3D data array and a predictor time series. Optimized for vectorized operations to avoid slow loops over lat/lon grid points.
- Parameters:
data (np.ndarray or xr.DataArray) – A 3D array of shape (time, lat, lon) containing spatial data. Missing values should be represented as NaN.
predictor (np.ndarray or xr.DataArray) – A 1D array of shape (time,) containing the predictor time series. Missing values should be represented as NaN.
- Returns:
A tuple containing: - correlation_coefficients: Pearson correlation coefficients with shape (lat, lon) - p_values: The p-values of the correlations with shape (lat, lon)
- Return type:
Tuple[np.ndarray, np.ndarray]
- Raises:
ValueError – If the time dimension of data doesn’t match the length of the predictor.
- pearson_correlation(x, y)[source]#
Calculate Pearson correlation coefficient.
- Parameters:
x (array-like) – Input data arrays.
y (array-like) – Input data arrays.
- Returns:
Pearson correlation coefficient.
- Return type:
Emergent Constraint Functions#
- gaussian_pdf(mu, sigma, x)[source]#
Calculate Gaussian probability density function.
- Parameters:
- Returns:
Probability density function values.
- Return type:
Union[np.ndarray, float]
References
Adapted from: blackcata/Emergent_Constraints
- emergent_constraint_posterior(constraint_data, target_data, constraint_grid, target_grid, obs_pdf)[source]#
Calculate posterior PDF using emergent constraint method.
This function applies the emergent constraint method to reduce uncertainty in climate projections by utilizing observational constraints.
- Parameters:
constraint_data (xr.DataArray) – Inter-model spread data for the constraint variable (e.g., model sensitivity).
target_data (xr.DataArray) – Inter-model spread data for the target variable (e.g., future projection).
constraint_grid (np.ndarray) – Grid values for the constraint variable.
target_grid (np.ndarray) – Grid values for the target variable.
obs_pdf (np.ndarray) – Observational PDF of the constraint variable.
- Returns:
A tuple containing: - posterior_pdf : np.ndarray - Posterior PDF of the target variable - posterior_std : float - Standard deviation of the target variable - posterior_mean : float - Mean of the target variable
- Return type:
References
Adapted from: blackcata/Emergent_Constraints Cox, P. M., et al. (2013). Nature, 494(7437), 341-344.
- emergent_constraint_prior(constraint_data, target_data, constraint_grid, target_grid)[source]#
Calculate prior probability distribution for emergent constraints.
- Parameters:
constraint_data (xr.DataArray) – Inter-model spread data for the constraint variable.
target_data (xr.DataArray) – Inter-model spread data for the target variable.
constraint_grid (np.ndarray) – Grid values for the constraint variable.
target_grid (np.ndarray) – Grid values for the target variable.
- Returns:
A tuple containing: - prior_pdf : np.ndarray - Prior PDF - prediction_error : np.ndarray - Prediction error array - regression_line : np.ndarray - Linear regression values
- Return type:
Tuple[np.ndarray, np.ndarray, np.ndarray]
References
Adapted from: blackcata/Emergent_Constraints
Legacy Functions (for backward compatibility)#
- calc_PDF_EC(tmp_x, tmp_y, x, y, PDF_x)[source]#
Legacy function name. Use emergent_constraint_posterior() instead.
Utility Functions#
- calculate_potential_temperature(temperature, pressure, reference_pressure=1000.0)[source]#
Calculate potential temperature using fast numpy operations.
This implementation uses lazy imports and avoids heavy metpy dependencies for simple potential temperature calculations.
- Parameters:
temperature (array-like) – Temperature values in Kelvin.
pressure (array-like) – Pressure values in hPa.
reference_pressure (float, optional) – Reference pressure in hPa, default is 1000.0.
- Returns:
Potential temperature values in Kelvin.
- Return type:
array-like
Notes
Uses the standard formula: theta = T * (P0/P)^(R/cp) where R/cp = 0.286 for dry air
- convert_longitude_range(data, lon='lon', center_on_180=True)[source]#
Wrap longitude coordinates of DataArray or Dataset to either -180..179 or 0..359.
- Parameters:
data (xr.DataArray or xr.Dataset) – An xarray DataArray or Dataset object containing longitude coordinates.
lon (str, optional) – The name of the longitude coordinate, default is ‘lon’.
center_on_180 (bool, optional) – If True, wrap longitude from 0..359 to -180..179; If False, wrap longitude from -180..179 to 0..359.
- Returns:
The DataArray or Dataset with wrapped longitude coordinates.
- Return type:
xr.DataArray or xr.Dataset
Example Usage#
Tropopause Calculation Examples
import skyborn as skb
import numpy as np
import xarray as xr
# === 1D Profile Analysis ===
# Single atmospheric profile
pressure = np.array([50, 100, 200, 300, 500, 700, 850, 1000]) # hPa
temperature = np.array([200, 210, 220, 230, 250, 270, 280, 288]) # K
result = skb.calc.trop_wmo_profile(temperature, pressure)
print(f"Tropopause: {result['pressure']:.1f} hPa at {result['height']:.0f} m")
# === xarray Interface - Simplified ===
# Load atmospheric data with level coordinate
ds = xr.open_dataset('era5_data.nc') # Temperature with level coordinate in hPa
# Auto-generate pressure from level coordinate - no pressure array needed!
result = skb.calc.troposphere.xarray.trop_wmo(ds.temperature)
print(f"Global tropopause calculated for {result.pressure.shape}")
# === 2D Cross-section Analysis ===
# Meridional cross-section (level, lat)
temp_meridional = ds.temperature.isel(time=0, lon=0) # (level, lat)
result_2d = skb.calc.troposphere.xarray.trop_wmo(temp_meridional)
# Result shape: (lat,) - tropopause height at each latitude
# === 4D Climate Analysis ===
# Multi-year dataset (time, level, lat, lon)
result_4d = skb.calc.troposphere.xarray.trop_wmo(ds.temperature)
# Result shape: (time, lat, lon) - preserves time and spatial dimensions
# Seasonal analysis
seasonal_mean = result_4d.height.groupby('time.season').mean()
# === Advanced Usage ===
# Custom pressure field and WMO criterion
result = skb.calc.troposphere.xarray.trop_wmo(
temperature=ds.temperature,
pressure=ds.pressure, # Custom pressure field
lapse_criterion=2.5, # Custom WMO threshold (K/km)
pressure_unit='Pa' # If pressure is in Pascals
)
Geostrophic Wind Examples
import skyborn as skb
import numpy as np
import xarray as xr
# === NumPy Interface ===
# Traditional interface with manual parameter specification
nlat, nlon = 73, 144
z_data = np.random.randn(nlat, nlon) * 100 + 5500 # Geopotential height [gpm]
lat = np.linspace(-90, 90, nlat) # Degrees north
lon = np.linspace(0, 360, nlon)[:-1] # Degrees east (0-357.5)
# Calculate geostrophic wind components
ug, vg = skb.calc.geostrophic_wind(z_data, lon, lat, 'yx')
print(f"Wind components: ug{ug.shape}, vg{vg.shape}")
# Class interface for derived quantities
gw = skb.calc.GeostrophicWind(z_data, lon, lat, 'yx')
speed = gw.speed()
print(f"Max wind speed: {speed.max():.1f} m/s")
# === xarray Interface - Simplified ===
# Load geopotential height data
z = xr.DataArray(
z_data,
dims=['lat', 'lon'],
coords={
'lat': (['lat'], lat, {'units': 'degrees_north'}),
'lon': (['lon'], lon, {'units': 'degrees_east'})
},
attrs={'long_name': '500 hPa geopotential height', 'units': 'gpm'}
)
# Automatic coordinate detection and parameter inference
from skyborn.calc.geostrophic.xarray import geostrophic_wind
result = geostrophic_wind(z) # That's it!
print(f"Automatic features:")
print(f" Longitude cyclic: {result.attrs['longitude_cyclic']}")
print(f" Latitude ordering: {result.attrs['latitude_ordering']}")
print(f" Output: ug{result.ug.shape}, vg{result.vg.shape}")
# === Multi-dimensional Examples ===
# 3D time series (time, lat, lon)
nt = 12
z_3d_data = np.random.randn(nt, nlat, nlon) * 100 + 5500
z_3d = xr.DataArray(
z_3d_data,
dims=['time', 'lat', 'lon'],
coords={
'time': pd.date_range('2023-01-01', periods=nt, freq='MS'),
'lat': lat, 'lon': lon
}
)
result_3d = geostrophic_wind(z_3d)
print(f"3D result: ug{result_3d.ug.shape}, vg{result_3d.vg.shape}")
# Seasonal analysis
seasonal_winds = result_3d.ug.groupby('time.season').mean()
# 4D multi-level data (time, level, lat, lon)
nz = 17
levels = [50, 100, 150, 200, 250, 300, 400, 500, 600, 700, 775, 850, 925, 1000]
z_4d_data = np.random.randn(nt, len(levels), nlat, nlon) * 100 + 5500
z_4d = xr.DataArray(
z_4d_data,
dims=['time', 'level', 'lat', 'lon'],
coords={
'time': pd.date_range('2023-01-01', periods=nt, freq='MS'),
'level': (['level'], levels, {'units': 'hPa'}),
'lat': lat, 'lon': lon
}
)
result_4d = geostrophic_wind(z_4d)
print(f"4D result: ug{result_4d.ug.shape}, vg{result_4d.vg.shape}")
# Class interface with xarray
from skyborn.calc.geostrophic.xarray import GeostrophicWind
gw_xr = GeostrophicWind(z)
speed_xr = gw_xr.speed()
print(f"xarray class speed: {speed_xr.attrs['standard_name']}")
Tropical Cyclone Potential Intensity (GPI) Examples
import skyborn as skb
import numpy as np
import xarray as xr
# === Single Profile Calculation ===
# Atmospheric profile data
pressure_levels = np.array([1000, 850, 700, 500, 300, 200, 100]) # mb
temperature = np.array([300, 290, 280, 270, 250, 230, 210]) # K
mixing_ratio = np.array([0.015, 0.010, 0.005, 0.002, 0.0005, 0.0001, 0.00005]) # kg/kg
# Surface conditions
sst = 302.0 # K (29°C - warm tropical ocean)
psl = 101325.0 # Pa (sea level pressure)
# NumPy interface
from skyborn.calc.GPI.core import potential_intensity
min_p, pi, err = potential_intensity(
sst, psl, pressure_levels, temperature, mixing_ratio
)
print(f"Potential Intensity: {pi:.1f} m/s, Min Pressure: {min_p:.1f} mb")
print(f"Error flag: {err} (1 = success)")
# === xarray Interface with Automatic Unit Conversion ===
from skyborn.calc.GPI.xarray import potential_intensity
# Create xarray data with various units (will be auto-converted)
temp_xr = xr.DataArray(
temperature - 273.15, # Convert to Celsius for demo
dims=['level'],
attrs={'units': '°C'} # Will be auto-converted to K
)
mixr_xr = xr.DataArray(
mixing_ratio * 1000, # Convert to g/kg for demo
dims=['level'],
attrs={'units': 'g/kg'} # Will be auto-converted to kg/kg
)
pres_xr = xr.DataArray(
pressure_levels,
dims=['level'],
attrs={'units': 'mb'}
)
# Automatic unit conversion and dimension detection
result = potential_intensity(sst, psl, pres_xr, temp_xr, mixr_xr)
print(f"xarray PI: {result.pi.values:.1f} m/s")
print(f"Minimum pressure: {result.min_pressure.values:.1f} mb")
# === 3D Gridded Data (Spatial Analysis) ===
nlat, nlon = 20, 30
nlevs = len(pressure_levels)
# Create 3D atmospheric data
temp_3d = xr.DataArray(
np.random.randn(nlevs, nlat, nlon) * 5 + 280,
dims=['level', 'lat', 'lon'],
coords={
'level': pressure_levels,
'lat': np.linspace(-30, 30, nlat),
'lon': np.linspace(0, 360, nlon, endpoint=False)
},
attrs={'units': 'K', 'long_name': 'Temperature'}
)
mixr_3d = xr.DataArray(
np.random.rand(nlevs, nlat, nlon) * 0.01 + 0.005,
dims=['level', 'lat', 'lon'],
coords=temp_3d.coords,
attrs={'units': 'kg/kg', 'long_name': 'Water vapor mixing ratio'}
)
sst_3d = xr.DataArray(
np.random.randn(nlat, nlon) * 2 + 300,
dims=['lat', 'lon'],
coords={'lat': temp_3d.lat, 'lon': temp_3d.lon},
attrs={'units': 'K', 'long_name': 'Sea surface temperature'}
)
psl_3d = xr.DataArray(
np.random.randn(nlat, nlon) * 500 + 101325,
dims=['lat', 'lon'],
coords={'lat': temp_3d.lat, 'lon': temp_3d.lon},
attrs={'units': 'Pa', 'long_name': 'Sea level pressure'}
)
# Calculate potential intensity for entire grid
result_3d = potential_intensity(sst_3d, psl_3d, pres_xr, temp_3d, mixr_3d)
print(f"3D PI shape: {result_3d.pi.shape}")
print(f"Max PI: {result_3d.pi.max().values:.1f} m/s")
print(f"Min central pressure: {result_3d.min_pressure.min().values:.1f} mb")
# Analyze tropical regions only
tropical_mask = np.abs(result_3d.lat) <= 25
tropical_pi = result_3d.pi.where(tropical_mask)
print(f"Mean tropical PI: {tropical_pi.mean().values:.1f} m/s")
# === 4D Time Series Analysis ===
ntimes = 12 # Monthly data
# Create 4D data (time, level, lat, lon)
temp_4d = xr.DataArray(
np.random.randn(ntimes, nlevs, nlat, nlon) * 5 + 280,
dims=['time', 'level', 'lat', 'lon'],
coords={
'time': pd.date_range('2023-01', periods=ntimes, freq='MS'),
'level': pressure_levels,
'lat': np.linspace(-30, 30, nlat),
'lon': np.linspace(0, 360, nlon, endpoint=False)
},
attrs={'units': 'K'}
)
mixr_4d = xr.DataArray(
np.random.rand(ntimes, nlevs, nlat, nlon) * 0.01 + 0.005,
dims=['time', 'level', 'lat', 'lon'],
coords=temp_4d.coords,
attrs={'units': 'kg/kg'}
)
sst_4d = xr.DataArray(
np.random.randn(ntimes, nlat, nlon) * 2 + 300,
dims=['time', 'lat', 'lon'],
coords={'time': temp_4d.time, 'lat': temp_4d.lat, 'lon': temp_4d.lon},
attrs={'units': 'K'}
)
psl_4d = xr.DataArray(
np.random.randn(ntimes, nlat, nlon) * 500 + 101325,
dims=['time', 'lat', 'lon'],
coords={'time': temp_4d.time, 'lat': temp_4d.lat, 'lon': temp_4d.lon},
attrs={'units': 'Pa'}
)
# Calculate monthly potential intensity
result_4d = potential_intensity(sst_4d, psl_4d, pres_xr, temp_4d, mixr_4d)
print(f"4D result shape: {result_4d.pi.shape}") # (time, lat, lon)
# Seasonal analysis
seasonal_pi = result_4d.pi.groupby('time.season').mean()
print(f"Summer mean PI: {seasonal_pi.sel(season='JJA').mean().values:.1f} m/s")
print(f"Winter mean PI: {seasonal_pi.sel(season='DJF').mean().values:.1f} m/s")
# === PI log decomposition diagnostics ===
sst_profile = xr.DataArray(sst, attrs={'units': 'K'})
psl_profile = xr.DataArray(psl, attrs={'units': 'Pa'})
temp_profile_xr = xr.DataArray(temperature, dims=['level'], coords={'level': pressure_levels}, attrs={'units': 'K'})
mixr_profile_xr = xr.DataArray(mixing_ratio, dims=['level'], coords={'level': pressure_levels}, attrs={'units': 'kg/kg'})
pres_profile_xr = xr.DataArray(pressure_levels, dims=['level'], attrs={'units': 'mb'})
from skyborn.calc.GPI.xarray import pi_log_decomposition
profile_full = pi_log_decomposition(
sst_profile,
psl_profile,
pres_profile_xr,
temp_profile_xr,
mixr_profile_xr,
outflow_source='cape_env'
)
print(profile_full[['pi', 't0', 'otl', 'lnpi', 'lneff', 'lndiseq', 'lnCKCD']])
Statistical Analysis Examples
# Statistical analysis
x = np.random.randn(100)
y = 2 * x + np.random.randn(100) * 0.5
slope, p_value = skb.linear_regression(x, y)
print(f"Linear regression: slope={slope:.4f}, p_value={p_value:.6f}")
correlation = skb.pearson_correlation(x, y)
print(f"Pearson correlation: {correlation:.4f}")
# Spatial correlation analysis
# Create sample spatial data (time, lat, lon)
n_time, n_lat, n_lon = 120, 36, 72
spatial_data = np.random.randn(n_time, n_lat, n_lon)
time_series = np.random.randn(n_time)
# Calculate spatial correlations efficiently
corr_map, p_values = skb.spatial_correlation(spatial_data, time_series)
# Works with xarray too
data_xr = xr.DataArray(spatial_data, dims=['time', 'lat', 'lon'])
predictor_xr = xr.DataArray(time_series, dims=['time'])
corr_xr, p_xr = skb.spatial_correlation(data_xr, predictor_xr)
# Emergent constraint analysis
x_values = np.linspace(-3, 3, 100)
pdf = skb.gaussian_pdf(mu=0, sigma=1, x=x_values)
# Apply emergent constraint
posterior_mean, posterior_std = skb.emergent_constraint_posterior(
prior_mean=3.0, prior_std=1.5,
obs_mean=0.5, obs_std=0.2,
relationship_slope=2.0, relationship_intercept=0.1
)
Emergent Constraints#
The emergent constraint module implements methods for reducing uncertainty in climate projections by leveraging relationships between observable present-day quantities and uncertain future projections.
For a complete interactive example, see Emergent Constraints Analysis.