1 | /*
|
---|
2 | * vmg - a versatile multigrid solver
|
---|
3 | * Copyright (C) 2012 Institute for Numerical Simulation, University of Bonn
|
---|
4 | *
|
---|
5 | * vmg is free software: you can redistribute it and/or modify
|
---|
6 | * it under the terms of the GNU General Public License as published by
|
---|
7 | * the Free Software Foundation, either version 3 of the License, or
|
---|
8 | * (at your option) any later version.
|
---|
9 | *
|
---|
10 | * vmg is distributed in the hope that it will be useful,
|
---|
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
13 | * GNU General Public License for more details.
|
---|
14 | *
|
---|
15 | * You should have received a copy of the GNU General Public License
|
---|
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
---|
17 | */
|
---|
18 |
|
---|
19 | /**
|
---|
20 | * @file bspline.hpp
|
---|
21 | * @author Julian Iseringhausen <isering@ins.uni-bonn.de>
|
---|
22 | * @date Mon Nov 21 13:27:22 2011
|
---|
23 | *
|
---|
24 | * @brief B-Splines for molecular dynamics.
|
---|
25 | *
|
---|
26 | */
|
---|
27 |
|
---|
28 | #ifndef BSPLINE_HPP_
|
---|
29 | #define BSPLINE_HPP_
|
---|
30 |
|
---|
31 | #include <cmath>
|
---|
32 |
|
---|
33 | #include "base/helper.hpp"
|
---|
34 | #include "base/index.hpp"
|
---|
35 | #include "base/polynomial.hpp"
|
---|
36 | #include "base/vector.hpp"
|
---|
37 | #include "grid/grid.hpp"
|
---|
38 | #include "units/particle/particle.hpp"
|
---|
39 |
|
---|
40 | namespace VMG
|
---|
41 | {
|
---|
42 |
|
---|
43 | namespace Particle
|
---|
44 | {
|
---|
45 |
|
---|
46 | class BSpline
|
---|
47 | {
|
---|
48 | public:
|
---|
49 | BSpline(const int& near_field_cells, const vmg_float& h);
|
---|
50 |
|
---|
51 | vmg_float EvaluateSpline(const vmg_float& val) const
|
---|
52 | {
|
---|
53 | for (unsigned int i=0; i<intervals.size(); ++i)
|
---|
54 | if (val < intervals[i])
|
---|
55 | return spline_nom[i](val) / spline_denom[i](val);
|
---|
56 | return 0.0;
|
---|
57 | }
|
---|
58 |
|
---|
59 | void SetSpline(Grid& grid, const Particle& p) const
|
---|
60 | {
|
---|
61 | assert(p.Pos().X() >= grid.Extent().Begin().X() && p.Pos().X() < grid.Extent().End().X());
|
---|
62 | assert(p.Pos().Y() >= grid.Extent().Begin().Y() && p.Pos().Y() < grid.Extent().End().Y());
|
---|
63 | assert(p.Pos().Z() >= grid.Extent().Begin().Z() && p.Pos().Z() < grid.Extent().End().Z());
|
---|
64 |
|
---|
65 | vmg_float *vals = new vmg_float[Helper::intpow(2*near_field_cells+1,3)];
|
---|
66 |
|
---|
67 | vmg_float temp_val;
|
---|
68 | vmg_float int_val = 0.0;
|
---|
69 | int c = 0;
|
---|
70 |
|
---|
71 | const int index_global_x = grid.Global().GlobalBegin().X() + std::floor((p.Pos().X() - grid.Extent().Begin().X()) / grid.Extent().MeshWidth().X());
|
---|
72 | const int index_global_y = grid.Global().GlobalBegin().Y() + std::floor((p.Pos().Y() - grid.Extent().Begin().Y()) / grid.Extent().MeshWidth().Y());
|
---|
73 | const int index_global_z = grid.Global().GlobalBegin().Z() + std::floor((p.Pos().Z() - grid.Extent().Begin().Z()) / grid.Extent().MeshWidth().Z());
|
---|
74 |
|
---|
75 | assert(index_global_x >= grid.Global().LocalBegin().X() && index_global_x < grid.Global().LocalEnd().X());
|
---|
76 | assert(index_global_y >= grid.Global().LocalBegin().Y() && index_global_y < grid.Global().LocalEnd().Y());
|
---|
77 | assert(index_global_z >= grid.Global().LocalBegin().Z() && index_global_z < grid.Global().LocalEnd().Z());
|
---|
78 |
|
---|
79 | const int index_local_x = index_global_x - grid.Global().LocalBegin().X() + grid.Local().HaloSize1().X();
|
---|
80 | const int index_local_y = index_global_y - grid.Global().LocalBegin().Y() + grid.Local().HaloSize1().Y();
|
---|
81 | const int index_local_z = index_global_z - grid.Global().LocalBegin().Z() + grid.Local().HaloSize1().Z();
|
---|
82 |
|
---|
83 | assert(index_local_x >= grid.Local().Begin().X() && index_local_x < grid.Local().End().X());
|
---|
84 | assert(index_local_y >= grid.Local().Begin().Y() && index_local_y < grid.Local().End().Y());
|
---|
85 | assert(index_local_z >= grid.Local().Begin().Z() && index_local_z < grid.Local().End().Z());
|
---|
86 |
|
---|
87 | const vmg_float pos_beg_x = grid.Extent().Begin().X() + grid.Extent().MeshWidth().X() * (index_global_x - grid.Global().GlobalBegin().X() - near_field_cells) - p.Pos().X();
|
---|
88 | const vmg_float pos_beg_y = grid.Extent().Begin().Y() + grid.Extent().MeshWidth().Y() * (index_global_y - grid.Global().GlobalBegin().Y() - near_field_cells) - p.Pos().Y();
|
---|
89 | const vmg_float pos_beg_z = grid.Extent().Begin().Z() + grid.Extent().MeshWidth().Z() * (index_global_z - grid.Global().GlobalBegin().Z() - near_field_cells) - p.Pos().Z();
|
---|
90 |
|
---|
91 | const vmg_float& h_x = grid.Extent().MeshWidth().X();
|
---|
92 | const vmg_float& h_y = grid.Extent().MeshWidth().Y();
|
---|
93 | const vmg_float& h_z = grid.Extent().MeshWidth().Z();
|
---|
94 |
|
---|
95 | // Iterate over all grid points which lie in the support of the interpolating B-Spline
|
---|
96 | vmg_float dir_x = pos_beg_x;
|
---|
97 | for (int i=-1*near_field_cells; i<=near_field_cells; ++i) {
|
---|
98 | vmg_float dir_y = pos_beg_y;
|
---|
99 | for (int j=-1*near_field_cells; j<=near_field_cells; ++j) {
|
---|
100 | vmg_float dir_z = pos_beg_z;
|
---|
101 | for (int k=-1*near_field_cells; k<=near_field_cells; ++k) {
|
---|
102 |
|
---|
103 | // Compute distance from grid point to particle
|
---|
104 | temp_val = EvaluateSpline(std::sqrt(dir_x*dir_x+dir_y*dir_y+dir_z*dir_z));
|
---|
105 | vals[c++] = temp_val * p.Charge();
|
---|
106 | int_val += temp_val;
|
---|
107 |
|
---|
108 | dir_z += h_z;
|
---|
109 | }
|
---|
110 | dir_y += h_y;
|
---|
111 | }
|
---|
112 | dir_x += h_x;
|
---|
113 | }
|
---|
114 |
|
---|
115 | // Reciprocal value of the numerically integrated spline
|
---|
116 | int_val = 1.0 / (int_val * h_x * h_y * h_z);
|
---|
117 |
|
---|
118 | c = 0;
|
---|
119 | for (int i=-1*near_field_cells; i<=near_field_cells; ++i)
|
---|
120 | for (int j=-1*near_field_cells; j<=near_field_cells; ++j)
|
---|
121 | for (int k=-1*near_field_cells; k<=near_field_cells; ++k)
|
---|
122 | grid(index_local_x + i,
|
---|
123 | index_local_y + j,
|
---|
124 | index_local_z + k) += vals[c++] * int_val;
|
---|
125 | assert( c == Helper::intpow(2*near_field_cells+1,3) );
|
---|
126 |
|
---|
127 | delete [] vals;
|
---|
128 | }
|
---|
129 |
|
---|
130 | void changeGridBySelfInducedPotential(Grid& grid, Particle& p, const vmg_float &sign) const
|
---|
131 | {
|
---|
132 | assert(p.Pos().X() >= grid.Extent().Begin().X() && p.Pos().X() < grid.Extent().End().X());
|
---|
133 | assert(p.Pos().Y() >= grid.Extent().Begin().Y() && p.Pos().Y() < grid.Extent().End().Y());
|
---|
134 | assert(p.Pos().Z() >= grid.Extent().Begin().Z() && p.Pos().Z() < grid.Extent().End().Z());
|
---|
135 |
|
---|
136 | const int index_global_x = grid.Global().GlobalBegin().X() + std::floor((p.Pos().X() - grid.Extent().Begin().X()) / grid.Extent().MeshWidth().X());
|
---|
137 | const int index_global_y = grid.Global().GlobalBegin().Y() + std::floor((p.Pos().Y() - grid.Extent().Begin().Y()) / grid.Extent().MeshWidth().Y());
|
---|
138 | const int index_global_z = grid.Global().GlobalBegin().Z() + std::floor((p.Pos().Z() - grid.Extent().Begin().Z()) / grid.Extent().MeshWidth().Z());
|
---|
139 |
|
---|
140 | assert(index_global_x >= grid.Global().LocalBegin().X() && index_global_x < grid.Global().LocalEnd().X());
|
---|
141 | assert(index_global_y >= grid.Global().LocalBegin().Y() && index_global_y < grid.Global().LocalEnd().Y());
|
---|
142 | assert(index_global_z >= grid.Global().LocalBegin().Z() && index_global_z < grid.Global().LocalEnd().Z());
|
---|
143 |
|
---|
144 | const int index_local_x = index_global_x - grid.Global().LocalBegin().X() + grid.Local().HaloSize1().X();
|
---|
145 | const int index_local_y = index_global_y - grid.Global().LocalBegin().Y() + grid.Local().HaloSize1().Y();
|
---|
146 | const int index_local_z = index_global_z - grid.Global().LocalBegin().Z() + grid.Local().HaloSize1().Z();
|
---|
147 |
|
---|
148 | assert(index_local_x >= grid.Local().Begin().X() && index_local_x < grid.Local().End().X());
|
---|
149 | assert(index_local_y >= grid.Local().Begin().Y() && index_local_y < grid.Local().End().Y());
|
---|
150 | assert(index_local_z >= grid.Local().Begin().Z() && index_local_z < grid.Local().End().Z());
|
---|
151 |
|
---|
152 | const vmg_float pos_beg_x = grid.Extent().Begin().X() + grid.Extent().MeshWidth().X() * (index_global_x - grid.Global().GlobalBegin().X() - near_field_cells) - p.Pos().X();
|
---|
153 | const vmg_float pos_beg_y = grid.Extent().Begin().Y() + grid.Extent().MeshWidth().Y() * (index_global_y - grid.Global().GlobalBegin().Y() - near_field_cells) - p.Pos().Y();
|
---|
154 | const vmg_float pos_beg_z = grid.Extent().Begin().Z() + grid.Extent().MeshWidth().Z() * (index_global_z - grid.Global().GlobalBegin().Z() - near_field_cells) - p.Pos().Z();
|
---|
155 |
|
---|
156 | const vmg_float& h_x = grid.Extent().MeshWidth().X();
|
---|
157 | const vmg_float& h_y = grid.Extent().MeshWidth().Y();
|
---|
158 | const vmg_float& h_z = grid.Extent().MeshWidth().Z();
|
---|
159 |
|
---|
160 | vmg_float dir_x = pos_beg_x;
|
---|
161 | for (int i=-1*near_field_cells; i<=near_field_cells; ++i) {
|
---|
162 | vmg_float dir_y = pos_beg_y;
|
---|
163 | for (int j=-1*near_field_cells; j<=near_field_cells; ++j) {
|
---|
164 | vmg_float dir_z = pos_beg_z;
|
---|
165 | for (int k=-1*near_field_cells; k<=near_field_cells; ++k) {
|
---|
166 | const double length_sq = dir_x*dir_x+dir_y*dir_y+dir_z*dir_z;
|
---|
167 | const double length = std::sqrt(length_sq);
|
---|
168 | grid(index_local_x + i,
|
---|
169 | index_local_y + j,
|
---|
170 | index_local_z + k) += sign * p.Charge() * EvaluatePotential(length);
|
---|
171 |
|
---|
172 | dir_z += h_z;
|
---|
173 | }
|
---|
174 | dir_y += h_y;
|
---|
175 | }
|
---|
176 | dir_x += h_x;
|
---|
177 | }
|
---|
178 | }
|
---|
179 |
|
---|
180 | void SubtractSelfInducedForces(const Grid& grid, Particle& p) const
|
---|
181 | {
|
---|
182 | assert(p.Pos().X() >= grid.Extent().Begin().X() && p.Pos().X() < grid.Extent().End().X());
|
---|
183 | assert(p.Pos().Y() >= grid.Extent().Begin().Y() && p.Pos().Y() < grid.Extent().End().Y());
|
---|
184 | assert(p.Pos().Z() >= grid.Extent().Begin().Z() && p.Pos().Z() < grid.Extent().End().Z());
|
---|
185 |
|
---|
186 | vmg_float temp_val = 0.;
|
---|
187 |
|
---|
188 | const int index_global_x = grid.Global().GlobalBegin().X() + std::floor((p.Pos().X() - grid.Extent().Begin().X()) / grid.Extent().MeshWidth().X());
|
---|
189 | const int index_global_y = grid.Global().GlobalBegin().Y() + std::floor((p.Pos().Y() - grid.Extent().Begin().Y()) / grid.Extent().MeshWidth().Y());
|
---|
190 | const int index_global_z = grid.Global().GlobalBegin().Z() + std::floor((p.Pos().Z() - grid.Extent().Begin().Z()) / grid.Extent().MeshWidth().Z());
|
---|
191 |
|
---|
192 | assert(index_global_x >= grid.Global().LocalBegin().X() && index_global_x < grid.Global().LocalEnd().X());
|
---|
193 | assert(index_global_y >= grid.Global().LocalBegin().Y() && index_global_y < grid.Global().LocalEnd().Y());
|
---|
194 | assert(index_global_z >= grid.Global().LocalBegin().Z() && index_global_z < grid.Global().LocalEnd().Z());
|
---|
195 |
|
---|
196 | const int index_local_x = index_global_x - grid.Global().LocalBegin().X() + grid.Local().HaloSize1().X();
|
---|
197 | const int index_local_y = index_global_y - grid.Global().LocalBegin().Y() + grid.Local().HaloSize1().Y();
|
---|
198 | const int index_local_z = index_global_z - grid.Global().LocalBegin().Z() + grid.Local().HaloSize1().Z();
|
---|
199 |
|
---|
200 | assert(index_local_x >= grid.Local().Begin().X() && index_local_x < grid.Local().End().X());
|
---|
201 | assert(index_local_y >= grid.Local().Begin().Y() && index_local_y < grid.Local().End().Y());
|
---|
202 | assert(index_local_z >= grid.Local().Begin().Z() && index_local_z < grid.Local().End().Z());
|
---|
203 |
|
---|
204 | const vmg_float pos_beg_x = grid.Extent().Begin().X() + grid.Extent().MeshWidth().X() * (index_global_x - grid.Global().GlobalBegin().X() - near_field_cells) - p.Pos().X();
|
---|
205 | const vmg_float pos_beg_y = grid.Extent().Begin().Y() + grid.Extent().MeshWidth().Y() * (index_global_y - grid.Global().GlobalBegin().Y() - near_field_cells) - p.Pos().Y();
|
---|
206 | const vmg_float pos_beg_z = grid.Extent().Begin().Z() + grid.Extent().MeshWidth().Z() * (index_global_z - grid.Global().GlobalBegin().Z() - near_field_cells) - p.Pos().Z();
|
---|
207 |
|
---|
208 | const vmg_float& h_x = grid.Extent().MeshWidth().X();
|
---|
209 | const vmg_float& h_y = grid.Extent().MeshWidth().Y();
|
---|
210 | const vmg_float& h_z = grid.Extent().MeshWidth().Z();
|
---|
211 |
|
---|
212 | vmg_float int_val = 0.;
|
---|
213 | vmg_float dir_x = pos_beg_x;
|
---|
214 | for (int i=-1*near_field_cells; i<=near_field_cells; ++i) {
|
---|
215 | vmg_float dir_y = pos_beg_y;
|
---|
216 | for (int j=-1*near_field_cells; j<=near_field_cells; ++j) {
|
---|
217 | vmg_float dir_z = pos_beg_z;
|
---|
218 | for (int k=-1*near_field_cells; k<=near_field_cells; ++k) {
|
---|
219 |
|
---|
220 | const double length_sq = dir_x*dir_x+dir_y*dir_y+dir_z*dir_z;
|
---|
221 | if (fabs(length_sq) > std::numeric_limits<double>::epsilon()) {
|
---|
222 | // Compute distance from grid point to particle
|
---|
223 | int_val += EvaluateSpline(std::sqrt(length_sq));
|
---|
224 | }
|
---|
225 |
|
---|
226 | dir_z += h_z;
|
---|
227 | }
|
---|
228 | dir_y += h_y;
|
---|
229 | }
|
---|
230 | dir_x += h_x;
|
---|
231 | }
|
---|
232 | // Reciprocal value of the numerically integrated spline
|
---|
233 | if (fabs(int_val) > std::numeric_limits<double>::epsilon())
|
---|
234 | int_val = 1. / (int_val * h_x * h_y * h_z);
|
---|
235 | else
|
---|
236 | int_val = 1. / (h_x * h_y * h_z);
|
---|
237 |
|
---|
238 | // Iterate over all grid points which lie in the support of the interpolating B-Spline
|
---|
239 | vmg_float test_int_val = 0.;
|
---|
240 | dir_x = pos_beg_x;
|
---|
241 | for (int i=-1*near_field_cells; i<=near_field_cells; ++i) {
|
---|
242 | vmg_float dir_y = pos_beg_y;
|
---|
243 | for (int j=-1*near_field_cells; j<=near_field_cells; ++j) {
|
---|
244 | vmg_float dir_z = pos_beg_z;
|
---|
245 | for (int k=-1*near_field_cells; k<=near_field_cells; ++k) {
|
---|
246 |
|
---|
247 | // Compute distance from grid point to particle
|
---|
248 | const double length_sq = dir_x*dir_x+dir_y*dir_y+dir_z*dir_z;
|
---|
249 | if (fabs(length_sq) > std::numeric_limits<double>::epsilon()) {
|
---|
250 | const double length = std::sqrt(length_sq);
|
---|
251 |
|
---|
252 | temp_val = h_x * h_y * h_z * int_val * EvaluateSpline(length);
|
---|
253 | test_int_val += temp_val;
|
---|
254 | p.Field()[0] -= p.Charge()* temp_val * dir_x / (length_sq*length);
|
---|
255 | p.Field()[1] -= p.Charge()* temp_val * dir_y / (length_sq*length);
|
---|
256 | p.Field()[2] -= p.Charge()* temp_val * dir_z / (length_sq*length);
|
---|
257 | } else {
|
---|
258 | std::cerr << "Value very small " << length_sq << "=("
|
---|
259 | << dir_x << ")^2+(" << dir_y << ")^2+(" << dir_z << ")^2 for particle at ("
|
---|
260 | << p.Pos().X() << ","
|
---|
261 | << p.Pos().Y() << ","
|
---|
262 | << p.Pos().Z() << ")"
|
---|
263 | << "\n";
|
---|
264 | }
|
---|
265 | dir_z += h_z;
|
---|
266 | }
|
---|
267 | dir_y += h_y;
|
---|
268 | }
|
---|
269 | dir_x += h_x;
|
---|
270 | }
|
---|
271 | if ( fabs(test_int_val -1.) > std::numeric_limits<double>::epsilon()*1e4 ) {
|
---|
272 | std::cerr << "Integrated spline value should be 1 but is " << test_int_val << std::endl;
|
---|
273 | assert(0);
|
---|
274 | }
|
---|
275 | }
|
---|
276 |
|
---|
277 | vmg_float EvaluatePotential(const vmg_float& val) const
|
---|
278 | {
|
---|
279 | for (unsigned int i=0; i<intervals.size(); ++i)
|
---|
280 | if (val < intervals[i])
|
---|
281 | return potential_nom[i](val) / potential_denom[i](val);
|
---|
282 | return potential_nom.back()(val) / potential_denom.back()(val);
|
---|
283 | }
|
---|
284 |
|
---|
285 | vmg_float EvaluateField(const vmg_float& val) const
|
---|
286 | {
|
---|
287 | for (unsigned int i=0; i<intervals.size(); ++i)
|
---|
288 | if (val < intervals[i])
|
---|
289 | return field_nom[i](val) / field_denom[i](val);
|
---|
290 | return 0.0;
|
---|
291 | }
|
---|
292 |
|
---|
293 | const vmg_float& GetAntiDerivativeAtZero() const
|
---|
294 | {
|
---|
295 | return antid;
|
---|
296 | }
|
---|
297 |
|
---|
298 | private:
|
---|
299 | std::vector<Polynomial> spline_nom, spline_denom;
|
---|
300 | std::vector<Polynomial> potential_nom, potential_denom;
|
---|
301 | std::vector<Polynomial> field_nom, field_denom;
|
---|
302 | vmg_float antid;
|
---|
303 | std::vector<vmg_float> intervals;
|
---|
304 |
|
---|
305 | const vmg_float R;
|
---|
306 | const int near_field_cells;
|
---|
307 | };
|
---|
308 |
|
---|
309 | }
|
---|
310 |
|
---|
311 | }
|
---|
312 |
|
---|
313 | #endif /* BSPLINE_HPP_ */
|
---|