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

dockonsurf / modules / dos_input.py @ e1c5f171

Historique | Voir | Annoter | Télécharger (27,37 ko)

1
"""Functions to deal with DockOnSurf input files.
2

3
Functions
4
try_command:Tries to run a command and logs its exceptions (expected and not).
5
str2lst: Converts a string of integers, and groups of them, to a list of lists.
6
check_expect_val: Checks whether the value of an option has an adequate value.
7
read_input: Sets up the calculation by reading the parameters from input file.
8
get_run_type: Gets 'run_type' value and checks that its value is acceptable.
9
get_code: Gets 'code' value and checks that its value is acceptable.
10
get_batch_q_sys: Gets 'batch_q_sys' value and checks that its value is
11
acceptable.
12
get_relaunch_err: Gets 'relaunch_err' value and checks that its value is
13
acceptable.
14
get_max_jobs: Gets 'max_jobs' value and checks that its value is acceptable.
15
get_special_atoms: Gets 'special_atoms' value and checks that its value is
16
acceptable.
17
get_isol_inp_file: Gets 'isol_inp_file' value and checks that its value is
18
acceptable.
19
get_cluster_magns: Gets 'cluster_magns' value and checks that its value is
20
acceptable.
21
get_num_conformers: Gets 'num_conformers' value and checks that its value is
22
acceptable.
23
get_num_prom_cand: Gets 'num_prom_cand' value and checks that its value is
24
acceptable.
25
get_iso_rmsd: Gets 'iso_rmsd' value and checks that its value is acceptable.
26
get_pre_opt: Gets 'pre_opt' value and checks that its value is acceptable.
27
get_screen_inp_file: Gets 'screen_inp_file' value and checks that its value is
28
acceptable.
29
get_sites: Gets 'sites' value and checks that its value is acceptable.
30
get_molec_ads_ctrs: Gets 'molec_ads_ctrs' value and checks that its value is
31
acceptable.
32
get_try_disso: Gets 'try_disso' value and checks that its value is acceptable.
33
get_pts_per_angle: Gets 'pts_per_angle' value and checks that its value is
34
acceptable.
35
get_coll_thrsld: Gets 'coll_thrsld' value and checks that its value is
36
acceptable.
37
get_screen_rmsd: Gets 'screen_rmsd' value and checks that its value is
38
acceptable.
39
get_coll_bottom_z: Gets 'coll_bottom_z' value and checks that its value is
40
acceptable.
41
get_refine_inp_file: Gets 'refine_inp_file' value and checks that its value is
42
acceptable.
43
get_energy_cutoff: Gets 'energy_cutoff' value and checks that its value is
44
acceptable.
45
"""
46
import os.path
47
import logging
48
from configparser import ConfigParser, NoSectionError, NoOptionError, \
49
    MissingSectionHeaderError, DuplicateOptionError
50
import numpy as np
51
from modules.utilities import try_command
52

    
53
logger = logging.getLogger('DockOnSurf')
54

    
55
dos_inp = ConfigParser(inline_comment_prefixes='#',
56
                       empty_lines_in_values=False)
57

    
58
new_answers = {'n': False, 'none': False, 'nay': False,
59
               'y': True, '': True, 'aye': True, 'sure': True}
60
for answer, val in new_answers.items():
61
    dos_inp.BOOLEAN_STATES[answer] = val
62
turn_false_answers = [answer for answer in dos_inp.BOOLEAN_STATES
63
                      if dos_inp.BOOLEAN_STATES[answer] is False]
64

    
65
no_sect_err = "Section '%s' not found on input file"
66
no_opt_err = "Option '%s' not found on section '%s'"
67
num_error = "'%s' value must be a %s"
68

    
69

    
70
def str2lst(cmplx_str, func=int):  # TODO: enable deeper level of nested lists
71
    # TODO Treat all-enclosing parenthesis as a list instead of list of lists.
