source: src/Potentials/PartialNucleiChargeFitter.cpp@ 58fcbe5

Action_Thermostats Add_AtomRandomPerturbation Add_FitFragmentPartialChargesAction Add_RotateAroundBondAction Add_SelectAtomByNameAction Added_ParseSaveFragmentResults AddingActions_SaveParseParticleParameters Adding_Graph_to_ChangeBondActions Adding_MD_integration_tests Adding_ParticleName_to_Atom Adding_StructOpt_integration_tests AtomFragments Automaking_mpqc_open AutomationFragmentation_failures Candidate_v1.5.4 Candidate_v1.6.0 Candidate_v1.6.1 ChangeBugEmailaddress ChangingTestPorts ChemicalSpaceEvaluator CombiningParticlePotentialParsing 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_BoundInBox_CenterInBox_MoleculeActions Fix_ChargeSampling_PBC Fix_ChronosMutex Fix_FitPartialCharges Fix_FitPotential_needs_atomicnumbers Fix_ForceAnnealing Fix_IndependentFragmentGrids Fix_ParseParticles Fix_ParseParticles_split_forward_backward_Actions Fix_PopActions Fix_QtFragmentList_sorted_selection Fix_Restrictedkeyset_FragmentMolecule Fix_StatusMsg Fix_StepWorldTime_single_argument Fix_Verbose_Codepatterns Fix_fitting_potentials Fixes ForceAnnealing_goodresults ForceAnnealing_oldresults ForceAnnealing_tocheck ForceAnnealing_with_BondGraph ForceAnnealing_with_BondGraph_continued ForceAnnealing_with_BondGraph_continued_betteresults ForceAnnealing_with_BondGraph_contraction-expansion FragmentAction_writes_AtomFragments FragmentMolecule_checks_bonddegrees GeometryObjects Gui_Fixes Gui_displays_atomic_force_velocity ImplicitCharges IndependentFragmentGrids IndependentFragmentGrids_IndividualZeroInstances IndependentFragmentGrids_IntegrationTest IndependentFragmentGrids_Sole_NN_Calculation JobMarket_RobustOnKillsSegFaults JobMarket_StableWorkerPool JobMarket_unresolvable_hostname_fix MoreRobust_FragmentAutomation ODR_violation_mpqc_open PartialCharges_OrthogonalSummation PdbParser_setsAtomName PythonUI_with_named_parameters QtGui_reactivate_TimeChanged_changes Recreated_GuiChecks Rewrite_FitPartialCharges RotateToPrincipalAxisSystem_UndoRedo SaturateAtoms_findBestMatching SaturateAtoms_singleDegree StoppableMakroAction Subpackage_CodePatterns Subpackage_JobMarket Subpackage_LinearAlgebra Subpackage_levmar Subpackage_mpqc_open Subpackage_vmg Switchable_LogView ThirdParty_MPQC_rebuilt_buildsystem TrajectoryDependenant_MaxOrder TremoloParser_IncreasedPrecision TremoloParser_MultipleTimesteps TremoloParser_setsAtomName Ubuntu_1604_changes stable
Last change on this file since 58fcbe5 was 58fcbe5, checked in by Frederik Heber <heber@…>, 12 years ago

Added functor for fitting partial nuclear charges.

  • is simply via solving the over-determined linear system of equations.
  • added unit test.
  • added basic idea to documentation.
  • has a masking threshold to leave out spheres around nuclei from fit.
  • Property mode set to 100644
