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

dockonsurf / modules / dos_input.py @ 58ede1f9

Historique | Voir | Annoter | Télécharger (40,81 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 7dd94df7 Carles Marti
get_molec_ctrs: Gets 'molec_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 9cd032cf Carles Marti
    dos_inp.BOOLEAN_STATES[answer] = val  # TODO Check value 0
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 9cd032cf Carles Marti
turn_true_answers = [answer for answer in dos_inp.BOOLEAN_STATES
65 9cd032cf Carles Marti
                     if dos_inp.BOOLEAN_STATES[answer]]
66 8887f41d Carles
67 8887f41d Carles
no_sect_err = "Section '%s' not found on input file"
68 8887f41d Carles
no_opt_err = "Option '%s' not found on section '%s'"
69 8887f41d Carles
num_error = "'%s' value must be a %s"
70 8887f41d Carles
71 4533134f Carles Marti
72 14e0b660 Carles Martí
# Auxilary functions
73 14e0b660 Carles Martí
74 4533134f Carles Marti
def str2lst(cmplx_str, func=int):  # TODO: enable deeper level of nested lists
75 a7128ce1 Carles Marti
    # TODO Treat all-enclosing parenthesis as a list instead of list of lists.
76 b77be9ad Carles
    """Converts a string of integers, and groups of them, to a list.
77 b77be9ad Carles

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

86 4533134f Carles Marti
    @return list, list of integers (or floats), or list of integers (or floats)
87 4533134f Carles Marti
    in the case they were grouped. First, the singlets are placed, and then the
88 4533134f Carles Marti
    groups in input order.
89 b77be9ad Carles

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

144 b77be9ad Carles
    Keyword arguments:
145 b77be9ad Carles
    @param value: The variable to check if its value lies within the expected
146 b77be9ad Carles
    ones
147 b77be9ad Carles
    @param expect_vals: list, list of values allowed for the present option.
148 5f9f1400 Carles Marti
    @param err_msg: The error message to be prompted in both log and screen.
149 b77be9ad Carles
    @raise ValueError: if the value is not among the expected ones.
150 b77be9ad Carles
    @return True if the value is among the expected ones.
151 b77be9ad Carles
    """
152 5f9f1400 Carles Marti
    if err_msg is None:
153 5f9f1400 Carles Marti
        err_msg = f"'{value}' is not an adequate value.\n" \
154 5f9f1400 Carles Marti
                  f"Adequate values: {expect_vals}"
155 821dca42 Carles Marti
    if not any([exp_val == value for exp_val in expect_vals]):
156 5f9f1400 Carles Marti
        logger.error(err_msg)
157 5f9f1400 Carles Marti
        raise ValueError(err_msg)
158 b77be9ad Carles
159 b77be9ad Carles
    return True
160 b77be9ad Carles
161 b77be9ad Carles
162 f349bb54 Carles
def check_inp_files(inp_files, code, potcar_dir):
163 03fab2dd Carles
    if code == 'cp2k':
164 03fab2dd Carles
        from pycp2k import CP2K
165 9d3b680c Carles Martí
        if not isinstance(inp_files, str):
166 9d3b680c Carles Martí
            err_msg = "When using CP2K, only one input file is allowed"
167 9d3b680c Carles Martí
            logger.error(err_msg)
168 9d3b680c Carles Martí
            ValueError(err_msg)
169 9d3b680c Carles Martí
        elif not os.path.isfile(inp_files):
170 9d3b680c Carles Martí
            err_msg = f"Input file {inp_files} was not found."
171 9d3b680c Carles Martí
            logger.error(err_msg)
172 9d3b680c Carles Martí
            raise FileNotFoundError(err_msg)
173 03fab2dd Carles
        cp2k = CP2K()
174 03fab2dd Carles
        try_command(cp2k.parse,
175 9d3b680c Carles Martí
                    [(UnboundLocalError, "Invalid CP2K input file")], inp_files)
176 9d3b680c Carles Martí
    elif code == "vasp":
177 f349bb54 Carles
        if not potcar_dir:
178 f349bb54 Carles
            mand_files = ["INCAR", "KPOINTS", "POTCAR"]
179 f349bb54 Carles
        else:
180 f349bb54 Carles
            mand_files = ["INCAR", "KPOINTS"]
181 f349bb54 Carles
            if any("POTCAR" in inp_file for inp_file in inp_files):
182 f349bb54 Carles
                logger.warning("A POTCAR file was specified as input file "
183 f349bb54 Carles
                               "while the automatic generation of POTCAR was "
184 f349bb54 Carles
                               "also enabled via the 'potcar_dir' keyword. The "
185 f349bb54 Carles
                               "POTCAR specified as input_file will be used "
186 f349bb54 Carles
                               "instead of the auto-generated one.")
187 9d3b680c Carles Martí
        # Check that it inp_files is a list of file paths
188 9d3b680c Carles Martí
        if not isinstance(inp_files, list) and all(isinstance(inp_file, str)
189 9d3b680c Carles Martí
                                                   for inp_file in inp_files):
190 9d3b680c Carles Martí
            err_msg = "'inp_files' should be a list of file names/paths"
191 9d3b680c Carles Martí
            logger.error(err_msg)
192 f349bb54 Carles
            raise ValueError(err_msg)
193 9d3b680c Carles Martí
        # Check that all mandatory files are defined once and just once.
194 9d3b680c Carles Martí
        elif [[mand_file in inp_file for inp_file in inp_files].count(True)
195 9d3b680c Carles Martí
              for mand_file in mand_files].count(1) != len(mand_files):
196 9d3b680c Carles Martí
            err_msg = f"Each of the mandatory files {mand_files} must be " \
197 9d3b680c Carles Martí
                      f"defined once and just once."
198 9d3b680c Carles Martí
            logger.error(err_msg)
199 9d3b680c Carles Martí
            raise FileNotFoundError(err_msg)
200 9d3b680c Carles Martí
        # Check that the defined files exist
201 9d3b680c Carles Martí
        elif any(not os.path.isfile(inp_file) for inp_file in inp_files):
202 9d3b680c Carles Martí
            err_msg = f"At least one of the mandatory files {mand_files} was " \
203 9d3b680c Carles Martí
                      "not found."
204 9d3b680c Carles Martí
            logger.error(err_msg)
205 9d3b680c Carles Martí
            raise FileNotFoundError(err_msg)
206 9d3b680c Carles Martí
        # Check that mandatory files are actual vasp files.
207 9d3b680c Carles Martí
        else:
208 9d3b680c Carles Martí
            from pymatgen.io.vasp.inputs import Incar, Kpoints, Potcar
209 9d3b680c Carles Martí
            for inp_file in inp_files:
210 9d3b680c Carles Martí
                file_name = inp_file.split("/")[-1]
211 9d3b680c Carles Martí
                if not any(mand_file in file_name for mand_file in mand_files):
212 9d3b680c Carles Martí
                    continue
213 9d3b680c Carles Martí
                file_type = ""
214 9d3b680c Carles Martí
                for mand_file in mand_files:
215 9d3b680c Carles Martí
                    if mand_file in inp_file:
216 9d3b680c Carles Martí
                        file_type = mand_file
217 9d3b680c Carles Martí
                err = False
218 9d3b680c Carles Martí
                err_msg = f"'{inp_file}' is not a valid {file_name} file."
219 9d3b680c Carles Martí
                try:
220 9d3b680c Carles Martí
                    eval(file_type.capitalize()).from_file(inp_file)
221 9d3b680c Carles Martí
                except ValueError:
222 9d3b680c Carles Martí
                    logger.error(err_msg)
223 9d3b680c Carles Martí
                    err = ValueError(err_msg)
224 9d3b680c Carles Martí
                except IndexError:
225 9d3b680c Carles Martí
                    logger.error(err_msg)
226 9d3b680c Carles Martí
                    err = IndexError(err_msg)
227 9d3b680c Carles Martí
                else:
228 9d3b680c Carles Martí
                    if file_name == "INCAR":
229 9d3b680c Carles Martí
                        Incar.from_file("INCAR").check_params()
230 9d3b680c Carles Martí
                finally:
231 9d3b680c Carles Martí
                    if isinstance(err, BaseException):
232 9d3b680c Carles Martí
                        raise err
233 03fab2dd Carles
234 03fab2dd Carles
235 a7128ce1 Carles Marti
# Global
236 a7128ce1 Carles Marti
237 8887f41d Carles
def get_run_type():
238 b1d27be5 Carles
    isolated, screening, refinement = (False, False, False)
239 5cc4994b Carles
    run_type_vals = ['isolated', 'screening', 'refinement', 'adsorption',
240 5cc4994b Carles
                     'full']
241 081548a0 Carles Marti
    run_types = dos_inp.get('Global', 'run_type').split()
242 081548a0 Carles Marti
    for run_type in run_types:
243 081548a0 Carles Marti
        check_expect_val(run_type.lower(), run_type_vals)
244 081548a0 Carles Marti
        if 'isol' in run_type.lower():
245 081548a0 Carles Marti
            isolated = True
246 081548a0 Carles Marti
        if 'screen' in run_type.lower():
247 081548a0 Carles Marti
            screening = True
248 081548a0 Carles Marti
        if 'refine' in run_type.lower():
249 081548a0 Carles Marti
            refinement = True
250 081548a0 Carles Marti
        if 'adsor' in run_type.lower():
251 081548a0 Carles Marti
            screening, refinement = (True, True)
252 081548a0 Carles Marti
        if 'full' in run_type.lower():
253 081548a0 Carles Marti
            isolated, screening, refinement = (True, True, True)
254 5cc4994b Carles
255 8887f41d Carles
    return isolated, screening, refinement
256 17e72a49 Carles
257 8887f41d Carles
258 8887f41d Carles
def get_code():
259 9d3b680c Carles Martí
    code_vals = ['cp2k', 'vasp']
260 5cc4994b Carles
    check_expect_val(dos_inp.get('Global', 'code').lower(), code_vals)
261 5cc4994b Carles
    code = dos_inp.get('Global', 'code').lower()
262 8887f41d Carles
    return code
263 8887f41d Carles
264 17e72a49 Carles
265 8887f41d Carles
def get_batch_q_sys():
266 ec5bba46 Carles Marti
    batch_q_sys_vals = ['sge', 'lsf', 'irene', 'local'] + turn_false_answers
267 5cc4994b Carles
    check_expect_val(dos_inp.get('Global', 'batch_q_sys').lower(),
268 5cc4994b Carles
                     batch_q_sys_vals)
269 5cc4994b Carles
    batch_q_sys = dos_inp.get('Global', 'batch_q_sys').lower()
270 821dca42 Carles Marti
    if batch_q_sys.lower() in turn_false_answers:
271 821dca42 Carles Marti
        return False
272 821dca42 Carles Marti
    else:
273 821dca42 Carles Marti
        return batch_q_sys
274 17e72a49 Carles
275 8887f41d Carles
276 9d3b680c Carles Martí
def get_pbc_cell():
277 a5d76bfb Carles Martí
    from ase.atoms import Cell
278 9d3b680c Carles Martí
    err_msg = "'pbc_cell' must be either 3 vectors of size 3 or False."
279 9d3b680c Carles Martí
    pbc_cell_str = dos_inp.get('Global', 'pbc_cell', fallback="False")
280 9d3b680c Carles Martí
    if pbc_cell_str.lower() in turn_false_answers:
281 9d3b680c Carles Martí
        return False
282 9d3b680c Carles Martí
    else:
283 9d3b680c Carles Martí
        pbc_cell = np.array(try_command(str2lst, [(ValueError, err_msg)],
284 9d3b680c Carles Martí
                                        pbc_cell_str, float))
285 9d3b680c Carles Martí
        if pbc_cell.shape != (3, 3):
286 9d3b680c Carles Martí
            logger.error(err_msg)
287 9d3b680c Carles Martí
            raise ValueError(err_msg)
288 9d3b680c Carles Martí
        if np.linalg.det(pbc_cell) == 0.0:
289 9d3b680c Carles Martí
            err_msg = "The volume of the defined cell is 0"
290 9d3b680c Carles Martí
            logger.error(err_msg)
291 9d3b680c Carles Martí
            raise ValueError(err_msg)
292 a5d76bfb Carles Martí
        return Cell(pbc_cell)
293 9d3b680c Carles Martí
294 9d3b680c Carles Martí
295 99afde40 Carles
def get_subm_script():
296 14e0b660 Carles Martí
    subm_script = dos_inp.get('Global', 'subm_script')
297 14e0b660 Carles Martí
    if not os.path.isfile(subm_script):
298 695dcff8 Carles Marti
        logger.error(f'File {subm_script} not found.')
299 99afde40 Carles
        raise FileNotFoundError(f'File {subm_script} not found')
300 99afde40 Carles
    return subm_script
301 99afde40 Carles
302 99afde40 Carles
303 99afde40 Carles
def get_project_name():
304 bd573212 Carles
    project_name = dos_inp.get('Global', 'project_name', fallback='')
305 99afde40 Carles
    return project_name
306 99afde40 Carles
307 99afde40 Carles
308 8887f41d Carles
def get_relaunch_err():
309 09c3325a Carles Marti
    relaunch_err_vals = ['geo_not_conv']
310 b1d27be5 Carles
    relaunch_err = dos_inp.get('Global', 'relaunch_err',
311 17e72a49 Carles
                               fallback="False")
312 b1d27be5 Carles
    if relaunch_err.lower() in turn_false_answers:
313 8887f41d Carles
        return False
314 79e3db42 Carles
    else:
315 79e3db42 Carles
        check_expect_val(relaunch_err.lower(), relaunch_err_vals)
316 8887f41d Carles
    return relaunch_err
317 8887f41d Carles
318 8887f41d Carles
319 09c3325a Carles Marti
def get_max_jobs():
320 b6b1b03e Carles Marti
    import re
321 b6b1b03e Carles Marti
    err_msg = "'max_jobs' must be a list of, number plus 'p', 'q' or 'r', or " \
322 b6b1b03e Carles Marti
              "a combination of them without repeating letters.\n" \
323 b6b1b03e Carles Marti
              "eg: '2r 3p 4pr', '5q' or '3r 3p'"
324 b6b1b03e Carles Marti
    max_jobs_str = dos_inp.get('Global', 'max_jobs', fallback="inf").lower()
325 b6b1b03e Carles Marti
    str_vals = ["r", "p", "q", "rp", "rq", "pr", "qr"]
326 b6b1b03e Carles Marti
    max_jobs = {"r": np.inf, "p": np.inf, "rp": np.inf}
327 b6b1b03e Carles Marti
    if "inf" == max_jobs_str:
328 b6b1b03e Carles Marti
        return {"r": np.inf, "p": np.inf, "rp": np.inf}
329 b6b1b03e Carles Marti
    # Iterate over the number of requirements:
330 b6b1b03e Carles Marti
    for req in max_jobs_str.split():
331 b6b1b03e Carles Marti
        # Split numbers from letters into a list
332 b6b1b03e Carles Marti
        req_parts = re.findall(r'[a-z]+|\d+', req)
333 b6b1b03e Carles Marti
        if len(req_parts) != 2:
334 b6b1b03e Carles Marti
            logger.error(err_msg)
335 b6b1b03e Carles Marti
            raise ValueError(err_msg)
336 b6b1b03e Carles Marti
        if req_parts[0].isdecimal():
337 b6b1b03e Carles Marti
            req_parts[1] = req_parts[1].replace('q', 'p').replace('pr', 'rp')
338 b6b1b03e Carles Marti
            if req_parts[1] in str_vals and max_jobs[req_parts[1]] == np.inf:
339 b6b1b03e Carles Marti
                max_jobs[req_parts[1]] = int(req_parts[0])
340 b6b1b03e Carles Marti
        elif req_parts[1].isdecimal():
341 b6b1b03e Carles Marti
            req_parts[0] = req_parts[0].replace('q', 'p').replace('pr', 'rp')
342 b6b1b03e Carles Marti
            if req_parts[0] in str_vals and max_jobs[req_parts[0]] == np.inf:
343 b6b1b03e Carles Marti
                max_jobs[req_parts[0]] = int(req_parts[1])
344 b6b1b03e Carles Marti
        else:
345 b6b1b03e Carles Marti
            logger.error(err_msg)
346 b6b1b03e Carles Marti
            raise ValueError(err_msg)
347 09c3325a Carles Marti
348 09c3325a Carles Marti
    return max_jobs
349 09c3325a Carles Marti
350 09c3325a Carles Marti
351 8887f41d Carles
def get_special_atoms():
352 8887f41d Carles
    from ase.data import chemical_symbols
353 17e72a49 Carles
354 5cc4994b Carles
    spec_at_err = '\'special_atoms\' does not have an adequate format.\n' \
355 5cc4994b Carles
                  'Adequate format: (Fe1 Fe) (O1 O)'
356 5cc4994b Carles
    special_atoms = dos_inp.get('Global', 'special_atoms', fallback="False")
357 b1d27be5 Carles
    if special_atoms.lower() in turn_false_answers:
358 90819cc3 Carles Marti
        special_atoms = []
359 b1d27be5 Carles
    else:
360 8949edd0 Carles
        # Converts the string into a list of tuples
361 8949edd0 Carles
        lst_tple = [tuple(pair.replace("(", "").split()) for pair in
362 b1d27be5 Carles
                    special_atoms.split(")")[:-1]]
363 5cc4994b Carles
        if len(lst_tple) == 0:
364 9f7bb440 Carles
            logger.error(spec_at_err)
365 5cc4994b Carles
            raise ValueError(spec_at_err)
366 9f7bb440 Carles
        for i, tup in enumerate(lst_tple):
367 90819cc3 Carles Marti
            if not isinstance(tup, tuple) or len(tup) != 2:
368 9f7bb440 Carles
                logger.error(spec_at_err)
369 5cc4994b Carles
                raise ValueError(spec_at_err)
370 5cc4994b Carles
            if tup[1].capitalize() not in chemical_symbols:
371 5cc4994b Carles
                elem_err = "The second element of the couple should be an " \
372 9f7bb440 Carles
                           "actual element of the periodic table"
373 9f7bb440 Carles
                logger.error(elem_err)
374 5cc4994b Carles
                raise ValueError(elem_err)
375 9f7bb440 Carles
            if tup[0].capitalize() in chemical_symbols:
376 9f7bb440 Carles
                elem_err = "The first element of the couple is already an " \
377 9f7bb440 Carles
                           "actual element of the periodic table, "
378 9f7bb440 Carles
                logger.error(elem_err)
379 9f7bb440 Carles
                raise ValueError(elem_err)
380 9f7bb440 Carles
            for j, tup2 in enumerate(lst_tple):
381 9f7bb440 Carles
                if j <= i:
382 9f7bb440 Carles
                    continue
383 9f7bb440 Carles
                if tup2[0] == tup[0]:
384 9f7bb440 Carles
                    label_err = f'You have specified the label {tup[0]} to ' \
385 8887f41d Carles
                                f'more than one special atom'
386 9f7bb440 Carles
                    logger.error(label_err)
387 9f7bb440 Carles
                    raise ValueError(label_err)
388 b1d27be5 Carles
        special_atoms = lst_tple
389 8887f41d Carles
    return special_atoms
390 8887f41d Carles
391 8887f41d Carles
392 f349bb54 Carles
def get_potcar_dir():
393 f349bb54 Carles
    potcar_dir = dos_inp.get('Global', 'potcar_dir', fallback="False")
394 f349bb54 Carles
    if potcar_dir.lower() in turn_false_answers:
395 f349bb54 Carles
        return False
396 f349bb54 Carles
    elif not os.path.isdir(potcar_dir):
397 f349bb54 Carles
        err_msg = "'potcar_dir' must be either False or a directory."
398 f349bb54 Carles
        logger.error(err_msg)
399 f349bb54 Carles
        raise ValueError(err_msg)
400 f349bb54 Carles
    else:
401 f349bb54 Carles
        return potcar_dir
402 f349bb54 Carles
403 f349bb54 Carles
404 a7128ce1 Carles Marti
# Isolated
405 a7128ce1 Carles Marti
406 f349bb54 Carles
def get_isol_inp_file(code, potcar_dir):  # TODO allow spaces in path names
407 9d3b680c Carles Martí
    inp_file_lst = dos_inp.get('Isolated', 'isol_inp_file').split()
408 f349bb54 Carles
    check_inp_files(inp_file_lst[0] if len(inp_file_lst) == 1 else inp_file_lst,
409 f349bb54 Carles
                    code, potcar_dir)
410 9d3b680c Carles Martí
    return inp_file_lst[0] if len(inp_file_lst) == 1 else inp_file_lst
411 8887f41d Carles
412 8887f41d Carles
413 95dc2c8e Carles
def get_molec_file():
414 95dc2c8e Carles
    molec_file = dos_inp.get('Isolated', 'molec_file')
415 95dc2c8e Carles
    if not os.path.isfile(molec_file):
416 695dcff8 Carles Marti
        logger.error(f'File {molec_file} not found.')
417 95dc2c8e Carles
        raise FileNotFoundError(f'File {molec_file} not found')
418 95dc2c8e Carles
    return molec_file
419 95dc2c8e Carles
420 95dc2c8e Carles
421 8887f41d Carles
def get_num_conformers():
422 8887f41d Carles
    err_msg = num_error % ('num_conformers', 'positive integer')
423 8887f41d Carles
    num_conformers = try_command(dos_inp.getint, [(ValueError, err_msg)],
424 8887f41d Carles
                                 'Isolated', 'num_conformers', fallback=100)
425 8887f41d Carles
    if num_conformers < 1:
426 8887f41d Carles
        logger.error(err_msg)
427 8887f41d Carles
        raise ValueError(err_msg)
428 8887f41d Carles
    return num_conformers
429 8887f41d Carles
430 8887f41d Carles
431 4670488d Carles Marti
def get_pre_opt():
432 4670488d Carles Marti
    pre_opt_vals = ['uff', 'mmff'] + turn_false_answers
433 4670488d Carles Marti
    check_expect_val(dos_inp.get('Isolated', 'pre_opt').lower(), pre_opt_vals)
434 ad57fd42 Carles Marti
    pre_opt = dos_inp.get('Isolated', 'pre_opt').lower()
435 ad57fd42 Carles Marti
    if pre_opt in turn_false_answers:
436 4670488d Carles Marti
        return False
437 4670488d Carles Marti
    else:
438 4670488d Carles Marti
        return pre_opt
439 b1f6e69d Carles
440 70fa4e74 Carles Marti
441 a7128ce1 Carles Marti
# Screening
442 a7128ce1 Carles Marti
443 f349bb54 Carles
def get_screen_inp_file(code, potcar_dir):  # TODO allow spaces in path names
444 9d3b680c Carles Martí
    inp_file_lst = dos_inp.get('Screening', 'screen_inp_file').split()
445 f349bb54 Carles
    check_inp_files(inp_file_lst[0] if len(inp_file_lst) == 1 else inp_file_lst,
446 f349bb54 Carles
                    code, potcar_dir)
447 9d3b680c Carles Martí
    return inp_file_lst[0] if len(inp_file_lst) == 1 else inp_file_lst
448 8887f41d Carles
449 8887f41d Carles
450 a765b11c Carles Marti
def get_surf_file():
451 a765b11c Carles Marti
    surf_file = dos_inp.get('Screening', 'surf_file')
452 a765b11c Carles Marti
    if not os.path.isfile(surf_file):
453 695dcff8 Carles Marti
        logger.error(f'File {surf_file} not found.')
454 a765b11c Carles Marti
        raise FileNotFoundError(f'File {surf_file} not found')
455 a765b11c Carles Marti
    return surf_file
456 a765b11c Carles Marti
457 a765b11c Carles Marti
458 8887f41d Carles
def get_sites():
459 8887f41d Carles
    err_msg = 'The value of sites should be a list of atom numbers ' \
460 8887f41d Carles
              '(ie. positive integers) or groups of atom numbers ' \
461 8887f41d Carles
              'grouped by parentheses-like enclosers. \n' \
462 8887f41d Carles
              'eg. 128,(135 138;141) 87 {45, 68}'
463 8887f41d Carles
    # Convert the string into a list of lists
464 8887f41d Carles
    sites = try_command(str2lst,
465 8887f41d Carles
                        [(ValueError, err_msg), (AttributeError, err_msg)],
466 8887f41d Carles
                        dos_inp.get('Screening', 'sites'))
467 8887f41d Carles
    # Check all elements of the list (of lists) are positive integers
468 8887f41d Carles
    for site in sites:
469 8887f41d Carles
        if type(site) is list:
470 8887f41d Carles
            for atom in site:
471 8887f41d Carles
                if atom < 0:
472 8887f41d Carles
                    logger.error(err_msg)
473 8887f41d Carles
                    raise ValueError(err_msg)
474 8887f41d Carles
        elif type(site) is int:
475 8887f41d Carles
            if site < 0:
476 8887f41d Carles
                logger.error(err_msg)
477 8887f41d Carles
                raise ValueError(err_msg)
478 8887f41d Carles
        else:
479 8887f41d Carles
            logger.error(err_msg)
480 8887f41d Carles
            raise ValueError(err_msg)
481 8887f41d Carles
482 8887f41d Carles
    return sites
483 8887f41d Carles
484 8887f41d Carles
485 7dd94df7 Carles Marti
def get_surf_ctrs2():
486 7dd94df7 Carles Marti
    err_msg = 'The value of surf_ctrs2 should be a list of atom numbers ' \
487 7dd94df7 Carles Marti
              '(ie. positive integers) or groups of atom numbers ' \
488 7dd94df7 Carles Marti
              'grouped by parentheses-like enclosers. \n' \
489 7dd94df7 Carles Marti
              'eg. 128,(135 138;141) 87 {45, 68}'
490 7dd94df7 Carles Marti
    # Convert the string into a list of lists
491 7dd94df7 Carles Marti
    surf_ctrs2 = try_command(str2lst,
492 7dd94df7 Carles Marti
                             [(ValueError, err_msg), (AttributeError, err_msg)],
493 7dd94df7 Carles Marti
                             dos_inp.get('Screening', 'surf_ctrs2'))
494 7dd94df7 Carles Marti
    # Check all elements of the list (of lists) are positive integers
495 7dd94df7 Carles Marti
    for ctr in surf_ctrs2:
496 7dd94df7 Carles Marti
        if type(ctr) is list:
497 7dd94df7 Carles Marti
            for atom in ctr:
498 7dd94df7 Carles Marti
                if atom < 0:
499 7dd94df7 Carles Marti
                    logger.error(err_msg)
500 7dd94df7 Carles Marti
                    raise ValueError(err_msg)
501 7dd94df7 Carles Marti
        elif type(ctr) is int:
502 7dd94df7 Carles Marti
            if ctr < 0:
503 7dd94df7 Carles Marti
                logger.error(err_msg)
504 7dd94df7 Carles Marti
                raise ValueError(err_msg)
505 7dd94df7 Carles Marti
        else:
506 7dd94df7 Carles Marti
            logger.error(err_msg)
507 7dd94df7 Carles Marti
            raise ValueError(err_msg)
508 7dd94df7 Carles Marti
509 7dd94df7 Carles Marti
    return surf_ctrs2
510 7dd94df7 Carles Marti
511 7dd94df7 Carles Marti
512 7dd94df7 Carles Marti
def get_molec_ctrs():
513 7dd94df7 Carles Marti
    err_msg = 'The value of molec_ctrs should be a list of atom' \
514 8887f41d Carles
              ' numbers (ie. positive integers) or groups of atom ' \
515 8887f41d Carles
              'numbers enclosed by parentheses-like characters. \n' \
516 8887f41d Carles
              'eg. 128,(135 138;141) 87 {45, 68}'
517 8887f41d Carles
    # Convert the string into a list of lists
518 7dd94df7 Carles Marti
    molec_ctrs = try_command(str2lst,
519 7dd94df7 Carles Marti
                             [(ValueError, err_msg),
520 7dd94df7 Carles Marti
                              (AttributeError, err_msg)],
521 7dd94df7 Carles Marti
                             dos_inp.get('Screening', 'molec_ctrs'))
522 7dd94df7 Carles Marti
    # Check all elements of the list (of lists) are positive integers
523 7dd94df7 Carles Marti
    for ctr in molec_ctrs:
524 7dd94df7 Carles Marti
        if isinstance(ctr, list):
525 7dd94df7 Carles Marti
            for atom in ctr:
526 7dd94df7 Carles Marti
                if atom < 0:
527 7dd94df7 Carles Marti
                    logger.error(err_msg)
528 7dd94df7 Carles Marti
                    raise ValueError(err_msg)
529 7dd94df7 Carles Marti
        elif isinstance(ctr, int):
530 7dd94df7 Carles Marti
            if ctr < 0:
531 7dd94df7 Carles Marti
                logger.error(err_msg)
532 7dd94df7 Carles Marti
                raise ValueError(err_msg)
533 7dd94df7 Carles Marti
        else:
534 7dd94df7 Carles Marti
            logger.error(err_msg)
535 7dd94df7 Carles Marti
            raise ValueError(err_msg)
536 7dd94df7 Carles Marti
537 7dd94df7 Carles Marti
    return molec_ctrs
538 7dd94df7 Carles Marti
539 7dd94df7 Carles Marti
540 7dd94df7 Carles Marti
def get_molec_ctrs2():
541 7dd94df7 Carles Marti
    err_msg = 'The value of molec_ctrs2 should be a list of atom ' \
542 7dd94df7 Carles Marti
              'numbers (ie. positive integers) or groups of atom ' \
543 7dd94df7 Carles Marti
              'numbers enclosed by parentheses-like characters. \n' \
544 7dd94df7 Carles Marti
              'eg. 128,(135 138;141) 87 {45, 68}'
545 7dd94df7 Carles Marti
    # Convert the string into a list of lists
546 7dd94df7 Carles Marti
    molec_ctrs2 = try_command(str2lst, [(ValueError, err_msg),
547 7dd94df7 Carles Marti
                                        (AttributeError, err_msg)],
548 7dd94df7 Carles Marti
                              dos_inp.get('Screening', 'molec_ctrs2'))
549 7dd94df7 Carles Marti
550 8887f41d Carles
    # Check all elements of the list (of lists) are positive integers
551 7dd94df7 Carles Marti
    for ctr in molec_ctrs2:
552 c845c6f2 Carles Marti
        if isinstance(ctr, list):
553 8887f41d Carles
            for atom in ctr:
554 8887f41d Carles
                if atom < 0:
555 8887f41d Carles
                    logger.error(err_msg)
556 8887f41d Carles
                    raise ValueError(err_msg)
557 c845c6f2 Carles Marti
        elif isinstance(ctr, int):
558 8887f41d Carles
            if ctr < 0:
559 8887f41d Carles
                logger.error(err_msg)
560 8887f41d Carles
                raise ValueError(err_msg)
561 8887f41d Carles
        else:
562 8887f41d Carles
            logger.error(err_msg)
563 8887f41d Carles
            raise ValueError(err_msg)
564 8887f41d Carles
565 7dd94df7 Carles Marti
    return molec_ctrs2
566 8887f41d Carles
567 8887f41d Carles
568 7dd94df7 Carles Marti
def get_molec_ctrs3():
569 7dd94df7 Carles Marti
    err_msg = 'The value of molec_ctrs3 should be a list of atom ' \
570 c845c6f2 Carles Marti
              'numbers (ie. positive integers) or groups of atom ' \
571 c845c6f2 Carles Marti
              'numbers enclosed by parentheses-like characters. \n' \
572 c845c6f2 Carles Marti
              'eg. 128,(135 138;141) 87 {45, 68}'
573 c845c6f2 Carles Marti
    # Convert the string into a list of lists
574 7dd94df7 Carles Marti
    molec_ctrs3 = try_command(str2lst, [(ValueError, err_msg),
575 7dd94df7 Carles Marti
                                        (AttributeError, err_msg)],
576 7dd94df7 Carles Marti
                              dos_inp.get('Screening', 'molec_ctrs3'))
577 c845c6f2 Carles Marti
578 c845c6f2 Carles Marti
    # Check all elements of the list (of lists) are positive integers
579 7dd94df7 Carles Marti
    for ctr in molec_ctrs3:
580 c845c6f2 Carles Marti
        if isinstance(ctr, list):
581 c845c6f2 Carles Marti
            for atom in ctr:
582 c845c6f2 Carles Marti
                if atom < 0:
583 c845c6f2 Carles Marti
                    logger.error(err_msg)
584 c845c6f2 Carles Marti
                    raise ValueError(err_msg)
585 c845c6f2 Carles Marti
        elif isinstance(ctr, int):
586 c845c6f2 Carles Marti
            if ctr < 0:
587 c845c6f2 Carles Marti
                logger.error(err_msg)
588 c845c6f2 Carles Marti
                raise ValueError(err_msg)
589 c845c6f2 Carles Marti
        else:
590 c845c6f2 Carles Marti
            logger.error(err_msg)
591 c845c6f2 Carles Marti
            raise ValueError(err_msg)
592 c845c6f2 Carles Marti
593 7dd94df7 Carles Marti
    return molec_ctrs3
594 c845c6f2 Carles Marti
595 c845c6f2 Carles Marti
596 a98d4172 Carles Marti
def get_max_helic_angle():
597 a98d4172 Carles Marti
    err_msg = "'max_helic_angle' must be a positive number in degrees"
598 a98d4172 Carles Marti
    max_helic_angle = try_command(dos_inp.getfloat, [(ValueError, err_msg)],
599 a98d4172 Carles Marti
                                  'Screening', 'max_helic_angle',
600 a98d4172 Carles Marti
                                  fallback=180.0)
601 a98d4172 Carles Marti
    if max_helic_angle < 0:
602 a98d4172 Carles Marti
        logger.error(err_msg)
603 a98d4172 Carles Marti
        raise ValueError(err_msg)
604 a98d4172 Carles Marti
605 a98d4172 Carles Marti
    return max_helic_angle
606 a98d4172 Carles Marti
607 a98d4172 Carles Marti
608 8af49f6d Carles Marti
def get_select_magns():
609 8af49f6d Carles Marti
    select_magns_vals = ['energy', 'moi']
610 8af49f6d Carles Marti
    select_magns_str = dos_inp.get('Screening', 'select_magns',
611 14e0b660 Carles Martí
                                   fallback='moi')
612 8af49f6d Carles Marti
    select_magns_str.replace(',', ' ').replace(';', ' ')
613 8af49f6d Carles Marti
    select_magns = select_magns_str.split(' ')
614 8af49f6d Carles Marti
    select_magns = [m.lower() for m in select_magns]
615 8af49f6d Carles Marti
    for m in select_magns:
616 8af49f6d Carles Marti
        check_expect_val(m, select_magns_vals)
617 8af49f6d Carles Marti
    return select_magns
618 8af49f6d Carles Marti
619 8af49f6d Carles Marti
620 af2bf62f Carles Marti
def get_confs_per_magn():
621 af2bf62f Carles Marti
    err_msg = num_error % ('confs_per_magn', 'positive integer')
622 af2bf62f Carles Marti
    confs_per_magn = try_command(dos_inp.getint, [(ValueError, err_msg)],
623 af2bf62f Carles Marti
                                 'Screening', 'confs_per_magn', fallback=2)
624 af2bf62f Carles Marti
    if confs_per_magn <= 0:
625 af2bf62f Carles Marti
        logger.error(err_msg)
626 af2bf62f Carles Marti
        raise ValueError(err_msg)
627 af2bf62f Carles Marti
    return confs_per_magn
628 af2bf62f Carles Marti
629 af2bf62f Carles Marti
630 c845c6f2 Carles Marti
def get_surf_norm_vect():
631 d6da8693 Carles Marti
    err = "'surf_norm_vect' must be a 3 component vector, 'x', 'y' or 'z', " \
632 d6da8693 Carles Marti
          "'auto' or 'asann'."
633 9c16a7e2 Carles Marti
    cart_axes = {'x': [1.0, 0.0, 0.0], '-x': [-1.0, 0.0, 0.0],
634 9c16a7e2 Carles Marti
                 'y': [0.0, 1.0, 0.0], '-y': [0.0, -1.0, 0.0],
635 9c16a7e2 Carles Marti
                 'z': [0.0, 0.0, 1.0], '-z': [0.0, 0.0, -1.0]}
636 c845c6f2 Carles Marti
    surf_norm_vect_str = dos_inp.get('Screening', 'surf_norm_vect',
637 d6da8693 Carles Marti
                                     fallback="auto").lower()
638 d6da8693 Carles Marti
    if surf_norm_vect_str == "asann" or surf_norm_vect_str == "auto":
639 d6da8693 Carles Marti
        return 'auto'
640 9c16a7e2 Carles Marti
    if surf_norm_vect_str in cart_axes:
641 d6da8693 Carles Marti
        return np.array(cart_axes[surf_norm_vect_str])
642 d6da8693 Carles Marti
    surf_norm_vect = try_command(str2lst, [(ValueError, err)],
643 d6da8693 Carles Marti
                                 surf_norm_vect_str, float)
644 d6da8693 Carles Marti
    if len(surf_norm_vect) != 3:
645 d6da8693 Carles Marti
        logger.error(err)
646 d6da8693 Carles Marti
        raise ValueError(err)
647 3d56e566 Carles Marti
648 b6b1b03e Carles Marti
    return np.array(surf_norm_vect) / np.linalg.norm(surf_norm_vect)
649 c845c6f2 Carles Marti
650 c845c6f2 Carles Marti
651 fe91ddb2 Carles Marti
def get_adsorption_height():
652 fe91ddb2 Carles Marti
    err_msg = num_error % ('adsorption_height', 'positive number')
653 fe91ddb2 Carles Marti
    ads_height = try_command(dos_inp.getfloat, [(ValueError, err_msg)],
654 fe91ddb2 Carles Marti
                             'Screening', 'adsorption_height', fallback=2.5)
655 fe91ddb2 Carles Marti
    if ads_height <= 0:
656 fe91ddb2 Carles Marti
        logger.error(err_msg)
657 fe91ddb2 Carles Marti
        raise ValueError(err_msg)
658 fe91ddb2 Carles Marti
    return ads_height
659 fe91ddb2 Carles Marti
660 fe91ddb2 Carles Marti
661 7dd94df7 Carles Marti
def get_set_angles():
662 609104e3 Carles Marti
    set_vals = ['euler', 'internal']
663 7dd94df7 Carles Marti
    check_expect_val(dos_inp.get('Screening', 'set_angles').lower(), set_vals)
664 5261a07f Carles Marti
    set_angles = dos_inp.get('Screening', 'set_angles',
665 5261a07f Carles Marti
                             fallback='euler').lower()
666 7dd94df7 Carles Marti
    return set_angles
667 a7c7b43e Carles Marti
668 a7c7b43e Carles Marti
669 8887f41d Carles
def get_pts_per_angle():
670 d4bedc18 Carles Marti
    err_msg = num_error % ('sample_points_per_angle', 'positive integer')
671 8887f41d Carles
    pts_per_angle = try_command(dos_inp.getint,
672 8887f41d Carles
                                [(ValueError, err_msg)],
673 8887f41d Carles
                                'Screening', 'sample_points_per_angle',
674 8887f41d Carles
                                fallback=3)
675 d4bedc18 Carles Marti
    if pts_per_angle <= 0:
676 d4bedc18 Carles Marti
        logger.error(err_msg)
677 d4bedc18 Carles Marti
        raise ValueError(err_msg)
678 8887f41d Carles
    return pts_per_angle
679 8887f41d Carles
680 8887f41d Carles
681 7d97341d Carles Marti
def get_max_structures():
682 7d97341d Carles Marti
    err_msg = num_error % ('max_structures', 'positive integer')
683 21209d96 Carles Marti
    max_structs = dos_inp.get('Screening', 'max_structures', fallback="False")
684 21209d96 Carles Marti
    if max_structs.lower() in turn_false_answers:
685 21209d96 Carles Marti
        return np.inf
686 21209d96 Carles Marti
    if try_command(int, [(ValueError, err_msg)], max_structs) <= 0:
687 7d97341d Carles Marti
        logger.error(err_msg)
688 7d97341d Carles Marti
        raise ValueError(err_msg)
689 21209d96 Carles Marti
    return int(max_structs)
690 7d97341d Carles Marti
691 7d97341d Carles Marti
692 8887f41d Carles
def get_coll_thrsld():
693 fe91ddb2 Carles Marti
    err_msg = num_error % ('collision_threshold', 'positive number')
694 eef995b8 Carles Marti
    coll_thrsld_str = dos_inp.get('Screening', 'collision_threshold',
695 a44ad3c2 Carles Martí
                                  fallback="False")
696 eef995b8 Carles Marti
    if coll_thrsld_str.lower() in turn_false_answers:
697 eef995b8 Carles Marti
        return False
698 eef995b8 Carles Marti
    coll_thrsld = try_command(float, [(ValueError, err_msg)], coll_thrsld_str)
699 8887f41d Carles
700 8887f41d Carles
    if coll_thrsld <= 0:
701 8887f41d Carles
        logger.error(err_msg)
702 8887f41d Carles
        raise ValueError(err_msg)
703 8887f41d Carles
704 8887f41d Carles
    return coll_thrsld
705 8887f41d Carles
706 8887f41d Carles
707 f98ba5b0 Carles Marti
def get_min_coll_height(norm_vect):
708 5fb01677 Carles Marti
    err_msg = num_error % ('min_coll_height', 'decimal number')
709 5fb01677 Carles Marti
    min_coll_height = dos_inp.get('Screening', 'min_coll_height',
710 5fb01677 Carles Marti
                                  fallback="False")
711 5fb01677 Carles Marti
    if min_coll_height.lower() in turn_false_answers:
712 f98ba5b0 Carles Marti
        return False
713 f98ba5b0 Carles Marti
    min_coll_height = try_command(float, [(ValueError, err_msg)],
714 f98ba5b0 Carles Marti
                                  min_coll_height)
715 f98ba5b0 Carles Marti
    cart_axes = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0],
