2 Achegas 42ff189e48 ... b8c9ccc6a2

Autor SHA1 Mensaxe Data
  Henrik Kunzelmann b8c9ccc6a2 Final version %!s(int64=3) %!d(string=hai) anos
  Henrik Kunzelmann 74feaca94a WIP %!s(int64=3) %!d(string=hai) anos
Modificáronse 40 ficheiros con 483 adicións e 282 borrados
  1. BIN=BIN
      exampleNetworks/holeg_powerflow_test1.holon
  2. BIN=BIN
      exampleNetworks/ieee14_bus.holon
  3. BIN=BIN
      gradle/wrapper/gradle-wrapper.jar
  4. 2 1
      gradle/wrapper/gradle-wrapper.properties
  5. 169 164
      gradlew
  6. 4 10
      gradlew.bat
  7. 3 3
      src/api/EmailNotification.java
  8. 10 0
      src/classes/Edge.java
  9. 2 2
      src/classes/HolonObject.java
  10. 15 6
      src/holeg/HolegGateway.java
  11. 37 8
      src/holeg/HolegPowerFlow.java
  12. 5 0
      src/holeg/HolegPowerFlowContext.java
  13. 6 0
      src/holeg/PowerFlowSettings.java
  14. 2 0
      src/holeg/model/GridComparator.java
  15. 1 0
      src/holeg/model/GridEdge.java
  16. 1 0
      src/holeg/model/GridNode.java
  17. 6 2
      src/holeg/model/NodeType.java
  18. 2 0
      src/holeg/power_flow/Bus.java
  19. 5 1
      src/holeg/power_flow/FlowCalculation.java
  20. 5 1
      src/holeg/power_flow/NewtonRaphsonSolver.java
  21. 2 2
      src/holeg/power_flow/PowerFlowProgram.java
  22. 10 5
      src/holeg/power_flow/SolverResult.java
  23. 34 34
      src/holeg/power_flow/TestData.java
  24. 6 0
      src/holeg/simple_grid/SimpleGridEdge.java
  25. 6 0
      src/holeg/simple_grid/SimpleGridNode.java
  26. 3 3
      src/holeg/test_headless/TestHeadlessProgram.java
  27. 37 15
      src/holeg/test_sensitivity/TestSensitivityProgram.java
  28. 33 1
      src/holeg/ui/FlowTableWindow.java
  29. 9 0
      src/holeg/ui/PowerFlowAnalysisMenu.java
  30. 12 0
      src/holeg/ui/SettingsWindow.java
  31. 0 1
      src/ui/controller/CategoryController.java
  32. 0 1
      src/ui/controller/ObjectController.java
  33. 2 0
      src/ui/controller/UpdateController.java
  34. 7 1
      src/ui/model/Consumer.java
  35. 4 4
      src/ui/model/DecoratedNetwork.java
  36. 9 2
      src/ui/model/Passiv.java
  37. 7 1
      src/ui/model/Supplier.java
  38. 7 0
      src/ui/view/GUI.java
  39. 4 4
      src/ui/view/GroupNodeCanvas.java
  40. 16 10
      src/ui/view/MyCanvas.java

BIN=BIN
exampleNetworks/holeg_powerflow_test1.holon


BIN=BIN
exampleNetworks/ieee14_bus.holon


BIN=BIN
gradle/wrapper/gradle-wrapper.jar


+ 2 - 1
gradle/wrapper/gradle-wrapper.properties

@@ -1,5 +1,6 @@
+#Thu May 27 03:10:34 CEST 2021
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip

+ 169 - 164
gradlew

@@ -1,164 +1,169 @@
-#!/usr/bin/env bash
-
-##############################################################################
-##
-##  Gradle start up script for UN*X
-##
-##############################################################################
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
-
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
-
-warn ( ) {
-    echo "$*"
-}
-
-die ( ) {
-    echo
-    echo "$*"
-    echo
-    exit 1
-}
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-case "`uname`" in
-  CYGWIN* )
-    cygwin=true
-    ;;
-  Darwin* )
-    darwin=true
-    ;;
-  MINGW* )
-    msys=true
-    ;;
-esac
-
-# For Cygwin, ensure paths are in UNIX format before anything is touched.
-if $cygwin ; then
-    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
-fi
-
-# Attempt to set APP_HOME
-# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
-    ls=`ls -ld "$PRG"`
-    link=`expr "$ls" : '.*-> \(.*\)$'`
-    if expr "$link" : '/.*' > /dev/null; then
-        PRG="$link"
-    else
-        PRG=`dirname "$PRG"`"/$link"
-    fi
-done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >&-
-APP_HOME="`pwd -P`"
-cd "$SAVED" >&-
-
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
-
-# Determine the Java command to use to start the JVM.
-if [ -n "$JAVA_HOME" ] ; then
-    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
-        # IBM's JDK on AIX uses strange locations for the executables
-        JAVACMD="$JAVA_HOME/jre/sh/java"
-    else
-        JAVACMD="$JAVA_HOME/bin/java"
-    fi
-    if [ ! -x "$JAVACMD" ] ; then
-        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-    fi
-else
-    JAVACMD="java"
-    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-fi
-
-# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
-    MAX_FD_LIMIT=`ulimit -H -n`
-    if [ $? -eq 0 ] ; then
-        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
-            MAX_FD="$MAX_FD_LIMIT"
-        fi
-        ulimit -n $MAX_FD
-        if [ $? -ne 0 ] ; then
-            warn "Could not set maximum file descriptor limit: $MAX_FD"
-        fi
-    else
-        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
-    fi
-fi
-
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
-    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
-
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
-    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
-    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
-
-    # We build the pattern for arguments to be converted via cygpath
-    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
-    SEP=""
-    for dir in $ROOTDIRSRAW ; do
-        ROOTDIRS="$ROOTDIRS$SEP$dir"
-        SEP="|"
-    done
-    OURCYGPATTERN="(^($ROOTDIRS))"
-    # Add a user-defined pattern to the cygpath arguments
-    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
-        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
-    fi
-    # Now convert the arguments - kludge to limit ourselves to /bin/sh
-    i=0
-    for arg in "$@" ; do
-        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
-        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
-
-        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
-            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
-        else
-            eval `echo args$i`="\"$arg\""
-        fi
-        i=$((i+1))
-    done
-    case $i in
-        (0) set -- ;;
-        (1) set -- "$args0" ;;
-        (2) set -- "$args0" "$args1" ;;
-        (3) set -- "$args0" "$args1" "$args2" ;;
-        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
-        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
-        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
-        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
-        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
-        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
-    esac
-fi
-
-# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
-function splitJvmOpts() {
-    JVM_OPTS=("$@")
-}
-eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
-JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
-
-exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]]; then
+  cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

