Beispiel #1
0
def soil_temperature(g, meteo, temp_sky_eff, soil_label_prefix='other'):
    """Computes soil temperature

    Args:
        g: a multiscale tree graph object
        meteo (DataFrame): forcing meteorological variables
        t_sky_eff (float): [°C] effective sky temperature
        soil_label_prefix(str): prefix of soil nodes

    Returns:
        (double): [°C] soil temperature

    Notes:
        Heat loss into deeper soil layers is not considered.

    """

    relative_humidity, atm_pressure, temp_air = [
        float(meteo[x]) for x in ('hs', 'Pa', 'Tac')
    ]

    temp_sky_eff = utils.celsius_to_kelvin(temp_sky_eff)

    soil_nodes = [
        g.node(vid) for vid in g.property('geometry')
        if g.node(vid).label.startswith(soil_label_prefix)
    ][0]
    temp_leaves = utils.celsius_to_kelvin(
        mean(list(g.property('Tlc').values())))

    shortwave_inc = old_div(soil_nodes.Ei, (0.48 * 4.6))  # Ei not Eabs
    temp_soil = soil_nodes.Tsoil if 'Tsoil' in soil_nodes.properties(
    ) else temp_air

    def _SoilEnergyX(temp_soil):
        shortwave_abs = (
            1 - 0.25
        ) * shortwave_inc  # 0.25 is rough estimation of albedo of a bare soil
        longwave_net = e_soil * sigma * (1. * e_sky * temp_sky_eff**4 +
                                         1. * e_leaf * temp_leaves**4 -
                                         temp_soil**4)
        latent_heat = old_div(
            -lambda_ * 0.06 * utils.vapor_pressure_deficit(
                temp_air, temp_soil, relative_humidity),
            atm_pressure)  # 0.06 is gM from Bailey 2016 AFM 218-219:146-160
        sensible_heat = -0.5 * Cp * utils.celsius_to_kelvin(
            temp_soil -
            temp_air)  # 0.5 is gH from Bailey 2016 AFM 218-219:146-160
        energy_balance = shortwave_abs + longwave_net + latent_heat + sensible_heat
        return energy_balance

    t_soil0 = utils.kelvin_to_celsius(
        optimize.newton_krylov(_SoilEnergyX,
                               utils.celsius_to_kelvin(temp_soil)))

    soil_nodes.Tsoil = t_soil0

    return t_soil0
Beispiel #2
0
def arrhenius_1(param_name, leaf_temperature, photo_params):
    """Computes the effect of temperature on the photosynthetic parameters `Tx`, `Kc`, and `Ko`.

    Args:
        param_name (str): name of the parameter to be considered, one of 'Tx', 'Kc', and 'Ko'
        leaf_temperature (float): [°C] leaf temperature
        photo_params (dict): default values at 25 °C of Farquhar's model (cf. :func:`par_photo_default`)

    Returns:
        (float): the value of the given :arg:`param_name` at the given :arg:`leaf_temperature`

    References:
        Bernacchi et al. (2003)
            In vivo temperature response functions of parameters required to model RuBP-limited photosynthesis.
            Plant, Cell and Environment 26, 1419 –  1430

    """

    param_list = {
        'Tx': ('Tx25', 'RespT_Tx'),
        'Kc': ('Kc25', 'RespT_Kc'),
        'Ko': ('Ko25', 'RespT_Ko')
    }

    temp_k = utils.celsius_to_kelvin(leaf_temperature)
    param_key = param_list[param_name][1]
    shape_param, activation_energy = [
        photo_params[param_key][x] for x in ('c', 'deltaHa')
    ]
    param_value = exp(shape_param - (activation_energy / (R * temp_k)))

    return param_value
Beispiel #3
0
def arrhenius_2(param_name, leaf_temperature, photo_params):
    """Computes the effect of temperature on the photosynthetic parameters `Vcmax`, `Jmax`, `TPUmax`, and `Rdmax`.

    Args:
        param_name (str): name of the parameter to be considered, one of `Vcmax`, `Jmax`, `TPUmax`, and `Rdmax`
        leaf_temperature (float): [°C] leaf temperature
        photo_params (dict): default values at 25 °C of Farquhar's model (cf. :func:`par_photo_default`)

    Returns:
        (float): the value of the given :arg:`param_name` at the given :arg:`leaf_temperature`

    References:
        Bernacchi et al. (2003)
            In vivo temperature response functions of parameters required to model RuBP-limited photosynthesis.
            Plant, Cell and Environment 26, 1419 –  1430

    """

    enthalpy_activation = photo_params['ds']
    enthalpy_deactivation = photo_params['dHd']

    param_list = {
        'Vcmax': ('Vcm25', 'RespT_Vcm'),
        'Jmax': ('Jm25', 'RespT_Jm'),
        'TPUmax': ('TPU25', 'RespT_TPU'),
        'Rdmax': ('Rd', 'RespT_Rd')}

    temp_k = utils.celsius_to_kelvin(leaf_temperature)
    param_key = param_list[param_name][1]
    param_value_at_25 = photo_params[param_list[param_name][0]]
    shape_param, activation_energy = [photo_params[param_key][x] for x in ('c', 'deltaHa')]
    param_value = param_value_at_25 * (exp(shape_param - (activation_energy / (R * temp_k)))) / (
            1. + exp((enthalpy_activation * temp_k - enthalpy_deactivation) / (R * temp_k)))

    return param_value
