Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions source/lib/src/gpu/prod_force.cu
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,14 @@ void prod_force_a_gpu(FPTYPE* force,
DPErrcheck(gpuGetLastError());
DPErrcheck(gpuDeviceSynchronize());
const int ndescrpt = nnei * 4;
DPErrcheck(gpuMemset(force, 0, sizeof(FPTYPE) * nframes * nall * 3));
const size_t output_size =
sizeof(FPTYPE) * static_cast<size_t>(nframes) * nall * 3;
if (output_size > 0) {
DPErrcheck(gpuMemset(force, 0, output_size));
}
if (nframes == 0 || nloc == 0 || nnei == 0) {
return;
}

force_deriv_wrt_center_atom<FPTYPE, TPB><<<nframes * nloc, TPB>>>(
force, net_deriv, in_deriv, ndescrpt, nloc, nall);
Expand Down Expand Up @@ -142,7 +149,14 @@ void prod_force_r_gpu(FPTYPE* force,
DPErrcheck(gpuGetLastError());
DPErrcheck(gpuDeviceSynchronize());
const int ndescrpt = nnei * 1;
DPErrcheck(gpuMemset(force, 0, sizeof(FPTYPE) * nframes * nall * 3));
const size_t output_size =
sizeof(FPTYPE) * static_cast<size_t>(nframes) * nall * 3;
if (output_size > 0) {
DPErrcheck(gpuMemset(force, 0, output_size));
}
if (nframes == 0 || nloc == 0 || nnei == 0) {
return;
}

force_deriv_wrt_center_atom<FPTYPE, TPB><<<nframes * nloc, TPB>>>(
force, net_deriv, in_deriv, ndescrpt, nloc, nall);
Expand Down
16 changes: 12 additions & 4 deletions source/lib/src/gpu/prod_force_grad.cu
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,12 @@ void prod_force_grad_a_gpu(FPTYPE* grad_net,
DPErrcheck(gpuGetLastError());
DPErrcheck(gpuDeviceSynchronize());
const int ndescrpt = nnei * 4;
DPErrcheck(
gpuMemset(grad_net, 0, sizeof(FPTYPE) * nframes * nloc * ndescrpt));
const size_t output_size =
sizeof(FPTYPE) * static_cast<size_t>(nframes) * nloc * ndescrpt;
if (output_size == 0) {
return;
}
DPErrcheck(gpuMemset(grad_net, 0, output_size));
const int nblock = (ndescrpt + TPB - 1) / TPB;
dim3 block_grid(nframes * nloc, nblock);
dim3 thread_grid(TPB, 1);
Expand Down Expand Up @@ -122,8 +126,12 @@ void prod_force_grad_r_gpu(FPTYPE* grad_net,
DPErrcheck(gpuGetLastError());
DPErrcheck(gpuDeviceSynchronize());
const int ndescrpt = nnei * 1;
DPErrcheck(
gpuMemset(grad_net, 0, sizeof(FPTYPE) * nframes * nloc * ndescrpt));
const size_t output_size =
sizeof(FPTYPE) * static_cast<size_t>(nframes) * nloc * ndescrpt;
if (output_size == 0) {
return;
}
DPErrcheck(gpuMemset(grad_net, 0, output_size));
const int nblock = (ndescrpt + TPB - 1) / TPB;
dim3 block_grid(nframes * nloc, nblock);
dim3 thread_grid(TPB, 1);
Expand Down
14 changes: 12 additions & 2 deletions source/lib/src/gpu/prod_virial.cu
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,12 @@ void prod_virial_a_gpu(FPTYPE* virial,
DPErrcheck(gpuGetLastError());
DPErrcheck(gpuDeviceSynchronize());
DPErrcheck(gpuMemset(virial, 0, sizeof(FPTYPE) * 9));
DPErrcheck(gpuMemset(atom_virial, 0, sizeof(FPTYPE) * 9 * nall));
if (nall > 0) {
DPErrcheck(gpuMemset(atom_virial, 0, sizeof(FPTYPE) * 9 * nall));
}
if (nloc == 0 || nnei == 0) {
return;
}

const int LEN = 16;
int nblock = (nnei + LEN - 1) / LEN;
Expand Down Expand Up @@ -146,7 +151,12 @@ void prod_virial_r_gpu(FPTYPE* virial,
DPErrcheck(gpuGetLastError());
DPErrcheck(gpuDeviceSynchronize());
DPErrcheck(gpuMemset(virial, 0, sizeof(FPTYPE) * 9));
DPErrcheck(gpuMemset(atom_virial, 0, sizeof(FPTYPE) * 9 * nall));
if (nall > 0) {
DPErrcheck(gpuMemset(atom_virial, 0, sizeof(FPTYPE) * 9 * nall));
}
if (nloc == 0 || nnei == 0) {
return;
}

const int LEN = 16;
int nblock = (nnei + LEN - 1) / LEN;
Expand Down
14 changes: 12 additions & 2 deletions source/lib/src/gpu/prod_virial_grad.cu
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,12 @@ void prod_virial_grad_a_gpu(FPTYPE* grad_net,
DPErrcheck(gpuGetLastError());
DPErrcheck(gpuDeviceSynchronize());
const int ndescrpt = nnei * 4;
DPErrcheck(gpuMemset(grad_net, 0, sizeof(FPTYPE) * nloc * ndescrpt));
const size_t output_size =
sizeof(FPTYPE) * static_cast<size_t>(nloc) * ndescrpt;
if (output_size == 0) {
return;
}
DPErrcheck(gpuMemset(grad_net, 0, output_size));
const int LEN = 128;
const int nblock = (nloc + LEN - 1) / LEN;
dim3 block_grid(nblock, nnei);
Expand All @@ -117,7 +122,12 @@ void prod_virial_grad_r_gpu(FPTYPE* grad_net,
DPErrcheck(gpuGetLastError());
DPErrcheck(gpuDeviceSynchronize());
const int ndescrpt = nnei;
DPErrcheck(gpuMemset(grad_net, 0, sizeof(FPTYPE) * nloc * ndescrpt));
const size_t output_size =
sizeof(FPTYPE) * static_cast<size_t>(nloc) * ndescrpt;
if (output_size == 0) {
return;
}
DPErrcheck(gpuMemset(grad_net, 0, output_size));
const int LEN = 128;
const int nblock = (nloc + LEN - 1) / LEN;
dim3 block_grid(nblock, nnei);
Expand Down
43 changes: 43 additions & 0 deletions source/op/tf/custom_op.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
#include <iostream>
#include <limits>
#include <string>
#include <utility>
#include <vector>
Expand Down Expand Up @@ -51,6 +52,48 @@ inline Status InvalidArgument(Args&&... args) {
return tensorflow::errors::InvalidArgument(std::forward<Args>(args)...);
#endif
}

/**
* @brief Derive a dense tensor's per-atom width without truncating division.
*
* Several low-level TensorFlow ops flatten atom and feature dimensions into a
* single axis. Validate the flattened width before dividing by `nloc`; raw
Comment thread
njzjz marked this conversation as resolved.
* CPU/GPU kernels cannot safely consume a leftover partial atom row.
*
* @param per_atom_width Receives the validated feature width for one atom.
* @param shape Rank-two tensor shape whose second dimension is flattened.
* @param nloc Number of local atoms encoded in the flattened dimension.
* @param tensor_name Human-readable input name used in validation errors.
* @return An OK status, or InvalidArgument when the width is incompatible.
*/
inline Status GetPerAtomWidth(int* per_atom_width,
Comment thread
njzjz-bot marked this conversation as resolved.
const TensorShape& shape,
const int nloc,
const char* tensor_name) {
const int64_t flattened_width = shape.dim_size(1);
if (nloc < 0) {
return InvalidArgument("number of local atoms should be non-negative");
}
if (nloc == 0) {
if (flattened_width != 0) {
return InvalidArgument(tensor_name,
" width should be zero when nloc is zero");
}
*per_atom_width = 0;
return Status();
Comment thread
njzjz marked this conversation as resolved.
}
if (flattened_width % nloc != 0) {
return InvalidArgument(tensor_name, " width ", flattened_width,
" should be divisible by nloc ", nloc);
}
const int64_t width = flattened_width / nloc;
if (width > std::numeric_limits<int>::max()) {
return InvalidArgument(tensor_name,
" width per atom exceeds the supported int range");
}
*per_atom_width = static_cast<int>(width);
return Status();
}
} // namespace tf_compat
} // namespace deepmd

Expand Down
22 changes: 18 additions & 4 deletions source/op/tf/prod_force_grad_multi_device.cc
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,12 @@ class ProdForceSeAGradOp : public OpKernel {

int nframes = net_deriv_tensor.shape().dim_size(0);
int nloc = natoms(0);
int ndescrpt = nloc > 0 ? net_deriv_tensor.shape().dim_size(1) / nloc : 0;
int nnei = nloc > 0 ? nlist_tensor.shape().dim_size(1) / nloc : 0;
int ndescrpt;
int nnei;
OP_REQUIRES_OK(context, deepmd::tf_compat::GetPerAtomWidth(
&ndescrpt, net_deriv_shape, nloc, "net deriv"));
OP_REQUIRES_OK(context, deepmd::tf_compat::GetPerAtomWidth(
&nnei, nlist_shape, nloc, "nlist"));

// check the sizes
OP_REQUIRES(
Expand All @@ -97,6 +101,9 @@ class ProdForceSeAGradOp : public OpKernel {
OP_REQUIRES(
context, (nnei == n_a_sel + n_r_sel),
deepmd::tf_compat::InvalidArgument("number of neighbors should match"));
OP_REQUIRES(context, (static_cast<int64_t>(nnei) * 4 == ndescrpt),
deepmd::tf_compat::InvalidArgument(
"descriptor width should be four times neighbor width"));

// Create an output tensor
TensorShape grad_net_shape;
Expand Down Expand Up @@ -194,8 +201,12 @@ class ProdForceSeRGradOp : public OpKernel {

int nframes = net_deriv_tensor.shape().dim_size(0);
int nloc = natoms(0);
int ndescrpt = nloc > 0 ? net_deriv_tensor.shape().dim_size(1) / nloc : 0;
int nnei = nloc > 0 ? nlist_tensor.shape().dim_size(1) / nloc : 0;
int ndescrpt;
int nnei;
OP_REQUIRES_OK(context, deepmd::tf_compat::GetPerAtomWidth(
&ndescrpt, net_deriv_shape, nloc, "net deriv"));
OP_REQUIRES_OK(context, deepmd::tf_compat::GetPerAtomWidth(
&nnei, nlist_shape, nloc, "nlist"));

// check the sizes
OP_REQUIRES(
Expand All @@ -215,6 +226,9 @@ class ProdForceSeRGradOp : public OpKernel {
(int_64(nloc) * ndescrpt * 3 == in_deriv_shape.dim_size(1)),
deepmd::tf_compat::InvalidArgument(
"number of descriptors should match"));
OP_REQUIRES(context, (nnei == ndescrpt),
deepmd::tf_compat::InvalidArgument(
"descriptor width should equal neighbor width"));

// Create an output tensor
TensorShape grad_net_shape;
Expand Down
30 changes: 26 additions & 4 deletions source/op/tf/prod_force_multi_device.cc
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,16 @@ class ProdForceSeAOp : public OpKernel {
int nloc = natoms[0];
int nall = natoms[1];
int nframes = net_deriv_tensor.shape().dim_size(0);
int ndescrpt = nloc > 0 ? net_deriv_tensor.shape().dim_size(1) / nloc : 0;
int nnei = nloc > 0 ? nlist_tensor.shape().dim_size(1) / nloc : 0;
int ndescrpt;
int nnei;
OP_REQUIRES(context, (nall >= nloc),
deepmd::tf_compat::InvalidArgument(
"number of all atoms should be at least nloc"));
OP_REQUIRES_OK(context,
deepmd::tf_compat::GetPerAtomWidth(
&ndescrpt, net_deriv_tensor.shape(), nloc, "net deriv"));
Comment thread
njzjz marked this conversation as resolved.
OP_REQUIRES_OK(context, deepmd::tf_compat::GetPerAtomWidth(
&nnei, nlist_tensor.shape(), nloc, "nlist"));
// check the sizes
OP_REQUIRES(
context, (nframes == in_deriv_tensor.shape().dim_size(0)),
Expand All @@ -106,6 +114,9 @@ class ProdForceSeAOp : public OpKernel {
(int_64(nloc) * ndescrpt * 3 == in_deriv_tensor.shape().dim_size(1)),
deepmd::tf_compat::InvalidArgument(
"number of descriptors should match"));
OP_REQUIRES(context, (static_cast<int64_t>(nnei) * 4 == ndescrpt),
deepmd::tf_compat::InvalidArgument(
"descriptor width should be four times neighbor width"));
// Create an output tensor
TensorShape force_shape;
force_shape.AddDim(nframes);
Expand Down Expand Up @@ -199,8 +210,16 @@ class ProdForceSeROp : public OpKernel {
int nloc = natoms[0];
int nall = natoms[1];
int nframes = net_deriv_tensor.shape().dim_size(0);
int ndescrpt = nloc > 0 ? net_deriv_tensor.shape().dim_size(1) / nloc : 0;
int nnei = nloc > 0 ? nlist_tensor.shape().dim_size(1) / nloc : 0;
int ndescrpt;
int nnei;
OP_REQUIRES(context, (nall >= nloc),
deepmd::tf_compat::InvalidArgument(
"number of all atoms should be at least nloc"));
OP_REQUIRES_OK(context,
deepmd::tf_compat::GetPerAtomWidth(
&ndescrpt, net_deriv_tensor.shape(), nloc, "net deriv"));
OP_REQUIRES_OK(context, deepmd::tf_compat::GetPerAtomWidth(
&nnei, nlist_tensor.shape(), nloc, "nlist"));
// check the sizes
OP_REQUIRES(
context, (nframes == in_deriv_tensor.shape().dim_size(0)),
Expand All @@ -213,6 +232,9 @@ class ProdForceSeROp : public OpKernel {
in_deriv_tensor.shape().dim_size(1)),
deepmd::tf_compat::InvalidArgument(
"number of descriptors should match"));
OP_REQUIRES(context, (nnei == ndescrpt),
deepmd::tf_compat::InvalidArgument(
"descriptor width should equal neighbor width"));
// Create an output tensor
TensorShape force_shape;
force_shape.AddDim(nframes);
Expand Down
22 changes: 18 additions & 4 deletions source/op/tf/prod_virial_grad_multi_device.cc
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,12 @@ class ProdVirialSeAGradOp : public OpKernel {

int nframes = net_deriv_tensor.shape().dim_size(0);
int nloc = natoms(0);
int ndescrpt = nloc > 0 ? net_deriv_tensor.shape().dim_size(1) / nloc : 0;
int nnei = nloc > 0 ? nlist_tensor.shape().dim_size(1) / nloc : 0;
int ndescrpt;
int nnei;
OP_REQUIRES_OK(context, deepmd::tf_compat::GetPerAtomWidth(
&ndescrpt, net_deriv_shape, nloc, "net deriv"));
OP_REQUIRES_OK(context, deepmd::tf_compat::GetPerAtomWidth(
&nnei, nlist_shape, nloc, "nlist"));

// check the sizes
OP_REQUIRES(
Expand Down Expand Up @@ -109,6 +113,9 @@ class ProdVirialSeAGradOp : public OpKernel {
OP_REQUIRES(
context, (nnei == n_a_sel + n_r_sel),
deepmd::tf_compat::InvalidArgument("number of neighbors should match"));
OP_REQUIRES(context, (static_cast<int64_t>(nnei) * 4 == ndescrpt),
deepmd::tf_compat::InvalidArgument(
"descriptor width should be four times neighbor width"));

// Create an output tensor
TensorShape grad_net_shape;
Expand Down Expand Up @@ -223,8 +230,12 @@ class ProdVirialSeRGradOp : public OpKernel {

int nframes = net_deriv_tensor.shape().dim_size(0);
int nloc = natoms(0);
int ndescrpt = nloc > 0 ? net_deriv_tensor.shape().dim_size(1) / nloc : 0;
int nnei = nloc > 0 ? nlist_tensor.shape().dim_size(1) / nloc : 0;
int ndescrpt;
int nnei;
OP_REQUIRES_OK(context, deepmd::tf_compat::GetPerAtomWidth(
&ndescrpt, net_deriv_shape, nloc, "net deriv"));
OP_REQUIRES_OK(context, deepmd::tf_compat::GetPerAtomWidth(
&nnei, nlist_shape, nloc, "nlist"));

// check the sizes
OP_REQUIRES(
Expand All @@ -250,6 +261,9 @@ class ProdVirialSeRGradOp : public OpKernel {
OP_REQUIRES(
context, (int_64(nloc) * nnei * 3 == rij_shape.dim_size(1)),
deepmd::tf_compat::InvalidArgument("dim of rij should be nnei * 3"));
OP_REQUIRES(context, (nnei == ndescrpt),
deepmd::tf_compat::InvalidArgument(
"descriptor width should equal neighbor width"));

// Create an output tensor
TensorShape grad_net_shape;
Expand Down
30 changes: 26 additions & 4 deletions source/op/tf/prod_virial_multi_device.cc
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,17 @@ class ProdVirialSeAOp : public OpKernel {
const int* natoms = natoms_tensor.flat<int>().data();
int nloc = natoms[0];
int nall = natoms[1];
int nnei = nloc > 0 ? nlist_tensor.shape().dim_size(1) / nloc : 0;
int nframes = net_deriv_tensor.shape().dim_size(0);
int ndescrpt = nloc > 0 ? net_deriv_tensor.shape().dim_size(1) / nloc : 0;
int ndescrpt;
int nnei;
OP_REQUIRES(context, (nall >= nloc),
deepmd::tf_compat::InvalidArgument(
"number of all atoms should be at least nloc"));
OP_REQUIRES_OK(context,
deepmd::tf_compat::GetPerAtomWidth(
&ndescrpt, net_deriv_tensor.shape(), nloc, "net deriv"));
OP_REQUIRES_OK(context, deepmd::tf_compat::GetPerAtomWidth(
&nnei, nlist_tensor.shape(), nloc, "nlist"));
// check the sizes
OP_REQUIRES(
context, (nframes == in_deriv_tensor.shape().dim_size(0)),
Expand All @@ -94,6 +102,9 @@ class ProdVirialSeAOp : public OpKernel {
OP_REQUIRES(
context, (int_64(nloc) * nnei * 3 == rij_tensor.shape().dim_size(1)),
deepmd::tf_compat::InvalidArgument("dim of rij should be nnei * 3"));
OP_REQUIRES(context, (static_cast<int64_t>(nnei) * 4 == ndescrpt),
deepmd::tf_compat::InvalidArgument(
"descriptor width should be four times neighbor width"));
// Create an output tensor
TensorShape virial_shape;
virial_shape.AddDim(nframes);
Expand Down Expand Up @@ -179,9 +190,17 @@ class ProdVirialSeROp : public OpKernel {
const int* natoms = natoms_tensor.flat<int>().data();
int nloc = natoms[0];
int nall = natoms[1];
int nnei = nloc > 0 ? nlist_tensor.shape().dim_size(1) / nloc : 0;
int nframes = net_deriv_tensor.shape().dim_size(0);
int ndescrpt = nloc > 0 ? net_deriv_tensor.shape().dim_size(1) / nloc : 0;
int ndescrpt;
int nnei;
OP_REQUIRES(context, (nall >= nloc),
deepmd::tf_compat::InvalidArgument(
"number of all atoms should be at least nloc"));
OP_REQUIRES_OK(context,
deepmd::tf_compat::GetPerAtomWidth(
&ndescrpt, net_deriv_tensor.shape(), nloc, "net deriv"));
OP_REQUIRES_OK(context, deepmd::tf_compat::GetPerAtomWidth(
&nnei, nlist_tensor.shape(), nloc, "nlist"));
// check the sizes
OP_REQUIRES(
context, (nframes == in_deriv_tensor.shape().dim_size(0)),
Expand All @@ -200,6 +219,9 @@ class ProdVirialSeROp : public OpKernel {
OP_REQUIRES(
context, (int_64(nloc) * nnei * 3 == rij_tensor.shape().dim_size(1)),
deepmd::tf_compat::InvalidArgument("dim of rij should be nnei * 3"));
OP_REQUIRES(context, (nnei == ndescrpt),
deepmd::tf_compat::InvalidArgument(
"descriptor width should equal neighbor width"));
// Create an output tensor
TensorShape virial_shape;
virial_shape.AddDim(nframes);
Expand Down
Loading
Loading