716 f98ba5b0 Carles Marti
                 [-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, -1.0]]
717 f98ba5b0 Carles Marti
    err_msg = "'min_coll_height' option is only implemented for " \
718 f98ba5b0 Carles Marti
              "'surf_norm_vect' to be one of the x, y or z axes. "
719 8279a51d Carles Marti
    if not isinstance(norm_vect, str) or norm_vect != 'auto':
720 d6da8693 Carles Marti
        check_expect_val(norm_vect.tolist(), cart_axes, err_msg)
721 5fb01677 Carles Marti
    return min_coll_height
722 8887f41d Carles
723 8887f41d Carles
724 9cd032cf Carles Marti
def get_exclude_ads_ctr():
725 9cd032cf Carles Marti
    err_msg = "exclude_ads_ctr must have a boolean value."
726 9cd032cf Carles Marti
    exclude_ads_ctr = try_command(dos_inp.getboolean, [(ValueError, err_msg)],
727 9cd032cf Carles Marti
                                  "Screening", "exclude_ads_ctr",
728 9cd032cf Carles Marti
                                  fallback=False)
729 9cd032cf Carles Marti
    return exclude_ads_ctr
730 9cd032cf Carles Marti
731 9cd032cf Carles Marti
732 c25aa299 Carles Marti
def get_H_donor(spec_atoms):
733 c25aa299 Carles Marti
    from ase.data import chemical_symbols
