Skip to content
Merged
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
4 changes: 2 additions & 2 deletions reo/src/load_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -763,8 +763,8 @@ def __init__(self, dfm=None, user_profile=None, pvs=[], critical_loads_kw=None,
self.time_steps_per_hour)

if bau_sustained_time_steps > 0: # include critical load in bau load for the time that it can be met
self.bau_load_list[outage_start_time_step:outage_start_time_step+bau_sustained_time_steps] = \
critical_loads_kw[outage_start_time_step:outage_start_time_step+bau_sustained_time_steps]
self.bau_load_list[outage_start_time_step - 1:outage_start_time_step + bau_sustained_time_steps - 1] = \
critical_loads_kw[outage_start_time_step - 1:outage_start_time_step + bau_sustained_time_steps - 1]

# resilience_check_flag: True if existing diesel and/or PV can sustain critical load during outage
self.outage_start_time_step = outage_start_time_step
Expand Down
11 changes: 11 additions & 0 deletions reo/tests/posts/critical_load_bau_can_sustain_part_outage.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"Scenario": {"add_soc_incentive": false,
"Site": {"latitude": 30.48157, "longitude": -86.50159,
"Financial": {"analysis_years": 20, "escalation_pct": 0.023, "offtaker_discount_pct": 0.083, "offtaker_tax_pct": 0.26, "om_cost_escalation_pct": 0.025},
"ElectricTariff": {"blended_annual_rates_us_dollars_per_kwh": 0.1, "blended_annual_demand_charges_us_dollars_per_kw": 0},
"LoadProfile": {"doe_reference_name": "FlatLoad", "annual_kwh": 8760000.0, "year": 2021, "loads_kw_is_net": false, "outage_is_major_event": true, "critical_load_pct": 0.8, "outage_start_time_step": 5196, "outage_end_time_step": 5244},
"PV": {"installed_cost_us_dollars_per_kw": 1600.0, "can_export_beyond_site_load": true, "can_curtail": true, "existing_kw": 0.0},
"Generator": {"installed_cost_us_dollars_per_kw": 1000.0, "generator_only_runs_during_grid_outage": true, "generator_fuel_escalation_pct": 0.027, "fuel_slope_gal_per_kwh": 0.076, "fuel_avail_gal": 1525.0, "min_turn_down_pct": 0.0, "existing_kw": 2000.0, "diesel_fuel_cost_us_dollars_per_gallon": 1.0, "om_cost_us_dollars_per_kw": 10.0, "om_cost_us_dollars_per_kwh": 0.01, "max_kw": 10000.0, "min_kw": 0}
}
}
}
63 changes: 61 additions & 2 deletions reo/tests/test_critical_load_bau.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
from reo.nested_to_flat_output import nested_to_flat
from django.test import TestCase
from reo.models import ModelManager
from reo.utilities import check_common_outputs
from reo.utilities import check_common_outputs, annuity


class CriticalLoadBAUTests(ResourceTestCaseMixin, TestCase):
Expand All @@ -46,6 +46,65 @@ def setUp(self):
def get_response(self, data):
return self.api_client.post(self.reopt_base, format='json', data=data)

def test_critical_load_bau_can_sustain_part_outage(self):
"""
Test scenario with
- outage_start_time_step: 5196
- outage_end_time_step: 5244
- existing diesel generator 2001 kW
- available fuel 1000000000 gallons
"""
test_post = os.path.join('reo', 'tests', 'posts', 'critical_load_bau_can_sustain_part_outage.json')
nested_data = json.load(open(test_post, 'rb'))

resp = self.get_response(data=nested_data)
self.assertHttpCreated(resp)
r = json.loads(resp.content)
run_uuid = r.get('run_uuid')
d = ModelManager.make_response(run_uuid=run_uuid)
c = nested_to_flat(d['outputs'])
c['resilience_check_flag'] = d['outputs']['Scenario']['Site']['LoadProfile']['resilience_check_flag']
c['bau_sustained_time_steps'] = d['outputs']['Scenario']['Site']['LoadProfile']['bau_sustained_time_steps']
c["fuel_used_gal_bau"] = round(d['outputs']['Scenario']['Site']['Generator']['fuel_used_gal_bau'])

