NewtonRaphsonSolver.java 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. package holeg.power_flow;
  2. import Jama.LUDecomposition;
  3. import Jama.Matrix;
  4. import holeg.GridSolverResult;
  5. import java.util.Arrays;
  6. public class NewtonRaphsonSolver implements Solver {
  7. @Override
  8. public SolverResult solve(PowerFlowProblem problem, SolverSettings settings) {
  9. if (problem == null)
  10. throw new IllegalArgumentException("problem is null");
  11. if (settings == null)
  12. throw new IllegalArgumentException("settings is null");
  13. try {
  14. long timeStart = System.nanoTime();
  15. // Check data first
  16. if (!problem.checkForValidData())
  17. return SolverResult.error(SolverError.InvalidData);
  18. // Create performance monitor
  19. SolverPerformance performance = new SolverPerformance();
  20. // Build admittance matrix
  21. AdmittanceMatrix admittanceMatrix = AdmittanceMatrix.build(problem.buses, problem.lines);
  22. Matrix G_bus = admittanceMatrix.getG();
  23. Matrix B_bus = admittanceMatrix.getB();
  24. // Copy data over to do math with it
  25. int busCount = problem.buses.length;
  26. double[] V = new double[busCount];
  27. double[] delta = new double[busCount];
  28. double[] Pset = new double[busCount];
  29. double[] Qset = new double[busCount];
  30. for (int i = 0; i < busCount; i++) {
  31. V[i] = problem.buses[i].voltage;
  32. delta[i] = problem.buses[i].delta;
  33. Pset[i] = problem.buses[i].Pg - problem.buses[i].Pl; // generation - load
  34. Qset[i] = problem.buses[i].Qg - problem.buses[i].Ql;
  35. }
  36. // Find PQ/PV nodes
  37. int[] pq = Util.indicesOf(problem.buses, BusType.PQ);
  38. int[] pv = Util.indicesOf(problem.buses, BusType.PV);
  39. // Main solver iteration
  40. double tolerance = Double.MAX_VALUE;
  41. // Jacobian caching
  42. Matrix inverseJ = Matrix.identity(1, 1);
  43. double factor = 1;
  44. int jacobianRecalculation = 0;
  45. for (int iter = 1; iter <= settings.maxIterations; iter++) {
  46. // Check if we should stop
  47. if (Thread.interrupted())
  48. return SolverResult.error(SolverError.Interrupted);
  49. // Calculate current P/Q values
  50. double[] P = new double[busCount];
  51. double[] Q = new double[busCount];
  52. for (int i = 0; i < busCount; i++) {
  53. for (int j = 0; j < busCount; j++) {
  54. P[i] += V[i] * V[j] * (G_bus.get(i, j) * Math.cos(delta[i] - delta[j]) + B_bus.get(i, j) * Math.sin(delta[i] - delta[j]));
  55. Q[i] += V[i] * V[j] * (G_bus.get(i, j) * Math.sin(delta[i] - delta[j]) - B_bus.get(i, j) * Math.cos(delta[i] - delta[j]));
  56. }
  57. }
  58. // Reactive power limit
  59. if (iter > 2 && iter < 20) {
  60. for (int i = 0; i < pv.length; i++) {
  61. Bus bus = problem.buses[i];
  62. double q = Q[i] + bus.Ql;
  63. if (q < bus.Qmin)
  64. V[i] += 0.01;
  65. else if (q > bus.Qmax)
  66. V[i] -= 0.01;
  67. }
  68. }
  69. // Calculate error given by delta between set values and current values
  70. double[] dP = Util.subtract(Pset, P);
  71. double[] dQ = Util.subtract(Qset, Q);
  72. // Create error vector
  73. double[] error = new double[busCount - 1 + pq.length];
  74. for (int i = 0; i < busCount - 1; i++)
  75. error[i] = dP[i + 1];
  76. for (int i = 0; i < pq.length; i++)
  77. error[i + busCount - 1] = dQ[pq[i]];
  78. // Update performance
  79. performance.iterations = iter;
  80. performance.error = tolerance;
  81. // Calculate mismatch between values excepted and current values
  82. tolerance = Arrays.stream(error).map(Math::abs).max().orElse(0);
  83. if (tolerance < settings.minError)
  84. break;
  85. if (jacobianRecalculation <= 0) {
  86. // Jacobian matrix J1
  87. Matrix J1 = new Matrix(busCount - 1, busCount - 1);
  88. for (int i = 0; i < busCount - 1; i++) {
  89. int m = i + 1;
  90. for (int k = 0; k < busCount - 1; k++) {
  91. int n = k + 1;
  92. if (n == m) {
  93. for (int l = 0; l < busCount - 1; l++)
  94. J1.set(i, k, J1.get(i, k) + V[m] * V[l] * (-G_bus.get(m, l) * Math.sin(delta[m] - delta[l]) + B_bus.get(m, l) * Math.cos(delta[m] - delta[l])));
  95. J1.set(i, k, J1.get(i, k) - Math.pow(V[m], 2) * B_bus.get(m, m));
  96. } else
  97. J1.set(i, k, V[m] * V[n] * (G_bus.get(m, n) * Math.sin(delta[m] - delta[n]) - B_bus.get(m, n) * Math.cos(delta[m] - delta[n])));
  98. }
  99. }
  100. // Jacobian matrix J2
  101. Matrix J2 = new Matrix(busCount - 1, pq.length);
  102. for (int i = 0; i < busCount - 1; i++) {
  103. int m = i + 1;
  104. for (int k = 0; k < pq.length; k++) {
  105. int n = pq[k];
  106. if (n == m) {
  107. for (int l = 0; l < busCount; l++)
  108. J2.set(i, k, J2.get(i, k) + V[l] * (G_bus.get(m, l) * Math.cos(delta[m] - delta[l]) + B_bus.get(m, l) * Math.sin(delta[m] - delta[l])));
  109. J2.set(i, k, J2.get(i, k) + V[m] * G_bus.get(m, m));
  110. } else
  111. J2.set(i, k, V[m] * (G_bus.get(m, n) * Math.cos(delta[m] - delta[n]) + B_bus.get(m, n) * Math.sin(delta[m] - delta[n])));
  112. }
  113. }
  114. // Jacobian matrix J3
  115. Matrix J3 = new Matrix(pq.length, busCount - 1);
  116. for (int i = 0; i < pq.length; i++) {
  117. int m = pq[i];
  118. for (int k = 0; k < busCount - 1; k++) {
  119. int n = k + 1;
  120. if (n == m) {
  121. for (int l = 0; l < busCount; l++)
  122. J3.set(i, k, J3.get(i, k) + V[m] * V[l] * (G_bus.get(m, l) * Math.cos(delta[m] - delta[l]) + B_bus.get(m, l) * Math.sin(delta[m] - delta[l])));
  123. J3.set(i, k, J3.get(i, k) - Math.pow(V[m], 2) * G_bus.get(m, m));
  124. } else
  125. J3.set(i, k, V[m] * V[n] * (-G_bus.get(m, n) * Math.cos(delta[m] - delta[n]) - B_bus.get(m, n) * Math.sin(delta[m] - delta[n])));
  126. }
  127. }
  128. // Jacobian matrix J4
  129. Matrix J4 = new Matrix(pq.length, pq.length);
  130. for (int i = 0; i < pq.length; i++) {
  131. int m = pq[i];
  132. for (int k = 0; k < pq.length; k++) {
  133. int n = pq[k];
  134. if (n == m) {
  135. for (int l = 0; l < busCount; l++)
  136. J4.set(i, k, J4.get(i, k) + V[l] * (G_bus.get(m, l) * Math.sin(delta[m] - delta[l]) - B_bus.get(m, l) * Math.cos(delta[m] - delta[l])));
  137. J4.set(i, k, J4.get(i, k) - V[m] * B_bus.get(m, m));
  138. } else
  139. J4.set(i, k, V[m] * (G_bus.get(m, n) * Math.sin(delta[m] - delta[n]) - B_bus.get(m, n) * Math.cos(delta[m] - delta[n])));
  140. }
  141. }
  142. // Construct matrix like this:
  143. // J1 | J2
  144. // -------
  145. // J3 | J4
  146. Matrix J12 = Util.concatColumns(J1, J2);
  147. Matrix J34 = Util.concatColumns(J3, J4);
  148. Matrix J = Util.concatRows(J12, J34);
  149. // Inverse of matrix
  150. try {
  151. LUDecomposition lu = new LUDecomposition(J);
  152. inverseJ = lu.solve(Matrix.identity(J.getRowDimension(), J.getColumnDimension()));
  153. }
  154. catch (Exception e) {
  155. for (int row = 0; row < J.getRowDimension(); row++) {
  156. for (int col = 0; col < J.getColumnDimension(); col++)
  157. System.out.printf("%f ", J.get(col, row));
  158. System.out.println(";");
  159. }
  160. throw e;
  161. }
  162. // Try to find good factor to slow down convergence
  163. try {
  164. double[] eigenvalues = inverseJ.eig().getRealEigenvalues();
  165. double maxEigenvalue = Arrays.stream(eigenvalues).map(Math::abs).max().orElse(0);
  166. // Will only convergence when eigenvalues of matrix are < 1
  167. if (maxEigenvalue > 1)
  168. factor = 1 / maxEigenvalue;
  169. } catch (Exception unused) {
  170. // ignored
  171. }
  172. jacobianRecalculation = settings.jacobianRecalculationInterval;
  173. }
  174. else
  175. jacobianRecalculation--;
  176. // Newton step
  177. Matrix x = inverseJ.times(Util.fromArray(error));
  178. // Check for NaN
  179. for (int i = 0; i < x.getRowDimension(); i++)
  180. if (Double.isNaN(x.get(i, 0)))
  181. return SolverResult.error(SolverError.ConvergenceError);
  182. // Update current state (delta, voltage)
  183. for (int i = 0; i < busCount - 1; i++)
  184. delta[i + 1] += x.get(i, 0) * factor;
  185. for (int i = 0; i < pq.length; i++)
  186. V[pq[i]] += x.get(i + busCount - 1, 0) * factor;
  187. }
  188. // Tolerance is too high
  189. if (tolerance >= settings.maxError)
  190. return SolverResult.error(SolverError.ConvergenceError);
  191. // Complex voltages
  192. ComplexNumber[] voltages = new ComplexNumber[V.length];
  193. for (int i = 0; i < V.length; i++)
  194. voltages[i] = ComplexNumber.fromPolar(V[i], delta[i]);
  195. // Calculate flows
  196. FlowData flowData = FlowCalculation.calculate(admittanceMatrix, problem.buses, problem.lines, voltages);
  197. // Save finished time
  198. performance.timeInMilliseconds = (System.nanoTime() - timeStart) / 1e6;
  199. // Return success
  200. return SolverResult.success(performance, voltages, flowData);
  201. }
  202. catch(Exception e) {
  203. e.printStackTrace();
  204. }
  205. return SolverResult.error(SolverError.Internal);
  206. }
  207. }