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

dockonsurf / modules / dos_input.py @ 69d17e8b

Historique | Voir | Annoter | Télécharger (22,48 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 69d17e8b Carles
from 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
unexp_error = "An unexpected error occurred"
68 8887f41d Carles
69 8887f41d Carles
70 8887f41d Carles
def str2lst(cmplx_str):  # TODO: enable deeper level of nested lists
71 b77be9ad Carles
    """Converts a string of integers, and groups of them, to a list.
72 b77be9ad Carles

73 b77be9ad Carles
    Keyword arguments:
74 b77be9ad Carles
    @param cmplx_str: str, string of integers and groups of them enclosed by
75 b77be9ad Carles
    parentheses-like characters.
76 b77be9ad Carles
    - Group enclosers: '()' '[]' and '{}'.
77 b77be9ad Carles
    - Integer separators: ',' ';' and ' '.
78 b77be9ad Carles
    - Nested groups are not allowed: '3 ((6 7) 8) 4'.
79 b77be9ad Carles

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

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

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