Skip to content

Commit b4b22df

Browse files
committed
perf(dpa4): c++ infer path
1 parent 2dac033 commit b4b22df

4 files changed

Lines changed: 86 additions & 40 deletions

File tree

source/api_cc/include/DeepPot.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,24 @@ class DeepPotBackend : public DeepBaseModelBackend {
307307
computew_mixed_type(ener, force, virial, atom_energy, atom_virial, nframes,
308308
coord, atype, box, fparam, aparam, atomic);
309309
}
310+
/**
311+
* @brief GPU-resident edge-input inference for the SeZM/DPA4 graph-form .pt2:
312+
*given device edge tensors, write per-atom energy / force / virial back to
313+
* the device output pointers. The PyTorch Exportable backend overrides this;
314+
* every other backend inherits the throwing default. The signature is
315+
* intentionally torch-free so the dispatcher stays backend-agnostic (no
316+
* dynamic_cast into a PyTorch-heavy type, so ``libdeepmd_cc`` need not link
317+
* PyTorch).
318+
*/
319+
virtual void compute_edges_gpu(double* d_atom_energy,
320+
double* d_force,
321+
double* d_atom_virial,
322+
const double* d_coord,
323+
const int* d_atype,
324+
const int* d_edge_index,
325+
const double* d_edge_vec,
326+
const int nloc,
327+
const int nedge);
310328
};
311329

