File: reconstructFragmentProcessor.C

package info (click to toggle)
ball 1.5.0%2Bgit20180813.37fc53c-3
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 239,848 kB
  • sloc: cpp: 326,149; ansic: 4,208; python: 2,303; yacc: 1,778; lex: 1,099; xml: 958; sh: 322; makefile: 93
file content (343 lines) | stat: -rw-r--r-- 11,365 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
// -*- Mode: C++; tab-width: 2; -*-
// vi: set ts=2:
//

#include <BALL/STRUCTURE/reconstructFragmentProcessor.h>

#include <list>
#include <vector>

#include <BALL/KERNEL/PTE.h>
#include <BALL/SYSTEM/path.h>
#include <BALL/KERNEL/bond.h>
#include <BALL/KERNEL/forEach.h>
#include <BALL/MATHS/matrix44.h>
#include <BALL/DATATYPE/stringHashMap.h>
#include <BALL/STRUCTURE/fragmentDB.h>
#include <BALL/STRUCTURE/structureMapper.h>
	
using namespace std;

// #define BALL_DEBUG_RECONSTRUCTFRAGMENTPROCESSOR

#ifdef BALL_DEBUG_RECONSTRUCTFRAGMENTPROCESSOR
#	define DEBUG(a) Log.info() << "ReconstructFragmentProcessor: " << a << std::endl;
#else
#	define DEBUG(a)
#endif

namespace BALL 
{

	/////////////////////////////////////////////////////////////////
	//		ReconstructFragmentProcessor												     //
	/////////////////////////////////////////////////////////////////	

	void ReconstructFragmentProcessor::setFragmentDB(const FragmentDB& db)
	{
		fragment_db_ = &const_cast<FragmentDB&>(db);
	}

	const FragmentDB* ReconstructFragmentProcessor::getFragmentDB() const
	{
		return fragment_db_;
	}

	/**	Identify two reference atoms.
			Performs a breadth-first search for two additional heavy atoms
			starting from the center atom. These atoms are used as 
			anchor points for attaching the next atom.
	*/
	Triple<bool, const Atom*, const Atom*> 
	ReconstructFragmentProcessor::getTwoReferenceAtoms
		(const Atom& ref_center_atom, const HashSet<const Atom*>& allowed)
		
	{
		Triple<bool, const Atom*, const Atom*> result(false, 0, 0);
		
		// a hash set to remember all those atoms we have already visited
		list<const Atom*> atom_list;
		atom_list.push_back(&ref_center_atom);
		
		// abort if we found the three first atoms (beyond the center atom)
		// or we are running out of fresh atoms
		list<const Atom*>::iterator current(atom_list.begin());
		while ((atom_list.size() < 3) && (current != atom_list.end()))
		{
			Atom::BondConstIterator bond((*current)->beginBond());
			for (; +bond; ++bond)
			{
				const Atom* next_atom = bond->getPartner(**current);
				if (allowed.has(next_atom) 
						&& (find(atom_list.begin(), atom_list.end(), next_atom) == atom_list.end()))
				{
					atom_list.push_back(next_atom);
					if (atom_list.size() > 2)
					{
						bond = (*current)->endBond();
						break;
					}
				}
			}
			
			// try the bonds of the next atom in the list
			current++;
		}
		
		// copy the two  reference atoms to the result 
		// (omit the first atom, which is the center atom!)
		result.first = (atom_list.size() == 3);
		current = atom_list.begin();
		current++;
		if (current != atom_list.end())
		{
			result.second = *current;
			current++;
		}
		if (current != atom_list.end())
		{
			result.third  = *current;
		}

		return result;
	}
	
	// start function of ReconstructFragmentProcessor
	// nothing important is done here
	bool ReconstructFragmentProcessor::start()
	{
		inserted_atoms_.clear();
		
		if (fragment_db_ == 0)
		{
			Log.error() << "ReconstructFragmentProcessor: no FragmentDB defined. "
									<< "Use setFragmentDB() to associate a fragment database." << std::endl;
			return false;
		}
		
		return true;
	}
	
	// Processor finish method
	bool ReconstructFragmentProcessor::finish()
	{
		return true;
	}

