Révision a234c0e8

b/modules/dos_input.py
5 5
check_expect_val: Checks whether an option lies within its expected values.
6 6
read_input: Sets up the variables of DockOnSurf by reading from an input file.
7 7
"""
8
import os.path
8 9
import logging
10
from configparser import ConfigParser, NoSectionError, NoOptionError, \
11
    MissingSectionHeaderError, DuplicateOptionError
12

  
13
logger = logging.getLogger('DockOnSurf')
14

  
15
dos_inp = ConfigParser(inline_comment_prefixes='#',
16
                       empty_lines_in_values=False)
17

  
18
new_answers = {'n': False, 'none': False, 'nay': False,
19
               'y': True, 'sí': True, 'aye': True, 'sure': True}
20
for answer, val in new_answers.items():
21
    dos_inp.BOOLEAN_STATES[answer] = val
22
turn_false_answers = [answer for answer in dos_inp.BOOLEAN_STATES
23
                      if dos_inp.BOOLEAN_STATES[answer] is False]
24

  
25
no_sect_err = "Section '%s' not found on input file"
26
no_opt_err = "Option '%s' not found on section '%s'"
27
num_error = "'%s' value must be a %s"
28
unexp_error = "An unexpected error occurred"
29

  
30

  
31
def try_command(command, expct_error_types: list, *args, **kwargs):
32
    """Try to run a command and record exceptions (expected and not) on a log.
33
    
34
    @param command: method or function, the command to be executed.
35
    @param expct_error_types: tuple of tuples, every inner tuple is supposed to
36
    contain an exception type (eg. ValueError, TypeError, etc.) to be caught and
37
    a message to print in the log and on the screen explaining the exception.
38
    Error types that are not allow to be called with a custom message as only
39
    error argument are not supported.
40
    The outer tuple encloses all couples of error types and their relative
41
    messages.
42
    *args and **kwargs: arguments and keyword-arguments of the command to be
43
    executed.
44
    When trying to run 'command' with its args and kwargs, if an exception
45
    present on the 'error_types' occurs, its relative error message is recorded
46
    on the log and a same type exception is raised with the custom message.
47
    """
48

  
49
    err = False
50
    try:
51
        return_val = command(*args, **kwargs)
52
    except Exception as e:
53
        print('I am in exce')
54
        for expct_err in expct_error_types:
55
            if isinstance(e, expct_err[0]):
56
                logger.error(expct_err[1])
57
                err = expct_err[0](expct_err[1])
58
                break
59
        else:
60
            logger.exception(unexp_error)
61
            err = e
62
    else:
63
        print('Here I am')
64
        err = False
65
        return return_val
66
    finally:
67
        print(err)
68
        if isinstance(err, BaseException):
69
            raise err
9 70

  
10 71

  
11
def str2lst(cmplx_str):  # TODO: enable deeper level of nested lists \
12
                         # TODO: (eg. '3 ((6 7) 8) 4')
72
def str2lst(cmplx_str):  # TODO: enable deeper level of nested lists
13 73
    """Converts a string of integers, and groups of them, to a list.
14 74

  
15 75
    Keyword arguments:
......
25 85
    eg. '128,(135 138;141] 87 {45, 68}' -> [128, 87, [135, 138, 141], [45, 68]]
26 86
    """
27 87

  
28
    logger = logging.getLogger('DockOnSurf')
29

  
30
    error_msg = "Function argument should be a sequence of integer numbers " \
31
                "separated by ',' ';' or ' '." \
88
    # Checks
89
    error_msg = "Function argument should be a str,sequence of integer " \
90
                "numbers separated by ',' ';' or ' '." \
32 91
                "\nThey can be grouped in parentheses-like enclosers: '()', " \
33 92
                "'[]' or {}. Nested groups are not allowed. \n" \
34 93
                "eg. 128,(135 138;141) 87 {45, 68}"
35
    try:
36
        cmplx_str = cmplx_str.replace(',', ' ').replace(';', ' ').replace(
37
            '[', '(').replace(']', ')').replace('{', '(').replace('}', ')')
38
        list(map(int, cmplx_str.replace(')', '').replace('(', '').split()))
39
    except AttributeError:
40
        logger.error(error_msg)
41
        raise AttributeError(error_msg)
42
    except ValueError: # TODO Improve error handling
43
        logger.error(error_msg)
44
        raise ValueError(error_msg)
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(int, cmplx_str.replace(
101
        ')', '').replace('(', '').split()))
102

  
45 103
    deepness = 0
46 104
    for el in cmplx_str.split():
47 105
        if '(' in el:
......
85 143
    @raise ValueError: if the value is not among the expected ones.
86 144
    @return True if the value is among the expected ones.
87 145
    """
