OpenMD 3.2
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
ConvexHull.cpp
1/*
2 * Copyright (c) 2004-present, The University of Notre Dame. All rights
3 * reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 * SUPPORT OPEN SCIENCE! If you use OpenMD or its source code in your
32 * research, please cite the following paper when you publish your work:
33 *
34 * [1] Drisko et al., J. Open Source Softw. 9, 7004 (2024).
35 *
36 * Good starting points for code and simulation methodology are:
37 *
38 * [2] Meineke, et al., J. Comp. Chem. 26, 252-271 (2005).
39 * [3] Fennell & Gezelter, J. Chem. Phys. 124, 234104 (2006).
40 * [4] Sun, Lin & Gezelter, J. Chem. Phys. 128, 234107 (2008).
41 * [5] Vardeman, Stocker & Gezelter, J. Chem. Theory Comput. 7, 834 (2011).
42 * [6] Kuang & Gezelter, Mol. Phys., 110, 691-701 (2012).
43 * [7] Lamichhane, Gezelter & Newman, J. Chem. Phys. 141, 134109 (2014).
44 * [8] Bhattarai, Newman & Gezelter, Phys. Rev. B 99, 094106 (2019).
45 * [9] Drisko & Gezelter, J. Chem. Theory Comput. 20, 4986-4997 (2024).
46 */
47
48#include "math/ConvexHull.hpp"
49
50#include <algorithm>
51#include <fstream>
52#include <iostream>
53#include <iterator>
54#include <list>
55
56#ifdef IS_MPI
57#include <mpi.h>
58#endif
59
60#include "math/qhull.hpp"
61#include "utils/simError.h"
62
63#ifdef HAVE_QHULL
64using namespace OpenMD;
65using namespace std;
66
67ConvexHull::ConvexHull() : Hull(), options_("qhull FA Qt Pp QJ"), dim_(3) {}
68
69void ConvexHull::computeHull(vector<StuntDouble*> bodydoubles) {
70#ifdef HAVE_QHULL_REENTRANT
71 qhT qh_qh;
72 qhT* qh = &qh_qh;
73 QHULL_LIB_CHECK
74#endif
75
76 int numpoints = bodydoubles.size();
77
78 Triangles_.clear();
79
80 vertexT *vertex, **vertexp;
81 facetT* facet;
82 setT* vertices;
83 int curlong, totlong;
84
85 vector<double> ptArray(numpoints * dim_);
86
87 // Copy the positon vector into a points vector for qhull.
88 vector<StuntDouble*>::iterator SD;
89 int i = 0;
90
91 for (SD = bodydoubles.begin(); SD != bodydoubles.end(); ++SD) {
92 Vector3d pos = (*SD)->getPos();
93 ptArray[dim_ * i] = pos.x();
94 ptArray[dim_ * i + 1] = pos.y();
95 ptArray[dim_ * i + 2] = pos.z();
96 i++;
97 }
98
99 /* Clean up memory from previous convex hull calculations */
100 boolT ismalloc = False;
101
102 /* compute the hull for our local points (or all the points for single
103 processor versions) */
104#ifdef HAVE_QHULL_REENTRANT
105 qh_init_A(qh, NULL, NULL, stderr, 0, NULL);
106 int exitcode = setjmp(qh->errexit);
107 if (!exitcode) {
108 qh->NOerrexit = False;
109 qh_initflags(qh, const_cast<char*>(options_.c_str()));
110 qh_init_B(qh, &ptArray[0], numpoints, dim_, ismalloc);
111 qh_qhull(qh);
112 qh_check_output(qh);
113 exitcode = qh_ERRnone;
114 qh->NOerrexit = True;
115 } else {
116 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
117 "ConvexHull: Qhull failed to compute convex hull");
118 painCave.isFatal = 1;
119 simError();
120 }
121#else
122 qh_init_A(NULL, NULL, stderr, 0, NULL);
123 int exitcode = setjmp(qh errexit);
124 if (!exitcode) {
125 qh_initflags(const_cast<char*>(options_.c_str()));
126 qh_init_B(&ptArray[0], numpoints, dim_, ismalloc);
127 qh_qhull();
128 qh_check_output();
129 exitcode = qh_ERRnone;
130 qh NOerrexit = True;
131 } else {
132 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
133 "ConvexHull: Qhull failed to compute convex hull");
134 painCave.isFatal = 1;
135 simError();
136 }
137#endif
138
139#ifdef IS_MPI
140 // If we are doing the mpi version, set up some vectors for data communication
141
142 int nproc;
143 int myrank;
144
145 MPI_Comm_size(MPI_COMM_WORLD, &nproc);
146 MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
147
148 int localHullSites = 0;
149
150 vector<int> hullSitesOnProc(nproc, 0);
151 vector<int> coordsOnProc(nproc, 0);
152 vector<int> displacements(nproc, 0);
153 vector<int> vectorDisplacements(nproc, 0);
154
155 vector<double> coords;
156 vector<double> vels;
157 vector<int> indexMap;
158 vector<double> masses;
159
160 FORALLvertices {
161 localHullSites++;
162
163#ifdef HAVE_QHULL_REENTRANT
164 int idx = qh_pointid(qh, vertex->point);
165#else
166 int idx = qh_pointid(vertex->point);
167#endif
168
169 indexMap.push_back(idx);
170
171 coords.push_back(ptArray[dim_ * idx]);
172 coords.push_back(ptArray[dim_ * idx + 1]);
173 coords.push_back(ptArray[dim_ * idx + 2]);
174
175 StuntDouble* sd = bodydoubles[idx];
176
177 Vector3d vel = sd->getVel();
178 vels.push_back(vel.x());
179 vels.push_back(vel.y());
180 vels.push_back(vel.z());
181
182 masses.push_back(sd->getMass());
183 }
184
185 MPI_Allgather(&localHullSites, 1, MPI_INT, &hullSitesOnProc[0], 1, MPI_INT,
186 MPI_COMM_WORLD);
187
188 int globalHullSites = 0;
189 for (int iproc = 0; iproc < nproc; iproc++) {
190 globalHullSites += hullSitesOnProc[iproc];
191 coordsOnProc[iproc] = dim_ * hullSitesOnProc[iproc];
192 }
193
194 displacements[0] = 0;
195 vectorDisplacements[0] = 0;
196
197 for (int iproc = 1; iproc < nproc; iproc++) {
198 displacements[iproc] =
199 displacements[iproc - 1] + hullSitesOnProc[iproc - 1];
200 vectorDisplacements[iproc] =
201 vectorDisplacements[iproc - 1] + coordsOnProc[iproc - 1];
202 }
203
204 vector<double> globalCoords(dim_ * globalHullSites);
205 vector<double> globalVels(dim_ * globalHullSites);
206 vector<double> globalMasses(globalHullSites);
207
208 int count = coordsOnProc[myrank];
209
210 MPI_Allgatherv(&coords[0], count, MPI_DOUBLE, &globalCoords[0],
211 &coordsOnProc[0], &vectorDisplacements[0], MPI_DOUBLE,
212 MPI_COMM_WORLD);
213
214 MPI_Allgatherv(&vels[0], count, MPI_DOUBLE, &globalVels[0], &coordsOnProc[0],
215 &vectorDisplacements[0], MPI_DOUBLE, MPI_COMM_WORLD);
216
217 MPI_Allgatherv(&masses[0], localHullSites, MPI_DOUBLE, &globalMasses[0],
218 &hullSitesOnProc[0], &displacements[0], MPI_DOUBLE,
219 MPI_COMM_WORLD);
220
221 // Free previous hull
222#ifdef HAVE_QHULL_REENTRANT
223 qh_freeqhull(qh, !qh_ALL);
224 qh_memfreeshort(qh, &curlong, &totlong);
225#else
226 qh_freeqhull(!qh_ALL);
227 qh_memfreeshort(&curlong, &totlong);
228#endif
229 if (curlong || totlong) {
230 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
231 "ConvexHull: qhull internal warning:\n"
232 "\tdid not free %d bytes of long memory (%d pieces)",
233 totlong, curlong);
234 painCave.isFatal = 1;
235 simError();
236 }
237
238#ifdef HAVE_QHULL_REENTRANT
239 qh_init_A(qh, NULL, NULL, stderr, 0, NULL);
240 exitcode = setjmp(qh->errexit);
241 if (!exitcode) {
242 qh->NOerrexit = False;
243 qh_initflags(qh, const_cast<char*>(options_.c_str()));
244 qh_init_B(qh, &globalCoords[0], globalHullSites, dim_, ismalloc);
245 qh_qhull(qh);
246 qh_check_output(qh);
247 exitcode = qh_ERRnone;
248 qh->NOerrexit = True;
249 } else {
250 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
251 "ConvexHull: Qhull failed to compute convex hull");
252 painCave.isFatal = 1;
253 simError();
254 }
255#else
256 qh_init_A(NULL, NULL, stderr, 0, NULL);
257 exitcode = setjmp(qh errexit);
258 if (!exitcode) {
259 qh NOerrexit = False;
260 qh_initflags(const_cast<char*>(options_.c_str()));
261 qh_init_B(&globalCoords[0], globalHullSites, dim_, ismalloc);
262 qh_qhull();
263 qh_check_output();
264 exitcode = qh_ERRnone;
265 qh NOerrexit = True; /* no more setjmp */
266 } else {
267 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
268 "ConvexHull: Qhull failed to compute global convex hull");
269 painCave.isFatal = 1;
270 simError();
271
272 } // qh_new_qhull
273#endif
274
275#endif
276 // commented out below, so comment out here also.
277 // intPoint = qh interior_point;
278 // RealType calcvol = 0.0;
279
280#ifdef HAVE_QHULL_REENTRANT
281 qh_triangulate(qh);
282#else
283 qh_triangulate();
284#endif
285
286 FORALLfacets {
287 Triangle face;
288 // Qhull sets the unit normal in facet->normal
289 Vector3d V3dNormal(facet->normal[0], facet->normal[1], facet->normal[2]);
290 face.setUnitNormal(V3dNormal);
291
292#ifdef HAVE_QHULL_REENTRANT
293 RealType faceArea = qh_facetarea(qh, facet);
294 face.setArea(faceArea);
295 vertices = qh_facet3vertex(qh, facet);
296 coordT* center = qh_getcenter(qh, vertices);
297#else
298 RealType faceArea = qh_facetarea(facet);
299 face.setArea(faceArea);
300 vertices = qh_facet3vertex(facet);
301 coordT* center = qh_getcenter(vertices);
302#endif
303 Vector3d V3dCentroid(center[0], center[1], center[2]);
304 face.setCentroid(V3dCentroid);
305
306 Vector3d faceVel = V3Zero;
307 Vector3d p[3];
308 RealType faceMass = 0.0;
309
310 int ver = 0;
311
312 FOREACHvertex_(vertices) {
313#ifdef HAVE_QHULL_REENTRANT
314 int id = qh_pointid(qh, vertex->point);
315#else
316 int id = qh_pointid(vertex->point);
317#endif
318 p[ver][0] = vertex->point[0];
319 p[ver][1] = vertex->point[1];
320 p[ver][2] = vertex->point[2];
321 Vector3d vel;
322 RealType mass;
323
324#ifdef IS_MPI
325 vel = Vector3d(globalVels[dim_ * id], globalVels[dim_ * id + 1],
326 globalVels[dim_ * id + 2]);
327 mass = globalMasses[id];
328
329 // localID will be between 0 and hullSitesOnProc[myrank] if we
330 // own this guy.
331
332 int localID = id - displacements[myrank];
333
334 if (localID >= 0 && localID < hullSitesOnProc[myrank]) {
335 face.addVertexSD(bodydoubles[indexMap[localID]]);
336 } else {
337 face.addVertexSD(NULL);
338 }
339#else
340 vel = bodydoubles[id]->getVel();
341 mass = bodydoubles[id]->getMass();
342 face.addVertexSD(bodydoubles[id]);
343#endif
344 faceVel = faceVel + vel;
345 faceMass = faceMass + mass;
346 ver++;
347 } // Foreachvertex
348
349 face.addVertices(p[0], p[1], p[2]);
350 face.setFacetMass(faceMass);
351 face.setFacetVelocity(faceVel / RealType(3.0));
352 /*
353 RealType comparea = face.computeArea();
354 realT calcarea = qh_facetarea (facet);
355 Vector3d V3dCompNorm = -face.computeUnitNormal();
356 RealType thisOffset = ((0.0-p[0][0])*V3dCompNorm[0] +
357 (0.0-p[0][1])*V3dCompNorm[1] + (0.0-p[0][2])*V3dCompNorm[2]); RealType dist
358 = facet->offset + intPoint[0]*V3dNormal[0] + intPoint[1]*V3dNormal[1] +
359 intPoint[2]*V3dNormal[2]; cout
360 << "facet offset and computed offset: " << facet->offset << " " <<
361 thisOffset << endl; calcvol += -dist*comparea/qh hull_dim;
362 */
363 Triangles_.push_back(face);
364#ifdef HAVE_QHULL_REENTRANT
365 qh_settempfree(qh, &vertices);
366#else
367 qh_settempfree(&vertices);
368#endif
369 } // FORALLfacets
370
371#ifdef HAVE_QHULL_REENTRANT
372 qh_getarea(qh, qh->facet_list);
373 volume_ = qh->totvol;
374 area_ = qh->totarea;
375 qh_freeqhull(qh, !qh_ALL);
376 qh_memfreeshort(qh, &curlong, &totlong);
377
378#else
379 qh_getarea(qh facet_list);
380 volume_ = qh totvol;
381 area_ = qh totarea;
382
383 qh_freeqhull(!qh_ALL);
384 qh_memfreeshort(&curlong, &totlong);
385#endif
386 if (curlong || totlong) {
387 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
388 "ConvexHull: qhull internal warning:\n"
389 "\tdid not free %d bytes of long memory (%d pieces)",
390 totlong, curlong);
391 painCave.isFatal = 1;
392 simError();
393 }
394}
395
396// void ConvexHull::printHull(const string& geomFileName) {
397
398// #ifdef IS_MPI
399// if (worldRank == 0) {
400// #endif
401// FILE *newGeomFile;
402
403// //create new .omd file based on old .omd file
404// newGeomFile = fopen(geomFileName.c_str(), "w");
405// #ifdef HAVE_QHULL_REENTRANT
406// qh_findgood_all(qh, qh->facet_list);
407// #else
408// qh_findgood_all(qh facet_list);
409// #endif
410// for (int i = 0; i < qh_PRINTEND; i++)
411// #ifdef HAVE_QHULL_REENTRANT
412// qh_printfacets(qh, newGeomFile, qh->PRINTout[i], qh->facet_list, NULL,
413// !qh_ALL);
414// #else
415// qh_printfacets(newGeomFile, qh PRINTout[i], qh facet_list, NULL,
416// !qh_ALL);
417// #endif
418// fclose(newGeomFile);
419// #ifdef IS_MPI
420// }
421// #endif
422// }
423#endif // QHULL
Vector3d getVel()
Returns the current velocity of this stuntDouble.
RealType getMass()
Returns the mass of this stuntDouble.
Real & z()
Returns reference of the third element of Vector3.
Definition Vector3.hpp:123
Real & x()
Returns reference of the first element of Vector3.
Definition Vector3.hpp:99
Real & y()
Returns reference of the second element of Vector3.
Definition Vector3.hpp:111
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.