Example #1
0
 def root_example_rtp(self):
     """ an example used in the tests below, a main root with laterals """
     self.plant = rb.Organism(
     )  # Root has no dependency on RootSystem anymore
     p0 = rb.RootRandomParameter(self.plant)
     p0.name, p0.type, p0.la, p0.lb, p0.nob, p0.ln, p0.r, p0.dx = "taproot", 1, 1, 10, 20, (
         89. / 19.), 1, 0.5
     p0.successor = a2i([2])  # to rb.std_int_double_()
     p0.successorP = a2v([1.])  # rb.std_vector_double_()
     p1 = rb.RootRandomParameter(self.plant)
     p1.name, p1.type, p1.la, p1.ln, p1.r, p1.dx = "lateral", 2, 25, 0, 2, 0.1
     self.p0, self.p1 = p0, p1  # Python will garbage collect them away, if not stored
     self.plant.setOrganRandomParameter(
         self.p0)  # the organism manages the type parameters
     self.plant.setOrganRandomParameter(self.p1)
     self.param0 = self.p0.realize(
     )  # set up root by hand (without a root system)
     self.param0.la = 0  # its important parent has zero length, otherwise creation times are messed up
     self.param0.lb = 0
     # param0 is stored, because otherwise garbage collection deletes it, an program will crash <---
     parentroot = rb.Root(1, self.param0, True, True, 0., 0.,
                          rb.Vector3d(0, 0, -1), 0, 0, False, 0)
     parentroot.setOrganism(self.plant)
     parentroot.addNode(rb.Vector3d(0, 0, -3),
                        0)  # there is no nullptr in Python
     self.root = rb.Root(self.plant, self.p0.subType, rb.Vector3d(0, 0, -1),
                         0, parentroot, 0, 0)
     self.root.setOrganism(self.plant)
Example #2
0
 def test_sequential(self):
     """ tests if the the organ tree can be represented in a seqential list"""
     self.hand_example()
     self.add_nodes()  # only organs with number of nodes > 1 are considered
     ring = rb.Organ(self.human1, self.thumb, 0, 0, 4)  # add a ring to the thumb
     self.thumb.addChild(ring)
     ring.addNode(rb.Vector3d(0, -1, 1.6), self.thumb.getNodeId(1), 4)
     ring.addNode(rb.Vector3d(0, -1, 1.6), 4)
     organs = self.hand.getOrgans()
     self.assertEqual(len(organs), 4, "wrong number of organs")
Example #3
0
 def test_dynamics(self):
     """ tests if nodes created in last time step are correct """  #
     self.hand_example()
     self.hand.simulate(1)
     self.add_nodes()
     n0 = self.hand.getNumberOfNodes() - self.hand.getOldNumberOfNodes()
     n1 = self.little_finger.getNumberOfNodes() - self.little_finger.getOldNumberOfNodes()
     n2 = self.thumb.getNumberOfNodes() - self.thumb.getOldNumberOfNodes()
     self.assertEqual(n0, 4, "wrong number of new nodes")
     self.assertEqual(n1, 2, "wrong number of new nodes")
     self.assertEqual(n2, 2, "wrong number of new nodes")
     self.hand.simulate(1)
     n0 = self.hand.getNumberOfNodes() - self.hand.getOldNumberOfNodes()
     n1 = self.little_finger.getNumberOfNodes() - self.little_finger.getOldNumberOfNodes()
     n2 = self.thumb.getNumberOfNodes() - self.thumb.getOldNumberOfNodes()
     self.assertEqual(n0, 0, "wrong number of new nodes")
     self.assertEqual(n1, 0, "wrong number of new nodes")
     self.assertEqual(n2, 0, "wrong number of new nodes")
     self.hand.simulate(1)
     self.little_finger.addNode(rb.Vector3d(0, 1, 1.6), 6)
     n0 = self.hand.getNumberOfNodes() - self.hand.getOldNumberOfNodes()
     n1 = self.little_finger.getNumberOfNodes() - self.little_finger.getOldNumberOfNodes()
     n2 = self.thumb.getNumberOfNodes() - self.thumb.getOldNumberOfNodes()
     self.assertEqual(n0, 0, "wrong number of new nodes")
     self.assertEqual(n1, 1, "wrong number of new nodes")
     self.assertEqual(n2, 0, "wrong number of new nodes")
