/// <summary> /// Calculate the potential biomass that could be gained through herbivory on each grid cell autotroph stock /// </summary> /// <param name="gridCellCohorts">The cohorts in the grid cell</param> /// <param name="gridCellStocks">The stocks in the grid cell</param> /// <param name="actingCohort">The acting cohort</param> /// <param name="cellEnvironment">The environment in the current grid cell</param> /// <param name="madingleyCohortDefinitions">The functional group definitions for cohorts in the model</param> /// <param name="madingleyStockDefinitions">The functional group definitions for stocks in the model</param> public void GetEatingPotentialMarine(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList <string, double[]> cellEnvironment, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions) { // Set the total biomass eaten by the acting cohort to zero _TotalBiomassEatenByCohort = 0.0; // Get the individual body mass of the acting cohort _BodyMassHerbivore = gridCellCohorts[actingCohort].IndividualBodyMass; // Set the total number of units to handle all potential biomass eaten to zero _TimeUnitsToHandlePotentialFoodItems = 0.0; // Initialise the jagged arrays to hold the potential and actual biomass eaten in each of the grid cell autotroph stocks _BiomassesEaten = new double[gridCellStocks.Count][]; _PotentialBiomassesEaten = new double[gridCellStocks.Count][]; // Loop over rows in the jagged arrays and initialise each vector for (int i = 0; i < gridCellStocks.Count; i++) { _BiomassesEaten[i] = new double[gridCellStocks[i].Count]; _PotentialBiomassesEaten[i] = new double[gridCellStocks[i].Count]; } // Loop over functional groups that can be eaten foreach (int FunctionalGroup in _FunctionalGroupIndicesToEat) { // Loop over stocks within the functional group for (int i = 0; i < gridCellStocks[FunctionalGroup].Count; i++) { // Get the mass from this stock that is available for eating (assumes all marine autotrophic organisms are edible) //EdibleMass = gridCellStocks[FunctionalGroup][i].TotalBiomass * 0.1; EdibleMass = gridCellStocks[FunctionalGroup][i].TotalBiomass; // Calculate the potential biomass eaten from this stock by the acting cohort _PotentialBiomassesEaten[FunctionalGroup][i] = CalculatePotentialBiomassEatenMarine(EdibleMass, _BodyMassHerbivore); // Add the time required to handle the potential biomass eaten from this stock to the cumulative total for all stocks _TimeUnitsToHandlePotentialFoodItems += _PotentialBiomassesEaten[FunctionalGroup][i] * CalculateHandlingTimeMarine(_BodyMassHerbivore); } } }
/// <summary> /// Calculates the geometric community weighted mean body mass /// </summary> /// <param name="ecosystemModelGrid">The model grid</param> /// <param name="cellIndices">The list of indices of cells to be run in the current model simulation</param> /// <param name="cellIndex">The index of the current cell within the list of cells to be run</param> /// <returns>geometric community weighted mean body mass</returns> public double CalculateGeometricCommunityMeanBodyMass(ModelGrid ecosystemModelGrid, List <uint[]> cellIndices, int cellIndex) { //Get the cohorts for the specified cell GridCellCohortHandler CellCohorts = ecosystemModelGrid.GetGridCellCohorts(cellIndices[cellIndex][0], cellIndices[cellIndex][1]); double CumulativeAbundance = 0.0; double CumulativeLogBiomass = 0.0; //Retrieve the biomass foreach (var CohortList in CellCohorts) { foreach (Cohort c in CohortList) { CumulativeLogBiomass += Math.Log(c.IndividualBodyMass + c.IndividualReproductivePotentialMass) * c.CohortAbundance; CumulativeAbundance += c.CohortAbundance; } } double CWGMBM = Math.Exp(CumulativeLogBiomass / CumulativeAbundance); return(CWGMBM); }
/// <summary> /// Generate new cohorts from reproductive potential mass /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param> /// <param name="cellEnvironment">The environment of the current grid cell</param> /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param> /// <param name="madingleyCohortDefinitions">The definitions of cohort functional groups in the model</param> /// <param name="madingleyStockDefinitions">The definitions of stock functional groups in the model</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="tracker">An instance of ProcessTracker to hold diagnostics for reproduction</param> /// <param name="partial">Thread-locked variables</param> public void RunReproduction(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList <string, double[]> cellEnvironment, Dictionary <string, Dictionary <string, double> > deltas, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, uint currentTimestep, ProcessTracker tracker, ref ThreadLockedParallelVariables partial) { // Check that the abundance in the cohort to produce is greater than or equal to zero Debug.Assert(_OffspringCohortAbundance >= 0.0, "Offspring abundance < 0"); // Get the adult and juvenile masses of the cohort to produce double[] OffspringProperties = GetOffspringCohortProperties(gridCellCohorts, actingCohort, madingleyCohortDefinitions); // Update cohort abundance in case juvenile mass has been altered _OffspringCohortAbundance = (_OffspringCohortAbundance * gridCellCohorts[actingCohort].JuvenileMass) / OffspringProperties[0]; //Create the offspring cohort Cohort OffspringCohort = new Cohort((byte)actingCohort[0], OffspringProperties[0], OffspringProperties[1], OffspringProperties[0], _OffspringCohortAbundance, (ushort)currentTimestep, ref partial.NextCohortIDThreadLocked); // Add the offspring cohort to the grid cell cohorts array gridCellCohorts[actingCohort[0]].Add(OffspringCohort); // If the cohort has never been merged with another cohort, then add it to the tracker for output as diagnostics if ((!gridCellCohorts[actingCohort].Merged) && tracker.TrackProcesses) { tracker.RecordNewCohort((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], currentTimestep, _OffspringCohortAbundance, gridCellCohorts[actingCohort].AdultMass, gridCellCohorts[actingCohort].FunctionalGroupIndex); } // Subtract all of the reproductive potential mass of the parent cohort, which has been used to generate the new // cohort, from the delta reproductive potential mass deltas["reproductivebiomass"]["reproduction"] -= (gridCellCohorts[actingCohort].IndividualReproductivePotentialMass); }
/// <summary> /// Create the matrix of prey abundances in each weight bin /// </summary> /// <param name="gridCellCohorts">Cohorts in this grid cell</param> /// <param name="actingCohort">The predator cohort</param> /// <param name="functionalGroupIndicesToEat">The functional groups which this predator eats</param> /// <param name="logOptimalPreyBodySizeRatio">The (log-transformed) optimal ratio of prey to predator body mass</param> private void PopulateBinnedPreyAbundance(GridCellCohortHandler gridCellCohorts, int[] actingCohort, int[] functionalGroupIndicesToEat, double logPredatorMassPlusLogPredatorOptimalBodySizeRatio) { int BinNumber = 0; // Loop through prey functional groups foreach (var fg in functionalGroupIndicesToEat) { foreach (var cohort in gridCellCohorts[fg]) { // Calculate the difference between the actual body size ratio and the optimal ratio, // and then divide by the standard deviation in log ratio space to determine in // which bin to assign the prey item. BinNumber = GetBinNumber(cohort.IndividualBodyMass, logPredatorMassPlusLogPredatorOptimalBodySizeRatio); if ((0 < BinNumber) && (BinNumber < NumberOfBins)) { BinnedPreyDensities[fg, BinNumber] += cohort.CohortAbundance / _CellAreaHectares; } } } }
/// <summary> /// Make a record of the properties of the intial model cohorts in the new cohorts output file /// </summary> public void RecordInitialCohorts() { int i = 0; foreach (uint[] cell in _CellList) { if (ProcessTrackers[i].TrackProcesses) { GridCellCohortHandler TempCohorts = EcosystemModelGrid.GetGridCellCohorts(cell[0], cell[1]); for (int FunctionalGroup = 0; FunctionalGroup < TempCohorts.Count; FunctionalGroup++) { foreach (Cohort item in TempCohorts[FunctionalGroup]) { ProcessTrackers[i].RecordNewCohort(cell[0], cell[1], 0, item.CohortAbundance, item.AdultMass, item.FunctionalGroupIndex, new List<uint> { uint.MaxValue }, item.CohortID[0]); } } } i += 1; } }
/// <summary> /// Initialises the feeding interactions matrix each timestep /// </summary> public void InitialiseInteractionsMatrix(GridCellCohortHandler gridCellCohorts) { int MaxCohorts = 0; foreach (var CohortList in gridCellCohorts) { if (CohortList.Count > MaxCohorts) { MaxCohorts = CohortList.Count; } } FeedingInteractionsMatrixPredation = new List <Tuple <int, int, double, double> > [gridCellCohorts.Count, MaxCohorts]; FeedingInteractionsMatrixHerbivory = new List <Tuple <int, int, double, double> > [gridCellCohorts.Count, MaxCohorts]; for (int i = 0; i < gridCellCohorts.Count; i++) { for (int c = 0; c < MaxCohorts; c++) { FeedingInteractionsMatrixPredation[i, c] = new List <Tuple <int, int, double, double> >(); FeedingInteractionsMatrixHerbivory[i, c] = new List <Tuple <int, int, double, double> >(); } } }
/// <summary> /// Assigns biomass from body mass to reproductive potential mass /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param> /// <param name="cellEnvironment">The environment in the current grid cell</param> /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param> /// <param name="madingleyCohortDefinitions">The definitions of cohort functional groups in the model</param> /// <param name="madingleyStockDefinitions">The definitions of stock functional groups in the model</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="tracker">An instance of ProcessTracker to hold diagnostics for reproduction</param> public void AssignMassToReproductivePotential(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList <string, double[]> cellEnvironment, Dictionary <string, Dictionary <string, double> > deltas, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, uint currentTimestep, ProcessTracker tracker) { // If this is the first time reproductive potential mass has been assigned for this cohort, // then set the maturity time step for this cohort as the current model time step if (gridCellCohorts[actingCohort].MaturityTimeStep == uint.MaxValue) { gridCellCohorts[actingCohort].MaturityTimeStep = currentTimestep; // Track the generation length for this cohort if ((!gridCellCohorts[actingCohort].Merged) && tracker.TrackProcesses) { tracker.TrackMaturity((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], currentTimestep, gridCellCohorts[actingCohort].BirthTimeStep, gridCellCohorts[actingCohort].JuvenileMass, gridCellCohorts[actingCohort].AdultMass, gridCellCohorts[actingCohort].FunctionalGroupIndex); } } // Assign the specified mass to reproductive potential mass and remove it from individual biomass deltas["reproductivebiomass"]["reproduction"] += _BiomassToAssignToReproductivePotential; deltas["biomass"]["reproduction"] -= _BiomassToAssignToReproductivePotential; }
/// <summary> /// Gets a state variable density for specified functional groups of specified entity types in a specified grid cell /// </summary> /// <param name="variableName">The name of the variable to get: 'biomass' or 'abundance'</param> /// <param name="traitValue">The functional group trait value to get data for</param> /// <param name="functionalGroups">The functional group indices to get the state variable for</param> /// <param name="latCellIndex">The latitudinal index of the cell</param> /// <param name="lonCellIndex">The longitudinal index of the cell</param> /// <param name="stateVariableType">The type of entity to return the state variable for: 'stock' or 'cohort'</param> /// <param name="modelInitialisation">The Madingley Model initialisation</param> /// <returns>The state variable density for specified functional groups of specified entity types in a specified grid cell</returns> public double GetStateVariableDensity(string variableName, string traitValue, int[] functionalGroups, uint latCellIndex, uint lonCellIndex, string stateVariableType, MadingleyModelInitialisation modelInitialisation) { double returnValue = 0.0; switch (stateVariableType.ToLower()) { case "cohort": GridCellCohortHandler TempCohorts = InternalGrid[latCellIndex, lonCellIndex].GridCellCohorts; switch (variableName.ToLower()) { case "biomass": if (traitValue != "Zooplankton (all)") { foreach (int f in functionalGroups) { foreach (var item in TempCohorts[f]) { returnValue += ((item.IndividualBodyMass + item.IndividualReproductivePotentialMass) * item.CohortAbundance); } } } else { foreach (int f in functionalGroups) { foreach (var item in TempCohorts[f]) { if (item.IndividualBodyMass <= modelInitialisation.PlanktonDispersalThreshold) { returnValue += ((item.IndividualBodyMass + item.IndividualReproductivePotentialMass) * item.CohortAbundance); } } } } break; case "abundance": if (traitValue != "Zooplankton (all)") { foreach (int f in functionalGroups) { foreach (var item in TempCohorts[f]) { returnValue += item.CohortAbundance; } } } else { foreach (int f in functionalGroups) { foreach (var item in TempCohorts[f]) { if (item.IndividualBodyMass <= modelInitialisation.PlanktonDispersalThreshold) { returnValue += item.CohortAbundance; } } } } break; default: Debug.Fail("For cohorts, state variable name must be either 'biomass' or 'abundance'"); break; } break; case "stock": GridCellStockHandler TempStocks = InternalGrid[latCellIndex, lonCellIndex].GridCellStocks; switch (variableName.ToLower()) { case "biomass": foreach (int f in functionalGroups) { foreach (var item in TempStocks[f]) { returnValue += item.TotalBiomass; } } break; default: Debug.Fail("For stocks, state variable name must be 'biomass'"); break; } break; default: Debug.Fail("State variable type must be either 'cohort' or 'stock'"); break; } return(returnValue / (InternalGrid[latCellIndex, lonCellIndex].CellEnvironment["Cell Area"][0])); }
/// <summary> /// Apply the changes from predation to prey cohorts, and update deltas for the predator cohort /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="actingCohort">The acting cohort</param> /// <param name="cellEnvironment">The environment in the current grid cell</param> /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param> /// <param name="madingleyCohortDefinitions">The functional group definitions for cohorts in the model</param> /// <param name="madingleyStockDefinitions">The functional group definitions for stocks in the model</param> /// <param name="trackProcesses">An instance of ProcessTracker to hold diagnostics for predation</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="specificLocations">Whether the model is being run for specific locations</param> /// <param name="outputDetail">The level of output detail used in this model run</param> /// <param name="initialisation">The Madingley Model initialisation</param> public void RunEating(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList <string, double[]> cellEnvironment, Dictionary <string, Dictionary <string, double> > deltas, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, ProcessTracker trackProcesses, uint currentTimestep, Boolean specificLocations, string outputDetail, MadingleyModelInitialisation initialisation) { if (trackProcesses.TrackProcesses) { Track = (RandomNumberGenerator.GetUniform() > 0.975) ? true : false; } TempDouble = 0.0; // Temporary variable to hold the total time spent eating + 1. Saves an extra calculation in CalculateAbundanceEaten double TotalTimeUnitsToHandlePlusOne = TimeUnitsToHandlePotentialFoodItems + 1; // Loop over potential prey functional groups foreach (int FunctionalGroup in _FunctionalGroupIndicesToEat) { // Loop over cohorts within the functional group for (int i = 0; i < NumberCohortsPerFunctionalGroupNoNewCohorts[FunctionalGroup]; i++) { // Get the individual body mass of this cohort _BodyMassPrey = gridCellCohorts[FunctionalGroup][i].IndividualBodyMass; // Calculate the actual abundance of prey eaten from this cohort if (gridCellCohorts[FunctionalGroup][i].CohortAbundance > 0) { // Calculate the actual abundance of prey eaten from this cohort _AbundancesEaten[FunctionalGroup][i] = CalculateAbundanceEaten(_PotentialAbundanceEaten[FunctionalGroup][i], _PredatorAbundanceMultipliedByTimeEating, TotalTimeUnitsToHandlePlusOne, gridCellCohorts[FunctionalGroup][i].CohortAbundance); } else { _AbundancesEaten[FunctionalGroup][i] = 0; } // Remove number of prey eaten from the prey cohort gridCellCohorts[FunctionalGroup][i].CohortAbundance -= _AbundancesEaten[FunctionalGroup][i]; gridCellCohorts[actingCohort].TrophicIndex += (_BodyMassPrey + gridCellCohorts[FunctionalGroup][i].IndividualReproductivePotentialMass) * _AbundancesEaten[FunctionalGroup][i] * gridCellCohorts[FunctionalGroup][i].TrophicIndex; // If the process tracker is set and output detail is set to high and the prey cohort has never been merged, // then track its mortality owing to predation if (trackProcesses.TrackProcesses) { if ((outputDetail == "high") && (gridCellCohorts[FunctionalGroup][i].CohortID.Count == 1) && AbundancesEaten[FunctionalGroup][i] > 0) { trackProcesses.RecordMortality((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], gridCellCohorts [FunctionalGroup][i].BirthTimeStep, currentTimestep, gridCellCohorts[FunctionalGroup][i].IndividualBodyMass, gridCellCohorts[FunctionalGroup][i].AdultMass, gridCellCohorts[FunctionalGroup][i].FunctionalGroupIndex, gridCellCohorts[FunctionalGroup][i].CohortID[0], AbundancesEaten[FunctionalGroup][i], "predation"); } // If the model is being run for specific locations and if track processes has been specified, then track the mass flow between // prey and predator if (specificLocations) { trackProcesses.RecordPredationMassFlow(currentTimestep, _BodyMassPrey, _BodyMassPredator, _BodyMassPrey * _AbundancesEaten[FunctionalGroup][i]); if (outputDetail == "high") { trackProcesses.TrackPredationTrophicFlow((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], gridCellCohorts[FunctionalGroup][i].FunctionalGroupIndex, gridCellCohorts[actingCohort].FunctionalGroupIndex, madingleyCohortDefinitions, (_AbundancesEaten[FunctionalGroup][i] * _BodyMassPrey), _BodyMassPredator, _BodyMassPrey, initialisation, cellEnvironment["Realm"][0] == 2.0); } } } // Check that the abundance eaten from this cohort is not negative // Commented out for the purposes of speed //Debug.Assert( _AbundancesEaten[FunctionalGroup][i].CompareTo(0.0) >= 0, // "Predation negative for this prey cohort" + actingCohort); // Create a temporary value to speed up the predation function // This is equivalent to the body mass of the prey cohort including reproductive potential mass, times the abundance eaten of the prey cohort, // divided by the abundance of the predator TempDouble += (_BodyMassPrey + gridCellCohorts[FunctionalGroup][i].IndividualReproductivePotentialMass) * _AbundancesEaten[FunctionalGroup][i] / _AbundancePredator; } } // Add the biomass eaten and assimilated by an individual to the delta biomass for the acting (predator) cohort deltas["biomass"]["predation"] = TempDouble * _PredatorAssimilationEfficiency; // Move the biomass eaten but not assimilated by an individual into the organic matter pool deltas["organicpool"]["predation"] = TempDouble * _PredatorNonAssimilation * _AbundancePredator; // Check that the delta biomass from eating for the acting cohort is not negative //Debug.Assert(deltas["biomass"]["predation"] >= 0, "Predation yields negative biomass"); // Calculate the total biomass eaten by the acting (predator) cohort _TotalBiomassEatenByCohort = deltas["biomass"]["predation"] * _AbundancePredator; }
/// <summary> /// Calculate the potential number of prey that could be gained through predation on each cohort in the grid cell /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="actingCohort">The acting cohort</param> /// <param name="cellEnvironment">The environment in the current grid cell</param> /// <param name="madingleyCohortDefinitions">The functional group definitions for cohorts in the model</param> /// <param name="madingleyStockDefinitions">The functional group definitions for stocks in the model</param> public void GetEatingPotentialTerrestrial(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList <string, double[]> cellEnvironment, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions) { BinnedPreyDensities = new double[gridCellCohorts.Count, NumberOfBins]; // Set the total eaten by the acting cohort to zero _TotalBiomassEatenByCohort = 0.0; // Set the total number of units to handle all potential prey individuals eaten to zero _TimeUnitsToHandlePotentialFoodItems = 0.0; // Get the individual body mass of the acting (predator) cohort _BodyMassPredator = gridCellCohorts[actingCohort].IndividualBodyMass; // Get the abundance of the acting (predator) cohort _AbundancePredator = gridCellCohorts[actingCohort].CohortAbundance; // Pre-calculate individual values for this predator _SpecificPredatorKillRateConstant = _KillRateConstant * Math.Pow(_BodyMassPredator, (_KillRateConstantMassExponent)); _SpecificPredatorTimeUnitsEatingPerGlobalTimeStep = _DeltaT * _ProportionOfTimeEating; _PredatorAssimilationEfficiency = _AssimilationEfficiency; _PredatorNonAssimilation = (1 - _AssimilationEfficiency); // When body sizes are less than one gram, we have a flat handling time relationship to stop small things have extraordinarily short handling times // if (_BodyMassPredator > 1.0) // { _ReferenceMassRatioScalingTerrestrial = HandlingTimeScalarTerrestrial * Math.Pow(_ReferenceMass / _BodyMassPredator, _HandlingTimeExponentTerrestrial); // } // else // { // _ReferenceMassRatioScalingTerrestrial = HandlingTimeScalarTerrestrial * _ReferenceMass / _BodyMassPredator; // } _PredatorAbundanceMultipliedByTimeEating = _AbundancePredator * _SpecificPredatorTimeUnitsEatingPerGlobalTimeStep; _PredatorLogOptimalPreyBodySizeRatio = gridCellCohorts[actingCohort[0]][actingCohort[1]].LogOptimalPreyBodySizeRatio; LogPredatorMassPlusPredatorLogOptimalPreyBodySizeRatio = Math.Log(_BodyMassPredator) + _PredatorLogOptimalPreyBodySizeRatio; // Calculate the abundance of prey in each of the prey mass bins PopulateBinnedPreyAbundance(gridCellCohorts, actingCohort, FunctionalGroupIndicesToEat, LogPredatorMassPlusPredatorLogOptimalPreyBodySizeRatio); // Loop over potential prey functional groups foreach (int FunctionalGroup in FunctionalGroupIndicesToEat) { // Loop over cohorts within the functional group for (int i = 0; i < NumberCohortsPerFunctionalGroupNoNewCohorts[FunctionalGroup]; i++) { // Get the body mass of individuals in this cohort _BodyMassPrey = gridCellCohorts[FunctionalGroup][i].IndividualBodyMass; // Get the bin number of this prey cohort PreyMassBinNumber = GetBinNumber(_BodyMassPrey, LogPredatorMassPlusPredatorLogOptimalPreyBodySizeRatio); // Check whether the prey cohort still exists in the model (i.e. body mass > 0) if ((0 < PreyMassBinNumber) && (PreyMassBinNumber < NumberOfBins) && (_BodyMassPrey > 0)) { // Calculate the potential abundance from this cohort eaten by the acting cohort _PotentialAbundanceEaten[FunctionalGroup][i] = CalculateExpectedNumberKilledTerrestrial( gridCellCohorts[FunctionalGroup][i].CohortAbundance, _BodyMassPrey, PreyMassBinNumber, FunctionalGroup, _BodyMassPredator, _CarnivoreFunctionalGroups[FunctionalGroup], _OmnivoreFunctionalGroups[FunctionalGroup], _OmnivoreFunctionalGroups[actingCohort[0]], _PredatorLogOptimalPreyBodySizeRatio); // Add the time required to handle the potential abundance eaten from this cohort to the cumulative total for all cohorts _TimeUnitsToHandlePotentialFoodItems += _PotentialAbundanceEaten[FunctionalGroup][i] * CalculateHandlingTimeTerrestrial(_BodyMassPrey); } else { // Assign a potential abundance eaten of zero _PotentialAbundanceEaten[FunctionalGroup][i] = 0.0; } } } // No cannibalism; do this outside the loop to speed up the calculations _TimeUnitsToHandlePotentialFoodItems -= PotentialAbundanceEaten[actingCohort[0]][actingCohort[1]] * CalculateHandlingTimeTerrestrial(_BodyMassPredator); PotentialAbundanceEaten[actingCohort[0]][actingCohort[1]] = 0.0; }
/// <summary> /// Record dispersal events in the dispersal tracker /// </summary> /// <param name="inboundCohorts">The cohorts arriving in a grid cell in the current time step</param> /// <param name="outboundCohorts">The cohorts leaving a ce ll in the current time step</param> /// <param name="outboundCohortWeights">The body masses of cohorts leaving the cell in the current time step</param> /// <param name="currentTimeStep">The current model time step</param> /// <param name="madingleyModelGrid">The model grid</param> public void RecordDispersal(uint[, ,] inboundCohorts, uint[, ,] outboundCohorts, List <double>[,] outboundCohortWeights, uint currentTimeStep, ModelGrid madingleyModelGrid) { // Loop through cells in the grid and write out the necessary data for (uint ii = 0; ii < outboundCohorts.GetLength(0); ii++) { for (uint jj = 0; jj < outboundCohorts.GetLength(1); jj++) { double MeanOutboundCohortWeight = new double(); // Calculate the mean weight of outbound cohorts (ignoring abundance) if (outboundCohortWeights[ii, jj].Count == 0) { MeanOutboundCohortWeight = 0.0; } else { MeanOutboundCohortWeight = outboundCohortWeights[ii, jj].Average(); } // Calculate the mean weight of all cohorts (ignoring abundance) List <double> TempList = new List <double>(); GridCellCohortHandler Temp1 = madingleyModelGrid.GetGridCellCohorts(ii, jj); // Loop through functional groups for (int kk = 0; kk < Temp1.Count; kk++) { // Loop through cohorts for (int hh = 0; hh < Temp1[kk].Count; hh++) { // Add the cohort weight to the list TempList.Add(Temp1[kk][hh].IndividualBodyMass); } } // Calculate the mean weight double MeanCohortWeight = new double(); if (TempList.Count == 0) { MeanCohortWeight = 0.0; } else { MeanCohortWeight = TempList.Average(); } string newline = Convert.ToString(currentTimeStep) + '\t' + Convert.ToString(ii) + '\t' + Convert.ToString(jj) + '\t' + Convert.ToString(madingleyModelGrid.GetCellLatitude(ii)) + '\t' + Convert.ToString(madingleyModelGrid.GetCellLongitude(jj)) + '\t' + Convert.ToString(outboundCohorts[ii, jj, 0]) + '\t' + Convert.ToString(outboundCohorts[ii, jj, 1]) + '\t' + Convert.ToString(outboundCohorts[ii, jj, 2]) + '\t' + Convert.ToString(outboundCohorts[ii, jj, 3]) + '\t' + Convert.ToString(outboundCohorts[ii, jj, 4]) + '\t' + Convert.ToString(outboundCohorts[ii, jj, 5]) + '\t' + Convert.ToString(outboundCohorts[ii, jj, 6]) + '\t' + Convert.ToString(outboundCohorts[ii, jj, 7]) + '\t' + Convert.ToString(inboundCohorts[ii, jj, 0]) + '\t' + Convert.ToString(inboundCohorts[ii, jj, 1]) + '\t' + Convert.ToString(inboundCohorts[ii, jj, 2]) + '\t' + Convert.ToString(inboundCohorts[ii, jj, 3]) + '\t' + Convert.ToString(inboundCohorts[ii, jj, 4]) + '\t' + Convert.ToString(inboundCohorts[ii, jj, 5]) + '\t' + Convert.ToString(inboundCohorts[ii, jj, 6]) + '\t' + Convert.ToString(inboundCohorts[ii, jj, 7]) + '\t' + Convert.ToString(String.Format("{0:.000000}", MeanOutboundCohortWeight) + '\t' + Convert.ToString(String.Format("{0:.000000}", MeanCohortWeight))); SyncedDispersalWriter.WriteLine(newline); } } }
/// <summary> /// Calculate the potential number of prey that could be gained through predation on each cohort in the grid cell /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="actingCohort">The acting cohort</param> /// <param name="cellEnvironment">The environment in the current grid cell</param> /// <param name="madingleyCohortDefinitions">The functional group definitions for cohorts in the model</param> /// <param name="madingleyStockDefinitions">The functional group definitions for stocks in the model</param> public void GetEatingPotentialMarine(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList <string, double[]> cellEnvironment, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions) { BinnedPreyDensities = new double[gridCellCohorts.Count, NumberOfBins]; // Set the total eaten by the acting cohort to zero _TotalBiomassEatenByCohort = 0.0; // Set the total number of units to handle all potential prey individuals eaten to zero _TimeUnitsToHandlePotentialFoodItems = 0.0; // Get the individual body mass of the acting (predator) cohort _BodyMassPredator = gridCellCohorts[actingCohort].IndividualBodyMass; // Get the abundance of the acting (predator) cohort _AbundancePredator = gridCellCohorts[actingCohort].CohortAbundance; // Pre-calculate individual values for this predator to speed things up _SpecificPredatorKillRateConstant = _KillRateConstant * Math.Pow(_BodyMassPredator, (_KillRateConstantMassExponent)); _SpecificPredatorTimeUnitsEatingPerGlobalTimeStep = _DeltaT * _ProportionOfTimeEating; _PredatorAssimilationEfficiency = _AssimilationEfficiency; _PredatorNonAssimilation = (1 - _AssimilationEfficiency); _DietIsAllSpecial = madingleyCohortDefinitions.GetTraitNames("Diet", actingCohort[0]) == "allspecial"; _PredatorLogOptimalPreyBodySizeRatio = gridCellCohorts[actingCohort[0]][actingCohort[1]].LogOptimalPreyBodySizeRatio; // If a filter feeder, then optimal body size is a value not a ratio: convert it to a ratio to ensure that all calculations work correctly if (_DietIsAllSpecial) { // Optimal body size is actually a value, not a ratio, so convert it to a ratio based on the present body size _PredatorLogOptimalPreyBodySizeRatio = Math.Log( Math.Exp(gridCellCohorts[actingCohort[0]][actingCohort[1]].LogOptimalPreyBodySizeRatio) / gridCellCohorts[actingCohort[0]][actingCohort[1]].IndividualBodyMass); } // Calculate the reference mass scaling ratio _ReferenceMassRatioScalingMarine = HandlingTimeScalarMarine * Math.Pow(_ReferenceMass / _BodyMassPredator, _HandlingTimeExponentMarine); _PredatorAbundanceMultipliedByTimeEating = _AbundancePredator * _SpecificPredatorTimeUnitsEatingPerGlobalTimeStep; LogPredatorMassPlusPredatorLogOptimalPreyBodySizeRatio = Math.Log(_BodyMassPredator) + _PredatorLogOptimalPreyBodySizeRatio; // Calculate the abundance of prey in each of the prey mass bins PopulateBinnedPreyAbundance(gridCellCohorts, actingCohort, FunctionalGroupIndicesToEat, LogPredatorMassPlusPredatorLogOptimalPreyBodySizeRatio); // Loop over potential prey functional groups foreach (int FunctionalGroup in FunctionalGroupIndicesToEat) { // Eating operates differently for planktivores // This can certainly be sped up if (_DietIsAllSpecial) { // Loop over cohorts within the functional group for (int i = 0; i < NumberCohortsPerFunctionalGroupNoNewCohorts[FunctionalGroup]; i++) { // Get the body mass of individuals in this cohort _BodyMassPrey = gridCellCohorts[FunctionalGroup][i].IndividualBodyMass; // Get the bin number of this prey cohort PreyMassBinNumber = GetBinNumber(_BodyMassPrey, LogPredatorMassPlusPredatorLogOptimalPreyBodySizeRatio); // Check whether // The prey cohort is within the feeding range of the predator // the prey cohort still exists in the model (i.e. body mass > 0) // Currently having whales etc eat everything, but preferentially feed on very small things (i.e. filter feeders) if ((_PlanktonFunctionalGroups[FunctionalGroup]) && (0 < PreyMassBinNumber) && (PreyMassBinNumber < NumberOfBins) && (_BodyMassPrey > 0)) { // Calculate the potential abundance from this cohort eaten by the acting cohort _PotentialAbundanceEaten[FunctionalGroup][i] = CalculateExpectedNumberKilledMarine( gridCellCohorts[FunctionalGroup][i].CohortAbundance, _BodyMassPrey, PreyMassBinNumber, FunctionalGroup, _BodyMassPredator, _CarnivoreFunctionalGroups[FunctionalGroup], _OmnivoreFunctionalGroups[FunctionalGroup], _OmnivoreFunctionalGroups[actingCohort[0]], _PredatorLogOptimalPreyBodySizeRatio); // Add the time required to handle the potential abundance eaten from this cohort to the cumulative total for all cohorts _TimeUnitsToHandlePotentialFoodItems += _PotentialAbundanceEaten[FunctionalGroup][i] * CalculateHandlingTimeMarine(_BodyMassPrey); } else { // Assign a potential abundance eaten of zero _PotentialAbundanceEaten[FunctionalGroup][i] = 0.0; } } } else { // Loop over cohorts within the functional group for (int i = 0; i < NumberCohortsPerFunctionalGroupNoNewCohorts[FunctionalGroup]; i++) { // Get the body mass of individuals in this cohort _BodyMassPrey = gridCellCohorts[FunctionalGroup][i].IndividualBodyMass; // Get the bin number of this prey cohort PreyMassBinNumber = GetBinNumber(_BodyMassPrey, LogPredatorMassPlusPredatorLogOptimalPreyBodySizeRatio); // Check whether // The prey cohort is within the feeding range of the predator // the prey cohort still exists in the model (i.e. body mass > 0) if ((0 < PreyMassBinNumber) && (PreyMassBinNumber < NumberOfBins) && (_BodyMassPrey > 0)) { // Calculate the potential abundance from this cohort eaten by the acting cohort _PotentialAbundanceEaten[FunctionalGroup][i] = CalculateExpectedNumberKilledMarine( gridCellCohorts[FunctionalGroup][i].CohortAbundance, _BodyMassPrey, PreyMassBinNumber, FunctionalGroup, _BodyMassPredator, _CarnivoreFunctionalGroups[FunctionalGroup], _OmnivoreFunctionalGroups[FunctionalGroup], _OmnivoreFunctionalGroups[actingCohort[0]], _PredatorLogOptimalPreyBodySizeRatio); // Add the time required to handle the potential abundance eaten from this cohort to the cumulative total for all cohorts _TimeUnitsToHandlePotentialFoodItems += _PotentialAbundanceEaten[FunctionalGroup][i] * CalculateHandlingTimeMarine(_BodyMassPrey); } else { // Assign a potential abundance eaten of zero _PotentialAbundanceEaten[FunctionalGroup][i] = 0.0; } } } } // No cannibalism; do this outside the loop to speed up the calculations _TimeUnitsToHandlePotentialFoodItems -= PotentialAbundanceEaten[actingCohort[0]][actingCohort[1]] * CalculateHandlingTimeMarine(_BodyMassPredator); PotentialAbundanceEaten[actingCohort[0]][actingCohort[1]] = 0.0; }
/// <summary> /// Run eating /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param> /// <param name="cellEnvironment">The environment in the current grid cell</param> /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param> /// <param name="madingleyCohortDefinitions">The definitions for cohort functional groups in the model</param> /// <param name="madingleyStockDefinitions">The definitions for stock functional groups in the model</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="trackProcesses">An instance of ProcessTracker to hold diagnostics for eating</param> /// <param name="partial">Thread-locked variables</param> /// <param name="specificLocations">Whether the model is being run for specific locations</param> /// <param name="outputDetail">The level of output detail being used for the current model run</param> /// <param name="currentMonth">The current model month</param> /// <param name="initialisation">The Madingley Model initialisation</param> public void RunEcologicalProcess(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList <string, double[]> cellEnvironment, Dictionary <string, Dictionary <string, double> > deltas, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, uint currentTimestep, ProcessTracker trackProcesses, ref ThreadLockedParallelVariables partial, Boolean specificLocations, string outputDetail, uint currentMonth, MadingleyModelInitialisation initialisation) { PreviousTrophicIndex = gridCellCohorts[actingCohort].TrophicIndex; //Reset this cohort's trohic index ready for calculation across its feeding this timetsstep gridCellCohorts[actingCohort].TrophicIndex = 0.0; // Get the nutrition source (herbivory, carnivory or omnivory) of the acting cohort string NutritionSource = madingleyCohortDefinitions.GetTraitNames("Nutrition source", gridCellCohorts[actingCohort].FunctionalGroupIndex); // Switch to the appropriate eating process(es) given the cohort's nutrition source switch (NutritionSource) { case "herbivore": // Get the assimilation efficiency for herbivory for this cohort from the functional group definitions Implementations["revised herbivory"].AssimilationEfficiency = madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup ("herbivory assimilation", gridCellCohorts[actingCohort].FunctionalGroupIndex); // Get the proportion of time spent eating for this cohort from the functional group definitions Implementations["revised herbivory"].ProportionTimeEating = gridCellCohorts[actingCohort].ProportionTimeActive; // Calculate the potential biomass available from herbivory if (cellEnvironment["Realm"][0] == 2.0) { Implementations["revised herbivory"].GetEatingPotentialMarine (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, madingleyCohortDefinitions, madingleyStockDefinitions); } else { Implementations["revised herbivory"].GetEatingPotentialTerrestrial (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, madingleyCohortDefinitions, madingleyStockDefinitions); } // Run herbivory to apply changes in autotroph biomass from herbivory and add biomass eaten to the delta arrays Implementations["revised herbivory"].RunEating (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, trackProcesses, currentTimestep, specificLocations, outputDetail, initialisation); break; case "carnivore": // Get the assimilation efficiency for predation for this cohort from the functional group definitions Implementations["revised predation"].AssimilationEfficiency = madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup ("carnivory assimilation", gridCellCohorts[actingCohort].FunctionalGroupIndex); Implementations["revised predation"].ProportionTimeEating = gridCellCohorts[actingCohort].ProportionTimeActive; // Calculate the potential biomass available from predation if (cellEnvironment["Realm"][0] == 2.0) { Implementations["revised predation"].GetEatingPotentialMarine (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, madingleyCohortDefinitions, madingleyStockDefinitions); } else { Implementations["revised predation"].GetEatingPotentialTerrestrial (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, madingleyCohortDefinitions, madingleyStockDefinitions); } // Run predation to apply changes in prey biomass from predation and add biomass eaten to the delta arrays Implementations["revised predation"].RunEating (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, trackProcesses, currentTimestep, specificLocations, outputDetail, initialisation); break; case "omnivore": // Get the assimilation efficiency for predation for this cohort from the functional group definitions Implementations["revised predation"].AssimilationEfficiency = madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup ("carnivory assimilation", gridCellCohorts[actingCohort].FunctionalGroupIndex); // Get the assimilation efficiency for herbivory for this cohort from the functional group definitions Implementations["revised herbivory"].AssimilationEfficiency = madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup ("herbivory assimilation", gridCellCohorts[actingCohort].FunctionalGroupIndex); // Get the proportion of time spent eating and assign to both the herbivory and predation implementations double ProportionTimeEating = gridCellCohorts[actingCohort].ProportionTimeActive; Implementations["revised predation"].ProportionTimeEating = ProportionTimeEating; Implementations["revised herbivory"].ProportionTimeEating = ProportionTimeEating; // Calculate the potential biomass available from herbivory if (cellEnvironment["Realm"][0] == 2.0) { Implementations["revised herbivory"].GetEatingPotentialMarine (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, madingleyCohortDefinitions, madingleyStockDefinitions); } else { Implementations["revised herbivory"].GetEatingPotentialTerrestrial (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, madingleyCohortDefinitions, madingleyStockDefinitions); } // Calculate the potential biomass available from predation if (cellEnvironment["Realm"][0] == 2.0) { Implementations["revised predation"].GetEatingPotentialMarine (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, madingleyCohortDefinitions, madingleyStockDefinitions); } else { Implementations["revised predation"].GetEatingPotentialTerrestrial (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, madingleyCohortDefinitions, madingleyStockDefinitions); } // Calculate the total handling time for all expected kills from predation and expected plant matter eaten in herbivory TotalTimeToEatForOmnivores = Implementations["revised herbivory"].TimeUnitsToHandlePotentialFoodItems + Implementations["revised predation"].TimeUnitsToHandlePotentialFoodItems; // Assign this total time to the relevant variables in both herbviory and predation, so that actual amounts eaten are calculated correctly Implementations["revised herbivory"].TimeUnitsToHandlePotentialFoodItems = TotalTimeToEatForOmnivores; Implementations["revised predation"].TimeUnitsToHandlePotentialFoodItems = TotalTimeToEatForOmnivores; // Run predation to update prey cohorts and delta biomasses for the acting cohort Implementations["revised predation"].RunEating (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, trackProcesses, currentTimestep, specificLocations, outputDetail, initialisation); // Run herbivory to update autotroph biomass and delta biomasses for the acting cohort Implementations["revised herbivory"].RunEating (gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, trackProcesses, currentTimestep, specificLocations, outputDetail, initialisation); break; default: // For nutrition source that are not supported, throw an error Debug.Fail("The model currently does not contain an eating model for nutrition source:" + NutritionSource); break; } // Check that the biomasses from predation and herbivory in the deltas is a number Debug.Assert(!double.IsNaN(deltas["biomass"]["predation"]), "BiomassFromEating is NaN"); Debug.Assert(!double.IsNaN(deltas["biomass"]["herbivory"]), "BiomassFromEating is NaN"); double biomassEaten = 0.0; if (madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup("carnivory assimilation", gridCellCohorts[actingCohort].FunctionalGroupIndex) > 0) { biomassEaten += (deltas["biomass"]["predation"] / madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup("carnivory assimilation", gridCellCohorts[actingCohort].FunctionalGroupIndex)); } if (madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup("herbivory assimilation", gridCellCohorts[actingCohort].FunctionalGroupIndex) > 0) { biomassEaten += (deltas["biomass"]["herbivory"] / madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup("herbivory assimilation", gridCellCohorts[actingCohort].FunctionalGroupIndex)); } if (biomassEaten > 0.0) { gridCellCohorts[actingCohort].TrophicIndex = 1 + (gridCellCohorts[actingCohort].TrophicIndex / (biomassEaten * gridCellCohorts[actingCohort].CohortAbundance)); } else { gridCellCohorts[actingCohort].TrophicIndex = PreviousTrophicIndex; } }
/// <summary> /// Calculate the rate of individuals in a cohort that die from background mortality in a model time step /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param> /// <param name="bodyMassIncludingChangeThisTimeStep">The body mass of individuals in the acting cohort, including body mass change this time step through eating and mortality</param> /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param> /// <param name="currentTimestep">The current model time step</param> /// <returns>The rate of individuals in the cohort that die from background mortality</returns> public double CalculateMortalityRate(GridCellCohortHandler gridCellCohorts, int[] actingCohort, double bodyMassIncludingChangeThisTimeStep, Dictionary <string, Dictionary <string, double> > deltas, uint currentTimestep) { // Convert from mortality rate per mortality formulation time step to mortality rate per model time step return(_MortalityRate * DeltaT); }
public uint[] WeightedMassOrderedIndices(GridCellCohortHandler gridCellCohorts, uint[][] cohortIndices, uint numberIndices) { NonStaticSimpleRNG random = new NonStaticSimpleRNG(); random.SetSeedFromSystemTime(); // A vector to hold indices of cohorts in order int[] MassOrderedIndices; double AboveMinMass = 0; double OverallMinMass = 1E9; double MaxMass = 0; int MaxFG = 0; int MaxC = 0; double MinMass = 1E9; int MinFG = 0; int MinC = 0; for (int ll = 0; ll < gridCellCohorts.Count; ll++) { // Loop over gridCellCohorts in the functional group for (int kk = 0; kk < gridCellCohorts[ll].Count(); kk++) { //Check if this cohort is the smallest if (gridCellCohorts[ll][kk].IndividualBodyMass.CompareTo(MaxMass) > 0) { MaxFG = ll; MaxC = kk; MaxMass = gridCellCohorts[ll][kk].IndividualBodyMass; } //Check if this cohort is the smallest if (gridCellCohorts[ll][kk].IndividualBodyMass.CompareTo(OverallMinMass) < 0) { MinFG = ll; MinC = kk; OverallMinMass = gridCellCohorts[ll][kk].IndividualBodyMass; } } } double MassRange = MaxMass - OverallMinMass; double logMassRange = Math.Log(MaxMass) - Math.Log(OverallMinMass); MassOrderedIndices = (int[])(object)this.RandomlyOrderedIndices(numberIndices);//this.MassOrderedIndices(gridCellCohorts,cohortIndices,numberIndices); uint[] OrderedIndices = new uint[numberIndices]; int[] Acted = new int[numberIndices]; int[] UseIndices; int[] UnActedIndices; uint UnActedCounter; double p = 0; int OrderIndex = 0; int[] Cinds; do { //UseIndices = MassOrderedIndices.Where((x,idx) => Acted[idx] == 0).ToArray(); UseIndices = new int[numberIndices - Acted.Sum()]; UnActedIndices = new int[numberIndices - Acted.Sum()]; UnActedCounter = 0; for (int i = 0; i < Acted.Length; i++) { if (Acted[i] == 0) { UnActedIndices[UnActedCounter] = i; UnActedCounter++; } } for (int i = 0; i < UnActedIndices.Length; i++) { UseIndices[i] = MassOrderedIndices[UnActedIndices[i]]; } foreach (uint i in UseIndices) { Cinds = FindJaggedArrayIndex(i, cohortIndices, numberIndices); p = (Math.Log(gridCellCohorts[Cinds[0]][Cinds[1]].IndividualBodyMass) - Math.Log(OverallMinMass)) / logMassRange; if (random.GetUniform() > p) { OrderedIndices[OrderIndex] = cohortIndices[Cinds[0]][Cinds[1]]; OrderIndex++; Acted[Array.FindIndex(MassOrderedIndices, row => row == i)] = 1; } } } while (Acted.Sum() < (int)(numberIndices * 0.8)); UseIndices = new int[numberIndices - Acted.Sum()]; UnActedIndices = new int[numberIndices - Acted.Sum()]; UnActedCounter = 0; for (int i = 0; i < Acted.Length; i++) { if (Acted[i] == 0) { UnActedIndices[UnActedCounter] = i; UnActedCounter++; } } for (int i = 0; i < UnActedIndices.Length; i++) { UseIndices[i] = MassOrderedIndices[UnActedIndices[i]]; } foreach (uint i in UseIndices) { Cinds = FindJaggedArrayIndex(i, cohortIndices, numberIndices); OrderedIndices[OrderIndex] = cohortIndices[Cinds[0]][Cinds[1]]; OrderIndex++; Acted[Array.FindIndex(MassOrderedIndices, row => row == i)] = 1; } //OrderedIndices[OrderIndex] = cohortIndices[MaxFG][MaxC]; return(OrderedIndices); }
/// <summary> /// Generate new cohorts from reproductive potential mass /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param> /// <param name="cellEnvironment">The environment of the current grid cell</param> /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param> /// <param name="madingleyCohortDefinitions">The definitions of cohort functional groups in the model</param> /// <param name="madingleyStockDefinitions">The definitions of stock functional groups in the model</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="tracker">An instance of ProcessTracker to hold diagnostics for reproduction</param> /// <param name="partial">Thread-locked variables</param> /// <param name="iteroparous">Whether the acting cohort is iteroparous, as opposed to semelparous</param> /// <param name="currentMonth">The current model month</param> public void RunReproductionEvents(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList <string, double[]> cellEnvironment, Dictionary <string, Dictionary <string, double> > deltas, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, uint currentTimestep, ProcessTracker tracker, ref ThreadLockedParallelVariables partial, bool iteroparous, uint currentMonth) { // Adult non-reproductive biomass lost by semelparous organisms double AdultMassLost; // Offspring cohort abundance double _OffspringCohortAbundance; // Mass ratio of body mass + reproductive mass to adult body mass double CurrentMassRatio; // Individual body mass including change this time step as a result of other ecological processes double BodyMassIncludingChangeThisTimeStep; // Offspring juvenile and adult body masses double[] OffspringJuvenileAndAdultBodyMasses = new double[2]; // Offspring cohort Cohort OffspringCohort; // Individual reproductive mass including change this time step as a result of other ecological processes double ReproductiveMassIncludingChangeThisTimeStep; // Calculate the biomass of an individual in this cohort including changes this time step from other ecological processes BodyMassIncludingChangeThisTimeStep = 0.0; foreach (var Biomass in deltas["biomass"]) { // Add the delta biomass to net biomass BodyMassIncludingChangeThisTimeStep += Biomass.Value; } BodyMassIncludingChangeThisTimeStep += gridCellCohorts[actingCohort].IndividualBodyMass; // Calculate the reproductive biomass of an individual in this cohort including changes this time step from other ecological processes ReproductiveMassIncludingChangeThisTimeStep = 0.0; foreach (var ReproBiomass in deltas["reproductivebiomass"]) { // Add the delta reproductive biomass to net biomass ReproductiveMassIncludingChangeThisTimeStep += ReproBiomass.Value; } ReproductiveMassIncludingChangeThisTimeStep += gridCellCohorts[actingCohort].IndividualReproductivePotentialMass; // Get the current ratio of total individual mass (including reproductive potential) to adult body mass CurrentMassRatio = (BodyMassIncludingChangeThisTimeStep + ReproductiveMassIncludingChangeThisTimeStep) / gridCellCohorts[actingCohort].AdultMass; // Must have enough mass to hit reproduction threshold criterion, and either (1) be in breeding season, or (2) be a marine cell (no breeding season in marine cells) if ((CurrentMassRatio > _MassRatioThreshold) && ((cellEnvironment["Breeding Season"][currentMonth] == 1.0) || ((cellEnvironment["Realm"][0] == 2.0)))) { // Iteroparous and semelparous organisms have different strategies if (iteroparous) { // Iteroparous organisms do not allocate any of their current non-reproductive biomass to reproduction AdultMassLost = 0.0; // Calculate the number of offspring that could be produced given the reproductive potential mass of individuals _OffspringCohortAbundance = gridCellCohorts[actingCohort].CohortAbundance * ReproductiveMassIncludingChangeThisTimeStep / gridCellCohorts[actingCohort].JuvenileMass; } else { // Semelparous organisms allocate a proportion of their current non-reproductive biomass (including the effects of other ecological processes) to reproduction AdultMassLost = _SemelparityAdultMassAllocation * BodyMassIncludingChangeThisTimeStep; // Calculate the number of offspring that could be produced given the reproductive potential mass of individuals _OffspringCohortAbundance = gridCellCohorts[actingCohort].CohortAbundance * (AdultMassLost + ReproductiveMassIncludingChangeThisTimeStep) / gridCellCohorts[actingCohort].JuvenileMass; } // Check that the abundance in the cohort to produce is greater than or equal to zero Debug.Assert(_OffspringCohortAbundance >= 0.0, "Offspring abundance < 0"); // Get the adult and juvenile masses of the offspring cohort OffspringJuvenileAndAdultBodyMasses = GetOffspringCohortProperties(gridCellCohorts, actingCohort, madingleyCohortDefinitions); // Update cohort abundance in case juvenile mass has been altered through 'evolution' _OffspringCohortAbundance = (_OffspringCohortAbundance * gridCellCohorts[actingCohort].JuvenileMass) / OffspringJuvenileAndAdultBodyMasses[0]; double TrophicIndex; switch (madingleyCohortDefinitions.GetTraitNames("nutrition source", actingCohort[0])) { case "herbivore": TrophicIndex = 2; break; case "omnivore": TrophicIndex = 2.5; break; case "carnivore": TrophicIndex = 3; break; default: Debug.Fail("Unexpected nutrition source trait value when assigning trophic index"); TrophicIndex = 0.0; break; } // Create the offspring cohort OffspringCohort = new Cohort((byte)actingCohort[0], OffspringJuvenileAndAdultBodyMasses[0], OffspringJuvenileAndAdultBodyMasses[1], OffspringJuvenileAndAdultBodyMasses[0], _OffspringCohortAbundance, Math.Exp(gridCellCohorts[actingCohort].LogOptimalPreyBodySizeRatio), (ushort)currentTimestep, gridCellCohorts[actingCohort].ProportionTimeActive, ref partial.NextCohortIDThreadLocked, TrophicIndex, tracker.TrackProcesses); // Add the offspring cohort to the grid cell cohorts array gridCellCohorts[actingCohort[0]].Add(OffspringCohort); // If track processes has been specified then add the new cohort to the process tracker if (tracker.TrackProcesses) { tracker.RecordNewCohort((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], currentTimestep, _OffspringCohortAbundance, gridCellCohorts[actingCohort].AdultMass, gridCellCohorts[actingCohort].FunctionalGroupIndex, gridCellCohorts[actingCohort].CohortID, (uint)partial.NextCohortIDThreadLocked); } // Subtract all of the reproductive potential mass of the parent cohort, which has been used to generate the new // cohort, from the delta reproductive potential mass and delta adult body mass deltas["reproductivebiomass"]["reproduction"] -= ReproductiveMassIncludingChangeThisTimeStep; deltas["biomass"]["reproduction"] -= AdultMassLost; } else { // Organism is not large enough, or it is not the breeding season, so take no action } }
/// <summary> /// Calculates functional diversity of cohorts in a grid cell as functional richness and functional diveregence (using the Rao Index) /// </summary> /// <param name="ecosystemModelGrid">The model grid</param> /// <param name="cohortDefinitions">The functional group definitions for cohorts in the model</param> /// <param name="cellIndices">The list of cell indices in the current model simulation</param> /// <param name="cellIndex">The index of the current cell within the list of cells to run</param> /// <returns>A pair of values representing the functional richness and functional divergence (functional richness currently disabled!)</returns> public double[] CalculateFunctionalDiversity(ModelGrid ecosystemModelGrid, FunctionalGroupDefinitions cohortDefinitions, List <uint[]> cellIndices, int cellIndex) { //Get the cohorts for the specified cell GridCellCohortHandler CellCohorts = ecosystemModelGrid.GetGridCellCohorts(cellIndices[cellIndex][0], cellIndices[cellIndex][1]); //Variable to hold the functional richness value for the current cohorts double FunctionalRichness; //Variable to hold the functional divergence value for the current cohorts double RaoFunctionalDivergence = 0.0; double[,] Distances = new double[CellCohorts.GetNumberOfCohorts(), CellCohorts.GetNumberOfCohorts()]; List <string> AllTraitNames = cohortDefinitions.GetAllTraitNames().ToList(); AllTraitNames.Remove("realm"); AllTraitNames.Remove("heterotroph/autotroph"); AllTraitNames.Remove("diet"); string[] TraitNames = AllTraitNames.ToArray(); //Define upper and lower limits for body mass double MinMass = cohortDefinitions.GetBiologicalPropertyAllFunctionalGroups("minimum mass").Min(); double MaxMass = cohortDefinitions.GetBiologicalPropertyAllFunctionalGroups("maximum mass").Max(); //Define upp and lower limits for trophic index double MaxTI = 40.0; double MinTI = 1.0; // Construct an array of functional trait values for each cohort // Rows are specific cohorts // Columns are the functional traits (these include different types: // quantative: current mass, trophic index // nominal: diet, reproductive strategy, mobility, metabolism Tuple <double[], string[]>[] CohortFunctionalTraits = new Tuple <double[], string[]> [CellCohorts.GetNumberOfCohorts()]; double[] IndividualBodyMasses = new double[CellCohorts.GetNumberOfCohorts()]; double[] TrophicIndex = new double[CellCohorts.GetNumberOfCohorts()]; string[][] CohortNominalTraitValues = new string[TraitNames.Length][]; for (int i = 0; i < TraitNames.Length; i++) { CohortNominalTraitValues[i] = new string[CellCohorts.GetNumberOfCohorts()]; } // Construct a vector of cohort biomass (in case we want to weight by them) double[] CohortTotalBiomasses = new double[CellCohorts.GetNumberOfCohorts()]; string[] TraitValues = new string[TraitNames.Length]; double[] QuantitativeTraitValues = new double[2]; int CohortNumberCounter = 0; for (int fg = 0; fg < CellCohorts.Count; fg++) { foreach (Cohort c in CellCohorts[fg]) { TraitValues = cohortDefinitions.GetTraitValues(TraitNames, fg); for (int ii = 0; ii < TraitValues.Length; ii++) { CohortNominalTraitValues[ii][CohortNumberCounter] = TraitValues[ii]; } IndividualBodyMasses[CohortNumberCounter] = c.IndividualBodyMass; TrophicIndex[CohortNumberCounter] = c.TrophicIndex; QuantitativeTraitValues[0] = c.IndividualBodyMass; QuantitativeTraitValues[1] = c.TrophicIndex; CohortFunctionalTraits[CohortNumberCounter] = new Tuple <double[], string[]>(QuantitativeTraitValues, TraitValues); CohortTotalBiomasses[CohortNumberCounter] = (c.IndividualBodyMass + c.IndividualReproductivePotentialMass) * c.CohortAbundance; CohortNumberCounter++; } } List <double[, ]> DistanceList = new List <double[, ]>(); DistanceList.Add(CalculateDistanceMatrix(IndividualBodyMasses, MaxMass, MinMass)); DistanceList.Add(CalculateDistanceMatrix(TrophicIndex, MaxTI, MinTI)); foreach (string[] t in CohortNominalTraitValues) { DistanceList.Add(CalculateDistanceMatrix(t)); } Distances = CalculateAggregateDistance(DistanceList); RaoFunctionalDivergence = RaoEntropy(Distances, CohortTotalBiomasses); return(new double[] { 0.0, RaoFunctionalDivergence }); }
/// <summary> /// Initialize an implementation of reproduction. This is only in here to satisfy the requirements of IEcologicalProcessWithinGridCells /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="madingleyCohortDefinitions">The definitions for cohort functional groups in the model</param> /// <param name="madingleyStockDefinitions">The definitions for stock functional groups in the model</param> /// <param name="implementationKey">The name of the reproduction implementation to initialize</param> public void InitializeEcologicalProcess(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, string implementationKey) { }
/// <summary> /// Remove individuals lost from cohorts through direct harvesting of animals /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="harvestingScenario">The scenario of direct harvesting of animals to apply</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 the harvesting scenario</param> /// <param name="impactSteps">The number of time steps to apply the scenario for</param> /// <param name="cellEnvironment">The environment in the current grid cell</param> /// <param name="impactCell">The index of the cell, within the list of all cells to run, to apply the scenario for</param> public void RemoveHarvestedIndividuals(GridCellCohortHandler gridCellCohorts, Tuple <string, double, double> harvestingScenario, uint currentTimestep, uint burninSteps, uint impactSteps, uint totalSteps, SortedList <string, double[]> cellEnvironment, Boolean impactCell, string globalModelTimestepUnits, FunctionalGroupDefinitions cohortFGs) { if (impactCell) { //If this is marine cell if (cellEnvironment["Realm"][0] == 2.0) { if (harvestingScenario.Item1 == "no") { // Do not apply any harvesting } else if (harvestingScenario.Item1 == "constant") { double TargetBiomass; if (FisheriesCatch != null) { TargetBiomass = (1000 * FisheriesCatch.ModelGridCatchTotal[Convert.ToInt32(cellEnvironment["LatIndex"][0]), Convert.ToInt32(cellEnvironment["LonIndex"][0])]) / 12.0; } else { TargetBiomass = harvestingScenario.Item2; } // If the burn-in period has been completed, then apply // the harvesting scenario if (currentTimestep > burninSteps) { ApplyHarvesting(gridCellCohorts, TargetBiomass, cellEnvironment); } } else if (harvestingScenario.Item1 == "fish-catch") { //Initialise an instance of ApplyFishingCatches for this cell if (currentTimestep == burninSteps) { ApplyCatches[Convert.ToInt32(cellEnvironment["LatIndex"][0]), Convert.ToInt32(cellEnvironment["LonIndex"][0])] = new ApplyFishingCatches(FisheriesCatch); } if (currentTimestep > burninSteps) { //Bin the cohorts of the current cell ApplyCatches[Convert.ToInt32(cellEnvironment["LatIndex"][0]), Convert.ToInt32(cellEnvironment["LonIndex"][0])].BinCohorts(gridCellCohorts, FisheriesCatch, cohortFGs); //Now remove the catch ApplyCatches[Convert.ToInt32(cellEnvironment["LatIndex"][0]), Convert.ToInt32(cellEnvironment["LonIndex"][0])].ApplyCatches(gridCellCohorts, FisheriesCatch, Convert.ToInt32(cellEnvironment["LatIndex"][0]), Convert.ToInt32(cellEnvironment["LonIndex"][0])); } } } else { if (harvestingScenario.Item1 == "no") { // Do not apply any harvesting } else if (harvestingScenario.Item1 == "constant") { // If the burn-in period has been completed, then apply // the harvesting scenario if (currentTimestep > burninSteps) { ApplyHarvesting(gridCellCohorts, harvestingScenario.Item2, cellEnvironment); } } else if (harvestingScenario.Item1 == "temporary") { // If the burn-in period has been completed and the period of impact has not elapsed, // then apply the harvesting scenario if ((currentTimestep > burninSteps) && (currentTimestep <= (burninSteps + impactSteps))) { ApplyHarvesting(gridCellCohorts, harvestingScenario.Item2, cellEnvironment); } } else if (harvestingScenario.Item1 == "escalating") { // If the spin-up period has been completed, then apply a level of harvesting // according to the number of time-steps that have elapsed since the spin-up ended if (currentTimestep > burninSteps) { // Calculate the target biomass for harvesting based on the number of time steps that have elapsed since the spin-up double TargetBiomass = (Math.Min(50000, (((currentTimestep - burninSteps) / 12.0) * harvestingScenario.Item2))); // Apply the harvesting scenario using the calculated target biomass ApplyHarvesting(gridCellCohorts, TargetBiomass, cellEnvironment); } } else if (harvestingScenario.Item1 == "temp-escalating-declining") { // If the spin-up period has been completed, then apply a level of harvesting // according to the number of time-steps that have elapsed since the spin-up ended if ((currentTimestep > burninSteps) && (currentTimestep <= (burninSteps + impactSteps))) { // Calculate the target biomass for harvesting based on the number of time steps that have elapsed since the spin-up double TargetBiomass = (Math.Min(50000, (((currentTimestep - burninSteps) / 12.0) * harvestingScenario.Item2))); // Apply the harvesting scenario using the calculated target biomass ApplyHarvesting(gridCellCohorts, TargetBiomass, cellEnvironment); } else if (currentTimestep > (burninSteps + impactSteps)) { // Calculate the target biomass for harvesting based on the number of time steps that have elapsed since the spin-up double TargetBiomass = (Math.Min(50000, ((-(totalSteps - currentTimestep) / 12.0) * harvestingScenario.Item2))); // Apply the harvesting scenario using the calculated target biomass ApplyHarvesting(gridCellCohorts, TargetBiomass, cellEnvironment); } } else if (harvestingScenario.Item1 == "temp-escalating") { // If the spin-up period has been completed and the period of impact has not elapsed, // then remove a proportion of plant matter // according to the number of time-steps that have elapsed since the spin-up ended if ((currentTimestep > burninSteps) && (currentTimestep <= (burninSteps + impactSteps))) { // Calculate the target biomass for harvesting based on the number of time steps that have elapsed since the spin-up double TargetBiomass = (Math.Min(50000, (((currentTimestep - burninSteps) / 12.0) * harvestingScenario.Item2))); // Apply the harvesting scenarion using the calculated target biomass ApplyHarvesting(gridCellCohorts, TargetBiomass, cellEnvironment); } } else if (harvestingScenario.Item1 == "temp-escalating-const-rate") { // If the spin-up period has been completed and the period of impact (specified by the third scenario element // has not elapsed, // then remove a portion of plant matter // according to the number of time-steps that have elapsed since the spin-up ended int ConstImpactSteps = Convert.ToInt32(harvestingScenario.Item3 * Utilities.ConvertTimeUnits("year", globalModelTimestepUnits)); if ((currentTimestep > burninSteps) && (currentTimestep <= (burninSteps + ConstImpactSteps))) { // Calculate the target biomass for harvesting based on the number of time steps that have elapsed since the spin-up double TargetBiomass = (Math.Min(200000, (((currentTimestep - burninSteps) / 12.0) * harvestingScenario.Item2))); // Apply the harvesting scenarion using the calculated target biomass ApplyHarvesting(gridCellCohorts, TargetBiomass, cellEnvironment); } } else if (harvestingScenario.Item1 == "temp-escalating-const-rate-duration") { // If the spin-up period has been completed and the period of impact (specified by the third scenario element // has not elapsed, // then remove a proportion of plant matter // according to the number of time-steps that have elapsed since the spin-up ended int ConstImpactSteps = Convert.ToInt32(harvestingScenario.Item3 * Utilities.ConvertTimeUnits("year", globalModelTimestepUnits)); if ((currentTimestep > burninSteps) && (currentTimestep <= (burninSteps + impactSteps))) { //gridCellStocks[actingStock].TotalBiomass -= gridCellStocks[actingStock].TotalBiomass * // (Math.Min(1.0, (((currentTimestep - burninSteps) / 12.0) * humanNPPScenario.Item2))); double TargetBiomass = (Math.Min(200000, Math.Min(((ConstImpactSteps / 12.0) * harvestingScenario.Item2), (((currentTimestep - burninSteps) / 12.0) * harvestingScenario.Item2)))); // Apply the harvesting scenarion using the calculated target biomass ApplyHarvesting(gridCellCohorts, TargetBiomass, cellEnvironment); } } else { Debug.Fail("There is no method for the harvesting scenario specified"); } } } }
/// <summary> /// Update the individual and reproductive body masses of the acting cohort according to the delta biomasses from the ecological processes /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param> /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="tracker">A process tracker</param> /// <param name="cellEnvironment">The cell environment</param> private void UpdateBiomass(GridCellCohortHandler gridCellCohorts, int[] actingCohort, Dictionary <string, Dictionary <string, double> > deltas, uint currentTimestep, ProcessTracker tracker, SortedList <string, double[]> cellEnvironment) { // Extract the biomass deltas from the sorted list of all deltas Dictionary <string, double> deltaBiomass = deltas["biomass"]; if (tracker.TrackProcesses) { // Calculate net growth of individuals in this cohort double growth = deltaBiomass["predation"] + deltaBiomass["herbivory"] + deltaBiomass["metabolism"]; tracker.TrackTimestepGrowth((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], currentTimestep, gridCellCohorts[actingCohort].IndividualBodyMass, gridCellCohorts[actingCohort].FunctionalGroupIndex, growth, deltaBiomass["metabolism"], deltaBiomass["predation"], deltaBiomass["herbivory"]); } // Get all keys from the biomass deltas sorted list string[] KeyStrings = deltaBiomass.Keys.ToArray(); // Variable to calculate net biomass change to check that cohort individual body mass will not become negative double NetBiomass = 0.0; // Loop over all biomass deltas foreach (string key in KeyStrings) { // Update net biomass change NetBiomass += deltaBiomass[key]; } double BiomassCheck = 0.0; Boolean NetToBeApplied = true; // If cohort abundance is greater than zero, then check that the calculated net biomas will not make individual body mass become negative if (gridCellCohorts[actingCohort].CohortAbundance.CompareTo(0.0) > 0) { string output = "Biomass going negative, acting cohort: " + actingCohort[0].ToString() + ", " + actingCohort[1].ToString(); BiomassCheck = gridCellCohorts[actingCohort].IndividualBodyMass + NetBiomass; Debug.Assert((BiomassCheck).CompareTo(0.0) >= 0, output); } //Loop over all keys in the abundance deltas sorted list foreach (string key in KeyStrings) { // If cohort abundance is zero, then set cohort individual body mass to zero and reset the biomass delta to zero, // otherwise update cohort individual body mass and reset the biomass delta to zero if (gridCellCohorts[actingCohort].CohortAbundance.CompareTo(0.0) == 0) { gridCellCohorts[actingCohort].IndividualBodyMass = 0.0; deltaBiomass[key] = 0.0; } else { if (NetToBeApplied) { gridCellCohorts[actingCohort].IndividualBodyMass += NetBiomass; NetToBeApplied = false; } //gridCellCohorts[actingCohort].IndividualBodyMass += deltaBiomass[key]; deltaBiomass[key] = 0.0; } } // Check that individual body mass is still greater than zero Debug.Assert(gridCellCohorts[actingCohort].IndividualBodyMass.CompareTo(0.0) >= 0, "biomass < 0"); // If the current individual body mass is the largest that has been achieved by this cohort, then update the maximum achieved // body mass tracking variable for the cohort if (gridCellCohorts[actingCohort].IndividualBodyMass > gridCellCohorts[actingCohort].MaximumAchievedBodyMass) { gridCellCohorts[actingCohort].MaximumAchievedBodyMass = gridCellCohorts[actingCohort].IndividualBodyMass; } // Extract the reproductive biomass deltas from the sorted list of all deltas Dictionary <string, double> deltaReproductiveBiomass = deltas["reproductivebiomass"]; // Get all keys from the biomass deltas sorted list string[] KeyStrings2 = deltas["reproductivebiomass"].Keys.ToArray(); // Variable to calculate net reproductive biomass change to check that cohort individual body mass will not become negative double NetReproductiveBiomass = 0.0; // Loop over all reproductive biomass deltas foreach (string key in KeyStrings2) { // Update net reproductive biomass change NetReproductiveBiomass += deltaReproductiveBiomass[key]; } //Loop over all keys in the abundance deltas sorted list foreach (string key in KeyStrings2) { // If cohort abundance is zero, then set cohort reproductive body mass to zero and reset the biomass delta to zero, // otherwise update cohort reproductive body mass and reset the biomass delta to zero if (gridCellCohorts[actingCohort].CohortAbundance.CompareTo(0.0) == 0) { gridCellCohorts[actingCohort].IndividualReproductivePotentialMass = 0.0; deltaReproductiveBiomass[key] = 0.0; } else { gridCellCohorts[actingCohort].IndividualReproductivePotentialMass += deltaReproductiveBiomass[key]; deltaReproductiveBiomass[key] = 0.0; } } // Note that maturity time step is set in TReproductionBasic }
public double[] CalculateFunctionalRichness(ModelGrid ecosystemModelGrid, FunctionalGroupDefinitions cohortDefinitions, List <uint[]> cellIndices, int cellIndex, string trait) { //Get the cohorts for the specified cell GridCellCohortHandler CellCohorts = ecosystemModelGrid.GetGridCellCohorts(cellIndices[cellIndex][0], cellIndices[cellIndex][1]); double MinCurrentTraitValue = double.MaxValue; double MaxCurrentTraitValue = double.MinValue; double MinModelTraitValue = 0.0; double MaxModelTraitValue = 0.0; switch (trait.ToLower()) { case "biomass": foreach (var CohortList in CellCohorts) { foreach (var cohort in CohortList) { if (cohort.IndividualBodyMass < MinCurrentTraitValue) { MinCurrentTraitValue = cohort.IndividualBodyMass; } if (cohort.IndividualBodyMass > MaxCurrentTraitValue) { MaxCurrentTraitValue = cohort.IndividualBodyMass; } } } //Define upper and lower limits for body mass MinModelTraitValue = cohortDefinitions.GetBiologicalPropertyAllFunctionalGroups("minimum mass").Min(); MaxModelTraitValue = cohortDefinitions.GetBiologicalPropertyAllFunctionalGroups("maximum mass").Max(); break; case "trophic index": foreach (var CohortList in CellCohorts) { foreach (var cohort in CohortList) { if (cohort.TrophicIndex < MinCurrentTraitValue) { MinCurrentTraitValue = cohort.TrophicIndex; } if (cohort.TrophicIndex > MaxCurrentTraitValue) { MaxCurrentTraitValue = cohort.TrophicIndex; } } } //Define upper and lower limits for body mass MinModelTraitValue = MinTI; MaxModelTraitValue = MaxTI; break; default: Debug.Fail("Trait not recognised in calculation of ecosystem metrics: " + trait); break; } Debug.Assert((MaxModelTraitValue - MinModelTraitValue) > 0.0, "Division by zero or negative model trait values in calculation of functional richness"); double[] NewArray = { (MaxCurrentTraitValue - MinCurrentTraitValue) / (MaxModelTraitValue - MinModelTraitValue), MinCurrentTraitValue, MaxCurrentTraitValue }; return(NewArray); }
/// <summary> /// Run mortality /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="actingCohort">The position of the acting cohort in the jagged array of grid cell cohorts</param> /// <param name="cellEnvironment">The environment in the current grid cell</param> /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param> /// <param name="madingleyCohortDefinitions">The definitions for cohort functional groups in the model</param> /// <param name="madingleyStockDefinitions">The definitions for stock functional groups in the model</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="trackProcesses">An instance of ProcessTracker to hold diagnostics for mortality</param> /// <param name="partial">Thread-locked variables</param> /// <param name="specificLocations">Whether the model is being run for specific locations</param> /// <param name="outputDetail">The level output detail being used for the current model run</param> /// <param name="currentMonth">The current model month</param> /// <param name="initialisation">The Madingley Model initialisation</param> public void RunEcologicalProcess(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList <string, double[]> cellEnvironment, Dictionary <string, Dictionary <string, double> > deltas, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, uint currentTimestep, ProcessTracker trackProcesses, ref ThreadLockedParallelVariables partial, Boolean specificLocations, string outputDetail, uint currentMonth, MadingleyModelInitialisation initialisation) { // Variables to hold the mortality rates double MortalityRateBackground; double MortalityRateSenescence; double MortalityRateStarvation; // Variable to hold the total abundance lost to all forms of mortality double MortalityTotal; // Individual body mass including change this time step as a result of other ecological processes double BodyMassIncludingChangeThisTimeStep; // Individual reproductive mass including change this time step as a result of other ecological processes double ReproductiveMassIncludingChangeThisTimeStep; // Calculate the body mass of individuals in this cohort including mass gained through eating this time step, up to but not exceeding adult body mass for this cohort. // Should be fine because these deductions are made in the reproduction implementation, but use Math.Min to double check. BodyMassIncludingChangeThisTimeStep = 0.0; // Loop over all items in the biomass deltas foreach (var Biomass in deltas["biomass"]) { // Add the delta biomass to net biomass BodyMassIncludingChangeThisTimeStep += Biomass.Value; } BodyMassIncludingChangeThisTimeStep = Math.Min(gridCellCohorts[actingCohort].AdultMass, BodyMassIncludingChangeThisTimeStep + gridCellCohorts[actingCohort].IndividualBodyMass); // Temporary variable to hold net reproductive biomass change of individuals in this cohort as a result of other ecological processes ReproductiveMassIncludingChangeThisTimeStep = 0.0; // Loop over all items in the biomass deltas foreach (var Biomass in deltas["reproductivebiomass"]) { // Add the delta biomass to net biomass ReproductiveMassIncludingChangeThisTimeStep += Biomass.Value; } ReproductiveMassIncludingChangeThisTimeStep += gridCellCohorts[actingCohort].IndividualReproductivePotentialMass; // Check to see if the cohort has already been killed by predation etc if ((BodyMassIncludingChangeThisTimeStep).CompareTo(0.0) <= 0) { // If individual body mass is not greater than zero, then all individuals become extinct MortalityTotal = gridCellCohorts[actingCohort].CohortAbundance; } else { // Calculate background mortality rate MortalityRateBackground = Implementations["basic background mortality"].CalculateMortalityRate(gridCellCohorts, actingCohort, BodyMassIncludingChangeThisTimeStep, deltas, currentTimestep); // If the cohort has matured, then calculate senescence mortality rate, otherwise set rate to zero if (gridCellCohorts[actingCohort].MaturityTimeStep != uint.MaxValue) { MortalityRateSenescence = Implementations["basic senescence mortality"].CalculateMortalityRate(gridCellCohorts, actingCohort, BodyMassIncludingChangeThisTimeStep, deltas, currentTimestep); } else { MortalityRateSenescence = 0.0; } // Calculate the starvation mortality rate based on individual body mass and maximum body mass ever // achieved by this cohort MortalityRateStarvation = Implementations["basic starvation mortality"].CalculateMortalityRate(gridCellCohorts, actingCohort, BodyMassIncludingChangeThisTimeStep, deltas, currentTimestep); // Calculate the number of individuals that suffer mortality this time step from all sources of mortality MortalityTotal = (1 - Math.Exp(-MortalityRateBackground - MortalityRateSenescence - MortalityRateStarvation)) * gridCellCohorts[actingCohort].CohortAbundance; } // If the process tracker is on and output detail is set to high and this cohort has not been merged yet, then record // the number of individuals that have died if (trackProcesses.TrackProcesses && (outputDetail == "high") && (!gridCellCohorts[actingCohort].Merged)) { trackProcesses.RecordMortality((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], gridCellCohorts[actingCohort].BirthTimeStep, currentTimestep, gridCellCohorts[actingCohort].IndividualBodyMass, gridCellCohorts[actingCohort].AdultMass, gridCellCohorts[actingCohort].FunctionalGroupIndex, gridCellCohorts[actingCohort].CohortID[0], MortalityTotal, "sen/bg/starv"); } // Remove individuals that have died from the delta abundance for this cohort deltas["abundance"]["mortality"] = -MortalityTotal; // Add the biomass of individuals that have died to the delta biomass in the organic pool (including reproductive // potential mass, and mass gained through eating, and excluding mass lost through metabolism) deltas["organicpool"]["mortality"] = MortalityTotal * (BodyMassIncludingChangeThisTimeStep + ReproductiveMassIncludingChangeThisTimeStep); }
/// <summary> /// Merge cohorts for responsive dispersal only; merges identical cohorts, no matter how many times they have been merged before /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <returns>Number of cohorts merged</returns> public int MergeForResponsiveDispersalOnly(GridCellCohortHandler gridCellCohorts) { // Variable to track the total number of cohorts merged int NumberCombined = 0; //Loop over all functional groups for (int i = 0; i < gridCellCohorts.Count; i++) { // Loop over each cohort in each functional group for (int j = 0; j < gridCellCohorts[i].Count; j++) { // If that cohort has abundance greater than zero then check if there are similar cohorts that could be merged with it if (gridCellCohorts[i][j].CohortAbundance > 0) { // Loop over all cohorts above the jth in the cohort list for (int k = j + 1; k < gridCellCohorts[i].Count; k++) { // Check that kth cohort has abunance and that the two cohorts being compared do not represent a juvenile adult pairing if (gridCellCohorts[i][k].CohortAbundance > 0 && ((gridCellCohorts[i][j].MaturityTimeStep == uint.MaxValue && gridCellCohorts[i][k].MaturityTimeStep == uint.MaxValue) || (gridCellCohorts[i][j].MaturityTimeStep < uint.MaxValue && gridCellCohorts[i][k].MaturityTimeStep < uint.MaxValue))) { //Check that the individual masses are widentical if (gridCellCohorts[i][j].IndividualBodyMass == gridCellCohorts[i][k].IndividualBodyMass) { //Check that the adult masses are similar if (gridCellCohorts[i][j].AdultMass == gridCellCohorts[i][k].AdultMass) { //Check that the juvenile masses are similar if (gridCellCohorts[i][j].JuvenileMass == gridCellCohorts[i][k].JuvenileMass) { //Check that the Maximum achieved mass is similar if (gridCellCohorts[i][j].MaximumAchievedBodyMass == gridCellCohorts[i][k].MaximumAchievedBodyMass) { // In half of cases, add the abundance of the second cohort to that of the first and maintain the properties of the first if (RandomNumberGenerator.GetUniform() < 0.5) { // Add the abundance of the second cohort to that of the first gridCellCohorts[i][j].CohortAbundance += (gridCellCohorts[i][k].CohortAbundance * gridCellCohorts[i][k].IndividualBodyMass) / gridCellCohorts[i][j].IndividualBodyMass; // Set the abundance of the second cohort to zero gridCellCohorts[i][k].CohortAbundance = 0.0; // Add the reproductive potential mass of the second cohort to that of the first gridCellCohorts[i][j].IndividualReproductivePotentialMass += (gridCellCohorts[i][k].IndividualReproductivePotentialMass * gridCellCohorts[i][k].CohortAbundance) / gridCellCohorts[i][j].CohortAbundance; // Designate both cohorts as having merged gridCellCohorts[i][j].Merged = true; gridCellCohorts[i][k].Merged = true; } // In all other cases, add the abundance of the first cohort to that of the second and maintain the properties of the second else { // Add the abundance of the first cohort to that of the second gridCellCohorts[i][k].CohortAbundance += (gridCellCohorts[i][j].CohortAbundance * gridCellCohorts[i][j].IndividualBodyMass) / gridCellCohorts[i][k].IndividualBodyMass; // Set the abundance of the second cohort to zero gridCellCohorts[i][j].CohortAbundance = 0.0; // Add the reproductive potential mass of the second cohort to that of the first gridCellCohorts[i][k].IndividualReproductivePotentialMass += (gridCellCohorts[i][j].IndividualReproductivePotentialMass * gridCellCohorts[i][j].CohortAbundance) / gridCellCohorts[i][k].CohortAbundance; // Designate both cohorts as having merged gridCellCohorts[i][j].Merged = true; gridCellCohorts[i][k].Merged = true; } // Increment the number of cohorts combined NumberCombined += 1; } } } } } } } } } return(NumberCombined); }
public InputModelState(string outputPath, string filename, ModelGrid ecosystemModelGrid, List <uint[]> cellList) { //Set the input state flag to be true _InputState = true; // Construct the string required to access the file using Scientific Dataset string _ReadFileString = "msds:nc?file=input/ModelStates/" + filename + ".nc&openMode=readOnly"; // Open the data file using Scientific Dataset DataSet StateDataSet = DataSet.Open(_ReadFileString); float[] Latitude = StateDataSet.GetData <float[]>("Latitude"); float[] Longitude = StateDataSet.GetData <float[]>("Longitude"); float[] CohortFunctionalGroup = StateDataSet.GetData <float[]>("Cohort Functional Group"); float[] Cohort = StateDataSet.GetData <float[]>("Cohort"); float[] StockFunctionalGroup = StateDataSet.GetData <float[]>("Stock Functional Group"); float[] Stock = StateDataSet.GetData <float[]>("Stock"); // Check that the longitudes and latitudes in the input state match the cell environment for (int la = 0; la < Latitude.Length; la++) { Debug.Assert(ecosystemModelGrid.GetCellEnvironment((uint)la, 0)["Latitude"][0] == Latitude[la], "Error: input-state grid doesn't match current model grid"); } for (int lo = 0; lo < Longitude.Length; lo++) { Debug.Assert(ecosystemModelGrid.GetCellEnvironment(0, (uint)lo)["Longitude"][0] == Longitude[lo], "Error: input-state grid doesn't match current model grid"); } List <double[, , ]> CohortJuvenileMass = new List <double[, , ]>(); List <double[, , ]> CohortAdultMass = new List <double[, , ]>(); List <double[, , ]> CohortIndividualBodyMass = new List <double[, , ]>(); List <double[, , ]> CohortCohortAbundance = new List <double[, , ]>(); List <double[, , ]> CohortLogOptimalPreyBodySizeRatio = new List <double[, , ]>(); List <double[, , ]> CohortBirthTimeStep = new List <double[, , ]>(); List <double[, , ]> CohortProportionTimeActive = new List <double[, , ]>(); List <double[, , ]> CohortTrophicIndex = new List <double[, , ]>(); double[,,,] tempData = new double[Latitude.Length, Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length]; tempData = StateDataSet.GetData <double[, , , ]>("CohortJuvenileMass"); for (int la = 0; la < Latitude.Length; la++) { CohortJuvenileMass.Add(new double[Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length]); } foreach (uint[] cell in cellList) { for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++) { for (int c = 0; c < Cohort.Length; c++) { CohortJuvenileMass[(int)cell[0]][cell[1], fg, c] = tempData[ cell[0], cell[1], fg, c]; } } } tempData = StateDataSet.GetData <double[, , , ]>("CohortAdultMass"); for (int la = 0; la < Latitude.Length; la++) { CohortAdultMass.Add(new double[Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length]); } foreach (uint[] cell in cellList) { for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++) { for (int c = 0; c < Cohort.Length; c++) { CohortAdultMass[(int)cell[0]][cell[1], fg, c] = tempData[ cell[0], cell[1], fg, c]; } } } tempData = StateDataSet.GetData <double[, , , ]>("CohortIndividualBodyMass"); for (int la = 0; la < Latitude.Length; la++) { CohortIndividualBodyMass.Add(new double[Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length]); } foreach (uint[] cell in cellList) { for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++) { for (int c = 0; c < Cohort.Length; c++) { CohortIndividualBodyMass[(int)cell[0]][cell[1], fg, c] = tempData[ cell[0], cell[1], fg, c]; } } } tempData = StateDataSet.GetData <double[, , , ]>("CohortCohortAbundance"); for (int la = 0; la < Latitude.Length; la++) { CohortCohortAbundance.Add(new double[Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length]); } foreach (uint[] cell in cellList) { for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++) { for (int c = 0; c < Cohort.Length; c++) { CohortCohortAbundance[(int)cell[0]][cell[1], fg, c] = tempData[ cell[0], cell[1], fg, c]; } } } tempData = StateDataSet.GetData <double[, , , ]>("CohortLogOptimalPreyBodySizeRatio"); for (int la = 0; la < Latitude.Length; la++) { CohortLogOptimalPreyBodySizeRatio.Add(new double[Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length]); } foreach (uint[] cell in cellList) { for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++) { for (int c = 0; c < Cohort.Length; c++) { CohortLogOptimalPreyBodySizeRatio[(int)cell[0]][cell[1], fg, c] = tempData[ cell[0], cell[1], fg, c]; } } } tempData = StateDataSet.GetData <double[, , , ]>("CohortBirthTimeStep"); for (int la = 0; la < Latitude.Length; la++) { CohortBirthTimeStep.Add(new double[Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length]); } foreach (uint[] cell in cellList) { for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++) { for (int c = 0; c < Cohort.Length; c++) { CohortBirthTimeStep[(int)cell[0]][cell[1], fg, c] = tempData[ cell[0], cell[1], fg, c]; } } } tempData = StateDataSet.GetData <double[, , , ]>("CohortProportionTimeActive"); for (int la = 0; la < Latitude.Length; la++) { CohortProportionTimeActive.Add(new double[Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length]); } foreach (uint[] cell in cellList) { for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++) { for (int c = 0; c < Cohort.Length; c++) { CohortProportionTimeActive[(int)cell[0]][cell[1], fg, c] = tempData[ cell[0], cell[1], fg, c]; } } } tempData = StateDataSet.GetData <double[, , , ]>("CohortTrophicIndex"); for (int la = 0; la < Latitude.Length; la++) { CohortTrophicIndex.Add(new double[Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length]); } foreach (uint[] cell in cellList) { for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++) { for (int c = 0; c < Cohort.Length; c++) { CohortTrophicIndex[(int)cell[0]][cell[1], fg, c] = tempData[ cell[0], cell[1], fg, c]; } } } _GridCellCohorts = new GridCellCohortHandler[Latitude.Length, Longitude.Length]; long temp = 0; for (int cell = 0; cell < cellList.Count; cell++) { _GridCellCohorts[cellList[cell][0], cellList[cell][1]] = new GridCellCohortHandler(CohortFunctionalGroup.Length); for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++) { _GridCellCohorts[cellList[cell][0], cellList[cell][1]][fg] = new List <Cohort>(); for (int c = 0; c < Cohort.Length; c++) { if (CohortCohortAbundance[(int)cellList[cell][0]][cellList[cell][1], fg, c] > 0.0) { Cohort TempCohort = new Cohort( (byte)fg, CohortJuvenileMass[(int)cellList[cell][0]][cellList[cell][1], fg, c], CohortAdultMass[(int)cellList[cell][0]][cellList[cell][1], fg, c], CohortIndividualBodyMass[(int)cellList[cell][0]][cellList[cell][1], fg, c], CohortCohortAbundance[(int)cellList[cell][0]][cellList[cell][1], fg, c], Math.Exp(CohortLogOptimalPreyBodySizeRatio[(int)cellList[cell][0]][cellList[cell][1], fg, c]), Convert.ToUInt16(CohortBirthTimeStep[(int)cellList[cell][0]][cellList[cell][1], fg, c]), CohortProportionTimeActive[(int)cellList[cell][0]][cellList[cell][1], fg, c], ref temp, CohortTrophicIndex[(int)cellList[cell][0]][cellList[cell][1], fg, c], false); _GridCellCohorts[cellList[cell][0], cellList[cell][1]][fg].Add(TempCohort); } } } } CohortJuvenileMass.RemoveRange(0, CohortJuvenileMass.Count); CohortAdultMass.RemoveRange(0, CohortAdultMass.Count); CohortIndividualBodyMass.RemoveRange(0, CohortIndividualBodyMass.Count); CohortCohortAbundance.RemoveRange(0, CohortCohortAbundance.Count); CohortLogOptimalPreyBodySizeRatio.RemoveRange(0, CohortLogOptimalPreyBodySizeRatio.Count); CohortBirthTimeStep.RemoveRange(0, CohortBirthTimeStep.Count); CohortProportionTimeActive.RemoveRange(0, CohortProportionTimeActive.Count); CohortTrophicIndex.RemoveRange(0, CohortTrophicIndex.Count); List <double[, , ]> StockIndividualBodyMass = new List <double[, , ]>(); List <double[, , ]> StockTotalBiomass = new List <double[, , ]>(); double[,,,] tempData2 = new double[Latitude.Length, Longitude.Length, StockFunctionalGroup.Length, Stock.Length]; tempData2 = StateDataSet.GetData <double[, , , ]>("StockIndividualBodyMass"); for (int la = 0; la < Latitude.Length; la++) { StockIndividualBodyMass.Add(new double[Longitude.Length, StockFunctionalGroup.Length, Stock.Length]); } foreach (uint[] cell in cellList) { for (int fg = 0; fg < StockFunctionalGroup.Length; fg++) { for (int c = 0; c < Stock.Length; c++) { StockIndividualBodyMass[(int)cell[0]][cell[1], fg, c] = tempData2[ cell[0], cell[1], fg, c]; } } } tempData2 = StateDataSet.GetData <double[, , , ]>("StockTotalBiomass"); for (int la = 0; la < Latitude.Length; la++) { StockTotalBiomass.Add(new double[Longitude.Length, StockFunctionalGroup.Length, Stock.Length]); } foreach (uint[] cell in cellList) { for (int fg = 0; fg < StockFunctionalGroup.Length; fg++) { for (int c = 0; c < Stock.Length; c++) { StockTotalBiomass[(int)cell[0]][cell[1], fg, c] = tempData2[ cell[0], cell[1], fg, c]; } } } _GridCellStocks = new GridCellStockHandler[Latitude.Length, Longitude.Length]; for (int cell = 0; cell < cellList.Count; cell++) { _GridCellStocks[cellList[cell][0], cellList[cell][1]] = new GridCellStockHandler(StockFunctionalGroup.Length); for (int fg = 0; fg < StockFunctionalGroup.Length; fg++) { _GridCellStocks[cellList[cell][0], cellList[cell][1]][fg] = new List <Stock>(); for (int c = 0; c < Stock.Length; c++) { if (StockTotalBiomass[(int)cellList[cell][0]][cellList[cell][1], fg, c] > 0.0) { Stock TempStock = new Stock( (byte)fg, StockIndividualBodyMass[(int)cellList[cell][0]][cellList[cell][1], fg, c], StockTotalBiomass[(int)cellList[cell][0]][cellList[cell][1], fg, c]); _GridCellStocks[cellList[cell][0], cellList[cell][1]][fg].Add(TempStock); } } } } }
/// <summary> /// Initialises herbivory implementation each time step /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="madingleyCohortDefinitions">The definitions for cohorts in the model</param> /// <param name="madingleyStockDefinitions">The definitions for stocks in the model</param> /// <remarks>This only works if: a) herbivory is initialised in every grid cell; and b) if parallelisation is done by latitudinal strips /// It is critical to run this every time step</remarks> public void InitializeEatingPerTimeStep(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions) { // Get the functional group indices of all autotroph stocks _FunctionalGroupIndicesToEat = madingleyStockDefinitions.GetFunctionalGroupIndex("Heterotroph/Autotroph", "Autotroph", false); }
/// <summary> /// Merge cohorts until below a specified threshold number of cohorts in each grid cell /// </summary> /// <param name="gridCellCohorts">The cohorts within this grid cell</param> /// <param name="TotalNumberOfCohorts">The total number of cohorts in this grid cell</param> /// <param name="TargetCohortThreshold">The target threshold to reduce the number of cohorts to</param> /// <returns>The number of cohorts that have been merged</returns> public int MergeToReachThreshold(GridCellCohortHandler gridCellCohorts, int TotalNumberOfCohorts, int TargetCohortThreshold) { // A list of shortest distances between pairs of cohorts List <Tuple <double, int, int[]> > ShortestDistances = new List <Tuple <double, int, int[]> >(); // A holding list // List<Tuple<double, int, int[]>> HoldingList = new List<Tuple<double, int, int[]>>(); // Vector of lists of shortest distances in each functional group // List<Tuple<double, int, int[]>>[] ShortestDistancesPerFunctionalGroup = new List<Tuple<double, int, int[]>>[gridCellCohorts.Count]; // Temporary List <Tuple <double, int, int[]> >[] ShortestDistancesPerFunctionalGroup2 = new List <Tuple <double, int, int[]> > [gridCellCohorts.Count]; // How many cohorts to remove to hit the threshold int NumberToRemove = TotalNumberOfCohorts - TargetCohortThreshold; // Holds the pairwise distances between two cohorts; the functional group of the cohorts; the cohort IDs of each cohort Tuple <double, int, int[]> PairwiseDistance; // Loop through functional groups for (int ff = 0; ff < gridCellCohorts.Count; ff++) { // Temporary ShortestDistancesPerFunctionalGroup2[ff] = new List <Tuple <double, int, int[]> >(); // Loop through cohorts within functional groups for (int cc = 0; cc < gridCellCohorts[ff].Count - 1; cc++) { // Loop through comparison cohorts for (int dd = cc + 1; dd < gridCellCohorts[ff].Count; dd++) { // Randomly select which cohort is to be merge to & calculate distance between cohort pair if (RandomNumberGenerator.GetUniform() < 0.5) { PairwiseDistance = new Tuple <double, int, int[]>(CalculateDistance(gridCellCohorts[ff][cc], gridCellCohorts[ff][dd]), ff, new int[] { cc, dd }); } else { PairwiseDistance = new Tuple <double, int, int[]>(CalculateDistance(gridCellCohorts[ff][cc], gridCellCohorts[ff][dd]), ff, new int[] { dd, cc }); } // Temporary ShortestDistancesPerFunctionalGroup2[ff].Add(PairwiseDistance); } } // Temporary ShortestDistancesPerFunctionalGroup2[ff] = ShortestDistancesPerFunctionalGroup2[ff].OrderBy(x => x.Item1).ToList(); } // Hold the current position in the shortest distance list int CurrentListPosition = 0; List <int> IndicesToRemove; // Now that the shortest distances have been calculated, do the merging execution int FunctionalGroup; int CohortToMergeFrom; int CohortToMergeTo; // Temporary for (int gg = 0; gg < gridCellCohorts.Count; gg++) { IndicesToRemove = new List <int>(); CurrentListPosition = 0; while (CurrentListPosition < ShortestDistancesPerFunctionalGroup2[gg].Count) { CohortToMergeFrom = ShortestDistancesPerFunctionalGroup2[gg][CurrentListPosition].Item3[1]; CohortToMergeTo = ShortestDistancesPerFunctionalGroup2[gg][CurrentListPosition].Item3[0]; for (int cc = ShortestDistancesPerFunctionalGroup2[gg].Count - 1; cc > CurrentListPosition; cc--) { if (ShortestDistancesPerFunctionalGroup2[gg][cc].Item3[0] == CohortToMergeFrom || ShortestDistancesPerFunctionalGroup2[gg][cc].Item3[1] == CohortToMergeFrom) { ShortestDistancesPerFunctionalGroup2[gg].RemoveAt(cc); } } CurrentListPosition++; } } // Compile all shortest distances into a single list for merging purposes - note that we only need to do a limited number of merges for (int gg = 0; gg < gridCellCohorts.Count; gg++) { foreach (var distance in ShortestDistancesPerFunctionalGroup2[gg]) { ShortestDistances.Add(distance); } } ShortestDistances = ShortestDistances.OrderBy(x => x.Item1).ToList(); // Counts the number of merges that have happened int MergeCounter = 0; CurrentListPosition = 0; // While merging does not reach threshold, and while there are still elements in the list while ((MergeCounter < NumberToRemove) && (CurrentListPosition < ShortestDistances.Count)) { // Get pairwise traits FunctionalGroup = ShortestDistances[CurrentListPosition].Item2; CohortToMergeFrom = ShortestDistances[CurrentListPosition].Item3[1]; CohortToMergeTo = ShortestDistances[CurrentListPosition].Item3[0]; // Check whether either cohort has already merged to something else this timestep merge // execution, and hence is empty // if ((gridCellCohorts[FunctionalGroup][CohortToMergeFrom].CohortAbundance.CompareTo(0.0) > 0) || // (gridCellCohorts[FunctionalGroup][CohortToMergeTo].CohortAbundance.CompareTo(0.0) > 0)) // { // Add the abundance of the second cohort to that of the first gridCellCohorts[FunctionalGroup][CohortToMergeTo].CohortAbundance += (gridCellCohorts[FunctionalGroup][CohortToMergeFrom].CohortAbundance * gridCellCohorts[FunctionalGroup][CohortToMergeFrom].IndividualBodyMass) / gridCellCohorts[FunctionalGroup][CohortToMergeTo].IndividualBodyMass; // Add the reproductive potential mass of the second cohort to that of the first gridCellCohorts[FunctionalGroup][CohortToMergeTo].IndividualReproductivePotentialMass += (gridCellCohorts[FunctionalGroup][CohortToMergeFrom].IndividualReproductivePotentialMass * gridCellCohorts[FunctionalGroup][CohortToMergeFrom].CohortAbundance) / gridCellCohorts[FunctionalGroup][CohortToMergeTo].CohortAbundance; // Set the abundance of the second cohort to zero gridCellCohorts[FunctionalGroup][CohortToMergeFrom].CohortAbundance = 0.0; // Designate both cohorts as having merged gridCellCohorts[FunctionalGroup][CohortToMergeTo].Merged = true; gridCellCohorts[FunctionalGroup][CohortToMergeFrom].Merged = true; MergeCounter++; CurrentListPosition++; // } // else // { // CurrentListPosition++; // } } return(MergeCounter); }
/// <summary> /// Calculate the actual amount eaten in herbivory, apply the changes to the eaten autotroph stocks, and update deltas for the herbivore cohort /// </summary> /// <param name="gridCellCohorts">The cohorts in this grid cell</param> /// <param name="gridCellStocks">The stocks in this grid cell</param> /// <param name="actingCohort">The acting cohort</param> /// <param name="cellEnvironment">The environmental conditions in this grid cell</param> /// <param name="deltas">The sorted list to track changes in biomass and abundance of the acting cohort in this grid cell</param> /// <param name="madingleyCohortDefinitions">The functional group definitions for cohorts in the model</param> /// <param name="madingleyStockDefinitions">The functional group definitions for stocks in the model</param> /// <param name="trackProcesses">An instance of ProcessTracker to hold diagnostics for herbivory</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="specificLocations">Whether the model is being run for specific locations</param> /// <param name="outputDetail">The level of output detail being used in this model run</param> /// <param name="initialisation">The Madingley Model initialisation</param> public void RunEating(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList <string, double[]> cellEnvironment, Dictionary <string, Dictionary <string, double> > deltas, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, ProcessTracker trackProcesses, uint currentTimestep, Boolean specificLocations, string outputDetail, MadingleyModelInitialisation initialisation) { EdibleScaling = 1.0; if (cellEnvironment["Realm"][0] == 1.0) { EdibleScaling = 0.1; } // Loop over autotroph functional groups that can be eaten foreach (int FunctionalGroup in _FunctionalGroupIndicesToEat) { // Loop over stocks within the functional groups for (int i = 0; i < gridCellStocks[FunctionalGroup].Count; i++) { // Get the mass from this stock that is available for eating (assumes only 10% is edible in the terrestrial realm) EdibleMass = gridCellStocks[FunctionalGroup][i].TotalBiomass * EdibleScaling; // Calculate the biomass actually eaten from this stock by the acting cohort _BiomassesEaten[FunctionalGroup][i] = CalculateBiomassesEaten(_PotentialBiomassesEaten[FunctionalGroup][i], _TimeUnitsToHandlePotentialFoodItems, gridCellCohorts[actingCohort].CohortAbundance, EdibleMass); gridCellCohorts[actingCohort].TrophicIndex += _BiomassesEaten[FunctionalGroup][i]; // Remove the biomass eaten from the autotroph stock gridCellStocks[FunctionalGroup][i].TotalBiomass -= _BiomassesEaten[FunctionalGroup][i]; // If the model is being run for specific locations and if track processes has been specified, then track the mass flow between // primary producer and herbivore if (specificLocations && trackProcesses.TrackProcesses) { trackProcesses.RecordHerbivoryMassFlow(currentTimestep, _BodyMassHerbivore, _BiomassesEaten[FunctionalGroup][i]); } // If track processes has been specified and the output detail level is set to high and the model is being run for specific locations, // then track the flow of mass between trophic levels if (trackProcesses.TrackProcesses && (outputDetail == "high") && specificLocations) { trackProcesses.TrackHerbivoryTrophicFlow((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], gridCellCohorts[actingCohort].FunctionalGroupIndex, madingleyCohortDefinitions, _BiomassesEaten[FunctionalGroup][i], _BodyMassHerbivore, initialisation, cellEnvironment["Realm"][0] == 2.0); } // Check that the biomass eaten is not a negative value // Commented out for purposes of speed //Debug.Assert(_BiomassesEaten[FunctionalGroup][i] >= 0, // "Herbivory negative for this herbivore cohort" + actingCohort); // Add the biomass eaten and assimilated by an individual to the delta biomass for the acting cohort deltas["biomass"]["herbivory"] += _BiomassesEaten[FunctionalGroup][i] * AssimilationEfficiency / gridCellCohorts[actingCohort].CohortAbundance; // Move the biomass eaten but not assimilated by an individual into the organic matter pool deltas["organicpool"]["herbivory"] += _BiomassesEaten[FunctionalGroup][i] * (1 - AssimilationEfficiency); } // Check that the delta biomass from eating for the acting cohort is not negative // Commented out for the purposes of speed //Debug.Assert(deltas["biomass"]["herbivory"] >= 0, "Delta biomass from herbviory is negative"); // Calculate the total biomass eaten by the acting (herbivore) cohort _TotalBiomassEatenByCohort = deltas["biomass"]["herbivory"] * gridCellCohorts[actingCohort].CohortAbundance; } }
/// <summary> /// Initialize an implementation of mortality. This is only in here to satisfy the requirements of IEcologicalProcessAcrossGridCells /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="gridCellStocks">The stocks in the current grid cell</param> /// <param name="madingleyCohortDefinitions">The definitions for cohort functional groups in the model</param> /// <param name="madingleyStockDefinitions">The definitions for stock functional groups in the model</param> /// <param name="implementationKey">The name of the implementation of mortality to initialize</param> public void InitializeEcologicalProcess(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, string implementationKey, SortedList <string, double[]> cellEnvironmen) { }