d_expected = dict()
d_expected['status'] = 'optimal'

# calculate expected year one energy cost
energy_rate = nested_data['Scenario']['Site']['ElectricTariff']["blended_annual_rates_us_dollars_per_kwh"]
flat_load = nested_data['Scenario']['Site']['LoadProfile']["annual_kwh"] / 8760
outage_duration = (1 + nested_data['Scenario']['Site']['LoadProfile']["outage_end_time_step"] - nested_data['Scenario']['Site']['LoadProfile']["outage_start_time_step"])
d_expected['year_one_energy_cost_bau'] = energy_rate * flat_load * (8760 - outage_duration)

# calculate expected total energy cost
analysis_years = nested_data['Scenario']['Site']['Financial']["analysis_years"]
escalation_pct = nested_data['Scenario']['Site']['Financial']["escalation_pct"]
offtaker_discount_pct = nested_data['Scenario']['Site']['Financial']["offtaker_discount_pct"]
tax_fraction = 1 - nested_data['Scenario']['Site']['Financial']["offtaker_tax_pct"]
pwf_e = annuity(analysis_years, escalation_pct, offtaker_discount_pct)
d_expected["total_energy_cost_bau"] = round(pwf_e * flat_load * energy_rate * (8760 - outage_duration) * tax_fraction)

# calculate expected BAU outage performance
d_expected['resilience_check_flag'] = False
d_expected['bau_sustained_time_steps'] = int(nested_data['Scenario']['Site']['Generator']["fuel_avail_gal"] / (nested_data['Scenario']['Site']['Generator']["fuel_slope_gal_per_kwh"] * flat_load * nested_data['Scenario']['Site']['LoadProfile']["critical_load_pct"]))
d_expected["fuel_used_gal_bau"] = d_expected['bau_sustained_time_steps'] * (nested_data['Scenario']['Site']['Generator']["fuel_slope_gal_per_kwh"] * flat_load * nested_data['Scenario']['Site']['LoadProfile']["critical_load_pct"])

# calculate lcc components and lcc
bau_energy_cost = d['outputs']['Scenario']['Site']['ElectricTariff']["total_energy_cost_bau_us_dollars"]
bau_fixed_om_cost = d['outputs']['Scenario']['Site']['Generator']["existing_gen_total_fixed_om_cost_us_dollars"]
bau_var_om_cost = d['outputs']['Scenario']['Site']['Generator']["existing_gen_total_variable_om_cost_us_dollars"]
bau_fuel_used = d['outputs']['Scenario']['Site']['Generator']["fuel_used_gal_bau"]
pwf_gen_fuel = annuity(analysis_years, nested_data['Scenario']['Site']['Generator']["generator_fuel_escalation_pct"], offtaker_discount_pct)
bau_fuel_cost = tax_fraction*bau_fuel_used*nested_data['Scenario']['Site']['Generator']["diesel_fuel_cost_us_dollars_per_gallon"]*pwf_gen_fuel
d_expected['lcc_bau'] = round(bau_energy_cost + bau_fixed_om_cost + bau_var_om_cost + bau_fuel_cost)

try:
check_common_outputs(self, c, d_expected)
except:
print("Run {} expected outputs may have changed.".format(run_uuid))
print("Error message: {}".format(d['messages']['errors']))
raise

def test_critical_load_bau_can_sustain_outage(self):
"""
Test scenario with
Expand Down Expand Up @@ -95,5 +154,5 @@ def test_critical_load_bau_can_sustain_outage(self):
check_common_outputs(self, c, d_expected)
except:
print("Run {} expected outputs may have changed.".format(run_uuid))
print("Error message: {}".format(d['messages']))
print("Error message: {}".format(d['messages']['errors']))
raise