312330
/**

source/api_cc/include/DeepPotPTExpt.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ class DeepPotPTExpt : public DeepPotBackend {
318318
const int* d_edge_index,
319319
const double* d_edge_vec,
320320
const int nloc,
321-
const int nedge);
321+
const int nedge) override;
322322

323323
private:
324324
bool inited;

source/api_cc/src/DeepPot.cc

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -564,25 +564,15 @@ template void DeepPot::compute_mixed_type<float>(
564564
const std::vector<float>& aparam,
565565
const std::vector<double>& charge_spin);
566566

567-
void DeepPot::compute_edges_gpu(double* d_atom_energy,
568-
double* d_force,
569-
double* d_atom_virial,
570-
const double* d_coord,
571-
const int* d_atype,
572-
const int* d_edge_index,
573-
const double* d_edge_vec,
574-
const int nloc,
575-
const int nedge) {
576-
#if defined(BUILD_PYTORCH) && BUILD_PT_EXPT
577-
deepmd::DeepPotPTExpt* expt = dynamic_cast<deepmd::DeepPotPTExpt*>(dp.get());
578-
if (expt == nullptr) {
579-
throw deepmd::deepmd_exception(
580-
"compute_edges_gpu is only available for the PyTorch Exportable (.pt2) "
581-
"backend.");
582-
}
583-
expt->compute_edges_gpu(d_atom_energy, d_force, d_atom_virial, d_coord,
584-
d_atype, d_edge_index, d_edge_vec, nloc, nedge);
585-
#else
567+
void DeepPotBackend::compute_edges_gpu(double* d_atom_energy,
568+
double* d_force,
569+
double* d_atom_virial,
570+
const double* d_coord,
571+
const int* d_atype,
572+
const int* d_edge_index,
573+
const double* d_edge_vec,
574+
const int nloc,
575+
const int nedge) {
586576
(void)d_atom_energy;
587577
(void)d_force;
588578
(void)d_atom_virial;
@@ -593,9 +583,28 @@ void DeepPot::compute_edges_gpu(double* d_atom_energy,
593583
(void)nloc;
594584
(void)nedge;
595585
throw deepmd::deepmd_exception(
596-
"compute_edges_gpu requires the PyTorch Exportable backend, which is not "
597-
"built.");
598-
#endif
586+
"compute_edges_gpu (GPU-resident edge inference) is only supported by "
587+
"the "
588+
"PyTorch Exportable (.pt2) backend.");
589+
}
590+
591+
void DeepPot::compute_edges_gpu(double* d_atom_energy,
592+
double* d_force,
593+
double* d_atom_virial,
594+
const double* d_coord,
595+
const int* d_atype,
596+
const int* d_edge_index,
597+
const double* d_edge_vec,
598+
const int nloc,
599+
const int nedge) {
600+
// Polymorphic dispatch to the loaded backend: the PyTorch Exportable backend
601+
// overrides ``compute_edges_gpu``; other backends inherit the throwing
602+
// default. This replaces a ``dynamic_cast`` into the PyTorch-heavy
603+
// ``DeepPotPTExpt``, which the "load backends as plugins" refactor made
604+
// uncompilable in the backend-agnostic ``libdeepmd_cc`` (it does not link
605+
// PyTorch), so the cast branch was always stubbed out.
606+
dp->compute_edges_gpu(d_atom_energy, d_force, d_atom_virial, d_coord, d_atype,
607+
d_edge_index, d_edge_vec, nloc, nedge);
599608
}
600609

601610
int DeepPot::dim_chg_spin() const { return dp->dim_chg_spin(); }

source/api_cc/src/DeepPotPTExpt.cc

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1780,9 +1780,10 @@ void DeepPotPTExpt::compute_edges_gpu(double* d_atom_energy,
17801780
"compute_edges_gpu always returns the per-atom virial, but this .pt2 "
17811781
"model was exported without it (do_atomic_virial=False).");
17821782
}
1783-
if (!lower_input_is_edge_) {
1783+
if (!lower_input_is_edge_ && !lower_input_is_graph_) {
17841784
throw deepmd::deepmd_exception(
1785-
"compute_edges_gpu requires an edge-input (SeZM/DPA4) .pt2 model.");
1785+
"compute_edges_gpu requires an edge-input (SeZM/DPA4) or graph-input "
1786+
"(DPA1/DPA2/DPA3) .pt2 model.");
17861787
}
17871788
translate_error([&] {
17881789
const torch::Device device(torch::kCUDA, gpu_id);
@@ -1857,24 +1858,42 @@ void DeepPotPTExpt::compute_edges_gpu(double* d_atom_energy,
18571858
.to(device);
18581859
}
18591860

1860-
// === Step 4. Run the exported model on the device ===
1861-
std::vector<torch::Tensor> flat_outputs = run_model_edges(
1862-
coord_t, atype_t, edge_index, edge_vec, edge_scatter_index, edge_mask,
1863-
fparam_tensor, aparam_tensor, charge_spin_tensor);
1864-
std::map<std::string, torch::Tensor> output_map;
1865-
extract_outputs(output_map, flat_outputs);
1861+
// === Step 4. Run the exported model and read the per-atom outputs ===
1862+
// The two lower forms share the masked edge tensors but differ in both the
1863+
// input set and the output naming, so each form runs and unpacks itself;
1864+
// the result is always per-atom energy (nloc), force (nloc, 3) and virial
1865+
// (nloc, 9), copied out below.
1866+
at::Tensor ae, force_t, av;
1867+
std::map<std::string, torch::Tensor> out;
1868+
if (lower_input_is_graph_) {
1869+
// Graph (DPA1/DPA2/DPA3 NeighborGraph): single-frame node count and a
1870+
// flat node-major atype; the model returns the high-level per-atom
1871+
// quantities.
1872+
const at::Tensor n_node =
1873+
torch::full({1}, static_cast<std::int64_t>(nloc), opt_i64);
1874+
extract_outputs(
1875+
out, run_model_graph(atype_t.reshape({nloc}), n_node, edge_index,
1876+
edge_vec, edge_mask, fparam_tensor,
1877+
aparam_tensor, charge_spin_tensor));
1878+
ae = out["atom_energy"].reshape({nloc}).contiguous();
1879+
force_t = out["force"].reshape({nloc, 3}).contiguous();
1880+
av = out["atom_virial"].reshape({nloc, 9}).contiguous();
1881+
} else {
1882+
// Edge (SeZM/DPA4): coord + edge_scatter_index; the model returns the raw
1883+
// reduced-energy derivatives (force/virial per extended atom).
1884+
extract_outputs(
1885+
out, run_model_edges(coord_t, atype_t, edge_index, edge_vec,
1886+
edge_scatter_index, edge_mask, fparam_tensor,
1887+
aparam_tensor, charge_spin_tensor));
1888+
ae = out["energy"].reshape({nloc}).contiguous();
1889+
force_t =
1890+
out["energy_derv_r"].squeeze(-2).reshape({nloc, 3}).contiguous();
1891+
av = out["energy_derv_c"].squeeze(-2).reshape({nloc, 9}).contiguous();
1892+
}
18661893

1867-
// === Step 5. Copy outputs into caller GPU buffers (device-to-device) ===
1868-
// Per-atom energy: energy (1, nloc, 1) -> (nloc)
1869-
at::Tensor ae = output_map["energy"].reshape({nloc}).contiguous();
1894+
// === Step 5. Copy per-atom outputs into caller GPU buffers (D2D) ===
18701895
torch::from_blob(d_atom_energy, {nloc}, opt_f64).copy_(ae);
1871-
// Force: energy_derv_r (1, nloc, 1, 3) -> (nloc, 3) row-major
1872-
at::Tensor force_t =
1873-
output_map["energy_derv_r"].squeeze(-2).reshape({nloc, 3}).contiguous();
18741896
torch::from_blob(d_force, {nloc, 3}, opt_f64).copy_(force_t);
1875-
// Per-atom virial: energy_derv_c (1, nloc, 1, 9) -> (nloc, 9) row-major
1876-
at::Tensor av =
1877-
output_map["energy_derv_c"].squeeze(-2).reshape({nloc, 9}).contiguous();
18781897
torch::from_blob(d_atom_virial, {nloc, 9}, opt_f64).copy_(av);
18791898
});
18801899
}

0 commit comments

Comments
 (0)