23from typing
import List, Optional, Tuple, Union
38 def __init__(self, config, *args, **kwargs):
50 def attach(self, layer):
53 return KeyError(f
"{key} layer already added to DAG")
54 self.
_layers[layer.name] = layer
57 all_edges = defaultdict(set)
58 if layer.has_dependencies:
60 for child_idx, node
in enumerate(layer.nodes):
61 for input_
in node.requires:
63 parent_name, parent_idx = self.
_provides[input_]
64 all_edges[parent_name].add((parent_idx, child_idx))
70 for num, (parent, edges)
in enumerate(all_edges.items()):
84 for idx, node
in enumerate(layer.nodes):
85 for output
in node.provides:
88 def create_log_dir(self, log_dir="logs"):
89 os.makedirs(log_dir, exist_ok=
True)
91 def write_dag(self, filename, path=None, **kwargs):
92 write_dag(self, dag_file_name=filename, dag_dir=path, **kwargs)
94 def write_script(self, filename, path=None, formatter=None):
96 filename = os.path.join(path, filename)
101 with open(filename,
"w")
as f:
103 for layer
in self.walk(dags.WalkOrder(
"BREADTH")):
105 executable = layer.submit_description[
'executable']
106 args = layer.submit_description[
'arguments']
107 args = re.sub(
r"\$\(((\w+?))\)",
r"{\1}", args)
110 for idx, node_vars
in enumerate(layer.vars):
111 node_name = formatter.generate(layer.name, idx)
112 print(f
"# Job {node_name}", file=f)
113 print(executable +
" " + args.format(**node_vars) +
"\n", file=f)
117 """Register a layer to the DAG, making it callable.
120 def wrapped(self, *args, **kwargs):
121 return func(self.
config, self, *args, **kwargs)
122 setattr(cls, layer_name, wrapped)
125 def _get_edge_type(self, parent_name, child_name, edges):
126 parent = self.
_layers[parent_name]
127 child = self.
_layers[child_name]
128 edges = sorted(list(edges))
131 if len(edges) == (len(parent.nodes) + len(child.nodes)):
132 return dags.ManyToMany()
134 elif (len(parent.nodes) == len(child.nodes)
135 and all([parent_idx == child_idx
for parent_idx, child_idx
in edges])):
136 return dags.OneToOne()
143 """Get all registered DAG layers.
146 manager = pluggy.PluginManager(
"gstlal")
147 manager.add_hookspecs(plugins)
150 from gstlal.dags.layers
import io, psd
152 manager.register(psd)
156 for plugin_name
in manager.hook.layers():
157 for name, layer
in plugin_name.items():
158 registered[name] = layer
190def write_dag(dag, dag_dir=None, formatter=None, **kwargs):
195 return htcondor.dags.write_dag(dag, dag_dir, node_name_formatter=formatter, **kwargs)
200 """Defines a command-line argument (positional).
202 This provides some extra functionality over defining command line
203 argument explicitly, in addition to some extra parameters which
204 sets how condor interprets how to handle them within the DAG
205 and within submit descriptions.
210 The option name. Since this is a positional argument, it is not
211 used explicitly in the command, but is needed to define
212 variable names within jobs.
214 The positional argument value(s) used in a command.
216 Whether to track files defined here and used externally within
217 jobs to determine parent-child relationships when nodes specify
218 this option as an input or output. On by default.
220 Whether to allow remapping of output files being transferred.
221 If set, output files will be moved to their target directories
222 after files are transferred back. This is done to avoid issues
223 where the target directories are available on the submit node
224 but not on the exectute node. On by default.
226 Whether to hide this option. Used externally within jobs to
227 determine whether to define job arguments. This is typically used
228 when you want to track file I/O used by a job but isn't directly
229 specified in their commands. Off by default.
231 Same as suppress but allowing transfer remaps to still occur.
232 Used when you want to track file output which is not directly
233 specified in their command but whose file locations changed
234 compared to their inputs. Off by default.
238 >>> Argument("command", "run").vars()
241 >>> files = ["input_1.txt", "input_2.txt"]
242 >>> Argument("input-files", files).vars()
243 'input_1.txt input_2.txt'
247 argument: Union[int, float, str, List]
248 track: Optional[bool] =
True
249 remap: Optional[bool] =
True
250 suppress: Optional[bool] =
False
251 suppress_with_remap: Optional[bool] =
False
253 def __post_init__(self):
260 if isinstance(self.
argument, str)
or not isinstance(self.
argument, Iterable):
271 def arg_basename(self):
272 return [os.path.basename(arg)
for arg
in self.
argument]
274 def vars(self, basename=False):
275 if callable(basename):
281 args.append(os.path.basename(arg))
284 return " ".join(args)
290 def files(self, basename=False):
294 return ";".join([f
"{base}={arg}" for base, arg
in zip(self.
arg_basename, self.
argument)
if base != arg])
299 """Defines a command-line option (long form).
301 This provides some extra functionality over defining command line
302 options explicitly, in addition to some extra parameters which
303 sets how condor interprets how to handle them within the DAG
304 and within submit descriptions.
309 The option name to be used in a command.
311 The argument value(s) used in a command.
313 Whether to track files defined here and used externally within
314 jobs to determine parent-child relationships when nodes specify
315 this option as an input or output. On by default.
317 Whether to allow remapping of output files being transferred.
318 If set, output files will be moved to their target directories
319 after files are transferred back. This is done to avoid issues
320 where the target directories are available on the submit node
321 but not on the exectute node. On by default.
323 Whether to hide this option. Used externally within jobs to
324 determine whether to define job arguments. This is typically used
325 when you want to track file I/O used by a job but isn't directly
326 specified in their commands. Off by default.
328 Same as suppress but allowing transfer remaps to still occur.
329 Used when you want to track file output which is not directly
330 specified in their command but whose file locations changed
331 compared to their inputs. Off by default.
335 >>> Option("verbose").vars()
338 >>> Option("input-type", "file").vars()
341 >>> Option("ifos", ["H1", "L1", "V1"]).vars()
342 '--ifos H1 --ifos L1 --ifos V1'
346 argument: Optional[Union[int, float, str, List]] =
None
347 track: Optional[bool] =
True
348 remap: Optional[bool] =
True
349 suppress: Optional[bool] =
False
350 suppress_with_remap: Optional[bool] =
False
352 def __post_init__(self):
360 if isinstance(self.
argument, str)
or not isinstance(self.
argument, Iterable):
371 def arg_basename(self):
372 return [os.path.basename(arg)
for arg
in self.
argument]
374 def vars(self, basename=False):
376 return f
"--{self.name}"
377 elif callable(basename):
383 args.append(f
"--{self.name} {os.path.basename(arg)}")
385 args.append(f
"--{self.name} {arg}")
386 return " ".join(args)
388 return " ".join([f
"--{self.name} {arg}" for arg
in self.
arg_basename])
390 return " ".join([f
"--{self.name} {arg}" for arg
in self.
argument])
392 def files(self, basename=False):
396 return ";".join([f
"{base}={arg}" for base, arg
in zip(self.
arg_basename, self.
argument)
if base != arg])