Пример #1
0
def spanning_mtg(graph):
    """ Extract an MTG from the GroIMP graph.

    Parameters
    ----------
        - graph is a RootedGraph created from the GroIMP Graph

    TODO:
        - compress vertices to only usefull vertices (geometry)
        - compress edges to spanning mtg
    """
    # Manage a mapping between the graph and the mtg
    graph2mtg = {}
    g = graph
    edge_type = g.edge_property('edge_type')

    mtg = MTG()

    mtg.root = graph.root

    # Check if the graph contains decomposition (/) edges
    if not is_multiscale(graph):
        pass

    # Compute the scale from each vertex.
    # Select one decomposition path if there are several in the GroIMP graph
    _scales = scales(g)

    # Set the internal scale information to the MTG
    mtg._scale = _scales

    # Set the edge_type for each vertex (<, +)
    _edge_type = _build_edge_type(g, mtg)

    # Compute the tree information at all scales
    _children_and_parent(g, mtg)

    # Compute the complex (upscale) and components (downscale) for each vertex
    _complex_and_components(g, mtg)

    print "scales :", mtg._scale
    # Extract all the vertex properties.
    _vertex_properties(g, mtg)

    # Compute missing links to have constant time access (O(1)) to neighbourhood
    fat_mtg(mtg)
    print "scales :", mtg._scale

    return mtg
Пример #2
0
def spanning_mtg(graph):
    """ Extract an MTG from the GroIMP graph.

    Parameters
    ----------
        - graph is a RootedGraph created from the GroIMP Graph

    TODO:
        - compress vertices to only usefull vertices (geometry)
        - compress edges to spanning mtg
    """
    # Manage a mapping between the graph and the mtg
    graph2mtg = {}
    g = graph
    edge_type = g.edge_property('edge_type')

    mtg = MTG()

    mtg.root = graph.root

    # Check if the graph contains decomposition (/) edges
    if not is_multiscale(graph):
        pass

    # Compute the scale from each vertex.
    # Select one decomposition path if there are several in the GroIMP graph
    _scales = scales(g)

    # Set the internal scale information to the MTG
    mtg._scale = _scales

    # Set the edge_type for each vertex (<, +)
    _edge_type = _build_edge_type(g, mtg)

    # Compute the tree information at all scales
    _children_and_parent(g, mtg)

    # Compute the complex (upscale) and components (downscale) for each vertex
    _complex_and_components(g, mtg)

    print "scales :", mtg._scale
    # Extract all the vertex properties.
    _vertex_properties(g, mtg)

    # Compute missing links to have constant time access (O(1)) to neighbourhood
    fat_mtg(mtg)
    print "scales :", mtg._scale

    return mtg
Пример #3
0
    def build_mtg(self):
        """ Build an MTG structure from data.

        The MTG is composed of 3 scales: Plant, Crown, Phytomer

        """
        self._axis_vid = []

        self.g = MTG()
        g = self.g

        # build 2 elements: P plante and C crown
        #self._vid = plant_id = g.add_component(g.root, label='Plant')
        # add crown
        #self._vid = g.add_component(self._vid, label='Crown')
        self._order = 0
        self._scale = 3
        self._vid = None
        #self._axis_vid.append(self._vid)

        nb_lines = len(self.content) - self.prop_no
        for i in range(nb_lines):
            #print (self._vid)
            self.read_line()

        self.g = fat_mtg(g)
Пример #4
0
    def parse(self, fn):
        self.trash = []
        self._g = MTG()

        # Current proxy node for managing properties
        self._node = None

        doc = xml.parse(fn)
        root = doc.getroot()

        self.dispatch(root)

        self._g = fat_mtg(self._g)

        return self._g
Пример #5
0
    def parse(self, filename, debug=False):
        self.debug = debug
        self.trash = []
        self._g = MTG()

        # Current proxy node for managing properties
        self._node = None 

        doc = xml.parse(filename)
        root = doc.getroot()
        # recursive call of the functions to add neww plants/root axis to the MTG
        self.dispatch(root)

        g = fat_mtg(self._g)

        # Add metadata as property of the graph
        #g.graph_property()

        return g
Пример #6
0
    def parse(self, filename, debug=False):
        self.debug = debug
        self.trash = []
        self._g = MTG()

        # Current proxy node for managing properties
        self._node = None

        doc = xml.parse(filename)
        root = doc.getroot()
        # recursive call of the functions to add neww plants/root axis to the MTG
        self.dispatch(root)

        g = fat_mtg(self._g)

        # Add metadata as property of the graph
        #g.graph_property()

        return g
