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

dockonsurf / modules / dos_input.py @ 0db30d07

Historique | Voir | Annoter | Télécharger (23,92 ko)

1 b77be9ad Carles
"""Functions to deal with DockOnSurf input files.
2 b77be9ad Carles

3 b77be9ad Carles
Functions
4 0e83e6a6 Carles
try_command:Tries to run a command and logs its exceptions (expected and not).
5 0e83e6a6 Carles
str2lst: Converts a string of integers, and groups of them, to a list of lists.
6 0e83e6a6 Carles
check_expect_val: Checks whether the value of an option has an adequate value.
7 0e83e6a6 Carles
read_input: Sets up the calculation by reading the parameters from input file.
8 0e83e6a6 Carles
get_run_type: Gets 'run_type' value and checks that its value is acceptable.
9 0e83e6a6 Carles
get_code: Gets 'code' value and checks that its value is acceptable.
10 0e83e6a6 Carles
get_batch_q_sys: Gets 'batch_q_sys' value and checks that its value is
11 8887f41d Carles
acceptable.
12 0e83e6a6 Carles
get_relaunch_err: Gets 'relaunch_err' value and checks that its value is
13 8887f41d Carles
acceptable.
14 0e83e6a6 Carles
get_max_qw: Gets 'max_qw' value and checks that its value is acceptable.
15 0e83e6a6 Carles
get_special_atoms: Gets 'special_atoms' value and checks that its value is
16 8887f41d Carles
acceptable.
17 0e83e6a6 Carles
get_isol_inp_file: Gets 'isol_inp_file' value and checks that its value is
18 8887f41d Carles
acceptable.
19 0e83e6a6 Carles
get_cluster_magns: Gets 'cluster_magns' value and checks that its value is
20 8887f41d Carles
acceptable.
21 0e83e6a6 Carles
get_num_conformers: Gets 'num_conformers' value and checks that its value is
22 8887f41d Carles
acceptable.
23 0e83e6a6 Carles
get_num_prom_cand: Gets 'num_prom_cand' value and checks that its value is
24 8887f41d Carles
acceptable.
25 a5c74494 Carles
get_iso_rmsd: Gets 'iso_rmsd' value and checks that its value is acceptable.
26 b1f6e69d Carles
get_min_confs: Gets 'min_confs' value and checks that its value is acceptable.
27 0e83e6a6 Carles
get_screen_inp_file: Gets 'screen_inp_file' value and checks that its value is
28 8887f41d Carles
acceptable.
29 0e83e6a6 Carles
get_sites: Gets 'sites' value and checks that its value is acceptable.
30 0e83e6a6 Carles
get_molec_ads_ctrs: Gets 'molec_ads_ctrs' value and checks that its value is
31 8887f41d Carles
acceptable.
32 0e83e6a6 Carles
get_try_disso: Gets 'try_disso' value and checks that its value is acceptable.
33 0e83e6a6 Carles
get_pts_per_angle: Gets 'pts_per_angle' value and checks that its value is
34 8887f41d Carles
acceptable.
35 0e83e6a6 Carles
get_coll_thrsld: Gets 'coll_thrsld' value and checks that its value is
36 8887f41d Carles
acceptable.
37 0e83e6a6 Carles
get_screen_rmsd: Gets 'screen_rmsd' value and checks that its value is
38 8887f41d Carles
acceptable.
39 0e83e6a6 Carles
get_coll_bottom_z: Gets 'coll_bottom_z' value and checks that its value is
40 8887f41d Carles
acceptable.
41 0e83e6a6 Carles
get_refine_inp_file: Gets 'refine_inp_file' value and checks that its value is
42 8887f41d Carles
acceptable.
43 0e83e6a6 Carles
get_energy_cutoff: Gets 'energy_cutoff' value and checks that its value is
44 8887f41d Carles
acceptable.
45 17e72a49 Carles
"""
46 8887f41d Carles
import os.path
47 b5b2af64 Carles
import logging
48 8887f41d Carles
from configparser import ConfigParser, NoSectionError, NoOptionError, \
49 8887f41d Carles
    MissingSectionHeaderError, DuplicateOptionError
50 8887f41d Carles
51 8887f41d Carles
logger = logging.getLogger('DockOnSurf')
52 8887f41d Carles
53 8887f41d Carles
dos_inp = ConfigParser(inline_comment_prefixes='#',
54 8887f41d Carles
                       empty_lines_in_values=False)
55 8887f41d Carles
56 8887f41d Carles
new_answers = {'n': False, 'none': False, 'nay': False,
57 8887f41d Carles
               'y': True, '': True, 'aye': True, 'sure': True}
58 8887f41d Carles
for answer, val in new_answers.items():
59 8887f41d Carles
    dos_inp.BOOLEAN_STATES[answer] = val
60 8887f41d Carles
turn_false_answers = [answer for answer in dos_inp.BOOLEAN_STATES
61 8887f41d Carles
                      if dos_inp.BOOLEAN_STATES[answer] is False]
