Пример #1
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
Пример #2
0
    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
Пример #3
0
def batch_matlab_script(filename, batch):
    """func batch_matlab_script  :: Str, SimulationBatch -> 
       ----
       create a matlab script file which simulates all the Scenarios
       in the batch assuming their filename is 
           "psat_" + scenario.title + ".m"
    """

    EnsureNotEqual(len(batch), 0)
    with open(filename, "w") as matlab_stream:

        matlab_stream.write("initpsat;\n")
        matlab_stream.write("Settings.lfmit = 50;\n")
        matlab_stream.write("Settings.violations = 'on'\n")
        matlab_stream.write("OPF.basepg = 0;\n")
        matlab_stream.write("OPF.basepl = 0;\n")

        simtype = batch[0].simtype

        for scenario in batch:
            filename = "psat_" + scenario.title + ".m"
            matlab_stream.write("runpsat('" + filename + "','data');\n")

            EnsureEqual(simtype, scenario.simtype)

            if simtype == "pf":
                matlab_stream.write("runpsat pf;\n")
            elif simtype == "opf":
                matlab_stream.write("runpsat opf;\n")
            else:
                raise Error("expected pf or opf got: " + scenario.simtype)
            matlab_stream.write("runpsat pfrep;\n")

        matlab_stream.write("closepsat;\n")
        matlab_stream.write("exit\n")
Пример #4
0
def report_to_psat(report, psat):
    """func report_to_psat       :: PsatReport, PsatData -> PsatData
       ----
       Make a new PsatData based upon `psat` but contains the voltage,
       angle, and power values from `report`.
    """

    # TODO: if we can work out why PSAT has discrepencies in the
    #       number of items in input and output then add these line back

    # assert len(psat.lines) == report.num_line
    # assert len(psat.slack) == 1
    # assert len(psat.generators) == report.num_generator
    # assert len(psat.busses) == report.num_bus
    # assert len(psat.loads) == report.num_load
    # assert len(psat.demand) == 0
    # assert len(psat.supply) == len(psat.loads)

    new_psat = deepcopy(psat)
    pf = report.power_flow

    EnsureEqual(len(new_psat.slack), 1)
    slack = new_psat.slack.values()[0]
    slack.v_magnitude = float(pf[slack.bus_no].v)
    slack.ref_angle = float(pf[slack.bus_no].phase)
    slack.p_guess = float(pf[slack.bus_no].pg)

    for gen in new_psat.generators.values():
        if gen.bus_no in pf:
            gen.p = float(pf[gen.bus_no].pg)
            gen.v = float(pf[gen.bus_no].v)
        else:
            print "ERROR:", gen.bus_no

    for load in new_psat.loads.values():
        if load.bus_no in pf:
            load.p = float(pf[load.bus_no].pl)
            load.q = float(pf[load.bus_no].ql)
        else:
            print "ERROR:", load.bus_no

    # fix for reactive power on bus 39-43
    # for x in range(39,44):
    # assert str(new_psat.generators[x].v) == "1.014"
    # new_psat.generators[x].v = "1.01401"

    # assert len(new_psat.shunts) == 0
    # assert new_psat.loads[6].q == "1.299"

    if not new_psat.in_limits():
        print "not in limits"
        # TODO: I think it probably should raise... maybe
        # raise Error("new file is invalid. It hits static limits.")

    return new_psat
Пример #5
0
def make_failures(prob, count):
    """func make_failures        :: NetworkProbability, Int -> SimulationBatch
       ----
       Monte Carlo sample the network for unexpected changes. 
       e.g. new outages (failures), actual weather, actual load level, etc.
    """
    batch = SimulationBatch()
    for x in range(count):
        batch.add(prob.failures(str(x)))
    EnsureEqual(count, batch.size())
    return batch
Пример #6
0
def make_outages(prob, count):
    """func make_outages         :: NetworkProbability, Int -> SimulationBatch
       ----
       Monte Carlo sample the network for it's expected condition. 
       e.g. existing outages, weather & load forcast, etc.
    """
    batch = SimulationBatch()
    for x in range(count):
        batch.add(prob.outages(str(x)))
    EnsureEqual(count, batch.size())
    return batch
Пример #7
0
def text_to_scenario(text):
    """func text_to_scenario     :: Str -> Scenario
       ----
       make `text` into a Scenario by reading as if a 
       single element batch file
    """

    with closing(StringIO(text)) as batch_stream:
        batch = SimulationBatch()
        batch.read(batch_stream)

    EnsureEqual(len(batch), 1)
    return list(batch)[0]
Пример #8
0
    def remove_bus(self, bus_no):

        # TODO: really should be an 'assert' not an 'if' but make testing easier
        if len(self.slack) == 1:
            slack = self.slack.values()[0]
            EnsureNotEqual(slack.bus_no, bus_no)
        else:
            print "Expected one slack got %d" % len(self.slack)

        EnsureEqual(self.busses[bus_no].bus_no, bus_no)
        del self.busses[bus_no]

        # as we now have virtal busses that connect to generators
        # we need to find and delete any of those hat connect to
        # this bus.
        # We can find them by using the cid of the lines
        # find all lines with who's cid starts with "C" that
        # connect to this bus

        for line in self.lines.values():
            if line.fbus == bus_no:
                self.remove_line(line.cid)
                if line.cid.startswith("c"):
                    self.remove_bus(line.tbus)
            elif line.tbus == bus_no:
                self.remove_line(line.cid)
                if line.cid.startswith("c"):
                    self.remove_bus(line.fbus)

        # kill all connecting items
        for idx, shunt in self.shunts.items():
            if shunt.bus_no == bus_no:
                del self.shunts[idx]

        for idx, item in self.demand.items():
            if item.bus_no == bus_no:
                del self.demand[idx]

        for idx, item in self.supply.items():
            if item.bus_no == bus_no:
                del self.supply[idx]

        for idx, gen in self.generators.items():
            if gen.bus_no == bus_no:
                self.mismatch -= gen.p
                del self.generators[idx]

        for idx, load in self.loads.items():
            if load.bus_no == bus_no:
                self.mismatch += load.p
                del self.loads[idx]
