Skip to content
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

Prevent simultaneous charge/discharge plus other minor improvements #440

Open
wants to merge 53 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 52 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
b16eac0
add Logging to test/Project.toml
hdunham Aug 23, 2024
353085f
rm Xpress from test/Project.toml
hdunham Aug 23, 2024
b1f20bc
testing
hdunham Aug 23, 2024
757b08f
prevent elec stor simul charge/discharge
hdunham Aug 23, 2024
9daee88
testing
hdunham Aug 23, 2024
a76daa8
update testing
hdunham Aug 23, 2024
9a164b7
testing
hdunham Aug 23, 2024
6b89f17
testing
hdunham Aug 23, 2024
ffd919d
testing
hdunham Aug 23, 2024
50a0bf9
prevent simul charge/discharge in outages
hdunham Aug 27, 2024
a9891af
prevent simul charge/dis for thermal stor
hdunham Aug 27, 2024
0717319
testing
hdunham Aug 27, 2024
2b7008d
remove intermediate testing files
hdunham Aug 27, 2024
753c46a
Revert "rm Xpress from test/Project.toml"
hdunham Aug 29, 2024
f866abf
add missing possible keys to Scenario docstring
hdunham Aug 29, 2024
197e851
trying different locations that could be intl solar dataset
hdunham Sep 11, 2024
b576c34
handle CO2 emissions reduction fractions being nothing in value check
hdunham Sep 12, 2024
f9ceff5
debug failing off grid test
hdunham Sep 12, 2024
836202e
rm off grid debugging
hdunham Sep 12, 2024
417cd39
rm debugging changes
hdunham Sep 12, 2024
2b0371b
clean up wind and pv results processing
hdunham Sep 12, 2024
44f435d
use hours_per_time_step to simply equations
hdunham Sep 12, 2024
46b7f22
rm dupe loops of thermal stor constraints
hdunham Sep 12, 2024
7d6ff99
rename to add_storage_sum_grid_constraints
hdunham Sep 12, 2024
029674e
specify that PV capacity is kW DC in docstrings
hdunham Sep 12, 2024
e807431
improve comment in results/outages.jl
hdunham Sep 12, 2024
8572998
update simul charge/dis test
hdunham Sep 12, 2024
84ef7ca
change voll type to fix convert error
hdunham Sep 12, 2024
3bc3027
rm debugging
hdunham Sep 12, 2024
490e097
rm offgrid debug output files
hdunham Sep 12, 2024
4cecaaa
force critical_loads_kw to nothing when off grid
hdunham Sep 12, 2024
d8863c8
undo effic edit in pv results (will do all in another branch)
hdunham Sep 12, 2024
7876e20
Update CHANGELOG.md
hdunham Sep 12, 2024
f7b6e4d
comment out failing solar dataset test
hdunham Sep 12, 2024
7683c96
Revert "comment out failing solar dataset test"
hdunham Sep 13, 2024
d8fb2e2
replace cameroon with oulu; uncomment tests
hdunham Sep 16, 2024
71ebd60
Merge branch 'solar-dataset' into prevent-simul-charge-discharge
hdunham Sep 17, 2024
c6e1b06
Update CHANGELOG.md
hdunham Sep 17, 2024
36a6a6c
Merge branch 'develop' into prevent-simul-charge-discharge
hdunham Sep 25, 2024
e2aa523
add util function to wrap time steps larger than the max around year
hdunham Oct 1, 2024
22f6d9f
wrap around time steps that can be greater than max (outage model)
hdunham Oct 1, 2024
f4edfbb
Merge branch 'develop' into prevent-simul-charge-discharge
hdunham Oct 2, 2024
ee4a860
update changelog guidelines
hdunham Oct 2, 2024
973bb15
make outage in nogridcost_multiscenario test wrap around year
hdunham Oct 7, 2024
071b65e
Merge branch 'develop' into prevent-simul-charge-discharge
zolanaj Oct 8, 2024
1f78ab7
Update CHANGELOG.md
hdunham Oct 8, 2024
ee356ee
fix merge error
hdunham Oct 8, 2024
f34f0ef
rm temp comments from testing
hdunham Oct 8, 2024
8dc78f4
Merge branch 'outage_wrap' into prevent-simul-charge-discharge
hdunham Oct 8, 2024
097aa90
Update CHANGELOG.md
hdunham Oct 8, 2024
1f27348
rm dupe test "Outage with Generator"
hdunham Oct 8, 2024
45a58ca
rm dupe test "MPC"
hdunham Oct 8, 2024
2cb4b1d
Update Project.toml
zolanaj Oct 8, 2024
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
20 changes: 19 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Guidelines
- When making a Pull Request into `develop` start a new double-hash header for "Develop - YYYY-MM-DD"
- When working in feature branch, start a new double-hash header with the name of the branch and record changes under that
- When merging `develop` into a feature branch, keep the feature branch section and the "Develop" section separate to simplify merge conflicts
- When making a Pull Request into `develop`, merge the feature branch section into the "Develop" section (if it exists), else rename the feature branch header to "Develop"
- When making a Pull Request into `master` change "Develop" to the next version number