Example #4
0
 def add_nodes(self):
     """ used in the tests below, adds nodes to the hand example """
     self.hand.addNode(rb.Vector3d(0, 0, 0), 0)
     self.hand.addNode(rb.Vector3d(0, 0, 1.5), 0)
     self.hand.addNode(rb.Vector3d(0, -1, 1.6), 0)  # thumb
     self.hand.addNode(rb.Vector3d(0, 1, 1.6), 0)  # little finger
     thumb = self.hand.getNodeId(2)
     lf = self.hand.getNodeId(3)
     self.thumb.addNode(rb.Vector3d(0, -1, 1.6), thumb, 4)
     self.thumb.addNode(rb.Vector3d(0, -2, 2.5), 4)
     self.little_finger.addNode(rb.Vector3d(0, 1, 1.6), lf, 3)
     self.little_finger.addNode(rb.Vector3d(0, 1.7, 2.5), 3)
Example #5
0
 def test_constructors(self):
     """ tests three different kinds of constructors """
     self.root_example_rtp()
     # 1. constructor from scratch
     param = self.p0.realize()
     root = rb.Root(1, param, True, True, 0., 0., rb.Vector3d(0, 0, -1), 0,
                    0, False, 0)
     root.setOrganism(self.plant)
     root.addNode(rb.Vector3d(0, 0, -3),
                  0)  # parent must have at least one nodes
     # 2. used in simulation (must have parent, since there is no nullptr in Pyhton)
     root2 = rb.Root(self.plant, self.p1.subType, rb.Vector3d(0, 0, -1), 0,
                     root, 0, 0)
     root.addChild(root2)
     # 3. deep copy (with a factory function)
     plant2 = rb.Organism()
     root3 = root.copy(plant2)
     self.assertEqual(str(root), str(root3),
                      "deep copy: the organs shold be equal")
     self.assertIsNot(root.getParam(), root3.getParam(),
                      "deep copy: organs have same parameter set")
Example #6
0
 def rs_example_rtp(self):
     """ an example used in some of the tests below, 100 basals with laterals """
     self.rs = rb.RootSystem()
     srp = rb.SeedRandomParameter(self.rs)
     srp.subType = 0
     srp.seedPos = rb.Vector3d(0., 0., -3.)
     srp.maxB = 100
     srp.firstB = 10
     srp.delayB = 3
     self.rs.setRootSystemParameter(srp)
     p0 = rb.RootRandomParameter(self.rs)
     p0.name, p0.type, p0.la, p0.nob, p0.ln, p0.r, p0.dx = "taproot", 1, 10, 20, 89. / 19., 1, 0.5
     p0.lb = 2
     p0.successor = a2i([2])  # to rb.std_int_double_()
     p0.successorP = a2v([1.])  # rb.std_vector_double_()
     p1 = rb.RootRandomParameter(self.rs)
     p1.name, p1.type, p1.la, p1.ln, p1.r, p1.dx = "lateral", 2, 25, 0, 2, 0.1
     self.p0, self.p1, self.srp = p0, p1, srp  # Python will garbage collect them away, if not stored
     self.rs.setOrganRandomParameter(
         self.p0)  # the organism manages the type parameters
     self.rs.setOrganRandomParameter(self.p1)
Example #7
0
import matplotlib.pyplot as plt

rs = rb.RootSystem()
# name = "Triticum_aestivum_a_Bingham_2011" # is this the same as your wheat, Shehan?
name = "Zea_mays_1_Leitner_2010"
rs.openFile(name)

# Pore Geometry
x_ = (-10, -5, 1, 15)  # not 0, otherwise we start in crack
y_ = (0, 0, 0, 0)
crack = rb.SDF_PlantBox(0.25, 100, 160)  # cm
cracks_ = rb.std_vector_SDF_()
py_cracks = []

for i in range(0, len(y_)):
    v = rb.Vector3d(x_[i], y_[i], 0)
    py_cracks.append(rb.SDF_RotateTranslate(crack, v))
    cracks_.append(py_cracks[-1])