62 8887f41d Carles
63 8887f41d Carles
no_sect_err = "Section '%s' not found on input file"
64 8887f41d Carles
no_opt_err = "Option '%s' not found on section '%s'"
65 8887f41d Carles
num_error = "'%s' value must be a %s"
66 8887f41d Carles
unexp_error = "An unexpected error occurred"
67 8887f41d Carles
68 8887f41d Carles
69 8887f41d Carles
def try_command(command, expct_error_types: list, *args, **kwargs):
70 8887f41d Carles
    """Try to run a command and record exceptions (expected and not) on a log.
71 8887f41d Carles
    
72 8887f41d Carles
    @param command: method or function, the command to be executed.
73 8887f41d Carles
    @param expct_error_types: tuple of tuples, every inner tuple is supposed to
74 8887f41d Carles
    contain an exception type (eg. ValueError, TypeError, etc.) to be caught and
75 8887f41d Carles
    a message to print in the log and on the screen explaining the exception.
76 8887f41d Carles
    Error types that are not allow to be called with a custom message as only
77 8887f41d Carles
    error argument are not supported.
78 8887f41d Carles
    The outer tuple encloses all couples of error types and their relative
79 8887f41d Carles
    messages.
80 8887f41d Carles
    *args and **kwargs: arguments and keyword-arguments of the command to be
81 8887f41d Carles
    executed.
82 8887f41d Carles
    When trying to run 'command' with its args and kwargs, if an exception
83 8887f41d Carles
    present on the 'error_types' occurs, its relative error message is recorded
84 8887f41d Carles
    on the log and a same type exception is raised with the custom message.
85 8887f41d Carles
    """
86 8887f41d Carles
87 8887f41d Carles
    err = False
88 8887f41d Carles
    try:
89 8887f41d Carles
        return_val = command(*args, **kwargs)
90 8887f41d Carles
    except Exception as e:
91 8887f41d Carles
        for expct_err in expct_error_types:
92 8887f41d Carles
            if isinstance(e, expct_err[0]):
93 8887f41d Carles
                logger.error(expct_err[1])
94 8887f41d Carles
                err = expct_err[0](expct_err[1])
95 8887f41d Carles
                break
96 8887f41d Carles
        else:
97 8887f41d Carles
            logger.exception(unexp_error)
98 8887f41d Carles
            err = e
99 8887f41d Carles
    else:
100 8887f41d Carles
        err = False
101 8887f41d Carles
        return return_val
102 8887f41d Carles
    finally:
103 8887f41d Carles
        if isinstance(err, BaseException):
104 8887f41d Carles
            raise err
105 17e72a49 Carles
106 772b40e5 Carles
107 8887f41d Carles
def str2lst(cmplx_str):  # TODO: enable deeper level of nested lists
108 b77be9ad Carles
    """Converts a string of integers, and groups of them, to a list.
109 b77be9ad Carles

110 b77be9ad Carles
    Keyword arguments:
111 b77be9ad Carles
    @param cmplx_str: str, string of integers and groups of them enclosed by
112 b77be9ad Carles
    parentheses-like characters.
113 b77be9ad Carles
    - Group enclosers: '()' '[]' and '{}'.
114 b77be9ad Carles
    - Integer separators: ',' ';' and ' '.
115 b77be9ad Carles
    - Nested groups are not allowed: '3 ((6 7) 8) 4'.
116 b77be9ad Carles

117 b77be9ad Carles
    @return list, list of integers, or list of integers in the case they were
118 b77be9ad Carles
    grouped. First, the singlets are placed, and then the groups in input order.
119 b77be9ad Carles

120 b77be9ad Carles
    eg. '128,(135 138;141] 87 {45, 68}' -> [128, 87, [135, 138, 141], [45, 68]]
121 17e72a49 Carles
    """
122 b77be9ad Carles
123 8887f41d Carles
    # Checks
124 8887f41d Carles
    error_msg = "Function argument should be a str,sequence of integer " \
125 8887f41d Carles
                "numbers separated by ',' ';' or ' '." \
126 79e3db42 Carles
                "\nThey can be grouped in parentheses-like enclosers: '()', " \
127 79e3db42 Carles
                "'[]' or {}. Nested groups are not allowed. \n" \
128 79e3db42 Carles
                "eg. 128,(135 138;141) 87 {45, 68}"
129 8887f41d Carles
    cmplx_str = try_command(cmplx_str.replace, [(AttributeError, error_msg)],
130 8887f41d Carles
                            ',', ' ')
131 8887f41d Carles
132 8887f41d Carles
    cmplx_str = cmplx_str.replace(';', ' ').replace('[', '(').replace(
133 8887f41d Carles
        ']', ')').replace('{', '(').replace('}', ')')
134 8887f41d Carles
135 8887f41d Carles
    try_command(list, [(ValueError, error_msg)], map(int, cmplx_str.replace(
136 8887f41d Carles
        ')', '').replace('(', '').split()))
137 8887f41d Carles
138 b5b2af64 Carles
    deepness = 0
139 b5b2af64 Carles
    for el in cmplx_str.split():
140 b5b2af64 Carles
        if '(' in el:
141 b5b2af64 Carles
            deepness += 1
142 b5b2af64 Carles
        if ')' in el:
143 b5b2af64 Carles
            deepness += -1
144 b5b2af64 Carles
        if deepness > 1 or deepness < 0:
145 9f7bb440 Carles
            logger.error(error_msg)
146 b5b2af64 Carles
            raise ValueError(error_msg)
147 17e72a49 Carles
148 772b40e5 Carles
    init_list = cmplx_str.split()
149 772b40e5 Carles
    start_group = []
150 772b40e5 Carles
    end_group = []
