Bladeren bron

refactor some of the classes

Cláudio Gomes 8 jaren geleden
bovenliggende
commit
9c4175e7e1

+ 1 - 3
SemanticAdaptationForFMI/FMIAbstraction/src/sampleunits/PowerFMU.py

@@ -15,8 +15,6 @@ class PowerFMU(CTSimulationUnit_Euler):
         self.omega = "omega"
         self.i = "i"
         
-        state_vars = [self.theta, self.omega, self.i]
-        
         def get_v(up, down):
             return V if up > 0.5 else (-V if down>0.5 else 0.0)
         
@@ -34,6 +32,6 @@ class PowerFMU(CTSimulationUnit_Euler):
                              self.i: der_i
                              }
         
-        CTSimulationUnit_Euler.__init__(self, num_rtol, num_atol, internal_step_size, state_derivatives, {}, state_vars, input_vars)
+        CTSimulationUnit_Euler.__init__(self, name, num_rtol, num_atol, internal_step_size, state_derivatives, {}, input_vars)
     
     

+ 29 - 22
SemanticAdaptationForFMI/FMIAbstraction/src/scenarios/PowerFMU_Single.py

@@ -4,7 +4,13 @@ import logging
 l = logging.getLogger()
 l.setLevel(logging.DEBUG)
 
-power_fmu = PowerFMU("power", 1e-08, 1e-05, 0.001, 
+def env_up(time):
+    return 1.0 if time < 6 else 0.0
+
+def env_down(time):
+    return 0.0 if time < 6 else 1.0
+
+power = PowerFMU("power", 1e-08, 1e-05, 0.001, 
                      J=0.085, 
                      b=5, 
                      K=1.8, 
@@ -12,37 +18,38 @@ power_fmu = PowerFMU("power", 1e-08, 1e-05, 0.001,
                      L=0.036,
                      V=12)
 
-power_fmu.enterInitMode()
+power.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.up: 1.0,
-                            power_fmu.down: 0.0
+power.setValues(0, 0, {power.i: 0.0,
+                            power.omega: 0.0,
+                            power.theta: 0.0,
+                            power.tau: 0.0,
+                            power.up: env_up(0.0),
+                            power.down: env_down(0.0)
                             })
 
-power_fmu.exitInitMode()
-
-cosim_step_size = 0.01;
-time = 0.0
+power.exitInitMode()
 
-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.up: 1.0,
-                                  power_fmu.down: 0.0})
-    power_fmu.doStep(time, step, 0, cosim_step_size)
-    values = power_fmu.getValues(step, 0, [power_fmu.omega, power_fmu.i])
+cosim_step_size = 0.01;
+stop_time = 10.0;
+time = 0.0
+l.debug("cosim_step_size=%f", cosim_step_size)
+
+for step in range(1, int(stop_time / cosim_step_size) + 1):
+    power.setValues(step, 0, {power.tau: 0.0,
+                                  power.up: env_up(time),
+                                  power.down: env_down(time)})
+    power.doStep(time, step, 0, cosim_step_size)
+    values = power.getValues(step, 0, [power.omega, power.i])
     times.append(time)
