Package: penmon
NAMEpenmon - Implementation of weather station class in Python that supports
Penman-Monteith ETo Equation.INSTALL$ pip install penmonUSAGEimport penmon as pm

### create a station class with known location and elevation
station = pm.Station(latitude=41.42, altitude=109)
station.anemometer_height = 10

### getting a day instance for August 16th
day = station.day_entry(238, 
		temp_min = 19.5, 
		temp_max = 25.6, 
		wind_speed = 2.5,
		humidity_mean = 65,
		radiation_s =2 5.6
		)
print("ETo for this day is", day.eto() )DESCRIPTIONFull implementation ofPenman-MonteithEToequation based onUAN-FAO Irrigation and Drainage Paper 56.Penman-Monteithequation is used to calculate reference crop evapotranspirationETofor a given location using available climate data. This method provides many ways of estimating
missing climate data.Following are the least data required to estimate ETo (But the more data you provide
the better the accuracy gets):* latitude
* elevation
* date
* daily minimum temperature
* daily maximum temperatureIt can do this by making certain assumptions about the climate. These assumptions
can be fine-tuned.Climate-class is responsible for setting these assumptions.
We'll talk more about it later (see Climate class below)OVERVIEWTo calculateETo, including intermediate atmospheric data you first need to
define an instance of aStationwith a knownlatitudeandaltitude. Then
you request the station to create an instance of aDayEntry, which represents
a single day with a known date. We then set whatever data we know about that particular
day, and ask thedayto calculate information that we do not know, includingETo.BEWAREThis is pre-release version of the library and intended for review only. API of
the class may change in future releases. Do not assume backwards compatability
in future releases. ConsultsCHANGESfile before upgrading!Station CLASSCREATE A STATION instanceimport penmon as pm

station = pm.Station(latitude=41.42, altitude=109)latitudemust be a float between -90 and 90.altitudumust be a positive
integer. These arguments values are of utmost importance. Please make sure these
data are as accurate as you can get them be.latitudeis used to calculatesunset hour angle(Eq. 25) andExtraterrestrial radiation(Eq. 21),
which in turn, along with date, is used to calculate solar radiation!altitudeis used to calculateatmospheric pressure, which in turn is used to
calculatepsychrometric constant, which in turn is used to calculatevapour pressure,
which is used to calculatenet longwave radiation. As you can see, these
very innocent looking numbers are pretty much backbone of the whole equation.
Show them respect!ANEMOMETER HEIGHTAbovestationassumes its anemometer height is 2m above ground surface.
If it's not, you can set the height as:station.anemometer_height = 10Now all the wind_speed information is assumed to be wind speed at 10m
height. This is important information, since ETo is calculated with speed
close the crop surface, which is 2m. Library uses logarithmic algorithm
to convert the data accordingly. Again, important setting! Shoud you wish to
access calculated wind speed at 2m usewind_speed_2m()method:day.wind_speed=2.0;
u2m = day.wind_speed_2m();In the above exampleu2mreturns2.0if the anemometer was set to 2 meters.
If it was set to 10m, it returns 1.5. If it was set to 50 meters, it reads 1.2 m/s.STATION CLIMATEStation also makes certain assumptions about its climate. You can set this
by creating a new climate instance (seeClimateclass) and set is as:humid_climate = pm.Climate().humid().coastal().strong_winds()
arid_climate = pm.Climate().arid().interior().moderare_winds()
station.climate = humid_climateBy default it assumes we are inarid, interior location, with moderate winds.
If it's in arid climate, it makes certain assumption about the dew point temperature.
This temperature will be used to calculate relative humidity if humidity data
is missing. It deduces dew_point temperature by looking at the temp_min of the record.
In particulare, for arid location it substracts 2.0 degrees from temp_min. In
humid location it treats temp_min as temp_dew. In the following example
we set dew_point temperature 4.0 below temp_minclimate=pm.Climate() 
# above is the same as saying:
climate=pm.Climate().arid().interior().moderate_winds()

climate.dew_point_difference=4.0

station.climate=climate;

# from now on if humidity data is missing it substtracts 4.0 degrees
from temp_min to take a guess at temp_dewREFERENCE CROPIt assumes it will be calculating ETo for a refernce crop. According
to the original paper reference crop is assumed to be grass of 0.12m height,
aerodynamic resistance of 70 and albedo of 0.23. It you wish to calculate ETo
for a different reference crop you may do so as:different_crop = pm.Crop(resistance_a=208, albedo=0.23, height=0.12)
station.ref_crop = different_cropITERATE THROUGH INDIVIDUAL ENTRIESBased on the above example this station has no climate data available at this
moment. But if it were you would've been able to iterate through these records
like following:for a_day in station.days:
	# do stuff with a_dayDayEntry classOnce we have station data available we work with a day at a time. We first
