You are on page 1of 178

Project

Plant Growth Model


Name:

Weather.h
#pragma once

#ifndef _WEATHER_H_

#define _WEATHER_H_

struct TWeather

public:

TWeather()

year=2004,jday=1,time=0.0, daytime = jday+time, CO2=370.0, airT=20.0, PFD=0.0,


solRad=0.0,

RH=50.0, wind=1.5, rain=0.0, dayLength=12.0, soilT = airT, SoilMP_med=-100,

PredawnLWP=-0.05;

int year;

int jday;

double time, daytime;

double CO2, airT, PFD, solRad, RH, wind, rain, dayLength, soilT,ET_supply,

LeafWP, PredawnLWP,

pcrs,pcrl,

pcrq, TotalRootWeight, SoilMP_med;

float MaxRootDepth,ThetaAvail;

int DailyOutput, HourlyOutput;

};

#endif
Weather .cpp

#include "stdafx.h"

#include "weather.h"

Timer.h

#ifndef __TIMER_H__

#define __TIMER_H__

#include "cpmdate.h"

class Timer

private:

CPMDate *date;

double hour, step_size, steps_per_day;

int day_of_year;

public:

char* date_from_day_of_year(int d);

CPMDate* get_date();

int get_day_of_year() {return date->get_day_number();}

double get_hour() {return hour;}

double get_step_size() {return step_size;}

double get_steps_per_day() {return steps_per_day;}

bool step();

~Timer();
Timer();

Timer(double ss);

Timer(int day, int month, int year, double ss);

int julday(const int mm, const int id, const int iyyy);

void caldat(const int julian, int &mm, int &id, int &iyyy);

};

#endif

Timer.cpp

#include "stdafx.h"

#include "timer.h"

#include <cmath>

#include <iostream>

Timer::Timer()

date=NULL;

Timer::Timer(int dy, int my, int yr, double ss)

day_of_year=julday(my,dy,yr)-julday(1,1,yr)+1;// day_number

hour = 0.0; // hr

date = new CPMDate(day_of_year, yr);

step_size = ss; // hr

steps_per_day = 24.0/step_size;
}

Timer::~Timer()

if (date != NULL) delete date;

Timer::Timer(double ss)

step_size = ss;

hour = 0.0;

steps_per_day = 24.0/step_size;

date=new CPMDate();

CPMDate* Timer::get_date()

return date;

bool Timer::step()

// hour = 0.0;

hour += step_size;

// cout << day_of_year << " " << hour << "\n";

if (hour >= 24.0)

hour = 0.0;
day_of_year += 1;

date->increment();

return true;

return false;

char* Timer::date_from_day_of_year(int d)

CPMDate *dt = new CPMDate(d, (date->get_year()));

// return dt->get_date();

return dt->get_digit_string(); // set date format = mm/dd/yy, SK

int Timer::julday(const int mm, const int id, const int iyyy)

const int IGREG=15+31*(10+12*1582);

int ja,jul,jy=iyyy,jm;

if (jy == 0) throw("julday: there is no year zero.");

if (jy < 0) ++jy;

if (mm > 2) {

jm=mm+1;

} else {

--jy;

jm=mm+13;

jul = int(floor(365.25*jy)+floor(30.6001*jm)+id+1720995);

if (id+31*(mm+12*iyyy) >= IGREG) {


ja=int(0.01*jy);

jul += 2-ja+int(0.25*ja);

//jul=jul-2415019; // 1/1/1900 JD zero

jul=jul-2415079; // on 3/1/1900 jday = 1

return jul;

void Timer::caldat(int julian, int &mm, int &id, int &iyyy)

const int IGREG=2299161;

int ja,jalpha,jb,jc,jd,je;

julian=julian+2415019 ;

if (julian >= IGREG) {

jalpha=int(((double)(julian-1867216)-0.25)/36524.25);

ja=julian+1+jalpha-int(0.25*jalpha);

} else if (julian < 0) {

ja=julian+36525*(1-julian/36525);

} else

ja=julian;

jb=ja+1524;

jc=int(6680.0+((double)(jb-2439870)-122.1)/365.25);

jd=int(365*jc+(0.25*jc));

je=int((jb-jd)/30.6001);

id=jb-jd-int(30.6001*je);

mm=je-1;

if (mm > 12) mm -= 12;

iyyy=jc-4715;
if (mm > 2) --iyyy;

if (iyyy <= 0) --iyyy;

if (julian < 0) iyyy -= 100*(1-julian/36525);

ThermalTime.h

#pragma once

#define MINUTESPERDAY (24*60);

class CThermalTime

public:

CThermalTime(void);

virtual ~CThermalTime(void);

double get_Tcur() {return Tcur;}

double get_Tbase() {return Tbase;}

double get_Topt() {return Topt;}

double get_Tmax() {return Tmax;}

double get_sum() {return sum;}

double get_dTmpr() {return dTmpr;}

double get_timeStep() {return timeStep;}

double get_actualAge() {return actualAge;}

void set_Tcur(double x) {Tcur = x;}

void set_Tbase(double x) {Tbase = x;}

void set_Topt(double x) {Topt=x;}

void set_Tmax(double x) {Tmax=x;}

void set_timeStep(double x) {timeStep=x;}

void set_temperatures(double Tb, double To, double Tm) {Tbase = Tb; Topt = To; Tmax = Tm;}
void add(double x);

void update(double Tmpr, double step);

void initialize(double step);

private:

double Tcur;

double Tbase;

double Topt;

double Tmax;

double sum;

double dTmpr;

double timeStep;

double actualAge;

};

ThermalTime.cpp

#include "stdafx.h"

#include "thermaltime.h"

CThermalTime::CThermalTime(void)

Tcur = 25.0;

Tbase = 8.0;

Topt = 31.0;

Tmax = 43.3;

actualAge = sum = 0.0;


dTmpr = 0.0;

timeStep = 60.0;

void CThermalTime::initialize(double step)

Tcur = 0.0;

timeStep = step;

actualAge = sum = 0.0;

add(Tcur);

void CThermalTime::add(double x)

Tcur = x;

double dD = timeStep/MINUTESPERDAY;

if (Tcur <= Tbase||Tcur >= Tmax)

dTmpr = 0.0;

else

dTmpr = (Tcur-Tbase);

sum += (dTmpr*dD);

actualAge += dD;

}
void CThermalTime::update(double Tmpr, double step)

timeStep = step;

add(Tmpr);

CThermalTime::~CThermalTime(void)

Stem.h

#pragma once

#ifndef _STEM_H_

#define _STEM_H_

#include "organ.h"

class CStem: public COrgan

public:

CStem();

CStem(int);

~CStem();

double get_length() {return length;}

double get_diameter() {return diameter;}

void set_length(double x) {length=x;}

void set_diameter(double x) {diameter=x;}


void update(CDevelopment * dv);

private:

int rank;

double length;

double diameter;

};

#endif

Stem.cpp

#include "stdafx.h"

#include "stem.h"

CStem::CStem()

:COrgan(), length(0.0), diameter(0.0) {}

CStem::CStem(int n): COrgan()

rank = n;

length = diameter = 0.0;

CStem::~CStem() {}

void CStem::update(CDevelopment * dv)

COrgan::set_temperature(dv->get_Tcur());

COrgan::update();

}
Stadfex.h

#include <iostream>

#define __min(a, b) (((a)<(b))?(a):(b))

#define __max(a, b) (((a)>(b))?(a):(b))

#include <cstring>

#ifdef _WIN32

#endif

#ifndef _WIN32

#define strcpy_s(t, s) strcpy(t, s)

#define strcat_s(t, s) strcat(t, s)

#define strtok_s(s, d, c) strtok(s, d)

#define _itoa_s(i, a, n) snprintf(a, n, "%d", i)

#endif

Stadfex.cpp

#include "stdafx.h"

Solar.h
#ifndef SOLAR_H

#define SOLAR_H

class solar

public:

solar();

protected:

private:

};

#pragma once

#define cPFD 4.6

#define SolarConst 1367.0

#define FDIV_GUARD 1.0e-8

#define PI 3.1415

class CSolar

private:

int JDay;

// Environmental Components

double Time, Latitude, Longitude, altitude, tau;

double DayLength, Declination, SolarNoon, SinElevation, Azimuth;

double Sunrise, Sunset,HalfDay, Elevation, CosElevation;


double CosTheta;

// solar components

double PAR, SolarRadiation, PotentialSolarTotal,PotentialSolarDirect, PotentialSolarDiffuse;

double NIRTotal,NIRDiffuse,NIRDirect,PARTotal,PARDiffuse,PARDirect;

double PFD, NIR, PARFraction, NIRFraction;

double PotentialPARDiffuse,PotentialPARDirect, PotentialPARTotal;

double PotentialNIRDiffuse,PotentialNIRDirect, PotentialNIRTotal;

double SolarDirect, SolarDiffuse, FracDiffuse, PFDDirect, PFDDiffuse;

double FracPARDirect, FracPARDiffuse,


FracNIRDirect,FracNIRDiffuse,FracSolarDiffuse,FracSolarDirect;

double FracPFDDirect, FracPFDDiffuse;

double RowAzimuth;

bool useObs, useTau;

static double SDERP[9]; //used for declination calcs

void SetDayLength();

void SetDeclination();// Solar declination as a f(Jday)

void SetSolarNoon();

void SetSolarElevation() ; // Solar height (=elevation)

void SetAzimuth() ; // Solar azimuth measured from ?

double press();

double m() ;

void SetPotentialSolar();
void SetPotentialPAR();

void SetPotentialNIR();

void SetNIRTotal();

void SetNIRDirect();

void SetNIRDiffuse();

void SetPARTotal();

void SetPARDirect();

void SetPARDiffuse();

void SetFracPARDirect();

void SetFracNIRDirect();

void SetFracDiffuse();

void SetPARFraction(double fr);

void SetPARFraction();

public:

CSolar(void);

~CSolar(void);

void SetVal(int Day, double Time, double Lat, double Longi, double Alti, double SolRad0);

double GetDayLength() {return DayLength;}

double GetDeclination() {return Declination;}

double GetSolarNoon() {return SolarNoon;}


double GetSunrise() {return Sunrise;}

double GetSunset() {return Sunset;}

double GetSinElevation() {return SinElevation;}

double GetSolarElevation() {return Elevation;}

double GetAzimuth() {return Azimuth;}

double GetPotentialSolarDiffuse() {return PotentialSolarDiffuse;}

double GetPotentialSolarTotal() {return PotentialSolarTotal;}

double GetPotentialSolarDirect() {return PotentialSolarDirect;}

double GetSolarRadiation() {return SolarRadiation;}

double GetPAR() {return PAR;}

double GetPARFraction() {return PARFraction;}

double GetFracPARDirect () {return FracPARDirect;}

double GetFracPARDiffuse() {return 1.0-FracPARDirect;}

double GetFracNIRDirect() {return FracNIRDirect;}

double GetNIRFraction() {return 1.0-PARFraction;}

double GetPFD() {return PAR*cPFD;}

double GetPFDDiffuse() {return PAR*cPFD*(1.0-FracPARDirect);}

double GetPFDDirect() {return PAR*cPFD*FracPARDirect;}

double GetPFDTotal() {return PAR*cPFD;} // sane as GetPFD

double GetNIR() {return NIR;}

double GetNIRTotal() {return NIRTotal;}

double GetNIRDiffuse() {return NIRDiffuse;}

double GetNIRDirect() {return NIRDirect;}

};
#endif // SOLAR_H

Solar.cpp

#include <cmath>

#include <algorithm>

#include "solar.h"

using namespace std;

inline double DegToRad(double Deg) { return (Deg/180.0)*PI; }

inline double RadToDeg(double Rad) { return (Rad*180.0)/PI; }

double CSolar::SDERP[9] = {

0.3964E-0,0.3631E1,0.3838E-1,0.7659E-1,0.0000E0,-0.2297E2,-0.3885E0,-0.1587E-0,-
0.1021E-1

};

CSolar::CSolar()

JDay = 0;

Time = 0;

Latitude = 0;

Longitude = 0;
tau = 0.75;

useTau = false;

PFD = -99;

PAR = -99;

SolarRadiation = -99;

PARFraction = 0.5;

useObs = false;

HalfDay= 6.0;

DayLength=12;

CSolar::~CSolar()

void CSolar::SetVal(int Day, double Time, double Lat, double Longi, double Alti, double SolRad0) //
Altitude available

JDay = Day;

this->Time = Time*24;

Latitude = DegToRad(Lat);

Longitude = Longi;

useTau = false;

useObs = true;

altitude = max(50.0,Alti);

SetDeclination();

SetDayLength();

SetSolarNoon();
SetSolarElevation();

SetAzimuth();

// set up potential radiation values

SetPotentialSolar();

SetPotentialPAR();

SetPotentialNIR();

SetPARFraction();

SolarRadiation=SolRad0;

PAR=SolarRadiation*PARFraction;

SetPARFraction(PAR/SolarRadiation);

SetPotentialNIR();

SetFracNIRDirect();

SetNIRDiffuse();

SetNIRTotal();

SetFracPARDirect();

SetPARTotal();

SetPARDirect();

SetPARDiffuse();

NIR=SolarRadiation*(1-PARFraction);

PFD=PAR*cPFD;
}

void CSolar::SetDeclination()

// From GLYCIM

double Ang1;

int i, n, j;

Declination = SDERP[0];

for (i=2;i<=5;i++)

n = i - 1;

j = i + 4;

Ang1 = n*0.01721*JDay;

Declination = Declination + SDERP[i-1]*sin(Ang1) + SDERP[j-1]*cos(Ang1);

Declination = DegToRad(Declination);

void CSolar::SetDayLength()

double LatRad, D1, D2, D3,hs;

LatRad=Latitude;

D1 = sin(LatRad)*sin(Declination);
D2 = cos(LatRad)*cos(Declination);

D3 = D1 + D2;

hs = acos((-0.014544 - D1)/D2);

DayLength=hs*24/PI;

HalfDay=DayLength/2.0;

// 7.6394 = 180/3.1416/360*24*2

void CSolar::SetSolarNoon()

double divisor=15.0;

double LC, EqTime, Epsil;

// this should be more general

LC=fmod(Longitude,15);

LC=(LC*-1.0)*1/15;;

//

Epsil = DegToRad(279.575 + 0.9856*JDay);

EqTime = (-104.7*sin(Epsil)+596.2*sin(2*Epsil)+4.3*sin(3*Epsil)

-12.7*sin(4*Epsil) - 429.3*cos(Epsil)-2.0*cos(2*Epsil)

+ 19.3*cos(3*Epsil))/3600; // Calculating Equation of Time Eq 11.4 in Campbell and


Norman (1998)

SolarNoon=12.0 - LC - EqTime;

Sunrise=SolarNoon - HalfDay;
Sunset=SolarNoon + HalfDay;

void CSolar::SetSolarElevation()

// if Sin_elev < 0 then Elev = 0

// else

SinElevation= sin(Latitude)*sin(Declination)
+cos(Latitude)*cos(Declination)*cos(DegToRad(15*(Time-SolarNoon)));

Elevation=asin(SinElevation);

Elevation=max(FDIV_GUARD,Elevation);

// identical to sin_elev, theta is the zenith angle

CosTheta=max(FDIV_GUARD, SinElevation); //*Sign(Sin_Elev); this value should never be


negative, later used in log fn

if (fabs(CosTheta)<FDIV_GUARD)

{ CosTheta=0.0;}

CosElevation= sqrt(1-SinElevation*SinElevation);

void CSolar::SetAzimuth()

if(Time < SolarNoon)

Azimuth=
-acos((SinElevation*sin(Latitude)-sin(Declination))/(cos(Latitude)*CosElevation));

else if (Time > SolarNoon)

