diff --git a/autosubmit/job/job_list.py b/autosubmit/job/job_list.py index ab897ae27aa2d5393f7aa1f581abfa3c4e292c10..ea3a9fa959d83f08e078c2a0c0fc4980d0108fab 100644 --- a/autosubmit/job/job_list.py +++ b/autosubmit/job/job_list.py @@ -846,15 +846,19 @@ class JobList(object): all_jobs = [job.name for job in self._job_list] return all_jobs + def update_two_step_jobs(self): prev_jobs_to_run_first = self.jobs_to_run_first if len(self.jobs_to_run_first) > 0: self.jobs_to_run_first = [ job for job in self.jobs_to_run_first if job.status != Status.COMPLETED ] - #if len(self.jobs_to_run_first) > 0: - #waiting_jobs = [ job for job in self.jobs_to_run_first if job.status != Status.WAITING ] - #if len(waiting_jobs) == len(self.jobs_to_run_first): - #self.jobs_to_run_first = [] - #Log.warning("No more jobs to run first, there were still pending jobs but they're unable to run without their parents.") + keep_running = False + for job in self.jobs_to_run_first: + running_parents = [parent for parent in job.parents if parent.status != Status.WAITING and parent.status != Status.FAILED ] #job is parent of itself + if len(running_parents) == len(job.parents): + keep_running = True + if len(self.jobs_to_run_first) > 0 and keep_running is False: + raise AutosubmitCritical("No more jobs to run first, there were still pending jobs but they're unable to run without their parents or there are failed jobs.",7014) + def parse_two_step_start(self, unparsed_jobs): jobs_to_run_first = list() job_names = "" @@ -862,8 +866,8 @@ class JobList(object): jobs_to_check = unparsed_jobs.split("&") job_names = jobs_to_check[0] unparsed_jobs = jobs_to_check[1] - if "," in unparsed_jobs: - semiparsed_jobs = unparsed_jobs.split(",") + if ";" in unparsed_jobs: + semiparsed_jobs = unparsed_jobs.split(";") if 2 <= len(semiparsed_jobs) <= 4: section_job = semiparsed_jobs[0] date = semiparsed_jobs[1] @@ -894,6 +898,7 @@ class JobList(object): :return: jobs_list :rtype: list """ + jobs_by_name = [ job for job in self._job_list if re.search("(^|[^0-9a-z_])"+job.name.lower()+"([^a-z0-9_]|$)",job_names.lower()) is not None ] jobs = [ job for job in self._job_list if re.search("(^|[^0-9a-z_])"+job.section.lower()+"([^a-z0-9_]|$)",section_list.lower()) is not None ] if date_list != "": @@ -903,21 +908,65 @@ class JobList(object): jobs_final = [] jobs_final_2 = [] - + number_start_at = 0 + for char in self._member_list[0]: + if char.isdigit(): + break + number_start_at += 1 if member_or_chunk_list != "": + final_member_or_chunk_list = "" + number_range = re.findall("\[[0-9]+-[0-9]+\]", member_or_chunk_list.lower()) + if len(number_range) > 0: + for numbers_found in number_range: + number_range = numbers_found.split('-') + lower_bound = int(number_range[0][1:]) + upper_bound = int(number_range[1][:-1]) + for seq in xrange(lower_bound, upper_bound): + final_member_or_chunk_list += str(seq) + "," + final_member_or_chunk_list += str(upper_bound) + "," + numbers_separated = re.findall("\[[0-9]*[^-a-z]+\]", member_or_chunk_list.lower()) + if len(numbers_separated) > 0: + for numbers in numbers_separated: + if ',' in numbers: + numbers = numbers.split(',') + else: + numbers = numbers.split() + for number in numbers: + final_member_or_chunk_list += str(number).strip("[]") + "," + final_member_or_chunk_list = final_member_or_chunk_list if 'c' in member_or_chunk_list[0]: - jobs_final = [job for job in jobs_date if re.search("(^|[^0-9a-z_])" + str(job.chunk) + "([^a-z0-9_]|$)",member_or_chunk_list.lower()) is not None or job.running == "once"] + jobs_final = [job for job in jobs_date if re.search("(^|[^0-9a-z_])" + str(job.chunk) + "([^a-z0-9_]|$)",final_member_or_chunk_list) is not None or job.running == "once"] elif 'm' in member_or_chunk_list[0]: - jobs_final = [job for job in jobs_date if re.search("(^|[^0-9a-z_])" + str(job.member).lower() + "([^a-z0-9_]|$)", member_or_chunk_list.lower()) is not None or job.running == "once"] + jobs_final = [job for job in jobs_date if re.search("(^|[^0-9a-z_])" + str(job.member)[number_start_at:].lower() + "([^a-z0-9_]|$)", final_member_or_chunk_list) is not None or job.running == "once"] else: jobs_final = [] else: jobs_final = jobs_date if chunk_or_member_list != "": + final_member_or_chunk_list = "" + number_range = re.findall("\[[0-9]+-[0-9]+\]", member_or_chunk_list.lower()) + if len(number_range) > 0: + for numbers_found in number_range: + number_range = numbers_found.split('-') + lower_bound = int(number_range[0][1:]) + upper_bound = int(number_range[1][:-1]) + for seq in xrange(lower_bound, upper_bound): + final_member_or_chunk_list += str(seq) + "," + final_member_or_chunk_list += str(upper_bound) + "," + numbers_separated = re.findall("\[[0-9]+[^-a-z]+\]", member_or_chunk_list.lower()) + if len(numbers_separated) > 0: + for numbers in numbers_separated: + if ',' in numbers: + numbers = numbers.split(',') + else: + numbers = numbers.split() + for number in numbers: + final_member_or_chunk_list += str(number).strip("[]") + "," + final_member_or_chunk_list = final_member_or_chunk_list if 'c' in chunk_or_member_list[0]: - jobs_final_2 = [job for job in jobs_date if re.search("(^|[^0-9a-z_])" + str(job.chunk) + "([^a-z0-9_]|$)",chunk_or_member_list.lower()) is not None or job.running == "once"] + jobs_final_2 = [job for job in jobs_date if re.search("(^|[^0-9a-z_])" + str(job.chunk) + "([^a-z0-9_]|$)",final_member_or_chunk_list) is not None or job.running == "once"] elif 'm' in chunk_or_member_list[0]: - jobs_final_2 = [job for job in jobs_date if re.search("(^|[^0-9a-z_])" + str(job.member).lower() + "([^a-z0-9_]|$)", chunk_or_member_list.lower()) is not None or job.running == "once"] + jobs_final_2 = [job for job in jobs_date if re.search("(^|[^0-9a-z_])" + str(job.member)[number_start_at:].lower() + "([^a-z0-9_]|$)", final_member_or_chunk_list) is not None or job.running == "once"] else: jobs_final_2 = [] ultimate_jobs_list = list(set(jobs_final+jobs_by_name+jobs_final_2)) #Duplicates out diff --git a/docs/source/usage.rst b/docs/source/usage.rst index ea29da196870cdb03bd95e9a82bc9170e0c370a9..fbb04950a1338086c9d4a3ced5d11527b12c7ce9 100644 --- a/docs/source/usage.rst +++ b/docs/source/usage.rst @@ -59,4 +59,5 @@ Command list usage/configure usage/wrappers usage/remote_dependencies - usage/report \ No newline at end of file + usage/report + usage/run_two_step \ No newline at end of file diff --git a/docs/source/usage/run_two_step.rst b/docs/source/usage/run_two_step.rst new file mode 100644 index 0000000000000000000000000000000000000000..4714f165b88e4cf33f3c779c82237c98659672e5 --- /dev/null +++ b/docs/source/usage/run_two_step.rst @@ -0,0 +1,69 @@ +############################################################################################## +How to prepare an experiment to run in two independent job_list. (Priority jobs, Two-step-run) +############################################################################################## + +Feature overview +---------------- + +This feature allows to run an experiment in two separated steps without the need of do anything manually. + +To achieve this, you will have to use an special parameter called TWO_STEP_START in which you will put the list of the jobs that you want to run in an exclusive mode. These jobs will run until all of them finishes and once it finishes, the rest of the jobs will begun the execution. + +It can be activated through TWO_STEP_START and it is set on expdef_a02n.conf, under the [experiment] section. + +.. code-block:: ini + + [experiment] + DATELIST = 20120101 20120201 + MEMBERS = fc00[0-3] + CHUNKSIZEUNIT = day + CHUNKSIZE = 1 + NUMCHUNKS = 10 + CHUNKINI = + CALENDAR = standard + # To run before the rest of experiment: + TWO_STEP_START = + +In order to be easier to use, there are Three modes for use this feature: job_names and section,dates,member_or_chunk(M/C),chunk_or_member(C/M). + +* By using job_names alone, you will need to put all jobs names one by one divided by the char , . +* By using section,dates,member_or_chunk(M/C),chunk_or_member(C/M). You will be able to select multiple jobs at once combining these filters. +* Use both options, job_names and section,dates,member_or_chunk(M/C),chunk_or_member(C/M). You will have to put & between the two modes. + +There are 5 fields on TWO_STEP_START, all of them are optional but there are certain limitations: + +* **Job_name**: [Independent] List of job names, separated by ',' char. Optional, doesn't depend on any field. Separated from the rest of fields by '&' must be the first field if specified +* **Section**: [Independent] List of sections, separated by ',' char. Optional, can be used alone. Separated from the rest of fields by ';' +* **Dates**: [Depends on section] List of dates, separated by ',' char. Optional, but depends on Section field. Separated from the rest of fields by ';' +* **member_or_chunk**: [Depends on Dates(OR)] List of chunk or member, must start with C or M to indicate the filter type. Jobs are selected by [1,2,3..] or by a range [0-9] Optional, but depends on Dates field. Separated from the rest of fields by ';' +* **chunk_or_member**: [Depends on Dates(OR)] List of member or chunk, must start with M or C to indicate the filter type. Jobs are selected by [1,2,3..] or by a range [0-9] Optional, but depends on Dates field. Separated from the rest of fields by ';' + +Example +------- + +Guess the expdef configuration as follow: + +.. code-block:: ini + + [experiment] + DATELIST = 20120101 + MEMBERS = 00[0-1] + CHUNKSIZEUNIT = day + CHUNKSIZE = 1 + NUMCHUNKS = 2 + TWO_STEP_START = a02n_20120101_000_1_REDUCE&COMPILE_DA,SIM;20120101;c[1] + +Given this job_list ( jobs_conf has REMOTE_COMPILE(once),DA,SIM,REDUCE) + +['a02n_REMOTE_COMPILE', 'a02n_20120101_000_1_SIM', 'a02n_20120101_000_2_SIM', 'a02n_20120101_001_1_SIM', 'a02n_20120101_001_2_SIM', 'a02n_COMPILE_DA', 'a02n_20120101_1_DA', 'a02n_20120101_2_DA', 'a02n_20120101_000_1_REDUCE', 'a02n_20120101_000_2_REDUCE', 'a02n_20120101_001_1_REDUCE', 'a02n_20120101_001_2_REDUCE'] + +The priority jobs will be ( check TWO_STEP_START from expdef conf): + +['a02n_20120101_000_1_SIM', 'a02n_20120101_001_1_SIM', 'a02n_COMPILE_DA', 'a02n_20120101_000_1_REDUCE'] + + + +Finally, you can launch Autosubmit *run* in background and with ``nohup`` (continue running although the user who launched the process logs out). +:: + + nohup autosubmit run cxxx &