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

dockonsurf / modules / dos_input.py @ 695dcff8

Historique | Voir | Annoter | Télécharger (26,61 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 5fb01677 Carles Marti
import numpy as np
51 af3e2441 Carles Marti
from modules.utilities import try_command
52 8887f41d Carles
53 8887f41d Carles
logger = logging.getLogger('DockOnSurf')
54 8887f41d Carles
55 8887f41d Carles
dos_inp = ConfigParser(inline_comment_prefixes='#',
56 8887f41d Carles
                       empty_lines_in_values=False)
57 8887f41d Carles
58 8887f41d Carles
new_answers = {'n': False, 'none': False, 'nay': False,
59 8887f41d Carles
               'y': True, '': True, 'aye': True, 'sure': True}
60 8887f41d Carles
for answer, val in new_answers.items():
61 8887f41d Carles
    dos_inp.BOOLEAN_STATES[answer] = val
62 8887f41d Carles
turn_false_answers = [answer for answer in dos_inp.BOOLEAN_STATES
63 8887f41d Carles
                      if dos_inp.BOOLEAN_STATES[answer] is False]
64 8887f41d Carles
65 8887f41d Carles
no_sect_err = "Section '%s' not found on input file"
66 8887f41d Carles
no_opt_err = "Option '%s' not found on section '%s'"
67 8887f41d Carles
num_error = "'%s' value must be a %s"
68 8887f41d Carles
69 4533134f Carles Marti
70 4533134f Carles Marti
def str2lst(cmplx_str, func=int):  # TODO: enable deeper level of nested lists
71 a7128ce1 Carles Marti
    # TODO Treat all-enclosing parenthesis as a list instead of list of lists.
72 b77be9ad Carles
    """Converts a string of integers, and groups of them, to a list.
73 b77be9ad Carles

74 b77be9ad Carles
    Keyword arguments:
75 4533134f Carles Marti
    @param cmplx_str: str, string of integers (or floats) and groups of them
76 4533134f Carles Marti
    enclosed by parentheses-like characters.
77 b77be9ad Carles
    - Group enclosers: '()' '[]' and '{}'.
78 4533134f Carles Marti
    - Separators: ',' ';' and ' '.
79 b77be9ad Carles
    - Nested groups are not allowed: '3 ((6 7) 8) 4'.
80 4533134f Carles Marti
    @param func: either to use int or float
81 b77be9ad Carles

82 4533134f Carles Marti
    @return list, list of integers (or floats), or list of integers (or floats)
83 4533134f Carles Marti
    in the case they were grouped. First, the singlets are placed, and then the
84 4533134f Carles Marti
    groups in input order.
85 b77be9ad Carles

86 b77be9ad Carles
    eg. '128,(135 138;141] 87 {45, 68}' -> [128, 87, [135, 138, 141], [45, 68]]
87 17e72a49 Carles
    """
88 b77be9ad Carles
89 8887f41d Carles
    # Checks
90 a7128ce1 Carles Marti
    error_msg = "Function argument should be a str, sequence of integer " \
91 8887f41d Carles
                "numbers separated by ',' ';' or ' '." \
92 79e3db42 Carles
                "\nThey can be grouped in parentheses-like enclosers: '()', " \
93 79e3db42 Carles
                "'[]' or {}. Nested groups are not allowed. \n" \
94 79e3db42 Carles
                "eg. 128,(135 138;141) 87 {45, 68}"
95 8887f41d Carles
    cmplx_str = try_command(cmplx_str.replace, [(AttributeError, error_msg)],
96 8887f41d Carles
                            ',', ' ')
97 8887f41d Carles
98 8887f41d Carles
    cmplx_str = cmplx_str.replace(';', ' ').replace('[', '(').replace(
99 8887f41d Carles
        ']', ')').replace('{', '(').replace('}', ')')
100 8887f41d Carles
101 4533134f Carles Marti
    try_command(list, [(ValueError, error_msg)], map(func, cmplx_str.replace(
102 8887f41d Carles
        ')', '').replace('(', '').split()))
103 8887f41d Carles
104 b5b2af64 Carles
    deepness = 0
105 b5b2af64 Carles
    for el in cmplx_str.split():
106 b5b2af64 Carles
        if '(' in el:
107 b5b2af64 Carles
            deepness += 1
108 b5b2af64 Carles
        if ')' in el:
109 b5b2af64 Carles
            deepness += -1
110 b5b2af64 Carles
        if deepness > 1 or deepness < 0:
111 9f7bb440 Carles
            logger.error(error_msg)
112 b5b2af64 Carles
            raise ValueError(error_msg)
113 17e72a49 Carles
114 772b40e5 Carles
    init_list = cmplx_str.split()
115 772b40e5 Carles
    start_group = []
116 772b40e5 Carles
    end_group = []
117 772b40e5 Carles
    for i, element in enumerate(init_list):
118 73402e22 Carles
        if '(' in element:
119 772b40e5 Carles
            start_group.append(i)
120 17e72a49 Carles
            init_list[i] = element.replace('(', '')
121 73402e22 Carles
        if ')' in element:
122 772b40e5 Carles
            end_group.append(i)
123 17e72a49 Carles
            init_list[i] = element.replace(')', '')
124 772b40e5 Carles
125 4533134f Carles Marti
    init_list = list(map(func, init_list))
126 772b40e5 Carles
127 17e72a49 Carles
    new_list = []
128 772b40e5 Carles
    for start_el, end_el in zip(start_group, end_group):
129 17e72a49 Carles
        new_list.append(init_list[start_el:end_el + 1])
130 772b40e5 Carles
131 772b40e5 Carles
    for v in new_list:
132 772b40e5 Carles
        for el in v:
133 772b40e5 Carles
            init_list.remove(el)
134 772b40e5 Carles
    return init_list + new_list
135 772b40e5 Carles
136 17e72a49 Carles
137 b77be9ad Carles
def check_expect_val(value, expect_vals):
138 b77be9ad Carles
    """Checks whether an option lies within its expected values.
139 b77be9ad Carles

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