File: EFShiftProcessor.C

package info (click to toggle)
ball 1.5.0%2Bgit20180813.37fc53c-6
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 239,888 kB
  • sloc: cpp: 326,149; ansic: 4,208; python: 2,303; yacc: 1,778; lex: 1,099; xml: 958; sh: 322; makefile: 95
file content (482 lines) | stat: -rw-r--r-- 16,321 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
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
// -*- Mode: C++; tab-width: 2; -*-
// vi: set ts=2:
//

#include<BALL/NMR/EFShiftProcessor.h>
#include <BALL/KERNEL/bond.h>
#include <BALL/FORMAT/parameterSection.h>

#include <limits>

using namespace std;

namespace BALL
{

	const char* EFShiftProcessor::PROPERTY__EF_SHIFT = "ElectricFieldShift";

	EFShiftProcessor::EFShiftProcessor()
		:ShiftModule()
	{
	}

	EFShiftProcessor::EFShiftProcessor(const EFShiftProcessor& processor)
		: ShiftModule(processor),
			bond_list_(processor.bond_list_),
			effector_list_(processor.effector_list_),
			first_atom_expressions_(processor.first_atom_expressions_),
			second_atom_expressions_(processor.second_atom_expressions_),
			epsilon1_(processor.epsilon1_),
			epsilon2_(processor.epsilon2_),
			charge_map_(processor.charge_map_),
			exclude_residue_field_(processor.exclude_residue_field_),
			exclude_adjacent_residue_field_(processor.exclude_adjacent_residue_field_),
			carbonyl_influences_amide_field_(processor.carbonyl_influences_amide_field_),
			exclude_solvent_field_(processor.exclude_solvent_field_),
			cut_off2_(processor.cut_off2_),
			charge_factor_(processor.charge_factor_)
	{
	}

	EFShiftProcessor::~EFShiftProcessor()
	{
	}

	void EFShiftProcessor::init()
	{
		// By default, we assume the worst...
		valid_ = false;

		// If no parameters are assigned, abort immediately.
		if (parameters_ == 0)
		{
			return;
		}

		// Check that the parameter file contains the correct section...
		ParameterSection parameter_section;
		parameter_section.extractSection(*parameters_, "ElectricFieldEffect");

		// ...and that this section contains the correct column names.
		if ( !parameter_section.hasVariable("first_atom") || !parameter_section.hasVariable("second_atom")
				|| !parameter_section.hasVariable("epsilon1") || !parameter_section.hasVariable("epsilon2"))
		{
			return;
		}

		// Check for the option "exclude_residue_field".
		exclude_residue_field_ = false;
		if (parameter_section.options.has("exclude_residue_field"))
		{
			exclude_residue_field_ = parameter_section.options.getBool("exclude_residue_field");
		}

		// Check for the option "exclude_adjacent_field".
		exclude_adjacent_residue_field_ = false;
		if (parameter_section.options.has("exclude_adjacent_residue_field"))
		{
			exclude_adjacent_residue_field_ = parameter_section.options.getBool("exclude_adjacent_residue_field");
		}

		// Check for the option "carbonyl_influences_amide_field".
		carbonyl_influences_amide_field_ = false;
		if (parameter_section.options.has("carbonyl_influences_amide_field"))
		{
			carbonyl_influences_amide_field_ = parameter_section.options.getBool("carbonyl_influences_amide_field");
		}

		// Check for the option "exclude_solvent_field".
		exclude_solvent_field_ = false;
		if (parameter_section.options.has("exclude_solvent_field"))
		{
			exclude_solvent_field_ = parameter_section.options.getBool("exclude_solvent_field");
		}

		// Clear the arrays containing the expressions, the parameters, and the charge map.
		first_atom_expressions_.clear();
		second_atom_expressions_.clear();
		epsilon1_.clear();
		epsilon2_.clear();
		charge_map_.clear();

		// Extract the atom expressions and the corresponding polarizabilities.
		Position first_atom_column = parameter_section.getColumnIndex("first_atom");
		Position second_atom_column = parameter_section.getColumnIndex("second_atom");
		Position epsilon1_column = parameter_section.getColumnIndex("epsilon1");
		Position epsilon2_column = parameter_section.getColumnIndex("epsilon2");

		for (Position counter = 0; counter < parameter_section.getNumberOfKeys(); counter++)
		{
			first_atom_expressions_.push_back(Expression(parameter_section.getValue(counter, first_atom_column)));
			second_atom_expressions_.push_back(Expression(parameter_section.getValue(counter, second_atom_column)));
			epsilon1_.push_back(parameter_section.getValue(counter, epsilon1_column).toFloat());
			epsilon2_.push_back(parameter_section.getValue(counter, epsilon2_column).toFloat());
		}

		// Extract the charge assignment map.
		bool result = parameter_section.extractSection(*parameters_, "Charges");

		// Check for the cut off.
		cut_off2_ = std::numeric_limits<float>::max();
		if (parameter_section.options.has("cut_off"))
		{
			// Store the squared value of the cut off in the member cut_off2_.
			cut_off2_ = parameter_section.options.getReal("cut_off");
			cut_off2_ *= cut_off2_;
		}

		// For numeric aspects, here, the esu unit is divided by 
		// the charge_factor_, such that the molecules charges (which are given 
		// by PDB.org in elementary units) can easily be multiplied with. 
		// When computing the shift in the finish method, the charge_factor 
		// is again multiplied with.  
		// Default factor is 1.0 - default unit are elementary charges (e0)
		charge_factor_ = 1.0;
		if (parameter_section.options.has("unit"))
		{
			String unit = parameter_section.options["unit"];
			if (unit == "e0")
			{
				charge_factor_ = 1.0;
			}
			else if (unit == "ESU")
			{
				charge_factor_ = 1.0 / 4.8;
			}
			else
			{
				Log.warn() << "EFShiftProcessor::init: unknown unit for charges in file "
									 << parameters_->getFilename() << ", section [Charges]: "
									 << unit << " - using default unit elemtary charges (e0)." << endl;
			}
		}

		// Built the charge hash map.
		if (result && parameter_section.hasVariable("charge"))
		{
			Position charge_column = parameter_section.getColumnIndex("charge");
			for (Position i = 0; i < parameter_section.getNumberOfKeys(); i++)
			{
				charge_map_[parameter_section.getKey(i)] = charge_factor_ * parameter_section.getValue(i, charge_column).toFloat();
			}
		}

		//printParameters_();

		// Mark the module as initialized.
		valid_ = true;
	}

