def __init__( self, name, grid, grid_backend=None, params=StandardParameters(), verbose=False, **kwargs, ): UnitConverter.__init__(self, **kwargs) if verbose: self.print_base_units() self.name = name self.grid = grid self.grid_backend = grid_backend self.sub = grid.sub self.bus = grid.bus self.line = grid.line self.gen = grid.gen self.load = grid.load self.ext_grid = grid.ext_grid self.trafo = grid.trafo self.model = None self.params = params self.solver = pyo_opt.SolverFactory(self.params.solver_name) self.solver_status = None # Results self._initialize_results()
def optimal_schedule(m, p, nfd, pfd, solver_name, time_limit): import pyomo.opt as opt # This import is required for pyomo to recognise solvers import pyomo.environ as en solver = opt.SolverFactory(solver_name) model = create_model(m, p, nfd, pfd) if time_limit >= 0: if solver_name == 'cplex': solver.options['timelimit'] = time_limit elif solver_name == 'glpk': solver.options['tmlim'] = time_limit try: result = solver.solve(model) except Exception as error: return False, str(error) C_max = model.C_max.value # No solution if C_max == None: return False, 'No feasible solution found' # Construct schedule n = len(p) S = np.zeros((m, n), dtype=bool) for i in range(m): for j in range(n): S[i, j] = model.x[i, j].value != 0 return True, S
def optModel(self, **kwargs): self.solver = kwargs.get('solver', 'gurobi') self.optprob = opt.SolverFactory(self.solver) self.optprob.options["timeLimit"] = kwargs.get('timeLimit', 2000) self.optprob.options["threads"] = kwargs.get('threads', 7) self.optprob.options["MIPgap"] = kwargs.get('gap', 0.005) self.optprob.options["Heuristics"] = 0.5 logfile = os.path.join(kwargs.get('logPath', ""), "GurobiLog.txt") self.optprob.options["logfile"] = kwargs.get('logfile', logfile) self.optiRes = self.optprob.solve(self.M, tee=kwargs.get('tee', True))
def solve(m): """Solve the model.""" solver = po.SolverFactory('glpk') results = solver.solve( m ) #, tee=True, keepfiles=False, options_string="mip_tolerances_integrality=1e-9 mip_tolerances_mipgap=0") if (results.solver.status != pyomo.opt.SolverStatus.ok): logging.warning('Check solver not ok?') if (results.solver.termination_condition != pyomo.opt.TerminationCondition.optimal): logging.warning('Check solver optimality?') return results
def solve(self): solver = po.SolverFactory(self.solver_name) solver.options['timelimit'] = self.solver_max_runtime # if we're running things remotely, then we will use the NEOS server (https://neos-server.org/neos/) if self.solver_run_remotely: solver_manager = po.SolverManagerFactory('neos') results = solver_manager.solve(self.model, opt=solver) else: # tee=True displays solver output in the terminal # keepfiles=True keeps files passed to and from the solver results = solver.solve(self.model, tee=True, keepfiles=False) if (results.solver.status == po.SolverStatus.ok) and ( results.solver.termination_condition == po.TerminationCondition.optimal): print('this is feasible and optimal') elif results.solver.termination_condition == po.TerminationCondition.infeasible: print('infeasible') else: # something else is wrong print(results.solver)
def _k_medoids_exact(self, distances, n_clusters): """ Parameters ---------- distances : int, required Pairwise distances between each row. n_clusters : int, required Number of clusters. """ # Create model M = pyomo.ConcreteModel() # get distance matrix M.d = distances # set number of clusters M.no_k = n_clusters # Distances is a symmetrical matrix, extract its length length = distances.shape[0] # get indices M.i = [j for j in range(length)] M.j = [j for j in range(length)] # initialize vars M.z = pyomo.Var(M.i, M.j, within=pyomo.Binary) M.y = pyomo.Var(M.i, within=pyomo.Binary) # get objective def objRule(M): return sum(sum(M.d[i, j] * M.z[i, j] for j in M.j) for i in M.i) M.obj = pyomo.Objective(rule=objRule) # s.t. # Assign all candidates to clusters def candToClusterRule(M, j): return sum(M.z[i, j] for i in M.i) == 1 M.candToClusterCon = pyomo.Constraint(M.j, rule=candToClusterRule) # no of clusters def noClustersRule(M): return sum(M.y[i] for i in M.i) == M.no_k M.noClustersCon = pyomo.Constraint(rule=noClustersRule) # cluster relation def clusterRelationRule(M, i, j): return M.z[i, j] <= M.y[i] M.clusterRelationCon = pyomo.Constraint(M.i, M.j, rule=clusterRelationRule) # create optimization problem optprob = opt.SolverFactory(self.solver) if self.solver == 'gurobi': optprob.set_options("Threads=" + str(self.threads) + " TimeLimit=" + str(self.timelimit)) results = optprob.solve(M, tee=False) # check that it does not fail if self.solver == 'gurobi' and results['Solver'][0][ 'Termination condition'].index == 11: print(results['Solver'][0]['Termination message']) return False elif self.solver == 'gurobi' and not results['Solver'][0][ 'Termination condition'].index in [2, 7, 8, 9, 10]: # optimal raise ValueError(results['Solver'][0]['Termination message']) # Get results r_x = np.array([[round(M.z[i, j].value) for i in range(length)] for j in range(length)]) r_y = np.array([round(M.y[j].value) for j in range(length)]) r_obj = pyomo.value(M.obj) return (r_y, r_x.T, r_obj)
def _solve_model(model, solver, mipgap=0.001, timelimit=None, solver_tee=True, symbolic_solver_labels=False, solver_options=None, solve_method_options=None, return_solver=False, vars_to_load=None, set_instance=True): ''' Create and solve an Egret power system optimization model Parameters ---------- model : pyomo.environ.ConcreteModel A pyomo ConcreteModel object. solver : str or pyomo.opt.base.solvers.OptSolver Either a string specifying a pyomo solver name, or an instanciated pyomo solver mipgap : float (optional) Mipgap to use for unit commitment solve; default is 0.001 timelimit : float (optional) Time limit for unit commitment run. Default of None results in no time limit being set -- runs until mipgap is satisfied solver_tee : bool (optional) Display solver log. Default is True. symbolic_solver_labels : bool (optional) Use symbolic solver labels. Useful for debugging; default is False. solver_options : dict (optional) Other options to pass into the solver. Default is dict(). solve_method_options : dict (optional) Other options to pass into the pyomo solve method. Default is dict(). return_solver : bool (optional) Returns the solver object vars_to_load : list (optional) When supplied, and the solver is persistent, this will just load pyomo variables specificed set_instance : bool When the solver is persistent, this controls whether set_instance is called. Default is True Returns ------- ''' results = None ## termination conditions which are acceptable safe_termination_conditions = [ po.TerminationCondition.maxTimeLimit, po.TerminationCondition.maxIterations, po.TerminationCondition.minFunctionValue, po.TerminationCondition.minStepLength, po.TerminationCondition.globallyOptimal, po.TerminationCondition.locallyOptimal, po.TerminationCondition.feasible, po.TerminationCondition.optimal, po.TerminationCondition.maxEvaluations, po.TerminationCondition.other, ] if isinstance(solver, str): solver = po.SolverFactory(solver) elif isinstance(solver, po.base.OptSolver): pass else: raise Exception( 'solver must be string or an instanciated pyomo solver') _set_options(solver, mipgap, timelimit, solver_options) if solve_method_options is None: solve_method_options = dict() if isinstance(solver, PersistentSolver): if set_instance: solver.set_instance(model, symbolic_solver_labels=symbolic_solver_labels) results = solver.solve(model, tee=solver_tee, load_solutions=False, save_results=False, **solve_method_options) else: results = solver.solve(model, tee=solver_tee, \ symbolic_solver_labels=symbolic_solver_labels, load_solutions=False, **solve_method_options) if results.solver.termination_condition not in safe_termination_conditions: raise Exception( 'Problem encountered during solve, termination_condition {}'. format(results.solver.termination_condition)) if isinstance(solver, PersistentSolver): solver.load_vars(vars_to_load) if vars_to_load is None: if hasattr(model, "dual"): solver.load_duals() if hasattr(model, "slack"): solver.load_slacks() else: model.solutions.load_from(results) if return_solver: return model, results, solver return model, results
from __future__ import division from pyomo.environ import * from pyomo import opt # create the model model = ConcreteModel() model.x = Var() model.p = Param(mutable=True) # allows the parameter value to be updated model.o = Objective(expr=model.x) model.c = Constraint(expr=model.x >= model.p) # create a solver solver = opt.SolverFactory('cbc') # set the initial parameter value model.p.value = 2.0 model.pprint() # solve the model (the solution is loaded automatically) results = solver.solve(model) # print the solver termination condition print(results.solver.termination_condition) # update the parameter value model.p.value = 3.0 model.pprint() # solve the model again
def optNLModel(self): self.solver = 'ipopt' self.solver_io = 'nl' self.optprob = opt.SolverFactory(self.solver, solver_io=self.solver_io) self.optiRes = self.optprob.solve(self.M, tee=kwargs.get( 'tee', True)) #, warmstart=True)
def run(self): """Calculate optimal allocation for one time step. """ # Create a concrete model model = pyomo.ConcreteModel() solver = opt.SolverFactory('glpk') # Determine node types node_types = dict() nodes = dict() node_names = dict() # Needed to build connectivity matrix from links id_generator = self._assign_id() for node in self.target.nodes: n_id = id_generator.next() nodes[n_id] = node node_names[node.name] = n_id if node.type not in node_types.keys(): node_types[node.type] = [n_id] else: node_types[node.type].append(n_id) # Determine link types link_types = dict() links = dict() connectivity = dict() for i in nodes.keys(): for j in nodes.keys(): connectivity[i, j] = 0 for link in self.target.links: l_id = id_generator.next() links[l_id] = link startnode_id = node_names[link.start_node.name] endnode_id = node_names[link.end_node.name] connectivity[startnode_id, endnode_id] = l_id if link.type not in link_types.keys(): link_types[link.type] = [l_id] else: link_types[link.type].append(l_id) # Define node and link type sets model.nodes = pyomo.Set(initialize=nodes.keys()) model.InAndOut = pyomo.Set(initialize=node_types['InAndOut'], domain=pyomo.NonNegativeIntegers) model.Junction = pyomo.Set(initialize=node_types['Junction'], domain=pyomo.NonNegativeIntegers) model.Reservoir = pyomo.Set(initialize=node_types['Reservoir'], domain=pyomo.NonNegativeIntegers) model.Demand = pyomo.Set(initialize=node_types['Demand'], domain=pyomo.NonNegativeIntegers) model.links = pyomo.Set(initialize=links.keys()) model.Channel = pyomo.Set(initialize=link_types['Channel'], domain=pyomo.NonNegativeIntegers) # Assign parameters # 'InAndOut' and 'Junction' nodes do not have any data #-- Reservoir init_stor = dict() max_stor = dict() min_stor = dict() carryover_penalty = dict() for res in node_types['Reservoir']: # Assign storage of the last simulation, if available. Use # init_stor otherwise if len(nodes[res]._history['S']) == 0: init_stor[res] = nodes[res].init_stor else: init_stor[res] = nodes[res]._history['S'][-1] max_stor[res] = nodes[res].max_stor min_stor[res] = nodes[res].min_stor carryover_penalty[res] = nodes[res].carryover_penalty model.Res_init_stor = pyomo.Param(model.Reservoir, within=pyomo.Reals, initialize=init_stor) model.Res_max_stor = pyomo.Param(model.Reservoir, within=pyomo.Reals, initialize=max_stor) model.Res_min_stor = pyomo.Param(model.Reservoir, within=pyomo.Reals, initialize=min_stor) model.Res_carryover_penalty = pyomo.Param(model.Reservoir, within=pyomo.Reals, initialize=carryover_penalty) #-- Demand cons_coeff = dict() for dem in node_types['Demand']: cons_coeff[dem] = nodes[dem].consumption_coeff model.Dem_consumption_coeff = pyomo.Param(model.Demand, within=pyomo.Reals, initialize=cons_coeff) #-- Channel cost = dict() flowmult = dict() max_flow = dict() min_flow = dict() for cha in link_types['Channel']: cost[cha] = links[cha].cost flowmult[cha] = links[cha].flowmult max_flow[cha] = links[cha].max_flow min_flow[cha] = links[cha].min_flow model.Cha_cost = pyomo.Param(model.Channel, within=pyomo.Reals, initialize=cost) model.Cha_flowmult = pyomo.Param(model.Channel, within=pyomo.Reals, initialize=flowmult) model.Cha_max_flow = pyomo.Param(model.Channel, within=pyomo.Reals, initialize=max_flow) model.Cha_min_flow = pyomo.Param(model.Channel, within=pyomo.Reals, initialize=min_flow) # Connectivity matrix model.connectivity = pyomo.Param(model.nodes, model.nodes, within=pyomo.NonNegativeIntegers, initialize=connectivity) # Assign variables #-- InAndOut model.InA_Q = pyomo.Var(model.InAndOut, domain=pyomo.Reals) #-- Reservoir - no negative storage allowed model.Res_S = pyomo.Var(model.Reservoir, domain=pyomo.NonNegativeReals) #-- Demand - no negative delivery allowed model.Dem_delivery = pyomo.Var(model.Demand, domain=pyomo.NonNegativeReals) #-- Channel model.Cha_Q = pyomo.Var(model.Channel, domain=pyomo.Reals) # Set constraints def storage_limits(model, res): "Set minimum and maximum storage on reservoirs." return (model.Res_min_stor[res], model.Res_S[res], model.Res_max_stor[res]) model.storage_limits = pyomo.Constraint(model.Reservoir, rule=storage_limits) def flow_limits(model, chan): "Set minimum and maximum flow on channels." return (model.Cha_min_flow[chan], model.Cha_Q[chan], model.Cha_max_flow[chan]) model.flow_limits = pyomo.Constraint(model.Channel, rule=flow_limits) def mass_balance(model, node): "Make sure the mass balance is closed in all nodes." tot_outflow = 0 # Flow from node tot_inflow = 0 # Flow to node for i in model.nodes: out_link = model.connectivity[node, i] if out_link != 0: tot_outflow += model.Cha_Q[out_link] in_link = model.connectivity[i, node] if in_link != 0: tot_inflow += model.Cha_Q[in_link] \ * model.Cha_flowmult[in_link] if node in model.InAndOut: return model.InA_Q[node] == tot_outflow - tot_inflow elif node in model.Reservoir: return model.Res_S[node] == model.Res_init_stor[node] \ + tot_inflow - tot_outflow elif node in model.Demand: return model.Dem_delivery[node] == tot_inflow \ * model.Dem_consumption_coeff[node] # No return flow else: # Junction nodes return tot_inflow == tot_outflow model.mass_balance = pyomo.Constraint(model.nodes, rule=mass_balance) # Define objective function def objective(model): "Objective function: sum of cost_i * flow_i" flow_cost = sum(model.Cha_Q[i] * model.Cha_cost[i] for i in model.links) carryover_cost = sum(model.Res_S[i] * model.Res_carryover_penalty[i] for i in model.Reservoir) return flow_cost + carryover_cost model.objective = pyomo.Objective(rule=objective, sense=pyomo.minimize) # Run optimisation #model_instance = model.create_instance() #result = solver.solve(model_instance) #model_instance.load(result) result = solver.solve(model) model.solutions.load_from(result) # Extract results #-- Assign node variables self.target.nodes = [] for node_id in node_types['Reservoir']: node = nodes[node_id] node.S = model.Res_S[node_id].value self.target.nodes.append(node) for node_id in node_types['Demand']: node = nodes[node_id] node.delivery = model.Dem_delivery[node_id].value self.target.nodes.append(node) for node_id in node_types['InAndOut']: node = nodes[node_id] node.Q = model.InA_Q[node_id].value self.target.nodes.append(node) for node_id in node_types['Junction']: # No results here, but the node is needed in the following # timesteps self.target.nodes.append(nodes[node_id]) #-- Assign link variables self.target.links = [] for link_id, link in links.iteritems(): link.Q = model.Cha_Q[link_id].value self.target.links.append(link) #-- Assign objective self.target.cost = sum(link.cost * link.Q for link in self.target.links)
def optimize(self, declaresOptimizationProblem=True, timeSeriesAggregation=False, logFileName='', threads=3, solver='gurobi', timeLimit=None, optimizationSpecs='', warmstart=False): """ Optimize the specified energy system for which a pyomo ConcreteModel instance is built or called upon. A pyomo instance is optimized with the specified inputs, and the optimization results are further processed. **Default arguments:** :param declaresOptimizationProblem: states if the optimization problem should be declared (True) or not (False). (a) If true, the declareOptimizationProblem function is called and a pyomo ConcreteModel instance is built. (b) If false a previously declared pyomo ConcreteModel instance is used. |br| * the default value is True :type declaresOptimizationProblem: boolean :param timeSeriesAggregation: states if the optimization of the energy system model should be done with (a) the full time series (False) or (b) clustered time series data (True). |br| * the default value is False :type timeSeriesAggregation: boolean :param logFileName: logFileName is used for naming the log file of the optimization solver output if gurobi is used as the optimization solver. If the logFileName is given as an absolute path (e.g. logFileName = os.path.join(os.getcwd(), 'Results', 'logFileName.txt')) the log file will be stored in the specified directory. Otherwise, it will be stored by default in the directory where the executing python script is called. |br| * the default value is 'job' :type logFileName: string :param threads: number of computational threads used for solving the optimization (solver dependent input) if gurobi is used as the solver. A value of 0 results in using all available threads. If a value larger than the available number of threads are chosen, the value will reset to the maximum number of threads. |br| * the default value is 3 :type threads: positive integer :param solver: specifies which solver should solve the optimization problem (which of course has to be installed on the machine on which the model is run). |br| * the default value is 'gurobi' :type solver: string :param timeLimit: if not specified as None, indicates the maximum solve time of the optimization problem in seconds (solver dependent input). The use of this parameter is suggested when running models in runtime restricted environments (such as clusters with job submission systems). If the runtime limitation is triggered before an optimal solution is available, the best solution obtained up until then (if available) is processed. |br| * the default value is None :type timeLimit: strictly positive integer or None :param optimizationSpecs: specifies parameters for the optimization solver (see the respective solver documentation for more information) |br| * the default value is 'LogToConsole=1 OptimalityTol=1e-6' :type timeLimit: string :param warmstart: specifies if a warm start of the optimization should be considered (not always supported by the solvers). |br| * the default value is False :type warmstart: boolean Last edited: August 10, 2018 |br| @author: Lara Welder """ if declaresOptimizationProblem: self.declareOptimizationProblem( timeSeriesAggregation=timeSeriesAggregation) else: if self.pyM is None: raise TypeError( 'The optimization problem is not declared yet. Set the argument declaresOptimization' ' problem to True or call the declareOptimizationProblem function first.' ) # Get starting time of the optimization to, later on, obtain the total run time of the optimize function call timeStart = time.time() # Check correctness of inputs utils.checkOptimizeInput(timeSeriesAggregation, self.isTimeSeriesDataClustered, logFileName, threads, solver, timeLimit, optimizationSpecs, warmstart) # Store keyword arguments in the EnergySystemModel instance self.solverSpecs['logFileName'], self.solverSpecs[ 'threads'] = logFileName, threads self.solverSpecs['solver'], self.solverSpecs[ 'timeLimit'] = solver, timeLimit self.solverSpecs['optimizationSpecs'], self.solverSpecs[ 'hasTSA'] = optimizationSpecs, timeSeriesAggregation ################################################################################################################ # Solve the specified optimization problem # ################################################################################################################ # Set which solver should solve the specified optimization problem optimizer = opt.SolverFactory(solver) # Set, if specified, the time limit if self.solverSpecs['timeLimit'] is not None and solver == 'gurobi': optimizer.options['timelimit'] = timeLimit # Set the specified solver options if 'LogToConsole=' not in optimizationSpecs: if self.verbose == 2: optimizationSpecs += ' LogToConsole=0' # Solve optimization problem. The optimization solve time is stored and the solver information is printed. if solver == 'gurobi': optimizer.set_options('Threads=' + str(threads) + ' logfile=' + logFileName + ' ' + optimizationSpecs) solver_info = optimizer.solve(self.pyM, warmstart=warmstart, tee=True) else: solver_info = optimizer.solve(self.pyM, tee=True) self.solverSpecs['solvetime'] = time.time() - timeStart utils.output(solver_info.solver(), self.verbose, 0), utils.output(solver_info.problem(), self.verbose, 0) utils.output( 'Solve time: ' + str(self.solverSpecs['solvetime']) + ' sec.', self.verbose, 0) ################################################################################################################ # Post-process optimization output # ################################################################################################################ _t = time.time() # Post-process the optimization output by differentiating between different solver statuses and termination # conditions. First, check if the status and termination_condition of the optimization are acceptable. # If not, no output is generated. # TODO check if this is still compatible with the latest pyomo version status, termCondition = solver_info.solver.status, solver_info.solver.termination_condition if status == opt.SolverStatus.error or status == opt.SolverStatus.aborted or status == opt.SolverStatus.unknown: utils.output( 'Solver status: ' + str(status) + ', termination condition: ' + str(termCondition) + '. No output is generated.', self.verbose, 0) elif solver_info.solver.termination_condition == opt.TerminationCondition.infeasibleOrUnbounded or \ solver_info.solver.termination_condition == opt.TerminationCondition.infeasible or \ solver_info.solver.termination_condition == opt.TerminationCondition.unbounded: utils.output( 'Optimization problem is ' + str(solver_info.solver.termination_condition) + '. No output is generated.', self.verbose, 0) else: # If the solver status is not okay (hence either has a warning, an error, was aborted or has an unknown # status), show a warning message. if not solver_info.solver.termination_condition == opt.TerminationCondition.optimal and self.verbose < 2: warnings.warn( 'Output is generated for a non-optimal solution.') utils.output("\nProcessing optimization output...", self.verbose, 0) # Declare component specific sets, variables and constraints w = str(len(max(self.componentModelingDict.keys())) + 6) for key, mdl in self.componentModelingDict.items(): __t = time.time() mdl.setOptimalValues(self, self.pyM) outputString = ('for {:' + w + '}').format( key + ' ...') + "(%.4f" % (time.time() - __t) + "sec)" utils.output(outputString, self.verbose, 0) utils.output('\t\t(%.4f' % (time.time() - _t) + ' sec)\n', self.verbose, 0) # Store the runtime of the optimize function call in the EnergySystemModel instance self.solverSpecs['runtime'] = self.solverSpecs[ 'buildtime'] + time.time() - timeStart
def _solve_model(model, solver, mipgap=0.001, timelimit=None, solver_tee=True, symbolic_solver_labels=False, options=None, return_solver=False): ''' Create and solve an Egret power system optimization model Parameters ---------- model : pyomo.environ.ConcreteModel A pyomo ConcreteModel object. solver : str or pyomo.opt.base.solvers.OptSolver Either a string specifying a pyomo solver name, or an instanciated pyomo solver mipgap : float (optional) Mipgap to use for unit commitment solve; default is 0.001 timelimit : float (optional) Time limit for unit commitment run. Default of None results in no time limit being set -- runs until mipgap is satisfied solver_tee : bool (optional) Display solver log. Default is True. symbolic_solver_labels : bool (optional) Use symbolic solver labels. Useful for debugging; default is False. options : dict (optional) Other options to pass into the solver. Default is dict(). return_solver : bool (optional) Returns the solver object Returns ------- ''' results = None ## termination conditions which are acceptable safe_termination_conditions = [ po.TerminationCondition.maxTimeLimit, po.TerminationCondition.maxIterations, po.TerminationCondition.minFunctionValue, po.TerminationCondition.minStepLength, po.TerminationCondition.globallyOptimal, po.TerminationCondition.locallyOptimal, po.TerminationCondition.feasible, po.TerminationCondition.optimal, po.TerminationCondition.maxEvaluations, po.TerminationCondition.other, ] if isinstance(solver, str): solver = po.SolverFactory(solver) elif isinstance(solver, po.base.OptSolver): pass else: raise Exception( 'solver must be string or an instanciated pyomo solver') _set_options(solver, mipgap, timelimit, options) if isinstance(solver, PersistentSolver): solver.set_instance(model, symbolic_solver_labels=symbolic_solver_labels) results = solver.solve(model, tee=solver_tee) else: results = solver.solve(model, tee=solver_tee, \ symbolic_solver_labels=symbolic_solver_labels) if results.solver.termination_condition not in safe_termination_conditions: raise Exception( 'Problem encountered during solve, termination_condition {}'. format(results.solver.termination_condition)) if return_solver: return model, results, solver return model, results
def optimize(self, timeSeriesAggregation=False, jobName='job', threads=0, solver='gurobi', timeLimit=None, optimizationSpecs='LogToConsole=1 OptimalityTol=1e-6', warmstart=False, tsamSpecs=None): timeStart = time.time() self._solverSpecs['jobName'] = jobName self._solverSpecs['threads'] = threads self._solverSpecs['solver'] = solver self._solverSpecs['timeLimit'] = timeLimit self._solverSpecs['optimizationSpecs'] = optimizationSpecs self._solverSpecs['hasTSA'] = timeSeriesAggregation self._pyM = pyomo.ConcreteModel() pyM = self._pyM pyM.hasTSA = timeSeriesAggregation if timeSeriesAggregation and tsamSpecs is not None: self.cluster(**tsamSpecs) elif timeSeriesAggregation and not self._isTimeSeriesDataClustered and tsamSpecs is None: warnings.warn( 'The time series flag indicates that not all time series data might be clustered.\n' + 'Clustering time series data with default values:') self.cluster() for mdlName, mdl in self._componentModelingDict.items(): for comp in mdl._componentsDict.values(): comp.setTimeSeriesData(pyM.hasTSA) # Initialize time sets if not pyM.hasTSA: # Reset timeStepsPerPeriod in case it was overwritten by the clustering function self._timeStepsPerPeriod = self._totalTimeSteps self._interPeriodTimeSteps = list( range( int( len(self._totalTimeSteps) / len(self._timeStepsPerPeriod)) + 1)) self._periods = [0] self._periodsOrder = [0] self._periodOccurrences = [1] def initTimeSet(pyM): return ((p, t) for p in self._periods for t in self._timeStepsPerPeriod) def initInterTimeStepsSet(pyM): return ((p, t) for p in self._periods for t in range(len(self._timeStepsPerPeriod) + 1)) else: print('Number of typical periods:', len(self._typicalPeriods), 'Number of time steps per periods:', len(self._timeStepsPerPeriod)) def initTimeSet(pyM): return ((p, t) for p in self._typicalPeriods for t in self._timeStepsPerPeriod) def initInterTimeStepsSet(pyM): return ((p, t) for p in self._typicalPeriods for t in range(len(self._timeStepsPerPeriod) + 1)) pyM.timeSet = pyomo.Set(dimen=2, initialize=initTimeSet) pyM.interTimeStepsSet = pyomo.Set(dimen=2, initialize=initInterTimeStepsSet) # Create shared potential dictionary potentialDict = {} for mdlName, mdl in self._componentModelingDict.items(): for compName, comp in mdl._componentsDict.items(): if comp._sharedPotentialID is not None: for loc in self._locations: if comp._capacityMax[loc] != 0: potentialDict.setdefault( (comp._sharedPotentialID, loc), []).append(compName) pyM.sharedPotentialDict = potentialDict # Declare component specific sets, variables and constraints for key, comp in self._componentModelingDict.items(): _t = time.time() print('Declaring sets, variables and constraints for', key) print('\tdeclaring sets... '), comp.declareSets(self, pyM) print('\tdeclaring variables... '), comp.declareVariables( self, pyM), print('\tdeclaring constraints... ' ), comp.declareComponentConstraints(self, pyM) print("\t\t(%.4f" % (time.time() - _t), "sec)") _t = time.time() print('Declaring shared potential constraint...') def sharedPotentialConstraint(pyM, ID, loc): # sum up percentage of maximum capacity for each component and location & ensure that the total <= 100% return sum( comp.getSharedPotentialContribution(pyM, ID, loc) for comp in self._componentModelingDict.values()) <= 1 pyM.ConstraintSharedPotentials = \ pyomo.Constraint(pyM.sharedPotentialDict.keys(), rule=sharedPotentialConstraint) print("\t\t(%.4f" % (time.time() - _t), "sec)") _t = time.time() print('Declaring commodity balances...') def initLocationCommoditySet(pyM): return ( (loc, commod) for loc in self._locations for commod in self._commodities if any([ comp.hasOpVariablesForLocationCommodity(self, loc, commod) for comp in self._componentModelingDict.values() ])) pyM.locationCommoditySet = pyomo.Set( dimen=2, initialize=initLocationCommoditySet) def commodityBalanceConstraint(pyM, loc, commod, p, t): return sum( comp.getCommodityBalanceContribution(pyM, commod, loc, p, t) for comp in self._componentModelingDict.values()) == 0 pyM.commodityBalanceConstraint = pyomo.Constraint( pyM.locationCommoditySet, pyM.timeSet, rule=commodityBalanceConstraint) print("\t\t(%.4f" % (time.time() - _t), "sec)") _t = time.time() print('Declaring objective function...') def objective(pyM): TAC = sum( comp.getObjectiveFunctionContribution(self, pyM) for comp in self._componentModelingDict.values()) return TAC pyM.Obj = pyomo.Objective(rule=objective) print("\t\t(%.4f" % (time.time() - _t), "sec)") optimizer = opt.SolverFactory(solver) if self._solverSpecs['timeLimit'] is not None: optimizer.options['timelimit'] = timeLimit optimizer.set_options('Threads=' + str(threads) + ' logfile=' + jobName + ' ' + optimizationSpecs) solver_info = optimizer.solve(pyM, warmstart=warmstart, tee=True) print(solver_info.solver()) print(solver_info.problem()) print("\t\t(%.4f" % (time.time() - _t), "sec)") _t = time.time() results = {} if solver_info.solver.termination_condition == opt.TerminationCondition.infeasibleOrUnbounded or \ solver_info.solver.termination_condition == opt.TerminationCondition.infeasible or \ solver_info.solver.termination_condition == opt.TerminationCondition.unbounded: print('Optimization problem is ' + str(solver_info.solver.termination_condition) + '. No output is generated.') else: if not (solver_info.solver.status == opt.SolverStatus.ok and solver_info.solver.termination_condition == opt.TerminationCondition.optimal): warnings.warn( 'Output is generated for a non-optimal solution.') print("Processing optimization output...") # Declare component specific sets, variables and constraints for key, comp in self._componentModelingDict.items(): _t = time.time() comp.setOptimalValues(self, pyM) print("\t\t(%.4f" % (time.time() - _t), "sec)") self._runtime = time.time() - timeStart
def sim5R1C(self, solver=None, tee=True): """ Simulate the building model independently from any other optimization based on the 5R1C model of the DIN 137900/Thomas Schuetz 2017 with pyomo. Returns ------- None, but in Building.results or Building.detailedResults can the results be shown as pandas.DataFrame """ # take default solver from environment variables if solver is None: try: solver = os.environ["SOLVER"] except KeyError: solver = "gurobi" self.WACC = None self.lifetime = 40 self.heatCost = 0.08 self.coolCost = 0.02 self.elecCost = 0.25 # init concrete model M = pyomo.ConcreteModel() # set time index M.timeIndex = [(1, t) for t in range(len(self.times))] M.fullTimeIndex = M.timeIndex timediff = self.times[1] - self.times[0] M.stepSize = timediff.total_seconds() / 3600 # step size in hours M.hasTypPeriods = False M.WACC = 0.08 M.optInterval = 8760 # create heat and electricity input (PLEASE DO NOT CHANGE THE # SYNTAX OF THIS BLOCK - equivalent to enercore) M.connectIndex = [ ("Heat", "Building"), ("Electricity", "Building"), ("Cool", "Building"), ] M.connectVars = pyomo.Var( M.connectIndex, M.timeIndex, within=pyomo.NonNegativeReals ) M.bConnectedHeat = [("Heat", "Building")] M.bConnectedElec = [("Electricity", "Building")] M.bConnectedCool = [("Cool", "Building")] # init all indices and parameters M = self._initOpti(M) M = self._addOpti(M) # all profiles have been initialized here --> divide to constraint part if not M.hasTypPeriods: for profile_name in M.profiles: M.profiles[profile_name] = { timeIndex: M.profiles[profile_name][i] for i, timeIndex in enumerate(M.timeIndex) } # add other variables M = self._addVars(M) # add constraints M = self._addCons(M) # add cost M.bHeatCost = self.heatCost # eur/kWh M.bCoolCost = self.coolCost # eur/kWh M.bEectricityCost = self.elecCost # eur/kWh # add the maximal heat load as soft constraint in order to avoid infeasibility M.bMaxLoadViolation = pyomo.Var(within=pyomo.NonNegativeReals) # create a maximal heat load as soft constraint in order to avoid infeasibility def maxHeatingLoad(M, t1, t2): return ( sum(M.connectVars[c, t1, t2] for c in M.bConnectedHeat) - M.bMaxLoadViolation <= M.bMaxLoad ) M.maxHeatingLoadCon = pyomo.Constraint(M.timeIndex, rule=maxHeatingLoad) def maxCoolingLoad(M, t1, t2): return ( sum(M.connectVars[c, t1, t2] for c in M.bConnectedCool) - M.bMaxLoadViolation <= M.bMaxLoad ) M.maxCoolingLoadCon = pyomo.Constraint(M.timeIndex, rule=maxCoolingLoad) # objective function def minLoad(M): return ( sum( sum(M.connectVars[c, t] for c in M.bConnectedCool) for t in M.timeIndex ) * M.bCoolCost + sum( sum(M.connectVars[c, t] for c in M.bConnectedHeat) for t in M.timeIndex ) * M.bHeatCost + sum( sum(M.connectVars[c, t] for c in M.bConnectedElec) for t in M.timeIndex ) * M.bEectricityCost + sum(M.exVarCost[dec] * M.exVars[dec] for dec in M.exVarIx) + M.bMaxLoadViolation * 1e2 # penalty term ) M.obj = pyomo.Objective(rule=minLoad) # optimize self.M = M optprob = opt.SolverFactory(solver) optprob.options = utils.manageSolverOpts( solver, {"Threads": 1, "LogFile": ""} ) # , 'LogToConsole':0 # solve self.opt_results = optprob.solve(M, tee=tee) if not M.bMaxLoadViolation.value is None: if M.bMaxLoadViolation.value > 0.0: warnings.warn( "Maximal heat load exceeded by " + str(round(M.bMaxLoadViolation.value, 3)) + " kW", UserWarning, ) # read results self._readResults(M) return