Beispiel #1
0
    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()
Beispiel #2
0
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
Beispiel #3
0
    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))
Beispiel #4
0
        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
Beispiel #5
0
    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)
Beispiel #6
0
    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)
Beispiel #7
0
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
Beispiel #9
0
 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)
Beispiel #10
0
    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)
Beispiel #11
0
    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
Beispiel #12
0
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
Beispiel #13
0
    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
Beispiel #14
0
    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