88

  
89
    logger = logging.getLogger('DockOnSurf')
90

  
91 146
    adeq_val_err = "'%s' is not an adequate value.\n" \
92 147
                   "Adequate values: %s"
93 148
    if not any([exp_val in value for exp_val in expect_vals]):
......
97 152
    return True
98 153

  
99 154

  
100
def read_input(in_file):
101
    """Sets up the variables of DockOnSurf by reading from an input file.
102

  
103
    The Format of the input file should be of a .INI type like most config files
104
    able to be managed by the configparser standard library.
105
    Sections should be capitalized ('Global' is ok but 'global' or 'GLOBAL'
106
    are not).
107
    After setting up the logger and the ConfifParser options, this function
108
    checks that mandatory sections exists ('Global'). Options are read one at a
109
    time following an internal order (not necessarily the input order).
110
    For every section, section-mandatory options are checked to be present.
111
    All the other options have a default/fallback value if the option is not
112
    specified.
113
    The values of every option are checked and errors are recorded in the
114
    program log and an exception is raised.
115
    @param in_file: str, absolute or relative path to the input file to be read.
116
    @return: return_vars: dict, list of all the input variables together with
117
    their values
118
    """
119
    import os.path
120
    from configparser import ConfigParser, NoSectionError, NoOptionError, \
121
        MissingSectionHeaderError, DuplicateOptionError
122

  
123
    from ase.data import chemical_symbols
124

  
125
    logger = logging.getLogger('DockOnSurf')
126

  
127
    dos_inp = ConfigParser(inline_comment_prefixes='#',
128
                           empty_lines_in_values=False)
129

  
130
    new_answers = {'n': False, 'none': False, 'nay': False,
131
                   'y': True, 'sí': True, 'aye': True, 'sure': True}
132

  
133
    for answer, val in new_answers.items():
134
        dos_inp.BOOLEAN_STATES[answer] = val
135

  
136
    try:
137
        dos_inp.read(in_file)
138
    except MissingSectionHeaderError as e:
139
        logger.error('There are options in the input file without a Section '
140
                     'header')
141
        raise e
142
    except DuplicateOptionError as e:
143
        logger.error('There is an option in the input file that has been '
144
                     'specified more than once, possibly due to the lack of a '
145
                     'Section header')
146
        raise e
147

  
148
    turn_false_answers = [opt for opt in dos_inp.BOOLEAN_STATES
149
                          if dos_inp.BOOLEAN_STATES[opt] is False]
150

  
151
    return_vars = {}
152

  
153
    no_sect_err = "Section '%s' not found on input file"
154
    no_opt_err = "Option '%s' not found on section '%s'"
155
    num_error = "'%s' value must be a %s"
156

  
157
    ##############
158
    ### Global ###
159
    ##############
160
    ### Mandatory options ###
161
    if not dos_inp.has_section('Global'):
162
        logger.error(no_sect_err % 'Global')
163
        raise NoSectionError('Global')
164

  
165
    # Checks whether the mandatory options 'run_type', 'code', etc. are
166
    # present in the Global section and ensures they have an adequate
167
    # value
168
    mand_opts = ['run_type', 'code', 'batch_q_sys']
169
    for opt in mand_opts:
170
        if not dos_inp.has_option('Global', opt):
171
            logger.error(no_opt_err % (opt, 'Global'))
172
            raise NoOptionError(opt, 'Global')
173

  
174
    # run_type
155
def get_run_type():
175 156
    isolated, screening, refinement = (False, False, False)
176 157
    run_type_vals = ['isolated', 'screening', 'refinement', 'adsorption',
177 158
                     'full']
......
189 170
    if 'full' in run_type:
190 171
        isolated, screening, refinement = (True, True, True)
191 172

  
192
    return_vars['isolated'] = isolated
193
    return_vars['screening'] = screening
194
    return_vars['refinement'] = refinement
195
    # end run_type 
173
    return isolated, screening, refinement
196 174

  
197
    # code
175

  
176
def get_code():
198 177
    code_vals = ['cp2k']
199 178
    check_expect_val(dos_inp.get('Global', 'code').lower(), code_vals)
200 179
    code = dos_inp.get('Global', 'code').lower()
201
    return_vars['code'] = code
202
    # end code
180
    return code
181

  
203 182

  
204
    # batch_q_sys