	bool EFShiftProcessor::start()
	{
		// If the module is invalid, abort.
		if (!isValid())
		{
			return false;
		}

		// Clear the target bond and the effector list.
		bond_list_.clear();
		effector_list_.clear();

		return true;
	}

	bool EFShiftProcessor::finish()
	{
		// If the module is in an invalid state, abort.
		if (!isValid())
		{
			return false;
		}

		// If there were no effectors or no target bonds, return immediately.
		if (bond_list_.empty() || effector_list_.empty())
		{
			return true;
		}

		// If the solvent atoms should not act as sources. 
		if (exclude_solvent_field_)
		{
			// We build a new effector list.
			list<Atom*> tmp_effector_list;

			list<Atom*>::const_iterator effector_it = effector_list_.begin();
			for (; effector_it != effector_list_.end(); ++effector_it)
			{
				if ((*effector_it)->getResidue() && (*effector_it)->getResidue()->getName() != "HOH")
				{
					tmp_effector_list.push_back(*effector_it);
				}
			}
			// Replace the effector list.
			effector_list_ = tmp_effector_list;
		}

		// Iterate over all target bonds.
		std::vector<std::pair<Atom*, Atom*> >::iterator bond_it = bond_list_.begin();
		Index current_bond = 0;
		for (; bond_it != bond_list_.end(); ++bond_it)
		{
			Atom* first_atom  = bond_it->first;
			Atom* second_atom = bond_it->second;

			// Given a target bond -- 
			// calculate the electric field and the induced secondary shift.

			Vector3 first_atom_pos  = first_atom->getPosition();
			Vector3 second_atom_pos = second_atom->getPosition();
			Vector3 bond_vector(first_atom_pos - second_atom_pos);

			// The electric field. 
			Vector3 E(0.0);

			bool same_residue;
			bool adjacent_residues;

			// Test all effectors.
			list<Atom*>::const_iterator effector_it = effector_list_.begin();
			for (; effector_it != effector_list_.end(); ++effector_it)
			{
				// Exclude this effector--target combination from consideration if
				// effector is a cabonyl oxygen (O) and the target is an amid hydrogen (HN)
				// and carbonyl_influences_amide_field is set (read from options in init()).
				if (   !carbonyl_influences_amide_field_ && ((*effector_it)->getName() == "O")
						&& ( (first_atom->getName() == "H") || second_atom->getName() == "H" )
					 )
				{
					continue;
				}

				//  Exclude effectors from adjacent residue (fragment) if 
				//  exclude_adjacent_residue_field is set (read from options in init()). 
				//  and 
				// 	Exclude effectors from the same residue (fragment) if 
				//  exclude_residue_field is set (read from options in init()).

				//  First test whether we have atoms from same residue. 
				same_residue = ((*effector_it)->getFragment() == first_atom->getFragment());

				//  Then test whether we have atoms in adjacent residues.
				adjacent_residues = false;
				adjacent_residues = (   (*effector_it)->getFragment()->isNextSiblingOf(*(first_atom->getFragment()))
															||(*effector_it)->getFragment()->isPreviousSiblingOf(*(first_atom->getFragment()))
															||(abs((*effector_it)->getResidue()->getID().toInt() - first_atom->getResidue()->getID().toInt()) <= 1));
				// Exclude effectors if flags are set and exclude criterion holds. 
				if (   (!exclude_residue_field_           ||  !same_residue)
						&& (!exclude_adjacent_residue_field_  ||  !adjacent_residues) )
				{
					Vector3 distance(first_atom_pos - (*effector_it)->getPosition());
					float square_distance  = distance.getSquareLength();
					if (square_distance   <= cut_off2_)
					{
						// Translate the charge to ESU (from elementary charges) if neccessary.
						// NOTE: charge_factor_ is designed for switching between ESU and elementary units.
						//       For numerical aspects in the init() function the esu unit
						//       was divided by the charge factor, such that the molecules charges
						//       (given by PDB.org in elementary units) could easily be multiplied
						//       with. Here, we multiply again with the charge_factor_.
						float charge = (*effector_it)->getCharge() * 1./charge_factor_;

						// Add to the current contribution to the field.
						E += distance * charge / (square_distance * distance.getLength());
					}
				}
			}

			// Calculate the field component E_z along the bond axis.
			float Ez = (bond_vector * E) / bond_vector.getLength();

			// Calculate the secondary shift induced by this field.
			float delta_EF =    epsilon1_[expression_number_[current_bond]] * Ez
												+ epsilon2_[expression_number_[current_bond]] * E.getSquareLength();

			// Store the shift in the corresponding properties.
			float shift = first_atom->getProperty(ShiftModule::PROPERTY__SHIFT).getFloat();
			shift += delta_EF;
			first_atom->setProperty(ShiftModule::PROPERTY__SHIFT, shift);

			first_atom->setProperty(PROPERTY__EF_SHIFT, delta_EF);
			current_bond++;
		}

		// We have to do some ShiftX-y postprocessing: 
		// add for all CA-atoms 0.2 times the EF-shift-value of HA-atoms.
		postprocessing_();
		return true;
	}