151 772b40e5 Carles
    for i, element in enumerate(init_list):
152 73402e22 Carles
        if '(' in element:
153 772b40e5 Carles
            start_group.append(i)
154 17e72a49 Carles
            init_list[i] = element.replace('(', '')
155 73402e22 Carles
        if ')' in element:
156 772b40e5 Carles
            end_group.append(i)
157 17e72a49 Carles
            init_list[i] = element.replace(')', '')
158 772b40e5 Carles
159 772b40e5 Carles
    init_list = list(map(int, init_list))
160 772b40e5 Carles
161 17e72a49 Carles
    new_list = []
162 772b40e5 Carles
    for start_el, end_el in zip(start_group, end_group):
163 17e72a49 Carles
        new_list.append(init_list[start_el:end_el + 1])
164 772b40e5 Carles
165 772b40e5 Carles
    for v in new_list:
166 772b40e5 Carles
        for el in v:
167 772b40e5 Carles
            init_list.remove(el)
168 772b40e5 Carles
    return init_list + new_list
169 772b40e5 Carles
170 17e72a49 Carles
171 b77be9ad Carles
def check_expect_val(value, expect_vals):
172 b77be9ad Carles
    """Checks whether an option lies within its expected values.
173 b77be9ad Carles

174 b77be9ad Carles
    Keyword arguments:
175 b77be9ad Carles
    @param value: The variable to check if its value lies within the expected
176 b77be9ad Carles
    ones
177 b77be9ad Carles
    @param expect_vals: list, list of values allowed for the present option.
178 b77be9ad Carles
    @raise ValueError: if the value is not among the expected ones.
179 b77be9ad Carles
    @return True if the value is among the expected ones.
180 b77be9ad Carles
    """
181 9f7bb440 Carles
    adeq_val_err = "'%s' is not an adequate value.\n" \
182 b77be9ad Carles
                   "Adequate values: %s"
183 b77be9ad Carles
    if not any([exp_val in value for exp_val in expect_vals]):
184 9f7bb440 Carles
        logger.error(adeq_val_err % (value, expect_vals))
185 b77be9ad Carles
        raise ValueError(adeq_val_err % (value, expect_vals))
186 b77be9ad Carles
187 b77be9ad Carles
    return True
188 b77be9ad Carles
189 b77be9ad Carles
190 03fab2dd Carles
def check_inp_file(inp_file, code):
191 03fab2dd Carles
    if code == 'cp2k':
192 03fab2dd Carles
        from pycp2k import CP2K
193 03fab2dd Carles
        cp2k = CP2K()
194 03fab2dd Carles
        try_command(cp2k.parse,
195 03fab2dd Carles
                    [(UnboundLocalError, "Invalid CP2K input file")], inp_file)
196 03fab2dd Carles
197 03fab2dd Carles
198 8887f41d Carles
def get_run_type():
199 b1d27be5 Carles
    isolated, screening, refinement = (False, False, False)
200 5cc4994b Carles
    run_type_vals = ['isolated', 'screening', 'refinement', 'adsorption',
201 5cc4994b Carles
                     'full']
202 5cc4994b Carles
    check_expect_val(dos_inp.get('Global', 'run_type').lower(), run_type_vals)
203 5cc4994b Carles
204 5cc4994b Carles
    run_type = dos_inp.get('Global', 'run_type').lower()
205 5cc4994b Carles
    if 'isolated' in run_type:
206 5cc4994b Carles
        isolated = True
207 5cc4994b Carles
    if 'screening' in run_type:
208 5cc4994b Carles
        screening = True
209 5cc4994b Carles
    if 'refinement' in run_type:
210 5cc4994b Carles
        refinement = True
211 5cc4994b Carles
    if 'adsorption' in run_type:
212 5cc4994b Carles
        screening, refinement = (True, True)
213 5cc4994b Carles
    if 'full' in run_type:
214 5cc4994b Carles
        isolated, screening, refinement = (True, True, True)
215 5cc4994b Carles
216 8887f41d Carles
    return isolated, screening, refinement
217 17e72a49 Carles
218 8887f41d Carles
219 8887f41d Carles
def get_code():
220 772b40e5 Carles
    code_vals = ['cp2k']
221 5cc4994b Carles
    check_expect_val(dos_inp.get('Global', 'code').lower(), code_vals)
222 5cc4994b Carles
    code = dos_inp.get('Global', 'code').lower()
223 8887f41d Carles
    return code
224 8887f41d Carles
225 17e72a49 Carles
226 8887f41d Carles
def get_batch_q_sys():
227 0db30d07 Carles
    batch_q_sys_vals = ['sge', 'lsf', 'local', 'none']
228 5cc4994b Carles
    check_expect_val(dos_inp.get('Global', 'batch_q_sys').lower(),
229 5cc4994b Carles
                     batch_q_sys_vals)
230 5cc4994b Carles
    batch_q_sys = dos_inp.get('Global', 'batch_q_sys').lower()
231 8887f41d Carles
    return batch_q_sys
232 17e72a49 Carles
233 8887f41d Carles
234 99afde40 Carles
def get_subm_script():
235 99afde40 Carles
    subm_script = dos_inp.get('Global', 'subm_script', fallback=False)
236 99afde40 Carles
    if subm_script and not os.path.isfile(subm_script):
237 99afde40 Carles
        logger.error(f'File {subm_script} not found')
