File Coverage

blib/lib/Drought/PET/Thornthwaite.pm
Criterion Covered Total %
statement 67 73 91.7
branch 24 30 80.0
condition 26 39 66.6
subroutine 8 8 100.0
pod 2 2 100.0
total 127 152 83.5


line stmt bran cond sub pod time code
1             package Drought::PET::Thornthwaite;
2              
3 2     2   274898 use 5.006;
  2         10  
4 2     2   14 use strict;
  2         10  
  2         83  
5 2     2   13 use warnings;
  2         4  
  2         158  
6 2     2   13 use Carp qw(carp croak cluck confess);
  2         19  
  2         215  
7             require Exporter;
8 2     2   1374 use Math::Trig;
  2         41429  
  2         357  
9 2     2   25 use Scalar::Util qw(looks_like_number reftype);
  2         4  
  2         1726  
10              
11             our @ISA = qw(Exporter);
12             our @EXPORT_OK = qw(
13             pet_thornthwaite
14             tei_thornthwaite
15             );
16              
17             =head1 NAME
18              
19             Drought::PET::Thornthwaite - Calculate potential evapotranspiration (PET) using the Thornthwaite method
20              
21             =head1 VERSION
22              
23             Version 0.50
24              
25             =cut
26              
27             our $VERSION = '0.50';
28              
29             =head1 SYNOPSIS
30              
31             use Drought::PET::Thornthwaite qw(pet_thornthwaite tei_thornthwaite);
32              
33             =head1 DESCRIPTION
34              
35             Evapotranspiration is the amount of water removed from the soil due to both evaporation and
36             vegetative consumption. Factors such as sun angle, temperature, soil type, the amount of
37             available water, and the types of vegetation all affect the amount of evapotranspiration
38             that occurs in a given location. The potential evapotranspiration (PET) is defined as the
39             amount of evapotranspiration that would occur under a defined set of conditions if the amount
40             of available water in the soil was limitless and accessible. The PET is a component of various
41             drought and water balance models. Numerous techniques have been developed to calculate
42             and estimate potential evapotranspiration.
43              
44             The Drought::PET::Thornthwaite package provides functions that can calculate the PET
45             based on the Thornthwaite Equation (Thornthwaite 1948). This equation estimates PET based
46             on the day of year, the latitude of the target location, the temperature observed during
47             the period for which PET is calculated, and a monthly temperature-based climatological index
48             specific to the target region. This index is typically called the Thornthwaite heat index,
49             or the temperature efficiency index. The documentation for this package refers to this index
50             as the temperature efficiency index (TEI).
51              
52             =head1 EXPORT
53              
54             The following functions provided by the Drought::PET::Thornthwaite package
55             are exportable.
56              
57             =head2 pet_thornthwaite
58              
59             my $pet = pet_thornthwaite($yday,$ndays,$lat,$temp,$tei,$missing_val);
60              
61             Calculate the potential evapotranspiration in millimeters using the Thornthwaite Equation.
62             Temperatures at or below zero return a zero PET. For temperatures at or above 26.5 degrees,
63             an adjusted equation described in Huang et al. 1996 is used to estimate the PET, since the
64             original Thornthwaite Equation becomes increasingly inaccurate at higher temperatures.
65              
66             This function requires five arguments:
67              
68             =over 4
69              
70             =item * C<$yday>: The day of the year (1 - 366). If the PET is calculated for a period of more than one day, supply the midpoint date
71              
72             =item * C<$ndays>: The number of days in the period over which the PET is calculated, e.g., 7 for a weekly PET. Must be 1 or greater
73              
74             =item * C<$lat>: The latitude of the location for which the PET is calculated in degrees (must be 0 to 90)
75              
76             =item * C<$temp>: The average temperature observed during the period in degrees Celsius
77              
78             =item * C<$tei>: The climatological "temperature efficiency index" defined in Thornthwaite (1948)
79              
80             An optional sixth argument can be supplied, which would be considered the missing data value.
81             The default missing value is NaN. If the YDAY, NDAYS, or LAT arguments are non-numeric or
82             invalid, the pet_thornthwaite function will L.
83             If the TEMP or TEI arguments are undefined, missing, or invalid, the function will return
84             the missing value.
85              
86             This function currently only works for Northern Hemisphere locations.
87              
88             =back
89              
90             =cut
91              
92             sub pet_thornthwaite {
93             # Define a NaN value for later use
94 11     11 1 202777 my $inf = exp(~0 >> 1);
95 11         25 my $nan = $inf / $inf;
96              
97             # Make sure the caller supplied sufficient arguments
98 11 50 33     52 unless(@_ and @_ >= 5) { croak "Insufficient arguments supplied"; }
  0         0  
99              
100             # Get the arguments
101 11         14 my $yday = shift;
102 11         13 my $ndays = shift;
103 11         13 my $lat = shift;
104 11         13 my $temperature = shift;
105 11         13 my $tei = shift;
106 11         10 my $missing_val = $nan;
107              
108             # Replace the default missing value if the caller supplied one
109 11 100       18 if(@_) { $missing_val = shift; }
  1         2  
110              
111             # Validate the non-data arguments
112 11 50       40 if(not looks_like_number($yday)) { croak "YDAY arg must be numeric"; }
  0         0  
113 11         16 $yday = int($yday);
114 11 100 66     40 if(not $yday >= 1 or not $yday <= 366) { croak "YDAY arg must be between 1 and 366"; }
  2         393  
115 9 50       16 if(not looks_like_number($ndays)) { croak "NDAYS arg must be numeric"; }
  0         0  
116 9         11 $ndays = int($ndays);
117 9 100       28 if(not $ndays >= 1) { croak "NDAYS arg must be 1 or greater"; }
  2         221  
118 7 50       13 if(not looks_like_number($lat)) { croak "LAT arg must be numeric"; }
  0         0  
119 7 100 100     23 if(not $lat >= 0 or not $lat <= 90) { croak "LAT arg must be between 0 and 90"; }
  2         204  
120              
121             # Return the missing value if the data arguments are missing or invalid
122 5 100 33     51 if(
      33        
      66        
      100        
      66        
123             not defined($temperature) or
124             not defined($tei) or
125             not looks_like_number($temperature) or
126             not looks_like_number($tei) or
127             $temperature == $missing_val or
128             $tei == $missing_val or
129             not defined($temperature <=> 0) or
130             not defined($tei <=> 0)
131 2         8 ) { return $missing_val; }
132              
133             # Calculate the PET!
134              
135 3 100 66     15 if($temperature <= 0) { return 0; }
  1 100       3  
136              
137             elsif($temperature > 0 and $temperature < 26.5) {
138             # Calculate the hours of sunlight
139 1         2 my $lat_radians = (pi/180)*$lat;
140 1         51 my $solar_declination = 0.409*sin((2*pi/365)*$yday - 1.39);
141 1         9 my $sunset_angle = acos(-tan($lat_radians)*tan($solar_declination));
142 1         46 my $daylength = (24/pi)*$sunset_angle;
143             # Calculate alpha term
144 1         6 my $alpha = (6.75e-7)*($tei**3) - (7.71e-5)*($tei**2) + 0.01792*$tei + 0.49239;
145             # Apply the original Thornthwaite Equation
146 1         3 my $pet_gross = 16*((10*$temperature)/($tei))**$alpha;
147 1         5 return $pet_gross*($daylength/12)*($ndays/30);
148             }
149              
150             else {
151             # Apply the hot temperature formula described in Huang et al. 1996
152 1         6 return (-415.85 + 32.25*$temperature - 0.43*($temperature**2))*($ndays/30);
153             }
154              
155             }
156              
157             =head2 tei_thornthwaite
158              
159             my $tei = tei_thornthwaite($jan,$feb,$mar,$apr,$may,$jun,$jul,$aug,$sep,$oct,$nov,$dec);
160              
161             Calculates and returns the temperature efficiency index (TEI, sometimes called the
162             Thornthwaite heat index) based on a monthly average temperature climatology for a given
163             location. Since the equation involves a summation of adjusted monthly temperatures, 12
164             arguments are required, consisting of the average temperature for each calendar month of
165             the year. An optional 13th argument can also be supplied to define a numeric value
166             interpreted as missing data. If not supplied, the default missing value is NaN.
167              
168             The missing value will be returned if any of the temperature values are missing,
169             undef, or invalid (e.g., non-numeric).
170              
171             =cut
172              
173             sub tei_thornthwaite {
174             # Define a NaN value for later use
175 5     5 1 11 my $inf = exp(~0 >> 1);
176 5         7 my $nan = $inf / $inf;
177              
178             # Make sure the caller supplied sufficient arguments
179 5 50 33     20 unless(@_ and @_ >= 12) { croak "Insufficient arguments supplied"; }
  0         0  
180              
181             # Get the arguments
182 5         6 my @monthly_temperatures;
183 5         6 my $invalid = 0;
184 5         10 for(my $i=0; $i<12; $i++) { push(@monthly_temperatures,shift); }
  60         80  
185 5         5 my $missing_val = $nan;
186              
187             # Replace the default missing value if the caller supplied one
188 5 100       9 if(@_) { $missing_val = shift; }
  3         4  
189              
190             # Calculate the TEI!
191 5         6 my $tei = 0;
192              
193 5         9 for(my $i=0; $i<12; $i++) {
194             # Return a missing value if any of the monthly temps are missing or invalid
195 16 100 66     75 if(
      100        
      100        
196             not defined($monthly_temperatures[$i]) or
197             not looks_like_number($monthly_temperatures[$i]) or
198             $monthly_temperatures[$i] == $missing_val or
199             not defined($monthly_temperatures[$i] <=> 0)
200 5         42 ) { return $missing_val; }
201             # Set any monthly mean temp below 0 to 0
202 11 50       18 $monthly_temperatures[$i] = $monthly_temperatures[$i] > 0 ? $monthly_temperatures[$i] : 0;
203 11         23 $tei += ($monthly_temperatures[$i]/5)**1.514;
204             }
205              
206 0           return $tei;
207             }
208              
209             =head1 INSTALLATION
210              
211             The best way to install this module is with a CPAN client, which will resolve and
212             install the dependencies:
213              
214             cpan Drought::PET::Thornthwaite
215             cpanm Drought::PET::Thornthwaite
216              
217             You can also install the module directly from the distribution directory after
218             downloading it and extracting the files, which will also install the dependencies:
219              
220             cpan .
221             cpanm .
222              
223             If you want to install the module manually do the following in the distribution
224             directory:
225              
226             perl Makefile.PL
227             make
228             make test
229             make install
230              
231             =head1 SUPPORT AND DOCUMENTATION
232              
233             After installing, you can find documentation for this module with the
234             perldoc command.
235              
236             perldoc Drought::PET::Thornthwaite
237              
238             You can also look for information at:
239              
240             =over 4
241              
242             =item * RT: CPAN's request tracker (report bugs here)
243              
244             L
245              
246             =item * CPAN Ratings
247              
248             L
249              
250             =item * Search CPAN
251              
252             L
253              
254             =back
255              
256             =head1 BUGS
257              
258             Please report any bugs or feature requests to C, or through
259             the web interface at L. I will be notified, and then you'll
260             automatically be notified of progress on your bug as I make changes.
261              
262             =head1 AUTHOR
263              
264             Adam Allgood
265              
266             =head1 REFERENCES
267              
268             =over 4
269              
270             =item * Huang, J, H. M. Van Den Dool, and K. P. Georgarakos, 1996: Analysis of Model-Calculated Soil Moisture over the United States (1931-1993) and Applications to Long-Range Temperature Forecasts. I, B<9> 1350-1362.
271              
272             =item * Thornthwaite, C. W., 1948: An approach toward a rational classification of climate. I, B<38> 55-94.
273              
274             =back
275              
276             =head1 LICENSE AND COPYRIGHT
277              
278             This software is copyright (c) 2024 by Adam Allgood.
279              
280             This is free software; you can redistribute it and/or modify it under
281             the same terms as the Perl 5 programming language system itself.
282              
283             =cut
284              
285             1; # End of Drought::PET::Thornthwaite
286