def test_copy(): # Parameter simtime = 30. # days dt = 1. N = round(simtime / dt) # steps name = "Anagallis_femina_Leitner_2010" print("\n1 original") rs = pb.RootSystem() rs.openFile(name) rs.setSeed(1) # before initialize to mimic rs.initialize() rs2 = pb.RootSystem(rs) rs.simulate(simtime) l1 = np.sum(v2a(rs.getParameter("length"))) print("total length", l1) print("\n2 copy") rs2.simulate(simtime) l2 = np.sum(v2a(rs2.getParameter("length"))) print("total length", l2) print("\n3 rebuild same") rs3 = pb.RootSystem() rs3.openFile(name) rs3.setSeed(1) rs3.initialize() rs3.simulate(simtime) l3 = np.sum(v2a(rs3.getParameter("length"))) print("total length", l3) return
def test_copy(self): """ checks if the root system can be copied, and if randomness works """ seed = 110 # random seed name = "Brassica_oleracea_Vansteenkiste_2014" rs = pb.RootSystem() # the original rs.readParameters("../modelparameter/rootsystem/" + name + ".xml") rs.setSeed(seed) rs.initialize(False) rs2 = rs.copy() # copy root system n1 = rs.rand() self.assertIsNot(rs2, rs, "copy: not a copy") self.assertEqual(str(rs), str(rs2), "copy: the organisms should be equal") self.assertEqual(rs2.rand(), n1, "copy: random generator seed was not copied") rs.simulate(10) rs2.simulate(10) n2 = rs.rand() self.assertEqual(rs2.rand(), n2, "copy: simulation is not deterministic") rs3 = pb.RootSystem() # rebuild same rs3.readParameters("../modelparameter/rootsystem/" + name + ".xml") rs3.setSeed(seed) rs3.initialize(False) self.assertEqual(rs3.rand(), n1, "copy: random generator seed was not copied") rs3.simulate(10) self.assertEqual(rs3.rand(), n2, "copy: simulation is not deterministic")
def simOnce(name, simtime, lbins, lrange, zbins, zrange, dx, dt): abins = 100 arange = (-math.pi, math.pi) hl, hz, ha = None, None, None # histograms # Simulation rs = pb.RootSystem() path = "../../../modelparameter/rootsystem/" rs.readParameters(path + name + ".xml") for p in rs.getRootRandomParameter(): p.dx = dx rs.getRootRandomParameter( 4 ).theta = 80. / 180. * math.pi # fix insertion angle of the basal roots rs.initialize() N = round(simtime / dt) for i in range(0, N): rs.simulate(dt, True) # Analysis img = np.zeros((2 * lbins, 2 * lbins)) nodes = rs.getNodes() tips = rs.getRootTips() z_ = np.zeros(len(tips)) l_ = np.zeros(len(tips)) alpha_ = np.zeros(len(tips)) c = 0 # counter mx, my = 0, 0 # mean tip position for t in tips: x, y, z = nodes[t].x, nodes[t].y, nodes[t].z # tip top view i = math.floor(((x + lrange[1]) / (2. * lrange[1])) * 2. * float(lbins)) # np.around((x/lrange[1])*lbins+lbins) j = math.floor(((y + lrange[1]) / (2. * lrange[1])) * 2. * float(lbins)) # np.around((y/lrange[1])*lbins+lbins) i = min(i, 2 * lbins - 1) j = min(j, 2 * lbins - 1) i = int(max(i, 0)) j = int(max(j, 0)) img[j, i] += 1. # depth and length distribution z_[c] = z l_[c] = math.sqrt(x * x + y * y) alpha_[c] = math.atan2(x, y) c += 1 hl, bins = np.histogram(l_, bins=lbins, range=lrange) hz, bins = np.histogram(z_, bins=zbins, range=zrange) ha, bins = np.histogram(alpha_, bins=abins, range=arange) mx += nodes[t].x my += nodes[t].y if (len(tips) == 0): print("No tips?") return hl, hz, ha, img, mx, my else: return hl, hz, ha, img, mx / len(tips), my / len(tips)
def test_dynamics(self): """ incremental root system growth like needed for coupling""" name = "Anagallis_femina_Leitner_2010" # "maize_p2" # "Anagallis_femina_Leitner_2010" # "Zea_mays_4_Leitner_2014" rs = pb.RootSystem() rs.readParameters("../modelparameter/rootsystem/" + name + ".xml") rs.initialize(False) simtime = 60 # days dt = 1 N = round(simtime / dt) nodes = np.array((list(map(np.array, rs.getNodes())))) nodeCTs = np.array(rs.getNodeCTs()) seg = np.array([], dtype=np.int64).reshape(0, 2) cts = rs.getSegmentCTs() nonm = 0 for i in range(0, N): rs.simulate(dt, False) # MOVE NODES uni = np.array((list(map(np.array, rs.getUpdatedNodeIndices()))), dtype=np.int64) unodes = np.array((list(map(np.array, rs.getUpdatedNodes())))) ucts = np.array(rs.getUpdatedNodeCTs()) if len(uni) > 0: nodes[uni] = unodes # do the update nodeCTs[uni] = ucts nonm += uni.shape[0] # NEW NODES newnodes = np.array((list(map(np.array, rs.getNewNodes())))) newcts = np.array(rs.getNewNodeCTs()) newsegs = np.array((list(map(np.array, rs.getNewSegments()))), dtype=np.int64) if len(newnodes) != 0: nodes = np.vstack((nodes, newnodes)) nodeCTs = np.append(nodeCTs, newcts) if len(newsegs) != 0: seg = np.vstack((seg, newsegs)) nodes_ = np.array((list(map(np.array, rs.getNodes())))) nodeCTs_ = np.array(rs.getNodeCTs()) seg_ = np.array((list(map(np.array, rs.getSegments()))), dtype=np.int64) self.assertEqual(nodes_.shape, nodes.shape, "incremental growth: node lists are not equal") self.assertEqual(nodeCTs_.shape, nodeCTs.shape, "incremental growth: node lists are not equal") self.assertEqual(seg_.shape, seg.shape, "incremental growth: node lists are not equal") uneq = np.sum(nodes_ != nodes) / 3 self.assertEqual(uneq, 0, "incremental growth: node lists are not equal") uneq = np.sum(nodeCTs_ != nodeCTs) self.assertEqual( uneq, 0, "incremental growth: node creation time lists are not equal") seg = np.sort(seg, axis=0) # per default along the last axis seg_ = np.sort(seg_, axis=0) uneq = np.sum(seg_ != seg) / 2 self.assertEqual(uneq, 0, "incremental growth: segment lists are not equal")
def test_rsml(self): """ checks rsml functionality with Python rsml reader """ name = "Anagallis_femina_Leitner_2010" rs = pb.RootSystem() rs.readParameters("../modelparameter/rootsystem/" + name + ".xml") rs.initialize(False) simtime = 60 rs.simulate(simtime) rs.writeRSML(name + ".rsml") pl, props, funcs = read_rsml(name + ".rsml")
def root_example_rrp2(self): """ an example used in the tests below, a main root with laterals """ self.plant = pb.RootSystem( ) # store organism (not owned by Organ, or OrganRandomParameter) p0 = pb.RootRandomParameter(self.plant) p0.name, p0.subType, p0.la, p0.lb, p0.lmax, p0.ln, p0.lnk, p0.r, p0.dx, p0.dxMin = "taproot", 1, 0.95, 0.8, 10., 1.05, 0.01, 0.8, 0.25, 0.2 p0.successor = [2] p0.successorP = [1.] p1 = pb.RootRandomParameter(self.plant) p1.name, p1.subType, p1.lmax, p1.r, p1.dx = "lateral", 2, 2., 2., 2. self.plant.setOrganRandomParameter( p0) # the organism manages the type parameters and takes ownership self.plant.setOrganRandomParameter(p1) srp = pb.SeedRandomParameter(self.plant) self.plant.setOrganRandomParameter(srp) print("root p0, initial parameters: lmax = ", p0.lmax, ", lb = ", p0.lb, ", la = ", p0.la, ", ln = ", p0.ln) param0 = p0.realize() # set up root by hand (without a root system) print("root p0, realized parameters: lmax = ", sum((sum(param0.ln), param0.lb, param0.la)), ", lb = ", param0.lb, ", la = ", param0.la, ", mean ln = ", np.mean(param0.ln)) if ((param0.lb % p0.dx > 0) and (param0.lb % p0.dx < p0.dxMin * 0.99)): print("lb value does not fit with dx and dxMin") print(param0.lb % p0.dx) if ((param0.la % p0.dx > 0) and (param0.la % p0.dx < p0.dxMin * 0.99)): print("la value does not fit with dx and dxMin") print(param0.la % p0.dx) if (any([(lni % p0.dx > 0 and lni % p0.dx < p0.dxMin * 0.99) for lni in param0.ln])): print("ln value does not fit with dx and dxMin") param0.la, param0.lb = 0, 0 # its important parent has zero length, otherwise creation times are messed up parentroot = pb.Root(1, param0, True, True, 0., 0., pb.Vector3d(0, 0, -1), 0, 0, False, 0) # takes ownership of param0 parentroot.setOrganism(self.plant) parentroot.addNode(pb.Vector3d(0, 0, -1), 0) # there is no nullptr in Python self.parentroot = parentroot # store parent (not owned by child Organ) self.root = pb.Root(self.plant, p0.subType, pb.Vector3d(0, 0, -1), 0, self.parentroot, 0, 0) self.root.setOrganism(self.plant) self.p0 = p0
def test_polylines(self): """checks if the polylines have the right tips and bases """ name = "Brassica_napus_a_Leitner_2010" rs = pb.RootSystem() rs.readParameters("../modelparameter/rootsystem/" + name + ".xml") rs.initialize(False) rs.simulate(7) # days young polylines = rs.getPolylines() # Use polyline representation of the roots bases = np.zeros((len(polylines), 3)) tips = np.zeros((len(polylines), 3)) for i, r in enumerate(polylines): bases[i, :] = [r[0].x, r[0].y, r[0].z] tips[i, :] = [r[-1].x, r[-1].y, r[-1].z] nodes = np.array((list(map(np.array, rs.getNodes())))) # Or, use node indices to find tip or base nodes tipI = rs.getRootTips() baseI = rs.getRootBases() uneq = np.sum(nodes[baseI, :] != bases) + np.sum(nodes[tipI, :] != tips) self.assertEqual(uneq, 0, "polylines: tips or base nodes do not agree")
def initialize_root_systems(N :int, M :int, distN :float, distM :float): """ Initializes M*N root systems @param N number of rows @param M number of columns @param distN distance between rows @param distM distance between columns @return a list of initialized root systems """ allRS = [] for i in range(0, N): for j in range(0, M): rs = pb.RootSystem() rs.readParameters(path + name + ".xml") rs.getRootSystemParameter().seedPos = pb.Vector3d(distN * i, distM * j, -3.) # cm rs.initialize(False) # verbose = False allRS.append(rs) return allRS
def simulate(i): rs = pb.RootSystem() rs.readParameters(path + name + ".xml") set_all_sd(rs, 0.) # set all sd to zero p1 = rs.getRootRandomParameter(1) # tap and basal root type # 1. vary parameter p1.theta = theta0_[i] # 2. simulate rs.initializeLB(1, 1, False) rs.simulate(simtime, False) # 3. calculate target depth = 0. # mean depth rad_dist = 0. # mean raidal distance roots = rs.getPolylines() for r in roots: depth += r[-1].z rad_dist += math.hypot(r[-1].x, r[-1].y) depth /= len(roots) rad_dist /= len(roots) return depth, rad_dist
def rs_example_rtp(self): """ an example used in some of the tests below, 100 basals with laterals """ self.rs = pb.RootSystem() srp = pb.SeedRandomParameter(self.rs) srp.subType = 0 srp.seedPos = pb.Vector3d(0., 0., -3.) srp.maxB = 100 srp.firstB = 10 srp.delayB = 3 self.rs.setRootSystemParameter(srp) p0 = pb.RootRandomParameter(self.rs) p0.name, p0.subType, p0.la, p0.lmax, p0.ln, p0.r, p0.dx = "taproot", 1, 10, 101, 89. / 19., 1, 0.5 p0.lb = 2 p0.successor = [2] p0.successorP = [1.] p1 = pb.RootRandomParameter(self.rs) p1.name, p1.subType, 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(p0) # the organism manages the type parameters self.rs.setOrganRandomParameter(p1)
"""hydrotropism in a thin layer""" import sys sys.path.append("../../..") sys.path.append("../../../src/python_modules") import plantbox as pb import vtk_plot as vp rs = pb.RootSystem() path = "../../../modelparameter/rootsystem/" name = "Anagallis_femina_Leitner_2010" rs.readParameters(path + name + ".xml") # Manually set tropism to hydrotropism for the first ten root types sigma = [0.4, 1., 1., 1., 1.] * 2 for p in rs.getRootRandomParameter(): p.dx = 0.25 # adjust resolution p.tropismT = pb.TropismType.hydro p.tropismN = 2 # strength of tropism p.tropismS = sigma[p.subType - 1] # Static soil property in a thin layer maxS = 0.7 # maximal minS = 0.1 # minimal slope = 5 # linear gradient between min and max (cm) box = pb.SDF_PlantBox(30, 30, 2) # cm layer = pb.SDF_RotateTranslate(box, pb.Vector3d(0, 0, -16)) soil_prop = pb.SoilLookUpSDF(layer, maxS, minS, slope) # Set the soil properties before calling initialize rs.setSoil(soil_prop)
import sys sys.path.append("../../..") import time import numpy as np import matplotlib.pyplot as plt from matplotlib import ticker import matplotlib.colors as colors from pyevtk.hl import gridToVTK import plantbox as rb simtime_ = [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] for st in simtime_: # # Root system # rs = rb.RootSystem() path = "../../../modelparameter/rootsystem/" name = "test_root" rs.readParameters(path + name + ".xml") #set geometry width = 4 # cm depth = 15 soilcore = rb.SDF_PlantContainer(width, width, depth, True) rs.setGeometry(soilcore) rs.setSeed(0) rs.initialize() for ii in range(0, st):
def simOnce(name, simtime, lbins, lrange, zbins, zrange, dx, dt): abins = 100 arange = (-math.pi, math.pi) # Simulation rs = pb.RootSystem() rs.openFile(name, "modelparameter/") for p in rs.getRootRandomParameter(): p.dx = dx rs.initialize() rs.getRootRandomParameter( 4 ).theta = 80. / 180. * math.pi # fix insertion angle of the basal roots rs.initialize() N = round(simtime / dt) for i in range(0, N): rs.simulate(dt, True) # analysis img = np.zeros((2 * lbins, 2 * lbins)) nodes = rs.getNodes() tips = rs.getRootTips() notips = len(tips) z_ = np.zeros(notips) l_ = np.zeros(notips) alpha_ = np.zeros(notips) c = 0 mx = 0 my = 0 for t in tips: x = nodes[t].x y = nodes[t].y z = nodes[t].z # tip top view i = math.floor(((x + lrange[1]) / (2. * lrange[1])) * 2. * float(lbins)) # np.around((x/lrange[1])*lbins+lbins) j = math.floor(((y + lrange[1]) / (2. * lrange[1])) * 2. * float(lbins)) # np.around((y/lrange[1])*lbins+lbins) i = min(i, 2 * lbins - 1) j = min(j, 2 * lbins - 1) i = int(max(i, 0)) j = int(max(j, 0)) img[j, i] += 1. # depth, and length distribution z_[c] = z l_[c] = math.sqrt(x * x + y * y) alpha_[c] = math.atan2(x, y) c += 1 hl, bins = np.histogram(l_, bins=lbins, range=lrange) hz, bins = np.histogram(z_, bins=zbins, range=zrange) ha, bins = np.histogram(alpha_, bins=abins, range=arange) mx += nodes[t].x my += nodes[t].y return hl, hz, ha, img, mx / len(tips), my / len(tips)
# 1) Opens plant and root parameters from a file # 2) Simulates root growth # 3) Outputs a VTP (for vizualisation in ParaView) # # Computes analytical solution of moving point/line sources based on Carslaw and Jaeger # import sys sys.path.append("../..") import numpy as np import matplotlib.pyplot as plt from scipy import integrate import plantbox as rb rootsystem = rb.RootSystem() name = "anagallis_straight" # # Open plant and root parameter from a file # rootsystem.openFile(name) # rootsystem.writeParameters() # not exposed to python yet # # Initialize # rootsystem.initialize() # # Simulate
def err (fitparams): #parameters to be optimized lmaxp=fitparams[0] tropismNp=fitparams[1] tropismSp=fitparams[2] rp=fitparams[3] simtime = 120 M = 4 # number of plants in rows N = 2 # number of rows distp = 3 # distance between the root systems along row[cm] distr =12 # distance between the rows[cm] interrow=N*distr # inter-row spacing row=M*distp # row spacing r, depth, layers = 5, 100., 11 # Soil core analysis layerVolume = depth / layers * r * r * np.pi z_ = np.linspace(0, -1 * depth, layers) times = [120, 60, 30, 10] soilcolumn = pb.SDF_PlantContainer(r, r, depth, False) # in the center of the root soilcolumn1 = pb.SDF_RotateTranslate(soilcolumn, 0, 0, pb.Vector3d(-6, 0, 0)) soilcolumn2 = pb.SDF_RotateTranslate(soilcolumn, 0, 0, pb.Vector3d(6, 0, 0)) soilcolumns=[soilcolumn1,soilcolumn, soilcolumn2] with open('rld.pkl','rb') as f: measured_RLD = pkl.load(f) real_RLD=np.reshape(measured_RLD,(measured_RLD.shape[0]*measured_RLD.shape[1]*measured_RLD.shape[2],1)) rld=np.zeros([measured_RLD.shape[0],measured_RLD.shape[1],measured_RLD.shape[2]]) # rld=np.zeros([len(soilcolumns),len(times),layers]) path = "../../modelparameter/rootsystem/" name = "wheat" # fig, axes = plt.subplots(nrows = 1, ncols = len(soilcolumns), figsize = (16, 8)) # Make a root length distribution along the soil cores for k in range(len(soilcolumns)): # Initializes N*M root systems allRS = [] for i in range(0, N): for j in range(0, M): rs = pb.RootSystem() rs.readParameters(path + name + ".xml") p1 = rs.getRootRandomParameter(1) # tap and basal root type p1.lmax = lmaxp p1.tropismN=tropismNp p1.tropismS=tropismSp p1.r=rp for p in rs.getRootRandomParameter(): p.lns = 0 p.rs = 0 p.lmaxs = 0 p.thetas = 0 p.las = 0 p.lbs=0 rs.setSeed(1) rs.getRootSystemParameter().seedPos = pb.Vector3d(distr * i, distp * j, -3.) # cm rs.initialize() allRS.append(rs) # Simulate for rs in allRS: rs.simulate(simtime) # Export results as single vtp files (as polylines) ana = pb.SegmentAnalyser() # see example 3b for z, rs in enumerate(allRS): # vtpname = "results/rlds/plant_" + str(z) + ".vtp" # rs.write(vtpname) ana.addSegments(rs) # collect all # Write all into single file (segments) # ana.write("results/rlds/all_plants.vtp") ana.mapPeriodic(interrow, row) # ana.write("results/rlds/plant_periodic.vtp") rl_ = [] ana.crop(soilcolumns[k]) ana.pack() # ana.write("results/rlds/core"+str(k+1)+".vtp") # axes[k].set_title('Soil core'+' ' +str(k+1)) for j in range(len(times)): ana.filter("creationTime", 0, times[j]) rl_.append(ana.distribution("length", 0., -depth, layers, True)) # axes[k].plot(np.array(rl_[-1]) / layerVolume, z_) # axes[k].legend(["120 days", "60 days", "30 days", "15 days"]) rld[k]=np.array(rl_)/layerVolume # for a in axes: # a.set_xlabel('RLD $(cm/cm^3)$') # a.set_ylabel('Depth $(cm)$') # a.set_xlim(0,np.max(rld)) # fig.subplots_adjust() # plt.savefig("results/rlds/rld_plot.png") # plt.show() RLD=np.reshape(rld,(rld.shape[0]*rld.shape[1]*rld.shape[2],1)) # print(rld) # with open('results/rlds/rld.pkl','wb') as f: # pkl.dump(rld, f) # sys.exit() err = math.sqrt(sum(((np.subtract(RLD,real_RLD)))**2)/len(RLD)) #NRMSE return err