AirSeaFluxCode.py 35.7 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 13
class S88:
    def _wind_iterate(self, ind):
        if self.gust[0] == 1:
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
sbiri's avatar
sbiri committed
25 26 27
            self.u10n[ind] = self.wind[ind]-self.usr[ind]/kappa/np.power(
                self.GustFact[ind],0.5)*(np.log(self.h_in[0, ind]/self.ref10) -
                                         self.psim[ind])
sbiri's avatar
sbiri committed
28
            # option to not remove GustFact
sbiri's avatar
sbiri committed
29 30 31 32 33 34 35 36
            # self.u10n[ind] = self.wind[ind]-self.usr[ind]/kappa * \
            #     (np.log(self.h_in[0, ind]/self.ref10) - self.psim[ind])
            # these lines integrate up from the surface - doesn't work when
            # gustiness is on
            # 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
37
        else:
sbiri's avatar
sbiri committed
38 39
            # not sure this is needed - perhaps only to remove effects of
            # initalisation of wind
sbiri's avatar
sbiri committed
40 41 42 43 44 45
            self.wind[ind] = np.copy(self.spd[ind])

    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
46
        self.h_out = get_heights(self.hout, 1)
sbiri's avatar
sbiri committed
47

sbiri's avatar
sbiri committed
48 49 50
    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
51 52
        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
53 54 55
        self.dq_in = self.qair-self.qsea
        self.dq_full = self.qair-self.qsea

sbiri's avatar
sbiri committed
56 57 58 59
        # 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
60 61
        self.dt_in = self.theta-self.SST
        self.dt_full = self.theta-self.SST
sbiri's avatar
sbiri committed
62 63

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

sbiri's avatar
sbiri committed
69 70 71 72 73 74 75 76
        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
77 78
        self.cskin = cskin
        self.skin = skin
sbiri's avatar
sbiri committed
79 80
        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
sbiri's avatar
sbiri committed
81

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

sbiri's avatar
sbiri committed
89

sbiri's avatar
sbiri committed
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
    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
109 110
            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
111 112 113 114 115 116 117 118 119 120
            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
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
                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
142 143
        # 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
144 145 146 147
        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
148
        Rb = self.grav*self.h_in[1]*self.dtv/(self.T*np.power(self.wind, 2))
sbiri's avatar
sbiri committed
149
        self.monob = self.h_in[1]/12.0/Rb  # eq. 12 Grachev & Fairall 1997   # DO.THIS
sbiri's avatar
sbiri committed
150
        # where does 12.0 come from??
sbiri's avatar
sbiri committed
151 152 153

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

154 155
        # 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
156
        if self.cskin + self.wl > 0:
sbiri's avatar
sbiri committed
157 158
            self.dter, self.tkt, self.dtwl = [
                dummy_array(x) for x in (-0.3, 0.001, 0.3)]
159 160
            self.dqer = get_dqer(self.dter, self.SST, self.qsea,
                                 self.lv)
sbiri's avatar
sbiri committed
161 162
            self.Rnl = 0.97*(self.Rl-5.67e-8*np.power(
                self.SST-0.3*self.cskin, 4))
sbiri's avatar
sbiri committed
163 164
            self.Qs = 0.945*self.Rs
        else:
sbiri's avatar
sbiri committed
165 166 167 168
            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
169 170 171 172 173
        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
174 175
        self.cd10n, self.zo = cdn_calc(
            self.u10n, self.usr, self.theta, self.grav, self.meth)
sbiri's avatar
sbiri committed
176 177 178
        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
179 180 181 182
        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
183 184 185 186 187 188 189
        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
190 191 192 193
        # 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
194 195
        assert tol[0] in ['flux', 'ref', 'all'], "unknown tolerance input"

sbiri's avatar
sbiri committed
196 197
        old_vars = {"flux": ["tau", "sensible", "latent"],
                    "ref": ["u10n", "t10n", "q10n"]}
sbiri's avatar
sbiri committed
198 199 200
        old_vars["all"] = old_vars["ref"] + old_vars["flux"]
        old_vars = old_vars[tol[0]]

