Merge pull request #5 from cuihantao/master
[pylon.git] / pylon / main.py
blob15ddf27b532863f43640012898076ceb96b63205
1 #------------------------------------------------------------------------------
2 # Copyright (C) 2007-2010 Richard Lincoln
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 #------------------------------------------------------------------------------
17 """ Defines the entry point for Pylon.
18 """
20 #------------------------------------------------------------------------------
21 # Imports:
22 #------------------------------------------------------------------------------
24 import os
25 import sys
26 import logging
27 import optparse
29 from pylon.io import \
30 MATPOWERReader, PSSEReader, MATPOWERWriter, ReSTWriter, \
31 PickleReader, PickleWriter, PSSEWriter
33 from pylon import DCPF, NewtonPF, FastDecoupledPF, OPF, UDOPF
35 #------------------------------------------------------------------------------
36 # Logging:
37 #------------------------------------------------------------------------------
39 logging.basicConfig(stream=sys.stdout, level=logging.DEBUG,
40 format="%(levelname)s: %(message)s")
42 logger = logging.getLogger("pylon")
44 #------------------------------------------------------------------------------
45 # Read data file:
46 #------------------------------------------------------------------------------
48 def read_case(input, format=None):
49 """ Returns a case object from the given input file object. The data
50 format may be optionally specified.
51 """
52 # Map of data file types to readers.
53 format_map = {"matpower": MATPOWERReader,
54 "psse": PSSEReader, "pickle": PickleReader}
56 # Read case data.
57 if format_map.has_key(format):
58 reader_klass = format_map[format]
59 reader = reader_klass()
60 case = reader.read(input)
61 else:
62 # Try each of the readers at random.
63 for reader_klass in format_map.values():
64 reader = reader_klass()
65 try:
66 case = reader.read(input)
67 if case is not None:
68 break
69 except:
70 pass
71 else:
72 case = None
74 return case
76 #------------------------------------------------------------------------------
77 # Format detection:
78 #------------------------------------------------------------------------------
80 def detect_data_file(input, file_name=""):
81 """ Detects the format of a network data file according to the
82 file extension and the header.
83 """
84 _, ext = os.path.splitext(file_name)
86 if ext == ".m":
87 line = input.readline() # first line
88 if line.startswith("function"):
89 type = "matpower"
90 logger.info("Recognised MATPOWER data file.")
91 elif line.startswith("Bus.con" or line.startswith("%")):
92 type = "psat"
93 logger.info("Recognised PSAT data file.")
94 else:
95 type = "unrecognised"
96 input.seek(0) # reset buffer for parsing
98 elif (ext == ".raw") or (ext == ".psse"):
99 type = "psse"
100 logger.info("Recognised PSS/E data file.")
102 elif (ext == ".pkl") or (ext == ".pickle"):
103 type = "pickle"
104 logger.info("Recognised pickled case.")
106 else:
107 type = None
109 return type
111 #------------------------------------------------------------------------------
112 # "main" function:
113 #------------------------------------------------------------------------------
115 def main():
116 """ Parses the command line and call Pylon with the correct data.
118 parser = optparse.OptionParser(usage="usage: pylon [options] input_file",
119 version="%prog 0.4.4")
121 parser.add_option("-o", "--output", dest="output", metavar="FILE",
122 help="Write the solution report to FILE.")
124 # parser.add_option("-q", "--quiet", action="store_true", dest="quiet",
125 # default=False, help="Print less information.")
127 parser.add_option("-v", "--verbose", action="store_true", dest="verbose",
128 default=False, help="Print more information.")
130 # parser.add_option("-g", "--gui", action="store_true", dest="gui",
131 # default=False, help="Use the portable graphical interface to Pylon.")
133 # parser.add_option("-n", "--no-report", action="store_true",
134 # dest="no_report", default=False, help="Suppress report output.")
136 parser.add_option("-d", "--debug", action="store_true", dest="debug",
137 default=False, help="Print debug information.")
139 parser.add_option("-t", "--input-type", dest="type", metavar="TYPE",
140 default="any", help="The argument following the -t is used to "
141 "indicate the format type of the input data file. The types which are "
142 "currently supported include: matpower, psse [default: %default]"
143 " If not specified Pylon will try to determine the type according to "
144 "the file name extension and the file header.")
146 parser.add_option("-s", "--solver", dest="solver", metavar="SOLVER",
147 default="acpf", help="The argument following the -s is used to"
148 "indicate the type of routine to use in solving. The types which are "
149 "currently supported are: 'dcpf', 'acpf', 'dcopf', 'acopf', 'udopf' "
150 "and 'none' [default: %default].")
152 parser.add_option("-a", "--algorithm", action="store_true",
153 metavar="ALGORITHM", dest="algorithm", default="newton",
154 help="Indicates the algorithm type to be used for AC power flow. The "
155 "types which are currently supported are: 'newton' and 'fdpf' "
156 "[default: %default].")
158 parser.add_option("-T", "--output-type", dest="output_type",
159 metavar="OUTPUT_TYPE", default="rst", help="Indicates the output "
160 "format type. The type swhich are currently supported include: rst, "
161 "matpower, csv, excel and none [default: %default].")
163 (options, args) = parser.parse_args()
165 if options.verbose:
166 logger.setLevel(logging.INFO)
167 elif options.debug:
168 logger.setLevel(logging.DEBUG)
169 else:
170 logger.setLevel(logging.ERROR)
172 # Output.
173 outext = {'psse': '.raw', 'matpower': '.m'}
174 if options.output:
175 if options.output == "-":
176 outfile = sys.stdout
177 logger.setLevel(logging.CRITICAL) # must stay quiet
178 # options.output_type = "none"
179 else:
180 outfile = open(options.output, "wb")
181 elif options.output_type is not None:
182 if options.output_type in outext.keys():
183 inname, ext = os.path.splitext(args[0])
184 outfile = inname + outext[options.output_type]
185 else:
186 outfile = sys.stdout
187 else:
188 outfile = sys.stdout
189 # if not options.no_report:
190 # logger.setLevel(logging.CRITICAL) # must stay quiet
192 # Input.
193 if len(args) > 1:
194 parser.print_help()
195 sys.exit(1)
196 elif (len(args) == 0) or (args[0] == "-"):
197 filename = ""
198 if sys.stdin.isatty():
199 # True if the file is connected to a tty device, and False
200 # otherwise (pipeline or file redirection).
201 parser.print_help()
202 sys.exit(1)
203 else:
204 # Handle piped input ($ cat ehv3.raw | pylon | rst2pdf -o ans.pdf).
205 infile = sys.stdin
206 else:
207 filename = args[0]
208 infile = open(filename, "rb")
210 if options.type == "any":
211 type = detect_data_file(infile, filename)
212 else:
213 type = options.type
215 # Get the case from the input file-like object.
216 case = read_case(infile, type)
218 if case is not None:
219 # Routine (and algorithm) selection.
220 if options.solver == "dcpf":
221 solver = DCPF(case)
222 elif options.solver == "acpf":
223 if options.algorithm == "newton":
224 solver = NewtonPF(case)
225 elif options.algorithm == "fdpf":
226 solver = FastDecoupledPF(case)
227 else:
228 logger.critical("Invalid algorithm [%s]." % options.algorithm)
229 sys.exit(1)
230 elif options.solver == "dcopf":
231 solver = OPF(case, True)
232 elif options.solver == "acopf":
233 solver = OPF(case, False)
234 elif options.solver == "udopf":
235 solver = UDOPF(case)
236 elif options.solver == "none":
237 solver = None
238 else:
239 logger.critical("Invalid solver [%s]." % options.solver)
240 # sys.exit(1)
241 solver = None
243 # Output writer selection.
244 if options.output_type == "matpower":
245 writer = MATPOWERWriter(case)
246 elif options.output_type == "psse":
247 writer = PSSEWriter(case)
248 elif options.output_type == "rst":
249 writer = ReSTWriter(case)
250 elif options.output_type == "csv":
251 from pylon.io.excel import CSVWriter
252 writer = CSVWriter(case)
253 elif options.output_type == "excel":
254 from pylon.io.excel import ExcelWriter
255 writer = ExcelWriter(case)
256 elif options.output_type == "pickle":
257 writer = PickleWriter(case)
258 else:
259 logger.critical("Invalid output type [%s]." % options.output_type)
260 sys.exit(1)
262 if solver is not None:
263 solver.solve()
264 if options.output_type != "none":
265 writer.write(outfile)
266 print('Output file {0} written'.format(outfile))
267 else:
268 logger.critical("Unable to read case data.")
270 # Don't close stdin or stdout.
271 if len(args) == 1:
272 infile.close()
273 if options.output and not (options.output == "-"):
274 outfile.close()
276 if __name__ == "__main__":
277 main()
279 # EOF -------------------------------------------------------------------------