source: src/molecule_fragmentation.cpp@ 174e0e

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 174e0e was 266237, checked in by Frederik Heber <heber@…>, 15 years ago

Huge refactoring: molecule::ListOfBondsPerAtom and molecule::NumberOfBondsPerAtom removed, atom::ListOfBonds introduced. Unit Test for ListOfBonds manipulation introduced.

  • changes to builder.cpp: removed CreateListOfBondsPerAtom() calls, as the creation of the global arrays is not necessary anymore
  • changes to LinkedCell: LinkedCell::CheckBounds(int[NDIM]) does not admonish out of bonds as this is not desired for the local offset which may become out of bounds.
  • changes to lists.hpp templates: BUGFIX: unlink() now sets ->next and ->previous to NULL, cleanup() uses removedwithoutcheck()
  • new templates for molecule.hpp: SumPerAtom() allows for summation of the return value of atom:...() member fiunctions. This is needed e.g. for atom::CorrectBondDegree()

Signed-off-by: Frederik Heber <heber@…>

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