sbiri's avatar
sbiri committed
201 202
        new_vars = {"flux": ["tau", "sensible", "latent"],
                    "ref": ["u10n", "t10n", "q10n"]}
sbiri's avatar
sbiri committed
203 204 205 206 207 208 209 210 211 212 213
        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
214 215
        self.tsrv, self.psim, self.psit, self.psiq = [
            np.zeros(self.arr_shp)*self.msk for _ in range(4)]
sbiri's avatar
sbiri committed
216 217

        # extreme values for first comparison
218 219
        dummy_array = lambda val: np.full(self.arr_shp, val)*self.msk
        # you can use def instead of lambda
sbiri's avatar
sbiri committed
220 221 222
        # 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
223 224 225 226 227 228 229 230 231 232

        # 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
233
            old = np.array([np.copy(getattr(self, i)) for i in old_vars])
sbiri's avatar
sbiri committed
234 235

            # Calculate cdn
sbiri's avatar
sbiri committed
236 237 238 239 240
            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
241
                logging.info('break %s at iteration %s cd10n<0', meth, it)
sbiri's avatar
sbiri committed
242 243 244 245 246 247 248
                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
249 250
            # 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
251 252 253 254 255 256
            # 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
257
            # lines below with and without gustfactor
sbiri's avatar
sbiri committed
258 259 260 261 262
            self.u10n[ind] = self.wind[ind]-self.usr[ind]/kappa/np.power(
                self.GustFact[ind], 0.5)*(
                    np.log(self.h_in[0, ind]/self.ref10)-self.psim[ind])
            # 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
263 264 265


            # temperature
sbiri's avatar
sbiri committed
266 267 268 269 270 271 272 273
            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
274
            # wind
sbiri's avatar
sbiri committed
275 276 277 278 279 280 281 282
            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
283 284 285 286 287 288 289


            # Some parameterizations set a minimum on parameters
            try:
                self._minimum_params()
            except AttributeError:
                pass
sbiri's avatar
sbiri committed
290 291 292

            self.dt_full[ind] = self.dt_in[ind] - \
                self.dter[ind]*self.cskin - self.dtwl[ind]*self.wl
sbiri's avatar
sbiri committed
293
            self.dq_full[ind] = self.dq_in[ind] - self.dqer[ind]*self.cskin
sbiri's avatar
sbiri committed
294 295 296 297 298
            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
299 300 301

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

sbiri's avatar
sbiri committed
303
            # Logging output
sbiri's avatar
sbiri committed
304 305 306 307 308 309 310 311
            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
312 313

            if self.cskin + self.wl > 0:
sbiri's avatar
sbiri committed
314 315 316
                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
317 318
            # not sure how to handle lapse/potemp
            # well-mixed in potential temperature ...
319
            self.t10n[ind] = self.theta[ind]-self.tlapse[ind]*self.ref10 - \
sbiri's avatar
sbiri committed
320
                self.tsr[ind]/kappa * \
