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

dockonsurf / modules / dos_input.py @ 7d97341d

Historique | Voir | Annoter | Télécharger (27,22 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_max_structures():
501
    err_msg = num_error % ('max_structures', 'positive integer')
502
    max_structures = try_command(dos_inp.getint, [(ValueError, err_msg)],
503
                                 'Screening', 'max_structures', fallback=np.inf)
504
    if max_structures <= 0:
505
        logger.error(err_msg)
506
        raise ValueError(err_msg)
507
    return max_structures
508

    
509

    
510
def get_coll_thrsld():
511
    err_msg = num_error % ('collision_threshold',
512
                           'positive decimal number')
513

    
514
    coll_thrsld = try_command(dos_inp.getfloat,
515
                              [(ValueError, err_msg)],
516
                              'Screening', 'collision_threshold', fallback=1.2)
517
    if coll_thrsld <= 0:
518
        logger.error(err_msg)
519
        raise ValueError(err_msg)
520

    
521
    return coll_thrsld
522

    
523

    
524
def get_screen_rmsd():
525
    err_msg = num_error % ('screen_rmsd', 'positive decimal number')
526
    screen_rmsd = try_command(dos_inp.getfloat,
527
                              [(ValueError, err_msg)],
528
                              'Screening', 'screen_rmsd', fallback=0.05)
529
    if screen_rmsd <= 0:
530
        logger.error(err_msg)
531
        raise ValueError(err_msg)
532

    
533
    return screen_rmsd
534

    
535

    
536
def get_min_coll_height():
537
    err_msg = num_error % ('min_coll_height', 'decimal number')
538
    min_coll_height = dos_inp.get('Screening', 'min_coll_height',
539
                                  fallback="False")
540
    if min_coll_height.lower() in turn_false_answers:
541
        min_coll_height = False
542
    else:
543
        min_coll_height = try_command(float, [(ValueError, err_msg)],
544
                                      min_coll_height)
545

    
546
    return min_coll_height
547

    
548

    
549
def get_disso_atoms():
550
    err_msg = "try_disso should be have a boolean value (True or False)"
551
    disso_atoms_str = dos_inp.get('Screening', 'disso_atoms', fallback="False")
552
    disso_atoms = []
553
    if disso_atoms_str.lower() in turn_false_answers:
554
        pass
555
    else:
556
        for el in disso_atoms_str.split():
557
            try:
558
                disso_atoms.append(int(el))
559
            except ValueError:
560
                disso_atoms.append(el)
561
    return disso_atoms
562

    
563

    
564
# Refinement
565

    
566
def get_refine_inp_file():  # TODO if not specified try isol_inp_file.
567
    refine_inp_file = dos_inp.get('Refinement', 'refine_inp_file')
568
    if not os.path.isfile(refine_inp_file):
569
        logger.error(f'File {refine_inp_file} not found.')
570
        raise FileNotFoundError(f'File {refine_inp_file} not found')
571

    
572
    return refine_inp_file
573

    
574

    
575
def get_energy_cutoff():
576
    err_msg = num_error % ('energy_cutoff', 'positive decimal number')
577
    energy_cutoff = try_command(dos_inp.getfloat,
578
                                [(ValueError, err_msg)],
579
                                'Refinement', 'energy_cutoff', fallback=0.5)
580
    if energy_cutoff < 0:
581
        logger.error(err_msg)
582
        raise ValueError(err_msg)
583
    return energy_cutoff
584

    
585

    
586
def read_input(in_file):
587
    err = False
588
    try:
589
        dos_inp.read(in_file)
590
    except MissingSectionHeaderError as e:
591
        logger.error('There are options in the input file without a Section '
592
                     'header.')
593
        err = e
594
    except DuplicateOptionError as e:
595
        logger.error('There is an option in the input file that has been '
596
                     'specified more than once, possibly due to the lack of a '
597
                     'Section header.')
598
        err = e
599
    except Exception as e:
600
        err = e
601
    else:
602
        err = False
603
    finally:
604
        if isinstance(err, BaseException):
605
            raise err
606

    
607
    return_vars = {}
608

    
609
    # Global
610
    if not dos_inp.has_section('Global'):
611
        logger.error(no_sect_err % 'Global')
612
        raise NoSectionError('Global')
613

    
614
    # Mandatory options
615
    # Checks whether the mandatory options 'run_type', 'code', etc. are present.
616
    glob_mand_opts = ['run_type', 'batch_q_sys']
617
    for opt in glob_mand_opts:
618
        if not dos_inp.has_option('Global', opt):
619
            logger.error(no_opt_err % (opt, 'Global'))
620
            raise NoOptionError(opt, 'Global')
621

    
622
    # Gets which sections are to be carried out
623
    isolated, screening, refinement = get_run_type()
624
    return_vars['isolated'] = isolated
625
    return_vars['screening'] = screening
626
    return_vars['refinement'] = refinement
627
    return_vars['batch_q_sys'] = get_batch_q_sys()
628

    
629
    # Dependent options:
630
    return_vars['code'] = get_code()
631
    if return_vars['batch_q_sys'] and return_vars['batch_q_sys'] != 'local':
632
        if not dos_inp.has_option('Global', 'subm_script'):
633
            logger.error(no_opt_err % ('subm_script', 'Global'))
634
            raise NoOptionError('subm_script', 'Global')
635
        return_vars['subm_script'] = get_subm_script()
636
        return_vars['max_qw'] = get_max_qw()
637

    
638
    # Facultative options (Default/Fallback value present)
639
    return_vars['project_name'] = get_project_name()
640
    return_vars['relaunch_err'] = get_relaunch_err()
641
    return_vars['special_atoms'] = get_special_atoms()
642

    
643
    # Isolated
644
    if isolated:
645
        if not dos_inp.has_section('Isolated'):
646
            logger.error(no_sect_err % 'Isolated')
647
            raise NoSectionError('Isolated')
648
        # Mandatory options
649
        # Checks whether the mandatory options are present.
650
        iso_mand_opts = ['isol_inp_file', 'molec_file']
651
        for opt in iso_mand_opts:
652
            if not dos_inp.has_option('Isolated', opt):
653
                logger.error(no_opt_err % (opt, 'Isolated'))
654
                raise NoOptionError(opt, 'Isolated')
655
        return_vars['isol_inp_file'] = get_isol_inp_file()
656
        if 'code ' in return_vars:
657
            check_inp_file(return_vars['isol_inp_file'], return_vars['code'])
658
        return_vars['molec_file'] = get_molec_file()
659

    
660
        # Facultative options (Default/Fallback value present)
661
        return_vars['num_conformers'] = get_num_conformers()
662
        # return_vars['num_prom_cand'] = get_num_prom_cand()
663
        # return_vars['iso_rmsd'] = get_iso_rmsd()
664
        return_vars['min_confs'] = get_min_confs()
665

    
666
    # Screening
667
    if screening:
668
        if not dos_inp.has_section('Screening'):
669
            logger.error(no_sect_err % 'Screening')
670
            raise NoSectionError('Screening')
671
        # Mandatory options:
672
        # Checks whether the mandatory options are present.
673
        screen_mand_opts = ['screen_inp_file', 'surf_file', 'sites',
674
                            'molec_ads_ctrs', 'molec_neigh_ctrs']
675
        for opt in screen_mand_opts:
676
            if not dos_inp.has_option('Screening', opt):
677
                logger.error(no_opt_err % (opt, 'Screening'))
678
                raise NoOptionError(opt, 'Screening')
679
        return_vars['screen_inp_file'] = get_screen_inp_file()
680
        return_vars['surf_file'] = get_surf_file()
681
        return_vars['sites'] = get_sites()
682
        return_vars['molec_ads_ctrs'] = get_molec_ads_ctrs()
683
        return_vars['molec_neigh_ctrs'] = get_molec_neigh_ctrs()
684
        if len(return_vars['molec_ads_ctrs']) != \
685
                len(return_vars['molec_neigh_ctrs']):
686
            err = "'molec_ads_ctrs' and 'molec_neigh_ctrs' must have the same" \
687
                  "number of indides"
688
            logger.error(err)
689
            raise ValueError(err)
690

    
691
        # Facultative options (Default value present)
692
        return_vars['select_magns'] = get_select_magns()
693
        return_vars['confs_per_magn'] = get_confs_per_magn()
694
        return_vars['surf_norm_vect'] = get_surf_norm_vect()
695
        return_vars['disso_atoms'] = get_disso_atoms()
696
        return_vars['ads_algo'] = get_ads_algo()
697
        return_vars['sample_points_per_angle'] = get_pts_per_angle()
698
        return_vars['max_structures'] = get_max_structures()
699
        return_vars['collision_threshold'] = get_coll_thrsld()
700
        return_vars['min_coll_height'] = get_min_coll_height()
701
        cart_axes = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0],
