CpuObc.cpp 20 KB
Newer Older
1

2
/* Portions copyright (c) 2006-2009 Stanford University and Simbios.
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 * Contributors: Pande Group
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject
 * to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <string.h>
26
#include <stdlib.h>
27
#include <sstream>
Mark Friedrichs's avatar
Mark Friedrichs committed
28
29
#include <cmath>
#include <cstdio>
30
31

#include "../SimTKUtilities/SimTKOpenMMCommon.h"
32
#include "../SimTKReference/ReferenceForce.h"
Mark Friedrichs's avatar
Mark Friedrichs committed
33
#include "CpuObc.h"
34

35
36
37
using namespace OpenMM;
using namespace std;

38
39
/**---------------------------------------------------------------------------------------

Mark Friedrichs's avatar
Mark Friedrichs committed
40
    CpuObc constructor
41

Mark Friedrichs's avatar
Mark Friedrichs committed
42
43
44
    obcParameters      obcParameters object
    
    --------------------------------------------------------------------------------------- */
45

Mark Friedrichs's avatar
Mark Friedrichs committed
46
47
48
CpuObc::CpuObc( ObcParameters* obcParameters ) : _obcParameters(obcParameters), _includeAceApproximation(1) {
    _obcChain.resize(_obcParameters->getNumberOfAtoms());
}
49

Mark Friedrichs's avatar
Mark Friedrichs committed
50
/**---------------------------------------------------------------------------------------
51

Mark Friedrichs's avatar
Mark Friedrichs committed
52
    CpuObc destructor
53

Mark Friedrichs's avatar
Mark Friedrichs committed
54
    --------------------------------------------------------------------------------------- */
55

Mark Friedrichs's avatar
Mark Friedrichs committed
56
CpuObc::~CpuObc( ){
57
58
59
60
}

/**---------------------------------------------------------------------------------------

Mark Friedrichs's avatar
Mark Friedrichs committed
61
    Get ObcParameters reference
62

Mark Friedrichs's avatar
Mark Friedrichs committed
63
    @return ObcParameters reference
64

Mark Friedrichs's avatar
Mark Friedrichs committed
65
    --------------------------------------------------------------------------------------- */
66

Mark Friedrichs's avatar
Mark Friedrichs committed
67
68
ObcParameters* CpuObc::getObcParameters( void ) const {
    return _obcParameters;
69
70
71
72
}

/**---------------------------------------------------------------------------------------

Mark Friedrichs's avatar
Mark Friedrichs committed
73
    Set ObcParameters reference
74

Mark Friedrichs's avatar
Mark Friedrichs committed
75
    @param ObcParameters reference
76

Mark Friedrichs's avatar
Mark Friedrichs committed
77
    --------------------------------------------------------------------------------------- */
78

Mark Friedrichs's avatar
Mark Friedrichs committed
79
80
void CpuObc::setObcParameters(  ObcParameters* obcParameters ){
    _obcParameters = obcParameters;
81
82
83
84
}

/**---------------------------------------------------------------------------------------

Mark Friedrichs's avatar
Mark Friedrichs committed
85
   Return flag signalling whether AceApproximation for nonpolar term is to be included
86

Mark Friedrichs's avatar
Mark Friedrichs committed
87
   @return flag
88
89
90

   --------------------------------------------------------------------------------------- */

Mark Friedrichs's avatar
Mark Friedrichs committed
91
92
int CpuObc::includeAceApproximation( void ) const {
    return _includeAceApproximation;
93
94
95
96
}

/**---------------------------------------------------------------------------------------

Mark Friedrichs's avatar
Mark Friedrichs committed
97
   Set flag indicating whether AceApproximation is to be included
98

Mark Friedrichs's avatar
Mark Friedrichs committed
99
   @param includeAceApproximation new includeAceApproximation value
100
101
102

   --------------------------------------------------------------------------------------- */

Mark Friedrichs's avatar
Mark Friedrichs committed
103
104
void CpuObc::setIncludeAceApproximation( int includeAceApproximation ){
    _includeAceApproximation = includeAceApproximation;
105
106
107
108
}

/**---------------------------------------------------------------------------------------

Mark Friedrichs's avatar
Mark Friedrichs committed
109
    Return OBC chain derivative: size = _obcParameters->getNumberOfAtoms()
110

Mark Friedrichs's avatar
Mark Friedrichs committed
111
    @return array
112

Mark Friedrichs's avatar
Mark Friedrichs committed
113
    --------------------------------------------------------------------------------------- */