	// Processor application method
	Processor::Result ReconstructFragmentProcessor::operator () (Fragment& object)
	{
		// abort if the object is not a residue (only residues are 
		// contained in the fragment DB)																				
        if (!RTTI::isKindOf<Residue>(&object))
		{
			return Processor::CONTINUE;
		}

		// cast the object to a residue
		Residue& residue = dynamic_cast<Residue&>(object);

		// get the reference fragment from the fragment DB
		const Fragment* reference_fragment = fragment_db_->getReferenceFragment(residue);

		// complain if no reference fragment could be found
		if (reference_fragment == 0)
		{
			Log.warn() << "ReconstructFragmentProcessor: no reference fragment found for " 
							   << residue.getName() << ":" << residue.getID() << std::endl;
			return Processor::CONTINUE;
		}

		// Reconstruct the atoms and count the number of new atoms.
		// number_of_inserted_atoms_ += reconstructFragment(residue, *reference_fragment);
		list<Atom*> inserted_atoms;
		list<Atom*>::iterator it;

		inserted_atoms = reconstructFragment(residue, *reference_fragment);

		for (it = inserted_atoms.begin(); it != inserted_atoms.end(); ++it)
		{
			inserted_atoms_.push_back(*it);
		}

		// Next residue.
		return Processor::CONTINUE;
	}

	ReconstructFragmentProcessor::ReconstructFragmentProcessor(const FragmentDB& db)
		:	fragment_db_(&db),
			inserted_atoms_()
	{
	}
	
	// copy constructor	
	ReconstructFragmentProcessor::ReconstructFragmentProcessor(const ReconstructFragmentProcessor& rfp)
		:	UnaryProcessor<Fragment>(rfp),
			fragment_db_(rfp.fragment_db_),
			inserted_atoms_(rfp.inserted_atoms_)
	{
	}
	
	// default constructor	
	ReconstructFragmentProcessor::ReconstructFragmentProcessor()
		:	fragment_db_(0),
			inserted_atoms_()
	{
	}
	
	// destructor	
	ReconstructFragmentProcessor::~ReconstructFragmentProcessor()
	{
		fragment_db_ = 0;
	}

	list<Atom*>& ReconstructFragmentProcessor::getInsertedAtoms()
	{
		return inserted_atoms_;
	}

	// return the number of inserted atoms
	Size ReconstructFragmentProcessor::getNumberOfInsertedAtoms() const
	{
		return inserted_atoms_.size();
	}
 
