コード例 #1
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);
        }
コード例 #2
0
ファイル: MGHelper.cs プロジェクト: Anvoker/MicrogridPSO
        /// <summary>
        /// Precomputes the cost of each possible set of generator states at
        /// every time step.
        /// </summary>
        /// <param name="m">Microgrid input data.</param>
        /// <param name="genCount">Number of generators.</param>
        /// <param name="stepCount">The number of time steps.</param>
        /// <param name="stepSize">The size of each time step.</param>
        /// <param name="p_target">The amount of power the system needs to supply at a given time step.</param>
        /// <returns>A dictionary mapping a generator state and time step pair
        /// to the cost of purchasing the amount of power necessary to fulfill
        /// remaining demand for that specific pair.</returns>
        public static Dictionary <StateStep, float> ConstructCostDictionary(
            MGInput m,
            int stepCount,
            int stepSize,
            float penalty,
            float[] p_target)
        {
            // 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.

            // 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] = new Tuple <int, int>(
                    iStep * stepSize,
                    (iStep + 1) * stepSize - 1);
            }

            int nSqr             = 1 << m.genCount; //Bitwise squaring.
            int nPurchaseCombs   = nSqr * stepCount;
            var purchaseCostDict = new Dictionary <StateStep, float>(nPurchaseCombs);
            var indexToCost      = new Dictionary <int, float>(m.genCount);

            float[] p_thr = new float[m.genCount];

            for (int i = 0; i < m.genCount; i++)
            {
                // EUR for 1 hour of functioning
                float cost = ThermalGenerator.GetCost(
                    m.p_thr_max[i],
                    m.thr_c_a[i],
                    m.thr_c_b[i],
                    m.thr_c_c[i]);

                // EUR/MWh
                float alpha = cost / m.p_thr_max[i];
                indexToCost.Add(i, alpha);
            }

            float[] e_thr_remaining = new float[stepSize];
            float[] p_remaining     = new float[stepSize];
            var     orderedByCost   = indexToCost.OrderBy(x => x.Value).ToDictionary(x => x.Key, y => y.Value);

            for (int iState = 0; iState < nSqr; iState++)
            {
                for (int iStep = 0; iStep < stepCount; iStep++)
                {
                    float c_total = 0.0f;
                    int   t1      = stepInterval[iStep].Item1;
                    int   t2      = stepInterval[iStep].Item2;

                    for (int t = t1; t < t2; t++)
                    {
                        e_thr_remaining[t - t1] = p_target[t] / 60.0f;
                        p_remaining[t - t1]     = p_target[t];

                        foreach (KeyValuePair <int, float> kvp in orderedByCost)
                        {
                            int   iGen     = kvp.Key;
                            float alpha    = kvp.Value;
                            var   bitValue = iState & (1 << m.genCount - iGen - 1);
                            var   state    = ((bitValue | (~bitValue + 1)) >> 31) & 1;

                            if (state == 1)
                            {
                                if (alpha < m.price[t])
                                {
                                    if (m.canSell)
                                    {
                                        // Since we can sell and our generator
                                        // makes cheaper energy than the market price,
                                        // just produce at max power.
                                        p_thr[iGen] = m.p_thr_max[iGen];
                                    }
                                    else
                                    {
                                        // Since we can't sell but our generator
                                        // makes cheaper energy than market price,
                                        // we'll produce enough to cover local demand.
                                        p_thr[iGen] = Mathf.Clamp(m.p_thr_max[iGen], 0.0f, p_remaining[t - t1]);
                                    }
                                }
                                else
                                {
                                    if (m.canBuy)
                                    {
                                        // Since we can buy and our generator
                                        // makes more expensive energy than
                                        // the market price, produce no power
                                        // and buy.
                                        p_thr[iGen] = 0.0f;
                                    }
                                    else
                                    {
                                        // Since we can't buy, produce enough
                                        // power to cover local demand, regardless
                                        // of market price.
                                        p_thr[iGen] = Mathf.Clamp(m.p_thr_max[iGen], 0.0f, p_remaining[t - t1]);
                                    }
                                }

                                c_total += ThermalGenerator.GetCost(
                                    p_thr[iGen],
                                    m.thr_c_a[iGen],
                                    m.thr_c_b[iGen],
                                    m.thr_c_c[iGen]) / 60.0f;
                                p_remaining[t - t1]     -= p_thr[iGen];
                                e_thr_remaining[t - t1] -= p_thr[iGen] / 60.0f;
                            }
                            else
                            {
                                p_thr[iGen] = 0.0f;
                            }
                        }

                        if (e_thr_remaining[t - t1] < 0.0f)
                        {
                            if (m.canSell)
                            {
                                c_total += e_thr_remaining[t - t1] * m.price[t];
                            }
                        }
                        else
                        {
                            if (m.canBuy)
                            {
                                c_total += e_thr_remaining[t - t1] * m.price[t];
                            }
                            else
                            {
                                c_total += e_thr_remaining[t - t1] * penalty;
                            }
                        }
                    }

                    var id = new StateStep(iState, iStep);

#if UNITY_EDITOR || DEVELOPMENT_BUILD
                    if (purchaseCostDict.ContainsKey(id) &&
                        purchaseCostDict[id] != c_total)
                    {
                        throw new InvalidProgramException(
                                  $@"IDs and purchase costs need to have a 1 to 1
                            mapping. There can't be an ID with a different
                            purchase cost. If that happens, the code is wrong
                            somewhere.
                            UID: {id}.
                            Existing Value: {purchaseCostDict[id]}.
                            New Value: {c_total}.");
                    }
#endif

                    purchaseCostDict[id] = c_total;
                }
            }

            return(purchaseCostDict);
        }