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

dockonsurf / modules / dos_input.py @ 5fb01677

Historique | Voir | Annoter | Télécharger (26,58 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_qw: Gets 'max_qw' 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_min_confs: Gets 'min_confs' 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
    check_expect_val(dos_inp.get('Global', 'run_type').lower(), run_type_vals)
171

    
172
    run_type = dos_inp.get('Global', 'run_type').lower()
173
    if 'isolated' in run_type:
174
        isolated = True
175
    if 'screening' in run_type:
176
        screening = True
177
    if 'refinement' in run_type:
178
        refinement = True
179
    if 'adsorption' in run_type:
180
        screening, refinement = (True, True)
181
    if 'full' in run_type:
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', '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', 'false']
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_qw():
230
    err_msg = num_error % ('max_qw', 'positive integer')
231
    max_qw = try_command(dos_inp.getint, [(ValueError, err_msg)],
232
                         'Global', 'max_qw', fallback=3)
233

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

    
239

    
240
def get_special_atoms():
241
    from ase.data import chemical_symbols
242

    
243
    spec_at_err = '\'special_atoms\' does not have an adequate format.\n' \
244
                  'Adequate format: (Fe1 Fe) (O1 O)'
245
    special_atoms = dos_inp.get('Global', 'special_atoms', fallback="False")
246
    if special_atoms.lower() in turn_false_answers:
247
        special_atoms = False
248
    else:
249
        # Converts the string into a list of tuples
250
        lst_tple = [tuple(pair.replace("(", "").split()) for pair in
251
                    special_atoms.split(")")[:-1]]
252
        if len(lst_tple) == 0:
253
            logger.error(spec_at_err)
254
            raise ValueError(spec_at_err)
255
        for i, tup in enumerate(lst_tple):
256
            if type(tup) is not tuple or len(tup) != 2:
257
                logger.error(spec_at_err)
258
                raise ValueError(spec_at_err)
259
            if tup[1].capitalize() not in chemical_symbols:
260
                elem_err = "The second element of the couple should be an " \
261
                           "actual element of the periodic table"
262
                logger.error(elem_err)
263
                raise ValueError(elem_err)
264
            if tup[0].capitalize() in chemical_symbols:
265
                elem_err = "The first element of the couple is already an " \
266
                           "actual element of the periodic table, "
267
                logger.error(elem_err)
268
                raise ValueError(elem_err)
269
            for j, tup2 in enumerate(lst_tple):
270
                if j <= i:
271
                    continue
272
                if tup2[0] == tup[0]:
273
                    label_err = f'You have specified the label {tup[0]} to ' \
274
                                f'more than one special atom'
275
                    logger.error(label_err)
276
                    raise ValueError(label_err)
277
        special_atoms = lst_tple
278
    return special_atoms
279

    
280

    
281
# Isolated
282

    
283
def get_isol_inp_file():
284
    isol_inp_file = dos_inp.get('Isolated', 'isol_inp_file')
285
    if not os.path.isfile(isol_inp_file):
286
        logger.error(f'File {isol_inp_file} not found')
287
        raise FileNotFoundError(f'File {isol_inp_file} not found')
288
    return isol_inp_file
289

    
290

    
291
def get_molec_file():
292
    molec_file = dos_inp.get('Isolated', 'molec_file')
293
    if not os.path.isfile(molec_file):
294
        logger.error(f'File {molec_file} not found')
295
        raise FileNotFoundError(f'File {molec_file} not found')
296
    return molec_file
297

    
298

    
299
def get_num_conformers():
300
    err_msg = num_error % ('num_conformers', 'positive integer')
301
    num_conformers = try_command(dos_inp.getint, [(ValueError, err_msg)],
302
                                 'Isolated', 'num_conformers', fallback=100)
303
    if num_conformers < 1:
304
        logger.error(err_msg)
305
        raise ValueError(err_msg)
306
    return num_conformers
307

    
308

    
309
def get_num_prom_cand():
310
    err_msg = num_error % ('num_prom_cand', 'positive integer')
311
    num_prom_cand = try_command(dos_inp.getint, [(ValueError, err_msg)],
312
                                'Isolated', 'num_prom_cand', fallback=3)
313
    if num_prom_cand < 1:
314
        logger.error(err_msg)
315
        raise ValueError(err_msg)
316
    return num_prom_cand
317

    
318

    
319
def get_iso_rmsd():
320
    err_msg = num_error % ('iso_rmsd', 'positive decimal number')
321
    iso_rmsd = try_command(dos_inp.getfloat, [(ValueError, err_msg)],
322
                           'Isolated', 'iso_rmsd', fallback=0.05)
323
    if iso_rmsd <= 0.0:
324
        logger.error(err_msg)
325
        raise ValueError(err_msg)
326
    return iso_rmsd
327

    
328

    
329
def get_min_confs():
330
    err_msg = "'min_confs' should be have a boolean value (True or False)"
331
    min_confs = try_command(dos_inp.getboolean,
332
                            [(ValueError, err_msg)],
333
                            'Isolated', 'min_confs', fallback=True)
334
    return min_confs
335

    
336

    
337
# Screening
338

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

    
346

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

    
354

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

    
379
    return sites
380

    
381

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

    
407
    return molec_ads_ctrs
408

    
409

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

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

    
435
    return molec_neigh_ctrs
436

    
437

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

    
449

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

    
459

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

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

    
478
    return np.array(surf_norm_vect)
479

    
480

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

    
487

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

    
499

    
500
def get_coll_thrsld():
501
    err_msg = num_error % ('collision_threshold',
502
                           'positive decimal number')
503

    
504
    coll_thrsld = try_command(dos_inp.getfloat,
505
                              [(ValueError, err_msg)],
506
                              'Screening', 'collision_threshold', fallback=1.2)
507
    if coll_thrsld <= 0:
508
        logger.error(err_msg)
509
        raise ValueError(err_msg)
510

    
511
    return coll_thrsld
512

    
513

    
514
def get_screen_rmsd():
515
    err_msg = num_error % ('screen_rmsd', 'positive decimal number')
516
    screen_rmsd = try_command(dos_inp.getfloat,
517
                              [(ValueError, err_msg)],
518
                              'Screening', 'screen_rmsd', fallback=0.05)
519
    if screen_rmsd <= 0:
520
        logger.error(err_msg)
521
        raise ValueError(err_msg)
522

    
523
    return screen_rmsd
524

    
525

    
526
def get_min_coll_height():
527
    err_msg = num_error % ('min_coll_height', 'decimal number')
528
    min_coll_height = dos_inp.get('Screening', 'min_coll_height',
529
                                  fallback="False")
530
    if min_coll_height.lower() in turn_false_answers:
531
        min_coll_height = False
532
    else:
533
        min_coll_height = try_command(float, [(ValueError, err_msg)],
534
                                      min_coll_height)
535

    
536
    return min_coll_height
537

    
538

    
539
def get_try_disso():
540
    err_msg = "try_disso should be have a boolean value (True or False)"
541
    try_disso = try_command(dos_inp.getboolean,
542
                            [(ValueError, err_msg)],
543
                            'Screening', 'try_disso', fallback=False)
544
    return try_disso
545

    
546

    
547
# Refinement
548

    
549
def get_refine_inp_file():  # TODO if not specified try isol_inp_file.
550
    refine_inp_file = dos_inp.get('Refinement', 'refine_inp_file')
551
    if not os.path.isfile(refine_inp_file):
552
        logger.error(f'File {refine_inp_file} not found')
553
        raise FileNotFoundError(f'File {refine_inp_file} not found')
554

    
555
    return refine_inp_file
556

    
557

    
558
def get_energy_cutoff():
559
    err_msg = num_error % ('energy_cutoff', 'positive decimal number')
560
    energy_cutoff = try_command(dos_inp.getfloat,
561
                                [(ValueError, err_msg)],
562
                                'Refinement', 'energy_cutoff', fallback=0.5)
563
    if energy_cutoff < 0:
564
        logger.error(err_msg)
565
        raise ValueError(err_msg)
566
    return energy_cutoff
567

    
568

    
569
def read_input(in_file):
570
    err = False
571
    try:
572
        dos_inp.read(in_file)
573
    except MissingSectionHeaderError as e:
574
        logger.error('There are options in the input file without a Section '
575
                     'header')
576
        err = e
577
    except DuplicateOptionError as e:
578
        logger.error('There is an option in the input file that has been '
579
                     'specified more than once, possibly due to the lack of a '
580
                     'Section header')
581
        err = e
582
    except Exception as e:
583
        err = e
584
    else:
585
        err = False
586
    finally:
587
        if isinstance(err, BaseException):
588
            raise err
589

    
590
    return_vars = {}
591

    
592
    # Global
593
    if not dos_inp.has_section('Global'):
594
        logger.error(no_sect_err % 'Global')
595
        raise NoSectionError('Global')
596

    
597
    # Mandatory options
598
    # Checks whether the mandatory options 'run_type', 'code', etc. are present.
599
    glob_mand_opts = ['run_type', 'batch_q_sys']
600
    for opt in glob_mand_opts:
601
        if not dos_inp.has_option('Global', opt):
602
            logger.error(no_opt_err % (opt, 'Global'))
603
            raise NoOptionError(opt, 'Global')
604

    
605
    # Gets which sections are to be carried out
606
    isolated, screening, refinement = get_run_type()
607
    return_vars['isolated'] = isolated
608
    return_vars['screening'] = screening
609
    return_vars['refinement'] = refinement
610
    return_vars['batch_q_sys'] = get_batch_q_sys()
611

    
612
    # Dependent options:
613
    return_vars['code'] = get_code()
614
    if return_vars['batch_q_sys'] and return_vars['batch_q_sys'] != 'local':
615
        if not dos_inp.has_option('Global', 'subm_script'):
616
            logger.error(no_opt_err % ('subm_script', 'Global'))
617
            raise NoOptionError('subm_script', 'Global')
618
        return_vars['subm_script'] = get_subm_script()
619
        return_vars['max_qw'] = get_max_qw()
620

    
621
    # Facultative options (Default/Fallback value present)
622
    return_vars['project_name'] = get_project_name()
623
    return_vars['relaunch_err'] = get_relaunch_err()
624
    return_vars['special_atoms'] = get_special_atoms()
625

    
626
    # Isolated
627
    if isolated:
628
        if not dos_inp.has_section('Isolated'):
629
            logger.error(no_sect_err % 'Isolated')
630
            raise NoSectionError('Isolated')
631
        # Mandatory options
632
        # Checks whether the mandatory options are present.
633
        iso_mand_opts = ['isol_inp_file', 'molec_file']
634
        for opt in iso_mand_opts:
635
            if not dos_inp.has_option('Isolated', opt):
636
                logger.error(no_opt_err % (opt, 'Isolated'))
637
                raise NoOptionError(opt, 'Isolated')
638
        return_vars['isol_inp_file'] = get_isol_inp_file()
639
        if 'code ' in return_vars:
640
            check_inp_file(return_vars['isol_inp_file'], return_vars['code'])
641
        return_vars['molec_file'] = get_molec_file()
642

    
643
        # Facultative options (Default/Fallback value present)
644
        return_vars['num_conformers'] = get_num_conformers()
645
        # return_vars['num_prom_cand'] = get_num_prom_cand()
646
        # return_vars['iso_rmsd'] = get_iso_rmsd()
647
        return_vars['min_confs'] = get_min_confs()
648

    
649
    # Screening
650
    if screening:
651
        if not dos_inp.has_section('Screening'):
652
            logger.error(no_sect_err % 'Screening')
653
            raise NoSectionError('Screening')
654
        # Mandatory options:
655
        # Checks whether the mandatory options are present.
656
        screen_mand_opts = ['screen_inp_file', 'surf_file', 'sites',
657
                            'molec_ads_ctrs', 'molec_neigh_ctrs']
658
        for opt in screen_mand_opts:
659
            if not dos_inp.has_option('Screening', opt):
660
                logger.error(no_opt_err % (opt, 'Screening'))
661
                raise NoOptionError(opt, 'Screening')
662
        return_vars['screen_inp_file'] = get_screen_inp_file()
663
        return_vars['surf_file'] = get_surf_file()
664
        return_vars['sites'] = get_sites()
665
        return_vars['molec_ads_ctrs'] = get_molec_ads_ctrs()
666
        return_vars['molec_neigh_ctrs'] = get_molec_neigh_ctrs()
667
        if len(return_vars['molec_ads_ctrs']) != \
668
                len(return_vars['molec_neigh_ctrs']):
669
            err = "'molec_ads_ctrs' and 'molec_neigh_ctrs' must have the same" \
670
                  "number of indides"
671
            logger.error(err)
672
            raise ValueError(err)
673

    
674
        # Facultative options (Default value present)
675
        return_vars['select_magns'] = get_select_magns()
676
        return_vars['confs_per_magn'] = get_confs_per_magn()
677
        return_vars['surf_norm_vect'] = get_surf_norm_vect()
678
        return_vars['try_disso'] = get_try_disso()
679
        return_vars['ads_algo'] = get_ads_algo()
680
        return_vars['sample_points_per_angle'] = get_pts_per_angle()
681
        return_vars['collision_threshold'] = get_coll_thrsld()
682
        return_vars['min_coll_height'] = get_min_coll_height()
683
        cart_coords = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]
