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

dockonsurf / modules / dos_input.py @ b4b2f307

Historique | Voir | Annoter | Télécharger (26,8 ko)

1
"""Functions to deal with DockOnSurf input files.
2

3
Functions
4
try_command:Tries to run a command and logs its exceptions (expected and not).
5
str2lst: Converts a string of integers, and groups of them, to a list of lists.
6
check_expect_val: Checks whether the value of an option has an adequate value.
7
read_input: Sets up the calculation by reading the parameters from input file.
8
get_run_type: Gets 'run_type' value and checks that its value is acceptable.
9
get_code: Gets 'code' value and checks that its value is acceptable.
10
get_batch_q_sys: Gets 'batch_q_sys' value and checks that its value is
11
acceptable.
12
get_relaunch_err: Gets 'relaunch_err' value and checks that its value is
13
acceptable.
14
get_max_qw: Gets 'max_qw' value and checks that its value is acceptable.
15
get_special_atoms: Gets 'special_atoms' value and checks that its value is
16
acceptable.
17
get_isol_inp_file: Gets 'isol_inp_file' value and checks that its value is
18
acceptable.
19
get_cluster_magns: Gets 'cluster_magns' value and checks that its value is
20
acceptable.
21
get_num_conformers: Gets 'num_conformers' value and checks that its value is
22
acceptable.
23
get_num_prom_cand: Gets 'num_prom_cand' value and checks that its value is
24
acceptable.
25
get_iso_rmsd: Gets 'iso_rmsd' value and checks that its value is acceptable.
26
get_min_confs: Gets 'min_confs' value and checks that its value is acceptable.
27
get_screen_inp_file: Gets 'screen_inp_file' value and checks that its value is
28
acceptable.
29
get_sites: Gets 'sites' value and checks that its value is acceptable.
30
get_molec_ads_ctrs: Gets 'molec_ads_ctrs' value and checks that its value is
31
acceptable.
32
get_try_disso: Gets 'try_disso' value and checks that its value is acceptable.
33
get_pts_per_angle: Gets 'pts_per_angle' value and checks that its value is
34
acceptable.
35
get_coll_thrsld: Gets 'coll_thrsld' value and checks that its value is
36
acceptable.
37
get_screen_rmsd: Gets 'screen_rmsd' value and checks that its value is
38
acceptable.
39
get_coll_bottom_z: Gets 'coll_bottom_z' value and checks that its value is
40
acceptable.
41
get_refine_inp_file: Gets 'refine_inp_file' value and checks that its value is
42
acceptable.
43
get_energy_cutoff: Gets 'energy_cutoff' value and checks that its value is
44
acceptable.
45
"""
46
import os.path
47
import logging
48
from configparser import ConfigParser, NoSectionError, NoOptionError, \
49
    MissingSectionHeaderError, DuplicateOptionError
50
import numpy as np
51
from modules.utilities import try_command
52

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

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

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

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

    
69

    
70
def str2lst(cmplx_str, func=int):  # TODO: enable deeper level of nested lists
71
    # TODO Treat all-enclosing parenthesis as a list instead of list of lists.
