"git@git.noc.ac.uk:brecinosrivas/mdf_reader.git" did not exist on "8357821b98c562aafac8a835b99873230dd4729d"
AirSeaFluxCode.py 35.9 KB
Newer Older
sbiri's avatar
sbiri committed
1
import warnings
sbiri's avatar
sbiri committed
2
import numpy as np
3
import pandas as pd
sbiri's avatar
sbiri committed
4
import logging
5
from hum_subs import (get_hum, gamma)
sbiri's avatar
sbiri committed
6 7 8
from util_subs import *
from flux_subs import *
from cs_wl_subs import *
sbiri's avatar
sbiri committed
9 10


sbiri's avatar
sbiri committed
11 12
class S88:
    def _wind_iterate(self, ind):
13
        if self.gust[0] in [1, 2]:
sbiri's avatar
sbiri committed
14
            self.wind[ind] = np.sqrt(np.power(np.copy(self.spd[ind]), 2) +
sbiri's avatar
sbiri committed
15 16 17 18 19 20 21
                                     np.power(get_gust(self.gust[1],
                                                       self.theta[ind],
                                                       self.usr[ind],
                                            self.tsrv[ind], self.gust[2],
                                            self.grav[ind]), 2))
            # ratio of gusty to horizontal wind
            self.GustFact[ind] = self.wind[ind]/self.spd[ind]
sbiri's avatar
sbiri committed
22 23 24
            # if we update wind, also need to update u10n
            # remove effect of gustiness following Fairall et al. (2003)
            # usr is divided by (GustFact)^0.5
25 26 27 28 29 30 31 32
            self.u10n[ind] = self.wind[ind]-self.usr[ind]/kappa/np.sqrt(
                self.GustFact[ind])*(np.log(self.h_in[0, ind]/self.ref10) -
                                     self.psim[ind])
            if self.gust[0] == 2:
                self.GustFact[ind] = 1
                # option to not remove GustFact
                self.u10n[ind] = self.wind[ind]-self.usr[ind]/kappa*(
                    np.log(self.h_in[0, ind]/self.ref10)-self.psim[ind])
sbiri's avatar
sbiri committed
33 34 35
            # these lines integrate up from the surface - doesn't work when
            # gustiness is on
            # self.u10n[ind] = self.usr[ind]/kappa/np.power(
36
            #     self.GustFact[ind], 0.5)*(np.log(self.ref10/self.zo[ind]))
sbiri's avatar
sbiri committed
37 38
            # self.u10n[ind] = self.usr[ind]/kappa * \
            #     (np.log(self.ref10/self.zo[ind]))
sbiri's avatar
sbiri committed
39
        else:
sbiri's avatar
sbiri committed
40 41
            # not sure this is needed - perhaps only to remove effects of
            # initalisation of wind
sbiri's avatar
sbiri committed
42 43
            self.wind[ind] = np.copy(self.spd[ind])

sbiri's avatar
sbiri committed
44

sbiri's avatar
sbiri committed
45 46 47 48
    def get_heights(self, hin, hout=10):
        self.hout = hout
        self.hin = hin
        self.h_in = get_heights(hin, len(self.spd))
sbiri's avatar
sbiri committed
49
        self.h_out = get_heights(self.hout, 1)
sbiri's avatar
sbiri committed
50

sbiri's avatar
sbiri committed
51 52 53
    def get_specHumidity(self, qmeth="Buck2"):
        self.qair, self.qsea = get_hum(self.hum, self.T, self.SST, self.P,
                                       qmeth)
sbiri's avatar
sbiri committed
54 55
        if (np.all(np.isnan(self.qsea)) or np.all(np.isnan(self.qair))):
            raise ValueError("qsea and qair cannot be nan")
sbiri's avatar
sbiri committed
56 57 58
        self.dq_in = self.qair-self.qsea
        self.dq_full = self.qair-self.qsea

sbiri's avatar
sbiri committed
59 60 61 62
        # Set lapse rate and Potential Temperature (now we have humdity)
        self.cp = 1004.67*(1+0.00084*self.qsea)
        self.tlapse = gamma("dry", self.SST, self.T, self.qair/1000, self.cp)
        self.theta = np.copy(self.T)+self.tlapse*self.h_in[1]
sbiri's avatar
sbiri committed
63 64
        self.dt_in = self.theta-self.SST
        self.dt_full = self.theta-self.SST
sbiri's avatar
sbiri committed
65 66

    def _fix_coolskin_warmlayer(self, wl, cskin, skin, Rl, Rs):
sbiri's avatar
sbiri committed
67
        skin = self.skin if skin is None else skin
sbiri's avatar
sbiri committed
68 69
        assert wl in [0, 1], "wl not valid"
        assert cskin in [0, 1], "cskin not valid"
sbiri's avatar
sbiri committed
70 71
        assert skin in ["C35", "ecmwf", "Beljaars"], "Skin value not valid"

sbiri's avatar
sbiri committed
72 73 74 75 76 77 78 79
        if ((cskin == 1 or wl == 1) and
            (np.all(Rl == None) or np.all(np.isnan(Rl))) and
            ((np.all(Rs == None) or np.all(np.isnan(Rs))))):
            print("Cool skin/warm layer is switched ON; "
                  "Radiation input should not be empty")
            raise

        self.wl = wl
sbiri's avatar
sbiri committed
80 81
        self.cskin = cskin
        self.skin = skin
sbiri's avatar
sbiri committed
82 83
        self.Rs = np.full(self.spd.shape, np.nan) if Rs is None else Rs
        self.Rl = np.full(self.spd.shape, np.nan) if Rl is None else Rl
84
        # print(self.meth, self.cskin, self.skin, self.wl)
sbiri's avatar
sbiri committed
85

sbiri's avatar
sbiri committed
86
    def set_coolskin_warmlayer(self, wl=0, cskin=0, skin=None, Rl=None,
sbiri's avatar
sbiri committed
87
                               Rs=None):
sbiri's avatar
sbiri committed
88
        wl = 0 if wl is None else wl
sbiri's avatar
sbiri committed
89 90
        if hasattr(self, "skin") == False:
            self.skin = "C35"
sbiri's avatar
sbiri committed
91 92
        self._fix_coolskin_warmlayer(wl, cskin, skin, Rl, Rs)

sbiri's avatar
sbiri committed
93

