PSCF v1.1
Basis.tpp
1#ifndef PSCF_BASIS_TPP
2#define PSCF_BASIS_TPP
3
4/*
5* PSCF - Polymer Self-Consistent Field Theory
6*
7* Copyright 2016 - 2022, The Regents of the University of Minnesota
8* Distributed under the terms of the GNU General Public License.
9*/
10
11#include "Basis.h"
12#include "TWave.h"
13#include "groupFile.h"
14#include <pscf/crystal/UnitCell.h>
15#include <pscf/crystal/SpaceGroup.h>
16#include <pscf/crystal/shiftToMinimum.h>
17#include <pscf/mesh/Mesh.h>
18#include <pscf/mesh/MeshIterator.h>
19#include <algorithm>
20#include <vector>
21#include <set>
22#include <fstream>
23
24namespace Pscf {
25
26 /*
27 * Constructor.
28 */
29 template <int D>
31 : waves_(),
32 stars_(),
33 waveIds_(),
34 starIds_(),
35 nWave_(0),
36 nBasisWave_(0),
37 nStar_(0),
38 nBasis_(0),
39 unitCellPtr_(0),
40 meshPtr_(0),
41 isInitialized_(false)
42 {}
43
44 /*
45 * Destructor.
46 */
47 template <int D>
49 {}
50
51 /*
52 * Construct basis for pseudo-spectral scft.
53 */
54 template <int D>
55 void Basis<D>::makeBasis(Mesh<D> const & mesh,
56 UnitCell<D> const & unitCell,
57 std::string groupName)
58 {
59 SpaceGroup<D> group;
60 readGroup(groupName, group);
61 makeBasis(mesh, unitCell, group);
62 }
63
64 /*
65 * Construct a symmetry-adapted basis for pseudo-spectral scft.
66 */
67 template <int D>
68 void Basis<D>::makeBasis(Mesh<D> const & mesh,
69 UnitCell<D> const & unitCell,
70 SpaceGroup<D> const & group)
71 {
72 // Precondition: Check compatibility of mesh with space group
73 group.checkMeshDimensions(mesh.dimensions());
74
75 // Save pointers to mesh and unit cell
76 meshPtr_ = &mesh;
77 unitCellPtr_ = &unitCell;
78
79 // Allocate arrays
80 nWave_ = mesh.size();
81 waves_.allocate(nWave_);
82 waveIds_.allocate(nWave_);
83
84 // Make sorted array of waves
85 makeWaves();
86
87 // Identify stars of waves that are related by symmetry
88 makeStars(group);
89
90 // Apply validity test suite
91 bool valid = isValid();
92 if (!valid) {
93 UTIL_THROW("Basis failed validity check");
94 }
95
96 isInitialized_ = true;
97 }
98
99 /*
100 * Construct ordered list of waves.
101 *
102 * On exit:
103 * - Array waves_ contains list of waves ordered by sqNorm.
104 * - Each wave has indicesDft, indicesBz and sqNorm set.
105 * - Array stars_ is still empty.
106 */
107 template <int D>
109 {
110 IntVec<D> meshDimensions = mesh().dimensions();
111 std::vector< TWave<D> > twaves;
112 twaves.reserve(nWave_);
113
114 // Loop over dft mesh to generate all waves, add to twaves
115 TWave<D> w;
116 IntVec<D> v;
117 MeshIterator<D> itr(mesh().dimensions());
118 for (itr.begin(); !itr.atEnd(); ++itr) {
119 w.indicesDft = itr.position();
120 v = shiftToMinimum(w.indicesDft, meshDimensions, *unitCellPtr_);
121 w.indicesBz = v;
122 w.sqNorm = unitCell().ksq(v);
123 twaves.push_back(w);
124 }
125
126 // Sort temporary array twaves
127 TWaveNormComp<D> comp;
128 std::sort(twaves.begin(), twaves.end(), comp);
129
130 // Copy temporary array twaves into member variable waves_
131 for (int i = 0; i < nWave_; ++i) {
132 waves_[i].sqNorm = twaves[i].sqNorm;
133 waves_[i].indicesDft = twaves[i].indicesDft;
134 waves_[i].indicesBz = twaves[i].indicesBz;
135 }
136
137 }
138
139 /*
140 * Complete construction of a basis by grouping presorted waves into
141 * stars and completing initialization of all Wave and Star objects.
142 */
143 template <int D>
144 void Basis<D>::makeStars(SpaceGroup<D> const & group)
145 {
146
147 /*
148 * Conceptual definitions:
149 * - A "list" is a set of wavevectors of equal magnitude.
150 * - A "star" is a set of wavevectors that are related by symmetry.
151 * Each list may contain one or more complete stars. Lists are
152 * identified as an intermediate step in identification of stars.
153 *
154 * Local wavevector containers:
155 * During processing, wavevectors are temporarily stored in
156 * TWave<D> objects. The following local containers of TWave<D>
157 * objects used:
158 * list - a std::set of waves of equal norm (a "list")
159 * tempList - an ordered list, with contiguous stars
160 * star - a std::set of symmetry-related waves (a "star")
161 * tempStar - a sorted star, sorted by descending indicesBz
162 */
163
164 // Local TWave<D> containers and associated iterators
165 std::set< TWave<D>, TWaveDftComp<D> > list;
166 std::set< TWave<D>, TWaveDftComp<D> > star;
167 std::vector< TWave<D> > tempStar;
168 GArray< TWave<D> > tempList;
169 typename std::set< TWave<D>, TWaveDftComp<D> >::iterator rootItr;
170 typename std::set< TWave<D>, TWaveDftComp<D> >::iterator setItr;
171
172 // Local variables
173 TWave<D> wave;
174 Basis<D>::Star newStar;
175 std::complex<double> coeff;
176 double Gsq;
177 double Gsq_max;
178 double phase_diff;
179 const double twoPi = 2.0*Constants::Pi;
180 const double epsilon = 1.0E-8;
181 IntVec<D> meshDimensions = mesh().dimensions();
182 IntVec<D> rootVecBz; // BZ indices for root of this star
183 IntVec<D> vec; // Indices of temporary wavevector
184 IntVec<D> nVec; // Indices of negation of a wavevector
185 int listId = 0; // id for this list
186 int listBegin = 0; // id of first wave in this list
187 int listEnd = 0; // (id of last wave in this list) + 1
188 int listSize; // listEnd - listBegin
189 int starId = 0; // id for this star
190 int starBegin = 0; // id of first wave in this star
191 int i, j, k;
192 bool cancel;
193
194 /*
195 * Overview of algorithm:
196 *
197 * Precondition: Wavevectors in the array waves_ are sorted in
198 * nondecreasing order by wavevector norm.
199 *
200 * Loop over index i of array waves_ {
201 *
202 * Search for end of a "list" (i.e., contiguous block of waves
203 * of equal magnitude) by identifying changes in magnitude.
204 * The resulting list has indices [listBegin,listEnd-1].
205 * Set newList true.
206 *
207 * // Each list may contain one or more stars.
208 * // Process the newly identified list to identify stars
209 * If (newList) {
210 *
211 * Copy all waves in the range into std::set list
212 *
213 * Set rootItr to the first wave in list
214 *
215 * // Loop over stars within the list
216 * while (list.size() > 0) {
217 *
218 * // To generate a star from a root wave rootItr,
219 * // loop over symmetry operations of space group.
220 * For each symmetry operation group[j] {
221 * Compute vec = (rootItr->indicesBz)*group[j]
222 * Set phase = rootItr->indicesBz .dot. (group[j].t)
223 * Check for cancellation of the star, set "cancel" flag
224 * Add wave to std::set<TWave> star if not added before
225 * // Here, use of a std::set simplifies test of uniqueness
226 * }
227 *
228 * Copy all waves from star to std::vector<TWave> tempStar
229 * Sort tempStar by indicesBz, in descending order
230 * // Here, use of a std::vector for tempStar allows sorting
231 * For each wave in tempStar {
232 * Append the wave to std::vector<TWave> tempList
233 * Erase the wave from std::set<TWave> list
234 * // Here, use of a std::set for list simplifies erasure
235 * }
236 *
237 * Initialize a Star object named newStar, assign values
238 * to members beginId, endId, size, cancel
239 *
240 * // Assign values of newStar.invertFlag, rootItr, nextInvert
241 * if (nextInvert == -1) {
242 * // This is the second star in pair
243 * newStar.invertFlag = -1;
244 * nextInvert = 1;
245 * Set rootItr to the first wave in remaining list
246 * } else {
247 * Search for negation of rootItr in this star
248 * if negation is in this star {
249 * newStar.invertFlag = 0
250 * nextInvert = 1;
251 * Set rootItr to the first wave in remaining list
252 * } else
253 * Search for negation of rootItr in this remaining list
254 * if the negation is in the remaining list {
255 * newStar.invertFlag = 1
256 * nextInvert = -1;
257 * set rootItr to negation of current root
258 * }
259 * }
260 *
261 * Append newStar object to GArray<Star> stars_
262 *
263 * } // end loop over stars in a single list
264 *
265 * // At this point, tempList contains the contents of the
266 * // waves_ array occupying the range [beginId, endId-1],
267 * // grouped by stars, with waves within each star sorted
268 * // by indexBz.
269 *
270 * // Overwrite the block of array waves_ with indices in the
271 * // range [beginId, endId-1] with the contents of tempList.
272 * For each wave in tempList {
273 * Copy a TWave in tempList to a Basis:Wave in waves_
274 * Assign a complex coefficient of unit norm to the Wave
275 * }
276 *
277 * // At this point, coefficients of waves have unit magnitude
278 * // and correct relative phases within each star, but not
279 * // the final absolute phases or magnitude.
280 *
281 * } // end processing of one list (waves of equal norm)
282 *
283 * } // End initial processing of all waves and stars
284 *
285 * // Set phases of wave coefficients
286 * For each star in array stars_ {
287 * if star is closed under inversion (star.invertFlag == 0) {
288 * if star is cancelled {
289 * set coefficients of all waves to zero
290 * } else {
291 * Set the root to the first wave in the star
292 * Check closure (i.e., negation of root is in this star)
293 * For each wave in star:
294 * Divide coeff by the root coefficient
295 * }
296 * if (coeffs of root & negation are not complex conjugates){
297 * Divide all coeffs by a common phasor chosen to obtain
298 * complex conjugate coefficients for root and partner
299 * }
300 * }
301 * } else
302 * if (star.invertFlag == 1) {
303 * Set root of this star to the 1st wave in the star
304 * Find negation of root (aka "partner") in the next star
305 * If this star is cancelled {
306 * Set coefficients in this star and next to zero
307 * } else {
308 * For each wave in this star:
309 * Divide coeff by the root coefficient
310 * }
311 * For each wave in the next star:
312 * Divide coeff by the partner coefficient
313 * }
314 * }
315 * }
316 * // Note: If star.invertFlag = -1, do nothing because properties
317 * // of this star were all set when processing its partner.
318 * }
319 *
320 * // For all waves, normalize coefficients and set starId
321 * For each star in array stars_ {
322 * For each wave in this star {
323 * Set Wave.starId
324 * Divide coefficient by sqrt(double(star.size))
325 * }
326 * }
327 *
328 * // For all waves, set implicit member and add to look up table
329 * For each wave in array waves_ {
330 * Set Wave::implicit attribute
331 * Assign waveIds_[rank] = i
332 * }
333 */
334
335 // Loop over all waves
336 nBasis_ = 0;
337 nBasisWave_ = 0;
338 Gsq_max = waves_[0].sqNorm;
339 for (i = 1; i <= nWave_; ++i) {
340
341 // Determine if this wave begins a new list
342 bool newList = false;
343 if (i == nWave_) {
344 listEnd = i;
345 listSize = listEnd - listBegin;
346 newList = true;
347 } else {
348 Gsq = waves_[i].sqNorm;
349 if (Gsq > Gsq_max + epsilon) {
350 Gsq_max = Gsq;
351 listEnd = i;
352 listSize = listEnd - listBegin;
353 newList = true;
354 }
355 }
356
357 // Process completed list of wavectors of equal norm
358 if (newList) {
359
360 // Copy waves of equal norm into std::set "list"
361 list.clear();
362 tempList.clear();
363 for (j = listBegin; j < listEnd; ++j) {
364 wave.indicesDft = waves_[j].indicesDft;
365 wave.indicesBz = waves_[j].indicesBz;
366 wave.sqNorm = waves_[j].sqNorm;
367 if (j > listBegin) {
368 UTIL_CHECK( std::abs(wave.sqNorm-waves_[j].sqNorm)
369 < 2.0*epsilon );
370 }
371 list.insert(wave);
372 }
373
374 // On entry to each iteration of the loop over stars,
375 // rootItr and nextInvert are known. The iterator rootItr
376 // points to the wave in the remaining list that will be
377 // used as the root of the next star. The flag nextInvert
378 // is equal to -1 iff the previous star was the first of
379 // a pair that are open under inversion, and is equal
380 // to + 1 otherwise.
381
382 // Initial values for first star in this list
383 rootItr = list.begin();
384 int nextInvert = 1;
385
386 // Loop over stars with a list of waves of equal norm,
387 // removing each star from the list as it is identified.
388 // The root of the next star must have been chosen on
389 // entry to each iteration of this loop.
390
391 while (list.size() > 0) {
392
393 rootVecBz = rootItr->indicesBz;
394 Gsq = rootItr->sqNorm;
395 cancel = false;
396 star.clear();
397
398 // Construct a star from root vector, by applying every
399 // symmetry operation in the group to the root wavevector.
400 for (j = 0; j < group.size(); ++j) {
401
402 // Apply symmetry (i.e., multiply by rotation matrix)
403 // vec = rotated wavevector.
404 vec = rootVecBz*group[j];
405
406 // Check that rotated vector has same norm as root.
407 UTIL_CHECK(std::abs(Gsq - unitCell().ksq(vec)) < epsilon);
408
409 // Initialize TWave object associated with rotated wave
410 wave.sqNorm = Gsq;
411 wave.indicesBz = shiftToMinimum(vec, meshDimensions,
412 *unitCellPtr_);
413 wave.indicesDft = vec;
414 mesh().shift(wave.indicesDft);
415
416 // Compute phase for coeff. of wave in basis function.
417 // Convention -pi < phase <= pi.
418 wave.phase = 0.0;
419 for (k = 0; k < D; ++k) {
420 wave.phase += rootVecBz[k]*(group[j].t(k));
421 }
422 while (wave.phase > 0.5) {
423 wave.phase -= 1.0;
424 }
425 while (wave.phase <= -0.5) {
426 wave.phase += 1.0;
427 }
428 wave.phase *= twoPi;
429
430 // Check for cancellation of star: The star is
431 // cancelled if application of any symmetry operation
432 // in the group to the root vector yields a rotated
433 // vector equivalent to the root vector but with a
434 // nonzero phase, creating a contradiction.
435
436 if (wave.indicesDft == rootItr->indicesDft) {
437 if (std::abs(wave.phase) > 1.0E-6) {
438 cancel = true;
439 }
440 }
441
442 // Search for an equivalent wave already in the star.
443 // Note: Equivalent waves have equal DFT indices.
444 setItr = star.find(wave);
445
446 if (setItr == star.end()) {
447
448 // If no equivalent wave is found in the star,
449 // then add this wave to the star
450 star.insert(wave);
451
452 } else {
453
454 // If an equivalent wave is found, check if the
455 // phases are equivalent. If not, the star is
456 // cancelled.
457
458 phase_diff = setItr->phase - wave.phase;
459 while (phase_diff > 0.5) {
460 phase_diff -= 1.0;
461 }
462 while (phase_diff <= -0.5) {
463 phase_diff += 1.0;
464 }
465 if (std::abs(phase_diff) > 1.0E-6) {
466 cancel = true;
467 }
468
469 }
470
471 }
472
473 // Copy all waves from set star to std::vector tempStar
474 tempStar.clear();
475 setItr = star.begin();
476 for ( ; setItr != star.end(); ++setItr) {
477 tempStar.push_back(*setItr);
478 }
479
480 // Sort tempStar, in descending order by indicesBz.
481 TWaveBzComp<D> waveBzComp;
482 std::sort(tempStar.begin(), tempStar.end(), waveBzComp);
483
484 // Append contents of tempStar to tempList, erase from list
485 int tempStarSize = tempStar.size();
486 for (j = 0; j < tempStarSize; ++j) {
487 list.erase(tempStar[j]);
488 tempList.append(tempStar[j]);
489 }
490 UTIL_CHECK((int)(tempList.size()+list.size()) == listSize);
491
492 // If this star is not cancelled, increment the number of
493 // basis functions (nBasis_) & waves in basis (nBasisWave_)
494 if (!cancel) {
495 ++nBasis_;
496 nBasisWave_ += star.size();
497 }
498
499 // Initialize a Star object
500 // newStar.eigen = Gsq;
501 newStar.beginId = starBegin;
502 newStar.endId = newStar.beginId + star.size();
503 newStar.size = star.size();
504 newStar.cancel = cancel;
505 // Note: newStar.starInvert is not yet known
506
507 // Determine invertFlag, rootItr and nextInvert
508 if (nextInvert == -1) {
509
510 // If this star is 2nd of a pair related by inversion,
511 // set root for next star to 1st wave of remaining list.
512
513 newStar.invertFlag = -1;
514 rootItr = list.begin();
515 nextInvert = 1;
516
517 } else {
518
519 // If this star is not the 2nd of a pair of partners,
520 // then determine if it is closed under inversion.
521
522 // Compute negation nVec of root vector in FBZ
523 nVec.negate(rootVecBz);
524
525 // Shift negation nVec to the DFT mesh
526 (*meshPtr_).shift(nVec);
527
528 // Search for negation of root vector within this star
529 bool negationFound = false;
530 setItr = star.begin();
531 for ( ; setItr != star.end(); ++setItr) {
532 if (nVec == setItr->indicesDft) {
533 negationFound = true;
534 break;
535 }
536 }
537
538 if (negationFound) {
539
540 // If this star is closed under inversion, the root
541 // of next star is the 1st vector of remaining list.
542
543 newStar.invertFlag = 0;
544 rootItr = list.begin();
545 nextInvert = 1;
546
547 } else {
548
549 // This star is open under inversion, and is the
550 // first star of a pair related by inversion
551
552 newStar.invertFlag = 1;
553 nextInvert = -1;
554
555 // Find negation of the root of this star in the
556 // remaining list, and use this negation as the
557 // root of the next star.
558
559 setItr = list.begin();
560 for ( ; setItr != list.end(); ++setItr) {
561 if (nVec == setItr->indicesDft) {
562 negationFound = true;
563 rootItr = setItr;
564 break;
565 }
566 }
567 // If negationFound, then rootItr->indicesDft = nVec
568
569 // Failure to find the negation here is an error:
570 // It must be either in this star or remaining list
571
572 if (!negationFound) {
573 std::cout << "Negation not found for: " << "\n";
574 std::cout << " vec (ft):"
575 << rootItr->indicesDft <<"\n";
576 std::cout << " vec (bz):"
577 << rootItr->indicesBz <<"\n";
578 std::cout << "-vec (dft):" << nVec << "\n";
579 UTIL_CHECK(negationFound);
580 }
581
582 }
583
584 }
585
586 stars_.append(newStar);
587 ++starId;
588 starBegin = newStar.endId;
589
590 }
591 // End loop over stars within a list.
592
593 UTIL_CHECK(list.size() == 0);
594 UTIL_CHECK(tempList.size() == listEnd - listBegin);
595
596 // Copy tempList into corresponding section of waves_,
597 // overwriting the section of waves_ used to create the list.
598 // Compute a complex coefficient of unit norm for each wave.
599 for (j = 0; j < tempList.size(); ++j) {
600 k = j + listBegin;
601 waves_[k].indicesDft = tempList[j].indicesDft;
602 waves_[k].indicesBz = tempList[j].indicesBz;
603 waves_[k].sqNorm = tempList[j].sqNorm;
604 coeff = std::complex<double>(0.0, tempList[j].phase);
605 coeff = exp(coeff);
606 if (std::abs(imag(coeff)) < 1.0E-6) {
607 coeff = std::complex<double>(real(coeff), 0.0);
608 }
609 if (std::abs(real(coeff)) < 1.0E-6) {
610 coeff = std::complex<double>(0.0, imag(coeff));
611 }
612 waves_[k].coeff = coeff;
613 }
614
615 // Processing of list is now complete.
616 // Here, waves_[k].coeff has unit absolute magnitude, and
617 // correct relative phases for waves within a star, but
618 // the coeff may not be unity for the first or last wave in
619 // the star.
620
621 ++listId;
622 listBegin = listEnd;
623 }
624 // Finished processing a list of waves of equal norm
625
626 } // End loop over all waves
627 nStar_ = stars_.size();
628 // Complete initial processing of all lists and stars
629
630 /*
631 * Conventions for phases of wave coefficients (imposed below):
632 * - Coefficients of the root of each star and its negation must
633 * be complex conjugates.
634 * - In a closed star (starInvert = 0), the coefficient of the
635 * root must have a non-negative real part. If the root
636 * coefficient is pure imaginary, the imaginary part must
637 * be negative.
638 * - In a pair of open stars that are related by inversion
639 * symmetry, the coefficients of the first wave of the first star
640 * and the last wave of the second star (i.e., the roots of both
641 * stars) must have real coefficients.
642 */
643
644 // Final processing of phases of of waves in stars:
645 std::complex<double> rootCoeff; // Coefficient of root wave
646 std::complex<double> partCoeff; // Coefficient of partner of root
647 std::complex<double> d;
648 int rootId, partId;
649 for (i = 0; i < nStar_; ++i) {
650
651 // Treat open and closed stars differently
652
653 if (stars_[i].invertFlag == 0) {
654
655 // Identify root of this star (star i)
656 // Set the root to be the first wave in the star
657
658 rootId = stars_[i].beginId;
659 stars_[i].waveBz = waves_[rootId].indicesBz;
660
661 if (stars_[i].cancel) {
662
663 // If the star is cancelled, set all coefficients to zero
664 std::complex<double> czero(0.0, 0.0);
665 for (j = stars_[i].beginId; j < stars_[i].endId; ++j) {
666 waves_[j].coeff = czero;
667 }
668
669 } else { // if not cancelled
670
671 // Compute nVec = negation of root, shifted to DFT mesh
672 nVec.negate(waves_[rootId].indicesBz);
673 (*meshPtr_).shift(nVec);
674
675 // Find negation of root in this star, set partId to index
676 bool negationFound = false;
677 for (j = stars_[i].beginId; j < stars_[i].endId; ++j) {
678 if (nVec == waves_[j].indicesDft) {
679 partId = j;
680 negationFound = true;
681 break;
682 }
683 }
684
685 // For invertFlag == 0, failure to find nVec in this
686 // star is a fatal error
687 UTIL_CHECK(negationFound);
688
689 // Divide all coefficients by the root coefficient
690 rootCoeff = waves_[rootId].coeff;
691 for (j = stars_[i].beginId; j < stars_[i].endId; ++j) {
692 waves_[j].coeff /= rootCoeff;
693 }
694 rootCoeff = waves_[rootId].coeff;
695 UTIL_CHECK(std::abs(real(rootCoeff) - 1.0) < 1.0E-9);
696 UTIL_CHECK(std::abs(imag(rootCoeff)) < 1.0E-9);
697
698 // Require coefficients of root and negation are conjugates
699 if (partId != rootId) {
700
701 partCoeff = waves_[partId].coeff;
702 UTIL_CHECK(std::abs(std::abs(partCoeff) - 1.0) < 1.0E-9);
703 if (std::abs(partCoeff - rootCoeff) > 1.0E-6) {
704 d = sqrt(partCoeff);
705 if (real(d) < -1.0E-4) {
706 d = -d;
707 } else
708 if (std::abs(real(d)) <= 1.0E-4) {
709 if (imag(d) < 0.0) {
710 d = -d;
711 }
712 }
713 for (j=stars_[i].beginId; j < stars_[i].endId; ++j){
714 waves_[j].coeff /= d;
715 }
716 }
717
718 }
719
720 } // end if (cancel) ... else ...
721
722 } // end if (stars_[i].invertFlag == 0)
723 else
724 if (stars_[i].invertFlag == 1) {
725
726 // Process a pair of open stars related by inversion.
727
728 // Preconditions:
729 UTIL_CHECK(stars_[i].size == stars_[i+1].size);
730 UTIL_CHECK(stars_[i].cancel == stars_[i+1].cancel);
731 // UTIL_CHECK(stars_[i+1].invertFlag == -1);
732
733 // Identify root of this star (star i)
734 // Set the root to be the first wave in the star
735 rootId = stars_[i].beginId;
736 stars_[i].waveBz = waves_[rootId].indicesBz;
737
738 // Compute nVec = negation of root vector, shifted to DFT mesh
739 nVec.negate(waves_[rootId].indicesBz);
740 (*meshPtr_).shift(nVec);
741
742 // Seek negation of root wave in the next star (star i+1)
743 bool negationFound = false;
744 for (j = stars_[i+1].beginId; j < stars_[i+1].endId; ++j) {
745 if (nVec == waves_[j].indicesDft) {
746 partId = j;
747 stars_[i+1].waveBz = waves_[j].indicesBz;
748 negationFound = true;
749 break;
750 }
751 }
752
753 // For invertFlag == 1, absence of nVec in next star is fatal
754 UTIL_CHECK(negationFound);
755
756 if (stars_[i].cancel) {
757
758 std::complex<double> czero(0.0, 0.0);
759 for (j = stars_[i].beginId; j < stars_[i].endId; ++j) {
760 waves_[j].coeff = czero;
761 }
762 for (j = stars_[i+1].beginId; j < stars_[i+1].endId; ++j) {
763 waves_[j].coeff = czero;
764 }
765
766 } else { // if star is not cancelled
767
768 // Divide all coefficients in this star by root coeff
769 rootCoeff = waves_[rootId].coeff;
770 for (j = stars_[i].beginId; j < stars_[i].endId; ++j) {
771 waves_[j].coeff /= rootCoeff;
772 }
773
774 // Divide coefficients in next star by a partner coeff.
775 partCoeff = waves_[partId].coeff;
776 for (j = stars_[i+1].beginId; j < stars_[i+1].endId; ++j) {
777 waves_[j].coeff /= partCoeff;
778 }
779
780 } // end if (cancel) ... else ...
781
782 } // end if (invertFlag==0) ... else if (invertFlag==1) ...
783
784 // Note: If invertFlag == -1, do nothing and continue
785 // Related stars with invertFlag == 1 and -1 are treated together
786
787 } // end loop over stars
788
789 // For all waves, normalize coefficients and set starId
790 for (i = 0; i < nStar_; ++i) {
791 double snorm = 1.0/sqrt(double(stars_[i].size));
792 for (j = stars_[i].beginId; j < stars_[i].endId; ++j) {
793 waves_[j].coeff *= snorm;
794 waves_[j].starId = i;
795 }
796 }
797
798 // Set tiny real and imaginary parts to zero (due to round-off)
799 for (i = 0; i < nWave_; ++i) {
800 if (std::abs(real(waves_[i].coeff)) < 1.0E-8) {
801 waves_[i].coeff
802 = std::complex<double>(0.0, imag(waves_[i].coeff));
803 }
804 if (std::abs(imag(waves_[i].coeff)) < 1.0E-8) {
805 waves_[i].coeff
806 = std::complex<double>(real(waves_[i].coeff), 0.0);
807 }
808 }
809
810 // For each wave, set implicit attribute and add to look-up table
811 for (i = 0; i < nWave_; ++i) {
812 vec = waves_[i].indicesDft;
813
814 // Validity check - check that vec is in dft mesh
815 for (j = 0; j < D; ++j) {
816 UTIL_CHECK(vec[j] >= 0);
817 UTIL_CHECK(vec[j] < meshDimensions[j]);
818 }
819
820 // Set implicit attribute
821 if ((vec[D-1] + 1) > (meshDimensions[D-1]/2 + 1)) {
822 waves_[i].implicit = true;
823 } else {
824 waves_[i].implicit = false;
825 }
826
827 // Look up table for waves
828 waveIds_[mesh().rank(vec)] = i;
829 }
830
831 // Set Star::starId and Star::basisId, and to look-up table starIds_.
832 starIds_.allocate(nBasis_);
833 j = 0;
834 for (i = 0; i < nStar_; ++i) {
835 stars_[i].starId = i;
836 if (stars_[i].cancel) {
837 stars_[i].basisId = -1;
838 } else {
839 stars_[i].basisId = j;
840 UTIL_CHECK(j < nBasis_);
841 starIds_[j] = i;
842 ++j;
843 }
844 }
845 UTIL_CHECK(j == nBasis_);
846
847 }
848
849 // Return value of nBasis
850 template <int D>
852 { return nBasis_; }
853
854
855 template <int D>
856 void Basis<D>::outputWaves(std::ostream& out, bool outputAll) const
857 {
858 out << "N_wave" << std::endl;
859 if (outputAll) {
860 out << " " << nWave_ << std::endl;
861 } else {
862 out << " " << nBasisWave_ << std::endl;
863 }
864 int i, j, k, starId;
865 k = 0;
866 for (i = 0; i < nWave_; ++i) {
867 starId = waves_[i].starId;
868 if (outputAll || (!stars_[starId].cancel)) {
869 out << Int(k, 8);
870 out << Int(i, 8);
871 for (j = 0; j < D; ++j) {
872 out << Int(waves_[i].indicesBz[j], 5);
873 }
874 out << Int(waves_[i].starId, 6);
875 out << " " << Dbl(waves_[i].coeff.real(), 15);
876 out << " " << Dbl(waves_[i].coeff.imag(), 15);
877 out << std::endl;
878 k++;
879 }
880 }
881 }
882
883
884 template <int D>
885 void Basis<D>::outputStars(std::ostream& out, bool outputAll) const
886 {
887 // Output number of stars in appropriate format
888 if (outputAll) {
889 out << "N_star" << std::endl
890 << " " << nStar_ << std::endl;
891 } else {
892 out << "N_basis" << std::endl
893 << " " << nBasis_ << std::endl;
894 }
895
896 // Loop over stars
897 int i, j;
898 for (i = 0; i < nStar_; ++i) {
899 if (outputAll || (!stars_[i].cancel)) {
900 out << Int(stars_[i].basisId, 6); // basisId
901 out << Int(i, 6); // starId
902 out << Int(stars_[i].size, 5)
903 << Int(stars_[i].beginId, 8)
904 << Int(stars_[i].endId, 8)
905 << Int(stars_[i].invertFlag, 4);
906 if (outputAll) {
907 out << Int(stars_[i].cancel, 4);
908 }
909 for (j = 0; j < D; ++j) {
910 out << Int(stars_[i].waveBz[j], 6);
911 }
912 out << std::endl;
913 }
914 }
915 }
916
917 template <int D>
918 bool Basis<D>::isValid() const
919 {
920 double Gsq;
921 IntVec<D> v;
922 int is, ib, iw, iwp, j;
923
924 // Check total number of waves == # of grid points
925 if (nWave_ != mesh().size()) {
926 std::cout << "nWave != size of mesh" << std::endl;
927 return false;
928 }
929
930 // Loop over dft mesh to check consistency of waveIds_ and waves_
931 MeshIterator<D> itr(mesh().dimensions());
932 for (itr.begin(); !itr.atEnd(); ++itr) {
933 v = itr.position();
934 iw = waveId(v);
935 if (wave(iw).indicesDft != v) {
936 std::cout << "Inconsistent waveId and Wave::indicesDft"
937 << std::endl;
938 return false;
939 }
940 }
941
942 // Loop over elements of waves_, check consistency of wave data.
943 for (iw = 0; iw < nWave_; ++iw) {
944
945 // Check sqNorm
946 v = waves_[iw].indicesBz;
947 Gsq = unitCell().ksq(v);
948 if (std::abs(Gsq - waves_[iw].sqNorm) > 1.0E-8) {
949 std::cout << "\n";
950 std::cout << "Incorrect sqNorm:" << "\n"
951 << "wave.indicesBz = " << "\n"
952 << "wave.sqNorm = " << waves_[iw].sqNorm << "\n"
953 << "|v|^{2} = " << Gsq << "\n";
954 return false;
955 }
956
957 // Check that wave indicesBz is an image of indicesDft
958 mesh().shift(v);
959 if (v != waves_[iw].indicesDft) {
960 std::cout << "\n";
961 std::cout << "shift(indicesBz) != indicesDft" << std::endl;
962 return false;
963 }
964
965 // Compare Wave::starId to Star::beginId and Star::endId
966 is = waves_[iw].starId;
967 if (iw < stars_[is].beginId) {
968 std::cout << "\n";
969 std::cout << "Wave::starId < Star::beginId" << std::endl;
970 return false;
971 }
972 if (iw >= stars_[is].endId) {
973 std::cout << "\n";
974 std::cout << " Wave::starId >= Star::endId" << std::endl;
975 return false;
976 }
977 }
978
979 // Loop over all stars (elements of stars_ array)
980 int nWave = 0;
981 for (is = 0; is < nStar_; ++is) {
982
983 // Check star size
984 nWave += stars_[is].size;
985 if (stars_[is].size != stars_[is].endId - stars_[is].beginId) {
986 std::cout << "\n";
987 std::cout << "Inconsistent Star::size:" << std::endl;
988 std::cout << "Star id " << is << std::endl;
989 std::cout << "star size " << stars_[is].size << std::endl;
990 std::cout << "Star begin " << stars_[is].beginId << std::endl;
991 std::cout << "Star end " << stars_[is].endId << std::endl;
992 return false;
993 }
994 if (is > 0) {
995 if (stars_[is].beginId != stars_[is-1].endId) {
996 std::cout << "\n";
997 std::cout << "Star ranges not consecutive:" << std::endl;
998 std::cout << "Star id " << is << std::endl;
999 std::cout << "stars_[" << is << "]" << ".beginId = "
1000 << stars_[is].beginId << std::endl;
1001 std::cout << "stars_[" << is-1 << "]" << ".endId = "
1002 << stars_[is-1].endId << std::endl;
1003 return false;
1004 }
1005 }
1006
1007 // Check star ids of waves in star
1008 for (iw = stars_[is].beginId; iw < stars_[is].endId; ++iw) {
1009 if (waves_[iw].starId != is) {
1010 std::cout << "\n";
1011 std::cout << "Inconsistent Wave::starId :" << std::endl;
1012 std::cout << "star id " << is << std::endl;
1013 std::cout << "star beginId " << stars_[is].beginId << "\n";
1014 std::cout << "star endId " << stars_[is].endId << "\n";
1015 std::cout << "wave id " << iw << "\n";
1016 std::cout << "wave starId " << waves_[iw].starId << "\n";
1017 return false;
1018 }
1019 }
1020
1021 // Check Star::starId is equal to array index
1022 if (stars_[is].starId != is) {
1023 std::cout << "\n";
1024 std::cout << "stars_[is].starId != is for "
1025 << "is = " << is << "\n";
1026 return false;
1027 }
1028
1029 // Check Star::basisId and starIds_ look up table
1030 ib = stars_[is].basisId;
1031 if (stars_[is].cancel) {
1032 if (ib != -1) {
1033 std::cout << "\n";
1034 std::cout << "basisId != -1 for cancelled star\n";
1035 std::cout << "star id = " << is << "\n";
1036 return false;
1037 }
1038 } else {
1039 if (starIds_[ib] != is) {
1040 std::cout << "\n";
1041 std::cout << "starIds_[stars_[is].basisId] != is for: \n";
1042 std::cout << "is = " << is << "\n";
1043 std::cout << "ib = stars_[is].basisId = " << ib << "\n";
1044 std::cout << "starIds_[ib] = " << starIds_[ib]
1045 << "\n";
1046 return false;
1047 }
1048 }
1049
1050 // Check ordering of waves in star
1051 for (iw = stars_[is].beginId + 1; iw < stars_[is].endId; ++iw) {
1052 if (waves_[iw].indicesBz > waves_[iw-1].indicesBz) {
1053 std::cout << "\n";
1054 std::cout << "Failure of ordering by indicesB within star"
1055 << std::endl;
1056 return false;
1057 }
1058 if (waves_[iw].indicesBz == waves_[iw-1].indicesBz) {
1059 std::cout << "\n";
1060 std::cout << "Equal values of indicesBz within star"
1061 << std::endl;
1062 return false;
1063 }
1064 }
1065
1066 } // End do loop over all stars
1067
1068 // Check that all waves in mesh are accounted for in stars
1069 if (stars_[nStar_-1].endId != mesh().size()) {
1070 std::cout << "\n";
1071 std::cout << "Star endId of last star != mesh size" << std::endl;
1072 return false;
1073 }
1074 if (nWave != mesh().size()) {
1075 std::cout << "\n";
1076 std::cout << "Sum of star sizes != mesh size" << std::endl;
1077 return false;
1078 }
1079
1080 // Loop over closed stars and related pairs of stars.
1081 // Test closure under inversion and conjugacy of coefficients.
1082 std::complex<double> cdel;
1083 bool negationFound, cancel;
1084 is = 0;
1085 while (is < nStar_) {
1086 cancel = stars_[is].cancel;
1087
1088 if (stars_[is].invertFlag == 0) {
1089
1090 // Test that star is closed under inversion and real
1091 int begin = stars_[is].beginId;
1092 int end = stars_[is].endId;
1093 for (iw = begin; iw < end; ++iw) {
1094 v.negate(waves_[iw].indicesBz);
1095 mesh().shift(v);
1096 negationFound = false;
1097 for (iwp = begin; iw < end; ++iwp) {
1098 if (waves_[iwp].indicesDft == v) {
1099 negationFound = true;
1100 if (!cancel) {
1101 cdel = conj(waves_[iwp].coeff);
1102 cdel -= waves_[iw].coeff;
1103 }
1104 break;
1105 }
1106 }
1107 if (!negationFound) {
1108 std::cout << "\n";
1109 std::cout << "Negation not found in closed star"
1110 << std::endl;
1111 std::cout << "G = " << waves_[iw].indicesBz
1112 << "coeff = " << waves_[iw].coeff
1113 << std::endl;
1114 std::cout << "All waves in star " << is << "\n";
1115 for (j=begin; j < end; ++j) {
1116 std::cout << waves_[j].indicesBz << " "
1117 << waves_[j].coeff << "\n";
1118 }
1119 return false;
1120 }
1121 if (!cancel && std::abs(cdel) > 1.0E-8) {
1122 std::cout << "\n";
1123 std::cout << "Function for closed star is not real:"
1124 << "\n";
1125 std::cout << "+G = " << waves_[iw].indicesBz
1126 << " coeff = " << waves_[iw].coeff
1127 << "\n";
1128 std::cout << "-G = " << waves_[iwp].indicesBz
1129 << " coeff = " << waves_[iwp].coeff
1130 << "\n";
1131 std::cout << "Coefficients are not conjugates." << "\n";
1132 std::cout << "All waves in star " << is << "\n";
1133 for (j=begin; j < end; ++j) {
1134 std::cout << waves_[j].indicesBz << " "
1135 << waves_[j].coeff << "\n";
1136 }
1137 return false;
1138 }
1139 if (cancel && std::abs(waves_[iw].coeff) > 1.0E-8) {
1140 std::cout << "\n";
1141 std::cout << "Nonzero coefficient in a cancelled star"
1142 << "\n";
1143 std::cout << "G = " << waves_[iw].indicesBz
1144 << " coeff = " << waves_[iw].coeff
1145 << "\n";
1146 return false;
1147 }
1148 }
1149
1150 // Finished processing a closed star, increment counter is
1151 ++is;
1152
1153 } else {
1154
1155 // Test pairs of open stars
1156
1157 if (stars_[is].invertFlag != 1) {
1158 std::cout << "\n";
1159 std::cout << "Expected invertFlag == 1" << std::endl;
1160 return false;
1161 }
1162 if (stars_[is+1].invertFlag != -1) {
1163 std::cout << "\n";
1164 std::cout << "Expected invertFlag == -1" << std::endl;
1165 return false;
1166 }
1167 if (stars_[is+1].size != stars_[is].size) {
1168 std::cout << "\n";
1169 std::cout << "Partner stars of different size" << std::endl;
1170 return false;
1171 }
1172 if (stars_[is+1].cancel != stars_[is].cancel) {
1173 std::cout << "\n";
1174 std::cout << "Partners stars with different cancel flags"
1175 << std::endl;
1176 return false;
1177 }
1178
1179 // Begin and end wave ids for the first and second stars
1180 int begin1 = stars_[is].beginId;
1181 int end1 = stars_[is].endId;
1182 int begin2 = stars_[is+1].beginId;
1183 int end2 = stars_[is+1].endId;
1184
1185 // Check existence of negation and conjugate coefficients
1186 // Loop over waves in first star
1187 for (iw = begin1; iw < end1; ++iw) {
1188 v.negate(waves_[iw].indicesBz);
1189 mesh().shift(v);
1190 negationFound = false;
1191 // Loop over second star, searching for negation
1192 for (iwp = begin2; iw < end2; ++iwp) {
1193 if (waves_[iwp].indicesDft == v) {
1194 negationFound = true;
1195 if (!cancel) {
1196 cdel = conj(waves_[iwp].coeff);
1197 cdel -= waves_[iw].coeff;
1198 }
1199 break;
1200 }
1201 }
1202 if (!negationFound) {
1203 std::cout << "\n";
1204 std::cout << "Negation not found for G in open star"
1205 << std::endl;
1206 std::cout << "First star id = " << is << std::endl;
1207 std::cout << "+G = " << waves_[iw].indicesBz
1208 << "coeff = " << waves_[iw].coeff
1209 << std::endl;
1210 std::cout << "Waves in star " << is
1211 << " (starInvert ==1):" << "\n";
1212 for (j = begin1; j < end1; ++j) {
1213 std::cout << waves_[j].indicesBz << " "
1214 << waves_[j].coeff << "\n";
1215 }
1216 std::cout << "Waves in star " << is+1
1217 << " (starInvert == -1):" << "\n";
1218 for (j=begin2; j < end2; ++j) {
1219 std::cout << waves_[j].indicesBz << " "
1220 << waves_[j].coeff << "\n";
1221 }
1222 return false;
1223 } else
1224 if (!cancel && std::abs(cdel) > 1.0E-8) {
1225 std::cout << "\n";
1226 std::cout << "Error of coefficients in open stars:"
1227 << "\n";
1228 std::cout << "First star id = " << is << std::endl;
1229 std::cout << "+G = " << waves_[iw].indicesBz
1230 << " coeff = " << waves_[iw].coeff
1231 << "\n";
1232 std::cout << "-G = " << waves_[iwp].indicesBz
1233 << " coeff = " << waves_[iwp].coeff
1234 << "\n";
1235 std::cout << "Coefficients are not conjugates."
1236 << "\n";
1237 std::cout << "Waves in star " << is
1238 << " (starInvert ==1):" << "\n";
1239 for (j = begin1; j < end1; ++j) {
1240 std::cout << waves_[j].indicesBz << " "
1241 << waves_[j].coeff << "\n";
1242 }
1243 std::cout << "Waves in star " << is+1
1244 << " (starInvert == -1):" << "\n";
1245 for (j=begin2; j < end2; ++j) {
1246 std::cout << waves_[j].indicesBz << " "
1247 << waves_[j].coeff << "\n";
1248 }
1249 return false;
1250 }
1251 }
1252
1253 // Finished processing a pair, increment star counter by 2
1254 is += 2;
1255
1256 } // end if (stars_[is].invertFlag == 0) ... else ...
1257
1258 } // end while (is < nStar_) loop over stars
1259
1260 // Loop over basis functions
1261 for (ib = 0; ib < nBasis_; ++ib) {
1262 is = starIds_[ib];
1263 if (stars_[is].cancel) {
1264 std::cout << "\n";
1265 std::cout << "Star referred to by starIds_ is cancelled\n";
1266 return false;
1267 }
1268 if (stars_[is].basisId != ib) {
1269 std::cout << "\n";
1270 std::cout << "Eror: stars_[starIds_[ib]].basisId != ib\n";
1271 std::cout << "Basis function index ib = " << ib << "\n";
1272 std::cout << "is = starIds_[ib] = " << is << "\n";
1273 std::cout << "stars_[is].basisId = "
1274 << stars_[is].basisId << "\n";
1275 return false;
1276 }
1277 }
1278
1279 // The end of this function is reached iff all tests passed.
1280 return true;
1281 }
1282
1283}
1284#endif
Symmetry-adapted Fourier basis for pseudo-spectral scft.
Definition: Basis.h:346
~Basis()
Destructor.
Definition: Basis.tpp:48
int nBasis() const
Total number of nonzero symmetry-adapted basis functions.
Definition: Basis.tpp:851
bool isValid() const
Returns true if valid, false otherwise.
Definition: Basis.tpp:918
void outputStars(std::ostream &out, bool outputAll=false) const
Print a list of all stars to an output stream.
Definition: Basis.tpp:885
void makeBasis(Mesh< D > const &mesh, UnitCell< D > const &unitCell, SpaceGroup< D > const &group)
Construct basis for a specific mesh and space group.
Definition: Basis.tpp:68
Basis()
Default constructor.
Definition: Basis.tpp:30
void outputWaves(std::ostream &out, bool outputAll=false) const
Print a list of all waves to an output stream.
Definition: Basis.tpp:856
An IntVec<D, T> is a D-component vector of elements of integer type T.
Definition: IntVec.h:27
Iterator over points in a Mesh<D>.
Definition: MeshIterator.h:29
void begin()
Set iterator to the first point in the mesh.
bool atEnd() const
Is this the end (i.e., one past the last point)?
Definition: MeshIterator.h:141
IntVec< D > position() const
Get current position in the grid, as integer vector.
Definition: MeshIterator.h:122
Description of a regular grid of points in a periodic domain.
Definition: Mesh.h:61
IntVec< D > dimensions() const
Get an IntVec<D> of the grid dimensions.
Definition: Mesh.h:217
int size() const
Get total number of grid points.
Definition: Mesh.h:229
Crystallographic space group.
Definition: SpaceGroup.h:30
void checkMeshDimensions(IntVec< D > const &dimensions) const
Check if input mesh dimensions are compatible with space group.
Definition: SpaceGroup.tpp:64
Base template for UnitCell<D> classes, D=1, 2 or 3.
Definition: UnitCell.h:44
Vec< D, T > & negate(const Vec< D, T > &v)
Return negative of vector v.
Definition: Vec.h:520
static const double Pi
Trigonometric constant Pi.
Definition: Constants.h:35
Wrapper for a double precision number, for formatted ostream output.
Definition: Dbl.h:40
An automatically growable array, analogous to a std::vector.
Definition: GArray.h:34
int size() const
Return logical size of this array (i.e., current number of elements).
Definition: GArray.h:455
void clear()
Reset to empty state.
Definition: GArray.h:299
void append(Data const &data)
Append an element to the end of the sequence.
Definition: GArray.h:306
Wrapper for an int, for formatted ostream output.
Definition: Int.h:37
#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:51
C++ namespace for polymer self-consistent field theory (PSCF).
Simple wave struct for use within Basis construction.
Definition: TWave.h:23