{
Azimuth=
acos((SinElevation*sin(Latitude)-sin(Declination))/(cos(Latitude)*CosElevation));

else Azimuth=0;

double CSolar::press()

return 101.325*exp(-altitude/8200.0);

double CSolar::m()

return press()/(101.325*CosTheta);

void CSolar::SetPotentialSolar()

if (SinElevation>= 0.0)

PotentialSolarDirect = SolarConst*pow(tau,m())*SinElevation*(1+0.033*cos(2*PI*(JDay-
10)/365));

PotentialSolarDiffuse = 0.3*(1-
pow(tau,m()))*SolarConst*SinElevation*(1+0.033*cos(2*PI*(JDay-10)/365));

else
{

PotentialSolarDirect = 0.0;

PotentialSolarDiffuse = 0.0;

PotentialSolarTotal=PotentialSolarDiffuse + PotentialSolarDirect;

void CSolar::SetPotentialPAR()

PotentialPARDirect= 600.0*exp(-0.185*m())*CosTheta;

PotentialPARDiffuse= 0.4*(600.0-PotentialPARDirect)*CosTheta;

PotentialPARTotal=PotentialPARDirect+ PotentialPARDiffuse;

void CSolar::SetFracPARDirect() //Fraction of PAR in direct beam

const double A = 0.9, B = 0.7;

double ratio, ratio2, R1, R2, R3;

ratio = SolarRadiation/(PotentialPARTotal + PotentialNIRTotal);

ratio2=A-FDIV_GUARD;
R1=A-min(A-FDIV_GUARD,ratio);

R2=R1/B;

R3=pow(R2,2.0/3.0);

FracPARDirect= max(0.0,(PotentialPARDirect/PotentialPARTotal)*(1-pow((A-min(A-
FDIV_GUARD,ratio))/B,(2.0/3.0))));

void CSolar::SetPARTotal() // for measured

PARTotal= SolarRadiation*GetPARFraction();

void CSolar::SetPARDirect() // for measured

PARDirect= PARTotal*FracPARDirect;

void CSolar::SetPARDiffuse()

PARDiffuse= PARTotal*GetFracPARDiffuse();

void CSolar::SetPotentialNIR()

{
double x, w;

x = 1/CosTheta;

w=-1.1950+0.4459*log10(x)-0.0345*log10(x)*log10(x);

w = 1320.0*pow(10.0,w);

PotentialNIRDirect= (720.0*exp(-0.06*m())-w)*CosTheta;

PotentialNIRDiffuse= 0.6*(720.0-PotentialNIRDirect-w)*CosTheta;

PotentialNIRTotal=PotentialNIRDirect+PotentialNIRDiffuse;

void CSolar::SetFracNIRDirect() //Fraction of NIR in direct beam

const double C = 0.88, D = 0.68;

double ratio;

ratio = SolarRadiation/(PotentialPARTotal + PotentialNIRTotal);

FracNIRDirect= max(0.0,(PotentialNIRDirect/PotentialNIRTotal)*(1-pow((C-min(C-
FDIV_GUARD,ratio))/D,2/3)));

void CSolar::SetNIRTotal() // for measured

NIRTotal= SolarRadiation*GetNIRFraction();
}

void CSolar::SetNIRDirect() // for measured

NIRDirect= NIRTotal*FracNIRDirect;

void CSolar::SetNIRDiffuse()

NIRDiffuse= NIRTotal*FracNIRDiffuse;

void CSolar::SetPARFraction()

double tmp;

tmp = PotentialPARTotal/(PotentialPARTotal+PotentialNIRTotal);

if (useTau)

if (tau >= 0.7) {tmp= 0.45;}

else

if (tau <= 0.3) {

tmp= 0.55;

else {

tmp= 0.625 - tau*0.25;

}
}

PARFraction=min(max(FDIV_GUARD, tmp),(1.0-FDIV_GUARD));

void CSolar::SetPARFraction(double Fraction)

PARFraction = min(max(FDIV_GUARD, Fraction),(1.0-FDIV_GUARD));

#include <cmath>

#include <algorithm>

#include "solar.h"

using namespace std;

inline double DegToRad(double Deg) { return (Deg/180.0)*PI; }

inline double RadToDeg(double Rad) { return (Rad*180.0)/PI; }

double CSolar::SDERP[9] = {

0.3964E-0,0.3631E1,0.3838E-1,0.7659E-1,0.0000E0,-0.2297E2,-0.3885E0,-0.1587E-0,-
0.1021E-1

};

CSolar::CSolar()
{

JDay = 0;

Time = 0;

Latitude = 0;

Longitude = 0;

tau = 0.75;

useTau = false;

PFD = -99;

PAR = -99;

SolarRadiation = -99;

PARFraction = 0.5;

useObs = false;

HalfDay= 6.0;

DayLength=12;

CSolar::~CSolar()

void CSolar::SetVal(int Day, double Time, double Lat, double Longi, double Alti, double SolRad0) //
Altitude available

JDay = Day;

this->Time = Time*24;

Latitude = DegToRad(Lat);

Longitude = Longi;

useTau = false;
useObs = true;

altitude = max(50.0,Alti);

SetDeclination();

SetDayLength();

SetSolarNoon();

SetSolarElevation();

SetAzimuth();

// set up potential radiation values

SetPotentialSolar();

SetPotentialPAR();

SetPotentialNIR();

SetPARFraction();

SolarRadiation=SolRad0;

PAR=SolarRadiation*PARFraction;

SetPARFraction(PAR/SolarRadiation);

SetPotentialNIR();

SetFracNIRDirect();

SetNIRDiffuse();

SetNIRTotal();

SetFracPARDirect();

SetPARTotal();

SetPARDirect();

SetPARDiffuse();

NIR=SolarRadiation*(1-PARFraction);

PFD=PAR*cPFD;
}

void CSolar::SetDeclination()

// From GLYCIM

double Ang1;

int i, n, j;

Declination = SDERP[0];

for (i=2;i<=5;i++)

n = i - 1;

j = i + 4;

Ang1 = n*0.01721*JDay;

Declination = Declination + SDERP[i-1]*sin(Ang1) + SDERP[j-1]*cos(Ang1);

Declination = DegToRad(Declination);

void CSolar::SetDayLength()
{

double LatRad, D1, D2, D3,hs;

LatRad=Latitude;

D1 = sin(LatRad)*sin(Declination);

D2 = cos(LatRad)*cos(Declination);

D3 = D1 + D2;

hs = acos((-0.014544 - D1)/D2);

DayLength=hs*24/PI;

HalfDay=DayLength/2.0;

// 7.6394 = 180/3.1416/360*24*2

void CSolar::SetSolarNoon()

double divisor=15.0;

double LC, EqTime, Epsil;

// this should be more general

LC=fmod(Longitude,15);

LC=(LC*-1.0)*1/15;;

//

Epsil = DegToRad(279.575 + 0.9856*JDay);

EqTime = (-104.7*sin(Epsil)+596.2*sin(2*Epsil)+4.3*sin(3*Epsil)

-12.7*sin(4*Epsil) - 429.3*cos(Epsil)-2.0*cos(2*Epsil)
+ 19.3*cos(3*Epsil))/3600; // Calculating Equation of Time Eq 11.4 in Campbell and
Norman (1998)

SolarNoon=12.0 - LC - EqTime;

Sunrise=SolarNoon - HalfDay;

Sunset=SolarNoon + HalfDay;

void CSolar::SetSolarElevation()

// if Sin_elev < 0 then Elev = 0

// else

SinElevation= sin(Latitude)*sin(Declination)
+cos(Latitude)*cos(Declination)*cos(DegToRad(15*(Time-SolarNoon)));

Elevation=asin(SinElevation);

Elevation=max(FDIV_GUARD,Elevation);

// identical to sin_elev, theta is the zenith angle

CosTheta=max(FDIV_GUARD, SinElevation); //*Sign(Sin_Elev); this value should never be


negative, later used in log fn

if (fabs(CosTheta)<FDIV_GUARD)

{ CosTheta=0.0;}

CosElevation= sqrt(1-SinElevation*SinElevation);

void CSolar::SetAzimuth()

if(Time < SolarNoon)

{
Azimuth=
-acos((SinElevation*sin(Latitude)-sin(Declination))/(cos(Latitude)*CosElevation));

else if (Time > SolarNoon)

Azimuth=
acos((SinElevation*sin(Latitude)-sin(Declination))/(cos(Latitude)*CosElevation));

else Azimuth=0;

double CSolar::press()

return 101.325*exp(-altitude/8200.0);

double CSolar::m()

return press()/(101.325*CosTheta);

void CSolar::SetPotentialSolar()

if (SinElevation>= 0.0)

PotentialSolarDirect = SolarConst*pow(tau,m())*SinElevation*(1+0.033*cos(2*PI*(JDay-
10)/365));
PotentialSolarDiffuse = 0.3*(1-
pow(tau,m()))*SolarConst*SinElevation*(1+0.033*cos(2*PI*(JDay-10)/365));

else

PotentialSolarDirect = 0.0;

PotentialSolarDiffuse = 0.0;

PotentialSolarTotal=PotentialSolarDiffuse + PotentialSolarDirect;

void CSolar::SetPotentialPAR()

PotentialPARDirect= 600.0*exp(-0.185*m())*CosTheta;

PotentialPARDiffuse= 0.4*(600.0-PotentialPARDirect)*CosTheta;

PotentialPARTotal=PotentialPARDirect+ PotentialPARDiffuse;

void CSolar::SetFracPARDirect() //Fraction of PAR in direct beam


{

const double A = 0.9, B = 0.7;

double ratio, ratio2, R1, R2, R3;

ratio = SolarRadiation/(PotentialPARTotal + PotentialNIRTotal);

ratio2=A-FDIV_GUARD;

R1=A-min(A-FDIV_GUARD,ratio);

R2=R1/B;

R3=pow(R2,2.0/3.0);

FracPARDirect= max(0.0,(PotentialPARDirect/PotentialPARTotal)*(1-pow((A-min(A-
FDIV_GUARD,ratio))/B,(2.0/3.0))));

void CSolar::SetPARTotal() // for measured

PARTotal= SolarRadiation*GetPARFraction();

void CSolar::SetPARDirect() // for measured

PARDirect= PARTotal*FracPARDirect;

void CSolar::SetPARDiffuse()

PARDiffuse= PARTotal*GetFracPARDiffuse();

}
void CSolar::SetPotentialNIR()

double x, w;

x = 1/CosTheta;

w=-1.1950+0.4459*log10(x)-0.0345*log10(x)*log10(x);

w = 1320.0*pow(10.0,w);

PotentialNIRDirect= (720.0*exp(-0.06*m())-w)*CosTheta;

PotentialNIRDiffuse= 0.6*(720.0-PotentialNIRDirect-w)*CosTheta;

PotentialNIRTotal=PotentialNIRDirect+PotentialNIRDiffuse;

void CSolar::SetFracNIRDirect() //Fraction of NIR in direct beam

const double C = 0.88, D = 0.68;

double ratio;

ratio = SolarRadiation/(PotentialPARTotal + PotentialNIRTotal);

FracNIRDirect= max(0.0,(PotentialNIRDirect/PotentialNIRTotal)*(1-pow((C-min(C-
FDIV_GUARD,ratio))/D,2/3)));

}
void CSolar::SetNIRTotal() // for measured

NIRTotal= SolarRadiation*GetNIRFraction();

void CSolar::SetNIRDirect() // for measured

NIRDirect= NIRTotal*FracNIRDirect;

void CSolar::SetNIRDiffuse()

NIRDiffuse= NIRTotal*FracNIRDiffuse;

Shealth.h

void CSolar::SetPARFraction()

double tmp;

tmp = PotentialPARTotal/(PotentialPARTotal+PotentialNIRTotal);

if (useTau)

if (tau >= 0.7) {tmp= 0.45;}

else

if (tau <= 0.3) {


tmp= 0.55;

else {

tmp= 0.625 - tau*0.25;

PARFraction=min(max(FDIV_GUARD, tmp),(1.0-FDIV_GUARD));

void CSolar::SetPARFraction(double Fraction)

PARFraction = min(max(FDIV_GUARD, Fraction),(1.0-FDIV_GUARD));

Root.h

#pragma once

#include "organ.h"

class CRoots :

public COrgan

public:

CRoots(void);

virtual ~CRoots(void);

bool GetInitialized() {return Initialized;}

void SetInitialized() {Initialized=true;}


private:

bool Initialized;

};

Root.cpp

#include "stdafx.h"

#include "roots.h"

CRoots::CRoots(void)

Initialized=false;

CRoots::~CRoots(void)

Radstrans.h

#include "solar.h"

class CRadTrans

private:

enum CLeafAngle {Spherical, Horizontal, Vertical, Diaheliotropic, Empirical, Ellipsoidal, Corn};

//type TCover = (Glass, Acrylic, polyethyl, doublepoly, whitewashed, NoCover);

double absorp; //leaf absorptivity for PAR


double clump ;

double rho_soil ; // soil reflectivity for PAR band

double IrradianceDirect, IrradianceDiffuse, LAI, Elev, LeafAngleFactor, KbVal, KdVal;

CLeafAngle LeafAngle; // Solar elevation of a beam and cumulative LAI at the layer, diffused
fraction (fdf)

bool IsLeafAngleFactorUsed;

double Qbt(double L);

double Qb(double L);

double Qd(double L);

double Qsoil();

void Kb(double theta) ;

void Kd(double LA);

public:

CRadTrans(void); //sdf: diffused fraction of solar radiation

~CRadTrans(void);

void SetVal(CSolar Irradiance, double LAI, double leafAngleFactor);

double Qsc();

double Qsc(double L);

double Qtot(double L); //total irradiance (dir + dif) at depth L, simple empirical approach

double Irradiancetot(); // total PAR at top of the canopy

double Qsl();

double Qsh();

double Qsl(double L);

double Qsh(double L);

double Qdm() ;

double Qsoilm();

double GetZenith();
double Reflect();

double LAIsl();

double LAIsh();

double Fsl(double L);

double Fsh(double L);

double GetKb(){return KbVal;} ; // extiction coefficient assuming spherical leaf dist

double GetKd(){return KdVal;}

};

Radtrans.cpp

#include <cmath> // need for math functions

#include <algorithm> // need for max min functions

#include "radtrans.h"

using namespace std;

inline double cot(double a) { return 1/tan(a); }

inline double sqr(double a) {return (a * a);}

CRadTrans::CRadTrans()

LeafAngle = Spherical;

LeafAngleFactor = 1.0; // for spherical leaves


IsLeafAngleFactorUsed = false;

absorp = 0.85; //leaf absorptivity for PAR

clump = 1.0;

rho_soil = 0.10; // soil reflectivity for PAR band

CRadTrans::~CRadTrans()

void CRadTrans::SetVal(CSolar Irradiance, double SLAI, double LeafAngleFactorIn)

IrradianceDirect = Irradiance.GetPFDDirect();

IrradianceDiffuse = Irradiance.GetPFDDiffuse();

// the transmittance values obtained from Day and Bailey (1999), chap 3, ecosystems of the world 20:
greenhouse ecosystem, page 76

LAI = SLAI;

Elev = Irradiance.GetSolarElevation();

LeafAngleFactor = LeafAngleFactorIn;

IsLeafAngleFactorUsed = true;

Kb(GetZenith()); // Calculate KbVal

Kd(LAI); // Calculate KdVal

// GDiffuse = S;

}
double CRadTrans::GetZenith() // need to move this to CSolar

double zenith;

zenith=fabs(PI/2.0-Elev);

zenith=min(zenith,1.56);

return zenith;

double CRadTrans::Reflect()

return (1-sqrt(absorp))/(1+sqrt(absorp))*(2*GetKb()/(GetKb()+GetKd()));

void CRadTrans::Kb(double theta)

double x, tmp;

tmp = 0.5;

switch (LeafAngle)

case Spherical:

tmp = 0.5/sin(Elev);

break;

case Horizontal:
tmp = 1/sin(Elev); //1

break;

case Vertical:

tmp = (2/cot(Elev))/PI;

break;

case Diaheliotropic:

tmp = 1; // 1/sin_elev;

break;

case Empirical:

tmp = 0.667;

break;

default:

tmp= 0.5/sin(Elev);

if (IsLeafAngleFactorUsed == true) {

x = LeafAngleFactor; }

else

switch (LeafAngle)

case Spherical:

x = 1; //

break;

case Horizontal:

x = 10; //1

break;

case Vertical:

x = 0;
break;

case Corn:

x = 1.37;

break;

default:

x =1;

tmp = sqrt(sqr(x)+sqr(tan(theta)))/(x+1.774*pow(x+1.182,-0.733));

// else tmp = 0.5/sin(5);

KbVal=tmp*clump;

void CRadTrans::Kd(double LA)

const double gauss3[3] = {-0.774597,0,0.774597}; // abscissas

const double weight3[3] = {0.555556,0.888889,0.555556};

double K, FDiffuse, tmp, angle, x;

int i;

if (IsLeafAngleFactorUsed == true) {

x = LeafAngleFactor;}

else

{
switch (LeafAngle)

case Spherical:

x = 1; //

break;

case Horizontal:

x = 10; //1

break;

case Vertical:

x = 0;

break;

case Corn:

x = 1.37;

break;

default:

x = 1;

} //end else

FDiffuse = 0;

for (i = 0; i<3; i++) //diffused light ratio to ambient, itegrated over all incident angles from -90 to 90

angle = (PI/2)/2*(gauss3[i]) + (PI/2)/2;

tmp = sqrt(sqr(x)+sqr(tan(angle)))/(x+1.774*pow(x+1.182,-0.733));

FDiffuse = FDiffuse + (PI/2)/2*(2*exp(-tmp*LA)*sin(angle)*cos(angle))*weight3[i];

if (LA <= 0.0) {

K = 0.0;

}
else {K = -log(FDiffuse)/LA;}

KdVal=K*clump;

double CRadTrans::Irradiancetot() //total irradiance at the top of the canopy, passed over from either
observed PAR or TSolar or TIrradiance

return (IrradianceDirect + IrradianceDiffuse); //IrradianceDirect: beam radiation at top of canopy,


IrradianceDiffuse: diffuse radiation at top.

double CRadTrans::Qtot(double L) //total irradiance (Direct + Diffuse) at depth L, simple empirical


approach

double result;

result=Irradiancetot()*exp(-sqrt(absorp)*(GetKd()+GetKb())/2*L); //; //

return result;

double CRadTrans::Qbt(double L) // total beam radiation at depth L

return (IrradianceDirect*exp(-sqrt(absorp)*GetKb()*L));

double CRadTrans::Qd(double L) // net diffuse flux at depth of L within canopy

return IrradianceDiffuse*exp(-sqrt(absorp)*GetKd()*L);

}
double CRadTrans::Qdm() // weighted average absorved diffuse flux over depth of L within canopy
accounting for exponential decay

if (LAI <= 0) {

return 0;}

else {

return IrradianceDiffuse*(1-exp(-sqrt(absorp)*GetKd()*LAI))/(sqrt(absorp)*GetKd()*LAI); //
Integral Qd / Integral L

double CRadTrans::Qb(double L) // unintercepted beam (direct beam) flux at depth of L within canopy

return IrradianceDirect*exp(-GetKb()*L);

double CRadTrans::Qsl() // mean flux density on sunlit leaves

return GetKb()*IrradianceDirect + Qsh();

double CRadTrans::Qsl(double L) // flux density on sunlit leaves at delpth L

return GetKb()*IrradianceDirect + Qsh(L);

double CRadTrans::Qsh() // mean flux density on shaded leaves over LAI

{
return (Qdm() + Qsc() + Qsoilm()); // include soil reflection

double CRadTrans::Qsh(double L) // diffuse flux density on shaded leaves at depth L

return (Qd(L) + Qsc(L) + Qsoilm()); // include soil reflection

double CRadTrans::Qsoilm() // weighted average of Soil reflectance over canopy accounting for
exponential decay

if (LAI <= 0) {

return 0;}

else {

return Qsoil()*rho_soil*(1-exp(-sqrt(absorp)*GetKd()*LAI))/(sqrt(absorp)*GetKd()*LAI); //
Integral Qd / Integral L

double CRadTrans::Qsc() // weighted average scattered radiation within canopy

double totBeam, nonscatt;

if (LAI == 0) {

return 0;}

else

totBeam = IrradianceDirect*(1-exp(-sqrt(absorp)*GetKb()*LAI))/(sqrt(absorp)*GetKb()); // total


beam including scattered absorbed by canopy
nonscatt= IrradianceDirect*(1-exp(-GetKb()*LAI))/(GetKb()); //non scattered beam absorbed by
canopy

return (totBeam-nonscatt)/LAI; //mean scattered flux density

// return (Qbt(LAI)-Qb(LAI));

double CRadTrans::Qsc(double L) // scattered radiation at depth L in the canopy

return Qbt(L)-Qb(L); // total beam - nonscattered beam at depth L

double CRadTrans::Qsoil() // total PFD at the soil sufrace under the canopy

return Qtot(LAI);

double CRadTrans::LAIsl() // sunlit LAI assuming closed canopy; thus not accurate for row or isolated
canopy

if (Elev <= 0.01) {

return 0;}

else {

return (1-exp(-GetKb()*LAI))/GetKb();}

double CRadTrans::LAIsh()// shaded LAI assuming closed canopy

{
return LAI - LAIsl();

double CRadTrans::Fsl(double L) // sunlit fraction of current layer

if (Elev <= 0.01) {

return 0;}

else {

return exp(-GetKb()*L);}

double CRadTrans::Fsh(double L)

return 1 - Fsl(L);

Plant.h

#pragma once

#ifndef _PLANT_H_

#define _PLANT_H_

#include "organ.h"

#include "nodalunit.h"

#include "development.h"

#include "Roots.h"

#include "ear.h"

#include "gas_exchange.h"
#include <iostream>

#include <string>

struct TStage

public:

TStage() { V = 0.0; R = 0.0;}

double V, R;

};

class CPlant

public:

CPlant(const TInitInfo&, TGasExSpeciesParam&);

~CPlant();

CNodalUnit* get_nodalUnit() { return nodalUnit; }

CEar * get_ear() { return ear; }

CRoots * get_roots() { return roots; }

CDevelopment * get_develop() { return develop; }

CGasExchange * get_sunlit() { return this->sunlit; }

CGasExchange * get_shaded() { return this->shaded; } //get access to the pointers that point to
the sunlit/shade leaves Yang 8/29/06

int get_nodeNumber() { return nodeNumber; }

int get_finalNodeNumber() { return finalNodeNumber; }

double get_mass() { return mass; }

double get_age() { return age; }


double get_CH2O() { return CH2O; }

double get_N() { return TotalNitrogen; } //Total N in plant mg plant-1

double get_Pg() { return photosynthesis_gross; }

double get_Pn() { return photosynthesis_net; }

double get_assimilate() { return assimilate; }

double get_ET() { return transpiration; }

double get_ET_Old() { return transpirationOld; }

double get_tmpr() { return temperature; }

double get_C_pool() { return C_pool; }

double get_C_pool_root() { return C_pool_root; }

double get_C_reserve() { return C_reserve; }

double get_MaintenanceRespiration() { return maintRespiration; }

double get_stemMass() { return stemMass; }

double get_leafMass() { return leafMass; }

double get_earMass() { return earMass; }

double get_shootMass() { return shootMass; }

double get_rootMass() { return rootMass; }

double get_shootPart() { return shootPart; }

double get_leafPart() { return leafPart; }

double get_rootPart() { return rootPart; }

double get_DroppedLeafMass() { return droppedLeafmass; }

double get_activeLeafMass() { return activeLeafMass; }

double get_conductance() { return conductance; }

double get_VPD() { return VPD; }

double get_LeafArea() { return leafArea; }

double get_LeafN() { return leaf_N; }

double get_LeafNFraction() { return leaf_NFraction; }

double get_HourlyNitrogenDemand() { return HourlyNitrogenDemand; }

double get_CumulativeNitrogenDemand() { return CumulativeNitrogenDemand; }


double get_HourlyNitrogenSoilUptake() { return HourlyNitrogenSoilUptake; }

double get_CumulativeNitrogenSoilUptake() { return CumulativeNitrogenSoilUptake; }

double get_droppedLfArea() { return currentDroppedLfArea; }

double get_ptnLfIncrease() { return potentialLeafAreaIncrease; }

double get_sunlit_LAI() { return sunlit_LAI; }

double get_shaded_LAI() { return shaded_LAI; }

double get_sunlit_PFD() { return sunlit_PFD; }

double get_shaded_PFD() { return shaded_PFD; }

double get_sunlit_A_net() { return sunlit_A_net; }

double get_shaded_A_net() { return shaded_A_net; }

double get_sunlit_A_gross() { return sunlit_A_gross; }

double get_shaded_A_gross() { return shaded_A_gross; }

double get_sunlit_gs() { return sunlit_gs; }

double get_shaded_gs() { return shaded_gs; }

double getC2_effect() {return C2_effect; }

double getSunlitRatio() {return SunlitRatio; }

string getNote() { return note; }

TStage get_stage() {return stage;}

void setMass();

void set_age(double x) {age=x;}

void set_CH2O();

void set_N(double x) {TotalNitrogen= x;} // Units are grams. Was scaled from mg in crop.cpp

void set_HourlyNitrogenDemand (double x) {HourlyNitrogenDemand=x;}

void set_CumulativeNitrogenDemand (double x) {CumulativeNitrogenDemand=x;}

void set_HourlyNitrogenSoilUptake(double x) {HourlyNitrogenSoilUptake=x;}

void set_CumulativeNitrogenSoilUptake(double x) {CumulativeNitrogenSoilUptake=x;}

void set_C_pool_root(double x) {C_pool_root=x;}


void set_NitrogenRatio(double x) {NitrogenRatio=x;}

void update(const TWeather&);

void calcGasExchange(const TWeather & weather, const TGasExSpeciesParam& photoparam);

void calcMaintRespiration(const TWeather&);

double calcLeafArea();

double calcTotalLeafMass();

double calcActiveLeafMass();

double calcDroppedLeafMass();

double calcGreenLeafArea();

double calcActualGreenLeafArea();

double calcPotentialLeafArea();

double calcPotentialLeafAreaIncrease(void);//calculate potential leaf area increase without


carbon limitation YY

void calcPerLeafRelativeAreaIncrease();

double calcSenescentLeafArea();

double calcPotentialCarbondemand(); //calculate potential carbon demand for potential leaf


growth YY

void grow();

void C_allocation(const TWeather&);

void calcRed_FRedRatio(const TWeather&);

void writeNote(const TWeather &);

private:
TInitInfo initInfo;

TGasExSpeciesParam gasExparam;

CNodalUnit * nodalUnit; //nodal Unit object

CEar * ear; //ear object

CRoots * roots; //root object

CDevelopment * develop; //phenology object

CGasExchange * sunlit; //sunlit photosynthesis object

CGasExchange * shaded; //declare two pointers that point to a sunlit and a shaded leaf Yang
8/29/06

string note;

int finalNodeNumber; //final number of nodes

int nodeNumber; // currently initiated number of nodes

// Note C variables are as carbohydrate (same as dry matter)

// masses are also carbohydrate (dry matter)

double C_pool; // shorterm C pool, g(CH2O)

double C_avail; //Availabel carbon from long term reserved carbon pool YY

double C_pool_root; //storage for carbon allocated to roots but not used

//in the previous time step

double C_reserve; // longterm C pool

double C_content;

double C_demand;

double C_supply;

double C_ReserveLeaf; //holds extra C in leaf - allows SLA to change

double mass, seedMass,stemMass, leafMass, shootMass, rootMass, seedRootMass, earMass,


activeLeafMass, droppedLeafmass; // this is redundant, but for convenience of access

double maintRespiration;

double sowingDay;

double age;
double CH2O; // carbohydrate, also dry matter, g

double N; //Need to delete this - replaced by TotalNitrogen

double N_pool; //SK 8/20/10: Short-term N pool for remobilization, this should come mostly
from senescing leaves and can be purged daily to active leaves, not implemented at the moment

double leafArea, droppedLfArea;

double currentDroppedLfArea;

double previousDroppedlfArea;

double greenLeafArea,actualGreenLeafArea;

double senescentLeafArea;

double potentialLeafArea;

double potentialLeafAreaIncrease; //Increase in leaf area without carbon limitation YY

double PotentialLeafCarbonDemand; //Carbon demand for potential leaf growth without carbon
limitation YY

double photosynthesis_gross; // gros photosynthesis, umolCO2 m-2 s-1

double photosynthesis_net; // gros photosynthesis, umolCO2 m-2 s-1

double assimilate; //assimilation flux, g CO2 per plant per timestep

double transpiration, transpirationOld; //current and previous values of transpiration - g per


plant per hr

double VPD;

double conductance;

double temperature;

double shootPart; //g per plant Carbohydrate partitioined to shoot

double rootPart; // g per plant Carbohydrate partitioned to root

double shootPart_old; //g per plant carbohydrate partitioned to root in the previous time step

double rootPart_old;

double leafPart; //g per plant carbohydrate partitioned to leaf


double TotalNitrogen; //This is the total nitrogen content of the plant in g plant-1

double HourlyNitrogenDemand; // Nitrogen demand in g N plant-1

double CumulativeNitrogenDemand; // cumulativeNitrogen demand in g N plant-1

double CumulativeNitrogenSoilUptake; // Nitrogen uptake from the soil g N plant-1

double HourlyNitrogenSoilUptake;

double leaf_NFraction; //records the fraction of nitrogen in leaves YY

double leaf_N; //total nitrogen in the leaves of a plant YY (grams N per plant)

double leaf_N_content; //leaf nitrogen content (per unit square meter) of a plant YY

double OptimalLeafN; //g N holds leaf N content that is optimal

double NitrogenRatio; //optimal N ratio according to N Dilution ratio

double emerge_gdd;//records thermal time needed for plant to emergy YY

double sunlit_LAI, shaded_LAI; // sunlit and shaded LAI values

double sunlit_PFD, shaded_PFD;

double sunlit_A_net, shaded_A_net,

sunlit_A_gross, shaded_A_gross,

sunlit_gs, shaded_gs;

double SunlitRatio; // daily ratio of sunlit leaf area to use for scaling leaf expansion due to
carbon stress

double C2_effect;

TStage stage;

};

#endif
Plant.cpp

//

#include "stdafx.h"

#include "plant.h"

//#include "gas_exchange.h"

#include "radtrans.h"

#include "timer.h"

#include <cmath>

#include <vector>

#include <iostream>

#include <sstream>

#include <iomanip>

#define PRIMORDIA 5

#define CO2_MW 44.0098

#define C_MW 12.011

#define CH2O_MW 30.03

using namespace std;

CPlant::CPlant(const TInitInfo& info, TGasExSpeciesParam& photoparam)

nodalUnit = NULL;

ear = NULL;

roots = NULL;

develop = NULL;

seedMass = mass = CH2O = 0.275; // seed weight g/seed


C_content = 0.40; // 40% C, See Kim et al. (2007) EEB

C_pool = 0.0; //mass*C_content;

C_reserve = 0.0;

C_ReserveLeaf=0.0;

C_demand = C_supply = 0.0;

C_pool_root=0.0;

maintRespiration = 0.0;

C2_effect=1.0;

SunlitRatio=0.0;

// initialize plant part sizes //

shootPart = 0.60;

rootPart = 0.40;

earMass=droppedLeafmass=rootMass=0.0;

shootMass=seedMass*(1.0-rootPart);

rootMass=seedMass-shootMass;

leafMass = activeLeafMass=0.90*shootMass;

stemMass=0.10*shootMass;

shootPart_old = shootPart; rootPart_old = rootPart;

TotalNitrogen = 0.275*3.4/100.0; //assume nitrogen concentration at the beginning is 3.4% of the


total weight of the seed

leaf_N = 0;

leaf_NFraction = 0;

N_pool = 0.0;

CumulativeNitrogenDemand=0;

HourlyNitrogenDemand=0;
CumulativeNitrogenSoilUptake=0;

sunlit_LAI = shaded_LAI = 0.0;

sunlit_PFD = shaded_PFD = 0.0;

sunlit_A_net = shaded_A_net = 0;

sunlit_A_gross = shaded_A_gross = 0;

sunlit_gs=shaded_gs=0;

sowingDay = 1.0;

age = 0.0;

initInfo = info;

gasExparam = photoparam;

roots = new CRoots();

ear = new CEar();

develop = new CDevelopment(initInfo);

nodalUnit = new CNodalUnit[initInfo.genericLeafNo+10]; // create enough leaf nodes for now,


to be replaced by dynamic collection

for (int i=1; i <= PRIMORDIA; i++) // leaf[0] is a coleoptile, should start at 1

nodalUnit[i].initialize(i, develop);

nodalUnit[i].get_leaf()->set_mass(leafMass/PRIMORDIA); //assing initial mass for each


primordium

nodeNumber = i;

nodalUnit[0].initialize(0, develop);

finalNodeNumber = info.genericLeafNo;

leafArea =greenLeafArea = actualGreenLeafArea = senescentLeafArea = potentialLeafArea =


droppedLfArea = 0.0;

previousDroppedlfArea = currentDroppedLfArea = droppedLfArea = 0;


temperature = 15.0;

photosynthesis_net = photosynthesis_gross = transpiration = transpirationOld = assimilate = 0.0;

VPD = 0;

conductance = 0;

emerge_gdd = 0;

SunlitRatio = 0.0;

CPlant::~CPlant()

if (nodalUnit != NULL) delete [] nodalUnit;

if (roots != NULL) delete roots;

if (develop != NULL) delete develop;

if (ear != NULL) delete ear;

void CPlant::update(const TWeather & weather)

int TotalGrowingLeaves = 0;

int TotalDroppedLeaves = 0;

int TotalMatureLeaves = 0;

double PlantNitrogenContent;

double percentN;

if (develop->Emerged())

{
calcRed_FRedRatio(weather);

develop->update(weather);

finalNodeNumber = develop->get_youngestLeaf();

//SK: get N fraction allocated to leaves, this code is just moved from the end of the procedure,
this may be taken out to become a separate fn

double thermal_time = develop->get_GDDsum()-emerge_gdd;//record thermal time from


emergency YY

//Calculate faction of nitrogen in leaves (leaf NFraction) as a function of thermal time from
emergence

//Equation from Lindquist et al. 2007 YY

leaf_NFraction = 0.79688-0.00023747*thermal_time-
0.000000086145*thermal_time*thermal_time;

if (leaf_NFraction <=0)

leaf_NFraction = 0;//fraction of leaf n in total shoot n can't be smaller than zero.


YY

leaf_N = leaf_NFraction*this->get_N(); //calculate total nitrogen amount in the leaves


YY units are grams N in all the leaves

leaf_N_content = leaf_N/(this->greenLeafArea/10000); //SK 8/22/10: set avg greenleaf


N content before update in g/m2;

PlantNitrogenContent=this->get_N();

percentN=this->get_N()/shootMass;

double Ratio=0;
if (NitrogenRatio>0)

Ratio= percentN/NitrogenRatio;

if (!develop->Germinated())

temperature = develop->get_Tcur();

return;

else if(!develop->Emerged()) //after germination but emergence

if (!this->get_roots()->GetInitialized())

// now gets root mass from seed partitioning

this->get_roots()->SetInitialized();

this->get_roots()->import_CH2O(rootMass);

//rootMass=weather.TotalRootWeight;

temperature = develop->get_Tcur();

C_reserve = seedMass*C_content;

// C_pool +=
C_reserve*(1/20)*(1/24)*(initInfo.timeStep/60); // assume it takes 20 days to exhaust seed C reserve

C_pool = C_reserve;

for (int i = 1; i <= develop->get_LvsInitiated() ; i++)


{

if(!nodalUnit[i].isInitiated())

nodalUnit[i].initialize(i,develop);

nodeNumber = i;

else

nodalUnit[i].update(develop,weather.PredawnLWP);

// from germination to emergence, C supply is from the seed

if (nodalUnit[i].get_leaf()->isGrowing())

TotalGrowingLeaves++;

if (nodalUnit[i].get_leaf()->isDropped()) //Don't think we need this here,


can delete

TotalDroppedLeaves++;

// calculate relative area increases for leaves now that they are updated

//calcMaintRespiration(weather); // commented for now, have to test this for seedling

nodalUnit[0].get_leaf()->set_TotalGrowingLeaves(TotalGrowingLeaves);

nodalUnit[0].get_leaf()->set_TotalDroppedLeaves(TotalDroppedLeaves);
if(nodalUnit[1].get_leaf()->isInitiated() && !nodalUnit[1].get_leaf()->isAppeared())

calcMaintRespiration(weather);

C_allocation(weather);

seedMass = __max(0.0,seedMass-C_supply); // need setmass

setMass();

else if (nodalUnit[1].get_leaf()->isAppeared())

calcPerLeafRelativeAreaIncrease();

calcLeafArea();

if (develop->Emerged())

calcGasExchange(weather, gasExparam); //TODO add gas exchange


parameter structure

double c_pool1 = C_pool;

double c_pool2 = assimilate*CH2O_MW/CO2_MW; // convert from grams CO2


to grams carbohydrate (per hour per plant)

C_pool += c_pool2;

calcMaintRespiration(weather);

C_allocation(weather);

seedMass = __max(0.0,seedMass-C_supply*c_pool1/C_pool); // get seedmass


reduction only for those C from the seed

setMass();

return;

} //end not emerged

else if(!develop->Dead()) //jumps here if emergence is done


//code for above ground lifecycle

emerge_gdd = develop->get_EmergeGdd(); //if the plant emergies, then pass the


emerge_gdd value from the develop object into the plant object

for (int i = 1; i <= develop->get_LvsInitiated(); i++)

if(!nodalUnit[i].isInitiated())

nodalUnit[i].initialize(i,develop);

nodeNumber = i;

else

nodalUnit[i].get_leaf()->set_N_content(leaf_N_content); //SK 8/22/10: set leaf N


content before each nodal unit is updated

nodalUnit[i].update(develop, weather.PredawnLWP); //Pass the predawn leaf water potential


into a nodel

//to enable the model to simulate leaf expansion with the

//effect of predawn leaf water potential YY

if (nodalUnit[i].get_leaf()->isGrowing())

TotalGrowingLeaves++;

if (nodalUnit[i].get_leaf()->isDropped())

TotalDroppedLeaves++;

}
if (nodalUnit[i].get_leaf()->isMature())

TotalMatureLeaves++;

//SK 8/22/10: Why is this infor being stored in node0? this is confusing because it is supposed to be the
coleoptile

nodalUnit[0].get_leaf()->set_TotalGrowingLeaves(TotalGrowingLeaves); //TODO make


this a class variable and an CPlant:update method

nodalUnit[0].get_leaf()->set_TotalDroppedLeaves(TotalDroppedLeaves);

nodalUnit[0].get_leaf()->set_TotalMatureLeaves(TotalMatureLeaves);

if (TotalDroppedLeaves >= develop->get_totalLeaves())

develop->death.done = true;

develop->death.daytime = weather.daytime;

//SK: Below {} lumps N related codes

double shootmass = this->get_shootMass();

if (shootmass <=(100/initInfo.plantDensity)) //100 g m-2

double temp = 0.063*this->get_shootMass(); //when shoot biomass is lower


than 100 g/m2,

//the maximum [N] allowed is 6.3%;


//shoot biomass and Nitrogen are in g

if (TotalNitrogen>temp) //need to adjust demand or else there will be mass


balance problems

this->set_N(temp);

calcPerLeafRelativeAreaIncrease(); // calculate relative area increases for leaves

calcPotentialLeafArea();

calcGreenLeafArea();

calcActualGreenLeafArea();

leaf_N_content = leaf_N/(this->greenLeafArea/10000); //Calculate leaf nitrogen content


of per unit area;

calcLeafArea();

//leaf_N_content = leaf_N/this->actualGreenLeafArea; //Calculate leaf nitrogen content


of per unit area;

//defining leaf nitrogen content this way, we did not consider the difference in leaf
nitrogen content

//of sunlit and shaded leaf yet YY

// calcSenescentLeafArea();

droppedLfArea = (1-greenLeafArea/potentialLeafArea)*potentialLeafArea; //calculated


dropped leaf area YY

//SK 8/20/10: Changed it to get all non-green leaf area

currentDroppedLfArea = droppedLfArea - previousDroppedlfArea; //leaf dropped at this time step

// this->set_N((this->get_N()-(leaf_N/leafArea)*currentDroppedLfArea)); //calculated the


total amount of nitrogen after the dropped leaves take some nitrogen out
//no nitrogen remobilization from old leaf to young leaf is
considered for now YY

//SK 8/22/10: N remobilization is implicitly done by adjusting greenleaf area after determining
senesced leaf area. Currently it is assumed that all N is moved from the senesced to the active

previousDroppedlfArea = droppedLfArea;

//when less than 5% of total leaf area is green, physiological maturity is reached. SK

//see http://www.agry.purdue.edu/ext/corn/news/timeless/TopLeafDeath.html

if ((greenLeafArea <= 0.05*leafArea) && !develop->maturity.done)

develop->maturity.done = true;

develop->maturity.daytime = weather.daytime;

develop->death.done = true;

develop->death.daytime = weather.daytime;

cout << "* Physiological maturity " << develop->get_GDDsum() << " T growth: "
<< develop->get_Tgrow()

<< " green leaf %: " << greenLeafArea/leafArea*100 << endl;

if (greenLeafArea > 10)

calcGasExchange(weather, gasExparam);

calcMaintRespiration(weather);

C_allocation(weather);

if (abs(weather.time)<0.0001) //midnight activity

{
C_reserve += __max(0, C_pool);

C_pool = 0.0; //reset shorterm C_pool to zero at midnight, needs to be more


mechanistic

else //all other parts of the day

C_pool += assimilate*CH2O_MW/CO2_MW; // convert from grams CO2 to grams carbohydrate


(per hour per plant)

setMass();

void CPlant::setMass()

//TODO: allocate biomass into individual organs, currently it is allocated as a bulk to leaf, stem, and so
on

//so individual leaf doesn't have mass but the first or the last one has it all

double m = 0;

double CPlant::calcLeafArea()

double area = 0.0;

double dL = 0.0;

for (int i = 1; i <= develop->get_LvsInitiated(); i++)

dL = nodalUnit[i].get_leaf()->get_area();
area += dL ;

leafArea = area;

return area;

double CPlant::calcGreenLeafArea()

double area = 0.0;

for (int i = 1; i <= develop->get_LvsInitiated(); i++)

area += nodalUnit[i].get_leaf()->get_greenArea();

greenLeafArea = area;

return area;

double CPlant::calcActualGreenLeafArea()

double area = 0.0;

for (int i= 1; i<=develop->get_LvsInitiated(); i++)

area +=nodalUnit[i].get_leaf()->get_actualgreenArea();

actualGreenLeafArea=area;

return area;

double CPlant::calcSenescentLeafArea()
{

double area = 0.0;

for (int i = 1; i <= develop->get_LvsInitiated(); i++)

area += nodalUnit[i].get_leaf()->get_senescentArea();

senescentLeafArea = area;

return area;

double CPlant::calcPotentialLeafArea()

double area = 0.0;

for (int i = 1; i <= develop->get_LvsInitiated(); i++)

area += nodalUnit[i].get_leaf()->get_potentialArea();

potentialLeafArea = area;

return area;

double CPlant::calcPotentialLeafAreaIncrease()

double areaIncrease = 0.0;

for (int i=1; i<=develop->get_LvsInitiated(); i++)

areaIncrease +=nodalUnit[i].get_leaf()->get_potentialAreaIncrease();

potentialLeafAreaIncrease = areaIncrease;
return areaIncrease;

// calculate relative area increases for leaves now that they are updated

void CPlant::calcPerLeafRelativeAreaIncrease()

double PotentialLeafAreaIncrease=calcPotentialLeafAreaIncrease();

double RelativeLeafAreaIncrease=0;

for (int i = 1; i <= develop->get_LvsInitiated() ; i++)

RelativeLeafAreaIncrease=0.0; //Relative area increase is used for carbon


partitioning later.

if (PotentialLeafAreaIncrease>0)

RelativeLeafAreaIncrease=nodalUnit[i].get_leaf()-
>get_potentialAreaIncrease()/PotentialLeafAreaIncrease;

nodalUnit[i].get_leaf()-
>set_RelativeAreaIncrease(RelativeLeafAreaIncrease);

double CPlant::calcActiveLeafMass() //this is the total mass of active leaves that are not entirely dead
(e.g., dropped).

//It would be slightly greather than the green leaf mass because some senesced leaf area is included
until they are complely aged (dead), SK

double Mass = 0.0;


for (int i=1; i<=develop->get_LvsInitiated(); i++)

if (!nodalUnit[i].get_leaf()->isDropped())

Mass +=nodalUnit[i].get_leaf()->get_mass();

activeLeafMass=Mass;

return Mass;

double CPlant::calcDroppedLeafMass() //It has been pointed that corn leaves don't really drop and is
still likely to be attached to the node.

// this is true so a better term would be "dead" leaves (entirely senesced) but I am fine with continuing
to use dropped leaf as long as we are clear about the meaning, SK

double Mass = 0.0;

for (int i=1; i<=develop->get_LvsInitiated(); i++)

if (nodalUnit[i].get_leaf()->isDropped())

Mass+=nodalUnit[i].get_leaf()->get_mass();

droppedLeafmass=Mass;

return Mass;

double CPlant::calcTotalLeafMass()

double TotalMass = 0.0;


double Mass = 0.0;

double Area = 0.0;

for (int i=1; i<=develop->get_LvsInitiated(); i++)

Mass = nodalUnit[i].get_leaf()->get_mass();

Area = nodalUnit[i].get_leaf()->get_area();

nodalUnit[i].get_leaf()->set_SLA(Area/Mass); // Set SLA from current leaf area and mass,


SK

TotalMass += Mass;

leafMass=TotalMass; //this should equal to activeLeafMass + droppedLeafMass;

return TotalMass;

double CPlant::calcPotentialCarbondemand()

{ //this will only be used for total leaf area adjustment. If the individual leaf thing works

// out this will be deleted.

double SLA=200; //Just a mocking value for now. Need to find a more mechanistic way to
simulate change in SLA YY

// SK 8/20/10: changed it to 200 cm2/g based on data from Kim et al. (2007) EEB

double LeafMassDemand = potentialLeafAreaIncrease/SLA; //units are biomass not carbon

//PotentialCarbonDemand=carbondemand; //for now only carbon demand for leaf is calculated.

return LeafMassDemand;

void CPlant::calcGasExchange(const TWeather & weather, const TGasExSpeciesParam& photoparam)

const double tau = 0.50; // atmospheric transmittance, to be implemented as a variable => done
const double LAF = 1.37; // leaf angle factor for corn leaves, Campbell and Norman (1998)

//Make leaf width a function of growing leaves as average width will increase as the plant grows

// add it as a

const double leafwidth = 5.0; //to be calculated when implemented for individal leaves

const double atmPressure= 100.0; //kPa, to be predicted using altitude

double activeLeafRatio = greenLeafArea/leafArea;

double LAI = greenLeafArea*initInfo.plantDensity/(100.0*100.0);

CGasExchange * sunlit = new CGasExchange("Sunlit", this->leaf_N_content, photoparam);

CGasExchange * shaded = new CGasExchange("Shaded", this->leaf_N_content, photoparam);

CSolar *sun = new CSolar();

CRadTrans *light = new CRadTrans();

Timer timer;

int mm, dd, yy;

timer.caldat(weather.jday, mm, dd, yy);

int jday = timer.julday(1, 1, yy);

//int jday = 39022 + 1;

sun->SetVal(weather.jday - jday + 1, weather.time, initInfo.latitude, initInfo.longitude,


initInfo.altitude, weather.solRad);

light->SetVal(*sun, LAI, LAF);

double temp7;

temp7=sun->GetNIRTotal();

sunlit_PFD = light->Qsl();

shaded_PFD = light->Qsh();

sunlit_LAI = light->LAIsl();

shaded_LAI = light->LAIsh();
if (weather.jday == 37356 && weather.time * 24 >= 6) {

double temp10 = 1;

//Calculating transpiration and photosynthesis with stomatal controlled by leaf water potential
LeafWP Y

sunlit->SetVal(sunlit_PFD, weather.airT, weather.CO2, weather.RH,

weather.wind, atmPressure, leafwidth, weather.LeafWP,


weather.ET_supply*initInfo.plantDensity/3600/18.01/LAI);

shaded->SetVal(shaded_PFD, weather.airT, weather.CO2, weather.RH,

weather.wind, atmPressure, leafwidth, weather.LeafWP,


weather.ET_supply*initInfo.plantDensity/3600/18.01/LAI);

photosynthesis_gross = (sunlit->get_AGross()*sunlit_LAI + shaded-


>get_AGross()*shaded_LAI);//plantsPerMeterSquare units are umol CO2 m-2 ground s-1 ;

photosynthesis_net = (sunlit->get_ANet()*sunlit_LAI + shaded->get_ANet()*shaded_LAI);//

transpirationOld=transpiration; // when outputting the previous step transpiration is compared


to the current step's water uptake

transpiration=0;

if (sunlit_LAI > 0) transpiration=sunlit->get_Transpiration()*sunlit_LAI;

if (shaded_LAI > 0) transpiration+=shaded->get_Transpiration()*shaded_LAI;

transpiration=transpiration/(initInfo.plantDensity)*3600.0*18.01;//plantsPerMeterSquare units
are grams per plant per hour ;

// Units of Transpiration from sunlit->ET are mol m-2 (leaf area) s-1

// Calculation of transpiration from ET involves the conversion to gr per plant per hour

temperature = (sunlit->get_LeafTemperature()*sunlit_LAI + shaded-


>get_LeafTemperature()*shaded_LAI)/LAI;

//psi_l = (sunlit->get_psi()*sunlitLAI + shaded->get_psi()*shadedLAI)/LAI;

this->VPD = sunlit->get_VPD();

// photosynthesis_gross is umol CO2 m-2 leaf s-1

// in the following we convert to g C plant-1 per hour


assimilate =
(photosynthesis_gross*CO2_MW/1.0e6)*(60.0*initInfo.timeStep)/initInfo.plantDensity; // grams CO2
per plant per hour

photosynthesis_gross=photosynthesis_gross*CH2O_MW/1.0e6*(60.0*initInfo.timeStep)/initInfo.plantD
ensity; //grams carbo per plant per hour

photosynthesis_net=
photosynthesis_net*CH2O_MW/1.0e6*(60.0*initInfo.timeStep)/initInfo.plantDensity; //grams carbo
per plant per hour

if (sunlit_LAI != 0 && shaded_LAI !=0 && LAI !=0)

this->conductance=(sunlit->get_StomatalConductance()*sunlit_LAI+shaded-
>get_StomatalConductance()*shaded_LAI)/LAI; //average stomatal conductance Yang

if (this->conductance<0)

conductance=0;

else

this->conductance =0;

sunlit_A_net = sunlit->get_ANet();

shaded_A_net = shaded->get_ANet();

sunlit_A_gross = sunlit->get_AGross();

shaded_A_gross = shaded->get_AGross();

sunlit_gs = sunlit->get_StomatalConductance();

shaded_gs = shaded->get_StomatalConductance();
delete sunlit;

delete shaded;

delete sun;

delete light;

void CPlant::C_allocation(const TWeather & w)

double b1=2.325152587; // Normalized (0 to 1) temperature response fn parameters, Pasian and Lieth


(1990)

// Lieth and Pasian Scientifica Hortuculturae 46:109-128 1991

// parameters were fit using rose data -

double b2=0.185418876; // I'm using this because it can have broad optimal region unlike beta fn or
Arrhenius eqn

double b3=0.203535650;

const double Td = 48.6; //High temperature compensation point

// double shootPart = 0.0;

// double rootPart = 0.0;

double shootPart_real = 0.0;

double rootPart_real = 0.0;

//double leafPart = 0.0; made class variable for now to transfer, may have to do this for all

double sheathPart = 0.0;

double stalkPart = 0.0;

double reservePart = 0.0;

double huskPart = 0.0;

double cobPart = 0.0;

double grainPart = 0.0;

double flag = 0.0;


double g1=1+exp(b1-b2*w.airT);

double g2=0.0;

if (w.airT<Td) g2=1-exp(-b3*(Td-w.airT));

double tmprEffect = g2/g1;

double grofac = 1/(5*60/initInfo.timeStep);

double scale = 0.0;

double lfFact = develop->get_youngestLeaf() - develop->get_LvsAtTI();

scale = develop->get_phyllochronsFromTI() / (lfFact + develop->get_PhyllochronsToTassel());

double t1 = develop->get_progressToAnthesis();

double t2 = develop->get_phyllochronsFromTI();

C_supply = 0.0; // daily mobilization of carbon

const double C_min = 1.0;

if (C_pool > C_demand) //C_demand does not enter into equations until grain fill

C_supply = __max(C_pool*tmprEffect*grofac, 0); //CADD from Grant

C_pool -= C_supply; // C_Pool is what is left over from the carbon available for growth

else

if (abs(C_pool)<0.0001) //C_pool is zero

if (C_reserve >0)

C_supply = __max(C_reserve*tmprEffect*grofac, 0); //All the reserve is not


available
C_reserve-=C_supply; //reduce reserve pool for used carbon

else

if ((C_reserve > C_demand) && (C_demand>0))

if (C_pool <= 0.0)

C_reserve += C_pool; // C_pool negative here, add instead of subtract

C_pool = 0;

// deplete C_pool first* tmprEffect

C_supply = __max(C_demand* tmprEffect*grofac, 0);

C_reserve -= C_supply; //reserve C is used to increase grain mass

C_reserve += C_pool; // send remaining C (not enough to meet the demand) in shorterm
pool to reserve

C_pool = 0; // empty the C_pool

else

if (C_pool > (maintRespiration))

C_supply = maintRespiration;

C_pool -= __max(C_supply,0);

else

if (C_reserve > (maintRespiration))

C_supply = maintRespiration;
C_reserve -= __max(C_supply,0);

C_reserve += C_pool; // send remaining C (not enough to meet the demand) in shorterm
pool to reserve

C_pool = 0; // empty the C_pool

else

C_reserve += C_pool;

C_pool = 0.0;

C_supply = __min(C_reserve, maintRespiration);

double Fraction = __min(0.925, 0.50 + 0.50*scale); // eq 3 in Grant

// const double convFactor = 1/1.43; // equivalent Yg, Goudriaan and van Laar (1994)

double Yg = 0.750; // synthesis efficiency, ranges between 0.7 to 0.76 for corn, see Loomis and
Amthor (1999), Grant (1989), McCree (1988)

// this is the same as (PhyllochronsSinceTI - lvsAtTI/(totalLeaves - lvsAtTI)

shootPart = __max(0,Yg*(Fraction*(C_supply-maintRespiration))); // gCH2O partitioned to shoot

rootPart = __max(0,Yg*((1-Fraction)*(C_supply-maintRespiration))); // gCH2O partitioned to


roots

if (!develop->Germinated())

return;

else if (!develop->TasselInitiated())

if (w.pcrs>rootPart_old) // if in time step t-1, the value of pcrs is higher than that of pcrl

// give a half of carbon from shoot needed to meet root demand? SK


shootPart_real = __max(0, shootPart-(w.pcrs-rootPart_old)); //than take the difference
between the two out of the carbon allocation to shoot at time step t

rootPart_real = rootPart+ (w.pcrs-rootPart_old); //and put that amount of carbon into


carbon allocation to root at time step t.

else

C_pool_root +=__max(0,rootPart_old-w.pcrs);

shootPart_real = shootPart;

rootPart_real = rootPart-__max(0,rootPart_old-w.pcrs); //subtract out carbon sent to


the root pool

rootPart_old = rootPart;

leafPart = shootPart_real*0.725;

sheathPart = shootPart_real*0.275;

stalkPart = 0.0;

reservePart = 0.0;

huskPart = 0.0;

cobPart = 0.0;

grainPart = 0.0;

} //end not tasselinitiated

else if (!develop->GrainFillBegan())

//C partitioning - this is already calculated above, should remove as it is repition

shootPart = __max(0,Yg*((Fraction)*(C_supply-maintRespiration))); // gCH2O partitioned to


shoot
//Everything is partitioned to the shoot after grain filling begins. I removed Fraction from the
Equation above

rootPart = __max(0,Yg*((1-Fraction)*(C_supply-maintRespiration))); // gCH2O partitioned to


roots

// rootPart=0.0;

if (w.pcrs>rootPart_old)

shootPart_real = __max(0, shootPart-(w.pcrs-rootPart_old));

rootPart_real = rootPart+(w.pcrs-rootPart_old);

else

C_pool_root +=__max(0,rootPart_old-w.pcrs);

shootPart_real = shootPart;

rootPart_real = rootPart-__max(0,rootPart_old-w.pcrs); //subtract out carbon sent to


the root pool

rootPart_old = rootPart;

leafPart = shootPart_real*__max(0.725 - 0.775*scale,0);

sheathPart = shootPart_real*__max(0.275- 0.225*scale,0);

// allocate shootPart into components

if (scale <=0.85)

stalkPart = shootPart_real*1.10*scale;

else

reservePart = shootPart_real*__max(2.33-0.6*exp(scale), 0);


}

if (scale <= 1.0)

huskPart = shootPart_real*__max(exp(-7.75+6.60*scale),0);

else

huskPart = shootPart_real*__max(1.0 - 0.675*scale,0);

if (scale <= 1.125)

cobPart = shootPart_real*exp(-8.4+7.0*scale);

else

cobPart = shootPart_real*0.625;

//give reserve part what is left over, right now it is too high

if (reservePart>0)

double sum=cobPart+huskPart+leafPart+stalkPart+sheathPart;

reservePart=__max(0,shootPart-sum);

} //end not grainfill begin

else if (!develop->Dead())//no acutally kernel No. is calculated here ? Yang, 6/22/2003

// here only grain and root dry matter increases root should be zero but it is small now.
const int maxKernelNo = 800; // assumed maximum kerner number per ear

double maxKernelFillRate = 0.012*(initInfo.timeStep/(24*60)); //

//max kernel filling rate = 0.012g Kernel-1 day-1, Grant


(1989)

C_demand = maxKernelNo*maxKernelFillRate*tmprEffect*C_content; //dt added c_content

shootPart = __max(0,Yg*(C_supply-maintRespiration)); // gCH2O partitioned to shoot

rootPart=0.0; // no more partitioning to root during grain fill

if (w.pcrs>rootPart_old)

shootPart_real = __max(0, shootPart-(w.pcrs-rootPart_old));

rootPart_real = rootPart+ (w.pcrs-rootPart_old);

else

C_pool_root +=__max(0,rootPart_old-w.pcrs);

shootPart_real = shootPart;

rootPart_real = rootPart-__max(0,rootPart_old-w.pcrs); //subtract out carbon sent to


the root pool

flag = 0;

rootPart_old = rootPart;

grainPart = shootPart_real*1.0;

else
{

double stemPart = sheathPart + stalkPart; //TODO: sheath and stalk haven't been separated in this
model

//reservePart needs to be added later

double earPart = grainPart + cobPart + huskPart;

double sum= stemPart+earPart + leafPart;

// here we can allocate leaf part among leaves

CLeaf* leaf;

// Partition carbon to leaves relative to growth rate

// now need to find increment of Carbo to add to each leaf

// update leaf's mass

// first find the number of growing leaves. This is saved as a variable in the [0] nodal unit

double LeafPartSum=leafPart;

double PCarboDemandPerLeaf;

for (int i = develop->get_LvsInitiated(); i >=1 ; i--)

//need to find demand first

leaf= nodalUnit[i].get_leaf();

double totLA;

if (nodalUnit[i].isInitiated()&& LeafPartSum >= 0.0)

{
if (!leaf->isDead())

totLA = calcPotentialLeafArea();

PCarboDemandPerLeaf=__max(0.0, leaf-
>get_potentialArea()/totLA*leafPart); //Adjusting C allocation based on leaf size if not aging.

// doing it based on current


growth rate is more mechanistic

//but seems to have issue now.


To revisit. SK

if (LeafPartSum >= PCarboDemandPerLeaf)

leaf->import_CH2O(PCarboDemandPerLeaf);

LeafPartSum-=PCarboDemandPerLeaf;

else

leaf->import_CH2O(__min(LeafPartSum,
PCarboDemandPerLeaf));

LeafPartSum=0.0;

//cout <<"leafPartSum: " <<LeafPartSum <<"LeafPart: " <<leafPart<<endl;


this->get_roots()->set_ActualCarboIncrement(rootPart_real);

this->get_nodalUnit()->get_stem()->import_CH2O(stemPart);

// this->get_nodalUnit()->get_leaf()->import_CH2O(leafPart);

this->C_reserve += reservePart; // everything is carbohydrate now

// before emergence root weight has been initialized. Just dump this carbon for now.

if (develop->Emerged()) this->get_roots()->import_CH2O(rootPart_real);

this->get_ear()->import_CH2O(earPart);

double partSum = stemPart + earPart + leafPart; // checking the balance if sums up to shootPart

//cout <<C_pool << " " << C_ReserveLeaf <<endl;

void CPlant::calcMaintRespiration(const TWeather & w)

// based on McCree's paradigm, See McCree(1988), Amthor (2000), Goudriaan and van Laar (1994)

// units very important here, be explicit whether dealing with gC, gCH2O, or gCO2

const double Q10 = 2.0; // typical Q10 value for respiration, Loomis and Amthor (1999) Crop Sci
39:1584-1596 - could try 1.8

double dt = initInfo.timeStep/(24*60);

// const double maintCoeff = 0.015; // gCH2O g-1DM day-1 at 20C for young plants, Goudriaan and
van Laar (1994) Wageningen textbook p 54, 60-61

const double maintCoeff = 0.018;

double agefn = (greenLeafArea+1.0)/(leafArea+1.0); // as more leaves senesce maint cost should


go down, added 1 to both denom and numer to avoid division by zero.

//no maint cost for dead materials but needs to be more mechanistic, SK

//agefn=1.0;

double q10fn = pow(Q10,(w.airT - 20.0)/10); // should be soil temperature or leaf or


combination of use as --> (-stemMass*stem_coef) to reduce

// total mass. Implement later after testing


double stem_coef = min(1.0,droppedLeafmass / leafMass) ;

maintRespiration = q10fn*maintCoeff*agefn*((mass-droppedLeafmass))*dt;// gCH2O dt-1,


agefn effect removed. 11/17/14. SK.

void CPlant::calcRed_FRedRatio(const TWeather &weather)

// this function calculates an estimate of the Red to Far red light ratio from sunlit and shaded ET. This

// ration is used to estimate the effects of plant density on leaf expansion and LAI.

// A daily mean ratio is calculated. We use a 3 parameter sigmoid function to model the effect

//double Xo=0.43, B=0.05, A=1.2;

//double Xo=0.6, B=0.13, A=2.0; original

//double Xo=0.9, B=0.43, A=2.0;

double Xo=0.85, B=0.65, A=2.0;

double dt=initInfo.timeStep/(24.0*60);

double C2_effectTemp;

//First set counter to 0 if it is the beginning of the day.

if (abs(weather.time)<0.0001)

{// have to rename C2_effect to Light_effect

//Zhu et al. Journal of Experimental Botany, Vol. 65, No. 2, pp. 641–653, 2014

C2_effectTemp=exp(-(SunlitRatio-Xo)/B);

C2_effect=__min(1.0,A/(1.0+C2_effectTemp));

develop->set_shadeEffect(C2_effect);

SunlitRatio=0.0;

else

if (sunlit_LAI/(sunlit_LAI+shaded_LAI)>0.05) // calculate from emergence

{
SunlitRatio+=sunlit_LAI/(sunlit_LAI+shaded_LAI)*dt;

else SunlitRatio+=1.0*dt;

void CPlant::writeNote(const TWeather & w)

ostringstream oStr;

string s = "";

if (FLOAT_EQ(develop->germination.daytime,w.daytime)){s = "Germinated";}

if (FLOAT_EQ(develop->emergence.daytime,w.daytime)){s = "Emergence";}

if (FLOAT_EQ(develop->tasselInitiation.daytime,w.daytime)){s = "Tassel Initiation";}

if (FLOAT_EQ(develop->anthesis.daytime, w.daytime)){s = "Anthesis";}

if (FLOAT_EQ(develop->silking.daytime, w.daytime)){s = "Silking";}

if (FLOAT_EQ(develop->beginGrainFill.daytime, w.daytime)){s = "Begin grain filling";}

if (FLOAT_EQ(develop->maturity.daytime,w.daytime)){s = "Begin grain filling";}

if (s != "")

oStr << s << " "<< w.jday;

}
//note.swap(oStr.str());

note = oStr.str();

Organ.h

#pragma once

#ifndef _ORGAN_H_

#define _ORGAN_H_

#include "weather.h"

#include "thermaltime.h"

#include "initinfo.h"

#include "development.h"

#define CO2_MW 44.0098

#define C_MW 12.011

#define CH2O_MW 30.03

struct TElement

TElement() {CHO = 0; water = 0; nitrogen=0;}

double CHO;

double water;

double nitrogen;

};

class COrgan

public:
COrgan();

COrgan(const TInitInfo&);

virtual ~COrgan();

virtual void import_CH2O(double);// import CHO from the reserve, virtual common metabolic
reserve

virtual void import_N(double); // import N from the reserve

// virtual double export_CHO(); //export CHO to the reserve, virtual common metabolic reserve

// virtual double export_N();

// virtual void grow(double);

virtual void respire();

//dt added for temporary transfer of carbon to 2DSOIL

double get_PotentialCarboIncrement() {return PotentialCarboIncrement;}

double get_ActualCarboIncrement() {return ActualCarboIncrement;}

double get_age() {return age;}

double get_physAge() {return physAge;}

double get_mass() {return mass;}

double get_CH2O() {return CH2O;}

double get_N() {return N;}

double get_Nconc() {return N/mass;}

// TElement * get_element() {return element;}

double get_temperature() {return temperature;}

double get_longevity() {return longevity;}

double get_growthDuration() {return growthDuration;}

TInitInfo get_initInfo() {return initInfo;}

CThermalTime * get_GDD() {return GDD;}

//dt added for temporary 2DSOIL compatability

// dt added this function to return carbon for roots


// this will change later as we move the root stuff into the plant model

// from 2DSOIL

virtual void set_PotentialCarboIncrement(double x) {PotentialCarboIncrement = x;}

virtual void set_ActualCarboIncrement(double x) {ActualCarboIncrement = x;}

virtual void set_age(double x) {age = x;}

virtual void set_physAge(double x) {physAge=x;}

virtual void set_mass(double x) {mass=x;set_CH2O(x*(C_conc/C_MW*CH2O_MW));}

virtual void set_CH2O(double x) {CH2O=x;}

virtual void set_N(double x) {N=x;}

// virtual void set_element(TElement * x) {element=x;}

virtual void set_temperature(double x) {temperature=x;}

void set_longevity(double x) {longevity=x;}

virtual void set_growthDuration(double x) {growthDuration=x;}

virtual void initialize();

virtual void update();

private:

COrgan(const COrgan&);

TInitInfo initInfo;

TElement * element;

CThermalTime * GDD;

double age; // chronological age of an organ, days

double physAge; // physiological age accouting for temperature effect (in reference to
endGrowth and lifeSpan, days)

double mass; // biomass, g

double CH2O; //glucose, MW = 180.18 / 6 = 30.03 g

double C_conc, N; //nitrogen content, mg

double temperature; // organ temperature, C


double longevity; // life expectancy of an organ in days at optimal temperature (fastest growing
temp), days

double growthDuration; // physiological days to reach the end of growth (both cell division and
expansion) at optimal temperature, days

double PotentialCarboIncrement; //Carbon Allocation to roots or leaves for time increment

double ActualCarboIncrement; //Carbon Allocation to roots or leaves for time increment gr C


for roots, gr carbo dt-1

};

#endif

Organ.cpp

#include "stdafx.h"

#include "organ.h"

COrgan::COrgan()

age = physAge = mass = 0;

CH2O=N=0;

C_conc = 0.40;

temperature = 25;

growthDuration=10;

longevity=50;

GDD = NULL;

GDD = new CThermalTime();

//dt added for temporary transfer of carbon to 2DSOIL

PotentialCarboIncrement = 0;

ActualCarboIncrement=0;
}

COrgan::COrgan(const TInitInfo& info)

initInfo = info;

temperature=25.0;

CH2O=N=0;

C_conc = 0.40;

age = physAge = mass = 0;

growthDuration=10;

longevity=50;

GDD = NULL;

GDD = new CThermalTime();

PotentialCarboIncrement = 0; //Amount of potential carbo needed for new growth

ActualCarboIncrement=0; // Amount of actual carbo available for growth

COrgan::~COrgan()

if (GDD != NULL) delete GDD;

void COrgan::initialize()

if (GDD == NULL) GDD = new CThermalTime();

GDD->initialize(initInfo.timeStep);

void COrgan::update()

{
GDD->add(temperature);

age = GDD->get_actualAge();

physAge=GDD->get_sum();

mass = CH2O/CH2O_MW*C_MW/C_conc; // C content as carbohydrate

void COrgan::import_CH2O(double dCH2O)

CH2O += dCH2O;

mass = CH2O/CH2O_MW*C_MW/C_conc; // C content as carbohydrate

void COrgan::import_N(double dN)

N += dN;

void COrgan::respire()

// this needs to be worked on

// currently not used at all

double Rm = 0.02; //maintenance respiration

double Ka = 0.1; //growth respiration

CH2O -= Ka*CH2O + Rm*CH2O;

Leaf.h
#pragma once

#ifndef _LEAF_H_

#define _LEAF_H_

#include "organ.h"

#include "weather.h"

#include "development.h"

class CLeaf: public COrgan

//leaf blade, call it "leaf" for convenience

friend class CSheath;

public:

CLeaf(int rank, CDevelopment * dv); // take current leaf rank and total leaf number to calculate
potentialArea

~CLeaf();

bool isInitiated() {return initiated;}

bool isAppeared() {return appeared;}

bool isGrowing() {return growing;}

bool isMature() {return mature;}

bool isAging() {return aging;}

bool isDead() {return dead;}

bool isDropped() {return dropped;}

double get_area(){return area;}

double get_greenArea() {return greenArea;}

double get_senescentArea() {return senescentArea;}

double get_potentialArea(){return PotentialArea;}

double get_length() {return length;}


double get_SLA() {return SLA;}

double get_N_content() {return N_content;}

double get_N_Effect() { return N_effect; }

double get_potentialAreaIncrease () {return PotentialAreaIncrease;}

double get_RelativeAreaIncrease() {return RelativeAreaIncrease;}

double get_actualgreenArea() {return actualgreenArea;}

double get_droppedArea () {return droppedArea;}

double get_GDD2mature() {return GDD2mature;}

double get_Elongation_Age() { return elongAge; }

int get_TotalGrowingLeaves() {return TotalGrowingLeaves;}

int get_TotalDroppedLeaves() {return TotalDroppedLeaves;}

int get_TotalMatureLeaves() { return TotalMatureLeaves; }

int get_Rank() {return rank;}

void initialize(CDevelopment * dv);

void set_area(double x) {area=x;}

void set_length(double x) {length=x;}

void set_SLA(double x) {SLA=x;}

void set_TotalGrowingLeaves(int x) {TotalGrowingLeaves=x;}

void set_TotalDroppedLeaves(int x) {TotalDroppedLeaves=x;}

void set_TotalMatureLeaves(int x) {TotalMatureLeaves=x; }

double GTI(double);

void set_RelativeAreaIncrease(double x) {RelativeAreaIncrease=x;}

void update(CDevelopment *, double pdlwp);

//void elongate(CDevelopment *, double pdlwp);

void expand(CDevelopment *, double pdlwp);


// void senescence(CDevelopment *);

void senescence(CDevelopment *, double pdlwp);

void set_N_content(double x) {N_content=x;}

void set_GDD2mature(double x) {GDD2mature=x;}

void calc_dimensions(CDevelopment * dv);

void calcLongevity(double pdlwp);

double LWPeffect(double predawn_psil, double threshold);

private:

CLeaf(const CLeaf&);

bool initiated, appeared, growing, mature, aging, dead, dropped;

int rank;

int TotalGrowingLeaves, TotalDroppedLeaves, TotalMatureLeaves;

double PotentialArea; // potential leaf area

double FullyExpandedArea;

double PotentialAreaIncrease, old_leaf;//potential leaf area increase without temperature or


water limitation YY

double RelativeAreaIncrease; //Area increase of this leaf relative to all other leaves. Used to
partition carbon

double area, unstressedArea; // actual leaf area and accumulated potential area

double greenArea, actualgreenArea;//actualgreenArea is the green area of leaf growing under


carbon limitation

//SK 8/22/10: There appears to be no distinction between these two variables in the code.

double senescentArea, droppedArea;

double length;

double width;

double SLA;

double plastochrons, GDD2mature; // Fournier and Andrieu (1998), used for delay between
initiation and elongation
double phase1Delay, growthDuration, elongAge, elongRate, maxElongRate, seneAge, activeAge,
seneDuration;

double stayGreen, stayGreenDuration;

double ptnLength, ptnWidth,LM_min;

double actualArea;

double actualLength,actualwidth; //actual length and width of leaf under both drought stress
and carbon limitation

bool first; //indicates if this is the first time we call the elongate method;

double N_content;

double N_effect;

double WLRATIO, A_LW;

double T_peak, Tb_Leaf;

/*

SK 8/22/10: Leaf N content in mg/m2

*/

};

#endif

Leaf.cpp

#include "stdafx.h"

#include "leaf.h"

#include "weather.h"

#include "initinfo.h"

#include <cmath>

#include <algorithm>

#define MINUTESPERDAY (24*60);


using namespace std;

/* LWPeffect adjusts leaf growth for water stress. The functin is defined here. The parameter
psi_threshold_bars has two values, one for leaf growth

* and one for sescence.

*/

CLeaf::CLeaf(int n, CDevelopment * dv): COrgan()

rank = n;

length=width = area = PotentialArea = plastochrons = GDD2mature = 0.0;

SLA = 200.0; // temporary for now - it should vary by age. Value comes from some of Soo's work

PotentialAreaIncrease = 0;

RelativeAreaIncrease=0;

FullyExpandedArea=0;

TotalDroppedLeaves=0;

TotalMatureLeaves = 0;

actualArea = actualgreenArea=greenArea = senescentArea = droppedArea = unstressedArea=0;

actualLength=actualwidth=0;

initiated = appeared = growing = mature = aging = dead = dropped = false;

phase1Delay = growthDuration=stayGreenDuration = seneDuration = 0.0;

ptnLength=ptnWidth = 0.0;

maxElongRate = 12.0; //max elongation rate (cm per day) at optipmal temperature (Topt: 31C
with Tbase = 9.8C using 0.564 cm/dd rate from Fournier 1998 paper above

elongAge = 0.0; //physiological age during expansion phase

seneAge = 0.0; //age during senscence phase

activeAge = 0.0; // age during active phase after fully expansion before senescence

old_leaf = 0; //record leaf area in the last time step

N_content = 3.0; //no N stress


WLRATIO = 0.106; // leaf lamina width to length ratio

A_LW = 0.75; // leaf area coeff with respect to L*W

stayGreen = dv->get_stayGreen();

LM_min = dv->get_LM_min();

// T_peak is the optimal growth temperature at which the potential leaf size determined in
calc_mophology achieved. Similar concept to fig 3 of Fournier and Andreiu (1998)

T_peak = 18.7, Tb_Leaf = 8.0;

CLeaf::~CLeaf() {}

void CLeaf::initialize (CDevelopment * dv)

// set potential leaf area of the current rank based on generic leaf no, see Fournier and Andrieu (1998),
Birch et al., (1998)

// Routine below to calculate potentialArea will repeat in CLeaf::update to get actual leaf area after the
inductive phase to any additional leaves developed before tassel initiation, SK 1-20-12

COrgan::initialize();

calc_dimensions(dv);

initiated = true;

// growing=true; // DT

first=true;

stayGreenDuration = stayGreen*growthDuration;

seneDuration = growthDuration;

// uncomment for debugging

#if _DEBUG
std::cout << " GDDay " << dv->get_GDDsum() << " leaf " << dv-
>get_LvsInitiated() << " " << rank << " totalLeaves " << dv->get_totalLeaves()

<< " potential area " << PotentialArea << std::endl;

#endif

void CLeaf::calc_dimensions(CDevelopment *dv)

const double k = 24.0;

double totalLeaves = dv->get_totalLeaves(); //todo: should be a plant parameter not leaf

double L_max = (sqrt(LM_min*LM_min + k*(totalLeaves - dv->get_initInfo().genericLeafNo)));

/* LM_min is a length characteristic of the longest leaf,in Fournier and


Andrieu 1998, it was 90 cm

LA_max is a fn of leaf no (Birch et al, 1998 fig 4) with largest reported value
near 1000cm2.

This is implemented as lfno_effect below, SK

LM_min of 115cm gives LA of largest leaf 1050cm2 when totalLeaves are 25


and Nt=Ng, SK 1-20-12

Without lfno_effect, it can be set to 97cm for the largest leaf area to be at
750 cm2 with Nt ~= Ng (Lmax*Wmax*0.75)

based on Muchow, Sinclair, & Bennet (1990), SK 1-18-2012

Eventually, this needs to be a cultivar parameter and included in input file,


SK 1-18-12

The unit of k is cm^2 (Fournier and Andrieu 1998 Pg239). YY

L_max is the length of


the largest leaf when grown at T_peak. Here we assume LM_min is determined at growing Topt with
minmal (generic) leaf no, SK 8/2011

If this routine runs before TI, totalLeaves = genericLeafNo, and needs to be run
with each update until TI and total leaves are finalized, SK

*/

double n_m, a, b;

int hrank=rank;
n_m = 5.93 + 0.33*totalLeaves; // the rank of the largest leaf. YY (need to adjust here for the
maximum leaf size as a function of plant pop -DT 8/12/2015)