sbiri's avatar
sbiri committed
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
    def _update_coolskin_warmlayer(self, ind):
        if self.cskin == 1:
            if self.skin == "C35":
                self.dter[ind], self.tkt[ind] = cs_C35(np.copy(
                    self.SST[ind]), self.rho[ind], self.Rs[ind], self.Rnl[ind],
                    self.cp[ind], self.lv[ind], np.copy(self.tkt[ind]),
                    self.usr[ind], self.tsr[ind], self.qsr[ind], self.grav[ind])
            elif self.skin == "ecmwf":
                self.dter[ind] = cs_ecmwf(
                    self.rho[ind], self.Rs[ind], self.Rnl[ind], self.cp[ind],
                    self.lv[ind], self.usr[ind], self.tsr[ind], self.qsr[ind],
                    np.copy(self.SST[ind]), self.grav[ind])
            elif self.skin == "Beljaars":
                self.Qs[ind], self.dter[ind] = cs_Beljaars(
                    self.rho[ind], self.Rs[ind], self.Rnl[ind], self.cp[ind],
                    self.lv[ind], self.usr[ind], self.tsr[ind], self.qsr[ind],
                    self.grav[ind], np.copy(self.Qs[ind]))
            self.dqer[ind] = get_dqer(self.dter[ind], self.SST[ind],
                                      self.qsea[ind], self.lv[ind])
sbiri's avatar
sbiri committed
113 114
            self.skt[ind] = np.copy(self.SST[ind])+self.dter[ind]
            self.skq[ind] = np.copy(self.qsea[ind])+self.dqer[ind]
sbiri's avatar
sbiri committed
115 116 117 118 119 120 121 122 123 124
            if self.wl == 1:
                self.dtwl[ind] = wl_ecmwf(
                    self.rho[ind], self.Rs[ind], self.Rnl[ind], self.cp[ind],
                    self.lv[ind], self.usr[ind], self.tsr[ind], self.qsr[ind],
                    np.copy(self.SST[ind]), np.copy(self.skt[ind]),
                    np.copy(self.dter[ind]), self.grav[ind])
                self.skt[ind] = (np.copy(self.SST[ind])+self.dter[ind] +
                                 self.dtwl[ind])
                self.dqer[ind] = get_dqer(self.dter[ind], self.skt[ind],
                                          self.qsea[ind], self.lv[ind])
sbiri's avatar
sbiri committed
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
                self.skq[ind] = np.copy(self.qsea[ind])+self.dqer[ind]
        else:
            self.dter[ind] = np.zeros(self.SST[ind].shape)
            self.dqer[ind] = np.zeros(self.SST[ind].shape)
            self.dtwl[ind] = np.zeros(self.SST[ind].shape)
            self.tkt[ind] = np.zeros(self.SST[ind].shape)

    def _first_guess(self):
        # reference height1
        self.ref10 = 10

        #  first guesses
        self.t10n, self.q10n = np.copy(self.theta), np.copy(self.qair)
        self.rho = self.P*100/(287.1*self.t10n*(1+0.6077*self.q10n))
        self.lv = (2.501-0.00237*(self.SST-CtoK))*1e6  # J/kg

        #  Zeng et al. 1998
        self.tv = self.theta*(1+0.6077*self.qair)   # virtual potential T
        self.dtv = self.dt_in*(1+0.6077*self.qair)+0.6077*self.theta*self.dq_in

        # Set the wind array
sbiri's avatar
sbiri committed
146 147
        # self.wind = np.maximum(np.copy(self.spd), 3)
        self.wind = np.sqrt(np.power(np.copy(self.spd), 2)+0.25)
sbiri's avatar
sbiri committed
148 149 150 151
        self.GustFact = self.wind*0+1

        # Rb eq. 11 Grachev & Fairall 1997, use air temp height
        # use self.tv??  adjust wind to T-height?
sbiri's avatar
sbiri committed
152
        Rb = self.grav*self.h_in[1]*self.dtv/(self.T*np.power(self.wind, 2))
sbiri's avatar
sbiri committed
153
        self.monob = self.h_in[1]/12.0/Rb  # eq. 12 Grachev & Fairall 1997   # DO.THIS
sbiri's avatar
sbiri committed
154
        # where does 12.0 come from??
sbiri's avatar
sbiri committed
155 156 157

        # ------------

158 159
        # dummy_array = lambda val : np.full(self.T.shape, val)*self.msk
        def dummy_array(val): return np.full(self.T.shape, val)*self.msk
sbiri's avatar
sbiri committed
160
        if self.cskin + self.wl > 0:
sbiri's avatar
sbiri committed
161 162
            self.dter, self.tkt, self.dtwl = [
                dummy_array(x) for x in (-0.3, 0.001, 0.3)]
163 164
            self.dqer = get_dqer(self.dter, self.SST, self.qsea,
                                 self.lv)
sbiri's avatar
sbiri committed
165 166
            self.Rnl = 0.97*(self.Rl-5.67e-8*np.power(
                self.SST-0.3*self.cskin, 4))
sbiri's avatar
sbiri committed
167 168
            self.Qs = 0.945*self.Rs
        else:
sbiri's avatar
sbiri committed
169 170 171 172
            self.dter, self.dqer, self.dtwl = [
                dummy_array(x) for x in (0.0, 0.0, 0.0)]
            self.Rnl, self.Qs, self.tkt = [
                np.empty(self.arr_shp)*self.msk for _ in range(3)]
sbiri's avatar
sbiri committed
173 174 175 176 177
        self.skt = np.copy(self.SST)
        self.skq = np.copy(self.qsea)

        self.u10n = np.copy(self.wind)
        self.usr = 0.035*self.u10n
sbiri's avatar
sbiri committed
178 179
        self.cd10n, self.zo = cdn_calc(
            self.u10n, self.usr, self.theta, self.grav, self.meth)
sbiri's avatar
sbiri committed
180 181 182
        self.psim = psim_calc(self.h_in[0]/self.monob, self.meth)
        self.cd = cd_calc(self.cd10n, self.h_in[0], self.ref10, self.psim)
        self.usr = np.sqrt(self.cd*np.power(self.wind, 2))
sbiri's avatar
sbiri committed
183 184 185 186
        self.zot, self.zoq, self.tsr, self.qsr = [
            np.empty(self.arr_shp)*self.msk for _ in range(4)]
        self.ct10n, self.cq10n, self.ct, self.cq = [
            np.empty(self.arr_shp)*self.msk for _ in range(4)]
