Skip to content

[Code scan] Forward spin inputs through the dpmodel DeepEval backend #5661

Description

@njzjz

This issue comes from a Codex global scan of deepmodeling/deepmd-kit at commit 73de44b1f94471b2e3bdb6b11f57b34d7bc791bb.

Problem

The dpmodel DeepEval backend accepts **kwargs, but it drops them before calling the deserialized model. This makes spin models unevaluable through this backend because SpinModel.call() requires a spin argument.

DeepEval.eval() accepts arbitrary keyword arguments:

def eval(
self,
coords: Array,
cells: Array | None,
atom_types: Array,
atomic: bool = False,
fparam: Array | None = None,
aparam: Array | None = None,
**kwargs: Any,
) -> dict[str, Array]:

However, the batched call into _eval_model() passes only coordinates, cells, atom types, fparam, aparam, and request definitions:

request_defs = self._get_request_defs(atomic)
out = self._eval_func(self._eval_model, numb_test, natoms)(
coords, cells, atom_types, fparam, aparam, request_defs
)

_eval_model() has no **kwargs parameter and calls the model without forwarding spin or any other extra inputs:

def _eval_model(
self,
coords: Array,
cells: Array | None,
atom_types: Array,
fparam: Array | None,
aparam: Array | None,
request_defs: list[OutputVariableDef],
) -> dict[str, Array]:
model = self.dp
nframes = coords.shape[0]
if len(atom_types.shape) == 1:
natoms = len(atom_types)
atom_types = np.tile(atom_types, nframes).reshape(nframes, -1)
else:
natoms = len(atom_types[0])
coord_input = coords.reshape([-1, natoms, 3])
type_input = atom_types
if cells is not None:
box_input = cells.reshape([-1, 3, 3])
else:
box_input = None
if fparam is not None:
fparam_input = fparam.reshape(nframes, self.get_dim_fparam())
else:
fparam_input = None
if aparam is not None:
aparam_input = aparam.reshape(nframes, natoms, self.get_dim_aparam())
else:
aparam_input = None
do_atomic_virial = any(
x.category == OutputVariableCategory.DERV_C_REDU for x in request_defs
)
batch_output = model(
coord_input,
type_input,
box=box_input,
fparam=fparam_input,
aparam=aparam_input,
do_atomic_virial=do_atomic_virial,
)

But SpinModel.call() requires spin:

def call(
self,
coord: Array,
atype: Array,
spin: Array,
box: Array | None = None,
fparam: Array | None = None,
aparam: Array | None = None,
do_atomic_virial: bool = False,
charge_spin: Array | None = None,

The same backend exposes spin metadata through get_has_spin(), so this path appears intended to support spin models:

def get_has_spin(self) -> bool:
"""Check if the model has spin atom types."""
return hasattr(self.dp, "spin")
def get_use_spin(self) -> list[bool]:
"""Get the per-type spin usage of this model."""
if hasattr(self.dp, "spin"):
return self.dp.spin.use_spin.tolist()
return []

Impact

Calling the dpmodel inference backend on a serialized spin model cannot supply the spin tensor, so evaluation fails before producing energy/force outputs.

Suggested fix

Pass **kwargs through the auto-batch wrapper into _eval_model(), add **kwargs to _eval_model(), and forward the supported extra inputs into model(...). Add a dpmodel spin inference regression that calls the public evaluator with spin=....

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    In Progress

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions