def add(self, scenario):

        # if already added increment count rather than append.

        scenario.invariant()
        dicthash = scenario.dicthash()

        if dicthash in self.scenarios:

            # print "Updated [%d] = %s" % (self.scenarios[dicthash].count+1,
            #                              dicthash)

            # make sure we don't have hash collision
            Ensure(self.scenarios[dicthash].equal(scenario),
                   "we have a hash collision")

            # make sure we keep result info
            if scenario.result:
                if self.scenarios[dicthash].result:
                    EnsureEqual(scenario.result,
                                self.scenarios[dicthash].result)
                else:
                    self.scenarios[dicthash].result = scenario.result

            self.scenarios[dicthash].increment(scenario.count)
        else:
            # print "New [1]", dicthash
            self.scenarios[dicthash] = scenario
Exemple #2
0
def single_simulate(psat, simtype, title, clean=True):
    """func single_simulate      :: PsatData, Str, Bool -> PsatReport
       ----
       run matlab with the PsatData `psat` as either 
       power flow (pf) or optimal power flow (opf)
       return the results of the simulation.
       remove temp files if specified
    """

    matlab_filename = "matlab_" + title
    psat_filename = "psat_" + title + ".m"
    report_filename = "psat_" + title + "_01.txt"

    # make the matlab_script
    single_matlab_script(matlab_filename + ".m", psat_filename, simtype)

    # write the PsatData to file
    Ensure(psat.in_limits(),
           "no point simulating if it's already out of limits")
    with open(psat_filename, "w") as psat_file:
        psat.write(psat_file)

    # run matlab
    simulate(matlab_filename)

    # return the parsed report
    report = read_report(report_filename)
    if clean:
        clean_files()
    return report
Exemple #3
0
    def set_all_demand(self, value):
        # Note:: should I change P, Q or both.

        # this doesn't really do much other than check the data type
        # we need it that high for testing but the system wont work
        # with value above 1.08 currently
        Ensure(0 < value <= 4, "just a vague sanity check")

        for load in self.loads.values():
            newval = load.p * value
            self.mismatch += load.p - newval
            load.p = newval
            self.demand[load.bus_no].p_bid_max = newval
            self.demand[load.bus_no].p_bid_min = newval
Exemple #4
0
    def process_pflow_bus(self, tokens):
        # print "Bus Power Flow : %d : %s" % (tokens["bus"][0],tokens)
        # print tokens["bus"][0]

        pu_check = lambda x: dec_check(x, Decimal("-10.0"), Decimal("10.0"))
        for x in "v pg qg pl ql".split():
            self.ensure(pu_check(tokens[x]), "error : \n%s" % tokens)
        self.ensure(dec_check(tokens["phase"]), "error : \n%s" % tokens)

        # actually add to self.power_flow

        # are we to assume that there is a 1-to-1 mapping of names to the
        # natural numbers if not then we need a very gay lookup, if so then
        # ... woo
        # im going to assume they do map. even then, do we then keep the 1 based
        # counting or convert to zero based. GAAAAYYYY
        #  * if I convert the external file to zero based. it wont then match the
        #    journal paper
        #  * if I convert the interal representation it wont match the file or
        #    paper
        #  * if I leave it then there may be strange bugs as there will be a
        #    dummy element

        # I have decided to use a dict rather than a list, it still has integers
        # as the key it sovles the other problems. only requirement is that they
        # are unique integers.

        bus_num = tokens["bus"][0]

        Ensure(bus_num >= 0, "cant have negative bus_num (%s)" % bus_num)
        Ensure(bus_num not in self.power_flow,
               "already added bus " + str(bus_num))

        self.power_flow[bus_num] = self.PowerFlow(bus_num, tokens["v"],
                                                  tokens["phase"],
                                                  tokens["pg"], tokens["qg"],
                                                  tokens["pl"], tokens["ql"])
Exemple #5
0
def simulate(matlab_filename, single_item=True):
    """func simulate             :: Str -> [Bool]
       ----
       call matlab with the specified script.
    """

    try:

        # print "simulate", matlab_filename
        parameters = '-nodisplay -nojvm -nosplash -minimize -r '
        # parameters = '-automation -r '
        proc = subprocess.Popen('matlab ' + parameters + matlab_filename,
                                shell=True,
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE,
                                stdin=subprocess.PIPE)

        so, se = proc.communicate()

        if (False):
            print "SE"
            print "================================="
            print se
            print "================================="
            print "SO"
            print "================================="
            print so
            print "================================="

        EnsureEqual(se, "")

        result = parse_matlab_output(so)
        Ensure(
            len(result) >= 1,
            "there has to be at least one result (%s)" % str(result))

        if single_item:
            EnsureEqual(len(result), 1)
            if result[0]:
                print "[S] simulate passed."
            else:
                print "[S] simulate failed."

        return result

    except Exception as exce:
        print "[E] Error Caught at script.simulate (%s)" % matlab_filename
        print exce
        raise
Exemple #6
0
def parse_matlab_output(text):
    def found(msg, in_text):
        return in_text.find(msg) != -1

    result = []
    split_by_sim = text.split("Newton-Raphson Method for Power Flow")
    for n, sim_text in enumerate(split_by_sim[1:]):

        passed = True

        if found("IPM-OPF", sim_text):
            if not found("IPM-OPF completed in", sim_text):
                print "[P] opf not completed", n
                passed = False
        else:
            # we don't cae if the PF before the OPF fails.
            if found("Warning: Matrix is singular", sim_text):
                print "[P] singular matrix warning", n
                passed = False

            if found("Warning: Matrix is close to singular", sim_text):
                print "[P] near singular matrix warning", n
                passed = False

            if found("The error is increasing too much", sim_text):
                print "[P] large error", n
                passed = False

            if found("Convergence is likely not reachable", sim_text):
                print "[P] non-convergence", n
                passed = False

            if not found("Power Flow completed in", sim_text):
                print "[P] power flow not completed", n
                passed = False

        result.append(passed)

    Ensure(len(result) >= 1, str(result))
    return result
Exemple #7
0
 def __init__(self, seq):
     for x in seq:
         Ensure(x is True or x is False,
                "expected Boolena got %s" % str(x))
     self.seq = seq
     self.n = 0
 def invariant(self):
     Ensure(len(self.title) > 0, "Scenarios must have a title")
     Ensure(self.count > 0, "Scenarios must have a count")
     EnsureIn(self.simtype, set(["pf", "opf"]))
     if self.result:
         EnsureIn(self.result, set(["pass", "fail", "error"]))
Exemple #9
0
def generate_cases(n_outages=10, n_failures=1000, sim=True, full_sim=True):
    timer_begin = time.clock()
    timer_start = timer_begin
    print "[G] start simulation with %d states and %d contingencies." % (
        n_outages, n_failures)

    if full_sim:
        Ensure(n_outages and n_failures and sim,
               "can only do full sim if we have everything")

    clean_files()
    batch_size = 100
    psat = read_psat("rts.m")
    prob = read_probabilities("rts.net")

    # create the base cases by sampling for outages
    # simulate these and print to a file.
    # it should contain `n_outages` outages.

    summary_file = open("summary.txt", "w")

    mismatch_file = open("mismatch.txt", "w")
    print "Base Stats: mis= %f gen= %f load= %f lim ( %f < X < %f )" % psat.get_stats(
    )
    mismatch_file.write(
        as_csv("title mismatch gen load min max".split()) + "\n")
    mismatch_file.write(as_csv(["base"] + list(psat.get_stats())) + "\n")

    try:
        if n_outages:
            outage_batch = make_outage_cases(prob, n_outages)
            if sim:
                batch_simulate(outage_batch, psat, batch_size, True,
                               mismatch_file)

            with open("outage.txt", "w") as result_file:
                outage_batch.csv_write(result_file)

            print "-" * 80
            print "---- Outage Stats ----"
            outage_batch.write_stats(sys.stdout)
            summary_file.write("%s\n" % ("-" * 80))
            summary_file.write("Outage Stats\n")
            summary_file.write("%s\n" % ("-" * 80))
            outage_batch.write_stats(summary_file)

            timer_end = time.clock()
            timer_time = (timer_end - timer_start)
            print "[G] outages created in %d seconds." % int(
                math.ceil(timer_time))
            timer_start = time.clock()

        # do the same for one hour changes to the system.
        if n_failures:
            failure_batch = make_failure_cases(prob, n_failures)
            if sim:
                batch_simulate(failure_batch, psat, batch_size, True,
                               mismatch_file)

            with open("failure.txt", "w") as result_file:
                failure_batch.csv_write(result_file)

            print "-" * 80
            print "---- Failure Stats ----"
            failure_batch.write_stats(sys.stdout)
            summary_file.write("Failure Stats\n")
            summary_file.write("%s\n" % ("-" * 80))
            failure_batch.write_stats(summary_file)

            timer_end = time.clock()
            timer_time = (timer_end - timer_start)
            print "[G] failures created in %d seconds." % int(
                math.ceil(timer_time))
            timer_start = time.clock()

        # simulate each of the changes to each base case
        if full_sim:
            simulate_cases(outage_batch, failure_batch, psat, summary_file,
                           mismatch_file)

            timer_end = time.clock()
            timer_time = (timer_end - timer_start)
            print "[G] full sim  in %d seconds." % int(math.ceil(timer_time))
            timer_start = time.clock()
    finally:
        timer_end = time.clock()
        timer_time = (timer_end - timer_begin)
        print "[G] total time %d seconds." % int(math.ceil(timer_time))
