From e7defd7e398e5836bde1a015ed52c9c2e703ff42 Mon Sep 17 00:00:00 2001 From: Shenyulu <59901836+shenyulu@users.noreply.github.com> Date: Fri, 8 Nov 2024 21:19:11 +0800 Subject: [PATCH] fix: rename file in the `field` --- README.md | 2 +- docs/source/api_index/index.rst | 9 + src/easyclimate/field/__init__.py | 1 + src/easyclimate/field/mesoscale/__init__.py | 1 + .../field/mesoscale/potential_intensity.py | 315 ++++++++++++++++++ ...st_field_air_sea_interaction_index_amm.py} | 0 ...ir_sea_interaction_index_atlantic_nino.py} | 0 ...t_field_air_sea_interaction_index_enso.py} | 0 ...t_field_air_sea_interaction_index_iobm.py} | 0 ...st_field_air_sea_interaction_index_iod.py} | 0 ...est_field_mesoscale_potential_intensity.py | 228 +++++++++++++ 11 files changed, 555 insertions(+), 1 deletion(-) create mode 100644 src/easyclimate/field/mesoscale/__init__.py create mode 100644 src/easyclimate/field/mesoscale/potential_intensity.py rename test/{test_air_sea_interaction_index_amm.py => test_field_air_sea_interaction_index_amm.py} (100%) rename test/{test_air_sea_interaction_index_atlantic_nino.py => test_field_air_sea_interaction_index_atlantic_nino.py} (100%) rename test/{test_air_sea_interaction_index_enso.py => test_field_air_sea_interaction_index_enso.py} (100%) rename test/{test_air_sea_interaction_index_iobm.py => test_field_air_sea_interaction_index_iobm.py} (100%) rename test/{test_air_sea_interaction_index_iod.py => test_field_air_sea_interaction_index_iod.py} (100%) create mode 100644 test/test_field_mesoscale_potential_intensity.py diff --git a/README.md b/README.md index 89968f66..65e08cbc 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -easyclimate +easyclimate

A line of code to analyze climate