+ 4 - 10
gradlew.bat

@@ -8,14 +8,14 @@
 @rem Set local scope for the variables with windows NT shell
 if "%OS%"=="Windows_NT" setlocal
 
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
 set DIRNAME=%~dp0
 if "%DIRNAME%" == "" set DIRNAME=.
 set APP_BASE_NAME=%~n0
 set APP_HOME=%DIRNAME%
 
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
 @rem Find java.exe
 if defined JAVA_HOME goto findJavaFromJavaHome
 
@@ -46,10 +46,9 @@ echo location of your Java installation.
 goto fail
 
 :init
-@rem Get command-line arguments, handling Windowz variants
+@rem Get command-line arguments, handling Windows variants
 
 if not "%OS%" == "Windows_NT" goto win9xME_args
-if "%@eval[2+2]" == "4" goto 4NT_args
 
 :win9xME_args
 @rem Slurp the command line arguments.
@@ -60,11 +59,6 @@ set _SKIP=2
 if "x%~1" == "x" goto execute
 
 set CMD_LINE_ARGS=%*
-goto execute
-
-:4NT_args
-@rem Get arguments from the 4NT Shell from JP Software
-set CMD_LINE_ARGS=%$
 
 :execute
 @rem Setup the command line

+ 3 - 3
src/api/EmailNotification.java

@@ -14,7 +14,7 @@ import javax.swing.JPanel;
 import javax.swing.JPasswordField;
 import javax.swing.JTextField;
 
-import org.apache.commons.mail.*;
+//import org.apache.commons.mail.*;
 
 import utility.ImageImport;
 
@@ -136,7 +136,7 @@ public class EmailNotification {
 	}
 
 	public static void sendEmail(EmailSmtpInformation info, String subject, String message) {
-		Email email = new SimpleEmail();
+		/*Email email = new SimpleEmail();
 		email.setHostName(info.Hostname);
 		email.setSmtpPort(info.Port);
 		email.setAuthenticator(new DefaultAuthenticator(info.Username, info.Password));
@@ -149,7 +149,7 @@ public class EmailNotification {
 			email.send();
 		} catch (EmailException e) {
 			e.printStackTrace();
-		}
+		}*/
 	}
 	
 	private static void savePreferences() {

+ 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() {
 
     }

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

@@ -41,14 +41,18 @@ 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));
         }
 
+
         return result;
     }
 }

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

@@ -186,7 +186,6 @@ public class NewtonRaphsonSolver implements Solver {
                     }
 
                     // Try to find good factor to slow down convergence
-                    factor = 0.1;
                     try {
                         double[] eigenvalues = inverseJ.eig().getRealEigenvalues();
                         double maxEigenvalue = Arrays.stream(eigenvalues).map(Math::abs).max().orElse(0);
@@ -206,6 +205,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;

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

@@ -33,12 +33,12 @@ 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())
-            ((SimpleGridNode)node).print(System.out);
+            ((SimpleGridNode)node).print(System.err);
         for (GridEdge edge : grid.getEdges())
-            ((SimpleGridEdge)edge).print(System.out);
+            ((SimpleGridEdge)edge).print(System.err);
     }
 }

+ 37 - 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 = 20.0f;
+        float yStart = 0;
+        float yEnd = 20.0f;
+        float h = 0.1f;
+        int[] lineIndex = new int[] { 6 };
+        int busIndex = 12;
 
         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,24 @@ 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);
+                }*/
+
+                /*Line lineA = lines[lineIndex[0]];
+                lineA.R = R[0] * (1 + x);
+                Line lineB = lines[lineIndex[1]];
+                lineB.R = R[1] * (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 +81,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 +90,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) {