Example #1
0
def MakeNodesforScen(model, BFs, scennum):
    """ Make just those scenario tree nodes needed by a scenario.
        Return them as a list.
        NOTE: the nodes depend on the scenario model and are, in some sense,
              local to it.
        Args:
            BFs (list of int): branching factors
    """
    ndn = "ROOT_"+str((scennum-1) // BFs[0]) # scennum is one-based
    retval = [scenario_tree.ScenarioNode("ROOT",
                                         1.0,
                                         1,
                                         model.StageCost[1],
                                         None,
                                         [model.Pgt[1],
                                          model.Pgh[1],
                                          model.PDns[1],
                                          model.Vol[1]],
                                         model),
              scenario_tree.ScenarioNode(ndn,
                                         1.0/BFs[0],
                                         2,
                                         model.StageCost[2],
                                         None,
                                         [model.Pgt[2],
                                          model.Pgh[2],
                                          model.PDns[2],
                                          model.Vol[2]],
                                         model, parent_name="ROOT")
              ]
    return retval
Example #2
0
def MakeAllScenarioTreeNodes(model, bf):
    """ Make the tree nodes and put them in a dictionary.
        Assume three stages and a branching factor of bf.
        Note: this might not ever be called. (Except maybe for the EF)
        Note: mpisppy does not have leaf nodes.
        Aside: every rank makes their own nodes; these nodes do not 
        hold any data computed by a solution algorithm.
    """
    TreeNodes = dict()
    TreeNodes["ROOT"] = scenario_tree.ScenarioNode("ROOT",
                                                  1.0,
                                                  1,
                                                  model.StageCost[1],
                                                  None,
                                                  [model.Pgt[1],
                                                   model.Pgh[1],
                                                   model.PDns[1],
                                                   model.Vol[1]],
                                                  model)
    for b in range(bf):
        ndn = "ROOT_"+str(b)
        TreeNodes[ndn] = scenario_tree.ScenarioNode(ndn,
                                                   1.0/bf,
                                                   2,
                                                   model.StageCost[2],
                                                   None,
                                                  [model.Pgt[2],
                                                   model.Pgh[2],
                                                   model.PDns[2],
                                                   model.Vol[2]],
                                                    model,
                                                    parent_name="ROOT")
Example #3
0
def MakeNodesforScen(model, nodenames, branching_factors, starting_stage=1):
    #Create all nonleaf nodes used by the scenario
    #Compatible with sample scenario creation
    TreeNodes = []
    for stage in model.T:

        nonant_list = [
            model.stage_models[stage].RegularProd,
            model.stage_models[stage].OvertimeProd
        ]

        nonant_ef_suppl_list = [model.stage_models[stage].Inventory]
        if model.start_ups:
            nonant_ef_suppl_list.append(model.stage_models[stage].StartUp)

        if stage == 1:
            ndn = "ROOT"
            TreeNodes.append(
                scenario_tree.ScenarioNode(
                    name=ndn,
                    cond_prob=1.0,
                    stage=stage,
                    cost_expression=model.stage_models[stage].StageObjective,
                    scen_name_list=None,  # Not maintained
                    nonant_list=nonant_list,
                    scen_model=model,
                    nonant_ef_suppl_list=nonant_ef_suppl_list))
        elif stage <= starting_stage:
            parent_ndn = ndn
            ndn = parent_ndn + "_0"  #Only one node per stage before starting stage
            TreeNodes.append(
                scenario_tree.ScenarioNode(
                    name=ndn,
                    cond_prob=1.0,
                    stage=stage,
                    cost_expression=model.stage_models[stage].StageObjective,
                    scen_name_list=None,  # Not maintained
                    nonant_list=nonant_list,
                    scen_model=model,
                    nonant_ef_suppl_list=nonant_ef_suppl_list,
                    parent_name=parent_ndn))
        elif stage < max(model.T):  #We don't add the leaf node
            parent_ndn = ndn
            ndn = parent_ndn + "_" + nodenames[stage - starting_stage]
            TreeNodes.append(
                scenario_tree.ScenarioNode(
                    name=ndn,
                    cond_prob=1.0 /
                    branching_factors[stage - starting_stage - 1],
                    stage=stage,
                    cost_expression=model.stage_models[stage].StageObjective,
                    scen_name_list=None,  # Not maintained
                    nonant_list=nonant_list,
                    scen_model=model,
                    nonant_ef_suppl_list=nonant_ef_suppl_list,
                    parent_name=parent_ndn))
    return (TreeNodes)
Example #4
0
def scenario_creator(scenario_name, node_names=None, cb_data=None):
    """ The callback needs to create an instance and then attach
        the PySP nodes to it in a list _PySPnode_list ordered by stages.
        Optionally attach _PHrho. 
        Use cb_data for the scenario count (3 or 10)
    """
    if cb_data not in [3, 10]:
        raise RuntimeError("cb_data passed to scenario counter "
                           "must equal either 3 or 10")

    sizes_dir = os.path.dirname(mpisppy.examples.sizes.sizes.__file__)
    datadir = os.sep.join((sizes_dir, f"SIZES{cb_data}"))
    try:
        fname = datadir + os.sep + scenario_name + ".dat"
    except:
        print("FAIL: datadir=", datadir, " scenario_name=", scenario_name)

    model = ref.model.create_instance(fname)

    # now attach the one and only tree node (ROOT is a reserved word)
    model._PySPnode_list = [
        scenario_tree.ScenarioNode(
            "ROOT",
            1.0,
            1,
            model.FirstStageCost,
            None,
            [model.NumProducedFirstStage, model.NumUnitsCutFirstStage],
            model,
        )
    ]
    return model
Example #5
0
def MakeNodesforScen(model, nodenames, branching_factors):
    #Create all nonleaf nodes used by the scenario
    TreeNodes = []
    for stage in model.T:
        if stage == 1:
            ndn = "ROOT"
            TreeNodes.append(
                scenario_tree.ScenarioNode(
                    name=ndn,
                    cond_prob=1.0,
                    stage=stage,
                    cost_expression=model.stage_models[stage].StageObjective,
                    scen_name_list=None,  # Not maintained
                    nonant_list=[
                        model.stage_models[stage].RegularProd,
                        model.stage_models[stage].OvertimeProd
                    ],
                    scen_model=model,
                    nonant_ef_suppl_list=[model.stage_models[stage].Inventory],
                ))
        elif stage < max(model.T):  #We don't add the leaf node
            parent_ndn = ndn
            ndn = parent_ndn + "_" + nodenames[stage - 1]
            TreeNodes.append(
                scenario_tree.ScenarioNode(
                    name=ndn,
                    cond_prob=1.0 / branching_factors[stage - 2],
                    stage=stage,
                    cost_expression=model.stage_models[stage].StageObjective,
                    scen_name_list=None,  # Not maintained
                    nonant_list=[
                        model.stage_models[stage].RegularProd,
                        model.stage_models[stage].OvertimeProd
                    ],
                    scen_model=model,
                    nonant_ef_suppl_list=[model.stage_models[stage].Inventory],
                    parent_name=parent_ndn))
    return (TreeNodes)
Example #6
0
def scenario_creator(scenario_name, node_names=None, cb_data=None):
    """ The callback needs to create an instance and then attach
        the PySP nodes to it in a list _PySPnode_list ordered by stages.
        Optionally attach _PHrho.
    """
    datadir = cb_data
    fname = datadir + os.sep + scenario_name + ".dat"
    model = ref.model.create_instance(fname, name=scenario_name)

    # now attach the one and only tree node (ROOT is a reserved word)
    model._PySPnode_list = [
        scenario_tree.ScenarioNode(
            "ROOT", 1.0, 1, model.FirstStageCost, None, [model.FacilityOpen], model
        )
    ]
    return model
Example #7
0
def attach_root_node(model, firstobj, varlist, nonant_ef_suppl_list=None):
    """ Create a root node as a list to attach to a scenario model
    Args:
        model (ConcreteModel): model to which this will be attached
        firstobj (Pyomo Expression): First stage cost (e.g. model.FC)
        varlist (list): Pyomo Vars in first stage (e.g. [model.A, model.B])
        nonant_ef_suppl_list (list of pyo Var, Vardata or slices):
              vars for which nonanticipativity constraints tighten the EF
              (important for bundling)

    Note: 
       attaches a list consisting of one scenario node to the model
    """
    model._PySPnode_list = [
        scenario_tree.ScenarioNode("ROOT",1.0,1,firstobj, None, varlist, model,
                                   nonant_ef_suppl_list = nonant_ef_suppl_list)
    ]
Example #8
0
def scenario_creator(scenario_name, data_dir=None):
    """ The callback needs to create an instance and then attach
        the PySP nodes to it in a list _PySPnode_list ordered by stages.
        Optionally attach _PHrho.
    """
    if data_dir is None:
        raise ValueError(
            "kwarg `data_dir` is required for SSLP scenario_creator")
    fname = data_dir + os.sep + scenario_name + ".dat"
    model = ref.model.create_instance(fname, name=scenario_name)

    # now attach the one and only tree node (ROOT is a reserved word)
    model._PySPnode_list = [
        scenario_tree.ScenarioNode("ROOT", 1.0, 1, model.FirstStageCost, None,
                                   [model.FacilityOpen], model)
    ]
    return model
Example #9
0
def scenario_creator(sname, num_scens=None):
    scennum   = sputils.extract_num(sname)
    model = GBD_model_creator(scennum)
    
    # Create the list of nodes associated with the scenario (for two stage,
    # there is only one node associated with the scenario--leaf nodes are
    # ignored).
    model._mpisppy_node_list = [
        scenario_tree.ScenarioNode(
            name="ROOT",
            cond_prob=1.0,
            stage=1,
            cost_expression=model.obj,
            scen_name_list=None, # Deprecated?
            nonant_list=[model.x],
            scen_model=model,
        )
    ]
    #Add the probability of the scenario
    if num_scens is not None :
        model._mpisppy_probability = 1/num_scens
    
    return(model)
Example #10
0
def pysp2_callback(scenario_name,
                   node_names=None,
                   cb_data=None):
    ''' The callback needs to create an instance and then attach
        the PySP nodes to it in a list _PySPnode_list ordered by stages.
        Optionally attach _PHrho. Standard (1.0) PySP signature for now...
    '''

    instance = pysp_instance_creation_callback(scenario_name, 
                                               node_names, cb_data)

    # now attach the one and only tree node (ROOT is a reserved word)
    # UnitOn[*,*] is the only set of variables
    instance._PySPnode_list = [scenario_tree.ScenarioNode("ROOT",
                                                          1.0,
                                                          1,
                                                          instance.StageCost["Stage_1"], #"Stage_1" hardcodes the commitments in all time periods
                                                          None,
                                                          [instance.UnitOn],
                                                          instance,
                                                          [instance.UnitStart, instance.UnitStop, instance.StartupIndicator],
                                                          )]
    return instance
Example #11
0
def pysp2_callback(scenario_name, node_names=None, cb_data=None):
    """
    mpisppy signature for scenario creation.
    Then find a starting solution for the scenario if solver option is not None.
    Note that stage numbers are one-based.

    Args:
        scenario_name (str): put the scenario number on the end 
        node_names (int): not used
        cb_data: (dict) "etree", "solver", "epath", "tee", "acstream", 
                        "convex_relaxation"

    Returns:
        scenario (pyo.ConcreteModel): the scenario instance

    Attaches:
        _enodes (ACtree nodes): a list of the ACtree tree nodes
        _egret_md (egret tuple with dict as [1]) egret model data

    """
    # pull the number off the end of the scenario name
    scen_num = sputils.extract_num(scenario_name)

    etree = cb_data["etree"]
    solver = cb_data["solver"]
    acstream = cb_data["acstream"]
    convex_relaxation = cb_data["convex_relaxation"] if "convex_relaxation"\
                       in cb_data else False

    def lines_up_and_down(stage_md_dict, enode):
        # local routine to configure the lines in stage_md_dict for the scenario
        LinesDown = []
        for f in enode.FailedLines:
            LinesDown.append(f[0])
        for this_branch in stage_md_dict.elements("branch"):
            if this_branch[0] in enode.LinesUp:
                this_branch[1]["in_service"] = True
            elif this_branch[0] in LinesDown:
                this_branch[1]["in_service"] = False
            else:
                print("enode.LinesUp=", enode.LinesUp)
                print("enode.FailedLines=", enode.FailedLines)
                raise RuntimeError("Branch (line) {} neither up nor down in scenario {}".\
                               format(this_branch[0], scenario_name))

    def _egret_model(md_dict):
        # the exact acopf model is hard-wired here:
        if not convex_relaxation:
            pyomod, mdict = eac.create_riv_acopf_model(
                md_dict, include_feasibility_slack=True)

        else:
            pyomod, mdict = eac_relax.create_soc_relaxation(
                md_dict,
                include_feasibility_slack=True,
                use_linear_relaxation=False)
        return pyomod, mdict

    # pull the number off the end of the scenario name
    scen_num = sputils.extract_num(scenario_name)
    #print ("debug scen_num=",scen_num)

    numstages = etree.NumStages
    enodes = etree.Nodes_for_Scenario(scen_num)
    full_scenario_model = pyo.ConcreteModel()
    full_scenario_model.stage_models = dict()

    # look at egret/data/model_data.py for the format specification of md_dict
    first_stage_md_dict = _md_dict(cb_data)
    generator_set = first_stage_md_dict.attributes("generator")
    generator_names = generator_set["names"]

    # the following creates the first stage model
    full_scenario_model.stage_models[1], model_dict = _egret_model(
        first_stage_md_dict)

    full_scenario_model.stage_models[1].obj.deactivate()
    setattr(full_scenario_model, "stage_models_" + str(1),
            full_scenario_model.stage_models[1])

    for stage in range(2, numstages + 1):
        #print ("stage={}".format(stage))

        stage_md_dict = copy.deepcopy(first_stage_md_dict)
        #print ("debug: processing node {}".format(enodes[stage-1].Name))
        lines_up_and_down(stage_md_dict, enodes[stage - 1])

        full_scenario_model.stage_models[stage], model_dict = \
            _egret_model(stage_md_dict)
        full_scenario_model.stage_models[stage].obj.deactivate()
        setattr(full_scenario_model, "stage_models_" + str(stage),
                full_scenario_model.stage_models[stage])

    def aggregate_ramping_rule(m):
        """
        We are adding ramping to the obj instead of a constraint for now
        because we may not have ramp limit data.
        """
        retval = 0
        for stage in range(1, numstages):
            retval += sum((full_scenario_model.stage_models[stage+1].pg[this_gen]\
                    - full_scenario_model.stage_models[stage].pg[this_gen])**2\
                   for this_gen in generator_names)
        return retval

    full_scenario_model.ramping = pyo.Expression(rule=aggregate_ramping_rule)

    full_scenario_model.objective = pyo.Objective(expr=\
                            1000000.0 * full_scenario_model.ramping+\
                            sum(full_scenario_model.stage_models[stage].obj.expr\
                                    for stage in range(1,numstages+1)))

    inst = full_scenario_model
    # end code from PySP1

    node_list = list()

    parent_name = None
    for sm1, enode in enumerate(etree.Nodes_for_Scenario(scen_num)):
        stage = sm1 + 1
        if stage < etree.NumStages:
            node_list.append(
                scenario_tree.ScenarioNode(
                    name=enode.Name,
                    cond_prob=enode.CondProb,
                    stage=stage,
                    cost_expression=inst.stage_models[stage].obj,
                    scen_name_list=enode.ScenarioList,
                    nonant_list=[
                        inst.stage_models[stage].pg,
                        inst.stage_models[stage].qg
                    ],
                    scen_model=inst,
                    parent_name=parent_name))
            parent_name = enode.Name

    inst._PySPnode_list = node_list
    # Optionally assign probability to PySP_prob
    inst.PySP_prob = 1 / etree.numscens
    # solve it so subsequent code will have a good start
    if solver is not None:
        print(
            f"scenario creation callback is solving {scenario_name} on rank {rank}"
        )
        solver.solve(
            inst)  #, tee=True) #symbolic_solver_labels=True, keepfiles=True)

    # attachments
    inst._enodes = enodes
    inst._egret_md = first_stage_md_dict

    return inst
Example #12
0
def scenario_creator(
    scenario_name,
    use_integer=False,
    sense=pyo.minimize,
    crops_multiplier=1,
):
    """ Create a scenario for the (scalable) farmer example.
    
    Args:
        scenario_name (str):
            Name of the scenario to construct.
        use_integer (bool, optional):
            If True, restricts variables to be integer. Default is False.
        sense (int, optional):
            Model sense (minimization or maximization). Must be either
            pyo.minimize or pyo.maximize. Default is pyo.minimize.
        crops_multiplier (int, optional):
            Factor to control scaling. There will be three times this many
            crops. Default is 1.
    """
    # scenario_name has the form <str><int> e.g. scen12, foobar7
    # The digits are scraped off the right of scenario_name using regex then
    # converted mod 3 into one of the below avg./avg./above avg. scenarios
    scennum = sputils.extract_num(scenario_name)
    basenames = [
        'BelowAverageScenario', 'AverageScenario', 'AboveAverageScenario'
    ]
    basenum = scennum % 3
    groupnum = scennum // 3
    scenname = basenames[basenum] + str(groupnum)

    # The RNG is seeded with the scenario number so that it is
    # reproducible when used with multiple threads.
    # NOTE: if you want to do replicates, you will need to pass a seed
    # as a kwarg to scenario_creator then use seed+scennum as the seed argument.
    farmerstream.seed(scennum)

    # Check for minimization vs. maximization
    if sense not in [pyo.minimize, pyo.maximize]:
        raise ValueError("Model sense Not recognized")

    # Create the concrete model object
    model = pysp_instance_creation_callback(
        scenname,
        use_integer=use_integer,
        sense=sense,
        crops_multiplier=crops_multiplier,
    )

    # Create the list of nodes associated with the scenario (for two stage,
    # there is only one node associated with the scenario--leaf nodes are
    # ignored).
    model._PySPnode_list = [
        scenario_tree.ScenarioNode(
            name="ROOT",
            cond_prob=1.0,
            stage=1,
            cost_expression=model.FirstStageCost,
            scen_name_list=None,  # Deprecated?
            nonant_list=[model.DevotedAcreage],
            scen_model=model,
        )
    ]
    return model
Example #13
0
def _experiment_instance_creation_callback(scenario_name,
                                           node_names=None,
                                           cb_data=None):
    """
    This is going to be called by mpi-sppy or the local EF and it will call into
    the user's model's callback.

    Parameters:
    -----------
    scenario_name: `str` Scenario name should end with a number
    node_names: `None` ( Not used here )
    cb_data : dict with ["callback"], ["BootList"], 
              ["theta_names"], ["cb_data"], etc.
              "cb_data" is passed through to user's callback function
                        that is the "callback" value.
              "BootList" is None or bootstrap experiment number list.
                       (called cb_data by mpisppy)
 

    Returns:
    --------
    instance: `ConcreteModel`
        instantiated scenario

    Note:
    ----
    There is flexibility both in how the function is passed and its signature.
    """
    assert (cb_data is not None)
    outer_cb_data = cb_data
    scen_num_str = re.compile(r'(\d+)$').search(scenario_name).group(1)
    scen_num = int(scen_num_str)
    basename = scenario_name[:-len(scen_num_str)]  # to reconstruct name

    CallbackFunction = outer_cb_data["callback"]

    if callable(CallbackFunction):
        callback = CallbackFunction
    else:
        cb_name = CallbackFunction

        if "CallbackModule" not in outer_cb_data:
            raise RuntimeError(\
                "Internal Error: need CallbackModule in parmest callback")
        else:
            modname = outer_cb_data["CallbackModule"]

        if isinstance(modname, str):
            cb_module = im.import_module(modname, package=None)
        elif isinstance(modname, types.ModuleType):
            cb_module = modname
        else:
            print("Internal Error: bad CallbackModule")
            raise

        try:
            callback = getattr(cb_module, cb_name)
        except:
            print("Error getting function=" + cb_name + " from module=" +
                  str(modname))
            raise

    if "BootList" in outer_cb_data:
        bootlist = outer_cb_data["BootList"]
        #print("debug in callback: using bootlist=",str(bootlist))
        # assuming bootlist itself is zero based
        exp_num = bootlist[scen_num]
    else:
        exp_num = scen_num

    scen_name = basename + str(exp_num)

    cb_data = outer_cb_data["cb_data"]  # cb_data might be None.

    # at least three signatures are supported. The first is preferred
    try:
        instance = callback(experiment_number=exp_num, cb_data=cb_data)
    except TypeError:
        raise RuntimeError("Only one callback signature is supported: "
                           "callback(experiment_number, cb_data) ")
        """
        try:
            instance = callback(scenario_tree_model, scen_name, node_names)
        except TypeError:  # deprecated signature?
            try:
                instance = callback(scen_name, node_names)
            except:
                print("Failed to create instance using callback; TypeError+")
                raise
        except:
            print("Failed to create instance using callback.")
            raise
        """
    if hasattr(instance, "_mpisppy_node_list"):
        raise RuntimeError(
            f"scenario for experiment {exp_num} has _mpisppy_node_list")
    nonant_list = [instance.find_component(vstr) for vstr in\
                   outer_cb_data["theta_names"]]
    instance._mpisppy_node_list = [
        scenario_tree.ScenarioNode(
            name="ROOT",
            cond_prob=1.0,
            stage=1,
            cost_expression=instance.FirstStageCost,
            scen_name_list=None,  # Deprecated?
            nonant_list=nonant_list,
            scen_model=instance)
    ]

    if "ThetaVals" in outer_cb_data:
        thetavals = outer_cb_data["ThetaVals"]

        # dlw august 2018: see mea code for more general theta
        for vstr in thetavals:
            object = instance.find_component(vstr)
            if thetavals[vstr] is not None:
                #print("Fixing",vstr,"at",str(thetavals[vstr]))
                object.fix(thetavals[vstr])
            else:
                #print("Freeing",vstr)
                object.fixed = False

    return instance
Example #14
0
def pysp2_callback(
    scenario_name,
    etree=None,
    solver=None,
    epath=None,
    tee=False,
    acstream=None,
    convex_relaxation=False,
    verbose=False,
    ramp_coeff=1000000,
    load_mismatch_cost=1000,
    q_load_mismatch_cost=None,
):
    """
    mpisppy signature for scenario creation.
    Then find a starting solution for the scenario if solver option is not None.
    Note that stage numbers are one-based.

    Args:
        scenario_name (str):
            Put the scenario number on the end 
        etree ():
            Default is None.
        solver (str):
            Solver to use.
        epath (str):
            Path to the egret data
        tee (bool):
            If True, displays solver output. Default is False.
        acstream ():
            Default is None.
        convex_relaxation (bool):
            If True, build the convex relaxation. Default is False.
        verbose (bool, optional):
            If True, display verbose output. Default is False.
        ramp_coeff (int, optional):
            Default is 1e6.
        load_mismatch_cost (float, optional):
            Default is 1000.
        q_load_mismatch_cost (float, optional):
            Deafult is load_mismatch_cost.

    Returns:
        scenario (pyo.ConcreteModel): the scenario instance

    Attaches:
        _enodes (ACtree nodes): a list of the ACtree tree nodes
        _egret_md (egret tuple with dict as [1]) egret model data

    """
    print("Debug: convex_relaxation=", convex_relaxation)
    # pull the number off the end of the scenario name
    scen_num = sputils.extract_num(scenario_name)

    if q_load_mismatch_cost is None:
        q_load_mismatch_cost = load_mismatch_cost

    def lines_up_and_down(stage_md_dict, enode):
        # local routine to configure the lines in stage_md_dict for the scenario
        LinesDown = []
        for f in enode.FailedLines:
            LinesDown.append(f[0])
        for this_branch in stage_md_dict.elements("branch"):
            if this_branch[0] in enode.LinesUp:
                this_branch[1]["in_service"] = True
            elif this_branch[0] in LinesDown:
                this_branch[1]["in_service"] = False
            else:
                print("enode.LinesUp=", enode.LinesUp)
                print("enode.FailedLines=", enode.FailedLines)
                raise RuntimeError("Branch (line) {} neither up nor down in scenario {}".\
                               format(this_branch[0], scenario_name))

    def _egret_model(md_dict):
        # the exact acopf model is hard-wired here:
        md_dict.data['system']['load_mismatch_cost'] = load_mismatch_cost
        md_dict.data['system']['q_load_mismatch_cost'] = q_load_mismatch_cost
        if not convex_relaxation:
            pyomod, mdict = eac.create_riv_acopf_model(
                md_dict, include_feasibility_slack=True)

        else:
            pyomod, mdict = eac_relax.create_soc_relaxation(
                md_dict,
                include_feasibility_slack=True,
                use_linear_relaxation=False)
        return pyomod, mdict

    # pull the number off the end of the scenario name
    scen_num = sputils.extract_num(scenario_name)

    numstages = etree.NumStages
    enodes = etree.Nodes_for_Scenario(scen_num)
    full_scenario_model = pyo.ConcreteModel()
    full_scenario_model.stage_models = dict()

    # look at egret/data/model_data.py for the format specification of md_dict
    first_stage_md_dict = _md_dict(epath)
    generator_set = first_stage_md_dict.attributes("generator")
    generator_names = generator_set["names"]

    # the following creates the first stage model
    full_scenario_model.stage_models[1], model_dict = _egret_model(
        first_stage_md_dict)

    full_scenario_model.stage_models[1].obj.deactivate()
    setattr(full_scenario_model, "stage_models_" + str(1),
            full_scenario_model.stage_models[1])

    for stage in range(2, numstages + 1):
        #print ("stage={}".format(stage))

        stage_md_dict = copy.deepcopy(first_stage_md_dict)
        #print ("debug: processing node {}".format(enodes[stage-1].Name))
        lines_up_and_down(stage_md_dict, enodes[stage - 1])

        full_scenario_model.stage_models[stage], model_dict = \
            _egret_model(stage_md_dict)
        full_scenario_model.stage_models[stage].obj.deactivate()
        setattr(full_scenario_model, "stage_models_" + str(stage),
                full_scenario_model.stage_models[stage])

    def aggregate_ramping_rule(m):
        """
        We are adding ramping to the obj instead of a constraint for now
        because we may not have ramp limit data.
        """
        retval = 0
        for stage in range(1, numstages):
            retval += sum((full_scenario_model.stage_models[stage+1].pg[this_gen]\
                    - full_scenario_model.stage_models[stage].pg[this_gen])**2\
                   for this_gen in generator_names)
        return retval

    full_scenario_model.ramping = pyo.Expression(rule=aggregate_ramping_rule)

    full_scenario_model.objective = pyo.Objective(expr=\
                           ramp_coeff * full_scenario_model.ramping+\
                            sum(full_scenario_model.stage_models[stage].obj.expr\
                                    for stage in range(1,numstages+1)))

    inst = full_scenario_model
    # end code from PySP1

    node_list = list()

    parent_name = None
    for sm1, enode in enumerate(etree.Nodes_for_Scenario(scen_num)):
        stage = sm1 + 1
        if stage < etree.NumStages:
            node_list.append(
                scenario_tree.ScenarioNode(
                    name=enode.Name,
                    cond_prob=enode.CondProb,
                    stage=stage,
                    cost_expression=inst.stage_models[stage].obj,
                    scen_name_list=enode.ScenarioList,
                    nonant_list=[
                        inst.stage_models[stage].pg,
                        inst.stage_models[stage].qg
                    ],
                    scen_model=inst,
                    parent_name=parent_name))
            parent_name = enode.Name

    if verbose:
        print(f"\nScenario creation for {scenario_name} on rank {rank}"
              f"    FailedLines (line,minutes)={enode.FailedLines}")

    inst._PySPnode_list = node_list
    inst.PySP_prob = 1 / etree.numscens
    # solve it so subsequent code will have a good start
    if solver is not None:
        print(
            f"scenario creation callback is solving {scenario_name} on rank {rank}"
        )
        solver.solve(inst,
                     tee=tee)  #symbolic_solver_labels=True, keepfiles=True)

    # attachments
    inst._enodes = enodes
    inst._egret_md = first_stage_md_dict

    return inst
Example #15
0
def scenario_creator(
    scenario_name,
    solar_filname=None,
    use_LP=False,
    lam=None,
):
    """
    Args:
        scenario_name (str):
            Name of the scenario to create.
        solar_filename (str):
            File containing the solar data.
        use_LP (bool, optional):
            If True, uses LP. Default is False.
        lam (float):
            Value of the dual variable for the chance constraint.
    """
    if 'solar_filename' is None:
        raise ValueError("kwarg `solar_filename` is required")
    if 'lam' is None:
        raise RuntimeError("kwarg `lam` is required")

    data = getData(solar_filename)
    num_scenarios = data['solar'].shape[0]
    scenario_index = extract_scenario_index(scenario_name)
    if (scenario_index < 0) or (scenario_index >= num_scenarios):
        raise RuntimeError('Provided scenario index is invalid (must lie in '
                           '{0,1,...' + str(num_scenarios - 1) +
                           '} inclusive)')
    model = pyo.ConcreteModel()

    T = range(data['T'])
    Tm1 = range(data['T'] - 1)

    model.y = pyo.Var(T, within=pyo.NonNegativeReals)
    model.p = pyo.Var(T, bounds=(0, data['cMax']))
    model.q = pyo.Var(T, bounds=(0, data['dMax']))
    model.x = pyo.Var(T, bounds=(data['eMin'], data['eMax']))
    if (use_LP):
        model.z = pyo.Var([0], within=pyo.UnitInterval)
    else:
        model.z = pyo.Var([0], within=pyo.Binary)
    ''' "Flow balance" constraints '''
    def flow_balance_constraint_rule(model, t):
        return model.x[t+1]==model.x[t] + \
            data['eff'] * model.p[t] - (1/data['eff']) * model.q[t]

    model.flow_constr = pyo.Constraint(Tm1, rule=flow_balance_constraint_rule)
    ''' Big-M constraints '''

    def big_M_constraint_rule(model, t):
        return model.y[t] - model.q[t] + model.p[t] \
            <= data['solar'][scenario_index,t] + \
            data['M'][scenario_index,t] * model.z[0] # Why indexed??

    model.big_m_constr = pyo.Constraint(T, rule=big_M_constraint_rule)
    ''' Objective function (must be minimization or PH crashes) '''
    model.obj = pyo.Objective(expr=-pyo.dot_product(data['rev'], model.y) +
                              data['char'] * pyo.quicksum(model.p) +
                              data['disc'] * pyo.quicksum(model.q) +
                              lam * model.z[0],
                              sense=pyo.minimize)

    fscr = lambda model: pyo.dot_product(data['rev'], model.y)
    model.first_stage_cost = pyo.Expression(rule=fscr)

    model._PySPnode_list = [
        stree.ScenarioNode(name='ROOT',
                           cond_prob=1.,
                           stage=1,
                           cost_expression=model.first_stage_cost,
                           scen_name_list=None,
                           nonant_list=[model.y],
                           scen_model=model)
    ]

    return model
Example #16
0
def scenario_creator(scenario_name, node_names=None, cb_data=None):
    if (cb_data is None):
        raise RuntimeError('Must provide a cb_data dict to scenario creator. '
                'At a minimum, this dictionary must contain a key "lam" '
                'specifying the value of the dual multiplier lambda to use, '
                'and a key "solar_filename" specifying where the solar data '
                'is stored.')
    if ('solar_filename' not in cb_data):
        raise RuntimeError('Please provide a cb_data dict that contains '
                           '"solar_filename", with a valid path')
    if ('lam' not in cb_data):
        raise RuntimeError('Please provide a cb_data dict that contains '
                           '"lam", a value of the dual multiplier lambda.')

    data = getData(cb_data['solar_filename'])
    num_scenarios = data['solar'].shape[0]
    scenario_index = extract_scenario_index(scenario_name)
    if (scenario_index < 0) or (scenario_index >= num_scenarios):
        raise RuntimeError('Provided scenario index is invalid (must lie in '
                           '{0,1,...' + str(num_scenarios-1) + '} inclusive)')
    if ('use_LP' in cb_data):
        use_LP = cb_data['use_LP']
    else:
        use_LP = False

    model = pyo.ConcreteModel()

    T   = range(data['T'])
    Tm1 = range(data['T']-1)

    model.y = pyo.Var(T, within=pyo.NonNegativeReals)
    model.p = pyo.Var(T, bounds=(0, data['cMax']))
    model.q = pyo.Var(T, bounds=(0, data['dMax']))
    model.x = pyo.Var(T, bounds=(data['eMin'], data['eMax']))
    if (use_LP):
        model.z = pyo.Var([0], within=pyo.UnitInterval)
    else:
        model.z = pyo.Var([0], within=pyo.Binary)

    ''' "Flow balance" constraints '''
    def flow_balance_constraint_rule(model, t):
        return model.x[t+1]==model.x[t] + \
            data['eff'] * model.p[t] - (1/data['eff']) * model.q[t]
    model.flow_constr = pyo.Constraint(Tm1, rule=flow_balance_constraint_rule)
    
    ''' Big-M constraints '''
    def big_M_constraint_rule(model, t):
        return model.y[t] - model.q[t] + model.p[t] \
            <= data['solar'][scenario_index,t] + \
            data['M'][scenario_index,t] * model.z[0] # Why indexed??
    model.big_m_constr = pyo.Constraint(T, rule=big_M_constraint_rule)

    ''' Objective function (must be minimization or PH crashes) '''
    model.obj = pyo.Objective(expr=-pyo.dot_product(data['rev'], model.y)
        + data['char'] * pyo.quicksum(model.p)
        + data['disc'] * pyo.quicksum(model.q) + cb_data['lam'] * model.z[0],
        sense=pyo.minimize)

    fscr = lambda model: pyo.dot_product(data['rev'], model.y)
    model.first_stage_cost = pyo.Expression(rule=fscr)

    model._PySPnode_list = [
        stree.ScenarioNode(name='ROOT', cond_prob=1., stage=1,
            cost_expression=model.first_stage_cost, scen_name_list=None, 
            nonant_list=[model.y], scen_model=model)
    ]

    return model