180 likes | 290 Views
Struktur-Funktions-Modelle von Pflanzen - Sommersemester 2010 - Winfried Kurth Universität Göttingen, Lehrstuhl Computergrafik und Ökologische Informatik 12. Vorlesung: 8. 7. 2010. letztes Mal: Analyse des Fichtenmodells Fortsetzung: Vorstufen des Pappelmodells
E N D
Struktur-Funktions-Modelle von Pflanzen - Sommersemester 2010 - Winfried Kurth Universität Göttingen, Lehrstuhl Computergrafik und Ökologische Informatik 12. Vorlesung: 8. 7. 2010
letztes Mal: • Analyse des Fichtenmodells • Fortsetzung: Vorstufen des Pappelmodells • Beschreibung des Pappelmodells • Aufgabenstellung der Hausarbeit • Bewertungskriterien
als nächstes: • noch ein neuer Regeltyp: Instanzierungsregeln • dtd-Daten zu den ausgemessenen Pappeln • Beantwortung von Fragen zum Pappelmodell • und zur Hausarbeit
Ein weiterer Regeltyp in XL: • Instanzierungsregeln • Zweck: Ersetzung einzelner Module durch kompliziertere Strukturen, nur für die Darstellung • (wie bei Interpretationsregeln) • aber: es wird weniger abgespeichert (Einsparen von • Speicherplatz) • anders als bei Interpretationsregeln dürfen keine Turtle- • Befehle mit Wirkung auf andere Knoten verwendet werden • weitere Möglichkeit: „Replikator-Knoten“ zum Kopieren und Neuplatzieren von ganzen Strukturen
Instanzierungsregeln: Syntax kein neuer Regelpfeil Angabe der Instanzierungsregel direkt in der Moduldeklaration module A ==> B C D; ersetzt (instanziert) überall A durch B C D Beispiel sm09_b43.rgg
const int multiply = EDGE_0; /* selbstdefinierter Kantentyp */ module Tree ==> F(20, 1) [ M(-8) RU(45) F(6, 0.8) Sphere(1) ] [ M(-5) RU(-45) F(4, 0.6) Sphere(1) ] Sphere(2); module Replicator ==> [ getFirst(multiply) ] Translate(10, 0, 0) [ getFirst(multiply) ]; public void run1() [ Axiom ==> F(2, 6) P(10) Tree; ] public void run2() [ Axiom ==> F(2, 6) P(10) Replicator -multiply-> Tree; ] Tree wird mit der roten Struktur instanziert es wird eingefügt, was an der „multiply“-Kante hängt
Hausaufgabe vom letzten Mal: • - Fertigstellung der dtd-Datei auf Grundlage Ihrer Messdaten • - erste Plausibilitätsprüfung • erste Sichtung des Pappelmodells; Fragen dazu? • Anhang:XL-Code des Pappelmodells (ohne Photosyntheseteil)
/* Datei streckung.rgg, gehoert zu pappel.gsz */ /* Aenderungen gegenueber frueherer Version: Wichtig: Die Basiszeiteinheit dieser Simulation liegt bei einer Stunde, d.h. pro Ausfuehrung des run()-Regelblocks vergeht eine Stunde Simulationszeit. Definiert wird dies durch die Konstante NUMBER_OF_TIME_UNITS_DURING_A_DAY = 24. Alle weiteren Angaben sind relativ zu dieser Angabe. Die Photosyntheseberechnung wurde auf SI-Einheiten umgestellt. Kommentare wurden ergaenzt. Die Statusvariable fuer das Oeffnen des Datenfiles wurde geaendert. */ import java.io.*; // Import der zweiten rgg-Datei: import static photosynthese.*; /**************************************************************************/ /************************* Modul-Definitionen des Baumes ******************/ /**************************************************************************/ // Der ganze Baum: module Tree(int age, float pspool) extends Sphere(0.01); // ein einfaches Internodium mit einer festen, sehr kleinen Startgroesse: module Internode(int alter, int lnfb) extends F(0.01, 0.0005) { // Variable zur Speicherung der freien Assimilate: float pspool = 0; float mass = 0; { setShader(GREEN); } public double getVolume() { return Math.PI*(this.diameter)*(this.diameter) / 4 * this.length; } public double getMass() { return this.getVolume()*INTERNODIUM_DENSITY; } }
// ein Meristem als Keimzelle des Wachstums: module Meristem(int alter, int lnfb) extends Sphere(0.0005) {{ setShader(RED); }}; // lnfb = leaf number from base // der Blattstiel: module Petiole(int alter, int lnfb) extends F(0.001, 0.0001) { // Variable zur Speicherung der freien Assimilate: float pspool = 0.0; float mass = 0.0; { setShader(GREEN); } public double getVolume() { return Math.PI*(this.diameter)*(this.diameter) / 4 * this.length; } public double getMass() { return this.getVolume()*PETIOLE_DENSITY; } } // das Blatt: die Groesse wird hier ueber Scale gesetzt. // Basis der Scale-Berechnung ist 1 Meter Laenge und 0.77 Meter Breite // (definiert durch LEAF_LENGTH_WIDTH_RATIO). // Das Verhaeltnis ist zur Zeit unveraenderlich! module Blade(int alter, int lnfb) extends Parallelogram(1.0, 1.0/LEAF_LENGTH_WIDTH_RATIO) { //Variable zur Speicherung der freien Assimilate float pspool = 0; float leafarea = 0; float mass = 0; public double getMass() { return this.leafarea*LEAF_WEIGHT_PER_SQUAREMETER; } };
// Module zum Veraendern der Winkel (zwischen Internodium und Blattstiel // und zwischen Blattsiel und Blattspreite): // Die Eigenschaften des Moduls werden durch eine Aktualisierungsregel // veraendert, so dass sich die Position der Blaetter waehrend der Simulation // veraendert. module Winkel(super.angle, int max_angle) extends RL(angle); /*************** Die Sonne: **************************************/ // Die Sonne, sie umkreist den Baum einmal pro Tag ... module Sonne(float ppfd) extends Sphere(3.3) {{ setShader(RGBAShader.YELLOW);}}; // Hilfssymbol zur Bewegung der Sonne: module Sonnenwinkel(super.angle) extends RU(angle); /**************************************************************/ /************** Konstanten und globale Variablen: *************/ /**************************************************************/ const float NUMBER_OF_TIME_UNITS_DURING_A_DAY = 24; // dient der Umrechnung von Tageswerten (Photosynthese) auf die // Bezugsgroesse Basiszeiteinheiten. const Shader leafmat = shader("Leaf1"); // Textur fuer die Blaetter const PrintWriter tmpfile; // Ausgabespeicher: -> Ausgabe in Datei private Boolean tmpFileOpen = false; // Kontrollvariable, ob Datenfile offen ist. const Vector3d zz = new Vector3d(0, 0, 1); // Richtung nach oben const int PLASTOCHRON = 48; // Anzahl der Zeiteinheiten zwischen dem Erscheinen zweier Blaetter
// Maximale Wachstumszeiten fuer die drei Metamer-Bestandteile: // koennen fuer Stopbedingungen fuer das Wachstum genutzt werden. const int MAX_GROWTHTIME_INTERNODE = 600; // in Basiszeiteinheiten const int MAX_GROWTHTIME_PETIOLE = 30; // in Basiszeiteinheiten const int MAX_GROWTHTIME_LEAF = 96; // in Basiszeiteinheiten // Wachstumsraten fuer die drei Metamer-Bestandteile // Wachstum pro Zeiteinheit const float GROWTH_RATE_INTERNODE_LENGTH = 0.001; // in Metern const float GROWTH_RATE_INTERNODE_DIAMETER = 0.000004; // in Metern const float GROWTH_RATE_PETIOLE_LENGTH = 0.00117; // in Metern const float GROWTH_RATE_PETIOLE_DIAMETER = 0.000031; // in Metern const float GROWTH_RATE_LEAF_SCALE = 0.0016; // betrifft beide Achsen: Skalierungsfaktor der Basisgroesse des // Blattes von 1 Meter * 0.77 Meter // Definition von 3 Wachstumszonen am Baum: // Die oberste Wachstumszone wird definiert durch den maximalen // Leaf Position Index (LPI): -1 bis TOPSECTION_MAXLPI. // Die unterste Wachstumszone durch die angegebene maximale // Leaf Number From Base (LNFB): 1 bis BASESECTION_MAXLEAFNUMBER. // Die mittlere Wachstumszone umfasst alle anderen Blaetter. // Die Wachstumszone wird errechnet durch die Servicefunktion // getGrowthSection() const int TOPSECTION = 0; const int TOPSECTION_MAXLPI = 8; const int MIDDLESECTION= 1; const int BASESECTION = 2; const int BASESECTION_MAXLEAFNUMBER = 5; // maximale Dimensionen fuer die 3 Metamerbestandteile: // die Werte werden als Arry getrennt fuer die drei Wachstumszonen // angegeben const float[] MAX_GWROWTH_INTERNODE = {0.01, 0.03, 0.02}; // in Metern const float[] MAX_GWROWTH_PETIOLE = {0.01, 0.07, 0.02}; // in Metern const float[] MAX_GWROWTH_LEAF_SCALE = {0.07, 0.09, 0.03}; // in Metern
const float LEAF_LENGTH_WIDTH_RATIO = 1.3; // konstantes Laengen / Breitenverhaeltnis der Blaetter const float LEAFFORMFACTOR = 0.6; // Flaechenanteil der realen Blattflaeche an Laenge * Breite const float INTERNODIUM_DENSITY = 106.0; // in kg/m^3 const float PETIOLE_DENSITY = 106.0; // in kg/m^3 const float LEAF_WEIGHT_PER_SQUAREMETER =100.0; // in kg/m^2 /***************** Winkel: ****************/ const int LEAF_MAX_ANGLE = 55; const int PETIOLE_MAX_ANGLE = 90; // Maximaler Divergenzwinkel zwischen zwei Internodien: const int MAX_ORIENTATION_ANGLE = 5; // Azimuthwinkel fuer den Ansatzpunkt der Blaetter am Stamm: const int MAX_AZIMUTH = 160; const int MIN_AZIMUTH = 120; // Seitliche Drehung der Blattspreite um die Blattstielachse (nicht gemessen) const int MAX_ANGLE_LAMINA = 5; //angle of lamina /******************** globale Variablen: ***************************/ public float totalPSPool = 0.0; public int maxleaf = 0; // aktuelle Zahl aller Blaetter des Baumes public int time = 0; // Zaehlvariable fuer aktuelle Zeit
/***************************************************************//***************************************************************/ /**************** Regelbloecke: ********************************/ /***************************************************************/ protected void init() { time = 0; maxleaf = 0; [ Axiom ==> Tree(0, 0.03) Meristem(0, 0) { totalPSPool = 0.03; }, ^ Sonnenwinkel(0) RU(90.0) M(50.0) Sonne(0); ] } // Zentrale Methode zur Steuerung des Simulationslaufes: // Ruft die Regelbloecke fuer die Photosntheseberechnung, den Transport // der freien Assimilate und das Wachstum der Metamere und der Bewegung // der Sonne auf. Basis der zeitlichen Abfolge ist die Variable time. public void run() { if (!tmpFileOpen) newDataFile(); println("Stunde:" + time + " Gesamt-Produktion: " + totalPSPool); tmpfile.println("Stunde" + time + "Gesamt-Produktion: " + totalPSPool); for (apply(1)) moveSun(360 / NUMBER_OF_TIME_UNITS_DURING_A_DAY); for (apply(1)) photosynthese(NUMBER_OF_TIME_UNITS_DURING_A_DAY); for (apply(4)) transport(); for (apply(1)) grow(); tmpfile.flush(); // Schreiben des Speichers tmpfiles in die Datei. time ++; } // Regel zur Kalkulation der Photosynthese in den Blaettern: // Die Photosyntheseberechnung erfolgt immer fuer einen Tagesgang. // Daher ist es noetig, den Anteil fuer die Zeitschritte zu berechnen. // Hierzu wird durch number_of_time_units geteilt.
private void photosynthese(float number_of_time_units) { tmpfile.println(" Photosynthese"); [ s:Blade ::> { int lpi = getLPI(maxleaf, s[lnfb]); s[leafarea] = s[scale] * s[scale] / LEAF_LENGTH_WIDTH_RATIO * LEAFFORMFACTOR; println("blattflaeche" + s[leafarea]); float production = calculatePS(s[leafarea], convertMC(lpi)) / number_of_time_units; s[pspool] += production; totalPSPool += production; // tmpfile.println(" Blatt: " + s[lnfb] + " LPI " + lpi + // " Pool: " + s[pspool] + " Blattflaeche: " + s[leafarea]); } ] } // Regelblock fuer das Wachstum der Pflanze: // es existieren Unter-Regelbloecke fuer das Meristem, Internodium, Petiole, Blade und Winkel. private void grow() { // Regelblock fuer das Meristem: [ // erste Regel: Das Meristem altert m1:Meristem ::> m1[alter] :+= 1; // fuer ein neues Meristem: Bildung des Internodiums: m2:Meristem, ((m2[alter] == PLASTOCHRON) || (m2[lnfb] == 0)) ==> Internode(0, m2[lnfb]+1) [ Winkel(0, PETIOLE_MAX_ANGLE) Petiole(0, m2[lnfb]+1) RH(random(MAX_ANGLE_LAMINA, -MAX_ANGLE_LAMINA)) Winkel(1,LEAF_MAX_ANGLE) [ Blade(0, m2[lnfb]+1).(setShader(leafmat), setScale(0.001)) { maxleaf++; } ] ] RH(random(MAX_AZIMUTH, MIN_AZIMUTH)) RU(random(MAX_ORIENTATION_ANGLE, -MAX_ORIENTATION_ANGLE)) Meristem(0, m2[lnfb]+1); ] // Regelblock fuer das Veraendern der Winkel von Blattstiel und Blatt: [ x:Winkel, (x[angle] <= x[max_angle]) ::> x[angle] :+= 1; ]
// Regelblock fuer die Streckung des Internodiums [ i:Internode ::> { if (i[alter] < MAX_GROWTHTIME_INTERNODE && i[length] <= MAX_GWROWTH_INTERNODE[getGrowthSection(maxleaf, i[lnfb])]) { i[length] += GROWTH_RATE_INTERNODE_LENGTH; } // Laengenwachstum in den ersten 30 Tagen i[diameter] += GROWTH_RATE_INTERNODE_DIAMETER; // Dickenwachstum immer i[alter]++; // Hochsetzen des Alters i[mass] = i.getMass(); } ] // Regelblock fuer die Streckung des Blattstiels in den ersten 60 Tagen: [ p:Petiole, (p[alter] < MAX_GROWTHTIME_PETIOLE && p[length] < MAX_GWROWTH_PETIOLE[getGrowthSection(maxleaf, p[lnfb])]) ::> { p[length] += GROWTH_RATE_PETIOLE_LENGTH; // Laengenwachstum p[diameter] += GROWTH_RATE_PETIOLE_DIAMETER; // Dickenwachstum p[alter]++; // Hochsetzen des Alters p[mass] = p.getMass(); } ] // Regelblock fuer das Blattwachstum: [ s:Blade, (s[alter] < MAX_GROWTHTIME_LEAF && s[scale] < MAX_GWROWTH_LEAF_SCALE[getGrowthSection(maxleaf, s[lnfb])]) ::> { s.setScale(s[scale] + GROWTH_RATE_LEAF_SCALE); s[alter]++; s[mass] = s.getMass(); } ] }
// Regelblock fuer den Transport von Assimilaten innerhalb der Pflanze: es existieren mehrere Transportwege // Blade (Quelle) -> zum tragenden Internodium // Internodium -> zum benachbarten Internodium // Tree (Keimling) -> zum ersten Infernodium private void transport() { tmpfile.println(" Transport " ); // Transport Blade -> zum tragenden Internodiium [ b:Blade -ancestor-> i:Internode ::> { if (getLPI(maxleaf, b[lnfb]) >= 4) { float r = 0.01 * (b[pspool] - i[pspool]); b[pspool] :-= r; i[pspool] :+= r; tmpfile.println(" Blatt: " + b[lnfb] + " zu Internode " + i[lnfb] + " Menge: " + r); } } ] // Transport Internodium -> zum benachbarten Internodium [ i_oben:Internode -ancestor-> i_unten:Internode ::> { float r = 0.01 * (i_unten[pspool] - i_oben[pspool]); tmpfile.println(" Transport von Internodium: " + i_unten[lnfb] + "zu : " + i_oben[lnfb] + " Menge: " + r); i_unten[pspool] :-= r; i_oben[pspool] :+= r; } ] // Transport Tree -> zum ersten Internodium [ t:Tree -successor-> i:Internode, (t[pspool] > 0.0001) ::> { float r = 0.001 * t[pspool]; tmpfile.println(" Transport von Treepool zu Internodium: " + i[lnfb] + " Menge: " + r); t[pspool] :-= r; i[pspool] :+= r; } ] /* Hier moegl. Ergaenzung Transp. zum Meristem: Sinnvoll bei Kopplung d. Metamerbildung an vorh. Assimil. */ }
private void moveSun(float sonnenbewegung) [ // Bewegung der Sonne: Hat nur Bedeutung für die Animation. // Es existiert kein Bezug zur numerischen Simulation. x:Sonnenwinkel ::> x[angle] :+= sonnenbewegung; ] /********************** Methoden mit Zugriff auf die Struktur ***********/ // Beschattungsfunktion: prueft, ob es fuer einen Knoten "s" // innerhalb eines Sichtkegels mit der Oeffnungsweite 40 Grad (kann man veraendern) // ein Objekt f der Modul-Klasse Petiole (Blattstiel) gibt, wobei "s" (als // "Beobachter" sozusagen) nicht beruecksichtigt wird. Rueckgabewert ist false // oder true private static boolean isShaded(Node s) { return !empty( (* f:Petiole, (f != s && f in cone(s, zz, 40)) *) ); } /********************** Methoden zur Berechnung ************************/ private int getLPI(int maxLeaf, int lnfb) { return maxLeaf - lnfb - 1; } private int getGrowthSection(int maxleaf, int lnfb) { int section = 0; if (lnfb <= BASESECTION_MAXLEAFNUMBER) section = BASESECTION; else if(getLPI(maxleaf, lnfb) <= TOPSECTION_MAXLPI) section = TOPSECTION; else section = MIDDLESECTION; return section; }
/********************* Methoden fuer Serviveausgaben ********************/ private void newDataFile() { tmpfile = new PrintWriter(new FileWriter("c:\\tmp\\dataoutput3.txt")); tmpFileOpen = true; } //-------- Spezielle Methode (public) zum Schliessen der Datei public void closeDataFile() { tmpfile.close(); // Schliessen der Datei tmpFileOpen = false; }