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

dockonsurf / modules / dos_input.py @ 54be2a9e

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