Skip to content

Commit

Permalink
derConsumer & derUtilityCost: Removed all outage, resilience, and cri…
Browse files Browse the repository at this point in the history
…tical load components
  • Loading branch information
astronobri committed Nov 7, 2024
1 parent 319a4cc commit 2c29a4a
Show file tree
Hide file tree
Showing 4 changed files with 14 additions and 274 deletions.
42 changes: 0 additions & 42 deletions omf/models/derConsumer.html
Original file line number Diff line number Diff line change
Expand Up @@ -101,42 +101,6 @@
<option value="No" {{ 'selected' if allInputDataDict.generator == 'No' }}>No</option>
</select>
</div>
<!-- TODO: Create switch for outages (if outage, display outage options, otherwise dont)-->
<div class="shortInput">
<label class="tooltip">Simulate Outage<span class="classic">Run an outage simulation with the following start hour and duration.</span></label>
<select id="outage" name="outage" value="{{allInputDataDict.outage}}">
<option value="True" {{ 'selected' if allInputDataDict.outage == 'Yes' }}>Yes</option>
<option value="False" {{ 'selected' if allInputDataDict.outage == 'No' }}>No</option>
</select>
</div>
<div class="shortInput">
<label class="tooltip">Outage Start Hour<span class="classic">The starting hour of the outage. Specify an hour greater than 0 but less than 8760 to trigger optimization for resilience.</span></label>
<input type="number" id="outage_start_hour" name="outage_start_hour" value="{{allInputDataDict.outage_start_hour}}" min="0" max="8760" required="required"/>
</div>
<div class="shortInput">
<label class="tooltip">Outage Duration (hours)<span class="classic">The duration (in hours) of the outage. Specify a number between 1 - 8760 hours. </span></label>
<input type="number" id="outage_duration" name="outage_duration" value="{{allInputDataDict.outage_duration}}" min="1" max="8760" required="required"/>
</div>
<div class="shortInput">
<label class="tooltip">Critical Load Curve (.csv file)<span class="classic">Critical load profile (.csv) to be used in the outage analysis. Length 8760 in kW units. If critical load factor is given, this file will not be used.</span></label>
<input id="criticalLoadFile" type="file" style="display:none" onchange="handle_files(this.files,'criticalLoad','criticalLoadFileName')">
<input id="criticalLoad" name="criticalLoad" value="{{allInputDataDict.criticalLoad}}" type="hidden">
<div>
<label for="criticalLoadFile" class="fileButton">Choose File</label>
<input id="criticalLoadFileName" name="criticalLoadFileName" value="{{allInputDataDict.criticalLoadFileName}}" value='' readonly class="uploadFileName">
</div>
</div>
<div class="shortInput">
<label class="tooltip">Use Critical Load Factor instead?<span class="classic">Select Yes to use Critical Load Factor instead of the provided Critical Load .csv file.</span></label>
<select id="criticalLoadSwitch" name="criticalLoadSwitch" value="{{allInputDataDict.criticalLoadSwitch}}">
<option value="True" {{ 'selected' if allInputDataDict.outage == 'Yes' }}>Yes</option>
<option value="False" {{ 'selected' if allInputDataDict.outage == 'No' }}>No</option>
</select>
</div>
<div class="shortInput">
<label class="tooltip">Critical Load Factor<span class="classic">The fractional percentage of the demand curve representing the critical load that must be met during an outage. Format 0.XX (e.g. 0.50 signifies 50% of the daily demand is the critical load).</span></label>
<input type="text" id="criticalLoadFactor" name="criticalLoadFactor" value="{{allInputDataDict.criticalLoadFactor}}" min="0" max="1" pattern="^\d+\.?\d*?$" required="required"/>
</div>

<!-- Financial Inputs -->
<div class="wideInput">
Expand Down Expand Up @@ -305,12 +269,6 @@
<p class="reportTitle" style="page-break-before:always">Exported Power Overview (using REopt)</p>
<div id="exportedPowerData" class="tightContent" style="width: 1000px; height: 600px;"></div>

<p class="reportTitle" style="page-break-before:always">Resilience Overview</p>
<div id="resiliencePlotly" class="tightContent"></div>

<p class="reportTitle" style="page-break-before:always">Outage Survival Probability</p>
<div id="resilienceProbPlotly" class="tightContent"></div>

