/// <summary> /// Convert NPP estimate into biomass of an autotroph stock /// </summary> /// <param name="cellEnvironment">The environment of the current grid cell</param> /// <param name="gridCellStockHandler">The stock handler for the current stock</param> /// <param name="actingStock">The location of the stock to add biomass to</param> /// <param name="terrestrialNPPUnits">The units of the terrestrial NPP data</param> /// <param name="oceanicNPPUnits">The units of the oceanic NPP data</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="GlobalModelTimeStepUnit">The time step unit used in the model</param> /// <param name="trackProcesses">Whether to output data describing the ecological processes</param> /// <param name="globalTracker">Whether to output data describing the global-scale environment</param> /// <param name="outputDetail">The level of output detail to use for the outputs</param> /// <param name="specificLocations">Whether the model is being run for specific locations</param> /// <param name="currentMonth">The current month in the model run</param> public void ConvertNPPToAutotroph(SortedList <string, double[]> cellEnvironment, GridCellStockHandler gridCellStockHandler, int[] actingStock, string terrestrialNPPUnits, string oceanicNPPUnits, uint currentTimestep, string GlobalModelTimeStepUnit, ProcessTracker trackProcesses, GlobalProcessTracker globalTracker, string outputDetail, bool specificLocations, uint currentMonth) { // Get NPP from the cell environment double NPP = cellEnvironment["NPP"][currentMonth]; // If NPP is a mssing value then set to zero if (NPP == cellEnvironment["Missing Value"][0]) { NPP = 0.0; } // Check that this is an ocean cell if (cellEnvironment["Realm"][0] == 2.0) { // Check that the units of oceanic NPP are gC per m2 per day Debug.Assert(oceanicNPPUnits == "gC/m2/day", "Oceanic NPP data are not in the correct units for this formulation of the model"); // Convert to g/cell/month NPP *= _MsqToKmSqConversion; // Multiply by cell area to get g/cell/day NPP *= cellEnvironment["Cell Area"][0]; // Convert to g wet matter, assuming carbon content of phytoplankton is 10% of wet matter NPP *= _PhytoplanktonConversionRatio; // Finally convert to g/cell/month and add to the stock totalbiomass NPP *= Utilities.ConvertTimeUnits(GlobalModelTimeStepUnit, "day"); gridCellStockHandler[actingStock].TotalBiomass += NPP; if (trackProcesses.TrackProcesses && (outputDetail == "high") && specificLocations) { trackProcesses.TrackPrimaryProductionTrophicFlow((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], NPP); } if (globalTracker.TrackProcesses) { globalTracker.RecordNPP((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], (uint)actingStock[0], NPP / cellEnvironment["Cell Area"][0]); } // If the biomass of the autotroph stock has been made less than zero (i.e. because of negative NPP) then reset to zero if (gridCellStockHandler[actingStock].TotalBiomass < 0.0) { gridCellStockHandler[actingStock].TotalBiomass = 0.0; } } // Else if neither on land or in the ocean else { Debug.Fail("This is not a marine cell!"); // Set the autotroph biomass to zero gridCellStockHandler[actingStock].TotalBiomass = 0.0; } Debug.Assert(gridCellStockHandler[actingStock].TotalBiomass >= 0.0, "stock negative"); }
/// <summary> /// Convert NPP estimate into biomass of an autotroph stock /// </summary> /// <param name="cellEnvironment">The environment of the current grid cell</param> /// <param name="gridCellStockHandler">The stock handler for the current stock</param> /// <param name="actingStock">The location of the stock to add biomass to</param> /// <param name="terrestrialNPPUnits">The units of the terrestrial NPP data</param> /// <param name="oceanicNPPUnits">The units of the oceanic NPP data</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="GlobalModelTimeStepUnit">The time step unit used in the model</param> /// <param name="trackProcesses">Whether to output data describing the ecological processes</param> /// <param name="globalTracker">Whether to output data describing the global-scale environment</param> /// <param name="outputDetail">The level of output detail to use for the outputs</param> /// <param name="specificLocations">Whether the model is being run for specific locations</param> /// <param name="currentMonth">The current month in the model run</param> public void ConvertNPPToAutotroph(SortedList<string, double[]> cellEnvironment, GridCellStockHandler gridCellStockHandler, int[] actingStock, string terrestrialNPPUnits, string oceanicNPPUnits, uint currentTimestep, string GlobalModelTimeStepUnit, ProcessTracker trackProcesses, GlobalProcessTracker globalTracker, string outputDetail, bool specificLocations, uint currentMonth) { // Get NPP from the cell environment double NPP = cellEnvironment["NPP"][currentMonth]; // If NPP is a mssing value then set to zero if (NPP == cellEnvironment["Missing Value"][0]) NPP = 0.0; // Check that this is an ocean cell if (cellEnvironment["Realm"][0] == 2.0) { // Check that the units of oceanic NPP are gC per m2 per day Debug.Assert(oceanicNPPUnits == "gC/m2/day", "Oceanic NPP data are not in the correct units for this formulation of the model"); // Convert to g/cell/month NPP *= _MsqToKmSqConversion; // Multiply by cell area to get g/cell/day NPP *= cellEnvironment["Cell Area"][0]; // Convert to g wet matter, assuming carbon content of phytoplankton is 10% of wet matter NPP *= _PhytoplanktonConversionRatio; // Finally convert to g/cell/month and add to the stock totalbiomass NPP *= Utilities.ConvertTimeUnits(GlobalModelTimeStepUnit, "day"); gridCellStockHandler[actingStock].TotalBiomass += NPP; if (trackProcesses.TrackProcesses && (outputDetail == "high") && specificLocations) { trackProcesses.TrackPrimaryProductionTrophicFlow((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], NPP); } if (globalTracker.TrackProcesses) { globalTracker.RecordNPP((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], (uint)actingStock[0], NPP / cellEnvironment["Cell Area"][0]); } // If the biomass of the autotroph stock has been made less than zero (i.e. because of negative NPP) then reset to zero if (gridCellStockHandler[actingStock].TotalBiomass < 0.0) gridCellStockHandler[actingStock].TotalBiomass = 0.0; } // Else if neither on land or in the ocean else { Debug.Fail("This is not a marine cell!"); // Set the autotroph biomass to zero gridCellStockHandler[actingStock].TotalBiomass = 0.0; } Debug.Assert(gridCellStockHandler[actingStock].TotalBiomass >= 0.0, "stock negative"); }
/// <summary> /// Run ecological processes that operate on stocks within a single grid cell /// </summary> ///<param name="gridCellStocks">The stocks in the current grid cell</param> ///<param name="actingStock">The acting stock</param> ///<param name="cellEnvironment">The stocks in the current grid cell</param> ///<param name="environmentalDataUnits">List of units associated with the environmental variables</param> ///<param name="humanNPPScenario">The human appropriation of NPP scenario to apply</param> ///<param name="madingleyStockDefinitions">The functional group definitions for stocks in the model</param> ///<param name="currentTimeStep">The current model time step</param> ///<param name="burninSteps">The number of time steps to spin the model up for before applying human impacts</param> ///<param name="impactSteps">The number of time steps to apply human impacts for</param> ///<param name="globalModelTimeStepUnit">The time step unit used in the model</param> ///<param name="trackProcesses">Whether to track properties of ecological processes</param> ///<param name="tracker">An instance of the ecological process tracker</param> ///<param name="globalTracker">An instance of the global process tracker</param> ///<param name="currentMonth">The current model month</param> ///<param name="outputDetail">The level of detail to use in outputs</param> ///<param name="specificLocations">Whether to run the model for specific locations</param> ///<param name="impactCell">Whether this cell should have human impacts applied</param> public void RunWithinCellEcology(GridCellStockHandler gridCellStocks, int[] actingStock, SortedList <string, double[]> cellEnvironment, SortedList <string, string> environmentalDataUnits, Tuple <string, double, double> humanNPPScenario, FunctionalGroupDefinitions madingleyStockDefinitions, uint currentTimeStep, uint burninSteps, uint impactSteps, uint recoverySteps, uint instantStep, uint numInstantSteps, string globalModelTimeStepUnit, Boolean trackProcesses, ProcessTracker tracker, GlobalProcessTracker globalTracker, uint currentMonth, string outputDetail, bool specificLocations, Boolean impactCell) { int ScenarioYear; if (currentTimeStep < burninSteps) { ScenarioYear = 0; } else { ScenarioYear = (int)Math.Floor((currentTimeStep - burninSteps) / 12.0); } if (madingleyStockDefinitions.GetTraitNames("Realm", actingStock[0]) == "marine") { // Run the autotroph processor MarineNPPtoAutotrophStock.ConvertNPPToAutotroph(cellEnvironment, gridCellStocks, actingStock, environmentalDataUnits["LandNPP"], environmentalDataUnits["OceanNPP"], currentTimeStep, globalModelTimeStepUnit, tracker, globalTracker, outputDetail, specificLocations, currentMonth); } else if (madingleyStockDefinitions.GetTraitNames("Realm", actingStock[0]) == "terrestrial") { // Run the dynamic plant model to update the leaf stock for this time step double WetMatterNPP = DynamicPlantModel.UpdateLeafStock(cellEnvironment, gridCellStocks, actingStock, currentTimeStep, madingleyStockDefinitions. GetTraitNames("leaf strategy", actingStock[0]).Equals("deciduous"), globalModelTimeStepUnit, tracker, globalTracker, currentMonth, outputDetail, specificLocations); double fhanpp = HANPP.RemoveHumanAppropriatedMatter(WetMatterNPP, cellEnvironment, humanNPPScenario, gridCellStocks, actingStock, currentTimeStep, ScenarioYear, burninSteps, impactSteps, recoverySteps, instantStep, numInstantSteps, impactCell, globalModelTimeStepUnit); // Apply human appropriation of NPP gridCellStocks[actingStock].TotalBiomass += WetMatterNPP * (1.0 - fhanpp); if (globalTracker.TrackProcesses) { globalTracker.RecordHANPP((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], (uint)actingStock[0], fhanpp); } if (gridCellStocks[actingStock].TotalBiomass < 0.0) { gridCellStocks[actingStock].TotalBiomass = 0.0; } } else { Debug.Fail("Stock must be classified as belonging to either the marine or terrestrial realm"); } }
/// <summary> /// Run ecological processes that operate on stocks within a single grid cell /// </summary> ///<param name="gridCellStocks">The stocks in the current grid cell</param> ///<param name="actingStock">The acting stock</param> ///<param name="cellEnvironment">The stocks in the current grid cell</param> ///<param name="environmentalDataUnits">List of units associated with the environmental variables</param> ///<param name="humanNPPScenario">The human appropriation of NPP scenario to apply</param> ///<param name="madingleyStockDefinitions">The functional group definitions for stocks in the model</param> ///<param name="currentTimeStep">The current model time step</param> ///<param name="burninSteps">The number of time steps to spin the model up for before applying human impacts</param> ///<param name="impactSteps">The number of time steps to apply human impacts for</param> ///<param name="globalModelTimeStepUnit">The time step unit used in the model</param> ///<param name="trackProcesses">Whether to track properties of ecological processes</param> ///<param name="tracker">An instance of the ecological process tracker</param> ///<param name="globalTracker">An instance of the global process tracker</param> ///<param name="currentMonth">The current model month</param> ///<param name="outputDetail">The level of detail to use in outputs</param> ///<param name="specificLocations">Whether to run the model for specific locations</param> ///<param name="impactCell">Whether this cell should have human impacts applied</param> public void RunWithinCellEcology(GridCellStockHandler gridCellStocks, int[] actingStock, SortedList<string, double[]> cellEnvironment, SortedList<string, string> environmentalDataUnits, Madingley.Common.ScenarioParameter humanNPPScenario, FunctionalGroupDefinitions madingleyStockDefinitions, uint currentTimeStep, uint burninSteps, uint impactSteps, uint recoverySteps, uint instantStep, uint numInstantSteps, string globalModelTimeStepUnit, Boolean trackProcesses, ProcessTracker tracker, GlobalProcessTracker globalTracker, uint currentMonth, string outputDetail, bool specificLocations, Boolean impactCell) { if (madingleyStockDefinitions.GetTraitNames("Realm", actingStock[0]) == "marine") { // Run the autotroph processor MarineNPPtoAutotrophStock.ConvertNPPToAutotroph(cellEnvironment, gridCellStocks, actingStock, environmentalDataUnits["LandNPP"], environmentalDataUnits["OceanNPP"], currentTimeStep, globalModelTimeStepUnit, tracker, globalTracker, outputDetail, specificLocations, currentMonth); } else if (madingleyStockDefinitions.GetTraitNames("Realm", actingStock[0]) == "terrestrial") { // Run the dynamic plant model to update the leaf stock for this time step double WetMatterNPP = DynamicPlantModel.UpdateLeafStock(cellEnvironment, gridCellStocks, actingStock, currentTimeStep, madingleyStockDefinitions. GetTraitNames("leaf strategy", actingStock[0]).Equals("deciduous"), globalModelTimeStepUnit, tracker, globalTracker, currentMonth, outputDetail, specificLocations); /// <summary> double fhanpp = HANPP.RemoveHumanAppropriatedMatter(WetMatterNPP, cellEnvironment, humanNPPScenario, gridCellStocks, actingStock, currentTimeStep, burninSteps, impactSteps, recoverySteps, instantStep, numInstantSteps, impactCell, globalModelTimeStepUnit); // Apply human appropriation of NPP gridCellStocks[actingStock].TotalBiomass += WetMatterNPP * (1.0 - fhanpp); if (globalTracker.TrackProcesses) { globalTracker.RecordHANPP((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], (uint)actingStock[0], fhanpp); } if (gridCellStocks[actingStock].TotalBiomass < 0.0) gridCellStocks[actingStock].TotalBiomass = 0.0; } else { Debug.Fail("Stock must be classified as belonging to either the marine or terrestrial realm"); } }
/// <summary> /// Update the leaf stock during a time step given the environmental conditions in the grid cell /// </summary> /// <param name="cellEnvironment">The environment in the current grid cell</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="actingStock">The position of the acting stock in the array of grid cell stocks</param> /// <param name="currentTimeStep">The current model time step</param> /// <param name="deciduous">Whether the acting stock consists of deciduous leaves</param> /// <param name="GlobalModelTimeStepUnit">The time step unit used in the model</param> /// <param name="tracker">Whether to track properties of the ecological processes</param> /// <param name="globalTracker">Whether to output data describing the global environment</param> /// <param name="currentMonth">The current model month</param> /// <param name="outputDetail">The level of detail to use in model outputs</param> /// <param name="specificLocations">Whether the model is being run for specific locations</param> public double UpdateLeafStock(SortedList <string, double[]> cellEnvironment, GridCellStockHandler gridCellStocks, int[] actingStock, uint currentTimeStep, bool deciduous, string GlobalModelTimeStepUnit, ProcessTracker tracker, GlobalProcessTracker globalTracker, uint currentMonth, string outputDetail, bool specificLocations) { // ESTIMATE ANNUAL LEAF CARBON FIXATION ASSUMING ENVIRONMENT THROUGHOUT THE YEAR IS THE SAME AS IN THIS MONTH // Calculate annual NPP double NPP = this.CalculateMiamiNPP(cellEnvironment["Temperature"].Average(), cellEnvironment["Precipitation"].Sum()); // Calculate fractional allocation to structural tissue double FracStruct = this.CalculateFracStruct(NPP); // Estimate monthly NPP based on seasonality layer NPP *= cellEnvironment["Seasonality"][currentMonth]; // Calculate leaf mortality rates double AnnualLeafMortRate; double MonthlyLeafMortRate; double TimeStepLeafMortRate; if (deciduous) { // Calculate annual deciduous leaf mortality AnnualLeafMortRate = this.CalculateDeciduousAnnualLeafMortality(cellEnvironment["Temperature"].Average()); // For deciduous plants monthly leaf mortality is weighted by temperature deviance from the average, to capture seasonal patterns double[] ExpTempDev = new double[12]; double SumExpTempDev = 0.0; double[] TempDev = new double[12]; double Weight; for (int i = 0; i < 12; i++) { TempDev[i] = cellEnvironment["Temperature"][i] - cellEnvironment["Temperature"].Average(); ExpTempDev[i] = Math.Exp(-TempDev[i] / 3); SumExpTempDev += ExpTempDev[i]; } Weight = ExpTempDev[currentMonth] / SumExpTempDev; MonthlyLeafMortRate = AnnualLeafMortRate * Weight; TimeStepLeafMortRate = MonthlyLeafMortRate * Utilities.ConvertTimeUnits(GlobalModelTimeStepUnit, "month"); } else { // Calculate annual evergreen leaf mortality AnnualLeafMortRate = this.CalculateEvergreenAnnualLeafMortality(cellEnvironment["Temperature"].Average()); // For evergreen plants, leaf mortality is assumed to be equal throughout the year MonthlyLeafMortRate = AnnualLeafMortRate * (1.0 / 12.0); TimeStepLeafMortRate = MonthlyLeafMortRate * Utilities.ConvertTimeUnits(GlobalModelTimeStepUnit, "month"); } // Calculate fine root mortality rate double AnnualFRootMort = this.CalculateFineRootMortalityRate(cellEnvironment["Temperature"][currentMonth]); // Calculate the NPP allocated to non-structural tissues double FracNonStruct = (1 - FracStruct); // Calculate the fractional allocation to leaves double FracLeaves = FracNonStruct * this.CalculateLeafFracAllocation(AnnualLeafMortRate, AnnualFRootMort); // Calculate the fractional allocation of NPP to evergreen plant matter double FracEvergreen = this.CalculateFracEvergreen(cellEnvironment["Fraction Year Frost"][0]); // Update NPP depending on whether the acting stock is deciduous or evergreen if (deciduous) { NPP *= (1 - FracEvergreen); } else { NPP *= FracEvergreen; } // Calculate the fire mortality rate double FireMortRate = this.CalculateFireMortalityRate(NPP, cellEnvironment["Fraction Year Fire"][0]); // Calculate the structural mortality rate double StMort = this.CalculateStructuralMortality(cellEnvironment["AET"][currentMonth] * 12); // Calculate leaf C fixation double LeafCFixation = NPP * FracLeaves; // Convert from carbon to leaf wet matter double WetMatterIncrement = this.ConvertToLeafWetMass(LeafCFixation, cellEnvironment["Cell Area"][0]); // Convert from the monthly time step used for this process to the global model time step unit WetMatterIncrement *= Utilities.ConvertTimeUnits(GlobalModelTimeStepUnit, "month"); // Add the leaf wet matter to the acting stock //gridCellStocks[actingStock].TotalBiomass += Math.Max(-gridCellStocks[actingStock].TotalBiomass, WetMatterIncrement); double NPPWetMatter = Math.Max(-gridCellStocks[actingStock].TotalBiomass, WetMatterIncrement); // If the processer tracker is enabled and output detail is high and the model is being run for specific locations, then track the biomass gained through primary production if (tracker.TrackProcesses && (outputDetail == "high") && specificLocations) { tracker.TrackPrimaryProductionTrophicFlow((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], Math.Max(-gridCellStocks[actingStock].TotalBiomass, WetMatterIncrement)); } if (globalTracker.TrackProcesses) { globalTracker.RecordNPP((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], (uint)actingStock[0], this.ConvertToLeafWetMass(NPP, cellEnvironment["Cell Area"][0]) * Utilities.ConvertTimeUnits(GlobalModelTimeStepUnit, "month") / cellEnvironment["Cell Area"][0]); } // Calculate fractional leaf mortality double LeafMortFrac = 1 - Math.Exp(-TimeStepLeafMortRate); // Update the leaf stock biomass owing to the leaf mortality gridCellStocks[actingStock].TotalBiomass *= (1 - LeafMortFrac); NPPWetMatter *= (1 - LeafMortFrac); return(NPPWetMatter); }
/// <summary> /// Initializes the ecosystem model /// </summary> /// <param name="initialisation">An instance of the model initialisation class</param> /// <param name="scenarioParameters">The parameters for the scenarios to run</param> /// <param name="scenarioIndex">The index of the scenario being run</param> /// <param name="outputFilesSuffix">The suffix to be applied to all outputs from this model run</param> /// <param name="globalModelTimeStepUnit">The time step unit used in the model</param> /// <param name="simulation">The index of the simulation being run</param> public MadingleyModel(MadingleyModelInitialisation initialisation, ScenarioParameterInitialisation scenarioParameters, int scenarioIndex, string outputFilesSuffix, string globalModelTimeStepUnit, int simulation) { // Assign the properties for this model run AssignModelRunProperties(initialisation, scenarioParameters, scenarioIndex, outputFilesSuffix); // Set up list of global diagnostics SetUpGlobalDiagnosticsList(); // Set up the model grid SetUpModelGrid(initialisation, scenarioParameters, scenarioIndex, simulation); // Set up model outputs SetUpOutputs(initialisation, simulation, scenarioIndex); // Make the initial outputs InitialOutputs(outputFilesSuffix, initialisation, CurrentMonth); // Instance the array of process trackers ProcessTrackers = new ProcessTracker[_CellList.Count]; // Temporary variables Boolean varExists; // Set up process trackers for each grid cell for (int i = 0; i < _CellList.Count; i++) { ProcessTrackers[i] = new ProcessTracker(NumTimeSteps, EcosystemModelGrid.Lats, EcosystemModelGrid.Lons, _CellList, initialisation.ProcessTrackingOutputs, initialisation.TrackProcesses, CohortFunctionalGroupDefinitions, EcosystemModelGrid.GlobalMissingValue, outputFilesSuffix, initialisation.OutputPath, initialisation.ModelMassBins, SpecificLocations, i, initialisation, EcosystemModelGrid.GetEnviroLayer("Realm", 0, _CellList[i][0], _CellList[i][1], out varExists) == 2.0, EcosystemModelGrid.LatCellSize, EcosystemModelGrid.LonCellSize); } // Set up a cross cell process tracker TrackCrossCellProcesses = new CrossCellProcessTracker(initialisation.TrackCrossCellProcesses, "DispersalData", initialisation.OutputPath, outputFilesSuffix); //Set up a global process tracker if (SpecificLocations) initialisation.TrackGlobalProcesses = false; TrackGlobalProcesses = new GlobalProcessTracker(NumTimeSteps, EcosystemModelGrid.Lats, EcosystemModelGrid.Lons, _CellList, initialisation.ProcessTrackingOutputs, initialisation.TrackGlobalProcesses, CohortFunctionalGroupDefinitions, StockFunctionalGroupDefinitions, EcosystemModelGrid.GlobalMissingValue, outputFilesSuffix, initialisation.OutputPath, initialisation.ModelMassBins, SpecificLocations, initialisation, EcosystemModelGrid.LatCellSize, EcosystemModelGrid.LonCellSize); //Set-up the instance of OutputModelState WriteModelState = new OutputModelState(initialisation, outputFilesSuffix, simulation); if (SpecificLocations) initialisation.RunRealm = ""; // Record the initial cohorts in the process trackers RecordInitialCohorts(); // Initialise the class for cross-grid-cell ecology MadingleyEcologyCrossGridCell = new EcologyCrossGridCell(); // Initialise the time step timer TimeStepTimer = new StopWatch(); EcologyTimer = new StopWatch(); OutputTimer = new StopWatch(); // Set the global model time step unit _GlobalModelTimeStepUnit = globalModelTimeStepUnit; // Initialise the utility functions Utilities = new UtilityFunctions(); // Initialise the climate change impacts class ClimateChangeSimulator = new ClimateChange(); // Initialise the harvesting impacts class HarvestingSimulator = new Harvesting(EcosystemModelGrid.Lats, EcosystemModelGrid.Lons, (float)EcosystemModelGrid.LatCellSize); }
/// <summary> /// Update the leaf stock during a time step given the environmental conditions in the grid cell /// </summary> /// <param name="cellEnvironment">The environment in the current grid cell</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="actingStock">The position of the acting stock in the array of grid cell stocks</param> /// <param name="currentTimeStep">The current model time step</param> /// <param name="deciduous">Whether the acting stock consists of deciduous leaves</param> /// <param name="GlobalModelTimeStepUnit">The time step unit used in the model</param> /// <param name="tracker">Whether to track properties of the ecological processes</param> /// <param name="globalTracker">Whether to output data describing the global environment</param> /// <param name="currentMonth">The current model month</param> /// <param name="outputDetail">The level of detail to use in model outputs</param> /// <param name="specificLocations">Whether the model is being run for specific locations</param> public double UpdateLeafStock(SortedList<string, double[]> cellEnvironment, GridCellStockHandler gridCellStocks, int[] actingStock, uint currentTimeStep, bool deciduous, string GlobalModelTimeStepUnit, ProcessTracker tracker, GlobalProcessTracker globalTracker, uint currentMonth, string outputDetail, bool specificLocations) { // ESTIMATE ANNUAL LEAF CARBON FIXATION ASSUMING ENVIRONMENT THROUGHOUT THE YEAR IS THE SAME AS IN THIS MONTH // Calculate annual NPP double NPP = this.CalculateMiamiNPP(cellEnvironment["Temperature"].Average(), cellEnvironment["Precipitation"].Sum()); // Calculate fractional allocation to structural tissue double FracStruct = this.CalculateFracStruct(NPP); // Estimate monthly NPP based on seasonality layer NPP *= cellEnvironment["Seasonality"][currentMonth]; // Calculate leaf mortality rates double AnnualLeafMortRate; double MonthlyLeafMortRate; double TimeStepLeafMortRate; if (deciduous) { // Calculate annual deciduous leaf mortality AnnualLeafMortRate = this.CalculateDeciduousAnnualLeafMortality(cellEnvironment["Temperature"].Average()); // For deciduous plants monthly leaf mortality is weighted by temperature deviance from the average, to capture seasonal patterns double[] ExpTempDev = new double[12]; double SumExpTempDev = 0.0; double[] TempDev = new double[12]; double Weight; for (int i = 0; i < 12; i++) { TempDev[i] = cellEnvironment["Temperature"][i] - cellEnvironment["Temperature"].Average(); ExpTempDev[i] = Math.Exp(-TempDev[i] / 3); SumExpTempDev += ExpTempDev[i]; } Weight = ExpTempDev[currentMonth] / SumExpTempDev; MonthlyLeafMortRate = AnnualLeafMortRate * Weight; TimeStepLeafMortRate = MonthlyLeafMortRate * Utilities.ConvertTimeUnits(GlobalModelTimeStepUnit, "month"); } else { // Calculate annual evergreen leaf mortality AnnualLeafMortRate = this.CalculateEvergreenAnnualLeafMortality(cellEnvironment["Temperature"].Average()); // For evergreen plants, leaf mortality is assumed to be equal throughout the year MonthlyLeafMortRate = AnnualLeafMortRate * (1.0 / 12.0); TimeStepLeafMortRate = MonthlyLeafMortRate * Utilities.ConvertTimeUnits(GlobalModelTimeStepUnit, "month"); } // Calculate fine root mortality rate double AnnualFRootMort = this.CalculateFineRootMortalityRate(cellEnvironment["Temperature"][currentMonth]); // Calculate the NPP allocated to non-structural tissues double FracNonStruct = (1 - FracStruct); // Calculate the fractional allocation to leaves double FracLeaves = FracNonStruct * this.CalculateLeafFracAllocation(AnnualLeafMortRate, AnnualFRootMort); // Calculate the fractional allocation of NPP to evergreen plant matter double FracEvergreen = this.CalculateFracEvergreen(cellEnvironment["Fraction Year Frost"][0]); // Update NPP depending on whether the acting stock is deciduous or evergreen if (deciduous) { NPP *= (1 - FracEvergreen); } else { NPP *= FracEvergreen; } // Calculate the fire mortality rate double FireMortRate = this.CalculateFireMortalityRate(NPP, cellEnvironment["Fraction Year Fire"][0]); // Calculate the structural mortality rate double StMort = this.CalculateStructuralMortality(cellEnvironment["AET"][currentMonth] * 12); // Calculate leaf C fixation double LeafCFixation = NPP * FracLeaves; // Convert from carbon to leaf wet matter double WetMatterIncrement = this.ConvertToLeafWetMass(LeafCFixation, cellEnvironment["Cell Area"][0]); // Convert from the monthly time step used for this process to the global model time step unit WetMatterIncrement *= Utilities.ConvertTimeUnits(GlobalModelTimeStepUnit, "month"); // Add the leaf wet matter to the acting stock //gridCellStocks[actingStock].TotalBiomass += Math.Max(-gridCellStocks[actingStock].TotalBiomass, WetMatterIncrement); double NPPWetMatter = Math.Max(-gridCellStocks[actingStock].TotalBiomass, WetMatterIncrement); // If the processer tracker is enabled and output detail is high and the model is being run for specific locations, then track the biomass gained through primary production if (tracker.TrackProcesses && (outputDetail == "high") && specificLocations) { tracker.TrackPrimaryProductionTrophicFlow((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], Math.Max(-gridCellStocks[actingStock].TotalBiomass, WetMatterIncrement)); } if (globalTracker.TrackProcesses) { globalTracker.RecordNPP((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0],(uint)actingStock[0], this.ConvertToLeafWetMass(NPP, cellEnvironment["Cell Area"][0]) * Utilities.ConvertTimeUnits(GlobalModelTimeStepUnit, "month")/cellEnvironment["Cell Area"][0]); } // Calculate fractional leaf mortality double LeafMortFrac = 1 - Math.Exp(-TimeStepLeafMortRate); // Update the leaf stock biomass owing to the leaf mortality gridCellStocks[actingStock].TotalBiomass *= (1 - LeafMortFrac); NPPWetMatter *= (1 - LeafMortFrac); return (NPPWetMatter); }
/// <summary> /// Initializes the ecosystem model /// </summary> /// <param name="initialisation">An instance of the model initialisation class</param> /// <param name="scenarioParameters">The parameters for the scenarios to run</param> /// <param name="scenarioIndex">The index of the scenario being run</param> /// <param name="outputFilesSuffix">The suffix to be applied to all outputs from this model run</param> /// <param name="globalModelTimeStepUnit">The time step unit used in the model</param> /// <param name="simulation">The index of the simulation being run</param> public MadingleyModel(MadingleyModelInitialisation initialisation, ScenarioParameterInitialisation scenarioParameters, int scenarioIndex, string outputFilesSuffix, string globalModelTimeStepUnit, int simulation) { // Assign the properties for this model run AssignModelRunProperties(initialisation, scenarioParameters, scenarioIndex, outputFilesSuffix); #endif // Set up the model grid #if true this._CellList = initialisation.CellList.ToList(); SetUpModelGrid(initialisation); EcosystemModelGrid.SetGridCells(gridCells, this._CellList); #else SetUpModelGrid(initialisation, scenarioParameters, scenarioIndex, simulation); #endif // Set up model outputs SetUpOutputs(initialisation, simulation, scenarioIndex); // Make the initial outputs InitialOutputs(outputFilesSuffix, initialisation, CurrentMonth); // Instance the array of process trackers ProcessTrackers = new ProcessTracker[_CellList.Count]; // Temporary variables Boolean varExists; // Set up process trackers for each grid cell for (int i = 0; i < _CellList.Count; i++) { ProcessTrackers[i] = new ProcessTracker(NumTimeSteps, EcosystemModelGrid.Lats, EcosystemModelGrid.Lons, _CellList, initialisation.ProcessTrackingOutputs, initialisation.TrackProcesses, CohortFunctionalGroupDefinitions, EcosystemModelGrid.GlobalMissingValue, outputFilesSuffix, initialisation.OutputPath, initialisation.ModelMassBins, SpecificLocations, i, initialisation, EcosystemModelGrid.GetEnviroLayer("Realm", 0, _CellList[i][0], _CellList[i][1], out varExists) == 2.0, EcosystemModelGrid.LatCellSize, EcosystemModelGrid.LonCellSize); } // Set up a cross cell process tracker TrackCrossCellProcesses = new CrossCellProcessTracker(initialisation.TrackCrossCellProcesses, "DispersalData", initialisation.OutputPath, outputFilesSuffix); //Set up a global process tracker if (SpecificLocations) initialisation.TrackGlobalProcesses = false; TrackGlobalProcesses = new GlobalProcessTracker(NumTimeSteps, EcosystemModelGrid.Lats, EcosystemModelGrid.Lons, _CellList, initialisation.ProcessTrackingOutputs, initialisation.TrackGlobalProcesses, CohortFunctionalGroupDefinitions, StockFunctionalGroupDefinitions, EcosystemModelGrid.GlobalMissingValue, outputFilesSuffix, initialisation.OutputPath, initialisation.ModelMassBins, SpecificLocations, initialisation, EcosystemModelGrid.LatCellSize, EcosystemModelGrid.LonCellSize); //Set-up the instance of OutputModelState WriteModelState = new OutputModelState(initialisation, outputFilesSuffix, simulation); if (SpecificLocations) initialisation.RunRealm = ""; // Record the initial cohorts in the process trackers RecordInitialCohorts(); // Initialise the time step timer TimeStepTimer = new StopWatch(); EcologyTimer = new StopWatch(); OutputTimer = new StopWatch(); // Set the global model time step unit _GlobalModelTimeStepUnit = globalModelTimeStepUnit; }