File size: 10.5 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 then "
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
190 LOG(1, "INFO: I masked " << masked_points << " points in right-hand-side.");
191// LOG(4, "DEBUG: Right-hand side vector is " << SampledPotential << ".");
192}
193
194bool PartialNucleiChargeFitter::isGridPointSettable(
195 const positions_t &_positions,
196 const Vector &grid_position) const
197{
198 bool status = true;
199 for (positions_t::const_iterator iter = _positions.begin();
200 iter != _positions.end(); ++iter) {
201 status &= grid_position.DistanceSquared(*iter) > threshold*threshold;
202 }
203 return status;
204}
205
206PartialNucleiChargeFitter::~PartialNucleiChargeFitter()
207{
208 delete PotentialFromCharges;
209}
210
211
212void PartialNucleiChargeFitter::constructMatrix()
213{
214 const size_t rows = SampledPotential.getDimension();
215 const size_t cols = positions.size();
216 PotentialFromCharges = new MatrixContent( rows, cols );
217 // store step length per axis
218 double delta[3];
219 for (size_t i=0;i<3;++i)
220 delta[i] = grid_properties.getDeltaPerAxis(i);
221 // then for each charge ...
222 size_t masked_points = 0;
223 for (size_t nuclei_index = 0; nuclei_index < cols; ++nuclei_index) {
224 // ... calculate potential at each grid position,
225 // i.e. step through grid and calculate distance to charge position
226 Vector grid_position; // position of grid point in real domain
227 grid_position[0] = grid_properties.begin[0];
228 size_t N[3]; // discrete grid position
229 size_t index = 0; // component of column vector
230 for(N[0]=0; N[0] < total[0]; ++N[0]) {
231 grid_position[1] = grid_properties.begin[1];
232 for(N[1]=0; N[1] < total[1]; ++N[1]) {
233 grid_position[2] = grid_properties.begin[2];
234 for(N[2]=0; N[2] < total[2]; ++N[2]) {
235 if (isGridPointSettable(positions, grid_position)) {
236 const double distance = positions[nuclei_index].distance(grid_position);
237 ASSERT( distance >= 0,
238 "PartialNucleiChargeFitter::constructMatrix() - distance is negative?");
239 // Coulomb's constant is 1 in atomic units, see http://en.wikipedia.org/wiki/Atomic_units
240 const double epsilon0_au = 1.; //4.*M_PI*0.007957747154594767;
241 // ... with epsilon_0 in atom units from http://folk.uio.no/michalj/node72.html
242 const double value = 1./(epsilon0_au*distance);
243 PotentialFromCharges->at(index++, nuclei_index) = value;
244 } else {
245 ++masked_points;
246 PotentialFromCharges->at(index++, nuclei_index) = 0.;
247 }
248 grid_position[2] += delta[2];
249 }
250 grid_position[1] += delta[1];
251 }
252 grid_position[0] += delta[0];
253 }
254 ASSERT( index == PotentialFromCharges->getRows(),
255 "PartialNucleiChargeFitter::operator() - number of sampled positions "
256 +toString(index)+" unequal to set rows "
257 +toString(PotentialFromCharges->getRows())+".");
258 }
259
260 LOG(1, "INFO: I masked " << masked_points/cols << " points in matrix.");
261}
262
263double PartialNucleiChargeFitter::operator()()
264{
265 // prepare PartialCharges
266 PartialCharges = new VectorContent(positions.size());
267
268 // set up over-determined system's problem matrix A for Ax=b
269 // i.e. columns represent potential of a single charge at grid positions
270 constructMatrix();
271
272 // solve for x
273 *PartialCharges =
274 PotentialFromCharges->solveOverdeterminedLinearEquation(
275 SampledPotential);
276
277 LOG(4, "DEBUG: Solution vector is " << (*PotentialFromCharges) * (*PartialCharges) << ".");
278
279 // calculate residual vector
280 VectorContent residuum = (*PotentialFromCharges) * (*PartialCharges) - SampledPotential;
281 return residuum.Norm();
282}
283
284PartialNucleiChargeFitter::charges_t
285PartialNucleiChargeFitter::getSolutionAsCharges_t() const
286{
287 ASSERT( PartialCharges != NULL,
288 "PartialNucleiChargeFitter::getSolutionAsCharges_t() - PartialCharges requested prior to calculation.");
289 charges_t return_charges(positions.size(), 0.);
290 for (size_t i = 0; i < return_charges.size(); ++i)
291 return_charges[i] = PartialCharges->at(i);
292 return return_charges;
293}
Note: See TracBrowser for help on using the repository browser.