1 #include "buildISGTopology.h"
2 
3 #include "ExclusionTable.h"
4 #include "ExclusionType.h"
5 #include "GenericTopology.h"
6 #include "iSGPAR.h"
7 #include "PSF.h"
8 #include "Report.h"
9 #include "pmconstants.h"
10 #include "mathutilities.h"
11 #include "stringutilities.h"
12 #include "topologyutilities.h"
13 
14 #include <algorithm>
15 #include <list>
16 #include <map>
17 #include <set>
18 #include <string>
19 #include <vector>
20 
21 using std::list;
22 using std::map;
23 using std::set;
24 using std::pair;
25 using std::string;
26 using std::vector;
27 
28 using namespace ProtoMol::Report;
29 
30 //#define DEBUG_PRINT_MOLECULETABLE
31 
32 namespace ProtoMol {
33 
34   static void findNextNeighbor(int a, vector<int>& v, vector<PairInt>& p, vector<bool>& unused, const vector<vector<int> >& graph, set<PairInt>& pairs);
35   // bool cmpSize(const vector<int>& m1, const vector<int>& m2);
36 
37 
38   //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
39   //
40   //  buildISGTopology
41   //
42   //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
43 
buildISGTopology(GenericTopology * topo,const PSF & psf,const iSGPAR & par)44   void buildISGTopology(GenericTopology* topo,const PSF& psf, const iSGPAR& par) {
45     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
46     // First, generate the array of atomtypes
47     // Each time a new atom comes up, we need to check if it is
48     // already in the vector....
49     // NOTE:  this may take a while for large systems; however, it will cut
50     // down on the size of the atomTypes vector, and therefore, the amount
51     // access time in the back end.
52     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
53 
54     topo->atoms.clear();
55     topo->atomTypes.clear();
56     topo->bonds.clear();
57     topo->angles.clear();
58     topo->dihedrals.clear();
59     topo->impropers.clear();
60 
61     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
62     // Get the atoms
63     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
64 
65     map<string,int> atomLookUpTable;
66 
67     // loop over all atoms in the PSF object
68     for (vector<PSF::Atom>::const_iterator atom = psf.atoms.begin();
69 	 atom != psf.atoms.end(); ++atom) {
70 
71       // store the atom_type name for this atom
72       AtomType tempatomtype;
73       tempatomtype.name = atom->atom_type;
74       tempatomtype.symbolName = atomTypeToSymbolName(atom->atom_type);
75 
76       // Now check if this already exists (same name)
77       if (atomLookUpTable.find(tempatomtype.name) == atomLookUpTable.end()) {
78 
79 	// this is a new atom type, add to the atomTypes list
80 	atomLookUpTable[tempatomtype.name] = topo->atomTypes.size();
81 	topo->atomTypes.push_back(tempatomtype);
82       }
83 
84 
85       Atom tempatom;
86       // First, we need to find the index. (an integer corresponding
87       // to the type of the atom
88       tempatom.type = atomLookUpTable[tempatomtype.name];
89 
90       // Now, the mass and scaled charge.  These are straightforward.
91       tempatom.scaledCharge = (atom->charge)*Constant::SQRTCOULOMBCONSTANT;
92       tempatom.scaledMass = atom->mass;
93 
94       // Now we need the size of the group for heavy atom ordering
95       // We need to parse the name for any H's then any numbers following
96       // First, if the atom is an H then this is 0
97       if (atom->atom_type == "H"){
98 	tempatom.hvyAtom = 0;
99       }
100       else{
101 	// Otherwise, we need to parse..
102 	// Initialize to 1
103 	tempatom.hvyAtom = 1;
104 	for (unsigned int pos = 0; pos < atom->atom_type.size(); ++pos){
105 	  if (atom->atom_type[pos] == 'H'){
106 	    string number = "";
107 	    while (isdigit(atom->atom_type[++pos]))   {
108 	      number += atom->atom_type[pos];
109 	    }
110 	    if (number == "") // never entered loop, default is 1
111 	      number = "1";
112 	    tempatom.hvyAtom += atoi(number.c_str());
113 	  }
114 	}
115       }
116       // C/C++ starts at 0, where PSF/PDB at 1
117       tempatom.atomNum = atom->number-1;
118       // Also the molecule - using residue sequence for now
119       topo->atoms.push_back(tempatom);
120 
121     } // end loop over atoms
122 
123     // calculate the # of degrees of freedom, if there are any bond constraints
124     // they will be subtracted later by ModifierShake
125     topo->degreesOfFreedom = 3 * topo->atoms.size() - 3;
126 
127     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
128     // Get the bonds
129     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
130 
131     // First create look-up-table (into PAR object bonds)
132     map<string,vector<iSGPAR::Bond>::const_iterator> bondLookUpTable;
133 
134     // take the bonds from the PAR object and put then into the map/look-up-table
135     for (vector<iSGPAR::Bond>::const_iterator bond = par.bonds.begin();
136 	 bond != par.bonds.end(); ++bond){
137       bondLookUpTable[bond->atom1+","+bond->atom2] = bond;
138       //report << (*bond)<< ", " << bond->atom1 << ", " << bond->atom2 <<endr;
139     }
140 
141 
142     // Find the parameters from PAR
143     int ignoredBonds = 0;
144 
145     // loop over the bond list in the PSF object
146     for (vector<PSF::Bond>::const_iterator bond = psf.bonds.begin();
147 	 bond != psf.bonds.end(); ++bond){
148 
149       // store the ID numbers of the bonded atoms
150       int atom1 = bond->atom1-1;
151       int atom2 = bond->atom2-1;
152 
153       // store the type names of the bonded atoms
154       string bond1(topo->atomTypes[topo->atoms[atom1].type].name);
155       string bond2(topo->atomTypes[topo->atoms[atom2].type].name);
156 
157       // look into the bond table and see if this bond type was stored in the PAR file
158       map<string,vector<iSGPAR::Bond>::const_iterator>::const_iterator currentbond = bondLookUpTable.find(bond1+","+bond2);
159 
160       // if this bond type has not been found, try reversing the order of the atom types
161       if(currentbond == bondLookUpTable.end()){
162 	currentbond = bondLookUpTable.find(bond2+","+bond1);}
163 
164       // if we still have not found this bond type in the PAR object, report an error
165       if(currentbond == bondLookUpTable.end()){
166 	report << error << "Could not find bond \'"<<bond1<<"\'-\'"<<bond2<<"\'."<<std::endl;
167 	for (map<string,vector<iSGPAR::Bond>::const_iterator>::const_iterator i = bondLookUpTable.begin();
168 	     i != bondLookUpTable.end();
169 	     ++i){
170 	  report << plain << i->first<<std::endl;
171 	}
172 	report << endr;
173       }  // end if statement
174 
175       // if we have found this bond type then copy the bond parameters
176       // into the topology
177       Bond tempbond;
178 
179       // store the time-zero identity of this bond
180       int bondIdentity = psf.atoms[atom1].identity;
181       tempbond.springConstant = currentbond->second->forceConstant[bondIdentity];
182       tempbond.restLength = currentbond->second->distance[bondIdentity];
183       tempbond.atom1 = atom1;
184       tempbond.atom2 = atom2;
185       topo->bonds.push_back(tempbond);
186       if(tempbond.springConstant == 0.0)
187 	++ignoredBonds;
188     }
189 
190     if(ignoredBonds > 0)
191       report << hint << "Systems contains "<<ignoredBonds<<" bonds with zero force constants."<<endr;
192 
193     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
194     // Get the angles
195     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
196 
197     // First create look-up-table (into PAR object angles)
198     map<string,vector<iSGPAR::Angle>::const_iterator> angleLookUpTable;
199 
200     // take the angles from the PAR object and put them into the map
201     for (vector<iSGPAR::Angle>::const_iterator angle = par.angles.begin();
202 	 angle != par.angles.end(); ++angle){
203       angleLookUpTable[angle->atom1+","+angle->atom2+","+angle->atom3] = angle;
204       //report << (*angle)<<endr;
205     }
206 
207     // Find the parameters from PAR
208     int ignoredAngles = 0;
209 
210     // loop over the angle list in the PSF object
211     for (vector<PSF::Angle>::const_iterator angle = psf.angles.begin();
212 	 angle != psf.angles.end(); ++angle){
213 
214       // store the ID numbers of the atoms in this angle
215       int atom1 = angle->atom1-1;
216       int atom2 = angle->atom2-1;
217       int atom3 = angle->atom3-1;
218 
219       // store the type names of the atoms in this angle
220       string angle1(topo->atomTypes[topo->atoms[atom1].type].name);
221       string angle2(topo->atomTypes[topo->atoms[atom2].type].name);
222       string angle3(topo->atomTypes[topo->atoms[atom3].type].name);
223 
224       // look into the angle table and see if this angle type was stored in the PAR file
225       map<string,vector<iSGPAR::Angle>::const_iterator>::const_iterator currentangle = angleLookUpTable.find(angle1+","+angle2+","+angle3);
226 
227       // if this angle type has not been found, try reversing the order of the atom types
228       if(currentangle == angleLookUpTable.end())
229 	currentangle = angleLookUpTable.find(angle3+","+angle2+","+angle1);
230 
231       // if we still have not found this angle type in the PAR object, report an error
232       if(currentangle == angleLookUpTable.end())
233 	report << error << "Could not find angle \'"<<angle1<<"\'-\'"<<angle2<<"\'-\'"<<angle3<<"\'."<<endr;
234 
235       // if we have found this angle type then copy the angle parameters
236       // into the topology
237       Angle tempangle;
238 
239       // store the time-zero identity of this angle
240       int angleIdentity = psf.atoms[atom1].identity;
241       tempangle.atom1 = atom1;
242       tempangle.atom2 = atom2;
243       tempangle.atom3 = atom3;
244       tempangle.forceConstant = currentangle->second->forceConstant[angleIdentity];
245       tempangle.restAngle = dtor(currentangle->second->angleval[angleIdentity]);
246 
247       // check to see if a Urey-Bradley term has been specified
248       if (currentangle->second->ub_flag){ // do we want defaults for these
249 	tempangle.ureyBradleyConstant = currentangle->second->k_ub[angleIdentity];
250 	tempangle.ureyBradleyRestLength = currentangle->second->r_ub[angleIdentity];
251       }
252       // no Urey-Bradley term specified
253       else{
254 	tempangle.ureyBradleyConstant = 0.0;
255 	tempangle.ureyBradleyRestLength = 0.0;
256       }
257       topo->angles.push_back(tempangle);
258       if(tempangle.forceConstant == 0.0)
259 	++ignoredAngles;
260     }
261 
262     if(ignoredAngles > 0)
263       report << hint << "Systems contains "<<ignoredAngles<<" angles with zero force constants."<<endr;
264 
265 
266     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
267     // Get the dihedrals
268     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
269     //
270     // One change I made was to assume that a dihedral will only appear
271     // once in the .psf file regardless of it's multiplicity.  The
272     // multiplicity should be handled in the .par file.
273     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
274 
275 
276     // First create look-up-table (into PAR object)
277     map<string,vector<iSGPAR::Dihedral>::const_iterator> dihedralLookUpTable;
278 
279     // take the dihedrals from the PAR object and put them into the map
280     for (vector<iSGPAR::Dihedral>::const_iterator dihedral = par.dihedrals.begin();
281 	 dihedral != par.dihedrals.end(); ++dihedral){
282       dihedralLookUpTable[dihedral->atom1+","+dihedral->atom2+","+dihedral->atom3+","+dihedral->atom4] = dihedral;
283       //report << (*dihedral)<<endr;
284     }
285 
286     // Find the parameters from PAR
287     // loop over the dihedral list in the PSF object
288     for(vector<PSF::Dihedral>::const_iterator dihedral = psf.dihedrals.begin();
289 	dihedral != psf.dihedrals.end(); ++dihedral) {
290 
291       // store the ID numbers of the atoms in this dihedral
292       int atom1 = dihedral->atom1 - 1;
293       int atom2 = dihedral->atom2 - 1;
294       int atom3 = dihedral->atom3 - 1;
295       int atom4 = dihedral->atom4 - 1;
296 
297       // store the type names of the atoms in this dihedral
298       string dihedral1 = topo->atomTypes[topo->atoms[atom1].type].name;
299       string dihedral2 = topo->atomTypes[topo->atoms[atom2].type].name;
300       string dihedral3 = topo->atomTypes[topo->atoms[atom3].type].name;
301       string dihedral4 = topo->atomTypes[topo->atoms[atom4].type].name;
302 
303       // look into the dihedral table and see if this dihedral type was stored in the PAR file
304       map<string,vector<iSGPAR::Dihedral>::const_iterator>::const_iterator currentdihedral =
305 	dihedralLookUpTable.find(dihedral1+","+dihedral2+","+dihedral3+","+dihedral4);
306 
307       // if this dihedral type has not been found, try reversing the order of the atom types
308       if(currentdihedral == dihedralLookUpTable.end())
309 	currentdihedral = dihedralLookUpTable.find(dihedral4+","+dihedral3+","+dihedral2+","+dihedral1);
310 
311       // Try wildcards if necessary
312       if(currentdihedral == dihedralLookUpTable.end()){
313 	currentdihedral = dihedralLookUpTable.find("X,"+dihedral2+","+dihedral3+",X");
314 	if(currentdihedral == dihedralLookUpTable.end())
315 	  currentdihedral = dihedralLookUpTable.find("X,"+dihedral3+","+dihedral2+",X");
316       }
317 
318       // if we still have not found this dihedral type in the PAR object, report an error
319       if(currentdihedral == dihedralLookUpTable.end())
320 	report << error << "Could not find dihedral \'"<<dihedral1<<"\'-\'"<<dihedral2<<"\'-\'"<<dihedral3<<"\'-\'"<<dihedral4<<"\'."<<endr;
321 
322       // if we have found this dihedral type then copy the
323       // dihedral parameters into the topology
324       Torsion torsion;
325 
326       // store the time-zero identity of this torsion
327       int torsionIdentity = psf.atoms[atom1].identity;
328       torsion.atom1         = atom1;
329       torsion.atom2         = atom2;
330       torsion.atom3         = atom3;
331       torsion.atom4         = atom4;
332       torsion.periodicity   = currentdihedral->second->periodicity[torsionIdentity];
333       torsion.forceConstant = currentdihedral->second->forceConstant[torsionIdentity];
334       torsion.phaseShift    = dtor(currentdihedral->second->phaseShift[torsionIdentity]);
335       torsion.multiplicity  = currentdihedral->second->multiplicity[torsionIdentity];
336       torsion.DeltaK.resize(torsion.multiplicity);
337       torsion.DeltaPhase.resize(torsion.multiplicity);
338 
339       //report << plain
340       //     << dihedral1 <<","
341       //     << dihedral2 <<","
342       //     << dihedral3 <<","
343       //     << dihedral4 <<","
344       //     << torsion.atom1 <<","
345       //     << torsion.atom2 <<","
346       //     << torsion.atom3 <<","
347       //     << torsion.atom4 <<","
348       //     << torsion.periodicity.size() <<",";
349       //for (unsigned int tors=0; tors<torsion.periodicity.size(); tors++) {
350       //  report << torsion.periodicity[tors] <<","
351       //	 << torsion.forceConstant[tors] <<","
352       //	 << torsion.phaseShift[tors] <<","; }
353       //report << torsion.multiplicity << endr;
354 
355       if(topo->dihedrals.size() == 0 ||
356 	 topo->dihedrals[topo->dihedrals.size()-1].atom1 != atom1 ||
357 	 topo->dihedrals[topo->dihedrals.size()-1].atom2 != atom2 ||
358 	 topo->dihedrals[topo->dihedrals.size()-1].atom3 != atom3 ||
359 	 topo->dihedrals[topo->dihedrals.size()-1].atom4 != atom4){
360 	topo->dihedrals.push_back(torsion);
361 	//report << plain //<< (wildcard ? "#":"")
362 	//          << dihedral1 <<","
363 	//          << dihedral2 <<","
364 	//          << dihedral3 <<","
365 	//          << dihedral4 <<","
366 	//          << torsion.atom1 <<","
367 	//          << torsion.atom2 <<","
368 	//          << torsion.atom3 <<","
369 	//          << torsion.atom4 <<","
370 	//          << torsion.periodicity[0] <<","
371 	//          << torsion.periodicity.size() <<","
372 	//          << torsion.forceConstant[0] <<","
373 	//          << torsion.phaseShift[0] <<","
374 	//          << torsion.multiplicity << endr;
375       }
376     }
377 
378 
379     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
380     // Get the impropers
381     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
382     //
383     // One change I made was to assume that a improper will only appear
384     // once in the .psf file regardless of it's multiplicity.  The
385     // multiplicity should be handled in the .par file.
386     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
387 
388     //         No wildcard usage is allowed for bonds and angles. For dihedrals,
389     // two types are allowed; A - B - C - D (all four atoms specified) and
390     // X - A - B - X (only middle two atoms specified). Double dihedral
391     // specifications may be specified for the four atom type by listing a
392     // given set twice. When specifying this type in the topology file, specify
393     // a dihedral twice (with nothing intervening) and both forms will be used.
394     //
395     //         There are five choices for wildcard usage for improper dihedrals;
396     // 1) A - B - C - D  (all four atoms, double specification allowed)
397     // 2) A - X - X - B
398     // 3) X - A - B - C
399     // 4) X - A - B - X
400     // 5) X - X - A - B
401     // When classifying an improper dihedral, the first acceptable match (from
402     // the above order) is chosen. The match may be made in either direction
403     // ( A - B - C - D = D - C - B - A).
404     //
405     //         The periodicity value for dihedrals and improper dihedral terms
406     // must be an integer. If it is positive, then a cosine functional form is used.
407     // Only positive values of 1,2,3,4,5 and 6 are allowed for the vector, parallel
408     // vector and cray routines. Slow and scalar routines can use any positive
409     // integer and thus dihedral constrains can be of any periodicity.
410     //  Reference angle 0.0 and 180.0 degree correspond to minimum in staggered
411     // and eclipsed respectively. Any reference angle is allowed. The value
412     // 180 should be prefered over -180 since it is parsed faster and more
413     // accuratly. When the periodicity is given as zero, for OTHER THAN THE
414     // FIRST dihdral in a multiple dihedral set, then a the amplitude is a
415     // constant added to the energy. This is needed to effect the
416     // Ryckaert-Bellemans potential for hydrocarbons (see below).
417 
418 
419     // First create look-up-table into PAR object
420     map<string,vector<iSGPAR::Improper>::const_iterator> improperLookUpTable;
421 
422     // take the impropers from the PAR object and put them into the map
423     for (vector<iSGPAR::Improper>::const_iterator improper = par.impropers.begin();
424 	 improper != par.impropers.end(); ++improper){
425       improperLookUpTable[improper->atom1+","+improper->atom2+","+improper->atom3+","+improper->atom4] = improper;
426       //report << (*improper)<<endr;
427     }
428 
429     // Find the parameters from PAR
430     // loop over the improper list in the PSF object
431     for(vector<PSF::Improper>::const_iterator improper = psf.impropers.begin();
432 	improper != psf.impropers.end(); ++improper) {
433 
434       // store the ID numbers of the atoms in this improper
435       int atom1 = improper->atom1 - 1;
436       int atom2 = improper->atom2 - 1;
437       int atom3 = improper->atom3 - 1;
438       int atom4 = improper->atom4 - 1;
439 
440       // store the type names of the atoms in this improper
441       string improper1 = topo->atomTypes[topo->atoms[atom1].type].name;
442       string improper2 = topo->atomTypes[topo->atoms[atom2].type].name;
443       string improper3 = topo->atomTypes[topo->atoms[atom3].type].name;
444       string improper4 = topo->atomTypes[topo->atoms[atom4].type].name;
445 
446       // look into the improper table and see if this improper type was stored in the PAR file
447       map<string,vector<iSGPAR::Improper>::const_iterator>::const_iterator currentimproper =
448 	improperLookUpTable.find(improper1+","+improper2+","+improper3+","+improper4);
449 
450       // if this dihedral type has not been found, try reversing the order of the atom types
451       if(currentimproper == improperLookUpTable.end())
452 	currentimproper = improperLookUpTable.find(improper4+","+improper3+","+improper2+","+improper1);
453 
454       // Try wildcards if necessary
455       // 2) A - X - X - B
456       if(currentimproper == improperLookUpTable.end()){
457 	currentimproper = improperLookUpTable.find(improper1+",X,X,"+improper4);
458 	if(currentimproper == improperLookUpTable.end())
459 	  currentimproper = improperLookUpTable.find(improper4+",X,X,"+improper1);
460 
461       }
462       // 3) X - A - B - C
463       if(currentimproper == improperLookUpTable.end()){
464 	currentimproper = improperLookUpTable.find("X,"+improper2+","+improper3+","+improper4);
465 	if(currentimproper == improperLookUpTable.end())
466 	  currentimproper = improperLookUpTable.find(improper4+","+improper3+","+improper2+",X");
467       }
468 
469       // 4) X - A - B - X
470       if(currentimproper == improperLookUpTable.end()){
471 	currentimproper = improperLookUpTable.find("X,"+improper2+","+improper3+",X");
472 	if(currentimproper == improperLookUpTable.end())
473 	  currentimproper = improperLookUpTable.find("X,"+improper3+","+improper2+",X");
474       }
475 
476       // 5) X - X - A - B
477       if(currentimproper == improperLookUpTable.end()){
478 	currentimproper = improperLookUpTable.find("X,X,"+improper3+","+improper4);
479 	if(currentimproper == improperLookUpTable.end())
480 	  currentimproper = improperLookUpTable.find(improper4+","+improper3+",X,X");
481       }
482 
483       // if we still have not found this improper type in the PAR object, report an error
484       if(currentimproper == improperLookUpTable.end())
485 	report << error << "Could not find improper."<<endr;
486 
487       // if we have found this improper type then copy the
488       // improper parameters into the topology
489       Torsion torsion;
490 
491       // store the time-zero identity of this improper
492       int improperIdentity = psf.atoms[atom1].identity;
493       torsion.atom1         = atom1;
494       torsion.atom2         = atom2;
495       torsion.atom3         = atom3;
496       torsion.atom4         = atom4;
497       torsion.periodicity.push_back(currentimproper->second->periodicity[improperIdentity]);
498       torsion.forceConstant.push_back(currentimproper->second->forceConstant[improperIdentity]);
499       torsion.phaseShift.push_back(dtor(currentimproper->second->phaseShift[improperIdentity]));
500       torsion.multiplicity  = 1;
501       topo->impropers.push_back(torsion);
502       //     report << plain<< (wildcard ? "#":"")
503       //            << improper1 <<","
504       //            << improper2 <<","
505       //            << improper3 <<","
506       //            << improper4 <<","
507       //            << torsion.atom1 <<","
508       //            << torsion.atom2 <<","
509       //            << torsion.atom3 <<","
510       //            << torsion.atom4 <<","
511       //            << torsion.periodicity[0] <<","
512       //            << torsion.periodicity.size() <<","
513       //            << torsion.forceConstant[0] <<","
514       //            << torsion.phaseShift[0] <<","
515       //            << torsion.multiplicity << endr;
516     }
517 
518 
519     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
520     // LennardJonesParameters
521     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
522 
523     // get some array sizes
524     unsigned int sizeAtomTypes  = topo->atomTypes.size();
525     unsigned int sizeNonbondeds = par.nonbondeds.size();
526     unsigned int sizeNbfixs     = par.nbfixs.size();
527     unsigned int sizeLJbank     = par.nonbondeds[0].epsilon.size();
528 
529     // create the bank of Lennard-Jones parameter tables
530     topo->isgLJParms.createTables(sizeLJbank,sizeAtomTypes);
531 
532 
533     // loop over all atom types
534     for(unsigned int i=0;i<sizeAtomTypes;++i) {
535 
536       // sentinel
537       int foundI = 0;
538       unsigned int AtomI = sizeNonbondeds;
539 
540       // loop over all nonbonded types in the PAR object
541       for(unsigned int k=0;k<sizeNonbondeds;++k) {
542 
543 	// determine if this PAR nonbonded type is the same as the current atom type (i)
544 	int ok = equalWildcard(par.nonbondeds[k].atom,topo->atomTypes[i].name);
545 
546 	// if we have found the current atom type in the PAR object the record its index (k)
547 	if(ok > foundI){
548 	  AtomI = k;
549 	  foundI = ok;
550 	}
551 
552 	// if we have found the current atom type then exit the loop
553 	if(foundI > 1)
554 	  break;
555       } // end loop over k
556 
557       // report an error if we could not find atom type (i) in the PAR object
558       if(foundI <= 0)
559 	report << error <<"Could not find matching parameter nonbonded of atom \'"<<topo->atomTypes[i].name<<"\'."<<endr;
560 
561       // loop over all other (i != j) atom types
562       for(unsigned int j=i;j<sizeAtomTypes;++j) {
563 
564 	// sentinel
565 	int foundJ = 0;
566 	unsigned int AtomJ = sizeNonbondeds;
567 
568 	// loop over all nonbonded types in the PAR object
569 	for(unsigned int k=0;k<sizeNonbondeds;++k) {
570 
571 	  // determine if this PAR nonbonded type is the same as the current atom type (j)
572 	  int ok = equalWildcard(par.nonbondeds[k].atom,topo->atomTypes[j].name);
573 
574 	  // if we have found the current atom type in the PAR object the record its index (k)
575 	  if(ok > foundJ){
576 	    AtomJ = k;
577 	    foundJ = ok;
578 	  }
579 
580 	  // if we have found the current atom type then exit the loop
581 	  if(foundJ > 1)
582 	    break;
583 	}
584 
585 	// report an error if we could not find atom type (j) in the PAR object
586 	if(foundJ<=0)
587 	  report << error <<"Could not find matching parameter nonbonded of atom \'"<<topo->atomTypes[j].name<<"\'."<<endr;
588 
589 	// initialize a set of LJ parameters
590  	LennardJonesParameters paramsij;
591 
592 	// loop over all possible identities of atom i
593 	for (unsigned int identity_I=0; identity_I < sizeLJbank; identity_I++) {
594 
595 	  // loop over all possible identities for atom j
596 	  for (unsigned int identity_J = 0; identity_J < sizeLJbank; identity_J++) {
597 
598 	    // calculate the (Charmm28) minimum energy distances
599 	    Real sigma_i = par.nonbondeds[AtomI].sigma[identity_I];
600 	    Real sigma_j = par.nonbondeds[AtomJ].sigma[identity_J];
601 	    Real sigma14_i = par.nonbondeds[AtomI].sigma14[identity_I];
602 	    Real sigma14_j = par.nonbondeds[AtomJ].sigma14[identity_J];
603 
604 	    // calculate the minimum LJ pair energy
605 	    Real epsilon_i = par.nonbondeds[AtomI].epsilon[identity_I];
606 	    Real epsilon_j = par.nonbondeds[AtomJ].epsilon[identity_J];
607 	    Real epsilon14_i = par.nonbondeds[AtomI].epsilon14[identity_I];
608 	    Real epsilon14_j = par.nonbondeds[AtomJ].epsilon14[identity_J];
609 
610 	    // calculate the ij pair minimum distances and energies
611 	    Real r_ij = sigma_i + sigma_j;
612 	    Real e_ij = sqrt(epsilon_i * epsilon_j);
613 	    Real r14_ij = sigma14_i + sigma14_j;
614 	    Real e14_ij = sqrt(epsilon14_i * epsilon14_j);
615 
616 	    // stores these numbers into the paramsij object
617 	    paramsij.A = power<12>(r_ij) * e_ij;
618 	    paramsij.B = 2 * power<6>(r_ij) * e_ij;
619 	    paramsij.A14 = power<12>(r14_ij) * e14_ij;
620 	    paramsij.B14 = 2 * power<6>(r14_ij) * e14_ij;
621 
622 	    topo->isgLJParms.set(identity_I,identity_J, i, j, paramsij);
623 
624 	  } // end loop over identity_J
625 	} // end loop over identity_I
626       } // end loop over atom types (j)
627     } // end loop over atom types (i)
628 
629 
630     // NbFix
631     // loop over all Nbfix types
632     for(unsigned int k=0;k<sizeNbfixs;++k) {
633 
634       //report << par.nbfixs[k].atom1 << ","<<par.nbfixs[k].atom2<<endr;
635 
636       int ti = 0;
637       int tj = 0;
638       unsigned int bi = sizeNbfixs;
639       unsigned int bj = sizeNbfixs;
640 
641       for(unsigned int i=0;i<sizeAtomTypes;++i){
642 	int ok = equalWildcard(par.nbfixs[k].atom1,topo->atomTypes[i].name);
643 	if(ok > ti){
644 	  bi = i;
645 	  ti = ok;
646 	}
647 	if(ti > 1)
648 	  break;
649       }
650 
651       if(ti <=0) // ???
652 	continue;
653 
654       for(unsigned int j=0;j<sizeAtomTypes;++j){
655 	int ok = equalWildcard(par.nbfixs[k].atom2,topo->atomTypes[j].name);
656 	if(ok > tj){
657 	  bj = j;
658 	  tj = ok;
659 	}
660 	if(tj > 1)
661 	  break;
662       }
663 
664       if(tj<=0)
665 	report << error <<"Could not find matching parameter nbfix of atoms \'"<<par.nbfixs[k].atom1<<"\' - '"<<par.nbfixs[k].atom2<<"\'."<<endr;
666 
667       LennardJonesParameters paramsij;
668 
669       paramsij.A = par.nbfixs[k].a;
670       paramsij.B = par.nbfixs[k].b;
671       paramsij.A14 = par.nbfixs[k].a14;
672       paramsij.B14 = par.nbfixs[k].b14;
673       topo->lennardJonesParameters.set(bi, bj, paramsij);
674 
675     } // end loop over NbFix types
676 
677     // store the molecule information
678     buildMoleculeTable(topo);
679 
680     // build the nonbonded exclusion table
681     buildExclusionTable(topo,topo->exclude);
682 
683     // build the list of bonds, angles, etc. belonging to each molecule
684     buildMoleculeBondingLists(topo);
685 
686     // determine the time-zero identity of each molecule
687     for(unsigned int i=0; i<topo->molecules.size(); i++) {
688 
689       // the ID# of the first atom in this molecule
690       int firstAtom = topo->molecules[i][0];
691 
692       // get the identity number from the psf file
693       topo->molecules[i].type = psf.atoms[firstAtom].identity;
694       topo->molecules[i].newtype = psf.atoms[firstAtom].identity;
695     }
696 
697   } // end build topology function
698 
699 
700   //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
701   //
702   //  buildMoleculeTable
703   //
704   //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
705 
buildMoleculeTable(GenericTopology * topo)706   void buildMoleculeTable(GenericTopology *topo){
707 
708     // *** First we clear all molecules ***
709     topo->molecules.clear();
710 
711     const unsigned int numAtoms = topo->atoms.size();
712 
713     // *** Collecting all possible connections, building the graph ***
714     vector<vector<int> > graph(numAtoms,vector<int>());
715     set<pair<int,int> > pairs;
716     // *** Bonds ***
717     for(unsigned int i=0;i<topo->bonds.size();++i){
718       int a1 = topo->bonds[i].atom1;
719       int a2 = topo->bonds[i].atom2;
720       graph[a1].push_back(a2);
721       graph[a2].push_back(a1);
722       pairs.insert(pair<int,int>(std::min(a1,a2),std::max(a1,a2)));
723     }
724     unsigned int count = pairs.size();
725 
726     // *** Angles ***
727     for(unsigned int i=0;i<topo->angles.size();++i){
728       int a1 = topo->angles[i].atom1;
729       int a2 = topo->angles[i].atom2;
730       int a3 = topo->angles[i].atom3;
731       graph[a1].push_back(a2);
732       graph[a1].push_back(a3);
733       graph[a2].push_back(a1);
734       graph[a2].push_back(a3);
735       graph[a3].push_back(a1);
736       graph[a3].push_back(a2);
737       pairs.insert(pair<int,int>(std::min(a1,a2),std::max(a1,a2)));
738       pairs.insert(pair<int,int>(std::min(a3,a2),std::max(a3,a2)));
739     }
740 
741     if(count < pairs.size())
742       report << hint << "Angles added "<<pairs.size()-count<<" new bond(s)." <<endr;
743     count = pairs.size();
744 
745     // *** Dihedrals ***
746     for(unsigned int i=0;i<topo->dihedrals.size();++i){
747       int a1 = topo->dihedrals[i].atom1;
748       int a2 = topo->dihedrals[i].atom2;
749       int a3 = topo->dihedrals[i].atom3;
750       int a4 = topo->dihedrals[i].atom4;
751       graph[a1].push_back(a2);
752       graph[a1].push_back(a3);
753       graph[a1].push_back(a4);
754       graph[a2].push_back(a1);
755       graph[a2].push_back(a3);
756       graph[a2].push_back(a4);
757       graph[a3].push_back(a1);
758       graph[a3].push_back(a2);
759       graph[a3].push_back(a4);
760       graph[a4].push_back(a1);
761       graph[a4].push_back(a2);
762       graph[a4].push_back(a3);
763       pairs.insert(pair<int,int>(std::min(a1,a2),std::max(a1,a2)));
764       pairs.insert(pair<int,int>(std::min(a3,a2),std::max(a3,a2)));
765       pairs.insert(pair<int,int>(std::min(a3,a4),std::max(a3,a4)));
766     }
767     if(count < pairs.size())
768       report << hint << "Dihedrals added "<<pairs.size()-count<<" new bond(s)." <<endr;
769     count = pairs.size();
770 
771     // *** Impropers ***
772     set<pair<int,int> > pairsAddImpropers;
773     // Impropers are defined over the bonds 1-2,1-3,1-4 or 4-1,4-3,4-2
774     // but MTorsionSystemForce computes distances betweeen 1-2,2-3,3-4
775     // we have to take care about these differences ...
776     for(unsigned int i=0;i<topo->impropers.size();++i){
777       int a1 = topo->impropers[i].atom1;
778       int a2 = topo->impropers[i].atom2;
779       int a3 = topo->impropers[i].atom3;
780       int a4 = topo->impropers[i].atom4;
781       graph[a1].push_back(a2);
782       graph[a1].push_back(a3);
783       graph[a1].push_back(a4);
784       graph[a2].push_back(a1);
785       graph[a2].push_back(a3);
786       graph[a2].push_back(a4);
787       graph[a3].push_back(a1);
788       graph[a3].push_back(a2);
789       graph[a3].push_back(a4);
790       graph[a4].push_back(a1);
791       graph[a4].push_back(a2);
792       graph[a4].push_back(a3);
793       pair<int,int> p0(std::min(a1,a2),std::max(a1,a2));
794       pair<int,int> p1(std::min(a1,a3),std::max(a1,a3));
795       pair<int,int> p2(std::min(a1,a4),std::max(a1,a4));
796       pair<int,int> p3(std::min(a2,a3),std::max(a2,a3));
797       pair<int,int> p4(std::min(a2,a4),std::max(a2,a4));
798       pair<int,int> p5(std::min(a3,a4),std::max(a3,a4));
799       int j0 = 0;
800       int j1 = 0;
801       int j2 = 0;
802       int j3 = 0;
803       int j4 = 0;
804       int j5 = 0;
805       if(pairs.find(p0) != pairs.end()) j0++;
806       if(pairs.find(p1) != pairs.end()) j1++;
807       if(pairs.find(p2) != pairs.end()) j2++;
808       if(pairs.find(p3) != pairs.end()) j3++;
809       if(pairs.find(p4) != pairs.end()) j4++;
810       if(pairs.find(p5) != pairs.end()) j5++;
811       if(j0+j1+j2+j3+j4+j5 < 3){
812 	pairs.insert(p0);
813 	pairs.insert(p1);
814 	pairs.insert(p2);
815       }
816       pairsAddImpropers.insert(p0);
817       pairsAddImpropers.insert(p3);
818       pairsAddImpropers.insert(p5);
819     }
820     if(count < pairs.size())
821       report << hint << "Impropers added "<<pairs.size()-count<<" new bond(s)." <<endr;
822 
823     // Now add the improper pairs
824     for(set<pair<int,int> >::const_iterator i= pairsAddImpropers.begin();i != pairsAddImpropers.end();++i)
825       pairs.insert(*i);
826 
827 
828     count = pairs.size();
829     //report << hint << count << endr;
830     // To keep track which atoms already have been added
831     // to molecules.
832     vector<bool> unused(numAtoms,true);
833 
834 
835     // Recursively finding the atoms beloning to a molecule
836     for(unsigned int i=0;i<numAtoms;++i){
837       vector<int> v;
838       vector<PairInt> p;
839       findNextNeighbor(i,v,p,unused,graph,pairs);
840       if(v.size()>0){
841 	// sort(v.begin(),v.end());
842 	// add this atom list to the molecules array
843 	Molecule mol;
844 	mol.atoms = v;
845 	mol.pairs = p;
846 	topo->molecules.push_back(mol);
847       }
848     }
849 
850     // Uncomment to sort descending after size()
851     // sort(topo->molecules.begin(),topo->molecules.end(),cmpSize);
852 
853     // Look up table for atoms
854     const string h("H");
855     const string o("O");
856     for(unsigned int i=0;i<topo->molecules.size();++i){
857       Real mass = 0.0;
858       const vector<int>& mol = topo->molecules[i].atoms;
859       for(unsigned int j=0;j<mol.size();++j){
860 	int k = mol[j];
861 	topo->atoms[k].molecule = i;
862 	mass += topo->atoms[k].scaledMass;
863       }
864       topo->molecules[i].mass = mass;
865       topo->molecules[i].water = (mol.size() == 3 &&
866 				  ((topo->atomTypes[topo->atoms[mol[0]].type].symbolName == h &&
867 				    topo->atomTypes[topo->atoms[mol[1]].type].symbolName == h &&
868 				    topo->atomTypes[topo->atoms[mol[2]].type].symbolName == o) ||
869 				   (topo->atomTypes[topo->atoms[mol[0]].type].symbolName == h &&
870 				    topo->atomTypes[topo->atoms[mol[1]].type].symbolName == o &&
871 				    topo->atomTypes[topo->atoms[mol[2]].type].symbolName == h) ||
872 				   (topo->atomTypes[topo->atoms[mol[0]].type].symbolName == o &&
873 				    topo->atomTypes[topo->atoms[mol[1]].type].symbolName == h &&
874 				    topo->atomTypes[topo->atoms[mol[2]].type].symbolName == h)));
875 
876     }
877 
878 #if defined(DEBUG_PRINT_MOLECULETABLE)
879     report<< plain << endl << "[buildMoleculeTable]: molecule table printout:" << endl;
880     for(int i=0;i<topo->molecules.size();++i){
881       for(int j=0;j<topo->molecules[i].size();++j){
882 	report << topo->molecules[i][j]<<" ";
883       }
884       report << endl;
885     }
886     report << endr;
887 #endif
888   }
889 
890 
891   //____________________________________________________________findNextNeighbor
findNextNeighbor(int a,vector<int> & v,vector<PairInt> & p,vector<bool> & unused,const vector<vector<int>> & graph,set<PairInt> & pairs)892   void findNextNeighbor(int a, vector<int>& v, vector<PairInt>& p, vector<bool>& unused,
893 			const vector<vector<int> >& graph, set<PairInt>& pairs){
894     if(unused[a]){
895       v.push_back(a);
896       unused[a] = false;
897       for(unsigned int i=0;i<graph[a].size();++i){
898 	set<PairInt>::iterator itr = pairs.find(PairInt(std::min(a,graph[a][i]),std::max(a,graph[a][i])));
899 	if(itr != pairs.end()){
900 	  p.push_back(PairInt(a,graph[a][i]));
901 	  pairs.erase(itr);
902 	}
903 	findNextNeighbor(graph[a][i],v,p,unused,graph,pairs);
904       }
905     }
906   }
907 
908 
909   //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
910   //
911   //  buildExclusionTable
912   //
913   //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
914 
buildExclusionTable(GenericTopology * topo,const ExclusionType & exclusionType)915   void buildExclusionTable(GenericTopology* topo, const ExclusionType& exclusionType) {
916 
917     if(!exclusionType.valid())
918       report << error <<"[buildExclusionTable()] Exclusion type not defined/valid."<<endr;
919 
920     topo->exclude = exclusionType;
921 
922     const int numBonds = topo->bonds.size();
923     const int numAtoms = topo->atoms.size();
924     int a1,a2,a3;
925 
926     // *** Reformatting bond list ***
927     vector< set<int> > bondsParsed(numAtoms);
928     for (int i = 0; i < numBonds; ++i) {
929       a1=topo->bonds[i].atom1;
930       a2=topo->bonds[i].atom2;
931       bondsParsed[a1].insert(a2);
932       bondsParsed[a2].insert(a1);
933     }
934 
935     // *** Building exclusion list ***
936     topo->exclusions.resize(numAtoms);
937     topo->exclusions.clear();
938     list<ExclusionPair> one3s;
939     int exclusionsInserted = 0;
940     if(exclusionType!=ExclusionType::NONE) {
941       for(a2 = 0; a2 < numAtoms; ++a2) {
942 	for(set<int>::iterator i=bondsParsed[a2].begin();i!=bondsParsed[a2].end();++i) {
943 	  a1=*i;
944 	  if(a1<a2) {
945 	    topo->exclusions.add(a1,a2,EXCLUSION_FULL);
946 	    ++exclusionsInserted;
947 	  }
948 	  if(exclusionType==ExclusionType::ONE3||exclusionType==ExclusionType::ONE4||exclusionType==ExclusionType::ONE4MODIFIED) {
949 	    for(set<int>::iterator j=i; j!=bondsParsed[a2].end();++j) {
950 	      a3=*j;
951 	      topo->exclusions.add(a1,a3,EXCLUSION_FULL);
952 	      ++exclusionsInserted;
953 	      one3s.push_front(ExclusionPair(a1,a3));
954 	      one3s.push_front(ExclusionPair(a3,a1));
955 	    }
956 	  }
957 	}
958       }
959       if(exclusionType==ExclusionType::ONE4||exclusionType==ExclusionType::ONE4MODIFIED) {
960 	ExclusionClass currentType = EXCLUSION_NONE;
961 	if(exclusionType==ExclusionType::ONE4)
962 	  currentType=EXCLUSION_FULL;
963 	if(exclusionType==ExclusionType::ONE4MODIFIED)
964 	  currentType=EXCLUSION_MODIFIED;
965 	int curSrc=0;
966 	for(list<ExclusionPair>::iterator i=one3s.begin(); i!=one3s.end(); ++i,++curSrc) {
967 	  a1=i->a1;
968 	  a2=i->a2;
969 	  for(set<int>::iterator j=bondsParsed[a2].lower_bound(a1);j!=bondsParsed[a2].end();++j) {
970 	    a3=*j;
971 	    if(!topo->exclusions.check(a1,a3)) {
972 	      topo->exclusions.add(a1,a3,currentType);
973 	      ++exclusionsInserted;
974 	    }
975 	  }
976 	}
977       }
978     }
979   }
980 
981 
982   //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
983   //
984   //  buildMoleculeBondingLists
985   //
986   //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
987 
buildMoleculeBondingLists(GenericTopology * topo)988   void buildMoleculeBondingLists(GenericTopology* topo) {
989 
990     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
991     // bond lists
992     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
993     // loop over all bonds in the topology
994     for (unsigned int i=0; i<topo->bonds.size(); i++) {
995 
996       // get the ID#s of the two atoms in this bond
997       int atom1 = topo->bonds[i].atom1;
998       int atom2 = topo->bonds[i].atom2;
999 
1000       // loop sentinels
1001       bool found1 = false;
1002       bool found2 = false;
1003       unsigned int m;
1004 
1005       // loop over all molecules
1006       for (m=0; m<topo->molecules.size(); m++) {
1007 
1008 	// loop over all atoms in the atom list for this molecule
1009 	for (unsigned int a=0; a<topo->molecules[m].atoms.size(); a++) {
1010 
1011 	  if (atom1 == topo->molecules[m].atoms[a]) {found1 = true;}
1012 	  else if (atom2 == topo->molecules[m].atoms[a]) {found2 = true;}
1013 
1014 	  if (found1 && found2) {break;}
1015 	} // end atom list loop
1016 
1017 	if (found1 && found2) {break;}
1018       } // end loop over molecules
1019 
1020       // store this bond index number in the molecule's bond list
1021       topo->molecules[m].bondList.push_back(i);
1022     } // end loop over bonds
1023 
1024 
1025     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1026     // angle lists
1027     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1028     // loop over all angles in the topology
1029     for (unsigned int i=0; i<topo->angles.size(); i++) {
1030 
1031       // get the ID#s of the three atoms in this angle
1032       int atom1 = topo->angles[i].atom1;
1033       int atom2 = topo->angles[i].atom2;
1034       int atom3 = topo->angles[i].atom3;
1035 
1036       // loop sentinels
1037       bool found1 = false;
1038       bool found2 = false;
1039       bool found3 = false;
1040       unsigned int m;
1041 
1042       // loop over all molecules
1043       for (m=0; m<topo->molecules.size(); m++) {
1044 
1045 	// loop over all atoms in the atom list for this molecule
1046 	for (unsigned int a=0; a<topo->molecules[m].atoms.size(); a++) {
1047 
1048 	  if (atom1 == topo->molecules[m].atoms[a]) {found1 = true;}
1049 	  else if (atom2 == topo->molecules[m].atoms[a]) {found2 = true;}
1050 	  else if (atom3 == topo->molecules[m].atoms[a]) {found3 = true;}
1051 
1052 	  if (found1 && found2 && found3) {break;}
1053 	} // end atom list loop
1054 
1055 	if (found1 && found2 && found3) {break;}
1056       } // end loop over molecules
1057 
1058       // store this angle index number in the molecule's angle list
1059       topo->molecules[m].angleList.push_back(i);
1060     } // end loop over angles
1061 
1062     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1063     // dihedral lists
1064     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1065     // loop over all diherals in the topology
1066     for (unsigned int i=0; i<topo->dihedrals.size(); i++) {
1067 
1068       // get the ID#s of the four atoms in this dihedral
1069       int atom1 = topo->dihedrals[i].atom1;
1070       int atom2 = topo->dihedrals[i].atom2;
1071       int atom3 = topo->dihedrals[i].atom3;
1072       int atom4 = topo->dihedrals[i].atom4;
1073 
1074       // loop sentinels
1075       bool found1 = false;
1076       bool found2 = false;
1077       bool found3 = false;
1078       bool found4 = false;
1079       unsigned int m;
1080 
1081       // loop over all molecules
1082       for (m=0; m<topo->molecules.size(); m++) {
1083 
1084 	// loop over all atoms in the atom list for this molecule
1085 	for (unsigned int a=0; a<topo->molecules[m].atoms.size(); a++) {
1086 
1087 	  if (atom1 == topo->molecules[m].atoms[a]) {found1 = true;}
1088 	  else if (atom2 == topo->molecules[m].atoms[a]) {found2 = true;}
1089 	  else if (atom3 == topo->molecules[m].atoms[a]) {found3 = true;}
1090 	  else if (atom4 == topo->molecules[m].atoms[a]) {found4 = true;}
1091 
1092 	  if (found1 && found2 && found3 && found4) {break;}
1093 	} // end atom list loop
1094 
1095 	if (found1 && found2 && found3 && found4) {break;}
1096       } // end loop over molecules
1097 
1098       // store this dihedral index number in the molecule's dihderal list
1099       topo->molecules[m].dihedralList.push_back(i);
1100     } // end loop over dihedrals
1101 
1102 
1103     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1104     // improper lists
1105     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1106     // loop over all impropers in the topology
1107     for (unsigned int i=0; i<topo->impropers.size(); i++) {
1108 
1109       // get the ID#s of the four atoms in this improper
1110       int atom1 = topo->impropers[i].atom1;
1111       int atom2 = topo->impropers[i].atom2;
1112       int atom3 = topo->impropers[i].atom3;
1113       int atom4 = topo->impropers[i].atom4;
1114 
1115       // loop sentinels
1116       bool found1 = false;
1117       bool found2 = false;
1118       bool found3 = false;
1119       bool found4 = false;
1120       unsigned int m;
1121 
1122       // loop over all molecules
1123       for (m=0; m<topo->molecules.size(); m++) {
1124 
1125 	// loop over all atoms in the atom list for this molecule
1126 	for (unsigned int a=0; a<topo->molecules[m].atoms.size(); a++) {
1127 
1128 	  if (atom1 == topo->molecules[m].atoms[a]) {found1 = true;}
1129 	  else if (atom2 == topo->molecules[m].atoms[a]) {found2 = true;}
1130 	  else if (atom3 == topo->molecules[m].atoms[a]) {found3 = true;}
1131 	  else if (atom4 == topo->molecules[m].atoms[a]) {found4 = true;}
1132 
1133 	  if (found1 && found2 && found3 && found4) {break;}
1134 	} // end atom list loop
1135 
1136 	if (found1 && found2 && found4) {break;}
1137       } // end loop over molecules
1138 
1139       // store this bond index number in the molecule's improper list
1140       topo->molecules[m].improperList.push_back(i);
1141     } // end loop over impropers
1142   } // end function buildMoleculeBonding Lists
1143 
1144 } // end class
1145 
1146