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

dockonsurf / modules / dos_input.py @ 8af49f6d

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