Browse Source

cosim python abstraction for experiments

Cláudio Gomes 4 years ago
parent
commit
b07abc0843
21 changed files with 3942 additions and 0 deletions
  1. 2 0
      .gitignore
  2. 20 0
      ModelicaModels/PowerSystemStandAlone.mo
  3. 3 0
      SemanticAdaptationForFMI/Experiments/power_window_case_study/.gitignore
  4. 1735 0
      SemanticAdaptationForFMI/Experiments/power_window_case_study/crashedlog1.txt
  5. 1735 0
      SemanticAdaptationForFMI/Experiments/power_window_case_study/crashedlog2.txt
  6. 7 0
      SemanticAdaptationForFMI/Experiments/power_window_case_study/fmu20/fmu/cs/.gitignore
  7. BIN
      SemanticAdaptationForFMI/Experiments/power_window_case_study/fmu20/fmu/cs/PowerSystemStandAlone.fmu
  8. 13 0
      SemanticAdaptationForFMI/Experiments/power_window_case_study/fmu20/fmu/cs/check_fmus.ps1
  9. 17 0
      SemanticAdaptationForFMI/FMIAbstraction/.project
  10. 8 0
      SemanticAdaptationForFMI/FMIAbstraction/.pydevproject
  11. 35 0
      SemanticAdaptationForFMI/FMIAbstraction/src/sampleunits/PowerFMU.py
  12. 0 0
      SemanticAdaptationForFMI/FMIAbstraction/src/sampleunits/__init__.py
  13. 1 0
      SemanticAdaptationForFMI/FMIAbstraction/src/scenarios/.gitignore
  14. 52 0
      SemanticAdaptationForFMI/FMIAbstraction/src/scenarios/PowerFMU_Single.py
  15. 0 0
      SemanticAdaptationForFMI/FMIAbstraction/src/scenarios/__init__.py
  16. 0 0
      SemanticAdaptationForFMI/FMIAbstraction/src/test/__init__.py
  17. 180 0
      SemanticAdaptationForFMI/FMIAbstraction/src/units/AbstractSimulationUnit.py
  18. 42 0
      SemanticAdaptationForFMI/FMIAbstraction/src/units/CTSimulationUnit.py
  19. 65 0
      SemanticAdaptationForFMI/FMIAbstraction/src/units/CTSimulationUnit_Euler.py
  20. 27 0
      SemanticAdaptationForFMI/FMIAbstraction/src/units/Utils.py
  21. 0 0
      SemanticAdaptationForFMI/FMIAbstraction/src/units/__init__.py

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+/SemanticAdaptationForFMI/FMIAbstraction/src/sampleunits/*.pyc
+/SemanticAdaptationForFMI/FMIAbstraction/src/units/*.pyc

+ 20 - 0
ModelicaModels/PowerSystemStandAlone.mo

@@ -0,0 +1,20 @@
+model PowerSystemStandAlone
+  parameter Real J = 0.01;
+  parameter Real b = 0.1;
+  parameter Real K = 0.01;
+  parameter Real R = 1;
+  parameter Real L = 0.5;
+  parameter Real V = 12;
+  Real thetha;
+  Real e;
+  Real i;
+  Real T;
+  Real dthetha;
+  input Real tau;
+equation
+  T = K * i;
+  e = K * der(thetha);
+  J * der(dthetha) + b * der(thetha) = T;
+  L * der(i) + R * i = V - e;
+  der(thetha) = dthetha;
+end PowerSystemStandAlone;

+ 3 - 0
SemanticAdaptationForFMI/Experiments/power_window_case_study/.gitignore

@@ -0,0 +1,3 @@
+/log.txt
+/passing_log.txt
+/*.csv

File diff suppressed because it is too large
+ 1735 - 0
SemanticAdaptationForFMI/Experiments/power_window_case_study/crashedlog1.txt


File diff suppressed because it is too large
+ 1735 - 0
SemanticAdaptationForFMI/Experiments/power_window_case_study/crashedlog2.txt


+ 7 - 0
SemanticAdaptationForFMI/Experiments/power_window_case_study/fmu20/fmu/cs/.gitignore

@@ -0,0 +1,7 @@
+/log_echo.txt
+/log_example.txt
+/log_msd.txt
+/log_obstacle.txt
+/log_power_standalone.txt
+/log_window.txt
+/*.csv

BIN
SemanticAdaptationForFMI/Experiments/power_window_case_study/fmu20/fmu/cs/PowerSystemStandAlone.fmu


+ 13 - 0
SemanticAdaptationForFMI/Experiments/power_window_case_study/fmu20/fmu/cs/check_fmus.ps1

@@ -0,0 +1,13 @@
+param (
+	[switch]$nopause = $false
+)
+
+FMUChecker-2.0b3-win32\fmuCheck.win32.exe  -f -e "log_power_standalone.txt"  -l 6  -o "results_power_stand_alone.csv"  "PowerSystemStandAlone.fmu"
+FMUChecker-2.0b3-win32\fmuCheck.win32.exe  -f -e "log_echo.txt"  -l 6  -o "results_echo.csv"  "Echo.fmu"
+FMUChecker-2.0b3-win32\fmuCheck.win32.exe  -f -e "log_msd.txt"  -l 6  -o "results_msd.csv"  "mass_spring_damper.fmu"
+FMUChecker-2.0b3-win32\fmuCheck.win32.exe  -f -e "log_obstacle.txt"  -l 6  -o "results_obstacle.csv"  "Obstacle.fmu"
+FMUChecker-2.0b3-win32\fmuCheck.win32.exe  -f -e "log_window.txt"  -l 6  -o "results_window.csv"  "PW_Window.fmu"
+
+if (-not $nopause) {
+    pause
+}

+ 17 - 0
SemanticAdaptationForFMI/FMIAbstraction/.project

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>FMIAbstraction</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.python.pydev.PyDevBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.python.pydev.pythonNature</nature>
+	</natures>
+</projectDescription>

+ 8 - 0
SemanticAdaptationForFMI/FMIAbstraction/.pydevproject

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?eclipse-pydev version="1.0"?><pydev_project>
+<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
+<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
+<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
+<path>/${PROJECT_DIR_NAME}/src</path>
+</pydev_pathproperty>
+</pydev_project>

+ 35 - 0
SemanticAdaptationForFMI/FMIAbstraction/src/sampleunits/PowerFMU.py

@@ -0,0 +1,35 @@
+from units.CTSimulationUnit_Euler import CTSimulationUnit_Euler
+
+class PowerFMU(CTSimulationUnit_Euler):
+    
+    def __init__(self, name, num_rtol, num_atol, internal_step_size, J, b, K, R, L, V):
+        self.name = name
+        
+        #self.u = "u"
+        #self.d = "d"
+        self.tau = "tau"
+        
+        input_vars = [self.tau]
+        
+        self.theta = "theta"
+        self.omega = "omega"
+        self.i = "i"
+        
+        state_vars = [self.theta, self.omega, self.i]
+        
+        def der_theta(x, u):
+            return x[self.omega]
+        def der_omega(x, u):
+            return (K * x[self.i] - b * x[self.omega] - u[self.tau]) / J
+        def der_i(x, u):
+            return (V - K * x[self.omega] - R * x[self.i]) / L
+        
+        state_derivatives = {
+                             self.theta: der_theta,
+                             self.omega: der_omega,
+                             self.i: der_i
+                             }
+        
+        CTSimulationUnit_Euler.__init__(self, num_rtol, num_atol, internal_step_size, state_derivatives, {}, state_vars, input_vars)
+    
+    

+ 0 - 0
SemanticAdaptationForFMI/FMIAbstraction/src/sampleunits/__init__.py


+ 1 - 0
SemanticAdaptationForFMI/FMIAbstraction/src/scenarios/.gitignore

@@ -0,0 +1 @@
+/results.html

+ 52 - 0
SemanticAdaptationForFMI/FMIAbstraction/src/scenarios/PowerFMU_Single.py

@@ -0,0 +1,52 @@
+from bokeh.plotting import figure, output_file, show
+from sampleunits.PowerFMU import PowerFMU
+import logging
+l = logging.getLogger()
+l.setLevel(logging.DEBUG)
+
+power_fmu = PowerFMU("power", 1e-08, 1e-05, 0.001, J=0.01, b=0.1, K=0.01, R=1, L=0.5, V=12)
+
+power_fmu.enterInitMode()
+
+power_fmu.setValues(0, 0, {power_fmu.i: 0.0,
+                            power_fmu.omega: 0.0,
+                            power_fmu.theta: 0.0,
+                            power_fmu.tau: 0.0
+                            })
+
+power_fmu.exitInitMode()
+
+cosim_step_size = 0.01;
+time = 0.0
+
+l.debug("cosim_step_size=%f", cosim_step_size)
+
+trace_i = [0.0]
+trace_omega = [0.0]
+times = [0.0]
+
+for step in range(1, int(1 / cosim_step_size) + 1):
+    power_fmu.setValues(step, 0, {power_fmu.tau: 0.0})
+    power_fmu.doStep(time, step, 0, cosim_step_size)
+    values = power_fmu.getValues(step, 0, [power_fmu.omega, power_fmu.i])
+    times.append(time)
+    trace_omega.append(values[power_fmu.omega])
+    trace_i.append(values[power_fmu.i])
+    time += step
+
+color_pallete = [
+                "#e41a1c",
+                "#377eb8",
+                "#4daf4a",
+                "#984ea3",
+                "#ff7f00",
+                "#ffff33",
+                "#a65628",
+                "#f781bf"
+                 ]
+
+output_file("./results.html", title="Results")
+p = figure(title="Plot", x_axis_label='time', y_axis_label='')
+p.line(x=range(len(trace_omega)), y=trace_omega, legend="trace_omega", color=color_pallete[0])
+p.line(x=range(len(trace_i)), y=trace_i, legend="trace_i", color=color_pallete[1])
+show(p)

+ 0 - 0
SemanticAdaptationForFMI/FMIAbstraction/src/scenarios/__init__.py


+ 0 - 0
SemanticAdaptationForFMI/FMIAbstraction/src/test/__init__.py


+ 180 - 0
SemanticAdaptationForFMI/FMIAbstraction/src/units/AbstractSimulationUnit.py

@@ -0,0 +1,180 @@
+'''
+Created on Mar 5, 2016
+
+@author: claudio gomes
+'''
+from units.Utils import Utils
+
+import logging
+l = logging.getLogger()
+
+INIT_MODE = "Initialization"
+INSTANTIATED_MODE = "Instantiated"
+STEP_MODE = "Stepping"
+STEP_DISCARD = "Step_Discard"
+STEP_ACCEPT = "Step_Accept"
+
+class AbstractSimulationUnit(object):
+    """
+    Represents an abstraction simulation unit.
+    Each simulation unit stores the following traces:
+        - State trace: a map of vars to lists (string -> list(list(float)))
+            The first list represents a co-simulation step.
+            In each co-simulation step, due to fixed point iterations, one can have many computations of states.
+            This is important to keep track of to later analyze divergence.
+        - Computed time trace: a list of lists.
+            Each list represents the times computed at a specific step, through many iterations.
+    The computed time trace stores the times (as a float) indexed by the step performed by this simulation unit.
+    The same index is used to access the Input, Output and State traces.
+    It is important to keep the times at which simulation was performed 
+    because, internally, different simulation units might compute 
+    at different rates even when they agree on the external 
+    communication step size.
+    """
+    
+    def __init__(self, calc_functions, state_vars, input_vars):
+        l.debug(">AbstractSimulationUnit(%s, %s, %s)", calc_functions, state_vars, input_vars)
+        
+        self.__state = {}
+        self.__computed_time = []
+        self.__calc_functions = calc_functions
+        self.__mode = INSTANTIATED_MODE
+        self.__values_dirty = False
+        
+        self.__state_vars = state_vars
+        self.__input_vars = input_vars
+        self.__non_output_vars = list(state_vars)
+        self.__non_output_vars.extend(input_vars)
+        
+        assert len(self.__non_output_vars) == len(self.__input_vars) + len(self.__state_vars)
+        
+        for var in state_vars:
+            self.__state[var] = []
+        
+        for var in input_vars:
+            self.__state[var] = []
+        
+        l.debug("<AbstractSimulationUnit()")
+    
+    def _getNonOutputVars(self):
+        return self.__non_output_vars
+    
+    def _getInputVars(self):
+        return self.__input_vars
+    
+    def _getStateVars(self):
+        return self.__state_vars
+    
+    def rollback(self, toStep):
+        # Cleans the computed_time from toStep onwards.
+        assert toStep <= len(self.__computed_time), "Cannot rollback to a future step"
+        Utils.trimList(self.__computed_time, toStep)
+        raise "To be finished"
+        
+    def doStep(self, current_time, step, current_iteration, step_size):
+        l.debug(">doStep(t=%f, s=%d, i=%d, H=%f)", current_time, step, current_iteration, step_size)
+        assert self.__mode == STEP_MODE
+        assert step_size > 0
+        
+        if self.__values_dirty:
+            self.__calculateValues(step, current_iteration)
+            self.__values_dirty = False
+        
+        iteration_running = current_iteration > 0
+        
+        # Record time, even if the step is not accepted.
+        if not iteration_running:
+            assert step == len(self.__computed_time)
+            self.__computed_time.append([current_time + step_size])
+        else:
+            assert step == len(self.__computed_time) - 1
+            assert current_iteration == len(self.__computed_time[step])
+            self.__computed_time[step].append(current_time + step_size)
+        
+        result = self._doInternalSteps(current_time, step, current_iteration, step_size, iteration_running)
+        l.debug("<doStep()=%s", result)
+    
+    def _doInternalSteps(self, current_time, step, current_iteration, step_size, iteration_running):
+        raise "Must be implemented in subclasses"
+
+    def __calculateValues(self, current_step, current_iteration):
+        l.debug(">__calculateValues(%d, %d)", current_step, current_iteration)
+        # This does not support algebraic loops.
+        
+        # Get the state and input vars to be used for the computation of the values
+        current_state_snaptshot = {}
+        for var in self.__non_output_vars:
+            # Only up-to-date values go in the calculations.
+            # If a calcfunction crash, it's because this FMU is not being used correctly
+            if current_step < len(self.__state[var]):
+                if current_iteration < self.__state[var][current_step]:
+                    current_state_snaptshot[var] = self.__state[var][current_step][current_iteration]
+        
+        l.debug("current_state_snaptshot = %s", current_state_snaptshot)
+        
+        for var in self.__calc_functions:
+            assert self.__state.has_key(var)
+            new_value = self.__calc_functions[var](current_state_snaptshot)
+            if current_iteration==0:
+                assert current_step == len(self.__state[var])
+                self.__state[var].append([new_value])
+            else:
+                assert current_step == len(self.__state[var])-1
+                assert current_iteration == len(self.__state[var][current_step])
+                self.__state[var][current_step].append(new_value)
+        
+        l.debug("Updated portion of the state:\n%s", self._printState(current_step, current_iteration, self.__calc_functions.keys()))
+        
+        l.debug("<__calculateValues()")
+        
+    def __setDefaultValues(self, values):
+        Utils.copyMapToStateTrace(self.__state, 0, 0, values)
+    
+    def _printState(self,step,iteration, varNames=None):
+        varsToPrint = varNames if varNames != None else self.__state.keys()
+        strState = ""
+        for var in varsToPrint:
+            strState += var + "=" + str(self.__state[var][step][iteration]) + "\n"
+        return strState
+        
+    def setValues(self, current_step, current_iteration, values):
+        l.debug(">setValues(%d, %d, %s)", current_step, current_iteration, values)
+        Utils.copyMapToStateTrace(self.__state, current_step, current_iteration, values)
+        self.__values_dirty = True
+        l.debug("New state: \n%s", self._printState(current_step,current_iteration, values.keys()))
+        l.debug("<setValues()")
+        
+    
+    def getValues(self, current_step, current_iteration, var_names):
+        l.debug(">getValues(%d, %d, %s)", current_step, current_iteration, var_names)
+        if self.__values_dirty:
+            self.__calculateValues(current_step, current_iteration)
+            self.__values_dirty = True
+        
+        values = {}
+        for var in var_names:
+            assert self.__state.has_key(var)
+            assert current_step < len(self.__state[var]), "State is not initialized: " + str(self.__state)
+            assert current_iteration < len(self.__state[var][current_step])
+            values[var] = self.__state[var][current_step][current_iteration]
+        
+        l.debug("<getValues()=%s", values)
+        return values
+        
+    def enterInitMode(self):
+        l.debug(">enterInitMode()")
+        assert len(self.__computed_time) == 0
+        self.__computed_time.append([0.0])
+        
+        self.__mode = INIT_MODE
+        l.debug("<enterInitMode()")
+        
+    def exitInitMode(self):
+        l.debug(">exitInitMode()")
+        if self.__values_dirty:
+            self.__calculateValues(0, 0)
+            self.__values_dirty = False
+        self.__mode = STEP_MODE
+        l.debug("<exitInitMode()")
+        
+    

+ 42 - 0
SemanticAdaptationForFMI/FMIAbstraction/src/units/CTSimulationUnit.py

@@ -0,0 +1,42 @@
+'''
+Created on Mar 5, 2016
+
+@author: claudio gomes
+'''
+import numpy
+
+from units.AbstractSimulationUnit import AbstractSimulationUnit
+
+class CTSimulationUnit(AbstractSimulationUnit):
+    """
+    Represents a continuous time simulation unit.
+    To be subclassed by concrete implementations
+    """
+    
+    def __init__(self, num_rtol, num_atol, calc_functions, state_vars, input_vars):
+        AbstractSimulationUnit.__init__(self, calc_functions, state_vars, input_vars)
+        
+        self._num_rtol = num_rtol
+        self._num_atol = num_atol
+    
+    
+    def _isClose(self, a, b):
+        return numpy.isclose(a,b, self._num_rtol, self._num_atol)
+    
+    def _biggerThan(self, a, b):
+        return not numpy.isclose(a,b, self._num_rtol, self._num_atol) and a > b
+    
+    def _doNumericalSteps(self, current_time, step, current_iteration, step_size, iteration_running):
+        raise("To be implemented by subclasses")
+    
+    def _doInternalSteps(self, current_time, step, current_iteration, step_size, iteration_running):
+        assert self._biggerThan(step_size, 0), "step_size too small: {0}".format(step_size)
+        assert current_iteration >= 0
+        
+        iteration_running = current_iteration > 0
+        
+        return self._doNumericalSteps(current_time, step, current_iteration, step_size, iteration_running)
+                
+                
+                
+                

+ 65 - 0
SemanticAdaptationForFMI/FMIAbstraction/src/units/CTSimulationUnit_Euler.py

@@ -0,0 +1,65 @@
+'''
+Created on Mar 5, 2016
+
+@author: claudio gomes
+'''
+
+from units.AbstractSimulationUnit import STEP_ACCEPT
+from units.CTSimulationUnit import CTSimulationUnit
+import logging
+l = logging.getLogger()
+
+
+class CTSimulationUnit_Euler(CTSimulationUnit):
+    """
+    Represents a continuous time simulation unit that does internal forward euler steps 
+        and does constant input extrapolation
+    To be subclassed by concrete implementations
+    """
+    
+    def __init__(self, num_rtol, num_atol, internal_step_size, state_derivatives, calc_functions, state_vars, input_vars):
+        CTSimulationUnit.__init__(self,num_rtol, num_atol, calc_functions, state_vars, input_vars)
+        
+        self.__state_derivatives = state_derivatives
+        self.__internal_step_size = internal_step_size
+    
+    def _doNumericalSteps(self, current_time, step, current_iteration, step_size, iteration_running):
+        l.debug(">doNumericalSteps(%f, %d, %d, %f, %s)", current_time, step, current_iteration, step_size, iteration_running)
+        
+        assert not iteration_running, "Not supported"
+        
+        num_steps = int(round(step_size / self.__internal_step_size));
+        actual_step_size = step_size / num_steps; # Takes care of internal step size not dividing the co-sim step size.
+        
+        l.debug("num_steps = %d", num_steps)
+        l.debug("actual_step_size = %f", actual_step_size)
+        
+        # Use double buffering in the internal steps. 
+        # This ensures that any computation in the state derivatives use consistent values
+        state_buffers = [self.getValues(step-1, current_iteration, self._getStateVars()), {}]
+        next_state_idx = 1;
+        previous_state_idx = 0;
+        input_buffer = self.getValues(step, current_iteration, self._getInputVars())
+        
+        for microstep in range(num_steps):
+            l.debug("microstep = %d", microstep)
+            l.debug("state = %s", state_buffers[previous_state_idx])
+            for state_var in self._getStateVars():
+                assert self.__state_derivatives.has_key(state_var), "State derivative not provided for state var " + state_var
+                state_derivative_function = self.__state_derivatives[state_var]
+                previous_state = state_buffers[previous_state_idx]
+                der_state_var = state_derivative_function(previous_state, input_buffer)
+                state_buffers[next_state_idx][state_var] = previous_state[state_var] + der_state_var * actual_step_size
+            # TODO Any input extrapolation would be done here by changing the input_buffer
+            
+            # flip the buffers
+            l.debug("next_state_idx = %d, previous_state_idx = %d", next_state_idx, previous_state_idx)
+            next_state_idx = (next_state_idx+1) % 2
+            previous_state_idx = (previous_state_idx+1) % 2
+            l.debug("next_state_idx = %d, previous_state_idx = %d", next_state_idx, previous_state_idx)
+        
+        # Commit the new state
+        self.setValues(step, current_iteration, state_buffers[previous_state_idx])
+        
+        l.debug("<doNumericalSteps() = (%s, %d)", STEP_ACCEPT, step_size)
+        return (STEP_ACCEPT, step_size)

+ 27 - 0
SemanticAdaptationForFMI/FMIAbstraction/src/units/Utils.py

@@ -0,0 +1,27 @@
+'''
+Created on Mar 5, 2016
+
+@author: claudio gomes
+'''
+
+class Utils(object):
+    
+    @staticmethod
+    def copyMapToStateTrace(target, current_step, current_iteration, source):
+        for var in source:
+            assert target.has_key(var)
+            if current_iteration==0:
+                assert current_step == len(target[var])
+                target[var].append([source[var]])
+            else:
+                assert current_step == len(target[var])-1
+                assert current_iteration == len(target[var][current_step])
+                target[var][current_step].append(source[var])
+    
+    @staticmethod
+    def trimList(listToTrim, target_size):
+        assert target_size <= len(listToTrim)
+        assert target_size >= 0
+        while target_size < len(listToTrim):
+            del listToTrim[-1]
+        assert target_size == len(listToTrim)

+ 0 - 0
SemanticAdaptationForFMI/FMIAbstraction/src/units/__init__.py