source: src/Actions/PotentialAction/FitPartialChargesAction.cpp@ d8ed62

Action_Thermostats Add_AtomRandomPerturbation Add_FitFragmentPartialChargesAction Add_RotateAroundBondAction Add_SelectAtomByNameAction Added_ParseSaveFragmentResults Adding_Graph_to_ChangeBondActions Adding_MD_integration_tests Adding_ParticleName_to_Atom Adding_StructOpt_integration_tests 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_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 FragmentMolecule_checks_bonddegrees GeometryObjects Gui_Fixes 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 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 d8ed62 was d8ed62, checked in by Frederik Heber <heber@…>, 9 years ago

Rewrote FitPartialChargesAction to fit charges to currently selected atoms.

  • required AtomFragmentsMap in order to associate fragments to atoms.
  • updated documentation to match change in action.
  • Property mode set to 100644
File size: 13.8 KB
Line 
1/*
2 * Project: MoleCuilder
3 * Description: creates and alters molecular systems
4 * Copyright (C) 2013 Frederik Heber. All rights reserved.
5 *
6 *
7 * This file is part of MoleCuilder.
8 *
9 * MoleCuilder is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * MoleCuilder is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with MoleCuilder. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23/*
24 * FitPartialChargesAction.cpp
25 *
26 * Created on: Jul 03, 2013
27 * Author: heber
28 */
29
30// include config.h
31#ifdef HAVE_CONFIG_H
32#include <config.h>
33#endif
34
35// needs to come before MemDebug due to placement new
36#include <boost/archive/text_iarchive.hpp>
37
38#include "CodePatterns/MemDebug.hpp"
39
40#include "Atom/atom.hpp"
41#include "CodePatterns/Log.hpp"
42#include "Fragmentation/Exporters/ExportGraph_ToFiles.hpp"
43#include "Fragmentation/Graph.hpp"
44#include "World.hpp"
45
46#include <boost/bind.hpp>
47#include <boost/filesystem.hpp>
48#include <boost/foreach.hpp>
49#include <algorithm>
50#include <functional>
51#include <iostream>
52#include <string>
53
54#include "Actions/PotentialAction/FitPartialChargesAction.hpp"
55
56#include "Potentials/PartialNucleiChargeFitter.hpp"
57
58#include "Element/element.hpp"
59#include "Element/periodentafel.hpp"
60#include "Fragmentation/Homology/AtomFragmentsMap.hpp"
61#include "Fragmentation/Homology/HomologyContainer.hpp"
62#include "Fragmentation/Homology/HomologyGraph.hpp"
63#include "Fragmentation/Summation/SetValues/SamplingGrid.hpp"
64#include "FunctionApproximation/Extractors.hpp"
65#include "Potentials/PartialNucleiChargeFitter.hpp"
66#include "Potentials/Particles/ParticleFactory.hpp"
67#include "Potentials/Particles/ParticleRegistry.hpp"
68#include "Potentials/SerializablePotential.hpp"
69#include "World.hpp"
70
71using namespace MoleCuilder;
72
73// and construct the stuff
74#include "FitPartialChargesAction.def"
75#include "Action_impl_pre.hpp"
76/** =========== define the function ====================== */
77
78inline
79HomologyGraph getFirstGraphwithSpecifiedElements(
80 const HomologyContainer &homologies,
81 const SerializablePotential::ParticleTypes_t &types)
82{
83 ASSERT( !types.empty(),
84 "getFirstGraphwithSpecifiedElements() - charges is empty?");
85 // create charges
86 Fragment::charges_t charges;
87 charges.resize(types.size());
88 std::transform(types.begin(), types.end(),
89 charges.begin(), boost::lambda::_1);
90 // convert into count map
91 Extractors::elementcounts_t counts_per_charge =
92 Extractors::_detail::getElementCounts(charges);
93 ASSERT( !counts_per_charge.empty(),
94 "getFirstGraphwithSpecifiedElements() - charge counts are empty?");
95 LOG(2, "DEBUG: counts_per_charge is " << counts_per_charge << ".");
96 // we want to check each (unique) key only once
97 HomologyContainer::const_key_iterator olditer = homologies.key_end();
98 for (HomologyContainer::const_key_iterator iter =
99 homologies.key_begin(); iter != homologies.key_end();
100 iter = homologies.getNextKey(iter)) {
101 // if it's the same as the old one, skip it
102 if (olditer == iter)
103 continue;
104 else
105 olditer = iter;
106 // if it's a new key, check if every element has the right number of counts
107 Extractors::elementcounts_t::const_iterator countiter = counts_per_charge.begin();
108 for (; countiter != counts_per_charge.end(); ++countiter)
109 if (!(*iter).hasTimesAtomicNumber(
110 static_cast<size_t>(countiter->first),
111 static_cast<size_t>(countiter->second))
112 )
113 break;
114 if( countiter == counts_per_charge.end())
115 return *iter;
116 }
117 return HomologyGraph();
118}
119
120void enforceZeroTotalCharge(
121 PartialNucleiChargeFitter::charges_t &_averaged_charges)
122{
123 double charge_sum = 0.;
124 charge_sum = std::accumulate(_averaged_charges.begin(), _averaged_charges.end(), charge_sum);
125 if (fabs(charge_sum) > MYEPSILON) {
126 std::transform(
127 _averaged_charges.begin(), _averaged_charges.end(),
128 _averaged_charges.begin(),
129 boost::bind(std::minus<double>(), _1, charge_sum/_averaged_charges.size()));
130 }
131 charge_sum = 0.;
132 charge_sum = std::accumulate(_averaged_charges.begin(), _averaged_charges.end(), charge_sum);
133 ASSERT( fabs(charge_sum) < MYEPSILON,
134 "enforceZeroTotalCharge() - enforcing neutral net charge failed, "
135 +toString(charge_sum)+" is the remaining net charge.");
136}
137
138size_t obtainAverageChargesOverFragments(
139 PartialNucleiChargeFitter::charges_t &_average_charges,
140 const std::pair<
141 HomologyContainer::const_iterator,
142 HomologyContainer::const_iterator> &_range,
143 const double _radius
144 )
145{
146 HomologyContainer::const_iterator iter = _range.first;
147 if (!iter->second.containsGrids) {
148 ELOG(1, "This HomologyGraph does not contain sampled grids.");
149 return 0;
150 }
151 _average_charges.resize(iter->second.fragment.getCharges().size(), 0.);
152 size_t NoFragments = 0;
153 for (;
154 iter != _range.second; ++iter, ++NoFragments) {
155 if (!iter->second.containsGrids) {
156 ELOG(2, "This HomologyGraph does not contain sampled grids,\ndid you forget to add '--store-grids 1' to AnalyseFragmentResults.");
157 return 0;
158 }
159 const Fragment &fragment = iter->second.fragment;
160 // const double &energy = iter->second.energy;
161 // const SamplingGrid &charge = iter->second.charge_distribution;
162 const SamplingGrid &potential = iter->second.potential_distribution;
163 if ((potential.level == 0)
164 || ((potential.begin[0] == potential.end[0])
165 && (potential.begin[1] == potential.end[1])
166 && (potential.begin[2] == potential.end[2]))) {
167 ELOG(1, "Sampled grid contains grid made of zero points.");
168 return 0;
169 }
170
171 // then we extract positions from fragment
172 PartialNucleiChargeFitter::positions_t positions;
173 Fragment::positions_t fragmentpositions = fragment.getPositions();
174 positions.reserve(fragmentpositions.size());
175 BOOST_FOREACH( Fragment::position_t pos, fragmentpositions) {
176 positions.push_back( Vector(pos[0], pos[1], pos[2]) );
177 }
178 PartialNucleiChargeFitter fitter(potential, positions, _radius);
179 fitter();
180 PartialNucleiChargeFitter::charges_t return_charges = fitter.getSolutionAsCharges_t();
181 LOG(2, "DEBUG: fitted charges are " << return_charges);
182 std::transform(
183 return_charges.begin(), return_charges.end(),
184 _average_charges.begin(),
185 _average_charges.begin(),
186 std::plus<PartialNucleiChargeFitter::charge_t>());
187 }
188 if (NoFragments != 0)
189 std::transform(_average_charges.begin(), _average_charges.end(),
190 _average_charges.begin(),
191 std::bind1st(std::multiplies<PartialNucleiChargeFitter::charge_t>(),1./(double)NoFragments)
192 );
193 LOG(2, "DEBUG: final averaged charges are " << _average_charges);
194
195 return NoFragments;
196}
197
198ActionState::ptr PotentialFitPartialChargesAction::performCall()
199{
200 // check for selected atoms
201 const World &world = const_cast<const World &>(World::getInstance());
202 if (world.beginAtomSelection() == world.endAtomSelection()) {
203 STATUS("There are no atoms selected for fitting partial charges to.");
204 return Action::failure;
205 }
206
207 /// obtain possible fragments to each selected atom
208 AtomFragmentsMap::keysets_t fragments;
209 const AtomFragmentsMap::AtomFragmentsMap_t &atommap =
210 AtomFragmentsMap::getConstInstance().getMap();
211 for (World::AtomSelectionConstIterator iter = world.beginAtomSelection();
212 iter != world.endAtomSelection(); ++iter) {
213 const AtomFragmentsMap::AtomFragmentsMap_t::const_iterator atomiter =
214 atommap.find(iter->first);
215 if (atomiter == atommap.end()) {
216 ELOG(2, "There are no fragments associated to " << iter->first << ".");
217 continue;
218 }
219 const AtomFragmentsMap::keysets_t &keysets = atomiter->second;
220 fragments.insert( fragments.end(), keysets.begin(), keysets.end() );
221 }
222
223 /// then go through all fragments and get partial charges for each
224 typedef std::map<
225 KeySet,
226 PartialNucleiChargeFitter::charges_t> FragmentFittedChargeMap_t;
227 FragmentFittedChargeMap_t fittedcharges_per_fragment;
228 for (AtomFragmentsMap::keysets_t::const_iterator fragmentiter = fragments.begin();
229 fragmentiter != fragments.end(); ++fragmentiter) {
230 // fragment specifies the homology fragment to use
231 SerializablePotential::ParticleTypes_t fragmentnumbers(fragmentiter->begin(), fragmentiter->end());
232
233 // obtain range of homogolous fragments from container
234 HomologyContainer &homologies = World::getInstance().getHomologies();
235 const HomologyGraph graph = getFirstGraphwithSpecifiedElements(homologies,fragmentnumbers);
236 const HomologyContainer::range_t range = homologies.getHomologousGraphs(graph);
237 if (range.first == range.second) {
238 STATUS("HomologyContainer does not contain specified fragment.");
239 return Action::failure;
240 }
241
242 // fit and average partial charges over all homologous fragments
243 PartialNucleiChargeFitter::charges_t averaged_charges;
244 const size_t NoFragments =
245 obtainAverageChargesOverFragments(averaged_charges, range, params.radius.get());
246 if ((NoFragments == 0) && (range.first != range.second)) {
247 STATUS("Fitting for fragment "+toString(*fragmentiter)+" failed.");
248 return Action::failure;
249 }
250
251 // make sum of charges zero if desired
252 if (params.enforceZeroCharge.get())
253 enforceZeroTotalCharge(averaged_charges);
254
255 // place into map for later retrieval
256 fittedcharges_per_fragment.insert( std::make_pair(*fragmentiter, averaged_charges));
257
258 // output status info fitted charges
259 LOG(2, "DEBUG: For fragment " << *fragmentiter << " we have fitted the following charges "
260 << averaged_charges << ", averaged over " << NoFragments << " fragments.");
261 }
262
263 /// obtain average charge for each atom the fitted charges over all its fragments
264 typedef std::map<atomId_t, double> fitted_charges_t;
265 fitted_charges_t fitted_charges;
266 for (World::AtomSelectionConstIterator atomiter = world.beginAtomSelection();
267 atomiter != world.endAtomSelection(); ++atomiter) {
268 const AtomFragmentsMap::AtomFragmentsMap_t::const_iterator keysetsiter =
269 atommap.find(atomiter->first);
270 ASSERT(keysetsiter != atommap.end(),
271 "PotentialFitPartialChargesAction::performCall() - we checked already that "
272 +toString(atomiter->first)+" should be present!");
273 const AtomFragmentsMap::keysets_t & keysets = keysetsiter->second;
274
275 double average_charge = 0.;
276 size_t NoFragments = 0;
277 // go over all fragments associated to this atom
278 for (AtomFragmentsMap::keysets_t::const_iterator keysetsiter = keysets.begin();
279 keysetsiter != keysets.end(); ++keysetsiter) {
280 const KeySet &keyset = *keysetsiter;
281
282 // find the associated charge in the charge vector
283 const FragmentFittedChargeMap_t::const_iterator chargesiter =
284 fittedcharges_per_fragment.find(keyset);
285 ASSERT(chargesiter != fittedcharges_per_fragment.end(),
286 "PotentialFitPartialChargesAction::performCall() - no charge to "+toString(keyset)
287 +" any longer present in fittedcharges_per_fragment?");
288 const PartialNucleiChargeFitter::charges_t &charges = chargesiter->second;
289 PartialNucleiChargeFitter::charges_t::const_iterator chargeiter =
290 charges.begin();
291 const KeySet::const_iterator keysetiter = keyset.find(atomiter->first);
292 ASSERT( keysetiter != keyset.end(),
293 "PotentialFitPartialChargesAction::performCall() - atom "
294 +toString(atomiter->first)+" not contained in keyset "+toString(keyset));
295 std::advance(chargeiter, std::distance(keyset.begin(), keysetiter));
296 ASSERT( chargeiter != charges.end(),
297 "PotentialFitPartialChargesAction::performCall() - charges "
298 +toString(charges.size())+" and keyset "+toString(keyset.size())
299 +" do not have the same length?");
300
301 // and add onto charge sum
302 const double & charge_in_fragment = *chargeiter;
303 average_charge += charge_in_fragment;
304 ++NoFragments;
305 }
306 // average to obtain final partial charge for this atom
307 average_charge = 1./(size_t)NoFragments;
308 fitted_charges.insert( std::make_pair(atomiter->first, average_charge) );
309 }
310
311 /// place all fitted charges into ParticleRegistry
312 const ParticleFactory &factory =
313 const_cast<const ParticleFactory&>(ParticleFactory::getInstance());
314 const periodentafel &periode = *World::getInstance().getPeriode();
315 for (fitted_charges_t::const_iterator chargeiter = fitted_charges.begin();
316 chargeiter != fitted_charges.end(); ++chargeiter) {
317 const atom * const walker = World::getInstance().getAtom(AtomById(chargeiter->first));
318 ASSERT( walker != NULL,
319 "PotentialFitPartialChargesAction::performCall() - atom "
320 +toString(chargeiter->first)+" not present in the World?");
321 const double &charge = chargeiter->second;
322 const atomicNumber_t elementno = walker->getElementNo();
323 const std::string name = Particle::findFreeName(periode, elementno);
324 LOG(1, "INFO: Adding particle " << name << " for atom "
325 << *walker << " with element " << elementno << ", charge " << charge);
326 factory.createInstance(name, elementno, charge);
327 }
328
329 return Action::success;
330}
331
332ActionState::ptr PotentialFitPartialChargesAction::performUndo(ActionState::ptr _state) {
333 return Action::success;
334}
335
336ActionState::ptr PotentialFitPartialChargesAction::performRedo(ActionState::ptr _state){
337 return Action::success;
338}
339
340bool PotentialFitPartialChargesAction::canUndo() {
341 return false;
342}
343
344bool PotentialFitPartialChargesAction::shouldUndo() {
345 return false;
346}
347/** =========== end of function ====================== */
Note: See TracBrowser for help on using the repository browser.