NewtonRaphsonSolver.java 11 KB

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