114

115
vector<RealOpenMM>& CpuObc::getObcChain( void ){
Mark Friedrichs's avatar
Mark Friedrichs committed
116
    return _obcChain;
117
118
119
120
}

/**---------------------------------------------------------------------------------------

Mark Friedrichs's avatar
Mark Friedrichs committed
121
    Get Born radii based on papers:
122

Mark Friedrichs's avatar
Mark Friedrichs committed
123
124
       J. Phys. Chem. 1996 100, 19824-19839 (HCT paper)
       Proteins: Structure, Function, and Bioinformatcis 55:383-394 (2004) (OBC paper)
125

Mark Friedrichs's avatar
Mark Friedrichs committed
126
127
    @param atomCoordinates     atomic coordinates
    @param bornRadii           output array of Born radii
128

Mark Friedrichs's avatar
Mark Friedrichs committed
129
    --------------------------------------------------------------------------------------- */
130

Mark Friedrichs's avatar
Mark Friedrichs committed
131
void CpuObc::computeBornRadii( const vector<RealVec>& atomCoordinates, vector<RealOpenMM>& bornRadii ){
132

Mark Friedrichs's avatar
Mark Friedrichs committed
133
    // ---------------------------------------------------------------------------------------
134

Mark Friedrichs's avatar
Mark Friedrichs committed
135
136
137
138
139
140
    static const RealOpenMM zero    = static_cast<RealOpenMM>( 0.0 );
    static const RealOpenMM one     = static_cast<RealOpenMM>( 1.0 );
    static const RealOpenMM two     = static_cast<RealOpenMM>( 2.0 );
    static const RealOpenMM three   = static_cast<RealOpenMM>( 3.0 );
    static const RealOpenMM half    = static_cast<RealOpenMM>( 0.5 );
    static const RealOpenMM fourth  = static_cast<RealOpenMM>( 0.25 );
141

Mark Friedrichs's avatar
Mark Friedrichs committed
142
    // ---------------------------------------------------------------------------------------
143

Mark Friedrichs's avatar
Mark Friedrichs committed
144
    ObcParameters* obcParameters                = getObcParameters();
145

Mark Friedrichs's avatar
Mark Friedrichs committed
146
147
148
149
    int numberOfAtoms                           = obcParameters->getNumberOfAtoms();
    const RealOpenMMVector& atomicRadii         = obcParameters->getAtomicRadii();
    const RealOpenMMVector& scaledRadiusFactor  = obcParameters->getScaledRadiusFactors();
    RealOpenMMVector& obcChain                  = getObcChain();
150

Mark Friedrichs's avatar
Mark Friedrichs committed
151
152
153
154
    RealOpenMM dielectricOffset                 = obcParameters->getDielectricOffset();
    RealOpenMM alphaObc                         = obcParameters->getAlphaObc();
    RealOpenMM betaObc                          = obcParameters->getBetaObc();
    RealOpenMM gammaObc                         = obcParameters->getGammaObc();
155

Mark Friedrichs's avatar
Mark Friedrichs committed
156
    // ---------------------------------------------------------------------------------------
157

Mark Friedrichs's avatar
Mark Friedrichs committed
158
    // calculate Born radii
159

Mark Friedrichs's avatar
Mark Friedrichs committed
160
161
162
163
    for( int atomI = 0; atomI < numberOfAtoms; atomI++ ){
      
       RealOpenMM radiusI         = atomicRadii[atomI];
       RealOpenMM offsetRadiusI   = radiusI - dielectricOffset;
164

Mark Friedrichs's avatar
Mark Friedrichs committed
165
166
       RealOpenMM radiusIInverse  = one/offsetRadiusI;
       RealOpenMM sum             = zero;
167

Mark Friedrichs's avatar
Mark Friedrichs committed
168
       // HCT code
169

Mark Friedrichs's avatar
Mark Friedrichs committed
170
       for( int atomJ = 0; atomJ < numberOfAtoms; atomJ++ ){
171

Mark Friedrichs's avatar
Mark Friedrichs committed
172
          if( atomJ != atomI ){
173

Mark Friedrichs's avatar
Mark Friedrichs committed
174
175
176
177
178
179
180
181
             RealOpenMM deltaR[ReferenceForce::LastDeltaRIndex];
             if (_obcParameters->getPeriodic())
                 ReferenceForce::getDeltaRPeriodic( atomCoordinates[atomI], atomCoordinates[atomJ], _obcParameters->getPeriodicBox(), deltaR );
             else
                 ReferenceForce::getDeltaR( atomCoordinates[atomI], atomCoordinates[atomJ], deltaR );
             RealOpenMM r               = deltaR[ReferenceForce::RIndex];
             if (_obcParameters->getUseCutoff() && r > _obcParameters->getCutoffDistance())
                 continue;
182

Mark Friedrichs's avatar
Mark Friedrichs committed
183
184
185
             RealOpenMM offsetRadiusJ   = atomicRadii[atomJ] - dielectricOffset; 
             RealOpenMM scaledRadiusJ   = offsetRadiusJ*scaledRadiusFactor[atomJ];
             RealOpenMM rScaledRadiusJ  = r + scaledRadiusJ;
186

Mark Friedrichs's avatar
Mark Friedrichs committed
187
188
189
190
             if( offsetRadiusI < rScaledRadiusJ ){
                RealOpenMM rInverse = one/r;
                RealOpenMM l_ij     = offsetRadiusI > FABS( r - scaledRadiusJ ) ? offsetRadiusI : FABS( r - scaledRadiusJ );
                           l_ij     = one/l_ij;
191

Mark Friedrichs's avatar
Mark Friedrichs committed
192
                RealOpenMM u_ij     = one/rScaledRadiusJ;
193

Mark Friedrichs's avatar
Mark Friedrichs committed
194
195
196
197
198
                RealOpenMM l_ij2    = l_ij*l_ij;
                RealOpenMM u_ij2    = u_ij*u_ij;
 
                RealOpenMM ratio    = LN( (u_ij/l_ij) );
                RealOpenMM term     = l_ij - u_ij + fourth*r*(u_ij2 - l_ij2)  + ( half*rInverse*ratio) + (fourth*scaledRadiusJ*scaledRadiusJ*rInverse)*(l_ij2 - u_ij2);
199

Mark Friedrichs's avatar
Mark Friedrichs committed
200
201
202
                // this case (atom i completely inside atom j) is not considered in the original paper
                // Jay Ponder and the authors of Tinker recognized this and
                // worked out the details
203

Mark Friedrichs's avatar
Mark Friedrichs committed
204
205
206
207
                if( offsetRadiusI < (scaledRadiusJ - r) ){
                   term += two*( radiusIInverse - l_ij);
                }
                sum += term;
208

Mark Friedrichs's avatar
Mark Friedrichs committed
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
             }
          }
       }
 
       // OBC-specific code (Eqs. 6-8 in paper)

       sum                  *= half*offsetRadiusI;
       RealOpenMM sum2       = sum*sum;
       RealOpenMM sum3       = sum*sum2;
       RealOpenMM tanhSum    = TANH( alphaObc*sum - betaObc*sum2 + gammaObc*sum3 );
       
       bornRadii[atomI]      = one/( one/offsetRadiusI - tanhSum/radiusI ); 
 
       obcChain[atomI]       = offsetRadiusI*( alphaObc - two*betaObc*sum + three*gammaObc*sum2 );
       obcChain[atomI]       = (one - tanhSum*tanhSum)*obcChain[atomI]/radiusI;
224

Mark Friedrichs's avatar
Mark Friedrichs committed
225
226
    }
}
227