321 322
                    (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
323
                (np.log(self.h_in[2, ind]/self.ref10)-self.psiq[ind])
sbiri's avatar
sbiri committed
324 325

            # update stability info
sbiri's avatar
sbiri committed
326 327 328 329 330 331
            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
332
            if self.L == "tsrv":
sbiri's avatar
sbiri committed
333 334 335
                self.monob[ind] = get_Ltsrv(
                    self.tsrv[ind], self.grav[ind], self.tv[ind],
                    self.usr[ind])
sbiri's avatar
sbiri committed
336
            else:
sbiri's avatar
sbiri committed
337 338 339
                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
340 341 342

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

sbiri's avatar
sbiri committed
344
            # make sure you allow small negative values convergence
sbiri's avatar
sbiri committed
345 346
            if it == 1:
                self.u10n = np.where(self.u10n < 0, 0.5, self.u10n)
sbiri's avatar
sbiri committed
347

sbiri's avatar
sbiri committed
348
            self.itera[ind] = np.full(1, it)
sbiri's avatar
sbiri committed
349
            # remove effect of gustiness following Fairall et al. (2003)
sbiri's avatar
sbiri committed
350 351
            # usr is divided by (GustFact)^0.5 (here applied to sensible and
            # latent as well as tau)
sbiri's avatar
sbiri committed
352 353
            # GustFact should be 1 if gust is OFF
            self.tau = self.rho*np.power(self.usr, 2)/self.GustFact
sbiri's avatar
sbiri committed
354 355 356 357
            self.sensible = self.rho*self.cp*self.usr / \
                np.power(self.GustFact, 0.5)*self.tsr
            self.latent = self.rho*self.lv*self.usr / \
                np.power(self.GustFact, 0.5)*self.qsr
sbiri's avatar
sbiri committed
358
            # or leave as it is - gusty wind speed, or no gust
sbiri's avatar
sbiri committed
359 360 361
            # 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
362 363

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

sbiri's avatar
sbiri committed
366
            if it > 2:  # force at least three iterations
367
                d = np.abs(new-old)  # change over this iteration
sbiri's avatar
sbiri committed
368 369 370 371
                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
372 373 374 375 376 377 378 379

            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
380 381
        logging.info('method %s | # of points that did not converge :%s \n',
                     self.meth, self.ind[0].size)
sbiri's avatar
sbiri committed
382 383 384


    def _get_humidity(self):
385
        """Calculate RH used for flagging purposes & output."""
sbiri's avatar
sbiri committed
386
        if self.hum[0] in ('rh', 'no'):
sbiri's avatar
sbiri committed
387
            self.rh = self.hum[1]
sbiri's avatar
sbiri committed
388
        elif self.hum[0] == 'Td':
sbiri's avatar
sbiri committed
389 390 391
            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))
392
            # T = np.copy(self.T)
sbiri's avatar
sbiri committed
393 394 395
            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
396

397 398 399
    def _flag(self, out=0):
        """Set the general flags."""
        flag = np.full(self.arr_shp, "n", dtype="object")
sbiri's avatar
sbiri committed
400

sbiri's avatar
sbiri committed
401 402 403 404 405
        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
406
            else:
sbiri's avatar
sbiri committed
407 408
                flag = np.where(
                    np.isnan(self.spd+self.T+self.SST+self.P), "m", flag)
sbiri's avatar
sbiri committed
409
        else:
sbiri's avatar
sbiri committed
410 411
            if self.cskin == 1:
                flag = np.where(np.isnan(
412 413
                    self.spd+self.T+self.SST+self.hum[1]+self.P +
                    self.Rs+self.Rl), "m", flag)
sbiri's avatar
sbiri committed
414
            else:
sbiri's avatar
sbiri committed
415 416 417
                flag = np.where(
                    np.isnan(self.spd+self.T+self.SST+self.hum[1]+self.P),
                    "m", flag)
sbiri's avatar
sbiri committed
418 419 420 421

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

        # u10n flag
sbiri's avatar
sbiri committed
422 423 424 425 426
        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
427
        # q10n flag
sbiri's avatar
sbiri committed
428 429 430 431 432
        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))
433

sbiri's avatar
sbiri committed
434
        # t10n flag
sbiri's avatar
sbiri committed
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
        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
452
        else:
sbiri's avatar
sbiri committed
453 454 455 456 457
            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
458 459
        self.flag = flag

sbiri's avatar
sbiri committed
460 461 462
    def get_output(self, out=0):

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

464
        self._get_humidity()  # Get the Relative humidity
sbiri's avatar
sbiri committed
465 466 467 468
        self._flag(out=out)  # Get flags

        # remove effect of gustiness following Fairall et al. (2003)
        # usr is divided by (GustFact)^0.5