sbiri's avatar
sbiri committed
187 188 189 190 191 192 193
        self.tv10n = self.zot  # remove from output

    def iterate(self, maxiter=10, tol=None):
        if maxiter < 5:
            warnings.warn("Iteration number <5 - resetting to 5.")
            maxiter = 5

sbiri's avatar
sbiri committed
194 195 196 197
        # Decide which variables to use in tolerances based on tolerance
        # specification
        tol = ['all', 0.01, 0.01, 1e-05, 1e-3,
               0.1, 0.1] if tol is None else tol
sbiri's avatar
sbiri committed
198 199
        assert tol[0] in ['flux', 'ref', 'all'], "unknown tolerance input"

sbiri's avatar
sbiri committed
200 201
        old_vars = {"flux": ["tau", "sensible", "latent"],
                    "ref": ["u10n", "t10n", "q10n"]}
sbiri's avatar
sbiri committed
202 203 204
        old_vars["all"] = old_vars["ref"] + old_vars["flux"]
        old_vars = old_vars[tol[0]]

sbiri's avatar
sbiri committed
205 206
        new_vars = {"flux": ["tau", "sensible", "latent"],
                    "ref": ["u10n", "t10n", "q10n"]}
sbiri's avatar
sbiri committed
207 208 209 210 211 212 213 214 215 216 217
        new_vars["all"] = new_vars["ref"] + new_vars["flux"]
        new_vars = new_vars[tol[0]]
        # I'm sure there are better ways of doing this
        # extract tolerance values by deleting flag from tol
        tvals = np.delete(np.copy(tol), 0)
        tol_vals = list([float(tt) for tt in tvals])

        ind = np.where(self.spd > 0)
        it = 0

        # Setup empty arrays
sbiri's avatar
sbiri committed
218 219
        self.tsrv, self.psim, self.psit, self.psiq = [
            np.zeros(self.arr_shp)*self.msk for _ in range(4)]
sbiri's avatar
sbiri committed
220 221

        # extreme values for first comparison
222 223
        dummy_array = lambda val: np.full(self.arr_shp, val)*self.msk
        # you can use def instead of lambda
sbiri's avatar
sbiri committed
224 225 226
        # def dummy_array(val): return np.full(self.arr_shp, val)*self.msk
        self.itera, self.tau, self.sensible, self.latent = [
            dummy_array(x) for x in (-1, 1e+99, 1e+99, 1e+99)]
sbiri's avatar
sbiri committed
227 228 229 230 231 232 233 234 235 236

        # Generate the first guess values
        self._first_guess()

        #  iteration loop
        ii = True
        while ii & (it < maxiter):
            it += 1

            # Set the old variables (for comparison against "new")
sbiri's avatar
sbiri committed
237
            old = np.array([np.copy(getattr(self, i)) for i in old_vars])
sbiri's avatar
sbiri committed
238 239

            # Calculate cdn
sbiri's avatar
sbiri committed
240 241 242 243 244
            self.cd10n[ind], self.zo[ind] = cdn_calc(
                self.u10n[ind], self.usr[ind], self.theta[ind], self.grav[ind],
                self.meth)

            if np.all(np.isnan(self.cd10n)):
sbiri's avatar
sbiri committed
245
                logging.info('break %s at iteration %s cd10n<0', meth, it)
sbiri's avatar
sbiri committed
246 247 248 249 250 251 252
                break


            self.psim[ind] = psim_calc(
                self.h_in[0, ind]/self.monob[ind], self.meth)
            self.cd[ind] = cd_calc(
                self.cd10n[ind], self.h_in[0, ind], self.ref10, self.psim[ind])
sbiri's avatar
sbiri committed
253 254
            # remove effect of gustiness following Fairall et al. (2003)
            # usr is divided by (GustFact)^0.5 (updated in wind_iterate)
sbiri's avatar
sbiri committed
255 256 257 258 259 260
            # these 2 equations integrating from surface should be equivalent
            # - but they are not due to gustiness
            # self.u10n[ind] = self.usr[ind]/kappa/np.power(
            #     self.GustFact[ind],0.5)*(np.log(self.ref10/self.zo[ind]))
            # self.u10n[ind] = self.usr[ind]/kappa*(
            #     np.log(self.ref10/self.zo[ind]))
sbiri's avatar
sbiri committed
261
            # lines below with and without gustfactor
262 263 264 265 266 267 268 269 270
            # if self.gust[0] == 1:
            #     self.u10n[ind] = self.wind[ind]-self.usr[ind]/kappa/np.sqrt(
            #         self.GustFact[ind], 0.5)*(np.log(
            #             self.h_in[0, ind]/self.ref10)-self.psim[ind])
            # elif self.gust[0] == 2:
            #     self.u10n[ind] = self.wind[ind]-self.usr[ind]/kappa*(
            #         np.log(self.h_in[0, ind]/self.ref10)-self.psim[ind])
            # Update the wind values
            self._wind_iterate(ind)
sbiri's avatar
sbiri committed
271 272 273


            # temperature
sbiri's avatar
sbiri committed
274 275 276 277 278 279 280 281
            self.ct10n[ind], self.zot[ind] = ctqn_calc(
                "ct", self.h_in[1, ind]/self.monob[ind], self.cd10n[ind],
                self.usr[ind], self.zo[ind], self.theta[ind], self.meth)
            self.psit[ind] = psit_calc(
                self.h_in[1, ind]/self.monob[ind], self.meth)
            self.ct[ind] = ctq_calc(
                self.cd10n[ind], self.cd[ind], self.ct10n[ind],
                self.h_in[1, ind], self.ref10, self.psit[ind])
sbiri's avatar
sbiri committed
282
            # wind
sbiri's avatar
sbiri committed
283 284 285 286 287 288 289 290
            self.cq10n[ind], self.zoq[ind] = ctqn_calc(
                "cq", self.h_in[2, ind]/self.monob[ind], self.cd10n[ind],
                self.usr[ind], self.zo[ind], self.theta[ind], self.meth)
            self.psiq[ind] = psit_calc(
                self.h_in[2, ind]/self.monob[ind], self.meth)
            self.cq[ind] = ctq_calc(
                self.cd10n[ind], self.cd[ind], self.cq10n[ind],
                self.h_in[2, ind], self.ref10, self.psiq[ind])
sbiri's avatar
sbiri committed
291 292 293 294 295 296 297


            # Some parameterizations set a minimum on parameters
            try:
                self._minimum_params()
            except AttributeError:
                pass
