Skip to content

Commit

Permalink
Merge branch 'develop' into hydrogen
Browse files Browse the repository at this point in the history
  • Loading branch information
indu-manogaran committed Sep 25, 2023
2 parents bf8d9ed + fde4362 commit 1d2cbf2
Show file tree
Hide file tree
Showing 15 changed files with 167,035 additions and 150 deletions.
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,22 @@ Classify the change according to the following categories:
### Deprecated
### Removed

## v0.33.0
### Added
- Functionality to evaluate scenarios with Wind can in the ERP (`backup_reliability`)
- Dispatch data for outages: Wind, ElectricStorage SOC, and critical load
### Fixed
- Fix `backup_reliability_reopt_inputs(d, p, r)` so doesn't ignore `CHP` from REopt scenario
- In `backup_reliability_reopt_inputs(d, p, r)`, get `Generator` and `CHP` fuel related values from REopt results _Dict_ d and `REoptInputs` _struct_ p, unless the user overrides the REopt results by providing **generator_size_kw**
- Remove use of non-existent **tech_upgraded** `Outages` outputs, using **tech_microgrid_size_kw** instead
- Added missing **electric_storage_microgrid_upgraded** to `Outages` results
- Fix bug causing _InexactError_ in `num_battery_bins_default`
- Update docstrings in `backup_reliability.jl`
- Avoid supply > critical load during outages by changing load balance to ==
### Changed
- Updated REopt license
- Changed `backup_reliability` results key from **fuel_outage_survival_final_time_step** to **fuel_survival_final_time_step** for consistency with other keys

## v0.32.7
### Fixed
- Bugs in EASIUR health cost calcs
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "REopt"
uuid = "d36ad4e8-d74a-4f7a-ace1-eaea049febf6"
authors = ["Nick Laws", "Hallie Dunham <[email protected]>", "Bill Becker <[email protected]>", "Bhavesh Rathod <[email protected]>", "Alex Zolan <[email protected]>", "Amanda Farthing <[email protected]>"]
version = "0.32.6"
version = "0.33.0"