72
    """Converts a string of integers, and groups of them, to a list.
73

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

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

86
    eg. '128,(135 138;141] 87 {45, 68}' -> [128, 87, [135, 138, 141], [45, 68]]
87
    """
88

    
89
    # Checks
90
    error_msg = "Function argument should be a str, sequence of integer " \
91
                "numbers separated by ',' ';' or ' '." \
92
                "\nThey can be grouped in parentheses-like enclosers: '()', " \
93
                "'[]' or {}. Nested groups are not allowed. \n" \
94
                "eg. 128,(135 138;141) 87 {45, 68}"
95
    cmplx_str = try_command(cmplx_str.replace, [(AttributeError, error_msg)],
96
                            ',', ' ')
97

    
98
    cmplx_str = cmplx_str.replace(';', ' ').replace('[', '(').replace(
99
        ']', ')').replace('{', '(').replace('}', ')')
100

    
101
    try_command(list, [(ValueError, error_msg)], map(func, cmplx_str.replace(
102
        ')', '').replace('(', '').split()))
103

    
104
    deepness = 0
105
    for el in cmplx_str.split():
106
        if '(' in el:
107
            deepness += 1
108
        if ')' in el:
109
            deepness += -1
110
        if deepness > 1 or deepness < 0:
111
            logger.error(error_msg)
112
            raise ValueError(error_msg)
113

    
114
    init_list = cmplx_str.split()
115
    start_group = []
116
    end_group = []
117
    for i, element in enumerate(init_list):
118
        if '(' in element:
119
            start_group.append(i)
120
            init_list[i] = element.replace('(', '')
121
        if ')' in element:
122
            end_group.append(i)
123
            init_list[i] = element.replace(')', '')
124

    
125
    init_list = list(map(func, init_list))
126

    
127
    new_list = []
128
    for start_el, end_el in zip(start_group, end_group):
129
        new_list.append(init_list[start_el:end_el + 1])
130

    
131
    for v in new_list:
132
        for el in v:
133
            init_list.remove(el)
134
    return init_list + new_list
135

    
136

    
137
def check_expect_val(value, expect_vals):
138
    """Checks whether an option lies within its expected values.
139

140
    Keyword arguments:
141
    @param value: The variable to check if its value lies within the expected
142
    ones
143
    @param expect_vals: list, list of values allowed for the present option.
144
    @raise ValueError: if the value is not among the expected ones.
145
    @return True if the value is among the expected ones.
146
    """
147
    adeq_val_err = "'%s' is not an adequate value.\n" \
148
                   "Adequate values: %s"
149
    if not any([exp_val == value for exp_val in expect_vals]):
150
        logger.error(adeq_val_err % (value, expect_vals))
151
        raise ValueError(adeq_val_err % (value, expect_vals))
152

    
153
    return True
154

    
155

    
156
def check_inp_file(inp_file, code):
157
    if code == 'cp2k':
158
        from pycp2k import CP2K
159
        cp2k = CP2K()
160
        try_command(cp2k.parse,
161
                    [(UnboundLocalError, "Invalid CP2K input file")], inp_file)
162

    
163

    
164
# Global
165

    
166
def get_run_type():
167
    isolated, screening, refinement = (False, False, False)
168
    run_type_vals = ['isolated', 'screening', 'refinement', 'adsorption',
169
                     'full']
170
    run_types = dos_inp.get('Global', 'run_type').split()
171
    for run_type in run_types:
172
        check_expect_val(run_type.lower(), run_type_vals)
173
        if 'isol' in run_type.lower():
174
            isolated = True
175
        if 'screen' in run_type.lower():
176
            screening = True
177
        if 'refine' in run_type.lower():
178
            refinement = True
179
        if 'adsor' in run_type.lower():
180
            screening, refinement = (True, True)
181
        if 'full' in run_type.lower():
182
            isolated, screening, refinement = (True, True, True)
183

    
184
    return isolated, screening, refinement
185

    
186

    
187
def get_code():
188
    code_vals = ['cp2k']
