PSCF v1.4.0
rFieldIo.tpp
1#ifndef PRDC_FIELD_IO_UTIL_TPP
2#define PRDC_FIELD_IO_UTIL_TPP
3
4/*
5* PSCF - Polymer Self-Consistent Field
6*
7* Copyright 2015 - 2025, The Regents of the University of Minnesota
8* Distributed under the terms of the GNU General Public License.
9*/
10
11#include <prdc/crystal/Basis.h>
12#include <prdc/crystal/UnitCell.h>
13#include <prdc/crystal/shiftToMinimum.h>
14#include <prdc/crystal/replicateUnitCell.h>
15#include <prdc/field/fieldHeader.h>
16
17#include <pscf/mesh/Mesh.h>
18#include <pscf/mesh/MeshIterator.h>
19#include <pscf/mesh/MeshIteratorFortran.h>
20#include <pscf/math/IntVec.h>
21#include <pscf/math/arithmetic.h>
22
23#include <util/containers/DArray.h>
24#include <util/misc/FileMaster.h>
25#include <util/misc/Log.h>
26#include <util/format/Int.h>
27#include <util/format/Dbl.h>
28#include <util/math/Constants.h>
29
30#include <string>
31
32namespace Pscf {
33namespace Prdc {
34
35 // RGrid File Io Templates
36
37 template <int D, class AT>
38 void readRGridData(std::istream& in,
39 DArray<AT>& fields,
40 int nMonomer,
41 IntVec<D> const& dimensions)
42 {
43 UTIL_CHECK(nMonomer > 0);
44 UTIL_CHECK(fields.capacity() == nMonomer);
45
46 MeshIteratorFortran<D> iter(dimensions);
47 double value;
48 int rank;
49 for (iter.begin(); !iter.atEnd(); ++iter) {
50 rank = iter.rank();
51 for (int k = 0; k < nMonomer; ++k) {
52 in >> value;
53 assign(fields[k][rank], value);
54 }
55 UTIL_ASSERT(in.good());
56 }
57 UTIL_CHECK(in.good());
58 }
59
60 template <int D, class AT>
61 void readRGridData(std::istream& in,
62 AT& field,
63 IntVec<D> const & dimensions)
64 {
65 MeshIteratorFortran<D> iter(dimensions);
66 double value;
67 int rank;
68 for (iter.begin(); !iter.atEnd(); ++iter) {
69 rank = iter.rank();
70 in >> value;
71 UTIL_ASSERT(in.good());
72 assign(field[rank], value);
73 }
74 UTIL_CHECK(in.good());
75 }
76
77 template <int D, class AT>
78 void writeRGridData(std::ostream& out,
79 DArray<AT> const& fields,
80 int nMonomer,
81 IntVec<D> const& dimensions)
82 {
83 UTIL_CHECK(nMonomer > 0);
84 UTIL_CHECK(nMonomer == fields.capacity());
85
86 MeshIteratorFortran<D> iter(dimensions);
87 int rank, j;
88 for (iter.begin(); !iter.atEnd(); ++iter) {
89 rank = iter.rank();
90 for (j = 0; j < nMonomer; ++j) {
91 out << " " << Dbl(fields[j][rank], 21, 13);
92 }
93 out << std::endl;
94 }
95
96 }
97
98 template <int D, class AT>
99 void writeRGridData(std::ostream& out,
100 AT const& field,
101 IntVec<D> const& dimensions)
102 {
103 MeshIteratorFortran<D> iter(dimensions);
104 int rank;
105 for (iter.begin(); !iter.atEnd(); ++iter) {
106 rank = iter.rank();
107 out << " " << Dbl(field[rank], 21, 13);
108 out << std::endl;
109 }
110 }
111
112 // KGrid file IO templates
113
114 template <int D, class ACT>
115 void readKGridData(std::istream& in,
116 DArray<ACT> & fields,
117 int nMonomer,
118 IntVec<D> const& dftDimensions)
119 {
120 //using RT = typename ACT::RealType;
121
122 typename ACT::RealType x, y;
123 MeshIterator<D> iter(dftDimensions);
124 int rank, i, j, idum;
125 i = 0;
126 for (iter.begin(); !iter.atEnd(); ++iter) {
127 rank = iter.rank();
128 in >> idum;
129 UTIL_CHECK(i == idum);
130 UTIL_CHECK(i == rank);
131 for (j = 0; j < nMonomer; ++j) {
132 in >> x;
133 in >> y;
134 assign(fields[j][rank], x, y);
135 }
136 UTIL_ASSERT(in.good());
137 ++i;
138 }
139 UTIL_CHECK(in.good());
140 }
141
142 template <int D, class ACT>
143 void readKGridData(std::istream& in,
144 ACT& field,
145 IntVec<D> const& dftDimensions)
146 {
147 //using RT = typename ACT::RealType;
148
149 //RT x, y;
150 typename ACT::RealType x, y;
151 MeshIterator<D> iter(dftDimensions);
152 int rank, idum;
153 int i = 0;
154 for (iter.begin(); !iter.atEnd(); ++iter) {
155 rank = iter.rank();
156 in >> idum;
157 UTIL_CHECK(i == idum);
158 UTIL_CHECK(i == rank);
159 in >> x;
160 in >> y;
161 UTIL_ASSERT(in.good());
162 assign(field[rank], x, y);
163 ++i;
164 }
165 UTIL_CHECK(in.good());
166 }
167
168 template <int D, class ACT>
169 void writeKGridData(std::ostream& out,
170 DArray<ACT> const& fields,
171 int nMonomer,
172 IntVec<D> const& dftDimensions)
173 {
174 UTIL_CHECK(nMonomer > 0);
175 UTIL_CHECK(nMonomer == fields.capacity());
176
177 //using RT = typename ACT::RealType;
178
179 //RT x, y;
180 typename ACT::RealType x, y;
181 MeshIterator<D> iter(dftDimensions);
182 int rank;
183 int i = 0;
184 for (iter.begin(); !iter.atEnd(); ++iter) {
185 rank = iter.rank();
186 UTIL_CHECK(i == rank);
187 out << Int(rank, 5);
188 for (int j = 0; j < nMonomer; ++j) {
189 x = real(fields[j][rank]);
190 y = imag(fields[j][rank]);
191 out << " "
192 << Dbl(x, 21, 13)
193 << Dbl(y, 21, 13);
194 }
195 out << std::endl;
196 ++i;
197 }
198
199 }
200
201 template <int D, class ACT>
202 void writeKGridData(std::ostream& out,
203 ACT const& field,
204 IntVec<D> const& dftDimensions)
205 {
206 //using RT = typename ACT::RealType;
207
208 //RT x, y;
209 typename ACT::RealType x, y;
210 MeshIterator<D> iter(dftDimensions);
211 int rank, i;
212 i = 0;
213 for (iter.begin(); !iter.atEnd(); ++iter) {
214 rank = iter.rank();
215 UTIL_CHECK(i == rank);
216 x = real(field[rank]);
217 y = imag(field[rank]);
218 out << Int(rank, 5);
219 out << " "
220 << Dbl(x, 21, 13)
221 << Dbl(y, 21, 13);
222 out << std::endl;
223 ++i;
224 }
225 }
226
227 // Field data IO in symmetry-adapted basis format
228
229 /*
230 * Read the data section for an array of fields in basis format.
231 */
232 template <int D>
233 void readBasisData(std::istream& in,
234 DArray< DArray<double> >& fields,
235 UnitCell<D> const& unitCell,
236 Mesh<D> const& mesh,
237 Basis<D> const& basis,
238 int nBasisIn)
239 {
240 UTIL_CHECK(basis.isInitialized());
241 UTIL_CHECK(fields.isAllocated());
242
243 int nMonomer = fields.capacity();
244 UTIL_CHECK(nMonomer > 0);
245
246 // Initialize all field array elements to zero
247 int fieldCapacity = fields[0].capacity();
248 UTIL_CHECK(fieldCapacity > 0);
249 for (int i = 0; i < nMonomer; ++i) {
250 UTIL_CHECK(fields[i].capacity() == fieldCapacity);
251 for (int j = 0; j < fieldCapacity; ++j) {
252 fields[i][j] = 0.0;
253 }
254 }
255
256 // Reset nBasis = min(nBasisIn, fieldCapacity)
257 int nBasis = nBasisIn;
258 if (fieldCapacity < nBasisIn) {
259 nBasis = fieldCapacity;
260 }
261
262 // Allocate temp arrays used to read in components
263 DArray<double> temp, temp2;
264 temp.allocate(nMonomer);
265 temp2.allocate(nMonomer);
266
267 typename Basis<D>::Star const * starPtr;
268 typename Basis<D>::Star const * starPtr2;
269 IntVec<D> waveIn, waveIn2;
270 int sizeIn, sizeIn2;
271 int starId, starId2;
272 int basisId, basisId2;
273 int waveId, waveId2;
274
275 std::complex<double> coeff, phasor;
276 IntVec<D> waveMin, waveDft;
277 int nReversedPair = 0;
278 bool waveExists, sizeMatches;
279
280 // Loop over stars in input file to read field components
281 int i = 0;
282 while (i < nBasis) {
283
284 // Read next line of data
285 for (int j = 0; j < nMonomer; ++j) {
286 in >> temp[j]; // field components
287 }
288 in >> waveIn; // wave of star
289 in >> sizeIn; // # of waves in star
290 UTIL_CHECK(in.good());
291 ++i;
292
293 sizeMatches = false;
294 waveExists = false;
295
296 // Check if wavevector waveIn is a minimum image
297 waveMin = shiftToMinimum(waveIn, mesh.dimensions(), unitCell);
298 waveExists = (waveIn == waveMin);
299
300 if (waveExists) {
301
302 // Find the star containing waveIn
303 waveDft = waveIn;
304 mesh.shift(waveDft);
305 waveId = basis.waveId(waveDft);
306 starId = basis.wave(waveId).starId;
307 starPtr = &basis.star(starId);
308 UTIL_CHECK(!(starPtr->cancel));
309 basisId = starPtr->basisId;
310
311 if (starPtr->size == sizeIn) {
312 sizeMatches = true;
313 } else {
314 Log::file()
315 << "Warning: Inconsistent star size (line ignored)\n"
316 << "wave from file = " << waveIn << "\n"
317 << "size from file = " << sizeIn << "\n"
318 << "size of star = " << starPtr->size
319 << "\n";
320 sizeMatches = false;
321 }
322
323 }
324
325 if (waveExists && sizeMatches) { // Attempt to process wave
326
327 if (starPtr->invertFlag == 0) {
328
329 if (starPtr->waveMin == waveIn) {
330
331 // Copy components of closed star to fields array
332 for (int j = 0; j < nMonomer; ++j) {
333 fields[j][basisId] = temp[j];
334 }
335
336 } else {
337
338 Log::file()
339 << "Inconsistent wave of closed star on input\n"
340 << "wave from file = " << waveIn << "\n"
341 << "starId of wave = " << starId << "\n"
342 << "waveMin of star = " << starPtr->waveMin
343 << "\n";
344
345 }
346
347 } else {
348
349 // Read the next line
350 for (int j = 0; j < nMonomer; ++j) {
351 in >> temp2[j]; // components of field
352 }
353 in >> waveIn2; // wave of star
354 in >> sizeIn2; // # of wavevectors in star
355 UTIL_CHECK(in.good());
356 ++i;
357
358 // Check that waveIn2 is also in the 1st BZ
359 waveMin =
360 shiftToMinimum(waveIn2, mesh.dimensions(), unitCell);
361 UTIL_CHECK(waveIn2 == waveMin);
362
363 // Identify the star containing waveIn2
364 waveDft = waveIn2;
365 mesh.shift(waveDft);
366 waveId2 = basis.waveId(waveDft);
367 starId2 = basis.wave(waveId2).starId;
368 starPtr2 = &basis.star(starId2);
369 UTIL_CHECK(!(starPtr2->cancel));
370 basisId2 = starPtr2->basisId;
371 UTIL_CHECK(starPtr2->size == sizeIn2);
372 UTIL_CHECK(sizeIn == sizeIn2);
373
374 if (starPtr->invertFlag == 1) {
375
376 // This is a pair of open stars written in the same
377 // order as in this basis. Check preconditions:
378 UTIL_CHECK(starPtr2->invertFlag == -1);
379 UTIL_CHECK(starId2 = starId + 1);
380 UTIL_CHECK(basisId2 = basisId + 1);
381 UTIL_CHECK(starPtr->waveMin == waveIn);
382 UTIL_CHECK(starPtr2->waveMin == waveIn2);
383
384 // Copy components for both stars into fields array
385 for (int j = 0; j < nMonomer; ++j) {
386 fields[j][basisId] = temp[j];
387 fields[j][basisId2] = temp2[j];
388 }
389
390 } else
391 if (starPtr->invertFlag == -1) {
392
393 // This is a pair of open stars written in opposite
394 // order from in this basis. Check preconditions:
395 UTIL_CHECK(starPtr2->invertFlag == 1);
396 UTIL_CHECK(starId == starId2 + 1);
397 UTIL_CHECK(basisId == basisId2 + 1);
398 UTIL_CHECK(waveId == starPtr->beginId);
399
400 // Check that waveIn2 is negation of waveIn
401 IntVec<D> nVec;
402 nVec.negate(waveIn);
403 nVec =
404 shiftToMinimum(nVec, mesh.dimensions(), unitCell);
405 UTIL_CHECK(waveIn2 == nVec);
406
407 /*
408 * Consider two related stars, C and D, that are listed
409 * in the order (C,D) in the basis used in this code (the
410 * reading program), but that were listed in the opposite
411 * order (D,C) in the program that wrote the file (the
412 * writing program). In the basis of the reading program,
413 * star C has star index starId2, while star D has index
414 * starId = starid2 + 1.
415 *
416 * Let f(r) and f^{*}(r) denote the basis functions used
417 * by the reading program for stars C and D, respectively.
418 * Let u(r) and u^{*}(r) denote the corresponding basis
419 * functions used by the writing program for stars C
420 * and D. Let exp(i phi) denote the unit magnitude
421 * coefficient (i.e., phasor) within f(r) of the wave
422 * with wave index waveId2, which was the characteristic
423 * wave for star C in the writing program. The
424 * coefficient of this wave within the basis function
425 * u(r) used by the writing program must instead be real
426 * and positive. This implies that
427 * u(r) = exp(-i phi) f(r).
428 *
429 * Focus on the contribution to the field for a specific
430 * monomer type j. Let a and b denote the desired
431 * coefficients of stars C and D in the reading program,
432 * for which the total contribution of both stars to the
433 * field is:
434 *
435 * (a - ib) f(r) + (a + ib) f^{*}(r)
436 *
437 * Let A = temp[j] and B = temp2[j] denote the
438 * coefficients read from file in order (A,B). Noting
439 * that the stars were listed in the order (D,C) in the
440 * basis used by the writing program, the contribution
441 * of both stars must be (A-iB)u^{*}(r)+(A+iB)u(r), or:
442 *
443 * (A+iB) exp(-i phi)f(r) + (A-iB) exp(i phi) f^{*}(r)
444 *
445 * Comparing coefficients of f^{*}(r), we find that
446 *
447 * (a + ib) = (A - iB) exp(i phi)
448 *
449 * This equality is implemented below, where the
450 * variable "phasor" is set equal to exp(i phi).
451 */
452 phasor = basis.wave(waveId2).coeff;
453 phasor = phasor/std::abs(phasor);
454 for (int j = 0; j < nMonomer; ++j) {
455 coeff = std::complex<double>(temp[j],-temp2[j]);
456 coeff *= phasor;
457 fields[j][basisId2] = real(coeff);
458 fields[j][basisId ] = imag(coeff);
459 }
460
461 // Increment count of number of reversed open pairs
462 ++nReversedPair;
463
464 } else {
465 UTIL_THROW("Invalid starInvert value");
466 }
467
468 } // if (wavePtr->invertFlag == 0) ... else ...
469
470 } // if (waveExists && sizeMatches)
471
472 } // end while (i < nBasis)
473
474 if (nReversedPair > 0) {
475 Log::file() << "\n";
476 Log::file() << nReversedPair << " reversed pairs of open stars"
477 << " detected in readFieldsBasis\n";
478 }
479
480 }
481
482 /*
483 * Read the data section for a single field in basis format.
484 */
485 template <int D>
486 void readBasisData(std::istream& in,
487 DArray<double>& field,
488 UnitCell<D> const& unitCell,
489 Mesh<D> const& mesh,
490 Basis<D> const& basis,
491 int nBasisIn)
492 {
493 UTIL_CHECK(field.isAllocated());
494
495 // Local array of fields containing only one field
496 DArray< DArray<double> > fields;
497 fields.allocate(1);
498 fields[0].allocate(field.capacity());
499
500 // Call overloaded function that takes an array of fields
501 readBasisData(in, fields, unitCell, mesh, basis, nBasisIn);
502
503 // Copy data from local array fields to function parameter field
504 field = fields[0];
505 }
506
507 /*
508 * Write an array of fields in basis format to an output stream.
509 */
510 template <int D>
511 void writeBasisData(std::ostream &out,
512 DArray< DArray<double> > const & fields,
513 Basis<D> const & basis)
514 {
515 // Preconditions, set nMonomer and fieldCapacity
516 int nMonomer = fields.capacity();
517 UTIL_CHECK(nMonomer > 0);
518 int fieldCapacity = fields[0].capacity();
519 for (int j = 0; j < nMonomer; ++j) {
520 UTIL_CHECK(fieldCapacity == fields[j].capacity());
521 }
522 UTIL_CHECK(basis.isInitialized());
523
524 // Write fields
525 int ib = 0;
526 int nStar = basis.nStar();
527 for (int i = 0; i < nStar; ++i) {
528 if (ib >= fieldCapacity) break;
529 if (!basis.star(i).cancel) {
530 for (int j = 0; j < nMonomer; ++j) {
531 out << Dbl(fields[j][ib], 20, 10);
532 }
533 out << " ";
534 for (int j = 0; j < D; ++j) {
535 out << Int(basis.star(i).waveMin[j], 5);
536 }
537 out << Int(basis.star(i).size, 5) << std::endl;
538 ++ib;
539 }
540 }
541
542 }
543
544 /*
545 * Write data section of a single field in basis format.
546 */
547 template <int D>
548 void writeBasisData(std::ostream &out,
549 DArray<double> const & field,
550 Basis<D> const & basis)
551 {
552 UTIL_CHECK(field.isAllocated());
553
554 // Local array of fields containing only one field
555 DArray< DArray<double> > fields;
556 fields.allocate(1);
557 fields[0].allocate(field.capacity());
558 fields[0] = field;
559
560 // Call overloaded function that takes and array of fields
561 writeBasisData(out, fields, basis);
562 }
563
564 // Conversion between basis and k-grid formats
565
566 /*
567 * Convert an array of fields from basis to k-grid format.
568 */
569 template <int D, class ACT>
570 void convertBasisToKGrid(DArray<double> const & in,
571 ACT& out,
572 Basis<D> const& basis,
573 IntVec<D> const& dftDimensions)
574 {
575 UTIL_CHECK(basis.isInitialized());
576
577 // Create Mesh<D> with dimensions of DFT Fourier grid.
578 Mesh<D> dftMesh(dftDimensions);
579
580 typename Basis<D>::Star const* starPtr; // pointer to current star
581 typename Basis<D>::Wave const* wavePtr; // pointer to current wave
582 std::complex<double> component; // coefficient for star
583 std::complex<double> coeff; // coefficient for wave
584 IntVec<D> indices; // dft grid indices of wave
585 int rank; // dft grid rank of wave
586 int is; // star index
587 int ib; // basis index
588 int iw; // wave index
589
590 // Initialize all dft coponents to zero
591 for (rank = 0; rank < dftMesh.size(); ++rank) {
592 assign(out[rank], 0.0, 0.0);
593 }
594
595 // Loop over stars, skipping cancelled stars
596 is = 0;
597 while (is < basis.nStar()) {
598 starPtr = &(basis.star(is));
599
600 if (starPtr->cancel) {
601 ++is;
602 continue;
603 }
604
605 // Set basisId for uncancelled star
606 ib = starPtr->basisId;
607
608 if (starPtr->invertFlag == 0) {
609
610 // Make complex coefficient for star basis function
611 component = std::complex<double>(in[ib], 0.0);
612
613 // Loop over waves in closed star
614 for (iw = starPtr->beginId; iw < starPtr->endId; ++iw) {
615 wavePtr = &basis.wave(iw);
616 if (!wavePtr->implicit) {
617 coeff = component*(wavePtr->coeff);
618 indices = wavePtr->indicesStd;
619 rank = dftMesh.rank(indices);
620 //out[rank][0] = coeff.real();
621 //out[rank][1] = coeff.imag();
622 assign(out[rank], coeff);
623 }
624 }
625 ++is;
626
627 } else
628 if (starPtr->invertFlag == 1) {
629
630 // Loop over waves in first star
631 component = std::complex<double>(in[ib], -in[ib+1]);
632 component /= sqrt(2.0);
633 starPtr = &(basis.star(is));
634 for (iw = starPtr->beginId; iw < starPtr->endId; ++iw) {
635 wavePtr = &basis.wave(iw);
636 if (!(wavePtr->implicit)) {
637 coeff = component*(wavePtr->coeff);
638 indices = wavePtr->indicesStd;
639 rank = dftMesh.rank(indices);
640 //out[rank][0] = coeff.real();
641 //out[rank][1] = coeff.imag();
642 assign(out[rank], coeff);
643 }
644 }
645
646 // Loop over waves in second star
647 starPtr = &(basis.star(is+1));
648 UTIL_CHECK(starPtr->invertFlag == -1);
649 component = std::complex<double>(in[ib], +in[ib+1]);
650 component /= sqrt(2.0);
651 for (iw = starPtr->beginId; iw < starPtr->endId; ++iw) {
652 wavePtr = &basis.wave(iw);
653 if (!(wavePtr->implicit)) {
654 coeff = component*(wavePtr->coeff);
655 indices = wavePtr->indicesStd;
656 rank = dftMesh.rank(indices);
657 //out[rank][0] = coeff.real();
658 //out[rank][1] = coeff.imag();
659 assign(out[rank], coeff);
660 }
661 }
662
663 // Increment is by 2 (two stars were processed)
664 is += 2;
665
666 } else {
667
668 UTIL_THROW("Invalid invertFlag value");
669
670 }
671
672 }
673
674 }
675
676 template <int D, class ACT>
677 void convertKGridToBasis(ACT const& in,
678 DArray<double>& out,
679 Basis<D> const& basis,
680 IntVec<D> const& dftDimensions,
681 bool checkSymmetry,
682 double epsilon)
683 {
684 UTIL_CHECK(basis.isInitialized());
685
686 // Check if input field in k-grid format has specified symmetry
687 if (checkSymmetry) {
688 bool symmetric;
689 symmetric = hasSymmetry(in, basis, dftDimensions, epsilon, true);
690 if (!symmetric) {
691 Log::file() << std::endl
692 << "WARNING: Conversion of asymmetric field to"
693 << "symmetry-adapted basis in Prdc::convertKgridToBasis."
694 << std::endl << std::endl;
695 }
696 }
697
698 // Create Mesh<D> with dimensions of DFT Fourier grid.
699 Mesh<D> dftMesh(dftDimensions);
700
701 typename Basis<D>::Star const* starPtr; // pointer to current star
702 typename Basis<D>::Wave const* wavePtr; // pointer to current wave
703 std::complex<double> component; // coefficient for star
704 int rank; // dft grid rank of wave
705 int is; // star index
706 int ib; // basis index
707 int iw; // wave index
708
709 // Initialize all components to zero
710 for (is = 0; is < basis.nBasis(); ++is) {
711 out[is] = 0.0;
712 }
713
714 // Loop over stars
715 is = 0;
716 while (is < basis.nStar()) {
717 starPtr = &(basis.star(is));
718
719 if (starPtr->cancel) {
720 ++is;
721 continue;
722 }
723
724 // Set basis id for uncancelled star
725 ib = starPtr->basisId;
726
727 if (starPtr->invertFlag == 0) {
728
729 // Choose a wave in the star that is not implicit
730 int beginId = starPtr->beginId;
731 int endId = starPtr->endId;
732 iw = 0;
733 bool isImplicit = true;
734 while (isImplicit) {
735 wavePtr = &basis.wave(beginId + iw);
736 if (!wavePtr->implicit) {
737 isImplicit = false;
738 } else {
739 UTIL_CHECK(beginId + iw < endId - 1 - iw);
740 wavePtr = &basis.wave(endId - 1 - iw);
741 if (!wavePtr->implicit) {
742 isImplicit = false;
743 }
744 }
745 ++iw;
746 }
747 UTIL_CHECK(wavePtr->starId == is);
748
749 // Compute component value
750 rank = dftMesh.rank(wavePtr->indicesStd);
751 //component = std::complex<double>(in[rank][0], in[rank][1]);
752 assign(component, in[rank]);
753 component /= wavePtr->coeff;
754 out[ib] = component.real();
755 ++is;
756
757 } else
758 if (starPtr->invertFlag == 1) {
759
760 // Identify a characteristic wave that is not implicit:
761 // Either the first wave of the 1st star or its inverse
762 // in the second star
763 wavePtr = &basis.wave(starPtr->beginId);
764 UTIL_CHECK(wavePtr->starId == is);
765 if (wavePtr->implicit) {
766 iw = wavePtr->inverseId;
767 starPtr = &(basis.star(is+1));
768 UTIL_CHECK(starPtr->invertFlag == -1);
769 wavePtr = &basis.wave(iw);
770 UTIL_CHECK(!(wavePtr->implicit));
771 UTIL_CHECK(wavePtr->starId == is+1);
772 }
773 rank = dftMesh.rank(wavePtr->indicesStd);
774 //component = std::complex<double>(in[rank][0], in[rank][1]);
775 assign(component, in[rank]);
776 UTIL_CHECK(std::abs(wavePtr->coeff) > 1.0E-8);
777 component /= wavePtr->coeff;
778 component *= sqrt(2.0);
779
780 // Compute basis function coefficient values
781 if (starPtr->invertFlag == 1) {
782 out[ib] = component.real();
783 out[ib+1] = -component.imag();
784 } else {
785 out[ib] = component.real();
786 out[ib+1] = component.imag();
787 }
788
789 is += 2;
790 } else {
791 UTIL_THROW("Invalid invertFlag value");
792 }
793
794 } // loop over star index is
795 }
796
797 /*
798 * Test if an K-grid array has the declared space group symmetry.
799 * Return true if symmetric, false otherwise. Print error values
800 * if verbose == true and hasSymmetry == false.
801 */
802 template <int D, class ACT>
803 bool hasSymmetry(ACT const & in,
804 Basis<D> const& basis,
805 IntVec<D> const& dftDimensions,
806 double epsilon,
807 bool verbose)
808 {
809 UTIL_CHECK(basis.isInitialized());
810
811 typename Basis<D>::Star const* starPtr; // pointer to current star
812 typename Basis<D>::Wave const* wavePtr; // pointer to current wave
813 std::complex<double> waveCoeff; // coefficient from wave
814 std::complex<double> rootCoeff; // coefficient from root
815 std::complex<double> diff; // coefficient difference
816 int is; // star index
817 int iw; // wave index
818 int beginId, endId; // star begin, end ids
819 int rank; // dft grid rank of wave
820
821 double cancelledError(0.0); // max error from cancelled stars
822 double uncancelledError(0.0); // max error from uncancelled stars
823
824 // Create Mesh<D> with dimensions of DFT Fourier grid.
825 Mesh<D> dftMesh(dftDimensions);
826
827 // Loop over all stars
828 for (is = 0; is < basis.nStar(); ++is) {
829 starPtr = &(basis.star(is));
830
831 if (starPtr->cancel) {
832
833 // Check that coefficients are zero for all waves in star
834 beginId = starPtr->beginId;
835 endId = starPtr->endId;
836 for (iw = beginId; iw < endId; ++iw) {
837 wavePtr = &basis.wave(iw);
838 if (!wavePtr->implicit) {
839 rank = dftMesh.rank(wavePtr->indicesStd);
840 //waveCoeff
841 // = std::complex<double>(in[rank][0], in[rank][1]);
842 assign(waveCoeff, in[rank]);
843 if (std::abs(waveCoeff) > cancelledError) {
844 cancelledError = std::abs(waveCoeff);
845 if ((!verbose) && (cancelledError > epsilon)) {
846 return false;
847 }
848 }
849 }
850 }
851
852 } else {
853
854 // Check consistency of coeff values from all waves
855 bool hasRoot = false;
856 beginId = starPtr->beginId;
857 endId = starPtr->endId;
858 for (iw = beginId; iw < endId; ++iw) {
859 wavePtr = &basis.wave(iw);
860 if (!(wavePtr->implicit)) {
861 rank = dftMesh.rank(wavePtr->indicesStd);
862 assign(waveCoeff, in[rank]);
863 waveCoeff /= wavePtr->coeff;
864 if (hasRoot) {
865 diff = waveCoeff - rootCoeff;
866 if (std::abs(diff) > uncancelledError) {
867 uncancelledError = std::abs(diff);
868 if ((!verbose) && (uncancelledError > epsilon)) {
869 return false;
870 }
871 }
872 } else {
873 rootCoeff = waveCoeff;
874 hasRoot = true;
875 }
876 }
877 }
878
879 }
880
881 } // end loop over star index is
882
883 if ((cancelledError < epsilon) && (uncancelledError < epsilon)) {
884 return true;
885 } else if (verbose) {
886 Log::file()
887 << std::endl
888 << "Maximum coefficient of a cancelled star: "
889 << cancelledError << std::endl
890 << "Maximum error of coefficient for an uncancelled star: "
891 << uncancelledError << std::endl;
892 }
893 return false;
894 }
895
896 // Field file manipulations
897
898 template <int D, class AT>
899 void replicateUnitCell(std::ostream &out,
900 DArray<AT> const & fields,
901 IntVec<D> const & meshDimensions,
902 UnitCell<D> const & unitCell,
903 IntVec<D> const & replicas)
904 {
905 // Obtain number of monomer types
906 int nMonomer = fields.capacity();
907 UTIL_CHECK(nMonomer > 0);
908
909 // Compute properties of mesh for replicated fields
910 IntVec<D> repDimensions; // Dimensions
911 int repSize = 1; // Total number of grid points
912 for (int i = 0; i < D; ++i) {
913 UTIL_CHECK(replicas[i] > 0);
914 UTIL_CHECK(meshDimensions[i] > 0);
915 repDimensions[i] = replicas[i] * meshDimensions[i];
916 repSize *= repDimensions[i];
917 }
918
919 // Allocate arrays for replicated fields
920 DArray< DArray<double> > repFields;
921 repFields.allocate(nMonomer);
922 for (int i = 0; i < nMonomer; ++i) {
923 repFields[i].allocate(repSize);
924 }
925
926 // Replicate data
927 Mesh<D> mesh(meshDimensions);
928 Mesh<D> cellMesh(replicas);
929 Mesh<D> repMesh(repDimensions);
930 MeshIterator<D> iter(meshDimensions);
931 MeshIterator<D> cellIter(replicas);
932 IntVec<D> position;
933 IntVec<D> cellPosition;
934 IntVec<D> repPosition;
935 double value;
936 int repRank;
937 for (int i = 0; i < nMonomer; ++i) {
938 for (iter.begin(); !iter.atEnd(); ++iter) {
939 position = iter.position();
940 value = fields[i][iter.rank()];
941 for (cellIter.begin(); !cellIter.atEnd(); ++cellIter) {
942 cellPosition = cellIter.position();
943 for (int j=0; j < D; ++j) {
944 repPosition[j] = position[j]
945 + meshDimensions[j]*cellPosition[j];
946 }
947 repRank = repMesh.rank(repPosition);
948 repFields[i][repRank] = value;
949 }
950 }
951 }
952
953 // Create a replicated UnitCell<D> object
954 // Use function declared in prdc/crystal/replicateUnitCell.h
955 UnitCell<D> cell;
956 replicateUnitCell(replicas, unitCell, cell);
957
958 // Write header
959 int v1 = 1;
960 int v2 = 0;
961 std::string gName = "";
962 Pscf::Prdc::writeFieldHeader(out, v1, v2, cell, gName, nMonomer);
963 writeMeshDimensions(out, repDimensions);
964
965 // Write field data
966 writeRGridData(out, repFields, nMonomer, repDimensions);
967 }
968
969 template <int D, class AT>
970 void
971 expandRGridDimension(std::ostream &out,
972 DArray<AT> const & fields,
973 IntVec<D> const & meshDimensions,
974 UnitCell<D> const & unitCell,
975 int d,
976 DArray<int> newGridDimensions)
977 {
978 UTIL_THROW("Unimplemented base template");
979 }
980
981 template <class AT>
982 void
983 expandRGridDimension(std::ostream &out,
984 DArray<AT> const & fields,
985 IntVec<1> const & meshDimensions,
986 UnitCell<1> const & unitCell,
987 int d,
988 DArray<int> newGridDimensions)
989
990 {
991 // Check validity of expanded dimension d and newGridDimensions
992 UTIL_CHECK(d > 1);
993 UTIL_CHECK(d <= 3);
994 UTIL_CHECK(newGridDimensions.capacity() == (d - 1));
995
996
997 // Obtain number of monomer types
998 int nMonomer = fields.capacity();
999 UTIL_CHECK(nMonomer > 0);
1000
1001 // Set up necessary objects
1002 FSArray<double, 6> cellParameters;
1003 cellParameters.append(unitCell.parameter(0));
1004 int v1 = 1;
1005 int v2 = 0;
1006 std::string gName = "";
1007
1008 if (d == 2) {
1009
1010 // 1D expanded to 2D
1011
1012 // Set dimensions
1013 IntVec<2> dimensions;
1014 dimensions[0] = meshDimensions[0];
1015 dimensions[1] = newGridDimensions[0];
1016
1017 // Assign unit cell
1018 UnitCell<2> cell;
1019 if (dimensions[0] == dimensions[1]) {
1020 cell.set(UnitCell<2>::Square, cellParameters);
1021 } else {
1022 cellParameters.append((double)dimensions[1]/dimensions[0]
1023 * cellParameters[0]);
1024 cell.set(UnitCell<2>::Rectangular, cellParameters);
1025 }
1026
1027 // Write Header
1028 Pscf::Prdc::writeFieldHeader(out, v1, v2, cell, gName, nMonomer);
1029 out << "mesh " << std::endl
1030 << " " << dimensions << std::endl;
1031
1032 } else if (d == 3) {
1033
1034 // 1D expanded to 3D
1035
1036 // Set dimensions
1037 IntVec<3> dimensions;
1038 dimensions[0] = meshDimensions[0];
1039 dimensions[1] = newGridDimensions[0];
1040 dimensions[2] = newGridDimensions[1];
1041
1042 // Assign unit cell
1043 UnitCell<3> cell;
1044 if (dimensions[2] == dimensions[1]) {
1045 if (dimensions[1] == dimensions[0]) {
1046 cell.set(UnitCell<3>::Cubic, cellParameters);
1047 } else {
1048 cellParameters.append((double)dimensions[1]/dimensions[0]
1049 * cellParameters[0]);
1050 cellParameters.append((double)dimensions[2]/dimensions[0]
1051 * cellParameters[0]);
1052 cell.set(UnitCell<3>::Orthorhombic, cellParameters);
1053 }
1054 } else {
1055 if (dimensions[1] == dimensions[0]) {
1056 cellParameters.append((double)dimensions[2]/dimensions[0]
1057 * cellParameters[0]);
1058 cell.set(UnitCell<3>::Tetragonal, cellParameters);
1059 } else {
1060 cellParameters.append((double)dimensions[1]/dimensions[0]
1061 * cellParameters[0]);
1062 cellParameters.append((double)dimensions[2]/dimensions[0]
1063 * cellParameters[0]);
1064 cell.set(UnitCell<3>::Orthorhombic, cellParameters);
1065 }
1066 }
1067
1068 // Write Header
1069 Pscf::Prdc::writeFieldHeader(out, v1, v2, cell, gName, nMonomer);
1070 out << "mesh " << std::endl
1071 << " " << dimensions << std::endl;
1072
1073 } else {
1074
1075 UTIL_THROW("Invalid d value");
1076
1077 }
1078
1079 // Write expanded fields
1080 int nReplica = newGridDimensions[0];
1081 if (d == 3) {
1082 nReplica *= newGridDimensions[1];
1083 }
1084 MeshIteratorFortran<1> iter(meshDimensions);
1085 int rank;
1086 for (int i = 0; i < nReplica; ++i) {
1087 for (iter.begin(); !iter.atEnd(); ++iter) {
1088 rank = iter.rank();
1089 for (int j = 0; j < nMonomer; ++j) {
1090 out << " " << Dbl(fields[j][rank], 21, 13);
1091 }
1092 out << std::endl;
1093 }
1094 }
1095
1096 }
1097
1098 template <class AT>
1099 void expandRGridDimension(std::ostream &out,
1100 DArray<AT> const & fields,
1101 IntVec<2> const & meshDimensions,
1102 UnitCell<2> const & unitCell,
1103 int d,
1104 DArray<int> newGridDimensions)
1105 {
1106 // 2D expanded to 3D
1107
1108 // Check validity of expanded dimension d and newGridDimensions
1109 UTIL_CHECK(d == 3);
1110 UTIL_CHECK(newGridDimensions.capacity() == 1);
1111
1112 // Obtain number of monomer types
1113 int nMonomer = fields.capacity();
1114 UTIL_CHECK(nMonomer > 0);
1115
1116 // Set dimensions of mesh for replicated fields
1117 IntVec<3> dimensions;
1118 dimensions[0] = meshDimensions[0];
1119 dimensions[1] = meshDimensions[1];
1120 dimensions[2] = newGridDimensions[0];
1121
1122 // Set unit cell for replicated fields
1123 UnitCell<3> cell;
1124 FSArray<double, 6> cellParameters;
1125 cellParameters.append(unitCell.parameter(0));
1126 if (unitCell.lattice() == UnitCell<2>::Square) {
1127 if (newGridDimensions[0] == meshDimensions[0]){
1128 cell.set(UnitCell<3>::Cubic, cellParameters);
1129 } else {
1130 cellParameters.append((double)dimensions[2]/dimensions[0]
1131 * cellParameters[0]);
1132 cell.set(UnitCell<3>::Tetragonal, cellParameters);
1133 }
1134 } else if (unitCell.lattice() == UnitCell<2>::Rectangular) {
1135 cellParameters.append(unitCell.parameter(1));
1136 cellParameters.append((double)dimensions[2]/dimensions[0]
1137 * cellParameters[0]);
1138 cell.set(UnitCell<3>::Orthorhombic, cellParameters);
1139 } else if (unitCell.lattice() == UnitCell<2>::Hexagonal){
1140 cellParameters.append((double)dimensions[2]/dimensions[0]
1141 * cellParameters[0]);
1142 cell.set(UnitCell<3>::Hexagonal, cellParameters);
1143 } else if (unitCell.lattice() == UnitCell<2>::Rhombic) {
1144 cellParameters.append(unitCell.parameter(0));
1145 cellParameters.append((double)dimensions[2]/dimensions[0]
1146 * cellParameters[0]);
1147 cellParameters.append(Constants::Pi / 2);
1148 cellParameters.append(0.0);
1149 cellParameters.append(unitCell.parameter(1));
1150 cell.set(UnitCell<3>::Triclinic, cellParameters);
1151 } else if (unitCell.lattice() == UnitCell<2>::Oblique) {
1152 cellParameters.append(unitCell.parameter(1));
1153 cellParameters.append((double)dimensions[2]/dimensions[0]
1154 * cellParameters[0]);
1155 cellParameters.append(Constants::Pi / 2);
1156 cellParameters.append(0.0);
1157 cellParameters.append(unitCell.parameter(2));
1158 cell.set(UnitCell<3>::Triclinic, cellParameters);
1159 } else {
1160 UTIL_THROW("Unrecognized 2D lattice system.");
1161 }
1162
1163 // Write Header
1164 int v1 = 1;
1165 int v2 = 0;
1166 std::string gName = "";
1167 Pscf::Prdc::writeFieldHeader(out, v1, v2, cell, gName, nMonomer);
1168 out << "mesh " << std::endl
1169 << " " << dimensions << std::endl;
1170
1171 // Write expanded fields
1172 int nReplica = newGridDimensions[0];
1173 MeshIteratorFortran<2> iter(meshDimensions);
1174 int rank;
1175 for (int i = 0; i < nReplica; ++i) {
1176 for (iter.begin(); !iter.atEnd(); ++iter) {
1177 rank = iter.rank();
1178 for (int j = 0; j < nMonomer; ++j) {
1179 out << " " << Dbl(fields[j][rank], 21, 13);
1180 }
1181 out << std::endl;
1182 }
1183 }
1184
1185 }
1186
1187 template <class AT>
1188 void expandRGridDimension(std::ostream &out,
1189 DArray<AT> const & fields,
1190 IntVec<3> const & meshDimensions,
1191 UnitCell<3> const & unitCell,
1192 int d,
1193 DArray<int> newGridDimensions)
1194
1195 { UTIL_THROW("expandRGridDimension is invalid when D = 3."); }
1196
1197} // namespace Prdc
1198} // namespace Pscf
1199#endif
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 in "Fortran" order.
int rank() const
Return the scalar array rank of the associated grid point.
void begin()
Initialize the iterator to the first grid point.
bool atEnd() const
Is this the end (i.e., past the last grid point)?
Iterator over points in a Mesh<D>.
int rank() const
Get the rank of current element.
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)?
IntVec< D > position() const
Get current position in the grid, as integer vector.
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 rank(IntVec< D > const &position) const
Get the rank of a grid point with specified position.
Definition Mesh.tpp:72
int shift(int &coordinate, int i) const
Shift a periodic coordinate into range.
Definition Mesh.tpp:131
A list of wavevectors that are related by space-group symmetries.
Definition Basis.h:483
int basisId
Index of basis function associated with this star.
Definition Basis.h:564
IntVec< D > waveMin
Integer indices indicesMin of a characteristic wave of this star.
Definition Basis.h:545
int invertFlag
Index for inversion symmetry of star.
Definition Basis.h:532
int size
Number of wavevectors in this star.
Definition Basis.h:490
int beginId
Wave index of first wavevector in star.
Definition Basis.h:495
bool cancel
Is this star cancelled, i.e., associated with a zero function?
Definition Basis.h:579
Wavevector used to construct a basis function.
Definition Basis.h:392
std::complex< double > coeff
Coefficient of wave within the associated star basis function.
Definition Basis.h:399
int starId
Index of the star that contains this wavevector.
Definition Basis.h:428
Symmetry-adapted Fourier basis for pseudo-spectral SCFT.
Definition Basis.h:384
Wave const & wave(int id) const
Get a specific Wave, access by integer index.
Definition Basis.h:877
int waveId(IntVec< D > vector) const
Get the integer index of a wave, as required by wave(int id).
Definition Basis.h:891
int nStar() const
Total number of stars.
Definition Basis.h:872
bool isInitialized() const
Returns true iff this basis is fully initialized.
Definition Basis.h:899
Star const & star(int id) const
Get a Star, accessed by integer star index.
Definition Basis.h:882
Base template for UnitCell<D> classes, D=1, 2 or 3.
Definition UnitCell.h:56
Vec< D, T > & negate(const Vec< D, T > &v)
Return negative of vector v.
Definition Vec.h:520
int capacity() const
Return allocated size.
Definition Array.h:144
static const double Pi
Trigonometric constant Pi.
Definition Constants.h:35
Dynamically allocatable contiguous array template.
Definition DArray.h:32
void allocate(int capacity)
Allocate the underlying C array.
Definition DArray.h:269
bool isAllocated() const
Return true if this DArray has been allocated, false otherwise.
Definition DArray.h:151
Wrapper for a double precision number, for formatted ostream output.
Definition Dbl.h:40
A fixed capacity (static) contiguous array with a variable logical size.
Definition FSArray.h:38
void append(Data const &data)
Append data to the end of the array.
Definition FSArray.h:258
Wrapper for an int, for formatted ostream output.
Definition Int.h:37
static std::ostream & file()
Get log ostream by reference.
Definition Log.cpp:59
#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
#define UTIL_ASSERT(condition)
Assertion macro suitable for debugging serial or parallel code.
Definition global.h:75
void replicateUnitCell(IntVec< 1 > const &replicas, UnitCell< 1 > const &cellIn, UnitCell< 1 > &cellOut)
Create a replicated UnitCell<1>.
void writeFieldHeader(std::ostream &out, int ver1, int ver2, UnitCell< D > const &cell, std::string const &groupName, int nMonomer)
Write common part of field header (fortran PSCF format).
void writeKGridData(std::ostream &out, DArray< AT > const &fields, int nMonomer, IntVec< D > const &dftDimensions)
Write data for array of k-grid fields, with no header section.
bool hasSymmetry(AT const &in, Basis< D > const &basis, IntVec< D > const &dftDimensions, double epsilon=1.0e-8, bool verbose=true)
Check if a k-grid field has the declared space group symmetry.
void readKGridData(std::istream &in, DArray< AT > &fields, int nMonomer, IntVec< D > const &dftDimensions)
Read data for array of k-grid fields, with no header section.
void convertBasisToKGrid(DArray< double > const &components, AT &dft, Basis< D > const &basis, IntVec< D > const &dftDimensions)
Convert a real field from symmetrized basis to Fourier grid.
void writeMeshDimensions(std::ostream &out, IntVec< D > const &meshDimensions)
Write mesh dimensions to a field file header.
void expandRGridDimension(std::ostream &out, DArray< AT > const &fields, IntVec< D > const &meshDimensions, UnitCell< D > const &unitCell, int d, DArray< int > newGridDimensions)
Expand the dimensionality of space from D to d.
Definition rFieldIo.tpp:971
void writeRGridData(std::ostream &out, DArray< AT > const &fields, int nMonomer, IntVec< D > const &dimensions)
Write data for array of r-grid fields, with no header section.
Definition rFieldIo.tpp:78
void convertKGridToBasis(AT const &in, DArray< double > &out, Basis< D > const &basis, IntVec< D > const &dftDimensions, bool checkSymmetry=true, double epsilon=1.0e-8)
Convert a real field from Fourier grid to symmetrized basis.
void readRGridData(std::istream &in, DArray< AT > &fields, int nMonomer, IntVec< D > const &dimensions)
Read data for array of r-grid fields, with no header section.
Definition rFieldIo.tpp:38
void writeBasisData(std::ostream &out, DArray< DArray< double > > const &fields, Basis< D > const &basis)
Write data section of an array of fields in basis format.
Definition rFieldIo.tpp:511
void readBasisData(std::istream &in, DArray< DArray< double > > &fields, UnitCell< D > const &unitCell, Mesh< D > const &mesh, Basis< D > const &basis, int nStarIn)
Read an array of fields in basis format, without a header.
Definition rFieldIo.tpp:233
double imag(fftw_complex const &a)
Return the imaginary part of an fftw_complex number.
Definition cpu/complex.h:49
void assign(fftw_complex &z, double const &a, double const &b)
Create an fftw_complex from real and imaginary parts, z = a + ib.
double real(fftw_complex const &a)
Return the real part of an fftw_complex number.
Definition cpu/complex.h:38
Periodic fields and crystallography.
Definition complex.cpp:11
PSCF package top-level namespace.