77
88import xarray as xr
99from access .config import YAMLParser
10+ from experiment_runner .experiment_runner import ExperimentRunner
1011
11- from access .profiling .manager import ProfilingLog , ProfilingManager
12+ from access .profiling .manager import ProfilingExperiment , ProfilingExperimentStatus , ProfilingLog , ProfilingManager
1213from access .profiling .payujson_parser import PayuJSONProfilingParser
1314
1415logger = logging .getLogger (__name__ )
1516
1617
1718class PayuManager (ProfilingManager , ABC ):
18- """Abstract base class to handle profiling of Payu configurations."""
19+ """Abstract base class to handle profiling of Payu configurations.
20+
21+ Args:
22+ work_dir (Path): Path to directory used to generate and run profiling experiments.
23+ config_name (str): User supplied name. It is used to build some internal paths, but has no other effect.
24+ """
25+
26+ config_name : str # User supplied name. It is used to build some internal paths, but has no other effect.
27+ _nruns : int = 1 # Number of repetitions for the Payu experiments.
28+ _startfrom_restart : str = "cold" # Restart option for the Payu experiments.
29+
30+ def __init__ (self , work_dir : Path , config_name : str | None = None ) -> None :
31+ super ().__init__ (work_dir = work_dir )
32+ if config_name is None :
33+ self .config_name = "config"
34+ else :
35+ self .config_name = config_name
1936
2037 @abstractmethod
2138 def get_component_logs (self , path : Path ) -> dict [str , ProfilingLog ]:
@@ -27,6 +44,86 @@ def get_component_logs(self, path: Path) -> dict[str, ProfilingLog]:
2744 dict[str, ProfilingLog]: Dictionary mapping component names to their ProfilingLog instances.
2845 """
2946
47+ @property
48+ def nruns (self ) -> int :
49+ """Returns the number of repetitions for the Payu experiments.
50+
51+ Returns:
52+ int: Number of repetitions.
53+ """
54+ return self ._nruns
55+
56+ @nruns .setter
57+ def nruns (self , value : int ) -> None :
58+ """Sets the number of repetitions for the Payu experiments.
59+
60+ Args:
61+ value (int): Number of repetitions.
62+ """
63+ if value < 1 :
64+ raise ValueError ("Number of runs must be at least 1." )
65+ self ._nruns = value
66+
67+ @property
68+ def startfrom_restart (self ) -> str :
69+ """Returns the restart option for the Payu experiments.
70+
71+ Returns:
72+ str: Restart option.
73+ """
74+ return self ._startfrom_restart
75+
76+ @startfrom_restart .setter
77+ def startfrom_restart (self , value : str ) -> None :
78+ """Sets the restart option for the Payu experiments.
79+
80+ Args:
81+ value (str): Restart option.
82+ """
83+ self ._startfrom_restart = value
84+
85+ def generate_experiments (self , branches : list [str ]) -> None :
86+ """Generates Payu experiments for profiling data generation.
87+
88+ Args:
89+ branches (list[str]): List of branches to generate experiments for.
90+ """
91+
92+ for branch in branches :
93+ if branch in self .experiments :
94+ logger .info (f"Experiment for branch { branch } already exists. Skipping addition." )
95+ else :
96+ self .experiments [branch ] = ProfilingExperiment (self .work_dir / branch / self .config_name )
97+
98+ def run_experiments (self ) -> None :
99+ """Runs Payu experiments for profiling data generation."""
100+
101+ runner_config = {
102+ "test_path" : self .work_dir ,
103+ "repository_directory" : self .config_name ,
104+ "running_branches" : [],
105+ "keep_uuid" : True ,
106+ "nruns" : [],
107+ "startfrom_restart" : [],
108+ }
109+
110+ for path , exp in self .experiments .items ():
111+ print (path , exp .status )
112+ if exp .status == ProfilingExperimentStatus .NEW :
113+ runner_config ["running_branches" ].append (path )
114+ runner_config ["nruns" ].append (self .nruns )
115+ runner_config ["startfrom_restart" ].append (self .startfrom_restart )
116+ exp .status = ProfilingExperimentStatus .RUNNING
117+
118+ # Run the experiment runner
119+ ExperimentRunner (runner_config ).run ()
120+
121+ # We are marking all running experiments as done here, but later this should be implemented properly
122+ # so that an actual check is performed, probably somewhere else.
123+ for exp in self .experiments .values ():
124+ if exp .status == ProfilingExperimentStatus .RUNNING :
125+ exp .status = ProfilingExperimentStatus .DONE
126+
30127 def parse_ncpus (self , path : Path ) -> int :
31128 """Parses the number of CPUs used in a given Payu experiment.
32129
0 commit comments