702
                     [-1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, -1.0]]
703
        if return_vars['min_coll_height'] is not False and \
704
                return_vars['surf_norm_vect'].tolist() not in cart_axes:
705
            logger.warning("'min_coll_height' option is only implemented for "
706
                           "'surf_norm_vect' to be one of the x, y or z axes.")
707

    
708
    # Refinement
709
    if refinement:
710
        if not dos_inp.has_section('Refinement'):
711
            logger.error(no_sect_err % 'Refinement')
712
            raise NoSectionError('Refinement')
713
        # Mandatory options
714
        # Checks whether the mandatory options are present.
715
        ref_mand_opts = ['refine_inp_file']
716
        for opt in ref_mand_opts:
717
            if not dos_inp.has_option('Refinement', opt):
718
                logger.error(no_opt_err % (opt, 'Refinement'))
719
                raise NoOptionError(opt, 'Refinement')
720
        return_vars['refine_inp_file'] = get_refine_inp_file()
721

    
722
        # Facultative options (Default value present)
723
        return_vars['energy_cutoff'] = get_energy_cutoff()
724
        # end energy_cutoff
725

    
726
    return_vars_str = "\n\t".join([str(key) + ": " + str(value)
727
                                   for key, value in return_vars.items()])
728
    logger.info(
729
        f'Correctly read {in_file} parameters: \n\n\t{return_vars_str}\n.')
730

    
731
    return return_vars
732

    
733

    
734
if __name__ == "__main__":
735
    import sys
736

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