1111Action_MinImage::Action_MinImage () :
1212 dist_(0 ),
1313 useMass_(true ),
14- calcUsingMask_(false )
14+ calcUsingMask_(false ),
15+ toFace_(false ) // HIDDEN, TODO make not hidden after testing
1516{}
1617
1718void Action_MinImage::Help () const {
@@ -25,28 +26,43 @@ Action::RetType Action_MinImage::Init(ArgList& actionArgs, ActionInit& init, int
2526 // Get Keywords
2627 useMass_ = !(actionArgs.hasKey (" geom" ));
2728 calcUsingMask_ = actionArgs.hasKey (" maskcenter" );
29+ toFace_ = actionArgs.hasKey (" toface" );
30+ if (calcUsingMask_ && toFace_) {
31+ mprinterr (" Error: Specify either 'maskcenter' or 'toface', not both.\n " );
32+ return Action::ERR;
33+ }
2834 DataFile* outfile = init.DFL ().AddDataFile ( actionArgs.GetStringKey (" out" ), actionArgs );
2935 // Get Masks
3036 std::string mask1 = actionArgs.GetMaskNext ();
31- std::string mask2 = actionArgs.GetMaskNext ();
32- if (mask1.empty () || mask2.empty ()) {
33- mprinterr (" Error: Requires 2 masks\n " );
34- return Action::ERR;
35- }
3637 if (Mask1_.SetMaskString (mask1)) return Action::ERR;
37- if (Mask2_.SetMaskString (mask2)) return Action::ERR;
38+ std::string mask2;
39+ if (!toFace_) {
40+ mask2 = actionArgs.GetMaskNext ();
41+ if (mask1.empty () || mask2.empty ()) {
42+ mprinterr (" Error: Requires 2 masks\n " );
43+ return Action::ERR;
44+ }
45+ if (Mask2_.SetMaskString (mask2)) return Action::ERR;
46+ }
3847
3948 // Dataset to store distances
4049 MetaData md (actionArgs.GetStringNext ());
4150 md.SetScalarMode ( MetaData::M_DISTANCE );
4251 dist_ = init.DSL ().AddSet (DataSet::DOUBLE, md, " MID" );
4352 if (dist_==0 ) return Action::ERR;
53+ // Update metadata in case a default name was generated
54+ md.SetName ( dist_->Meta ().Name () );
4455 if (outfile != 0 ) outfile->AddDataSet ( dist_ );
45- if (!calcUsingMask_) {
56+ if (toFace_ || !calcUsingMask_) {
4657 md.SetAspect (" A1" );
4758 atom1_ = init.DSL ().AddSet (DataSet::INTEGER, md);
48- md.SetAspect (" A2" );
49- atom2_ = init.DSL ().AddSet (DataSet::INTEGER, md);
59+ if (toFace_) {
60+ md.SetAspect (" FACE" );
61+ atom2_ = init.DSL ().AddSet (DataSet::STRING, md);
62+ } else {
63+ md.SetAspect (" A2" );
64+ atom2_ = init.DSL ().AddSet (DataSet::INTEGER, md);
65+ }
5066 if (atom1_ == 0 || atom2_ == 0 ) return Action::ERR;
5167 if (outfile != 0 ) {
5268 outfile->AddDataSet ( atom1_ );
@@ -66,7 +82,9 @@ Action::RetType Action_MinImage::Init(ArgList& actionArgs, ActionInit& init, int
6682 minAtom2_.resize ( numthreads );
6783
6884 mprintf (" MINIMAGE: Looking for closest approach of" );
69- if (calcUsingMask_) {
85+ if (toFace_) {
86+ mprintf (" atoms in mask '%s' to unit cell faces.\n " , Mask1_.MaskString ());
87+ } else if (calcUsingMask_) {
7088 mprintf (" center of mask %s\n\t to images of center of mask %s\n " ,
7189 Mask1_.MaskString (), Mask2_.MaskString ());
7290 if (useMass_)
@@ -92,12 +110,21 @@ Action::RetType Action_MinImage::Setup(ActionSetup& setup) {
92110 return Action::SKIP;
93111 }
94112 if (setup.Top ().SetupIntegerMask ( Mask1_ )) return Action::ERR;
95- if (setup.Top ().SetupIntegerMask ( Mask2_ )) return Action::ERR;
96- mprintf (" \t %s (%i atoms) to %s (%i atoms)\n " ,Mask1_.MaskString (), Mask1_.Nselected (),
97- Mask2_.MaskString (),Mask2_.Nselected ());
98- if (Mask1_.None () || Mask2_.None ()) {
99- mprintf (" Warning: One or both masks have no atoms.\n " );
100- return Action::SKIP;
113+ if (!toFace_) {
114+ if (setup.Top ().SetupIntegerMask ( Mask2_ )) return Action::ERR;
115+ mprintf (" \t %s (%i atoms) to %s (%i atoms)\n " ,
116+ Mask1_.MaskString (), Mask1_.Nselected (),
117+ Mask2_.MaskString (), Mask2_.Nselected ());
118+ if (Mask1_.None () || Mask2_.None ()) {
119+ mprintf (" Warning: One or both masks have no atoms.\n " );
120+ return Action::SKIP;
121+ }
122+ } else {
123+ mprintf (" \t %s (%i atoms)\n " , Mask1_.MaskString (), Mask1_.Nselected ());
124+ if (Mask1_.None ()) {
125+ mprintf (" Warning: Mask has no atoms.\n " );
126+ return Action::SKIP;
127+ }
101128 }
102129
103130 return Action::OK;
@@ -116,6 +143,75 @@ static void WriteMatrix(Matrix_3x3 const& ucell, PDBfile& pdbout, const char* na
116143}
117144*/
118145
146+ /* * Distance of each atom in mask to each unit cell face. */
147+ double Action_MinImage::minDistToCellFace (AtomMask const & maskIn, Frame const & frameIn, int frameNum)
148+ {
149+ Box const & boxIn = frameIn.BoxCrd ();
150+ // Store face coords in cartesian space
151+ static const char * faceStr[] = { " +X" , " -X" , " +Y" , " -Y" , " +Z" , " -Z" };
152+ Vec3 Faces[6 ];
153+ Faces[0 ] = boxIn.UnitCell ().TransposeMult ( Vec3 (1.0 , 0.5 , 0.5 ) ); // +X
154+ Faces[1 ] = boxIn.UnitCell ().TransposeMult ( Vec3 (0.0 , 0.5 , 0.5 ) ); // -X
155+ Faces[2 ] = boxIn.UnitCell ().TransposeMult ( Vec3 (0.5 , 1.0 , 0.5 ) ); // +Y
156+ Faces[3 ] = boxIn.UnitCell ().TransposeMult ( Vec3 (0.5 , 0.0 , 0.5 ) ); // -Y
157+ Faces[4 ] = boxIn.UnitCell ().TransposeMult ( Vec3 (0.5 , 0.5 , 1.0 ) ); // +Z
158+ Faces[5 ] = boxIn.UnitCell ().TransposeMult ( Vec3 (0.5 , 0.5 , 0.0 ) ); // -Z
159+
160+ minDist_.assign (minDist_.size (), DBL_MAX);
161+ int mythread = 0 ;
162+ for (AtomMask::const_iterator at = maskIn.begin (); at != maskIn.end (); ++at)
163+ {
164+ // To fractional space
165+ Vec3 frac = boxIn.FracCell () * Vec3 ( frameIn.XYZ (*at) );
166+ // Bring back into main unit cell
167+ frac[0 ] = frac[0 ] - floor (frac[0 ]);
168+ frac[1 ] = frac[1 ] - floor (frac[1 ]);
169+ frac[2 ] = frac[2 ] - floor (frac[2 ]);
170+ if (frac[0 ] < 0.0 ) frac[0 ] += 1.0 ;
171+ if (frac[1 ] < 0.0 ) frac[1 ] += 1.0 ;
172+ if (frac[2 ] < 0.0 ) frac[2 ] += 1.0 ;
173+ // Back to Cartesian
174+ Vec3 cart = boxIn.UnitCell ().TransposeMult ( frac );
175+ // Distance to each face
176+ for (unsigned int idx = 0 ; idx < 6 ; idx++) {
177+ Vec3 dxyz = Faces[idx] - cart;
178+ double dist2 = dxyz.Magnitude2 ();
179+ // mprintf("DEBUG: %3i Dist from at %i to face %s is %g face={%g %g %g} cart={%g %g %g}\n", frameNum+1, *at+1, faceStr[idx], sqrt(dist2),
180+ // Faces[idx][0], Faces[idx][1], Faces[idx][2], cart[0], cart[1], cart[2]);
181+ // minDist2 = std::min(minDist2, dist2);
182+ if (dist2 < minDist_[mythread]) {
183+ minDist_[mythread] = dist2;
184+ minAtom1_[mythread] = *at;
185+ minAtom2_[mythread] = idx;
186+ // rprintf("DEBUG: New Min Dist: Atom %i to %i (%g)\n",
187+ // Mask1_[m1]+1,Mask2_[m2]+1,sqrt(Dist2));
188+ // pdbout_.WriteHET(1, minxyz_[0], minxyz_[1], minxyz_[2]);
189+ }
190+ }
191+ }
192+ // Find lowest minDist
193+ double globalMin = minDist_[0 ];
194+ int min1 = minAtom1_[0 ];
195+ int min2 = minAtom2_[0 ];
196+ for (unsigned int n = 1 ; n != minDist_.size (); n++)
197+ if (minDist_[n] < globalMin) {
198+ globalMin = minDist_[n];
199+ min1 = minAtom1_[n];
200+ min2 = minAtom2_[n];
201+ }
202+ ++min1;
203+ // ++min2;
204+ // By convention make atom 1 the lowest number
205+ // int lowest_num = std::min(min1, min2);
206+ // int highest_num = std::max(min1, min2);
207+ // atom1_->Add(frameNum, &lowest_num);
208+ // atom2_->Add(frameNum, &highest_num);
209+ atom1_->Add (frameNum, &min1);
210+ atom2_->Add (frameNum, faceStr[min2]);
211+
212+ return globalMin;
213+ }
214+
119215double Action_MinImage::MinNonSelfDist2 (Vec3 const & a1, Vec3 const & a2, Box const & boxIn) {
120216// a1.Print("A1");
121217// a2.Print("A2");
@@ -165,7 +261,10 @@ Action::RetType Action_MinImage::DoAction(int frameNum, ActionFrame& frm) {
165261// pdbout_.OpenWrite("minimage.pdb");
166262 double Dist2;
167263
168- if (calcUsingMask_) {
264+ if (toFace_) {
265+ Dist2 = minDistToCellFace (Mask1_, frm.Frm (), frameNum);
266+ Dist2 = sqrt ( Dist2 );
267+ } else if (calcUsingMask_) {
169268 // Use center of mask1 and mask2
170269 Vec3 a1, a2;
171270 if (useMass_) {
0 commit comments