Coverage for /builds/kinetik161/ase/ase/calculators/turbomole/parameters.py: 38.93%
298 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-12-10 11:04 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-12-10 11:04 +0000
1# type: ignore
2"""turbomole parameters management classes and functions"""
4import os
5import re
6from math import floor, log10
8import numpy as np
10from ase.calculators.turbomole.reader import parse_data_group, read_data_group
11from ase.calculators.turbomole.writer import add_data_group, delete_data_group
12from ase.units import Bohr, Ha
15class TurbomoleParameters(dict):
16 """class to manage turbomole parameters"""
18 available_functionals = [
19 'slater-dirac-exchange', 's-vwn', 'vwn', 's-vwn_Gaussian', 'pwlda',
20 'becke-exchange', 'b-lyp', 'b-vwn', 'lyp', 'b-p', 'pbe', 'tpss',
21 'bh-lyp', 'b3-lyp', 'b3-lyp_Gaussian', 'pbe0', 'tpssh', 'lhf', 'oep',
22 'b97-d', 'b2-plyp'
23 ]
25 # nested dictionary with parameters attributes
26 parameter_spec = {
27 'automatic orbital shift': {
28 'comment': None,
29 'default': 0.1,
30 'group': 'scforbitalshift',
31 'key': 'automatic',
32 'mapping': {
33 'to_control': lambda a: a / Ha,
34 'from_control': lambda a: a * Ha
35 },
36 'type': float,
37 'units': 'eV',
38 'updateable': True
39 },
40 'basis set definition': {
41 'comment': 'used only in restart',
42 'default': None,
43 'group': 'basis',
44 'key': None,
45 'type': list,
46 'units': None,
47 'updateable': False
48 },
49 'basis set name': {
50 'comment': 'current default from module "define"',
51 'default': 'def-SV(P)',
52 'group': 'basis',
53 'key': None,
54 'type': str,
55 'units': None,
56 'updateable': False
57 },
58 'closed-shell orbital shift': {
59 'comment': 'does not work with automatic',
60 'default': None,
61 'group': 'scforbitalshift',
62 'key': 'closedshell',
63 'mapping': {
64 'to_control': lambda a: a / Ha,
65 'from_control': lambda a: a * Ha
66 },
67 'type': float,
68 'units': 'eV',
69 'updateable': True
70 },
71 'damping adjustment step': {
72 'comment': None,
73 'default': None,
74 'group': 'scfdamp',
75 'key': 'step',
76 'type': float,
77 'units': None,
78 'updateable': True
79 },
80 'default eht atomic orbitals': {
81 'comment': None,
82 'default': None,
83 'group': None,
84 'key': None,
85 'type': bool,
86 'units': None,
87 'updateable': False
88 },
89 'density convergence': {
90 'comment': None,
91 'default': None,
92 'group': 'denconv',
93 'key': 'denconv',
94 'mapping': {
95 'to_control': lambda a: int(-log10(a)),
96 'from_control': lambda a: 10**(-a)
97 },
98 'non-define': True,
99 'type': float,
100 'units': None,
101 'updateable': True
102 },
103 'density functional': {
104 'comment': None,
105 'default': 'b-p',
106 'group': 'dft',
107 'key': 'functional',
108 'type': str,
109 'units': None,
110 'updateable': True
111 },
112 'energy convergence': {
113 'comment': 'jobex -energy <int>',
114 'default': None,
115 'group': None,
116 'key': None,
117 'mapping': {
118 'to_control': lambda a: a / Ha,
119 'from_control': lambda a: a * Ha
120 },
121 'type': float,
122 'units': 'eV',
123 'updateable': True
124 },
125 'esp fit': {
126 'comment': 'ESP fit',
127 'default': None,
128 'group': 'esp_fit',
129 'key': 'esp_fit',
130 'type': str,
131 'units': None,
132 'updateable': True,
133 'non-define': True
134 },
135 'fermi annealing factor': {
136 'comment': None,
137 'default': 0.95,
138 'group': 'fermi',
139 'key': 'tmfac',
140 'type': float,
141 'units': None,
142 'updateable': True
143 },
144 'fermi final temperature': {
145 'comment': None,
146 'default': 300.,
147 'group': 'fermi',
148 'key': 'tmend',
149 'type': float,
150 'units': 'Kelvin',
151 'updateable': True
152 },
153 'fermi homo-lumo gap criterion': {
154 'comment': None,
155 'default': 0.1,
156 'group': 'fermi',
157 'key': 'hlcrt',
158 'mapping': {
159 'to_control': lambda a: a / Ha,
160 'from_control': lambda a: a * Ha
161 },
162 'type': float,
163 'units': 'eV',
164 'updateable': True
165 },
166 'fermi initial temperature': {
167 'comment': None,
168 'default': 300.,
169 'group': 'fermi',
170 'key': 'tmstrt',
171 'type': float,
172 'units': 'Kelvin',
173 'updateable': True
174 },
175 'fermi stopping criterion': {
176 'comment': None,
177 'default': 0.001,
178 'group': 'fermi',
179 'key': 'stop',
180 'mapping': {
181 'to_control': lambda a: a / Ha,
182 'from_control': lambda a: a * Ha
183 },
184 'type': float,
185 'units': 'eV',
186 'updateable': True
187 },
188 'force convergence': {
189 'comment': 'jobex -gcart <int>',
190 'default': None,
191 'group': None,
192 'key': None,
193 'mapping': {
194 'to_control': lambda a: a / Ha * Bohr,
195 'from_control': lambda a: a * Ha / Bohr
196 },
197 'type': float,
198 'units': 'eV/Angstrom',
199 'updateable': True
200 },
201 'geometry optimization iterations': {
202 'comment': 'jobex -c <int>',
203 'default': None,
204 'group': None,
205 'key': None,
206 'type': int,
207 'units': None,
208 'updateable': True
209 },
210 'grid size': {
211 'comment': None,
212 'default': 'm3',
213 'group': 'dft',
214 'key': 'gridsize',
215 'type': str,
216 'units': None,
217 'updateable': True
218 },
219 'ground state': {
220 'comment': 'only this is currently supported',
221 'default': True,
222 'group': None,
223 'key': None,
224 'type': bool,
225 'units': None,
226 'updateable': False
227 },
228 'initial damping': {
229 'comment': None,
230 'default': None,
231 'group': 'scfdamp',
232 'key': 'start',
233 'type': float,
234 'units': None,
235 'updateable': True
236 },
237 'initial guess': {
238 'comment': '"eht", "hcore" or {"use": "<path/to/control>"}',
239 'default': 'eht',
240 'group': None,
241 'key': None,
242 'type': (str, dict),
243 'units': None,
244 'updateable': False
245 },
246 'minimal damping': {
247 'comment': None,
248 'default': None,
249 'group': 'scfdamp',
250 'key': 'min',
251 'type': float,
252 'units': None,
253 'updateable': True
254 },
255 'multiplicity': {
256 'comment': None,
257 'default': None,
258 'group': None,
259 'key': None,
260 'type': int,
261 'units': None,
262 'updateable': False
263 },
264 'non-automatic orbital shift': {
265 'comment': None,
266 'default': False,
267 'group': 'scforbitalshift',
268 'key': 'noautomatic',
269 'type': bool,
270 'units': None,
271 'updateable': True
272 },
273 'numerical hessian': {
274 'comment': 'NumForce will be used if dictionary exists',
275 'default': None,
276 'group': None,
277 'key': None,
278 'type': dict,
279 'units': None,
280 'updateable': True
281 },
282 'point group': {
283 'comment': 'only c1 supported',
284 'default': 'c1',
285 'group': 'symmetry',
286 'key': 'symmetry',
287 'type': str,
288 'units': None,
289 'updateable': False
290 },
291 'ri memory': {
292 'comment': None,
293 'default': 1000,
294 'group': 'ricore',
295 'key': 'ricore',
296 'type': int,
297 'units': 'Megabyte',
298 'updateable': True
299 },
300 'rohf': {
301 'comment': 'used only in restart',
302 'default': None,
303 'group': None,
304 'key': None,
305 'type': bool,
306 'units': None,
307 'updateable': False
308 },
309 'scf energy convergence': {
310 'comment': None,
311 'default': None,
312 'group': 'scfconv',
313 'key': 'scfconv',
314 'mapping': {
315 'to_control': lambda a: int(floor(-log10(a / Ha))),
316 'from_control': lambda a: 10**(-a) * Ha
317 },
318 'type': float,
319 'units': 'eV',
320 'updateable': True
321 },
322 'scf iterations': {
323 'comment': None,
324 'default': 60,
325 'group': 'scfiterlimit',
326 'key': 'scfiterlimit',
327 'type': int,
328 'units': None,
329 'updateable': True
330 },
331 'task': {
332 'comment': '"energy calculation" = "energy", '
333 '"gradient calculation" = "gradient", '
334 '"geometry optimization" = "optimize", '
335 '"normal mode analysis" = "frequencies"',
336 'default': 'energy',
337 'group': None,
338 'key': None,
339 'type': str,
340 'units': None,
341 'updateable': True
342 },
343 'title': {
344 'comment': None,
345 'default': '',
346 'group': 'title',
347 'key': 'title',
348 'type': str,
349 'units': None,
350 'updateable': False
351 },
352 'total charge': {
353 'comment': None,
354 'default': 0,
355 'group': None,
356 'key': None,
357 'type': int,
358 'units': None,
359 'updateable': False
360 },
361 'transition vector': {
362 'comment': 'vector for transition state optimization',
363 'default': None,
364 'group': 'statpt',
365 'key': 'itrvec',
366 'type': int,
367 'units': None,
368 'updateable': True,
369 'non-define': True
370 },
371 'uhf': {
372 'comment': None,
373 'default': None,
374 'group': 'uhf',
375 'key': 'uhf',
376 'type': bool,
377 'units': None,
378 'updateable': False
379 },
380 'use basis set library': {
381 'comment': 'only true implemented',
382 'default': True,
383 'group': 'basis',
384 'key': None,
385 'type': bool,
386 'units': None,
387 'updateable': False
388 },
389 'use dft': {
390 'comment': None,
391 'default': True,
392 'group': 'dft',
393 'key': 'dft',
394 'type': bool,
395 'units': None,
396 'updateable': False
397 },
398 'use fermi smearing': {
399 'comment': None,
400 'default': False,
401 'group': 'fermi',
402 'key': 'fermi',
403 'type': bool,
404 'units': None,
405 'updateable': True
406 },
407 'use redundant internals': {
408 'comment': None,
409 'default': False,
410 'group': 'redundant',
411 'key': None,
412 'type': bool,
413 'units': None,
414 'updateable': False
415 },
416 'use resolution of identity': {
417 'comment': None,
418 'default': False,
419 'group': 'rij',
420 'key': 'rij',
421 'type': bool,
422 'units': None,
423 'updateable': False
424 }
425 }
427 spec_names = {
428 'default': 'default_parameters',
429 'comment': 'parameter_comment',
430 'updateable': 'parameter_updateable',
431 'type': 'parameter_type',
432 'key': 'parameter_key',
433 'group': 'parameter_group',
434 'units': 'parameter_units',
435 'mapping': 'parameter_mapping',
436 'non-define': 'parameter_no_define'
437 }
438 # flat dictionaries with parameters attributes
439 default_parameters = {}
440 parameter_group = {}
441 parameter_type = {}
442 parameter_key = {}
443 parameter_units = {}
444 parameter_comment = {}
445 parameter_updateable = {}
446 parameter_mapping = {}
447 parameter_no_define = {}
449 def __init__(self, **kwargs):
450 # construct flat dictionaries with parameter attributes
451 for p in self.parameter_spec:
452 for k in self.spec_names:
453 if k in list(self.parameter_spec[p].keys()):
454 subdict = getattr(self, self.spec_names[k])
455 subdict.update({p: self.parameter_spec[p][k]})
456 super().__init__(**self.default_parameters)
457 self.update(kwargs)
459 def update(self, dct):
460 """check the type of parameters in dct and then update"""
461 for par in dct.keys():
462 if par not in self.parameter_spec:
463 raise ValueError('invalid parameter: ' + par)
465 for key, val in dct.items():
466 correct_type = self.parameter_spec[key]['type']
467 if not isinstance(val, (correct_type, type(None))):
468 msg = str(key) + ' has wrong type: ' + str(type(val))
469 raise TypeError(msg)
470 self[key] = val
472 def update_data_groups(self, params_update):
473 """updates data groups in the control file"""
474 # construct a list of data groups to update
475 grps = []
476 for p in list(params_update.keys()):
477 if self.parameter_group[p] is not None:
478 grps.append(self.parameter_group[p])
480 # construct a dictionary of data groups and update params
481 dgs = {}
482 for g in grps:
483 dgs[g] = {}
484 for p in self.parameter_key:
485 if g == self.parameter_group[p]:
486 if self.parameter_group[p] == self.parameter_key[p]:
487 if p in list(params_update.keys()):
488 val = params_update[p]
489 pmap = list(self.parameter_mapping.keys())
490 if val is not None and p in pmap:
491 fun = self.parameter_mapping[p]['to_control']
492 val = fun(params_update[p])
493 dgs[g] = val
494 else:
495 if p in list(self.params_old.keys()):
496 val = self.params_old[p]
497 pmap = list(self.parameter_mapping.keys())
498 if val is not None and p in pmap:
499 fun = self.parameter_mapping[p]['to_control']
500 val = fun(self.params_old[p])
501 dgs[g][self.parameter_key[p]] = val
502 if p in list(params_update.keys()):
503 val = params_update[p]
504 pmap = list(self.parameter_mapping.keys())
505 if val is not None and p in pmap:
506 fun = self.parameter_mapping[p]['to_control']
507 val = fun(params_update[p])
508 dgs[g][self.parameter_key[p]] = val
510 # write dgs dictionary to a data group
511 for g in dgs:
512 delete_data_group(g)
513 if isinstance(dgs[g], dict):
514 string = ''
515 for key in list(dgs[g].keys()):
516 if dgs[g][key] is None:
517 continue
518 elif isinstance(dgs[g][key], bool):
519 if dgs[g][key]:
520 string += ' ' + key
521 else:
522 string += ' ' + key + '=' + str(dgs[g][key])
523 add_data_group(g, string=string)
524 else:
525 if isinstance(dgs[g], bool):
526 if dgs[g]:
527 add_data_group(g, string='')
528 else:
529 add_data_group(g, string=str(dgs[g]))
531 def update_no_define_parameters(self):
532 """process key parameters that are not written with define"""
533 for p, v in self.items():
534 if p in self.parameter_no_define and self.parameter_no_define[p]:
535 if v:
536 if p in self.parameter_mapping:
537 fun = self.parameter_mapping[p]['to_control']
538 val = fun(v)
539 else:
540 val = v
541 delete_data_group(self.parameter_group[p])
542 if self.parameter_group[p] != self.parameter_key[p]:
543 val = '\n ' + self.parameter_key[p] + ' ' + str(val)
544 add_data_group(self.parameter_group[p], str(val))
545 else:
546 delete_data_group(self.parameter_group[p])
548 def verify(self):
549 """detect wrong or not implemented parameters"""
551 if getattr(self, 'define_str', None) is not None:
552 assert isinstance(self.define_str, str), 'define_str must be str'
553 assert len(self.define_str) != 0, 'define_str may not be empty'
554 else:
555 for par in self:
556 assert par in self.parameter_spec, 'invalid parameter: ' + par
558 if self.get('use dft'):
559 func_list = [x.lower() for x in self.available_functionals]
560 func = self['density functional']
561 assert func.lower() in func_list, (
562 'density functional not available / not supported'
563 )
565 assert self['multiplicity'] is not None, 'multiplicity not defined'
566 assert self['multiplicity'] > 0, 'multiplicity has wrong value'
568 if self.get('rohf'):
569 raise NotImplementedError('ROHF not implemented')
570 if self['initial guess'] not in ['eht', 'hcore']:
571 if not (isinstance(self['initial guess'], dict) and
572 'use' in self['initial guess'].keys()):
573 raise ValueError('Wrong input for initial guess')
574 if not self['use basis set library']:
575 raise NotImplementedError('Explicit basis set definition')
576 if self['point group'] != 'c1':
577 raise NotImplementedError('Point group not impemeneted')
579 def get_define_str(self, natoms):
580 """construct a define string from the parameters dictionary"""
582 if getattr(self, 'define_str', None):
583 return self.define_str
585 define_str_tpl = (
586 '\n__title__\na coord\n__inter__\n'
587 'bb all __basis_set__\n*\neht\n__eht_aos_str__y\n__charge_str__'
588 '__occ_str____single_atom_str____norb_str____dft_str____ri_str__'
589 '__scfiterlimit____fermi_str____damp_str__q\n'
590 )
592 params = self
594 if params['use redundant internals']:
595 internals_str = 'ired\n*'
596 else:
597 internals_str = '*\nno'
598 charge_str = str(params['total charge']) + '\n'
600 if params['multiplicity'] == 1:
601 if params['uhf']:
602 occ_str = 'n\ns\n*\n'
603 else:
604 occ_str = 'y\n'
605 elif params['multiplicity'] == 2:
606 occ_str = 'y\n'
607 elif params['multiplicity'] == 3:
608 occ_str = 'n\nt\n*\n'
609 else:
610 unpaired = params['multiplicity'] - 1
611 if params['use fermi smearing']:
612 occ_str = 'n\nuf ' + str(unpaired) + '\n*\n'
613 else:
614 occ_str = 'n\nu ' + str(unpaired) + '\n*\n'
616 if natoms != 1:
617 single_atom_str = ''
618 else:
619 single_atom_str = '\n'
621 if params['multiplicity'] == 1 and not params['uhf']:
622 norb_str = ''
623 else:
624 norb_str = 'n\n'
626 if params['use dft']:
627 dft_str = 'dft\non\n*\n'
628 else:
629 dft_str = ''
631 if params['density functional']:
632 dft_str += 'dft\nfunc ' + params['density functional'] + '\n*\n'
634 if params['grid size']:
635 dft_str += 'dft\ngrid ' + params['grid size'] + '\n*\n'
637 if params['use resolution of identity']:
638 ri_str = 'ri\non\nm ' + str(params['ri memory']) + '\n*\n'
639 else:
640 ri_str = ''
642 if params['scf iterations']:
643 scfmaxiter = params['scf iterations']
644 scfiter_str = 'scf\niter\n' + str(scfmaxiter) + '\n\n'
645 else:
646 scfiter_str = ''
647 if params['scf energy convergence']:
648 conv = floor(-log10(params['scf energy convergence'] / Ha))
649 scfiter_str += 'scf\nconv\n' + str(int(conv)) + '\n\n'
651 fermi_str = ''
652 if params['use fermi smearing']:
653 fermi_str = 'scf\nfermi\n'
654 if params['fermi initial temperature']:
655 par = str(params['fermi initial temperature'])
656 fermi_str += '1\n' + par + '\n'
657 if params['fermi final temperature']:
658 par = str(params['fermi final temperature'])
659 fermi_str += '2\n' + par + '\n'
660 if params['fermi annealing factor']:
661 par = str(params['fermi annealing factor'])
662 fermi_str += '3\n' + par + '\n'
663 if params['fermi homo-lumo gap criterion']:
664 par = str(params['fermi homo-lumo gap criterion'])
665 fermi_str += '4\n' + par + '\n'
666 if params['fermi stopping criterion']:
667 par = str(params['fermi stopping criterion'])
668 fermi_str += '5\n' + par + '\n'
669 fermi_str += '\n\n'
671 damp_str = ''
672 damp_keys = ('initial damping', 'damping adjustment step',
673 'minimal damping')
674 damp_pars = [params[k] for k in damp_keys]
675 if any(damp_pars):
676 damp_str = 'scf\ndamp\n'
677 for par in damp_pars:
678 par_str = str(par) if par else ''
679 damp_str += par_str + '\n'
680 damp_str += '\n'
682 eht_aos_str = 'y\n' if params['default eht atomic orbitals'] else ''
684 define_str = define_str_tpl
685 define_str = re.sub('__title__', params['title'], define_str)
686 define_str = re.sub('__basis_set__', params['basis set name'],
687 define_str)
688 define_str = re.sub('__charge_str__', charge_str, define_str)
689 define_str = re.sub('__occ_str__', occ_str, define_str)
690 define_str = re.sub('__norb_str__', norb_str, define_str)
691 define_str = re.sub('__dft_str__', dft_str, define_str)
692 define_str = re.sub('__ri_str__', ri_str, define_str)
693 define_str = re.sub('__single_atom_str__', single_atom_str,
694 define_str)
695 define_str = re.sub('__inter__', internals_str, define_str)
696 define_str = re.sub('__scfiterlimit__', scfiter_str, define_str)
697 define_str = re.sub('__fermi_str__', fermi_str, define_str)
698 define_str = re.sub('__damp_str__', damp_str, define_str)
699 define_str = re.sub('__eht_aos_str__', eht_aos_str, define_str)
701 return define_str
703 def read_restart(self, atoms, results):
704 """read parameters from control file"""
706 params = {}
707 pdgs = {}
708 for p in self.parameter_group:
709 if self.parameter_group[p] and self.parameter_key[p]:
710 pdgs[p] = parse_data_group(
711 read_data_group(self.parameter_group[p]),
712 self.parameter_group[p]
713 )
715 for p in self.parameter_key:
716 if self.parameter_key[p]:
717 if self.parameter_key[p] == self.parameter_group[p]:
718 if pdgs[p] is None:
719 if self.parameter_type[p] is bool:
720 params[p] = False
721 else:
722 params[p] = None
723 else:
724 if self.parameter_type[p] is bool:
725 params[p] = True
726 else:
727 typ = self.parameter_type[p]
728 val = typ(pdgs[p])
729 mapping = self.parameter_mapping
730 if p in list(mapping.keys()):
731 fun = mapping[p]['from_control']
732 val = fun(val)
733 params[p] = val
734 else:
735 if pdgs[p] is None:
736 params[p] = None
737 elif isinstance(pdgs[p], str):
738 if self.parameter_type[p] is bool:
739 params[p] = (pdgs[p] == self.parameter_key[p])
740 else:
741 if self.parameter_key[p] not in list(pdgs[p].keys()):
742 if self.parameter_type[p] is bool:
743 params[p] = False
744 else:
745 params[p] = None
746 else:
747 typ = self.parameter_type[p]
748 val = typ(pdgs[p][self.parameter_key[p]])
749 mapping = self.parameter_mapping
750 if p in list(mapping.keys()):
751 fun = mapping[p]['from_control']
752 val = fun(val)
753 params[p] = val
755 # non-group or non-key parameters
757 # per-element and per-atom basis sets not implemented in calculator
758 basis_sets = {bs['nickname'] for bs in results['basis set']}
759 assert len(basis_sets) == 1
760 params['basis set name'] = list(basis_sets)[0]
761 params['basis set definition'] = results['basis set']
763 # rohf, multiplicity and total charge
764 orbs = results['molecular orbitals']
765 params['rohf'] = (bool(len(read_data_group('rohf'))) or
766 bool(len(read_data_group('roothaan'))))
767 core_charge = 0
768 if results['ecps']:
769 for ecp in results['ecps']:
770 for symbol in atoms.get_chemical_symbols():
771 if symbol.lower() == ecp['element'].lower():
772 core_charge -= ecp['number of core electrons']
773 if params['uhf']:
774 alpha_occ = [o['occupancy'] for o in orbs if o['spin'] == 'alpha']
775 beta_occ = [o['occupancy'] for o in orbs if o['spin'] == 'beta']
776 spin = (np.sum(alpha_occ) - np.sum(beta_occ)) * 0.5
777 params['multiplicity'] = int(2 * spin + 1)
778 nuclear_charge = int(sum(atoms.numbers))
779 electron_charge = -int(sum(alpha_occ) + sum(beta_occ))
780 electron_charge += core_charge
781 params['total charge'] = nuclear_charge + electron_charge
782 elif not params['rohf']: # restricted HF (closed shell)
783 params['multiplicity'] = 1
784 nuclear_charge = int(sum(atoms.numbers))
785 electron_charge = -int(sum(o['occupancy'] for o in orbs))
786 electron_charge += core_charge
787 params['total charge'] = nuclear_charge + electron_charge
788 else:
789 raise NotImplementedError('ROHF not implemented')
791 # task-related parameters
792 if os.path.exists('job.start'):
793 with open('job.start') as log:
794 lines = log.readlines()
795 for line in lines:
796 if 'CRITERION FOR TOTAL SCF-ENERGY' in line:
797 en = int(re.search(r'10\*{2}\((-\d+)\)', line).group(1))
798 mapp = self.parameter_mapping['energy convergence']
799 params['energy convergence'] = mapp['from_control'](10**en)
800 if 'CRITERION FOR MAXIMUM NORM OF SCF-ENERGY GRADIENT' in line:
801 gr = int(re.search(r'10\*{2}\((-\d+)\)', line).group(1))
802 mapp = self.parameter_mapping['force convergence']
803 params['force convergence'] = mapp['from_control'](10**gr)
804 if 'AN OPTIMIZATION WITH MAX' in line:
805 cy = int(re.search(r'MAX. (\d+) CYCLES', line).group(1))
806 params['geometry optimization iterations'] = cy
807 self.update(params)
808 self.params_old = params
810 def update_restart(self, dct):
811 """update parameters after a restart"""
812 nulst = [k for k in dct.keys() if not self.parameter_updateable[k]]
813 if len(nulst) != 0:
814 raise ValueError(f'parameters {nulst} cannot be changed')
815 self.update(dct)
816 self.update_data_groups(dct)