diff --git a/omf/models/hostingCapacity.py b/omf/models/hostingCapacity.py index ebf5d4f82..16f033365 100644 --- a/omf/models/hostingCapacity.py +++ b/omf/models/hostingCapacity.py @@ -1,5 +1,4 @@ -''' Calculate solar photovoltaic system output using PVWatts. ''' - +''' Calculate hosting capacity using traditional and/or AMI-based methods. ''' import base64 import shutil from os.path import join as pJoin @@ -14,155 +13,133 @@ import omf from omf.models import __neoMetaModel__ from omf.models.__neoMetaModel__ import * -from omf.models import derInterconnection +from omf.solvers import opendss # Model metadata: -tooltip = "The hostingCapacity model calculates the kW hosting capacity available at each meter in the provided AMI data." +tooltip = "Calculate hosting capacity using traditional and/or AMI-based methods." modelName, template = __neoMetaModel__.metadata(__file__) hidden = False def work(modelDir, inputDict): - outData = {} - import mohca_cl - - with open(pJoin(modelDir,inputDict['inputDataFileName']),'w', newline='') as pv_stream: - pv_stream.write(inputDict['inputDataFileContent']) - - inputPath = pJoin(modelDir, inputDict['inputDataFileName']) - outputPath = pJoin(modelDir, 'mohcaOutput.csv') - - mohcaOutput = [] - if inputDict[ "mohcaAlgorithm" ] == "sandia1": - mohcaOutput = mohca_cl.sandia1( inputPath, outputPath ) - elif inputDict[ "mohcaAlgorithm" ] == "sandia2": - mohcaOutput = mohca_cl.sandia2( inputPath, outputPath ) - else: - errorMessage = "Algorithm name error" - raise Exception(errorMessage) - - mohcaResults = mohcaOutput[0].rename(columns={'kW_hostable': 'voltage_cap_kW'}) - - mohcaHistogramFigure = px.histogram( mohcaResults, x='voltage_cap_kW', template="simple_white", color_discrete_sequence=["MediumPurple"] ) - mohcaHistogramFigure.update_layout(bargap=0.5) - - barChartDF = mohcaResults - barChartDF['thermal_cap'] = [1] - barChartDF['max_cap_kW'] = np.minimum( barChartDF['voltage_cap_kW'], barChartDF['thermal_cap']) - - mohcaBarChartFigure = px.bar(barChartDF, x='busname', y=['voltage_cap_kW', 'thermal_cap', 'max_cap_kW'], barmode='group', color_discrete_sequence=["green", "lightblue", "MediumPurple"], template="simple_white" ) - # mohcaBarChartFigure.update_layout() - - # Line graph of the data - # timeSeriesFigure = px.line( mohcaResults.sort_values( by="kW_hostable", ascending=False, ignore_index=True ), x='busname', y='kW_hostable', markers=True, color_discrete_sequence=['purple', "blue", "green"]) - # timeSeriesFigure.update_yaxes(rangemode="tozero") # This line sets the base of the y axis to be 0. - - # traditional hosting capacity - # if they uploaded an omd circuit file - circuitFileStatus = inputDict.get('optionalCircuitFile', 0) - print('STATUS!', circuitFileStatus, inputDict.get('optionalCircuitFile', 0)) - if ( circuitFileStatus == 'on' ): - feederName = [x for x in os.listdir(modelDir) if x.endswith('.omd')][0] - inputDict['feederName1'] = feederName - omd = json.load(open(pJoin(modelDir, feederName))) - tree = omd.get('tree',{}) - #for k, v in omd['tree'].items(): - - # once we take the omd file and convert it to a tree, we can loop through the objects to find all the 'bus' objects, that can be our second input - # then there are option inputs for the step and kw for the user to put in? idk - # print( os.path.abspath(feederName) ) why is this in the omf directory and not the modelDir? - traditionalHCResults = omf.solvers.opendss.hosting_capacity_verbose(pJoin(modelDir, feederName), modelDir, ['701', '730', '703', '724'], int( inputDict["traditionalHCSteps"] ), int( inputDict["traditionalHCkW"] )) - tradHCDF = traditionalHCResults[0] - print( tradHCDF ) - - omf.geo.map_omd(pJoin(modelDir, feederName), modelDir, open_browser=False ) - # outData['traditionalHCMap'] = pJoin( modelDir, 'geoJson_offline.html') - outData['traditionalHCMap'] = open( pJoin( modelDir, "geoJson_offline.html"), 'r' ).read() - - traditionalHCFigure = make_subplots(specs=[[{"secondary_y": True }]]) - traditionalHCFigure.add_trace( - go.Scatter( x = tradHCDF.index, y = tradHCDF["kw_add"], name= "kw_add"), - secondary_y=False - ) - traditionalHCFigure.add_trace( - go.Scatter( x = tradHCDF.index, y = tradHCDF["v_max_pu1"], name= "v_max_pu1"), - secondary_y=True - ) - traditionalHCFigure.add_trace( - go.Scatter( x = tradHCDF.index, y = tradHCDF["v_max_pu2"], name= "v_max_pu2"), - secondary_y=True - ) - traditionalHCFigure.add_trace( - go.Scatter( x = tradHCDF.index, y = tradHCDF["v_max_pu3"], name= "v_max_pu3"), - secondary_y=True - ) - traditionalHCFigure.add_trace( - go.Scatter( x = tradHCDF.index, y = tradHCDF["v_max_all_pu"], name= "v_max_all_pu"), - secondary_y=True - ) - traditionalHCFigure.update_yaxes( title_text="Voltage ( PU )", secondary_y = False) - traditionalHCFigure.update_yaxes( title_text="Total Additional Generation Added ( kW ) ", secondary_y = True) - outData['traditionalGraphData'] = json.dumps( traditionalHCFigure, cls=py.utils.PlotlyJSONEncoder ) - outData['traditionalHCTableHeadings'] = traditionalHCResults[0].columns.values.tolist() - outData['traditionalHCTableValues'] = ( list( traditionalHCResults[0].itertuples(index=False, name=None))) - - # Stdout/stderr. - outData['stdout'] = "Success" - outData['stderr'] = "" - outData['mohcaHistogramFigure'] = json.dumps( mohcaHistogramFigure, cls=py.utils.PlotlyJSONEncoder ) - outData['mohcaBarChartFigure'] = json.dumps( mohcaBarChartFigure, cls=py.utils.PlotlyJSONEncoder ) - outData['mohcaHCTableHeadings'] = mohcaResults.columns.values.tolist() - outData['mohcaHCTableValues'] = ( list(mohcaResults.sort_values( by="voltage_cap_kW", ascending=False, ignore_index=True ).itertuples(index=False, name=None)) ) #NOTE: kW_hostable - - # Stdout/stderr. - outData['stdout'] = "Success" - outData['stderr'] = "" - return outData + outData = {} + import mohca_cl + with open(pJoin(modelDir,inputDict['inputDataFileName']),'w', newline='') as pv_stream: + pv_stream.write(inputDict['inputDataFileContent']) + inputPath = pJoin(modelDir, inputDict['inputDataFileName']) + outputPath = pJoin(modelDir, 'mohcaOutput.csv') + mohcaOutput = [] + if inputDict[ "mohcaAlgorithm" ] == "sandia1": + mohcaOutput = mohca_cl.sandia1( inputPath, outputPath ) + elif inputDict[ "mohcaAlgorithm" ] == "sandia2": + mohcaOutput = mohca_cl.sandia2( inputPath, outputPath ) + else: + errorMessage = "Algorithm name error" + raise Exception(errorMessage) + mohcaResults = mohcaOutput[0].rename(columns={'kW_hostable': 'voltage_cap_kW'}) + mohcaHistogramFigure = px.histogram( mohcaResults, x='voltage_cap_kW', template="simple_white", color_discrete_sequence=["MediumPurple"] ) + mohcaHistogramFigure.update_layout(bargap=0.5) + barChartDF = mohcaResults + barChartDF['thermal_cap'] = [1] + barChartDF['max_cap_kW'] = np.minimum( barChartDF['voltage_cap_kW'], barChartDF['thermal_cap']) + mohcaBarChartFigure = px.bar(barChartDF, x='busname', y=['voltage_cap_kW', 'thermal_cap', 'max_cap_kW'], barmode='group', color_discrete_sequence=["green", "lightblue", "MediumPurple"], template="simple_white" ) + # mohcaBarChartFigure.update_layout() + # Line graph of the data + # timeSeriesFigure = px.line( mohcaResults.sort_values( by="kW_hostable", ascending=False, ignore_index=True ), x='busname', y='kW_hostable', markers=True, color_discrete_sequence=['purple', "blue", "green"]) + # timeSeriesFigure.update_yaxes(rangemode="tozero") # This line sets the base of the y axis to be 0. + # traditional hosting capacity + # if they uploaded an omd circuit file + circuitFileStatus = inputDict.get('optionalCircuitFile', 0) + print('STATUS!', circuitFileStatus, inputDict.get('optionalCircuitFile', 0)) + if ( circuitFileStatus == 'on' ): + feederName = [x for x in os.listdir(modelDir) if x.endswith('.omd')][0] + inputDict['feederName1'] = feederName + tree = opendss.dssConvert.omdToTree(pJoin(modelDir, feederName)) + opendss.dssConvert.treeToDss(tree, pJoin(modelDir, 'circuit.dss')) + traditionalHCResults = opendss.hosting_capacity_all(pJoin(modelDir, 'circuit.dss'), int(inputDict["traditionalHCSteps"]), int(inputDict["traditionalHCkW"])) + tradHCDF = pd.DataFrame(traditionalHCResults) + print('TRADREZ', traditionalHCResults) + omf.geo.map_omd(pJoin(modelDir, feederName), modelDir, open_browser=False ) + outData['traditionalHCMap'] = open( pJoin( modelDir, "geoJson_offline.html"), 'r' ).read() + traditionalHCFigure = make_subplots(specs=[[{"secondary_y": True }]]) + traditionalHCFigure.add_trace( + go.Scatter( x = tradHCDF.index, y = tradHCDF["max_kw"], name= "max_kw"), + secondary_y=False + ) + # traditionalHCFigure.add_trace( + # go.Scatter( x = tradHCDF.index, y = tradHCDF["v_max_pu1"], name= "v_max_pu1"), + # secondary_y=True + # ) + # traditionalHCFigure.add_trace( + # go.Scatter( x = tradHCDF.index, y = tradHCDF["v_max_pu2"], name= "v_max_pu2"), + # secondary_y=True + # ) + # traditionalHCFigure.add_trace( + # go.Scatter( x = tradHCDF.index, y = tradHCDF["v_max_pu3"], name= "v_max_pu3"), + # secondary_y=True + # ) + # traditionalHCFigure.add_trace( + # go.Scatter( x = tradHCDF.index, y = tradHCDF["v_max_all_pu"], name= "v_max_all_pu"), + # secondary_y=True + # ) + # traditionalHCFigure.update_yaxes( title_text="Voltage ( PU )", secondary_y = False) + traditionalHCFigure.update_yaxes( title_text="Total Additional Generation Added ( kW ) ", secondary_y = True) + outData['traditionalGraphData'] = json.dumps( traditionalHCFigure, cls=py.utils.PlotlyJSONEncoder ) + # outData['traditionalHCTableHeadings'] = traditionalHCResults[0].columns.values.tolist() + # outData['traditionalHCTableValues'] = ( list( traditionalHCResults[0].itertuples(index=False, name=None))) + # Stdout/stderr. + outData['stdout'] = "Success" + outData['stderr'] = "" + outData['mohcaHistogramFigure'] = json.dumps( mohcaHistogramFigure, cls=py.utils.PlotlyJSONEncoder ) + outData['mohcaBarChartFigure'] = json.dumps( mohcaBarChartFigure, cls=py.utils.PlotlyJSONEncoder ) + outData['mohcaHCTableHeadings'] = mohcaResults.columns.values.tolist() + outData['mohcaHCTableValues'] = ( list(mohcaResults.sort_values( by="voltage_cap_kW", ascending=False, ignore_index=True ).itertuples(index=False, name=None)) ) #NOTE: kW_hostable + return outData def runtimeEstimate(modelDir): - ''' Estimated runtime of model in minutes. ''' - return 0.5 + ''' Estimated runtime of model in minutes. ''' + return 0.5 def new(modelDir): - ''' Create a new instance of this model. Returns true on success, false on failure. ''' - test_file_name = 'sandia_loc1_test_data.csv' - test_file_path = pJoin(omf.omfDir,'static','testFiles', test_file_name) - test_circuit_name = "ieee37_LBL" - test_file_contents = open(test_file_path).read() - defaultInputs = { - "mohcaAlgorithm": 'sandia1', - "feederName1": test_circuit_name, - "inputDataFileName": test_file_name, - "inputDataFileContent": test_file_contents, - "modelType": modelName, - "traditionalHCSteps": 10, - "optionalCircuitFile": 'on', - "traditionalHCkW": 1 - } - creationCode = __neoMetaModel__.new(modelDir, defaultInputs) - try: - shutil.copyfile(pJoin(__neoMetaModel__._omfDir, "static", "hostingcapacityfiles", defaultInputs["feederName1"]+'.omd'), pJoin(modelDir, defaultInputs["feederName1"]+'.omd')) - except: - return False - return creationCode + ''' Create a new instance of this model. Returns true on success, false on failure. ''' + meter_file_name = 'sandia_loc1_test_data.csv' + meter_file_path = pJoin(omf.omfDir,'static','testFiles', meter_file_name) + meter_file_contents = open(meter_file_path).read() + defaultInputs = { + "modelType": modelName, + "mohcaAlgorithm": 'sandia1', + "inputDataFileName": meter_file_name, + "inputDataFileContent": meter_file_contents, + "feederName1": 'ieee37_LBL', + "traditionalHCSteps": 10, + "optionalCircuitFile": 'on', + "traditionalHCkW": 1 + } + creationCode = __neoMetaModel__.new(modelDir, defaultInputs) + try: + shutil.copyfile( + pJoin(__neoMetaModel__._omfDir, "static", "hostingcapacityfiles", defaultInputs["feederName1"]+'.omd'), + pJoin(modelDir, defaultInputs["feederName1"]+'.omd')) + except: + return False + return creationCode @neoMetaModel_test_setup def _disabled_tests(): - # Location - modelLoc = pJoin(__neoMetaModel__._omfDir,"data","Model","admin","Automated Testing of " + modelName) - # Blow away old test results if necessary. - try: - shutil.rmtree(modelLoc) - except: - # No previous test results. - pass - # Create New. - new(modelLoc) - # Pre-run. - __neoMetaModel__.renderAndShow(modelLoc) - # Run the model. - __neoMetaModel__.runForeground(modelLoc) - # Show the output. - __neoMetaModel__.renderAndShow(modelLoc) + # Location + modelLoc = pJoin(__neoMetaModel__._omfDir,"data","Model","admin","Automated Testing of " + modelName) + # Blow away old test results if necessary. + try: + shutil.rmtree(modelLoc) + except: + pass # No previous test results. + # Create New. + new(modelLoc) + # Pre-run. + __neoMetaModel__.renderAndShow(modelLoc) + # Run the model. + __neoMetaModel__.runForeground(modelLoc) + # Show the output. + __neoMetaModel__.renderAndShow(modelLoc) if __name__ == '__main__': - _disabled_tests() + _disabled_tests() \ No newline at end of file