/// <summary>
        /// Record dispersal events in the dispersal tracker
        /// </summary>
        /// <param name="inboundCohorts">The cohorts arriving in a cell in the current time step</param>
        /// <param name="outboundCohorts">The cohorts leaving a cell in the current time step</param>
        /// <param name="outboundCohortWeights">The body masses of cohorts leaving the cell in the current time step</param>
        /// <param name="timestep">The current model time step</param>
        /// <param name="madingleyModelGrid">The model grid</param>
        public void RecordDispersalForACell(uint[, ,] inboundCohorts, uint[, ,] outboundCohorts, List<double>[,] outboundCohortWeights, uint timestep, ModelGrid madingleyModelGrid)
        {
            var count = inboundCohorts.GetLength(0) * inboundCohorts.GetLength(1);

            var copy = new Madingley.Common.GridCellDispersal[count];

            for (var kk = 0; kk < count; kk++)
            {
                var ii = (int)kk / inboundCohorts.GetLength(1);
                var jj = kk % inboundCohorts.GetLength(1);

                var denter =
                    new Tuple<Madingley.Common.CohortsEnterDirection, int>[]
                    {
                        Tuple.Create(Madingley.Common.CohortsEnterDirection.North, (int)inboundCohorts[ii, jj, 0]),
                        Tuple.Create(Madingley.Common.CohortsEnterDirection.NorthEast, (int)inboundCohorts[ii, jj, 1]),
                        Tuple.Create(Madingley.Common.CohortsEnterDirection.East, (int)inboundCohorts[ii, jj, 2]),
                        Tuple.Create(Madingley.Common.CohortsEnterDirection.SouthEast, (int)inboundCohorts[ii, jj, 3]),
                        Tuple.Create(Madingley.Common.CohortsEnterDirection.South, (int)inboundCohorts[ii, jj, 4]),
                        Tuple.Create(Madingley.Common.CohortsEnterDirection.SouthWest, (int)inboundCohorts[ii, jj, 5]),
                        Tuple.Create(Madingley.Common.CohortsEnterDirection.West, (int)inboundCohorts[ii, jj, 6]),
                        Tuple.Create(Madingley.Common.CohortsEnterDirection.NorthWest, (int)inboundCohorts[ii, jj, 7]),
                    };

                var enter = denter.ToDictionary(l => l.Item1, l => l.Item2);

                var dexit =
                    new Tuple<Madingley.Common.CohortsExitDirection, int>[]
                    {
                        Tuple.Create(Madingley.Common.CohortsExitDirection.North, (int)outboundCohorts[ii, jj, 0]),
                        Tuple.Create(Madingley.Common.CohortsExitDirection.NorthEast, (int)outboundCohorts[ii, jj, 1]),
                        Tuple.Create(Madingley.Common.CohortsExitDirection.East, (int)outboundCohorts[ii, jj, 2]),
                        Tuple.Create(Madingley.Common.CohortsExitDirection.SouthEast, (int)outboundCohorts[ii, jj, 3]),
                        Tuple.Create(Madingley.Common.CohortsExitDirection.South, (int)outboundCohorts[ii, jj, 4]),
                        Tuple.Create(Madingley.Common.CohortsExitDirection.SouthWest, (int)outboundCohorts[ii, jj, 5]),
                        Tuple.Create(Madingley.Common.CohortsExitDirection.West, (int)outboundCohorts[ii, jj, 6]),
                        Tuple.Create(Madingley.Common.CohortsExitDirection.NorthWest, (int)outboundCohorts[ii, jj, 7]),
                    };

                var exit = dexit.ToDictionary(l => l.Item1, l => l.Item2);

                var weights = outboundCohortWeights[ii, jj].ToArray();

                var cell = madingleyModelGrid.GetGridCell((uint)ii, (uint)jj);

                var ccell = Converters.ConvertCellData(cell);

                copy[kk] = new Madingley.Common.GridCellDispersal(enter, exit, weights, ccell);
            }

            this.GridCellDispersals = copy;
        }
        /// <summary>
        /// Run responsive dispersal
        /// </summary>
        /// <param name="cellIndices">The longitudinal and latitudinal indices of the current grid cell</param>
        /// <param name="gridForDispersal">The model grid to run dispersal for</param>
        /// <param name="cohortToDisperse">The cohort for which to apply the dispersal process</param>
        /// <param name="actingCohortFunctionalGroup">The functional group index of the acting cohort</param>
        /// <param name="actingCohortNumber">The position of the acting cohort within the functional group in the array of grid cell cohorts</param>
        /// <param name="currentMonth">The current model month</param>
        public void RunDispersal(uint[] cellIndices, ModelGrid gridForDispersal, Cohort cohortToDisperse, 
            int actingCohortFunctionalGroup, int actingCohortNumber, uint currentMonth)
        {
            // Starvation driven dispersal takes precedence over density driven dispersal (i.e. a cohort can't do both). Also, the delta
            // arrays only allow each cohort to perform one type of dispersal each time step
            bool CohortDispersed = false;

            // Check for starvation-driven dispersal
            CohortDispersed = CheckStarvationDispersal(gridForDispersal, cellIndices[0], cellIndices[1], cohortToDisperse, actingCohortFunctionalGroup, actingCohortNumber);

            if (!CohortDispersed)
            {
                // Check for density driven dispersal
                CheckDensityDrivenDispersal(gridForDispersal, cellIndices[0], cellIndices[1], cohortToDisperse, actingCohortFunctionalGroup, actingCohortNumber);
            }
        }
        /// <summary>
        /// Run dispersal
        /// </summary>
        public void RunCrossGridCellEcologicalProcess(uint[] cellIndex, ModelGrid gridForDispersal, bool dispersalOnly, FunctionalGroupDefinitions madingleyCohortDefinitions, FunctionalGroupDefinitions madingleyStockDefinitions, uint currentMonth)
        {

            // Create a temporary handler for grid cell cohorts
            GridCellCohortHandler WorkingGridCellCohorts;

            // Get the lat and lon indices
            uint ii = cellIndex[0];
            uint jj = cellIndex[1];

            // A boolean to check that the environmental layer exists
            bool varExists;

            // Check to see if the cell is marine
            double CellRealm = gridForDispersal.GetEnviroLayer("Realm", 0, ii, jj, out varExists);

            // Go through all of the cohorts in turn and see if they disperse
            WorkingGridCellCohorts = gridForDispersal.GetGridCellCohorts(ii, jj);

            // Loop through cohorts, and perform dispersal according to cohort type and status
            for (int kk = 0; kk < WorkingGridCellCohorts.Count; kk++)
            {
                // Work through the list of cohorts
                for (int ll = 0; ll < WorkingGridCellCohorts[kk].Count; ll++)
                {
                    // Check to see if the cell is marine and the cohort type is planktonic
                    if (CellRealm == 2.0 &&
                        ((madingleyCohortDefinitions.GetTraitNames("Mobility", WorkingGridCellCohorts[kk][ll].FunctionalGroupIndex) == "planktonic") || (WorkingGridCellCohorts[kk][ll].IndividualBodyMass <= PlanktonThreshold)))
                    {
                        // Run advective dispersal
                        Implementations["basic advective dispersal"].RunDispersal(cellIndex, gridForDispersal, WorkingGridCellCohorts[kk][ll], kk, ll, currentMonth);

                    }
                    // Otherwise, if mature do responsive dispersal
                    else if (WorkingGridCellCohorts[kk][ll].MaturityTimeStep < uint.MaxValue)
                    {
                        // Run diffusive dispersal 
                        Implementations["basic responsive dispersal"].RunDispersal(cellIndex, gridForDispersal, WorkingGridCellCohorts[kk][ll], kk, ll, currentMonth);
                    }
                    // If the cohort is immature, run diffusive dispersal
                    else
                    {
                        Implementations["basic diffusive dispersal"].RunDispersal(cellIndex, gridForDispersal, WorkingGridCellCohorts[kk][ll], kk, ll, currentMonth);
                    }
                }
            }
        }
        /// <summary>
        /// Initializes the ecosystem model
        /// </summary>
        /// <param name="mmi">An instance of the model initialisation class</param> 
        public static void Load(Tuple<Madingley.Common.Environment, SortedList<string, EnviroData>> mmi, bool readData)
        {
            var e = mmi.Item1;

            if (e.SpecificLocations == false)
            {
                var CellList = new List<Tuple<int, int>>();

                var NumLatCells = (uint)((e.TopLatitude - e.BottomLatitude) / e.CellSize);
                var NumLonCells = (uint)((e.RightmostLongitude - e.LeftmostLongitude) / e.CellSize);

                // Loop over all cells in the model
                for (int latitudeIndex = 0; latitudeIndex < NumLatCells; latitudeIndex++)
                {
                    for (int longitudeIndex = 0; longitudeIndex < NumLonCells; longitudeIndex++)
                    {
                        // Add the vector to the list of all active grid cells
                        CellList.Add(Tuple.Create(latitudeIndex, longitudeIndex));
                    }
                }

                e.FocusCells = CellList;
            }

            if (readData)
            {
                var cellList = e.FocusCells.Select(a => new UInt32[] { (uint)a.Item1, (uint)a.Item2 }).ToList();

                var EcosystemModelGrid = new ModelGrid((float)e.BottomLatitude, (float)e.LeftmostLongitude, (float)e.TopLatitude, (float)e.RightmostLongitude,
                    (float)e.CellSize, (float)e.CellSize, cellList, mmi.Item2,
                    e.SpecificLocations);

                Func<Tuple<int, int>, IDictionary<string, double[]>> convertCellEnvironment =
                    cell =>
                    {
                        var env = EcosystemModelGrid.GetCellEnvironment((uint)cell.Item1, (uint)cell.Item2);

                        return env.ToDictionary(kv => kv.Key, kv => kv.Value.ToArray());
                    };

                e.CellEnvironment = e.FocusCells.Select(convertCellEnvironment).ToList();
            }
        }
        /// <summary>
        /// Calculates the mean trophic level of all individual organisms in a grid cell
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid</param>
        /// <param name="cellIndices">The list of cell indices in the current model simulation</param>
        /// <param name="cellIndex">The index of the current cell in the list of cells to run</param>
        /// <returns>The mean trophic level of individuals in the grid cell</returns>
        public double CalculateMeanTrophicLevelCell(ModelGrid ecosystemModelGrid, List <uint[]> cellIndices, int cellIndex)
        {
            //Get the cohorts for the specified cell
            GridCellCohortHandler CellCohorts = ecosystemModelGrid.GetGridCellCohorts(cellIndices[cellIndex][0], cellIndices[cellIndex][1]);
            double BiomassWeightedTI          = 0.0;
            double TotalBiomass  = 0.0;
            double CohortBiomass = 0.0;

            foreach (var CohortList in CellCohorts)
            {
                foreach (Cohort c in CohortList)
                {
                    CohortBiomass      = (c.IndividualBodyMass + c.IndividualReproductivePotentialMass) * c.CohortAbundance;
                    BiomassWeightedTI += CohortBiomass * c.TrophicIndex;
                    TotalBiomass      += CohortBiomass;
                }
            }

            return(BiomassWeightedTI / TotalBiomass);
        }
        /// <summary>
        /// Return the distribution of biomasses among trophic level bins
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid</param>
        /// <param name="cellIndices">The list of cell indices to be run in the current model simulation</param>
        /// <param name="cellIndex">The index of the current cell in the list of cells to be run</param>
        /// <returns>The distribution of biomasses among trophic level bins</returns>
        public double[] CalculateTrophicDistribution(ModelGrid ecosystemModelGrid, List <uint[]> cellIndices, int cellIndex)
        {
            //Get the cohorts for the specified cell
            GridCellCohortHandler CellCohorts = ecosystemModelGrid.GetGridCellCohorts(cellIndices[cellIndex][0], cellIndices[cellIndex][1]);

            double[] TrophicIndexBinMasses = new double[NumberTrophicBins];
            int      BinIndex;


            foreach (var CohortList in CellCohorts)
            {
                foreach (Cohort c in CohortList)
                {
                    BinIndex = _TrophicIndexBinValues.ToList().IndexOf(_TrophicIndexBinValues.Last(x => x < c.TrophicIndex));
                    TrophicIndexBinMasses[BinIndex] += (c.IndividualBodyMass + c.IndividualReproductivePotentialMass) * c.CohortAbundance;
                }
            }

            return(TrophicIndexBinMasses);
        }
        /// <summary>
        /// Run diffusive dispersal
        /// </summary>
        /// <param name="cellIndices">List of indices of active cells in the model grid</param>
        /// <param name="gridForDispersal">The model grid to run dispersal for</param>
        /// <param name="cohortToDisperse">The cohort for which to run the dispersal process for</param>
        /// <param name="actingCohortFunctionalGroup">The functional group index of the acting cohort</param>
        /// <param name="actingCohortNumber">The position of the cohort within the functional group in the array of grid cell cohorts</param>
        /// <param name="currentMonth">The current model month</param>
        public void RunDispersal(uint[] cellIndices, ModelGrid gridForDispersal, Cohort cohortToDisperse,
            int actingCohortFunctionalGroup, int actingCohortNumber, uint currentMonth)
        {
            // Calculate dispersal speed for the cohort         
            double DispersalSpeed = CalculateDispersalSpeed(cohortToDisperse.IndividualBodyMass);

            // A double to indicate whether or not the cohort has dispersed, and if it has dispersed, where to
            double CohortDispersed = 0;

            // Temporary variables to keep track of directions in which cohorts enter/exit cells during the multiple advection steps per time step
            uint ExitDirection = new uint();
            uint EntryDirection = new uint();
            ExitDirection = 9999;

            // Get the probability of dispersal
            double[] DispersalArray = CalculateDispersalProbability(gridForDispersal, cellIndices[0], cellIndices[1], DispersalSpeed);

            // Check to see if it does disperse
            CohortDispersed = CheckForDispersal(DispersalArray[0]);

            // If it does, check to see where it will end up
            if (CohortDispersed > 0)
            {
                // Check to see if the direction is actually dispersable
                uint[] DestinationCell = CellToDisperseTo(gridForDispersal, cellIndices[0], cellIndices[1], DispersalArray, CohortDispersed, DispersalArray[4], DispersalArray[5], ref ExitDirection, ref EntryDirection);

                if (DestinationCell[0] < 999999)
                {
                    // Update the delta array of cohorts
                    gridForDispersal.DeltaFunctionalGroupDispersalArray[cellIndices[0], cellIndices[1]].Add((uint)actingCohortFunctionalGroup);
                    gridForDispersal.DeltaCohortNumberDispersalArray[cellIndices[0], cellIndices[1]].Add((uint)actingCohortNumber);

                    // Update the delta array of cells to disperse to
                    gridForDispersal.DeltaCellToDisperseToArray[cellIndices[0], cellIndices[1]].Add(DestinationCell);

                    // Update the delta array of exit and entry directions
                    gridForDispersal.DeltaCellExitDirection[cellIndices[0], cellIndices[1]].Add(ExitDirection);
                    gridForDispersal.DeltaCellEntryDirection[cellIndices[0], cellIndices[1]].Add(EntryDirection);
                }
            }
        }
        /// <summary>
        /// Calculates the geometric community weighted mean body mass
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid</param>
        /// <param name="cellIndices">The list of indices of cells to be run in the current model simulation</param>
        /// <param name="cellIndex">The index of the current cell within the list of cells to be run</param>
        /// <returns>geometric community weighted mean body mass</returns>
        public double CalculateGeometricCommunityMeanBodyMass(ModelGrid ecosystemModelGrid, List <uint[]> cellIndices, int cellIndex)
        {
            //Get the cohorts for the specified cell
            GridCellCohortHandler CellCohorts = ecosystemModelGrid.GetGridCellCohorts(cellIndices[cellIndex][0], cellIndices[cellIndex][1]);
            double CumulativeAbundance        = 0.0;
            double CumulativeLogBiomass       = 0.0;

            //Retrieve the biomass
            foreach (var CohortList in CellCohorts)
            {
                foreach (Cohort c in CohortList)
                {
                    CumulativeLogBiomass += Math.Log(c.IndividualBodyMass + c.IndividualReproductivePotentialMass) * c.CohortAbundance;
                    CumulativeAbundance  += c.CohortAbundance;
                }
            }

            double CWGMBM = Math.Exp(CumulativeLogBiomass / CumulativeAbundance);

            return(CWGMBM);
        }
Beispiel #9
0
        /// <summary>
        /// Initializes the ecosystem model
        /// </summary>
        /// <param name="mmi">An instance of the model initialisation class</param>
        public static void Load(Tuple <Madingley.Common.Environment, SortedList <string, EnviroData> > mmi)
        {
            var e = mmi.Item1;

            if (e.SpecificLocations == false)
            {
                var CellList = new List <Tuple <int, int> >();

                var NumLatCells = (uint)((e.TopLatitude - e.BottomLatitude) / e.CellSize);
                var NumLonCells = (uint)((e.RightmostLongitude - e.LeftmostLongitude) / e.CellSize);

                // Loop over all cells in the model
                for (int latitudeIndex = 0; latitudeIndex < NumLatCells; latitudeIndex++)
                {
                    for (int longitudeIndex = 0; longitudeIndex < NumLonCells; longitudeIndex++)
                    {
                        // Add the vector to the list of all active grid cells
                        CellList.Add(Tuple.Create(latitudeIndex, longitudeIndex));
                    }
                }

                e.FocusCells = CellList;
            }

            var cellList = e.FocusCells.Select(a => new UInt32[] { (uint)a.Item1, (uint)a.Item2 }).ToList();

            var EcosystemModelGrid = new ModelGrid((float)e.BottomLatitude, (float)e.LeftmostLongitude, (float)e.TopLatitude, (float)e.RightmostLongitude,
                                                   (float)e.CellSize, (float)e.CellSize, cellList, mmi.Item2,
                                                   e.SpecificLocations);

            Func <Tuple <int, int>, IDictionary <string, double[]> > convertCellEnvironment =
                cell =>
            {
                var env = EcosystemModelGrid.GetCellEnvironment((uint)cell.Item1, (uint)cell.Item2);

                return(env.ToDictionary(kv => kv.Key, kv => kv.Value.ToArray()));
            };

            e.CellEnvironment = e.FocusCells.Select(convertCellEnvironment).ToList();
        }
Beispiel #10
0
        /// <summary>
        /// Calculates the probability of diffusive dispersal given average individual dispersal speed
        /// </summary>
        /// <param name="madingleyGrid">The model grid</param>
        /// <param name="latIndex">The latitude index of the grid cell to check for dispersal</param>
        /// <param name="lonIndex">The longitude index of the grid cell to check for dispersal</param>
        /// <param name="dispersalSpeed">The average speed at which individuals in this cohort move around their environment, in km per month</param>
        /// <returns>A six element array.
        /// The first element is the probability of dispersal.
        /// The second element is the probability of dispersing in the u (longitudinal) direction
        /// The third element is the probability of dispersing in the v (latitudinal) direction
        /// The fourth element is the probability of dispersing in the diagonal direction
        /// The fifth element is the u velocity modified by the random diffusion component
        /// The sixth element is the v velocity modified by the random diffusion component
        /// Note that the second, third, and fourth elements are always positive; thus, they do not indicate 'direction' in terms of dispersal.</returns>
        private double[] CalculateDispersalProbability(ModelGrid madingleyGrid, uint latIndex, uint lonIndex, double dispersalSpeed)
        {
            // Check that the u speed and v speed are not greater than the cell length. If they are, then rescale them; this limits the max velocity
            // so that cohorts cannot be advected more than one grid cell per time step
            double LatCellLength = madingleyGrid.CellHeightsKm[latIndex];
            double LonCellLength = madingleyGrid.CellWidthsKm[latIndex];

            // Pick a direction at random
            double RandomDirection = RandomNumberGenerator.GetUniform() * 2 * Math.PI;

            // Calculate the u and v components given the dispersal speed
            double uSpeed = dispersalSpeed * Math.Cos(RandomDirection);
            double vSpeed = dispersalSpeed * Math.Sin(RandomDirection);

            // Calculate the area of the grid cell that is now outside in the diagonal direction
            double AreaOutsideBoth = Math.Abs(uSpeed * vSpeed);

            // Calculate the area of the grid cell that is now outside in the u direction (not including the diagonal)
            double AreaOutsideU = Math.Abs(uSpeed * LatCellLength) - AreaOutsideBoth;

            // Calculate the proportion of the grid cell that is outside in the v direction (not including the diagonal
            double AreaOutsideV = Math.Abs(vSpeed * LonCellLength) - AreaOutsideBoth;

            // Get the cell area, in kilometres squared
            double CellArea = madingleyGrid.GetCellEnvironment(latIndex, lonIndex)["Cell Area"][0];

            // Convert areas to a probability
            double DispersalProbability = (AreaOutsideU + AreaOutsideV + AreaOutsideBoth) / CellArea;

            // Check that the whole cell hasn't moved out. This could happen if dispersal speed was high enough
            if (DispersalProbability >= 1)
            {
                Debug.Fail("Dispersal probability in diffusion should always be <= 1");
            }

            double[] NewArray = { DispersalProbability, AreaOutsideU / CellArea, AreaOutsideV / CellArea, AreaOutsideBoth / CellArea, uSpeed, vSpeed };

            return(NewArray);
        }