sbiri's avatar
sbiri committed
298 299 300

            self.dt_full[ind] = self.dt_in[ind] - \
                self.dter[ind]*self.cskin - self.dtwl[ind]*self.wl
sbiri's avatar
sbiri committed
301
            self.dq_full[ind] = self.dq_in[ind] - self.dqer[ind]*self.cskin
sbiri's avatar
sbiri committed
302 303 304 305 306
            self.usr[ind], self.tsr[ind], self.qsr[ind] = get_strs(
                self.h_in[:, ind], self.monob[ind], self.wind[ind],
                self.zo[ind], self.zot[ind], self.zoq[ind], self.dt_full[ind],
                self.dq_full[ind], self.cd[ind], self.ct[ind], self.cq[ind],
                self.meth)
sbiri's avatar
sbiri committed
307 308 309

            # Update CS/WL parameters
            self._update_coolskin_warmlayer(ind)
sbiri's avatar
sbiri committed
310

sbiri's avatar
sbiri committed
311
            # Logging output
sbiri's avatar
sbiri committed
312 313 314 315 316 317 318 319
            log_vars = {"dter": 2, "dqer": 7, "tkt": 2,
                        "Rnl": 2, "usr": 3, "tsr": 4, "qsr": 7}
            log_vars = [np.round(np.nanmedian(getattr(self, V)), R)
                        for V, R in log_vars.items()]
            log_vars.insert(0, self.meth)
            logging.info(
                'method {} | dter = {} | dqer = {} | tkt = {} | Rnl = {} |'
                ' usr = {} | tsr = {} | qsr = {}'.format(*log_vars))
sbiri's avatar
sbiri committed
320 321

            if self.cskin + self.wl > 0:
sbiri's avatar
sbiri committed
322 323 324
                self.Rnl[ind] = 0.97*(self.Rl[ind]-5.67e-8 *
                                      np.power(self.SST[ind] +
                                               self.dter[ind]*self.cskin, 4))
sbiri's avatar
sbiri committed
325 326
            # not sure how to handle lapse/potemp
            # well-mixed in potential temperature ...
327
            self.t10n[ind] = self.theta[ind]-self.tlapse[ind]*self.ref10 - \
sbiri's avatar
sbiri committed
328
                self.tsr[ind]/kappa * \
329 330
                    (np.log(self.h_in[1, ind]/self.ref10)-self.psit[ind])
            self.q10n[ind] = self.qair[ind]-self.qsr[ind]/kappa * \
sbiri's avatar
sbiri committed
331
                (np.log(self.h_in[2, ind]/self.ref10)-self.psiq[ind])
sbiri's avatar
sbiri committed
332 333

            # update stability info
sbiri's avatar
sbiri committed
334 335 336 337 338 339
            self.tsrv[ind] = get_tsrv(
                self.tsr[ind], self.qsr[ind], self.theta[ind], self.qair[ind])
            self.Rb[ind] = get_Rb(
                self.grav[ind], self.usr[ind], self.h_in[0, ind],
                self.h_in[1, ind], self.tv[ind], self.dtv[ind], self.wind[ind],
                self.monob[ind], self.meth)
sbiri's avatar
sbiri committed
340
            if self.L == "tsrv":
sbiri's avatar
sbiri committed
341 342 343
                self.monob[ind] = get_Ltsrv(
                    self.tsrv[ind], self.grav[ind], self.tv[ind],
                    self.usr[ind])
sbiri's avatar
sbiri committed
344
            else:
sbiri's avatar
sbiri committed
345 346 347
                self.monob[ind] = get_LRb(
                    self.Rb[ind], self.h_in[1,ind], self.monob[ind],
                    self.zo[ind], self.zot[ind], self.meth)
sbiri's avatar
sbiri committed
348 349 350

            # Update the wind values
            self._wind_iterate(ind)
sbiri's avatar
sbiri committed
351

sbiri's avatar
sbiri committed
352
            # make sure you allow small negative values convergence
sbiri's avatar
sbiri committed
353 354
            if it == 1:
                self.u10n = np.where(self.u10n < 0, 0.5, self.u10n)
sbiri's avatar
sbiri committed
355

sbiri's avatar
sbiri committed
356
            self.itera[ind] = np.full(1, it)
sbiri's avatar
sbiri committed
357
            # remove effect of gustiness following Fairall et al. (2003)
sbiri's avatar
sbiri committed
358 359
            # usr is divided by (GustFact)^0.5 (here applied to sensible and
            # latent as well as tau)
sbiri's avatar
sbiri committed
360 361
            # GustFact should be 1 if gust is OFF
            self.tau = self.rho*np.power(self.usr, 2)/self.GustFact
sbiri's avatar
sbiri committed
362
            self.sensible = self.rho*self.cp*self.usr / \
363
                np.sqrt(self.GustFact)*self.tsr
sbiri's avatar
sbiri committed
364
            self.latent = self.rho*self.lv*self.usr / \
365
                np.sqrt(self.GustFact)*self.qsr
sbiri's avatar
sbiri committed
366
            # or leave as it is - gusty wind speed, or no gust
sbiri's avatar
sbiri committed
367 368 369
            # self.tau = self.rho*np.power(self.usr, 2)
            # self.sensible = self.rho*self.cp*self.usr*self.tsr
            # self.latent = self.rho*self.lv*self.usr*self.qsr
sbiri's avatar
sbiri committed
370 371

            # Set the new variables (for comparison against "old")
sbiri's avatar
sbiri committed
372
            new = np.array([np.copy(getattr(self, i)) for i in new_vars])
sbiri's avatar
sbiri committed
373

sbiri's avatar
sbiri committed
374
            if it > 2:  # force at least three iterations
375
                d = np.abs(new-old)  # change over this iteration
sbiri's avatar
sbiri committed
376 377 378 379
                for ii in range(0, len(tol_vals)):
                    d[ii, ] = d[ii, ]/tol_vals[ii]  # ratio to tolerance
                # identifies non-convergence
                ind = np.where(d.max(axis=0) >= 1)
sbiri's avatar
sbiri committed
380 381 382 383 384 385 386 387

            self.ind = np.copy(ind)
            ii = False if (ind[0].size == 0) else True
            # End of iteration loop

        self.itera[ind] = -1
        self.itera = np.where(self.itera > maxiter, -1, self.itera)
        logging.info('method %s | # of iterations:%s', self.meth, it)
sbiri's avatar
sbiri committed
388 389
        logging.info('method %s | # of points that did not converge :%s \n',
                     self.meth, self.ind[0].size)
