Exemplo n.º 1
0
def addScaledRandomHouses(inFeed):
	''' Take a feeder, translate each triplex_node under a meter in to a scaled, semi-randomized house object. '''
	houseArchetypes = _get_house_archetypes()
	childrenPath = os.path.join(omf.omfDir, 'static', 'testFiles', 'houseChildren.glm')
	childrenArchetypes = feeder.parse(childrenPath)
	tripNodeKeys = _get_by_key_val(inFeed, 'object', 'triplex_node', getAll=True)
	tripLoadKeys = [k for k in tripNodeKeys if 'parent' in inFeed[k]]
	maxKey = feeder.getMaxKey(inFeed) + 1
	inFeed[maxKey] = {'omftype': 'module', 'argument': 'residential'}
	maxKey += 1
	inFeed[maxKey] = {'omftype': '#include','argument': '\"schedulesResponsiveLoads.glm\"'}
	maxKey += 1
	for tripKey in tripLoadKeys:
		tMeter = inFeed[_get_by_key_val(inFeed, 'name', inFeed[tripKey]['parent'])]
		tPower = complex(inFeed[tripKey]['power_12']).real
		newHouse = dict(random.choice(list(houseArchetypes.values())))
		newHouse['name'] += '_' + str(tripKey)
		newHouse['parent'] = tMeter['name']
		newHouse['schedule_skew'] = str(random.gauss(2000,500))
		newHouse['floor_area'] = str(500.0 + 0.50*tPower) # Add 500 because very small floor_areas break GLD.
		newHouse['latitude'] = tMeter.get('latitude','0.0')
		newHouse['longitude'] = tMeter.get('longitude','0.0')
		inFeed[maxKey] = newHouse
		maxKey += 1
		for childKey in childrenArchetypes:
			newChild = dict(childrenArchetypes[childKey])
			newChild['name'] += '_' + str(tripKey) + '_' + str(childKey)
			newChild['parent'] = newHouse['name']
			newChild['latitude'] = tMeter.get('latitude','0.0')
			newChild['longitude'] = tMeter.get('longitude','0.0')
			newChild['schedule_skew'] = str(random.gauss(8000,1000))
			inFeed[maxKey] = newChild
			maxKey += 1
		del inFeed[tripKey]
Exemplo n.º 2
0
def oneLineGridlab(temp_dir):
    '''
	Create a one-line diagram of the input GLM and return a PNG of it.

	Form parameters:
	:param glm: a GLM file.
	:param useLatLons: 'True' if the diagram should be drawn with coordinate values taken from within the GLM, 'False' if the diagram should be drawn
		with artificial coordinates using Graphviz NEATO.

	Details:
	:OMF fuction: omf.feeder.latLonNxGraph().
	:run-time: about 1 to 30 seconds.
	'''
    glm_path = os.path.join(temp_dir, 'in.glm')
    feed = feeder.parse(glm_path)
    graph = feeder.treeToNxGraph(feed)
    neatoLayout = True if request.form.get('useLatLons') == 'False' else False
    # Clear old plots.
    plt.clf()
    plt.close()
    # Plot new plot.
    feeder.latLonNxGraph(graph,
                         labels=False,
                         neatoLayout=neatoLayout,
                         showPlot=False)
    plt.savefig(os.path.join(temp_dir, filenames["ongl"]))
Exemplo n.º 3
0
def createNewGLM(glmFile):
	'''Takes a GLM file and adds a solar inverter/panel pair to each meter on GLM.  Based on Taxonomic Feeders'''
	feed = feeder.parse('./Taxonomy_Feeders-master/'+glmFile)

	'''Following block provides automatic coversion for taxonmic feeders'''
	if 'timestamp' in feed[0]:
		feed[0]['starttime'] = feed[0].pop('timestamp')
	if 'omftype' in feed[1] and '#set' in feed[1]['omftype']:
		feed[1]['omftype'] = 'module'
		feed[1]['argument']  = 'tape;\nmodule generators'
	
	meter_list = []
	for item in feed:
		if 'object' not in feed[item]:
			pass
		elif 'meter' in feed[item]['object']:
			meter_list.append(item)

	count = 1
	for item in meter_list:
		addRandomSolar(feed, item, count)
		count += 1

	output = open('./Taxonomy_Feeders-solar/'+'.'.join(glmFile.split('.')[:2])+'-solarAdd.glm', 'w')
	output.write(feeder.write(feed))
	output.close()
Exemplo n.º 4
0
def createNewGLM(glmFile):
    '''Takes a GLM file and adds a solar inverter/panel pair to each meter on GLM.  Based on Taxonomic Feeders'''
    feed = feeder.parse('./Taxonomy_Feeders-master/' + glmFile)
    '''Following block provides automatic coversion for taxonmic feeders'''
    if 'timestamp' in feed[0]:
        feed[0]['starttime'] = feed[0].pop('timestamp')
    if 'omftype' in feed[1] and '#set' in feed[1]['omftype']:
        feed[1]['omftype'] = 'module'
        feed[1]['argument'] = 'tape;\nmodule generators'

    meter_list = []
    for item in feed:
        if 'object' not in feed[item]:
            pass
        elif 'meter' in feed[item]['object']:
            meter_list.append(item)

    count = 1
    for item in meter_list:
        addRandomSolar(feed, item, count)
        count += 1

    output = open(
        './Taxonomy_Feeders-solar/' + '.'.join(glmFile.split('.')[:2]) +
        '-solarAdd.glm', 'w')
    output.write(feeder.write(feed))
    output.close()
Exemplo n.º 5
0
def _test9():
    # test KillAllAtTime agent
    glmPath = omf.omfDir + '/scratch/CIGAR/test_smsSingle.glm'
    from omf import cyberAttack
    cosimProps = {
        'port': '6267',
        'hostname': 'localhost',
        'glmPath': glmPath,
        'startTime': '2000-01-01 00:00:00',
        'endTime': '2000-01-05 00:00:00',
        'stepSizeSeconds': 3600
    }
    # Gather the things to attack
    from omf import feeder
    tree = feeder.parse(glmPath)
    namesOfInverters = []
    for key in tree:
        ob = tree[key]
        if ob.get('object', '') == 'inverter' and 'name' in ob:
            namesOfInverters.append(ob['name'])
    # Set up the agents and the simulation
    agents = []
    agents.append(
        cyberAttack.KillAllAtTime('KillAllInverters', '2000-01-01 12:00:00',
                                  'MagnitudeOfKillInput', namesOfInverters))
    print('Starting co-sim with the KillAllInverters agent.')
    coord = Coordinator(agents, cosimProps)
    print(coord.drawPrettyResults())
Exemplo n.º 6
0
def _validate_oneLineGridlab(temp_dir):
    '''TODO'''
    glm_path = os.path.join(temp_dir, 'in.glm')
    request.files['glm'].save(glm_path)
    tree = feeder.parse(glm_path)
    if not distNetViz.contains_valid_coordinates(
            tree) and request.form['useLatLons'] == 'True':
        return ({
            'useLatLons': 'True'
        }, ("Since the submitted GLM contained no coordinates, or the coordinates could not be parsed as floats, "
            "'useLatLons' must be 'False' because artificial coordinates must be used to draw the GLM."
            ))
    return (None, None)
Exemplo n.º 7
0
def glmToModel(glmPath, modelDir):
    ''' One shot model creation from glm. '''
    tree = feeder.parse(glmPath)
    # Run powerflow. First name the folder for it.
    # Remove old copy of the model.
    shutil.rmtree(modelDir, ignore_errors=True)
    # Create the model directory.
    new(modelDir)
    # Create the .omd.
    os.remove(modelDir + '/Olin Barre Geo.omd')
    with open(modelDir + '/Olin Barre Geo.omd', 'w') as omdFile:
        omd = dict(feeder.newFeederWireframe)
        omd['tree'] = tree
        json.dump(omd, omdFile, indent=4)
Exemplo n.º 8
0
def gridlabdToGfm(temp_dir):
    '''
	Convert a GridLAB-D model (i.e. .glm file) into a LANL ANSI General Fragility Model and return the GFM model as JSON. Note that this is not the
	main fragility model for GRIP.

	Form parameters:
	:param glm: a GLM file.
	:param phase_variation: maximum phase unbalance allowed in the optimization model.
	:param chance_constraint: indicates the percent of damage scenarios where load constraints above must be met.
	:param critical_load_met: indicates the percent of critical load that must be met in each damage scenario. 
	:param total_load_met: indicates the percent of non-critical load that must be met in each damage scenario. 
	:param maxDGPerGenerator: the maximum DG capacity that a generator supports in MW. 
	:param dgUnitCost: the cost of adding distributed generation to a load in $/MW.
	:param generatorCandidates: the IDs of nodes on the system where the user wants to consider adding distributed generation. At least one node is
		required.
	:type generatorCandidates: one long string delimited with commas.
	:param criticalLoads: the IDs of loads on the system that the user declares to be critical (must-run).
	:type criticalLoads: one long string delimited with commas.

	Details:
	:OMF function: omf.models.resilientDist.convertToGFM().
	:run-time: a few seconds.
	'''
    fName = 'in.glm'
    f = request.files['glm']
    glmPath = os.path.join(temp_dir, fName)
    f.save(glmPath)
    gfmInputTemplate = {
        'phase_variation': float(request.form.get('phase_variation', 0.15)),
        'chance_constraint': float(request.form.get('chance_constraint', 1)),
        'critical_load_met': float(request.form.get('critical_load_met', .98)),
        'total_load_met': float(request.form.get('total_load_met', .9)),
        'maxDGPerGenerator': float(request.form.get('max_dg_per_generator',
                                                    1)),
        'dgUnitCost': float(request.form.get('dg_unit_cost', 1000000)),
        'generatorCandidates': request.form.get('generator_candidates', ''),
        'criticalLoads': request.form.get('critical_loads', '')
    }
    feederModel = {
        'nodes': [],  # Don't need these.
        'tree': feeder.parse(glmPath)
    }
    gfmDict = resilientDist.convertToGFM(gfmInputTemplate, feederModel)
    with open(os.path.join(temp_dir, filenames["glgfm"]), 'w') as f:
        json.dump(gfmDict, f)
Exemplo n.º 9
0
def glmForceLayout(temp_dir):
    '''
	Inject artifical coordinates into a GridLAB-D .glm and return the .glm.

	Form parameters:
	:param glm: a GLM file

	Details:
	:OMF function: omf.distNetViz.insert_coordinates()
	:run-time: a few seconds
	'''
    glm_path = os.path.join(temp_dir, 'in.glm')
    glm_file = request.files['glm']
    glm_file.save(glm_path)
    tree = feeder.parse(glm_path)
    distNetViz.insert_coordinates(tree)
    with open(os.path.join(temp_dir, filenames['gfl']), 'w') as f:
        f.write(feeder.sortedWrite(tree))
