Changeset d8ed62
- Timestamp:
- May 18, 2016, 10:02:53 PM (9 years ago)
- 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)
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
doc/userguide/userguide.xml
rd9dbef rd8ed62 1845 1845 behavior in the molecular fragment, namely the (covalently) bonded interaction. 1846 1846 In order to model the Coulomb long-range interaction as well without solving 1847 for the electronic ground state in each time step, parti clecharges1847 for the electronic ground state in each time step, partial charges 1848 1848 are used that capture to some degree the created dipoles due to 1849 charge transfer from one atom to another when bonded.</para> 1850 <para>Note that so far the placement of partial charges is restricted to the position of nuclei in the molecular system. There are more complex ways of placing partial charges, e.g. as employed in higher TIP water molecules, that also use anti-bonding potentials. This is so far not implemented.</para> 1849 charge transfer from one atom to another when bonded. These are called 1850 partial charges because they combine both nuclei and electronic 1851 charges to yield an in general fractional charge.</para> 1852 <para>Note that so far the placement of partial charges is restricted 1853 to the position of nuclei in the molecular system. There are more 1854 complex ways of placing partial charges, e.g. as employed in higher 1855 TIP water molecules, that also use anti-bonding potentials. This is 1856 so far not implemented.</para> 1851 1857 <para>To allow least-squares regression of these partial charges, we 1852 1858 need the results of long-range calculations and the <emphasis role="bold">store-grids</emphasis> 1853 option (see above under <link linkend="fragmentation">Fragmentation </link>) must have been given. With these sampled charge density and 1854 Coulomb potential stored in the homology containers, we call this 1855 action as follows.</para> 1859 option (see above under <link linkend="fragmentation">Fragmentation </link>) 1860 must have been given.</para> 1861 <para>Furthermore, we require associations between selected atoms and 1862 the fragments, residing in the <link linkend="homology">Homology container</link>. 1863 These are contained in the <link linkend="atomfragments">AtomFragments association</link> 1864 container, that can also be parsed and stored.</para> 1865 <para>With these sampled charge density and Coulomb potential stored 1866 in the homology containers, we call this action as follows.</para> 1856 1867 <programlisting> 1857 1868 ... --fit-partial-charges \ 1858 --fragment-charges 8 1 1 \ 1859 --potential-file water.potentials \ 1860 --radius 0.2 1861 </programlisting> 1862 <para>This will again use a water molecule as homologous fragment 1863 "key" to request all configurations of this type from the homologies container. Results are 1864 stored in <filename>water.potentials</filename>. The radius is used 1865 to mark the region directly around the nuclei from the fit 1866 procedure. As here the charges of the core electrons and the nuclei 1867 itself dominate, we however are only interested in a good 1869 --potential-file water.particles \ 1870 --radius 1.5 1871 </programlisting> 1872 <para>Assume that a water molecule has been selected previously. Then 1873 all homologous fragments that contain any of the water molecules are 1874 used as "key" to request all configurations of this type 1875 from the homologies container. For each of the atoms then an average 1876 partial charge is computed by fitting their respective Coulomb 1877 potential to the obtained from the fragment calculations. Resulting 1878 values are stored in <filename>water.particles</filename>. The 1879 radius is used to mask a certain region directly around the nuclei 1880 from the fit procedure. As here the charges of the core electrons and 1881 the nuclei itself dominate, we however are only interested in a good 1868 1882 approximation to the long-range potential, this mask radius allows 1869 1883 to give the range of the excluded zone.</para> -
src/Actions/PotentialAction/FitPartialChargesAction.cpp
rd9dbef rd8ed62 58 58 #include "Element/element.hpp" 59 59 #include "Element/periodentafel.hpp" 60 #include "Fragmentation/Homology/AtomFragmentsMap.hpp" 60 61 #include "Fragmentation/Homology/HomologyContainer.hpp" 61 62 #include "Fragmentation/Homology/HomologyGraph.hpp" … … 117 118 } 118 119 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; 120 void 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 138 size_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; 141 147 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.); 147 152 size_t NoFragments = 0; 148 153 for (; 149 iter != range.second; ++iter, ++NoFragments) {154 iter != _range.second; ++iter, ++NoFragments) { 150 155 if (!iter->second.containsGrids) { 151 156 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; 153 158 } 154 159 const Fragment &fragment = iter->second.fragment; … … 161 166 && (potential.begin[2] == potential.end[2]))) { 162 167 ELOG(1, "Sampled grid contains grid made of zero points."); 163 return Action::failure;168 return 0; 164 169 } 165 170 … … 171 176 positions.push_back( Vector(pos[0], pos[1], pos[2]) ); 172 177 } 173 PartialNucleiChargeFitter fitter(potential, positions, params.radius.get());178 PartialNucleiChargeFitter fitter(potential, positions, _radius); 174 179 fitter(); 175 180 PartialNucleiChargeFitter::charges_t return_charges = fitter.getSolutionAsCharges_t(); 181 LOG(2, "DEBUG: fitted charges are " << return_charges); 176 182 std::transform( 177 183 return_charges.begin(), return_charges.end(), 178 averaged_charges.begin(),179 averaged_charges.begin(),184 _average_charges.begin(), 185 _average_charges.begin(), 180 186 std::plus<PartialNucleiChargeFitter::charge_t>()); 181 187 } 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 198 ActionState::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 205 312 const ParticleFactory &factory = 206 313 const_cast<const ParticleFactory&>(ParticleFactory::getInstance()); 207 314 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 } 220 328 221 329 return Action::success; -
src/Actions/PotentialAction/FitPartialChargesAction.def
rd9dbef rd8ed62 19 19 // ValueStorage by the token "Z" -> first column: int, Z, "Z" 20 20 // "undefine" if no parameters are required, use (NOPARAM_DEFAULT) for each (undefined) default value 21 #define paramtypes ( std::vector<const element *>)(double)(bool)22 #define paramtokens (" fragment-charges")("radius")("enforce-net-zero-charge")23 #define paramdescriptions (" charges specifying the fragment")("radius of sphere around nuclei where potential does not need to match")("whether to make the sum of fitted charged become zero")24 #define paramdefaults ( NOPARAM_DEFAULT)(PARAM_DEFAULT(0))(PARAM_DEFAULT(1))25 #define paramreferences ( fragment)(radius)(enforceZeroCharge)21 #define paramtypes (double)(bool) 22 #define paramtokens ("radius")("enforce-net-zero-charge") 23 #define paramdescriptions ("radius of sphere around nuclei where potential does not need to match")("whether to make the sum of fitted charged become zero") 24 #define paramdefaults (PARAM_DEFAULT(0))(PARAM_DEFAULT(1)) 25 #define paramreferences (radius)(enforceZeroCharge) 26 26 #define paramvalids \ 27 (STLVectorValidator< std::vector<const element *> >(1,99, ElementValidator())) \28 27 (NonNegativeValidator<double>()) \ 29 28 (DummyValidator<bool>()) … … 41 40 42 41 // finally the information stored in the ActionTrait specialization 43 #define DESCRIPTION "fits partial nuclear charges to present particle types from loaded homologies containing sampledgrids."42 #define DESCRIPTION "fits partial nuclear charges to selected atoms from averages over homologous fragments containing sampled charge grids." 44 43 #undef SHORTFORM -
tests/regression/Potential/FitPartialCharges/testsuite-potential-fit-partial-charges.at
rd9dbef rd8ed62 24 24 AT_KEYWORDS([potential parse-homologies fit-partial-charges save-particle-parameters]) 25 25 AT_SKIP_IF([../../molecuilder --help fit-partial-charges; if test $? -eq 5; then /bin/true; else /bin/false; fi]) 26 AT_XFAIL_IF([/bin/true]) 26 27 27 28 # homology file created with water.pdb and as follows:
Note:
See TracChangeset
for help on using the changeset viewer.