def pet(temperature_celsius: np.ndarray, latitude_degrees: float, data_start_year: int) -> np.ndarray: """ This function computes potential evapotranspiration (PET) using Thornthwaite's equation. :param temperature_celsius: an array of average temperature values, in degrees Celsius :param latitude_degrees: the latitude of the location, in degrees north, must be within range [-90.0 ... 90.0] (inclusive), otherwise a ValueError is raised :param data_start_year: the initial year of the input dataset :return: an array of PET values, of the same size and shape as the input temperature values array, in millimeters/time step :rtype: 1-D numpy.ndarray of floats """ # make sure we're not dealing with all NaN values if np.ma.isMaskedArray(temperature_celsius) and ( temperature_celsius.count() == 0): # we started with all NaNs for the temperature, so just return the same as PET return temperature_celsius else: # we were passed a vanilla Numpy array, look for indices where the value == NaN if np.all(np.isnan(temperature_celsius)): # we started with all NaNs for the temperature, so just return the same return temperature_celsius # If we've been passed an array of latitude values then just use # the first one -- useful when applying this function with xarray.GroupBy # or numpy.apply_along_axis() where we've had to duplicate values in a 3-D # array of latitudes in order to correspond with a 3-D array of temperatures. if isinstance(latitude_degrees, np.ndarray) and (latitude_degrees.size > 1): latitude_degrees = latitude_degrees.flat[0] # make sure we're not dealing with a NaN or out-of-range latitude value if ((latitude_degrees is not None) and not np.isnan(latitude_degrees) and (latitude_degrees < 90.0) and (latitude_degrees > -90.0)): # compute and return the PET values using Thornthwaite's equation return eto.eto_thornthwaite( temperature_celsius, latitude_degrees, data_start_year, ) else: message = ("Invalid latitude value: " + str(latitude_degrees) + " (must be in degrees north, between -90.0 and " + "90.0 inclusive)") _logger.error(message) raise ValueError(message)
def test_eto_thornthwaite(temps_celsius, latitude_degrees, data_year_start_monthly, pet_thornthwaite_mm): # compute PET from the monthly temperatures, latitude, and initial years computed_pet = eto.eto_thornthwaite(temps_celsius, latitude_degrees, data_year_start_monthly ) # make sure PET is being computed as expected np.testing.assert_allclose(computed_pet, pet_thornthwaite_mm.flatten(), atol=0.001, equal_nan=True, err_msg="PET (Thornthwaite) values not computed as expected") # make sure that a 3-D array raises an error reshaped_temps = np.reshape(temps_celsius[0:1400], (123, 2, 6)) pytest.raises(ValueError, eto.eto_thornthwaite, reshaped_temps, latitude_degrees, data_year_start_monthly) # make sure that an invalid latitude value (lat > 90) raises an error pytest.raises(ValueError, eto.eto_thornthwaite, temps_celsius, 91.0, # latitude > 90 is invalid data_year_start_monthly) # make sure that an invalid latitude value (lat < -90) raises an error pytest.raises(ValueError, eto.eto_thornthwaite, temps_celsius, -91.0, # latitude < -90 is invalid data_year_start_monthly) # make sure that an invalid latitude value (None) raises an error pytest.raises(TypeError, eto.eto_thornthwaite, temps_celsius, None, data_year_start_monthly) # make sure that an invalid latitude value (NaN) raises an error pytest.raises(ValueError, eto.eto_thornthwaite, temps_celsius, np.NaN, data_year_start_monthly)