PSCF v1.3
UnitCell3.cpp
1/*
2* PSCF - Polymer Self-Consistent Field
3*
4* Copyright 2015 - 2025, The Regents of the University of Minnesota
5* Distributed under the terms of the GNU General Public License.
6*/
7
8#include "UnitCell.h"
9#include <util/math/Constants.h>
10
11namespace Pscf {
12namespace Prdc {
13
14 using namespace Util;
15
16 // Member functions of UnitCell<3>
17
18 /*
19 * Constructor.
20 */
22 : lattice_(Null)
23 {}
24
25 /*
26 * Assignment operator.
27 */
29 {
30 if (lattice_ != UnitCell<3>::Null) {
31 UTIL_CHECK(other.lattice_ == lattice_);
32 }
33 isInitialized_ = false;
34 lattice_ = other.lattice_;
35 setNParameter();
37 for (int i = 0; i < nParameter_; ++i) {
38 parameters_[i] = other.parameters_[i];
39 }
40 setLattice();
41 return *this;
42 }
43
44 /*
45 * Read the lattice system and set nParameter.
46 */
48 {
49 UTIL_CHECK(lattice_ != UnitCell<3>::Null);
50 if (lattice_ == UnitCell<3>::Cubic) {
51 nParameter_ = 1;
52 } else
53 if (lattice_ == UnitCell<3>::Tetragonal) {
54 nParameter_ = 2;
55 } else
56 if (lattice_ == UnitCell<3>::Orthorhombic) {
57 nParameter_ = 3;
58 } else
59 if (lattice_ == UnitCell<3>::Monoclinic) {
60 nParameter_ = 4;
61 } else
62 if (lattice_ == UnitCell<3>::Triclinic) {
63 nParameter_ = 6;
64 } else
65 if (lattice_ == UnitCell<3>::Rhombohedral) {
66 nParameter_ = 2;
67 } else
68 if (lattice_ == UnitCell<3>::Hexagonal) {
69 nParameter_ = 2;
70 } else {
71 UTIL_THROW("Invalid value");
72 }
73 }
74
75 /*
76 * Set Bravais and reciprocal lattice parameters, and drBasis.
77 *
78 * Function UnitCellBase::initializeToZero() must be called before
79 * setBasis() to initialize all elements of vectors and tensors to
80 * zero. Only nonzero elements need to be set here.
81 */
82 void UnitCell<3>::setBasis()
83 {
84 UTIL_CHECK(lattice_ != UnitCell<3>::Null);
85 UTIL_CHECK(nParameter_ > 0);
86
87 // Set elements for specific lattice types
88 double twoPi = 2.0*Constants::Pi;
89 int i;
90 if (lattice_ == UnitCell<3>::Cubic) {
91 UTIL_CHECK(nParameter_ == 1);
92 for (i=0; i < 3; ++i) {
93 rBasis_[i][i] = parameters_[0];
94 kBasis_[i][i] = twoPi/parameters_[0];
95 drBasis_[0](i,i) = 1.0;
96 }
97 } else
98 if (lattice_ == UnitCell<3>::Tetragonal) {
99 UTIL_CHECK(nParameter_ == 2);
100
101 rBasis_[0][0] = parameters_[0];
102 rBasis_[1][1] = parameters_[0];
103 rBasis_[2][2] = parameters_[1];
104
105 drBasis_[0](0,0) = 1.0;
106 drBasis_[0](1,1) = 1.0;
107 drBasis_[1](2,2) = 1.0;
108
109 kBasis_[0][0] = twoPi/parameters_[0];
110 kBasis_[1][1] = kBasis_[0][0];
111 kBasis_[2][2] = twoPi/parameters_[1];
112
113 }else
114 if (lattice_ == UnitCell<3>::Orthorhombic) {
115 UTIL_CHECK(nParameter_ == 3);
116 for (i=0; i < 3; ++i) {
117 rBasis_[i][i] = parameters_[i];
118 drBasis_[i](i,i) = 1.0;
119 kBasis_[i][i] = twoPi/parameters_[i];
120 }
121 } else
122 if (lattice_ == UnitCell<3>::Hexagonal) {
123 UTIL_CHECK(nParameter_ == 2);
124 double a = parameters_[0];
125 double c = parameters_[1];
126 double rt3 = sqrt(3.0);
127
128 rBasis_[0][0] = a;
129 rBasis_[1][0] = -0.5*a;
130 rBasis_[1][1] = 0.5*rt3*a;
131 rBasis_[2][2] = c;
132
133 drBasis_[0](0, 0) = 1.0;
134 drBasis_[0](1, 0) = -0.5;
135 drBasis_[0](1, 1) = 0.5*rt3;
136 drBasis_[1](2, 2) = 1.0;
137
138 kBasis_[0][0] = twoPi/a;
139 kBasis_[0][1] = twoPi/(rt3*a);
140 kBasis_[1][0] = 0.0;
141 kBasis_[1][1] = twoPi/(0.5*rt3*a);
142 kBasis_[2][2] = twoPi/(c);
143
144 } else
145 if (lattice_ == UnitCell<3>::Rhombohedral) {
146 UTIL_CHECK(nParameter_ == 2);
147
148 // Set parameters
149 double a, beta, theta;
150 a = parameters_[0]; // magnitude of Bravais basis vectors
151 beta = parameters_[1]; // angle between basis vectors
152 theta = acos( sqrt( (2.0*cos(beta) + 1.0)/3.0 ) );
153 // theta is the angle of all basis vectors from the z axis
154
155 /*
156 * Bravais lattice vectora a_i with i=0, 1, or 2 have form:
157 *
158 * a_i = a * (z * cos(theta) + u_i * sin(theta) )
159 *
160 * where z denotes a unit vector parallel to the z axis, and
161 * the u_i are three unit vectors in the x-y separated by
162 * angles of 2 pi /3 (or 120 degrees), for which u_0 is
163 * parallel to the x axis. Note that u_i . u_j = -1/2 for any
164 * i not equal to j.
165 *
166 * The angle between any two such Bravais basis vectors is easily
167 * computed from the dot product, a_i . a_j for unequal i and j.
168 * This gives:
169 *
170 * cos beta = cos^2(theta) - 0.5 sin^2 (theta)
171 * = (3cos^{2}(theta) - 1)/2
172 *
173 * The angle theta is computed above by solving this equation
174 * for theta as a function of the input parameter beta.
175 *
176 * The corresponding reciprocal lattice vectors have the form
177 *
178 * b_i = (2*pi/3*a)( z/cos(theta) + 2 * u_i / sin(theta) )
179 *
180 * It is straightforward to confirm that these reciprocal
181 * vectors satisfy the condition a_i . b_j = 2 pi delta_{ij}
182 */
183
184 // Trig function values
185 double cosT = cos(theta);
186 double sinT = sin(theta);
187 double sqrt3 = sqrt(3.0);
188 double sinPi3 = 0.5*sqrt3;
189 double cosPi3 = 0.5;
190
191 // Bravais basis vectors
192 rBasis_[0][0] = sinT * a;
193 rBasis_[0][2] = cosT * a;
194 rBasis_[1][0] = -cosPi3 * sinT * a;
195 rBasis_[1][1] = sinPi3 * sinT * a;
196 rBasis_[1][2] = cosT * a;
197 rBasis_[2][0] = -cosPi3 * sinT * a;
198 rBasis_[2][1] = -sinPi3 * sinT * a;
199 rBasis_[2][2] = cosT * a;
200
201 // Reciprocal lattice basis vectors
202 kBasis_[0][0] = 2.0 * twoPi /( 3.0 * sinT * a);
203 kBasis_[0][2] = twoPi /( 3.0 * cosT * a);
204 kBasis_[1][0] = -2.0 * cosPi3 * twoPi /( 3.0 * sinT * a);
205 kBasis_[1][1] = 2.0 * sinPi3 * twoPi /( 3.0 * sinT * a);
206 kBasis_[1][2] = twoPi /( 3.0 * cosT * a);
207 kBasis_[2][0] = -2.0 * cosPi3 * twoPi /( 3.0 * sinT * a);
208 kBasis_[2][1] = -2.0 * sinPi3 * twoPi /( 3.0 * sinT * a);
209 kBasis_[2][2] = twoPi /( 3.0 * cosT * a);
210
211 // Derivatives with respect to length a
212 drBasis_[0](0,0) = sinT;
213 drBasis_[0](0,2) = cosT;
214 drBasis_[0](1,0) = -cosPi3 * sinT;
215 drBasis_[0](1,1) = sinPi3 * sinT;
216 drBasis_[0](1,2) = cosT;
217 drBasis_[0](2,0) = -cosPi3 * sinT;
218 drBasis_[0](2,1) = -sinPi3* sinT;
219 drBasis_[0](2,2) = cosT;
220
221 // Define alpha = d(theta)/d(beta)
222 double alpha = -sin(beta)/(3.0*cosT*sinT);
223
224 // Derivatives with respect to beta
225 drBasis_[1](0,0) = cosT * alpha * a;
226 drBasis_[1](0,2) = -sinT * alpha * a;
227 drBasis_[1](1,0) = -cosPi3 * cosT * alpha * a;
228 drBasis_[1](1,1) = sinPi3 * cosT * alpha * a;
229 drBasis_[1](1,2) = -sinT * alpha * a;
230 drBasis_[1](2,0) = -cosPi3 * cosT * alpha * a;
231 drBasis_[1](2,1) = -sinPi3* cosT * alpha * a;
232 drBasis_[1](2,2) = -sinT * alpha * a;
233
234 } else
235 if (lattice_ == UnitCell<3>::Monoclinic) {
236 UTIL_CHECK(nParameter_ == 4);
237
238 /*
239 * Description: Bravais basis vectors A, B, and C
240 * A (vector 0) is along x axis
241 * B (vector 1) is along y axis
242 * C (vector 2) is in the x-z plane, an angle beta from A/x
243 * B is the unique axis (perpendicular to A and C)
244 * Angle beta is stored in radians.
245 */
246
247 // Parameters
248 double a = parameters_[0]; // length of A
249 double b = parameters_[1]; // length of B
250 double c = parameters_[2]; // length of C
251 double beta = parameters_[3]; // angle between C and A/x
252
253 double cb = cos(beta);
254 double sb = sin(beta);
255
256 // Nonzero components of basis vectors
257 // For rBasis_[i][j], i:basis vector, j:Cartesian component
258 rBasis_[0][0] = a;
259 rBasis_[1][1] = b;
260 rBasis_[2][0] = c*cb;
261 rBasis_[2][2] = c*sb;
262
263 // Nonzero derivatives of basis vectors with respect to parameters
264 // For drBasis_[k](i,j), k:parameter, i:basis vector, j:Cartesian
265 drBasis_[0](0,0) = 1.0;
266 drBasis_[1](1,1) = 1.0;
267 drBasis_[2](2,0) = cb;
268 drBasis_[2](2,2) = sb;
269 drBasis_[3](2,0) = c*sb;
270 drBasis_[3](2,2) = -c*cb;
271
272 // Reciprocal lattice vectors
273 kBasis_[0][0] = twoPi/a;
274 kBasis_[0][2] = -twoPi*cb/(a*sb);
275 kBasis_[1][1] = twoPi / b;
276 kBasis_[2][2] = twoPi/(c*sb);
277
278 } else
279 if (lattice_ == UnitCell<3>::Triclinic) {
280 UTIL_CHECK(nParameter_ == 6);
281
282 /*
283 * Description: Bravais basis vectors A, B, and C
284 * A (vector 0) is along x axis
285 * B (vector 1) is in the x-y plane, an angle gamma from x axis
286 * C (vector 2) is tilted by an angle theta from z axis
287 * phi is the angle beween c-z an x-z (or a-z) planes
288 * All three angles are stored in radians
289 */
290 double a = parameters_[0]; // length of A
291 double b = parameters_[1]; // length of B
292 double c = parameters_[2]; // length of C
293 double phi = parameters_[3]; // angle between c-z and a-z planes
294 double theta = parameters_[4]; // angle between c and z axis
295 double gamma = parameters_[5]; // angle between a and b
296
297 // sine and cosine of all angles
298 double cosPhi = cos(phi);
299 double sinPhi = sin(phi);
300 double cosTheta = cos(theta);
301 double sinTheta = sin(theta);
302 double cosGamma = cos(gamma);
303 double sinGamma = sin(gamma);
304
305 // Nonzero components of Bravais basis vectors
306 rBasis_[0][0] = a;
307 rBasis_[1][0] = b * cosGamma;
308 rBasis_[1][1] = b * sinGamma;
309 rBasis_[2][0] = c * sinTheta * cosPhi;
310 rBasis_[2][1] = c * sinTheta * sinPhi;
311 rBasis_[2][2] = c * cosTheta;
312
313 // Nonzero derivatives of basis vectors with respect to parameters
314 drBasis_[0](0,0) = 1.0;
315 drBasis_[1](1,0) = cosGamma;
316 drBasis_[1](1,1) = sinGamma;
317 drBasis_[2](2,0) = sinTheta * cosPhi;
318 drBasis_[2](2,1) = sinTheta * sinPhi;
319 drBasis_[2](2,2) = cosTheta;
320 drBasis_[3](2,0) = -c * sinTheta * sinPhi;
321 drBasis_[3](2,1) = c * sinTheta * cosPhi;
322 drBasis_[4](2,0) = c * cosTheta * cosPhi;
323 drBasis_[4](2,1) = c * cosTheta * sinPhi;
324 drBasis_[4](2,2) = -c * sinTheta;
325 drBasis_[5](1,0) = -b * sinGamma;
326 drBasis_[5](1,1) = b * cosGamma;
327
328 // Reciprocal lattice vectors
329 kBasis_[0][0] = twoPi / a;
330 kBasis_[0][1] = -twoPi * cosGamma / (a * sinGamma);
331 kBasis_[0][2] = sinTheta * (cosGamma * sinPhi - sinGamma * cosPhi);
332 kBasis_[0][2] = twoPi * kBasis_[0][2] / (a * sinGamma * cosTheta);
333
334 kBasis_[1][1] = twoPi/(b*sinGamma);
335 kBasis_[1][2] = -twoPi*sinPhi*sinTheta/(b*cosTheta*sinGamma);
336
337 kBasis_[2][2] = twoPi/(c*cosTheta);
338
339 } else {
340 UTIL_THROW("Unimplemented 3D lattice type");
341 }
342 }
343
344 /*
345 * Set lattice system of the unit cell (but not the parameters).
346 */
348 {
350 if (lattice_ != UnitCell<3>::Null) {
351 UTIL_CHECK(lattice == lattice_);
352 }
353 isInitialized_ = false;
354 lattice_ = lattice;
355 setNParameter();
356 }
357
358 /*
359 * Set state of the unit cell.
360 */
363 {
364 set(lattice);
366 for (int i = 0; i < nParameter_; ++i) {
367 parameters_[i] = parameters[i];
368 }
369 setLattice();
370 }
371
372 /*
373 * Get the length of the unit cell.
374 */
375 double UnitCell<3>::volume() const
376 {
377 double v = 0.0;
378 v += rBasis_[0][0]*rBasis_[1][1]*rBasis_[2][2];
379 v -= rBasis_[0][0]*rBasis_[1][2]*rBasis_[2][1];
380 v += rBasis_[0][1]*rBasis_[1][2]*rBasis_[2][0];
381 v -= rBasis_[0][1]*rBasis_[1][0]*rBasis_[2][2];
382 v += rBasis_[0][2]*rBasis_[1][0]*rBasis_[2][1];
383 v -= rBasis_[0][2]*rBasis_[1][1]*rBasis_[2][0];
384 return v;
385 }
386
387 // UnitCell<3>::LatticeSystem stream IO operators
388
389 /*
390 * Extract a UnitCell<3>::LatticeSystem from an istream as a string.
391 */
392 std::istream& operator >> (std::istream& in,
394 {
395
396 std::string buffer;
397 in >> buffer;
398 if (buffer == "Cubic" || buffer == "cubic") {
399 lattice = UnitCell<3>::Cubic;
400 } else
401 if (buffer == "Tetragonal" || buffer == "tetragonal") {
402 lattice = UnitCell<3>::Tetragonal;
403 } else
404 if (buffer == "Orthorhombic" || buffer == "orthorhombic") {
406 } else
407 if (buffer == "Monoclinic" || buffer == "monoclinic") {
408 lattice = UnitCell<3>::Monoclinic;
409 } else
410 if (buffer == "Triclinic" || buffer == "triclinic") {
411 lattice = UnitCell<3>::Triclinic;
412 } else
413 if (buffer == "Rhombohedral" || buffer == "rhombohedral") {
415 } else
416 if (buffer == "Hexagonal" || buffer == "hexagonal") {
417 lattice = UnitCell<3>::Hexagonal;
418 } else {
419 UTIL_THROW("Invalid UnitCell<3>::LatticeSystem value input");
420 }
421 return in;
422 }
423
424 /*
425 * Insert a UnitCell<3>::LatticeSystem to an ostream as a string.
426 */
427 std::ostream& operator << (std::ostream& out,
429 {
430 if (lattice == UnitCell<3>::Cubic) {
431 out << "cubic";
432 } else
433 if (lattice == UnitCell<3>::Tetragonal) {
434 out << "tetragonal";
435 } else
436 if (lattice == UnitCell<3>::Orthorhombic) {
437 out << "orthorhombic";
438 } else
439 if (lattice == UnitCell<3>::Monoclinic) {
440 out << "monoclinic";
441 } else
442 if (lattice == UnitCell<3>::Triclinic) {
443 out << "triclinic";
444 } else
445 if (lattice == UnitCell<3>::Rhombohedral) {
446 out << "rhombohedral";
447 } else
448 if (lattice == UnitCell<3>::Hexagonal) {
449 out << "hexagonal";
450 } else
451 if (lattice == UnitCell<3>::Null) {
452 out << "Null";
453 } else {
454 UTIL_THROW("This should never happen");
455 }
456 return out;
457 }
458
459}
460}
bool isInitialized_
Has this unit cell been fully initialized?
FArray< RealVec< D >, D > rBasis_
Array of Bravais lattice basis vectors.
FSArray< double, 6 > parameters() const
Get the parameters of this unit cell.
FArray< double, 6 > parameters_
Parameters used to describe the unit cell.
void setLattice()
Compute all protected data, given latticeSystem and parameters.
int nParameter_
Number of parameters required to specify unit cell.
void set(UnitCell< 3 >::LatticeSystem lattice)
Set the lattice system, but not unit cell parameters.
LatticeSystem lattice() const
Return lattice system enumeration value.
Definition UnitCell.h:451
Base template for UnitCell<D> classes, D=1, 2 or 3.
Definition UnitCell.h:56
static const double Pi
Trigonometric constant Pi.
Definition Constants.h:35
A fixed capacity (static) contiguous array with a variable logical size.
Definition FSArray.h:38
#define UTIL_CHECK(condition)
Assertion macro suitable for serial or parallel production code.
Definition global.h:68
#define UTIL_THROW(msg)
Macro for throwing an Exception, reporting function, file and line number.
Definition global.h:49
Periodic fields and crystallography.
Definition CField.cpp:11
PSCF package top-level namespace.
Definition param_pc.dox:1
std::istream & operator>>(std::istream &in, Pair< Data > &pair)
Input a Pair from an istream.
Definition Pair.h:44
std::ostream & operator<<(std::ostream &out, const Pair< Data > &pair)
Output a Pair to an ostream, without line breaks.
Definition Pair.h:57