44import subprocess
55import threading
66import copy
7+ import time , datetime
78
89__DIFFLIB__ = False
910try :
@@ -163,7 +164,7 @@ def __init__(self, hostname=None, pwd=None, extra_options="", workdir = "",
163164 self .workdir = r""
164165 self .submit_command = "sbatch --wait"
165166 self .submit_name = "SBATCH"
166- self .terminal = "#! /bin/bash"
167+ self .terminal = "/bin/bash"
167168 self .v_nodes = "-N "
168169 self .use_nodes = True
169170 self .v_cpu = "-n "
@@ -240,6 +241,7 @@ def __init__(self, hostname=None, pwd=None, extra_options="", workdir = "",
240241 self .additional_script_parameters = None
241242
242243
244+
243245 # Allow to setup additional custom extra parameters
244246 self .custom_params = {}
245247
@@ -396,7 +398,201 @@ def CheckCommunication(self):
396398 return False
397399
398400 return True
399-
401+
402+ def create_submission_script (self , labels ):
403+ """
404+ CREATE THE SUBMISSION SCRIPT
405+ ===========================================
406+
407+ This is a function that is general and does not depend on the specific
408+ calculator. It is usefull to create the header of the submission script.
409+
410+ Parameters
411+ ----------
412+ labels : list
413+ It is a list of the labels of the calculations to be done.
414+
415+ Returns
416+ -------
417+ submission_header : string
418+ The text of the submission header.
419+ """
420+
421+ # prepare the submission script
422+ submission = "#!" + self .terminal + "\n "
423+
424+
425+
426+ # Add the submission options
427+ if self .use_nodes :
428+ submission += "#%s %s%d\n " % (self .submit_name , self .v_nodes , self .n_nodes )
429+ if self .use_cpu :
430+ submission += "#%s %s%d\n " % (self .submit_name , self .v_cpu , self .n_cpu )
431+ if self .use_time :
432+ submission += "#%s %s%s\n " % (self .submit_name , self .v_time , self .time )
433+ if self .use_account :
434+ submission += "#%s %s%s\n " % (self .submit_name , self .v_account , self .account_name )
435+ if self .use_memory :
436+ submission += "#%s %s%s\n " % (self .submit_name , self .v_memory , self .ram )
437+ if self .use_partition :
438+ submission += "#%s %s%s\n " % (self .submit_name , self .v_partition , self .partition_name )
439+
440+ # Append the additional parameters
441+ for add_parameter in self .custom_params :
442+ if self .custom_params [add_parameter ] is None :
443+ submission += "#{} --{}\n " .format (self .submit_name , add_parameter )
444+ else :
445+ submission += "#{} --{}={}\n " .format (self .submit_name , add_parameter , self .custom_params [add_parameter ])
446+
447+
448+ # Add the set -x option
449+ if self .add_set_minus_x :
450+ submission += "set -x\n "
451+
452+ # Add the loading of the modules
453+ submission += self .load_modules + "\n "
454+
455+ # Go to the working directory
456+ submission += "cd " + self .workdir + "\n "
457+
458+ # If any, apply the extra text before and after the calculation
459+ other_input = ""
460+ other_output = ""
461+ if (self .additional_script_parameters is not None ):
462+ other_input , other_output = self .additional_script_parameters (labels )
463+
464+ submission += other_input
465+
466+ # Use the xargs trick
467+ #submission += "xargs -d " + r"'\n'" + " -L1 -P%d -a %s -- bash -c\n" % (n_togheder,
468+ for i , lbl in enumerate (labels ):
469+ submission += self .get_execution_command (lbl )
470+
471+
472+ submission += other_output
473+
474+ return submission
475+
476+ def get_execution_command (self , label ):
477+ """
478+ GET THE EXECUTION COMMAND
479+ =========================
480+
481+ Return the command used in the submission script to actually execute the calculation.
482+
483+ Parameters
484+ ----------
485+ label : string
486+ The label of the calculation
487+
488+ Returns
489+ -------
490+ commnad : string
491+ The command to be appended to the submission script
492+ """
493+
494+ # Get the MPI command replacing NPROC
495+ new_mpicmd = self .mpi_cmd .replace ("NPROC" , str (self .n_cpu ))
496+
497+ # Replace the NPOOL variable and the PREFIX in the binary
498+ binary = self .binary .replace ("NPOOL" , str (self .n_pool )).replace ("PREFIX" , label )
499+
500+
501+ tmt_str = ""
502+ if self .use_timeout :
503+ tmt_str = "timeout %d " % self .timeout
504+ return "%s%s %s\n " % (tmt_str , new_mpicmd , binary )
505+
506+ def prepare_input_file (self , structure , calc , label ):
507+ """
508+ PREPARE THE INPUT FILE
509+ ======================
510+
511+ This is specific for quantum espresso and must be inherit and replaced for
512+ other calculators.
513+
514+ This crates the input file and copy it in the working directory.
515+
516+ Parameters
517+ ----------
518+ structure : CellConstructor.Structure.Structure
519+ The atomic structure on which to run the calculation
520+ calc : the ASE or CellConstructor calculator.
521+ In this case, it works with quantum espresso
522+ label : string
523+ The unique name of this calculation
524+ """
525+
526+ # Prepare the input file
527+ atm = structure .get_ase_atoms ()
528+ atm .set_calculator (calc )
529+ ase .io .write ("%s/%s.pwi" % (self .local_workdir , label ),
530+ atm , ** calc .parameters )
531+
532+
533+
534+ # First of all clean eventually input/output file of this very same calculation
535+ cmd = self .sshcmd + " %s 'rm -f %s/%s%s %s/%s%s'" % (self .hostname ,
536+ self .workdir , label , ".pwi" ,
537+ self .workdir , label , ".pwo" )
538+ self .ExecuteCMD (cmd , False )
539+ # cp_res = os.system(cmd + " > /dev/null")
540+ # if cp_res != 0:
541+ # print "Error while executing:", cmd
542+ # print "Return code:", cp_res
543+ # sys.stderr.write(cmd + ": exit with code " + str(cp_res) + "\n")
544+ #
545+ # Copy the file into the cluster
546+ cmd = self .scpcmd + " %s/%s%s %s:%s/" % (self .local_workdir , label ,
547+ ".pwi" , self .hostname ,
548+ self .workdir )
549+ cp_res = self .ExecuteCMD (cmd , False )
550+ if not cp_res :
551+ print ("Error while executing:" , cmd )
552+ print ("Return code:" , cp_res )
553+ sys .stderr .write (cmd + ": exit with code " + str (cp_res ) + "\n " )
554+ return cp_res
555+ #cp_res = os.system(cmd + " > /dev/null")
556+
557+
558+ def submit (self , script_location ):
559+ """
560+ SUBMIT THE CALCULATION
561+ ======================
562+
563+ Submit the calculation. Compose the command into a cmd variable, then submit it through:
564+
565+ .. code ::
566+
567+ return self.ExecuteCMD(cmd, True, return_output=True)
568+
569+
570+
571+ Parameters
572+ ----------
573+ script_localtion : string
574+ Path to the submission script inside the cluster.
575+
576+ Results
577+ -------
578+ success : bool
579+ Result of the execution of the submission command.
580+ It is what returned from self.ExecuteCMD(cmd, False)
581+ """
582+
583+ cmd = "{ssh} {host} '{submit_cmd} {script}'"
584+ if self .use_active_shell :
585+ cmd = "{ssh} {host} -t '{shell} --login -c \" {submit_cmd} {script}\" '" .format (shell = self .terminal )
586+
587+
588+ cmd = cmd .format (ssh = self .sshcmd , host = self .hostname ,
589+ submit_cmd = self .submit_name , script = script_location )
590+
591+ #cmd = self.sshcmd + " %s '%s %s/%s.sh'" % (self.hostname, self.submit_command,
592+ # self.workdir, label+ "_" + str(indices[0]))
593+
594+ return self .ExecuteCMD (cmd , True , return_output = True )
595+
400596 def batch_submission (self , list_of_structures , calc , indices ,
401597 in_extension , out_extension ,
402598 label = "ESP" , n_togheder = 1 ):
@@ -446,7 +642,6 @@ def batch_submission(self, list_of_structures, calc, indices,
446642 # Prepare the input atoms
447643 app_list = ""
448644 new_ncpu = self .n_cpu * n_togheder
449- new_mpicmd = self .mpi_cmd .replace ("NPROC" , str (self .n_cpu ))
450645 results = [None ] * N_structs
451646 submitted = []
452647 submission_labels = []
@@ -455,44 +650,10 @@ def batch_submission(self, list_of_structures, calc, indices,
455650 lbl = label + "_" + str (indices [i ])
456651 submission_labels .append (lbl )
457652
458- atm = list_of_structures [i ].get_ase_atoms ()
459- atm .set_calculator (calc )
460- ase .io .write ("%s/%s%s" % (self .local_workdir , lbl , in_extension ),
461- atm ,** calc .parameters )
462-
463-
464- # Add the file in the applist
465- binary = self .binary .replace ("NPOOL" , str (self .n_pool )).replace ("PREFIX" , lbl )
466-
467-
468- # First of all clean eventually input/output file of this very same calculation
469- cmd = self .sshcmd + " %s 'rm -f %s/%s%s %s/%s%s'" % (self .hostname ,
470- self .workdir , lbl , in_extension ,
471- self .workdir , lbl , out_extension )
472- self .ExecuteCMD (cmd , False )
473- # cp_res = os.system(cmd + " > /dev/null")
474- # if cp_res != 0:
475- # print "Error while executing:", cmd
476- # print "Return code:", cp_res
477- # sys.stderr.write(cmd + ": exit with code " + str(cp_res) + "\n")
478- #
479- # Copy the file into the cluster
480- cmd = self .scpcmd + " %s/%s%s %s:%s/" % (self .local_workdir , lbl ,
481- in_extension , self .hostname ,
482- self .workdir )
483- cp_res = self .ExecuteCMD (cmd , False )
484-
485- #cp_res = os.system(cmd + " > /dev/null")
486- if not cp_res :
487- print ("Error while executing:" , cmd )
488- print ("Return code:" , cp_res )
489- sys .stderr .write (cmd + ": exit with code " + str (cp_res ) + "\n " )
653+ # Create the input file and copy it into the cluster
654+ if not self .prepare_input_file (list_of_structures [i ], calc , lbl ):
490655 continue
491-
492- tmt_str = ""
493- if self .use_timeout :
494- tmt_str = "timeout %d " % self .timeout
495- app_list += "%s%s %s\n " % (tmt_str , new_mpicmd , binary )
656+
496657 submitted .append (i )
497658
498659 # Save the app list and copy it to the destination
@@ -512,59 +673,8 @@ def batch_submission(self, list_of_structures, calc, indices,
512673# sys.stderr.write(cmd + ": exit with code " + str(cp_res) + "\n")
513674# return results #[None] * N_structs
514675
515-
516- # prepare the submission script
517- submission = self .terminal + "\n "
518-
519- # Add the submission options
520- if self .use_nodes :
521- submission += "#%s %s%d\n " % (self .submit_name , self .v_nodes , self .n_nodes )
522- if self .use_cpu :
523- submission += "#%s %s%d\n " % (self .submit_name , self .v_cpu , new_ncpu )
524- if self .use_time :
525- submission += "#%s %s%s\n " % (self .submit_name , self .v_time , self .time )
526- if self .use_account :
527- submission += "#%s %s%s\n " % (self .submit_name , self .v_account , self .account_name )
528- if self .use_memory :
529- submission += "#%s %s%s\n " % (self .submit_name , self .v_memory , self .ram )
530- if self .use_partition :
531- submission += "#%s %s%s\n " % (self .submit_name , self .v_partition , self .partition_name )
532-
533- # Append the additional parameters
534- for add_parameter in self .custom_params :
535- adder_string = "--{}" .format (add_parameter )
536- if add_parameter .startswith ("-" ):
537- adder_string = add_parameter
538-
539- if self .custom_params [add_parameter ] is None :
540- submission += "#{} {}\n " .format (self .submit_name , adder_string )
541- else :
542- submission += "#{} {}={}\n " .format (self .submit_name , adder_string , self .custom_params [add_parameter ])
543-
544-
545- # Add the set -x option
546- if self .add_set_minus_x :
547- submission += "set -x\n "
548-
549- # Add the loading of the modules
550- submission += self .load_modules + "\n "
551-
552- # Go to the working directory
553- submission += "cd " + self .workdir + "\n "
554-
555- # If any, apply the extra text before and after the calculation
556- other_input = ""
557- other_output = ""
558- if self .additional_script_parameters is not None :
559- other_input , other_output = self .additional_script_parameters (submission_labels )
560676
561- submission += other_input
562-
563- # Use the xargs trick
564- #submission += "xargs -d " + r"'\n'" + " -L1 -P%d -a %s -- bash -c\n" % (n_togheder,
565- submission += app_list
566-
567- submission += other_output
677+ submission = self .create_submission_script (submission_labels )
568678
569679 # Copy the submission script
570680 sub_fpath = "%s/%s.sh" % (self .local_workdir , label + "_" + str (indices [0 ]))
@@ -582,11 +692,10 @@ def batch_submission(self, list_of_structures, calc, indices,
582692
583693
584694 # Run the simulation
585- cmd = self .sshcmd + " %s '%s %s/%s.sh'" % (self .hostname , self .submit_command ,
586- self .workdir , label + "_" + str (indices [0 ]))
587- status , submission_output = self .ExecuteCMD (cmd , True , return_output = True )
588695
589- # If the command for submission is non blocking, we need to check periodically wether the calculation has been completed
696+ sub_script_loc = os .path .join (self .workdir , label + "_" + str (indices [0 ]) + ".sh" )
697+ cp_res , submission_output = self .submit (sub_script_loc )
698+
590699 if self .nonblocking_command :
591700 job_id = self .get_job_id_from_submission_output (submission_output )
592701
@@ -598,12 +707,6 @@ def batch_submission(self, list_of_structures, calc, indices,
598707 while not self .check_job_finished (job_id ):
599708 time .sleep (self .check_timeout )
600709
601- # cp_res = os.system(cmd + " > /dev/null")
602- # if cp_res != 0:
603- # print "Error while executing:", cmd
604- # print "Return code:", cp_res
605- # sys.stderr.write(cmd + ": exit with code " + str(cp_res))
606- #
607710
608711 # Collect the output back
609712 for i in submitted :
0 commit comments