Browse Source

Remember last solved grids to reuse them for decorating, thus saving the time needed for recalculation

Henrik Kunzelmann 3 years ago
parent
commit
427ec32ed1

BIN
res/Button_Images/Thumbs.db


+ 17 - 1
src/classes/Edge.java

@@ -20,7 +20,11 @@ public class Edge {
     @Expose
     private float realLength = 1;
     @Expose
-    private float override = 1;
+    private float overrideResistance = 0;
+    @Expose
+    private float overrideReactance = 0;
+    @Expose
+    private float overrideShuntSusceptance = 0;
     ArrayList<Integer> tags;
     // for internal use --> flow of electricity (simulation state)
     ArrayList<Integer> pseudoTags;
@@ -45,6 +49,18 @@ public class Edge {
         return realLength;
     }
 
+    public float getOverrideResistance() {
+        return overrideResistance;
+    }
+
+    public float getOverrideReactance() {
+        return overrideReactance;
+    }
+
+    public float getOverrideShuntSusceptance() {
+        return overrideShuntSusceptance;
+    }
+
     @Expose
     private boolean breakedManuel = false;
     @Expose

+ 24 - 10
src/holeg/HolegGateway.java

@@ -3,10 +3,7 @@ package holeg;
 import classes.AbstractCanvasObject;
 import classes.HolonObject;
 import classes.Node;
-import holeg.model.Grid;
-import holeg.model.GridEdge;
-import holeg.model.GridNode;
-import holeg.model.NodeType;
+import holeg.model.*;
 import holeg.power_flow.ComplexNumber;
 import holeg.simple_grid.SimpleGridBuilder;
 import holeg.simple_grid.SimpleGridEdge;
@@ -32,11 +29,11 @@ public class HolegGateway {
             node.tag = object;
 
             if (object.getElements().stream().anyMatch(e -> e.getEleName().equalsIgnoreCase("SLACK")))
-                node.setType(NodeType.Slack);
+                node.typeByDesign = NodeType.Slack;
             else if (object.getElements().stream().anyMatch(e -> e.getEleName().equalsIgnoreCase("TRANSFORMER")))
-                node.setType(NodeType.Transformer);
+                node.typeByDesign = NodeType.Transformer;
             else
-                node.setType(NodeType.Bus);
+                node.typeByDesign = NodeType.Bus;
             canvasToGrid.put(object, node);
         }
         for (AbstractCanvasObject object : network.getNodeAndSwitches()) {
@@ -50,6 +47,8 @@ public class HolegGateway {
             if (a == null || b == null)
                 continue;
             SimpleGridEdge edge = gridBuilder.connect(a, b, cable.getModel().getRealLength());
+            edge.overrideImpedance = new ComplexNumber(cable.getModel().getOverrideResistance(), cable.getModel().getOverrideReactance());
+            edge.overrideShuntSusceptance = cable.getModel().getOverrideShuntSusceptance();
             edge.tag = cable;
         }
         return gridBuilder.getGrid();
@@ -59,9 +58,9 @@ public class HolegGateway {
         for (GridNode node : grid.getNodes()) {
             SimpleGridNode simpleNode = (SimpleGridNode) node;
             if (node.getPowerGeneration().lenSquared() > 0)
-                decoratedNetwork.getSupplierList().add(new Supplier((HolonObject) simpleNode.tag, (float) node.getPowerGeneration().real, 0, simpleNode.voltage, simpleNode.phaseDegrees, simpleNode.type == NodeType.Slack));
+                decoratedNetwork.getSupplierList().add(new Supplier((HolonObject) simpleNode.tag, (float) node.getPowerGeneration().real, 0, simpleNode.voltage, simpleNode.phaseDegrees, simpleNode.typeSolved == 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.type == NodeType.Slack));
+                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));
             else if (simpleNode.tag instanceof HolonObject)
                 decoratedNetwork.getPassivNoEnergyList().add(new Passiv((HolonObject) simpleNode.tag));
         }
@@ -76,9 +75,24 @@ public class HolegGateway {
         }
     }
 
