Statistiques
| Branche: | Tag: | Révision :

dockonsurf / modules / calculation.py @ d8d92cfb

Historique | Voir | Annoter | Télécharger (9,13 ko)

1 3d6a9d3c Carles
import os
2 3d6a9d3c Carles
import logging
3 3d6a9d3c Carles
4 3d6a9d3c Carles
logger = logging.getLogger('DockOnSurf')
5 3d6a9d3c Carles
6 3d6a9d3c Carles
7 f85667b9 Carles Marti
def check_finished_calcs(run_type, code):
8 f85667b9 Carles Marti
    """Returns two lists of calculations finished normally and abnormally.
9 f85667b9 Carles Marti

10 f85667b9 Carles Marti
    @param run_type: The type of calculation to check.
11 f85667b9 Carles Marti
    @param code: The code used for the specified job.
12 f85667b9 Carles Marti
    @return finished_calcs: List of calculations that have finished normally.
13 f85667b9 Carles Marti
    @return unfinished_calcs: List of calculations that have finished abnormally
14 f85667b9 Carles Marti
    """
15 f85667b9 Carles Marti
    from glob import glob
16 f85667b9 Carles Marti
    from modules.utilities import tail
17 f85667b9 Carles Marti
18 f85667b9 Carles Marti
    finished_calcs = []
19 f85667b9 Carles Marti
    unfinished_calcs = []
20 f85667b9 Carles Marti
    for conf in os.listdir(run_type):
21 f85667b9 Carles Marti
        if not os.path.isdir(f'{run_type}/{conf}') or 'conf_' not in conf:
22 f85667b9 Carles Marti
            continue
23 f85667b9 Carles Marti
        if code == 'cp2k':
24 f85667b9 Carles Marti
            out_file_list = glob(f"{run_type}/{conf}/*.out")
25 f85667b9 Carles Marti
            restart_file_list = glob(f"{run_type}/{conf}/*-1.restart")
26 f85667b9 Carles Marti
            if len(out_file_list) == 0 or len(restart_file_list) == 0:
27 f85667b9 Carles Marti
                unfinished_calcs.append(conf)  # TODO specify separetely out and
28 b6b1b03e Carles Marti
                # TODO restart
29 f85667b9 Carles Marti
            elif len(out_file_list) > 1 or len(restart_file_list) > 1:
30 f85667b9 Carles Marti
                warn_msg = f'There is more than one file matching the {code} ' \
31 f85667b9 Carles Marti
                           f'pattern for finished calculation (*.out / ' \
32 f85667b9 Carles Marti
                           f'*-1.restart) in {run_type}/{conf}: ' \
33 f85667b9 Carles Marti
                           f'{out_file_list, restart_file_list}. ' \
34 f85667b9 Carles Marti
                           f'Skipping directory.'
35 f85667b9 Carles Marti
                logger.warning(warn_msg)
36 f85667b9 Carles Marti
                unfinished_calcs.append(conf)
37 f85667b9 Carles Marti
            else:
38 f85667b9 Carles Marti
                with open(out_file_list[0], 'rb') as out_fh:
39 f85667b9 Carles Marti
                    if "PROGRAM STOPPED IN" not in tail(out_fh):
40 f85667b9 Carles Marti
                        unfinished_calcs.append(conf)
41 f85667b9 Carles Marti
                    else:
42 f85667b9 Carles Marti
                        finished_calcs.append(conf)
43 f85667b9 Carles Marti
    return finished_calcs, unfinished_calcs
44 f85667b9 Carles Marti
45 f85667b9 Carles Marti
46 f8c4eafe Carles
def prep_cp2k(inp_file, run_type, atms_list):  # TODO name to PROJECT_NAME
47 f3004731 Carles
    """Prepares the directories to run isolated calculation with CP2K.
48 3d6a9d3c Carles

49 f3004731 Carles
    @param inp_file: CP2K Input file to run the calculations with.
50 f3004731 Carles
    @param run_type: Type of calculation. 'isolated', 'screening' or
51 f3004731 Carles
        'refinement'
52 f3004731 Carles
    @param atms_list: list of ase.Atoms objects to run the calculation of.
53 f3004731 Carles
    @return: None
54 f3004731 Carles
    """
55 f3004731 Carles
    from shutil import copy
56 f3004731 Carles
    import ase.io
57 f3004731 Carles
    from pycp2k import CP2K
