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

dockonsurf / modules / dos_input.py @ 54be2a9e

Historique | Voir | Annoter | Télécharger (22,81 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 get_run_type():
191
    isolated, screening, refinement = (False, False, False)
192
    run_type_vals = ['isolated', 'screening', 'refinement', 'adsorption',
193
                     'full']
194
    check_expect_val(dos_inp.get('Global', 'run_type').lower(), run_type_vals)
195

    
196
    run_type = dos_inp.get('Global', 'run_type').lower()
197
    if 'isolated' in run_type:
198
        isolated = True
199
    if 'screening' in run_type:
200
        screening = True
201
    if 'refinement' in run_type:
202
        refinement = True
203
    if 'adsorption' in run_type:
204
        screening, refinement = (True, True)
205
    if 'full' in run_type:
206
        isolated, screening, refinement = (True, True, True)
207

    
208
    return isolated, screening, refinement
209

    
210

    
211
def get_code():
212
    code_vals = ['cp2k']
213
    check_expect_val(dos_inp.get('Global', 'code').lower(), code_vals)
214
    code = dos_inp.get('Global', 'code').lower()
215
    return code
216

    
217

    
218
def get_batch_q_sys():
219
    batch_q_sys_vals = ['sge']
220
    check_expect_val(dos_inp.get('Global', 'batch_q_sys').lower(),
221
                     batch_q_sys_vals)
222
    batch_q_sys = dos_inp.get('Global', 'batch_q_sys').lower()
223
    return batch_q_sys
224

    
225

    
226
def get_relaunch_err():
227
    relaunch_err_vals = ['geo_not_conv', 'false']
228
    relaunch_err = dos_inp.get('Global', 'relaunch_err',
229
                               fallback="False")
230
    if relaunch_err.lower() in turn_false_answers:
231
        return False
232
    else:
233
        check_expect_val(relaunch_err.lower(), relaunch_err_vals)
234
    return relaunch_err
235

    
236

    
237
def get_max_qw():
238
    err_msg = num_error % ('max_qw', 'positive integer')
239
    max_qw = try_command(dos_inp.getint, [(ValueError, err_msg)],
240
                         'Global', 'max_qw', fallback=3)
241

    
242
    if max_qw < 1:
243
        logger.error(num_error % ('max_qw', 'positive integer'))
244
        raise ValueError(num_error % ('max_qw', 'positive integer'))
245
    return max_qw
246

    
247

    
248
def get_special_atoms():
249
    from ase.data import chemical_symbols
250

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

    
288

    
289
def get_isol_inp_file():
290
    isol_inp_file = dos_inp.get('Isolated', 'isol_inp_file')
291
    if not os.path.isfile(isol_inp_file):
292
        logger.error(f'File {isol_inp_file} not found')
293
        raise FileNotFoundError(f'File {isol_inp_file} not found')
294
    return isol_inp_file
295

    
296

    
297
def get_molec_file():
298
    molec_file = dos_inp.get('Isolated', 'molec_file')
299
    if not os.path.isfile(molec_file):
300
        logger.error(f'File {molec_file} not found')
301
        raise FileNotFoundError(f'File {molec_file} not found')
302
    return molec_file
303

    
304

    
305
def get_cluster_magns():
306
    clust_magns_vals = ['energy', 'moi']
307
    cluster_magns_str = dos_inp.get('Isolated', 'cluster_magns',
308
                                    fallback='energy')
309
    cluster_magns_str.replace(',', ' ').replace(';', ' ')
310
    cluster_magns = cluster_magns_str.split(' ')
311
    cluster_magns = [m.lower() for m in cluster_magns]
312
    for m in cluster_magns:
313
        check_expect_val(m, clust_magns_vals)
314
    return cluster_magns
315

    
316

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

    
326

    
327
def get_num_prom_cand():
328
    err_msg = num_error % ('num_prom_cand', 'positive integer')
329
    num_prom_cand = try_command(dos_inp.getint, [(ValueError, err_msg)],
330
                                'Isolated', 'num_prom_cand', fallback=3)
331
    if num_prom_cand < 1:
332
        logger.error(err_msg)
333
        raise ValueError(err_msg)
334
    return num_prom_cand
335

    
336

    
337
def get_iso_rmsd():
338
    err_msg = num_error % ('iso_rmsd', 'positive decimal number')
339
    iso_rmsd = try_command(dos_inp.getfloat, [(ValueError, err_msg)],
340
                           'Isolated', 'iso_rmsd', fallback=0.05)
341
    if iso_rmsd <= 0.0:
342
        logger.error(err_msg)
343
        raise ValueError(err_msg)
344
    return iso_rmsd
345

    
346

    
347
def get_min_confs():
348
    err_msg = "'min_confs' should be have a boolean value (True or False)"
349
    min_confs = try_command(dos_inp.getboolean,
350
                            [(ValueError, err_msg)],
351
                            'Isolated', 'min_confs', fallback=True)
352
    return min_confs
353

    
354

    
355
def get_screen_inp_file():
356
    screen_inp_file = dos_inp.get('Screening', 'screen_inp_file')
357
    if not os.path.isfile(screen_inp_file):
358
        logger.error(f'File {screen_inp_file} not found')
359
        raise FileNotFoundError(f'File {screen_inp_file} not found')
360
    return screen_inp_file
361

    
362

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

    
387
    return sites
388

    
389

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

    
415
    return molec_ads_ctrs
416

    
417

    
418
def get_try_disso():
419
    err_msg = "try_disso should be have a boolean value (True or False)"
420
    try_disso = try_command(dos_inp.getboolean,
421
                            [(ValueError, err_msg)],
422
                            'Screening', 'try_disso', fallback=False)
423
    return try_disso
424

    
425

    
426
def get_pts_per_angle():
427
    err_msg = num_error % ('sample_points_per_angle',
428
                           'positive integer')
429
    pts_per_angle = try_command(dos_inp.getint,
430
                                [(ValueError, err_msg)],
431
                                'Screening', 'sample_points_per_angle',
432
                                fallback=3)
433

    
434
    return pts_per_angle
435

    
436

    
437
def get_coll_thrsld():
438
    err_msg = num_error % ('collision_threshold',
439
                           'positive decimal number')
440

    
441
    coll_thrsld = try_command(dos_inp.getfloat,
442
                              [(ValueError, err_msg)],
443
                              'Screening', 'collision_threshold', fallback=1.2)
444
    if coll_thrsld <= 0:
445
        logger.error(err_msg)
446
        raise ValueError(err_msg)
447

    
448
    return coll_thrsld
449

    
450

    
451
def get_screen_rmsd():
452
    err_msg = num_error % ('screen_rmsd', 'positive decimal number')
453
    screen_rmsd = try_command(dos_inp.getfloat,
454
                              [(ValueError, err_msg)],
455
                              'Screening', 'screen_rmsd', fallback=0.05)
456
    if screen_rmsd <= 0:
457
        logger.error(err_msg)
458
        raise ValueError(err_msg)
459

    
460
    return screen_rmsd
461

    
462

    
463
def get_coll_bottom_z():
464
    err_msg = num_error % ('collision_bottom_z', 'decimal number')
465
    coll_bottom_z = dos_inp.get('Screening', 'collision_bottom_z',
466
                                fallback="False")
467
    if coll_bottom_z.lower() in turn_false_answers:
468
        coll_bottom_z = False
469
    else:
470
        coll_bottom_z = try_command(float, [(ValueError, err_msg)],
471
                                    coll_bottom_z)
472

    
473
    return coll_bottom_z
474

    
475

    
476
def get_refine_inp_file():
477
    refine_inp_file = dos_inp.get('Refinement', 'refine_inp_file')
478
    if not os.path.isfile(refine_inp_file):
479
        logger.error(f'File {refine_inp_file} not found')
480
        raise FileNotFoundError(f'File {refine_inp_file} not found')
481

    
482
    return refine_inp_file
483

    
484

    
485
def get_energy_cutoff():
486
    err_msg = num_error % ('energy_cutoff', 'positive decimal number')
487
    energy_cutoff = try_command(dos_inp.getfloat,
488
                                [(ValueError, err_msg)],
489
                                'Refinement', 'energy_cutoff', fallback=0.5)
490
    if energy_cutoff < 0:
491
        logger.error(err_msg)
492
        raise ValueError(err_msg)
493
    return energy_cutoff
494

    
495

    
496
def read_input(in_file):
497
    err = False
498
    try:
499
        dos_inp.read(in_file)
500
    except MissingSectionHeaderError as e:
501
        logger.error('There are options in the input file without a Section '
502
                     'header')
503
        err = e
504
    except DuplicateOptionError as e:
505
        logger.error('There is an option in the input file that has been '
506
                     'specified more than once, possibly due to the lack of a '
507
                     'Section header')
508
        err = e
509
    except Exception as e:
510
        err = e
511
    else:
512
        err = False
513
    finally:
514
        if isinstance(err, BaseException):
515
            raise err
516

    
517
    return_vars = {}
518

    
519
    # Global
520
    if not dos_inp.has_section('Global'):
521
        logger.error(no_sect_err % 'Global')
522
        raise NoSectionError('Global')
523

    
524
    # Mandatory options
525
    # Checks whether the mandatory options 'run_type', 'code', etc. are present.
526
    screen_mand_opts = ['run_type', 'code', 'batch_q_sys']
527
    for opt in screen_mand_opts:
528
        if not dos_inp.has_option('Global', opt):
529
            logger.error(no_opt_err % (opt, 'Global'))
530
            raise NoOptionError(opt, 'Global')
531

    
532
    # Gets which sections are to be carried out
533
    isolated, screening, refinement = get_run_type()
534
    return_vars['isolated'] = isolated
535
    return_vars['screening'] = screening
536
    return_vars['refinement'] = refinement
537
    return_vars['code'] = get_code()
538
    return_vars['batch_q_sys'] = get_batch_q_sys()
539

    
540
    # Facultative options (Default/Fallback value present)
541
    return_vars['relaunch_err'] = get_relaunch_err()
542
    return_vars['max_qw'] = get_max_qw()
543
    return_vars['special_atoms'] = get_special_atoms()
544

    
545
    # Isolated
546
    if isolated:
547
        if not dos_inp.has_section('Isolated'):
548
            logger.error(no_sect_err % 'Isolated')
549
            raise NoSectionError('Isolated')
550
        # Mandatory options
551
        # Checks whether the mandatory options are present.
552
        iso_mand_opts = ['isol_inp_file', 'molec_file']
553
        for opt in iso_mand_opts:
554
            if not dos_inp.has_option('Isolated', opt):
555
                logger.error(no_opt_err % (opt, 'Isolated'))
556
                raise NoOptionError(opt, 'Isolated')
557
        return_vars['isol_inp_file'] = get_isol_inp_file()
558
        return_vars['molec_file'] = get_molec_file()
559

    
560
        # Facultative options (Default/Fallback value present)
561
        return_vars['cluster_magns'] = get_cluster_magns()
562
        return_vars['num_conformers'] = get_num_conformers()
563
        return_vars['num_prom_cand'] = get_num_prom_cand()
564
        return_vars['iso_rmsd'] = get_iso_rmsd()
565
        return_vars['min_confs'] = get_min_confs()
566

    
567
    # Screening
568
    if screening:
569
        if not dos_inp.has_section('Screening'):
570
            logger.error(no_sect_err % 'Screening')
571
            raise NoSectionError('Screening')
572
        # Mandatory options:
573
        # Checks whether the mandatory options are present.
574
        screen_mand_opts = ['sites', 'molec_ads_ctrs', 'screen_inp_file']
575
        for opt in screen_mand_opts:
576
            if not dos_inp.has_option('Screening', opt):
577
                logger.error(no_opt_err % (opt, 'Screening'))
578
                raise NoOptionError(opt, 'Screening')
579
        return_vars['screen_inp_file'] = get_screen_inp_file()
580
        return_vars['sites'] = get_sites()
581
        return_vars['molec_ads_ctrs'] = get_molec_ads_ctrs()
582

    
583
        # Facultative options (Default value present)
584
        return_vars['try_disso'] = get_try_disso()
585
        return_vars['sample_points_per_angle'] = get_pts_per_angle()
586
        return_vars['collision_threshold'] = get_coll_thrsld()
587
        return_vars['screen_rmsd'] = get_screen_rmsd()
588
        return_vars['collision_bottom_z'] = get_coll_bottom_z()
589

    
590
    # Refinement
591
    if refinement:
592
        if not dos_inp.has_section('Refinement'):
593
            logger.error(no_sect_err % 'Refinement')
594
            raise NoSectionError('Refinement')
595
        # Mandatory options
596
        # Checks whether the mandatory options are present.
597
        ref_mand_opts = ['refine_inp_file']
598
        for opt in ref_mand_opts:
599
            if not dos_inp.has_option('Refinement', opt):
600
                logger.error(no_opt_err % (opt, 'Refinement'))
601
                raise NoOptionError(opt, 'Refinement')
602
        return_vars['refine_inp_file'] = get_refine_inp_file()
603

    
604
        # Facultative options (Default value present)
605
        return_vars['energy_cutoff'] = get_energy_cutoff()
606
        # end energy_cutoff
607

    
608
    return_vars_str = "\n\t".join([str(key) + ": " + str(val)
609
                                   for key, val in return_vars.items()])
610
    logger.info(
611
        f'Correctly read {in_file} parameters: \n\n\t{return_vars_str}\n')
612

    
613
    return return_vars
614

    
615

    
616
if __name__ == "__main__":
617
    import sys
618

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