コード例 #1
0
 def __init__(self,
              heading=Vector3(0., 1., 0.),
              up=Vector3(0., 0., 1.),
              left=Vector3(1., 0., 0.)):
     self.heading = heading
     self.left = up
     self.up = left
コード例 #2
0
def test_rupture():
    torque = Vector3(0, 0, 50e5)
    radius = 1
    assert rupture(torque, radius) == False
    torque = Vector3(0, 0, 50e6)
    radius = 1
    assert rupture(torque, radius) == True
コード例 #3
0
def test_reorient_frame():
    v1 = Vector3(2, 1, 1)
    v2 = Vector3(1, 2, 1)
    v3 = Vector3(1, 1, 2)
    rotation_velocity = Vector3(1, 1, 1)
    length = 1
    frame = Frame(v1, v2, v3)
    reorient_frame(frame, rotation_velocity, length)
コード例 #4
0
ファイル: test_v3d.py プロジェクト: pradal/MAppleT
def test_norm():
    """norm is equivalent to length in v3 mapplet"""
    from math import sqrt
    v=Vector3(0,4,4);
    v.normalize();
    assert v == Vector3(0, sqrt(2)/2, sqrt(2)/2)
    #v = v.__norm__()
    v = norm(v)
    assert v>1-tol and v<1+tol
コード例 #5
0
def reorient_frame(initial_hlu, rotation_velocity, rv_norm, length):
    h = Vector3(initial_hlu.heading)
    h.normalize()
    l = Vector3(initial_hlu.left)
    l.normalize()

    #vl = rotation_velocity.normalize() #_ look at v3d length definition
    #vl is replaced by rv_norm

    if fabs(rv_norm*length) >= 0.01:
        h = rotate(rotation_velocity.x, rotation_velocity.y, rotation_velocity.z, rv_norm*length, h.x, h.y, h.z)
        l = rotate(rotation_velocity.x, rotation_velocity.y, rotation_velocity.z, rv_norm*length, l.x, l.y, l.z)
    h.normalize()
    l.normalize()
    return Frame(h, l, cross(h, l))
コード例 #6
0
def test_axis_angle():
    from openalea.stocatree.physics import _AxisAngle
    angle = 0.5
    v3 = Vector3(0, 0.5, 0.5)
    aa = _AxisAngle(v3, angle, check=True)
    aa.axis_angle_to_quaternion()

    w3 = Vector3(0.2, 0.2, 0.2)
    aa.rotate(w3)
    r1 = aa._rotate1(w3)
    r2 = aa._rotate2(w3)
    r3 = aa._rotate3(w3)
    r4 = aa._rotate4(w3)
    aa.angle = 0.
    r5 = aa.rotate(w3)
コード例 #7
0
def reaction_wood_target(up, heading, previous_heading):
    cos_gh  = Vector3(0.0, 0.0, 1.0) * heading
    cos_pu = previous_heading * up
    cos_ph = previous_heading * heading

    inclination = 0
    if cos_pu*cos_ph >= 0.0:
        try:
            inclination = acos(cos_ph)
        except:
            #pass
            print str(cos_ph)
    else:
        try:
            inclination = -acos(cos_ph)
        except:
            print str(cos_ph)
    percentage  = 0.1635 * (1.0 - cos_gh) - 0.1778 * inclination;
    r = 3.14159*2. * percentage;

    if (r < 0.0):
        r = 0.0
    elif (r > 3.14159):
        r = 3.141459

    return r
コード例 #8
0
 def _rotate1(self, v):
     """method of rotation that uses Quaternion method"""
     q = self.axis_angle_to_quaternion()
     """print '========'
     # orginal method 
     w = q[0] * v.x + q[1] * v.y + q[2] * v.z
     x = q[3] * v.x + q[1] * v.z - q[2] * v.y
     y = q[3] * v.y - q[0] * v.z + q[2] * v.x
     z = q[3] * v.z + q[0] * v.y - q[1] * v.x
     res = Vector3( w * q[0] + x * q[3] - y * q[2] + z * q[1],
                     w * q[1] + x * q[2] + y * q[3] - z * q[0],
                     w * q[2] - x * q[1] - y * q[0] + z * q[3]
                   )
     print res.x, res.y, res.z 
     """
     a = q[3]
     b = q[0]
     c = q[1]
     d = q[2]
     t2 = a * b
     t3 = a * c
     t4 = a * d
     t5 = -b * b
     t6 = b * c
     t7 = b * d
     t8 = -c * c
     t9 = c * d
     t10 = -d * d
     v1new = 2 * ((t8 + t10) * v.x + (t6 - t4) * v.y +
                  (t3 + t7) * v.z) + v.x
     v2new = 2 * ((t4 + t6) * v.x + (t5 + t10) * v.y +
                  (t9 - t2) * v.z) + v.y
     v3new = 2 * ((t7 - t3) * v.x + (t2 + t9) * v.y + (t5 + t8) * v.z) + v.z
     return Vector3(v1new, v2new, v3new)
