source: src/Fragmentation/Fragmentation.cpp@ 339910

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 Candidate_v1.7.0 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 339910 was 339910, checked in by Frederik Heber <heber@…>, 13 years ago

Fragmentation now operates on a single molecule.

  • Property mode set to 100644
File size: 32.2 KB
Line 
1/*
2 * Project: MoleCuilder
3 * Description: creates and alters molecular systems
4 * Copyright (C) 2010-2012 University of Bonn. 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 * Fragmentation.cpp
25 *
26 * Created on: Oct 18, 2011
27 * Author: heber
28 */
29
30#ifdef HAVE_CONFIG_H
31#include <config.h>
32#endif
33
34#include "CodePatterns/MemDebug.hpp"
35
36#include "Fragmentation.hpp"
37
38#include "CodePatterns/Assert.hpp"
39#include "CodePatterns/Info.hpp"
40#include "CodePatterns/Log.hpp"
41
42#include "Atom/atom.hpp"
43#include "Bond/bond.hpp"
44#include "Descriptors/MoleculeDescriptor.hpp"
45#include "Element/element.hpp"
46#include "Element/periodentafel.hpp"
47#include "Fragmentation/AdaptivityMap.hpp"
48#include "Fragmentation/AtomMask.hpp"
49#include "Fragmentation/fragmentation_helpers.hpp"
50#include "Fragmentation/Graph.hpp"
51#include "Fragmentation/helpers.hpp"
52#include "Fragmentation/KeySet.hpp"
53#include "Fragmentation/PowerSetGenerator.hpp"
54#include "Fragmentation/UniqueFragments.hpp"
55#include "Graph/BondGraph.hpp"
56#include "Graph/CheckAgainstAdjacencyFile.hpp"
57#include "Graph/ListOfLocalAtoms.hpp"
58#include "molecule.hpp"
59#include "MoleculeLeafClass.hpp"
60#include "MoleculeListClass.hpp"
61#include "Parser/FormatParserStorage.hpp"
62#include "World.hpp"
63
64
65/** Constructor of class Fragmentation.
66 *
67 * \param _mol molecule for internal use (looking up atoms)
68 * \param _FileChecker instance contains adjacency parsed from elsewhere
69 * \param _saturation whether to treat hydrogen special and saturate dangling bonds or not
70 */
71Fragmentation::Fragmentation(molecule *_mol, CheckAgainstAdjacencyFile &_FileChecker, const enum HydrogenSaturation _saturation) :
72 mol(_mol),
73 saturation(_saturation),
74 FileChecker(_FileChecker)
75{}
76
77/** Destructor of class Fragmentation.
78 *
79 */
80Fragmentation::~Fragmentation()
81{}
82
83
84/** Performs a many-body bond order analysis for a given bond order.
85 * -# parses adjacency, keysets and orderatsite files
86 * -# RootStack is created for every subgraph (here, later we implement the "update 10 sites with highest energ
87y contribution", and that's why this consciously not done in the following loop)
88 * -# in a loop over all subgraphs
89 * -# calls FragmentBOSSANOVA with this RootStack and within the subgraph molecule structure
90 * -# creates molecule (fragment)s from the returned keysets (StoreFragmentFromKeySet)
91 * -# combines the generated molecule lists from all subgraphs
92 * -# saves to disk: fragment configs, adjacency, orderatsite, keyset files
93 * Note that as we split "this" molecule up into a list of subgraphs, i.e. a MoleculeListClass, we have two sets
94 * of vertex indices: Global always means the index in "this" molecule, whereas local refers to the molecule or
95 * subgraph in the MoleculeListClass.
96 * \param atomids atoms to fragment, used in AtomMask
97 * \param Order up to how many neighbouring bonds a fragment contains in BondOrderScheme::BottumUp scheme
98 * \param prefix prefix string for every fragment file name (may include path)
99 * \param &DFS contains bond structure analysis data
100 * \return 1 - continue, 2 - stop (no fragmentation occured)
101 */
102int Fragmentation::FragmentMolecule(const std::vector<atomId_t> &atomids, int Order, std::string prefix, DepthFirstSearchAnalysis &DFS)
103{
104 MoleculeListClass *BondFragments = NULL;
105 std::fstream File;
106 bool FragmentationToDo = true;
107 bool CheckOrder = false;
108 Graph TotalGraph; // graph with all keysets however local numbers
109 int TotalNumberOfKeySets = 0;
110 AtomMask_t AtomMask(atomids);
111
112 LOG(0, std::endl);
113 switch (saturation) {
114 case DoSaturate:
115 LOG(0, "I will treat hydrogen special and saturate dangling bonds with it.");
116 break;
117 case DontSaturate:
118 LOG(0, "Hydrogen is treated just like the rest of the lot.");
119 break;
120 default:
121 ASSERT(0, "Fragmentation::FragmentMolecule() - there is a saturation setting which I have no idea about.");
122 break;
123 }
124
125 // ++++++++++++++++++++++++++++ INITIAL STUFF: Bond structure analysis, file parsing, ... ++++++++++++++++++++++++++++++++++++++++++
126
127 // ===== 1. Check whether bond structure is same as stored in files ====
128
129 // === compare it with adjacency file ===
130 {
131 const std::vector<atomId_t> globalids = getGlobalIdsFromLocalIds(*mol, atomids);
132 FragmentationToDo = FragmentationToDo && FileChecker(globalids);
133 }
134
135 // ===== 3. if structure still valid, parse key set file and others =====
136 Graph ParsedFragmentList;
137 FragmentationToDo = FragmentationToDo && ParsedFragmentList.ParseKeySetFile(prefix);
138
139 // ===== 4. check globally whether there's something to do actually (first adaptivity check)
140 FragmentationToDo = FragmentationToDo && ParseOrderAtSiteFromFile(atomids, prefix);
141
142 // =================================== Begin of FRAGMENTATION ===============================
143 // ===== 6a. assign each keyset to its respective subgraph =====
144 ListOfLocalAtoms_t ListOfLocalAtoms;
145 Graph FragmentList;
146 AssignKeySetsToFragment(ParsedFragmentList, ListOfLocalAtoms, FragmentList, true);
147
148 // ===== 6b. prepare and go into the adaptive (Order<0), single-step (Order==0) or incremental (Order>0) cycle
149 KeyStack RootStack;
150 FragmentationToDo = false; // if CheckOrderAtSite just ones recommends fragmentation, we will save fragments afterwards
151 bool LoopDoneAlready = false;
152 while ((CheckOrder = CheckOrderAtSite(AtomMask, ParsedFragmentList, Order, prefix, LoopDoneAlready))) {
153 FragmentationToDo = FragmentationToDo || CheckOrder;
154 LoopDoneAlready = true; // last plus one entry is used as marker that we have been through this loop once already in CheckOrderAtSite()
155 // ===== 6b. fill RootStack for each subgraph (second adaptivity check) =====
156 FillRootStackForSubgraphs(RootStack, AtomMask);
157
158 // call BOSSANOVA method
159 FragmentBOSSANOVA(mol, FragmentList, RootStack);
160 }
161 LOG(2, "CheckOrder is " << CheckOrder << ".");
162
163 // ==================================== End of FRAGMENTATION ============================================
164
165 // ===== 8a. translate list into global numbers (i.e. ones that are valid in "this" molecule, not in MolecularWalker->Leaf)
166 TranslateIndicesToGlobalIDs(FragmentList, TotalNumberOfKeySets, TotalGraph);
167
168 // ===== 8b. gather keyset lists (graphs) from all subgraphs and transform into MoleculeListClass =====
169 //if (FragmentationToDo) { // we should always store the fragments again as coordination might have changed slightly without changing bond structure
170 // allocate memory for the pointer array and transmorph graphs into full molecular fragments
171 BondFragments = new MoleculeListClass(World::getPointer());
172 int k=0;
173 for(Graph::iterator runner = TotalGraph.begin(); runner != TotalGraph.end(); runner++) {
174 KeySet test = (*runner).first;
175 LOG(0, "Fragment No." << (*runner).second.first << " with TEFactor " << (*runner).second.second << ".");
176 BondFragments->insert(StoreFragmentFromKeySet(test, World::getInstance().getConfig()));
177 k++;
178 }
179 LOG(0, k << "/" << BondFragments->ListOfMolecules.size() << " fragments generated from the keysets.");
180
181 // ===== 9. Save fragments' configuration and keyset files et al to disk ===
182 if (BondFragments->ListOfMolecules.size() != 0) {
183 // create the SortIndex from BFS labels to order in the config file
184 std::map<atomId_t, int> SortIndex;
185 CreateMappingLabelsToConfigSequence(SortIndex);
186
187 LOG(1, "Writing " << BondFragments->ListOfMolecules.size() << " possible bond fragmentation configs");
188 bool write_status = true;
189 for (std::vector<std::string>::const_iterator iter = typelist.begin();
190 iter != typelist.end();
191 ++iter) {
192 LOG(2, "INFO: Writing bond fragments for type " << (*iter) << ".");
193 write_status = write_status
194 && BondFragments->OutputConfigForListOfFragments(
195 prefix,
196 FormatParserStorage::getInstance().getTypeFromName(*iter));
197 }
198 if (write_status)
199 LOG(1, "All configs written.");
200 else
201 LOG(1, "Some config writing failed.");
202
203 // store force index reference file
204 BondFragments->StoreForcesFile(prefix, SortIndex);
205
206 // store keysets file
207 TotalGraph.StoreKeySetFile(prefix);
208
209 {
210 // store Adjacency file
211 std::string filename = prefix + ADJACENCYFILE;
212 mol->StoreAdjacencyToFile(filename);
213 }
214
215 // store Hydrogen saturation correction file
216 BondFragments->AddHydrogenCorrection(prefix);
217
218 // store adaptive orders into file
219 StoreOrderAtSiteFile(prefix);
220
221 // restore orbital and Stop values
222 //CalculateOrbitals(*configuration);
223 } else {
224 LOG(1, "FragmentList is zero on return, splitting failed.");
225 }
226 // remove all create molecules again from the World including their atoms
227 for (MoleculeList::iterator iter = BondFragments->ListOfMolecules.begin();
228 !BondFragments->ListOfMolecules.empty();
229 iter = BondFragments->ListOfMolecules.begin()) {
230 // remove copied atoms and molecule again
231 molecule *mol = *iter;
232 mol->removeAtomsinMolecule();
233 World::getInstance().destroyMolecule(mol);
234 BondFragments->ListOfMolecules.erase(iter);
235 }
236 delete(BondFragments);
237 LOG(0, "End of bond fragmentation.");
238
239 return ((int)(!FragmentationToDo)+1); // 1 - continue, 2 - stop (no fragmentation occured)
240};
241
242
243/** Performs BOSSANOVA decomposition at selected sites, increasing the cutoff by one at these sites.
244 * -# constructs a complete keyset of the molecule
245 * -# In a loop over all possible roots from the given rootstack
246 * -# increases order of root site
247 * -# calls PowerSetGenerator with this order, the complete keyset and the rootkeynr
248 * -# for all consecutive lower levels PowerSetGenerator is called with the suborder, the higher order keyset
249as the restricted one and each site in the set as the root)
250 * -# these are merged into a fragment list of keysets
251 * -# All fragment lists (for all orders, i.e. from all destination fields) are merged into one list for return
252 * Important only is that we create all fragments, it is not important if we create them more than once
253 * as these copies are filtered out via use of the hash table (KeySet).
254 * \param *out output stream for debugging
255 * \param Fragment&*List list of already present keystacks (adaptive scheme) or empty list
256 * \param &RootStack stack with all root candidates (unequal to each atom in complete molecule if adaptive scheme is applied)
257 * \return pointer to Graph list
258 */
259void Fragmentation::FragmentBOSSANOVA(molecule *mol, Graph &FragmentList, KeyStack &RootStack)
260{
261 Info FunctionInfo(__func__);
262 Graph ***FragmentLowerOrdersList = NULL;
263 int NumLevels = 0;
264 int NumMolecules = 0;
265 int TotalNumMolecules = 0;
266 int *NumMoleculesOfOrder = NULL;
267 int Order = 0;
268 int UpgradeCount = RootStack.size();
269 KeyStack FragmentRootStack;
270 int RootKeyNr = 0;
271 int RootNr = 0;
272 UniqueFragments FragmentSearch;
273
274 // FragmentLowerOrdersList is a 2D-array of pointer to MoleculeListClass objects, one dimension represents the ANOVA expansion of a single order (i.e. 5)
275 // with all needed lower orders that are subtracted, the other dimension is the BondOrder (i.e. from 1 to 5)
276 NumMoleculesOfOrder = new int[UpgradeCount];
277 FragmentLowerOrdersList = new Graph**[UpgradeCount];
278
279 for(int i=0;i<UpgradeCount;i++) {
280 NumMoleculesOfOrder[i] = 0;
281 FragmentLowerOrdersList[i] = NULL;
282 }
283
284 FragmentSearch.Init(mol->FindAtom(RootKeyNr));
285
286 // Construct the complete KeySet which we need for topmost level only (but for all Roots)
287 KeySet CompleteMolecule;
288 for (molecule::const_iterator iter = mol->begin(); iter != mol->end(); ++iter) {
289 CompleteMolecule.insert((*iter)->GetTrueFather()->getNr());
290 }
291
292 // this can easily be seen: if Order is 5, then the number of levels for each lower order is the total sum of the number of levels above, as
293 // each has to be split up. E.g. for the second level we have one from 5th, one from 4th, two from 3th (which in turn is one from 5th, one from 4th),
294 // hence we have overall four 2th order levels for splitting. This also allows for putting all into a single array (FragmentLowerOrdersList[])
295 // with the order along the cells as this: 5433222211111111 for BondOrder 5 needing 16=pow(2,5-1) cells (only we use bit-shifting which is faster)
296 RootNr = 0; // counts through the roots in RootStack
297 while ((RootNr < UpgradeCount) && (!RootStack.empty())) {
298 RootKeyNr = RootStack.front();
299 RootStack.pop_front();
300 atom *Walker = mol->FindAtom(RootKeyNr);
301 // check cyclic lengths
302 //if ((MinimumRingSize[Walker->GetTrueFather()->getNr()] != -1) && (Walker->GetTrueFather()->AdaptiveOrder+1 > MinimumRingSize[Walker->GetTrueFather()->getNr()])) {
303 // LOG(0, "Bond order " << Walker->GetTrueFather()->AdaptiveOrder << " of Root " << *Walker << " greater than or equal to Minimum Ring size of " << MinimumRingSize << " found is not allowed.");
304 //} else
305 {
306 // increase adaptive order by one
307 Walker->GetTrueFather()->AdaptiveOrder++;
308 Order = Walker->AdaptiveOrder = Walker->GetTrueFather()->AdaptiveOrder;
309
310 // initialise Order-dependent entries of UniqueFragments structure
311 class PowerSetGenerator PSG(&FragmentSearch, Walker->AdaptiveOrder);
312
313 // allocate memory for all lower level orders in this 1D-array of ptrs
314 NumLevels = 1 << (Order-1); // (int)pow(2,Order);
315 FragmentLowerOrdersList[RootNr] = new Graph*[NumLevels];
316 for (int i=0;i<NumLevels;i++)
317 FragmentLowerOrdersList[RootNr][i] = NULL;
318
319 // create top order where nothing is reduced
320 LOG(0, "==============================================================================================================");
321 LOG(0, "Creating KeySets of Bond Order " << Order << " for " << *Walker << ", " << (RootStack.size()-RootNr) << " Roots remaining."); // , NumLevels is " << NumLevels << "
322
323 // Create list of Graphs of current Bond Order (i.e. F_{ij})
324 FragmentLowerOrdersList[RootNr][0] = new Graph;
325 FragmentSearch.PrepareForPowersetGeneration(1., FragmentLowerOrdersList[RootNr][0], Walker);
326 NumMoleculesOfOrder[RootNr] = PSG(CompleteMolecule, saturation);
327
328 // output resulting number
329 LOG(1, "Number of resulting KeySets is: " << NumMoleculesOfOrder[RootNr] << ".");
330 if (NumMoleculesOfOrder[RootNr] != 0) {
331 NumMolecules = 0;
332 } else {
333 Walker->GetTrueFather()->MaxOrder = true;
334 }
335 // now, we have completely filled each cell of FragmentLowerOrdersList[] for the current Walker->AdaptiveOrder
336 //NumMoleculesOfOrder[Walker->AdaptiveOrder-1] = NumMolecules;
337 TotalNumMolecules += NumMoleculesOfOrder[RootNr];
338// LOG(1, "Number of resulting molecules for Order " << (int)Walker->GetTrueFather()->AdaptiveOrder << " is: " << NumMoleculesOfOrder[RootNr] << ".");
339 RootStack.push_back(RootKeyNr); // put back on stack
340 RootNr++;
341 }
342 }
343 LOG(0, "==============================================================================================================");
344 LOG(1, "Total number of resulting molecules is: " << TotalNumMolecules << ".");
345 LOG(0, "==============================================================================================================");
346
347 // cleanup FragmentSearch structure
348 FragmentSearch.Cleanup();
349
350 // now, FragmentLowerOrdersList is complete, it looks - for BondOrder 5 - as this (number is the ANOVA Order of the terms therein)
351 // 5433222211111111
352 // 43221111
353 // 3211
354 // 21
355 // 1
356
357 // Subsequently, we combine all into a single list (FragmentList)
358 CombineAllOrderListIntoOne(FragmentList, FragmentLowerOrdersList, RootStack, mol);
359 FreeAllOrdersList(FragmentLowerOrdersList, RootStack, mol);
360 delete[](NumMoleculesOfOrder);
361};
362
363/** Stores a fragment from \a KeySet into \a molecule.
364 * First creates the minimal set of atoms from the KeySet, then creates the bond structure from the complete
365 * molecule and adds missing hydrogen where bonds were cut.
366 * \param *out output stream for debugging messages
367 * \param &Leaflet pointer to KeySet structure
368 * \param IsAngstroem whether we have Ansgtroem or bohrradius
369 * \return pointer to constructed molecule
370 */
371molecule * Fragmentation::StoreFragmentFromKeySet(KeySet &Leaflet, bool IsAngstroem)
372{
373 Info info(__func__);
374 ListOfLocalAtoms_t SonList;
375 molecule *Leaf = World::getInstance().createMolecule();
376
377 StoreFragmentFromKeySet_Init(mol, Leaf, Leaflet, SonList);
378 // create the bonds between all: Make it an induced subgraph and add hydrogen
379// LOG(2, "Creating bonds from father graph (i.e. induced subgraph creation).");
380 CreateInducedSubgraphOfFragment(mol, Leaf, SonList, IsAngstroem);
381
382 //Leaflet->Leaf->ScanForPeriodicCorrection(out);
383 return Leaf;
384};
385
386
387/** Estimates by educated guessing (using upper limit) the expected number of fragments.
388 * The upper limit is
389 * \f[
390 * n = N \cdot C^k
391 * \f]
392 * where \f$C=2^c\f$ and c is the maximum bond degree over N number of atoms.
393 * \param *out output stream for debugging
394 * \param order bond order k
395 * \return number n of fragments
396 */
397int Fragmentation::GuesstimateFragmentCount(int order)
398{
399 size_t c = 0;
400 int FragmentCount;
401 // get maximum bond degree
402 for (molecule::const_iterator iter = mol->begin(); iter != mol->end(); ++iter) {
403 const BondList& ListOfBonds = (*iter)->getListOfBonds();
404 c = (ListOfBonds.size() > c) ? ListOfBonds.size() : c;
405 }
406 FragmentCount = (saturation == DoSaturate ? mol->getNoNonHydrogen() : mol->getAtomCount()) *(1 << (c*order));
407 LOG(1, "Upper limit for this subgraph is " << FragmentCount << " for "
408 << mol->getNoNonHydrogen() << " non-H atoms with maximum bond degree of " << c << ".");
409 return FragmentCount;
410};
411
412
413/** Checks whether the OrderAtSite is still below \a Order at some site.
414 * \param AtomMask defines true/false per global Atom::Nr to mask in/out each nuclear site, used to activate given number of site to increment order adaptively
415 * \param *GlobalKeySetList list of keysets with global ids (valid in "this" molecule) needed for adaptive increase
416 * \param Order desired Order if positive, desired exponent in threshold criteria if negative (0 is single-step)
417 * \param path path to ENERGYPERFRAGMENT file (may be NULL if Order is non-negative)
418 * \param LoopDoneAlready indicate whether we have done a fragmentation loop already
419 * \return true - needs further fragmentation, false - does not need fragmentation
420 */
421bool Fragmentation::CheckOrderAtSite(AtomMask_t &AtomMask, const Graph &GlobalKeySetList, int Order, std::string path, bool LoopDoneAlready)
422{
423 bool status = false;
424
425 // initialize mask list
426 AtomMask.clear();
427
428 if (Order < 0) { // adaptive increase of BondOrder per site
429 if (LoopDoneAlready) // break after one step
430 return false;
431
432 // transmorph graph keyset list into indexed KeySetList
433 AdaptivityMap * IndexKeySetList = GlobalKeySetList.GraphToAdaptivityMap();
434
435 // parse the EnergyPerFragment file
436 IndexKeySetList->ScanAdaptiveFileIntoMap(path); // (Root No., (Value, Order)) !
437 // then map back onto (Value, (Root Nr., Order)) (i.e. sorted by value to pick the highest ones)
438 IndexKeySetList->ReMapAdaptiveCriteriaListToValue(mol);
439
440 // pick the ones still below threshold and mark as to be adaptively updated
441 if (IndexKeySetList->IsAdaptiveCriteriaListEmpty()) {
442 ELOG(2, "Unable to parse file, incrementing all.");
443 for (molecule::const_iterator iter = mol->begin(); iter != mol->end(); ++iter) {
444 if ((saturation == DontSaturate) || ((*iter)->getType()->getAtomicNumber() != 1)) // skip hydrogen
445 {
446 AtomMask.setTrue((*iter)->getNr()); // include all (non-hydrogen) atoms
447 status = true;
448 }
449 }
450 } else {
451 IndexKeySetList->MarkUpdateCandidates(AtomMask, Order, mol);
452 }
453
454 delete[](IndexKeySetList);
455 } else { // global increase of Bond Order
456 for(molecule::const_iterator iter = mol->begin(); iter != mol->end(); ++iter) {
457 if ((saturation == DontSaturate) || ((*iter)->getType()->getAtomicNumber() != 1)) // skip hydrogen
458 {
459 AtomMask.setTrue((*iter)->getNr()); // include all (non-hydrogen) atoms
460 if ((Order != 0) && ((*iter)->AdaptiveOrder < Order)) // && ((*iter)->AdaptiveOrder < MinimumRingSize[(*iter)->getNr()]))
461 status = true;
462 }
463 }
464 if ((!Order) && (!LoopDoneAlready)) // single stepping, just check
465 status = true;
466
467 if (!status) {
468 if (Order == 0)
469 LOG(1, "Single stepping done.");
470 else
471 LOG(1, "Order at every site is already equal or above desired order " << Order << ".");
472 }
473 }
474
475 PrintAtomMask(AtomMask, mol->getAtomCount()); // for debugging
476
477 return status;
478};
479
480/** Stores pairs (Atom::Nr, Atom::AdaptiveOrder) into file.
481 * Atoms not present in the file get "-1".
482 * \param &path path to file ORDERATSITEFILE
483 * \return true - file writable, false - not writable
484 */
485bool Fragmentation::StoreOrderAtSiteFile(std::string &path)
486{
487 string line;
488 ofstream file;
489
490 line = path + ORDERATSITEFILE;
491 file.open(line.c_str());
492 LOG(1, "Writing OrderAtSite " << ORDERATSITEFILE << " ... ");
493 if (file.good()) {
494 for_each(mol->begin(),mol->end(),bind2nd(mem_fun(&atom::OutputOrder), &file));
495 file.close();
496 LOG(1, "done.");
497 return true;
498 } else {
499 LOG(1, "failed to open file " << line << ".");
500 return false;
501 }
502};
503
504
505/** Parses pairs(Atom::Nr, Atom::AdaptiveOrder) from file and stores in molecule's Atom's.
506 * Atoms not present in the file get "0".
507 * \param atomids atoms to fragment, used in AtomMask
508 * \param &path path to file ORDERATSITEFILEe
509 * \return true - file found and scanned, false - file not found
510 * \sa ParseKeySetFile() and CheckAdjacencyFileAgainstMolecule() as this is meant to be used in conjunction with the two
511 */
512bool Fragmentation::ParseOrderAtSiteFromFile(const std::vector<atomId_t> &atomids, std::string &path)
513{
514 Info FunctionInfo(__func__);
515 typedef unsigned char order_t;
516 typedef std::map<atomId_t, order_t> OrderArray_t;
517 OrderArray_t OrderArray;
518 AtomMask_t MaxArray(atomids);
519 bool status;
520 int AtomNr, value;
521 string line;
522 ifstream file;
523
524 line = path + ORDERATSITEFILE;
525 file.open(line.c_str());
526 if (file.good()) {
527 while (!file.eof()) { // parse from file
528 AtomNr = -1;
529 file >> AtomNr;
530 if (AtomNr != -1) { // test whether we really parsed something (this is necessary, otherwise last atom is set twice and to 0 on second time)
531 file >> value;
532 OrderArray[AtomNr] = value;
533 file >> value;
534 MaxArray.setValue(AtomNr, (bool)value);
535 //LOG(2, "AtomNr " << AtomNr << " with order " << (int)OrderArray[AtomNr] << " and max order set to " << (int)MaxArray[AtomNr] << ".");
536 }
537 }
538 file.close();
539
540 // set atom values
541 for(molecule::iterator iter=mol->begin();iter!=mol->end();++iter){
542 (*iter)->AdaptiveOrder = OrderArray[(*iter)->getNr()];
543 (*iter)->MaxOrder = MaxArray.isTrue((*iter)->getNr());
544 }
545 //SetAtomValueToIndexedArray( OrderArray, &atom::getNr(), &atom::AdaptiveOrder );
546 //SetAtomValueToIndexedArray( MaxArray, &atom::getNr(), &atom::MaxOrder );
547
548 LOG(1, "\t ... done.");
549 status = true;
550 } else {
551 LOG(1, "\t ... failed to open file " << line << ".");
552 status = false;
553 }
554
555 return status;
556};
557
558/** Create a SortIndex to map from atomic labels to the sequence in which the atoms are given in the config file.
559 * \param &SortIndex Mapping array of size molecule::AtomCount
560 * \return true - success, false - failure of SortIndex alloc
561 */
562bool Fragmentation::CreateMappingLabelsToConfigSequence(std::map<atomId_t, int> &SortIndex)
563{
564 if (!SortIndex.empty()) {
565 LOG(1, "SortIndex has " << SortIndex.size() << " entries and is not empty as expected.");
566 return false;
567 }
568
569 int AtomNo = 0;
570 for(molecule::const_iterator iter=mol->begin();iter!=mol->end();++iter){
571 const int id = (*iter)->getNr();
572#ifndef NDEBUG
573 std::pair<std::map<atomId_t, int>::const_iterator, bool> inserter =
574#endif
575 SortIndex.insert( std::make_pair(id, AtomNo++) );
576 ASSERT( inserter.second ,
577 "Fragmentation::CreateMappingLabelsToConfigSequence() - same SortIndex set twice.");
578 }
579
580 return true;
581};
582
583
584/** Initializes some value for putting fragment of \a *mol into \a *Leaf.
585 * \param *mol total molecule
586 * \param *Leaf fragment molecule
587 * \param &Leaflet pointer to KeySet structure
588 * \param SonList calloc'd list which atom of \a *Leaf is a son of which atom in \a *mol
589 * \return number of atoms in fragment
590 */
591int Fragmentation::StoreFragmentFromKeySet_Init(molecule *mol, molecule *Leaf, KeySet &Leaflet, ListOfLocalAtoms_t &SonList)
592{
593 atom *FatherOfRunner = NULL;
594
595 // first create the minimal set of atoms from the KeySet
596 int size = 0;
597 for(KeySet::iterator runner = Leaflet.begin(); runner != Leaflet.end(); runner++) {
598 FatherOfRunner = mol->FindAtom((*runner)); // find the id
599 SonList.insert( std::make_pair(FatherOfRunner->getNr(), Leaf->AddCopyAtom(FatherOfRunner) ) );
600 size++;
601 }
602 return size;
603};
604
605
606/** Creates an induced subgraph out of a fragmental key set, adding bonds and hydrogens (if treated specially).
607 * \param *out output stream for debugging messages
608 * \param *mol total molecule
609 * \param *Leaf fragment molecule
610 * \param IsAngstroem whether we have Ansgtroem or bohrradius
611 * \param SonList list which atom of \a *Leaf is a son of which atom in \a *mol
612 */
613void Fragmentation::CreateInducedSubgraphOfFragment(molecule *mol, molecule *Leaf, ListOfLocalAtoms_t &SonList, bool IsAngstroem)
614{
615 bool LonelyFlag = false;
616 atom *OtherFather = NULL;
617 atom *FatherOfRunner = NULL;
618
619 // we increment the iter just before skipping the hydrogen
620 // as we use AddBond, we cannot have a const_iterator here
621 for (molecule::iterator iter = Leaf->begin(); iter != Leaf->end();) {
622 LonelyFlag = true;
623 FatherOfRunner = (*iter)->father;
624 ASSERT(FatherOfRunner,"Atom without father found");
625 if (SonList.find(FatherOfRunner->getNr()) != SonList.end()) { // check if this, our father, is present in list
626 // create all bonds
627 const BondList& ListOfBonds = FatherOfRunner->getListOfBonds();
628 for (BondList::const_iterator BondRunner = ListOfBonds.begin();
629 BondRunner != ListOfBonds.end();
630 ++BondRunner) {
631 OtherFather = (*BondRunner)->GetOtherAtom(FatherOfRunner);
632 if (SonList.find(OtherFather->getNr()) != SonList.end()) {
633// LOG(2, "INFO: Father " << *FatherOfRunner << " of son " << *SonList[FatherOfRunner->getNr()]
634// << " is bound to " << *OtherFather << ", whose son is "
635// << *SonList[OtherFather->getNr()] << ".");
636 if (OtherFather->getNr() > FatherOfRunner->getNr()) { // add bond (Nr check is for adding only one of both variants: ab, ba)
637 std::stringstream output;
638// output << "ACCEPT: Adding Bond: "
639 output << Leaf->AddBond((*iter), SonList[OtherFather->getNr()], (*BondRunner)->BondDegree);
640// LOG(3, output.str());
641 //NumBonds[(*iter)->getNr()]++;
642 } else {
643// LOG(3, "REJECY: Not adding bond, labels in wrong order.");
644 }
645 LonelyFlag = false;
646 } else {
647// LOG(2, "INFO: Father " << *FatherOfRunner << " of son " << *SonList[FatherOfRunner->getNr()]
648// << " is bound to " << *OtherFather << ", who has no son in this fragment molecule.");
649 if (saturation == DoSaturate) {
650// LOG(3, "ACCEPT: Adding Hydrogen to " << (*iter)->Name << " and a bond in between.");
651 if (!Leaf->AddHydrogenReplacementAtom((*BondRunner), (*iter), FatherOfRunner, OtherFather, IsAngstroem))
652 exit(1);
653 }
654 //NumBonds[(*iter)->getNr()] += Binder->BondDegree;
655 }
656 }
657 } else {
658 ELOG(1, "Son " << (*iter)->getName() << " has father " << FatherOfRunner->getName() << " but its entry in SonList is " << SonList[FatherOfRunner->getNr()] << "!");
659 }
660 if ((LonelyFlag) && (Leaf->getAtomCount() > 1)) {
661 LOG(0, **iter << "has got bonds only to hydrogens!");
662 }
663 ++iter;
664 if (saturation == DoSaturate) {
665 while ((iter != Leaf->end()) && ((*iter)->getType()->getAtomicNumber() == 1)){ // skip added hydrogen
666 iter++;
667 }
668 }
669 }
670};
671
672/** Sets the desired output types of the fragment configurations.
673 *
674 * @param types vector of desired types.
675 */
676void Fragmentation::setOutputTypes(const std::vector<std::string> &types)
677{
678 typelist = types;
679}
680
681/** Fills the root stack for sites to be used as root in fragmentation depending on order or adaptivity criteria
682 * Again, as in \sa FillBondStructureFromReference steps recursively through each Leaf in this chain list of molecule's.
683 * \param &RootStack stack to be filled
684 * \param AtomMask defines true/false per global Atom::Nr to mask in/out each nuclear site
685 * \return true - stack is non-empty, fragmentation necessary, false - stack is empty, no more sites to update
686 */
687void Fragmentation::FillRootStackForSubgraphs(KeyStack &RootStack, const AtomMask_t &AtomMask)
688{
689 for(molecule::const_iterator iter = mol->begin(); iter != mol->end(); ++iter) {
690 const atom * const Father = (*iter)->GetTrueFather();
691 if (AtomMask.isTrue(Father->getNr())) // apply mask
692 if ((saturation == DontSaturate) || ((*iter)->getType()->getAtomicNumber() != 1)) // skip hydrogen
693 RootStack.push_front((*iter)->getNr());
694 }
695}
696
697/** The indices per keyset are compared to the respective father's Atom::Nr in each subgraph and thus put into \a **&FragmentList.
698 * \param *KeySetList list with all keysets
699 * \param ListOfLocalAtoms Lookup table for each subgraph and index of each atom in global molecule, may be NULL on start, then it is filled
700 * \param **&FragmentList list to be allocated and returned
701 * \param FreeList true - ***ListOfLocalAtoms is free'd before return, false - it is not
702 * \retuen true - success, false - failure
703 */
704bool Fragmentation::AssignKeySetsToFragment(Graph &KeySetList, ListOfLocalAtoms_t &ListOfLocalAtoms, Graph &FragmentList, bool FreeList)
705{
706 Info FunctionInfo(__func__);
707 bool status = true;
708 size_t KeySetCounter = 0;
709
710 // fill ListOfLocalAtoms if NULL was given
711 if (!mol->FillListOfLocalAtoms(ListOfLocalAtoms, mol->getAtomCount())) {
712 LOG(1, "Filling of ListOfLocalAtoms failed.");
713 return false;
714 }
715
716 if (KeySetList.size() != 0) { // if there are some scanned keysets at all
717 // assign scanned keysets
718 KeySet TempSet;
719 for (Graph::iterator runner = KeySetList.begin(); runner != KeySetList.end(); runner++) { // key sets contain global numbers!
720 if (ListOfLocalAtoms[mol->FindAtom(*((*runner).first.begin()))->getNr()] != NULL) {// as we may assume that that bond structure is unchanged, we only test the first key in each set
721 // translate keyset to local numbers
722 for (KeySet::iterator sprinter = (*runner).first.begin(); sprinter != (*runner).first.end(); sprinter++)
723 TempSet.insert(ListOfLocalAtoms[mol->FindAtom(*sprinter)->getNr()]->getNr());
724 // insert into FragmentList
725 FragmentList.insert(GraphPair(TempSet, pair<int, double> (KeySetCounter++, (*runner).second.second)));
726 }
727 TempSet.clear();
728 }
729 } else
730 LOG(1, "KeySetList is NULL or empty.");
731
732 if (FreeList) {
733 // free the index lookup list
734 ListOfLocalAtoms.clear();
735 }
736 return status;
737}
738
739/** Translate list into global numbers (i.e. ones that are valid in "this" molecule, not in MolecularWalker->Leaf)
740 * \param &FragmentList Graph with local numbers per fragment
741 * \param &TotalNumberOfKeySets global key set counter
742 * \param &TotalGraph Graph to be filled with global numbers
743 */
744void Fragmentation::TranslateIndicesToGlobalIDs(Graph &FragmentList, int &TotalNumberOfKeySets, Graph &TotalGraph)
745{
746 Info FunctionInfo(__func__);
747 for (Graph::iterator runner = FragmentList.begin(); runner != FragmentList.end(); runner++) {
748 KeySet TempSet;
749 for (KeySet::iterator sprinter = (*runner).first.begin(); sprinter != (*runner).first.end(); sprinter++)
750 TempSet.insert((mol->FindAtom(*sprinter))->GetTrueFather()->getNr());
751 TotalGraph.insert(GraphPair(TempSet, pair<int, double> (TotalNumberOfKeySets++, (*runner).second.second)));
752 }
753}
754
Note: See TracBrowser for help on using the repository browser.