//Function to bin cohorts according to the mass bins defined for the catch data /// <summary> /// Bin cohorts according to the mass bins defined for the catch data /// Constructs a list of functional group and cohort indices falling within each mass bin /// as well as the total biomass available to be fished in each /// </summary> /// <param name="c">The grid cell cohorts</param> /// <param name="fishCatch">Fisheries catch data</param> public void BinCohorts(GridCellCohortHandler c, InputCatchData fishCatch, FunctionalGroupDefinitions cohortFGs) { int mb = 0; int[] FishFGs = cohortFGs.GetFunctionalGroupIndex("Endo/Ectotherm", "Ectotherm",false); for (int i = 0; i < BinnedCohorts.Length; i++) { BinnedCohorts[i] = new List<Tuple<int[], double>>(); } foreach (int fg in FishFGs) { for (int i = 0; i < c[fg].Count(); i++) { //Find the mass bin for this cohort mb = fishCatch.MassBins.ToList().FindIndex(a => a >= c[fg,i].AdultMass); if (mb < 0) mb = fishCatch.UnknownMassBinIndex - 1; //Check if the current bodymass is greater than the proportion of the adult mass if (c[fg, i].IndividualBodyMass >= c[fg, i].AdultMass * AdultMassProportionFished) { //Calculate the total biomass of this cohort double CohortBiomass = (c[fg, i].IndividualBodyMass + c[fg, i].IndividualReproductivePotentialMass) * c[fg, i].CohortAbundance; //Add the indices and total biomass to the bins BinnedCohorts[mb].Add(new Tuple<int[], double>(new int[] { fg, i }, CohortBiomass)); BinnedTotalModelBiomass[mb] += CohortBiomass; } } } }
/// <summary> /// Update the abundance of the acting cohort according to the delta abundances from the ecological processes /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="actingCohort">The location 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> private void UpdateAbundance(GridCellCohortHandler gridCellCohorts, int[] actingCohort, Dictionary<string,Dictionary<string, double>> deltas) { // Extract the abundance deltas from the sorted list of all deltas Dictionary<string, double> deltaAbundance = deltas["abundance"]; // Get all keys from the abundance deltas sorted list string[] KeyStrings = deltaAbundance.Keys.ToArray(); // Variable to calculate net abundance change to check that cohort abundance will not become negative double NetAbundanceChange = 0.0; // Loop over all abundance deltas foreach (var key in KeyStrings) { // Update net abundance change NetAbundanceChange += deltaAbundance[key]; } // Check that cohort abundance will not become negative Debug.Assert((gridCellCohorts[actingCohort].CohortAbundance + NetAbundanceChange).CompareTo(0.0) >= 0, "Cohort abundance < 0"); //Loop over all keys in the abundance deltas sorted list foreach (var key in KeyStrings) { // Update the abundance of the acting cohort gridCellCohorts[actingCohort].CohortAbundance += deltaAbundance[key]; // Reset the current delta abundance to zero deltaAbundance[key] = 0.0; } }
public void ApplyCatches(GridCellCohortHandler c, InputCatchData fishCatch, int latIndex, int lonIndex) { //Hold the total catch in each mass bin for this cell double[] BinnedCellCatch = new double[fishCatch.MassBins.Length]; //TO DO: make the time division flexible according to the model timestep for (int mb = 0; mb < BinnedCellCatch.Length; mb++) { BinnedCellCatch[mb] = fishCatch.ModelGridCatch[latIndex, lonIndex, mb]/12.0; if (BinnedCellCatch[mb] > 0) { if (BinnedTotalModelBiomass[mb] <= BinnedCellCatch[mb]) { DefecitCatch[mb] = BinnedCellCatch[mb] - BinnedTotalModelBiomass[mb]; BinnedCellCatch[mb] = BinnedTotalModelBiomass[mb]; } foreach (var v in BinnedCohorts[mb]) { double Contribution = v.Item2 / BinnedTotalModelBiomass[mb]; double AbundanceCaught = Contribution * BinnedCellCatch[mb] / (c[v.Item1].IndividualBodyMass + c[v.Item1].IndividualReproductivePotentialMass); c[v.Item1].CohortAbundance -= AbundanceCaught; } } } }
/// <summary> /// Apply the results of direct harvesting of animals to the cohorts in a grid cell /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="targetBiomass">The target biomass to be harvested</param> /// <param name="cellEnvironment">The environment in the current grid cell</param> public void ApplyHarvesting(GridCellCohortHandler gridCellCohorts, double targetBiomass, SortedList<string, double[]> cellEnvironment, uint currentTimestep) { // Create variable to hold total available animal biomass double TotalAvailableBiomass = 0.0; double TotalBiomassTimesPreference = 0.0; double TotalBiomassHarvested = 0.0; // Create variable to hold estimate of preference for a given cohort double Preference; // Create variable to hold the biomass of a cohort actually harvested double BiomassHarvested; // Create jagged arrays mirroring the cohort handler to hold calculations double[][] AvailableBiomass = new double[gridCellCohorts.Count][]; double[][] BiomassTimesPreference = new double[gridCellCohorts.Count][]; // Loop over functional groups and initialise rows in the jagged arrays for (int i = 0; i < gridCellCohorts.Count; i++) { AvailableBiomass[i] = new double[gridCellCohorts[i].Count]; BiomassTimesPreference[i] = new double[gridCellCohorts[i].Count]; } // Convert target biomass from kg per km squared to g per cell targetBiomass *= 1000; //kg to g conversion targetBiomass *= cellEnvironment["Cell Area"][0]; // Loop over cohorts and calculate available biomass and biomass times preference in each cohort, and total available biomass for (int fg = 0; fg < gridCellCohorts.Count; fg++) { for (int c = 0; c < gridCellCohorts[fg].Count; c++) { TotalAvailableBiomass += (gridCellCohorts[fg][c].IndividualBodyMass * gridCellCohorts[fg][c].CohortAbundance); AvailableBiomass[fg][c] = (gridCellCohorts[fg][c].IndividualBodyMass * gridCellCohorts[fg][c].CohortAbundance); Preference = 1 / (1 + Math.Exp(-(-8 + 0.8 * Math.Log(gridCellCohorts[fg][c].IndividualBodyMass)))); BiomassTimesPreference[fg][c] = AvailableBiomass[fg][c] * Preference; TotalBiomassTimesPreference += BiomassTimesPreference[fg][c]; } } // Loop over cohorts again, and calculate and apply the actual amount of biomass harvested for (int fg = 0; fg < gridCellCohorts.Count; fg++) { for (int c = 0; c < gridCellCohorts[fg].Count; c++) { BiomassHarvested = Math.Min(AvailableBiomass[fg][c], targetBiomass * BiomassTimesPreference[fg][c] / TotalBiomassTimesPreference); TotalBiomassHarvested += BiomassHarvested; gridCellCohorts[fg][c].CohortAbundance -= (BiomassHarvested / gridCellCohorts[fg][c].IndividualBodyMass); } } SyncHarvestWriter.WriteLine( cellEnvironment["Latitude"][0] + "," + cellEnvironment["Longitude"][0] + "," + currentTimestep + "," + TotalBiomassHarvested); }
/// <summary> /// Initializes an implementation of 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="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 eating to initialize</param> /// <param name="cellEnvironment">The environment in the current grid cell</param> /// <remarks>Eating needs to be initialized every time step</remarks> public void InitializeEcologicalProcess(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, string implementationKey, SortedList<string, double[]> cellEnvironment) { // Initialize the implementation of the eating process Implementations[implementationKey].InitializeEatingPerTimeStep(gridCellCohorts, gridCellStocks, madingleyCohortDefinitions, madingleyStockDefinitions,cellEnvironment); }
public InputModelState( GridCellCohortHandler[,] gridCellCohorts, GridCellStockHandler[,] gridCellStocks) { this._GridCellCohorts = gridCellCohorts; this._GridCellStocks = gridCellStocks; this._InputState = true; }
/// <summary> /// Calculate the proportion of individuals in a cohort that die from starvation mortality each 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">Body mass including change from other ecological functions this time step; should not exceed adult mass</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 proportion of individuals in the cohort that die from starvation mortality</returns> public double CalculateMortalityRate(GridCellCohortHandler gridCellCohorts, int[] actingCohort, double bodyMassIncludingChangeThisTimeStep, Dictionary<string, Dictionary<string, double>> deltas, uint currentTimestep) { // Calculate the starvation rate of the cohort given individual body masses compared to the maximum body // mass ever achieved double _MortalityRate = CalculateStarvationRate(gridCellCohorts, actingCohort, bodyMassIncludingChangeThisTimeStep, deltas); // Convert the mortality rate from formulation time step units to model time step units return _MortalityRate * DeltaT; }
/// <summary> /// Apply all updates from the ecological processes to the properties of the acting cohort and to the environment /// </summary> /// <param name="gridCellCohorts">The cohorts in the current grid cell</param> /// <param name="actingCohort">The location of the acting cohort in the jagged array of grid cell cohorts</param> /// <param name="cellEnvironment">The environment in the current gird 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="currentTimestep">The current model time step</param> /// <param name="tracker">A process tracker</param> public void UpdateAllEcology(GridCellCohortHandler gridCellCohorts, int[] actingCohort, SortedList<string, double[]> cellEnvironment, Dictionary<string, Dictionary<string, double>> deltas, uint currentTimestep, ProcessTracker tracker) { // Apply cohort abundance changes UpdateAbundance(gridCellCohorts, actingCohort, deltas); // Apply cohort biomass changes UpdateBiomass(gridCellCohorts, actingCohort, deltas, currentTimestep, tracker, cellEnvironment); // Apply changes to the environmental biomass pools UpdatePools(cellEnvironment, deltas); }
/// <summary> /// Run metabolism for the acting 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 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 the stock functional groups in the model</param> /// <param name="currentTimestep">The current model time step</param> /// <param name="currentMonth">The current model month</param> public void RunMetabolism(GridCellCohortHandler gridCellCohorts, GridCellStockHandler gridCellStocks, int[] actingCohort, SortedList<string, double[]> cellEnvironment, Dictionary<string, Dictionary<string, double>> deltas, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, uint currentTimestep, uint currentMonth) { // Calculate metabolic loss for an individual and add the value to the delta biomass for metabolism deltas["biomass"]["metabolism"] = -CalculateIndividualMetabolicRate(gridCellCohorts[actingCohort].IndividualBodyMass, cellEnvironment["Temperature"][currentMonth] + _TemperatureUnitsConvert) * _DeltaT; // If metabolic loss is greater than individual body mass after herbivory and predation, then set equal to individual body mass deltas["biomass"]["metabolism"] = Math.Max(deltas["biomass"]["metabolism"],-(gridCellCohorts[actingCohort].IndividualBodyMass + deltas["biomass"]["predation"] + deltas["biomass"]["herbivory"])); // Add total metabolic loss for all individuals in the cohort to delta biomass for metabolism in the respiratory CO2 pool deltas["respiratoryCO2pool"]["metabolism"] = -deltas["biomass"]["metabolism"] * gridCellCohorts[actingCohort].CohortAbundance; }
/// <summary> /// Calculate the rate of individuals in a cohort that die from senescence 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 senescence mortality</returns> public double CalculateMortalityRate(GridCellCohortHandler gridCellCohorts, int[] actingCohort, double bodyMassIncludingChangeThisTimeStep, Dictionary<string, Dictionary<string, double>> deltas, uint currentTimestep) { // Calculate the age (in model time steps) that the cohort reached maturity double TimeToMaturity = gridCellCohorts[actingCohort].MaturityTimeStep - gridCellCohorts[actingCohort].BirthTimeStep; // Calculate how many model time steps since the cohort reached maturity double AgePostMaturity = currentTimestep - gridCellCohorts[actingCohort].MaturityTimeStep; // Calculate the time since maturity as a fraction of the time that it took the cohort to reach maturity double FractionalAgePostMaturity = AgePostMaturity/(TimeToMaturity+1); // Calculate the mortality rate per mortality formulation time step as a function of the exponential of the previous fraction double AgeRelatedMortalityRate = _MortalityRate * Math.Exp(FractionalAgePostMaturity); // Convert the mortality rate from formulation time step units to model time step units return AgeRelatedMortalityRate * DeltaT; }
/// <summary> /// Run reproduction /// </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="processTracker">An instance of ProcessTracker to hold diagnostics for eating</param> /// <param name="partial">Thread-locked variables for the parallelised version</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 this 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 processTracker, ref ThreadLockedParallelVariables partial, Boolean specificLocations, string outputDetail, uint currentMonth, MadingleyModelInitialisation initialisation) { // Holds the reproductive strategy of a cohort bool _Iteroparous = madingleyCohortDefinitions.GetTraitNames("reproductive strategy", actingCohort[0])=="iteroparity"; // Assign mass to reproductive potential Implementations["reproduction basic"].RunReproductiveMassAssignment(gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, currentTimeStep, processTracker); // Run reproductive events. Note that we can't skip juveniles here as they could conceivably grow to adulthood and get enough biomass to reproduce in a single time step // due to other ecological processes Implementations["reproduction basic"].RunReproductionEvents(gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, currentTimeStep, processTracker, ref partial, _Iteroparous, currentMonth); }
public void RecordCohorts(uint lat, uint lon, uint currentTimeStep, GridCellCohortHandler cohorts) { foreach (var fg in cohorts) { foreach (Cohort c in fg) { SyncCohortWriter.WriteLine( Convert.ToString(lat) + '\t' + Convert.ToString(lon) + '\t' + Convert.ToString(currentTimeStep) + '\t' + Convert.ToString(c.FunctionalGroupIndex) + '\t' + Convert.ToString(c.IndividualBodyMass) + '\t' + Convert.ToString(c.CohortAbundance) + '\t' + Convert.ToString(c.JuvenileMass) + '\t' + Convert.ToString(c.AdultMass)); } } }
/// <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> /// 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> /// Run metabolism /// </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 metabolism</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) { double Realm = cellEnvironment["Realm"][0]; if (madingleyCohortDefinitions.GetTraitNames("Heterotroph/Autotroph", gridCellCohorts[actingCohort].FunctionalGroupIndex) == "heterotroph") { if (madingleyCohortDefinitions.GetTraitNames("Endo/Ectotherm", gridCellCohorts[actingCohort].FunctionalGroupIndex) == "endotherm") { Implementations["basic endotherm"].RunMetabolism(gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, currentTimestep, currentMonth); } else { Implementations["basic ectotherm"].RunMetabolism(gridCellCohorts, gridCellStocks, actingCohort, cellEnvironment, deltas, madingleyCohortDefinitions, madingleyStockDefinitions, currentTimestep, currentMonth); } } // 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")) { trackProcesses.TrackTimestepMetabolism((uint)cellEnvironment["LatIndex"][0], (uint)cellEnvironment["LonIndex"][0], currentTimestep, gridCellCohorts[actingCohort].IndividualBodyMass, actingCohort[0], cellEnvironment["Temperature"][currentMonth], deltas["biomass"]["metabolism"]); } }
/// <summary> /// Initialises predation 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) predation 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 heterotroph cohorts (i.e. potential prey) _FunctionalGroupIndicesToEat = madingleyCohortDefinitions.GetFunctionalGroupIndex("Heterotroph/Autotroph", "heterotroph", false); // Initialise the vector to hold the number of cohorts in each functional group at the start of the time step NumberCohortsPerFunctionalGroupNoNewCohorts = new int[gridCellCohorts.Count]; // Initialise the jagged arrays to hold the potential and actual numbers of prey eaten in each of the grid cell cohorts _AbundancesEaten = new double[gridCellCohorts.Count][]; _PotentialAbundanceEaten = new double[gridCellCohorts.Count][]; // Initialise the vector to identify carnivore cohorts _CarnivoreFunctionalGroups = new Boolean[_FunctionalGroupIndicesToEat.Length]; _OmnivoreFunctionalGroups = new Boolean[_FunctionalGroupIndicesToEat.Length]; _PlanktonFunctionalGroups = new Boolean[_FunctionalGroupIndicesToEat.Length]; // Loop over rows in the jagged arrays, initialise each vector within the jagged arrays, and calculate the current number of cohorts in // each functional group for (int i = 0; i < gridCellCohorts.Count; i++) { // Calculate the current number of cohorts in this functional group int NumCohortsThisFG = gridCellCohorts[i].Count; NumberCohortsPerFunctionalGroupNoNewCohorts[i] = NumCohortsThisFG; // Initialise the jagged arrays _AbundancesEaten[i] = new double[NumberCohortsPerFunctionalGroupNoNewCohorts[i]]; _PotentialAbundanceEaten[i] = new double[NumberCohortsPerFunctionalGroupNoNewCohorts[i]]; } // Loop over functional groups that are potential prey and determine which are carnivores foreach (int FunctionalGroup in FunctionalGroupIndicesToEat) _CarnivoreFunctionalGroups[FunctionalGroup] = madingleyCohortDefinitions.GetTraitNames("Nutrition source", FunctionalGroup) == "carnivore"; foreach (int FunctionalGroup in FunctionalGroupIndicesToEat) _OmnivoreFunctionalGroups[FunctionalGroup] = madingleyCohortDefinitions.GetTraitNames("Nutrition source", FunctionalGroup) == "omnivore"; foreach (int FunctionalGroup in FunctionalGroupIndicesToEat) _PlanktonFunctionalGroups[FunctionalGroup] = madingleyCohortDefinitions.GetTraitNames("Mobility", FunctionalGroup) == "planktonic"; }
/// <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> /// 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; }
private void RunWithinCellDispersalOnly(uint latCellIndex, uint lonCellIndex, ThreadLockedParallelVariables partial, GridCellCohortHandler workingGridCellCohorts, GridCellStockHandler workingGridCellStocks) { // Merge cohorts. Requires cohorts to be identical, for testing purposes (remember that they don't grow etc) // SHOULD ONLY BE RUN FOR RESPONSIVE DISPERSAL TESTING //partial.Combinations = Merger.MergeForResponsiveDispersalOnly(workingGridCellCohorts); // Loop over cohorts and remove any whose abundance is below the extinction threshold for (int kk = 0; kk < CohortFunctionalGroupDefinitions.GetNumberOfFunctionalGroups(); kk++) { // Create a list to hold the cohorts to remove List<int> CohortIndicesToRemove = new List<int>(); // Loop through each cohort in the functional group for (int ll = 0; ll < workingGridCellCohorts[kk].Count; ll++) { // If cohort abundance is less than the extinction threshold then add to the list for extinction if (workingGridCellCohorts[kk][ll].CohortAbundance <= _ExtinctionThreshold) { CohortIndicesToRemove.Add(ll); partial.Extinctions += 1; } } // Note that we don't keep track of the organic biomass pool if running dispersal only, since there are cohorts with strange biomasses for (int ll = (CohortIndicesToRemove.Count - 1); ll >= 0; ll--) { // Remove the extinct cohort from the list of cohorts workingGridCellCohorts[kk].RemoveAt(CohortIndicesToRemove[ll]); } } // Write out the updated cohort numbers after all ecological processes have occured EcosystemModelGrid.SetGridCellCohorts(workingGridCellCohorts, latCellIndex, lonCellIndex); }
private void RunWithinCellCohortEcology(uint latCellIndex, uint lonCellIndex, ThreadLockedParallelVariables partial, GridCellCohortHandler workingGridCellCohorts, GridCellStockHandler workingGridCellStocks,string outputDetail, int cellIndex, MadingleyModelInitialisation initialisation) { // Local instances of classes EcologyCohort MadingleyEcologyCohort = new EcologyCohort(); Activity CohortActivity = new Activity(); CohortMerge CohortMerger = new CohortMerge(DrawRandomly); // A list of the original cohorts inside a particular grid cell int[] OriginalGridCellCohortsNumbers; // A vector to hold the order in which cohorts will act uint[] RandomCohortOrder; // A jagged array to keep track of cohorts that are being worked on uint[][] CohortIndices; // The location of the acting cohort int[] ActingCohort = new int[2]; // Temporary local variables int EcosystemModelParallelTempval1; int EcosystemModelParallelTempval2; // Boolean to pass into function to get cell environmental data to check if the specified variable exists bool VarExists; // variable to track cohort number uint TotalCohortNumber = 0; // Fill in the array with the number of cohorts per functional group before ecological processes are run OriginalGridCellCohortsNumbers = new int[workingGridCellCohorts.Count]; for (int i = 0; i < workingGridCellCohorts.Count; i++) { OriginalGridCellCohortsNumbers[i] = workingGridCellCohorts[i].Count; } // Initialize ecology for stocks and cohorts MadingleyEcologyCohort.InitializeEcology(EcosystemModelGrid.GetCellEnvironment(latCellIndex, lonCellIndex)["Cell Area"][0], _GlobalModelTimeStepUnit, DrawRandomly); // Create a jagged array indexed by functional groups to hold cohort indices CohortIndices = new uint[CohortFunctionalGroupDefinitions.GetNumberOfFunctionalGroups()][]; // Loop over functional groups for (int ll = 0; ll < CohortFunctionalGroupDefinitions.GetNumberOfFunctionalGroups(); ll++) { // Dimension the number of columns in each row of the jagged array to equal number of gridCellCohorts in each functional group if (workingGridCellCohorts[ll] == null) { CohortIndices[ll] = new uint[0]; } else { CohortIndices[ll] = new uint[workingGridCellCohorts[ll].Count()]; } // Loop over gridCellCohorts in the functional group for (int kk = 0; kk < CohortIndices[ll].Count(); kk++) { // Fill jagged array with indices for each cohort CohortIndices[ll][kk] = TotalCohortNumber; TotalCohortNumber += 1; } } if (DrawRandomly) { // Randomly order the cohort indices RandomCohortOrder = Utilities.RandomlyOrderedIndices(TotalCohortNumber); } else { RandomCohortOrder = Utilities.NonRandomlyOrderedCohorts(TotalCohortNumber, CurrentTimeStep); } // Diagnostic biological variables don't need to be reset every cohort, but rather every grid cell EcosystemModelParallelTempval2 = 0; // Initialise eating formulations MadingleyEcologyCohort.EatingFormulations["Basic eating"].InitializeEcologicalProcess(workingGridCellCohorts, workingGridCellStocks, CohortFunctionalGroupDefinitions, StockFunctionalGroupDefinitions, "revised predation"); MadingleyEcologyCohort.EatingFormulations["Basic eating"].InitializeEcologicalProcess(workingGridCellCohorts, workingGridCellStocks , CohortFunctionalGroupDefinitions, StockFunctionalGroupDefinitions, "revised herbivory"); // Loop over randomly ordered gridCellCohorts to implement biological functions for (int ll = 0; ll < RandomCohortOrder.Length; ll++) { // Locate the randomly chosen cohort within the array of lists of gridCellCohorts in the grid cell ActingCohort = Utilities.FindJaggedArrayIndex(RandomCohortOrder[ll], CohortIndices, TotalCohortNumber); // Perform all biological functions except dispersal (which is cross grid cell) if (workingGridCellCohorts[ActingCohort].CohortAbundance.CompareTo(_ExtinctionThreshold) > 0) { // Calculate number of cohorts in this functional group in this grid cell before running ecology EcosystemModelParallelTempval1 = workingGridCellCohorts[ActingCohort[0]].Count; CohortActivity.AssignProportionTimeActive(workingGridCellCohorts[ActingCohort], EcosystemModelGrid.GetCellEnvironment(latCellIndex, lonCellIndex), CohortFunctionalGroupDefinitions, CurrentTimeStep, CurrentMonth); // Run ecology MadingleyEcologyCohort.RunWithinCellEcology(workingGridCellCohorts, workingGridCellStocks, ActingCohort, EcosystemModelGrid.GetCellEnvironment(latCellIndex, lonCellIndex), EcosystemModelGrid.GetCellDeltas(latCellIndex, lonCellIndex), CohortFunctionalGroupDefinitions, StockFunctionalGroupDefinitions, CurrentTimeStep, ProcessTrackers[cellIndex], ref partial, SpecificLocations,outputDetail, CurrentMonth, initialisation); // Update the properties of the acting cohort MadingleyEcologyCohort.UpdateEcology(workingGridCellCohorts, workingGridCellStocks, ActingCohort, EcosystemModelGrid.GetCellEnvironment(latCellIndex, lonCellIndex), EcosystemModelGrid.GetCellDeltas( latCellIndex, lonCellIndex), CohortFunctionalGroupDefinitions, StockFunctionalGroupDefinitions, CurrentTimeStep, ProcessTrackers[cellIndex]); // Add newly produced cohorts to the tracking variable EcosystemModelParallelTempval2 += workingGridCellCohorts[ActingCohort[0]].Count - EcosystemModelParallelTempval1; // Check that the mass of individuals in this cohort is still >= 0 after running ecology Debug.Assert(workingGridCellCohorts[ActingCohort].IndividualBodyMass >= 0.0, "Biomass < 0 for this cohort"); } // Check that the mass of individuals in this cohort is still >= 0 after running ecology Debug.Assert(workingGridCellCohorts[ActingCohort].IndividualBodyMass >= 0.0, "Biomass < 0 for this cohort"); } // Update diagnostics of productions partial.Productions += EcosystemModelParallelTempval2; RunExtinction(latCellIndex, lonCellIndex, partial, workingGridCellCohorts, cellIndex); // Merge cohorts, if necessary if (workingGridCellCohorts.GetNumberOfCohorts() > initialisation.MaxNumberOfCohorts) { partial.Combinations = CohortMerger.MergeToReachThresholdFast(workingGridCellCohorts, workingGridCellCohorts.GetNumberOfCohorts(), initialisation.MaxNumberOfCohorts); //Run extinction a second time to remove those cohorts that have been set to zero abundance when merging RunExtinction(latCellIndex, lonCellIndex, partial, workingGridCellCohorts, cellIndex); } else partial.Combinations = 0; // Write out the updated cohort numbers after all ecological processes have occured EcosystemModelGrid.SetGridCellCohorts(workingGridCellCohorts, latCellIndex, lonCellIndex); }
/// <summary> /// Carries out extinction on cohorts that have an abundance below a defined extinction threshold /// </summary> private void RunExtinction(uint latCellIndex, uint lonCellIndex, ThreadLockedParallelVariables partial, GridCellCohortHandler workingGridCellCohorts, int cellIndex) { bool VarExists; // Loop over cohorts and remove any whose abundance is below the extinction threshold for (int kk = 0; kk < CohortFunctionalGroupDefinitions.GetNumberOfFunctionalGroups(); kk++) { // Create a list to hold the cohorts to remove List<int> CohortIndicesToRemove = new List<int>(); // Loop through each cohort in the functional group for (int ll = 0; ll < workingGridCellCohorts[kk].Count; ll++) { // If cohort abundance is less than the extinction threshold then add to the list for extinction if (workingGridCellCohorts[kk][ll].CohortAbundance.CompareTo(_ExtinctionThreshold) <= 0 || workingGridCellCohorts[kk][ll].IndividualBodyMass.CompareTo(0.0) == 0) { CohortIndicesToRemove.Add(ll); partial.Extinctions += 1; // If track processes is set and output detail is set to high and the cohort being made extinct has never been merged, // then output its mortality profile if (ProcessTrackers[cellIndex].TrackProcesses && (InitialisationFileStrings["OutputDetail"] == "high") && (workingGridCellCohorts[kk][ll].CohortID.Count == 1)) { ProcessTrackers[cellIndex].OutputMortalityProfile(workingGridCellCohorts[kk][ll].CohortID[0]); } } } // Code to add the biomass to the biomass pool and dispose of the cohort for (int ll = (CohortIndicesToRemove.Count - 1); ll >= 0; ll--) { // Add biomass of the extinct cohort to the organic matter pool EcosystemModelGrid.SetEnviroLayer("Organic Pool", 0, EcosystemModelGrid.GetEnviroLayer("Organic Pool", 0, latCellIndex, lonCellIndex, out VarExists) + (workingGridCellCohorts[kk][CohortIndicesToRemove[ll]].IndividualBodyMass + workingGridCellCohorts[kk][CohortIndicesToRemove[ll]].IndividualReproductivePotentialMass) * workingGridCellCohorts[kk][CohortIndicesToRemove[ll]].CohortAbundance, latCellIndex, lonCellIndex); Debug.Assert(EcosystemModelGrid.GetEnviroLayer("Organic Pool", 0, latCellIndex, lonCellIndex, out VarExists) > 0, "Organic pool < 0"); if (ProcessTrackers[cellIndex].TrackProcesses && SpecificLocations == true) ProcessTrackers[cellIndex].RecordExtinction(latCellIndex, lonCellIndex, CurrentTimeStep, workingGridCellCohorts[kk][CohortIndicesToRemove[ll]].Merged, workingGridCellCohorts[kk][CohortIndicesToRemove[ll]].CohortID); // Remove the extinct cohort from the list of cohorts workingGridCellCohorts[kk].RemoveAt(CohortIndicesToRemove[ll]); } } }
/// <summary> /// Calculates the rate of starvation mortality given current body mass and the maximum body mass ever achieved. Note that metabolic costs are already included in the deltas passed in /// the body mass including change this time step, so no change in body mass should mean no starvation (as metabolic costs have already been met) /// </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="bodyMassIncludingChangeThisTimeStep">Body mass including change from other ecological functions this time step; should not exceed adult mass</param> /// <returns>The starvation mortality rate in mortality formulation time step units</returns> private double CalculateStarvationRate(GridCellCohortHandler gridCellCohorts, int[] actingCohort, double bodyMassIncludingChangeThisTimeStep, Dictionary<string, Dictionary<string, double>> deltas) { if (bodyMassIncludingChangeThisTimeStep < gridCellCohorts[actingCohort].MaximumAchievedBodyMass) { // Calculate the first part of the relationship between body mass and mortality rate double k = -(bodyMassIncludingChangeThisTimeStep - _LogisticInflectionPoint * gridCellCohorts[actingCohort]. MaximumAchievedBodyMass) / (_LogisticScalingParameter * gridCellCohorts[actingCohort].MaximumAchievedBodyMass); // Calculate mortality rate return _MaximumStarvationRate / (1 + Math.Exp(-k)); } else return 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> /// 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, Madingley.Common.ScenarioParameter 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.ParamString == "no") { // Do not apply any harvesting } else if (harvestingScenario.ParamString == "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.ParamDouble1; } // If the burn-in period has been completed, then apply // the harvesting scenario if (currentTimestep > burninSteps) { ApplyHarvesting(gridCellCohorts, TargetBiomass, cellEnvironment, currentTimestep); } } else if (harvestingScenario.ParamString == "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.ParamString == "no") { // Do not apply any harvesting } else if (harvestingScenario.ParamString == "constant") { // If the burn-in period has been completed, then apply // the harvesting scenario if (currentTimestep > burninSteps) { ApplyHarvesting(gridCellCohorts, harvestingScenario.ParamDouble1, cellEnvironment, currentTimestep); } } else if (harvestingScenario.ParamString == "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.ParamDouble1, cellEnvironment, currentTimestep); } } else if (harvestingScenario.ParamString == "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.ParamDouble1))); // Apply the harvesting scenario using the calculated target biomass ApplyHarvesting(gridCellCohorts, TargetBiomass, cellEnvironment, currentTimestep); } } else if (harvestingScenario.ParamString == "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.ParamDouble1))); // Apply the harvesting scenario using the calculated target biomass ApplyHarvesting(gridCellCohorts, TargetBiomass, cellEnvironment, currentTimestep); } 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.ParamDouble1))); // Apply the harvesting scenario using the calculated target biomass ApplyHarvesting(gridCellCohorts, TargetBiomass, cellEnvironment, currentTimestep); } } else if (harvestingScenario.ParamString == "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.ParamDouble1))); // Apply the harvesting scenarion using the calculated target biomass ApplyHarvesting(gridCellCohorts, TargetBiomass, cellEnvironment, currentTimestep); } } else if (harvestingScenario.ParamString == "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.ParamDouble2 * 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.ParamDouble1))); // Apply the harvesting scenarion using the calculated target biomass ApplyHarvesting(gridCellCohorts, TargetBiomass, cellEnvironment, currentTimestep); } } else if (harvestingScenario.ParamString == "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.ParamDouble2 * 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.ParamDouble1), (((currentTimestep - burninSteps) / 12.0) * harvestingScenario.ParamDouble1)))); // Apply the harvesting scenarion using the calculated target biomass ApplyHarvesting(gridCellCohorts, TargetBiomass, cellEnvironment, currentTimestep); } } else { Debug.Fail("There is no method for the harvesting scenario specified"); } } } }
/// <summary> /// Constructor for a grid cell; creates cell and reads in environmental data /// </summary> /// <param name="latitude">The latitude of the grid cell</param> /// <param name="latIndex">The latitudinal index of the grid cell</param> /// <param name="longitude">The longitude of the grid cell</param> /// <param name="lonIndex">The longitudinal index of the grid cell</param> /// <param name="latCellSize">The latitudinal dimension of the grid cell</param> /// <param name="lonCellSize">The longitudinal dimension of the grid cell</param> /// <param name="dataLayers">A list of environmental data variables in the model</param> /// <param name="missingValue">The missing value to be applied to all data in the grid cell</param> /// <param name="cohortFunctionalGroups">The definitions for cohort functional groups in the model</param> /// <param name="stockFunctionalGroups">The definitions for stock functional groups in the model</param> /// <param name="globalDiagnostics">A list of global diagnostic variables for the model grid</param> /// <param name="tracking">Whether process-tracking is enabled</param> /// <param name="specificLocations">Whether the model is being run for specific locations</param> public GridCell(float latitude, uint latIndex, float longitude, uint lonIndex, float latCellSize, float lonCellSize, SortedList<string, EnviroData> dataLayers, double missingValue, FunctionalGroupDefinitions cohortFunctionalGroups, FunctionalGroupDefinitions stockFunctionalGroups, SortedList<string, double> globalDiagnostics,Boolean tracking, bool specificLocations) { // Boolean to track when environmental data are missing Boolean EnviroMissingValue; // Initialise the utility functions Utilities = new UtilityFunctions(); // Temporary vector for holding initial values of grid cell properties double[] tempVector; // Initialise deltas sorted list _Deltas = new Dictionary<string, Dictionary<string, double>>(); // Initialize delta abundance sorted list with appropriate processes Dictionary<string, double> DeltaAbundance = new Dictionary<string, double>(); DeltaAbundance.Add("mortality", 0.0); // Add delta abundance sorted list to deltas sorted list _Deltas.Add("abundance", DeltaAbundance); // Initialize delta biomass sorted list with appropriate processes Dictionary<string, double> DeltaBiomass = new Dictionary<string, double>(); DeltaBiomass.Add("metabolism", 0.0); DeltaBiomass.Add("predation", 0.0); DeltaBiomass.Add("herbivory", 0.0); DeltaBiomass.Add("reproduction", 0.0); // Add delta biomass sorted list to deltas sorted list _Deltas.Add("biomass", DeltaBiomass); // Initialize delta reproductive biomass vector with appropriate processes Dictionary<string, double> DeltaReproductiveBiomass = new Dictionary<string, double>(); DeltaReproductiveBiomass.Add("reproduction", 0.0); // Add delta reproduction sorted list to deltas sorted list _Deltas.Add("reproductivebiomass", DeltaReproductiveBiomass); // Initialize organic pool delta vector with appropriate processes Dictionary<string, double> DeltaOrganicPool = new Dictionary<string, double>(); DeltaOrganicPool.Add("herbivory", 0.0); DeltaOrganicPool.Add("predation", 0.0); DeltaOrganicPool.Add("mortality", 0.0); // Add delta organic pool sorted list to deltas sorted list _Deltas.Add("organicpool", DeltaOrganicPool); // Initialize respiratory CO2 pool delta vector with appropriate processes Dictionary<string, double> DeltaRespiratoryCO2Pool = new Dictionary<string, double>(); DeltaRespiratoryCO2Pool.Add("metabolism", 0.0); // Add delta respiratory CO2 pool to deltas sorted list _Deltas.Add("respiratoryCO2pool", DeltaRespiratoryCO2Pool); // Set the grid cell values of latitude, longitude and missing value as specified _Latitude = latitude; _Longitude = longitude; // Initialise list of environmental data layer values _CellEnvironment = new SortedList<string, double[]>(); //Add the latitude and longitude tempVector = new double[1]; tempVector[0] = latitude; _CellEnvironment.Add("Latitude", tempVector); tempVector = new double[1]; tempVector[0] = longitude; _CellEnvironment.Add("Longitude", tempVector); // Add an organic matter pool to the cell environment to track organic biomass not held by animals or plants with an initial value of 0 tempVector = new double[1]; tempVector[0] = 0.0; _CellEnvironment.Add("Organic Pool", tempVector); // Add a repsiratory CO2 pool to the cell environment with an initial value of 0 tempVector = new double[1]; tempVector[0] = 0.0; _CellEnvironment.Add("Respiratory CO2 Pool", tempVector); // Add the grid cell area (in km2) to the cell environment with an initial value of 0 tempVector = new double[1]; // Calculate the area of this grid cell tempVector[0] = Utilities.CalculateGridCellArea(latitude, lonCellSize, latCellSize); // Add it to the cell environment _CellEnvironment.Add("Cell Area", tempVector); //Add the latitude and longitude indices tempVector = new double[1]; tempVector[0] = latIndex; _CellEnvironment.Add("LatIndex", tempVector); tempVector = new double[1]; tempVector[0] = lonIndex; _CellEnvironment.Add("LonIndex", tempVector); // Add the missing value of data in the grid cell to the cell environment tempVector = new double[1]; tempVector[0] = missingValue; _CellEnvironment.Add("Missing Value", tempVector); // Loop through environmental data layers and extract values for this grid cell // Also standardise missing values // Loop over variables in the list of environmental data foreach (string LayerName in dataLayers.Keys) { // Initiliase the temporary vector of values to be equal to the number of time intervals in the environmental variable tempVector = new double[dataLayers[LayerName].NumTimes]; // Loop over the time intervals in the environmental variable for (int hh = 0; hh < dataLayers[LayerName].NumTimes; hh++) { // Add the value of the environmental variable at this time interval to the temporary vector tempVector[hh] = dataLayers[LayerName].GetValue(_Latitude, _Longitude, (uint)hh, out EnviroMissingValue,latCellSize,lonCellSize); // If the environmental variable is a missing value, then change the value to equal the standard missing value for this cell if (EnviroMissingValue) tempVector[hh] = missingValue; } // Add the values of the environmental variables to the cell environment, with the name of the variable as the key _CellEnvironment.Add(LayerName, tempVector); } if (_CellEnvironment.ContainsKey("LandSeaMask")) { if (_CellEnvironment["LandSeaMask"][0].CompareTo(0.0) == 0) { if (ContainsData(_CellEnvironment["OceanTemp"], _CellEnvironment["Missing Value"][0])) { //This is a marine cell tempVector = new double[1]; tempVector[0] = 2.0; _CellEnvironment.Add("Realm", tempVector); _CellEnvironment.Add("NPP", _CellEnvironment["OceanNPP"]); _CellEnvironment.Add("DiurnalTemperatureRange", _CellEnvironment["OceanDTR"]); if (_CellEnvironment.ContainsKey("Temperature")) { if(_CellEnvironment.ContainsKey("SST")) { _CellEnvironment["Temperature"] = _CellEnvironment["SST"]; } else { } } else { _CellEnvironment.Add("Temperature", _CellEnvironment["SST"]); } } else { //This is a freshwater cell and in this model formulation is characterised as belonging to the terrestrial realm tempVector = new double[1]; tempVector[0] = 2.0; _CellEnvironment.Add("Realm", tempVector); _CellEnvironment.Add("NPP", _CellEnvironment["LandNPP"]); _CellEnvironment.Add("DiurnalTemperatureRange", _CellEnvironment["LandDTR"]); } } else { //This is a land cell tempVector = new double[1]; tempVector[0] = 1.0; _CellEnvironment.Add("Realm", tempVector); _CellEnvironment.Add("NPP", _CellEnvironment["LandNPP"]); _CellEnvironment.Add("DiurnalTemperatureRange", _CellEnvironment["LandDTR"]); } } else { Debug.Fail("No land sea mask defined - a mask is required to initialise appropriate ecology"); } //Calculate and add the standard deviation of monthly temperature as a measure of seasonality //Also calculate and add the annual mean temperature for this cell tempVector = new double[12]; double[] sdtemp = new double[12]; double[] meantemp = new double[12]; tempVector = _CellEnvironment["Temperature"]; double Average = tempVector.Average(); meantemp[0] = Average; double SumOfSquaresDifferences = tempVector.Select(val => (val - Average) * (val - Average)).Sum(); sdtemp[0] = Math.Sqrt(SumOfSquaresDifferences / tempVector.Length); _CellEnvironment.Add("SDTemperature", sdtemp); _CellEnvironment.Add("AnnualTemperature", meantemp); //Remove unrequired cell environment layers if (_CellEnvironment.ContainsKey("LandNPP")) _CellEnvironment.Remove("LandNPP"); if (_CellEnvironment.ContainsKey("LandDTR")) _CellEnvironment.Remove("LandDTR"); if (_CellEnvironment.ContainsKey("OceanNPP")) _CellEnvironment.Remove("OceanNPP"); if (_CellEnvironment.ContainsKey("OceanDTR")) _CellEnvironment.Remove("OceanDTR"); if (_CellEnvironment.ContainsKey("SST")) _CellEnvironment.Remove("SST"); // CREATE NPP SEASONALITY LAYER _CellEnvironment.Add("Seasonality", CalculateNPPSeasonality(_CellEnvironment["NPP"], _CellEnvironment["Missing Value"][0])); // Calculate other climate variables from temperature and precipitation // Declare an instance of the climate variables calculator ClimateVariablesCalculator CVC = new ClimateVariablesCalculator(); // Calculate the fraction of the year that experiences frost double[] NDF = new double[1]; NDF[0] = CVC.GetNDF(_CellEnvironment["FrostDays"], _CellEnvironment["Temperature"],_CellEnvironment["Missing Value"][0]); _CellEnvironment.Add("Fraction Year Frost", NDF); double[] frostMonthly = new double[12]; frostMonthly[0] = Math.Min(_CellEnvironment["FrostDays"][0] / 31.0, 1.0); frostMonthly[1] = Math.Min(_CellEnvironment["FrostDays"][1] / 28.0, 1.0); frostMonthly[2] = Math.Min(_CellEnvironment["FrostDays"][2] / 31.0, 1.0); frostMonthly[3] = Math.Min(_CellEnvironment["FrostDays"][3] / 30.0, 1.0); frostMonthly[4] = Math.Min(_CellEnvironment["FrostDays"][4] / 31.0, 1.0); frostMonthly[5] = Math.Min(_CellEnvironment["FrostDays"][5] / 30.0, 1.0); frostMonthly[6] = Math.Min(_CellEnvironment["FrostDays"][6] / 31.0, 1.0); frostMonthly[7] = Math.Min(_CellEnvironment["FrostDays"][7] / 31.0, 1.0); frostMonthly[8] = Math.Min(_CellEnvironment["FrostDays"][8] / 30.0, 1.0); frostMonthly[9] = Math.Min(_CellEnvironment["FrostDays"][9] / 31.0, 1.0); frostMonthly[10] = Math.Min(_CellEnvironment["FrostDays"][10] / 30.0, 1.0); frostMonthly[11] = Math.Min(_CellEnvironment["FrostDays"][11] / 31.0, 1.0); _CellEnvironment.Add("Fraction Month Frost", frostMonthly); _CellEnvironment.Remove("FrostDays"); // Calculate AET and the fractional length of the fire season Tuple<double[], double, double> TempTuple = new Tuple<double[], double, double>(new double[12], new double(), new double()); TempTuple = CVC.MonthlyActualEvapotranspirationSoilMoisture(_CellEnvironment["AWC"][0], _CellEnvironment["Precipitation"], _CellEnvironment["Temperature"]); _CellEnvironment.Add("AET", TempTuple.Item1); _CellEnvironment.Add("Fraction Year Fire", new double[1] { TempTuple.Item3 / 360 }); // Designate a breeding season for this grid cell, where a month is considered to be part of the breeding season if its NPP is at // least 80% of the maximum NPP throughout the whole year double[] BreedingSeason = new double[12]; for (int i = 0; i < 12; i++) { if ((_CellEnvironment["Seasonality"][i] / _CellEnvironment["Seasonality"].Max()) > 0.5) { BreedingSeason[i] = 1.0; } else { BreedingSeason[i] = 0.0; } } _CellEnvironment.Add("Breeding Season", BreedingSeason); // Initialise the grid cell cohort and stock handlers _GridCellCohorts = new GridCellCohortHandler(cohortFunctionalGroups.GetNumberOfFunctionalGroups()); _GridCellStocks = new GridCellStockHandler(stockFunctionalGroups.GetNumberOfFunctionalGroups()); }
/// <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 }
/// <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> /// 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> /// 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> /// Assign the juvenile and adult masses of the new cohort to produce /// </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="madingleyCohortDefinitions">The definitions of cohort functional groups in the model</param> /// <returns>A vector containing the juvenile and adult masses of the cohort to be produced</returns> private double[] GetOffspringCohortProperties(GridCellCohortHandler gridCellCohorts, int[] actingCohort, FunctionalGroupDefinitions madingleyCohortDefinitions) { // A two-element vector holding adult and juvenile body masses in elements zero and one respectively double[] _CohortJuvenileAdultMasses = new double[2]; // Determine whether offspring cohort 'evolves' in terms of adult and juvenile body masses if (RandomNumberGenerator.GetUniform() > _MassEvolutionProbabilityThreshold) { // Determine the new juvenile body mass _CohortJuvenileAdultMasses[0] = Math.Max(RandomNumberGenerator.GetNormal(gridCellCohorts[actingCohort].JuvenileMass, _MassEvolutionStandardDeviation * gridCellCohorts[actingCohort].JuvenileMass), madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup("Minimum mass",actingCohort[0])); // Determine the new adult body mass _CohortJuvenileAdultMasses[1] = Math.Min(RandomNumberGenerator.GetNormal(gridCellCohorts[actingCohort].AdultMass, _MassEvolutionStandardDeviation * gridCellCohorts[actingCohort].AdultMass), madingleyCohortDefinitions.GetBiologicalPropertyOneFunctionalGroup("Maximum mass", actingCohort[0])); } // If not, it just gets the same values as the parent cohort else { // Assign masses to the offspring cohort that are equal to those of the parent cohort _CohortJuvenileAdultMasses[0] = gridCellCohorts[actingCohort].JuvenileMass; _CohortJuvenileAdultMasses[1] = gridCellCohorts[actingCohort].AdultMass; } // Return the vector of adult and juvenile masses return _CohortJuvenileAdultMasses; }