コード例 #1
0
ファイル: storage.py プロジェクト: llavin13/switch
 def Total_Storage_Energy_Install_Costs_Annual_rule(m, p):
     # Handle the degenerate case of no storage projects.
     if len(m.STORAGE_PROJECTS) == 0:
         return 0
     # Construct and cache a set for summation as needed
     if not hasattr(m, 'Storage_Build_Summation_dict'):
         m.Storage_Build_Summation_dict = {}
         for (proj, p2) in m.STORAGE_PROJECTS * m.PERIODS:
             m.Storage_Build_Summation_dict[p2] = set(
                 (proj, bld_yr)
                 for bld_yr in m.G_NEW_BUILD_YEARS[m.proj_gen_tech[proj]]
                 if bld_yr <= p2 <= m.proj_end_year[proj, bld_yr])
     # Use pop to free memory
     relevant_proj_build_years = m.Storage_Build_Summation_dict.pop(p)
     return sum(m.BuildStorageEnergyMWh[proj, bld_yr] *
                m.proj_storage_energy_overnight_cost[proj, bld_yr] *
                crf(m.interest_rate, m.g_max_age[m.proj_gen_tech[proj]])
                for (proj, bld_yr) in relevant_proj_build_years)
コード例 #2
0
ファイル: build.py プロジェクト: alexter42/switch
def define_components(mod):
    """

    Adds components to a Pyomo abstract model object to describe
    generation and storage projects. Unless otherwise stated, all power
    capacity is specified in units of MW and all sets and parameters
    are mandatory.

    PROJECTS is the set of generation and storage projects that have
    been built or could potentially be built. A project is a combination
    of generation technology, load zone and location. A particular
    build-out of a project should also include the year in which
    construction was complete and additional capacity came online.
    Members of this set are abbreviated as proj or prj in parameter
    names and indexes. Use of p instead of prj is discouraged because p
    is reserved for period.

    proj_dbid[prj] is an external database id for each project. This is
    an optional parameter than defaults to the project index.

    proj_gen_tech[prj] describes what kind of generation technology a
    projects is using.

    proj_load_zone[prj] is the load zone this project is built in.

    VARIABLE_PROJECTS is a subset of PROJECTS that only includes
    variable generators such as wind or solar that have exogenous
    constraints on their energy production.

    BASELOAD_PROJECTS is a subset of PROJECTS that only includes
    baseload generators such as coal or geothermal.

    LZ_PROJECTS[lz in LOAD_ZONES] is an indexed set that lists all
    projects within each load zone.

    PROJECTS_CAP_LIMITED is the subset of PROJECTS that are capacity
    limited. Most of these will be generator types that are resource
    limited like wind, solar or geothermal, but this can be specified
    for any project. Some existing or proposed projects may have upper
    bounds on increasing capacity or replacing capacity as it is retired
    based on permits or local air quality regulations.

    proj_capacity_limit_mw[proj] is defined for generation technologies
    that are resource limited and do not compete for land area. This
    describes the maximum possible capacity of a project in units of
    megawatts.

    -- CONSTRUCTION --

    PROJECT_BUILDYEARS is a two-dimensional set of projects and the
    years in which construction or expansion occured or can occur. You
    can think of a project as a physical site that can be built out over
    time. BuildYear is the year in which construction is completed and
    new capacity comes online, not the year when constrution begins.
    BuildYear will be in the past for existing projects and will be the
    first year of an investment period for new projects. Investment
    decisions are made for each project/invest period combination. This
    set is derived from other parameters for all new construction. This
    set also includes entries for existing projects that have already
    been built; information for legacy projects come from other files
    and their build years will usually not correspond to the set of
    investment periods. There are two recommended options for
    abbreviating this set for denoting indexes: typically this should be
    written out as (proj, build_year) for clarity, but when brevity is
    more important (prj, b) is acceptable.

    NEW_PROJ_BUILDYEARS is a subset of PROJECT_BUILDYEARS that only
    includes projects that have not yet been constructed. This is
    derived by joining the set of PROJECTS with the set of
    NEW_GENERATION_BUILDYEARS using generation technology.

    EXISTING_PROJ_BUILDYEARS is a subset of PROJECT_BUILDYEARS that
    only includes existing projects.

    proj_existing_cap[(proj, build_year) in EXISTING_PROJ_BUILDYEARS] is
    a parameter that describes how much capacity was built in the past
    for existing projects.

    BuildProj[proj, build_year] is a decision variable that describes
    how much capacity of a project to install in a given period. This also
    stores the amount of capacity that was installed in existing projects
    that are still online.

    ProjCapacity[proj, period] is an expression that returns the total
    capacity online in a given period. This is the sum of installed capacity
    minus all retirements.

    Max_Build_Potential[proj] is a constraint defined for each project
    that enforces maximum capacity limits for resource-limited projects.

        ProjCapacity <= proj_capacity_limit_mw

    proj_end_year[(proj, build_year) in PROJECT_BUILDYEARS] is the last
    investment period in the simulation that a given project build will
    be operated. It can either indicate retirement or the end of the
    simulation. This is derived from g_max_age.

    --- OPERATIONS ---

    PROJECT_BUILDS_OPERATIONAL_PERIODS[proj, build_year] is an indexed
    set that describes which periods a given project build will be
    operational.

    PROJECT_PERIOD_ONLINE_BUILD_YRS[proj, period] is a complementary
    indexed set that identify which build years will still be online
    for the given project in the given period. For some project-period
    combinations, this will be an empty set.

    PROJECT_OPERATIONAL_PERIODS describes periods in which projects
    could be operational. Unlike the related sets above, it is not
    indexed. Instead it is specified as a set of (proj, period)
    combinations useful for indexing other model components.


    --- COSTS ---

    proj_connect_cost_per_mw[prj] is the cost of grid upgrades to support a
    new project, in dollars per peak MW. These costs include new
    transmission lines to a substation, substation upgrades and any
    other grid upgrades that are needed to deliver power from the
    interconnect point to the load center or from the load center to the
    broader transmission network.

    The following cost components are defined for each project and build
    year. These parameters will always be available, but will typically
    be populated by the generic costs specified in generator costs
    inputs file and the load zone cost adjustment multipliers from
    load_zones inputs file.

    proj_overnight_cost[proj, build_year] is the overnight capital cost per
    MW of capacity for building a project in the given period. By
    "installed in the given period", I mean that it comes online at the
    beginning of the given period and construction starts before that.

    proj_fixed_om[proj, build_year] is the annual fixed Operations and
    Maintenance costs (O&M) per MW of capacity for given project that
    was installed in the given period.

    -- Derived cost parameters --

    proj_capital_cost_annual[proj, build_year] is the annualized loan
    payments for a project's capital and connection costs in units of
    $/MW per year. This is specified in non-discounted real dollars in a
    future period, not real dollars in net present value.

    Proj_Fixed_Costs_Annual[proj, period] is the total annual fixed
    costs (capital as well as fixed operations & maintenance) incurred
    by a project in a period. This reflects all of the builds are
    operational in the given period. This is an expression that reflect
    decision variables.

    Total_Proj_Fixed_Costs_Annual[period] is the sum of
    Proj_Fixed_Costs_Annual[proj, period] for all projects that could be
    online in the target period. This aggregation is performed for the
    benefit of the objective function.

    --- DELAYED IMPLEMENATION ---

    The following components are not implemented at this time.

    proj_energy_capacity_overnight_cost[proj, period] defaults to the
    generic costs of the energy component of a storage technology. It
    can be overridden if different projects have different cost
    components. For new CAES projects, this could easily be overridden
    based on whether an empty gas well was nearby that could be reused,
    whether the local geological conditions made it easy or difficult to
    drill and construct underground storage, or whether an above-ground
    pressurized vessel would be needed. For new battery projects, a
    generic cost would be completely sufficient.

    proj_replacement_id[prj] is defined for projects that could replace
    existing generators.

    LOCATIONS_WITH_COMPETITION is the set of locations that have limited
    land area where multiple projects can compete for space. Members of
    this set are abbreviated as either loc or a lowercase L "l" in
    parameter names and indexes.

    loc_area_km2[l] describes the land area available for development
    at a particular location in units of square kilometers.

    proj_location[prj] is only defined for projects that compete with each
    other for limited land space at a given location. It refers to a
    member of the set LOCATIONS_WITH_COMPETITION. For example, if solar
    thermal and solar PV projects were competing for the same parcel of
    land, they would need the same location id.

    proj_land_footprint_mw_km2[prj] describes the land footprint of a project
    in units of megawatts per square kilometer.

    Max_Build_Location[location] is a constraint defined for each project
    that enforces maximum capacity limits for resource-limited locations.

        sum(BuildProj/proj_land_footprint_mw_km2) <= loc_area_km2

    ccs_pipeline_cost_per_mw[proj, build_year] is the normalize cost of
    a ccs pipeline sized relative to a project's emissions intensity.

    ProjCommitToMinBuild[proj, build_year] is a binary decision variable
    that is only defined for generation technologies that have minimum
    build requirements as specified by g_min_build_capacity[g].

    Enforce_Min_Build[proj, build_year] is a constraint that forces
    project build-outs to meet the minimum build requirements for
    generation technologies that have those requirements. This is
    defined as a pair of constraints that force BuildProj to be 0 when
    ProjCommitToMinBuild is 0, and force BuildProj to be greater than
    g_min_build_capacity when ProjCommitToMinBuild is 1. The value used
    for max_reasonable_build_capacity can be set to something like three
    times the sum of the peak demand of all load zones in a given
    period, or just to the maximum possible floating point value. When
    ProjCommitToMinBuild is 1, the upper constraint should be non-binding.

        ProjCommitToMinBuild * g_min_build_capacity <= BuildProj ...
            <= ProjCommitToMinBuild * max_reasonable_build_capacity

    Decommission[proj, build_year, period] is a decision variable that
    allows early retirement of portions of projects. Any portion of a
    project that is decomisssioned early will not incur fixed O&M
    costs and cannot be brought back into service in later periods.

    NameplateCapacity[proj, build_year, period] is an expression that
    describes the amount of capacity available from a particular project
    build in a given period. This takes into account any decomissioning
    that occured.

        NameplateCapacity = BuildProj - sum(Decommission)

    """

    mod.PROJECTS = Set()
    mod.proj_dbid = Param(mod.PROJECTS, default=lambda m, proj: proj)
    mod.proj_gen_tech = Param(mod.PROJECTS, within=mod.GENERATION_TECHNOLOGIES)
    mod.proj_load_zone = Param(mod.PROJECTS, within=mod.LOAD_ZONES)
    mod.min_data_check('PROJECTS', 'proj_gen_tech', 'proj_load_zone')
    mod.VARIABLE_PROJECTS = Set(initialize=mod.PROJECTS,
                                filter=lambda m, proj:
                                (m.g_is_variable[m.proj_gen_tech[proj]]))
    mod.BASELOAD_PROJECTS = Set(initialize=mod.PROJECTS,
                                filter=lambda m, proj:
                                (m.g_is_baseload[m.proj_gen_tech[proj]]))
    mod.LZ_PROJECTS = Set(
        mod.LOAD_ZONES,
        initialize=lambda m, lz: set(p for p in m.PROJECTS
                                     if m.proj_load_zone[p] == lz))
    mod.PROJECTS_CAP_LIMITED = Set(within=mod.PROJECTS)
    mod.proj_capacity_limit_mw = Param(mod.PROJECTS_CAP_LIMITED,
                                       within=PositiveReals)
    # Add PROJECTS_LOCATION_LIMITED & associated stuff later

    mod.FUEL_BASED_PROJECTS = Set(
        initialize=mod.PROJECTS,
        filter=lambda m, pr: m.g_uses_fuel[m.proj_gen_tech[pr]])
    mod.NON_FUEL_BASED_PROJECTS = Set(
        initialize=mod.PROJECTS,
        filter=lambda m, pr: not m.g_uses_fuel[m.proj_gen_tech[pr]])

    def init_proj_buildyears(m):
        project_buildyears = set()
        for proj in m.PROJECTS:
            g = m.proj_gen_tech[proj]
            for b in m.G_NEW_BUILD_YEARS[g]:
                project_buildyears.add((proj, b))
        return project_buildyears

    mod.NEW_PROJ_BUILDYEARS = Set(dimen=2, initialize=init_proj_buildyears)
    mod.EXISTING_PROJ_BUILDYEARS = Set(dimen=2)
    mod.proj_existing_cap = Param(mod.EXISTING_PROJ_BUILDYEARS,
                                  within=PositiveReals)
    mod.min_data_check('proj_existing_cap')
    mod.PROJECT_BUILDYEARS = Set(
        dimen=2,
        initialize=lambda m: set(m.EXISTING_PROJ_BUILDYEARS | m.
                                 NEW_PROJ_BUILDYEARS))

    def init_proj_end_year(m, proj, build_year):
        g = m.proj_gen_tech[proj]
        max_age = m.g_max_age[g]
        earliest_study_year = m.period_start[m.PERIODS.first()]
        if build_year + max_age < earliest_study_year:
            return build_year + max_age
        for p in m.PERIODS:
            if build_year + max_age < m.period_end[p]:
                break
        return p

    mod.proj_end_year = Param(mod.PROJECT_BUILDYEARS,
                              initialize=init_proj_end_year)
    mod.min_data_check('proj_end_year')

    mod.PROJECT_BUILDS_OPERATIONAL_PERIODS = Set(
        mod.PROJECT_BUILDYEARS,
        within=mod.PERIODS,
        ordered=True,
        initialize=lambda m, proj, bld_yr: set(p for p in m.PERIODS
                                               if bld_yr <= p <= m.
                                               proj_end_year[proj, bld_yr]))
    # The set of build years that could be online in the given period
    # for the given project.
    mod.PROJECT_PERIOD_ONLINE_BUILD_YRS = Set(
        mod.PROJECTS,
        mod.PERIODS,
        initialize=lambda m, proj, p: set(
            bld_yr for (prj, bld_yr) in m.PROJECT_BUILDYEARS
            if prj == proj and bld_yr <= p <= m.proj_end_year[proj, bld_yr]))

    def bounds_BuildProj(model, proj, bld_yr):
        if ((proj, bld_yr) in model.EXISTING_PROJ_BUILDYEARS):
            return (model.proj_existing_cap[proj, bld_yr],
                    model.proj_existing_cap[proj, bld_yr])
        elif (proj in model.PROJECTS_CAP_LIMITED):
            # This does not replace Max_Build_Potential because
            # Max_Build_Potential applies across all build years.
            return (0, model.proj_capacity_limit_mw[proj])
        else:
            return (0, None)

    mod.BuildProj = Var(mod.PROJECT_BUILDYEARS,
                        within=NonNegativeReals,
                        bounds=bounds_BuildProj)

    # Some projects are retired before the first study period, so they
    # don't appear in the objective function or any constraints.
    # In this case, pyomo may leave the variable value undefined even
    # after a solve, instead of assigning a value within the allowed
    # range. This causes errors in the Progressive Hedging code, which
    # expects every variable to have a value after the solve. So as a
    # starting point we assign an appropriate value to all the existing
    # projects here.
    def BuildProj_assign_default_value(m, proj, bld_yr):
        m.BuildProj[proj, bld_yr] = m.proj_existing_cap[proj, bld_yr]

    mod.BuildProj_assign_default_value = BuildAction(
        mod.EXISTING_PROJ_BUILDYEARS, rule=BuildProj_assign_default_value)

    # To Do: Subtract retirements after I write support for that.
    mod.ProjCapacity = Expression(
        mod.PROJECTS,
        mod.PERIODS,
        rule=lambda m, proj, period: sum(
            m.BuildProj[proj, bld_yr]
            for bld_yr in m.PROJECT_PERIOD_ONLINE_BUILD_YRS[proj, period]))

    mod.Max_Build_Potential = Constraint(
        mod.PROJECTS_CAP_LIMITED,
        mod.PERIODS,
        rule=lambda m, proj, p:
        (m.proj_capacity_limit_mw[proj] >= m.ProjCapacity[proj, p]))

    # Costs
    mod.proj_connect_cost_per_mw = Param(mod.PROJECTS, within=NonNegativeReals)
    mod.min_data_check('proj_connect_cost_per_mw')

    # The next few parameters need values, but those can come from
    # their parent technology or this specific project. If neither
    # data source was provided, throw an error.
    def proj_overnight_cost_default_rule(m, proj, bld_yr):
        g = m.proj_gen_tech[proj]
        if (g, bld_yr) in m.g_overnight_cost:
            return (m.g_overnight_cost[g, bld_yr] *
                    m.lz_cost_multipliers[m.proj_load_zone[proj]])
        else:
            raise ValueError(
                ("No overnight costs were provided for project {} " +
                 "or its generation technology {}.").format(proj, g))

    mod.proj_overnight_cost = Param(mod.PROJECT_BUILDYEARS,
                                    within=NonNegativeReals,
                                    default=proj_overnight_cost_default_rule)

    def proj_fixed_om_default_rule(m, proj, bld_yr):
        g = m.proj_gen_tech[proj]
        if (g, bld_yr) in m.g_fixed_o_m:
            return (m.g_fixed_o_m[g, bld_yr] *
                    m.lz_cost_multipliers[m.proj_load_zone[proj]])
        else:
            raise ValueError(
                ("No fixed O & M costs were provided for project {} " +
                 "or its generation technology {}.").format(proj, g))

    mod.proj_fixed_om = Param(mod.PROJECT_BUILDYEARS,
                              within=NonNegativeReals,
                              default=proj_fixed_om_default_rule)
    # Derived annual costs
    mod.proj_capital_cost_annual = Param(
        mod.PROJECT_BUILDYEARS,
        initialize=lambda m, proj, bld_yr:
        ((m.proj_overnight_cost[proj, bld_yr] + m.proj_connect_cost_per_mw[
            proj]) * crf(m.interest_rate, m.g_max_age[m.proj_gen_tech[proj]])))

    mod.PROJECT_OPERATIONAL_PERIODS = Set(
        dimen=2,
        initialize=lambda m: set(
            (proj, p) for (proj, bld_yr) in m.PROJECT_BUILDYEARS
            for p in m.PROJECT_BUILDS_OPERATIONAL_PERIODS[proj, bld_yr]))
    mod.Proj_Fixed_Costs_Annual = Expression(
        mod.PROJECT_OPERATIONAL_PERIODS,
        rule=lambda m, proj, p: sum(m.BuildProj[proj, bld_yr] *
                                    (m.proj_capital_cost_annual[proj, bld_yr] +
                                     m.proj_fixed_om[proj, bld_yr])
                                    for (prj, bld_yr) in m.PROJECT_BUILDYEARS
                                    if
                                    (p in m.PROJECT_BUILDS_OPERATIONAL_PERIODS[
                                        prj, bld_yr] and proj == prj)))
    # Summarize costs for the objective function. Units should be total
    # annual future costs in $base_year real dollars. The objective
    # function will convert these to base_year Net Present Value in
    # $base_year real dollars.
    mod.Total_Proj_Fixed_Costs_Annual = Expression(
        mod.PERIODS,
        rule=lambda m, p: sum(m.Proj_Fixed_Costs_Annual[proj, p] for
                              (proj, period) in m.PROJECT_OPERATIONAL_PERIODS
                              if p == period))
    mod.cost_components_annual.append('Total_Proj_Fixed_Costs_Annual')
