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

dockonsurf / modules / dos_input.py @ 9fd1daa6

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