Repository: Unidata/MetPy

Issue:
el and lfc don't adjust profile for nans
As noted:

        It looks that the "_remove_nans()" remove nans for p, T, and Td. 
However, when I provide "parcel_profile" like the example, it is not touched, so I think the error occurs because of the difference of length between prof and other variables.

_Originally posted by @DJ4seasons in https://github.com/Unidata/MetPy/discussions/2920#discussioncomment-4939165_

`el` (and `lfc`), when given a profile (rather than calculating it themselves), don't adjust the profile when removing nans.

Reproducer:
```python
from metpy.units import units
import metpy.calc as mpcalc
import numpy as np
p = [1008., 1000., 950., 900., 850., 800., 750., 700., 650., 600.,
     550., 500., 450., 400., 350., 300., 250., 200.,
     175., 150., 125., 100., 80., 70., 60., 50.,
     40., 30., 25., 20.] * units.hPa
T = [29.3, 28.1, 23.5, 20.9, 18.4, 15.9, 13.1, 10.1, 6.7, 3.1,
     -0.5, -4.5, -9.0, -14.8, -21.5, -29.7, -40.0, -52.4,
     -59.2, -66.5, -74.1, -78.5, -76.0, -71.6, -66.7, -61.3,
     -56.3, -51.7, -50.7, -47.5] * units.degC
rh = [.85, .65, .36, .39, .82, .72, .75, .86, .65, .22, .52,
      .66, .64, .20, .05, .75, .76, .45, .25, .48, .76, .88,
      .56, .88, .39, .67, .15, .04, .94, .35] * units.dimensionless
rh[-2] = np.nan
Td = mpcalc.dewpoint_from_relative_humidity(T, rh)
prof = mpcalc.parcel_profile(p, T[0], Td[0]).to('degC')
mpcalc.el(p, T, Td, prof)
``` 

Discussion:

