1 |
/* |
2 |
* Copyright (c) 2005 The University of Notre Dame. All Rights Reserved. |
3 |
* |
4 |
* The University of Notre Dame grants you ("Licensee") a |
5 |
* non-exclusive, royalty free, license to use, modify and |
6 |
* redistribute this software in source and binary code form, provided |
7 |
* that the following conditions are met: |
8 |
* |
9 |
* 1. Redistributions of source code must retain the above copyright |
10 |
* notice, this list of conditions and the following disclaimer. |
11 |
* |
12 |
* 2. Redistributions in binary form must reproduce the above copyright |
13 |
* notice, this list of conditions and the following disclaimer in the |
14 |
* documentation and/or other materials provided with the |
15 |
* distribution. |
16 |
* |
17 |
* This software is provided "AS IS," without a warranty of any |
18 |
* kind. All express or implied conditions, representations and |
19 |
* warranties, including any implied warranty of merchantability, |
20 |
* fitness for a particular purpose or non-infringement, are hereby |
21 |
* excluded. The University of Notre Dame and its licensors shall not |
22 |
* be liable for any damages suffered by licensee as a result of |
23 |
* using, modifying or distributing the software or its |
24 |
* derivatives. In no event will the University of Notre Dame or its |
25 |
* licensors be liable for any lost revenue, profit or data, or for |
26 |
* direct, indirect, special, consequential, incidental or punitive |
27 |
* damages, however caused and regardless of the theory of liability, |
28 |
* arising out of the use of or inability to use software, even if the |
29 |
* University of Notre Dame has been advised of the possibility of |
30 |
* such damages. |
31 |
* |
32 |
* SUPPORT OPEN SCIENCE! If you use OpenMD or its source code in your |
33 |
* research, please cite the appropriate papers when you publish your |
34 |
* work. Good starting points are: |
35 |
* |
36 |
* [1] Meineke, et al., J. Comp. Chem. 26, 252-271 (2005). |
37 |
* [2] Fennell & Gezelter, J. Chem. Phys. 124, 234104 (2006). |
38 |
* [3] Sun, Lin & Gezelter, J. Chem. Phys. 128, 234107 (2008). |
39 |
* [4] Kuang & Gezelter, J. Chem. Phys. 133, 164101 (2010). |
40 |
* [5] Vardeman, Stocker & Gezelter, J. Chem. Theory Comput. 7, 834 (2011). |
41 |
*/ |
42 |
|
43 |
#include <algorithm> |
44 |
#include <functional> |
45 |
#include "applications/sequentialProps/ContactAngle2.hpp" |
46 |
#include "utils/simError.h" |
47 |
#include "io/DumpReader.hpp" |
48 |
#include "primitives/Molecule.hpp" |
49 |
#include "utils/NumericConstant.hpp" |
50 |
#include "utils/PhysicalConstants.hpp" |
51 |
#include "math/Eigenvalue.hpp" |
52 |
|
53 |
namespace OpenMD { |
54 |
|
55 |
ContactAngle2::ContactAngle2(SimInfo* info, const std::string& filename, |
56 |
const std::string& sele, RealType solidZ, |
57 |
RealType centroidX, RealType centroidY, |
58 |
RealType threshDens, RealType bufferLength, |
59 |
int nrbins, int nzbins) |
60 |
: SequentialAnalyzer(info, filename), solidZ_(solidZ), |
61 |
centroidX_(centroidX), centroidY_(centroidY), |
62 |
threshDens_(threshDens), bufferLength_(bufferLength), nRBins_(nrbins), |
63 |
nZBins_(nzbins), selectionScript_(sele), seleMan_(info), |
64 |
evaluator_(info) { |
65 |
|
66 |
setOutputName(getPrefix(filename) + ".ca2"); |
67 |
|
68 |
evaluator_.loadScriptString(sele); |
69 |
|
70 |
if (!evaluator_.isDynamic()) { |
71 |
seleMan_.setSelectionSet(evaluator_.evaluate()); |
72 |
} |
73 |
} |
74 |
|
75 |
void ContactAngle2::doFrame() { |
76 |
StuntDouble* sd; |
77 |
int i; |
78 |
|
79 |
// set up the bins for density analysis |
80 |
|
81 |
Mat3x3d hmat = info_->getSnapshotManager()->getCurrentSnapshot()->getHmat(); |
82 |
RealType len = std::min(hmat(0, 0), hmat(1, 1)); |
83 |
RealType zLen = hmat(2,2); |
84 |
|
85 |
RealType dr = len / (RealType) nRBins_; |
86 |
RealType dz = zLen / (RealType) nZBins_; |
87 |
|
88 |
std::vector<std::vector<RealType> > histo; |
89 |
histo.resize(nRBins_); |
90 |
for (unsigned int i = 0; i < histo.size(); ++i){ |
91 |
histo[i].resize(nZBins_); |
92 |
std::fill(histo[i].begin(), histo[i].end(), 0.0); |
93 |
} |
94 |
|
95 |
if (evaluator_.isDynamic()) { |
96 |
seleMan_.setSelectionSet(evaluator_.evaluate()); |
97 |
} |
98 |
|
99 |
|
100 |
// RealType mtot = 0.0; |
101 |
// Vector3d com(V3Zero); |
102 |
// RealType mass; |
103 |
|
104 |
// for (sd = seleMan_.beginSelected(i); sd != NULL; |
105 |
// sd = seleMan_.nextSelected(i)) { |
106 |
// mass = sd->getMass(); |
107 |
// mtot += mass; |
108 |
// com += sd->getPos() * mass; |
109 |
// } |
110 |
|
111 |
// com /= mtot; |
112 |
|
113 |
Vector3d com(centroidX_, centroidY_, solidZ_); |
114 |
|
115 |
// now that we have the centroid, we can make cylindrical density maps |
116 |
Vector3d pos; |
117 |
RealType r; |
118 |
RealType z; |
119 |
|
120 |
for (sd = seleMan_.beginSelected(i); sd != NULL; |
121 |
sd = seleMan_.nextSelected(i)) { |
122 |
pos = sd->getPos() - com; |
123 |
|
124 |
// r goes from zero upwards |
125 |
r = sqrt(pow(pos.x(), 2) + pow(pos.y(), 2)); |
126 |
// z is possibly symmetric around 0 |
127 |
z = pos.z(); |
128 |
|
129 |
int whichRBin = int(r / dr); |
130 |
int whichZBin = int( (zLen/2.0 + z) / dz); |
131 |
|
132 |
if ((whichRBin < int(nRBins_)) && (whichZBin >= 0) && (whichZBin < int(nZBins_))) { |
133 |
histo[whichRBin][whichZBin] += sd->getMass(); |
134 |
} |
135 |
|
136 |
} |
137 |
|
138 |
for(unsigned int i = 0 ; i < histo.size(); ++i){ |
139 |
|
140 |
RealType rL = i * dr; |
141 |
RealType rU = rL + dr; |
142 |
RealType volSlice = NumericConstant::PI * dz * (( rU*rU ) - ( rL*rL )); |
143 |
|
144 |
for (unsigned int j = 0; j < histo[i].size(); ++j) { |
145 |
histo[i][j] *= PhysicalConstants::densityConvert / volSlice; |
146 |
} |
147 |
} |
148 |
|
149 |
std::vector<Vector<RealType, 2> > points; |
150 |
points.clear(); |
151 |
|
152 |
for (unsigned int j = 0; j < nZBins_; ++j) { |
153 |
|
154 |
// The z coordinates were measured relative to the selection |
155 |
// center of mass. However, we're interested in the elevation |
156 |
// above the solid surface. Also, the binning was done around |
157 |
// zero with enough bins to cover the zLength of the box: |
158 |
|
159 |
RealType thez = com.z() - solidZ_ - zLen/2.0 + dz * (j + 0.5); |
160 |
bool aboveThresh = false; |
161 |
bool foundThresh = false; |
162 |
int rloc = 0; |
163 |
|
164 |
for (std::size_t i = 0; i < nRBins_; ++i) { |
165 |
|
166 |
if (histo[i][j] >= threshDens_) aboveThresh = true; |
167 |
|
168 |
if (aboveThresh && (histo[i][j] <= threshDens_)) { |
169 |
rloc = i; |
170 |
foundThresh = true; |
171 |
aboveThresh = false; |
172 |
} |
173 |
|
174 |
} |
175 |
if (foundThresh) { |
176 |
Vector<RealType,2> point; |
177 |
point[0] = dr*(rloc+0.5); |
178 |
point[1] = thez; |
179 |
|
180 |
if (thez > bufferLength_) { |
181 |
points.push_back( point ); |
182 |
} |
183 |
} |
184 |
} |
185 |
|
186 |
int numPoints = points.size(); |
187 |
|
188 |
// Compute the average of the data points. |
189 |
Vector<RealType, 2> average = points[0]; |
190 |
int i0; |
191 |
for (i0 = 1; i0 < numPoints; ++i0) { |
192 |
average += points[i0]; |
193 |
} |
194 |
RealType invNumPoints = ((RealType)1)/(RealType)numPoints; |
195 |
average *= invNumPoints; |
196 |
|
197 |
DynamicRectMatrix<RealType> mat(4, 4); |
198 |
int row, col; |
199 |
for (row = 0; row < 4; ++row) { |
200 |
for (col = 0; col < 4; ++col){ |
201 |
mat(row,col) = 0.0; |
202 |
} |
203 |
} |
204 |
for (int i = 0; i < numPoints; ++i) { |
205 |
RealType x = points[i][0]; |
206 |
RealType y = points[i][1]; |
207 |
RealType x2 = x*x; |
208 |
RealType y2 = y*y; |
209 |
RealType xy = x*y; |
210 |
RealType r2 = x2+y2; |
211 |
RealType xr2 = x*r2; |
212 |
RealType yr2 = y*r2; |
213 |
RealType r4 = r2*r2; |
214 |
|
215 |
mat(0,1) += x; |
216 |
mat(0,2) += y; |
217 |
mat(0,3) += r2; |
218 |
mat(1,1) += x2; |
219 |
mat(1,2) += xy; |
220 |
mat(1,3) += xr2; |
221 |
mat(2,2) += y2; |
222 |
mat(2,3) += yr2; |
223 |
mat(3,3) += r4; |
224 |
} |
225 |
mat(0,0) = (RealType)numPoints; |
226 |
|
227 |
for (row = 0; row < 4; ++row) { |
228 |
for (col = 0; col < row; ++col) { |
229 |
mat(row,col) = mat(col,row); |
230 |
} |
231 |
} |
232 |
|
233 |
for (row = 0; row < 4; ++row) { |
234 |
for (col = 0; col < 4; ++col) { |
235 |
mat(row,col) *= invNumPoints; |
236 |
} |
237 |
} |
238 |
|
239 |
JAMA::Eigenvalue<RealType> eigensystem(mat); |
240 |
DynamicRectMatrix<RealType> evects(4, 4); |
241 |
DynamicVector<RealType> evals(4); |
242 |
|
243 |
eigensystem.getRealEigenvalues(evals); |
244 |
eigensystem.getV(evects); |
245 |
|
246 |
DynamicVector<RealType> evector = evects.getColumn(0); |
247 |
RealType inv = ((RealType)1)/evector[3]; // beware zero divide |
248 |
RealType coeff[3]; |
249 |
for (row = 0; row < 3; ++row) { |
250 |
coeff[row] = inv*evector[row]; |
251 |
} |
252 |
|
253 |
Vector<RealType, 2> center; |
254 |
|
255 |
center[0] = -((RealType)0.5)*coeff[1]; |
256 |
center[1] = -((RealType)0.5)*coeff[2]; |
257 |
RealType radius = sqrt(fabs(center[0]*center[0] + center[1]*center[1] |
258 |
- coeff[0])); |
259 |
|
260 |
int i1; |
261 |
for (i1 = 0; i1 < 100; ++i1) { |
262 |
// Update the iterates. |
263 |
Vector<RealType, 2> current = center; |
264 |
|
265 |
// Compute average L, dL/da, dL/db. |
266 |
RealType lenAverage = (RealType)0; |
267 |
Vector<RealType, 2> derLenAverage = Vector<RealType, 2>(0.0); |
268 |
for (i0 = 0; i0 < numPoints; ++i0) { |
269 |
Vector<RealType, 2> diff = points[i0] - center; |
270 |
RealType length = diff.length(); |
271 |
if (length > 1e-6) { |
272 |
lenAverage += length; |
273 |
RealType invLength = ((RealType)1)/length; |
274 |
derLenAverage -= invLength*diff; |
275 |
} |
276 |
} |
277 |
lenAverage *= invNumPoints; |
278 |
derLenAverage *= invNumPoints; |
279 |
|
280 |
center = average + lenAverage*derLenAverage; |
281 |
radius = lenAverage; |
282 |
|
283 |
Vector<RealType, 2> diff = center - current; |
284 |
if (fabs(diff[0]) <= 1e-6 && fabs(diff[1]) <= 1e-6) { |
285 |
break; |
286 |
} |
287 |
} |
288 |
|
289 |
RealType zCen = center[1]; |
290 |
RealType rDrop = radius; |
291 |
RealType ca; |
292 |
|
293 |
if (fabs(zCen) > rDrop) { |
294 |
ca = 180.0; |
295 |
} else { |
296 |
ca = 90.0 + asin(zCen/rDrop)*(180.0/M_PI); |
297 |
} |
298 |
|
299 |
values_.push_back( ca ); |
300 |
|
301 |
} |
302 |
} |
303 |
|
304 |
|