a = -10.61 + 0.25*totalLeaves;

b = -5.99 + 0.27*totalLeaves;

if (rank > int(n_m))

hrank = rank - 1;

//equation 7 in Fournier and Andrieu (1998). YY

// Attempt to increase area of leaves above max leaf

ptnLength = L_max*exp(a/2*pow(hrank/n_m-1,2)+b/2*pow(hrank/n_m-1,3)); //*dv-


>get_shadeEffect()

//equa 8(b)(Actually eqn 6? - eqn 8 deals with leaf age -


DT)

//in Fournier and Andrieu(1998). YY

growthDuration = ptnLength/maxElongRate; // shortest possible linear phase duration in


physiological time (days instead of GDD) modeified form of equa 8(a)Fournier and Andrieu(1998)

double W_max = L_max*WLRATIO;//Fournier and Andrieu(1998) Pg242 YY

double LA_max = L_max*W_max*A_LW; // daughtry and hollinger (1984) Fournier and


Andrieu(1998) Pg242 YY

double lfno_effect = max(0.5, min(1.0, exp(-1.17+0.047*totalLeaves))); // Fig 4 of Birch et al. (1998)

PotentialArea=lfno_effect*LA_max*exp(a*pow(hrank/n_m-1,2)+b*pow(hrank/n_m-
1,3)); //equa 6. Fournier and Andrieu(1998) multiplied by Birch et al. (1998) leaf no effect

