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

dockonsurf / modules / dos_input.py @ b4aef3d7

Historique | Voir | Annoter | Télécharger (25,15 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
from modules.utilities import try_command
51

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

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

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

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

    
68

    
69
def str2lst(cmplx_str, func=int):  # TODO: enable deeper level of nested lists
70
    """Converts a string of integers, and groups of them, to a list.
71

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

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

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

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

    
96
    cmplx_str = cmplx_str.replace(';', ' ').replace('[', '(').replace(
97
        ']', ')').replace('{', '(').replace('}', ')')
98

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

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

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

    
123
    init_list = list(map(func, init_list))
124

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

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

    
134

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

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

    
151
    return True
152

    
153

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

    
161

    
162
def get_run_type():
163
    isolated, screening, refinement = (False, False, False)
164
    run_type_vals = ['isolated', 'screening', 'refinement', 'adsorption',
165
                     'full']
166
    check_expect_val(dos_inp.get('Global', 'run_type').lower(), run_type_vals)
167

    
168
    run_type = dos_inp.get('Global', 'run_type').lower()
169
    if 'isolated' in run_type:
170
        isolated = True
171
    if 'screening' in run_type:
172
        screening = True
173
    if 'refinement' in run_type:
174
        refinement = True
175
    if 'adsorption' in run_type:
176
        screening, refinement = (True, True)
177
    if 'full' in run_type:
178
        isolated, screening, refinement = (True, True, True)
179

    
180
    return isolated, screening, refinement
181

    
182

    
183
def get_code():
184
    code_vals = ['cp2k']
185
    check_expect_val(dos_inp.get('Global', 'code').lower(), code_vals)
186
    code = dos_inp.get('Global', 'code').lower()
187
    return code
188

    
189

    
190
def get_batch_q_sys():
191
    batch_q_sys_vals = ['sge', 'lsf', 'local', 'none']
192
    check_expect_val(dos_inp.get('Global', 'batch_q_sys').lower(),
193
                     batch_q_sys_vals)
194
    batch_q_sys = dos_inp.get('Global', 'batch_q_sys').lower()
195
    return batch_q_sys
196

    
197

    
198
def get_subm_script():
199
    subm_script = dos_inp.get('Global', 'subm_script', fallback=False)
200
    if subm_script and not os.path.isfile(subm_script):
201
        logger.error(f'File {subm_script} not found')
202
        raise FileNotFoundError(f'File {subm_script} not found')
203
    return subm_script
204

    
205

    
206
def get_project_name():
207
    project_name = dos_inp.get('Global', 'project_name', fallback='')
208
    return project_name
209

    
210

    
211
def get_relaunch_err():
212
    relaunch_err_vals = ['geo_not_conv', 'false']
213
    relaunch_err = dos_inp.get('Global', 'relaunch_err',
214
                               fallback="False")
215
    if relaunch_err.lower() in turn_false_answers:
216
        return False
217
    else:
218
        check_expect_val(relaunch_err.lower(), relaunch_err_vals)
219
    return relaunch_err
220

    
221

    
222
def get_max_qw():
223
    err_msg = num_error % ('max_qw', 'positive integer')
224
    max_qw = try_command(dos_inp.getint, [(ValueError, err_msg)],
225
                         'Global', 'max_qw', fallback=3)
226

    
227
    if max_qw < 1:
228
        logger.error(num_error % ('max_qw', 'positive integer'))
229
        raise ValueError(num_error % ('max_qw', 'positive integer'))
230
    return max_qw
231

    
232

    
233
def get_special_atoms():
234
    from ase.data import chemical_symbols
235

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

    
273

    
274
def get_isol_inp_file():
275
    isol_inp_file = dos_inp.get('Isolated', 'isol_inp_file')
276
    if not os.path.isfile(isol_inp_file):
277
        logger.error(f'File {isol_inp_file} not found')
278
        raise FileNotFoundError(f'File {isol_inp_file} not found')
279
    return isol_inp_file
280

    
281

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

    
289

    
290
def get_cluster_magns():
291
    clust_magns_vals = ['energy', 'moi']
292
    cluster_magns_str = dos_inp.get('Isolated', 'cluster_magns',
293
                                    fallback='energy')
294
    cluster_magns_str.replace(',', ' ').replace(';', ' ')
295
    cluster_magns = cluster_magns_str.split(' ')
296
    cluster_magns = [m.lower() for m in cluster_magns]
297
    for m in cluster_magns:
298
        check_expect_val(m, clust_magns_vals)
299
    return cluster_magns
300

    
301

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

    
311

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

    
321

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

    
331

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

    
339

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

    
347

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

    
372
    return sites
373

    
374

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

    
400
    return molec_ads_ctrs
401

    
402

    
403
def get_surf_file():
404
    surf_file = dos_inp.get('Screening', 'surf_file')
405
    if not os.path.isfile(surf_file):
406
        logger.error(f'File {surf_file} not found')
407
        raise FileNotFoundError(f'File {surf_file} not found')
408
    return surf_file
409

    
410

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

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

    
436
    return molec_neigh_ctrs
437

    
438

    
439
def get_surf_norm_vect():
440
    import numpy as np
441
    err = "'surf_norm_vect' must be either a 3 component vector or 'x', 'y' " \
442
          "or 'z'"
443
    coords = {'x': [1.0, 0.0, 0.0],
444
              'y': [0.0, 1.0, 0.0],
445
              'z': [0.0, 0.0, 1.0]}
446
    surf_norm_vect_str = dos_inp.get('Screening', 'surf_norm_vect',
447
                                     fallback=[0.0, 0.0, 1.0])
448
    try:
449
        surf_norm_vect = str2lst(surf_norm_vect_str, float)
450
        if len(surf_norm_vect) != 3:
451
            logger.error(err)
452
            raise ValueError(err)
453
    except ValueError:
454
        if len(surf_norm_vect_str) != 1 or surf_norm_vect_str not in coords:
455
            logger.error(err)
456
            raise ValueError(err)
457
        else:
458
            surf_norm_vect = coords[surf_norm_vect_str]
459
    return np.array(surf_norm_vect)
460

    
461

    
462
def get_try_disso():
463
    err_msg = "try_disso should be have a boolean value (True or False)"
464
    try_disso = try_command(dos_inp.getboolean,
465
                            [(ValueError, err_msg)],
466
                            'Screening', 'try_disso', fallback=False)
467
    return try_disso
468

    
469

    
470
def get_pts_per_angle():
471
    err_msg = num_error % ('sample_points_per_angle', 'positive integer')
472
    pts_per_angle = try_command(dos_inp.getint,
473
                                [(ValueError, err_msg)],
474
                                'Screening', 'sample_points_per_angle',
475
                                fallback=3)
476
    if pts_per_angle <= 0:
477
        logger.error(err_msg)
478
        raise ValueError(err_msg)
479
    return pts_per_angle
480

    
481

    
482
def get_coll_thrsld():
483
    err_msg = num_error % ('collision_threshold',
484
                           'positive decimal number')
485

    
486
    coll_thrsld = try_command(dos_inp.getfloat,
487
                              [(ValueError, err_msg)],
488
                              'Screening', 'collision_threshold', fallback=1.2)
489
    if coll_thrsld <= 0:
490
        logger.error(err_msg)
491
        raise ValueError(err_msg)
492

    
493
    return coll_thrsld
494

    
495

    
496
def get_screen_rmsd():
497
    err_msg = num_error % ('screen_rmsd', 'positive decimal number')
498
    screen_rmsd = try_command(dos_inp.getfloat,
499
                              [(ValueError, err_msg)],
500
                              'Screening', 'screen_rmsd', fallback=0.05)
501
    if screen_rmsd <= 0:
502
        logger.error(err_msg)
503
        raise ValueError(err_msg)
504

    
505
    return screen_rmsd
506

    
507

    
508
def get_coll_bottom():
509
    err_msg = num_error % ('collision_bottom', 'decimal number')
510
    coll_bottom = dos_inp.get('Screening', 'collision_bottom',
511
                              fallback="False")
512
    if coll_bottom.lower() in turn_false_answers:
513
        coll_bottom = False
514
    else:
515
        coll_bottom = try_command(float, [(ValueError, err_msg)], coll_bottom)
516

    
517
    return coll_bottom
518

    
519

    
520
def get_refine_inp_file():
521
    refine_inp_file = dos_inp.get('Refinement', 'refine_inp_file')
522
    if not os.path.isfile(refine_inp_file):
523
        logger.error(f'File {refine_inp_file} not found')
524
        raise FileNotFoundError(f'File {refine_inp_file} not found')
525

    
526
    return refine_inp_file
527

    
528

    
529
def get_energy_cutoff():
530
    err_msg = num_error % ('energy_cutoff', 'positive decimal number')
531
    energy_cutoff = try_command(dos_inp.getfloat,
532
                                [(ValueError, err_msg)],
533
                                'Refinement', 'energy_cutoff', fallback=0.5)
534
    if energy_cutoff < 0:
535
        logger.error(err_msg)
536
        raise ValueError(err_msg)
537
    return energy_cutoff
538

    
539

    
540
def read_input(in_file):
541
    err = False
542
    try:
543
        dos_inp.read(in_file)
544
    except MissingSectionHeaderError as e:
545
        logger.error('There are options in the input file without a Section '
546
                     'header')
547
        err = e
548
    except DuplicateOptionError as e:
549
        logger.error('There is an option in the input file that has been '
550
                     'specified more than once, possibly due to the lack of a '
551
                     'Section header')
552
        err = e
553
    except Exception as e:
554
        err = e
555
    else:
556
        err = False
557
    finally:
558
        if isinstance(err, BaseException):
559
            raise err
560

    
561
    return_vars = {}
562

    
563
    # Global
564
    if not dos_inp.has_section('Global'):
565
        logger.error(no_sect_err % 'Global')
566
        raise NoSectionError('Global')
567

    
568
    # Mandatory options
569
    # Checks whether the mandatory options 'run_type', 'code', etc. are present.
570
    glob_mand_opts = ['run_type', 'code', 'batch_q_sys']
571
    for opt in glob_mand_opts:
572
        if not dos_inp.has_option('Global', opt):
573
            logger.error(no_opt_err % (opt, 'Global'))
574
            raise NoOptionError(opt, 'Global')
575

    
576
    # Gets which sections are to be carried out
577
    isolated, screening, refinement = get_run_type()
578
    return_vars['isolated'] = isolated
579
    return_vars['screening'] = screening
580
    return_vars['refinement'] = refinement
581
    return_vars['code'] = get_code()
582
    return_vars['batch_q_sys'] = get_batch_q_sys()
583

    
584
    # Dependent options:
585
    return_vars['subm_script'] = get_subm_script()
586
    if return_vars['batch_q_sys'] != 'local' and not return_vars['subm_script']:
587
        sub_err = "'subm_script' must be provided if 'batch_q_sys' is not local"
588
        logger.error(sub_err)
589
        raise ValueError(sub_err)
590

    
591
    # Facultative options (Default/Fallback value present)
592
    return_vars['project_name'] = get_project_name()
593
    return_vars['relaunch_err'] = get_relaunch_err()
594
    return_vars['max_qw'] = get_max_qw()
595
    return_vars['special_atoms'] = get_special_atoms()
596

    
597
    # Isolated
598
    if isolated:
599
        if not dos_inp.has_section('Isolated'):
600
            logger.error(no_sect_err % 'Isolated')
601
            raise NoSectionError('Isolated')
602
        # Mandatory options
603
        # Checks whether the mandatory options are present.
604
        iso_mand_opts = ['isol_inp_file', 'molec_file']
605
        for opt in iso_mand_opts:
606
            if not dos_inp.has_option('Isolated', opt):
607
                logger.error(no_opt_err % (opt, 'Isolated'))
608
                raise NoOptionError(opt, 'Isolated')
609
        return_vars['isol_inp_file'] = get_isol_inp_file()
610
        check_inp_file(return_vars['isol_inp_file'], return_vars['code'])
611
        return_vars['molec_file'] = get_molec_file()
612

    
613
        # Facultative options (Default/Fallback value present)
614
        return_vars['cluster_magns'] = get_cluster_magns()
615
        return_vars['num_conformers'] = get_num_conformers()
616
        # return_vars['num_prom_cand'] = get_num_prom_cand()
617
        # return_vars['iso_rmsd'] = get_iso_rmsd()
618
        return_vars['min_confs'] = get_min_confs()
619

    
620
    # Screening
621
    if screening:
622
        if not dos_inp.has_section('Screening'):
623
            logger.error(no_sect_err % 'Screening')
624
            raise NoSectionError('Screening')
625
        # Mandatory options:
626
        # Checks whether the mandatory options are present.
627
        screen_mand_opts = ['sites', 'molec_ads_ctrs', 'screen_inp_file',
628
                            'surf_file', 'molec_neigh_ctrs']
629
        for opt in screen_mand_opts:
630
            if not dos_inp.has_option('Screening', opt):
631
                logger.error(no_opt_err % (opt, 'Screening'))
632
                raise NoOptionError(opt, 'Screening')
633
        return_vars['screen_inp_file'] = get_screen_inp_file()
634
        return_vars['sites'] = get_sites()
635
        return_vars['molec_ads_ctrs'] = get_molec_ads_ctrs()
636
        return_vars['surf_file'] = get_surf_file()
637
        return_vars['molec_neigh_ctrs'] = get_molec_neigh_ctrs()
638
        if len(return_vars['molec_ads_ctrs']) != \
639
                len(return_vars['molec_neigh_ctrs']):
640
            err = "'molec_ads_ctrs' and 'molec_neigh_ctrs' must have the same" \
641
                  "number of indides"
642
            logger.error(err)
643
            raise ValueError(err)
644

    
645
        # Facultative options (Default value present)
646
        return_vars['surf_norm_vect'] = get_surf_norm_vect()
647
        return_vars['try_disso'] = get_try_disso()
648
        return_vars['sample_points_per_angle'] = get_pts_per_angle()
649
        return_vars['collision_threshold'] = get_coll_thrsld()
650
        # return_vars['screen_rmsd'] = get_screen_rmsd()
651
        return_vars['collision_bottom'] = get_coll_bottom()
652

    
653
    # Refinement
654
    if refinement:
655
        if not dos_inp.has_section('Refinement'):
656
            logger.error(no_sect_err % 'Refinement')
657
            raise NoSectionError('Refinement')
658
        # Mandatory options
659
        # Checks whether the mandatory options are present.
660
        ref_mand_opts = ['refine_inp_file']
661
        for opt in ref_mand_opts:
662
            if not dos_inp.has_option('Refinement', opt):
663
                logger.error(no_opt_err % (opt, 'Refinement'))
664
                raise NoOptionError(opt, 'Refinement')
665
        return_vars['refine_inp_file'] = get_refine_inp_file()
666

    
667
        # Facultative options (Default value present)
668
        return_vars['energy_cutoff'] = get_energy_cutoff()
669
        # end energy_cutoff
670

    
671
    return_vars_str = "\n\t".join([str(key) + ": " + str(value)
672
                                   for key, value in return_vars.items()])
673
    logger.info(
674
        f'Correctly read {in_file} parameters: \n\n\t{return_vars_str}\n')
675

    
676
    return return_vars
677

    
678

    
679
if __name__ == "__main__":
680
    import sys
681

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