734 14e0b660 Carles Martí
    err_msg = "The value of 'h_donor' must be either False, a chemical symbol "\
735 c25aa299 Carles Marti
              "or an atom index"
736 c25aa299 Carles Marti
    h_donor_str = dos_inp.get('Screening', 'h_donor', fallback="False")
737 c25aa299 Carles Marti
    h_donor = []
738 c25aa299 Carles Marti
    if h_donor_str.lower() in turn_false_answers:
739 c25aa299 Carles Marti
        return False
740 c25aa299 Carles Marti
    err = False
741 c25aa299 Carles Marti
    for el in h_donor_str.split():
742 c25aa299 Carles Marti
        try:
743 c25aa299 Carles Marti
            h_donor.append(int(el))
744 c25aa299 Carles Marti
        except ValueError:
745 c25aa299 Carles Marti
            if el not in chemical_symbols + [nw_sym for pairs in spec_atoms
746 c25aa299 Carles Marti
                                             for nw_sym in pairs]:
747 c25aa299 Carles Marti
                err = True
748 c25aa299 Carles Marti
            else:
749 c25aa299 Carles Marti
                h_donor.append(el)
750 c25aa299 Carles Marti
        finally:
751 c25aa299 Carles Marti
            if err:
752 c25aa299 Carles Marti
                logger.error(err_msg)