//LA_max the area of the largest leaf

} //PotentialArea potential final area of a leaf with rank "n". YY

}
void CLeaf::update(CDevelopment * dv, double PredawnLWP)

COrgan::set_temperature(dv->get_Tcur());

COrgan::update();

// calc_dimensions(dv);

expand(dv, PredawnLWP);

senescence(dv, PredawnLWP);

greenArea = max(0.0, area-senescentArea);

void CLeaf::expand(CDevelopment * dv, double PredawnLWP)

//leaf expansion rate based on a determinate sigmoid function by Yin et al. (2003)

double CriticalNitrogen;

CriticalNitrogen= __max(0.25,this->N_content);

N_effect= __max(0.0, (2 / (1 + exp(-2.9*(N_content - 0.25))) - 1));

N_effect = __min(1, N_effect);

const double psi_threshold_bars = -0.8657;

double water_effect=dv->LWPeffect(PredawnLWP, psi_threshold_bars);

double shade_effect= dv->get_shadeEffect();

double T = dv->get_Tcur();

double T_gro = dv->get_Tgrow();


double T_effect_size = max(0.0,
(T_gro-Tb_Leaf)/(T_peak-Tb_Leaf)*exp(1.0-(T_gro-Tb_Leaf)/(T_peak-Tb_Leaf)));

