def test_simple_substitute_param(self): def diffeq(m, t, i): return m.dxdt[t, i] == t * m.x[t, i - 1] ** 2 + m.y ** 2 + m.x[t, i + 1] + m.x[t, i - 1] m = self.m t = IndexTemplate(m.TIME) e = diffeq(m, t, 2) self.assertTrue(isinstance(e, EXPR._ExpressionBase)) _map = {} E = substitute_template_expression(e, substitute_getitem_with_param, _map) self.assertIsNot(e, E) self.assertEqual(len(_map), 3) idx1 = _GetItemIndexer(m.x[t, 1]) self.assertIs(idx1._base, m.x) self.assertEqual(len(idx1._args), 2) self.assertIs(idx1._args[0], t) self.assertEqual(idx1._args[1], 1) self.assertIn(idx1, _map) idx2 = _GetItemIndexer(m.dxdt[t, 2]) self.assertIs(idx2._base, m.dxdt) self.assertEqual(len(idx2._args), 2) self.assertIs(idx2._args[0], t) self.assertEqual(idx2._args[1], 2) self.assertIn(idx2, _map) idx3 = _GetItemIndexer(m.x[t, 3]) self.assertIs(idx3._base, m.x) self.assertEqual(len(idx3._args), 2) self.assertIs(idx3._args[0], t) self.assertEqual(idx3._args[1], 3) self.assertIn(idx3, _map) self.assertFalse(idx1 == idx2) self.assertFalse(idx1 == idx3) self.assertFalse(idx2 == idx3) idx4 = _GetItemIndexer(m.x[t, 2]) self.assertNotIn(idx4, _map) t.set_value(5) self.assertEqual((e._args[0](), e._args[1]()), (10, 136)) self.assertEqual(str(E), "dxdt[{TIME},2] == {TIME} * x[{TIME},1]**2.0 + y**2.0 + x[{TIME},3] + x[{TIME},1]") _map[idx1].set_value(value(m.x[value(t), 1])) _map[idx2].set_value(value(m.dxdt[value(t), 2])) _map[idx3].set_value(value(m.x[value(t), 3])) self.assertEqual((E._args[0](), E._args[1]()), (10, 136)) _map[idx1].set_value(12) _map[idx2].set_value(34) self.assertEqual((E._args[0](), E._args[1]()), (34, 738))
def test_substitute_casadi_intrinsic1(self): m = self.m m.y = Var() t = IndexTemplate(m.t) e = m.v[t] templatemap = {} e2 = substitute_template_expression( e, substitute_getitem_with_casadi_sym, templatemap) e3 = substitute_intrinsic_function( e2, substitute_intrinsic_function_with_casadi) self.assertIs(type(e3), casadi.SX) m.del_component('y')
def test_substitute_casadi_intrinsic3(self): m = self.m m.y = Var() t = IndexTemplate(m.t) e = sin(m.dv[t] + m.v[t]) + log(m.v[t] * m.y + m.dv[t]**2) templatemap = {} e2 = substitute_template_expression( e, substitute_getitem_with_casadi_sym, templatemap) e3 = substitute_intrinsic_function( e2, substitute_intrinsic_function_with_casadi) self.assertIs(e3._args[0]._operator, casadi.sin) self.assertIs(e3._args[1]._operator, casadi.log) m.del_component('y')
def test_simple_substitute_index(self): def diffeq(m, t, i): return m.dxdt[t, i] == t * m.x[t, i]**2 + m.y**2 m = self.m t = IndexTemplate(m.TIME) e = diffeq(m, t, 2) t.set_value(5) self.assertTrue(isinstance(e, EXPR._ExpressionBase)) self.assertEqual((e._args[0](), e._args[1]()), (10, 126)) E = substitute_template_expression(e, substitute_template_with_value) self.assertIsNot(e, E) self.assertEqual(str(E), 'dxdt[5,2] == 5.0 * x[5,2]**2.0 + y**2.0')
def test_simple_substitute_index(self): def diffeq(m, t, i): return m.dxdt[t, i] == t * m.x[t, i] ** 2 + m.y ** 2 m = self.m t = IndexTemplate(m.TIME) e = diffeq(m, t, 2) t.set_value(5) self.assertTrue(isinstance(e, EXPR._ExpressionBase)) self.assertEqual((e._args[0](), e._args[1]()), (10, 126)) E = substitute_template_expression(e, substitute_template_with_value) self.assertIsNot(e, E) self.assertEqual(str(E), "dxdt[5,2] == 5.0 * x[5,2]**2.0 + y**2.0")
def test_substitute_casadi_sym(self): m = self.m m.y = Var() t = IndexTemplate(m.t) e = m.dv[t] + m.v[t] + m.y + t templatemap = {} e2 = substitute_template_expression( e, substitute_getitem_with_casadi_sym, templatemap) self.assertEqual(len(templatemap), 2) self.assertIs(type(e2._args[0]), casadi.SX) self.assertIs(type(e2._args[1]), casadi.SX) self.assertIsNot(type(e2._args[2]), casadi.SX) self.assertIs(type(e2._args[3]), IndexTemplate) m.del_component('y')
def test_substitute_casadi_intrinsic4(self): m = self.m m.y = Var() t = IndexTemplate(m.t) e = m.v[t] * sin(m.dv[t] + m.v[t]) * t templatemap = {} e2 = substitute_template_expression( e, substitute_getitem_with_casadi_sym, templatemap) e3 = substitute_intrinsic_function( e2, substitute_intrinsic_function_with_casadi) self.assertIs(type(e3._numerator[0]), casadi.SX) self.assertIs(e3._numerator[1]._operator, casadi.sin) self.assertIs(type(e3._numerator[2]), IndexTemplate) m.del_component('y')
def test_simple_substitute_param(self): def diffeq(m, t, i): return m.dxdt[t, i] == t*m.x[t, i-1]**2 + m.y**2 + \ m.x[t, i+1] + m.x[t, i-1] m = self.m t = IndexTemplate(m.TIME) e = diffeq(m, t, 2) self.assertTrue(isinstance(e, EXPR._ExpressionBase)) _map = {} E = substitute_template_expression(e, substitute_getitem_with_param, _map) self.assertIsNot(e, E) self.assertEqual(len(_map), 3) idx1 = _GetItemIndexer(m.x[t, 1]) self.assertIs(idx1._base, m.x) self.assertEqual(len(idx1._args), 2) self.assertIs(idx1._args[0], t) self.assertEqual(idx1._args[1], 1) self.assertIn(idx1, _map) idx2 = _GetItemIndexer(m.dxdt[t, 2]) self.assertIs(idx2._base, m.dxdt) self.assertEqual(len(idx2._args), 2) self.assertIs(idx2._args[0], t) self.assertEqual(idx2._args[1], 2) self.assertIn(idx2, _map) idx3 = _GetItemIndexer(m.x[t, 3]) self.assertIs(idx3._base, m.x) self.assertEqual(len(idx3._args), 2) self.assertIs(idx3._args[0], t) self.assertEqual(idx3._args[1], 3) self.assertIn(idx3, _map) self.assertFalse(idx1 == idx2) self.assertFalse(idx1 == idx3) self.assertFalse(idx2 == idx3) idx4 = _GetItemIndexer(m.x[t, 2]) self.assertNotIn(idx4, _map) t.set_value(5) self.assertEqual((e._args[0](), e._args[1]()), (10, 136)) self.assertEqual( str(E), 'dxdt[{TIME},2] == {TIME} * x[{TIME},1]**2.0 + y**2.0 + x[{TIME},3] + x[{TIME},1]' ) _map[idx1].set_value(value(m.x[value(t), 1])) _map[idx2].set_value(value(m.dxdt[value(t), 2])) _map[idx3].set_value(value(m.x[value(t), 3])) self.assertEqual((E._args[0](), E._args[1]()), (10, 136)) _map[idx1].set_value(12) _map[idx2].set_value(34) self.assertEqual((E._args[0](), E._args[1]()), (34, 738))
def __init__(self, m, package='scipy'): self._intpackage = package if self._intpackage not in ['scipy', 'casadi']: raise DAE_Error( "Unrecognized simulator package %s. Please select from " "%s" % (self._intpackage, ['scipy', 'casadi'])) if self._intpackage == 'scipy': if not scipy_available: # Converting this to a warning so that Simulator initialization # can be tested even when scipy is unavailable logger.warning("The scipy module is not available. You may " "build the Simulator object but you will not " "be able to run the simulation.") substituter = substitute_getitem_with_param else: if not casadi_available: # Initializing the simulator for use with casadi requires # access to casadi objects. Therefore, we must throw an error # here instead of a warning. raise ValueError("The casadi module is not available. " "Cannot simulate model.") substituter = substitute_getitem_with_casadi_sym # Check for active Blocks and throw error if any are found if len(list(m.component_data_objects(Block, active=True, descend_into=False))): raise DAE_Error("The Simulator cannot handle hierarchical models " "at the moment.") temp = m.component_map(ContinuousSet) if len(temp) != 1: raise DAE_Error( "Currently the simulator may only be applied to " "Pyomo models with a single ContinuousSet") # Get the ContinuousSet in the model contset = list(temp.values())[0] # Create a index template for the continuous set cstemplate = IndexTemplate(contset) # Ensure that there is at least one derivative in the model derivs = m.component_map(DerivativeVar) if len(derivs) == 0: raise DAE_Error("Cannot simulate a model with no derivatives") templatemap = {} # Map for template substituter rhsdict = {} # Map of derivative to its RHS templated expr derivlist = [] # Ordered list of derivatives alglist = [] # list of templated algebraic equations # Loop over constraints to find differential equations with separable # RHS. Must find a RHS for every derivative var otherwise ERROR. Build # dictionary of DerivativeVar:RHS equation. for con in m.component_objects(Constraint, active=True): # Skip the discretization equations if model is discretized if '_disc_eq' in con.name: continue # Check dimension of the Constraint. Check if the # Constraint is indexed by the continuous set and # determine its order in the indexing sets if con.dim() == 0: continue elif con._implicit_subsets is None: # Check if the continuous set is the indexing set if con._index is not contset: continue else: csidx = 0 noncsidx = (None,) else: temp = con._implicit_subsets dimsum = 0 csidx = -1 noncsidx = None for s in temp: if s is contset: if csidx != -1: raise DAE_Error( "Cannot simulate the constraint %s because " "it is indexed by duplicate ContinuousSets" % con.name) csidx = dimsum elif noncsidx is None: noncsidx = s else: noncsidx = noncsidx.cross(s) dimsum += s.dimen if csidx == -1: continue # Get the rule used to construct the constraint conrule = con.rule for i in noncsidx: # Insert the index template and call the rule to # create a templated expression if i is None: tempexp = conrule(m, cstemplate) else: if not isinstance(i, tuple): i = (i,) tempidx = i[0:csidx] + (cstemplate,) + i[csidx:] tempexp = conrule(m, *tempidx) # Check to make sure it's an _EqualityExpression if not type(tempexp) is EXPR._EqualityExpression: continue # Check to make sure it's a differential equation with # separable RHS args = None # Case 1: m.dxdt[t] = RHS if type(tempexp._args[0]) is EXPR._GetItemExpression: args = _check_getitemexpression(tempexp, 0) # Case 2: RHS = m.dxdt[t] if args is None: if type(tempexp._args[1]) is EXPR._GetItemExpression: args = _check_getitemexpression(tempexp, 1) # Case 3: m.p*m.dxdt[t] = RHS if args is None: if type(tempexp._args[0]) is EXPR._ProductExpression: args = _check_productexpression(tempexp, 0) # Case 4: RHS = m.p*m.dxdt[t] if args is None: if type(tempexp._args[1]) is EXPR._ProductExpression: args = _check_productexpression(tempexp, 1) # Case 5: m.dxdt[t] + CONSTANT = RHS # or CONSTANT + m.dxdt[t] = RHS if args is None: if type(tempexp._args[0]) is EXPR._SumExpression: args = _check_sumexpression(tempexp, 0) # Case 6: RHS = m.dxdt[t] + CONSTANT if args is None: if type(tempexp._args[1]) is EXPR._SumExpression: args = _check_sumexpression(tempexp, 1) # Case 7: RHS = m.p*m.dxdt[t] + CONSTANT # This case will be caught by Case 6 if p is immutable. If # p is mutable then this case will not be detected as a # separable differential equation # At this point if args is not None then args[0] contains # the _GetItemExpression for the DerivativeVar and args[1] # contains the RHS expression. If args is None then the # constraint is considered an algebraic equation if args is None: # Constraint is an algebraic equation or unsupported # differential equation if self._intpackage == 'scipy': raise DAE_Error( "Model contains an algebraic equation or " "unrecognized differential equation. Constraint " "'%s' cannot be simulated using Scipy. If you are " "trying to simulate a DAE model you must use " "CasADi as the integration package." % str(con.name)) tempexp = tempexp._args[0] - tempexp._args[1] algexp = substitute_template_expression(tempexp, substituter, templatemap) algexp = substitute_intrinsic_function( algexp, substitute_intrinsic_function_with_casadi) alglist.append(algexp) continue # Add the differential equation to rhsdict and derivlist dv = args[0] RHS = args[1] dvkey = _GetItemIndexer(dv) if dvkey in rhsdict.keys(): raise DAE_Error( "Found multiple RHS expressions for the " "DerivativeVar %s" % str(dvkey)) derivlist.append(dvkey) tempexp = substitute_template_expression( RHS, substituter, templatemap) if self._intpackage is 'casadi': # After substituting GetItemExpression objects # replace intrinsic Pyomo functions with casadi # functions tempexp = substitute_intrinsic_function( tempexp, substitute_intrinsic_function_with_casadi) rhsdict[dvkey] = tempexp # Check to see if we found a RHS for every DerivativeVar in # the model # FIXME: Not sure how to rework this for multi-index case # allderivs = derivs.keys() # if set(allderivs) != set(derivlist): # missing = list(set(allderivs)-set(derivlist)) # print("WARNING: Could not find a RHS expression for the " # "following DerivativeVar components "+str(missing)) # Create ordered list of differential variables corresponding # to the list of derivatives. diffvars = [] for deriv in derivlist: sv = deriv._base.get_state_var() diffvars.append(_GetItemIndexer(sv[deriv._args])) # Create ordered list of algebraic variables and time-varying # parameters algvars = [] for item in iterkeys(templatemap): if item._base.name in derivs.keys(): # Make sure there are no DerivativeVars in the # template map raise DAE_Error( "Cannot simulate a differential equation with " "multiple DerivativeVars") if item not in diffvars: # Finds time varying parameters and algebraic vars algvars.append(item) if self._intpackage == 'scipy': # Function sent to scipy integrator def _rhsfun(t, x): residual = [] cstemplate.set_value(t) for idx, v in enumerate(diffvars): if v in templatemap: templatemap[v].set_value(x[idx]) for d in derivlist: residual.append(rhsdict[d]()) return residual self._rhsfun = _rhsfun self._contset = contset self._cstemplate = cstemplate self._diffvars = diffvars self._derivlist = derivlist self._templatemap = templatemap self._rhsdict = rhsdict self._alglist = alglist self._algvars = algvars self._model = m self._tsim = None self._simsolution = None # The algebraic vars in the most recent simulation self._simalgvars = None # The time-varying inputs in the most recent simulation self._siminputvars = None