238 99afde40 Carles
        raise FileNotFoundError(f'File {subm_script} not found')
239 99afde40 Carles
    return subm_script
240 99afde40 Carles
241 99afde40 Carles
242 99afde40 Carles
def get_project_name():
243 bd573212 Carles
    project_name = dos_inp.get('Global', 'project_name', fallback='')
244 99afde40 Carles
    return project_name
245 99afde40 Carles
246 99afde40 Carles
247 8887f41d Carles
def get_relaunch_err():
248 9f7bb440 Carles
    relaunch_err_vals = ['geo_not_conv', 'false']
249 b1d27be5 Carles
    relaunch_err = dos_inp.get('Global', 'relaunch_err',
250 17e72a49 Carles
                               fallback="False")
251 b1d27be5 Carles
    if relaunch_err.lower() in turn_false_answers:
252 8887f41d Carles
        return False
253 79e3db42 Carles
    else:
254 79e3db42 Carles
        check_expect_val(relaunch_err.lower(), relaunch_err_vals)
255 8887f41d Carles
    return relaunch_err
256 8887f41d Carles
257 8887f41d Carles
258 8887f41d Carles
def get_max_qw():
259 8887f41d Carles
    err_msg = num_error % ('max_qw', 'positive integer')
260 8887f41d Carles
    max_qw = try_command(dos_inp.getint, [(ValueError, err_msg)],
261 8887f41d Carles
                         'Global', 'max_qw', fallback=3)
262 17e72a49 Carles
263 9f7bb440 Carles
    if max_qw < 1:
264 9f7bb440 Carles
        logger.error(num_error % ('max_qw', 'positive integer'))
265 9f7bb440 Carles
        raise ValueError(num_error % ('max_qw', 'positive integer'))
266 8887f41d Carles
    return max_qw
267 8887f41d Carles
268 8887f41d Carles
269 8887f41d Carles
def get_special_atoms():
270 8887f41d Carles
    from ase.data import chemical_symbols
271 17e72a49 Carles
272 5cc4994b Carles
    spec_at_err = '\'special_atoms\' does not have an adequate format.\n' \
273 5cc4994b Carles
                  'Adequate format: (Fe1 Fe) (O1 O)'
274 5cc4994b Carles
    special_atoms = dos_inp.get('Global', 'special_atoms', fallback="False")
275 b1d27be5 Carles
    if special_atoms.lower() in turn_false_answers:
276 b1d27be5 Carles
        special_atoms = False
277 b1d27be5 Carles
    else:
278 8949edd0 Carles
        # Converts the string into a list of tuples
279 8949edd0 Carles
        lst_tple = [tuple(pair.replace("(", "").split()) for pair in
280 b1d27be5 Carles
                    special_atoms.split(")")[:-1]]
281 5cc4994b Carles
        if len(lst_tple) == 0:
282 9f7bb440 Carles
            logger.error(spec_at_err)
283 5cc4994b Carles
            raise ValueError(spec_at_err)
284 9f7bb440 Carles
        for i, tup in enumerate(lst_tple):
285 5cc4994b Carles
            if type(tup) is not tuple or len(tup) != 2:
286 9f7bb440 Carles
                logger.error(spec_at_err)
287 5cc4994b Carles
                raise ValueError(spec_at_err)
288 5cc4994b Carles
            if tup[1].capitalize() not in chemical_symbols:
289 5cc4994b Carles
                elem_err = "The second element of the couple should be an " \
290 9f7bb440 Carles
                           "actual element of the periodic table"
291 9f7bb440 Carles
                logger.error(elem_err)
292 5cc4994b Carles
                raise ValueError(elem_err)
293 9f7bb440 Carles
            if tup[0].capitalize() in chemical_symbols:
294 9f7bb440 Carles
                elem_err = "The first element of the couple is already an " \
295 9f7bb440 Carles
                           "actual element of the periodic table, "
296 9f7bb440 Carles
                logger.error(elem_err)
297 9f7bb440 Carles
                raise ValueError(elem_err)
298 9f7bb440 Carles
            for j, tup2 in enumerate(lst_tple):
299 9f7bb440 Carles
                if j <= i:
300 9f7bb440 Carles
                    continue
301 9f7bb440 Carles
                if tup2[0] == tup[0]:
302 9f7bb440 Carles
                    label_err = f'You have specified the label {tup[0]} to ' \
303 8887f41d Carles
                                f'more than one special atom'
304 9f7bb440 Carles
                    logger.error(label_err)
305 9f7bb440 Carles
                    raise ValueError(label_err)
306 b1d27be5 Carles
        special_atoms = lst_tple
307 8887f41d Carles
    return special_atoms
308 8887f41d Carles
309 8887f41d Carles
310 8887f41d Carles
def get_isol_inp_file():
311 8887f41d Carles
    isol_inp_file = dos_inp.get('Isolated', 'isol_inp_file')
312 8887f41d Carles
    if not os.path.isfile(isol_inp_file):
313 8887f41d Carles
        logger.error(f'File {isol_inp_file} not found')
314 8887f41d Carles
        raise FileNotFoundError(f'File {isol_inp_file} not found')
315 8887f41d Carles
    return isol_inp_file
316 8887f41d Carles
317 8887f41d Carles
318 95dc2c8e Carles
def get_molec_file():
319 95dc2c8e Carles
    molec_file = dos_inp.get('Isolated', 'molec_file')