	list<Atom*> ReconstructFragmentProcessor::reconstructFragment
		(Fragment& fragment, const Fragment& tplate)
	{
		// We count the number of atoms created.
		Size number_of_inserted_atoms = 0;
		list<Atom*> inserted_atoms;
		
		// Get a copy of the atom names occurring in the current reference fragment....
		StringHashMap<Atom*> name_to_atom;
		AtomIterator it = fragment.beginAtom();
		for (; +it; ++it)
		{
			name_to_atom.insert(pair<String, Atom*>(it->getName(), const_cast<Atom*>(&*it)));
			DEBUG("inserting " << it->getName() << " = " << (void*)(&*it) << " into name_to_atom map. ")
		}
		
		// ...and remove the names of existing atoms.
		// At the same time, we construct a hash map relating the atom names
		// of the residues to the corresponding atom pointers.
		HashMap<const Atom*, Atom*> tpl_to_res;
		HashSet<const Atom*> transformed;
		AtomConstIterator cit = tplate.beginAtom();
		for (; +cit; ++cit)
		{	
			if (name_to_atom.has(cit->getName()))
			{
				// remember that the coordinates of this one are correct
				Atom* res_atom = name_to_atom[cit->getName()];
				transformed.insert(&*cit);
				tpl_to_res.insert(std::pair<const Atom*, Atom*>(&*cit, res_atom));
				DEBUG("inserting " << (void*)&*cit << " = " << (void*)(res_atom) << " into tpl_to_res map. ")
			}
			else
			{
				// We create a copy of the existing atom and insert it into
				// the residue. Coordinates are bogus, but we'll correct that
				// later on.
				Atom* new_atom = reinterpret_cast<Atom*>(cit->create(false));
				fragment.insert(*new_atom);
				tpl_to_res.insert(std::pair<const Atom*, Atom*>(&*cit, new_atom));
				// update the atom count
				number_of_inserted_atoms++;
				inserted_atoms.push_back(new_atom);
				DEBUG("creating atom " << (void*)(new_atom) << " for tpl atom " << cit->getName() << " (" << (void*)&*cit << ")")
			}
		}

		// We've now made sure that all atoms of the tplate exist in the 
    // reconstructed residue as well (careful, not the other way round!)
		// we can now start to adjust the atom coordinates.

		// If no atoms were in common, there's not much we can do...
		// Trivial solution: no atoms are actually matched to each 
		// other, so we just leave the coordinates the way they
		// are (copy of the tpl coordinates) and return.
		if (!transformed.isEmpty())
		{
			// Otherwise, we start adjusting coordinates
			// We use a hash set for BFS
			HashSet<const Atom*> visited;
			list<const Atom*> stack;
			stack.push_back(*transformed.begin());
			while (!stack.empty())
			{
				const Atom* current = stack.front();
				stack.erase(stack.begin());
				visited.insert(current);

				DEBUG("center is " << (void*)current << " (" << current->getFullName() << ") visited = " 
							<< (visited.has(current)) << " transformed = " << transformed.has(current) 
							<< " @ " << current->getPosition())
				DEBUG("residue atom is @ " << tpl_to_res[current]->getPosition()  << " (dist = " 
							<< tpl_to_res[current]->getPosition().getDistance(current->getPosition()) << ")")

				for (Atom::BondConstIterator bond = current->beginBond(); +bond; ++ bond)
				{
					const Atom* next = bond->getPartner(*current);
					DEBUG("examining " << (void*)next << " (" << next->getFullName() << ") visited = " 
								<< (visited.has(next)) << " transformed = " << transformed.has(next))
					if (!visited.has(next))
					{
						stack.push_back(next);
						visited.insert(next);
						if (!transformed.has(next))
						{
							DEBUG("searching reference atoms for "<< next->getFullName())
							Triple<bool, const Atom*, const Atom*> ref_atoms;
							ref_atoms = getTwoReferenceAtoms(*current, transformed);
							DEBUG("reference atoms: " << (ref_atoms.second == 0 ? String("-") : ref_atoms.second->getFullName())
										<< " / " << (ref_atoms.third == 0 ? String("-") : ref_atoms.third->getFullName()))

							Matrix4x4 T;
							if (ref_atoms.first)
							{
								// we can map all three atoms, great!
								DEBUG("mapping three atoms: " << tpl_to_res[current]->getFullName() << "/" 
											<< tpl_to_res[ref_atoms.second]->getFullName() << "/" << tpl_to_res[ref_atoms.third]->getFullName())
								DEBUG("onto:                " << current->getFullName() << "/" 
											<< ref_atoms.second->getFullName() << "/" << ref_atoms.third->getFullName())
								DEBUG("from: " << tpl_to_res[current]->getPosition() << "/" 
											<< tpl_to_res[ref_atoms.second]->getPosition() << "/" << tpl_to_res[ref_atoms.third]->getPosition())
								DEBUG("to:   " << current->getPosition() << "/" 
											<< ref_atoms.second->getPosition() << "/" << ref_atoms.third->getPosition())
								T = StructureMapper::matchPoints
											(current->getPosition(), ref_atoms.second->getPosition(), ref_atoms.third->getPosition(),
											 tpl_to_res[current]->getPosition(), 
											 tpl_to_res[ref_atoms.second]->getPosition(), 
											 tpl_to_res[ref_atoms.third]->getPosition());
								DEBUG("found two reference atoms, mapped with T =\n" << T)
							}
							else 
							{
								// We could map the two center atoms only, which corresponds to 
								// a simple translation by the difference of the two atom positions.
								T.setIdentity();
								T.setTranslation(tpl_to_res[current]->getPosition() - current->getPosition());
								DEBUG("translating by " << T)
							}

							// Transform the coordinates of the atom we're interest in
							tpl_to_res[next]->setPosition(T * tpl_to_res[next]->getPosition());

							// Remember that we already took care of that guy.
							transformed.insert(next);
							DEBUG(next->getFullName() << " is transformed: " << tpl_to_res[next]->getPosition() << "/" << next->getPosition())
							DEBUG("distance = " << tpl_to_res[next]->getPosition().getDistance(next->getPosition()))
						}
					}
				}
			}
		}
		
		// Return the number of atoms created.
		// return number_of_inserted_atoms;
		return inserted_atoms;
	}

} // namespace BALL