source: src/molecule_fragmentation.cpp@ fc1b24

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 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 fc1b24 was 49e1ae, checked in by Frederik Heber <heber@…>, 15 years ago

cstring header was missing in files, supplying definition of strlen, strcpy, and so on.

This was noted on laptop with gcc 4.1 (on workstation we have gcc 4.2)

  • Property mode set to 100644
File size: 76.3 KB
Line 
1/*
2 * molecule_fragmentation.cpp
3 *
4 * Created on: Oct 5, 2009
5 * Author: heber
6 */
7
8#include <cstring>
9
10#include "atom.hpp"
11#include "bond.hpp"
12#include "config.hpp"
13#include "element.hpp"
14#include "helpers.hpp"
15#include "lists.hpp"
16#include "log.hpp"
17#include "memoryallocator.hpp"
18#include "molecule.hpp"
19#include "periodentafel.hpp"
20
21/************************************* Functions for class molecule *********************************/
22
23
24/** Estimates by educated guessing (using upper limit) the expected number of fragments.
25 * The upper limit is
26 * \f[
27 * n = N \cdot C^k
28 * \f]
29 * where \f$C=2^c\f$ and c is the maximum bond degree over N number of atoms.
30 * \param *out output stream for debugging
31 * \param order bond order k
32 * \return number n of fragments
33 */
34int molecule::GuesstimateFragmentCount(int order)
35{
36 size_t c = 0;
37 int FragmentCount;
38 // get maximum bond degree
39 atom *Walker = start;
40 while (Walker->next != end) {
41 Walker = Walker->next;
42 c = (Walker->ListOfBonds.size() > c) ? Walker->ListOfBonds.size() : c;
43 }
44 FragmentCount = NoNonHydrogen*(1 << (c*order));
45 Log() << Verbose(1) << "Upper limit for this subgraph is " << FragmentCount << " for " << NoNonHydrogen << " non-H atoms with maximum bond degree of " << c << "." << endl;
46 return FragmentCount;
47};
48
49/** Scans a single line for number and puts them into \a KeySet.
50 * \param *out output stream for debugging
51 * \param *buffer buffer to scan
52 * \param &CurrentSet filled KeySet on return
53 * \return true - at least one valid atom id parsed, false - CurrentSet is empty
54 */
55bool ScanBufferIntoKeySet(char *buffer, KeySet &CurrentSet)
56{
57 stringstream line;
58 int AtomNr;
59 int status = 0;
60
61 line.str(buffer);
62 while (!line.eof()) {
63 line >> AtomNr;
64 if (AtomNr >= 0) {
65 CurrentSet.insert(AtomNr); // insert at end, hence in same order as in file!
66 status++;
67 } // else it's "-1" or else and thus must not be added
68 }
69 Log() << Verbose(1) << "The scanned KeySet is ";
70 for(KeySet::iterator runner = CurrentSet.begin(); runner != CurrentSet.end(); runner++) {
71 Log() << Verbose(0) << (*runner) << "\t";
72 }
73 Log() << Verbose(0) << endl;
74 return (status != 0);
75};
76
77/** Parses the KeySet file and fills \a *FragmentList from the known molecule structure.
78 * Does two-pass scanning:
79 * -# Scans the keyset file and initialises a temporary graph
80 * -# Scans TEFactors file and sets the TEFactor of each key set in the temporary graph accordingly
81 * Finally, the temporary graph is inserted into the given \a FragmentList for return.
82 * \param *out output stream for debugging
83 * \param *path path to file
84 * \param *FragmentList empty, filled on return
85 * \return true - parsing successfully, false - failure on parsing (FragmentList will be NULL)
86 */
87bool ParseKeySetFile(char *path, Graph *&FragmentList)
88{
89 bool status = true;
90 ifstream InputFile;
91 stringstream line;
92 GraphTestPair testGraphInsert;
93 int NumberOfFragments = 0;
94 char *filename = Malloc<char>(MAXSTRINGSIZE, "molecule::ParseKeySetFile - filename");
95
96 if (FragmentList == NULL) { // check list pointer
97 FragmentList = new Graph;
98 }
99
100 // 1st pass: open file and read
101 Log() << Verbose(1) << "Parsing the KeySet file ... " << endl;
102 sprintf(filename, "%s/%s%s", path, FRAGMENTPREFIX, KEYSETFILE);
103 InputFile.open(filename);
104 if (InputFile != NULL) {
105 // each line represents a new fragment
106 char *buffer = Malloc<char>(MAXSTRINGSIZE, "molecule::ParseKeySetFile - *buffer");
107 // 1. parse keysets and insert into temp. graph
108 while (!InputFile.eof()) {
109 InputFile.getline(buffer, MAXSTRINGSIZE);
110 KeySet CurrentSet;
111 if ((strlen(buffer) > 0) && (ScanBufferIntoKeySet(buffer, CurrentSet))) { // if at least one valid atom was added, write config
112 testGraphInsert = FragmentList->insert(GraphPair (CurrentSet,pair<int,double>(NumberOfFragments++,1))); // store fragment number and current factor
113 if (!testGraphInsert.second) {
114 eLog() << Verbose(0) << "KeySet file must be corrupt as there are two equal key sets therein!" << endl;
115 performCriticalExit();
116 }
117 }
118 }
119 // 2. Free and done
120 InputFile.close();
121 InputFile.clear();
122 Free(&buffer);
123 Log() << Verbose(1) << "done." << endl;
124 } else {
125 Log() << Verbose(1) << "File " << filename << " not found." << endl;
126 status = false;
127 }
128
129 Free(&filename);
130 return status;
131};
132
133/** Parses the TE factors file and fills \a *FragmentList from the known molecule structure.
134 * -# Scans TEFactors file and sets the TEFactor of each key set in the temporary graph accordingly
135 * \param *out output stream for debugging
136 * \param *path path to file
137 * \param *FragmentList graph whose nodes's TE factors are set on return
138 * \return true - parsing successfully, false - failure on parsing
139 */
140bool ParseTEFactorsFile(char *path, Graph *FragmentList)
141{
142 bool status = true;
143 ifstream InputFile;
144 stringstream line;
145 GraphTestPair testGraphInsert;
146 int NumberOfFragments = 0;
147 double TEFactor;
148 char *filename = Malloc<char>(MAXSTRINGSIZE, "molecule::ParseTEFactorsFile - filename");
149
150 if (FragmentList == NULL) { // check list pointer
151 FragmentList = new Graph;
152 }
153
154 // 2nd pass: open TEFactors file and read
155 Log() << Verbose(1) << "Parsing the TEFactors file ... " << endl;
156 sprintf(filename, "%s/%s%s", path, FRAGMENTPREFIX, TEFACTORSFILE);
157 InputFile.open(filename);
158 if (InputFile != NULL) {
159 // 3. add found TEFactors to each keyset
160 NumberOfFragments = 0;
161 for(Graph::iterator runner = FragmentList->begin();runner != FragmentList->end(); runner++) {
162 if (!InputFile.eof()) {
163 InputFile >> TEFactor;
164 (*runner).second.second = TEFactor;
165 Log() << Verbose(2) << "Setting " << ++NumberOfFragments << " fragment's TEFactor to " << (*runner).second.second << "." << endl;
166 } else {
167 status = false;
168 break;
169 }
170 }
171 // 4. Free and done
172 InputFile.close();
173 Log() << Verbose(1) << "done." << endl;
174 } else {
175 Log() << Verbose(1) << "File " << filename << " not found." << endl;
176 status = false;
177 }
178
179 // free memory
180 Free(&filename);
181
182 return status;
183};
184
185/** Stores key sets to file.
186 * \param *out output stream for debugging
187 * \param KeySetList Graph with Keysets
188 * \param *path path to file
189 * \return true - file written successfully, false - writing failed
190 */
191bool StoreKeySetFile(Graph &KeySetList, char *path)
192{
193 ofstream output;
194 bool status = true;
195 string line;
196
197 // open KeySet file
198 line = path;
199 line.append("/");
200 line += FRAGMENTPREFIX;
201 line += KEYSETFILE;
202 output.open(line.c_str(), ios::out);
203 Log() << Verbose(1) << "Saving key sets of the total graph ... ";
204 if(output != NULL) {
205 for(Graph::iterator runner = KeySetList.begin(); runner != KeySetList.end(); runner++) {
206 for (KeySet::iterator sprinter = (*runner).first.begin();sprinter != (*runner).first.end(); sprinter++) {
207 if (sprinter != (*runner).first.begin())
208 output << "\t";
209 output << *sprinter;
210 }
211 output << endl;
212 }
213 Log() << Verbose(0) << "done." << endl;
214 } else {
215 eLog() << Verbose(0) << "Unable to open " << line << " for writing keysets!" << endl;
216 performCriticalExit();
217 status = false;
218 }
219 output.close();
220 output.clear();
221
222 return status;
223};
224
225
226/** Stores TEFactors to file.
227 * \param *out output stream for debugging
228 * \param KeySetList Graph with factors
229 * \param *path path to file
230 * \return true - file written successfully, false - writing failed
231 */
232bool StoreTEFactorsFile(Graph &KeySetList, char *path)
233{
234 ofstream output;
235 bool status = true;
236 string line;
237
238 // open TEFactors file
239 line = path;
240 line.append("/");
241 line += FRAGMENTPREFIX;
242 line += TEFACTORSFILE;
243 output.open(line.c_str(), ios::out);
244 Log() << Verbose(1) << "Saving TEFactors of the total graph ... ";
245 if(output != NULL) {
246 for(Graph::iterator runner = KeySetList.begin(); runner != KeySetList.end(); runner++)
247 output << (*runner).second.second << endl;
248 Log() << Verbose(1) << "done." << endl;
249 } else {
250 Log() << Verbose(1) << "failed to open " << line << "." << endl;
251 status = false;
252 }
253 output.close();
254
255 return status;
256};
257
258/** For a given graph, sorts KeySets into a (index, keyset) map.
259 * \param *GlobalKeySetList list of keysets with global ids (valid in "this" molecule) needed for adaptive increase
260 * \return map from index to keyset
261 */
262map<int,KeySet> * GraphToIndexedKeySet(Graph *GlobalKeySetList)
263{
264 map<int,KeySet> *IndexKeySetList = new map<int,KeySet>;
265 for(Graph::iterator runner = GlobalKeySetList->begin(); runner != GlobalKeySetList->end(); runner++) {
266 IndexKeySetList->insert( pair<int,KeySet>(runner->second.first,runner->first) );
267 }
268 return IndexKeySetList;
269};
270
271/** Inserts a (\a No, \a value) pair into the list, overwriting present one.
272 * Note if values are equal, No will decided on which is first
273 * \param *out output stream for debugging
274 * \param &AdaptiveCriteriaList list to insert into
275 * \param &IndexedKeySetList list to find key set for a given index \a No
276 * \param FragOrder current bond order of fragment
277 * \param No index of keyset
278 * \param value energy value
279 */
280void InsertIntoAdaptiveCriteriaList(map<int, pair<double,int> > *AdaptiveCriteriaList, map<int,KeySet> &IndexKeySetList, int FragOrder, int No, double Value)
281{
282 map<int,KeySet>::iterator marker = IndexKeySetList.find(No); // find keyset to Frag No.
283 if (marker != IndexKeySetList.end()) { // if found
284 Value *= 1 + MYEPSILON*(*((*marker).second.begin())); // in case of equal energies this makes them not equal without changing anything actually
285 // as the smallest number in each set has always been the root (we use global id to keep the doubles away), seek smallest and insert into AtomMask
286 pair <map<int, pair<double,int> >::iterator, bool> InsertedElement = AdaptiveCriteriaList->insert( make_pair(*((*marker).second.begin()), pair<double,int>( fabs(Value), FragOrder) ));
287 map<int, pair<double,int> >::iterator PresentItem = InsertedElement.first;
288 if (!InsertedElement.second) { // this root is already present
289 if ((*PresentItem).second.second < FragOrder) // if order there is lower, update entry with higher-order term
290 //if ((*PresentItem).second.first < (*runner).first) // as higher-order terms are not always better, we skip this part (which would always include this site into adaptive increase)
291 { // if value is smaller, update value and order
292 (*PresentItem).second.first = fabs(Value);
293 (*PresentItem).second.second = FragOrder;
294 Log() << Verbose(2) << "Updated element (" << (*PresentItem).first << ",[" << (*PresentItem).second.first << "," << (*PresentItem).second.second << "])." << endl;
295 } else {
296 Log() << Verbose(2) << "Did not update element " << (*PresentItem).first << " as " << FragOrder << " is less than or equal to " << (*PresentItem).second.second << "." << endl;
297 }
298 } else {
299 Log() << Verbose(2) << "Inserted element (" << (*PresentItem).first << ",[" << (*PresentItem).second.first << "," << (*PresentItem).second.second << "])." << endl;
300 }
301 } else {
302 Log() << Verbose(1) << "No Fragment under No. " << No << "found." << endl;
303 }
304};
305
306/** Scans the adaptive order file and insert (index, value) into map.
307 * \param *out output stream for debugging
308 * \param *path path to ENERGYPERFRAGMENT file (may be NULL if Order is non-negative)
309 * \param &IndexedKeySetList list to find key set for a given index \a No
310 * \return adaptive criteria list from file
311 */
312map<int, pair<double,int> > * ScanAdaptiveFileIntoMap(char *path, map<int,KeySet> &IndexKeySetList)
313{
314 map<int, pair<double,int> > *AdaptiveCriteriaList = new map<int, pair<double,int> >;
315 int No = 0, FragOrder = 0;
316 double Value = 0.;
317 char *buffer = Malloc<char>(MAXSTRINGSIZE, "molecule::CheckOrderAtSite: *buffer");
318 sprintf(buffer, "%s/%s%s.dat", path, FRAGMENTPREFIX, ENERGYPERFRAGMENT);
319 ifstream InputFile(buffer, ios::in);
320
321 if (CountLinesinFile(InputFile) > 0) {
322 // each line represents a fragment root (Atom::nr) id and its energy contribution
323 InputFile.getline(buffer, MAXSTRINGSIZE); // skip comment lines
324 InputFile.getline(buffer, MAXSTRINGSIZE);
325 while(!InputFile.eof()) {
326 InputFile.getline(buffer, MAXSTRINGSIZE);
327 if (strlen(buffer) > 2) {
328 //Log() << Verbose(2) << "Scanning: " << buffer << endl;
329 stringstream line(buffer);
330 line >> FragOrder;
331 line >> ws >> No;
332 line >> ws >> Value; // skip time entry
333 line >> ws >> Value;
334 No -= 1; // indices start at 1 in file, not 0
335 //Log() << Verbose(2) << " - yields (" << No << "," << Value << ", " << FragOrder << ")" << endl;
336
337 // clean the list of those entries that have been superceded by higher order terms already
338 InsertIntoAdaptiveCriteriaList(AdaptiveCriteriaList, IndexKeySetList, FragOrder, No, Value);
339 }
340 }
341 // close and done
342 InputFile.close();
343 InputFile.clear();
344 }
345 Free(&buffer);
346
347 return AdaptiveCriteriaList;
348};
349
350/** Maps adaptive criteria list back onto (Value, (Root Nr., Order))
351 * (i.e. sorted by value to pick the highest ones)
352 * \param *out output stream for debugging
353 * \param &AdaptiveCriteriaList list to insert into
354 * \param *mol molecule with atoms
355 * \return remapped list
356 */
357map<double, pair<int,int> > * ReMapAdaptiveCriteriaListToValue(map<int, pair<double,int> > *AdaptiveCriteriaList, molecule *mol)
358{
359 atom *Walker = mol->start;
360 map<double, pair<int,int> > *FinalRootCandidates = new map<double, pair<int,int> > ;
361 Log() << Verbose(1) << "Root candidate list is: " << endl;
362 for(map<int, pair<double,int> >::iterator runner = AdaptiveCriteriaList->begin(); runner != AdaptiveCriteriaList->end(); runner++) {
363 Walker = mol->FindAtom((*runner).first);
364 if (Walker != NULL) {
365 //if ((*runner).second.second >= Walker->AdaptiveOrder) { // only insert if this is an "active" root site for the current order
366 if (!Walker->MaxOrder) {
367 Log() << Verbose(2) << "(" << (*runner).first << ",[" << (*runner).second.first << "," << (*runner).second.second << "])" << endl;
368 FinalRootCandidates->insert( make_pair( (*runner).second.first, pair<int,int>((*runner).first, (*runner).second.second) ) );
369 } else {
370 Log() << Verbose(2) << "Excluding (" << *Walker << ", " << (*runner).first << ",[" << (*runner).second.first << "," << (*runner).second.second << "]), as it has reached its maximum order." << endl;
371 }
372 } else {
373 eLog() << Verbose(0) << "Atom No. " << (*runner).second.first << " was not found in this molecule." << endl;
374 performCriticalExit();
375 }
376 }
377 return FinalRootCandidates;
378};
379
380/** Marks all candidate sites for update if below adaptive threshold.
381 * Picks a given number of highest values and set *AtomMask to true.
382 * \param *out output stream for debugging
383 * \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
384 * \param FinalRootCandidates list candidates to check
385 * \param Order desired order
386 * \param *mol molecule with atoms
387 * \return true - if update is necessary, false - not
388 */
389bool MarkUpdateCandidates(bool *AtomMask, map<double, pair<int,int> > &FinalRootCandidates, int Order, molecule *mol)
390{
391 atom *Walker = mol->start;
392 int No = -1;
393 bool status = false;
394 for(map<double, pair<int,int> >::iterator runner = FinalRootCandidates.upper_bound(pow(10.,Order)); runner != FinalRootCandidates.end(); runner++) {
395 No = (*runner).second.first;
396 Walker = mol->FindAtom(No);
397 //if (Walker->AdaptiveOrder < MinimumRingSize[Walker->nr]) {
398 Log() << Verbose(2) << "Root " << No << " is still above threshold (10^{" << Order <<"}: " << runner->first << ", setting entry " << No << " of Atom mask to true." << endl;
399 AtomMask[No] = true;
400 status = true;
401 //} else
402 //Log() << Verbose(2) << "Root " << No << " is still above threshold (10^{" << Order <<"}: " << runner->first << ", however MinimumRingSize of " << MinimumRingSize[Walker->nr] << " does not allow further adaptive increase." << endl;
403 }
404 return status;
405};
406
407/** print atom mask for debugging.
408 * \param *out output stream for debugging
409 * \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
410 * \param AtomCount number of entries in \a *AtomMask
411 */
412void PrintAtomMask(bool *AtomMask, int AtomCount)
413{
414 Log() << Verbose(2) << " ";
415 for(int i=0;i<AtomCount;i++)
416 Log() << Verbose(0) << (i % 10);
417 Log() << Verbose(0) << endl;
418 Log() << Verbose(2) << "Atom mask is: ";
419 for(int i=0;i<AtomCount;i++)
420 Log() << Verbose(0) << (AtomMask[i] ? "t" : "f");
421 Log() << Verbose(0) << endl;
422};
423
424/** Checks whether the OrderAtSite is still below \a Order at some site.
425 * \param *out output stream for debugging
426 * \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
427 * \param *GlobalKeySetList list of keysets with global ids (valid in "this" molecule) needed for adaptive increase
428 * \param Order desired Order if positive, desired exponent in threshold criteria if negative (0 is single-step)
429 * \param *MinimumRingSize array of max. possible order to avoid loops
430 * \param *path path to ENERGYPERFRAGMENT file (may be NULL if Order is non-negative)
431 * \return true - needs further fragmentation, false - does not need fragmentation
432 */
433bool molecule::CheckOrderAtSite(bool *AtomMask, Graph *GlobalKeySetList, int Order, int *MinimumRingSize, char *path)
434{
435 atom *Walker = start;
436 bool status = false;
437
438 // initialize mask list
439 for(int i=AtomCount;i--;)
440 AtomMask[i] = false;
441
442 if (Order < 0) { // adaptive increase of BondOrder per site
443 if (AtomMask[AtomCount] == true) // break after one step
444 return false;
445
446 // transmorph graph keyset list into indexed KeySetList
447 if (GlobalKeySetList == NULL) {
448 eLog() << Verbose(1) << "Given global key set list (graph) is NULL!" << endl;
449 return false;
450 }
451 map<int,KeySet> *IndexKeySetList = GraphToIndexedKeySet(GlobalKeySetList);
452
453 // parse the EnergyPerFragment file
454 map<int, pair<double,int> > *AdaptiveCriteriaList = ScanAdaptiveFileIntoMap(path, *IndexKeySetList); // (Root No., (Value, Order)) !
455 if (AdaptiveCriteriaList->empty()) {
456 eLog() << Verbose(2) << "Unable to parse file, incrementing all." << endl;
457 while (Walker->next != end) {
458 Walker = Walker->next;
459 #ifdef ADDHYDROGEN
460 if (Walker->type->Z != 1) // skip hydrogen
461 #endif
462 {
463 AtomMask[Walker->nr] = true; // include all (non-hydrogen) atoms
464 status = true;
465 }
466 }
467 }
468 // then map back onto (Value, (Root Nr., Order)) (i.e. sorted by value to pick the highest ones)
469 map<double, pair<int,int> > *FinalRootCandidates = ReMapAdaptiveCriteriaListToValue(AdaptiveCriteriaList, this);
470
471 // pick the ones still below threshold and mark as to be adaptively updated
472 MarkUpdateCandidates(AtomMask, *FinalRootCandidates, Order, this);
473
474 Free(&IndexKeySetList);
475 Free(&AdaptiveCriteriaList);
476 Free(&FinalRootCandidates);
477 } else { // global increase of Bond Order
478 while (Walker->next != end) {
479 Walker = Walker->next;
480 #ifdef ADDHYDROGEN
481 if (Walker->type->Z != 1) // skip hydrogen
482 #endif
483 {
484 AtomMask[Walker->nr] = true; // include all (non-hydrogen) atoms
485 if ((Order != 0) && (Walker->AdaptiveOrder < Order)) // && (Walker->AdaptiveOrder < MinimumRingSize[Walker->nr]))
486 status = true;
487 }
488 }
489 if ((Order == 0) && (AtomMask[AtomCount] == false)) // single stepping, just check
490 status = true;
491
492 if (!status) {
493 if (Order == 0)
494 Log() << Verbose(1) << "Single stepping done." << endl;
495 else
496 Log() << Verbose(1) << "Order at every site is already equal or above desired order " << Order << "." << endl;
497 }
498 }
499
500 PrintAtomMask(AtomMask, AtomCount); // for debugging
501
502 return status;
503};
504
505/** Create a SortIndex to map from atomic labels to the sequence in which the atoms are given in the config file.
506 * \param *out output stream for debugging
507 * \param *&SortIndex Mapping array of size molecule::AtomCount
508 * \return true - success, false - failure of SortIndex alloc
509 */
510bool molecule::CreateMappingLabelsToConfigSequence(int *&SortIndex)
511{
512 if (SortIndex != NULL) {
513 Log() << Verbose(1) << "SortIndex is " << SortIndex << " and not NULL as expected." << endl;
514 return false;
515 }
516 SortIndex = Malloc<int>(AtomCount, "molecule::CreateMappingLabelsToConfigSequence: *SortIndex");
517 for(int i=AtomCount;i--;)
518 SortIndex[i] = -1;
519
520 int AtomNo = 0;
521 SetIndexedArrayForEachAtomTo( SortIndex, &atom::nr, &IncrementalAbsoluteValue, AtomNo );
522
523 return true;
524};
525
526/** Performs a many-body bond order analysis for a given bond order.
527 * -# parses adjacency, keysets and orderatsite files
528 * -# performs DFS to find connected subgraphs (to leave this in was a design decision: might be useful later)
529 * -# RootStack is created for every subgraph (here, later we implement the "update 10 sites with highest energ
530y contribution", and that's why this consciously not done in the following loop)
531 * -# in a loop over all subgraphs
532 * -# calls FragmentBOSSANOVA with this RootStack and within the subgraph molecule structure
533 * -# creates molecule (fragment)s from the returned keysets (StoreFragmentFromKeySet)
534 * -# combines the generated molecule lists from all subgraphs
535 * -# saves to disk: fragment configs, adjacency, orderatsite, keyset files
536 * Note that as we split "this" molecule up into a list of subgraphs, i.e. a MoleculeListClass, we have two sets
537 * of vertex indices: Global always means the index in "this" molecule, whereas local refers to the molecule or
538 * subgraph in the MoleculeListClass.
539 * \param *out output stream for debugging
540 * \param Order up to how many neighbouring bonds a fragment contains in BondOrderScheme::BottumUp scheme
541 * \param *configuration configuration for writing config files for each fragment
542 * \return 1 - continue, 2 - stop (no fragmentation occured)
543 */
544int molecule::FragmentMolecule(int Order, config *configuration)
545{
546 MoleculeListClass *BondFragments = NULL;
547 int *SortIndex = NULL;
548 int *MinimumRingSize = new int[AtomCount];
549 int FragmentCounter;
550 MoleculeLeafClass *MolecularWalker = NULL;
551 MoleculeLeafClass *Subgraphs = NULL; // list of subgraphs from DFS analysis
552 fstream File;
553 bool FragmentationToDo = true;
554 class StackClass<bond *> *BackEdgeStack = NULL, *LocalBackEdgeStack = NULL;
555 bool CheckOrder = false;
556 Graph **FragmentList = NULL;
557 Graph *ParsedFragmentList = NULL;
558 Graph TotalGraph; // graph with all keysets however local numbers
559 int TotalNumberOfKeySets = 0;
560 atom **ListOfAtoms = NULL;
561 atom ***ListOfLocalAtoms = NULL;
562 bool *AtomMask = NULL;
563
564 Log() << Verbose(0) << endl;
565#ifdef ADDHYDROGEN
566 Log() << Verbose(0) << "I will treat hydrogen special and saturate dangling bonds with it." << endl;
567#else
568 Log() << Verbose(0) << "Hydrogen is treated just like the rest of the lot." << endl;
569#endif
570
571 // ++++++++++++++++++++++++++++ INITIAL STUFF: Bond structure analysis, file parsing, ... ++++++++++++++++++++++++++++++++++++++++++
572
573 // ===== 1. Check whether bond structure is same as stored in files ====
574
575 // create lookup table for Atom::nr
576 FragmentationToDo = FragmentationToDo && CreateFatherLookupTable(start, end, ListOfAtoms, AtomCount);
577
578 // === compare it with adjacency file ===
579 FragmentationToDo = FragmentationToDo && CheckAdjacencyFileAgainstMolecule(configuration->configpath, ListOfAtoms);
580 Free(&ListOfAtoms);
581
582 // ===== 2. perform a DFS analysis to gather info on cyclic structure and a list of disconnected subgraphs =====
583 Subgraphs = DepthFirstSearchAnalysis(BackEdgeStack);
584
585 // analysis of the cycles (print rings, get minimum cycle length) for each subgraph
586 for(int i=AtomCount;i--;)
587 MinimumRingSize[i] = AtomCount;
588 MolecularWalker = Subgraphs;
589 FragmentCounter = 0;
590 while (MolecularWalker->next != NULL) {
591 MolecularWalker = MolecularWalker->next;
592 // fill the bond structure of the individually stored subgraphs
593 MolecularWalker->FillBondStructureFromReference(this, FragmentCounter, ListOfLocalAtoms, false); // we want to keep the created ListOfLocalAtoms
594 Log() << Verbose(0) << "Analysing the cycles of subgraph " << MolecularWalker->Leaf << " with nr. " << FragmentCounter << "." << endl;
595 LocalBackEdgeStack = new StackClass<bond *> (MolecularWalker->Leaf->BondCount);
596// // check the list of local atoms for debugging
597// Log() << Verbose(0) << "ListOfLocalAtoms for this subgraph is:" << endl;
598// for (int i=0;i<AtomCount;i++)
599// if (ListOfLocalAtoms[FragmentCounter][i] == NULL)
600// Log() << Verbose(0) << "\tNULL";
601// else
602// Log() << Verbose(0) << "\t" << ListOfLocalAtoms[FragmentCounter][i]->Name;
603 Log() << Verbose(0) << "Gathering local back edges for subgraph " << MolecularWalker->Leaf << " with nr. " << FragmentCounter << "." << endl;
604 MolecularWalker->Leaf->PickLocalBackEdges(ListOfLocalAtoms[FragmentCounter++], BackEdgeStack, LocalBackEdgeStack);
605 Log() << Verbose(0) << "Analysing the cycles of subgraph " << MolecularWalker->Leaf << " with nr. " << FragmentCounter << "." << endl;
606 MolecularWalker->Leaf->CyclicStructureAnalysis(LocalBackEdgeStack, MinimumRingSize);
607 Log() << Verbose(0) << "Done with Analysing the cycles of subgraph " << MolecularWalker->Leaf << " with nr. " << FragmentCounter << "." << endl;
608 delete(LocalBackEdgeStack);
609 }
610 delete(BackEdgeStack);
611
612 // ===== 3. if structure still valid, parse key set file and others =====
613 FragmentationToDo = FragmentationToDo && ParseKeySetFile(configuration->configpath, ParsedFragmentList);
614
615 // ===== 4. check globally whether there's something to do actually (first adaptivity check)
616 FragmentationToDo = FragmentationToDo && ParseOrderAtSiteFromFile(configuration->configpath);
617
618 // =================================== Begin of FRAGMENTATION ===============================
619 // ===== 6a. assign each keyset to its respective subgraph =====
620 Subgraphs->next->AssignKeySetsToFragment(this, ParsedFragmentList, ListOfLocalAtoms, FragmentList, (FragmentCounter = 0), true);
621
622 // ===== 6b. prepare and go into the adaptive (Order<0), single-step (Order==0) or incremental (Order>0) cycle
623 KeyStack *RootStack = new KeyStack[Subgraphs->next->Count()];
624 AtomMask = new bool[AtomCount+1];
625 AtomMask[AtomCount] = false;
626 FragmentationToDo = false; // if CheckOrderAtSite just ones recommends fragmentation, we will save fragments afterwards
627 while ((CheckOrder = CheckOrderAtSite(AtomMask, ParsedFragmentList, Order, MinimumRingSize, configuration->configpath))) {
628 FragmentationToDo = FragmentationToDo || CheckOrder;
629 AtomMask[AtomCount] = true; // last plus one entry is used as marker that we have been through this loop once already in CheckOrderAtSite()
630 // ===== 6b. fill RootStack for each subgraph (second adaptivity check) =====
631 Subgraphs->next->FillRootStackForSubgraphs(RootStack, AtomMask, (FragmentCounter = 0));
632
633 // ===== 7. fill the bond fragment list =====
634 FragmentCounter = 0;
635 MolecularWalker = Subgraphs;
636 while (MolecularWalker->next != NULL) {
637 MolecularWalker = MolecularWalker->next;
638 Log() << Verbose(1) << "Fragmenting subgraph " << MolecularWalker << "." << endl;
639 //MolecularWalker->Leaf->OutputListOfBonds(out); // output atom::ListOfBonds for debugging
640 if (MolecularWalker->Leaf->first->next != MolecularWalker->Leaf->last) {
641 // call BOSSANOVA method
642 Log() << Verbose(0) << endl << " ========== BOND ENERGY of subgraph " << FragmentCounter << " ========================= " << endl;
643 MolecularWalker->Leaf->FragmentBOSSANOVA(FragmentList[FragmentCounter], RootStack[FragmentCounter], MinimumRingSize);
644 } else {
645 eLog() << Verbose(1) << "Subgraph " << MolecularWalker << " has no atoms!" << endl;
646 }
647 FragmentCounter++; // next fragment list
648 }
649 }
650 Log() << Verbose(2) << "CheckOrder is " << CheckOrder << "." << endl;
651 delete[](RootStack);
652 delete[](AtomMask);
653 delete(ParsedFragmentList);
654 delete[](MinimumRingSize);
655
656
657 // ==================================== End of FRAGMENTATION ============================================
658
659 // ===== 8a. translate list into global numbers (i.e. ones that are valid in "this" molecule, not in MolecularWalker->Leaf)
660 Subgraphs->next->TranslateIndicesToGlobalIDs(FragmentList, (FragmentCounter = 0), TotalNumberOfKeySets, TotalGraph);
661
662 // free subgraph memory again
663 FragmentCounter = 0;
664 if (Subgraphs != NULL) {
665 while (Subgraphs->next != NULL) {
666 Subgraphs = Subgraphs->next;
667 delete(FragmentList[FragmentCounter++]);
668 delete(Subgraphs->previous);
669 }
670 delete(Subgraphs);
671 }
672 Free(&FragmentList);
673
674 // ===== 8b. gather keyset lists (graphs) from all subgraphs and transform into MoleculeListClass =====
675 //if (FragmentationToDo) { // we should always store the fragments again as coordination might have changed slightly without changing bond structure
676 // allocate memory for the pointer array and transmorph graphs into full molecular fragments
677 BondFragments = new MoleculeListClass();
678 int k=0;
679 for(Graph::iterator runner = TotalGraph.begin(); runner != TotalGraph.end(); runner++) {
680 KeySet test = (*runner).first;
681 Log() << Verbose(0) << "Fragment No." << (*runner).second.first << " with TEFactor " << (*runner).second.second << "." << endl;
682 BondFragments->insert(StoreFragmentFromKeySet(test, configuration));
683 k++;
684 }
685 Log() << Verbose(0) << k << "/" << BondFragments->ListOfMolecules.size() << " fragments generated from the keysets." << endl;
686
687 // ===== 9. Save fragments' configuration and keyset files et al to disk ===
688 if (BondFragments->ListOfMolecules.size() != 0) {
689 // create the SortIndex from BFS labels to order in the config file
690 CreateMappingLabelsToConfigSequence(SortIndex);
691
692 Log() << Verbose(1) << "Writing " << BondFragments->ListOfMolecules.size() << " possible bond fragmentation configs" << endl;
693 if (BondFragments->OutputConfigForListOfFragments(configuration, SortIndex))
694 Log() << Verbose(1) << "All configs written." << endl;
695 else
696 Log() << Verbose(1) << "Some config writing failed." << endl;
697
698 // store force index reference file
699 BondFragments->StoreForcesFile(configuration->configpath, SortIndex);
700
701 // store keysets file
702 StoreKeySetFile(TotalGraph, configuration->configpath);
703
704 // store Adjacency file
705 StoreAdjacencyToFile(configuration->configpath);
706
707 // store Hydrogen saturation correction file
708 BondFragments->AddHydrogenCorrection(configuration->configpath);
709
710 // store adaptive orders into file
711 StoreOrderAtSiteFile(configuration->configpath);
712
713 // restore orbital and Stop values
714 CalculateOrbitals(*configuration);
715
716 // free memory for bond part
717 Log() << Verbose(1) << "Freeing bond memory" << endl;
718 delete(FragmentList); // remove bond molecule from memory
719 Free(&SortIndex);
720 } else {
721 Log() << Verbose(1) << "FragmentList is zero on return, splitting failed." << endl;
722 }
723 delete(BondFragments);
724 Log() << Verbose(0) << "End of bond fragmentation." << endl;
725
726 return ((int)(!FragmentationToDo)+1); // 1 - continue, 2 - stop (no fragmentation occured)
727};
728
729
730/** Stores pairs (Atom::nr, Atom::AdaptiveOrder) into file.
731 * Atoms not present in the file get "-1".
732 * \param *out output stream for debugging
733 * \param *path path to file ORDERATSITEFILE
734 * \return true - file writable, false - not writable
735 */
736bool molecule::StoreOrderAtSiteFile(char *path)
737{
738 stringstream line;
739 ofstream file;
740
741 line << path << "/" << FRAGMENTPREFIX << ORDERATSITEFILE;
742 file.open(line.str().c_str());
743 Log() << Verbose(1) << "Writing OrderAtSite " << ORDERATSITEFILE << " ... " << endl;
744 if (file != NULL) {
745 ActOnAllAtoms( &atom::OutputOrder, &file );
746 file.close();
747 Log() << Verbose(1) << "done." << endl;
748 return true;
749 } else {
750 Log() << Verbose(1) << "failed to open file " << line.str() << "." << endl;
751 return false;
752 }
753};
754
755/** Parses pairs(Atom::nr, Atom::AdaptiveOrder) from file and stores in molecule's Atom's.
756 * Atoms not present in the file get "0".
757 * \param *out output stream for debugging
758 * \param *path path to file ORDERATSITEFILEe
759 * \return true - file found and scanned, false - file not found
760 * \sa ParseKeySetFile() and CheckAdjacencyFileAgainstMolecule() as this is meant to be used in conjunction with the two
761 */
762bool molecule::ParseOrderAtSiteFromFile(char *path)
763{
764 unsigned char *OrderArray = Calloc<unsigned char>(AtomCount, "molecule::ParseOrderAtSiteFromFile - *OrderArray");
765 bool *MaxArray = Calloc<bool>(AtomCount, "molecule::ParseOrderAtSiteFromFile - *MaxArray");
766 bool status;
767 int AtomNr, value;
768 stringstream line;
769 ifstream file;
770
771 Log() << Verbose(1) << "Begin of ParseOrderAtSiteFromFile" << endl;
772 line << path << "/" << FRAGMENTPREFIX << ORDERATSITEFILE;
773 file.open(line.str().c_str());
774 if (file != NULL) {
775 while (!file.eof()) { // parse from file
776 AtomNr = -1;
777 file >> AtomNr;
778 if (AtomNr != -1) { // test whether we really parsed something (this is necessary, otherwise last atom is set twice and to 0 on second time)
779 file >> value;
780 OrderArray[AtomNr] = value;
781 file >> value;
782 MaxArray[AtomNr] = value;
783 //Log() << Verbose(2) << "AtomNr " << AtomNr << " with order " << (int)OrderArray[AtomNr] << " and max order set to " << (int)MaxArray[AtomNr] << "." << endl;
784 }
785 }
786 file.close();
787
788 // set atom values
789 SetAtomValueToIndexedArray( OrderArray, &atom::nr, &atom::AdaptiveOrder );
790 SetAtomValueToIndexedArray( MaxArray, &atom::nr, &atom::MaxOrder );
791
792 Log() << Verbose(1) << "done." << endl;
793 status = true;
794 } else {
795 Log() << Verbose(1) << "failed to open file " << line.str() << "." << endl;
796 status = false;
797 }
798 Free(&OrderArray);
799 Free(&MaxArray);
800
801 Log() << Verbose(1) << "End of ParseOrderAtSiteFromFile" << endl;
802 return status;
803};
804
805
806
807/** Looks through a StackClass<atom *> and returns the likeliest removal candiate.
808 * \param *out output stream for debugging messages
809 * \param *&Leaf KeySet to look through
810 * \param *&ShortestPathList list of the shortest path to decide which atom to suggest as removal candidate in the end
811 * \param index of the atom suggested for removal
812 */
813int molecule::LookForRemovalCandidate(KeySet *&Leaf, int *&ShortestPathList)
814{
815 atom *Runner = NULL;
816 int SP, Removal;
817
818 Log() << Verbose(2) << "Looking for removal candidate." << endl;
819 SP = -1; //0; // not -1, so that Root is never removed
820 Removal = -1;
821 for (KeySet::iterator runner = Leaf->begin(); runner != Leaf->end(); runner++) {
822 Runner = FindAtom((*runner));
823 if (Runner->type->Z != 1) { // skip all those added hydrogens when re-filling snake stack
824 if (ShortestPathList[(*runner)] > SP) { // remove the oldest one with longest shortest path
825 SP = ShortestPathList[(*runner)];
826 Removal = (*runner);
827 }
828 }
829 }
830 return Removal;
831};
832
833/** Initializes some value for putting fragment of \a *mol into \a *Leaf.
834 * \param *mol total molecule
835 * \param *Leaf fragment molecule
836 * \param &Leaflet pointer to KeySet structure
837 * \param **SonList calloc'd list which atom of \a *Leaf is a son of which atom in \a *mol
838 * \return number of atoms in fragment
839 */
840int StoreFragmentFromKeySet_Init(molecule *mol, molecule *Leaf, KeySet &Leaflet, atom **SonList)
841{
842 atom *FatherOfRunner = NULL;
843
844 Leaf->BondDistance = mol->BondDistance;
845 for(int i=NDIM*2;i--;)
846 Leaf->cell_size[i] = mol->cell_size[i];
847
848 // first create the minimal set of atoms from the KeySet
849 int size = 0;
850 for(KeySet::iterator runner = Leaflet.begin(); runner != Leaflet.end(); runner++) {
851 FatherOfRunner = mol->FindAtom((*runner)); // find the id
852 SonList[FatherOfRunner->nr] = Leaf->AddCopyAtom(FatherOfRunner);
853 size++;
854 }
855 return size;
856};
857
858/** Creates an induced subgraph out of a fragmental key set, adding bonds and hydrogens (if treated specially).
859 * \param *out output stream for debugging messages
860 * \param *mol total molecule
861 * \param *Leaf fragment molecule
862 * \param IsAngstroem whether we have Ansgtroem or bohrradius
863 * \param **SonList list which atom of \a *Leaf is a son of which atom in \a *mol
864 */
865void CreateInducedSubgraphOfFragment(molecule *mol, molecule *Leaf, atom **SonList, bool IsAngstroem)
866{
867 bool LonelyFlag = false;
868 atom *OtherFather = NULL;
869 atom *FatherOfRunner = NULL;
870 Leaf->CountAtoms();
871
872 atom *Runner = Leaf->start;
873 while (Runner->next != Leaf->end) {
874 Runner = Runner->next;
875 LonelyFlag = true;
876 FatherOfRunner = Runner->father;
877 if (SonList[FatherOfRunner->nr] != NULL) { // check if this, our father, is present in list
878 // create all bonds
879 for (BondList::const_iterator BondRunner = FatherOfRunner->ListOfBonds.begin(); BondRunner != FatherOfRunner->ListOfBonds.end(); (++BondRunner)) {
880 OtherFather = (*BondRunner)->GetOtherAtom(FatherOfRunner);
881// Log() << Verbose(2) << "Father " << *FatherOfRunner << " of son " << *SonList[FatherOfRunner->nr] << " is bound to " << *OtherFather;
882 if (SonList[OtherFather->nr] != NULL) {
883// Log() << Verbose(0) << ", whose son is " << *SonList[OtherFather->nr] << "." << endl;
884 if (OtherFather->nr > FatherOfRunner->nr) { // add bond (nr check is for adding only one of both variants: ab, ba)
885// Log() << Verbose(3) << "Adding Bond: ";
886// Log() << Verbose(0) <<
887 Leaf->AddBond(Runner, SonList[OtherFather->nr], (*BondRunner)->BondDegree);
888// Log() << Verbose(0) << "." << endl;
889 //NumBonds[Runner->nr]++;
890 } else {
891// Log() << Verbose(3) << "Not adding bond, labels in wrong order." << endl;
892 }
893 LonelyFlag = false;
894 } else {
895// Log() << Verbose(0) << ", who has no son in this fragment molecule." << endl;
896#ifdef ADDHYDROGEN
897 //Log() << Verbose(3) << "Adding Hydrogen to " << Runner->Name << " and a bond in between." << endl;
898 if(!Leaf->AddHydrogenReplacementAtom((*BondRunner), Runner, FatherOfRunner, OtherFather, IsAngstroem))
899 exit(1);
900#endif
901 //NumBonds[Runner->nr] += Binder->BondDegree;
902 }
903 }
904 } else {
905 eLog() << Verbose(1) << "Son " << Runner->Name << " has father " << FatherOfRunner->Name << " but its entry in SonList is " << SonList[FatherOfRunner->nr] << "!" << endl;
906 }
907 if ((LonelyFlag) && (Leaf->AtomCount > 1)) {
908 Log() << Verbose(0) << *Runner << "has got bonds only to hydrogens!" << endl;
909 }
910#ifdef ADDHYDROGEN
911 while ((Runner->next != Leaf->end) && (Runner->next->type->Z == 1)) // skip added hydrogen
912 Runner = Runner->next;
913#endif
914 }
915};
916
917/** Stores a fragment from \a KeySet into \a molecule.
918 * First creates the minimal set of atoms from the KeySet, then creates the bond structure from the complete
919 * molecule and adds missing hydrogen where bonds were cut.
920 * \param *out output stream for debugging messages
921 * \param &Leaflet pointer to KeySet structure
922 * \param IsAngstroem whether we have Ansgtroem or bohrradius
923 * \return pointer to constructed molecule
924 */
925molecule * molecule::StoreFragmentFromKeySet(KeySet &Leaflet, bool IsAngstroem)
926{
927 atom **SonList = Calloc<atom*>(AtomCount, "molecule::StoreFragmentFromStack: **SonList");
928 molecule *Leaf = new molecule(elemente);
929
930// Log() << Verbose(1) << "Begin of StoreFragmentFromKeyset." << endl;
931 StoreFragmentFromKeySet_Init(this, Leaf, Leaflet, SonList);
932 // create the bonds between all: Make it an induced subgraph and add hydrogen
933// Log() << Verbose(2) << "Creating bonds from father graph (i.e. induced subgraph creation)." << endl;
934 CreateInducedSubgraphOfFragment(this, Leaf, SonList, IsAngstroem);
935
936 //Leaflet->Leaf->ScanForPeriodicCorrection(out);
937 Free(&SonList);
938// Log() << Verbose(1) << "End of StoreFragmentFromKeyset." << endl;
939 return Leaf;
940};
941
942
943/** Clears the touched list
944 * \param *out output stream for debugging
945 * \param verbosity verbosity level
946 * \param *&TouchedList touched list
947 * \param SubOrder current suborder
948 * \param TouchedIndex currently touched
949 */
950void SPFragmentGenerator_ClearingTouched(int verbosity, int *&TouchedList, int SubOrder, int &TouchedIndex)
951{
952 Log() << Verbose(1+verbosity) << "Clearing touched list." << endl;
953 for (TouchedIndex=SubOrder+1;TouchedIndex--;) // empty touched list
954 TouchedList[TouchedIndex] = -1;
955 TouchedIndex = 0;
956
957}
958
959/** Adds the current combination of the power set to the snake stack.
960 * \param *out output stream for debugging
961 * \param verbosity verbosity level
962 * \param CurrentCombination
963 * \param SetDimension maximum number of bits in power set
964 * \param *FragmentSet snake stack to remove from
965 * \param *&TouchedList touched list
966 * \param TouchedIndex currently touched
967 * \return number of set bits
968 */
969int AddPowersetToSnakeStack(int verbosity, int CurrentCombination, int SetDimension, KeySet *FragmentSet, bond **BondsSet, int *&TouchedList, int &TouchedIndex)
970{
971 atom *OtherWalker = NULL;
972 bool bit = false;
973 KeySetTestPair TestKeySetInsert;
974
975 int Added = 0;
976 for (int j=0;j<SetDimension;j++) { // pull out every bit by shifting
977 bit = ((CurrentCombination & (1 << j)) != 0); // mask the bit for the j-th bond
978 if (bit) { // if bit is set, we add this bond partner
979 OtherWalker = BondsSet[j]->rightatom; // rightatom is always the one more distant, i.e. the one to add
980 //Log() << Verbose(1+verbosity) << "Current Bond is " << BondsSet[j] << ", checking on " << *OtherWalker << "." << endl;
981 Log() << Verbose(2+verbosity) << "Adding " << *OtherWalker << " with nr " << OtherWalker->nr << "." << endl;
982 TestKeySetInsert = FragmentSet->insert(OtherWalker->nr);
983 if (TestKeySetInsert.second) {
984 TouchedList[TouchedIndex++] = OtherWalker->nr; // note as added
985 Added++;
986 } else {
987 Log() << Verbose(2+verbosity) << "This was item was already present in the keyset." << endl;
988 }
989 } else {
990 Log() << Verbose(2+verbosity) << "Not adding." << endl;
991 }
992 }
993 return Added;
994};
995
996/** Counts the number of elements in a power set.
997 * \param *SetFirst
998 * \param *SetLast
999 * \param *&TouchedList touched list
1000 * \param TouchedIndex currently touched
1001 * \return number of elements
1002 */
1003int CountSetMembers(bond *SetFirst, bond *SetLast, int *&TouchedList, int TouchedIndex)
1004{
1005 int SetDimension = 0;
1006 bond *Binder = SetFirst; // start node for this level
1007 while (Binder->next != SetLast) { // compare to end node of this level
1008 Binder = Binder->next;
1009 for (int k=TouchedIndex;k--;) {
1010 if (Binder->Contains(TouchedList[k])) // if we added this very endpiece
1011 SetDimension++;
1012 }
1013 }
1014 return SetDimension;
1015};
1016
1017/** Counts the number of elements in a power set.
1018 * \param *BondsList bonds list to fill
1019 * \param *SetFirst
1020 * \param *SetLast
1021 * \param *&TouchedList touched list
1022 * \param TouchedIndex currently touched
1023 * \return number of elements
1024 */
1025int FillBondsList(bond **BondsList, bond *SetFirst, bond *SetLast, int *&TouchedList, int TouchedIndex)
1026{
1027 int SetDimension = 0;
1028 bond *Binder = SetFirst; // start node for this level
1029 while (Binder->next != SetLast) { // compare to end node of this level
1030 Binder = Binder->next;
1031 for (int k=0;k<TouchedIndex;k++) {
1032 if (Binder->leftatom->nr == TouchedList[k]) // leftatom is always the close one
1033 BondsList[SetDimension++] = Binder;
1034 }
1035 }
1036 return SetDimension;
1037};
1038
1039/** Remove all items that were added on this SP level.
1040 * \param *out output stream for debugging
1041 * \param verbosity verbosity level
1042 * \param *FragmentSet snake stack to remove from
1043 * \param *&TouchedList touched list
1044 * \param TouchedIndex currently touched
1045 */
1046void RemoveAllTouchedFromSnakeStack(int verbosity, KeySet *FragmentSet, int *&TouchedList, int &TouchedIndex)
1047{
1048 int Removal = 0;
1049 for(int j=0;j<TouchedIndex;j++) {
1050 Removal = TouchedList[j];
1051 Log() << Verbose(2+verbosity) << "Removing item nr. " << Removal << " from snake stack." << endl;
1052 FragmentSet->erase(Removal);
1053 TouchedList[j] = -1;
1054 }
1055 Log() << Verbose(2) << "Remaining local nr.s on snake stack are: ";
1056 for(KeySet::iterator runner = FragmentSet->begin(); runner != FragmentSet->end(); runner++)
1057 Log() << Verbose(0) << (*runner) << " ";
1058 Log() << Verbose(0) << endl;
1059 TouchedIndex = 0; // set Index to 0 for list of atoms added on this level
1060};
1061
1062/** From a given set of Bond sorted by Shortest Path distance, create all possible fragments of size \a SetDimension.
1063 * -# loops over every possible combination (2^dimension of edge set)
1064 * -# inserts current set, if there's still space left
1065 * -# yes: calls SPFragmentGenerator with structure, created new edge list and size respective to root dist
1066ance+1
1067 * -# no: stores fragment into keyset list by calling InsertFragmentIntoGraph
1068 * -# removes all items added into the snake stack (in UniqueFragments structure) added during level (root
1069distance) and current set
1070 * \param *out output stream for debugging
1071 * \param FragmentSearch UniqueFragments structure with all values needed
1072 * \param RootDistance current shortest path level, whose set of edges is represented by **BondsSet
1073 * \param SetDimension Number of possible bonds on this level (i.e. size of the array BondsSet[])
1074 * \param SubOrder remaining number of allowed vertices to add
1075 */
1076void molecule::SPFragmentGenerator(struct UniqueFragments *FragmentSearch, int RootDistance, bond **BondsSet, int SetDimension, int SubOrder)
1077{
1078 int verbosity = 0; //FragmentSearch->ANOVAOrder-SubOrder;
1079 int NumCombinations;
1080 int bits, TouchedIndex, SubSetDimension, SP, Added;
1081 int SpaceLeft;
1082 int *TouchedList = Malloc<int>(SubOrder + 1, "molecule::SPFragmentGenerator: *TouchedList");
1083 bond **BondsList = NULL;
1084 KeySetTestPair TestKeySetInsert;
1085
1086 NumCombinations = 1 << SetDimension;
1087
1088 // here for all bonds of Walker all combinations of end pieces (from the bonds)
1089 // have to be added and for the remaining ANOVA order GraphCrawler be called
1090 // recursively for the next level
1091
1092 Log() << Verbose(1+verbosity) << "Begin of SPFragmentGenerator." << endl;
1093 Log() << Verbose(1+verbosity) << "We are " << RootDistance << " away from Root, which is " << *FragmentSearch->Root << ", SubOrder is " << SubOrder << ", SetDimension is " << SetDimension << " and this means " << NumCombinations-1 << " combination(s)." << endl;
1094
1095 // initialised touched list (stores added atoms on this level)
1096 SPFragmentGenerator_ClearingTouched(verbosity, TouchedList, SubOrder, TouchedIndex);
1097
1098 // create every possible combination of the endpieces
1099 Log() << Verbose(1+verbosity) << "Going through all combinations of the power set." << endl;
1100 for (int i=1;i<NumCombinations;i++) { // sweep through all power set combinations (skip empty set!)
1101 // count the set bit of i
1102 bits = 0;
1103 for (int j=SetDimension;j--;)
1104 bits += (i & (1 << j)) >> j;
1105
1106 Log() << Verbose(1+verbosity) << "Current set is " << Binary(i | (1 << SetDimension)) << ", number of bits is " << bits << "." << endl;
1107 if (bits <= SubOrder) { // if not greater than additional atoms allowed on stack, continue
1108 // --1-- add this set of the power set of bond partners to the snake stack
1109 Added = AddPowersetToSnakeStack(verbosity, i, SetDimension, FragmentSearch->FragmentSet, BondsSet, TouchedList, TouchedIndex);
1110
1111 SpaceLeft = SubOrder - Added ;// SubOrder - bits; // due to item's maybe being already present, this does not work anymore
1112 if (SpaceLeft > 0) {
1113 Log() << Verbose(1+verbosity) << "There's still some space left on stack: " << SpaceLeft << "." << endl;
1114 if (SubOrder > 1) { // Due to Added above we have to check extra whether we're not already reaching beyond the desired Order
1115 // --2-- look at all added end pieces of this combination, construct bond subsets and sweep through a power set of these by recursion
1116 SP = RootDistance+1; // this is the next level
1117
1118 // first count the members in the subset
1119 SubSetDimension = CountSetMembers(FragmentSearch->BondsPerSPList[2*SP], FragmentSearch->BondsPerSPList[2*SP+1], TouchedList, TouchedIndex);
1120
1121 // then allocate and fill the list
1122 BondsList = Malloc<bond*>(SubSetDimension, "molecule::SPFragmentGenerator: **BondsList");
1123 SubSetDimension = FillBondsList(BondsList, FragmentSearch->BondsPerSPList[2*SP], FragmentSearch->BondsPerSPList[2*SP+1], TouchedList, TouchedIndex);
1124
1125 // then iterate
1126 Log() << Verbose(2+verbosity) << "Calling subset generator " << SP << " away from root " << *FragmentSearch->Root << " with sub set dimension " << SubSetDimension << "." << endl;
1127 SPFragmentGenerator(FragmentSearch, SP, BondsList, SubSetDimension, SubOrder-bits);
1128
1129 Free(&BondsList);
1130 }
1131 } else {
1132 // --2-- otherwise store the complete fragment
1133 Log() << Verbose(1+verbosity) << "Enough items on stack for a fragment!" << endl;
1134 // store fragment as a KeySet
1135 Log() << Verbose(2) << "Found a new fragment[" << FragmentSearch->FragmentCounter << "], local nr.s are: ";
1136 for(KeySet::iterator runner = FragmentSearch->FragmentSet->begin(); runner != FragmentSearch->FragmentSet->end(); runner++)
1137 Log() << Verbose(0) << (*runner) << " ";
1138 Log() << Verbose(0) << endl;
1139 //if (!CheckForConnectedSubgraph(FragmentSearch->FragmentSet))
1140 //eLog() << Verbose(1) << "The found fragment is not a connected subgraph!" << endl;
1141 InsertFragmentIntoGraph(FragmentSearch);
1142 }
1143
1144 // --3-- remove all added items in this level from snake stack
1145 Log() << Verbose(1+verbosity) << "Removing all items that were added on this SP level " << RootDistance << "." << endl;
1146 RemoveAllTouchedFromSnakeStack(verbosity, FragmentSearch->FragmentSet, TouchedList, TouchedIndex);
1147 } else {
1148 Log() << Verbose(2+verbosity) << "More atoms to add for this set (" << bits << ") than space left on stack " << SubOrder << ", skipping this set." << endl;
1149 }
1150 }
1151 Free(&TouchedList);
1152 Log() << Verbose(1+verbosity) << "End of SPFragmentGenerator, " << RootDistance << " away from Root " << *FragmentSearch->Root << " and SubOrder is " << SubOrder << "." << endl;
1153};
1154
1155/** Allocates memory for UniqueFragments::BondsPerSPList.
1156 * \param *out output stream
1157 * \param Order bond order (limits BFS exploration and "number of digits" in power set generation
1158 * \param FragmentSearch UniqueFragments
1159 * \sa FreeSPList()
1160 */
1161void InitialiseSPList(int Order, struct UniqueFragments &FragmentSearch)
1162{
1163 FragmentSearch.BondsPerSPList = Malloc<bond*>(Order * 2, "molecule::PowerSetGenerator: ***BondsPerSPList");
1164 FragmentSearch.BondsPerSPCount = Malloc<int>(Order, "molecule::PowerSetGenerator: *BondsPerSPCount");
1165 for (int i=Order;i--;) {
1166 FragmentSearch.BondsPerSPList[2*i] = new bond(); // start node
1167 FragmentSearch.BondsPerSPList[2*i+1] = new bond(); // end node
1168 FragmentSearch.BondsPerSPList[2*i]->next = FragmentSearch.BondsPerSPList[2*i+1]; // intertwine these two
1169 FragmentSearch.BondsPerSPList[2*i+1]->previous = FragmentSearch.BondsPerSPList[2*i];
1170 FragmentSearch.BondsPerSPCount[i] = 0;
1171 }
1172};
1173
1174/** Free's memory for for UniqueFragments::BondsPerSPList.
1175 * \param *out output stream
1176 * \param Order bond order (limits BFS exploration and "number of digits" in power set generation
1177 * \param FragmentSearch UniqueFragments\
1178 * \sa InitialiseSPList()
1179 */
1180void FreeSPList(int Order, struct UniqueFragments &FragmentSearch)
1181{
1182 Free(&FragmentSearch.BondsPerSPCount);
1183 for (int i=Order;i--;) {
1184 delete(FragmentSearch.BondsPerSPList[2*i]);
1185 delete(FragmentSearch.BondsPerSPList[2*i+1]);
1186 }
1187 Free(&FragmentSearch.BondsPerSPList);
1188};
1189
1190/** Sets FragmenSearch to initial value.
1191 * Sets UniqueFragments::ShortestPathList entries to zero, UniqueFragments::BondsPerSPCount to zero (except zero level to 1) and
1192 * adds initial bond UniqueFragments::Root to UniqueFragments::Root to UniqueFragments::BondsPerSPList
1193 * \param *out output stream
1194 * \param Order bond order (limits BFS exploration and "number of digits" in power set generation
1195 * \param FragmentSearch UniqueFragments
1196 * \sa FreeSPList()
1197 */
1198void SetSPList(int Order, struct UniqueFragments &FragmentSearch)
1199{
1200 // prepare Label and SP arrays of the BFS search
1201 FragmentSearch.ShortestPathList[FragmentSearch.Root->nr] = 0;
1202
1203 // prepare root level (SP = 0) and a loop bond denoting Root
1204 for (int i=Order;i--;)
1205 FragmentSearch.BondsPerSPCount[i] = 0;
1206 FragmentSearch.BondsPerSPCount[0] = 1;
1207 bond *Binder = new bond(FragmentSearch.Root, FragmentSearch.Root);
1208 add(Binder, FragmentSearch.BondsPerSPList[1]);
1209};
1210
1211/** Resets UniqueFragments::ShortestPathList and cleans bonds from UniqueFragments::BondsPerSPList.
1212 * \param *out output stream
1213 * \param Order bond order (limits BFS exploration and "number of digits" in power set generation
1214 * \param FragmentSearch UniqueFragments
1215 * \sa InitialiseSPList()
1216 */
1217void ResetSPList(int Order, struct UniqueFragments &FragmentSearch)
1218{
1219 bond *Binder = NULL;
1220 Log() << Verbose(0) << "Free'ing all found lists. and resetting index lists" << endl;
1221 for(int i=Order;i--;) {
1222 Log() << Verbose(1) << "Current SP level is " << i << ": ";
1223 Binder = FragmentSearch.BondsPerSPList[2*i];
1224 while (Binder->next != FragmentSearch.BondsPerSPList[2*i+1]) {
1225 Binder = Binder->next;
1226 // Log() << Verbose(0) << "Removing atom " << Binder->leftatom->nr << " and " << Binder->rightatom->nr << "." << endl; // make sure numbers are local
1227 FragmentSearch.ShortestPathList[Binder->leftatom->nr] = -1;
1228 FragmentSearch.ShortestPathList[Binder->rightatom->nr] = -1;
1229 }
1230 // delete added bonds
1231 cleanup(FragmentSearch.BondsPerSPList[2*i], FragmentSearch.BondsPerSPList[2*i+1]);
1232 // also start and end node
1233 Log() << Verbose(0) << "cleaned." << endl;
1234 }
1235};
1236
1237
1238/** Fills the Bonds per Shortest Path List and set the vertex labels.
1239 * \param *out output stream
1240 * \param Order bond order (limits BFS exploration and "number of digits" in power set generation
1241 * \param FragmentSearch UniqueFragments
1242 * \param *mol molecule with atoms and bonds
1243 * \param RestrictedKeySet Restricted vertex set to use in context of molecule
1244 */
1245void FillSPListandLabelVertices(int Order, struct UniqueFragments &FragmentSearch, molecule *mol, KeySet RestrictedKeySet)
1246{
1247 // Actually, we should construct a spanning tree vom the root atom and select all edges therefrom and put them into
1248 // according shortest path lists. However, we don't. Rather we fill these lists right away, as they do form a spanning
1249 // tree already sorted into various SP levels. That's why we just do loops over the depth (CurrentSP) and breadth
1250 // (EdgeinSPLevel) of this tree ...
1251 // In another picture, the bonds always contain a direction by rightatom being the one more distant from root and hence
1252 // naturally leftatom forming its predecessor, preventing the BFS"seeker" from continuing in the wrong direction.
1253 int AtomKeyNr = -1;
1254 atom *Walker = NULL;
1255 atom *OtherWalker = NULL;
1256 atom *Predecessor = NULL;
1257 bond *CurrentEdge = NULL;
1258 bond *Binder = NULL;
1259 int RootKeyNr = FragmentSearch.Root->GetTrueFather()->nr;
1260 int RemainingWalkers = -1;
1261 int SP = -1;
1262
1263 Log() << Verbose(0) << "Starting BFS analysis ..." << endl;
1264 for (SP = 0; SP < (Order-1); SP++) {
1265 Log() << Verbose(1) << "New SP level reached: " << SP << ", creating new SP list with " << FragmentSearch.BondsPerSPCount[SP] << " item(s)";
1266 if (SP > 0) {
1267 Log() << Verbose(0) << ", old level closed with " << FragmentSearch.BondsPerSPCount[SP-1] << " item(s)." << endl;
1268 FragmentSearch.BondsPerSPCount[SP] = 0;
1269 } else
1270 Log() << Verbose(0) << "." << endl;
1271
1272 RemainingWalkers = FragmentSearch.BondsPerSPCount[SP];
1273 CurrentEdge = FragmentSearch.BondsPerSPList[2*SP]; /// start of this SP level's list
1274 while (CurrentEdge->next != FragmentSearch.BondsPerSPList[2*SP+1]) { /// end of this SP level's list
1275 CurrentEdge = CurrentEdge->next;
1276 RemainingWalkers--;
1277 Walker = CurrentEdge->rightatom; // rightatom is always the one more distant
1278 Predecessor = CurrentEdge->leftatom; // ... and leftatom is predecessor
1279 AtomKeyNr = Walker->nr;
1280 Log() << Verbose(0) << "Current Walker is: " << *Walker << " with nr " << Walker->nr << " and SP of " << SP << ", with " << RemainingWalkers << " remaining walkers on this level." << endl;
1281 // check for new sp level
1282 // go through all its bonds
1283 Log() << Verbose(1) << "Going through all bonds of Walker." << endl;
1284 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
1285 OtherWalker = (*Runner)->GetOtherAtom(Walker);
1286 if ((RestrictedKeySet.find(OtherWalker->nr) != RestrictedKeySet.end())
1287 #ifdef ADDHYDROGEN
1288 && (OtherWalker->type->Z != 1)
1289 #endif
1290 ) { // skip hydrogens and restrict to fragment
1291 Log() << Verbose(2) << "Current partner is " << *OtherWalker << " with nr " << OtherWalker->nr << " in bond " << *(*Runner) << "." << endl;
1292 // set the label if not set (and push on root stack as well)
1293 if ((OtherWalker != Predecessor) && (OtherWalker->GetTrueFather()->nr > RootKeyNr)) { // only pass through those with label bigger than Root's
1294 FragmentSearch.ShortestPathList[OtherWalker->nr] = SP+1;
1295 Log() << Verbose(3) << "Set Shortest Path to " << FragmentSearch.ShortestPathList[OtherWalker->nr] << "." << endl;
1296 // add the bond in between to the SP list
1297 Binder = new bond(Walker, OtherWalker); // create a new bond in such a manner, that bond::rightatom is always the one more distant
1298 add(Binder, FragmentSearch.BondsPerSPList[2*(SP+1)+1]);
1299 FragmentSearch.BondsPerSPCount[SP+1]++;
1300 Log() << Verbose(3) << "Added its bond to SP list, having now " << FragmentSearch.BondsPerSPCount[SP+1] << " item(s)." << endl;
1301 } else {
1302 if (OtherWalker != Predecessor)
1303 Log() << Verbose(3) << "Not passing on, as index of " << *OtherWalker << " " << OtherWalker->GetTrueFather()->nr << " is smaller than that of Root " << RootKeyNr << "." << endl;
1304 else
1305 Log() << Verbose(3) << "This is my predecessor " << *Predecessor << "." << endl;
1306 }
1307 } else Log() << Verbose(2) << "Is not in the restricted keyset or skipping hydrogen " << *OtherWalker << "." << endl;
1308 }
1309 }
1310 }
1311};
1312
1313/** prints the Bonds per Shortest Path list in UniqueFragments.
1314 * \param *out output stream
1315 * \param Order bond order (limits BFS exploration and "number of digits" in power set generation
1316 * \param FragmentSearch UniqueFragments
1317 */
1318void OutputSPList(int Order, struct UniqueFragments &FragmentSearch)
1319{
1320 bond *Binder = NULL;
1321 Log() << Verbose(0) << "Printing all found lists." << endl;
1322 for(int i=1;i<Order;i++) { // skip the root edge in the printing
1323 Binder = FragmentSearch.BondsPerSPList[2*i];
1324 Log() << Verbose(1) << "Current SP level is " << i << "." << endl;
1325 while (Binder->next != FragmentSearch.BondsPerSPList[2*i+1]) {
1326 Binder = Binder->next;
1327 Log() << Verbose(2) << *Binder << endl;
1328 }
1329 }
1330};
1331
1332/** Simply counts all bonds in all UniqueFragments::BondsPerSPList lists.
1333 * \param *out output stream
1334 * \param Order bond order (limits BFS exploration and "number of digits" in power set generation
1335 * \param FragmentSearch UniqueFragments
1336 */
1337int CountNumbersInBondsList(int Order, struct UniqueFragments &FragmentSearch)
1338{
1339 bond *Binder = NULL;
1340 int SP = -1; // the Root <-> Root edge must be subtracted!
1341 for(int i=Order;i--;) { // sum up all found edges
1342 Binder = FragmentSearch.BondsPerSPList[2*i];
1343 while (Binder->next != FragmentSearch.BondsPerSPList[2*i+1]) {
1344 Binder = Binder->next;
1345 SP++;
1346 }
1347 }
1348 return SP;
1349};
1350
1351/** Creates a list of all unique fragments of certain vertex size from a given graph \a Fragment for a given root vertex in the context of \a this molecule.
1352 * -# initialises UniqueFragments structure
1353 * -# fills edge list via BFS
1354 * -# creates the fragment by calling recursive function SPFragmentGenerator with UniqueFragments structure, 0 as
1355 root distance, the edge set, its dimension and the current suborder
1356 * -# Free'ing structure
1357 * Note that we may use the fact that the atoms are SP-ordered on the atomstack. I.e. when popping always the last, we first get all
1358 * with SP of 2, then those with SP of 3, then those with SP of 4 and so on.
1359 * \param *out output stream for debugging
1360 * \param Order bond order (limits BFS exploration and "number of digits" in power set generation
1361 * \param FragmentSearch UniqueFragments structure containing TEFactor, root atom and so on
1362 * \param RestrictedKeySet Restricted vertex set to use in context of molecule
1363 * \return number of inserted fragments
1364 * \note ShortestPathList in FragmentSearch structure is probably due to NumberOfAtomsSPLevel and SP not needed anymore
1365 */
1366int molecule::PowerSetGenerator(int Order, struct UniqueFragments &FragmentSearch, KeySet RestrictedKeySet)
1367{
1368 bond **BondsList = NULL;
1369 int Counter = FragmentSearch.FragmentCounter; // mark current value of counter
1370
1371 Log() << Verbose(0) << endl;
1372 Log() << Verbose(0) << "Begin of PowerSetGenerator with order " << Order << " at Root " << *FragmentSearch.Root << "." << endl;
1373
1374 SetSPList(Order, FragmentSearch);
1375
1376 // do a BFS search to fill the SP lists and label the found vertices
1377 FillSPListandLabelVertices(Order, FragmentSearch, this, RestrictedKeySet);
1378
1379 // outputting all list for debugging
1380 OutputSPList(Order, FragmentSearch);
1381
1382 // creating fragments with the found edge sets (may be done in reverse order, faster)
1383 int SP = CountNumbersInBondsList(Order, FragmentSearch);
1384 Log() << Verbose(0) << "Total number of edges is " << SP << "." << endl;
1385 if (SP >= (Order-1)) {
1386 // start with root (push on fragment stack)
1387 Log() << Verbose(0) << "Starting fragment generation with " << *FragmentSearch.Root << ", local nr is " << FragmentSearch.Root->nr << "." << endl;
1388 FragmentSearch.FragmentSet->clear();
1389 Log() << Verbose(0) << "Preparing subset for this root and calling generator." << endl;
1390
1391 // prepare the subset and call the generator
1392 BondsList = Calloc<bond*>(FragmentSearch.BondsPerSPCount[0], "molecule::PowerSetGenerator: **BondsList");
1393 BondsList[0] = FragmentSearch.BondsPerSPList[0]->next; // on SP level 0 there's only the root bond
1394
1395 SPFragmentGenerator(&FragmentSearch, 0, BondsList, FragmentSearch.BondsPerSPCount[0], Order);
1396
1397 Free(&BondsList);
1398 } else {
1399 Log() << Verbose(0) << "Not enough total number of edges to build " << Order << "-body fragments." << endl;
1400 }
1401
1402 // as FragmentSearch structure is used only once, we don't have to clean it anymore
1403 // remove root from stack
1404 Log() << Verbose(0) << "Removing root again from stack." << endl;
1405 FragmentSearch.FragmentSet->erase(FragmentSearch.Root->nr);
1406
1407 // free'ing the bonds lists
1408 ResetSPList(Order, FragmentSearch);
1409
1410 // return list
1411 Log() << Verbose(0) << "End of PowerSetGenerator." << endl;
1412 return (FragmentSearch.FragmentCounter - Counter);
1413};
1414
1415bool KeyCompare::operator() (const KeySet SubgraphA, const KeySet SubgraphB) const
1416{
1417 //Log() << Verbose(0) << "my check is used." << endl;
1418 if (SubgraphA.size() < SubgraphB.size()) {
1419 return true;
1420 } else {
1421 if (SubgraphA.size() > SubgraphB.size()) {
1422 return false;
1423 } else {
1424 KeySet::iterator IteratorA = SubgraphA.begin();
1425 KeySet::iterator IteratorB = SubgraphB.begin();
1426 while ((IteratorA != SubgraphA.end()) && (IteratorB != SubgraphB.end())) {
1427 if ((*IteratorA) < (*IteratorB))
1428 return true;
1429 else if ((*IteratorA) > (*IteratorB)) {
1430 return false;
1431 } // else, go on to next index
1432 IteratorA++;
1433 IteratorB++;
1434 } // end of while loop
1435 }// end of check in case of equal sizes
1436 }
1437 return false; // if we reach this point, they are equal
1438};
1439
1440
1441/** Combines all KeySets from all orders into single ones (with just unique entries).
1442 * \param *out output stream for debugging
1443 * \param *&FragmentList list to fill
1444 * \param ***FragmentLowerOrdersList
1445 * \param &RootStack stack with all root candidates (unequal to each atom in complete molecule if adaptive scheme is applied)
1446 * \param *mol molecule with atoms and bonds
1447 */
1448int CombineAllOrderListIntoOne(Graph *&FragmentList, Graph ***FragmentLowerOrdersList, KeyStack &RootStack, molecule *mol)
1449{
1450 int RootNr = 0;
1451 int RootKeyNr = 0;
1452 int StartNr = 0;
1453 int counter = 0;
1454 int NumLevels = 0;
1455 atom *Walker = NULL;
1456
1457 Log() << Verbose(0) << "Combining the lists of all orders per order and finally into a single one." << endl;
1458 if (FragmentList == NULL) {
1459 FragmentList = new Graph;
1460 counter = 0;
1461 } else {
1462 counter = FragmentList->size();
1463 }
1464
1465 StartNr = RootStack.back();
1466 do {
1467 RootKeyNr = RootStack.front();
1468 RootStack.pop_front();
1469 Walker = mol->FindAtom(RootKeyNr);
1470 NumLevels = 1 << (Walker->AdaptiveOrder - 1);
1471 for(int i=0;i<NumLevels;i++) {
1472 if (FragmentLowerOrdersList[RootNr][i] != NULL) {
1473 InsertGraphIntoGraph(*FragmentList, (*FragmentLowerOrdersList[RootNr][i]), &counter);
1474 }
1475 }
1476 RootStack.push_back(Walker->nr);
1477 RootNr++;
1478 } while (RootKeyNr != StartNr);
1479 return counter;
1480};
1481
1482/** Free's memory allocated for all KeySets from all orders.
1483 * \param *out output stream for debugging
1484 * \param ***FragmentLowerOrdersList
1485 * \param &RootStack stack with all root candidates (unequal to each atom in complete molecule if adaptive scheme is applied)
1486 * \param *mol molecule with atoms and bonds
1487 */
1488void FreeAllOrdersList(Graph ***FragmentLowerOrdersList, KeyStack &RootStack, molecule *mol)
1489{
1490 Log() << Verbose(1) << "Free'ing the lists of all orders per order." << endl;
1491 int RootNr = 0;
1492 int RootKeyNr = 0;
1493 int NumLevels = 0;
1494 atom *Walker = NULL;
1495 while (!RootStack.empty()) {
1496 RootKeyNr = RootStack.front();
1497 RootStack.pop_front();
1498 Walker = mol->FindAtom(RootKeyNr);
1499 NumLevels = 1 << (Walker->AdaptiveOrder - 1);
1500 for(int i=0;i<NumLevels;i++) {
1501 if (FragmentLowerOrdersList[RootNr][i] != NULL) {
1502 delete(FragmentLowerOrdersList[RootNr][i]);
1503 }
1504 }
1505 Free(&FragmentLowerOrdersList[RootNr]);
1506 RootNr++;
1507 }
1508 Free(&FragmentLowerOrdersList);
1509};
1510
1511
1512/** Performs BOSSANOVA decomposition at selected sites, increasing the cutoff by one at these sites.
1513 * -# constructs a complete keyset of the molecule
1514 * -# In a loop over all possible roots from the given rootstack
1515 * -# increases order of root site
1516 * -# calls PowerSetGenerator with this order, the complete keyset and the rootkeynr
1517 * -# for all consecutive lower levels PowerSetGenerator is called with the suborder, the higher order keyset
1518as the restricted one and each site in the set as the root)
1519 * -# these are merged into a fragment list of keysets
1520 * -# All fragment lists (for all orders, i.e. from all destination fields) are merged into one list for return
1521 * Important only is that we create all fragments, it is not important if we create them more than once
1522 * as these copies are filtered out via use of the hash table (KeySet).
1523 * \param *out output stream for debugging
1524 * \param Fragment&*List list of already present keystacks (adaptive scheme) or empty list
1525 * \param &RootStack stack with all root candidates (unequal to each atom in complete molecule if adaptive scheme is applied)
1526 * \param *MinimumRingSize minimum ring size for each atom (molecule::Atomcount)
1527 * \return pointer to Graph list
1528 */
1529void molecule::FragmentBOSSANOVA(Graph *&FragmentList, KeyStack &RootStack, int *MinimumRingSize)
1530{
1531 Graph ***FragmentLowerOrdersList = NULL;
1532 int NumLevels = 0;
1533 int NumMolecules = 0;
1534 int TotalNumMolecules = 0;
1535 int *NumMoleculesOfOrder = NULL;
1536 int Order = 0;
1537 int UpgradeCount = RootStack.size();
1538 KeyStack FragmentRootStack;
1539 int RootKeyNr = 0;
1540 int RootNr = 0;
1541 struct UniqueFragments FragmentSearch;
1542
1543 Log() << Verbose(0) << "Begin of FragmentBOSSANOVA." << endl;
1544
1545 // FragmentLowerOrdersList is a 2D-array of pointer to MoleculeListClass objects, one dimension represents the ANOVA expansion of a single order (i.e. 5)
1546 // with all needed lower orders that are subtracted, the other dimension is the BondOrder (i.e. from 1 to 5)
1547 NumMoleculesOfOrder = Calloc<int>(UpgradeCount, "molecule::FragmentBOSSANOVA: *NumMoleculesOfOrder");
1548 FragmentLowerOrdersList = Calloc<Graph**>(UpgradeCount, "molecule::FragmentBOSSANOVA: ***FragmentLowerOrdersList");
1549
1550 // initialise the fragments structure
1551 FragmentSearch.FragmentCounter = 0;
1552 FragmentSearch.FragmentSet = new KeySet;
1553 FragmentSearch.Root = FindAtom(RootKeyNr);
1554 FragmentSearch.ShortestPathList = Malloc<int>(AtomCount, "molecule::PowerSetGenerator: *ShortestPathList");
1555 for (int i=AtomCount;i--;) {
1556 FragmentSearch.ShortestPathList[i] = -1;
1557 }
1558
1559 // Construct the complete KeySet which we need for topmost level only (but for all Roots)
1560 atom *Walker = start;
1561 KeySet CompleteMolecule;
1562 while (Walker->next != end) {
1563 Walker = Walker->next;
1564 CompleteMolecule.insert(Walker->GetTrueFather()->nr);
1565 }
1566
1567 // 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
1568 // 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),
1569 // hence we have overall four 2th order levels for splitting. This also allows for putting all into a single array (FragmentLowerOrdersList[])
1570 // 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)
1571 RootNr = 0; // counts through the roots in RootStack
1572 while ((RootNr < UpgradeCount) && (!RootStack.empty())) {
1573 RootKeyNr = RootStack.front();
1574 RootStack.pop_front();
1575 Walker = FindAtom(RootKeyNr);
1576 // check cyclic lengths
1577 //if ((MinimumRingSize[Walker->GetTrueFather()->nr] != -1) && (Walker->GetTrueFather()->AdaptiveOrder+1 > MinimumRingSize[Walker->GetTrueFather()->nr])) {
1578 // Log() << Verbose(0) << "Bond order " << Walker->GetTrueFather()->AdaptiveOrder << " of Root " << *Walker << " greater than or equal to Minimum Ring size of " << MinimumRingSize << " found is not allowed." << endl;
1579 //} else
1580 {
1581 // increase adaptive order by one
1582 Walker->GetTrueFather()->AdaptiveOrder++;
1583 Order = Walker->AdaptiveOrder = Walker->GetTrueFather()->AdaptiveOrder;
1584
1585 // initialise Order-dependent entries of UniqueFragments structure
1586 InitialiseSPList(Order, FragmentSearch);
1587
1588 // allocate memory for all lower level orders in this 1D-array of ptrs
1589 NumLevels = 1 << (Order-1); // (int)pow(2,Order);
1590 FragmentLowerOrdersList[RootNr] = Calloc<Graph*>(NumLevels, "molecule::FragmentBOSSANOVA: **FragmentLowerOrdersList[]");
1591
1592 // create top order where nothing is reduced
1593 Log() << Verbose(0) << "==============================================================================================================" << endl;
1594 Log() << Verbose(0) << "Creating KeySets of Bond Order " << Order << " for " << *Walker << ", " << (RootStack.size()-RootNr) << " Roots remaining." << endl; // , NumLevels is " << NumLevels << "
1595
1596 // Create list of Graphs of current Bond Order (i.e. F_{ij})
1597 FragmentLowerOrdersList[RootNr][0] = new Graph;
1598 FragmentSearch.TEFactor = 1.;
1599 FragmentSearch.Leaflet = FragmentLowerOrdersList[RootNr][0]; // set to insertion graph
1600 FragmentSearch.Root = Walker;
1601 NumMoleculesOfOrder[RootNr] = PowerSetGenerator(Walker->AdaptiveOrder, FragmentSearch, CompleteMolecule);
1602
1603 // output resulting number
1604 Log() << Verbose(1) << "Number of resulting KeySets is: " << NumMoleculesOfOrder[RootNr] << "." << endl;
1605 if (NumMoleculesOfOrder[RootNr] != 0) {
1606 NumMolecules = 0;
1607 } else {
1608 Walker->GetTrueFather()->MaxOrder = true;
1609 }
1610 // now, we have completely filled each cell of FragmentLowerOrdersList[] for the current Walker->AdaptiveOrder
1611 //NumMoleculesOfOrder[Walker->AdaptiveOrder-1] = NumMolecules;
1612 TotalNumMolecules += NumMoleculesOfOrder[RootNr];
1613// Log() << Verbose(1) << "Number of resulting molecules for Order " << (int)Walker->GetTrueFather()->AdaptiveOrder << " is: " << NumMoleculesOfOrder[RootNr] << "." << endl;
1614 RootStack.push_back(RootKeyNr); // put back on stack
1615 RootNr++;
1616
1617 // free Order-dependent entries of UniqueFragments structure for next loop cycle
1618 FreeSPList(Order, FragmentSearch);
1619 }
1620 }
1621 Log() << Verbose(0) << "==============================================================================================================" << endl;
1622 Log() << Verbose(1) << "Total number of resulting molecules is: " << TotalNumMolecules << "." << endl;
1623 Log() << Verbose(0) << "==============================================================================================================" << endl;
1624
1625 // cleanup FragmentSearch structure
1626 Free(&FragmentSearch.ShortestPathList);
1627 delete(FragmentSearch.FragmentSet);
1628
1629 // now, FragmentLowerOrdersList is complete, it looks - for BondOrder 5 - as this (number is the ANOVA Order of the terms therein)
1630 // 5433222211111111
1631 // 43221111
1632 // 3211
1633 // 21
1634 // 1
1635
1636 // Subsequently, we combine all into a single list (FragmentList)
1637 CombineAllOrderListIntoOne(FragmentList, FragmentLowerOrdersList, RootStack, this);
1638 FreeAllOrdersList(FragmentLowerOrdersList, RootStack, this);
1639 Free(&NumMoleculesOfOrder);
1640
1641 Log() << Verbose(0) << "End of FragmentBOSSANOVA." << endl;
1642};
1643
1644/** Corrects the nuclei position if the fragment was created over the cell borders.
1645 * Scans all bonds, checks the distance, if greater than typical, we have a candidate for the correction.
1646 * We remove the bond whereafter the graph probably separates. Then, we translate the one component periodically
1647 * and re-add the bond. Looping on the distance check.
1648 * \param *out ofstream for debugging messages
1649 */
1650void molecule::ScanForPeriodicCorrection()
1651{
1652 bond *Binder = NULL;
1653 bond *OtherBinder = NULL;
1654 atom *Walker = NULL;
1655 atom *OtherWalker = NULL;
1656 double *matrix = ReturnFullMatrixforSymmetric(cell_size);
1657 enum Shading *ColorList = NULL;
1658 double tmp;
1659 Vector Translationvector;
1660 //class StackClass<atom *> *CompStack = NULL;
1661 class StackClass<atom *> *AtomStack = new StackClass<atom *>(AtomCount);
1662 bool flag = true;
1663
1664 Log() << Verbose(2) << "Begin of ScanForPeriodicCorrection." << endl;
1665
1666 ColorList = Calloc<enum Shading>(AtomCount, "molecule::ScanForPeriodicCorrection: *ColorList");
1667 while (flag) {
1668 // remove bonds that are beyond bonddistance
1669 for(int i=NDIM;i--;)
1670 Translationvector.x[i] = 0.;
1671 // scan all bonds
1672 Binder = first;
1673 flag = false;
1674 while ((!flag) && (Binder->next != last)) {
1675 Binder = Binder->next;
1676 for (int i=NDIM;i--;) {
1677 tmp = fabs(Binder->leftatom->x.x[i] - Binder->rightatom->x.x[i]);
1678 //Log() << Verbose(3) << "Checking " << i << "th distance of " << *Binder->leftatom << " to " << *Binder->rightatom << ": " << tmp << "." << endl;
1679 if (tmp > BondDistance) {
1680 OtherBinder = Binder->next; // note down binding partner for later re-insertion
1681 unlink(Binder); // unlink bond
1682 Log() << Verbose(2) << "Correcting at bond " << *Binder << "." << endl;
1683 flag = true;
1684 break;
1685 }
1686 }
1687 }
1688 if (flag) {
1689 // create translation vector from their periodically modified distance
1690 for (int i=NDIM;i--;) {
1691 tmp = Binder->leftatom->x.x[i] - Binder->rightatom->x.x[i];
1692 if (fabs(tmp) > BondDistance)
1693 Translationvector.x[i] = (tmp < 0) ? +1. : -1.;
1694 }
1695 Translationvector.MatrixMultiplication(matrix);
1696 //Log() << Verbose(3) << "Translation vector is ";
1697 Translationvector.Output();
1698 Log() << Verbose(0) << endl;
1699 // apply to all atoms of first component via BFS
1700 for (int i=AtomCount;i--;)
1701 ColorList[i] = white;
1702 AtomStack->Push(Binder->leftatom);
1703 while (!AtomStack->IsEmpty()) {
1704 Walker = AtomStack->PopFirst();
1705 //Log() << Verbose (3) << "Current Walker is: " << *Walker << "." << endl;
1706 ColorList[Walker->nr] = black; // mark as explored
1707 Walker->x.AddVector(&Translationvector); // translate
1708 for (BondList::const_iterator Runner = Walker->ListOfBonds.begin(); Runner != Walker->ListOfBonds.end(); (++Runner)) {
1709 if ((*Runner) != Binder) {
1710 OtherWalker = (*Runner)->GetOtherAtom(Walker);
1711 if (ColorList[OtherWalker->nr] == white) {
1712 AtomStack->Push(OtherWalker); // push if yet unexplored
1713 }
1714 }
1715 }
1716 }
1717 // re-add bond
1718 link(Binder, OtherBinder);
1719 } else {
1720 Log() << Verbose(3) << "No corrections for this fragment." << endl;
1721 }
1722 //delete(CompStack);
1723 }
1724
1725 // free allocated space from ReturnFullMatrixforSymmetric()
1726 delete(AtomStack);
1727 Free(&ColorList);
1728 Free(&matrix);
1729 Log() << Verbose(2) << "End of ScanForPeriodicCorrection." << endl;
1730};
Note: See TracBrowser for help on using the repository browser.