Пример #7
0
def mtg_factory(parameters,
                metamer_factory=adel_metamer,
                leaf_sectors=1,
                leaves=None,
                stand=None,
                axis_dynamics=None,
                add_elongation=False,
                topology=['plant', 'axe_id', 'numphy'],
                split=False,
                aborting_tiller_reduction=1.0,
                leaf_db=None):
    """ Construct a MTG from a dictionary of parameters.

    The dictionary contains the parameters of all metamers in the stand (topology + properties).
    metamer_factory is a function that build metamer properties and metamer elements from parameters dict.
    leaf_sectors is an integer giving the number of LeafElements per Leaf blade
    leaves is a {species:adel.geometric_elements.Leaves} dict
    stand is a list of tuple (xy_position_tuple, azimuth) of plant positions
    axis_dynamics is a 3 levels dict describing axis dynamic. 1st key level is plant number, 2nd key level is axis number, and third ky level are labels of values (n, tip, ssi, disp)
    topology is the list of key names used in parameters dict for plant number, axe number and metamer number
    aborting_tiller_reduction is a scaling factor applied to reduce all dimensions of organs of tillers that will abort

    Axe number 0 is compulsory
  
    """

    if leaf_db is not None:
        raise AdelDeprecationError(
            'leaf_db argument is deprecated, use leaves argument instead')

    if leaves is None:
        dynamic_leaf_db = {0: False}
        leaves = {0: None}
    else:
        dynamic_leaf_db = {k: leaves[k].dynamic for k in leaves}

    g = MTG()

    # buffers
    # for detection of newplant/newaxe
    prev_plant = 0
    prev_axe = -1
    # current vid
    vid_plant = -1
    vid_axe = -1
    vid_metamer = -1
    vid_node = -1
    vid_elt = -1
    # vid of top of stem nodes and elements
    vid_topstem_node = -1
    vid_topstem_element = -1
    # vid of plant main stem (axe0)
    vid_main_stem = -1
    # buffer for the vid of main stem anchor points for the first metamer, node and element of tillers
    metamers = []
    nodes = []
    elts = []

    dp = parameters
    nrow = len(dp['plant'])

    for i in range(nrow):
        plant, num_metamer = [
            int(convert(dp.get(x)[i], undef=None))
            for x in [topology[e] for e in [0, 2]]
        ]
        axe = dp.get(topology[1])[i]
        mspos = int(convert(dp.get('ms_insertion')[i], undef=None))
        args = properties_from_dict(dp, i, exclude=topology)
        # Add plant if new
        if plant != prev_plant:
            label = 'plant' + str(plant)
            position = (0, 0, 0)
            azimuth = 0
            species = 0
            if 'species' in args:
                species = args['species']
            if stand and len(stand) >= plant:
                position, azimuth = stand[plant - 1]
            vid_plant = g.add_component(g.root,
                                        label=label,
                                        edge_type='/',
                                        position=position,
                                        azimuth=azimuth,
                                        refplant_id=args.get('refplant_id'),
                                        species=species)
            # reset buffers
            prev_axe = -1
            vid_axe = -1
            vid_metamer = -1
            vid_node = -1
            vid_elt = -1
            vid_topstem_node = -1
            vid_topstem_element = -1
            vid_main_stem = -1
            metamers = []
            nodes = []
            elts = []

        # Add axis
        if axe != prev_axe:
            label = ''.join(axe.split('.'))
            timetable = None
            if axis_dynamics:
                timetable = axis_dynamics[str(plant)][str(axe)]
            if axe == 'MS':
                vid_axe = g.add_component(vid_plant,
                                          edge_type='/',
                                          label=label,
                                          timetable=timetable,
                                          HS_final=args.get('HS_final'),
                                          nff=args.get('nff'),
                                          hasEar=args.get('hasEar'),
                                          azimuth=args.get('az_insertion'))
                vid_main_stem = vid_axe
            else:
                vid_axe = g.add_child(vid_main_stem,
                                      edge_type='+',
                                      label=label,
                                      timetable=timetable,
                                      HS_final=args.get('HS_final'),
                                      nff=args.get('nff'),
                                      hasEar=args.get('hasEar'),
                                      azimuth=args.get('az_insertion'))

        # Add metamer
        assert num_metamer > 0
        # args are added to metamers only if metamer_factory is none, otherwise compute metamer components
        components = []
        if metamer_factory:
            xysr_key = None
            if leaves[
                    species] is not None and 'LcType' in args and 'LcIndex' in args:
                lctype = int(args['LcType'])
                lcindex = int(args['LcIndex'])
                if lctype != -999 and lcindex != -999:
                    age = None
                    if dynamic_leaf_db[species]:
                        age = float(
                            args['rph']
                        ) - 0.3  # age_db = HS - rank + 1 = ph - 1.3 - rank +1 = rph - .3
                        if age != 'NA':
                            age = max(0, int(float(age)))
                    xysr_key = leaves[species].get_leaf_key(
                        lctype, lcindex, age)

            elongation = None
            if add_elongation:
                startleaf = -.4
                endleaf = 1.6
                stemleaf = 1.2
                startE = endleaf
                endE = startE + (endleaf - startleaf) / stemleaf
                endBlade = endleaf
                if args['Gl'] > 0:
                    endBlade = args['Ll'] / args['Gl'] * (endleaf - startleaf)
                elongation = {
                    'startleaf': startleaf,
                    'endBlade': endBlade,
                    'endleaf': endleaf,
                    'endE': endE
                }
            if not 'ntop' in args:
                args.update({'ntop': None})
            if not 'Gd' in args:
                args.update({'Gd': 0.19})
            args.update({'split': split})
            if args.get('HS_final') < args.get('nff'):
                for what in ('Ll', 'Lv', 'Lr', 'Lsen', 'L_shape', 'Lw_shape',
                             'Gl', 'Gv', 'Gsen', 'Gd', 'El', 'Ev', 'Esen',
                             'Ed'):
                    args.update(
                        {what: args.get(what) * aborting_tiller_reduction})
            components = metamer_factory(Lsect=leaf_sectors,
                                         shape_key=xysr_key,
                                         elongation=elongation,
                                         leaves=leaves[species],
                                         **args)
            args = {'L_shape': args.get('L_shape')}
        #
        label = 'metamer' + str(num_metamer)
        new_metamer = g.add_component(vid_axe,
                                      edge_type='/',
                                      label=label,
                                      **args)
        if axe == 'MS' and num_metamer == 1:
            vid_metamer = new_metamer
        elif num_metamer == 1:
            # add the edge with the bearing metamer on main stem
            vid_metamer = metamers[mspos - 1]
            vid_metamer = g.add_child(vid_metamer,
                                      child=new_metamer,
                                      edge_type='+')
        else:
            vid_metamer = g.add_child(vid_metamer,
                                      child=new_metamer,
                                      edge_type='<')

        # add metamer components, if any
        if len(components) > 0:
            # deals with first component (internode) and first element
            node, elements = get_component(components, 0)
            element = elements[0]
            new_node = g.add_component(vid_metamer, edge_type='/', **node)
            new_elt = g.add_component(new_node, edge_type='/', **element)
            if axe == 'MS' and num_metamer == 1:  # root of main stem
                vid_node = new_node
                vid_elt = new_elt
            elif num_metamer == 1:  # root of tiller
                vid_node = nodes[mspos - 1]
                vid_node = g.add_child(vid_node, child=new_node, edge_type='+')
                vid_elt = elts[mspos - 1]
                vid_elt = g.add_child(vid_elt, child=new_elt, edge_type='+')
            else:
                vid_node = g.add_child(vid_topstem_node,
                                       child=new_node,
                                       edge_type='<')
                vid_elt = g.add_child(vid_topstem_element,
                                      child=new_elt,
                                      edge_type='<')
            # add other elements of first component (the internode)
            for i in range(1, len(elements)):
                element = elements[i]
                vid_elt = g.add_child(vid_elt, edge_type='<', **element)
            vid_topstem_node = vid_node
            vid_topstem_element = vid_elt  # last element of internode

            # add other components
            for i in range(1, len(components)):
                node, elements = get_component(components, i)
                if node['label'] == 'sheath':
                    edge_type = '+'
                else:
                    edge_type = '<'
                vid_node = g.add_child(vid_node, edge_type=edge_type, **node)
                element = elements[0]
                new_elt = g.add_component(vid_node, edge_type='/', **element)
                vid_elt = g.add_child(vid_elt,
                                      child=new_elt,
                                      edge_type=edge_type)
                for j in range(1, len(elements)):
                    element = elements[j]
                    vid_elt = g.add_child(vid_elt, edge_type='<', **element)

                    # update buffers
        if axe == 'MS':
            metamers.append(vid_metamer)
            if len(components) > 0:
                nodes.append(vid_topstem_node)
                elts.append(vid_topstem_element)
        prev_plant = plant
        prev_axe = axe

    return fat_mtg(g)
