| [c4a323] | 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 | /*
|
|---|
| [50d49d] | 24 | * FitPartialChargesAction.cpp
|
|---|
| [c4a323] | 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 |
|
|---|
| [01120c] | 46 | #include <boost/bimap.hpp>
|
|---|
| [91f907] | 47 | #include <boost/bimap/multiset_of.hpp>
|
|---|
| [a9099d] | 48 | #include <boost/bind.hpp>
|
|---|
| [c4a323] | 49 | #include <boost/filesystem.hpp>
|
|---|
| 50 | #include <boost/foreach.hpp>
|
|---|
| 51 | #include <algorithm>
|
|---|
| [9b68fc] | 52 | #include <functional>
|
|---|
| [c4a323] | 53 | #include <iostream>
|
|---|
| 54 | #include <string>
|
|---|
| 55 |
|
|---|
| [50d49d] | 56 | #include "Actions/PotentialAction/FitPartialChargesAction.hpp"
|
|---|
| [c4a323] | 57 |
|
|---|
| 58 | #include "Potentials/PartialNucleiChargeFitter.hpp"
|
|---|
| 59 |
|
|---|
| [01120c] | 60 | #include "AtomIdSet.hpp"
|
|---|
| 61 | #include "Descriptors/AtomIdDescriptor.hpp"
|
|---|
| [c4a323] | 62 | #include "Element/element.hpp"
|
|---|
| [2082637] | 63 | #include "Element/periodentafel.hpp"
|
|---|
| [d8ed62] | 64 | #include "Fragmentation/Homology/AtomFragmentsMap.hpp"
|
|---|
| [c4a323] | 65 | #include "Fragmentation/Homology/HomologyContainer.hpp"
|
|---|
| 66 | #include "Fragmentation/Homology/HomologyGraph.hpp"
|
|---|
| 67 | #include "Fragmentation/Summation/SetValues/SamplingGrid.hpp"
|
|---|
| 68 | #include "FunctionApproximation/Extractors.hpp"
|
|---|
| 69 | #include "Potentials/PartialNucleiChargeFitter.hpp"
|
|---|
| [708ec1] | 70 | #include "Potentials/Particles/ParticleFactory.hpp"
|
|---|
| 71 | #include "Potentials/Particles/ParticleRegistry.hpp"
|
|---|
| [c4a323] | 72 | #include "Potentials/SerializablePotential.hpp"
|
|---|
| 73 | #include "World.hpp"
|
|---|
| 74 |
|
|---|
| 75 | using namespace MoleCuilder;
|
|---|
| 76 |
|
|---|
| 77 | // and construct the stuff
|
|---|
| [50d49d] | 78 | #include "FitPartialChargesAction.def"
|
|---|
| [c4a323] | 79 | #include "Action_impl_pre.hpp"
|
|---|
| 80 | /** =========== define the function ====================== */
|
|---|
| 81 |
|
|---|
| [91f907] | 82 | namespace detail {
|
|---|
| 83 | typedef std::map<KeySet, HomologyGraph> KeysetsToGraph_t;
|
|---|
| 84 |
|
|---|
| 85 | typedef std::map<HomologyGraph, PartialNucleiChargeFitter::charges_t> GraphFittedChargeMap_t;
|
|---|
| 86 |
|
|---|
| 87 | typedef std::map<atomId_t, double> fitted_charges_t;
|
|---|
| 88 |
|
|---|
| 89 | typedef std::map<HomologyGraph, size_t> GraphIndex_t;
|
|---|
| 90 |
|
|---|
| 91 | typedef std::set<size_t> AtomsGraphIndices_t;
|
|---|
| 92 | typedef boost::bimaps::bimap<
|
|---|
| 93 | AtomsGraphIndices_t,
|
|---|
| 94 | boost::bimaps::multiset_of<atomId_t> > GraphIndices_t;
|
|---|
| 95 |
|
|---|
| 96 | typedef std::map<atomId_t, std::string> AtomParticleNames_t;
|
|---|
| 97 |
|
|---|
| 98 | typedef std::map<std::set<size_t>, std::string> GraphToNameMap_t;
|
|---|
| 99 | };
|
|---|
| 100 |
|
|---|
| [01120c] | 101 | static void enforceZeroTotalCharge(
|
|---|
| [d8ed62] | 102 | PartialNucleiChargeFitter::charges_t &_averaged_charges)
|
|---|
| 103 | {
|
|---|
| 104 | double charge_sum = 0.;
|
|---|
| 105 | charge_sum = std::accumulate(_averaged_charges.begin(), _averaged_charges.end(), charge_sum);
|
|---|
| 106 | if (fabs(charge_sum) > MYEPSILON) {
|
|---|
| 107 | std::transform(
|
|---|
| 108 | _averaged_charges.begin(), _averaged_charges.end(),
|
|---|
| 109 | _averaged_charges.begin(),
|
|---|
| 110 | boost::bind(std::minus<double>(), _1, charge_sum/_averaged_charges.size()));
|
|---|
| [c4a323] | 111 | }
|
|---|
| [d8ed62] | 112 | charge_sum = 0.;
|
|---|
| 113 | charge_sum = std::accumulate(_averaged_charges.begin(), _averaged_charges.end(), charge_sum);
|
|---|
| 114 | ASSERT( fabs(charge_sum) < MYEPSILON,
|
|---|
| 115 | "enforceZeroTotalCharge() - enforcing neutral net charge failed, "
|
|---|
| 116 | +toString(charge_sum)+" is the remaining net charge.");
|
|---|
| [01120c] | 117 |
|
|---|
| 118 | LOG(2, "DEBUG: final charges with net zero charge are " << _averaged_charges);
|
|---|
| [d8ed62] | 119 | }
|
|---|
| [9b68fc] | 120 |
|
|---|
| [01120c] | 121 | static size_t obtainAverageChargesOverFragments(
|
|---|
| [d8ed62] | 122 | PartialNucleiChargeFitter::charges_t &_average_charges,
|
|---|
| 123 | const std::pair<
|
|---|
| 124 | HomologyContainer::const_iterator,
|
|---|
| 125 | HomologyContainer::const_iterator> &_range,
|
|---|
| 126 | const double _radius
|
|---|
| 127 | )
|
|---|
| 128 | {
|
|---|
| 129 | HomologyContainer::const_iterator iter = _range.first;
|
|---|
| [c4a323] | 130 | if (!iter->second.containsGrids) {
|
|---|
| [d8ed62] | 131 | ELOG(1, "This HomologyGraph does not contain sampled grids.");
|
|---|
| 132 | return 0;
|
|---|
| [c4a323] | 133 | }
|
|---|
| [d8ed62] | 134 | _average_charges.resize(iter->second.fragment.getCharges().size(), 0.);
|
|---|
| [9b68fc] | 135 | size_t NoFragments = 0;
|
|---|
| 136 | for (;
|
|---|
| [d8ed62] | 137 | iter != _range.second; ++iter, ++NoFragments) {
|
|---|
| [9b68fc] | 138 | if (!iter->second.containsGrids) {
|
|---|
| 139 | ELOG(2, "This HomologyGraph does not contain sampled grids,\ndid you forget to add '--store-grids 1' to AnalyseFragmentResults.");
|
|---|
| [d8ed62] | 140 | return 0;
|
|---|
| [9b68fc] | 141 | }
|
|---|
| 142 | const Fragment &fragment = iter->second.fragment;
|
|---|
| 143 | // const double &energy = iter->second.energy;
|
|---|
| 144 | // const SamplingGrid &charge = iter->second.charge_distribution;
|
|---|
| 145 | const SamplingGrid &potential = iter->second.potential_distribution;
|
|---|
| 146 | if ((potential.level == 0)
|
|---|
| 147 | || ((potential.begin[0] == potential.end[0])
|
|---|
| 148 | && (potential.begin[1] == potential.end[1])
|
|---|
| 149 | && (potential.begin[2] == potential.end[2]))) {
|
|---|
| 150 | ELOG(1, "Sampled grid contains grid made of zero points.");
|
|---|
| [d8ed62] | 151 | return 0;
|
|---|
| [9b68fc] | 152 | }
|
|---|
| 153 |
|
|---|
| 154 | // then we extract positions from fragment
|
|---|
| 155 | PartialNucleiChargeFitter::positions_t positions;
|
|---|
| 156 | Fragment::positions_t fragmentpositions = fragment.getPositions();
|
|---|
| 157 | positions.reserve(fragmentpositions.size());
|
|---|
| 158 | BOOST_FOREACH( Fragment::position_t pos, fragmentpositions) {
|
|---|
| 159 | positions.push_back( Vector(pos[0], pos[1], pos[2]) );
|
|---|
| 160 | }
|
|---|
| [d8ed62] | 161 | PartialNucleiChargeFitter fitter(potential, positions, _radius);
|
|---|
| [9b68fc] | 162 | fitter();
|
|---|
| 163 | PartialNucleiChargeFitter::charges_t return_charges = fitter.getSolutionAsCharges_t();
|
|---|
| [d8ed62] | 164 | LOG(2, "DEBUG: fitted charges are " << return_charges);
|
|---|
| [9b68fc] | 165 | std::transform(
|
|---|
| 166 | return_charges.begin(), return_charges.end(),
|
|---|
| [d8ed62] | 167 | _average_charges.begin(),
|
|---|
| 168 | _average_charges.begin(),
|
|---|
| [9b68fc] | 169 | std::plus<PartialNucleiChargeFitter::charge_t>());
|
|---|
| [c4a323] | 170 | }
|
|---|
| [d8ed62] | 171 | if (NoFragments != 0)
|
|---|
| 172 | std::transform(_average_charges.begin(), _average_charges.end(),
|
|---|
| 173 | _average_charges.begin(),
|
|---|
| 174 | std::bind1st(std::multiplies<PartialNucleiChargeFitter::charge_t>(),1./(double)NoFragments)
|
|---|
| 175 | );
|
|---|
| 176 | LOG(2, "DEBUG: final averaged charges are " << _average_charges);
|
|---|
| 177 |
|
|---|
| 178 | return NoFragments;
|
|---|
| 179 | }
|
|---|
| 180 |
|
|---|
| [01120c] | 181 | inline SerializablePotential::ParticleTypes_t
|
|---|
| 182 | getParticleTypesForAtomIdSet(const AtomIdSet &_atoms)
|
|---|
| 183 | {
|
|---|
| 184 | SerializablePotential::ParticleTypes_t particletypes;
|
|---|
| 185 | particletypes.resize(_atoms.size());
|
|---|
| 186 | std::transform(
|
|---|
| 187 | _atoms.begin(), _atoms.end(),
|
|---|
| 188 | particletypes.begin(),
|
|---|
| 189 | boost::bind(&atom::getElementNo, _1));
|
|---|
| 190 | return particletypes;
|
|---|
| 191 | }
|
|---|
| 192 |
|
|---|
| [c1ec8e] | 193 | static
|
|---|
| 194 | std::set<KeySet> accumulateKeySetsForAtoms(
|
|---|
| 195 | const AtomFragmentsMap::AtomFragmentsMap_t &_atommap,
|
|---|
| 196 | const std::vector<const atom *> &_selected_atoms)
|
|---|
| [d8ed62] | 197 | {
|
|---|
| [01120c] | 198 | std::set<KeySet> fragments;
|
|---|
| [c1ec8e] | 199 | for (std::vector<const atom *>::const_iterator iter = _selected_atoms.begin();
|
|---|
| 200 | iter != _selected_atoms.end(); ++iter) {
|
|---|
| 201 | const atomId_t atomid = (*iter)->getId();
|
|---|
| [d8ed62] | 202 | const AtomFragmentsMap::AtomFragmentsMap_t::const_iterator atomiter =
|
|---|
| [c1ec8e] | 203 | _atommap.find(atomid);
|
|---|
| 204 | if ((*iter)->getElementNo() != 1) {
|
|---|
| 205 | if (atomiter == _atommap.end()) {
|
|---|
| 206 | ELOG(2, "There are no fragments associated to " << atomid << ".");
|
|---|
| [01120c] | 207 | continue;
|
|---|
| 208 | }
|
|---|
| 209 | const AtomFragmentsMap::keysets_t &keysets = atomiter->second;
|
|---|
| [c1ec8e] | 210 | LOG(2, "DEBUG: atom " << atomid << " has " << keysets.size() << " fragments.");
|
|---|
| [01120c] | 211 | fragments.insert( keysets.begin(), keysets.end() );
|
|---|
| 212 | } else {
|
|---|
| [c1ec8e] | 213 | LOG(3, "DEBUG: Skipping atom " << atomid << " as it's hydrogen.");
|
|---|
| [a9099d] | 214 | }
|
|---|
| 215 | }
|
|---|
| [c1ec8e] | 216 | return fragments;
|
|---|
| 217 | }
|
|---|
| [a9099d] | 218 |
|
|---|
| [c1ec8e] | 219 | static
|
|---|
| 220 | void getKeySetsToGraphMapping(
|
|---|
| [91f907] | 221 | detail::KeysetsToGraph_t &_keyset_graphs,
|
|---|
| 222 | detail::GraphFittedChargeMap_t &_fittedcharges_per_fragment,
|
|---|
| [c1ec8e] | 223 | const std::set<KeySet> &_fragments,
|
|---|
| 224 | const AtomFragmentsMap &_atomfragments)
|
|---|
| 225 | {
|
|---|
| 226 | for (std::set<KeySet>::const_iterator fragmentiter = _fragments.begin();
|
|---|
| 227 | fragmentiter != _fragments.end(); ++fragmentiter) {
|
|---|
| [01120c] | 228 | const KeySet &keyset = *fragmentiter;
|
|---|
| [c1ec8e] | 229 | const AtomFragmentsMap::indices_t &forceindices = _atomfragments.getFullKeyset(keyset);
|
|---|
| [01120c] | 230 | ASSERT( !forceindices.empty(),
|
|---|
| [c1ec8e] | 231 | "getKeySetsToGraphMapping() - force keyset to "+toString(keyset)+" is empty.");
|
|---|
| [01120c] | 232 | KeySet forcekeyset;
|
|---|
| 233 | forcekeyset.insert(forceindices.begin(), forceindices.end());
|
|---|
| 234 | forcekeyset.erase(-1);
|
|---|
| 235 | const HomologyGraph graph(forcekeyset);
|
|---|
| 236 | LOG(2, "DEBUG: Associating keyset " << forcekeyset << " with graph " << graph);
|
|---|
| [c1ec8e] | 237 | _keyset_graphs.insert( std::make_pair(keyset, graph) );
|
|---|
| 238 | _fittedcharges_per_fragment.insert( std::make_pair(graph, PartialNucleiChargeFitter::charges_t()) );
|
|---|
| [01120c] | 239 | }
|
|---|
| [c1ec8e] | 240 | }
|
|---|
| [d8ed62] | 241 |
|
|---|
| [c1ec8e] | 242 | static
|
|---|
| 243 | bool getPartialChargesForAllGraphs(
|
|---|
| [91f907] | 244 | detail::GraphFittedChargeMap_t &_fittedcharges_per_fragment,
|
|---|
| [c1ec8e] | 245 | const HomologyContainer &_homologies,
|
|---|
| 246 | const double _mask_radius,
|
|---|
| 247 | const bool enforceZeroCharge)
|
|---|
| 248 | {
|
|---|
| [91f907] | 249 | for (detail::GraphFittedChargeMap_t::iterator graphiter = _fittedcharges_per_fragment.begin();
|
|---|
| [c1ec8e] | 250 | graphiter != _fittedcharges_per_fragment.end(); ++graphiter) {
|
|---|
| [01120c] | 251 | const HomologyGraph &graph = graphiter->first;
|
|---|
| 252 | LOG(2, "DEBUG: Now fitting charges to graph " << graph);
|
|---|
| [c1ec8e] | 253 | const HomologyContainer::range_t range = _homologies.getHomologousGraphs(graph);
|
|---|
| [d8ed62] | 254 | if (range.first == range.second) {
|
|---|
| [c1ec8e] | 255 | ELOG(0, "HomologyContainer does not contain specified fragment.");
|
|---|
| 256 | return false;
|
|---|
| [d8ed62] | 257 | }
|
|---|
| 258 |
|
|---|
| 259 | // fit and average partial charges over all homologous fragments
|
|---|
| [01120c] | 260 | PartialNucleiChargeFitter::charges_t &averaged_charges = graphiter->second;
|
|---|
| [d8ed62] | 261 | const size_t NoFragments =
|
|---|
| [c1ec8e] | 262 | obtainAverageChargesOverFragments(averaged_charges, range, _mask_radius);
|
|---|
| [d8ed62] | 263 | if ((NoFragments == 0) && (range.first != range.second)) {
|
|---|
| [c1ec8e] | 264 | ELOG(0, "Fitting for fragment "+toString(*graphiter)+" failed.");
|
|---|
| 265 | return false;
|
|---|
| [d8ed62] | 266 | }
|
|---|
| 267 |
|
|---|
| 268 | // make sum of charges zero if desired
|
|---|
| [c1ec8e] | 269 | if (enforceZeroCharge)
|
|---|
| [d8ed62] | 270 | enforceZeroTotalCharge(averaged_charges);
|
|---|
| 271 |
|
|---|
| 272 | // output status info fitted charges
|
|---|
| [01120c] | 273 | LOG(2, "DEBUG: For fragment " << *graphiter << " we have fitted the following charges "
|
|---|
| [d8ed62] | 274 | << averaged_charges << ", averaged over " << NoFragments << " fragments.");
|
|---|
| 275 | }
|
|---|
| [c1ec8e] | 276 | return true;
|
|---|
| 277 | }
|
|---|
| [d8ed62] | 278 |
|
|---|
| [c1ec8e] | 279 | double fitAverageChargeToAtom(
|
|---|
| 280 | const atom * const _walker,
|
|---|
| 281 | const AtomFragmentsMap &_atomfragments,
|
|---|
| [91f907] | 282 | const detail::KeysetsToGraph_t &_keyset_graphs,
|
|---|
| 283 | const detail::GraphFittedChargeMap_t &_fittedcharges_per_fragment)
|
|---|
| [c1ec8e] | 284 | {
|
|---|
| 285 | const atom * surrogate = _walker;
|
|---|
| 286 | if (surrogate->getElementNo() == 1) {
|
|---|
| 287 | // it's hydrogen, check its bonding and use its bond partner instead to request
|
|---|
| 288 | // keysets
|
|---|
| 289 | const BondList &ListOfBonds = surrogate->getListOfBonds();
|
|---|
| 290 | if ( ListOfBonds.size() != 1) {
|
|---|
| 291 | ELOG(1, "Solitary hydrogen in atom " << surrogate->getId() << " detected, skipping.");
|
|---|
| 292 | return 0.;
|
|---|
| [d8ed62] | 293 | }
|
|---|
| [c1ec8e] | 294 | surrogate = (*ListOfBonds.begin())->GetOtherAtom(surrogate);
|
|---|
| 295 | }
|
|---|
| 296 | const atomId_t walkerid = surrogate->getId();
|
|---|
| 297 | const AtomFragmentsMap::AtomFragmentsMap_t &atommap = _atomfragments.getMap();
|
|---|
| 298 | const AtomFragmentsMap::AtomFragmentsMap_t::const_iterator keysetsiter =
|
|---|
| 299 | atommap.find(walkerid);
|
|---|
| 300 | ASSERT(keysetsiter != atommap.end(),
|
|---|
| 301 | "fitAverageChargeToAtom() - we checked already that "+toString(walkerid)
|
|---|
| 302 | +" should be present!");
|
|---|
| 303 | const AtomFragmentsMap::keysets_t & keysets = keysetsiter->second;
|
|---|
| 304 |
|
|---|
| 305 | double average_charge = 0.;
|
|---|
| 306 | size_t NoFragments = 0;
|
|---|
| 307 | // go over all fragments associated to this atom
|
|---|
| 308 | for (AtomFragmentsMap::keysets_t::const_iterator keysetsiter = keysets.begin();
|
|---|
| 309 | keysetsiter != keysets.end(); ++keysetsiter) {
|
|---|
| 310 | const KeySet &keyset = *keysetsiter;
|
|---|
| 311 |
|
|---|
| 312 | const AtomFragmentsMap::indices_t &forcekeyset = _atomfragments.getFullKeyset(keyset);
|
|---|
| 313 | ASSERT( !forcekeyset.empty(),
|
|---|
| 314 | "fitAverageChargeToAtom() - force keyset to "+toString(keyset)+" is empty.");
|
|---|
| 315 |
|
|---|
| 316 | // find the associated charge in the charge vector
|
|---|
| [91f907] | 317 | const std::map<KeySet, HomologyGraph>::const_iterator keysetgraphiter =
|
|---|
| [c1ec8e] | 318 | _keyset_graphs.find(keyset);
|
|---|
| 319 | ASSERT( keysetgraphiter != _keyset_graphs.end(),
|
|---|
| 320 | "fitAverageChargeToAtom() - keyset "+toString(keyset)
|
|---|
| 321 | +" not contained in keyset_graphs.");
|
|---|
| 322 | const HomologyGraph &graph = keysetgraphiter->second;
|
|---|
| [91f907] | 323 | const detail::GraphFittedChargeMap_t::const_iterator chargesiter =
|
|---|
| [c1ec8e] | 324 | _fittedcharges_per_fragment.find(graph);
|
|---|
| 325 | ASSERT(chargesiter != _fittedcharges_per_fragment.end(),
|
|---|
| 326 | "fitAverageChargeToAtom() - no charge to "+toString(keyset)
|
|---|
| 327 | +" any longer present in fittedcharges_per_fragment?");
|
|---|
| 328 | const PartialNucleiChargeFitter::charges_t &charges = chargesiter->second;
|
|---|
| 329 | ASSERT( charges.size() == forcekeyset.size(),
|
|---|
| 330 | "fitAverageChargeToAtom() - charges "+toString(charges.size())+" and keyset "
|
|---|
| 331 | +toString(forcekeyset.size())+" do not have the same length?");
|
|---|
| 332 | PartialNucleiChargeFitter::charges_t::const_iterator chargeiter =
|
|---|
| 333 | charges.begin();
|
|---|
| 334 | const AtomFragmentsMap::indices_t::const_iterator forcekeysetiter =
|
|---|
| 335 | std::find(forcekeyset.begin(), forcekeyset.end(), _walker->getId());
|
|---|
| 336 | ASSERT( forcekeysetiter != forcekeyset.end(),
|
|---|
| 337 | "fitAverageChargeToAtom() - atom "+toString(_walker->getId())
|
|---|
| 338 | +" not contained in force keyset "+toString(forcekeyset));
|
|---|
| 339 | std::advance(chargeiter, std::distance(forcekeyset.begin(), forcekeysetiter));
|
|---|
| 340 |
|
|---|
| 341 | // and add onto charge sum
|
|---|
| 342 | const double & charge_in_fragment = *chargeiter;
|
|---|
| 343 | average_charge += charge_in_fragment;
|
|---|
| 344 | ++NoFragments;
|
|---|
| [d8ed62] | 345 | }
|
|---|
| [c1ec8e] | 346 | // average to obtain final partial charge for this atom
|
|---|
| 347 | average_charge *= 1./(double)NoFragments;
|
|---|
| [d8ed62] | 348 |
|
|---|
| [c1ec8e] | 349 | return average_charge;
|
|---|
| 350 | }
|
|---|
| 351 |
|
|---|
| 352 | void addToParticleRegistry(
|
|---|
| 353 | const ParticleFactory &factory,
|
|---|
| 354 | const periodentafel &periode,
|
|---|
| [91f907] | 355 | const detail::fitted_charges_t &_fitted_charges,
|
|---|
| 356 | const detail::GraphIndices_t &_GraphIndices,
|
|---|
| 357 | detail::AtomParticleNames_t &_atom_particlenames)
|
|---|
| [c1ec8e] | 358 | {
|
|---|
| [91f907] | 359 | detail::GraphToNameMap_t GraphToNameMap;
|
|---|
| 360 | for (detail::fitted_charges_t::const_iterator chargeiter = _fitted_charges.begin();
|
|---|
| [c1ec8e] | 361 | chargeiter != _fitted_charges.end(); ++chargeiter) {
|
|---|
| [91f907] | 362 | const atomId_t &atomid = chargeiter->first;
|
|---|
| 363 | const detail::GraphIndices_t::right_const_iterator graphiter = _GraphIndices.right.find(atomid);
|
|---|
| 364 | const detail::GraphToNameMap_t::const_iterator nameiter = GraphToNameMap.find(graphiter->second);
|
|---|
| 365 | std::string name;
|
|---|
| 366 | if (nameiter != GraphToNameMap.end()) {
|
|---|
| 367 | name = nameiter->second;
|
|---|
| 368 | } else {
|
|---|
| 369 | const atom * const walker = World::getInstance().getAtom(AtomById(atomid));
|
|---|
| 370 | ASSERT( walker != NULL,
|
|---|
| 371 | "addToParticleRegistry() - atom "+toString(atomid)+" not present in the World?");
|
|---|
| 372 | const double &charge = chargeiter->second;
|
|---|
| 373 | const atomicNumber_t elementno = walker->getElementNo();
|
|---|
| 374 | name = Particle::findFreeName(periode, elementno);
|
|---|
| 375 | GraphToNameMap[graphiter->second] = name;
|
|---|
| 376 | LOG(1, "INFO: Adding particle " << name << " for atom "
|
|---|
| 377 | << *walker << " with element " << elementno << ", charge " << charge);
|
|---|
| 378 | factory.createInstance(name, elementno, charge);
|
|---|
| 379 | }
|
|---|
| 380 | _atom_particlenames.insert( std::make_pair(atomid, name) );
|
|---|
| [708ec1] | 381 | }
|
|---|
| [c1ec8e] | 382 | }
|
|---|
| 383 |
|
|---|
| 384 | ActionState::ptr PotentialFitPartialChargesAction::performCall()
|
|---|
| 385 | {
|
|---|
| 386 | // check for selected atoms
|
|---|
| 387 | const World &world = const_cast<const World &>(World::getInstance());
|
|---|
| 388 | if (world.beginAtomSelection() == world.endAtomSelection()) {
|
|---|
| 389 | STATUS("There are no atoms selected for fitting partial charges to.");
|
|---|
| 390 | return Action::failure;
|
|---|
| 391 | }
|
|---|
| 392 |
|
|---|
| 393 | /// obtain possible fragments to each selected atom
|
|---|
| 394 | const AtomFragmentsMap &atomfragments = AtomFragmentsMap::getConstInstance();
|
|---|
| 395 | if (!atomfragments.checkCompleteness()) {
|
|---|
| 396 | STATUS("AtomFragmentsMap failed internal consistency check, missing forcekeysets?");
|
|---|
| 397 | return Action::failure;
|
|---|
| 398 | }
|
|---|
| 399 | std::set<KeySet> fragments =
|
|---|
| 400 | accumulateKeySetsForAtoms( atomfragments.getMap(), world.getSelectedAtoms());
|
|---|
| 401 |
|
|---|
| 402 | // reduce given fragments to homologous graphs to avoid multiple fittings
|
|---|
| [91f907] | 403 | detail::KeysetsToGraph_t keyset_graphs;
|
|---|
| 404 | detail::GraphFittedChargeMap_t fittedcharges_per_fragment;
|
|---|
| [c1ec8e] | 405 | getKeySetsToGraphMapping(keyset_graphs, fittedcharges_per_fragment, fragments, atomfragments);
|
|---|
| 406 |
|
|---|
| 407 | /// then go through all fragments and get partial charges for each
|
|---|
| [91f907] | 408 | const HomologyContainer &homologies = World::getInstance().getHomologies();
|
|---|
| [c1ec8e] | 409 | const bool status = getPartialChargesForAllGraphs(
|
|---|
| 410 | fittedcharges_per_fragment,
|
|---|
| [91f907] | 411 | homologies,
|
|---|
| [c1ec8e] | 412 | params.radius.get(),
|
|---|
| 413 | params.enforceZeroCharge.get());
|
|---|
| 414 | if (!status)
|
|---|
| 415 | return Action::failure;
|
|---|
| 416 |
|
|---|
| 417 | /// obtain average charge for each atom the fitted charges over all its fragments
|
|---|
| [91f907] | 418 | detail::fitted_charges_t fitted_charges;
|
|---|
| [c1ec8e] | 419 | for (World::AtomSelectionConstIterator atomiter = world.beginAtomSelection();
|
|---|
| 420 | atomiter != world.endAtomSelection(); ++atomiter) {
|
|---|
| 421 | const double average_charge = fitAverageChargeToAtom(
|
|---|
| 422 | atomiter->second, atomfragments, keyset_graphs, fittedcharges_per_fragment);
|
|---|
| 423 |
|
|---|
| 424 | if (average_charge != 0.) {
|
|---|
| 425 | LOG(2, "DEBUG: For atom " << atomiter->first << " we have an average charge of "
|
|---|
| 426 | << average_charge);
|
|---|
| 427 |
|
|---|
| 428 | fitted_charges.insert( std::make_pair(atomiter->first, average_charge) );
|
|---|
| 429 | }
|
|---|
| 430 | }
|
|---|
| 431 |
|
|---|
| [91f907] | 432 | /// make Particles be used for every atom that was fitted on the same number of graphs
|
|---|
| 433 | detail::GraphIndex_t GraphIndex;
|
|---|
| 434 | size_t index = 0;
|
|---|
| 435 | for (HomologyContainer::const_key_iterator iter = homologies.key_begin();
|
|---|
| 436 | iter != homologies.key_end(); iter = homologies.getNextKey(iter)) {
|
|---|
| 437 | GraphIndex.insert( std::make_pair( *iter, index++));
|
|---|
| 438 | }
|
|---|
| 439 | LOG(2, "DEBUG: There are " << index << " unique graphs in the homology container.");
|
|---|
| 440 |
|
|---|
| 441 | // go through every atom, get all graphs, convert to GraphIndex and store
|
|---|
| 442 |
|
|---|
| 443 | detail::GraphIndices_t GraphIndices;
|
|---|
| 444 | const AtomFragmentsMap::AtomFragmentsMap_t &atommap = atomfragments.getMap();
|
|---|
| 445 | for (World::AtomSelectionConstIterator atomiter = world.beginAtomSelection();
|
|---|
| 446 | atomiter != world.endAtomSelection(); ++atomiter) {
|
|---|
| 447 | const atomId_t walkerid = atomiter->first;
|
|---|
| 448 | const AtomFragmentsMap::AtomFragmentsMap_t::const_iterator keysetsiter =
|
|---|
| 449 | atommap.find(walkerid);
|
|---|
| 450 | ASSERT(keysetsiter != atommap.end(),
|
|---|
| 451 | "PotentialFitPartialChargesAction::performCall() - we checked already that "
|
|---|
| 452 | +toString(walkerid)+" should be present!");
|
|---|
| 453 | const AtomFragmentsMap::keysets_t & keysets = keysetsiter->second;
|
|---|
| 454 |
|
|---|
| 455 | detail::AtomsGraphIndices_t AtomsGraphIndices;
|
|---|
| 456 |
|
|---|
| 457 | // go over all fragments associated to this atom
|
|---|
| 458 | for (AtomFragmentsMap::keysets_t::const_iterator keysetsiter = keysets.begin();
|
|---|
| 459 | keysetsiter != keysets.end(); ++keysetsiter) {
|
|---|
| 460 | const KeySet &keyset = *keysetsiter;
|
|---|
| 461 | const std::map<KeySet, HomologyGraph>::const_iterator keysetgraphiter =
|
|---|
| 462 | keyset_graphs.find(keyset);
|
|---|
| 463 | ASSERT( keysetgraphiter != keyset_graphs.end(),
|
|---|
| 464 | "PotentialFitPartialChargesAction::performCall() - keyset "+toString(keyset)
|
|---|
| 465 | +" not contained in keyset_graphs.");
|
|---|
| 466 | const HomologyGraph &graph = keysetgraphiter->second;
|
|---|
| 467 | const detail::GraphIndex_t::const_iterator indexiter = GraphIndex.find(graph);
|
|---|
| 468 | ASSERT( indexiter != GraphIndex.end(),
|
|---|
| 469 | "PotentialFitPartialChargesAction::performCall() - graph "+toString(graph)
|
|---|
| 470 | +" not contained in GraphIndex.");
|
|---|
| 471 | AtomsGraphIndices.insert( indexiter->second );
|
|---|
| 472 | }
|
|---|
| 473 | GraphIndices.left.insert( std::make_pair(AtomsGraphIndices, walkerid) );
|
|---|
| 474 | LOG(2, "DEBUG: Atom " << *atomiter->second << " has graph indices " << AtomsGraphIndices);
|
|---|
| 475 | }
|
|---|
| 476 |
|
|---|
| [c1ec8e] | 477 | /// place all fitted charges into ParticleRegistry
|
|---|
| [91f907] | 478 | detail::AtomParticleNames_t atom_particlenames;
|
|---|
| [c1ec8e] | 479 | addToParticleRegistry(
|
|---|
| 480 | ParticleFactory::getConstInstance(),
|
|---|
| 481 | *World::getInstance().getPeriode(),
|
|---|
| [91f907] | 482 | fitted_charges,
|
|---|
| 483 | GraphIndices,
|
|---|
| 484 | atom_particlenames);
|
|---|
| 485 | LOG(1, "INFO: The atoms received the following particles " << atom_particlenames);
|
|---|
| [c4a323] | 486 |
|
|---|
| 487 | return Action::success;
|
|---|
| 488 | }
|
|---|
| 489 |
|
|---|
| [50d49d] | 490 | ActionState::ptr PotentialFitPartialChargesAction::performUndo(ActionState::ptr _state) {
|
|---|
| [c4a323] | 491 | return Action::success;
|
|---|
| 492 | }
|
|---|
| 493 |
|
|---|
| [50d49d] | 494 | ActionState::ptr PotentialFitPartialChargesAction::performRedo(ActionState::ptr _state){
|
|---|
| [c4a323] | 495 | return Action::success;
|
|---|
| 496 | }
|
|---|
| 497 |
|
|---|
| [50d49d] | 498 | bool PotentialFitPartialChargesAction::canUndo() {
|
|---|
| [c4a323] | 499 | return false;
|
|---|
| 500 | }
|
|---|
| 501 |
|
|---|
| [50d49d] | 502 | bool PotentialFitPartialChargesAction::shouldUndo() {
|
|---|
| [c4a323] | 503 | return false;
|
|---|
| 504 | }
|
|---|
| 505 | /** =========== end of function ====================== */
|
|---|