### Formatting
Expand All @@ -23,6 +25,22 @@ Classify the change according to the following categories:
### Deprecated
### Removed

## Develop #prevent-simul-charge-discharge
### Changed
- Replace all `1/p.s.settings.time_steps_per_hour` with `p.hours_per_time_step` for simplicity/consistency
- Rename function `add_storage_sum_constraints` to `add_storage_sum_grid_constraints` for clarity
### Added
- Constraints to prevent simultaneous charge/discharge of storage
- Specify in docstrings that **PV** **max_kw** and **size_kw** are kW-DC
- Add the Logging package to `test/Project.toml` because it is used in `runtests.jl`
### Fixed
- Force **ElectricLoad** **critical_load_kw** to be _nothing_ when **off_grid_flag** is _true_ (**critical_load_fraction** was already being forced to 1, but the user was still able to get around this by providing **critical_load_kw**)
- Removed looping over storage name in functions `add_hot_thermal_storage_dispatch_constraints` and `add_cold_thermal_storage_dispatch_constraints` because this loop is already done when calling these functions and storage name is passed in as argument `b`
- Remove extraneous line of code in `results/wind.jl`
- Change type of **value_of_lost_load** in **FinancialInputs** struct to fix convert error when user provides an _Int_
- Change international location in "Solar Dataset" test set from Cameroon to Oulu because the locations in the NSRDB have been expanded significantly so there is now an NSRDB point at Cameroon
- Handle edge case where the values of **outage_start_time_steps** and **outage_durations** makes an outage extend beyond the end of the year. The outage will now wrap around to the beginning of the year.

