source: src/Potentials/PartialNucleiChargeFitter.cpp@ 64cafb2

Action_Thermostats Add_AtomRandomPerturbation Add_FitFragmentPartialChargesAction Add_RotateAroundBondAction Add_SelectAtomByNameAction Adding_Graph_to_ChangeBondActions Adding_MD_integration_tests Adding_StructOpt_integration_tests Automaking_mpqc_open AutomationFragmentation_failures Candidate_v1.5.4 Candidate_v1.6.0 Candidate_v1.6.1 ChangeBugEmailaddress ChangingTestPorts ChemicalSpaceEvaluator Combining_Subpackages Debian_Package_split Debian_package_split_molecuildergui_only Disabling_MemDebug Docu_Python_wait EmpiricalPotential_contain_HomologyGraph EmpiricalPotential_contain_HomologyGraph_documentation Enable_parallel_make_install Enhance_userguide Enhanced_StructuralOptimization Enhanced_StructuralOptimization_continued Example_ManyWaysToTranslateAtom Exclude_Hydrogens_annealWithBondGraph FitPartialCharges_GlobalError Fix_ChargeSampling_PBC Fix_ChronosMutex Fix_FitPartialCharges Fix_FitPotential_needs_atomicnumbers Fix_ForceAnnealing Fix_IndependentFragmentGrids Fix_ParseParticles Fix_ParseParticles_split_forward_backward_Actions Fix_StatusMsg Fix_StepWorldTime_single_argument Fix_Verbose_Codepatterns Fixes ForceAnnealing_goodresults ForceAnnealing_oldresults ForceAnnealing_tocheck ForceAnnealing_with_BondGraph ForceAnnealing_with_BondGraph_continued ForceAnnealing_with_BondGraph_continued_betteresults ForceAnnealing_with_BondGraph_contraction-expansion GeometryObjects Gui_displays_atomic_force_velocity IndependentFragmentGrids IndependentFragmentGrids_IndividualZeroInstances IndependentFragmentGrids_IntegrationTest IndependentFragmentGrids_Sole_NN_Calculation JobMarket_RobustOnKillsSegFaults JobMarket_StableWorkerPool JobMarket_unresolvable_hostname_fix ODR_violation_mpqc_open PartialCharges_OrthogonalSummation PythonUI_with_named_parameters QtGui_reactivate_TimeChanged_changes Recreated_GuiChecks RotateToPrincipalAxisSystem_UndoRedo SaturateAtoms_findBestMatching SaturateAtoms_singleDegree StoppableMakroAction Subpackage_CodePatterns Subpackage_JobMarket Subpackage_LinearAlgebra Subpackage_levmar Subpackage_mpqc_open Subpackage_vmg ThirdParty_MPQC_rebuilt_buildsystem TrajectoryDependenant_MaxOrder TremoloParser_IncreasedPrecision TremoloParser_MultipleTimesteps Ubuntu_1604_changes stable
Last change on this file since 64cafb2 was 8eafd6, checked in by Frederik Heber <heber@…>, 9 years ago

Fixed typo in PartialNucleiChargeFitter.

  • Property mode set to 100644
