/// <summary>Perform the overall Canopy Energy Balance</summary> private void BalanceCanopyEnergy(MicroClimateZone MCZone) { ShortWaveRadiation(MCZone); EnergyTerms(MCZone); LongWaveRadiation(MCZone); SoilHeatRadiation(MCZone); }
/// <summary>Calculate the interception loss of water from the canopy</summary> private void CalculateInterception(MicroClimateZone MCZone) { double sumLAI = 0.0; double sumLAItot = 0.0; for (int i = 0; i <= MCZone.numLayers - 1; i++) { for (int j = 0; j <= MCZone.Canopies.Count - 1; j++) { sumLAI += MCZone.Canopies[j].LAI[i]; sumLAItot += MCZone.Canopies[j].LAItot[i]; } } double totalInterception = a_interception * Math.Pow(weather.Rain, b_interception) + c_interception * sumLAItot + d_interception; totalInterception = Math.Max(0.0, Math.Min(0.99 * weather.Rain, totalInterception)); for (int i = 0; i <= MCZone.numLayers - 1; i++) { for (int j = 0; j <= MCZone.Canopies.Count - 1; j++) { MCZone.Canopies[j].interception[i] = MathUtilities.Divide(MCZone.Canopies[j].LAI[i], sumLAI, 0.0) * totalInterception; } } }
/// <summary>Calculate the aerodynamic decoupling for system compartments</summary> private void CalculateOmega(MicroClimateZone MCZone) { for (int i = 0; i <= MCZone.numLayers - 1; i++) { for (int j = 0; j <= MCZone.Canopies.Count - 1; j++) { MCZone.Canopies[j].Omega[i] = CalcOmega(weather.MinT, weather.MaxT, weather.AirPressure, MCZone.Canopies[j].Ga[i], MCZone.Canopies[j].Gc[i]); } } }
/// <summary> /// This model is for strip crops where there is verticle overlap of the shortest and tallest crops canopy but no horizontal overlap /// </summary> /// <param name="tallest"></param> /// <param name="shortest"></param> private void DoStripCropShortWaveRadiation(ref MicroClimateZone tallest, ref MicroClimateZone shortest) { if (MathUtilities.Sum(tallest.DeltaZ) > 0) // Don't perform calculations if layers are empty { double Ht = MathUtilities.Sum(tallest.DeltaZ); // Height of tallest strip double Hs = MathUtilities.Sum(shortest.DeltaZ); // Height of shortest strip double Wt = (tallest.Zone as Zones.RectangularZone).Width; // Width of tallest strip double Ws = (shortest.Zone as Zones.RectangularZone).Width; // Width of shortest strip double Ft = Wt / (Wt + Ws); // Fraction of space in tallest strip double Fs = Ws / (Wt + Ws); // Fraction of space in the shortest strip double LAIt = MathUtilities.Sum(tallest.LAItotsum); // LAI of tallest strip double LAIs = MathUtilities.Sum(shortest.LAItotsum); // LAI of shortest strip double Kt = tallest.Canopies[0].Ktot; // Extinction Coefficient of the tallest strip double Ks = shortest.Canopies[0].Ktot; // Extinction Coefficient of the shortest strip double Httop = Ht - Hs; // Height of the top layer in tallest strip (ie distance from top of shortest to top of tallest) double LAIttop = Httop / Ht * LAIt; // LAI of the top layer of the tallest strip (ie LAI in tallest strip above height of shortest strip) double LAItbot = LAIt - LAIttop; // LAI of the bottom layer of the tallest strip (ie LAI in tallest strip below height of the shortest strip) double LAIttophomo = Ft * LAIttop; // LAI of top layer of tallest strip if spread homogeneously across all of the space double Ftblack = (Math.Sqrt(Math.Pow(Httop, 2) + Math.Pow(Wt, 2)) - Httop) / Wt; // View factor for top layer of tallest strip double Fsblack = (Math.Sqrt(Math.Pow(Httop, 2) + Math.Pow(Ws, 2)) - Httop) / Ws; // View factor for top layer of shortest strip double Tt = Ft * (Ftblack * Math.Exp(-Kt * LAIttop) + Ft * (1 - Ftblack) * Math.Exp(-Kt * LAIttophomo)) + Fs * Ft * (1 - Fsblack) * Math.Exp(-Kt * LAIttophomo); // Transmission of light to bottom of top layer in tallest strip double Ts = Fs * (Fsblack + Fs * (1 - Fsblack) * Math.Exp(-Kt * LAIttophomo)) + Ft * Fs * ((1 - Ftblack) * Math.Exp(-Kt * LAIttophomo)); // Transmission of light to bottom of top layer in shortest strip double Intttop = 1 - Tt - Ts; // Interception by the top layer of the tallest strip (ie light intercepted in tallest strip above height of shortest strip) double Inttbot = (Tt * (1 - Math.Exp(-Kt * LAItbot))); // Interception by the bottom layer of the tallest strip double Soilt = (Tt * (Math.Exp(-Kt * LAItbot))); // Transmission to the soil below tallest strip double Ints = Ts * (1 - Math.Exp(-Ks * LAIs)); // Interception by the shortest strip double Soils = Ts * (Math.Exp(-Ks * LAIs)); // Transmission to the soil below shortest strip double EnergyBalanceCheck = Intttop + Inttbot + Soilt + Ints + Soils; // Sum of all light fractions (should equal 1) if (Math.Abs(1 - EnergyBalanceCheck) > 0.001) { throw (new Exception("Energy Balance not maintained in strip crop light interception model")); } //tallest.Canopies[0].Rs[0] = weather.Radn * (Intttop + Inttbot) / Ft; //tallest.SurfaceRs = weather.Radn * Soilt / Ft; CalculateLayeredShortWaveRadiation(tallest, weather.Radn * (Intttop + Inttbot) / Ft); // if (shortest.Canopies[0].Rs != null) // if (shortest.Canopies[0].Rs.Length > 0) // shortest.Canopies[0].Rs[0] = weather.Radn * Ints / Fs; //shortest.SurfaceRs = weather.Radn * Soils / Fs; CalculateLayeredShortWaveRadiation(shortest, weather.Radn * Ints / Fs); } else { //tallest.Canopies[0].Rs[0] =0; tallest.SurfaceRs = weather.Radn; //shortest.Canopies[0].Rs[0] = 0; shortest.SurfaceRs = weather.Radn; } }
/// <summary> /// Create a new MicroClimateZone for a given simulation zone /// </summary> /// <param name="newZone"></param> private void CreateMCZone(Zone newZone) { MicroClimateZone myZone = new MicroClimateZone(); myZone.zone = newZone; myZone.Reset(); foreach (ICanopy canopy in Apsim.ChildrenRecursively(newZone, typeof(ICanopy))) { myZone.Canopies.Add(new CanopyType(canopy)); } microClimateZones.Add(myZone); }
/// <summary>Calculate the aerodynamic conductance for system compartments</summary> private void CalculateGa(MicroClimateZone MCZone) { double sumDeltaZ = MathUtilities.Sum(MCZone.DeltaZ); double sumLAI = MathUtilities.Sum(MCZone.LAItotsum); double totalGa = AerodynamicConductanceFAO(weather.Wind, refheight, sumDeltaZ, sumLAI); for (int i = 0; i <= MCZone.numLayers - 1; i++) { for (int j = 0; j <= MCZone.Canopies.Count - 1; j++) { MCZone.Canopies[j].Ga[i] = totalGa * MathUtilities.Divide(MCZone.Canopies[j].Rs[i], MCZone.sumRs, 0.0); } } }
/// <summary> /// Calculate Radiation loss to soil heating /// </summary> private void SoilHeatRadiation(MicroClimateZone MCZone) { double radnint = MCZone.sumRs; // Intercepted SW radiation MCZone.soil_heat = SoilHeatFlux(weather.Radn, radnint, soil_heat_flux_fraction); // SoilHeat balance Proportional to Short Wave Balance // ==================================================== for (int i = MCZone.numLayers - 1; i >= 0; i += -1) { for (int j = 0; j <= MCZone.Canopies.Count - 1; j++) { MCZone.Canopies[j].Rsoil[i] = MathUtilities.Divide(MCZone.Canopies[j].Rs[i], weather.Radn, 0.0) * MCZone.soil_heat; } } }
/// <summary> /// Calculates interception of short wave by canopy compartments /// </summary> private void CalculateShortWaveRadiation(MicroClimateZone MCZone) { // Perform Top-Down Light Balance // ============================== double Rin = weather.Radn; double Rint = 0; for (int i = MCZone.numLayers - 1; i >= 0; i += -1) { Rint = Rin * (1.0 - Math.Exp(-MCZone.layerKtot[i] * MCZone.LAItotsum[i])); for (int j = 0; j <= MCZone.Canopies.Count - 1; j++) { MCZone.Canopies[j].Rs[i] = Rint * MathUtilities.Divide(MCZone.Canopies[j].Ftot[i] * MCZone.Canopies[j].Ktot, MCZone.layerKtot[i], 0.0); } Rin -= Rint; } }
/// <summary>Calculate the canopy conductance for system compartments</summary> private void CalculateGc(MicroClimateZone MCZone) { double Rin = weather.Radn; for (int i = MCZone.numLayers - 1; i >= 0; i += -1) { double Rflux = Rin * 1000000.0 / (dayLength * hr2s) * (1.0 - MCZone._albedo); double Rint = 0.0; for (int j = 0; j <= MCZone.Canopies.Count - 1; j++) { MCZone.Canopies[j].Gc[i] = CanopyConductance(MCZone.Canopies[j].Canopy.Gsmax, MCZone.Canopies[j].Canopy.R50, MCZone.Canopies[j].Canopy.FRGR, MCZone.Canopies[j].Fgreen[i], MCZone.layerKtot[i], MCZone.LAItotsum[i], Rflux); Rint += MCZone.Canopies[j].Rs[i]; } // Calculate Rin for the next layer down Rin -= Rint; } }
/// <summary> /// Calculate Net Long Wave Radiation Balance /// </summary> private void LongWaveRadiation(MicroClimateZone MCZone) { double sunshineHours = CalcSunshineHours(weather.Radn, dayLengthLight, weather.Latitude, Clock.Today.DayOfYear); double fractionClearSky = MathUtilities.Divide(sunshineHours, dayLengthLight, 0.0); double averageT = CalcAverageT(weather.MinT, weather.MaxT); MCZone.NetLongWaveRadiation = LongWave(averageT, fractionClearSky, emissivity) * dayLength * hr2s / 1000000.0; // W to MJ // Long Wave Balance Proportional to Short Wave Balance // ==================================================== for (int i = MCZone.numLayers - 1; i >= 0; i += -1) { for (int j = 0; j <= MCZone.Canopies.Count - 1; j++) { MCZone.Canopies[j].Rl[i] = MathUtilities.Divide(MCZone.Canopies[j].Rs[i], weather.Radn, 0.0) * MCZone.NetLongWaveRadiation; } } }
/// <summary> /// Calculate the overall system energy terms /// </summary> private void EnergyTerms(MicroClimateZone MCZone) { MCZone.sumRs = 0.0; MCZone._albedo = 0.0; emissivity = 0.0; for (int i = MCZone.numLayers - 1; i >= 0; i += -1) { for (int j = 0; j <= MCZone.Canopies.Count - 1; j++) { MCZone._albedo += MathUtilities.Divide(MCZone.Canopies[j].Rs[i], weather.Radn, 0.0) * MCZone.Canopies[j].Canopy.Albedo; emissivity += MathUtilities.Divide(MCZone.Canopies[j].Rs[i], weather.Radn, 0.0) * Emissivity; MCZone.sumRs += MCZone.Canopies[j].Rs[i]; } } MCZone._albedo += (1.0 - MathUtilities.Divide(MCZone.sumRs, weather.Radn, 0.0)) * soil_albedo; emissivity += (1.0 - MathUtilities.Divide(MCZone.sumRs, weather.Radn, 0.0)) * soil_emissivity; }
/// <summary> /// Calculates interception of short wave by canopy compartments /// </summary> private void CalculateLayeredShortWaveRadiation(MicroClimateZone ZoneMC, double Rin) { // Perform Top-Down Light Balance // ============================== double Rint = 0; for (int i = ZoneMC.numLayers - 1; i >= 0; i += -1) { if (double.IsNaN(Rint)) { throw new Exception("Bad Radiation Value in Light partitioning"); } Rint = Rin * (1.0 - Math.Exp(-ZoneMC.layerKtot[i] * ZoneMC.LAItotsum[i])); for (int j = 0; j <= ZoneMC.Canopies.Count - 1; j++) { ZoneMC.Canopies[j].Rs[i] = Rint * MathUtilities.Divide(ZoneMC.Canopies[j].Ftot[i] * ZoneMC.Canopies[j].Ktot, ZoneMC.layerKtot[i], 0.0); } Rin -= Rint; } ZoneMC.SurfaceRs = Rin; }
/// <summary>Send an energy balance event</summary> private void SetCanopyEnergyTerms(MicroClimateZone MCZone) { for (int j = 0; j <= MCZone.Canopies.Count - 1; j++) { if (MCZone.Canopies[j].Canopy != null) { CanopyEnergyBalanceInterceptionlayerType[] lightProfile = new CanopyEnergyBalanceInterceptionlayerType[MCZone.numLayers]; double totalPotentialEp = 0; double totalInterception = 0.0; for (int i = 0; i <= MCZone.numLayers - 1; i++) { lightProfile[i] = new CanopyEnergyBalanceInterceptionlayerType(); lightProfile[i].thickness = MCZone.DeltaZ[i]; lightProfile[i].amount = MCZone.Canopies[j].Rs[i] * MCZone.RadnGreenFraction(j); totalPotentialEp += MCZone.Canopies[j].PET[i]; totalInterception += MCZone.Canopies[j].interception[i]; } MCZone.Canopies[j].Canopy.PotentialEP = totalPotentialEp; MCZone.Canopies[j].Canopy.LightProfile = lightProfile; } } }
/// <summary>Calculate the Penman-Monteith water demand</summary> private void CalculatePM(MicroClimateZone MCZone) { // zero a few things, and sum a few others double sumRl = 0.0; double sumRsoil = 0.0; double sumInterception = 0.0; double freeEvapGa = 0.0; for (int j = 0; j <= MCZone.Canopies.Count - 1; j++) { sumRl += MathUtilities.Sum(MCZone.Canopies[j].Rl); sumRsoil += MathUtilities.Sum(MCZone.Canopies[j].Rsoil); sumInterception += MathUtilities.Sum(MCZone.Canopies[j].interception); freeEvapGa += MathUtilities.Sum(MCZone.Canopies[j].Ga); } double netRadiation = ((1.0 - MCZone._albedo) * MCZone.sumRs + sumRl + sumRsoil) * 1000000.0; // MJ/J netRadiation = Math.Max(0.0, netRadiation); double freeEvapGc = freeEvapGa * 1000000.0; // infinite surface conductance double freeEvap = CalcPenmanMonteith(netRadiation, weather.MinT, weather.MaxT, weather.VP, weather.AirPressure, dayLength, freeEvapGa, freeEvapGc); MCZone.dryleaffraction = 1.0 - MathUtilities.Divide(sumInterception * (1.0 - night_interception_fraction), freeEvap, 0.0); MCZone.dryleaffraction = Math.Max(0.0, MCZone.dryleaffraction); for (int i = 0; i <= MCZone.numLayers - 1; i++) { for (int j = 0; j <= MCZone.Canopies.Count - 1; j++) { netRadiation = 1000000.0 * ((1.0 - MCZone._albedo) * MCZone.Canopies[j].Rs[i] + MCZone.Canopies[j].Rl[i] + MCZone.Canopies[j].Rsoil[i]); netRadiation = Math.Max(0.0, netRadiation); MCZone.Canopies[j].PETr[i] = CalcPETr(netRadiation * MCZone.dryleaffraction, weather.MinT, weather.MaxT, weather.AirPressure, MCZone.Canopies[j].Ga[i], MCZone.Canopies[j].Gc[i]); MCZone.Canopies[j].PETa[i] = CalcPETa(weather.MinT, weather.MaxT, weather.VP, weather.AirPressure, dayLength * MCZone.dryleaffraction, MCZone.Canopies[j].Ga[i], MCZone.Canopies[j].Gc[i]); MCZone.Canopies[j].PET[i] = MCZone.Canopies[j].PETr[i] + MCZone.Canopies[j].PETa[i]; } } }
/// <summary> /// This model is for tree crops where there is no vertical overlap of the shortest and tallest canopy but the tallest canopy can overlap the shortest horzontally /// </summary> /// <param name="tree"></param> /// <param name="alley"></param> private void DoTreeRowCropShortWaveRadiation(ref MicroClimateZone tree, ref MicroClimateZone alley) { if (MathUtilities.Sum(tree.DeltaZ) > 0) // Don't perform calculations if layers are empty { double Ht = MathUtilities.Sum(tree.DeltaZ); // Height of tree canopy double CDt = 0; //tree.Canopies[0].Canopy.Depth / 1000; // Depth of tree canopy foreach (MicroClimateCanopy c in tree.Canopies) { if (c.Canopy.Depth < c.Canopy.Height) { if (CDt > 0.0) { throw new Exception("Can't have two tree canopies"); } else { CDt = c.Canopy.Depth / 1000; } } } double CBHt = Ht - CDt; // Base hight of the tree canopy double Ha = MathUtilities.Sum(alley.DeltaZ); // Height of alley canopy if ((Ha > CBHt) & (tree.DeltaZ.Length > 1)) { throw (new Exception("Height of the alley canopy must not exceed the base height of the tree canopy")); } double Wt = (tree.Zone as Zones.RectangularZone).Width; // Width of tree zone double Wa = (alley.Zone as Zones.RectangularZone).Width; // Width of alley zone double CWt = 0; //Math.Min(tree.Canopies[0].Canopy.Width / 1000, (Wt + Wa));// Width of the tree canopy foreach (MicroClimateCanopy c in tree.Canopies) { if (c.Canopy.Depth < c.Canopy.Height) { if (CWt > 0.0) { throw new Exception("Can't have two tree canopies"); } else { CWt = Math.Min(c.Canopy.Width / 1000, (Wt + Wa)); } } } double WaOl = Math.Min(CWt - Wt, Wa); // Width of tree canopy that overlap the alley zone double WaOp = Wa - WaOl; // Width of the open alley zone between tree canopies double Ft = CWt / (Wt + Wa); // Fraction of space in tree canopy double Fs = WaOp / (Wt + Wa); // Fraction of open space in the alley row double LAIt = MathUtilities.Sum(tree.LAItotsum); // LAI of trees double LAIa = MathUtilities.Sum(alley.LAItotsum); // LAI of alley crop double Kt = tree.Canopies[0].Ktot; // Extinction Coefficient of trees double Ka = alley.Canopies[0].Ktot; // Extinction Coefficient of alley crop double LAIthomo = Ft * LAIt; // LAI of trees if spread homogeneously across row and alley zones double Ftbla = (Math.Sqrt(Math.Pow(CDt, 2) + Math.Pow(CWt, 2)) - CDt) / CWt; // View factor for the tree canopy if a black body double Fabla = (Math.Sqrt(Math.Pow(CDt, 2) + Math.Pow(WaOp, 2)) - CDt) / WaOp; // View factor for the gap between trees in alley if trees a black body if (WaOp == 0) { Fabla = 0; } //All transmission and interception values below are a fraction of the total short wave radiation incident to both the tree and alley rows double Tt = Ft * (Ftbla * Math.Exp(-Kt * LAIt) + Ft * (1 - Ftbla) * Math.Exp(-Kt * LAIthomo)) + Fs * Ft * (1 - Fabla) * Math.Exp(-Kt * LAIthomo); // Transmission of light to the bottom of the tree canopy double Ta = Fs * (Fabla + Fs * (1 - Fabla) * Math.Exp(-Kt * LAIthomo)) + Ft * Fs * ((1 - Ftbla) * Math.Exp(-Kt * LAIthomo)); // Transmission of light to the bottom of the gap in the tree canopy double It = 1 - Tt - Ta; // Interception by the trees double St = Tt * Wt / CWt; // Transmission to the soil in the tree zone double IaOl = Tt * WaOl / CWt * (1 - Math.Exp(-Ka * LAIa)); // Interception by the alley canopy below the overlap of the trees double IaOp = Ta * (1 - Math.Exp(-Ka * LAIa)); // Interception by the alley canopy in the gaps between tree canopy double Ia = IaOl + IaOp; // Interception by the alley canopy double SaOl = Tt * WaOl / CWt * (Math.Exp(-Ka * LAIa)); // Transmission to the soil beneigth the alley canopy under the tree canopy double SaOp = Ta * (Math.Exp(-Ka * LAIa)); // Transmission to the soil beneigth the alley canopy in the open double Sa = SaOl + SaOp; // Transmission to the soil beneight the alley double EnergyBalanceCheck = It + St + Ia + Sa; // Sum of all light fractions (should equal 1) if (Math.Abs(1 - EnergyBalanceCheck) > 0.001) { throw (new Exception("Energy Balance not maintained in strip crop light interception model")); } Ft = (Wt) / (Wt + Wa); // Remove overlap so scaling back to zone ground area works Fs = (Wa) / (Wt + Wa); // Remove overlap so scaling back to zone ground area works CalculateLayeredShortWaveRadiation(tree, weather.Radn * It / Ft); CalculateLayeredShortWaveRadiation(alley, weather.Radn * Ia / Fs); } else { tree.SurfaceRs = weather.Radn; CalculateLayeredShortWaveRadiation(alley, weather.Radn); } }
/// <summary> /// This model is for strip crops where there is no verticle overlap of the shortest and tallest crops canopy but no horizontal overlap /// </summary> /// <param name="vine"></param> /// <param name="alley"></param> private void DoVineStripShortWaveRadiation(ref MicroClimateZone vine, ref MicroClimateZone alley) { if (MathUtilities.Sum(vine.DeltaZ) > 0) // Don't perform calculations if layers are empty { double Ht = MathUtilities.Sum(vine.DeltaZ); // Height of tree canopy double CDt = vine.Canopies[0].Canopy.Depth / 1000; // Depth of tree canopy double CBHt = Ht - CDt; // Base hight of the tree canopy double Ha = MathUtilities.Sum(alley.DeltaZ); // Height of alley canopy if ((Ha > CBHt) & (vine.DeltaZ.Length > 1)) { throw (new Exception("Height of the alley canopy must not exceed the base height of the tree canopy")); } double Wt = (vine.Zone as Zones.RectangularZone).Width; // Width of tree zone double Wa = (alley.Zone as Zones.RectangularZone).Width; // Width of alley zone double CWt = vine.Canopies[0].Canopy.Width / 1000; // Width of the tree canopy double WaOl = (CWt + Wt) / 2; //adjusted Width of tree canopy, because the tree canopy is smaller than the strip width, so there would be a gap between alley and tree double WaOp = Wa + Wt - WaOl; // Width of the open alley zone between tree canopies double Ft = WaOl / (Wt + Wa); // Fraction of space in tree canopy double Fs = WaOp / (Wt + Wa); // Fraction of open space in the alley row double LAIt = MathUtilities.Sum(vine.LAItotsum); // LAI of tallest strip double LAIs = MathUtilities.Sum(alley.LAItotsum); // LAI of shortest strip double Kt = 0; // Extinction Coefficient of the tallest strip if (vine.Canopies.Count > 0) // If it exists... { Kt = vine.Canopies[0].Ktot; } double Ka = 0; // Extinction Coefficient of the shortest strip if (alley.Canopies.Count > 0) // If it exists... { Ka = alley.Canopies[0].Ktot; } double Httop = Ht - Ha; // distance from top of shortest to top of tallest double LAIthomo = Ft * LAIt; // LAI of top layer of tallest strip if spread homogeneously across all of the space double Ftblack = (Math.Sqrt(Math.Pow(CDt, 2) + Math.Pow(WaOl, 2)) - CDt) / WaOl; // View factor for top layer of tallest strip double Fsblack = (Math.Sqrt(Math.Pow(Httop, 2) + Math.Pow(WaOp, 2)) - Httop) / WaOp; // View factor for top layer of shortest strip double Tt = Ft * (Ftblack * Math.Exp(-Kt * LAIt) + Ft * (1 - Ftblack) * Math.Exp(-Kt * LAIthomo)) + Fs * Ft * (1 - Fsblack) * Math.Exp(-Kt * LAIthomo); // Transmission of light to bottom of top layer in tallest strip double Ts = Fs * (Fsblack + Fs * (1 - Fsblack) * Math.Exp(-Kt * LAIthomo)) + Ft * Fs * ((1 - Ftblack) * Math.Exp(-Kt * LAIthomo)); // Transmission of light to bottom of top layer in shortest strip double Intttop = 1 - Tt - Ts; // Interception by the top layer of the tallest strip (ie light intercepted in tallest strip above height of shortest strip) double Inttbot = (Tt * (1 - Math.Exp(-Kt * 0))); // Interception by the bottom layer of the tallest strip double Soilt = (Tt * (Math.Exp(-Kt * 0))); // Transmission to the soil below tallest strip double Ints = Ts * (1 - Math.Exp(-Ka * LAIs)); // Interception by the shortest strip double Soils = Ts * (Math.Exp(-Ka * LAIs)); // Transmission to the soil below shortest strip double EnergyBalanceCheck = Intttop + Inttbot + Soilt + Ints + Soils; // Sum of all light fractions (should equal 1) if (Math.Abs(1 - EnergyBalanceCheck) > 0.001) { throw (new Exception("Energy Balance not maintained in strip crop light interception model")); } Ft = (Wt) / (Wt + Wa); // Remove overlap so scaling back to zone ground area works Fs = (Wa) / (Wt + Wa); // Remove overlap so scaling back to zone ground area works if (vine.Canopies.Count > 0) { vine.Canopies[0].Rs[0] = weather.Radn * (Intttop + Inttbot) / Ft; } vine.SurfaceRs = weather.Radn * Soilt / Ft; if (alley.Canopies.Count > 0 && alley.Canopies[0].Rs != null) { if (alley.Canopies[0].Rs.Length > 0) { alley.Canopies[0].Rs[0] = weather.Radn * Ints / Fs; } } alley.SurfaceRs = weather.Radn * Soils / Fs; } else { //tallest.Canopies[0].Rs[0] =0; vine.SurfaceRs = weather.Radn; //shortest.Canopies[0].Rs[0] = 0; alley.SurfaceRs = weather.Radn; } }
/// <summary> /// This model is for strip crops where there is no verticle overlap of the shortest and tallest crops canopy but no horizontal overlap /// </summary> /// <param name="vine"></param> /// <param name="alley"></param> private void DoVineStripShortWaveRadiation(ref MicroClimateZone vine, ref MicroClimateZone alley) { if (vine.DeltaZ.Sum() > 0) // Don't perform calculations if layers are empty { double Ht = vine.DeltaZ.Sum(); // Height of tree canopy double CDt = vine.Canopies[0].Canopy.Depth / 1000; // Depth of tree canopy double CBHt = Ht - CDt; // Base hight of the tree canopy double Ha = alley.DeltaZ.Sum(); // Height of alley canopy if ((Ha > CBHt) & (vine.DeltaZ.Length > 1)) { throw (new Exception("Height of the alley canopy must not exceed the base height of the tree canopy")); } double Wt = (vine.Zone as Zones.RectangularZone).Width; // Width of tree zone double Wa = (alley.Zone as Zones.RectangularZone).Width; // Width of alley zone double CWt = vine.Canopies[0].Canopy.Width / 1000; // Width of the tree canopy double WaOp = Wa + Wt - CWt; // Width of the open alley zone between tree canopies double Ft = CWt / (Wt + Wa); // Fraction of space in tree canopy double Fs = WaOp / (Wt + Wa); // Fraction of open space in the alley row double LAIt = vine.LAItotsum.Sum() * Wt / CWt; // adjusting the LAI of tallest strip based on new width double LAIs = alley.LAItotsum.Sum() * Wa / WaOp; // adjusting the LAI of shortest strip based on new width double Kt = 0; // Extinction Coefficient of the tallest strip if (vine.Canopies.Count > 0 & LAIt > 0) // If it exists... { Kt = vine.Canopies[0].Ktot; } double Ka = 0; // Extinction Coefficient of the shortest strip if (alley.Canopies.Count > 0 & LAIs > 0) // If it exists... { Ka = alley.Canopies[0].Ktot; } double Httop = Ht - Ha; // distance from top of shortest to top of tallest double LAIthomo = Ft * LAIt; // LAI of top layer of tallest strip if spread homogeneously across all of the space double fhomo = 1 - Math.Exp(-Kt * LAIthomo); double fcompr = (1 - Math.Exp(-Kt * LAIt)) * CWt / WaOp; double IPblackt = (Math.Sqrt(Math.Pow(CDt, 2) + Math.Pow(WaOp, 2)) - CDt) / WaOp; double IRblackt = (Math.Sqrt(Math.Pow(CDt, 2) + Math.Pow(CWt, 2)) - CDt) / CWt; // View factor for top layer of tallest strip double SPt = IPblackt + (1 - IPblackt) * Math.Exp(-Kt * LAIthomo); double SRt = IRblackt * Math.Exp(-Kt * LAIt) + (1 - IRblackt) * Math.Exp(-Kt * LAIthomo); // Transmission of light to bottom of top layer in tallest strip double W = 0; if (vine.Canopies.Count > 0 & LAIt > 0 & Kt > 0) // If it exists... { W = (SPt - SRt) / (1 - Math.Exp(-Kt * LAIt)); } double ftop = fhomo * (1 - W) + fcompr * W; // light interception by the vine row //use different height and LAI distribution for calculating the light penetrate to the interrow //this is for accounting the effect of trunk, cordon, and post that shading on the interrow double IPblackb = (Math.Sqrt(Math.Pow(Httop, 2) + Math.Pow(WaOp, 2)) - Httop) / WaOp; //bottom part double IRblackb = (Math.Sqrt(Math.Pow(Httop, 2) + Math.Pow(CWt, 2)) - Httop) / CWt; // View factor for top layer of tallest strip double SPb = IPblackb + (1 - IPblackb) * Math.Exp(-Kt * LAIthomo); double SRb = IRblackb * Math.Exp(-Kt * LAIt) + (1 - IRblackb) * Math.Exp(-Kt * LAIthomo); // Transmission of light to bottom of top layer in tallest strip //double Wb = (SPt - SRt) / (1 - Math.Exp(-Kt * LAIt)); //double fb = fhomo * (1 - W) + fcompr * W; //light interception by the vine row double Soilt = SRb * Ft; // Transmission to the soil below tallest strip double Intttop = ftop; // Interception by the top layer of the tallest strip (ie light intercepted in tallest strip above height of shortest strip) double Ints = SPb * (1 - Math.Exp(-Ka * LAIs)) * Fs; // Interception by the shortest strip double Soils = SPb * (Math.Exp(-Ka * LAIs)) * Fs; // Transmission to the soil below shortest strip Ft = (Wt) / (Wt + Wa); // Scaling back to zone ground area works Fs = (Wa) / (Wt + Wa); // Scaling back to zone ground area works // Perform Top-Down Light Balance for tree zone // ============================== double Rint = 0; double Rin = weather.Radn * Intttop / Ft; for (int i = vine.numLayers - 1; i >= 0; i += -1) { if (double.IsNaN(Rint)) { throw new Exception("Bad Radiation Value in Light partitioning"); } Rint = Rin; for (int j = 0; j <= vine.Canopies.Count - 1; j++) { vine.Canopies[j].Rs[i] = Rint * MathUtilities.Divide(vine.Canopies[j].Ftot[i] * vine.Canopies[j].Ktot, vine.layerKtot[i], 0.0); } Rin -= Rint; } vine.SurfaceRs = weather.Radn * Soilt / Ft; // Perform Top-Down Light Balance for alley zone // ============================== Rint = 0; Rin = weather.Radn * Ints / Fs; for (int i = alley.numLayers - 1; i >= 0; i += -1) { if (double.IsNaN(Rint)) { throw new Exception("Bad Radiation Value in Light partitioning"); } Rint = Rin; for (int j = 0; j <= alley.Canopies.Count - 1; j++) { alley.Canopies[j].Rs[i] = Rint * MathUtilities.Divide(alley.Canopies[j].Ftot[i] * alley.Canopies[j].Ktot, alley.layerKtot[i], 0.0); } Rin -= Rint; } alley.SurfaceRs = weather.Radn * Soils / Fs; } else { //tallest.Canopies[0].Rs[0] =0; vine.SurfaceRs = weather.Radn; //shortest.Canopies[0].Rs[0] = 0; alley.SurfaceRs = weather.Radn; } }