コード例 #3
0
def define_components(m):
    
    m.PH_PROJECTS = Set()
    
    m.ph_load_zone = Param(m.PH_PROJECTS)
    
    m.ph_capital_cost_per_mw = Param(m.PH_PROJECTS, within=NonNegativeReals)
    m.ph_project_life = Param(m.PH_PROJECTS, within=NonNegativeReals)
    
    # annual O&M cost for pumped hydro project, percent of capital cost
    m.ph_fixed_om_percent = Param(m.PH_PROJECTS, within=NonNegativeReals)
    
    # total annual cost
    m.ph_fixed_cost_per_mw_per_year = Param(m.PH_PROJECTS, initialize=lambda m, p:
        m.ph_capital_cost_per_mw[p] * 
            (crf(m.interest_rate, m.ph_project_life[p]) + m.ph_fixed_om_percent[p])
    )
    
    # round-trip efficiency of the pumped hydro facility
    m.ph_efficiency = Param(m.PH_PROJECTS)
    
    # average energy available from water inflow each day
    # (system must balance energy net of this each day)
    m.ph_inflow_mw = Param(m.PH_PROJECTS)
    
    # maximum size of pumped hydro project
    m.ph_max_capacity_mw = Param(m.PH_PROJECTS)
    
    # How much pumped hydro to build
    m.BuildPumpedHydroMW = Var(m.PH_PROJECTS, m.PERIODS, within=NonNegativeReals)
    m.Pumped_Hydro_Proj_Capacity_MW = Expression(m.PH_PROJECTS, m.PERIODS, rule=lambda m, pr, pe:
        sum(m.BuildPumpedHydroMW[pr, pp] for pp in m.CURRENT_AND_PRIOR_PERIODS[pe])
    )

    # flag indicating whether any capacity is added to each project each year
    m.BuildAnyPumpedHydro = Var(m.PH_PROJECTS, m.PERIODS, within=Binary)    

    # How to run pumped hydro
    m.PumpedHydroProjGenerateMW = Var(m.PH_PROJECTS, m.TIMEPOINTS, within=NonNegativeReals)
    m.PumpedHydroProjStoreMW = Var(m.PH_PROJECTS, m.TIMEPOINTS, within=NonNegativeReals)

    # constraints on construction of pumped hydro

    # don't build more than the max allowed capacity
    m.Pumped_Hydro_Max_Build = Constraint(m.PH_PROJECTS, m.PERIODS, rule=lambda m, pr, pe:
        m.Pumped_Hydro_Proj_Capacity_MW[pr, pe] <= m.ph_max_capacity_mw[pr]
    )
    
    # force the build flag on for the year(s) when pumped hydro is built
    m.Pumped_Hydro_Set_Build_Flag = Constraint(m.PH_PROJECTS, m.PERIODS, rule=lambda m, pr, pe:
        m.BuildPumpedHydroMW[pr, pe] <= m.BuildAnyPumpedHydro[pr, pe] * m.ph_max_capacity_mw[pr]
    )
    # only build in one year (can be deactivated to allow incremental construction)
    m.Pumped_Hydro_Build_Once = Constraint(m.PH_PROJECTS, rule=lambda m, pr:
        sum(m.BuildAnyPumpedHydro[pr, pe] for pe in m.PERIODS) <= 1)
    # only build full project size (deactivated by default, to allow smaller projects)
    m.Pumped_Hydro_Build_All_Or_None = Constraint(m.PH_PROJECTS, m.PERIODS, rule=lambda m, pr, pe:
        m.BuildPumpedHydroMW[pr, pe] == m.BuildAnyPumpedHydro[pr, pe] * m.ph_max_capacity_mw[pr]
    )
    m.Deactivate_Pumped_Hydro_Build_All_Or_None = BuildAction(rule=lambda m:
        m.Pumped_Hydro_Build_All_Or_None.deactivate()
    )
    
    # limits on pumping and generation
    m.Pumped_Hydro_Max_Generate_Rate = Constraint(m.PH_PROJECTS, m.TIMEPOINTS, rule=lambda m, pr, t:
        m.PumpedHydroProjGenerateMW[pr, t]
        <=
        m.Pumped_Hydro_Proj_Capacity_MW[pr, m.tp_period[t]]
    )
    m.Pumped_Hydro_Max_Store_Rate = Constraint(m.PH_PROJECTS, m.TIMEPOINTS, rule=lambda m, pr, t:
        m.PumpedHydroProjStoreMW[pr, t]
        <=
        m.Pumped_Hydro_Proj_Capacity_MW[pr, m.tp_period[t]]
    )

    # return reservoir to at least the starting level every day, net of any inflow
    # it can also go higher than starting level, which indicates spilling surplus water
    m.Pumped_Hydro_Daily_Balance = Constraint(m.PH_PROJECTS, m.TIMESERIES, rule=lambda m, pr, ts:
        sum(
            m.PumpedHydroProjStoreMW[pr, tp] * m.ph_efficiency[pr]
            + m.ph_inflow_mw[pr]
            - m.PumpedHydroProjGenerateMW[pr, tp]
            for tp in m.TS_TPS[ts]
         ) >= 0
    )

    m.GeneratePumpedHydro = Expression(m.LOAD_ZONES, m.TIMEPOINTS, rule=lambda m, z, t:
        sum(m.PumpedHydroProjGenerateMW[pr, t] for pr in m.PH_PROJECTS if m.ph_load_zone[pr]==z)
    )
    m.StorePumpedHydro = Expression(m.LOAD_ZONES, m.TIMEPOINTS, rule=lambda m, z, t:
        sum(m.PumpedHydroProjStoreMW[pr, t] for pr in m.PH_PROJECTS if m.ph_load_zone[pr]==z)
    )
    
    # calculate costs
    m.Pumped_Hydro_Fixed_Cost_Annual = Expression(m.PERIODS, rule=lambda m, pe:
        sum(m.ph_fixed_cost_per_mw_per_year[pr] * m.Pumped_Hydro_Proj_Capacity_MW[pr, pe] for pr in m.PH_PROJECTS)
    )
    m.cost_components_annual.append('Pumped_Hydro_Fixed_Cost_Annual')
    
    # add the pumped hydro to the model's energy balance
    m.LZ_Energy_Components_Produce.append('GeneratePumpedHydro')
    m.LZ_Energy_Components_Consume.append('StorePumpedHydro')
    
    # total pumped hydro capacity in each zone each period (for reporting)
    m.Pumped_Hydro_Capacity_MW = Expression(m.LOAD_ZONES, m.PERIODS, rule=lambda m, z, pe:
        sum(m.Pumped_Hydro_Proj_Capacity_MW[pr, pe] for pr in m.PH_PROJECTS if m.ph_load_zone[pr]==z)
    )
        
    # force construction of a fixed amount of pumped hydro
    if m.options.ph_mw is not None:
        print "Forcing construction of {m} MW of pumped hydro.".format(m=m.options.ph_mw)
        m.Build_Pumped_Hydro_MW = Constraint(m.LOAD_ZONES, rule=lambda m, z:
            m.Pumped_Hydro_Capacity_MW[z, m.PERIODS.last()] == m.options.ph_mw
        )
    # force construction of pumped hydro only in a certain period
    if m.options.ph_year is not None:
        print "Allowing construction of pumped hydro only in {p}.".format(p=m.options.ph_year)
        m.Build_Pumped_Hydro_Year = Constraint(
            m.PH_PROJECTS, m.PERIODS,
            rule=lambda m, pr, pe:
                m.BuildPumpedHydroMW[pr, pe] == 0.0 if pe != m.options.ph_year else Constraint.Skip
        )