72
    """Converts a string of integers, and groups of them, to a list.
73

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

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

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

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

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

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

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

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

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

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

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

    
136

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

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

    
153
    return True
154

    
155

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

    
163

    
164
# Global
165

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

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

    
184
    return isolated, screening, refinement
185

    
186

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

    
193

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

    
204

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

    
212

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

    
217

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

    
228

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

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

    
239

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

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

    
280

    
281
# Isolated
282

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

    
290

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

    
298

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

    
308

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

    
318

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

    
328

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

    
336

    
337
# Screening
338

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

    
346

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

    
354

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

    
379
    return sites
380

    
381

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

    
407
    return molec_ads_ctrs
408

    
409

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

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

    
435
    return molec_neigh_ctrs
436

    
437

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

    
449

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

    
459

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

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

    
478
    return np.array(surf_norm_vect)
479

    
480

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

    
487

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

    
499

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

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

    
511
    return coll_thrsld
512

    
513

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

    
523
    return screen_rmsd
524

    
525

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

    
536
    return min_coll_height
537

    
538

    
539
def get_disso_atoms():
540
    err_msg = "try_disso should be have a boolean value (True or False)"
541
    disso_atoms_str = dos_inp.get('Screening', 'disso_atoms', fallback="False")
542
    disso_atoms = []
543
    if disso_atoms_str.lower() in turn_false_answers:
544
        pass
545
    else:
546
        for el in disso_atoms_str.split():
547
            try:
548
                disso_atoms.append(int(el))
549
            except ValueError:
550
                disso_atoms.append(el)
551
    return disso_atoms
552

    
553

    
554
# Refinement
555

    
556
def get_refine_inp_file():  # TODO if not specified try isol_inp_file.
557
    refine_inp_file = dos_inp.get('Refinement', 'refine_inp_file')
558
    if not os.path.isfile(refine_inp_file):
559
        logger.error(f'File {refine_inp_file} not found.')
560
        raise FileNotFoundError(f'File {refine_inp_file} not found')
561

    
562
    return refine_inp_file
563

    
564

    
565
def get_energy_cutoff():
566
    err_msg = num_error % ('energy_cutoff', 'positive decimal number')
567
    energy_cutoff = try_command(dos_inp.getfloat,
568
                                [(ValueError, err_msg)],
569
                                'Refinement', 'energy_cutoff', fallback=0.5)
570
    if energy_cutoff < 0:
571
        logger.error(err_msg)
572
        raise ValueError(err_msg)
573
    return energy_cutoff
574

    
575

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

    
597
    return_vars = {}
598

    
599
    # Global
600
    if not dos_inp.has_section('Global'):
601
        logger.error(no_sect_err % 'Global')
602
        raise NoSectionError('Global')
603

    
604
    # Mandatory options
605
    # Checks whether the mandatory options 'run_type', 'code', etc. are present.
606
    glob_mand_opts = ['run_type', 'batch_q_sys']
607
    for opt in glob_mand_opts:
608
        if not dos_inp.has_option('Global', opt):
609
            logger.error(no_opt_err % (opt, 'Global'))
610
            raise NoOptionError(opt, 'Global')
611

    
612
    # Gets which sections are to be carried out
613
    isolated, screening, refinement = get_run_type()
614
    return_vars['isolated'] = isolated
615
    return_vars['screening'] = screening
616
    return_vars['refinement'] = refinement
617
    return_vars['batch_q_sys'] = get_batch_q_sys()
618

    
619
    # Dependent options:
620
    return_vars['code'] = get_code()
621
    if return_vars['batch_q_sys'] and return_vars['batch_q_sys'] != 'local':
622
        if not dos_inp.has_option('Global', 'subm_script'):
623
            logger.error(no_opt_err % ('subm_script', 'Global'))
624
            raise NoOptionError('subm_script', 'Global')
625
        return_vars['subm_script'] = get_subm_script()
626
        return_vars['max_qw'] = get_max_qw()
627

    
628
    # Facultative options (Default/Fallback value present)
629
    return_vars['project_name'] = get_project_name()
630
    return_vars['relaunch_err'] = get_relaunch_err()
631
    return_vars['special_atoms'] = get_special_atoms()
632

    
633
    # Isolated
634
    if isolated:
635
        if not dos_inp.has_section('Isolated'):
636
            logger.error(no_sect_err % 'Isolated')
637
            raise NoSectionError('Isolated')
638
        # Mandatory options
639
        # Checks whether the mandatory options are present.
640
        iso_mand_opts = ['isol_inp_file', 'molec_file']
641
        for opt in iso_mand_opts:
642
            if not dos_inp.has_option('Isolated', opt):
643
                logger.error(no_opt_err % (opt, 'Isolated'))
644
                raise NoOptionError(opt, 'Isolated')
645
        return_vars['isol_inp_file'] = get_isol_inp_file()
646
        if 'code ' in return_vars:
647
            check_inp_file(return_vars['isol_inp_file'], return_vars['code'])
648
        return_vars['molec_file'] = get_molec_file()
649

    
650
        # Facultative options (Default/Fallback value present)
651
        return_vars['num_conformers'] = get_num_conformers()
652
        # return_vars['num_prom_cand'] = get_num_prom_cand()
653
        # return_vars['iso_rmsd'] = get_iso_rmsd()
654
        return_vars['min_confs'] = get_min_confs()
655

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

    
681
        # Facultative options (Default value present)
682
        return_vars['select_magns'] = get_select_magns()
683
        return_vars['confs_per_magn'] = get_confs_per_magn()
684
        return_vars['surf_norm_vect'] = get_surf_norm_vect()
685
        return_vars['disso_atoms'] = get_disso_atoms()
686
        return_vars['ads_algo'] = get_ads_algo()
687
        return_vars['sample_points_per_angle'] = get_pts_per_angle()
688
        return_vars['collision_threshold'] = get_coll_thrsld()
689
        return_vars['min_coll_height'] = get_min_coll_height()
690
        cart_axes = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0],
691
                     [-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, -1.0]]
692
        if return_vars['min_coll_height'] is not False and \
693
                return_vars['surf_norm_vect'].tolist() not in cart_axes:
694
            logger.warning("'min_coll_height' option is only implemented for "
695
                           "'surf_norm_vect' to be one of the x, y or z axes.")
696

    
697
    # Refinement
698
    if refinement:
699
        if not dos_inp.has_section('Refinement'):
700
            logger.error(no_sect_err % 'Refinement')
701
            raise NoSectionError('Refinement')
702
        # Mandatory options
703
        # Checks whether the mandatory options are present.
704
        ref_mand_opts = ['refine_inp_file']
705
        for opt in ref_mand_opts:
706
            if not dos_inp.has_option('Refinement', opt):
707
                logger.error(no_opt_err % (opt, 'Refinement'))
708
                raise NoOptionError(opt, 'Refinement')
709
        return_vars['refine_inp_file'] = get_refine_inp_file()
710

    
711
        # Facultative options (Default value present)
712
        return_vars['energy_cutoff'] = get_energy_cutoff()
713
        # end energy_cutoff
714

    
715
    return_vars_str = "\n\t".join([str(key) + ": " + str(value)
716
                                   for key, value in return_vars.items()])
717
    logger.info(
718
        f'Correctly read {in_file} parameters: \n\n\t{return_vars_str}\n.')
719

    
720
    return return_vars
721

    
722

    
723
if __name__ == "__main__":
724
    import sys
725

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