def cereals(json=None, classic=False, unit='cm', seed=None, plant=None):
    """
    Generate a 'geometric-based' MTG representation of cereals

    Args:
        json: a dict of parameters with:
            leaf_order : a list of int defining leaf rank
            leaf_polylines : [[(x, y, z, r), ..., ], ...] list of list tuple
            sampling  leaf midribs. r is leaf width at position x, y, z along
            leaf midrib
            stem : [(x, y, z, r), ..., ] list of tuples sampling stem from its
            base to its end. r is the stem diameter at x,y,z position along the
             stem
        classic: (bool) should stem cylinders be classical pgl cylinders ?
        unit: (string) desired length unit for the output mtg
        seed: (int) a seed for the random number generator
    """
    if plant is None:
        blade_dimensions, stem_dimensions, leaves = as_plant(json)

        dim = blade_dimensions.merge(stem_dimensions)
        dim = dim.sort_values('ntop', ascending=False)
        relative_azimuth = dim.leaf_azimuth.copy()
        relative_azimuth[1:] = numpy.diff(relative_azimuth)
    else:
        dim = plant
        leaves = {
            row['ntop']: row['leaf_shape']
            for index, row in dim.iterrows()
        }

    g = MTG()
    vid_node = g.add_component(g.root, label='plant', edge_type='/')
    for i, row in dim.iterrows():
        internode = {
            'label': 'StemElement',
            'length': row['L_internode'],
            'is_green': True,
            'diameter_base': row['W_internode'],
            'diameter_top': row['W_internode'],
            'azimuth': row['leaf_azimuth']
        }

        vid_node = g.add_child(vid_node, edge_type='<', **internode)

        blade = {
            'label': 'LeafElement',
            'shape': leaves[row['ntop']],
            'shape_mature_length': row['L_blade'],
            'length': row['L_blade'],
            'visible_length': row['L_blade'],
            'is_green': True,
            'srb': 0,
            'srt': 1,
            'lrolled': 0,
            'd_rolled': 0,
            'shape_max_width': row['W_blade'],
            'stem_diameter': row['W_internode']
        }

        vid_blade = g.add_child(vid_node, edge_type='+', **blade)

    g = fat_mtg(g)

    # Compute geometry
    g = mtg_interpreter(g, classic=classic)

    return g