58 af3e2441 Carles Marti
    from modules.utilities import check_bak
59 f3004731 Carles
    cp2k = CP2K()
60 f3004731 Carles
    cp2k.parse(inp_file)
61 f3004731 Carles
    force_eval = cp2k.CP2K_INPUT.FORCE_EVAL_list[0]
62 b05058e1 Carles
    if force_eval.SUBSYS.TOPOLOGY.Coord_file_name is None:
63 2dfa562f Carles Martí
        logger.warning("'COORD_FILE_NAME' not specified on CP2K input. Using\n"
64 2dfa562f Carles Martí
                       "default name 'coord.xyz'. A new CP2K input file with "
65 2dfa562f Carles Martí
                       "the 'COORD_FILE_NAME' variable is created. If there\n"
66 695dcff8 Carles Marti
                       "is a name conflict the old file will be backed up.")
67 b05058e1 Carles
        force_eval.SUBSYS.TOPOLOGY.Coord_file_name = 'coord.xyz'
68 2dfa562f Carles Martí
        print(inp_file.split('/')[-1])
69 2dfa562f Carles Martí
        check_bak(inp_file.split('/')[-1])
70 2dfa562f Carles Martí
        cp2k.write_input_file(inp_file.split('/')[-1])
71 b05058e1 Carles
72 f3004731 Carles
    coord_file = force_eval.SUBSYS.TOPOLOGY.Coord_file_name
73 f3004731 Carles
74 99afde40 Carles
    # Creating and setting up directories for every configuration.
75 f3004731 Carles
    for i, conf in enumerate(atms_list):
76 f3004731 Carles
        os.mkdir(f'{run_type}/conf_{i}')
77 f3004731 Carles
        copy(inp_file, f'{run_type}/conf_{i}/')
78 f3004731 Carles
        ase.io.write(f'{run_type}/conf_{i}/{coord_file}', conf)
79 f3004731 Carles
80 f3004731 Carles
81 2be92b2c Carles Marti
def get_jobs_status(job_ids, stat_cmd, stat_dict):
82 670284be Carles
    """Returns a list of job status for a list of job ids.
83 99afde40 Carles

84 670284be Carles
    @param job_ids: list of all jobs to be checked their status.
85 2be92b2c Carles Marti
    @param stat_cmd: Command to check job status.
86 2be92b2c Carles Marti
    @param stat_dict: Dictionary with pairs of job status (r, p, f) and the
87 2be92b2c Carles Marti
        pattern it matches in the output of the stat_cmd.
88 670284be Carles
    @return: list of status for every job.
89 99afde40 Carles
    """
90 2be92b2c Carles Marti
    from subprocess import PIPE, Popen
91 670284be Carles
    status_list = []
92 670284be Carles
    for job in job_ids:
93 2be92b2c Carles Marti
        stat_order = stat_cmd % job
94 2be92b2c Carles Marti
        stat_msg = Popen(stat_order, shell=True,
95 2be92b2c Carles Marti
                         stdout=PIPE).communicate()[0].decode('utf-8').strip()
96 2be92b2c Carles Marti
        if stat_dict['r'] == stat_msg:
97 670284be Carles
            status_list.append('r')
98 2be92b2c Carles Marti
        elif stat_dict['p'] == stat_msg:
99 2be92b2c Carles Marti
            status_list.append('p')
100 2be92b2c Carles Marti
        elif stat_dict['f'] == stat_msg:
101 b461f289 Carles Marti
            status_list.append('f')
102 b461f289 Carles Marti
        else:
103 2be92b2c Carles Marti
            logger.warning(f'Unrecognized job status: {job}')
104 b461f289 Carles Marti
    return status_list
105 b461f289 Carles Marti
106 b461f289 Carles Marti
107 09c3325a Carles Marti
def submit_jobs(run_type, sub_cmd, sub_script, stat_cmd, stat_dict, max_jobs,
108 b6b1b03e Carles Marti
                name):
