Exemple #1
0
def to_FLDs(*args, **kw):
    """Load or recieve a template workbook of CrossSections and convert them all to FLD files
    args:
        can either be a path string to a target template workbook or an
        existing SectionBook object
    kw:
        path - output destination for FLD files"""
    if (type(args[0]) == str):
        #load the template
        sb = fields_funks.load_template(args[0])
        if ('path' not in kw):
            kw['path'] = os.path.dirname(args[0])
    elif (isinstance(args[0], fields_class.SectionBook)):
        sb = args[0]
    else:
        raise (fields_class.EMFError(
            """Input argument to to_FLDs() must be a filepath or a SectionBook."""
        ))
    #check for duplicate sheets
    sheets = []
    for xs in sb:
        if (xs.sheet in sheets):
            raise (fields_class.EMFError(
                """Can't create FLD files because of duplicate CrossSection names. Name "%s" is used at least twice."""
                % xs.sheet))
        else:
            sheets.append(xs.sheet)
    #generate FLD files
    for xs in sb:
        to_FLD(xs, **kw)
Exemple #2
0
def to_FLD(xs, **kw):
    """Create an FLD input file for FIELDS from a CrossSection object
    args:
        xs - CrossSection object
    kw:
        path - output file destination"""
    #check input
    if (not isinstance(xs, fields_class.CrossSection)):
        raise (fields_class.EMFError(
            """Input argument to to_FLD() must be a CrossSection object, not an input of type: %s Use to_FLDs() for a SectionBook object."""
            % str(type(xs))))
    #get a filename
    fn = fields_funks._path_manage(xs.sheet, 'FLD', **kw)
    #write the .FLD file
    ofile = open(fn, 'w')
    #miscellaneous stuff first
    _write_FLD_entries(ofile, xs.sheet, xs.title, 60, xs.soil_resistivity,
                       xs.max_dist, xs.step, xs.sample_height, xs.lROW,
                       xs.rROW)
    #number of conductors and ground wires
    Lconds = len(xs.hot)
    _write_FLD_entries(ofile, Lconds)
    Lgrounds = len(xs.gnd)
    _write_FLD_entries(ofile, Lgrounds)
    #write the hot and gnd conductor data in the same format
    for c in xs.hot + xs.gnd:
        _write_FLD_entries(ofile, c.tag, c.x, c.y, c.subconds, c.d_cond,
                           c.d_bund, 'ED!(I)', c.I, c.V, c.phase)
    #write the ground wire data a second time, in a different format
    for c in xs.gnd:
        _write_FLD_entries(ofile, c.tag, c.x, c.y, c.d_cond, 0, 0)
    #close/save
    ofile.close()
    print('FLD file generated: %s' % fn)
Exemple #3
0
def _bisect(xs, conds, x_sample, funk, target, hlow, hhigh, max_iter, rel_err):
    #get sample x and y arrays with a single element in each
    x_sample = np.array([x_sample], dtype=float)
    y_sample = xs.sample_height*np.ones((2,), dtype=float)
    #evaluate at the bracketing values
    flow = funk(hlow, target, xs, conds, x_sample, y_sample)
    fhigh = funk(hhigh, target, xs, conds, x_sample, y_sample)
    #check that the root is bracketed
    if(flow*fhigh > 0.):
        raise(fields_class.EMFError("""
        The root is not bracketed with an upper height adjustment limit
        of %g. Rootfinding with bisection can't be performed.
            f(h_0 = %g) = %g
            f(h_1 = %g) = %g""" % (hhigh, hlow, flow, hhigh, fhigh)))
    #evaluate at a midpoint
    hmid = (hhigh + hlow)/2.0
    fmid = funk(hmid, target, xs, conds, x_sample, y_sample)
    count = 1
    #iterate
    while((abs(fmid/target) > rel_err) and (count < max_iter)):
        #test and throw half out
        if(fmid*flow > 0.):
            hlow = hmid
        elif(fmid*fhigh > 0.):
            hhigh = hmid
        elif(fmid == 0.):
            return(hmid)
        #evaluate at middle
        hmid = (hhigh + hlow)/2.0
        fmid = funk(hmid, target, xs, conds, x_sample, y_sample)
        #increment
        count += 1
    #check if the iteration limit was hit
    if(count == max_iter):
        raise(fields_class.EMFError("""
        Failure in _bisection method. The iteration limit of %d was exseeded
        with a relative error threshold of %g. The final estimate was
        %g""" % (max_iter, rel_err, fmid)))
    return(hmid)
