Skip to content

Commit

Permalink
Merge pull request #167 from NREL/develop
Browse files Browse the repository at this point in the history
v0.24.0 Output naming changes
  • Loading branch information
adfarth authored Jan 6, 2023
2 parents da1a54f + 018d85a commit 0e93d9f
Show file tree
Hide file tree
Showing 28 changed files with 444 additions and 366 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ Classify the change according to the following categories:
### Deprecated
### Removed

## v0.24.0
### Changed
- Major name change overall for outputs/results. Changed energy-related outputs with "year_one" in name to "annual" to reflect that they are actually average annual output values. Changed any "average_annual" naming to "annual" to simplify. Changed `to_tes` and `to_battery` outputs to `to_storage` for consistency
### Added
- Added **thermal_production_series_mmbtu_per_hour** to CHP results.
### Removed
- Removed `Wind` and `Generator` outputs **year_one_energy_produced_kwh** since these techs do not include degradation
## v0.23.0
### Added
- Add **REoptLogger** type of global logger with a standard out to the console and to a dictionary
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.23.0"
version = "0.24.0"

[deps]
ArchGDAL = "c9ce4bd3-c3d5-55b8-8973-c0e20141b8c3"
Expand Down
23 changes: 11 additions & 12 deletions src/core/absorption_chiller.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,12 @@
"""
`AbsorptionChiller` is an optional REopt input with the following keys and default values:
```julia
thermal_consumption_hot_water_or_steam::Union{String, Nothing} = nothing
chp_prime_mover::String = ""
thermal_consumption_hot_water_or_steam::Union{String, Nothing} = nothing # Defaults to "hot_water" if chp_prime_mover or boiler_type are not provided
chp_prime_mover::String = "" # Informs thermal_consumption_hot_water_or_steam if not provided
#Required if neither "thermal_consumption_hot_water_or_steam" nor "chp_prime_mover" nor an ExistingBoiler included in inputs:
# Defaults for fields below are dependent on thermal_consumption_hot_water_or_steam and max cooling load
installed_cost_per_ton::Union{Float64, Nothing} = nothing
om_cost_per_ton::Union{Float64, Nothing} = nothing
#Optional
min_ton::Float64 = 0.0,
max_ton::Float64 = BIG_NUMBER,
cop_thermal::Union{Float64, Nothing} = nothing,
Expand All @@ -49,12 +46,14 @@
macrs_bonus_fraction::Float64 = 0
```
!!! note "Required inputs"
To model AbsorptionChiller, you must provide at least one of the following: (i) `thermal_consumption_hot_water_or_steam` from $(HOT_WATER_OR_STEAM), (ii)
(ii), `chp_prime_mover` from $(PRIME_MOVERS),or (iii) all of the "custom inputs" defined below.
If prime_mover is provided, any missing value from the "custom inputs" will be populated from data/absorption_chiller/defaults.json,
based on the `thermal_consumption_hot_water_or_steam` or `prime_mover`. boiler_type is "steam" if `prime_mover` is "combustion_turbine"
and is "hot_water" for all other `prime_mover` types.
!!! Note
To model AbsorptionChiller, there is logic which informs defaults for costs and COP:
(i) `thermal_consumption_hot_water_or_steam` from $(HOT_WATER_OR_STEAM),
(ii) `chp_prime_mover` from $(PRIME_MOVERS), or
(iii) if (i) and (ii) are not provided, the default `thermal_consumption_hot_water_or_steam` is `hot_water`
The defaults for costs and COP will be populated from data/absorption_chiller/defaults.json,
based on the `thermal_consumption_hot_water_or_steam` or `chp_prime_mover`.
`boiler_type` is "steam" if `prime_mover` is "combustion_turbine" and is "hot_water" for all other `chp_prime_mover` types.
"""
Base.@kwdef mutable struct AbsorptionChiller <: AbstractThermalTech
thermal_consumption_hot_water_or_steam::Union{String, Nothing} = nothing
Expand Down
2 changes: 1 addition & 1 deletion src/core/chp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ prime_movers = ["recip_engine", "micro_turbine", "combustion_turbine", "fuel_cel
emissions_factor_lb_PM25_per_mmbtu::Float64 = FUEL_DEFAULTS["emissions_factor_lb_PM25_per_mmbtu"][fuel_type]
```
!!! note defaults and "Required inputs"
!!! note "Defaults and required inputs"
See the `get_chp_defaults_prime_mover_size_class()` function docstring for details on the logic of choosing the type of CHP that is modeled
If no information is provided, the default `prime_mover` is `recip_engine` and the `size_class` is 1 which represents
the widest range of sizes available.
Expand Down
3 changes: 1 addition & 2 deletions src/core/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,7 @@ function annuity_escalation(analysis_period::Int, rate_escalation::Real, rate_di
end


function levelization_factor(years::Int, rate_escalation::Real, rate_discount::Real,
rate_degradation::Real)
function levelization_factor(years::Int, rate_escalation::Real, rate_discount::Real, rate_degradation::Real)
#=
NOTE: levelization_factor for an electricity producing tech is the ratio of:
- an annuity with an escalation rate equal to the electricity cost escalation rate, starting year 1,
Expand Down
12 changes: 6 additions & 6 deletions src/outagesim/backup_reliability.jl
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ function backup_reliability_inputs(d::Dict, p::REoptInputs; r::Dict = Dict())::D
critical_loads_kw = p.s.electric_load.critical_loads_kw

if "CHP" in keys(d)
chp_generation = get(d["CHP"], "year_one_electric_production_series_kw", zero_array)
chp_generation = get(d["CHP"], "electric_production_series_kw", zero_array)
critical_loads_kw .-= chp_generation
end

Expand All @@ -456,10 +456,10 @@ function backup_reliability_inputs(d::Dict, p::REoptInputs; r::Dict = Dict())::D
pv_kw_ac_hourly = zero_array
if "PV" in keys(d)
pv_kw_ac_hourly = (
get(d["PV"], "year_one_to_battery_series_kw", zero_array)
+ get(d["PV"], "year_one_curtailed_production_series_kw", zero_array)
+ get(d["PV"], "year_one_to_load_series_kw", zero_array)
+ get(d["PV"], "year_one_to_grid_series_kw", zero_array)
get(d["PV"], "electric_to_storage_series_kw", zero_array)
+ get(d["PV"], "electric_curtailed_series_kw", zero_array)
+ get(d["PV"], "electric_to_load_series_kw", zero_array)
+ get(d["PV"], "electric_to_grid_series_kw", zero_array)
)
end
if microgrid_only && !Bool(get(d, "PV_upgraded", false))
Expand All @@ -477,7 +477,7 @@ function backup_reliability_inputs(d::Dict, p::REoptInputs; r::Dict = Dict())::D

batt_kwh = get(d["Storage"], "size_kwh", 0)
batt_kw = get(d["Storage"], "size_kw", 0)
init_soc = get(d["Storage"], "year_one_soc_series_fraction", [])
init_soc = get(d["Storage"], "soc_series_fraction", [])

if microgrid_only && !Bool(get(d, "storage_upgraded", false))
batt_kwh = 0
Expand Down
10 changes: 5 additions & 5 deletions src/outagesim/outage_simulator.jl
Original file line number Diff line number Diff line change
Expand Up @@ -259,10 +259,10 @@ function simulate_outages(d::Dict, p::REoptInputs; microgrid_only::Bool=false)
pv_kw_ac_hourly = zeros(length(p.time_steps))
if "PV" in keys(d)
pv_kw_ac_hourly = (
get(d["PV"], "year_one_to_battery_series_kw", zeros(length(p.time_steps)))
+ get(d["PV"], "year_one_curtailed_production_series_kw", zeros(length(p.time_steps)))
+ get(d["PV"], "year_one_to_load_series_kw", zeros(length(p.time_steps)))
+ get(d["PV"], "year_one_to_grid_series_kw", zeros(length(p.time_steps)))
get(d["PV"], "electric_to_storage_series_kw", zeros(length(p.time_steps)))
+ get(d["PV"], "electric_curtailed_series_kw", zeros(length(p.time_steps)))
+ get(d["PV"], "electric_to_load_series_kw", zeros(length(p.time_steps)))
+ get(d["PV"], "electric_to_grid_series_kw", zeros(length(p.time_steps)))
)
end
if microgrid_only && !Bool(get(d["Outages"], "PV_upgraded", false))
Expand All @@ -275,7 +275,7 @@ function simulate_outages(d::Dict, p::REoptInputs; microgrid_only::Bool=false)
if "ElectricStorage" in keys(d)
batt_kwh = get(d["ElectricStorage"], "size_kwh", 0)
batt_kw = get(d["ElectricStorage"], "size_kw", 0)
init_soc = get(d["ElectricStorage"], "year_one_soc_series_fraction", zeros(length(p.time_steps)))
init_soc = get(d["ElectricStorage"], "soc_series_fraction", zeros(length(p.time_steps)))
end
if microgrid_only && !Bool(get(d["Outages"], "storage_upgraded", false))
batt_kwh = 0
Expand Down
32 changes: 17 additions & 15 deletions src/results/absorption_chiller.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,16 @@
# *********************************************************************************
"""
`AbsorptionChiller` results keys:
- `size_kw` Power capacity size of the absorption chiller system [kW]
- `size_ton`
- `year_one_to_tes_series_ton`
- `year_one_thermal_consumption_series`
- `year_one_thermal_consumption_kwh`
- `year_one_thermal_production_tonhour`
- `year_one_electric_consumption_series`
- `year_one_electric_consumption_kwh`
- `size_kw` # Optimal power capacity size of the absorption chiller system [kW]
- `size_ton`
- `thermal_to_storage_series_ton` # Thermal production to ColdThermalStorage
- `thermal_to_load_series_ton` # Thermal production to cooling load
- `thermal_consumption_series_mmbtu_per_hour`
- `annual_thermal_consumption_mmbtu`
- `annual_thermal_production_tonhour`
- `electric_consumption_series_kw`
- `annual_electric_consumption_kwh`
"""
function add_absorption_chiller_results(m::JuMP.AbstractModel, p::REoptInputs, d::Dict; _n="")
# Adds the `AbsorptionChiller` results to the dictionary passed back from `run_reopt` using the solved model `m` and the `REoptInputs` for node `_n`.
Expand All @@ -53,29 +55,29 @@ function add_absorption_chiller_results(m::JuMP.AbstractModel, p::REoptInputs, d
r["size_ton"] = r["size_kw"] / KWH_THERMAL_PER_TONHOUR
@expression(m, ABSORPCHLtoTESKW[ts in p.time_steps],
sum(m[:dvProductionToStorage][b,t,ts] for b in p.s.storage.types.cold, t in p.techs.absorption_chiller))
r["year_one_to_tes_series_ton"] = round.(value.(ABSORPCHLtoTESKW) ./ KWH_THERMAL_PER_TONHOUR, digits=5)
r["thermal_to_storage_series_ton"] = round.(value.(ABSORPCHLtoTESKW) ./ KWH_THERMAL_PER_TONHOUR, digits=5)
@expression(m, ABSORPCHLtoLoadKW[ts in p.time_steps],
sum(m[:dvThermalProduction][t,ts] for t in p.techs.absorption_chiller)
- ABSORPCHLtoTESKW[ts])
r["year_one_to_load_series_ton"] = round.(value.(ABSORPCHLtoLoadKW) ./ KWH_THERMAL_PER_TONHOUR, digits=5)
r["thermal_to_load_series_ton"] = round.(value.(ABSORPCHLtoLoadKW) ./ KWH_THERMAL_PER_TONHOUR, digits=5)
@expression(m, ABSORPCHLThermalConsumptionSeriesKW[ts in p.time_steps],
sum(m[:dvThermalProduction][t,ts] / p.thermal_cop[t] for t in p.techs.absorption_chiller))
r["year_one_thermal_consumption_series_mmbtu_per_hour"] = round.(value.(ABSORPCHLThermalConsumptionSeriesKW) ./ KWH_PER_MMBTU, digits=5)
r["thermal_consumption_series_mmbtu_per_hour"] = round.(value.(ABSORPCHLThermalConsumptionSeriesKW) ./ KWH_PER_MMBTU, digits=5)
@expression(m, Year1ABSORPCHLThermalConsumptionKWH,
p.hours_per_time_step * sum(m[:dvThermalProduction][t,ts] / p.thermal_cop[t]
for t in p.techs.absorption_chiller, ts in p.time_steps))
r["year_one_thermal_consumption_mmbtu"] = round(value(Year1ABSORPCHLThermalConsumptionKWH) / KWH_PER_MMBTU, digits=5)
r["annual_thermal_consumption_mmbtu"] = round(value(Year1ABSORPCHLThermalConsumptionKWH) / KWH_PER_MMBTU, digits=5)
@expression(m, Year1ABSORPCHLThermalProdKWH,
p.hours_per_time_step * sum(m[:dvThermalProduction][t, ts]
for t in p.techs.absorption_chiller, ts in p.time_steps))
r["year_one_thermal_production_tonhour"] = round(value(Year1ABSORPCHLThermalProdKWH) / KWH_THERMAL_PER_TONHOUR, digits=5)
r["annual_thermal_production_tonhour"] = round(value(Year1ABSORPCHLThermalProdKWH) / KWH_THERMAL_PER_TONHOUR, digits=5)
@expression(m, ABSORPCHLElectricConsumptionSeries[ts in p.time_steps],
sum(m[:dvThermalProduction][t,ts] / p.cop[t] for t in p.techs.absorption_chiller) )
r["year_one_electric_consumption_series_kw"] = round.(value.(ABSORPCHLElectricConsumptionSeries), digits=3)
r["electric_consumption_series_kw"] = round.(value.(ABSORPCHLElectricConsumptionSeries), digits=3)
@expression(m, Year1ABSORPCHLElectricConsumption,
p.hours_per_time_step * sum(m[:dvThermalProduction][t,ts] / p.cop[t]
for t in p.techs.absorption_chiller, ts in p.time_steps))
r["year_one_electric_consumption_kwh"] = round(value(Year1ABSORPCHLElectricConsumption), digits=3)
r["annual_electric_consumption_kwh"] = round(value(Year1ABSORPCHLElectricConsumption), digits=3)

d["AbsorptionChiller"] = r
nothing
Expand Down
41 changes: 23 additions & 18 deletions src/results/boiler.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,28 +30,33 @@
"""
`Boiler` results keys:
- `size_mmbtu_per_hour` # Thermal production capacity size of the Boiler [MMBtu/hr]
- `year_one_fuel_consumption_series_mmbtu_per_hour` # Fuel consumption series [MMBtu]
- `year_one_fuel_consumption_mmbtu` # Fuel consumed in a year [MMBtu]
- `year_one_thermal_production_series_mmbtu_per_hour` # Thermal energy production series [MMBtu/hr]
- `year_one_thermal_production_mmbtu` # Thermal energy produced in a year [MMBtu]
- `year_one_thermal_to_tes_series_mmbtu_per_hour` # Thermal power production to HotThermalStorage series [MMBtu/hr]
- `year_one_thermal_to_steamturbine_series_mmbtu_per_hour` # Thermal power production to SteamTurbine series [MMBtu/hr]
- `year_one_thermal_to_load_series_mmbtu_per_hour` # Thermal power production to serve the heating load series [MMBtu/hr]
- `lifecycle_fuel_cost` # Life cycle fuel cost [\$]
- `year_one_fuel_cost` # Year one fuel cost [\$]
- `fuel_consumption_series_mmbtu_per_hour` # Fuel consumption series [MMBtu/hr]
- `annual_fuel_consumption_mmbtu` # Fuel consumed in a year [MMBtu]
- `thermal_production_series_mmbtu_per_hour` # Thermal energy production series [MMBtu/hr]
- `annual_thermal_production_mmbtu` # Thermal energy produced in a year [MMBtu]
- `thermal_to_storage_series_mmbtu_per_hour` # Thermal power production to TES (HotThermalStorage) series [MMBtu/hr]
- `thermal_to_steamturbine_series_mmbtu_per_hour` # Thermal power production to SteamTurbine series [MMBtu/hr]
- `thermal_to_load_series_mmbtu_per_hour` # Thermal power production to serve the heating load series [MMBtu/hr]
- `lifecycle_fuel_cost_after_tax` # Life cycle fuel cost [\$]
- `year_one_fuel_cost_before_tax` # Year one fuel cost [\$]
- `lifecycle_per_unit_prod_om_costs` # Life cycle production-based O&M cost [\$]
!!! note "'Series' and 'Annual' energy outputs are average annual"
REopt performs load balances using average annual production values for technologies that include degradation.
Therefore, all timeseries (`_series`) and `annual_` results should be interpretted as energy outputs averaged over the analysis period.
"""

function add_boiler_results(m::JuMP.AbstractModel, p::REoptInputs, d::Dict; _n="")
r = Dict{String, Any}()
r["size_mmbtu_per_hour"] = round(value(m[Symbol("dvSize"*_n)]["Boiler"]) / KWH_PER_MMBTU, digits=3)
r["year_one_fuel_consumption_series_mmbtu_per_hour"] =
r["fuel_consumption_series_mmbtu_per_hour"] =
round.(value.(m[:dvFuelUsage]["Boiler", ts] for ts in p.time_steps) / KWH_PER_MMBTU, digits=3)
r["year_one_fuel_consumption_mmbtu"] = round(sum(r["year_one_fuel_consumption_series_mmbtu_per_hour"]), digits=3)
r["annual_fuel_consumption_mmbtu"] = round(sum(r["fuel_consumption_series_mmbtu_per_hour"]), digits=3)

r["year_one_thermal_production_series_mmbtu_per_hour"] =
r["thermal_production_series_mmbtu_per_hour"] =
round.(value.(m[:dvThermalProduction]["Boiler", ts] for ts in p.time_steps) / KWH_PER_MMBTU, digits=5)
r["year_one_thermal_production_mmbtu"] = round(sum(r["year_one_thermal_production_series_mmbtu_per_hour"]), digits=3)
r["annual_thermal_production_mmbtu"] = round(sum(r["thermal_production_series_mmbtu_per_hour"]), digits=3)

if !isempty(p.s.storage.types.hot)
@expression(m, BoilerToHotTESKW[ts in p.time_steps],
Expand All @@ -60,25 +65,25 @@ function add_boiler_results(m::JuMP.AbstractModel, p::REoptInputs, d::Dict; _n="
else
BoilerToHotTESKW = zeros(length(p.time_steps))
end
r["year_one_thermal_to_tes_series_mmbtu_per_hour"] = round.(value.(BoilerToHotTESKW / KWH_PER_MMBTU), digits=3)
r["thermal_to_storage_series_mmbtu_per_hour"] = round.(value.(BoilerToHotTESKW / KWH_PER_MMBTU), digits=3)

if !isempty(p.techs.steam_turbine) && p.s.boiler.can_supply_steam_turbine
@expression(m, BoilerToSteamTurbine[ts in p.time_steps], m[:dvThermalToSteamTurbine]["Boiler",ts])
else
BoilerToSteamTurbine = zeros(length(p.time_steps))
end
r["year_one_thermal_to_steamturbine_series_mmbtu_per_hour"] = round.(value.(BoilerToSteamTurbine), digits=3)
r["thermal_to_steamturbine_series_mmbtu_per_hour"] = round.(value.(BoilerToSteamTurbine), digits=3)

BoilerToLoad = @expression(m, [ts in p.time_steps],
m[:dvThermalProduction]["Boiler", ts] - BoilerToHotTESKW[ts] - BoilerToSteamTurbine[ts]
)
r["year_one_thermal_to_load_series_mmbtu_per_hour"] = round.(value.(BoilerToLoad / KWH_PER_MMBTU), digits=3)
r["thermal_to_load_series_mmbtu_per_hour"] = round.(value.(BoilerToLoad / KWH_PER_MMBTU), digits=3)

lifecycle_fuel_cost = p.pwf_fuel["Boiler"] * value(
sum(m[:dvFuelUsage]["Boiler", ts] * p.fuel_cost_per_kwh["Boiler"][ts] for ts in p.time_steps)
)
r["lifecycle_fuel_cost"] = round(lifecycle_fuel_cost * (1 - p.s.financial.offtaker_tax_rate_fraction), digits=3)
r["year_one_fuel_cost"] = round(lifecycle_fuel_cost / p.pwf_fuel["Boiler"], digits=3)
r["lifecycle_fuel_cost_after_tax"] = round(lifecycle_fuel_cost * (1 - p.s.financial.offtaker_tax_rate_fraction), digits=3)
r["year_one_fuel_cost_before_tax"] = round(lifecycle_fuel_cost / p.pwf_fuel["Boiler"], digits=3)

r["lifecycle_per_unit_prod_om_costs"] = round(value(m[:TotalBoilerPerUnitProdOMCosts]), digits=3)

Expand Down
Loading

0 comments on commit 0e93d9f

Please sign in to comment.