def flatten_dae_variables(model, time): """ This function takes in a (hierarchical, block-structured) Pyomo model and a `ContinuousSet` and returns two lists of "flattened" variables. The first is a list of all `_VarData` that are not indexed by the `ContinuousSet` and the second is a list of `Reference` components such that each reference is indexed only by the specified `ContinuousSet`. This function is convenient for identifying variables that are implicitly indexed by the `ContinuousSet`, for example, a singleton `Var` living on a `Block` that is indexed by the `ContinuousSet`. Parameters ---------- model : Concrete Pyomo model time : ``pyomo.dae.ContinuousSet`` Returns ------- Two lists """ assert time.model() is model.model() block_queue = [model] regular_vars = [] time_indexed_vars = [] while block_queue: b = block_queue.pop(0) b_sets = b.index_set().subsets() if time in b_sets: for _slice in generate_time_indexed_block_slices(b, time): time_indexed_vars.append(Reference(_slice)) continue block_queue.extend(list(b.component_objects(Block, descend_into=False))) for v in b.component_objects(SubclassOf(Var), descend_into=False): v_sets = v.index_set().subsets() if time in v_sets: for _slice in generate_time_only_slices(v, time): time_indexed_vars.append(Reference(_slice)) else: regular_vars.extend(list(v.values())) return regular_vars, time_indexed_vars
def test_set_variance(self): blk = self.make_block() time = blk.time t0 = time.first() variance_list = [(var[t0], 0.05) for var in blk.component_objects(SubclassOf(NmpcVar))] blk.set_variance(variance_list) for var in blk.DIFFERENTIAL_BLOCK[:].var: assert var.variance == 0.05 for var in blk.INPUT_BLOCK[:].var: assert var.variance == 0.05 for var in blk.ALGEBRAIC_BLOCK[:].var: assert var.variance == 0.05 for var in blk.MEASUREMENT_BLOCK[:].var: assert var.variance == 0.05 for var in blk.FIXED_BLOCK[:].var: assert var.variance == 0.05 for var in blk.DERIVATIVE_BLOCK[:].var: assert var.variance == 0.05
def categorize_variables(model, time): assert time.model() is model.model() block_queue = [model] regular_vars = [] time_indexed_vars = [] while block_queue: b = block_queue.pop(0) b_sets = identify_member_sets(b.index_set()) if time in b_sets: for _slice in generate_time_indexed_block_slices(b, time): time_indexed_vars.append(Reference(_slice)) continue block_queue.extend(list(b.component_objects(Block, descend_into=False))) for v in b.component_objects(SubclassOf(Var), descend_into=False): v_sets = identify_member_sets(v.index_set()) if time in v_sets: for _slice in generate_time_only_slices(v, time): time_indexed_vars.append(Reference(_slice)) else: regular_vars.extend(list(v.values())) return regular_vars, time_indexed_vars
def exitNode(self, node, data): if isinstance(node, ProductExpression): ans = self.mcpp.multiply(data[0], data[1]) elif isinstance(node, SumExpression): ans = data[0] for arg in data[1:]: ans = self.mcpp.add(ans, arg) elif isinstance(node, PowExpression): if type(node.arg(1)) == int: ans = self.mcpp.try_binary_fcn(self.mcpp.power, data[0], data[1]) elif type(node.arg(1)) == float: ans = self.mcpp.try_binary_fcn(self.mcpp.powerf, data[0], data[1]) else: ans = self.mcpp.try_binary_fcn(self.mcpp.powerx, data[0], data[1]) elif isinstance(node, ReciprocalExpression): # Note: unreachable after ReciprocalExpression was removed ans = self.mcpp.try_unary_fcn(self.mcpp.reciprocal, data[0]) elif isinstance(node, DivisionExpression): ans = self.mcpp.try_binary_fcn(self.mcpp.divide, data[0], data[1]) elif isinstance(node, NegationExpression): ans = self.mcpp.negation(data[0]) elif isinstance(node, AbsExpression): ans = self.mcpp.try_unary_fcn(self.mcpp.mc_abs, data[0]) elif isinstance(node, LinearExpression): raise NotImplementedError( 'Quicksum has bugs that prevent proper usage of MC++.') # ans = self.mcpp.newConstant(node.constant) # for coef, var in zip(node.linear_coefs, node.linear_vars): # ans = self.mcpp.add( # ans, # self.mcpp.multiply( # self.mcpp.newConstant(coef), # self.register_num(var))) elif isinstance(node, UnaryFunctionExpression): if node.name == "exp": ans = self.mcpp.try_unary_fcn(self.mcpp.exponential, data[0]) elif node.name == "log": ans = self.mcpp.try_unary_fcn(self.mcpp.logarithm, data[0]) elif node.name == "sin": ans = self.mcpp.try_unary_fcn(self.mcpp.trigSin, data[0]) elif node.name == "cos": ans = self.mcpp.try_unary_fcn(self.mcpp.trigCos, data[0]) elif node.name == "tan": ans = self.mcpp.try_unary_fcn(self.mcpp.trigTan, data[0]) elif node.name == "asin": ans = self.mcpp.try_unary_fcn(self.mcpp.atrigSin, data[0]) elif node.name == "acos": ans = self.mcpp.try_unary_fcn(self.mcpp.atrigCos, data[0]) elif node.name == "atan": ans = self.mcpp.try_unary_fcn(self.mcpp.atrigTan, data[0]) elif node.name == "sqrt": ans = self.mcpp.try_unary_fcn(self.mcpp.mc_sqrt, data[0]) else: raise NotImplementedError("Unknown unary function: %s" % (node.name, )) elif isinstance(node, NPV_expressions): ans = self.mcpp.newConstant(value(data[0])) elif type(node) in nonpyomo_leaf_types: ans = self.mcpp.newConstant(node) elif not node.is_expression_type(): ans = self.register_num(node) elif type(node) in SubclassOf(Expression) or isinstance( node, _ExpressionData): ans = data[0] else: raise RuntimeError("Unhandled expression type: %s" % (type(node))) if ans is None: msg = self.mcpp.get_last_exception_message() msg = msg.decode("utf-8") raise MCPP_Error(msg) return ans
def exitNode(self, node, data): if isinstance(node, ProductExpression): ans = self.mcpp.new_mult(data[0], data[1]) elif isinstance(node, SumExpression): ans = data[0] for arg in data[1:]: ans = self.mcpp.new_add(ans, arg) elif isinstance(node, PowExpression): if type(node.arg(1)) == int: ans = self.mcpp.new_power(data[0], data[1]) else: # Non-integer exponent. Must reformulate. # We use x^n = exp(n*log(x)) ans = self.mcpp.new_exponential( self.mcpp.new_mult(data[1], self.mcpp.new_logarithm(data[0]))) elif isinstance(node, ReciprocalExpression): ans = self.mcpp.new_reciprocal( self.mcpp.new_createConstant(1), data[0]) elif isinstance(node, NegationExpression): ans = self.mcpp.new_negation(data[0]) elif isinstance(node, AbsExpression): ans = self.mcpp.new_abs(data[0]) elif isinstance(node, LinearExpression): raise NotImplementedError( 'Quicksum has bugs that prevent proper usage of MC++.') # ans = self.mcpp.new_createConstant(node.constant) # for coef, var in zip(node.linear_coefs, node.linear_vars): # ans = self.mcpp.new_add( # ans, # self.mcpp.new_mult( # self.mcpp.new_createConstant(coef), # self.register_num(var))) elif isinstance(node, UnaryFunctionExpression): if node.name == "exp": ans = self.mcpp.new_exponential(data[0]) elif node.name == "log": ans = self.mcpp.new_logarithm(data[0]) elif node.name == "sin": ans = self.mcpp.new_trigSin(data[0]) elif node.name == "cos": ans = self.mcpp.new_trigCos(data[0]) elif node.name == "tan": ans = self.mcpp.new_trigTan(data[0]) elif node.name == "asin": ans = self.mcpp.new_atrigSin(data[0]) elif node.name == "acos": ans = self.mcpp.new_atrigCos(data[0]) elif node.name == "atan": ans = self.mcpp.new_atrigTan(data[0]) elif node.name == "sqrt": ans = self.mcpp.new_sqrt(data[0]) else: raise NotImplementedError("Unknown unary function: %s" % (node.name,)) elif any(isinstance(node, npv) for npv in NPV_expressions): ans = self.mcpp.new_NPV(value(data[0])) elif type(node) in nonpyomo_leaf_types: ans = self.mcpp.new_createConstant(node) elif not node.is_expression_type(): ans = self.register_num(node) elif type(node) in SubclassOf(Expression): ans = data[0] else: raise RuntimeError("Unhandled expression type: %s" % (type(node))) return ans
def _construct(self): """ Generates time-indexed references and categorizes them. """ model = self.mod time = self.time try: inputs = self._inputs except AttributeError: inputs = self._inputs = None try: measurements = self._measurements except AttributeError: measurements = self._measurements = None # TODO: Give the user the option to provide their own # category_dict (they know the structure of their model # better than I do...) if self._category_dict is not None: category_dict = self.category_dict = self._category_dict if (VC.INPUT not in category_dict and inputs is not None): self.category_dict[VC.INPUT] = inputs if (VC.MEASUREMENT not in category_dict and measurements is not None): self.category_dict[VC.MEASUREMENT] = measurements self.dae_vars = [] for categ, varlist in category_dict.items(): if categ is not VC.MEASUREMENT: # Assume that measurements are duplicates self.dae_vars.extend(varlist) else: scalar_vars, dae_vars = flatten_dae_components( model, time, ctype=Var, ) self.scalar_vars = scalar_vars self.dae_vars = dae_vars category_dict = categorize_dae_variables( dae_vars, time, inputs, measurements=measurements, ) self.category_dict = category_dict keys = list(category_dict) for categ in keys: if not category_dict[categ]: # Empty categories cause problems for us down the road # due empty (unknown dimension) slices. category_dict.pop(categ) self._add_category_blocks() self._add_category_references() self.categories = set(category_dict) # Now that we don't rely on knowing what the categories # will be, we do not need these attributes. They are, however, # used in the tests, so for now, we add them if possible. if VC.DIFFERENTIAL in category_dict: self.differential_vars = category_dict[VC.DIFFERENTIAL] if VC.ALGEBRAIC in category_dict: self.algebraic_vars = category_dict[VC.ALGEBRAIC] if VC.DERIVATIVE in category_dict: self.derivative_vars = category_dict[VC.DERIVATIVE] if VC.INPUT in category_dict: self.input_vars = category_dict[VC.INPUT] if VC.FIXED in category_dict: self.fixed_vars = category_dict[VC.FIXED] if VC.MEASUREMENT in category_dict: self.measurement_vars = category_dict.pop(VC.MEASUREMENT) # The categories in category_dict now form a partition of the # time-indexed variables. This is necessary to have a well-defined # vardata map, which maps each vardata to a unique component indexed # only by time. # Maps each vardata (of a time-indexed var) to the NmpcVar # that contains it. self.vardata_map = ComponentMap( (var[t], var) for var in self.component_objects(SubclassOf(NmpcVar)) #for varlist in category_dict.values() #for var in varlist for t in time if var.ctype is not MeasuredVar) # NOTE: looking up var[t] instead of iterating over values() # appears to be ~ 5x faster # These should be overridden by a call to `set_sample_time` # The defaults assume that the entire model is one sample. self.sample_points = [time.first(), time.last()] self.sample_point_indices = [1, len(time)]