753 c25aa299 Carles Marti
                ValueError(err_msg)
754 c25aa299 Carles Marti
    return h_donor
755 c25aa299 Carles Marti
756 c25aa299 Carles Marti
757 c25aa299 Carles Marti
def get_H_acceptor(spec_atoms):
758 c25aa299 Carles Marti
    from ase.data import chemical_symbols
759 c25aa299 Carles Marti
    err_msg = "The value of 'h_acceptor' must be either 'all', a chemical " \
760 30e34792 Carles Martí
              "symbol or an atom index."
761 c25aa299 Carles Marti
    h_acceptor_str = dos_inp.get('Screening', 'h_acceptor', fallback="all")
762 c25aa299 Carles Marti
    if h_acceptor_str.lower() == "all":
763 c25aa299 Carles Marti
        return "all"
764 c25aa299 Carles Marti
    h_acceptor = []
765 c25aa299 Carles Marti
    err = False
766 c25aa299 Carles Marti
    for el in h_acceptor_str.split():
767 c25aa299 Carles Marti
        try:
768 c25aa299 Carles Marti
            h_acceptor.append(int(el))
769 c25aa299 Carles Marti
        except ValueError:
770 c25aa299 Carles Marti
            if el not in chemical_symbols + [nw_sym for pairs in spec_atoms
771 c25aa299 Carles Marti
                                             for nw_sym in pairs]:
772 c25aa299 Carles Marti
                err = True
773 c25aa299 Carles Marti
            else:
774 c25aa299 Carles Marti
                h_acceptor.append(el)
775 c25aa299 Carles Marti
        finally:
776 c25aa299 Carles Marti
            if err:
777 c25aa299 Carles Marti
                logger.error(err_msg)
778 30e34792 Carles Martí
                raise ValueError(err_msg)
779 c25aa299 Carles Marti
    return h_acceptor
780 a765b11c Carles Marti
781 a765b11c Carles Marti
782 b75bf97d Carles Marti
def get_use_molec_file():
783 b75bf97d Carles Marti
    use_molec_file = dos_inp.get('Screening', 'use_molec_file',
784 b75bf97d Carles Marti
                                 fallback='False')
785 13731dc4 Carles Marti
    if use_molec_file.lower() in turn_false_answers:
786 b75bf97d Carles Marti
        return False
787 b75bf97d Carles Marti
    if not os.path.isfile(use_molec_file):
788 b75bf97d Carles Marti
        logger.error(f'File {use_molec_file} not found.')
789 b75bf97d Carles Marti
        raise FileNotFoundError(f'File {use_molec_file} not found')
790 b75bf97d Carles Marti
791 b75bf97d Carles Marti
    return use_molec_file