sbiri's avatar
sbiri committed
390 391 392


    def _get_humidity(self):
393
        """Calculate RH used for flagging purposes & output."""
sbiri's avatar
sbiri committed
394
        if self.hum[0] in ('rh', 'no'):
sbiri's avatar
sbiri committed
395
            self.rh = self.hum[1]
sbiri's avatar
sbiri committed
396
        elif self.hum[0] == 'Td':
sbiri's avatar
sbiri committed
397 398 399
            Td = self.hum[1]  # dew point temperature (K)
            Td = np.where(Td < 200, np.copy(Td)+CtoK, np.copy(Td))
            T = np.where(self.T < 200, np.copy(self.T)+CtoK, np.copy(self.T))
400
            # T = np.copy(self.T)
sbiri's avatar
sbiri committed
401 402 403
            esd = 611.21*np.exp(17.502*((Td-CtoK)/(Td-32.19)))
            es = 611.21*np.exp(17.502*((T-CtoK)/(T-32.19)))
            self.rh = 100*esd/es
sbiri's avatar
sbiri committed
404

405 406 407
    def _flag(self, out=0):
        """Set the general flags."""
        flag = np.full(self.arr_shp, "n", dtype="object")
sbiri's avatar
sbiri committed
408

sbiri's avatar
sbiri committed
409 410 411 412 413
        if self.hum[0] == 'no':
            if self.cskin == 1:
                flag = np.where(
                    np.isnan(self.spd+self.T+self.SST+self.P+self.Rs+self.Rl),
                    "m", flag)
sbiri's avatar
sbiri committed
414
            else:
sbiri's avatar
sbiri committed
415 416
                flag = np.where(
                    np.isnan(self.spd+self.T+self.SST+self.P), "m", flag)
sbiri's avatar
sbiri committed
417
        else:
sbiri's avatar
sbiri committed
418 419
            if self.cskin == 1:
                flag = np.where(np.isnan(
420 421
                    self.spd+self.T+self.SST+self.hum[1]+self.P +
                    self.Rs+self.Rl), "m", flag)
sbiri's avatar
sbiri committed
422
            else:
sbiri's avatar
sbiri committed
423 424 425
                flag = np.where(
                    np.isnan(self.spd+self.T+self.SST+self.hum[1]+self.P),
                    "m", flag)
sbiri's avatar
sbiri committed
426 427 428 429

            flag = np.where(self.rh > 100, "r", flag)

        # u10n flag
sbiri's avatar
sbiri committed
430 431 432 433 434
        flag = np.where(((self.u10n < 0) | (self.u10n > 200)) & (flag == "n"),
                        "u",
                        np.where(((self.u10n < 0) | (self.u10n > 200)) &
                                 (np.char.find(flag.astype(str), 'u') == -1),
                                 flag+[","]+["u"], flag))
sbiri's avatar
sbiri committed
435
        # q10n flag
sbiri's avatar
sbiri committed
436 437 438 439 440
        flag = np.where(
            ((self.q10n < 0) | (self.q10n > 40*0.001)) & (flag == "n"), "q",
            np.where(
                ((self.q10n < 0) | (self.q10n > 40*0.001)) & (flag != "n"),
                flag+[","]+["q"], flag))
441

sbiri's avatar
sbiri committed
442
        # t10n flag
sbiri's avatar
sbiri committed
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
        flag = np.where(
            ((self.t10n < 173) | (self.t10n > 373)) & (flag == "n"), "t",
            np.where(((self.t10n < 173) | (self.t10n > 373)) & (flag != "n"),
                     flag+[","]+["t"], flag))

        flag = np.where(
            ((self.Rb < -0.5) | (self.Rb > 0.2) |
             ((self.hin[0]/self.monob) > 1000)) & (flag == "n"), "l",
            np.where(((self.Rb < -0.5) | (self.Rb > 0.2) |
                      ((self.hin[0]/self.monob) > 1000)) &
                     (flag != "n"), flag+[","]+["l"], flag))

        if out == 1:
            flag = np.where((self.itera == -1) & (flag == "n"), "i", np.where(
                (self.itera == -1) &
                ((flag != "n") & (np.char.find(flag.astype(str), 'm') == -1)),
                flag+[","]+["i"], flag))
sbiri's avatar
sbiri committed
460
        else:
sbiri's avatar
sbiri committed
461 462 463 464 465
            flag = np.where((self.itera == -1) & (flag == "n"), "i", np.where(
                (self.itera == -1) &
                ((flag != "n") & (np.char.find(flag.astype(str), 'm') == -1) &
                 (np.char.find(flag.astype(str), 'u') == -1)),
                flag+[","]+["i"], flag))
sbiri's avatar
sbiri committed
466 467
        self.flag = flag

sbiri's avatar
sbiri committed
468 469 470
    def get_output(self, out=0):

        assert out in [0, 1], "out must be either 0 or 1"
sbiri's avatar
sbiri committed
471

472
        self._get_humidity()  # Get the Relative humidity
sbiri's avatar
sbiri committed
473 474 475 476
        self._flag(out=out)  # Get flags

        # remove effect of gustiness following Fairall et al. (2003)
        # usr is divided by (GustFact)^0.5
477
        self.uref = self.spd-self.usr/kappa/np.sqrt(self.GustFact) * \
sbiri's avatar
sbiri committed
478 479
            (np.log(self.h_in[0]/self.h_out[0])-self.psim +
             psim_calc(self.h_out[0]/self.monob, self.meth))
sbiri's avatar
sbiri committed
480
        # include lapse rate adjustment as theta is well-mixed
sbiri's avatar
sbiri committed
481 482 483 484 485 486
        self.tref = self.theta-self.tlapse*self.h_out[1]-self.tsr/kappa * \
            (np.log(self.h_in[1]/self.h_out[1])-self.psit +
             psit_calc(self.h_out[1]/self.monob, self.meth))
        self.qref = self.qair-self.qsr/kappa * \
            (np.log(self.h_in[2]/self.h_out[2])-self.psiq +
             psit_calc(self.h_out[2]/self.monob, self.meth))
sbiri's avatar
sbiri committed
487

sbiri's avatar
sbiri committed
488 489 490
        if self.wl == 0:
            self.dtwl = np.zeros(self.T.shape)*self.msk
            # reset to zero if not used
sbiri's avatar
sbiri committed
491 492

        # Do not calculate lhf if a measure of humidity is not input
