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

dockonsurf / modules / dos_input.py @ e1c5f171

Historique | Voir | Annoter | Télécharger (27,37 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 09c3325a Carles Marti
get_max_jobs: Gets 'max_jobs' 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 4670488d Carles Marti
get_pre_opt: Gets 'pre_opt' value and checks that its value is acceptable.
27 0e83e6a6 Carles
get_screen_inp_file: Gets 'screen_inp_file' value and checks that its value is
28 8887f41d Carles
acceptable.
29 0e83e6a6 Carles
get_sites: Gets 'sites' value and checks that its value is acceptable.
30 0e83e6a6 Carles
get_molec_ads_ctrs: Gets 'molec_ads_ctrs' value and checks that its value is
31 8887f41d Carles
acceptable.
32 0e83e6a6 Carles
get_try_disso: Gets 'try_disso' value and checks that its value is acceptable.
33 0e83e6a6 Carles
get_pts_per_angle: Gets 'pts_per_angle' value and checks that its value is
34 8887f41d Carles
acceptable.
35 0e83e6a6 Carles
get_coll_thrsld: Gets 'coll_thrsld' value and checks that its value is
36 8887f41d Carles
acceptable.
37 0e83e6a6 Carles
get_screen_rmsd: Gets 'screen_rmsd' value and checks that its value is
38 8887f41d Carles
acceptable.
39 0e83e6a6 Carles
get_coll_bottom_z: Gets 'coll_bottom_z' value and checks that its value is
40 8887f41d Carles
acceptable.
41 0e83e6a6 Carles
get_refine_inp_file: Gets 'refine_inp_file' value and checks that its value is
42 8887f41d Carles
acceptable.
43 0e83e6a6 Carles
get_energy_cutoff: Gets 'energy_cutoff' value and checks that its value is
44 8887f41d Carles
acceptable.
45 17e72a49 Carles
"""
46 8887f41d Carles
import os.path
47 b5b2af64 Carles
import logging
48 8887f41d Carles
from configparser import ConfigParser, NoSectionError, NoOptionError, \
49 8887f41d Carles
    MissingSectionHeaderError, DuplicateOptionError
50 5fb01677 Carles Marti
import numpy as np
51 af3e2441 Carles Marti
from modules.utilities import try_command
52 8887f41d Carles
53 8887f41d Carles
logger = logging.getLogger('DockOnSurf')
54 8887f41d Carles
55 8887f41d Carles
dos_inp = ConfigParser(inline_comment_prefixes='#',
56 8887f41d Carles
                       empty_lines_in_values=False)
57 8887f41d Carles
58 8887f41d Carles
new_answers = {'n': False, 'none': False, 'nay': False,
59 8887f41d Carles
               'y': True, '': True, 'aye': True, 'sure': True}
60 8887f41d Carles
for answer, val in new_answers.items():
61 8887f41d Carles
    dos_inp.BOOLEAN_STATES[answer] = val
62 8887f41d Carles
turn_false_answers = [answer for answer in dos_inp.BOOLEAN_STATES
63 8887f41d Carles
                      if dos_inp.BOOLEAN_STATES[answer] is False]
64 8887f41d Carles
65 8887f41d Carles
no_sect_err = "Section '%s' not found on input file"
66 8887f41d Carles
no_opt_err = "Option '%s' not found on section '%s'"
67 8887f41d Carles
num_error = "'%s' value must be a %s"
68 8887f41d Carles
69 4533134f Carles Marti
70 4533134f Carles Marti
def str2lst(cmplx_str, func=int):  # TODO: enable deeper level of nested lists
71 a7128ce1 Carles Marti
    # TODO Treat all-enclosing parenthesis as a list instead of list of lists.
72 b77be9ad Carles
    """Converts a string of integers, and groups of them, to a list.
73 b77be9ad Carles

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

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

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

140 b77be9ad Carles
    Keyword arguments:
141 b77be9ad Carles
    @param value: The variable to check if its value lies within the expected
142 b77be9ad Carles
    ones
143 b77be9ad Carles
    @param expect_vals: list, list of values allowed for the present option.
144 b77be9ad Carles
    @raise ValueError: if the value is not among the expected ones.
145 b77be9ad Carles
    @return True if the value is among the expected ones.
146 b77be9ad Carles
    """
147 9f7bb440 Carles
    adeq_val_err = "'%s' is not an adequate value.\n" \
148 b77be9ad Carles
                   "Adequate values: %s"
149 821dca42 Carles Marti
    if not any([exp_val == value for exp_val in expect_vals]):
150 9f7bb440 Carles
        logger.error(adeq_val_err % (value, expect_vals))
151 b77be9ad Carles
        raise ValueError(adeq_val_err % (value, expect_vals))
152 b77be9ad Carles
153 b77be9ad Carles
    return True
154 b77be9ad Carles
155 b77be9ad Carles
156 03fab2dd Carles
def check_inp_file(inp_file, code):
157 03fab2dd Carles
    if code == 'cp2k':
158 03fab2dd Carles
        from pycp2k import CP2K
159 03fab2dd Carles
        cp2k = CP2K()
160 03fab2dd Carles
        try_command(cp2k.parse,
161 03fab2dd Carles
                    [(UnboundLocalError, "Invalid CP2K input file")], inp_file)
162 03fab2dd Carles
163 03fab2dd Carles
164 a7128ce1 Carles Marti
# Global
165 a7128ce1 Carles Marti
166 8887f41d Carles
def get_run_type():
167 b1d27be5 Carles
    isolated, screening, refinement = (False, False, False)
168 5cc4994b Carles
    run_type_vals = ['isolated', 'screening', 'refinement', 'adsorption',
169 5cc4994b Carles
                     'full']
170 081548a0 Carles Marti
    run_types = dos_inp.get('Global', 'run_type').split()
171 081548a0 Carles Marti
    for run_type in run_types:
172 081548a0 Carles Marti
        check_expect_val(run_type.lower(), run_type_vals)
173 081548a0 Carles Marti
        if 'isol' in run_type.lower():
174 081548a0 Carles Marti
            isolated = True
175 081548a0 Carles Marti
        if 'screen' in run_type.lower():
176 081548a0 Carles Marti
            screening = True
177 081548a0 Carles Marti
        if 'refine' in run_type.lower():
178 081548a0 Carles Marti
            refinement = True
179 081548a0 Carles Marti
        if 'adsor' in run_type.lower():
180 081548a0 Carles Marti
            screening, refinement = (True, True)
181 081548a0 Carles Marti
        if 'full' in run_type.lower():
182 081548a0 Carles Marti
            isolated, screening, refinement = (True, True, True)
183 5cc4994b Carles
184 8887f41d Carles
    return isolated, screening, refinement
185 17e72a49 Carles
186 8887f41d Carles
187 8887f41d Carles
def get_code():
188 772b40e5 Carles
    code_vals = ['cp2k']
189 5cc4994b Carles
    check_expect_val(dos_inp.get('Global', 'code').lower(), code_vals)
190 5cc4994b Carles
    code = dos_inp.get('Global', 'code').lower()
191 8887f41d Carles
    return code
192 8887f41d Carles
193 17e72a49 Carles
194 8887f41d Carles
def get_batch_q_sys():
195 ec5bba46 Carles Marti
    batch_q_sys_vals = ['sge', 'lsf', 'irene', 'local'] + turn_false_answers
196 5cc4994b Carles
    check_expect_val(dos_inp.get('Global', 'batch_q_sys').lower(),
197 5cc4994b Carles
                     batch_q_sys_vals)
198 5cc4994b Carles
    batch_q_sys = dos_inp.get('Global', 'batch_q_sys').lower()
199 821dca42 Carles Marti
    if batch_q_sys.lower() in turn_false_answers:
200 821dca42 Carles Marti
        return False
201 821dca42 Carles Marti
    else:
202 821dca42 Carles Marti
        return batch_q_sys
203 17e72a49 Carles
204 8887f41d Carles
205 99afde40 Carles
def get_subm_script():
206 99afde40 Carles
    subm_script = dos_inp.get('Global', 'subm_script', fallback=False)
207 99afde40 Carles
    if subm_script and not os.path.isfile(subm_script):
208 695dcff8 Carles Marti
        logger.error(f'File {subm_script} not found.')
209 99afde40 Carles
        raise FileNotFoundError(f'File {subm_script} not found')
210 99afde40 Carles
    return subm_script
211 99afde40 Carles
212 99afde40 Carles
213 99afde40 Carles
def get_project_name():
214 bd573212 Carles
    project_name = dos_inp.get('Global', 'project_name', fallback='')
215 99afde40 Carles
    return project_name
216 99afde40 Carles
217 99afde40 Carles
218 8887f41d Carles
def get_relaunch_err():
219 09c3325a Carles Marti
    relaunch_err_vals = ['geo_not_conv']
220 b1d27be5 Carles
    relaunch_err = dos_inp.get('Global', 'relaunch_err',
221 17e72a49 Carles
                               fallback="False")
222 b1d27be5 Carles
    if relaunch_err.lower() in turn_false_answers:
223 8887f41d Carles
        return False
224 79e3db42 Carles
    else:
225 79e3db42 Carles
        check_expect_val(relaunch_err.lower(), relaunch_err_vals)
226 8887f41d Carles
    return relaunch_err
227 8887f41d Carles
228 8887f41d Carles
229 09c3325a Carles Marti
def get_max_jobs():
230 09c3325a Carles Marti
    err_msg = num_error % ('max_jobs', 'positive integer')
231 09c3325a Carles Marti
    max_jobs = try_command(dos_inp.getint, [(ValueError, err_msg)],
232 70fa4e74 Carles Marti
                           'Global', 'max_jobs', fallback=3)
233 09c3325a Carles Marti
234 09c3325a Carles Marti
    if max_jobs < 1:
235 09c3325a Carles Marti
        logger.error(num_error % ('max_jobs', 'positive integer'))
236 09c3325a Carles Marti
        raise ValueError(num_error % ('max_jobs', 'positive integer'))
237 09c3325a Carles Marti
    return max_jobs
238 09c3325a Carles Marti
239 09c3325a Carles Marti
240 09c3325a Carles Marti
def get_type_max():
241 09c3325a Carles Marti
    running = ['r', 'run', 'running']
242 09c3325a Carles Marti
    pending = ['p', 'pending', 'q', 'qw', 'queued']
243 09c3325a Carles Marti
    type_max = dos_inp.get('Global', 'type_max', fallback="False")
244 75d6a31b Carles Marti
    if type_max.lower() in turn_false_answers:
245 09c3325a Carles Marti
        return False
246 75d6a31b Carles Marti
    elif type_max.lower() in running:
247 09c3325a Carles Marti
        return 'r'
248 75d6a31b Carles Marti
    elif type_max.lower() in pending:
249 09c3325a Carles Marti
        return 'p'
250 09c3325a Carles Marti
    else:
251 ad57fd42 Carles Marti
        err = f"Value for 'type_max' is not accepted. Accepted values: " \
252 70fa4e74 Carles Marti
              f"{running + pending + turn_false_answers}."
253 09c3325a Carles Marti
        logger.error(err)
254 09c3325a Carles Marti
        raise ValueError(err)
255 17e72a49 Carles
256 8887f41d Carles
257 8887f41d Carles
def get_special_atoms():
258 8887f41d Carles
    from ase.data import chemical_symbols
259 17e72a49 Carles
260 5cc4994b Carles
    spec_at_err = '\'special_atoms\' does not have an adequate format.\n' \
261 5cc4994b Carles
                  'Adequate format: (Fe1 Fe) (O1 O)'
262 5cc4994b Carles
    special_atoms = dos_inp.get('Global', 'special_atoms', fallback="False")
263 b1d27be5 Carles
    if special_atoms.lower() in turn_false_answers:
264 90819cc3 Carles Marti
        special_atoms = []
265 b1d27be5 Carles
    else:
266 8949edd0 Carles
        # Converts the string into a list of tuples
267 8949edd0 Carles
        lst_tple = [tuple(pair.replace("(", "").split()) for pair in
268 b1d27be5 Carles
                    special_atoms.split(")")[:-1]]
269 5cc4994b Carles
        if len(lst_tple) == 0:
270 9f7bb440 Carles
            logger.error(spec_at_err)
271 5cc4994b Carles
            raise ValueError(spec_at_err)
272 9f7bb440 Carles
        for i, tup in enumerate(lst_tple):
273 90819cc3 Carles Marti
            if not isinstance(tup, tuple) or len(tup) != 2:
274 9f7bb440 Carles
                logger.error(spec_at_err)
275 5cc4994b Carles
                raise ValueError(spec_at_err)
276 5cc4994b Carles
            if tup[1].capitalize() not in chemical_symbols:
277 5cc4994b Carles
                elem_err = "The second element of the couple should be an " \
278 9f7bb440 Carles
                           "actual element of the periodic table"
279 9f7bb440 Carles
                logger.error(elem_err)
280 5cc4994b Carles
                raise ValueError(elem_err)
281 9f7bb440 Carles
            if tup[0].capitalize() in chemical_symbols:
282 9f7bb440 Carles
                elem_err = "The first element of the couple is already an " \
283 9f7bb440 Carles
                           "actual element of the periodic table, "
284 9f7bb440 Carles
                logger.error(elem_err)
285 9f7bb440 Carles
                raise ValueError(elem_err)
286 9f7bb440 Carles
            for j, tup2 in enumerate(lst_tple):
287 9f7bb440 Carles
                if j <= i:
288 9f7bb440 Carles
                    continue
289 9f7bb440 Carles
                if tup2[0] == tup[0]:
290 9f7bb440 Carles
                    label_err = f'You have specified the label {tup[0]} to ' \
291 8887f41d Carles
                                f'more than one special atom'
292 9f7bb440 Carles
                    logger.error(label_err)
293 9f7bb440 Carles
                    raise ValueError(label_err)
294 b1d27be5 Carles
        special_atoms = lst_tple
295 8887f41d Carles
    return special_atoms
296 8887f41d Carles
297 8887f41d Carles
298 a7128ce1 Carles Marti
# Isolated
299 a7128ce1 Carles Marti
300 8887f41d Carles
def get_isol_inp_file():
301 8887f41d Carles
    isol_inp_file = dos_inp.get('Isolated', 'isol_inp_file')
302 8887f41d Carles
    if not os.path.isfile(isol_inp_file):
303 695dcff8 Carles Marti
        logger.error(f'File {isol_inp_file} not found.')
304 8887f41d Carles
        raise FileNotFoundError(f'File {isol_inp_file} not found')
305 8887f41d Carles
    return isol_inp_file
306 8887f41d Carles
307 8887f41d Carles
308 95dc2c8e Carles
def get_molec_file():
309 95dc2c8e Carles
    molec_file = dos_inp.get('Isolated', 'molec_file')
310 95dc2c8e Carles
    if not os.path.isfile(molec_file):
311 695dcff8 Carles Marti
        logger.error(f'File {molec_file} not found.')
312 95dc2c8e Carles
        raise FileNotFoundError(f'File {molec_file} not found')
313 95dc2c8e Carles
    return molec_file
314 95dc2c8e Carles
315 95dc2c8e Carles
316 8887f41d Carles
def get_num_conformers():
317 8887f41d Carles
    err_msg = num_error % ('num_conformers', 'positive integer')
318 8887f41d Carles
    num_conformers = try_command(dos_inp.getint, [(ValueError, err_msg)],
319 8887f41d Carles
                                 'Isolated', 'num_conformers', fallback=100)
320 8887f41d Carles
    if num_conformers < 1:
321 8887f41d Carles
        logger.error(err_msg)
322 8887f41d Carles
        raise ValueError(err_msg)
323 8887f41d Carles
    return num_conformers
324 8887f41d Carles
325 8887f41d Carles
326 4670488d Carles Marti
def get_pre_opt():
327 4670488d Carles Marti
    pre_opt_vals = ['uff', 'mmff'] + turn_false_answers
328 4670488d Carles Marti
    check_expect_val(dos_inp.get('Isolated', 'pre_opt').lower(), pre_opt_vals)
329 ad57fd42 Carles Marti
    pre_opt = dos_inp.get('Isolated', 'pre_opt').lower()
330 ad57fd42 Carles Marti
    if pre_opt in turn_false_answers:
331 4670488d Carles Marti
        return False
332 4670488d Carles Marti
    else:
333 4670488d Carles Marti
        return pre_opt
334 b1f6e69d Carles
335 70fa4e74 Carles Marti
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 695dcff8 Carles Marti
        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 695dcff8 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 af2bf62f 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 af2bf62f Carles Marti
def get_confs_per_magn():
450 af2bf62f Carles Marti
    err_msg = num_error % ('confs_per_magn', 'positive integer')
451 af2bf62f Carles Marti
    confs_per_magn = try_command(dos_inp.getint, [(ValueError, err_msg)],
452 af2bf62f Carles Marti
                                 'Screening', 'confs_per_magn', fallback=2)
453 af2bf62f Carles Marti
    if confs_per_magn <= 0:
454 af2bf62f Carles Marti
        logger.error(err_msg)
455 af2bf62f Carles Marti
        raise ValueError(err_msg)
456 af2bf62f Carles Marti
    return confs_per_magn
457 af2bf62f Carles Marti
458 af2bf62f Carles Marti
459 c845c6f2 Carles Marti
def get_surf_norm_vect():
460 c845c6f2 Carles Marti
    err = "'surf_norm_vect' must be either a 3 component vector or 'x', 'y' " \
461 c845c6f2 Carles Marti
          "or 'z'"
462 9c16a7e2 Carles Marti
    cart_axes = {'x': [1.0, 0.0, 0.0], '-x': [-1.0, 0.0, 0.0],
463 9c16a7e2 Carles Marti
                 'y': [0.0, 1.0, 0.0], '-y': [0.0, -1.0, 0.0],
464 9c16a7e2 Carles Marti
                 'z': [0.0, 0.0, 1.0], '-z': [0.0, 0.0, -1.0]}
465 c845c6f2 Carles Marti
    surf_norm_vect_str = dos_inp.get('Screening', 'surf_norm_vect',
466 5fb01677 Carles Marti
                                     fallback="z")
467 3d56e566 Carles Marti
468 9c16a7e2 Carles Marti
    if surf_norm_vect_str in cart_axes:
469 9c16a7e2 Carles Marti
        surf_norm_vect = cart_axes[surf_norm_vect_str]
470 3d56e566 Carles Marti
    else:
471 3d56e566 Carles Marti
        surf_norm_vect = try_command(str2lst, [(ValueError, err)],
472 5fb01677 Carles Marti
                                     surf_norm_vect_str, float)
473 c845c6f2 Carles Marti
        if len(surf_norm_vect) != 3:
474 c845c6f2 Carles Marti
            logger.error(err)
475 c845c6f2 Carles Marti
            raise ValueError(err)
476 3d56e566 Carles Marti
477 d4bedc18 Carles Marti
    return np.array(surf_norm_vect)
478 c845c6f2 Carles Marti
479 c845c6f2 Carles Marti
480 a7c7b43e Carles Marti
def get_ads_algo():
481 a7c7b43e Carles Marti
    algo_vals = ['euler', 'chemcat']
482 a7c7b43e Carles Marti
    check_expect_val(dos_inp.get('Screening', 'ads_algo').lower(), algo_vals)
483 ed5531ef Carles Marti
    ads_algo = dos_inp.get('Screening', 'ads_algo', fallback='euler').lower()
484 a7c7b43e Carles Marti
    return ads_algo
485 a7c7b43e Carles Marti
486 a7c7b43e Carles Marti
487 8887f41d Carles
def get_pts_per_angle():
488 d4bedc18 Carles Marti
    err_msg = num_error % ('sample_points_per_angle', 'positive integer')
489 8887f41d Carles
    pts_per_angle = try_command(dos_inp.getint,
490 8887f41d Carles
                                [(ValueError, err_msg)],
491 8887f41d Carles
                                'Screening', 'sample_points_per_angle',
492 8887f41d Carles
                                fallback=3)
493 d4bedc18 Carles Marti
    if pts_per_angle <= 0:
494 d4bedc18 Carles Marti
        logger.error(err_msg)
495 d4bedc18 Carles Marti
        raise ValueError(err_msg)
496 8887f41d Carles
    return pts_per_angle
497 8887f41d Carles
498 8887f41d Carles
499 7d97341d Carles Marti
def get_max_structures():
500 7d97341d Carles Marti
    err_msg = num_error % ('max_structures', 'positive integer')
501 7d97341d Carles Marti
    max_structures = try_command(dos_inp.getint, [(ValueError, err_msg)],
502 7d97341d Carles Marti
                                 'Screening', 'max_structures', fallback=np.inf)
503 7d97341d Carles Marti
    if max_structures <= 0:
504 7d97341d Carles Marti
        logger.error(err_msg)
505 7d97341d Carles Marti
        raise ValueError(err_msg)
506 7d97341d Carles Marti
    return max_structures
507 7d97341d Carles Marti
508 7d97341d Carles Marti
509 8887f41d Carles
def get_coll_thrsld():
510 8887f41d Carles
    err_msg = num_error % ('collision_threshold',
511 8887f41d Carles
                           'positive decimal number')
512 8887f41d Carles
513 8887f41d Carles
    coll_thrsld = try_command(dos_inp.getfloat,
514 8887f41d Carles
                              [(ValueError, err_msg)],
515 8887f41d Carles
                              'Screening', 'collision_threshold', fallback=1.2)
516 8887f41d Carles
    if coll_thrsld <= 0:
517 8887f41d Carles
        logger.error(err_msg)
518 8887f41d Carles
        raise ValueError(err_msg)
519 8887f41d Carles
520 8887f41d Carles
    return coll_thrsld
521 8887f41d Carles
522 8887f41d Carles
523 8887f41d Carles
def get_screen_rmsd():
524 8887f41d Carles
    err_msg = num_error % ('screen_rmsd', 'positive decimal number')
525 8887f41d Carles
    screen_rmsd = try_command(dos_inp.getfloat,
526 8887f41d Carles
                              [(ValueError, err_msg)],
527 8887f41d Carles
                              'Screening', 'screen_rmsd', fallback=0.05)
528 8887f41d Carles
    if screen_rmsd <= 0:
529 8887f41d Carles
        logger.error(err_msg)
530 8887f41d Carles
        raise ValueError(err_msg)
531 8887f41d Carles
532 8887f41d Carles
    return screen_rmsd
533 8887f41d Carles
534 8887f41d Carles
535 5fb01677 Carles Marti
def get_min_coll_height():
536 5fb01677 Carles Marti
    err_msg = num_error % ('min_coll_height', 'decimal number')
537 5fb01677 Carles Marti
    min_coll_height = dos_inp.get('Screening', 'min_coll_height',
538 5fb01677 Carles Marti
                                  fallback="False")
539 5fb01677 Carles Marti
    if min_coll_height.lower() in turn_false_answers:
540 5fb01677 Carles Marti
        min_coll_height = False
541 8887f41d Carles
    else:
542 5fb01677 Carles Marti
        min_coll_height = try_command(float, [(ValueError, err_msg)],
543 5fb01677 Carles Marti
                                      min_coll_height)
544 8887f41d Carles
545 5fb01677 Carles Marti
    return min_coll_height
546 8887f41d Carles
547 8887f41d Carles
548 b4b2f307 Carles Marti
def get_disso_atoms():
549 b4b2f307 Carles Marti
    disso_atoms_str = dos_inp.get('Screening', 'disso_atoms', fallback="False")
550 b4b2f307 Carles Marti
    disso_atoms = []
551 b4b2f307 Carles Marti
    if disso_atoms_str.lower() in turn_false_answers:
552 b4b2f307 Carles Marti
        pass
553 b4b2f307 Carles Marti
    else:
554 b4b2f307 Carles Marti
        for el in disso_atoms_str.split():
555 b4b2f307 Carles Marti
            try:
556 b4b2f307 Carles Marti
                disso_atoms.append(int(el))
557 b4b2f307 Carles Marti
            except ValueError:
558 b4b2f307 Carles Marti
                disso_atoms.append(el)
559 b4b2f307 Carles Marti
    return disso_atoms
560 a765b11c Carles Marti
561 a765b11c Carles Marti
562 a7128ce1 Carles Marti
# Refinement
563 a7128ce1 Carles Marti
564 a7128ce1 Carles Marti
def get_refine_inp_file():  # TODO if not specified try isol_inp_file.
565 8887f41d Carles
    refine_inp_file = dos_inp.get('Refinement', 'refine_inp_file')
566 8887f41d Carles
    if not os.path.isfile(refine_inp_file):
567 695dcff8 Carles Marti
        logger.error(f'File {refine_inp_file} not found.')
568 8887f41d Carles
        raise FileNotFoundError(f'File {refine_inp_file} not found')
569 8887f41d Carles
570 8887f41d Carles
    return refine_inp_file
571 8887f41d Carles
572 8887f41d Carles
573 8887f41d Carles
def get_energy_cutoff():
574 8887f41d Carles
    err_msg = num_error % ('energy_cutoff', 'positive decimal number')
575 8887f41d Carles
    energy_cutoff = try_command(dos_inp.getfloat,
576 8887f41d Carles
                                [(ValueError, err_msg)],
577 8887f41d Carles
                                'Refinement', 'energy_cutoff', fallback=0.5)
578 8887f41d Carles
    if energy_cutoff < 0:
579 8887f41d Carles
        logger.error(err_msg)
580 8887f41d Carles
        raise ValueError(err_msg)
581 8887f41d Carles
    return energy_cutoff
582 8887f41d Carles
583 8887f41d Carles
584 8887f41d Carles
def read_input(in_file):
585 8887f41d Carles
    err = False
586 8887f41d Carles
    try:
587 8887f41d Carles
        dos_inp.read(in_file)
588 8887f41d Carles
    except MissingSectionHeaderError as e:
589 8887f41d Carles
        logger.error('There are options in the input file without a Section '
590 695dcff8 Carles Marti
                     'header.')
591 8887f41d Carles
        err = e
592 8887f41d Carles
    except DuplicateOptionError as e:
593 8887f41d Carles
        logger.error('There is an option in the input file that has been '
594 0bca5abb Carles Marti
                     'specified more than once.')
595 8887f41d Carles
        err = e
596 8887f41d Carles
    except Exception as e:
597 8887f41d Carles
        err = e
598 8887f41d Carles
    else:
599 8887f41d Carles
        err = False
600 8887f41d Carles
    finally:
601 8887f41d Carles
        if isinstance(err, BaseException):
602 8887f41d Carles
            raise err
603 17e72a49 Carles
604 8887f41d Carles
    return_vars = {}
605 8887f41d Carles
606 8887f41d Carles
    # Global
607 8887f41d Carles
    if not dos_inp.has_section('Global'):
608 8887f41d Carles
        logger.error(no_sect_err % 'Global')
609 8887f41d Carles
        raise NoSectionError('Global')
610 8887f41d Carles
611 8887f41d Carles
    # Mandatory options
612 8887f41d Carles
    # Checks whether the mandatory options 'run_type', 'code', etc. are present.
613 821dca42 Carles Marti
    glob_mand_opts = ['run_type', 'batch_q_sys']
614 7fd58762 Carles
    for opt in glob_mand_opts:
615 8887f41d Carles
        if not dos_inp.has_option('Global', opt):
616 8887f41d Carles
            logger.error(no_opt_err % (opt, 'Global'))
617 8887f41d Carles
            raise NoOptionError(opt, 'Global')
618 8887f41d Carles
619 8887f41d Carles
    # Gets which sections are to be carried out
620 8887f41d Carles
    isolated, screening, refinement = get_run_type()
621 8887f41d Carles
    return_vars['isolated'] = isolated
622 8887f41d Carles
    return_vars['screening'] = screening
623 8887f41d Carles
    return_vars['refinement'] = refinement
624 8887f41d Carles
    return_vars['batch_q_sys'] = get_batch_q_sys()
625 8887f41d Carles
626 99afde40 Carles
    # Dependent options:
627 821dca42 Carles Marti
    return_vars['code'] = get_code()
628 09c3325a Carles Marti
    if return_vars['batch_q_sys']:
629 09c3325a Carles Marti
        return_vars['type_max'] = get_type_max()
630 09c3325a Carles Marti
        return_vars['max_jobs'] = get_max_jobs()
631 09c3325a Carles Marti
        if return_vars['batch_q_sys'] != 'local':
632 09c3325a Carles Marti
            if not dos_inp.has_option('Global', 'subm_script'):
633 09c3325a Carles Marti
                logger.error(no_opt_err % ('subm_script', 'Global'))
634 09c3325a Carles Marti
                raise NoOptionError('subm_script', 'Global')
635 09c3325a Carles Marti
            return_vars['subm_script'] = get_subm_script()
636 09c3325a Carles Marti
        elif return_vars['type_max'] == "p":
637 09c3325a Carles Marti
            err = "'type_max' cannot be set to 'pending'/'queued' when " \
638 09c3325a Carles Marti
                  "'batch_q_sys' is set to 'local' "
639 09c3325a Carles Marti
            logger.error(err)
640 09c3325a Carles Marti
            raise ValueError(err)
641 99afde40 Carles
642 8887f41d Carles
    # Facultative options (Default/Fallback value present)
643 99afde40 Carles
    return_vars['project_name'] = get_project_name()
644 8887f41d Carles
    return_vars['relaunch_err'] = get_relaunch_err()
645 8887f41d Carles
    return_vars['special_atoms'] = get_special_atoms()
646 8887f41d Carles
647 8887f41d Carles
    # Isolated
648 b1d27be5 Carles
    if isolated:
649 b606c71a Carles
        if not dos_inp.has_section('Isolated'):
650 b606c71a Carles
            logger.error(no_sect_err % 'Isolated')
651 b606c71a Carles
            raise NoSectionError('Isolated')
652 8887f41d Carles
        # Mandatory options
653 8887f41d Carles
        # Checks whether the mandatory options are present.
654 95dc2c8e Carles
        iso_mand_opts = ['isol_inp_file', 'molec_file']
655 8887f41d Carles
        for opt in iso_mand_opts:
656 8887f41d Carles
            if not dos_inp.has_option('Isolated', opt):
657 8887f41d Carles
                logger.error(no_opt_err % (opt, 'Isolated'))
658 8887f41d Carles
                raise NoOptionError(opt, 'Isolated')
659 8887f41d Carles
        return_vars['isol_inp_file'] = get_isol_inp_file()
660 821dca42 Carles Marti
        if 'code ' in return_vars:
661 821dca42 Carles Marti
            check_inp_file(return_vars['isol_inp_file'], return_vars['code'])
662 95dc2c8e Carles
        return_vars['molec_file'] = get_molec_file()
663 8887f41d Carles
664 8887f41d Carles
        # Facultative options (Default/Fallback value present)
665 8887f41d Carles
        return_vars['num_conformers'] = get_num_conformers()
666 9fd1daa6 Carles
        # return_vars['num_prom_cand'] = get_num_prom_cand()
667 9fd1daa6 Carles
        # return_vars['iso_rmsd'] = get_iso_rmsd()
668 4670488d Carles Marti
        return_vars['pre_opt'] = get_pre_opt()
669 8887f41d Carles
670 8887f41d Carles
    # Screening
671 b1d27be5 Carles
    if screening:
672 772b40e5 Carles
        if not dos_inp.has_section('Screening'):
673 9f7bb440 Carles
            logger.error(no_sect_err % 'Screening')
674 772b40e5 Carles
            raise NoSectionError('Screening')
675 8887f41d Carles
        # Mandatory options:
676 8887f41d Carles
        # Checks whether the mandatory options are present.
677 a765b11c Carles Marti
        screen_mand_opts = ['screen_inp_file', 'surf_file', 'sites',
678 a765b11c Carles Marti
                            'molec_ads_ctrs', 'molec_neigh_ctrs']
679 8887f41d Carles
        for opt in screen_mand_opts:
680 772b40e5 Carles
            if not dos_inp.has_option('Screening', opt):
681 9f7bb440 Carles
                logger.error(no_opt_err % (opt, 'Screening'))
682 b1d27be5 Carles
                raise NoOptionError(opt, 'Screening')
683 8887f41d Carles
        return_vars['screen_inp_file'] = get_screen_inp_file()
684 a765b11c Carles Marti
        return_vars['surf_file'] = get_surf_file()
685 8887f41d Carles
        return_vars['sites'] = get_sites()
686 8887f41d Carles
        return_vars['molec_ads_ctrs'] = get_molec_ads_ctrs()
687 ab2d90e2 Carles Marti
        return_vars['molec_neigh_ctrs'] = get_molec_neigh_ctrs()
688 ab2d90e2 Carles Marti
        if len(return_vars['molec_ads_ctrs']) != \
689 ab2d90e2 Carles Marti
                len(return_vars['molec_neigh_ctrs']):
690 ab2d90e2 Carles Marti
            err = "'molec_ads_ctrs' and 'molec_neigh_ctrs' must have the same" \
691 ab2d90e2 Carles Marti
                  "number of indides"
692 ab2d90e2 Carles Marti
            logger.error(err)
693 ab2d90e2 Carles Marti
            raise ValueError(err)
694 8887f41d Carles
695 8887f41d Carles
        # Facultative options (Default value present)
696 8af49f6d Carles Marti
        return_vars['select_magns'] = get_select_magns()
697 af2bf62f Carles Marti
        return_vars['confs_per_magn'] = get_confs_per_magn()
698 ab2d90e2 Carles Marti
        return_vars['surf_norm_vect'] = get_surf_norm_vect()
699 b4b2f307 Carles Marti
        return_vars['disso_atoms'] = get_disso_atoms()
700 a7c7b43e Carles Marti
        return_vars['ads_algo'] = get_ads_algo()
701 8887f41d Carles
        return_vars['sample_points_per_angle'] = get_pts_per_angle()
702 7d97341d Carles Marti
        return_vars['max_structures'] = get_max_structures()
703 8887f41d Carles
        return_vars['collision_threshold'] = get_coll_thrsld()
704 5fb01677 Carles Marti
        return_vars['min_coll_height'] = get_min_coll_height()
705 9c16a7e2 Carles Marti
        cart_axes = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0],
706 9c16a7e2 Carles Marti
                     [-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, -1.0]]
707 5fb01677 Carles Marti
        if return_vars['min_coll_height'] is not False and \
708 9c16a7e2 Carles Marti
                return_vars['surf_norm_vect'].tolist() not in cart_axes:
709 5fb01677 Carles Marti
            logger.warning("'min_coll_height' option is only implemented for "
710 695dcff8 Carles Marti
                           "'surf_norm_vect' to be one of the x, y or z axes.")
711 8887f41d Carles
712 8887f41d Carles
    # Refinement
713 b1d27be5 Carles
    if refinement:
714 8887f41d Carles
        if not dos_inp.has_section('Refinement'):
715 8887f41d Carles
            logger.error(no_sect_err % 'Refinement')
716 8887f41d Carles
            raise NoSectionError('Refinement')
717 8887f41d Carles
        # Mandatory options
718 8887f41d Carles
        # Checks whether the mandatory options are present.
719 8887f41d Carles
        ref_mand_opts = ['refine_inp_file']
720 8887f41d Carles
        for opt in ref_mand_opts:
721 8887f41d Carles
            if not dos_inp.has_option('Refinement', opt):
722 8887f41d Carles
                logger.error(no_opt_err % (opt, 'Refinement'))
723 8887f41d Carles
                raise NoOptionError(opt, 'Refinement')
724 8887f41d Carles
        return_vars['refine_inp_file'] = get_refine_inp_file()
725 8887f41d Carles
726 8887f41d Carles
        # Facultative options (Default value present)
727 8887f41d Carles
        return_vars['energy_cutoff'] = get_energy_cutoff()
728 b1d27be5 Carles
        # end energy_cutoff
729 9f7bb440 Carles
730 a5cc42ff Carles Marti
    return_vars_str = "\n\t".join([str(key) + ": " + str(value)
731 a5cc42ff Carles Marti
                                   for key, value in return_vars.items()])
732 d8a6314e Carles
    logger.info(
733 62a87644 Carles Marti
        f'Correctly read {in_file} parameters: \n\n\t{return_vars_str}\n')
734 d8a6314e Carles
735 772b40e5 Carles
    return return_vars
736 8887f41d Carles
737 8887f41d Carles
738 8887f41d Carles
if __name__ == "__main__":
739 8887f41d Carles
    import sys
740 8887f41d Carles
741 8887f41d Carles
    print(read_input(sys.argv[1]))