	Processor::Result EFShiftProcessor::operator () (Composite& object)
	{
		// Here, we collect all target bonds and
		// all charged atoms (as effectors of the electric field).
        if (RTTI::isKindOf<Atom>(&object))
		{
			Atom* atom_ptr = RTTI::castTo<Atom>(object);

			// Assign the charge (if it is defined for this atom).
			String full_name = atom_ptr->getFullName();
			full_name.substitute(":", " ");
			atom_ptr->setCharge(0.0);

			if (charge_map_.has(full_name))
			{
				atom_ptr->setCharge(charge_map_[full_name]);
			}
			else
			{
				// Try wildcard match for the residue name.
				full_name = "* " + atom_ptr->getName();
				if (charge_map_.has(full_name))
				{
					atom_ptr->setCharge(charge_map_[full_name]);
				}
			}

			// Store all charged atoms in the effector list.
			if (atom_ptr->getCharge() != 0.0)
			{
				effector_list_.push_back(atom_ptr);
			}

			Atom::BondIterator bond_it = atom_ptr->beginBond();
			for (; +bond_it; ++bond_it)
			{
				Atom* first_atom  = 0;
				Atom* second_atom = 0;

				bool match_found  = false;
				Index j = -1;

				// Iterate over all target bond expressions and 
				// try to match them with the bond's atoms.
				for (Position i = 0; i < first_atom_expressions_.size(); ++i)
				{
					// First, try to match first/first and second/second.
					if (  (first_atom_expressions_[i](*(bond_it->getFirstAtom())))
							&&(second_atom_expressions_[i](*(bond_it->getSecondAtom())))
						 )
					{
						// Remember the atoms and the bond type (for the parameters).
						first_atom  = const_cast<Atom*>(bond_it->getFirstAtom());
						second_atom = const_cast<Atom*>(bond_it->getSecondAtom());
						match_found = true;
						j = i;
						break;
					}
					// Otherwise: try first/second and second/first.
					else if (first_atom_expressions_[i](*(bond_it->getSecondAtom()))
							&& second_atom_expressions_[i](*(bond_it->getFirstAtom())))
					{
						// Remember the atoms and the bond type (for the parameters).
						first_atom  = const_cast<Atom*>(bond_it->getSecondAtom());
						second_atom = const_cast<Atom*>(bond_it->getFirstAtom());
						match_found = true;
						j = i;
						break;
					}
				}

				if (match_found)
				{
					// Only include each bond once!
					if (find(bond_list_.begin(), bond_list_.end(), pair<Atom*, Atom*>(first_atom, second_atom))==bond_list_.end())
					{
						bond_list_.push_back(std::pair<Atom*, Atom*>(first_atom, second_atom));
						expression_number_.push_back(j);
					}
				}
			}
		}

		return Processor::CONTINUE;
	}