Exemple #10
0
def fix_mismatch(mismatch, power, min_limit, max_limit):
    """
    func fix_mismatch :: Real, [Real], [Real], [Real] -> [Real]
    
    change the total generated power by `mismatch`.
    Do this based upon current power of each generator
    taking into account its limits.
    Returns a list of new generator powers
    """

    EnsureEqual(len(power), len(min_limit))
    EnsureEqual(len(power), len(max_limit))

    if mismatch == 0:
        return power

    done = [False for _ in range(len(power))]
    result = [0.0 for _ in range(len(power))]

    def find_limit_max(m):
        """find the index of the first generator that will
        be limited. or None """
        for n in range(len(done)):
            if (not done[n]) and (power[n] * m > max_limit[n]):
                return n
        return None

    def find_limit_min(m):
        """find the index of the first generator that will
        be limited. or None """
        for n in range(len(done)):
            if (not done[n]) and (power[n] * m < min_limit[n]):
                return n
        return None

    Ensure(
        sum(min_limit) < sum(power) + mismatch < sum(max_limit),
        "mismatch of %f is outside limits (%f < %f < %f)" %
        (mismatch, sum(min_limit), sum(power) + mismatch, sum(max_limit)))

    # print "mismatch\t%f" % mismatch
    # print "total gen\t%f" % sum(power)
    # print "total min gen\t%f" % sum(min_limit)
    # print "total max gen\t%f" % sum(max_limit)
    # print "-"*10
    # print "power\t%s" % as_csv(power,"\t")
    # print "min_limit\t%s" % as_csv(min_limit,"\t")
    # print "max_limit\t%s" % as_csv(max_limit,"\t")
    # if mismatch > 0:
    #     print as_csv([b-a for a,b in zip(power, max_limit)], "\t")
    #     print sum(max_limit) - sum(power)
    # else:
    #     print as_csv([b-a for a,b in zip(power, min_limit)], "\t")
    #     print sum(power) - sum(min_limit)

    # deal with each generator that will be limited
    while True:
        Ensure(not all(done), "programmer error")

        # print "fix_mismatch", len([1 for x in done if x])

        total_gen = sum(power[i] for i in range(len(done)) if not done[i])
        EnsureNotEqual(total_gen, 0)

        multiplier = 1.0 + (mismatch / total_gen)

        # we shouldn't really care about the miltiplier as long as
        # the limits are being met should we?
        Ensure(0 <= multiplier <= 5, "vague sanity check")

        if mismatch < 0:
            idx_gen = find_limit_min(multiplier)
            if idx_gen is None:
                break

            # print "generator hit min limit:", idx_gen
            result[idx_gen] = min_limit[idx_gen]
            mismatch -= result[idx_gen] - power[idx_gen]
            done[idx_gen] = True
        else:
            idx_gen = find_limit_max(multiplier)
            if idx_gen is None:
                break

            # print "generator hit max limit:", idx_gen
            result[idx_gen] = max_limit[idx_gen]
            mismatch -= result[idx_gen] - power[idx_gen]
            done[idx_gen] = True

    # deal with all the other generators
    # knowing that none of them will limit
    for idx in range(len(power)):
        if not done[idx]:
            # print "set generator", idx
            result[idx] = power[idx] * multiplier
            mismatch -= result[idx] - power[idx]
            done[idx] = True

    # check nothing is out of limits
    for idx in range(len(power)):
        Ensure(
            power[idx] == 0
            or (min_limit[idx] <= power[idx] <= max_limit[idx]),
            "Power (%d) out of limit (%f<=%f<=%f)" %
            (idx, min_limit[idx], power[idx], max_limit[idx]))
    Ensure(mismatch < 0.001, "should be much mismatch left after fixing it")
    Ensure(all(done), "should have fixed everything")

    return result
