From b35e0d999c0e4d45c2159e5762cb995bd56946b8 Mon Sep 17 00:00:00 2001 From: sbiri <sbiri@noc.ac.uk> Date: Tue, 21 Jul 2020 12:36:43 +0100 Subject: [PATCH] get_init checks input values and sets defaults hum_subs contains all subroutines related to humidity calculations util_subs contains all utility subroutines --- get_init.py | 131 +++++++++++++++ hum_subs.py | 453 +++++++++++++++++++++++++++++++++++++++++++++++++++ util_subs.py | 96 +++++++++++ 3 files changed, 680 insertions(+) create mode 100644 get_init.py create mode 100644 hum_subs.py create mode 100644 util_subs.py diff --git a/get_init.py b/get_init.py new file mode 100644 index 0000000..26699e3 --- /dev/null +++ b/get_init.py @@ -0,0 +1,131 @@ +import numpy as np +import sys + +def get_init(spd, T, SST, lat, P, Rl, Rs, cskin, gust, L, tol, meth, qmeth): + """ + Checks initial input values and sets defaults if needed + + Parameters + ---------- + spd : float + relative wind speed in m/s (is assumed as magnitude difference + between wind and surface current vectors) + T : float + air temperature in K + SST : float + sea surface temperature in K + lat : float + latitude (deg), default 45deg + P : float + air pressure (hPa), default 1013hPa + Rl : float + downward longwave radiation (W/m^2) + Rs : float + downward shortwave radiation (W/m^2) + cskin : int + 0 switch cool skin adjustment off, else 1 + default is 1 + gust : int + 3x1 [x, beta, zi] x=1 to include the effect of gustiness, else 0 + beta gustiness parameter, beta=1 for UA, beta=1.2 for COARE + zi PBL height (m) 600 for COARE, 1000 for UA and ERA5, 800 default + default for COARE [1, 1.2, 600] + default for UA, ERA5 [1, 1, 1000] + default else [1, 1.2, 800] + L : int + Monin-Obukhov length definition options + 0 : default for S80, S88, LP82, YT96 and LY04 + 1 : following UA (Zeng et al., 1998), default for UA + 2 : following ERA5 (IFS Documentation cy46r1), default for ERA5 + 3 : COARE3.5 (Edson et al., 2013), default for C30, C35 and C40 + tol : float + 4x1 or 7x1 [option, lim1-3 or lim1-6] + option : 'flux' to set tolerance limits for fluxes only lim1-3 + option : 'ref' to set tolerance limits for height adjustment lim-1-3 + option : 'all' to set tolerance limits for both fluxes and height + adjustment lim1-6 ['all', 0.01, 0.01, 5e-05, 0.01, 1, 1] + meth : str + "S80","S88","LP82","YT96","UA","LY04","C30","C35","C40","ERA5" + qmeth : str + is the saturation evaporation method to use amongst + "HylandWexler","Hardy","Preining","Wexler","GoffGratch","CIMO", + "MagnusTetens","Buck","Buck2","WMO","WMO2000","Sonntag","Bolton", + "IAPWS","MurphyKoop"] + default is Buck2 + + Returns + ------- + lat : float + latitude + P : float + air pressure (hPa) + Rl : float + downward longwave radiation (W/m^2) + Rs : float + downward shortwave radiation (W/m^2) + cskin : int + cool skin adjustment switch + gust : int + gustiness switch + tol : float + tolerance limits + L : int + MO length switch + + """ + if ((type(spd) != np.ndarray) or (type(T) != np.ndarray) or + (type(SST) != np.ndarray)): + sys.exit("input type of spd, T and SST should be numpy.ndarray") + # if input values are nan break + if meth not in ["S80", "S88", "LP82", "YT96", "UA", "LY04", "C30", "C35", + "C40","ERA5"]: + sys.exit("unknown method") + if qmeth not in ["HylandWexler", "Hardy", "Preining", "Wexler", "CIMO", + "GoffGratch", "MagnusTetens", "Buck", "Buck2", "WMO", + "WMO2000", "Sonntag", "Bolton", "IAPWS", "MurphyKoop"]: + sys.exit("unknown q-method") + if (np.all(np.isnan(spd)) or np.all(np.isnan(T)) or np.all(np.isnan(SST))): + sys.exit("input wind, T or SST is empty") + if (np.all(lat == None)): # set latitude to 45deg if empty + lat = 45*np.ones(spd.shape) + elif ((np.all(lat != None)) and (np.size(lat) == 1)): + lat = np.ones(spd.shape)*np.copy(lat) + if ((np.all(P == None)) or np.all(np.isnan(P))): + P = np.ones(spd.shape)*1013 + elif (((np.all(P != None)) or np.all(~np.isnan(P))) and np.size(P) == 1): + P = np.ones(spd.shape)*np.copy(P) + if (np.all(Rl == None) or np.all(np.isnan(Rl))): + Rl = np.ones(spd.shape)*370 # set to default for COARE3.5 + if (np.all(Rs == None) or np.all(np.isnan(Rs))): + Rs = np.ones(spd.shape)*150 # set to default for COARE3.5 + if ((cskin == None) and (meth == "S80" or meth == "S88" or meth == "LP82" + or meth == "YT96" or meth == "LY04")): + cskin = 0 + elif ((cskin == None) and (meth == "UA" or meth == "C30" + or meth == "C35" or meth == "C40" + or meth == "ERA5")): + cskin = 1 + if ((gust == None) and (meth == "C30" or meth == "C35" or meth == "C40")): + gust = [1, 1.2, 600] + elif ((gust == None) and (meth == "UA" or meth == "ERA5")): + gust = [1, 1, 1000] + elif (gust == None): + gust = [1, 1.2, 800] + elif (np.size(gust) < 3): + sys.exit("gust input must be a 3x1 array") + if (L not in [None, 0, 1, 2, 3]): + sys.exit("L input must be either None, 0, 1, 2 or 3") + if ((L == None) and (meth == "S80" or meth == "S88" or meth == "LP82" + or meth == "YT96" or meth == "LY04")): + L = 0 + elif ((L == None) and (meth == "UA")): + L = 1 + elif ((L == None) and (meth == "ERA5")): + L = 2 + elif ((L == None) and (meth == "C30" or meth == "C35" or meth == "C40")): + L = 3 + if (tol == None): + tol = ['flux', 0.01, 1, 1] + elif (tol[0] not in ['flux', 'ref', 'all']): + sys.exit("unknown tolerance input") + return lat, P, Rl, Rs, cskin, gust, tol, L \ No newline at end of file diff --git a/hum_subs.py b/hum_subs.py new file mode 100644 index 0000000..cb6a1b5 --- /dev/null +++ b/hum_subs.py @@ -0,0 +1,453 @@ +import numpy as np +import sys +import logging +from util_subs import (CtoK) + +def VaporPressure(temp, P, phase, meth): + """ + Calculate the saturation vapor pressure + + For temperatures above 0 deg C the vapor pressure over liquid water + is calculated. + + The optional parameter 'liquid' changes the calculation to vapor pressure + over liquid water over the entire temperature range. + + The current default fomulas are Hyland and Wexler for liquid and + Goff Gratch for ice. + + Parameters + ---------- + temp : float + Temperature in [deg C] + phase : str + 'liquid' : Calculate vapor pressure over liqiud water or + 'ice' : Calculate vapor pressure over ice + meth : str + formula to be used + MartiMauersberger : vaporpressure formula from Marti Mauersberger + MagnusTetens : vaporpressure formula from Magnus Tetens + GoffGratch : vaporpressure formula from Goff Gratch + Buck_original : vaporpressure formula from Buck (original) + Buck_manual : vaporpressure formula from the Buck manual + CIMO : vaporpressure formula recommended by CIMO + Vaisala : vaporpressure formula from Vaisala + WMO_Goff : vaporpressure formula from WMO, which should have been Goff + WMO2000 : vaporpressure formula from WMO (2000) containing a typo + Wexler : vaporpressure formula from Wexler (1976) + Sonntag : vaporpressure formula from Sonntag (1994) + Bolton : vaporpressure formula from Bolton (1980) + Fukuta : vaporpressure formula from Fukuta (2003) + HylandWexler : vaporpressure formula from Hyland and Wexler (1983) + IAPWS : vaporpressure formula from IAPWS (2002) + Preining : vaporpressure formula from Preining (2002) + MurphyKoop : vaporpressure formula from Murphy and Koop (2005) + + Returns + ------- + P : float + Saturation vapor pressure in [hPa] + + + Author + ------ + Ported to Python and modified by S. Biri from Holger Voemel's original +""" + logging.basicConfig(filename='VaporPressure.log', + format='%(asctime)s %(message)s',level=logging.info) + + Psat = np.zeros(temp.size)*np.nan + if (np.nanmin(temp) > 200): # if Ta in Kelvin convert to Celsius + temp = temp-273.16 + T = np.copy(temp)+273.16 # Most formulas use T in [K] + # Formulas using [C] use the variable temp + # Calculate saturation pressure over liquid water + if (phase == 'liquid'): + if (meth == 'HylandWexler' or meth == ''): + logging.info("Source Hyland, R. W. and A. Wexler, Formulations \ + for the Thermodynamic Properties of the saturated \ + Phases of H2O from 173.15K to 473.15K, \ + ASHRAE Trans, 89(2A), 500-519, 1983.") + Psat = np.exp(-0.58002206e4/T+0.13914993e1-0.48640239e-1*T + + 0.41764768e-4*np.power(T, 2) - + 0.14452093e-7*np.power(T, 3) + + 0.65459673e1*np.log(T))/100 + if (meth == 'Hardy'): + logging.info("Source Hardy, B., 1998, ITS-90 Formulations \ + for Vapor Pressure, Frostpoint Temperature, \ + Dewpoint Temperature, and Enhancement Factors \ + in the Range -100 to +100°C, The Proceedings of \ + the Third International Symposium on Humidity & \ + Moisture, London, England") + Psat = np.exp(-2.8365744e3/np.power(T, 2)-6.028076559e3/T + + 1.954263612e1-2.737830188e-2*T + + 1.6261698e-5*np.power(T, 2) + + 7.0229056e-10*np.power(T, 3) - + 1.8680009e-13*np.power(T, 4) + + 2.7150305*np.log(T))/100 + if (meth == 'Preining'): + logging.info("Source : Vehkamaeki, H., M. Kulmala, I. Napari, \ + K. E. J. Lehtinen, C.Timmreck, M. Noppel, and \ + A. Laaksonen (2002), J. Geophys. Res., 107, \ + doi:10.1029/2002JD002184.") + Psat = np.exp(-7235.424651/T+77.34491296+5.7113e-3*T - + 8.2*np.log(T))/100 + if (meth == 'Wexler'): + logging.info("Wexler, A., Vapor Pressure Formulation for Water \ + in Range 0 to 100 C. A Revision, Journal of Research \ + of the National Bureau of Standards - A. Physics and \ + Chemistry, September - December 1976, Vol. 80A, \ + Nos.5 and 6, 775-785") + Psat = np.exp(-0.29912729e4*np.power(T, -2) - + 0.60170128e4*np.power(T, -1) + + 0.1887643854e2-0.28354721e-1*T + + 0.17838301e-4*np.power(T, 2) - + 0.84150417e-9*np.power(T, 3) + + 0.44412543e-12*np.power(T, 4) + # This line was corrected from '-' to '+' following the original citation. (HV 20140819). The change makes only negligible difference + 2.858487*np.log(T))/100 + if ((meth == 'GoffGratch') or (meth == 'MartiMauersberger')): + # logging.info("Marti and Mauersberger don't have a vapor pressure \ + # curve over liquid. Using Goff Gratch instead; \ + # Goff Gratch formulation Source : Smithsonian \ + # Meteorological Tables, 5th edition, p. 350, 1984 \ + # From original source: Goff & Gratch (1946), p. 107) \ + # Goff Gratch formulation; Source : Smithsonian \ + # Meteorological Tables, 5th edition, p. 350, 1984 \ + # From original source: Goff and Gratch (1946), p. 107") + Ts = 373.16 # steam point temperature in K + ews = 1013.246 # saturation pressure at steam point temperature + Psat = np.power(10, -7.90298*(Ts/T-1)+5.02808*np.log10(Ts/T) - + 1.3816e-7*(np.power(10, 11.344*(1-T/Ts))-1) + + 8.1328e-3*(np.power(10, -3.49149*(Ts/T-1))-1) + + np.log10(ews)) + if (meth == 'CIMO'): + logging.info("Source: Annex 4B, Guide to Meteorological \ + Instruments and Methods of Observation, \ + WMO Publication No 8, 7th edition, Geneva, 2008. \ + (CIMO Guide)") + Psat = (6.112*np.exp(17.62*temp/(243.12+temp)) * + (1.0016+3.15e-6*P-0.074/P)) + if (meth == 'MagnusTetens'): + logging.info("Source: Murray, F. W., On the computation of \ + saturation vapor pressure, J. Appl. Meteorol., \ + 6, 203-204, 1967.") + Psat = np.power(10, 7.5*(temp)/(temp+237.5)+0.7858) + # Murray quotes this as the original formula and + Psat = 6.1078*np.exp(17.269388*temp/(temp+237.3)) + # this as the mathematical aquivalent in the form of base e. + if (meth == 'Buck'): + logging.info("Bucks vapor pressure formulation based on \ + Tetens formula. Source: Buck, A. L., \ + New equations for computing vapor pressure and \ + enhancement factor, J. Appl. Meteorol., 20, \ + 1527-1532, 1981.") + Psat = (6.1121*np.exp(17.502*temp/(240.97+temp)) * + (1.0007+(3.46e-6*P))) + if (meth == 'Buck2'): + logging.info("Bucks vapor pressure formulation based on \ + Tetens formula. Source: Buck Research, \ + Model CR-1A Hygrometer Operating Manual, Sep 2001") + Psat = (6.1121*np.exp((18.678-(temp)/234.5)*(temp)/(257.14+temp)) * + (1+1e-4*(7.2+P*(0.0320)+5.9e-6*np.power(T, 2)))) + if (meth == 'WMO'): + logging.info("Intended WMO formulation, originally published by \ + Goff (1957) incorrectly referenced by WMO technical \ + regulations, WMO-NO 49, Vol I, General \ + Meteorological Standards and Recommended Practices, \ + App. A, Corrigendum Aug 2000. and incorrectly \ + referenced by WMO technical regulations, \ + WMO-NO 49, Vol I, General Meteorological Standards \ + and Recommended Practices, App. A, 1988.") + Ts = 273.16 # triple point temperature in K + Psat = np.power(10, 10.79574*(1-Ts/T)-5.028*np.log10(T/Ts) + + 1.50475e-4*(1-np.power(10, -8.2969*(T/Ts-1))) + + 0.42873e-3*(np.power(10, -4.76955*(1-Ts/T))-1) + + 0.78614) + if (meth == 'WMO2000'): + logging.info("WMO formulation, which is very similar to Goff \ + Gratch. Source : WMO technical regulations, \ + WMO-NO 49, Vol I, General Meteorological Standards \ + and Recommended Practices, App. A, \ + Corrigendum Aug 2000.") + Ts = 273.16 # triple point temperature in K + Psat = np.power(10, 10.79574*(1-Ts/T)-5.028*np.log10(T/Ts) + + 1.50475e-4*(1-np.power(10, -8.2969*(T/Ts-1))) + + 0.42873e-3*(np.power(10, -4.76955*(1.-Ts/T))-1) + + 0.78614) + if (meth == 'Sonntag'): + logging.info("Source: Sonntag, D., Advancements in the field of \ + hygrometry, Meteorol. Z., N. F., 3, 51-66, 1994.") + Psat = np.exp(-6096.9385*np.power(T, -1)+16.635794 - + 2.711193e-2*T+1.673952e-5*np.power(T, 2) + + 2.433502*np.log(T))#*(1.0016+P*3.15e-6-0.074/P) + if (meth == 'Bolton'): + logging.info("Source: Bolton, D., The computation of equivalent \ + potential temperature, Monthly Weather Report, 108, \ + 1046-1053, 1980. equation (10)") + Psat = 6.112*np.exp(17.67*temp/(temp+243.5)) + if (meth == 'IAPWS'): + logging.info("Source: Wagner W. and A. Pruss (2002), The IAPWS \ + formulation 1995 for the thermodynamic properties \ + of ordinary water substance for general and \ + scientific use, J. Phys. Chem. Ref. Data, 31(2), \ + 387-535. This is the 'official' formulation from \ + the International Association for the Properties of \ + Water and Steam The valid range of this formulation \ + is 273.16 <= T <= 647.096 K and is based on the \ + ITS90 temperature scale.") + Tc = 647.096 # K : Temperature at the critical point + Pc = 22.064e4 # hPa : Vapor pressure at the critical point + nu = (1-T/Tc) + a1, a2, a3 = -7.85951783, 1.84408259, -11.7866497 + a4, a5, a6 = 22.6807411, -15.9618719, 1.80122502 + Psat = (Pc*np.exp(Tc/T*(a1*nu+a2*np.power(nu, 1.5) + + a3*np.power(nu, 3)+a4*np.power(nu, 3.5) + + a5*np.power(nu, 4)+ a6*np.power(nu, 7.5)))) + if (meth == 'MurphyKoop'): + logging.info("Source : Murphy and Koop, Review of the vapour \ + pressure of ice and supercooled water for \ + atmospheric applications, Q. J. R. Meteorol. \ + Soc (2005), 131, pp. 1539-1565.") + Psat = np.exp(54.842763-6763.22/T-4.210*np.log(T)+0.000367*T + + np.tanh(0.0415*(T-218.8))*(53.878-1331.22/T - + 9.44523*np.log(T)+0.014025*T))/100 + # Calculate saturation pressure over ice ---------------------------------- + elif (phase == 'ice'): + logging.info("Default uses Goff Gratch over ice. There is little \ + ambiguity in the ice saturation curve. Goff Gratch \ + is widely used.") + if (meth == 'MartiMauersberger'): + logging.info("Source : Marti, J. and K Mauersberger, A survey and \ + new measurements of ice vapor pressure at \ + temperatures between 170 and 250 K, GRL 20, \ + 363-366, 1993.") + Psat = np.power(10, -2663.5/T+12.537)/100 + if (meth == 'HylandWexler'): + logging.info("Source Hyland, R. W. and A. Wexler, Formulations \ + for the Thermodynamic Properties of the saturated \ + Phases of H2O from 173.15K to 473.15K, ASHRAE Trans,\ + 89(2A), 500-519, 1983.") + Psat = np.exp(-0.56745359e4/T+0.63925247e1-0.96778430e-2*T + + 0.62215701e-6*np.power(T, 2) + + 0.20747825e-8*np.power(T, 3) - + 0.9484024e-12*np.power(T, 4) + + 0.41635019e1*np.log(T))/100 + if (meth == 'Wexler'): + logging.info("Wexler, A., Vapor pressure formulation for ice, \ + Journal of Research of the National Bureau of \ + Standards-A. 81A, 5-20, 1977.") + Psat = np.exp(-0.58653696e4*np.power(T, -1)+0.22241033e2 + + 0.13749042e-1*T-0.34031775e-4*np.power(T, 2) + + 0.26967687e-7*np.power(T, 3) + + 0.6918651*np.log(T))/100 + if (meth == 'Hardy'): + logging.info("Source Hardy, B., 1998, ITS-90 Formulations for \ + Vapor Pressure, Frostpoint Temperature, Dewpoint \ + Temperature, and Enhancement Factors in the Range \ + -100 to +100°C, The Proceedings of the Third \ + International Symposium on Humidity & Moisture, \ + London, England. These coefficients are updated to \ + ITS90 based on the work by Bob Hardy at Thunder \ + Scientific: http://www.thunderscientific.com/\ + tech_info/reflibrary/its90formulas.pdf \ + The difference to the older ITS68 coefficients used \ + by Wexler is academic.") + Psat = np.exp(-0.58666426e4*np.power(T, -1)+0.2232870244e2 + + 0.139387003e-1*T-0.34262402e-4*np.power(T, 2) + + 0.27040955e-7*np.power(T, 3) + + 0.67063522e-1*np.log(T))/100 + if (meth == 'GoffGratch' or meth == '' or meth == 'IAPWS'): + logging.info("IAPWS does not provide a vapor pressure formulation \ + over ice use Goff Gratch instead.\ + Source : Smithsonian Meteorological Tables, \ + 5th edition, p. 350, 1984") + ei0 = 6.1071 # mbar + T0 = 273.16 # triple point in K + Psat = np.power(10, -9.09718*(T0/T-1)-3.56654*np.log10(T0/T) + + 0.876793*(1-T/T0)+np.log10(ei0)) + if (meth == 'MagnusTetens'): + logging.info("Source: Murray, F. W., On the computation of \ + saturation vapor pressure, J. Appl. Meteorol., 6, \ + 203-204, 1967.") + Psat = np.power(10, 9.5*temp/(265.5+temp)+0.7858) + # Murray quotes this as the original formula and + Psat = 6.1078*np.exp(21.8745584*(T-273.16)/(T-7.66)) + # this as the mathematical aquivalent in the form of base e. + if (meth == 'Buck'): + logging.info("Bucks vapor pressure formulation based on Tetens \ + formula. Source: Buck, A. L., New equations for \ + computing vapor pressure and enhancement factor, \ + J. Appl. Meteorol., 20, 1527-1532, 1981.") + Psat = (6.1115*np.exp(22.452*temp/(272.55+temp)) * + (1.0003+(4.18e-6*P))) + if (meth == 'Buck2'): + logging.info("Bucks vapor pressure formulation based on Tetens \ + formula. Source: Buck Research, Model CR-1A \ + Hygrometer Operating Manual, Sep 2001") + Psat = (6.1115*np.exp((23.036-temp/333.7)*temp/(279.82+temp)) * + (1+1e-4*(2.2+P*(0.0383+6.4e-6*np.power(T, 2))))) + if (meth == 'CIMO'): + logging.info("Source: Annex 4B, Guide to Meteorological \ + Instruments and Methods of Observation, \ + WMO Publication No 8, 7th edition, Geneva, 2008. \ + (CIMO Guide)") + Psat = (6.112*np.exp(22.46*temp/(272.62+temp)) * + (1.0016+3.15e-6*P-0.074/P)) + if (meth == 'WMO' or meth == 'WMO2000'): + logging.info("There is no typo issue in the WMO formulations for \ + ice. WMO formulation, which is very similar to Goff \ + Gratch. Source : WMO technical regulations, \ + WMO-NO 49, Vol I, General Meteorological Standards \ + and Recommended Practices, Aug 2000, App. A.") + T0 = 273.16 # triple point temperature in K + Psat = np.power(10, -9.09685*(T0/T-1)-3.56654*np.log10(T0/T) + + 0.87682*(1-T/T0)+0.78614) + if (meth == 'Sonntag'): + logging.info("Source: Sonntag, D., Advancements in the field of \ + hygrometry, Meteorol. Z., N. F., 3, 51-66, 1994.") + Psat = np.exp(-6024.5282*np.power(T, -1)+24.721994+1.0613868e-2*T - + 1.3198825e-5*np.power(T, 2)-0.49382577*np.log(T)) + if (meth == 'MurphyKoop'): + logging.info("Source : Murphy and Koop, Review of the vapour \ + pressure of ice and supercooled water for \ + atmospheric applications, Q. J. R. Meteorol. Soc \ + (2005), 131, pp. 1539-1565.") + Psat = np.exp(9.550426-5723.265/T+3.53068*np.log(T) - + 0.00728332*T)/100 + if (meth == 'McIDAS'): + logging.info("Source : Unknown, Received from Xin Jin \ + <xjin@ssec.wisc.edu>") + A0, A1, A2 = 0.7859063157, 0.3579242320, -0.1292820828 + A3, A4, A5 = 0.5937519208, 0.4482949133, 0.2176664827 + E = A0+temp*(A1+temp*(A2+temp*(A3+temp*(A4+temp*A5)))) + Psat = np.power(10, E) + + s = np.where(temp > 0) + if (s.size[0] >= 1): + logging.info("Independent of the formula used for ice, use \ + Hyland Wexler (water) for temperatures above freezing\ + (see above). Source Hyland, R. W. and A. Wexler, \ + Formulations for the Thermodynamic Properties of the \ + saturated Phases of H2O from 173.15K to 473.15K, \ + ASHRAE Trans, 89(2A), 500-519, 1983.") + Psat_w = np.exp(-0.58002206e4/T+0.13914993e1-0.48640239e-1*T + + 0.41764768e-4*np.power(T, 2) - + 0.14452093e-7*np.power(T, 3) + + 0.65459673e1*np.log(T))/100 + Psat[s] = Psat_w[s] + + return Psat +#----------------------------------------------------------------------------- + + +def qsat_sea(T, P, qmeth): + """ Computes surface saturation specific humidity (g/kg) + + Parameters + ---------- + T : float + temperature ($^\\circ$\\,C) + P : float + pressure (mb) + qmeth : str + method to calculate vapor pressure + + Returns + ------- + qs : float + """ + T = np.asarray(T) + if (np.nanmin(T) > 200): # if Ta in Kelvin convert to Celsius + T = T-CtoK + ex = VaporPressure(T, P, 'liquid', qmeth) + es = 0.98*ex # reduction at sea surface + qs = 622*es/(P-0.378*es) + return qs +#----------------------------------------------------------------------------- + + +def qsat_air(T, P, rh, qmeth): + """ Computes saturation specific humidity (g/kg) as in C35 + + Parameters + ---------- + T : float + temperature ($^\circ$\,C) + P : float + pressure (mb) + rh : float + relative humidity (%) + qmeth : str + method to calculate vapor pressure + + Returns + ------- + q : float + em : float + """ + T = np.asarray(T) + if (np.nanmin(T) > 200): # if Ta in Kelvin convert to Celsius + T = T-CtoK + es = VaporPressure(T, P, 'liquid', qmeth) + em = 0.01*rh*es + q = 622*em/(P-0.378*em) + return q +#------------------------------------------------------------------------------ + + +def get_hum(hum, T, sst, P, qmeth): + """ + Get specific humidity output + + Parameters + ---------- + hum : array + humidity input switch 2x1 [x, values] default is relative humidity + x='rh' : relative humidity in % + x='q' : specific humidity (g/kg) + x='Td' : dew point temperature (K) + T : float + air temperature in K + sst : float + sea surface temperature in K + P : float + air pressure at sea level in hPa + qmeth : str + method to calculate specific humidity from vapor pressure + + Returns + ------- + qair : float + specific humidity of air + qsea : float + specific humidity over sea surface + + """ + if (hum == None): + RH = np.ones(sst.shape)*80 + qsea = qsat_sea(sst, P, qmeth)/1000 # surface water q (g/kg) + qair = qsat_air(T, P, RH, qmeth)/1000 # q of air (g/kg) + elif (hum[0] not in ['rh', 'q', 'Td']): + sys.exit("unknown humidity input") + qair, qsea = np.nan, np.nan + elif (hum[0] == 'rh'): + RH = hum[1] + if (np.all(RH < 1)): + sys.exit("input relative humidity units should be \%") + qair, qsea = np.nan, np.nan + qsea = qsat_sea(sst, P, qmeth)/1000 # surface water q (g/kg) + qair = qsat_air(T, P, RH, qmeth)/1000 # q of air (g/kg) + elif (hum[0] == 'q'): + qair = hum[1] + qsea = qsat_sea(sst, P, qmeth)/1000 # surface water q (g/kg) + elif (hum[0] == 'Td'): + Td = hum[1] # dew point temperature (K) + Td = np.where(Td < 200, np.copy(Td)+CtoK, np.copy(Td)) + T = np.where(T < 200, np.copy(T)+CtoK, np.copy(T)) + esd = 611.21*np.exp(17.502*((Td-273.16)/(Td-32.19))) + es = 611.21*np.exp(17.502*((T-273.16)/(T-32.19))) + RH = 100*esd/es + qair = qsat_air(T, P, RH, qmeth)/1000 # q of air (g/kg) + qsea = qsat_sea(sst, P, qmeth)/1000 # surface water q (g/kg) + return qair, qsea +#------------------------------------------------------------------------------ \ No newline at end of file diff --git a/util_subs.py b/util_subs.py new file mode 100644 index 0000000..19b44f2 --- /dev/null +++ b/util_subs.py @@ -0,0 +1,96 @@ +import numpy as np + +CtoK = 273.16 # 273.15 +""" Conversion factor for $^\circ\,$C to K """ + +kappa = 0.4 # NOTE: 0.41 +""" von Karman's constant """ +#------------------------------------------------------------------------------ + + +def get_heights(h, dim_len): + """ Reads input heights for velocity, temperature and humidity + + Parameters + ---------- + h : float + input heights (m) + dim_len : int + length dimension + + Returns + ------- + hh : array + """ + hh = np.zeros((3, dim_len)) + if (type(h) == float or type(h) == int): + hh[0, :], hh[1, :], hh[2, :] = h, h, h + elif (len(h) == 2 and np.ndim(h) == 1): + hh[0, :], hh[1, :], hh[2, :] = h[0], h[1], h[1] + elif (len(h) == 3 and np.ndim(h) == 1): + hh[0, :], hh[1, :], hh[2, :] = h[0], h[1], h[2] + elif (len(h) == 1 and np.ndim(h) == 2): + hh = np.zeros((3, h.shape[1])) + hh[0, :], hh[1, :], hh[2, :] = h[0, :], h[0, :], h[0, :] + elif (len(h) == 2 and np.ndim(h) == 2): + hh = np.zeros((3, h.shape[1])) + hh[0, :], hh[1, :], hh[2, :] = h[0, :], h[1, :], h[1, :] + elif (len(h) == 3 and np.ndim(h) == 2): + hh = np.zeros((3, h.shape[1])) + hh = np.copy(h) + return hh +# --------------------------------------------------------------------- + + +def gc(lat, lon=None): + """ Computes gravity relative to latitude + + Parameters + ---------- + lat : float + latitude ($^\circ$) + lon : float + longitude ($^\circ$, optional) + + Returns + ------- + gc : float + gravity constant (m/s^2) + """ + gamma = 9.7803267715 + c1 = 0.0052790414 + c2 = 0.0000232718 + c3 = 0.0000001262 + c4 = 0.0000000007 + if lon is not None: + lon_m, lat_m = np.meshgrid(lon, lat) + else: + lat_m = lat + phi = lat_m*np.pi/180. + xx = np.sin(phi) + gc = (gamma*(1+c1*np.power(xx, 2)+c2*np.power(xx, 4)+c3*np.power(xx, 6) + + c4*np.power(xx, 8))) + return gc +# --------------------------------------------------------------------- + + +def visc_air(T): + """ Computes the kinematic viscosity of dry air as a function of air temp. + following Andreas (1989), CRREL Report 89-11. + + Parameters + ---------- + Ta : float + air temperature ($^\circ$\,C) + + Returns + ------- + visa : float + kinematic viscosity (m^2/s) + """ + T = np.asarray(T) + if (np.nanmin(T) > 200): # if Ta in Kelvin convert to Celsius + T = T-273.16 + visa = 1.326e-5*(1+6.542e-3*T+8.301e-6*np.power(T, 2) - + 4.84e-9*np.power(T, 3)) + return visa -- GitLab