Skip to content

Commit a1ea57f

Browse files
Merge pull request #1412 from Iximiel/feature/atomicDistributionfcc
Adding the FCC and BCC atomic distributions to the collection
2 parents 6b17716 + e286743 commit a1ea57f

2 files changed

Lines changed: 224 additions & 10 deletions

File tree

src/tools/AtomDistribution.cpp

Lines changed: 187 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,16 @@ std::unique_ptr<AtomDistribution> AtomDistribution::getAtomDistribution(std::str
3737
distribution = std::make_unique<twoGlobs>();
3838
} else if (atomicDistr == "sc") {
3939
distribution = std::make_unique<tiledSimpleCubic>();
40+
} else if (atomicDistr == "ibcc") {
41+
distribution = std::make_unique<inscribedBodyCenteredCubic>();
42+
} else if (atomicDistr == "bcc") {
43+
distribution = std::make_unique<tiledBodyCenteredCubic>();
44+
} else if (atomicDistr == "ifcc") {
45+
distribution = std::make_unique<inscribedFaceCenteredCubic>();
46+
} else if (atomicDistr == "fcc") {
47+
distribution = std::make_unique<tiledFaceCenteredCubic>();
4048
} else {
41-
plumed_error() << R"(The atomic distribution can be only "line", "cube", "sphere", "globs" and "sc", the input was ")"
49+
plumed_error() << R"(The atomic distribution can be only "line", "cube", "sphere", "globs", "sc", "ibcc", "bcc", "ifcc",and "fcc", the input was ")"
4250
<< atomicDistr <<'"';
4351
}
4452
return distribution;
@@ -188,6 +196,19 @@ void uniformCube::frame(View<Vector> posToUpdate,
188196

189197
}
190198

