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

dockonsurf / modules / dos_input.py @ 0db30d07

Historique | Voir | Annoter | Télécharger (23,92 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

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

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

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

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

    
68

    
69
def try_command(command, expct_error_types: list, *args, **kwargs):
70
    """Try to run a command and record exceptions (expected and not) on a log.
71
    
72
    @param command: method or function, the command to be executed.
73
    @param expct_error_types: tuple of tuples, every inner tuple is supposed to
74
    contain an exception type (eg. ValueError, TypeError, etc.) to be caught and
75
    a message to print in the log and on the screen explaining the exception.
76
    Error types that are not allow to be called with a custom message as only
77
    error argument are not supported.
78
    The outer tuple encloses all couples of error types and their relative
79
    messages.
80
    *args and **kwargs: arguments and keyword-arguments of the command to be
81
    executed.
82
    When trying to run 'command' with its args and kwargs, if an exception
83
    present on the 'error_types' occurs, its relative error message is recorded
84
    on the log and a same type exception is raised with the custom message.
85
    """
86

    
87
    err = False
88
    try:
89
        return_val = command(*args, **kwargs)
90
    except Exception as e:
91
        for expct_err in expct_error_types:
92
            if isinstance(e, expct_err[0]):
93
                logger.error(expct_err[1])
94
                err = expct_err[0](expct_err[1])
95
                break
96
        else:
97
            logger.exception(unexp_error)
98
            err = e
99
    else:
100
        err = False
101
        return return_val
102
    finally:
103
        if isinstance(err, BaseException):
104
            raise err
105

    
106

    
107
def str2lst(cmplx_str):  # TODO: enable deeper level of nested lists
108
    """Converts a string of integers, and groups of them, to a list.
109

110
    Keyword arguments:
111
    @param cmplx_str: str, string of integers and groups of them enclosed by
112
    parentheses-like characters.
113
    - Group enclosers: '()' '[]' and '{}'.
114
    - Integer separators: ',' ';' and ' '.
115
    - Nested groups are not allowed: '3 ((6 7) 8) 4'.
116

117
    @return list, list of integers, or list of integers in the case they were
118
    grouped. First, the singlets are placed, and then the groups in input order.
119

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

    
123
    # Checks
124
    error_msg = "Function argument should be a str,sequence of integer " \
125
                "numbers separated by ',' ';' or ' '." \
126
                "\nThey can be grouped in parentheses-like enclosers: '()', " \
127
                "'[]' or {}. Nested groups are not allowed. \n" \
128
                "eg. 128,(135 138;141) 87 {45, 68}"
129
    cmplx_str = try_command(cmplx_str.replace, [(AttributeError, error_msg)],
130
                            ',', ' ')
131

    
132
    cmplx_str = cmplx_str.replace(';', ' ').replace('[', '(').replace(
133
        ']', ')').replace('{', '(').replace('}', ')')
134

    
135
    try_command(list, [(ValueError, error_msg)], map(int, cmplx_str.replace(
136
        ')', '').replace('(', '').split()))
137

    
138
    deepness = 0
139
    for el in cmplx_str.split():
140
        if '(' in el:
141
            deepness += 1
142
        if ')' in el:
143
            deepness += -1
144
        if deepness > 1 or deepness < 0:
145
            logger.error(error_msg)
146
            raise ValueError(error_msg)
147

    
148
    init_list = cmplx_str.split()
149
    start_group = []
150
    end_group = []
151
    for i, element in enumerate(init_list):
152
        if '(' in element:
153
            start_group.append(i)
154
            init_list[i] = element.replace('(', '')
155
        if ')' in element:
156
            end_group.append(i)
157
            init_list[i] = element.replace(')', '')
158

    
159
    init_list = list(map(int, init_list))
160

    
161
    new_list = []
162
    for start_el, end_el in zip(start_group, end_group):
163
        new_list.append(init_list[start_el:end_el + 1])
164

    
165
    for v in new_list:
166
        for el in v:
167
            init_list.remove(el)
168
    return init_list + new_list
169

    
170

    
171
def check_expect_val(value, expect_vals):
172
    """Checks whether an option lies within its expected values.
173

174
    Keyword arguments:
175
    @param value: The variable to check if its value lies within the expected
176
    ones
177
    @param expect_vals: list, list of values allowed for the present option.
178
    @raise ValueError: if the value is not among the expected ones.
179
    @return True if the value is among the expected ones.
180
    """
181
    adeq_val_err = "'%s' is not an adequate value.\n" \
182
                   "Adequate values: %s"
183
    if not any([exp_val in value for exp_val in expect_vals]):
184
        logger.error(adeq_val_err % (value, expect_vals))
185
        raise ValueError(adeq_val_err % (value, expect_vals))
186

    
187
    return True
188

    
189

    
190
def check_inp_file(inp_file, code):
191
    if code == 'cp2k':
192
        from pycp2k import CP2K
193
        cp2k = CP2K()
194
        try_command(cp2k.parse,
195
                    [(UnboundLocalError, "Invalid CP2K input file")], inp_file)
196

    
197

    
198
def get_run_type():
199
    isolated, screening, refinement = (False, False, False)
200
    run_type_vals = ['isolated', 'screening', 'refinement', 'adsorption',
201
                     'full']
202
    check_expect_val(dos_inp.get('Global', 'run_type').lower(), run_type_vals)
203

    
204
    run_type = dos_inp.get('Global', 'run_type').lower()
205
    if 'isolated' in run_type:
206
        isolated = True
207
    if 'screening' in run_type:
208
        screening = True
209
    if 'refinement' in run_type:
210
        refinement = True
211
    if 'adsorption' in run_type:
212
        screening, refinement = (True, True)
213
    if 'full' in run_type:
214
        isolated, screening, refinement = (True, True, True)
215

    
216
    return isolated, screening, refinement
217

    
218

    
219
def get_code():
220
    code_vals = ['cp2k']
221
    check_expect_val(dos_inp.get('Global', 'code').lower(), code_vals)
222
    code = dos_inp.get('Global', 'code').lower()
223
    return code
224

    
225

    
226
def get_batch_q_sys():
227
    batch_q_sys_vals = ['sge', 'lsf', 'local', 'none']
228
    check_expect_val(dos_inp.get('Global', 'batch_q_sys').lower(),
229
                     batch_q_sys_vals)
230
    batch_q_sys = dos_inp.get('Global', 'batch_q_sys').lower()
231
    return batch_q_sys
232

    
233

    
234
def get_subm_script():
235
    subm_script = dos_inp.get('Global', 'subm_script', fallback=False)
236
    if subm_script and not os.path.isfile(subm_script):
237
        logger.error(f'File {subm_script} not found')
238
        raise FileNotFoundError(f'File {subm_script} not found')
239
    return subm_script
240

    
241

    
242
def get_project_name():
243
    project_name = dos_inp.get('Global', 'project_name', fallback='')
244
    return project_name
245

    
246

    
247
def get_relaunch_err():
248
    relaunch_err_vals = ['geo_not_conv', 'false']
249
    relaunch_err = dos_inp.get('Global', 'relaunch_err',
250
                               fallback="False")
251
    if relaunch_err.lower() in turn_false_answers:
252
        return False
253
    else:
254
        check_expect_val(relaunch_err.lower(), relaunch_err_vals)
255
    return relaunch_err
256

    
257

    
258
def get_max_qw():
259
    err_msg = num_error % ('max_qw', 'positive integer')
260
    max_qw = try_command(dos_inp.getint, [(ValueError, err_msg)],
261
                         'Global', 'max_qw', fallback=3)
262

    
263
    if max_qw < 1:
264
        logger.error(num_error % ('max_qw', 'positive integer'))
265
        raise ValueError(num_error % ('max_qw', 'positive integer'))
266
    return max_qw
267

    
268

    
269
def get_special_atoms():
270
    from ase.data import chemical_symbols
271

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

    
309

    
310
def get_isol_inp_file():
311
    isol_inp_file = dos_inp.get('Isolated', 'isol_inp_file')
312
    if not os.path.isfile(isol_inp_file):
313
        logger.error(f'File {isol_inp_file} not found')
314
        raise FileNotFoundError(f'File {isol_inp_file} not found')
315
    return isol_inp_file
316

    
317

    
318
def get_molec_file():
319
    molec_file = dos_inp.get('Isolated', 'molec_file')
320
    if not os.path.isfile(molec_file):
321
        logger.error(f'File {molec_file} not found')
322
        raise FileNotFoundError(f'File {molec_file} not found')
323
    return molec_file
324

    
325

    
326
def get_cluster_magns():
327
    clust_magns_vals = ['energy', 'moi']
328
    cluster_magns_str = dos_inp.get('Isolated', 'cluster_magns',
329
                                    fallback='energy')
330
    cluster_magns_str.replace(',', ' ').replace(';', ' ')
331
    cluster_magns = cluster_magns_str.split(' ')
332
    cluster_magns = [m.lower() for m in cluster_magns]
333
    for m in cluster_magns:
334
        check_expect_val(m, clust_magns_vals)
335
    return cluster_magns
336

    
337

    
338
def get_num_conformers():
339
    err_msg = num_error % ('num_conformers', 'positive integer')
340
    num_conformers = try_command(dos_inp.getint, [(ValueError, err_msg)],
341
                                 'Isolated', 'num_conformers', fallback=100)
342
    if num_conformers < 1:
343
        logger.error(err_msg)
344
        raise ValueError(err_msg)
345
    return num_conformers
346

    
347

    
348
def get_num_prom_cand():
349
    err_msg = num_error % ('num_prom_cand', 'positive integer')
350
    num_prom_cand = try_command(dos_inp.getint, [(ValueError, err_msg)],
351
                                'Isolated', 'num_prom_cand', fallback=3)
352
    if num_prom_cand < 1:
353
        logger.error(err_msg)
354
        raise ValueError(err_msg)
355
    return num_prom_cand
356

    
357

    
358
def get_iso_rmsd():
359
    err_msg = num_error % ('iso_rmsd', 'positive decimal number')
360
    iso_rmsd = try_command(dos_inp.getfloat, [(ValueError, err_msg)],
361
                           'Isolated', 'iso_rmsd', fallback=0.05)
362
    if iso_rmsd <= 0.0:
363
        logger.error(err_msg)
364
        raise ValueError(err_msg)
365
    return iso_rmsd
366

    
367

    
368
def get_min_confs():
369
    err_msg = "'min_confs' should be have a boolean value (True or False)"
370
    min_confs = try_command(dos_inp.getboolean,
371
                            [(ValueError, err_msg)],
372
                            'Isolated', 'min_confs', fallback=True)
373
    return min_confs
374

    
375

    
376
def get_screen_inp_file():
377
    screen_inp_file = dos_inp.get('Screening', 'screen_inp_file')
378
    if not os.path.isfile(screen_inp_file):
379
        logger.error(f'File {screen_inp_file} not found')
380
        raise FileNotFoundError(f'File {screen_inp_file} not found')
381
    return screen_inp_file
382

    
383

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

    
408
    return sites
409

    
410

    
411
def get_molec_ads_ctrs():
412
    err_msg = 'The value of molec_ads_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_ads_ctrs = try_command(str2lst,
418
                                 [(ValueError, err_msg),
419
                                  (AttributeError, err_msg)],
420
                                 dos_inp.get('Screening', 'molec_ads_ctrs'))
421
    # Check all elements of the list (of lists) are positive integers
422
    for ctr in molec_ads_ctrs:
423
        if type(ctr) is list:
424
            for atom in ctr:
425
                if atom < 0:
426
                    logger.error(err_msg)
427
                    raise ValueError(err_msg)
428
        elif type(ctr) is 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_ads_ctrs
437

    
438

    
439
def get_try_disso():
440
    err_msg = "try_disso should be have a boolean value (True or False)"
441
    try_disso = try_command(dos_inp.getboolean,
442
                            [(ValueError, err_msg)],
443
                            'Screening', 'try_disso', fallback=False)
444
    return try_disso
445

    
446

    
447
def get_pts_per_angle():
448
    err_msg = num_error % ('sample_points_per_angle',
449
                           'positive integer')
450
    pts_per_angle = try_command(dos_inp.getint,
451
                                [(ValueError, err_msg)],
452
                                'Screening', 'sample_points_per_angle',
453
                                fallback=3)
454

    
455
    return pts_per_angle
456

    
457

    
458
def get_coll_thrsld():
459
    err_msg = num_error % ('collision_threshold',
460
                           'positive decimal number')
461

    
462
    coll_thrsld = try_command(dos_inp.getfloat,
463
                              [(ValueError, err_msg)],
464
                              'Screening', 'collision_threshold', fallback=1.2)
465
    if coll_thrsld <= 0:
466
        logger.error(err_msg)
467
        raise ValueError(err_msg)
468

    
469
    return coll_thrsld
470

    
471

    
472
def get_screen_rmsd():
473
    err_msg = num_error % ('screen_rmsd', 'positive decimal number')
474
    screen_rmsd = try_command(dos_inp.getfloat,
475
                              [(ValueError, err_msg)],
476
                              'Screening', 'screen_rmsd', fallback=0.05)
477
    if screen_rmsd <= 0:
478
        logger.error(err_msg)
479
        raise ValueError(err_msg)
480

    
481
    return screen_rmsd
482

    
483

    
484
def get_coll_bottom_z():
485
    err_msg = num_error % ('collision_bottom_z', 'decimal number')
486
    coll_bottom_z = dos_inp.get('Screening', 'collision_bottom_z',
487
                                fallback="False")
488
    if coll_bottom_z.lower() in turn_false_answers:
489
        coll_bottom_z = False
490
    else:
491
        coll_bottom_z = try_command(float, [(ValueError, err_msg)],
492
                                    coll_bottom_z)
493

    
494
    return coll_bottom_z
495

    
496

    
497
def get_refine_inp_file():
498
    refine_inp_file = dos_inp.get('Refinement', 'refine_inp_file')
499
    if not os.path.isfile(refine_inp_file):
500
        logger.error(f'File {refine_inp_file} not found')
501
        raise FileNotFoundError(f'File {refine_inp_file} not found')
502

    
503
    return refine_inp_file
504

    
505

    
506
def get_energy_cutoff():
507
    err_msg = num_error % ('energy_cutoff', 'positive decimal number')
508
    energy_cutoff = try_command(dos_inp.getfloat,
509
                                [(ValueError, err_msg)],
510
                                'Refinement', 'energy_cutoff', fallback=0.5)
511
    if energy_cutoff < 0:
512
        logger.error(err_msg)
513
        raise ValueError(err_msg)
514
    return energy_cutoff
515

    
516

    
517
def read_input(in_file):
518
    err = False
519
    try:
520
        dos_inp.read(in_file)
521
    except MissingSectionHeaderError as e:
522
        logger.error('There are options in the input file without a Section '
523
                     'header')
524
        err = e
525
    except DuplicateOptionError as e:
526
        logger.error('There is an option in the input file that has been '
527
                     'specified more than once, possibly due to the lack of a '
528
                     'Section header')
529
        err = e
530
    except Exception as e:
531
        err = e
532
    else:
533
        err = False
534
    finally:
535
        if isinstance(err, BaseException):
536
            raise err
537

    
538
    return_vars = {}
539

    
540
    # Global
541
    if not dos_inp.has_section('Global'):
542
        logger.error(no_sect_err % 'Global')
543
        raise NoSectionError('Global')
544

    
545
    # Mandatory options
546
    # Checks whether the mandatory options 'run_type', 'code', etc. are present.
547
    glob_mand_opts = ['run_type', 'code', 'batch_q_sys']
548
    for opt in glob_mand_opts:
549
        if not dos_inp.has_option('Global', opt):
550
            logger.error(no_opt_err % (opt, 'Global'))
551
            raise NoOptionError(opt, 'Global')
552

    
553
    # Gets which sections are to be carried out
554
    isolated, screening, refinement = get_run_type()
555
    return_vars['isolated'] = isolated
556
    return_vars['screening'] = screening
557
    return_vars['refinement'] = refinement
558
    return_vars['code'] = get_code()
559
    return_vars['batch_q_sys'] = get_batch_q_sys()
560

    
561
    # Dependent options:
562
    return_vars['subm_script'] = get_subm_script()
563
    if return_vars['batch_q_sys'] != 'local' and not return_vars['subm_script']:
564
        sub_err = "'subm_script' must be provided if 'batch_q_sys' is not local"
565
        logger.error(sub_err)
566
        raise NoOptionError(opt, 'Global') # TODO Change to ValueError
567

    
568
    # Facultative options (Default/Fallback value present)
569
    return_vars['project_name'] = get_project_name()
570
    return_vars['relaunch_err'] = get_relaunch_err()
571
    return_vars['max_qw'] = get_max_qw()
572
    return_vars['special_atoms'] = get_special_atoms()
573

    
574
    # Isolated
575
    if isolated:
576
        if not dos_inp.has_section('Isolated'):
577
            logger.error(no_sect_err % 'Isolated')
578
            raise NoSectionError('Isolated')
579
        # Mandatory options
580
        # Checks whether the mandatory options are present.
581
        iso_mand_opts = ['isol_inp_file', 'molec_file']
582
        for opt in iso_mand_opts:
583
            if not dos_inp.has_option('Isolated', opt):
584
                logger.error(no_opt_err % (opt, 'Isolated'))
585
                raise NoOptionError(opt, 'Isolated')
586
        return_vars['isol_inp_file'] = get_isol_inp_file()
587
        check_inp_file(return_vars['isol_inp_file'], return_vars['code'])
588
        return_vars['molec_file'] = get_molec_file()
589

    
590
        # Facultative options (Default/Fallback value present)
591
        return_vars['cluster_magns'] = get_cluster_magns()
592
        return_vars['num_conformers'] = get_num_conformers()
593
        # return_vars['num_prom_cand'] = get_num_prom_cand()
594
        # return_vars['iso_rmsd'] = get_iso_rmsd()
595
        return_vars['min_confs'] = get_min_confs()
596

    
597
    # Screening
598
    if screening:
599
        if not dos_inp.has_section('Screening'):
600
            logger.error(no_sect_err % 'Screening')
601
            raise NoSectionError('Screening')
602
        # Mandatory options:
603
        # Checks whether the mandatory options are present.
604
        screen_mand_opts = ['sites', 'molec_ads_ctrs', 'screen_inp_file']
605
        for opt in screen_mand_opts:
606
            if not dos_inp.has_option('Screening', opt):
607
                logger.error(no_opt_err % (opt, 'Screening'))
608
                raise NoOptionError(opt, 'Screening')
609
        return_vars['screen_inp_file'] = get_screen_inp_file()
610
        return_vars['sites'] = get_sites()
611
        return_vars['molec_ads_ctrs'] = get_molec_ads_ctrs()
612

    
613
        # Facultative options (Default value present)
614
        return_vars['try_disso'] = get_try_disso()
615
        return_vars['sample_points_per_angle'] = get_pts_per_angle()
616
        return_vars['collision_threshold'] = get_coll_thrsld()
617
        # return_vars['screen_rmsd'] = get_screen_rmsd()
618
        return_vars['collision_bottom_z'] = get_coll_bottom_z()
619

    
620
    # Refinement
621
    if refinement:
622
        if not dos_inp.has_section('Refinement'):
623
            logger.error(no_sect_err % 'Refinement')
624
            raise NoSectionError('Refinement')
625
        # Mandatory options
626
        # Checks whether the mandatory options are present.
627
        ref_mand_opts = ['refine_inp_file']
628
        for opt in ref_mand_opts:
629
            if not dos_inp.has_option('Refinement', opt):
630
                logger.error(no_opt_err % (opt, 'Refinement'))
631
                raise NoOptionError(opt, 'Refinement')
632
        return_vars['refine_inp_file'] = get_refine_inp_file()
633

    
634
        # Facultative options (Default value present)
635
        return_vars['energy_cutoff'] = get_energy_cutoff()
636
        # end energy_cutoff
637

    
638
    return_vars_str = "\n\t".join([str(key) + ": " + str(val)
639
                                   for key, val in return_vars.items()])
640
    logger.info(
641
        f'Correctly read {in_file} parameters: \n\n\t{return_vars_str}\n')
642

    
643
    return return_vars
644

    
645

    
646
if __name__ == "__main__":
647
    import sys
648

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