183
def get_batch_q_sys():
205 184
    batch_q_sys_vals = ['sge']
206 185
    check_expect_val(dos_inp.get('Global', 'batch_q_sys').lower(),
207 186
                     batch_q_sys_vals)
208 187
    batch_q_sys = dos_inp.get('Global', 'batch_q_sys').lower()
209
    return_vars['batch_q_sys'] = batch_q_sys
210
    # end batch_q_sys
188
    return batch_q_sys
211 189

  
212
    ### Facultative options (Default value present) ###
213
    # relaunch_err
190

  
191
def get_relaunch_err():
214 192
    relaunch_err_vals = ['geo_not_conv', 'false']
215 193
    relaunch_err = dos_inp.get('Global', 'relaunch_err',
216 194
                               fallback="False")
217 195
    if relaunch_err.lower() in turn_false_answers:
218
        relaunch_err = False
196
        return False
219 197
    else:
220 198
        check_expect_val(relaunch_err.lower(), relaunch_err_vals)
221
    return_vars['relaunch_err'] = relaunch_err
222
    # end relaunch_err
199
    return relaunch_err
200

  
201

  
202
def get_max_qw():
203
    err_msg = num_error % ('max_qw', 'positive integer')
204
    max_qw = try_command(dos_inp.getint, [(ValueError, err_msg)],
205
                         'Global', 'max_qw', fallback=3)
223 206

  
224
    # max_qw
225
    try:
226
        max_qw = dos_inp.getint('Global', 'max_qw', fallback=3)
227
    except ValueError:
228
        logger.error(num_error % ('max_qw', 'positive integer'))
229
        raise ValueError(num_error % ('max_qw', 'positive integer'))
230 207
    if max_qw < 1:
231 208
        logger.error(num_error % ('max_qw', 'positive integer'))
232 209
        raise ValueError(num_error % ('max_qw', 'positive integer'))
233
    return_vars['max_qw'] = max_qw
234
    # end max_qw
210
    return max_qw
211

  
212

  
213
def get_special_atoms():
214
    from ase.data import chemical_symbols
235 215

  
236
    # special_atoms
237 216
    spec_at_err = '\'special_atoms\' does not have an adequate format.\n' \
238 217
                  'Adequate format: (Fe1 Fe) (O1 O)'
239 218
    special_atoms = dos_inp.get('Global', 'special_atoms', fallback="False")
......
265 244
                    continue
266 245
                if tup2[0] == tup[0]:
267 246
                    label_err = f'You have specified the label {tup[0]} to ' \
268
                                 f'more than one special atom'
247
                                f'more than one special atom'
269 248
                    logger.error(label_err)
270 249
                    raise ValueError(label_err)
271 250
        special_atoms = lst_tple
272
    return_vars['special_atoms'] = special_atoms
273
    # end special_atoms
251
    return special_atoms
252

  
253

  
254
def get_isol_inp_file():
255
    isol_inp_file = dos_inp.get('Isolated', 'isol_inp_file')
256
    if not os.path.isfile(isol_inp_file):
257
        logger.error(f'File {isol_inp_file} not found')
258
        raise FileNotFoundError(f'File {isol_inp_file} not found')
259
    return isol_inp_file
260

  
261

  
262
def get_cluster_magns():
263
    clust_magns_vals = ['energy', 'moi']
264
    cluster_magns_str = dos_inp.get('Isolated', 'cluster_magns',
265
                                    fallback='energy')
266
    cluster_magns_str.replace(',', ' ').replace(';', ' ')
267
    cluster_magns = cluster_magns_str.split(' ')
268
    cluster_magns = [m.lower() for m in cluster_magns]
269
    for m in cluster_magns:
270
        check_expect_val(m, clust_magns_vals)
271
    return cluster_magns
272

  
273

  
274
def get_num_conformers():
275
    err_msg = num_error % ('num_conformers', 'positive integer')
276
    num_conformers = try_command(dos_inp.getint, [(ValueError, err_msg)],
277
                                 'Isolated', 'num_conformers', fallback=100)
278
    if num_conformers < 1:
279
        logger.error(err_msg)
280
        raise ValueError(err_msg)
281
    return num_conformers
282

  
283

  
284
def get_num_prom_cand():
285
    err_msg = num_error % ('num_prom_cand', 'positive integer')
286
    num_prom_cand = try_command(dos_inp.getint, [(ValueError, err_msg)],
287
                                'Isolated', 'num_prom_cand', fallback=3)
288
    if num_prom_cand < 1:
289
        logger.error(err_msg)