Пример #9
0
def read_lsystem_string( string,
                         symbol_at_scale,
                         functional_symbol={},
                         mtg=None ):
    """Read a string generated by a lsystem.

    :Parameters:

    - `string`: The lsystem string representing the axial tree.
    - `symbol_at_scale`: A dict containing the scale for each symbol name.

    :Optional parameters:

    - `functional_symbol`: A dict containing a function for specific symbols.
        The args of the function have to be coherent with those in the string.
        The return type of the functions have to be a dictionary of properties: dict(name, value)

    :Return:

        MTG object
    """

    import openalea.plantgl.all as pgl
    s = string

    def transform(turtle, mesh):
        x = turtle.getUp()
        z = turtle.getHeading()

        bo = pgl.BaseOrientation(x, z^x)
        matrix = pgl.Transform4(bo.getMatrix())
        matrix.translate(turtle.getPosition())
        mesh = mesh.transform(matrix)
        return mesh
        

    # 1. Create the mtg structure.
    if mtg is None:
        mtg = MTG()

    # 2. add some properties to the MTG
    mtg.add_property('index')
    mtg.add_property('can_label')
    mtg.add_property('geometry')
    mtg.add_property('tissue_type')

    vid = mtg.root # vid of the support tree, i.e. at the finest scale
    current_vertex = mtg.root
    branching_stack = []

    pending_edge = '' # edge type for the next edge to be created
    scale = 0

    lsys_symbols = ['[', ']', '/', '+', '^', 'f']
    modules = symbol_at_scale.keys()
    symbols = lsys_symbols + modules

    index = dict(zip(symbol_at_scale.keys(), [0]*len(symbol_at_scale)))

    is_ramif = False 

    # 2. Create a PlantGL Turtle...
    turtle = pgl.Turtle()

    max_scale = max(symbol_at_scale.values())

    for edge_type in symbols:
        if edge_type != 'f':
            s = s.replace(edge_type, '\n%s'%edge_type)
        else:
            s = s.replace('f(', '\nf(')
    l = s.split()

    try:
        plant_name = [s for s in symbol_at_scale.keys() if 'plant' in s.lower()][0]
    except:
        ValueError("""Incorrect plant name (should be plant)""")

    for node in l:
        # Check if node is a module

        tag = node[0]

        if tag == '[':
            branching_stack.append(vid)
            turtle.push()
            is_ramif = True
        elif tag == ']':
            vid = branching_stack.pop()
            current_vertex = vid
            scale = mtg.scale(vid)
            turtle.pop()
            is_ramif = False
        elif tag == '/':
            args = get_args(node[1:])
            if args:
                angle = get_float(args[1:-1])
                turtle.rollR(angle)
            else:
                turtle.rollR()
        elif tag == '+':
            args = get_args(node[1:])
            if args:
                angle = get_float(args[1:-1])
                turtle.left(angle)
            else:
                turtle.left()
        elif tag == '^':
            args = get_args(node[1:])
            if args:
                angle = get_float(args[1:-1])
                turtle.up(angle)
            else:
                turtle.up()
        elif tag == 'f' and node[1] == '(':
            args = get_args(node[1:])
            if args:
                length = get_float(args[1:-1])
                if length > 0:
                    turtle.f(length)
            else:
                turtle.f()
        else:
            # add new modules to the mtg (i.e. add nodes)
            name = get_name(node)
            if name not in modules:
                print 'Unknow element %s'% name
                continue
            
            module_scale = symbol_at_scale[name]
            if is_ramif:
                edge_type = '+'
            else:
                edge_type = '<'

            log(node, module_scale, edge_type )
            
            if module_scale == scale:
                if mtg.scale(vid) == scale:
                    vid = mtg.add_child(vid, edge_type=edge_type, label=name)
                    current_vertex = vid
                    pending_edge = ''

                    log('','Cas 1.1', scale, 
                        'mtg.scale(vid)', mtg.scale(vid), 
                        'generated vertex', vid)

                    assert mtg.scale(vid) == module_scale
                else:
                    # add the edge to the current vertex
                    current_vertex = mtg.add_child(current_vertex, 
                                                   edge_type=edge_type, 
                                                   label=name)
                    log('', 'Cas 1.2', scale, 
                        'mtg.scale(vid)', mtg.scale(vid), 
                        'generated vertex', current_vertex)
                    assert mtg.scale(current_vertex) == module_scale
                is_ramif = False
            elif module_scale > scale:
                log('', 'Cas 2', scale, 'mtg.scale(vid)', mtg.scale(vid))

                old_current_vertex = current_vertex
                while module_scale > scale:
                    if mtg.scale(vid) == scale:
                        assert vid == current_vertex
                        vid = mtg.add_component(vid)
                        current_vertex = vid
                        log('', '', 'Cas 2.1', scale, 'generate new component', current_vertex)
                        scale += 1
                        if module_scale == scale:
                            assert mtg.scale(current_vertex) == module_scale
                            mtg.property('label')[current_vertex] = name
                            break
                    else:
                        scale += 1
                        current_vertex = mtg.add_component(current_vertex)
                else:
                    log(node, 'add_child(%d, child=%d)'%(old_current_vertex, current_vertex))
                    mtg.property('label')[current_vertex] = name
                    if mtg.scale(vid) == scale:
                        vid = mtg.add_child(vid, child=current_vertex, edge_type=edge_type)
                        is_ramif = False
            else:
                assert module_scale < scale
                while module_scale < scale:
                    scale -= 1
                    current_vertex = mtg.complex(current_vertex)
                else:
                    current_vertex = mtg.add_child(current_vertex, edge_type=edge_type, label=name)
                    assert mtg.scale(current_vertex) == module_scale
        
            # MANAGE the properties, the geometry and the indices!!!
            index[name] += 1
            if name == plant_name:
                for k in index.keys():
                    if k != name:
                        index[k] = 0

            mtg.property('index')[current_vertex] = index[name]
            if name in functional_symbol:
                features = eval(node, functional_symbol)
                geom = features.get('geometry')
                canlabel = features.get('label')
                ttype = features.get('tissue_type')
                
                if geom:
                    # get the transformation from the turtle
                    geom = transform(turtle, geom)
                    mtg.property('geometry')[current_vertex] = geom

                    if name == 'StemElement':
                        # parse args to know how the turtle has to move .
                        args = get_args(node)[1:-1]
                        list_args= args.split(',')
                        length = float(list_args[1]) # 2nd arg
                        if length > 0:
                            turtle.f(length)

                if canlabel:
                    canlabel.elt_id = index[name]
                    plant_id = mtg.complex_at_scale(current_vertex, scale=1)
                    canlabel.plant_id = mtg.property('index')[plant_id]
                    mtg.property('can_label')[current_vertex] = canlabel

                if ttype:
                    mtg.property('tissue_type')[current_vertex] = ttype
                    
    mtg = fat_mtg(mtg)
    return mtg