792 b75bf97d Carles Marti
793 b75bf97d Carles Marti
794 a7128ce1 Carles Marti
# Refinement
795 a7128ce1 Carles Marti
796 f349bb54 Carles
def get_refine_inp_file(code, potcar_dir):
797 9d3b680c Carles Martí
    inp_file_lst = dos_inp.get('Refinement', 'refine_inp_file').split()
798 f349bb54 Carles
    check_inp_files(inp_file_lst[0] if len(inp_file_lst) == 1 else inp_file_lst,
799 f349bb54 Carles
                    code, potcar_dir)
800 9d3b680c Carles Martí
    return inp_file_lst[0] if len(inp_file_lst) == 1 else inp_file_lst
801 8887f41d Carles
802 8887f41d Carles
803 8887f41d Carles
def get_energy_cutoff():
804 8887f41d Carles
    err_msg = num_error % ('energy_cutoff', 'positive decimal number')
805 8887f41d Carles
    energy_cutoff = try_command(dos_inp.getfloat,
806 8887f41d Carles
                                [(ValueError, err_msg)],
807 8887f41d Carles
                                'Refinement', 'energy_cutoff', fallback=0.5)
808 8887f41d Carles
    if energy_cutoff < 0:
809 8887f41d Carles
        logger.error(err_msg)
810 8887f41d Carles
        raise ValueError(err_msg)
811 8887f41d Carles
    return energy_cutoff
812 8887f41d Carles
813 8887f41d Carles
814 c25aa299 Carles Marti
# Read input parameters
815 c25aa299 Carles Marti
816 8887f41d Carles
def read_input(in_file):
817 9d3b680c Carles Martí
    from modules.formats import adapt_format
818 9d3b680c Carles Martí
819 9d3b680c Carles Martí
    err_msg = False
820 8887f41d Carles
    try:
821 8887f41d Carles
        dos_inp.read(in_file)
822 8887f41d Carles
    except MissingSectionHeaderError as e:
823 8887f41d Carles
        logger.error('There are options in the input file without a Section '
824 695dcff8 Carles Marti
                     'header.')
825 14e0b660 Carles Martí
        err_msg = e
826 8887f41d Carles
    except DuplicateOptionError as e:
827 8887f41d Carles
        logger.error('There is an option in the input file that has been '
828 0bca5abb Carles Marti
                     'specified more than once.')
829 14e0b660 Carles Martí
        err_msg = e
830 8887f41d Carles
    except Exception as e:
831 14e0b660 Carles Martí
        err_msg = e
832 8887f41d Carles
    else:
833 14e0b660 Carles Martí
        err_msg = False
834 8887f41d Carles
    finally:
835 14e0b660 Carles Martí
        if isinstance(err_msg, BaseException):
836 14e0b660 Carles Martí
            raise err_msg
837 17e72a49 Carles
838 7dd94df7 Carles Marti
    inp_vars = {}
839 8887f41d Carles
840 8887f41d Carles
    # Global
841 8887f41d Carles
    if not dos_inp.has_section('Global'):
842 8887f41d Carles
        logger.error(no_sect_err % 'Global')
843 8887f41d Carles
        raise NoSectionError('Global')
844 8887f41d Carles
845 8887f41d Carles
    # Mandatory options
846 8887f41d Carles
    # Checks whether the mandatory options 'run_type', 'code', etc. are present.
847 14e0b660 Carles Martí
    glob_mand_opts = ['run_type', 'code', 'batch_q_sys']
848 7fd58762 Carles
    for opt in glob_mand_opts:
849 8887f41d Carles
        if not dos_inp.has_option('Global', opt):
850 8887f41d Carles
            logger.error(no_opt_err % (opt, 'Global'))
851 8887f41d Carles
            raise NoOptionError(opt, 'Global')
852 8887f41d Carles
853 8887f41d Carles
    # Gets which sections are to be carried out
854 8887f41d Carles
    isolated, screening, refinement = get_run_type()
855 7dd94df7 Carles Marti
    inp_vars['isolated'] = isolated
856 7dd94df7 Carles Marti
    inp_vars['screening'] = screening
857 7dd94df7 Carles Marti
    inp_vars['refinement'] = refinement
858 14e0b660 Carles Martí
    inp_vars['code'] = get_code()
859 7dd94df7 Carles Marti
    inp_vars['batch_q_sys'] = get_batch_q_sys()
860 8887f41d Carles
861 99afde40 Carles
    # Dependent options:
862 7dd94df7 Carles Marti
    if inp_vars['batch_q_sys']:
863 7dd94df7 Carles Marti
        inp_vars['max_jobs'] = get_max_jobs()
864 7dd94df7 Carles Marti
        if inp_vars['batch_q_sys'] != 'local':
865 09c3325a Carles Marti
            if not dos_inp.has_option('Global', 'subm_script'):
866 09c3325a Carles Marti
                logger.error(no_opt_err % ('subm_script', 'Global'))
867 09c3325a Carles Marti
                raise NoOptionError('subm_script', 'Global')
868 7dd94df7 Carles Marti
            inp_vars['subm_script'] = get_subm_script()
869 f349bb54 Carles
    if inp_vars['code'] == "vasp":
870 f349bb54 Carles
        inp_vars['potcar_dir'] = get_potcar_dir()
871 99afde40 Carles
872 8887f41d Carles
    # Facultative options (Default/Fallback value present)
873 9d3b680c Carles Martí
    inp_vars['pbc_cell'] = get_pbc_cell()
874 7dd94df7 Carles Marti
    inp_vars['project_name'] = get_project_name()
875 8b3d4772 Carles Marti
    # inp_vars['relaunch_err'] = get_relaunch_err()
876 7dd94df7 Carles Marti
    inp_vars['special_atoms'] = get_special_atoms()
877 8887f41d Carles
878 8887f41d Carles
    # Isolated
879 b1d27be5 Carles
    if isolated:
880 b606c71a Carles
        if not dos_inp.has_section('Isolated'):
881 b606c71a Carles
            logger.error(no_sect_err % 'Isolated')
882 b606c71a Carles
            raise NoSectionError('Isolated')
883 8887f41d Carles
        # Mandatory options
884 8887f41d Carles
        # Checks whether the mandatory options are present.
885 95dc2c8e Carles
        iso_mand_opts = ['isol_inp_file', 'molec_file']
886 8887f41d Carles
        for opt in iso_mand_opts:
887 8887f41d Carles
            if not dos_inp.has_option('Isolated', opt):
888 8887f41d Carles
                logger.error(no_opt_err % (opt, 'Isolated'))
889 8887f41d Carles
                raise NoOptionError(opt, 'Isolated')
890 f349bb54 Carles
        inp_vars['isol_inp_file'] = get_isol_inp_file(inp_vars['code'],
891 f349bb54 Carles
                                                      inp_vars['potcar_dir'])
892 7dd94df7 Carles Marti
        inp_vars['molec_file'] = get_molec_file()
893 8887f41d Carles
894 9d3b680c Carles Martí
        # Checks for PBC
895 9d3b680c Carles Martí
        atms = adapt_format('ase', inp_vars['molec_file'],
896 9d3b680c Carles Martí
                            inp_vars['special_atoms'])
897 9d3b680c Carles Martí
        if inp_vars['code'] == 'vasp' and np.linalg.det(atms.cell) == 0.0 \
898 9d3b680c Carles Martí
                and inp_vars['pbc_cell'] is False:
899 a5d76bfb Carles Martí
            err_msg = "When running calculations with 'VASP', the PBC cell " \
900 9d3b680c Carles Martí
                      "should be provided either implicitely inside " \
901 9d3b680c Carles Martí
                      "'molec_file' or by setting the 'pbc_cell' option."
902 9d3b680c Carles Martí
            logger.error(err_msg)
903 9d3b680c Carles Martí
            raise ValueError(err_msg)
904 a5d76bfb Carles Martí
        elif inp_vars['pbc_cell'] is False and np.linalg.det(atms.cell) != 0.0:
905 a5d76bfb Carles Martí
            inp_vars['pbc_cell'] = atms.cell
906 a5d76bfb Carles Martí
            logger.info(f"Obtained pbc_cell from '{inp_vars['molec_file']}' "
907 a5d76bfb Carles Martí
                        f"file.")
908 a5d76bfb Carles Martí
        elif (atms.cell != 0).any() and not np.allclose(inp_vars['pbc_cell'],
909 a5d76bfb Carles Martí
                                                        atms.cell):
910 9d3b680c Carles Martí
            logger.warning("'molec_file' has an implicit cell defined "
911 a5d76bfb Carles Martí
                           f"different than 'pbc_cell'.\n"
912 a5d76bfb Carles Martí
                           f"'molec_file' = {atms.cell}.\n"
913 a5d76bfb Carles Martí
                           f"'pbc_cell' = {inp_vars['pbc_cell']}).\n"
914 a5d76bfb Carles Martí
                           "'pbc_cell' value will be used.")
915 9d3b680c Carles Martí
916 8887f41d Carles
        # Facultative options (Default/Fallback value present)
917 7dd94df7 Carles Marti
        inp_vars['num_conformers'] = get_num_conformers()
918 7dd94df7 Carles Marti
        inp_vars['pre_opt'] = get_pre_opt()
919 8887f41d Carles
920 8887f41d Carles
    # Screening
921 b1d27be5 Carles
    if screening:
922 772b40e5 Carles
        if not dos_inp.has_section('Screening'):
923 9f7bb440 Carles
            logger.error(no_sect_err % 'Screening')
924 772b40e5 Carles
            raise NoSectionError('Screening')
925 8887f41d Carles
        # Mandatory options:
926 8887f41d Carles
        # Checks whether the mandatory options are present.
927 a765b11c Carles Marti
        screen_mand_opts = ['screen_inp_file', 'surf_file', 'sites',
928 7dd94df7 Carles Marti
                            'molec_ctrs']
929 8887f41d Carles
        for opt in screen_mand_opts:
930 772b40e5 Carles
            if not dos_inp.has_option('Screening', opt):
931 9f7bb440 Carles
                logger.error(no_opt_err % (opt, 'Screening'))
932 b1d27be5 Carles
                raise NoOptionError(opt, 'Screening')
933 f349bb54 Carles
        inp_vars['screen_inp_file'] = get_screen_inp_file(inp_vars['code'],
934 f349bb54 Carles
                                                          inp_vars[
935 f349bb54 Carles
                                                              'potcar_dir'])
936 7dd94df7 Carles Marti
        inp_vars['surf_file'] = get_surf_file()
937 7dd94df7 Carles Marti
        inp_vars['sites'] = get_sites()
938 7dd94df7 Carles Marti
        inp_vars['molec_ctrs'] = get_molec_ctrs()
939 8887f41d Carles
940 9d3b680c Carles Martí
        # Checks for PBC