189
    check_expect_val(dos_inp.get('Global', 'code').lower(), code_vals)
190
    code = dos_inp.get('Global', 'code').lower()
191
    return code
192

    
193

    
194
def get_batch_q_sys():
195
    batch_q_sys_vals = ['sge', 'lsf', 'irene', 'local'] + turn_false_answers
196
    check_expect_val(dos_inp.get('Global', 'batch_q_sys').lower(),
197
                     batch_q_sys_vals)
198
    batch_q_sys = dos_inp.get('Global', 'batch_q_sys').lower()
199
    if batch_q_sys.lower() in turn_false_answers:
200
        return False
201
    else:
202
        return batch_q_sys
203

    
204

    
205
def get_subm_script():
206
    subm_script = dos_inp.get('Global', 'subm_script', fallback=False)
207
    if subm_script and not os.path.isfile(subm_script):
208
        logger.error(f'File {subm_script} not found.')
209
        raise FileNotFoundError(f'File {subm_script} not found')
210
    return subm_script
211

    
212

    
213
def get_project_name():
214
    project_name = dos_inp.get('Global', 'project_name', fallback='')
215
    return project_name
216

    
217

    
218
def get_relaunch_err():
219
    relaunch_err_vals = ['geo_not_conv']
220
    relaunch_err = dos_inp.get('Global', 'relaunch_err',
221
                               fallback="False")
222
    if relaunch_err.lower() in turn_false_answers:
223
        return False
224
    else:
225
        check_expect_val(relaunch_err.lower(), relaunch_err_vals)
226
    return relaunch_err
227

    
228

    
229
def get_max_jobs():
230
    err_msg = num_error % ('max_jobs', 'positive integer')
231
    max_jobs = try_command(dos_inp.getint, [(ValueError, err_msg)],
232
                           'Global', 'max_jobs', fallback=3)
233

    
234
    if max_jobs < 1:
235
        logger.error(num_error % ('max_jobs', 'positive integer'))
236
        raise ValueError(num_error % ('max_jobs', 'positive integer'))
237
    return max_jobs
238

    
239

    
240
def get_type_max():
241
    running = ['r', 'run', 'running']
242
    pending = ['p', 'pending', 'q', 'qw', 'queued']
243
    type_max = dos_inp.get('Global', 'type_max', fallback="False")
244
    if type_max.lower() in turn_false_answers:
245
        return False
246
    elif type_max.lower() in running:
247
        return 'r'
248
    elif type_max.lower() in pending:
249
        return 'p'
250
    else:
251
        err = f"Value for 'type_max' is not accepted. Accepted values: " \
252
              f"{running + pending + turn_false_answers}."
253
        logger.error(err)
254
        raise ValueError(err)
255

    
256

    
257
def get_special_atoms():
258
    from ase.data import chemical_symbols
259

    
260
    spec_at_err = '\'special_atoms\' does not have an adequate format.\n' \
261
                  'Adequate format: (Fe1 Fe) (O1 O)'
262
    special_atoms = dos_inp.get('Global', 'special_atoms', fallback="False")
263
    if special_atoms.lower() in turn_false_answers:
264
        special_atoms = []
265
    else:
266
        # Converts the string into a list of tuples
267
        lst_tple = [tuple(pair.replace("(", "").split()) for pair in
268
                    special_atoms.split(")")[:-1]]
269
        if len(lst_tple) == 0:
270
            logger.error(spec_at_err)
271
            raise ValueError(spec_at_err)
272
        for i, tup in enumerate(lst_tple):
273
            if not isinstance(tup, tuple) or len(tup) != 2:
274
                logger.error(spec_at_err)
275
                raise ValueError(spec_at_err)
276
            if tup[1].capitalize() not in chemical_symbols:
277
                elem_err = "The second element of the couple should be an " \
278
                           "actual element of the periodic table"
279
                logger.error(elem_err)
280
                raise ValueError(elem_err)
281
            if tup[0].capitalize() in chemical_symbols:
282
                elem_err = "The first element of the couple is already an " \
283
                           "actual element of the periodic table, "
284
                logger.error(elem_err)
285
                raise ValueError(elem_err)
286
            for j, tup2 in enumerate(lst_tple):
287
                if j <= i:
288
                    continue
289
                if tup2[0] == tup[0]:
290
                    label_err = f'You have specified the label {tup[0]} to ' \
291
                                f'more than one special atom'
292
                    logger.error(label_err)
293
                    raise ValueError(label_err)
294
        special_atoms = lst_tple
295
    return special_atoms
296

    
297

    
298
# Isolated
299

    
300
def get_isol_inp_file():
301
    isol_inp_file = dos_inp.get('Isolated', 'isol_inp_file')
302
    if not os.path.isfile(isol_inp_file):
303
        logger.error(f'File {isol_inp_file} not found.')
304
        raise FileNotFoundError(f'File {isol_inp_file} not found')
305
    return isol_inp_file
306

    
307

    
308
def get_molec_file():
309
    molec_file = dos_inp.get('Isolated', 'molec_file')
310
    if not os.path.isfile(molec_file):
311
        logger.error(f'File {molec_file} not found.')
312
        raise FileNotFoundError(f'File {molec_file} not found')
313
    return molec_file
314

    
315

    
316
def get_num_conformers():
317
    err_msg = num_error % ('num_conformers', 'positive integer')
318
    num_conformers = try_command(dos_inp.getint, [(ValueError, err_msg)],
319
                                 'Isolated', 'num_conformers', fallback=100)
320
    if num_conformers < 1:
321
        logger.error(err_msg)
322
        raise ValueError(err_msg)
323
    return num_conformers
324

    
325

    
326
def get_pre_opt():
327
    pre_opt_vals = ['uff', 'mmff'] + turn_false_answers
328
    check_expect_val(dos_inp.get('Isolated', 'pre_opt').lower(), pre_opt_vals)
329
    pre_opt = dos_inp.get('Isolated', 'pre_opt').lower()
330
    if pre_opt in turn_false_answers:
331
        return False
332
    else:
333
        return pre_opt
334

    
335

    
336
# Screening
337

    
338
def get_screen_inp_file():
339
    screen_inp_file = dos_inp.get('Screening', 'screen_inp_file')
340
    if not os.path.isfile(screen_inp_file):
341
        logger.error(f'File {screen_inp_file} not found.')
342
        raise FileNotFoundError(f'File {screen_inp_file} not found')
343
    return screen_inp_file
344

    
345

    
346
def get_surf_file():
347
    surf_file = dos_inp.get('Screening', 'surf_file')
348
    if not os.path.isfile(surf_file):
349
        logger.error(f'File {surf_file} not found.')
350
        raise FileNotFoundError(f'File {surf_file} not found')
351
    return surf_file
352

    
353

    
354
def get_sites():
355
    err_msg = 'The value of sites should be a list of atom numbers ' \
356
              '(ie. positive integers) or groups of atom numbers ' \
357
              'grouped by parentheses-like enclosers. \n' \
358
              'eg. 128,(135 138;141) 87 {45, 68}'
359
    # Convert the string into a list of lists
360
    sites = try_command(str2lst,
361
                        [(ValueError, err_msg), (AttributeError, err_msg)],
362
                        dos_inp.get('Screening', 'sites'))
363
    # Check all elements of the list (of lists) are positive integers
364
    for site in sites:
365
        if type(site) is list:
366
            for atom in site:
367
                if atom < 0:
368
                    logger.error(err_msg)
369
                    raise ValueError(err_msg)
370
        elif type(site) is int:
371
            if site < 0:
372
                logger.error(err_msg)
373
                raise ValueError(err_msg)
374
        else:
375
            logger.error(err_msg)
376
            raise ValueError(err_msg)
377

    
378
    return sites