Пример #10
0
def mtg_factory(params, sectors = 1):
    """ Construct a MTG from a dictionary.

    The dictionary contains the parameters and a list of elements. Sector is an integer giving the number of LeafElements per Leaf
    The keys of params are:
        - plant: indx of plant
        - axe: 
        - numphy
        - Lv
        - L_shape
        - Lsen
        - Lw_shape
        - LcType
        - LcIndex
        - Linc
        - Laz
        - Lpo
        - Lpos
        - Gl
        - Gv
        - Gsen
        - Gpo
        - Gpos
        - Gd
        - Ginc
        - El
        - Ev
        - Esen
        - Ed
        - Einc
        - Epo
        - Epos

    :TODO: 
        * add length and final_length (DONE)
        * fix naming convention for Linc: relative inclination (DONE)
        * Add a scale to define the tissue types (ear, sheath, laminae, gain)
        * diam and final_diam (resp. width)
        * function reset length
        * function phenology(g, table) -> dynamic parameters (start_thermaltime, end_thermaltime)
        * function growthThermaltime(g, tt, dtt): tt=thermaltime du couvert
        * function growthThermaltime(g, tt, dtt, stress factor)
        * stress factor: offre/demande
            - demand = :math:`D=\int_{tt}^{tt+dtt}{S(x)dx}*\rho_s+\int_{tt}^{tt+dtt}{V(x)dx}*\rho_v`
            - offre : :math:`sum{E_abs}*\eps_b`
            => ds = ds_predit* stress_factor
        * give the area to the leaf model
        * update properties
    """

    if not _check_adel_parameters(params):
        raise ValueError('Adel parameters are invalid')

    g = MTG()
    topology = ['plant', 'axe', 'numphy']

    # buffers
    prev_plant = 0
    prev_axe = -1
    prev_metamer = -1
    vid_plant = -1
    vid_axe = -1
    vid_metamer = -1
    vid_node = -1

    # store the metamer vids of the axis 0 
    metamers = []
    nodes = []
    edge_type = '<'

    dp = params
    nrow = len(params['plant'])
    for i in range(nrow):

        #plant, axe, num_metamer = [int(convert(d.get(x),undef=None)) for x in topology]
        plant = int(dp['plant'][i])
        try:
            axe = int(dp['axe'][i])
        except:
            axe = int(dp['ms_insertion'][i])
        num_metamer = int(dp['numphy'][i])

        #plant, axe, num_metamer = [int(convert(d.get(x),undef=None)) for x in topology]
        #print 'plant: %d, axe:%d, nb_metamers: %d'%(plant, axe, num_metamer)
        # Add new plant
        if plant != prev_plant:
            label = 'plant'+str(plant)
            vid_plant = g.add_component(g.root, label=label, edge_type='/')
            vid_axe = -1
            vid_metamer = -1
            vid_node = -1
            prev_axe = -1

        if num_metamer < prev_metamer:
            prev_axe = -1
            prev_metamer = -1
            
        # Add an axis
        if axe != prev_axe:
            label = 'axe'+str(axe)
           
            
            if axe == 0:
                vid_axe = g.add_component(vid_plant,edge_type='/',label=label)
                vid_node = -1
            else:
                #args['edge_type'] = '+'
                edge_type = '+'
                assert g.parent(vid_plant) is None
                new_axe = g.add_child(vid_axe,edge_type=edge_type,label=label)
                print vid_axe, new_axe, vid_plant
                assert g.parent(vid_plant) is None
                #vid, vid_axe = g.add_child_and_complex(metamers[axe], complex=vid_axe, **args)

        # Add metamers
        args = properties_from_dict(dp,i,exclude=topology)

        assert num_metamer > 0
        label = 'metamer'+str(num_metamer)

        if axe==0 and num_metamer==1:
            metamers=[]
            edge_type = '/'
            vid_metamer = g.add_component(vid_axe, edge_type='/', label = label, **args)
        elif num_metamer == 1:
            # Add the first element connected to previous metamer on the previous axis
            edge_type = '+'
            vid_metamer = g.add_component(vid_axe, label=label, **args)
            vid_metamer =  g.add_child(metamers[axe-1], child=vid_metamer, edge_type='+')
            vid_node = nodes[axe-1]
        else:
            edge_type = '<'
            vid_metamer = g.add_child(vid_metamer, label=label, edge_type='<',**args)

        if axe == 0:
            metamers.append(vid_metamer)

        # Add internode, sheath, and lamina

        # Visible Internode
        #if args['Ev'] > 0.:
        tissue = 'internode'

        # Several cases:
        Egreen = args['Ev']-args['Esen'] if args['Ev']-args['Esen'] >0. else 0.
        Esen = args['Ev'] if Egreen == 0. else args['Esen']

        #  - Green element and perhaps a senescent one
        if vid_node == -1:
            # first metamer on the axis
            vid_node= g.add_component(vid_metamer, label='StemElement', edge_type='/',
                length=Egreen,final_length=args['El'],po=args['Epo'], diam=args['Ed'], tissue=tissue, inclination=args['Einc'] )
            assert edge_type == '/'
        else:
            # first element on the metamer which has a parent metamer on the axis
            new_node = g.add_component(vid_metamer)
            vid_node= g.add_child(vid_node,child=new_node, 
                    label='StemElement', edge_type=edge_type,
                    length=Egreen,final_length=args['El'],po=args['Epo'], diam=args['Ed'], tissue=tissue, inclination=args['Einc'] )
        # Add a senescent element
        #if args['Esen'] > 0.:
        vid_node = g.add_child(vid_node, label='StemElement', edge_type='<',
            length=Esen,final_length=args['El'],po=args['Epos'], diam=args['Ed'], tissue=tissue, sen=True, inclination=0.)


        # Sheath
        # Same logic that described previously.
        Ggreen = args['Gv']-args['Gsen'] if args['Gv']-args['Gsen'] >0. else 0.
        Gsen = args['Gv'] if Ggreen == 0. else args['Gsen']
    
        vid_node= g.add_child(vid_node, label='StemElement', edge_type='<',
        length=Ggreen,final_length=args['Gl'],po=args['Gpo'], diam=args['Gd'], tissue=tissue, inclination=args['Ginc'] )
        #if args['Gsen'] > 0.:
        vid_node= g.add_child(vid_node, label='StemElement', edge_type='<',
        length=Gsen,final_length=args['Gl'],po=args['Gpos'], diam=args['Gd'], sen=True, tissue=tissue, inclination=0.)

        if axe == 0:
            nodes.append(vid_node)

        # Lamina
        #if args['Lv'] > 0.:
        l_node = vid_node
        tissue='lamina'
        ds = 1. / sectors
        srlim = 1.-(args['Lsen']/args['Lv'])
        st = ds
        edge_type = '+'
        Laz = args['Laz']

        Lgreen = args['Lv']-args['Lsen'] if args['Lv']-args['Lsen'] >0. else 0.
        Lsen = args['Lv'] if Lgreen == 0. else args['Lsen']
        for isect in range(sectors):
            # create one green and/or one senescent sector
            srb_green = st-ds
            srt_green = min(st, max(srb_green,srlim))
            srb_sen = srt_green
            srt_sen= st

            l_node = g.add_child(l_node, label='LeafElement', edge_type=edge_type,
                                 length=args['Lv'],final_length=args['L_shape'],po=args['Lpo'],
                                 Ll=args['Ll'], Lw= args['Lw_shape'], LcType=args['LcType'], 
                                 LcIndex=args['LcIndex'], Laz=Laz,Linc=args['Linc'], 
                                 srb=srb_green, srt=srt_green, tissue=tissue)

            l_node = g.add_child(l_node, label='LeafElement', edge_type='<',
                                 length=args['Lv'],final_length=args['L_shape'],po=args['Lpos'],
                                 Ll=args['Ll'], Lw= args['Lw_shape'], LcType=args['LcType'], 
                                 LcIndex=args['LcIndex'], Laz=Laz,Linc=args['Linc'], 
                                 srb=srb_sen, srt=srt_sen, sen=True, tissue=tissue)
            st += ds
            edge_type = '<'

        
        prev_plant = plant
        prev_axe = axe
        prev_metamer = num_metamer

    
    return fat_mtg(g)
