/// <summary> /// Constructor for process tracker: Initialises the trackers for individual processes /// </summary> /// <param name="numTimesteps">The number of time steps in the model</param> /// <param name="lats">The latitudes of active grid cells in the model</param> /// <param name="lons">The longitudes of active grid cells in the model</param> /// <param name="cellIndices">List of indices of active cells in the model grid</param> /// <param name="Filenames">The filenames of the output files to write the tracking results to</param> /// <param name="trackProcesses">Whether to track processes</param> /// <param name="cohortDefinitions">The definitions for cohort functional groups in the model</param> /// <param name="missingValue">The missing value to use in process tracking output files</param> /// <param name="outputFileSuffix">The suffix to be applied to output files from process tracking</param> /// <param name="outputPath">The path to the folder to be used for process tracking outputs</param> /// <param name="trackerMassBins">The mass bins to use for categorising output data in the process trackers</param> /// <param name="specificLocations">Whether the model is being run for specific locations</param> /// <param name="initialisation">The Madingley Model initialisation</param> /// <param name="latCellSize">The size of grid cells latitudinally</param> /// <param name="lonCellSize">The size of grid cells longitudinally</param> public GlobalProcessTracker(uint numTimesteps, float[] lats, float[] lons, List<uint[]> cellIndices, SortedList<string, string> Filenames, Boolean trackProcesses, FunctionalGroupDefinitions cohortDefinitions, FunctionalGroupDefinitions stockDefinitions, double missingValue, string outputFileSuffix, string outputPath, MassBinsHandler trackerMassBins, Boolean specificLocations, MadingleyModelInitialisation initialisation, float latCellSize, float lonCellSize) { // Initialise trackers for ecological processes _TrackProcesses = trackProcesses; if (_TrackProcesses) { _TrackNPP = new GlobalNPPTracker(outputPath, lats.Length, lons.Length, lats, lons, latCellSize, lonCellSize, (int)numTimesteps,stockDefinitions.GetNumberOfFunctionalGroups(),outputFileSuffix); } }
/// <summary> /// Constructor for process tracker: Initialises the trackers for individual processes /// </summary> /// <param name="numTimesteps">The number of time steps in the model</param> /// <param name="lats">The latitudes of active grid cells in the model</param> /// <param name="lons">The longitudes of active grid cells in the model</param> /// <param name="cellIndices">List of indices of active cells in the model grid</param> /// <param name="Filenames">The filenames of the output files to write the tracking results to</param> /// <param name="trackProcesses">Whether to track processes</param> /// <param name="cohortDefinitions">The definitions for cohort functional groups in the model</param> /// <param name="missingValue">The missing value to use in process tracking output files</param> /// <param name="outputFileSuffix">The suffix to be applied to output files from process tracking</param> /// <param name="outputPath">The path to the folder to be used for process tracking outputs</param> /// <param name="trackerMassBins">The mass bins to use for categorising output data in the process trackers</param> /// <param name="specificLocations">Whether the model is being run for specific locations</param> /// <param name="initialisation">The Madingley Model initialisation</param> /// <param name="latCellSize">The size of grid cells latitudinally</param> /// <param name="lonCellSize">The size of grid cells longitudinally</param> public GlobalProcessTracker(uint numTimesteps, float[] lats, float[] lons, List <uint[]> cellIndices, SortedList <string, string> Filenames, Boolean trackProcesses, FunctionalGroupDefinitions cohortDefinitions, FunctionalGroupDefinitions stockDefinitions, double missingValue, string outputFileSuffix, string outputPath, MassBinsHandler trackerMassBins, Boolean specificLocations, MadingleyModelInitialisation initialisation, float latCellSize, float lonCellSize) { // Initialise trackers for ecological processes _TrackProcesses = trackProcesses; if (_TrackProcesses) { _TrackNPP = new GlobalNPPTracker(outputPath, lats.Length, lons.Length, lats, lons, latCellSize, lonCellSize, (int)numTimesteps, stockDefinitions.GetNumberOfFunctionalGroups(), outputFileSuffix); } }
/// <summary> /// Seed grid cell with stocks, as specified in the model input files /// </summary> /// <param name="functionalGroups">A reference to the stock functional group handler</param> /// <param name="cellEnvironment">The environment in the grid cell</param> /// <param name="globalDiagnostics">A list of global diagnostic variables for the model grid</param> private void SeedGridCellStocks(ref FunctionalGroupDefinitions functionalGroups, ref SortedList<string, double[]> cellEnvironment, SortedList<string, double> globalDiagnostics) { // Set the seed for the random number generator from the system time RandomNumberGenerator.SetSeedFromSystemTime(); Stock NewStock; // Define local variables int[] FunctionalGroupsToUse; // Get the individual body masses for organisms in each stock functional group double[] IndividualMass = functionalGroups.GetBiologicalPropertyAllFunctionalGroups("individual mass"); // Check which realm the cell is in if (cellEnvironment["Realm"][0] == 1.0 && _CellEnvironment["Precipitation"][0] != _CellEnvironment["Missing Value"][0] && _CellEnvironment["Temperature"][0] != _CellEnvironment["Missing Value"][0]) { // Get the indices of all terrestrial functional groups FunctionalGroupsToUse = functionalGroups.GetFunctionalGroupIndex("realm", "terrestrial", true); } else if (cellEnvironment["Realm"][0] == 2.0 && _CellEnvironment["NPP"][0] != _CellEnvironment["Missing Value"][0]) { // Get the indices of all marine functional groups FunctionalGroupsToUse = functionalGroups.GetFunctionalGroupIndex("realm", "marine", true); } else { // For cells without a realm designation, no functional groups will be used FunctionalGroupsToUse = new int[0]; } // Loop over all functional groups in the model for (int FunctionalGroup = 0; FunctionalGroup < functionalGroups.GetNumberOfFunctionalGroups(); FunctionalGroup++) { // Create a new list to hold the stocks in the grid cell _GridCellStocks[FunctionalGroup] = new List<Stock>(); // If it is a functional group that corresponds to the current realm, then seed the stock if (FunctionalGroupsToUse.Contains(FunctionalGroup)) { if (_CellEnvironment["Realm"][0] == 1.0) { // An instance of the terrestrial carbon model class RevisedTerrestrialPlantModel PlantModel = new RevisedTerrestrialPlantModel(); // Calculate predicted leaf mass at equilibrium for this stock double LeafMass = PlantModel.CalculateEquilibriumLeafMass(_CellEnvironment, functionalGroups.GetTraitNames("leaf strategy", FunctionalGroup) == "deciduous"); // Initialise the new stock with the relevant properties NewStock = new Stock((byte)FunctionalGroup, IndividualMass[FunctionalGroup], LeafMass); // Add the new stock to the list of grid cell stocks _GridCellStocks[FunctionalGroup].Add(NewStock); // Increment the variable tracking the total number of stocks in the model globalDiagnostics["NumberOfStocksInModel"]++; } else if (FunctionalGroupsToUse.Contains(FunctionalGroup)) { // Initialise the new stock with the relevant properties NewStock = new Stock((byte)FunctionalGroup, IndividualMass[FunctionalGroup], 1e12); // Add the new stock to the list of grid cell stocks _GridCellStocks[FunctionalGroup].Add(NewStock); // Increment the variable tracking the total number of stocks in the model globalDiagnostics["NumberOfStocksInModel"]++; } else { } } } }
/// <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> /// Seed grid cell with cohorts, as specified in the model input files /// </summary> /// <param name="functionalGroups">The functional group definitions for cohorts in the grid cell</param> /// <param name="cellEnvironment">The environment in the grid cell</param> /// <param name="globalDiagnostics">A list of global diagnostic variables</param> /// <param name="nextCohortID">YThe unique ID to assign to the next cohort produced</param> /// <param name="tracking">boolean to indicate if cohorts are to be tracked in this model</param> /// <param name="totalCellTerrestrialCohorts">The total number of cohorts to be seeded in each terrestrial grid cell</param> /// <param name="totalCellMarineCohorts">The total number of cohorts to be seeded in each marine grid cell</param> /// <param name="DrawRandomly">Whether the model is set to use random draws</param> /// <param name="ZeroAbundance">Set this parameter to 'true' if you want to seed the cohorts with zero abundance</param> private void SeedGridCellCohorts(ref FunctionalGroupDefinitions functionalGroups, ref SortedList<string, double[]> cellEnvironment, SortedList<string, double> globalDiagnostics, Int64 nextCohortID, Boolean tracking, double totalCellTerrestrialCohorts, double totalCellMarineCohorts, Boolean DrawRandomly, Boolean ZeroAbundance) { // Set the seed for the random number generator from the system time RandomNumberGenerator.SetSeedFromSystemTime(); // StreamWriter tempsw = new StreamWriter("C://Temp//adult_juvenile_masses.txt"); // tempsw.WriteLine("adult mass\tjuvenilemass"); // Define local variables double CohortJuvenileMass; double CohortAdultMassRatio; double CohortAdultMass; double ExpectedLnAdultMassRatio; int[] FunctionalGroupsToUse; double NumCohortsThisCell; double TotalNewBiomass =0.0; // Get the minimum and maximum possible body masses for organisms in each functional group double[] MassMinima = functionalGroups.GetBiologicalPropertyAllFunctionalGroups("minimum mass"); double[] MassMaxima = functionalGroups.GetBiologicalPropertyAllFunctionalGroups("maximum mass"); string[] NutritionSource = functionalGroups.GetTraitValuesAllFunctionalGroups("nutrition source"); double[] ProportionTimeActive = functionalGroups.GetBiologicalPropertyAllFunctionalGroups("proportion suitable time active"); //Variable for altering the juvenile to adult mass ratio for marine cells when handling certain functional groups eg baleen whales double Scaling = 0.0; Int64 CohortIDIncrementer = nextCohortID; // Check which realm the cell is in if (cellEnvironment["Realm"][0] == 1.0) { // Get the indices of all terrestrial functional groups FunctionalGroupsToUse = functionalGroups.GetFunctionalGroupIndex("realm", "terrestrial", true); NumCohortsThisCell = totalCellTerrestrialCohorts; } else { // Get the indices of all marine functional groups FunctionalGroupsToUse = functionalGroups.GetFunctionalGroupIndex("realm", "marine", true); NumCohortsThisCell = totalCellMarineCohorts; } Debug.Assert(cellEnvironment["Realm"][0] > 0.0, "Missing realm for grid cell"); if (NumCohortsThisCell > 0) { // Loop over all functional groups in the model for (int FunctionalGroup = 0; FunctionalGroup < functionalGroups.GetNumberOfFunctionalGroups(); FunctionalGroup++) { // Create a new list to hold the cohorts in the grid cell _GridCellCohorts[FunctionalGroup] = new List<Cohort>(); // If it is a functional group that corresponds to the current realm, then seed cohorts if (FunctionalGroupsToUse.Contains(FunctionalGroup)) { // Loop over the initial number of cohorts double NumberOfCohortsInThisFunctionalGroup = 1.0; if (!ZeroAbundance) { NumberOfCohortsInThisFunctionalGroup = functionalGroups.GetBiologicalPropertyOneFunctionalGroup("initial number of gridcellcohorts", FunctionalGroup); } for (int jj = 0; jj < NumberOfCohortsInThisFunctionalGroup; jj++) { // Check whether the model is set to randomly draw the body masses of new cohorts if (DrawRandomly) { // Draw adult mass from a log-normal distribution with mean -6.9 and standard deviation 10.0, // within the bounds of the minimum and maximum body masses for the functional group CohortAdultMass = Math.Pow(10, (RandomNumberGenerator.GetUniform() * (Math.Log10(MassMaxima[FunctionalGroup]) - Math.Log10(50 * MassMinima[FunctionalGroup])) + Math.Log10(50 * MassMinima[FunctionalGroup]))); // Terrestrial and marine organisms have different optimal prey/predator body mass ratios if (cellEnvironment["Realm"][0] == 1.0) // Optimal prey body size 10% OptimalPreyBodySizeRatio = Math.Max(0.01, RandomNumberGenerator.GetNormal(0.1, 0.02)); else { if (functionalGroups.GetTraitNames("Diet", FunctionalGroup) == "allspecial") { // Note that for this group // it is actually (despite the name) not an optimal prey body size ratio, but an actual body size. // This is because it is invariant as the predator (filter-feeding baleen whale) grows. // See also the predation classes. OptimalPreyBodySizeRatio = Math.Max(0.00001, RandomNumberGenerator.GetNormal(0.0001, 0.1)); } else { // Optimal prey body size or marine organisms is 10% OptimalPreyBodySizeRatio = Math.Max(0.01, RandomNumberGenerator.GetNormal(0.1, 0.02)); } } // Draw from a log-normal distribution with mean 10.0 and standard deviation 5.0, then add one to obtain // the ratio of adult to juvenile body mass, and then calculate juvenile mass based on this ratio and within the // bounds of the minimum and maximum body masses for this functional group if (cellEnvironment["Realm"][0] == 1.0) { do { ExpectedLnAdultMassRatio = 2.24 + 0.13 * Math.Log(CohortAdultMass); CohortAdultMassRatio = 1.0 + RandomNumberGenerator.GetLogNormal(ExpectedLnAdultMassRatio, 0.5); CohortJuvenileMass = CohortAdultMass * 1.0 / CohortAdultMassRatio; } while (CohortAdultMass <= CohortJuvenileMass || CohortJuvenileMass < MassMinima[FunctionalGroup]); } // In the marine realm, have a greater difference between the adult and juvenile body masses, on average else { uint Counter = 0; Scaling = 0.2; // Use the scaling to deal with baleen whales not having such a great difference do { ExpectedLnAdultMassRatio = 2.5 + Scaling * Math.Log(CohortAdultMass); CohortAdultMassRatio = 1.0 + 10 * RandomNumberGenerator.GetLogNormal(ExpectedLnAdultMassRatio, 0.5); CohortJuvenileMass = CohortAdultMass * 1.0 / CohortAdultMassRatio; Counter++; if (Counter > 10) { Scaling -= 0.01; Counter = 0; } } while (CohortAdultMass <= CohortJuvenileMass || CohortJuvenileMass < MassMinima[FunctionalGroup]); } } else { // Use the same seed for the random number generator every time RandomNumberGenerator.SetSeed((uint)(jj + 1), (uint)((jj + 1) * 3)); // Draw adult mass from a log-normal distribution with mean -6.9 and standard deviation 10.0, // within the bounds of the minimum and maximum body masses for the functional group CohortAdultMass = Math.Pow(10, (RandomNumberGenerator.GetUniform() * (Math.Log10(MassMaxima[FunctionalGroup]) - Math.Log10(50 * MassMinima[FunctionalGroup])) + Math.Log10(50 * MassMinima[FunctionalGroup]))); OptimalPreyBodySizeRatio = Math.Max(0.01, RandomNumberGenerator.GetNormal(0.1, 0.02)); // Draw from a log-normal distribution with mean 10.0 and standard deviation 5.0, then add one to obtain // the ratio of adult to juvenile body mass, and then calculate juvenile mass based on this ratio and within the // bounds of the minimum and maximum body masses for this functional group if (cellEnvironment["Realm"][0] == 1.0) { do { ExpectedLnAdultMassRatio = 2.24 + 0.13 * Math.Log(CohortAdultMass); CohortAdultMassRatio = 1.0 + RandomNumberGenerator.GetLogNormal(ExpectedLnAdultMassRatio, 0.5); CohortJuvenileMass = CohortAdultMass * 1.0 / CohortAdultMassRatio; } while (CohortAdultMass <= CohortJuvenileMass || CohortJuvenileMass < MassMinima[FunctionalGroup]); } // In the marine realm, have a greater difference between the adult and juvenile body masses, on average else { do { ExpectedLnAdultMassRatio = 2.24 + 0.13 * Math.Log(CohortAdultMass); CohortAdultMassRatio = 1.0 + 10 * RandomNumberGenerator.GetLogNormal(ExpectedLnAdultMassRatio, 0.5); CohortJuvenileMass = CohortAdultMass * 1.0 / CohortAdultMassRatio; } while (CohortAdultMass <= CohortJuvenileMass || CohortJuvenileMass < MassMinima[FunctionalGroup]); } } // An instance of Cohort to hold the new cohort Cohort NewCohort; //double NewBiomass = Math.Pow(0.2, (Math.Log10(CohortAdultMass))) * (1.0E9 * _CellEnvironment["Cell Area"][0]) / NumCohortsThisCell; // 3000*(0.6^log(mass)) gives individual cohort biomass density in g ha-1 // * 100 to give g km-2 // * cell area to give g grid cell //*3300/NumCohortsThisCell scales total initial biomass in the cell to some approximately reasonable mass double NewBiomass = (3300 / NumCohortsThisCell) * 100 * 3000 * Math.Pow(0.6, (Math.Log10(CohortJuvenileMass))) * (_CellEnvironment["Cell Area"][0]); TotalNewBiomass += NewBiomass; double NewAbund = 0.0; if (!ZeroAbundance) { NewAbund = NewBiomass / CohortJuvenileMass; } /* // TEMPORARILY MARINE ONLY if (cellEnvironment["Realm"][0] == 1) { NewAbund = 0.0; } */ double TrophicIndex; switch (NutritionSource[FunctionalGroup]) { case "herbivore": TrophicIndex = 2; break; case "omnivore": TrophicIndex = 2.5; break; case "carnivore": TrophicIndex = 3; break; default: Debug.Fail("Unexpected nutrition source trait value when assigning trophic index"); TrophicIndex = 0.0; break; } // Initialise the new cohort with the relevant properties NewCohort = new Cohort((byte)FunctionalGroup, CohortJuvenileMass, CohortAdultMass, CohortJuvenileMass, NewAbund, OptimalPreyBodySizeRatio, (ushort)0, ProportionTimeActive[FunctionalGroup], ref CohortIDIncrementer,TrophicIndex, tracking); // Add the new cohort to the list of grid cell cohorts _GridCellCohorts[FunctionalGroup].Add(NewCohort); // TEMPORARY /* // Check whether the model is set to randomly draw the body masses of new cohorts if ((Longitude % 4 == 0) && (Latitude % 4 == 0)) { if (DrawRandomly) { CohortAdultMass = 100000; CohortJuvenileMass = 100000; } else { CohortAdultMass = 100000; CohortJuvenileMass = 100000; } // An instance of Cohort to hold the new cohort Cohort NewCohort; double NewBiomass = (1.0E7 * _CellEnvironment["Cell Area"][0]) / NumCohortsThisCell; double NewAbund = 0.0; NewAbund = 3000; // Initialise the new cohort with the relevant properties NewCohort = new Cohort((byte)FunctionalGroup, CohortJuvenileMass, CohortAdultMass, CohortJuvenileMass, NewAbund, (ushort)0, ref nextCohortID, tracking); // Add the new cohort to the list of grid cell cohorts _GridCellCohorts[FunctionalGroup].Add(NewCohort); } */ // Incrememt the variable tracking the total number of cohorts in the model globalDiagnostics["NumberOfCohortsInModel"]++; } } } } else { // Loop over all functional groups in the model for (int FunctionalGroup = 0; FunctionalGroup < functionalGroups.GetNumberOfFunctionalGroups(); FunctionalGroup++) { // Create a new list to hold the cohorts in the grid cell _GridCellCohorts[FunctionalGroup] = new List<Cohort>(); } } // tempsw.Dispose(); }
public void OutputCurrentModelState(ModelGrid currentModelGrid, FunctionalGroupDefinitions functionalGroupHandler, List<uint[]> cellIndices, uint currentTimestep, int maximumNumberOfCohorts, string filename) { float[] Latitude = currentModelGrid.Lats; float[] Longitude = currentModelGrid.Lons; float[] CohortFunctionalGroup = new float[functionalGroupHandler.GetNumberOfFunctionalGroups()]; for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++) { CohortFunctionalGroup[fg] = fg; } int CellCohortNumber = 0; GridCellCohortHandler CellCohorts; for (int cellIndex = 0; cellIndex < cellIndices.Count; cellIndex++) { CellCohorts = currentModelGrid.GetGridCellCohorts(cellIndices[cellIndex][0], cellIndices[cellIndex][1]); for (int i = 0; i < CellCohorts.Count; i++) { if (CellCohorts[i].Count > CellCohortNumber) CellCohortNumber = CellCohorts[i].Count; } } int MaxNumberCohorts = Math.Max(CellCohortNumber, maximumNumberOfCohorts); float[] Cohort = new float[MaxNumberCohorts]; for (int c = 0; c < Cohort.Length; c++) { Cohort[c] = c; } //Define an array for stock functional group - there are only three currently float[] StockFunctionalGroup = new float[] { 1, 2, 3 }; //Define an array for index of stocks - there is only one currently float[] Stock = new float[] { 1 }; string Filename = filename + "_" + currentTimestep.ToString() + Simulation.ToString(); StateOutput = SDSCreator.CreateSDS("netCDF", Filename, _OutputPath); //Define the cohort properties for output string[] CohortProperties = new string[] {"JuvenileMass", "AdultMass", "IndividualBodyMass", "CohortAbundance", "BirthTimeStep", "MaturityTimeStep", "LogOptimalPreyBodySizeRatio", "MaximumAchievedBodyMass","Merged","TrophicIndex","ProportionTimeActive"}; //define the dimensions for cohort outputs string[] dims = new string[] { "Latitude", "Longitude", "Cohort Functional Group", "Cohort" }; // Add the variables for each cohort property // Then calculate the state for this property and put the data to this variable foreach (string v in CohortProperties) { DataConverter.AddVariable(StateOutput, "Cohort" + v, 4, dims, currentModelGrid.GlobalMissingValue, Latitude, Longitude, CohortFunctionalGroup, Cohort); StateOutput.PutData<double[, , ,]>("Cohort" + v, CalculateCurrentCohortState(currentModelGrid, v, Latitude.Length, Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length, cellIndices)); StateOutput.Commit(); } //Define the stock properties for output string[] StockProperties = new string[] { "IndividualBodyMass", "TotalBiomass" }; //define the dimensions for cohort outputs dims = new string[] { "Latitude", "Longitude", "Stock Functional Group", "Stock" }; // Add the variables for each stock property // Then calculate the state for this property and put the data to this variable foreach (string v in StockProperties) { DataConverter.AddVariable(StateOutput, "Stock" + v, 4, dims, currentModelGrid.GlobalMissingValue, Latitude, Longitude, StockFunctionalGroup, Stock); StateOutput.PutData<double[, , ,]>("Stock" + v, CalculateCurrentStockState(currentModelGrid, v, Latitude.Length, Longitude.Length, StockFunctionalGroup.Length, Stock.Length, cellIndices)); StateOutput.Commit(); } //Close this data set StateOutput.Dispose(); }
public void OutputCurrentModelState(ModelGrid currentModelGrid, FunctionalGroupDefinitions functionalGroupHandler, List <uint[]> cellIndices, uint currentTimestep, int maximumNumberOfCohorts, string filename) { float[] Latitude = currentModelGrid.Lats; float[] Longitude = currentModelGrid.Lons; float[] CohortFunctionalGroup = new float[functionalGroupHandler.GetNumberOfFunctionalGroups()]; for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++) { CohortFunctionalGroup[fg] = fg; } int CellCohortNumber = 0; GridCellCohortHandler CellCohorts; for (int cellIndex = 0; cellIndex < cellIndices.Count; cellIndex++) { CellCohorts = currentModelGrid.GetGridCellCohorts(cellIndices[cellIndex][0], cellIndices[cellIndex][1]); for (int i = 0; i < CellCohorts.Count; i++) { if (CellCohorts[i].Count > CellCohortNumber) { CellCohortNumber = CellCohorts[i].Count; } } } int MaxNumberCohorts = Math.Max(CellCohortNumber, maximumNumberOfCohorts); float[] Cohort = new float[MaxNumberCohorts]; for (int c = 0; c < Cohort.Length; c++) { Cohort[c] = c; } //Define an array for stock functional group - there are only three currently float[] StockFunctionalGroup = new float[] { 1, 2, 3 }; //Define an array for index of stocks - there is only one currently float[] Stock = new float[] { 1 }; string Filename = filename + "_" + currentTimestep.ToString() + Simulation.ToString(); StateOutput = SDSCreator.CreateSDS("netCDF", Filename, _OutputPath); //Define the cohort properties for output string[] CohortProperties = new string[] { "JuvenileMass", "AdultMass", "IndividualBodyMass", "IndividualReproductivePotentialMass", "CohortAbundance", "BirthTimeStep", "MaturityTimeStep", "LogOptimalPreyBodySizeRatio", "MaximumAchievedBodyMass", "Merged", "TrophicIndex", "ProportionTimeActive" }; //define the dimensions for cohort outputs string[] dims = new string[] { "Latitude", "Longitude", "Cohort Functional Group", "Cohort" }; // Add the variables for each cohort property // Then calculate the state for this property and put the data to this variable foreach (string v in CohortProperties) { DataConverter.AddVariable(StateOutput, "Cohort" + v, 4, dims, currentModelGrid.GlobalMissingValue, Latitude, Longitude, CohortFunctionalGroup, Cohort); StateOutput.PutData <double[, , , ]>("Cohort" + v, CalculateCurrentCohortState(currentModelGrid, v, Latitude.Length, Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length, cellIndices)); StateOutput.Commit(); } //Define the stock properties for output string[] StockProperties = new string[] { "IndividualBodyMass", "TotalBiomass" }; //define the dimensions for cohort outputs dims = new string[] { "Latitude", "Longitude", "Stock Functional Group", "Stock" }; // Add the variables for each stock property // Then calculate the state for this property and put the data to this variable foreach (string v in StockProperties) { DataConverter.AddVariable(StateOutput, "Stock" + v, 4, dims, currentModelGrid.GlobalMissingValue, Latitude, Longitude, StockFunctionalGroup, Stock); StateOutput.PutData <double[, , , ]>("Stock" + v, CalculateCurrentStockState(currentModelGrid, v, Latitude.Length, Longitude.Length, StockFunctionalGroup.Length, Stock.Length, cellIndices)); StateOutput.Commit(); } //Close this data set StateOutput.Dispose(); }
/// <summary> /// Seed the stocks and cohorts from output from a previous simulation /// </summary> /// <param name="cellIndices">A list of the active cells in the model grid</param> /// <param name="cohortFunctionalGroupDefinitions">The functional group definitions for cohorts in the model</param> /// <param name="stockFunctionalGroupDefinitions">The functional group definitions for stocks in the model</param> /// <param name="globalDiagnostics">A list of global diagnostic variables</param> /// <param name="nextCohortID">The ID number to be assigned to the next produced cohort</param> /// <param name="tracking">Whether process-tracking is enabled</param> /// <param name="DrawRandomly">Whether the model is set to use a random draw</param> /// <param name="dispersalOnly">Whether to run dispersal only (i.e. to turn off all other ecological processes</param> /// <param name="processTrackers">An instance of the ecological process tracker</param> public void SeedGridCellStocksAndCohorts(List<uint[]> cellIndices, InputModelState inputModelState, FunctionalGroupDefinitions cohortFunctionalGroupDefinitions, FunctionalGroupDefinitions stockFunctionalGroupDefinitions) { int ii = 1; Console.WriteLine("Seeding grid cell stocks and cohorts:"); //Check to see if the correct number of functional groups exist in the definitions file and in the input state if (cohortFunctionalGroupDefinitions.GetNumberOfFunctionalGroups() != inputModelState.GridCellCohorts[ cellIndices[0][0],cellIndices[0][1]].Count) { Console.WriteLine("Mismatch in the number of functional groups defined in CohortFunctionalGroupDefinitions.csv set-up file and the Model State being read in"); Environment.Exit(0); } int[] TerrestrialStockFunctionalIndices = stockFunctionalGroupDefinitions.GetFunctionalGroupIndex("Realm", "Terrestrial", false); int[] MarineStockFunctionalIndices = stockFunctionalGroupDefinitions.GetFunctionalGroupIndex("Realm", "Marine", false); int[] TerrestrialCohortFunctionalIndices = cohortFunctionalGroupDefinitions.GetFunctionalGroupIndex("Realm", "Terrestrial", false); int[] MarineCohortFunctionalIndices = cohortFunctionalGroupDefinitions.GetFunctionalGroupIndex("Realm", "Marine", false); foreach (uint[] cellIndexPair in cellIndices) { for (int i = 0; i < inputModelState.GridCellCohorts[cellIndexPair[0], cellIndexPair[1]].Count; i++) { InternalGrid[cellIndexPair[0], cellIndexPair[1]].GridCellCohorts[i] = new List<Cohort>(); } //Check which cohorts should be initialised for each cell if (InternalGrid[cellIndexPair[0], cellIndexPair[1]].CellEnvironment["Realm"][0] == 1) { //This is a terrestrial cell so only add the terrestrial stocks foreach (int fg in TerrestrialCohortFunctionalIndices) { //Cohort[] tempGridCellCohorts = (Cohort[])inputModelState.GridCellCohorts[cellIndexPair[0], cellIndexPair[1]][fg].ToArray().Clone(); //Cohort[] tempGridCellCohorts = (Cohort[])Array.ConvertAll(inputModelState.GridCellCohorts[cellIndexPair[0], cellIndexPair[1]][fg].ToArray(), // element => (Cohort)element.Clone()); Cohort[] tempGridCellCohorts = inputModelState.GridCellCohorts[cellIndexPair[0], cellIndexPair[1]][fg].ToArray().Select(cohort => new Cohort(cohort)).ToArray(); InternalGrid[cellIndexPair[0], cellIndexPair[1]].GridCellCohorts[fg] = tempGridCellCohorts.ToList(); } } else { // this is a marine cell so only add the marine stocks foreach (int fg in MarineCohortFunctionalIndices) { //Cohort[] tempGridCellCohorts = (Cohort[])inputModelState.GridCellCohorts[cellIndexPair[0], cellIndexPair[1]][fg].ToArray().Clone(); //Cohort[] tempGridCellCohorts = (Cohort[])Array.ConvertAll(inputModelState.GridCellCohorts[cellIndexPair[0], cellIndexPair[1]][fg].ToArray(), // element => (Cohort)element.Clone()); Cohort[] tempGridCellCohorts = inputModelState.GridCellCohorts[cellIndexPair[0], cellIndexPair[1]][fg].ToArray().Select(cohort => new Cohort(cohort)).ToArray(); InternalGrid[cellIndexPair[0], cellIndexPair[1]].GridCellCohorts[fg] = tempGridCellCohorts.ToList(); } } for (int i = 0; i < inputModelState.GridCellStocks[cellIndexPair[0], cellIndexPair[1]].Count; i++) { InternalGrid[cellIndexPair[0], cellIndexPair[1]].GridCellStocks[i] = new List<Stock>(); } //Check which stocks should be initialised for each cell if (InternalGrid[cellIndexPair[0], cellIndexPair[1]].CellEnvironment["Realm"][0] == 1) { //This is a terrestrial cell so only add the terrestrial stocks foreach (int fg in TerrestrialStockFunctionalIndices) { //Stock[] tempGridCellStocks = (Stock[])inputModelState.GridCellStocks[cellIndexPair[0], cellIndexPair[1]][fg].ToArray().Clone(); //Stock[] tempGridCellStocks = (Stock[])Array.ConvertAll(inputModelState.GridCellStocks[cellIndexPair[0], cellIndexPair[1]][fg].ToArray(), // element => (Stock)element.Clone()); Stock[] tempGridCellStocks = inputModelState.GridCellStocks[cellIndexPair[0], cellIndexPair[1]][fg].ToArray().Select(stock => new Stock(stock)).ToArray(); InternalGrid[cellIndexPair[0], cellIndexPair[1]].GridCellStocks[fg] = tempGridCellStocks.ToList(); } } else { // this is a marine cell so only add the marine stocks foreach (int fg in MarineStockFunctionalIndices) { //Stock[] tempGridCellStocks = (Stock[])inputModelState.GridCellStocks[cellIndexPair[0], cellIndexPair[1]][fg].ToArray().Clone(); //Stock[] tempGridCellStocks = (Stock[])Array.ConvertAll(inputModelState.GridCellStocks[cellIndexPair[0], cellIndexPair[1]][fg].ToArray(), // element => (Stock)element.Clone()); Stock[] tempGridCellStocks = inputModelState.GridCellStocks[cellIndexPair[0], cellIndexPair[1]][fg].ToArray().Select(stock => new Stock(stock)).ToArray(); InternalGrid[cellIndexPair[0], cellIndexPair[1]].GridCellStocks[fg] = tempGridCellStocks.ToList(); } } Console.Write("\rGrid Cell: {0} of {1}", ii++, cellIndices.Count); } Console.WriteLine(""); Console.WriteLine(""); }
public void SaveTimestep(int currentTimestep) { UInt32 hh = this.CurrentTimeStep; MadingleyModelInitialisation initialisation = this.ModelInitialisation; // Temporary variables Boolean varExists; // For runs with specific locations and where track processes has been specified, write out mass flows data and reset the mass flow tracker // for the next time step if (SpecificLocations) { for (var cellIndex = 0; cellIndex < _CellList.Count; cellIndex++) { if (ProcessTrackers[cellIndex].TrackProcesses) { ProcessTrackers[cellIndex].EndTimeStepPredationTracking(CurrentTimeStep); ProcessTrackers[cellIndex].EndTimeStepHerbvioryTracking(CurrentTimeStep); } } } if (TrackGlobalProcesses.TrackProcesses) { for (uint ii = 0; ii < StockFunctionalGroupDefinitions.GetNumberOfFunctionalGroups(); ii++) { TrackGlobalProcesses.StoreNPPGrid(hh, ii); TrackGlobalProcesses.StoreHANPPGrid(hh, ii); } } #endif OutputTimer.Start(); // Write the global outputs for this time step GlobalOutputs.TimeStepOutputs(EcosystemModelGrid, CurrentTimeStep, CurrentMonth, TimeStepTimer, CohortFunctionalGroupDefinitions, StockFunctionalGroupDefinitions, _CellList, GlobalDiagnosticVariables, initialisation); OutputTimer.Stop(); Console.WriteLine("Global Outputs took: {0}", OutputTimer.GetElapsedTimeSecs()); OutputTimer.Start(); if (SpecificLocations) { // Loop over grid cells and write (a) time step outputs, and (b) trophic flow data (if process tracking is on) for (int i = 0; i < _CellList.Count; i++) { // Write out the grid cell outputs for this time step CellOutputs[i].TimeStepOutputs(EcosystemModelGrid, CohortFunctionalGroupDefinitions, StockFunctionalGroupDefinitions, _CellList, i, GlobalDiagnosticVariables, TimeStepTimer, NumTimeSteps, CurrentTimeStep, initialisation, CurrentMonth, EcosystemModelGrid.GetEnviroLayer("Realm", 0, _CellList[i][0], _CellList[i][1], out varExists) == 2.0); // Write out trophic flow data for this time step if (ProcessTrackers[i].TrackProcesses) ProcessTrackers[i].WriteTimeStepTrophicFlows(CurrentTimeStep, EcosystemModelGrid.NumLatCells, EcosystemModelGrid.NumLonCells, initialisation, EcosystemModelGrid.GetEnviroLayer("Realm", 0, _CellList[i][0], _CellList[i][1], out varExists) == 2.0); } } else { // Write out grid outputs for this time step GridOutputs.TimeStepOutputs(EcosystemModelGrid, CohortFunctionalGroupDefinitions, StockFunctionalGroupDefinitions, _CellList, CurrentTimeStep, initialisation); } OutputTimer.Stop(); Console.WriteLine("Cell/Grid Outputs took: {0}", OutputTimer.GetElapsedTimeSecs()); // Write the results of dispersal to the console Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("Number of cohorts that dispersed this time step: {0}\n", Dispersals); Console.ForegroundColor = ConsoleColor.White; Dispersals = 0; if (OutputModelStateTimestep.Contains(hh)) { OutputTimer.Start(); Console.WriteLine("Outputting model state"); //Writing to text based output WriteModelState.OutputCurrentModelState(EcosystemModelGrid, _CellList, hh); WriteModelState.OutputCurrentModelState(EcosystemModelGrid, CohortFunctionalGroupDefinitions, _CellList, CurrentTimeStep, initialisation.MaxNumberOfCohorts, "ModelState"); OutputTimer.Stop(); // Write the results of dispersal to the console Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("Writing model state took: {0}", OutputTimer.GetElapsedTimeSecs()); Console.ForegroundColor = ConsoleColor.White; /*ReadModelState = new InputModelState( initialisation.OutputPath, "ModelState0");*/ } }