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, led by
7 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8 * and including many others, as listed in the AUTHORS file in the
9 * top-level source directory and at http://www.gromacs.org.
11 * GROMACS is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public License
13 * as published by the Free Software Foundation; either version 2.1
14 * of the License, or (at your option) any later version.
16 * GROMACS is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with GROMACS; if not, see
23 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 * If you want to redistribute modifications to GROMACS, please
27 * consider that scientific software is very special. Version
28 * control is crucial - bugs must be traceable. We will be happy to
29 * consider code for inclusion in the official distribution, but
30 * derived work must not be called official GROMACS. Details are found
31 * in the README & COPYING files - if they are missing, get the
32 * official version at http://www.gromacs.org.
34 * To help us fund GROMACS development, we humbly ask that you cite
35 * the research papers on the package. Check out http://www.gromacs.org.
39 #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)
70 if ( (fabs(f) < 1.00) )
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 */
84 xus[ 0] = 0.; xus[ 1] = 0.; xus[ 2] = 1.;
85 xus[ 3] = rh*cos(TORAD(72.)); xus[ 4] = rh*sin(TORAD(72.)); xus[ 5] = rg;
86 xus[ 6] = rh*cos(TORAD(144.)); xus[ 7] = rh*sin(TORAD(144.)); xus[ 8] = rg;
87 xus[ 9] = rh*cos(TORAD(216.)); xus[10] = rh*sin(TORAD(216.)); xus[11] = rg;
88 xus[12] = rh*cos(TORAD(288.)); xus[13] = rh*sin(TORAD(288.)); xus[14] = rg;
89 xus[15] = rh; xus[16] = 0; xus[17] = rg;
90 xus[18] = rh*cos(TORAD(36.)); xus[19] = rh*sin(TORAD(36.)); xus[20] = -rg;
91 xus[21] = rh*cos(TORAD(108.)); xus[22] = rh*sin(TORAD(108.)); xus[23] = -rg;
92 xus[24] = -rh; xus[25] = 0; xus[26] = -rg;
93 xus[27] = rh*cos(TORAD(252.)); xus[28] = rh*sin(TORAD(252.)); xus[29] = -rg;
94 xus[30] = rh*cos(TORAD(324.)); xus[31] = rh*sin(TORAD(324.)); xus[32] = -rg;
95 xus[33] = 0.; xus[34] = 0.; xus[35] = -1.;
100 static void divarc(real x1, real y1, real z1,
101 real x2, real y2, real z2,
102 int div1, int div2, real *xr, real *yr, real *zr)
105 real xd, yd, zd, dd, d1, d2, s, x, y, z;
106 real phi, sphi, cphi;
111 dd = std::sqrt(xd*xd+yd*yd+zd*zd);
112 GMX_ASSERT(dd >= DP_TOL, "Rotation axis vector too short");
114 d1 = x1*x1+y1*y1+z1*z1;
115 d2 = x2*x2+y2*y2+z2*z2;
116 GMX_ASSERT(d1 >= 0.5, "Vector 1 too short");
117 GMX_ASSERT(d2 >= 0.5, "Vector 2 too short");
119 phi = safe_asin(dd/std::sqrt(d1*d2));
120 phi = phi*(static_cast<real>(div1))/(static_cast<real>(div2));
121 sphi = sin(phi); cphi = cos(phi);
122 s = (x1*xd+y1*yd+z1*zd)/dd;
124 x = xd*s*(1.-cphi)/dd + x1 * cphi + (yd*z1-y1*zd)*sphi/dd;
125 y = yd*s*(1.-cphi)/dd + y1 * cphi + (zd*x1-z1*xd)*sphi/dd;
126 z = zd*s*(1.-cphi)/dd + z1 * cphi + (xd*y1-x1*yd)*sphi/dd;
127 dd = std::sqrt(x*x+y*y+z*z);
128 *xr = x/dd; *yr = y/dd; *zr = z/dd;
131 /* densit...required dots per unit sphere */
132 static std::vector<real> ico_dot_arc(int densit)
134 /* dot distribution on a unit sphere based on an icosaeder *
135 * great circle average refining of icosahedral face */
137 int i, j, k, tl, tl2, tn;
138 real a, d, x, y, z, x2, y2, z2, x3, y3, z3;
139 real xij, yij, zij, xji, yji, zji, xik, yik, zik, xki, yki, zki,
140 xjk, yjk, zjk, xkj, ykj, zkj;
142 /* calculate tessalation level */
143 a = std::sqrt(((static_cast<real>(densit))-2.)/10.);
144 const int tess = static_cast<int>(ceil(a));
145 const int ndot = 10*tess*tess+2;
146 GMX_RELEASE_ASSERT(ndot >= densit, "Inconsistent surface dot formula");
148 std::vector<real> xus(3*ndot);
149 const real rh = icosaeder_vertices(xus.data());
154 a = rh*rh*2.*(1.-cos(TORAD(72.)));
155 /* calculate tessalation of icosaeder edges */
156 for (i = 0; i < 11; i++)
158 for (j = i+1; j < 12; j++)
160 x = xus[3*i]-xus[3*j];
161 y = xus[1+3*i]-xus[1+3*j]; z = xus[2+3*i]-xus[2+3*j];
163 if (std::fabs(a-d) > DP_TOL)
167 for (tl = 1; tl < tess; tl++)
169 GMX_ASSERT(tn < ndot, "Inconsistent precomputed surface dot count");
170 divarc(xus[3*i], xus[1+3*i], xus[2+3*i],
171 xus[3*j], xus[1+3*j], xus[2+3*j],
172 tl, tess, &xus[3*tn], &xus[1+3*tn], &xus[2+3*tn]);
177 /* calculate tessalation of icosaeder faces */
178 for (i = 0; i < 10; i++)
180 for (j = i+1; j < 11; j++)
182 x = xus[3*i]-xus[3*j];
183 y = xus[1+3*i]-xus[1+3*j]; z = xus[2+3*i]-xus[2+3*j];
185 if (std::fabs(a-d) > DP_TOL)
190 for (k = j+1; k < 12; k++)
192 x = xus[3*i]-xus[3*k];
193 y = xus[1+3*i]-xus[1+3*k]; z = xus[2+3*i]-xus[2+3*k];
195 if (std::fabs(a-d) > DP_TOL)
199 x = xus[3*j]-xus[3*k];
200 y = xus[1+3*j]-xus[1+3*k]; z = xus[2+3*j]-xus[2+3*k];
202 if (std::fabs(a-d) > DP_TOL)
206 for (tl = 1; tl < tess-1; tl++)
208 divarc(xus[3*j], xus[1+3*j], xus[2+3*j],
209 xus[3*i], xus[1+3*i], xus[2+3*i],
210 tl, tess, &xji, &yji, &zji);
211 divarc(xus[3*k], xus[1+3*k], xus[2+3*k],
212 xus[3*i], xus[1+3*i], xus[2+3*i],
213 tl, tess, &xki, &yki, &zki);
215 for (tl2 = 1; tl2 < tess-tl; tl2++)
217 divarc(xus[3*i], xus[1+3*i], xus[2+3*i],
218 xus[3*j], xus[1+3*j], xus[2+3*j],
219 tl2, tess, &xij, &yij, &zij);
220 divarc(xus[3*k], xus[1+3*k], xus[2+3*k],
221 xus[3*j], xus[1+3*j], xus[2+3*j],
222 tl2, tess, &xkj, &ykj, &zkj);
223 divarc(xus[3*i], xus[1+3*i], xus[2+3*i],
224 xus[3*k], xus[1+3*k], xus[2+3*k],
225 tess-tl-tl2, tess, &xik, &yik, &zik);
226 divarc(xus[3*j], xus[1+3*j], xus[2+3*j],
227 xus[3*k], xus[1+3*k], xus[2+3*k],
228 tess-tl-tl2, tess, &xjk, &yjk, &zjk);
229 divarc(xki, yki, zki, xji, yji, zji, tl2, tess-tl,
231 divarc(xkj, ykj, zkj, xij, yij, zij, tl, tess-tl2,
233 divarc(xjk, yjk, zjk, xik, yik, zik, tl, tl+tl2,
235 GMX_ASSERT(tn < ndot,
236 "Inconsistent precomputed surface dot count");
237 x = x+x2+x3; y = y+y2+y3; z = z+z2+z3;
238 d = std::sqrt(x*x+y*y+z*z);
248 GMX_ASSERT(tn == ndot, "Inconsistent precomputed surface dot count");
249 } /* end of if (tess > 1) */
252 } /* end of routine ico_dot_arc */
254 /* densit...required dots per unit sphere */
255 static std::vector<real> ico_dot_dod(int densit)
257 /* dot distribution on a unit sphere based on an icosaeder *
258 * great circle average refining of icosahedral face */
260 int i, j, k, tl, tl2, tn, tess, j1, j2;
261 real a, d, x, y, z, x2, y2, z2, x3, y3, z3, ai_d, adod;
262 real xij, yij, zij, xji, yji, zji, xik, yik, zik, xki, yki, zki,
263 xjk, yjk, zjk, xkj, ykj, zkj;
265 /* calculate tesselation level */
266 a = std::sqrt(((static_cast<real>(densit))-2.)/30.);
267 tess = std::max(static_cast<int>(ceil(a)), 1);
268 const int ndot = 30*tess*tess+2;
269 GMX_RELEASE_ASSERT(ndot >= densit, "Inconsistent surface dot formula");
271 std::vector<real> xus(3*ndot);
272 const real rh = icosaeder_vertices(xus.data());
275 /* square of the edge of an icosaeder */
276 a = rh*rh*2.*(1.-cos(TORAD(72.)));
277 /* dodecaeder vertices */
278 for (i = 0; i < 10; i++)
280 for (j = i+1; j < 11; j++)
282 x = xus[3*i]-xus[3*j];
283 y = xus[1+3*i]-xus[1+3*j]; z = xus[2+3*i]-xus[2+3*j];
285 if (std::fabs(a-d) > DP_TOL)
289 for (k = j+1; k < 12; k++)
291 x = xus[3*i]-xus[3*k];
292 y = xus[1+3*i]-xus[1+3*k]; z = xus[2+3*i]-xus[2+3*k];
294 if (std::fabs(a-d) > DP_TOL)
298 x = xus[3*j]-xus[3*k];
299 y = xus[1+3*j]-xus[1+3*k]; z = xus[2+3*j]-xus[2+3*k];
301 if (std::fabs(a-d) > DP_TOL)
305 x = xus[ 3*i]+xus[ 3*j]+xus[ 3*k];
306 y = xus[1+3*i]+xus[1+3*j]+xus[1+3*k];
307 z = xus[2+3*i]+xus[2+3*j]+xus[2+3*k];
308 d = std::sqrt(x*x+y*y+z*z);
309 xus[3*tn] = x/d; xus[1+3*tn] = y/d; xus[2+3*tn] = z/d;
318 /* square of the edge of an dodecaeder */
319 adod = 4.*(cos(TORAD(108.))-cos(TORAD(120.)))/(1.-cos(TORAD(120.)));
320 /* square of the distance of two adjacent vertices of ico- and dodecaeder */
321 ai_d = 2.*(1.-std::sqrt(1.-a/3.));
323 /* calculate tessalation of mixed edges */
324 for (i = 0; i < 31; i++)
326 j1 = 12; j2 = 32; a = ai_d;
331 for (j = j1; j < j2; j++)
333 x = xus[3*i]-xus[3*j];
334 y = xus[1+3*i]-xus[1+3*j]; z = xus[2+3*i]-xus[2+3*j];
336 if (std::fabs(a-d) > DP_TOL)
340 for (tl = 1; tl < tess; tl++)
342 GMX_ASSERT(tn < ndot,
343 "Inconsistent precomputed surface dot count");
344 divarc(xus[3*i], xus[1+3*i], xus[2+3*i],
345 xus[3*j], xus[1+3*j], xus[2+3*j],
346 tl, tess, &xus[3*tn], &xus[1+3*tn], &xus[2+3*tn]);
351 /* calculate tessalation of pentakisdodecahedron faces */
352 for (i = 0; i < 12; i++)
354 for (j = 12; j < 31; j++)
356 x = xus[3*i]-xus[3*j];
357 y = xus[1+3*i]-xus[1+3*j]; z = xus[2+3*i]-xus[2+3*j];
359 if (std::fabs(ai_d-d) > DP_TOL)
364 for (k = j+1; k < 32; k++)
366 x = xus[3*i]-xus[3*k];
367 y = xus[1+3*i]-xus[1+3*k]; z = xus[2+3*i]-xus[2+3*k];
369 if (std::fabs(ai_d-d) > DP_TOL)
373 x = xus[3*j]-xus[3*k];
374 y = xus[1+3*j]-xus[1+3*k]; z = xus[2+3*j]-xus[2+3*k];
376 if (std::fabs(adod-d) > DP_TOL)
380 for (tl = 1; tl < tess-1; tl++)
382 divarc(xus[3*j], xus[1+3*j], xus[2+3*j],
383 xus[3*i], xus[1+3*i], xus[2+3*i],
384 tl, tess, &xji, &yji, &zji);
385 divarc(xus[3*k], xus[1+3*k], xus[2+3*k],
386 xus[3*i], xus[1+3*i], xus[2+3*i],
387 tl, tess, &xki, &yki, &zki);
389 for (tl2 = 1; tl2 < tess-tl; tl2++)
391 divarc(xus[3*i], xus[1+3*i], xus[2+3*i],
392 xus[3*j], xus[1+3*j], xus[2+3*j],
393 tl2, tess, &xij, &yij, &zij);
394 divarc(xus[3*k], xus[1+3*k], xus[2+3*k],
395 xus[3*j], xus[1+3*j], xus[2+3*j],
396 tl2, tess, &xkj, &ykj, &zkj);
397 divarc(xus[3*i], xus[1+3*i], xus[2+3*i],
398 xus[3*k], xus[1+3*k], xus[2+3*k],
399 tess-tl-tl2, tess, &xik, &yik, &zik);
400 divarc(xus[3*j], xus[1+3*j], xus[2+3*j],
401 xus[3*k], xus[1+3*k], xus[2+3*k],
402 tess-tl-tl2, tess, &xjk, &yjk, &zjk);
403 divarc(xki, yki, zki, xji, yji, zji, tl2, tess-tl,
405 divarc(xkj, ykj, zkj, xij, yij, zij, tl, tess-tl2,
407 divarc(xjk, yjk, zjk, xik, yik, zik, tl, tl+tl2,
409 GMX_ASSERT(tn < ndot,
410 "Inconsistent precomputed surface dot count");
411 x = x+x2+x3; y = y+y2+y3; z = z+z2+z3;
412 d = std::sqrt(x*x+y*y+z*z);
422 GMX_ASSERT(tn == ndot, "Inconsistent precomputed surface dot count");
423 } /* end of if (tess > 1) */
426 } /* end of routine ico_dot_dod */
428 static int unsp_type(int densit)
432 while (10*i1*i1+2 < densit)
437 while (30*i2*i2+2 < densit)
441 if (10*i1*i1-2 < 30*i2*i2-2)
451 static std::vector<real> make_unsp(int densit, int cubus)
453 int *ico_wk, *ico_pt;
454 int ico_cube, ico_cube_cb, i, j, k, l, ijk, tn, tl, tl2;
458 int mode = unsp_type(densit);
459 std::vector<real> xus;
460 if (mode == UNSP_ICO_ARC)
462 xus = ico_dot_arc(densit);
464 else if (mode == UNSP_ICO_DOD)
466 xus = ico_dot_dod(densit);
470 GMX_RELEASE_ASSERT(false, "Invalid unit sphere mode");
473 const int ndot = static_cast<int>(xus.size())/3;
475 /* determine distribution of points in elementary cubes */
483 while (i*i*i*2 < ndot)
487 ico_cube = std::max(i-1, 0);
489 ico_cube_cb = ico_cube*ico_cube*ico_cube;
490 const real del_cube = 2./(static_cast<real>(ico_cube));
492 for (l = 0; l < ndot; l++)
494 i = std::max(static_cast<int>(floor((1.+xus[3*l])/del_cube)), 0);
499 j = std::max(static_cast<int>(floor((1.+xus[1+3*l])/del_cube)), 0);
504 k = std::max(static_cast<int>(floor((1.+xus[2+3*l])/del_cube)), 0);
509 ijk = i+j*ico_cube+k*ico_cube*ico_cube;
513 snew(ico_wk, 2*ico_cube_cb+1);
515 ico_pt = ico_wk+ico_cube_cb;
516 for (l = 0; l < ndot; l++)
518 ico_wk[work[l]]++; /* dots per elementary cube */
521 /* reordering of the coordinate array in accordance with box number */
523 for (i = 0; i < ico_cube; i++)
525 for (j = 0; j < ico_cube; j++)
527 for (k = 0; k < ico_cube; k++)
531 ijk = i+ico_cube*j+ico_cube*ico_cube*k;
533 for (l = tl2; l < ndot; l++)
537 x = xus[3*l]; y = xus[1+3*l]; z = xus[2+3*l];
538 xus[3*l] = xus[3*tn];
539 xus[1+3*l] = xus[1+3*tn]; xus[2+3*l] = xus[2+3*tn];
540 xus[3*tn] = x; xus[1+3*tn] = y; xus[2+3*tn] = z;
541 ijk = work[l]; work[l] = work[tn]; work[tn] = ijk;
556 nsc_dclm_pbc(const rvec *coords, const ArrayRef<const real> &radius, int nat,
557 const real *xus, int n_dot, int mode,
558 real *value_of_area, real **at_area,
560 real **lidots, int *nu_dots,
561 int index[], AnalysisNeighborhood *nb,
564 const real dotarea = FOURPI/static_cast<real>(n_dot);
568 fprintf(debug, "nsc_dclm: n_dot=%5d %9.3f\n", n_dot, dotarea);
571 /* start with neighbour list */
572 /* calculate neighbour list with the box algorithm */
577 real area = 0.0, vol = 0.0;
578 real *dots = nullptr, *atom_area = nullptr;
579 int lfnr = 0, maxdots = 0;
580 if (mode & FLAG_VOLUME)
584 if (mode & FLAG_DOTS)
586 maxdots = (3*n_dot*nat)/10;
590 if (mode & FLAG_ATOM_AREA)
592 snew(atom_area, nat);
595 // Compute the center of the molecule for volume calculation.
596 // In principle, the center should not influence the results, but that is
597 // only true at the limit of infinite dot density, so this makes the
598 // results translation-invariant.
599 // With PBC, if the molecule is broken across the boundary, the computation
600 // is broken in other ways as well, so it does not need to be considered
602 real xs = 0.0, ys = 0.0, zs = 0.0;
603 for (int i = 0; i < nat; ++i)
605 const int iat = index[i];
606 xs += coords[iat][XX];
607 ys += coords[iat][YY];
608 zs += coords[iat][ZZ];
614 AnalysisNeighborhoodPositions pos(coords, radius.size());
615 pos.indexed(constArrayRefFromArray(index, nat));
616 AnalysisNeighborhoodSearch nbsearch(nb->initSearch(pbc, pos));
618 std::vector<int> wkdot(n_dot);
620 for (int i = 0; i < nat; ++i)
622 const int iat = index[i];
623 const real ai = radius[iat];
624 const real aisq = ai*ai;
625 AnalysisNeighborhoodPairSearch pairSearch(
626 nbsearch.startPairSearch(coords[iat]));
627 AnalysisNeighborhoodPair pair;
628 std::fill(wkdot.begin(), wkdot.end(), 1);
629 int currDotCount = n_dot;
630 while (currDotCount > 0 && pairSearch.findNextPair(&pair))
632 const int jat = index[pair.refIndex()];
633 const real aj = radius[jat];
634 const real d2 = pair.distance2();
635 if (iat == jat || d2 > gmx::square(ai+aj))
639 const rvec &dx = pair.dx();
640 const real refdot = (d2 + aisq - aj*aj)/(2*ai);
641 // TODO: Consider whether micro-optimizations from the old
642 // implementation would be useful, compared to the complexity that
643 // they bring: instead of this direct loop, the neighbors were
644 // stored into a temporary array, the loop order was
645 // reversed (first over dots, then over neighbors), and for each
646 // dot, it was first checked whether the same neighbor that
647 // resulted in marking the previous dot covered would also cover
648 // this dot. This presumably plays together with sorting of the
649 // surface dots (done in make_unsp) to avoid some of the looping.
650 // Alternatively, we could keep a skip list here to avoid
651 // repeatedly looping over dots that have already marked as
653 for (int j = 0; j < n_dot; ++j)
655 if (wkdot[j] && iprod(&xus[3*j], dx) > refdot)
663 const real a = aisq * dotarea * currDotCount;
665 if (mode & FLAG_ATOM_AREA)
669 const real xi = coords[iat][XX];
670 const real yi = coords[iat][YY];
671 const real zi = coords[iat][ZZ];
672 if (mode & FLAG_DOTS)
674 for (int l = 0; l < n_dot; l++)
679 if (maxdots <= 3*lfnr+1)
681 maxdots = maxdots+n_dot*3;
682 srenew(dots, maxdots);
684 dots[3*lfnr-3] = ai*xus[3*l]+xi;
685 dots[3*lfnr-2] = ai*xus[1+3*l]+yi;
686 dots[3*lfnr-1] = ai*xus[2+3*l]+zi;
690 if (mode & FLAG_VOLUME)
692 real dx = 0.0, dy = 0.0, dz = 0.0;
693 for (int l = 0; l < n_dot; l++)
702 vol = vol+aisq*(dx*(xi-xs)+dy*(yi-ys)+dz*(zi-zs) + ai*currDotCount);
706 if (mode & FLAG_VOLUME)
708 *value_of_vol = vol*FOURPI/(3.*n_dot);
710 if (mode & FLAG_DOTS)
715 if (mode & FLAG_ATOM_AREA)
717 *at_area = atom_area;
719 *value_of_area = area;
723 fprintf(debug, "area=%8.3f\n", area);
730 class SurfaceAreaCalculator::Impl
737 std::vector<real> unitSphereDots_;
738 ArrayRef<const real> radius_;
740 mutable AnalysisNeighborhood nb_;
743 SurfaceAreaCalculator::SurfaceAreaCalculator()
748 SurfaceAreaCalculator::~SurfaceAreaCalculator()
752 void SurfaceAreaCalculator::setDotCount(int dotCount)
754 impl_->unitSphereDots_ = make_unsp(dotCount, 4);
757 void SurfaceAreaCalculator::setRadii(const ArrayRef<const real> &radius)
759 impl_->radius_ = radius;
762 const real maxRadius = *std::max_element(radius.begin(), radius.end());
763 impl_->nb_.setCutoff(2*maxRadius);
767 void SurfaceAreaCalculator::setCalculateVolume(bool bVolume)
771 impl_->flags_ |= FLAG_VOLUME;
775 impl_->flags_ &= ~FLAG_VOLUME;
779 void SurfaceAreaCalculator::setCalculateAtomArea(bool bAtomArea)
783 impl_->flags_ |= FLAG_ATOM_AREA;
787 impl_->flags_ &= ~FLAG_ATOM_AREA;
791 void SurfaceAreaCalculator::setCalculateSurfaceDots(bool bDots)
795 impl_->flags_ |= FLAG_DOTS;
799 impl_->flags_ &= ~FLAG_DOTS;
803 void SurfaceAreaCalculator::calculate(
804 const rvec *x, const t_pbc *pbc,
805 int nat, int index[], int flags, real *area, real *volume,
806 real **at_area, real **lidots, int *n_dots) const
808 flags |= impl_->flags_;
810 if (volume == nullptr)
812 flags &= ~FLAG_VOLUME;
818 if (at_area == nullptr)
820 flags &= ~FLAG_ATOM_AREA;
826 if (lidots == nullptr)
834 if (n_dots == nullptr)
842 nsc_dclm_pbc(x, impl_->radius_, nat,
843 &impl_->unitSphereDots_[0], impl_->unitSphereDots_.size()/3,
844 flags, area, at_area, volume, lidots, n_dots, index,