Пример #9
0
def make_outage_cases(prob, count):
    """func make_outage_cases         :: NetworkProbability, Int -> SimulationBatch
       ----
       like `make_outages` but makes `count` *after* reducing.
       
       Monte Carlo sample the network for it's expected condition. 
       e.g. existing outages, weather & load forcast, etc.
    """
    batch = SimulationBatch()
    current = 0
    while len(batch) < count:
        batch.add(prob.outages(str(current)))
        current += 1
    EnsureEqual(count, len(batch))
    return batch
Пример #10
0
def make_failure_cases(prob, count):
    """func make_failure_cases        :: NetworkProbability, Int -> SimulationBatch
       ----
       like `make_failures` but makes `count` *after* reducing.
       
       Monte Carlo sample the network for unexpected changes. 
       e.g. new outages (failures), actual weather, actual load level, etc.
    """
    batch = SimulationBatch()
    current = 0
    while len(batch) < count:
        batch.add(prob.failures(str(current)))
        current += 1
    EnsureEqual(count, len(batch))
    return batch
Пример #11
0
    def read(self, stream):

        current_scen = None

        for line in stream:

            line = [x.lower() for x in line.split()]

            # comments
            if len(line) == 0 or line[0].startswith("#"):
                continue

            # title
            elif line[0].startswith("["):
                if current_scen is not None:
                    # logger.debug("Added Scenario: %s" % title)
                    self.add(current_scen)

                title = line[0][1:-1]
                simtype = line[1]

                if len(line) == 3:
                    count = int(line[2])
                else:
                    EnsureEqual(len(line), 2)
                    count = 1

                EnsureIn(simtype, set("pf opf".split()))
                current_scen = Scenario(title, simtype, count)

            # remove
            elif line[0] == "remove":
                if line[1] == "bus":
                    current_scen.kill_bus.append(int(line[2]))
                elif line[1] == "line":
                    current_scen.kill_line.append(line[2])
                elif line[1] == "generator":
                    current_scen.kill_gen.append(line[2])
                else:
                    raise Error("got %s expected (line, generator, bus)" %
                                line[1])

            # set
            elif line[0] == "set":
                if line[1:3] == ["all", "demand"]:
                    current_scen.all_demand = float(line[3])
                else:
                    raise Error("got %s expected 'demand'" % line[1])

            # results
            elif line[0] == "result":
                EnsureIn(line[1], set("pass fail error".split()))
                current_scen.result = line[1]

            # nothing else allowed
            else:
                raise Error("got %s expected (remove, set, result, [])" %
                            line[0])

        if current_scen is not None:
            # logger.debug("Added Scenario: %s" % title)
            self.add(current_scen)
Пример #12
0
def batch_simulate(batch, psat, size=10, clean=True, mismatch_file=None):
    """func batch_simulate       :: SimulationBatch, PsatData, Int -> 
       ----
       Simulate all Scenarios in `batch` (with a base of `psat`) in groups
       of size `size`. Modify `batch` in place. delete all temp files
       if it succedes 

    """

    print "[b] batch simulate %d cases" % len(batch)
    for n, group in enumerate(split_every(size, batch)):
        try:
            timer_start = time.clock()
            print "[b] simulating batch", n + 1, "of", int(
                math.ceil(len(batch) / size)) + 1
            sys.stdout.flush()

            # make the matlab_script
            matlab_filename = "matlab_" + str(n)
            batch_matlab_script(matlab_filename + ".m", group)

            # write all the scenarios to file as psat_files
            for scenario in group:
                scenario.result = None

                try:
                    new_psat = scenario_to_psat(scenario, psat)
                except Exception as exce:
                    print "[E] Error Caught at script.batch_simulate (%s) - failed to convert scenario to psat" % scenario.title
                    print exce
                    scenario.result = "error"
                    # we probably shouldn't have this but it might cause error in matlab as it is expected.
                    new_psat = deepcopy(psat)

                if mismatch_file:
                    mismatch_file.write(
                        as_csv([scenario.title] + list(new_psat.get_stats())) +
                        "\n")

                new_psat_filename = "psat_" + scenario.title + ".m"
                with open(new_psat_filename, "w") as new_psat_file:
                    new_psat.write(new_psat_file)

            # run matlab
            resutls = simulate(matlab_filename, False)
            EnsureEqual(len(resutls), len(group))
            for res, scenario in zip(resutls, group):
                if not (res):
                    print "[b] did not converge (%s)" % scenario.title
                    scenario.result = "fail"

            # gather results
            for scenario in group:
                if not scenario.result:
                    report_filename = "psat_" + scenario.title + "_01.txt"
                    try:
                        report = read_report(report_filename)
                        scenario.result = report_in_limits(report)
                    except Exception as exce:
                        print "[E] Error Caught at script.batch_simulate (%s) - report check failure" % scenario.title
                        print exce
                        scenario.result = "error"

            timer_end = time.clock()
            timer_time = (timer_end - timer_start)
            print "[b] batch time of", int(math.ceil(timer_time)), "seconds"
        except Exception as exce:
            print "[E] Error Caught at script.batch_simulate (%s) - failed to simulate batch" % scenario.title
            print exce

    if clean:
        clean_files()
Пример #13
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
Пример #14
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]
Пример #15
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")