Skip to content

Commit

Permalink
Merge branch 'develop' into cambium
Browse files Browse the repository at this point in the history
  • Loading branch information
adfarth committed Aug 10, 2023
2 parents 877463f + 37b19fb commit 2472ecb
Show file tree
Hide file tree
Showing 17 changed files with 58 additions and 39 deletions.
15 changes: 12 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,27 @@ Classify the change according to the following categories:
### Fixed
- Adjust grid emissions profiles for day of week alignment with load_year.
- In `test_with_xpress.jl`, updated "Emissions and Renewable Energy Percent" expected values to account for load year adjustment.
## Develop

## Develop 2023-08-09
### Changed
- Changed unit test expected values due to update to PVWatts v8, which slightly changed expected PV production factors.
- Updated `get_existing_chiller_cop` function to accept scalar values instead of vectors to allow for faster API transactions.
### Fixed
- Steamturbine defaults processing
- simulated_load monthly values processing

## v0.32.4
### Changed
- Consolidated PVWatts API calls to 1 call (previously 3 separate calls existed). API call occurs in `src/core/utils.jl/call_pvwatts_api()`. This function is called for PV in `src/core/production_factor.jl/get_production_factor(PV)` and for GHP in `src/core/scenario.jl`. If GHP and PV are evaluated together, the GHP PVWatts call for ambient temperature is also used to assign the pv.production_factor_series in Scenario.jl so that the PVWatts API does not get called again downstream in `get_production_factor(PV)`.
- In `src/core/utils.jl/call_pvwatts_api()`, updated NSRDB bounds used in PVWatts query (now includes southern New Zealand)
- Updated PV Watts version from v6 to v8. PVWatts V8 updates the weather data to 2020 TMY data from the NREL NSRDB for locations covered by the database. (The NSRDB weather data used in PVWatts V6 is from around 2015.) See other differences at https://developer.nrel.gov/docs/solar/pvwatts/.
- Made PV struct mutable: This allows for assigning pv.production_factor_series when calling PVWatts for GHP, to avoid a extra PVWatts calls later.
- Changed unit test expected values due to update to PVWatts v8, which slightly changed expected PV production factors.
- Changed **fuel_avail_gal** default to 1e9 for on-grid scenarios (same as off-grid)
### Fixed
- Issue with using a leap year with a URDB rate - the URDB rate was creating energy_rate of length 8784 instead of intended 8760
- Don't double add adjustments to urdb rates with non-standard units
- Don't double add adjustments to urdb rates with non-standard units
- Corrected `Generator` **installed_cost_per_kw** from 500 to 650 if **only_runs_during_grid_outage** is _true_ or 800 if _false_
- Corrected `SteamTurbine` defaults population from `get_steam_turbine_defaults_size_class()`

## v0.32.3
### Fixed
Expand Down
1 change: 1 addition & 0 deletions src/REopt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export
avert_emissions_profiles,
cambium_emissions_profile,
easiur_data
get_existing_chiller_default_cop