290
        raise ValueError(err_msg)
291
    return num_prom_cand
292

  
293

  
294
def get_iso_rmsd():
295
    err_msg = num_error % ('iso_rmsd', 'positive decimal number')
296
    iso_rmsd = try_command(dos_inp.getfloat, [(ValueError, err_msg)],
297
                           'Isolated', 'iso_rmsd', fallback=0.05)
298
    if iso_rmsd <= 0.0:
299
        logger.error(err_msg)
300
        raise ValueError(err_msg)
301
    return iso_rmsd
302

  
303

  
304
def get_screen_inp_file():
305
    screen_inp_file = dos_inp.get('Screening', 'screen_inp_file')
306
    if not os.path.isfile(screen_inp_file):
307
        logger.error(f'File {screen_inp_file} not found')
308
        raise FileNotFoundError(f'File {screen_inp_file} not found')
309
    return screen_inp_file
310

  
311

  
312
def get_sites():
313
    err_msg = 'The value of sites should be a list of atom numbers ' \
314
              '(ie. positive integers) or groups of atom numbers ' \
315
              'grouped by parentheses-like enclosers. \n' \
316
              'eg. 128,(135 138;141) 87 {45, 68}'
317
    # Convert the string into a list of lists
318
    sites = try_command(str2lst,
319
                        [(ValueError, err_msg), (AttributeError, err_msg)],
320
                        dos_inp.get('Screening', 'sites'))
321
    # Check all elements of the list (of lists) are positive integers
322
    for site in sites:
323
        if type(site) is list:
324
            for atom in site:
325
                if atom < 0:
326
                    logger.error(err_msg)
327
                    raise ValueError(err_msg)
328
        elif type(site) is int:
329
            if site < 0:
330
                logger.error(err_msg)
331
                raise ValueError(err_msg)
332
        else:
333
            logger.error(err_msg)
334
            raise ValueError(err_msg)
335

  
336
    return sites
337

  
338

  
339
def get_molec_ads_ctrs():
340
    err_msg = 'The value of molec_ads_ctrs should be a list of atom' \
341
              ' numbers (ie. positive integers) or groups of atom ' \
342
              'numbers enclosed by parentheses-like characters. \n' \
343
              'eg. 128,(135 138;141) 87 {45, 68}'
344
    # Convert the string into a list of lists
345
    molec_ads_ctrs = try_command(str2lst,
346
                                 [(ValueError, err_msg),
347
                                  (AttributeError, err_msg)],
348
                                 dos_inp.get('Screening', 'molec_ads_ctrs'))
349
    # Check all elements of the list (of lists) are positive integers
350
    for ctr in molec_ads_ctrs:
351
        if type(ctr) is list:
352
            for atom in ctr:
353
                if atom < 0:
354
                    logger.error(err_msg)
355
                    raise ValueError(err_msg)
356
        elif type(ctr) is int:
357
            if ctr < 0:
358
                logger.error(err_msg)
359
                raise ValueError(err_msg)
360
        else:
361
            logger.error(err_msg)
362
            raise ValueError(err_msg)
363

  
364
    return molec_ads_ctrs
365

  
366

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

  
374

  
375
def get_pts_per_angle():
376
    err_msg = num_error % ('sample_points_per_angle',
377
                           'positive integer')
378
    pts_per_angle = try_command(dos_inp.getint,
379
                                [(ValueError, err_msg)],
380
                                'Screening', 'sample_points_per_angle',
381
                                fallback=3)
382

  
383
    return pts_per_angle
384

  
385

  
386
def get_coll_thrsld():
387
    err_msg = num_error % ('collision_threshold',
388
                           'positive decimal number')
389

  
390
    coll_thrsld = try_command(dos_inp.getfloat,
391
                              [(ValueError, err_msg)],
392
                              'Screening', 'collision_threshold', fallback=1.2)
393
    if coll_thrsld <= 0:
394
        logger.error(err_msg)
395
        raise ValueError(err_msg)
396

  
397
    return coll_thrsld
398

  
399

  
400
def get_screen_rmsd():
401
    err_msg = num_error % ('screen_rmsd', 'positive decimal number')
402
    screen_rmsd = try_command(dos_inp.getfloat,
403
                              [(ValueError, err_msg)],
404
                              'Screening', 'screen_rmsd', fallback=0.05)
405
    if screen_rmsd <= 0:
406
        logger.error(err_msg)
407
        raise ValueError(err_msg)
408

  
409
    return screen_rmsd
410

  
411

  
412
def get_coll_bottom_z():
413
    err_msg = num_error % ('collision_bottom_z', 'decimal number')