Exemple #11
0
    def fix_mismatch(self):
        """
        Changes the generators power to compensate for the imbalance caused 
        by remove_* or set_*. It sets each generator proportionally based 
        upon it's current generating power (though it respects generator 
        limits). We then need to check limits, though if the algorithm is
        working we shouldn't need to.

        It does this by using `self.mismatch`
        TODO: Not sure what to do with reactive power
        TODO: make this only count scheduleable generators i.e. not wind farms
        """

        if self.mismatch != 0:

            scheduleable_generators = self.generators.values()

            gpowers = [gen.p for gen in scheduleable_generators
                       ] + [slack.p_guess for slack in self.slack.values()]

            min_limit = []
            max_limit = []
            for gen in scheduleable_generators:
                min_limit.append(
                    sum(supply.p_bid_min for supply in self.supply.values()
                        if supply.bus_no == gen.bus_no))
                max_limit.append(
                    sum(supply.p_bid_max for supply in self.supply.values()
                        if supply.bus_no == gen.bus_no))

            for slack in self.slack.values():
                min_limit.append(
                    sum(supply.p_bid_min for supply in self.supply.values()
                        if supply.bus_no == slack.bus_no))
                max_limit.append(
                    sum(supply.p_bid_max for supply in self.supply.values()
                        if supply.bus_no == slack.bus_no))

            #print "-----"
            #print "t %f => %f < %f < %f" % (self.mismatch, sum(min_limit), sum(powers), sum(max_limit))
            #for pmin,power,pmax in zip(min_limit, powers, max_limit):
            #    print "p %f < %f < %f" % (pmin,power,pmax)
            #print "-----"

            # check nothing starts out of limits
            gnames = [gen.bus_no for gen in scheduleable_generators
                      ] + [slack.bus_no for slack in self.slack.values()]

            for idx in range(len(gpowers)):
                if not (min_limit[idx] <= gpowers[idx] <= max_limit[idx]):

                    print "Power (#%d - bus(%d)) started out of limit (%f<=%f<=%f)" % (
                        idx, gnames[idx], min_limit[idx], gpowers[idx],
                        max_limit[idx])

            res = fix_mismatch(-self.mismatch, gpowers, min_limit, max_limit)

            for newp, generator in zip(res, scheduleable_generators):
                generator.p = newp

        Ensure(self.in_limits(), "fixing mismatch should leave it in limit")
Exemple #12
0
    def remove_generator(self, supply_id):
        """kill the specified supply and corresponding generator
           with the requiement that there is only one generator
           per busbar.
           Could kill the line and busbar as well in most cases.
           But where there was only one gen on a bus anyway it 
           doesn't have a virtual bus hence might have a load.
        """

        bus_no = self.supply[supply_id].bus_no

        # really just for testing (my god that is poor style)
        if len(self.slack) == 0:
            Ensure(bus_no in self.generators,
                   "missing generator info (%s)" % bus_no)
            self.mismatch -= self.generators[bus_no].p
            del self.generators[bus_no]
            del self.supply[supply_id]
            return

        EnsureEqual(len(self.slack), 1)
        slack = self.slack.values()[0]

        if slack.bus_no == bus_no:

            # todo: lets just hope that's accurate, it's probably not.
            self.mismatch -= slack.p_guess

            # del self.generators[bus_no]
            # we are not doing this because we instead change the slack to the
            # new value from the chosen bus.

            # we do want to remove this.
            del self.supply[supply_id]

            allowed_slacks = [37, 38]
            gen = None
            for x in allowed_slacks:
                if x in self.generators:
                    # print "deleting slack bus: new slack =", x
                    EnsureEqual(self.generators[x].bus_no, x,
                                "%s, %s" % (self.generators[x].bus_no, x))
                    gen = self.generators[x]
                    break
            Ensure(gen, "no slack bus")

            slack.bus_no = gen.bus_no
            slack.s_rating = gen.s_rating
            slack.v_rating = gen.v_rating
            slack.v_magnitude = gen.v
            slack.ref_angle = 0.0
            slack.q_max = gen.q_max
            slack.q_min = gen.q_min
            slack.v_max = gen.v_max
            slack.v_min = gen.v_min
            slack.p_guess = gen.p
            slack.lp_coeff = gen.lp_coeff
            slack.ref_bus = 1.0
            slack.status = gen.status
            del self.generators[gen.bus_no]
        else:
            EnsureIn(bus_no, self.generators, "missing generator info")
            self.mismatch -= self.generators[bus_no].p
            del self.generators[bus_no]
            del self.supply[supply_id]
Exemple #13
0
 def read_as_bus_no(storage, classtype):
     EnsureEqual(len(storage), 0)
     for item in read_section(stream, classtype):
         storage[item.bus_no] = item
     Ensure(len(storage) >= 0, "failed to read any items")
