22#include < algorithm> // sort
33#include " Constants.h" // PI
44#include " Action_Vector.h"
5+ #include " CharMask.h"
56#include " CpptrajStdio.h"
67#include " DistRoutines.h" // MinImagedVec, includes Matrix_3x3 for principal
78#include " DataSet_Vector.h"
@@ -18,9 +19,17 @@ Action_Vector::Action_Vector() :
1819 useMass_(true ),
1920 dipole_in_debye_(false ),
2021 CurrentParm_(0 ),
21- outfile_(0 )
22+ outfile_(0 ),
23+ cmask_(0 ),
24+ debug_(0 )
2225{}
2326
27+ // DESTRUCTOR
28+ Action_Vector::~Action_Vector () {
29+ if (vcorr_!=0 ) delete[] vcorr_;
30+ if (cmask_ != 0 ) delete cmask_;
31+ }
32+
2433// Action_Vector::Help()
2534void Action_Vector::Help () const {
2635 mprintf (" \t [<name>] <Type> [out <filename> [ptrajoutput]] [<mask1>] [<mask2>]\n "
@@ -46,23 +55,20 @@ void Action_Vector::Help() const {
4655 " If 'debye' is specified, report 'dipole' vector in Debye instead of e-*Ang.\n " );
4756}
4857
49- // DESTRUCTOR
50- Action_Vector::~Action_Vector () {
51- if (vcorr_!=0 ) delete[] vcorr_;
52- }
53-
5458const char * Action_Vector::ModeString_[] = {
5559 " NO_OP" , " Principal X" , " Principal Y" , " Principal Z" ,
5660 " Dipole" , " Box" , " Mask" ,
5761 " CorrPlane" , " Center" , " Unit cell X" , " Unit cell Y" , " Unit cell Z" ,
58- " Box Center" , " MinImage" , " Momentum" , " Velocity" , " Force"
62+ " Box Center" , " MinImage" , " Momentum" , " Velocity" , " Force" ,
63+ " Bond Dipole"
5964};
6065
6166const bool Action_Vector::NeedsOrigin_[] = {
6267 false , true , true , true ,
6368 true , false , true ,
6469 true , false , true , true , true ,
65- false , true , false , false , false
70+ false , true , false , false , false ,
71+ true
6672};
6773
6874static Action::RetType WarnDeprecated () {
@@ -82,6 +88,7 @@ static inline Action::RetType DeprecatedErr(const char* key) {
8288// Action_Vector::Init()
8389Action::RetType Action_Vector::Init (ArgList& actionArgs, ActionInit& init, int debugIn)
8490{
91+ debug_ = debugIn;
8592 DataFile* df = 0 ;
8693 std::string filename = actionArgs.GetStringKey (" out" );
8794 if (actionArgs.hasKey (" trajout" )) return DeprecatedErr ( " trajout" );
@@ -120,6 +127,8 @@ Action::RetType Action_Vector::Init(ArgList& actionArgs, ActionInit& init, int d
120127 mode_ = FORCE;
121128 else if (actionArgs.hasKey (" dipole" ))
122129 mode_ = DIPOLE;
130+ else if (actionArgs.hasKey (" bonddipole" ))
131+ mode_ = BONDDIPOLE;
123132 else if (actionArgs.hasKey (" box" ))
124133 mode_ = BOX;
125134 else if (actionArgs.hasKey (" corrplane" ))
@@ -176,6 +185,9 @@ Action::RetType Action_Vector::Init(ArgList& actionArgs, ActionInit& init, int d
176185 }
177186 if (mask2_.SetMaskString ( maskexpr )) return Action::ERR;
178187 }
188+ // Allocate Char mask for bond dipole
189+ if (mode_ == BONDDIPOLE)
190+ cmask_ = new CharMask ();
179191 // Set up vector dataset and IRED status
180192 MetaData md (actionArgs.GetStringNext (), MetaData::M_VECTOR);
181193 if (isIred) md.SetScalarType ( MetaData::IREDVEC );
@@ -215,7 +227,9 @@ Action::RetType Action_Vector::Init(ArgList& actionArgs, ActionInit& init, int d
215227 mprintf (" \n " );
216228 if (gridSet_ != 0 )
217229 mprintf (" \t Extracting box vectors from grid set '%s'\n " , gridSet_->legend ());
218- if (mode_ == DIPOLE) {
230+ if (mode_ == DIPOLE || mode_ == BONDDIPOLE) {
231+ if (mode_ == BONDDIPOLE) // TODO remove when no longer testing
232+ mprintf (" Warning: 'bonddipole' mode is still experimental. Use with caution.\n " );
219233 if (dipole_in_debye_)
220234 mprintf (" \t Dipole vector units will be Debye.\n " );
221235 else
@@ -252,6 +266,10 @@ Action::RetType Action_Vector::Setup(ActionSetup& setup) {
252266 mprinterr (" Error: First vector mask is empty.\n " );
253267 return Action::ERR;
254268 }
269+ // Set up char mask if needed
270+ if (cmask_ != 0 ) {
271+ *cmask_ = CharMask ( mask_.ConvertToCharMask (), mask_.Nselected () );
272+ }
255273 }
256274
257275 // Allocate space for CORRPLANE.
@@ -405,6 +423,187 @@ void Action_Vector::Mask(Frame const& currentFrame) {
405423 Vec_->AddVxyzo (VXYZ, CXYZ);
406424}
407425
426+ /* * Calculate dipole */
427+ bool Action_Vector::calcBondDipole (Vec3& vBond, Vec3& vDipole, Vec3& vOrigin, double q0, Vec3 const & XYZ0, Vec3 const & XYZ1)
428+ {
429+ bool hasCharge = true ;
430+ if (q0 < 0.0 ) {
431+ vBond = XYZ0 - XYZ1;
432+ vDipole = vBond * (-q0);
433+ vOrigin = XYZ1;
434+ } else if (q0 > 0.0 ) {
435+ vBond = XYZ1 - XYZ0;
436+ vDipole = vBond * q0;
437+ vOrigin = XYZ0;
438+ } else
439+ hasCharge = false ;
440+ return hasCharge;
441+ }
442+
443+ // / \return true if all atoms bonded to atomIn (ignoring ignoreIdx) have only the one bond back to atomIn
444+ static inline bool bondedAtomsHave1bond (Topology const & topIn, Atom const & atomIn, int ignoreIdx)
445+ {
446+ for (int ii=0 ; ii < atomIn.Nbonds (); ii++) {
447+ int bondedAtomIdx = atomIn.Bond (ii);
448+ if (bondedAtomIdx != ignoreIdx) {
449+ if (topIn[bondedAtomIdx].Nbonds () != 1 ) return false ;
450+ }
451+ }
452+ return true ;
453+ }
454+
455+ static inline double bondedAtomsCharge (Topology const & topIn, Atom const & atomIn, int ignoreIdx)
456+ {
457+ double sumQ = 0.0 ;
458+ for (int ii=0 ; ii < atomIn.Nbonds (); ii++) {
459+ int bondedAtomIdx = atomIn.Bond (ii);
460+ if (bondedAtomIdx != ignoreIdx) {
461+ if (topIn[bondedAtomIdx].Nbonds () == 1 )
462+ sumQ += topIn[bondedAtomIdx].Charge ();
463+ }
464+ }
465+ return sumQ;
466+ }
467+
468+ /* * Calculate net dipole from individual bond dipoles. */
469+ void Action_Vector::BondDipole_individualBonds (Frame const & currentFrame) {
470+ Vec3 VXYZ (0.0 , 0.0 , 0.0 ); // Negative, head
471+ Vec3 OXYZ (0.0 , 0.0 , 0.0 ); // Positive, tail
472+ Topology const & currentTop = *CurrentParm_;
473+
474+ unsigned int Nbonds = 0 ;
475+ for (AtomMask::const_iterator iat = mask_.begin (); iat != mask_.end (); ++iat)
476+ {
477+ Atom const & atom0 = currentTop[*iat];
478+ Vec3 XYZ0 ( currentFrame.XYZ (*iat) );
479+ double q0 = atom0.Charge ();
480+ for (Atom::bond_iterator bit = atom0.bondbegin (); bit != atom0.bondend (); ++bit)
481+ {
482+ if (*bit > *iat && cmask_->AtomInCharMask ( *bit )) {
483+ // Both atoms in the bond are selected.
484+ Atom const & atom1 = currentTop[*bit];
485+ Vec3 XYZ1 ( currentFrame.XYZ (*bit) );
486+ double q1 = atom1.Charge ();
487+ Vec3 vDipole (0.0 , 0.0 , 0.0 );
488+ Vec3 vBond (0.0 , 0.0 , 0.0 );
489+ Vec3 vOrigin (0.0 , 0.0 , 0.0 );
490+ bool hasCharge = false ;
491+ if (atom0.Element () != atom1.Element ()) {
492+ if (atom0.Element ()==Atom::HYDROGEN || atom0.Nbonds () == 1 ) {
493+ hasCharge = calcBondDipole ( vBond, vDipole, vOrigin, q0, XYZ0, XYZ1 );
494+ } else if (atom1.Element ()==Atom::HYDROGEN || atom1.Nbonds () == 1 ) {
495+ hasCharge = calcBondDipole ( vBond, vDipole, vOrigin, q1, XYZ1, XYZ0 );
496+ } else if (atom0.Nbonds () > 1 && bondedAtomsHave1bond (currentTop, atom0, *bit)) {
497+ // atom0 is bonded to a single other atom, X-atom0-atom1. Sum up X and atom0 charge
498+ double q0X = q0 + bondedAtomsCharge (currentTop, atom0, *bit);
499+ if (debug_ > 1 )
500+ mprintf (" DEBUG: Total charge on %s is %f\n " , currentTop.AtomMaskName (*iat).c_str (), q0X);
501+ hasCharge = calcBondDipole ( vBond, vDipole, vOrigin, q0X, XYZ0, XYZ1 );
502+ } else if (atom1.Nbonds () > 1 && bondedAtomsHave1bond (currentTop, atom1, *iat)) {
503+ // atom1 is bonded to a single other atom, X-atom1-atom0. Sum up X and atom1 charge
504+ double q1X = q1 + bondedAtomsCharge (currentTop, atom1, *iat);
505+ if (debug_ > 1 )
506+ mprintf (" DEBUG: Total charge on %s is %f\n " , currentTop.AtomMaskName (*bit).c_str (), q1X);
507+ hasCharge = calcBondDipole ( vBond, vDipole, vOrigin, q1X, XYZ1, XYZ0 );
508+ } else {
509+ mprintf (" Warning: Unhandled number of bonds: %s=%i, %s=%i\n " ,
510+ currentTop.AtomMaskName (*iat).c_str (), atom0.Nbonds (),
511+ currentTop.AtomMaskName (*bit).c_str (), atom1.Nbonds ());
512+ }
513+ }
514+
515+ if (hasCharge) {
516+ // Subtract half the dipole from the bond center to get the dipole origin
517+ // Vec3 vBondCtr = vBond / 2.0;
518+ // Vec3 vDipoleHalf = vDipole / 2.0;
519+ // Vec3 vOrigin = vOrigin + vBondCtr - vDipoleHalf;
520+ // DEBUG
521+ if (debug_ > 1 ) {
522+ mprintf (" DEBUG: Bond %s(%f) -- %s(%f) bxyz={%f %f %f} oxyz={%f %f %f} vxyz={%f %f %f} mag=%f\n " ,
523+ currentTop.AtomMaskName (*iat).c_str (), q0,
524+ currentTop.AtomMaskName (*bit).c_str (), q1,
525+ vBond[0 ], vBond[1 ], vBond[2 ],
526+ vOrigin[0 ], vOrigin[1 ], vOrigin[2 ],
527+ vDipole[0 ], vDipole[1 ], vDipole[2 ],
528+ sqrt (vDipole.Magnitude2 ()));
529+ }
530+ // Sum
531+ VXYZ += vDipole;
532+ OXYZ += vOrigin;
533+ Nbonds++;
534+ }
535+ }
536+ } // END loop over bonded atoms
537+ } // END loop over mask atoms
538+ if (dipole_in_debye_)
539+ VXYZ /= Constants::DEBYE_EA;
540+ OXYZ /= (double )Nbonds;
541+ Vec_->AddVxyzo ( VXYZ, OXYZ );
542+ }
543+
544+ /* * Calculate net dipole from atoms, origin from individual bond dipoles. */
545+ void Action_Vector::BondDipole_net_bondOrigin (Frame const & currentFrame) {
546+ Vec3 VXYZ (0.0 , 0.0 , 0.0 ); // Negative, head
547+ Vec3 OXYZ (0.0 , 0.0 , 0.0 ); // Positive, tail
548+ Topology const & currentTop = *CurrentParm_;
549+
550+ unsigned int Nbonds = 0 ;
551+ for (AtomMask::const_iterator iat = mask_.begin (); iat != mask_.end (); ++iat)
552+ {
553+ Atom const & atom0 = currentTop[*iat];
554+ Vec3 XYZ0 ( currentFrame.XYZ (*iat) );
555+ double q0 = atom0.Charge ();
556+ double en0 = atom0.PaulingElectroNeg ();
557+ // Note that negative charge will decrease the vector (towards the origin)
558+ // but by convention we want to point towards the negative charge, so at
559+ // the end the vector will have to be negated.
560+ VXYZ += ( XYZ0 * q0 );
561+ // Store bond origins according to electronegativity.
562+ for (Atom::bond_iterator bit = atom0.bondbegin (); bit != atom0.bondend (); ++bit)
563+ {
564+ if (*bit > *iat && cmask_->AtomInCharMask ( *bit )) {
565+ // Both atoms in the bond are selected.
566+ Atom const & atom1 = currentTop[*bit];
567+ Vec3 XYZ1 ( currentFrame.XYZ (*bit) );
568+ double en1 = atom1.PaulingElectroNeg ();
569+ Vec3 vOrigin (0.0 , 0.0 , 0.0 );
570+ bool en_is_different = true ;
571+ if (en0 > en1) {
572+ // atom0 is more negative
573+ // vBond = XYZ0 - XYZ1;
574+ vOrigin = XYZ1;
575+ } else if (en1 > en0) {
576+ // atom1 is more negative
577+ // vBond = XYZ1 - XYZ0;
578+ vOrigin = XYZ0;
579+ } else
580+ en_is_different = false ;
581+
582+ if (en_is_different) {
583+ // DEBUG
584+ if (debug_ > 1 ) {
585+ double q1 = atom1.Charge (); // DEBUG
586+ mprintf (" DEBUG: Bond %s(%f) -- %s(%f) oxyz={%f %f %f}\n " ,
587+ currentTop.AtomMaskName (*iat).c_str (), q0,
588+ currentTop.AtomMaskName (*bit).c_str (), q1,
589+ vOrigin[0 ], vOrigin[1 ], vOrigin[2 ]);
590+ }
591+ // Sum
592+ OXYZ += vOrigin;
593+ Nbonds++;
594+ }
595+ }
596+ } // END loop over bonded atoms
597+ } // END loop over mask atoms
598+ if (dipole_in_debye_)
599+ VXYZ /= Constants::DEBYE_EA;
600+ OXYZ /= (double )Nbonds;
601+ VXYZ.Neg ();
602+ // Shift it so the center of VXYZ will end up at the origin.
603+ OXYZ -= (VXYZ / 2.0 );
604+ Vec_->AddVxyzo ( VXYZ, OXYZ );
605+ }
606+
408607// Action_Vector::Dipole()
409608void Action_Vector::Dipole (Frame const & currentFrame) {
410609 Vec3 VXYZ (0.0 , 0.0 , 0.0 );
@@ -509,6 +708,8 @@ Action::RetType Action_Vector::DoAction(int frameNum, ActionFrame& frm) {
509708 case VELOCITY : Vec_->AddVxyz ( CalcCenter (frm.Frm ().vAddress (), mask_) ); break ;
510709 case FORCE : Vec_->AddVxyz ( CalcCenter (frm.Frm ().fAddress (), mask_) ); break ;
511710 case DIPOLE : Dipole (frm.Frm ()); break ;
711+ // case BONDDIPOLE : BondDipole_individualBonds(frm.Frm()); break;
712+ case BONDDIPOLE : BondDipole_net_bondOrigin (frm.Frm ()); break ;
512713 case PRINCIPAL_X :
513714 case PRINCIPAL_Y :
514715 case PRINCIPAL_Z : Principal (frm.Frm ()); break ;
0 commit comments