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

dockonsurf / modules / dos_input.py @ 30e34792

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