Ignore:
Timestamp:
May 18, 2016, 10:02:53 PM (9 years ago)
Author:
Frederik Heber <heber@…>
Branches:
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
Children:
01120c
Parents:
d9dbef
git-author:
Frederik Heber <heber@…> (03/07/16 13:51:28)
git-committer:
Frederik Heber <heber@…> (05/18/16 22:02:53)
Message:

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.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/Actions/PotentialAction/FitPartialChargesAction.cpp

    rd9dbef rd8ed62  
    5858#include "Element/element.hpp"
    5959#include "Element/periodentafel.hpp"
     60#include "Fragmentation/Homology/AtomFragmentsMap.hpp"
    6061#include "Fragmentation/Homology/HomologyContainer.hpp"
    6162#include "Fragmentation/Homology/HomologyGraph.hpp"
     
    117118}
    118119
    119 ActionState::ptr PotentialFitPartialChargesAction::performCall() {
    120 
    121   // fragment specifies the homology fragment to use
    122   SerializablePotential::ParticleTypes_t fragmentnumbers;
    123   {
    124     const std::vector<const element *> &fragment = params.fragment.get();
    125     std::transform(fragment.begin(), fragment.end(), std::back_inserter(fragmentnumbers),
    126         boost::bind(&element::getAtomicNumber, _1));
    127   }
    128 
    129   // parse homologies into container
    130   HomologyContainer &homologies = World::getInstance().getHomologies();
    131   const HomologyGraph graph = getFirstGraphwithSpecifiedElements(homologies,fragmentnumbers);
    132   HomologyContainer::range_t range = homologies.getHomologousGraphs(graph);
    133   // for the moment just use the very first fragment
    134   if (range.first == range.second) {
    135     STATUS("HomologyContainer does not contain specified fragment.");
    136     return Action::failure;
    137   }
    138 
    139   // average partial charges over all fragments
    140   HomologyContainer::const_iterator iter = range.first;
     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;
    141147  if (!iter->second.containsGrids) {
    142     STATUS("This HomologyGraph does not contain sampled grids.");
    143     return Action::failure;
    144   }
    145   PartialNucleiChargeFitter::charges_t averaged_charges;
    146   averaged_charges.resize(iter->second.fragment.getCharges().size(), 0.);
     148    ELOG(1, "This HomologyGraph does not contain sampled grids.");
     149    return 0;
     150  }
     151  _average_charges.resize(iter->second.fragment.getCharges().size(), 0.);
    147152  size_t NoFragments = 0;
    148153  for (;
    149       iter != range.second; ++iter, ++NoFragments) {
     154      iter != _range.second; ++iter, ++NoFragments) {
    150155    if (!iter->second.containsGrids) {
    151156      ELOG(2, "This HomologyGraph does not contain sampled grids,\ndid you forget to add '--store-grids 1' to AnalyseFragmentResults.");
    152       return Action::failure;
     157      return 0;
    153158    }
    154159    const Fragment &fragment = iter->second.fragment;
     
    161166            && (potential.begin[2] == potential.end[2]))) {
    162167      ELOG(1, "Sampled grid contains grid made of zero points.");
    163       return Action::failure;
     168      return 0;
    164169    }
    165170
     
    171176      positions.push_back( Vector(pos[0], pos[1], pos[2]) );
    172177    }
    173     PartialNucleiChargeFitter fitter(potential, positions, params.radius.get());
     178    PartialNucleiChargeFitter fitter(potential, positions, _radius);
    174179    fitter();
    175180    PartialNucleiChargeFitter::charges_t return_charges = fitter.getSolutionAsCharges_t();
     181    LOG(2, "DEBUG: fitted charges are " << return_charges);
    176182    std::transform(
    177183        return_charges.begin(), return_charges.end(),
    178         averaged_charges.begin(),
    179         averaged_charges.begin(),
     184        _average_charges.begin(),
     185        _average_charges.begin(),
    180186        std::plus<PartialNucleiChargeFitter::charge_t>());
    181187  }
    182   std::transform(averaged_charges.begin(),averaged_charges.end(),
    183       averaged_charges.begin(),
    184       std::bind1st(std::multiplies<PartialNucleiChargeFitter::charge_t>(),1./NoFragments)
    185   );
    186 
    187   // make sum of charges zero if desired
    188   if (params.enforceZeroCharge.get()) {
    189     double charge_sum = 0.;
    190     charge_sum = std::accumulate(averaged_charges.begin(), averaged_charges.end(), charge_sum);
    191     if (fabs(charge_sum) > MYEPSILON) {
    192       std::transform(
    193           averaged_charges.begin(), averaged_charges.end(),
    194           averaged_charges.begin(),
    195           boost::bind(std::minus<double>(), _1, charge_sum/averaged_charges.size()));
    196     }
    197     charge_sum = 0.;
    198     charge_sum = std::accumulate(averaged_charges.begin(), averaged_charges.end(), charge_sum);
    199     ASSERT( fabs(charge_sum) < MYEPSILON,
    200         "PotentialFitPartialChargesAction::performCall() - enforcing neutral net charge failed, "
    201         +toString(charge_sum)+" is the remaining net charge.");
    202   }
    203 
    204   // place all fitted charges into ParticleRegistry
     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
    205312  const ParticleFactory &factory =
    206313      const_cast<const ParticleFactory&>(ParticleFactory::getInstance());
    207314  const periodentafel &periode = *World::getInstance().getPeriode();
    208   ASSERT(averaged_charges.size() == fragmentnumbers.size(),
    209       "PotentialFitPartialChargesAction::performCall() - charges and elements length mismatch.");
    210   for (size_t i=0;i<averaged_charges.size(); ++i) {
    211     const std::string name = Particle::findFreeName(periode, fragmentnumbers[i]);
    212     LOG(2, "INFO: Adding particle " << name << " for element "
    213         << fragmentnumbers[i] << ", charge " << averaged_charges[i]);
    214     factory.createInstance(name, fragmentnumbers[i], averaged_charges[i]);
    215   }
    216 
    217   // output fitted charges
    218   LOG(0, "STATUS: We have fitted the following charges " << averaged_charges
    219       << ", averaged over " << NoFragments << " fragments.");
     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  }
    220328
    221329  return Action::success;
Note: See TracChangeset for help on using the changeset viewer.