Mark Friedrichs's avatar
Mark Friedrichs committed
228
/**---------------------------------------------------------------------------------------
229

Mark Friedrichs's avatar
Mark Friedrichs committed
230
    Get nonpolar solvation force constribution via ACE approximation
231

Mark Friedrichs's avatar
Mark Friedrichs committed
232
233
234
235
    @param obcParameters parameters
    @param bornRadii                 Born radii
    @param energy                    energy (output): value is incremented from input value 
    @param forces                    forces: values are incremented from input values
236

Mark Friedrichs's avatar
Mark Friedrichs committed
237
    --------------------------------------------------------------------------------------- */
238

Mark Friedrichs's avatar
Mark Friedrichs committed
239
240
241
242
void CpuObc::computeAceNonPolarForce( const ObcParameters* obcParameters,
                                      const RealOpenMMVector& bornRadii, 
                                      RealOpenMM* energy,
                                      RealOpenMMVector& forces ) const {
243

Mark Friedrichs's avatar
Mark Friedrichs committed
244
    // ---------------------------------------------------------------------------------------
245

Mark Friedrichs's avatar
Mark Friedrichs committed
246
247
248
    static const RealOpenMM zero     = static_cast<RealOpenMM>( 0.0 );
    static const RealOpenMM minusSix = -6.0;
    static const RealOpenMM six      = static_cast<RealOpenMM>( 6.0 );
249

Mark Friedrichs's avatar
Mark Friedrichs committed
250
    // ---------------------------------------------------------------------------------------
251

Mark Friedrichs's avatar
Mark Friedrichs committed
252
    // compute the nonpolar solvation via ACE approximation
253

Mark Friedrichs's avatar
Mark Friedrichs committed
254
255
    const RealOpenMM probeRadius          = obcParameters->getProbeRadius();
    const RealOpenMM surfaceAreaFactor    = obcParameters->getPi4Asolv();
256

Mark Friedrichs's avatar
Mark Friedrichs committed
257
258
    const RealOpenMMVector& atomicRadii   = obcParameters->getAtomicRadii();
    int numberOfAtoms                     = obcParameters->getNumberOfAtoms();
259

Mark Friedrichs's avatar
Mark Friedrichs committed
260
    // the original ACE equation is based on Eq.2 of
261

Mark Friedrichs's avatar
Mark Friedrichs committed
262
263
264
265
    // M. Schaefer, C. Bartels and M. Karplus, "Solution Conformations
    // and Thermodynamics of Structured Peptides: Molecular Dynamics
    // Simulation with an Implicit Solvation Model", J. Mol. Biol.,
    // 284, 835-848 (1998)  (ACE Method)
266

Mark Friedrichs's avatar
Mark Friedrichs committed
267
268
    // The original equation includes the factor (atomicRadii[atomI]/bornRadii[atomI]) to the first power,
    // whereas here the ratio is raised to the sixth power: (atomicRadii[atomI]/bornRadii[atomI])**6
269

Mark Friedrichs's avatar
Mark Friedrichs committed
270
271
272
    // This modification was made by Jay Ponder who observed it gave better correlations w/
    // observed values. He did not think it was important enough to write up, so there is
    // no paper to cite.
273

Mark Friedrichs's avatar
Mark Friedrichs committed
274
275
276
277
278
279
280
281
282
    for( int atomI = 0; atomI < numberOfAtoms; atomI++ ){
        if( bornRadii[atomI] > zero ){
            RealOpenMM r            = atomicRadii[atomI] + probeRadius;
            RealOpenMM ratio6       = POW( atomicRadii[atomI]/bornRadii[atomI], six );
            RealOpenMM saTerm       = surfaceAreaFactor*r*r*ratio6;
            *energy                += saTerm;
            forces[atomI]          += minusSix*saTerm/bornRadii[atomI]; 
        }
    }
283
284
285
286
}