<p class="reportTitle">Monthly Cost Comparison</p>
<div id="levelizedCostReport" class="tightContent">
<div id="levelizedCostTableDiv" style="display:inline-block; width:1000px">
Expand Down
101 changes: 3 additions & 98 deletions omf/models/derConsumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,8 @@ def create_REopt_jl_jsonFile(modelDir, inputDict):
scenario['ElectricStorage'] = {
##TODO: Add options here, if needed
#scenario['ElectricStorage']['size_kw'] = 2
#"min_kw": 2,
#"min_kwh": 8,
"min_kw": 2,
"min_kwh": 8,
'total_rebate_per_kw': float(inputDict['total_rebate_per_kw']),
'macrs_option_years': float(inputDict['macrs_option_years']),
'macrs_bonus_fraction': float(inputDict['macrs_bonus_fraction']),
Expand All @@ -199,25 +199,6 @@ def create_REopt_jl_jsonFile(modelDir, inputDict):
scenario['Generator'] = {
##TODO: Add options here, if needed
}

## Add an Outage section if enabled
if inputDict['outage'] == True:
scenario['ElectricUtility'] = {
'outage_start_time_step': int(inputDict['outage_start_hour']),
'outage_end_time_step': int(inputDict['outage_start_hour'])+int(inputDict['outage_duration'])
}

## Critical Load input section
if inputDict['criticalLoadSwitch'] == 'No': ## switch = No means the user wants to upload a critical load profile instead of using a critical load factor to determine the critical load
#TODO: This piece is not working. REopt gives an error that the resilience file is not being created. Unclear what the issue is 9/2024
criticalLoad = np.asarray([float(value) for value in inputDict['criticalLoad'].split('\n') if value.strip()]) ## process input format into an array
criticalLoad = criticalLoad.tolist() if isinstance(criticalLoad, np.ndarray) else criticalLoad ## make criticalLoad array into a list for REopt
#scenario['ElectricLoad']['critical_load_series_kw'] = criticalLoad
else:
scenario['ElectricLoad']['critical_load_fraction'] = float(inputDict['criticalLoadFactor'])
#criticalLoad = demand_array*criticalLoadFactor
#criticalLoad = criticalLoad.tolist() if isinstance(criticalLoad, np.ndarray) else criticalLoad ## make criticalLoad array into a list for REopt
#scenario['ElectricLoad']['critical_load_series_kw'] = criticalLoad

## Save the scenario file
## NOTE: reopt_jl currently requires a path for the input file, so the file must be saved to a location
Expand Down Expand Up @@ -380,7 +361,7 @@ def work(modelDir, inputDict):
# print('Successfully loaded REopt test file. \n')

## Run REopt.jl
reopt_jl.run_reopt_jl(modelDir, 'reopt_input_scenario.json', outages=inputDict['outage'])
reopt_jl.run_reopt_jl(modelDir, 'reopt_input_scenario.json')
#reopt_jl.run_reopt_jl(modelDir, '/Users/astronobri/Documents/CIDER/scratch/reopt_input_scenario_26may2024_0258.json', outages=inputDict['outage'])
with open(pJoin(modelDir, 'results.json')) as jsonFile:
reoptResults = json.load(jsonFile)
Expand All @@ -397,17 +378,6 @@ def work(modelDir, inputDict):
year = inputDict['year'] ## Use the user provided year if none found in reoptResults
timestamps = pd.date_range(start=f'{year}-01-01', end=f'{year}-12-31 23:00:00', periods=np.size(demand))

## If outage is specified in the inputs, load the resilience results
if (inputDict['outage']):
try:
with open(pJoin(modelDir, 'resultsResilience.json')) as jsonFile:
reoptResultsResilience = json.load(jsonFile)
outData.update(reoptResultsResilience) ## Update out file with resilience results
except FileNotFoundError:
results_file = pJoin(modelDir, 'resultsResilience.json')
print(f"File '{results_file}' not found. REopt may not have simulated the outage.")
raise

## Run vbatDispatch, unless it is disabled
## TODO: Check that the rest of the code functions if the vbat (TESS) load type is None
if inputDict['load_type'] != '0': ## Load type 0 corresponds to the "None" option, which disables this vbatDispatch function
Expand Down Expand Up @@ -601,68 +571,6 @@ def work(modelDir, inputDict):
outData['derOverviewData'] = json.dumps(fig.data, cls=plotly.utils.PlotlyJSONEncoder)
outData['derOverviewLayout'] = json.dumps(fig.layout, cls=plotly.utils.PlotlyJSONEncoder)

## Add REopt resilience plot (adapted from omf/models/microgridDesign.py) ########################################################################################################################
#helper function for generating output graphs
def makeGridLine(x,y,color,name):
plotLine = go.Scatter(
x = x,
y = y,
line = dict( color=(color)),
name = name,
hoverlabel = dict(namelength = -1),
showlegend=True,
stackgroup='one',
mode='none'
)
return plotLine
#Set plotly layout ---------------------------------------------------------------
plotlyLayout = go.Layout(
width=1000,
height=375,
legend=dict(
x=0,
y=1.25,
orientation="h")
)
x = list(range(len(reoptResults['ElectricUtility']['electric_to_load_series_kw'])))
plotData = []
#x_values = pd.to_datetime(x, unit = 'h', origin = pd.Timestamp(f'{year}-01-01'))
x_values = timestamps
powerGridToLoad = makeGridLine(x_values,reoptResults['ElectricUtility']['electric_to_load_series_kw'],'blue','Load met by Grid')
plotData.append(powerGridToLoad)

if (inputDict['outage']): ## TODO: condense this code if possible
outData['resilience'] = reoptResultsResilience['resilience_by_time_step']
outData['minOutage'] = reoptResultsResilience['resilience_hours_min']
outData['maxOutage'] = reoptResultsResilience['resilience_hours_max']
outData['avgOutage'] = reoptResultsResilience['resilience_hours_avg']
outData['survivalProbX'] = reoptResultsResilience['outage_durations']
outData['survivalProbY'] = reoptResultsResilience['probs_of_surviving']

plotData = []
resilience = go.Scatter(
x=x,
y=outData['resilience'],
line=dict( color=('red') ),
)
plotData.append(resilience)
plotlyLayout['yaxis'].update(title='Longest Outage survived (Hours)')
plotlyLayout['xaxis'].update(title='Start Hour')
outData['resilienceData'] = json.dumps(plotData, cls=plotly.utils.PlotlyJSONEncoder)
outData['resilienceLayout'] = json.dumps(plotlyLayout, cls=plotly.utils.PlotlyJSONEncoder)

plotData = []
survivalProb = go.Scatter(
x=outData['survivalProbX'],
y=outData['survivalProbY'],
line=dict( color=('red') ),
name='Probability of Surviving Outage of a Given Duration')
plotData.append(survivalProb)
plotlyLayout['yaxis'].update(title='Probability of Meeting Critical Load')
plotlyLayout['xaxis'].update(title='Outage Length (Hours)')
outData['resilienceProbData' ] = json.dumps(plotData, cls=plotly.utils.PlotlyJSONEncoder)
outData['resilienceProbLayout'] = json.dumps(plotlyLayout, cls=plotly.utils.PlotlyJSONEncoder)


## Create Exported Power plot object ######################################################################################################################################################
fig = go.Figure()
Expand Down Expand Up @@ -859,9 +767,6 @@ def new(modelDir):
'PV': 'Yes',
'BESS': 'Yes',
'generator': 'No',
'outage': True,
'outage_start_hour': '4637',
'outage_duration': '3',

## Financial Inputs
'demandChargeURDB': 'Yes',
Expand Down
54 changes: 3 additions & 51 deletions omf/models/derUtilityCost.html
Original file line number Diff line number Diff line change
Expand Up @@ -99,43 +99,6 @@
</select>
</div>

<!-- TODO: Create switch for outages (if outage, display outage options, otherwise dont)-->
<div class="shortInput">
<label>Simulate Outage</label>
<select id="outage" name="outage" value="{{allInputDataDict.outage}}">
<option value="True" {{ 'selected' if allInputDataDict.outage == 'Yes' }}>Yes</option>
<option value="False" {{ 'selected' if allInputDataDict.outage == 'No' }}>No</option>
</select>
</div>
<div class="shortInput">
<label class="tooltip">Outage Start Hour<span class="classic">The starting hour of the outage. Specify an hour greater than 0 but less than 8760 to trigger optimization for resilience. If 0 is entered, the model will run an economic optimization without fossil generation.</span></label>
<input type="number" id="outage_start_hour" name="outage_start_hour" value="{{allInputDataDict.outage_start_hour}}" min="0" max="8760" required="required"/>
</div>
<div class="shortInput">
<label class="tooltip">Outage Duration (hours)<span class="classic">The duration (in hours) of the outage. Specify a number between 1 - 8760 hours. </span></label>
<input type="number" id="outage_duration" name="outage_duration" value="{{allInputDataDict.outage_duration}}" min="1" max="8760" required="required"/>
</div>
<div class="shortInput">
<label class="tooltip">Critical Load Curve (.csv file)<span class="classic">Critical load profile (.csv) to be used in the outage analysis. Length 8760 in kW units. If critical load factor is given, this file will not be used.</span></label>
<input id="criticalLoadFile" type="file" style="display:none" onchange="handle_files(this.files,'criticalLoad','criticalLoadFileName')">
<input id="criticalLoad" name="criticalLoad" value="{{allInputDataDict.criticalLoad}}" type="hidden">
<div>
<label for="criticalLoadFile" class="fileButton">Choose File</label>
<input id="criticalLoadFileName" name="criticalLoadFileName" value="{{allInputDataDict.criticalLoadFileName}}" value='' readonly class="uploadFileName">
</div>
</div>
<div class="shortInput">
<label class="tooltip">Use Critical Load Factor instead?<span class="classic">Select Yes to use Critical Load Factor instead of the provided Critical Load .csv file.</span></label>
<select id="criticalLoadSwitch" name="criticalLoadSwitch" value="{{allInputDataDict.criticalLoadSwitch}}">
<option value="True" {{ 'selected' if allInputDataDict.outage == 'Yes' }}>Yes</option>
<option value="False" {{ 'selected' if allInputDataDict.outage == 'No' }}>No</option>
</select>
</div>
<div class="shortInput">
<label class="tooltip">Critical Load Factor<span class="classic">The fractional percentage of the demand curve representing the critical load that must be met during an outage. Format 0.XX (e.g. 0.50 signifies 50% of the daily demand is the critical load).</span></label>
<input type="text" id="criticalLoadFactor" name="criticalLoadFactor" value="{{allInputDataDict.criticalLoadFactor}}" min="0" max="1" pattern="^\d+\.?\d*?$" required="required"/>
</div>

<!-- Financial Inputs -->
<div class="wideInput">
<p class="inputSectionHeader">Financial Inputs</p>
Expand Down Expand Up @@ -171,7 +134,7 @@

<!-- vbatDispatch Specific Inputs -->
<div class="wideInput">
<p class="inputSectionHeader">Thermal Device Inputs</p>
<p class="inputSectionHeader">Thermal Energy Storage Device Inputs</p>
</div>
<hr style="border-style: solid; border-color: #196b12; margin-top: 10px;">
<div class="shortInput">
Expand Down Expand Up @@ -280,20 +243,9 @@
<p class="reportTitle" style="page-break-before:always">Thermal Battery Power Profile</p>
<div id="thermalBatPowerPlot" class="tightContent" style="width: 1000px; height: 600px;"></div>

<!--
<p class="reportTitle" style="page-break-before:always">Thermal Device Temperature Profile</p>
<div id="thermalDevicePlotData" class="tightContent" style="width: 1000px; height: 600px;"></div>
-->

<p class="reportTitle" style="page-break-before:always">Chemical BESS Charge Percentage</p>
<div id="batteryChargePlotly" class="tightContent" style="width: 1000px; height: 600px;"></div>

<p class="reportTitle" style="page-break-before:always">Resilience Overview</p>
<div id="resiliencePlotly" class="tightContent"></div>

<p class="reportTitle" style="page-break-before:always">Outage Survival Probability</p>
<div id="resilienceProbPlotly" class="tightContent"></div>

<p class="reportTitle">Monthly Cost Comparison</p>
<div id="levelizedCostReport" class="tightContent">
<div id="levelizedCostTableDiv" style="display:inline-block; width:1000px">
Expand Down Expand Up @@ -341,8 +293,8 @@
insertMetric("monthlySummaryTable","Total Cost ($)", allOutputData.totalCost)
insertMetric("monthlySummaryTable","Adjusted Total Cost ($)", allOutputData.totalCostAdjusted)
insertMetric("monthlySummaryTable","Utility Savings ($)", allOutputData.savings)
insertMetric("monthlySummaryTable","1 kW Consumer Savings ($)", allOutputData.totalSavingsSmallConsumer)
insertMetric("monthlySummaryTable","10 kW Consumer Savings ($)", allOutputData.totalSavingsLargeConsumer)
//insertMetric("monthlySummaryTable","1 kW Consumer Savings ($)", allOutputData.totalSavingsSmallConsumer)
//insertMetric("monthlySummaryTable","10 kW Consumer Savings ($)", allOutputData.totalSavingsLargeConsumer)
</script>
</div>
</div>
Expand Down
Loading

0 comments on commit 2c29a4a

Please sign in to comment.