320 95dc2c8e Carles
    if not os.path.isfile(molec_file):
321 95dc2c8e Carles
        logger.error(f'File {molec_file} not found')
322 95dc2c8e Carles
        raise FileNotFoundError(f'File {molec_file} not found')
323 95dc2c8e Carles
    return molec_file
324 95dc2c8e Carles
325 95dc2c8e Carles
326 8887f41d Carles
def get_cluster_magns():
327 8887f41d Carles
    clust_magns_vals = ['energy', 'moi']
328 8887f41d Carles
    cluster_magns_str = dos_inp.get('Isolated', 'cluster_magns',
329 8887f41d Carles
                                    fallback='energy')
330 8887f41d Carles
    cluster_magns_str.replace(',', ' ').replace(';', ' ')
331 8887f41d Carles
    cluster_magns = cluster_magns_str.split(' ')
332 8887f41d Carles
    cluster_magns = [m.lower() for m in cluster_magns]
333 8887f41d Carles
    for m in cluster_magns:
334 8887f41d Carles
        check_expect_val(m, clust_magns_vals)
335 8887f41d Carles
    return cluster_magns
336 8887f41d Carles
337 8887f41d Carles
338 8887f41d Carles
def get_num_conformers():
339 8887f41d Carles
    err_msg = num_error % ('num_conformers', 'positive integer')
340 8887f41d Carles
    num_conformers = try_command(dos_inp.getint, [(ValueError, err_msg)],
341 8887f41d Carles
                                 'Isolated', 'num_conformers', fallback=100)
342 8887f41d Carles
    if num_conformers < 1:
343 8887f41d Carles
        logger.error(err_msg)
344 8887f41d Carles
        raise ValueError(err_msg)
345 8887f41d Carles
    return num_conformers
346 8887f41d Carles
347 8887f41d Carles
348 8887f41d Carles
def get_num_prom_cand():
349 8887f41d Carles
    err_msg = num_error % ('num_prom_cand', 'positive integer')
350 8887f41d Carles
    num_prom_cand = try_command(dos_inp.getint, [(ValueError, err_msg)],
351 8887f41d Carles
                                'Isolated', 'num_prom_cand', fallback=3)
352 8887f41d Carles
    if num_prom_cand < 1:
353 8887f41d Carles
        logger.error(err_msg)
354 8887f41d Carles
        raise ValueError(err_msg)
355 8887f41d Carles
    return num_prom_cand
356 8887f41d Carles
357 8887f41d Carles
358 a5c74494 Carles
def get_iso_rmsd():
359 a5c74494 Carles
    err_msg = num_error % ('iso_rmsd', 'positive decimal number')
360 a5c74494 Carles
    iso_rmsd = try_command(dos_inp.getfloat, [(ValueError, err_msg)],
361 a5c74494 Carles
                           'Isolated', 'iso_rmsd', fallback=0.05)
362 a5c74494 Carles
    if iso_rmsd <= 0.0:
363 8887f41d Carles
        logger.error(err_msg)
364 8887f41d Carles
        raise ValueError(err_msg)
365 a5c74494 Carles
    return iso_rmsd
366 8887f41d Carles
367 8887f41d Carles
368 b1f6e69d Carles
def get_min_confs():
369 b1f6e69d Carles
    err_msg = "'min_confs' should be have a boolean value (True or False)"
370 b1f6e69d Carles
    min_confs = try_command(dos_inp.getboolean,
371 b1f6e69d Carles
                            [(ValueError, err_msg)],
372 b1f6e69d Carles
                            'Isolated', 'min_confs', fallback=True)
373 b1f6e69d Carles
    return min_confs
374 b1f6e69d Carles
375 b1f6e69d Carles
376 8887f41d Carles
def get_screen_inp_file():
377 8887f41d Carles
    screen_inp_file = dos_inp.get('Screening', 'screen_inp_file')
378 8887f41d Carles
    if not os.path.isfile(screen_inp_file):
379 8887f41d Carles
        logger.error(f'File {screen_inp_file} not found')
380 8887f41d Carles
        raise FileNotFoundError(f'File {screen_inp_file} not found')
381 8887f41d Carles
    return screen_inp_file
382 8887f41d Carles
383 8887f41d Carles
384 8887f41d Carles
def get_sites():
385 8887f41d Carles
    err_msg = 'The value of sites should be a list of atom numbers ' \
386 8887f41d Carles
              '(ie. positive integers) or groups of atom numbers ' \
387 8887f41d Carles
              'grouped by parentheses-like enclosers. \n' \
388 8887f41d Carles
              'eg. 128,(135 138;141) 87 {45, 68}'
389 8887f41d Carles
    # Convert the string into a list of lists
390 8887f41d Carles
    sites = try_command(str2lst,
391 8887f41d Carles
                        [(ValueError, err_msg), (AttributeError, err_msg)],
392 8887f41d Carles
                        dos_inp.get('Screening', 'sites'))
393 8887f41d Carles
    # Check all elements of the list (of lists) are positive integers
394 8887f41d Carles
    for site in sites:
395 8887f41d Carles
        if type(site) is list:
396 8887f41d Carles
            for atom in site:
397 8887f41d Carles
                if atom < 0:
398 8887f41d Carles
                    logger.error(err_msg)