Exemple #14
0
    def lfgenerator(self, output, scenario):
        """from the loadflow kill everything in the killlist
        and save resulting loadlow to writer."""

        # 1. find all in the set killlist & loadflow.branches.keys
        # 2. find all in the set killlist & loadflow.busbars.keys
        # 3. kill all the generator linking lines
        #    i.e. kill lines 'X + 2.'

        killlist = set([x.strip() for x in scenario.kill_list])
        allbranches = set(self.branches.keys())
        allbusses = set(self.busbars.keys())

        branchkill = allbranches & killlist
        buskill = allbusses & killlist
        assert (branchkill | buskill == killlist)

        # kill lines on dead busbars
        deadlines = []
        for (name, value) in self.branches.items():
            if name in branchkill or value[4].strip(
            ) in buskill or value[5].strip() in buskill:
                deadlines += [name]
        deadlines = set(deadlines)

        # if we have killed a generator connecting line, kill its generator
        special_kill = set()
        for name in deadlines:
            if name.startswith("XG"):
                special_kill |= set([name[1:]])
        buskill |= special_kill

        # print "killlist", killlist
        # print "deadlines", deadlines
        # print "branchkill", branchkill
        # print "buskill", buskill

        branchkill |= deadlines
        assert (branchkill <= allbranches)
        assert (buskill <= allbusses)

        csvwriter = csv.writer(output)

        # the title
        csvwriter.writerow([self.title])

        # remembering to change the number of bus and line
        numbus = int(self.header[0]) - len(buskill)
        numline = int(self.header[1]) - len(branchkill)
        csvwriter.writerow([numbus, numline] + self.header[2:])

        # make sure there is a slack bus
        newslackbus = None

        def is_slack_bus(busline):
            return int(busline[0]) == 2

        slackbuslines = filter(is_slack_bus, self.busbars.values())
        assert (len(slackbuslines) == 1)
        slackbus = slackbuslines[0][2].strip()

        if slackbus in buskill:
            for item in ["G13", "G14"]:
                if item not in buskill:
                    newslackbus = item
                    break
            Ensure(newslackbus is not None,
                   "failed to find a replacement slackbus")

        # fix power mismatch
        names = {}
        powers = []
        load_powers = []

        min_limit = []
        max_limit = []

        unscheduleable = windlevel.unscheduleable

        total_gen_power = 0
        total_unscheduleable_gen = 0

        for name, value in self.busbars.items():
            if name not in killlist:
                if value[3].strip() != "":
                    if name not in unscheduleable:
                        total_gen_power += float(value[3])
                        names[name] = len(powers)
                        powers.append(float(value[3]))
                        min_limit.append(0)  # no minimum level for a generator
                        max_limit.append(
                            float(self.limits_checker.gen_limit[name]))
                    else:
                        gen_id = windlevel.unscheduleable.index(name)
                        gen_power = float(value[3]) * float(
                            scenario.wind_levels[gen_id])
                        total_gen_power += gen_power
                        total_unscheduleable_gen += gen_power

                if value[7].strip() != "":
                    load_powers.append(float(value[7]))

        # mismatch is sum(load_power) + line_losses - sum(gen_power) after: fix mismatch, bus_level and killed.
        mismatch = (sum(load_powers) *
                    scenario.bus_level) + self.line_losses - total_gen_power
        fixed_powers = fix_mismatch(mismatch, powers, min_limit, max_limit)
        # line for `main.py test` to check with notes.
        # print "mismatch", 8550 - (sum(load_powers) * scenario.bus_level), 8997.9 - sum(fixed_powers) - total_unscheduleable_gen

        # ignore everything in killlist, print the rest
        for (name, value) in self.busbars.items():
            if name not in buskill:
                # don't modify self
                new_value = value[:]

                # if we have a load power - times it by the bus_level
                if new_value[7].strip() != "":
                    new_value[7] = str(
                        float(new_value[7]) * scenario.bus_level)

                # if we have a new generator power get it from fix mismatch
                if name in names:
                    new_value[3] = str(fixed_powers[names[name]])

                # if we have an unscheduleable gen get it's power from the level
                if name in windlevel.unscheduleable:
                    gen_id = windlevel.unscheduleable.index(name)
                    new_value[3] = str(
                        float(new_value[3]) *
                        float(scenario.wind_levels[gen_id]))

                # print it out
                # print len(new_value), " ".join(new_value)

                if name != newslackbus:
                    csvwriter.writerow(new_value)
                else:
                    csvwriter.writerow(["2"] + new_value[1:])

        # same with the branches
        # remember to kill lines on dead busbars
        for (name, value) in self.branches.items():
            if name not in branchkill:
                if value[4].strip() not in buskill and value[5].strip(
                ) not in buskill:
                    csvwriter.writerow(value)