@@ -381,9 +381,9 @@ def __init__(self, output_file_path, logger_name=None):
381
381
self ._logger = _get_logger (logger_name )
382
382
self .output_file_path = output_file_path
383
383
384
- f = open (self .output_file_path )
385
- self ._lines = list (f .readlines ())
386
- f . close ()
384
+ with open (self .output_file_path , encoding = 'utf-8' ) as f :
385
+ self ._lines = list (f .readlines ())
386
+ self . _lines_by_category = self . _get_lines_by_category ()
387
387
388
388
# TODO generic-er result value map
389
389
@@ -393,19 +393,26 @@ def __init__(self, output_file_path, logger_name=None):
393
393
fields = category_fields [1 ]
394
394
395
395
self .result [category ] = {}
396
+ category_lines = self ._lines_by_category .get (category , [])
397
+
396
398
for field in fields :
397
399
if isinstance (field , _EqualSignDelimitedField ):
398
- self .result [category ][field .field_name ] = self ._get_equal_sign_delimited_field (field .field_name )
400
+ self .result [category ][field .field_name ] = self ._get_equal_sign_delimited_field (
401
+ field .field_name , search_lines = category_lines
402
+ )
399
403
elif isinstance (field , _UnlabeledStringField ):
400
404
self .result [category ][field .field_name ] = self ._get_unlabeled_string_field (
401
- field .field_name , field .marker_prefixes
405
+ field .field_name , field .marker_prefixes , search_lines = category_lines
402
406
)
403
407
else :
404
408
is_string_field = isinstance (field , _StringValueField )
405
409
field_name = field .field_name if is_string_field else field
406
410
indent = 4 if category != 'Simulation Metadata' else 1
407
411
self .result [category ][field_name ] = self ._get_result_field (
408
- field_name , is_string_value_field = is_string_field , min_indentation_spaces = indent
412
+ field_name ,
413
+ is_string_value_field = is_string_field ,
414
+ min_indentation_spaces = indent ,
415
+ search_lines = category_lines ,
409
416
)
410
417
411
418
try :
@@ -446,6 +453,35 @@ def __init__(self, output_file_path, logger_name=None):
446
453
if self ._get_end_use_option () is not None :
447
454
self .result ['metadata' ]['End-Use Option' ] = self ._get_end_use_option ().name
448
455
456
+ def _get_lines_by_category (self ) -> dict [str , list [str ]]:
457
+ """
458
+ Parses the raw output file lines into a dictionary where keys are
459
+ category headers and values are the lines belonging to that category.
460
+ """
461
+ lines_by_category = {}
462
+ current_category = None
463
+ known_headers = list (self ._RESULT_FIELDS_BY_CATEGORY .keys ())
464
+
465
+ for line in self ._lines :
466
+
467
+ def get_header_content (h_ : str ) -> str :
468
+ if h_ == 'Simulation Metadata' :
469
+ return h_
470
+ return f'***{ h_ } ***'
471
+
472
+ # Check if the line is a category header
473
+ found_header = next ((h for h in known_headers if get_header_content (h ) == line .strip ()), None )
474
+
475
+ if found_header :
476
+ current_category = found_header
477
+ if current_category not in lines_by_category :
478
+ lines_by_category [current_category ] = []
479
+ elif current_category :
480
+ # Append the line to the current category if one has been found
481
+ lines_by_category [current_category ].append (line )
482
+
483
+ return lines_by_category
484
+
449
485
@property
450
486
def direct_use_heat_breakeven_price_USD_per_MMBTU (self ):
451
487
summary = self .result ['SUMMARY OF RESULTS' ]
@@ -552,9 +588,18 @@ def _json_fields(self) -> MappingProxyType:
552
588
except FileNotFoundError :
553
589
return {}
554
590
555
- def _get_result_field (self , field_name : str , is_string_value_field : bool = False , min_indentation_spaces : int = 4 ):
591
+ def _get_result_field (
592
+ self ,
593
+ field_name : str ,
594
+ is_string_value_field : bool = False ,
595
+ min_indentation_spaces : int = 4 ,
596
+ search_lines : list [str ] | None = None ,
597
+ ):
598
+ if search_lines is None :
599
+ search_lines = self ._lines
600
+
556
601
# TODO make this less fragile with proper regex
557
- matching_lines = set (filter (lambda line : f'{ min_indentation_spaces * " " } { field_name } : ' in line , self . _lines ))
602
+ matching_lines = set (filter (lambda line : f'{ min_indentation_spaces * " " } { field_name } : ' in line , search_lines ))
558
603
559
604
if len (matching_lines ) == 0 :
560
605
self ._logger .debug (f'Field not found: { field_name } ' )
@@ -592,14 +637,17 @@ def normalize_spaces(matched_line):
592
637
593
638
return {'value' : self ._parse_number (str_val , field = f'field "{ field_name } "' ), 'unit' : unit }
594
639
595
- def _get_equal_sign_delimited_field (self , field_name ):
640
+ def _get_equal_sign_delimited_field (self , field_name , search_lines : list [str ] | None = None ):
641
+ if search_lines is None :
642
+ search_lines = self ._lines
643
+
596
644
metadata_markers = (
597
645
f' { field_name } = ' ,
598
646
# Previous versions of GEOPHIRES erroneously included an extra space after the field name so we include
599
647
# the pattern for it for backwards compatibility with existing .out files.
600
648
f' { field_name } = ' ,
601
649
)
602
- matching_lines = set (filter (lambda line : any (m in line for m in metadata_markers ), self . _lines ))
650
+ matching_lines = set (filter (lambda line : any (m in line for m in metadata_markers ), search_lines ))
603
651
604
652
if len (matching_lines ) == 0 :
605
653
self ._logger .debug (f'Equal sign-delimited field not found: { field_name } ' )
@@ -619,8 +667,13 @@ def _get_equal_sign_delimited_field(self, field_name):
619
667
self ._logger .error (f'Unexpected error extracting equal sign-delimited field { field_name } ' ) # Shouldn't happen
620
668
return None
621
669
622
- def _get_unlabeled_string_field (self , field_name : str , marker_prefixes : list [str ]):
623
- matching_lines = set (filter (lambda line : any (m in line for m in marker_prefixes ), self ._lines ))
670
+ def _get_unlabeled_string_field (
671
+ self , field_name : str , marker_prefixes : list [str ], search_lines : list [str ] | None = None
672
+ ):
673
+ if search_lines is None :
674
+ search_lines = self ._lines
675
+
676
+ matching_lines = set (filter (lambda line : any (m in line for m in marker_prefixes ), search_lines ))
624
677
625
678
if len (matching_lines ) == 0 :
626
679
self ._logger .debug (f'Unlabeled string field not found: { field_name } ' )
0 commit comments