379

    
380

    
381
def get_molec_ads_ctrs():
382
    err_msg = 'The value of molec_ads_ctrs should be a list of atom' \
383
              ' numbers (ie. positive integers) or groups of atom ' \
384
              'numbers enclosed by parentheses-like characters. \n' \
385
              'eg. 128,(135 138;141) 87 {45, 68}'
386
    # Convert the string into a list of lists
387
    molec_ads_ctrs = try_command(str2lst,
388
                                 [(ValueError, err_msg),
389
                                  (AttributeError, err_msg)],
390
                                 dos_inp.get('Screening', 'molec_ads_ctrs'))
391
    # Check all elements of the list (of lists) are positive integers
392
    for ctr in molec_ads_ctrs:
393
        if isinstance(ctr, list):
394
            for atom in ctr:
395
                if atom < 0:
396
                    logger.error(err_msg)
397
                    raise ValueError(err_msg)
398
        elif isinstance(ctr, int):
399
            if ctr < 0:
400
                logger.error(err_msg)
401
                raise ValueError(err_msg)
402
        else:
403
            logger.error(err_msg)
404
            raise ValueError(err_msg)
405

    
406
    return molec_ads_ctrs
407

    
408

    
409
def get_molec_neigh_ctrs():
410
    err_msg = 'The value of molec_neigh_ctrs should be a list of atom ' \
411
              'numbers (ie. positive integers) or groups of atom ' \
412
              'numbers enclosed by parentheses-like characters. \n' \
413
              'eg. 128,(135 138;141) 87 {45, 68}'
414
    # Convert the string into a list of lists
415
    molec_neigh_ctrs = try_command(str2lst, [(ValueError, err_msg),
416
                                             (AttributeError, err_msg)],
417
                                   dos_inp.get('Screening', 'molec_neigh_ctrs'))
418

    
419
    # Check all elements of the list (of lists) are positive integers
420
    for ctr in molec_neigh_ctrs:
421
        if isinstance(ctr, list):
422
            for atom in ctr:
423
                if atom < 0:
424
                    logger.error(err_msg)
425
                    raise ValueError(err_msg)
426
        elif isinstance(ctr, int):
427
            if ctr < 0:
428
                logger.error(err_msg)
429
                raise ValueError(err_msg)
430
        else:
431
            logger.error(err_msg)
432
            raise ValueError(err_msg)
433

    
434
    return molec_neigh_ctrs
435

    
436

    
437
def get_select_magns():
438
    select_magns_vals = ['energy', 'moi']
439
    select_magns_str = dos_inp.get('Screening', 'select_magns',
440
                                   fallback='energy')
441
    select_magns_str.replace(',', ' ').replace(';', ' ')
442
    select_magns = select_magns_str.split(' ')
443
    select_magns = [m.lower() for m in select_magns]
444
    for m in select_magns:
445
        check_expect_val(m, select_magns_vals)
446
    return select_magns
447

    
448

    
449
def get_confs_per_magn():
450
    err_msg = num_error % ('confs_per_magn', 'positive integer')
451
    confs_per_magn = try_command(dos_inp.getint, [(ValueError, err_msg)],
452
                                 'Screening', 'confs_per_magn', fallback=2)
453
    if confs_per_magn <= 0:
454
        logger.error(err_msg)
455
        raise ValueError(err_msg)
456
    return confs_per_magn
457

    
458

    
459
def get_surf_norm_vect():
460
    err = "'surf_norm_vect' must be either a 3 component vector or 'x', 'y' " \
461
          "or 'z'"
462
    cart_axes = {'x': [1.0, 0.0, 0.0], '-x': [-1.0, 0.0, 0.0],
463
                 'y': [0.0, 1.0, 0.0], '-y': [0.0, -1.0, 0.0],
464
                 'z': [0.0, 0.0, 1.0], '-z': [0.0, 0.0, -1.0]}
465
    surf_norm_vect_str = dos_inp.get('Screening', 'surf_norm_vect',
466
                                     fallback="z")