Beispiel #4
0
 def _SoilEnergyX(temp_soil):
     shortwave_abs = (
         1 - 0.25
     ) * shortwave_inc  # 0.25 is rough estimation of albedo of a bare soil
     longwave_net = e_soil * sigma * (1. * e_sky * temp_sky_eff**4 +
                                      1. * e_leaf * temp_leaves**4 -
                                      temp_soil**4)
     latent_heat = -lambda_ * 0.06 * utils.vapor_pressure_deficit(
         temp_air, temp_soil, relative_humidity
     ) / atm_pressure  # 0.06 is gM from Bailey 2016 AFM 218-219:146-160
     sensible_heat = -0.5 * Cp * utils.celsius_to_kelvin(
         temp_soil -
         temp_air)  # 0.5 is gH from Bailey 2016 AFM 218-219:146-160
     energy_balance = shortwave_abs + longwave_net + latent_heat + sensible_heat
     return energy_balance
Beispiel #5
0
def leaf_temperature(g,
                     meteo,
                     t_soil,
                     t_sky_eff,
                     t_init=None,
                     form_factors=None,
                     gbh=None,
                     ev=None,
                     ei=None,
                     solo=True,
                     ff_type=True,
                     leaf_lbl_prefix='L',
                     max_iter=100,
                     t_error_crit=0.01,
                     t_step=0.5):
    """Computes the temperature of each individual leaf and soil elements.

    Args:
        g: a multiscale tree graph object
        meteo (DataFrame): forcing meteorological variables
        t_soil (float): [°C] soil surface temperature
        t_sky_eff (float): [°C] effective sky temperature
        t_init(float or dict): [°C] temperature used for initialisation
        form_factors(3-tuple of float or dict): form factors for soil, sky and leaves.
            if None (default) (0.5, 0.5, 0.5) is used for all leaves
        gbh (float or dict): [W m-2 K-1] boundary layer conductance for heat
            if None (default) a default model is called with length=10cm and wind_speed as found in meteo
        ev (float or dict): [mol m-2 s-1] evaporation flux
            if None (default) evaporation is set to zero for all leaves
        ei (float or dict): [umol m-2 s-1] photosynthetically active radition (PAR) incident on leaves
            if None (default) PAR is set to zero for all leaves
        solo (bool):
            if True (default), calculates energy budget for each element assuming the temperatures of surrounding
                leaves as constant (from previous calculation step)
            if False, computes simultaneously all temperatures using `sympy.solvers.nsolve` (**very costly!!!**)
        ff_type (bool): form factor type flag. If true fform factor for a given leaf is expected to be a single value, or a dict of ff otherwxie
        leaf_lbl_prefix (str): the prefix of the leaf label
        max_iter (int): maximum allowed iteration (used only when :arg:`solo` is True)
        t_error_crit (float): [°C] maximum allowed error in leaf temperature (used only when :arg:`solo` is True)
        t_step (float): [°C] maximum temperature step between two consecutive iterations

    Returns:
        (dict): [°C] the tempearture of individual leaves given as the dictionary keys
        (int): [-] the number of iterations (not None only when :arg:`solo` is True)

    """

    leaves = get_leaves(g, leaf_lbl_prefix)
    it = 0

    if t_init is None:
        t_init = meteo.Tac[0]
    if form_factors is None:
        form_factors = 0.5, 0.5, 0.5
    if gbh is None:
        gbh = _gbH(0.1, meteo.u[0])
    if ev is None:
        ev = 0
    if ei is None:
        ei = 0

    k_soil, k_sky, k_leaves = form_factors
    properties = {}
    for what in ('t_init', 'gbh', 'ev', 'ei', 'k_soil', 'k_sky', 'k_leaves'):
        val = eval(what)
        if isinstance(val, dict):
            properties[what] = val
        else:
            properties[what] = {vid: val for vid in leaves}

    # macro-scale climatic data
    temp_sky = utils.celsius_to_kelvin(t_sky_eff)
    temp_air = utils.celsius_to_kelvin(meteo.Tac[0])
    temp_soil = utils.celsius_to_kelvin(t_soil)

    # initialisation
    t_prev = properties['t_init']

    # iterative calculation of leaves temperature
    if solo:
        t_error_trace = []
        it_step = t_step
        for it in range(max_iter):
            t_dict = {}

            for vid in leaves:
                shortwave_inc = properties['ei'][vid] / (0.48 * 4.6
                                                         )  # Ei not Eabs

                ff_sky = properties['k_sky'][vid]
                ff_leaves = properties['k_leaves'][vid]
                ff_soil = properties['k_soil'][vid]

                gb_h = properties['gbh'][vid]
                evap = properties['ev'][vid]
                t_leaf = t_prev[vid]

                if not ff_type:
                    longwave_grain_from_leaves = -sigma * sum([
                        ff_leaves[ivid] *
                        (utils.celsius_to_kelvin(t_prev[ivid]))**4
                        for ivid in ff_leaves
                    ])
                else:
                    longwave_grain_from_leaves = ff_leaves * sigma * (
                        utils.celsius_to_kelvin(t_leaf))**4

                def _VineEnergyX(t_leaf):
                    shortwave_abs = a_glob * shortwave_inc
                    longwave_net = e_leaf * (ff_sky * e_sky * sigma * temp_sky ** 4 +
                                             e_leaf * longwave_grain_from_leaves +
                                             ff_soil * e_soil * sigma * temp_soil ** 4) \
                                   - 2 * e_leaf * sigma * t_leaf ** 4
                    latent_heat_loss = -lambda_ * evap
                    sensible_heat_net = -gb_h * (t_leaf - temp_air)
                    energy_balance = shortwave_abs + longwave_net + latent_heat_loss + sensible_heat_net
                    return energy_balance

                t_leaf0 = utils.kelvin_to_celsius(
                    optimize.newton_krylov(_VineEnergyX,
                                           utils.celsius_to_kelvin(t_leaf)))

                t_dict[vid] = t_leaf0

            t_new = t_dict

            # evaluation of leaf temperature conversion criterion
            error_dict = {vtx: abs(t_prev[vtx] - t_new[vtx]) for vtx in leaves}

            t_error = max(error_dict.values())
            t_error_trace.append(t_error)

            if t_error < t_error_crit:
                break
            else:
                try:
                    if abs(t_error_trace[-1] -
                           t_error_trace[-2]) < t_error_crit:
                        it_step = max(0.01, it_step / 2.)
                except IndexError:
                    pass

                t_next = {}
                for vtx_id in t_new.keys():
                    tx = t_prev[vtx_id] + it_step * (t_new[vtx_id] -
                                                     t_prev[vtx_id])
                    t_next[vtx_id] = tx

                t_prev = t_next

    # matrix iterative calculation of leaves temperature ('not solo' case)
    else:
        it = 1
        t_lst = []
        t_dict = {vid: Symbol('t%d' % vid) for vid in leaves}

        eq_lst = []
        t_leaf_lst = []
        for vid in leaves:
            shortwave_inc = properties['ei'][vid] / (0.48 * 4.6)  # Ei not Eabs
            ff_sky = properties['k_sky'][vid]
            ff_leaves = properties['k_leaves'][vid]

            ff_soil = properties['k_soil'][vid]
            gb_h = properties['gbh'][vid]
            evap = properties['ev'][vid]
            t_leaf = t_prev[vid]

            t_leaf_lst.append(t_leaf)
            t_lst.append(t_dict[vid])

            eq_aux = 0.
            for ivid in ff_leaves:
                if not g.node(ivid).label.startswith('soil'):
                    eq_aux += -ff_leaves[ivid] * ((t_dict[ivid])**4)

            eq = (a_glob * shortwave_inc + e_leaf * sigma *
                  (ff_sky * e_sky *
                   (temp_sky**4) + e_leaf * eq_aux + ff_soil * e_soil *
                   (temp_sky**4) - 2 * (t_dict[vid])**4) - lambda_ * evap -
                  gb_h * Cp * (t_dict[vid] - temp_air))

            eq_lst.append(eq)

        tt = time.time()
        t_leaf0_lst = nsolve(eq_lst, t_lst, t_leaf_lst, verify=False) - 273.15
        print("---%s seconds ---" % (time.time() - tt))

        t_new = {}
        for ivid, vid in enumerate(leaves):
            t_new[vid] = float(t_leaf0_lst[ivid])
            ivid += 1

    return t_new, it