def test_relative_humidity_to_dewpoint(self):
        """Ensures correct output from relative_humidity_to_dewpoint."""

        these_dewpoints_k = moisture_conversions.relative_humidity_to_dewpoint(
            RELATIVE_HUMIDITIES, TEMPERATURES_KELVINS, TOTAL_PRESSURES_PASCALS)
        self.assertTrue(
            numpy.allclose(these_dewpoints_k,
                           DEWPOINTS_KELVINS,
                           atol=TOLERANCE))
    def test_relative_humidity_to_dewpoint_second(self):
        """Ensures correct output from relative_humidity_to_dewpoint.

        In this case, using fixed relative humidities (zero when air pressure is
        zero).
        """

        these_dewpoints_kelvins = moisture_conv.relative_humidity_to_dewpoint(
            relative_humidities=RELATIVE_HUMIDITIES_FIXED,
            temperatures_kelvins=TEMPERATURES_KELVINS,
            total_pressures_pascals=PRESSURES_PASCALS)

        self.assertTrue(
            numpy.allclose(these_dewpoints_kelvins,
                           DEWPOINTS_KELVINS,
                           atol=TOLERANCE))
def soundings_to_metpy_dictionaries(sounding_matrix,
                                    field_names,
                                    height_levels_m_agl=None,
                                    storm_elevations_m_asl=None):
    """Converts soundings to format required by MetPy.

    If `sounding_matrix` contains pressures, `height_levels_m_agl` and
    `storm_elevations_m_asl` will not be used.

    Otherwise, `height_levels_m_agl` and `storm_elevations_m_asl` will be used
    to estimate the pressure levels for each sounding.

    :param sounding_matrix: numpy array (E x H_s x F_s) of soundings.
    :param field_names: list (length F_s) of field names, in the order that they
        appear in `sounding_matrix`.
    :param height_levels_m_agl: numpy array (length H_s) of height levels
        (metres above ground level), in the order that they appear in
        `sounding_matrix`.
    :param storm_elevations_m_asl: length-E numpy array of storm elevations
        (metres above sea level).
    :return: list_of_metpy_dictionaries: length-E list of dictionaries.  The
        format of each dictionary is described in the input doc for
        `sounding_plotting.plot_sounding`.
    """

    error_checking.assert_is_string_list(field_names)
    error_checking.assert_is_numpy_array(numpy.array(field_names),
                                         num_dimensions=1)
    check_soundings(sounding_matrix=sounding_matrix,
                    num_fields=len(field_names))

    try:
        pressure_index = field_names.index(soundings.PRESSURE_NAME)
        pressure_matrix_pascals = sounding_matrix[..., pressure_index]
    except ValueError:
        error_checking.assert_is_geq_numpy_array(height_levels_m_agl, 0)
        error_checking.assert_is_numpy_array(height_levels_m_agl,
                                             num_dimensions=1)

        error_checking.assert_is_numpy_array_without_nan(
            storm_elevations_m_asl)
        error_checking.assert_is_numpy_array(storm_elevations_m_asl,
                                             num_dimensions=1)

        num_height_levels = len(height_levels_m_agl)
        num_examples = len(storm_elevations_m_asl)
        check_soundings(sounding_matrix=sounding_matrix,
                        num_examples=num_examples,
                        num_height_levels=num_height_levels)

        height_matrix_m_asl = numpy.full((num_examples, num_height_levels),
                                         numpy.nan)
        for i in range(num_examples):
            height_matrix_m_asl[i, ...] = (height_levels_m_agl +
                                           storm_elevations_m_asl[i])

        pressure_matrix_pascals = standard_atmo.height_to_pressure(
            height_matrix_m_asl)

    try:
        temperature_index = field_names.index(soundings.TEMPERATURE_NAME)
        temperature_matrix_kelvins = sounding_matrix[..., temperature_index]
    except ValueError:
        virtual_pot_temp_index = field_names.index(
            soundings.VIRTUAL_POTENTIAL_TEMPERATURE_NAME)
        temperature_matrix_kelvins = (
            temperature_conversions.temperatures_from_potential_temperatures(
                potential_temperatures_kelvins=sounding_matrix[
                    ..., virtual_pot_temp_index],
                total_pressures_pascals=pressure_matrix_pascals))

    try:
        specific_humidity_index = field_names.index(
            soundings.SPECIFIC_HUMIDITY_NAME)
        dewpoint_matrix_kelvins = (
            moisture_conversions.specific_humidity_to_dewpoint(
                specific_humidities_kg_kg01=sounding_matrix[
                    ..., specific_humidity_index],
                total_pressures_pascals=pressure_matrix_pascals))
    except ValueError:
        relative_humidity_index = field_names.index(
            soundings.RELATIVE_HUMIDITY_NAME)
        dewpoint_matrix_kelvins = (
            moisture_conversions.relative_humidity_to_dewpoint(
                relative_humidities=sounding_matrix[...,
                                                    relative_humidity_index],
                temperatures_kelvins=temperature_matrix_kelvins,
                total_pressures_pascals=pressure_matrix_pascals))

    temperature_matrix_celsius = temperature_conversions.kelvins_to_celsius(
        temperature_matrix_kelvins)
    dewpoint_matrix_celsius = temperature_conversions.kelvins_to_celsius(
        dewpoint_matrix_kelvins)

    try:
        u_wind_index = field_names.index(soundings.U_WIND_NAME)
        v_wind_index = field_names.index(soundings.V_WIND_NAME)
        include_wind = True
    except ValueError:
        include_wind = False

    num_examples = sounding_matrix.shape[0]
    list_of_metpy_dictionaries = [None] * num_examples

    for i in range(num_examples):
        list_of_metpy_dictionaries[i] = {
            soundings.PRESSURE_COLUMN_METPY:
            pressure_matrix_pascals[i, :] * PASCALS_TO_MB,
            soundings.TEMPERATURE_COLUMN_METPY:
            temperature_matrix_celsius[i, :],
            soundings.DEWPOINT_COLUMN_METPY: dewpoint_matrix_celsius[i, :],
        }

        if include_wind:
            list_of_metpy_dictionaries[i].update({
                soundings.U_WIND_COLUMN_METPY:
                (sounding_matrix[i, ..., u_wind_index] *
                 METRES_PER_SECOND_TO_KT),
                soundings.V_WIND_COLUMN_METPY:
                (sounding_matrix[i, ..., v_wind_index] *
                 METRES_PER_SECOND_TO_KT)
            })

    return list_of_metpy_dictionaries