コード例 #9
0
def reorient_frame(initial_hlu, rotation_velocity, length):
    """Reorient frame

    The initial frame is an HLU (Heading Up Left, turtle axes) frame, made of tree orthogonal vectors that indicate the heading, 
    upwards and left directions of the beam.

    Then, the HLU frame is rotated around the rotation velocity vector.

    length is used to rotate the frame only when the product of rotation_velocity and length
    is large enough.

    :param initial_hlu: a `Frame` defining the initial HLU frame at the beginning of the season
    :param rotation_velocity:
    :param length: length of metamer

    :returns: a new rotated frame 

    .. todo:: describe the algorithm

    .. note:: this function has been optimised to used the rotate function from
        optimisation.pyx that computes the rotation of the AxisAngle around a
        given vector. It replaces the AxisAngle./quaternion of the origninal code of
        MappleT.

     ::

        import openalea.stocatree.optimisation as optimisation
        optimisation.rotate(hlu, Vector3, length)


    """
    h = Vector3(initial_hlu.heading)
    h.normalize()
    l = Vector3(initial_hlu.left)
    l.normalize()
    vl = rotation_velocity.normalize()  #_ look at v3d length definition
    if abs(vl * length) >= 0.01:
        h = optimisation.rotate(rotation_velocity.x, rotation_velocity.y,
                                rotation_velocity.z, vl * length, h.x, h.y,
                                h.z)
        l = optimisation.rotate(rotation_velocity.x, rotation_velocity.y,
                                rotation_velocity.z, vl * length, l.x, l.y,
                                l.z)
    h.normalize()
    l.normalize()
    return Frame(h, l, cross(h, l))
コード例 #10
0
    def calculate_rotation_velocity(self, simulation, stake=True):
        r"""Calculate the rotation velocity

        :param bool stake: (default is True)

        Calculate the rotation velocity of a torque

        .. math::

            \Omega_\tau = \frac{\tau}{R}

        where R is the rigidity and :math:`\tau` the cumulated total torque.

        The memory rotation depends on the lost masses

        .. math::

            \Omega_m =  \frac{\Delta m }{m} ?

        .. math::

            \Omega' = \Omega_\tau + \Omega_m

        .. math::

            \Omega = \Omega' * \alpha + (1-\alpha)*\Omega
        """
        if stake and self.trunk:
            self.rotation_velocity = Vector3()
            return None

        # Calculate the rotation velocity (Taylor-Hell, 2005)
        self.acting_rotation = self.cumulated_torque / self.rigidity

        # Hypothesis: the shape memory is proportional to the mass
        # removed (inspired by Almeras. 2002)
        # The if-else here was added by Han on 22-04-2011 to avoid the case that
        # self.pre_harvest_mass is sometimes euqal to 0
        if simulation.events.harvest.active:
            if self.pre_harvest_mass != 0:
                delta_mass = (self.pre_harvest_mass -
                              self.cumulated_mass) / self.pre_harvest_mass
            else:
                delta_mass = 0
            #delta_mass = 0.5
            self.rotation_memory = self.pre_harvest_rotation * delta_mass

        new_rotation_velocity = self.acting_rotation + self.rotation_memory
        # Hypothesis: The total rotation velocity is the sum of the acting
        # rotation and the shape memory
        self.rotation_velocity = new_rotation_velocity * \
            simulation.rotation_convergence.step + self.rotation_velocity * \
            (1.0 - simulation.rotation_convergence.step)

        self.rv_norm = self.rotation_velocity.normalize()