414
    coll_bottom_z = dos_inp.get('Screening', 'collision_bottom_z',
415
                                fallback="False")
416
    if coll_bottom_z.lower() in turn_false_answers:
417
        coll_bottom_z = False
418
    else:
419
        try_command(float, [(ValueError, err_msg)], coll_bottom_z)
420

  
421
    return coll_bottom_z
422

  
423

  
424
def get_refine_inp_file():
425
    refine_inp_file = dos_inp.get('Refinement', 'refine_inp_file')
426
    if not os.path.isfile(refine_inp_file):
427
        logger.error(f'File {refine_inp_file} not found')
428
        raise FileNotFoundError(f'File {refine_inp_file} not found')
429

  
430
    return refine_inp_file
431

  
432

  
433
def get_energy_cutoff():
434
    err_msg = num_error % ('energy_cutoff', 'positive decimal number')
435
    energy_cutoff = try_command(dos_inp.getfloat,
436
                                [(ValueError, err_msg)],
437
                                'Refinement', 'energy_cutoff', fallback=0.5)
438
    if energy_cutoff < 0:
439
        logger.error(err_msg)
440
        raise ValueError(err_msg)
441
    return energy_cutoff
442

  
443

  
444
def read_input(in_file):
445
    err = False
446
    try:
447
        dos_inp.read(in_file)
448
    except MissingSectionHeaderError as e:
449
        logger.error('There are options in the input file without a Section '
450
                     'header')
451
        err = e
452
    except DuplicateOptionError as e:
453
        logger.error('There is an option in the input file that has been '
454
                     'specified more than once, possibly due to the lack of a '
455
                     'Section header')
456
        err = e
457
    except Exception as e:
458
        err = e
459
    else:
460
        err = False
461
    finally:
462
        if isinstance(err, BaseException):
463
            raise err
274 464

  
275
    ################
276
    ### Isolated ###
277
    ################
465
    return_vars = {}
466

  
467
    # Global
468
    if not dos_inp.has_section('Global'):
469
        logger.error(no_sect_err % 'Global')
470
        raise NoSectionError('Global')
471

  
472
    # Mandatory options
473
    # Checks whether the mandatory options 'run_type', 'code', etc. are present.
474
    screen_mand_opts = ['run_type', 'code', 'batch_q_sys']
475
    for opt in screen_mand_opts:
476
        if not dos_inp.has_option('Global', opt):
477
            logger.error(no_opt_err % (opt, 'Global'))
478
            raise NoOptionError(opt, 'Global')
479

  
480
    # Gets which sections are to be carried out
481
    isolated, screening, refinement = get_run_type()
482
    return_vars['isolated'] = isolated
483
    return_vars['screening'] = screening
484
    return_vars['refinement'] = refinement
485
    return_vars['code'] = get_code()
486
    return_vars['batch_q_sys'] = get_batch_q_sys()
487

  
488
    # Facultative options (Default/Fallback value present)
489
    return_vars['relaunch_err'] = get_relaunch_err()
490
    return_vars['max_qw'] = get_max_qw()
491
    return_vars['special_atoms'] = get_special_atoms()
492

  
493
    # Isolated
278 494
    if isolated:
279
        ### Mandatory options if Isolated ###
280 495
        if not dos_inp.has_section('Isolated'):
281 496
            logger.error(no_sect_err % 'Isolated')
282 497
            raise NoSectionError('Isolated')
283

  
284
        # isol_inp_file
285
        if 'isol_inp_file' not in dos_inp.options('Isolated'):
286
            logger.error(no_opt_err % ('isol_inp_file', 'Isolated'))
287
            raise NoOptionError('isol_inp_file', 'Isolated')
288
        isol_inp_file = dos_inp.get('Isolated', 'isol_inp_file')
289
        if not os.path.isfile(isol_inp_file):
290
            logger.error(f'File {isol_inp_file} not found')
291
            raise FileNotFoundError(f'File {isol_inp_file} not found')
292
        return_vars['isol_inp_file'] = isol_inp_file
293
        # end isol_inp_file
294

  
295
        ### Facultative options (Default/Fallback value present) ###
296
        # cluster_magns
297
        clust_magns_vals = ['energy', 'moi']
298
        cluster_magns_str = dos_inp.get('Isolated', 'cluster_magns',
299
                                        fallback='energy')
300
        cluster_magns_str.replace(',', ' ').replace(';', ' ')
301
        cluster_magns = cluster_magns_str.split(' ')