コード例 #4
0
ファイル: hydrogen.py プロジェクト: ranjitster/switch
def define_components(m):
    
    # make helper set identifying all timeseries in each period
    if hasattr(m, "PERIOD_TS"):
        print "DEPRECATION NOTE: PERIOD_TS is defined in hydrogen.py, but it already exists, so this can be removed."
    else:
        m.PERIOD_TS = Set(m.PERIODS, ordered=True, within=m.TIMESERIES, initialize=lambda m, p:
            [ts for ts in m.TIMESERIES if m.ts_period[ts] == p])

    # electrolyzer details
    m.hydrogen_electrolyzer_capital_cost_per_mw = Param()
    m.hydrogen_electrolyzer_fixed_cost_per_mw_year = Param(default=0.0)
    m.hydrogen_electrolyzer_variable_cost_per_kg = Param(default=0.0)  # assumed to include any refurbishment needed
    m.hydrogen_electrolyzer_kg_per_mwh = Param() # assumed to deliver H2 at enough pressure for liquifier and daily buffering
    m.hydrogen_electrolyzer_life_years = Param()
    m.BuildElectrolyzerMW = Var(m.LOAD_ZONES, m.PERIODS, within=NonNegativeReals)
    m.ElectrolyzerCapacityMW = Expression(m.LOAD_ZONES, m.PERIODS, rule=lambda m, z, p: 
        sum(m.BuildElectrolyzerMW[z, p_] for p_ in m.CURRENT_AND_PRIOR_PERIODS[p]))
    m.RunElectrolyzerMW = Var(m.LOAD_ZONES, m.TIMEPOINTS, within=NonNegativeReals)
    m.ProduceHydrogenKgPerHour = Expression(m.LOAD_ZONES, m.TIMEPOINTS, rule=lambda m, z, t:
        m.RunElectrolyzerMW[z, t] * m.hydrogen_electrolyzer_kg_per_mwh)

    # note: we assume there is a gaseous hydrogen storage tank that is big enough to buffer
    # daily production, storage and withdrawals of hydrogen, but we don't include a cost
    # for this (because it will be negligible compared to the rest of the costs)
    # This allows the system to do some intra-day arbitrage without going all the way to liquification

    # liquifier details
    m.hydrogen_liquifier_capital_cost_per_kg_per_hour = Param()
    m.hydrogen_liquifier_fixed_cost_per_kg_hour_year = Param(default=0.0)
    m.hydrogen_liquifier_variable_cost_per_kg = Param(default=0.0)
    m.hydrogen_liquifier_mwh_per_kg = Param()
    m.hydrogen_liquifier_life_years = Param()
    m.BuildLiquifierKgPerHour = Var(m.LOAD_ZONES, m.PERIODS, within=NonNegativeReals)  # capacity to build, measured in kg/hour of throughput
    m.LiquifierCapacityKgPerHour = Expression(m.LOAD_ZONES, m.PERIODS, rule=lambda m, z, p: 
        sum(m.BuildLiquifierKgPerHour[z, p_] for p_ in m.CURRENT_AND_PRIOR_PERIODS[p]))
    m.LiquifyHydrogenKgPerHour = Var(m.LOAD_ZONES, m.TIMEPOINTS, within=NonNegativeReals)
    m.LiquifyHydrogenMW = Expression(m.LOAD_ZONES, m.TIMEPOINTS, rule=lambda m, z, t:
        m.LiquifyHydrogenKgPerHour[z, t] * m.hydrogen_liquifier_mwh_per_kg
    )
    
    # storage tank details
    m.liquid_hydrogen_tank_capital_cost_per_kg = Param()
    m.liquid_hydrogen_tank_life_years = Param()
    m.BuildLiquidHydrogenTankKg = Var(m.LOAD_ZONES, m.PERIODS, within=NonNegativeReals) # in kg
    m.LiquidHydrogenTankCapacityKg = Expression(m.LOAD_ZONES, m.PERIODS, rule=lambda m, z, p:
        sum(m.BuildLiquidHydrogenTankKg[z, p_] for p_ in m.CURRENT_AND_PRIOR_PERIODS[p]))
    m.StoreLiquidHydrogenKg = Expression(m.LOAD_ZONES, m.TIMESERIES, rule=lambda m, z, ts:
        m.ts_duration_of_tp[ts] * sum(m.LiquifyHydrogenKgPerHour[z, tp] for tp in m.TS_TPS[ts])
    )
    m.WithdrawLiquidHydrogenKg = Var(m.LOAD_ZONES, m.TIMESERIES, within=NonNegativeReals)
    # note: we assume the system will be large enough to neglect boil-off

    # fuel cell details
    m.hydrogen_fuel_cell_capital_cost_per_mw = Param()
    m.hydrogen_fuel_cell_fixed_cost_per_mw_year = Param(default=0.0)
    m.hydrogen_fuel_cell_variable_cost_per_mwh = Param(default=0.0) # assumed to include any refurbishment needed
    m.hydrogen_fuel_cell_mwh_per_kg = Param()
    m.hydrogen_fuel_cell_life_years = Param()
    m.BuildFuelCellMW = Var(m.LOAD_ZONES, m.PERIODS, within=NonNegativeReals)
    m.FuelCellCapacityMW = Expression(m.LOAD_ZONES, m.PERIODS, rule=lambda m, z, p: 
        sum(m.BuildFuelCellMW[z, p_] for p_ in m.CURRENT_AND_PRIOR_PERIODS[p]))
    m.DispatchFuelCellMW = Var(m.LOAD_ZONES, m.TIMEPOINTS, within=NonNegativeReals)
    m.ConsumeHydrogenKgPerHour = Expression(m.LOAD_ZONES, m.TIMEPOINTS, rule=lambda m, z, t:
        m.DispatchFuelCellMW[z, t] / m.hydrogen_fuel_cell_mwh_per_kg
    )

    # hydrogen mass balances
    # note: this allows for buffering of same-day production and consumption 
    # of hydrogen without ever liquifying it
    m.Hydrogen_Conservation_of_Mass_Daily = Constraint(m.LOAD_ZONES, m.TIMESERIES, rule=lambda m, z, ts:
        m.StoreLiquidHydrogenKg[z, ts] - m.WithdrawLiquidHydrogenKg[z, ts]
        == 
        m.ts_duration_of_tp[ts] * sum(
            m.ProduceHydrogenKgPerHour[z, tp] - m.ConsumeHydrogenKgPerHour[z, tp] 
            for tp in m.TS_TPS[ts]
        )
    )
    m.Hydrogen_Conservation_of_Mass_Annual = Constraint(m.LOAD_ZONES, m.PERIODS, rule=lambda m, z, p:
        sum(
            (m.StoreLiquidHydrogenKg[z, ts] - m.WithdrawLiquidHydrogenKg[z, ts]) 
                * m.ts_scale_to_year[ts]
            for ts in m.PERIOD_TS[p]
        ) == 0
    )

    # limits on equipment
    m.Max_Run_Electrolyzer = Constraint(m.LOAD_ZONES, m.TIMEPOINTS, rule=lambda m, z, t:
        m.RunElectrolyzerMW[z, t] <= m.ElectrolyzerCapacityMW[z, m.tp_period[t]])
    m.Max_Run_Fuel_Cell = Constraint(m.LOAD_ZONES, m.TIMEPOINTS, rule=lambda m, z, t:
        m.DispatchFuelCellMW[z, t] <= m.FuelCellCapacityMW[z, m.tp_period[t]])
    m.Max_Run_Liquifier = Constraint(m.LOAD_ZONES, m.TIMEPOINTS, rule=lambda m, z, t:
        m.LiquifyHydrogenKgPerHour[z, t] <= m.LiquifierCapacityKgPerHour[z, m.tp_period[t]])
    
    # there must be enough storage to hold _all_ the production each period (net of same-day consumption)
    # note: this assumes we cycle the system only once per year (store all energy, then release all energy)
    # alternatives: allow monthly or seasonal cycling, or directly model the whole year with inter-day linkages
    m.Max_Store_Liquid_Hydrogen = Constraint(m.LOAD_ZONES, m.PERIODS, rule=lambda m, z, p:
        sum(m.StoreLiquidHydrogenKg[z, ts] * m.ts_scale_to_year[ts] for ts in m.PERIOD_TS[p])
        <= m.LiquidHydrogenTankCapacityKg[z, p]
    )
    
    # add electricity consumption and production to the model
    m.LZ_Energy_Components_Consume.append('RunElectrolyzerMW')
    m.LZ_Energy_Components_Consume.append('LiquifyHydrogenMW')
    m.LZ_Energy_Components_Produce.append('DispatchFuelCellMW')

    # add costs to the model
    m.HydrogenVariableCost = Expression(m.TIMEPOINTS, rule=lambda m, t:
        sum(
            m.ProduceHydrogenKgPerHour[z, t] * m.hydrogen_electrolyzer_variable_cost_per_kg
            + m.LiquifyHydrogenKgPerHour[z, t] * m.hydrogen_liquifier_variable_cost_per_kg
            + m.DispatchFuelCellMW[z, t] * m.hydrogen_fuel_cell_variable_cost_per_mwh
            for z in m.LOAD_ZONES
        )
    )
    m.HydrogenFixedCostAnnual = Expression(m.PERIODS, rule=lambda m, p:
        sum(
            m.ElectrolyzerCapacityMW[z, p] * (
                m.hydrogen_electrolyzer_capital_cost_per_mw * crf(m.interest_rate, m.hydrogen_electrolyzer_life_years)
                + m.hydrogen_electrolyzer_fixed_cost_per_mw_year)
            + m.LiquifierCapacityKgPerHour[z, p] * (
                m.hydrogen_liquifier_capital_cost_per_kg_per_hour * crf(m.interest_rate, m.hydrogen_liquifier_life_years)
                + m.hydrogen_liquifier_fixed_cost_per_kg_hour_year)
            + m.LiquidHydrogenTankCapacityKg[z, p] * (
                m.liquid_hydrogen_tank_capital_cost_per_kg * crf(m.interest_rate, m.liquid_hydrogen_tank_life_years))
            + m.FuelCellCapacityMW[z, p] * (
                m.hydrogen_fuel_cell_capital_cost_per_mw * crf(m.interest_rate, m.hydrogen_fuel_cell_life_years)
                + m.hydrogen_fuel_cell_fixed_cost_per_mw_year)
            for z in m.LOAD_ZONES
        )
    )
    m.cost_components_tp.append('HydrogenVariableCost')
    m.cost_components_annual.append('HydrogenFixedCostAnnual')
