def stem_example_rtp(self): """ an example used in the tests below, a main stem with laterals """ self.plant = pb.Organism( ) # store organism (not owned by Organ, or OrganRandomParameter) p0 = pb.StemRandomParameter(self.plant) p0.name, p0.subType, p0.la, p0.lb, p0.lmax, p0.ln, p0.r, p0.dx = "main", 1, 1, 10, 100, ( 89. / 19.), 1, 0.5 p0.successor = [3] p0.successorP = [1.] p1 = pb.StemRandomParameter(self.plant) p1.name, p1.subType, p1.la, p1.ln, p1.r, p1.dx = "lateral", 3, 25, 0, 2, 0.1 self.p0, self.p1 = p0, p1 # needed at later point self.plant.setOrganRandomParameter( p0) # the organism manages the type parameters and takes ownership self.plant.setOrganRandomParameter(p1) # TODO (first node is not set, if seed is used) self.plant.setOrganRandomParameter(pb.SeedRandomParameter(self.plant)) self.seed = pb.Seed( self.plant) # store parent (not owned by child Organ) # param0 = p0.realize() # set up stem by hand (without a stem system) param0.la, param0.lb = 0, 0 # its important parent has zero length, otherwise creation times are messed up parentstem = pb.Stem(1, param0, True, True, 0., 0., pb.Vector3d(0, 0, -1), 0, 0, False, 0) # takes ownership of param0 parentstem.setOrganism(self.plant) parentstem.addNode(pb.Vector3d(0, 0, -3), 0) # there is no nullptr in Python self.parentstem = parentstem # store parent (not owned by child Organ) # self.stem = pb.Stem(self.plant, p0.subType, pb.Vector3d(0, 0, -1), 0, self.parentstem, 0, 0) self.stem.setOrganism(self.plant)
def test_leaf(self): """ leaf without lateral leafs """ ons = pb.Matrix3d(pb.Vector3d(0., 0., 1.), pb.Vector3d(0., 1., 0.), pb.Vector3d(1., 0., 0.)) plant = pb.Plant() # store organism (not owned by Organ, or OrganRandomParameter) p0 = pb.LeafRandomParameter(plant) p0.name, p0.subType, p0.la, p0.lb, p0.lmax, p0.ln, p0.r, p0.dx = "leaf", 1, 3.5, 1., 7.5, 3, 1, 0.1 phi = np.array([-90, -45, 0., 45, 90]) / 180. * np.pi l = np.array([3, 2.2, 1.7, 2, 3.5]) N = 100 # N is rather high for testing p0.createLeafRadialGeometry(phi, l, N) # y = np.array([-3, -3 * 0.7, 0., 3.5 * 0.7, 3.5]) # l = np.array([0., 2.2 * 0.7, 1.7, 1.8 * 0.7, 0.]) # N = 105 # N is rather high for testing # p0.createLeafGeometry(y, l, N) plant.setOrganRandomParameter(p0) # the organism manages the type parameters and takes ownership plant.setOrganRandomParameter(pb.SeedRandomParameter(plant)) # because we cannot pass a nullptr to pb.Leaf(...) L48 param0 = p0.realize() # set up leaf by hand (without a leaf syleaf) param0.la, param0.lb = 0, 0 # its important parent has zero length, otherwise creation times are messed up parentleaf = pb.Leaf(1, param0, True, True, 0., 0., ons, 0, False, 0) # takes ownership of param0 parentleaf.setOrganism(plant) parentleaf.addNode(pb.Vector3d(0, 0, -3), 0) # there is no nullptr in Python leaf = pb.Leaf(plant, p0.subType, ons, 0, parentleaf , 0) leaf.setOrganism(plant) leaf.simulate(7) vp.plot_leaf(leaf)
def test_constructors(self): """ tests two kinds of constructors and copy""" self.root_example_rrp() # 1. constructor from scratch param = self.p0.realize() root = pb.Root(1, param, True, True, 0., 0., pb.Vector3d(0, 0, -1), 0, 0, False, 0) root.setOrganism(self.plant) root.addNode(pb.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 = pb.Root(self.plant, self.p1.subType, pb.Vector3d(0, 0, -1), 0, root, 0, 0) root.addChild(root2) # 3. deep copy (with a factory function) plant2 = pb.Organism() root3 = root.copy(plant2) self.assertEqual( str(root), str(root3), "deep copy: the root string representations shold be equal") self.assertIsNot(root.getParam(), root3.getParam(), "deep copy: roots have same specific parameter set" ) # type OrganSpecificParameter self.assertEqual(str(root.param()), str(root3.param()), "deep copy: roots have different parameter values" ) # type RootSpecificParameter
def root_example_rrp(self): """ an example used in the tests below, a main root with laterals """ self.plant = pb.Organism( ) # 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.r, p0.dx = "taproot", 1, 10., 1., 100., 1., 1.5, 0.5 p0.successor = [2] p0.successorP = [1.] p1 = pb.RootRandomParameter(self.plant) p1.name, p1.subType, p1.lmax, p1.r, p1.dx = "lateral", 2, 25., 2., 0.1 self.p0, self.p1 = p0, p1 # needed at later point 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) param0 = p0.realize() # set up root by hand (without a root system) 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, -3), 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)
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 = pb.Organ(self.human1, self.thumb, 0, 0, 4, self.ons, 0) # add a ring to the thumb self.thumb.addChild(ring) ring.addNode(pb.Vector3d(0, -1, 1.6), self.thumb.getNodeId(1), 4) ring.addNode(pb.Vector3d(0, -1, 1.6), 4) organs = self.hand.getOrgans()
def hand_example(self): """ an example used in the tests below, a hand with two fingers """ self.human1 = pb.Organism() # same example as in test_constructor ... otp = pb.OrganRandomParameter(self.human1) self.human1.setOrganRandomParameter(otp) op = otp.realize() self.hand = pb.Organ(self.human1.getOrganIndex(), op, True, True, 0, 15., pb.Vector3d(0., 0., 1.), 0., 0, False, 0) self.hand.setOrganism(self.human1) self.thumb = pb.Organ(self.human1, self.hand, 0, 0, 4, pb.Vector3d(0., 0., 1.), 0., 0) # delayedfor 4 days self.little_finger = pb.Organ(self.human1, self.hand, 0, 0, 3, pb.Vector3d(0., 0., 1.), 0., 0) # delayed for 3 days self.hand.addChild(self.thumb)
def read_rsml(file_name:str, verbose=True): """ reads an RSML file and converts to MappedSegments with units [cm] @file_name the file name of the rsml, including file extension (e.g. "test.rsml" ) @return a CPlantBox MappedSegments object """ polylines, props, funcs = rsml.read_rsml(file_name) bn = 0 # count base roots for i, _ in enumerate(polylines): if props["parent-poly"][i] < 0: bn += 1 if bn > 1: polylines, props, funcs = rsml.artificial_shoot(polylines, props, funcs) if verbose: print("XylemFluxPython.read_rsml: added an artificial shoot") nodes, segs = rsml.get_segments(polylines, props) radii, seg_ct, types = rsml.get_parameter(polylines, funcs, props) if verbose: print("XylemFluxPython.read_rsml: read rsml with", len(nodes), "nodes and", len(segs), "segments") nodes = np.array(nodes) # for slicing in the plots nodes2 = [] # Conversions... for n in nodes: nodes2.append(pb.Vector3d(n[0] , n[1] , n[2])) segs2 = [] nodeCTs = np.zeros((len(nodes), 1)) # we need node creation times for i, s in enumerate(segs): nodeCTs[s[1]] = seg_ct[i] segs2.append(pb.Vector2i(int(s[0]), int(s[1]))) radii = np.array(radii) types = np.array(types, dtype=np.int64) - 1 # index must start with 0 if verbose: print(" nodeCTs [{:g}, {:g}] days".format(np.min(nodeCTs), np.max(nodeCTs))) print(" raddii [{:g}, {:g}] cm".format(np.min(radii), np.max(radii))) print(" subTypes [{:g}, {:g}] ".format(np.min(types), np.max(types))) return pb.MappedSegments(nodes2, nodeCTs, segs2, radii, types) # root system grid
def convert_to_xylem_flux_(self): """ converts the polylines to a SegmentAnalyser and a MappedSegments object, and stores max_ct uses: properties["parent-poly"], properties["parent-nodes"] radii, cts, types """ nodes, segs = rsml_reader.get_segments(self.polylines, self.properties) # fetch nodes and segments segRadii = np.zeros((segs.shape[0], 1)) # convert to paramter per segment segCTs = np.zeros((segs.shape[0], 1)) subTypes = np.zeros((segs.shape[0], 1)) for i, s in enumerate(segs): segRadii[i] = self.radii[s[1]] # seg to node index segCTs[i] = self.cts[s[1]] subTypes[i] = self.types[s[1]] if np.isnan(subTypes[0]): subTypes = np.ones((len(segs),), dtype=np.int64) segs_ = [pb.Vector2i(s[0], s[1]) for s in segs] # convert to CPlantBox types nodes_ = [pb.Vector3d(n[0], n[1], n[2]) for n in nodes] self.analyser = pb.SegmentAnalyser(nodes_, segs_, segCTs, segRadii) self.analyser.addData("subType", subTypes) ms = pb.MappedSegments(self.analyser.nodes, np.array(self.cts), segs_, np.array(segRadii), np.array(subTypes)) self.xylem_flux = xylem_flux.XylemFluxPython(ms) self.base_nodes = self.get_base_node_indices_() self.xylem_flux.neumann_ind = self.base_nodes # needed for suf self.xylem_flux.dirichlet_ind = self.base_nodes # needed for krs self.base_segs = self.xylem_flux.find_base_segments()
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(pb.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")
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 read_rsml(file_name: str): """ Reads an RSML file and converts to MappedSegments with units [cm] """ polylines, props, funcs = rsml.read_rsml(file_name) assert len(polylines) == len( props['parent-poly'] ), "XylemFluxPython.read_rsml: wrong number of parent-poly tags" if not 'parent-node' in props: # reconstruct... print("parse_rsml: no parent-node tag found, reconstructing...") props['parent-node'] = [] for i in range(0, len(polylines)): if props['parent-poly'][i] >= 0: pni = XylemFluxPython.connect( np.array(polylines[i][0]), polylines[props['parent-poly'][i]]) else: pni = -1 # print("parent", props['parent-poly'][i], "pni", pni) props['parent-node'].append(pni) assert len(polylines) == len( props['parent-node'] ), "XylemFluxPython.read_rsml: wrong number of parent-node tags" nodes, segs = rsml.get_segments(polylines, props) print("Read rsml:", len(nodes), "segs", len(segs), "segs") nodes2 = [pb.Vector3d(n[0], n[1], n[2]) for n in nodes] # convert to plantbox types segs2 = [pb.Vector2i(int(s[0]), int(s[1])) for s in segs] radii_, nodeCTs_, types_ = [], [], [] for i, p in enumerate(polylines): for j in range(0, len(p)): if 'diameter' in funcs: radii_.append(funcs['diameter'][i][j] / 2) else: print("XylemFluxPython.read_rsml: no diameter tag found") radii_.append(0.) if 'emergence_time' in funcs: nodeCTs_.append(funcs['emergence_time'][i][j]) else: print( "XylemFluxPython.read_rsml: no emergence_time tag found" ) nodeCTs_.append(0.) if 'type' in funcs: types_.append(funcs["type"][i][j]) elif 'type' in props: types_.append(props["type"][i]) else: print("XylemFluxPython.read_rsml: no type tag found") types_.append(0) nodeCTs = np.array(nodeCTs_) # creation times per node radii = np.zeros((len(segs), 1)) # radii per segment types = np.ones((len(segs), 1), dtype=np.int64) # types per semgent for i, s in enumerate(segs): radii[i] = radii_[s[1]] types[i] = types_[s[1]] - 1 # index must start with 0 return pb.MappedSegments(nodes2, nodeCTs, segs2, radii, types) # root system grid
def add_nodes(self): """ used in the tests below, adds nodes to the hand example """ self.hand.addNode(pb.Vector3d(0, 0, 0), 0) self.hand.addNode(pb.Vector3d(0, 0, 1.5), 0) self.hand.addNode(pb.Vector3d(0, -1, 1.6), 0) # thumb self.hand.addNode(pb.Vector3d(0, 1, 1.6), 0) # little finger thumb = self.hand.getNodeId(2) lf = self.hand.getNodeId(3) self.thumb.addNode(pb.Vector3d(0, -1, 1.6), thumb, 4) self.thumb.addNode(pb.Vector3d(0, -2, 2.5), 4) self.little_finger.addNode(pb.Vector3d(0, 1, 1.6), lf, 3) self.little_finger.addNode(pb.Vector3d(0, 1.7, 2.5), 3)
def test_constructors(self): """ tests two kinds of constructors and copy""" self.stem_example_rtp() # 1. constructor from scratch param = self.p0.realize() stem = pb.Stem(1, param, True, True, 0., 0., pb.Vector3d(0, 0, -1), 0, 0, False, 0) stem.setOrganism(self.plant) stem.addNode(pb.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) stem2 = pb.Stem(self.plant, self.p1.subType, pb.Vector3d(0, 0, -1), 0, stem, 0, 0) stem.addChild(stem2) # 3. deep copy (with a factory function) plant2 = pb.Organism() stem3 = stem.copy(plant2) self.assertEqual(str(stem), str(stem3), "deep copy: the organs shold be equal") self.assertIsNot(stem.getParam(), stem3.getParam(), "deep copy: organs have same parameter set")
def test_constructors(self): """ tests three different kinds of constructors """ human1 = pb.Organism() otp = pb.OrganRandomParameter(human1) human1.setOrganRandomParameter(otp) op = otp.realize() # 1. constructor from scratch hand = pb.Organ(human1.getOrganIndex(), op, True, True, 0., 15., pb.Vector3d(0., 0., 1.), 0., 0, False, 0) hand.setOrganism(human1) # 2. used in simulation (must have parent, since there is no nullptr in Pyhton) thumb = pb.Organ(human1, hand, 0, 0, 4, pb.Vector3d(0., 0., 1.), 0., 0) little_finger = pb.Organ(human1, hand, 0, 0, 3, pb.Vector3d(0., 0., 1.), 0., 0) hand.addChild(thumb) hand.addChild(little_finger) # 3. deep copy (with a factory function) human2 = pb.Organism() human2.setOrganRandomParameter(otp.copy(human2)) hand2 = hand.copy(human2) self.assertEqual(str(hand), str(hand2), "deep copy: the organs should be equal") self.assertIsNot(hand.getParam(), hand2.getParam(), "deep copy: organs have same parameter set") self.assertEqual(str(hand.getParam()), str(hand2.getParam()), "deep copy: the different parameter set values") self.assertEqual(str(hand.getOrganRandomParameter()), str(hand2.getOrganRandomParameter()), "deep copy: the different random parameter set values")
def stem_example_rtp(self, phytomereGrowth = "sequential"): """ an example used in the tests below, a main stem with laterals """ self.plant = pb.Plant() # store organism (not owned by Organ, or OrganRandomParameter) p0 = pb.StemRandomParameter(self.plant) p0.name, p0.subType, p0.la, p0.lb, p0.lmax, p0.ln, p0.r, p0.dx, p0.dxMin = "main", 1, 10., 10., 100., 1., 1.5, 1, 0.5 p0.delayLat = 1. p0.delayNG = 2. p0.successor = [5] p0.successorP = [1.] if phytomereGrowth == "sequential": p0.nodalGrowth = 0 if phytomereGrowth == "equal": p0.nodalGrowth = 1 p1 = pb.StemRandomParameter(self.plant) p1.name, p1.subType, p1.lmax, p1.r, p1.dx, p1.dxMin = "lateral", 5, 5., 2., 1, 0.5 self.p0, self.p1 = p0, p1 # needed at later point 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) # creates seed organ (otherwise throws error in plant::simulate()) # test == True => no need to give root parameter self.plant.initialize(verbose = False, test = True) param0 = p0.realize() # set up stem by hand (without a stem system) param0.la, param0.lb = 0, 0 # its important parent has zero length, otherwise creation times are messed up self.ons = pb.Matrix3d(pb.Vector3d(0., 0., 1.), pb.Vector3d(0., 1., 0.), pb.Vector3d(1., 0., 0.)) parentstem = pb.Stem(1, param0, True, True, 0., 0., self.ons, 0, False, 0) # takes ownership of param0 parentstem.setOrganism(self.plant) parentstem.addNode(pb.Vector3d(0, 0, -3), 0) # there is no nullptr in Python self.parentstem = parentstem # store parent (not owned by child Organ) self.stem = pb.Stem(self.plant, p0.subType, self.ons, 0, self.parentstem , 0) self.stem.setOrganism(self.plant)
def soil_cores(x :list, y :list, r :float, h :float): """ A lsit of soil core geometries with a fixed location in the field @param x x coordinates of the soil cores (cm) @param y y coordinates of the soil cores (cm) @param r radius of the soil core (cm) @param h height of the soil core (cm) """ assert len(x) == len(y), "coordinate length must be equal" core = pb.SDF_PlantContainer(r, r, h, False) cores = [] for i in range(0, len(x)): cores.append(pb.SDF_RotateTranslate(core, 0., pb.SDF_Axis.xaxis, pb.Vector3d(x[i], y[i], 0.))) # just translate return cores;
def get_srp(self): self.plant = pb.Organism() # need to store this srp = pb.SeedRandomParameter(self.plant) srp.seedPos = pb.Vector3d(0, 0, -2) srp.firstB = 3 # basal srp.delayB = 5 srp.maxB = 7 srp.nC = 7 srp.firstSB = 30 # shoot borne srp.delaySB = 10 srp.delayRC = 70 srp.nz = 0.7 srp.maxTil = 3 # stem srp.simtime = 10 self.plant.setOrganRandomParameter(srp) return srp
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 read_rsml(file_name: str, verbose=True): """ reads an RSML file and converts to MappedSegments with units [cm] @file_name the file name of the rsml, including file extension (e.g. "test.rsml" ) @return a CPlantBox MappedSegments object """ polylines, props, funcs, _ = rsml.read_rsml(file_name) bn = 0 # count base roots for i, _ in enumerate(polylines): if props["parent-poly"][i] < 0: bn += 1 if bn > 1: rsml.artificial_shoot(polylines, props, funcs) if verbose: print("XylemFluxPython.read_rsml: added an artificial shoot") nodes, segs = rsml.get_segments(polylines, props) if verbose: print("XylemFluxPython.read_rsml: read rsml with", len(nodes), "nodes and", len(segs), "segments") nodes2 = [pb.Vector3d(n[0], n[1], n[2]) for n in nodes] # Conversions to PlantBox types segs2 = [pb.Vector2i(int(s[0]), int(s[1])) for s in segs] radii, cts, types, tag_names = rsml.get_parameter( polylines, funcs, props) segRadii = np.zeros( (segs.shape[0], 1)) # convert to paramter per segment segTypes = np.zeros((segs.shape[0], 1)) for i, s in enumerate(segs): segRadii[i] = radii[s[1]] # seg to node index segTypes[i] = types[s[1]] if verbose: print(" cts [{:g}, {:g}] days".format( np.min(cts), np.max(cts))) print(" raddii [{:g}, {:g}] cm".format( np.min(radii), np.max(radii))) print(" subTypes [{:g}, {:g}] ".format( np.min(types), np.max(types))) print() return pb.MappedSegments(nodes2, cts, segs2, segRadii, segTypes) # root system grid
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)
def read_rsml(file_name: str): """ Reads an RSML file and converts to MappedSegments with units [cm] """ polylines, props, funcs = rsml.read_rsml(file_name) nodes, segs = rsml.get_segments(polylines, props) radii, seg_ct, types = rsml.get_parameter(polylines, funcs, props) print("Read rsml:", len(nodes), "nodes", len(radii), "radii") nodes = np.array(nodes) # for slicing in the plots nodes2 = [] # Conversions... for n in nodes: nodes2.append(pb.Vector3d(n[0], n[1], n[2])) segs2 = [] nodeCTs = np.zeros((len(nodes), 1)) # we need node creation times for i, s in enumerate(segs): nodeCTs[s[1]] = seg_ct[i] segs2.append(pb.Vector2i(int(s[0]), int(s[1]))) radii = np.array(radii) types = np.array(types, dtype=np.int64) - 1 # index must start with 0 return pb.MappedSegments(nodes2, nodeCTs, segs2, radii, types) # root system grid
TairK = TairC + 273.15 es = 0.61078 * math.exp(17.27 * TairC / (TairC + 237.3)) ea = es * RH VPD = es - ea # root system pl = pb.MappedPlant() #pb.MappedRootSystem() #pb.MappedPlant() path = "../../../modelparameter/plant/" #"../../../modelparameter/rootsystem/" name = "manyleaves" #"Anagallis_femina_Leitner_2010" # Zea_mays_1_Leitner_2010 pl.readParameters(path + name + ".xml") """ soil """ min_ = np.array([-5, -5, -15]) max_ = np.array([9, 4, 0]) res_ = np.array([5, 5, 5]) pl.setRectangularGrid(pb.Vector3d(min_), pb.Vector3d(max_), pb.Vector3d(res_), True) # cut and map segments pl.initialize() pl.simulate(simtime, False) #rs.simulate(simtime, False) #test to see if works in case of several simulate r = Leuning(pl) nodes = r.get_nodes() tiproots, tipstem, tipleaf = r.get_organ_nodes_tips( ) #end node of end segment of each organ node_tips = np.concatenate((tiproots, tipstem, tipleaf)) tiproots, tipstem, tipleaf = r.get_organ_segments_tips( ) #end segment of each organ seg_tips = np.concatenate((tiproots, tipstem, tipleaf))
rs = pb.MappedRootSystem() rs.readParameters(path + name + ".xml") if not periodic: sdf = pb.SDF_PlantBox(0.99 * (max_b[0] - min_b[0]), 0.99 * (max_b[1] - min_b[1]), max_b[2] - min_b[2]) else: sdf = pb.SDF_PlantBox(np.Inf, np.Inf, max_b[2] - min_b[2]) rs.setGeometry(sdf) rs.initialize() rs.simulate(rs_age, False) r = XylemFluxPython(rs) init_conductivities(r, age_dependent) """ Coupling (map indices) """ picker = lambda x, y, z: s.pick([x, y, z]) r.rs.setSoilGrid(picker) # maps segments r.rs.setRectangularGrid(pb.Vector3d(min_b), pb.Vector3d(max_b), pb.Vector3d(cell_number), True) r.test() # sanity checks nodes = r.get_nodes() cci = picker(nodes[0, 0], nodes[0, 1], nodes[0, 2]) # collar cell index """ Numerical solution """ start_time = timeit.default_timer() x_, y_ = [], [] sx = s.getSolutionHead() # inital condition, solverbase.py N = round(sim_time / dt) t = 0. for i in range(0, N): rx = r.solve(rs_age + t, -trans * sinusoidal(t), sx[cci], sx, True, wilting_point) # xylem_flux.py
"""scales insertion angle""" import sys sys.path.append("../../..") import numpy as np 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") # box with a left and a right compartment for analysis sideBox = pb.SDF_PlantBox(10, 20, 50) left = pb.SDF_RotateTranslate(sideBox, pb.Vector3d(-4.99, 0, 0)) right = pb.SDF_RotateTranslate(sideBox, pb.Vector3d(4.99, 0, 0)) leftright = pb.SDF_Union(left, right) rs.setGeometry(leftright) # left compartment has a minimum of 0.01, 1 elsewhere maxS = 1. # maximal minS = 0.1 # minimal slope = 1. # [cm] linear gradient between min and max leftC = pb.SDF_Complement(left) soilprop = pb.SoilLookUpSDF(leftC, maxS, minS, slope) # Manually set scaling function and tropism parameters sigma = [0.4, 1., 1., 1., 1.] * 2 for p in rs.getRootRandomParameter(): if p.subType > 2: p.dx = 0.25 # adjust resolution
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) # 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 the soil properties (soil_prop) rs.simulate(dt)
""" nodes and segments from measurements """ import sys sys.path.append("../../..") import plantbox as pb # Data from any source, as Python types nodes = [ [0, 1, 0], [0, 1, -1], [0, 1, -2], [0, 1, -3], ] segs = [[0, 1], [1, 2], [2, 3]] cts = [0., 0., 0.] radii = [0.1, 0.1, 0.1] # convert from Python to C++ binding types nodes = [pb.Vector3d(n[0], n[1], n[2]) for n in nodes] segs = [pb.Vector2i(s[0], s[1]) for s in segs] # create the SegmentAnalyser without underlying RootSystem ana = pb.SegmentAnalyser(nodes, segs, cts, radii) print("length", ana.getSummed("length")) ana.write("results/example_3d.vtp", ["creationTime", "radius"])
def vector_3d(a): return pb.Vector3d(a[0], a[1], a[2])
path = "../../modelparameter/rootsystem/" name = "Glycine_max_Moraes2020_opt2" simtime = 154 N = 17 # number of columns M = 3 # number of rows dist = 38 # inter-row distance [cm] distp = 6 # inter-plant distance within the rows [cm] # 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") rs.getRootSystemParameter().seedPos = pb.Vector3d( distp * i, dist * j, -3.) # cm rs.initialize(False) # verbose = False allRS.append(rs) # Simulate rs.setSeed(2) for rs in allRS: rs.simulate(simtime, False) # verbose = False # Export results as single vtp files (as polylines) ana = pb.SegmentAnalyser() # see example 3b for i, rs in enumerate(allRS): vtpname = "results/" + name + "/" + str(i) + ".vtp" rs.write(vtpname) ana.addSegments(rs) # collect all
p2.a, p2.a_s = 0.0501, 0.0069 # [cm] radius TODO p2.theta = (180. - 98.45) / 180. * np.pi # [rad] # p2.lmax, p2.lmaxs = 3.339, 0.2 * 3.339 # fit k and r # p2.r, p2.rs = 3.745, 0.1 * 3.745 # fit k and r p2.lmax, p2.lmaxs = 5, 0.5 # fit r p2.r, p2.rs = 0.483, 0.1 * 0.483 # fit r p2.r, p2.rs = 0.823, 0.1 * 0.823 # fit r (last measurement removed) # by visual comparison p2.tropismT, p2.tropismN, p2.tropismS = pb.TropismType.gravi, 1, 0.2 rs.setOrganRandomParameter(p0) rs.setOrganRandomParameter(p1) rs.setOrganRandomParameter(p2) """ seed """ srp = pb.SeedRandomParameter(rs) # with default values srp.seedPos = pb.Vector3d(0., 0., -0.6) # [cm] seed position TODO ? srp.maxB = 0 # [-] number of basal roots (neglecting basal roots and shoot borne) rs.setRootSystemParameter(srp) """ container geometry """ length = 7.425 # cm height = 14.31 # cm rhizotron = pb.SDF_PlantBox(length, length, height) rs.setGeometry(rhizotron) # soilcore, or rhizotron # elongation impedance at boundaries layer = 0.5 # cm smaller_rhizotron = pb.SDF_PlantBox(length - 2 * layer, length - 2 * layer, height - layer) scale_elongation = Boundary_Elongation_Impedance( smaller_rhizotron, 0.1) # 1 instead of 0.1 would disable the impedance for p in rs.getRootRandomParameter():
from cmath import pi sys.path.append("../..") import plantbox as pb import numpy as np import matplotlib.pyplot as plt path = "../../modelparameter/rootsystem/" name = "Glycine_max_Moraes2020_opt2" rs = pb.RootSystem() rs.readParameters(path + name + ".xml") # Create and set geometry rs.setMinDx(1.e-3) x0 = pb.Vector3d(0., 0., -1.) nx = pb.Vector3d(1., 0., -1.) ny = pb.Vector3d(0., 1., -1.) soil_layer = pb.SDF_HalfPlane(x0, nx, ny) # there was bug, with updated CPlantBox rs.setGeometry(soil_layer) rs.setSeed(2) rs.initialize() simtime = 154. # days dt = 1. N = round(simtime / dt) # steps fig, ax1 = plt.subplots() # Plot some length over time