1 // $Id$
2 //
3 // Copyright (C) 2005-2008 Greg Landrum and Rational Discovery LLC
4 //
5 // @@ All Rights Reserved @@
6 // This file is part of the RDKit.
7 // The contents are covered by the terms of the BSD license
8 // which is included in the file license.txt, found at the root
9 // of the RDKit source tree.
10 //
11 #include "UniformGrid3D.h"
12 #include <DataStructs/DiscreteValueVect.h>
13 #include <RDGeneral/StreamOps.h>
14 #include <RDGeneral/Exceptions.h>
15 #include "point.h"
16 #include <fstream>
17 #include <cstdint>
18
19 constexpr double OFFSET_TOL = 1.e-8;
20 constexpr double SPACING_TOL = 1.e-8;
21
22 using namespace RDKit;
23
24 namespace RDGeom {
25 unsigned int ci_GRIDPICKLE_VERSION = 0x1;
26
UniformGrid3D(const UniformGrid3D & other)27 UniformGrid3D::UniformGrid3D(const UniformGrid3D &other) : Grid3D(other) {
28 PRECONDITION(other.dp_storage, "cannot copy an uninitialized grid");
29 auto *data = new RDKit::DiscreteValueVect(*other.dp_storage);
30 initGrid(other.d_numX * other.d_spacing, other.d_numY * other.d_spacing,
31 other.d_numZ * other.d_spacing, other.d_spacing,
32 other.dp_storage->getValueType(), other.d_offSet, data);
33 }
34
operator =(const UniformGrid3D & other)35 UniformGrid3D &UniformGrid3D::operator=(const UniformGrid3D &other) {
36 if (&other == this) {
37 return *this;
38 }
39 PRECONDITION(other.dp_storage, "cannot copy an uninitialized grid");
40 delete dp_storage;
41 auto *data = new RDKit::DiscreteValueVect(*other.dp_storage);
42 initGrid(other.d_numX * other.d_spacing, other.d_numY * other.d_spacing,
43 other.d_numZ * other.d_spacing, other.d_spacing,
44 other.dp_storage->getValueType(), other.d_offSet, data);
45 return *this;
46 }
47
initGrid(double dimX,double dimY,double dimZ,double spacing,RDKit::DiscreteValueVect::DiscreteValueType valType,const RDGeom::Point3D & offSet,RDKit::DiscreteValueVect * data)48 void UniformGrid3D::initGrid(
49 double dimX, double dimY, double dimZ, double spacing,
50 RDKit::DiscreteValueVect::DiscreteValueType valType,
51 const RDGeom::Point3D &offSet, RDKit::DiscreteValueVect *data) {
52 PRECONDITION(dimX > 0.0, "Invalid x-dimension for grid");
53 PRECONDITION(dimY > 0.0, "Invalid y-dimension for grid");
54 PRECONDITION(dimZ > 0.0, "Invalid z-dimension for grid");
55 PRECONDITION(spacing > 0.0, "Invalid spacing for grid");
56 d_numX = static_cast<unsigned int>(floor(dimX / spacing + 0.5));
57 d_numY = static_cast<unsigned int>(floor(dimY / spacing + 0.5));
58 d_numZ = static_cast<unsigned int>(floor(dimZ / spacing + 0.5));
59 PRECONDITION((!data) || data->getValueType() == valType,
60 "grid data type mismatch");
61 PRECONDITION((!data) || data->getLength() == d_numX * d_numY * d_numZ,
62 "grid data size mismatch");
63
64 d_spacing = spacing;
65 d_offSet = offSet;
66 if (!data) {
67 dp_storage =
68 new RDKit::DiscreteValueVect(valType, d_numX * d_numY * d_numZ);
69 } else {
70 dp_storage = data;
71 }
72 }
73
UniformGrid3D(const std::string & pkl)74 UniformGrid3D::UniformGrid3D(const std::string &pkl) {
75 dp_storage = nullptr;
76 this->initFromText(pkl.c_str(), pkl.size());
77 }
UniformGrid3D(const char * pkl,const unsigned int len)78 UniformGrid3D::UniformGrid3D(const char *pkl, const unsigned int len) {
79 dp_storage = nullptr;
80 this->initFromText(pkl, len);
81 }
82
~UniformGrid3D()83 UniformGrid3D::~UniformGrid3D() {
84 delete dp_storage;
85 dp_storage = nullptr;
86 }
87
getGridIndex(unsigned int xi,unsigned int yi,unsigned int zi) const88 int UniformGrid3D::getGridIndex(unsigned int xi, unsigned int yi,
89 unsigned int zi) const {
90 if (xi >= d_numX) {
91 return -1;
92 }
93 if (yi >= d_numY) {
94 return -1;
95 }
96 if (zi >= d_numZ) {
97 return -1;
98 }
99 return (zi * d_numX * d_numY + yi * d_numX + xi);
100 }
101
getGridIndices(unsigned int idx,unsigned int & xi,unsigned int & yi,unsigned int & zi) const102 void UniformGrid3D::getGridIndices(unsigned int idx, unsigned int &xi,
103 unsigned int &yi, unsigned int &zi) const {
104 if (idx >= d_numX * d_numY * d_numZ) {
105 throw IndexErrorException(idx);
106 }
107 xi = idx % d_numX;
108 yi = (idx % (d_numX * d_numY)) / d_numX;
109 zi = idx / (d_numX * d_numY);
110 }
111
getGridPointIndex(const Point3D & point) const112 int UniformGrid3D::getGridPointIndex(const Point3D &point) const {
113 Point3D tPt(point);
114 tPt -= d_offSet; // d_origin;
115 tPt /= d_spacing;
116 int xi, yi, zi;
117 double move = 0.5;
118 xi = static_cast<int>(floor(tPt.x + move));
119 yi = static_cast<int>(floor(tPt.y + move));
120 zi = static_cast<int>(floor(tPt.z + move));
121
122 if ((xi < 0) || (xi >= static_cast<int>(d_numX))) {
123 return -1;
124 }
125 if ((yi < 0) || (yi >= static_cast<int>(d_numY))) {
126 return -1;
127 }
128 if ((zi < 0) || (zi >= static_cast<int>(d_numZ))) {
129 return -1;
130 }
131
132 return (zi * d_numX * d_numY + yi * d_numX + xi);
133 }
134
getVal(const Point3D & point) const135 int UniformGrid3D::getVal(const Point3D &point) const {
136 int id = getGridPointIndex(point);
137 if (id < 0) {
138 return -1;
139 }
140 return dp_storage->getVal(static_cast<unsigned int>(id));
141 }
142
getVal(unsigned int pointId) const143 unsigned int UniformGrid3D::getVal(unsigned int pointId) const {
144 return dp_storage->getVal(pointId);
145 }
146
setVal(const Point3D & point,unsigned int val)147 void UniformGrid3D::setVal(const Point3D &point, unsigned int val) {
148 int id = getGridPointIndex(point);
149 if (id < 0) {
150 return;
151 }
152 dp_storage->setVal(static_cast<unsigned int>(id), val);
153 }
154
getGridPointLoc(unsigned int pointId) const155 Point3D UniformGrid3D::getGridPointLoc(unsigned int pointId) const {
156 if (pointId >= d_numX * d_numY * d_numZ) {
157 throw IndexErrorException(pointId);
158 }
159 Point3D res;
160 res.x = (pointId % d_numX) * d_spacing;
161 // the rounding here is intentional, we want the coordinates of a grid point
162 res.y =
163 static_cast<double>((pointId % (d_numX * d_numY)) / d_numX) * d_spacing;
164 res.z = static_cast<double>(pointId / (d_numX * d_numY)) * d_spacing;
165 res += d_offSet; // d_origin;
166 return res;
167 }
168
setVal(unsigned int pointId,unsigned int val)169 void UniformGrid3D::setVal(unsigned int pointId, unsigned int val) {
170 dp_storage->setVal(pointId, val);
171 }
172
compareParams(const UniformGrid3D & other) const173 bool UniformGrid3D::compareParams(const UniformGrid3D &other) const {
174 if (d_numX != other.getNumX()) {
175 return false;
176 }
177 if (d_numY != other.getNumY()) {
178 return false;
179 }
180 if (d_numZ != other.getNumZ()) {
181 return false;
182 }
183 if (fabs(d_spacing - other.getSpacing()) > SPACING_TOL) {
184 return false;
185 }
186 Point3D dOffset = d_offSet;
187 dOffset -= other.getOffset();
188 if (dOffset.lengthSq() > OFFSET_TOL) {
189 return false;
190 }
191 return true;
192 }
193
setSphereOccupancy(const Point3D & center,double radius,double stepSize,int maxNumLayers,bool ignoreOutOfBound)194 void UniformGrid3D::setSphereOccupancy(const Point3D ¢er, double radius,
195 double stepSize, int maxNumLayers,
196 bool ignoreOutOfBound) {
197 int ptIndex = this->getGridPointIndex(center);
198 if (ptIndex == -1) {
199 if (ignoreOutOfBound) {
200 return;
201 } else {
202 throw GridException("Center outside the grdi boundary");
203 }
204 }
205 Point3D gPt(center); // point on the grid
206 gPt -= d_offSet;
207
208 gPt /= d_spacing;
209
210 // unsigned int z = ptIndex/(d_numX*d_numY);
211 // unsigned int y = (ptIndex%(d_numX*d_numY))/d_numX;
212 // unsigned int x = ptIndex%d_numX;
213
214 unsigned int bPerVal = dp_storage->getNumBitsPerVal();
215 unsigned int maxVal = (1 << bPerVal) - 1;
216 unsigned int nLayers = maxVal;
217 unsigned int valStep = 1;
218 if ((maxNumLayers > 0) && (maxNumLayers <= static_cast<int>(nLayers))) {
219 nLayers = maxNumLayers;
220 valStep = (maxVal + 1) / nLayers;
221 }
222 double bgRad = radius / d_spacing; // base radius in grid coords
223 double gStepSize = stepSize / d_spacing; // step size in grid coords
224 double gRadius =
225 bgRad + nLayers * gStepSize; // largest radius in grid coords
226 double gRad2 = gRadius * gRadius;
227 double bgRad2 = bgRad * bgRad;
228 int i, j, k;
229 double dx, dy, dz, d, d2, dy2z2, dz2;
230 int xmin, xmax, ymin, ymax, zmin, zmax;
231 xmax = (int)floor(gPt.x + gRadius);
232 xmin = (int)ceil(gPt.x - gRadius);
233 ymax = (int)floor(gPt.y + gRadius);
234 ymin = (int)ceil(gPt.y - gRadius);
235 zmax = (int)floor(gPt.z + gRadius);
236 zmin = (int)ceil(gPt.z - gRadius);
237
238 unsigned int oval, val, valChange;
239 int ptId1, ptId2;
240 for (k = zmin; k <= zmax; ++k) {
241 if ((k >= 0) &&
242 (k < (int)d_numZ)) { // we are inside the grid in the z-direction
243 dz = static_cast<double>(k) - gPt.z;
244 dz2 = dz * dz;
245 ptId1 = k * d_numX * d_numY;
246 for (j = ymin; j <= ymax; ++j) {
247 if ((j >= 0) &&
248 (j < (int)d_numY)) { // inside the grid in the y-direction
249 dy = static_cast<double>(j) - gPt.y;
250 dy2z2 = dy * dy + dz2;
251 if (dy2z2 < gRad2) { // we are within the radius at least from the
252 // y,z coordinates
253 ptId2 = ptId1 + j * d_numX;
254 for (i = xmin; i <= xmax; ++i) {
255 if ((i >= 0) && (i < (int)d_numX)) {
256 oval = dp_storage->getVal((unsigned int)(ptId2 + i));
257 if (oval < maxVal) { // if we are already at maxVal we will not
258 // change that
259 dx = static_cast<double>(i) - gPt.x;
260 d2 = dx * dx + dy2z2;
261 if (d2 < gRad2) { // we are inside the sphere
262 if (d2 < bgRad2) {
263 val = maxVal;
264 } else {
265 d = sqrt(d2);
266 valChange = (static_cast<unsigned int>(
267 (d - bgRad) / gStepSize + 1)) *
268 (valStep);
269 if (valChange < maxVal) {
270 val = maxVal - valChange;
271 } else {
272 val = 0;
273 }
274 }
275 if (val > oval) {
276 dp_storage->setVal(ptId2 + i, val);
277 }
278 } // we are inside the sphere
279 } // grid point does not already have maxVal
280 } // inside the grid in x-direction
281 } // loop over points in x-direction
282 } // inside the sphere based on only z and y coords
283 } // we are inside the grid in the y-direction
284 } // loop over points in y-direction
285 } // inside grid in z-direction
286 } // loop over points in z-direction
287 }
288
operator |=(const UniformGrid3D & other)289 UniformGrid3D &UniformGrid3D::operator|=(const UniformGrid3D &other) {
290 PRECONDITION(dp_storage, "uninitialized grid");
291 PRECONDITION(other.dp_storage, "uninitialized grid");
292 PRECONDITION(compareParams(other), "incompatible grids");
293
294 // EFF: we're probably doing too much copying here:
295 RDKit::DiscreteValueVect *newData =
296 new RDKit::DiscreteValueVect((*dp_storage) | (*other.dp_storage));
297 delete dp_storage;
298 dp_storage = newData;
299 return *this;
300 }
301
operator &=(const UniformGrid3D & other)302 UniformGrid3D &UniformGrid3D::operator&=(const UniformGrid3D &other) {
303 PRECONDITION(dp_storage, "uninitialized grid");
304 PRECONDITION(other.dp_storage, "uninitialized grid");
305 PRECONDITION(compareParams(other), "incompatible grids");
306
307 // EFF: we're probably doing too much copying here:
308 RDKit::DiscreteValueVect *newData =
309 new RDKit::DiscreteValueVect((*dp_storage) & (*other.dp_storage));
310 delete dp_storage;
311 dp_storage = newData;
312 return *this;
313 }
314
operator +=(const UniformGrid3D & other)315 UniformGrid3D &UniformGrid3D::operator+=(const UniformGrid3D &other) {
316 PRECONDITION(dp_storage, "uninitialized grid");
317 PRECONDITION(other.dp_storage, "uninitialized grid");
318 PRECONDITION(compareParams(other), "incompatible grids");
319
320 // EFF: we're probably doing too much copying here:
321 *dp_storage += *other.dp_storage;
322 return *this;
323 }
324
operator -=(const UniformGrid3D & other)325 UniformGrid3D &UniformGrid3D::operator-=(const UniformGrid3D &other) {
326 PRECONDITION(dp_storage, "uninitialized grid");
327 PRECONDITION(other.dp_storage, "uninitialized grid");
328 PRECONDITION(compareParams(other), "incompatible grids");
329
330 // EFF: we're probably doing too much copying here:
331 *dp_storage -= *other.dp_storage;
332 return *this;
333 }
334
toString() const335 std::string UniformGrid3D::toString() const {
336 std::stringstream ss(std::ios_base::binary | std::ios_base::out |
337 std::ios_base::in);
338 std::int32_t tVers = ci_GRIDPICKLE_VERSION * -1;
339 streamWrite(ss, tVers);
340 std::uint32_t tInt;
341 tInt = d_numX;
342 streamWrite(ss, tInt);
343 tInt = d_numY;
344 streamWrite(ss, tInt);
345 tInt = d_numZ;
346 streamWrite(ss, tInt);
347 streamWrite(ss, d_spacing);
348 streamWrite(ss, d_offSet.x);
349 streamWrite(ss, d_offSet.y);
350 streamWrite(ss, d_offSet.z);
351
352 std::string storePkl = dp_storage->toString();
353 std::uint32_t pklSz = storePkl.size();
354 streamWrite(ss, pklSz);
355 ss.write(storePkl.c_str(), pklSz * sizeof(char));
356
357 std::string res(ss.str());
358 return (res);
359 }
initFromText(const char * pkl,const unsigned int length)360 void UniformGrid3D::initFromText(const char *pkl, const unsigned int length) {
361 std::stringstream ss(std::ios_base::binary | std::ios_base::in |
362 std::ios_base::out);
363 ss.write(pkl, length);
364 std::int32_t tVers;
365 streamRead(ss, tVers);
366 tVers *= -1;
367 if (tVers == 0x1) {
368 } else {
369 throw ValueErrorException("bad version in UniformGrid3D pickle");
370 }
371 std::uint32_t tInt;
372 streamRead(ss, tInt);
373 d_numX = tInt;
374 streamRead(ss, tInt);
375 d_numY = tInt;
376 streamRead(ss, tInt);
377 d_numZ = tInt;
378 streamRead(ss, d_spacing);
379 double oX, oY, oZ;
380 streamRead(ss, oX);
381 streamRead(ss, oY);
382 streamRead(ss, oZ);
383 d_offSet = Point3D(oX, oY, oZ);
384
385 std::uint32_t pklSz;
386 streamRead(ss, pklSz);
387 auto *buff = new char[pklSz];
388 ss.read(buff, pklSz * sizeof(char));
389 delete dp_storage;
390 dp_storage = new RDKit::DiscreteValueVect(buff, pklSz);
391 delete[] buff;
392 }
393
writeGridToStream(const UniformGrid3D & grid,std::ostream & outStrm)394 void writeGridToStream(const UniformGrid3D &grid, std::ostream &outStrm) {
395 int dimX = (int)grid.getNumX(); //+2;
396 int dimY = (int)grid.getNumY(); //+2;
397 int dimZ = (int)grid.getNumZ(); //+2;
398 double spacing = grid.getSpacing();
399 double lenX = dimX * spacing;
400 double lenY = dimY * spacing;
401 double lenZ = dimZ * spacing;
402 Point3D offSet = grid.getOffset();
403 offSet /= spacing;
404 outStrm << "Grid file representing a Shape \n\n";
405 outStrm << lenX << " " << lenY << " " << lenZ << " 90.0 90.0 90.0"
406 << std::endl;
407 outStrm << dimX - 1 << " " << dimY - 1 << " " << dimZ - 1 << std::endl;
408
409 int outX1 = (int)(floor(offSet.x + 0.5));
410 int outX2 = (int)(floor(offSet.x + 0.5)) + (int)(dimX - 1);
411 // REVIEW: ok - here is a fix to try and make the grid closer to the molecule
412 // when displayed
413 // (at least in PyMol). The difference between the pair of values (outX1,
414 // outX2) is (dimX-1),
415 // that is not the case with pairs the (outY1, outY2) a (outZ1, outZ2). In
416 // these cases, the difference is
417 // dimY and dimZ respectively. Not sure why this should be the case, but
418 // almost always we get a
419 // better display in PyMol.
420 int outY1 = (int)(floor(offSet.y + 0.5));
421 int outY2 = (int)(floor(offSet.y + 0.5)) + (int)(dimY);
422 int outZ1 = (int)(floor(offSet.z + 0.5));
423 int outZ2 = (int)(floor(offSet.z + 0.5)) + (int)(dimZ);
424
425 outStrm << "1"
426 << " " << outX1 << " " << outX2 << " " << outY1 << " " << outY2 << " "
427 << outZ1 << " " << outZ2 << "\n";
428 unsigned int i, nPts = grid.getSize();
429 for (i = 0; i < nPts; i++) {
430 outStrm << static_cast<double>(grid.getVal(i)) << std::endl;
431 }
432 }
433
writeGridToFile(const UniformGrid3D & grid,const std::string & filename)434 void writeGridToFile(const UniformGrid3D &grid, const std::string &filename) {
435 // std::ofstream ofStrm(filename.c_str());
436 auto *ofStrm = new std::ofstream(filename.c_str());
437 auto *oStrm = static_cast<std::ostream *>(ofStrm);
438 writeGridToStream(grid, *oStrm);
439 delete ofStrm;
440 }
441 } // namespace RDGeom
442