File size: 13.4 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.getDiscreteWindowIndices(
89 grid.begin, grid.end,
90 grid.begin_window, grid.end_window,
91 pre_offset,
92 post_offset,
93 length,
94 total
95 );
96 const size_t calculated_size = length[0]*length[1]*length[2];
97 ASSERT( calculated_size == grid.sampled_grid.size(),
98 "PartialNucleiChargeFitter::PartialNucleiChargeFitter() - grid does not match size indicated by its window.");
99
100 const double potential_sum = std::accumulate(grid.sampled_grid.begin(), grid.sampled_grid.end(), 0.);
101 if ( fabs(potential_sum) > std::numeric_limits<double>::epsilon()*1e4 ) {
102 ELOG(2, "Potential sum is not less than "
103 << std::numeric_limits<double>::epsilon()*1e4 << " but "
104 << potential_sum << ".");
105 }
106
107 SamplingGrid::sampledvalues_t::const_iterator griditer = grid.sampled_grid.begin();
108 size_t index=0;
109 size_t N[3];
110 Vector grid_position; // position of grid point in real domain
111 size_t masked_points = 0;
112 // store step length per axis
113 double delta[3];
114 for (size_t i=0;i<3;++i)
115 delta[i] = grid_properties.getDeltaPerAxis(i);
116 /// convert sampled potential into a vector
117 grid_position[0] = grid_properties.begin[0];
118 for(N[0]=0; N[0] < pre_offset[0]; ++N[0]) {
119 grid_position[1] = grid_properties.begin[1];
120 for(N[1]=0; N[1] < total[1]; ++N[1]) {
121 grid_position[2] = grid_properties.begin[2];
122 for(N[2]=0; N[2] < total[2]; ++N[2]) {
123 const_cast<VectorContent &>(SampledPotential)[index++] = 0.;
124 grid_position[2] += delta[2];
125 }
126 grid_position[1] += delta[1];
127 }
128 grid_position[0] += delta[0];
129 }
130 for(N[0]=0; N[0] < length[0]; ++N[0]) {
131 grid_position[1] = grid_properties.begin[1];
132 for(N[1]=0; N[1] < pre_offset[1]; ++N[1]) {
133 grid_position[2] = grid_properties.begin[2];
134 for(N[2]=0; N[2] < total[2]; ++N[2]) {
135 const_cast<VectorContent &>(SampledPotential)[index++] = 0.;
136 grid_position[2] += delta[2];
137 }
138 grid_position[1] += delta[1];
139 }
140 for(N[1]=0; N[1] < length[1]; ++N[1]) {
141 grid_position[2] = grid_properties.begin[2];
142 for(N[2]=0; N[2] < pre_offset[2]; ++N[2]) {
143 const_cast<VectorContent &>(SampledPotential)[index++] = 0.;
144 grid_position[2] += delta[2];
145 }
146 for(N[2]=0; N[2] < length[2]; ++N[2]) {
147 if (isGridPointSettable(positions, grid_position))
148 const_cast<VectorContent &>(SampledPotential)[index++] = *griditer++;
149 else {
150 // skip point
151 ++griditer;
152 ++masked_points;
153 const_cast<VectorContent &>(SampledPotential)[index++] = 0.;
154 }
155 grid_position[2] += delta[2];
156 }
157 for(N[2]=0; N[2] < post_offset[2]; ++N[2]) {
158 const_cast<VectorContent &>(SampledPotential)[index++] = 0.;
159 grid_position[2] += delta[2];
160 }
161 grid_position[1] += delta[1];
162 }
163 for(N[1]=0; N[1] < post_offset[1]; ++N[1]) {
164 grid_position[2] = grid_properties.begin[2];
165 for(N[2]=0; N[2] < total[2]; ++N[2]) {
166 const_cast<VectorContent &>(SampledPotential)[index++] = 0.;
167 grid_position[2] += delta[2];
168 }
169 grid_position[1] += delta[1];
170 }
171 grid_position[0] += delta[0];
172 }
173 for(N[0]=0; N[0] < post_offset[0]; ++N[0]) {
174 grid_position[1] = grid_properties.begin[1];
175 for(N[1]=0; N[1] < total[1]; ++N[1]) {
176 grid_position[2] = grid_properties.begin[2];
177 for(N[2]=0; N[2] < total[2]; ++N[2]) {
178 const_cast<VectorContent &>(SampledPotential)[index++] = 0.;
179 grid_position[2] += delta[2];
180 }
181 grid_position[1] += delta[1];
182 }
183 grid_position[0] += delta[0];
184 }
185 // set remainder of points to zero
186 ASSERT( index == SampledPotential.getDimension(),
187 "PartialNucleiChargeFitter::PartialNucleiChargeFitter() - not enough or more than calculated sample points.");
188
189#ifndef NDEBUG
190 // write vector as paraview csv file file
191 {
192 size_t N[3];
193 size_t index = 0;
194 std::ofstream paraview_output("solution.csv");
195 paraview_output << "x coord,y coord,z coord,scalar" << std::endl;
196 for(N[0]=0; N[0] < total[0]; ++N[0]) {
197 for(N[1]=0; N[1] < total[1]; ++N[1]) {
198 for(N[2]=0; N[2] < total[2]; ++N[2]) {
199 paraview_output
200 << (double)N[0]/(double)total[0] << ","
201 << (double)N[1]/(double)total[1] << ","
202 << (double)N[2]/(double)total[2] << ","
203 << SampledPotential.at(index++) << std::endl;
204 }
205 }
206 }
207 paraview_output.close();
208 }
209#endif
210
211 LOG(1, "INFO: I masked " << masked_points << " points in right-hand-side.");
212// LOG(4, "DEBUG: Right-hand side vector is " << SampledPotential << ".");
213}
214
215bool PartialNucleiChargeFitter::isGridPointSettable(
216 const positions_t &_positions,
217 const Vector &grid_position) const
218{
219 bool status = true;
220 for (positions_t::const_iterator iter = _positions.begin();
221 iter != _positions.end(); ++iter) {
222 status &= grid_position.DistanceSquared(*iter) > threshold*threshold;
223 }
224 return status;
225}
226
227PartialNucleiChargeFitter::~PartialNucleiChargeFitter()
228{
229 if (PartialCharges != NULL)
230 delete PartialCharges;
231
232 if (PotentialFromCharges != NULL)
233 delete PotentialFromCharges;
234}
235
236
237void PartialNucleiChargeFitter::constructMatrix()
238{
239 const size_t rows = SampledPotential.getDimension();
240 const size_t cols = positions.size();
241
242 // allocate memory for PotentialFromCharges
243 if (PotentialFromCharges != NULL) {
244 delete PotentialFromCharges;
245 PotentialFromCharges = NULL;
246 }
247 PotentialFromCharges = new MatrixContent( rows, cols );
248 // store step length per axis
249 double delta[3];
250 for (size_t i=0;i<3;++i)
251 delta[i] = grid_properties.getDeltaPerAxis(i);
252 // then for each charge ...
253 size_t masked_points = 0;
254 for (size_t nuclei_index = 0; nuclei_index < cols; ++nuclei_index) {
255 // ... calculate potential at each grid position,
256 // i.e. step through grid and calculate distance to charge position
257 Vector grid_position; // position of grid point in real domain
258 grid_position[0] = grid_properties.begin[0];
259 size_t N[3]; // discrete grid position
260 size_t index = 0; // component of column vector
261 for(N[0]=0; N[0] < total[0]; ++N[0]) {
262 grid_position[1] = grid_properties.begin[1];
263 for(N[1]=0; N[1] < total[1]; ++N[1]) {
264 grid_position[2] = grid_properties.begin[2];
265 for(N[2]=0; N[2] < total[2]; ++N[2]) {
266 if (isGridPointSettable(positions, grid_position)) {
267 const double distance = positions[nuclei_index].distance(grid_position);
268 ASSERT( distance >= 0,
269 "PartialNucleiChargeFitter::constructMatrix() - distance is negative?");
270 // Coulomb's constant is 1 in atomic units, see http://en.wikipedia.org/wiki/Atomic_units
271 const double epsilon0_au = 1.; //4.*M_PI*0.007957747154594767;
272 // ... with epsilon_0 in atom units from http://folk.uio.no/michalj/node72.html
273 const double value = 1./(epsilon0_au*distance);
274 PotentialFromCharges->at(index++, nuclei_index) = value;
275 } else {
276 ++masked_points;
277 PotentialFromCharges->at(index++, nuclei_index) = 0.;
278 }
279 grid_position[2] += delta[2];
280 }
281 grid_position[1] += delta[1];
282 }
283 grid_position[0] += delta[0];
284 }
285 ASSERT( index == PotentialFromCharges->getRows(),
286 "PartialNucleiChargeFitter::operator() - number of sampled positions "
287 +toString(index)+" unequal to set rows "
288 +toString(PotentialFromCharges->getRows())+".");
289 }
290
291 LOG(1, "INFO: I masked " << masked_points/cols << " points in matrix.");
292}
293
294double PartialNucleiChargeFitter::operator()()
295{
296 // prepare PartialCharges
297 if (PartialCharges != NULL) {
298 delete PartialCharges;
299 PartialCharges = NULL;
300 }
301 PartialCharges = new VectorContent(positions.size());
302
303 // set up over-determined system's problem matrix A for Ax=b
304 // i.e. columns represent potential of a single charge at grid positions
305 constructMatrix();
306
307 // solve for x
308 *PartialCharges =
309 PotentialFromCharges->solveOverdeterminedLinearEquation(
310 SampledPotential);
311
312// LOG(4, "DEBUG: Solution vector is " << (*PotentialFromCharges) * (*PartialCharges) << ".");
313
314 // calculate residual vector
315 VectorContent residuum = (*PotentialFromCharges) * (*PartialCharges) - SampledPotential;
316
317#ifndef NDEBUG
318 // write solution to file
319 writeMatrix();
320
321 // write vector as paraview csv file file
322 {
323 size_t N[3];
324 size_t index = 0;
325 std::ofstream paraview_output("residuum.csv");
326 paraview_output << "x coord,y coord,z coord,scalar" << std::endl;
327 for(N[0]=0; N[0] < total[0]; ++N[0]) {
328 for(N[1]=0; N[1] < total[1]; ++N[1]) {
329 for(N[2]=0; N[2] < total[2]; ++N[2]) {
330 paraview_output
331 << (double)N[0]/(double)total[0] << ","
332 << (double)N[1]/(double)total[1] << ","
333 << (double)N[2]/(double)total[2] << ","
334 << residuum.at(index++) << std::endl;
335 }
336 }
337 }
338 paraview_output.close();
339 }
340#endif
341
342 // calculate L1 and L2 errors
343 double residuum_l1 = 0.;
344 for (size_t i=0; i< residuum.getDimension(); ++i)
345 if (residuum_l1 < residuum[i])
346 residuum_l1 = residuum[i];
347 LOG(1, "INFO: L2-Norm of residuum is " << residuum.Norm() << ".");
348 LOG(1, "INFO: L1-Norm of residuum is " << residuum_l1 << ".");
349
350 return residuum.Norm();
351}
352
353void PartialNucleiChargeFitter::writeMatrix()
354{
355 constructMatrix();
356
357 // write matrix as paraview csv file file
358 size_t N[3];
359 size_t index=0;
360 std::string filename = std::string("potential.csv");
361 std::ofstream paraview_output(filename.c_str());
362 paraview_output << "x coord,y coord,z coord,scalar" << std::endl;
363 for(N[0]=0; N[0] < total[0]; ++N[0]) {
364 for(N[1]=0; N[1] < total[1]; ++N[1]) {
365 for(N[2]=0; N[2] < total[2]; ++N[2]) {
366 double sum = 0.;
367 for (size_t nuclei_index = 0; nuclei_index < positions.size(); ++nuclei_index) {
368 sum+= PotentialFromCharges->at(index, nuclei_index)*PartialCharges->at(nuclei_index);
369 }
370 paraview_output
371 << (double)N[0]/(double)total[0] << ","
372 << (double)N[1]/(double)total[1] << ","
373 << (double)N[2]/(double)total[2] << ","
374 << sum << std::endl;
375 index++;
376 }
377 }
378 }
379 paraview_output.close();
380}
381
382PartialNucleiChargeFitter::charges_t
383PartialNucleiChargeFitter::getSolutionAsCharges_t() const
384{
385 ASSERT( PartialCharges != NULL,
386 "PartialNucleiChargeFitter::getSolutionAsCharges_t() - PartialCharges requested prior to calculation.");
387 charges_t return_charges(positions.size(), 0.);
388 for (size_t i = 0; i < return_charges.size(); ++i)
389 return_charges[i] = PartialCharges->at(i);
390 return return_charges;
391}
Note: See TracBrowser for help on using the repository browser.