302
        cluster_magns = [m.lower() for m in cluster_magns]
303
        for m in cluster_magns:
304
            check_expect_val(m, clust_magns_vals)
305
        return_vars['cluster_magns'] = cluster_magns
306
        # end cluster_magns
307

  
308
        # num_conformers
309
        try:
310
            num_conformers = dos_inp.getint('Isolated', 'num_conformers',
311
                                            fallback=100)
312
        except ValueError:
313
            logger.error(num_error % ('num_conformers', 'positive integer'))
314
            raise ValueError(num_error % ('num_conformers', 'positive integer'))
315
        if num_conformers < 1:
316
            logger.error(num_error % ('num_conformers', 'positive integer'))
317
            raise ValueError(num_error % ('num_conformers', 'positive integer'))
318
        return_vars['num_conformers'] = num_conformers
319
        # end num_conformers
320

  
321
        # num_prom_cand
322
        try:
323
            num_prom_cand = dos_inp.getint('Isolated', 'num_prom_cand',
324
                                       fallback=3)
325
        except ValueError:
326
            logger.error(num_error % ('num_prom_cand', 'positive integer'))
327
            raise ValueError(num_error % ('num_prom_cand', 'positive integer'))
328
        if num_prom_cand < 1:
329
            logger.error(num_error % ('num_prom_cand', 'positive integer'))
330
            raise ValueError(num_error % ('num_prom_cand', 'positive integer'))
331
        return_vars['num_prom_cand'] = num_prom_cand
332
        # end num_prom_cand
333

  
334
        # iso_rmsd
335
        try:
336
            iso_rmsd = dos_inp.getfloat('Isolated', 'iso_rmsd',
337
                                                  fallback=0.05)
338
        except ValueError:
339
            logger.error(num_error % ('iso_rmsd', 'positive decimal number'))
340
            raise ValueError(num_error % ('iso_rmsd', 'positive decimal '
341
                                                      'number'))
342
        if iso_rmsd <= 0.0:
343
            logger.error(num_error % ('iso_rmsd', 'positive decimal number'))
344
            raise ValueError(num_error % ('iso_rmsd', 'positive decimal '
345
                                                      'number'))
346
        return_vars['iso_rmsd'] = iso_rmsd
347
        # end iso_rmsd
348

  
349
    #################
350
    ### Screening ###
351
    #################
498
        # Mandatory options
499
        # Checks whether the mandatory options are present.
500
        iso_mand_opts = ['iso_inp_file']
501
        for opt in iso_mand_opts:
502
            if not dos_inp.has_option('Isolated', opt):
503
                logger.error(no_opt_err % (opt, 'Isolated'))
504
                raise NoOptionError(opt, 'Isolated')
505
        return_vars['isol_inp_file'] = get_isol_inp_file()
506

  
507
        # Facultative options (Default/Fallback value present)
508
        return_vars['cluster_magns'] = get_cluster_magns()
509
        return_vars['num_conformers'] = get_num_conformers()
510
        return_vars['num_prom_cand'] = get_num_prom_cand()
511
        return_vars['iso_rmsd'] = get_iso_rmsd()
512

  
513
    # Screening
352 514
    if screening:
353
        ### Mandatory options ###
354 515
        if not dos_inp.has_section('Screening'):
355 516
            logger.error(no_sect_err % 'Screening')
356 517
            raise NoSectionError('Screening')
357

  
358
        # screen_inp_file
359
        if 'screen_inp_file' not in dos_inp.options('Screening'):
360
            logger.error(no_opt_err % ('screen_inp_file', 'Screening'))
361
            raise NoOptionError('screen_inp_file', 'Screening')
362
        screen_inp_file = dos_inp.get('Screening', 'screen_inp_file')
363
        if not os.path.isfile(screen_inp_file):
364
            logger.error(f'File {screen_inp_file} not found')
365
            raise FileNotFoundError(f'File {screen_inp_file} not found')
366
        return_vars['screen_inp_file'] = screen_inp_file
367
        # end screen_inp_file
368

  
369
        # Checks whether the mandatory options 'sites', 'molec_ads_ctrs', etc.
370
        # are present in the Screening section
371
        mand_opts = ['sites', 'molec_ads_ctrs']
372
        for opt in mand_opts:
518
        # Mandatory options:
519
        # Checks whether the mandatory options are present.
520
        screen_mand_opts = ['sites', 'molec_ads_ctrs', 'screen_inp_file']
521
        for opt in screen_mand_opts:
373 522
            if not dos_inp.has_option('Screening', opt):
