3232 OS_ENV_KEY_NGEN_LOG_FILE_PREFIX ,
3333 OS_ENV_KEY_RESULTS_DIR ,
3434 initialize_logger ,
35+ initialize_hindcast_logger ,
3536 set_os_env_key ,
3637)
3738
5051# setup the logger
5152logger , _ = initialize_logger ()
5253
54+ # Set dedicated ewts_id for hindcast orchesetration logger.
55+ HINDCAST_LOGGER_ID = "hindcast_logger"
56+
5357
5458class ConfigCache :
5559 """
@@ -68,7 +72,6 @@ def __init__(self, valid_yaml: str = None, run_dir: str = None, no_valid: bool =
6872 raise ValueError (msg )
6973 self .valid_yaml = valid_yaml
7074 self .valid_config = load_yaml (valid_yaml )
71- logger .info (f"Validation file loaded from: { valid_yaml } " )
7275 self .gpkg_cats , self .gpkg_nexus , self .ngen_exe , self .gage0 = extract_config (
7376 self .valid_config , self .valid_yaml
7477 )
@@ -107,7 +110,7 @@ class ForecastExecutionManager:
107110
108111 Parameters
109112 ----------
110- real_path : str
113+ real_path : str
111114 Path to existing realization file
112115 config_cache : ConfigCache
113116 Instance of ConfigCache
@@ -420,7 +423,7 @@ def postprocess(self, suppress_output: bool = False) -> None:
420423 self .output_csv = Path (run_output_dir , self .gage0 + "_output.csv" )
421424 output .to_csv (self .output_csv )
422425
423- logger .info (f"Fcst-mgr NGEN run outputs saved at: { run_output_dir } " )
426+ logger .info (f"Fcst-mgr NGEN postprocessing outputs saved at: { run_output_dir } " )
424427
425428 self ._status = RunStatus .POSTPROCESSED
426429 logger .status (
@@ -775,7 +778,11 @@ def run_hindcast(input_path, valid_yaml, fcst_run_name, cycle_interval, num_iter
775778 If provided, will be used for first hindcast cycle (hind_cycle=0)
776779 Subsequent cycles will use warm start states
777780 """
778- logger .info (f'Initializing hindcast runs from: { valid_yaml } ' )
781+ # Set up hindcast orchestration logger, initialized once the hindcast root directory is known
782+ hindcast_logger = None
783+
784+ # Buffer messages logged before orchestration log's directory is resolvable (before first build_fcst() call)
785+ pending_logs = [f'Initializing hindcast runs from: { valid_yaml } ' ]
779786
780787 if valid_yaml is None :
781788 msg = "valid_yaml must be provided for hindcast run"
@@ -791,7 +798,7 @@ def run_hindcast(input_path, valid_yaml, fcst_run_name, cycle_interval, num_iter
791798 # Validate that all hindcast intervals fall on valid cycle hours for this configuration
792799 check_hind_intervals (input_path , hind_interval )
793800
794- logger . info (f"Initializing hindcast runs at intervals: { hind_interval } " )
801+ pending_logs . append (f"Initializing hindcast runs at intervals: { hind_interval } " )
795802
796803 # Initialize previous hindcast cycle for coordinating warm starts
797804 prev_hind_cycle = 0
@@ -805,19 +812,19 @@ def run_hindcast(input_path, valid_yaml, fcst_run_name, cycle_interval, num_iter
805812 # Skip warm start for first hindcast, which will use the cold start state
806813 if hind_cycle != 0 :
807814
808- logger .info (f"Initializing warm start AnA run for hindcast iteration at { hind_cycle } hours" )
815+ hindcast_logger .info (f"Initializing warm start AnA run for hindcast iteration at { hind_cycle } hours" )
809816
810817 # Generate msw-mgr inputs for warm start run for hindcast iteration
811818 warm_start_real_path , warm_start_state = build_fcst (input_path = input_path , valid_yaml = valid_yaml ,
812819 fcst_run_name = fcst_run_name , use_warm_start = True ,
813820 hind_cycle = hind_cycle , prev_hind_cycle = prev_hind_cycle ,
814821 save_state = True , load_state_from = prev_warm_start_state )
815- logger .info (f"Warm start realization file for hindcast iteration at { hind_cycle } hours written to: { warm_start_real_path } " )
822+ hindcast_logger .info (f"Warm start realization file for hindcast iteration at { hind_cycle } hours written to: { warm_start_real_path } " )
816823
817824 # Execute warm start ngen run to generate hindcasting model states
818825 run_workflow (warm_start_real_path , config_cache , suppress_output = True )
819- logger .info (f"Warm start run for hindcast iteration at { hind_cycle } hours completed" )
820- logger .info (f"Warm start state saved to { warm_start_state } " )
826+ hindcast_logger .info (f"Warm start run for hindcast iteration at { hind_cycle } hours completed" )
827+ hindcast_logger .info (f"Warm start state saved to { warm_start_state } " )
821828
822829 # Update state to be used by warm start in next iteration
823830 prev_warm_start_state = warm_start_state
@@ -831,24 +838,37 @@ def run_hindcast(input_path, valid_yaml, fcst_run_name, cycle_interval, num_iter
831838 'hind_cycle' : hind_cycle
832839 }
833840
834- logger .info (f"Initializing hindcast run for iteration at { hind_cycle } hours" )
841+ msg = f"Initializing hindcast run for iteration at { hind_cycle } hours"
842+ if hind_cycle == 0 :
843+ pending_logs .append (msg )
844+ else :
845+ hindcast_logger .info (msg )
835846
836847 # Load from cold start state for first cycle if it's provided
837848 if hind_cycle == 0 :
838849 if cold_start_state is not None :
839850 hind_kwargs ['load_state_from' ] = cold_start_state
840- logger . info (f"Hindcast iteration at { hind_cycle } hours loading state from: { cold_start_state } " )
851+ pending_logs . append (f"Hindcast iteration at { hind_cycle } hours loading state from: { cold_start_state } " )
841852 # Otherwise, load from warm start state
842853 else :
843854 hind_kwargs ['load_state_from' ] = warm_start_state
844- logger .info (f"Hindcast iteration at { hind_cycle } hours loading state from: { warm_start_state } " )
855+ hindcast_logger .info (f"Hindcast iteration at { hind_cycle } hours loading state from: { warm_start_state } " )
845856
846857 hind_real_path , _ = build_fcst (** hind_kwargs )
847- logger .info (f"Hindcast realization file for iteration at { hind_cycle } hours written to: { hind_real_path } " )
858+
859+ # Initialize hindcast orchestration logger after first build_fcst() call; hindcast root directory now resolvable
860+ if hindcast_logger is None :
861+ hindcast_root = Path (hind_real_path ).parent .parent
862+ hindcast_logger = initialize_hindcast_logger (str (hindcast_root ))
863+ # Flush pending logs
864+ for msg in pending_logs :
865+ hindcast_logger .info (msg )
866+
867+ hindcast_logger .info (f'Hindcast realization file for iteration at { hind_cycle } hours written to: { hind_real_path } ' )
848868
849869 # Run hindcasting period
850870 run_workflow (hind_real_path , config_cache )
851- logger .info (f"Hindcast run for iteration at { hind_cycle } hours completed" )
871+ hindcast_logger .info (f"Hindcast run for iteration at { hind_cycle } hours completed" )
852872
853873 # Store previous hindcast cycle value to set next warm start duration
854874 prev_hind_cycle = hind_cycle
0 commit comments