Source code for transfer

"""
    transfer
    ********

    Handles transfer of LightVegeManager results to plant Models.

    Currently, it manages CN-Wheat and l-egume. Only l-egume needs additionnal processes to
    converts Dataframe results in a compatible format.

    l-egume expects the absorb PAR either per plant or locally following a grid of voxels, and the transmitted PAR
    locally following a grid of voxels. Here, we will convert and transform results for CARIBU and RATP to those l-egume format.
"""
import numpy
import scipy

[docs]def transfer_ratp_legume(m_lais, energy, ratp_grid, voxels_outputs, nb0, epsilon=1e-8): """Transfers LightVegeManager outputs from RATP to l-egume Absorb and transmitted PAR will follow a RATP grid of voxels, matching the dimensions of intern l-egume grid of voxels. :param m_lais: leaf area represented in a numpy.array of dimension [number of species, number of z layers, number of y layers, number of x layers] :type m_lais: numpy.array :param energy: input energy :type energy: float :param ratp_grid: RATP grid of voxels :type ratp_grid: pyratp.grid :param voxels_outputs: results from LightVegeManager :type voxels_outputs: pandas.Dataframe :param nb0: number of empty layers from top of the canopy and maximum z layers in m_lais :type nb0: int :param epsilon: criteria of minimum intercepted portion of PAR in a non empty voxel :type epsilon: float :return: two array with lighting informations * ``res_abs_i``: absorb PAR in each voxel for RATP grid of voxels. It has the same dimensions as ``m_lais`` * ``res_trans``: transmitted PAR in each voxel, i.e. the energy leaving the voxels from input rays. This value is not dependent on specy dimensions are (number of z layers, number of y layers, number of x layers) :rtype: numpy.array, numpy.array """ # initialize absorb energy array res_abs_i = numpy.zeros((m_lais.shape[0], m_lais.shape[1], m_lais.shape[2], m_lais.shape[3])) # voxel top side area dS = ratp_grid.dx * ratp_grid.dy res_trans = numpy.ones((m_lais.shape[1], m_lais.shape[2], m_lais.shape[3])) # maximum transmitted energy is total incoming energy per area res_trans = res_trans * (energy * dS) for ix in range(m_lais.shape[3]): for iy in range(m_lais.shape[2]): for iz in range(ratp_grid.njz): legume_iz = iz + nb0 condition_x = voxels_outputs.Nx == m_lais.shape[2] - iy vox_data = voxels_outputs[condition_x & (voxels_outputs.Ny == ix + 1) & (voxels_outputs.Nz == iz + 1)] if not vox_data.empty : a = min(sum(vox_data["Transmitted"]), dS) res_trans[legume_iz, iy, ix] = energy * a s_entity = 0 for k in range(m_lais.shape[0]): s_entity += m_lais[k][legume_iz][iy][ix] if s_entity > 0.0: for ie in range(m_lais.shape[0]): if len(vox_data) > 0: v_dat = vox_data[vox_data.VegetationType == ie + 1] v = v_dat["Intercepted"].values[0] if v > epsilon: res_abs_i[ie, legume_iz, iy, ix] = energy * v # if a voxel has leaf area > 0, it must have a minimum intercepted energy value else: res_abs_i[ie, legume_iz, iy, ix] = epsilon return res_abs_i, res_trans
[docs]def transfer_caribu_legume( energy, skylayer, id, elements_outputs, sensors_outputs, sensors_dxyz, sensors_nxyz, m_lais, list_invar, list_lstring, list_dicFeuilBilanR, infinite, epsilon, ): """Transfers LightVegeManager outputs from CARIBU to l-egume We will update list_invar which stores the total intercepted energy for each plant, and return an array storing transmitted energy following the intern grid of voxels in l-egume. To do so, we used virtual sensors in CARIBU to get incoming radiations in selected locations. :param energy: input energy :type energy: float :param skylayer: number of empty layers from top of the canopy and maximum z layers in m_lais :type skylayer: int :param id: list of indices of input scenes associated with l-egume :type id: list of int :param elements_outputs: Dataframe results of elements formatted by :func:outputs.out_caribu_elements :type elements_outputs: pandas.Dataframe :param sensors_outputs: lighting results of virtual sensors form CARIBU in the format for each bandwidth computed, .. code-block:: sensors_outputs[band+" Eabs"] = {sensor_id : energy} sensors_outputs[band+" Ei"] = {sensor_id : energy} :type sensors_outputs: dict of list :param sensors_dxyz: size of sides of a voxel in the grid of virtual sensors [dx, dy, dz] :type sensors_dxyz: list :param sensors_nxyz: number of sensors in each direction in the grid [nx, ny, nz] :type sensors_nxyz: list :param m_lais: leaf area represented in a numpy.array of dimension [number of species, number of z layers, number of y layers, number of x layers] :type m_lais: numpy.array :param list_invar: from l-egume, each element corresponds to an input specy of l-egume. Each element is a dict invar stores instant intern variables of l-egume. :type list_invar: list of dict :param list_lstring: from l-egume, each element corresponds to an input specy of l-egume. Each element is a dict lstring stores the l-system of each plant :type list_lstring: list of dict :param list_dicFeuilBilanR: from l-egume, each element corresponds to an input specy of l-egume. Each element is a dict dicFeuiBilanR stores correspondances between voxels grid and each plant :type list_dicFeuilBilanR: list of dict :param infinite: if the user wishes to activate infinitisation of the grid :type infinite: bool :param epsilon: criteria of minimum intercepted portion of PAR in a voxel (if res_abs_i is zero, l-egume will crash) :type epsilon: float :raises ValueError: Virtual sensors and finite scene doesn't work yet with CARIBU :return: * it updates ``list_invar`` and its key entries ``"parap"`` and ``"parip"``, each element if the scipy.array is the sum of all intercepted energy for each plant. This process is a rewrite of ``calc_paraF`` in ShootMorpho.py module of l-egume, adapted to LightVegeManager numerotation of triangles * ``res_trans`` an array of transmitted energy for each voxel in a grid of dimensions ``sensors_dxyz * sensors_nxyz`` :rtype: numpy.array """ ## Absorb radiations for each plant of each specy for k in range(len(list_invar)): # initialize absorb energy nplantes = len(list_invar[k]["Hplante"]) list_invar[k]["parap"] = scipy.array([0.0] * nplantes) list_invar[k]["parip"] = scipy.array([0.0] * nplantes) if id == None: filter = elements_outputs.VegetationType == k ent_organs_outputs = elements_outputs[filter] elif type(id) == list or type(id) == tuple: filter = elements_outputs.VegetationType == id[k] ent_organs_outputs = elements_outputs[filter] # non empty scene for i in range(len(ent_organs_outputs)): organe_id = int(ent_organs_outputs.iloc[i]["Organ"]) # PAR in W/m² par_intercept = ent_organs_outputs.iloc[i]["par Ei"] * energy S_leaf = ent_organs_outputs.iloc[i]["Area"] id_plante = list_lstring[k][organe_id][0] p_s = par_intercept * S_leaf a = float(list_invar[k]["parip"][id_plante]) list_invar[k]["parip"][id_plante] = a + p_s # we remove senescent leaves if list_lstring[k][organe_id][9] != "sen": a = float(list_invar[k]["parap"][id_plante]) list_invar[k]["parap"][id_plante] = a + p_s # all non empty plant must have a minimum intercepted energy if len(list_invar[k]["parip"]) == len(list_dicFeuilBilanR[k]["surf"]) : for p in range(len(list_invar[k]["parip"])): if list_invar[k]["parip"][p] == 0.0 and list_dicFeuilBilanR[k]["surf"][p] > 0.0: list_invar[k]["parip"][p] = epsilon # conversion c = (3600 * 24) / 1000000 list_invar[k]["parap"] *= c list_invar[k]["parip"] *= c ## Transmitted radiations throughout a grid of voxels res_trans = numpy.ones((m_lais.shape[1], m_lais.shape[2], m_lais.shape[3])) # if non empty scene if not elements_outputs.empty: # different treatment if scene is infinite, (issues with virtual sensors and finite scene with CARIBU) if infinite: nb0 = min(m_lais.shape[1] - sensors_nxyz[2], m_lais.shape[1]) ID_capt = 0 for ix in range(sensors_nxyz[0]): for iy in range(sensors_nxyz[1]): for iz in range(sensors_nxyz[2] - skylayer): a = min(sensors_outputs["par"][ID_capt], 1.) res_trans[nb0 + ((sensors_nxyz[2] - 1)) - iz][iy][ix] = a ID_capt += 1 else: raise ValueError("CARIBU Sensor + no infinite -> Doesn't work yet") # gives maximum transmitted energy dS = sensors_dxyz[0] * sensors_dxyz[1] res_trans = res_trans * energy * dS return res_trans