sbiri's avatar
sbiri committed
493 494 495 496 497
        # This gets filled into a pd dataframe and so no need to specify y
        # dimension of array
        if self.hum[0] == 'no':
            self.latent, self.qsr, self.q10n = np.empty(3)
            self.qref, self.qair, self.rh = np.empty(3)
sbiri's avatar
sbiri committed
498 499 500 501 502

        # Set the final wind speed values
        # this seems to be gust (was wind_speed)
        self.ug = np.sqrt(np.power(self.wind, 2)-np.power(self.spd, 2))

sbiri's avatar
sbiri committed
503 504
        # Get class specific flags (will only work if self.u_hi and self.u_lo
        # have been set in the class)
sbiri's avatar
sbiri committed
505 506 507 508 509 510
        try:
            self._class_flag()
        except AttributeError:
            pass

        # Combine all output variables into a pandas array
sbiri's avatar
sbiri committed
511 512 513
        res_vars = ("tau", "sensible", "latent", "monob", "cd", "cd10n", "ct",
                    "ct10n", "cq", "cq10n", "tsrv", "tsr", "qsr", "usr",
                    "psim", "psit", "psiq", "u10n", "t10n", "q10n", "zo",
514
                    "zot", "zoq", "uref", "tref", "qref", "dter",
sbiri's avatar
sbiri committed
515
                    "dqer", "dtwl", "qair", "qsea", "Rl", "Rs", "Rnl", "ug",
516
                    "Rb", "rh", "tkt", "lv", "itera")
sbiri's avatar
sbiri committed
517 518

        res = np.zeros((len(res_vars), len(self.spd)))
sbiri's avatar
sbiri committed
519 520
        for i, value in enumerate(res_vars):
            res[i][:] = getattr(self, value)
sbiri's avatar
sbiri committed
521

sbiri's avatar
sbiri committed
522
        if out == 0:
sbiri's avatar
sbiri committed
523 524
            res[:, self.ind] = np.nan
            # set missing values where data have non acceptable values
sbiri's avatar
sbiri committed
525
            if self.hum[0] != 'no':
526 527 528
                res[:-1] = np.asarray([
                    np.where(self.q10n < 0, np.nan, res[i][:])
                    for i in range(len(res_vars)-1)])
529 530
                # len(res_vars)-1 instead of len(res_vars) in order to keep
                # itera= -1 for no convergence
531 532 533
            res[:-1] = np.asarray([
                np.where(self.u10n < 0, np.nan, res[i][:])
                for i in range(len(res_vars)-1)])
sbiri's avatar
sbiri committed
534
        else:
sbiri's avatar
sbiri committed
535 536 537 538
            warnings.warn("Warning: the output will contain values for points"
                          " that have not converged and negative values "
                          "(if any) for u10n/q10n")

539 540
        resAll = pd.DataFrame(data=res.T, index=range(self.nlen),
                              columns=res_vars)
sbiri's avatar
sbiri committed
541 542 543 544 545 546 547 548

        resAll["flag"] = self.flag

        return resAll

    def add_variables(self, spd, T, SST, lat=None, hum=None, P=None, L=None):

        # Add the mandatory variables
sbiri's avatar
sbiri committed
549 550 551 552
        assert type(spd) == type(T) == type(
            SST) == np.ndarray, "input type of spd, T and SST should be"
        " numpy.ndarray"
        self.L = "tsrv" if L is None else L
sbiri's avatar
sbiri committed
553 554 555
        self.arr_shp = spd.shape
        self.nlen = len(spd)
        self.spd = spd
sbiri's avatar
sbiri committed
556
        # self.T = T
sbiri's avatar
sbiri committed
557
        self.T = np.where(T < 200, np.copy(T)+CtoK, np.copy(T))
sbiri's avatar
sbiri committed
558
        self.hum = ['no', np.full(SST.shape, 80)] if hum is None else hum
sbiri's avatar
sbiri committed
559
        self.SST = np.where(SST < 200, np.copy(SST)+CtoK, np.copy(SST))
sbiri's avatar
sbiri committed
560
        self.lat = np.full(self.arr_shp, 45) if lat is None else lat
sbiri's avatar
sbiri committed
561
        self.grav = gc(self.lat)
562
        self.P = np.full(self.nlen, 1013) if P is None else P
sbiri's avatar
sbiri committed
563 564

        # mask to preserve missing values when initialising variables
sbiri's avatar
sbiri committed
565
        self.msk = np.empty(SST.shape)
sbiri's avatar
sbiri committed
566 567 568
        self.msk = np.where(np.isnan(spd+T+SST), np.nan, 1)
        self.Rb = np.empty(SST.shape)*self.msk

sbiri's avatar
sbiri committed
569 570
    def add_gust(self, gust=None):
        if np.all(gust is None):
sbiri's avatar
sbiri committed
571 572 573
            try:
                gust = self.default_gust
            except AttributeError:
sbiri's avatar
sbiri committed
574 575
                gust = [0, 0, 0]  # gustiness OFF
                # gust = [1, 1.2, 800]
sbiri's avatar
sbiri committed
576 577
        elif ((np.size(gust) < 3) and (gust == 0)):
            gust = [0, 0, 0]
sbiri's avatar
sbiri committed
578

sbiri's avatar
sbiri committed
579
        assert np.size(gust) == 3, "gust input must be a 3x1 array"
580
        assert gust[0] in [0, 1, 2], "gust at position 0 must be 0, 1 or 2"
sbiri's avatar
sbiri committed
581 582 583
        self.gust = gust

    def _class_flag(self):
sbiri's avatar
sbiri committed
584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607
        """A flag specific to this class - only used for certain classes where
         u_lo and u_hi are defined"""
        # flag.tmp = np.where(
        #     ((self.u10n < self.u_lo[0]) | (self.u10n > self.u_hi[0])),"o", "")
        # self.flag = np.where(
        #     self.flag == "n" & flag.tmp == "o", "o", self.flag)
        # self.flag = np.where(
        #     flag.tmp == "o" & self.flag != "n" & self.flag != "o" &
        #     self.flag != "m", self.flag+[","]+["o"], self.flag)
        # flag.add = ["u", "q", "t"]
        # self.flag = np.where(
        #     flag.tmp == "o" & (np.char.find(self.flag.astype(str), 'u') == -1 |
        #                        np.char.find(self.flag.astype(str), 'q') == -1 |
        #                        np.char.find(self.flag.astype(str), 't') == -1),
        #     self.flag+[","]+["o"], self.flag))
        self.flag = np.where(((self.u10n < self.u_lo[0]) |
                              (self.u10n > self.u_hi[0])) &
                             (self.flag == "n"), "o",
                             np.where(((self.u10n < self.u_lo[1]) |
                                       (self.u10n > self.u_hi[1])) &
                                      ((self.flag != "n") & (np.char.find(
                                          self.flag.astype(str), 'u') == -1) &
                                       (np.char.find(
                                           self.flag.astype(str), 'q') == -1)),
sbiri's avatar
sbiri committed
608 609 610 611 612 613 614 615 616
                                      self.flag+[","]+["o"], self.flag))

    def __init__(self):
        self.meth = "S88"