//final leaf size is adjusted by growth temperature determining cell size during elongation

//final leaf

// See Kim et al. (2012) Agro J. for more information on how this relationship has been
derermined basned on multiple studies and is applicable across environments

double dD = dv->get_initInfo().timeStep/MINUTESPERDAY; // time step as day fraction

if (dv->get_LvsAppeared() >= rank && !appeared)

appeared = true;

// growthDuration - end of growth period, time to maturity

double growthDuration_half = growthDuration/2; // max. growth rate assumed to be half of


growthDuration

//+(1-C2_effect) proposed C2_effect for elongAge, add to 1.0

if (appeared && !mature)

elongAge += dv->beta_fn(T, 1.0,dv->get_T_Opt(), dv->get_T_ceil())*dD; // Todo:


implement Parent and Tardieu (2011, 2012) approach for leaf elongation in response to T and VPD, and
normalized at 20C, SK, Nov 2012

// elongAge indicates where it is now along the elongation stage or duration. duration is
determined by totallengh/maxElongRate which gives the shortest duration to reach full elongation in
the unit of days.

elongAge = __min(growthDuration, elongAge);

// area = __max(0.0, water_effect*T_effect_size*PotentialArea*(1.0 + (t_e-elongAge)/(t_e-


growthDuration_half))*pow(elongAge/t_e, (t_e/(t_e-growthDuration_half))));

// this following is a decay function for the effect of age on growth


double maxExpansionRate = T_effect_size*PotentialArea*(2*growthDuration-
growthDuration_half)/(growthDuration*(growthDuration- growthDuration_half))*

pow(growthDuration_half /growthDuration, growthDuration_half


/(growthDuration- growthDuration_half));

// The following equation is a beta function. The analogs are - growthDuration is T_Ceil,
growthDuration_half is optimum temp (T_OPT),

// elongage is temperature

PotentialAreaIncrease =__max(0.0,maxExpansionRate*__max(0.0, (growthDuration-


elongAge)/(growthDuration- growthDuration_half)*

pow(elongAge/growthDuration_half,growthDuration_half/(growthDuration-
growthDuration_half)))*dD);

//potential leaf area increase without any limitations

//C2_effect = 1.0; // place holder

double dA = PotentialAreaIncrease; // growth temperature effect is included in


determining potential area

//water_effect=1.0;

//N_effect=1.0;

//shade_effect = 1.0;

area += dA*__min(water_effect,N_effect)*shade_effect;

unstressedArea+=dA;

if (unstressedArea >= PotentialArea || elongAge >= growthDuration)

mature = true;

set_GDD2mature (get_physAge());

growing = false;

else growing = true;

return;
}

void CLeaf::senescence(CDevelopment * dv, double PredawnLWP)

// DT added N_effect 3/29/2016

double dD = dv->get_initInfo().timeStep/MINUTESPERDAY;

double T = (double)dv->get_Tcur();

double T_opt = dv->get_T_Opt();

double T_grow = dv->get_Tgrow();

double N_effect = __max(0.0, (2/(1+exp(-2.9*(N_content-0.25)))-1)); //SK 8/20/10: as in Sinclair and


Horie, 1989 Crop sciences, N availability index scaled between 0 and 1 based on

// This assumes 0.25mg/m2 minimum N required, and below this the value is 0.0.

N_effect = __min(1, N_effect);

const double psi_threshold_bars = -4.0; //threshold predawn leaf water potential (in bars)
below which water stress triggers senescence, needs to be substantiated with lit or exp evidence, SK

// This is the water potential at which considerable reduction in leaf growth takes place in corn,
sunflower, and soybean in Boyear (1970)

double water_effect=dv->LWPeffect(PredawnLWP, psi_threshold_bars);

double shade_effect = dv->get_shadeEffect();

//water_effect = 1;

//N_effect = 1;

double seneDuration_half; // max. growth rate assumed to be half of growthDuration

double Q10 = 2.0;

double q10fn = pow(Q10,(T - T_opt)/10);

// Assumes physiological time for senescence is the same as that for growth though this may be
adjusted by stayGreen trait

// a peaked fn like beta fn not used here because aging should accelerate with
increasing T not slowing down at very high T like growth,
// instead a q10 fn normalized to be 1 at T_opt is used, this means above T_opt aging
accelerates.

double scale = 1.0; // scale for reduction in leaf lifespan and aging rate

if (!mature && !aging && !dead)

stayGreenDuration = stayGreen*growthDuration;

seneDuration = growthDuration*1.2; // end of growth period, time to maturity. Assume


senesence is faster than growth

// changed this from 0.75 to 1.2 not sure if I will keep it.

else if (mature && !aging && !dead)

activeAge += q10fn*dD;

scale = 0.5;

stayGreenDuration = __max(0.0, stayGreenDuration - scale*(1.0 -


__min(water_effect,N_effect))*dD);

//One day of cumulative severe water stress (i.e., water_effect = 0.0 around -4MPa)
would result in a reduction of leaf lifespan in relation staygreeness and growthDuration, SK

//if scale is 1.0, one day of severe water stress shortens one day of stayGreenDuration

if (activeAge >= stayGreenDuration)

activeAge = stayGreenDuration;

aging = true;

else if (aging && !dead)

seneAge += q10fn*dD;
scale = 0.5;

//if scale is 0.5, one day of severe water stress at predawn shortens one half day of agingDuration

seneDuration = __max(0.0, seneDuration - scale*(1.0 -


__min(water_effect,N_effect))*dD);

seneDuration_half = seneDuration/2;

seneAge = __min(seneDuration, seneAge);

// double maxRate =
area*(2*seneDuration-seneDuration_half)/(seneDuration*(seneDuration-
seneDuration_half))*pow(seneDuration_half/seneDuration,seneDuration_half/(seneDuration-
seneDuration_half));

// double nonstressAgingRate = __max(0.0,maxRate*__max(0.0,


(seneDuration-seneAge)/(seneDuration-
seneDuration_half)*pow(seneAge/seneDuration_half,seneDuration_half/(seneDuration-
seneDuration_half))));

// double dA = nonstressAgingRate*dD;

//Leaf senescence accelerates with drought and heat. see


http://www.agry.purdue.edu/ext/corn/news/timeless/TopLeafDeath.html

// senescentArea += dA;

senescentArea = __max(0.0, this->area*(1.0 + (seneDuration-seneAge)/(seneDuration-


seneDuration_half))*

pow(seneAge/seneDuration, (seneDuration/(seneDuration-
seneDuration_half))));

if (senescentArea >= area || seneAge >= seneDuration)

senescentArea = area; dead = true;

else dead = false;

else if (dead && get_physAge() >= get_GDD2mature())

dropped = true;
}

return;

//create a function which simulates the reducing in leaf expansion rate

//when predawn leaf water potential decreases. Parameterization of rf_psil

//and rf_sensitivity are done with the data from Boyer (1970) and Tanguilig et al (1987) YY

double CLeaf::LWPeffect(double predawn_psi_bars, double threshold)

//DT Oct 10, 2012 changed this so it was not as sensitive to stress near -0.5 lwp

//SK Sept 16, 2014 recalibrated/rescaled parameter estimates in Yang's paper. The scale of
Boyer data wasn't set correctly

//sensitivity = 1.92, LeafWPhalf = -1.86, the sensitivity parameter may be raised by 0.3 to 0.5 to
make it less sensitivy at high LWP, SK

double psi_f=-1.4251; // -1.0, was -1.4251 later changed to -2.3;

double s_f=0.4258; // was 0.4258 0.5;

double psi_th = threshold; // threshold wp below which stress effect shows up

double effect;

effect=__min(1.0, (1+exp(psi_f*s_f))/(1+exp(s_f*(psi_f-(predawn_psi_bars-psi_th)))));

if (effect >1 ) effect=1;

return effect;

Intialize.h

#pragma once

#ifndef _INITINFO_H_
#define _INITINFO_H_

#define MINUTESPERDAY (24*60);

#ifndef FLOAT_EQ

#define EPSILON 0.001 // floating point comparison tolerance

#define FLOAT_EQ(x,v) (((v - EPSILON) < x) && (x <( v + EPSILON)))

#define MINUTESPERDAY (24*60);

#endif

struct TInitInfo

public:

TInitInfo()

char description[255] = "\0";

char cultivar[255]="\0";

GDD_rating = 1331;

genericLeafNo=15;

latitude = 38.0; longitude = 0.0; altitude = 50.0;

sowingDay = 150;

beginDay = 1; endDay = 365;

year = 2004;

timeStep=5.0;

plantDensity = 8.0;

CO2 = 370.0;

Rmax_LIR=.0978;

Rmax_LTAR = 0.53;

DayLengthSensitive=true;

PhyllochronsToSilk=8;

PhyllochronsToTassel = 1;
stayGreen = 4.5;

LM_min = 125.0;

char description[255];

char cultivar[255];

int GDD_rating; // GDD or GTI rating of the cv, see Stewart 1999 for conversion between MRMR
and other ratings

int genericLeafNo; // leaf number at the end of juvenile phase independent of environmental
ques of leaf initiation

double plantDensity;

double latitude, longitude, altitude;

int sowingDay, beginDay, endDay;

double CO2;

int year;

double timeStep;

bool DayLengthSensitive; //1 if daylength sensitive

double Rmax_LIR, Rmax_LTAR; // Maximum Leaf tip initiation and appearance rates

double stayGreen; // staygreen trait of the hybrid (originally 4.5)

double LM_min; //Length of largest leaf

double PhyllochronsToSilk; //number of phyllochrons from tassel initiation for 75% silking.

double PhyllochronsToTassel; // number of phyllochrons past tassel initiation when tassels are
fully emerged. (not input yet)

//todo these above 2 variables are also in development - need to remove them from there.

//check units

};

#endif

Intialize.cpp
#include "stdafx.h"

#include "initinfo.h"

Development.h

#pragma once

#ifndef _DEVELOPMENT_H_

#define _DEVELOPMENT_H_

#include "weather.h"

#include "initinfo.h"

#include <iostream>

#include <string>

using namespace std;

enum EPhase

Seed, Juvenile, Inductive, preSilking, Reproductive, Maturity

};

struct TEvent

public:

TEvent() {daytime = 0.0; done=false;}

double daytime;

bool done;

};

class CDevelopment
{

public:

CDevelopment(const TInitInfo&);

~CDevelopment();

void set_shadeEffect(double x) {shadeEffect=x;}

double beta_fn(double t, double R_max, double t_m, double t_e);

double calcGTI(double, bool);

double calcGDD(double);

int update(const TWeather&);

double LWPeffect(double predawn_psi_bars, double threshold);

TInitInfo get_initInfo() {return initInfo;}

int get_youngestLeaf() {return youngestLeaf;}

int get_totalLeaves() {return (int) totLeafNo;} // removed +1 that was previously here to count
only those leaves fully initiated, SK 1-19-12

//double get_totalLeaves() {return totLeafNo;} // take the value as double , SK 1-19-12

double get_phyllochronsFromTI() {return phyllochronsFromTI;}

double get_phyllochronsToSilk() { return PhyllochronsToSilk; }

double get_progressToAnthesis() { return progressToAnthesis; }

double get_PhyllochronsToTassel() { return PhyllochronsToTassel; }

double get_stayGreen() { return stayGreen; }

double get_LM_min() { return LM_min; }

double get_LvsAtTI() {return LvsAtTI;}

double get_LvsInitiated(){return LvsInitiated;}

double get_LvsAppeared(){return LvsAppeared;} // tip appreance rate is most conservative


across cultivars and temperature regimes, persoanl comm with Dr. Tollenaar

double get_GDDsum() {return GDDsum;}

double get_EmergeGdd() {return emerge_gdd;}

double get_dGDD() {return dGDD;}

double get_dt() {return dt;} // added 08-16-11, SK


double get_Tcur() {return T_cur;}

double get_Tavg() {return T_avg;}

double get_Rmax_LIR() {return Rmax_LIR/dt;}

double get_Rmax_LTAR() {return Rmax_LTAR/dt;}

double get_T_Opt() {return T_opt;}

double get_Tbase() {return T_base;}

double get_T_ceil() {return T_ceil;}

double get_Tgrow() {return T_grow;}

double get_shadeEffect() {return shadeEffect;}

double get_T_Air() { return T_air; }

bool Germinated() {return germination.done;}

bool Emerged() {return emergence.done;}

bool TasselInitiated() {return tasselInitiation.done;}

bool Tasseled() { return tasselFull.done;}

bool Flowered() {return anthesis.done;}

bool Silked() {return silking.done;}

bool GrainFillBegan() {return beginGrainFill.done;}

bool Matured() {return maturity.done;}

bool Dead() {return death.done;} // when all leaves are senescend and dead, the whole-plant is
pronounced dead. SK

string getNote() {return note;}

TEvent germination;

TEvent emergence;

TEvent tasselInitiation;

TEvent tasselFull;

TEvent anthesis;
TEvent silking;

TEvent beginGrainFill;

TEvent maturity;

TEvent death;

private:

CDevelopment(const CDevelopment&); // supressing shallow copy constructor

int GDD_rating;

double dGDD; // delta GDD

double dt, steps; // timestep, step counter, SK

double GDDsum; // cumulative GDD from sowing with Tbase= 8.0 and Topt = 34 as in CERES

double emerge_gdd; //thermal time need for a corn plant to emerge YY 4/2/09

double GDDgrain; // cumulative GDD from silking

double dGTI; // delta GTI

double GTIsum; // cumulative GTI, Stewart et al (1998), equivalent to GDD of Tbase = 10 and
Topt 30

double Rmax_LIR, Rmax_LTAR, Rmax_Germination, Rmax_Emergence, stayGreen, LM_min;

bool DayLengthSensitive; //True if Day Length Sensitive

double T_base, T_opt, T_ceil, T_cur, T_avg, T_grow, T_grow_sum, T_ind, T_air; // T_grow: mean
temperature of the growing season from day 1 up to now, SK

double totLeafNo, LvsToInduce, juvLeafNo, LvsAtTI, phyllochronsFromTI; //number of total,


juvenile (genetic coeff) lvs, and lvs appeared at tassel initiation

double P2; //photoperiod sensitivity as used in CERES-Maize

double GerminationRate, EmergenceRate, LvsInitiated, LvsAppeared, LvsExpanded;

double progressToAnthesis, progressToTasselEmerg, inductionPeriod;

int initLeafNo, youngestLeaf, curLeafNo, inductions;

double PhyllochronsToSilk; // number of phyllochrons past tassel initiation when silking takes
place

double PhyllochronsToTassel; // number of phyllochrons past tassel initiation when tassels are
fully emerged
double shadeEffect; //effect of shade with respect to R/FR, see JXB (2014) Zhu, 8-13-2015, SK

string note;

TInitInfo initInfo;

};

