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"
49 #include "gromacs/math/functions.h"
50 #include "gromacs/math/utilities.h"
51 #include "gromacs/math/vec.h"
52 #include "gromacs/pbcutil/pbc.h"
53 #include "gromacs/selection/nbsearch.h"
54 #include "gromacs/utility/fatalerror.h"
55 #include "gromacs/utility/gmxassert.h"
56 #include "gromacs/utility/smalloc.h"
60 #define UNSP_ICO_DOD 9
61 #define UNSP_ICO_ARC 10
63 #define FOURPI (4.*M_PI)
64 #define TORAD(A) ((A)*0.017453293)
67 static real
safe_asin(real f
)
69 if ( (fabs(f
) < 1.00) )
73 GMX_ASSERT(fabs(f
) - 1.0 > DP_TOL
, "Invalid argument");
77 /* routines for dot distributions on the surface of the unit sphere */
78 static real
icosaeder_vertices(real
*xus
)
80 const real rh
= std::sqrt(1.-2.*cos(TORAD(72.)))/(1.-cos(TORAD(72.)));
81 const real rg
= cos(TORAD(72.))/(1.-cos(TORAD(72.)));
82 /* icosaeder vertices */
83 xus
[ 0] = 0.; xus
[ 1] = 0.; xus
[ 2] = 1.;
84 xus
[ 3] = rh
*cos(TORAD(72.)); xus
[ 4] = rh
*sin(TORAD(72.)); xus
[ 5] = rg
;
85 xus
[ 6] = rh
*cos(TORAD(144.)); xus
[ 7] = rh
*sin(TORAD(144.)); xus
[ 8] = rg
;
86 xus
[ 9] = rh
*cos(TORAD(216.)); xus
[10] = rh
*sin(TORAD(216.)); xus
[11] = rg
;
87 xus
[12] = rh
*cos(TORAD(288.)); xus
[13] = rh
*sin(TORAD(288.)); xus
[14] = rg
;
88 xus
[15] = rh
; xus
[16] = 0; xus
[17] = rg
;
89 xus
[18] = rh
*cos(TORAD(36.)); xus
[19] = rh
*sin(TORAD(36.)); xus
[20] = -rg
;
90 xus
[21] = rh
*cos(TORAD(108.)); xus
[22] = rh
*sin(TORAD(108.)); xus
[23] = -rg
;
91 xus
[24] = -rh
; xus
[25] = 0; xus
[26] = -rg
;
92 xus
[27] = rh
*cos(TORAD(252.)); xus
[28] = rh
*sin(TORAD(252.)); xus
[29] = -rg
;
93 xus
[30] = rh
*cos(TORAD(324.)); xus
[31] = rh
*sin(TORAD(324.)); xus
[32] = -rg
;
94 xus
[33] = 0.; xus
[34] = 0.; xus
[35] = -1.;
99 static void divarc(real x1
, real y1
, real z1
,
100 real x2
, real y2
, real z2
,
101 int div1
, int div2
, real
*xr
, real
*yr
, real
*zr
)
104 real xd
, yd
, zd
, dd
, d1
, d2
, s
, x
, y
, z
;
105 real phi
, sphi
, cphi
;
110 dd
= std::sqrt(xd
*xd
+yd
*yd
+zd
*zd
);
111 GMX_ASSERT(dd
>= DP_TOL
, "Rotation axis vector too short");
113 d1
= x1
*x1
+y1
*y1
+z1
*z1
;
114 d2
= x2
*x2
+y2
*y2
+z2
*z2
;
115 GMX_ASSERT(d1
>= 0.5, "Vector 1 too short");
116 GMX_ASSERT(d2
>= 0.5, "Vector 2 too short");
118 phi
= safe_asin(dd
/std::sqrt(d1
*d2
));
119 phi
= phi
*(static_cast<real
>(div1
))/(static_cast<real
>(div2
));
120 sphi
= sin(phi
); cphi
= cos(phi
);
121 s
= (x1
*xd
+y1
*yd
+z1
*zd
)/dd
;
123 x
= xd
*s
*(1.-cphi
)/dd
+ x1
* cphi
+ (yd
*z1
-y1
*zd
)*sphi
/dd
;
124 y
= yd
*s
*(1.-cphi
)/dd
+ y1
* cphi
+ (zd
*x1
-z1
*xd
)*sphi
/dd
;
125 z
= zd
*s
*(1.-cphi
)/dd
+ z1
* cphi
+ (xd
*y1
-x1
*yd
)*sphi
/dd
;
126 dd
= std::sqrt(x
*x
+y
*y
+z
*z
);
127 *xr
= x
/dd
; *yr
= y
/dd
; *zr
= z
/dd
;
130 /* densit...required dots per unit sphere */
131 static std::vector
<real
> ico_dot_arc(int densit
)
133 /* dot distribution on a unit sphere based on an icosaeder *
134 * great circle average refining of icosahedral face */
136 int i
, j
, k
, tl
, tl2
, tn
;
137 real a
, d
, x
, y
, z
, x2
, y2
, z2
, x3
, y3
, z3
;
138 real xij
, yij
, zij
, xji
, yji
, zji
, xik
, yik
, zik
, xki
, yki
, zki
,
139 xjk
, yjk
, zjk
, xkj
, ykj
, zkj
;
141 /* calculate tessalation level */
142 a
= std::sqrt(((static_cast<real
>(densit
))-2.)/10.);
143 const int tess
= static_cast<int>(ceil(a
));
144 const int ndot
= 10*tess
*tess
+2;
145 GMX_RELEASE_ASSERT(ndot
>= densit
, "Inconsistent surface dot formula");
147 std::vector
<real
> xus(3*ndot
);
148 const real rh
= icosaeder_vertices(xus
.data());
153 a
= rh
*rh
*2.*(1.-cos(TORAD(72.)));
154 /* calculate tessalation of icosaeder edges */
155 for (i
= 0; i
< 11; i
++)
157 for (j
= i
+1; j
< 12; j
++)
159 x
= xus
[3*i
]-xus
[3*j
];
160 y
= xus
[1+3*i
]-xus
[1+3*j
]; z
= xus
[2+3*i
]-xus
[2+3*j
];
162 if (std::fabs(a
-d
) > DP_TOL
)
166 for (tl
= 1; tl
< tess
; tl
++)
168 GMX_ASSERT(tn
< ndot
, "Inconsistent precomputed surface dot count");
169 divarc(xus
[3*i
], xus
[1+3*i
], xus
[2+3*i
],
170 xus
[3*j
], xus
[1+3*j
], xus
[2+3*j
],
171 tl
, tess
, &xus
[3*tn
], &xus
[1+3*tn
], &xus
[2+3*tn
]);
176 /* calculate tessalation of icosaeder faces */
177 for (i
= 0; i
< 10; i
++)
179 for (j
= i
+1; j
< 11; j
++)
181 x
= xus
[3*i
]-xus
[3*j
];
182 y
= xus
[1+3*i
]-xus
[1+3*j
]; z
= xus
[2+3*i
]-xus
[2+3*j
];
184 if (std::fabs(a
-d
) > DP_TOL
)
189 for (k
= j
+1; k
< 12; k
++)
191 x
= xus
[3*i
]-xus
[3*k
];
192 y
= xus
[1+3*i
]-xus
[1+3*k
]; z
= xus
[2+3*i
]-xus
[2+3*k
];
194 if (std::fabs(a
-d
) > DP_TOL
)
198 x
= xus
[3*j
]-xus
[3*k
];
199 y
= xus
[1+3*j
]-xus
[1+3*k
]; z
= xus
[2+3*j
]-xus
[2+3*k
];
201 if (std::fabs(a
-d
) > DP_TOL
)
205 for (tl
= 1; tl
< tess
-1; tl
++)
207 divarc(xus
[3*j
], xus
[1+3*j
], xus
[2+3*j
],
208 xus
[3*i
], xus
[1+3*i
], xus
[2+3*i
],
209 tl
, tess
, &xji
, &yji
, &zji
);
210 divarc(xus
[3*k
], xus
[1+3*k
], xus
[2+3*k
],
211 xus
[3*i
], xus
[1+3*i
], xus
[2+3*i
],
212 tl
, tess
, &xki
, &yki
, &zki
);
214 for (tl2
= 1; tl2
< tess
-tl
; tl2
++)
216 divarc(xus
[3*i
], xus
[1+3*i
], xus
[2+3*i
],
217 xus
[3*j
], xus
[1+3*j
], xus
[2+3*j
],
218 tl2
, tess
, &xij
, &yij
, &zij
);
219 divarc(xus
[3*k
], xus
[1+3*k
], xus
[2+3*k
],
220 xus
[3*j
], xus
[1+3*j
], xus
[2+3*j
],
221 tl2
, tess
, &xkj
, &ykj
, &zkj
);
222 divarc(xus
[3*i
], xus
[1+3*i
], xus
[2+3*i
],
223 xus
[3*k
], xus
[1+3*k
], xus
[2+3*k
],
224 tess
-tl
-tl2
, tess
, &xik
, &yik
, &zik
);
225 divarc(xus
[3*j
], xus
[1+3*j
], xus
[2+3*j
],
226 xus
[3*k
], xus
[1+3*k
], xus
[2+3*k
],
227 tess
-tl
-tl2
, tess
, &xjk
, &yjk
, &zjk
);
228 divarc(xki
, yki
, zki
, xji
, yji
, zji
, tl2
, tess
-tl
,
230 divarc(xkj
, ykj
, zkj
, xij
, yij
, zij
, tl
, tess
-tl2
,
232 divarc(xjk
, yjk
, zjk
, xik
, yik
, zik
, tl
, tl
+tl2
,
234 GMX_ASSERT(tn
< ndot
,
235 "Inconsistent precomputed surface dot count");
236 x
= x
+x2
+x3
; y
= y
+y2
+y3
; z
= z
+z2
+z3
;
237 d
= std::sqrt(x
*x
+y
*y
+z
*z
);
247 GMX_ASSERT(tn
== ndot
, "Inconsistent precomputed surface dot count");
248 } /* end of if (tess > 1) */
251 } /* end of routine ico_dot_arc */
253 /* densit...required dots per unit sphere */
254 static std::vector
<real
> ico_dot_dod(int densit
)
256 /* dot distribution on a unit sphere based on an icosaeder *
257 * great circle average refining of icosahedral face */
259 int i
, j
, k
, tl
, tl2
, tn
, tess
, j1
, j2
;
260 real a
, d
, x
, y
, z
, x2
, y2
, z2
, x3
, y3
, z3
, ai_d
, adod
;
261 real xij
, yij
, zij
, xji
, yji
, zji
, xik
, yik
, zik
, xki
, yki
, zki
,
262 xjk
, yjk
, zjk
, xkj
, ykj
, zkj
;
264 /* calculate tesselation level */
265 a
= std::sqrt(((static_cast<real
>(densit
))-2.)/30.);
266 tess
= std::max(static_cast<int>(ceil(a
)), 1);
267 const int ndot
= 30*tess
*tess
+2;
268 GMX_RELEASE_ASSERT(ndot
>= densit
, "Inconsistent surface dot formula");
270 std::vector
<real
> xus(3*ndot
);
271 const real rh
= icosaeder_vertices(xus
.data());
274 /* square of the edge of an icosaeder */
275 a
= rh
*rh
*2.*(1.-cos(TORAD(72.)));
276 /* dodecaeder vertices */
277 for (i
= 0; i
< 10; i
++)
279 for (j
= i
+1; j
< 11; j
++)
281 x
= xus
[3*i
]-xus
[3*j
];
282 y
= xus
[1+3*i
]-xus
[1+3*j
]; z
= xus
[2+3*i
]-xus
[2+3*j
];
284 if (std::fabs(a
-d
) > DP_TOL
)
288 for (k
= j
+1; k
< 12; k
++)
290 x
= xus
[3*i
]-xus
[3*k
];
291 y
= xus
[1+3*i
]-xus
[1+3*k
]; z
= xus
[2+3*i
]-xus
[2+3*k
];
293 if (std::fabs(a
-d
) > DP_TOL
)
297 x
= xus
[3*j
]-xus
[3*k
];
298 y
= xus
[1+3*j
]-xus
[1+3*k
]; z
= xus
[2+3*j
]-xus
[2+3*k
];
300 if (std::fabs(a
-d
) > DP_TOL
)
304 x
= xus
[ 3*i
]+xus
[ 3*j
]+xus
[ 3*k
];
305 y
= xus
[1+3*i
]+xus
[1+3*j
]+xus
[1+3*k
];
306 z
= xus
[2+3*i
]+xus
[2+3*j
]+xus
[2+3*k
];
307 d
= std::sqrt(x
*x
+y
*y
+z
*z
);
308 xus
[3*tn
] = x
/d
; xus
[1+3*tn
] = y
/d
; xus
[2+3*tn
] = z
/d
;
317 /* square of the edge of an dodecaeder */
318 adod
= 4.*(cos(TORAD(108.))-cos(TORAD(120.)))/(1.-cos(TORAD(120.)));
319 /* square of the distance of two adjacent vertices of ico- and dodecaeder */
320 ai_d
= 2.*(1.-std::sqrt(1.-a
/3.));
322 /* calculate tessalation of mixed edges */
323 for (i
= 0; i
< 31; i
++)
325 j1
= 12; j2
= 32; a
= ai_d
;
330 for (j
= j1
; j
< j2
; j
++)
332 x
= xus
[3*i
]-xus
[3*j
];
333 y
= xus
[1+3*i
]-xus
[1+3*j
]; z
= xus
[2+3*i
]-xus
[2+3*j
];
335 if (std::fabs(a
-d
) > DP_TOL
)
339 for (tl
= 1; tl
< tess
; tl
++)
341 GMX_ASSERT(tn
< ndot
,
342 "Inconsistent precomputed surface dot count");
343 divarc(xus
[3*i
], xus
[1+3*i
], xus
[2+3*i
],
344 xus
[3*j
], xus
[1+3*j
], xus
[2+3*j
],
345 tl
, tess
, &xus
[3*tn
], &xus
[1+3*tn
], &xus
[2+3*tn
]);
350 /* calculate tessalation of pentakisdodecahedron faces */
351 for (i
= 0; i
< 12; i
++)
353 for (j
= 12; j
< 31; j
++)
355 x
= xus
[3*i
]-xus
[3*j
];
356 y
= xus
[1+3*i
]-xus
[1+3*j
]; z
= xus
[2+3*i
]-xus
[2+3*j
];
358 if (std::fabs(ai_d
-d
) > DP_TOL
)
363 for (k
= j
+1; k
< 32; k
++)
365 x
= xus
[3*i
]-xus
[3*k
];
366 y
= xus
[1+3*i
]-xus
[1+3*k
]; z
= xus
[2+3*i
]-xus
[2+3*k
];
368 if (std::fabs(ai_d
-d
) > DP_TOL
)
372 x
= xus
[3*j
]-xus
[3*k
];
373 y
= xus
[1+3*j
]-xus
[1+3*k
]; z
= xus
[2+3*j
]-xus
[2+3*k
];
375 if (std::fabs(adod
-d
) > DP_TOL
)
379 for (tl
= 1; tl
< tess
-1; tl
++)
381 divarc(xus
[3*j
], xus
[1+3*j
], xus
[2+3*j
],
382 xus
[3*i
], xus
[1+3*i
], xus
[2+3*i
],
383 tl
, tess
, &xji
, &yji
, &zji
);
384 divarc(xus
[3*k
], xus
[1+3*k
], xus
[2+3*k
],
385 xus
[3*i
], xus
[1+3*i
], xus
[2+3*i
],
386 tl
, tess
, &xki
, &yki
, &zki
);
388 for (tl2
= 1; tl2
< tess
-tl
; tl2
++)
390 divarc(xus
[3*i
], xus
[1+3*i
], xus
[2+3*i
],
391 xus
[3*j
], xus
[1+3*j
], xus
[2+3*j
],
392 tl2
, tess
, &xij
, &yij
, &zij
);
393 divarc(xus
[3*k
], xus
[1+3*k
], xus
[2+3*k
],
394 xus
[3*j
], xus
[1+3*j
], xus
[2+3*j
],
395 tl2
, tess
, &xkj
, &ykj
, &zkj
);
396 divarc(xus
[3*i
], xus
[1+3*i
], xus
[2+3*i
],
397 xus
[3*k
], xus
[1+3*k
], xus
[2+3*k
],
398 tess
-tl
-tl2
, tess
, &xik
, &yik
, &zik
);
399 divarc(xus
[3*j
], xus
[1+3*j
], xus
[2+3*j
],
400 xus
[3*k
], xus
[1+3*k
], xus
[2+3*k
],
401 tess
-tl
-tl2
, tess
, &xjk
, &yjk
, &zjk
);
402 divarc(xki
, yki
, zki
, xji
, yji
, zji
, tl2
, tess
-tl
,
404 divarc(xkj
, ykj
, zkj
, xij
, yij
, zij
, tl
, tess
-tl2
,
406 divarc(xjk
, yjk
, zjk
, xik
, yik
, zik
, tl
, tl
+tl2
,
408 GMX_ASSERT(tn
< ndot
,
409 "Inconsistent precomputed surface dot count");
410 x
= x
+x2
+x3
; y
= y
+y2
+y3
; z
= z
+z2
+z3
;
411 d
= std::sqrt(x
*x
+y
*y
+z
*z
);
421 GMX_ASSERT(tn
== ndot
, "Inconsistent precomputed surface dot count");
422 } /* end of if (tess > 1) */
425 } /* end of routine ico_dot_dod */
427 static int unsp_type(int densit
)
431 while (10*i1
*i1
+2 < densit
)
436 while (30*i2
*i2
+2 < densit
)
440 if (10*i1
*i1
-2 < 30*i2
*i2
-2)
450 static std::vector
<real
> make_unsp(int densit
, int cubus
)
452 int *ico_wk
, *ico_pt
;
453 int ico_cube
, ico_cube_cb
, i
, j
, k
, l
, ijk
, tn
, tl
, tl2
;
457 int mode
= unsp_type(densit
);
458 std::vector
<real
> xus
;
459 if (mode
== UNSP_ICO_ARC
)
461 xus
= ico_dot_arc(densit
);
463 else if (mode
== UNSP_ICO_DOD
)
465 xus
= ico_dot_dod(densit
);
469 GMX_RELEASE_ASSERT(false, "Invalid unit sphere mode");
472 const int ndot
= static_cast<int>(xus
.size())/3;
474 /* determine distribution of points in elementary cubes */
482 while (i
*i
*i
*2 < ndot
)
486 ico_cube
= std::max(i
-1, 0);
488 ico_cube_cb
= ico_cube
*ico_cube
*ico_cube
;
489 const real del_cube
= 2./(static_cast<real
>(ico_cube
));
491 for (l
= 0; l
< ndot
; l
++)
493 i
= std::max(static_cast<int>(floor((1.+xus
[3*l
])/del_cube
)), 0);
498 j
= std::max(static_cast<int>(floor((1.+xus
[1+3*l
])/del_cube
)), 0);
503 k
= std::max(static_cast<int>(floor((1.+xus
[2+3*l
])/del_cube
)), 0);
508 ijk
= i
+j
*ico_cube
+k
*ico_cube
*ico_cube
;
512 snew(ico_wk
, 2*ico_cube_cb
+1);
514 ico_pt
= ico_wk
+ico_cube_cb
;
515 for (l
= 0; l
< ndot
; l
++)
517 ico_wk
[work
[l
]]++; /* dots per elementary cube */
520 /* reordering of the coordinate array in accordance with box number */
522 for (i
= 0; i
< ico_cube
; i
++)
524 for (j
= 0; j
< ico_cube
; j
++)
526 for (k
= 0; k
< ico_cube
; k
++)
530 ijk
= i
+ico_cube
*j
+ico_cube
*ico_cube
*k
;
532 for (l
= tl2
; l
< ndot
; l
++)
536 x
= xus
[3*l
]; y
= xus
[1+3*l
]; z
= xus
[2+3*l
];
537 xus
[3*l
] = xus
[3*tn
];
538 xus
[1+3*l
] = xus
[1+3*tn
]; xus
[2+3*l
] = xus
[2+3*tn
];
539 xus
[3*tn
] = x
; xus
[1+3*tn
] = y
; xus
[2+3*tn
] = z
;
540 ijk
= work
[l
]; work
[l
] = work
[tn
]; work
[tn
] = ijk
;
555 nsc_dclm_pbc(const rvec
*coords
, const ArrayRef
<const real
> &radius
, int nat
,
556 const real
*xus
, int n_dot
, int mode
,
557 real
*value_of_area
, real
**at_area
,
559 real
**lidots
, int *nu_dots
,
560 int index
[], AnalysisNeighborhood
*nb
,
563 const real dotarea
= FOURPI
/static_cast<real
>(n_dot
);
567 fprintf(debug
, "nsc_dclm: n_dot=%5d %9.3f\n", n_dot
, dotarea
);
570 /* start with neighbour list */
571 /* calculate neighbour list with the box algorithm */
576 real area
= 0.0, vol
= 0.0;
577 real
*dots
= nullptr, *atom_area
= nullptr;
578 int lfnr
= 0, maxdots
= 0;
579 if (mode
& FLAG_VOLUME
)
583 if (mode
& FLAG_DOTS
)
585 maxdots
= (3*n_dot
*nat
)/10;
589 if (mode
& FLAG_ATOM_AREA
)
591 snew(atom_area
, nat
);
594 // Compute the center of the molecule for volume calculation.
595 // In principle, the center should not influence the results, but that is
596 // only true at the limit of infinite dot density, so this makes the
597 // results translation-invariant.
598 // With PBC, if the molecule is broken across the boundary, the computation
599 // is broken in other ways as well, so it does not need to be considered
601 real xs
= 0.0, ys
= 0.0, zs
= 0.0;
602 for (int i
= 0; i
< nat
; ++i
)
604 const int iat
= index
[i
];
605 xs
+= coords
[iat
][XX
];
606 ys
+= coords
[iat
][YY
];
607 zs
+= coords
[iat
][ZZ
];
613 AnalysisNeighborhoodPositions
pos(coords
, radius
.size());
614 pos
.indexed(constArrayRefFromArray(index
, nat
));
615 AnalysisNeighborhoodSearch
nbsearch(nb
->initSearch(pbc
, pos
));
617 std::vector
<int> wkdot(n_dot
);
619 for (int i
= 0; i
< nat
; ++i
)
621 const int iat
= index
[i
];
622 const real ai
= radius
[iat
];
623 const real aisq
= ai
*ai
;
624 AnalysisNeighborhoodPairSearch
pairSearch(
625 nbsearch
.startPairSearch(coords
[iat
]));
626 AnalysisNeighborhoodPair pair
;
627 std::fill(wkdot
.begin(), wkdot
.end(), 1);
628 int currDotCount
= n_dot
;
629 while (currDotCount
> 0 && pairSearch
.findNextPair(&pair
))
631 const int jat
= index
[pair
.refIndex()];
632 const real aj
= radius
[jat
];
633 const real d2
= pair
.distance2();
634 if (iat
== jat
|| d2
> gmx::square(ai
+aj
))
638 const rvec
&dx
= pair
.dx();
639 const real refdot
= (d2
+ aisq
- aj
*aj
)/(2*ai
);
640 // TODO: Consider whether micro-optimizations from the old
641 // implementation would be useful, compared to the complexity that
642 // they bring: instead of this direct loop, the neighbors were
643 // stored into a temporary array, the loop order was
644 // reversed (first over dots, then over neighbors), and for each
645 // dot, it was first checked whether the same neighbor that
646 // resulted in marking the previous dot covered would also cover
647 // this dot. This presumably plays together with sorting of the
648 // surface dots (done in make_unsp) to avoid some of the looping.
649 // Alternatively, we could keep a skip list here to avoid
650 // repeatedly looping over dots that have already marked as
652 for (int j
= 0; j
< n_dot
; ++j
)
654 if (wkdot
[j
] && iprod(&xus
[3*j
], dx
) > refdot
)
662 const real a
= aisq
* dotarea
* currDotCount
;
664 if (mode
& FLAG_ATOM_AREA
)
668 const real xi
= coords
[iat
][XX
];
669 const real yi
= coords
[iat
][YY
];
670 const real zi
= coords
[iat
][ZZ
];
671 if (mode
& FLAG_DOTS
)
673 for (int l
= 0; l
< n_dot
; l
++)
678 if (maxdots
<= 3*lfnr
+1)
680 maxdots
= maxdots
+n_dot
*3;
681 srenew(dots
, maxdots
);
683 dots
[3*lfnr
-3] = ai
*xus
[3*l
]+xi
;
684 dots
[3*lfnr
-2] = ai
*xus
[1+3*l
]+yi
;
685 dots
[3*lfnr
-1] = ai
*xus
[2+3*l
]+zi
;
689 if (mode
& FLAG_VOLUME
)
691 real dx
= 0.0, dy
= 0.0, dz
= 0.0;
692 for (int l
= 0; l
< n_dot
; l
++)
701 vol
= vol
+aisq
*(dx
*(xi
-xs
)+dy
*(yi
-ys
)+dz
*(zi
-zs
) + ai
*currDotCount
);
705 if (mode
& FLAG_VOLUME
)
707 *value_of_vol
= vol
*FOURPI
/(3.*n_dot
);
709 if (mode
& FLAG_DOTS
)
714 if (mode
& FLAG_ATOM_AREA
)
716 *at_area
= atom_area
;
718 *value_of_area
= area
;
722 fprintf(debug
, "area=%8.3f\n", area
);
729 class SurfaceAreaCalculator::Impl
736 std::vector
<real
> unitSphereDots_
;
737 ArrayRef
<const real
> radius_
;
739 mutable AnalysisNeighborhood nb_
;
742 SurfaceAreaCalculator::SurfaceAreaCalculator()
747 SurfaceAreaCalculator::~SurfaceAreaCalculator()
751 void SurfaceAreaCalculator::setDotCount(int dotCount
)
753 impl_
->unitSphereDots_
= make_unsp(dotCount
, 4);
756 void SurfaceAreaCalculator::setRadii(const ArrayRef
<const real
> &radius
)
758 impl_
->radius_
= radius
;
761 const real maxRadius
= *std::max_element(radius
.begin(), radius
.end());
762 impl_
->nb_
.setCutoff(2*maxRadius
);
766 void SurfaceAreaCalculator::setCalculateVolume(bool bVolume
)
770 impl_
->flags_
|= FLAG_VOLUME
;
774 impl_
->flags_
&= ~FLAG_VOLUME
;
778 void SurfaceAreaCalculator::setCalculateAtomArea(bool bAtomArea
)
782 impl_
->flags_
|= FLAG_ATOM_AREA
;
786 impl_
->flags_
&= ~FLAG_ATOM_AREA
;
790 void SurfaceAreaCalculator::setCalculateSurfaceDots(bool bDots
)
794 impl_
->flags_
|= FLAG_DOTS
;
798 impl_
->flags_
&= ~FLAG_DOTS
;
802 void SurfaceAreaCalculator::calculate(
803 const rvec
*x
, const t_pbc
*pbc
,
804 int nat
, int index
[], int flags
, real
*area
, real
*volume
,
805 real
**at_area
, real
**lidots
, int *n_dots
) const
807 flags
|= impl_
->flags_
;
809 if (volume
== nullptr)
811 flags
&= ~FLAG_VOLUME
;
817 if (at_area
== nullptr)
819 flags
&= ~FLAG_ATOM_AREA
;
825 if (lidots
== nullptr)
833 if (n_dots
== nullptr)
841 nsc_dclm_pbc(x
, impl_
->radius_
, nat
,
842 &impl_
->unitSphereDots_
[0], impl_
->unitSphereDots_
.size()/3,
843 flags
, area
, at_area
, volume
, lidots
, n_dots
, index
,