class S80(S88):

    def __init__(self):
        self.meth = "S80"
sbiri's avatar
sbiri committed
617 618
        self.u_lo = [6, 6]
        self.u_hi = [22, 22]
sbiri's avatar
sbiri committed
619

sbiri's avatar
sbiri committed
620

sbiri's avatar
sbiri committed
621 622 623 624
class YT96(S88):

    def __init__(self):
        self.meth = "YT96"
625
        # no limits to u range as we use eq. 21 for cdn
sbiri's avatar
sbiri committed
626 627
        # self.u_lo = [0, 3]
        # self.u_hi = [26, 26]
sbiri's avatar
sbiri committed
628 629

class LP82(S88):
sbiri's avatar
sbiri committed
630

sbiri's avatar
sbiri committed
631 632
    def __init__(self):
        self.meth = "LP82"
sbiri's avatar
sbiri committed
633 634
        self.u_lo = [3, 3]
        self.u_hi = [25, 25]
sbiri's avatar
sbiri committed
635 636 637 638 639 640 641 642 643 644


class NCAR(S88):


    def _minimum_params(self):
        self.cd = np.maximum(np.copy(self.cd), 1e-4)
        self.ct = np.maximum(np.copy(self.ct), 1e-4)
        self.cq = np.maximum(np.copy(self.cq), 1e-4)
        self.zo = np.minimum(np.copy(self.zo), 0.0025)
sbiri's avatar
sbiri committed
645

sbiri's avatar
sbiri committed
646 647
    def __init__(self):
        self.meth = "NCAR"
sbiri's avatar
sbiri committed
648 649
        self.u_lo = [0.5, 0.5]
        self.u_hi = [999, 999]
sbiri's avatar
sbiri committed
650 651

class UA(S88):
sbiri's avatar
sbiri committed
652

sbiri's avatar
sbiri committed
653 654
    def __init__(self):
        self.meth = "UA"
sbiri's avatar
sbiri committed
655 656 657
        self.default_gust = [1, 1, 1000]
        self.u_lo = [-999, -999]
        self.u_hi = [18, 18]
sbiri's avatar
sbiri committed
658 659 660


class C30(S88):
sbiri's avatar
sbiri committed
661 662
    # def set_coolskin_warmlayer(self, wl=0, cskin=1, skin="C35", Rl=None, Rs=None):
    #     self._fix_coolskin_warmlayer(wl, cskin, skin, Rl, Rs)
sbiri's avatar
sbiri committed
663 664 665

    def __init__(self):
        self.meth = "C30"
sbiri's avatar
sbiri committed
666
        self.default_gust = [1, 1.2, 600]
sbiri's avatar
sbiri committed
667
        self.skin = "C35"
sbiri's avatar
sbiri committed
668 669 670 671

class C35(C30):
    def __init__(self):
        self.meth = "C35"
sbiri's avatar
sbiri committed
672
        self.default_gust = [1, 1.2, 600]
sbiri's avatar
sbiri committed
673
        self.skin = "C35"
sbiri's avatar
sbiri committed
674 675

class ecmwf(C30):
sbiri's avatar
sbiri committed
676 677 678
    # def set_coolskin_warmlayer(self, wl=0, cskin=1, skin="ecmwf", Rl=None,
    #                            Rs=None):
    #     self._fix_coolskin_warmlayer(wl, cskin, skin, Rl, Rs)
sbiri's avatar
sbiri committed
679 680 681

    def __init__(self):
        self.meth = "ecmwf"
sbiri's avatar
sbiri committed
682
        self.default_gust = [1, 1, 1000]
sbiri's avatar
sbiri committed
683
        self.skin = "ecmwf"
sbiri's avatar
sbiri committed
684 685

class Beljaars(C30):
sbiri's avatar
sbiri committed
686 687 688
    # def set_coolskin_warmlayer(self, wl=0, cskin=1, skin="Beljaars", Rl=None,
    #                            Rs=None):
    #     self._fix_coolskin_warmlayer(wl, cskin, skin, Rl, Rs)
sbiri's avatar
sbiri committed
689

sbiri's avatar
sbiri committed
690 691
    def __init__(self):
        self.meth = "Beljaars"
sbiri's avatar
sbiri committed
692
        self.default_gust = [1, 1, 1000]
sbiri's avatar
sbiri committed
693
        self.skin = "Beljaars"
sbiri's avatar
sbiri committed
694 695


696
def AirSeaFluxCode(spd, T, SST, lat=None, hum=None, P=None, hin=18, hout=10,
sbiri's avatar
sbiri committed
697
                   Rl=None, Rs=None, cskin=0, skin=None, wl=0, gust=None,
sbiri's avatar
sbiri committed
698 699
                   meth="S88", qmeth="Buck2", tol=None, maxiter=10, out=0,
                   L=None):
