@@ -27,6 +27,11 @@ template <typename T> bool Approx(T a, T b, T rtol = 1.0e-5) {
2727
2828void Warn (const std::string &message) { ExaGOLog (EXAGO_LOG_WARN, message); }
2929
30+ std::string RemoveNulls (std::string str) {
31+ str.erase (std::remove (str.begin (), str.end (), ' \0 ' ), str.end ());
32+ return str;
33+ }
34+
3035std::string Strip (std::string str) {
3136 auto notspace = [](char c) { return !std::isspace (c); };
3237 str.erase (begin (str), std::find_if (begin (str), end (str), notspace));
@@ -48,12 +53,25 @@ std::string ReadLine(std::istream &is) {
4853}
4954
5055std::istream &SkipLeadingWhitespace (std::istream &is) {
51- while (std::isspace (is.peek ())) {
56+ while (std::isspace (is.peek ()) && is. peek () != ' \n ' ) {
5257 is.get ();
5358 }
5459 return is;
5560}
5661
62+ bool IsQRecord (std::istream &is) {
63+ bool result = false ;
64+ if (SkipLeadingWhitespace (is).peek () == ' Q' ) {
65+ is.get ();
66+ auto eof = std::char_traits<char >::eof ();
67+ if (is.peek () == eof || std::isspace (is.peek ())) {
68+ result = true ;
69+ }
70+ is.unget ();
71+ }
72+ return result;
73+ }
74+
5775class QuoteStringParse {
5876public:
5977 operator std::string () const { return s_; }
@@ -67,11 +85,18 @@ class QuoteStringParse {
6785 if (!(qc == ' \' ' || qc == ' \" ' )) {
6886 Error (" First character expected to be single or double quote" );
6987 }
88+ // Skip opening quote
7089 is.get ();
7190
7291 // Get line to closing quote
7392 std::getline (is, qs.s_ , qc);
7493
94+ // Check for eof
95+ if (is.peek () == std::char_traits<char >::eof ()) {
96+ // Put stream in eof state
97+ is.get ();
98+ }
99+
75100 return is;
76101 }
77102
@@ -148,26 +173,59 @@ class LineItemStream : public std::istream {
148173 LineItemStream (std::istream &is) : std::istream(is.rdbuf()), is_(&is) {
149174 do {
150175 NextLine ();
151- } while (StartsWith (" @!" ));
176+ } while (! Empty () && StartsWith (" @!" ));
152177 }
153178
154179 CheckedLineItemStream Checked () { return CheckedLineItemStream (*this ); }
155180
156181 operator bool () const { return !q_.empty (); }
157182
183+ bool Empty () const noexcept { return q_.empty (); }
158184 std::size_t Size () const noexcept { return q_.size (); }
159185 const std::string &Raw () const noexcept { return line_; }
160186
161187 bool StartsWith (const std::string &sub) const {
188+ if (q_.empty ()) {
189+ return false ;
190+ }
162191 return q_.front ().find (sub) == 0 ;
163192 }
164193
194+ bool IsSectionHeader () {
195+ if (Empty ()) {
196+ return false ;
197+ }
198+ if (Size () <= 2 ) {
199+ if (Strip (q_.front ()).find (" 0" ) == 0 ) {
200+ auto remainder = Strip (q_.front ().substr (1 ));
201+ if (remainder.empty () || remainder.find (" /" ) == 0 ) {
202+ return true ;
203+ }
204+ }
205+ }
206+ return false ;
207+ }
208+
165209 LineItemStream &NextLine () {
166- line_ = ReadLine (*is_);
167- std::istringstream iss (line_);
168210 q_.clear ();
211+ if (IsQRecord (*is_) || is_->eof ()) {
212+ return *this ;
213+ }
214+ line_ = ReadLine (*is_);
215+ if (line_.empty ()) {
216+ return *this ;
217+ }
218+ if (line_.find (" @!" ) == 0 || line_.find (" 0 /" ) == 0 ) {
219+ q_.push_back (line_);
220+ return *this ;
221+ }
222+ std::string noComment;
223+ std::getline (std::istringstream (line_), noComment, ' /' );
224+ std::istringstream iss (noComment);
169225 for (std::string item; std::getline (iss, item, ' ,' );) {
170- q_.push_back (Strip (item));
226+ item = RemoveNulls (Strip (item));
227+ CheckSingleItem (item);
228+ q_.push_back (item);
171229 }
172230 return *this ;
173231 }
@@ -193,6 +251,25 @@ class LineItemStream : public std::istream {
193251 return out;
194252 }
195253
254+ void CheckSingleItem (const std::string &item) {
255+ if (item.empty ()) {
256+ return ;
257+ }
258+ std::istringstream iss (item);
259+ if (iss.peek () == ' \' ' || iss.peek () == ' \" ' ) {
260+ QuoteStringParse p{};
261+ while (iss.peek () == ' \' ' || iss.peek () == ' \" ' ) {
262+ iss >> p;
263+ }
264+ } else {
265+ std::string p;
266+ iss >> p;
267+ }
268+ if (!iss.eof ()) {
269+ Error (" PSS(R)E parser: Space delimiters not supported" );
270+ }
271+ }
272+
196273 std::istream *is_{nullptr };
197274
198275 std::string line_;
@@ -209,29 +286,34 @@ inline CheckedLineItemStream &CheckedLineItemStream::operator>>(T &item) {
209286
210287CaseID ParseCaseID (std::istream &is) {
211288 CaseID cid;
289+ LineItemStream firstLine (is);
212290 std::string record;
213- std::getline (is, record, ' /' );
291+ auto recordLineStream = std::stringstream (firstLine.String ());
292+ std::getline (recordLineStream, record, ' /' );
214293 std::istringstream rec_stream (record);
215294 LineItemStream lis (rec_stream);
216- lis >> cid.ic >> cid.sbase >> cid.rev ;
295+ auto clis = lis.Checked ();
296+ clis >> cid.ic >> cid.sbase >> cid.rev ;
217297 int supported[] = {32 , 33 , 34 };
218298 if (std::find (std::begin (supported), std::end (supported), cid.rev ) ==
219299 std::end (supported)) {
220300 Error (" PSS(R)E Power Flow Raw Data Files are supported only for "
221301 " versions 32, 33 and 34. This file is tagged version " +
222302 std::to_string (cid.rev ));
223303 }
224- lis >> cid.xfrrat >> cid.nxfrat >> cid.basfrq ;
304+ clis >> cid.xfrrat >> cid.nxfrat >> cid.basfrq ;
225305
226- cid.extra [0 ] = ReadLine (is);
227- cid.extra [1 ] = ReadLine (is);
228- cid.extra [2 ] = ReadLine (is);
306+ cid.extra [0 ] = ReadLine (recordLineStream);
307+ if (is && !IsQRecord (is) && !is.eof ()) {
308+ cid.extra [1 ] = ReadLine (is);
309+ cid.extra [2 ] = ReadLine (is);
310+ }
229311 return cid;
230312}
231313
232314void SkipSystemWideData (std::istream &is) {
233315 LineItemStream lis (is);
234- while (!lis.StartsWith (" 0 /" )) {
316+ while (!lis.Empty () && !lis. StartsWith (" 0 /" )) {
235317 lis.NextLine ();
236318 }
237319}
@@ -468,9 +550,9 @@ struct Parser {
468550
469551 template <typename T> std::vector<T> ParseRecords (std::istream &is) {
470552 std::vector<T> recs;
471- while (is && is. peek () != ' Q ' ) {
553+ while (is && ! IsQRecord (is) && !is. eof () ) {
472554 LineItemStream lis (is);
473- if (lis.StartsWith ( " 0 / " )) {
555+ if (lis.IsSectionHeader ( )) {
474556 break ;
475557 }
476558 auto &rec = recs.emplace_back ();
@@ -516,14 +598,6 @@ std::size_t BusMapping::GetInternalIndex(const std::string &bus_name) const {
516598 return name_map_.at (bus_name);
517599}
518600
519- std::size_t BusMapping::GetBusNumber (const std::string &bus_name) const {
520- return buses_[GetInternalIndex (bus_name)].i ;
521- }
522-
523- const std::string &BusMapping::GetBusName (std::size_t bus_number) const {
524- return buses_[GetInternalIndex (bus_number)].name ;
525- }
526-
527601const Bus &BusMapping::GetBus (const std::string &bus_name) const {
528602 return buses_[GetInternalIndex (bus_name)];
529603}
@@ -532,6 +606,14 @@ const Bus &BusMapping::GetBus(std::size_t bus_number) const {
532606 return buses_[GetInternalIndex (bus_number)];
533607}
534608
609+ std::size_t BusMapping::GetBusNumber (const std::string &bus_name) const {
610+ return GetBus (bus_name).i ;
611+ }
612+
613+ const std::string &BusMapping::GetBusName (std::size_t bus_number) const {
614+ return GetBus (bus_number).name ;
615+ }
616+
535617void BusMapping::Resolve (BusRef &busref, BusMapping::Optional optional) const {
536618 if (req_unique_names_) {
537619 if (busref.id == 0 && busref.name .empty ()) {
@@ -565,6 +647,10 @@ void BusMapping::Resolve(BusRef &busref, BusMapping::Optional optional) const {
565647 }
566648 Error (" PSS(R)E parser: Bus reference number must be positive, got 0" );
567649 }
650+ if (!HasBus (busref.id )) {
651+ Error (" PSS(R)E parser: Bus " + std::to_string (busref.id ) +
652+ " does not exist" );
653+ }
568654 busref.bus = &GetBus (busref.id );
569655 }
570656}
@@ -724,11 +810,16 @@ Network ParseNetwork(std::istream &is) {
724810}
725811
726812Network ParseNetwork (const std::string &filename) {
727- std::ifstream is (filename);
728813 auto quoted_filename = " \' " + filename + " \' " ;
814+ if (!std::filesystem::exists (filename)) {
815+ Error (" PSS(R)E parser: File not found: " + quoted_filename);
816+ }
817+
818+ std::ifstream is (filename);
729819 if (!is) {
730- Error (" Failed to open file: " + quoted_filename);
820+ Error (" PSS(R)E parser: Failed to open file: " + quoted_filename);
731821 }
822+
732823 ExaGOLog (EXAGO_LOG_INFO, " Parsing PSS(R)E raw data file: " + quoted_filename);
733824 auto nw = ParseNetwork (is);
734825 nw.file_name = filename;
0 commit comments