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

dockonsurf / modules / dos_input.py @ 3d56e566

Historique | Voir | Annoter | Télécharger (25,71 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
    # TODO Treat all-enclosing parenthesis as a list instead of list of lists.
71
    """Converts a string of integers, and groups of them, to a list.
72

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

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

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

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

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

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

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

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

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

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

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

    
135

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

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

    
152
    return True
153

    
154

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

    
162

    
163
# Global
164

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

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

    
183
    return isolated, screening, refinement
184

    
185

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

    
192

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

    
203

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

    
211

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

    
216

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

    
227

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

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

    
238

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

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

    
279

    
280
# Isolated
281

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

    
289

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

    
297

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

    
307

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

    
317

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

    
327

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

    
335

    
336
# Screening
337

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

    
345

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

    
353

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

    
378
    return sites
379

    
380

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

    
406
    return molec_ads_ctrs
407

    
408

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

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

    
434
    return molec_neigh_ctrs
435

    
436

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

    
448

    
449
def get_surf_norm_vect():
450
    import numpy as np
451
    err = "'surf_norm_vect' must be either a 3 component vector or 'x', 'y' " \
452
          "or 'z'"
453
    coords = {'x': [1.0, 0.0, 0.0],
454
              'y': [0.0, 1.0, 0.0],
455
              'z': [0.0, 0.0, 1.0]}
456
    surf_norm_vect_str = dos_inp.get('Screening', 'surf_norm_vect',
457
                                     fallback="0.0 0.0 1.0")
458

    
459
    if len(surf_norm_vect_str) == 1 and surf_norm_vect_str in coords:
460
        surf_norm_vect = coords[surf_norm_vect_str]
461
    else:
462
        surf_norm_vect = try_command(str2lst, [(ValueError, err)],
463
                                     surf_norm_vect_str)
464
        if len(surf_norm_vect) != 3:
465
            logger.error(err)
466
            raise ValueError(err)
467

    
468
    return np.array(surf_norm_vect)
469

    
470

    
471
def get_ads_algo():
472
    algo_vals = ['euler', 'chemcat']
473
    check_expect_val(dos_inp.get('Screening', 'ads_algo').lower(), algo_vals)
474
    ads_algo = dos_inp.get('Screening', 'ads_algo', fallback='euler').lower()
475
    return ads_algo
476

    
477

    
478
def get_pts_per_angle():
479
    err_msg = num_error % ('sample_points_per_angle', 'positive integer')
480
    pts_per_angle = try_command(dos_inp.getint,
481
                                [(ValueError, err_msg)],
482
                                'Screening', 'sample_points_per_angle',
483
                                fallback=3)
484
    if pts_per_angle <= 0:
485
        logger.error(err_msg)
486
        raise ValueError(err_msg)
487
    return pts_per_angle
488

    
489

    
490
def get_coll_thrsld():
491
    err_msg = num_error % ('collision_threshold',
492
                           'positive decimal number')
493

    
494
    coll_thrsld = try_command(dos_inp.getfloat,
495
                              [(ValueError, err_msg)],
496
                              'Screening', 'collision_threshold', fallback=1.2)
497
    if coll_thrsld <= 0:
498
        logger.error(err_msg)
499
        raise ValueError(err_msg)
500

    
501
    return coll_thrsld
502

    
503

    
504
def get_screen_rmsd():
505
    err_msg = num_error % ('screen_rmsd', 'positive decimal number')
506
    screen_rmsd = try_command(dos_inp.getfloat,
507
                              [(ValueError, err_msg)],
508
                              'Screening', 'screen_rmsd', fallback=0.05)
509
    if screen_rmsd <= 0:
510
        logger.error(err_msg)
511
        raise ValueError(err_msg)
512

    
513
    return screen_rmsd
514

    
515

    
516
def get_coll_bottom():
517
    err_msg = num_error % ('collision_bottom', 'decimal number')
518
    coll_bottom = dos_inp.get('Screening', 'collision_bottom',
519
                              fallback="False")
520
    if coll_bottom.lower() in turn_false_answers:
521
        coll_bottom = False
522
    else:
523
        coll_bottom = try_command(float, [(ValueError, err_msg)], coll_bottom)
524

    
525
    return coll_bottom
526

    
527

    
528
def get_try_disso():
529
    err_msg = "try_disso should be have a boolean value (True or False)"
530
    try_disso = try_command(dos_inp.getboolean,
531
                            [(ValueError, err_msg)],
532
                            'Screening', 'try_disso', fallback=False)
533
    return try_disso
534

    
535

    
536
# Refinement
537

    
538
def get_refine_inp_file():  # TODO if not specified try isol_inp_file.
539
    refine_inp_file = dos_inp.get('Refinement', 'refine_inp_file')
540
    if not os.path.isfile(refine_inp_file):
541
        logger.error(f'File {refine_inp_file} not found')
542
        raise FileNotFoundError(f'File {refine_inp_file} not found')
543

    
544
    return refine_inp_file
545

    
546

    
547
def get_energy_cutoff():
548
    err_msg = num_error % ('energy_cutoff', 'positive decimal number')
549
    energy_cutoff = try_command(dos_inp.getfloat,
550
                                [(ValueError, err_msg)],
551
                                'Refinement', 'energy_cutoff', fallback=0.5)
552
    if energy_cutoff < 0:
553
        logger.error(err_msg)
554
        raise ValueError(err_msg)
555
    return energy_cutoff
556

    
557

    
558
def read_input(in_file):
559
    err = False
560
    try:
561
        dos_inp.read(in_file)
562
    except MissingSectionHeaderError as e:
563
        logger.error('There are options in the input file without a Section '
564
                     'header')
565
        err = e
566
    except DuplicateOptionError as e:
567
        logger.error('There is an option in the input file that has been '
568
                     'specified more than once, possibly due to the lack of a '
569
                     'Section header')
570
        err = e
571
    except Exception as e:
572
        err = e
573
    else:
574
        err = False
575
    finally:
576
        if isinstance(err, BaseException):
577
            raise err
578

    
579
    return_vars = {}
580

    
581
    # Global
582
    if not dos_inp.has_section('Global'):
583
        logger.error(no_sect_err % 'Global')
584
        raise NoSectionError('Global')
585

    
586
    # Mandatory options
587
    # Checks whether the mandatory options 'run_type', 'code', etc. are present.
588
    glob_mand_opts = ['run_type', 'batch_q_sys']
589
    for opt in glob_mand_opts:
590
        if not dos_inp.has_option('Global', opt):
591
            logger.error(no_opt_err % (opt, 'Global'))
592
            raise NoOptionError(opt, 'Global')
593

    
594
    # Gets which sections are to be carried out
595
    isolated, screening, refinement = get_run_type()
596
    return_vars['isolated'] = isolated
597
    return_vars['screening'] = screening
598
    return_vars['refinement'] = refinement
599
    return_vars['batch_q_sys'] = get_batch_q_sys()
600

    
601
    # Dependent options:
602
    return_vars['code'] = get_code()
603
    if return_vars['batch_q_sys'] and return_vars['batch_q_sys'] != 'local':
604
        if not dos_inp.has_option('Global', 'subm_script'):
605
            logger.error(no_opt_err % ('subm_script', 'Global'))
606
            raise NoOptionError('subm_script', 'Global')
607
        return_vars['subm_script'] = get_subm_script()
608
        return_vars['max_qw'] = get_max_qw()
609

    
610
    # Facultative options (Default/Fallback value present)
611
    return_vars['project_name'] = get_project_name()
612
    return_vars['relaunch_err'] = get_relaunch_err()
613
    return_vars['special_atoms'] = get_special_atoms()
614

    
615
    # Isolated
616
    if isolated:
617
        if not dos_inp.has_section('Isolated'):
618
            logger.error(no_sect_err % 'Isolated')
619
            raise NoSectionError('Isolated')
620
        # Mandatory options
621
        # Checks whether the mandatory options are present.
622
        iso_mand_opts = ['isol_inp_file', 'molec_file']
623
        for opt in iso_mand_opts:
624
            if not dos_inp.has_option('Isolated', opt):
625
                logger.error(no_opt_err % (opt, 'Isolated'))
626
                raise NoOptionError(opt, 'Isolated')
627
        return_vars['isol_inp_file'] = get_isol_inp_file()
628
        if 'code ' in return_vars:
629
            check_inp_file(return_vars['isol_inp_file'], return_vars['code'])
630
        return_vars['molec_file'] = get_molec_file()
631

    
632
        # Facultative options (Default/Fallback value present)
633
        return_vars['num_conformers'] = get_num_conformers()
634
        # return_vars['num_prom_cand'] = get_num_prom_cand()
635
        # return_vars['iso_rmsd'] = get_iso_rmsd()
636
        return_vars['min_confs'] = get_min_confs()
637

    
638
    # Screening
639
    if screening:
640
        if not dos_inp.has_section('Screening'):
641
            logger.error(no_sect_err % 'Screening')
642
            raise NoSectionError('Screening')
643
        # Mandatory options:
644
        # Checks whether the mandatory options are present.
645
        screen_mand_opts = ['screen_inp_file', 'surf_file', 'sites',
646
                            'molec_ads_ctrs', 'molec_neigh_ctrs']
647
        for opt in screen_mand_opts:
648
            if not dos_inp.has_option('Screening', opt):
649
                logger.error(no_opt_err % (opt, 'Screening'))
650
                raise NoOptionError(opt, 'Screening')
651
        return_vars['screen_inp_file'] = get_screen_inp_file()
652
        return_vars['surf_file'] = get_surf_file()
653
        return_vars['sites'] = get_sites()
654
        return_vars['molec_ads_ctrs'] = get_molec_ads_ctrs()
655
        return_vars['molec_neigh_ctrs'] = get_molec_neigh_ctrs()
656
        if len(return_vars['molec_ads_ctrs']) != \
657
                len(return_vars['molec_neigh_ctrs']):
658
            err = "'molec_ads_ctrs' and 'molec_neigh_ctrs' must have the same" \
659
                  "number of indides"
660
            logger.error(err)
661
            raise ValueError(err)
662

    
663
        # Facultative options (Default value present)
664
        return_vars['select_magns'] = get_select_magns()
665
        return_vars['surf_norm_vect'] = get_surf_norm_vect()
666
        return_vars['try_disso'] = get_try_disso()
667
        return_vars['ads_algo'] = get_ads_algo()
668
        return_vars['sample_points_per_angle'] = get_pts_per_angle()
669
        return_vars['collision_threshold'] = get_coll_thrsld()
670
        # return_vars['screen_rmsd'] = get_screen_rmsd()
671
        return_vars['collision_bottom'] = get_coll_bottom()
672

    
673
    # Refinement
674
    if refinement:
675
        if not dos_inp.has_section('Refinement'):
676
            logger.error(no_sect_err % 'Refinement')
677
            raise NoSectionError('Refinement')
678
        # Mandatory options
679
        # Checks whether the mandatory options are present.
680
        ref_mand_opts = ['refine_inp_file']
681
        for opt in ref_mand_opts:
682
            if not dos_inp.has_option('Refinement', opt):
683
                logger.error(no_opt_err % (opt, 'Refinement'))
684
                raise NoOptionError(opt, 'Refinement')
685
        return_vars['refine_inp_file'] = get_refine_inp_file()
686

    
687
        # Facultative options (Default value present)
688
        return_vars['energy_cutoff'] = get_energy_cutoff()
689
        # end energy_cutoff
690

    
691
    return_vars_str = "\n\t".join([str(key) + ": " + str(value)
692
                                   for key, value in return_vars.items()])
693
    logger.info(
694
        f'Correctly read {in_file} parameters: \n\n\t{return_vars_str}\n')
695

    
696
    return return_vars
697

    
698

    
699
if __name__ == "__main__":
700
    import sys
701

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