467

    
468
    if surf_norm_vect_str in cart_axes:
469
        surf_norm_vect = cart_axes[surf_norm_vect_str]
470
    else:
471
        surf_norm_vect = try_command(str2lst, [(ValueError, err)],
472
                                     surf_norm_vect_str, float)
473
        if len(surf_norm_vect) != 3:
474
            logger.error(err)
475
            raise ValueError(err)
476

    
477
    return np.array(surf_norm_vect)
478

    
479

    
480
def get_ads_algo():
481
    algo_vals = ['euler', 'chemcat']
482
    check_expect_val(dos_inp.get('Screening', 'ads_algo').lower(), algo_vals)
483
    ads_algo = dos_inp.get('Screening', 'ads_algo', fallback='euler').lower()
484
    return ads_algo
485

    
486

    
487
def get_pts_per_angle():
488
    err_msg = num_error % ('sample_points_per_angle', 'positive integer')
489
    pts_per_angle = try_command(dos_inp.getint,
490
                                [(ValueError, err_msg)],
491
                                'Screening', 'sample_points_per_angle',
492
                                fallback=3)
493
    if pts_per_angle <= 0:
494
        logger.error(err_msg)
495
        raise ValueError(err_msg)
496
    return pts_per_angle
497

    
498

    
499
def get_max_structures():
500
    err_msg = num_error % ('max_structures', 'positive integer')
501
    max_structures = try_command(dos_inp.getint, [(ValueError, err_msg)],
502
                                 'Screening', 'max_structures', fallback=np.inf)
503
    if max_structures <= 0:
504
        logger.error(err_msg)
505
        raise ValueError(err_msg)
506
    return max_structures
507

    
508

    
509
def get_coll_thrsld():
510
    err_msg = num_error % ('collision_threshold',
511
                           'positive decimal number')
512

    
513
    coll_thrsld = try_command(dos_inp.getfloat,
514
                              [(ValueError, err_msg)],
515
                              'Screening', 'collision_threshold', fallback=1.2)
516
    if coll_thrsld <= 0:
517
        logger.error(err_msg)
518
        raise ValueError(err_msg)
519

    
520
    return coll_thrsld
521

    
522

    
523
def get_screen_rmsd():
524
    err_msg = num_error % ('screen_rmsd', 'positive decimal number')
525
    screen_rmsd = try_command(dos_inp.getfloat,
526
                              [(ValueError, err_msg)],
527
                              'Screening', 'screen_rmsd', fallback=0.05)
528
    if screen_rmsd <= 0:
529
        logger.error(err_msg)
530
        raise ValueError(err_msg)
531

    
532
    return screen_rmsd
533

    
534

    
535
def get_min_coll_height():
536
    err_msg = num_error % ('min_coll_height', 'decimal number')
537
    min_coll_height = dos_inp.get('Screening', 'min_coll_height',
538
                                  fallback="False")
539
    if min_coll_height.lower() in turn_false_answers:
540
        min_coll_height = False
541
    else:
542
        min_coll_height = try_command(float, [(ValueError, err_msg)],
543
                                      min_coll_height)
544

    
545
    return min_coll_height
546

    
547

    
548
def get_disso_atoms():
549
    disso_atoms_str = dos_inp.get('Screening', 'disso_atoms', fallback="False")
550
    disso_atoms = []
551
    if disso_atoms_str.lower() in turn_false_answers:
552
        pass
553
    else:
554
        for el in disso_atoms_str.split():
555
            try:
556
                disso_atoms.append(int(el))
557
            except ValueError:
558
                disso_atoms.append(el)
559
    return disso_atoms
560

    
561

    
562
# Refinement
563

    
564
def get_refine_inp_file():  # TODO if not specified try isol_inp_file.
565
    refine_inp_file = dos_inp.get('Refinement', 'refine_inp_file')
566
    if not os.path.isfile(refine_inp_file):
567
        logger.error(f'File {refine_inp_file} not found.')