Exemple #4
0
def drop_template(*args, **kw):
    """Copy the emf.fields template in the current directory or a directory specified by an input string
    args:
        drop_path - string, path of copied template file"""
    #check inputs
    if(len(args) > 1):
        raise(fields_class.EMFError("""drop_template only accepts zero or one input argument. A string can be passed to specify the directory in which the template file is copied. With no arguments, the template file is copied into the current directory."""))
    elif(len(args) == 1):
        kw = {'path': args[0]}
    #get template file path
    template_path = os.path.dirname(os.path.dirname(__file__))
    template_path = os.path.join(template_path, 'templates')
    template_path = os.path.join(template_path, 'fields-template.xlsx')
    #get drop path
    drop_path = _path_manage('fields-template', 'xlsx', **kw)
    #copy and notify
    shutil.copyfile(template_path, drop_path)
    print('emf.fields template written to: %s' % drop_path)
Exemple #5
0
def load_template(file_path, **kw):
    """Import conductor data from an excel template, loading each conductor into a Conductor object, each Conductor into a CrossSection object, and each CrossSection object into a SectionBook object. The SectionBook object is returned.
    args:
        template_path - string, path to cross section template excel
                        workbook
    kw:
        sheets - list of strings, a list of sheet names to load, default is
                all sheets"""
    #import the cross sections as a dictionary of pandas DataFrames, also
    #getting a list of the ordered sheets
    file_path = _check_extension(file_path, 'xlsx', """Templates must be excel workbooks. The input target path "%s" is not recognized as an excel file""" % file_path)
    xl = pd.ExcelFile(file_path)
    sheets = xl.sheet_names
    frames = xl.parse(sheetname = None, skiprows = [0,1,2,3],
            parse_cols = 16, header = None)
    #remove necessary sheets if the 'sheets' keyword is passed in
    if('sheets' in kw):
        include = kw['sheets']
        sheets = [sh for sh in sheets if sh in include]
    #create a SectionBook object to store the CrossSection objects
    basename = os.path.basename(file_path)
    if('.' in basename):
        name = basename[:basename.index('.')]
    else:
        name = basename
    sb = fields_class.SectionBook(name)
    #convert the dataframes into a list of CrossSection objects
    titles = []
    for k in sheets:
        #load miscellaneous information applicable to the whole CrossSection
        df = frames[k]
        xs = fields_class.CrossSection(k)
        misc = df[1].values
        xs.tag = misc[0]
        xs.title = str(misc[1])
        #check for duplicate title inputs
        if(xs.title in titles):
            raise(fields_class.EMFError("""Cross-sections should have unique title entries. title: "%s" in sheet: "%s" is used by at least one other sheet.""" % (xs.title, k)))
        else:
            titles.append(xs.title)
        xs.soil_resistivity = misc[3]
        xs.max_dist = misc[4]
        xs.step = misc[5]
        xs.sample_height = misc[6]
        xs.lROW = misc[7]
        xs.rROW = misc[8]
        #load hot conductors
        tags, x, y = [], [], []
        for i in range(df[3].dropna().shape[0]):
            #initialize a Conductor
            cond = fields_class.Conductor(df[2].iat[i])
            #check for conductors with identical tags (names/labels)
            if(cond.tag in tags):
                raise(fields_class.EMFError("""Conductors in a Cross Section must have unique tags. The conductor tag "%s" in sheet:     "%s" is used at least twice."""
                % (cond.tag, k)))
            else:
                tags.append(cond.tag)
            #cond.freq = misc[2]
            cond.x = df[3].iat[i]
            cond.y = df[4].iat[i]
            #check for conductors with identical x,y coordinates
            if(cond.x in x):
                idx = x.index(cond.x)
                if(cond.y == y[idx]):
                    raise(fields_class.EMFError("""Conductors cannot have identical x,y coordinates. Conductor "%s" is in the exact same place as conductor "%s"."""
                % (cond.tag, tags[idx])))
            else:
                x.append(cond.x)
                y.append(cond.y)
            cond.subconds = df.iat[i,5]
            cond.d_cond = df.iat[i,6]
            cond.d_bund = df.iat[i,7]
            cond.V = df.iat[i,8]
            cond.I = df.iat[i,9]
            cond.phase = df.iat[i,10]
            xs.add_conductor(cond)
        #load grounded conductors
        tags, x, y = [], [], []
        for i in range(df[12].dropna().shape[0]):
            #initialize a Conductor
            cond = fields_class.Conductor(df.iat[i,11])
            #check for conductors with identical tags (names/labels)
            if(cond.tag in tags):
                raise(fields_class.EMFError("""Conductors in a Cross Section must have unique tags. The conductor tag "%s" in sheet: "%s" is used at least twice."""
                % (cond.tag, k)))
            else:
                tags.append(cond.tag)
            #cond.freq = misc[2]
            cond.x = df.iat[i,12]
            cond.y = df.iat[i,13]
            #check for conductors with identical x,y coordinates
            if(cond.x in x):
                idx = x.index(cond.x)
                if(cond.y == y[idx]):
                    raise(fields_class.EMFError("""Conductors cannot have identical x,y coordinates. Conductor "%s" is in the exact same place as conductor "%s"."""
                % (cond.tag, tags[idx])))
            else:
                x.append(cond.x)
                y.append(cond.y)
            cond.subconds = 1.
            cond.d_cond = df.iat[i,14]
            cond.d_bund = df.iat[i,14]
            cond.V = 0.
            cond.I = df.iat[i,15]
            cond.phase = df.iat[i,16]
            xs.add_conductor(cond)
        #add the CrossSection object to the SectionBook
        #fields automatically updated upon addition to SectionBook
        sb.add_section(xs)
    #return the SectionBook object
    return(sb)
