public new void Show()
        {
            mgInput = microgrid.Input;

            batteryRatedPowerIF.SetTextWithoutNotify(mgInput.p_bat_max.ToString("F3"));
            batteryRatedEnergyIF.SetTextWithoutNotify(mgInput.e_bat_max.ToString("F3"));
            batteryRatedPower  = mgInput.p_bat_max;
            batteryRatedEnergy = mgInput.e_bat_max;

            base.Show();
        }
Example #2
0
        /// <summary>
        /// Executes a search on the provided microgrid data to generate
        /// optimal unit states for the formulated Unit Commitment Problem
        /// (UCP).
        /// </summary>
        /// <param name="input">Microgrid input data.</param>
        /// <param name="options">Defines the parameters for the PSO algorithm.</param>
        /// <param name="p_target">The local power demand at every moment of time.</param>
        /// <param name="progressCallback">Callback invoked every time the algorithm finishes a new iteration.</param>
        /// <returns>Binary integer array of unit states.</returns>
        public static async Task <int[, ]> UCPSearch(
            MGInput input,
            Options options,
            float[] p_target,
            OnNewSnapshotCallback progressCallback)
        {
            // Execute the PSO search.
#if !UNITY_WEBGL || (UNITY_WEBGL && UNITY_SSM_WEBGL_THREADING_CAPABLE)
            var t = new Task <int[]>(() =>
            {
                return(UCPExecutePSORuns(
                           input,
                           options,
                           p_target,
                           progressCallback));
            });

            t.Start();
            int[] u_thr_step_1d = await t;
#else
            int[] u_thr_step_1d = UCPExecutePSORuns(
                input,
                options,
                p_target,
                progressCallback);
#endif

            // Map the results from a 1D array to a 2D array where one
            // dimension is the generator states and the other dimension is
            // time, measured in time steps.
            int[,] u_thr_step_2d = MGHelper.Int1DToBin2D(
                u_thr_step_1d,
                options.stepCount,
                input.genCount);

            // Translate the time dimension from time steps to real time.
            float stepSize = input.tCount / options.stepCount;
            int[,] u_thr = MGHelper.TranslateTimeStep(
                u_thr_step_2d,
                stepSize,
                input.tCount,
                input.genCount);

            return(u_thr);
        }
        public void Show(MGInput defaultMGInput)
        {
            mgInput               = defaultMGInput;
            mgInput.thr_c_a       = new float[0];
            mgInput.thr_c_b       = new float[0];
            mgInput.thr_c_c       = new float[0];
            mgInput.thr_min_dtime = new float[0];
            mgInput.thr_min_utime = new float[0];
            mgInput.p_thr_max     = new float[0];
            mgInput.genCount      = 0;

            batteryRatedPowerIF.SetTextWithoutNotify(mgInput.p_bat_max.ToString("F3"));
            batteryRatedEnergyIF.SetTextWithoutNotify(mgInput.e_bat_max.ToString("F3"));
            batteryRatedPower  = mgInput.p_bat_max;
            batteryRatedEnergy = mgInput.e_bat_max;

            base.Show();
        }
