2 Commits 6cdf7e0cf3 ... 42ff189e48

Author SHA1 Message Date
  Henrik Kunzelmann 42ff189e48 Handle any dead-lock that may hang the UI and stop it forcefully 3 years ago
  Henrik Kunzelmann b70a3efe23 Override in per-unit system implemented 3 years ago

+ 22 - 0
src/classes/Edge.java

@@ -20,6 +20,8 @@ public class Edge {
     @Expose
     private float realLength = 1;
     @Expose
+    private boolean overrideInPerUnit = false;
+    @Expose
     private float overrideResistance = 0;
     @Expose
     private float overrideReactance = 0;
@@ -49,6 +51,10 @@ public class Edge {
         return realLength;
     }
 
+    public boolean getOverrideInPerUnit() {
+        return overrideInPerUnit;
+    }
+
     public float getOverrideResistance() {
         return overrideResistance;
     }
@@ -123,6 +129,22 @@ public class Edge {
         this.realLength = realLength;
     }
 
+    public void setOverrideInPerUnit(boolean overrideInPerUnit) {
+        this.overrideInPerUnit = overrideInPerUnit;
+    }
+
+    public void setOverrideResistance(float overrideResistance) {
+        this.overrideResistance = overrideResistance;
+    }
+
+    public void setOverrideReactance(float overrideReactance) {
+        this.overrideReactance = overrideReactance;
+    }
+
+    public void setOverrideShuntSusceptance(float overrideShuntSusceptance) {
+        this.overrideShuntSusceptance = overrideShuntSusceptance;
+    }
+
     /**
      * Getter for the Source.
      *

+ 104 - 63
src/holeg/HolegGateway.java

@@ -13,11 +13,17 @@ import holeg.ui.SolveResultMessageBox;
 import ui.controller.FlexManager;
 import ui.controller.SingletonControl;
 import ui.model.*;
+import ui.view.GUI;
 
+import javax.swing.*;
+import java.awt.*;
 import java.util.*;
+import java.util.List;
 import java.util.stream.Collectors;
 
 public class HolegGateway {
+    private static ThreadLocal<Boolean> onlyUpdateVisual = new ThreadLocal<Boolean>();
+
     private static Grid convert(MinimumNetwork network, int iteration, FlexManager flexManager) {
         SimpleGridBuilder gridBuilder = new SimpleGridBuilder();
         HashMap<AbstractCanvasObject, SimpleGridNode> canvasToGrid = new HashMap<>();
@@ -74,9 +80,9 @@ public class HolegGateway {
 
                 // Create supplier, consumer or passiv object
                 if (node.getPowerGeneration().lenSquared() > 0)
-                    decoratedNetwork.getSupplierList().add(new Supplier((HolonObject) simpleNode.tag, (float) node.getPowerGeneration().real, 0, simpleNode.voltage, simpleNode.phaseDegrees, simpleNode.typeSolved == NodeType.Slack));
+                    decoratedNetwork.getSupplierList().add(new Supplier((HolonObject) simpleNode.tag, (float) node.getPowerGeneration().real, 0, simpleNode.getVoltage(), simpleNode.phaseDegrees, simpleNode.getTypeByDesign() == NodeType.Slack));
                 else if (node.getPowerConsumption().lenSquared() > 0)
-                    decoratedNetwork.getConsumerList().add(new Consumer((HolonObject) simpleNode.tag, (float) node.getPowerConsumption().real, simpleNode.voltage, simpleNode.phaseDegrees, Math.cos(simpleNode.getPowerConsumption().angle()), simpleNode.typeSolved == NodeType.Slack));
+                    decoratedNetwork.getConsumerList().add(new Consumer((HolonObject) simpleNode.tag, (float) node.getPowerConsumption().real, simpleNode.getVoltage(), simpleNode.phaseDegrees, Math.cos(simpleNode.getPowerConsumption().angle()), simpleNode.getTypeByDesign() == NodeType.Slack));
                 else if (simpleNode.tag instanceof HolonObject)
                     decoratedNetwork.getPassivNoEnergyList().add(new Passiv((HolonObject) simpleNode.tag));
             }
@@ -133,7 +139,28 @@ public class HolegGateway {
         return context;
     }
 
-    public static synchronized void solve(PowerFlowSettings settings, MinimumNetwork minimumNetwork, int iteration, FlexManager flexManager, DecoratedNetwork network) {
+    public static void solve(PowerFlowSettings settings, MinimumNetwork minimumNetwork, int iteration, FlexManager flexManager, DecoratedNetwork network) {
+        Thread solverWrapper = new Thread(() -> {
+            try {
+                solveInternal(settings, minimumNetwork, iteration, flexManager, network);
+            } catch (Exception e) {
+                SolveResultMessageBox.showInternalError(e);
+            }
+        });
+        try {
+            solverWrapper.setName("Solver Wrapper " + Long.toHexString(solverWrapper.getId()));
+            solverWrapper.start();
+            solverWrapper.join(4000);
+            if (solverWrapper.isAlive()) {
+                solverWrapper.stop();
+                SolveResultMessageBox.showSolverTookTooLongToStart();
+            }
+        } catch (Exception e) {
+            SolveResultMessageBox.showInternalError(e);
+        }
+    }
+
+    private static void solveInternal(PowerFlowSettings settings, MinimumNetwork minimumNetwork, int iteration, FlexManager flexManager, DecoratedNetwork network) {
         // Find context
         HolegPowerFlowContext context = getContextFromHolonObjects(minimumNetwork.getHolonObjectList());
         if (context == null)
@@ -143,7 +170,7 @@ public class HolegGateway {
         solve(settings, context, minimumNetwork, iteration, flexManager, network);
     }
 
-    private static synchronized void solve(PowerFlowSettings settings, HolegPowerFlowContext context, MinimumNetwork minimumNetwork, int iteration, FlexManager flexManager, DecoratedNetwork network) {
+    private static void solve(PowerFlowSettings settings, HolegPowerFlowContext context, MinimumNetwork minimumNetwork, int iteration, FlexManager flexManager, DecoratedNetwork network) {
         if (settings == null)
             throw new IllegalArgumentException("settings is null");
         if (context == null)
@@ -160,73 +187,87 @@ public class HolegGateway {
             return;
         }
 
-        boolean solve = !PowerFlowAnalysisMenu.getInstance().areUpdatesDisabled();
-        Grid grid = convert(minimumNetwork, iteration, flexManager);
-
-        // Check if the grid is equal to one already solved
-        if (settings.onlyUpdateGridWhenChanged) {
-            for (Grid lastSolved : context.lastSolvedGrids) {
-                if (GridComparator.isEqual(lastSolved, grid)) {
-                    decorateNetwork(minimumNetwork, iteration, flexManager, network, lastSolved);
-                    return;
+        synchronized (context.lock) {
+            boolean solve = !PowerFlowAnalysisMenu.getInstance().areUpdatesDisabled();
+            Grid grid = convert(minimumNetwork, iteration, flexManager);
+
+            // Check if the grid is equal to one already solved
+            if (settings.onlyUpdateGridWhenChanged) {
+                for (Grid lastSolved : context.lastSolvedGrids) {
+                    if (GridComparator.isEqual(lastSolved, grid)) {
+                        decorateNetwork(minimumNetwork, iteration, flexManager, network, lastSolved);
+                        return;
+                    }
                 }
-            }
 
-            // Only remember grid when solving
-            if (solve) {
-                // Keep size constrained to 32 saved grids
-                if (context.lastSolvedGrids.size() >= 32)
-                    context.lastSolvedGrids.remove(0);
+                // Only remember grid when solving
+                if (solve) {
+                    // Keep size constrained to 32 saved grids
+                    if (context.lastSolvedGrids.size() >= 32)
+                        context.lastSolvedGrids.remove(0);
 
-                // Grid is updated by solver directly, so we can save the reference to the grid
-                context.lastSolvedGrids.add(grid);
+                    // Grid is updated by solver directly, so we can save the reference to the grid
+                    context.lastSolvedGrids.add(grid);
+                }
             }
-        }
-
-        // Stop old solver
-        context.stopSolver();
-
-        // Create solver job
-        Thread solverJob = new Thread(() -> {
-            long start = System.nanoTime();
-            GridSolverResult result = null;
 
-            // Starting solving when requested
-            if (solve) {
-                HolegPowerFlow powerFlow = new HolegPowerFlow();
-                result = powerFlow.solve(grid, settings);
+            // We got no solution for this grid
+            if (onlyUpdateVisual.get() != null && onlyUpdateVisual.get()) {
+                decorateNetwork(minimumNetwork, iteration, flexManager, network, grid);
+                return;
             }
 
-            // Decorate network
-            decorateNetwork(minimumNetwork, iteration, flexManager, network, grid);
-            context.solverTimeMilliseconds = (System.nanoTime() - start) / 1e6f;
-
-            // Update canvas and other visuals
-            context.showGridForVisual = grid;
-            SingletonControl.getInstance().getControl().calculateStateAndVisualForCurrentTimeStep();
-
-            // Show result message box
-            if (result != null && PowerFlowAnalysisMenu.getInstance().shouldShowResult())
-                SolveResultMessageBox.show(result);
-        });
-
-
-        // Wait or save solver job
-        try {
-            if (settings.waitForSolverJob) {
-                // Start and wait till solver job is finished
-                solverJob.start();
-                solverJob.join();
-            }
-            else {
-                // Render the non-solved grid and start the real solver job
-                decorateNetwork(minimumNetwork, iteration, flexManager, network, grid);
-                context.solverJob = solverJob;
-                solverJob.start();
+            // Stop old solver
+            context.stopSolver();
+
+            // Create solver job
+            Thread solverJob = new Thread(() -> {
+                try {
+                    long start = System.nanoTime();
+                    GridSolverResult result = null;
+
+                    // Starting solving when requested
+                    if (solve) {
+                        HolegPowerFlow powerFlow = new HolegPowerFlow();
+                        result = powerFlow.solve(grid, settings);
+                    }
+
+                    // Decorate network
+                    decorateNetwork(minimumNetwork, iteration, flexManager, network, grid);
+                    context.solverTimeMilliseconds = (System.nanoTime() - start) / 1e6f;
+
+                    // Update canvas and other visuals
+                    context.showGridForVisual = grid;
+                    onlyUpdateVisual.set(true);
+                    SingletonControl.getInstance().getControl().calculateStateAndVisualForCurrentTimeStep();
+                    onlyUpdateVisual.set(false);
+
+                    // Show result message box
+                    if (result != null && PowerFlowAnalysisMenu.getInstance().shouldShowResult())
+                        SolveResultMessageBox.show(result);
+                }
+                catch(Exception e) {
+                    SolveResultMessageBox.showInternalError(e);
+                }
+            });
+            solverJob.setName("Solver 0x" + Integer.toHexString(context.hashCode()));
+
+
+            // Wait or save solver job
+            try {
+                if (settings.waitForSolverJob) {
+                    // Start and wait till solver job is finished
+                    solverJob.start();
+                    solverJob.join();
+                } else {
+                    // Render the non-solved grid and start the real solver job
+                    decorateNetwork(minimumNetwork, iteration, flexManager, network, grid);
+                    context.solverJob = solverJob;
+                    solverJob.start();
+                }
+            } catch (InterruptedException e) {
+                e.printStackTrace();
             }
         }
-        catch(InterruptedException e) {
-            e.printStackTrace();
-        }
     }
 }

+ 14 - 6
src/holeg/HolegPowerFlow.java

@@ -97,6 +97,7 @@ public class HolegPowerFlow {
                         // Notify main thread that we are done
                         semaphore.release();
                     });
+                    jobs[jobID].setName("Solver Job " + Long.toHexString(jobs[jobID].getId()));
                 }
 
                 // Check if we should have stopped
@@ -276,12 +277,14 @@ public class HolegPowerFlow {
         // Convert from grid to power flow objects
         for (GridNode node : grid.getNodes())
             createGridNode(buses, node);
-        for (GridEdge edge : grid.getEdges())
-            createGridLine(grid, lines, edge);
 
         // Scale voltages and scale power
         double scaleVoltage = 1.0 / 230.0;
         double scalePower = scalePower(buses);
+        double scaleResistance = 1.0 / ((scaleVoltage * scaleVoltage) / scalePower);
+
+        for (GridEdge edge : grid.getEdges())
+            createGridLine(grid, lines, edge, scaleResistance);
 
         // Find position for slack node
         addSlackNode(buses, lines, tryCount, settings);
@@ -319,19 +322,24 @@ public class HolegPowerFlow {
         buses.add(bus);
     }
 
-    private void createGridLine(Grid grid, List<Line> lines, GridEdge edge) {
+    private void createGridLine(Grid grid, List<Line> lines, GridEdge edge, double scaleResistance) {
         Line line = new Line();
         line.tag = edge;
         if (ComplexNumber.isNullOrZero(edge.getOverrideImpedance())) {
             // Calculate values for line based on sample datasheet
-            line.R = 0.000194 * edge.getLengthKilometers();
-            line.X = 0.000592 * edge.getLengthKilometers();
-            line.B_2 = 0.000528;
+            line.R = 0.00642 * edge.getLengthKilometers() * scaleResistance;
+            line.X = 0.0083 * edge.getLengthKilometers() * scaleResistance;
+            line.B_2 = (0.0001 * edge.getLengthKilometers()) / scaleResistance;
         }
         else {
             line.R = edge.getOverrideImpedance().real;
             line.X = edge.getOverrideImpedance().imaginary;
             line.B_2 = edge.getOverrideShuntSusceptance();
+            if (!edge.getOverrideInPerUnit()) {
+                line.R *= scaleResistance;
+                line.X *= scaleResistance;
+                line.B_2 *= scaleResistance;
+            }
         }
         line.a = 1;
 

+ 16 - 10
src/holeg/HolegPowerFlowContext.java

@@ -6,30 +6,36 @@ import java.util.ArrayList;
 import java.util.List;
 
 public class HolegPowerFlowContext {
+    public final Object lock = new Object();
     public List<Grid> lastSolvedGrids = new ArrayList<>();
     public Thread solverJob;
     public float solverTimeMilliseconds;
     public Grid showGridForVisual;
 
     public void clearCache() {
-        lastSolvedGrids.clear();
+        synchronized (lock) {
+            lastSolvedGrids.clear();
+        }
     }
 
     public boolean isSolving() {
-        return solverJob != null && solverJob.isAlive();
+        synchronized (lock) {
+            return solverJob != null && solverJob.isAlive();
+        }
     }
 
     public void stopSolver() {
-        if (solverJob != null) {
-            try {
-                solverJob.interrupt();
-                // wait till old solver job has finished or is interrupted
-                solverJob.join(100);
-            }
-            catch(InterruptedException ignored) {
+        synchronized (lock) {
+            if (solverJob != null) {
+                try {
+                    solverJob.interrupt();
+                    // wait till old solver job has finished or is interrupted
+                    solverJob.join(100);
+                } catch (InterruptedException ignored) {
 
+                }
+                solverJob = null;
             }
-            solverJob = null;
         }
     }
 }

+ 2 - 0
src/holeg/model/GridComparator.java

@@ -101,6 +101,8 @@ public class GridComparator {
         if (a == null || b == null)
             return false;
 
+        if (a.getOverrideInPerUnit() != b.getOverrideInPerUnit())
+            return false;
         if (a.getLengthKilometers() != b.getLengthKilometers())
             return false;
         if (!ComplexNumber.equals(a.getOverrideImpedance(), b.getOverrideImpedance()))

+ 1 - 0
src/holeg/model/GridEdge.java

@@ -7,6 +7,7 @@ public interface GridEdge {
     GridNode getTo();
 
     double getLengthKilometers();
+    boolean getOverrideInPerUnit();
     ComplexNumber getOverrideImpedance();
     double getOverrideShuntSusceptance();
 

+ 1 - 0
src/holeg/model/GridNode.java

@@ -9,6 +9,7 @@ public interface GridNode {
 
     NodeType getTypeByDesign();
     NodeType getTypeSolved();
+    double getVoltage();
     ComplexNumber getPowerConsumption();
     ComplexNumber getPowerGeneration();
     Object getTag();

+ 6 - 0
src/holeg/simple_grid/SimpleGridEdge.java

@@ -9,6 +9,7 @@ public class SimpleGridEdge implements GridEdge {
     public SimpleGridNode from;
     public SimpleGridNode to;
     public double lengthKilometers = 1;
+    public boolean overrideInPerUnit = false;
     public ComplexNumber overrideImpedance = null;
     public double overrideShuntSusceptance = 0;
     public double current = 0;
@@ -31,6 +32,11 @@ public class SimpleGridEdge implements GridEdge {
         return lengthKilometers;
     }
 
+    @Override
+    public boolean getOverrideInPerUnit() {
+        return overrideInPerUnit;
+    }
+
     @Override
     public ComplexNumber getOverrideImpedance() {
         return overrideImpedance;

+ 5 - 0
src/holeg/simple_grid/SimpleGridNode.java

@@ -34,6 +34,11 @@ public class SimpleGridNode implements GridNode {
         return edges;
     }
 
+    @Override
+    public double getVoltage() {
+        return voltage;
+    }
+
     @Override
     public ComplexNumber getPowerConsumption() {
         return powerConsumption;

+ 9 - 0
src/holeg/ui/SolveResultMessageBox.java

@@ -22,4 +22,13 @@ public class SolveResultMessageBox {
                 break;
         }
     }
+
+    public static void showSolverTookTooLongToStart() {
+        JOptionPane.showMessageDialog(null,"Could not solve grid.","Solver took too long to start", JOptionPane.ERROR_MESSAGE);
+    }
+
+    public static void showInternalError(Exception exception) {
+        exception.printStackTrace();
+        JOptionPane.showMessageDialog(null,"Could not solve grid.","Internal error.", JOptionPane.ERROR_MESSAGE);
+    }
 }

+ 1 - 1
src/ui/controller/Control.java

@@ -550,7 +550,7 @@ public class Control {
      * calculates the flow of the edges and the supply for objects for the
      * current Timestep.
      */
-    public void calculateStateAndVisualForCurrentTimeStep() {
+    public synchronized void calculateStateAndVisualForCurrentTimeStep() {
     	calculateStateAndVisualForTimeStep(model.getCurIteration());
     }
 

+ 8 - 0
src/ui/controller/UpdateController.java

@@ -451,12 +451,20 @@ public class UpdateController {
             Object[] tempStatus = {"Length", model.getSelectedEdge().getLength() };
             model.getPropertyTable().addRow(tempStatus);
 			model.getPropertyTable().addRow(new Object[]{"Real length in km", model.getSelectedEdge().getRealLength()});
+			model.getPropertyTable().addRow(new Object[]{"Override in per unit", model.getSelectedEdge().getOverrideInPerUnit()});
+			model.getPropertyTable().addRow(new Object[]{"Override resistance", model.getSelectedEdge().getOverrideResistance()});
+			model.getPropertyTable().addRow(new Object[]{"Override reactance", model.getSelectedEdge().getOverrideReactance()});
+			model.getPropertyTable().addRow(new Object[]{"Override shunt susceptance", model.getSelectedEdge().getOverrideShuntSusceptance()});
             // For edges, the only possible editable cell is the max
 			// flow
 			model.getPropertyTable().setCellEditable(0, 1, false);
 			model.getPropertyTable().setCellEditable(2, 1, true);
 			model.getPropertyTable().setCellEditable(3, 1, false);
 			model.getPropertyTable().setCellEditable(4, 1, true);
+			model.getPropertyTable().setCellEditable(5, 1, true);
+			model.getPropertyTable().setCellEditable(6, 1, true);
+			model.getPropertyTable().setCellEditable(7, 1, true);
+			model.getPropertyTable().setCellEditable(8, 1, true);
 		} else if (getActualCps() == null) {
 			deleteRows(model.getSingleTable());
 			deleteRows(model.getMultiTable());

+ 27 - 1
src/ui/view/GUI.java

@@ -1685,6 +1685,31 @@ public class GUI implements CategoryListener {
 						controller.calculateStateAndVisualForCurrentTimeStep();
 					}
 				}
+				else if (selValueYBool == 5) {
+					model.getSelectedEdge().setOverrideInPerUnit((boolean) btemp);
+					controller.calculateStateAndVisualForCurrentTimeStep();
+				}
+				else if (selValueY == 6) {
+					float value = Float.parseFloat(temp.toString());
+					if (value >= 0) {
+						model.getSelectedEdge().setOverrideResistance(value);
+						controller.calculateStateAndVisualForCurrentTimeStep();
+					}
+				}
+				else if (selValueY == 7) {
+					float value = Float.parseFloat(temp.toString());
+					if (value >= 0) {
+						model.getSelectedEdge().setOverrideReactance(value);
+						controller.calculateStateAndVisualForCurrentTimeStep();
+					}
+				}
+				else if (selValueY == 8) {
+					float value = Float.parseFloat(temp.toString());
+					if (value >= 0) {
+						model.getSelectedEdge().setOverrideShuntSusceptance(value);
+						controller.calculateStateAndVisualForCurrentTimeStep();
+					}
+				}
 				// Status edition through a check box
 				if (selValueYBool == 3) {
 					Boolean bbTemp = Boolean.parseBoolean(btemp.toString());
@@ -2271,6 +2296,7 @@ public class GUI implements CategoryListener {
 			elementGraph.setText(Languages.getLanguage()[25]);
 			canvas.tempCps = null;
 			canvas.repaint();
+			controller.calculateStateAndVisualForCurrentTimeStep();
 			IdCounter.resetCounter();
 			IdCounterElem.resetCounter();
 		});
@@ -2744,7 +2770,7 @@ public class GUI implements CategoryListener {
 	 *
 	 * @return the Frame
 	 */
-	JFrame getFrmCyberPhysical() {
+	public JFrame getFrmCyberPhysical() {
 		return holegJFrame;
 	}