## v0.48.0
### Added
- Added new file `src/core/ASHP.jl` with new technology **ASHP**, which uses electricity as input and provides heating and/or cooling as output; load balancing and technology-specific constraints have been updated and added accordingly
Expand Down
18 changes: 13 additions & 5 deletions src/constraints/outage_constraints.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
function add_dv_UnservedLoad_constraints(m,p)
# Effective load balance
@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]
- sum( m[:dvMGRatedProduction][t, s, tz, ts] * (p.production_factor[t, tz+ts-1] + p.unavailability[t][tz+ts-1]) * p.levelization_factor[t]
m[:dvUnservedLoad][s, tz, ts] == p.s.electric_load.critical_loads_kw[time_step_wrap_around(tz+ts-1, time_steps_per_hour=p.s.settings.time_steps_per_hour)]
- sum( m[:dvMGRatedProduction][t, s, tz, ts] * (p.production_factor[t, time_step_wrap_around(tz+ts-1, time_steps_per_hour=p.s.settings.time_steps_per_hour)] + p.unavailability[t][time_step_wrap_around(tz+ts-1, time_steps_per_hour=p.s.settings.time_steps_per_hour)]) * p.levelization_factor[t]
- m[:dvMGProductionToStorage][t, s, tz, ts] - m[:dvMGCurtail][t, s, tz, ts]
for t in p.techs.elec
)
Expand All @@ -22,7 +22,7 @@ end

function add_outage_cost_constraints(m,p)
@constraint(m, [s in p.s.electric_utility.scenarios, tz in p.s.electric_utility.outage_start_time_steps],
m[:dvMaxOutageCost][s] >= p.pwf_e * sum(p.value_of_lost_load_per_kwh[tz+ts-1] * m[:dvUnservedLoad][s, tz, ts] for ts in 1:p.s.electric_utility.outage_durations[s])
m[:dvMaxOutageCost][s] >= p.pwf_e * sum(p.value_of_lost_load_per_kwh[time_step_wrap_around(tz+ts-1, time_steps_per_hour=p.s.settings.time_steps_per_hour)] * m[:dvUnservedLoad][s, tz, ts] for ts in 1:p.s.electric_utility.outage_durations[s])
)

@expression(m, ExpectedOutageCost,
Expand Down Expand Up @@ -116,7 +116,7 @@ function add_MG_production_constraints(m,p)
# Electrical production sent to storage or export must be less than technology's rated production
@constraint(m, [t in p.techs.elec, 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[:dvMGProductionToStorage][t, s, tz, ts] + m[:dvMGCurtail][t, s, tz, ts] <=
(p.production_factor[t, tz+ts-1] + p.unavailability[t][tz+ts-1]) * p.levelization_factor[t] * m[:dvMGRatedProduction][t, s, tz, ts]
(p.production_factor[t, time_step_wrap_around(tz+ts-1, time_steps_per_hour=p.s.settings.time_steps_per_hour)] + p.unavailability[t][time_step_wrap_around(tz+ts-1, time_steps_per_hour=p.s.settings.time_steps_per_hour)]) * p.levelization_factor[t] * m[:dvMGRatedProduction][t, s, tz, ts]
)
Comment on lines -119 to 120
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the wraparound function is required for data from the initial problem (e.g., production factors and site load) and NOT required for the microgrid state of charge because that's only using the starting time period SOC from the full-year problem and then using the (tz,ts) tuple instead of an "absolute-value" time step that could otherwise exceed 8760. Is that right?

I spot-checked a case and the m[:dvStorageEnergy"] reconciles correctly for the long-duration case in the testset - just making sure I understand.


@constraint(m, [t in p.techs.elec, 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],
Expand All @@ -138,7 +138,7 @@ function add_MG_Gen_fuel_burn_constraints(m,p)
# Define dvMGFuelUsed by summing over outage time_steps.
@constraint(m, [t in p.techs.gen, s in p.s.electric_utility.scenarios, tz in p.s.electric_utility.outage_start_time_steps],
m[:dvMGFuelUsed][t, s, tz] == fuel_slope_gal_per_kwhe * p.hours_per_time_step * p.levelization_factor[t] *
sum( (p.production_factor[t, tz+ts-1] + p.unavailability[t][tz+ts-1]) * m[:dvMGRatedProduction][t, s, tz, ts] for ts in 1:p.s.electric_utility.outage_durations[s])
sum( (p.production_factor[t, time_step_wrap_around(tz+ts-1, time_steps_per_hour=p.s.settings.time_steps_per_hour)] + p.unavailability[t][time_step_wrap_around(tz+ts-1, time_steps_per_hour=p.s.settings.time_steps_per_hour)]) * m[:dvMGRatedProduction][t, s, tz, ts] for ts in 1:p.s.electric_utility.outage_durations[s])
+ fuel_intercept_gal_per_hr * p.hours_per_time_step *
sum( m[:binMGGenIsOnInTS][s, tz, ts] for ts in 1:p.s.electric_utility.outage_durations[s])
)
Expand Down Expand Up @@ -287,6 +287,14 @@ function add_MG_storage_dispatch_constraints(m,p)
)
)

# Prevent simultaneous charge and discharge by limitting charging alone to not make the SOC exceed 100%
@constraint(m, [ts in p.time_steps_without_grid],
m[:dvStorageEnergy]["ElectricStorage"] >= m[:dvMGStoredEnergy][s, tz, ts-1] + p.hours_per_time_step * (
p.s.storage.attr["ElectricStorage"].charge_efficiency * sum(m[:dvMGProductionToStorage][t, s, tz, ts] for t in p.techs.elec)
)
)

# Min SOC
if p.s.storage.attr["ElectricStorage"].soc_min_applies_during_outages
# Minimum state of charge
@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],
Expand Down
70 changes: 50 additions & 20 deletions src/constraints/storage_constraints.jl
Original file line number Diff line number Diff line change
Expand Up @@ -50,21 +50,35 @@ end

function add_elec_storage_dispatch_constraints(m, p, b; _n="")

# Constraint (4g): state-of-charge for electrical storage - with grid
# Constraint (4g)-1: state-of-charge for electrical storage - with grid
@constraint(m, [ts in p.time_steps_with_grid],
m[Symbol("dvStoredEnergy"*_n)][b, ts] == m[Symbol("dvStoredEnergy"*_n)][b, ts-1] + p.hours_per_time_step * (
sum(p.s.storage.attr[b].charge_efficiency * m[Symbol("dvProductionToStorage"*_n)][b, t, ts] for t in p.techs.elec)
+ p.s.storage.attr[b].grid_charge_efficiency * m[Symbol("dvGridToStorage"*_n)][b, ts]
- m[Symbol("dvDischargeFromStorage"*_n)][b,ts] / p.s.storage.attr[b].discharge_efficiency
)
)

# Constraint (4h): state-of-charge for electrical storage - no grid
# Constraint (4g)-2: state-of-charge for electrical storage - no grid
@constraint(m, [ts in p.time_steps_without_grid],
m[Symbol("dvStoredEnergy"*_n)][b, ts] == m[Symbol("dvStoredEnergy"*_n)][b, ts-1] + p.hours_per_time_step * (
sum(p.s.storage.attr[b].charge_efficiency * m[Symbol("dvProductionToStorage"*_n)][b,t,ts] for t in p.techs.elec)
- m[Symbol("dvDischargeFromStorage"*_n)][b, ts] / p.s.storage.attr[b].discharge_efficiency
)
)

# Constraint (4h): prevent simultaneous charge and discharge by limitting charging alone to not make the SOC exceed 100%
# (4h)-1: with grid
@constraint(m, [ts in p.time_steps_with_grid],
m[Symbol("dvStorageEnergy"*_n)][b] >= m[Symbol("dvStoredEnergy"*_n)][b, ts-1] + p.hours_per_time_step * (
sum(p.s.storage.attr[b].charge_efficiency * m[Symbol("dvProductionToStorage"*_n)][b, t, ts] for t in p.techs.elec)
+ p.s.storage.attr[b].grid_charge_efficiency * m[Symbol("dvGridToStorage"*_n)][b, ts]
)
)
# (4h)-2: no grid
@constraint(m, [ts in p.time_steps_without_grid],
m[Symbol("dvStorageEnergy"*_n)][b] >= m[Symbol("dvStoredEnergy"*_n)][b, ts-1] + p.hours_per_time_step * (
sum(p.s.storage.attr[b].charge_efficiency * m[Symbol("dvProductionToStorage"*_n)][b,t,ts] for t in p.techs.elec)
)
)

# Constraint (4i)-1: Dispatch to electrical storage is no greater than power capacity
Expand Down Expand Up @@ -104,16 +118,24 @@ end
function add_hot_thermal_storage_dispatch_constraints(m, p, b; _n="")

# Constraint (4j)-1: Reconcile state-of-charge for (hot) thermal storage
@constraint(m, [b in p.s.storage.types.hot, ts in p.time_steps],
m[Symbol("dvStoredEnergy"*_n)][b,ts] == m[Symbol("dvStoredEnergy"*_n)][b,ts-1] + (1/p.s.settings.time_steps_per_hour) * (
p.s.storage.attr[b].charge_efficiency * sum(m[Symbol("dvHeatToStorage"*_n)][b,t,q,ts] for t in union(p.techs.heating, p.techs.chp), q in p.heating_loads) -
sum(m[Symbol("dvHeatFromStorage"*_n)][b,q,ts] for q in p.heating_loads) / p.s.storage.attr[b].discharge_efficiency -
p.s.storage.attr[b].thermal_decay_rate_fraction * m[Symbol("dvStorageEnergy"*_n)][b]
@constraint(m, [ts in p.time_steps],
m[Symbol("dvStoredEnergy"*_n)][b,ts] == m[Symbol("dvStoredEnergy"*_n)][b,ts-1] + p.hours_per_time_step * (
p.s.storage.attr[b].charge_efficiency * sum(m[Symbol("dvHeatToStorage"*_n)][b,t,q,ts] for t in union(p.techs.heating, p.techs.chp), q in p.heating_loads) -
sum(m[Symbol("dvHeatFromStorage"*_n)][b,q,ts] for q in p.heating_loads) / p.s.storage.attr[b].discharge_efficiency -
p.s.storage.attr[b].thermal_decay_rate_fraction * m[Symbol("dvStorageEnergy"*_n)][b]
)
)

# Prevent simultaneous charge and discharge by limitting charging alone to not make the SOC exceed 100%
@constraint(m, [ts in p.time_steps],
m[Symbol("dvStorageEnergy"*_n)][b] >= m[Symbol("dvStoredEnergy"*_n)][b,ts-1] + p.hours_per_time_step * (
p.s.storage.attr[b].charge_efficiency * sum(m[Symbol("dvHeatToStorage"*_n)][b,t,q,ts] for t in union(p.techs.heating, p.techs.chp), q in p.heating_loads)
- p.s.storage.attr[b].thermal_decay_rate_fraction * m[Symbol("dvStorageEnergy"*_n)][b]
)
)

#Constraint (4n)-1: Dispatch to and from thermal storage is no greater than power capacity
@constraint(m, [b in p.s.storage.types.hot, ts in p.time_steps],
@constraint(m, [ts in p.time_steps],
m[Symbol("dvStoragePower"*_n)][b] >=
sum(m[Symbol("dvHeatFromStorage"*_n)][b,q,ts] +
sum(m[Symbol("dvHeatToStorage"*_n)][b,t,q,ts] for t in union(p.techs.heating, p.techs.chp))
Expand All @@ -122,14 +144,14 @@ function add_hot_thermal_storage_dispatch_constraints(m, p, b; _n="")
# TODO missing thermal storage constraints from API ???

# Constraint (4o): Discharge from storage is equal to sum of heat from storage for all qualities
@constraint(m, HeatDischargeReconciliation[b in p.s.storage.types.hot, ts in p.time_steps],
@constraint(m, HeatDischargeReconciliation[ts in p.time_steps],
m[Symbol("dvDischargeFromStorage"*_n)][b,ts] ==
sum(m[Symbol("dvHeatFromStorage"*_n)][b,q,ts] for q in p.heating_loads)
)

#Do not allow GHP to charge storage
if !isempty(p.techs.ghp)
for b in p.s.storage.types.hot, t in p.techs.ghp, q in p.heating_loads, ts in p.time_steps
for t in p.techs.ghp, q in p.heating_loads, ts in p.time_steps
fix(m[Symbol("dvHeatToStorage"*_n)][b,t,q,ts], 0.0, force=true)
end
end
Expand All @@ -140,36 +162,44 @@ function add_cold_thermal_storage_dispatch_constraints(m, p, b; _n="")

# Constraint (4f)-2: (Cold) Thermal production sent to storage or grid must be less than technology's rated production
if !isempty(p.techs.cooling)
@constraint(m, CoolingTechProductionFlowCon[b in p.s.storage.types.cold, t in p.techs.cooling, ts in p.time_steps],
@constraint(m, CoolingTechProductionFlowCon[t in p.techs.cooling, ts in p.time_steps],
m[Symbol("dvProductionToStorage"*_n)][b,t,ts] <=
m[Symbol("dvCoolingProduction"*_n)][t,ts]
)
end

# Constraint (4j)-2: Reconcile state-of-charge for (cold) thermal storage
@constraint(m, ColdTESInventoryCon[b in p.s.storage.types.cold, ts in p.time_steps],
m[Symbol("dvStoredEnergy"*_n)][b,ts] == m[Symbol("dvStoredEnergy"*_n)][b,ts-1] + (1/p.s.settings.time_steps_per_hour) * (
sum(p.s.storage.attr[b].charge_efficiency * m[Symbol("dvProductionToStorage"*_n)][b,t,ts] for t in p.techs.cooling) -
m[Symbol("dvDischargeFromStorage"*_n)][b,ts]/p.s.storage.attr[b].discharge_efficiency -
p.s.storage.attr[b].thermal_decay_rate_fraction * m[Symbol("dvStorageEnergy"*_n)][b]
@constraint(m, ColdTESInventoryCon[ts in p.time_steps],
m[Symbol("dvStoredEnergy"*_n)][b,ts] == m[Symbol("dvStoredEnergy"*_n)][b,ts-1] + p.hours_per_time_step * (
sum(p.s.storage.attr[b].charge_efficiency * m[Symbol("dvProductionToStorage"*_n)][b,t,ts] for t in p.techs.cooling) -
m[Symbol("dvDischargeFromStorage"*_n)][b,ts]/p.s.storage.attr[b].discharge_efficiency -
p.s.storage.attr[b].thermal_decay_rate_fraction * m[Symbol("dvStorageEnergy"*_n)][b]
)
)

# Prevent simultaneous charge and discharge by limitting charging alone to not make the SOC exceed 100%
@constraint(m, [ts in p.time_steps],
m[Symbol("dvStorageEnergy"*_n)][b] >= m[Symbol("dvStoredEnergy"*_n)][b,ts-1] + p.hours_per_time_step * (
p.s.storage.attr[b].charge_efficiency * sum(m[Symbol("dvProductionToStorage"*_n)][b,t,ts] for t in p.techs.cooling)
- p.s.storage.attr[b].thermal_decay_rate_fraction * m[Symbol("dvStorageEnergy"*_n)][b]
)
)

#Constraint (4n)-2: Dispatch to and from thermal storage is no greater than power capacity
@constraint(m, [b in p.s.storage.types.cold, ts in p.time_steps],
@constraint(m, [ts in p.time_steps],
m[Symbol("dvStoragePower"*_n)][b] >= m[Symbol("dvDischargeFromStorage"*_n)][b,ts] +
sum(m[Symbol("dvProductionToStorage"*_n)][b,t,ts] for t in p.techs.cooling)
)

#Do not allow GHP to charge storage
if !isempty(p.techs.ghp)
for b in p.s.storage.types.cold, t in p.techs.ghp, ts in p.time_steps
for t in p.techs.ghp, ts in p.time_steps
fix(m[Symbol("dvProductionToStorage"*_n)][b,t,ts], 0.0, force=true)
end
end
end

function add_storage_sum_constraints(m, p; _n="")
function add_storage_sum_grid_constraints(m, p; _n="")

##Constraint (8c): Grid-to-storage no greater than grid purchases
@constraint(m, [ts in p.time_steps_with_grid],
Expand Down
2 changes: 1 addition & 1 deletion src/constraints/thermal_tech_constraints.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function add_boiler_tech_constraints(m, p; _n="")
)
if "Boiler" in p.techs.boiler # ExistingBoiler does not have om_cost_per_kwh
m[:TotalBoilerPerUnitProdOMCosts] = @expression(m, p.third_party_factor * p.pwf_om *
sum(p.s.boiler.om_cost_per_kwh / p.s.settings.time_steps_per_hour *
sum(p.s.boiler.om_cost_per_kwh * p.hours_per_time_step *
m[Symbol("dvHeatingProduction"*_n)]["Boiler",q,ts] for q in p.heating_loads, ts in p.time_steps)
)
else
Expand Down
2 changes: 1 addition & 1 deletion src/core/bau_inputs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ function setup_bau_emissions_inputs(p::REoptInputs, s_bau::BAUScenario, generato
bau_grid_to_load = [max(i,0) for i in bau_grid_to_load]
end

bau_grid_emissions_lb_CO2_per_year = sum(p.s.electric_utility.emissions_factor_series_lb_CO2_per_kwh .* bau_grid_to_load) / p.s.settings.time_steps_per_hour
bau_grid_emissions_lb_CO2_per_year = sum(p.s.electric_utility.emissions_factor_series_lb_CO2_per_kwh .* bau_grid_to_load) * p.hours_per_time_step
bau_emissions_lb_CO2_per_year += bau_grid_emissions_lb_CO2_per_year

## Generator emissions (during outages)
Expand Down
12 changes: 9 additions & 3 deletions src/core/electric_load.jl
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,15 @@ mutable struct ElectricLoad # mutable to adjust (critical_)loads_kw based off o
min_load_met_annual_fraction::Real = off_grid_flag ? 0.99999 : 1.0 # if off grid, 99.999%, else must be 100%. Applied to each time_step as a % of electric load.
)

if off_grid_flag && !(critical_load_fraction == 1.0)
@warn "ElectricLoad critical_load_fraction must be 1.0 (100%) for off-grid scenarios. Any other value will be overriden when `off_grid_flag` is true. If you wish to alter the load profile or load met, adjust the loads_kw or min_load_met_annual_fraction."
critical_load_fraction = 1.0
if off_grid_flag
if !isnothing(critical_loads_kw)
@warn "ElectricLoad critical_loads_kw will be ignored because `off_grid_flag` is true. If you wish to alter the load profile or load met, adjust the loads_kw or min_load_met_annual_fraction."
critical_loads_kw = nothing
end
if critical_load_fraction != 1.0
@warn "ElectricLoad critical_load_fraction must be 1.0 (100%) for off-grid scenarios. Any other value will be overriden when `off_grid_flag` is true. If you wish to alter the load profile or load met, adjust the loads_kw or min_load_met_annual_fraction."
critical_load_fraction = 1.0
end
end

if !(off_grid_flag)
Expand Down
2 changes: 1 addition & 1 deletion src/core/financial.jl
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ struct Financial
owner_tax_rate_fraction::Float64
owner_discount_rate_fraction::Float64
analysis_years::Int
value_of_lost_load_per_kwh::Union{Array{Float64,1}, Float64}
value_of_lost_load_per_kwh::Union{Array{<:Real,1}, Real}
microgrid_upgrade_cost_fraction::Float64
macrs_five_year::Array{Float64,1}
macrs_seven_year::Array{Float64,1}
Expand Down
2 changes: 1 addition & 1 deletion src/core/pv.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
location::String="both", # one of ["roof", "ground", "both"]
existing_kw::Real=0,
min_kw::Real=0,
max_kw::Real=1.0e9, # max new capacity (beyond existing_kw)
max_kw::Real=1.0e9, # max new DC capacity (beyond existing_kw)
installed_cost_per_kw::Real=1790.0,
om_cost_per_kw::Real=18.0,
degradation_fraction::Real=0.005,
Expand Down
Loading
Loading