700
    """
sbiri's avatar
sbiri committed
701 702 703
    Calculate turbulent surface fluxes using different parameterizations.

    Calculate height adjusted values for spd, T, q
sbiri's avatar
sbiri committed
704 705 706 707 708 709 710 711 712 713 714

    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 (will convert if < 200)
        SST : float
            sea surface temperature in K (will convert if < 200)
        lat : float
715 716 717
            latitude (deg), default 45deg
        hum : float
            humidity input switch 2x1 [x, values] default is relative humidity
718 719 720
            x='rh' : relative humidity in %
            x='q' : specific humidity (g/kg)
            x='Td' : dew point temperature (K)
sbiri's avatar
sbiri committed
721
        P : float
722
            air pressure (hPa), default 1013hPa
sbiri's avatar
sbiri committed
723
        hin : float
724
            sensor heights in m (array 3x1 or 3xn), default 18m
sbiri's avatar
sbiri committed
725 726 727 728 729 730
        hout : float
            output height, default is 10m
        Rl : float
            downward longwave radiation (W/m^2)
        Rs : float
            downward shortwave radiation (W/m^2)
sbiri's avatar
sbiri committed
731
        cskin : int
732 733
            0 switch cool skin adjustment off, else 1
            default is 1
734 735 736 737
        skin : str
            cool skin method option "C35", "ecmwf" or "Beljaars"
        wl : int
            warm layer correction default is 0, to switch on set to 1
738
        gust : int
739 740
            3x1 [x, beta, zi] x=0 gustiness is OFF, x=1 gustiness is ON and
            use gustiness factor, x=2 gustiness is ON and gustiness factor=1;
741
            beta gustiness parameter, beta=1 for UA, beta=1.2 for COARE
742
            zi PBL height (m) 600 for COARE, 1000 for UA and ecmwf
743
            default for COARE [1, 1.2, 600]
744
            default for UA, ecmwf [1, 1, 1000]
745
            else default is switched OFF
746
        meth : str
sbiri's avatar
sbiri committed
747
            "S80", "S88", "LP82", "YT96", "UA", "NCAR", "C30", "C35",
748
            "ecmwf", "Beljaars"
749 750
        qmeth : str
            is the saturation evaporation method to use amongst
751 752
            "HylandWexler","Hardy","Preining","Wexler","GoffGratch","WMO",
            "MagnusTetens","Buck","Buck2","WMO2018","Sonntag","Bolton",
753 754 755 756 757 758 759
            "IAPWS","MurphyKoop"]
            default is Buck2
        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
sbiri's avatar
sbiri committed
760 761
                    adjustment lim1-6
           default is tol=['all', 0.01, 0.01, 1e-05, 1e-3, 0.1, 0.1]
sbiri's avatar
sbiri committed
762 763
        maxiter : int
            number of iterations (default = 10)
764
        out : int
765 766
            set 0 to set points that have not converged, negative values of
                  u10n and q10n to missing (default)
767
            set 1 to keep points
768
        L : str
sbiri's avatar
sbiri committed
769
           Monin-Obukhov length definition options
sbiri's avatar
sbiri committed
770
           "tsrv"  : default for "S80", "S88", "LP82", "YT96", "UA", "NCAR",
sbiri's avatar
sbiri committed
771 772 773
                     "C30", "C35"
           "Rb" : following ecmwf (IFS Documentation cy46r1), default for
                  "ecmwf", "Beljaars"
sbiri's avatar
sbiri committed
774 775 776
    Returns
    -------
        res : array that contains
sbiri's avatar
sbiri committed
777 778 779 780
                       1. momentum flux       (N/m^2)
                       2. sensible heat       (W/m^2)
                       3. latent heat         (W/m^2)
                       4. Monin-Obhukov length (m)
sbiri's avatar
sbiri committed
781
                       5. drag coefficient (cd)
sbiri's avatar
sbiri committed
782
                       6. neutral drag coefficient (cd10n)
sbiri's avatar
sbiri committed
783
                       7. heat exchange coefficient (ct)
sbiri's avatar
sbiri committed
784
                       8. neutral heat exchange coefficient (ct10n)
sbiri's avatar
sbiri committed
785
                       9. moisture exhange coefficient (cq)
sbiri's avatar
sbiri committed
786
                       10. neutral moisture exchange coefficient (cq10n)
sbiri's avatar
sbiri committed
787
                       11. star virtual temperatcure (tsrv)
sbiri's avatar
sbiri committed
788
                       12. star temperature (tsr)
789 790
                       13. star specific humidity (qsr)
                       14. star wind speed (usr)
sbiri's avatar
sbiri committed
791
                       15. momentum stability function (psim)
792 793
                       16. heat stability function (psit)
                       17. moisture stability function (psiq)
794
                       18. 10m neutral wind speed (u10n)
795
                       19. 10m neutral temperature (t10n)
sbiri's avatar
sbiri committed
796 797 798 799 800 801 802
                       20. 10m neutral specific humidity (q10n)
                       21. surface roughness length (zo)
                       22. heat roughness length (zot)
                       23. moisture roughness length (zoq)
                       24. wind speed at reference height (uref)
                       25. temperature at reference height (tref)
                       26. specific humidity at reference height (qref)
803 804 805 806 807 808 809 810 811 812 813 814 815 816
                       27. cool-skin temperature depression (dter)
                       28. cool-skin humidity depression (dqer)
                       29. warm layer correction (dtwl)
                       30. specific humidity of air (qair)
                       31. specific humidity at sea surface (qsea)
                       32. downward longwave radiation (Rl)
                       33. downward shortwave radiation (Rs)
                       34. downward net longwave radiation (Rnl)
                       35. gust wind speed (ug)
                       36. Bulk Richardson number (Rib)
                       37. relative humidity (rh)
                       38. thickness of the viscous layer (delta)
                       39. lv latent heat of vaporization (Jkg−1)
                       40. number of iterations until convergence
sbiri's avatar
sbiri committed
817
                       41. flag ("n": normal, "o": out of nominal range,
818
                                 "u": u10n<0, "q":q10n<0
819
                                 "m": missing,
820
                                 "l": Rib<-0.5 or Rib>0.2 or z/L>1000,
sbiri's avatar
sbiri committed
821
                                 "r" : rh>100%,
822
                                 "t" : t10n<173K or t10n>373K
823
                                 "i": convergence fail at n)
824

825
    2021 / Author S. Biri
826 827
    2021 / Restructured by R. Cornes
    2021 / Simplified by E. Kent
sbiri's avatar
sbiri committed
828
    """
sbiri's avatar
sbiri committed
829
    logging.basicConfig(filename='flux_calc.log', filemode="w",
830
                        format='%(asctime)s %(message)s', level=logging.INFO)
831
    logging.captureWarnings(True)
sbiri's avatar
sbiri committed
832 833 834 835 836 837

    iclass = globals()[meth]()
    iclass.add_gust(gust=gust)
    iclass.add_variables(spd, T, SST, lat=lat, hum=hum, P=P, L=L)
    iclass.get_heights(hin, hout)
    iclass.get_specHumidity(qmeth=qmeth)
sbiri's avatar
sbiri committed
838 839
    iclass.set_coolskin_warmlayer(wl=wl, cskin=cskin, skin=skin, Rl=Rl, Rs=Rs)
    iclass.iterate(tol=tol, maxiter=maxiter)
sbiri's avatar
sbiri committed
840 841
    resAll = iclass.get_output(out=out)

842
    return resAll