374 523
                logger.error(no_opt_err % (opt, 'Screening'))
375 524
                raise NoOptionError(opt, 'Screening')
376

  
377
        # sites
378
        sites_err = 'The value of sites should be a list of atom numbers ' \
379
                    '(ie. positive integers) or groups of atom numbers ' \
380
                    'grouped by parentheses-like enclosers. \n' \
381
                    'eg. 128,(135 138;141) 87 {45, 68}'
382
        try:
383
            sites = str2lst(dos_inp.get('Screening', 'sites'))
384
        except (ValueError, AttributeError):
385
            logger.error(sites_err)
386
            raise ValueError(sites_err)
387

  
388
        # Check all elements of the list (of lists) are positive integers
389
        for site in sites:
390
            if type(site) is list:
391
                for atom in site:
392
                    if atom < 0:
393
                        logger.error(sites_err)
394
                        raise ValueError(sites_err)
395
            elif type(site) is int:
396
                if site < 0:
397
                    logger.error(sites_err)
398
                    raise ValueError(sites_err)
399
            else:
400
                logger.error(sites_err)
401
                raise ValueError(sites_err)
402
        return_vars['sites'] = sites
403
        # end sites
404

  
405
        # molec_ads_ctrs
406
        molec_ads_err = 'The value of molec_ads_ctrs should be a list of atom' \
407
                        ' numbers (ie. positive integers) or groups of atom ' \
408
                        'numbers enclosed by parentheses-like characters. \n' \
409
                        'eg. 128,(135 138;141) 87 {45, 68}'
410
        try:
411
            molec_ads_ctrs = str2lst(dos_inp.get('Screening', 'molec_ads_ctrs'))
412
        except (ValueError, AttributeError):
413
            logger.error(molec_ads_err)
414
            raise ValueError(molec_ads_err)
415

  
416
        # Check all elements of the list (of lists) are positive integers
417
        for ctr in molec_ads_ctrs:
418
            if type(ctr) is list:
419
                for atom in ctr:
420
                    if atom < 0:
421
                        logger.error(molec_ads_err)
422
                        raise ValueError(molec_ads_err)
423
            elif type(ctr) is int:
424
                if ctr < 0:
425
                    logger.error(molec_ads_err)
426
                    raise ValueError(molec_ads_err)
427
            else:
428
                logger.error(molec_ads_err)
429
                raise ValueError(molec_ads_err)
430
        return_vars['molec_ads_ctrs'] = molec_ads_ctrs
431
        # end molec_ads_ctrs
432

  
433
        ### Facultative options (Default value present) ###
434

  
435
        # try_disso
436
        try:
437
            try_disso = dos_inp.getboolean('Screening', 'try_disso',
438
                                           fallback=False)
439
        except ValueError:
440
            disso_err = 'try_disso should be have a boolean value (True or ' \
441
                        'False) '
442
            logger.error(disso_err)
443
            raise ValueError(disso_err)
444
        return_vars['try_disso'] = try_disso
445
        # end try_disso
446

  
447
        # sample_points_per_angle
448
        try:
449
            sample_points_per_angle = dos_inp.getint('Screening',
450
                                                     'sample_points_per_angle',
451
                                                     fallback=3)
452
        except ValueError:
453
            logger.error(num_error % ('sample_points_per_angle',
454
                                      'positive integer'))
455
            raise ValueError(num_error % ('sample_points_per_angle',
456
                                          'positive integer'))
457
        if sample_points_per_angle < 1:
458
            logger.error(num_error % ('sample_points_per_angle',
459
                                      'positive integer'))
460
            raise ValueError(num_error % ('sample_points_per_angle',
461
                                          'positive integer'))
462
        return_vars['sample_points_per_angle'] = sample_points_per_angle
463
        # end sample_points_per_angle
464

  
465
        # collision_threshold
466
        try:
467
            collision_threshold = dos_inp.getfloat('Screening',
468
                                                   'collision_threshold',
469
                                                   fallback=1.2)
470
        except ValueError:
471
            logger.error(num_error % ('collision_threshold',
472
                                      'positive decimal number'))
473
            raise ValueError(num_error % ('collision_threshold',
474
                                          'positive decimal number'))
475
        return_vars['collision_threshold'] = collision_threshold
476
        if collision_threshold <= 0:
477
            logger.error(num_error % ('collision_threshold',
478
                                      'positive decimal number'))
479
            raise ValueError(num_error % ('collision_threshold',
480
                                          'positive decimal number'))
481
        # end collision_threshold