Example #4
0
        private static int[] UCPExecutePSORuns(
            MGInput m,
            Options o,
            float[] p_target,
            OnNewSnapshotCallback progressCallback)
        {
            Random random = o.randomSeed.HasValue
                ? new Random(o.randomSeed.Value)
                : new Random();

            int runCount  = o.runCount;
            int iterCount = o.iterCount;
            int pCount    = o.particleCount;
            int stepCount = o.stepCount;
            int stepSize  = m.tCount / stepCount;

            int[] rBest        = new int[stepCount];
            float rBestFitness = float.MaxValue;
            float totalIter    = runCount * iterCount;
            var   threads      = new Thread[runCount];
            var   results      = new PSOResult[runCount];

            Dictionary <StateStep, float> cDict;

            cDict = MGHelper.ConstructCostDictionary(m, stepCount, stepSize, 400.0f, p_target);

            // Precompute an array that maps generator indices to the operating
            // costs for that generator if it ran at maximum power for a
            // timestep.
            float[] c_thr_max_1m = MGHelper.ThermalGenerator.GetCostAtMaxPower(
                m.genCount,
                m.p_thr_max,
                m.thr_c_a,
                m.thr_c_b,
                m.thr_c_c,
                stepSize);

            var lockObject     = new object();
            var iterLatest     = new int[runCount];
            var progressPerRun = new IterationSnapshot[runCount, iterCount];

            for (int iRun = 0; iRun < runCount; iRun++)
            {
                iterLatest[iRun] = -1;
            }

            void progressInner(
                int runIndex,
                int iterIndex,
                IterationSnapshot progressStruct)
            {
                lock (lockObject)
                {
                    iterLatest[runIndex] = iterLatest[runIndex] < iterIndex
                        ? iterIndex
                        : iterLatest[runIndex];

                    progressPerRun[runIndex, iterIndex] = progressStruct;

                    progressCallback?.Invoke(
                        runIndex,
                        iterIndex,
                        (int[])iterLatest.Clone(),
                        (IterationSnapshot[, ])progressPerRun.Clone());
                }
            }

#if !UNITY_WEBGL || (UNITY_WEBGL && UNITY_SSM_WEBGL_THREADING_CAPABLE)
            // Run the PSO multiple times in parallel.
            for (int iRun = 0; iRun < runCount; iRun++)
            {
                int _iRun = iRun;
                threads[_iRun] = new Thread(() =>
                {
                    var r          = random.Next();
                    results[_iRun] = UCPExecutePSORun(
                        _iRun, m, o, r, p_target, c_thr_max_1m, cDict, progressInner);
                });

                threads[_iRun].IsBackground = true;
                threads[_iRun].Start();
            }

            for (int iRun = 0; iRun < threads.Length; iRun++)
            {
                threads[iRun].Join();
            }
#else
            for (int iRun = 0; iRun < runCount; iRun++)
            {
                var r = random.Next();
                results[iRun] = UCPExecutePSORun(
                    iRun,
                    m,
                    o,
                    r,
                    p_target,
                    c_thr_max_1m,
                    cDict,
                    progressInner);
            }
#endif


            // Pick the best run.
            for (int iRun = 0; iRun < runCount; iRun++)
            {
                PSOResult result = results[iRun];
                if (result.bestFitness < rBestFitness)
                {
                    rBestFitness = result.bestFitness;
                    Array.Copy(result.bestPos, rBest, stepCount);
                }
            }

            return(rBest);
        }
