@@ -582,18 +582,21 @@ def spei(
582582 precip_var_name : Optional [str ] = None ,
583583 pet_var_name : Optional [str ] = None ,
584584 temp_var_name : Optional [str ] = None ,
585- distribution : str = DEFAULT_DISTRIBUTION
585+ distribution : str = DEFAULT_DISTRIBUTION ,
586+ pet_method : str = 'thornthwaite' ,
587+ temp_min : Optional [Union [np .ndarray , xr .DataArray ]] = None ,
588+ temp_max : Optional [Union [np .ndarray , xr .DataArray ]] = None
586589) -> Union [xr .DataArray , Tuple [xr .DataArray , Dict [str , np .ndarray ]]]:
587590 """
588591 Calculate Standardized Precipitation Evapotranspiration Index (SPEI).
589592
590593 SPEI uses the water balance (P - PET) instead of just precipitation.
591594 PET can be provided directly or calculated from temperature using
592- the Thornthwaite method.
595+ Thornthwaite or Hargreaves-Samani method.
593596
594597 :param precip: precipitation data in mm
595598 :param pet: potential evapotranspiration in mm (optional if temperature provided)
596- :param temperature: temperature in ° C for PET calculation (optional if PET provided)
599+ :param temperature: mean temperature in C for PET calculation (optional if PET provided)
597600 :param latitude: latitude for PET calculation (required if using temperature)
598601 :param scale: accumulation period in time steps
599602 :param periodicity: 'monthly' or 'daily'
@@ -608,6 +611,11 @@ def spei(
608611 :param distribution: distribution type ('gamma', 'pearson3', 'log_logistic',
609612 'gev', 'gen_logistic'). Default: 'gamma'.
610613 Note: Pearson III or Log-Logistic are recommended for SPEI.
614+ :param pet_method: PET calculation method ('thornthwaite' or 'hargreaves').
615+ - 'thornthwaite': Uses only mean temperature (default)
616+ - 'hargreaves': Uses mean, min, max temperature (better for arid regions)
617+ :param temp_min: minimum temperature in C (required for Hargreaves method)
618+ :param temp_max: maximum temperature in C (required for Hargreaves method)
611619 :return: SPEI values as xarray DataArray, or tuple (SPEI, params)
612620
613621 Example:
@@ -617,9 +625,13 @@ def spei(
617625 >>> # With Pearson III distribution (recommended for SPEI)
618626 >>> spei_12 = spei(precip_da, pet=pet_da, scale=12, distribution='pearson3')
619627
620- >>> # With temperature (auto-compute PET )
628+ >>> # With temperature - Thornthwaite method (default )
621629 >>> spei_12 = spei(precip_da, temperature=temp_da, latitude=lat_da, scale=12)
622630
631+ >>> # With temperature - Hargreaves method (better for arid regions)
632+ >>> spei_12 = spei(precip_da, temperature=temp_mean, latitude=lat_da, scale=12,
633+ ... pet_method='hargreaves', temp_min=tmin, temp_max=tmax)
634+
623635 >>> # Save and reuse parameters
624636 >>> spei_12, params = spei(precip_da, pet=pet_da, scale=12, return_params=True)
625637 >>> save_fitting_params(params, 'spei_params.nc', scale=12,
@@ -666,11 +678,18 @@ def spei(
666678 pet_array = np .asarray (pet )
667679 elif temperature is not None :
668680 # Compute PET from temperature
669- _logger .info ("Computing PET from temperature using Thornthwaite method" )
681+ pet_method = pet_method .lower ()
682+ _logger .info (f"Computing PET from temperature using { pet_method .capitalize ()} method" )
670683
671684 if latitude is None :
672685 raise ValueError ("latitude required for PET calculation from temperature" )
673686
687+ if pet_method == 'hargreaves' and (temp_min is None or temp_max is None ):
688+ raise ValueError (
689+ "Hargreaves method requires temp_min and temp_max parameters. "
690+ "Use pet_method='thornthwaite' if only mean temperature is available."
691+ )
692+
674693 # Handle temperature input
675694 if isinstance (temperature , xr .Dataset ):
676695 if temp_var_name is None :
@@ -693,8 +712,13 @@ def spei(
693712 if data_start_year is None :
694713 raise ValueError ("data_start_year required for PET calculation" )
695714
696- # Calculate PET
697- pet_da = calculate_pet (temp_da , latitude , data_start_year )
715+ # Calculate PET using specified method
716+ pet_da = calculate_pet (
717+ temp_da , latitude , data_start_year ,
718+ method = pet_method ,
719+ temp_min = temp_min ,
720+ temp_max = temp_max
721+ )
698722 pet_array = pet_da .values if isinstance (pet_da , xr .DataArray ) else pet_da
699723 else :
700724 raise ValueError ("Either 'pet' or 'temperature' (with 'latitude') must be provided" )
@@ -822,14 +846,17 @@ def spei_multi_scale(
822846 pet_var_name : Optional [str ] = None ,
823847 temp_var_name : Optional [str ] = None ,
824848 distribution : str = DEFAULT_DISTRIBUTION ,
825- global_attrs : Optional [Dict ] = None
849+ global_attrs : Optional [Dict ] = None ,
850+ pet_method : str = 'thornthwaite' ,
851+ temp_min : Optional [Union [np .ndarray , xr .DataArray ]] = None ,
852+ temp_max : Optional [Union [np .ndarray , xr .DataArray ]] = None
826853) -> Union [xr .Dataset , Tuple [xr .Dataset , Dict [int , Dict [str , np .ndarray ]]]]:
827854 """
828855 Calculate SPEI for multiple time scales.
829856
830857 :param precip: precipitation data
831858 :param pet: potential evapotranspiration (optional if temperature provided)
832- :param temperature: temperature for PET calculation
859+ :param temperature: mean temperature for PET calculation
833860 :param latitude: latitude for PET calculation
834861 :param scales: list of accumulation scales (e.g., [1, 3, 6, 12])
835862 :param periodicity: 'monthly' or 'daily'
@@ -844,6 +871,9 @@ def spei_multi_scale(
844871 'gev', 'gen_logistic'). Default: 'gamma'
845872 :param global_attrs: optional dict of global attributes to override defaults
846873 (e.g., {'institution': 'My Org', 'source': 'My Project'})
874+ :param pet_method: PET calculation method ('thornthwaite' or 'hargreaves')
875+ :param temp_min: minimum temperature (required for Hargreaves)
876+ :param temp_max: maximum temperature (required for Hargreaves)
847877 :return: Dataset with SPEI for all scales
848878
849879 Example:
@@ -852,6 +882,10 @@ def spei_multi_scale(
852882 >>> # With Pearson III (recommended for SPEI)
853883 >>> spei_ds = spei_multi_scale(precip_da, pet=pet_da, scales=[3, 12],
854884 ... distribution='pearson3')
885+ >>> # With Hargreaves PET
886+ >>> spei_ds = spei_multi_scale(precip_da, temperature=tmean, latitude=lat,
887+ ... scales=[3, 12], pet_method='hargreaves',
888+ ... temp_min=tmin, temp_max=tmax)
855889 """
856890 dist = distribution .lower ()
857891 _logger .info (f"Computing SPEI for scales: { scales } (distribution={ dist } )" )
@@ -879,7 +913,10 @@ def spei_multi_scale(
879913 precip_var_name = precip_var_name ,
880914 pet_var_name = pet_var_name ,
881915 temp_var_name = temp_var_name ,
882- distribution = dist
916+ distribution = dist ,
917+ pet_method = pet_method ,
918+ temp_min = temp_min ,
919+ temp_max = temp_max
883920 )
884921
885922 var_name_out = get_variable_name ('spei' , s , periodicity , distribution = dist )
0 commit comments