482

  
483
        # screen_rmsd
484
        try:
485
            screen_rmsd = dos_inp.getfloat('Screening',
486
                                                     'screen_rmsd',
487
                                                     fallback=0.05)
488
        except ValueError:
489
            logger.error(num_error % ('screen_rmsd', 'positive decimal number'))
490
            raise ValueError(num_error % ('screen_rmsd', 'positive decimal '
491
                                                         'number'))
492
        if screen_rmsd <= 0:
493
            logger.error(num_error % ('screen_rmsd', 'positive decimal number'))
494
            raise ValueError(num_error % ('screen_rmsd', 'positive decimal '
495
                                                         'number'))
496
        return_vars['screen_rmsd'] = screen_rmsd
497
        # end screen_rmsd
498

  
499
        # collision_bottom_z
500
        collision_bottom_z = dos_inp.get('Screening', 'collision_bottom_z',
501
                                         fallback="False")
502
        if collision_bottom_z.lower() in turn_false_answers:
503
            collision_bottom_z = False
504
        else:
505
            try:
506
                collision_bottom_z = float(collision_bottom_z)
507
            except ValueError:
508
                logger.error(num_error % ('collision_bottom_z',
509
                                          'decimal number'))
510
                raise ValueError(num_error % ('collision_bottom_z',
511
                                              'decimal number'))
512
        return_vars['collision_bottom_z'] = collision_bottom_z
513
        # end collision_bottom_z
514

  
515
    ##################
516
    ### Refinement ###
517
    ##################
525
        return_vars['screen_inp_file'] = get_screen_inp_file()
526
        return_vars['sites'] = get_sites()
527
        return_vars['molec_ads_ctrs'] = get_molec_ads_ctrs()
528

  
529
        # Facultative options (Default value present)
530
        return_vars['try_disso'] = get_try_disso()
531
        return_vars['sample_points_per_angle'] = get_pts_per_angle()
532
        return_vars['collision_threshold'] = get_coll_thrsld()
533
        return_vars['screen_rmsd'] = get_screen_rmsd()
534
        return_vars['collision_bottom_z'] = get_coll_bottom_z()
535

  
536
    # Refinement
518 537
    if refinement:
519
        ### Mandatory options ###
520
        # refine_inp_file
521
        if 'refine_inp_file' not in dos_inp.options('Refinement'):
522
            logger.error(no_opt_err % ('refine_inp_file', 'Refinement'))
523
            raise NoOptionError('refine_inp_file', 'Refinement')
524
        refine_inp_file = dos_inp.get('Refinement', 'refine_inp_file')
525
        if not os.path.isfile(refine_inp_file):
526
            logger.error(f'File {refine_inp_file} not found')
527
            raise FileNotFoundError(f'File {refine_inp_file} not found')
528
        return_vars['refine_inp_file'] = refine_inp_file
529
        # end refine_inp_file
530

  
531
        ### Facultative options (Default value present) ###
532
        # energy_cutoff
533
        try:
534
            energy_cutoff = dos_inp.getfloat('Refinement', 'energy_cutoff',
535
                                             fallback=0.5)
536
        except ValueError:
537
            logger.error(num_error % ('energy_cutoff',
538
                                      'positive decimal number'))
539
            raise ValueError(num_error % ('energy_cutoff',
540
                                          'positive decimal number'))
541
        if energy_cutoff < 0:
542
            logger.error(num_error % ('energy_cutoff',
543
                                      'positive decimal number'))
544
            raise ValueError(num_error % ('energy_cutoff',
545
                                          'positive decimal number'))
546
        return_vars['energy_cutoff'] = energy_cutoff
538
        if not dos_inp.has_section('Refinement'):
539
            logger.error(no_sect_err % 'Refinement')
540
            raise NoSectionError('Refinement')
541
        # Mandatory options
542
        # Checks whether the mandatory options are present.
543
        ref_mand_opts = ['refine_inp_file']
544
        for opt in ref_mand_opts:
545
            if not dos_inp.has_option('Refinement', opt):
546
                logger.error(no_opt_err % (opt, 'Refinement'))
547
                raise NoOptionError(opt, 'Refinement')
548
        return_vars['refine_inp_file'] = get_refine_inp_file()
549

  
550
        # Facultative options (Default value present)
551
        return_vars['energy_cutoff'] = get_energy_cutoff()
547 552
        # end energy_cutoff
548 553

  
549 554
    return return_vars
555

  
556

  
557
if __name__ == "__main__":
558
    import sys
559

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

Formats disponibles : Unified diff