@@ -196,6 +196,8 @@ def __init__(self, file_path):
196196 self ._input_lock = None
197197 self ._software_lock = None
198198 self ._experiment_graph = None
199+ # A dict storing fom values, currently it only stores inmem FOMs
200+ self ._fom_map = {}
199201
200202 # Ensure we always have the application name, and this is never empty
201203 self .license_names = self .license_names + [self .name ]
@@ -1427,12 +1429,8 @@ def _define_commands(self, exec_graph, success_list=None):
14271429 )
14281430 logs .append (expanded_log )
14291431
1430- analysis_logs , _ = self ._analysis_dicts (success_list )
1431-
1432- for log in analysis_logs :
1433- logs .append (log )
1434-
1435- logs = list (dict .fromkeys (logs ))
1432+ analysis_logs , _ , _ = self ._analysis_dicts (success_list )
1433+ logs = list (set (logs ) | analysis_logs .keys ())
14361434
14371435 for log in logs :
14381436 self ._command_list .append ('rm -f "%s"' % log )
@@ -2156,7 +2154,7 @@ def _archive_experiments(self, workspace, app_inst=None):
21562154
21572155 # Copy all figure of merit files
21582156 criteria_list = self .success_list
2159- analysis_files , _ = self ._analysis_dicts (criteria_list )
2157+ analysis_files , _ , _ = self ._analysis_dicts (criteria_list )
21602158 for file in analysis_files .keys ():
21612159 if os .path .exists (file ):
21622160 shutil .copy (file , archive_experiment_dir )
@@ -2201,6 +2199,30 @@ def _prepare_analysis(self, workspace, app_inst=None):
22012199 """
22022200 pass
22032201
2202+ def _extract_inmem_foms (self , inmem_fom_defs , fom_values ):
2203+ """Extract in-memory FOMs"""
2204+ for context , foms in inmem_fom_defs .items ():
2205+ if context not in fom_values :
2206+ fom_values [context ] = {}
2207+ foms = inmem_fom_defs [context ]["foms" ]
2208+ for fom in foms :
2209+ fom_conf = inmem_fom_defs [context ]["foms" ][fom ]
2210+ # Currently inmem FOM does not have semantics for expanded vars,
2211+ # so use the already expanded name and unit
2212+ fom_name = fom_conf ["fom_name_expanded" ]
2213+ # TODO: this can be extended to support derived FOMs,
2214+ # since the `fom_values` contains resolved file-based FOMs
2215+ fom_map_key = fom_conf ["fom_map_key" ]
2216+ fom_value = self ._fom_map .get (fom_map_key )
2217+ expanded_fom_value = self .expander .expand_var (fom_value )
2218+ fom_values [context ][fom_name ] = {
2219+ "value" : expanded_fom_value ,
2220+ "units" : fom_conf ["units_expanded" ],
2221+ "origin" : fom_conf ["origin" ],
2222+ "origin_type" : fom_conf ["origin_type" ],
2223+ "fom_type" : fom_conf ["fom_type" ],
2224+ }
2225+
22042226 register_phase (
22052227 "analyze_experiments" ,
22062228 pipeline = "analyze" ,
@@ -2247,17 +2269,16 @@ def format_context(context_match, context_format):
22472269 context_string = context_format .format (** context_val )
22482270 return context_string
22492271
2250- fom_values = {}
2251-
22522272 criteria_list = self .success_list
22532273 if not criteria_list :
22542274 criteria_list = ramble .success_criteria .ScopedCriteriaList ()
22552275 criteria_list .reset ()
22562276
2257- files , definitions = self ._analysis_dicts (criteria_list )
2277+ files , f_defs , inmem_defs = self ._analysis_dicts (criteria_list )
22582278
22592279 exp_lock = self .experiment_lock ()
22602280
2281+ fom_values = {}
22612282 # Iterate over files. We already know they exist
22622283 with lk .ReadTransaction (exp_lock ):
22632284 for file , file_conf in files .items ():
@@ -2292,9 +2313,7 @@ def format_context(context_match, context_format):
22922313 # Iterate over contexts and add matched contexts to active_contexts
22932314 for context , foms in file_conf ["contexts" ].items ():
22942315 if not context == _NULL_CONTEXT :
2295- context_conf = definitions [context ][
2296- "definition"
2297- ]
2316+ context_conf = f_defs [context ]["definition" ]
22982317 context_match = context_conf ["regex" ].match (
22992318 line
23002319 )
@@ -2314,7 +2333,7 @@ def format_context(context_match, context_format):
23142333 fom_values [context_name ] = {}
23152334
23162335 for fom in foms :
2317- fom_conf = definitions [context ]["foms" ][fom ]
2336+ fom_conf = f_defs [context ]["foms" ][fom ]
23182337 fom_match = fom_conf ["regex" ].match (line )
23192338
23202339 if fom_match :
@@ -2389,6 +2408,7 @@ def format_context(context_match, context_format):
23892408 "fom_type"
23902409 ],
23912410 }
2411+ self ._extract_inmem_foms (inmem_defs , fom_values )
23922412
23932413 # Test all non-file based success criteria
23942414 for criteria_obj in criteria_list .all_criteria ():
@@ -2397,7 +2417,7 @@ def format_context(context_match, context_format):
23972417 criteria_obj .mark_found ()
23982418
23992419 # If an app has no FOMs defined, don't fail it for that
2400- success = (not definitions ) or False
2420+ success = (not f_defs and not inmem_defs ) or False
24012421 for fom in fom_values .values ():
24022422 for value in fom .values ():
24032423 if (
@@ -2733,12 +2753,13 @@ def _analysis_dicts(self, criteria_list):
27332753
27342754 Returns:
27352755 files (dict): All files that need to be processed
2736- contexts (dict): Any contexts that have been defined
2737- foms (dict): All figures of merit that need to be extracted
2756+ file_fom_defs (dict): Definitions of all file-backed FOMs to be extracted
2757+ inmem_fom_defs (dict): Definitions of all in-memory FOMs to be extracted
27382758 """
27392759
27402760 files = {}
2741- definitions = {}
2761+ file_fom_defs = {}
2762+ inmem_fom_defs = {}
27422763
27432764 # Add the application defined criteria
27442765 criteria_list .flush_scope ("application_definition" )
@@ -2861,32 +2882,38 @@ def _analysis_dicts(self, criteria_list):
28612882 "definitions and 'when' conditions."
28622883 )
28632884
2864- # Copy context definition for contexts used by a FOM
2865- if context not in definitions :
2866- definitions [context ] = {
2867- "definition" : {},
2868- "foms" : {},
2869- }
2870- if context != _NULL_CONTEXT :
2871- regex_str = self .expander .expand_var (
2872- all_contexts [context ]["regex" ]
2873- )
2874- definitions [context ]["definition" ] = {
2875- "regex" : re .compile (regex_str ),
2876- "format" : all_contexts [context ][
2877- "output_format"
2878- ],
2885+ def _preset_context_dict (dest_def_dict , context ):
2886+ # Copy context definition for contexts used by a FOM
2887+ if context not in dest_def_dict :
2888+ dest_def_dict [context ] = {
2889+ "definition" : {},
2890+ "foms" : {},
28792891 }
2892+ if context != _NULL_CONTEXT :
2893+ regex_str = self .expander .expand_var (
2894+ all_contexts [context ]["regex" ]
2895+ )
2896+ dest_def_dict [context ]["definition" ] = {
2897+ "regex" : re .compile (regex_str ),
2898+ "format" : all_contexts [context ][
2899+ "output_format"
2900+ ],
2901+ }
28802902
28812903 for fom , source_def in source_foms .items ():
2882- if fom in definitions [context ]["foms" ]:
2904+ is_inmem = source_def ["fom_map_key" ] is not None
2905+ dest_def_dict = (
2906+ inmem_fom_defs if is_inmem else file_fom_defs
2907+ )
2908+ _preset_context_dict (dest_def_dict , context )
2909+ if fom in dest_def_dict [context ]["foms" ]:
28832910 logger .warn (
28842911 f"FOM { fom } already defined in context { context } by "
2885- f"{ definitions [context ]['foms' ][fom ]['origin' ]} . "
2912+ f"{ dest_def_dict [context ]['foms' ][fom ]['origin' ]} . "
28862913 f"Overwriting with new definition from { source .name } "
28872914 )
28882915 else :
2889- definitions [context ]["foms" ][fom ] = {}
2916+ dest_def_dict [context ]["foms" ][fom ] = {}
28902917
28912918 def _expand_var (var ):
28922919 return self .expander .expand_var (
@@ -2905,12 +2932,21 @@ def _try_expand_var_or_none(var: str, expander):
29052932 "origin" : source .name ,
29062933 "origin_type" : source .origin_type ,
29072934 "contexts" : set (source_def ["contexts" ]),
2908- "group" : _expand_var (source_def ["group_name" ]),
2935+ "group" : (
2936+ ""
2937+ if is_inmem
2938+ else _expand_var (source_def ["group_name" ])
2939+ ),
29092940 "units" : _expand_var (source_def ["units" ]),
2910- "regex" : re .compile (
2911- _expand_var (source_def ["regex" ])
2941+ "regex" : (
2942+ ""
2943+ if is_inmem
2944+ else re .compile (
2945+ _expand_var (source_def ["regex" ])
2946+ )
29122947 ),
29132948 "fom_type" : source_def ["fom_type" ].to_dict (),
2949+ "fom_map_key" : source_def ["fom_map_key" ],
29142950 # If expansion works (i.e., it doesn't rely on the matched fom
29152951 # groups), then cache it here to avoid repeated expansion later.
29162952 "units_expanded" : _try_expand_var_or_none (
@@ -2921,8 +2957,10 @@ def _try_expand_var_or_none(var: str, expander):
29212957 ),
29222958 }
29232959
2924- definitions [context ]["foms" ][fom ] = fom_def
2960+ dest_def_dict [context ]["foms" ][fom ] = fom_def
29252961
2962+ if is_inmem :
2963+ continue
29262964 log_path = _expand_var (source_def ["log_file" ])
29272965 # Ensure log path is absolute. If not, prepend the experiment run dir
29282966 if (
@@ -2944,7 +2982,11 @@ def _try_expand_var_or_none(var: str, expander):
29442982 logger .debug ("Log = %s" % log_path )
29452983 logger .debug ("Conf = %s" % fom_def )
29462984
2947- return files , definitions
2985+ return files , file_fom_defs , inmem_fom_defs
2986+
2987+ def add_inmem_fom_value (self , fom_map_key , value ):
2988+ """Add value to an in-memory FOM"""
2989+ self ._fom_map [fom_map_key ] = value
29482990
29492991 def read_status (self ):
29502992 """Read status from an experiment's status file, if possible.
0 commit comments