109 2be92b2c Carles Marti
    """Submits jobs to a custom queuing system with the provided script
110 b461f289 Carles Marti

111 b461f289 Carles Marti
    @param run_type: Type of calculation. 'isolated', 'screening', 'refinement'
112 b6b1b03e Carles Marti
    @param sub_cmd: Bash command used to submit jobs.
113 b461f289 Carles Marti
    @param sub_script: script for the job submission.
114 b6b1b03e Carles Marti
    @param stat_cmd: Bash command to check job status.
115 b6b1b03e Carles Marti
    @param stat_dict: Dictionary with pairs of job status: r, p, f (ie. running
116 b6b1b03e Carles Marti
        pending and finished) and the pattern it matches in the output of the
117 b6b1b03e Carles Marti
        stat_cmd.
118 b6b1b03e Carles Marti
    @param max_jobs: dict: Contains the maximum number of jobs to be both
119 b6b1b03e Carles Marti
        running, pending/queued and pending+running. When the relevant maximum
120 b6b1b03e Carles Marti
        is reached no jobs more are submitted.
121 2be92b2c Carles Marti
    @param name: name of the project.
122 b461f289 Carles Marti
    """
123 b461f289 Carles Marti
    from shutil import copy
124 b461f289 Carles Marti
    from time import sleep
125 2be92b2c Carles Marti
    from subprocess import PIPE, Popen
126 b461f289 Carles Marti
    subm_jobs = []
127 b461f289 Carles Marti
    init_dir = os.getcwd()
128 b461f289 Carles Marti
    for conf in os.listdir(run_type):
129 b461f289 Carles Marti
        i = conf.split('_')[1]
130 b6b1b03e Carles Marti
        while get_jobs_status(subm_jobs, stat_cmd, stat_dict).count("r") + \
131 b6b1b03e Carles Marti
                get_jobs_status(subm_jobs, stat_cmd, stat_dict).count("p") \
132 b6b1b03e Carles Marti
                >= max_jobs['rp']\
133 b6b1b03e Carles Marti
                or get_jobs_status(subm_jobs, stat_cmd, stat_dict).count("r") \
134 b6b1b03e Carles Marti
                >= max_jobs['r'] \
135 b6b1b03e Carles Marti
                or get_jobs_status(subm_jobs, stat_cmd, stat_dict).count("p") \
136 b6b1b03e Carles Marti
                >= max_jobs['p']:
137 b461f289 Carles Marti
            sleep(30)
138 b461f289 Carles Marti
        copy(sub_script, f"{run_type}/{conf}")
139 b461f289 Carles Marti
        os.chdir(f"{run_type}/{conf}")
140 ca309a2c Carles Marti
        job_name = f'{name[:5]}{run_type[:3].capitalize()}{i}'
141 2be92b2c Carles Marti
        sub_order = sub_cmd % (job_name, sub_script)
142 2be92b2c Carles Marti
        subm_msg = Popen(sub_order, shell=True, stdout=PIPE).communicate()[0]
143 2be92b2c Carles Marti
        job_id = None
144 2be92b2c Carles Marti
        for word in subm_msg.decode("utf-8").split():
145 2be92b2c Carles Marti
            try:
146 2be92b2c Carles Marti
                job_id = int(word)
147 2be92b2c Carles Marti
                break
148 2be92b2c Carles Marti
            except ValueError:
149 2be92b2c Carles Marti
                continue
150 2be92b2c Carles Marti
        subm_jobs.append(job_id)
151 b461f289 Carles Marti
        os.chdir(init_dir)
152 b461f289 Carles Marti
153 b461f289 Carles Marti
    logger.info('All jobs have been submitted, waiting for them to finish.')
154 2be92b2c Carles Marti
    while not all([stat == 'f' for stat in
155 2be92b2c Carles Marti
                   get_jobs_status(subm_jobs, stat_cmd, stat_dict)]):
156 b461f289 Carles Marti
        sleep(30)
157 b461f289 Carles Marti
    logger.info('All jobs have finished.')
158 12001182 Carles
159 12001182 Carles
160 f3004731 Carles
def run_calc(run_type, inp_vars, atms_list):
161 3d6a9d3c Carles
    """Directs the calculation run according to the provided arguments.
162 3d6a9d3c Carles

163 f3004731 Carles
    @param run_type: Type of calculation. 'isolated', 'screening' or
164 f3004731 Carles
    'refinement'
165 3d6a9d3c Carles
    @param inp_vars: Calculation parameters from input file.
166 f3004731 Carles
    @param atms_list: List of ase.Atoms objects containing the sets of atoms
167 f3004731 Carles
    aimed to run the calculations of.
168 3d6a9d3c Carles
    """
169 821dca42 Carles Marti
    from modules.utilities import check_bak
