Coverage for /builds/ase/ase/ase/calculators/turbomole/parameters.py : 39.32%

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