コード例 #11
0
    def pruning_reaction_angle(self, phylo_angle):
        """
      :param hlu: Frame
      :param phyllo_angle: float
      :param farthest_apex: int
      :param number: int
      :returns branching_angle: float
      :returns phyllotactic_angle: float
      """

        #Determining angle between heading and vertical
        angle_to_vert = round(
            acos(dot(self.hlu.heading.normed(), Vector3(0, 0, 1))), 2)
        print "####### Angle from heading to vert : ", angle_to_vert

        #Fixing the ratio of that angle to be used as branching angle depending on the pruning intensity
        vert_ratio = 1 - ((1.0 * self.number) /
                          (self.number + self.farthest_apex))
        print "####### Ratio from length : ", vert_ratio

        new_branching_angle = vert_ratio * angle_to_vert
        print "####### Angle chosen : ", new_branching_angle

        #Determining a possible phyllotactic angle that will divert the less from vertical
        angles = []

        #for i in range(360):
        #  hlu = rotate_frame_at_branch(self.hlu, new_branching_angle, i)
        #  angles.append((acos(dot(hlu.heading.normed(), Vector3(0,0,1)))))
        #return new_branching_angle, angles.index(min(angles))

        for i in range(5):
            hlu = rotate_frame_at_branch(
                self.hlu, new_branching_angle,
                i * phylo_angle + self.phyllotactic_angle)
            angles.append((acos(dot(hlu.heading.normed(), Vector3(0, 0, 1)))))
        return new_branching_angle, angles.index(
            min(angles)) * phylo_angle + self.phyllotactic_angle
コード例 #12
0
def test_reaction_wood_target():
    up = Vector3(1., 1., 1.)
    heading = Vector3(1., 0., -0.)
    previous_heading = Vector3(-1., -1., -1.)

    reaction_wood_target(up, heading, previous_heading)

    #r statement
    up = Vector3(0., 0., 1.)
    heading = Vector3(0., 1., 0.)
    previous_heading = Vector3(3.1, 1., 1.)
    reaction_wood_target(up, heading, previous_heading)
コード例 #13
0
def rotate(v3x,  v3y,  v3z,  angle,  vx,  vy,  vz):
    c =  cos(angle)
    t2 =  1 - c
    t6 =  t2*v3x
    t7 =  t6*v3y
    s =  sin(angle)
    t9 =  s*v3z
    t11 = t6*v3z
    t12 = s*v3y
    t19 = t2*v3y*v3z
    t20 = s*v3x
    t24 = v3z*v3z
    R00 = c + t2*v3x*v3x
    R01 = t7 - t9
    R02 = t11 + t12
    R10 = t7 + t9
    R11 = c + t2*v3y*v3y
    R12 = t19 - t20
    R20 = t11 - t12
    R21 = t19 + t20
    R22 = c + t2*t24
    return Vector3(R00*vx+R01*vy+R02*vz, R10*vx+R11*vy+R12*vz, R20*vx+R21*vy+R22*vz)
コード例 #14
0
ファイル: test_v3d.py プロジェクト: pradal/MAppleT
def test_vector3_cross():
    """equivalent of % operator in v3 mapplet"""
    v1 = Vector3(1.0, 0, 0)
    v4 = Vector3(4.0, 5, -1)
    assert cross(v4,v1) == Vector3(0, -1, -5)
コード例 #15
0
def test_stress():
    torque = Vector3(100, 100, 500)
    radius = 0.1
    stress(torque, radius)
コード例 #16
0
ファイル: test_v3d.py プロジェクト: pradal/MAppleT
def test_normSquared():
    """normSquared is equivalent to length_sq in v3 mapplet"""
    from math import sqrt
    v=Vector3(1,2,3);
    assert 14==normSquared(v)
コード例 #17
0
def test_rotate_frame_at_branch():
    v1 = Vector3(2, 1, 1)
    v2 = Vector3(1, 2, 1)
    v3 = Vector3(1, 1, 2)
    frame = Frame(v1, v2, v3)
    rotate_frame_at_branch(frame, 45, 45)
コード例 #18
0
def test_Frame():
    v1 = Vector3(2, 1, 1)
    v2 = Vector3(1, 2, 1)
    v3 = Vector3(1, 1, 2)
    f = Frame(v1, v2, v3)
    print f
コード例 #19
0
ファイル: test_v3d.py プロジェクト: pradal/MAppleT
def test_vector3():
    tropism = Vector3(0,0.1,0)
    tropism.y += .1;
    assert tropism.y == 0.2
    assert tropism.x == 0.
    assert tropism.z == 0