/**---------------------------------------------------------------------------------------

Mark Friedrichs's avatar
Mark Friedrichs committed
287
    Get Obc Born energy and forces
288

Mark Friedrichs's avatar
Mark Friedrichs committed
289
290
291
    @param atomCoordinates     atomic coordinates
    @param partialCharges      partial charges
    @param forces              forces
292

Mark Friedrichs's avatar
Mark Friedrichs committed
293
    The array bornRadii is also updated and the obcEnergy
294

Mark Friedrichs's avatar
Mark Friedrichs committed
295
    --------------------------------------------------------------------------------------- */
296

Mark Friedrichs's avatar
Mark Friedrichs committed
297
298
RealOpenMM CpuObc::computeBornEnergyForces( const vector<RealVec>& atomCoordinates,
                                            const RealOpenMMVector& partialCharges, vector<RealVec>& inputForces ){
299

Mark Friedrichs's avatar
Mark Friedrichs committed
300
    // ---------------------------------------------------------------------------------------
301

Mark Friedrichs's avatar
Mark Friedrichs committed
302
303
304
305
306
307
308
309
    static const RealOpenMM zero    = static_cast<RealOpenMM>( 0.0 );
    static const RealOpenMM one     = static_cast<RealOpenMM>( 1.0 );
    static const RealOpenMM two     = static_cast<RealOpenMM>( 2.0 );
    static const RealOpenMM three   = static_cast<RealOpenMM>( 3.0 );
    static const RealOpenMM four    = static_cast<RealOpenMM>( 4.0 );
    static const RealOpenMM half    = static_cast<RealOpenMM>( 0.5 );
    static const RealOpenMM fourth  = static_cast<RealOpenMM>( 0.25 );
    static const RealOpenMM eighth  = static_cast<RealOpenMM>( 0.125 );
310

Mark Friedrichs's avatar
Mark Friedrichs committed
311
    // ---------------------------------------------------------------------------------------
312

Mark Friedrichs's avatar
Mark Friedrichs committed
313
314
    const ObcParameters* obcParameters = getObcParameters();
    const int numberOfAtoms            = obcParameters->getNumberOfAtoms();
315

Mark Friedrichs's avatar
Mark Friedrichs committed
316
    // ---------------------------------------------------------------------------------------
317

Mark Friedrichs's avatar
Mark Friedrichs committed
318
    // constants
319

Mark Friedrichs's avatar
Mark Friedrichs committed
320
321
322
323
324
325
    RealOpenMM preFactor;
    if( obcParameters->getSoluteDielectric() != zero && obcParameters->getSolventDielectric() != zero ){
        preFactor = two*obcParameters->getElectricConstant()*( (one/obcParameters->getSoluteDielectric()) - (one/obcParameters->getSolventDielectric()) );
    } else {
        preFactor = zero;
    }   
326

Mark Friedrichs's avatar
Mark Friedrichs committed
327
    const RealOpenMM dielectricOffset    = obcParameters->getDielectricOffset();
328

Mark Friedrichs's avatar
Mark Friedrichs committed
329
    // ---------------------------------------------------------------------------------------
330

Mark Friedrichs's avatar
Mark Friedrichs committed
331
    // compute Born radii
332

Mark Friedrichs's avatar
Mark Friedrichs committed
333
334
    RealOpenMMVector bornRadii( numberOfAtoms );
    computeBornRadii( atomCoordinates, bornRadii );
335

Mark Friedrichs's avatar
Mark Friedrichs committed
336
    // set energy/forces to zero
337

Mark Friedrichs's avatar
Mark Friedrichs committed
338
339
    RealOpenMM obcEnergy                 = zero;
    RealOpenMMVector bornForces( numberOfAtoms, 0.0 );
340

Mark Friedrichs's avatar
Mark Friedrichs committed
341
    // ---------------------------------------------------------------------------------------
342

Mark Friedrichs's avatar
Mark Friedrichs committed
343
344
345
346
347
    // compute the nonpolar solvation via ACE approximation
     
    if( includeAceApproximation() ){
       computeAceNonPolarForce( obcParameters, bornRadii, &obcEnergy, bornForces );
    }
348
 
Mark Friedrichs's avatar
Mark Friedrichs committed
349
    // ---------------------------------------------------------------------------------------
350

Mark Friedrichs's avatar
Mark Friedrichs committed
351
    // first main loop
352

Mark Friedrichs's avatar
Mark Friedrichs committed
353
    for( int atomI = 0; atomI < numberOfAtoms; atomI++ ){
354
 
Mark Friedrichs's avatar
Mark Friedrichs committed
355
356
       RealOpenMM partialChargeI = preFactor*partialCharges[atomI];
       for( int atomJ = atomI; atomJ < numberOfAtoms; atomJ++ ){
357

Mark Friedrichs's avatar
Mark Friedrichs committed
358
359
360
361
362
363
364
          RealOpenMM deltaR[ReferenceForce::LastDeltaRIndex];
          if (_obcParameters->getPeriodic())
              ReferenceForce::getDeltaRPeriodic( atomCoordinates[atomI], atomCoordinates[atomJ], _obcParameters->getPeriodicBox(), deltaR );
          else
              ReferenceForce::getDeltaR( atomCoordinates[atomI], atomCoordinates[atomJ], deltaR );
          if (_obcParameters->getUseCutoff() && deltaR[ReferenceForce::RIndex] > _obcParameters->getCutoffDistance())
              continue;
365

Mark Friedrichs's avatar
Mark Friedrichs committed
366
367
368
369
          RealOpenMM r2                 = deltaR[ReferenceForce::R2Index];
          RealOpenMM deltaX             = deltaR[ReferenceForce::XIndex];
          RealOpenMM deltaY             = deltaR[ReferenceForce::YIndex];
          RealOpenMM deltaZ             = deltaR[ReferenceForce::ZIndex];
370

Mark Friedrichs's avatar
Mark Friedrichs committed
371
372
          RealOpenMM alpha2_ij          = bornRadii[atomI]*bornRadii[atomJ];
          RealOpenMM D_ij               = r2/(four*alpha2_ij);
373

Mark Friedrichs's avatar
Mark Friedrichs committed
374
375
376
377
378
379
          RealOpenMM expTerm            = EXP( -D_ij );
          RealOpenMM denominator2       = r2 + alpha2_ij*expTerm; 
          RealOpenMM denominator        = SQRT( denominator2 ); 
          
          RealOpenMM Gpol               = (partialChargeI*partialCharges[atomJ])/denominator; 
          RealOpenMM dGpol_dr           = -Gpol*( one - fourth*expTerm )/denominator2;  
380

Mark Friedrichs's avatar
Mark Friedrichs committed
381
          RealOpenMM dGpol_dalpha2_ij   = -half*Gpol*expTerm*( one + D_ij )/denominator2;
382

Mark Friedrichs's avatar
Mark Friedrichs committed
383
          if( atomI != atomJ ){
384

Mark Friedrichs's avatar
Mark Friedrichs committed
385
              bornForces[atomJ]        += dGpol_dalpha2_ij*bornRadii[atomI];
386

Mark Friedrichs's avatar
Mark Friedrichs committed
387
388
389
              deltaX                   *= dGpol_dr;
              deltaY                   *= dGpol_dr;
              deltaZ                   *= dGpol_dr;
390

Mark Friedrichs's avatar
Mark Friedrichs committed
391
392
393
              inputForces[atomI][0]    += deltaX;
              inputForces[atomI][1]    += deltaY;
              inputForces[atomI][2]    += deltaZ;
394

Mark Friedrichs's avatar
Mark Friedrichs committed
395
396
397
              inputForces[atomJ][0]    -= deltaX;
              inputForces[atomJ][1]    -= deltaY;
              inputForces[atomJ][2]    -= deltaZ;
398

Mark Friedrichs's avatar
Mark Friedrichs committed
399
400
401
          } else {
             Gpol *= half;
          }
402

Mark Friedrichs's avatar
Mark Friedrichs committed
403
404
          obcEnergy         += Gpol;
          bornForces[atomI] += dGpol_dalpha2_ij*bornRadii[atomJ];
405

Mark Friedrichs's avatar
Mark Friedrichs committed
406
407
       }
    }
408

Mark Friedrichs's avatar
Mark Friedrichs committed
409
    // ---------------------------------------------------------------------------------------
410

Mark Friedrichs's avatar
Mark Friedrichs committed
411
    // second main loop
412

Mark Friedrichs's avatar
Mark Friedrichs committed
413
414
    const RealOpenMMVector& obcChain            = getObcChain();
    const RealOpenMMVector& atomicRadii         = obcParameters->getAtomicRadii();
415

Mark Friedrichs's avatar
Mark Friedrichs committed
416
417
418
419
    const RealOpenMM alphaObc                   = obcParameters->getAlphaObc();
    const RealOpenMM betaObc                    = obcParameters->getBetaObc();
    const RealOpenMM gammaObc                   = obcParameters->getGammaObc();
    const RealOpenMMVector& scaledRadiusFactor  = obcParameters->getScaledRadiusFactors();
420
421
422

    // compute factor that depends only on the outer loop index

Mark Friedrichs's avatar
Mark Friedrichs committed
423
424
425
    for( int atomI = 0; atomI < numberOfAtoms; atomI++ ){
       bornForces[atomI] *= bornRadii[atomI]*bornRadii[atomI]*obcChain[atomI];      
    }
426

Mark Friedrichs's avatar
Mark Friedrichs committed
427
    for( int atomI = 0; atomI < numberOfAtoms; atomI++ ){
428
 
Mark Friedrichs's avatar
Mark Friedrichs committed
429
       // radius w/ dielectric offset applied
430

Mark Friedrichs's avatar
Mark Friedrichs committed
431
432
       RealOpenMM radiusI        = atomicRadii[atomI];
       RealOpenMM offsetRadiusI  = radiusI - dielectricOffset;
433

Mark Friedrichs's avatar
Mark Friedrichs committed
434
       for( int atomJ = 0; atomJ < numberOfAtoms; atomJ++ ){
435

Mark Friedrichs's avatar
Mark Friedrichs committed
436
          if( atomJ != atomI ){
437

Mark Friedrichs's avatar
Mark Friedrichs committed
438
439
440
441
442
443
444
445
446
447
448
449
             RealOpenMM deltaR[ReferenceForce::LastDeltaRIndex];
             if (_obcParameters->getPeriodic())
                ReferenceForce::getDeltaRPeriodic( atomCoordinates[atomI], atomCoordinates[atomJ], _obcParameters->getPeriodicBox(), deltaR );
             else 
                ReferenceForce::getDeltaR( atomCoordinates[atomI], atomCoordinates[atomJ], deltaR );
             if (_obcParameters->getUseCutoff() && deltaR[ReferenceForce::RIndex] > _obcParameters->getCutoffDistance())
                    continue;
    
             RealOpenMM deltaX             = deltaR[ReferenceForce::XIndex];
             RealOpenMM deltaY             = deltaR[ReferenceForce::YIndex];
             RealOpenMM deltaZ             = deltaR[ReferenceForce::ZIndex];
             RealOpenMM r                  = deltaR[ReferenceForce::RIndex];
450
 
Mark Friedrichs's avatar
Mark Friedrichs committed
451
             // radius w/ dielectric offset applied
452

Mark Friedrichs's avatar
Mark Friedrichs committed
453
             RealOpenMM offsetRadiusJ      = atomicRadii[atomJ] - dielectricOffset;
454

Mark Friedrichs's avatar
Mark Friedrichs committed
455
456
457
             RealOpenMM scaledRadiusJ      = offsetRadiusJ*scaledRadiusFactor[atomJ];
             RealOpenMM scaledRadiusJ2     = scaledRadiusJ*scaledRadiusJ;
             RealOpenMM rScaledRadiusJ     = r + scaledRadiusJ;
458

Mark Friedrichs's avatar
Mark Friedrichs committed
459
460
             // dL/dr & dU/dr are zero (this can be shown analytically)
             // removed from calculation
461

Mark Friedrichs's avatar
Mark Friedrichs committed
462
             if( offsetRadiusI < rScaledRadiusJ ){
463

Mark Friedrichs's avatar
Mark Friedrichs committed
464
465
                RealOpenMM l_ij          = offsetRadiusI > FABS( r - scaledRadiusJ ) ? offsetRadiusI : FABS( r - scaledRadiusJ );
                     l_ij                = one/l_ij;
466

Mark Friedrichs's avatar
Mark Friedrichs committed
467
                RealOpenMM u_ij          = one/rScaledRadiusJ;
468

Mark Friedrichs's avatar
Mark Friedrichs committed
469
                RealOpenMM l_ij2         = l_ij*l_ij;
470

Mark Friedrichs's avatar
Mark Friedrichs committed
471
                RealOpenMM u_ij2         = u_ij*u_ij;
472
 
Mark Friedrichs's avatar
Mark Friedrichs committed
473
474
                RealOpenMM rInverse      = one/r;
                RealOpenMM r2Inverse     = rInverse*rInverse;
475

Mark Friedrichs's avatar
Mark Friedrichs committed
476
                RealOpenMM t3            = eighth*(one + scaledRadiusJ2*r2Inverse)*(l_ij2 - u_ij2) + fourth*LN( u_ij/l_ij )*r2Inverse;
477

Mark Friedrichs's avatar
Mark Friedrichs committed
478
                RealOpenMM de            = bornForces[atomI]*t3*rInverse;
479

Mark Friedrichs's avatar
Mark Friedrichs committed
480
481
482
483
484
485
486
                deltaX                  *= de;
                deltaY                  *= de;
                deltaZ                  *= de;
    
                inputForces[atomI][0]   -= deltaX;
                inputForces[atomI][1]   -= deltaY;
                inputForces[atomI][2]   -= deltaZ;
487
  
Mark Friedrichs's avatar
Mark Friedrichs committed
488
489
490
                inputForces[atomJ][0]   += deltaX;
                inputForces[atomJ][1]   += deltaY;
                inputForces[atomJ][2]   += deltaZ;
491
 
Mark Friedrichs's avatar
Mark Friedrichs committed
492
493
494
             }
          }
       }
495

Mark Friedrichs's avatar
Mark Friedrichs committed
496
    }
497

Mark Friedrichs's avatar
Mark Friedrichs committed
498
    return obcEnergy;
499
}