Esempio n. 1
0
        /// <summary>
        /// Calculate the thermocline depth
        /// </summary>
        /// <param name="dataset">The dataset to use</param>
        /// <param name="mixedTempDifferential">The minimum mixed temp differnetial</param>
        /// <param name="preCalculatedDensities">The set of precalculated densities</param>
        /// <param name="seasonal">Whether or not to look check that values aren't seasonal</param>
        /// <param name="minimumMetalimionSlope">The minimum metalimion slope</param>
        /// <returns></returns>
        public static Dictionary<DateTime, ThermoclineDepthDetails> CalculateThermoclineDepth(Dataset dataset, double mixedTempDifferential = 0, IEnumerable<DensitySeries> preCalculatedDensities = null, bool seasonal = false, float minimumMetalimionSlope = 0.1f)
        {
            var thermocline = new Dictionary<DateTime, ThermoclineDepthDetails>();
            var densities = (preCalculatedDensities == null) ? CalculateDensity(dataset).OrderBy(x => x.Depth).ToArray() : preCalculatedDensities.OrderBy(x => x.Depth).ToArray();

            var densityColumns = GenerateDensityColumns(densities);

            var timeStamps = densityColumns.Keys.ToArray();

            foreach (var t in timeStamps)
            {
                var thermoclineDetails = new ThermoclineDepthDetails();
                var depths = densityColumns[t].Keys.OrderBy(x => x).ToArray();
                if (depths.Length < 3) //We need at least 3 depths to calculate
                    continue;

                if (mixedTempDifferential > 0)
                {
                    var orderedSensors = dataset.Sensors.Where(x => x.SensorType == "Water_Temperature").OrderBy(x => x.Elevation).ToArray();
                    if (orderedSensors.Any())
                    {
                        var first = orderedSensors.First(x => x.CurrentState.Values.ContainsKey(t));
                        var last = orderedSensors.Last(x => x.CurrentState.Values.ContainsKey(t));

                        if (first != null && last != null)
                        {
                            if (first.CurrentState.Values[t] - last.CurrentState.Values[t] <= mixedTempDifferential)
                                continue;
                        }
                    }
                }

                var slopes = new double[depths.Length];

                for (var i = 1; i < depths.Length - 1; i++)
                {
                    slopes[i] = (densityColumns[t][depths[i + 1]] - densityColumns[t][depths[i]]) /
                                (depths[i + 1] - depths[i]);
                }

                thermoclineDetails.DrhoDz = slopes;

                var maxSlope = slopes.Max();
                var indexOfMaxium = Array.IndexOf(slopes, maxSlope);

                thermoclineDetails.ThermoclineIndex = indexOfMaxium;
                thermoclineDetails.ThermoclineDepth = (depths[indexOfMaxium] + depths[indexOfMaxium + 1]) / 2;

                if (indexOfMaxium > 1 && indexOfMaxium < depths.Length - 2)
                {
                    var sdn = -(depths[indexOfMaxium + 1] - depths[indexOfMaxium]) / (slopes[indexOfMaxium + 1] - slopes[indexOfMaxium]);
                    var sup = (depths[indexOfMaxium] - depths[indexOfMaxium - 1]) / (slopes[indexOfMaxium] - slopes[indexOfMaxium - 1]);
                    var upD = depths[indexOfMaxium];
                    var dnD = depths[indexOfMaxium + 1];

                    if (!(double.IsInfinity(sdn) || double.IsInfinity(sup) || double.IsNaN(sdn) || double.IsNaN(sup)))
                    {
                        thermoclineDetails.ThermoclineDepth = (float)(dnD * (sdn / (sdn + sup)) + upD * (sup / (sdn + sup)));
                    }
                }

                if (seasonal)
                {
                    const float minPercentageForUniqueTheroclineStep = 0.15f;

                    var minCutPoint = Math.Max(minPercentageForUniqueTheroclineStep * maxSlope, minimumMetalimionSlope);

                    var localPeaks = LocalPeaks(slopes, minCutPoint);

                    if (localPeaks.Any())
                    {
                        var indexOfSeasonallyAdjustedMaximum = Array.IndexOf(slopes, localPeaks.Last());

                        if (indexOfSeasonallyAdjustedMaximum > indexOfMaxium + 1)
                        {
                            thermoclineDetails.SeasonallyAdjustedThermoclineIndex = indexOfSeasonallyAdjustedMaximum;
                            thermoclineDetails.SeasonallyAdjustedThermoclineDepth = (depths[indexOfSeasonallyAdjustedMaximum] + depths[indexOfSeasonallyAdjustedMaximum + 1]) / 2;

                            if (indexOfSeasonallyAdjustedMaximum > 1 && indexOfSeasonallyAdjustedMaximum < depths.Length - 2)
                            {
                                var sdn = -(depths[indexOfSeasonallyAdjustedMaximum + 1] - depths[indexOfSeasonallyAdjustedMaximum]) / (slopes[indexOfSeasonallyAdjustedMaximum + 1] - slopes[indexOfSeasonallyAdjustedMaximum]);
                                var sup = (depths[indexOfSeasonallyAdjustedMaximum] - depths[indexOfSeasonallyAdjustedMaximum - 1]) / (slopes[indexOfSeasonallyAdjustedMaximum] - slopes[indexOfSeasonallyAdjustedMaximum - 1]);
                                var upD = depths[indexOfSeasonallyAdjustedMaximum];
                                var dnD = depths[indexOfSeasonallyAdjustedMaximum + 1];

                                if (!(double.IsInfinity(sdn) || double.IsInfinity(sup) || double.IsNaN(sdn) || double.IsNaN(sup)))
                                {
                                    thermoclineDetails.SeasonallyAdjustedThermoclineDepth = (float)(dnD * (sdn / (sdn + sup)) + upD * (sup / (sdn + sup)));
                                }
                            }
                        }
                        else
                        {
                            thermoclineDetails.NoSeasonalFound();
                        }
                    }
                    else
                    {
                        thermoclineDetails.NoSeasonalFound();
                    }
                }
                else
                {
                    thermoclineDetails.NoSeasonalFound();
                }

                thermocline[t] = thermoclineDetails;
            }

            return thermocline;
        }