sbiri's avatar
sbiri committed
469 470 471
        self.uref = self.spd-self.usr/kappa/np.power(self.GustFact, 0.5) * \
            (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
472
        # include lapse rate adjustment as theta is well-mixed
sbiri's avatar
sbiri committed
473 474 475 476 477 478
        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
479

sbiri's avatar
sbiri committed
480 481 482
        if self.wl == 0:
            self.dtwl = np.zeros(self.T.shape)*self.msk
            # reset to zero if not used
sbiri's avatar
sbiri committed
483 484

        # Do not calculate lhf if a measure of humidity is not input
sbiri's avatar
sbiri committed
485 486 487 488 489
        # 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
490 491 492 493 494

        # 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
495 496
        # 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
497 498 499 500 501 502
        try:
            self._class_flag()
        except AttributeError:
            pass

        # Combine all output variables into a pandas array
sbiri's avatar
sbiri committed
503 504 505
        res_vars = ("tau", "sensible", "latent", "monob", "cd", "cd10n", "ct",
                    "ct10n", "cq", "cq10n", "tsrv", "tsr", "qsr", "usr",
                    "psim", "psit", "psiq", "u10n", "t10n", "q10n", "zo",
506
                    "zot", "zoq", "uref", "tref", "qref", "dter",
sbiri's avatar
sbiri committed
507
                    "dqer", "dtwl", "qair", "qsea", "Rl", "Rs", "Rnl", "ug",
508
                    "Rb", "rh", "tkt", "lv", "itera")
sbiri's avatar
sbiri committed
509 510

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

sbiri's avatar
sbiri committed
514
        if out == 0:
sbiri's avatar
sbiri committed
515 516
            res[:, self.ind] = np.nan
            # set missing values where data have non acceptable values
sbiri's avatar
sbiri committed
517
            if self.hum[0] != 'no':
518 519 520 521 522 523
                res[:-1] = np.asarray([np.where(self.q10n < 0, np.nan, res[i][:])
                                 for i in range(len(res_vars)-1)])
                # len(res_vars)-1 instead of len(res_vars) in order to keep
                # itera= -1 for no convergence
            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
524
        else:
sbiri's avatar
sbiri committed
525 526 527 528
            warnings.warn("Warning: the output will contain values for points"
                          " that have not converged and negative values "
                          "(if any) for u10n/q10n")

529 530
        resAll = pd.DataFrame(data=res.T, index=range(self.nlen),
                              columns=res_vars)
sbiri's avatar
sbiri committed
531 532 533 534 535 536 537 538

        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
539 540 541 542
        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
543 544 545
        self.arr_shp = spd.shape
        self.nlen = len(spd)
        self.spd = spd
sbiri's avatar
sbiri committed
546
        # self.T = T
sbiri's avatar
sbiri committed
547
        self.T = np.where(T < 200, np.copy(T)+CtoK, np.copy(T))
sbiri's avatar
sbiri committed
548
        self.hum = ['no', np.full(SST.shape, 80)] if hum is None else hum
sbiri's avatar
sbiri committed
549
        self.SST = np.where(SST < 200, np.copy(SST)+CtoK, np.copy(SST))
sbiri's avatar
sbiri committed
550
        self.lat = np.full(self.arr_shp, 45) if lat is None else lat
sbiri's avatar
sbiri committed
551 552 553 554
        self.grav = gc(self.lat)
        self.P = np.full(n, 1013) if P is None else P

        # mask to preserve missing values when initialising variables
sbiri's avatar
sbiri committed
555
        self.msk = np.empty(SST.shape)
sbiri's avatar
sbiri committed
556 557 558
        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
559 560
    def add_gust(self, gust=None):
        if np.all(gust is None):
sbiri's avatar
sbiri committed
561 562 563
            try:
                gust = self.default_gust
            except AttributeError:
sbiri's avatar
sbiri committed
564
                gust = [1, 1.2, 800]
sbiri's avatar
sbiri committed
565 566
        elif ((np.size(gust) < 3) and (gust == 0)):
            gust = [0, 0, 0]
sbiri's avatar
sbiri committed
567

sbiri's avatar
sbiri committed
568
        assert np.size(gust) == 3, "gust input must be a 3x1 array"
sbiri's avatar
sbiri committed
569
        assert gust[0] in [0, 1], "gust at position 0 must be 0 or 1"
sbiri's avatar
sbiri committed
570 571 572
        self.gust = gust

    def _class_flag(self):
sbiri's avatar
sbiri committed
573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
        """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
597 598 599 600
                                      self.flag+[","]+["o"], self.flag))

    def __init__(self):
        self.meth = "S88"
601
        self.default_gust = [0, 0, 0]
sbiri's avatar
sbiri committed
602 603 604 605 606

class S80(S88):

    def __init__(self):
        self.meth = "S80"
sbiri's avatar
sbiri committed
607 608
        self.u_lo = [6, 6]
        self.u_hi = [22, 22]
609
        self.default_gust = [0, 0, 0]
sbiri's avatar
sbiri committed
610

sbiri's avatar
sbiri committed
611 612 613 614
class YT96(S88):

    def __init__(self):
        self.meth = "YT96"
615 616
        self.default_gust = [0, 0, 0]
        # no limits to u range as we use eq. 21 for cdn
sbiri's avatar
sbiri committed
617 618
        # self.u_lo = [0, 3]
        # self.u_hi = [26, 26]
sbiri's avatar
sbiri committed
619 620

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

sbiri's avatar
sbiri committed
622 623
    def __init__(self):
        self.meth = "LP82"
624
        self.default_gust = [0, 0, 0]
sbiri's avatar
sbiri committed
625 626
        self.u_lo = [3, 3]
        self.u_hi = [25, 25]
sbiri's avatar
sbiri committed
627 628 629 630 631 632 633 634 635 636


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
637

sbiri's avatar
sbiri committed
638 639
    def __init__(self):
        self.meth = "NCAR"
640
        self.default_gust = [0, 0, 0]
sbiri's avatar
sbiri committed
641 642
        self.u_lo = [0.5, 0.5]
        self.u_hi = [999, 999]
sbiri's avatar
sbiri committed
643 644

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

sbiri's avatar
sbiri committed
646 647
    def __init__(self):
        self.meth = "UA"
sbiri's avatar
sbiri committed
648 649 650
        self.default_gust = [1, 1, 1000]
        self.u_lo = [-999, -999]
        self.u_hi = [18, 18]
sbiri's avatar
sbiri committed
651 652 653


class C30(S88):
sbiri's avatar
sbiri committed
654 655
    # 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
656 657 658

    def __init__(self):
        self.meth = "C30"
sbiri's avatar
sbiri committed
659
        self.default_gust = [1, 1.2, 600]
sbiri's avatar
sbiri committed
660
        self.skin = "C35"
sbiri's avatar
sbiri committed
661 662 663 664

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

class ecmwf(C30):
sbiri's avatar
sbiri committed
669 670 671
    # 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
672 673 674

    def __init__(self):
        self.meth = "ecmwf"
sbiri's avatar
sbiri committed
675
        self.default_gust = [1, 1, 1000]
sbiri's avatar
sbiri committed
676
        self.skin = "ecmwf"
sbiri's avatar
sbiri committed
677 678

class Beljaars(C30):
sbiri's avatar
sbiri committed
679 680 681
    # 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
682

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


689
def AirSeaFluxCode(spd, T, SST, lat=None, hum=None, P=None, hin=18, hout=10,
sbiri's avatar
sbiri committed
690
                   Rl=None, Rs=None, cskin=0, skin=None, wl=0, gust=None,
sbiri's avatar
sbiri committed
691 692
                   meth="S88", qmeth="Buck2", tol=None, maxiter=10, out=0,
                   L=None):
693
    """
sbiri's avatar
sbiri committed
694 695 696
    Calculate turbulent surface fluxes using different parameterizations.

    Calculate height adjusted values for spd, T, q
sbiri's avatar
sbiri committed
697 698 699 700 701 702 703 704 705 706 707

    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
708 709 710
            latitude (deg), default 45deg
        hum : float
            humidity input switch 2x1 [x, values] default is relative humidity
711 712 713
            x='rh' : relative humidity in %
            x='q' : specific humidity (g/kg)
            x='Td' : dew point temperature (K)
sbiri's avatar
sbiri committed
714
        P : float
715
            air pressure (hPa), default 1013hPa
sbiri's avatar
sbiri committed
716
        hin : float
717
            sensor heights in m (array 3x1 or 3xn), default 18m
sbiri's avatar
sbiri committed
718 719 720 721 722 723
        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
724
        cskin : int
725 726
            0 switch cool skin adjustment off, else 1
            default is 1
727 728 729 730
        skin : str
            cool skin method option "C35", "ecmwf" or "Beljaars"
        wl : int
            warm layer correction default is 0, to switch on set to 1
731
        gust : int
732 733
            4x1 [x, gf, beta, zi] x=1 to include the effect of gustiness,
            else 0; gf="gfON" or "gfOFF" to use gustiness factor;
734
            beta gustiness parameter, beta=1 for UA, beta=1.2 for COARE
735
            zi PBL height (m) 600 for COARE, 1000 for UA and ecmwf
736
            default for COARE [1, 1.2, 600]
737
            default for UA, ecmwf [1, 1, 1000]
738
            else default is switched OFF
739
        meth : str
sbiri's avatar
sbiri committed
740
            "S80", "S88", "LP82", "YT96", "UA", "NCAR", "C30", "C35",
741
            "ecmwf", "Beljaars"
742 743
        qmeth : str
            is the saturation evaporation method to use amongst
744 745
            "HylandWexler","Hardy","Preining","Wexler","GoffGratch","WMO",
            "MagnusTetens","Buck","Buck2","WMO2018","Sonntag","Bolton",
746 747 748 749 750 751 752
            "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
753 754
                    adjustment lim1-6
           default is tol=['all', 0.01, 0.01, 1e-05, 1e-3, 0.1, 0.1]
sbiri's avatar
sbiri committed
755 756
        maxiter : int
            number of iterations (default = 10)
757
        out : int
758 759
            set 0 to set points that have not converged, negative values of
                  u10n and q10n to missing (default)
760
            set 1 to keep points
761
        L : str
sbiri's avatar
sbiri committed
762
           Monin-Obukhov length definition options
sbiri's avatar
sbiri committed
763
           "tsrv"  : default for "S80", "S88", "LP82", "YT96", "UA", "NCAR",
sbiri's avatar
sbiri committed
764 765 766
                     "C30", "C35"
           "Rb" : following ecmwf (IFS Documentation cy46r1), default for
                  "ecmwf", "Beljaars"
sbiri's avatar
sbiri committed
767 768 769
    Returns
    -------
        res : array that contains
sbiri's avatar
sbiri committed
770 771 772 773
                       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
774
                       5. drag coefficient (cd)
sbiri's avatar
sbiri committed
775
                       6. neutral drag coefficient (cd10n)
sbiri's avatar
sbiri committed
776
                       7. heat exchange coefficient (ct)
sbiri's avatar
sbiri committed
777
                       8. neutral heat exchange coefficient (ct10n)
sbiri's avatar
sbiri committed
778
                       9. moisture exhange coefficient (cq)
sbiri's avatar
sbiri committed
779
                       10. neutral moisture exchange coefficient (cq10n)
sbiri's avatar
sbiri committed
780
                       11. star virtual temperatcure (tsrv)
sbiri's avatar
sbiri committed
781
                       12. star temperature (tsr)
782 783
                       13. star specific humidity (qsr)
                       14. star wind speed (usr)
sbiri's avatar
sbiri committed
784
                       15. momentum stability function (psim)
785 786
                       16. heat stability function (psit)
                       17. moisture stability function (psiq)
787
                       18. 10m neutral wind speed (u10n)
788
                       19. 10m neutral temperature (t10n)
sbiri's avatar
sbiri committed
789 790 791 792 793 794 795
                       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)
796 797 798 799 800 801 802 803 804 805 806 807 808 809
                       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
810
                       41. flag ("n": normal, "o": out of nominal range,
811
                                 "u": u10n<0, "q":q10n<0
812
                                 "m": missing,
813
                                 "l": Rib<-0.5 or Rib>0.2 or z/L>1000,
sbiri's avatar
sbiri committed
814
                                 "r" : rh>100%,
815
                                 "t" : t10n<173K or t10n>373K
816
                                 "i": convergence fail at n)
817

818
    2021 / Author S. Biri
819 820
    2021 / Restructured by R. Cornes
    2021 / Simplified by E. Kent
sbiri's avatar
sbiri committed
821
    """
sbiri's avatar
sbiri committed
822
    logging.basicConfig(filename='flux_calc.log', filemode="w",
823
                        format='%(asctime)s %(message)s', level=logging.INFO)
824
    logging.captureWarnings(True)
sbiri's avatar
sbiri committed
825 826 827 828 829 830

    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
831 832
    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
833 834
    resAll = iclass.get_output(out=out)

835
    return resAll