Patch:
diff --git a/src/metpy/calc/thermo.py b/src/metpy/calc/thermo.py
index 483a6f6b70..327aff534c 100644
--- a/src/metpy/calc/thermo.py
+++ b/src/metpy/calc/thermo.py
@@ -667,12 +667,15 @@ def lfc(pressure, temperature, dewpoint, parcel_temperature_profile=None, dewpoi
        Renamed ``dewpt``,``dewpoint_start`` parameters to ``dewpoint``, ``dewpoint_start``
 
     """
-    pressure, temperature, dewpoint = _remove_nans(pressure, temperature, dewpoint)
     # Default to surface parcel if no profile or starting pressure level is given
     if parcel_temperature_profile is None:
-        new_stuff = parcel_profile_with_lcl(pressure, temperature, dewpoint)
-        pressure, temperature, dewpoint, parcel_temperature_profile = new_stuff
+        pressure, temperature, dewpoint = _remove_nans(pressure, temperature, dewpoint)
+        new_profile = parcel_profile_with_lcl(pressure, temperature, dewpoint)
+        pressure, temperature, dewpoint, parcel_temperature_profile = new_profile
         parcel_temperature_profile = parcel_temperature_profile.to(temperature.units)
+    else:
+        new_profile = _remove_nans(pressure, temperature, dewpoint, parcel_temperature_profile)
+        pressure, temperature, dewpoint, parcel_temperature_profile = new_profile
 
     if dewpoint_start is None:
         dewpoint_start = dewpoint[0]
@@ -878,12 +881,15 @@ def el(pressure, temperature, dewpoint, parcel_temperature_profile=None, which='
        Renamed ``dewpt`` parameter to ``dewpoint``
 
     """
-    pressure, temperature, dewpoint = _remove_nans(pressure, temperature, dewpoint)
     # Default to surface parcel if no profile or starting pressure level is given
     if parcel_temperature_profile is None:
-        new_stuff = parcel_profile_with_lcl(pressure, temperature, dewpoint)
-        pressure, temperature, dewpoint, parcel_temperature_profile = new_stuff
+        pressure, temperature, dewpoint = _remove_nans(pressure, temperature, dewpoint)
+        new_profile = parcel_profile_with_lcl(pressure, temperature, dewpoint)
+        pressure, temperature, dewpoint, parcel_temperature_profile = new_profile
         parcel_temperature_profile = parcel_temperature_profile.to(temperature.units)
+    else:
+        new_profile = _remove_nans(pressure, temperature, dewpoint, parcel_temperature_profile)
+        pressure, temperature, dewpoint, parcel_temperature_profile = new_profile
 
     # If the top of the sounding parcel is warmer than the environment, there is no EL
     if parcel_temperature_profile[-1] > temperature[-1]:


Test patch:
diff --git a/tests/calc/test_thermo.py b/tests/calc/test_thermo.py
index 93bd5d36d5..3119ed1da4 100644
--- a/tests/calc/test_thermo.py
+++ b/tests/calc/test_thermo.py
@@ -663,6 +663,27 @@ def test_lfc_equals_lcl():
     assert_almost_equal(lfc_temp, 15.8714 * units.celsius, 2)
 
 
+def test_lfc_profile_nan():
+    """Test LFC when the profile includes NaN values."""
+    levels = np.array([959., 779.2, 751.3, 724.3, 700., 269.]) * units.mbar
+    temperatures = np.array([22.2, 14.6, np.nan, 9.4, 7., -38.]) * units.degC
+    dewpoints = np.array([19., -11.2, -10.8, -10.4, np.nan, -53.2]) * units.degC
+    lfc_pressure, lfc_temperature = lfc(levels, temperatures, dewpoints)
+    assert_almost_equal(lfc_pressure, 727.3365 * units.mbar, 3)
+    assert_almost_equal(lfc_temperature, 9.6977 * units.degC, 3)
+
+
+def test_lfc_profile_nan_with_parcel_profile():
+    """Test LFC when the profile includes NaN values, and parcel temp profile is specified."""
+    levels = np.array([959., 779.2, 751.3, 724.3, 700., 269.]) * units.mbar
+    temperatures = np.array([22.2, 14.6, np.nan, 9.4, 7., -38.]) * units.degC
+    dewpoints = np.array([19., -11.2, -10.8, -10.4, np.nan, -53.2]) * units.degC
+    parcel_temps = parcel_profile(levels, temperatures[0], dewpoints[0]).to('degC')
+    lfc_pressure, lfc_temperature = lfc(levels, temperatures, dewpoints, parcel_temps)
+    assert_almost_equal(lfc_pressure, 727.3365 * units.mbar, 3)
+    assert_almost_equal(lfc_temperature, 9.6977 * units.degC, 3)
+
+
 def test_sensitive_sounding():
     """Test quantities for a sensitive sounding (#902)."""
     # This sounding has a very small positive area in the low level. It's only captured
@@ -987,6 +1008,27 @@ def test_el_below_lcl():
     assert_nan(el_t, t.units)
 
 
+def test_el_profile_nan():
+    """Test EL when the profile includes NaN values."""
+    levels = np.array([959., 779.2, 751.3, 724.3, 700., 269.]) * units.mbar
+    temperatures = np.array([22.2, 14.6, np.nan, 9.4, 7., -38.]) * units.degC
+    dewpoints = np.array([19., -11.2, -10.8, -10.4, np.nan, -53.2]) * units.degC
+    el_pressure, el_temperature = el(levels, temperatures, dewpoints)
+    assert_almost_equal(el_pressure, 673.0104 * units.mbar, 3)
+    assert_almost_equal(el_temperature, 5.8853 * units.degC, 3)
+
+
+def test_el_profile_nan_with_parcel_profile():
+    """Test EL when the profile includes NaN values, and a parcel temp profile is specified."""
+    levels = np.array([959., 779.2, 751.3, 724.3, 700., 269.]) * units.mbar
+    temperatures = np.array([22.2, 14.6, np.nan, 9.4, 7., -38.]) * units.degC
+    dewpoints = np.array([19., -11.2, -10.8, -10.4, np.nan, -53.2]) * units.degC
+    parcel_temps = parcel_profile(levels, temperatures[0], dewpoints[0]).to('degC')
+    el_pressure, el_temperature = el(levels, temperatures, dewpoints, parcel_temps)
+    assert_almost_equal(el_pressure, 673.0104 * units.mbar, 3)
+    assert_almost_equal(el_temperature, 5.8853 * units.degC, 3)
+
+
 def test_wet_psychrometric_vapor_pressure():
     """Test calculation of vapor pressure from wet and dry bulb temperatures."""
     p = 1013.25 * units.mbar