Henrik Kunzelmann 2 years ago
parent
commit
74feaca94a
34 changed files with 309 additions and 104 deletions
  1. BIN
      exampleNetworks/ieee14_bus.holon
  2. 10 0
      src/classes/Edge.java
  3. 2 2
      src/classes/HolonObject.java
  4. 15 6
      src/holeg/HolegGateway.java
  5. 37 8
      src/holeg/HolegPowerFlow.java
  6. 5 0
      src/holeg/HolegPowerFlowContext.java
  7. 6 0
      src/holeg/PowerFlowSettings.java
  8. 2 0
      src/holeg/model/GridComparator.java
  9. 1 0
      src/holeg/model/GridEdge.java
  10. 1 0
      src/holeg/model/GridNode.java
  11. 6 2
      src/holeg/model/NodeType.java
  12. 2 0
      src/holeg/power_flow/Bus.java
  13. 13 1
      src/holeg/power_flow/FlowCalculation.java
  14. 8 3
      src/holeg/power_flow/NewtonRaphsonSolver.java
  15. 2 2
      src/holeg/power_flow/PowerFlowProgram.java
  16. 10 5
      src/holeg/power_flow/SolverResult.java
  17. 34 34
      src/holeg/power_flow/TestData.java
  18. 6 0
      src/holeg/simple_grid/SimpleGridEdge.java
  19. 6 0
      src/holeg/simple_grid/SimpleGridNode.java
  20. 1 1
      src/holeg/test_headless/TestHeadlessProgram.java
  21. 32 15
      src/holeg/test_sensitivity/TestSensitivityProgram.java
  22. 33 1
      src/holeg/ui/FlowTableWindow.java
  23. 9 0
      src/holeg/ui/PowerFlowAnalysisMenu.java
  24. 12 0
      src/holeg/ui/SettingsWindow.java
  25. 0 1
      src/ui/controller/CategoryController.java
  26. 0 1
      src/ui/controller/ObjectController.java
  27. 2 0
      src/ui/controller/UpdateController.java
  28. 7 1
      src/ui/model/Consumer.java
  29. 4 4
      src/ui/model/DecoratedNetwork.java
  30. 9 2
      src/ui/model/Passiv.java
  31. 7 1
      src/ui/model/Supplier.java
  32. 7 0
      src/ui/view/GUI.java
  33. 4 4
      src/ui/view/GroupNodeCanvas.java
  34. 16 10
      src/ui/view/MyCanvas.java

BIN
exampleNetworks/ieee14_bus.holon


+ 10 - 0
src/classes/Edge.java

@@ -27,6 +27,8 @@ public class Edge {
     private float overrideReactance = 0;
     @Expose
     private float overrideShuntSusceptance = 0;
+    @Expose
+    private float overrideTapRatio = 1;
     ArrayList<Integer> tags;
     // for internal use --> flow of electricity (simulation state)
     ArrayList<Integer> pseudoTags;
@@ -67,6 +69,10 @@ public class Edge {
         return overrideShuntSusceptance;
     }
 
+    public float getOverrideTapRatio() {
+        return overrideTapRatio;
+    }
+
     @Expose
     private boolean breakedManuel = false;
     @Expose
@@ -145,6 +151,10 @@ public class Edge {
         this.overrideShuntSusceptance = overrideShuntSusceptance;
     }
 
+    public void setOverrideTapRatio(float overrideTapRatio) {
+        this.overrideTapRatio = overrideTapRatio;
+    }
+
     /**
      * Getter for the Source.
      *

+ 2 - 2
src/classes/HolonObject.java

@@ -204,12 +204,12 @@ public class HolonObject extends AbstractCanvasObject {
      */
     public float getEnergyAtTimeStep(int timestep)
     {
-    	return getElements().stream().filter(element -> element.isActive()).map(element -> element.getEnergyAtTimeStep(timestep)).reduce(0.0f, (a, b) -> a + b);
+    	return getElements().stream().filter(element -> element.isActive() && !element.getEleName().startsWith("__")).map(element -> element.getEnergyAtTimeStep(timestep)).reduce(0.0f, (a, b) -> a + b);
     }
 
     public float getReactiveEnergyAtTimeStep(int timestep)
     {
-        return getElements().stream().filter(element -> element.isActive()).map(element -> element.getReactivePowerAtTimestep(timestep)).reduce(0.0f, (a, b) -> a + b);
+        return getElements().stream().filter(element -> element.isActive() && !element.getEleName().startsWith("__")).map(element -> element.getReactivePowerAtTimestep(timestep)).reduce(0.0f, (a, b) -> a + b);
     }
     
     public float getEnergyAtTimeStepWithFlex(int timestep, FlexManager flexManager)

+ 15 - 6
src/holeg/HolegGateway.java

@@ -1,6 +1,7 @@
 package holeg;
 
 import classes.AbstractCanvasObject;
+import classes.HolonElement;
 import classes.HolonObject;
 import classes.Node;
 import holeg.model.*;
@@ -39,11 +40,17 @@ public class HolegGateway {
                 node = gridBuilder.addGenerator(new ComplexNumber(power, reactivePower));
             node.tag = object;
 
+            // Find voltage
+            Optional<HolonElement> voltageOverride = object.getElements().stream().filter(e -> e.getEleName().equalsIgnoreCase("__VOLTAGE")).findFirst();
+            voltageOverride.ifPresent(e -> node.voltage = e.getEnergyAtTimeStep(0));
+
             // Find type by design
             if (object.getElements().stream().anyMatch(e -> e.getEleName().equalsIgnoreCase("SLACK")))
                 node.typeByDesign = NodeType.Slack;
-            else if (object.getElements().stream().anyMatch(e -> e.getEleName().equalsIgnoreCase("TRANSFORMER")))
-                node.typeByDesign = NodeType.Transformer;
+            else if (object.getElements().stream().anyMatch(e -> e.getEleName().equalsIgnoreCase("PV")))
+                node.typeByDesign = NodeType.PV;
+            else if (object.getElements().stream().anyMatch(e -> e.getEleName().equalsIgnoreCase("PQ")))
+                node.typeByDesign = NodeType.PQ;
             else
                 node.typeByDesign = NodeType.Bus;
             canvasToGrid.put(object, node);
@@ -63,8 +70,10 @@ public class HolegGateway {
             if (a == null || b == null)
                 continue;
             SimpleGridEdge edge = gridBuilder.connect(a, b, Math.max(1, cable.getModel().getRealLength()));
+            edge.overrideInPerUnit = cable.getModel().getOverrideInPerUnit();
             edge.overrideImpedance = new ComplexNumber(cable.getModel().getOverrideResistance(), cable.getModel().getOverrideReactance());
             edge.overrideShuntSusceptance = cable.getModel().getOverrideShuntSusceptance();
+            edge.overrideTapRatio = cable.getModel().getOverrideTapRatio();
             edge.tag = cable;
         }
         return gridBuilder.getGrid();
@@ -80,11 +89,11 @@ 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.getVoltage(), simpleNode.phaseDegrees, simpleNode.getTypeByDesign() == NodeType.Slack));
+                    decoratedNetwork.getSupplierList().add(new Supplier((HolonObject) simpleNode.tag, (float) node.getPowerGeneration().real, 0, simpleNode.getVoltage(), simpleNode.phaseDegrees, simpleNode.getTypeSolved() == NodeType.Slack, simpleNode.index));
                 else if (node.getPowerConsumption().lenSquared() > 0)