cracks = rb.SDF_Union(cracks_)
rs.setPoreGeometry(cracks)

# Increased elongation within the pores
maxS = 2  # twice the elongation rate within the pore
minS = 1  # normal elongation rate
slope = 0
soil_prop = rb.SoilLookUpSDF(cracks, maxS, minS, slope)

# Adjust Tropism
sigma = [0.4] * 10
for i in range(0, 10):
Example #8
0
import numpy as np
import matplotlib.pyplot as plt

rs = rb.RootSystem()
name = "Brassica_oleracea_Vansteenkiste_2014"
rs.openFile(name)
rs.initialize()
rs.simulate(120, True)
rs.write(name + ".vtp")

# Soil core analysis
r, depth, layers = 10, 100., 100
soilcolumn = rb.SDF_PlantContainer(r, r, depth,
                                   False)  # in the center of the root
soilcolumn2 = rb.SDF_RotateTranslate(soilcolumn, 0, 0,
                                     rb.Vector3d(10, 0, 0))  # shift 10 cm
geom = soilcolumn2

z_ = np.linspace(0, -1 * depth, layers)
fig, axes = plt.subplots(nrows=1, ncols=4, figsize=(16, 8))
for a in axes:
    a.set_xlabel('RLD (cm/cm)')
    a.set_ylabel('Depth (cm)')

# Make a root length distribution
ana = rb.SegmentAnalyser(rs)
rl_ = ana.distribution(rb.ScalarType.length, 0., depth, layers, True)
axes[0].set_title('All roots (120 days)')
axes[0].plot(rl_, z_)

# Make a root length distribution along the soil core
Example #9
0
# Manually set tropism to hydrotropism for the first ten root types
sigma = [0.4, 1., 1., 1., 1. ] * 2
for i in range(0,10):  
    p = rs.getRootTypeParameter(i+1)
    p.dx = 0.25 # adjust resolution
    p.tropismT = rb.TropismType.hydro
    p.tropismN = 2 # strength of tropism
    p.tropismS = sigma[i] 
     
# Static soil property
maxS = 0.7 # maximal 
minS = 0.1 # minimal 
slope = 5 # linear gradient between min and max (cm)
box = rb.SDF_PlantBox(30,30,2) # cm
layer = rb.SDF_RotateTranslate(box, rb.Vector3d(0,0,-16))
soil_prop = rb.SoilPropertySDF(layer, maxS, minS, slope)

# Set the soil properties before calling initialize
rs.setSoil(soil_prop)

# Initialize
rs.initialize()

# Simulate
simtime = 100 # e.g. 30 or 60 days
dt = 1
N = round(simtime/dt)
for _ in range(0,N):
    # in a dynamic soil setting you would need to update soil_prop        
    rs.simulate(dt)   
Example #10
0
from rb_tools import *

rs = rb.RootSystem()
name = "Anagallis_femina_Leitner_2010"
rs.openFile(name)

scale_elongation = rb.EquidistantGrid1D(
    0, -50, 100)  # for root elongation from 0 cm to -50 cm, 100 layers

soil_strength = np.ones((99, )) * 0.5  # some data

scales = np.exp(-0.4 * soil_strength)  # scales from some equation (TODO)

scale_elongation.data = a2v(scales)  # set proportionality factors

print("value at -3 cm", scale_elongation.getValue(rb.Vector3d(0, 0, -3)))

# Manually set scale elongation function
for i in range(0, 10):
    p = rs.getRootTypeParameter(i + 1)
    p.se = scale_elongation

# Simulation
rs.initialize()
simtime = 120.
dt = 1.
N = 120 / dt
for i in range(0, round(N)):

    # update soil model (e.g. soil_strength)
Example #11
0
import py_rootbox as rb
import math

rs = rb.RootSystem()

# Open plant and root parameter from a file
name = "Zea_mays_4_Leitner_2014"
rs.openFile(name)

