Skip to content

add china modules #142

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions switch_model/china/tech_plans.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# This module was prepared by Josiah Johnston in collaboration with Gang He
# This module can incorperate two types of technological plans: 1)technology plan by tech, period, and load zones, such as wind, solar development plan in each province; 2) national total tech plan/limit by period, such as national total coal capacity limit
# You can use either or both constraints but make sure plans are larger than actual historical data
# Add switch_model.china.tech_plans to the modules.txt file to use this module
# You can cite below two papers to use this module:
# - He, Gang, Jiang Lin, Froylan Sifuentes, Xu Liu, Nikit Abhyankar, and Amol Phadke. 2020. “Rapid Cost Decrease of Renewables and Storage Accelerates the Decarbonization of China’s Power System.” Nature Communications 11 (1): 2486. https://doi.org/10.1038/s41467-020-16184-x.
# - Zhang, Chao, Gang He, Josiah Johnston, and Lijin Zhong. 2021. “Long-Term Transition of China’s Power Sector under Carbon Neutrality Target and Water Withdrawal Constraint.” Journal of Cleaner Production 329 (December): 129765. https://doi.org/10.1016/j.jclepro.2021.129765.


import os
from pyomo.environ import *

"""
Enable capacity plans which establish a minimum capacity target for a
particular technology in given province & period. This supports both the wind, solar, & nuclear plans.

Also enable upper limits on total generation capacity of particular
technologies per period. This supports plans of national nuclear limits.
"""

def define_components(mod):
mod.CAPACITY_PLAN_INDEX = Set(
dimen=3,
within=mod.ENERGY_SOURCES * mod.LOAD_ZONES * mod.PERIODS)
mod.planned_capacity_mw = Param(mod.CAPACITY_PLAN_INDEX)

mod.TOTAL_CAPACITY_LIMIT_INDEX = Set(
dimen=2,
within=mod.ENERGY_SOURCES * mod.PERIODS)
mod.total_capacity_limit_mw = Param(mod.TOTAL_CAPACITY_LIMIT_INDEX)

# Only track for entries we are tracking to save time & RAM
mod.CapacityByEnergySourceZonePeriod = Expression(
mod.CAPACITY_PLAN_INDEX,
# m:model; e: energy source; z: zone; p: period
rule=lambda m, e, z, p: (
sum(m.GenCapacity[g, p]
# use GENS_BY_TECHNOLOGY if using technology plan
for g in m.GENS_BY_ENERGY_SOURCE[e]
if m.gen_load_zone[g] == z
)
)
)
mod.Enforce_Capacity_Plan = Constraint(
mod.CAPACITY_PLAN_INDEX,
rule=lambda m, e, z, p: (
m.CapacityByEnergySourceZonePeriod[e,z,p] >= m.planned_capacity_mw[e,z,p]
)
)

mod.TotalCapByEnergySource = Expression(
mod.TOTAL_CAPACITY_LIMIT_INDEX,
rule=lambda m, e, p: (
sum(m.GenCapacity[g, p] for g in m.GENS_BY_ENERGY_SOURCE[e])
)
)
mod.Enforce_Total_Capacity_Limit = Constraint(
mod.TOTAL_CAPACITY_LIMIT_INDEX,
rule=lambda m, e, p: (
m.TotalCapByEnergySource[e,p] <= m.total_capacity_limit_mw[e,p]
)
)


def load_inputs(mod, switch_data, inputs_dir):
"""
Both files are optional.

capacity_plans.csv
ENERGY_SOURCES LOAD_ZONES PERIOD planned_capacity_mw

total_capacity_limits.csv
ENERGY_SOURCES PERIOD total_capacity_limit_mw
"""
switch_data.load_aug(
filename=os.path.join(inputs_dir, 'capacity_plans.csv'),
optional=True,
auto_select=True,
index=mod.CAPACITY_PLAN_INDEX,
param=(mod.planned_capacity_mw,))
switch_data.load_aug(
filename=os.path.join(inputs_dir, 'total_capacity_limits.csv'),
optional=True,
auto_select=True,
index=mod.TOTAL_CAPACITY_LIMIT_INDEX,
param=(mod.total_capacity_limit_mw,))
116 changes: 116 additions & 0 deletions switch_model/china/water_limits.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# This module was prepared by Josiah Johnston in collaboration with Gang He
# This module introduces water withdrawal constraints per water basin per period.
# Data needed: gene_info.csv table needs gen_water_basin, gen_cooling_water_m3_per_mwh for each plant; water_limit_annual.csv WATER_BASINS PERIOD water_basin_name_cn water_basin_limit_mm3
# Add switch_model.china.water_limit to the modules.txt file to use this module
# You can cite below paper to use this module:
# - Zhang, Chao, Gang He, Josiah Johnston, and Lijin Zhong. 2021. “Long-Term Transition of China’s Power Sector under Carbon Neutrality Target and Water Withdrawal Constraint.” Journal of Cleaner Production 329 (December): 129765. https://doi.org/10.1016/j.jclepro.2021.129765.

import os
import pandas as pd

from pyomo.environ import *

"""
Limit cooling water withdrawals for thermal power plants based on annual water
withdrawals goals per water basin.
"""

def define_components(mod):
mod.WATER_BASIN_PERIODS = Set(
dimen=2,
validate=lambda m, b, p: p in m.PERIODS)
mod.WATER_BASINS = Set(
initialize=lambda m: set([b for b, p in m.WATER_BASIN_PERIODS]))

mod.water_basin_limit_mm3 = Param(mod.WATER_BASIN_PERIODS)
mod._water_basin_name_cn_raw = Param(mod.WATER_BASIN_PERIODS)

# Validate water basin english-Chinese names are consistent in every record
mod.consistent_water_basin_names = BuildCheck(
mod.WATER_BASINS,
rule=lambda m, wb: \
len(set(
[m._water_basin_name_cn_raw[wb,p]
for _wb, p in m.WATER_BASIN_PERIODS if _wb == wb]
)) == 1
)

def default_water_basin_name_cn(m, wb):
names = set([
m._water_basin_name_cn_raw[wb,p]
for _wb, p in m.WATER_BASIN_PERIODS if _wb == wb
])
if names:
return names.pop()
else:
return wb
mod.water_basin_name_cn = Param(mod.WATER_BASINS,
initialize=default_water_basin_name_cn)

mod.gen_water_basin = Param(mod.GENERATION_PROJECTS, within=mod.WATER_BASINS)
mod.gen_cooling_water_m3_per_mwh = Param(mod.GENERATION_PROJECTS)

mod.GENS_IN_WATER_BASIN = Set(
mod.WATER_BASINS,
initialize=lambda m, wb: set(
g for g in m.GENERATION_PROJECTS if m.gen_water_basin[g] == wb))
mod.AnnualCoolingWaterWithdrawals_mm3 = Expression(
mod.WATER_BASIN_PERIODS,
rule=lambda m, wb, p: sum(
m.gen_cooling_water_m3_per_mwh[g] *
sum(m.DispatchGen[g, t] * m.tp_weight_in_year[t]
for t in m.TPS_FOR_GEN_IN_PERIOD[g, p]
)
for g in m.GENS_IN_WATER_BASIN[wb]
) / 1000000.0,
doc="Total cooling water withdrawals per basin by thermal plants, "
"scaled to annual average in units of million cubic meters."
)
mod.Enforce_Cooling_Water_Limits = Constraint(
mod.WATER_BASIN_PERIODS,
rule=lambda m, wb, p: (
m.AnnualCoolingWaterWithdrawals_mm3[wb,p] <= m.water_basin_limit_mm3[wb,p]
)
)


def load_inputs(mod, switch_data, inputs_dir):
"""
generation_projects_info.csv needs these extra columns:
gen_water_basin, gen_cooling_water_m3_per_mwh

water_limit_annual.csv
WATER_BASINS PERIOD water_basin_name_cn water_basin_limit_mm3

Note: The optional Chinese-character basin name (water_basin_name_cn) in
water_limit_annual is de-normalized because it is actually keyed by
WATER_BASINS, not WATER_BASINS & PERIOD. It exists to make manual review &
input file preparation more convenient, and is normalized & checked for
consistency during model creation.
"""
switch_data.load_aug(
filename=os.path.join(inputs_dir, 'generation_projects_info.csv'),
auto_select=True,
param=(mod.gen_water_basin, mod.gen_cooling_water_m3_per_mwh))
switch_data.load_aug(
filename=os.path.join(inputs_dir, 'water_limit_annual.csv'),
optional_params=['water_basin_name_cn'],
select=['WATER_BASINS', 'PERIOD', 'water_basin_name_cn',
'water_basin_limit_mm3'],
index=mod.WATER_BASIN_PERIODS,
param=(mod._water_basin_name_cn_raw, mod.water_basin_limit_mm3))


def post_solve(instance, outdir):
m = instance
normalized_dat = [{
"WATER_BASIN": wb,
"PERIOD": p,
"water_basin_name_cn": m.water_basin_name_cn[wb],
"water_basin_limit_mm3": m.water_basin_limit_mm3[wb, p],
"AnnualCoolingWaterWithdrawals_mm3": value(m.AnnualCoolingWaterWithdrawals_mm3[wb, p])
} for wb, p in m.WATER_BASIN_PERIODS]
df = pd.DataFrame(normalized_dat)
df.sort_values(by=["WATER_BASIN", "PERIOD"], inplace=True)
df.set_index(["WATER_BASIN", "PERIOD"], inplace=True)
df.to_csv(os.path.join(outdir, "cooling_water.csv"))