941 a5d76bfb Carles Martí
        # Checks for PBC
942 9d3b680c Carles Martí
        atms = adapt_format('ase', inp_vars['surf_file'],
943 9d3b680c Carles Martí
                            inp_vars['special_atoms'])
944 9d3b680c Carles Martí
        if inp_vars['code'] == 'vasp' and np.linalg.det(atms.cell) == 0.0 \
945 9d3b680c Carles Martí
                and inp_vars['pbc_cell'] is False:
946 a5d76bfb Carles Martí
            err_msg = "When running calculations with 'VASP', the PBC cell " \
947 9d3b680c Carles Martí
                      "should be provided either implicitely inside " \
948 a5d76bfb Carles Martí
                      "'surf_file' or by setting the 'pbc_cell' option."
949 9d3b680c Carles Martí
            logger.error(err_msg)
950 9d3b680c Carles Martí
            raise ValueError(err_msg)
951 a5d76bfb Carles Martí
        elif inp_vars['pbc_cell'] is False and np.linalg.det(atms.cell) != 0.0:
952 a5d76bfb Carles Martí
            inp_vars['pbc_cell'] = atms.cell
953 a5d76bfb Carles Martí
            logger.info(f"Obtained pbc_cell from '{inp_vars['surf_file']}' "
954 a5d76bfb Carles Martí
                        f"file.")
955 a5d76bfb Carles Martí
        elif (atms.cell != 0).any() and not np.allclose(inp_vars['pbc_cell'],
956 a5d76bfb Carles Martí
                                                        atms.cell):
957 a5d76bfb Carles Martí
            logger.warning("'surf_file' has an implicit cell defined "
958 a5d76bfb Carles Martí
                           f"different than 'pbc_cell'.\n"
959 a5d76bfb Carles Martí
                           f"'surf_file' = {atms.cell}.\n"
960 a5d76bfb Carles Martí
                           f"'pbc_cell' = {inp_vars['pbc_cell']}).\n"
961 9d3b680c Carles Martí
                           "'pbc_cell' value will be used.")
962 9d3b680c Carles Martí
963 8887f41d Carles
        # Facultative options (Default value present)
964 7dd94df7 Carles Marti
        inp_vars['select_magns'] = get_select_magns()
965 7dd94df7 Carles Marti
        inp_vars['confs_per_magn'] = get_confs_per_magn()
966 7dd94df7 Carles Marti
        inp_vars['surf_norm_vect'] = get_surf_norm_vect()
967 fe91ddb2 Carles Marti
        inp_vars['adsorption_height'] = get_adsorption_height()
968 7dd94df7 Carles Marti
        inp_vars['set_angles'] = get_set_angles()
969 7dd94df7 Carles Marti
        inp_vars['sample_points_per_angle'] = get_pts_per_angle()
970 7dd94df7 Carles Marti
        inp_vars['collision_threshold'] = get_coll_thrsld()
971 f98ba5b0 Carles Marti
        inp_vars['min_coll_height'] = get_min_coll_height(
972 f98ba5b0 Carles Marti
            inp_vars['surf_norm_vect'])
973 8b3d4772 Carles Marti
        if inp_vars['min_coll_height'] is False \
974 8b3d4772 Carles Marti
                and inp_vars['collision_threshold'] is False:
975 8b3d4772 Carles Marti
            logger.warning("Collisions are deactivated: Overlapping of "
976 8b3d4772 Carles Marti
                           "adsorbate and surface is possible")
977 9cd032cf Carles Marti
        inp_vars['exclude_ads_ctr'] = get_exclude_ads_ctr()
978 b75bf97d Carles Marti
        inp_vars['h_donor'] = get_H_donor(inp_vars['special_atoms'])
979 b75bf97d Carles Marti
        inp_vars['max_structures'] = get_max_structures()
980 b75bf97d Carles Marti
        inp_vars['use_molec_file'] = get_use_molec_file()
981 8887f41d Carles
982 8b3d4772 Carles Marti
        # Options depending on the value of others
983 609104e3 Carles Marti
        if inp_vars['set_angles'] == "internal":
984 609104e3 Carles Marti
            internal_opts = ['molec_ctrs2', 'molec_ctrs3', 'surf_ctrs2',
985 609104e3 Carles Marti
                             'max_helic_angle']
986 609104e3 Carles Marti
            for opt in internal_opts:
987 7dd94df7 Carles Marti
                if not dos_inp.has_option('Screening', opt):
988 7dd94df7 Carles Marti
                    logger.error(no_opt_err % (opt, 'Screening'))
989 7dd94df7 Carles Marti
                    raise NoOptionError(opt, 'Screening')
990 a98d4172 Carles Marti
            inp_vars['max_helic_angle'] = get_max_helic_angle()
991 7dd94df7 Carles Marti
            inp_vars['molec_ctrs2'] = get_molec_ctrs2()
992 7dd94df7 Carles Marti
            inp_vars['molec_ctrs3'] = get_molec_ctrs3()
993 7dd94df7 Carles Marti
            inp_vars['surf_ctrs2'] = get_surf_ctrs2()
994 7dd94df7 Carles Marti
            if len(inp_vars["molec_ctrs2"]) != len(inp_vars['molec_ctrs']) \
995 7dd94df7 Carles Marti
                    or len(inp_vars["molec_ctrs3"]) != \
996 7dd94df7 Carles Marti
                    len(inp_vars['molec_ctrs']) \
997 7dd94df7 Carles Marti
                    or len(inp_vars['surf_ctrs2']) != len(inp_vars['sites']):
998 14e0b660 Carles Martí
                err_msg = "'molec_ctrs' 'molec_ctrs2' and 'molec_ctrs3' must " \
999 14e0b660 Carles Martí
                          "have the same number of indices "
1000 14e0b660 Carles Martí
                logger.error(err_msg)
1001 14e0b660 Carles Martí
                raise ValueError(err_msg)
1002 7dd94df7 Carles Marti
1003 c25aa299 Carles Marti
        if inp_vars['h_donor'] is False:
1004 c25aa299 Carles Marti
            inp_vars['h_acceptor'] = False
1005 c25aa299 Carles Marti
        else:
1006 c25aa299 Carles Marti
            inp_vars['h_acceptor'] = get_H_acceptor(inp_vars['special_atoms'])
1007 c25aa299 Carles Marti
1008 8887f41d Carles
    # Refinement
1009 b1d27be5 Carles
    if refinement:
1010 8887f41d Carles
        if not dos_inp.has_section('Refinement'):
1011 8887f41d Carles
            logger.error(no_sect_err % 'Refinement')
1012 8887f41d Carles
            raise NoSectionError('Refinement')
1013 8887f41d Carles
        # Mandatory options
1014 8887f41d Carles
        # Checks whether the mandatory options are present.
1015 8887f41d Carles
        ref_mand_opts = ['refine_inp_file']
1016 8887f41d Carles
        for opt in ref_mand_opts:
1017 8887f41d Carles
            if not dos_inp.has_option('Refinement', opt):
1018 8887f41d Carles
                logger.error(no_opt_err % (opt, 'Refinement'))
1019 8887f41d Carles
                raise NoOptionError(opt, 'Refinement')
1020 f349bb54 Carles
        inp_vars['refine_inp_file'] = get_refine_inp_file(inp_vars['code'],
1021 f349bb54 Carles
                                                          inp_vars[
1022 f349bb54 Carles
                                                              'potcar_dir'])
1023 8887f41d Carles
1024 8887f41d Carles
        # Facultative options (Default value present)
1025 7dd94df7 Carles Marti
        inp_vars['energy_cutoff'] = get_energy_cutoff()
1026 b1d27be5 Carles
        # end energy_cutoff
1027 9f7bb440 Carles
1028 a5cc42ff Carles Marti
    return_vars_str = "\n\t".join([str(key) + ": " + str(value)
1029 7dd94df7 Carles Marti
                                   for key, value in inp_vars.items()])
1030 14e0b660 Carles Martí
    logger.info(f'Correctly read {in_file} parameters:'
1031 14e0b660 Carles Martí
                f' \n\n\t{return_vars_str}\n')
1032 d8a6314e Carles
1033 7dd94df7 Carles Marti
    return inp_vars
1034 8887f41d Carles
1035 8887f41d Carles
1036 8887f41d Carles
if __name__ == "__main__":
1037 8887f41d Carles
    import sys
1038 8887f41d Carles
1039 8887f41d Carles
    print(read_input(sys.argv[1]))