Пример #11
0
def topological_table_to_mtg(csv_file, epsilon=1e-6):
    f = open(csv_file)
    l=f.readline()

    sniffer = csv.Sniffer()
    dialect = sniffer.sniff(l)
    # Go from start of file
    f.seek(0)

    reader = csv.DictReader(f, dialect=dialect)

    # Build the MTG
    g = MTG()
    topology = ['plant', 'axe', 'numphy']

    # buffers
    prev_plant = 0
    prev_axe = -1
    prev_metamer = -1
    vid_plant = -1
    vid_axe = -1
    vid_metamer = -1
    vid_node = -1

    # store the metamer vids of the axis 0 
    metamers = []
    nodes = []
    edge_type = '<'
    for d in reader:
        plant, axe, num_metamer = [int(convert(d.get(x),undef=None)) for x in topology]
        
        #print 'adding vertices for plant: %d, axe:%d, metamer: %d'%(plant, axe, num_metamer)
        # Add new plant
        if plant != prev_plant:
            label = 'plant'+str(plant)
            vid_plant = g.add_component(g.root, edge_type='/',label=label)
            vid_axe = -1
            vid_metamer = -1
            vid_node = -1
            prev_axe = -1
        #??
        if num_metamer < prev_metamer:
            prev_axe = -1
            prev_metamer = -1
            
        # Add an axis
        if axe != prev_axe:
            label = 'axe'+str(axe)
           
            
            if axe == 0:
                vid_axe = g.add_component(vid_plant,edge_type='/',label=label)
                vid_node = -1
            else:
                #args['edge_type'] = '+'
                edge_type = '+'
                vid_axe = g.add_child(vid_axe, edge_type=edge_type, label=label)
                #vid, vid_axe = g.add_child_and_complex(metamers[axe], complex=vid_axe, **args)

        # Add metamers
        args = properties(d, exclude=topology)

        assert num_metamer > 0
        label = 'metamer'+str(num_metamer)
        new_axe = True

        if axe==0 and num_metamer==1:
            metamers=[]
            edge_type = '/'
            vid_metamer = g.add_component(vid_axe, edge_type='/', label = label, **args)
        elif num_metamer == 1:
            # Add the first element connected to previous metamer on the previous axis
            edge_type = '+'
            vid_metamer, vid_axe =  g.add_child_and_complex(metamers[axe-1], complex=vid_axe, edge_type='+',label=label, **args)
            vid_node = nodes[axe-1]
        else:
            edge_type = '<'
            vid_metamer = g.add_child(vid_metamer, label=label, edge_type='<',**args)
            new_axe = False

        if axe == 0:
            metamers.append(vid_metamer)

        # Add internode, sheath, and lamina
        nb_internodes = 0
        nb_sheath = 0
        nb_leaf = 0

        # Internode
        if args['Ev'] > 0.:
            if args['Esen'] > args['Ev']:
                if vid_node == -1:
                    vid_node= g.add_component(vid_metamer, label='Esen', edge_type='/',length=args['Ev'],po=args['Epos'], diam=args['Ed'] )
                    assert edge_type == '/'
                else:
                    vid_node, vid_metamer= g.add_child_and_complex(vid_node, complex=vid_metamer, label='Esen', edge_type=edge_type,length=args['Ev'],po=args['Epos'], diam=args['Ed'] )
            else:
                if vid_node == -1:
                    vid_node= g.add_component(vid_metamer, label='Egreen', edge_type='/',length=args['Ev']-args['Esen'],po=args['Epo'], diam=args['Ed'] )
                    assert edge_type == '/'
                else:
                    vid_node, vid_metamer= g.add_child_and_complex(vid_node, complex=vid_metamer, label='Egreen', edge_type=edge_type,length=args['Ev']-args['Esen'],po=args['Epo'], diam=args['Ed'] )
                vid_node = g.add_child(vid_node, label='Esen', edge_type='<',length=args['Esen'],po=args['Epos'], diam=args['Ed'])
        else:
            if new_axe and args['Gv'] == 0.:
                if vid_node == -1:
                    vid_node= g.add_component(vid_metamer, label='Egreen', edge_type='/',length=0.,po=args['Epo'], diam=args['Ed'] )
                else:
                    vid_node, vid_metamer= g.add_child_and_complex(vid_node, complex=vid_metamer, label='Egreen', edge_type=edge_type,length=0.,po=args['Epo'], diam=args['Ed'] )
        # Sheath
        if args['Gv'] > 0.:
            if args['Gsen'] > args['Gv']:
                vid_node= g.add_child(vid_node, label='Gsen', edge_type='<',length=args['Gv'],po=args['Gpos'], diam=args['Gd'] )
            else:
                vid_node = g.add_child(vid_node, label='Ggreen', edge_type='<',length=args['Gv']-args['Gsen'],po=args['Gpo'], diam=args['Gd'] )
                vid_node = g.add_child(vid_node, label='Gsen', edge_type='<',length=args['Gsen'],po=args['Gpos'], diam=args['Gd'])

        if axe == 0:
            nodes.append(vid_node)

        # Laminae
        if args['Lv'] > 0.:
            if args['Lsen'] > args['Lv']:
                l_node= g.add_child(vid_node, label='Lsen', edge_type='+',length=args['Lv'],po=args['Lpos'],
                                      Ll=args['Ll'], Lw= args['Lw'], LcType=args['LcType'], LcIndex=args['LcIndex'], Linc=args['Linc'], Laz=args['Laz'],
                                      srb=0, srt=1)
            else:
                l_node = g.add_child(vid_node, label='Lgreen', edge_type='+',length=args['Lv']-args['Lsen'],po=args['Lpo'],
                                       Ll=args['Ll'], Lw= args['Lw'], LcType=args['LcType'], LcIndex=args['LcIndex'], Linc=args['Linc'], Laz=args['Laz'],
                                      srb=0, srt=1-(args['Lsen']/args['Lv']))
                l_node = g.add_child(l_node, label='Lsen', edge_type='<',length=args['Lsen'],po=args['Lpos'],
                                       Ll=args['Ll'], Lw= args['Lw'], LcType=args['LcType'], LcIndex=args['LcIndex'], Linc=args['Linc'], Laz=args['Laz'],
                                      srt=1, srb=1-(args['Lsen']/args['Lv']))

        
        prev_plant = plant
        prev_axe = axe
        prev_metamer = num_metamer

        


    f.close()

    return fat_mtg(g)
