From 2ab01484865b6743c6e7bd01e6facfeb91c635cb Mon Sep 17 00:00:00 2001
From: sbiri <sbiri@noc.ac.uk>
Date: Tue, 23 Mar 2021 12:12:26 +0000
Subject: [PATCH] - flag "u" set relative to u10n - for first three iterations
 u10n<0 is set to 0.5 to allow convergence for small negative values - added
 initial restriction for n not to be less than 5; if so it is set to 5

---
 AirSeaFluxCode.py | 107 ++++++++++++++++++++++++++++------------------
 flux_subs.py      |   2 +-
 get_init.py       |  25 +++++------
 3 files changed, 79 insertions(+), 55 deletions(-)

diff --git a/AirSeaFluxCode.py b/AirSeaFluxCode.py
index 38222e1..fae10df 100644
--- a/AirSeaFluxCode.py
+++ b/AirSeaFluxCode.py
@@ -71,8 +71,8 @@ def AirSeaFluxCode(spd, T, SST, lat=None, hum=None, P=None, hin=18, hout=10,
            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, 1e-05, 1e-3, 0.1, 0.1]
-           default is tol=['flux', 1e-3, 0.1, 0.1]
+                    adjustment lim1-6
+           default is tol=['all', 0.01, 0.01, 1e-05, 1e-3, 0.1, 0.1]
         n : int
             number of iterations (defautl = 10)
         out : int