Exemple #6
0
def optimize_phasing(xs, circuits, **kw):
    """Permute the phasing of non-grounded conductors and find the arrangement that results in the lowest fields at the left and right edge of the ROW. The number of hot conductors must be a multiple of three. The phases of consecutive groups of three conductors are swapped around, assuming that those groups represent a single three-phase transfer circuit.
    args:
        xs - target CrossSection object
        circuits - list of lists of Conductor tags, or 'all'. If a list of
                    lists, each sublist contains the Conductor tags of the
                    Conductors comprising a single circuit. If 'all',
                    circuits are assumed to be consecutive groups of three
                    conductors. (consecutive according to the order in which
                    hot conductors were added to the CrossSection)
    kw:
        save - bool, toggle saving of the results DataFrame to an excel book
        path - string, location/filename for saved results workbook,
                forces saving even if no 'save' keyword is used.
    returns:
        res - pandas DataFrame listing conductor phasings that optimize
                electric and magnetic fields at both ROW edges.
        opt - new SectionBook object containing the permuted phasings that
                optimize the E and B fields at the left and right ROW edges."""

    if(circuits == 'all'):
        #number of hot wires
        hot = xs.hot
        N = len(xs.hot)
        #check the number of hot lines
        if(N % 3 != 0):
            raise(fields_class.EMFError("""The number of hot (not grounded) conductors must be a multiple of three for phase optimization with 'all' circuits. Circuits are assumed to be three-phase and conductors comprising each circuit are assumed to be consecutive groups of three, in the order that they appear in the template. The number of hot conductors is not a multiple of three in the CrossSection named: %s""" % xs.sheet))
        #number of circuits, groups of 3 hot conductors
        G = int(N/3)
        #circuits, consecutive groups of three conductors
        circuits = [[] for i in range(G)]
        for i in range(G):
            for j in range(3):
                circuits[i].append(hot[i*3 + j].tag)
    else:
        #check that all conductor tags are present and refer to hot conds
        gnd = xs.gnd
        for circ in circuits:
            for tag in circ:
                if(xs[tag] is None):
                    raise(fields_class.EMFError("""Unrecognized conductor tag: %s All conductor tags must refer to Conductor objects in the target CrossSecton object.""" % repr(tag)))
                if(xs[tag] in gnd):
                    raise(fields_class.EMFError("""Only phasing of non-grounded Conductors can be permuted. Tag "%s" refers to a grounded Conductor""" % repr(tag)))
    #convert the conductor tags to integer indices in xs.conds
    for i in range(len(circuits)):
        for j in range(len(circuits[i])):
            circuits[i][j] = xs._tag2idx[circuits[i][j]]
    #all permutations of the phases of each circuit
    perm = []
    for c in circuits:
        perm.append(list(itertools.permutations(c)))
    #all possible arrangements of line phasings, 6 permutations for each circuit
    #so 6^(N/3) total line arrangements. Leave P as a generator to avoid storing
    #a huge, factorial sized array of indices
    P = itertools.product(*perm)
    #variables to find the minima with respect to each field and ROW edge
    B_left_min, B_left_arr, B_right_min, B_right_arr = np.inf, [], np.inf, []
    E_left_min, E_left_arr, E_right_min, E_right_arr = np.inf, [], np.inf, []
    #get coordinates of the ROW edges
    x_ROW = np.array([xs.lROW, xs.rROW], dtype=float)
    y_ROW = xs.sample_height*np.ones((2,), dtype=float)
    #pull conductor data
    x, y, I, V, phase = xs.x, xs.y, xs.I, xs.V, xs.phase
    subconds, d_cond, d_bund = xs.subconds, xs.d_cond, xs.d_bund
    #phasing array to swap the elements around in
    phase_swap = phase.copy()
    #store a flattened version of the conductor indices for swapping
    conds = np.array([i for j in circuits for i in j], dtype=int)
    #loop through all possible arrangements in P
    for arr in P:
        #flatten the new arrangement
        new_arr = _flatten(arr)
        #swap phases according to the new phasing arrangement
        phase_swap[conds] = phase[new_arr]
        #calculate fields with index swapped phases
        Ex, Ey = fields_calcs.E_field(x, y, subconds, d_cond, d_bund, V,
                phase_swap, x_ROW, y_ROW)
        Ex, Ey, Eprod, Emax = fields_calcs.phasors_to_magnitudes(Ex, Ey)
        Bx, By = fields_calcs.B_field(x, y, I, phase_swap, x_ROW, y_ROW)
        Bx, By, Bprod, Bmax = fields_calcs.phasors_to_magnitudes(Bx, By)
        #test for minima
        if(Bmax[0] < B_left_min):
            B_left_min, B_left_arr = Bmax[0], new_arr
        if(Bmax[1] < B_right_min):
            B_right_min, B_right_arr = Bmax[1], new_arr
        if(Emax[0] < E_left_min):
            E_left_min, E_left_arr = Emax[0], new_arr
        if(Emax[1] < E_right_min):
            E_right_min, E_right_arr = Emax[1], new_arr
    #return results in a DataFrame
    results = pd.DataFrame(data={
        'Optimal Phasing - Bmax Left ROW Edge': phase[B_left_arr],
        'Optimal Phasing - Bmax Right ROW Edge': phase[B_right_arr],
        'Optimal Phasing - Emax Left ROW Edge': phase[E_left_arr],
        'Optimal Phasing - Emax Right ROW Edge': phase[E_right_arr]},
        index=[xs.conds[i].tag for i in conds])
    #compile a new sectionbook with the optimal phasings
    fn = _path_str_condition(xs.sheet).replace(' ', '-')
    xs = xs.copy()
    opt = fields_class.SectionBook(xs.sheet + '-optimal_phasing')
    xs.sheet += ' (original)'
    xs.tag = 'Phase Optimized'
    opt.add_section(xs)
    names = ['Optimized for Bmax left','Optimized for Bmax right',
            'Optimized for Emax left','Optimized for Emax right']
    for n, ti in zip(names, results.columns):
        #copy the input xs
        new_xs = xs.copy()
        #change the identification fields
        new_xs.sheet, new_xs.title, new_xs.tag = n, ti, 'Phase Optimized'
        #swap the conductor phasings
        for c in new_xs.hot:
            t = c.tag
            if(t in results.index):
                c.phase = results.at[t, ti]
        #store new_xs in the SectionBook
        opt.add_section(new_xs)
    #deal with saving
    if('path' in kw):
        kw['save'] = True
    if('save' in kw):
        if(kw['save']):
            fn = _path_manage(fn + '_phase_optimization', 'xlsx', **kw)
            xl = pd.ExcelWriter(fn, engine='xlsxwriter')
            results.to_excel(xl, index_label='Conductor Tag',
                sheet_name='phase_assignments')
            opt.ROW_edge_export(xl=xl)
            df, c, h = _xs_sb_diff(xs, opt)
            df.to_excel(xl, sheet_name='ROW_edge_diff', index=False,
                    columns=c, header=h)
            for xs in opt:
                xs.fields.to_excel(xl, sheet_name=xs.sheet)
            xl.save()
            print('Phase optimization results written to: %s' % fn)

    return(results, opt)