# 1. Creates a square rhizotron r*r, with height h, rotated around the x-axis 
r, h, alpha = 20, 4, 45
rhizotron2 = rb.SDF_PlantContainer(r,r,h,True)
posA = rb.Vector3d(0,r,-h/2) # origin before rotation
A = rb.Matrix3d.rotX(alpha/180.*math.pi)
posA = A.times(posA) # origin after rotation
rotatedRhizotron = rb.SDF_RotateTranslate(rhizotron2,alpha,0,posA.times(-1))

# 2. A split pot experiment
topBox = rb.SDF_PlantBox(22,20,5)
sideBox = rb.SDF_PlantBox(10,20,35)
left = rb.SDF_RotateTranslate(sideBox, rb.Vector3d(-6,0,-5))
right = rb.SDF_RotateTranslate(sideBox, rb.Vector3d(6,0,-5))
box_ = rb.std_vector_SDF_()
box_.append(topBox)
box_.append(left)
box_.append(right)
splitBox = rb.SDF_Union(box_)

# 3. Rhizotubes as obstacles 
box = rb.SDF_PlantBox(96,126,130) # box
rhizotube = rb.SDF_PlantContainer(6.4,6.4,96,False) # a single rhizotube
Example #12
0
"""analysis of results using signed distance functions"""
import py_rootbox as rb
import numpy as np
import matplotlib.pyplot as plt

rs = rb.RootSystem()
name = "Brassica_oleracea_Vansteenkiste_2014"
rs.readParameters("modelparameter/" + name + ".xml")
rs.initialize()
rs.simulate(120)

# Soil core analysis
r, depth, layers = 10, 100., 100
soilcolumn = rb.SDF_PlantContainer(r, r, depth, False)  # in the center of the root
soilcolumn2 = rb.SDF_RotateTranslate(soilcolumn, 0, 0, rb.Vector3d(10, 0, 0))  # shift 10 cm

# pick one geometry for further analysis
geom = soilcolumn

z_ = np.linspace(0, -1 * depth, layers)
fig, axes = plt.subplots(nrows = 1, ncols = 4, figsize = (16, 8))
for a in axes:
    a.set_xlabel('RLD (cm/cm)')
    a.set_ylabel('Depth (cm)')

# Make a root length distribution
ana = rb.SegmentAnalyser(rs)
rl_ = ana.distribution("length", 0., depth, layers, True)
axes[0].set_title('All roots (120 days)')
axes[0].plot(rl_, z_)
Example #13
0
import py_rootbox as rb
from multiprocessing import Pool

name = "Zea_mays_4_Leitner_2014"
N = 3 # number of columns and rows
dist = 40 # distance between the root systems [cm]

# Creates and initializes N*N root systems
allRS = []
for i in range(0,N):
    for j in range(0,N):
         rs = rb.RootSystem()
         rs.openFile(name) 
         rs.getRootSystemParameter().seedPos = rb.Vector3d(dist*i,dist*j,-3) # set position of seed [cm]
         allRS.append(rs)
         rs.initialize()

# Simulate parallel
simtime = 120
def simulate(i):
    allRS[i].simulate(simtime)
     
pool = Pool()
param_space = range(0,len(allRS))   
d = [1 for res in pool.imap(simulate,param_space)] 

# Export results as single vtp files (as polylines)
c = 0
ana = rb.SegmentAnalyser() # see example 3b
for rs in allRS:
      c += 1 # root system number
Example #14
0
"""multiple root systems"""
import py_rootbox as rb

name = "Zea_mays_4_Leitner_2014"
simtime = 120
N = 3  # number of columns and rows
dist = 40  # distance between the root systems [cm]

# Initializes N*N root systems
allRS = []
for i in range(0, N):
    for j in range(0, N):
        rs = rb.RootSystem()
        rs.readParameters("modelparameter/" + name + ".xml")
        rs.getRootSystemParameter().seedPos = rb.Vector3d(
            dist * i, dist * j, -3.)  # cm
        rs.initialize()
        allRS.append(rs)

# Simulate
for rs in allRS:
    rs.simulate(simtime, True)

# Export results as single vtp files (as polylines)
ana = rb.SegmentAnalyser()  # see example 3b
for i, rs in enumerate(allRS):
    vtpname = "../results/example_2b_" + str(i) + ".vtp"
    rs.write(vtpname)
    ana.addSegments(rs)  # collect all