import HTTP
import JSON
Expand Down
12 changes: 6 additions & 6 deletions src/core/generator.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,19 @@
"""
`Generator` is an optional REopt input with the following keys and default values:
```julia
only_runs_during_grid_outage::Bool = true,
existing_kw::Real = 0,
min_kw::Real = 0,
max_kw::Real = 1.0e6,
installed_cost_per_kw::Real = 500.0,
installed_cost_per_kw::Real = only_runs_during_grid_outage ? 650.0 : 800.0,
om_cost_per_kw::Real = off_grid_flag ? 20.0 : 10.0,
om_cost_per_kwh::Real = 0.0,
fuel_cost_per_gallon::Real = 3.0,
electric_efficiency_full_load::Real = 0.3233,
electric_efficiency_half_load::Real = electric_efficiency_full_load,
fuel_avail_gal::Real = off_grid_flag ? 1.0e9 : 660.0,
fuel_avail_gal::Real = 1.0e9,
fuel_higher_heating_value_kwh_per_gal::Real = 40.7,
min_turn_down_fraction::Real = off_grid_flag ? 0.15 : 0.0,
only_runs_during_grid_outage::Bool = true,
sells_energy_back_to_grid::Bool = false,
can_net_meter::Bool = false,
can_wholesale::Bool = false,
Expand Down Expand Up @@ -125,19 +125,19 @@ struct Generator <: AbstractGenerator
function Generator(;
off_grid_flag::Bool = false,
analysis_years::Int = 25,
only_runs_during_grid_outage::Bool = true,
existing_kw::Real = 0,
min_kw::Real = 0,
max_kw::Real = 1.0e6,
installed_cost_per_kw::Real = 500.0,
installed_cost_per_kw::Real = only_runs_during_grid_outage ? 650.0 : 800.0,
om_cost_per_kw::Real= off_grid_flag ? 20.0 : 10.0,
om_cost_per_kwh::Real = 0.0,
fuel_cost_per_gallon::Real = 3.0,
electric_efficiency_full_load::Real = 0.3233,
electric_efficiency_half_load::Real = electric_efficiency_full_load,
fuel_avail_gal::Real = off_grid_flag ? 1.0e9 : 660.0,
fuel_avail_gal::Real = 1.0e9,
fuel_higher_heating_value_kwh_per_gal::Real = KWH_PER_GAL_DIESEL,
min_turn_down_fraction::Real = off_grid_flag ? 0.15 : 0.0,
only_runs_during_grid_outage::Bool = true,
sells_energy_back_to_grid::Bool = false,
can_net_meter::Bool = false,
can_wholesale::Bool = false,
Expand Down
22 changes: 11 additions & 11 deletions src/core/heating_cooling_loads.jl
Original file line number Diff line number Diff line change
Expand Up @@ -209,23 +209,23 @@ end

"""
function get_existing_chiller_default_cop(; existing_chiller_max_thermal_factor_on_peak_load=nothing,
loads_kw=nothing,
loads_kw_thermal=nothing)
max_load_kw=nothing,
max_load_kw_thermal=nothing)
This function returns the default value for ExistingChiller.cop based on:
1. No information about load, returns average of lower and higher cop default values (`cop_unknown_thermal`)
2. If the cooling electric `loads_kw` is known, we first guess the thermal load profile using `cop_unknown_thermal`,
2. If the cooling electric `max_load_kw` is known, we first guess the thermal load profile using `cop_unknown_thermal`,
and then we use the default logic to determine the `existing_chiller_cop` based on the peak thermal load with a thermal factor multiplier.
3. If the cooling thermal `loads_kw_thermal` is known, same as 2. but we don't have to guess the cop to convert electric to thermal load first.
3. If the cooling thermal `max_load_kw_thermal` is known, same as 2. but we don't have to guess the cop to convert electric to thermal load first.
"""
function get_existing_chiller_default_cop(; existing_chiller_max_thermal_factor_on_peak_load=nothing, loads_kw=nothing, loads_kw_thermal=nothing)
function get_existing_chiller_default_cop(; existing_chiller_max_thermal_factor_on_peak_load=nothing, max_load_kw=nothing, max_load_kw_thermal=nothing)
cop_less_than_100_ton = 4.40
cop_more_than_100_ton = 4.69
cop_unknown_thermal = (cop_less_than_100_ton + cop_more_than_100_ton) / 2.0
max_cooling_load_ton = nothing
if !isnothing(loads_kw_thermal)
max_cooling_load_ton = maximum(loads_kw_thermal) / KWH_THERMAL_PER_TONHOUR
elseif !isnothing(loads_kw)
max_cooling_load_ton = maximum(loads_kw) / KWH_THERMAL_PER_TONHOUR * cop_unknown_thermal
if !isnothing(max_load_kw_thermal)
max_cooling_load_ton = max_load_kw_thermal / KWH_THERMAL_PER_TONHOUR
elseif !isnothing(max_load_kw)
max_cooling_load_ton = max_load_kw / KWH_THERMAL_PER_TONHOUR * cop_unknown_thermal
end
if isnothing(max_cooling_load_ton) || isnothing(existing_chiller_max_thermal_factor_on_peak_load)
return cop_unknown_thermal
Expand Down Expand Up @@ -366,7 +366,7 @@ struct CoolingLoad
elseif (!isempty(doe_reference_name) || !isempty(blended_doe_reference_names)) || isnothing(existing_chiller_cop)
# Generated loads_kw (electric) above based on the building's default fraction of electric profile
chiller_cop = get_existing_chiller_default_cop(;existing_chiller_max_thermal_factor_on_peak_load=existing_chiller_max_thermal_factor_on_peak_load,
loads_kw=loads_kw)
max_load_kw=maximum(loads_kw))
else
chiller_cop = existing_chiller_cop
end
Expand All @@ -376,7 +376,7 @@ struct CoolingLoad
# Now that cooling thermal loads_kw_thermal is known, update existing_chiller_cop if it was not input
if isnothing(existing_chiller_cop)
existing_chiller_cop = get_existing_chiller_default_cop(;existing_chiller_max_thermal_factor_on_peak_load=existing_chiller_max_thermal_factor_on_peak_load,
loads_kw_thermal=loads_kw_thermal)
max_load_kw_thermal=maximum(loads_kw_thermal))
end

if length(loads_kw_thermal) < 8760*time_steps_per_hour
Expand Down
8 changes: 4 additions & 4 deletions src/core/scenario.jl
Original file line number Diff line number Diff line change
Expand Up @@ -303,14 +303,14 @@ function Scenario(d::Dict; flex_hvac_from_json=false)
if haskey(d, "ExistingChiller")
if !haskey(d["ExistingChiller"], "cop")
d["ExistingChiller"]["cop"] = get_existing_chiller_default_cop(; existing_chiller_max_thermal_factor_on_peak_load=1.25,
loads_kw=nothing,
loads_kw_thermal=chiller_inputs[:loads_kw_thermal])
max_load_kw=nothing,
max_load_kw_thermal=maximum(chiller_inputs[:loads_kw_thermal]))
end
chiller_inputs = merge(chiller_inputs, dictkeys_tosymbols(d["ExistingChiller"]))
else
chiller_inputs[:cop] = get_existing_chiller_default_cop(; existing_chiller_max_thermal_factor_on_peak_load=1.25,
loads_kw=nothing,
loads_kw_thermal=chiller_inputs[:loads_kw_thermal])
max_load_kw=nothing,
max_load_kw_thermal=maximum(chiller_inputs[:loads_kw_thermal]))
end
existing_chiller = ExistingChiller(; chiller_inputs...)
end
Expand Down
6 changes: 3 additions & 3 deletions src/core/simulated_load.jl
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,9 @@ function simulated_load(d::Dict)
# Monthly loads (default is empty list)
monthly_totals_kwh = get(d, "monthly_totals_kwh", Real[])
if !isempty(monthly_totals_kwh)
if length(monthly_totals_kwh != 12)
if length(monthly_totals_kwh) != 12
throw(@error("monthly_totals_kwh must contain a value for each of the 12 months"))
end
end
bad_index = []
for (i, kwh) in enumerate(monthly_totals_kwh)
if isnothing(kwh)
Expand Down Expand Up @@ -398,7 +398,7 @@ function simulated_load(d::Dict)
# Monthly loads (default is empty list)
monthly_tonhour = get(d, "monthly_tonhour", Real[])
if !isempty(monthly_tonhour)
if length(monthly_tonhour != 12)
if length(monthly_tonhour) != 12
throw(@error("monthly_tonhour must contain a value for each of the 12 months"))
end
bad_index = []
Expand Down
4 changes: 3 additions & 1 deletion src/core/steam_turbine.jl
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,10 @@ function SteamTurbine(d::Dict; avg_boiler_fuel_load_mmbtu_per_hour::Union{Float6
)

# set all missing default values in custom_chp_inputs
defaults = get_steam_turbine_defaults_size_class(;avg_boiler_fuel_load_mmbtu_per_hour=avg_boiler_fuel_load_mmbtu_per_hour,
stm_defaults_response = get_steam_turbine_defaults_size_class(;avg_boiler_fuel_load_mmbtu_per_hour=avg_boiler_fuel_load_mmbtu_per_hour,
size_class=st.size_class)

defaults = stm_defaults_response["default_inputs"]
for (k, v) in custom_st_inputs
if isnan(v)
if !(k == :inlet_steam_temperature_degF && !isnan(st.inlet_steam_superheat_degF))
Expand Down
12 changes: 4 additions & 8 deletions src/core/urdb.jl
Original file line number Diff line number Diff line change
Expand Up @@ -293,14 +293,10 @@ function parse_urdb_energy_costs(d::Dict, year::Int; time_steps_per_hour=1, bigM
else
tier_use = tier
end
if non_kwh_units
rate = rate_average
total_rate = rate
else
rate = get(d["energyratestructure"][period][tier_use], "rate", 0)
total_rate = rate + get(d["energyratestructure"][period][tier_use], "adj", 0)
end

total_rate = non_kwh_units ?
rate_average :
(get(d["energyratestructure"][period][tier_use], "rate", 0) +
get(d["energyratestructure"][period][tier_use], "adj", 0))
sell = get(d["energyratestructure"][period][tier_use], "sell", 0)

for step in range(1, stop=time_steps_per_hour) # repeat hourly rates intrahour
Expand Down
4 changes: 2 additions & 2 deletions src/mpc/structs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ function MPCGenerator(;
fuel_cost_per_gallon::Real = 3.0,
electric_efficiency_full_load::Real = 0.3233,
electric_efficiency_half_load::Real = electric_efficiency_full_load,
fuel_avail_gal::Real = 660.0,
fuel_avail_gal::Real = 1.0e9,
fuel_higher_heating_value_kwh_per_gal::Real = KWH_PER_GAL_DIESEL,
min_turn_down_fraction::Real = 0.0, # TODO change this to non-zero value
only_runs_during_grid_outage::Bool = true,
Expand All @@ -310,7 +310,7 @@ struct MPCGenerator <: AbstractGenerator
fuel_cost_per_gallon::Real = 3.0,
electric_efficiency_full_load::Real = 0.3233,
electric_efficiency_half_load::Real = electric_efficiency_full_load,
fuel_avail_gal::Real = 660.0,
fuel_avail_gal::Real = 1.0e9,
fuel_higher_heating_value_kwh_per_gal::Real = KWH_PER_GAL_DIESEL,
min_turn_down_fraction::Real = 0.0, # TODO change this to non-zero value
only_runs_during_grid_outage::Bool = true,
Expand Down
2 changes: 2 additions & 0 deletions test/scenarios/backup_reliability_reopt_inputs.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
"federal_rebate_per_kw": 350.0
},
"Generator": {
"installed_cost_per_kw": 500.0,
"fuel_avail_gal": 660,
"min_kw": 200,
"max_kw": 200
},
Expand Down
1 change: 1 addition & 0 deletions test/scenarios/emissions.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"co2_from_avert": true
},
"Generator": {
"installed_cost_per_kw": 500.0,
"max_kw": 500.0,
"fuel_avail_gal": 125.0,
"min_turn_down_fraction": 0.0,
Expand Down
1 change: 1 addition & 0 deletions test/scenarios/generator.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"co2_from_avert": true
},
"Generator": {
"installed_cost_per_kw": 500.0,
"max_kw": 500.0,
"fuel_avail_gal": 125.0,
"min_turn_down_fraction": 0.0,
Expand Down
1 change: 1 addition & 0 deletions test/scenarios/mpc.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05]
},
"Generator": {
"fuel_avail_gal": 660,
"size_kw": 30,
"only_runs_during_grid_outage": false
},
Expand Down
2 changes: 2 additions & 0 deletions test/scenarios/nogridcost_minresilhours.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
"critical_load_fraction": 1
},
"Generator": {
"fuel_avail_gal": 660,
"installed_cost_per_kw": 500.0
},
"Financial": {
"value_of_lost_load_per_kwh": 0.001,
Expand Down
2 changes: 2 additions & 0 deletions test/scenarios/nogridcost_multiscenario.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
"critical_load_fraction": 0.1
},
"Generator": {
"fuel_avail_gal": 660,
"installed_cost_per_kw": 500.0,
"max_kw": 0.0
},
"Financial": {
Expand Down
2 changes: 2 additions & 0 deletions test/scenarios/outage.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
"co2_from_avert": true
},
"Generator": {
"fuel_avail_gal": 660,
"installed_cost_per_kw": 500.0,
"existing_kw": 0.0,
"min_turn_down_fraction": 0.0,
"only_runs_during_grid_outage": true,
Expand Down
2 changes: 1 addition & 1 deletion test/test_with_xpress.jl
Original file line number Diff line number Diff line change
Expand Up @@ -799,7 +799,7 @@ end
# When the user specifies inputs["ExistingChiller"]["cop"], this changes the **electric** consumption of the chiller to meet that cooling thermal load
crb_cop = REopt.get_existing_chiller_default_cop(;
existing_chiller_max_thermal_factor_on_peak_load=s.existing_chiller.max_thermal_factor_on_peak_load,
loads_kw_thermal=s.cooling_load.loads_kw_thermal)
max_load_kw_thermal=maximum(s.cooling_load.loads_kw_thermal))
cooling_thermal_load_tonhour_total = 1427329.0 * crb_cop / REopt.KWH_THERMAL_PER_TONHOUR # From CRB models, in heating_cooling_loads.jl, BuiltInCoolingLoad data for location (SanFrancisco Hospital)
cooling_electric_load_total_mod_cop_kwh = cooling_thermal_load_tonhour_total / inputs.s.existing_chiller.cop * REopt.KWH_THERMAL_PER_TONHOUR

Expand Down

0 comments on commit 2472ecb

Please sign in to comment.