684
        if return_vars['min_coll_height'] is not False and \
685
                list(map(abs, return_vars['surf_norm_vect'].tolist())) not in \
686
                cart_coords:
687
            logger.warning("'min_coll_height' option is only implemented for "
688
                           "'surf_norm_vect' to be one of the x, y or z axes")
689

    
690
    # Refinement
691
    if refinement:
692
        if not dos_inp.has_section('Refinement'):
693
            logger.error(no_sect_err % 'Refinement')
694
            raise NoSectionError('Refinement')
695
        # Mandatory options
696
        # Checks whether the mandatory options are present.
697
        ref_mand_opts = ['refine_inp_file']
698
        for opt in ref_mand_opts:
699
            if not dos_inp.has_option('Refinement', opt):
700
                logger.error(no_opt_err % (opt, 'Refinement'))
701
                raise NoOptionError(opt, 'Refinement')
702
        return_vars['refine_inp_file'] = get_refine_inp_file()
703

    
704
        # Facultative options (Default value present)
705
        return_vars['energy_cutoff'] = get_energy_cutoff()
706
        # end energy_cutoff
707

    
708
    return_vars_str = "\n\t".join([str(key) + ": " + str(value)
709
                                   for key, value in return_vars.items()])
710
    logger.info(
711
        f'Correctly read {in_file} parameters: \n\n\t{return_vars_str}\n')
712

    
713
    return return_vars
714

    
715

    
716
if __name__ == "__main__":
717
    import sys
718

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