need to get a single day, identified by a day number:day = station.day_entry(238)dayis an instance ofDayEntryclass.238above representsAugust 26th- it is 238th day of the year. Day number can only be an integer
in 1-366 range. It also supports a date string:day = station.day_entry("2020-08-26")
day.day_number # returns 238If you have to pass a date string that has a different template than "%Y-%m-%d",
you can pass your template string to the method as follows. Above example assumes
following defaultdate_templatevalue:day = station.day_entry("2020-08-26", date_template="%Y-%m-%d")To learn more about date template placeholders refer tostrptime()manual either
fromdatetimemodule, or by refering to your system'sstrptimemanual
(available in all linux/unix machines).Based on this day number alone library is able to calculate inverse
of the relative Sun-Earth distance (Eq. 23), solar declination (Eq. 24),
which is used to calculate possible daylight hours for that particular day for
that particular latitude. It's amazing how much information we can deduce based
on this single number!Once you havedayinstance now all the fun begins!Everyday has following attributes:- day_number (whatever we passed to the constructor)
- station (set automatically by station)

# following are required to be set:
- temp_min
- temp_max

# following are better to be set:
- temp_dew
- wind_speed
- humidity_min
- humidity_max
- radiation_s	- Solar Radiation

# following are optional, if above attributes are known
- radiation_a
- temp_mean
- temp_dry     - dry bulb temperature
- temp_wet     - wet bulb temperatureINTERMEDIATE CALCULATIONSBefore setting any of the above attributes following information is available for
us. These information do not use any recorded data, but only uses
mostly astronomical calculations.day.atmospheric_pressure()Returns atmostpheric pressure in kPa for station elevation. For the above example
altitude (109m) returns 100.0kpa. Atmospheric pressure is also available throughstation.atmospheric_pressure()call. In fact,DayEntry.atmospheric_pressure()is just an alias to it.Value returned is a pressure inkPa. If you wish to convert it to mercury
scale multiply it by7.50to getmm Hg, or0.295to getin Hg.day.latent_heat_of_vaporization()Returns 2.45day.specific_heat()Returns 0.001013day.psychrometric_constant()For the elevation in this example returns 0.0665.day.saturation_vapour_pressure(T)GivenT- temperature returns saturation vapour pressure. For example for T=25.0
returns 3.168day.slope_of_saturation_vapour_pressure(T)GivenT- temperature returns slope of saturation vapour pressure curve. For example
for T=25.0 returns 0.188684day.relative_sun_distance()Returns inverse of relative Earth-Sun distance. For this example returns 0.981day.solar_declination()For this example returns 0.172. This is angle in radians. To convert this to
degrees multiply by 180 and devide tomath.pi(). For example, 0.172 rad is the
same as 9.8 degrees. So Sun's declination was 9.8 degrees north relative to equatorial
plane. If the value were negative it would've meant the Sun is declined to the
south of the equatorial plane.day.sunset_hour_angle()For this example, returns 1.725. Seeday.solar_declinationto convert this
to degrees.day.R_a()Returns extraterrestrial radiation in MJ/m2/day. For this given example returns 40.3.day.R_a_in_mm()The same as above but in mm. 16.4 for this example.day.daylight_hours()Possible daylight hours for this day.day.solar_radiation()Depends on the value ofday.sunshine_hoursattribute. Returns solar radiation
in MJ/m2/dayday.solar_radiation_in_mm()Same as above, but returns in mm.day.R_so()Calculates Clear-Sky solar radiation.day.R_ns()Given hours of direct sunlight (inday.sunshine_hoursattribute) calculates
net solar radiation (or shortwave radiation) Takes into account albedo of the crop.day.soil_heat_flux()Returns 0.00 for daily calculations.RECORDED DATA MANIPULATIONSAt this point we still cannot calculateETo. some vital information are still
missing. Let's record some data for this day:day.temp_min = 19.5
day.tmep_max = 25.6Now we can calculateETofor the first time:day.eto() # returns 3.2mmTo calculate ETo with the current recorded data the library did lots of assumptions
and calculations based on empirical data. Let's help it further by recording
other important information:day.temp_dew = 15.0
day.eto() # returns 3.58mm

day.wind_speed=2.50
day.eto()# returns 3.82mmRecording solar radiation gets us the most accurate ETo:day.radiation_s = 25.0
day.eto() # returns 5.04mTODO AND ISSUESSee:Issues at github.comSEE ALSOlibpenmon- port of the current module into C++. Seegithub.com/sherzodr/libpenmonAUTHORSherzod Ruzmetovsherzodr@gmail.com