# Write all into single file (segments)
Example #15
0
def call(cc):

    rs = rb.RootSystem()
    #name = "Triticum_aestivum_a_Bingham_2011" # is this the same as your wheat, Shehan?
    name = "wheat"
    rs.openFile(name)

    # Pore Geometry
    x_ = (-10, -5, 5, 15)  # not 0, otherwise we start in crack
    y_ = (0, 0, 0, 0)
    #x_=(-10, -5)
    #y_=(0,0)
    crack = rb.SDF_PlantBox(1.0, 100, 160)  # cm
    cracks_ = rb.std_vector_SDF_()
    py_cracks = []

    for i in range(0, len(y_)):
        v = rb.Vector3d(x_[i], y_[i], 0)
        py_cracks.append(rb.SDF_RotateTranslate(crack, v))
        cracks_.append(py_cracks[-1])

    cracks = rb.SDF_Union(cracks_)
    rs.setPoreGeometry(cracks)

    # Increased elongation within the pores
    maxS = 2  # twice the elongation rate within the pore
    minS = 1  # normal elongation rate
    slope = 0
    soil_prop = rb.SoilLookUpSDF(cracks, maxS, minS, slope)

    # Adjust Tropism
    sigma = [0.4] * 10
    for i in range(0, 10):
        p = rs.getRootTypeParameter(i + 1)
        p.dx = 0.25  # adjust resolution
        p.tropismT = rb.TropismType.gravi
        p.tropismN = 1  # strength of tropism
        p.tropismS = sigma[i]
        p.se = soil_prop

    # Pore Local Axes
    v1 = rb.Vector3d(0.67, 0, 0)
    v2 = rb.Vector3d(0, 0.67, 0)
    v3 = rb.Vector3d(0, 0, 0.67)
    rs.setPoreLocalAxes(rb.Matrix3d(v1, v2, v3))

    # Pore Conductivity Tensor
    t1 = rb.Vector3d(1.33, 0, 0)
    t2 = rb.Vector3d(0, 50.33, 0)
    t3 = rb.Vector3d(0, 0, 50.33)
    rs.setPoreConductivity(rb.Matrix3d(t1, t2, t3))

    # Set up depth dependent elongation scaling function
    scale_elongation = rb.EquidistantGrid1D(
        0, -160, 17)  # todo: replace this by reading in data from CSV file
    scales = np.zeros(
        len(scale_elongation.grid)
    ) + 0.1  # scales from some equation (scale = function(soil_strength) ), where scale in (0,1)
    scale_elongation.data = a2v(scales)  # set proportionality factors

    # Proportionally scale this function
    se2 = rb.ProportionalElongation()
    se2.setBaseLookUp(scale_elongation)

    # multiply the scale elongation functions
    se3 = rb.MultiplySoilLookUps(se2, soil_prop)

    # Manually set scaling function
    for i in range(0, 10):
        p = rs.getRootTypeParameter(i + 1)
        p.se = se3

    # Initialize
    rs.initialize()

    # Simulate
    simtime = 30 * 8  # e.g. 30 or 60 days
    dt = 1  #0.5 * 1./24.
    N = round(simtime / dt)

    for i in range(0, N):

        # time-dependent and depth-dependent scaling function
        scales = np.loadtxt('W2.csv', delimiter=';',
                            usecols=i)  # reading in ith column from CSV file
        scale_elongation.data = a2v(scales *
                                    1.00)  # set the data of scale elongation
        rs.simulate(dt)

    # Export results (as vtp)
    #rs.write("../results/crack.vtp")

    # Export cracks
    #rs.setGeometry(cracks)  # just for vizualisation
    #rs.write("../results/crack.py")

    z_ = np.linspace(0, -1 * 160, 160)

    # Make a root length distribution
    ana = rb.SegmentAnalyser(rs)
    rl_ = ana.distribution(rb.ScalarType.length, 0, 160, 160, True)
    np.set_printoptions(precision=4)
    np.savetxt("cw_" + str(cc) + ".txt", rl_, fmt="%.2f")