Example #5
0
        private static PSOResult UCPExecutePSORun(
            int runIndex,
            MGInput mg,
            Options OptsPSO,
            int randomSeed,
            float[] p_target,
            float[] c_thr_max_1m,
            Dictionary <StateStep, float> cDict,
            OnNewSnapshotInnerCallback progressCallback = null)
        {
            var random    = new Random(randomSeed);
            int iterCount = OptsPSO.iterCount;
            int pCount    = OptsPSO.particleCount;
            int stepCount = OptsPSO.stepCount;
            int stepSize  = mg.tCount / stepCount;

            // The power demand that needs to be fulfilled at each time step.
            float[] p_target_step_max = new float[stepCount];

            // The min. downtime of each generator normalized to our time step.
            float[] dtime_norm = new float[mg.genCount];

            // The min. uptime of each generator normalized to our time step.
            float[] utime_norm = new float[mg.genCount];

            // Initialize min. downtime and uptime arrays.
            for (int iGen = 0; iGen < mg.genCount; iGen++)
            {
                dtime_norm[iGen] = mg.thr_min_dtime[iGen] / stepSize;
                utime_norm[iGen] = mg.thr_min_utime[iGen] / stepSize;
            }

            // Iterate through all time steps and initialize p_target_step_max.
            for (int iStep = 0; iStep < stepCount; iStep++)
            {
                // Start time of the current step.
                int t1 = iStep * stepSize;

                // End time of the current step.
                int t2 = (iStep + 1) * stepSize - 1;

                // Sum up the power demand for every real time unit across
                // our time step.
                for (int t = t1; t < t2; t++)
                {
                    p_target_step_max[iStep] +=
                        UnityEngine.Mathf.Max(
                            p_target_step_max[iStep],
                            p_target[t]);
                }
            }

            // Alpha - a criterion variable for how cost efficient a generator is.
            // Initialize an array to keep track of the alpha of every generator.
            float[] thr_alpha = MGHelper.ThermalGenerator.GetAlpha(
                mg.genCount, mg.p_thr_max, mg.thr_c_a, mg.thr_c_b, mg.thr_c_c);

            // Get an array of the indices of generators, sorted by their alpha.
            // In other words, if the 5th generator has the 2nd lowest alpha,
            // then thr_sorted_by_alpha[2] == 5.
            var thr_sorted_by_alpha = thr_alpha
                                      .Select(x => thr_alpha.OrderBy(alpha => alpha).ToList()
                                              .IndexOf(x)).ToArray();

            // Initialize an array for our particles.
            var p = InitParticles(pCount, mg.genCount, stepCount, random);

            // Initialize an array for keeping track of the global best at each time step.
            var gBest = new int[stepCount];

            // Variable for keeping track of the global best.
            float gBestFitness = float.MaxValue;

            // How many iterations have past since we've improved our global best.
            int iterSinceLastGBest = -1;

            // Go through all of our iterations, updating the particles each time.
            for (int iIter = 0; iIter < iterCount; iIter++)
            {
                iterSinceLastGBest++;

                // Percent of iterations completed.
                float completion = iIter / (float)iterCount;

                // Get the probability of inertia being applied to the
                // particle's velocity update.
                //
                // This probability decreases with each iteration, effectively
                // slowing down particles.
                float prob_inertia = UnityEngine.Mathf.Lerp(
                    1.0f, 0.35f, completion);

                // Get the probability of the particle's velocity being updated
                // to a random value.
                //
                // This probability increases with each iteration the swarm
                // goes through without finding a better global best.
                float prob_craziness = UnityEngine.Mathf.Lerp(
                    0.005f, 0.50f, iterSinceLastGBest / 100.0f);

                // Determines if the current iteration is an iteration where
                // it's possible to apply a heuristic adjustment to the swarm.
                //
                // This allows us to apply the adjustment only every n iterations.
                bool isHeuristicIter = iIter % 10 == 0;

                // Probability of a particle undergoing heuristic adjustment
                // when the iteration is a heuristic adjustment enabled
                // iteration.
                //
                // This allows us to control, on average, what percentage of
                // particles get affected by the heuristic adjustment.
                float prob_heuristic = 0.25f;

                // Step 1.
                // Iterate through all particles, evaluating their fitness
                // and establishing their personal best.
                for (int iP = 0; iP < pCount; iP++)
                {
                    float fitness = GetFitness(p[iP].pos, stepCount, cDict);

                    if (fitness < p[iP].fitnessBest)
                    {
                        p[iP].fitnessBest = fitness;
                        Array.Copy(p[iP].pos, p[iP].pBest, stepCount);
                    }
                }

                // Step 2.
                // Iterate through all particles, establishing the global best
                // in the swarm.
                for (int iP = 0; iP < pCount; iP++)
                {
                    if (p[iP].fitnessBest < gBestFitness)
                    {
                        gBestFitness = p[iP].fitnessBest;
                        Array.Copy(p[iP].pBest, gBest, stepCount);
                        iterSinceLastGBest = 0;
                    }
                }

                // Step 3.
                // Update the velocity and position of every particle.
                //
                // Optionally apply a heuristic adjustment aimed at speeding
                // the convergence of the swarm.
                //
                // Revert state switches of generators that would violate the
                // min/max uptime constraints of that generator. This is
                // necessary because the particles have no knowledge of those
                // constraionts. So we deal with that simply by undoing
                // illegal state switches.
                for (int iP = 0; iP < pCount; iP++)
                {
                    Binary.UpdateVel(
                        p[iP].pos,
                        p[iP].vel,
                        p[iP].pBest,
                        gBest,
                        prob_inertia,
                        prob_craziness,
                        mg.genCount,
                        random);

                    Binary.UpdatePos(p[iP].pos, p[iP].vel);

                    /*
                     * if (isHeuristicIter)
                     * {
                     *  if (random.NextDouble() < prob_heuristic)
                     *  {
                     *      HeuristicAdjustment(
                     *          mg.genCount,
                     *          stepCount,
                     *          stepSize,
                     *          p[iP].pos,
                     *          thr_sorted_by_alpha,
                     *          mg.p_thr_max,
                     *          p_target_step_max);
                     *  }
                     * }
                     */

                    MGHelper.FixMinTimeConstraints(
                        p[iP].pos, stepCount, mg.genCount, dtime_norm, utime_norm);
                }

                var snapshot = new IterationSnapshot(
                    iIter,
                    iterCount,
                    stepCount,
                    mg.genCount,
                    gBestFitness,
                    gBest,
                    p);

                progressCallback?.Invoke(runIndex, iIter, snapshot);
            }


            return(new PSOResult
            {
                bestPos = gBest,
                bestFitness = gBestFitness
            });
        }