-    public static void solve(MinimumNetwork minimumNetwork, int iteration, FlexManager flexManager, DecoratedNetwork network) {
+    public static void solve(HolegPowerFlowContext context, MinimumNetwork minimumNetwork, int iteration, FlexManager flexManager, DecoratedNetwork network) {
         Grid grid = convert(minimumNetwork, iteration, flexManager);
 
+        // Check if the grid is equal to the one already solved
+        if (context != null) {
+            for (Grid lastSolved : context.lastSolvedGrids) {
+                if (GridComparator.isEqual(lastSolved, grid)) {
+                    decorateNetwork(minimumNetwork, iteration, flexManager, network, lastSolved);
+                    return;
+                }
+            }
+
+            // Keep size constrained to 32 saved grids
+            if (context.lastSolvedGrids.size() >= 32)
+                context.lastSolvedGrids.remove(0);
+            context.lastSolvedGrids.add(grid);
+        }
+
         HolegPowerFlow powerFlow = new HolegPowerFlow();
         GridSolverResult result = powerFlow.solve(grid, PowerFlowSettings.getDefault());
         decorateNetwork(minimumNetwork, iteration, flexManager, network, grid);

+ 18 - 7
src/holeg/HolegPowerFlow.java

@@ -35,7 +35,7 @@ public class HolegPowerFlow {
 
             // Calculate number of calculation tries
             int solveTryCount;
-            if (island.getNodes().stream().anyMatch(n -> n.getType() == NodeType.Slack))
+            if (island.getNodes().stream().anyMatch(n -> n.getTypeByDesign() == NodeType.Slack))
                 solveTryCount = 1;
             else if (settings.slackNodePlacementStrategy == SlackNodePlacementStrategy.MinimizeSlack)
                 solveTryCount = island.getNodes().size();
@@ -199,7 +199,7 @@ public class HolegPowerFlow {
         bus.Ql = Math.abs(node.getPowerConsumption().imaginary);
         bus.tag = node;
 
-        if (node.getType() == NodeType.Slack)
+        if (node.getTypeByDesign() == NodeType.Slack)
             bus.type = BusType.Slack;
         else if (node.getPowerGeneration().isZero()) {
             bus.Pg = 0;
@@ -220,10 +220,17 @@ public class HolegPowerFlow {
     private void createGridLine(Grid grid, List<Line> lines, GridEdge edge) {
         Line line = new Line();
         line.tag = edge;
-        // 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;
+        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;
+        }
+        else {
+            line.R = edge.getOverrideImpedance().real;
+            line.X = edge.getOverrideImpedance().imaginary;
+            line.B_2 = edge.getOverrideShuntSusceptance();
+        }
         line.a = 1;
 
         // Calculate from/to, swap if needed
@@ -413,8 +420,12 @@ public class HolegPowerFlow {
             node.setVoltage(result.voltages[i].len() / problem.scaleVoltage);
             node.setPhase(Math.toDegrees(result.voltages[i].angle()));
             node.setCurrent(result.flowData.busCurrent[i] / currentScale);
+
+            // Overwrite slack type
             if (problem.buses[i].type == BusType.Slack)
-                node.setType(NodeType.Slack);
+                node.setTypeSolved(NodeType.Slack);
+            else
+                node.setTypeSolved(node.getTypeByDesign());
         }
 
         for (int i = 0; i < problem.lines.length; i++) {

+ 11 - 0
src/holeg/HolegPowerFlowContext.java

@@ -0,0 +1,11 @@
+package holeg;
+
+import holeg.model.Grid;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class HolegPowerFlowContext {
+    public PowerFlowSettings settings = PowerFlowSettings.getDefault();
+    public List<Grid> lastSolvedGrids = new ArrayList<>();
+}

+ 2 - 0
src/holeg/PowerFlowSettings.java

@@ -4,6 +4,7 @@ import holeg.power_flow.SolverSettings;
 
 public class PowerFlowSettings {
     public SolverSettings solverSettings;
+    public boolean onlyUpdateGridWhenChanged;
     public boolean skipGridsWithNoProducers;
     public boolean replaceNodeWithSlackNode;
     public SlackNodePlacementStrategy slackNodePlacementStrategy;
@@ -12,6 +13,7 @@ public class PowerFlowSettings {
 
     public PowerFlowSettings() {
         solverSettings = new SolverSettings(); // use default
+        onlyUpdateGridWhenChanged = true;
         skipGridsWithNoProducers = true;
         replaceNodeWithSlackNode = true;
         slackNodePlacementStrategy = SlackNodePlacementStrategy.MinimizeSlack;

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

@@ -0,0 +1,100 @@
+package holeg.model;
+
+import holeg.power_flow.ComplexNumber;
+
+import java.util.HashSet;
+
+public class GridComparator {
+    public static boolean isEqual(Grid a, Grid b) {
+        if (a == b)
+            return true;
+        if (a == null || b == null)
+            return false;
+        if (a.getNodes().size() != b.getNodes().size())
+            return false;
+        if (a.getEdges().size() != b.getEdges().size())
+            return false;
+
+        // Check if nodes are equal
+        HashSet<GridNode> nodesChecked = new HashSet<>();
+        for (GridNode node : a.getNodes()) {
+            // Find a matching node
+            boolean found = false;
+            for (GridNode bNode : b.getNodes()) {
+                if (nodesChecked.contains(bNode))
+                    continue;
+                if (isEqualProperties(node, bNode)) {
+                    found = true;
+                    nodesChecked.add(bNode);
+                    break;
+                }
+            }
+
+            // No node found with matching properties
+            if (!found)
+                return false;
+        }
+
+        // Check if edges are equal
+        HashSet<GridEdge> edgesChecked = new HashSet<>();
+        for (GridEdge edge : a.getEdges()) {
+            // Find a matching edge
+            boolean found = false;
+            for (GridEdge bEdge : b.getEdges()) {
+                if (edgesChecked.contains(bEdge))
+                    continue;
+                if (isEqual(edge, bEdge)) {
+                    found = true;
+                    edgesChecked.add(bEdge);
+                    break;
+                }
+            }
+
+            // No edge found with matching properties
+            if (!found)
+                return false;
+        }
+        return true;
+    }
+
+    private static boolean isEqual(GridEdge a, GridEdge b) {
+        if (!isEqualProperties(a, b))
+            return false;
+
+        if (isEqualProperties(a.getFrom(), b.getFrom()) && isEqualProperties(a.getTo(), b.getTo()))
+            return true;
+        if (isEqualProperties(a.getTo(), b.getFrom()) && isEqualProperties(a.getFrom(), b.getTo()))
+            return true;
+        return false;
+    }
+
+    public static boolean isEqualProperties(GridNode a, GridNode b) {
+        if (a == b)
+            return true;
+        if (a == null || b == null)
+            return false;
+
+        if (!a.getTypeByDesign().equals(b.getTypeByDesign()))
+            return false;
+        if (!ComplexNumber.equals(a.getPowerConsumption(), b.getPowerConsumption()))
+            return false;
+        if (!ComplexNumber.equals(a.getPowerGeneration(), b.getPowerGeneration()))
+            return false;
+        return true;
+    }
+
+    public static boolean isEqualProperties(GridEdge a, GridEdge b) {
+        if (a == b)
+            return true;
+        if (a == null || b == null)
+            return false;
+
+        if (a.getLengthKilometers() != b.getLengthKilometers())
+            return false;
+        if (!ComplexNumber.equals(a.getOverrideImpedance(), b.getOverrideImpedance()))
+            return false;
+        if (a.getOverrideShuntSusceptance() != b.getOverrideShuntSusceptance())
+            return false;
+        return true;
+    }
+}

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

@@ -7,6 +7,8 @@ public interface GridEdge {
     GridNode getTo();
 
     double getLengthKilometers();
+    ComplexNumber getOverrideImpedance();
+    double getOverrideShuntSusceptance();
 
     void setCurrent(double current);
     void setPowerFlow(ComplexNumber power);

+ 3 - 2
src/holeg/model/GridNode.java

@@ -7,12 +7,13 @@ import java.util.List;
 public interface GridNode {
     List<GridEdge> getEdges();
 
-    NodeType getType();
+    NodeType getTypeByDesign();
+    NodeType getTypeSolved();
     ComplexNumber getPowerConsumption();
     ComplexNumber getPowerGeneration();
 
     void setVoltage(double voltage);
     void setPhase(double phaseDegrees);
     void setCurrent(double current);
-    void setType(NodeType type);
+    void setTypeSolved(NodeType type);
 }

+ 27 - 0
src/holeg/power_flow/ComplexNumber.java

@@ -1,5 +1,7 @@
 package holeg.power_flow;
 
+import java.util.Objects;
+
 public class ComplexNumber {
     public final double real;
     public final double imaginary;
@@ -76,4 +78,29 @@ public class ComplexNumber {
             return String.format("%.4f + %.4fj", real, imaginary);
         return String.format("%.4f - %.4fj", real, Math.abs(imaginary));
     }
+
+    public static boolean isNullOrZero(ComplexNumber number) {
+        return number == null || number.isZero();
+    }
+
+    public static boolean equals(ComplexNumber a, ComplexNumber b) {
+        if (a == b)
+            return true;
+        if (a == null || b == null)
+            return false;
+        return a.equals(b);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ComplexNumber that = (ComplexNumber) o;
+        return Double.compare(that.real, real) == 0 && Double.compare(that.imaginary, imaginary) == 0;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(real, imaginary);
+    }
 }

+ 1 - 1
src/holeg/power_flow/NewtonRaphsonSolver.java

@@ -208,7 +208,7 @@ public class NewtonRaphsonSolver implements Solver {
                     V[pq[i]] += x.get(i + busCount - 1, 0) * factor;
             }
 
-            // Tolerance is to high
+            // Tolerance is too high
             if (tolerance >= settings.maxError)
                 return SolverResult.error(SolverError.ConvergenceError);
 

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

@@ -9,6 +9,8 @@ public class SimpleGridEdge implements GridEdge {
     public SimpleGridNode from;
     public SimpleGridNode to;
     public double lengthKilometers = 1;
+    public ComplexNumber overrideImpedance = null;
+    public double overrideShuntSusceptance = 0;
     public double current = 0;
     public ComplexNumber power = ComplexNumber.Zero;
     public ComplexNumber loss = ComplexNumber.Zero;
@@ -29,6 +31,16 @@ public class SimpleGridEdge implements GridEdge {
         return lengthKilometers;
     }
 
+    @Override
+    public ComplexNumber getOverrideImpedance() {
+        return overrideImpedance;
+    }
+
+    @Override
+    public double getOverrideShuntSusceptance() {
+        return overrideShuntSusceptance;
+    }
+
     @Override
     public void setCurrent(double current) {
         this.current = current;

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

@@ -16,11 +16,17 @@ public class SimpleGridNode implements GridNode {
     public double phaseDegrees;
     public double current;
     public Object tag;
-    public NodeType type;
+    public NodeType typeByDesign;
+    public NodeType typeSolved;
 
     @Override
-    public NodeType getType() {
-        return type;
+    public NodeType getTypeByDesign() {
+        return typeByDesign;
+    }
+
+    @Override
+    public NodeType getTypeSolved() {
+        return typeSolved;
     }
 
     @Override
@@ -54,8 +60,8 @@ public class SimpleGridNode implements GridNode {
     }
 
     @Override
-    public void setType(NodeType type) {
-        this.type = type;
+    public void setTypeSolved(NodeType type) {
+        this.typeSolved = type;
     }
 
     public void print(PrintStream stream) {

+ 4 - 1
src/ui/controller/SimulationManager.java

@@ -4,6 +4,7 @@ import classes.*;
 import classes.comparator.EnergyMinToMaxComparator;
 import classes.comparator.MinEnergyComparator;
 import classes.comparator.WeakestBattery;
+import holeg.HolegPowerFlowContext;
 import ui.model.IntermediateCableWithState;
 import ui.controller.FlexManager.FlexState;
 import ui.model.DecoratedCable;
@@ -49,6 +50,8 @@ public class SimulationManager {
 	private int timeStep;
 	private FlexiblePane flexPane;
 
+	private HolegPowerFlowContext holegPowerFlowContext = new HolegPowerFlowContext();
+
 	/**
 	 * Constructor.
 	 *
@@ -134,7 +137,7 @@ public class SimulationManager {
 		ArrayList<DecoratedNetwork> decorNetworks = new ArrayList<DecoratedNetwork>();
 		FairnessModel actualFairnessModel = model.getFairnessModel();
 		for (MinimumNetwork net : list) {
-			decorNetworks.add(new DecoratedNetwork(net, timestep, actualFairnessModel, newFlexManager));
+			decorNetworks.add(new DecoratedNetwork(holegPowerFlowContext, net, timestep, actualFairnessModel, newFlexManager));
 		}
 
 		

+ 3 - 2
src/ui/model/DecoratedNetwork.java

@@ -6,6 +6,7 @@ import java.util.stream.Collectors;
 import classes.HolonElement;
 import classes.HolonObject;
 import holeg.HolegGateway;
+import holeg.HolegPowerFlowContext;
 import ui.controller.FlexManager;
 import ui.model.DecoratedCable.CableState;
 import ui.model.DecoratedHolonObject.HolonObjectState;
@@ -19,7 +20,7 @@ public class DecoratedNetwork {
 	private ArrayList<DecoratedCable> decoratedCableList = new ArrayList<DecoratedCable>();
 	private int timestep;
 
-	public DecoratedNetwork(MinimumNetwork minimumNetwork, int Iteration, FairnessModel actualFairnessModel, FlexManager flexManager){	
+	public DecoratedNetwork(HolegPowerFlowContext context, MinimumNetwork minimumNetwork, int Iteration, FairnessModel actualFairnessModel, FlexManager flexManager){
 		this.timestep = Iteration;
 		switch(actualFairnessModel) {
 		case AllEqual:
@@ -30,7 +31,7 @@ public class DecoratedNetwork {
 			break;
 		case PowerFlowAnalysis:
 			// TODO: (Henrik)
-			HolegGateway.solve(minimumNetwork, Iteration, flexManager, this);
+			HolegGateway.solve(context, minimumNetwork, Iteration, flexManager, this);
 			calculateStates();
 			break;
 		}