コード例 #5
0
ファイル: build.py プロジェクト: sergiocastellanos/switch
def define_components(mod):
    """

    Adds components to a Pyomo abstract model object to describe
    generation and storage projects. Unless otherwise stated, all power
    capacity is specified in units of MW and all sets and parameters
    are mandatory.

    PROJECTS is the set of generation and storage projects that have
    been built or could potentially be built. A project is a combination
    of generation technology, load zone and location. A particular
    build-out of a project should also include the year in which
    construction was complete and additional capacity came online.
    Members of this set are abbreviated as proj or prj in parameter
    names and indexes. Use of p instead of prj is discouraged because p
    is reserved for period.

    proj_dbid[prj] is an external database id for each project. This is
    an optional parameter than defaults to the project index.

    proj_gen_tech[prj] describes what kind of generation technology a
    projects is using.

    proj_load_zone[prj] is the load zone this project is built in.

    VARIABLE_PROJECTS is a subset of PROJECTS that only includes
    variable generators such as wind or solar that have exogenous
    constraints on their energy production.

    BASELOAD_PROJECTS is a subset of PROJECTS that only includes
    baseload generators such as coal or geothermal.

    LZ_PROJECTS[lz in LOAD_ZONES] is an indexed set that lists all
    projects within each load zone.

    PROJECTS_CAP_LIMITED is the subset of PROJECTS that are capacity
    limited. Most of these will be generator types that are resource
    limited like wind, solar or geothermal, but this can be specified
    for any project. Some existing or proposed projects may have upper
    bounds on increasing capacity or replacing capacity as it is retired
    based on permits or local air quality regulations.

    proj_capacity_limit_mw[proj] is defined for generation technologies
    that are resource limited and do not compete for land area. This
    describes the maximum possible capacity of a project in units of
    megawatts.

    -- CONSTRUCTION --

    PROJECT_BUILDYEARS is a two-dimensional set of projects and the
    years in which construction or expansion occured or can occur. You
    can think of a project as a physical site that can be built out over
    time. BuildYear is the year in which construction is completed and
    new capacity comes online, not the year when constrution begins.
    BuildYear will be in the past for existing projects and will be the
    first year of an investment period for new projects. Investment
    decisions are made for each project/invest period combination. This
    set is derived from other parameters for all new construction. This
    set also includes entries for existing projects that have already
    been built; information for legacy projects come from other files
    and their build years will usually not correspond to the set of
    investment periods. There are two recommended options for
    abbreviating this set for denoting indexes: typically this should be
    written out as (proj, build_year) for clarity, but when brevity is
    more important (prj, b) is acceptable.

    NEW_PROJ_BUILDYEARS is a subset of PROJECT_BUILDYEARS that only
    includes projects that have not yet been constructed. This is
    derived by joining the set of PROJECTS with the set of
    NEW_GENERATION_BUILDYEARS using generation technology.

    EXISTING_PROJ_BUILDYEARS is a subset of PROJECT_BUILDYEARS that
    only includes existing projects.

    proj_existing_cap[(proj, build_year) in EXISTING_PROJ_BUILDYEARS] is
    a parameter that describes how much capacity was built in the past
    for existing projects.

    BuildProj[proj, build_year] is a decision variable that describes
    how much capacity of a project to install in a given period. This also
    stores the amount of capacity that was installed in existing projects
    that are still online.

    ProjCapacity[proj, period] is an expression that returns the total
    capacity online in a given period. This is the sum of installed capacity
    minus all retirements.

    Max_Build_Potential[proj] is a constraint defined for each project
    that enforces maximum capacity limits for resource-limited projects.

        ProjCapacity <= proj_capacity_limit_mw

    proj_end_year[(proj, build_year) in PROJECT_BUILDYEARS] is the last
    investment period in the simulation that a given project build will
    be operated. It can either indicate retirement or the end of the
    simulation. This is derived from g_max_age.

    --- OPERATIONS ---

    PROJECT_BUILDS_OPERATIONAL_PERIODS[proj, build_year] is an indexed
    set that describes which periods a given project build will be
    operational.

    PROJECT_PERIOD_ONLINE_BUILD_YRS[proj, period] is a complementary
    indexed set that identify which build years will still be online
    for the given project in the given period. For some project-period
    combinations, this will be an empty set.

    PROJECT_OPERATIONAL_PERIODS describes periods in which projects
    could be operational. Unlike the related sets above, it is not
    indexed. Instead it is specified as a set of (proj, period)
    combinations useful for indexing other model components.


    --- COSTS ---

    proj_connect_cost_per_mw[prj] is the cost of grid upgrades to support a
    new project, in dollars per peak MW. These costs include new
    transmission lines to a substation, substation upgrades and any
    other grid upgrades that are needed to deliver power from the
    interconnect point to the load center or from the load center to the
    broader transmission network.

    The following cost components are defined for each project and build
    year. These parameters will always be available, but will typically
    be populated by the generic costs specified in generator costs
    inputs file and the load zone cost adjustment multipliers from
    load_zones inputs file.

    proj_overnight_cost[proj, build_year] is the overnight capital cost per
    MW of capacity for building a project in the given period. By
    "installed in the given period", I mean that it comes online at the
    beginning of the given period and construction starts before that.

    proj_fixed_om[proj, build_year] is the annual fixed Operations and
    Maintenance costs (O&M) per MW of capacity for given project that
    was installed in the given period.

    -- Derived cost parameters --

    proj_capital_cost_annual[proj, build_year] is the annualized loan
    payments for a project's capital and connection costs in units of
    $/MW per year. This is specified in non-discounted real dollars in a
    future period, not real dollars in net present value.

    Proj_Fixed_Costs_Annual[proj, period] is the total annual fixed
    costs (capital as well as fixed operations & maintenance) incurred
    by a project in a period. This reflects all of the builds are
    operational in the given period. This is an expression that reflect
    decision variables.

    Total_Proj_Fixed_Costs_Annual[period] is the sum of
    Proj_Fixed_Costs_Annual[proj, period] for all projects that could be
    online in the target period. This aggregation is performed for the
    benefit of the objective function.

    --- DELAYED IMPLEMENATION ---

    The following components are not implemented at this time.

    proj_energy_capacity_overnight_cost[proj, period] defaults to the
    generic costs of the energy component of a storage technology. It
    can be overridden if different projects have different cost
    components. For new CAES projects, this could easily be overridden
    based on whether an empty gas well was nearby that could be reused,
    whether the local geological conditions made it easy or difficult to
    drill and construct underground storage, or whether an above-ground
    pressurized vessel would be needed. For new battery projects, a
    generic cost would be completely sufficient.

    proj_replacement_id[prj] is defined for projects that could replace
    existing generators.

    LOCATIONS_WITH_COMPETITION is the set of locations that have limited
    land area where multiple projects can compete for space. Members of
    this set are abbreviated as either loc or a lowercase L "l" in
    parameter names and indexes.

    loc_area_km2[l] describes the land area available for development
    at a particular location in units of square kilometers.

    proj_location[prj] is only defined for projects that compete with each
    other for limited land space at a given location. It refers to a
    member of the set LOCATIONS_WITH_COMPETITION. For example, if solar
    thermal and solar PV projects were competing for the same parcel of
    land, they would need the same location id.

    proj_land_footprint_mw_km2[prj] describes the land footprint of a project
    in units of megawatts per square kilometer.

    Max_Build_Location[location] is a constraint defined for each project
    that enforces maximum capacity limits for resource-limited locations.

        sum(BuildProj/proj_land_footprint_mw_km2) <= loc_area_km2

    ccs_pipeline_cost_per_mw[proj, build_year] is the normalize cost of
    a ccs pipeline sized relative to a project's emissions intensity.

    ProjCommitToMinBuild[proj, build_year] is a binary decision variable
    that is only defined for generation technologies that have minimum
    build requirements as specified by g_min_build_capacity[g].

    Enforce_Min_Build[proj, build_year] is a constraint that forces
    project build-outs to meet the minimum build requirements for
    generation technologies that have those requirements. This is
    defined as a pair of constraints that force BuildProj to be 0 when
    ProjCommitToMinBuild is 0, and force BuildProj to be greater than
    g_min_build_capacity when ProjCommitToMinBuild is 1. The value used
    for max_reasonable_build_capacity can be set to something like three
    times the sum of the peak demand of all load zones in a given
    period, or just to the maximum possible floating point value. When
    ProjCommitToMinBuild is 1, the upper constraint should be non-binding.

        ProjCommitToMinBuild * g_min_build_capacity <= BuildProj ...
            <= ProjCommitToMinBuild * max_reasonable_build_capacity

    Decommission[proj, build_year, period] is a decision variable that
    allows early retirement of portions of projects. Any portion of a
    project that is decomisssioned early will not incur fixed O&M
    costs and cannot be brought back into service in later periods.

    NameplateCapacity[proj, build_year, period] is an expression that
    describes the amount of capacity available from a particular project
    build in a given period. This takes into account any decomissioning
    that occured.

        NameplateCapacity = BuildProj - sum(Decommission)

    """

    mod.PROJECTS = Set()
    mod.proj_dbid = Param(mod.PROJECTS, default=lambda m, proj: proj)
    mod.proj_gen_tech = Param(mod.PROJECTS, within=mod.GENERATION_TECHNOLOGIES)
    mod.proj_load_zone = Param(mod.PROJECTS, within=mod.LOAD_ZONES)
    mod.min_data_check('PROJECTS', 'proj_gen_tech', 'proj_load_zone')
    mod.VARIABLE_PROJECTS = Set(
        initialize=mod.PROJECTS,
        filter=lambda m, proj: (
            m.g_is_variable[m.proj_gen_tech[proj]]))
    mod.BASELOAD_PROJECTS = Set(
        initialize=mod.PROJECTS,
        filter=lambda m, proj: (
            m.g_is_baseload[m.proj_gen_tech[proj]]))
    mod.LZ_PROJECTS = Set(
        mod.LOAD_ZONES,
        initialize=lambda m, lz: set(
            p for p in m.PROJECTS if m.proj_load_zone[p] == lz))
    mod.PROJECTS_CAP_LIMITED = Set(within=mod.PROJECTS)
    mod.proj_capacity_limit_mw = Param(
        mod.PROJECTS_CAP_LIMITED,
        within=PositiveReals)
    # Add PROJECTS_LOCATION_LIMITED & associated stuff later

    mod.FUEL_BASED_PROJECTS = Set(
        initialize=mod.PROJECTS,
        filter=lambda m, pr: m.g_uses_fuel[m.proj_gen_tech[pr]])
    mod.NON_FUEL_BASED_PROJECTS = Set(
        initialize=mod.PROJECTS,
        filter=lambda m, pr: not m.g_uses_fuel[m.proj_gen_tech[pr]])

    def init_proj_buildyears(m):
        project_buildyears = set()
        for proj in m.PROJECTS:
            g = m.proj_gen_tech[proj]
            for b in m.G_NEW_BUILD_YEARS[g]:
                project_buildyears.add((proj, b))
        return project_buildyears
    mod.NEW_PROJ_BUILDYEARS = Set(
        dimen=2,
        initialize=init_proj_buildyears)
    mod.EXISTING_PROJ_BUILDYEARS = Set(
        dimen=2)
    mod.proj_existing_cap = Param(
        mod.EXISTING_PROJ_BUILDYEARS,
        within=PositiveReals)
    mod.min_data_check('proj_existing_cap')
    mod.PROJECT_BUILDYEARS = Set(
        dimen=2,
        initialize=lambda m: set(
            m.EXISTING_PROJ_BUILDYEARS | m.NEW_PROJ_BUILDYEARS))

    def init_proj_end_year(m, proj, build_year):
        g = m.proj_gen_tech[proj]
        max_age = m.g_max_age[g]
        earliest_study_year = m.period_start[m.PERIODS.first()]
        if build_year + max_age < earliest_study_year:
            return build_year + max_age
        for p in m.PERIODS:
            if build_year + max_age < m.period_end[p]:
                break
        return p
    mod.proj_end_year = Param(
        mod.PROJECT_BUILDYEARS,
        initialize=init_proj_end_year)
    mod.min_data_check('proj_end_year')

    mod.PROJECT_BUILDS_OPERATIONAL_PERIODS = Set(
        mod.PROJECT_BUILDYEARS,
        within=mod.PERIODS,
        ordered=True,
        initialize=lambda m, proj, bld_yr: set(
            p for p in m.PERIODS
            if bld_yr <= p <= m.proj_end_year[proj, bld_yr]))
    # The set of build years that could be online in the given period
    # for the given project.
    mod.PROJECT_PERIOD_ONLINE_BUILD_YRS = Set(
        mod.PROJECTS, mod.PERIODS,
        initialize=lambda m, proj, p: set(
            bld_yr for (prj, bld_yr) in m.PROJECT_BUILDYEARS
            if prj == proj and bld_yr <= p <= m.proj_end_year[proj, bld_yr]))

    def bounds_BuildProj(model, proj, bld_yr):
        if((proj, bld_yr) in model.EXISTING_PROJ_BUILDYEARS):
            return (model.proj_existing_cap[proj, bld_yr],
                    model.proj_existing_cap[proj, bld_yr])
        elif(proj in model.PROJECTS_CAP_LIMITED):
            # This does not replace Max_Build_Potential because
            # Max_Build_Potential applies across all build years.
            return (0, model.proj_capacity_limit_mw[proj])
        else:
            return (0, None)
    mod.BuildProj = Var(
        mod.PROJECT_BUILDYEARS,
        within=NonNegativeReals,
        bounds=bounds_BuildProj)
    # Some projects are retired before the first study period, so they
    # don't appear in the objective function or any constraints. 
    # In this case, pyomo may leave the variable value undefined even 
    # after a solve, instead of assigning a value within the allowed
    # range. This causes errors in the Progressive Hedging code, which
    # expects every variable to have a value after the solve. So as a 
    # starting point we assign an appropriate value to all the existing 
    # projects here.
    def BuildProj_assign_default_value(m, proj, bld_yr):
        m.BuildProj[proj, bld_yr] = m.proj_existing_cap[proj, bld_yr]
    mod.BuildProj_assign_default_value = BuildAction(
        mod.EXISTING_PROJ_BUILDYEARS,
        rule=BuildProj_assign_default_value
    )

    # To Do: Subtract retirements after I write support for that.
    mod.ProjCapacity = Expression(
        mod.PROJECTS, mod.PERIODS,
        rule=lambda m, proj, period: sum(
            m.BuildProj[proj, bld_yr]
            for bld_yr in m.PROJECT_PERIOD_ONLINE_BUILD_YRS[proj, period]))

    mod.Max_Build_Potential = Constraint(
        mod.PROJECTS_CAP_LIMITED, mod.PERIODS,
        rule=lambda m, proj, p: (
            m.proj_capacity_limit_mw[proj] >= m.ProjCapacity[proj, p]))

    # Costs
    mod.proj_connect_cost_per_mw = Param(mod.PROJECTS, within=NonNegativeReals)
    mod.min_data_check('proj_connect_cost_per_mw')

    # The next few parameters need values, but those can come from
    # their parent technology or this specific project. If neither
    # data source was provided, throw an error.
    def proj_overnight_cost_default_rule(m, proj, bld_yr):
        g = m.proj_gen_tech[proj]
        if (g, bld_yr) in m.g_overnight_cost:
            return(m.g_overnight_cost[g, bld_yr] *
                   m.lz_cost_multipliers[m.proj_load_zone[proj]])
        else:
            raise ValueError(
                ("No overnight costs were provided for project {} " +
                 "or its generation technology {}.").format(proj, g))
    mod.proj_overnight_cost = Param(
        mod.PROJECT_BUILDYEARS,
        within=NonNegativeReals,
        default=proj_overnight_cost_default_rule)

    def proj_fixed_om_default_rule(m, proj, bld_yr):
        g = m.proj_gen_tech[proj]
        if (g, bld_yr) in m.g_fixed_o_m:
            return(m.g_fixed_o_m[g, bld_yr] *
                   m.lz_cost_multipliers[m.proj_load_zone[proj]])
        else:
            raise ValueError(
                ("No fixed O & M costs were provided for project {} " +
                 "or its generation technology {}.").format(pr, g))
    mod.proj_fixed_om = Param(
        mod.PROJECT_BUILDYEARS,
        within=NonNegativeReals,
        default=proj_fixed_om_default_rule)
    # Derived annual costs
    mod.proj_capital_cost_annual = Param(
        mod.PROJECT_BUILDYEARS,
        initialize=lambda m, proj, bld_yr: (
            (m.proj_overnight_cost[proj, bld_yr] +
                m.proj_connect_cost_per_mw[proj]) *
            crf(m.interest_rate, m.g_max_age[m.proj_gen_tech[proj]])))

    mod.PROJECT_OPERATIONAL_PERIODS = Set(
        dimen=2,
        initialize=lambda m: set(
            (proj, p)
            for (proj, bld_yr) in m.PROJECT_BUILDYEARS
            for p in m.PROJECT_BUILDS_OPERATIONAL_PERIODS[proj, bld_yr]))
    mod.Proj_Fixed_Costs_Annual = Expression(
        mod.PROJECT_OPERATIONAL_PERIODS,
        rule=lambda m, proj, p: sum(
            m.BuildProj[proj, bld_yr] *
            (m.proj_capital_cost_annual[proj, bld_yr] +
             m.proj_fixed_om[proj, bld_yr])
            for (prj, bld_yr) in m.PROJECT_BUILDYEARS
            if (p in m.PROJECT_BUILDS_OPERATIONAL_PERIODS[prj, bld_yr] and
                proj == prj)))
    # Summarize costs for the objective function. Units should be total
    # annual future costs in $base_year real dollars. The objective
    # function will convert these to base_year Net Present Value in
    # $base_year real dollars.
    mod.Total_Proj_Fixed_Costs_Annual = Expression(
        mod.PERIODS,
        rule=lambda m, p: sum(
            m.Proj_Fixed_Costs_Annual[proj, p]
            for (proj, period) in m.PROJECT_OPERATIONAL_PERIODS
            if p == period))
    mod.cost_components_annual.append('Total_Proj_Fixed_Costs_Annual')