コード例 #20
0
    def __init__(self,
                 phyllotactic_angle=-144.0,
                 branching_angle=-45.,
                 floral_angle=-10.,
                 tropism=0.1,
                 preformed_leaves=8,
                 spur_death_probability=0.3,
                 inflorescence_death_probability=0.2):
        """**Constructor**

        ================================== ============== ==============
        parameters                         default values  units
        ================================== ============== ==============
        phyllotactic_angle                 144             degrees
        branching angle                    45              degrees
        ================================== ============== ==============


        :param phyllotactic_angle: default is -144 degrees
        :param branching_angle: default is -45 degrees
        :param floral_angle: default -10 degrees
        :param spur_death_probability: default is0.3
        :param tropism: the z value of the tropism vector (efault is z=0.1)
        :param preformed_leaves: 8
        :param inflorescence_death_probability: default is 0.2

        In addition, several attributes are defined:

            * an initial HLU frame is also defined to be along the z axis.
            * trunk_radius in meters
            * fruits : number of fruits
            * cross_sectional_area in m^2
            * growth units
            * fruit_load

        .. todo:: finalise this doc. end_terminal_expansion not used !
            Neither in original code.

        """
        self.angle_unit = 'radians'
        self.phyllotactic_angle = phyllotactic_angle / 180.0 * pi
        self.branching_angle = branching_angle / 180.0 * pi
        self.floral_branching_angle = floral_angle / 180.0 * pi
        # the original mappelt code the initial hlu frame as follows
        #self.initial_hlu  = Frame(Vector3( 0.0, 1.0, 0.0), Vector3( 0.0, 0.0, 1.0), Vector3(-1.0, 0.0, 0.0)); #
        # this one seems to work, which seems logival since HLU when the tree starts corresponds to zxy or zyx.
        self.initial_hlu = Frame(Vector3(0.0, 0.0, 1.0),
                                 Vector3(0.0, 1.0, 0.0), Vector3(1., 0.0, 0.))
        # the original one from mapplet leads to trunk being horizontal
        #self.initial_hlu                     = Frame(Vector3( 0.0, 1.0, 0.0), Vector3( 0.0, 0.0, 1.0), Vector3(-1., 0.0, 0.));
        self.spur_death_probability = spur_death_probability
        self.inflorescence_death_probability = inflorescence_death_probability
        self.preformed_leaves = preformed_leaves

        # variables
        self.trunk_radius = 0.0  #in m
        self.trunk_cross_sectional_area = 0  #cm**2'
        self.fruit_load = 0.  #1/m**2
        #self.end_terminal_expansion          = convert_to_day(11, 1);
        # Count the number of growth units (note that the parent_unit_id starts from 0):
        # Commented by Han on 02-05-2011
        self.growth_units = 0
        # Count the number of first-order branches (note that the parent_branch_id starts from 0)
        # Added by Han on 02-05-2011
        self.first_branches = 0
        self.tropism = Vector3(0.0, 0.0, tropism)
        #// same as in N original mapplet
        self.fruits = 0
コード例 #21
0
def test_clamp():
    """TO FINALISE"""
    v = Vector3(0.000001, 1, 1)
    clamp_v3d_components_if_near_zero(v)
コード例 #22
0
 def test_update_position(self):
     from vplants.plantgl.all import Vector3
     self.data.update_position()
     self.data.update_position(left_metamer_position=Vector3(1, 1, 1))
