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

dockonsurf / modules / dos_input.py @ 3d56e566

Historique | Voir | Annoter | Télécharger (25,71 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 a7128ce1 Carles Marti
    # TODO Treat all-enclosing parenthesis as a list instead of list of 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 4533134f Carles Marti
    @param cmplx_str: str, string of integers (or floats) and groups of them
75 4533134f Carles Marti
    enclosed by parentheses-like characters.
76 b77be9ad Carles
    - Group enclosers: '()' '[]' and '{}'.
77 4533134f Carles Marti
    - Separators: ',' ';' and ' '.
78 b77be9ad Carles
    - Nested groups are not allowed: '3 ((6 7) 8) 4'.
79 4533134f Carles Marti
    @param func: either to use int or float
80 b77be9ad Carles

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

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

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