1#ifndef PRDC_FIELD_IO_UTIL_TPP
2#define PRDC_FIELD_IO_UTIL_TPP
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>
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>
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>
37 template <
int D,
class AT>
51 for (
int k = 0; k < nMonomer; ++k) {
53 assign(fields[k][rank], value);
60 template <
int D,
class AT>
72 assign(field[rank], value);
77 template <
int D,
class AT>
90 for (j = 0; j < nMonomer; ++j) {
91 out <<
" " <<
Dbl(fields[j][rank], 21, 13);
98 template <
int D,
class AT>
107 out <<
" " <<
Dbl(field[rank], 21, 13);
114 template <
int D,
class ACT>
122 typename ACT::RealType x, y;
124 int rank, i, j, idum;
126 for (iter.begin(); !iter.atEnd(); ++iter) {
131 for (j = 0; j < nMonomer; ++j) {
134 assign(fields[j][rank], x, y);
142 template <
int D,
class ACT>
150 typename ACT::RealType x, y;
154 for (iter.begin(); !iter.atEnd(); ++iter) {
162 assign(field[rank], x, y);
168 template <
int D,
class ACT>
180 typename ACT::RealType x, y;
184 for (iter.begin(); !iter.atEnd(); ++iter) {
188 for (
int j = 0; j < nMonomer; ++j) {
189 x =
real(fields[j][rank]);
190 y =
imag(fields[j][rank]);
201 template <
int D,
class ACT>
209 typename ACT::RealType x, y;
213 for (iter.begin(); !iter.atEnd(); ++iter) {
216 x =
real(field[rank]);
217 y =
imag(field[rank]);
247 int fieldCapacity = fields[0].
capacity();
249 for (
int i = 0; i < nMonomer; ++i) {
250 UTIL_CHECK(fields[i].capacity() == fieldCapacity);
251 for (
int j = 0; j < fieldCapacity; ++j) {
257 int nBasis = nBasisIn;
258 if (fieldCapacity < nBasisIn) {
259 nBasis = fieldCapacity;
272 int basisId, basisId2;
275 std::complex<double> coeff, phasor;
277 int nReversedPair = 0;
278 bool waveExists, sizeMatches;
285 for (
int j = 0; j < nMonomer; ++j) {
297 waveMin = shiftToMinimum(waveIn, mesh.
dimensions(), unitCell);
298 waveExists = (waveIn == waveMin);
305 waveId = basis.
waveId(waveDft);
307 starPtr = &basis.
star(starId);
311 if (starPtr->
size == sizeIn) {
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
325 if (waveExists && sizeMatches) {
329 if (starPtr->
waveMin == waveIn) {
332 for (
int j = 0; j < nMonomer; ++j) {
333 fields[j][basisId] = temp[j];
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
350 for (
int j = 0; j < nMonomer; ++j) {
360 shiftToMinimum(waveIn2, mesh.
dimensions(), unitCell);
366 waveId2 = basis.
waveId(waveDft);
368 starPtr2 = &basis.
star(starId2);
385 for (
int j = 0; j < nMonomer; ++j) {
386 fields[j][basisId] = temp[j];
387 fields[j][basisId2] = temp2[j];
404 shiftToMinimum(nVec, mesh.
dimensions(), unitCell);
453 phasor = phasor/std::abs(phasor);
454 for (
int j = 0; j < nMonomer; ++j) {
455 coeff = std::complex<double>(temp[j],-temp2[j]);
457 fields[j][basisId2] =
real(coeff);
458 fields[j][basisId ] =
imag(coeff);
474 if (nReversedPair > 0) {
476 Log::file() << nReversedPair <<
" reversed pairs of open stars"
477 <<
" detected in readFieldsBasis\n";
518 int fieldCapacity = fields[0].
capacity();
519 for (
int j = 0; j < nMonomer; ++j) {
520 UTIL_CHECK(fieldCapacity == fields[j].capacity());
526 int nStar = basis.
nStar();
527 for (
int i = 0; i < nStar; ++i) {
528 if (ib >= fieldCapacity)
break;
530 for (
int j = 0; j < nMonomer; ++j) {
531 out <<
Dbl(fields[j][ib], 20, 10);
534 for (
int j = 0; j < D; ++j) {
569 template <
int D,
class ACT>
572 Basis<D>
const& basis,
578 Mesh<D> dftMesh(dftDimensions);
580 typename Basis<D>::Star
const* starPtr;
581 typename Basis<D>::Wave
const* wavePtr;
582 std::complex<double> component;
583 std::complex<double> coeff;
591 for (rank = 0; rank < dftMesh.size(); ++rank) {
592 assign(out[rank], 0.0, 0.0);
597 while (is < basis.nStar()) {
598 starPtr = &(basis.star(is));
600 if (starPtr->cancel) {
606 ib = starPtr->basisId;
608 if (starPtr->invertFlag == 0) {
611 component = std::complex<double>(in[ib], 0.0);
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);
628 if (starPtr->invertFlag == 1) {
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);
647 starPtr = &(basis.star(is+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);
676 template <
int D,
class ACT>
689 symmetric =
hasSymmetry(in, basis, dftDimensions, epsilon,
true);
692 <<
"WARNING: Conversion of asymmetric field to"
693 <<
"symmetry-adapted basis in Prdc::convertKgridToBasis."
694 << std::endl << std::endl;
699 Mesh<D> dftMesh(dftDimensions);
703 std::complex<double> component;
710 for (is = 0; is < basis.nBasis(); ++is) {
716 while (is < basis.nStar()) {
717 starPtr = &(basis.star(is));
719 if (starPtr->cancel) {
725 ib = starPtr->basisId;
727 if (starPtr->invertFlag == 0) {
730 int beginId = starPtr->beginId;
731 int endId = starPtr->endId;
733 bool isImplicit =
true;
735 wavePtr = &basis.wave(beginId + iw);
736 if (!wavePtr->implicit) {
740 wavePtr = &basis.wave(endId - 1 - iw);
741 if (!wavePtr->implicit) {
750 rank = dftMesh.rank(wavePtr->indicesStd);
752 assign(component, in[rank]);
753 component /= wavePtr->coeff;
754 out[ib] = component.real();
758 if (starPtr->invertFlag == 1) {
763 wavePtr = &basis.wave(starPtr->beginId);
765 if (wavePtr->implicit) {
766 iw = wavePtr->inverseId;
767 starPtr = &(basis.star(is+1));
769 wavePtr = &basis.wave(iw);
773 rank = dftMesh.rank(wavePtr->indicesStd);
775 assign(component, in[rank]);
776 UTIL_CHECK(std::abs(wavePtr->coeff) > 1.0E-8);
777 component /= wavePtr->coeff;
778 component *= sqrt(2.0);
781 if (starPtr->invertFlag == 1) {
782 out[ib] = component.real();
783 out[ib+1] = -component.imag();
785 out[ib] = component.real();
786 out[ib+1] = component.imag();
802 template <
int D,
class ACT>
813 std::complex<double> waveCoeff;
814 std::complex<double> rootCoeff;
815 std::complex<double> diff;
821 double cancelledError(0.0);
822 double uncancelledError(0.0);
825 Mesh<D> dftMesh(dftDimensions);
828 for (is = 0; is < basis.nStar(); ++is) {
829 starPtr = &(basis.star(is));
831 if (starPtr->cancel) {
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);
842 assign(waveCoeff, in[rank]);
843 if (std::abs(waveCoeff) > cancelledError) {
844 cancelledError = std::abs(waveCoeff);
845 if ((!verbose) && (cancelledError > epsilon)) {
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;
865 diff = waveCoeff - rootCoeff;
866 if (std::abs(diff) > uncancelledError) {
867 uncancelledError = std::abs(diff);
868 if ((!verbose) && (uncancelledError > epsilon)) {
873 rootCoeff = waveCoeff;
883 if ((cancelledError < epsilon) && (uncancelledError < epsilon)) {
885 }
else if (verbose) {
888 <<
"Maximum coefficient of a cancelled star: "
889 << cancelledError << std::endl
890 <<
"Maximum error of coefficient for an uncancelled star: "
891 << uncancelledError << std::endl;
898 template <
int D,
class AT>
912 for (
int i = 0; i < D; ++i) {
915 repDimensions[i] = replicas[i] * meshDimensions[i];
916 repSize *= repDimensions[i];
922 for (
int i = 0; i < nMonomer; ++i) {
929 Mesh<D> repMesh(repDimensions);
937 for (
int i = 0; i < nMonomer; ++i) {
940 value = fields[i][iter.
rank()];
941 for (cellIter.
begin(); !cellIter.
atEnd(); ++cellIter) {
943 for (
int j=0; j < D; ++j) {
944 repPosition[j] = position[j]
945 + meshDimensions[j]*cellPosition[j];
947 repRank = repMesh.
rank(repPosition);
948 repFields[i][repRank] = value;
961 std::string gName =
"";
969 template <
int D,
class AT>
986 UnitCell<1>
const & unitCell,
1003 cellParameters.
append(unitCell.parameter(0));
1006 std::string gName =
"";
1014 dimensions[0] = meshDimensions[0];
1015 dimensions[1] = newGridDimensions[0];
1019 if (dimensions[0] == dimensions[1]) {
1020 cell.set(UnitCell<2>::Square, cellParameters);
1022 cellParameters.
append((
double)dimensions[1]/dimensions[0]
1023 * cellParameters[0]);
1024 cell.set(UnitCell<2>::Rectangular, cellParameters);
1029 out <<
"mesh " << std::endl
1030 <<
" " << dimensions << std::endl;
1032 }
else if (d == 3) {
1038 dimensions[0] = meshDimensions[0];
1039 dimensions[1] = newGridDimensions[0];
1040 dimensions[2] = newGridDimensions[1];
1044 if (dimensions[2] == dimensions[1]) {
1045 if (dimensions[1] == dimensions[0]) {
1048 cellParameters.
append((
double)dimensions[1]/dimensions[0]
1049 * cellParameters[0]);
1050 cellParameters.append((
double)dimensions[2]/dimensions[0]
1051 * cellParameters[0]);
1055 if (dimensions[1] == dimensions[0]) {
1056 cellParameters.append((
double)dimensions[2]/dimensions[0]
1057 * cellParameters[0]);
1060 cellParameters.append((
double)dimensions[1]/dimensions[0]
1061 * cellParameters[0]);
1062 cellParameters.append((
double)dimensions[2]/dimensions[0]
1063 * cellParameters[0]);
1070 out <<
"mesh " << std::endl
1071 <<
" " << dimensions << std::endl;
1080 int nReplica = newGridDimensions[0];
1082 nReplica *= newGridDimensions[1];
1086 for (
int i = 0; i < nReplica; ++i) {
1087 for (iter.begin(); !iter.atEnd(); ++iter) {
1089 for (
int j = 0; j < nMonomer; ++j) {
1090 out <<
" " <<
Dbl(fields[j][rank], 21, 13);
1118 dimensions[0] = meshDimensions[0];
1119 dimensions[1] = meshDimensions[1];
1120 dimensions[2] = newGridDimensions[0];
1125 cellParameters.
append(unitCell.parameter(0));
1127 if (newGridDimensions[0] == meshDimensions[0]){
1130 cellParameters.
append((
double)dimensions[2]/dimensions[0]
1131 * cellParameters[0]);
1135 cellParameters.
append(unitCell.parameter(1));
1136 cellParameters.
append((
double)dimensions[2]/dimensions[0]
1137 * cellParameters[0]);
1140 cellParameters.append((
double)dimensions[2]/dimensions[0]
1141 * cellParameters[0]);
1144 cellParameters.append(unitCell.parameter(0));
1145 cellParameters.append((
double)dimensions[2]/dimensions[0]
1146 * cellParameters[0]);
1148 cellParameters.append(0.0);
1149 cellParameters.append(unitCell.parameter(1));
1152 cellParameters.append(unitCell.parameter(1));
1153 cellParameters.append((
double)dimensions[2]/dimensions[0]
1154 * cellParameters[0]);
1156 cellParameters.append(0.0);
1157 cellParameters.append(unitCell.parameter(2));
1160 UTIL_THROW(
"Unrecognized 2D lattice system.");
1166 std::string gName =
"";
1168 out <<
"mesh " << std::endl
1169 <<
" " << dimensions << std::endl;
1172 int nReplica = newGridDimensions[0];
1175 for (
int i = 0; i < nReplica; ++i) {
1176 for (iter.begin(); !iter.atEnd(); ++iter) {
1178 for (
int j = 0; j < nMonomer; ++j) {
1179 out <<
" " <<
Dbl(fields[j][rank], 21, 13);
1195 {
UTIL_THROW(
"expandRGridDimension is invalid when D = 3."); }
An IntVec<D, T> is a D-component vector of elements of integer type T.
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.
IntVec< D > dimensions() const
Get an IntVec<D> of the grid dimensions.
int rank(IntVec< D > const &position) const
Get the rank of a grid point with specified position.
int shift(int &coordinate, int i) const
Shift a periodic coordinate into range.
A list of wavevectors that are related by space-group symmetries.
int basisId
Index of basis function associated with this star.
IntVec< D > waveMin
Integer indices indicesMin of a characteristic wave of this star.
int invertFlag
Index for inversion symmetry of star.
int size
Number of wavevectors in this star.
int beginId
Wave index of first wavevector in star.
bool cancel
Is this star cancelled, i.e., associated with a zero function?
Wavevector used to construct a basis function.
std::complex< double > coeff
Coefficient of wave within the associated star basis function.
int starId
Index of the star that contains this wavevector.
Symmetry-adapted Fourier basis for pseudo-spectral SCFT.
Wave const & wave(int id) const
Get a specific Wave, access by integer index.
int waveId(IntVec< D > vector) const
Get the integer index of a wave, as required by wave(int id).
int nStar() const
Total number of stars.
bool isInitialized() const
Returns true iff this basis is fully initialized.
Star const & star(int id) const
Get a Star, accessed by integer star index.
Base template for UnitCell<D> classes, D=1, 2 or 3.
Vec< D, T > & negate(const Vec< D, T > &v)
Return negative of vector v.
int capacity() const
Return allocated size.
static const double Pi
Trigonometric constant Pi.
Dynamically allocatable contiguous array template.
void allocate(int capacity)
Allocate the underlying C array.
bool isAllocated() const
Return true if this DArray has been allocated, false otherwise.
Wrapper for a double precision number, for formatted ostream output.
A fixed capacity (static) contiguous array with a variable logical size.
void append(Data const &data)
Append data to the end of the array.
Wrapper for an int, for formatted ostream output.
static std::ostream & file()
Get log ostream by reference.
#define UTIL_CHECK(condition)
Assertion macro suitable for serial or parallel production code.
#define UTIL_THROW(msg)
Macro for throwing an Exception, reporting function, file and line number.
#define UTIL_ASSERT(condition)
Assertion macro suitable for debugging serial or parallel code.
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.
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.
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.
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.
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.
double imag(fftw_complex const &a)
Return the imaginary part of an fftw_complex number.
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.
Periodic fields and crystallography.
PSCF package top-level namespace.