コード例 #23
0
    def __init__(self,
                 floral=False,
                 number=0,
                 hlu=None,
                 zone=None,
                 observation=None,
                 parent_observation=None,
                 parent_unit_id=None,
                 parent_fbr_id=None,
                 parent_tree_id=0,
                 p_angle=0.,
                 b_angle=0.,
                 wood=None,
                 internode=None,
                 fruit=None,
                 leaf=None):
        """**Constructor**


        Leaf, internode, fruit and wood must be provided

        :param bool floral:
        :param int number: rank of the metamer in the GU
        :param Frame hlu:
        :param int zone:
        :param int observation:
        :param float p_angle: phyllotactic angle in degrees
        :param float b_angle: branching angle in degrees
        :param wood: a :class:`~openalea.stocatree.wood.Wood` instance
        :param internode: a :class:`~openalea.stocatree.internode.Internode` instance
        :param fruit: a :class:`~openalea.stocatree.fruit.Fruit` instance
        :param leaf: a :class:`~openalea.stocatree.leaf.Leaf` instance

        """
        """
        Parameters added by Han, commented on 02-05-2011:
        parent_unit_id: the id of the parent unit (the unit where the metamer is located)
        parent_fbr_id: the id of the parent branch (the branch where the metamer is located)
        # If the parent_fbr_id is 0, it represents "trunk".
        """
        if leaf == None:
            raise ValueError("leaf must be provided as a Leaf class")
        else:
            self.leaf = leaf

        if fruit == None:
            raise ValueError("fruit must be provided as a Fruit class")
        else:
            self.fruit = fruit

        if wood == None:
            raise ValueError("wood must be provided as a Wood class")
        else:
            self.wood = wood

        if internode == None:
            raise ValueError("internode must be provided as a Internode class")
        else:
            self.internode = internode

        self.number = number  # Yield the rank of the metamer
        self.closest_apex = 0  # Distance to the closest apex
        self.farthest_apex = 0  # Distance to the farthest apex
        self.sons_nb = 0  # Cumulated sum of metamer sons
        self.pruning_react = True  # wether the metamer could react to pruning
        self.pruned_data = None  # If metamer was below the pruning point, this will contain the data required to determine pruning reaction that will be passed on previous metamers (rankwise), i.e. reacting position from cutting point [0,2], closest_apex, farthest_apex, sons_nb

        self.observation = observation
        self.parent_observation = parent_observation  # Yield the shoot type of that metamer
        self.parent_unit_id = parent_unit_id
        self.parent_fbr_id = parent_fbr_id
        self.parent_tree_id = parent_tree_id

        try:
            self.zone = zone_converter[zone]
        except:
            print zone

        self.hlu = hlu
        self.cumulated_mass = 0.  #in 'kg'
        self.radius = self.leaf.petiole_radius  # petiole_radius is in m
        self.offset = 0  # used to shift first branching node to keep it from disapearing into cambial growth
        self.cumulated_torque = Vector3(0., 0., 0.)
        self.developped = False  #// to avoid more than 1 lateral shoot
        self.phyllotactic_angle = p_angle  #// azimuth / parent metamer (around h) #TODO must be in [0,2pi]
        self.branching_angle = b_angle  #TODO must be in [0,2pi]
        self.rigidity = 0.  # in Pa * m**4
        self.age = 0  # in days
        self.year = 0
        self.length = self.internode._min_length  # internode units is in m
        if floral == True:
            self.fruit.state = 'flower'
        else:
            self.fruit.state = 'no_flower'

        self.trunk = False

        #Vector3D are initilised to 0,0,0 but could have been anything.
        self.rotation_memory = Vector3(
            0., 0., 0.)  # // remaining rotation after harvest
        self.rotation_velocity = Vector3(
            0., 0., 0.)  # acting_rotation + rotation_memory
        self.acting_rotation = Vector3(
            0., 0., 0.)  # // due to mass and tropism actions
        self.position = Vector3(0., 0., 0.)  #// absolute

        #Rotation velocity is normalized and its norm is saved in rv_norm
        self.rv_norm = self.rotation_velocity.normalize()

        self.season_initial_heading = self.hlu.heading  #// used to compute reaction wood
        self.external_layer = 0  #// index in vector cambial::pool

        #TEST
        self.layers = []
        self.nlayers = 0
        self.total_second_moment_of_area = 0.

        self.pre_harvest_mass = 0.0  #in g #// used to compute rotation_memory
        self.pre_harvest_radius = 0  #in meters
        self.pre_harvest_rotation = Vector3(0.0, 0.0, 0.0)
        """

        // The conditional on 'number' is to get around a bug in LPFG.
        // LPFG creates lots of temporary instances of objects that are
        // never used.  We only want the constructor to have an effect
        // when the object is one that is actually used in the structure
        // and not a temporary object.
        """
        if (self.number):
            self.season_initial_heading = self.hlu.heading
            l = cambial_layer(thickness=self.radius, radius=0)
            self.layers.append(l)
            self.nlayers += 1

        #The following attributes were added by Han on 29-04-2011
        #They are mainly used in the "attribute_list" for statistics output
        self.leaf_state = ''
        self.leaf_area = 0
        #Leaf area returned from plangGL after light interception
        self.ta_pgl = 0
        #Silhouette area returned from plantGL after light interception
        self.sa_pgl = 0
        self.star_pgl = 0

        #Added by Han on 11-07-2012, as a flag to control first-year sylleptic growth
        self.sylleptic = False
        #Flag to set if a metamer should be pruned
        self.to_prune = False
        #Flag to signal a cut
        self.cut = False