Example #16
0
# Distance Grid
nx = 1
ny = 1000
nz = 1000
X = np.linspace(-0.25 / 2, 0.25 / 2, nx)
Y = np.linspace(-27 / 2, 27 / 2, ny)
Z = np.linspace(0, -27, nz)
X_, Y_, Z_ = np.meshgrid(X, Y, Z, indexing="ij")  # stupid matlab default
D = np.zeros(X_.shape)
print(D.shape)

for i in range(0, X_.shape[0]):
    for j in range(0, X_.shape[1]):
        for k in range(0, X_.shape[2]):
            D[i, j, k] = rs_sdf.getDist(
                rb.Vector3d(X_[i, j, k], Y_[i, j, k], Z_[i, j, k]))

D[D < -100] = -10

fig1 = plt.figure()
ax = plt.axes()

D_ = D[int(nx / 2), :, :]
levels = np.linspace(-5, 0.05, 100)
cs = ax.contourf(Y_[int(nx / 2), :, :],
                 Z_[int(nx / 2), :, :],
                 D_,
                 levels=levels,
                 cmap='jet')  # levels = levels, locator = ticker.LogLocator(),
ax.set_xlabel('x')
ax.set_ylabel('z')
Example #17
0
p0.tropismN = 1.
p0.tropismS = 0.2

p1.name = "lateral"
p1.subType = 2
p1.la = 25
p1.las = 10  # add standard deviation
p1.ln = 0
p1.r = 2
p1.dx = 0.1
p1.tropismS = 0.3

rs.setOrganRandomParameter(p0)
rs.setOrganRandomParameter(p1)

# Root system parameter (neglecting shoot borne)

rsp = rb.SeedRandomParameter(rs)
rsp.seedPos = rb.Vector3d(0., 0., -3.)
rsp.maxB = 100
rsp.firstB = 10.
rsp.delayB = 3.
rs.setRootSystemParameter(rsp)

rs.initialize()
rs.simulate(40, False)

rs.write("../results/example_3c.vtp")

print("done.")
Example #18
0
import py_rootbox as rb
import numpy as np
from rb_tools import *
import matplotlib.pyplot as plt

N = 4  # layers between a and b
a = -3  # cm
b = -7  # cm
scale_elongation = rb.EquidistantGrid1D(a, b, N + 1)
scales = range(0, N)
scale_elongation.data = a2v(scales)  # set proportionality factors

z_ = np.linspace(0, -10, 1000)
y_ = np.zeros((1000))

for i, z in enumerate(z_):
    y_[i] = scale_elongation.getValue(rb.Vector3d(0, 0, z))

plt.plot(z_, y_)

plt.plot(a, 0, "r*")  # interval borders
plt.plot(b, N - 1, "r*")

plt.xlabel("z (cm)")
plt.ylabel("y value (?)")

plt.show()

print("done.")
Example #19
0
import py_rootbox as rb
import math

rs = rb.RootSystem()
name = "Anagallis_femina_Leitner_2010"
rs.openFile(name)

# box with a left and a right compartment for analysis
sideBox = rb.SDF_PlantBox(10, 20, 50)
left = rb.SDF_RotateTranslate(sideBox, rb.Vector3d(-4.99, 0, 0))
right = rb.SDF_RotateTranslate(sideBox, rb.Vector3d(4.99, 0, 0))
leftright = rb.SDF_Union(left, right)
rs.setGeometry(leftright)

# left compartment has a minimum of 0.01, 1 elsewhere
maxS = 1.  # maximal
minS = 0.01  # minimal
slope = 1.  # [cm] linear gradient between min and max
leftC = rb.SDF_Complement(left)
soilprop = rb.SoilLookUpSDF(leftC, maxS, minS, slope)  # for root elongation
soilprop2 = rb.SoilLookUpSDF(left, 1., 0.002, slope)  # for branching

# Manually set scaling function and tropism parameters
sigma = [0.4, 1., 1., 1., 1.] * 2
for i in range(0, 10):
    p = rs.getRootTypeParameter(i + 1)
    p.dx = 0.25  # adjust resolution
    p.tropismS = sigma[i]

    # 1. Scale elongation
    p.se = soilprop