Beispiel #11
0
        /// <summary>
        /// Generates the file outputs for the current time step
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid</param>
        /// <param name="currentTimeStep">The current model time step</param>
        private void TimeStepFileOutputs(ModelGrid ecosystemModelGrid, uint currentTimeStep)
        {
            Console.WriteLine("Writing global ouputs to file...\n");

            // Write out the basic diagnostic variables
            DataConverter.ValueToSDS1D(OrganicPoolOut, "Organic matter pool", "Time step",
                                       ecosystemModelGrid.GlobalMissingValue, BasicOutput, (int)currentTimeStep + 1);
            DataConverter.ValueToSDS1D(RespiratoryPoolOut, "Respiratory CO2 pool", "Time step",
                                       ecosystemModelGrid.GlobalMissingValue, BasicOutput, (int)currentTimeStep + 1);
            DataConverter.ValueToSDS1D(TotalNumberOfCohorts, "Number of cohorts in model", "Time step",
                                       ecosystemModelGrid.GlobalMissingValue, BasicOutput, (int)currentTimeStep + 1);
            DataConverter.ValueToSDS1D(TotalNumberOfStocks, "Number of stocks in model", "Time step",
                                       ecosystemModelGrid.GlobalMissingValue, BasicOutput, (int)currentTimeStep + 1);
            DataConverter.ValueToSDS1D(NumberOfCohortsExtinct, "Number of cohorts extinct", "Time step",
                                       ecosystemModelGrid.GlobalMissingValue, BasicOutput, (int)currentTimeStep + 1);
            DataConverter.ValueToSDS1D(NumberOfCohortsProduced, "Number of cohorts produced", "Time step",
                                       ecosystemModelGrid.GlobalMissingValue, BasicOutput, (int)currentTimeStep + 1);
            DataConverter.ValueToSDS1D(NumberOfCohortsCombined, "Number of cohorts combined", "Time step",
                                       ecosystemModelGrid.GlobalMissingValue, BasicOutput, (int)currentTimeStep + 1);
            DataConverter.ValueToSDS1D(TotalLivingBiomass, "Total living biomass", "Time step",
                                       ecosystemModelGrid.GlobalMissingValue, BasicOutput, (int)currentTimeStep + 1);
        }
        /// <summary>
        /// Calculates the abundances and biomasses within mass bins for all functional groups in the cohort indices array
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid</param>
        /// <param name="cellIndices">List of indices of active cells in the model grid</param>
        /// <param name="cellIndex">The number of the current cell in the list of indices of active cells</param>
        /// <param name="marineCell">Whether the current cell is a marine cell</param>
        private void CalculateMassBinOutputs(ModelGrid ecosystemModelGrid, List<uint[]> cellIndices, int cellIndex, Boolean marineCell)
        {
            string[] Keys;
            if (marineCell)
            {
                // Get the cohort trait combinations to consider
                Keys = CohortTraitIndicesMarine.Keys.ToArray();
            }
            else
            {
                // Get the cohort trait combinations to consider
                Keys = CohortTraitIndices.Keys.ToArray();
            }
            // Loop over trait combinations
            foreach (var TraitValue in Keys)
            {
                // Declare vectors to hold abundance and biomass in mass bins for this trait combination
                double[] WorkingAbundanceInMassBins = new double[MassBinNumber];
                double[] WorkingBiomassInMassBins = new double[MassBinNumber];

                // Declare arrays to hold abundance and biomass in juvenile vs. adult mass bins for this trait combination
                double[,] WorkingAbundanceJuvenileAdultMassBins = new double[MassBinNumber, MassBinNumber];
                double[,] WorkingBiomassJuvenileAdultMassBins = new double[MassBinNumber, MassBinNumber];

                // Create a temporary local copy of the cohorts in this grid cell
                GridCellCohortHandler TempCohorts = ecosystemModelGrid.GetGridCellCohorts(cellIndices[cellIndex][0],cellIndices[cellIndex][1]);

                if (marineCell)
                {
                    // Loop over functional  groups
                    foreach (int FunctionalGroupIndex in CohortTraitIndicesMarine[TraitValue])
                    {
                        // Loop over all cohorts in this functional  group
                        for (int cohort = 0; cohort < TempCohorts[FunctionalGroupIndex].Count; cohort++)
                        {
                            // Find the appropriate mass bin for the cohort
                            int mb = 0;
                            do
                            {
                                mb++;
                            } while (mb < (MassBins.Length - 1) && TempCohorts[FunctionalGroupIndex][cohort].IndividualBodyMass > MassBins[mb]);

                            // Add the cohort's abundance to the approriate mass bin. Note that we have to differentiate here for non-obligate zooplankton, because they are only added in if they are
                            // below the zooplankton mass threshold (or an obligate zooplankton)
                            if (String.Equals(TraitValue, "Zooplankton (all)"))
                            {
                                if (TempCohorts[FunctionalGroupIndex][cohort].IndividualBodyMass < PlanktonSizeThreshold || CohortTraitIndicesMarine["Obligate zooplankton"].Contains(FunctionalGroupIndex))
                                {
                                    WorkingAbundanceInMassBins[mb - 1] += TempCohorts[FunctionalGroupIndex][cohort].CohortAbundance;

                                    // Add the cohort's biomass to the approriate mass bin
                                    WorkingBiomassInMassBins[mb - 1] += TempCohorts[FunctionalGroupIndex][cohort].CohortAbundance * TempCohorts[FunctionalGroupIndex][cohort].IndividualBodyMass;

                                    // Find the mass bin appropriate for this cohort's juvenile mass
                                    int j = 0;
                                    do
                                    {
                                        j++;
                                    } while (j < (MassBins.Length - 1) && TempCohorts[FunctionalGroupIndex][cohort].JuvenileMass > MassBins[j]);
                                    // Find the mass bin appropriate for this cohort's adult mass
                                    int a = 0;
                                    do
                                    {
                                        a++;
                                    } while (a < (MassBins.Length - 1) && TempCohorts[FunctionalGroupIndex][cohort].AdultMass > MassBins[a]);

                                   // Add the cohort's abundance to this adult vs juvenile mass bins
                                    WorkingAbundanceJuvenileAdultMassBins[a - 1, j - 1] += TempCohorts[FunctionalGroupIndex][cohort].CohortAbundance;
                                    // Add the cohort's biomass to this adult vs juvenile mass bins
                                    WorkingBiomassJuvenileAdultMassBins[a - 1, j - 1] += TempCohorts[FunctionalGroupIndex][cohort].CohortAbundance * TempCohorts[FunctionalGroupIndex][cohort].IndividualBodyMass;
                                }
                            }
                            else
                            {
                                WorkingAbundanceInMassBins[mb - 1] += TempCohorts[FunctionalGroupIndex][cohort].CohortAbundance;

                                // Add the cohort's biomass to the approriate mass bin
                                WorkingBiomassInMassBins[mb - 1] += TempCohorts[FunctionalGroupIndex][cohort].CohortAbundance * TempCohorts[FunctionalGroupIndex][cohort].IndividualBodyMass;

                                // Find the mass bin appropriate for this cohort's juvenile mass
                                int j = 0;
                                do
                                {
                                    j++;
                                } while (j < (MassBins.Length - 1) && TempCohorts[FunctionalGroupIndex][cohort].JuvenileMass > MassBins[j]);
                                // Find the mass bin appropriate for this cohort's adult mass
                                int a = 0;
                                do
                                {
                                    a++;
                                } while (a < (MassBins.Length - 1) && TempCohorts[FunctionalGroupIndex][cohort].AdultMass > MassBins[a]);

                                // Add the cohort's abundance to this adult vs juvenile mass bins
                                WorkingAbundanceJuvenileAdultMassBins[a - 1, j - 1] += TempCohorts[FunctionalGroupIndex][cohort].CohortAbundance;
                                // Add the cohort's biomass to this adult vs juvenile mass bins
                                WorkingBiomassJuvenileAdultMassBins[a - 1, j - 1] += TempCohorts[FunctionalGroupIndex][cohort].CohortAbundance * TempCohorts[FunctionalGroupIndex][cohort].IndividualBodyMass;
                            }
                        }
                    }

                    //Copy the working vectors and arrays to the sortedlist for the current key value
                    AbundancesInMassBins[TraitValue] = WorkingAbundanceInMassBins;
                    BiomassesInMassBins[TraitValue] = WorkingBiomassInMassBins;
                    AbundancesInJuvenileAdultMassBins[TraitValue] = WorkingAbundanceJuvenileAdultMassBins;
                    BiomassesInJuvenileAdultMassBins[TraitValue] = WorkingBiomassJuvenileAdultMassBins;
                }
                else
                {
                    // Loop over functional  groups
                    foreach (int FunctionalGroupIndex in CohortTraitIndices[TraitValue])
                    {
                        // Loop over all cohorts in this functional  group
                        for (int cohort = 0; cohort < TempCohorts[FunctionalGroupIndex].Count; cohort++)
                        {
                            // Find the appropriate mass bin for the cohort
                            int mb = 0;
                            do
                            {
                                mb++;
                            } while (mb < (MassBins.Length - 1) && TempCohorts[FunctionalGroupIndex][cohort].IndividualBodyMass > MassBins[mb]);

                            // Add the cohort's abundance to the approriate mass bin
                            WorkingAbundanceInMassBins[mb - 1] += TempCohorts[FunctionalGroupIndex][cohort].CohortAbundance;
                            // Add the cohort's biomass to the approriate mass bin
                            WorkingBiomassInMassBins[mb - 1] += TempCohorts[FunctionalGroupIndex][cohort].CohortAbundance * TempCohorts[FunctionalGroupIndex][cohort].IndividualBodyMass;

                            // Find the mass bin appropriate for this cohort's juvenile mass
                            int j = 0;
                            do
                            {
                                j++;
                            } while (j < (MassBins.Length - 1) && TempCohorts[FunctionalGroupIndex][cohort].JuvenileMass > MassBins[j]);
                            // Find the mass bin appropriate for this cohort's adult mass
                            int a = 0;
                            do
                            {
                                a++;
                            } while (a < (MassBins.Length - 1) && TempCohorts[FunctionalGroupIndex][cohort].AdultMass > MassBins[a]);

                            // Add the cohort's abundance to this adult vs juvenile mass bins
                            WorkingAbundanceJuvenileAdultMassBins[a - 1, j - 1] += TempCohorts[FunctionalGroupIndex][cohort].CohortAbundance;
                            // Add the cohort's biomass to this adult vs juvenile mass bins
                            WorkingBiomassJuvenileAdultMassBins[a - 1, j - 1] += TempCohorts[FunctionalGroupIndex][cohort].CohortAbundance * TempCohorts[FunctionalGroupIndex][cohort].IndividualBodyMass;

                        }
                    }

                    //Copy the working vectors and arrays to the sortedlist for the current key value
                    AbundancesInMassBins[TraitValue] = WorkingAbundanceInMassBins;
                    BiomassesInMassBins[TraitValue] = WorkingBiomassInMassBins;
                    AbundancesInJuvenileAdultMassBins[TraitValue] = WorkingAbundanceJuvenileAdultMassBins;
                    BiomassesInJuvenileAdultMassBins[TraitValue] = WorkingBiomassJuvenileAdultMassBins;
                }
            }

            // Loop over trait combinations and log abundances and body masses
            foreach (var TraitValue in Keys)
            {
                for (int i = 0; i < MassBins.Length; i++)
                {
                    AbundancesInMassBins[TraitValue][i] = (AbundancesInMassBins[TraitValue][i] > 0) ? Math.Log(AbundancesInMassBins[TraitValue][i]) : ecosystemModelGrid.GlobalMissingValue;
                    BiomassesInMassBins[TraitValue][i] = (BiomassesInMassBins[TraitValue][i] > 0) ? Math.Log(BiomassesInMassBins[TraitValue][i]) : ecosystemModelGrid.GlobalMissingValue;
                    for (int j = 0; j < MassBins.Length; j++)
                    {
                        AbundancesInJuvenileAdultMassBins[TraitValue][i, j] = (AbundancesInJuvenileAdultMassBins[TraitValue][i, j] > 0) ? Math.Log(AbundancesInJuvenileAdultMassBins[TraitValue][i, j]) : ecosystemModelGrid.GlobalMissingValue;
                        BiomassesInJuvenileAdultMassBins[TraitValue][i, j] = (BiomassesInJuvenileAdultMassBins[TraitValue][i, j] > 0) ? Math.Log(BiomassesInJuvenileAdultMassBins[TraitValue][i, j]) : ecosystemModelGrid.GlobalMissingValue;
                    }
                }

            }
        }
        /// <summary>
        /// Calculate outputs associated with high-level outputs
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid</param>
        /// <param name="cellIndices">List of indices of active cells in the model grid</param>
        /// <param name="cellIndex">The number of the current cell in the list of active cells</param>
        /// <param name="marineCell">Whether the current cell is a marine cell</param>
        private void CalculateHighLevelOutputs(ModelGrid ecosystemModelGrid, List<uint[]> cellIndices, int cellIndex, Boolean marineCell)
        {
            // Calcalate the outputs arranged by mass bin
            CalculateMassBinOutputs(ecosystemModelGrid, cellIndices, cellIndex, marineCell);

            // Declare vectors to hold properties of tracked cohorts
            TrackedCohortIndividualMasses = new double[TrackedCohorts.Count];
            TrackedCohortAbundances = new double[TrackedCohorts.Count];

            // Get a temporary local copy of the cohorts in the grid cell
            GridCellCohortHandler TempCohorts = ecosystemModelGrid.GetGridCellCohorts(cellIndices[cellIndex][0], cellIndices[cellIndex][1]);

            // Loop over functional groups in the grid cell cohorts
            for (int j = 0; j < TempCohorts.Count; j++)
            {
                // Loop over cohorts within this functional group
                for (int k = 0; k < TempCohorts[j].Count; k++)
                {
                    // Loop over all cohort IDs
                    foreach (uint CohortID in TempCohorts[j][k].CohortID)
                    {
                        // Check whether the cohort ID corresponds to a tracked cohort
                        if (TrackedCohorts.Contains(CohortID))
                        {
                            // Get the position of the cohort in the list of tracked cohorts
                            int position = TrackedCohorts.FindIndex(
                                delegate(uint i)
                                {
                                    return i == CohortID;
                                });
                            // Add the body mass and abundance of the tracked cohort to the appropriate position in the output vector
                            TrackedCohortIndividualMasses[position] = TempCohorts[j][k].IndividualBodyMass;
                            TrackedCohortAbundances[position] = TempCohorts[j][k].CohortAbundance;
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Set up all outputs (live, console and file) prior to the model run
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid that output data will be derived from</param>
        /// <param name="cohortFunctionalGroupDefinitions">The definitions for cohort functional groups</param>
        /// <param name="stockFunctionalGroupDefinitions">The definitions for stock functional groups</param>
        /// <param name="numTimeSteps">The number of time steps in the model run</param>
        /// <param name="outputFilesSuffix">The suffix to be applied to all output files from the current model run</param>
        /// <param name="cellIndices">List of indices of active cells in the model grid</param>
        /// <param name="cellIndex">The number of the current grid cell in the list of indices of active cells</param>
        /// <param name="marineCell">Whether the current cell is a marine cell</param>
        public void SetUpOutputs(ModelGrid ecosystemModelGrid, FunctionalGroupDefinitions cohortFunctionalGroupDefinitions,
            FunctionalGroupDefinitions stockFunctionalGroupDefinitions, uint numTimeSteps, string outputFilesSuffix, List<uint[]> cellIndices,
            int cellIndex, Boolean marineCell)
        {
            Console.WriteLine("Setting up grid cell outputs...\n");

            // Set the suffix for all output files
            _OutputSuffix = outputFilesSuffix + "_Cell" + cellIndex;

            // Create vector to hold the values of the time dimension
            TimeSteps = new float[numTimeSteps + 1];

            // Set the first value to be -1 (this will hold initial outputs)
            TimeSteps[0] = 0;

            // Fill other values from 0 (this will hold outputs during the model run)
            for (int i = 1; i < numTimeSteps + 1; i++)
            {
                TimeSteps[i] = i;
            }

            // Initialise the trait based outputs
            InitialiseTraitBasedOutputs(cohortFunctionalGroupDefinitions, stockFunctionalGroupDefinitions, marineCell);

            // Setup low-level outputs
            SetUpLowLevelOutputs(numTimeSteps, ecosystemModelGrid);

            if ((ModelOutputDetail == OutputDetailLevel.Medium) || (ModelOutputDetail == OutputDetailLevel.High))
            {
                // Setup medium-level outputs
                SetupMediumLevelOutputs(ecosystemModelGrid, marineCell);

                if (ModelOutputDetail == OutputDetailLevel.High)
                {
                    // Setup high-level outputs
                    SetUpHighLevelOutputs(ecosystemModelGrid, cellIndices,cellIndex, cohortFunctionalGroupDefinitions, marineCell);
                }
            }
        }
        /// <summary>
        /// Write to the output file values of the output variables at the end of the model run
        /// </summary>
        /// <param name="EcosystemModelGrid">The model grid to get data from</param>
        /// <param name="CohortFunctionalGroupDefinitions">Definitions of the cohort functional groups in the model</param>
        /// <param name="StockFunctionalGroupDefinitions">Definitions of the stock functional groups in the model</param>
        /// <param name="cellIndices">List of indices of active cells in the model grid</param>
        /// <param name="cellNumber">The number of the current cell in the list of indices of active cells</param>
        /// <param name="GlobalDiagnosticVariables">List of global diagnostic variables</param>
        /// <param name="initialisation">The Madingley Model initialisation</param>
        /// <param name="month">The current month in the model run</param>
        /// <param name="marineCell">Whether the current cell is a marine cell</param>
        public void FinalOutputs(ModelGrid EcosystemModelGrid, FunctionalGroupDefinitions CohortFunctionalGroupDefinitions, 
            FunctionalGroupDefinitions StockFunctionalGroupDefinitions, List<uint[]> cellIndices, int cellNumber, 
            SortedList<string, double> GlobalDiagnosticVariables, MadingleyModelInitialisation initialisation, uint month, Boolean marineCell)
        {
            // Calculate output variables
            CalculateOutputs(EcosystemModelGrid, CohortFunctionalGroupDefinitions, StockFunctionalGroupDefinitions, cellIndices,cellNumber, GlobalDiagnosticVariables, initialisation, month, marineCell);

            // Dispose of the dataset objects
            BasicOutputMemory.Clone("msds:nc?file="+ _OutputPath + "BasicOutputs" + _OutputSuffix + ".nc&openMode=create");
            BasicOutputMemory.Dispose();

            if (LiveOutputs)
            {
                DataSetToViewLive.Dispose();
            }

            if (ModelOutputDetail == OutputDetailLevel.High)
            {
                // Dispose of the dataset objects for high detail level outputs
                MassBinsOutputMemory.Clone("msds:nc?file="+ _OutputPath + "MassBinsOutputs" + _OutputSuffix + ".nc&openMode=create");
                MassBinsOutputMemory.Dispose();

                TrackedCohortsOutputMemory.Clone("msds:nc?file="+ _OutputPath + "TrackedCohortsOutputs" + _OutputSuffix + ".nc&openMode=create");
                TrackedCohortsOutputMemory.Dispose();

            }

            Metrics.CloseRserve();
        }
        /// <summary>
        /// Generate file outputs for the current time step
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid</param>
        /// <param name="cohortFunctionalGroupDefinitions">The functional group definitions for cohorts in the model</param>
        /// <param name="currentTimeStep">The current time step</param>
        /// <param name="MarineCell">Whether the current cell is a marine cell</param>
        /// <param name="cellIndices">The list of all cells to run the model for</param>
        /// <param name="cellIndex">The index of the current cell in the list of all cells to run the model for</param>
        private void TimeStepFileOutputs(ModelGrid ecosystemModelGrid, FunctionalGroupDefinitions cohortFunctionalGroupDefinitions, 
            uint currentTimeStep, Boolean MarineCell, List<uint[]> cellIndices, int cellIndex)
        {
            Console.WriteLine("Writing grid cell ouputs to file...\n");
            //Write the low level outputs first
            DataConverter.ValueToSDS1D(TotalLivingBiomassDensity, "Total Biomass density", "Time step",
                            ecosystemModelGrid.GlobalMissingValue,
                            BasicOutputMemory, (int)currentTimeStep + 1);
            DataConverter.ValueToSDS1D(TotalHeterotrophAbundanceDensity, "Heterotroph Abundance density", "Time step",
                            ecosystemModelGrid.GlobalMissingValue,
                            BasicOutputMemory, (int)currentTimeStep + 1);
            DataConverter.ValueToSDS1D(TotalHeterotrophBiomassDensity, "Heterotroph Biomass density", "Time step",
                            ecosystemModelGrid.GlobalMissingValue,
                            BasicOutputMemory, (int)currentTimeStep + 1);
            // File outputs for medium and high detail levels
            if ((ModelOutputDetail == OutputDetailLevel.Medium) || (ModelOutputDetail == OutputDetailLevel.High))
            {
                if (MarineCell)
                {
                    // Loop over all cohort trait value combinations and output abundances, densities and biomasses
                    foreach (string TraitValue in CohortTraitIndicesMarine.Keys)
                    {
                        DataConverter.ValueToSDS1D(TotalDensitiesOut[TraitValue], TraitValue + " density", "Time step", ecosystemModelGrid.GlobalMissingValue, BasicOutputMemory, (int)currentTimeStep + 1);
                        DataConverter.ValueToSDS1D(TotalBiomassDensitiesOut[TraitValue], TraitValue + " biomass density", "Time step", ecosystemModelGrid.GlobalMissingValue, BasicOutputMemory, (int)currentTimeStep + 1);
                    }

                    // Loop over all stock trait value combinations and output biomasses
                    foreach (string TraitValue in StockTraitIndicesMarine.Keys)
                    {
                        DataConverter.ValueToSDS1D(TotalBiomassDensitiesOut[TraitValue], TraitValue + " biomass density", "Time step",
                            ecosystemModelGrid.GlobalMissingValue, BasicOutputMemory, (int)currentTimeStep + 1);
                    }
                }
                else
                {
                    // Loop over all cohort trait value combinations and output abudnances, densities and biomasses
                    foreach (string TraitValue in CohortTraitIndices.Keys)
                    {
                        DataConverter.ValueToSDS1D(TotalDensitiesOut[TraitValue], TraitValue + " density", "Time step", ecosystemModelGrid.GlobalMissingValue, BasicOutputMemory, (int)currentTimeStep + 1);
                        DataConverter.ValueToSDS1D(TotalBiomassDensitiesOut[TraitValue], TraitValue + " biomass density", "Time step", ecosystemModelGrid.GlobalMissingValue, BasicOutputMemory, (int)currentTimeStep + 1);
                    }

                    // Loop over all stock trait value combinations and output biomasses
                    foreach (string TraitValue in StockTraitIndices.Keys)
                    {
                        DataConverter.ValueToSDS1D(TotalBiomassDensitiesOut[TraitValue], TraitValue + " biomass density", "Time step",
                            ecosystemModelGrid.GlobalMissingValue, BasicOutputMemory, (int)currentTimeStep + 1);
                    }
                }

                // If ouputting ecosystem metrics has been specified then add these metrics to the output
                if (OutputMetrics)
                {
                    DataConverter.ValueToSDS1D(Metrics.CalculateMeanTrophicLevelCell(ecosystemModelGrid, cellIndices, cellIndex),
                                                "Mean Trophic Level", "Time step", ecosystemModelGrid.GlobalMissingValue,
                                                BasicOutputMemory, (int)currentTimeStep + 1);
                    DataConverter.ValueToSDS1D(Metrics.CalculateFunctionalEvennessRao(ecosystemModelGrid,cohortFunctionalGroupDefinitions, cellIndices, cellIndex,"trophic index"),
                                                "Trophic Evenness", "Time step", ecosystemModelGrid.GlobalMissingValue,
                                                BasicOutputMemory, (int)currentTimeStep + 1);
                    DataConverter.ValueToSDS1D(Metrics.CalculateFunctionalEvennessRao(ecosystemModelGrid, cohortFunctionalGroupDefinitions, cellIndices, cellIndex, "biomass"),
                                                "Biomass Evenness", "Time step", ecosystemModelGrid.GlobalMissingValue,
                                                BasicOutputMemory, (int)currentTimeStep + 1);
                    double[] FunctionalDiversity = Metrics.CalculateFunctionalDiversity(ecosystemModelGrid, cohortFunctionalGroupDefinitions,
                        cellIndices, cellIndex);
                    DataConverter.ValueToSDS1D(FunctionalDiversity[0],
                                                 "Functional Richness", "Time step", ecosystemModelGrid.GlobalMissingValue,
                                                 BasicOutputMemory, (int)currentTimeStep + 1);
                    DataConverter.ValueToSDS1D(FunctionalDiversity[1],
                                                "Rao Functional Evenness", "Time step", ecosystemModelGrid.GlobalMissingValue,
                                                BasicOutputMemory, (int)currentTimeStep + 1);

                    DataConverter.ValueToSDS1D(Metrics.CalculateFunctionalRichness(ecosystemModelGrid, cohortFunctionalGroupDefinitions, cellIndices, cellIndex, "Biomass")[0],
                                                "Biomass Richness", "Time step", ecosystemModelGrid.GlobalMissingValue,
                                                BasicOutputMemory, (int)currentTimeStep + 1);
                    DataConverter.ValueToSDS1D(Metrics.CalculateFunctionalRichness(ecosystemModelGrid, cohortFunctionalGroupDefinitions, cellIndices, cellIndex, "Biomass")[1],
                                                "Min Bodymass", "Time step", ecosystemModelGrid.GlobalMissingValue,
                                                BasicOutputMemory, (int)currentTimeStep + 1);
                    DataConverter.ValueToSDS1D(Metrics.CalculateFunctionalRichness(ecosystemModelGrid, cohortFunctionalGroupDefinitions, cellIndices, cellIndex, "Biomass")[2],
                                                "Max Bodymass", "Time step", ecosystemModelGrid.GlobalMissingValue,
                                                BasicOutputMemory, (int)currentTimeStep + 1);

                    DataConverter.ValueToSDS1D(Metrics.CalculateFunctionalRichness(ecosystemModelGrid, cohortFunctionalGroupDefinitions, cellIndices, cellIndex, "Trophic index")[0],
                                                "Trophic Richness", "Time step", ecosystemModelGrid.GlobalMissingValue,
                                                BasicOutputMemory, (int)currentTimeStep + 1);
                    DataConverter.ValueToSDS1D(Metrics.CalculateFunctionalRichness(ecosystemModelGrid, cohortFunctionalGroupDefinitions, cellIndices, cellIndex, "Trophic index")[1],
                                                "Min Trophic Index", "Time step", ecosystemModelGrid.GlobalMissingValue,
                                                BasicOutputMemory, (int)currentTimeStep + 1);
                    DataConverter.ValueToSDS1D(Metrics.CalculateFunctionalRichness(ecosystemModelGrid, cohortFunctionalGroupDefinitions, cellIndices, cellIndex, "Trophic index")[2],
                                                "Max Trophic Index", "Time step", ecosystemModelGrid.GlobalMissingValue,
                                                BasicOutputMemory, (int)currentTimeStep + 1);

                    DataConverter.ValueToSDS1D(Metrics.CalculateArithmeticCommunityMeanBodyMass(ecosystemModelGrid, cellIndices, cellIndex),
                                                "Arithmetic Mean Bodymass", "Time step", ecosystemModelGrid.GlobalMissingValue,
                                                BasicOutputMemory, (int)currentTimeStep + 1);
                    DataConverter.ValueToSDS1D(Metrics.CalculateGeometricCommunityMeanBodyMass(ecosystemModelGrid, cellIndices, cellIndex),
                                                "Geometric Mean Bodymass", "Time step", ecosystemModelGrid.GlobalMissingValue,
                                                BasicOutputMemory, (int)currentTimeStep + 1);
                }

                if (TrackMarineSpecifics && MarineCell)
                {
                    DataConverter.ValueToSDS1D(TotalIncomingNPP, "Incoming NPP", "Time step", ecosystemModelGrid.GlobalMissingValue,
                        BasicOutputMemory, (int)currentTimeStep + 1);
                }

                if (currentTimeStep % 600 == 0 && currentTimeStep > 0)
                {
                    BasicOutputMemory.Clone("msds:nc?file=" + _OutputPath + "BasicOutputs" + _OutputSuffix + ".nc&openMode=create");
                    Console.WriteLine("Cloning grid cell ouputs to file...\n");
                }

                // File outputs for high detail level
                if (ModelOutputDetail == OutputDetailLevel.High)
                {

                    if (OutputMetrics)
                    {
                        DataConverter.VectorToSDS2D(Metrics.CalculateTrophicDistribution(ecosystemModelGrid,cellIndices,cellIndex), "Trophic Index Distribution",
                            new string[2] { "Time step", "Trophic Index Bins" }, TimeSteps, Metrics.TrophicIndexBinValues, ecosystemModelGrid.GlobalMissingValue, MassBinsOutputMemory, (int)currentTimeStep + 1);
                    }

                    // Loop over trait combinations in the mass bin outputs
                    foreach (var TraitValue in AbundancesInMassBins.Keys)
                    {
                        // Write out abundances in each of the mass bins to the relevant two-dimensional output variables
                        DataConverter.VectorToSDS2D(AbundancesInMassBins[TraitValue], "Log " + TraitValue + " abundance in mass bins",
                            new string[2] { "Time step", "Mass bin" }, TimeSteps, MassBins, ecosystemModelGrid.GlobalMissingValue, MassBinsOutputMemory, (int)currentTimeStep + 1);
                        DataConverter.VectorToSDS2D(BiomassesInMassBins[TraitValue], "Log " + TraitValue + " biomass in mass bins",
                            new string[2] { "Time step", "Mass bin" }, TimeSteps, MassBins, ecosystemModelGrid.GlobalMissingValue, MassBinsOutputMemory, (int)currentTimeStep + 1);
                        // Write out abundances in combinations of juvenile and adult mass bins to the relevant three-dimensional output variable
                        DataConverter.Array2DToSDS3D(AbundancesInJuvenileAdultMassBins[TraitValue], "Log " + TraitValue + " abundance in juvenile vs adult bins",
                            new string[] { "Adult Mass bin", "Juvenile Mass bin", "Time step" },
                            (int)currentTimeStep + 1,
                            ecosystemModelGrid.GlobalMissingValue,
                            MassBinsOutputMemory);
                        // Write out biomasses in combinations of juvenile and adult mass bins to the relevant three-dimensional output variable
                        DataConverter.Array2DToSDS3D(BiomassesInJuvenileAdultMassBins[TraitValue], "Log " + TraitValue + " biomass in juvenile vs adult bins",
                            new string[] { "Adult Mass bin", "Juvenile Mass bin", "Time step" },
                            (int)currentTimeStep + 1,
                            ecosystemModelGrid.GlobalMissingValue,
                            MassBinsOutputMemory);
                    }

                    // Loop over tracked cohorts
                    for (int i = 0; i < TrackedCohorts.Count; i++)
                    {
                        // Add the individual body mass and abundance of the tracked cohort to the output dataset
                        string[] TrackedCohortDimensions = new string[] { "Time step", "Cohort ID" };
                        DataConverter.ValueToSDS2D(TrackedCohortIndividualMasses[i], "Individual body mass", TrackedCohortDimensions,
                            ecosystemModelGrid.GlobalMissingValue, TrackedCohortsOutputMemory, (int)currentTimeStep + 1, i);
                        DataConverter.ValueToSDS2D(TrackedCohortAbundances[i], "Number of individuals", TrackedCohortDimensions,
                            ecosystemModelGrid.GlobalMissingValue, TrackedCohortsOutputMemory, (int)currentTimeStep + 1, i);
                    }

                    if (currentTimeStep % 600 ==0 && currentTimeStep > 0)
                    {
                        MassBinsOutputMemory.Clone("msds:nc?file=" + _OutputPath + "MassBinsOutputs" + _OutputSuffix + ".nc&openMode=create");

                        TrackedCohortsOutputMemory.Clone("msds:nc?file=" + _OutputPath + "TrackedCohortsOutputs" + _OutputSuffix + ".nc&openMode=create");

                    }

                }

            }
        }
Beispiel #17
0
        /// <summary>
        /// Run advective dispersal
        /// </summary>
        /// <param name="cellIndex">The longitudinal and latitudinal indices of the focal grid cell</param>
        /// <param name="gridForDispersal">The model grid to run dispersal for</param>
        /// <param name="cohortToDisperse">The cohort to run dispersal for</param>
        /// <param name="actingCohortFunctionalGroup">The functional group index of the acting cohort</param>
        /// <param name="actingCohortNumber">The position of the acting cohort wihtin the functional group in the array of grid cell cohorts</param>
        /// <param name="currentMonth">The current model month</param>
        public void RunDispersal(uint[] cellIndex, ModelGrid gridForDispersal, Cohort cohortToDisperse, int actingCohortFunctionalGroup,
                                 int actingCohortNumber, uint currentMonth)
        {
            // An array to hold the dispersal information
            double[] DispersalArray = new double[6];

            // A double to indicate whether or not the cohort has dispersed, and if it has dispersed, where to
            double CohortDispersed = 0;

            // Temporary variables to keep track of directions in which cohorts enter/exit cells during the multiple advection steps per time step
            uint ExitDirection  = new uint();
            uint EntryDirection = new uint();

            ExitDirection = 9999;

            // An array to hold the present cohort location for the intermediate steps that occur before the final dispersal this time step
            uint[] PresentLocation = { cellIndex[0], cellIndex[1] };

            // Get the u speed and the v speed from the cell data
            double uAdvectiveSpeed = gridForDispersal.GetEnviroLayer("uVel", currentMonth, PresentLocation[0], PresentLocation[1],
                                                                     out varExists);

            Debug.Assert(uAdvectiveSpeed != -9999);

            double vAdvectiveSpeed = gridForDispersal.GetEnviroLayer("vVel", currentMonth, PresentLocation[0], PresentLocation[1],
                                                                     out varExists);

            Debug.Assert(vAdvectiveSpeed != -9999);

            uAdvectiveSpeed = RescaleDispersalSpeed(uAdvectiveSpeed);
            vAdvectiveSpeed = RescaleDispersalSpeed(vAdvectiveSpeed);


            // Loop through a number of times proportional to the rescaled dispersal
            for (int mm = 0; mm < _AdvectionTimeStepsPerModelTimeStep; mm++)
            {
                // Get the probability of dispersal
                DispersalArray = CalculateDispersalProbability(gridForDispersal, PresentLocation[0], PresentLocation[1],
                                                               currentMonth, uAdvectiveSpeed, vAdvectiveSpeed);

                // Check to see if it does disperse
                CohortDispersed = CheckForDispersal(DispersalArray[0]);

                // If it does, check to see where it will end up
                if (CohortDispersed > 0)
                {
                    // Check to see if the direction is actually dispersable
                    uint[] DestinationCell = CellToDisperseTo(gridForDispersal, PresentLocation[0], PresentLocation[1],
                                                              DispersalArray, CohortDispersed, DispersalArray[4], DispersalArray[5], ref ExitDirection,
                                                              ref EntryDirection);

                    // If it is, go ahead and update the cohort location
                    if (DestinationCell[0] < 999999)
                    {
                        PresentLocation = DestinationCell;

                        // Get the u speed and the v speed from the cell data
                        uAdvectiveSpeed = gridForDispersal.GetEnviroLayer("uVel", currentMonth, PresentLocation[0],
                                                                          PresentLocation[1], out varExists);
                        vAdvectiveSpeed = gridForDispersal.GetEnviroLayer("vVel", currentMonth, PresentLocation[0],
                                                                          PresentLocation[1], out varExists);
                        uAdvectiveSpeed = RescaleDispersalSpeed(uAdvectiveSpeed);
                        vAdvectiveSpeed = RescaleDispersalSpeed(vAdvectiveSpeed);
                    }
                }
            }


            // Update the dipersal deltas for this cohort, if necessary
            if ((cellIndex[0] != PresentLocation[0]) || (cellIndex[1] != PresentLocation[1]))
            {
                // Update the delta array of cohorts
                gridForDispersal.DeltaFunctionalGroupDispersalArray[cellIndex[0], cellIndex[1]].Add((uint)actingCohortFunctionalGroup);
                gridForDispersal.DeltaCohortNumberDispersalArray[cellIndex[0], cellIndex[1]].Add((uint)actingCohortNumber);

                // Update the delta array of cells to disperse to
                gridForDispersal.DeltaCellToDisperseToArray[cellIndex[0], cellIndex[1]].Add(PresentLocation);

                // Update the delta array of exit and entry directions
                gridForDispersal.DeltaCellExitDirection[cellIndex[0], cellIndex[1]].Add(ExitDirection);
                gridForDispersal.DeltaCellEntryDirection[cellIndex[0], cellIndex[1]].Add(EntryDirection);
            }
        }
        public double[] CalculateFunctionalRichness(ModelGrid ecosystemModelGrid, FunctionalGroupDefinitions cohortDefinitions,
                                                    List <uint[]> cellIndices, int cellIndex, string trait)
        {
            //Get the cohorts for the specified cell
            GridCellCohortHandler CellCohorts = ecosystemModelGrid.GetGridCellCohorts(cellIndices[cellIndex][0], cellIndices[cellIndex][1]);
            double MinCurrentTraitValue       = double.MaxValue;
            double MaxCurrentTraitValue       = double.MinValue;
            double MinModelTraitValue         = 0.0;
            double MaxModelTraitValue         = 0.0;

            switch (trait.ToLower())
            {
            case "biomass":

                foreach (var CohortList in CellCohorts)
                {
                    foreach (var cohort in CohortList)
                    {
                        if (cohort.IndividualBodyMass < MinCurrentTraitValue)
                        {
                            MinCurrentTraitValue = cohort.IndividualBodyMass;
                        }

                        if (cohort.IndividualBodyMass > MaxCurrentTraitValue)
                        {
                            MaxCurrentTraitValue = cohort.IndividualBodyMass;
                        }
                    }
                }


                //Define upper and lower limits for body mass
                MinModelTraitValue = cohortDefinitions.GetBiologicalPropertyAllFunctionalGroups("minimum mass").Min();
                MaxModelTraitValue = cohortDefinitions.GetBiologicalPropertyAllFunctionalGroups("maximum mass").Max();
                break;

            case "trophic index":
                foreach (var CohortList in CellCohorts)
                {
                    foreach (var cohort in CohortList)
                    {
                        if (cohort.TrophicIndex < MinCurrentTraitValue)
                        {
                            MinCurrentTraitValue = cohort.TrophicIndex;
                        }

                        if (cohort.TrophicIndex > MaxCurrentTraitValue)
                        {
                            MaxCurrentTraitValue = cohort.TrophicIndex;
                        }
                    }
                }


                //Define upper and lower limits for body mass
                MinModelTraitValue = MinTI;
                MaxModelTraitValue = MaxTI;

                break;

            default:
                Debug.Fail("Trait not recognised in calculation of ecosystem metrics: " + trait);
                break;
            }

            Debug.Assert((MaxModelTraitValue - MinModelTraitValue) > 0.0, "Division by zero or negative model trait values in calculation of functional richness");

            double[] NewArray = { (MaxCurrentTraitValue - MinCurrentTraitValue) / (MaxModelTraitValue - MinModelTraitValue), MinCurrentTraitValue, MaxCurrentTraitValue };

            return(NewArray);
        }
        // Determine to which cell the cohort disperses
        /// <summary>
        /// Determines the cell to which a cohort disperses
        /// </summary>
        /// <param name="madingleyGrid">The ecosystem model grid</param>
        /// <param name="latIndex">The latitudinal index of the cell being run</param>
        /// <param name="lonIndex">The longitudinal index of the cell being run</param>
        /// <param name="dispersalArray"></param>
        /// <param name="RandomValue"></param>
        /// <param name="uSpeedIncDiffusion"></param>
        /// <param name="vSpeedIncDiffusion"></param>
        /// <param name="exitDirection"></param>
        /// <param name="entryDirection"></param>
        /// <returns></returns>
        protected uint[] CellToDisperseTo(ModelGrid madingleyGrid, uint latIndex, uint lonIndex, double[] dispersalArray,
                                          double RandomValue, double uSpeedIncDiffusion, double vSpeedIncDiffusion, ref uint exitDirection,
                                          ref uint entryDirection)
        {
            uint[] DestinationCell;

            // Check to see in which axis the cohort disperses

            // Note that the values in the dispersal array are the proportional area moved outside the grid cell in each direction; we simply compare the random draw to this
            // to determine the direction in which the cohort moves probabilistically

            // Longitudinally
            if (RandomValue <= dispersalArray[1])
            {
                // Work out whether dispersal is to the cell to the E or the W
                if (uSpeedIncDiffusion > 0)
                {
                    DestinationCell = madingleyGrid.CheckDispersalEast(latIndex, lonIndex);

                    // Record entry and exit directions. Exit direction is only recorded the first time it happens during a (model) timestep, not each advection time step.
                    if (exitDirection == 9999)
                    {
                        exitDirection = 2;
                    }
                    entryDirection = 6;
                }
                else
                {
                    DestinationCell = madingleyGrid.CheckDispersalWest(latIndex, lonIndex);

                    // Record entry and exit directions. Exit direction is only recorded the first time it happens during a (model) timestep, not each advection time step.
                    if (exitDirection == 9999)
                    {
                        exitDirection = 6;
                    }
                    entryDirection = 2;
                }
            }
            else
            {
                // Latitudinally
                if (RandomValue <= (dispersalArray[1] + dispersalArray[2]))
                {
                    // Work out whether dispersal is to the cell to the N or the S
                    if (vSpeedIncDiffusion > 0)
                    {
                        DestinationCell = madingleyGrid.CheckDispersalNorth(latIndex, lonIndex);

                        // Record entry and exit directions. Exit direction is only recorded the first time it happens during a (model) timestep, not each advection time step.
                        if (exitDirection == 9999)
                        {
                            exitDirection = 0;
                        }
                        entryDirection = 4;
                    }
                    else
                    {
                        DestinationCell = madingleyGrid.CheckDispersalSouth(latIndex, lonIndex);

                        // Record entry and exit directions. Exit direction is only recorded the first time it happens during a (model) timestep, not each advection time step.
                        if (exitDirection == 9999)
                        {
                            exitDirection = 4;
                        }
                        entryDirection = 0;
                    }
                }
                else
                {
                    // Diagonally. Note that DispersalArray[0] is equal to dispersalArray[1] + dispersalArray[2] + dispersalArray[3], but it
                    // is both faster to compare and also avoids any rounding errors.
                    if (RandomValue <= (dispersalArray[0]))
                    {
                        // Work out to which cell dispersal occurs
                        if (uSpeedIncDiffusion > 0)
                        {
                            if (vSpeedIncDiffusion > 0)
                            {
                                DestinationCell = madingleyGrid.CheckDispersalNorthEast(latIndex, lonIndex);

                                // Record entry and exit directions. Exit direction is only recorded the first time it happens during a (model) timestep, not each advection time step.
                                if (exitDirection == 9999)
                                {
                                    exitDirection = 1;
                                }
                                entryDirection = 5;
                            }
                            else
                            {
                                DestinationCell = madingleyGrid.CheckDispersalSouthEast(latIndex, lonIndex);

                                // Record entry and exit directions. Exit direction is only recorded the first time it happens during a (model) timestep, not each advection time step.
                                if (exitDirection == 9999)
                                {
                                    exitDirection = 5;
                                }
                                entryDirection = 1;
                            }
                        }
                        else
                        {
                            if (vSpeedIncDiffusion > 0)
                            {
                                DestinationCell = madingleyGrid.CheckDispersalNorthWest(latIndex, lonIndex);

                                // Record entry and exit directions. Exit direction is only recorded the first time it happens during a (model) timestep, not each advection time step.
                                if (exitDirection == 9999)
                                {
                                    exitDirection = 7;
                                }
                                entryDirection = 3;
                            }
                            else
                            {
                                DestinationCell = madingleyGrid.CheckDispersalSouthWest(latIndex, lonIndex);

                                // Record entry and exit directions. Exit direction is only recorded the first time it happens during a (model) timestep, not each advection time step.
                                if (exitDirection == 9999)
                                {
                                    exitDirection = 3;
                                }
                                entryDirection = 7;
                            }
                        }
                    }
                    else
                    {
                        // This should never happen. Means that the random number indicates dispersal by being lower than the probability, but
                        // that in the comparison abive, it is higher than the probability.
                        Debug.Fail("Error when determining which cell to disperse to");
                        Console.WriteLine("Error when determining which cell to disperse to");
                        Console.ReadKey();
                        DestinationCell = new uint[2] {
                            9999999, 9999999
                        };
                    }
                }
            }
            return(DestinationCell);
        }
Beispiel #20
0
        public void TimeStepOutputs(ModelGrid ecosystemModelGrid, FunctionalGroupDefinitions cohortFunctionalGroupDefinitions, FunctionalGroupDefinitions
                                    stockFunctionalGroupDefinitions, List <uint[]> cellIndices, uint currentTimeStep, MadingleyModelInitialisation initialisation)
        {
            // Calculate the output variables for this time step
            CalculateOutputs(ecosystemModelGrid, cohortFunctionalGroupDefinitions, stockFunctionalGroupDefinitions, cellIndices, initialisation, currentTimeStep);

            // Write the total biomass of cohorts to the live display
            if (LiveOutputs)
            {
                DataConverter.Array2DToSDS2D(LogBiomassDensityGridCohorts, "Log(Biomass density, g/km^2)", ecosystemModelGrid.Lats,
                                             ecosystemModelGrid.Lons, 0, DataSetToViewLive);
            }

            Console.WriteLine("Writing grid ouputs to file...\n");

            // Add the grid of total biomass in cells to the file dataset
            DataConverter.Array2DToSDS3D(LogBiomassDensityGridCohorts, "Biomass density", new string[] { "Latitude", "Longitude", "Time step" },
                                         (int)currentTimeStep + 1, 0, GridOutput);

            // Add the grid of total abudance in cells to the file dataset
            DataConverter.Array2DToSDS3D(LogAbundanceDensityGridCohorts, "Abundance density", new string[] { "Latitude", "Longitude", "Time step" },
                                         (int)currentTimeStep + 1, 0, GridOutput);

            // Temporary outputs to check plant model
            DataConverter.Array2DToSDS3D(Realm, "Realm", new string[] { "Latitude", "Longitude", "Time step" },
                                         (int)currentTimeStep + 1, ecosystemModelGrid.GlobalMissingValue, GridOutput);
            DataConverter.Array2DToSDS3D(FrostDays, "Fraction year frost", new string[] { "Latitude", "Longitude", "Time step" },
                                         (int)currentTimeStep + 1, ecosystemModelGrid.GlobalMissingValue, GridOutput);
            DataConverter.Array2DToSDS3D(FracEvergreen, "Fraction evergreen", new string[] { "Latitude", "Longitude", "Time step" },
                                         (int)currentTimeStep + 1, ecosystemModelGrid.GlobalMissingValue, GridOutput);
            DataConverter.Array2DToSDS3D(HANPP, "HANPP", new string[] { "Latitude", "Longitude", "Time step" },
                                         (int)currentTimeStep + 1, ecosystemModelGrid.GlobalMissingValue, GridOutput);
            DataConverter.Array2DToSDS3D(Temperature, "Temperature", new string[] { "Latitude", "Longitude", "Time step" },
                                         (int)currentTimeStep + 1, ecosystemModelGrid.GlobalMissingValue, GridOutput);



            if ((ModelOutputDetail == OutputDetailLevel.Medium) || (ModelOutputDetail == OutputDetailLevel.High))
            {
                foreach (var TraitValue in BiomassDensityGrid.Keys)
                {
                    // Add the biomass grids for individual trait combinations to the file dataset
                    DataConverter.Array2DToSDS3D(BiomassDensityGrid[TraitValue], TraitValue + "biomass density", new string[]
                                                 { "Latitude", "Longitude", "Time step" }, (int)currentTimeStep + 1, ecosystemModelGrid.GlobalMissingValue, GridOutput);
                }

                foreach (var Key in AbundanceDensityGrid.Keys)
                {
                    DataConverter.Array2DToSDS3D(AbundanceDensityGrid[Key], Key + "abundance density",
                                                 new string[] { "Latitude", "Longitude", "Time step" },
                                                 (int)currentTimeStep + 1,
                                                 ecosystemModelGrid.GlobalMissingValue,
                                                 GridOutput);
                }
            }

            // Output ecosystem metrics
            if (OutputMetrics)
            {
                foreach (string Key in MetricsGrid.Keys)
                {
                    DataConverter.Array2DToSDS3D(MetricsGrid[Key], Key,
                                                 new string[] { "Latitude", "Longitude", "Time step" },
                                                 (int)currentTimeStep + 1,
                                                 ecosystemModelGrid.GlobalMissingValue,
                                                 GridOutput);
                }
            }
        }
        private bool CheckStarvationDispersal(ModelGrid gridForDispersal, uint latIndex, uint lonIndex, Cohort cohortToDisperse, int functionalGroup, int cohortNumber)
        {
            // A boolean to check whether a cohort has dispersed
            bool CohortHasDispersed = false;

            // Check for starvation driven dispersal
            // What is the present body mass of the cohort?
            // Note that at present we are just tracking starvation for adults
            double IndividualBodyMass = cohortToDisperse.IndividualBodyMass;
            double AdultMass          = cohortToDisperse.AdultMass;

            // Temporary variables to keep track of directions in which cohorts enter/exit cells during the multiple advection steps per time step
            uint ExitDirection  = new uint();
            uint EntryDirection = new uint();

            ExitDirection = 9999;

            // Assume a linear relationship between probability of dispersal and body mass loss, up to _StarvationDispersalBodyMassThreshold
            // at which point the cohort will try to disperse every time step
            if (IndividualBodyMass < AdultMass)
            {
                double ProportionalPresentMass = IndividualBodyMass / AdultMass;

                // If the body mass loss is greater than the starvation dispersal body mass threshold, then the cohort tries to disperse
                if (ProportionalPresentMass < _StarvationDispersalBodyMassThreshold)
                {
                    // Cohort tries to disperse
                    double[] DispersalArray  = CalculateDispersalProbability(gridForDispersal, latIndex, lonIndex, CalculateDispersalSpeed(AdultMass));
                    double   CohortDispersed = CheckForDispersal(DispersalArray[0]);
                    if (CohortDispersed > 0)
                    {
                        uint[] DestinationCell = CellToDisperseTo(gridForDispersal, latIndex, lonIndex, DispersalArray, DispersalArray[0], DispersalArray[4], DispersalArray[5], ref ExitDirection, ref EntryDirection);

                        // Update the delta array of cells to disperse to, if the cohort moves
                        if (DestinationCell[0] < 999999)
                        {
                            // Update the delta array of cohorts
                            gridForDispersal.DeltaFunctionalGroupDispersalArray[latIndex, lonIndex].Add((uint)functionalGroup);
                            gridForDispersal.DeltaCohortNumberDispersalArray[latIndex, lonIndex].Add((uint)cohortNumber);

                            // Update the delta array of cells to disperse to
                            gridForDispersal.DeltaCellToDisperseToArray[latIndex, lonIndex].Add(DestinationCell);

                            // Update the delta array of exit and entry directions
                            gridForDispersal.DeltaCellExitDirection[latIndex, lonIndex].Add(ExitDirection);
                            gridForDispersal.DeltaCellEntryDirection[latIndex, lonIndex].Add(EntryDirection);
                        }
                    }

                    // Note that regardless of whether or not it succeeds, if a cohort tries to disperse, it is counted as having dispersed for
                    // the purposes of not then allowing it to disperse based on its density.
                    CohortHasDispersed = true;
                }

                // Otherwise, the cohort has a chance of trying to disperse proportional to its mass loss
                else
                {
                    // Cohort tries to disperse with a particular probability
                    // Draw a random number
                    if (((1.0 - ProportionalPresentMass) / (1.0 - _StarvationDispersalBodyMassThreshold)) > RandomNumberGenerator.GetUniform())
                    {
                        // Cohort tries to disperse
                        double[] DispersalArray  = CalculateDispersalProbability(gridForDispersal, latIndex, lonIndex, CalculateDispersalSpeed(AdultMass));
                        double   CohortDispersed = CheckForDispersal(DispersalArray[0]);
                        if (CohortDispersed > 0)
                        {
                            uint[] DestinationCell = CellToDisperseTo(gridForDispersal, latIndex, lonIndex, DispersalArray, DispersalArray[0], DispersalArray[4], DispersalArray[5], ref ExitDirection, ref EntryDirection);

                            // Update the delta array of cells to disperse to, if the cohort moves
                            if (DestinationCell[0] < 999999)
                            {
                                // Update the delta array of cohorts
                                gridForDispersal.DeltaFunctionalGroupDispersalArray[latIndex, lonIndex].Add((uint)functionalGroup);
                                gridForDispersal.DeltaCohortNumberDispersalArray[latIndex, lonIndex].Add((uint)cohortNumber);

                                // Update the delta array of cells to disperse to
                                gridForDispersal.DeltaCellToDisperseToArray[latIndex, lonIndex].Add(DestinationCell);

                                // Update the delta array of exit and entry directions
                                gridForDispersal.DeltaCellExitDirection[latIndex, lonIndex].Add(ExitDirection);
                                gridForDispersal.DeltaCellEntryDirection[latIndex, lonIndex].Add(EntryDirection);
                            }
                        }

                        CohortHasDispersed = true;
                    }
                }
            }
            return(CohortHasDispersed);
        }
        public InputModelState(string outputPath, string filename, ModelGrid ecosystemModelGrid, List <uint[]> cellList)
        {
            //Set the input state flag to be true
            _InputState = true;

            // Construct the string required to access the file using Scientific Dataset
            string _ReadFileString = "msds:nc?file=input/ModelStates/" + filename + ".nc&openMode=readOnly";

            // Open the data file using Scientific Dataset
            DataSet StateDataSet = DataSet.Open(_ReadFileString);


            float[] Latitude              = StateDataSet.GetData <float[]>("Latitude");
            float[] Longitude             = StateDataSet.GetData <float[]>("Longitude");
            float[] CohortFunctionalGroup = StateDataSet.GetData <float[]>("Cohort Functional Group");
            float[] Cohort = StateDataSet.GetData <float[]>("Cohort");

            float[] StockFunctionalGroup = StateDataSet.GetData <float[]>("Stock Functional Group");
            float[] Stock = StateDataSet.GetData <float[]>("Stock");


            // Check that the longitudes and latitudes in the input state match the cell environment
            for (int la = 0; la < Latitude.Length; la++)
            {
                Debug.Assert(ecosystemModelGrid.GetCellEnvironment((uint)la, 0)["Latitude"][0] == Latitude[la],
                             "Error: input-state grid doesn't match current model grid");
            }
            for (int lo = 0; lo < Longitude.Length; lo++)
            {
                Debug.Assert(ecosystemModelGrid.GetCellEnvironment(0, (uint)lo)["Longitude"][0] == Longitude[lo],
                             "Error: input-state grid doesn't match current model grid");
            }

            List <double[, , ]> CohortJuvenileMass                = new List <double[, , ]>();
            List <double[, , ]> CohortAdultMass                   = new List <double[, , ]>();
            List <double[, , ]> CohortIndividualBodyMass          = new List <double[, , ]>();
            List <double[, , ]> CohortCohortAbundance             = new List <double[, , ]>();
            List <double[, , ]> CohortLogOptimalPreyBodySizeRatio = new List <double[, , ]>();
            List <double[, , ]> CohortBirthTimeStep               = new List <double[, , ]>();
            List <double[, , ]> CohortProportionTimeActive        = new List <double[, , ]>();
            List <double[, , ]> CohortTrophicIndex                = new List <double[, , ]>();

            double[,,,] tempData = new double[Latitude.Length, Longitude.Length,
                                              CohortFunctionalGroup.Length, Cohort.Length];

            tempData = StateDataSet.GetData <double[, , , ]>("CohortJuvenileMass");


            for (int la = 0; la < Latitude.Length; la++)
            {
                CohortJuvenileMass.Add(new double[Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length]);
            }

            foreach (uint[] cell in cellList)
            {
                for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++)
                {
                    for (int c = 0; c < Cohort.Length; c++)
                    {
                        CohortJuvenileMass[(int)cell[0]][cell[1], fg, c] = tempData[
                            cell[0], cell[1], fg, c];
                    }
                }
            }


            tempData = StateDataSet.GetData <double[, , , ]>("CohortAdultMass");

            for (int la = 0; la < Latitude.Length; la++)
            {
                CohortAdultMass.Add(new double[Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length]);
            }

            foreach (uint[] cell in cellList)
            {
                for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++)
                {
                    for (int c = 0; c < Cohort.Length; c++)
                    {
                        CohortAdultMass[(int)cell[0]][cell[1], fg, c] = tempData[
                            cell[0], cell[1], fg, c];
                    }
                }
            }

            tempData = StateDataSet.GetData <double[, , , ]>("CohortIndividualBodyMass");

            for (int la = 0; la < Latitude.Length; la++)
            {
                CohortIndividualBodyMass.Add(new double[Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length]);
            }

            foreach (uint[] cell in cellList)
            {
                for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++)
                {
                    for (int c = 0; c < Cohort.Length; c++)
                    {
                        CohortIndividualBodyMass[(int)cell[0]][cell[1], fg, c] = tempData[
                            cell[0], cell[1], fg, c];
                    }
                }
            }

            tempData = StateDataSet.GetData <double[, , , ]>("CohortCohortAbundance");

            for (int la = 0; la < Latitude.Length; la++)
            {
                CohortCohortAbundance.Add(new double[Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length]);
            }

            foreach (uint[] cell in cellList)
            {
                for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++)
                {
                    for (int c = 0; c < Cohort.Length; c++)
                    {
                        CohortCohortAbundance[(int)cell[0]][cell[1], fg, c] = tempData[
                            cell[0], cell[1], fg, c];
                    }
                }
            }

            tempData = StateDataSet.GetData <double[, , , ]>("CohortLogOptimalPreyBodySizeRatio");

            for (int la = 0; la < Latitude.Length; la++)
            {
                CohortLogOptimalPreyBodySizeRatio.Add(new double[Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length]);
            }

            foreach (uint[] cell in cellList)
            {
                for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++)
                {
                    for (int c = 0; c < Cohort.Length; c++)
                    {
                        CohortLogOptimalPreyBodySizeRatio[(int)cell[0]][cell[1], fg, c] = tempData[
                            cell[0], cell[1], fg, c];
                    }
                }
            }

            tempData = StateDataSet.GetData <double[, , , ]>("CohortBirthTimeStep");

            for (int la = 0; la < Latitude.Length; la++)
            {
                CohortBirthTimeStep.Add(new double[Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length]);
            }

            foreach (uint[] cell in cellList)
            {
                for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++)
                {
                    for (int c = 0; c < Cohort.Length; c++)
                    {
                        CohortBirthTimeStep[(int)cell[0]][cell[1], fg, c] = tempData[
                            cell[0], cell[1], fg, c];
                    }
                }
            }

            tempData = StateDataSet.GetData <double[, , , ]>("CohortProportionTimeActive");

            for (int la = 0; la < Latitude.Length; la++)
            {
                CohortProportionTimeActive.Add(new double[Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length]);
            }

            foreach (uint[] cell in cellList)
            {
                for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++)
                {
                    for (int c = 0; c < Cohort.Length; c++)
                    {
                        CohortProportionTimeActive[(int)cell[0]][cell[1], fg, c] = tempData[
                            cell[0], cell[1], fg, c];
                    }
                }
            }

            tempData = StateDataSet.GetData <double[, , , ]>("CohortTrophicIndex");

            for (int la = 0; la < Latitude.Length; la++)
            {
                CohortTrophicIndex.Add(new double[Longitude.Length, CohortFunctionalGroup.Length, Cohort.Length]);
            }

            foreach (uint[] cell in cellList)
            {
                for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++)
                {
                    for (int c = 0; c < Cohort.Length; c++)
                    {
                        CohortTrophicIndex[(int)cell[0]][cell[1], fg, c] = tempData[
                            cell[0], cell[1], fg, c];
                    }
                }
            }

            _GridCellCohorts = new GridCellCohortHandler[Latitude.Length, Longitude.Length];

            long temp = 0;

            for (int cell = 0; cell < cellList.Count; cell++)
            {
                _GridCellCohorts[cellList[cell][0], cellList[cell][1]] = new GridCellCohortHandler(CohortFunctionalGroup.Length);

                for (int fg = 0; fg < CohortFunctionalGroup.Length; fg++)
                {
                    _GridCellCohorts[cellList[cell][0], cellList[cell][1]][fg] = new List <Cohort>();
                    for (int c = 0; c < Cohort.Length; c++)
                    {
                        if (CohortCohortAbundance[(int)cellList[cell][0]][cellList[cell][1], fg, c] > 0.0)
                        {
                            Cohort TempCohort = new Cohort(
                                (byte)fg,
                                CohortJuvenileMass[(int)cellList[cell][0]][cellList[cell][1], fg, c],
                                CohortAdultMass[(int)cellList[cell][0]][cellList[cell][1], fg, c],
                                CohortIndividualBodyMass[(int)cellList[cell][0]][cellList[cell][1], fg, c],
                                CohortCohortAbundance[(int)cellList[cell][0]][cellList[cell][1], fg, c],
                                Math.Exp(CohortLogOptimalPreyBodySizeRatio[(int)cellList[cell][0]][cellList[cell][1], fg, c]),
                                Convert.ToUInt16(CohortBirthTimeStep[(int)cellList[cell][0]][cellList[cell][1], fg, c]),
                                CohortProportionTimeActive[(int)cellList[cell][0]][cellList[cell][1], fg, c], ref temp,
                                CohortTrophicIndex[(int)cellList[cell][0]][cellList[cell][1], fg, c],
                                false);

                            _GridCellCohorts[cellList[cell][0], cellList[cell][1]][fg].Add(TempCohort);
                        }
                    }
                }
            }

            CohortJuvenileMass.RemoveRange(0, CohortJuvenileMass.Count);
            CohortAdultMass.RemoveRange(0, CohortAdultMass.Count);
            CohortIndividualBodyMass.RemoveRange(0, CohortIndividualBodyMass.Count);
            CohortCohortAbundance.RemoveRange(0, CohortCohortAbundance.Count);
            CohortLogOptimalPreyBodySizeRatio.RemoveRange(0, CohortLogOptimalPreyBodySizeRatio.Count);
            CohortBirthTimeStep.RemoveRange(0, CohortBirthTimeStep.Count);
            CohortProportionTimeActive.RemoveRange(0, CohortProportionTimeActive.Count);
            CohortTrophicIndex.RemoveRange(0, CohortTrophicIndex.Count);

            List <double[, , ]> StockIndividualBodyMass = new List <double[, , ]>();
            List <double[, , ]> StockTotalBiomass       = new List <double[, , ]>();

            double[,,,] tempData2 = new double[Latitude.Length, Longitude.Length,
                                               StockFunctionalGroup.Length, Stock.Length];

            tempData2 = StateDataSet.GetData <double[, , , ]>("StockIndividualBodyMass");

            for (int la = 0; la < Latitude.Length; la++)
            {
                StockIndividualBodyMass.Add(new double[Longitude.Length, StockFunctionalGroup.Length, Stock.Length]);
            }

            foreach (uint[] cell in cellList)
            {
                for (int fg = 0; fg < StockFunctionalGroup.Length; fg++)
                {
                    for (int c = 0; c < Stock.Length; c++)
                    {
                        StockIndividualBodyMass[(int)cell[0]][cell[1], fg, c] = tempData2[
                            cell[0], cell[1], fg, c];
                    }
                }
            }

            tempData2 = StateDataSet.GetData <double[, , , ]>("StockTotalBiomass");

            for (int la = 0; la < Latitude.Length; la++)
            {
                StockTotalBiomass.Add(new double[Longitude.Length, StockFunctionalGroup.Length, Stock.Length]);
            }

            foreach (uint[] cell in cellList)
            {
                for (int fg = 0; fg < StockFunctionalGroup.Length; fg++)
                {
                    for (int c = 0; c < Stock.Length; c++)
                    {
                        StockTotalBiomass[(int)cell[0]][cell[1], fg, c] = tempData2[
                            cell[0], cell[1], fg, c];
                    }
                }
            }

            _GridCellStocks = new GridCellStockHandler[Latitude.Length, Longitude.Length];

            for (int cell = 0; cell < cellList.Count; cell++)
            {
                _GridCellStocks[cellList[cell][0], cellList[cell][1]] = new GridCellStockHandler(StockFunctionalGroup.Length);

                for (int fg = 0; fg < StockFunctionalGroup.Length; fg++)
                {
                    _GridCellStocks[cellList[cell][0], cellList[cell][1]][fg] = new List <Stock>();
                    for (int c = 0; c < Stock.Length; c++)
                    {
                        if (StockTotalBiomass[(int)cellList[cell][0]][cellList[cell][1], fg, c] > 0.0)
                        {
                            Stock TempStock = new Stock(
                                (byte)fg,
                                StockIndividualBodyMass[(int)cellList[cell][0]][cellList[cell][1], fg, c],
                                StockTotalBiomass[(int)cellList[cell][0]][cellList[cell][1], fg, c]);

                            _GridCellStocks[cellList[cell][0], cellList[cell][1]][fg].Add(TempStock);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Extract an array of values from a state variable in a model grid and add to a two-dimensional variable in an SDS object
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid to extract data from</param>
        /// <param name="cellIndices">List of indices of active cells in the model grid</param>
        /// <param name="gridVariableName">The name of the state variable in the model grid</param>
        /// <param name="traitValue">The trait value of the functional groups to get data for</param>
        /// <param name="variableType">The type of the state variable: 'stock' or 'cohort'</param>
        /// <param name="outputVariableName">The name of the variable to write to</param>
        /// <param name="SDSObject">The SDS object to write to</param>
        /// <param name="functionalGroupHandler">The functional group handler corresponding to cohorts or stocks</param>
        /// <param name="initialisation">The Madingley Model initialisation</param>
        public void Array2DToSDS2D(ModelGrid ecosystemModelGrid, List <uint[]> cellIndices, string gridVariableName, string traitValue,
                                   string variableType, string outputVariableName, DataSet SDSObject, FunctionalGroupDefinitions functionalGroupHandler,
                                   MadingleyModelInitialisation initialisation)
        {
            // Get the missing value from the model grid
            double MissingValue = ecosystemModelGrid.GlobalMissingValue;

            // create an array to hold the data to output
            double[,] dataToConvert = new double[ecosystemModelGrid.NumLatCells, ecosystemModelGrid.NumLonCells];

            // generate arrays to hold latitudes and longitudes
            float[] lats = new float[ecosystemModelGrid.NumLatCells];
            float[] lons = new float[ecosystemModelGrid.NumLonCells];

            // Populate arrays of latitudes and longitudes, converting from bottom left cell references as used in the model grid
            // to cell centre references as required by SDS
            for (uint ii = 0; ii < ecosystemModelGrid.NumLatCells; ii++)
            {
                lats[ii] = ecosystemModelGrid.Lats[ii] + (ecosystemModelGrid.LatCellSize / 2);
            }

            for (uint jj = 0; jj < ecosystemModelGrid.NumLonCells; jj++)
            {
                lons[jj] = ecosystemModelGrid.Lons[jj] + (ecosystemModelGrid.LonCellSize / 2);
            }

            // Get the required data from the model grid
            dataToConvert = ecosystemModelGrid.GetStateVariableGrid(gridVariableName, traitValue, functionalGroupHandler.AllFunctionalGroupsIndex, cellIndices,
                                                                    variableType, initialisation);

            // If not already contained in the SDS, add the dimension information (geographic coordinates)
            if (SDSObject.Variables.Contains("Latitude"))
            {
            }
            else
            {
                SDSObject.AddVariable <float>("Latitude", lats, "Lat");
            }
            if (SDSObject.Variables.Contains("Longitude"))
            {
            }
            else
            {
                SDSObject.AddVariable <float>("Longitude", lons, "Lon");
            }

            // If the SDS object already contains the output variable, then add the data. Otherwise, define the variable and then add the data
            if (SDSObject.Variables.Contains(outputVariableName))
            {
                SDSObject.PutData <double[, ]>(outputVariableName, dataToConvert);
                // Commit the changes
                SDSObject.Commit();
            }
            else
            {
                // Set up the dimensions and add the gridded data
                string[] dimensions = { "Lat", "Lon" };
                var      DataGrid   = SDSObject.AddVariable <double>(outputVariableName, dataToConvert, dimensions);

                // Add appropriate metadata (including missing values)
                DataGrid.Metadata["DisplayName"]  = outputVariableName;
                DataGrid.Metadata["MissingValue"] = (double)MissingValue;

                // Commit changes to update data set
                SDSObject.Commit();
            }
        }
        /// <summary>
        /// Generates the initial file outputs
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid</param>
        /// <param name="cohortFunctionalGroupDefinitions">The functional group definitions for cohorts in the model</param>
        /// <param name="MarineCell">Whether the current cell is a marine cell</param>
        /// <param name="cellIndices">The list of all cells to run the model for</param>
        /// <param name="cellIndex">The index of the current cell in the list of all cells to run the model for</param>
        private void InitialFileOutputs(ModelGrid ecosystemModelGrid, FunctionalGroupDefinitions cohortFunctionalGroupDefinitions, 
            Boolean MarineCell, List<uint[]> cellIndices, int cellIndex)
        {
            Console.WriteLine("Writing initial grid cell outputs to memory...");

            //Write the low level outputs first
            DataConverter.ValueToSDS1D(TotalLivingBiomassDensity, "Total Biomass density", "Time step",
                            ecosystemModelGrid.GlobalMissingValue,
                            BasicOutputMemory, 0);
            DataConverter.ValueToSDS1D(TotalHeterotrophAbundanceDensity, "Heterotroph Abundance density", "Time step",
                            ecosystemModelGrid.GlobalMissingValue,
                            BasicOutputMemory, 0);
            DataConverter.ValueToSDS1D(TotalHeterotrophBiomassDensity, "Heterotroph Biomass density", "Time step",
                            ecosystemModelGrid.GlobalMissingValue,
                            BasicOutputMemory, 0);

            // File outputs for medium and high detail levels
            if ((ModelOutputDetail == OutputDetailLevel.Medium) || (ModelOutputDetail == OutputDetailLevel.High))
            {

                if (MarineCell)
                {
                    foreach (string TraitValue in CohortTraitIndicesMarine.Keys)
                    {
                        // Write densities, biomasses and abundances in different functional groups to the relevant one-dimensional output variables
                        DataConverter.ValueToSDS1D(TotalDensitiesOut[TraitValue], TraitValue + " density", "Time step",
                            ecosystemModelGrid.GlobalMissingValue,
                            BasicOutputMemory, 0);
                        DataConverter.ValueToSDS1D(TotalBiomassDensitiesOut[TraitValue], TraitValue + " biomass density", "Time step",
                            ecosystemModelGrid.GlobalMissingValue,
                            BasicOutputMemory, 0);
                    }

                    foreach (string TraitValue in StockTraitIndicesMarine.Keys)
                    {
                        DataConverter.ValueToSDS1D(TotalBiomassDensitiesOut[TraitValue], TraitValue + " biomass density", "Time step",
                            ecosystemModelGrid.GlobalMissingValue,
                        BasicOutputMemory, 0);
                    }
                }
                else
                {
                    foreach (string TraitValue in CohortTraitIndices.Keys)
                    {
                        // Write densities, biomasses and abundances in different functional groups to the relevant one-dimensional output variables
                        DataConverter.ValueToSDS1D(TotalDensitiesOut[TraitValue], TraitValue + " density", "Time step",
                            ecosystemModelGrid.GlobalMissingValue,
                            BasicOutputMemory, 0);
                        DataConverter.ValueToSDS1D(TotalBiomassDensitiesOut[TraitValue], TraitValue + " biomass density", "Time step",
                            ecosystemModelGrid.GlobalMissingValue,
                            BasicOutputMemory, 0);
                    }

                    foreach (string TraitValue in StockTraitIndices.Keys)
                    {
                        DataConverter.ValueToSDS1D(TotalBiomassDensitiesOut[TraitValue], TraitValue + " biomass density", "Time step",
                           ecosystemModelGrid.GlobalMissingValue,
                        BasicOutputMemory, 0);
                    }
                }

                if (OutputMetrics)
                {
                    DataConverter.ValueToSDS1D(Metrics.CalculateMeanTrophicLevelCell(ecosystemModelGrid,cellIndices,cellIndex),
                                                "Mean Trophic Level", "Time step", ecosystemModelGrid.GlobalMissingValue,
                                                BasicOutputMemory, 0);
                    DataConverter.ValueToSDS1D(Metrics.CalculateFunctionalEvennessRao(ecosystemModelGrid, cohortFunctionalGroupDefinitions,cellIndices, cellIndex,"trophic index"),
                                                "Trophic Evenness", "Time step", ecosystemModelGrid.GlobalMissingValue,
                                                BasicOutputMemory, 0);
                    DataConverter.ValueToSDS1D(Metrics.CalculateFunctionalEvennessRao(ecosystemModelGrid, cohortFunctionalGroupDefinitions, cellIndices, cellIndex, "biomass"),
                                                "Biomass Evenness", "Time step", ecosystemModelGrid.GlobalMissingValue,
                                                BasicOutputMemory, 0);

                    double[] FunctionalDiversity = Metrics.CalculateFunctionalDiversity(ecosystemModelGrid, cohortFunctionalGroupDefinitions,
                        cellIndices, cellIndex);

                    DataConverter.ValueToSDS1D(FunctionalDiversity[0],
                                                 "Functional Richness", "Time step", ecosystemModelGrid.GlobalMissingValue,
                                                 BasicOutputMemory, 0);
                    DataConverter.ValueToSDS1D(FunctionalDiversity[1],
                                                "Rao Functional Evenness", "Time step", ecosystemModelGrid.GlobalMissingValue,
                                                BasicOutputMemory, 0);

                    DataConverter.ValueToSDS1D(Metrics.CalculateFunctionalRichness(ecosystemModelGrid, cohortFunctionalGroupDefinitions, cellIndices, cellIndex, "Biomass")[0],
                                                "Biomass Richness", "Time step", ecosystemModelGrid.GlobalMissingValue,
                                                BasicOutputMemory, 0);
                    DataConverter.ValueToSDS1D(Metrics.CalculateFunctionalRichness(ecosystemModelGrid, cohortFunctionalGroupDefinitions, cellIndices, cellIndex, "Biomass")[1],
                                                "Min Bodymass", "Time step", ecosystemModelGrid.GlobalMissingValue,
                                                BasicOutputMemory, 0);
                    DataConverter.ValueToSDS1D(Metrics.CalculateFunctionalRichness(ecosystemModelGrid, cohortFunctionalGroupDefinitions, cellIndices, cellIndex, "Biomass")[2],
                                                "Max Bodymass", "Time step", ecosystemModelGrid.GlobalMissingValue,
                                                BasicOutputMemory, 0);

                    DataConverter.ValueToSDS1D(Metrics.CalculateFunctionalRichness(ecosystemModelGrid, cohortFunctionalGroupDefinitions, cellIndices, cellIndex, "Trophic index")[0],
                                                "Trophic Richness", "Time step", ecosystemModelGrid.GlobalMissingValue,
                                                BasicOutputMemory, 0);
                    DataConverter.ValueToSDS1D(Metrics.CalculateFunctionalRichness(ecosystemModelGrid, cohortFunctionalGroupDefinitions, cellIndices, cellIndex, "Trophic index")[1],
                                                "Min Trophic Index", "Time step", ecosystemModelGrid.GlobalMissingValue,
                                                BasicOutputMemory, 0);
                    DataConverter.ValueToSDS1D(Metrics.CalculateFunctionalRichness(ecosystemModelGrid, cohortFunctionalGroupDefinitions, cellIndices, cellIndex, "Trophic index")[2],
                                                "Max Trophic Index", "Time step", ecosystemModelGrid.GlobalMissingValue,
                                                BasicOutputMemory, 0);

                    DataConverter.ValueToSDS1D(Metrics.CalculateArithmeticCommunityMeanBodyMass(ecosystemModelGrid, cellIndices, cellIndex),
                                                "Arithmetic Mean Bodymass", "Time step", ecosystemModelGrid.GlobalMissingValue,
                                                BasicOutputMemory, 0);
                    DataConverter.ValueToSDS1D(Metrics.CalculateGeometricCommunityMeanBodyMass(ecosystemModelGrid, cellIndices, cellIndex),
                                                "Geometric Mean Bodymass", "Time step", ecosystemModelGrid.GlobalMissingValue,
                                                BasicOutputMemory, 0);

                }

                if (MarineCell && TrackMarineSpecifics)
                {
                    DataConverter.ValueToSDS1D(TotalIncomingNPP, "Incoming NPP", "Time step", ecosystemModelGrid.GlobalMissingValue,
                        BasicOutputMemory, 0);
                }

                // File outputs for high detail level
                if (ModelOutputDetail == OutputDetailLevel.High)
                {
                    if (OutputMetrics)
                    {
                        DataConverter.VectorToSDS2D(Metrics.CalculateTrophicDistribution(ecosystemModelGrid,cellIndices,cellIndex), "Trophic Index Distribution",
                        new string[2] { "Time step", "Trophic Index Bins" }, TimeSteps, Metrics.TrophicIndexBinValues, ecosystemModelGrid.GlobalMissingValue, MassBinsOutputMemory, 0);
                    }

                    foreach (var TraitValue in AbundancesInMassBins.Keys)
                    {
                        DataConverter.VectorToSDS2D(AbundancesInMassBins[TraitValue], "Log " + TraitValue + " abundance in mass bins",
                        new string[2] { "Time step", "Mass bin" }, TimeSteps, MassBins, ecosystemModelGrid.GlobalMissingValue, MassBinsOutputMemory, 0);
                        DataConverter.VectorToSDS2D(BiomassesInMassBins[TraitValue], "Log " + TraitValue + " biomass in mass bins",
                        new string[2] { "Time step", "Mass bin" }, TimeSteps, MassBins, ecosystemModelGrid.GlobalMissingValue, MassBinsOutputMemory, 0);
                        // Write out abundances in combinations of juvenile and adult mass bins to the relevant three-dimensional output variables
                        DataConverter.Array2DToSDS3D(AbundancesInJuvenileAdultMassBins[TraitValue],
                            "Log " + TraitValue + " abundance in juvenile vs adult bins",
                            new string[] { "Adult Mass bin", "Juvenile Mass bin", "Time step" },
                            0,
                            ecosystemModelGrid.GlobalMissingValue,
                            MassBinsOutputMemory);
                        DataConverter.Array2DToSDS3D(BiomassesInJuvenileAdultMassBins[TraitValue],
                            "Log " + TraitValue + " biomass in juvenile vs adult bins",
                            new string[] { "Adult Mass bin", "Juvenile Mass bin", "Time step" },
                            0,
                            ecosystemModelGrid.GlobalMissingValue,
                            MassBinsOutputMemory);
                    }

                    // Write outputs for tracking individual cohorts

                    // Loop over tracked cohorts
                    for (int i = 0; i < TrackedCohorts.Count; i++)
                    {
                        // Add the individual body mass of the tracked cohort to the output dataset
                        string[] TrackedCohortDimensions = new string[] { "Time step", "Cohort ID" };
                        DataConverter.ValueToSDS2D(TrackedCohortIndividualMasses[i], "Individual body mass", TrackedCohortDimensions,
                            ecosystemModelGrid.GlobalMissingValue, TrackedCohortsOutputMemory, 0, i);
                        DataConverter.ValueToSDS2D(TrackedCohortAbundances[i], "Number of individuals", TrackedCohortDimensions,
                            ecosystemModelGrid.GlobalMissingValue, TrackedCohortsOutputMemory, 0, i);
                    }

                }

            }
        }
Beispiel #25
0
 /// <summary>
 /// Record dispersal events in the dispersal tracker
 /// </summary>
 /// <param name="inboundCohorts">The cohorts arriving in a cell in the current time step</param>
 /// <param name="outboundCohorts">The cohorts leaving a cell in the current time step</param>
 /// <param name="outboundCohortWeights">The body masses of cohorts leaving the cell in the current time step</param>
 /// <param name="timestep">The current model time step</param>
 /// <param name="madingleyModelGrid">The model grid</param>
 public void RecordDispersalForACell(uint[, ,] inboundCohorts, uint[, ,] outboundCohorts, List <double>[,] outboundCohortWeights, uint timestep, ModelGrid madingleyModelGrid)
 {
     _TrackDispersal.RecordDispersal(inboundCohorts, outboundCohorts, outboundCohortWeights, timestep, madingleyModelGrid);
 }
Beispiel #26
0
        private void CalculateOutputs(ModelGrid ecosystemModelGrid, FunctionalGroupDefinitions cohortFunctionalGroupDefinitions,
                                      FunctionalGroupDefinitions stockFunctionalGroupDefinitions, List <uint[]> cellIndices, MadingleyModelInitialisation initialisation, uint currentTimestep)
        {
            // Get grids of the total biomass densities of all stocks and all cohorts in each grid cell
            LogBiomassDensityGridCohorts = ecosystemModelGrid.GetStateVariableGridLogDensityPerSqKm("Biomass", "NA", cohortFunctionalGroupDefinitions.
                                                                                                    AllFunctionalGroupsIndex, cellIndices, "cohort", initialisation);
            LogBiomassDensityGridStocks = ecosystemModelGrid.GetStateVariableGridLogDensityPerSqKm("Biomass", "NA", stockFunctionalGroupDefinitions.
                                                                                                   AllFunctionalGroupsIndex, cellIndices, "stock", initialisation);

            // Get grids of total abundance densities of all stocks and all cohorts in each grid cell
            LogAbundanceDensityGridCohorts = ecosystemModelGrid.GetStateVariableGridLogDensityPerSqKm("Abundance", "NA", cohortFunctionalGroupDefinitions.
                                                                                                      AllFunctionalGroupsIndex, cellIndices, "cohort", initialisation);

            // Loop over grid cells and add stock and cohort biomass density to get the total of all biomass densities
            for (int ii = 0; ii < ecosystemModelGrid.NumLatCells; ii++)
            {
                for (int jj = 0; jj < ecosystemModelGrid.NumLonCells; jj++)
                {
                    LogBiomassDensityGrid[ii, jj] = Math.Log(Math.Exp(LogBiomassDensityGridCohorts[ii, jj]) + Math.Exp(LogBiomassDensityGridStocks[ii, jj]));
                }
            }

            string[] Keys = CohortTraitIndices.Keys.ToArray();
            foreach (string Key in Keys)
            {
                BiomassDensityGrid[Key]   = ecosystemModelGrid.GetStateVariableGridLogDensityPerSqKm("Biomass", "NA", CohortTraitIndices[Key], cellIndices, "cohort", initialisation);
                AbundanceDensityGrid[Key] = ecosystemModelGrid.GetStateVariableGridLogDensityPerSqKm("Abundance", "NA", CohortTraitIndices[Key], cellIndices, "cohort", initialisation);
            }

            Keys = StockTraitIndices.Keys.ToArray();
            foreach (string Key in Keys)
            {
                BiomassDensityGrid[Key] = ecosystemModelGrid.GetStateVariableGridLogDensityPerSqKm("Biomass", "NA", StockTraitIndices[Key], cellIndices, "stock", initialisation);
            }


            // Temporary outputs to check plant model

            Realm = ecosystemModelGrid.GetEnviroGrid("Realm", 0);

            FrostDays   = ecosystemModelGrid.GetEnviroGrid("Fraction Year Frost", 0);
            Temperature = ecosystemModelGrid.GetEnviroGrid("Temperature", Utils.GetCurrentMonth(currentTimestep, initialisation.GlobalModelTimeStepUnit));

            for (int i = 0; i < ecosystemModelGrid.NumLatCells; i++)
            {
                for (int j = 0; j < ecosystemModelGrid.NumLonCells; j++)
                {
                    FracEvergreen[i, j] = BiomassDensityGrid["evergreen"][i, j] / BiomassDensityGrid["autotroph"][i, j];
                }
            }

            HANPP = ecosystemModelGrid.GetEnviroGrid("HANPP", 0);

            if (OutputMetrics)
            {
                //Calculate the values for the ecosystem metrics for each of the grid cells
                for (int i = 0; i < cellIndices.Count; i++)
                {
                    uint latIndex = cellIndices[i][0];
                    uint lonIndex = cellIndices[i][1];
                    MetricsGrid["Mean Trophic Level"][latIndex, lonIndex] = Metrics.CalculateMeanTrophicLevelCell(ecosystemModelGrid, cellIndices, i);
                    MetricsGrid["Trophic Evenness"][latIndex, lonIndex]   = Metrics.CalculateFunctionalEvennessRao(ecosystemModelGrid, cohortFunctionalGroupDefinitions, cellIndices, i, "trophic index");
                    MetricsGrid["Biomass Evenness"][latIndex, lonIndex]   = Metrics.CalculateFunctionalEvennessRao(ecosystemModelGrid, cohortFunctionalGroupDefinitions, cellIndices, i, "biomass");
                    double[] FunctionalDiversity = Metrics.CalculateFunctionalDiversity(ecosystemModelGrid, cohortFunctionalGroupDefinitions,
                                                                                        cellIndices, i);
                    // Functional Richness not currently calculated
                    //MetricsGrid["Functional Richness"][latIndex, lonIndex] = FunctionalDiversity[0];
                    MetricsGrid["Rao Functional Evenness"][latIndex, lonIndex] = FunctionalDiversity[1];
                    MetricsGrid["Biomass Richness"][latIndex, lonIndex]        = Metrics.CalculateFunctionalRichness(ecosystemModelGrid, cohortFunctionalGroupDefinitions, cellIndices, i, "Biomass")[0];
                    MetricsGrid["Min Bodymass"][latIndex, lonIndex]            = Metrics.CalculateFunctionalRichness(ecosystemModelGrid, cohortFunctionalGroupDefinitions, cellIndices, i, "Biomass")[1];
                    MetricsGrid["Max Bodymass"][latIndex, lonIndex]            = Metrics.CalculateFunctionalRichness(ecosystemModelGrid, cohortFunctionalGroupDefinitions, cellIndices, i, "Biomass")[2];
                    MetricsGrid["Trophic Richness"][latIndex, lonIndex]        = Metrics.CalculateFunctionalRichness(ecosystemModelGrid, cohortFunctionalGroupDefinitions, cellIndices, i, "Trophic Index")[0];
                    MetricsGrid["Min Trophic Index"][latIndex, lonIndex]       = Metrics.CalculateFunctionalRichness(ecosystemModelGrid, cohortFunctionalGroupDefinitions, cellIndices, i, "Trophic Index")[1];
                    MetricsGrid["Max Trophic Index"][latIndex, lonIndex]       = Metrics.CalculateFunctionalRichness(ecosystemModelGrid, cohortFunctionalGroupDefinitions, cellIndices, i, "Trophic Index")[2];

                    MetricsGrid["Arithmetic Mean Bodymass"][latIndex, lonIndex] = Metrics.CalculateArithmeticCommunityMeanBodyMass(ecosystemModelGrid, cellIndices, i);
                    MetricsGrid["Geometric Mean Bodymass"][latIndex, lonIndex]  = Metrics.CalculateGeometricCommunityMeanBodyMass(ecosystemModelGrid, cellIndices, i);
                }
            }
        }
Beispiel #27
0
        public void InitialOutputs(ModelGrid ecosystemModelGrid, FunctionalGroupDefinitions cohortFunctionalGroupDefinitions, FunctionalGroupDefinitions
                                   stockFunctionalGroupDefinitions, List <uint[]> cellIndices, MadingleyModelInitialisation initialisation)
        {
            Console.WriteLine("Writing initial grid outputs...");

            // Calculate the output variables
            CalculateOutputs(ecosystemModelGrid, cohortFunctionalGroupDefinitions, stockFunctionalGroupDefinitions, cellIndices, initialisation, 0);

            // Write the total biomass of cohorts to the live display
            if (LiveOutputs)
            {
                DataConverter.Array2DToSDS2D(LogBiomassDensityGridCohorts, "Log(Biomass density, g/km^2)", ecosystemModelGrid.Lats,
                                             ecosystemModelGrid.Lons, ecosystemModelGrid.GlobalMissingValue, DataSetToViewLive);
            }

            // Add the grid of total biomass in cells to the file dataset
            DataConverter.Array2DToSDS3D(LogBiomassDensityGridCohorts, "Biomass density", new string[] { "Latitude", "Longitude", "Time step" }, 0,
                                         ecosystemModelGrid.GlobalMissingValue, GridOutput);

            DataConverter.Array2DToSDS3D(LogAbundanceDensityGridCohorts, "Abundance density", new string[] { "Latitude", "Longitude", "Time step" }, 0,
                                         ecosystemModelGrid.GlobalMissingValue, GridOutput);


            // Temporary outputs to check plant model
            DataConverter.Array2DToSDS3D(Realm, "Realm", new string[] { "Latitude", "Longitude", "Time step" },
                                         0, ecosystemModelGrid.GlobalMissingValue, GridOutput);

            DataConverter.Array2DToSDS3D(HANPP, "HANPP", new string[] { "Latitude", "Longitude", "Time step" },
                                         0, ecosystemModelGrid.GlobalMissingValue, GridOutput);

            DataConverter.Array2DToSDS3D(Temperature, "Temperature", new string[] { "Latitude", "Longitude", "Time step" },
                                         0, ecosystemModelGrid.GlobalMissingValue, GridOutput);

            // File outputs for medium and high detail levels
            if ((ModelOutputDetail == OutputDetailLevel.Medium) || (ModelOutputDetail == OutputDetailLevel.High))
            {
                // Add the biomass grids for individual trait combinations to the file dataset
                foreach (var Key in BiomassDensityGrid.Keys)
                {
                    DataConverter.Array2DToSDS3D(BiomassDensityGrid[Key], Key + "biomass density", new string[]
                                                 { "Latitude", "Longitude", "Time step" }, 0, ecosystemModelGrid.GlobalMissingValue, GridOutput);
                }

                // Add the abundance density grid
                foreach (var Key in AbundanceDensityGrid.Keys)
                {
                    DataConverter.Array2DToSDS3D(AbundanceDensityGrid[Key], Key + "abundance density",
                                                 new string[] { "Latitude", "Longitude", "Time step" },
                                                 0,
                                                 ecosystemModelGrid.GlobalMissingValue,
                                                 GridOutput);
                }

                // Output ecosystem metrics
                if (OutputMetrics)
                {
                    foreach (string Key in MetricsGrid.Keys)
                    {
                        DataConverter.Array2DToSDS3D(MetricsGrid[Key], Key,
                                                     new string[] { "Latitude", "Longitude", "Time step" },
                                                     0,
                                                     ecosystemModelGrid.GlobalMissingValue,
                                                     GridOutput);
                    }
                }
            }
        }
        /// <summary>
        /// Calculates functional diversity of cohorts in a grid cell as functional richness and functional diveregence (using the Rao Index)
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid</param>
        /// <param name="cohortDefinitions">The functional group definitions for cohorts in the model</param>
        /// <param name="cellIndices">The list of cell indices in the current model simulation</param>
        /// <param name="cellIndex">The index of the current cell within the list of cells to run</param>
        /// <returns>A pair of values representing the functional richness and functional divergence (functional richness currently disabled!)</returns>
        public double[] CalculateFunctionalDiversity(ModelGrid ecosystemModelGrid, FunctionalGroupDefinitions cohortDefinitions,
                                                     List <uint[]> cellIndices, int cellIndex)
        {
            //Get the cohorts for the specified cell
            GridCellCohortHandler CellCohorts = ecosystemModelGrid.GetGridCellCohorts(cellIndices[cellIndex][0], cellIndices[cellIndex][1]);

            //Variable to hold the functional richness value for the current cohorts
            double FunctionalRichness;
            //Variable to hold the functional divergence value for the current cohorts
            double RaoFunctionalDivergence = 0.0;

            double[,] Distances = new double[CellCohorts.GetNumberOfCohorts(), CellCohorts.GetNumberOfCohorts()];

            List <string> AllTraitNames = cohortDefinitions.GetAllTraitNames().ToList();

            AllTraitNames.Remove("realm");
            AllTraitNames.Remove("heterotroph/autotroph");
            AllTraitNames.Remove("diet");
            string[] TraitNames = AllTraitNames.ToArray();


            //Define upper and lower limits for body mass
            double MinMass = cohortDefinitions.GetBiologicalPropertyAllFunctionalGroups("minimum mass").Min();
            double MaxMass = cohortDefinitions.GetBiologicalPropertyAllFunctionalGroups("maximum mass").Max();
            //Define upp and lower limits for trophic index
            double MaxTI = 40.0;
            double MinTI = 1.0;

            // Construct an array of functional trait values for each cohort
            // Rows are specific cohorts
            // Columns are the functional traits (these include different types:
            //      quantative: current mass, trophic index
            //      nominal: diet, reproductive strategy, mobility, metabolism
            Tuple <double[], string[]>[] CohortFunctionalTraits = new Tuple <double[], string[]> [CellCohorts.GetNumberOfCohorts()];
            double[]   IndividualBodyMasses     = new double[CellCohorts.GetNumberOfCohorts()];
            double[]   TrophicIndex             = new double[CellCohorts.GetNumberOfCohorts()];
            string[][] CohortNominalTraitValues = new string[TraitNames.Length][];

            for (int i = 0; i < TraitNames.Length; i++)
            {
                CohortNominalTraitValues[i] = new string[CellCohorts.GetNumberOfCohorts()];
            }

            // Construct a vector of cohort biomass (in case we want to weight by them)
            double[] CohortTotalBiomasses = new double[CellCohorts.GetNumberOfCohorts()];


            string[] TraitValues             = new string[TraitNames.Length];
            double[] QuantitativeTraitValues = new double[2];
            int      CohortNumberCounter     = 0;

            for (int fg = 0; fg < CellCohorts.Count; fg++)
            {
                foreach (Cohort c in CellCohorts[fg])
                {
                    TraitValues = cohortDefinitions.GetTraitValues(TraitNames, fg);
                    for (int ii = 0; ii < TraitValues.Length; ii++)
                    {
                        CohortNominalTraitValues[ii][CohortNumberCounter] = TraitValues[ii];
                    }


                    IndividualBodyMasses[CohortNumberCounter] = c.IndividualBodyMass;
                    TrophicIndex[CohortNumberCounter]         = c.TrophicIndex;

                    QuantitativeTraitValues[0] = c.IndividualBodyMass;
                    QuantitativeTraitValues[1] = c.TrophicIndex;

                    CohortFunctionalTraits[CohortNumberCounter] = new Tuple <double[], string[]>(QuantitativeTraitValues, TraitValues);

                    CohortTotalBiomasses[CohortNumberCounter] = (c.IndividualBodyMass + c.IndividualReproductivePotentialMass) * c.CohortAbundance;

                    CohortNumberCounter++;
                }
            }

            List <double[, ]> DistanceList = new List <double[, ]>();

            DistanceList.Add(CalculateDistanceMatrix(IndividualBodyMasses, MaxMass, MinMass));
            DistanceList.Add(CalculateDistanceMatrix(TrophicIndex, MaxTI, MinTI));
            foreach (string[] t in CohortNominalTraitValues)
            {
                DistanceList.Add(CalculateDistanceMatrix(t));
            }

            Distances = CalculateAggregateDistance(DistanceList);

            RaoFunctionalDivergence = RaoEntropy(Distances, CohortTotalBiomasses);

            return(new double[] { 0.0, RaoFunctionalDivergence });
        }
        /// <summary>
        /// Apply all updates from the ecological processes to the properties of the acting cohort and to the environment
        /// </summary>
        public void UpdateAllCrossGridCellEcology(ModelGrid madingleyModelGrid, ref uint dispersalCounter, CrossCellProcessTracker trackCrossCellProcesses, uint currentTimeStep)
        {
            // Create an array to hold the number of cohorts dispersing in each direction from each grid cell
            uint[, ,] InboundCohorts = new uint[madingleyModelGrid.DeltaFunctionalGroupDispersalArray.GetLength(0), madingleyModelGrid.DeltaFunctionalGroupDispersalArray.GetLength(1), 8];

            // Create an array to hold the number of cohorts dispersing in each direction to each grid cell
            uint[, ,] OutboundCohorts = new uint[madingleyModelGrid.DeltaFunctionalGroupDispersalArray.GetLength(0), madingleyModelGrid.DeltaFunctionalGroupDispersalArray.GetLength(1), 8];

            // Create an list array to hold the weights of cohorts dispersing from grid cell. Dimensions are: num grid cells lon, num grid cells lat, num cohorts dispersing
            List <double>[,] OutboundCohortWeights = new List <double> [madingleyModelGrid.DeltaFunctionalGroupDispersalArray.GetLength(0), madingleyModelGrid.DeltaFunctionalGroupDispersalArray.GetLength(1)];

            for (uint ii = 0; ii < madingleyModelGrid.DeltaFunctionalGroupDispersalArray.GetLength(0); ii++)
            {
                for (uint jj = 0; jj < madingleyModelGrid.DeltaFunctionalGroupDispersalArray.GetLength(1); jj++)
                {
                    OutboundCohortWeights[ii, jj] = new List <double>();
                }
            }

            // Loop through the delta array that holds the grid cells of the cohorts that are flagged as needing to be moved
            for (uint ii = 0; ii < madingleyModelGrid.DeltaFunctionalGroupDispersalArray.GetLength(0); ii++)
            {
                for (uint jj = 0; jj < madingleyModelGrid.DeltaFunctionalGroupDispersalArray.GetLength(1); jj++)
                {
                    // No cohorts to move if there are none in the delta dispersal array
                    if (madingleyModelGrid.DeltaFunctionalGroupDispersalArray[ii, jj].Count == 0)
                    {
                    }
                    // Otherwise, loop through the cohorts and change the pointers/references to them one-by-one
                    else
                    {
                        for (int kk = 0; kk < madingleyModelGrid.DeltaFunctionalGroupDispersalArray[ii, jj].Count; kk++)
                        {
                            // Find out which grid cell it is going to
                            uint[] CellToDisperseTo = madingleyModelGrid.DeltaCellToDisperseToArray[ii, jj].ElementAt(kk);

                            // Functional group is identified by the first array
                            uint CohortToDisperseFG = madingleyModelGrid.DeltaFunctionalGroupDispersalArray[ii, jj].ElementAt(kk);

                            // Cohort number is identified by the second array
                            uint CohortToDisperseNum = madingleyModelGrid.DeltaCohortNumberDispersalArray[ii, jj].ElementAt(kk);

                            // If track processes is on, add this to the list of inbound and outbound cohorts
                            if (trackCrossCellProcesses.TrackCrossCellProcesses)
                            {
                                WriteOutCrossGridCell(madingleyModelGrid, CellToDisperseTo, InboundCohorts, OutboundCohorts, OutboundCohortWeights, ii, jj,
                                                      CohortToDisperseFG, CohortToDisperseNum, madingleyModelGrid.DeltaCellExitDirection[ii, jj].ElementAt(kk),
                                                      madingleyModelGrid.DeltaCellEntryDirection[ii, jj].ElementAt(kk));
                            }

                            // Simmply add it to the existing cohorts in that FG in the grid cell to disperse to
                            madingleyModelGrid.AddNewCohortToGridCell(CellToDisperseTo[0], CellToDisperseTo[1], (int)CohortToDisperseFG, madingleyModelGrid.GetGridCellIndividualCohort(ii, jj, (int)CohortToDisperseFG, (int)CohortToDisperseNum));

                            // Update the dispersal counter
                            dispersalCounter++;

                            // So now there is a pointer in the grid cell to which it is going. We have to delete the pointers in the original cell and in the
                            // delta array, but we need to do this without messing with the list structure; i.e. wait until all cohorts have been moved
                        }
                    }
                }
            }

            // Reset the delta arrays and remove the pointers to the cohorts in the original list
            for (uint ii = 0; ii < madingleyModelGrid.DeltaFunctionalGroupDispersalArray.GetLength(0); ii++)
            {
                for (uint jj = 0; jj < madingleyModelGrid.DeltaFunctionalGroupDispersalArray.GetLength(1); jj++)
                {
                    // No cohorts to move if there are none in the delta dispersal array
                    if (madingleyModelGrid.DeltaFunctionalGroupDispersalArray[ii, jj].Count == 0)
                    {
                    }
                    // Otherwise, loop through the cohorts and change the pointers/references to them one-by-one
                    else
                    {
                        // Delete the cohorts from the original grid cell. Note that this needs to be done carefully to ensure that the correct ones
                        // are deleted (lists shift about when an internal element is deleted.
                        madingleyModelGrid.DeleteGridCellIndividualCohorts(ii, jj, madingleyModelGrid.DeltaFunctionalGroupDispersalArray[ii, jj], madingleyModelGrid.DeltaCohortNumberDispersalArray[ii, jj]);

                        // Reset the lists in the delta dispersal arrays
                        madingleyModelGrid.DeltaFunctionalGroupDispersalArray[ii, jj] = new List <uint>();
                        madingleyModelGrid.DeltaCohortNumberDispersalArray[ii, jj]    = new List <uint>();

                        // Reset the list in the grid cells to disperse to array
                        madingleyModelGrid.DeltaCellToDisperseToArray[ii, jj] = new List <uint[]>();

                        // Reset the lists in the delta dispersal arrays
                        madingleyModelGrid.DeltaCellExitDirection[ii, jj]  = new List <uint>();
                        madingleyModelGrid.DeltaCellEntryDirection[ii, jj] = new List <uint>();
                    }
                }
            }

            if (trackCrossCellProcesses.TrackCrossCellProcesses)
            {
                // If we are tracking dispersal, then write out how many cohorts have moved to a file
                trackCrossCellProcesses.RecordDispersalForACell(InboundCohorts, OutboundCohorts, OutboundCohortWeights, currentTimeStep, madingleyModelGrid);
            }
        }
        /// <summary>
        /// Sets up the outputs associated with medium and high levels of output detail
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid</param>
        /// <param name="marineCell">Whether the current cell is a marine cell</param>
        private void SetupMediumLevelOutputs(ModelGrid ecosystemModelGrid, Boolean marineCell)
        {
            string[] TimeDimension = { "Time step" };

            if (OutputMetrics)
            {
                DataConverter.AddVariable(BasicOutputMemory, "Mean Trophic Level", "dimensionless", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                DataConverter.AddVariable(BasicOutputMemory, "Trophic Evenness", "dimensionless", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                DataConverter.AddVariable(BasicOutputMemory, "Biomass Evenness", "dimensionless", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                DataConverter.AddVariable(BasicOutputMemory, "Functional Richness", "dimensionless", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                DataConverter.AddVariable(BasicOutputMemory, "Rao Functional Evenness", "dimensionless", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                DataConverter.AddVariable(BasicOutputMemory, "Biomass Richness", "dimensionless", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                DataConverter.AddVariable(BasicOutputMemory, "Trophic Richness", "dimensionless", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);

                DataConverter.AddVariable(BasicOutputMemory, "Max Bodymass", "g", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                DataConverter.AddVariable(BasicOutputMemory, "Min Bodymass", "g", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                DataConverter.AddVariable(BasicOutputMemory, "Max Trophic Index", "dimensionless", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                DataConverter.AddVariable(BasicOutputMemory, "Min Trophic Index", "dimensionless", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                DataConverter.AddVariable(BasicOutputMemory, "Geometric Mean Bodymass", "g", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                DataConverter.AddVariable(BasicOutputMemory, "Arithmetic Mean Bodymass", "g", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
            }

            if (marineCell)
            {
                foreach (string TraitValue in CohortTraitIndicesMarine.Keys)
                {

                    DataConverter.AddVariable(BasicOutputMemory, TraitValue + " density", "Individuals / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                    DataConverter.AddVariable(BasicOutputMemory, TraitValue + " biomass density", "Kg / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                }

                foreach (string TraitValue in StockTraitIndicesMarine.Keys)
                {
                    DataConverter.AddVariable(BasicOutputMemory, TraitValue + " biomass density", "Kg / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                }

                if (TrackMarineSpecifics)
                {
                    // Add a variable to keep track of the NPP incoming from the VGPM model
                    DataConverter.AddVariable(BasicOutputMemory, "Incoming NPP", "gC / m^2 / day", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                }
            }
            else
            {
                foreach (string TraitValue in CohortTraitIndices.Keys)
                {

                    DataConverter.AddVariable(BasicOutputMemory, TraitValue + " density", "Individuals / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                    DataConverter.AddVariable(BasicOutputMemory, TraitValue + " biomass density", "Kg / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                }

                foreach (string TraitValue in StockTraitIndices.Keys)
                {
                    DataConverter.AddVariable(BasicOutputMemory, TraitValue + " biomass density", "Kg / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                }
            }
        }
        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>
        /// Generate the live outputs for the current time step
        /// </summary>
        /// <param name="numTimeSteps">The number of time steps in the model run</param>
        /// <param name="currentTimeStep">The current time step</param>
        /// <param name="ecosystemModelGrid">The model grid</param>
        /// <param name="marineCell">Whether the current cell is a marine cell</param>
        private void TimeStepLiveOutputs(uint numTimeSteps, uint currentTimeStep, ModelGrid ecosystemModelGrid, Boolean marineCell)
        {
            // Output to the live graph view according to the specified level of detail
            if (ModelOutputDetail == OutputDetailLevel.Low)
            {
                // Rescale the y-axis if necessary
                if (TotalLivingBiomass > MaximumYValue)
                {
                    MaximumYValue = TotalLivingBiomass * 1.1;
                    DataSetToViewLive.Metadata["VisualHints"] = "\"Total living biomass\"[Time step]; Style:Polyline; Visible: 0,1," +
                    numTimeSteps.ToString() + "," + MaximumYValue.ToString() +
                    "; LogScale:Y; Stroke:#D95F02; Thickness:3; Title:\"Total Biomass" + "\"";
                }
                // Write out total living biomass
                DataConverter.ValueToSDS1D(TotalLivingBiomass, "Total living biomass", "Time step",
                    ecosystemModelGrid.GlobalMissingValue, DataSetToViewLive, (int)currentTimeStep + 1);

            }
            else
            {
                //Find the max value in the TotalBiomassDensities
                double MaxVal = 0.0;
                foreach (var KVPair in TotalBiomassDensitiesOut)
                {
                    if (KVPair.Value > MaxVal) MaxVal = KVPair.Value;
                }
                // Rescale the y-axis if necessary
                if (MaxVal > MaximumYValue)
                {
                    MaximumYValue = MaxVal * 1.1;
                    DataSetToViewLive.Metadata["VisualHints"] = "\"autotroph biomass\"[Time step]; Style:Polyline; Visible: 0,1,"
                        + numTimeSteps.ToString() + ","
                        + MaximumYValue.ToString() + "; LogScale:Y;  Stroke:#FF008040;Thickness:3;;\"carnivore biomass\"[Time step] ; Style:Polyline; Visible: 0,1,"
                        + numTimeSteps.ToString() + ","
                        + MaximumYValue.ToString() + "; LogScale:Y;  Stroke:#FFFF0000;Thickness:3;;\"herbivore biomass\"[Time step] ; Style:Polyline; Visible: 0,1,"
                        + numTimeSteps.ToString() + ","
                        + MaximumYValue.ToString() + "; LogScale:Y;  Stroke:#FF00FF00;Thickness:3;;\"omnivore biomass\"[Time step] ; Style:Polyline; Visible: 0,1,"
                        + numTimeSteps.ToString() + ","
                        + MaximumYValue.ToString() + "; LogScale:Y;  Stroke:#FF0000FF;Thickness:3; Title:\"Biomass Densities";
                }

                if (marineCell)
                {
                    foreach (string TraitValue in CohortTraitIndicesMarine.Keys)
                    {
                        // Output the total carnivore, herbivore and omnivore abundances
                        DataConverter.ValueToSDS1D(TotalDensitiesOut[TraitValue], TraitValue + " density", "Time step", ecosystemModelGrid.GlobalMissingValue, DataSetToViewLive, (int)currentTimeStep + 1);
                        DataConverter.ValueToSDS1D(TotalBiomassDensitiesOut[TraitValue], TraitValue + " biomass", "Time step", ecosystemModelGrid.GlobalMissingValue, DataSetToViewLive, (int)currentTimeStep + 1);
                    }

                    foreach (string TraitValue in StockTraitIndicesMarine.Keys)
                    {
                        // Add in the initial values of stock biomass density
                        DataConverter.ValueToSDS1D(TotalBiomassDensitiesOut[TraitValue], TraitValue + " biomass", "Time step", ecosystemModelGrid.GlobalMissingValue, DataSetToViewLive, (int)currentTimeStep + 1);
                    }
                }
                else
                {
                    foreach (string TraitValue in CohortTraitIndices.Keys)
                    {
                        // Output the total carnivore, herbivore and omnivore abundances
                        DataConverter.ValueToSDS1D(TotalDensitiesOut[TraitValue], TraitValue + " density", "Time step", ecosystemModelGrid.GlobalMissingValue, DataSetToViewLive, (int)currentTimeStep + 1);
                        DataConverter.ValueToSDS1D(TotalBiomassDensitiesOut[TraitValue], TraitValue + " biomass", "Time step", ecosystemModelGrid.GlobalMissingValue, DataSetToViewLive, (int)currentTimeStep + 1);
                    }

                    foreach (string TraitValue in StockTraitIndices.Keys)
                    {
                        // Add in the initial values of stock biomass density
                        DataConverter.ValueToSDS1D(TotalBiomassDensitiesOut[TraitValue], TraitValue + " biomass", "Time step", ecosystemModelGrid.GlobalMissingValue, DataSetToViewLive, (int)currentTimeStep + 1);
                    }
                }
            }
        }
        private double[, , ,] CalculateCurrentCohortState(ModelGrid currentModelState, string variableName, int numLats, int numLons, int numFG, int numCohorts, List <uint[]> cellList)
        {
            //Calculate the cohort state
            double[, , ,] State = new double[numLats, numLons, numFG, numCohorts];
            GridCellCohortHandler CellCohorts;

            for (int cellIndex = 0; cellIndex < cellList.Count; cellIndex++)
            {
                CellCohorts = currentModelState.GetGridCellCohorts(cellList[cellIndex][0], cellList[cellIndex][1]);

                for (int functionalGroupIndex = 0; functionalGroupIndex < CellCohorts.Count; functionalGroupIndex++)
                {
                    for (int cohortIndex = 0; cohortIndex < CellCohorts[functionalGroupIndex].Count; cohortIndex++)
                    {
                        switch (variableName)
                        {
                        case "JuvenileMass":
                            State[cellList[cellIndex][0], cellList[cellIndex][1], functionalGroupIndex, cohortIndex] = CellCohorts[functionalGroupIndex][cohortIndex].JuvenileMass;
                            break;

                        case "AdultMass":
                            State[cellList[cellIndex][0], cellList[cellIndex][1], functionalGroupIndex, cohortIndex] = CellCohorts[functionalGroupIndex][cohortIndex].AdultMass;
                            break;

                        case "IndividualBodyMass":
                            State[cellList[cellIndex][0], cellList[cellIndex][1], functionalGroupIndex, cohortIndex] = CellCohorts[functionalGroupIndex][cohortIndex].IndividualBodyMass;
                            break;

                        case "CohortAbundance":
                            State[cellList[cellIndex][0], cellList[cellIndex][1], functionalGroupIndex, cohortIndex] = CellCohorts[functionalGroupIndex][cohortIndex].CohortAbundance;
                            break;

                        case "BirthTimeStep":
                            State[cellList[cellIndex][0], cellList[cellIndex][1], functionalGroupIndex, cohortIndex] = (double)CellCohorts[functionalGroupIndex][cohortIndex].BirthTimeStep;
                            break;

                        case "MaturityTimeStep":
                            State[cellList[cellIndex][0], cellList[cellIndex][1], functionalGroupIndex, cohortIndex] = (double)CellCohorts[functionalGroupIndex][cohortIndex].MaturityTimeStep;
                            break;

                        case "LogOptimalPreyBodySizeRatio":
                            State[cellList[cellIndex][0], cellList[cellIndex][1], functionalGroupIndex, cohortIndex] = CellCohorts[functionalGroupIndex][cohortIndex].LogOptimalPreyBodySizeRatio;
                            break;

                        case "MaximumAchievedBodyMass":
                            State[cellList[cellIndex][0], cellList[cellIndex][1], functionalGroupIndex, cohortIndex] = CellCohorts[functionalGroupIndex][cohortIndex].MaximumAchievedBodyMass;
                            break;

                        case "Merged":
                            State[cellList[cellIndex][0], cellList[cellIndex][1], functionalGroupIndex, cohortIndex] = Convert.ToDouble(CellCohorts[functionalGroupIndex][cohortIndex].Merged);
                            break;

                        case "TrophicIndex":
                            State[cellList[cellIndex][0], cellList[cellIndex][1], functionalGroupIndex, cohortIndex] = CellCohorts[functionalGroupIndex][cohortIndex].TrophicIndex;
                            break;

                        case "ProportionTimeActive":
                            State[cellList[cellIndex][0], cellList[cellIndex][1], functionalGroupIndex, cohortIndex] = CellCohorts[functionalGroupIndex][cohortIndex].ProportionTimeActive;
                            break;
                        }
                    }
                }
            }

            return(State);
        }
        /// <summary>
        /// Write to the output file values of the output variables before the first time step
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid to get data from</param>C:\madingley-ecosystem-model\Madingley\Output and tracking\PredationTracker.cs
        /// <param name="cohortFunctionalGroupDefinitions">The definitions of cohort functional groups in the model</param>
        /// <param name="stockFunctionalGroupDefinitions">The definitions of stock functional groups in the model</param>
        /// <param name="cellIndices">List of indices of active cells in the model grid</param>
        /// <param name="cellNumber">The number of the current cell in the list of indices of active cells</param>
        /// <param name="globalDiagnosticVariables">List of global diagnostic variables</param>
        /// <param name="numTimeSteps">The number of time steps in the model run</param>
        /// <param name="initialisation">The Madingley Model initialisation</param>
        /// <param name="month">The current month in the model run</param>
        /// <param name="marineCell">Whether the current cell is a marine cell</param>
        public void InitialOutputs(ModelGrid ecosystemModelGrid, FunctionalGroupDefinitions cohortFunctionalGroupDefinitions,
            FunctionalGroupDefinitions stockFunctionalGroupDefinitions, List<uint[]> cellIndices, int cellNumber,
            SortedList<string, double> globalDiagnosticVariables, uint numTimeSteps, MadingleyModelInitialisation initialisation, 
            uint month, Boolean marineCell)
        {
            // Calculate values of the output variables to be used
            CalculateOutputs(ecosystemModelGrid, cohortFunctionalGroupDefinitions, stockFunctionalGroupDefinitions, cellIndices, cellNumber, globalDiagnosticVariables, initialisation, month, marineCell);

            // Generate the intial live outputs
            if (LiveOutputs)
            {
                InitialLiveOutputs(ecosystemModelGrid, marineCell);
            }
            // Generate the intial file outputs
            InitialFileOutputs(ecosystemModelGrid, cohortFunctionalGroupDefinitions, marineCell,cellIndices,cellNumber);
        }
        /// <summary>
        /// Calculates the probability of advective dispersal given the grid cell
        /// </summary>
        /// <param name="madingleyGrid">The model grid</param>
        /// <param name="latIndex">The latitude index of the grid cell to check for dispersal</param>
        /// <param name="lonIndex">The longitude index of the grid cell to check for dispersal</param>
        /// <param name="currentMonth">The current model month</param>
        /// <returns>A six element array.
        /// The first element is the probability of dispersal.
        /// The second element is the probability of dispersing in the u (longitudinal) direction
        /// The third element is the probability of dispersing in the v (latitudinal) direction
        /// The fourth element is the probability of dispersing in the diagonal direction
        /// The fifth element is the distance travelled in the u direction (u velocity modified by the random diffusion component)
        /// The sixth element is the distance travelled in the v direction (v velocity modified by the random diffusion component)
        /// Note that the second, third, and fourth elements are always positive; thus, they do not indicate 'direction' in terms of dispersal.</returns>
        private double[] CalculateDispersalProbability(ModelGrid madingleyGrid, uint latIndex, uint lonIndex, uint currentMonth, double rescaleduSpeed, double rescaledvSpeed)
        {
            // Distance travelled in u (longitudinal) direction
            double uDistanceTravelled;

            // Distance travelled in v (latitudinal) direction
            double vDistanceTravelled;

            // U and V components of the diffusive velocity
            double[] DiffusiveUandVComponents = new double[2];

            // Length in km of a cell boundary latitudinally
            double LatCellLength;

            // Length in km of a cell boundary longitudinally
            double LonCellLength;

            // Area of the grid cell that is outside in the diagonal direction after dispersal, in kilometres squared
            double AreaOutsideBoth;

            // Area of the grid cell that is  outside in the u (longitudinal) direction after dispersal, in kilometres squared
            double AreaOutsideU;

            // Area of the grid cell that is  outside in the v (latitudinal) direction after dispersal, in kilometres squared
            double AreaOutsideV;

            // Cell area, in kilometres squared
            double CellArea;

            // Probability of dispersal
            double DispersalProbability;

            // Calculate the diffusive movement speed, with a direction chosen at random
            DiffusiveUandVComponents = CalculateDiffusion();

            // Calculate the distance travelled in this dispersal (not global) time step. both advective and diffusive speeds need to have been converted to km / advective model time step
            uDistanceTravelled = rescaleduSpeed + DiffusiveUandVComponents[0];
            vDistanceTravelled = rescaledvSpeed + DiffusiveUandVComponents[1];

            // Check that the u distance travelled and v distance travelled are not greater than the cell length
            LatCellLength = madingleyGrid.CellHeightsKm[latIndex];
            LonCellLength = madingleyGrid.CellWidthsKm[latIndex];

            if (Math.Abs(uDistanceTravelled) >= LonCellLength)
            {
                Debug.Fail("u velocity greater than cell width");
            }
            if (Math.Abs(vDistanceTravelled) >= LatCellLength)
            {
                Debug.Fail("v velocity greater than cell width");
            }

            // We assume that the whole grid cell moves at the given velocity and calculate the area that is then outside the original grid cell location.
            // This then becomes the probability of dispersal

            // Calculate the area of the grid cell that is now outside in the diagonal direction.
            AreaOutsideBoth = Math.Abs(uDistanceTravelled * vDistanceTravelled);

            // Calculate the area of the grid cell that is now outside in the u (longitudinal) direction (not including the diagonal)
            AreaOutsideU = Math.Abs(uDistanceTravelled * LatCellLength) - AreaOutsideBoth;

            // Calculate the proportion of the grid cell that is outside in the v (latitudinal) direction (not including the diagonal)
            AreaOutsideV = Math.Abs(vDistanceTravelled * LonCellLength) - AreaOutsideBoth;

            // Get the cell area, in kilometres squared
            CellArea = madingleyGrid.GetCellEnvironment(latIndex, lonIndex)["Cell Area"][0];

            // Convert areas to a probability
            DispersalProbability = (AreaOutsideU + AreaOutsideV + AreaOutsideBoth) / CellArea;

            // Check that the whole cell hasn't moved out. Could this happen for the fastest currents in a month? Definitely,
            // if current speeds were not constrained
            if (DispersalProbability >= 1)
            {
                Debug.Fail("Dispersal probability in advection should always be <= 1");
            }

            double[] NewArray = { DispersalProbability, AreaOutsideU / CellArea, AreaOutsideV / CellArea, AreaOutsideBoth / CellArea, uDistanceTravelled, vDistanceTravelled };
            return(NewArray);
        }
        /// <summary>
        /// Write to the output file values of the output variables during the model time steps
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid to get data from</param>
        /// <param name="cohortFunctionalGroupDefinitions">The definitions of the cohort functional groups in the model</param>
        /// <param name="stockFunctionalGroupDefinitions">The definitions of the stock  functional groups in the model</param>
        /// <param name="cellIndices">List of indices of active cells in the model grid</param>
        /// <param name="cellNumber">The number of the current cell in the list of indices of active cells</param>
        /// <param name="globalDiagnosticVariables">List of global diagnostic variables</param>
        /// <param name="timeStepTimer">The timer for the current time step</param>
        /// <param name="numTimeSteps">The number of time steps in the model run</param>
        /// <param name="currentTimestep">The current model time step</param>
        /// <param name="initialisation">The Madingley Model initialisation</param>
        /// <param name="month">The current month in the model run</param>
        /// <param name="marineCell">Whether the current cell is a marine cell</param>
        public void TimeStepOutputs(ModelGrid ecosystemModelGrid, FunctionalGroupDefinitions cohortFunctionalGroupDefinitions, 
            FunctionalGroupDefinitions stockFunctionalGroupDefinitions, List<uint[]> cellIndices, int cellNumber,
            SortedList<string, double> globalDiagnosticVariables, StopWatch timeStepTimer, uint numTimeSteps, uint currentTimestep, 
            MadingleyModelInitialisation initialisation, uint month, Boolean marineCell)
        {
            // Calculate values of the output variables to be used
            CalculateOutputs(ecosystemModelGrid, cohortFunctionalGroupDefinitions, stockFunctionalGroupDefinitions, cellIndices, cellNumber, globalDiagnosticVariables, initialisation, month, marineCell);

            // Generate the live outputs for this time step
            if (LiveOutputs)
            {
                TimeStepLiveOutputs(numTimeSteps, currentTimestep, ecosystemModelGrid, marineCell);
            }

            // Generate the console outputs for the current time step
            TimeStepConsoleOutputs(currentTimestep, timeStepTimer);

            // Generate the file outputs for the current time step
            TimeStepFileOutputs(ecosystemModelGrid, cohortFunctionalGroupDefinitions, currentTimestep, marineCell, cellIndices,cellNumber);
        }
        /// <summary>
        /// Calculate trophic evenness using the Rao Index
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid</param>
        /// <param name="cellIndices">The list of indices of cells to be run in the current simulation</param>
        /// <param name="cellIndex">The index of the current cell within the list of cells to be run</param>
        /// <returns>Trophic evenness</returns>
        public double CalculateFunctionalEvennessRao(ModelGrid ecosystemModelGrid, FunctionalGroupDefinitions cohortDefinitions,
            List<uint[]> cellIndices, int cellIndex, string trait)
        {
            //Get the cohorts for the specified cell
            GridCellCohortHandler CellCohorts = ecosystemModelGrid.GetGridCellCohorts(cellIndices[cellIndex][0], cellIndices[cellIndex][1]);

            double[] EvennessValues = new double[2];

            double[,] Distances = new double[CellCohorts.GetNumberOfCohorts(), CellCohorts.GetNumberOfCohorts()];

            double[] FunctionalTrait = new double[CellCohorts.GetNumberOfCohorts()];
            double MaxModelTraitValue=0;
            double MinModelTraitValue=0;

            // Construct a vector of cohort biomass (in case we want to weight by them)
            double[] CohortTotalBiomasses = new double[CellCohorts.GetNumberOfCohorts()];

            int CohortNumberCounter = 0;
            switch (trait.ToLower())
            {
                case "biomass":
                    for (int fg = 0; fg < CellCohorts.Count; fg++)
                    {
                        foreach (Cohort c in CellCohorts[fg])
                        {

                            FunctionalTrait[CohortNumberCounter] = c.IndividualBodyMass;
                            CohortTotalBiomasses[CohortNumberCounter] = (c.IndividualBodyMass + c.IndividualReproductivePotentialMass) * c.CohortAbundance;

                            CohortNumberCounter++;
                        }
                    }

                    //Define upper and lower limits for body mass
                    MinModelTraitValue = cohortDefinitions.GetBiologicalPropertyAllFunctionalGroups("minimum mass").Min();
                    MaxModelTraitValue = cohortDefinitions.GetBiologicalPropertyAllFunctionalGroups("maximum mass").Max();
                    break;
                case "trophic index":
                    for (int fg = 0; fg < CellCohorts.Count; fg++)
                    {
                        foreach (Cohort c in CellCohorts[fg])
                        {

                            FunctionalTrait[CohortNumberCounter] = c.IndividualBodyMass;
                            CohortTotalBiomasses[CohortNumberCounter] = (c.IndividualBodyMass + c.IndividualReproductivePotentialMass) * c.CohortAbundance;

                            CohortNumberCounter++;
                        }
                    }
                    MinModelTraitValue = MinTI;
                    MaxModelTraitValue = MaxTI;
                    break;
            }

            Distances = CalculateDistanceMatrix(FunctionalTrait, MaxModelTraitValue, MinModelTraitValue);

            return RaoEntropy(Distances, CohortTotalBiomasses);
        }
        /// <summary>
        /// Calculate outputs associated with low-level outputs
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid</param>
        /// <param name="cellIndices">The list of indices of active cells in the model grid</param>
        /// <param name="cellIndex">The position of the current cell in the list of active cells</param>
        /// <param name="globalDiagnosticVariables">The global diagnostic variables for this model run</param>
        /// <param name="cohortFunctionalGroupDefinitions">The functional group definitions of cohorts in the model</param>
        /// <param name="stockFunctionalGroupDefinitions">The functional group definitions of stocks in the model</param>
        /// <param name="initialisation">The Madingley Model initialisation</param>
        /// <param name="month">The current month in the model run</param>
        /// <param name="MarineCell">Whether the current cell is a marine cell</param>
        private void CalculateLowLevelOutputs(ModelGrid ecosystemModelGrid, List<uint[]> cellIndices, int cellIndex, 
            SortedList<string,double> globalDiagnosticVariables, FunctionalGroupDefinitions cohortFunctionalGroupDefinitions,
            FunctionalGroupDefinitions stockFunctionalGroupDefinitions, MadingleyModelInitialisation initialisation, uint month, 
            Boolean MarineCell)
        {
            // Reset the total living biomass
            TotalLivingBiomass = 0.0;

            string[] Keys;

            if (MarineCell)
            {
                // Get the list of cohort trait combinations to consider
                Keys = CohortTraitIndicesMarine.Keys.ToArray();

                // Get biomass, abundance and densities for each of the trait combinations. Note that the GetStateVariableDensity function deals with the assessment of whether cohorts contain individuals
                // of low enough mass to be considered zooplankton in the marine realm
                foreach (string TraitValue in Keys)
                {
                    // Biomass density
                    TotalBiomassDensitiesOut[TraitValue] = ecosystemModelGrid.GetStateVariableDensity("Biomass", TraitValue, CohortTraitIndicesMarine[TraitValue], cellIndices[cellIndex][0], cellIndices[cellIndex][1], "cohort", initialisation) / 1000.0;

                    // Density
                    TotalDensitiesOut[TraitValue] = ecosystemModelGrid.GetStateVariableDensity("Abundance", TraitValue, CohortTraitIndicesMarine[TraitValue], cellIndices[cellIndex][0], cellIndices[cellIndex][1], "cohort", initialisation);
                }
            }
            else
            {
                // Get the list of cohort trait combinations to consider
                Keys = CohortTraitIndices.Keys.ToArray();

                // Get biomass, abundance and densities for each of the trait combinations
                foreach (string TraitValue in Keys)
                {
                    // Biomass density
                    TotalBiomassDensitiesOut[TraitValue] = ecosystemModelGrid.GetStateVariableDensity("Biomass", TraitValue, CohortTraitIndices[TraitValue], cellIndices[cellIndex][0], cellIndices[cellIndex][1], "cohort", initialisation) / 1000.0;

                    // Density
                    TotalDensitiesOut[TraitValue] = ecosystemModelGrid.GetStateVariableDensity("Abundance", TraitValue, CohortTraitIndices[TraitValue], cellIndices[cellIndex][0], cellIndices[cellIndex][1], "cohort", initialisation);
                }
            }

            // Add the total biomass of all cohorts to the total living biomass variable
            TotalLivingBiomass += ecosystemModelGrid.GetStateVariable("Biomass", "NA", cohortFunctionalGroupDefinitions.AllFunctionalGroupsIndex,
                cellIndices[cellIndex][0], cellIndices[cellIndex][1], "cohort", initialisation);
            TotalLivingBiomassDensity = ecosystemModelGrid.GetStateVariableDensity("Biomass", "NA", cohortFunctionalGroupDefinitions.AllFunctionalGroupsIndex, cellIndices[cellIndex][0], cellIndices[cellIndex][1], "cohort", initialisation) / 1000.0;
            TotalHeterotrophAbundanceDensity = ecosystemModelGrid.GetStateVariableDensity("Abundance", "NA", cohortFunctionalGroupDefinitions.AllFunctionalGroupsIndex, cellIndices[cellIndex][0], cellIndices[cellIndex][1], "cohort", initialisation);
            TotalHeterotrophBiomassDensity = TotalLivingBiomassDensity;

            if (MarineCell)
            {
                // Get the list of stock trait combinations to consider
                Keys = StockTraitIndicesMarine.Keys.ToArray();

                // Get biomass and biomass density for each of the trait combinations
                foreach (string TraitValue in Keys)
                {
                    // Density
                    TotalBiomassDensitiesOut[TraitValue] = ecosystemModelGrid.GetStateVariableDensity("Biomass", TraitValue, StockTraitIndicesMarine[TraitValue], cellIndices[cellIndex][0], cellIndices[cellIndex][1], "stock", initialisation) / 1000.0;
                }
            }
            else
            {
                // Get the list of stock trait combinations to consider
                Keys = StockTraitIndices.Keys.ToArray();

                // Get biomass and biomass density for each of the trait combinations
                foreach (string TraitValue in Keys)
                {
                    // Density
                    TotalBiomassDensitiesOut[TraitValue] = ecosystemModelGrid.GetStateVariableDensity("Biomass", TraitValue, StockTraitIndices[TraitValue], cellIndices[cellIndex][0], cellIndices[cellIndex][1], "stock", initialisation) / 1000.0;
                }
            }

            // Add the total biomass of all stocks to the total living biomass variable
            TotalLivingBiomass += ecosystemModelGrid.GetStateVariable("Biomass", "NA", stockFunctionalGroupDefinitions.AllFunctionalGroupsIndex,
                cellIndices[cellIndex][0], cellIndices[cellIndex][1], "stock", initialisation);
            TotalLivingBiomassDensity += ecosystemModelGrid.GetStateVariableDensity("Biomass", "NA", stockFunctionalGroupDefinitions.AllFunctionalGroupsIndex, cellIndices[cellIndex][0], cellIndices[cellIndex][1], "stock", initialisation) / 1000.0;

            if (TrackMarineSpecifics && MarineCell)
            {
                bool varExists;
                TotalIncomingNPP = ecosystemModelGrid.GetEnviroLayer("NPP", month, cellIndices[cellIndex][0], cellIndices[cellIndex][1], out varExists);
            }
        }
        /// <summary>
        /// Calculates the geometric community weighted mean body mass
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid</param>
        /// <param name="cellIndices">The list of indices of cells to be run in the current model simulation</param>
        /// <param name="cellIndex">The index of the current cell within the list of cells to be run</param>
        /// <returns>geometric community weighted mean body mass</returns>
        public double CalculateGeometricCommunityMeanBodyMass(ModelGrid ecosystemModelGrid, List<uint[]> cellIndices, int cellIndex)
        {
            //Get the cohorts for the specified cell
            GridCellCohortHandler CellCohorts = ecosystemModelGrid.GetGridCellCohorts(cellIndices[cellIndex][0], cellIndices[cellIndex][1]);
            double CumulativeAbundance = 0.0;
            double CumulativeLogBiomass = 0.0;

            //Retrieve the biomass
            foreach (var CohortList in CellCohorts)
            {
                foreach (Cohort c in CohortList)
                {
                    CumulativeLogBiomass += Math.Log(c.IndividualBodyMass + c.IndividualReproductivePotentialMass) * c.CohortAbundance;
                    CumulativeAbundance += c.CohortAbundance;
                }
            }

            double CWGMBM = Math.Exp(CumulativeLogBiomass / CumulativeAbundance);

            return (CWGMBM);
        }
        /// <summary>
        /// Calculates the variables to output
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid to get output data from</param>
        /// <param name="cohortFunctionalGroupDefinitions">Definitions of the cohort functional groups in the model</param>
        /// <param name="stockFunctionalGroupDefinitions">Definitions of the stock functional groups in the model</param>
        /// <param name="cellIndices">List of indices of active cells in the model grid</param>
        /// <param name="cellNumber">The number of the current cell in the list of indices of active cells</param>
        /// <param name="globalDiagnosticVariables">The sorted list of global diagnostic variables in the model</param>
        /// <param name="initialisation">The Madingley Model initialisation</param>
        /// <param name="month">The current month in the model run</param>
        /// <param name="marineCell">Whether the current cell is a marine cell</param>
        private void CalculateOutputs(ModelGrid ecosystemModelGrid, FunctionalGroupDefinitions cohortFunctionalGroupDefinitions,
            FunctionalGroupDefinitions stockFunctionalGroupDefinitions, List<uint[]> cellIndices, int cellNumber, SortedList<string, double>
            globalDiagnosticVariables, MadingleyModelInitialisation initialisation, uint month, Boolean marineCell)
        {
            // Calculate low-level outputs
            CalculateLowLevelOutputs(ecosystemModelGrid, cellIndices, cellNumber, globalDiagnosticVariables, cohortFunctionalGroupDefinitions,
                stockFunctionalGroupDefinitions, initialisation, month, marineCell);

            if (ModelOutputDetail == OutputDetailLevel.High)
            {
                // Calculate high-level outputs
                CalculateHighLevelOutputs(ecosystemModelGrid, cellIndices, cellNumber, marineCell);
            }
        }
        /// <summary>
        /// Return the distribution of biomasses among trophic level bins
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid</param>
        /// <param name="cellIndices">The list of cell indices to be run in the current model simulation</param>
        /// <param name="cellIndex">The index of the current cell in the list of cells to be run</param>
        /// <returns>The distribution of biomasses among trophic level bins</returns>
        public double[] CalculateTrophicDistribution(ModelGrid ecosystemModelGrid, List<uint[]> cellIndices, int cellIndex)
        {
            //Get the cohorts for the specified cell
            GridCellCohortHandler CellCohorts = ecosystemModelGrid.GetGridCellCohorts(cellIndices[cellIndex][0], cellIndices[cellIndex][1]);
            double[] TrophicIndexBinMasses = new double[NumberTrophicBins];
            int BinIndex;

            foreach (var CohortList in CellCohorts)
            {
                foreach (Cohort c in CohortList)
                {
                    BinIndex = _TrophicIndexBinValues.ToList().IndexOf(_TrophicIndexBinValues.Last(x => x < c.TrophicIndex));
                    TrophicIndexBinMasses[BinIndex] += (c.IndividualBodyMass + c.IndividualReproductivePotentialMass) * c.CohortAbundance;
                }
            }

            return TrophicIndexBinMasses;
        }
        /// <summary>
        /// Sets up the model grid within a Madingley model run
        /// </summary>
        /// <param name="initialisation">An instance of the model initialisation class</param> 
        /// <param name="scenarioParameters">The parameters for the scenarios to run</param>
        /// <param name="scenarioIndex">The index of the scenario that this model is to run</param>
        public void SetUpModelGrid(MadingleyModelInitialisation initialisation,
            ScenarioParameterInitialisation scenarioParameters, int scenarioIndex, int simulation)
        {
            // If the intialisation file contains a column pointing to another file of specific locations, and if this column is not blank then read the
            // file indicated
            if (SpecificLocations)
            {
                // Set up the model grid using these locations
                EcosystemModelGrid = new ModelGrid(BottomLatitude, LeftmostLongitude, TopLatitude, RightmostLongitude,
                    CellSize, CellSize, _CellList, EnviroStack, CohortFunctionalGroupDefinitions, StockFunctionalGroupDefinitions,
                    GlobalDiagnosticVariables, initialisation.TrackProcesses, SpecificLocations, RunGridCellsInParallel,GlobalModelTimeStepUnit);
            }
            else
            {
                _CellList = new List<uint[]>();
                //Switched order so we create cell list first then initialise cells using list rather than grid.

                uint NumLatCells = (uint)((TopLatitude - BottomLatitude) / CellSize);
                uint NumLonCells = (uint)((RightmostLongitude - LeftmostLongitude) / CellSize);

                // Loop over all cells in the model
                for (uint ii = 0; ii < NumLatCells; ii += 1)
                {
                    for (uint jj = 0; jj < NumLonCells; jj += 1)
                    {
                        // Define a vector to hold the pair of latitude and longitude indices for this grid cell
                        uint[] cellIndices = new uint[2];

                        // Add the latitude and longitude indices to this vector
                        cellIndices[0] = ii;
                        cellIndices[1] = jj;

                        // Add the vector to the list of all active grid cells
                        _CellList.Add(cellIndices);

                    }
                }

                EcologyTimer = new StopWatch();
                EcologyTimer.Start();

                // Set up a full model grid (i.e. not for specific locations)
                // Set up the model grid using these locations
                EcosystemModelGrid = new ModelGrid(BottomLatitude, LeftmostLongitude, TopLatitude, RightmostLongitude,
                    CellSize, CellSize, _CellList, EnviroStack, CohortFunctionalGroupDefinitions, StockFunctionalGroupDefinitions,
                    GlobalDiagnosticVariables, initialisation.TrackProcesses, SpecificLocations, RunGridCellsInParallel, GlobalModelTimeStepUnit);

                List<int> cellsToRemove = new List<int>();
                if (initialisation.RunRealm == "terrestrial")
                {
                    for (int ii = 0; ii < _CellList.Count; ii += 1)
                    {
                        if ((EcosystemModelGrid.GetCellEnvironment(_CellList[ii][0], _CellList[ii][1])["Realm"][0] == 2.0) ||
                            (EcosystemModelGrid.GetCellEnvironment(_CellList[ii][0], _CellList[ii][1])["LandSeaMask"][0] == 0.0))
                        {
                            cellsToRemove.Add(ii);
                        }
                    }
                }
                else if (initialisation.RunRealm == "marine")
                {
                    for (int ii = 0; ii < _CellList.Count; ii += 1)
                    {
                        if (EcosystemModelGrid.GetCellEnvironment(_CellList[ii][0], _CellList[ii][1])["Realm"][0] == 1.0)
                        {
                            cellsToRemove.Add(ii);
                        }
                    }
                }

                for (int ii = (cellsToRemove.Count - 1); ii >= 0; ii--)
                {
                    _CellList.RemoveAt(cellsToRemove[ii]);
                }

                EcologyTimer.Stop();
                Console.WriteLine("Time to initialise cells: {0}", EcologyTimer.GetElapsedTimeSecs());

                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("Madingley Model memory usage post grid cell seed: {0}", GC.GetTotalMemory(true) / 1E9, " (G Bytes)\n");
                Console.ForegroundColor = ConsoleColor.White;

            }

            if (initialisation.InputState)
            {
                InputModelState = new InputModelState(initialisation.ModelStatePath[simulation],
                    initialisation.ModelStateFilename[simulation],EcosystemModelGrid, _CellList);
            }

            // When the last simulation for the current scenario
            // if ((scenarioParameters.scenarioSimulationsNumber.Count == 1) && (scenarioIndex == scenarioParameters.scenarioSimulationsNumber[scenarioIndex] - 1)) EnviroStack.Clear();
            // Seed stocks and cohorts in the grid cells
            // If input state from output from a previous simulation
            if (initialisation.InputState)
            {
                // Seed grid cell cohort and stocks
                EcosystemModelGrid.SeedGridCellStocksAndCohorts(_CellList, InputModelState, CohortFunctionalGroupDefinitions, StockFunctionalGroupDefinitions);

                //remove cohorts that do not contain any biomass
                foreach (uint[] CellPair in _CellList)
                {

                    GridCellCohortHandler workingGridCellCohorts = EcosystemModelGrid.GetGridCellCohorts(CellPair[0], CellPair[1]);

                    for (int kk = 0; kk < CohortFunctionalGroupDefinitions.GetNumberOfFunctionalGroups(); kk++)
                    {
                        // Loop through each cohort in the functional group
                        for (int ll = (workingGridCellCohorts[kk].Count - 1); ll >= 0; ll--)
                        {
                            // If cohort abundance is less than the extinction threshold then add to the list for extinction
                            if (workingGridCellCohorts[kk][ll].CohortAbundance.CompareTo(0) <= 0 || workingGridCellCohorts[kk][ll].IndividualBodyMass.CompareTo(0.0) == 0)
                            {
                                // Remove the extinct cohort from the list of cohorts
                                workingGridCellCohorts[kk].RemoveAt(ll);
                            }
                        }

                    }
                }

            }
            else
            {
                EcosystemModelGrid.SeedGridCellStocksAndCohorts(_CellList, CohortFunctionalGroupDefinitions, StockFunctionalGroupDefinitions,
                    GlobalDiagnosticVariables, ref NextCohortID, InitialisationFileStrings["OutputDetail"] == "high", DrawRandomly,
                    initialisation.DispersalOnly, InitialisationFileStrings["DispersalOnlyType"], RunGridCellsInParallel);
            }

            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("Madingley Model memory usage pre Collect: {0}", Math.Round(GC.GetTotalMemory(true) / 1E9, 2), " (GBytes)");
            Console.ForegroundColor = ConsoleColor.White;
            GC.Collect();

            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("Madingley Model memory usage post Collect: {0}", Math.Round(GC.GetTotalMemory(true) / 1E9, 5), " (GBytes)\n");
            Console.ForegroundColor = ConsoleColor.White;
        }
Beispiel #43
0
        /// <summary>
        /// Record dispersal events in the dispersal tracker
        /// </summary>
        /// <param name="inboundCohorts">The cohorts arriving in a cell in the current time step</param>
        /// <param name="outboundCohorts">The cohorts leaving a cell in the current time step</param>
        /// <param name="outboundCohortWeights">The body masses of cohorts leaving the cell in the current time step</param>
        /// <param name="timestep">The current model time step</param>
        /// <param name="madingleyModelGrid">The model grid</param>
        public void RecordDispersalForACell(uint[, ,] inboundCohorts, uint[, ,] outboundCohorts, List <double>[,] outboundCohortWeights, uint timestep, ModelGrid madingleyModelGrid)
        {
            var count = inboundCohorts.GetLength(0) * inboundCohorts.GetLength(1);

            var copy = new Madingley.Common.GridCellDispersal[count];

            for (var kk = 0; kk < count; kk++)
            {
                var ii = (int)kk / inboundCohorts.GetLength(1);
                var jj = kk % inboundCohorts.GetLength(1);

                var denter =
                    new Tuple <Madingley.Common.CohortsEnterDirection, int>[]
                {
                    Tuple.Create(Madingley.Common.CohortsEnterDirection.North, (int)inboundCohorts[ii, jj, 0]),
                    Tuple.Create(Madingley.Common.CohortsEnterDirection.NorthEast, (int)inboundCohorts[ii, jj, 1]),
                    Tuple.Create(Madingley.Common.CohortsEnterDirection.East, (int)inboundCohorts[ii, jj, 2]),
                    Tuple.Create(Madingley.Common.CohortsEnterDirection.SouthEast, (int)inboundCohorts[ii, jj, 3]),
                    Tuple.Create(Madingley.Common.CohortsEnterDirection.South, (int)inboundCohorts[ii, jj, 4]),
                    Tuple.Create(Madingley.Common.CohortsEnterDirection.SouthWest, (int)inboundCohorts[ii, jj, 5]),
                    Tuple.Create(Madingley.Common.CohortsEnterDirection.West, (int)inboundCohorts[ii, jj, 6]),
                    Tuple.Create(Madingley.Common.CohortsEnterDirection.NorthWest, (int)inboundCohorts[ii, jj, 7]),
                };

                var enter = denter.ToDictionary(l => l.Item1, l => l.Item2);

                var dexit =
                    new Tuple <Madingley.Common.CohortsExitDirection, int>[]
                {
                    Tuple.Create(Madingley.Common.CohortsExitDirection.North, (int)outboundCohorts[ii, jj, 0]),
                    Tuple.Create(Madingley.Common.CohortsExitDirection.NorthEast, (int)outboundCohorts[ii, jj, 1]),
                    Tuple.Create(Madingley.Common.CohortsExitDirection.East, (int)outboundCohorts[ii, jj, 2]),
                    Tuple.Create(Madingley.Common.CohortsExitDirection.SouthEast, (int)outboundCohorts[ii, jj, 3]),
                    Tuple.Create(Madingley.Common.CohortsExitDirection.South, (int)outboundCohorts[ii, jj, 4]),
                    Tuple.Create(Madingley.Common.CohortsExitDirection.SouthWest, (int)outboundCohorts[ii, jj, 5]),
                    Tuple.Create(Madingley.Common.CohortsExitDirection.West, (int)outboundCohorts[ii, jj, 6]),
                    Tuple.Create(Madingley.Common.CohortsExitDirection.NorthWest, (int)outboundCohorts[ii, jj, 7]),
                };

                var exit = dexit.ToDictionary(l => l.Item1, l => l.Item2);

                var weights = outboundCohortWeights[ii, jj].ToArray();

                var cell = madingleyModelGrid.GetGridCell((uint)ii, (uint)jj);

                var ccell = Converters.ConvertCellData(cell);

                copy[kk] = new Madingley.Common.GridCellDispersal(enter, exit, weights, ccell);
            }

            this.GridCellDispersals = copy;
        }
Beispiel #44
0
        public void SetupOutputs(ModelGrid ecosystemModelGrid, string outputFilesSuffix, uint numTimeSteps,
                                 FunctionalGroupDefinitions cohortFunctionalGroupDefinitions, FunctionalGroupDefinitions
                                 stockFunctionalGroupDefinitions)
        {
            // Create an SDS object to hold grid outputs
            GridOutput = SDSCreator.CreateSDS("netCDF", "GridOutputs" + outputFilesSuffix, _OutputPath);

            // Initilalise trait-based outputs
            InitialiseTraitBasedOutputs(cohortFunctionalGroupDefinitions, stockFunctionalGroupDefinitions);

            // Create vector to hold the values of the time dimension
            TimeSteps = new float[numTimeSteps + 1];

            // Set the first value to be 0 (this will hold initial outputs)
            TimeSteps[0] = 0;

            // Fill other values from 0 (this will hold outputs during the model run)
            for (int i = 1; i < numTimeSteps + 1; i++)
            {
                TimeSteps[i] = i;
            }

            // Declare vectors for geographical dimension data
            float[] outLats = new float[ecosystemModelGrid.NumLatCells];
            float[] outLons = new float[ecosystemModelGrid.NumLonCells];

            // Populate the dimension variable vectors with cell centre latitude and longitudes
            for (int i = 0; i < ecosystemModelGrid.NumLatCells; i++)
            {
                outLats[i] = ecosystemModelGrid.Lats[i] + (ecosystemModelGrid.LatCellSize / 2);
            }

            for (int jj = 0; jj < ecosystemModelGrid.NumLonCells; jj++)
            {
                outLons[jj] = ecosystemModelGrid.Lons[jj] + (ecosystemModelGrid.LonCellSize / 2);
            }

            //GridOutputArray = new double[ecosystemModelGrid.NumLatCells, ecosystemModelGrid.NumLonCells, numTimeSteps];

            // Add output variables that are dimensioned geographically and temporally to grid output file
            string[] GeographicalDimensions = { "Latitude", "Longitude", "Time step" };
            DataConverter.AddVariable(GridOutput, "Biomass density", 3, GeographicalDimensions, 0, outLats, outLons, TimeSteps);
            DataConverter.AddVariable(GridOutput, "Abundance density", 3, GeographicalDimensions, 0, outLats, outLons, TimeSteps);

            // Initialise the arrays that will be used for the grid-based outputs
            LogBiomassDensityGridCohorts   = new double[ecosystemModelGrid.NumLatCells, ecosystemModelGrid.NumLonCells];
            LogBiomassDensityGridStocks    = new double[ecosystemModelGrid.NumLatCells, ecosystemModelGrid.NumLonCells];
            LogBiomassDensityGrid          = new double[ecosystemModelGrid.NumLatCells, ecosystemModelGrid.NumLonCells];
            LogAbundanceDensityGridCohorts = new double[ecosystemModelGrid.NumLatCells, ecosystemModelGrid.NumLonCells];

            FrostDays     = new double[ecosystemModelGrid.NumLatCells, ecosystemModelGrid.NumLonCells];
            FracEvergreen = new double[ecosystemModelGrid.NumLatCells, ecosystemModelGrid.NumLonCells];
            Realm         = new double[ecosystemModelGrid.NumLatCells, ecosystemModelGrid.NumLonCells];
            HANPP         = new double[ecosystemModelGrid.NumLatCells, ecosystemModelGrid.NumLonCells];
            Temperature   = new double[ecosystemModelGrid.NumLatCells, ecosystemModelGrid.NumLonCells];

            // Temporary outputs for checking plant model
            DataConverter.AddVariable(GridOutput, "Fraction year frost", 3, GeographicalDimensions, ecosystemModelGrid.GlobalMissingValue, outLats, outLons, TimeSteps);
            DataConverter.AddVariable(GridOutput, "Fraction evergreen", 3, GeographicalDimensions, ecosystemModelGrid.GlobalMissingValue, outLats, outLons, TimeSteps);
            DataConverter.AddVariable(GridOutput, "Realm", 3, GeographicalDimensions, ecosystemModelGrid.GlobalMissingValue, outLats, outLons, TimeSteps);
            DataConverter.AddVariable(GridOutput, "HANPP", 3, GeographicalDimensions, ecosystemModelGrid.GlobalMissingValue, outLats, outLons, TimeSteps);
            DataConverter.AddVariable(GridOutput, "Temperature", 3, GeographicalDimensions, ecosystemModelGrid.GlobalMissingValue, outLats, outLons, TimeSteps);

            // Set up outputs for medium or high output levels
            if ((ModelOutputDetail == OutputDetailLevel.Medium) || (ModelOutputDetail == OutputDetailLevel.High))
            {
                double[,] temp = new double[ecosystemModelGrid.NumLatCells, ecosystemModelGrid.NumLonCells];
                foreach (string TraitValue in CohortTraitIndices.Keys)
                {
                    BiomassDensityGrid.Add(TraitValue, temp);
                    DataConverter.AddVariable(GridOutput, TraitValue + "biomass density", 3, GeographicalDimensions, 0, outLats, outLons, TimeSteps);
                    AbundanceDensityGrid.Add(TraitValue, temp);
                    DataConverter.AddVariable(GridOutput, TraitValue + "abundance density", 3, GeographicalDimensions, 0, outLats, outLons, TimeSteps);
                }

                foreach (string TraitValue in StockTraitIndices.Keys)
                {
                    BiomassDensityGrid.Add(TraitValue, temp);
                    DataConverter.AddVariable(GridOutput, TraitValue + "biomass density", 3, GeographicalDimensions, 0, outLats, outLons, TimeSteps);
                }


                if (OutputMetrics)
                {
                    double[,] MTL = new double[ecosystemModelGrid.NumLatCells, ecosystemModelGrid.NumLonCells];
                    MetricsGrid.Add("Mean Trophic Level", MTL);

                    double[,] TE = new double[ecosystemModelGrid.NumLatCells, ecosystemModelGrid.NumLonCells];
                    MetricsGrid.Add("Trophic Evenness", TE);

                    double[,] BE = new double[ecosystemModelGrid.NumLatCells, ecosystemModelGrid.NumLonCells];
                    MetricsGrid.Add("Biomass Evenness", BE);

                    double[,] FR = new double[ecosystemModelGrid.NumLatCells, ecosystemModelGrid.NumLonCells];
                    MetricsGrid.Add("Functional Richness", FR);

                    double[,] RFE = new double[ecosystemModelGrid.NumLatCells, ecosystemModelGrid.NumLonCells];
                    MetricsGrid.Add("Rao Functional Evenness", RFE);

                    double[,] BR = new double[ecosystemModelGrid.NumLatCells, ecosystemModelGrid.NumLonCells];
                    MetricsGrid.Add("Biomass Richness", BR);

                    double[,] TR = new double[ecosystemModelGrid.NumLatCells, ecosystemModelGrid.NumLonCells];
                    MetricsGrid.Add("Trophic Richness", TR);

                    double[,] Bmax = new double[ecosystemModelGrid.NumLatCells, ecosystemModelGrid.NumLonCells];
                    MetricsGrid.Add("Max Bodymass", Bmax);

                    double[,] Bmin = new double[ecosystemModelGrid.NumLatCells, ecosystemModelGrid.NumLonCells];
                    MetricsGrid.Add("Min Bodymass", Bmin);

                    double[,] TImax = new double[ecosystemModelGrid.NumLatCells, ecosystemModelGrid.NumLonCells];
                    MetricsGrid.Add("Max Trophic Index", TImax);

                    double[,] TImin = new double[ecosystemModelGrid.NumLatCells, ecosystemModelGrid.NumLonCells];
                    MetricsGrid.Add("Min Trophic Index", TImin);

                    double[,] ArithMean = new double[ecosystemModelGrid.NumLatCells, ecosystemModelGrid.NumLonCells];
                    MetricsGrid.Add("Arithmetic Mean Bodymass", ArithMean);

                    double[,] GeomMean = new double[ecosystemModelGrid.NumLatCells, ecosystemModelGrid.NumLonCells];
                    MetricsGrid.Add("Geometric Mean Bodymass", GeomMean);

                    DataConverter.AddVariable(GridOutput, "Mean Trophic Level", 3, GeographicalDimensions, 0, outLats, outLons, TimeSteps);
                    DataConverter.AddVariable(GridOutput, "Trophic Evenness", 3, GeographicalDimensions, 0, outLats, outLons, TimeSteps);
                    DataConverter.AddVariable(GridOutput, "Biomass Evenness", 3, GeographicalDimensions, 0, outLats, outLons, TimeSteps);
                    DataConverter.AddVariable(GridOutput, "Functional Richness", 3, GeographicalDimensions, 0, outLats, outLons, TimeSteps);
                    DataConverter.AddVariable(GridOutput, "Rao Functional Evenness", 3, GeographicalDimensions, 0, outLats, outLons, TimeSteps);
                    DataConverter.AddVariable(GridOutput, "Biomass Richness", 3, GeographicalDimensions, 0, outLats, outLons, TimeSteps);
                    DataConverter.AddVariable(GridOutput, "Trophic Richness", 3, GeographicalDimensions, 0, outLats, outLons, TimeSteps);
                    DataConverter.AddVariable(GridOutput, "Max Bodymass", 3, GeographicalDimensions, 0, outLats, outLons, TimeSteps);
                    DataConverter.AddVariable(GridOutput, "Min Bodymass", 3, GeographicalDimensions, 0, outLats, outLons, TimeSteps);
                    DataConverter.AddVariable(GridOutput, "Max Trophic Index", 3, GeographicalDimensions, 0, outLats, outLons, TimeSteps);
                    DataConverter.AddVariable(GridOutput, "Min Trophic Index", 3, GeographicalDimensions, 0, outLats, outLons, TimeSteps);
                    DataConverter.AddVariable(GridOutput, "Arithmetic Mean Bodymass", 3, GeographicalDimensions, 0, outLats, outLons, TimeSteps);
                    DataConverter.AddVariable(GridOutput, "Geometric Mean Bodymass", 3, GeographicalDimensions, 0, outLats, outLons, TimeSteps);
                }
            }
        }
        /// <summary>
        /// Sets up the outputs associated with the high level of output detail
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid</param>
        /// <param name="cellIndices">The indices of active cells in the model grid</param>
        /// <param name="cellNumber">The index of the current cell in the list of active cells</param>
        /// <param name="cohortFunctionalGroupDefinitions">The functional group definitions for cohorts in the model</param>
        /// <param name="marineCell">Whether the current cell is a marine cell</param>
        private void SetUpHighLevelOutputs(ModelGrid ecosystemModelGrid, List<uint[]> cellIndices, int cellNumber,
            FunctionalGroupDefinitions cohortFunctionalGroupDefinitions, Boolean marineCell)
        {
            // Create an SDS object for outputs by mass bin
            // MassBinsOutput = SDSCreator.CreateSDS("netCDF", "MassBins" + _OutputSuffix, _OutputPath);
            MassBinsOutputMemory = SDSCreator.CreateSDSInMemory(true);

            // Add relevant output variables to the mass bin output file
            string[] MassBinDimensions = { "Time step", "Mass bin" };
            string[] DoubleMassBinDimensions = new string[] { "Adult Mass bin", "Juvenile Mass bin", "Time step" };

            if (OutputMetrics)
            {
                DataConverter.AddVariable(MassBinsOutputMemory, "Trophic Index Distribution", 2, new string[] {"Time step","Trophic Index Bins"}, ecosystemModelGrid.GlobalMissingValue, TimeSteps, Metrics.TrophicIndexBinValues);
            }

            if (marineCell)
            {
                foreach (string TraitValue in CohortTraitIndicesMarine.Keys)
                {
                    DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValue + " abundance in mass bins", 2, MassBinDimensions, ecosystemModelGrid.GlobalMissingValue, TimeSteps, MassBins);
                    DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValue + " biomass in mass bins", 2, MassBinDimensions, ecosystemModelGrid.GlobalMissingValue, TimeSteps, MassBins);
                    DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValue + " abundance in juvenile vs adult bins", 3, DoubleMassBinDimensions, ecosystemModelGrid.GlobalMissingValue, MassBins, MassBins, TimeSteps);
                    DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValue + " biomass in juvenile vs adult bins", 3, DoubleMassBinDimensions, ecosystemModelGrid.GlobalMissingValue, MassBins, MassBins, TimeSteps);
                }
            }
            else
            {
                foreach (string TraitValue in CohortTraitIndices.Keys)
                {
                    DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValue + " abundance in mass bins", 2, MassBinDimensions, ecosystemModelGrid.GlobalMissingValue, TimeSteps, MassBins);
                    DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValue + " biomass in mass bins", 2, MassBinDimensions, ecosystemModelGrid.GlobalMissingValue, TimeSteps, MassBins);
                    DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValue + " abundance in juvenile vs adult bins", 3, DoubleMassBinDimensions, ecosystemModelGrid.GlobalMissingValue, MassBins, MassBins, TimeSteps);
                    DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValue + " biomass in juvenile vs adult bins", 3, DoubleMassBinDimensions, ecosystemModelGrid.GlobalMissingValue, MassBins, MassBins, TimeSteps);
                }
            }

            // Create an SDS object in memory for tracked cohorts outputs
            // TrackedCohortsOutput = SDSCreator.CreateSDS("netCDF", "TrackedCohorts" + _OutputSuffix, _OutputPath);
            TrackedCohortsOutputMemory = SDSCreator.CreateSDSInMemory(true);

            // Initialise list to hold tracked cohorts
            TrackedCohorts = new List<uint>();

            // Identify cohorts to track
            GridCellCohortHandler TempCohorts = null;
            bool FoundCohorts = false;

            // Get a local copy of the cohorts in the grid cell
            TempCohorts = ecosystemModelGrid.GetGridCellCohorts(cellIndices[cellNumber][0], cellIndices[cellNumber][1]);

            // Loop over functional groups and check whether any cohorts exist in this grid cell
            foreach (var CohortList in TempCohorts)
            {
                if (CohortList.Count > 0)
                {
                    FoundCohorts = true;
                    break;
                }
            }

            // If there are some cohorts in the grid cell, then setup the tracked cohorts
            if (FoundCohorts)
            {
                // Initialise stream writer to hold details of tracked cohorts
                StreamWriter sw = new StreamWriter(_OutputPath + "TrackedCohortProperties" + _OutputSuffix + ".txt");
                sw.WriteLine("Output ID\tCohort ID\tFunctional group index\tNutrition source\tDiet\tRealm\tMobility\tJuvenile mass\tAdult mass");

                // Counter for tracked cohorts
                int TrackedCohortCounter = 0;

                for (int i = 0; i < TempCohorts.Count; i++)
                {
                    if (TempCohorts[i].Count > 0)
                    {
                        for (int j = 0; j < TempCohorts[i].Count; j++)
                        {
                            // Write out properties of the selected cohort
                            sw.WriteLine(Convert.ToString(TrackedCohortCounter) + '\t' + Convert.ToString(TempCohorts[i][j].CohortID[0]) + '\t' + i + '\t' +
                                cohortFunctionalGroupDefinitions.GetTraitNames("Nutrition source", i) + '\t' + cohortFunctionalGroupDefinitions.
                                GetTraitNames("Diet", i) + '\t' + cohortFunctionalGroupDefinitions.GetTraitNames("Realm", i) + '\t' +
                                cohortFunctionalGroupDefinitions.GetTraitNames("Mobility", i) + '\t' + TempCohorts[i][j].JuvenileMass + '\t' +
                                TempCohorts[i][j].AdultMass);

                            // Add the ID of the cohort to the list of tracked cohorts
                            TrackedCohorts.Add(TempCohorts[i][j].CohortID[0]);

                            // Increment the counter of tracked cohorts
                            TrackedCohortCounter++;
                        }
                    }
                }

                // Generate an array of floating points to index the tracked cohorts in the output file
                float[] OutTrackedCohortIDs = new float[TrackedCohortCounter];
                for (int i = 0; i < TrackedCohortCounter; i++)
                {
                    OutTrackedCohortIDs[i] = i;
                }

                // Set up outputs for tracked cohorts
                string[] TrackedCohortsDimensions = { "Time step", "Cohort ID" };

                // Add output variables for the tracked cohorts output
                DataConverter.AddVariable(TrackedCohortsOutputMemory, "Individual body mass", 2, TrackedCohortsDimensions,
                ecosystemModelGrid.GlobalMissingValue, TimeSteps, OutTrackedCohortIDs);
                DataConverter.AddVariable(TrackedCohortsOutputMemory, "Number of individuals", 2, TrackedCohortsDimensions,
                ecosystemModelGrid.GlobalMissingValue, TimeSteps, OutTrackedCohortIDs);

                // Dispose of the streamwriter
                sw.Dispose();
            }

                // Get a list of all possible combinations of trait values as a jagged array
            string[][] TraitValueSearch;

                if (marineCell)
                    TraitValueSearch = CalculateAllCombinations(CohortTraitValuesMarine[CohortTraits[0]], CohortTraitValuesMarine[CohortTraits[1]]);
                else
                    TraitValueSearch = CalculateAllCombinations(CohortTraitValues[CohortTraits[0]], CohortTraitValues[CohortTraits[1]]);

                // Add the functional group indices of these trait combinations to the list of indices of the trait values to consider,
                // keyed with a concatenated version of the trait values
                string TraitValueJoin = "";
                string[] TimeDimension = { "Time step" };
                for (int i = 0; i < TraitValueSearch.Count(); i++)
                {
                    TraitValueJoin = "";
                    foreach (string TraitValue in TraitValueSearch[i])
                    {
                        TraitValueJoin += TraitValue + " ";
                    }

                    if (marineCell)
                    {
                        // Only add indices of marine functional groups
                        int[] TempIndices = cohortFunctionalGroupDefinitions.GetFunctionalGroupIndex(CohortTraits, TraitValueSearch[i], true);
                        Boolean[] TempIndices2 = new Boolean[TempIndices.GetLength(0)];
                        for (int ii = 0; ii < TempIndices.GetLength(0); ii++)
                        {
                            if (cohortFunctionalGroupDefinitions.GetTraitNames("Realm", TempIndices[ii]).Equals("Marine", StringComparison.OrdinalIgnoreCase))
                            {
                                TempIndices2[ii] = true;
                            }
                        }

                        // Extract only the indices which are marine
                        int[] TempIndices3 = Enumerable.Range(0, TempIndices2.Length).Where(zz => TempIndices2[zz]).ToArray();

                        if (TempIndices3.Length > 0)
                        {
                            // Extract the values at these indices
                            for (int ii = 0; ii < TempIndices3.Length; ii++)
                            {
                                TempIndices3[ii] = TempIndices[TempIndices3[ii]];
                            }

                            // Add in the indices for this functional group and this realm
                            CohortTraitIndices.Add(TraitValueJoin, TempIndices3);

                            DataConverter.AddVariable(BasicOutputMemory, TraitValueJoin + " density", "Individuals / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                            DataConverter.AddVariable(BasicOutputMemory, TraitValueJoin + " biomass density", "Kg / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                            DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValueJoin + " abundance in mass bins", 2, MassBinDimensions, ecosystemModelGrid.GlobalMissingValue, TimeSteps, MassBins);
                            DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValueJoin + " biomass in mass bins", 2, MassBinDimensions, ecosystemModelGrid.GlobalMissingValue, TimeSteps, MassBins);
                            DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValueJoin + " abundance in juvenile vs adult bins", 3, DoubleMassBinDimensions, ecosystemModelGrid.GlobalMissingValue, MassBins, MassBins, TimeSteps);
                            DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValueJoin + " biomass in juvenile vs adult bins", 3, DoubleMassBinDimensions, ecosystemModelGrid.GlobalMissingValue, MassBins, MassBins, TimeSteps);

                            TotalBiomassDensitiesOut.Add(TraitValueJoin, 0.0);
                            TotalDensitiesOut.Add(TraitValueJoin, 0.0);
                        }
                    }
                    else
                    {
                        // Only add indices of terrestrial functional groups
                        int[] TempIndices = cohortFunctionalGroupDefinitions.GetFunctionalGroupIndex(CohortTraits, TraitValueSearch[i], true);
                        Boolean[] TempIndices2 = new Boolean[TempIndices.GetLength(0)];
                        for (int ii = 0; ii < TempIndices.GetLength(0); ii++)
                        {
                            if (cohortFunctionalGroupDefinitions.GetTraitNames("Realm", TempIndices[ii]).Equals("Terrestrial", StringComparison.OrdinalIgnoreCase))
                            {
                                TempIndices2[ii] = true;
                            }
                        }

                        // Extract only the indices which are terrestrial
                        int[] TempIndices3 = Enumerable.Range(0, TempIndices2.Length).Where(zz => TempIndices2[zz]).ToArray();

                        if (TempIndices3.Length > 0)
                        {
                            // Extract the values at these indices
                            for (int ii = 0; ii < TempIndices3.Length; ii++)
                            {
                                TempIndices3[ii] = TempIndices[TempIndices3[ii]];
                            }

                            // Add in the indices for this functional group and this realm
                            CohortTraitIndices.Add(TraitValueJoin, TempIndices3);

                            DataConverter.AddVariable(BasicOutputMemory, TraitValueJoin + " density", "Individuals / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                            DataConverter.AddVariable(BasicOutputMemory, TraitValueJoin + " biomass density", "Kg / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                            DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValueJoin + " abundance in mass bins", 2, MassBinDimensions, ecosystemModelGrid.GlobalMissingValue, TimeSteps, MassBins);
                            DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValueJoin + " biomass in mass bins", 2, MassBinDimensions, ecosystemModelGrid.GlobalMissingValue, TimeSteps, MassBins);
                            DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValueJoin + " abundance in juvenile vs adult bins", 3, DoubleMassBinDimensions, ecosystemModelGrid.GlobalMissingValue, MassBins, MassBins, TimeSteps);
                            DataConverter.AddVariable(MassBinsOutputMemory, "Log " + TraitValueJoin + " biomass in juvenile vs adult bins", 3, DoubleMassBinDimensions, ecosystemModelGrid.GlobalMissingValue, MassBins, MassBins, TimeSteps);

                            TotalBiomassDensitiesOut.Add(TraitValueJoin, 0.0);
                            TotalDensitiesOut.Add(TraitValueJoin, 0.0);
                        }
                    }
                }
        }
Beispiel #46
0
        /// <summary>
        /// Record dispersal events in the dispersal tracker
        /// </summary>
        /// <param name="inboundCohorts">The cohorts arriving in a grid cell in the current time step</param>
        /// <param name="outboundCohorts">The cohorts leaving a ce  ll in the current time step</param>
        /// <param name="outboundCohortWeights">The body masses of cohorts leaving the cell in the current time step</param>
        /// <param name="currentTimeStep">The current model time step</param>
        /// <param name="madingleyModelGrid">The model grid</param>
        public void RecordDispersal(uint[, ,] inboundCohorts, uint[, ,] outboundCohorts, List <double>[,] outboundCohortWeights, uint currentTimeStep, ModelGrid madingleyModelGrid)
        {
            // Loop through cells in the grid and write out the necessary data
            for (uint ii = 0; ii < outboundCohorts.GetLength(0); ii++)
            {
                for (uint jj = 0; jj < outboundCohorts.GetLength(1); jj++)
                {
                    double MeanOutboundCohortWeight = new double();

                    // Calculate the mean weight of outbound cohorts (ignoring abundance)
                    if (outboundCohortWeights[ii, jj].Count == 0)
                    {
                        MeanOutboundCohortWeight = 0.0;
                    }
                    else
                    {
                        MeanOutboundCohortWeight = outboundCohortWeights[ii, jj].Average();
                    }

                    // Calculate the mean weight of all cohorts (ignoring abundance)
                    List <double> TempList = new List <double>();

                    GridCellCohortHandler Temp1 = madingleyModelGrid.GetGridCellCohorts(ii, jj);

                    // Loop through functional groups
                    for (int kk = 0; kk < Temp1.Count; kk++)
                    {
                        // Loop through cohorts
                        for (int hh = 0; hh < Temp1[kk].Count; hh++)
                        {
                            // Add the cohort weight to the list
                            TempList.Add(Temp1[kk][hh].IndividualBodyMass);
                        }
                    }

                    // Calculate the mean weight
                    double MeanCohortWeight = new double();
                    if (TempList.Count == 0)
                    {
                        MeanCohortWeight = 0.0;
                    }
                    else
                    {
                        MeanCohortWeight = TempList.Average();
                    }

                    string newline = Convert.ToString(currentTimeStep) + '\t' + Convert.ToString(ii) + '\t' +
                                     Convert.ToString(jj) + '\t' + Convert.ToString(madingleyModelGrid.GetCellLatitude(ii)) + '\t' +
                                     Convert.ToString(madingleyModelGrid.GetCellLongitude(jj)) + '\t' +
                                     Convert.ToString(outboundCohorts[ii, jj, 0]) + '\t' + Convert.ToString(outboundCohorts[ii, jj, 1]) + '\t' +
                                     Convert.ToString(outboundCohorts[ii, jj, 2]) + '\t' + Convert.ToString(outboundCohorts[ii, jj, 3]) + '\t' +
                                     Convert.ToString(outboundCohorts[ii, jj, 4]) + '\t' + Convert.ToString(outboundCohorts[ii, jj, 5]) + '\t' +
                                     Convert.ToString(outboundCohorts[ii, jj, 6]) + '\t' + Convert.ToString(outboundCohorts[ii, jj, 7]) + '\t' +
                                     Convert.ToString(inboundCohorts[ii, jj, 0]) + '\t' + Convert.ToString(inboundCohorts[ii, jj, 1]) + '\t' +
                                     Convert.ToString(inboundCohorts[ii, jj, 2]) + '\t' + Convert.ToString(inboundCohorts[ii, jj, 3]) + '\t' +
                                     Convert.ToString(inboundCohorts[ii, jj, 4]) + '\t' + Convert.ToString(inboundCohorts[ii, jj, 5]) + '\t' +
                                     Convert.ToString(inboundCohorts[ii, jj, 6]) + '\t' + Convert.ToString(inboundCohorts[ii, jj, 7]) + '\t' +
                                     Convert.ToString(String.Format("{0:.000000}", MeanOutboundCohortWeight) + '\t' +
                                                      Convert.ToString(String.Format("{0:.000000}", MeanCohortWeight)));

                    SyncedDispersalWriter.WriteLine(newline);
                }
            }
        }
 /// <summary>
 /// Sets up the outputs associated with all levels of output detail
 /// </summary>
 /// <param name="numTimeSteps">The number of time steps in the model run</param>
 /// <param name="ecosystemModelGrid">The model grid</param>
 private void SetUpLowLevelOutputs(uint numTimeSteps, ModelGrid ecosystemModelGrid)
 {
     // Create an SDS object to hold total abundance and biomass data
     // BasicOutput = SDSCreator.CreateSDS("netCDF", "BasicOutputs" + _OutputSuffix, _OutputPath);
     BasicOutputMemory = SDSCreator.CreateSDSInMemory(true);
     string[] TimeDimension = { "Time step" };
     DataConverter.AddVariable(BasicOutputMemory, "Total Biomass density", "Kg / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
     DataConverter.AddVariable(BasicOutputMemory, "Heterotroph Abundance density", "Individuals / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
     DataConverter.AddVariable(BasicOutputMemory, "Heterotroph Biomass density", "Kg / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
 }
        /// <summary>
        /// Calculates functional diversity of cohorts in a grid cell as functional richness and functional diveregence (using the Rao Index)
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid</param>
        /// <param name="cohortDefinitions">The functional group definitions for cohorts in the model</param>
        /// <param name="cellIndices">The list of cell indices in the current model simulation</param>
        /// <param name="cellIndex">The index of the current cell within the list of cells to run</param>
        /// <returns>A pair of values representing the functional richness and functional divergence (functional richness currently disabled!)</returns>
        public double[] CalculateFunctionalDiversity(ModelGrid ecosystemModelGrid, FunctionalGroupDefinitions cohortDefinitions, 
            List<uint[]> cellIndices, int cellIndex)
        {
            //Get the cohorts for the specified cell
            GridCellCohortHandler CellCohorts = ecosystemModelGrid.GetGridCellCohorts(cellIndices[cellIndex][0], cellIndices[cellIndex][1]);

            //Variable to hold the functional richness value for the current cohorts
            double FunctionalRichness;
            //Variable to hold the functional divergence value for the current cohorts
            double RaoFunctionalDivergence = 0.0;
            double[,] Distances= new double[CellCohorts.GetNumberOfCohorts(), CellCohorts.GetNumberOfCohorts()];

            List<string> AllTraitNames = cohortDefinitions.GetAllTraitNames().ToList();

            AllTraitNames.Remove("realm");
            AllTraitNames.Remove("heterotroph/autotroph");
            AllTraitNames.Remove("diet");
            string[] TraitNames = AllTraitNames.ToArray();

            //Define upper and lower limits for body mass
            double MinMass = cohortDefinitions.GetBiologicalPropertyAllFunctionalGroups("minimum mass").Min();
            double MaxMass = cohortDefinitions.GetBiologicalPropertyAllFunctionalGroups("maximum mass").Max();
            //Define upp and lower limits for trophic index
            double MaxTI = 40.0;
            double MinTI = 1.0;

            // Construct an array of functional trait values for each cohort
            // Rows are specific cohorts
            // Columns are the functional traits (these include different types:
            //      quantative: current mass, trophic index
            //      nominal: diet, reproductive strategy, mobility, metabolism
            Tuple<double[], string[]>[] CohortFunctionalTraits = new Tuple<double[], string[]>[CellCohorts.GetNumberOfCohorts()];
            double[] IndividualBodyMasses = new double[CellCohorts.GetNumberOfCohorts()];
            double[] TrophicIndex = new double[CellCohorts.GetNumberOfCohorts()];
            string[][] CohortNominalTraitValues= new string[TraitNames.Length][];

            for (int i = 0; i < TraitNames.Length; i++)
            {
                CohortNominalTraitValues[i] = new string[CellCohorts.GetNumberOfCohorts()];
            }

            // Construct a vector of cohort biomass (in case we want to weight by them)
            double[] CohortTotalBiomasses = new double[CellCohorts.GetNumberOfCohorts()];

            string[] TraitValues = new string[TraitNames.Length];
            double[] QuantitativeTraitValues= new double[2];
            int CohortNumberCounter = 0;
            for (int fg = 0; fg < CellCohorts.Count; fg++)
            {
                foreach (Cohort c in CellCohorts[fg])
                {
                    TraitValues = cohortDefinitions.GetTraitValues(TraitNames, fg);
                    for (int ii = 0; ii < TraitValues.Length; ii++)
                    {
                        CohortNominalTraitValues[ii][CohortNumberCounter] = TraitValues[ii];
                    }

                    IndividualBodyMasses[CohortNumberCounter] = c.IndividualBodyMass;
                    TrophicIndex[CohortNumberCounter] = c.TrophicIndex;

                    QuantitativeTraitValues[0] = c.IndividualBodyMass;
                    QuantitativeTraitValues[1] = c.TrophicIndex;

                    CohortFunctionalTraits[CohortNumberCounter] = new Tuple<double[], string[]>(QuantitativeTraitValues, TraitValues);

                    CohortTotalBiomasses[CohortNumberCounter] = (c.IndividualBodyMass + c.IndividualReproductivePotentialMass) * c.CohortAbundance;

                    CohortNumberCounter++;
                }
            }

            List<double[,]> DistanceList = new List<double[,]>();

            DistanceList.Add(CalculateDistanceMatrix(IndividualBodyMasses, MaxMass, MinMass));
            DistanceList.Add(CalculateDistanceMatrix(TrophicIndex, MaxTI, MinTI));
            foreach (string[] t in CohortNominalTraitValues)
            {
                DistanceList.Add(CalculateDistanceMatrix(t));
            }

            Distances = CalculateAggregateDistance(DistanceList);

            RaoFunctionalDivergence = RaoEntropy(Distances, CohortTotalBiomasses);

            return new double[] {0.0,RaoFunctionalDivergence};
        }
Beispiel #49
0
        public void OutputCurrentModelState(ModelGrid currentModelGrid, List <uint[]> cellIndices, uint currentTimestep)
        {
            GridCellCohortHandler TempCohorts;
            GridCellStockHandler  TempStocks;

            string context;
            string organism;

            context = Convert.ToString(currentTimestep) + "\t";

            using (var StateWriter = File.AppendText(this.FileName))
            {
                foreach (uint[] cell in cellIndices)
                {
                    context = Convert.ToString(currentTimestep) + "\t" +
                              Convert.ToString(currentModelGrid.GetCellLatitude(cell[0])) + "\t" +
                              Convert.ToString(currentModelGrid.GetCellLongitude(cell[1])) + "\t";

                    TempStocks  = currentModelGrid.GetGridCellStocks(cell[0], cell[1]);
                    TempCohorts = currentModelGrid.GetGridCellCohorts(cell[0], cell[1]);

                    foreach (List <Stock> ListS in TempStocks)
                    {
                        foreach (Stock S in ListS)
                        {
                            organism = "-999\tS" + Convert.ToString(S.FunctionalGroupIndex) + "\t" +
                                       "-999\t-999\t" + Convert.ToString(S.IndividualBodyMass) + "\t" +
                                       Convert.ToString(S.TotalBiomass / S.IndividualBodyMass) + "\t" +
                                       "-999\t-999\t-999\t-999\t-999\t-999";
                            StateWriter.WriteLine(context + organism);
                        }
                    }

                    foreach (List <Cohort> ListC in TempCohorts)
                    {
                        foreach (Cohort C in ListC)
                        {
#if true
                            var cohortIds = C.CohortID.Select(id => Convert.ToString(id));

                            organism = String.Join(";", cohortIds) + "\t" +
#else
                            organism = Convert.ToString(C.CohortID) + "\t" +
#endif
                                       Convert.ToString(C.FunctionalGroupIndex) + "\t" +
                                       Convert.ToString(C.JuvenileMass) + "\t" +
                                       Convert.ToString(C.AdultMass) + "\t" +
                                       Convert.ToString(C.IndividualBodyMass) + "\t" +
                                       Convert.ToString(C.CohortAbundance) + "\t" +
                                       Convert.ToString(C.BirthTimeStep) + "\t" +
                                       Convert.ToString(C.MaturityTimeStep) + "\t" +
                                       Convert.ToString(C.LogOptimalPreyBodySizeRatio) + "\t" +
                                       Convert.ToString(C.MaximumAchievedBodyMass) + "\t" +
                                       Convert.ToString(C.TrophicIndex) + "\t" +
                                       Convert.ToString(C.ProportionTimeActive);

                            StateWriter.WriteLine(context + organism);
                        }
                    }
                }
            }
        }
        public double[] CalculateFunctionalRichness(ModelGrid ecosystemModelGrid, FunctionalGroupDefinitions cohortDefinitions, 
            List<uint[]> cellIndices, int cellIndex, string trait)
        {
            //Get the cohorts for the specified cell
            GridCellCohortHandler CellCohorts = ecosystemModelGrid.GetGridCellCohorts(cellIndices[cellIndex][0], cellIndices[cellIndex][1]);
            double MinCurrentTraitValue = double.MaxValue;
            double MaxCurrentTraitValue = double.MinValue;
            double MinModelTraitValue = 0.0;
            double MaxModelTraitValue = 0.0;

            switch (trait.ToLower())
            {
                case "biomass":

                    foreach (var CohortList in CellCohorts)
                    {
                        foreach (var cohort in CohortList)
                        {

                            if (cohort.IndividualBodyMass < MinCurrentTraitValue) MinCurrentTraitValue = cohort.IndividualBodyMass;

                            if (cohort.IndividualBodyMass > MaxCurrentTraitValue) MaxCurrentTraitValue = cohort.IndividualBodyMass;

                        }
                    }

                    //Define upper and lower limits for body mass
                    MinModelTraitValue = cohortDefinitions.GetBiologicalPropertyAllFunctionalGroups("minimum mass").Min();
                    MaxModelTraitValue = cohortDefinitions.GetBiologicalPropertyAllFunctionalGroups("maximum mass").Max();
                    break;
                case "trophic index":
                    foreach (var CohortList in CellCohorts)
                    {
                        foreach (var cohort in CohortList)
                        {

                            if (cohort.TrophicIndex < MinCurrentTraitValue) MinCurrentTraitValue = cohort.TrophicIndex;

                            if (cohort.TrophicIndex > MaxCurrentTraitValue) MaxCurrentTraitValue = cohort.TrophicIndex;

                        }
                    }

                    //Define upper and lower limits for body mass
                    MinModelTraitValue = MinTI;
                    MaxModelTraitValue = MaxTI;

                    break;
                default:
                    Debug.Fail("Trait not recognised in calculation of ecosystem metrics: " + trait);
                    break;
            }

            Debug.Assert((MaxModelTraitValue - MinModelTraitValue) > 0.0, "Division by zero or negative model trait values in calculation of functional richness");

            double[] NewArray = {(MaxCurrentTraitValue-MinCurrentTraitValue)/(MaxModelTraitValue-MinModelTraitValue),MinCurrentTraitValue,MaxCurrentTraitValue};

            return NewArray;
        }
        /// <summary>
        /// Set up the file, screen and live outputs prior to the model run
        /// </summary>
        /// <param name="EcosystemModelGrid">The model grid that output data will be derived from</param>
        /// <param name="CohortFunctionalGroupDefinitions">The definitions for cohort functional groups</param>
        /// <param name="StockFunctionalGroupDefinitions">The definitions for stock functional groups</param>
        /// <param name="NumTimeSteps">The number of time steps in the model run</param>
        public void SetUpOutputs(ModelGrid EcosystemModelGrid, FunctionalGroupDefinitions CohortFunctionalGroupDefinitions, 
            FunctionalGroupDefinitions StockFunctionalGroupDefinitions, uint NumTimeSteps, string FileOutputs)
        {
            // Get the functional group indices of herbivore, carnivore and omnivore cohorts, and autotroph stocks
            string[] Trait = { "Nutrition source" };
            string[] Trait2 = { "Heterotroph/Autotroph" };
            string[] TraitValue1 = { "Herbivory" };
            string[] TraitValue2 = { "Carnivory" };
            string[] TraitValue3 = { "Omnivory" };
            string[] TraitValue4 = { "Autotroph" };

            HerbivoreIndices = CohortFunctionalGroupDefinitions.GetFunctionalGroupIndex(Trait, TraitValue1, false);
            CarnivoreIndices = CohortFunctionalGroupDefinitions.GetFunctionalGroupIndex(Trait, TraitValue2, false);
            OmnivoreIndices = CohortFunctionalGroupDefinitions.GetFunctionalGroupIndex(Trait, TraitValue3, false);
            AutotrophIndices = StockFunctionalGroupDefinitions.GetFunctionalGroupIndex(Trait2, TraitValue4, false);

            // Set up vectors to hold dimension data for the output variables
            float[] outLats = new float[EcosystemModelGrid.NumLatCells];
            float[] outLons = new float[EcosystemModelGrid.NumLonCells];
            float[] IdentityMassBins;

            // Populate the dimension variable vectors with cell centre latitude and longitudes
            for (int ii = 0; ii < EcosystemModelGrid.NumLatCells; ii++)
            {
                outLats[ii] = EcosystemModelGrid.Lats[ii] + (EcosystemModelGrid.LatCellSize / 2);
            }

            for (int jj = 0; jj < EcosystemModelGrid.NumLonCells; jj++)
            {
                outLons[jj] = EcosystemModelGrid.Lons[jj] + (EcosystemModelGrid.LonCellSize / 2);
            }

            // Create vector to hold the values of the time dimension
            OutTimes = new float[NumTimeSteps + 1];
            // Set the first value to be -1 (this will hold initial outputs)
            OutTimes[0] = -1;
            // Fill other values from 0 (this will hold outputs during the model run)
            for (int ii = 1; ii < NumTimeSteps + 1; ii++)
            {
                OutTimes[ii] = ii + 1;
            }

            // Set up a vector to hold (log) individual body mass bins
            OutMassBins = new float[MassBinNumber];
            IdentityMassBins = new float[MassBinNumber];

            // Get the (log) minimum and maximum possible (log) masses across all functional groups combined, start with default values of
            // Infinity and -Infinity
            float MaximumMass = -1 / 0F;
            float MinimumMass = 1 / 0F;
            foreach (int FunctionalGroupIndex in CohortFunctionalGroupDefinitions.AllFunctionalGroupsIndex)
            {
                MinimumMass = (float)Math.Min(MinimumMass, Math.Log(CohortFunctionalGroupDefinitions.GetBiologicalPropertyOneFunctionalGroup("minimum mass", FunctionalGroupIndex)));
                MaximumMass = (float)Math.Max(MaximumMass, Math.Log(CohortFunctionalGroupDefinitions.GetBiologicalPropertyOneFunctionalGroup("maximum mass", FunctionalGroupIndex)));
            }

            // Get the interval required to span the range between the minimum and maximum values in 100 steps
            float MassInterval = (MaximumMass - MinimumMass) / MassBinNumber;

            // Fill the vector of output mass bins with (log) body masses spread evenly between the minimum and maximum values
            for (int ii = 0; ii < MassBinNumber; ii++)
            {
                OutMassBins[ii] = MinimumMass + ii * MassInterval;
                IdentityMassBins[ii] = Convert.ToSingle(Math.Exp(Convert.ToDouble(OutMassBins[ii])));
            }

            // Create file for model outputs
            DataSetForFileOutput = CreateSDSObject.CreateSDS("netCDF", FileOutputs);

            // Add three-dimensional variables to output file, dimensioned by latitude, longtiude and time
            string[] dimensions3D = { "Latitude", "Longitude", "Time step" };
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Biomass density", 3, dimensions3D, 0, outLats, outLons, OutTimes);


            dimensions3D = new string[] { "Adult Mass bin", "Juvenile Mass bin", "Time step" };
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Log Carnivore abundance in juvenile vs adult bins", 3, dimensions3D,Math.Log(0), OutMassBins, OutMassBins, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Log Herbivore abundance in juvenile vs adult bins", 3, dimensions3D, Math.Log(0), OutMassBins, OutMassBins, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Log Carnivore biomass in juvenile vs adult bins", 3, dimensions3D, Math.Log(0), OutMassBins, OutMassBins, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Log Herbivore biomass in juvenile vs adult bins", 3, dimensions3D, Math.Log(0), OutMassBins, OutMassBins, OutTimes);

            // Add two-dimensional variables to output file, dimensioned by mass bins and time
            string[] dimensions2D = { "Time step", "Mass bin" };
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Log Carnivore abundance in mass bins", 2, dimensions2D, Math.Log(0), OutTimes, OutMassBins);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Log Herbivore abundance in mass bins", 2, dimensions2D, Math.Log(0), OutTimes, OutMassBins);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Log Carnivore biomass in mass bins", 2, dimensions2D, Math.Log(0), OutTimes, OutMassBins);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Log Herbivore biomass in mass bins", 2, dimensions2D, Math.Log(0), OutTimes, OutMassBins);


            // Add one-dimensional variables to the output file, dimensioned by time
            string[] dimensions1D = { "Time step" };
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Herbivore density", "Individuals / km^2", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Herbivore abundance", "Individuals", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Herbivore biomass", "Kg / km^2", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Carnivore density", "Individuals / km^2", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Carnivore abundance", "Individuals", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes); 
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Carnivore biomass", "Kg / km^2", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Omnivore density", "Individuals / km^2", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Omnivore abundance", "Individuals", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes); 
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Omnivore biomass", "Kg / km^2", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Autotroph biomass", "Kg / km^2", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Organic matter pool", "Kg / km^2", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Respiratory CO2 pool", "Kg / km^2", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Number of cohorts extinct", "", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Number of cohorts produced", "", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Number of cohorts combined", "", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Number of cohorts in model", "", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);
            ArraySDSConvert.AddVariable(DataSetForFileOutput, "Number of stocks in model", "", 1, dimensions1D, EcosystemModelGrid.GlobalMissingValue, OutTimes);

            // Add one-dimensional variables to the output file, dimensioned by mass bin index
            // To enable outputs to be visualised against mass instead of index



            // Initialise the arrays that will be used for the grid-based outputs
            LogBiomassDensityGridCohorts = new double[EcosystemModelGrid.NumLatCells, EcosystemModelGrid.NumLonCells];
            LogBiomassDensityGridStocks = new double[EcosystemModelGrid.NumLatCells, EcosystemModelGrid.NumLonCells];
            LogBiomassDensityGrid = new double[EcosystemModelGrid.NumLatCells, EcosystemModelGrid.NumLonCells];

        }
        /// <summary>
        /// Calculates the mean trophic level of all individual organisms in a grid cell
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid</param>
        /// <param name="cellIndices">The list of cell indices in the current model simulation</param>
        /// <param name="cellIndex">The index of the current cell in the list of cells to run</param>
        /// <returns>The mean trophic level of individuals in the grid cell</returns>
        public double CalculateMeanTrophicLevelCell(ModelGrid ecosystemModelGrid,List<uint[]> cellIndices, int cellIndex)
        {
            //Get the cohorts for the specified cell
            GridCellCohortHandler CellCohorts = ecosystemModelGrid.GetGridCellCohorts(cellIndices[cellIndex][0], cellIndices[cellIndex][1]);
            double BiomassWeightedTI = 0.0;
            double TotalBiomass = 0.0;
            double CohortBiomass = 0.0;

            foreach (var CohortList in CellCohorts)
            {
                foreach (Cohort c in CohortList)
                {
                    CohortBiomass = (c.IndividualBodyMass + c.IndividualReproductivePotentialMass) * c.CohortAbundance;
                    BiomassWeightedTI += CohortBiomass * c.TrophicIndex;
                    TotalBiomass += CohortBiomass;
                }
            }

            return BiomassWeightedTI/TotalBiomass;
        }
 /// <summary>
 /// Calculates the variables to output
 /// </summary>
 /// <param name="EcosystemModelGrid">The model grid to get output data from</param>
 /// <param name="CohortFunctionalGroupDefinitions">Definitions of the cohort functional groups in the model</param>
 /// <param name="StockFunctionalGroupDefinitions">Definitions of the stock functional groups in the model</param>
 /// <param name="_LatCellIndices">A vector of the latitudinal indices of live cells in the model grid</param>
 /// <param name="_LonCellIndices">A vector of the longitudinal indices of live cells in the model grid</param>
 /// <param name="GlobalDiagnosticVariables">The sorted list of global diagnostic variables in the model</param>
 public void CalculateOutputs(ModelGrid EcosystemModelGrid, FunctionalGroupDefinitions CohortFunctionalGroupDefinitions, 
     FunctionalGroupDefinitions StockFunctionalGroupDefinitions, uint[] _LatCellIndices, uint[] _LonCellIndices, SortedList<string, double> 
     GlobalDiagnosticVariables)
 {
     
     # region Get biomasses and abundances from model grid
        /// <summary>
        /// Calculates trophic evenness using the FRO Index of Mouillot et al.
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid</param>
        /// <param name="cellIndices">The list of indices of cells to be run in the current model simulation</param>
        /// <param name="cellIndex">The index of the current cell within the list of cells to be run</param>
        /// <returns>Trophic evenness</returns>
        /// <remarks>From Mouillot et al (2005) Functional regularity: a neglected aspect of functional diversity, Oecologia</remarks>
        public double CalculateTrophicEvennessFRO(ModelGrid ecosystemModelGrid, List<uint[]> cellIndices, int cellIndex)
        {
            //Get the cohorts for the specified cell
            GridCellCohortHandler CellCohorts = ecosystemModelGrid.GetGridCellCohorts(cellIndices[cellIndex][0], cellIndices[cellIndex][1]);
            List<double[]> TrophicIndexBiomassDistribution = new List<double[]>();
            double[] TIBiomass;
            double[] EW;

            foreach (var CohortList in CellCohorts)
            {
                foreach (Cohort c in CohortList)
                {
                    TIBiomass = new double[2];
                    TIBiomass[0] = c.TrophicIndex;
                    TIBiomass[1] = (c.IndividualBodyMass + c.IndividualReproductivePotentialMass) * c.CohortAbundance;
                    TrophicIndexBiomassDistribution.Add(TIBiomass);
                }
            }

            TrophicIndexBiomassDistribution = TrophicIndexBiomassDistribution.OrderBy(x => x[0]).ToList();

            //Use the Mouillot Evenness index - Functional Regularity Index or FRO
            //From Mouillot et al (2005) Functional regularity: a neglected aspect of functional diversity, Oecologia

            EW = new double[TrophicIndexBiomassDistribution.Count];
            double TotalEW = 0.0 ;

            for (int ii = 0; ii < TrophicIndexBiomassDistribution.Count-1; ii++)
            {
                EW[ii] = (TrophicIndexBiomassDistribution[ii + 1][0] - TrophicIndexBiomassDistribution[ii][0]) / (TrophicIndexBiomassDistribution[ii + 1][1] + TrophicIndexBiomassDistribution[ii][1]);
                TotalEW += EW[ii];
            }

            double FRO = 0.0;

            for (int ii = 0; ii < TrophicIndexBiomassDistribution.Count - 1; ii++)
            {
                FRO += Math.Min(EW[ii]/TotalEW,1.0/(TrophicIndexBiomassDistribution.Count-1));
            }

            return FRO;
        }
        /// <summary>
        /// Generates the intial output to the live dataset view
        /// </summary>
        /// <param name="ecosystemModelGrid">The model grid</param>
        /// <param name="marineCell">Whether the current cell is a marine cell</param>
        private void InitialLiveOutputs(ModelGrid ecosystemModelGrid, Boolean marineCell)
        {
            // Create a string holding the name of the x-axis variable
            string[] TimeDimension = { "Time step" };

            // Add the x-axis to the plots (time step)
            DataSetToViewLive.AddAxis("Time step", "Month", TimeSteps);

            // Add the relevant output variables depending on the specified level of detail
            if (ModelOutputDetail == OutputDetailLevel.Low)
            {
                // Add the variable for total living biomass
                DataConverter.AddVariable(DataSetToViewLive, "Total living biomass", "kg", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);

                // Add the initial value of total living biomass
                DataConverter.ValueToSDS1D(TotalLivingBiomass, "Total living biomass", "Time step", ecosystemModelGrid.GlobalMissingValue, DataSetToViewLive, 0);
            }
            else
            {
               if (marineCell)
                {
                    foreach (string TraitValue in CohortTraitIndicesMarine.Keys)
                    {
                        // Add in the carnivore and herbivore abundance variables
                        DataConverter.AddVariable(DataSetToViewLive, TraitValue + " density", "Individuals / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                        // Add in the initial values of carnivore and herbivore abundance
                        DataConverter.ValueToSDS1D(TotalDensitiesOut[TraitValue], TraitValue + " density", "Time step", ecosystemModelGrid.GlobalMissingValue, DataSetToViewLive, 0);
                        // Add in the carnivore and herbivore biomass variables
                        DataConverter.AddVariable(DataSetToViewLive, TraitValue + " biomass", "Kg / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                        // Add in the initial values of carnivore and herbivore abundance
                        DataConverter.ValueToSDS1D(TotalBiomassDensitiesOut[TraitValue], TraitValue + " biomass", "Time step", ecosystemModelGrid.GlobalMissingValue, DataSetToViewLive, 0);
                    }

                    foreach (string TraitValue in StockTraitIndicesMarine.Keys)
                    {
                        // Add in the stock biomass variables
                        DataConverter.AddVariable(DataSetToViewLive, TraitValue + " biomass", "Kg / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                        // Add in the initial value
                        DataConverter.ValueToSDS1D(TotalBiomassDensitiesOut[TraitValue], TraitValue + " biomass", "Time step", ecosystemModelGrid.GlobalMissingValue, DataSetToViewLive, 0);
                    }
                }
                else
                {
                    foreach (string TraitValue in CohortTraitIndices.Keys)
                    {
                        // Add in the carnivore and herbivore abundance variables
                        DataConverter.AddVariable(DataSetToViewLive, TraitValue + " density", "Individuals / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                        // Add in the initial values of carnivore and herbivore abundance
                        DataConverter.ValueToSDS1D(TotalDensitiesOut[TraitValue], TraitValue + " density", "Time step", ecosystemModelGrid.GlobalMissingValue, DataSetToViewLive, 0);
                        // Add in the carnivore and herbivore biomass variables
                        DataConverter.AddVariable(DataSetToViewLive, TraitValue + " biomass", "Kg / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                        // Add in the initial values of carnivore and herbivore abundance
                        DataConverter.ValueToSDS1D(TotalBiomassDensitiesOut[TraitValue], TraitValue + " biomass", "Time step", ecosystemModelGrid.GlobalMissingValue, DataSetToViewLive, 0);
                    }

                    foreach (string TraitValue in StockTraitIndices.Keys)
                    {
                        // Add in the stock biomass variables
                        DataConverter.AddVariable(DataSetToViewLive, TraitValue + " biomass", "Kg / km^2", 1, TimeDimension, ecosystemModelGrid.GlobalMissingValue, TimeSteps);
                        // Add in the initial value
                        DataConverter.ValueToSDS1D(TotalBiomassDensitiesOut[TraitValue], TraitValue + " biomass", "Time step", ecosystemModelGrid.GlobalMissingValue, DataSetToViewLive, 0);
                    }
                }

            }
        }