@@ -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