[deps]
ArchGDAL = "c9ce4bd3-c3d5-55b8-8973-c0e20141b8c3"
Expand Down
2 changes: 1 addition & 1 deletion src/constraints/outage_constraints.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
function add_dv_UnservedLoad_constraints(m,p)
# effective load balance (with slack in dvUnservedLoad)
@constraint(m, [s in p.s.electric_utility.scenarios, tz in p.s.electric_utility.outage_start_time_steps, ts in p.s.electric_utility.outage_time_steps],
m[:dvUnservedLoad][s, tz, ts] >= p.s.electric_load.critical_loads_kw[tz+ts-1]
m[:dvUnservedLoad][s, tz, ts] == p.s.electric_load.critical_loads_kw[tz+ts-1]
- sum( m[:dvMGRatedProduction][t, s, tz, ts] * p.production_factor[t, tz+ts-1] * p.levelization_factor[t]
- m[:dvMGProductionToStorage][t, s, tz, ts] - m[:dvMGCurtail][t, s, tz, ts]
for t in p.techs.elec
Expand Down
3 changes: 2 additions & 1 deletion src/core/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,8 @@ function dictkeys_tosymbols(d::Dict)
"emissions_factor_series_lb_SO2_per_kwh",
"emissions_factor_series_lb_PM25_per_kwh",
#for ERP
"pv_production_factor_series", "battery_starting_soc_series_fraction",
"pv_production_factor_series", "wind_production_factor_series",
"battery_starting_soc_series_fraction",
"monthly_mmbtu", "monthly_tonhour"
] && !isnothing(v)
try
Expand Down
300 changes: 211 additions & 89 deletions src/outagesim/backup_reliability.jl

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/outagesim/outage_simulator.jl
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ function simulate_outages(d::Dict, p::REoptInputs; microgrid_only::Bool=false)
batt_kw = get(d["ElectricStorage"], "size_kw", 0)
init_soc = get(d["ElectricStorage"], "soc_series_fraction", zeros(length(p.time_steps)))
end
if microgrid_only && !Bool(get(d["Outages"], "storage_upgraded", false))
if microgrid_only && !Bool(get(d["Outages"], "electric_storage_microgrid_upgraded", false))
batt_kwh = 0
batt_kw = 0
init_soc = zeros(length(p.time_steps))
Expand Down
24 changes: 22 additions & 2 deletions src/results/outages.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
- `pv_to_storage_series_kw` Array of PV power sent to the battery in every outage modeled.
- `pv_curtailed_series_kw` Array of PV curtailed in every outage modeled.
- `pv_to_load_series_kw` Array of PV power used to meet load in every outage modeled.
- `wind_microgrid_size_kw` Optimal microgrid Wind capacity.
- `wind_microgrid_upgrade_cost` The cost to include the Wind system in the microgrid.
- `wind_to_storage_series_kw` Array of Wind power sent to the battery in every outage modeled.
- `wind_curtailed_series_kw` Array of Wind curtailed in every outage modeled.
- `wind_to_load_series_kw` Array of Wind power used to meet load in every outage modeled.
- `generator_microgrid_size_kw` Optimal microgrid Generator capacity. Note that the name `Generator` can change based on user provided `Generator.name`.
- `generator_microgrid_upgrade_cost` The cost to include the Generator system in the microgrid.
- `generator_to_storage_series_kw` Array of Generator power sent to the battery in every outage modeled.
Expand All @@ -25,6 +30,8 @@
- `chp_to_load_series_kw` Array of CHP power used to meet load in every outage modeled.
- `chp_fuel_used_per_outage_mmbtu` Array of fuel used in every outage modeled, summed over all CHPs.
- `microgrid_upgrade_capital_cost` Total capital cost of including technologies in the microgrid
- `critical_loads_per_outage_series_kw` Critical load series in every outage modeled
- `soc_series_fraction` ElectricStorage state of charge series in every outage modeled
!!! warn
The output keys for "Outages" are subject to change.
Expand Down Expand Up @@ -65,15 +72,28 @@ function add_outage_results(m, p, d::Dict)
# instead of all ts b/c dvUnservedLoad has unused values in third dimension
end
r["unserved_load_per_outage_kwh"] = round.(unserved_load_per_outage, digits=2)
r["electric_storage_microgrid_upgraded"] = Bool(round(value(m[:binMGStorageUsed]), digits=0))
r["storage_microgrid_upgrade_cost"] = value(m[:dvMGStorageUpgradeCost])
r["microgrid_upgrade_capital_cost"] = r["storage_microgrid_upgrade_cost"]
if !isempty(p.s.storage.types.elec) && Bool(round(value(m[:binMGStorageUsed]), digits=0))
if !isempty(p.s.storage.types.elec) && r["electric_storage_microgrid_upgraded"]
r["storage_discharge_series_kw"] = value.(m[:dvMGDischargeFromStorage]).data
electric_storage_energy_capacity_kwh = round(sum(value(m[Symbol("dvStorageEnergy")][b]) for b in p.s.storage.types.elec), digits=2)
r["soc_series_fraction"] = round.(value.(m[:dvMGStoredEnergy][:,:,1:end]).data ./ electric_storage_energy_capacity_kwh, digits=3)
else
r["storage_discharge_series_kw"] = []
r["soc_series_fraction"] = []
end

r["critical_loads_per_outage_series_kw"] = zeros(S, T, TS)
for ts in p.s.electric_utility.outage_time_steps
for (t, tz) in enumerate(p.s.electric_utility.outage_start_time_steps)
for s in p.s.electric_utility.scenarios
r["critical_loads_per_outage_series_kw"][s,t,ts] = p.s.electric_load.critical_loads_kw[tz+ts-1]
end
end
end

for (tech_type_name, tech_set) in [("pv", p.techs.pv), ("generator", p.techs.gen), ("chp", p.techs.chp)]
for (tech_type_name, tech_set) in [("pv", p.techs.pv), ("wind", "Wind" in p.techs.elec ? ["Wind"] : String[]), ("generator", p.techs.gen), ("chp", p.techs.chp)]
if !isempty(tech_set)
r[tech_type_name * "_microgrid_size_kw"] = round(
sum(
Expand Down
2 changes: 1 addition & 1 deletion src/results/pv.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ function add_pv_results(m::JuMP.AbstractModel, p::REoptInputs, d::Dict; _n="")

for t in p.techs.pv
r = Dict{String, Any}()
r["production_factor_series"] = p.production_factor[t, :]
r["production_factor_series"] = Vector(p.production_factor[t, :])
r["size_kw"] = round(value(m[Symbol("dvSize"*_n)][t]), digits=4)

# NOTE: must use anonymous expressions in this loop to overwrite values for cases with multiple PV
Expand Down
12 changes: 6 additions & 6 deletions src/results/wind.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,26 @@ function add_wind_results(m::JuMP.AbstractModel, p::REoptInputs, d::Dict; _n="")

r = Dict{String, Any}()
t = "Wind"
r["production_factor_series"] = p.production_factor[t, :]
r["production_factor_series"] = Vector(p.production_factor[t, :])
per_unit_size_om = @expression(m, p.third_party_factor * p.pwf_om * m[:dvSize][t] * p.om_cost_per_kw[t])

r["size_kw"] = round(value(m[:dvSize][t]), digits=2)
r["lifecycle_om_cost_after_tax"] = round(value(per_unit_size_om) * (1 - p.s.financial.owner_tax_rate_fraction), digits=0)
r["year_one_om_cost_before_tax"] = round(value(per_unit_size_om) / (p.pwf_om * p.third_party_factor), digits=0)

if !isempty(p.s.storage.types.elec)
WindToStorage = @expression(m, [ts in p.time_steps],
sum(m[:dvProductionToStorage][b, t, ts] for b in p.s.storage.types.elec))
WindToStorage = (sum(m[:dvProductionToStorage][b, t, ts] for b in p.s.storage.types.elec) for ts in p.time_steps)
PVtoBatt = (sum(m[Symbol("dvProductionToStorage"*_n)][b, t, ts] for b in p.s.storage.types.elec) for ts in p.time_steps)

else
WindToStorage = zeros(length(p.time_steps))
end
r["electric_to_storage_series_kw"] = round.(value.(WindToStorage), digits=3)

r["annual_energy_exported_kwh"] = 0.0
if !isempty(p.s.electric_tariff.export_bins)
WindToGrid = @expression(m, [ts in p.time_steps],
sum(m[:dvProductionToGrid][t, u, ts] for u in p.export_bins_by_tech[t]))
r["electric_to_grid_series_kw"] = round.(value.(WindToGrid), digits=3).data
WindToGrid = (sum(m[:dvProductionToGrid][t, u, ts] for u in p.export_bins_by_tech[t]) for ts in p.time_steps)
r["electric_to_grid_series_kw"] = round.(value.(WindToGrid), digits=3)
r["annual_energy_exported_kwh"] = round(
sum(r["electric_to_grid_series_kw"]) * p.hours_per_time_step, digits=0)
else
Expand Down
28 changes: 17 additions & 11 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,6 @@ else # run HiGHS tests
"battery_operational_availability" => 1.0,
"battery_minimum_soc_fraction" => 0.0,
"battery_starting_soc_series_fraction" => results["ElectricStorage"]["soc_series_fraction"],
"pv_operational_availability" => 1.0,
"critical_loads_kw" => results["ElectricLoad"]["critical_load_series_kw"]#4000*ones(8760)#p.s.electric_load.critical_loads_kw
)
reliability_results = backup_reliability(reliability_inputs)
Expand All @@ -352,7 +351,7 @@ else # run HiGHS tests
@test simresults["probs_of_surviving"][i] reliability_results["mean_fuel_survival_by_duration"][i] atol=0.01
end

# Second, gen, PV, battery
# Second, gen, PV, Wind, battery
reopt_inputs = JSON.parsefile("./scenarios/backup_reliability_reopt_inputs.json")
reopt_inputs["ElectricLoad"]["annual_kwh"] = 4*reopt_inputs["ElectricLoad"]["annual_kwh"]
p = REoptInputs(reopt_inputs)
Expand All @@ -370,6 +369,7 @@ else # run HiGHS tests
"battery_operational_availability" => 1.0,
"battery_minimum_soc_fraction" => 0.0,
"pv_operational_availability" => 1.0,
"wind_operational_availability" => 1.0
)
reliability_results = backup_reliability(results, p, reliability_inputs)
for i = 1:min(length(simresults["probs_of_surviving"]), reliability_inputs["max_outage_duration"])
Expand Down Expand Up @@ -488,33 +488,39 @@ else # run HiGHS tests
reliability_results = backup_reliability(input_dict)
@test reliability_results["unlimited_fuel_mean_cumulative_survival_by_duration"][24] (0.99^20)*(0.9*0.98) atol=0.00001

#More realistic case of hospital load with 2 generators, PV, and battery
#More complex case of hospital load with 2 generators, PV, wind, and battery
reliability_inputs = JSON.parsefile("./scenarios/backup_reliability_inputs.json")
reliability_results = backup_reliability(reliability_inputs)
@test reliability_results["unlimited_fuel_cumulative_survival_final_time_step"][1] 0.858756 atol=0.0001
@test reliability_results["cumulative_survival_final_time_step"][1] 0.858756 atol=0.0001
@test reliability_results["mean_cumulative_survival_final_time_step"] 0.904242 atol=0.0001#0.833224


# Test gens+pv+wind+batt with 3 arg version of backup_reliability
# Attention! REopt optimization results are presaved in erp_gens_batt_pv_wind_reopt_results.json
# If you modify backup_reliability_reopt_inputs.json, you must add this before JSON.parsefile:
# results = run_reopt(model, p)
# open("scenarios/erp_gens_batt_pv_wind_reopt_results.json","w") do f
# JSON.print(f, results, 4)
# end
for input_key in [
"generator_size_kw",
"battery_size_kw",
"battery_size_kwh",
"pv_size_kw",
"wind_size_kw",
"critical_loads_kw",
"pv_production_factor_series"
"pv_production_factor_series",
"wind_production_factor_series"
]
delete!(reliability_inputs, input_key)
end

model = Model(optimizer_with_attributes(HiGHS.Optimizer,
"output_flag" => false, "log_to_console" => false)
)
# note: the wind prod series in backup_reliability_reopt_inputs.json is actually a PV profile (to in order to test a wind scenario that should give same results as an existing PV one)
p = REoptInputs("./scenarios/backup_reliability_reopt_inputs.json")
results = run_reopt(model, p)
results = JSON.parsefile("./scenarios/erp_gens_batt_pv_wind_reopt_results.json")
reliability_results = backup_reliability(results, p, reliability_inputs)

@test reliability_results["unlimited_fuel_cumulative_survival_final_time_step"][1] 0.802997 atol=0.0001
@test reliability_results["cumulative_survival_final_time_step"][1] 0.802997 atol=0.0001
@test reliability_results["mean_cumulative_survival_final_time_step"] 0.817586 atol=0.0001
@test reliability_results["mean_cumulative_survival_final_time_step"] 0.817586 atol=0.001
end
end
Loading

0 comments on commit 1d2cbf2

Please sign in to comment.