-    trace_omega.append(values[power_fmu.omega])
-    trace_i.append(values[power_fmu.i])
-    time += step
+    trace_omega.append(values[power.omega])
+    trace_i.append(values[power.i])
+    time += cosim_step_size
 
 color_pallete = [
                 "#e41a1c",

+ 68 - 69
SemanticAdaptationForFMI/FMIAbstraction/src/units/AbstractSimulationUnit.py

@@ -32,32 +32,35 @@ class AbstractSimulationUnit(object):
     communication step size.
     """
     
-    def __init__(self, calc_functions, state_vars, input_vars):
-        l.debug(">AbstractSimulationUnit(%s, %s, %s)", calc_functions, state_vars, input_vars)
+    def __init__(self, name, output_functions, state_vars, input_vars):
+        l.debug(">AbstractSimulationUnit(%s, %s, %s, %s)", name, output_functions, state_vars, input_vars)
         
+        self._name = name
         self.__state = {}
         self.__computed_time = []
-        self.__calc_functions = calc_functions
         self.__mode = INSTANTIATED_MODE
-        self.__values_dirty = False
+        self.__outputs_dirty = False
         
+        self.__out_functions = output_functions
         self.__state_vars = state_vars
         self.__input_vars = input_vars
-        self.__non_output_vars = list(state_vars)
-        self.__non_output_vars.extend(input_vars)
+        self.__output_vars = output_functions.keys()
+        self.__state_and_input_vars = list(state_vars)
+        self.__state_and_input_vars.extend(input_vars)
         
-        assert len(self.__non_output_vars) == len(self.__input_vars) + len(self.__state_vars)
+        assert len(self.__state_and_input_vars) == len(self.__input_vars) + len(self.__state_vars)
         
         for var in state_vars:
             self.__state[var] = []
-        
+        for var in self.__output_vars:
+            self.__state[var] = []
         for var in input_vars:
             self.__state[var] = []
         
         l.debug("<AbstractSimulationUnit()")
     
     def _getInputAndStateVars(self):
-        return self.__non_output_vars
+        return self.__state_and_input_vars
     
     def _getStateVars(self):
         return self.__state_vars
@@ -70,110 +73,106 @@ class AbstractSimulationUnit(object):
         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:
+    
+    def __recordTime(self, current_time, step, current_iteration, step_size):
+        if current_iteration == 0:
             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)
+       
+    def doStep(self, time, step, current_iteration, step_size):
+        l.debug(">%s.doStep(t=%f, s=%d, i=%d, H=%f)", self._name,  time, step, current_iteration, step_size)
+        assert self.__mode == STEP_MODE
+        assert step_size > 0
         
-        result = self._doInternalSteps(current_time, step, current_iteration, step_size, iteration_running)
-        l.debug("<doStep()=%s", result)
+        if self.__outputs_dirty:
+            self.__computeOutputs(step, current_iteration)
+            self.__outputs_dirty = False
+        
+        # Record time, even if the step is not accepted.
+        self.__recordTime(time, step, current_iteration, step_size)
+        
+        result = self._doInternalSteps(time, step, current_iteration, step_size)
+        l.debug("<%s.doStep()=%s", self._name, result)
     
-    def _doInternalSteps(self, current_time, step, current_iteration, step_size, iteration_running):
+    def _doInternalSteps(self, time, step, iteration, cosim_step_size):
         raise "Must be implemented in subclasses"
 
-    def __calculateValues(self, current_step, current_iteration):
-        l.debug(">__calculateValues(%d, %d)", current_step, current_iteration)
+    def __computeOutputs(self, step, iteration):
+        l.debug(">__computeOutputs(%d, %d)", step, 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._getInputAndStateVars():
-            # 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]
+        # Only up-to-date values go in the calculations.
+        # If a calcfunction crashes, it's because this FMU is not being used correctly
+        current_state_snaptshot = Utils.getValuesUpToDate(self.__state, self._getStateVars(), step, iteration)
+        current_input_snaptshot = Utils.getValuesUpToDate(self.__state, self._getInputVars(), step, iteration)
         
         l.debug("current_state_snaptshot = %s", current_state_snaptshot)
+        l.debug("current_input_snaptshot = %s", current_input_snaptshot)
+        
+        l.debug("outputs to update = %s", self.__output_vars)
         
-        for var in self.__calc_functions:
-            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)
+        for var in self.__output_vars:
+            new_value = self.__out_functions[var](current_state_snaptshot, current_input_snaptshot)
+            Utils.copyValueToStateTrace(self.__state, var, step, iteration, new_value)
         
-        l.debug("Updated portion of the state:\n%s", self._printState(current_step, current_iteration, self.__calc_functions.keys()))
+        l.debug("Updated portion of the state:\n%s", 
+                    self._printState(step, iteration, self.__out_functions.keys()))
         
-        l.debug("<__calculateValues()")
+        l.debug("<__computeOutputs()")
         
     def __setDefaultValues(self, values):
         Utils.copyMapToStateTrace(self.__state, 0, 0, values)
     
-    def _printState(self,step,iteration, varNames=None):
+    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 setValues(self, step, iteration, values):
+        l.debug(">%s.setValues(%d, %d, %s)", self._name, step, iteration, values)
+        Utils.copyMapToStateTrace(self.__state, step, iteration, values)
+        self.__outputs_dirty = True
+        l.debug("New state: \n%s", self._printState(step,iteration, values.keys()))
+        l.debug("<%s.setValues()", self._name)
         
     
-    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
+    def getValues(self, step, iteration, var_names):
+        l.debug(">%s.getValues(%d, %d, %s)", self._name, step, iteration, var_names)
+        if self.__outputs_dirty:
+            self.__computeOutputs(step, iteration)
+            self.__outputs_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]
+            assert step < len(self.__state[var]), "State is not initialized: " + str(self.__state)
+            assert iteration < len(self.__state[var][step])
+            values[var] = self.__state[var][step][iteration]
         
-        l.debug("<getValues()=%s", values)
+        l.debug("<%s.getValues()=%s", self._name, values)
         return values
         
     def enterInitMode(self):
-        l.debug(">enterInitMode()")
+        l.debug(">%s.enterInitMode()", self._name)
         assert len(self.__computed_time) == 0
         self.__computed_time.append([0.0])
         
         self.__mode = INIT_MODE
-        l.debug("<enterInitMode()")
+        l.debug("<%s.enterInitMode()", self._name)
         
     def exitInitMode(self):
-        l.debug(">exitInitMode()")
-        if self.__values_dirty:
-            self.__calculateValues(0, 0)
-            self.__values_dirty = False
+        l.debug(">%s.exitInitMode()", self._name)
+        if self.__outputs_dirty:
+            self.__computeOutputs(0, 0)
+            self.__outputs_dirty = False
         self.__mode = STEP_MODE
-        l.debug("<exitInitMode()")
+        l.debug("<%s.exitInitMode()", self._name)
         
     

+ 55 - 14
SemanticAdaptationForFMI/FMIAbstraction/src/units/CTSimulationUnit.py

@@ -3,9 +3,14 @@ Created on Mar 5, 2016
 
 @author: claudio gomes
 '''
+import logging
+
 import numpy
 
-from units.AbstractSimulationUnit import AbstractSimulationUnit
+from units.AbstractSimulationUnit import AbstractSimulationUnit, STEP_ACCEPT
+
+
+l = logging.getLogger()
 
 class CTSimulationUnit(AbstractSimulationUnit):
     """
@@ -13,12 +18,15 @@ class CTSimulationUnit(AbstractSimulationUnit):
     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)
