/// <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> /// 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> /// 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> /// 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); }