diff --git a/omf/scratch/hostingcapacity/downlineLoad.py b/omf/scratch/hostingcapacity/downlineLoad.py new file mode 100644 index 000000000..663d8289e --- /dev/null +++ b/omf/scratch/hostingcapacity/downlineLoad.py @@ -0,0 +1,78 @@ +import networkx as nx +from pathlib import Path +import pandas as pd +import math + +import omf +from omf.solvers import opendss +import os + +''' +Simply adds up all load further away from the substation than the given bus to estimate rough hosting capacity. +with open(Path(modelDir, 'treefile.txt'), "w") as file: + for item in tree: + file.write(f"{item}\n") + +''' + +def myGetCoords(dssFilePath): + '''Takes in an OpenDSS circuit definition file and outputs the bus coordinates as a dataframe.''' + dssFileLoc = os.path.dirname(dssFilePath) + opendss.runDSS(dssFilePath) + opendss.runDssCommand(f'export buscoords "{dssFileLoc}/coords.csv"') + coords = pd.read_csv(dssFileLoc + '/coords.csv', header=None) + #JENNY + coords.columns = ['Element', 'X', 'Y'] + return coords + +def my_networkPlot(filePath, figsize=(20,20), output_name='networkPlot.png', show_labels=True, node_size=300, font_size=8): + ''' Plot the physical topology of the circuit. + Returns a networkx graph of the circuit as a bonus. ''' + dssFileLoc = os.path.dirname(os.path.abspath(filePath)) + opendss.runDSS(filePath) + coords = myGetCoords(filePath) + opendss.runDssCommand(f'export voltages "{dssFileLoc}/volts.csv"') + volts = pd.read_csv(dssFileLoc + '/volts.csv') + #JENNY + coords.columns = ['Bus', 'X', 'Y'] + + buses = opendss.get_meter_buses(filePath) + G = nx.Graph() + # Get the coordinates. + pos = {} + for index, row in coords.iterrows(): + try: + bus_name = str(int(row['Bus'])) + except: + bus_name = row['Bus'] + # Get the connecting edges using Pandas. + + lines = opendss.dss.utils.lines_to_dataframe() + edges = [] + for index, row in lines.iterrows(): + #HACK: dss upercases everything in the coordinate output. + bus1 = row['Bus1'].split('.')[0].upper() + bus2 = row['Bus2'].split('.')[0].upper() + edges.append((bus1, bus2)) + G.add_edges_from(edges) + # Remove buses withouts coords + no_pos_nodes = set(G.nodes()) - set(pos) + G.remove_nodes_from(list(no_pos_nodes)) + return G + +def downline_hosting_capacity( FNAME ): + fullpath = os.path.abspath(FNAME) + tree = opendss.dssConvert.omdToTree(fullpath) + + +if __name__ == '__main__': + modelDir = Path(omf.omfDir, 'scratch', 'hostingcapacity') + circuit_file = 'circuit.dss' + beginning_test_file = Path( omf.omfDir, 'static', 'publicFeeders', 'iowa240.clean.dss.omd') + + tree = opendss.dssConvert.omdToTree(beginning_test_file.resolve()) # this tree is a list + + graph = my_networkPlot( os.path.join( modelDir, circuit_file) ) + print( graph.nodes() ) + #print( nx.descendants(graph, "EQ_SOURCE_BUS") ) + diff --git a/omf/scratch/hostingcapacity/hosting_cap_repro.py b/omf/scratch/hostingcapacity/hosting_cap_repro.py new file mode 100644 index 000000000..ee5431f39 --- /dev/null +++ b/omf/scratch/hostingcapacity/hosting_cap_repro.py @@ -0,0 +1,39 @@ +import omf +from pathlib import Path +from omf.models import __neoMetaModel__ +from omf.models.__neoMetaModel__ import * +from omf.solvers import opendss +from omf.solvers import mohca_cl +import time + +meter_file_name = 'mohcaInputCustom.csv' +meter_file_path = Path(omf.omfDir,'static','testFiles', 'hostingCapacity', meter_file_name) +modeldir = Path(omf.omfDir, 'scratch', 'hostingCapacity') + +# 2541.865383386612 - iowa state +def run_ami_algorithm(modelDir, meterfileinput, outData): + # mohca data-driven hosting capacity + inputPath = Path(modelDir, meterfileinput) + outputPath = Path(modelDir, 'AMI_output.csv') + AMI_start_time = time.time() + AMI_output = mohca_cl.sandia1( inputPath, outputPath ) + AMI_end_time = time.time() + runtime = AMI_end_time - AMI_start_time + return runtime + + +def convert_seconds_to_hms_ms(seconds): + # Convert seconds to milliseconds + milliseconds = seconds * 1000 + + # Calculate hours, minutes, seconds, and milliseconds + hours, remainder = divmod(milliseconds, 3600000) # 3600000 milliseconds in an hour + minutes, remainder = divmod(remainder, 60000) # 60000 milliseconds in a minute + seconds, milliseconds = divmod(remainder, 1000) # 1000 milliseconds in a second + + # Format the output + return "{:02d}:{:02d}:{:02d}.{:03d}".format(int(hours), int(minutes), int(seconds), int(milliseconds)) + +runtime = run_ami_algorithm(modeldir, meter_file_path, 'output.csv') +print( 'AMI_runtime: ', runtime) +print( 'formatted time: ', convert_seconds_to_hms_ms( runtime )) \ No newline at end of file diff --git a/omf/scratch/hostingcapacity/networkPlot.png b/omf/scratch/hostingcapacity/networkPlot.png new file mode 100644 index 000000000..6b9ba6cc6 Binary files /dev/null and b/omf/scratch/hostingcapacity/networkPlot.png differ diff --git a/omf/scratch/hostingcapacity/selenium_test_hostingCapacity.py b/omf/scratch/hostingcapacity/selenium_test_hostingCapacity.py new file mode 100644 index 000000000..1423a5cfd --- /dev/null +++ b/omf/scratch/hostingcapacity/selenium_test_hostingCapacity.py @@ -0,0 +1,133 @@ +import time +import sys + +from pathlib import Path + +import omf + +# Selenium Imports + +def login_to_OMF( driver, username, password ): + from selenium.webdriver.common.keys import Keys + from selenium.webdriver.common.by import By + + + username_field = driver.find_element(by=By.ID, value="username") + password_field = driver.find_element(by=By.ID, value='password') + username_field.send_keys(username) + password_field.send_keys(password) + password_field.send_keys(Keys.RETURN) + + + +def firefox_test( url ): + firefox_geckodriver_path = "/snap/bin/firefox" + firefox_options = webdriver.FirefoxOptions() + driver_service = webdriver.FirefoxService(executable_path=firefox_geckodriver_path) + + driver = webdriver.Firefox(service=driver_service, options=firefox_options) + driver.get( url ) + + time.sleep(2) + driver.quit() + +def chrome_test_iowa_state_algo( url, username, password ): + from selenium import webdriver + from selenium.webdriver.common.by import By + from selenium.webdriver.common.keys import Keys + from selenium.webdriver.support.select import Select + from selenium.webdriver.support.ui import WebDriverWait + from selenium.webdriver.support import expected_conditions + + retVal = True + + test_name = "Selenium Test for Hosting Capacity Iowa State Algorithm" + modelDir = Path( omf.omfDir, "data", "Model", username, test_name ) + + iowa_state_input = 'isu_testInputData.csv' + default_name = 'mohcaInputCustom.csv' + + # TODO: Do I want to check if the automated test exists already and if so, delete it? + + driver = webdriver.Chrome() + driver.get( url ) + + login_to_OMF( driver, username, password ) + + # Create hostingCapacity Model + driver.find_element(by=By.ID, value="newModelButton").click() + driver.find_element(by=By.XPATH, value="//a[contains(@href, 'hostingCapacity')]" ).click() + + # Not sure if waiting one second for the pop-up for the name is needed + wait = WebDriverWait(driver, 1) + alert = wait.until(expected_conditions.alert_is_present()) + + alert.send_keys(test_name) + alert.accept() + + # Wait for model to load up + # wait = WebDriverWait(driver, 3) # Adjust the timeout as needed + wait.until(expected_conditions.staleness_of(driver.find_element(By.TAG_NAME, "html"))) + + # Upload Iowa State Input File + ami_data_input = driver.find_element(by=By.ID, value="AMIDataFile") + # Input must be of type string and be the absolute path to the file + ami_data_input.send_keys( str( Path( omf.omfDir, 'static', 'testFiles', 'hostingCapacity', iowa_state_input).resolve() ) ) + + # Set Algorithm + algo_type_dropdown = driver.find_element(By.ID, 'algorithm') + select = Select(algo_type_dropdown) + select.select_by_visible_text('iastate') + + # Turn Traditional Hosting Cap Off + trad_algo_dropdown = driver.find_element(By.ID, 'optionalCircuitFile') + select = Select(trad_algo_dropdown) + select.select_by_visible_text('Off') + + time.sleep(3) + + driver.find_element(by=By.ID, value="runButton").click() + + #Can check for raw output and see if that is a good source to determine completion? + + userAMIDisplayFileName = driver.find_element(by=By.ID, value="userAMIDisplayFileName") + newFileUploadedName = userAMIDisplayFileName.get_attribute('value') + + # Check if the HTML updates with the new file name that the user uploaded + try: + assert newFileUploadedName == iowa_state_input + # DELETE print( newFileUploadedName + " " + voltageTestFile1) + except AssertionError: + print( "FAILED: Assertion that the HTML display name of the AMI Data input file correctly changed to the users file name") + print( "Actual: " + newFileUploadedName + " Expected: " + iowa_state_input ) + retVal = False + + #Check if the file is present in the directory with the correct standard naming convention name + if Path( modelDir, default_name).exists() == False: + print( default_name + " does not exist in the directory") + retVal = False + + #Check to make sure that a file by the name the user uploaded is NOT there + if Path( modelDir, iowa_state_input ).exists(): + print( "File created with users file input name: " + iowa_state_input ) + retVal = False + + # TODO: Check outputs - p vague idea + # One idea - check if the raw input/output files exists. I think that's present for every algorithm after completion. + WebDriverWait(driver, 600).until(expected_conditions.presence_of_element_located((By.ID, 'rawOutput'))) + + driver.quit() + return retVal + +if __name__ == '__main__': + url = 'http://localhost:5000/' + if len( sys.argv ) != 3: + print("Usage: python script.py ") + sys.exit(1) + + username = sys.argv[1] + password = sys.argv[2] + + if chrome_test_iowa_state_algo( url, username, password ): + print( "PASSED: Hosting Capacity Iowa State Algorithm") + #firefox_test \ No newline at end of file diff --git a/omf/scratch/mohca/hosting_cap_repro.py b/omf/scratch/mohca/hosting_cap_repro.py deleted file mode 100644 index 601415f06..000000000 --- a/omf/scratch/mohca/hosting_cap_repro.py +++ /dev/null @@ -1,90 +0,0 @@ -import omf -from omf.models import __neoMetaModel__ -from omf.models.__neoMetaModel__ import * -from omf.solvers import opendss -import pathlib -import pandas as pd -import numpy as np -import plotly.express as px - -def list_of_dicts_to_dataframe(list_of_dicts): - ''' Does what the funciton name says. Make sure the dicts have consistent keys. ''' - return pd.DataFrame(list_of_dicts) - -modelDir = pathlib.PurePath(omf.omfDir, 'scratch', 'mohca') - -''' -# Example of hosting capacity for all buses with loads, i.e. metered buses. -# fnameDSS = pathlib.PurePath(omf.omfDir, 'data', 'Model', 'admin', 'good work', 'circuit.dss') -fnameDSS = pathlib.PurePath(omf.omfDir, 'solvers', 'opendss', 'iowa240.clean.dss') -meter_buses = opendss.get_meter_buses(fnameDSS) -iowa_hosting_dss = opendss.hosting_capacity_all(fnameDSS, kwSTEPS=10, kwValue=10.0) -iowa_df = list_of_dicts_to_dataframe(iowa_hosting_dss) -print(iowa_df.head) - -fnameOMDfromDSS = pathlib.PurePath( fnameDSS, pathlib.PurePath(modelDir, 'iowatest.omd' )) -fnameTestDSSfromOMD = pathlib.PurePath( modelDir, 'circuit.dss') - -opendss.dssConvert.dssToOmd(fnameDSS, fnameOMDfromDSS) -tree = opendss.dssConvert.omdToTree(fnameOMDfromDSS) -opendss.dssConvert.treeToDss(tree, fnameTestDSSfromOMD) -meter_buses = opendss.get_meter_buses(fnameTestDSSfromOMD) -iowa_hosting = opendss.hosting_capacity_all(fnameTestDSSfromOMD, 10, 10.0, BUS_LIST=meter_buses) -iowa_df_omd = list_of_dicts_to_dataframe(iowa_hosting) -print(iowa_df_omd.head) - -fnameOMD = pathlib.PurePath(omf.omfDir, 'static', 'publicFeeders', 'iowa240c1.clean.dss.omd') -tree = opendss.dssConvert.omdToTree(fnameOMD) -opendss.dssConvert.treeToDss(tree, pathlib.PurePath(modelDir, 'circuit.dss')) -meter_buses = opendss.get_meter_buses('circuit.dss') -iowa_hosting = opendss.hosting_capacity_all('circuit.dss', 10, 10.0, BUS_LIST=meter_buses) -iowa_df = list_of_dicts_to_dataframe(iowa_hosting) -print( iowa_df.head ) -''' - -def bar_chart_coloring( row ): - color = 'black' - if row['thermal_violation'] and not row['voltage_violation']: - color = 'orange' - elif not row['thermal_violation'] and row['voltage_violation']: - color = 'yellow' - elif not row['thermal_violation'] and not row['voltage_violation']: - color = 'green' - else: - color = 'red' - return color - -fname_syracuse = pJoin('/home', 'jenny', 'Downloads', 'Wheatland_syracuse.dss') -meter_buses = opendss.get_meter_buses(fname_syracuse) -syracuse_hosting = opendss.hosting_capacity_all(fname_syracuse, 10, 10.0, BUS_LIST=meter_buses) -syracuse_df = list_of_dicts_to_dataframe(syracuse_hosting) -syracuse_df.to_csv('syracuse_output.csv') - -syracuse_df['plot_color'] = syracuse_df.apply ( lambda row: bar_chart_coloring(row), axis=1 ) -syracuse_figure = px.bar( syracuse_df, x='bus', y='max_kw', barmode='group', color='plot_color', color_discrete_map={ 'red': 'red', 'orange': 'orange', 'green': 'green'}, template='simple_white' ) -syracuse_figure.update_xaxes(categoryorder='array', categoryarray=syracuse_df.bus.values) -colorToKey = {'orange':'thermal_violation', 'green': 'voltage_violation', 'red': 'both_violation'} -syracuse_figure.for_each_trace(lambda t: t.update(name = colorToKey[t.name], - legendgroup = colorToKey[t.name], - hovertemplate = t.hovertemplate.replace(t.name, colorToKey[t.name]) - ) - ) -syracuse_figure.show() - -# Windings Test -fname_shelley = pJoin('/home', 'jenny', 'Downloads', 'Wheatland_shelley.dss') -shelley_hosting = opendss.hosting_capacity_all('Wheatland_shelley.dss', 5, 10) -shelley_df = list_of_dicts_to_dataframe( shelley_hosting ) -shelley_df.to_csv('shelley_output.csv') -print( shelley_df ) - -shelley_df['plot_color'] = shelley_df.apply ( lambda row: bar_chart_coloring(row), axis=1 ) -shelley_figure = px.bar( shelley_df, x='bus', y='max_kw', barmode='group', color='plot_color', color_discrete_map={ 'red': 'red', 'orange': 'orange', 'green': 'green'}, template='simple_white' ) -shelley_figure.update_xaxes(categoryorder='array', categoryarray=shelley_df.bus.values) -colorToKey = {'orange':'thermal_violation', 'green': 'voltage_violation', 'red': 'both_violation'} -shelley_figure.for_each_trace(lambda t: t.update(name = colorToKey[t.name], - legendgroup = colorToKey[t.name], - hovertemplate = t.hovertemplate.replace(t.name, colorToKey[t.name]) - ) - ) -shelley_figure.show() \ No newline at end of file diff --git a/omf/scratch/transformerPairing/selenium_tests.py b/omf/scratch/transformerPairing/selenium_tests.py index 398a999cc..91210927a 100644 --- a/omf/scratch/transformerPairing/selenium_tests.py +++ b/omf/scratch/transformerPairing/selenium_tests.py @@ -47,21 +47,17 @@ def chrome_test( url, username, password ): password_field = driver.find_element(by=By.ID, value='password') username_field.send_keys(username) password_field.send_keys(password) - time.sleep(2) password_field.send_keys(Keys.RETURN) # Create transformerPairing Model driver.find_element(by=By.ID, value="newModelButton").click() - time.sleep(2) ## find transformerPairing Model using xpath #transformerPairingModelButton = driver.find_element(by=By.XPATH, value="//a[contains(@href, 'transformerPairing')]" ) # transformerPairingModelButton.click() #driver.execute_script("arguments[0].click();", transformerPairingModelButton) driver.find_element(by=By.XPATH, value="//a[contains(@href, 'transformerPairing')]" ).click() - time.sleep(2) - wait = WebDriverWait(driver, 2) alert = wait.until(expected_conditions.alert_is_present()) alert.send_keys(test_name) @@ -72,17 +68,13 @@ def chrome_test( url, username, password ): voltage_file_input = driver.find_element(by=By.ID, value="voltageDataFile") # Input must be of type string and be the absolute path to the file - voltage_file_input.send_keys( str( Path(voltageTestFile1).resolve() ) ) - - # Should I wait one second? idk. - - time.sleep(2) + voltage_file_input.send_keys( str( Path(voltageTestFile1).resolve() ) )\ driver.find_element(by=By.ID, value="runButton").click() # Wait? - time.sleep(10) + time.sleep(5) userInputVoltageDisplayName = driver.find_element(by=By.ID, value="userInputVoltageDisplayName") newFileUploadedName = userInputVoltageDisplayName.get_attribute('value') @@ -121,31 +113,8 @@ def chrome_test( url, username, password ): print( "Actual: " + str(firstValue) + " Expected: " + str(voltageTestFile1Value) ) retVal = False - #Check the allInputData.json file - #ATM - not working. Maybe a lock thing like the issue with before. Maybe save for the end..? If at all? - ''' - try: - with open( Path(modelDir, "allInputData.json"), 'r' ) as jsonFile: - file_contents = jsonFile.read() - try: - assert file_contents['userInputVoltageDisplayName'] == voltageTestFile1 - except AssertionError: - print("Assertion failed for ['userInputVoltageDisplayName'] in allInputData.json") - print("Actual: " + file_contents['userInputVoltageDisplayName'] + " Expected: " + voltageTestFile1) - - try: - assert file_contents['voltageDataFileName'] == defaultVoltageFileName - except AssertionError: - print("Assertion failed for ['voltageDataFileName'] in allInputData.json") - print("Actual: " + file_contents['voltageDataFileName'] + " Expected: " + defaultVoltageFileName) - except: - print( "allInputData.json not found or empty") - retVal = False - ''' - # TODO: Check outputs - p vague idea - - time.sleep(4) + WebDriverWait(driver, 600).until(expected_conditions.presence_of_element_located((By.ID, 'rawOutput'))) driver.quit() return retVal