Esempio n. 2
0
        /// <summary>
        /// Calculate the thermocline depth
        /// </summary>
        /// <param name="dataset">The dataset to use</param>
        /// <param name="mixedTempDifferential">The minimum mixed temp differnetial</param>
        /// <param name="preCalculatedDensities">The set of precalculated densities</param>
        /// <param name="seasonal">Whether or not to look check that values aren't seasonal</param>
        /// <param name="minimumMetalimionSlope">The minimum metalimion slope</param>
        /// <returns></returns>
        public static Dictionary <DateTime, ThermoclineDepthDetails> CalculateThermoclineDepth(Dataset dataset, double mixedTempDifferential = 0, IEnumerable <DensitySeries> preCalculatedDensities = null, bool seasonal = false, float minimumMetalimionSlope = 0.1f)
        {
            var thermocline = new Dictionary <DateTime, ThermoclineDepthDetails>();
            var densities   = (preCalculatedDensities == null) ? CalculateDensity(dataset).OrderBy(x => x.Depth).ToArray() : preCalculatedDensities.OrderBy(x => x.Depth).ToArray();

            var densityColumns = GenerateDensityColumns(densities);

            var timeStamps = densityColumns.Keys.ToArray();


            foreach (var t in timeStamps)
            {
                var thermoclineDetails = new ThermoclineDepthDetails();
                var depths             = densityColumns[t].Keys.OrderBy(x => x).ToArray();
                if (depths.Length < 3) //We need at least 3 depths to calculate
                {
                    continue;
                }

                if (mixedTempDifferential > 0)
                {
                    var orderedSensors = dataset.Sensors.Where(x => x.SensorType == "Water_Temperature").OrderBy(x => x.Elevation).ToArray();
                    if (orderedSensors.Any())
                    {
                        var first = orderedSensors.First(x => x.CurrentState.Values.ContainsKey(t));
                        var last  = orderedSensors.Last(x => x.CurrentState.Values.ContainsKey(t));

                        if (first != null && last != null)
                        {
                            if (first.CurrentState.Values[t] - last.CurrentState.Values[t] <= mixedTempDifferential)
                            {
                                continue;
                            }
                        }
                    }
                }

                var slopes = new double[depths.Length];

                for (var i = 1; i < depths.Length - 1; i++)
                {
                    slopes[i] = (densityColumns[t][depths[i + 1]] - densityColumns[t][depths[i]]) /
                                (depths[i + 1] - depths[i]);
                }

                thermoclineDetails.DrhoDz = slopes;

                var maxSlope      = slopes.Max();
                var indexOfMaxium = Array.IndexOf(slopes, maxSlope);

                thermoclineDetails.ThermoclineIndex = indexOfMaxium;
                thermoclineDetails.ThermoclineDepth = (depths[indexOfMaxium] + depths[indexOfMaxium + 1]) / 2;

                if (indexOfMaxium > 1 && indexOfMaxium < depths.Length - 2)
                {
                    var sdn = -(depths[indexOfMaxium + 1] - depths[indexOfMaxium]) / (slopes[indexOfMaxium + 1] - slopes[indexOfMaxium]);
                    var sup = (depths[indexOfMaxium] - depths[indexOfMaxium - 1]) / (slopes[indexOfMaxium] - slopes[indexOfMaxium - 1]);
                    var upD = depths[indexOfMaxium];
                    var dnD = depths[indexOfMaxium + 1];

                    if (!(double.IsInfinity(sdn) || double.IsInfinity(sup) || double.IsNaN(sdn) || double.IsNaN(sup)))
                    {
                        thermoclineDetails.ThermoclineDepth = (float)(dnD * (sdn / (sdn + sup)) + upD * (sup / (sdn + sup)));
                    }
                }

                if (seasonal)
                {
                    const float minPercentageForUniqueTheroclineStep = 0.15f;

                    var minCutPoint = Math.Max(minPercentageForUniqueTheroclineStep * maxSlope, minimumMetalimionSlope);

                    var localPeaks = LocalPeaks(slopes, minCutPoint);

                    if (localPeaks.Any())
                    {
                        var indexOfSeasonallyAdjustedMaximum = Array.IndexOf(slopes, localPeaks.Last());

                        if (indexOfSeasonallyAdjustedMaximum > indexOfMaxium + 1)
                        {
                            thermoclineDetails.SeasonallyAdjustedThermoclineIndex = indexOfSeasonallyAdjustedMaximum;
                            thermoclineDetails.SeasonallyAdjustedThermoclineDepth = (depths[indexOfSeasonallyAdjustedMaximum] + depths[indexOfSeasonallyAdjustedMaximum + 1]) / 2;

                            if (indexOfSeasonallyAdjustedMaximum > 1 && indexOfSeasonallyAdjustedMaximum < depths.Length - 2)
                            {
                                var sdn = -(depths[indexOfSeasonallyAdjustedMaximum + 1] - depths[indexOfSeasonallyAdjustedMaximum]) / (slopes[indexOfSeasonallyAdjustedMaximum + 1] - slopes[indexOfSeasonallyAdjustedMaximum]);
                                var sup = (depths[indexOfSeasonallyAdjustedMaximum] - depths[indexOfSeasonallyAdjustedMaximum - 1]) / (slopes[indexOfSeasonallyAdjustedMaximum] - slopes[indexOfSeasonallyAdjustedMaximum - 1]);
                                var upD = depths[indexOfSeasonallyAdjustedMaximum];
                                var dnD = depths[indexOfSeasonallyAdjustedMaximum + 1];

                                if (!(double.IsInfinity(sdn) || double.IsInfinity(sup) || double.IsNaN(sdn) || double.IsNaN(sup)))
                                {
                                    thermoclineDetails.SeasonallyAdjustedThermoclineDepth = (float)(dnD * (sdn / (sdn + sup)) + upD * (sup / (sdn + sup)));
                                }
                            }
                        }
                        else
                        {
                            thermoclineDetails.NoSeasonalFound();
                        }
                    }
                    else
                    {
                        thermoclineDetails.NoSeasonalFound();
                    }
                }
                else
                {
                    thermoclineDetails.NoSeasonalFound();
                }

                thermocline[t] = thermoclineDetails;
            }

            return(thermocline);
        }