+    def __init__(self, name, num_rtol, num_atol, 
+                                  state_derivatives, output_functions, 
+                                  input_vars):
+        AbstractSimulationUnit.__init__(self, name, output_functions, state_derivatives.keys(), input_vars)
         
         self._num_rtol = num_rtol
         self._num_atol = num_atol
-    
+        
+        self._state_derivatives = state_derivatives
     
     def _isClose(self, a, b):
         return numpy.isclose(a,b, self._num_rtol, self._num_atol)
@@ -26,17 +34,50 @@ class CTSimulationUnit(AbstractSimulationUnit):
     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 _doNumericalStep(self, next_x, previous_x, inputs, time, step, iteration, next_cosim_time):
+        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
+    def _doInternalSteps(self, time, step, iteration, cosim_step_size):
+        l.debug(">%s._doInternalSteps(%f, %d, %d, %f)", self._name, time, step, iteration, cosim_step_size)
+        
+        assert self._biggerThan(cosim_step_size, 0), "cosim_step_size too small: {0}".format(cosim_step_size)
+        assert iteration == 0, "Fixed point iterations not supported yet."
+        
+        # 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, iteration, self._getStateVars()), {}]
+        next_state_idx = 0;
+        previous_state_idx = 1;
+        input_buffer = self.getValues(step, iteration, self._getInputVars())
         