diff --git a/docs/source/api_index/index.rst b/docs/source/api_index/index.rst index 729491c3..f9370a8d 100644 --- a/docs/source/api_index/index.rst +++ b/docs/source/api_index/index.rst @@ -127,3 +127,12 @@ Ocean easyclimate.field.ocean.oceanic_front easyclimate.field.ocean.stability easyclimate.field.ocean.thermal + +Mesoscale +:::::::::::::::::::::::::::::::::::::::: + +.. autosummary:: + :toctree: generated/ + + easyclimate.field.mesoscale + easyclimate.field.mesoscale.potential_intensity diff --git a/src/easyclimate/field/__init__.py b/src/easyclimate/field/__init__.py index b6f5ab27..57f741b7 100644 --- a/src/easyclimate/field/__init__.py +++ b/src/easyclimate/field/__init__.py @@ -3,3 +3,4 @@ from . import land from . import monsoon from . import ocean +from . import mesoscale diff --git a/src/easyclimate/field/mesoscale/__init__.py b/src/easyclimate/field/mesoscale/__init__.py new file mode 100644 index 00000000..b92b3c0f --- /dev/null +++ b/src/easyclimate/field/mesoscale/__init__.py @@ -0,0 +1 @@ +from .potential_intensity import * diff --git a/src/easyclimate/field/mesoscale/potential_intensity.py b/src/easyclimate/field/mesoscale/potential_intensity.py new file mode 100644 index 00000000..972f06b4 --- /dev/null +++ b/src/easyclimate/field/mesoscale/potential_intensity.py @@ -0,0 +1,315 @@ +""" +Potential intensity of TC +""" + +import xarray as xr +from ...core.utility import ( + transfer_data_difference_units, + transfer_data_multiple_units, +) +from ...core.diagnosis import transfer_specific_humidity_2_mixing_ratio +from typing import Literal + +from tcpyPI import pi +from tcpyPI.utilities import pi_efficiency, pi_diseq_resid, decompose_pi + +__all__ = [ + "calc_potential_intensity_Bister_Emanuel_2002", +] + + +def calc_potential_intensity_Bister_Emanuel_2002( + sst_data: xr.DataArray, + sst_data_units: Literal["celsius", "kelvin", "fahrenheit"], + surface_pressure_data: xr.DataArray, + surface_pressure_data_units: Literal["hPa", "Pa", "mbar"], + temperature_data: xr.DataArray, + temperature_data_units: Literal["celsius", "kelvin", "fahrenheit"], + specific_humidity_data: xr.DataArray, + specific_humidity_data_units: str, + vertical_dim: str, + vertical_dim_units: str, + CKCD: float = 0.9, + ascent_flag: bool = False, + diss_flag: bool = True, + V_reduc: float = 0.8, + ptop: float = 50, + miss_handle: bool = True, +) -> xr.Dataset: + """ + Calculate potential intensity of TC (tropical cyclone) according to the Bister and Emanuel (2002) algorithm. + + This function calculates the maximum wind speed and mimimum central pressure achievable + in tropical cyclones, given a sounding and a sea surface temperature. + + From Bister and Emanuel (1998) EQN. 21, PI may be computed directly via: + + .. math:: + + V_{max}^{2} = \\frac{C_k}{C_D}(\\frac{T_{s} - T_{0}}{T_{0}})(h_0^* - h^*), + + where :math:`C_k` and :math:`C_D` are the enthalpy and momentum surface exchange coefficients, + respectively; :math:`T_{s}` is the sea surface temperature; :math:`T_{0}` is the mean outflow temperature; + :math:`h_0^*` is the saturation moist static energy at the sea surface; + and :math:`h^*` is the moist static energy of the free troposphere. + The ratio :math:`\\frac{C_k}{C_D}` is an uncertain quantity typically taken + to be a constant (default is 0.9, see Emanuel 2003 and references therein). + + Building on this definition, one can extract TC efficiency + and disequilibrium, and decompose the terms to determine their relative contributions to potential intensity. + + The efficiency of TC PI is the Carnot efficiency. Typical values range between 50-70% in the tropics. + + Each term in the PI equation may decomposed by taking the natural logarithm of both sides, arriving at (Wing et al. 2015; EQN. 2): + + .. math:: + 2*\\log(V_{max}) = \\log(\\frac{C_k}{C_D}) + \\log(\\frac{T_{s} - T_{0}}{T_{0}}) + \\log(h_0^* - h^*). + + Note that the units of everything input to the functions (and particularly the temperatures) must match. + + Parameters + ---------- + sst_data: :py:class:`xarray.DataArray`. + The sea surface temperature data. + sst_data_units: :py:class:`str `. + The unit corresponding to `sst_data` value. Optional values are `celsius`, `kelvin`, `fahrenheit`. + surface_pressure_data: :py:class:`xarray.DataArray`. + Mean surface sea level pressure. + surface_pressure_data_units: :py:class:`str `. + The unit corresponding to `surface_pressure_data` value. Optional values are `hPa`, `Pa`, `mbar`. + temperature_data: :py:class:`xarray.DataArray`. + Atmospheric temperature. + temperature_data_units: :py:class:`str `. + The unit corresponding to `temperature_data` value. Optional values are `celsius`, `kelvin`, `fahrenheit`. + specific_humidity_data: :py:class:`xarray.DataArray`. + The Specific humidity of air. + specific_humidity_data_units: :py:class:`str `. + The unit corresponding to `specific_humidity` value. Optional values are `kg/kg`, `g/g`, `g/kg` and so on. + vertical_dim: :py:class:`str `. + Vertical coordinate dimension name. + vertical_dim_units: :py:class:`str `. + The unit corresponding to the vertical p-coordinate value. Optional values are `hPa`, `Pa`, `mbar`. + CKCD: :py:class:`float `, default 0.9. + Ratio of :math:`C_k` to :math:`C_D` (unitless number), i.e. the ratio of the exchange coefficients of enthalpy and + momentum flux (e.g. see Bister and Emanuel 1998, EQN. 17-18). More discussion on :math:`\\frac{C_k}{C_D}` is found in Emanuel (2003). + Default is 0.9 based on e.g. Wing et al. (2015). + ascent_flag: :py:class:`bool `, default False. + Adjustable constant fraction (unitless fraction) for buoyancy of displaced parcels, + where `True` is Reversible ascent (default) and `False` is Pseudo-adiabatic ascent. + V_reduc: :py:class:`float `, default 0.8. + Adjustable constant fraction (unitless fraction) for reduction of gradient winds to 10-m winds + see Emanuel (2000) and Powell (1980). + ptop: :py:class:`float `, default 50 **hPa**. + Pressure below which sounding is ignored (**hPa**). + miss_handle: :py:class:`bool `, default True. + Flag that determines how missing (NaN) values are handled in CAPE calculation. + - If `False` (BE02 default), NaN values in profile are ignored and PI is still calcuated. + - If `True`, given NaN values PI will be set to missing (with `IFLAG=3` in CAPE calculation). + + .. note:: + If any missing values are between the lowest valid level and ptop + then PI will automatically be set to missing (with `IFLAG=3` in CAPE calculation) + + Returns + ------- + - vmax: The maximum surface wind speed (m/s) reduced to reflect surface drag via :math:`V_{\\text{reduc}}`. + - pmin: The minimum central pressure (hPa) + - ifl: A flag value: A value of 1 means OK; a value of 0 indicates no convergence; a value of 2 + means that the CAPE routine failed to converge; a value of 3 means the CAPE routine failed due to + missing data in the inputs. + - t0: The outflow temperature (K) + - otl: The outflow temperature level (hPa), defined as the level of neutral bouyancy + where the outflow temperature is found, i.e. where buoyancy is actually equal + to zero under the condition of an air parcel that is saturated at sea level pressure. + - eff: Tropical cyclone efficiency. + - diseq: Thermodynamic disequilibrium. + - lnpi: Natural :math:`\\log(\\text{Potential Intensity})` + - lneff: Natural :math:`\\log(\\text{Tropical Cyclone Efficiency})` + - lndiseq: Natural :math:`\\log(\\text{Thermodynamic Disequilibrium})` + - lnCKCD: Natural :math:`\\log(C_k/C_D)` + + Reference + -------------- + - https://github.com/dgilford/tcpyPI + + - Bister, M., Emanuel, K.A. Dissipative heating and hurricane intensity. Meteorl. Atmos. Phys. 65, 233–240 (1998). https://doi.org/10.1007/BF01030791 + - Bister, M., and K. A. Emanuel, Low frequency variability of tropical cyclone potential intensity, 1, Interannual to interdecadal variability, J. Geophys. Res., 107(D24), 4801, https://doi.org/10.1029/2001JD000776, 2002. + - Emanuel, K.: A Statistical Analysis of Tropical Cyclone Intensity, Mon. Weather Rev., 128, 1139–1152, https://doi.org/10.1175/1520-0493(2000)128<1139:ASAOTC>2.0.CO;2, 2000. + - Emanuel, K.: Tropical Cyclones, Annu. Rev. Earth Pl. Sc., 31, 75–104, https://doi.org/10.1146/annurev.earth.31.100901.141259, 2003. + - Gilford, D. M.: pyPI (v1.3): Tropical Cyclone Potential Intensity Calculations in Python, Geosci. Model Dev., 14, 2351–2369, https://doi.org/10.5194/gmd-14-2351-2021, 2021. + - Powell, M. D.: Evaluations of Diagnostic Marine Boundary-Layer Models Applied to Hurricanes, Mon. Weather Rev., 108, 757–766, https://doi.org/10.1175/1520-0493(1980)108<0757:EODMBL>2.0.CO;2, 1980. + - Wing, A. A., Emanuel, K., and Solomon, S.: On the factors affecting trends and variability in tropical cyclone potential intensity, Geophys. Res. Lett., 42, 8669–8677, https://doi.org/10.1002/2015GL066145, 2015. + """ + # Transfer bool value to int value + ascent_flag = 1 if ascent_flag else 0 + diss_flag = 1 if diss_flag else 0 + miss_handle = 1 if miss_handle else 0 + + # Change the sea surface temperature data unit to `degC` + sst_data = transfer_data_difference_units(sst_data, sst_data_units, "celsius") + + # Change the temperature data data unit to `degC` + temperature_data = transfer_data_difference_units( + temperature_data, temperature_data_units, "celsius" + ) + + # Change the surface pressure data unit to `hPa` + surface_pressure_data = transfer_data_multiple_units( + surface_pressure_data, surface_pressure_data_units, "hPa" + ) + + pressure_value = temperature_data[vertical_dim] + # Change the `pressure_value` unit to `hPa` + pressure_value = transfer_data_multiple_units( + pressure_value, vertical_dim_units, "hPa" + ) + + # transfer specific humidity to mixing ratio + mixing_ratio_data = transfer_specific_humidity_2_mixing_ratio( + specific_humidity_data=specific_humidity_data, + specific_humidity_data_units=specific_humidity_data_units, + ) + + # calculate PI over the whole data set using the xarray universal function + result = xr.apply_ufunc( + pi, + sst_data, + surface_pressure_data, + pressure_value, + temperature_data, + mixing_ratio_data, + kwargs=dict( + CKCD=CKCD, + ascent_flag=ascent_flag, + diss_flag=diss_flag, + V_reduc=V_reduc, + ptop=ptop, + miss_handle=miss_handle, + ), + input_core_dims=[ + [], + [], + [ + vertical_dim, + ], + [ + vertical_dim, + ], + [ + vertical_dim, + ], + ], + output_core_dims=[[], [], [], [], []], + vectorize=True, + ) + vmax, pmin, ifl, t0, otl = result + + # The analyses need SSTs are in kelvin + sst_data = transfer_data_difference_units(sst_data, "celsius", "kelvin") + + # calculate efficiency + efficiency = xr.apply_ufunc( + pi_efficiency, + sst_data, + t0, + input_core_dims=[ + [], + [], + ], + output_core_dims=[ + [], + ], + vectorize=True, + ) + + diseq = xr.apply_ufunc( + pi_diseq_resid, + vmax, + sst_data, + t0, + kwargs=dict(CKCD=CKCD), + input_core_dims=[ + [], + [], + [], + ], + output_core_dims=[ + [], + ], + vectorize=True, + ) + + result1 = xr.apply_ufunc( + decompose_pi, + vmax, + sst_data, + t0, + kwargs=dict(CKCD=CKCD), + input_core_dims=[ + [], + [], + [], + ], + output_core_dims=[ + [], + [], + [], + [], + ], + vectorize=True, + ) + lnpi, lneff, lndiseq, lnCKCD = result1 + + out_ds = xr.Dataset( + { + "vmax": vmax, + "pmin": pmin, + "ifl": ifl, + "t0": t0, + "otl": otl, + "eff": efficiency, + "diseq": diseq, + "lnpi": lnpi, + "lneff": lneff, + "lndiseq": lndiseq, + "lnCKCD": lnCKCD, + } + ) + + # add names and units + out_ds.vmax.attrs["standard_name"], out_ds.vmax.attrs["units"] = ( + "Maximum Potential Intensity", + "m/s", + ) + out_ds.pmin.attrs["standard_name"], out_ds.pmin.attrs["units"] = ( + "Minimum Central Pressure", + "hPa", + ) + out_ds.ifl.attrs["standard_name"] = "pyPI Flag" + out_ds.t0.attrs["standard_name"], out_ds.t0.attrs["units"] = ( + "Outflow Temperature", + "K", + ) + out_ds.otl.attrs["standard_name"], out_ds.otl.attrs["units"] = ( + "Outflow Temperature Level", + "hPa", + ) + + out_ds.eff.attrs["standard_name"], out_ds.eff.attrs["units"] = ( + "Tropical Cyclone Efficiency", + "unitless fraction", + ) + out_ds.diseq.attrs["standard_name"], out_ds.diseq.attrs["units"] = ( + "Thermodynamic Disequilibrium", + "J/kg", + ) + out_ds.lnpi.attrs["standard_name"] = "Natural log(Potential Intensity)" + out_ds.lneff.attrs["standard_name"] = "Natural log(Tropical Cyclone Efficiency)" + out_ds.lndiseq.attrs["standard_name"] = "Natural log(Thermodynamic Disequilibrium)" + out_ds.lnCKCD.attrs["standard_name"], out_ds.lnCKCD.attrs["units"] = ( + "Natural log(Ck/CD)", + "unitless constant", + ) + + # return the output as an xarray data structure + return out_ds diff --git a/test/test_air_sea_interaction_index_amm.py b/test/test_field_air_sea_interaction_index_amm.py similarity index 100% rename from test/test_air_sea_interaction_index_amm.py rename to test/test_field_air_sea_interaction_index_amm.py diff --git a/test/test_air_sea_interaction_index_atlantic_nino.py b/test/test_field_air_sea_interaction_index_atlantic_nino.py similarity index 100% rename from test/test_air_sea_interaction_index_atlantic_nino.py rename to test/test_field_air_sea_interaction_index_atlantic_nino.py diff --git a/test/test_air_sea_interaction_index_enso.py b/test/test_field_air_sea_interaction_index_enso.py similarity index 100% rename from test/test_air_sea_interaction_index_enso.py rename to test/test_field_air_sea_interaction_index_enso.py diff --git a/test/test_air_sea_interaction_index_iobm.py b/test/test_field_air_sea_interaction_index_iobm.py similarity index 100% rename from test/test_air_sea_interaction_index_iobm.py rename to test/test_field_air_sea_interaction_index_iobm.py diff --git a/test/test_air_sea_interaction_index_iod.py b/test/test_field_air_sea_interaction_index_iod.py similarity index 100% rename from test/test_air_sea_interaction_index_iod.py rename to test/test_field_air_sea_interaction_index_iod.py diff --git a/test/test_field_mesoscale_potential_intensity.py b/test/test_field_mesoscale_potential_intensity.py new file mode 100644 index 00000000..2b438b22 --- /dev/null +++ b/test/test_field_mesoscale_potential_intensity.py @@ -0,0 +1,228 @@ +""" +pytest for field/mesoscale/potential_intensity +""" + +import pytest + +import easyclimate as ecl +import xarray as xr +import numpy as np + +testdata_sst = xr.DataArray( + np.array([[28.861475, 28.552972], [29.157007, 28.649042]]), + dims=("lat", "lon"), + coords={"lat": np.array([2.5, 0.0]), "lon": np.array([-177.5, -175.0])}, + attrs={"standard_name": "Sea Surface Temperature", "units": "degrees C"}, +) +testdata_msl = xr.DataArray( + np.array([[1008.075708, 1008.171802], [1008.005922, 1008.088748]]), + dims=("lat", "lon"), + coords={"lat": np.array([2.5, 0.0]), "lon": np.array([-177.5, -175.0])}, + attrs={"standard_name": "Mean Sea Level Pressure", "units": "hPa"}, +) +testdata_t = xr.DataArray( + np.array( + [ + [[27.353636, 27.222672], [27.21099, 26.938538]], + [[25.190057, 25.069938], [25.007941, 24.750216]], + [[23.092178, 22.964785], [22.902211, 22.671951]], + [[21.464064, 21.194161], [21.422638, 21.28721]], + [[20.135445, 19.904204], [20.2931, 20.135855]], + [[19.135903, 19.107665], [19.392363, 19.440976]], + [[18.297735, 18.37927], [18.58249, 18.641281]], + [[17.206641, 17.328192], [17.450027, 17.518263]], + [[15.9993, 16.088557], [16.159143, 16.248171]], + [[14.678321, 14.700328], [14.817254, 14.830638]], + [[13.276337, 13.264193], [13.373714, 13.333164]], + [[11.805724, 11.77128], [11.80218, 11.800547]], + [[10.245422, 10.189297], [10.17729, 10.205829]], + [[6.958592, 6.955302], [6.958394, 6.957223]], + [[3.667473, 3.660736], [3.809784, 3.702164]], + [[0.086626, 0.099989], [0.178932, 0.153677]], + [[-4.108487, -3.972281], [-3.976002, -3.855817]], + [[-8.5458, -8.51175], [-8.630228, -8.564163]], + [[-13.561634, -13.553428], [-13.84696, -13.798673]], + [[-20.077641, -19.99213], [-20.159432, -20.108628]], + [[-28.503128, -28.444674], [-28.420612, -28.401702]], + [[-38.741623, -38.853656], [-38.611279, -38.734565]], + [[-51.723332, -51.758043], [-51.649847, -51.732749]], + [[-67.683181, -67.657047], [-67.849234, -67.727188]], + [[-84.802469, -84.811532], [-84.599578, -84.656952]], + [[-82.980426, -83.06524], [-83.076922, -83.306107]], + [[-72.109836, -71.961065], [-72.277637, -72.140224]], + [[-65.229228, -65.242002], [-65.227432, -65.291973]], + [[-56.258264, -56.437082], [-55.930041, -56.037194]], + [[-51.276108, -51.220998], [-51.082832, -51.050359]], + [[-44.492267, -44.431645], [-44.665341, -44.653366]], + ] + ), + dims=("level", "lat", "lon"), + coords={ + "level": np.array( + [ + 1000.0, + 975.0, + 950.0, + 925.0, + 900.0, + 875.0, + 850.0, + 825.0, + 800.0, + 775.0, + 750.0, + 725.0, + 700.0, + 650.0, + 600.0, + 550.0, + 500.0, + 450.0, + 400.0, + 350.0, + 300.0, + 250.0, + 200.0, + 150.0, + 100.0, + 70.0, + 50.0, + 40.0, + 30.0, + 20.0, + 10.0, + ] + ), + "lat": np.array([2.5, 0.0]), + "lon": np.array([-177.5, -175.0]), + }, + attrs={"standard_name": "Atmospheric Temperature", "units": "degrees C"}, +) +testdata_q = xr.DataArray( + np.array( + [ + [[1.752169e01, 1.739373e01], [1.755779e01, 1.747079e01]], + [[1.711666e01, 1.697109e01], [1.715569e01, 1.708690e01]], + [[1.677095e01, 1.662145e01], [1.673994e01, 1.663940e01]], + [[1.593267e01, 1.590978e01], [1.558633e01, 1.544861e01]], + [[1.473882e01, 1.455964e01], [1.387122e01, 1.373549e01]], + [[1.304239e01, 1.267559e01], [1.199529e01, 1.146268e01]], + [[1.127705e01, 1.066488e01], [1.025696e01, 9.864766e00]], + [[9.847610e00, 9.396217e00], [9.291184e00, 8.872944e00]], + [[8.918181e00, 8.675946e00], [8.745527e00, 8.273892e00]], + [[8.272269e00, 8.040189e00], [8.070926e00, 7.926245e00]], + [[7.787410e00, 7.493204e00], [7.475906e00, 7.490718e00]], + [[7.253088e00, 6.989394e00], [6.969766e00, 6.952205e00]], + [[6.702615e00, 6.516205e00], [6.319682e00, 6.375766e00]], + [[5.709916e00, 5.521406e00], [5.232806e00, 5.305737e00]], + [[4.398887e00, 4.284769e00], [4.266110e00, 4.302661e00]], + [[3.194687e00, 2.911649e00], [3.216351e00, 3.126061e00]], + [[2.166236e00, 1.930091e00], [2.258709e00, 2.104025e00]], + [[1.585033e00, 1.382272e00], [1.726041e00, 1.579070e00]], + [[1.132454e00, 9.975798e-01], [1.327680e00, 1.184414e00]], + [[7.866390e-01, 7.202522e-01], [8.988403e-01, 8.505270e-01]], + [[4.513145e-01, 4.453679e-01], [5.425988e-01, 5.050453e-01]], + [[2.165959e-01, 1.997725e-01], [2.575166e-01, 2.287391e-01]], + [[7.002809e-02, 6.306999e-02], [8.013048e-02, 6.932322e-02]], + [[1.401548e-02, 1.282958e-02], [1.460552e-02, 1.323063e-02]], + [[1.314765e-03, 1.300477e-03], [1.292135e-03, 1.284880e-03]], + [[1.717125e-03, 1.726488e-03], [1.672255e-03, 1.674200e-03]], + [[2.587957e-03, 2.586587e-03], [2.588366e-03, 2.586200e-03]], + [[2.572437e-03, 2.572403e-03], [2.572174e-03, 2.572327e-03]], + [[2.530370e-03, 2.530549e-03], [2.530676e-03, 2.530960e-03]], + [[2.556868e-03, 2.556884e-03], [2.555793e-03, 2.555478e-03]], + [[2.825350e-03, 2.825275e-03], [2.810023e-03, 2.809112e-03]], + ] + ), + dims=("level", "lat", "lon"), + coords={ + "level": np.array( + [ + 1000.0, + 975.0, + 950.0, + 925.0, + 900.0, + 875.0, + 850.0, + 825.0, + 800.0, + 775.0, + 750.0, + 725.0, + 700.0, + 650.0, + 600.0, + 550.0, + 500.0, + 450.0, + 400.0, + 350.0, + 300.0, + 250.0, + 200.0, + 150.0, + 100.0, + 70.0, + 50.0, + 40.0, + 30.0, + 20.0, + 10.0, + ] + ), + "lat": np.array([2.5, 0.0]), + "lon": np.array([-177.5, -175.0]), + }, + attrs={"standard_name": "Specific Humidity", "units": "g/kg"}, +) + + +def test_calc_potential_intensity_Bister_Emanuel_2002(): + result_data = ecl.field.mesoscale.calc_potential_intensity_Bister_Emanuel_2002( + sst_data=testdata_sst, + sst_data_units="degC", + surface_pressure_data=testdata_msl, + surface_pressure_data_units="hPa", + temperature_data=testdata_t, + temperature_data_units="degC", + specific_humidity_data=testdata_q, + specific_humidity_data_units="g/kg", + vertical_dim="level", + vertical_dim_units="hPa", + ) + result_data1 = result_data.vmax.data.flatten()[1:3] + result_data2 = result_data.pmin.data.flatten()[1:3] + result_data3 = result_data.ifl.data.flatten()[1:3] + result_data4 = result_data.t0.data.flatten()[1:3] + result_data5 = result_data.otl.data.flatten()[1:3] + result_data6 = result_data.eff.data.flatten()[1:3] + result_data7 = result_data.diseq.data.flatten()[1:3] + result_data8 = result_data.lnpi.data.flatten()[1:3] + result_data9 = result_data.lneff.data.flatten()[1:3] + result_data10 = result_data.lndiseq.data.flatten()[1:3] + result_data11 = result_data.lnCKCD.data.flatten()[1:3] + + refer_data1 = np.array([90.93510812, 98.74747325]) + refer_data2 = np.array([900.4934007, 882.44831663]) + refer_data3 = np.array([1, 1]) + refer_data4 = np.array([189.45530961, 189.75865991]) + refer_data5 = np.array([80.81349033, 76.1947957]) + refer_data6 = np.array([0.59247567, 0.5931131]) + refer_data7 = np.array([15507.79821296, 18267.19889297]) + refer_data8 = np.array([9.02029231, 9.18513163]) + refer_data9 = np.array([-0.52344546, -0.52237017]) + refer_data10 = np.array([9.64909829, 9.81286232]) + refer_data11 = np.array([-0.10536052, -0.10536052]) + + assert np.isclose(result_data1, refer_data1).all() + assert np.isclose(result_data2, refer_data2).all() + assert np.isclose(result_data3, refer_data3).all() + assert np.isclose(result_data4, refer_data4).all() + assert np.isclose(result_data5, refer_data5).all() + assert np.isclose(result_data6, refer_data6).all() + assert np.isclose(result_data7, refer_data7).all() + assert np.isclose(result_data8, refer_data8).all() + assert np.isclose(result_data9, refer_data9).all() + assert np.isclose(result_data10, refer_data10).all() + assert np.isclose(result_data11, refer_data11).all()