#endif

Development.cpp

#include "stdafx.h"

#include <cmath>

#include "development.h"

#include <iostream>

#include <string>

#include <algorithm>

//#using <mscorlib.dll>

using namespace std;

CDevelopment::CDevelopment(const TInitInfo& info)

{ // DT -8/16/2016 reorganized some of these variables to make the initialization more consistent.

// removed SetParms method since it duplicated a much of what is done in the constructor. I
moved

// the code from SetParms to here.

dt = info.timeStep / MINUTESPERDAY; //converting minute to day decimal, 1= a day

LvsAppeared = LvsExpanded = progressToAnthesis = 0; progressToTasselEmerg = 0;

GerminationRate = EmergenceRate = LvsInitiated = 0;

GDDsum = GDDgrain = dGDD = phyllochronsFromTI = 0;


GTIsum = dGTI = 0;

emerge_gdd = 0;

stayGreen = info.stayGreen;

LM_min = info.LM_min;

Rmax_LIR = info.Rmax_LIR;

Rmax_LTAR = info.Rmax_LTAR;

DayLengthSensitive = info.DayLengthSensitive;

PhyllochronsToSilk = info.PhyllochronsToSilk;

PhyllochronsToTassel = info.PhyllochronsToTassel;

totLeafNo = juvLeafNo = info.genericLeafNo;

GDD_rating = info.GDD_rating;

totLeafNo = juvLeafNo = info.genericLeafNo;

Rmax_Germination = Rmax_Emergence = 0;

initLeafNo = youngestLeaf = 5;

curLeafNo = 1;

LvsAtTI = 1;

LvsInitiated = initLeafNo;

LvsToInduce = 0.0;

inductionPeriod = 0.0;

inductions = 0;

T_grow_sum = steps = 0.0;

T_grow = T_ind = -99;

shadeEffect = 1.0;
Rmax_Germination = 0.45*dt; // max rate of germination per day, assume it takes two day at 31
C, needs to be replaced

// Itabari et al., 1993 Expl Agric. 29:351-


364 has info on this

Rmax_Emergence = 0.2388*dt;

Rmax_LTAR = Rmax_LTAR*dt; // Kim et al. (2007); Kim and Reddy (2004), 0.581 from Yan and
Hunt (1999), equivalent phyllochron in CERES

//cdt changed from 0.524 to test for colorado


data used 0.374

Rmax_LIR = Rmax_LIR*dt; // best fit of K and W (1983), Kim and Reddy (2004) originally 0.978
(for colorado tried 0.558

T_base = 8.0;

T_opt = 32.1; // These Topt and Tceil values from Kim et al. (2007), also see Kim and Reddy
(2004), Yan and Hunt (1999), SK

T_ceil = 43.7;

P2 = 0.5;

// Save for later

initInfo = info;

CDevelopment::~CDevelopment(void)

int CDevelopment::update(const TWeather& wthr)

double Jday = wthr.jday;


T_cur = max(0., wthr.airT);

T_air = wthr.airT;

if (LvsAppeared < 9) T_cur = max(0., wthr.soilT);

double addLeafPhotoPeriod, addLeafTemperature, addLeafTotal;

// double dt = initInfo.timeStep/(24*60); //converting minute to day decimal, 1= a day

if (!germination.done)

//TODO: implement germination rate model of temperature.

// for now assume it germinates immidiately after sowing

GerminationRate += beta_fn(T_cur, Rmax_Germination, T_opt, T_ceil);

if (GerminationRate >= 0.5)

germination.done = true;

germination.daytime = wthr.daytime;

// initialize T_grow

T_grow = T_cur;

T_grow_sum = T_cur;

cout << "* Germinated: GDDsum " << GDDsum << " time step (min): " << dt*(24
* 60) << endl;

else // if (germination.done)

T_grow_sum += T_cur;

steps++;

T_grow = T_grow_sum / steps; // mean growing season temperature since germination,


SK 1-19-12
if (!emergence.done)

EmergenceRate += beta_fn(T_cur, Rmax_Emergence, T_opt, T_ceil);

if (EmergenceRate >= 1.0)

emergence.done = true;

emergence.daytime = wthr.daytime;

cout << "* Emergence: GDDsum " << GDDsum << " Growing season T "
<< T_grow << endl;

GDDsum = 0.0; //reset GDDsum from emergernce, SK

emerge_gdd = GDDsum; //gdd at emergence YY 4/2/09

// corn->LvsAppeared = 1.0;

//LvsAppeared = 2;

if (!tasselInitiation.done && emergence.done)

double temp1 = LvsInitiated;

LvsInitiated += beta_fn(T_cur, Rmax_LIR, T_opt, T_ceil);

if (LvsInitiated < temp1)

int iii = 1;

curLeafNo = (int)LvsInitiated;

if (LvsInitiated >= juvLeafNo)

// inductive phase begins after juvenile stage and ends with tassel
initiation
{

//Equation 4 in Grant 1989 Ag. J. (81)

//dt 12/11/2012 broke the equation in two to separate temperature and


daylenght effects

if (T_ind == -99) { T_ind = T_grow; } //mean temperature during


induction period

addLeafTemperature = __max(0.0, (13.6 - 1.89*T_ind +


0.081*T_ind*T_ind - 0.001*T_ind*T_ind*T_ind));

addLeafPhotoPeriod = 0.0;

if (DayLengthSensitive)

addLeafPhotoPeriod = __max(0.0, 0.1*(juvLeafNo -


10.0)*(wthr.dayLength - 12.5));

addLeafTotal = (addLeafTemperature + addLeafPhotoPeriod);

//addLeaf = __max(0, 0.1*(juvLeafNo-10.0)*(wthr.dayLength-12.5) +


(13.9-1.89*T_cur+0.0795*T_cur*T_cur - 0.001*T_cur*T_cur*T_cur)); //Equation 4 in Grant 1989 Ag. J.
(81)

// effect of photoperiod and temperature on leaf no. used as Grant


(1989)

// Added back the temperature effect on leaf number and revised the
algorithm to accumulate addLeafNo to totLeafNo.

// Changed to respond to mean growing season temperature upto this


point.

// This has little mechanistic basis. Needs improvements. SK 1-19-12

LvsToInduce = (LvsToInduce*inductions + addLeafTotal) / (inductions +


1);

T_ind = (T_ind*inductions + T_cur) / (inductions + 1);

inductions++;

inductionPeriod += dt;
// totLeafNo = juvLeafNo + addedLvs/inductionPeriod; //get a
mean value over this period

// LvsAtTI = LvsInitiated; //Should be LvsInitiated. Already


confirmed with Soo. 7/27/2006

// uncomment the following for debugging

// cout << "* Inductive phase: " << LvsInitiated << " " << totLeafNo << "
" << juvLeafNo << " " << addedLvs/inductionPeriod << endl;

double actualAddedLvs = LvsInitiated - juvLeafNo;

if (actualAddedLvs >= LvsToInduce)

youngestLeaf = totLeafNo = (int)LvsInitiated;

curLeafNo = youngestLeaf;

tasselInitiation.done = true;

tasselInitiation.daytime = wthr.daytime;

LvsInitiated = youngestLeaf;

LvsAtTI = LvsAppeared;

cout << "* Tassel initiation: GDDsum " << GDDsum << " Growing
season T " << T_grow << endl;

else if (tasselInitiation.done)

phyllochronsFromTI += beta_fn(T_cur, Rmax_LTAR, T_opt, T_ceil); // to be used


for C partitoining time scaling, see Plant.cpp

if ((LvsAppeared < (int)LvsInitiated))

LvsAppeared += beta_fn(T_cur, Rmax_LTAR, T_opt, T_ceil);


if (LvsAppeared >= (int)LvsInitiated)

LvsAppeared = LvsInitiated;

//DT Sep 21, 2016 added a variable to indicate tasseling is done at 1 phyllocrhon from
the last leaf appearing

if (tasselInitiation.done && (LvsAppeared >= (int)LvsInitiated))

//todo should move !silking.done to the if statement for silking

// if (((tasselInitiation.done) && (!silking.done) && !DayLengthSensitive)

// || ((LvsAppeared >= (int) LvsInitiated) && (!silking.done) &&


DayLengthSensitive))

if (!tasselFull.done)

progressToTasselEmerg += beta_fn(T_cur, Rmax_LTAR, T_opt, T_ceil); //


Assume full tassel emergence occurs at total tip appeared + 1.5 phyllochrons

if ((progressToTasselEmerg >= PhyllochronsToTassel) && (!tasselFull.done))


//was 3

tasselFull.done = true;

tasselFull.daytime = wthr.daytime;

cout << "* Tassel fully emerged: GDDsum " << GDDsum << " Growing
season T " << T_grow << endl;

if (!silking.done && tasselFull.done)

progressToAnthesis += beta_fn(T_cur, Rmax_LTAR, T_opt, T_ceil); //


Assume 75% Silking occurs at total tip appeared + 3 phyllochrons

if ((progressToAnthesis >= PhyllochronsToSilk) && !silking.done) //was 3

{
silking.done = true;

silking.daytime = wthr.daytime;

cout << "* Silking: GDDsum " << GDDsum << " Growing season T " <<
T_grow << endl;

if (silking.done)

GDDgrain += calcGDD(T_cur)*dt;

if (GDDgrain >= 170 && (!beginGrainFill.done)) // where is this number '170'


from? SK

//Todo: GTI was found more accurate for grain filling stage, See Thijs
phenolog paper (2014)

beginGrainFill.done = true;

beginGrainFill.daytime = wthr.daytime;

cout << "* Grain filling begins: GDDsum " << GDDsum << " Growing
season T " << T_grow << endl;

// if (!maturity.done)

dGTI = (calcGTI(T_cur, silking.done)*dt);

dGDD = calcGDD(T_cur)*dt;

GDDsum += dGDD;

GTIsum += dGTI;

// if (GDDsum >= GDD_rating && (!maturity.done))


// {

// maturity.done = true;

// maturity.daytime = wthr.daytime;

// cout << "* Matured: GDDsum " << GDDsum << " Growing season T " << T_grow
<< endl;

// }

return 0;

double CDevelopment::beta_fn(double t, double R_max, double t_o, double t_c)

// beta function, See Yin et al. (1995), Ag For Meteorol., Yan and Hunt (1999) AnnBot, SK

double f, g, alpha;

const double t_b = 0.0, beta = 1.0;

if (t <= t_b || t >= t_c) return 0.0;

if (t_c <= t_o || t_o <= t_b) return 0.0;

f = (t - t_b) / (t_o - t_b);

g = (t_c - t) / (t_c - t_o);

alpha = beta*(t_o - t_b) / (t_c - t_o);

return R_max*pow(f, alpha)*pow(g, beta);

}
double CDevelopment::calcGTI(double T_avg, bool Silked)

// General Thermal Index, Stewart et al. (1998)

//Phenological temperature response of maize. Agron. J. 90: 73–79.

// double b1 = 0.0432;

double b1 = 0.011178;

double T_opt = 32.2;

//return b1*T_avg*T_avg*(1-0.6667*T_avg/T_opt);

//if (Silked = false) return b1*T_avg*T_avg*(1-0.6667*T_avg/T_opt);

//else

return 5.358 + 0.011178*T_avg*T_avg;

double CDevelopment::calcGDD(double T_avg)

// GDD model with base 8. See Birch et al. (2003) Eu J Agron

//double const T_base = 8.0;

double const T_base = 8.0;

double const T_opt = 34.0;

//double const T_opt = 30.0;

return min(T_avg, T_opt) - T_base;

// if (Silked = false) return b1*T_avg*T_avg*(1-0.6667*T_avg/T_opt);

// else return 5.358 + 0.011178*T_avg*T_avg;

//create a function which simulates the reducing in leaf expansion rate

//when predawn leaf water potential decreases. Parameterization of rf_psil

//and rf_sensitivity are done with the data from Boyer (1970) and Tanguilig et al (1987) YY

double CDevelopment::LWPeffect(double predawn_psi_bars, double threshold)


{

//DT Oct 10, 2012 changed this so it was not as sensitive to stress near -0.5 lwp

//SK Sept 16, 2014 recalibrated/rescaled parameter estimates in Yang's paper. The scale of
Boyer data wasn't set correctly

//sensitivity = 1.92, LeafWPhalf = -1.86, the sensitivity parameter may be raised by 0.3 to 0.5 to
make it less sensitivy at high LWP, SK

double psi_f = -1.4251; // -1.0, was -1.4251 later changed to -2.3;

double s_f = 0.4258; // was 0.4258 0.5;

double psi_th = threshold; // threshold wp below which stress effect shows up

double effect;

effect = __min(1.0, (1 + exp(psi_f*s_f)) / (1 + exp(s_f*(psi_f - (predawn_psi_bars - psi_th)))));

if (effect >1) effect = 1;

return effect;

Crop.h

// The following ifdef block is the standard way of creating macros which make exporting

// from a DLL simpler. All files within this DLL are compiled with the PLANT_EXPORTS

// symbol defined on the command line. this symbol should not be defined on any project

// that uses this DLL. This way any other project whose source files include this file see

// PLANT_API functions as being imported from a DLL, whereas this DLL sees symbols

// defined with this macro as being exported.

// See creating a pocket PC dll using C++ article for information

// on how to do this

#ifdef _WIN32

#ifdef MYPLANT_EXPORTS

#define PLANT_API __declspec(dllexport)

#else
#define PLANT_API __declspec(dllexport)

#endif

#else

#define PLANT_API

#endif

#include "initinfo.h"

// Other global definitions go here

double p_VMAX,

PopSlab,

Trel, // relative time since emergence

Period, // one hour of time

Emergence,Crop,pool; // Day of year plant emerged

TInitInfo initInfo; //Initial input data stored here

double old_shoot_weightPerM2 = 0; //Declare and initilize a variable to save

//the above-ground biomass in each time step YY

bool bEmergence=false; //boolean for emergence occurring

int ModNum=0, //Module Number

errPlant=1 // error number to return to 2DSOIL

const double CO2=370, Press=98;

const double cm2perm2=10000;

double TotalTillerLeafArea, TotalLeafArea, TillerLeafArea,

MainStemLeafArea;
double netPhotosyn,respiration, GPhotosyn;

int nNodes, nLeaves, ntLeaves, nTiller;

const double maxAge=47;

double WaterUptake=0; // hourly water uptake from 2dsoil

double NitrogenUptake=0; // nitrogen uptake value from 2dsoil accumulated between time
steps mg/plant

double NitrogenUptakeOld=0;

double CumulativeNitrogenDemand=0.0; //grams plant-1

double CumulativeActualNFromSoil=0.0; //grams plant-1

double U_N, U_M, U_P, U_D; //U_N maximum observed N uptake rate (g N m-2 ground d-1)
(Lindquist et al, 2007) YY

double d = 0.075; //d: shape coefficient in the logistic function to simulate cumulative N uptake
(Equation 9 in Lindquist et al. 2007)

double q_n = 0.032; //q_n the maximum ratio of daily N uptake to measured daily growth rate
(g N g-1) (Lindquist et al., 2007)

double N_min = 4.10; //maximum nitrogen concentration for C4 species: 4.1% (Lindquist et al.
2007)

double N_shape = 0.5; //shape coefficient in calculation of nitrogen concentration in relation to


up-ground biomass (equation 4 Lindquist et al, 2007) YY

//Common Structures defined here

double massIncrease=0; //increase in shoot biomass in each time step YY

double shoot_weightPerM2=0;

double SLNmin = 0.5; //SLNmin: Base specific leaf nitrogen content

double CurrentNUptakeError=0;

double CumulativeNUptakeError=0;

double ET_diff=0; //for debugging

float LAMDAS, LAMDAC;

const int NumNPD=4000, NumElD=3500, NumBPD=600, NSeepD = 2,


NumSPD= 30, NumSD =10, NDrainD=2, NumDR=30,

NumGD = 3, NumPlD=100,

NMatD=15, NumModD=20, MBandD=15,

NumSurfDatD=3+NumGD+NumSD;

int compare(const void *arg1, const void *arg2); //Function for comparing two numbers in a
sort routine

#pragma pack(2)

struct ShootCommon{

double PCRL,PCRQ,PCRS,HourlyCarboUsed,ET_demand,LCAI,Cover,Convr;

float MaxRootDepth,Shade,Height,LAI,AWUPS,nitroDemand;

float xBStem,yBStem,SGT,PSIM,

LAREAT,PopRow,RowSp,RowAng,PopArea,CEC,

EORSCS,AWUPSS,SolRad,Total_Eor,

Total_Pcrs,SIncrSink,Psild,

OsmFac, EOMult,LeafWP, NDemandError, CumulativeNDemandError,

TotalRootWeight, InitialRootCarbo,

ConstI[2],constK[2], Cmin0[2];

int isGerminated, isEmerged;

};

// DT Made IR float (from int)

//Weather

struct WeathCommon{

int MSW1,MSW2,MSW3,MSW4,MSW5,MSW6,MSW7;

float BSOLAR,ETCORR,BTEMP,ATEMP,ERAIN,BWIND,BIR,WINDA, IRAV;

int JDAY, NCD,JDLAST;

float CLDFAC,DEL[24],RINT[24],RNS,RNC,RAIN,IR;

float WIND,CO2,TDUSK,TDUSKY,CPREC[NumSD],TAIR[24],VPD[24],ROUGH,

RADINT[24],WATTSM[24],DIFINT[24],ROWINC[24];
float CLOUD, SHADOW[24],DIFWAT[24],DIRINT[24];

float WATACT, WATRAT, WATPOT, RNLU;

int NumF[40],NumFP;

float hFur[40],QF;

int IFUR;

float GAIR[NumGD],PG,LATUDE,Longitude, Altitude,

RI,par[24],parint[24],daylng;

float AutoIrrigAmt;

int AutoIrrigateF;

};

//grid

struct GridCommon{

int NumNP, NumEl, IJ, KAT, MBand,Nmat, KX[4][NumElD];

float x[NumNPD], y[NumNPD], Area[NumElD], nodeArea[NumNPD];

};

//nodal

struct NodeCommon{

int NumSol,NumG,ListN[NumNPD],ListNE[NumNPD],MatNumN[NumNPD];

float hNew[NumNPD], ThNew[NumNPD], Vx[NumNPD], Vz[NumNPD],

Q[NumNPD], Conc[NumSD][NumNPD], g[NumGD][NumNPD],

Tmpr[NumNPD],Con[NumNPD],TcsXX[NumNPD],RO[NumNPD],

hNew_org[NumNPD], QAct[NumNPD],ThetaAvail, ThetaFull;

float ThAvail[NumNPD],ThFull[NMatD];

bool lOrt;

};

//elements
struct ElementCommon{

int MatNumE[NumElD];

float Sink[NumNPD], cSink[NumSD][NumNPD],

gSink[NumGD][NumNPD],tSink[NumNPD],

RTWT[NumElD],RUTDEN[NumNPD];

};

//boundary

struct BoundaryCommon{

int NumBP, NSurf, NVarBW,NVarBS,NVarBT,NVarBG,

NumSurfDat, NSeep, NSP[NSeepD], NP[NumSPD][NSeepD],

NDrain,NDR[NDrainD],NDNumDR[NDrainD],

KXB[NumBPD];

int CodeW[NumNPD],CodeS[NumNPD],CodeT[NumNPD],

CodeG[NumNPD],PCodeW[NumNPD];

float Width[NumBPD], VarBW[3][NumBPD],

VarBS[NumSD][NumBPD],

VarBT[4][NumBPD], VarBG[3][NumGD][NumBPD],EO,Tpot;

};

// Time

struct TimeCommon{

double tNext[NumModD],dtMx[4],Time, Step, dtOpt,dtMin,

dMul1, dMul2,tTDB[4],Tfin,tAtm;

float Tinit;

int lInput,Iter;

int DailyOutput, HourlyOutput,RunFlag, DailyWeather, HourlyWeather;

int beginDay, sowingDay, endDay, OutputSoilNo, OutPutSoilYes, Year;


int iTime,iDawn,iDusk;

double TimeStep;

};

//modules

struct ModuleCommon{

int NumMod,Movers[4], NShoot;

};

struct ErrorCommon{

int errPlant;

};

