/// <summary>Mets the variables.</summary> private void MetVariables(MicroClimateZone MCZone) { MCZone.averageT = CalcAverageT(weather.MinT, weather.MaxT); // This is the length of time within the day during which // Evaporation will take place MCZone.dayLength = MathUtilities.DayLength(Clock.Today.Day, sun_angle, weather.Latitude); // This is the length of time within the day during which // the sun is above the horizon MCZone.dayLengthLight = MathUtilities.DayLength(Clock.Today.Day, SunSetAngle, weather.Latitude); MCZone.sunshineHours = CalcSunshineHours(weather.Radn, MCZone.dayLengthLight, weather.Latitude, Clock.Today.Day); MCZone.fractionClearSky = MathUtilities.Divide(MCZone.sunshineHours, MCZone.dayLengthLight, 0.0); }
/// <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, MCZone.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, MCZone.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> /// 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 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>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 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 / (MCZone.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>Perform the overall Canopy Energy Balance</summary> private void BalanceCanopyEnergy(MicroClimateZone MCZone) { ShortWaveRadiation(MCZone); EnergyTerms( MCZone); LongWaveRadiation( MCZone); SoilHeatRadiation( MCZone); }
/// <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 ShortWaveRadiation(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; } RadIntTotal = 1 - weather.Radn / Rin; }
/// <summary> /// Calculate Net Long Wave Radiation Balance /// </summary> private void LongWaveRadiation(MicroClimateZone MCZone) { MCZone.netLongWave = LongWave(MCZone.averageT, MCZone.fractionClearSky, emissivity) * MCZone.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.netLongWave; }
/// <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; }