/// <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); }
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); }