Exemplo n.º 10
0
def _testingPlot():
    PREFIX = os.path.join(os.path.dirname(__file__), '../scratch/CIGAR/')
    #PREFIX = omf.omfDir + '/scratch/CIGAR/'

    FNAME = 'test_base_R4-25.00-1.glm_CLEAN.glm'
    # FNAME = 'test_Exercise_4_2_1.glm'
    # FNAME = 'test_ieee37node.glm'
    # FNAME = 'test_ieee123nodeBetter.glm'
    # FNAME = 'test_large-R5-35.00-1.glm_CLEAN.glm'c
    # FNAME = 'test_medium-R4-12.47-1.glm_CLEAN.glm'
    # FNAME = 'test_smsSingle.glm'

    tree = feeder.parse(PREFIX + FNAME)
    chart = drawPlot(tree, neatoLayout=True, perUnitScale=False, rezSqIn=400)
    #chart = drawPlot(PREFIX + FNAME, neatoLayout=True, edgeCol=True, nodeLabs="Voltage", edgeLabs="Current", perUnitScale=False, rezSqIn=400)

    chart.savefig(PREFIX + "YO_WHATS_GOING_ON.png")
    plt.show()
Exemplo n.º 11
0
def gridlabRun(temp_dir):
    '''
	Run a .glm through GridLAB-D and return the results as JSON.

	Form parameters:
	:param glm: a GLM file.

	Details:
	:OMF fuction: omf.solvers.gridlabd.runInFileSystem().
	:run-time: up to a few hours.
	TODO: think about attachment support.
	'''
    fName = 'in.glm'
    f = request.files['glm']
    glmOnDisk = os.path.join(temp_dir, fName)
    f.save(glmOnDisk)
    feed = feeder.parse(glmOnDisk)
    outDict = gridlabd.runInFilesystem(feed,
                                       attachments=[],
                                       keepFiles=True,
                                       workDir=temp_dir,
                                       glmName='out.glm')
    with open(os.path.join(temp_dir, filenames["glrun"]), 'w') as f:
        json.dump(outDict, f)
Exemplo n.º 12
0
def read_glm_files(directory):
	""" Read each .glm file and return a dictionary of dictionaries
	"""
	os.chdir(directory)
	return [feeder.parse(file_path) for file_path in glob.glob("*.glm")]
Exemplo n.º 13
0
import omf.feeder as feeder
from omf.solvers.gridlabd import runInFilesystem

feed = feeder.parse('GC-12.47-1.glm')
maxKey = feeder.getMaxKey(feed)
print(feed[1])
feed[maxKey + 1] = {
    'object': 'node',
    'name': 'test_solar_node',
    'phases': 'ABCN',
    'nominal_voltage': '7200'
}

feed[maxKey + 2] = {
    'object': 'underground_line',
    'name': 'test_solar_line',
    'phases': 'ABCN',
    'from': 'test_solar_node',
    'to': 'GC-12-47-1_node_26',
    'length': '100',
    'configuration': 'line_configuration:6'
}