	void EFShiftProcessor::printTargets_()
	{
		Log.info() << "********* \n EF: list of target bonds" << std::endl;
		std::vector<std::pair<Atom*, Atom*> >::iterator tbond_it = bond_list_.begin();
		for (; tbond_it != bond_list_.end(); ++tbond_it)
		{
			Log.info() << tbond_it->first->getFullName() << "  " << tbond_it->second->getFullName() << std::endl; 
		}
		Log.info() << "------------------------------\n" << std::endl;
	}

	void EFShiftProcessor::printEffectors_()
	{
		Log.info() << "********* \n EF: list of effectors" << std::endl;
		list<Atom*>::const_iterator effector_it = effector_list_.begin();
		for (; effector_it != effector_list_.end(); ++effector_it)
		{
			Log.info() << (*effector_it)->getFullName() <<"  " << (*effector_it)->getName()  << "  "  << std::endl; 
		}
		Log.info() << "------------------------------\n" << std::endl;
	}

	void	EFShiftProcessor::printParameters_()
	{
		Log.info() << "********* \n EF: list of parameters" << std::endl;
		Log.info() << "exclude_residue_field  " <<  exclude_residue_field_ << std::endl;
		Log.info() << "exclude_adjacent_residue_field  "	<< exclude_adjacent_residue_field_ << std::endl;
		Log.info() << "carbonyl_influences_amide_field  "	<< carbonyl_influences_amide_field_ << std::endl;
		Log.info() << "exclude_solvent_field  " << exclude_solvent_field_ << std::endl;
	  Log.info() << "cut_off" << cut_off2_ << std::endl;
		Log.info() << "unit" << (charge_factor_ > 0.9 ? "e0" :"ESU")<< std::endl;
		Log.info() << "------------------------------\n" << std::endl;
	}


	void  EFShiftProcessor::postprocessing_()
	{
		System* system = NULL;

		// Try to get the system.
		std::vector<std::pair<Atom*, Atom*> >::iterator tbond_it = bond_list_.begin();
		for (; tbond_it != bond_list_.end(); ++tbond_it)
		{
            if  (RTTI::isKindOf<System>(&tbond_it->first->getRoot()))
			{
				system = dynamic_cast<System*>(&(tbond_it->first->getRoot()));
				break;
			}
		}

		if (system)
		{
			// Add for all CA-atoms 0.2 times the EF-shift-values of the bound HA-atom.
			for (BALL::ResidueIterator r_it = system->beginResidue(); r_it != system->endResidue(); ++r_it)
			{
				Atom* CA = 0;
				Atom* HA = 0;

				for (BALL::AtomIterator at_it = r_it->beginAtom(); +at_it; ++at_it)
				{
					if (at_it->getName() == "CA")
						CA = &(*at_it);
					if (at_it->getName() == "HA")
						HA = &(*at_it);
				}

				if (CA && HA)
				{
					float total = CA->getProperty(ShiftModule::PROPERTY__SHIFT).getFloat();
					float ca_shift = CA->getProperty(BALL::EFShiftProcessor::PROPERTY__EF_SHIFT).getFloat();
					float ha_shift = HA->getProperty(BALL::EFShiftProcessor::PROPERTY__EF_SHIFT).getFloat();

					CA->setProperty(BALL::EFShiftProcessor::PROPERTY__EF_SHIFT, ca_shift + 0.2*ha_shift);
					CA->setProperty(ShiftModule::PROPERTY__SHIFT, total+ 0.2*ha_shift );
				}
			}
		}
		else
		{
			Log.error() << "Error in EFShiftProcessor: no system found for postprocessing. ("
			            << __FILE__ << " " << __LINE__ << ")" <<  std::endl;
		}
	}

} // namespace BALL