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

dockonsurf / modules / dos_input.py @ b4aef3d7

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

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

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

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

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