def __init__(self, suite, clock): typecheck('clock', clock, Clock) typecheck('suite', suite, Suite) self.__clock = copy.copy(clock) self.__suite = suite self.__nodes = collections.defaultdict(dict) self.__cycles = collections.defaultdict(OrderedDict)
def node_size(self, rank_spec): typecheck('rank_spec', rank_spec, crow.sysenv.jobs.JobRankSpec) can_hyper = self.hyperthreading_allowed max_per_node = self.cores_per_node if can_hyper and rank_spec.get('hyperthreading', False): max_per_node *= self.cpus_per_core return max_per_node
def __init__(self, child, locals, path=''): typecheck('child', child, Sequence) self.__child = list(child) self.__cache = list(child) self.__locals = locals self.__globals = {} self._path = path
def subdict_iter(d): typecheck('d', d, Mapping) dkeys = [k for k in d.keys()] vallist = [v for v in d.values()] piter = itertools.product(*vallist) dvalues = [p for p in piter] for j in range(len(dvalues)): yield dict([i for i in zip(dkeys, dvalues[j])])
def __init__(self, child, path='', globals=None): #assert(not isinstance(child,dict_eval)) typecheck('child', child, Mapping) self.__child = copy(child) self.__cache = copy(child) self.__globals = {} if globals is None else globals self.__is_validated = False self._path = path
def assume(tree, existing_cycles, current_cycle, assume_complete=None, assume_never_run=None): typecheck('tree', tree, LogicalDependency) if isinstance(tree, CycleExistsDependency): rel_cycle = tree.dt + current_cycle if rel_cycle in existing_cycles: return TRUE_DEPENDENCY return FALSE_DEPENDENCY elif isinstance(tree, TaskExistsDependency): cycle = current_cycle + tree.view.path[0] if assume_complete and assume_complete(tree.path) or \ assume_never_run and assume_never_run(tree.path): return FALSE_DEPENDENCY alarm = tree.view.get_alarm(default=existing_cycles) if cycle in alarm: return TRUE_DEPENDENCY else: return FALSE_DEPENDENCY elif isinstance(tree, AndDependency): a = TRUE_DEPENDENCY for d in tree: a = a & assume(d, existing_cycles, current_cycle, assume_complete, assume_never_run) return a elif isinstance(tree, OrDependency): a = FALSE_DEPENDENCY for d in tree: a = a | assume(d, existing_cycles, current_cycle, assume_complete, assume_never_run) return a elif isinstance(tree, NotDependency): return ~assume(tree.depend, existing_cycles, current_cycle, assume_complete, assume_never_run) elif isinstance(tree, StateDependency): if assume_never_run and assume_never_run(tree.path): return FALSE_DEPENDENCY if assume_complete and assume_complete(tree.path): return TRUE_DEPENDENCY if tree.state==COMPLETED \ else FALSE_DEPENDENCY if current_cycle + tree.path[0] not in existing_cycles: # Prior cycle tasks will never complete, run, or fail. return FALSE_DEPENDENCY return tree elif isinstance(tree, EventDependency): if assume_never_run and assume_never_run(tree.event.parent.path): return FALSE_DEPENDENCY if assume_complete and assume_complete(tree.event.parent.path): return FALSE_DEPENDENCY if current_cycle + tree.path[0] not in existing_cycles: # Prior cycle events will never be set. return FALSE_DEPENDENCY return tree return tree
def convert_event_dep(fd,task,dep_path,event_name,clock,time_format,negate,undated): assert(isinstance(undated,OrderedDict)) typecheck('clock',clock,crow.tools.Clock) task_path,did_undated=undate_path(clock.now,time_format,task.path,undated) dep_path,did_undated=undate_path(clock.now,time_format,dep_path,undated) rel_path=relative_path(task_path,dep_path) if did_undated and rel_path[0]=='/': undated[rel_path]=1 fd.write(f'{rel_path}:{event_name}{" is clear" if negate else ""}')
def __init__(self, view, state): if state not in [COMPLETED, RUNNING, FAILED]: raise TypeError('Invalid state. Must be one of the constants ' 'COMPLETED, RUNNING, or FAILED') typecheck('view', view, SuiteView) if isinstance(view, SlotView): raise NotImplementedError('Data dependencies are not implemented') self.view = view self.state = state
def convert_state_dep(fd,task,dep,clock,time_format,negate,undated): assert(isinstance(undated,OrderedDict)) typecheck('clock',clock,crow.tools.Clock) task_path,did_undated=undate_path(clock.now,time_format,task.path,undated) dep_path,did_undated=undate_path(clock.now,time_format,dep.view.path,undated) rel_path=relative_path(task_path,dep_path) if did_undated and rel_path[0]=='/': undated[rel_path]=1 state=ECFLOW_STATE_MAP[dep.state] fd.write(f'{rel_path} {"!=" if negate else "=="} {state}')
def _rocotoify_dep(self, dep, defining_path): typecheck('dep', dep, LogicalDependency) try: if dep in self.__rocotoified: return self.__rocotoified[dep] roco = self._rocotoify_dep_impl(dep, defining_path) self.__rocotoified[dep] = roco return roco except RecursionError as re: raise SelfReferentialDependency( f'/{"/".join([str(d) for d in defining_path[1:]])}: ' 'cyclic dependency graph referenced from this task.')
def representer(dumper,data): assert('up' not in data) typecheck('data',data,Mapping) raw_data=data._raw_child() typecheck('data._raw_child()',raw_data,Mapping) try: if key is None: return dumper.represent_data(raw_data) else: return dumper.represent_mapping(key,raw_data) except(IndexError,TypeError,ValueError) as e: _logger.error(f'{data._path}: cannot represent: {e} (key={key})') raise
def __init__(self, view, cycle): self.view = view self.trigger = TRUE_DEPENDENCY self.complete = FALSE_DEPENDENCY self.time = ZERO_DT self.cycle = cycle self.alarm = view.get_alarm() self.trigger = view.get_trigger_dep().copy_dependencies() self.complete = view.get_complete_dep().copy_dependencies() if 'Time' in view and view.Time is not None: typecheck('Time', view.Time, datetime.timedelta) self.time = copy.copy(view.Time) self.children = collections.OrderedDict()
def remove_undefined_tasks(self, tree): typecheck('tree', tree, LogicalDependency) # NOTE: Do not remove event dependencies for undefined tasks. # They are critical to allow ecflow to use a task that waits # for data and sets an event while rocoto uses a data event # with no task. if isinstance(tree, StateDependency): # Node is not defined, so assume it is complete dep_path = SuitePath([_ZERO_DT] + tree.view.path[1:]) if dep_path not in self.__all_defined: tree = TRUE_DEPENDENCY elif isinstance(tree, NotDependency): tree = ~self.remove_undefined_tasks(tree.depend) elif isinstance(tree, AndDependency) or isinstance(tree, OrDependency): deplist = [self.remove_undefined_tasks(t) for t in tree] tree = type(tree)(*deplist) return tree
def omp_threads_for(self, rank_spec): typecheck('rank_spec', rank_spec, crow.sysenv.jobs.JobRankSpec) omp_threads = max(1, rank_spec.get('OMP_NUM_THREADS', 1)) # print('omp_thread = ',omp_threads) if omp_threads != MAXIMUM_THREADS: return omp_threads can_hyper = self.hyperthreading_allowed max_threads_per_node = self.cores_per_node if can_hyper and rank_spec.get('hyperthreads', False): max_threads_per_node *= max( 1, min(self.cpus_per_core, rank_spec.hyperthreads)) result = max_threads_per_node // self.max_ranks_per_node(rank_spec) # print('max_threads_per_node = ', max_threads_per_node) # print('self.max_ranks_per_node(rank_spec) = ',self.max_ranks_per_node(rank_spec)) return result
def omp_threads_for(self, rank_spec): typecheck('rank_spec', rank_spec, crow.sysenv.jobs.JobRankSpec) omp_threads = max(1, rank_spec.get('OMP_NUM_THREADS', 1)) if omp_threads != MAXIMUM_THREADS: return omp_threads can_hyper = self.hyperthreading_allowed max_ranks_per_node = self.cores_per_node if can_hyper and rank_spec.get('hyperthreading', False): max_ranks_per_node *= self.cpus_per_core if rank_spec.is_mpi(): ppn = max_ranks_per_node else: ppn = 1 max_ppn = rank_spec.get('max_ppn', 0) if max_ppn: ppn = min(max_ppn, ppn) return max_ranks_per_node // ppn
def assume(self, clock, assume_complete=None, assume_never_run=None): trigger0 = self.trigger complete0 = self.complete typecheck('self.alarm', self.alarm, Clock) if self.cycle not in self.alarm: self.trigger = FALSE_DEPENDENCY self.complete = FALSE_DEPENDENCY elif self.view.get('Disable', False): self.trigger = FALSE_DEPENDENCY self.complete = FALSE_DEPENDENCY else: self.trigger = algebra_simplify( algebra_assume(self.trigger, clock, self.cycle, assume_complete, assume_never_run)) self.complete = algebra_simplify( algebra_assume(self.complete, clock, self.cycle, assume_complete, assume_never_run)) if trigger0 != self.trigger or complete0 != self.complete: return True return False
def _duplicate(self, parent, dimensions, dimval, dimidx): child_dimensions = dimensions if 'Foreach' in self: typecheck(f'{self._path}.Foreach', self.Foreach, Sequence, 'sequence') d2 = dict() for idxname in self.Foreach: if idxname in dimensions: d2[idxname] = dimensions[idxname] else: raise KeyError( f'{self._path}.Foreach: {idxname}: no such dimension') dimensions = d2 dict_iter = [{}] if dimensions: dimensions_to_dimidx = dict() for k, v in dimensions.items(): dimensions_to_dimidx[k] = [n for n in range(len(v))] dict_iter = subdict_iter(dimensions_to_dimidx) for more_dimidx in dict_iter: child_dimidx = copy(dimidx) child_dimidx.update(more_dimidx) child_dimval = dict() for i_dimname, i_dimidx in child_dimidx.items(): child_dimval[i_dimname] = dimensions[i_dimname][i_dimidx] cls = ARRAY_ELEMENT_TYPE_MAP[type(self)] t = cls(self._raw_child(), globals=self._globals()) t._path = self._path # used if Name is missing t['dimlist'] = dict_eval(dimensions) t['dimval'] = dict_eval(child_dimval) t['dimidx'] = dict_eval(child_dimidx) name = t.Name t._path = f'{parent._path}.{name}' for k, v in self._raw_child().items(): if hasattr(v, '_duplicate'): for name2, content2 in v._duplicate( t, child_dimensions, dimval, dimidx): t[name2] = content2 yield name, t
def max_ranks_per_node(self, rank_spec): typecheck('rank_spec', rank_spec, crow.sysenv.jobs.JobRankSpec, print_contents=True) can_hyper = self.hyperthreading_allowed max_per_node = self.cores_per_node max_threads_per_node = self.cores_per_node if can_hyper and rank_spec.get('hyperthreads', False): max_threads_per_node *= max( 1, min(self.cpus_per_core, rank_spec.hyperthreads)) max_per_node *= max( 1, min(self.cpus_per_core, rank_spec.hyperthreads)) threads_per_node = max_per_node omp_threads = max(1, rank_spec.get('OMP_NUM_THREADS', 1)) if omp_threads != MAXIMUM_THREADS: max_per_node //= omp_threads elif rank_spec.mpi_ranks < 2: # Special case: maximum threads with non-MPI job, so return 1 return 1 max_ppn = rank_spec.get('max_ppn', 0) if max_ppn: max_per_node = min(max_ppn, max_per_node) if self.memory_per_node: max_per_node = int( min(max_per_node, self.memory_per_node / rank_spec.memory_per_rank)) if max_per_node < 1: raise MachineTooSmallError( f'Specification too large for node: max threads {threads_per_node} for {rank_spec!r} in partition with {self.cores_per_node} cores per node{"" if not self.memory_per_node else ("and "+str(self.memory_per_node)+" MB of RAM per node")}.' ) return max_per_node
def from_suite(suite, filename): typecheck('suite', suite, Suite) typecheck('filename', filename, str) df = Dataflow(filename) # First pass: add output slots: for actor, slot, sdata in _walk_task_tree_for(suite, ConfigOutputSlot): loc, meta = _parse_slot(actor, slot, sdata, 'O') df.add_output_slot(actor, slot, loc, meta) # Second pass: add input slots: for actor, slot, sdata in _walk_task_tree_for(suite, ConfigInputSlot): meta = _parse_slot(actor, slot, sdata, 'I') _logger.debug(f'{actor}.{slot}: add input slot with meta {meta}') df.add_input_slot(actor, slot, meta) for ometa in meta_expand_iter(meta): _logger.debug(f'{actor}.{slot}: will check meta {ometa}') for ometa in meta_expand_iter(meta): _logger.debug(f"{actor}.{slot}: check input slot meta {ometa}") odata = sdata.get_output_slot(ometa) oslot = None for oslot in df.find_output_slot(odata.get_actor_path(), odata.get_slot_name(), ometa): break islot = None for islot in df.find_input_slot(actor, slot, ometa): break assert (islot) if not oslot: raise ValueError(f'{actor}.{slot} output refers to ' 'invalid or missing output slot.') _logger.debug(f"{islot}: connect to {oslot}") islot.connect_to(oslot) return df
def max_ranks_per_node(self, rank_spec): typecheck('rank_spec', rank_spec, crow.sysenv.jobs.JobRankSpec, print_contents=True) can_hyper = self.hyperthreading_allowed max_per_node = self.cores_per_node if can_hyper and rank_spec.get('hyperthreading', False): max_per_node *= self.cpus_per_core threads_per_node = max_per_node omp_threads = max(1, rank_spec.get('OMP_NUM_THREADS', 1)) if omp_threads != MAXIMUM_THREADS: max_per_node //= omp_threads max_ppn = rank_spec.get('max_ppn', 0) if max_ppn: max_per_node = min(max_ppn, max_per_node) if max_per_node < 1: raise MachineTooSmallError( f'Specification too large for node: max {threads_per_node} for {rank_spec!r}' ) return max_per_node
def __init__(self, event): typecheck('event', event, EventView) self.event = event
def command_without_exe(parallelism, jobspec, exe): typecheck('jobspec', jobspec, crow.sysenv.JobResourceSpec) shell_command_obj = parallelism.make_ShellCommand(jobspec) cmd = list(shell_command_obj.command) return ' '.join([s for s in cmd if s != exe])
def to_ecflow(suite): typecheck('suite',suite,Suite) return ToEcflow(suite).to_ecflow()
def to_ecflow(suite,apply_overrides=True): typecheck('suite',suite,Suite) return ToEcflow(suite,apply_overrides=apply_overrides).to_ecflow()
def __init__(self, *args): if not args: raise ValueError('Tried to create an empty OrDependency') self.depends = list(args) for dep in self.depends: typecheck('A dependency', dep, LogicalDependency)
def add_ecf_file(self,ecf_file_set_name,path_string,ecf_file_contents): typecheck('ecf_file_set_name',ecf_file_set_name,str) typecheck('path_string',path_string,str) typecheck('ecf_file_contents',ecf_file_contents,str) self.ecf_files[ecf_file_set_name][path_string]=ecf_file_contents assert(self.have_ecf_file(ecf_file_set_name,path_string))
def _make_suite_def(self,cycle): self._select_cycle(cycle) clock=self.suite.Clock suite_name_format=self.suite.ecFlow.suite_name suite_name=cycle.strftime(suite_name_format) undated=OrderedDict() sio=StringIO() if 'before_suite_def' in self.suite: sio.write(self.suite.before_suite_def) sio.write('\n') sio.write(f'suite {suite_name}\n') if 'ecflow_def' in self.suite: for line in self.suite.ecflow_def.splitlines(): sio.write(f'{self.indent}{line.rstrip()}\n') ECF_FILES=self.suite.ecf_file_set.ECF_FILES sio.write(f"{self.indent}edit ECF_FILES '{ECF_FILES}'\n") def exit_fun(node): if node.is_family(): indent=max(0,len(node.path)-1)*self.indent ended=f'/{suite_name}/{node.view.task_path_str}' ended=re.sub('/+','/',ended) sio.write(f'{indent}endfamily # {ended}\n') for node in self._walk_job_graph(cycle,skip_fun=skip_fun,exit_fun=exit_fun): indent0=max(0,len(node.path)-1)*self.indent indent1=max(0,len(node.path))*self.indent nodetype='task' if node.is_task() else 'family' sio.write(f'{indent0}{nodetype} {node.path[-1]}') if node.is_family(): started=f' # /{suite_name}/{node.view.task_path_str}' started=re.sub('/+','/',started) sio.write(started) if 'ecf_file_set' in node.view: ECF_FILES=node.view.ecf_file_set.ECF_FILES sio.write(f"\n{self.indent}edit ECF_FILES '{ECF_FILES}'") sio.write('\n') if 'ecflow_def' in node.view: for line in node.view.ecflow_def.splitlines(): sio.write(f'{indent1}{line.rstrip()}\n') if 'Dummy' in node.view and node.view.Dummy: sio.write(f"{indent1}edit ECF_DUMMY_TASK ''\n") sio.write(f"{indent1}defstatus complete\n") if node.trigger not in [FALSE_DEPENDENCY,TRUE_DEPENDENCY]: sio.write(f'{indent1}trigger ') dep_to_ecflow(sio,node,node.trigger,clock,suite_name_format,undated) sio.write('\n') if node.complete not in [FALSE_DEPENDENCY,TRUE_DEPENDENCY]: sio.write(f'{indent1}complete ') dep_to_ecflow(sio,node,node.complete,clock,suite_name_format,undated) sio.write('\n') if node.time>ZERO_DT: when=cycle+node.time ectime=when.strftime('%H:%M') sio.write(f'{indent1}time {ectime}\n') if self.settings.dates_in_time_dependencies: ecdate=when.strftime('%d.%m.%Y') sio.write(f'{indent1}date {ecdate}\n') event_number=node.view.get('ecflow_first_event_number',1) typecheck(f'{node.view.task_path_var}.ecflow_first_event_number',event_number,int) if node.is_task(): for item in node.view.child_iter(): if item.is_event(): sio.write(f'{indent1}event {event_number} ' f'{item.path[-1]}\n') event_number+=1 sio.write(f'endsuite # /{suite_name}\n') suite_def_without_externs=sio.getvalue() sio.close() sio=StringIO() if undated: for d in undated.keys(): sio.write(f'extern {d}\n') sio.write(suite_def_without_externs) suite_def=sio.getvalue() sio.close() else: suite_def=suite_def_without_externs return suite_name, suite_def
def __init__(self, depend): typecheck('A dependency', depend, LogicalDependency) self.depend = depend
def __init__(self, view): typecheck('view', view, TaskableView, 'Task or Tamily') self.view = view
def to_rocoto(suite, apply_overrides=True): typecheck('suite', suite, Suite) return ToRocoto(suite, apply_overrides=apply_overrides)._expand_workflow_xml()