Example #6
0
        public int[,] UCPSearch(
            MGInput m,
            float[] p_target,
            Action <float> progressCallback)
        {
            // Exhaustive Search is very expensive so we use a coarser step.
            // But we'll need to translate from the coarse step to the
            // orginal step so we'll store the translation coefficient
            // in stepSize.
            int stepCount = options.stepCount;
            int stepSize  = m.tCount / stepCount;
            int taskCount = options.taskCount;

            // Get the total number of possible combinations.
            long nCombinations = (long)1 << (m.genCount * stepCount);

            // Out fitness variable.
            float lowestCost = Mathf.Infinity;

            // Pre-calculate fuel costs at max power because we'll use them
            // later to calculate a presumptive total cost.
            float[] cost_thr_at_max = ThermalGenerator.GetCostAtMaxPower(
                m.genCount, m.p_thr_max, m.thr_c_a, m.thr_c_b, m.thr_c_c, 60.0f);

            // We only need to verify min uptime/downtime constraints if our
            // step size is smaller than the the biggest min u/dtime. Otherwise
            // our step is large enough that the algorithm will never be in a
            // situation where it's trying to switch a generator that can't be
            // switched due to min downtime/uptime constraints.
            bool[] isValidationNeeded = IsMinTimeVerificationNeeded(
                m.genCount, stepSize, m.thr_min_dtime, m.thr_min_utime);

            // t1 and t2 define the interval of time this step occupies.
            var stepInterval = new Tuple <int, int> [stepCount];

            for (int iStep = 0; iStep < stepCount; iStep++)
            {
                stepInterval[iStep] = Tuple.Create(
                    iStep * stepSize,
                    (iStep + 1) * stepSize - 1);
            }

            // It's expensive to compute the cost of the amount of power the
            // microgrid needs to purchase inside the main loop. There are only
            // 2^n * stepCount combinations possible which end up being
            // redundantly recalculated a lot if done in the main loop.
            // So we compute all of them ahead of time and store them in a
            // dictionary.
            Dictionary <StateStep, float> purchaseCostDict
                = ConstructCostDictionary(m, stepCount, stepSize, 400, p_target);

            // Running the search in threads makes pruning high cost
            // combinations happen earlier and more often because a low cost
            // variant is discovered earlier on.
            long bestComb        = -1;
            var  tasks           = new Task[taskCount];
            long combSegmentSize = nCombinations / taskCount;

            var taskParams = new TaskParams(
                p_target,
                stepCount,
                stepSize,
                stepInterval,
                isValidationNeeded,
                cost_thr_at_max,
                purchaseCostDict);

            var taskProgress = new float[taskCount];

            void progressCallbackInner(int id, float completion)
            {
                taskProgress[id] = completion;
                float totalProgress = 0;

                for (int i = 0; i < taskProgress.Length; i++)
                {
                    totalProgress += taskProgress[i];
                }
                progressCallback(totalProgress / taskCount);
            }

            for (int iTask = 0; iTask < taskCount; iTask++)
            {
                taskProgress[iTask] = 0.0f;
                long searchStart = iTask * combSegmentSize;
                long searchEnd   = (iTask + 1) * combSegmentSize;
                int  taskID      = iTask;
                var  task        = Task.Run(() => SearchTask(
                                                m,
                                                searchStart,
                                                searchEnd,
                                                ref bestComb,
                                                ref lowestCost,
                                                taskParams,
                                                progressCallbackInner,
                                                taskID));
                tasks[iTask] = task;
            }

            var waiter = Task.WhenAll(tasks.ToArray());

            waiter.Wait();

            // Write out the step based state array
            int[,] u_thr_best_step = new int[m.genCount, m.tCount];
            for (int iStep = 0; iStep < stepCount; iStep++)
            {
                for (int iGen = m.genCount - 1; iGen >= 0; iGen--)
                {
                    u_thr_best_step[iGen, iStep] = (int)(bestComb % 2);
                    bestComb /= 2;
                }
            }

            // Translate step-based state array to original time based
            // state array.
            int[,] u_thr_best = new int[m.genCount, m.tCount];
            for (int t = 0; t < m.tCount; t++)
            {
                int currStep = t / stepSize;
                for (int iGen = 0; iGen < m.genCount; iGen++)
                {
                    u_thr_best[iGen, t] = u_thr_best_step[iGen, currStep];
                }
            }

            return(u_thr_best);
        }