-        iteration_running = current_iteration > 0
+        next_cosim_time = time+cosim_step_size
         
-        return self._doNumericalSteps(current_time, step, current_iteration, step_size, iteration_running)
-                
-                
-                
+        internal_time = time;
+        
+        (do_next_step, micro_step_size) = (True, 0.0)
+        
+        while do_next_step:
+            
+            internal_time += micro_step_size
+            l.debug("internal_time=%f", internal_time)
+            
+            # 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)
+            previous_state = state_buffers[previous_state_idx]
+            next_state = state_buffers[next_state_idx]
+            
+            (do_next_step, micro_step_size) = self._doNumericalStep(next_state, previous_state, input_buffer, internal_time, step, iteration, next_cosim_time)
+        
+        
+        # Commit the new state
+        self.setValues(step, iteration, state_buffers[previous_state_idx])
+        
+        l.debug("<%s._doInternalSteps() = (%s, %d)", self._name, STEP_ACCEPT, cosim_step_size)
+        return (STEP_ACCEPT, cosim_step_size)
+
                 

+ 21 - 45
SemanticAdaptationForFMI/FMIAbstraction/src/units/CTSimulationUnit_Euler.py

@@ -4,7 +4,6 @@ Created on Mar 5, 2016
 @author: claudio gomes
 '''
 
-from units.AbstractSimulationUnit import STEP_ACCEPT
 from units.CTSimulationUnit import CTSimulationUnit
 import logging
 l = logging.getLogger()
@@ -17,50 +16,27 @@ class CTSimulationUnit_Euler(CTSimulationUnit):
     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)
+    def __init__(self, name, num_rtol, num_atol, internal_step_size,
+                 state_derivatives, output_functions, 
+                 input_vars):
+        CTSimulationUnit.__init__(self, name, num_rtol, num_atol,
+                                  state_derivatives, output_functions,
+                                  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.__state_derivatives.keys():
-                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)
+    def _doNumericalStep(self, next_x, previous_x, inputs, time, step, iteration, stop_time):
+        l.debug(">%s._doNumericalStep(%s, %s, %s, %f, %d, %d, %f)", 
+                self._name, next_x, previous_x, inputs, time, step, iteration, stop_time)
+        
+        hit_stop_time = self._biggerThan(time + self.__internal_step_size, stop_time)
+        step_size = stop_time - time if hit_stop_time else self.__internal_step_size
+        for state_var in self._state_derivatives.keys():
+            state_derivative_function = self._state_derivatives[state_var]
+            der_state_var = state_derivative_function(previous_x, inputs)
+            next_x[state_var] = previous_x[state_var] + der_state_var * step_size
+        
+        l.debug("<%s._doNumericalStep()=(%s, %f)", 
+                self._name, not hit_stop_time, step_size)
+        return (not hit_stop_time, step_size)
+    

+ 23 - 9
SemanticAdaptationForFMI/FMIAbstraction/src/units/Utils.py

@@ -7,16 +7,30 @@ Created on Mar 5, 2016
 class Utils(object):
     
     @staticmethod
-    def copyMapToStateTrace(target, current_step, current_iteration, source):
+    def copyMapToStateTrace(target, step, 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])
+            Utils.copyValueToStateTrace(target, var, step, iteration, source)
+    
+    @staticmethod
+    def getValuesUpToDate(source, vars_to_select, step, iteration):
+        result = {}
+        for var in vars_to_select:
+            # Only up-to-date values go in the calculations.
+            # If a calcfunction crash, it's because this FMU is not being used correctly
+            if step < len(source[var]):
+                if iteration < source[var][step]:
+                    result[var] = source[var][step][iteration]
+        return result
+    
+    @staticmethod
+    def copyValueToStateTrace(target, var, step, iteration, source):
+        assert target.has_key(var)
+        if step == len(target[var]):
+            target[var].append([source[var]])
+        elif iteration == len(target[var][step]):
+            target[var][step].append(source[var])
+        else:
+            target[var][step][iteration] = source[var]
     
     @staticmethod
     def trimList(listToTrim, target_size):