Source code for CARIBUinputs

'''
    CARIBUinputs
    *************

    Manages and prepares input information for CARIBU.

    In this module we use two of the LightVegeManager's inputs dict.

    *  ``parameters`` corresponding to CARIBU parameters and contains

        .. code-block:: python
            
            caribu_args = {
                            "sun algo" : "ratp",
                            "sun algo" : "caribu",

                            "caribu opt" : {
                                            band0 = (reflectance, transmittance),
                                            band1 = (reflectance, transmittance),
                                            ...
                                            },
                            "debug" : bool,
                            "soil mesh" : bool,
                            "sensors" : ["grid", dxyz, nxyz, orig]
                            }
    
    * ``geometry`` corresponding to the geometric information with the scenes inputs

        .. code-block:: python

            geometry = {
                        "scenes" : [scene0, scene1, scene2, ...] ,
                        "domain" : ((xmin, ymin), (xmax, ymax)),
                        "stems id" : [(id_element, id_scene), ...],
                        "transformations" : {
                                                "scenes unit" : kwarg ,
                                                "rescale" : kwarg ,
                                                "translate" : kwarg ,
                                                "xyz orientation" : kwarg
                                            }
                        }
    
    .. seealso:: For more details :ref:`Inputs description <inputs>`

'''

[docs]def Prepare_CARIBU(trimesh, geometry, matching_ids, minmax, parameters, infinite, idsensors) : """Format optical parameters and prepare virtual sensors and debug if activated :param trimesh: triangles mesh aggregated by indice elements .. code-block:: { id : [triangle1, triangle2, ...]} :type trimesh: dict :param geometry: geometric parameters from inputs of LightVegeManager :type geometry: dict :param matching_ids: dict that matches new element indices in trimesh with specy indice and input element indice, .. code-block:: matching_ids = { new_element_id : (input_element_id, specy_id)} :type matching_ids: dict :param minmax: list of mininuml point and maximum point in a triangles mesh :type minmax: [3-tuple, 3-tuple] :param parameters: RATP parameters from inputs of LightVegeManager :type parameters: dict :param infinite: if the user wishes to activate infinitisation of the grid :type infinite: bool :param idsensors: Sets which input scenes the grid of virtual sensors will match. Elements of this list refers to indices in the list ``geometry["scenes"]``. If no input indices are given, the grid will match the global scene ``trimesh``. Otherwise, you can match the grid to a specific set of input scenes :type idsensors: list of int :return: * ``opt``: optical parameters formatted for CARIBU. It takes the form of dict, where each key is a bandlength (PAR, NIR etc...) and values are transmittance and reflectance (or only reflectance for stems elements) * ``sensors_caribu``: geometric data of the virtual sensors in CARIBU scene format. Sensors are horizontal square made of two triangles .. code-block:: sensors_caribu = { sensor_id : [triangle1, triangle2], ...} * ``debug``: boolean if user wants to activate th edebug option in CARIBU * ``matching_sensors_species``: dict where each key is a sensor ID and value, its matching input vegetation type :rtype: dict, dict, bool, dict """ # manage stems element and creates optical parameters stems = [] if 'stems id' in geometry : stems = geometry["stems id"] opt = CARIBU_opticals(matching_ids, parameters, stems) # debug, sensors, domain sensors_caribu = None matching_sensors_species = {} if "sensors" in parameters and isinstance(parameters["sensors"], list): if "sensors" in parameters and parameters["sensors"][0] == "grid" : dxyz = parameters["sensors"][1] nxyz = parameters["sensors"][2] orig = parameters["sensors"][3] arg = (dxyz, nxyz, orig, minmax[1], trimesh, matching_ids, idsensors, infinite) sensors_caribu, \ sensors_plantgl, \ Pmax_capt = create_caribu_legume_sensors(*arg) for i in sensors_caribu.keys(): matching_sensors_species[i] = 0 elif "sensors" in parameters and isinstance(parameters["sensors"], dict): sensors_caribu = {} matching_sensors_species = {} start_id = 0 for specy_indice, sensors_parameters in parameters["sensors"].items(): dxyz = sensors_parameters[1] nxyz = sensors_parameters[2] orig = sensors_parameters[3] arg = (dxyz, nxyz, orig, minmax[1], trimesh, matching_ids, idsensors, infinite, start_id) sensors_dict, \ sensors_plantgl, \ Pmax_capt = create_caribu_legume_sensors(*arg) sensors_caribu.update(sensors_dict) start_id = max(sensors_dict.keys()) + 1 for key in sensors_dict.keys(): matching_sensors_species[key] = specy_indice # infinite scene pattern if not precised in the inputs if "domain" not in geometry : # special case for l-egume and virtual sensors if "sensors" in parameters and parameters["sensors"][0] == "grid" : reduction_domain = 0 d = ((orig[0] + reduction_domain, orig[1] + reduction_domain), (nxyz[0] * dxyz[0] - reduction_domain, nxyz[1] * dxyz[1] - reduction_domain)) else: d = ((minmax[0][0], minmax[0][1]), (minmax[1][0], minmax[1][1])) geometry["domain"] = d debug = False if "debug" in parameters and parameters["debug"] : debug = True return opt, sensors_caribu, debug, matching_sensors_species
[docs]def CARIBU_opticals(matching_ids, parameters, stems_id=None) : """Sets optical parameters for CARIBU from LightVegeManager inputs It removes transmittance for stems elements if precised :param matching_ids: dict that matches new element indices in trimesh with specy indice and input element indice, :code:`matching_ids = { new_element_id : (input_element_id, specy_id)}` :type matching_ids: dict :param parameters: RATP parameters from inputs of LightVegeManager :type parameters: dict :param stems_id: list of potential stems element in the input scenes, defaults to None :type stems_id: list of 2-tuple, optional :return: optical parameters formatted for CARIBU. It takes the form of dict, where each key is a bandlength (PAR, NIR etc...) and values are transmittance and reflectance (or only reflectance for stems elements) :rtype: dict """ opt = {} for band, coef in parameters["caribu opt"].items() : opt[band] = {} for id, val in matching_ids.items(): for band, coef in parameters["caribu opt"].items() : # id is element, stems_id is a list of (element id, specy id) # if stem then no transmittance, the object is opaque if stems_id is not None and (val[0], val[1]) in stems_id: #: (reflectance,) of the stems ou organe opaque opt[band][id] = (coef[0],) else: # A 2-tuple encode a symmetric translucent material defined # by a reflectance and a transmittance # A 4-tuple encode an asymmetric translucent material defined # the reflectance and transmittance # of the upper and lower side respectively #: (reflectance, transmittance) of the adaxial side of the # leaves, élément translucide symétrique opt[band][id] = coef return opt
[docs]def create_caribu_legume_sensors(dxyz, nxyz, orig, pmax, trimesh, matching_ids, id_sensors, infinite, start_id=0) : """Creates a set of virtual sensors following a voxels grid each sensor is a square made by two triangles and takes place on the bottom face of a voxel The grid follow the xyz axis (and so the voxels) :param dxyz: [dx, dy, dz] size of a voxel in each direction :type dxyz: list :param nxyz: [nx, ny, nz] number of voxels on each axis :type nxyz: list :param orig: [x_origin, y_origin, z_origin], origin of the grid, cartesian coordinates :type orig: list (or tuple) :param pmax: [x, y, z] maximum point of trimesh in the xyz space :type pmax: lsit (or tuple) :param trimesh: triangles mesh aggregated by indice elements .. code-block:: { id : [triangle1, triangle2, ...]} :type trimesh: dict :param matching_ids: dict that matches new element indices in trimesh with specy indice and input element indice, :code:`matching_ids = { new_element_id : (input_element_id, specy_id)}` :type matching_ids: dict :param idsensors: Sets which input scenes the grid of virtual sensors will match. Elements of this list refers to indices in the list ``geometry["scenes"]``. If no input indices are given, the grid will match the global scene ``trimesh``. Otherwise, you can match the grid to a specific set of input scenes :type idsensors: list of int :param infinite: if the user wishes to activate infinitisation of the grid :type infinite: bool :return: it returns 3 objects: * ``sensors_caribu``, geometric data of the virtual sensors in CARIBU scene format. Sensors are horizontal square made of two triangles :code:`sensors_caribu = { sensor_id : [triangle1, triangle2], ...}` * ``s_capt``, PlantGL scene of the virtual sensors * ``sensors_maxcenter``, [x, y, z] representing point on the highest sensors layer and middle of xy plane in the grid :rtype: dict, PlantGL scene, list """ import openalea.plantgl.all as pgl from lightvegemanager.voxelsmesh import reduce_layers_from_trimesh # if number of filled layers is less than number of expected layers (nxyz[2]) skylayer = reduce_layers_from_trimesh(trimesh, pmax, dxyz, nxyz, matching_ids, id_sensors) # initialize the PlantGL scene s_capt = pgl.Scene() # point to output sensors_maxcenter = [ orig[0] + 0.5*dxyz[0]*nxyz[0] , orig[1] + 0.5*dxyz[1]*nxyz[1] , orig[2] + dxyz[2] * (nxyz[2] - skylayer -1)] # orientation changes depending if the scene is infinite or not if infinite : # square looks upward points = [orig, (orig[0] + dxyz[0], orig[1], orig[2]), (orig[0] + dxyz[0], orig[1] + dxyz[1], orig[2]), (orig[0], orig[1] + dxyz[1], orig[2])] else : # square looks downwards points = [orig, (orig[0], dxyz[1], orig[2]), (dxyz[0], dxyz[1], orig[2]), (dxyz[0], orig[1], orig[2])] normals = [(0, 0, 1) for i in range(4)] indices = [(0, 1, 2, 3)] # plantGL square square_pgl = pgl.QuadSet(points, indices, normals, indices) # generate the sensors in plantGL format among the grid ID_capt = start_id dico_translat = {} for ix in range(nxyz[0]): for iy in range(nxyz[1]): for iz in range(nxyz[2] - skylayer): # translation vector tx = ix * dxyz[0] ty = iy * dxyz[1] tz = iz * dxyz[2] # save the vector dico_translat[ID_capt] = [tx, ty, tz] # adds a squared sensor to the plantGL scene sensor = pgl.Translated(geometry=square_pgl, translation=(tx, ty, tz)) s_capt.add(pgl.Shape(geometry=sensor, id=ID_capt)) ID_capt += 1 # sensors in CARIBU scene format with triangles caribu_sensors = {} for c in s_capt: # Preparation pt_lst = c.geometry.geometry.pointList idx_lst = c.geometry.geometry.indexList # two triangles for each squarred sensor for i in range(0, len(idx_lst)): triangles = [] # upper triangle x = [pt_lst[idx_lst[i][j]][0] + dico_translat[c.id][0] for j in range(3)] y = [pt_lst[idx_lst[i][j]][1] + dico_translat[c.id][1] for j in range(3)] z = [pt_lst[idx_lst[i][j]][2] + dico_translat[c.id][2] for j in range(3)] triangles.append((list(zip(x, y, z)))) # lower triangle ids = [0, 2, 3] x = [pt_lst[idx_lst[i][j]][0] + dico_translat[c.id][0] for j in ids] y = [pt_lst[idx_lst[i][j]][1] + dico_translat[c.id][1] for j in ids] z = [pt_lst[idx_lst[i][j]][2] + dico_translat[c.id][2] for j in ids] triangles.append((list(zip(x, y, z)))) # save the triangles caribu_sensors[c.id] = triangles return caribu_sensors, s_capt, sensors_maxcenter
[docs]def run_caribu(c_scene, direct_active, infinite, sensors, energy=1.) : """runs caribu depending on input options :param c_scene: instance of CaribuScene containing geometry, light source(s), opt etc... :type c_scene: CaribuScene :param direct_active: Whether only first order interception is to be computed, default is True (no rediffusions) :type direct_active: bool :param infinite: if the user wishes to activate infinitisation of the grid :type infinite: bool :param sensors: geometric data of the virtual sensors in CARIBU scene format. Sensors are horizontal square made of two triangles .. code:: python sensors_caribu = { sensor_id : [triangle1, triangle2], ...} :type sensors: dict :param energy: input energy value :type energy: float, optional default is 1. :return: results are stored in two dict - raw (dict of dict) a {band_name: {result_name: property}} dict of dict. Except for result_name='sensors', each property is a {primitive_id: [values,]} dict containing results for individual triangles of the primitive - aggregated (dict of dict) : a {band_name: {result_name: property}} Except for result_name='sensors', each property is a {primitive_id: value} dict containing aggregated results for each primitive result_name are : - area (float): the individual areas (m2) - Eabs (float): the surfacic density of energy absorbed (m-2) - Ei (float): the surfacic density of energy incoming (m-2) additionally, if split_face is True - Ei_inf (float): the surfacic density of energy incoming on the inferior face (m-2) - Ei_sup (float): the surfacic density of energy incoming on the superior face (m-2) - sensors (dict of dict): area, surfacic density of incoming direct energy and surfacic density of incoming total energy of sensors grouped by id, if any :rtype: dict of dict, dict of dict """ if sensors is None : raw, aggregated = c_scene.run(direct=direct_active, infinite=infinite) else : raw, aggregated = c_scene.run(direct=direct_active, infinite=infinite, sensors=sensors) # adjust relative results with energy input value for band in raw : for radtype in ("Eabs", "Ei") : for k in raw[band][radtype].keys(): raw[band][radtype][k] = [x * energy for x in raw[band][radtype][k]] for k in aggregated[band][radtype].keys(): aggregated[band][radtype][k] *= energy return raw, aggregated