Example #7
0
        private static void SearchTask(MGInput m,
                                       long CombStart,
                                       long CombEnd,
                                       ref long globalBestComb,
                                       ref float globalLowestCost,
                                       TaskParams p,
                                       Action <int, float> progressCallback,
                                       int taskID)
        {
            // An array we use to store a candidate solution.
            int[,] u_thr_candidate = new int[m.genCount, p.stepCount];

            int[] states = new int[m.genCount];

            long  localBestComb;
            float localLowestCost = Mathf.Infinity;

            float[] dtime_norm = new float[m.genCount];
            float[] utime_norm = new float[m.genCount];
            for (int iGen = 0; iGen < m.genCount; iGen++)
            {
                dtime_norm[iGen] = m.thr_min_dtime[iGen] / p.stepSize;
                utime_norm[iGen] = m.thr_min_utime[iGen] / p.stepSize;
            }

            for (long iComb = CombStart; iComb < CombEnd; iComb++)
            {
                long  currComb = iComb;
                float c_total  = 0.0f;
                // 1 1 1 1 0 1 1 1 0 0 0 1 1 0 0 1
                // We assume the state is valid until proven otherwise.
                bool isStateValid = true;

                for (int iStep = 0; iStep < p.stepCount; iStep++)
                {
                    float c_thr_sum_step = 0.0f;

                    currComb = IntToBinary(currComb, m.genCount, states);

                    for (int ithr = 0; ithr < m.genCount; ithr++)
                    {
                        // Get the binary digit representing the on/off sate
                        // for our current thr and step.
                        u_thr_candidate[ithr, iStep] = states[ithr];

                        if (!isStateValid)
                        {
                            break;
                        }

                        // The total cost incurred by the thrs over the time
                        // interval of the step.
                        c_thr_sum_step += states[ithr] * p.cost_thr_at_max[ithr]
                                          * p.stepSize;

                        // The total power the thrs can give at any t moment
                        // during the step. It's constant throughout the step
                        // because the state of units is constant throughout
                        // the time interval of the step.
                    } // END thr

                    int ii = BinaryToInt(states);
                    var id = new StateStep(ii, iStep);


                    c_total += c_thr_sum_step + p.purchaseDict[id];

                    // Pruning. If the total cost is already higher than the
                    // lowest cost then there's no way this combination can
                    // be the optimal one, so we abandon verifying it and break.
                    if (c_total > globalLowestCost)
                    {
                        break;
                    }
                } // END Step

                for (int ithr = 0; ithr < m.genCount; ithr++)
                {
                    if (!MGHelper.IsStateValid(GetRow(u_thr_candidate, ithr),
                                               dtime_norm[ithr], utime_norm[ithr], p.stepCount))
                    {
                        isStateValid = false;
                        break;
                    }
                }

                if (isStateValid)
                {
                    if (c_total < localLowestCost)
                    {
                        progressCallback(taskID,
                                         (float)((double)(iComb - CombStart)
                                                 / (double)(CombEnd - CombStart)));
                        localLowestCost = c_total;
                        localBestComb   = iComb;

                        if (localLowestCost < Volatile.Read(ref globalLowestCost))
                        {
                            Interlocked.Exchange(ref globalLowestCost, localLowestCost);
                            Interlocked.Exchange(ref globalBestComb, localBestComb);
                        }
                    }
                }
            } // END Combination
        }