2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5 * Copyright (c) 2001-2007, The GROMACS development team.
6 * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
7 * Copyright (c) 2019,2020, by the GROMACS development team, led by
8 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
9 * and including many others, as listed in the AUTHORS file in the
10 * top-level source directory and at http://www.gromacs.org.
12 * GROMACS is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public License
14 * as published by the Free Software Foundation; either version 2.1
15 * of the License, or (at your option) any later version.
17 * GROMACS is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with GROMACS; if not, see
24 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
25 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 * If you want to redistribute modifications to GROMACS, please
28 * consider that scientific software is very special. Version
29 * control is crucial - bugs must be traceable. We will be happy to
30 * consider code for inclusion in the official distribution, but
31 * derived work must not be called official GROMACS. Details are found
32 * in the README & COPYING files - if they are missing, get the
33 * official version at http://www.gromacs.org.
35 * To help us fund GROMACS development, we humbly ask that you cite
36 * the research papers on the package. Check out http://www.gromacs.org.
40 #include "surfacearea.h"
50 #include "gromacs/math/functions.h"
51 #include "gromacs/math/utilities.h"
52 #include "gromacs/math/vec.h"
53 #include "gromacs/pbcutil/pbc.h"
54 #include "gromacs/selection/nbsearch.h"
55 #include "gromacs/utility/fatalerror.h"
56 #include "gromacs/utility/gmxassert.h"
57 #include "gromacs/utility/smalloc.h"
61 #define UNSP_ICO_DOD 9
62 #define UNSP_ICO_ARC 10
64 #define FOURPI (4. * M_PI)
65 #define TORAD(A) ((A)*0.017453293)
68 static real safe_asin(real f)
74 GMX_ASSERT(fabs(f) - 1.0 > DP_TOL, "Invalid argument");
78 /* routines for dot distributions on the surface of the unit sphere */
79 static real icosaeder_vertices(real* xus)
81 const real rh = std::sqrt(1. - 2. * cos(TORAD(72.))) / (1. - cos(TORAD(72.)));
82 const real rg = cos(TORAD(72.)) / (1. - cos(TORAD(72.)));
83 /* icosaeder vertices */
87 xus[3] = rh * cos(TORAD(72.));
88 xus[4] = rh * sin(TORAD(72.));
90 xus[6] = rh * cos(TORAD(144.));
91 xus[7] = rh * sin(TORAD(144.));
93 xus[9] = rh * cos(TORAD(216.));
94 xus[10] = rh * sin(TORAD(216.));
96 xus[12] = rh * cos(TORAD(288.));
97 xus[13] = rh * sin(TORAD(288.));
102 xus[18] = rh * cos(TORAD(36.));
103 xus[19] = rh * sin(TORAD(36.));
105 xus[21] = rh * cos(TORAD(108.));
106 xus[22] = rh * sin(TORAD(108.));
111 xus[27] = rh * cos(TORAD(252.));
112 xus[28] = rh * sin(TORAD(252.));
114 xus[30] = rh * cos(TORAD(324.));
115 xus[31] = rh * sin(TORAD(324.));
125 divarc(real x1, real y1, real z1, real x2, real y2, real z2, int div1, int div2, real* xr, real* yr, real* zr)
128 real xd, yd, zd, dd, d1, d2, s, x, y, z;
129 real phi, sphi, cphi;
131 xd = y1 * z2 - y2 * z1;
132 yd = z1 * x2 - z2 * x1;
133 zd = x1 * y2 - x2 * y1;
134 dd = std::sqrt(xd * xd + yd * yd + zd * zd);
135 GMX_ASSERT(dd >= DP_TOL, "Rotation axis vector too short");
137 d1 = x1 * x1 + y1 * y1 + z1 * z1;
138 d2 = x2 * x2 + y2 * y2 + z2 * z2;
139 GMX_ASSERT(d1 >= 0.5, "Vector 1 too short");
140 GMX_ASSERT(d2 >= 0.5, "Vector 2 too short");
142 phi = safe_asin(dd / std::sqrt(d1 * d2));
143 phi = phi * (static_cast<real>(div1)) / (static_cast<real>(div2));
146 s = (x1 * xd + y1 * yd + z1 * zd) / dd;
148 x = xd * s * (1. - cphi) / dd + x1 * cphi + (yd * z1 - y1 * zd) * sphi / dd;
149 y = yd * s * (1. - cphi) / dd + y1 * cphi + (zd * x1 - z1 * xd) * sphi / dd;
150 z = zd * s * (1. - cphi) / dd + z1 * cphi + (xd * y1 - x1 * yd) * sphi / dd;
151 dd = std::sqrt(x * x + y * y + z * z);
157 /* densit...required dots per unit sphere */
158 static std::vector<real> ico_dot_arc(int densit)
160 /* dot distribution on a unit sphere based on an icosaeder *
161 * great circle average refining of icosahedral face */
163 int i, j, k, tl, tl2, tn;
164 real a, d, x, y, z, x2, y2, z2, x3, y3, z3;
165 real xij, yij, zij, xji, yji, zji, xik, yik, zik, xki, yki, zki, xjk, yjk, zjk, xkj, ykj, zkj;
167 /* calculate tessalation level */
168 a = std::sqrt(((static_cast<real>(densit)) - 2.) / 10.);
169 const int tess = static_cast<int>(ceil(a));
170 const int ndot = 10 * tess * tess + 2;
171 GMX_RELEASE_ASSERT(ndot >= densit, "Inconsistent surface dot formula");
173 std::vector<real> xus(3 * ndot);
174 const real rh = icosaeder_vertices(xus.data());
179 a = rh * rh * 2. * (1. - cos(TORAD(72.)));
180 /* calculate tessalation of icosaeder edges */
181 for (i = 0; i < 11; i++)
183 for (j = i + 1; j < 12; j++)
185 x = xus[3 * i] - xus[3 * j];
186 y = xus[1 + 3 * i] - xus[1 + 3 * j];
187 z = xus[2 + 3 * i] - xus[2 + 3 * j];
188 d = x * x + y * y + z * z;
189 if (std::fabs(a - d) > DP_TOL)
193 for (tl = 1; tl < tess; tl++)
195 GMX_ASSERT(tn < ndot, "Inconsistent precomputed surface dot count");
196 divarc(xus[3 * i], xus[1 + 3 * i], xus[2 + 3 * i], xus[3 * j], xus[1 + 3 * j],
197 xus[2 + 3 * j], tl, tess, &xus[3 * tn], &xus[1 + 3 * tn], &xus[2 + 3 * tn]);
202 /* calculate tessalation of icosaeder faces */
203 for (i = 0; i < 10; i++)
205 for (j = i + 1; j < 11; j++)
207 x = xus[3 * i] - xus[3 * j];
208 y = xus[1 + 3 * i] - xus[1 + 3 * j];
209 z = xus[2 + 3 * i] - xus[2 + 3 * j];
210 d = x * x + y * y + z * z;
211 if (std::fabs(a - d) > DP_TOL)
216 for (k = j + 1; k < 12; k++)
218 x = xus[3 * i] - xus[3 * k];
219 y = xus[1 + 3 * i] - xus[1 + 3 * k];
220 z = xus[2 + 3 * i] - xus[2 + 3 * k];
221 d = x * x + y * y + z * z;
222 if (std::fabs(a - d) > DP_TOL)
226 x = xus[3 * j] - xus[3 * k];
227 y = xus[1 + 3 * j] - xus[1 + 3 * k];
228 z = xus[2 + 3 * j] - xus[2 + 3 * k];
229 d = x * x + y * y + z * z;
230 if (std::fabs(a - d) > DP_TOL)
234 for (tl = 1; tl < tess - 1; tl++)
236 divarc(xus[3 * j], xus[1 + 3 * j], xus[2 + 3 * j], xus[3 * i],
237 xus[1 + 3 * i], xus[2 + 3 * i], tl, tess, &xji, &yji, &zji);
238 divarc(xus[3 * k], xus[1 + 3 * k], xus[2 + 3 * k], xus[3 * i],
239 xus[1 + 3 * i], xus[2 + 3 * i], tl, tess, &xki, &yki, &zki);
241 for (tl2 = 1; tl2 < tess - tl; tl2++)
243 divarc(xus[3 * i], xus[1 + 3 * i], xus[2 + 3 * i], xus[3 * j],
244 xus[1 + 3 * j], xus[2 + 3 * j], tl2, tess, &xij, &yij, &zij);
245 divarc(xus[3 * k], xus[1 + 3 * k], xus[2 + 3 * k], xus[3 * j],
246 xus[1 + 3 * j], xus[2 + 3 * j], tl2, tess, &xkj, &ykj, &zkj);
247 divarc(xus[3 * i], xus[1 + 3 * i], xus[2 + 3 * i], xus[3 * k], xus[1 + 3 * k],
248 xus[2 + 3 * k], tess - tl - tl2, tess, &xik, &yik, &zik);
249 divarc(xus[3 * j], xus[1 + 3 * j], xus[2 + 3 * j], xus[3 * k], xus[1 + 3 * k],
250 xus[2 + 3 * k], tess - tl - tl2, tess, &xjk, &yjk, &zjk);
251 divarc(xki, yki, zki, xji, yji, zji, tl2, tess - tl, &x, &y, &z);
252 divarc(xkj, ykj, zkj, xij, yij, zij, tl, tess - tl2, &x2, &y2, &z2);
253 divarc(xjk, yjk, zjk, xik, yik, zik, tl, tl + tl2, &x3, &y3, &z3);
254 GMX_ASSERT(tn < ndot, "Inconsistent precomputed surface dot count");
258 d = std::sqrt(x * x + y * y + z * z);
260 xus[1 + 3 * tn] = y / d;
261 xus[2 + 3 * tn] = z / d;
268 GMX_ASSERT(tn == ndot, "Inconsistent precomputed surface dot count");
269 } /* end of if (tess > 1) */
272 } /* end of routine ico_dot_arc */
274 /* densit...required dots per unit sphere */
275 static std::vector<real> ico_dot_dod(int densit)
277 /* dot distribution on a unit sphere based on an icosaeder *
278 * great circle average refining of icosahedral face */
280 int i, j, k, tl, tl2, tn, tess, j1, j2;
281 real a, d, x, y, z, x2, y2, z2, x3, y3, z3, ai_d, adod;
282 real xij, yij, zij, xji, yji, zji, xik, yik, zik, xki, yki, zki, xjk, yjk, zjk, xkj, ykj, zkj;
284 /* calculate tesselation level */
285 a = std::sqrt(((static_cast<real>(densit)) - 2.) / 30.);
286 tess = std::max(static_cast<int>(ceil(a)), 1);
287 const int ndot = 30 * tess * tess + 2;
288 GMX_RELEASE_ASSERT(ndot >= densit, "Inconsistent surface dot formula");
290 std::vector<real> xus(3 * ndot);
291 const real rh = icosaeder_vertices(xus.data());
294 /* square of the edge of an icosaeder */
295 a = rh * rh * 2. * (1. - cos(TORAD(72.)));
296 /* dodecaeder vertices */
297 for (i = 0; i < 10; i++)
299 for (j = i + 1; j < 11; j++)
301 x = xus[3 * i] - xus[3 * j];
302 y = xus[1 + 3 * i] - xus[1 + 3 * j];
303 z = xus[2 + 3 * i] - xus[2 + 3 * j];
304 d = x * x + y * y + z * z;
305 if (std::fabs(a - d) > DP_TOL)
309 for (k = j + 1; k < 12; k++)
311 x = xus[3 * i] - xus[3 * k];
312 y = xus[1 + 3 * i] - xus[1 + 3 * k];
313 z = xus[2 + 3 * i] - xus[2 + 3 * k];
314 d = x * x + y * y + z * z;
315 if (std::fabs(a - d) > DP_TOL)
319 x = xus[3 * j] - xus[3 * k];
320 y = xus[1 + 3 * j] - xus[1 + 3 * k];
321 z = xus[2 + 3 * j] - xus[2 + 3 * k];
322 d = x * x + y * y + z * z;
323 if (std::fabs(a - d) > DP_TOL)
327 x = xus[3 * i] + xus[3 * j] + xus[3 * k];
328 y = xus[1 + 3 * i] + xus[1 + 3 * j] + xus[1 + 3 * k];
329 z = xus[2 + 3 * i] + xus[2 + 3 * j] + xus[2 + 3 * k];
330 d = std::sqrt(x * x + y * y + z * z);
332 xus[1 + 3 * tn] = y / d;
333 xus[2 + 3 * tn] = z / d;
342 /* square of the edge of an dodecaeder */
343 adod = 4. * (cos(TORAD(108.)) - cos(TORAD(120.))) / (1. - cos(TORAD(120.)));
344 /* square of the distance of two adjacent vertices of ico- and dodecaeder */
345 ai_d = 2. * (1. - std::sqrt(1. - a / 3.));
347 /* calculate tessalation of mixed edges */
348 for (i = 0; i < 31; i++)
358 for (j = j1; j < j2; j++)
360 x = xus[3 * i] - xus[3 * j];
361 y = xus[1 + 3 * i] - xus[1 + 3 * j];
362 z = xus[2 + 3 * i] - xus[2 + 3 * j];
363 d = x * x + y * y + z * z;
364 if (std::fabs(a - d) > DP_TOL)
368 for (tl = 1; tl < tess; tl++)
370 GMX_ASSERT(tn < ndot, "Inconsistent precomputed surface dot count");
371 divarc(xus[3 * i], xus[1 + 3 * i], xus[2 + 3 * i], xus[3 * j], xus[1 + 3 * j],
372 xus[2 + 3 * j], tl, tess, &xus[3 * tn], &xus[1 + 3 * tn], &xus[2 + 3 * tn]);
377 /* calculate tessalation of pentakisdodecahedron faces */
378 for (i = 0; i < 12; i++)
380 for (j = 12; j < 31; j++)
382 x = xus[3 * i] - xus[3 * j];
383 y = xus[1 + 3 * i] - xus[1 + 3 * j];
384 z = xus[2 + 3 * i] - xus[2 + 3 * j];
385 d = x * x + y * y + z * z;
386 if (std::fabs(ai_d - d) > DP_TOL)
391 for (k = j + 1; k < 32; k++)
393 x = xus[3 * i] - xus[3 * k];
394 y = xus[1 + 3 * i] - xus[1 + 3 * k];
395 z = xus[2 + 3 * i] - xus[2 + 3 * k];
396 d = x * x + y * y + z * z;
397 if (std::fabs(ai_d - d) > DP_TOL)
401 x = xus[3 * j] - xus[3 * k];
402 y = xus[1 + 3 * j] - xus[1 + 3 * k];
403 z = xus[2 + 3 * j] - xus[2 + 3 * k];
404 d = x * x + y * y + z * z;
405 if (std::fabs(adod - d) > DP_TOL)
409 for (tl = 1; tl < tess - 1; tl++)
411 divarc(xus[3 * j], xus[1 + 3 * j], xus[2 + 3 * j], xus[3 * i],
412 xus[1 + 3 * i], xus[2 + 3 * i], tl, tess, &xji, &yji, &zji);
413 divarc(xus[3 * k], xus[1 + 3 * k], xus[2 + 3 * k], xus[3 * i],
414 xus[1 + 3 * i], xus[2 + 3 * i], tl, tess, &xki, &yki, &zki);
416 for (tl2 = 1; tl2 < tess - tl; tl2++)
418 divarc(xus[3 * i], xus[1 + 3 * i], xus[2 + 3 * i], xus[3 * j],
419 xus[1 + 3 * j], xus[2 + 3 * j], tl2, tess, &xij, &yij, &zij);
420 divarc(xus[3 * k], xus[1 + 3 * k], xus[2 + 3 * k], xus[3 * j],
421 xus[1 + 3 * j], xus[2 + 3 * j], tl2, tess, &xkj, &ykj, &zkj);
422 divarc(xus[3 * i], xus[1 + 3 * i], xus[2 + 3 * i], xus[3 * k], xus[1 + 3 * k],
423 xus[2 + 3 * k], tess - tl - tl2, tess, &xik, &yik, &zik);
424 divarc(xus[3 * j], xus[1 + 3 * j], xus[2 + 3 * j], xus[3 * k], xus[1 + 3 * k],
425 xus[2 + 3 * k], tess - tl - tl2, tess, &xjk, &yjk, &zjk);
426 divarc(xki, yki, zki, xji, yji, zji, tl2, tess - tl, &x, &y, &z);
427 divarc(xkj, ykj, zkj, xij, yij, zij, tl, tess - tl2, &x2, &y2, &z2);
428 divarc(xjk, yjk, zjk, xik, yik, zik, tl, tl + tl2, &x3, &y3, &z3);
429 GMX_ASSERT(tn < ndot, "Inconsistent precomputed surface dot count");
433 d = std::sqrt(x * x + y * y + z * z);
435 xus[1 + 3 * tn] = y / d;
436 xus[2 + 3 * tn] = z / d;
443 GMX_ASSERT(tn == ndot, "Inconsistent precomputed surface dot count");
444 } /* end of if (tess > 1) */
447 } /* end of routine ico_dot_dod */
449 static int unsp_type(int densit)
453 while (10 * i1 * i1 + 2 < densit)
458 while (30 * i2 * i2 + 2 < densit)
462 if (10 * i1 * i1 - 2 < 30 * i2 * i2 - 2)
472 static std::vector<real> make_unsp(int densit, int cubus)
474 int *ico_wk, *ico_pt;
475 int ico_cube, ico_cube_cb, i, j, k, l, ijk, tn, tl, tl2;
479 int mode = unsp_type(densit);
480 std::vector<real> xus;
481 if (mode == UNSP_ICO_ARC)
483 xus = ico_dot_arc(densit);
485 else if (mode == UNSP_ICO_DOD)
487 xus = ico_dot_dod(densit);
491 GMX_RELEASE_ASSERT(false, "Invalid unit sphere mode");
494 const int ndot = ssize(xus) / 3;
496 /* determine distribution of points in elementary cubes */
504 while (i * i * i * 2 < ndot)
508 ico_cube = std::max(i - 1, 0);
510 ico_cube_cb = ico_cube * ico_cube * ico_cube;
511 const real del_cube = 2. / (static_cast<real>(ico_cube));
513 for (l = 0; l < ndot; l++)
515 i = std::max(static_cast<int>(floor((1. + xus[3 * l]) / del_cube)), 0);
520 j = std::max(static_cast<int>(floor((1. + xus[1 + 3 * l]) / del_cube)), 0);
525 k = std::max(static_cast<int>(floor((1. + xus[2 + 3 * l]) / del_cube)), 0);
530 ijk = i + j * ico_cube + k * ico_cube * ico_cube;
534 snew(ico_wk, 2 * ico_cube_cb + 1);
536 ico_pt = ico_wk + ico_cube_cb;
537 for (l = 0; l < ndot; l++)
539 ico_wk[work[l]]++; /* dots per elementary cube */
542 /* reordering of the coordinate array in accordance with box number */
544 for (i = 0; i < ico_cube; i++)
546 for (j = 0; j < ico_cube; j++)
548 for (k = 0; k < ico_cube; k++)
552 ijk = i + ico_cube * j + ico_cube * ico_cube * k;
553 *(ico_pt + ijk) = tn;
554 for (l = tl2; l < ndot; l++)
561 xus[3 * l] = xus[3 * tn];
562 xus[1 + 3 * l] = xus[1 + 3 * tn];
563 xus[2 + 3 * l] = xus[2 + 3 * tn];
574 *(ico_wk + ijk) = tl;
584 static void nsc_dclm_pbc(const rvec* coords,
585 const ArrayRef<const real>& radius,
596 AnalysisNeighborhood* nb,
599 const real dotarea = FOURPI / static_cast<real>(n_dot);
603 fprintf(debug, "nsc_dclm: n_dot=%5d %9.3f\n", n_dot, dotarea);
606 /* start with neighbour list */
607 /* calculate neighbour list with the box algorithm */
612 real area = 0.0, vol = 0.0;
613 real *dots = nullptr, *atom_area = nullptr;
614 int lfnr = 0, maxdots = 0;
615 if (mode & FLAG_VOLUME)
619 if (mode & FLAG_DOTS)
621 maxdots = (3 * n_dot * nat) / 10;
625 if (mode & FLAG_ATOM_AREA)
627 snew(atom_area, nat);
630 // Compute the center of the molecule for volume calculation.
631 // In principle, the center should not influence the results, but that is
632 // only true at the limit of infinite dot density, so this makes the
633 // results translation-invariant.
634 // With PBC, if the molecule is broken across the boundary, the computation
635 // is broken in other ways as well, so it does not need to be considered
637 real xs = 0.0, ys = 0.0, zs = 0.0;
638 for (int i = 0; i < nat; ++i)
640 const int iat = index[i];
641 xs += coords[iat][XX];
642 ys += coords[iat][YY];
643 zs += coords[iat][ZZ];
649 AnalysisNeighborhoodPositions pos(coords, radius.size());
650 pos.indexed(constArrayRefFromArray(index, nat));
651 AnalysisNeighborhoodSearch nbsearch(nb->initSearch(pbc, pos));
653 std::vector<int> wkdot(n_dot);
655 for (int i = 0; i < nat; ++i)
657 const int iat = index[i];
658 const real ai = radius[iat];
659 const real aisq = ai * ai;
660 AnalysisNeighborhoodPairSearch pairSearch(nbsearch.startPairSearch(coords[iat]));
661 AnalysisNeighborhoodPair pair;
662 std::fill(wkdot.begin(), wkdot.end(), 1);
663 int currDotCount = n_dot;
664 while (currDotCount > 0 && pairSearch.findNextPair(&pair))
666 const int jat = index[pair.refIndex()];
667 const real aj = radius[jat];
668 const real d2 = pair.distance2();
669 if (iat == jat || d2 > gmx::square(ai + aj))
673 const rvec& dx = pair.dx();
674 const real refdot = (d2 + aisq - aj * aj) / (2 * ai);
675 // TODO: Consider whether micro-optimizations from the old
676 // implementation would be useful, compared to the complexity that
677 // they bring: instead of this direct loop, the neighbors were
678 // stored into a temporary array, the loop order was
679 // reversed (first over dots, then over neighbors), and for each
680 // dot, it was first checked whether the same neighbor that
681 // resulted in marking the previous dot covered would also cover
682 // this dot. This presumably plays together with sorting of the
683 // surface dots (done in make_unsp) to avoid some of the looping.
684 // Alternatively, we could keep a skip list here to avoid
685 // repeatedly looping over dots that have already marked as
687 for (int j = 0; j < n_dot; ++j)
689 if (wkdot[j] && iprod(&xus[3 * j], dx) > refdot)
697 const real a = aisq * dotarea * currDotCount;
699 if (mode & FLAG_ATOM_AREA)
703 const real xi = coords[iat][XX];
704 const real yi = coords[iat][YY];
705 const real zi = coords[iat][ZZ];
706 if (mode & FLAG_DOTS)
708 for (int l = 0; l < n_dot; l++)
713 if (maxdots <= 3 * lfnr + 1)
715 maxdots = maxdots + n_dot * 3;
716 srenew(dots, maxdots);
718 dots[3 * lfnr - 3] = ai * xus[3 * l] + xi;
719 dots[3 * lfnr - 2] = ai * xus[1 + 3 * l] + yi;
720 dots[3 * lfnr - 1] = ai * xus[2 + 3 * l] + zi;
724 if (mode & FLAG_VOLUME)
726 real dx = 0.0, dy = 0.0, dz = 0.0;
727 for (int l = 0; l < n_dot; l++)
731 dx = dx + xus[3 * l];
732 dy = dy + xus[1 + 3 * l];
733 dz = dz + xus[2 + 3 * l];
736 vol = vol + aisq * (dx * (xi - xs) + dy * (yi - ys) + dz * (zi - zs) + ai * currDotCount);
740 if (mode & FLAG_VOLUME)
742 *value_of_vol = vol * FOURPI / (3. * n_dot);
744 if (mode & FLAG_DOTS)
746 GMX_RELEASE_ASSERT(nu_dots != nullptr, "Must have valid nu_dots pointer");
748 GMX_RELEASE_ASSERT(lidots != nullptr, "Must have valid lidots pointer");
751 if (mode & FLAG_ATOM_AREA)
753 GMX_RELEASE_ASSERT(at_area != nullptr, "Must have valid at_area pointer");
754 *at_area = atom_area;
756 *value_of_area = area;
760 fprintf(debug, "area=%8.3f\n", area);
767 class SurfaceAreaCalculator::Impl
770 Impl() : flags_(0) {}
772 std::vector<real> unitSphereDots_;
773 ArrayRef<const real> radius_;
775 mutable AnalysisNeighborhood nb_;
778 SurfaceAreaCalculator::SurfaceAreaCalculator() : impl_(new Impl()) {}
780 SurfaceAreaCalculator::~SurfaceAreaCalculator() {}
782 void SurfaceAreaCalculator::setDotCount(int dotCount)
784 impl_->unitSphereDots_ = make_unsp(dotCount, 4);
787 void SurfaceAreaCalculator::setRadii(const ArrayRef<const real>& radius)
789 impl_->radius_ = radius;
792 const real maxRadius = *std::max_element(radius.begin(), radius.end());
793 impl_->nb_.setCutoff(2 * maxRadius);
797 void SurfaceAreaCalculator::setCalculateVolume(bool bVolume)
801 impl_->flags_ |= FLAG_VOLUME;
805 impl_->flags_ &= ~FLAG_VOLUME;
809 void SurfaceAreaCalculator::setCalculateAtomArea(bool bAtomArea)
813 impl_->flags_ |= FLAG_ATOM_AREA;
817 impl_->flags_ &= ~FLAG_ATOM_AREA;
821 void SurfaceAreaCalculator::setCalculateSurfaceDots(bool bDots)
825 impl_->flags_ |= FLAG_DOTS;
829 impl_->flags_ &= ~FLAG_DOTS;
833 void SurfaceAreaCalculator::calculate(const rvec* x,
844 flags |= impl_->flags_;
846 if (volume == nullptr)
848 flags &= ~FLAG_VOLUME;
854 if (at_area == nullptr)
856 flags &= ~FLAG_ATOM_AREA;
862 if (lidots == nullptr)
870 if (n_dots == nullptr)
878 nsc_dclm_pbc(x, impl_->radius_, nat, &impl_->unitSphereDots_[0], impl_->unitSphereDots_.size() / 3,
879 flags, area, at_area, volume, lidots, n_dots, index, &impl_->nb_, pbc);