コード例 #24
0
def reaction_wood_target(up, heading, previous_heading):
    r"""Reaction wood target

    The reaction wood is proportional to the change in inclination
    over a season (Almeras, 2001). Hypothesis: The reaction wood is
    also proportional to the inclination from gravity.

    .. math::

        P_r = 0.1635 - 0.1778 \theta_s

    where :math:`P_r` is the radial portion of the outermost cambial layer that
    become reaction wood and :math:`\theta_s` is the change in inclination of the
    internode over the season (i.e., the cahnge in :math:`\vec{H}`, the heading
    vector of the HLU frame since the start of the spring.

    In order to also take into account the gravity, The firs term in the equation above
    must be multiply by a coefficient that varies with the angle between the internode and
    :math:`\vec{u}` a unit vector opposite to  :math:`-\vec{g}`

    :param Vector3 up:
    :param heading: vector3
    :param previous_heading: vector3
    :returns: reaction_wood (double)
    """
    #multiply by gravity normalised
    cos_gh = Vector3(0.0, 0.0, 1.0) * heading
    cos_pu = previous_heading * up
    cos_ph = previous_heading * heading

    if cos_pu * cos_ph >= 0.0:
        try:
            inclination = acos(cos_ph / 1.0001)
        except:
            print 'Problem with acos(cos_ph) where cos_ph=%f' % cos_ph
            inclination = 0.
    else:
        try:
            inclination = -acos(cos_ph)
        except:
            tol = 1e-6
            try:

                inclination = -acos(cos_ph - tol)
                ValueError('try again with tol set %s %s' %
                           (cos_ph, cos_ph - 1.))
            except:
                print ' cos+_pu=', cos_pu, ' cos_ph=', cos_ph, 'cosgh=', cos_gh
                print cos_ph - 1.
                raise ValueError('Problem with acos(cos_ph) where cos_ph=%f' %
                                 cos_ph)

    percentage = 0.1635 * (1.0 - cos_gh) - 0.1778 * inclination
    r = constants.two_pi * percentage
    #if r!=0:
    #    print ' cos+_pu=', cos_pu, ' cos_ph=', cos_ph, ' incl=',inclination, ' percentage', percentage, 'r=', r

    if (r < 0.0):
        r = 0.0
    elif (r > constants.pi):
        r = constants.pi

    return r
コード例 #25
0
ファイル: test_v3d.py プロジェクト: pradal/MAppleT
def test_operator():
    v1 = Vector3(1.0, 0, 0)
    v2 = Vector3(0, 1, 0)
    assert v1+v2 == Vector3(1,1,0)
    assert v1-v2 == Vector3(1,-1,0)
    assert v1*2 == Vector3(2,0,0)
コード例 #26
0
from openalea.stocatree.tools.simulation import SimulationStocatree
from openalea.stocatree.sequences import Markov, generate_sequence, terminal_fate
from openalea.stocatree.metamer import metamer_data
from openalea.stocatree.growth_unit import growth_unit_data
from openalea.sequence_analysis import HiddenSemiMarkov
from vplants.plantgl.all import Vector3, cross, Viewer
from openalea.stocatree.srandom import boolean_event
from openalea.stocatree.physics import rotate_frame_at_branch, rupture
from openalea.stocatree.tools.surface import *
from openalea.stocatree import get_shared_data

import time
import os
import datetime

gravity = Vector3(0.0, 0.0, -9.81)
#// in m s^-2 original mappleT

# First, read the configuration file
options = ConfigParams(get_shared_data('stocatree.ini'))

# Then, define a data structure to store outputs such as MTG, counts, sequences and so on
data = Data(options=options, revision=__revision__)

# Initialise the simulation
simulation = SimulationStocatree(dt=options.general.time_step,
                                 starting_date=options.general.starting_year,
                                 ending_date=options.general.end_year)

# Read PGLshape surfaces
stride = int(options.stocatree.stride_number)