feed[maxKey + 3] = {
    'object': 'meter',
    'name': 'test_solar_meter',
    'parent': 'test_solar_node',
    'phases': 'ABCN',
    'nominal_voltage': '480'
}
feed[maxKey + 4] = {
Exemplo n.º 14
0
'measured_current_1.real, ' + \
'measured_current_2.real, ' + \
'measured_current_N.real, ' + \
'indiv_measured_power_1.real, ' + \
'indiv_measured_power_2.real, ' + \
'indiv_measured_power_N.real'


METER_FILENAME = 'meter.csv'
OUTPUT_FILENAME = 'faultyData.csv'

# load circuit ---------------------------------------------------------------------

# if circuit is defined in a glm file 
if CIRCUIT_PATH.endswith('.glm'):
	tree = feeder.parse(CIRCUIT_PATH)
	attachments = []

# if circuit is defined in a omd file
elif CIRCUIT_PATH.endswith('.omd'):
	omd = json.load(open(CIRCUIT_PATH))
	tree = omd.get('tree', {})
	attachments = omd.get('attachments',[])

else: # incorrect file type
	raise Exception('Invalid input file type. We require a .glm or .omd.')


# modify circuit -------------------------------------------------------------------

# assume circuit doesnt have a clock for keeping time or a tape module for recording
Exemplo n.º 15
0
import omf.feeder as feeder
from omf.solvers.gridlabd import runInFilesystem

feed = feeder.parse('GC-12.47-1.glm')
maxKey = feeder.getMaxKey(feed)
print (feed[1])
feed[maxKey + 1] = {
	'object': 'node', 'name': 'test_solar_node', 'phases': 'ABCN', 
	'nominal_voltage': '7200'
}

feed[maxKey + 2] = {
	'object': 'underground_line', 'name': 'test_solar_line', 'phases': 'ABCN',
	'from': 'test_solar_node', 'to': 'GC-12-47-1_node_26', 'length': '100',
	'configuration': 'line_configuration:6'
}

feed[maxKey + 3] = {
	'object': 'meter', 'name': 'test_solar_meter', 'parent': 'test_solar_node',
	'phases': 'ABCN', 'nominal_voltage': '480'
}
feed[maxKey + 4] = {
	'object': 'inverter', 'name': 'test_solar_inverter', 'parent': 'test_solar_meter',
	'phases': 'AS', 'inverter_type': 'PWM', 'power_factor': '1.0', 
	'generator_status': 'ONLINE', 'generator_mode': 'CONSTANT_PF'
					}
feed[maxKey + 5] = {
	'object': 'solar', 'name': 'test_solar', 'parent': 'test_solar_inverter', 'area': '1000000 sf',
	'generator_status': 'ONLINE', 'efficiency': '0.2', 'generator_mode': 'SUPPLY_DRIVEN',
	'panel_type': 'SINGLE_CRYSTAL_SILICON'
}
Exemplo n.º 16
0
import io, os, json, tempfile
from pathlib import Path
import requests
import pytest
from flask import url_for, request
import omf
from omf import feeder
from omf.solvers import gridlabd
from omf.scratch.GRIP import grip

# Place to hack on stuff. Not intended to be run with real tests via pytest
if __name__ == '__main__':
    temp_dir = tempfile.mkdtemp()
    path = Path(omf.omfDir) / 'scratch/CIGAR/test_ieee123nodeBetter.glm'
    #path = Path(__file__).parent / 'test-files/ieee123_pole_vulnerability.glm'
    feed = feeder.parse(path)
    #import pdb; pdb.set_trace()
    outDict = gridlabd.runInFilesystem(feed,
                                       attachments=[],
                                       keepFiles=True,
                                       workDir=temp_dir,
                                       glmName='out.glm')
    print(outDict)


@pytest.fixture(scope="module")  # The client should only be created once
def client():
    # testing must be set to true on the Flask application
    grip.app.config['TESTING'] = True
    # create a test client with built-in Flask code
    client = grip.app.test_client()
Exemplo n.º 17
0
def getRandomGridlabModelFromTemplate(templatePath, \
 deleteEVCharger, waterHeaterType, coolingType, heatingType):

    # initialize variables
    toDelete = []
    toInsert = []

    # get gridlab model from template
    gridlabModel = feeder.parse(templatePath)

    # calculate random house sqft as gaussian and convert to percent of range
    # percent of range is used to scale unresponsive and responsive loads
    houseSqft = random.gauss(HOUSE_SQFT_MEAN, HOUSE_SQFT_STDDEV)
    normalizedHouseSqft = abs(houseSqft - HOUSE_SQFT_MEAN) / HOUSE_SQFT_STDDEV
    sqftAsPercent = norm.cdf(normalizedHouseSqft) * 100

    # set simulation params appropriately
    for modelItemKey, modelItem in gridlabModel.items():

        # skew all schedules by random gaussians
        if (modelItem.get('schedule_skew') != None):
            gridlabModel[modelItemKey]['schedule_skew'] = \
             random.gauss(SCHEDULE_SKEW_MEAN,SCHEDULE_SKEW_STDDEV)

        # set all recoder intervals based on user provided timestep
        if (modelItem.get('object') == 'recorder'):
            gridlabModel[modelItemKey]['interval'] = TIMESTEP

        # make the water heater electric or gas
        elif (modelItem.get('object') == 'waterheater'):
            gridlabModel[modelItemKey]['heat_mode'] = waterHeaterType

        # set simulation climate randomly
        elif (modelItem.get('object') == 'climate'):
            # climateFile = glob(CLIMATE_FILES)[218]
            climateFile = random.choice(glob(CLIMATE_FILES))
            gridlabModel[modelItemKey]['tmyfile'] = climateFile

        # set time between samples given user provided timestep
        elif (modelItem.get('argument') != None) and \
        modelItem.get('argument').startswith('minimum_timestep='):
            gridlabModel[modelItemKey]['argument'] = \
             'minimum_timestep=' + str(TIMESTEP)

        # set clock given user provided start time, stop time, and timezone
        elif (modelItem.get('clock') != None):
            simStartTime = '\'' + SIM_START_TIME + '\''
            simStopTime = '\'' + SIM_STOP_TIME + '\''
            gridlabModel[modelItemKey]['starttime'] = simStartTime
            gridlabModel[modelItemKey]['stoptime'] = simStopTime
            gridlabModel[modelItemKey]['timezone'] = TIMEZONE

        # add random multipliers to house loads based on house sqft
        elif (modelItem.get('base_power') == 'responsive_loads') or \
         (modelItem.get('base_power') == 'unresponsive_loads'):

            # add some noise to sqftAsPercentage to ensure
            # that the loads do not use the same multiplier
            sqftAsPercentWithNoise = sqftAsPercent + \
             random.gauss(0, SQFT_AS_PERCENT_NOISE_STDDEV)
            if sqftAsPercentWithNoise < 0:
                sqftAsPercentWithNoise = 0
            elif sqftAsPercentWithNoise > 100:
                sqftAsPercentWithNoise = 100

            # scale loads
            multiplierRange = MAX_LOAD_MULTIPLIER - MIN_LOAD_MULTIPLIER
            multiplier = (sqftAsPercentWithNoise / 100) * multiplierRange
            multiplier = MIN_LOAD_MULTIPLIER + multiplier
            gridlabModel[modelItemKey]['base_power'] += '*' + str(multiplier)

        # remove the ev charger from house some of the time
        elif (modelItem.get('object') == 'evcharger_det'):

            # create recorder to save ev charger data
            evRecorder = {
                'object': 'recorder',
                'interval': TIMESTEP,
                'property': 'power.real',
                'line_units': 'NONE',
                'file': 'out_ev_charger.csv',
                'parent': 'ev_house0'
            }

            if deleteEVCharger:
                # delete charger and point recorder to
                # an empty meter that just outputs zeros
                toDelete.append(modelItemKey)
                evRecorder['parent'] = 'zero_meter'
                evRecorder['property'] = 'measured_power.real'

            # add recorder to insert queue
            toInsert.append(evRecorder)

        # set house params based on random house and
        # randomize heating and cooling setpoints, and
        # heating and cooling type
        elif (modelItem.get('object') == 'house'):

            # set params of template house based on random house
            house = loadModeling.get_random_new_house()
            house['floor_area'] = houseSqft
            for houseKey, houseVal in house.items():
                if (houseKey != 'name') and (houseKey != 'parent'):
                    gridlabModel[modelItemKey][houseKey] = houseVal

            # use user provided gaussian to set
            # house heating and cooling setpoints
            heatingSetpoint, coolingSetpoint = 0, 0
            while (heatingSetpoint >= coolingSetpoint):
                heatingSetpoint = random.gauss(HEATING_SETPOINT_MEAN,
                                               HEATING_SETPOINT_STDDEV)
                coolingSetpoint = random.gauss(COOLING_SETPOINT_MEAN,
                                               COOLING_SETPOINT_STDDEV)
            gridlabModel[modelItemKey]['heating_setpoint'] = heatingSetpoint
            gridlabModel[modelItemKey]['cooling_setpoint'] = coolingSetpoint

            # set house heating and cooling type
            gridlabModel[modelItemKey]['heating_system_type'] = heatingType
            gridlabModel[modelItemKey]['cooling_system_type'] = coolingType

    # insert and delete the appropriate items
    for item in toInsert:
        feeder.insert(gridlabModel, item)
    for modelItemKey in toDelete:
        del gridlabModel[modelItemKey]

    # output model
    return gridlabModel
Exemplo n.º 18
0
def drawPlot(path,
             workDir=None,
             neatoLayout=False,
             edgeLabs=None,
             nodeLabs=None,
             edgeCol=None,
             nodeCol=None,
             faultLoc=None,
             faultType=None,
             customColormap=False,
             scaleMin=None,
             scaleMax=None,
             rezSqIn=400,
             simTime='2000-01-01 0:00:00',
             loadLoc=None):
    ''' Draw a color-coded map of the voltage drop on a feeder.
	path is the full path to the GridLAB-D .glm file or OMF .omd file.
	workDir is where GridLAB-D will run, if it's None then a temp dir is used.
	neatoLayout=True means the circuit is displayed using a force-layout approach.
	edgeCol property must be either 'Current', 'Power', 'Rating', 'PercentOfRating', or None
	nodeCol property must be either 'Voltage', 'VoltageImbalance', 'perUnitVoltage', 'perUnit120Voltage', or None
	edgeLabs and nodeLabs properties must be either 'Name', 'Value', or None
	edgeCol and nodeCol can be set to false to avoid coloring edges or nodes
	customColormap=True means use a one that is nicely scaled to perunit values highlighting extremes.
	faultType and faultLoc are the type of fault and the name of the line that it occurs on.
	Returns a matplotlib object.'''
    # Be quiet matplotlib:
    # warnings.filterwarnings("ignore")
    if path.endswith('.glm'):
        tree = feeder.parse(path)
        attachments = []
    elif path.endswith('.omd'):
        with open(path) as f:
            omd = json.load(f)
        tree = omd.get('tree', {})
        attachments = omd.get('attachments', [])
    else:
        raise Exception('Invalid input file type. We require a .glm or .omd.')
    #print path
    # add fault object to tree
    def safeInt(x):
        try:
            return int(x)
        except:
            return 0

    biggestKey = max([safeInt(x) for x in tree.keys()])
    # Add Reliability module
    tree[str(biggestKey * 10)] = {
        "module": "reliability",
        "maximum_event_length": "18000",
        "report_event_log": "true"
    }
    CLOCK_START = simTime
    dt_start = parser.parse(CLOCK_START)
    dt_end = dt_start + relativedelta(seconds=+20)
    CLOCK_END = str(dt_end)
    CLOCK_RANGE = CLOCK_START + ',' + CLOCK_END
    if faultType != None:
        # Add eventgen object (the fault)
        tree[str(biggestKey * 10 + 1)] = {
            "object": "eventgen",
            "name": "ManualEventGen",
            "parent": "RelMetrics",
            "fault_type": faultType,
            "manual_outages": faultLoc + ',' + CLOCK_RANGE
        }  # TODO: change CLOCK_RANGE to read the actual start and stop time, not just hard-coded
        # Add fault_check object
        tree[str(biggestKey * 10 + 2)] = {
            "object": "fault_check",
            "name": "test_fault",
            "check_mode": "ONCHANGE",
            "eventgen_object": "ManualEventGen",
            "output_filename": "Fault_check_out.txt"
        }
        # Add reliabilty metrics object
        tree[str(biggestKey * 10 + 3)] = {
            "object": "metrics",
            "name": "RelMetrics",
            "report_file": "Metrics_Output.csv",
            "module_metrics_object": "PwrMetrics",
            "metrics_of_interest": '"SAIFI,SAIDI,CAIDI,ASAI,MAIFI"',
            "customer_group": '"groupid=METERTEST"',
            "metric_interval": "5 h",
            "report_interval": "5 h"
        }
        # Add power_metrics object
        tree[str(biggestKey * 10 + 4)] = {
            "object": "power_metrics",
            "name": "PwrMetrics",
            "base_time_value": "1 h"
        }

        # HACK: set groupid for all meters so outage stats are collected.
        noMeters = True
        for key in tree:
            if tree[key].get('object', '') in ['meter', 'triplex_meter']:
                tree[key]['groupid'] = "METERTEST"
                noMeters = False
        if noMeters:
            raise Exception(
                "No meters detected on the circuit. Please add at least one meter to allow for collection of outage statistics."
            )
    for key in tree:
        if 'clock' in tree[key]:
            tree[key]['starttime'] = "'" + CLOCK_START + "'"
            tree[key]['stoptime'] = "'" + CLOCK_END + "'"

    # dictionary to hold info on lines present in glm
    edge_bools = dict.fromkeys([
        'underground_line', 'overhead_line', 'triplex_line', 'transformer',
        'regulator', 'fuse', 'switch'
    ], False)
    # Map to speed up name lookups.
    nameToIndex = {tree[key].get('name', ''): key for key in tree.keys()}
    # Get rid of schedules and climate and check for all edge types:
    for key in list(tree.keys()):
        obtype = tree[key].get("object", "")
        if obtype == 'underground_line':
            edge_bools['underground_line'] = True
        elif obtype == 'overhead_line':
            edge_bools['overhead_line'] = True
        elif obtype == 'triplex_line':
            edge_bools['triplex_line'] = True
        elif obtype == 'transformer':
            edge_bools['transformer'] = True
        elif obtype == 'regulator':
            edge_bools['regulator'] = True
        elif obtype == 'fuse':
            edge_bools['fuse'] = True
        elif obtype == 'switch':
            edge_bools['switch'] = True
        if tree[key].get("argument",
                         "") == "\"schedules.glm\"" or tree[key].get(
                             "tmyfile", "") != "":
            del tree[key]
    # Make sure we have a voltage dump and current dump:
    tree[str(biggestKey * 10 + 5)] = {
        "object": "voltdump",
        "filename": "voltDump.csv"
    }
    tree[str(biggestKey * 10 + 6)] = {
        "object": "currdump",
        "filename": "currDump.csv"
    }
    # Line rating dumps
    tree[feeder.getMaxKey(tree) + 1] = {'module': 'tape'}
    for key in edge_bools.keys():
        if edge_bools[key]:
            tree[feeder.getMaxKey(tree) + 1] = {
                'object': 'group_recorder',
                'group': '"class=' + key + '"',
                'property': 'continuous_rating',
                'file': key + '_cont_rating.csv'
            }
    #Record initial status readout of each fuse/recloser/switch/sectionalizer before running
    # Reminder: fuse objects have 'phase_X_status' instead of 'phase_X_state'
    protDevices = dict.fromkeys(
        ['fuse', 'recloser', 'switch', 'sectionalizer'], False)
    #dictionary of protective device initial states for each phase
    protDevInitStatus = {}
    #dictionary of protective devices final states for each phase after running Gridlab-D
    protDevFinalStatus = {}
    #dictionary of protective device types to help the testing and debugging process
    protDevTypes = {}
    protDevOpModes = {}
    for key in tree:
        obj = tree[key]
        obType = obj.get('object')
        if obType in protDevices.keys():
            obName = obj.get('name', '')
            protDevTypes[obName] = obType
            if obType != 'fuse':
                protDevOpModes[obName] = obj.get('operating_mode',
                                                 'INDIVIDUAL')
            protDevices[obType] = True
            protDevInitStatus[obName] = {}
            protDevFinalStatus[obName] = {}
            for phase in ['A', 'B', 'C']:
                if obType != 'fuse':
                    phaseState = obj.get('phase_' + phase + '_state', 'CLOSED')
                else:
                    phaseState = obj.get('phase_' + phase + '_status', 'GOOD')
                if phase in obj.get('phases', ''):
                    protDevInitStatus[obName][phase] = phaseState
    #print protDevInitStatus

    #Create a recorder for protective device states
    for key in protDevices.keys():
        if protDevices[key]:
            for phase in ['A', 'B', 'C']:
                if key != 'fuse':
                    tree[feeder.getMaxKey(tree) + 1] = {
                        'object': 'group_recorder',
                        'group': '"class=' + key + '"',
                        'property': 'phase_' + phase + '_state',
                        'file': key + '_phase_' + phase + '_state.csv'
                    }
                else:
                    tree[feeder.getMaxKey(tree) + 1] = {
                        'object': 'group_recorder',
                        'group': '"class=' + key + '"',
                        'property': 'phase_' + phase + '_status',
                        'file': key + '_phase_' + phase + '_state.csv'
                    }

    # Run Gridlab.
    if not workDir:
        workDir = tempfile.mkdtemp()
        print('@@@@@@', workDir)
    # for i in range(6):
    # 	gridlabOut = gridlabd.runInFilesystem(tree, attachments=attachments, workDir=workDir)
    # 	#HACK: workaround for shoddy macOS gridlabd build.
    # 	if 'error when setting parent' not in gridlabOut.get('stderr','OOPS'):
    # 		break
    gridlabOut = gridlabd.runInFilesystem(tree,
                                          attachments=attachments,
                                          workDir=workDir)

    #Record final status readout of each fuse/recloser/switch/sectionalizer after running
    try:
        for key in protDevices.keys():
            if protDevices[key]:
                for phase in ['A', 'B', 'C']:
                    with open(pJoin(workDir,
                                    key + '_phase_' + phase + '_state.csv'),
                              newline='') as statusFile:
                        reader = csv.reader(statusFile)
                        # loop past the header,
                        keys = []
                        vals = []
                        for row in reader:
                            if '# timestamp' in row:
                                keys = row
                                i = keys.index('# timestamp')
                                keys.pop(i)
                                vals = next(reader)
                                vals.pop(i)
                        for pos, key2 in enumerate(keys):
                            protDevFinalStatus[key2][phase] = vals[pos]
    except:
        pass
    #print protDevFinalStatus
    #compare initial and final states of protective devices
    #quick compare to see if they are equal
    #print cmp(protDevInitStatus, protDevFinalStatus)
    #find which values changed
    changedStates = {}
    #read voltDump values into a dictionary.
    try:
        with open(pJoin(workDir, 'voltDump.csv'), newline='') as dumpFile:
            reader = csv.reader(dumpFile)
            next(reader)  # Burn the header.
            keys = next(reader)
            voltTable = []
            for row in reader:
                rowDict = {}
                for pos, key in enumerate(keys):
                    rowDict[key] = row[pos]
                voltTable.append(rowDict)
    except:
        raise Exception(
            'GridLAB-D failed to run with the following errors:\n' +
            gridlabOut['stderr'])
    # read currDump values into a dictionary
    with open(pJoin(workDir, 'currDump.csv'), newline='') as currDumpFile:
        reader = csv.reader(currDumpFile)
        next(reader)  # Burn the header.
        keys = next(reader)
        currTable = []
        for row in reader:
            rowDict = {}
            for pos, key in enumerate(keys):
                rowDict[key] = row[pos]
            currTable.append(rowDict)
    # read line rating values into a single dictionary
    lineRatings = {}
    rating_in_VA = []
    for key1 in edge_bools.keys():
        if edge_bools[key1]:
            with open(pJoin(workDir, key1 + '_cont_rating.csv'),
                      newline='') as ratingFile:
                reader = csv.reader(ratingFile)
                # loop past the header,
                keys = []
                vals = []
                for row in reader:
                    if '# timestamp' in row:
                        keys = row
                        i = keys.index('# timestamp')
                        keys.pop(i)
                        vals = next(reader)
                        vals.pop(i)
                for pos, key2 in enumerate(keys):
                    lineRatings[key2] = abs(float(vals[pos]))
    #edgeTupleRatings = lineRatings copy with to-from tuple as keys for labeling
    edgeTupleRatings = {}
    for edge in lineRatings:
        for obj in tree.values():
            if obj.get('name', '').replace('"', '') == edge:
                nodeFrom = obj.get('from')
                nodeTo = obj.get('to')
                coord = (nodeFrom, nodeTo)
                ratingVal = lineRatings.get(edge)
                edgeTupleRatings[coord] = ratingVal
    # Calculate average node voltage deviation. First, helper functions.
    def digits(x):
        ''' Returns number of digits before the decimal in the float x. '''
        return math.ceil(math.log10(x + 1))

    def avg(l):
        ''' Average of a list of ints or floats. '''
        # HACK: add a small value to the denominator to avoid divide by zero for out of service locations (i.e. zero voltage).
        return sum(l) / (len(l) + 0.00000000000000001)

    # Detect the feeder nominal voltage:
    for key in tree:
        ob = tree[key]
        if type(ob) == dict and ob.get('bustype', '') == 'SWING':
            feedVoltage = float(ob.get('nominal_voltage', 1))
    # Tot it all up.
    nodeVolts = {}
    nodeVoltsPU = {}
    nodeVoltsPU120 = {}
    voltImbalances = {}
    for row in voltTable:
        allVolts = []
        allVoltsPU = []
        allDiffs = []
        nodeName = row.get('node_name', '')
        for phase in ['A', 'B', 'C']:
            realVolt = abs(float(row['volt' + phase + '_real']))
            imagVolt = abs(float(row['volt' + phase + '_imag']))
            phaseVolt = math.sqrt((realVolt**2) + (imagVolt**2))
            if phaseVolt != 0.0:
                treeKey = nameToIndex.get(nodeName, 0)
                nodeObj = tree.get(treeKey, {})
                try:
                    nominal_voltage = float(nodeObj['nominal_voltage'])
                except:
                    nominal_voltage = feedVoltage
                allVolts.append(phaseVolt)
                normVolt = (phaseVolt / nominal_voltage)
                allVoltsPU.append(normVolt)
        avgVolts = avg(allVolts)
        avgVoltsPU = avg(allVoltsPU)
        avgVoltsPU120 = 120 * avgVoltsPU
        nodeVolts[nodeName] = float("{0:.2f}".format(avgVolts))
        nodeVoltsPU[nodeName] = float("{0:.2f}".format(avgVoltsPU))
        nodeVoltsPU120[nodeName] = float("{0:.2f}".format(avgVoltsPU120))
        if len(allVolts) == 3:
            voltA = allVolts.pop()
            voltB = allVolts.pop()
            voltC = allVolts.pop()
            allDiffs.append(abs(float(voltA - voltB)))
            allDiffs.append(abs(float(voltA - voltC)))
            allDiffs.append(abs(float(voltB - voltC)))
            maxDiff = max(allDiffs)
            voltImbal = maxDiff / avgVolts
            voltImbalances[nodeName] = float("{0:.2f}".format(voltImbal))
        # Use float("{0:.2f}".format(avg(allVolts))) if displaying the node labels
    nodeLoadNames = {}
    nodeNames = {}
    for key in nodeVolts.keys():
        nodeNames[key] = key
        if key == loadLoc:
            nodeLoadNames[key] = "LOAD: " + key
    # find edge currents by parsing currdump
    edgeCurrentSum = {}
    edgeCurrentMax = {}
    for row in currTable:
        allCurr = []
        for phase in ['A', 'B', 'C']:
            realCurr = abs(float(row['curr' + phase + '_real']))
            imagCurr = abs(float(row['curr' + phase + '_imag']))
            phaseCurr = math.sqrt((realCurr**2) + (imagCurr**2))
            allCurr.append(phaseCurr)
        edgeCurrentSum[row.get('link_name', '')] = sum(allCurr)
        edgeCurrentMax[row.get('link_name', '')] = max(allCurr)
    # When just showing current as labels, use sum of the three lines' current values, when showing the per unit values (current/rating), use the max of the three
    #edgeTupleCurrents = edgeCurrents copy with to-from tuple as keys for labeling
    edgeTupleCurrents = {}
    #edgeValsPU = values normalized per unit by line ratings
    edgeValsPU = {}
    #edgeTupleValsPU = edgeValsPU copy with to-from tuple as keys for labeling
    edgeTupleValsPU = {}
    #edgeTuplePower = dict with to-from tuples as keys and sim power as values for debugging
    edgeTuplePower = {}
    #edgeTupleNames = dict with to-from tuples as keys and names as values for debugging
    edgeTupleNames = {}
    #edgeTupleFaultNames = dict with to-from tuples as keys and the name of the Fault as the only value
    edgeTupleFaultNames = {}
    #edgeTupleProtDevs = dict with to-from tuples as keys and the initial of the type of protective device as the value
    edgeTupleProtDevs = {}
    #linePhases = dictionary containing the number of phases on each line for line-width purposes
    linePhases = {}
    edgePower = {}
    for edge in edgeCurrentSum:
        for obj in tree.values():
            obname = obj.get('name', '').replace('"', '')
            if obname == edge:
                objType = obj.get('object')
                nodeFrom = obj.get('from')
                nodeTo = obj.get('to')
                coord = (nodeFrom, nodeTo)
                currVal = edgeCurrentSum.get(edge)
                voltVal = avg([nodeVolts.get(nodeFrom), nodeVolts.get(nodeTo)])
                power = (currVal * voltVal) / 1000
                lineRating = lineRatings.get(edge, 10.0**9)
                edgePerUnitVal = (edgeCurrentMax.get(edge)) / lineRating
                edgeTupleCurrents[coord] = "{0:.2f}".format(currVal)
                edgeTuplePower[coord] = "{0:.2f}".format(power)
                edgePower[edge] = power
                edgeValsPU[edge] = edgePerUnitVal
                edgeTupleValsPU[coord] = "{0:.2f}".format(edgePerUnitVal)
                edgeTupleNames[coord] = edge
                if faultLoc == edge:
                    edgeTupleFaultNames[coord] = "FAULT: " + edge
                phaseStr = obj.get('phases', '').replace('"', '').replace(
                    'N', '').replace('S', '')
                numPhases = len(phaseStr)
                if (numPhases < 1) or (numPhases > 3):
                    numPhases = 1
                linePhases[edge] = numPhases
                protDevLabel = ""
                protDevBlownStr = ""
                if objType in protDevices.keys():
                    for phase in protDevFinalStatus[obname].keys():
                        if objType == 'fuse':
                            if protDevFinalStatus[obname][phase] == "BLOWN":
                                protDevBlownStr = "!"
                        else:
                            if protDevFinalStatus[obname][phase] == "OPEN":
                                protDevBlownStr = "!"
                if objType == 'fuse':
                    protDevLabel = 'F'
                elif objType == 'switch':
                    protDevLabel = 'S'
                elif objType == 'recloser':
                    protDevLabel = 'R'
                elif objType == 'sectionalizer':
                    protDevLabel = 'X'
                edgeTupleProtDevs[coord] = protDevLabel + protDevBlownStr
    #define which dict will be used for edge line color
    edgeColors = edgeValsPU
    #define which dict will be used for edge label
    edgeLabels = edgeTupleValsPU
    # Build the graph.
    fGraph = feeder.treeToNxGraph(tree)
    # TODO: consider whether we can set figsize dynamically.
    wlVal = int(math.sqrt(float(rezSqIn)))
    voltChart = plt.figure(figsize=(wlVal, wlVal))
    plt.axes(frameon=0)
    plt.axis('off')
    voltChart.gca().set_aspect('equal')
    plt.tight_layout()
    #set axes step equal
    if neatoLayout:
        # HACK: work on a new graph without attributes because graphViz tries to read attrs.
        cleanG = nx.Graph(fGraph.edges())
        cleanG.add_nodes_from(fGraph)
        positions = graphviz_layout(cleanG, prog='neato')
    else:
        remove_nodes = [
            n for n in fGraph if fGraph.nodes[n].get('pos', (0, 0)) == (0, 0)
        ]
        fGraph.remove_nodes_from(remove_nodes)
        positions = {n: fGraph.nodes[n].get('pos', (0, 0)) for n in fGraph}
    # Need to get edge names from pairs of connected node names.
    edgeNames = []
    for e in fGraph.edges():
        edgeNames.append((fGraph.edges[e].get('name',
                                              'BLANK')).replace('"', ''))
    #create custom colormap
    if customColormap:
        if scaleMin != None and scaleMax != None:
            scaleDif = scaleMax - scaleMin
            custom_cm = matplotlib.colors.LinearSegmentedColormap.from_list(
                'custColMap', [(scaleMin, 'blue'),
                               (scaleMin + (0.12 * scaleDif), 'darkgray'),
                               (scaleMin + (0.56 * scaleDif), 'darkgray'),
                               (scaleMin + (0.8 * scaleDif), 'red')])
            vmin = scaleMin
            vmax = scaleMax
        else:
            custom_cm = matplotlib.colors.LinearSegmentedColormap.from_list(
                'custColMap', [(0.0, 'blue'), (0.15, 'darkgray'),
                               (0.7, 'darkgray'), (1.0, 'red')])
            vmin = 0
            vmax = 1.25
        custom_cm.set_under(color='black')
    else:
        custom_cm = plt.cm.get_cmap('viridis')
        if scaleMin != None and scaleMax != None:
            vmin = scaleMin
            vmax = scaleMax
        else:
            vmin = None
            vmax = None
    drawColorbar = False
    emptyColors = {}
    #draw edges with or without colors
    if edgeCol != None:
        drawColorbar = True
        if edgeCol == "Current":
            edgeList = [edgeCurrentSum.get(n, 1) for n in edgeNames]
            drawColorbar = True
        elif edgeCol == "Power":
            edgeList = [edgePower.get(n, 1) for n in edgeNames]
            drawColorbar = True
        elif edgeCol == "Rating":
            edgeList = [lineRatings.get(n, 10.0**9) for n in edgeNames]
            drawColorbar = True
        elif edgeCol == "PercentOfRating":
            edgeList = [edgeValsPU.get(n, .5) for n in edgeNames]
            drawColorbar = True
        else:
            edgeList = [emptyColors.get(n, .6) for n in edgeNames]
            print(
                "WARNING: edgeCol property must be 'Current', 'Power', 'Rating', 'PercentOfRating', or None"
            )
    else:
        edgeList = [emptyColors.get(n, .6) for n in edgeNames]
    edgeIm = nx.draw_networkx_edges(
        fGraph,
        pos=positions,
        edge_color=edgeList,
        width=[linePhases.get(n, 1) for n in edgeNames],
        edge_cmap=custom_cm)
    #draw edge labels
    if edgeLabs != None:
        if edgeLabs == "Name":
            edgeLabels = edgeTupleNames
        elif edgeLabs == "Fault":
            edgeLabels = edgeTupleFaultNames
        elif edgeLabs == "Value":
            if edgeCol == "Current":
                edgeLabels = edgeTupleCurrents
            elif edgeCol == "Power":
                edgeLabels = edgeTuplePower
            elif edgeCol == "Rating":
                edgeLabels = edgeTupleRatings
            elif edgeCol == "PercentOfRating":
                edgeLabels = edgeTupleValsPU
            else:
                edgeLabels = None
                print(
                    "WARNING: edgeCol property cannot be set to None when edgeLabs property is set to 'Value'"
                )
        elif edgeLabs == "ProtDevs":
            edgeLabels = edgeTupleProtDevs
        else:
            edgeLabs = None
            print(
                "WARNING: edgeLabs property must be either 'Name', 'Value', or None"
            )
    if edgeLabs != None:
        edgeLabelsIm = nx.draw_networkx_edge_labels(fGraph,
                                                    pos=positions,
                                                    edge_labels=edgeLabels,
                                                    font_size=8)
    # draw nodes with or without color
    if nodeCol != None:
        if nodeCol == "Voltage":
            nodeList = [nodeVolts.get(n, 1) for n in fGraph.nodes()]
            drawColorbar = True
        elif nodeCol == "VoltageImbalance":
            nodeList = [voltImbalances.get(n, 1) for n in fGraph.nodes()]
            drawColorbar = True
        elif nodeCol == "perUnitVoltage":
            nodeList = [nodeVoltsPU.get(n, .5) for n in fGraph.nodes()]
            drawColorbar = True
        elif nodeCol == "perUnit120Voltage":
            nodeList = [nodeVoltsPU120.get(n, 120) for n in fGraph.nodes()]
            drawColorbar = True
        else:
            nodeList = [emptyColors.get(n, 1) for n in fGraph.nodes()]
            print(
                "WARNING: nodeCol property must be 'Voltage', 'VoltageImbalance', 'perUnitVoltage', 'perUnit120Voltage', or None"
            )
    else:
        nodeList = [emptyColors.get(n, .6) for n in fGraph.nodes()]

    nodeIm = nx.draw_networkx_nodes(fGraph,
                                    pos=positions,
                                    node_color=nodeList,
                                    linewidths=0,
                                    node_size=30,
                                    vmin=vmin,
                                    vmax=vmax,
                                    cmap=custom_cm)
    #draw node labels
    nodeLabels = {}
    if nodeLabs != None:
        if nodeLabs == "Name":
            nodeLabels = nodeNames
        elif nodeLabs == "Value":
            if nodeCol == "Voltage":
                nodeLabels = nodeVolts
            elif nodeCol == "VoltageImbalance":
                nodeLabels = voltImbalances
            elif nodeCol == "perUnitVoltage":
                nodeLabels = nodeVoltsPU
            elif nodeCol == "perUnit120Voltage":
                nodeLabels = nodeVoltsPU120
            else:
                nodeLabels = None
                print(
                    "WARNING: nodeCol property cannot be set to None when nodeLabs property is set to 'Value'"
                )
        #HACK: add hidden node label option for displaying specified load name
        elif nodeLabs == "Load":
            nodeLabels = nodeLoadNames
        else:
            nodeLabs = None
            print(
                "WARNING: nodeLabs property must be either 'Name', 'Value', or None"
            )
    if nodeLabs != None:
        nodeLabelsIm = nx.draw_networkx_labels(fGraph,
                                               pos=positions,
                                               labels=nodeLabels,
                                               font_size=8)
    plt.sci(nodeIm)
    # plt.clim(110,130)
    if drawColorbar:
        plt.colorbar()
    return voltChart
Exemplo n.º 19
0
def viz(pathToOmdOrGlm, forceLayout=False, outputPath=None):
    ''' Vizualize a distribution system.'''
    # HACK: make sure we have our homebrew binaries available.
    os.environ['PATH'] += os.pathsep + '/usr/local/bin'
    # Load in the feeder.
    with open(pathToOmdOrGlm, 'r') as feedFile:
        if pathToOmdOrGlm.endswith('.omd'):
            thisFeed = {
                'tree': json.load(feedFile)['tree']
            }  # TODO: later bring back attachments.
        elif pathToOmdOrGlm.endswith('.glm'):
            thisFeed = {'tree': feeder.parse(pathToOmdOrGlm, filePath=True)}
        tree = thisFeed['tree']
    # If there is zero lat/lon info, do force layout by default.
    latLonCount = 0
    for key in tree:
        for subKey in ['latitude', 'longitude']:
            if subKey in tree[key]:
                latLonCount += 1
    if latLonCount == 0:
        forceLayout = True
    # Force layout of feeders with no lat/lon information so we can actually see what's there.
    if forceLayout:
        print "Force laying out the graph..."
        # Use graphviz to lay out the graph.
        inGraph = feeder.treeToNxGraph(tree)
        # HACK: work on a new graph without attributes because graphViz tries to read attrs.
        cleanG = nx.Graph(inGraph.edges())
        # HACK2: might miss nodes without edges without the following.
        cleanG.add_nodes_from(inGraph)
        pos = nx.nx_agraph.graphviz_layout(cleanG, prog='neato')
        # # Charting the feeder in matplotlib:
        # feeder.latLonNxGraph(inGraph, labels=False, neatoLayout=True, showPlot=True)
        # Insert the latlons.
        for key in tree:
            obName = tree[key].get('name', '')
            thisPos = pos.get(obName, None)
            if thisPos != None:
                tree[key]['longitude'] = thisPos[0]
                tree[key]['latitude'] = thisPos[1]
    # Set up temp directory and copy the feeder and viewer in to it.
    if outputPath == None:
        tempDir = tempfile.mkdtemp()
    else:
        tempDir = outputPath
    #HACK: make sure we get the required files from the right place.
    SOURCE_DIR = os.path.dirname(__file__) + '/'
    shutil.copy(SOURCE_DIR + '/distNetViz.html', tempDir + '/viewer.html')
    shutil.copy(SOURCE_DIR + '/svg-pan-zoom.js', tempDir + '/svg-pan-zoom.js')
    # Grab the library we need.
    with open(SOURCE_DIR + 'svg-pan-zoom.js', 'r') as pzFile:
        pzData = pzFile.read()
    # Rewrite the load lines in viewer.html
    # Note: you can't juse open the file in r+ mode because, based on the way the file is mapped to memory, you can only overwrite a line with another of exactly the same length.
    for line in fileinput.input(tempDir + '/viewer.html', inplace=1):
        if line.lstrip().startswith("<script id='feederLoadScript''>"):
            print ""  # Remove the existing load.
        elif line.lstrip().startswith("<script id='feederInsert'>"):
            print "<script id='feederInsert'>\ntestFeeder=" + json.dumps(
                thisFeed, indent=4)  # load up the new feeder.
        elif line.lstrip().startswith("<script id='panZoomInsert'>"):
            print "<script id='panZoomInsert'>\n" + pzData  # load up the new feeder.
        else:
            print line.rstrip()
    # os.system('open -a "Google Chrome" ' + '"file://' + tempDir + '/viewer.html"')
    webbrowser.open_new("file://" + tempDir + '/viewer.html')
Exemplo n.º 20
0
def drawTable(path, workDir=None):
    #return self.log

    # warnings.filterwarnings("ignore")
    if path.endswith('.glm'):
        tree = feeder.parse(path)
        attachments = []
    elif path.endswith('.omd'):
        with open(path) as f:
            omd = json.load(f)
        tree = omd.get('tree', {})
        attachments = omd.get('attachments', [])
    else:
        raise Exception('Invalid input file type. We require a .glm or .omd.')

    # Reminder: fuse objects have 'phase_X_status' instead of 'phase_X_state'
    protDevices = dict.fromkeys(
        ['fuse', 'recloser', 'switch', 'sectionalizer'], False)
    #dictionary of protective device initial states for each phase
    protDevInitStatus = {}
    #dictionary of protective devices final states for each phase after running Gridlab-D
    protDevFinalStatus = {}
    #dictionary of protective device types to help the testing and debugging process
    protDevTypes = {}
    protDevOpModes = {}
    for key in tree:
        obj = tree[key]
        obType = obj.get('object')
        if obType in protDevices.keys():
            obName = obj.get('name', '')
            protDevTypes[obName] = obType
            if obType != 'fuse':
                protDevOpModes[obName] = obj.get('operating_mode',
                                                 'INDIVIDUAL')
            protDevices[obType] = True
            protDevInitStatus[obName] = {}
            protDevFinalStatus[obName] = {}
            for phase in ['A', 'B', 'C']:
                if obType != 'fuse':
                    phaseState = obj.get('phase_' + phase + '_state', 'CLOSED')
                else:
                    phaseState = obj.get('phase_' + phase + '_status', 'GOOD')
                if phase in obj.get('phases', ''):
                    protDevInitStatus[obName][phase] = phaseState

    for key in protDevices.keys():
        if protDevices[key]:
            for phase in ['A', 'B', 'C']:
                with open(pJoin(workDir,
                                key + '_phase_' + phase + '_state.csv'),
                          newline='') as statusFile:
                    reader = csv.reader(statusFile)
                    # loop past the header,
                    keys = []
                    vals = []
                    for row in reader:
                        if '# timestamp' in row:
                            keys = row
                            i = keys.index('# timestamp')
                            keys.pop(i)
                            vals = next(reader)
                            vals.pop(i)
                    for pos, key2 in enumerate(keys):
                        protDevFinalStatus[key2][phase] = vals[pos]

    html_str = """
		<table cellpadding="0" cellspacing="0">
			<thead>
				<tr>
					<th>Protective Device Name</th>
					<th>Device Type</th>
					<th>Initial States</th>
					<th>Final States</th>
					<th>Changes</th>
				</tr>
			</thead>
			<tbody>"""
    for device in protDevInitStatus.keys():
        row_str = "<tr><td>" + device + "</td><td>"
        devType = protDevTypes[device]
        if devType == 'fuse':
            row_str += "Fuse (F)</td><td>"
        elif devType == 'switch':
            row_str += "Switch (S)</td><td>"
        elif devType == 'recloser':
            row_str += "Recloser (R)</td><td>"
        elif devType == 'sectionalizer':
            row_str += "Sectionalizer (X)</td><td>"
        else:
            row_str += "Unknown</td><td>"
        for phase in protDevInitStatus[device].keys():
            row_str += "Phase " + phase + " = " + protDevInitStatus[device][
                phase] + "</br>"
        row_str += "</td><td>"
        for phase in protDevFinalStatus[device].keys():
            row_str += "Phase " + phase + " = " + protDevFinalStatus[device][
                phase] + "</br>"
        row_str += "</td>"
        noChange = True
        change_str = ""
        for phase in protDevFinalStatus[device].keys():
            try:
                if protDevInitStatus[device][phase] != protDevFinalStatus[
                        device][phase]:
                    change_str += "Phase " + phase + " : " + protDevInitStatus[
                        device][phase] + " -> " + protDevFinalStatus[device][
                            phase] + "</br>"
                    noChange = False
            except:
                pass  #key error...
        if noChange:
            row_str += "<td>No Change"
        else:
            row_str += "<td style=\"color: red;\">"
            row_str += change_str
        row_str += "</td></tr>"
        html_str += row_str
    html_str += """</tbody></table>"""
    return html_str
Exemplo n.º 21
0
import omf.feeder as feeder

feed = feeder.parse('./Taxonomy_Feeders-solar/GC-12.47-1-solarAdd.glm')
feeder.attachRecorders(feed, 'Inverter', 'object', 'inverter')

output = open('./taxo_temp/GC-12.47-1-solarAdd.glm', 'w')
output.write(feeder.write(feed))
output.close()

# phaseA_V_Out, phaseB_V_Out, phaseC_V_Out, phaseA_I_Out, phaseB_I_Out, phaseC_I_Out;
Exemplo n.º 22
0
def read_glm_files(directory):
    """ Read each .glm file and return a dictionary of dictionaries
	"""
    os.chdir(directory)
    return [feeder.parse(file_path) for file_path in glob.glob("*.glm")]
Exemplo n.º 23
0
from omf import feeder
from helperfuncs import *

if "parsedFeeder" not in globals():
	# Parsing the feeder takes a long time, so I don't want to do it every time I modify and re-run this script
	print "parsing RectorAlphaPhases.glm"
	parsedFeeder = feeder.parse("RectorAlphaPhases.glm")


newtree = {}
print "Parsing done, converting dict"
for guid, t in parsedFeeder.items():
	treeObj = dictcopy(t)
	if treeObj.get("name") and treeObj.get("phases"):
		treeObj["id"] = guid
		newtree[treeObj["name"]] = treeObj
print "Done"

links = {k:v for k, v in newtree.items() if v.get("from") and v.get("to")}

problemlinks = []
print "Finding problem links"
for lname, ldata in links.items():
	fromnode = newtree[ldata["from"]]
	tonode = newtree[ldata["to"]]
	if gp(ldata) != gp(fromnode) or gp(ldata) != gp(tonode):
		problemlinks.append({
			"fromnode":fromnode,
			"ldata":ldata,
			"tonode":tonode
			})
Exemplo n.º 24
0
from omf import feeder
from helperfuncs import *

if "parsedFeeder" not in globals():
    # Parsing the feeder takes a long time, so I don't want to do it every time I modify and re-run this script
    print "parsing RectorAlphaPhases.glm"
    parsedFeeder = feeder.parse("RectorAlphaPhases.glm")

newtree = {}
print "Parsing done, converting dict"
for guid, t in parsedFeeder.items():
    treeObj = dictcopy(t)
    if treeObj.get("name") and treeObj.get("phases"):
        treeObj["id"] = guid
        newtree[treeObj["name"]] = treeObj
print "Done"

links = {k: v for k, v in newtree.items() if v.get("from") and v.get("to")}

problemlinks = []
print "Finding problem links"
for lname, ldata in links.items():
    fromnode = newtree[ldata["from"]]
    tonode = newtree[ldata["to"]]
    if gp(ldata) != gp(fromnode) or gp(ldata) != gp(tonode):
        problemlinks.append({
            "fromnode": fromnode,
            "ldata": ldata,
            "tonode": tonode
        })
Exemplo n.º 25
0
from os.path import basename
import os

# TODO: Put into callable function, with glmFile and playerFolderPath as input
glmFile = '../static/testFiles/solarToNegLoads.glm'
playerFolderPath = '../static/testFiles/solarToNegLoadPlayerFiles/'
solarObjs = []
dieselObjs = []
solarKeys = []
inverterKeys = []
inverters = []
meterKeys = []
meterNames = []
meters = []
invs = []
tree = feeder.parse(glmFile)
# Find solar objects
for row in tree:
	if 'object' in tree[row].keys():
		if tree[row]['object'] == 'solar':
			solarObjs.append(tree[row])
			solarKeys.append(row)
			inverters.append(tree[row]['parent'])
# Find inverters of solar objs
for row in tree:
	if 'object' in tree[row].keys():
		if tree[row]['object'] == 'inverter':
			if tree[row]['name'] in inverters:
				inverterKeys.append(row)
				invs.append(tree[row])
				meterNames.append(tree[row]['parent'])
Exemplo n.º 26
0
def viz(pathToOmdOrGlm,
        forceLayout=False,
        outputPath=None,
        outputName='viewer.html',
        open_file=True):
    ''' Vizualize a distribution system.'''
    # HACK: make sure we have our homebrew binaries available.
    os.environ['PATH'] += os.pathsep + '/usr/local/bin'
    # Load in the feeder.
    with open(pathToOmdOrGlm, 'r') as feedFile:
        if pathToOmdOrGlm.endswith('.omd'):
            thisFeed = {
                'tree': json.load(feedFile)['tree']
            }  # TODO: later bring back attachments.
        elif pathToOmdOrGlm.endswith('.glm'):
            thisFeed = {'tree': feeder.parse(pathToOmdOrGlm, filePath=True)}
        tree = thisFeed['tree']
    ## Force layout of feeders with no lat/lon information so we can actually see what's there.
    if forceLayout:
        print(
            'forceLayout was set to True, so force layout is applied regardless of coordinate detection.'
        )
        insert_coordinates(tree)
    elif not contains_coordinates(tree):
        print(
            'Warning: no lat/lon coordinates detected, so force layout required.'
        )
        insert_coordinates(tree)
    # Set up temp directory and copy the feeder and viewer in to it.
    if outputPath is None:
        tempDir = tempfile.mkdtemp()
    else:
        tempDir = os.path.abspath(outputPath)
    #HACK: make sure we get the required files from the right place.
    # shutil.copy(omf.omfDir + '/templates/distNetViz.html', tempDir + '/' + outputName)
    # shutil.copy(omf.omfDir + '/static/svg-pan-zoom.js', tempDir + '/svg-pan-zoom.js')
    # Grab the library we need.
    with open(omf.omfDir + '/static/svg-pan-zoom.js', 'r') as pzFile:
        pzData = pzFile.read()
    with open(omf.omfDir + '/static/chroma.min.js', 'r') as chromaFile:
        chromaData = chromaFile.read()
    with open(omf.omfDir + '/static/papaparse.min.js', 'r') as papaFile:
        papaData = papaFile.read()
    with open(omf.omfDir + '/static/jquery.js', 'r') as jquery_file:
        jquery_data = jquery_file.read()
    with open(omf.omfDir + '/static/jquery-ui.min.js', 'r') as jquery_ui_file:
        jquery_ui_data = jquery_ui_file.read()
    with open(omf.omfDir + '/static/jquery-ui.min.css',
              'r') as jquery_css_file:
        jquery_css_data = jquery_css_file.read()
    # TEMPLATE HACKING
    from jinja2 import Template
    templateCont = open(omf.omfDir + '/templates/distNetViz.html', 'r+').read()
    templateString = templateCont.encode('utf-8')
    template = Template(templateString)

    def id():
        return ""

    component_json = get_components()
    rend = template.render(thisFeederData=json.dumps(thisFeed),
                           thisFeederName=pathToOmdOrGlm,
                           thisFeederNum=1,
                           thisModelName="Local Filesystem",
                           thisOwner="NONE",
                           components=component_json,
                           jasmine=None,
                           spec=None,
                           publicFeeders=[],
                           userFeeders=[],
                           csrf_token=id,
                           showFileMenu=True,
                           currentUser=None)
    with open(tempDir + '/' + outputName, 'w') as outFile:
        outFile.write(rend)
    # Insert the panZoom library.
    # Note: you can't juse open the file in r+ mode because, based on the way the file is mapped to memory, you can only overwrite a line with another of exactly the same length.
    for line in fileinput.input(tempDir + '/' + outputName, inplace=1):
        if line.lstrip().startswith(
                '<link rel="stylesheet" href="/static/jquery-ui.min.css">'):
            print ""
        elif line.lstrip().startswith(
                '<script type="text/javascript" src="/static/jquery.js"></script>'
        ):
            print ""
        elif line.lstrip().startswith(
                '<script type="text/javascript" src="/static/jquery-ui.min.js"></script>'
        ):
            print ""
        elif line.lstrip().startswith(
                '<script type="text/javascript" src="/static/svg-pan-zoom.js"></script>'
        ):
            print ""
        elif line.lstrip().startswith(
                '<script type="text/javascript" src="/static/chroma.min.js"></script>'
        ):
            print ""
        elif line.lstrip().startswith(
                '<script type="text/javascript" src="/static/papaparse.min.js"></script>'
        ):
            print ""
        elif line.lstrip().startswith(
                '<link rel="shortcut icon" href="/static/favicon.ico"/>'):
            print(
                '<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAQAAAAAAAoAQAAFgAAACgAAAAQAAAAIAAAAAEABAAAAAAAgAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAioqKAGlpaQDU1NQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIAAgACAAIAAgACAzIzMjMyMwIDAgMCAwIDAiIiIiIiIgMCAwEDAgMCAwIDMTMyMzIzAgMBAwIDAgMCIiIiIiIiAwIDAQMCAwIDAgMxMzIzMjMCAwEDAgMCAwIiIiIiIiIDAAMAAwADAAMAAzMzMzMzMwAAAAAAAAAAAABwAAd3cAAEABAABVVQAAAAUAAFVVAABAAQAAVVUAAAAFAABVVQAAQAEAAFVVAAAABQAA3d0AAMABAAD//wAA"/>'
            )
        elif line.lstrip().startswith('<script id="panZoomInsert">'):
            print '<script id="panZoomInsert">\n' + pzData  # load up the new feeder.
        elif line.lstrip().startswith('<script id="chromaInsert">'):
            print '<script id="chromaInsert">\n' + chromaData
        elif line.lstrip().startswith('<script id="papaParseInsert">'):
            print '<script id="papaParseInsert">\n' + papaData
        elif line.lstrip().startswith('<script id="jqueryInsert">'):
            print '<script id="jqueryInsert">\n' + jquery_data
        elif line.lstrip().startswith('<script id="jqueryUiInsert">'):
            print '<script id="jqueryUiInsert">\n' + jquery_ui_data
        elif line.lstrip().startswith('<style id="jqueryCssInsert">'):
            print '<style id="jqueryCssInsert">\n' + jquery_css_data
        else:
            print line.rstrip()
    # os.system('open -a "Google Chrome" ' + '"file://' + tempDir + '/' + outputName"')
    # webbrowser.open_new("file://" + tempDir + '/' + outputName)
    if open_file:
        open_browser(tempDir, outputName)
Exemplo n.º 27
0
def work(modelDir, inputDict):
    ''' Run the model in its directory. '''

    outData = {}

    feederName = [x for x in os.listdir(modelDir)
                  if x.endswith('.omd')][0][:-4]
    inputDict['feederName1'] = feederName

    with open(pJoin(modelDir, feederName + '.omd')) as f:
        omd = json.load(f)
    if inputDict.get('layoutAlgorithm', 'geospatial') == 'geospatial':
        neato = False
    else:
        neato = True

    path = pJoin(modelDir, feederName + '.omd')
    if path.endswith('.glm'):
        tree = feeder.parse(path)
        attachments = []
    elif path.endswith('.omd'):
        with open(path) as f:
            omd = json.load(f)
        tree = omd.get('tree', {})
        attachments = omd.get('attachments', [])
    else:
        raise Exception('Invalid input file type. We require a .glm or .omd.')

    # dictionary to hold info on lines present in glm
    edge_bools = dict.fromkeys([
        'underground_line', 'overhead_line', 'triplex_line', 'transformer',
        'regulator', 'fuse', 'switch'
    ], False)
    # Get rid of schedules and climate and check for all edge types:
    for key in list(tree.keys()):
        obtype = tree[key].get('object', '')
        if obtype == 'underground_line':
            edge_bools['underground_line'] = True
        elif obtype == 'overhead_line':
            edge_bools['overhead_line'] = True
        elif obtype == 'triplex_line':
            edge_bools['triplex_line'] = True
        elif obtype == 'transformer':
            edge_bools['transformer'] = True
        elif obtype == 'regulator':
            edge_bools['regulator'] = True
        elif obtype == 'fuse':
            edge_bools['fuse'] = True
        elif obtype == 'switch':
            edge_bools['switch'] = True
        if tree[key].get('argument',
                         '') == '\"schedules.glm\"' or tree[key].get(
                             'tmyfile', '') != '':
            del tree[key]

    # print edge_bools

    # Make sure we have a voltDump:
    def safeInt(x):
        try:
            return int(x)
        except:
            return 0

    biggestKey = max([safeInt(x) for x in tree.keys()])
    tree[str(biggestKey * 10)] = {
        'object': 'voltdump',
        'filename': 'voltDump.csv'
    }
    tree[str(biggestKey * 10 + 1)] = {
        'object': 'currdump',
        'filename': 'currDump.csv'
    }

    # Line rating dumps
    tree[feeder.getMaxKey(tree) + 1] = {'module': 'tape'}
    for key in edge_bools.keys():
        if edge_bools[key]:
            tree[feeder.getMaxKey(tree) + 1] = {
                'object': 'group_recorder',
                'group': '"class=' + key + '"',
                'limit': 1,
                'property': 'continuous_rating',
                'file': key + '_cont_rating.csv'
            }

    if edge_bools['regulator']:
        tree[feeder.getMaxKey(tree) + 1] = {
            'object': 'group_recorder',
            'group': '"class=regulator"',
            'limit': 1000,
            'property': 'tap_A',
            'file': 'tap_A.csv',
            'interval': 0
        }

        tree[feeder.getMaxKey(tree) + 1] = {
            'object': 'group_recorder',
            'group': '"class=regulator"',
            'limit': 1000,
            'property': 'tap_B',
            'file': 'tap_B.csv',
            'interval': 0
        }

        tree[feeder.getMaxKey(tree) + 1] = {
            'object': 'group_recorder',
            'group': '"class=regulator"',
            'limit': 1000,
            'property': 'tap_C',
            'file': 'tap_C.csv',
            'interval': 0
        }

    # get start and stop time for the simulation
    [startTime, stopTime] = ['', '']
    for key in tree.keys():
        obname = tree[key].get('object', '')
        starttime = tree[key].get('starttime', '')
        stoptime = tree[key].get('stoptime', '')
        if starttime != '' and stoptime != '':
            startTime = tree[key]['starttime']
            stopTime = tree[key]['stoptime']
            break

    # Map to speed up name lookups.
    nameToIndex = {tree[key].get('name', ''): key for key in tree.keys()}

    # find the key of the relavant added DER components
    addedDerKey = nameToIndex[inputDict['newGeneration']]
    addedDerInverterKey = nameToIndex[tree[addedDerKey]['parent']]
    addedBreakKey = nameToIndex[inputDict['newGenerationBreaker']]
    poi = tree[addedBreakKey]['to']

    # set solar generation to provided insolation value
    insolation = float(inputDict['newGenerationInsolation'])
    if insolation > 1000:
        insolation = 1000
    elif insolation < 0:
        insolation = 0
    # cant set insolation directly but without climate object it defaults to 1000
    # whilch is about 10x max sun output and we can set shading factor between 0 and 1
    # to effectively control insolation
    tree[addedDerKey]['shading_factor'] = insolation / 1000

    # initialize variables
    flickerViolations = []
    flickerThreshold = float(inputDict['flickerThreshold'])
    voltageViolations = []
    [upperVoltThresh, lowerVoltThresh,
     lowerVoltThresh600] = [1.05, 0.95, 0.975]
    thermalViolations = []
    thermalThreshold = float(inputDict['thermalThreshold']) / 100
    reversePowerFlow = []
    tapViolations = []
    tapThreshold = float(inputDict['tapThreshold'])
    faults = ['SLG-A', 'SLG-B', 'SLG-C', 'TLG']
    faultLocs = [
        inputDict['newGenerationBreaker'], inputDict['newGenerationStepUp']
    ]
    faultBreaker = [[] for i in range(2 * len(faults))
                    ]  # the 2 is for the 2 load conditions
    faultStepUp = [[] for i in range(2 * len(faults))]
    faultCurrentViolations = []
    faultCurrentThreshold = float(inputDict['faultCurrentThreshold'])
    faultPOIVolts = []
    faultVoltsThreshold = float(inputDict['faultVoltsThreshold'])

    # run analysis for both load conditions
    for loadCondition in ['Peak', 'Min']:

        # if a peak load file is provided, use it to set peak loads in the tree
        if (loadCondition == 'Peak') and (inputDict['peakLoadData'] != ''):
            peakLoadData = inputDict['peakLoadData'].split('\r\n')
            for data in peakLoadData:
                if str(data) != '':
                    key = data.split(',')[0]
                    val = data.split(',')[1]
                    tree[key]['power_12'] = val

        elif (loadCondition == 'Min'):
            # if a min load file is provided use is to set the min loads
            if inputDict['minLoadData'] != '':
                minLoadData = inputDict['minLoadData'].split('\r\n')
                for data in minLoadData:
                    if str(data) != '':
                        key = data.split(',')[0]
                        val = data.split(',')[1]
                        tree[key]['power_12'] = val

            else:  # if no min load file is provided set min load to be 1/3 of peak + noise
                for key in tree.keys():
                    obtype = tree[key].get('object', '')
                    if obtype == 'triplex_node':
                        load = tree[key].get('power_12', '')
                        if load != '':
                            load = float(load)
                            minLoad = (load / 3) + (load * 0.1 *
                                                    random.triangular(-1, 1))
                            tree[key]['power_12'] = str(minLoad)

        # initialize variables
        flicker = {}
        [maxFlickerLocation, maxFlickerVal] = ['', 0]

        # run analysis with DER on and off under both load conditions
        for der in ['On', 'Off']:

            # if der is Off set added DER offline, if its On set DER online
            if der is 'Off':
                tree[addedDerKey]['generator_status'] = 'OFFLINE'
                tree[addedDerInverterKey]['generator_status'] = 'OFFLINE'
            else:  # der is on
                tree[addedDerKey]['generator_status'] = 'ONLINE'
                tree[addedDerInverterKey]['generator_status'] = 'ONLINE'

            # run gridlab solver
            data = runGridlabAndProcessData(tree,
                                            attachments,
                                            edge_bools,
                                            workDir=modelDir)
            print(tree[addedDerKey])
            print(tree[addedDerInverterKey])

            # generate voltage, current and thermal plots
            filename = 'voltageDer' + der + loadCondition
            chart = drawPlot(tree,
                             nodeDict=data['percentChangeVolts'],
                             neatoLayout=neato,
                             nodeFlagBounds=[114, 126],
                             defaultNodeVal=120)
            chart.savefig(pJoin(modelDir, filename + 'Chart.png'))
            with open(pJoin(modelDir, filename + 'Chart.png'), 'rb') as inFile:
                outData[filename] = base64.standard_b64encode(
                    inFile.read()).decode('ascii')

            filename = 'currentDer' + der + loadCondition
            chart = drawPlot(tree,
                             nodeDict=data['edgeCurrentSum'],
                             neatoLayout=neato)
            chart.savefig(pJoin(modelDir, filename + 'Chart.png'))
            with open(pJoin(modelDir, filename + 'Chart.png'), 'rb') as inFile:
                outData[filename] = base64.standard_b64encode(
                    inFile.read()).decode('ascii')

            filename = 'thermalDer' + der + loadCondition
            chart = drawPlot(tree,
                             nodeDict=data['edgeValsPU'],
                             neatoLayout=neato)
            chart.savefig(pJoin(modelDir, filename + 'Chart.png'))
            with open(pJoin(modelDir, filename + 'Chart.png'), 'rb') as inFile:
                outData[filename] = base64.standard_b64encode(
                    inFile.read()).decode('ascii')

            # calculate max and min voltage and track badwidth violations
            [maxVoltsLocation, maxVoltsVal] = ['', 0]
            [minVoltsLocation, minVoltsVal] = ['', float('inf')]
            for key in data['nodeVolts'].keys():

                voltVal = float(data['nodeVolts'][key])
                nominalVoltVal = float(data['nominalVolts'][key])

                if maxVoltsVal <= voltVal:
                    maxVoltsVal = voltVal
                    maxVoltsLocation = key
                if minVoltsVal >= voltVal:
                    minVoltsVal = voltVal
                    minVoltsLocation = key

                change = 100 * ((voltVal - nominalVoltVal) / nominalVoltVal)
                if voltVal > 600:
                    violation = (voltVal >= (upperVoltThresh*nominalVoltVal)) or \
                     (voltVal <= (lowerVoltThresh600*nominalVoltVal))
                else:
                    violation = (voltVal >= (upperVoltThresh*nominalVoltVal)) or \
                     (voltVal <= (lowerVoltThresh*nominalVoltVal))
                content = [key, nominalVoltVal, voltVal, change, \
                 loadCondition +' Load, DER ' + der,violation]
                voltageViolations.append(content)

            outData['maxVolts' + loadCondition + 'Der' +
                    der] = [maxVoltsLocation, maxVoltsVal]
            outData['minVolts' + loadCondition + 'Der' +
                    der] = [minVoltsLocation, minVoltsVal]

            # check for thermal violations
            for key in data['edgeValsPU'].keys():
                thermalVal = float(data['edgeValsPU'][key])
                content = [key, 100*thermalVal,\
                 loadCondition+' Load, DER '+der,(thermalVal>=thermalThreshold)]
                thermalViolations.append(content)

            if edge_bools['regulator']:
                # check for reverse regulator powerflow
                for key in tree.keys():
                    obtype = tree[key].get("object", "")
                    obname = tree[key].get("name", "")
                    if obtype == 'regulator':
                        powerVal = float(data['edgePower'][obname])
                        content = [obname, powerVal,\
                         loadCondition+' Load, DER '+der,(powerVal<0)]
                        reversePowerFlow.append(content)

                # check for tap_position values and violations
                if der == 'On':
                    tapPositions = copy.deepcopy(data['tapPositions'])
                else:  # der off
                    for tapType in ['tapA', 'tapB', 'tapC']:
                        for key in tapPositions[tapType].keys():
                            # calculate tapPositions
                            tapDerOn = int(tapPositions[tapType][key])
                            tapDerOff = int(data['tapPositions'][tapType][key])
                            tapDifference = abs(tapDerOn - tapDerOff)
                            # check for violations
                            content = [loadCondition, key+' '+tapType, tapDerOn, \
                             tapDerOff,tapDifference, (tapDifference>=tapThreshold)]
                            tapViolations.append(content)

            #induce faults and measure fault currents
            for faultLocation in faultLocs:
                for faultNum, faultType in enumerate(faults):
                    faultIndex = faultNum
                    if loadCondition == 'Min':
                        faultIndex = faultNum + len(faults)

                    treeCopy =  createTreeWithFault( tree, \
                     faultType, faultLocation, startTime, stopTime )
                    faultData = runGridlabAndProcessData(treeCopy, attachments, \
                     edge_bools, workDir=modelDir)
                    faultVolts = faultData['nodeVolts']
                    faultCurrents = faultData['edgeCurrentSum']

                    # get fault current values at the breaker when
                    # the fault is at the breaker
                    if faultLocation == inputDict['newGenerationBreaker']:
                        if der == 'On':
                            faultBreaker[faultIndex] = [
                                loadCondition, faultType
                            ]
                            faultBreaker[faultIndex].append(\
                             float(faultCurrents[\
                              inputDict['newGenerationBreaker']]))
                        else:  #der off
                            faultBreaker[faultIndex].append(\
                             float(faultCurrents[inputDict['newGenerationBreaker']]))
                            faultBreaker[faultIndex].append(\
                             faultBreaker[faultIndex][2] - \
                             faultBreaker[faultIndex][3])

                        # get fault voltage values at POI
                        preFaultval = data['nodeVolts'][poi]
                        postFaultVal = faultVolts[poi]
                        percentChange = 100 * (postFaultVal / preFaultval)
                        faultPOIVolts.append(['Der '+ der + ' ' + \
                         loadCondition + ' Load', poi, faultType, preFaultval,\
                          postFaultVal, percentChange, \
                          (percentChange>=faultVoltsThreshold)])

                    # get fault current values at the transformer when
                    # the fault is at the transformer
                    else:  #faultLocation == newGenerationStepUp
                        if der == 'On':
                            faultStepUp[faultIndex] = [
                                loadCondition, faultType
                            ]
                            faultStepUp[faultIndex].append(\
                             float(faultCurrents[\
                              inputDict['newGenerationStepUp']]))
                        else:  #der off
                            faultStepUp[faultIndex].append(\
                             float(faultCurrents[inputDict[\
                              'newGenerationStepUp']]))
                            faultStepUp[faultIndex].append(\
                             faultStepUp[faultIndex][2] - \
                             faultStepUp[faultIndex][3])

                    # get fault violations when der is on
                    if der == 'On':
                        for key in faultCurrents.keys():
                            preFaultval = float(data['edgeCurrentSum'][key])
                            postFaultVal = float(faultCurrents[key])
                            difference = abs(preFaultval - postFaultVal)
                            if preFaultval == 0:
                                percentChange = 0
                            else:
                                percentChange = 100 * (difference /
                                                       preFaultval)

                            content = [loadCondition, faultLocation, faultType, key, \
                             preFaultval, postFaultVal, percentChange, \
                             (percentChange>=faultCurrentThreshold)]
                            faultCurrentViolations.append(content)

            # calculate flicker, keep track of max, and violations
            if der == 'On':
                flicker = copy.deepcopy(data['nodeVolts'])
            else:  # der off
                for key in flicker.keys():
                    # calculate flicker
                    derOn = float(flicker[key])
                    derOff = float(data['nodeVolts'][key])
                    flickerVal = 100 * (1 - (derOff / derOn))
                    flicker[key] = flickerVal
                    # check for max
                    if maxFlickerVal <= flickerVal:
                        maxFlickerVal = flickerVal
                        maxFlickerLocation = key
                    # check for violations
                    content = [key, flickerVal,loadCondition+' Load',\
                    (flickerVal>=flickerThreshold)]
                    flickerViolations.append(content)

        # plot flicker
        filename = 'flicker' + loadCondition
        chart = drawPlot(tree, nodeDict=flicker, neatoLayout=neato)
        chart.savefig(pJoin(modelDir, filename + 'Chart.png'))
        with open(pJoin(modelDir, filename + 'Chart.png'), "rb") as inFile:
            outData[filename] = base64.standard_b64encode(
                inFile.read()).decode('ascii')

        # save max flicker info to output dictionary
        outData['maxFlicker' +
                loadCondition] = [maxFlickerLocation, maxFlickerVal]

    outData['voltageViolations'] = voltageViolations
    outData['flickerViolations'] = flickerViolations
    outData['thermalViolations'] = thermalViolations
    outData['reversePowerFlow'] = reversePowerFlow
    outData['tapViolations'] = tapViolations
    outData['faultBreaker'] = faultBreaker
    outData['faultStepUp'] = faultStepUp
    outData['faultCurrentViolations'] = faultCurrentViolations
    outData['faultPOIVolts'] = faultPOIVolts

    return outData
Exemplo n.º 28
0
def _get_house_archetypes():
	return feeder.parse(os.path.join(omf.omfDir, "static/testFiles/houseArchetypes.glm"))
Exemplo n.º 29
0
		0.0, # TODO: get a windspeed
		data_hum[i],
		data_solar[i] * 0.75, # TODO: better solar direct
		data_solar[i] * 0.25, # TODO: better solar diffuse
		data_solar[i]
	]
	data_full.append(row)

with open(CSV_NAME,'w') as wFile:
	weather_writer = csv.writer(wFile)
	weather_writer.writerow(['temperature','wind_speed','humidity','solar_dir','solar_diff','solar_global'])
	for row in data_full:
		weather_writer.writerow(row)

# Add stuff to the feeder.
myTree = feeder.parse(GLM_PATH)

# Delete all climate then reinsert.
reader_name = 'weatherReader'
climate_name = 'MyClimate'
for key in myTree.keys():
	obName = myTree[key].get('name','')
	obType = myTree[key].get('object','')
	if obName in [reader_name, climate_name] or obType is 'climate':
		del myTree[key]
oldMax = feeder.getMaxKey(myTree)
myTree[oldMax + 1] = {'omftype':'module', 'argument':'tape'}
myTree[oldMax + 2] = {'omftype':'module', 'argument':'climate'}
myTree[oldMax + 3] = {'object':'csv_reader', 'name':reader_name, 'filename':CSV_NAME}
myTree[oldMax + 4] = {'object':'climate', 'name':climate_name, 'reader': reader_name, 'tmyfile':CSV_NAME}