@@ -132,17 +132,20 @@ def AirSeaFluxCode(spd, T, SST, lat=None, hum=None, P=None, hin=18, hout=10,
 
     2021 / Author S. Biri
     """
-    logging.basicConfig(filename='flux_calc.log',
+    logging.basicConfig(filename='flux_calc.log', filemode="w",
                         format='%(asctime)s %(message)s',level=logging.INFO)
     logging.captureWarnings(True)
     #  check input values and set defaults where appropriate
-    lat, hum, P, Rl, Rs, cskin, skin, wl, gust, tol, L = get_init(spd, T, SST,
-                                                                  lat, hum, P,
-                                                                  Rl, Rs,
-                                                                  cskin, skin,
-                                                                  wl, gust, L,
-                                                                  tol, meth,
-                                                                  qmeth)
+    lat, hum, P, Rl, Rs, cskin, skin, wl, gust, tol, L, n = get_init(spd, T,
+                                                                     SST, lat,
+                                                                     hum, P,
+                                                                     Rl, Rs,
+                                                                     cskin,
+                                                                     skin,
+                                                                     wl, gust,
+                                                                     L, tol, n,
+                                                                     meth,
+                                                                     qmeth)
     flag = np.ones(spd.shape, dtype="object")*"n"
     flag = np.where(np.isnan(spd+T+SST+lat+hum[1]+P+Rs), "m", flag)
     ref_ht = 10        # reference height
@@ -150,8 +153,9 @@ def AirSeaFluxCode(spd, T, SST, lat=None, hum=None, P=None, hin=18, hout=10,
     h_out = get_heights(hout, 1)       # desired height of output variables
     logging.info('method %s, inputs: lat: %s | P: %s | Rl: %s |'
                  ' Rs: %s | gust: %s | cskin: %s | L : %s', meth,
-                 np.nanmedian(lat), np.nanmedian(P), np.nanmedian(Rl),
-                 np.nanmedian(Rs), gust, cskin, L)
+                 np.nanmedian(lat), np.round(np.nanmedian(P), 2),
+                 np.round(np.nanmedian(Rl),2 ), np.round(np.nanmedian(Rs), 2),
+                 gust, cskin, L)
     #  set up/calculate temperatures and specific humidities
     th = np.where(T < 200, (np.copy(T)+CtoK) *
                   np.power(1000/P,287.1/1004.67),
@@ -164,7 +168,8 @@ def AirSeaFluxCode(spd, T, SST, lat=None, hum=None, P=None, hin=18, hout=10,
     Ta = np.where(T < 200, np.copy(T)+CtoK+tlapse*h_in[1],
                   np.copy(T)+tlapse*h_in[1])  # convert to Kelvin if needed
     logging.info('method %s and q method %s | qsea:%s, qair:%s', meth, qmeth,
-                  np.nanmedian(qsea), np.nanmedian(qair))
+                 np.round(np.nanmedian(qsea), 7),
+                 np.round(np.nanmedian(qair), 7))
     if (np.all(np.isnan(qsea)) or np.all(np.isnan(qair))):
         print("qsea and qair cannot be nan")
 
@@ -322,10 +327,13 @@ def AirSeaFluxCode(spd, T, SST, lat=None, hum=None, P=None, hin=18, hout=10,
            tkt[ind] = 0.001*np.ones(T[ind].shape)
         logging.info('method %s | dter = %s | dqer = %s | tkt = %s | Rnl = %s '
                      '| usr = %s | tsr = %s | qsr = %s', meth,
-                     np.nanmedian(dter), np.nanmedian(dqer),
-                     np.nanmedian(tkt), np.nanmedian(Rnl),
-                     np.nanmedian(usr), np.nanmedian(tsr),
-                     np.nanmedian(qsr))
+                     np.round(np.nanmedian(dter), 2),
+                     np.round(np.nanmedian(dqer), 7),
+                     np.round(np.nanmedian(tkt), 2),
+                     np.round(np.nanmedian(Rnl), 2),
+                     np.round(np.nanmedian(usr), 3),
+                     np.round(np.nanmedian(tsr), 4),
+                     np.round(np.nanmedian(qsr), 7))
         Rnl[ind] = 0.97*(5.67e-8*np.power(sst[ind] -
                           dter[ind]*cskin, 4)-Rl[ind])
         t10n[ind] = (Ta[ind] -
@@ -362,6 +370,8 @@ def AirSeaFluxCode(spd, T, SST, lat=None, hum=None, P=None, hin=18, hout=10,
             wind[ind] = np.copy(spd[ind])
         u10n[ind] = wind[ind]-usr[ind]/kappa*(np.log(h_in[0, ind]/10) -
                                               psim[ind])
+        if (it < 4): # make sure you allow small negative values convergence
+            u10n = np.where(u10n < 0, 0.5, u10n)
         flag = np.where((u10n < 0) & (flag == "n"), "u",
                         np.where((u10n < 0) & (flag != "u"),
                                  flag+[","]+["u"], flag))
@@ -398,7 +408,7 @@ def AirSeaFluxCode(spd, T, SST, lat=None, hum=None, P=None, hin=18, hout=10,
     itera[ind] = -1
     # itera = np.where(itera > n, -1, itera)
     logging.info('method %s | # of iterations:%s', meth, it)
-    logging.info('method %s | # of points that did not converge :%s', meth,
+    logging.info('method %s | # of points that did not converge :%s \n', meth,
                   ind[0].size)
     # calculate output parameters
     rho = (0.34838*P)/(tv10n)
@@ -428,30 +438,43 @@ def AirSeaFluxCode(spd, T, SST, lat=None, hum=None, P=None, hin=18, hout=10,
                     np.where((q10n < 0) & (flag != "n"), flag+[","]+["q"],
                              flag))
     flag = np.where(((Rb < -0.5) | (Rb > 0.2)) & (flag == "n"), "l",
-                    np.where(((Rb < -0.5) | (Rb > 0.2)) & (flag != "n"),
-                             flag+[","]+["l"], flag))
+                    np.where(((Rb < -0.5) | (Rb > 0.2)) &
+                             ((flag != "n") & (("u" in flag) == False) &
+                              (("q" in flag) == False)), flag+[","]+["l"], flag))
     flag = np.where((itera == -1) & (flag == "n"), "i",
-                    np.where((itera == -1) & (flag != "n"), flag+[","]+["i"],
-                             flag))
+                    np.where((itera == -1) &
+                             ((flag != "n") & (("u" in flag) == False) &
+                              (("q" in flag) == False)),
+                             flag+[","]+["i"], flag))
     if (meth == "S80"):
-        flag = np.where(((spd < 6) | (spd > 22)) & (flag == "n"), "o",
-                        np.where(((spd < 6) | (spd > 22)) & (flag != "n"),
+        flag = np.where(((u10n < 6) | (u10n > 22)) & (flag == "n"), "o",
+                        np.where(((u10n < 6) | (u10n > 22)) &
+                                 ((flag != "n") & (("u" in flag) == False) &
+                                  (("q" in flag) == False)),
                                  flag+[","]+["o"], flag))
     elif (meth == "LP82"):
-        flag = np.where(((spd < 3) | (spd > 25)) & (flag == "n"), "o",
-                        np.where(((spd < 3) | (spd > 25)) & (flag != "n"),
+        flag = np.where(((u10n < 3) | (u10n > 25)) & (flag == "n"), "o",
+                        np.where(((u10n < 3) | (u10n > 25)) &
+                                 ((flag != "n") & (("u" in flag) == False) &
+                                  (("q" in flag) == False)),
                                  flag+[","]+["o"], flag))
     elif (meth == "YT96"):
-        flag = np.where(((spd < 3) | (spd > 26)) & (flag == "n"), "o",
-                        np.where(((spd < 3) | (spd > 26)) & (flag != "n"),
+        flag = np.where(((u10n < 3) | (u10n > 26)) & (flag == "n"), "o",
+                        np.where(((u10n < 3) | (u10n > 26)) &
+                                 ((flag != "n") & (("u" in flag) == False) &
+                                  (("q" in flag) == False)),
                                  flag+[","]+["o"], flag))
     elif (meth == "UA"):
-        flag = np.where((spd > 18) & (flag == "n"), "o",
-                        np.where((spd > 18) & (flag != "n"),
+        flag = np.where((u10n > 18) & (flag == "n"), "o",
+                        np.where((u10n > 18) &
+                                 ((flag != "n") & (("u" in flag) == False) &
+                                  (("q" in flag) == False)),
                                  flag+[","]+["o"], flag))
     elif (meth == "LY04"):
-        flag = np.where((spd < 0.5) & (flag == "n"), "o",
-                        np.where((spd < 0.5) & (flag != "n"),
+        flag = np.where((u10n < 0.5) & (flag == "n"), "o",
+                        np.where((u10n < 0.5) &
+                                 ((flag != "n") & (("u" in flag) == False) &
+                                  (("q" in flag) == False)),
                                  flag+[","]+["o"], flag))
     if (hum == None):
         rh = np.ones(sst.shape)*80
@@ -462,8 +485,8 @@ def AirSeaFluxCode(spd, T, SST, lat=None, hum=None, P=None, hin=18, hout=10,
         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)))
+        esd = 611.21*np.exp(17.502*((Td-CtoK)/(Td-32.19)))
+        es = 611.21*np.exp(17.502*((T-CtoK)/(T-32.19)))
         rh = 100*esd/es
         rh = np.where(rh > 100, np.nan, rh)
 
@@ -511,17 +534,17 @@ def AirSeaFluxCode(spd, T, SST, lat=None, hum=None, P=None, hin=18, hout=10,
     if (out == 0):
         res[:, ind] = np.nan
     # set missing values where data have non acceptable values
-    res = np.asarray([np.where((spd < 0) | (q10n < 0), np.nan,
+    res = np.asarray([np.where(q10n < 0, np.nan,
                                res[i][:]) for i in range(39)])
     # output with pandas
     resAll = pd.DataFrame(data=res.T, index=range(len(spd)),
-                        columns=["tau", "shf", "lhf", "L", "cd", "cdn", "ct",
-                                 "ctn", "cq", "cqn", "tsrv", "tsr", "qsr",
-                                 "usr", "psim", "psit","psiq", "u10n", "t10n",
-                                 "tv10n", "q10n", "zo", "zot", "zoq", "uref",
-                                 "tref", "qref", "iteration", "dter", "dqer",
-                                 "dtwl", "qair", "qsea", "Rl", "Rs", "Rnl",
-                                 "ug", "Rib", "rh"])
+                          columns=["tau", "shf", "lhf", "L", "cd", "cdn", "ct",
+                                   "ctn", "cq", "cqn", "tsrv", "tsr", "qsr",
+                                   "usr", "psim", "psit","psiq", "u10n",
+                                   "t10n", "tv10n", "q10n", "zo", "zot", "zoq",
+                                   "uref", "tref", "qref", "iteration", "dter",
+                                   "dqer", "dtwl", "qair", "qsea", "Rl", "Rs",
+                                   "Rnl", "ug", "Rib", "rh"])
     resAll["flag"] = flag
     return resAll
 
diff --git a/flux_subs.py b/flux_subs.py
index c83f71d..454a0d4 100755
--- a/flux_subs.py
+++ b/flux_subs.py
@@ -206,7 +206,7 @@ def ctcq_calc(cdn, cd, ctn, cqn, hin, hout, psit, psiq):
     hin : float
         original temperature/humidity sensor height [m]
     hout : float
-        reference height                            [m]
+        reference height                   [m]
     psit : float
         heat stability function
     psiq : float
diff --git a/get_init.py b/get_init.py
index 73b18ff..7df08f8 100644
--- a/get_init.py
+++ b/get_init.py
@@ -2,7 +2,7 @@ import numpy as np
 import sys
 
 def get_init(spd, T, SST, lat, hum, P, Rl, Rs, cskin, skin, wl, gust, L, tol,
-             meth, qmeth):
+             n, meth, qmeth):
     """
     Checks initial input values and sets defaults if needed
 
@@ -17,7 +17,7 @@ def get_init(spd, T, SST, lat, hum, P, Rl, Rs, cskin, skin, wl, gust, L, tol,
         sea surface temperature in K
     lat : float
         latitude (deg), default 45deg
-    hum : array
+    hum : float
         relative humidity, if None is set to 80%
     P : float
         air pressure (hPa), default 1013hPa
@@ -40,12 +40,14 @@ def get_init(spd, T, SST, lat, hum, P, Rl, Rs, cskin, skin, wl, gust, L, tol,
         default else [1, 1.2, 800]
     L : int
         Monin-Obukhov length definition options
-    tol : array
+    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, 1e-3, 0.1, 0.1]
+    n : int
+        number of iterations
     meth : str
         "S80","S88","LP82","YT96","UA","LY04","C30","C35","ecmwf",
         "Beljaars"
@@ -78,7 +80,8 @@ def get_init(spd, T, SST, lat, hum, P, Rl, Rs, cskin, skin, wl, gust, L, tol,
         tolerance limits
     L : int
         MO length switch
-
+    n : int
+        number of iterations
     """
     # check if input is correct (type, size, value) and set defaults
     if ((type(spd) != np.ndarray) or (type(T) != np.ndarray) or
@@ -143,15 +146,13 @@ def get_init(spd, T, SST, lat, hum, P, Rl, Rs, cskin, skin, wl, gust, L, tol,
         sys.exit("gust input must be a 3x1 array")
     if (L not in [None, "S80", "ecmwf"]):
         sys.exit("L input must be either None, S80 or ecmwf")
-    if ((L == None) and (meth == "S80" or meth == "S88" or meth == "LP82"
-                         or meth == "YT96" or meth == "LY04" or
-                         meth == "UA" or meth == "C30" or meth == "C35"
-                         or meth == "Beljaars")):
-        L = "S80"
-    elif ((L == None) and (meth == "ecmwf")):
+    if (L == None):
         L = "ecmwf"
     if (tol == None):
-        tol = ['flux', 1e-3, 0.1, 0.1]
+        tol = ['all', 0.01, 0.01, 1e-05, 1e-3, 0.1, 0.1]
     elif (tol[0] not in ['flux', 'ref', 'all']):
         sys.exit("unknown tolerance input")
-    return lat, hum, P, Rl, Rs, cskin, skin, wl, gust, tol, L
+    if (n < 5):
+        n = 5
+        print("Number of iterations should not be less than 5; n is set to 5")
+    return lat, hum, P, Rl, Rs, cskin, skin, wl, gust, tol, L, n
-- 
GitLab