399 8887f41d Carles
                    raise ValueError(err_msg)
400 8887f41d Carles
        elif type(site) is int:
401 8887f41d Carles
            if site < 0:
402 8887f41d Carles
                logger.error(err_msg)
403 8887f41d Carles
                raise ValueError(err_msg)
404 8887f41d Carles
        else:
405 8887f41d Carles
            logger.error(err_msg)
406 8887f41d Carles
            raise ValueError(err_msg)
407 8887f41d Carles
408 8887f41d Carles
    return sites
409 8887f41d Carles
410 8887f41d Carles
411 8887f41d Carles
def get_molec_ads_ctrs():
412 8887f41d Carles
    err_msg = 'The value of molec_ads_ctrs should be a list of atom' \
413 8887f41d Carles
              ' numbers (ie. positive integers) or groups of atom ' \
414 8887f41d Carles
              'numbers enclosed by parentheses-like characters. \n' \
415 8887f41d Carles
              'eg. 128,(135 138;141) 87 {45, 68}'
416 8887f41d Carles
    # Convert the string into a list of lists
417 8887f41d Carles
    molec_ads_ctrs = try_command(str2lst,
418 8887f41d Carles
                                 [(ValueError, err_msg),
419 8887f41d Carles
                                  (AttributeError, err_msg)],
420 8887f41d Carles
                                 dos_inp.get('Screening', 'molec_ads_ctrs'))
421 8887f41d Carles
    # Check all elements of the list (of lists) are positive integers
422 8887f41d Carles
    for ctr in molec_ads_ctrs:
423 8887f41d Carles
        if type(ctr) is list:
424 8887f41d Carles
            for atom in ctr:
425 8887f41d Carles
                if atom < 0:
426 8887f41d Carles
                    logger.error(err_msg)
427 8887f41d Carles
                    raise ValueError(err_msg)
428 8887f41d Carles
        elif type(ctr) is int:
429 8887f41d Carles
            if ctr < 0:
430 8887f41d Carles
                logger.error(err_msg)
431 8887f41d Carles
                raise ValueError(err_msg)
432 8887f41d Carles
        else:
433 8887f41d Carles
            logger.error(err_msg)
434 8887f41d Carles
            raise ValueError(err_msg)
435 8887f41d Carles
436 8887f41d Carles
    return molec_ads_ctrs
437 8887f41d Carles
438 8887f41d Carles
439 8887f41d Carles
def get_try_disso():
440 8887f41d Carles
    err_msg = "try_disso should be have a boolean value (True or False)"
441 8887f41d Carles
    try_disso = try_command(dos_inp.getboolean,
442 8887f41d Carles
                            [(ValueError, err_msg)],
443 8887f41d Carles
                            'Screening', 'try_disso', fallback=False)
444 8887f41d Carles
    return try_disso
445 8887f41d Carles
446 8887f41d Carles
447 8887f41d Carles
def get_pts_per_angle():
448 8887f41d Carles
    err_msg = num_error % ('sample_points_per_angle',
449 8887f41d Carles
                           'positive integer')
450 8887f41d Carles
    pts_per_angle = try_command(dos_inp.getint,
451 8887f41d Carles
                                [(ValueError, err_msg)],
452 8887f41d Carles
                                'Screening', 'sample_points_per_angle',
453 8887f41d Carles
                                fallback=3)
454 8887f41d Carles
455 8887f41d Carles
    return pts_per_angle
456 8887f41d Carles
457 8887f41d Carles
458 8887f41d Carles
def get_coll_thrsld():
459 8887f41d Carles
    err_msg = num_error % ('collision_threshold',
460 8887f41d Carles
                           'positive decimal number')
461 8887f41d Carles
462 8887f41d Carles
    coll_thrsld = try_command(dos_inp.getfloat,
463 8887f41d Carles
                              [(ValueError, err_msg)],
464 8887f41d Carles
                              'Screening', 'collision_threshold', fallback=1.2)
465 8887f41d Carles
    if coll_thrsld <= 0:
466 8887f41d Carles
        logger.error(err_msg)
467 8887f41d Carles
        raise ValueError(err_msg)
468 8887f41d Carles
469 8887f41d Carles
    return coll_thrsld
470 8887f41d Carles
471 8887f41d Carles
472 8887f41d Carles
def get_screen_rmsd():
473 8887f41d Carles
    err_msg = num_error % ('screen_rmsd', 'positive decimal number')
474 8887f41d Carles
    screen_rmsd = try_command(dos_inp.getfloat,
475 8887f41d Carles
                              [(ValueError, err_msg)],
476 8887f41d Carles
                              'Screening', 'screen_rmsd', fallback=0.05)
477 8887f41d Carles
    if screen_rmsd <= 0:
478 8887f41d Carles
        logger.error(err_msg)
479 8887f41d Carles
        raise ValueError(err_msg)
480 8887f41d Carles
481 8887f41d Carles
    return screen_rmsd
482 8887f41d Carles
483 8887f41d Carles
484 8887f41d Carles
def get_coll_bottom_z():
485 8887f41d Carles
    err_msg = num_error % ('collision_bottom_z', 'decimal number')
486 8887f41d Carles
    coll_bottom_z = dos_inp.get('Screening', 'collision_bottom_z',
487 8887f41d Carles
                                fallback="False")
