@@ -81,6 +81,61 @@ void validate_cartesian_input(const std::vector<VALUETYPE>& values,
8181 }
8282}
8383
84+ /* *
85+ * @brief Derive the frame count carried by a per-atom Cartesian input.
86+ *
87+ * The standalone entry points take the frame count nowhere in their
88+ * signature; it is the coordinates that hold it, as a whole multiple of the
89+ * atom count.
90+ */
91+ template <typename VALUETYPE >
92+ int frame_count (const std::vector<VALUETYPE >& coord,
93+ const std::size_t atom_count) {
94+ const std::size_t stride = atom_count * 3 ;
95+ if (coord.size () % stride != 0 || coord.empty ()) {
96+ throw deepmd::deepmd_exception (
97+ " coord holds " + std::to_string (coord.size ()) +
98+ " values, which is not a whole number of frames of " +
99+ std::to_string (atom_count) + " atoms" );
100+ }
101+ return static_cast <int >(coord.size () / stride);
102+ }
103+
104+ /* *
105+ * @brief Validate an input laid out as one block of @p stride per frame.
106+ *
107+ * An empty input stands for an absent one and is left to the frame to
108+ * interpret.
109+ */
110+ template <typename VALUETYPE >
111+ void validate_frame_blocks (const std::vector<VALUETYPE >& values,
112+ const int nframes,
113+ const std::size_t stride,
114+ const char * name) {
115+ const std::size_t expected = static_cast <std::size_t >(nframes) * stride;
116+ if (!values.empty () && values.size () != expected) {
117+ throw deepmd::deepmd_exception (
118+ std::string (name) + " holds " + std::to_string (values.size ()) +
119+ " values but " + std::to_string (nframes) + " frames require " +
120+ std::to_string (expected));
121+ }
122+ }
123+
124+ /* *
125+ * @brief Take the block one frame owns, or nothing when the input is absent.
126+ */
127+ template <typename T>
128+ std::vector<T> frame_block (const std::vector<T>& values,
129+ const int frame,
130+ const std::size_t stride) {
131+ if (values.empty ()) {
132+ return {};
133+ }
134+ const auto first = values.begin () + static_cast <std::ptrdiff_t >(frame) *
135+ static_cast <std::ptrdiff_t >(stride);
136+ return std::vector<T>(first, first + static_cast <std::ptrdiff_t >(stride));
137+ }
138+
84139/* *
85140 * @brief Build the frame-parameter input of the conditional graph tail.
86141 *
@@ -813,17 +868,69 @@ void NativeSpinPTExpt::compute(ENERGYVTYPE& ener,
813868 const std::vector<VALUETYPE >& fparam,
814869 const std::vector<VALUETYPE >& aparam,
815870 const bool atomic) {
871+ if (atype.empty ()) {
872+ throw deepmd::deepmd_exception (
873+ " standalone native-spin inference requires at least one atom" );
874+ }
875+ const std::size_t nloc = atype.size ();
876+ const int nframes = frame_count (coord, nloc);
877+ validate_frame_blocks (spin, nframes, nloc * 3 , " spin" );
878+ validate_frame_blocks (box, nframes, 9 , " box" );
879+ validate_frame_blocks (fparam, nframes, static_cast <std::size_t >(dfparam),
880+ " fparam" );
881+ validate_frame_blocks (aparam, nframes,
882+ nloc * static_cast <std::size_t >(daparam), " aparam" );
883+
884+ ener.clear ();
885+ force.clear ();
886+ force_mag.clear ();
887+ virial.clear ();
888+ atom_energy.clear ();
889+ atom_virial.clear ();
890+ for (int ff = 0 ; ff < nframes; ++ff) {
891+ ENERGYVTYPE frame_ener;
892+ std::vector<VALUETYPE > frame_force, frame_force_mag, frame_virial,
893+ frame_atom_energy, frame_atom_virial;
894+ compute_frame (
895+ frame_ener, frame_force, frame_force_mag, frame_virial,
896+ frame_atom_energy, frame_atom_virial, frame_block (coord, ff, nloc * 3 ),
897+ frame_block (spin, ff, nloc * 3 ), atype, frame_block (box, ff, 9 ),
898+ frame_block (fparam, ff, static_cast <std::size_t >(dfparam)),
899+ frame_block (aparam, ff, nloc * static_cast <std::size_t >(daparam)),
900+ atomic);
901+ ener.insert (ener.end (), frame_ener.begin (), frame_ener.end ());
902+ force.insert (force.end (), frame_force.begin (), frame_force.end ());
903+ force_mag.insert (force_mag.end (), frame_force_mag.begin (),
904+ frame_force_mag.end ());
905+ virial.insert (virial.end (), frame_virial.begin (), frame_virial.end ());
906+ atom_energy.insert (atom_energy.end (), frame_atom_energy.begin (),
907+ frame_atom_energy.end ());
908+ atom_virial.insert (atom_virial.end (), frame_atom_virial.begin (),
909+ frame_atom_virial.end ());
910+ }
911+ }
912+
913+ template <typename VALUETYPE , typename ENERGYVTYPE >
914+ void NativeSpinPTExpt::compute_frame (ENERGYVTYPE & ener,
915+ std::vector<VALUETYPE >& force,
916+ std::vector<VALUETYPE >& force_mag,
917+ std::vector<VALUETYPE >& virial,
918+ std::vector<VALUETYPE >& atom_energy,
919+ std::vector<VALUETYPE >& atom_virial,
920+ const std::vector<VALUETYPE >& coord,
921+ const std::vector<VALUETYPE >& spin,
922+ const std::vector<int >& atype,
923+ const std::vector<VALUETYPE >& box,
924+ const std::vector<VALUETYPE >& fparam,
925+ const std::vector<VALUETYPE >& aparam,
926+ const bool atomic) {
816927 const torch::Device device = gpu_enabled ? torch::Device (torch::kCUDA , gpu_id)
817928 : torch::Device (torch::kCPU );
818929 const auto f64_options = torch::TensorOptions ().dtype (torch::kFloat64 );
819930 const torch::ScalarType float_type =
820931 std::is_same<VALUETYPE , float >::value ? torch::kFloat32 : torch::kFloat64 ;
821932 const int nloc = static_cast <int >(atype.size ());
822933 const int nframes = 1 ;
823- if (atype.empty ()) {
824- throw deepmd::deepmd_exception (
825- " standalone native-spin inference requires at least one atom" );
826- }
827934 validate_cartesian_input (coord, atype.size (), " coord" );
828935 validate_cartesian_input (spin, atype.size (), " spin" );
829936
0 commit comments