Пример #12
0
def new_mtg_factory(parameters, metamer_factory=adel_metamer, leaf_sectors=1,
                    leaves=None, stand=None, axis_dynamics=None,
                    add_elongation=False,
                    topology=('plant', 'axe_id', 'numphy'), split=False,
                    aborting_tiller_reduction=1.0, leaf_db=None):
    """A 'clone' of mtg_factory that uses mtg_edition functions
    """

    if leaf_db is not None:
        raise AdelDeprecationError(
            'leaf_db argument is deprecated, use leaves argument instead')

    if leaves is None:
        dynamic_leaf_db = {0: False}
        leaves = {0: None}
    else:
        dynamic_leaf_db = {k: leaves[k].dynamic for k in leaves}

    g = MTG()

    # buffers
    # for detection of newplant/newaxe
    prev_plant = 0
    prev_axe = -1

    dp = parameters
    nrow = len(dp['plant'])
    species = 0
    for i in range(nrow):
        plant, num_metamer = [int(convert(dp.get(x)[i], undef=None)) for x in
                              [topology[e] for e in [0, 2]]]
        axe = dp.get(topology[1])[i]
        args = properties_from_dict(dp, i, exclude=topology)

        # Add plant if new
        if plant != prev_plant:
            if axe != 'MS':
                raise ValueError('Main stem is expected first when a new plant '
                                 'is declared')

            position, azimuth = (0, 0, 0), 0
            if stand and len(stand) >= plant:
                position, azimuth = stand[plant - 1]
            plant_properties = {'position': position, 'azimuth': azimuth,
                'refplant_id': args.get('refplant_id'),
                                        'species': species}

            timetable = None
            if axis_dynamics:
                timetable = axis_dynamics[str(plant)][str(axe)]
            mainstem_properties = dict(timetable=timetable,
                                       HS_final=args.get('HS_final'),
                                       nff=args.get('nff'),
                                       hasEar=args.get('hasEar'),
                                       azimuth=args.get('az_insertion'))

            add_plant(g, plant_number=plant, plant_properties=plant_properties,
                      axis_properties=mainstem_properties)
            prev_plant = plant
        # Add axis
        if axe != prev_axe and axe != 'MS':
            timetable = None
            if axis_dynamics:
                timetable = axis_dynamics[str(plant)][str(axe)]
            axe_properties = dict(timetable=timetable,
                                  HS_final=args.get('HS_final'),
                                  nff=args.get('nff'),
                                  hasEar=args.get('hasEar'),
                                  azimuth=args.get('az_insertion'))
            add_axe(g, axe, plant, axis_properties=axe_properties)
            prev_axe = axe
        # Add metamer
        assert num_metamer > 0
        # args are added to metamers only if metamer_factory is none,
        # otherwise compute metamer components
        components = []
        if metamer_factory:
            xysr_key = None
            if leaves[species] is not None and 'LcType' in args and 'LcIndex' in args:
                lctype = int(args['LcType'])
                lcindex = int(args['LcIndex'])
                if lctype != -999 and lcindex != -999:
                    age = None
                    if dynamic_leaf_db[species]:
                        age = float(args[
                                        'rph']) - 0.3  # age_db = HS - rank + 1 = ph - 1.3 - rank +1 = rph - .3
                        if age != 'NA':
                            age = max(0, int(float(age)))
                    xysr_key = leaves[species].get_leaf_key(lctype, lcindex, age)

            elongation = None
            if add_elongation:
                startleaf = -.4
                endleaf = 1.6
                stemleaf = 1.2
                startE = endleaf
                endE = startE + (endleaf - startleaf) / stemleaf
                endBlade = endleaf
                if args['Gl'] > 0:
                    endBlade = args['Ll'] / args['Gl'] * (endleaf - startleaf)
                elongation = {'startleaf': startleaf, 'endBlade': endBlade,
                              'endleaf': endleaf, 'endE': endE}
            if not 'ntop' in args:
                args.update({'ntop': None})
            if not 'Gd' in args:
                args.update({'Gd': 0.19})
            args.update({'split': split})
            if args.get('HS_final') < args.get('nff'):
                for what in (
                        'Ll', 'Lv', 'Lr', 'Lsen', 'L_shape', 'Lw_shape', 'Gl',
                        'Gv', 'Gsen', 'Gd', 'El', 'Ev', 'Esen', 'Ed'):
                    args.update(
                        {what: args.get(what) * aborting_tiller_reduction})
            components = metamer_factory(Lsect=leaf_sectors, shape_key=xysr_key,
                                         elongation=elongation, leaves=leaves[species],
                                         **args)
            args = {'L_shape': args.get('L_shape')}
        #
        metamer_properties = args
        internode, sheath, blade = None, None, None
        elts = {k: [] for k in ('internode', 'sheath', 'blade')}
        if len(components) > 0:
            internode, sheath, blade = components
            internode.pop('label')
            sheath.pop('label')
            blade.pop('label')
            elts['internode'] = internode.pop('elements')
            elts['sheath'] = sheath.pop('elements')
            elts['blade'] = blade.pop('elements')

        vid_metamer = add_vegetative_metamer(g, plant, axe,
                                             metamer_properties=metamer_properties,
                                             internode_properties=internode,
                                             sheath_properties=sheath,
                                             blade_properties=blade)
        for organ in g.components(vid_metamer):
            label = g.property('label')[organ]
            if label in elts and len(elts[label]) > 0:
                insert_elements(g, organ, elts[label])
    return fat_mtg(g)
Пример #13
0
def mtg_factory(parameters, metamer_factory=None, leaf_sectors=1, leaves = None, stand = None, axis_dynamics = None, add_elongation = False, topology = ['plant','axe_id','numphy'], split=False, aborting_tiller_reduction = 1.0, leaf_db =None):
    """ Construct a MTG from a dictionary of parameters.

    The dictionary contains the parameters of all metamers in the stand (topology + properties).
    metamer_factory is a function that build metamer properties and metamer elements from parameters dict.
    leaf_sectors is an integer giving the number of LeafElements per Leaf blade
    leaves is an instance of adel.geometric_elements.Leaves class
    stand is a list of tuple (xy_position_tuple, azimuth) of plant positions
    axis_dynamics is a 3 levels dict describing axis dynamic. 1st key level is plant number, 2nd key level is axis number, and third ky level are labels of values (n, tip, ssi, disp)
    topology is the list of key names used in parameters dict for plant number, axe number and metamer number
    aborting_tiller_reduction is a scaling factor applied to reduce all dimensions of organs of tillers that will abort

    Axe number 0 is compulsory
  
    """
    
    if leaf_db is not None:
        raise AdelDeprecationError('leaf_db argument is deprecated, use leaves argument instead')
    
    if leaves is None:
        dynamic_leaf_db = False
    else:
        dynamic_leaf_db = leaves.dynamic
    
    g = MTG()

    # buffers
    # for detection of newplant/newaxe
    prev_plant = 0
    prev_axe = -1
    # current vid
    vid_plant = -1
    vid_axe = -1
    vid_metamer = -1
    vid_node = -1
    vid_elt = -1
    # vid of top of stem nodes and elements
    vid_topstem_node = -1
    vid_topstem_element = -1
    #vid of plant main stem (axe0)
    vid_main_stem = -1
    # buffer for the vid of main stem anchor points for the first metamer, node and element of tillers
    metamers = []
    nodes = []
    elts = []

    dp = parameters
    nrow = len(dp['plant'])
    
    for i in range(nrow):
        plant, num_metamer = [int(convert(dp.get(x)[i],undef=None)) for x in [topology[e] for e in [0,2]]]        
        axe = dp.get(topology[1])[i]
        mspos = int(convert(dp.get('ms_insertion')[i],undef=None))
        args = properties_from_dict(dp,i,exclude=topology)
        # Add plant if new
        if plant != prev_plant:
            label = 'plant' + str(plant)
            position = (0,0,0)
            azimuth = 0
            if stand and len(stand) >= plant:
                position,azimuth = stand[plant-1]
            vid_plant = g.add_component(g.root, label=label, edge_type='/', position = position, azimuth = azimuth, refplant_id = args.get('refplant_id'))
            #reset buffers
            prev_axe = -1            
            vid_axe = -1
            vid_metamer = -1
            vid_node = -1
            vid_elt = -1
            vid_topstem_node = -1
            vid_topstem_element = -1
            vid_main_stem = -1
            metamers = []
            nodes = []
            elts = []
            
        # Add axis
        if axe != prev_axe:
            label = ''.join(axe.split('.'))
            timetable = None
            if axis_dynamics:
                timetable = axis_dynamics[str(plant)][str(axe)]
            if axe == 'MS':
                vid_axe = g.add_component(vid_plant,edge_type='/',label=label, timetable=timetable, HS_final=args.get('HS_final'), nff=args.get('nff'), hasEar=args.get('hasEar'), azimuth=args.get('az_insertion'))
                vid_main_stem = vid_axe
            else:
                vid_axe = g.add_child(vid_main_stem, edge_type='+',label=label, timetable=timetable, HS_final=args.get('HS_final'), nff=args.get('nff'), hasEar=args.get('hasEar'), azimuth=args.get('az_insertion'))

        # Add metamer
        assert num_metamer > 0
        # args are added to metamers only if metamer_factory is none, otherwise compute metamer components
        components = []
        if metamer_factory:
            xysr_key = None
            if leaves is not None and 'LcType' in args and 'LcIndex' in args:
                lctype = int(args['LcType'])
                lcindex = int(args['LcIndex'])
                if lctype != -999 and lcindex != -999:
                    age = None
                    if dynamic_leaf_db:
                        age = float(args['rph']) - 0.3 # age_db = HS - rank + 1 = ph - 1.3 - rank +1 = rph - .3
                        if age != 'NA':
                            age = max(0,int(float(age)))
                    xysr_key = leaves.get_leaf_key(lctype, lcindex, age)
            
            elongation = None
            if add_elongation:
                startleaf = -.4
                endleaf = 1.6
                stemleaf = 1.2
                startE = endleaf
                endE = startE + (endleaf - startleaf) / stemleaf
                endBlade = endleaf
                if args['Gl'] > 0:
                    endBlade = args['Ll'] / args['Gl'] * (endleaf - startleaf)
                elongation = {'startleaf' : startleaf , 'endBlade':endBlade, 'endleaf': endleaf, 'endE': endE}
            if not 'ntop' in args:
                args.update({'ntop':None})
            if not 'Gd' in args:
                args.update({'Gd':0.19}) 
            args.update({'split':split})
            if args.get('HS_final') < args.get('nff'):
                for what in ('Ll', 'Lv', 'Lr', 'Lsen', 'L_shape', 'Lw_shape', 'Gl', 'Gv', 'Gsen', 'Gd', 'El', 'Ev', 'Esen', 'Ed'):
                    args.update({what:args.get(what) * aborting_tiller_reduction})
            components = metamer_factory(Lsect = leaf_sectors, shape_key = xysr_key, elongation = elongation, leaves = leaves, **args)
            args={'L_shape':args.get('L_shape')}
        #
        label = 'metamer'+str(num_metamer)
        new_metamer = g.add_component(vid_axe, edge_type='/', label = label, **args)
        if axe=='MS' and num_metamer==1:
            vid_metamer = new_metamer
        elif num_metamer == 1:
            # add the edge with the bearing metamer on main stem
            vid_metamer = metamers[mspos-1]
            vid_metamer =  g.add_child(vid_metamer, child=new_metamer, edge_type='+')
        else:
            vid_metamer = g.add_child(vid_metamer, child=new_metamer, edge_type='<')

        # add metamer components, if any           
        if len(components) > 0:
            # deals with first component (internode) and first element 
            node, elements = get_component(components,0)
            element = elements[0]
            new_node = g.add_component(vid_metamer, edge_type='/', **node)
            new_elt = g.add_component(new_node, edge_type='/', **element)
            if axe=='MS' and num_metamer==1: #root of main stem
                vid_node = new_node
                vid_elt =  new_elt                   
            elif num_metamer == 1: # root of tiller                   
                vid_node = nodes[mspos - 1]
                vid_node = g.add_child(vid_node, child = new_node, edge_type='+')
                vid_elt = elts[mspos - 1]
                vid_elt = g.add_child(vid_elt, child = new_elt, edge_type='+')
            else:
                vid_node = g.add_child(vid_topstem_node, child=new_node, edge_type='<')
                vid_elt = g.add_child(vid_topstem_element, child=new_elt, edge_type='<')
            #add other elements of first component (the internode)
            for i in range(1,len(elements)):
                element = elements[i]
                vid_elt = g.add_child(vid_elt, edge_type='<',**element)
            vid_topstem_node = vid_node
            vid_topstem_element = vid_elt #last element of internode  
            
            # add other components   
            for i in range(1,len(components)):
                node, elements = get_component(components,i)
                if node['label'] == 'sheath':
                    edge_type = '+'
                else:
                    edge_type = '<'
                vid_node = g.add_child(vid_node, edge_type=edge_type, **node)      
                element = elements[0]
                new_elt = g.add_component(vid_node, edge_type='/', **element)
                vid_elt = g.add_child(vid_elt, child=new_elt, edge_type=edge_type)
                for j in range(1,len(elements)):
                    element = elements[j]
                    vid_elt = g.add_child(vid_elt, edge_type='<',**element) 
                
        #update buffers 
        if axe == 'MS' :
            metamers.append(vid_metamer)
            if len(components) > 0:
                nodes.append(vid_topstem_node)
                elts.append(vid_topstem_element)
        prev_plant = plant
        prev_axe = axe
    
    return fat_mtg(g)