568
        raise FileNotFoundError(f'File {refine_inp_file} not found')
569

    
570
    return refine_inp_file
571

    
572

    
573
def get_energy_cutoff():
574
    err_msg = num_error % ('energy_cutoff', 'positive decimal number')
575
    energy_cutoff = try_command(dos_inp.getfloat,
576
                                [(ValueError, err_msg)],
577
                                'Refinement', 'energy_cutoff', fallback=0.5)
578
    if energy_cutoff < 0:
579
        logger.error(err_msg)
580
        raise ValueError(err_msg)
581
    return energy_cutoff
582

    
583

    
584
def read_input(in_file):
585
    err = False
586
    try:
587
        dos_inp.read(in_file)
588
    except MissingSectionHeaderError as e:
589
        logger.error('There are options in the input file without a Section '
590
                     'header.')
591
        err = e
592
    except DuplicateOptionError as e:
593
        logger.error('There is an option in the input file that has been '
594
                     'specified more than once.')
595
        err = e
596
    except Exception as e:
597
        err = e
598
    else:
599
        err = False
600
    finally:
601
        if isinstance(err, BaseException):
602
            raise err
603

    
604
    return_vars = {}
605

    
606
    # Global
607
    if not dos_inp.has_section('Global'):
608
        logger.error(no_sect_err % 'Global')
609
        raise NoSectionError('Global')
610

    
611
    # Mandatory options
612
    # Checks whether the mandatory options 'run_type', 'code', etc. are present.
613
    glob_mand_opts = ['run_type', 'batch_q_sys']
614
    for opt in glob_mand_opts:
615
        if not dos_inp.has_option('Global', opt):
616
            logger.error(no_opt_err % (opt, 'Global'))
617
            raise NoOptionError(opt, 'Global')
618

    
619
    # Gets which sections are to be carried out
620
    isolated, screening, refinement = get_run_type()
621
    return_vars['isolated'] = isolated
622
    return_vars['screening'] = screening
623
    return_vars['refinement'] = refinement
624
    return_vars['batch_q_sys'] = get_batch_q_sys()
625

    
626
    # Dependent options:
627
    return_vars['code'] = get_code()
628
    if return_vars['batch_q_sys']:
629
        return_vars['type_max'] = get_type_max()
630
        return_vars['max_jobs'] = get_max_jobs()
631
        if return_vars['batch_q_sys'] != 'local':
632
            if not dos_inp.has_option('Global', 'subm_script'):
633
                logger.error(no_opt_err % ('subm_script', 'Global'))
634
                raise NoOptionError('subm_script', 'Global')
635
            return_vars['subm_script'] = get_subm_script()
636
        elif return_vars['type_max'] == "p":
637
            err = "'type_max' cannot be set to 'pending'/'queued' when " \
638
                  "'batch_q_sys' is set to 'local' "
639
            logger.error(err)
640
            raise ValueError(err)
641

    
642
    # Facultative options (Default/Fallback value present)
643
    return_vars['project_name'] = get_project_name()
644
    return_vars['relaunch_err'] = get_relaunch_err()
645
    return_vars['special_atoms'] = get_special_atoms()
646

    
647
    # Isolated
648
    if isolated:
649
        if not dos_inp.has_section('Isolated'):
650
            logger.error(no_sect_err % 'Isolated')
651
            raise NoSectionError('Isolated')
652
        # Mandatory options
653
        # Checks whether the mandatory options are present.
654
        iso_mand_opts = ['isol_inp_file', 'molec_file']
655
        for opt in iso_mand_opts:
656
            if not dos_inp.has_option('Isolated', opt):
657
                logger.error(no_opt_err % (opt, 'Isolated'))
658
                raise NoOptionError(opt, 'Isolated')
659
        return_vars['isol_inp_file'] = get_isol_inp_file()
660
        if 'code ' in return_vars:
661
            check_inp_file(return_vars['isol_inp_file'], return_vars['code'])
662
        return_vars['molec_file'] = get_molec_file()