170 3d6a9d3c Carles
    run_types = ['isolated', 'screening', 'refinement']
171 3d6a9d3c Carles
    if not isinstance(run_type, str) or run_type.lower() not in run_types:
172 1e36f905 Carles Marti
        run_type_err = f"'run_type' must be one of the following: {run_types}"
173 3d6a9d3c Carles
        logger.error(run_type_err)
174 3d6a9d3c Carles
        raise ValueError(run_type_err)
175 3d6a9d3c Carles
176 821dca42 Carles Marti
    if inp_vars['batch_q_sys']:
177 821dca42 Carles Marti
        logger.info(f"Running {run_type} calculation with {inp_vars['code']} on"
178 695dcff8 Carles Marti
                    f" {inp_vars['batch_q_sys']}.")
179 821dca42 Carles Marti
    else:
180 695dcff8 Carles Marti
        logger.info(f"Doing a dry run of {run_type}.")
181 c3cb279a Carles
    check_bak(run_type)
182 c3cb279a Carles
    os.mkdir(run_type)
183 1e36f905 Carles Marti
184 1e36f905 Carles Marti
    # Prepare directories and files for relevant code.
185 1b54d787 Carles Marti
    if inp_vars['code'] == 'cp2k':
186 821dca42 Carles Marti
        if run_type == 'isolated':
187 821dca42 Carles Marti
            prep_cp2k(inp_vars['isol_inp_file'], run_type, atms_list)
188 821dca42 Carles Marti
        elif run_type == 'screening':
189 821dca42 Carles Marti
            prep_cp2k(inp_vars['screen_inp_file'], run_type, atms_list)
190 86cf3a38 Carles Marti
        elif run_type == 'refinement':
191 86cf3a38 Carles Marti
            prep_cp2k(inp_vars['refine_inp_file'], run_type, atms_list)
192 a5f73b4c Carles
    # elif: inp_vars['code'] == 'Other codes here'
193 99afde40 Carles
194 1e36f905 Carles Marti
    # Submit/run Jobs
195 12001182 Carles
    if inp_vars['batch_q_sys'] == 'sge':
196 2be92b2c Carles Marti
        stat_cmd = "qstat | grep %s | awk '{print $5}'"
197 2be92b2c Carles Marti
        stat_dict = {'r': 'r', 'p': 'qw', 'f': ''}
198 2be92b2c Carles Marti
        submit_jobs(run_type, 'qsub -N %s %s', inp_vars['subm_script'],
199 09c3325a Carles Marti
                    stat_cmd, stat_dict, inp_vars['max_jobs'],
200 b6b1b03e Carles Marti
                    inp_vars['project_name'])
201 2be92b2c Carles Marti
    elif inp_vars['batch_q_sys'] == 'lsf':
202 1e36f905 Carles Marti
        stat_cmd = "bjobs -w | grep %s | awk '{print $5}'"  # TODO Adapt to
203 cf8fe0e3 Carles Marti
        stat_dict = {'r': 'r', 'p': 'qw', 'f': ''}  # TODO actual command
204 1e36f905 Carles Marti
        submit_jobs(run_type, 'bsub -J %s %s', inp_vars['subm_script'],
205 09c3325a Carles Marti
                    stat_cmd, stat_dict, inp_vars['max_jobs'],
206 b6b1b03e Carles Marti
                    inp_vars['project_name'])
207 ec5bba46 Carles Marti
    elif inp_vars['batch_q_sys'] == 'irene':
208 ec5bba46 Carles Marti
        stat_cmd = "ccc_mstat | grep %s | awk '{print $10}' | cut -c1"
209 ec5bba46 Carles Marti
        stat_dict = {'r': 'R', 'p': 'P', 'f': ''}
210 ec5bba46 Carles Marti
        submit_jobs(run_type, 'ccc_msub -r %s %s', inp_vars['subm_script'],
211 ec5bba46 Carles Marti
                    stat_cmd, stat_dict, inp_vars['max_jobs'],
212 b6b1b03e Carles Marti
                    inp_vars['project_name'])
213 ec5bba46 Carles Marti
214 1a1164e0 Carles Marti
    elif inp_vars['batch_q_sys'] == 'local':
215 1a1164e0 Carles Marti
        pass  # TODO implement local
216 1a1164e0 Carles Marti
    elif not inp_vars['batch_q_sys']:
217 0db30d07 Carles
        pass