199+
/// A simple correction to getting (in ceil mode) the correct nearest perfect cube
200+
unsigned ceiledPerfectCube(const unsigned N) {
201+
// In some case a perfect cube is not well represented by double
202+
// and will get uncorrectly ceiled up (for example 24389 will result in a 30 and not in 29)
203+
204+
// This assumes that N!=0
205+
const size_t x = std::ceil(std::cbrt(static_cast<double>(N)))-1;
206+
if ( x*x*x >= N) {
207+
return x;
208+
}
209+
return x+1;
210+
}
211+
191212
void tiledSimpleCubic::frame(View<Vector> posToUpdate,
192213
View<double,9> box,
193214
unsigned /*step*/,
@@ -196,15 +217,7 @@ void tiledSimpleCubic::frame(View<Vector> posToUpdate,
196217
//I do not think that write a spacefilling curve, like Hilbert, Peano or Morton
197218
//could be a good idea, in this case
198219

199-
// the lambda is here because in some case a perfect cube is not well represented
200-
// by double and will get uncorrectly ceiled up (for example 24389 will result in a 30 and not in 29)
201-
const unsigned rmax = [&] {
202-
const size_t x = std::ceil(std::cbrt(static_cast<double>(posToUpdate.size())))-1;
203-
if ( x*x*x >= posToUpdate.size()) {
204-
return x;
205-
}
206-
return x+1;
207-
}();
220+
const unsigned rmax =ceiledPerfectCube(posToUpdate.size());
208221

209222
auto s=posToUpdate.begin();
210223
auto e=posToUpdate.end();
@@ -230,6 +243,170 @@ void tiledSimpleCubic::frame(View<Vector> posToUpdate,
230243

231244
}
232245

246+
void inscribedFaceCenteredCubic::frame(View<Vector> posToUpdate,
247+
View<double,9> box,
248+
unsigned /*step*/,
249+
Random& rng) {
250+
//Here we are exploiting a litte trick:
251+
// if you remove the even or odd atoms from a simple cubic built with our algorithm,
252+
//you get an fcc boxed in a cube
253+
const unsigned rmax = [&] {
254+
auto x = ceiledPerfectCube(2*posToUpdate.size());
255+
//rmax needs to be even for this trick to work
256+
if ( x%2==0) {
257+
return x;
258+
} else {
259+
return x+1;
260+
}
261+
262+
}();
263+
264+
auto s=posToUpdate.begin();
265+
auto e=posToUpdate.end();
266+
//I am using the iterators:this is slightly faster,
267+
// enough to overcome the cost of the vtable that I added
268+
for (unsigned k=0; k<rmax&&s!=e; ++k) {
269+
for (unsigned j=0; j<rmax&&s!=e; ++j) {
270+
//we choose to show only the atoms with even index: (i+j+k)%2
271+
//Like this we skip steps and lots of divisions
272+
for (unsigned i=(j+k)%2; i<rmax&&s!=e; i+=2) {
273+
*s = 0.5*Vector (i,j,k);
274+
++s;
275+
}
276+
}
277+
}
278+
box[0]=0.5*rmax;
279+
box[1]=0.0;
280+
box[2]=0.0;
281+
box[3]=0.0;
282+
box[4]=0.5*rmax;
283+
box[5]=0.0;
284+
box[6]=0.0;
285+
box[7]=0.0;
286+
box[8]=0.5*rmax;
287+
288+
}
289+
290+
void tiledFaceCenteredCubic::frame(View<Vector> posToUpdate,
291+
View<double,9> box,
292+
unsigned /*step*/,
293+
Random& rng) {
294+
295+
const unsigned rmax =ceiledPerfectCube(posToUpdate.size());
296+
297+
auto s=posToUpdate.begin();
298+
auto e=posToUpdate.end();
299+
#define X PLMD::Versors::xp<double>
300+
#define Y PLMD::Versors::yp<double>
301+
#define Z PLMD::Versors::zp<double>
302+
const auto a=sqrt(2)*0.5*(X+Y);
303+
const auto b=sqrt(2)*0.5*(X+Z);
304+
const auto c=sqrt(2)*0.5*(Y+Z);
305+
#undef X
306+
#undef Y
307+
#undef Z
308+
//I am using the iterators:this is slightly faster,
309+
// enough to overcome the cost of the vtable that I added
310+
for (unsigned k=0; k<rmax&&s!=e; ++k) {
311+
for (unsigned j=0; j<rmax&&s!=e; ++j) {
312+
for (unsigned i=0; i<rmax&&s!=e; ++i) {
313+
*s = i*a
314+
+j*b
315+
+k*c;
316+
++s;
317+
}
318+
}
319+
}
320+
321+
box.subview<0,3>() = a*rmax;
322+
box.subview<3,3>() = b*rmax;
323+
box.subview<6,3>() = c*rmax;
324+
325+
}
326+
327+
void tiledBodyCenteredCubic::frame(View<Vector> posToUpdate,
328+
View<double,9> box,
329+
unsigned /*step*/,
330+
Random& rng) {
331+
//For the base functionality of this see the comment in the tiledSimpleCubic
332+
333+
const unsigned rmax =ceiledPerfectCube(posToUpdate.size());
334+
335+
auto s=posToUpdate.begin();
336+
auto e=posToUpdate.end();
337+
#define X PLMD::Versors::xp<double>
338+
#define Y PLMD::Versors::yp<double>
339+
#define Z PLMD::Versors::zp<double>
340+
const auto a=0.5*(-X+Y+Z);
341+
const auto b=0.5*( X-Y+Z);
342+
const auto c=0.5*( X+Y-Z);
343+
#undef X
344+
#undef Y
345+
#undef Z
346+
//I am using the iterators:this is slightly faster,
347+
// enough to overcome the cost of the vtable that I added
348+
for (unsigned k=0; k<rmax&&s!=e; ++k) {
349+
for (unsigned j=0; j<rmax&&s!=e; ++j) {
350+
for (unsigned i=0; i<rmax&&s!=e; ++i) {
351+
*s = i*a
352+
+j*b
353+
+k*c;
354+
++s;
355+
}
356+
}
357+
}
358+
359+
box.subview<0,3>() = a*rmax;
360+
box.subview<3,3>() = b*rmax;
361+
box.subview<6,3>() = c*rmax;
362+
363+
}
364+
365+
void inscribedBodyCenteredCubic::frame(View<Vector> posToUpdate,
366+
View<double,9> box,
367+
unsigned /*step*/,
368+
Random& rng) {
369+
//Here we are exploiting a litte trick:
370+
// if you remove the even or odd atoms from a simple cubic built with our algorithm,
371+
//you get an bcc boxed in a cube
372+
const unsigned rmax = [&] {
373+
//we take one atoms every 4
374+
auto x = ceiledPerfectCube(4*posToUpdate.size());
375+
//rmax needs to be even for this trick to work
376+
if ( x%2==0) {
377+
return x;
378+
} else {
379+
return x+1;
380+
}
381+
382+
}();
383+
384+
auto s=posToUpdate.begin();
385+
auto e=posToUpdate.end();
386+
//I am using the iterators:this is slightly faster,
387+
// enough to overcome the cost of the vtable that I added
388+
for (unsigned k=0; k<rmax&&s!=e; ++k) {
389+
//we alternate on the z axis the choice between atoms with both i and j even or odd
390+
//The +=2 skips an extra check in the inner body
391+
for (unsigned j=k%2; j<rmax&&s!=e; j+=2) {
392+
for (unsigned i=k%2; i<rmax&&s!=e; i+=2) {
393+
*s = 0.5*Vector (i,j,k);
394+
++s;
395+
}
396+
}
397+
}
398+
box[0]=0.5*rmax;
399+
box[1]=0.0;
400+
box[2]=0.0;
401+
box[3]=0.0;
402+
box[4]=0.5*rmax;
403+
box[5]=0.0;
404+
box[6]=0.0;
405+
box[7]=0.0;
406+
box[8]=0.5*rmax;
407+
408+
}
409+
233410
void fileTraj::rewind() {
234411
auto errormessage=parser.rewind();
235412
if (errormessage) {

src/tools/AtomDistribution.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,43 @@ struct tiledSimpleCubic:public AtomDistribution {
9999
Random& rng) override;
100100
};
101101

102+
/// This FCC is contained in a orthogonal cubic box
103+
///
104+
/// Fun facts: full cubes at 4, 32, 108, 256, 500 atoms and so on.
105+
///
106+
/// At certain sizes above the ones above (36, 504), you get
107+
/// good (100) slabs quite separated on th z axis
108+
struct inscribedFaceCenteredCubic:public AtomDistribution {
109+
void frame(View<Vector> posToUpdate,
110+
View<double,9> box,
111+
unsigned /*step*/,
112+
Random& rng) override;
113+
};
114+
115+
/// This FCC is contained in a "standard" FCC box
116+
struct tiledFaceCenteredCubic:public AtomDistribution {
117+
void frame(View<Vector> posToUpdate,
118+
View<double,9> box,
119+
unsigned /*step*/,
120+
Random& rng) override;
121+
};
122+
123+
/// This BCC is contained in a orthogonal cubic box
124+
struct inscribedBodyCenteredCubic:public AtomDistribution {
125+
void frame(View<Vector> posToUpdate,
126+
View<double,9> box,
127+
unsigned /*step*/,
128+
Random& rng) override;
129+
};
130+
131+
/// This BCC is contained in a "standard" FCC box
132+
struct tiledBodyCenteredCubic:public AtomDistribution {
133+
void frame(View<Vector> posToUpdate,
134+
View<double,9> box,
135+
unsigned /*step*/,
136+
Random& rng) override;
137+
};
138+
102139
/// atomic distribution from a trajectory file
103140
class fileTraj:public AtomDistribution {
104141
TrajectoryParser parser;

0 commit comments

Comments
 (0)