コード例 #6
0
ファイル: trans_build.py プロジェクト: bmaluenda/switch
def define_components(mod):
    """

    Adds components to a Pyomo abstract model object to describe bulk
    transmission of an electric grid. This includes parameters, build
    decisions and constraints. Unless otherwise stated, all power
    capacity is specified in units of MW and all sets and parameters are
    mandatory.

    TRANSMISSION_LINES is the complete set of transmission pathways
    connecting load zones. Each member of this set is a one dimensional
    identifier such as "A-B". This set has no regard for directionality
    of transmisison lines and will generate an error if you specify two
    lines that move in opposite directions such as (A to B) and (B to
    A). Another derived set - TRANS_LINES_DIRECTIONAL - stores
    directional information. Transmission may be abbreviated as trans or
    tx in parameter names or indexes.

    trans_lz1[tx] and trans_lz2[tx] specify the load zones at either end
    of a transmission line. The order of 1 and 2 is unimportant, but you
    are encouraged to be consistent to simplify merging information back
    into external databases.

    trans_dbid[tx in TRANSMISSION_LINES] is an external database
    identifier for each transmission line. This is an optional parameter
    than defaults to the identifier of the transmission line.

    trans_length_km[tx in TRANSMISSION_LINES] is the length of each
    transmission line in kilometers.

    trans_efficiency[tx in TRANSMISSION_LINES] is the proportion of
    energy sent down a line that is delivered. If 2 percent of energy
    sent down a line is lost, this value would be set to 0.98.

    trans_new_build_allowed[tx in TRANSMISSION_LINES] is a binary value
    indicating whether new transmission build-outs are allowed along a
    transmission line. This optional parameter defaults to True.

    TRANS_BUILD_YEARS is the set of transmission lines and years in
    which they have been or could be built. This set includes past and
    potential future builds. All future builds must come online in the
    first year of an investment period. This set is composed of two
    elements with members: (tx, build_year). For existing transmission
    where the build years are not known, build_year is set to 'Legacy'.

    EXISTING_TRANS_BLD_YRS is a subset of TRANS_BUILD_YEARS that lists
    builds that happened before the first investment period. For most
    datasets the build year is unknown, so is it always set to 'Legacy'.

    existing_trans_cap[tx in TRANSMISSION_LINES] is a parameter that
    describes how many MW of capacity has been installed before the
    start of the study.

    NEW_TRANS_BLD_YRS is a subset of TRANS_BUILD_YEARS that describes
    potential builds.

    BuildTrans[(tx, bld_yr) in TRANS_BUILD_YEARS] is a decision variable
    that describes the transfer capacity in MW installed on a cooridor
    in a given build year. For existing builds, this variable is locked
    to the existing capacity.

    TransCapacity[(tx, bld_yr) in TRANS_BUILD_YEARS] is an expression
    that returns the total nameplate transfer capacity of a transmission
    line in a given period. This is the sum of existing and newly-build
    capacity.

    trans_derating_factor[tx in TRANSMISSION_LINES] is an overall
    derating factor for each transmission line that can reflect forced
    outage rates, stability or contingency limitations. This parameter
    is optional and defaults to 1. This parameter should be in the
    range of 0 to 1, being 0 a value that disables the line completely.

    TransCapacityAvailable[(tx, bld_yr) in TRANS_BUILD_YEARS] is an
    expression that returns the available transfer capacity of a
    transmission line in a given period, taking into account the
    nameplate capacity and derating factor.

    trans_terrain_multiplier[tx in TRANSMISSION_LINES] is
    a cost adjuster applied to each transmission line that reflects the
    additional costs that may be incurred for traversing that specific
    terrain. Crossing mountains or cities will be more expensive than
    crossing plains. This parameter is optional and defaults to 1. This
    parameter should be in the range of 0.5 to 3.

    trans_capital_cost_per_mw_km describes the generic costs of building
    new transmission in units of $BASE_YEAR per MW transfer capacity per
    km. This is optional and defaults to 1000.

    trans_lifetime_yrs is the number of years in which a capital
    construction loan for a new transmission line is repaid. This
    optional parameter defaults to 20 years based on 2009 WREZ
    transmission model transmission data. At the end of this time,
    we assume transmission lines will be rebuilt at the same cost.

    trans_fixed_o_m_fraction is describes the fixed Operations and
    Maintenance costs as a fraction of capital costs. This optional
    parameter defaults to 0.03 based on 2009 WREZ transmission model
    transmission data costs for existing transmission maintenance.

    trans_cost_hourly[tx TRANSMISSION_LINES] is the cost of building
    transmission lines in units of $BASE_YEAR / MW- transfer-capacity /
    hour. This derived parameter is based on the total annualized
    capital and fixed O&M costs, then divides that by hours per year to
    determine the portion of costs incurred hourly.

    TRANS_DIRECTIONAL is a derived set of directional paths that
    electricity can flow along transmission lines. Each element of this
    set is a two-dimensional entry that describes the origin and
    destination of the flow: (load_zone_from, load_zone_to). Every
    transmission line will generate two entries in this set. Members of
    this set are abbreviated as trans_d where possible, but may be
    abbreviated as tx in situations where brevity is important and it is
    unlikely to be confused with the overall transmission line.

    trans_d_line[trans_d] is the transmission line associated with this
    directional path.

    PERIOD_RELEVANT_TRANS_BUILDS[p in PERIODS] is an indexed set that
    describes which transmission builds will be operational in a given
    period. Currently, transmission lines are kept online indefinitely,
    with parts being replaced as they wear out.
    
    PERIOD_RELEVANT_TRANS_BUILDS[p] will return a subset of (tx, bld_yr)
    in TRANS_BUILD_YEARS.

    --- Delayed implementation ---

    is_dc_line ... Do I even need to implement this?

    --- NOTES ---

    The cost stream over time for transmission lines differs from the
    SWITCH-WECC model. The SWITCH-WECC model assumed new transmission
    had a financial lifetime of 20 years, which was the length of the
    loan term. During this time, fixed operations & maintenance costs
    were also incurred annually and these were estimated to be 3 percent
    of the initial capital costs. These fixed O&M costs were obtained
    from the 2009 WREZ transmission model transmission data costs for
    existing transmission maintenance .. most of those lines were old
    and their capital loans had been paid off, so the O&M were the costs
    of keeping them operational. SWITCH-WECC basically assumed the lines
    could be kept online indefinitely with that O&M budget, with
    components of the lines being replaced as needed. This payment
    schedule and lifetimes was assumed to hold for both existing and new
    lines. This made the annual costs change over time, which could
    create edge effects near the end of the study period. SWITCH-WECC
    had different cost assumptions for local T&D; capital expenses and
    fixed O&M expenses were rolled in together, and those were assumed
    to continue indefinitely. This basically assumed that local T&D would
    be replaced at the end of its financial lifetime.

    SWITCH-Pyomo treats all transmission and distribution (long-
    distance or local) the same. Any capacity that is built will be kept
    online indefinitely. At the end of its financial lifetime, existing
    capacity will be retired and rebuilt, so the annual cost of a line
    upgrade will remain constant in every future year.

    """

    mod.TRANSMISSION_LINES = Set()
    mod.trans_lz1 = Param(mod.TRANSMISSION_LINES, within=mod.LOAD_ZONES)
    mod.trans_lz2 = Param(mod.TRANSMISSION_LINES, within=mod.LOAD_ZONES)
    mod.min_data_check('TRANSMISSION_LINES', 'trans_lz1', 'trans_lz2')
    mod.trans_dbid = Param(mod.TRANSMISSION_LINES, default=lambda m, tx: tx)
    mod.trans_length_km = Param(mod.TRANSMISSION_LINES, within=PositiveReals)
    mod.trans_efficiency = Param(
        mod.TRANSMISSION_LINES,
        within=PositiveReals,
        validate=lambda m, val, tx: val <= 1)
    mod.EXISTING_TRANS_BLD_YRS = Set(
        dimen=2,
        initialize=lambda m: set(
            (tx, 'Legacy') for tx in m.TRANSMISSION_LINES))
    mod.existing_trans_cap = Param(
        mod.TRANSMISSION_LINES,
        within=NonNegativeReals)
    mod.min_data_check(
        'trans_length_km', 'trans_efficiency', 'EXISTING_TRANS_BLD_YRS',
        'existing_trans_cap')
    mod.trans_new_build_allowed = Param(
        mod.TRANSMISSION_LINES, within=Boolean, default=True)
    mod.NEW_TRANS_BLD_YRS = Set(
        dimen=2,
        initialize=lambda m: m.TRANSMISSION_LINES * m.PERIODS,
        filter=lambda m, tx, p: m.trans_new_build_allowed[tx])
    mod.TRANS_BUILD_YEARS = Set(
        dimen=2,
        initialize=lambda m: m.EXISTING_TRANS_BLD_YRS | m.NEW_TRANS_BLD_YRS)
    mod.PERIOD_RELEVANT_TRANS_BUILDS = Set(
        mod.PERIODS,
        within=mod.TRANS_BUILD_YEARS,
        initialize=lambda m, p: set(
            (tx, bld_yr) for (tx, bld_yr) in m.TRANS_BUILD_YEARS
            if bld_yr <= p))

    def bounds_BuildTrans(model, tx, bld_yr):
        if((tx, bld_yr) in model.EXISTING_TRANS_BLD_YRS):
            return (model.existing_trans_cap[tx],
                    model.existing_trans_cap[tx])
        else:
            return (0, None)
    mod.BuildTrans = Var(
        mod.TRANS_BUILD_YEARS,
        within=NonNegativeReals,
        bounds=bounds_BuildTrans)
    mod.TransCapacity = Expression(
        mod.TRANSMISSION_LINES, mod.PERIODS,
        rule=lambda m, tx, period: sum(
            m.BuildTrans[tx, bld_yr]
            for (tx2, bld_yr) in m.TRANS_BUILD_YEARS
            if tx2 == tx and (bld_yr == 'Legacy' or bld_yr <= period)))
    mod.trans_derating_factor = Param(
        mod.TRANSMISSION_LINES,
        within=NonNegativeReals,
        default=1,
        validate=lambda m, val, tx: val <= 1)
    mod.TransCapacityAvailable = Expression(
        mod.TRANSMISSION_LINES, mod.PERIODS,
        rule=lambda m, tx, period: (
            m.TransCapacity[tx, period] * m.trans_derating_factor[tx]))
    mod.trans_terrain_multiplier = Param(
        mod.TRANSMISSION_LINES,
        within=Reals,
        default=1,
        validate=lambda m, val, tx: val >= 0.5 and val <= 3)
    mod.trans_capital_cost_per_mw_km = Param(
        within=PositiveReals,
        default=1000)
    mod.trans_lifetime_yrs = Param(
        within=PositiveReals,
        default=20)
    mod.trans_fixed_o_m_fraction = Param(
        within=PositiveReals,
        default=0.03)
    # Total annual fixed costs for building new transmission lines...
    # Multiply capital costs by capital recover factor to get annual
    # payments. Add annual fixed O&M that are expressed as a fraction of
    # overnight costs.
    mod.trans_cost_annual = Param(
        mod.TRANSMISSION_LINES,
        within=PositiveReals,
        initialize=lambda m, tx: (
            m.trans_capital_cost_per_mw_km * m.trans_terrain_multiplier[tx] *
            m.trans_length_km[tx] * (crf(m.interest_rate, m.trans_lifetime_yrs) +
                m.trans_fixed_o_m_fraction)))
    # An expression to summarize annual costs for the objective
    # function. Units should be total annual future costs in $base_year
    # real dollars. The objective function will convert these to
    # base_year Net Present Value in $base_year real dollars.
    mod.Trans_Fixed_Costs_Annual = Expression(
        mod.PERIODS,
        rule=lambda m, p: sum(
            m.BuildTrans[tx, bld_yr] * m.trans_cost_annual[tx]
            for (tx, bld_yr) in m.PERIOD_RELEVANT_TRANS_BUILDS[p]))
    mod.cost_components_annual.append('Trans_Fixed_Costs_Annual')

    def init_TRANS_DIRECTIONAL(model):
        tx_dir = set()
        for tx in model.TRANSMISSION_LINES:
            tx_dir.add((model.trans_lz1[tx], model.trans_lz2[tx]))
            tx_dir.add((model.trans_lz2[tx], model.trans_lz1[tx]))
        return tx_dir
    mod.TRANS_DIRECTIONAL = Set(
        dimen=2,
        initialize=init_TRANS_DIRECTIONAL)
    mod.CONNECTED_LOAD_ZONES = Set(
        mod.LOAD_ZONES,
        initialize=lambda m, lz: set(
            z for z in m.LOAD_ZONES if (lz,z) in m.TRANS_DIRECTIONAL))

    def init_trans_d_line(m, lz_from, lz_to):
        for tx in m.TRANSMISSION_LINES:
            if((m.trans_lz1[tx] == lz_from and m.trans_lz2[tx] == lz_to) or
               (m.trans_lz2[tx] == lz_from and m.trans_lz1[tx] == lz_to)):
                return tx
    mod.trans_d_line = Param(
        mod.TRANS_DIRECTIONAL,
        within=mod.TRANSMISSION_LINES,
        initialize=init_trans_d_line)
コード例 #7
0
ファイル: storage.py プロジェクト: bmaluenda/switch
def define_components(mod):
    """
    
    STORAGE_TECH is a subset of GENERATION_TECHNOLOGIES that can
    store electricity for later discharge. Members of this set can
    be abbreviated as storage or s.

    g_storage_efficiency[STORAGE_TECH] describes the round trip
    efficiency of a storage technology. A storage technology that is 75
    percent efficient would have a storage_efficiency of .75. If 1 MWh
    was stored in such a storage project, 750 kWh would be available for
    extraction later. Internal leakage or energy dissipation of storage
    technologies is assumed to be neglible, which is consistent with
    short-duration storage technologies currently on the market which
    tend to consume stored power within 1 day. If a given storage
    technology has significant internal discharge when it stores power
    for extended time perios, then those behaviors will need to be
    modeled in more detail.

    g_store_to_release_ratio[STORAGE_TECH] describes the maximum rate
    that energy can be stored, expressed as a ratio of discharge power
    capacity. This is an optional parameter and will default to 1. If a
    storage project has 1 MW of dischage capacity and a max_store_rate
    of 1.2, then it can consume up to 1.2 MW of power while charging.

    g_storage_energy_overnight_cost[storage, period] is the overnight
    capital cost per MWh of energy capacity for building the given
    storage technology installed in the given investment period. This is
    only defined for storage technologies. Note that this describes the
    energy component and the overnight_cost describes the power
    component.
    
    The previous three parameters provide default values for projects
    that implement that technology. These parameters may be overridden
    on a project-specific basis via proj_storage_efficiency[STORAGE_PROJECTS],
    proj_store_to_release_ratio[STORAGE_PROJECTS], and
    proj_storage_energy_overnight_cost[(proj, bld_yr) in STORAGE_PROJECT_BUILDYEARS]

    STORAGE_PROJECTS is the set of projects that use storage technologies.

    STORAGE_PROJECT_BUILDYEARS is the subset of PROJECT_BUILDYEARS, restricted
    to storage projects.

    BuildStorageEnergyMWh[(proj, bld_yr) in STORAGE_PROJECT_BUILDYEARS]
    is a decision of how much energy capacity to build onto a storage
    project. This is analogous to BuildProj, but for energy rather than power.
    
    Total_Storage_Energy_Install_Costs_Annual[PERIODS] is an expression of the
    annual costs incurred by the BuildStorageEnergyMWh decision.
    
    StorageEnergyCapacity[proj, period] is an expression describing the
    cumulative available energy capacity of BuildStorageEnergyMWh. This is
    analogous to ProjCapacity.
    
    STORAGE_PROJ_DISPATCH_POINTS is the subset of PROJ_DISPATCH_POINTS,
    restricted to storage projects.

    ChargeStorage[(proj, t) in STORAGE_PROJ_DISPATCH_POINTS] is a dispatch
    decision of how much to charge a storage project in each timepoint.
    
    LZ_NetCharge[LOAD_ZONE, TIMEPOINT] is an expression describing the
    aggregate impact of ChargeStorage in each load zone and timepoint.
    
    Charge_Storage_Upper_Limit[(proj, t) in STORAGE_PROJ_DISPATCH_POINTS]
    constrains ChargeStorage to available power capacity (accounting for
    proj_store_to_release_ratio)
    
    StateOfChargeMWh[(proj, t) in STORAGE_PROJ_DISPATCH_POINTS] is a variable
    for tracking state of charge. This value stores the state of charge at
    the end of each timepoint for each storage project.
    
    Track_State_Of_Charge[(proj, t) in STORAGE_PROJ_DISPATCH_POINTS] constrains
    StateOfChargeMWh based on the StateOfChargeMWh in the previous timepoint,
    ChargeStorage and DispatchProj.
    
    State_Of_Charge_Upper_Limit[(proj, t) in STORAGE_PROJ_DISPATCH_POINTS]
    constrains StateOfChargeMWh based on installed energy capacity.

    """

    mod.STORAGE_PROJECTS = Set(within=mod.PROJECTS)
    mod.proj_storage_efficiency = Param(
        mod.STORAGE_PROJECTS,
        within=PercentFraction)
    mod.proj_store_to_release_ratio = Param(
        mod.STORAGE_PROJECTS,
        within=PositiveReals,
        default=1.0)

    mod.STORAGE_PROJECT_BUILDYEARS = Set(
        dimen=2,
        initialize=mod.PROJECT_BUILDYEARS,
        filter=lambda m, proj, bld_yr: proj in m.STORAGE_PROJECTS)
    mod.proj_storage_energy_overnight_cost = Param(
        mod.STORAGE_PROJECT_BUILDYEARS,
        within=NonNegativeReals)
    mod.min_data_check('proj_storage_energy_overnight_cost')
    mod.BuildStorageEnergyMWh = Var(
        mod.STORAGE_PROJECT_BUILDYEARS,
        within=NonNegativeReals)

    # Summarize capital costs of energy storage for the objective function.
    mod.Total_Storage_Energy_Install_Costs_Annual = Expression(
        mod.PERIODS,
        rule=lambda m, p: sum(m.BuildStorageEnergyMWh[proj, bld_yr] *
                   m.proj_storage_energy_overnight_cost[proj, bld_yr] *
                   crf(m.interest_rate, m.proj_max_age[proj])
                   for (proj, bld_yr) in m.STORAGE_PROJECT_BUILDYEARS))
    mod.cost_components_annual.append(
        'Total_Storage_Energy_Install_Costs_Annual')

    mod.StorageEnergyCapacity = Expression(
        mod.STORAGE_PROJECTS, mod.PERIODS,
        rule=lambda m, proj, period: sum(
            m.BuildStorageEnergyMWh[proj, bld_yr]
            for bld_yr in m.PROJECT_PERIOD_ONLINE_BUILD_YRS[proj, period]))

    mod.STORAGE_PROJ_DISPATCH_POINTS = Set(
        initialize=mod.PROJ_DISPATCH_POINTS,
        filter=lambda m, proj, t: proj in m.STORAGE_PROJECTS)

    mod.ChargeStorage = Var(
        mod.STORAGE_PROJ_DISPATCH_POINTS,
        within=NonNegativeReals)
    
    # Summarize storage charging for the energy balance equations
    def LZ_NetCharge_rule(m, lz, t):
        # Construct and cache a set for summation as needed
        if not hasattr(m, 'Storage_Charge_Summation_dict'):
            m.Storage_Charge_Summation_dict = {}
            for (lz2, t2) in m.LOAD_ZONES * m.TIMEPOINTS:
                m.Storage_Charge_Summation_dict[lz2, t2] = set()
                for proj in m.PROJECTS_ACTIVE_IN_TIMEPOINT[t]:
                    if (proj not in m.STORAGE_PROJECTS or
                        m.proj_load_zone[proj] != lz2):
                        continue
                    m.Storage_Charge_Summation_dict[lz2, t2].add(proj)
        # Use pop to free memory
        relevant_projects = m.Storage_Charge_Summation_dict.pop((lz, t))
        return sum(m.ChargeStorage[proj, t] for proj in relevant_projects)
    mod.LZ_NetCharge = Expression(
        mod.LOAD_ZONES, mod.TIMEPOINTS,
        rule=LZ_NetCharge_rule)
    # Register net dispatch as contributing to a load zone's energy
    mod.LZ_Energy_Components_Consume.append('LZ_NetCharge')

    def Charge_Storage_Upper_Limit_rule(m, proj, t):
        return m.ChargeStorage[proj,t] <= \
            m.DispatchUpperLimit[proj, t] * m.proj_store_to_release_ratio[proj]
    mod.Charge_Storage_Upper_Limit = Constraint(
        mod.STORAGE_PROJ_DISPATCH_POINTS,
        rule=Charge_Storage_Upper_Limit_rule)
                
    mod.StateOfChargeMWh = Var(
        mod.STORAGE_PROJ_DISPATCH_POINTS,
        within=NonNegativeReals)

    def Track_State_Of_Charge_rule(m, proj, t):
        return m.StateOfChargeMWh[proj, t] == \
            m.StateOfChargeMWh[proj, m.tp_previous[t]] + \
            (m.ChargeStorage[proj, t] * m.proj_storage_efficiency[proj] -
             m.DispatchProj[proj, t]) * m.tp_duration_hrs[t]
    mod.Track_State_Of_Charge = Constraint(
        mod.STORAGE_PROJ_DISPATCH_POINTS,
        rule=Track_State_Of_Charge_rule)

    def State_Of_Charge_Upper_Limit_rule(m, proj, t):
        return m.StateOfChargeMWh[proj, t] <= \
            m.StorageEnergyCapacity[proj, m.tp_period[t]]
    mod.State_Of_Charge_Upper_Limit = Constraint(
        mod.STORAGE_PROJ_DISPATCH_POINTS,
        rule=State_Of_Charge_Upper_Limit_rule)
