11# SPDX-License-Identifier: LGPL-3.0-or-later
22import copy
3- from typing import (
4- Any ,
5- )
63
74from deepmd .dpmodel .atomic_model .dp_atomic_model import (
85 DPAtomicModel ,
1613from deepmd .dpmodel .fitting .base_fitting import (
1714 BaseFitting ,
1815)
19- from deepmd .dpmodel .fitting .ener_fitting import (
20- EnergyFittingNet ,
21- )
2216from deepmd .dpmodel .model .base_model import (
2317 BaseModel ,
2418)
25- from deepmd .dpmodel .model .dipole_model import (
26- DipoleModel ,
27- )
28- from deepmd .dpmodel .model .dos_model import (
29- DOSModel ,
30- )
3119from deepmd .dpmodel .model .dp_zbl_model import (
3220 DPZBLModel ,
3321)
34- from deepmd .dpmodel .model .dpa4_model import (
35- DPA4EnergyModel ,
22+ from deepmd .dpmodel .model .model_factory import (
23+ BackendModelFactory ,
3624)
37- from deepmd .dpmodel .model .ener_model import (
38- EnergyModel ,
25+ from deepmd .dpmodel .model .model_factory import (
26+ get_spin_model as get_spin_model_from_factory ,
3927)
4028from deepmd .dpmodel .model .native_spin_model import (
4129 NativeSpinEnergyModel ,
4230)
43- from deepmd .dpmodel .model .polar_model import (
44- PolarModel ,
45- )
46- from deepmd .dpmodel .model .property_model import (
47- PropertyModel ,
48- )
4931from deepmd .dpmodel .model .spin_model import (
5032 SpinModel ,
5133)
5436 normalize_spin_use_spin ,
5537)
5638
57- _DPA4_SEZM_DESCRIPTOR_TYPES = ("dpa4" , "DPA4" , "sezm" , "SeZM" )
58-
39+ _model_factory = BackendModelFactory (
40+ descriptor_base = BaseDescriptor ,
41+ fitting_base = BaseFitting ,
42+ model_base = BaseModel ,
43+ backend_name = "DP" ,
44+ atomic_model = DPAtomicModel ,
45+ pairtab_model = PairTabAtomicModel ,
46+ zbl_model = DPZBLModel ,
47+ )
48+ get_zbl_model = _model_factory .get_zbl_model
5949
60- def _get_standard_model_components (
61- data : dict [str , Any ], ntypes : int
62- ) -> tuple [BaseDescriptor , BaseFitting , str ]:
63- # descriptor
64- data ["descriptor" ]["ntypes" ] = ntypes
65- data ["descriptor" ]["type_map" ] = copy .deepcopy (data ["type_map" ])
66- descriptor = BaseDescriptor (** data ["descriptor" ])
67- # fitting
68- fitting_net = data .get ("fitting_net" , {})
69- fitting_net ["type" ] = fitting_net .get ("type" , "ener" )
70- fitting_net ["ntypes" ] = descriptor .get_ntypes ()
71- fitting_net ["type_map" ] = copy .deepcopy (data ["type_map" ])
72- fitting_net ["mixed_types" ] = descriptor .mixed_types ()
73- if fitting_net ["type" ] in ["dipole" , "polar" ]:
74- fitting_net ["embedding_width" ] = descriptor .get_dim_emb ()
75- fitting_net ["dim_descrpt" ] = descriptor .get_dim_out ()
76- grad_force = "direct" not in fitting_net ["type" ]
77- if not grad_force :
78- fitting_net ["out_dim" ] = descriptor .get_dim_emb ()
79- if "ener" in fitting_net ["type" ]:
80- fitting_net ["return_energy" ] = True
81- fitting = BaseFitting (** fitting_net )
82- return descriptor , fitting , fitting_net ["type" ]
50+ _DPA4_SEZM_DESCRIPTOR_TYPES = ("dpa4" , "DPA4" , "sezm" , "SeZM" )
8351
8452
85- def get_standard_model (data : dict ) -> EnergyModel :
86- """Get a EnergyModel from a dictionary.
53+ def get_standard_model (data : dict ) -> BaseModel :
54+ """Get a standard model from a dictionary.
8755
8856 Parameters
8957 ----------
9058 data : dict
9159 The data to construct the model.
9260 """
93- if "type_embedding" in data :
94- raise ValueError (
95- "In the DP backend, type_embedding is not at the model level, but within the descriptor. See type embedding documentation for details."
96- )
9761 data = copy .deepcopy (data )
98- ntypes = len (data ["type_map" ])
9962 # Analytical bridging (e.g. ZBL): the radii feed the DESCRIPTOR's
10063 # InnerClamp/BridgingSwitch (mirrors pt's builder); the method builds the
10164 # atomic model's InterPotential below.
@@ -104,105 +67,42 @@ def get_standard_model(data: dict) -> EnergyModel:
10467 if bridging_enabled :
10568 data ["descriptor" ]["inner_clamp_r_inner" ] = data .get ("bridging_r_inner" , 0.5 )
10669 data ["descriptor" ]["inner_clamp_r_outer" ] = data .get ("bridging_r_outer" , 0.8 )
107- descriptor , fitting , fitting_net_type = _get_standard_model_components (data , ntypes )
70+ model = _model_factory .get_standard_model (data )
71+ if not bridging_enabled :
72+ return model
73+
74+ descriptor = model .atomic_model .descriptor
10875 atom_exclude_types = data .get ("atom_exclude_types" , [])
10976 pair_exclude_types = data .get ("pair_exclude_types" , [])
110-
111- if fitting_net_type == "dipole" :
112- modelcls = DipoleModel
113- elif fitting_net_type == "polar" :
114- modelcls = PolarModel
115- elif fitting_net_type == "dos" :
116- modelcls = DOSModel
117- elif fitting_net_type in ["ener" , "direct_force_ener" ]:
118- modelcls = EnergyModel
119- elif fitting_net_type in ["dpa4_ener" , "sezm_ener" ]:
120- modelcls = DPA4EnergyModel
121- elif fitting_net_type == "property" :
122- modelcls = PropertyModel
123- else :
124- raise RuntimeError (f"Unknown fitting type: { fitting_net_type } " )
125-
126- model = modelcls (
127- descriptor = descriptor ,
128- fitting = fitting ,
129- type_map = data ["type_map" ],
130- atom_exclude_types = atom_exclude_types ,
131- pair_exclude_types = pair_exclude_types ,
77+ # Composition, not a flag (first-principles design): the analytical
78+ # bridging term is its own atomic model, summed with the learned one by the
79+ # existing linear composition machinery.
80+ from deepmd .dpmodel .atomic_model .inter_potential import (
81+ InterPotentialAtomicModel ,
82+ )
83+ from deepmd .dpmodel .atomic_model .linear_atomic_model import (
84+ LinearEnergyAtomicModel ,
85+ )
86+ from deepmd .dpmodel .model .dp_linear_model import (
87+ LinearEnergyModel ,
13288 )
133- if bridging_enabled :
134- # Composition, not a flag (first-principles design): the analytical
135- # bridging term is its own atomic model, summed with the learned one
136- # by the existing linear composition machinery.
137- from deepmd .dpmodel .atomic_model .inter_potential import (
138- InterPotentialAtomicModel ,
139- )
140- from deepmd .dpmodel .atomic_model .linear_atomic_model import (
141- LinearEnergyAtomicModel ,
142- )
143- from deepmd .dpmodel .model .dp_linear_model import (
144- LinearEnergyModel ,
145- )
146-
147- zbl_atomic = InterPotentialAtomicModel (
148- type_map = data ["type_map" ],
149- mode = bridging_method ,
150- rcut = descriptor .get_rcut (),
151- sel = descriptor .get_sel (),
152- )
153- composed = LinearEnergyAtomicModel (
154- models = [model .atomic_model , zbl_atomic ],
155- type_map = data ["type_map" ],
156- weights = "sum" ,
157- # Both exclusions belong to the composition: its children share one
158- # graph, so "excluded" must cover the analytical term too.
159- atom_exclude_types = atom_exclude_types ,
160- pair_exclude_types = pair_exclude_types ,
161- )
162- return LinearEnergyModel (atomic_model_ = composed )
163- return model
164-
165-
166- def get_zbl_model (data : dict ) -> DPZBLModel :
167- data = copy .deepcopy (data )
168- data ["descriptor" ]["ntypes" ] = len (data ["type_map" ])
169- data ["descriptor" ]["type_map" ] = data ["type_map" ]
170- descriptor = BaseDescriptor (** data ["descriptor" ])
171- fitting_type = data ["fitting_net" ].pop ("type" )
172- data ["fitting_net" ]["type_map" ] = data ["type_map" ]
173- if fitting_type == "ener" :
174- fitting = EnergyFittingNet (
175- ntypes = descriptor .get_ntypes (),
176- dim_descrpt = descriptor .get_dim_out (),
177- mixed_types = descriptor .mixed_types (),
178- ** data ["fitting_net" ],
179- )
180- else :
181- raise ValueError (f"Unknown fitting type { fitting_type } " )
18289
183- dp_model = DPAtomicModel (descriptor , fitting , type_map = data ["type_map" ])
184- # pairtab
185- filepath = data ["use_srtab" ]
186- pt_model = PairTabAtomicModel (
187- filepath ,
188- descriptor .get_rcut (),
189- descriptor .get_sel (),
90+ zbl_atomic = InterPotentialAtomicModel (
19091 type_map = data ["type_map" ],
92+ mode = bridging_method ,
93+ rcut = descriptor .get_rcut (),
94+ sel = descriptor .get_sel (),
19195 )
192-
193- rmin = data ["sw_rmin" ]
194- rmax = data ["sw_rmax" ]
195- atom_exclude_types = data .get ("atom_exclude_types" , [])
196- pair_exclude_types = data .get ("pair_exclude_types" , [])
197- return DPZBLModel (
198- dp_model ,
199- pt_model ,
200- rmin ,
201- rmax ,
96+ composed = LinearEnergyAtomicModel (
97+ models = [model .atomic_model , zbl_atomic ],
20298 type_map = data ["type_map" ],
99+ weights = "sum" ,
100+ # Both exclusions belong to the composition: its children share one
101+ # graph, so "excluded" must cover the analytical term too.
203102 atom_exclude_types = atom_exclude_types ,
204103 pair_exclude_types = pair_exclude_types ,
205104 )
105+ return LinearEnergyModel (atomic_model_ = composed )
206106
207107
208108def get_spin_model (data : dict ) -> SpinModel :
@@ -218,30 +118,11 @@ def get_spin_model(data: dict) -> SpinModel:
218118 "the virtual-atom (deepspin) scheme is not supported for "
219119 "DPA4/SeZM; use spin scheme 'native'"
220120 )
221- data = copy .deepcopy (data )
222- # include virtual spin and placeholder types
223- data ["type_map" ] += [item + "_spin" for item in data ["type_map" ]]
224- spin = Spin (
225- use_spin = data ["spin" ]["use_spin" ],
226- virtual_scale = data ["spin" ]["virtual_scale" ],
227- )
228- pair_exclude_types = spin .get_pair_exclude_types (
229- exclude_types = data .get ("pair_exclude_types" , None )
121+ return get_spin_model_from_factory (
122+ data ,
123+ standard_model_factory = get_standard_model ,
124+ spin_model = SpinModel ,
230125 )
231- data ["pair_exclude_types" ] = pair_exclude_types
232- # for descriptor data stat
233- data ["descriptor" ]["exclude_types" ] = pair_exclude_types
234- atom_exclude_types = spin .get_atom_exclude_types (
235- exclude_types = data .get ("atom_exclude_types" , None )
236- )
237- data ["atom_exclude_types" ] = atom_exclude_types
238- if "env_protection" not in data ["descriptor" ]:
239- data ["descriptor" ]["env_protection" ] = 1e-6
240- if data ["descriptor" ]["type" ] in ["se_e2_a" ]:
241- # only expand sel for se_e2_a
242- data ["descriptor" ]["sel" ] += data ["descriptor" ]["sel" ]
243- backbone_model = get_standard_model (data )
244- return SpinModel (backbone_model = backbone_model , spin = spin )
245126
246127
247128def get_native_spin_model (data : dict ) -> NativeSpinEnergyModel :
@@ -315,15 +196,9 @@ def get_model(data: dict) -> BaseModel:
315196 data : dict
316197 The data to construct the model.
317198 """
318- model_type = data .get ("type" , "standard" )
319- if model_type == "standard" :
320- if "spin" in data :
321- if data ["spin" ].get ("scheme" , "deepspin" ) == "native" :
322- return get_native_spin_model (data )
323- return get_spin_model (data )
324- elif "use_srtab" in data :
325- return get_zbl_model (data )
326- else :
327- return get_standard_model (data )
328- else :
329- return BaseModel .get_class_by_type (model_type ).get_model (data )
199+ return _model_factory .get_model (
200+ data ,
201+ standard_model_factory = get_standard_model ,
202+ spin_model_factory = get_spin_model ,
203+ native_spin_model_factory = get_native_spin_model ,
204+ )
0 commit comments