Files
rdkit/Code/Geometry/UniformRealValueGrid3D.cpp
Greg Landrum c90cee9b77 Add Molecular Interaction Fields (#7993)
* Add RealValueVect.

* Add UniformRealValueGrid3D

* Add Molecular Interaction Fields (MIFs)

* line endings

* cherry-pick f1bc94a4c8

* format

* Adapt tests for python3.

* Adapt RealValueVector pickling for python3.

* Speed-up of MIF calculations.

* Bugfix in MIFDescriptors.cpp.

* all tests pass

* clean up some memory leaks

* update copyrights

* rename

* rename the library

* complete the rename

* lost file

* another forgotten file

* cleanup

* clang-tidy

* clang-tidy

* windows DLL builds work

* python wrapper and tests cleanup

* convert to catch2 testing

* switch RealValueVect to use std::vector

* remove obsolete friend

* - Replace explicit loops with stdlib implicit equivalents
- Replace explicit types with auto where possible
- Avoid unnecessary copy operations where possible
- Replace raw pointers with exception-safe unique_ptr
- Replace C-style #define with constexpr
- Replace C-style casts with C++ casts
- Replace C-style arrays with std::vector
- Avoid code duplication with templated operators
- Replace VdWaals class taking multiple atom type definitions and force-field name as string parameter with force-field-specific classes deriving from an abstract VdWaals class
- Replace x,y,z doubles with Point3D class where possible
- Removed unused (and untested) DistanceToClosestAtom class
- Renamed some variables and functions for better clarity
- Converted tabs to spaces
- Made the mol parameter in cube read/write functions optional for convenience
- Made the Python wrappers more pythonic (e.g., avoid C++-style passing objects as parameters which are modified in place)
- Implemented alternative Python class constructors using boost::python::make_constructor rather than with external non-class functions
- The Python wrappers taking a sequence of Point3D now take a sequence of sequences, such that the output of Conformer.GetPositions() can be passed
- Made the Python wrapper sequence parsing more robust
- Removed duplicated code from Python wrappers

* - avoid an unnecessary copy

* progress

* works

* more cleanup

* all tests pass

* changes in response to review

---------

Co-authored-by: dfhahn <dfhahn@users.noreply.github.com>
Co-authored-by: ptosco <paolo.tosco@novartis.com>
2024-12-14 17:08:43 +01:00

320 lines
9.2 KiB
C++

//
// Copyright (c) 2014-2024, Novartis Institutes for BioMedical Research and
// other RDKit contributors
//
// @@ All Rights Reserved @@
// This file is part of the RDKit.
// The contents are covered by the terms of the BSD license
// which is included in the file license.txt, found at the root
// of the RDKit source tree.
//
#include "UniformRealValueGrid3D.h"
#include <DataStructs/RealValueVect.h>
#include <RDGeneral/StreamOps.h>
#include <RDGeneral/Exceptions.h>
#include <Geometry/point.h>
#include <fstream>
#include <iomanip>
using namespace RDKit;
namespace {
constexpr double OFFSET_TOL = 1.e-4;
constexpr double SPACING_TOL = 1.e-4;
constexpr const char *INCOMPATIBLE_GRIDS = "incompatible grids";
} // end anonymous namespace
namespace RDGeom {
std::int32_t ci_RealValueGrid3DPICKLE_VERSION = 0x1;
UniformRealValueGrid3D::UniformRealValueGrid3D(
const UniformRealValueGrid3D &other) {
UniformRealValueGrid3D::initGrid(
other.d_numX * other.d_spacing, other.d_numY * other.d_spacing,
other.d_numZ * other.d_spacing, other.d_spacing, other.d_offSet,
&other.d_storage);
}
void UniformRealValueGrid3D::initGrid(double dimX, double dimY, double dimZ,
double spacing,
const RDGeom::Point3D &offSet,
const RDKit::RealValueVect *data) {
PRECONDITION(dimX > 0.0, "Invalid x-dimension for grid");
PRECONDITION(dimY > 0.0, "Invalid y-dimension for grid");
PRECONDITION(dimZ > 0.0, "Invalid z-dimension for grid");
PRECONDITION(spacing > 0.0, "Invalid spacing for grid");
d_numX = static_cast<unsigned int>(floor(dimX / spacing + 0.5));
d_numY = static_cast<unsigned int>(floor(dimY / spacing + 0.5));
d_numZ = static_cast<unsigned int>(floor(dimZ / spacing + 0.5));
// PRECONDITION((!data)||data->getValueType()==double,"grid data type
// mismatch");
PRECONDITION(!data || data->size() == d_numX * d_numY * d_numZ,
"grid data size mismatch");
d_spacing = spacing;
d_offSet = offSet;
if (!data) {
d_storage.setLength(d_numX * d_numY * d_numZ);
d_storage.setToVal(0.0);
} else {
d_storage = *data;
}
}
UniformRealValueGrid3D::UniformRealValueGrid3D(const std::string &pkl) {
initFromText(pkl.c_str(), pkl.size());
}
UniformRealValueGrid3D::UniformRealValueGrid3D(const char *pkl,
unsigned int len) {
initFromText(pkl, len);
}
int UniformRealValueGrid3D::getGridIndex(unsigned int xi, unsigned int yi,
unsigned int zi) const {
if (xi >= d_numX) {
return -1;
}
if (yi >= d_numY) {
return -1;
}
if (zi >= d_numZ) {
return -1;
}
return (zi * d_numX * d_numY + yi * d_numX + xi);
}
void UniformRealValueGrid3D::getGridIndices(unsigned int idx, unsigned int &xi,
unsigned int &yi,
unsigned int &zi) const {
if (idx >= d_numX * d_numY * d_numZ) {
throw IndexErrorException(idx);
}
xi = idx % d_numX;
yi = (idx % (d_numX * d_numY)) / d_numX;
zi = idx / (d_numX * d_numY);
}
int UniformRealValueGrid3D::getGridPointIndex(
const RDGeom::Point3D &point) const {
auto tPt = point;
tPt -= d_offSet; // d_origin;
tPt /= d_spacing;
constexpr double move = 0.5;
auto xi = static_cast<int>(floor(tPt.x + move));
auto yi = static_cast<int>(floor(tPt.y + move));
auto zi = static_cast<int>(floor(tPt.z + move));
if ((xi < 0) || (xi >= static_cast<int>(d_numX))) {
return -1;
}
if ((yi < 0) || (yi >= static_cast<int>(d_numY))) {
return -1;
}
if ((zi < 0) || (zi >= static_cast<int>(d_numZ))) {
return -1;
}
return (zi * d_numX * d_numY + yi * d_numX + xi);
}
double UniformRealValueGrid3D::getVal(const RDGeom::Point3D &point) const {
auto id = getGridPointIndex(point);
if (id < 0) {
return -1;
}
return d_storage.getVal(static_cast<unsigned int>(id));
}
double UniformRealValueGrid3D::getVal(unsigned int pointId) const {
return d_storage.getVal(pointId);
}
void UniformRealValueGrid3D::setVal(const RDGeom::Point3D &point, double val) {
auto id = getGridPointIndex(point);
if (id < 0) {
return;
}
d_storage.setVal(static_cast<unsigned int>(id), val);
}
RDGeom::Point3D UniformRealValueGrid3D::getGridPointLoc(
unsigned int pointId) const {
if (pointId >= d_numX * d_numY * d_numZ) {
throw IndexErrorException(pointId);
}
RDGeom::Point3D res((pointId % d_numX) * d_spacing,
((pointId % (d_numX * d_numY)) / d_numX) * d_spacing,
(pointId / (d_numX * d_numY)) * d_spacing);
res += d_offSet; // d_origin;
return res;
}
void UniformRealValueGrid3D::setVal(unsigned int pointId, double val) {
d_storage.setVal(pointId, val);
}
bool UniformRealValueGrid3D::compareParams(
const UniformRealValueGrid3D &other) const {
if (d_numX != other.getNumX()) {
return false;
}
if (d_numY != other.getNumY()) {
return false;
}
if (d_numZ != other.getNumZ()) {
return false;
}
if (fabs(d_spacing - other.getSpacing()) > SPACING_TOL) {
return false;
}
auto dOffset = d_offSet;
dOffset -= other.getOffset();
return dOffset.lengthSq() <= OFFSET_TOL;
}
bool UniformRealValueGrid3D::compareVectors(
const UniformRealValueGrid3D &other) const {
return d_storage.compareVectors(other.d_storage);
}
bool UniformRealValueGrid3D::compareGrids(
const UniformRealValueGrid3D &other) const {
if (!compareParams(other)) {
return false;
}
return d_storage.compareVectors(other.d_storage);
}
UniformRealValueGrid3D &UniformRealValueGrid3D::operator=(
const UniformRealValueGrid3D &other) {
if (this == &other) {
return *this;
}
d_numX = other.d_numX;
d_numY = other.d_numY;
d_numZ = other.d_numZ;
d_spacing = other.d_spacing;
d_offSet = other.d_offSet;
d_storage = other.d_storage;
return *this;
}
UniformRealValueGrid3D &UniformRealValueGrid3D::operator|=(
const UniformRealValueGrid3D &other) {
PRECONDITION(compareParams(other), INCOMPATIBLE_GRIDS);
d_storage |= other.d_storage;
return *this;
}
UniformRealValueGrid3D &UniformRealValueGrid3D::operator&=(
const UniformRealValueGrid3D &other) {
PRECONDITION(compareParams(other), INCOMPATIBLE_GRIDS);
d_storage &= other.d_storage;
return *this;
}
UniformRealValueGrid3D &UniformRealValueGrid3D::operator+=(
const UniformRealValueGrid3D &other) {
PRECONDITION(compareParams(other), INCOMPATIBLE_GRIDS);
d_storage += other.d_storage;
return *this;
}
UniformRealValueGrid3D &UniformRealValueGrid3D::operator-=(
const UniformRealValueGrid3D &other) {
PRECONDITION(compareParams(other), INCOMPATIBLE_GRIDS);
d_storage -= other.d_storage;
return *this;
}
std::string UniformRealValueGrid3D::toString() const {
std::stringstream ss(std::ios_base::binary | std::ios_base::out |
std::ios_base::in);
std::int32_t tVers = ci_RealValueGrid3DPICKLE_VERSION * -1;
streamWrite(ss, tVers);
std::uint32_t tInt;
tInt = d_numX;
streamWrite(ss, tInt);
tInt = d_numY;
streamWrite(ss, tInt);
tInt = d_numZ;
streamWrite(ss, tInt);
streamWrite(ss, d_spacing);
streamWrite(ss, d_offSet.x);
streamWrite(ss, d_offSet.y);
streamWrite(ss, d_offSet.z);
std::string storePkl = d_storage.toString();
std::uint32_t pklSz = storePkl.size();
streamWrite(ss, pklSz);
ss.write(storePkl.c_str(), pklSz * sizeof(char));
return ss.str();
}
void UniformRealValueGrid3D::initFromText(const char *pkl,
const unsigned int length) {
std::stringstream ss(std::ios_base::binary | std::ios_base::in |
std::ios_base::out);
ss.write(pkl, length);
std::int32_t tVers;
streamRead(ss, tVers);
tVers = -tVers;
if (tVers != 0x1) {
throw ValueErrorException("bad version in UniformRealValueGrid3D pickle");
}
std::uint32_t tInt;
streamRead(ss, tInt);
d_numX = tInt;
streamRead(ss, tInt);
d_numY = tInt;
streamRead(ss, tInt);
d_numZ = tInt;
streamRead(ss, d_spacing);
double oX, oY, oZ;
streamRead(ss, oX);
streamRead(ss, oY);
streamRead(ss, oZ);
d_offSet = RDGeom::Point3D(oX, oY, oZ);
std::uint32_t pklSz;
streamRead(ss, pklSz);
std::vector<char> buff(pklSz);
ss.read(buff.data(), pklSz * sizeof(char));
d_storage = RDKit::RealValueVect(buff.data(), pklSz);
}
UniformRealValueGrid3D operator&(const UniformRealValueGrid3D &grd1,
const UniformRealValueGrid3D &grd2) {
UniformRealValueGrid3D ans(grd1);
ans &= grd2;
return ans;
};
UniformRealValueGrid3D operator|(const UniformRealValueGrid3D &grd1,
const UniformRealValueGrid3D &grd2) {
UniformRealValueGrid3D ans(grd1);
ans |= grd2;
return ans;
};
UniformRealValueGrid3D operator+(const UniformRealValueGrid3D &grd1,
const UniformRealValueGrid3D &grd2) {
UniformRealValueGrid3D ans(grd1);
ans += grd2;
return ans;
};
UniformRealValueGrid3D operator-(const UniformRealValueGrid3D &grd1,
const UniformRealValueGrid3D &grd2) {
UniformRealValueGrid3D ans(grd1);
ans -= grd2;
return ans;
};
} // namespace RDGeom