コード例 #8
0
def define_components(m):
    
    m.pumped_hydro_capital_cost_per_mw = Param()
    m.pumped_hydro_project_life = Param()
    
    # annual O&M cost for pumped hydro project, percent of capital cost
    m.pumped_hydro_fixed_om_percent = Param()
    
    # total annual cost
    m.pumped_hydro_fixed_cost_per_mw_per_year = Param(initialize=lambda m:
        m.pumped_hydro_capital_cost_per_mw * 
            (crf(m.interest_rate, m.pumped_hydro_project_life) + m.pumped_hydro_fixed_om_percent)
    )
    
    # round-trip efficiency of the pumped hydro facility
    m.pumped_hydro_efficiency = Param()
    
    # average energy available from water inflow each day
    # (system must balance energy net of this each day)
    m.pumped_hydro_inflow_mw = Param()
    
    # maximum size of pumped hydro project
    m.pumped_hydro_max_capacity_mw = Param(default=1000)

    # How much pumped hydro to build
    m.BuildPumpedHydroMW = Var(m.LOAD_ZONES, m.PERIODS, within=NonNegativeReals)
    m.Pumped_Hydro_Capacity_MW = Expression(m.LOAD_ZONES, m.PERIODS, rule=lambda m, z, p:
        sum(m.BuildPumpedHydroMW[z, pp] for pp in m.CURRENT_AND_PRIOR_PERIODS[p])
    )

    # constraints on construction of pumped hydro
    m.BuildAnyPumpedHydro = Var(m.LOAD_ZONES, m.PERIODS, bounds=(0, 1)) # within=Binary)
    # force the build flag on for the year(s) when pumped hydro is built, 
    # and cap the build at the max allowed capacity
    m.Pumped_Hydro_Max_Build = Constraint(m.LOAD_ZONES, m.PERIODS, rule=lambda m, z, p:
        m.BuildPumpedHydroMW[z, p] <= m.BuildAnyPumpedHydro[z, p] * m.pumped_hydro_max_capacity_mw
    )
    # only build pumped hydro in one period (can't add incrementally)
    m.Pumped_Hydro_Only_Build_Once = Constraint(m.LOAD_ZONES, rule=lambda m, z:
        sum(m.BuildAnyPumpedHydro[z, p] for p in m.PERIODS) <= 1
    )

    # How to run pumped hydro
    m.GeneratePumpedHydro = Var(m.LOAD_ZONES, m.TIMEPOINTS, within=NonNegativeReals)
    m.StorePumpedHydro = Var(m.LOAD_ZONES, m.TIMEPOINTS, within=NonNegativeReals)
    
    # calculate costs
    m.Pumped_Hydro_Fixed_Cost_Annual = Expression(m.PERIODS, rule=lambda m, p:
        sum(m.pumped_hydro_fixed_cost_per_mw_per_year * m.Pumped_Hydro_Capacity_MW[z, p] for z in m.LOAD_ZONES)
    )
    m.cost_components_annual.append('Pumped_Hydro_Fixed_Cost_Annual')
    
    # add the pumped hydro to the model's energy balance
    m.LZ_Energy_Components_Produce.append('GeneratePumpedHydro')
    m.LZ_Energy_Components_Consume.append('StorePumpedHydro')
    
    # limits on pumping and generation
    m.Pumped_Hydro_Max_Generate_Rate = Constraint(m.LOAD_ZONES, m.TIMEPOINTS, rule=lambda m, z, t:
        m.GeneratePumpedHydro[z, t]
        <=
        m.Pumped_Hydro_Capacity_MW[z, m.tp_period[t]]
    )
    m.Pumped_Hydro_Max_Store_Rate = Constraint(m.LOAD_ZONES, m.TIMEPOINTS, rule=lambda m, z, t:
        m.StorePumpedHydro[z, t]
        <=
        m.Pumped_Hydro_Capacity_MW[z, m.tp_period[t]]
    )

    # return reservoir to the starting level every day, net of any inflow
    # it can also go higher than starting level, which indicates spilling surplus water
    m.Pumped_Hydro_Daily_Balance = Constraint(m.LOAD_ZONES, m.TIMESERIES, rule=lambda m, z, ts:
        sum(
            m.StorePumpedHydro[z, tp] * m.pumped_hydro_efficiency
            + m.pumped_hydro_inflow_mw
            - m.GeneratePumpedHydro[z, tp]
            for tp in m.TS_TPS[ts]
         ) >= 0
    )
コード例 #9
0
ファイル: pumped_hydro.py プロジェクト: ranjitster/switch
def define_components(m):

    m.PH_PROJECTS = Set()

    m.ph_load_zone = Param(m.PH_PROJECTS)

    m.ph_capital_cost_per_mw = Param(m.PH_PROJECTS, within=NonNegativeReals)
    m.ph_project_life = Param(m.PH_PROJECTS, within=NonNegativeReals)

    # annual O&M cost for pumped hydro project, percent of capital cost
    m.ph_fixed_om_percent = Param(m.PH_PROJECTS, within=NonNegativeReals)

    # total annual cost
    m.ph_fixed_cost_per_mw_per_year = Param(
        m.PH_PROJECTS,
        initialize=lambda m, p: m.ph_capital_cost_per_mw[p] * (crf(
            m.interest_rate, m.ph_project_life[p]) + m.ph_fixed_om_percent[p]))

    # round-trip efficiency of the pumped hydro facility
    m.ph_efficiency = Param(m.PH_PROJECTS)

    # average energy available from water inflow each day
    # (system must balance energy net of this each day)
    m.ph_inflow_mw = Param(m.PH_PROJECTS)

    # maximum size of pumped hydro project
    m.ph_max_capacity_mw = Param(m.PH_PROJECTS)

    # How much pumped hydro to build
    m.BuildPumpedHydroMW = Var(m.PH_PROJECTS,
                               m.PERIODS,
                               within=NonNegativeReals)
    m.Pumped_Hydro_Proj_Capacity_MW = Expression(
        m.PH_PROJECTS,
        m.PERIODS,
        rule=lambda m, pr, pe: sum(m.BuildPumpedHydroMW[pr, pp]
                                   for pp in m.CURRENT_AND_PRIOR_PERIODS[pe]))

    # flag indicating whether any capacity is added to each project each year
    m.BuildAnyPumpedHydro = Var(m.PH_PROJECTS, m.PERIODS, within=Binary)

    # How to run pumped hydro
    m.PumpedHydroProjGenerateMW = Var(m.PH_PROJECTS,
                                      m.TIMEPOINTS,
                                      within=NonNegativeReals)
    m.PumpedHydroProjStoreMW = Var(m.PH_PROJECTS,
                                   m.TIMEPOINTS,
                                   within=NonNegativeReals)

    # constraints on construction of pumped hydro

    # don't build more than the max allowed capacity
    m.Pumped_Hydro_Max_Build = Constraint(
        m.PH_PROJECTS,
        m.PERIODS,
        rule=lambda m, pr, pe: m.Pumped_Hydro_Proj_Capacity_MW[
            pr, pe] <= m.ph_max_capacity_mw[pr])

    # force the build flag on for the year(s) when pumped hydro is built
    m.Pumped_Hydro_Set_Build_Flag = Constraint(
        m.PH_PROJECTS,
        m.PERIODS,
        rule=lambda m, pr, pe: m.BuildPumpedHydroMW[pr, pe] <= m.
        BuildAnyPumpedHydro[pr, pe] * m.ph_max_capacity_mw[pr])
    # only build in one year (can be deactivated to allow incremental construction)
    m.Pumped_Hydro_Build_Once = Constraint(
        m.PH_PROJECTS,
        rule=lambda m, pr: sum(m.BuildAnyPumpedHydro[pr, pe]
                               for pe in m.PERIODS) <= 1)
    # only build full project size (deactivated by default, to allow smaller projects)
    m.Pumped_Hydro_Build_All_Or_None = Constraint(
        m.PH_PROJECTS,
        m.PERIODS,
        rule=lambda m, pr, pe: m.BuildPumpedHydroMW[pr, pe] == m.
        BuildAnyPumpedHydro[pr, pe] * m.ph_max_capacity_mw[pr])
    m.Deactivate_Pumped_Hydro_Build_All_Or_None = BuildAction(
        rule=lambda m: m.Pumped_Hydro_Build_All_Or_None.deactivate())

    # limits on pumping and generation
    m.Pumped_Hydro_Max_Generate_Rate = Constraint(
        m.PH_PROJECTS,
        m.TIMEPOINTS,
        rule=lambda m, pr, t: m.PumpedHydroProjGenerateMW[
            pr, t] <= m.Pumped_Hydro_Proj_Capacity_MW[pr, m.tp_period[t]])
    m.Pumped_Hydro_Max_Store_Rate = Constraint(
        m.PH_PROJECTS,
        m.TIMEPOINTS,
        rule=lambda m, pr, t: m.PumpedHydroProjStoreMW[
            pr, t] <= m.Pumped_Hydro_Proj_Capacity_MW[pr, m.tp_period[t]])

    # return reservoir to at least the starting level every day, net of any inflow
    # it can also go higher than starting level, which indicates spilling surplus water
    m.Pumped_Hydro_Daily_Balance = Constraint(
        m.PH_PROJECTS,
        m.TIMESERIES,
        rule=lambda m, pr, ts: sum(m.PumpedHydroProjStoreMW[
            pr, tp] * m.ph_efficiency[pr] + m.ph_inflow_mw[pr] - m.
                                   PumpedHydroProjGenerateMW[pr, tp]
                                   for tp in m.TS_TPS[ts]) >= 0)

    m.GeneratePumpedHydro = Expression(
        m.LOAD_ZONES,
        m.TIMEPOINTS,
        rule=lambda m, z, t: sum(m.PumpedHydroProjGenerateMW[pr, t]
                                 for pr in m.PH_PROJECTS
                                 if m.ph_load_zone[pr] == z))
    m.StorePumpedHydro = Expression(
        m.LOAD_ZONES,
        m.TIMEPOINTS,
        rule=lambda m, z, t: sum(m.PumpedHydroProjStoreMW[pr, t]
                                 for pr in m.PH_PROJECTS
                                 if m.ph_load_zone[pr] == z))

    # calculate costs
    m.Pumped_Hydro_Fixed_Cost_Annual = Expression(
        m.PERIODS,
        rule=lambda m, pe: sum(m.ph_fixed_cost_per_mw_per_year[pr] * m.
                               Pumped_Hydro_Proj_Capacity_MW[pr, pe]
                               for pr in m.PH_PROJECTS))
    m.cost_components_annual.append('Pumped_Hydro_Fixed_Cost_Annual')

    # add the pumped hydro to the model's energy balance
    m.LZ_Energy_Components_Produce.append('GeneratePumpedHydro')
    m.LZ_Energy_Components_Consume.append('StorePumpedHydro')

    # total pumped hydro capacity in each zone each period (for reporting)
    m.Pumped_Hydro_Capacity_MW = Expression(
        m.LOAD_ZONES,
        m.PERIODS,
        rule=lambda m, z, pe: sum(m.Pumped_Hydro_Proj_Capacity_MW[pr, pe]
                                  for pr in m.PH_PROJECTS
                                  if m.ph_load_zone[pr] == z))

    # force construction of a fixed amount of pumped hydro
    if m.options.ph_mw is not None:
        print "Forcing construction of {m} MW of pumped hydro.".format(
            m=m.options.ph_mw)
        m.Build_Pumped_Hydro_MW = Constraint(
            m.LOAD_ZONES,
            rule=lambda m, z: m.Pumped_Hydro_Capacity_MW[
                z, m.PERIODS.last()] == m.options.ph_mw)
    # force construction of pumped hydro only in a certain period
    if m.options.ph_year is not None:
        print "Allowing construction of pumped hydro only in {p}.".format(
            p=m.options.ph_year)
        m.Build_Pumped_Hydro_Year = Constraint(
            m.PH_PROJECTS,
            m.PERIODS,
            rule=lambda m, pr, pe: m.BuildPumpedHydroMW[pr, pe] == 0.0
            if pe != m.options.ph_year else Constraint.Skip)