488 8887f41d Carles
    if coll_bottom_z.lower() in turn_false_answers:
489 8887f41d Carles
        coll_bottom_z = False
490 8887f41d Carles
    else:
491 8887f41d Carles
        coll_bottom_z = try_command(float, [(ValueError, err_msg)],
492 8887f41d Carles
                                    coll_bottom_z)
493 8887f41d Carles
494 8887f41d Carles
    return coll_bottom_z
495 8887f41d Carles
496 8887f41d Carles
497 8887f41d Carles
def get_refine_inp_file():
498 8887f41d Carles
    refine_inp_file = dos_inp.get('Refinement', 'refine_inp_file')
499 8887f41d Carles
    if not os.path.isfile(refine_inp_file):
500 8887f41d Carles
        logger.error(f'File {refine_inp_file} not found')
501 8887f41d Carles
        raise FileNotFoundError(f'File {refine_inp_file} not found')
502 8887f41d Carles
503 8887f41d Carles
    return refine_inp_file
504 8887f41d Carles
505 8887f41d Carles
506 8887f41d Carles
def get_energy_cutoff():
507 8887f41d Carles
    err_msg = num_error % ('energy_cutoff', 'positive decimal number')
508 8887f41d Carles
    energy_cutoff = try_command(dos_inp.getfloat,
509 8887f41d Carles
                                [(ValueError, err_msg)],
510 8887f41d Carles
                                'Refinement', 'energy_cutoff', fallback=0.5)
511 8887f41d Carles
    if energy_cutoff < 0:
512 8887f41d Carles
        logger.error(err_msg)
513 8887f41d Carles
        raise ValueError(err_msg)
514 8887f41d Carles
    return energy_cutoff
515 8887f41d Carles
516 8887f41d Carles
517 8887f41d Carles
def read_input(in_file):
518 8887f41d Carles
    err = False
519 8887f41d Carles
    try:
520 8887f41d Carles
        dos_inp.read(in_file)
521 8887f41d Carles
    except MissingSectionHeaderError as e:
522 8887f41d Carles
        logger.error('There are options in the input file without a Section '
523 8887f41d Carles
                     'header')
524 8887f41d Carles
        err = e
525 8887f41d Carles
    except DuplicateOptionError as e:
526 8887f41d Carles
        logger.error('There is an option in the input file that has been '
527 8887f41d Carles
                     'specified more than once, possibly due to the lack of a '
528 8887f41d Carles
                     'Section header')
529 8887f41d Carles
        err = e
530 8887f41d Carles
    except Exception as e:
531 8887f41d Carles
        err = e
532 8887f41d Carles
    else:
533 8887f41d Carles
        err = False
534 8887f41d Carles
    finally:
535 8887f41d Carles
        if isinstance(err, BaseException):
536 8887f41d Carles
            raise err
537 17e72a49 Carles
538 8887f41d Carles
    return_vars = {}
539 8887f41d Carles
540 8887f41d Carles
    # Global
541 8887f41d Carles
    if not dos_inp.has_section('Global'):
542 8887f41d Carles
        logger.error(no_sect_err % 'Global')
543 8887f41d Carles
        raise NoSectionError('Global')
544 8887f41d Carles
545 8887f41d Carles
    # Mandatory options
546 8887f41d Carles
    # Checks whether the mandatory options 'run_type', 'code', etc. are present.
547 bcc9ff75 Carles
    glob_mand_opts = ['run_type', 'code', 'batch_q_sys']
548 7fd58762 Carles
    for opt in glob_mand_opts:
549 8887f41d Carles
        if not dos_inp.has_option('Global', opt):
550 8887f41d Carles
            logger.error(no_opt_err % (opt, 'Global'))
551 8887f41d Carles
            raise NoOptionError(opt, 'Global')
552 8887f41d Carles
553 8887f41d Carles
    # Gets which sections are to be carried out
554 8887f41d Carles
    isolated, screening, refinement = get_run_type()
555 8887f41d Carles
    return_vars['isolated'] = isolated
556 8887f41d Carles
    return_vars['screening'] = screening
557 8887f41d Carles
    return_vars['refinement'] = refinement
558 8887f41d Carles
    return_vars['code'] = get_code()
559 8887f41d Carles
    return_vars['batch_q_sys'] = get_batch_q_sys()
560 8887f41d Carles
561 99afde40 Carles
    # Dependent options:
562 99afde40 Carles
    return_vars['subm_script'] = get_subm_script()
563 99afde40 Carles
    if return_vars['batch_q_sys'] != 'local' and not return_vars['subm_script']:
564 99afde40 Carles
        sub_err = "'subm_script' must be provided if 'batch_q_sys' is not local"
565 99afde40 Carles
        logger.error(sub_err)
566 bd573212 Carles
        raise NoOptionError(opt, 'Global') # TODO Change to ValueError
567 99afde40 Carles
568 8887f41d Carles
    # Facultative options (Default/Fallback value present)
569 99afde40 Carles
    return_vars['project_name'] = get_project_name()
570 8887f41d Carles
    return_vars['relaunch_err'] = get_relaunch_err()
571 8887f41d Carles
    return_vars['max_qw'] = get_max_qw()
572 8887f41d Carles
    return_vars['special_atoms'] = get_special_atoms()
573 8887f41d Carles
574 8887f41d Carles
    # Isolated
575 b1d27be5 Carles
    if isolated:
576 b606c71a Carles
        if not dos_inp.has_section('Isolated'):
577 b606c71a Carles
            logger.error(no_sect_err % 'Isolated')
578 b606c71a Carles
            raise NoSectionError('Isolated')
579 8887f41d Carles
        # Mandatory options
580 8887f41d Carles
        # Checks whether the mandatory options are present.
581 95dc2c8e Carles
        iso_mand_opts = ['isol_inp_file', 'molec_file']
582 8887f41d Carles
        for opt in iso_mand_opts:
583 8887f41d Carles
            if not dos_inp.has_option('Isolated', opt):
584 8887f41d Carles
                logger.error(no_opt_err % (opt, 'Isolated'))
585 8887f41d Carles
                raise NoOptionError(opt, 'Isolated')
586 8887f41d Carles
        return_vars['isol_inp_file'] = get_isol_inp_file()
587 03fab2dd Carles
        check_inp_file(return_vars['isol_inp_file'], return_vars['code'])
588 95dc2c8e Carles
        return_vars['molec_file'] = get_molec_file()
589 8887f41d Carles
590 8887f41d Carles
        # Facultative options (Default/Fallback value present)
591 8887f41d Carles
        return_vars['cluster_magns'] = get_cluster_magns()
592 8887f41d Carles
        return_vars['num_conformers'] = get_num_conformers()
593 9fd1daa6 Carles
        # return_vars['num_prom_cand'] = get_num_prom_cand()
594 9fd1daa6 Carles
        # return_vars['iso_rmsd'] = get_iso_rmsd()
595 b1f6e69d Carles
        return_vars['min_confs'] = get_min_confs()
596 8887f41d Carles
597 8887f41d Carles
    # Screening
598 b1d27be5 Carles
    if screening:
599 772b40e5 Carles
        if not dos_inp.has_section('Screening'):
600 9f7bb440 Carles
            logger.error(no_sect_err % 'Screening')
601 772b40e5 Carles
            raise NoSectionError('Screening')
602 8887f41d Carles
        # Mandatory options:
603 8887f41d Carles
        # Checks whether the mandatory options are present.
604 8887f41d Carles
        screen_mand_opts = ['sites', 'molec_ads_ctrs', 'screen_inp_file']
605 8887f41d Carles
        for opt in screen_mand_opts:
606 772b40e5 Carles
            if not dos_inp.has_option('Screening', opt):
607 9f7bb440 Carles
                logger.error(no_opt_err % (opt, 'Screening'))
608 b1d27be5 Carles
                raise NoOptionError(opt, 'Screening')
609 8887f41d Carles
        return_vars['screen_inp_file'] = get_screen_inp_file()
610 8887f41d Carles
        return_vars['sites'] = get_sites()
611 8887f41d Carles
        return_vars['molec_ads_ctrs'] = get_molec_ads_ctrs()
612 8887f41d Carles
613 8887f41d Carles
        # Facultative options (Default value present)
614 8887f41d Carles
        return_vars['try_disso'] = get_try_disso()
615 8887f41d Carles
        return_vars['sample_points_per_angle'] = get_pts_per_angle()
616 8887f41d Carles
        return_vars['collision_threshold'] = get_coll_thrsld()
617 9fd1daa6 Carles
        # return_vars['screen_rmsd'] = get_screen_rmsd()
618 8887f41d Carles
        return_vars['collision_bottom_z'] = get_coll_bottom_z()
619 8887f41d Carles
620 8887f41d Carles
    # Refinement
621 b1d27be5 Carles
    if refinement:
622 8887f41d Carles
        if not dos_inp.has_section('Refinement'):
623 8887f41d Carles
            logger.error(no_sect_err % 'Refinement')
624 8887f41d Carles
            raise NoSectionError('Refinement')
625 8887f41d Carles
        # Mandatory options
626 8887f41d Carles
        # Checks whether the mandatory options are present.
627 8887f41d Carles
        ref_mand_opts = ['refine_inp_file']
628 8887f41d Carles
        for opt in ref_mand_opts:
629 8887f41d Carles
            if not dos_inp.has_option('Refinement', opt):
630 8887f41d Carles
                logger.error(no_opt_err % (opt, 'Refinement'))
631 8887f41d Carles
                raise NoOptionError(opt, 'Refinement')
632 8887f41d Carles
        return_vars['refine_inp_file'] = get_refine_inp_file()
633 8887f41d Carles
634 8887f41d Carles
        # Facultative options (Default value present)
635 8887f41d Carles
        return_vars['energy_cutoff'] = get_energy_cutoff()
636 b1d27be5 Carles
        # end energy_cutoff
637 9f7bb440 Carles
638 d8a6314e Carles
    return_vars_str = "\n\t".join([str(key) + ": " + str(val)
639 d8a6314e Carles
                                   for key, val in return_vars.items()])
640 d8a6314e Carles
    logger.info(
641 d8a6314e Carles
        f'Correctly read {in_file} parameters: \n\n\t{return_vars_str}\n')
642 d8a6314e Carles
643 772b40e5 Carles
    return return_vars
644 8887f41d Carles
645 8887f41d Carles
646 8887f41d Carles
if __name__ == "__main__":
647 8887f41d Carles
    import sys
648 8887f41d Carles
649 8887f41d Carles
    print(read_input(sys.argv[1]))