HolegPowerFlow.java 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. package holeg;
  2. import holeg.model.Grid;
  3. import holeg.model.GridEdge;
  4. import holeg.model.GridNode;
  5. import holeg.power_flow.*;
  6. import holeg.ui.PowerFlowAnalysisMenu;
  7. import javax.swing.*;
  8. import java.io.ByteArrayOutputStream;
  9. import java.io.IOException;
  10. import java.io.PrintStream;
  11. import java.nio.charset.StandardCharsets;
  12. import java.util.*;
  13. public class HolegPowerFlow {
  14. private Random random = new Random();
  15. public GridSolverResult solve(Grid grid) {
  16. List<Grid> islands = findIslands(grid);
  17. // Use default settings
  18. SolverSettings settings = new SolverSettings();
  19. boolean anySolved = false;
  20. GridSolverResult sumResult = GridSolverResult.Solved;
  21. // Try to solve each island
  22. for (Grid island : islands) {
  23. boolean solved = false;
  24. // Try to solve problem 6 times -> each time the slack node has a new position (random)
  25. for (int i = 0; i < 6; i++) {
  26. // Build problem and solve it
  27. PowerFlowProblem problem = buildProblem(island);
  28. Solver solver = new NewtonRaphsonSolver();
  29. SolverResult result = solver.solve(problem, settings);
  30. // Debug message
  31. if (PowerFlowAnalysisMenu.getInstance().shouldShowDebug() && (result.solved || i == 3))
  32. debugShowResultAsMessageBox(problem, result);
  33. // Update grid if solved
  34. if (result.solved) {
  35. updateGrid(island, problem, result);
  36. solved = true;
  37. break;
  38. }
  39. }
  40. if (!solved)
  41. sumResult = GridSolverResult.Error;
  42. else
  43. anySolved = true;
  44. }
  45. // Check if some islands failed, but not all
  46. if (anySolved && sumResult == GridSolverResult.Error)
  47. return GridSolverResult.PartialFailure;
  48. return sumResult;
  49. }
  50. private List<Grid> findIslands(Grid grid) {
  51. List<Grid> islands = new ArrayList<>();
  52. List<GridNode> nodes = grid.getNodes();
  53. // Keeps track if the node at any index was already visited
  54. boolean[] visited = new boolean[nodes.size()];
  55. for (int i = 0; i < nodes.size(); i++) {
  56. if (visited[i])
  57. continue;
  58. // First node or node that is not connected to any other node -> new island
  59. Grid island = new Grid();
  60. Stack<Integer> nodesToVisit = new Stack<>();
  61. // Add first node
  62. nodesToVisit.push(i);
  63. // Depth first search algorithm
  64. while (!nodesToVisit.empty()) {
  65. int index = nodesToVisit.pop();
  66. // Check if already visited
  67. if (visited[index])
  68. continue;
  69. visited[index] = true;
  70. // Add node to island
  71. GridNode node = nodes.get(index);
  72. island.addNode(node);
  73. // Iterate over all edges
  74. List<GridEdge> edges = grid.getEdges(node);
  75. for (GridEdge edge : edges) {
  76. island.addEdge(edge);
  77. // Select other end of the edge for next visit
  78. if (edge.getFrom() == node)
  79. nodesToVisit.push(nodes.indexOf(edge.getTo()));
  80. else
  81. nodesToVisit.push(nodes.indexOf(edge.getFrom()));
  82. }
  83. }
  84. islands.add(island);
  85. }
  86. return islands;
  87. }
  88. private PowerFlowProblem buildProblem(Grid grid) {
  89. List<Bus> buses = new ArrayList<>();
  90. List<Line> lines = new ArrayList<>();
  91. // Convert from grid to power flow objects
  92. for (GridNode node : grid.getNodes())
  93. createGridNode(buses, node);
  94. for (GridEdge edge : grid.getEdges())
  95. createGridLine(grid, lines, edge);
  96. // Scale voltages and scale power
  97. double scaleVoltage = scaleVoltages(buses);
  98. double scalePower = scalePower(buses);
  99. // Find position for slack node
  100. addSlackNode(buses, lines);
  101. // Sort lines
  102. lines.sort(Comparator.comparingInt(a -> a.from));
  103. // Create problem
  104. return new PowerFlowProblem(buses.toArray(new Bus[0]), lines.toArray(new Line[0]), scaleVoltage, scalePower);
  105. }
  106. private void createGridNode(List<Bus> buses, GridNode node) {
  107. Bus bus = new Bus();
  108. bus.delta = 0;
  109. bus.voltage = 1;
  110. bus.Pl = node.getPowerConsumption().real;
  111. bus.Ql = node.getPowerConsumption().imaginary;
  112. bus.tag = node;
  113. if (node.getPowerGeneration().isZero()) {
  114. bus.Pg = 0;
  115. bus.Qg = 0;
  116. bus.type = BusType.PQ;
  117. }
  118. else {
  119. bus.Pg = node.getPowerGeneration().real;
  120. bus.Qg = node.getPowerGeneration().imaginary;
  121. bus.type = BusType.PV;
  122. }
  123. buses.add(bus);
  124. }
  125. private void createGridLine(Grid grid, List<Line> lines, GridEdge edge) {
  126. Line line = new Line();
  127. line.tag = edge;
  128. // Calculate values for line based on sample datasheet
  129. line.R = 0.01938; //0.791 * edge.getLengthKilometers();
  130. line.X = 0.1; //0.11 * edge.getLengthKilometers();
  131. line.B_2 = 0.1; //0.5 * 0.350141 * edge.getLengthKilometers();
  132. line.a = 1;
  133. line.from = grid.getNodes().indexOf(edge.getFrom());
  134. line.to = grid.getNodes().indexOf(edge.getTo());
  135. lines.add(line);
  136. }
  137. private double scaleVoltages(List<Bus> buses) {
  138. double decimal = 1;
  139. while(true) {
  140. boolean nextDecimal = false;
  141. for (Bus bus : buses) {
  142. if (bus.voltage > decimal) {
  143. nextDecimal = true;
  144. break;
  145. }
  146. }
  147. if (!nextDecimal)
  148. break;
  149. decimal *= 10;
  150. }
  151. for (Bus bus : buses)
  152. bus.voltage /= decimal;
  153. return 1.0 / decimal;
  154. }
  155. private double scalePower(List<Bus> buses) {
  156. double decimal = 1;
  157. while(true) {
  158. boolean nextDecimal = false;
  159. for (Bus bus : buses) {
  160. if (Math.abs(bus.Pl) > decimal || Math.abs(bus.Pg) > decimal || Math.abs(bus.Ql) > decimal || Math.abs(bus.Qg) > decimal) {
  161. nextDecimal = true;
  162. break;
  163. }
  164. }
  165. if (!nextDecimal)
  166. break;
  167. decimal *= 10;
  168. }
  169. for (Bus bus : buses) {
  170. bus.Pl /= decimal;
  171. bus.Pg /= decimal;
  172. bus.Ql /= decimal;
  173. bus.Qg /= decimal;
  174. }
  175. return 1.0 / decimal;
  176. }
  177. private void addSlackNode(List<Bus> buses, List<Line> lines) {
  178. // Find possible positions
  179. List<Integer> possiblePositions = new ArrayList<>();
  180. for (int i = 0; i < buses.size(); i++)
  181. if (buses.get(i).Pg > 0)
  182. possiblePositions.add(i);
  183. // No generators? Add all positions
  184. if (possiblePositions.size() == 0) {
  185. for (int i = 0; i < buses.size(); i++)
  186. possiblePositions.add(i);
  187. }
  188. // Choose position in grid
  189. int position = possiblePositions.get(random.nextInt(possiblePositions.size()));
  190. // Create slack node
  191. Bus slack = new Bus();
  192. slack.voltage = 1;
  193. slack.delta = 0;
  194. slack.type = BusType.Slack;
  195. buses.add(slack);
  196. // Create short line
  197. Line line = new Line();
  198. line.from = position;
  199. line.to = buses.indexOf(slack);
  200. line.R = 0.01938;
  201. line.X = 0.1;
  202. line.B_2 = 0.1;
  203. line.a = 1;
  204. lines.add(line);
  205. }
  206. private void debugShowResultAsMessageBox(PowerFlowProblem problem, SolverResult result) {
  207. try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
  208. final String utf8 = StandardCharsets.UTF_8.name();
  209. try (PrintStream ps = new PrintStream(baos, true, utf8)) {
  210. result.printTo(problem, ps);
  211. }
  212. String data = baos.toString(utf8);
  213. JOptionPane.showMessageDialog(null, data,"Debug output", JOptionPane.INFORMATION_MESSAGE);
  214. } catch (IOException e) {
  215. e.printStackTrace();
  216. }
  217. }
  218. private void updateGrid(Grid grid, PowerFlowProblem problem, SolverResult result) {
  219. double currentScale = 1;
  220. for (int i = 0; i < problem.buses.length; i++) {
  221. GridNode node = (GridNode)problem.buses[i].tag;
  222. if (node == null)
  223. continue;
  224. node.setVoltage(result.voltages[i].real / problem.scaleVoltage);
  225. node.setCurrent(result.flowData.busCurrent[i] / currentScale);
  226. }
  227. for (int i = 0; i < problem.lines.length; i++) {
  228. GridEdge edge = (GridEdge)problem.lines[i].tag;
  229. if (edge == null)
  230. continue;
  231. edge.setPowerFlow(result.flowData.linePower[i].multiply(1 / problem.scalePower));
  232. edge.setLineLoss(result.flowData.linePowerLoss[i].multiply(1 / problem.scalePower));
  233. edge.setCurrent(result.flowData.lineCurrent[i] / currentScale);
  234. }
  235. }
  236. }