source: src/Potentials/PartialNucleiChargeFitter.cpp@ 9afdef

FitPartialCharges_GlobalError
Last change on this file since 9afdef was 9afdef, checked in by Frederik Heber <heber@…>, 8 years ago

WARNFIX: PartialNucleiChargeFitter::PartialNucleiChargeFitter has unused variable in NDEBUG case.

  • Property mode set to 100644
File size: 14.8 KB
Line 
1/*
2 * Project: MoleCuilder
3 * Description: creates and alters molecular systems
4 * Copyright (C) 2013 Frederik Heber. All rights reserved.
5 * Please see the LICENSE file or "Copyright notice" in builder.cpp for details.
6 *
7 *
8 * This file is part of MoleCuilder.
9 *
10 * MoleCuilder is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * MoleCuilder is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with MoleCuilder. If not, see <http://www.gnu.org/licenses/>.
22 */
23
24/*
25 * PartialNucleiChargeFitter.cpp
26 *
27 * Created on: 12.05.2013
28 * Author: heber
29 */
30
31// include config.h
32#ifdef HAVE_CONFIG_H
33#include <config.h>
34#endif
35
36#include "CodePatterns/MemDebug.hpp"
37
38#include "PartialNucleiChargeFitter.hpp"
39
40#include <cmath>
41#include <fstream>
42#include <limits>
43#include <numeric>
44
45#include "LinearAlgebra/MatrixContent.hpp"
46#include "LinearAlgebra/VectorContent.hpp"
47
48#include "CodePatterns/Assert.hpp"
49#include "CodePatterns/Log.hpp"
50
51#include "Fragmentation/Summation/SetValues/SamplingGrid.hpp"
52
53PartialNucleiChargeFitter::dimensions_t
54PartialNucleiChargeFitter::getGridDimensions(const SamplingGrid &grid) const
55{
56 // convert sampled potential into a vector
57 const double round_offset =
58 (std::numeric_limits<size_t>::round_style == std::round_toward_zero) ?
59 0.5 : 0.; // need offset to get to round_toward_nearest behavior
60 dimensions_t total(3,0);
61 for(size_t index=0;index<3;++index) {
62 const double delta = grid.getDeltaPerAxis(index);
63 // delta is conversion factor from box length to discrete length, i.e. number of points
64 total[index] = (grid.end[index] - grid.begin[index])/delta+round_offset;
65 }
66 return total;
67}
68
69PartialNucleiChargeFitter::PartialNucleiChargeFitter(
70 const SamplingGrid &grid,
71 const positions_t &_positions,
72 const double _threshold) :
73 total(getGridDimensions(grid)),
74 SampledPotential(std::accumulate(total.begin(), total.end(), 1, std::multiplies<double>())),
75 grid_properties(static_cast<const SamplingGridProperties &>(grid)),
76 positions(_positions),
77 PotentialFromCharges(NULL),
78 PartialCharges(NULL),
79 threshold(_threshold)
80{
81 // we must take care of the "window", i.e. there may be less entries in sampled_grid
82 // vector as we would expect from size of grid ((2^level)^3) as 0-entries have been
83 // omitted.
84 size_t pre_offset[3];
85 size_t post_offset[3];
86 size_t length[3];
87 size_t total[3];
88 grid.getDiscreteWindowCopyIndices(
89 grid.begin, grid.end,
90 grid.begin_window, grid.end_window,
91 pre_offset,
92 post_offset,
93 length,
94 total
95 );
96#ifndef NDEBUG
97 const size_t calculated_size = length[0]*length[1]*length[2];
98#endif
99 ASSERT( calculated_size == grid.sampled_grid.size(),
100 "PartialNucleiChargeFitter::PartialNucleiChargeFitter() - grid does not match size indicated by its window.");
101
102 double minimum = std::numeric_limits<double>::max();
103 double maximum = std::numeric_limits<double>::min();
104 double average = 0.;
105 for (SamplingGrid::sampledvalues_t::const_iterator iter = grid.sampled_grid.begin();
106 iter != grid.sampled_grid.end(); ++iter) {
107 minimum = std::min(minimum, *iter);
108 maximum = std::max(maximum, *iter);
109 average += *iter;
110 }
111 LOG(2, "DEBUG: Max over grid is " << maximum
112 << ", minimum is " << minimum
113 << ", and average is " << average/(double)grid.sampled_grid.size());
114
115 const double potential_sum = std::accumulate(grid.sampled_grid.begin(), grid.sampled_grid.end(), 0.);
116 if ( fabs(potential_sum) > std::numeric_limits<double>::epsilon()*1e4 ) {
117 ELOG(2, "Potential sum is not less than "
118 << std::numeric_limits<double>::epsilon()*1e4 << " but "
119 << potential_sum << ".");
120 }
121
122 SamplingGrid::sampledvalues_t::const_iterator griditer = grid.sampled_grid.begin();
123 size_t index=0;
124 size_t N[3];
125 Vector grid_position; // position of grid point in real domain
126 size_t masked_points = 0;
127 // store step length per axis
128 double delta[3];
129 for (size_t i=0;i<3;++i)
130 delta[i] = grid_properties.getDeltaPerAxis(i);
131 /// convert sampled potential into a vector
132 grid_position[0] = grid_properties.begin[0];
133 for(N[0]=0; N[0] < pre_offset[0]; ++N[0]) {
134 grid_position[1] = grid_properties.begin[1];
135 for(N[1]=0; N[1] < total[1]; ++N[1]) {
136 grid_position[2] = grid_properties.begin[2];
137 for(N[2]=0; N[2] < total[2]; ++N[2]) {
138 const_cast<VectorContent &>(SampledPotential)[index++] = 0.;
139 grid_position[2] += delta[2];
140 }
141 grid_position[1] += delta[1];
142 }
143 grid_position[0] += delta[0];
144 }
145 for(N[0]=0; N[0] < length[0]; ++N[0]) {
146 grid_position[1] = grid_properties.begin[1];
147 for(N[1]=0; N[1] < pre_offset[1]; ++N[1]) {
148 grid_position[2] = grid_properties.begin[2];
149 for(N[2]=0; N[2] < total[2]; ++N[2]) {
150 const_cast<VectorContent &>(SampledPotential)[index++] = 0.;
151 grid_position[2] += delta[2];
152 }
153 grid_position[1] += delta[1];
154 }
155 for(N[1]=0; N[1] < length[1]; ++N[1]) {
156 grid_position[2] = grid_properties.begin[2];
157 for(N[2]=0; N[2] < pre_offset[2]; ++N[2]) {
158 const_cast<VectorContent &>(SampledPotential)[index++] = 0.;
159 grid_position[2] += delta[2];
160 }
161 for(N[2]=0; N[2] < length[2]; ++N[2]) {
162 if (isGridPointSettable(positions, grid_position))
163 const_cast<VectorContent &>(SampledPotential)[index++] = *griditer++;
164 else {
165 // skip point
166 ++griditer;
167 ++masked_points;
168 const_cast<VectorContent &>(SampledPotential)[index++] = 0.;
169 }
170 grid_position[2] += delta[2];
171 }
172 for(N[2]=0; N[2] < post_offset[2]; ++N[2]) {
173 const_cast<VectorContent &>(SampledPotential)[index++] = 0.;
174 grid_position[2] += delta[2];
175 }
176 grid_position[1] += delta[1];
177 }
178 for(N[1]=0; N[1] < post_offset[1]; ++N[1]) {
179 grid_position[2] = grid_properties.begin[2];
180 for(N[2]=0; N[2] < total[2]; ++N[2]) {
181 const_cast<VectorContent &>(SampledPotential)[index++] = 0.;
182 grid_position[2] += delta[2];
183 }
184 grid_position[1] += delta[1];
185 }
186 grid_position[0] += delta[0];
187 }
188 for(N[0]=0; N[0] < post_offset[0]; ++N[0]) {
189 grid_position[1] = grid_properties.begin[1];
190 for(N[1]=0; N[1] < total[1]; ++N[1]) {
191 grid_position[2] = grid_properties.begin[2];
192 for(N[2]=0; N[2] < total[2]; ++N[2]) {
193 const_cast<VectorContent &>(SampledPotential)[index++] = 0.;
194 grid_position[2] += delta[2];
195 }
196 grid_position[1] += delta[1];
197 }
198 grid_position[0] += delta[0];
199 }
200 // set remainder of points to zero
201 ASSERT( index == SampledPotential.getDimension(),
202 "PartialNucleiChargeFitter::PartialNucleiChargeFitter() - not enough or more than calculated sample points.");
203
204#ifndef NDEBUG
205 // write vector as paraview csv file file
206 {
207 size_t N[3];
208 size_t index = 0;
209 std::ofstream paraview_output("solution.csv");
210 paraview_output << "x coord,y coord,z coord,scalar" << std::endl;
211 for(N[0]=0; N[0] < total[0]; ++N[0]) {
212 for(N[1]=0; N[1] < total[1]; ++N[1]) {
213 for(N[2]=0; N[2] < total[2]; ++N[2]) {
214 paraview_output
215 << (double)N[0]/(double)total[0] << ","
216 << (double)N[1]/(double)total[1] << ","
217 << (double)N[2]/(double)total[2] << ","
218 << SampledPotential.at(index++) << std::endl;
219 }
220 }
221 }
222 paraview_output.close();
223 }
224#endif
225
226 LOG(1, "INFO: I masked " << masked_points << " points in right-hand-side.");
227// LOG(4, "DEBUG: Right-hand side vector is " << SampledPotential << ".");
228}
229
230bool PartialNucleiChargeFitter::isGridPointSettable(
231 const positions_t &_positions,
232 const Vector &grid_position) const
233{
234 bool status = true;
235 for (positions_t::const_iterator iter = _positions.begin();
236 iter != _positions.end(); ++iter) {
237 status &= grid_position.DistanceSquared(*iter) > threshold*threshold;
238 }
239 return status;
240}
241
242PartialNucleiChargeFitter::~PartialNucleiChargeFitter()
243{
244 if (PartialCharges != NULL)
245 delete PartialCharges;
246
247 if (PotentialFromCharges != NULL)
248 delete PotentialFromCharges;
249}
250
251
252void PartialNucleiChargeFitter::constructMatrix()
253{
254 const size_t rows = SampledPotential.getDimension();
255 const size_t cols = positions.size();
256
257 // allocate memory for PotentialFromCharges
258 if (PotentialFromCharges != NULL) {
259 delete PotentialFromCharges;
260 PotentialFromCharges = NULL;
261 }
262 PotentialFromCharges = new MatrixContent( rows, cols );
263 // store step length per axis
264 double delta[3];
265 for (size_t i=0;i<3;++i)
266 delta[i] = grid_properties.getDeltaPerAxis(i);
267 // then for each charge ...
268 size_t masked_points = 0;
269 for (size_t nuclei_index = 0; nuclei_index < cols; ++nuclei_index) {
270 // ... calculate potential at each grid position,
271 // i.e. step through grid and calculate distance to charge position
272 Vector grid_position; // position of grid point in real domain
273 grid_position[0] = grid_properties.begin[0];
274 size_t N[3]; // discrete grid position
275 size_t index = 0; // component of column vector
276 for(N[0]=0; N[0] < total[0]; ++N[0]) {
277 grid_position[1] = grid_properties.begin[1];
278 for(N[1]=0; N[1] < total[1]; ++N[1]) {
279 grid_position[2] = grid_properties.begin[2];
280 for(N[2]=0; N[2] < total[2]; ++N[2]) {
281 if (isGridPointSettable(positions, grid_position)) {
282 const double distance = positions[nuclei_index].distance(grid_position);
283 ASSERT( distance >= 0,
284 "PartialNucleiChargeFitter::constructMatrix() - distance is negative?");
285 // Coulomb's constant is 1 in atomic units, see http://en.wikipedia.org/wiki/Atomic_units
286 const double epsilon0_au = 1.; //4.*M_PI*0.007957747154594767;
287 // ... with epsilon_0 in atom units from http://folk.uio.no/michalj/node72.html
288 const double value = 1./(epsilon0_au*distance);
289 PotentialFromCharges->at(index++, nuclei_index) = value;
290 } else {
291 ++masked_points;
292 PotentialFromCharges->at(index++, nuclei_index) = 0.;
293 }
294 grid_position[2] += delta[2];
295 }
296 grid_position[1] += delta[1];
297 }
298 grid_position[0] += delta[0];
299 }
300 ASSERT( index == PotentialFromCharges->getRows(),
301 "PartialNucleiChargeFitter::operator() - number of sampled positions "
302 +toString(index)+" unequal to set rows "
303 +toString(PotentialFromCharges->getRows())+".");
304 }
305
306 LOG(1, "INFO: I masked " << masked_points/cols << " points in matrix.");
307}
308
309VectorContent PartialNucleiChargeFitter::calculateResiduum()
310{
311 constructMatrix();
312
313 // calculate residual vector
314 VectorContent residuum = (*PotentialFromCharges) * (*PartialCharges) - SampledPotential;
315
316 return residuum;
317}
318
319void PartialNucleiChargeFitter::prepareCharges(const size_t _size)
320{
321 // prepare PartialCharges
322 if (PartialCharges != NULL) {
323 delete PartialCharges;
324 PartialCharges = NULL;
325 }
326 PartialCharges = new VectorContent(_size);
327}
328
329double PartialNucleiChargeFitter::operator()()
330{
331 prepareCharges(positions.size());
332
333 // set up over-determined system's problem matrix A for Ax=b
334 // i.e. columns represent potential of a single charge at grid positions
335 constructMatrix();
336
337 // solve for x
338 *PartialCharges =
339 PotentialFromCharges->solveOverdeterminedLinearEquation(
340 SampledPotential);
341
342// LOG(4, "DEBUG: Solution vector is " << (*PotentialFromCharges) * (*PartialCharges) << ".");
343
344 LOG(2, "DEBUG: Norm of right-hand side is " << SampledPotential.Norm());
345
346 // calculate residuum (forces matrix reconstruction)
347 VectorContent residuum = calculateResiduum();
348
349#ifndef NDEBUG
350 // write solution to file
351 writeMatrix();
352
353 // write vector as paraview csv file file
354 {
355 size_t N[3];
356 size_t index = 0;
357 std::ofstream paraview_output("residuum.csv");
358 paraview_output << "x coord,y coord,z coord,scalar" << std::endl;
359 for(N[0]=0; N[0] < total[0]; ++N[0]) {
360 for(N[1]=0; N[1] < total[1]; ++N[1]) {
361 for(N[2]=0; N[2] < total[2]; ++N[2]) {
362 paraview_output
363 << (double)N[0]/(double)total[0] << ","
364 << (double)N[1]/(double)total[1] << ","
365 << (double)N[2]/(double)total[2] << ","
366 << residuum.at(index++) << std::endl;
367 }
368 }
369 }
370 paraview_output.close();
371 }
372#endif
373
374 // calculate L1 and L2 errors
375 double residuum_l1 = 0.;
376 for (size_t i=0; i< residuum.getDimension(); ++i)
377 if (residuum_l1 < residuum[i])
378 residuum_l1 = residuum[i];
379 LOG(1, "INFO: L2-Norm of residuum is " << residuum.Norm() << ".");
380 LOG(1, "INFO: L1-Norm of residuum is " << residuum_l1 << ".");
381
382 return residuum.Norm();
383}
384
385bool PartialNucleiChargeFitter::setCharges(const charges_t &_charges)
386{
387 // check sizes
388 if (positions.size() != _charges.size()) {
389 return false;
390 }
391 // (re-)allocate memory
392 prepareCharges(positions.size());
393 // and place charges in vector
394 for(size_t i=0;i<_charges.size();++i)
395 (*PartialCharges)[i] = _charges[i];
396
397 return true;
398}
399
400void PartialNucleiChargeFitter::writeMatrix()
401{
402 // only construct if not yet present
403 if (PotentialFromCharges == NULL)
404 constructMatrix();
405
406 // write matrix as paraview csv file file
407 size_t N[3];
408 size_t index=0;
409 std::string filename = std::string("potential.csv");
410 std::ofstream paraview_output(filename.c_str());
411 paraview_output << "x coord,y coord,z coord,scalar" << std::endl;
412 for(N[0]=0; N[0] < total[0]; ++N[0]) {
413 for(N[1]=0; N[1] < total[1]; ++N[1]) {
414 for(N[2]=0; N[2] < total[2]; ++N[2]) {
415 double sum = 0.;
416 for (size_t nuclei_index = 0; nuclei_index < positions.size(); ++nuclei_index) {
417 sum+= PotentialFromCharges->at(index, nuclei_index)*PartialCharges->at(nuclei_index);
418 }
419 paraview_output
420 << (double)N[0]/(double)total[0] << ","
421 << (double)N[1]/(double)total[1] << ","
422 << (double)N[2]/(double)total[2] << ","
423 << sum << std::endl;
424 index++;
425 }
426 }
427 }
428 paraview_output.close();
429}
430
431PartialNucleiChargeFitter::charges_t
432PartialNucleiChargeFitter::getSolutionAsCharges_t() const
433{
434 ASSERT( PartialCharges != NULL,
435 "PartialNucleiChargeFitter::getSolutionAsCharges_t() - PartialCharges requested prior to calculation.");
436 charges_t return_charges(positions.size(), 0.);
437 for (size_t i = 0; i < return_charges.size(); ++i)
438 return_charges[i] = PartialCharges->at(i);
439 return return_charges;
440}
Note: See TracBrowser for help on using the repository browser.