struct FileCommon{

double starter;

char WeatherFile[132], TimeFile[132], BiologyFile[132],

ClimateFile[132], NitrogenFile[132], SoluteFile[132],

SoilFile[132],

ManagementFile[132], DripFile[132],

WaterFile[132], WaterBoundaryFile[132],

GraphicsFile[132], InitialsFile[132],VarietyFile[132],

NodeGraphics[132],ElemGraphics[132],NodeGeomFile[132],

GeometryFile[132], SurfaceGraphics[132],

FluxGraphics[132], MasssBalanceFile[132],MassBalanceFileOut[132],

LeafFileIn[132], RunFile[132], MassBalanceRunoffFileOut[132];

};
#pragma pack()

#ifdef __cplusplus

extern "C" {

#endif

// Your exported function headers go here

#ifdef _WIN32

PLANT_API void _stdcall CROP(struct ShootCommon *, WeathCommon *,

#else

PLANT_API void crop(struct ShootCommon *, WeathCommon *,

#endif

GridCommon *, NodeCommon *,

ElementCommon *,
BoundaryCommon *,

TimeCommon *,
ModuleCommon *,

FileCommon *);

#ifdef __cplusplus

#endif
Crop.cpp

#include "stdafx.h"

#include "crop.h"

#include "controller.h"

#include "time.h"

#include <cstdlib>

#include <cstdio>

#include <fstream>

#include <string>

#include <iomanip>

using namespace std;

#include <cmath>

#define endl "\n"

#define comma ","

int compare(const void *arg1, const void *arg2)

/* Compare all of both strings: */

if (*(double*)arg1 > *(double*)arg2) return 1;

else if (*(double*)arg1 < *(double*)arg2) return -1;

return 0;

};

#ifdef _WIN32

void _stdcall CROP(struct

#else

void crop(struct

#endif

ShootCommon *SHOOTR,
WeathCommon *Weather,

GridCommon *grid_public,

NodeCommon *node_public,

ElementCommon *ele_public,

BoundaryCommon *bound_public,

TimeCommon *time_public,

ModuleCommon *module_public,

FileCommon *file_public

// I think pLeaf can be local since the leaf array holds the list

// and the pointers are not lost between invocations of the procedure

//First read input data if start of simulation

char* Buffer=(char*)calloc(256,sizeof(char));

static CController* pSC; //SK, declare as static to ensure only one copy is instantiated during
2DSOIL execution

// varFile contains variety information, GraphicFile holds output,LeafFile holds individual leaves

if (time_public->lInput==1) //SK, initialiing crop module

// Parse the file names from the FORTRAN strings passed from 2dsoil

//KY looks like GNU Fortran handle linebreak differently, making filename detection unusable

//KY this new macro based on std::string should work on both platforms with smaller code

#define SETSTR(s, n) std::string s(n, sizeof(n)); s.erase(s.find_last_not_of(" \n\r\t")+1);


SETSTR(varFile, file_public->VarietyFile);

SETSTR(GraphicFile, file_public->GraphicsFile);

SETSTR(LeafFile, file_public->LeafFileIn);

// DT 10/07/2014 moved reading of initials file to soil model (Init.for)

initInfo.plantDensity=SHOOTR->PopArea;

initInfo.latitude=Weather->LATUDE;

initInfo.longitude=Weather->Longitude;

initInfo.altitude=Weather->Altitude;

initInfo.year=time_public->Year;

initInfo.sowingDay=time_public->sowingDay;

initInfo.beginDay=time_public->beginDay;

initInfo.endDay=time_public->endDay;

initInfo.timeStep=time_public->TimeStep;

time_public->iTime=1;

SHOOTR->LCAI=0.0;

SHOOTR->LAREAT=0.0;

SHOOTR->Height=0.0;

//dt change for debugging purposes

SHOOTR->Convr=1.0; // was 0.1 or 0.38 should be 1.0 as dry matter is used in all cases

SHOOTR->AWUPS = 0.0; //initialize AWUPS, AWUPS_old and LeafWP in 2DSOIL Yang


8/15/06

SHOOTR->LeafWP = -0.5;

SHOOTR->PCRS = 0.0;

SHOOTR->ET_demand = 0.0;

SHOOTR->HourlyCarboUsed=0; //it is also zero'd upon initialization in 2dsoil

Period=time_public->TimeStep/60/24; // period should be in days, input in minutes

PopSlab=SHOOTR->PopRow/100.0*SHOOTR->EOMult;
SHOOTR->isEmerged=SHOOTR->isEmerged=0;

/*These two lines show the relationships among some of the space variables.

--PopSlab=SHOOTR->PopRow/100*SHOOTR->RowSp*SHOOTR->EOMult;

--PlantDensity=SHOOTR->PopRow*100.0/SHOOTR->RowSp;

*/

// A new plant model object is created and initialized (calls initialize function) here

// ***************************************************************************

pSC = new CController(varFile.c_str(), GraphicFile.c_str(), LeafFile.c_str(), initInfo);


//Consider putting file names in initInfo

// ***************************************************************************

NitrogenUptake=pSC->getPlant()->get_N()*PopSlab; //initialize nitrogen uptake with


what is already in the plant

//SK 8/20/10: this is curious but OK

SHOOTR->NDemandError=0;

SHOOTR->CumulativeNDemandError=0;

time_public->RunFlag=1;

module_public->NumMod=module_public->NumMod+1 ;

ModNum=module_public->NumMod;

time_public->tNext[ModNum-1]=pSC->getSowingDay();

} // end if

} //end initialization

//SK: Running the crop module step by step


if (module_public->NShoot>0)

WaterUptake=WaterUptake+SHOOTR->AWUPS*time_public->Step; //g water per slab


taken up in an hour

NitrogenUptake=NitrogenUptake+SHOOTR->SIncrSink/1.0e6; //Cumulative N (mass, g


(plant slab)-1) in this time step

// Note that SIncrSink has been multiplied by time step in the solute uptake routing

// the 1000 scales from ug to mg.

if(fabs(time_public->Time-time_public->tNext[ModNum-1])< fabs(0.001*time_public-
>Step))

//If the sowing date has come and there is not plant, let the program know so other
calculations are not done

if((module_public->NShoot == 0) && (fabs(time_public->Time-pSC-


>getSowingDay()))<0.001)

module_public->NShoot=1;

//DT added soil temperature from around seed area (right now it is taken as
default - 20)

// find soil temperature of surface 5 cm

double Es;

//WaterUptake=WaterUptake*24; //Water uptake rate per day per slab

// calculate error for demand and actual uptake, if negative, demand is greater
then uptake

CurrentNUptakeError=NitrogenUptake/PopSlab-pSC->getPlant()-
>get_CumulativeNitrogenDemand();

CumulativeNUptakeError+=CurrentNUptakeError;
TWeather wthr;

wthr.HourlyOutput= time_public->HourlyOutput;

wthr.DailyOutput=time_public->DailyOutput;

wthr.jday = Weather->JDAY;

wthr.time = time_public->Time-Weather->JDAY;

wthr.CO2 = Weather->CO2;

if (Weather->CO2<=0)

wthr.CO2=initInfo.CO2; //Can set CO2 in initials for specific


simulations where CO2 is constant

wthr.airT = Weather->TAIR[time_public-> iTime-1];

wthr.PFD = Weather->par[time_public->iTime-1]*4.6; // conversion


from PAR in Wm-2 to umol s-1 m-2

wthr.solRad = Weather->WATTSM[time_public->iTime-1]; //one hour


Total Radiation incident at soil surface (Wm-2)

Es = (0.611*exp(17.502*wthr.airT/(240.97+wthr.airT))); // saturated
vapor pressure at airT

wthr.RH = (1-(Weather->VPD[time_public->iTime-1]/Es))*100.0; //
relative humidity in percent

wthr.rain = Weather->RINT[time_public->iTime-1];

wthr.wind = Weather->WIND*(1000.0/3600.0); // conversion from km


hr-1 to m s-1

wthr.dayLength = Weather->daylng;

wthr.LeafWP = SHOOTR->LeafWP/10; //and leaf water potential


information into MAIZESIM Yang 8/15/06 MPa

wthr.pcrl=SHOOTR->PCRL/PopSlab/24.;

wthr.pcrq=SHOOTR->PCRQ/PopSlab/24.;

if (abs(wthr.time-0.2083)<0.0001) //If time is 5 am, then pass the leaf


water potential (the predawn leaf water potential)
{

wthr.PredawnLWP=SHOOTR->LeafWP; //Here LeafWP is in bar.


Since the LWPeffect in leaf.cpp uses leaf water potential

//in bar, so here PredawnLWP is in bar, instead of being scaled


to MPa. YY

wthr.pcrs = SHOOTR->HourlyCarboUsed/PopSlab; //original

SHOOTR->HourlyCarboUsed=0.0;

wthr.TotalRootWeight=SHOOTR->TotalRootWeight/PopSlab;

wthr.MaxRootDepth=SHOOTR->MaxRootDepth;

// Available water is cm per profile - should be divided by PopSlab

wthr.ThetaAvail=node_public->ThetaAvail/PopSlab;

if (NitrogenUptake >0 )

double nuptake = NitrogenUptake/PopSlab; //uptake is now


grams per plant

double leafloss = pSC->getPlant()->get_droppedLfArea();

double nloss = leafloss*SLNmin;

pSC->getPlant()->set_N(NitrogenUptake/PopSlab); // Units are converted from g slab-1 to g


plant -1 YY
}

if (SHOOTR->LAI==0)

wthr.ET_supply=0;

else

wthr.ET_supply = WaterUptake/(SHOOTR->EOMult*SHOOTR-
>PopRow)*100; //units area gr per plant per hour

ET_diff=wthr.ET_supply*24-SHOOTR->ET_demand;

int end=grid_public->NumNP;

double soilT=0, maxY=0;

double soilMP[400];

int count=0;

double LowerBoundary=5;

// first find top of grid

for (int i=0; i<end; i++)

if (grid_public->y[i] > maxY) maxY=grid_public->y[i];

LowerBoundary=maxY-LowerBoundary;

// now find average temperature in layer between surface and lower boundary

for (int i=0; i<end; i++)

{
if ( grid_public->y[i]>=LowerBoundary)

soilT=soilT+node_public->Tmpr[i];

soilMP[i] = node_public->hNew[i];

count++;

wthr.soilT=soilT/count;

std::qsort((void *)soilMP, count, sizeof(double),compare);

wthr.SoilMP_med = soilMP[count / 2];

int ier = pSC->getErrStatus();

if ( ier == 0 )

ier = pSC->run(wthr); //Pass weather into the "run" function

//of the controller pSC YY

// Assumes that germination takes place about halfway through the sowing date

if (wthr.time >= 0.49 && wthr.time <= 0.51)

if (!pSC->getPlant()->get_develop()->Germinated())

{
cout << "Germinating:" <<wthr.jday <<endl;

// The remaining groups of code handle carbon and nitrogen exchange between 2dsoil and maizsim

if ((pSC->getPlant()->get_develop()->Germinated()) && (!pSC->getPlant()->get_develop()-


>Emerged()))

// begin root growth at germination

if (!SHOOTR->isGerminated)

SHOOTR->isGerminated=1;

SHOOTR->InitialRootCarbo=pSC->getPlant()-
>get_rootMass()*PopSlab; // get initial root mass to distribute over initial nodes

if (pSC->getPlant()->get_develop()->Emerged())

// pass appropriate data to 2DSOIL file structures

if (!SHOOTR->isEmerged) SHOOTR->isEmerged=1;

if ((pSC->getPlant()->get_C_pool_root()>0) && (pSC->getPlant()-


>get_rootPart()<0.00001)) // this assures the pool is only used at night

// minimizes
complexity when p// since we have to add leftover carbo from pcrq to the shoot

{
SHOOTR->PCRL=(pSC->getPlant()->get_rootPart()
+pool)*24*PopSlab;

pSC->getPlant()->set_C_pool_root(0.0);

else

SHOOTR->PCRL=(pSC->getPlant()->get_rootPart())*24*PopSlab;

bool gf=pSC->getPlant()->get_develop()->GrainFillBegan();

SHOOTR->PCRQ=(pSC->getPlant()->get_rootPart()+ (pSC->getPlant()-
>get_shootPart()))*24*PopSlab;

if (gf)

SHOOTR->PCRQ=(pSC->getPlant()->get_rootPart())*24*PopSlab +

(0.75*pSC->getPlant()->get_shootPart())*24*PopSlab;

wthr.pcrl= SHOOTR->PCRL/PopSlab/24;

wthr.pcrq= SHOOTR->PCRQ/PopSlab/24;

SHOOTR->LCAI =pSC->getPlant()-> calcGreenLeafArea()* pSC-


>getInitInfo().plantDensity/(100*100);

SHOOTR->Cover= 1.0 - exp (-0.79*SHOOTR->LCAI);

SHOOTR->Shade=(float)SHOOTR->Cover*SHOOTR->RowSp*SHOOTR-
>EOMult;
SHOOTR->Height=min(SHOOTR->Shade,SHOOTR->RowSp);

SHOOTR->ET_demand = (pSC->getPlant()->get_ET()*24);//pass ET
demand from shoot to root. Yang

SHOOTR->LAI=(float)pSC->getPlant()->calcGreenLeafArea()*(float)pSC-
>getInitInfo().plantDensity/(100*100);

//Pass LAI from maizesim into 2dsoil

shoot_weightPerM2 = pSC->getPlant()->get_shootMass()*pSC-
>getInitInfo().plantDensity; //Calculate total shoot mass per meter aquared YY

massIncrease = (shoot_weightPerM2 - old_shoot_weightPerM2); //Calculated increase in


above-ground biomass per m2 YY

massIncrease = max(0.0,massIncrease); // was going zero 2/6/2016 DT

double NitrogenRatio; //optimal N ratio according to N Dilution ratio

if (shoot_weightPerM2<100)

NitrogenRatio = N_min/100; //when shoot weight is lower than


100 g m-2, then nitrogen concentration is assumed to by .0410 g/g

else

NitrogenRatio = N_min/100.0 *pow(shoot_weightPerM2,(1.0-N_shape));


//sqrt(shoot_weightPerM2); //Calcualte above ground potential N concentration

//concentration as a function of aboveground biomass (Greenwood et


al., 1990; Lindquist et al., 2007) YY

pSC->getPlant()->set_NitrogenRatio(NitrogenRatio/10.0);

// double d=075; //d: shape coefficient in the logistic function to simulate


cumulative N uptake (Equation 9 in Lindquist et al. 2007)
U_N = 0.359*d/4; //U_N maximum observed N uptake rate (g N m-2
ground d-1) (Lindquist et al, 2007) YY

//The unit of U_N is g N m-2 ground d-1

U_M = q_n*massIncrease*24; //U_M maximum uptake rate as limited


by maximum N fraction per unit (Equation 2 in Lindquist et al., 2007)

if (shoot_weightPerM2<100) //if shoot weight<100 (g m-2) then U_P is


calculated this way

U_P = (N_min/100.0)*massIncrease*24; // U_P potential rate of


N accumulation (g N m-2 ground d-1) (Lindquist et al. 2007)

else //otherwise, it is calculated like this (Equation 6, Lindquist et al.,


2007) YY

U_P = ((1.0-
N_shape)*N_min*10.0/100.0)*pow(shoot_weightPerM2,-N_shape)*massIncrease*24;

U_D = N_min*10/100*pow(shoot_weightPerM2,-N_shape)-pSC-
>getPlant()->get_N()*(pSC->getInitInfo().plantDensity/(100*100));

double HourlyActualNFromSoil =(NitrogenUptake-NitrogenUptakeOld)/PopSlab; //houly rate


per day

double HourlyNitrogenDemand= max(U_P, 0.)/pSC-


>getInitInfo().plantDensity/24.0; //Determine the nitrogen demand (equation 1 Lindquist et al. 2007) in
grams plant-1

pSC->getPlant()->set_HourlyNitrogenSoilUptake(HourlyActualNFromSoil);

pSC->getPlant()->set_HourlyNitrogenDemand(HourlyNitrogenDemand);
// now do cumulative amounts

CumulativeNitrogenDemand+=HourlyNitrogenDemand; // grams plant-1


day-1

pSC->getPlant()->set_CumulativeNitrogenDemand(CumulativeNitrogenDemand); //units are g


plant day-1

pSC->getPlant()-
>set_CumulativeNitrogenSoilUptake(NitrogenUptake/PopSlab);

double OldNDemand;

OldNDemand=SHOOTR->nitroDemand/PopSlab/1e6/24;

SHOOTR->nitroDemand =
(float)HourlyNitrogenDemand*(float)PopSlab*1e6*24; //Pass the nitrogen demand into 2dsoil YY

//Units are ug slab-1

old_shoot_weightPerM2 = shoot_weightPerM2; //Save the value of the


above_ground biomass of this time-step

NitrogenUptakeOld=NitrogenUptake; // save the cumulative N uptake


from this time step;

SHOOTR->NDemandError=(float)CurrentNUptakeError;

SHOOTR->CumulativeNDemandError=(float)CumulativeNUptakeError;

if (pSC->getPlant()->get_develop()->Dead())

// if (pSC->getLastDayOfSim() <= pSC->getTime()->get_day_of_year())

cout << "Completing crop simulation..." <<endl;

module_public->NShoot=0; //tell 2dsoil that crops harvested

time_public->tNext[ModNum-1]=1e12; // set the next time so the


model

pSC = NULL; // if matured points to nothing


delete pSC;

time_public->RunFlag=0;

else

time_public->tNext[ModNum-1]=time_public->Time+Period;

WaterUptake=0;

//NitrogenUptake=0;

}// end hourly calculations code

return;

Controll.h

//Class Controller

//

// Controller.h

//

//Based on CPM simulation_controller

#pragma once

#ifndef _CONTROLLER_H_

#define _CONTROLLER_H_

#include "timer.h"

#include "development.h"

#include "plant.h"

#include "weather.h"
#include "initinfo.h"

#include "gas_ex_species_param.h"

#ifndef FLOAT_EQ

#define EPSILON 0.001 // floating point comparison tolerance

#define FLOAT_EQ(x,v) (((v - EPSILON) < x) && (x <( v + EPSILON)))

#define MINUTESPERDAY (24*60);

#endif

class CController

private:

enum InputDataFormat {DDSOIL, ICASA, SPAR, FACE}; // input data format

bool cropEmerged, cropHarvested;

int firstDayOfSim, lastDayOfSim, SowingDay;

// cdt 12/06/2010 - I had to increase the size of these from 132 to 133. We couldn't use

// strcpy in vs 2008 so I changed to strcpy_s but apparently it requires enough space for the null

// terminatino character in the destination string

char varietyFile[133], outputFile[133], cropFile[133], logFile[133],LeafFile[133];

char DebugFile[133];

int iCur, // current record number

errorFlag;

TInitInfo initInfo;

CPlant* plant;

Timer* time;

TWeather* weather;

CDevelopment* develop;
InputDataFormat weatherFormat;

TGasExSpeciesParam GasExParam;

public:

CController(const char*, const char*, const char*, TInitInfo);

~CController();

void setErrStatus(int ier) {errorFlag = ier;}

int getErrStatus() {return errorFlag;}

// char* getWeatherFile() {return weatherFile;}

char* getInitFile() {return varietyFile;}

// char* getOutputFile() {return outputFile;}

char* getLogFile() {return logFile;}

CPlant * getPlant() {return plant;}

void addOutputMessage(char*);

void readWeatherFrom2DSOIL(const TWeather &);

void outputToCropFile();

void outputToLeafFile();

void outputToDebug();

Timer* getTime() {return time;}

double getFirstDayOfSim() {return firstDayOfSim;}

double getLastDayOfSim() {return lastDayOfSim;}

double getSowingDay() {return SowingDay; }

double RootWeightFrom2DSOIL;

float MaxRootDepth, AvailableWater;

// TODO add similar here to get gas exchange parameters

TInitInfo getInitInfo() {return initInfo;}


void initialize();

int run(const TWeather &);

double ET_supply; //actual supply of water to plant mol m-2 (leaf) s-1

};

#endif

Control.cpp

#include "stdafx.h"

#include "controller.h"

#include "initinfo.h"

#include <fstream>

#include <iostream>

#include <sstream>

#include <string>

#include <iomanip>

#include <cmath>

#include <time.h>

#include <stdlib.h>

#include <string.h>

#ifndef FLOAT_EQ

#define EPSILON 0.001 // floating point comparison tolerance

#define FLOAT_EQ(x,v) (((v - EPSILON) < x) && (x <( v + EPSILON)))

#endif

#define comma ","

#define MINUTESPERDAY (24*60);


// const a = 17.27; b = 237.7; //constant in deg C

inline double E_sat(double T){return 0.6105*exp(17.27*T/(237.7+T));}

//#using <mscorlib.dll>

using namespace std;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

CController::CController(const char* filename, const char* outfile, const char* LFile, TInitInfo iniInfo)

time = NULL;

weather = NULL;

plant = NULL;

strcpy(varietyFile, filename);

strcpy(cropFile, outfile);

char *pch=(char*)calloc(133,sizeof(char));

char *next=(char*)calloc(133,sizeof(char));

char *ext=".dbg";

char* temp=(char*)calloc(133,sizeof(char));

// strcpy(temp, 133,cropFile);

temp=strcat(pch,ext);

strcpy(DebugFile,temp);

strcpy(LeafFile, LFile);

//DebugFile=outfile

//strcpy_s(DebugFile,"Debug.out");

initInfo = iniInfo;

iCur = 0;
weatherFormat = ICASA;

firstDayOfSim = 0;

lastDayOfSim = 365;

initialize();

errorFlag = 0;

CController::~CController()

if ( time != NULL )

delete time;

if ( weather != NULL )

delete [] weather ;

if ( plant != NULL )

delete plant;

// if ( output != NULL )

// delete output;

//**********************************************************************

void CController::initialize()

#ifndef _DEBUG_FILE