-                    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));
+                    decoratedNetwork.getConsumerList().add(new Consumer((HolonObject) simpleNode.tag, (float) node.getPowerConsumption().real, simpleNode.getVoltage(), simpleNode.phaseDegrees, Math.cos(simpleNode.getPowerConsumption().angle()), simpleNode.getTypeSolved() == NodeType.Slack, simpleNode.index));
                 else if (simpleNode.tag instanceof HolonObject)
-                    decoratedNetwork.getPassivNoEnergyList().add(new Passiv((HolonObject) simpleNode.tag));
+                    decoratedNetwork.getPassivNoEnergyList().add(new Passiv((HolonObject) simpleNode.tag, simpleNode.index));
             }
 
             // Convert all edges
@@ -229,7 +238,7 @@ public class HolegGateway {
                     // Starting solving when requested
                     if (solve) {
                         HolegPowerFlow powerFlow = new HolegPowerFlow();
-                        result = powerFlow.solve(grid, settings);
+                        result = powerFlow.solve(grid, settings, context);
                     }
 
                     // Decorate network

+ 37 - 8
src/holeg/HolegPowerFlow.java

@@ -18,7 +18,7 @@ import java.util.concurrent.Semaphore;
 public class HolegPowerFlow {
     private Random random = new Random();
 
-    public GridSolverResult solve(Grid grid, PowerFlowSettings settings) {
+    public GridSolverResult solve(Grid grid, PowerFlowSettings settings, HolegPowerFlowContext context) {
         if (grid == null)
             throw new IllegalArgumentException("grid is null");
         if (settings == null)
@@ -104,6 +104,8 @@ public class HolegPowerFlow {
                 if (Thread.interrupted())
                     return GridSolverResult.Interrupted;
 
+                System.out.println("     ");
+
                 // Check if actually we have jobs to run
                 if (jobs.length > 0) {
                     boolean[] resultUsed = new boolean[jobs.length];
@@ -144,6 +146,8 @@ public class HolegPowerFlow {
                                     if (result.error == SolverError.Interrupted)
                                         return GridSolverResult.Interrupted;
 
+                                    System.out.printf("Problem %d, solved: %s, slack: %.3f\n", i, result.solved ? "true" : "false", result.solved ? result.flowData.busInjection[i].real / problem.scalePower : 0);
+
                                     // Solver was successful?
                                     if (result.solved) {
                                         // Simply use the first solved result
@@ -201,8 +205,17 @@ public class HolegPowerFlow {
 
                 // Update grid or set error
                 if (!error) {
-                    if (bestProblem != null)
+                    if (bestProblem != null) {
                         updateGrid(island, bestProblem, bestResult);
+
+                        // Save problem and result to context
+                        if (context != null) {
+                            synchronized (context.lock) {
+                                context.problem = bestProblem;
+                                context.result = bestResult;
+                            }
+                        }
+                    }
                     anySolved = true;
                 }
                 else
@@ -279,9 +292,9 @@ public class HolegPowerFlow {
             createGridNode(buses, node);
 
         // Scale voltages and scale power
-        double scaleVoltage = 1.0 / 230.0;
-        double scalePower = scalePower(buses);
-        double scaleResistance = 1.0 / ((scaleVoltage * scaleVoltage) / scalePower);
+        double scaleVoltage = settings.disableAutomaticScaling ? 1 : 1.0 / 230.0;
+        double scalePower = settings.disableAutomaticScaling ? 1 : scalePower(buses);
+        double scaleResistance = settings.disableAutomaticScaling ? 1 :  1.0 / ((scaleVoltage * scaleVoltage) / scalePower);
 
         for (GridEdge edge : grid.getEdges())
             createGridLine(grid, lines, edge, scaleResistance);
@@ -304,8 +317,21 @@ public class HolegPowerFlow {
         bus.Ql = Math.abs(node.getPowerConsumption().imaginary);
         bus.tag = node;
 
-        if (node.getTypeByDesign() == NodeType.Slack)
-            bus.type = BusType.Slack;
+        if (node.getTypeByDesign() != NodeType.Bus) {
+            switch(node.getTypeByDesign()) {
+                case PV:
+                    bus.type = BusType.PV;
+                    break;
+                case PQ:
+                    bus.type = BusType.PQ;
+                    break;
+                case Slack:
+                    bus.type = BusType.Slack;
+                    break;
+            }
+            bus.Pg = Math.abs(node.getPowerGeneration().real);
+            bus.Qg = Math.abs(node.getPowerGeneration().imaginary);
+        }
         else if (node.getPowerGeneration().isZero()) {
             bus.Pg = 0;
             bus.Qg = 0;
@@ -318,6 +344,7 @@ public class HolegPowerFlow {
         }
         bus.Qmin = -1000000;
         bus.Qmax = 1000000;
+        bus.index = buses.size();
 
         buses.add(bus);
     }
@@ -511,7 +538,7 @@ public class HolegPowerFlow {
         try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
             final String utf8 = StandardCharsets.UTF_8.name();
             try (PrintStream ps = new PrintStream(stream, true, utf8)) {
-                result.printTo(problem, ps);
+                result.printTo(problem, ps, "");
             }
             String data = stream.toString(utf8);
             JOptionPane.showMessageDialog(null, data,"Debug output", JOptionPane.INFORMATION_MESSAGE);
@@ -531,6 +558,8 @@ public class HolegPowerFlow {
             node.setPhase(Math.toDegrees(result.voltages[i].angle()));
             node.setCurrent(result.flowData.busCurrent[i] / currentScale);
 
+            node.setIndex(problem.buses[i].index);
+
             // Overwrite slack type
             if (problem.buses[i].type == BusType.Slack)
                 node.setTypeSolved(NodeType.Slack);

+ 5 - 0
src/holeg/HolegPowerFlowContext.java

@@ -1,6 +1,8 @@
 package holeg;
 
 import holeg.model.Grid;
+import holeg.power_flow.PowerFlowProblem;
+import holeg.power_flow.SolverResult;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -12,6 +14,9 @@ public class HolegPowerFlowContext {
     public float solverTimeMilliseconds;
     public Grid showGridForVisual;
 
+    public PowerFlowProblem problem;
+    public SolverResult result;
+
     public void clearCache() {
         synchronized (lock) {
             lastSolvedGrids.clear();

+ 6 - 0
src/holeg/PowerFlowSettings.java

@@ -42,6 +42,11 @@ public class PowerFlowSettings {
      */
     public double minVoltageUntilInvalid;
 
+    /**
+     * Disables the automatic scaling for the power and voltage values.
+     */
+    public boolean disableAutomaticScaling;
+
     public PowerFlowSettings() {
         solverSettings = new SolverSettings(); // use default
         onlyUpdateGridWhenChanged = true;
@@ -52,6 +57,7 @@ public class PowerFlowSettings {
         slackNodePlacementStrategy = SlackNodePlacementStrategy.MinimizeSlack;
         maxSlackPowerUntilInvalid = Double.MAX_VALUE;
         minVoltageUntilInvalid = 0.3;
+        disableAutomaticScaling = false;
     }
 
     /**

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

@@ -109,6 +109,8 @@ public class GridComparator {
             return false;
         if (a.getOverrideShuntSusceptance() != b.getOverrideShuntSusceptance())
             return false;
+        if (a.getOverrideTapRatio() != b.getOverrideTapRatio())
+            return false;
         return true;
     }
 }

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

@@ -10,6 +10,7 @@ public interface GridEdge {
     boolean getOverrideInPerUnit();
     ComplexNumber getOverrideImpedance();
     double getOverrideShuntSusceptance();
+    double getOverrideTapRatio();
 
     void setCurrent(double current);
     void setPowerFlow(ComplexNumber power);

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

@@ -14,6 +14,7 @@ public interface GridNode {
     ComplexNumber getPowerGeneration();
     Object getTag();
 
+    void setIndex(int index);
     void setVoltage(double voltage);
     void setPhase(double phaseDegrees);
     void setCurrent(double current);

+ 6 - 2
src/holeg/model/NodeType.java

@@ -10,7 +10,11 @@ public enum NodeType {
      */
     Slack,
     /**
-     * The node is a transformer.
+     * The node is a PV node.
      */
-    Transformer
+    PV,
+    /**
+     * The node is PQ node.
+     */
+    PQ
 }

+ 2 - 0
src/holeg/power_flow/Bus.java

@@ -12,6 +12,8 @@ public class Bus {
     public BusType type;
     public Object tag;
 
+    public int index;
+
     public Bus() {
 
     }

+ 13 - 1
src/holeg/power_flow/FlowCalculation.java

@@ -41,14 +41,26 @@ public class FlowCalculation {
             // Power = Current * Voltage
             result.busInjection[i] = busCurrent.get(i, 0).conjugate().multiply(voltages[i]);
         }
+
         for (int i = 0; i < lines.length; i++) {
             Line line = lines[i];
 
             result.lineCurrent[i] = lineCurrent.get(line.from, line.to).len();
-            result.linePower[i] = linePower.get(line.from, line.to).negate();
+            ComplexNumber powerAB = linePower.get(line.from, line.to).negate();
+            ComplexNumber powerBA = linePower.get(line.to, line.from);
+            result.linePower[i] = powerAB.real > powerBA.real ? powerAB : powerBA;
             result.linePowerLoss[i] = linePower.get(line.from, line.to).add(linePower.get(line.to, line.from));
         }
 
+        for (int i = 0; i < buses.length; i++) {
+            result.busInjection[i] = ComplexNumber.Zero;
+
+            for (int j = 0; j < result.linePower.length; j++)
+                 if (lines[j].from == i || lines[j].to == i)
+                    result.busInjection[i] = result.busInjection[i].add(ComplexNumber.One.multiply(result.linePower[j].real));
+        }
+
+
         return result;
     }
 }

+ 8 - 3
src/holeg/power_flow/NewtonRaphsonSolver.java

@@ -186,8 +186,8 @@ public class NewtonRaphsonSolver implements Solver {
                     }
 
                     // Try to find good factor to slow down convergence
-                    factor = 0.1;
-                    try {
+                    factor = 0.5;
+                    /*try {
                         double[] eigenvalues = inverseJ.eig().getRealEigenvalues();
                         double maxEigenvalue = Arrays.stream(eigenvalues).map(Math::abs).max().orElse(0);
 
@@ -196,7 +196,7 @@ public class NewtonRaphsonSolver implements Solver {
                             factor = 1 / maxEigenvalue;
                     } catch (Exception unused) {
                         // ignored
-                    }
+                    }*/
 
                     jacobianRecalculation = settings.jacobianRecalculationInterval;
                 }
@@ -206,6 +206,11 @@ public class NewtonRaphsonSolver implements Solver {
                 // Newton step
                 Matrix x = inverseJ.times(Util.fromArray(error));
 
+                // Check for NaN
+                for (int i = 0; i < x.getRowDimension(); i++)
+                    if (Double.isNaN(x.get(i, 0)))
+                        return SolverResult.error(SolverError.ConvergenceError);
+
                 // Update current state (delta, voltage)
                 for (int i = 0; i < busCount - 1; i++)
                     delta[i + 1] += x.get(i, 0) * factor;

+ 2 - 2
src/holeg/power_flow/PowerFlowProgram.java

@@ -3,7 +3,7 @@ package holeg.power_flow;
 public class PowerFlowProgram {
     public static void main(String[] args) {
         // Load data
-        Bus[] buses = TestData.getIEEE14BussesHugeGeneration();
+        Bus[] buses = TestData.getIEEE14Busses();
         Line[] lines = TestData.getIEEE14Lines();
 
         // Create problem and solver
@@ -14,6 +14,6 @@ public class PowerFlowProgram {
         SolverResult result = solver.solve(problem, new SolverSettings());
 
         // Print result
-        result.printTo(problem, System.out);
+        result.printTo(problem, System.out, "M");
     }
 }

+ 10 - 5
src/holeg/power_flow/SolverResult.java

@@ -33,7 +33,7 @@ public class SolverResult {
         return new SolverResult(false, error, null,null, null);
     }
 
-    public void printTo(PowerFlowProblem problem, PrintStream stream) {
+    public void printTo(PowerFlowProblem problem, PrintStream stream, String unitPrefix) {
         if (problem == null)
             throw new IllegalArgumentException("problem is null");
         if (stream == null)
@@ -54,25 +54,30 @@ public class SolverResult {
 
         // Buses
         for (int i = 0; i < voltages.length; i++) {
-            stream.printf("[Bus %2d %s] Voltage: %5.4f Vpu (%4.2f°), Power: %4.2f kW",
+            stream.printf("[Bus %2d %s] Voltage: %5.4f Vpu (%4.2f°), Power: %4.2f %sW",
                     i,
                     problem.buses[i].type.name(),
                     voltages[i].len(),
                     Math.toDegrees(voltages[i].angle()),
-                    flowData.busInjection[i].real / problem.scalePower);
+                    flowData.busInjection[i].real / problem.scalePower,
+                    unitPrefix);
             stream.println();
         }
 
         // Lines
         for (int i = 0; i < problem.lines.length; i++) {
             Line line = problem.lines[i];
-            stream.printf("[Line %2d -> %2d] Power: %5.3f kW, %5.3f kvar, Loss: %5.3f kW, %5.3f kvar",
+            stream.printf("[Line %2d -> %2d] Power: %5.3f %sW, %5.3f %sVAR, Loss: %5.3f %sW, %5.3f %sVAR",
                     line.from,
                     line.to,
                     flowData.linePower[i].real / problem.scalePower,
+                    unitPrefix,
                     flowData.linePower[i].imaginary / problem.scalePower,
+                    unitPrefix,
                     flowData.linePowerLoss[i].real / problem.scalePower,
-                    flowData.linePowerLoss[i].imaginary / problem.scalePower);
+                    unitPrefix,
+                    flowData.linePowerLoss[i].imaginary / problem.scalePower,
+                    unitPrefix);
             stream.println();
         }
     }

+ 34 - 34
src/holeg/power_flow/TestData.java

@@ -3,20 +3,20 @@ package holeg.power_flow;
 public class TestData {
     public static Bus[] getIEEE14Busses() {
         return new Bus[]{
-                new Bus(1.06, 0, 0, 0, 0, 0, 0, 0, BusType.Slack),
-                new Bus(1.045, 0, 40, 42.4, 21.7, 12.7, -40, 50, BusType.PV),
-                new Bus(1.010, 0, 0, 23.4, 94.2, 19.0, 0, 40, BusType.PV),
-                new Bus(1.0, 0, 0, 0, 47.8, -3.9, 0, 0, BusType.PQ),
-                new Bus(1.0, 0, 0, 0, 7.6, 1.6, 0, 0, BusType.PQ),
-                new Bus(1.070, 0, 0, 12.2, 11.2, 7.5, -6, 24, BusType.PV),
-                new Bus(1.0, 0, 0, 0, 0.0, 0.0, 0, 0, BusType.PQ),
-                new Bus(1.090, 0, 0, 17.4, 0.0, 0.0, -6, 24, BusType.PV),
-                new Bus(1.0, 0, 0, 0, 29.5, 16.6, 0, 0, BusType.PQ),
-                new Bus(1.0, 0, 0, 0, 9.0, 5.8, 0, 0, BusType.PQ),
-                new Bus(1.0, 0, 0, 0, 3.5, 1.8, 0, 0, BusType.PQ),
-                new Bus(1.0, 0, 0, 0, 6.1, 1.6, 0, 0, BusType.PQ),
-                new Bus(1.0, 0, 0, 0, 13.5, 5.8, 0, 0, BusType.PQ),
-                new Bus(1.0, 0, 0, 0, 14.9, 5.0, 0, 0, BusType.PQ)
+                new Bus(1.06, 0, 0, 0, 0, 0, 0, 0, BusType.Slack), // 1
+                new Bus(1.045, 0, 40, 42.4, 21.7, 12.7, -40, 50, BusType.PV), // 2
+                new Bus(1.010, 0, 0, 23.4, 94.2, 19.0, 0, 40, BusType.PV), // 3
+                new Bus(1.0, 0, 0, 0, 47.8, -3.9, 0, 0, BusType.PQ), // 4
+                new Bus(1.0, 0, 0, 0, 7.6, 1.6, 0, 0, BusType.PQ), // 5
+                new Bus(1.070, 0, 0, 12.2, 11.2, 7.5, -6, 24, BusType.PV), // 6
+                new Bus(1.0, 0, 0, 0, 0.0, 0.0, 0, 0, BusType.PQ), // 7
+                new Bus(1.090, 0, 0, 17.4, 0.0, 0.0, -6, 24, BusType.PV), // 8
+                new Bus(1.0, 0, 0, 0, 29.5, 16.6, 0, 0, BusType.PQ), // 9
+                new Bus(1.0, 0, 0, 0, 9.0, 5.8, 0, 0, BusType.PQ), // 10
+                new Bus(1.0, 0, 0, 0, 3.5, 1.8, 0, 0, BusType.PQ), // 11
+                new Bus(1.0, 0, 0, 0, 6.1, 1.6, 0, 0, BusType.PQ), // 12
+                new Bus(1.0, 0, 0, 0, 13.5, 5.8, 0, 0, BusType.PQ),  // 13
+                new Bus(1.0, 0, 0, 0, 14.9, 5.0, 0, 0, BusType.PQ) // 14
         };
     }
 
@@ -79,26 +79,26 @@ public class TestData {
 
     public static Line[] getIEEE14Lines() {
         return new Line[]{
-                new Line(1, 2, 0.01938, 0.05917, 0.0264, 1),
-                new Line(1, 5, 0.05403, 0.22304, 0.0246, 1),
-                new Line(2, 3, 0.04699, 0.19797, 0.0219, 1),
-                new Line(2, 4, 0.05811, 0.17632, 0.0170, 1),
-                new Line(2, 5, 0.05695, 0.17388, 0.0173, 1),
-                new Line(3, 4, 0.06701, 0.17103, 0.0064, 1),
-                new Line(4, 5, 0.01335, 0.04211, 0.0, 1),
-                new Line(4, 7, 0.0, 0.20912, 0.0, 0.978),
-                new Line(4, 9, 0.0, 0.55618, 0.0, 0.969),
-                new Line(5, 6, 0.0, 0.25202, 0.0, 0.932),
-                new Line(6, 11, 0.09498, 0.19890, 0.0, 1),
-                new Line(6, 12, 0.12291, 0.25581, 0.0, 1),
-                new Line(6, 13, 0.06615, 0.13027, 0.0, 1),
-                new Line(7, 8, 0.0, 0.17615, 0.0, 1),
-                new Line(7, 9, 0.0, 0.11001, 0.0, 1),
-                new Line(9, 10, 0.03181, 0.08450, 0.0, 1),
-                new Line(9, 14, 0.12711, 0.27038, 0.0, 1),
-                new Line(10, 11, 0.08205, 0.19207, 0.0, 1),
-                new Line(12, 13, 0.22092, 0.19988, 0.0, 1),
-                new Line(13, 14, 0.17093, 0.34802, 0.0, 1)
+                new Line(1, 2, 0.01938, 0.05917, 0.0264, 1), // 0
+                new Line(1, 5, 0.05403, 0.22304, 0.0246, 1), // 1
+                new Line(2, 3, 0.04699, 0.19797, 0.0219, 1), // 2
+                new Line(2, 4, 0.05811, 0.17632, 0.0170, 1), // 3
+                new Line(2, 5, 0.05695, 0.17388, 0.0173, 1), // 4
+                new Line(3, 4, 0.06701, 0.17103, 0.0064, 1), // 5
+                new Line(4, 5, 0.01335, 0.04211, 0.0, 1), // 6
+                new Line(4, 7, 0.0, 0.20912, 0.0, 0.978), // 7
+                new Line(4, 9, 0.0, 0.55618, 0.0, 0.969), // 8
+                new Line(5, 6, 0.0, 0.25202, 0.0, 0.932), // 9
+                new Line(6, 11, 0.09498, 0.19890, 0.0, 1), // 10
+                new Line(6, 12, 0.12291, 0.25581, 0.0, 1), // 11
+                new Line(6, 13, 0.06615, 0.13027, 0.0, 1), // 12
+                new Line(7, 8, 0.0, 0.17615, 0.0, 1), // 13
+                new Line(7, 9, 0.0, 0.11001, 0.0, 1), // 14
+                new Line(9, 10, 0.03181, 0.08450, 0.0, 1), // 15
+                new Line(9, 14, 0.12711, 0.27038, 0.0, 1), // 16
+                new Line(10, 11, 0.08205, 0.19207, 0.0, 1), // 17
+                new Line(12, 13, 0.22092, 0.19988, 0.0, 1), // 18
+                new Line(13, 14, 0.17093, 0.34802, 0.0, 1) // 19
         };
     }
 

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

@@ -12,6 +12,7 @@ public class SimpleGridEdge implements GridEdge {
     public boolean overrideInPerUnit = false;
     public ComplexNumber overrideImpedance = null;
     public double overrideShuntSusceptance = 0;
+    public double overrideTapRatio = 0;
     public double current = 0;
     public ComplexNumber power = ComplexNumber.Zero;
     public ComplexNumber loss = ComplexNumber.Zero;
@@ -47,6 +48,11 @@ public class SimpleGridEdge implements GridEdge {
         return overrideShuntSusceptance;
     }
 
+    @Override
+    public double getOverrideTapRatio() {
+        return overrideTapRatio;
+    }
+
     @Override
     public void setCurrent(double current) {
         this.current = current;

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

@@ -9,6 +9,7 @@ import java.io.PrintStream;
 import java.util.List;
 
 public class SimpleGridNode implements GridNode {
+    public int index;
     public List<GridEdge> edges;
     public ComplexNumber powerConsumption = ComplexNumber.Zero;
     public ComplexNumber powerGeneration = ComplexNumber.Zero;
@@ -54,6 +55,11 @@ public class SimpleGridNode implements GridNode {
         return tag;
     }
 
+    @Override
+    public void setIndex(int index) {
+        this.index = index;
+    }
+
     @Override
     public void setVoltage(double voltage) {
         this.voltage = voltage;

+ 1 - 1
src/holeg/test_headless/TestHeadlessProgram.java

@@ -33,7 +33,7 @@ public class TestHeadlessProgram {
         // Grid
         Grid grid = builder.getGrid();
         HolegPowerFlow flow = new HolegPowerFlow();
-        flow.solve(grid, PowerFlowSettings.getDefault());
+        flow.solve(grid, PowerFlowSettings.getDefault(), null);
 
         // Show results
         for (GridNode node : grid.getNodes())

+ 32 - 15
src/holeg/test_sensitivity/TestSensitivityProgram.java

@@ -4,7 +4,10 @@ import holeg.power_flow.*;
 
 import java.io.*;
 import java.nio.charset.StandardCharsets;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
 import java.util.List;
 
 public class TestSensitivityProgram {
@@ -13,13 +16,13 @@ public class TestSensitivityProgram {
         Bus[] buses = TestData.getIEEE14Busses();
         Line[] lines = TestData.getIEEE14Lines();
 
-        float xStart = -0.8f;
-        float xEnd = 10.0f;
-        float yStart = -0.8f;
-        float yEnd = 10.0f;
-        float h = 0.5f;
-        int lineIndex = 7;
-        int busIndex = 5;
+        float xStart = 0;
+        float xEnd = 2.0f;
+        float yStart = 0;
+        float yEnd = 2.0f;
+        float h = 0.01f;
+        int[] lineIndex = new int[] { 0, 1 };
+        int busIndex = 3;
 
         List<String> csvOutput = new ArrayList<>();
 
@@ -31,11 +34,18 @@ public class TestSensitivityProgram {
         }
         csvOutput.add(xLine.toString());
 
-        double R = lines[lineIndex].R;
-        double X = lines[lineIndex].X;
+        double[] R = new double[lineIndex.length];
+        double[] X = new double[lineIndex.length];
+
+        for (int i = 0; i < lineIndex.length; i++) {
+            R[i] = lines[lineIndex[i]].R;
+            X[i] = lines[lineIndex[i]].X;
+        }
 
         double Pg = buses[busIndex].Pg;
         double Pl = buses[busIndex].Pl;
+        double Qg = buses[busIndex].Qg;
+        double Ql = buses[busIndex].Ql;
 
         for (float y = yStart; y <= yEnd; y += h) {
             System.out.printf("%f / %f\n", y, yEnd);
@@ -45,13 +55,19 @@ public class TestSensitivityProgram {
             csvLine.append(";");
             for (float x = xStart; x <= xEnd; x += h) {
 
-                Line line = lines[lineIndex];
-                line.R = R * (1 + x);
-                line.X = X * (1 + y);
+                for (int i = 0; i <lineIndex.length; i++) {
+                    Line line = lines[lineIndex[i]];
+                    line.R = R[i] * (1 + x);
+                    line.X = X[i] * (1 + y);
+                }
 
                 /*Bus bus = buses[busIndex];
                 bus.Pg = Pg * (1 + x);
-                bus.Pl = Pl * (1 + y);*/
+                bus.Qg = Qg * (1 + y);*/
+
+                /*Bus bus = buses[busIndex];
+                bus.Pl = Pl * (1 + x);
+                bus.Ql = Ql * (1 + y);*/
 
                 // Create problem and solver
                 PowerFlowProblem problem = new PowerFlowProblem(buses, lines, 1, 1 / 100.0);
@@ -60,7 +76,7 @@ public class TestSensitivityProgram {
                 // Solve the problem with default settings
                 SolverResult result = solver.solve(problem, new SolverSettings());
 
-                if (result.solved)
+                if (result.solved && Arrays.stream(result.voltages).allMatch(v -> v.real > 0.3))
                     csvLine.append(1);
                 else
                     csvLine.append(0);
@@ -69,7 +85,8 @@ public class TestSensitivityProgram {
             csvOutput.add(csvLine.toString());
         }
 
-        writeToFile("C:\\Users\\Henrik\\Desktop\\test.csv", csvOutput);
+        String d = new SimpleDateFormat("yyyy-MM-dd hh-mm-ss").format(new Date());
+        writeToFile("C:\\Users\\hkunz\\Desktop\\test" + d + ".csv", csvOutput);
         System.out.println("Done!");
     }
 

+ 33 - 1
src/holeg/ui/FlowTableWindow.java

@@ -1,14 +1,46 @@
 package holeg.ui;
 
+import holeg.HolegGateway;
+import holeg.HolegPowerFlowContext;
+
 import javax.swing.*;
 import java.awt.*;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
 
 public class FlowTableWindow extends JDialog {
+    private JTextArea textArea;
     public FlowTableWindow(Frame owner) {
         super(owner);
 
         setTitle("HOLEG: Power flow table");
         setSize(500, 600);
-        add(new Label("Work in progress"));
+
+        textArea = (JTextArea) add(new JTextArea("Updating..."));
+    }
+
+    public void update() {
+        try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
+            final String utf8 = StandardCharsets.UTF_8.name();
+            try (PrintStream ps = new PrintStream(stream, true, utf8)) {
+
+                for (HolegPowerFlowContext context : HolegGateway.getALlContext()) {
+                    synchronized (context.lock) {
+                        if (context.result != null && context.problem != null) {
+                            context.result.printTo(context.problem, ps, "M");
+                        }
+                    }
+                }
+            }
+            String data = stream.toString(utf8);
+            textArea.setText(data);
+        } catch (IOException e) {
+            e.printStackTrace();
+
+            textArea.setText(e.toString());
+        }
+        doLayout();
     }
 }

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

@@ -4,6 +4,7 @@ import holeg.HolegGateway;
 import holeg.HolegPowerFlow;
 import holeg.HolegPowerFlowContext;
 import holeg.PowerFlowSettings;
+import holeg.test_sensitivity.TestSensitivityProgram;
 import ui.controller.SimulationManager;
 import ui.controller.SingletonControl;
 import ui.model.Model;
@@ -22,6 +23,7 @@ public class PowerFlowAnalysisMenu extends JMenu {
     private JCheckBoxMenuItem disableUpdates;
     private JCheckBoxMenuItem showResultMessageBox;
     private JCheckBoxMenuItem showDebugMessageBox;
+    private JCheckBoxMenuItem showVoltages;
 
     private static PowerFlowAnalysisMenu instance;
 
@@ -43,6 +45,8 @@ public class PowerFlowAnalysisMenu extends JMenu {
         disableUpdates = (JCheckBoxMenuItem) add(new JCheckBoxMenuItem("Disable automatic updates"));
         showResultMessageBox = (JCheckBoxMenuItem) add(new JCheckBoxMenuItem("Show result message"));
         showDebugMessageBox = (JCheckBoxMenuItem) add(new JCheckBoxMenuItem("Show debug message"));
+        showVoltages = (JCheckBoxMenuItem) add(new JCheckBoxMenuItem("Show voltagess"));
+        showVoltages.setState(true);
 
         settingsWindow = new SettingsWindow(owner, getPowerFlowSettings());
         flowTableWindow = new FlowTableWindow(owner);
@@ -53,6 +57,7 @@ public class PowerFlowAnalysisMenu extends JMenu {
 
         showFlow.addActionListener((e) -> {
             flowTableWindow.setVisible(true);
+            flowTableWindow.update();
         });
 
         clearCache.addActionListener((e) -> {
@@ -68,6 +73,8 @@ public class PowerFlowAnalysisMenu extends JMenu {
         });
 
         instance = this;
+
+        //TestSensitivityProgram.test();
     }
 
     private void clearCache() {
@@ -101,6 +108,8 @@ public class PowerFlowAnalysisMenu extends JMenu {
         return showDebugMessageBox.getState();
     }
 
+    public boolean shouldShowVoltages() { return showVoltages.getState(); }
+
     public static PowerFlowAnalysisMenu getInstance() {
         return instance;
     }

+ 12 - 0
src/holeg/ui/SettingsWindow.java

@@ -42,6 +42,14 @@ public class SettingsWindow extends JDialog {
             settings.solverSettings.maxIterations = e;
         });
 
+        createNumberTextBox("Min error", settings.solverSettings.minError, (e) -> {
+            settings.solverSettings.minError = e;
+        });
+
+        createNumberTextBox("Max error", settings.solverSettings.maxError, (e) -> {
+            settings.solverSettings.maxError = e;
+        });
+
         createNumberTextBox("Jacobian recalculation interval", settings.solverSettings.jacobianRecalculationInterval, (e) -> {
             settings.solverSettings.jacobianRecalculationInterval = e;
         });
@@ -49,6 +57,10 @@ public class SettingsWindow extends JDialog {
         createNumberTextBox("Min voltage until invalid", settings.minVoltageUntilInvalid, (e) -> {
             settings.minVoltageUntilInvalid = e;
         });
+
+        createCheckbox("Disable automatic scaling", settings.disableAutomaticScaling, (e) -> {
+            settings.disableAutomaticScaling = e;
+        });
     }
 
     private void createCheckbox(String label, boolean value, Consumer<Boolean> listener) {

+ 0 - 1
src/ui/controller/CategoryController.java

@@ -52,7 +52,6 @@ public class CategoryController {
 		addNewHolonBattery(mpC.searchCat("Component"), "Battery", "/Images/battery.png");
 
 		addNewHolonObject(mpC.searchCat("Power Flow Analysis"), "Slack", new ArrayList<HolonElement>(), "/Images/slack-node.png");
-		addNewHolonObject(mpC.searchCat("Power Flow Analysis"), "Transformer", new ArrayList<HolonElement>(), "/Images/transformer-1.png");
 	}
 
 	/**

+ 0 - 1
src/ui/controller/ObjectController.java

@@ -39,7 +39,6 @@ public class ObjectController {
         addNewElementIntoCategoryObject("Building", "House", "Light", 5, -50);
         addNewElementIntoCategoryObject("Building", "House", "Solar Panels", 1, 300);
         addNewElementIntoCategoryObject("Power Flow Analysis", "Slack", "SLACK", 1, 0);
-        addNewElementIntoCategoryObject("Power Flow Analysis", "Transformer", "TRANSFORMER", 1, 0);
     }
 
     /**

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

@@ -455,6 +455,7 @@ public class UpdateController {
 			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()});
+			model.getPropertyTable().addRow(new Object[]{"Override tap ratio", model.getSelectedEdge().getOverrideTapRatio()});
             // For edges, the only possible editable cell is the max
 			// flow
 			model.getPropertyTable().setCellEditable(0, 1, false);
@@ -465,6 +466,7 @@ public class UpdateController {
 			model.getPropertyTable().setCellEditable(6, 1, true);
 			model.getPropertyTable().setCellEditable(7, 1, true);
 			model.getPropertyTable().setCellEditable(8, 1, true);
+			model.getPropertyTable().setCellEditable(9, 1, true);
 		} else if (getActualCps() == null) {
 			deleteRows(model.getSingleTable());
 			deleteRows(model.getMultiTable());

+ 7 - 1
src/ui/model/Consumer.java

@@ -14,14 +14,16 @@ public class Consumer extends DecoratedHolonObject {
 	private float energySelfSupplied;
 	private double powerFactor;
 	private boolean isSlack;
+	private int index;
 
-	public Consumer(HolonObject objectToLookAt, float energyNeededFromNetwork, double voltage, double phaseDegrees, double powerFactor, boolean isSlack) {
+	public Consumer(HolonObject objectToLookAt, float energyNeededFromNetwork, double voltage, double phaseDegrees, double powerFactor, boolean isSlack, int index) {
 		super(objectToLookAt);
 		this.energyNeededFromNetwork = energyNeededFromNetwork;
 		this.setVoltage(voltage);
 		this.setPhaseDegrees(phaseDegrees);
 		this.powerFactor = powerFactor;
 		this.isSlack = isSlack;
+		this.index = index;
 	}
 
 	@Override
@@ -99,4 +101,8 @@ public class Consumer extends DecoratedHolonObject {
 	public double getPowerFactor() {
 		return powerFactor;
 	}
+
+	public int getIndex() {
+		return index;
+	}
 }

+ 4 - 4
src/ui/model/DecoratedNetwork.java

@@ -250,10 +250,10 @@ public class DecoratedNetwork {
 			float energyNeeded = hObject.getEnergyNeededFromConsumingElementsWithFlex(Iteration, flexManager);
 			float energySelfProducing = hObject.getEnergySelfProducingFromProducingElementsWithFlex(Iteration, flexManager);
 			if(energyNeeded < energySelfProducing) {
-				Supplier sup = new Supplier(hObject, energySelfProducing - energyNeeded, energyNeeded, 0, 0, false);
+				Supplier sup = new Supplier(hObject, energySelfProducing - energyNeeded, energyNeeded, 0, 0, false, 0);
 				supplierList.add(sup);
 			} else if (energyNeeded > energySelfProducing) {
-				Consumer con = new Consumer(hObject, energyNeeded - energySelfProducing, 0, 0, 0,false);
+				Consumer con = new Consumer(hObject, energyNeeded - energySelfProducing, 0, 0, 0,false, 0);
 				con.setMinimumConsumingElementEnergy(hObject.getMinimumConsumingElementEnergyWithFlex(Iteration, flexManager));
 				con.setEnergyFromConsumingElemnets(hObject.getEnergyNeededFromConsumingElementsWithFlex(Iteration, flexManager));
 				con.setEnergySelfSupplied(hObject.getEnergySelfProducingFromProducingElementsWithFlex(Iteration, flexManager));
@@ -261,10 +261,10 @@ public class DecoratedNetwork {
 			}else if(energyNeeded == energySelfProducing) {
 				
 				if (energySelfProducing == 0.0f) {
-					Passiv pas = new Passiv(hObject);
+					Passiv pas = new Passiv(hObject, 0);
 					passivNoEnergyList.add(pas);
 				} else {
-					Consumer con = new Consumer(hObject, 0.0f, 0, 0,0,false);
+					Consumer con = new Consumer(hObject, 0.0f, 0, 0,0,false, 0);
 					con.setEnergyNeededFromNetwork(0.0f);
 					con.setMinimumConsumingElementEnergy(hObject.getMinimumConsumingElementEnergyWithFlex(Iteration, flexManager));
 					con.setEnergyFromConsumingElemnets(hObject.getEnergyNeededFromConsumingElementsWithFlex(Iteration, flexManager));

+ 9 - 2
src/ui/model/Passiv.java

@@ -3,10 +3,17 @@ package ui.model;
 import classes.HolonObject;
 
 public class Passiv extends DecoratedHolonObject {
-	public Passiv(HolonObject objectToLookAt) {
+	private int index;
+
+	public Passiv(HolonObject objectToLookAt, int index) {
 		super(objectToLookAt);
+		this.index = index;
+	}
+
+	public int getIndex() {
+		return index;
 	}
-	
+
 	@Override
 	float getEnergy() {
 		return 0;

+ 7 - 1
src/ui/model/Supplier.java

@@ -11,8 +11,9 @@ public class Supplier extends DecoratedHolonObject {
 	private float energySupplied;
 	private float energySelfConsuming;
 	private boolean isSlack;
+	private int index;
 
-	public Supplier(HolonObject objectToLookAt, float energyToSupplyNetwork, float energySelfConsuming, double voltage, double phaseDegrees, boolean isSlack) {
+	public Supplier(HolonObject objectToLookAt, float energyToSupplyNetwork, float energySelfConsuming, double voltage, double phaseDegrees, boolean isSlack, int index) {
 		super(objectToLookAt);
 		this.energyToSupplyNetwork = energyToSupplyNetwork;
 		this.energySupplied = 0.0f;
@@ -20,6 +21,7 @@ public class Supplier extends DecoratedHolonObject {
 		this.setVoltage(voltage);
 		this.setPhaseDegrees(phaseDegrees);
 		this.isSlack = isSlack;
+		this.index = index;
 	}
 
 	@Override
@@ -71,4 +73,8 @@ public class Supplier extends DecoratedHolonObject {
 	public boolean isSlack() {
 		return isSlack;
 	}
+
+	public int getIndex() {
+		return index;
+	}
 }

+ 7 - 0
src/ui/view/GUI.java

@@ -1710,6 +1710,13 @@ public class GUI implements CategoryListener {
 						controller.calculateStateAndVisualForCurrentTimeStep();
 					}
 				}
+				else if (selValueY == 9) {
+					float value = Float.parseFloat(temp.toString());
+					if (value >= 0) {
+						model.getSelectedEdge().setOverrideTapRatio(value);
+						controller.calculateStateAndVisualForCurrentTimeStep();
+					}
+				}
 				// Status edition through a check box
 				if (selValueYBool == 3) {
 					Boolean bbTemp = Boolean.parseBoolean(btemp.toString());

+ 4 - 4
src/ui/view/GroupNodeCanvas.java

@@ -356,17 +356,17 @@ public class GroupNodeCanvas extends AbstractCanvas implements MouseListener, Mo
 		g.fillRect(pos.x - controller.getScaleDiv2(), pos.y - controller.getScaleDiv2(), controller.getScale(), controller.getScale());
 		drawCanvasObject(g, decoratedHolonObject.getModel().getImage(), pos);
 	}
-	private void drawCanvasObjectString(Graphics2D g, Position posOfCanvasObject, float energy, double voltage, double phase, double powerFactor, boolean slack) {
-	    MyCanvas.drawCanvasObjectString(controller, g, posOfCanvasObject, energy, voltage, phase, powerFactor, slack);
+	private void drawCanvasObjectString(Graphics2D g, Position posOfCanvasObject, float energy, double voltage, double phase, double powerFactor, boolean slack, int index) {
+	    MyCanvas.drawCanvasObjectString(controller, g, posOfCanvasObject, energy, voltage, phase, powerFactor, slack, index);
 	}
 	private void paintConsumer(Graphics2D g, Consumer con){
 		paintCanvasObject(g, con);
 		paintSupplyBar(g,con.getSupplyBarPercentage(), getStateColor(con.getState()), con.getModel().getPosition());
-		drawCanvasObjectString(g, con.getModel().getPosition(), -con.getEnergyNeededFromNetwork(), con.getVoltage(), con.getPhaseDegrees(), con.getPowerFactor(), con.isSlack());
+		drawCanvasObjectString(g, con.getModel().getPosition(), -con.getEnergyNeededFromNetwork(), con.getVoltage(), con.getPhaseDegrees(), con.getPowerFactor(), con.isSlack(), con.getIndex());
 	}
 	private void paintSupplier(Graphics2D g, Supplier sup){
 		paintCanvasObject(g, sup);
-		drawCanvasObjectString(g, sup.getModel().getPosition(), sup.getEnergyToSupplyNetwork(), sup.getVoltage(), sup.getPhaseDegrees(), -1, sup.isSlack());
+		drawCanvasObjectString(g, sup.getModel().getPosition(), sup.getEnergyToSupplyNetwork(), sup.getVoltage(), sup.getPhaseDegrees(), -1, sup.isSlack(), sup.getIndex());
 	}
 	
 	private void drawCanvasObject(Graphics2D g, String Image, Position pos) {

+ 16 - 10
src/ui/view/MyCanvas.java

@@ -6,6 +6,7 @@ import com.google.gson.JsonParseException;
 
 import holeg.HolegGateway;
 import holeg.HolegPowerFlowContext;
+import holeg.ui.PowerFlowAnalysisMenu;
 import javafx.geometry.Pos;
 import ui.controller.Control;
 import ui.controller.SingletonControl;
@@ -363,29 +364,34 @@ public class MyCanvas extends AbstractCanvas implements MouseListener,
 		g.fillRect(pos.x - controller.getScaleDiv2(), pos.y - controller.getScaleDiv2(), controller.getScale(), controller.getScale());
 		drawCanvasObject(g, decoratedHolonObject.getModel().getImage(), pos);
 	}
-	public static void drawCanvasObjectString(Control controller, Graphics2D g, Position posOfCanvasObject, float energy, double voltage, double phaseDegrees, double powerFactor, boolean isSlack) {
+	public static void drawCanvasObjectString(Control controller, Graphics2D g, Position posOfCanvasObject, float energy, double voltage, double phaseDegrees, double powerFactor, boolean isSlack, int index) {
 		g.setColor(Color.BLACK);
 		g.setFont(new Font("TimesNewRoman", Font.PLAIN, (int) (controller.getScale() / 4f) )); 
 		g.drawString((energy > 0)? "+" + energy : Float.toString(energy), posOfCanvasObject.x - controller.getScaleDiv2(), posOfCanvasObject.y - controller.getScaleDiv2() - 1);
 
 		// Only show voltage when given
-		if (voltage > 0) {
-			if (powerFactor >= 0)
-				g.drawString(String.format("%.0f V (%.1f deg) PF: %d%%", voltage, phaseDegrees, Math.round(powerFactor * 100)), posOfCanvasObject.x - controller.getScaleDiv2(), posOfCanvasObject.y - controller.getScaleDiv2() - 14);
-			else
-				g.drawString(String.format("%.0f V (%.1f deg)", voltage, phaseDegrees), posOfCanvasObject.x - controller.getScaleDiv2(), posOfCanvasObject.y - controller.getScaleDiv2() - 14);
+		if (PowerFlowAnalysisMenu.getInstance().shouldShowVoltages()) {
+			if (voltage > 0) {
+				if (powerFactor >= 0)
+					g.drawString(String.format("%.0f V (%.1f deg) PF: %d%% [%d]", voltage, phaseDegrees, Math.round(powerFactor * 100), index), posOfCanvasObject.x - controller.getScaleDiv2(), posOfCanvasObject.y - controller.getScaleDiv2() - 14);
+				else
+					g.drawString(String.format("%.0f V (%.1f deg) [%d]", voltage, phaseDegrees, index), posOfCanvasObject.x - controller.getScaleDiv2(), posOfCanvasObject.y - controller.getScaleDiv2() - 14);
+			}
+			if (isSlack)
+				g.drawString("SLACK", posOfCanvasObject.x - controller.getScaleDiv2(), posOfCanvasObject.y - controller.getScaleDiv2() - 28);
+		}
+		else {
+			g.drawString(String.format("[%d]", index), posOfCanvasObject.x - controller.getScaleDiv2(), posOfCanvasObject.y - controller.getScaleDiv2() - 14);
 		}
-		if (isSlack)
-			g.drawString("SLACK", posOfCanvasObject.x - controller.getScaleDiv2(), posOfCanvasObject.y - controller.getScaleDiv2() - 28);
 	}
 	private void paintConsumer(Graphics2D g, Consumer con){
 		paintCanvasObject(g, con);
 		paintSupplyBar(g,con.getSupplyBarPercentage(), getStateColor(con.getState()), con.getModel().getPosition());
-		drawCanvasObjectString(controller, g, con.getModel().getPosition(), -con.getEnergyNeededFromNetwork(), con.getVoltage(), con.getPhaseDegrees(), con.getPowerFactor(), con.isSlack());
+		drawCanvasObjectString(controller, g, con.getModel().getPosition(), -con.getEnergyNeededFromNetwork(), con.getVoltage(), con.getPhaseDegrees(), con.getPowerFactor(), con.isSlack(), con.getIndex());
 	}
 	private void paintSupplier(Graphics2D g, Supplier sup){
 		paintCanvasObject(g, sup);
-		drawCanvasObjectString(controller, g, sup.getModel().getPosition(), sup.getEnergyToSupplyNetwork(), sup.getVoltage(), sup.getPhaseDegrees(), -1, sup.isSlack());
+		drawCanvasObjectString(controller, g, sup.getModel().getPosition(), sup.getEnergyToSupplyNetwork(), sup.getVoltage(), sup.getPhaseDegrees(), -1, sup.isSlack(), sup.getIndex());
 	}
 	
 	private void drawCanvasObject(Graphics2D g, String Image, Position pos) {