663

    
664
        # Facultative options (Default/Fallback value present)
665
        return_vars['num_conformers'] = get_num_conformers()
666
        # return_vars['num_prom_cand'] = get_num_prom_cand()
667
        # return_vars['iso_rmsd'] = get_iso_rmsd()
668
        return_vars['pre_opt'] = get_pre_opt()
669

    
670
    # Screening
671
    if screening:
672
        if not dos_inp.has_section('Screening'):
673
            logger.error(no_sect_err % 'Screening')
674
            raise NoSectionError('Screening')
675
        # Mandatory options:
676
        # Checks whether the mandatory options are present.
677
        screen_mand_opts = ['screen_inp_file', 'surf_file', 'sites',
678
                            'molec_ads_ctrs', 'molec_neigh_ctrs']
679
        for opt in screen_mand_opts:
680
            if not dos_inp.has_option('Screening', opt):
681
                logger.error(no_opt_err % (opt, 'Screening'))
682
                raise NoOptionError(opt, 'Screening')
683
        return_vars['screen_inp_file'] = get_screen_inp_file()
684
        return_vars['surf_file'] = get_surf_file()
685
        return_vars['sites'] = get_sites()
686
        return_vars['molec_ads_ctrs'] = get_molec_ads_ctrs()
687
        return_vars['molec_neigh_ctrs'] = get_molec_neigh_ctrs()
688
        if len(return_vars['molec_ads_ctrs']) != \
689
                len(return_vars['molec_neigh_ctrs']):
690
            err = "'molec_ads_ctrs' and 'molec_neigh_ctrs' must have the same" \
691
                  "number of indides"
692
            logger.error(err)
693
            raise ValueError(err)
694

    
695
        # Facultative options (Default value present)
696
        return_vars['select_magns'] = get_select_magns()
697
        return_vars['confs_per_magn'] = get_confs_per_magn()
698
        return_vars['surf_norm_vect'] = get_surf_norm_vect()
699
        return_vars['disso_atoms'] = get_disso_atoms()
700
        return_vars['ads_algo'] = get_ads_algo()
701
        return_vars['sample_points_per_angle'] = get_pts_per_angle()
702
        return_vars['max_structures'] = get_max_structures()
703
        return_vars['collision_threshold'] = get_coll_thrsld()
704
        return_vars['min_coll_height'] = get_min_coll_height()
705
        cart_axes = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0],
706
                     [-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, -1.0]]
707
        if return_vars['min_coll_height'] is not False and \
708
                return_vars['surf_norm_vect'].tolist() not in cart_axes:
709
            logger.warning("'min_coll_height' option is only implemented for "
710
                           "'surf_norm_vect' to be one of the x, y or z axes.")
711

    
712
    # Refinement
713
    if refinement:
714
        if not dos_inp.has_section('Refinement'):
715
            logger.error(no_sect_err % 'Refinement')
716
            raise NoSectionError('Refinement')
717
        # Mandatory options
718
        # Checks whether the mandatory options are present.
719
        ref_mand_opts = ['refine_inp_file']
720
        for opt in ref_mand_opts:
721
            if not dos_inp.has_option('Refinement', opt):
722
                logger.error(no_opt_err % (opt, 'Refinement'))
723
                raise NoOptionError(opt, 'Refinement')
724
        return_vars['refine_inp_file'] = get_refine_inp_file()
725

    
726
        # Facultative options (Default value present)
727
        return_vars['energy_cutoff'] = get_energy_cutoff()
728
        # end energy_cutoff
729

    
730
    return_vars_str = "\n\t".join([str(key) + ": " + str(value)
731
                                   for key, value in return_vars.items()])
732
    logger.info(
733
        f'Correctly read {in_file} parameters: \n\n\t{return_vars_str}\n')
734

    
735
    return return_vars
736

    
737

    
738
if __name__ == "__main__":
739
    import sys
740

    
741
    print(read_input(sys.argv[1]))