ofstream DebugOut(DebugFile, ios::out);

DebugOut

<< setw(10) << "date"

<< setw(6) << "jday"

<< setw(7) << "time"

<< setw(3) << "sunlit"


<< setw(4) << "shaded"

<< setw(5) << "Pn"

<<setw(7) << "Pg"

<<setw(7) << "Lvs Init"

<<setw(9) << "Lvs apprd"

<<setw(9) << "Lvs grwg"

<<setw(9) << "C to Lvs"

<<setw(9) <<"C2_effect"

<<setw(9) <<"sunlitRatio"

<<endl

DebugOut.close();

#endif

Timer dConvert; //object to convert dates

int mm, dd,yy; // for calendar dates

char* Buffer=(char*)calloc(256,sizeof(char)); //to hold duumy strings from variety file

cout << "Initializing Controller object...." << endl <<endl <<endl ;

cout <<setiosflags(ios::left) << endl

<< endl << endl;

ofstream LeafOut(LeafFile, ios::out);

LeafOut << setiosflags(ios::left)

<< setiosflags(ios::fixed)

<< setw(10) << "date,"

<< setw(6) << "jday,"

<< setw(7) << "time,"


<< setw(9) << "Lvs_Init,"

<< setw(9) << "Lvs_Apr,"

<< setw(9) << "Leaf_#,"

<< setw(7) << "area,"

<< setw(10) << "mass,"

<< setw(10) << "Sen_Area,"

<< setw(10) << "Pntl_Area,"

<< setw(9) << "Elong_age,"

<< setw(9) << "CarbRat,"

<< setw(9) << "SLA,"

<< setw(9) << "dropped,"

<< setw(9) << "state,"

<< setw(9) << "GDDSum"

<< endl;

//This is the plant file for output (see function "output to crop file"

ofstream cropOut(cropFile, ios::out);

cropOut << setiosflags(ios::left)

<< setiosflags(ios::fixed)

<< setw(9) << "date,"

<< setw(6) << "jday,"

<< setw(8) << "time,"

<< setw(8) << "Leaves,"

<< setw(11)<< "MaturLvs,"

<< setw(8) << "Dropped,"

<< setw(9) << "LA/pl,"

<< setw(9) << "LA_dead,"


<< setw(8) << "LAI,"

<< setw(8) << "RH,"

<< setw(8) << "LeafWP,"

<< setw(8) << "PFD,"

<< setw(8) << "SolRad,"

<< setw(8) << "SoilT,"

<< setw(8) << "Tair,"

<< setw(8) << "Tcan,"

<< setw(11) << "ETdmd,"

<< setw(11) << "ETsply,"

<< setw(5) << "Pn,"

<< setw(8) << "Pg,"

<< setw(10) << "Respir,"

<< setw(8) << "av_gs,"

#ifndef _INTERFACE

<< setw(12) << "sunlit_LAI,"

<< setw(12) << "shaded_LAI,"

<< setw(12) << "sunlit_PFD,"

<< setw(12) << "shaded_PFD,"

<< setw(12) << "sunlit_An,"

<< setw(12) << "shaded_An,"

<< setw(12) << "sunlit_Ag,"

<< setw(12) << "shaded_Ag,"

<< setw(12) << "sunlit_gs,"

<< setw(12) << "shaded_gs,"

#endif

<< setw(9) << "VPD,"

<< setw(10) << "Nitr,"

<< setw(10) << "N_Dem,"


<< setw(10) << "NUpt,"

<< setw(10) << "LeafN,"

//<< setw(10) << "N_Effect,"

<< setw(10) << "PCRL,"

<< setw(8) << "totalDM,"

<< setw(8) << "shootDM,"

<< setw(8) << "earDM,"

<< setw(10) << "TotleafDM,"

<< setw(10) << "DrpLfDM,"

<< setw(8) << "stemDM,"

<< setw(8) << "rootDM,"

<< setw(8) << "SoilRt,"

<< setw(8) << "MxRtDep,"

<< setw(8) << "AvailW,"

<< setw(9) << "solubleC,"

<< setw(9) << "Note"

<< endl;

// Read variety file here and fill in data structures

try

ifstream cfs(varietyFile, ios::in);

if (!cfs)

throw "Variety File not found.";

cfs.getline(initInfo.description, sizeof(initInfo.description),'\n');
//Pull cultivar name from description

//cfs.getline(initInfo.cultivar, sizeof(initInfo.cultivar),'\n') ;

string cult = initInfo.description;

int res1 = cult.find(":");

cfs.getline(Buffer, 256,'\n');

cfs.getline(Buffer, 256,'\n');

cfs >>initInfo.genericLeafNo >> initInfo.DayLengthSensitive

>>initInfo.stayGreen >>initInfo.LM_min

>>initInfo.Rmax_LTAR >> initInfo.Rmax_LIR >> initInfo.PhyllochronsToSilk;

initInfo.GDD_rating = 1900;

// end reading cultivar specific data from variety file

// now read species specific data at the end of the file

// loop until we find the location in the file

string location = "[Gas_Exchange Species Parameters]";

int result=-1;

char strTest[255];

string strTest2;

do

cfs.getline(Buffer,256,'\n');

strcpy(strTest, Buffer); //Actually this is not needed, the assign statement will also take
char*

strTest2.assign(strTest); //I keep it to show another way of using char* and char. but,
buffer must be null terminated

result = strTest2.find(location);

} while (result ==-1);

cfs.getline(Buffer, 256, '\n'); //get section title

// cfs.getline(strTest, strlen(strTest));
cfs.getline(Buffer, 256, '\n'); //get var names

// cfs.getline(Buffer, 255, '\n');

cfs >> GasExParam.EaVp >>

GasExParam.EaVc >>

GasExParam.Eaj >>

GasExParam.Hj >>

GasExParam.Sj >>

GasExParam.Vpm25 >>

GasExParam.Vcm25 >>

GasExParam.Jm25 >>

GasExParam.Rd25 >>

GasExParam.Ear >>

GasExParam.g0 >>

GasExParam.g1 ;

cfs.getline(Buffer, 256, '\n'); // the '>>' operator does not read the carriage return after
all the data so we need to read once more

cfs.getline(Buffer, 256, '\n');

cfs.getline(Buffer, 256, '\n');

cfs >> GasExParam.f >>

GasExParam.scatt >>

GasExParam.Kc25 >>

GasExParam.Ko25>>

GasExParam.Kp25 >>

GasExParam.gbs >>

GasExParam.gi >>

GasExParam.gamma1 ;

cfs.getline(Buffer, 256, '\n');

cfs.getline(Buffer, 256, '\n');

cfs.getline(Buffer, 256, '\n');


cfs >> GasExParam.Gamma_gsw >>

GasExParam.sf >>

GasExParam.phyf >>

GasExParam.stomaRatio >>

GasExParam.widthPara >>

GasExParam.LfWidth;

cfs.getline(Buffer, 256, '\n');

cfs.getline(Buffer, 256, '\n');

cfs.getline(Buffer, 256, '\n');

cfs >> GasExParam.internalCO2Ratio >>

GasExParam.SC_param >>

GasExParam.BLC_param;

dConvert.caldat(initInfo.sowingDay,mm,dd,yy);

if (cfs.eof()) cfs.close();

cout << "Done reading variety file: " << varietyFile << endl <<endl;

cout << "Simulation information for:" << endl;

cout << setiosflags(ios::left)

<< setw(20) << "Description: " << initInfo.description << endl <<endl

<< setw(10) << "Cultivar: " << initInfo.cultivar << endl

<< setw(6) << "Generic Leaf Number: " << initInfo.genericLeafNo << endl

<< setw(6) << "Day Length Sensitive: " << initInfo.DayLengthSensitive << endl

<< setw(6) << "Stay Green Parameter: " <<initInfo.stayGreen << endl

<< setw(6) << "Maximum length of largest leaf: " << initInfo.LM_min <<endl

<< setw(6) << "Rmax Leaf initiation rate: " << initInfo.Rmax_LIR << " " << "Rmax
Leaf tip appearance rate: " << initInfo.Rmax_LTAR << endl

<< setw(6) << "Phyllochrons to Silk: " << initInfo.PhyllochronsToSilk << endl
<<endl
<< setw(6) << "Year: " << initInfo.year << endl

<< setw(6) << "Sowing day: " << mm << "/" <<dd <<"/" << yy << endl

<< setw(6) << "TimeStep (min): " << initInfo.timeStep << endl

<< setw(6) << "average [CO2]: " << initInfo.CO2 << endl << endl

catch(const char* message)

cerr << message << "\n";

exit(1);

firstDayOfSim = initInfo.beginDay;

lastDayOfSim = initInfo.endDay;

SowingDay =initInfo.sowingDay;

cropEmerged = false;

cropHarvested = false;

dConvert.caldat(firstDayOfSim,mm,dd,yy);

//not sure if we need this class

time = new Timer(dd, mm, yy,initInfo.timeStep/60.0); // Timer class gets stepsize in hours

//dt modified this after modifying code to use julian day

int dim = (int)(((lastDayOfSim+1)-firstDayOfSim)*(24*60/initInfo.timeStep)); // counting total


records of weather data

weather = new TWeather[dim];

plant = new CPlant(initInfo, GasExParam); //todo send gas exch params here?

}
void CController::readWeatherFrom2DSOIL(const TWeather & wthr)

weatherFormat = DDSOIL;

weather[iCur] = wthr;

ET_supply = wthr.ET_supply;

weather[iCur].daytime = wthr.jday + wthr.time;

int CController::run(const TWeather & wthr) //todo pass gas exchange parameters here to plant

readWeatherFrom2DSOIL(wthr);

if (weather[iCur].jday >= initInfo.sowingDay && weather[iCur].jday <= lastDayOfSim)

plant->update(weather[iCur]);

RootWeightFrom2DSOIL=wthr.TotalRootWeight;

MaxRootDepth= wthr.MaxRootDepth;

AvailableWater= wthr.ThetaAvail;

// plant->update(weather[iCur]);

// dt added ability to output daily based on 2dsoil output

// Always hourly for now - have to add code to average values

// if ((weather[iCur].DailyOutput==1)&&(int(weather[iCur].time*24.0)==6))

// {

outputToCropFile();

// if (plant->get_develop()->Germinated())
outputToLeafFile();

#ifndef _DEBUG_FILE

if (plant->get_develop()->Germinated()) outputToDebug();

#endif

// }

// if (weather[iCur].HourlyOutput==1)

// {

// outputToCropFile();

// if (plant->get_develop()->Germinated())

// outputToLeafFile();

// }

iCur++;

time->step();

return 0;

void CController::outputToCropFile()

int mm,id,iyyy;

string DateForOutput;

double av_gs = plant->get_conductance();

if (!plant->get_develop()->Emerged())

av_gs=0;

double vpd = plant->get_VPD();

if(vpd<0)
{

vpd=0;

time->caldat(weather[iCur].jday, mm, id,iyyy);

#if 0

DateForOutput.Format("%.2d/%.2d/%4i",mm,id,iyyy);

#else

char DateForOutputBuff[16];

sprintf(DateForOutputBuff, "%.2d/%.2d/%4i", mm, id, iyyy);

DateForOutput = DateForOutputBuff;

#endif

string s = "";

if (plant->get_develop()->Matured()) {s="Matured";}

else if (plant->get_develop()->GrainFillBegan()) {s="grainFill";}

else if (plant->get_develop()->Silked()) {s="Silked";}

else if (plant->get_develop()->Flowered()) {s="Flowered";}

else if (plant->get_develop()->Tasseled()) {s = "Tasseled"; }

else if (plant->get_develop()->TasselInitiated()) {s="Tasselinit";}

else if (plant->get_develop()->Emerged()) {s="Emerged";}

else if (plant->get_develop()->Germinated()) {s="Germinated";}

else if (plant->get_develop()->Dead()) {s="Inactive";}

else {s="none";}

// if (FLOAT_EQ(plant->get_develop()-
>emergence.daytime,weather[iCur].daytime)){s = "Emergence";}

//// if (FLOAT_EQ(plant->get_develop()-
>tasselInitiation.daytime,weather[iCur].daytime)){s = "Tassel Initiation";}

// if (FLOAT_EQ(plant->get_develop()->anthesis.daytime, weather[iCur].daytime))
{s = "Anthesis";}

// if (FLOAT_EQ(plant->get_develop()->silking.daytime, weather[iCur].daytime)){s
= "Silking";}
// if (FLOAT_EQ(plant->get_develop()->beginGrainFill.daytime,
weather[iCur].daytime)){s = "Begin grain filling";}

// if (FLOAT_EQ(plant->get_develop()->maturity.daytime,weather[iCur].daytime))
{s = "Begin grain filling";}

ofstream ostr(cropFile, ios::app);

ostr << setiosflags(ios::right)

<< setiosflags(ios::fixed)

<< setw(9) << DateForOutput << comma

<< setw(6) << weather[iCur].jday <<comma

<< setw(8) << setprecision(0) << weather[iCur].time*24.0 << comma

<< setw(8) << setprecision(2) << plant->get_develop()-


>get_LvsAppeared() << comma

<< setw(8) << setprecision(2) << plant->get_nodalUnit()->get_leaf()-


>get_TotalMatureLeaves() << comma

<< setw(8) << setprecision(2) << plant->get_nodalUnit()->get_leaf()-


>get_TotalDroppedLeaves() << comma

<< setw(9) << setprecision(2) << plant->calcGreenLeafArea() << comma

<< setw(9) << setprecision(2) << plant->calcSenescentLeafArea() <<


comma

<< setw(8) << setprecision(2) << plant-


>calcGreenLeafArea()*initInfo.plantDensity/(100*100) << comma

<< setw(8) << setprecision(2) << weather[iCur].RH << comma

<< setw(8) << setprecision(4) << weather[iCur].LeafWP << comma


//print out leaf water potential Yang 8/22/06

<< setw(8) << setprecision(2) << weather[iCur].PFD << comma

<< setw(8) << setprecision(2) << weather[iCur].solRad << comma

<< setw(8) << setprecision(2) << weather[iCur].soilT << comma

<< setw(8) << setprecision(2) << weather[iCur].airT << comma

<< setw(8) << setprecision(2) << plant->get_tmpr() << comma

<< setw(10) << setprecision(3) << plant->get_ET_Old() << comma


<< setw(10) << setprecision(3) << ET_supply << comma //in both cases
transpiration is grams per plant per hour

<< setw(8) << setprecision(4) << plant->get_Pn() << comma //g Carbo
per plant per hour

<< setw(8) << setprecision(4) << plant->get_Pg() << comma

<< setw(8) << setprecision(4) << plant->get_MaintenanceRespiration()


<< comma //dt 03/2011 added to better calc mass balance g carbon per plant per hour

<< setw(8) << setprecision(4) << av_gs << comma //return average
stomatal conductance Yang 10/31/06

#ifndef _INTERFACE

<< setw(12) << setprecision(3) << plant->get_sunlit_LAI() << comma

<< setw(12) << setprecision(3) << plant->get_shaded_LAI() << comma

<< setw(12) << setprecision(2) << plant->get_sunlit_PFD() << comma

<< setw(12) << setprecision(2) << plant->get_shaded_PFD() << comma

<< setw(12) << setprecision(4) << plant->get_sunlit_A_net() << comma

<< setw(12) << setprecision(4) << plant->get_shaded_A_net() << comma

<< setw(12) << setprecision(4) << plant->get_sunlit_A_gross() << comma

<< setw(12) << setprecision(4) << plant->get_shaded_A_gross() <<


comma

<< setw(12) << setprecision(4) << plant->get_sunlit_gs() << comma

<< setw(12) << setprecision(4) << plant->get_shaded_gs() << comma

#endif

<< setw(9) << setprecision(3) << vpd << comma

<< setw(10) << setprecision(4) << plant->get_N() << comma

<< setw(10) << setprecision(4) << plant-


>get_CumulativeNitrogenDemand() << comma

<< setw(10) << setprecision(4) << plant-


>get_CumulativeNitrogenSoilUptake() << comma

<< setw(10) << setprecision(4) << plant->get_LeafN() << comma //return


mass of N in leaves YY

//<< setw(10) << setprecision(4) << plant->


<< setw(10)<< setprecision(4)<< plant->get_roots()-
>get_ActualCarboIncrement() << comma

<< setw(8) << setprecision(3) << plant->get_mass() << comma

<< setw(8) << setprecision(3) << plant->get_shootMass() << comma


//masses are grams per plant

<< setw(8) << setprecision(2) << plant->get_earMass() << comma

<< setw(8) << setprecision(2) << plant->get_leafMass() << comma

<< setw(8) << setprecision(2) << plant->get_DroppedLeafMass() <<


comma

<< setw(8) << setprecision(2) << plant->get_stemMass() << comma

<< setw(8) << setprecision(3) << plant->get_rootMass() << comma

<< setw(8) << setprecision(3) << RootWeightFrom2DSOIL << comma

<< setw(8) << setprecision(1) << MaxRootDepth << comma

<< setw(12) << setprecision(3) << AvailableWater << comma

<< setw(8) << setprecision(2) << plant->get_C_reserve() << comma

<< setw(20)<< setiosflags(ios::skipws) << "\"" + s + "\""

// << setw(9) << setprecision(2) << weather[iCur].dayLength

// << setw(9) << setprecision(2) << plant->get_develop()->get_GDDsum()

<< endl;

ostr.close();

void CController::outputToLeafFile()

int mm,id,iyyy;

CNodalUnit* nU;

CDevelopment* myDevelop=plant->get_develop();

string DateForOutput;
time->caldat(weather[iCur].jday, mm, id,iyyy);

#if 0

DateForOutput.Format("%.2d/%.2d/%4i",mm,id,iyyy);

#else

char DateForOutputBuff[16];

sprintf(DateForOutputBuff, "%.2d/%.2d/%4i", mm, id, iyyy);

DateForOutput = DateForOutputBuff;

#endif

ofstream ostr(LeafFile, ios::app);

ostr << setiosflags(ios::right)

<< setiosflags(ios::fixed);

for (int i = 1; i <= myDevelop->get_LvsInitiated(); i++)

nU=&plant->get_nodalUnit()[i]; // note the use of "&" I needed to call a function

// outside the class as opposed to calling a function in a class

ostr << setw(11) << DateForOutput << comma

<< setw(7) << weather[iCur].jday << comma

<< setw(3) << setprecision(0) << weather[iCur].time*24.0 << comma

<< setw(9) << setprecision(2) << plant->get_develop()->get_LvsInitiated() <<


comma

<< setw(9) << setprecision(2) << plant->get_develop()->get_LvsAppeared() <<


comma

<< setw(9) << setprecision (0)<< nU->get_leaf()->get_Rank() << comma

<< setw(9) << setprecision(3) << nU->get_leaf()->get_greenArea() << comma

// << scientific

<< setw(10) << setprecision(4) << nU->get_leaf()->get_mass() << comma

<< fixed

<< setw(9) << setprecision(3) << nU->get_leaf()->get_senescentArea() <<


comma

<< setw(9) << setprecision(3) << nU->get_leaf()->get_potentialArea() << comma


<< setw(9) << setprecision(3) << nU->get_leaf()->get_Elongation_Age() <<
comma

<< setw(9) << setprecision(3) << nU->get_leaf()->get_N_content() << comma

<< setw(9) << setprecision(1) << nU->get_leaf()->get_SLA() << comma

<< setw(9) << setprecision(3) << nU->get_leaf()->isDropped() << comma

<< setw(9) << setprecision(3) << nU->get_leaf()->isGrowing() << comma

<< setw(9) << setprecision(2) << plant->get_develop()->get_GDDsum()

<< endl;

ostr.close();

nU=NULL;

void CController::outputToDebug()

// needed for saving information on carbon allocation and assimilation

// Can be modified for other variables.

// only called if _DEBUG_FILE is defined.

CNodalUnit* nU;

CDevelopment* myDevelop=plant->get_develop();

int mm,id,iyyy,i;

string DateForOutput;

time->caldat(weather[iCur].jday, mm, id,iyyy);

#if 0

DateForOutput.Format("%.2d/%.2d/%4i",mm,id,iyyy);

#else

char DateForOutputBuff[16];

sprintf(DateForOutputBuff, "%.2d/%.2d/%4i", mm, id, iyyy);


DateForOutput = DateForOutputBuff;

#endif

ofstream DebugOut(DebugFile, ios::app);

DebugOut << setiosflags(ios::right)

<< setiosflags(ios::fixed);

DebugOut << setw(11) << DateForOutput

<< setw(7) << weather[iCur].jday

<< setw(3) << setprecision(0) << weather[iCur].time*24.0

<< setw(7) << setprecision(2) << plant->get_sunlit_LAI()

<< setw(7) << setprecision(2) << plant->get_shaded_LAI()

<< setw(8) << setprecision(2) << plant->get_Pn()*1000.0 //g Carbo per plant
per hour

<< setw(8) << setprecision(2) << plant->get_Pg()*1000.0

<< setw(8) << setprecision(0) << myDevelop->get_LvsInitiated()

<< setw(8) << setprecision(0) << myDevelop->get_LvsAppeared()

<< setw(8) << setprecision(0) << plant->get_nodalUnit()->get_leaf()-


>get_TotalGrowingLeaves()

<< setw(8) << setprecision(2) << plant->get_leafPart()*1000.0

<< setw(6) << setprecision(2) << plant->getC2_effect()

<< setw(6) << setprecision(2) <<plant->getSunlitRatio()

<< endl;

myDevelop=NULL;

DebugOut.close();

}
Main.cpp

#include<iostream>

#include "Crop.h"

using namespace std;

int main()

cout<<"\n\n\n\t\tPlant Growth Model System\n\n";

cout<<"\n 1) For Initialization\n";

cout<<"\n 2) For Rate Calculation\n";

cout<<"\n 3) For Integration\n";

cout<<"\n 4) For Output\n";

cout<<"\n 5) For Exit \n";

Crop obj1;

obj1.intialize();

obj1.calc();

return 0;

You might also like