コード例 #10
0
def define_components(mod):
    """

    Adds components to a Pyomo abstract model object to describe
    generation and storage projects. Unless otherwise stated, all power
    capacity is specified in units of MW and all sets and parameters
    are mandatory.

    GENERATION_PROJECTS is the set of generation and storage projects that
    have been built or could potentially be built. A project is a combination
    of generation technology, load zone and location. A particular build-out
    of a project should also include the year in which construction was
    complete and additional capacity came online. Members of this set are
    abbreviated as gen in parameter names and g in indexes. Use of p instead
    of g is discouraged because p is reserved for period.

    gen_dbid[g] is an external database id for each generation project. This is
    an optional parameter than defaults to the project index.

    gen_tech[g] describes what kind of technology a generation project is
    using.

    gen_load_zone[g] is the load zone this generation project is built in.

    VARIABLE_GENS is a subset of GENERATION_PROJECTS that only includes
    variable generators such as wind or solar that have exogenous
    constraints on their energy production.

    BASELOAD_GENS is a subset of GENERATION_PROJECTS that only includes
    baseload generators such as coal or geothermal.

    GENS_IN_ZONE[z in LOAD_ZONES] is an indexed set that lists all
    generation projects within each load zone.

    CAPACITY_LIMITED_GENS is the subset of GENERATION_PROJECTS that are
    capacity limited. Most of these will be generator types that are resource
    limited like wind, solar or geothermal, but this can be specified for any
    generation project. Some existing or proposed generation projects may have
    upper bounds on increasing capacity or replacing capacity as it is retired
    based on permits or local air quality regulations.

    gen_capacity_limit_mw[g] is defined for generation technologies that are
    resource limited and do not compete for land area. This describes the
    maximum possible capacity of a generation project in units of megawatts.

    -- CONSTRUCTION --

    GEN_BLD_YRS is a two-dimensional set of generation projects and the
    years in which construction or expansion occured or can occur. You
    can think of a project as a physical site that can be built out over
    time. BuildYear is the year in which construction is completed and
    new capacity comes online, not the year when constrution begins.
    BuildYear will be in the past for existing projects and will be the
    first year of an investment period for new projects. Investment
    decisions are made for each project/invest period combination. This
    set is derived from other parameters for all new construction. This
    set also includes entries for existing projects that have already
    been built and planned projects whose capacity buildouts have already been
    decided; information for legacy projects come from other files
    and their build years will usually not correspond to the set of
    investment periods. There are two recommended options for
    abbreviating this set for denoting indexes: typically this should be
    written out as (g, build_year) for clarity, but when brevity is
    more important (g, b) is acceptable.

    NEW_GEN_BLD_YRS is a subset of GEN_BLD_YRS that only
    includes projects that have not yet been constructed. This is
    derived by joining the set of GENERATION_PROJECTS with the set of
    NEW_GENERATION_BUILDYEARS using generation technology.

    PREDETERMINED_GEN_BLD_YRS is a subset of GEN_BLD_YRS that
    only includes existing or planned projects that are not subject to
    optimization.

    gen_predetermined_cap[(g, build_year) in PREDETERMINED_GEN_BLD_YRS] is
    a parameter that describes how much capacity was built in the past
    for existing projects, or is planned to be built for future projects.

    BuildGen[g, build_year] is a decision variable that describes
    how much capacity of a project to install in a given period. This also
    stores the amount of capacity that was installed in existing projects
    that are still online.

    GenCapacity[g, period] is an expression that returns the total
    capacity online in a given period. This is the sum of installed capacity
    minus all retirements.

    Max_Build_Potential[g] is a constraint defined for each project
    that enforces maximum capacity limits for resource-limited projects.

        GenCapacity <= gen_capacity_limit_mw

    NEW_GEN_WITH_MIN_BUILD_YEARS is the subset of NEW_GEN_BLD_YRS for
    which minimum capacity build-out constraints will be enforced.

    BuildMinGenCap[g, build_year] is a binary variable that indicates
    whether a project will build capacity in a period or not. If the model is
    committing to building capacity, then the minimum must be enforced.

    Enforce_Min_Build_Lower[g, build_year]  and
    Enforce_Min_Build_Upper[g, build_year] are a pair of constraints that
    force project build-outs to meet the minimum build requirements for
    generation technologies that have those requirements. They force BuildGen
    to be 0 when BuildMinGenCap is 0, and to be greater than
    g_min_build_capacity when BuildMinGenCap is 1. In the latter case,
    the upper constraint should be non-binding; the upper limit is set to 10
    times the peak non-conincident demand of the entire system.

    --- OPERATIONS ---

    PERIODS_FOR_GEN_BLD_YR[g, build_year] is an indexed
    set that describes which periods a given project build will be
    operational.

    BLD_YRS_FOR_GEN_PERIOD[g, period] is a complementary
    indexed set that identify which build years will still be online
    for the given project in the given period. For some project-period
    combinations, this will be an empty set.

    GEN_PERIODS describes periods in which generation projects
    could be operational. Unlike the related sets above, it is not
    indexed. Instead it is specified as a set of (g, period)
    combinations useful for indexing other model components.


    --- COSTS ---

    gen_connect_cost_per_mw[g] is the cost of grid upgrades to support a
    new project, in dollars per peak MW. These costs include new
    transmission lines to a substation, substation upgrades and any
    other grid upgrades that are needed to deliver power from the
    interconnect point to the load center or from the load center to the
    broader transmission network.

    The following cost components are defined for each project and build
    year. These parameters will always be available, but will typically
    be populated by the generic costs specified in generator costs
    inputs file and the load zone cost adjustment multipliers from
    load_zones inputs file.

    gen_overnight_cost[g, build_year] is the overnight capital cost per
    MW of capacity for building a project in the given period. By
    "installed in the given period", I mean that it comes online at the
    beginning of the given period and construction starts before that.

    gen_fixed_om[g, build_year] is the annual fixed Operations and
    Maintenance costs (O&M) per MW of capacity for given project that
    was installed in the given period.

    -- Derived cost parameters --

    gen_capital_cost_annual[g, build_year] is the annualized loan
    payments for a project's capital and connection costs in units of
    $/MW per year. This is specified in non-discounted real dollars in a
    future period, not real dollars in net present value.

    Proj_Fixed_Costs_Annual[g, period] is the total annual fixed
    costs (capital as well as fixed operations & maintenance) incurred
    by a project in a period. This reflects all of the builds are
    operational in the given period. This is an expression that reflect
    decision variables.

    ProjFixedCosts[period] is the sum of
    Proj_Fixed_Costs_Annual[g, period] for all projects that could be
    online in the target period. This aggregation is performed for the
    benefit of the objective function.

    TODO:
    - Allow early capacity retirements with savings on fixed O&M
    
    """
    mod.GENERATION_PROJECTS = Set()

    ######## NEW ADDITION ##########
    mod.NON_PRODUCING_GENERATION_PROJECTS = Set()
    mod.PRODUCING_GENERATION_PROJECTS = Set(
        dimen=1,
        initialize=lambda m: m.GENERATION_PROJECTS - m.
        NON_PRODUCING_GENERATION_PROJECTS)
    ##############################

    mod.gen_dbid = Param(mod.GENERATION_PROJECTS,
                         default=lambda m, g: g)  #NOT USED
    mod.gen_tech = Param(mod.GENERATION_PROJECTS)
    mod.GENERATION_TECHNOLOGIES = Set(
        initialize=lambda m: {m.gen_tech[g]
                              for g in m.GENERATION_PROJECTS})  #NOT USED
    mod.gen_energy_source = Param(
        mod.GENERATION_PROJECTS,
        validate=lambda m, val, g: val in m.ENERGY_SOURCES or val == "multiple"
    )  #NOT USED
    mod.gen_load_zone = Param(mod.GENERATION_PROJECTS, within=mod.LOAD_ZONES)
    mod.gen_max_age = Param(mod.GENERATION_PROJECTS, within=PositiveIntegers)
    mod.gen_is_variable = Param(
        mod.GENERATION_PROJECTS,
        within=Boolean)  # NOT USED except scenario_data
    mod.gen_is_baseload = Param(
        mod.GENERATION_PROJECTS,
        within=Boolean)  # used in gen_min_load_fraction, init_gen_availability
    mod.gen_is_cogen = Param(
        mod.GENERATION_PROJECTS, within=Boolean, default=False
    )  # prevent non-cogen plants from burning pure LSFO after 2017 due to MATS emission restrictions
    mod.gen_is_distributed = Param(mod.GENERATION_PROJECTS,
                                   within=Boolean,
                                   default=False)  #in dispatch.py
    mod.gen_scheduled_outage_rate = Param(mod.GENERATION_PROJECTS,
                                          within=PercentFraction,
                                          default=0)
    mod.gen_forced_outage_rate = Param(mod.GENERATION_PROJECTS,
                                       within=PercentFraction,
                                       default=0)
    mod.min_data_check('GENERATION_PROJECTS', 'gen_tech', 'gen_energy_source',
                       'gen_load_zone', 'gen_max_age', 'gen_is_variable',
                       'gen_is_baseload')

    mod.GENS_IN_ZONE = Set(
        mod.LOAD_ZONES,
        initialize=lambda m, z: set(g for g in m.GENERATION_PROJECTS
                                    if m.gen_load_zone[g] == z))
    mod.VARIABLE_GENS = Set(initialize=mod.GENERATION_PROJECTS,
                            filter=lambda m, g: m.gen_is_variable[g])
    mod.BASELOAD_GENS = Set(
        initialize=mod.GENERATION_PROJECTS,
        filter=lambda m, g: m.gen_is_baseload[
            g])  #VARIABLE_GENS and BASELOAD_GENS don't cover the whole space

    mod.CAPACITY_LIMITED_GENS = Set(within=mod.GENERATION_PROJECTS)  #NOT USED
    mod.gen_capacity_limit_mw = Param(mod.CAPACITY_LIMITED_GENS,
                                      within=PositiveReals)  #NOT USED
    mod.DISCRETELY_SIZED_GENS = Set(
        within=mod.GENERATION_PROJECTS)  #### NOT USED
    mod.gen_unit_size = Param(mod.DISCRETELY_SIZED_GENS, within=PositiveReals)
    mod.CCS_EQUIPPED_GENS = Set(
        within=mod.GENERATION_PROJECTS
    )  #carbon capture and storage would eliminate the emission >> see dispatch.py
    mod.gen_ccs_capture_efficiency = Param(mod.CCS_EQUIPPED_GENS,
                                           within=PercentFraction)
    mod.gen_ccs_energy_load = Param(mod.CCS_EQUIPPED_GENS,
                                    within=PercentFraction)

    mod.gen_uses_fuel = Param(mod.GENERATION_PROJECTS,
                              initialize=lambda m, g:
                              (m.gen_energy_source[g] in m.FUELS or m.
                               gen_energy_source[g] == "multiple"))

    ############ NEW ADDITION ########################################################################
    # mod.NON_FUEL_BASED_GENS = Set(
    #     initialize=mod.GENERATION_PROJECTS,
    #     filter=lambda m, g: not m.gen_uses_fuel[g] not m.gen_downstream[g])
    mod.NON_FUEL_BASED_GENS = Set(initialize=mod.GENERATION_PROJECTS,
                                  filter=lambda m, g: not m.gen_uses_fuel[g])
    ##################################################################################################

    mod.FUEL_BASED_GENS = Set(initialize=mod.GENERATION_PROJECTS,
                              filter=lambda m, g: m.gen_uses_fuel[g])
    mod.gen_full_load_heat_rate = Param(mod.FUEL_BASED_GENS,
                                        within=PositiveReals)
    mod.MULTIFUEL_GENS = Set(
        initialize=mod.GENERATION_PROJECTS,
        filter=lambda m, g: m.gen_energy_source[g] == "multiple")
    mod.FUELS_FOR_MULTIFUEL_GEN = Set(mod.MULTIFUEL_GENS, within=mod.FUELS)
    mod.FUELS_FOR_GEN = Set(
        mod.FUEL_BASED_GENS,
        initialize=lambda m, g:
        (m.FUELS_FOR_MULTIFUEL_GEN[g]
         if g in m.MULTIFUEL_GENS else [m.gen_energy_source[g]]))

    mod.PREDETERMINED_GEN_BLD_YRS = Set(dimen=2)
    mod.PREDETERMINED_GEN_BLD_YRS2 = Set(dimen=2)
    mod.GEN_BLD_YRS = Set(dimen=2,
                          validate=lambda m, g, bld_yr:
                          ((g, bld_yr) in m.PREDETERMINED_GEN_BLD_YRS or
                           (g, bld_yr) in m.GENERATION_PROJECTS * m.PERIODS))
    mod.NEW_GEN_BLD_YRS = Set(
        dimen=2,
        initialize=lambda m: m.GEN_BLD_YRS - m.PREDETERMINED_GEN_BLD_YRS)
    mod.GEN_BLD_YRS2 = Set(dimen=2,
                           validate=lambda m, g, bld_yr:
                           ((g, bld_yr) in m.PREDETERMINED_GEN_BLD_YRS2 or
                            (g, bld_yr) in m.GENERATION_PROJECTS * m.PERIODS))
    mod.NEW_GEN_BLD_YRS2 = Set(
        dimen=2,
        initialize=lambda m: m.GEN_BLD_YRS2 - m.PREDETERMINED_GEN_BLD_YRS2)
    mod.gen_predetermined_cap = Param(mod.PREDETERMINED_GEN_BLD_YRS,
                                      within=NonNegativeReals)
    mod.gen_predetermined_cap2 = Param(mod.PREDETERMINED_GEN_BLD_YRS2,
                                       within=NonNegativeReals)
    mod.min_data_check('gen_predetermined_cap')
    mod.min_data_check('gen_predetermined_cap2')

    def _gen_build_can_operate_in_period(m, g, build_year, period):
        if build_year in m.PERIODS:
            online = m.period_start[build_year]
        else:
            online = build_year
        retirement = online + m.gen_max_age[g]
        return (online <= m.period_start[period] < retirement)
        # This is probably more correct, but is a different behavior
        # mid_period = m.period_start[period] + 0.5 * m.period_length_years[period]
        # return online <= m.period_start[period] and mid_period <= retirement

    # The set of periods when a project built in a certain year will be online
    mod.PERIODS_FOR_GEN_BLD_YR = Set(
        mod.GEN_BLD_YRS,
        within=mod.PERIODS,
        ordered=True,
        initialize=lambda m, g, bld_yr: set(
            period for period in m.PERIODS
            if _gen_build_can_operate_in_period(m, g, bld_yr, period)))
    # The set of build years that could be online in the given period
    # for the given project.
    mod.BLD_YRS_FOR_GEN_PERIOD = Set(
        mod.GENERATION_PROJECTS,
        mod.PERIODS,
        initialize=lambda m, g, period: set(
            bld_yr for (gen, bld_yr) in m.GEN_BLD_YRS
            if gen == g and _gen_build_can_operate_in_period(
                m, g, bld_yr, period)))

    def bounds_BuildGen(model, g, bld_yr):
        if ((g, bld_yr) in model.PREDETERMINED_GEN_BLD_YRS):
            return (model.gen_predetermined_cap[g, bld_yr],
                    model.gen_predetermined_cap[g, bld_yr])
        elif (g in model.CAPACITY_LIMITED_GENS):
            # This does not replace Max_Build_Potential because
            # Max_Build_Potential applies across all build years.
            return (0, model.gen_capacity_limit_mw[g])
        else:
            return (0, None)

    mod.BuildGen = Var(mod.GEN_BLD_YRS,
                       within=NonNegativeReals,
                       bounds=bounds_BuildGen)

    # Some projects are retired before the first study period, so they
    # don't appear in the objective function or any constraints.
    # In this case, pyomo may leave the variable value undefined even
    # after a solve, instead of assigning a value within the allowed
    # range. This causes errors in the Progressive Hedging code, which
    # expects every variable to have a value after the solve. So as a
    # starting point we assign an appropriate value to all the existing
    # projects here.
    def BuildGen_assign_default_value(m, g, bld_yr):
        m.BuildGen[g, bld_yr] = m.gen_predetermined_cap[g, bld_yr]

    mod.BuildGen_assign_default_value = BuildAction(
        mod.PREDETERMINED_GEN_BLD_YRS, rule=BuildGen_assign_default_value)

    def bounds_BuildGen2(model, g, bld_yr):
        if ((g, bld_yr) in model.PREDETERMINED_GEN_BLD_YRS2):
            return (model.gen_predetermined_cap2[g, bld_yr],
                    model.gen_predetermined_cap2[g, bld_yr])
        elif (g in model.CAPACITY_LIMITED_GENS):
            # This does not replace Max_Build_Potential because
            # Max_Build_Potential applies across all build years.
            return (0, model.gen_capacity_limit_mw[g])
        else:
            return (0, None)

    mod.BuildGen2 = Var(mod.GEN_BLD_YRS,
                        within=NonNegativeReals,
                        bounds=bounds_BuildGen2)

    def BuildGen_assign_default_value2(m, g, bld_yr):
        m.BuildGen2[g, bld_yr] = m.gen_predetermined_cap2[g, bld_yr]

    mod.BuildGen_assign_default_value2 = BuildAction(
        mod.PREDETERMINED_GEN_BLD_YRS2, rule=BuildGen_assign_default_value2)

    mod.GEN_PERIODS = Set(dimen=2,
                          initialize=mod.GENERATION_PROJECTS * mod.PERIODS)

    mod.GenCapacity = Expression(
        mod.GEN_PERIODS,
        rule=lambda m, g, period: sum(m.BuildGen[g, bld_yr] for bld_yr in m.
                                      BLD_YRS_FOR_GEN_PERIOD[g, period]))

    mod.Max_Build_Potential = Constraint(
        mod.CAPACITY_LIMITED_GENS,
        mod.PERIODS,
        rule=lambda m, g, p:
        (m.gen_capacity_limit_mw[g] >= m.GenCapacity[g, p]))

    # The following components enforce minimum capacity build-outs.
    # Note that this adds binary variables to the model.
    mod.gen_min_build_capacity = Param(mod.GENERATION_PROJECTS,
                                       within=NonNegativeReals,
                                       default=0)
    mod.NEW_GEN_WITH_MIN_BUILD_YEARS = Set(initialize=mod.NEW_GEN_BLD_YRS,
                                           filter=lambda m, g, p:
                                           (m.gen_min_build_capacity[g] > 0))
    mod.BuildMinGenCap = Var(mod.NEW_GEN_WITH_MIN_BUILD_YEARS, within=Binary)
    mod.Enforce_Min_Build_Lower = Constraint(
        mod.NEW_GEN_WITH_MIN_BUILD_YEARS,
        rule=lambda m, g, p: (m.BuildMinGenCap[g, p] * m.
                              gen_min_build_capacity[g] <= m.BuildGen[g, p]))

    # Define a constant for enforcing binary constraints on project capacity
    # The value of 100 GW should be larger than any expected build size. For
    # perspective, the world's largest electric power plant (Three Gorges Dam)
    # is 22.5 GW. I tried using 1 TW, but CBC had numerical stability problems
    # with that value and chose a suboptimal solution for the
    # discrete_and_min_build example which is installing capacity of 3-5 MW.
    mod._gen_max_cap_for_binary_constraints = 10**5
    mod.Enforce_Min_Build_Upper = Constraint(
        mod.NEW_GEN_WITH_MIN_BUILD_YEARS,
        rule=lambda m, g, p: (m.BuildGen[g, p] <= m.BuildMinGenCap[g, p] * mod.
                              _gen_max_cap_for_binary_constraints))

    # Costs
    mod.gen_variable_om = Param(mod.GENERATION_PROJECTS,
                                within=NonNegativeReals)
    mod.gen_connect_cost_per_mw = Param(mod.GENERATION_PROJECTS,
                                        within=NonNegativeReals)
    mod.min_data_check('gen_variable_om', 'gen_connect_cost_per_mw')

    mod.gen_overnight_cost = Param(mod.GEN_BLD_YRS, within=NonNegativeReals)
    mod.gen_fixed_om = Param(mod.GEN_BLD_YRS, within=NonNegativeReals)
    mod.min_data_check('gen_overnight_cost', 'gen_fixed_om')

    # Derived annual costs
    mod.gen_capital_cost_annual = Param(
        mod.GEN_BLD_YRS,
        initialize=lambda m, g, bld_yr: (
            (m.gen_overnight_cost[g, bld_yr] + m.gen_connect_cost_per_mw[g]
             ) * crf(m.interest_rate, m.gen_max_age[g])))

    mod.GenCapitalCosts = Expression(
        mod.GEN_PERIODS,
        rule=lambda m, g, p: sum(m.BuildGen[g, bld_yr] * m.
                                 gen_capital_cost_annual[g, bld_yr]
                                 for bld_yr in m.BLD_YRS_FOR_GEN_PERIOD[g, p]))
    mod.GenFixedOMCosts = Expression(
        mod.GEN_PERIODS,
        rule=lambda m, g, p: sum(m.BuildGen[g, bld_yr] * m.gen_fixed_om[
            g, bld_yr] for bld_yr in m.BLD_YRS_FOR_GEN_PERIOD[g, p]))
    # Summarize costs for the objective function. Units should be total
    # annual future costs in $base_year real dollars. The objective
    # function will convert these to base_year Net Present Value in
    # $base_year real dollars.

    ####### NEW ADDITION ########################################################################################################################
    mod.gen_hawaii_share = Param(mod.GENERATION_PROJECTS,
                                 within=NonNegativeReals)
    # mod.TotalGenFixedCosts = Expression(
    #     mod.PERIODS,
    #     rule=lambda m, p:  sum( (m.GenCapitalCosts[g, p] + m.GenFixedOMCosts[g, p]) * m.gen_hawaii_share[g]
    #     for g in m.GENERATION_PROJECTS )
    #     )
    # mod.Cost_Components_Per_Period.append('TotalGenFixedCosts')

    mod.TotalGenFixedCosts = Expression(
        mod.PERIODS,
        rule=lambda m, p: sum(m.GenCapitalCosts[g, p] + m.GenFixedOMCosts[g, p]
                              for g in m.GENERATION_PROJECTS))
    mod.Cost_Components_Per_Period.append('TotalGenFixedCosts')

    mod.GENERATION_PROJECTS_EXTRACTION = Set(
        dimen=1,
        initialize=[
            "Wells_Shale_Oil", "Wells_Offshore_Oil", "Wells_Shale_Gas",
            "Wells_Offshore_Gas", "Mines_Coal_Surface"
        ],
        filter=lambda m, r: r in mod.GENERATION_PROJECTS)
    mod.GENERATION_PROJECTS_EXTRACTION_PERIOD = Set(
        dimen=2, initialize=mod.GENERATION_PROJECTS_EXTRACTION * mod.PERIODS)
    # mod.GENERATION_PROJECTS_EXTRACTION_FUEL = Set(dimen=2, within=mod.GENERATION_PROJECTS)
    mod.GENERATION_PROJECTS_EXTRACTION_ALL = Set(
        dimen=1,
        initialize=[
            "Wells_Shale_Oil", "Wells_Conventional_Oil"
            "Wells_Offshore_Oil", "Wells_Shale_Gas", "Wells_Offshore_Gas",
            "Wells_Conventional_Gas", "Mines_Coal_Surface",
            "Mines_Coal_Underground"
        ],
        filter=lambda m, r: r in m.GENERATION_PROJECTS)

    # mod.EXTRACTION_SET_FOR_GEN_RATIO = Set(mod.GENERATION_PROJECTS_EXTRACTION, within=mod.GENERATION_PROJECTS_EXTRACTION_ALL)
    # mod.EXTRACTION_SET_FOR_GEN = Set(mod.GENERATION_PROJECTS_EXTRACTION,
    #     initialize=lambda m, g: (
    #         m.EXTRACTION_SET_FOR_GEN_RATIO[g]
    #         if g in m.GENERATION_PROJECTS_EXTRACTION
    #         else [m.gen_energy_source[g]]))
    # mod.MULTIFUEL_GENS = Set(
    #     initialize=mod.GENERATION_PROJECTS,
    #     filter=lambda m, g: m.gen_energy_source[g] == "multiple")
    # mod.FUELS_FOR_MULTIFUEL_GEN = Set(mod.MULTIFUEL_GENS, within=mod.FUELS)
    # mod.FUELS_FOR_GEN = Set(mod.FUEL_BASED_GENS,
    #     initialize=lambda m, g: (
    #         m.FUELS_FOR_MULTIFUEL_GEN[g]
    #         if g in m.MULTIFUEL_GENS
    #         else [m.gen_energy_source[g]]))
    mod.Limited_Fuels = Set(dimen=1,
                            initialize=["Oil", "LNG", "Coal"],
                            filter=lambda m, r: r in m.FUELS)
    mod.FUELS_PERIOD = Set(dimen=2, initialize=mod.Limited_Fuels * mod.PERIODS)
コード例 #11
0
def define_components(m):
    
    # TODO: change this to allow multiple storage technologies.

    # battery capital cost
    # TODO: accept a single battery_capital_cost_per_mwh_capacity value or the annual values shown here
    m.BATTERY_CAPITAL_COST_YEARS = Set() # list of all years for which capital costs are available
    m.battery_capital_cost_per_mwh_capacity_by_year = Param(m.BATTERY_CAPITAL_COST_YEARS)
    
    # TODO: merge this code with batteries.py and auto-select between fixed calendar life and cycle life
    # based on whether battery_n_years or battery_n_cycles is provided. (Or find some hybrid that can
    # handle both well?)
    # number of years the battery can last; we assume there is no limit on cycle life within this period
    m.battery_n_years = Param()
    # maximum depth of discharge
    m.battery_max_discharge = Param()
    # round-trip efficiency
    m.battery_efficiency = Param()
    # fastest time that storage can be emptied (down to max_discharge)
    m.battery_min_discharge_time = Param()

    # amount of battery capacity to build and use (in MWh)
    # TODO: integrate this with other project data, so it can contribute to reserves, etc.
    m.BuildBattery = Var(m.LOAD_ZONES, m.PERIODS, within=NonNegativeReals)
    m.Battery_Capacity = Expression(m.LOAD_ZONES, m.PERIODS, rule=lambda m, z, p:
        sum(
            m.BuildBattery[z, bld_yr] 
                for bld_yr in m.CURRENT_AND_PRIOR_PERIODS[p] if bld_yr + m.battery_n_years > p
        )
    )

    # rate of charging/discharging battery
    m.ChargeBattery = Var(m.LOAD_ZONES, m.TIMEPOINTS, within=NonNegativeReals)
    m.DischargeBattery = Var(m.LOAD_ZONES, m.TIMEPOINTS, within=NonNegativeReals)

    # storage level at start of each timepoint
    m.BatteryLevel = Var(m.LOAD_ZONES, m.TIMEPOINTS, within=NonNegativeReals)

    # add the storage to the model's energy balance
    m.LZ_Energy_Components_Produce.append('DischargeBattery')
    m.LZ_Energy_Components_Consume.append('ChargeBattery')
    
    # add the batteries to the objective function

    # cost recovery for any battery capacity currently active
    m.BatteryAnnualCost = Expression(
        m.PERIODS,
        rule=lambda m, p: sum(
            m.BuildBattery[z, bld_yr] 
            * m.battery_capital_cost_per_mwh_capacity_by_year[bld_yr] 
            * crf(m.interest_rate, m.battery_n_years)
                for bld_yr in m.CURRENT_AND_PRIOR_PERIODS[p] if bld_yr + m.battery_n_years > p
                    for z in m.LOAD_ZONES 
        )
    )
    m.cost_components_annual.append('BatteryAnnualCost')

    # Calculate the state of charge based on conservation of energy
    # NOTE: this is circular for each day
    # NOTE: the overall level for the day is free, but the levels each timepoint are chained.
    m.Battery_Level_Calc = Constraint(m.LOAD_ZONES, m.TIMEPOINTS, rule=lambda m, z, t:
        m.BatteryLevel[z, t] == 
            m.BatteryLevel[z, m.tp_previous[t]]
            + m.battery_efficiency * m.ChargeBattery[z, m.tp_previous[t]] 
            - m.DischargeBattery[z, m.tp_previous[t]]
    )
      
    # limits on storage level
    m.Battery_Min_Level = Constraint(m.LOAD_ZONES, m.TIMEPOINTS, rule=lambda m, z, t: 
        (1.0 - m.battery_max_discharge) * m.Battery_Capacity[z, m.tp_period[t]]
        <= 
        m.BatteryLevel[z, t]
    )
    m.Battery_Max_Level = Constraint(m.LOAD_ZONES, m.TIMEPOINTS, rule=lambda m, z, t: 
        m.BatteryLevel[z, t]
        <= 
        m.Battery_Capacity[z, m.tp_period[t]]
    )

    m.Battery_Max_Charge = Constraint(m.LOAD_ZONES, m.TIMEPOINTS, rule=lambda m, z, t:
        m.ChargeBattery[z, t]
        <=
        m.Battery_Capacity[z, m.tp_period[t]] * m.battery_max_discharge / m.battery_min_discharge_time
    )
    m.Battery_Max_Disharge = Constraint(m.LOAD_ZONES, m.TIMEPOINTS, rule=lambda m, z, t:
        m.DischargeBattery[z, t]
        <=
        m.Battery_Capacity[z, m.tp_period[t]] * m.battery_max_discharge / m.battery_min_discharge_time
    )