diff --git a/.gitignore b/.gitignore index 041cbb151..264981220 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,4 @@ cmake-build-debug/ *.mexa* *.txt *.mrc - +**/.vscode/ diff --git a/Qt/main.cpp b/Qt/main.cpp index 669a941fb..c4af93610 100644 --- a/Qt/main.cpp +++ b/Qt/main.cpp @@ -23,6 +23,6 @@ int main(int argc, char *argv[]) a.setFont(font); PRISMMainWindow w; w.show(); - w.setGeometry(100,100,850,700); + w.setGeometry(100, 100, 850, 700); return a.exec(); } diff --git a/Qt/prism_colormapper.cpp b/Qt/prism_colormapper.cpp index 42276d8ea..c6381343c 100644 --- a/Qt/prism_colormapper.cpp +++ b/Qt/prism_colormapper.cpp @@ -3,65 +3,72 @@ #include #include #include -namespace Prismatic{ +namespace Prismatic +{ - void Colormapper::setColormap(const Colormap& cmap){this->colormap = cmap;} +void Colormapper::setColormap(const Colormap &cmap) { this->colormap = cmap; } - QRgb Colormapper::getColor(const double value, const double contrastMin, const double contrastMax) { +QRgb Colormapper::getColor(const double value, const double contrastMin, const double contrastMax) +{ - Color color_fraction; - Color c; -// std::cout << "value " << value << std::endl; -// std::cout << "contrastMax " << contrastMax << std::endl; -// std::cout << "contrastMin " << contrastMin << std::endl; - if (value <= contrastMin){ - // use first color in colormap - color_fraction = this->colormap[0]; + Color color_fraction; + Color c; + // std::cout << "value " << value << std::endl; + // std::cout << "contrastMax " << contrastMax << std::endl; + // std::cout << "contrastMin " << contrastMin << std::endl; + if (value <= contrastMin) + { + // use first color in colormap + color_fraction = this->colormap[0]; - // convert to uint8 - c = Color{(unsigned char)(color_fraction.r * 255.0), - (unsigned char)(color_fraction.g * 255.0), - (unsigned char)(color_fraction.b * 255.0)}; - } else if (value >= contrastMax){ - // use last color in colormap - color_fraction = this->colormap[this->colormap.size() - 1]; + // convert to uint8 + c = Color{(unsigned char)(color_fraction.r * 255.0), + (unsigned char)(color_fraction.g * 255.0), + (unsigned char)(color_fraction.b * 255.0)}; + } + else if (value >= contrastMax) + { + // use last color in colormap + color_fraction = this->colormap[this->colormap.size() - 1]; - // convert to uint8 - c = Color{(unsigned char)(color_fraction.r * 255.0), - (unsigned char)(color_fraction.g * 255.0), - (unsigned char)(color_fraction.b * 255.0)}; - } else{ - // linearly interpolate the color value based upon the two nearest positional neighbors + // convert to uint8 + c = Color{(unsigned char)(color_fraction.r * 255.0), + (unsigned char)(color_fraction.g * 255.0), + (unsigned char)(color_fraction.b * 255.0)}; + } + else + { + // linearly interpolate the color value based upon the two nearest positional neighbors - // determine what fraction of max value the value is - float fractional_pos = (value - contrastMin) / (contrastMax - contrastMin); + // determine what fraction of max value the value is + float fractional_pos = (value - contrastMin) / (contrastMax - contrastMin); - // map this fraction to a color (or decimal index between two colors) - float colorIndex = fractional_pos * (this->colormap.size() - 1); + // map this fraction to a color (or decimal index between two colors) + float colorIndex = fractional_pos * (this->colormap.size() - 1); - // determine the two relevant colors - int lowerColorIndex = (int)std::floor(colorIndex); - int upperColorIndex = (int)std::ceil(colorIndex); - Color cLow, cHigh; - cLow = this->colormap[lowerColorIndex]; - cHigh = this->colormap[upperColorIndex]; + // determine the two relevant colors + int lowerColorIndex = (int)std::floor(colorIndex); + int upperColorIndex = (int)std::ceil(colorIndex); + Color cLow, cHigh; + cLow = this->colormap[lowerColorIndex]; + cHigh = this->colormap[upperColorIndex]; - // determine the weights - double weight1, weight2; - weight2 = colorIndex - (float)lowerColorIndex; - weight1 = 1-weight2; -// std::cout << "weight1 = " << weight1 << std::endl; -// std::cout << "weight2 = " << weight2 << std::endl; -// std::cout << "lowerColorIndex = " << lowerColorIndex << std::endl; -// std::cout << "upperColorIndex = " << upperColorIndex << std::endl; + // determine the weights + double weight1, weight2; + weight2 = colorIndex - (float)lowerColorIndex; + weight1 = 1 - weight2; + // std::cout << "weight1 = " << weight1 << std::endl; + // std::cout << "weight2 = " << weight2 << std::endl; + // std::cout << "lowerColorIndex = " << lowerColorIndex << std::endl; + // std::cout << "upperColorIndex = " << upperColorIndex << std::endl; - // make the interpolated color - c = Color{(unsigned char)( (weight1*cLow.r + weight2*cHigh.r) * 255.0), - (unsigned char)( (weight1*cLow.g + weight2*cHigh.g) * 255.0), - (unsigned char)( (weight1*cLow.b + weight2*cHigh.b) * 255.0)}; - } - return qRgba(c.r, c.g, c.b, 255); -// return qRgba(0, 0, 0, 255); - } - + // make the interpolated color + c = Color{(unsigned char)((weight1 * cLow.r + weight2 * cHigh.r) * 255.0), + (unsigned char)((weight1 * cLow.g + weight2 * cHigh.g) * 255.0), + (unsigned char)((weight1 * cLow.b + weight2 * cHigh.b) * 255.0)}; + } + return qRgba(c.r, c.g, c.b, 255); + // return qRgba(0, 0, 0, 255); } + +} // namespace Prismatic diff --git a/Qt/prism_progressbar.cpp b/Qt/prism_progressbar.cpp index 068bedb79..b03f458bc 100644 --- a/Qt/prism_progressbar.cpp +++ b/Qt/prism_progressbar.cpp @@ -14,106 +14,114 @@ #include "prism_progressbar.h" #include "ui_prism_progressbar.h" #include -prism_progressbar::prism_progressbar(PRISMMainWindow *_parent) : - parent(_parent), - ui(new Ui::prism_progressbar), - potentialCurrentSlice(-1), - potentialTotalSlices(0), - SMatrixCurrentBeam(-1), - SMatrixTotalBeams(0), - currentProbe(0), - totalProbes(0) +prism_progressbar::prism_progressbar(PRISMMainWindow *_parent) : parent(_parent), + ui(new Ui::prism_progressbar), + potentialCurrentSlice(-1), + potentialTotalSlices(0), + SMatrixCurrentBeam(-1), + SMatrixTotalBeams(0), + currentProbe(0), + totalProbes(0) { ui->setupUi(this); ui->progressBar->setValue(0); connect(this, SIGNAL(updateDescriptionMessage(QString)), this, SLOT(updateDescription(QString))); connect(this, SIGNAL(updateCalcStatus(QString)), this, SLOT(updateCalcStatusMessage(QString))); - connect(this, SIGNAL(updateProgressBar(int)), ui->progressBar, SLOT(setValue(int))); - + connect(this, SIGNAL(updateProgressBar(int)), ui->progressBar, SLOT(setValue(int))); } -void prism_progressbar::setStepPotential(){ +void prism_progressbar::setStepPotential() +{ ui->lbl_Description->setText(QString("Computing Projected Potential Slices")); } -void prism_progressbar::setTitle(const QString str){ +void prism_progressbar::setTitle(const QString str) +{ ui->lbl_Algorithm->setText(str); } - //void prism_progressbar::setAlgorithmPRISM(){ // ui->lbl_calcStatus->setText("Computing Projected Potential Slices"); //} -void prism_progressbar::update_calculatingPotential(long current, long total){ +void prism_progressbar::update_calculatingPotential(long current, long total) +{ potentialCurrentSlice = std::max(potentialCurrentSlice, current); ui->lbl_calcStatus->setText(QString("Slice ") + - QString::number(current + 1) + - QString("/") + - QString::number(total)); + QString::number(current + 1) + + QString("/") + + QString::number(total)); } -void prism_progressbar::updateDescription(const QString str){ +void prism_progressbar::updateDescription(const QString str) +{ ui->lbl_Description->setText(str); } //void prism_progressbar::setText(const QString str){ // ui->lbl_Description->setText(str); //} -void prism_progressbar::setProgress(int val){ - emit updateProgressBar(val); +void prism_progressbar::setProgress(int val) +{ + emit updateProgressBar(val); } -void prism_progressbar::updateCalcStatusMessage(const QString str){ +void prism_progressbar::updateCalcStatusMessage(const QString str) +{ ui->lbl_calcStatus->setText(str); } -void prism_progressbar::signalCalcStatusMessage(const QString str){ +void prism_progressbar::signalCalcStatusMessage(const QString str) +{ emit updateCalcStatus(str); } -void prism_progressbar::signalDescriptionMessage(const QString str){ +void prism_progressbar::signalDescriptionMessage(const QString str) +{ emit updateDescriptionMessage(str); } -void prism_progressbar::signalPotentialUpdate(const long current, const long total){ -// std::lock_guard gatekeeper(this->parent->potentialLock); - potentialCurrentSlice = std::max(potentialCurrentSlice, current); - emit updateCalcStatus(QString("Slice ") + - QString::number(potentialCurrentSlice + 1) + - QString("/") + - QString::number(total)); - emit updateProgressBar(100*(current+1)/total); +void prism_progressbar::signalPotentialUpdate(const long current, const long total) +{ + // std::lock_guard gatekeeper(this->parent->potentialLock); + potentialCurrentSlice = std::max(potentialCurrentSlice, current); + emit updateCalcStatus(QString("Slice ") + + QString::number(potentialCurrentSlice + 1) + + QString("/") + + QString::number(total)); + emit updateProgressBar(100 * (current + 1) / total); } -void prism_progressbar::signalScompactUpdate(const long current, const long total){ -// std::lock_guard gatekeeper(this->parent->sMatrixLock); +void prism_progressbar::signalScompactUpdate(const long current, const long total) +{ + // std::lock_guard gatekeeper(this->parent->sMatrixLock); SMatrixCurrentBeam = std::max(SMatrixCurrentBeam, current); -// std::cout << "SMatrixCurrentBeam + 1 = " << SMatrixCurrentBeam + 1 << std::endl; + // std::cout << "SMatrixCurrentBeam + 1 = " << SMatrixCurrentBeam + 1 << std::endl; emit updateCalcStatus(QString("Plane Wave ") + QString::number(SMatrixCurrentBeam + 1) + QString("/") + QString::number(total)); - emit updateProgressBar(100*(SMatrixCurrentBeam+1)/total); + emit updateProgressBar(100 * (SMatrixCurrentBeam + 1) / total); } -void prism_progressbar::resetOutputs(){ +void prism_progressbar::resetOutputs() +{ currentProbe = 0; SMatrixCurrentBeam = 0; potentialCurrentSlice = 0; -// std::cout << "SMatrixCurrentBeam + 1 = " << SMatrixCurrentBeam + 1 << std::endl; -// emit updateCalcStatus(QString("Probe Position ") + -// QString::number(currentProbe + 1) + -// QString("/") + -// QString::number(total)); -// emit updateProgressBar(100*(currentProbe+1)/total); + // std::cout << "SMatrixCurrentBeam + 1 = " << SMatrixCurrentBeam + 1 << std::endl; + // emit updateCalcStatus(QString("Probe Position ") + + // QString::number(currentProbe + 1) + + // QString("/") + + // QString::number(total)); + // emit updateProgressBar(100*(currentProbe+1)/total); emit updateProgressBar(0); - } -void prism_progressbar::signalOutputUpdate(const long current, const long total){ -// std::lock_guard gatekeeper(this->parent->outputLock); +void prism_progressbar::signalOutputUpdate(const long current, const long total) +{ + // std::lock_guard gatekeeper(this->parent->outputLock); currentProbe = std::max(currentProbe, current); -// std::cout << "SMatrixCurrentBeam + 1 = " << SMatrixCurrentBeam + 1 << std::endl; + // std::cout << "SMatrixCurrentBeam + 1 = " << SMatrixCurrentBeam + 1 << std::endl; emit updateCalcStatus(QString("Probe Position ") + QString::number(currentProbe + 1) + QString("/") + QString::number(total)); - emit updateProgressBar(100*(currentProbe+1)/total); + emit updateProgressBar(100 * (currentProbe + 1) / total); } prism_progressbar::~prism_progressbar() diff --git a/Qt/prismmainwindow.h b/Qt/prismmainwindow.h index a3a811d44..74ac91ca0 100644 --- a/Qt/prismmainwindow.h +++ b/Qt/prismmainwindow.h @@ -14,7 +14,6 @@ #ifndef PRISMMAINWINDOW_H #define PRISMMAINWINDOW_H - #include #include #include @@ -28,8 +27,9 @@ #include "ArrayND.h" #include "params.h" #include "prism_colormapper.h" -namespace Ui { - class PRISMMainWindow; +namespace Ui +{ +class PRISMMainWindow; } // forward declare the thread classes that run the work @@ -43,7 +43,7 @@ class PRISMMainWindow : public QMainWindow // access the protected mutex locks and arrays friend class PRISMThread; friend class PotentialThread; -// friend class SMatrixThread; + // friend class SMatrixThread; friend class ProbeThread; friend class FullPRISMCalcThread; friend class FullMultisliceCalcThread; @@ -51,13 +51,13 @@ class PRISMMainWindow : public QMainWindow public: explicit PRISMMainWindow(QWidget *parent = 0); - bool potentialIsReady(); + bool potentialIsReady(); bool overwriteFile(); - bool SMatrixIsReady(); - bool OutputIsReady(); + bool SMatrixIsReady(); + bool OutputIsReady(); bool checkoutputArrayExists(); bool checkpotentialArrayExists(); - void updateUCdims(const std::string& filename); + void updateUCdims(const std::string &filename); ~PRISMMainWindow(); public slots: @@ -65,16 +65,16 @@ public slots: void selectParameterFile(); void writeParameterFile(); void readParams(const std::string param_filename); - void setInterpolationFactorX(); - void setInterpolationFactorY(); - void setFilenameAtoms_fromDialog(); - void setFilenameOutput_fromLineEdit(); - void setFilenameOutput_fromDialog(); - void setNumGPUs(const int& numGPUs); - void setNumThreads(const int& numThreads); - void setNumFP(const int& numFP); - void setNumNS(const int& numSlices); - void setNumStreams(const int& numFP); + void setInterpolationFactorX(); + void setInterpolationFactorY(); + void setFilenameAtoms_fromDialog(); + void setFilenameOutput_fromLineEdit(); + void setFilenameOutput_fromDialog(); + void setNumGPUs(const int &numGPUs); + void setNumThreads(const int &numThreads); + void setNumFP(const int &numFP); + void setNumNS(const int &numSlices); + void setNumStreams(const int &numFP); void setPixelSizeX_fromLineEdit(); void setPixelSizeY_fromLineEdit(); void setPotBound_fromLineEdit(); @@ -95,8 +95,8 @@ public slots: void setE0_fromLineEdit(); void setprobeStepX_fromLineEdit(); void setprobeStepY_fromLineEdit(); - void setAlgo_PRISM(); - void setAlgo_Multislice(); + void setAlgo_PRISM(); + void setAlgo_Multislice(); void calculatePotential(); void calculateAll(); void calculateProbe(); @@ -107,17 +107,17 @@ public slots: void updateOutputDisplay(); void updateOutputFloatImage(); void updateSliders_fromLineEdits(); - void updateSliders_fromLineEdits_ang(); + void updateSliders_fromLineEdits_ang(); void updateContrastPotMin(); void updateContrastPotMax(); void updateContrastAngMin(); void updateContrastAngMax(); void updateSlider_lineEdits_min(int); void updateSlider_lineEdits_max(int); - void updateSlider_lineEdits_max_ang(int val); - void updateSlider_lineEdits_min_ang(int val); + void updateSlider_lineEdits_max_ang(int val); + void updateSlider_lineEdits_min_ang(int val); void updateAlphaMax(); - void resizeEvent(QResizeEvent* event); + void resizeEvent(QResizeEvent *event); void redrawImages(); void saveCurrentOutputImage(); void setStreamingMode(int); @@ -133,11 +133,11 @@ public slots: void toggle2DOutput(); void toggle3DOutput(); void toggle4DOutput(); - void toggleDPC_CoM(); - void togglePotentialSlices(); + void toggleDPC_CoM(); + void togglePotentialSlices(); void toggleThermalEffects(); void toggleOccupancy(); - void toggleNyquist(); + void toggleNyquist(); void setscan_WindowXMin_fromLineEdit(); void setscan_WindowXMax_fromLineEdit(); void setscan_WindowYMin_fromLineEdit(); @@ -145,10 +145,10 @@ public slots: void resetCalculation(); void newRandomSeed(); -// void updateProbeK_PRISM(Prismatic::Array2D); -// void updateProbeR_PRISM(Prismatic::Array2D); -// void updateProbeK_Multislice(Prismatic::Array2D); -// void updateProbeR_Multislice(Prismatic::Array2D); + // void updateProbeK_PRISM(Prismatic::Array2D); + // void updateProbeR_PRISM(Prismatic::Array2D); + // void updateProbeK_Multislice(Prismatic::Array2D); + // void updateProbeR_Multislice(Prismatic::Array2D); void probeK_PRISMReceived(Prismatic::Array2D); void probeR_PRISMReceived(Prismatic::Array2D); void probeK_MultisliceReceived(Prismatic::Array2D); @@ -203,19 +203,19 @@ public slots: void flipOverwrite(); protected: - void setFilenameAtoms(const std::string& filename); - void setFilenameOutput(const std::string& filename); - void setRealspacePixelSize(const PRISMATIC_FLOAT_PRECISION& pixel_size); - void setPotBound(const PRISMATIC_FLOAT_PRECISION& potBound); - void setNumFP(const size_t& numFP); - void setNumNS(const size_t& numSlices); - void setE0(const PRISMATIC_FLOAT_PRECISION& E0); - void setAlphaBeamMax(const PRISMATIC_FLOAT_PRECISION& alphaBeamMax); - void setSliceThickness(const PRISMATIC_FLOAT_PRECISION& thickness); - void setCellDimX(const int& dimX); - void setCellDimY(const int& dimY); - void setCellDimZ(const int& dimZ); - void setAlgo(const Prismatic::Algorithm algo); + void setFilenameAtoms(const std::string &filename); + void setFilenameOutput(const std::string &filename); + void setRealspacePixelSize(const PRISMATIC_FLOAT_PRECISION &pixel_size); + void setPotBound(const PRISMATIC_FLOAT_PRECISION &potBound); + void setNumFP(const size_t &numFP); + void setNumNS(const size_t &numSlices); + void setE0(const PRISMATIC_FLOAT_PRECISION &E0); + void setAlphaBeamMax(const PRISMATIC_FLOAT_PRECISION &alphaBeamMax); + void setSliceThickness(const PRISMATIC_FLOAT_PRECISION &thickness); + void setCellDimX(const int &dimX); + void setCellDimY(const int &dimY); + void setCellDimZ(const int &dimZ); + void setAlgo(const Prismatic::Algorithm algo); private: Ui::PRISMMainWindow *ui; @@ -223,7 +223,7 @@ public slots: Prismatic::Metadata *meta; Prismatic::Parameters pars; Prismatic::Parameters pars_multi; - Prismatic::Metadata* getMetadata(){return this->meta;} + Prismatic::Metadata *getMetadata() { return this->meta; } Prismatic::Array3D potential; Prismatic::Array4D output; Prismatic::Array1D detectorAngles; @@ -276,7 +276,6 @@ public slots: PRISMATIC_FLOAT_PRECISION contrast_outputMax; PRISMATIC_FLOAT_PRECISION currently_calculated_X; PRISMATIC_FLOAT_PRECISION currently_calculated_Y; - }; unsigned char getUcharFromFloat(PRISMATIC_FLOAT_PRECISION val, diff --git a/Qt/saveatomiccoordinatesdialog.cpp b/Qt/saveatomiccoordinatesdialog.cpp index 9b0f8ec82..74028aa61 100644 --- a/Qt/saveatomiccoordinatesdialog.cpp +++ b/Qt/saveatomiccoordinatesdialog.cpp @@ -1,22 +1,24 @@ #include "saveatomiccoordinatesdialog.h" #include "ui_saveatomiccoordinatesdialog.h" -SaveAtomicCoordinatesDialog::SaveAtomicCoordinatesDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::SaveAtomicCoordinatesDialog) +SaveAtomicCoordinatesDialog::SaveAtomicCoordinatesDialog(QWidget *parent) : QDialog(parent), + ui(new Ui::SaveAtomicCoordinatesDialog) { ui->setupUi(this); } -void SaveAtomicCoordinatesDialog::SaveAtomCoords(){ +void SaveAtomicCoordinatesDialog::SaveAtomCoords() +{ emit signalSaveAtomCoords(ui->lineEdit_Filename->text(), ui->lineEdit_Comment->text()); } -void SaveAtomicCoordinatesDialog::setFilenameText(QString text){ +void SaveAtomicCoordinatesDialog::setFilenameText(QString text) +{ ui->lineEdit_Filename->setText(text); } -void SaveAtomicCoordinatesDialog::setCommentText(QString text){ +void SaveAtomicCoordinatesDialog::setCommentText(QString text) +{ ui->lineEdit_Comment->setText(text); } diff --git a/Qt/saveatomiccoordinatesdialog.h b/Qt/saveatomiccoordinatesdialog.h index 7b866f577..83f0969e2 100644 --- a/Qt/saveatomiccoordinatesdialog.h +++ b/Qt/saveatomiccoordinatesdialog.h @@ -3,7 +3,8 @@ #include -namespace Ui { +namespace Ui +{ class SaveAtomicCoordinatesDialog; } @@ -20,6 +21,7 @@ public slots: void setCommentText(QString); signals: void signalSaveAtomCoords(QString, QString); + private: Ui::SaveAtomicCoordinatesDialog *ui; }; diff --git a/docs/command-line.md b/docs/command-line.md index cfbf7b815..f75770b1c 100644 --- a/docs/command-line.md +++ b/docs/command-line.md @@ -3,24 +3,24 @@ PRISM contains a command line tool, `prism`, that can be used to run simulations from within a terminal, bash script, etc. Building it requires the CMake variable `PRISM_ENABLE_CLI=1` at compilation time, which is the default behavior. ### Options -The following options are available with `prism`, each documented as **_long form_** **_(short form)_** *parameters* : description -* --**_input-file (-i)_** *filename* : the filename containing the atomic coordinates, which should be a plain text file with comma-separated values in the format x, y, z, Z -* --**_output-file(-o)_** *filename* : output filename -* --**_interp-factor (-f)_** *number* : PRISM interpolation factor -* --**_num-threads (-j)_** *number* : number of CPU threads to use -* --**_num-streams (-S)_** *number* : number of CUDA streams to create per GPU -* --**_slice-thickness (-s)_** *thickness* : thickness of each slice of projected potential (in Angstroms) -* --**_num-gpus (-g)_** *number* : number of GPUs to use. A runtime check is performed to check how many are actually available, and minimum of these two numbers is used. -* --**_help(-h)_** : print information about the available options -* --**_pixel-size (-p)_** *pixel_size* : size of simulation pixel size -* --**_cell-dimension (-c)_** *x y z* : size of sample in x, y, z directions (in Angstroms) -* --**_algorithm (-a)_** *p/m* : the simulation algorithm to use, either (p)rism or (m)ultislice -* --**_energy (-E)_** *value* : the energy of the electron beam (in keV) -* --**_alpha-max (-A)_** *angle* : the maximum probe angle to consider (in mrad) -* --**_potential-bound (-P)_** *value* : the maximum radius from the center of each atom to compute the potental (in Angstroms) -* --**_also-do-cpu-work (-C)_** *0/1* : boolean value used to determine whether or not to also create CPU workers in addition to GPU ones -* --**_force-streaming-mode_** *0/1* : boolean value to force code to use (true) or not use (false) streaming versions of GPU codes. The default behavior is to estimate the needed memory from input parameters and choose automatically. -* --**_probe-step (-r)_** *step_size* : step size of the probe (in Angstroms) -* --**_num-FP (-F)_** *number* : number of frozen phonon configurations to calculate +The following options are available with `prism`, each documented as **_long form_** **_(short form)_** _parameters_ : description +- --**_input-file (-i)_** _filename_ : the filename containing the atomic coordinates, which should be a plain text file with comma-separated values in the format x, y, z, Z +- --**_output-file(-o)_** _filename_ : output filename +- --**_interp-factor (-f)_** _number_ : PRISM interpolation factor +- --**_num-threads (-j)_** _number_ : number of CPU threads to use +- --**_num-streams (-S)_** _number_ : number of CUDA streams to create per GPU +- --**_slice-thickness (-s)_** _thickness_ : thickness of each slice of projected potential (in Angstroms) +- --**_num-gpus (-g)_** _number_ : number of GPUs to use. A runtime check is performed to check how many are actually available, and minimum of these two numbers is used. +- --**_help(-h)_** : print information about the available options +- --**_pixel-size (-p)_** _pixel_size_ : size of simulation pixel size +- --**_cell-dimension (-c)_** _x y z_ : size of sample in x, y, z directions (in Angstroms) +- --**_algorithm (-a)_** _p/m_ : the simulation algorithm to use, either (p)rism or (m)ultislice +- --**_energy (-E)_** _value_ : the energy of the electron beam (in keV) +- --**_alpha-max (-A)_** _angle_ : the maximum probe angle to consider (in mrad) +- --**_potential-bound (-P)_** _value_ : the maximum radius from the center of each atom to compute the potental (in Angstroms) +- --**_also-do-cpu-work (-C)_** _0/1_ : boolean value used to determine whether or not to also create CPU workers in addition to GPU ones +- --**_force-streaming-mode_** _0/1_ : boolean value to force code to use (true) or not use (false) streaming versions of GPU codes. The default behavior is to estimate the needed memory from input parameters and choose automatically. +- --**_probe-step (-r)_** _step_size_ : step size of the probe (in Angstroms) +- --**_num-FP (-F)_** _number_ : number of frozen phonon configurations to calculate diff --git a/examples/thickness_scan.py b/examples/thickness_scan.py index 56e085399..ccf33a096 100644 --- a/examples/thickness_scan.py +++ b/examples/thickness_scan.py @@ -1,4 +1,4 @@ -# This script calculates multiple images using a unit cell tiled varying numbers of times in Z. +# This script calculates multiple images using a unit cell tiled varying numbers of times in Z. # This type of simulation is useful for determining the true thickness of a sample from experimental images import pyprismatic as pr @@ -6,29 +6,29 @@ import matplotlib.pyplot as plt base_output_name = "thickness_scan" -base_output_ext = ".mrc" -atom_filename = "../SI100.XYZ" -tileX=tileY=3 +base_output_ext = ".mrc" +atom_filename = "../SI100.XYZ" +tileX = tileY = 3 meta = pr.Metadata(filenameAtoms=atom_filename, tileX=tileX, tileY=tileY) -output_filenames=[] +output_filenames = [] # run simulations -for tileZ in range(1,17): - meta.tileZ = tileZ - meta.filenameOutput = base_output_name + str(tileZ) + base_output_ext - output_filenames.extend([meta.filenameOutput]) - meta.go() +for tileZ in range(1, 17): + meta.tileZ = tileZ + meta.filenameOutput = base_output_name + str(tileZ) + base_output_ext + output_filenames.extend([meta.filenameOutput]) + meta.go() # print the results f, ax = plt.subplots(4, 4) ax = ax.ravel() for i, filename in enumerate(output_filenames): - stack = pr.fileio.readMRC(filename) - img = np.sum(stack[:, :, :10], axis=2) - ax[i].imshow(img) - ax[i].set_title("Thicknesss: {:.2f}$\AA$".format((i + 1) * 5.43)) - ax[i].set_yticklabels([]) - ax[i].set_xticklabels([]) + stack = pr.fileio.readMRC(filename) + img = np.sum(stack[:, :, :10], axis=2) + ax[i].imshow(img) + ax[i].set_title("Thickness: {:.2f}$\AA$".format((i + 1) * 5.43)) + ax[i].set_yticklabels([]) + ax[i].set_xticklabels([]) plt.suptitle("Bright Field (0-10 mrad) PRISM images for SI100 of varying thicknesses") plt.show() diff --git a/include/ArrayND.h b/include/ArrayND.h index c05e2a59b..e0bd8a0dd 100644 --- a/include/ArrayND.h +++ b/include/ArrayND.h @@ -22,582 +22,663 @@ #include #include #include -namespace Prismatic { - template - class ArrayND { - // ND array class for data indexed as C-style, i.e. arr.at(k,j,i) where i is the fastest varying index - // and k is the slowest - - // T is expected to be a std::vector - public: - ArrayND(T _data, - std::array _dims); - ArrayND(){}; - size_t get_dimi() const {return this->dims[N-1];} - size_t get_dimj() const {return this->dims[N-2];} - size_t get_dimk() const {return this->dims[N-3];} - size_t get_diml() const {return this->dims[N-4]; } - size_t get_dimm() const {return this->dims[N-5]; } - size_t size() const {return this->arr_size;} - typename T::iterator begin(); - typename T::iterator end(); - typename T::const_iterator begin() const; - typename T::const_iterator end() const; - typename T::value_type& at(const size_t& i); - typename T::value_type& at(const size_t& j, const size_t& i); - typename T::value_type& at(const size_t& k, const size_t& j,const size_t& i); - typename T::value_type& at(const size_t& l, const size_t& k,const size_t& j, const size_t& i); - typename T::value_type at(const size_t& i)const; - typename T::value_type at(const size_t& j, const size_t& i)const; - typename T::value_type at(const size_t& k, const size_t& j,const size_t& i)const; - typename T::value_type at(const size_t& l, const size_t& k,const size_t& j, const size_t& i)const; - - typename T::value_type& operator[](const size_t& i); - typename T::value_type operator[](const size_t& i)const; - ArrayND operator-( const ArrayND& other); - ArrayND operator+( const ArrayND& other); - ArrayND operator*( const ArrayND& other); - ArrayND operator/( const ArrayND& other); - ArrayND operator-( const typename T::value_type& val); - ArrayND operator+( const typename T::value_type& val); - ArrayND operator*( const typename T::value_type& val); - ArrayND operator/( const typename T::value_type& val); - ArrayND operator-( const ArrayND& other)const; - ArrayND operator+( const ArrayND& other)const; - ArrayND operator*( const ArrayND& other)const; - ArrayND operator/( const ArrayND& other)const; - ArrayND& operator-=(const ArrayND& other); - ArrayND& operator+=(const ArrayND& other); - ArrayND& operator*=(const ArrayND& other); - ArrayND& operator/=(const ArrayND& other); - ArrayND operator-( const typename T::value_type& val)const; - ArrayND operator+( const typename T::value_type& val)const; - ArrayND operator*( const typename T::value_type& val)const; - ArrayND operator/( const typename T::value_type& val)const; - ArrayND& operator-=(const typename T::value_type& val); - ArrayND& operator+=(const typename T::value_type& val); - ArrayND& operator*=(const typename T::value_type& val); - ArrayND& operator/=(const typename T::value_type& val); - - inline void toMRC_f(const char* filename)const; - private: - std::array dims; - std::array strides; - size_t arr_size; - T data; - - }; - - template - ArrayND::ArrayND(T _data, - std::array _dims):data(_data){ - size_t _size = 1; - for (auto& i:_dims)_size*=i; - if (_data.size() != _size){ - throw std::domain_error("PRISM: Size mismatch! Desired array size does not match size of input data\n"); - } - this->arr_size = _size; - this->dims = _dims; - - size_t stride = 1; - for (auto i = (N-1); i > 0; --i) { - stride *= this->dims[i]; - this->strides[i-1] = stride; - } - }; - - template - typename T::iterator ArrayND::begin(){return this->data.begin();} - - template - typename T::iterator ArrayND::end(){return this->data.end();} - - template - typename T::const_iterator ArrayND::begin() const {return this->data.begin();} - - template - typename T::const_iterator ArrayND::end() const {return this->data.end();} - - template - typename T::value_type& ArrayND::at(const size_t& i){ - return data[i]; - } - - template - typename T::value_type& ArrayND::at(const size_t& j, const size_t& i){ - return data[j*strides[0] + i]; - } - - template - typename T::value_type& ArrayND::at(const size_t& k, const size_t& j,const size_t& i){ - return data[k*strides[0] + j*strides[1] + i]; - } - - template - typename T::value_type& ArrayND::at(const size_t& l, const size_t& k,const size_t& j, const size_t& i){ - return data[l*strides[0] + k*strides[1] + j*strides[2] + i]; - } - - template - typename T::value_type ArrayND::at(const size_t& i)const{ - return data[i]; +namespace Prismatic +{ +template +class ArrayND +{ + // ND array class for data indexed as C-style, i.e. arr.at(k,j,i) where i is the fastest varying index + // and k is the slowest + + // T is expected to be a std::vector +public: + ArrayND(T _data, + std::array _dims); + ArrayND(){}; + size_t get_dimi() const { return this->dims[N - 1]; } + size_t get_dimj() const { return this->dims[N - 2]; } + size_t get_dimk() const { return this->dims[N - 3]; } + size_t get_diml() const { return this->dims[N - 4]; } + size_t get_dimm() const { return this->dims[N - 5]; } + size_t size() const { return this->arr_size; } + typename T::iterator begin(); + typename T::iterator end(); + typename T::const_iterator begin() const; + typename T::const_iterator end() const; + typename T::value_type &at(const size_t &i); + typename T::value_type &at(const size_t &j, const size_t &i); + typename T::value_type &at(const size_t &k, const size_t &j, const size_t &i); + typename T::value_type &at(const size_t &l, const size_t &k, const size_t &j, const size_t &i); + typename T::value_type at(const size_t &i) const; + typename T::value_type at(const size_t &j, const size_t &i) const; + typename T::value_type at(const size_t &k, const size_t &j, const size_t &i) const; + typename T::value_type at(const size_t &l, const size_t &k, const size_t &j, const size_t &i) const; + + typename T::value_type &operator[](const size_t &i); + typename T::value_type operator[](const size_t &i) const; + ArrayND operator-(const ArrayND &other); + ArrayND operator+(const ArrayND &other); + ArrayND operator*(const ArrayND &other); + ArrayND operator/(const ArrayND &other); + ArrayND operator-(const typename T::value_type &val); + ArrayND operator+(const typename T::value_type &val); + ArrayND operator*(const typename T::value_type &val); + ArrayND operator/(const typename T::value_type &val); + ArrayND operator-(const ArrayND &other) const; + ArrayND operator+(const ArrayND &other) const; + ArrayND operator*(const ArrayND &other) const; + ArrayND operator/(const ArrayND &other) const; + ArrayND &operator-=(const ArrayND &other); + ArrayND &operator+=(const ArrayND &other); + ArrayND &operator*=(const ArrayND &other); + ArrayND &operator/=(const ArrayND &other); + ArrayND operator-(const typename T::value_type &val) const; + ArrayND operator+(const typename T::value_type &val) const; + ArrayND operator*(const typename T::value_type &val) const; + ArrayND operator/(const typename T::value_type &val) const; + ArrayND &operator-=(const typename T::value_type &val); + ArrayND &operator+=(const typename T::value_type &val); + ArrayND &operator*=(const typename T::value_type &val); + ArrayND &operator/=(const typename T::value_type &val); + + inline void toMRC_f(const char *filename) const; + +private: + std::array dims; + std::array strides; + size_t arr_size; + T data; +}; + +template +ArrayND::ArrayND(T _data, + std::array _dims) : data(_data) +{ + size_t _size = 1; + for (auto &i : _dims) + _size *= i; + if (_data.size() != _size) + { + throw std::domain_error("PRISM: Size mismatch! Desired array size does not match size of input data\n"); } - - template - typename T::value_type ArrayND::at(const size_t& j, const size_t& i)const{ - return data[j*strides[0] + i]; + this->arr_size = _size; + this->dims = _dims; + + size_t stride = 1; + for (auto i = (N - 1); i > 0; --i) + { + stride *= this->dims[i]; + this->strides[i - 1] = stride; } +}; - template - typename T::value_type ArrayND::at(const size_t& k, const size_t& j,const size_t& i)const{ - return data[k*strides[0] + j*strides[1] + i]; - } +template +typename T::iterator ArrayND::begin() { return this->data.begin(); } - template - typename T::value_type ArrayND::at(const size_t& l, const size_t& k,const size_t& j, const size_t& i)const{ - return data[l*strides[0] + k*strides[1] + j*strides[2] + i]; - } +template +typename T::iterator ArrayND::end() { return this->data.end(); } +template +typename T::const_iterator ArrayND::begin() const { return this->data.begin(); } +template +typename T::const_iterator ArrayND::end() const { return this->data.end(); } - template - typename T::value_type& ArrayND::operator[](const size_t& i){return data[i];} - - template - typename T::value_type ArrayND::operator[](const size_t& i)const{return data[i];} - - template - ArrayND ArrayND::operator-(const ArrayND& other){ - ArrayND result(*this); - typename T::value_type* o = other.begin(); - for (auto& i:result)i-=*o++; - return result; - } - - template - ArrayND ArrayND::operator+(const ArrayND& other){ - ArrayND result(*this); - typename T::value_type* o = other.begin(); - for (auto& i:result)i+=*o++; - return result; - } - - template - ArrayND ArrayND::operator*(const ArrayND& other){ - ArrayND result(*this); - typename T::value_type* o = other.begin(); - for (auto& i:result)i*=*o++; - return result; - } - - template - ArrayND ArrayND::operator/(const ArrayND& other){ - ArrayND result(*this); - typename T::value_type* o = other.begin(); - for (auto& i:result)i/=*o++; - return result; - } - - template - ArrayND ArrayND::operator-(const typename T::value_type& val){ - ArrayND result(*this); - for (auto& i:result)i-=val; - return result; - } - - template - ArrayND ArrayND::operator+(const typename T::value_type& val){ - ArrayND result(*this); - for (auto& i:result)i+=val; - return result; - } - - template - ArrayND ArrayND::operator*(const typename T::value_type& val){ - ArrayND result(*this); - for (auto& i:result)i*=val; - return result; - } - - template - ArrayND ArrayND::operator/(const typename T::value_type& val){ - ArrayND result(*this); - for (auto& i:result)i/=val; - return result; - } - - template - ArrayND ArrayND::operator-(const ArrayND& other)const{ - ArrayND result(*this); - typename T::value_type* o = other.begin(); - for (auto& i:result)i-=*o++; - return result; - } +template +typename T::value_type &ArrayND::at(const size_t &i) +{ + return data[i]; +} - template - ArrayND ArrayND::operator+(const ArrayND& other)const{ - ArrayND result(*this); - typename T::value_type* o = other.begin(); - for (auto& i:result)i+=*o++; - return result; - } +template +typename T::value_type &ArrayND::at(const size_t &j, const size_t &i) +{ + return data[j * strides[0] + i]; +} - template - ArrayND ArrayND::operator*(const ArrayND& other)const{ - ArrayND result(*this); - typename T::value_type* o = other.begin(); - for (auto& i:result)i*=*o++; - return result; - } +template +typename T::value_type &ArrayND::at(const size_t &k, const size_t &j, const size_t &i) +{ + return data[k * strides[0] + j * strides[1] + i]; +} - template - ArrayND ArrayND::operator/(const ArrayND& other)const{ - ArrayND result(*this); - typename T::value_type* o = other.begin(); - for (auto& i:result)i/=*o++; - return result; - } +template +typename T::value_type &ArrayND::at(const size_t &l, const size_t &k, const size_t &j, const size_t &i) +{ + return data[l * strides[0] + k * strides[1] + j * strides[2] + i]; +} - template - ArrayND ArrayND::operator-(const typename T::value_type& val)const{ - ArrayND result(*this); - for (auto& i:result)i-=val; - return result; - } +template +typename T::value_type ArrayND::at(const size_t &i) const +{ + return data[i]; +} - template - ArrayND ArrayND::operator+(const typename T::value_type& val)const{ - ArrayND result(*this); - for (auto& i:result)i+=val; - return result; - } +template +typename T::value_type ArrayND::at(const size_t &j, const size_t &i) const +{ + return data[j * strides[0] + i]; +} - template - ArrayND ArrayND::operator*(const typename T::value_type& val)const{ - ArrayND result(*this); - for (auto& i:result)i*=val; - return result; - } +template +typename T::value_type ArrayND::at(const size_t &k, const size_t &j, const size_t &i) const +{ + return data[k * strides[0] + j * strides[1] + i]; +} - template - ArrayND ArrayND::operator/(const typename T::value_type& val)const{ - ArrayND result(*this); - for (auto& i:result)i/=val; - return result; - } +template +typename T::value_type ArrayND::at(const size_t &l, const size_t &k, const size_t &j, const size_t &i) const +{ + return data[l * strides[0] + k * strides[1] + j * strides[2] + i]; +} - template - ArrayND& ArrayND::operator-=(const typename T::value_type& val){ - for (auto& i:(*this))i-=val; - return *this; - } +template +typename T::value_type &ArrayND::operator[](const size_t &i) { return data[i]; } - template - ArrayND& ArrayND::operator+=(const typename T::value_type& val){ - for (auto& i:(*this))i+=val; - return *this; - } +template +typename T::value_type ArrayND::operator[](const size_t &i) const { return data[i]; } - template - ArrayND& ArrayND::operator*=(const typename T::value_type& val){ - for (auto& i:(*this))i*=val; - return *this; - } +template +ArrayND ArrayND::operator-(const ArrayND &other) +{ + ArrayND result(*this); + typename T::value_type *o = other.begin(); + for (auto &i : result) + i -= *o++; + return result; +} - template - ArrayND& ArrayND::operator/=(const typename T::value_type& val){ - for (auto& i:(*this))i/=val; - return *this; - } +template +ArrayND ArrayND::operator+(const ArrayND &other) +{ + ArrayND result(*this); + typename T::value_type *o = other.begin(); + for (auto &i : result) + i += *o++; + return result; +} - template - ArrayND& ArrayND::operator-=(const ArrayND& other){ - auto o = other.begin(); - for (auto& i:*this)i-=*o++; - return *this; - } +template +ArrayND ArrayND::operator*(const ArrayND &other) +{ + ArrayND result(*this); + typename T::value_type *o = other.begin(); + for (auto &i : result) + i *= *o++; + return result; +} - template - ArrayND& ArrayND::operator+=(const ArrayND& other){ - auto o = other.begin(); - for (auto& i:*this)i+=*o++; - return *this; - } +template +ArrayND ArrayND::operator/(const ArrayND &other) +{ + ArrayND result(*this); + typename T::value_type *o = other.begin(); + for (auto &i : result) + i /= *o++; + return result; +} - template - ArrayND& ArrayND::operator*=(const ArrayND& other){ - auto o = other.begin(); - for (auto& i:*this)i*=*o++; - return *this; - } +template +ArrayND ArrayND::operator-(const typename T::value_type &val) +{ + ArrayND result(*this); + for (auto &i : result) + i -= val; + return result; +} - template - ArrayND& ArrayND::operator/=(const ArrayND& other){ - auto o = other.begin(); - for (auto& i:*this)i/=*o++; - return *this; - } +template +ArrayND ArrayND::operator+(const typename T::value_type &val) +{ + ArrayND result(*this); + for (auto &i : result) + i += val; + return result; +} - template - Prismatic::ArrayND > ones_ND(const std::array dims){ - size_t size = 1; - for (auto& i:dims)size*=i; - return Prismatic::ArrayND >(std::vector(size,1), dims); - } - - template - Prismatic::ArrayND > zeros_ND(const std::array dims){ - size_t size = 1; - for (auto& i:dims)size*=i; - return Prismatic::ArrayND >(std::vector(size,0), dims); - } - - template - using Array2D_T = Prismatic::ArrayND<2, std::vector >; - template - using Array1D_T = Prismatic::ArrayND<1, std::vector >; - template - std::pair, Array2D_T> meshgrid(const Array1D_T& Y, const Array1D_T& X){ - Array2D_T yy = zeros_ND<2, T>({{Y.size(), X.size()}}); - Array2D_T xx = zeros_ND<2, T>({{Y.size(), X.size()}}); - for (auto j = 0; j < xx.get_dimj(); ++j){ - for (auto i = 0; i < xx.get_dimi(); ++i){ - yy.at(j,i) = Y[j]; - xx.at(j,i) = X[i]; - } - } - return std::pair, Array2D_T >(yy,xx); - } +template +ArrayND ArrayND::operator*(const typename T::value_type &val) +{ + ArrayND result(*this); + for (auto &i : result) + i *= val; + return result; +} - template <> - inline void ArrayND<3, std::vector >::toMRC_f(const char* filename) const{ - // output to an MRC file in float format - // see http://bio3d.colorado.edu/imod/doc/mrc_format.txt for details - std::ofstream f(filename, std::ios::binary |std::ios::out); - if (f) { - int int_header[56]; - char char_header[800]; - std::memset((void *) char_header, 0, 800); - std::memset((void *) int_header, 0, 56 * 4); - int_header[0] = (int) dims[2]; //nx - int_header[1] = (int) dims[1]; //ny - int_header[2] = (int) dims[0]; //nz - int_header[3] = 2; //mode, float - f.write((char*)int_header,56*4); //use 4 instead of sizeof(int) because architecture may change but file format won't - f.write(char_header,800); - float* data_buffer = new float[this->size()]; - for (auto i = 0; i < this->size(); ++i)data_buffer[i] = (float)data[i]; - f.write((char*)data_buffer,this->size()*sizeof(float)); - delete[] data_buffer; - } - } +template +ArrayND ArrayND::operator/(const typename T::value_type &val) +{ + ArrayND result(*this); + for (auto &i : result) + i /= val; + return result; +} +template +ArrayND ArrayND::operator-(const ArrayND &other) const +{ + ArrayND result(*this); + typename T::value_type *o = other.begin(); + for (auto &i : result) + i -= *o++; + return result; +} - template <> - inline void ArrayND<3, std::vector >::toMRC_f(const char* filename) const{ - // output to an MRC file in float format - // see http://bio3d.colorado.edu/imod/doc/mrc_format.txt for details - std::ofstream f(filename, std::ios::binary |std::ios::out); - if (f) { - int int_header[56]; - char char_header[800]; - std::memset((void *) char_header, 0, 800); - std::memset((void *) int_header, 0, 56 * 4); - int_header[0] = (int) dims[2]; //nx - int_header[1] = (int) dims[1]; //ny - int_header[2] = (int) dims[0]; //nz - int_header[3] = 2; //mode, float - f.write((char*)int_header,56*4); //use 4 instead of sizeof(int) because architecture may change but file format won't - f.write(char_header,800); - - float* data_buffer = new float[this->size()]; - for (auto i = 0; i < this->size(); ++i)data_buffer[i] = data[i]; - f.write((char*)data_buffer,this->size()*sizeof(float)); - delete[] data_buffer; - } else { - std::cout << "error opening file " << filename << std::endl; - } - } +template +ArrayND ArrayND::operator+(const ArrayND &other) const +{ + ArrayND result(*this); + typename T::value_type *o = other.begin(); + for (auto &i : result) + i += *o++; + return result; +} - template <> - inline void ArrayND<2, std::vector >::toMRC_f(const char* filename) const{ - // output to an MRC file in float format - // see http://bio3d.colorado.edu/imod/doc/mrc_format.txt for details - std::ofstream f(filename, std::ios::binary |std::ios::out); - if (f) { - int int_header[56]; - char char_header[800]; - std::memset((void *) char_header, 0, 800); - std::memset((void *) int_header, 0, 56 * sizeof(int)); - int_header[0] = (int) dims[1]; //nx - int_header[1] = (int) dims[0]; //ny - int_header[2] = (int) 1; //nz - int_header[3] = 2; //mode, float - f.write((char*)int_header,56*4); //use 4 instead of sizeof(int) because architecture may change but file format won't - f.write(char_header,800); - float* data_buffer = new float[this->size()]; - for (auto i = 0; i < this->size(); ++i)data_buffer[i] = (float)data[i]; - f.write((char*)data_buffer,this->size()*sizeof(float)); - delete[] data_buffer; +template +ArrayND ArrayND::operator*(const ArrayND &other) const +{ + ArrayND result(*this); + typename T::value_type *o = other.begin(); + for (auto &i : result) + i *= *o++; + return result; +} + +template +ArrayND ArrayND::operator/(const ArrayND &other) const +{ + ArrayND result(*this); + typename T::value_type *o = other.begin(); + for (auto &i : result) + i /= *o++; + return result; +} + +template +ArrayND ArrayND::operator-(const typename T::value_type &val) const +{ + ArrayND result(*this); + for (auto &i : result) + i -= val; + return result; +} + +template +ArrayND ArrayND::operator+(const typename T::value_type &val) const +{ + ArrayND result(*this); + for (auto &i : result) + i += val; + return result; +} + +template +ArrayND ArrayND::operator*(const typename T::value_type &val) const +{ + ArrayND result(*this); + for (auto &i : result) + i *= val; + return result; +} + +template +ArrayND ArrayND::operator/(const typename T::value_type &val) const +{ + ArrayND result(*this); + for (auto &i : result) + i /= val; + return result; +} + +template +ArrayND &ArrayND::operator-=(const typename T::value_type &val) +{ + for (auto &i : (*this)) + i -= val; + return *this; +} + +template +ArrayND &ArrayND::operator+=(const typename T::value_type &val) +{ + for (auto &i : (*this)) + i += val; + return *this; +} + +template +ArrayND &ArrayND::operator*=(const typename T::value_type &val) +{ + for (auto &i : (*this)) + i *= val; + return *this; +} + +template +ArrayND &ArrayND::operator/=(const typename T::value_type &val) +{ + for (auto &i : (*this)) + i /= val; + return *this; +} + +template +ArrayND &ArrayND::operator-=(const ArrayND &other) +{ + auto o = other.begin(); + for (auto &i : *this) + i -= *o++; + return *this; +} + +template +ArrayND &ArrayND::operator+=(const ArrayND &other) +{ + auto o = other.begin(); + for (auto &i : *this) + i += *o++; + return *this; +} + +template +ArrayND &ArrayND::operator*=(const ArrayND &other) +{ + auto o = other.begin(); + for (auto &i : *this) + i *= *o++; + return *this; +} + +template +ArrayND &ArrayND::operator/=(const ArrayND &other) +{ + auto o = other.begin(); + for (auto &i : *this) + i /= *o++; + return *this; +} + +template +Prismatic::ArrayND> ones_ND(const std::array dims) +{ + size_t size = 1; + for (auto &i : dims) + size *= i; + return Prismatic::ArrayND>(std::vector(size, 1), dims); +} + +template +Prismatic::ArrayND> zeros_ND(const std::array dims) +{ + size_t size = 1; + for (auto &i : dims) + size *= i; + return Prismatic::ArrayND>(std::vector(size, 0), dims); +} + +template +using Array2D_T = Prismatic::ArrayND<2, std::vector>; +template +using Array1D_T = Prismatic::ArrayND<1, std::vector>; +template +std::pair, Array2D_T> meshgrid(const Array1D_T &Y, const Array1D_T &X) +{ + Array2D_T yy = zeros_ND<2, T>({{Y.size(), X.size()}}); + Array2D_T xx = zeros_ND<2, T>({{Y.size(), X.size()}}); + for (auto j = 0; j < xx.get_dimj(); ++j) + { + for (auto i = 0; i < xx.get_dimi(); ++i) + { + yy.at(j, i) = Y[j]; + xx.at(j, i) = X[i]; } } + return std::pair, Array2D_T>(yy, xx); +} - template <> - inline void ArrayND<2, std::vector >::toMRC_f(const char* filename) const{ - // output to an MRC file in float format - // see http://bio3d.colorado.edu/imod/doc/mrc_format.txt for details - std::ofstream f(filename, std::ios::binary |std::ios::out); - if (f) { - int int_header[56]; - char char_header[800]; - std::memset((void *) char_header, 0, 800); - std::memset((void *) int_header, 0, 56 * sizeof(int)); - int_header[0] = (int) dims[1]; //nx - int_header[1] = (int) dims[0]; //ny - int_header[2] = (int) 1; //nz - int_header[3] = 2; //mode, float - f.write((char*)int_header,56*4); //use 4 instead of sizeof(int) because architecture may change but file format won't - f.write(char_header,800); - float* data_buffer = new float[this->size()]; - for (auto i = 0; i < this->size(); ++i)data_buffer[i] = (float)data[i]; - f.write((char*)data_buffer,this->size()*sizeof(float)); - delete[] data_buffer; - } +template <> +inline void ArrayND<3, std::vector>::toMRC_f(const char *filename) const +{ + // output to an MRC file in float format + // see http://bio3d.colorado.edu/imod/doc/mrc_format.txt for details + std::ofstream f(filename, std::ios::binary | std::ios::out); + if (f) + { + int int_header[56]; + char char_header[800]; + std::memset((void *)char_header, 0, 800); + std::memset((void *)int_header, 0, 56 * 4); + int_header[0] = (int)dims[2]; //nx + int_header[1] = (int)dims[1]; //ny + int_header[2] = (int)dims[0]; //nz + int_header[3] = 2; //mode, float + f.write((char *)int_header, 56 * 4); //use 4 instead of sizeof(int) because architecture may change but file format won't + f.write(char_header, 800); + float *data_buffer = new float[this->size()]; + for (auto i = 0; i < this->size(); ++i) + data_buffer[i] = (float)data[i]; + f.write((char *)data_buffer, this->size() * sizeof(float)); + delete[] data_buffer; } +} - template <> - inline void ArrayND<2, std::vector >::toMRC_f(const char* filename) const{ - // output to an MRC file in float format - // see http://bio3d.colorado.edu/imod/doc/mrc_format.txt for details - std::ofstream f(filename, std::ios::binary |std::ios::out); - if (f) { - int int_header[56]; - char char_header[800]; - std::memset((void *) char_header, 0, 800); - std::memset((void *) int_header, 0, 56 * sizeof(int)); - int_header[0] = (int) dims[1]; //nx - int_header[1] = (int) dims[0]; //ny - int_header[2] = (int) 1; //nz - int_header[3] = 2; //mode, float - f.write((char*)int_header,56*4); //use 4 instead of sizeof(int) because architecture may change but file format won't - f.write(char_header,800); - float* data_buffer = new float[this->size()]; - for (auto i = 0; i < this->size(); ++i)data_buffer[i] = (float)data[i]; - f.write((char*)data_buffer,this->size()*sizeof(float)); - delete[] data_buffer; - } +template <> +inline void ArrayND<3, std::vector>::toMRC_f(const char *filename) const +{ + // output to an MRC file in float format + // see http://bio3d.colorado.edu/imod/doc/mrc_format.txt for details + std::ofstream f(filename, std::ios::binary | std::ios::out); + if (f) + { + int int_header[56]; + char char_header[800]; + std::memset((void *)char_header, 0, 800); + std::memset((void *)int_header, 0, 56 * 4); + int_header[0] = (int)dims[2]; //nx + int_header[1] = (int)dims[1]; //ny + int_header[2] = (int)dims[0]; //nz + int_header[3] = 2; //mode, float + f.write((char *)int_header, 56 * 4); //use 4 instead of sizeof(int) because architecture may change but file format won't + f.write(char_header, 800); + + float *data_buffer = new float[this->size()]; + for (auto i = 0; i < this->size(); ++i) + data_buffer[i] = data[i]; + f.write((char *)data_buffer, this->size() * sizeof(float)); + delete[] data_buffer; } + else + { + std::cout << "error opening file " << filename << std::endl; + } +} +template <> +inline void ArrayND<2, std::vector>::toMRC_f(const char *filename) const +{ + // output to an MRC file in float format + // see http://bio3d.colorado.edu/imod/doc/mrc_format.txt for details + std::ofstream f(filename, std::ios::binary | std::ios::out); + if (f) + { + int int_header[56]; + char char_header[800]; + std::memset((void *)char_header, 0, 800); + std::memset((void *)int_header, 0, 56 * sizeof(int)); + int_header[0] = (int)dims[1]; //nx + int_header[1] = (int)dims[0]; //ny + int_header[2] = (int)1; //nz + int_header[3] = 2; //mode, float + f.write((char *)int_header, 56 * 4); //use 4 instead of sizeof(int) because architecture may change but file format won't + f.write(char_header, 800); + float *data_buffer = new float[this->size()]; + for (auto i = 0; i < this->size(); ++i) + data_buffer[i] = (float)data[i]; + f.write((char *)data_buffer, this->size() * sizeof(float)); + delete[] data_buffer; + } +} - // We want to prevents programming errors like querying the size of the 3rd dimension of - // a 2D object. Rather than introducing a check at runtime, I'll just delete them. Unfortunately, - // this does require full template instantiation, so each type has to be done separately. - template <> - size_t ArrayND<1, std::vector >::get_dimj() const = delete; - template <> - size_t ArrayND<1, std::vector >::get_dimk() const = delete; - template <> - size_t ArrayND<1, std::vector >::get_diml() const = delete; - template <> - size_t ArrayND<1, std::vector >::get_dimm() const = delete; - template <> - size_t ArrayND<2, std::vector >::get_dimk() const = delete; - template <> - size_t ArrayND<2, std::vector >::get_diml() const = delete; - template <> - size_t ArrayND<2, std::vector >::get_dimm() const = delete; - template <> - size_t ArrayND<3, std::vector >::get_diml() const = delete; - template <> - size_t ArrayND<3, std::vector >::get_dimm() const = delete; - template <> - size_t ArrayND<4, std::vector >::get_dimm() const = delete; - - template <> - size_t ArrayND<1, std::vector< std::complex > >::get_dimj() const = delete; - template <> - size_t ArrayND<1, std::vector< std::complex > >::get_dimk() const = delete; - template <> - size_t ArrayND<1, std::vector< std::complex > >::get_diml() const = delete; - template <> - size_t ArrayND<1, std::vector< std::complex > >::get_dimm() const = delete; - template <> - size_t ArrayND<2, std::vector< std::complex > >::get_dimk() const = delete; - template <> - size_t ArrayND<2, std::vector< std::complex > >::get_diml() const = delete; - template <> - size_t ArrayND<2, std::vector< std::complex > >::get_dimm() const = delete; - template <> - size_t ArrayND<3, std::vector< std::complex > >::get_diml() const = delete; - template <> - size_t ArrayND<3, std::vector< std::complex > >::get_dimm() const = delete; - template <> - size_t ArrayND<4, std::vector< std::complex > >::get_dimm() const = delete; - - template <> - size_t ArrayND<1, std::vector >::get_dimj() const = delete; - template <> - size_t ArrayND<1, std::vector >::get_dimk() const = delete; - template <> - size_t ArrayND<1, std::vector >::get_diml() const = delete; - template <> - size_t ArrayND<1, std::vector >::get_dimm() const = delete; - template <> - size_t ArrayND<2, std::vector >::get_dimk() const = delete; - template <> - size_t ArrayND<2, std::vector >::get_diml() const = delete; - template <> - size_t ArrayND<2, std::vector >::get_dimm() const = delete; - template <> - size_t ArrayND<3, std::vector >::get_diml() const = delete; - template <> - size_t ArrayND<3, std::vector >::get_dimm() const = delete; - template <> - size_t ArrayND<4, std::vector >::get_dimm() const = delete; - - template <> - size_t ArrayND<1, std::vector< std::complex > >::get_dimj() const = delete; - template <> - size_t ArrayND<1, std::vector< std::complex > >::get_dimk() const = delete; - template <> - size_t ArrayND<1, std::vector< std::complex > >::get_diml() const = delete; - template <> - size_t ArrayND<1, std::vector< std::complex > >::get_dimm() const = delete; - template <> - size_t ArrayND<2, std::vector< std::complex > >::get_dimk() const = delete; - template <> - size_t ArrayND<2, std::vector< std::complex > >::get_diml() const = delete; - template <> - size_t ArrayND<2, std::vector< std::complex > >::get_dimm() const = delete; - template <> - size_t ArrayND<3, std::vector< std::complex > >::get_diml() const = delete; - template <> - size_t ArrayND<3, std::vector< std::complex > >::get_dimm() const = delete; - template <> - size_t ArrayND<4, std::vector< std::complex > >::get_dimm() const = delete; - - template <> - size_t ArrayND<1, std::vector >::get_dimj() const = delete; - template <> - size_t ArrayND<1, std::vector >::get_dimk() const = delete; - template <> - size_t ArrayND<1, std::vector >::get_diml() const = delete; - template <> - size_t ArrayND<1, std::vector >::get_dimm() const = delete; - template <> - size_t ArrayND<2, std::vector >::get_dimk() const = delete; - template <> - size_t ArrayND<2, std::vector >::get_diml() const = delete; - template <> - size_t ArrayND<2, std::vector >::get_dimm() const = delete; - template <> - size_t ArrayND<3, std::vector >::get_diml() const = delete; - template <> - size_t ArrayND<3, std::vector >::get_dimm() const = delete; - template <> - size_t ArrayND<4, std::vector >::get_dimm() const = delete; +template <> +inline void ArrayND<2, std::vector>::toMRC_f(const char *filename) const +{ + // output to an MRC file in float format + // see http://bio3d.colorado.edu/imod/doc/mrc_format.txt for details + std::ofstream f(filename, std::ios::binary | std::ios::out); + if (f) + { + int int_header[56]; + char char_header[800]; + std::memset((void *)char_header, 0, 800); + std::memset((void *)int_header, 0, 56 * sizeof(int)); + int_header[0] = (int)dims[1]; //nx + int_header[1] = (int)dims[0]; //ny + int_header[2] = (int)1; //nz + int_header[3] = 2; //mode, float + f.write((char *)int_header, 56 * 4); //use 4 instead of sizeof(int) because architecture may change but file format won't + f.write(char_header, 800); + float *data_buffer = new float[this->size()]; + for (auto i = 0; i < this->size(); ++i) + data_buffer[i] = (float)data[i]; + f.write((char *)data_buffer, this->size() * sizeof(float)); + delete[] data_buffer; + } +} +template <> +inline void ArrayND<2, std::vector>::toMRC_f(const char *filename) const +{ + // output to an MRC file in float format + // see http://bio3d.colorado.edu/imod/doc/mrc_format.txt for details + std::ofstream f(filename, std::ios::binary | std::ios::out); + if (f) + { + int int_header[56]; + char char_header[800]; + std::memset((void *)char_header, 0, 800); + std::memset((void *)int_header, 0, 56 * sizeof(int)); + int_header[0] = (int)dims[1]; //nx + int_header[1] = (int)dims[0]; //ny + int_header[2] = (int)1; //nz + int_header[3] = 2; //mode, float + f.write((char *)int_header, 56 * 4); //use 4 instead of sizeof(int) because architecture may change but file format won't + f.write(char_header, 800); + float *data_buffer = new float[this->size()]; + for (auto i = 0; i < this->size(); ++i) + data_buffer[i] = (float)data[i]; + f.write((char *)data_buffer, this->size() * sizeof(float)); + delete[] data_buffer; + } } +// We want to prevents programming errors like querying the size of the 3rd dimension of +// a 2D object. Rather than introducing a check at runtime, I'll just delete them. Unfortunately, +// this does require full template instantiation, so each type has to be done separately. +template <> +size_t ArrayND<1, std::vector>::get_dimj() const = delete; +template <> +size_t ArrayND<1, std::vector>::get_dimk() const = delete; +template <> +size_t ArrayND<1, std::vector>::get_diml() const = delete; +template <> +size_t ArrayND<1, std::vector>::get_dimm() const = delete; +template <> +size_t ArrayND<2, std::vector>::get_dimk() const = delete; +template <> +size_t ArrayND<2, std::vector>::get_diml() const = delete; +template <> +size_t ArrayND<2, std::vector>::get_dimm() const = delete; +template <> +size_t ArrayND<3, std::vector>::get_diml() const = delete; +template <> +size_t ArrayND<3, std::vector>::get_dimm() const = delete; +template <> +size_t ArrayND<4, std::vector>::get_dimm() const = delete; + +template <> +size_t ArrayND<1, std::vector>>::get_dimj() const = delete; +template <> +size_t ArrayND<1, std::vector>>::get_dimk() const = delete; +template <> +size_t ArrayND<1, std::vector>>::get_diml() const = delete; +template <> +size_t ArrayND<1, std::vector>>::get_dimm() const = delete; +template <> +size_t ArrayND<2, std::vector>>::get_dimk() const = delete; +template <> +size_t ArrayND<2, std::vector>>::get_diml() const = delete; +template <> +size_t ArrayND<2, std::vector>>::get_dimm() const = delete; +template <> +size_t ArrayND<3, std::vector>>::get_diml() const = delete; +template <> +size_t ArrayND<3, std::vector>>::get_dimm() const = delete; +template <> +size_t ArrayND<4, std::vector>>::get_dimm() const = delete; + +template <> +size_t ArrayND<1, std::vector>::get_dimj() const = delete; +template <> +size_t ArrayND<1, std::vector>::get_dimk() const = delete; +template <> +size_t ArrayND<1, std::vector>::get_diml() const = delete; +template <> +size_t ArrayND<1, std::vector>::get_dimm() const = delete; +template <> +size_t ArrayND<2, std::vector>::get_dimk() const = delete; +template <> +size_t ArrayND<2, std::vector>::get_diml() const = delete; +template <> +size_t ArrayND<2, std::vector>::get_dimm() const = delete; +template <> +size_t ArrayND<3, std::vector>::get_diml() const = delete; +template <> +size_t ArrayND<3, std::vector>::get_dimm() const = delete; +template <> +size_t ArrayND<4, std::vector>::get_dimm() const = delete; + +template <> +size_t ArrayND<1, std::vector>>::get_dimj() const = delete; +template <> +size_t ArrayND<1, std::vector>>::get_dimk() const = delete; +template <> +size_t ArrayND<1, std::vector>>::get_diml() const = delete; +template <> +size_t ArrayND<1, std::vector>>::get_dimm() const = delete; +template <> +size_t ArrayND<2, std::vector>>::get_dimk() const = delete; +template <> +size_t ArrayND<2, std::vector>>::get_diml() const = delete; +template <> +size_t ArrayND<2, std::vector>>::get_dimm() const = delete; +template <> +size_t ArrayND<3, std::vector>>::get_diml() const = delete; +template <> +size_t ArrayND<3, std::vector>>::get_dimm() const = delete; +template <> +size_t ArrayND<4, std::vector>>::get_dimm() const = delete; + +template <> +size_t ArrayND<1, std::vector>::get_dimj() const = delete; +template <> +size_t ArrayND<1, std::vector>::get_dimk() const = delete; +template <> +size_t ArrayND<1, std::vector>::get_diml() const = delete; +template <> +size_t ArrayND<1, std::vector>::get_dimm() const = delete; +template <> +size_t ArrayND<2, std::vector>::get_dimk() const = delete; +template <> +size_t ArrayND<2, std::vector>::get_diml() const = delete; +template <> +size_t ArrayND<2, std::vector>::get_dimm() const = delete; +template <> +size_t ArrayND<3, std::vector>::get_diml() const = delete; +template <> +size_t ArrayND<3, std::vector>::get_dimm() const = delete; +template <> +size_t ArrayND<4, std::vector>::get_dimm() const = delete; + +} // namespace Prismatic #endif //PRISM_ARRAYND_H diff --git a/include/Multislice_calcOutput.h b/include/Multislice_calcOutput.h index c6052d7de..b63a688ee 100644 --- a/include/Multislice_calcOutput.h +++ b/include/Multislice_calcOutput.h @@ -27,49 +27,49 @@ #include "fftw3.h" #include "WorkDispatcher.h" -namespace Prismatic{ - using namespace std; - void setupCoordinates_multislice(Parameters& pars); +namespace Prismatic +{ +using namespace std; +void setupCoordinates_multislice(Parameters &pars); - void setupDetector_multislice(Parameters& pars); +void setupDetector_multislice(Parameters &pars); - void setupProbes_multislice(Parameters& pars); +void setupProbes_multislice(Parameters &pars); - void createTransmission(Parameters& pars); +void createTransmission(Parameters &pars); - void createStack(Parameters& pars); +void createStack(Parameters &pars); - void formatOutput_CPU_integrate(Parameters& pars, - Array2D< complex >& psi, - const Array2D &alphaInd, - const size_t currentSlice, - const size_t ay, - const size_t ax); +void formatOutput_CPU_integrate(Parameters &pars, + Array2D> &psi, + const Array2D &alphaInd, + const size_t currentSlice, + const size_t ay, + const size_t ax); - void formatOutput_CPU_integrate_batch(Parameters& pars, - Array1D< complex >& psi_stack, - const Array2D &alphaInd, - const size_t currentSlice, - const size_t ay, - const size_t ax); +void formatOutput_CPU_integrate_batch(Parameters &pars, + Array1D> &psi_stack, + const Array2D &alphaInd, + const size_t currentSlice, + const size_t ay, + const size_t ax); - std::pair >, Prismatic::Array2D< std::complex > > - getSingleMultisliceProbe_CPU(Parameters &pars, const PRISMATIC_FLOAT_PRECISION xp, const PRISMATIC_FLOAT_PRECISION yp); - void getMultisliceProbe_CPU_batch(Parameters& pars, - const size_t Nstart, - const size_t Nstop, - PRISMATIC_FFTW_PLAN& plan_forward, - PRISMATIC_FFTW_PLAN& plan_inverse, - Array1D >& psi_stack); - void getMultisliceProbe_CPU(Parameters& pars, - const size_t ay, - const size_t ax, - PRISMATIC_FFTW_PLAN& plan_forward, - PRISMATIC_FFTW_PLAN& plan_inverse, - Array2D >& psi); - void buildMultisliceOutput_CPUOnly(Parameters& pars); +std::pair>, Prismatic::Array2D>> +getSingleMultisliceProbe_CPU(Parameters &pars, const PRISMATIC_FLOAT_PRECISION xp, const PRISMATIC_FLOAT_PRECISION yp); +void getMultisliceProbe_CPU_batch(Parameters &pars, + const size_t Nstart, + const size_t Nstop, + PRISMATIC_FFTW_PLAN &plan_forward, + PRISMATIC_FFTW_PLAN &plan_inverse, + Array1D> &psi_stack); +void getMultisliceProbe_CPU(Parameters &pars, + const size_t ay, + const size_t ax, + PRISMATIC_FFTW_PLAN &plan_forward, + PRISMATIC_FFTW_PLAN &plan_inverse, + Array2D> &psi); +void buildMultisliceOutput_CPUOnly(Parameters &pars); - - void Multislice_calcOutput(Parameters& pars); -} +void Multislice_calcOutput(Parameters &pars); +} // namespace Prismatic #endif //PRISMATIC_MULTISLICE_H diff --git a/include/PRISM01_calcPotential.h b/include/PRISM01_calcPotential.h index c30b8ab08..4832e0ef6 100644 --- a/include/PRISM01_calcPotential.h +++ b/include/PRISM01_calcPotential.h @@ -27,27 +27,26 @@ #include "ArrayND.h" #include "projectedPotential.h" - - namespace Prismatic { - void fetch_potentials(Array3D& potentials, - const std::vector& atomic_species, - const Array1D& xr, - const Array1D& yr); - - std::vector get_unique_atomic_species(Parameters& pars); - - void generateProjectedPotentials(Parameters& pars, - const Array3D& potLookup, - const std::vector& unique_species, - const Array1D& xvec, - const Array1D& yvec, - const Array1D& uLookup); - +namespace Prismatic +{ +void fetch_potentials(Array3D &potentials, + const std::vector &atomic_species, + const Array1D &xr, + const Array1D &yr); + +std::vector get_unique_atomic_species(Parameters &pars); + +void generateProjectedPotentials(Parameters &pars, + const Array3D &potLookup, + const std::vector &unique_species, + const Array1D &xvec, + const Array1D &yvec, + const Array1D &uLookup); //#ifdef PRISMATIC_BUILDING_GUI // void PRISM01_calcPotential(Parameters& pars, prism_progressbar *progressbar=NULL); //#else - void PRISM01_calcPotential(Parameters& pars); +void PRISM01_calcPotential(Parameters &pars); //#endif //PRISMATIC_ENABLE_GPU -} +} // namespace Prismatic #endif //PRISMATIC_PRISM01_H diff --git a/include/PRISM03_calcOutput.h b/include/PRISM03_calcOutput.h index 80e33aa5b..eb3380d76 100644 --- a/include/PRISM03_calcOutput.h +++ b/include/PRISM03_calcOutput.h @@ -23,28 +23,29 @@ #include "fftw3.h" #include "utility.h" -namespace Prismatic { - Array2D array2D_subset(const Array2D &arr, - const size_t &starty, const size_t &stepy, const size_t &stopy, - const size_t &startx, const size_t &stepx, const size_t &stopx); +namespace Prismatic +{ +Array2D array2D_subset(const Array2D &arr, + const size_t &starty, const size_t &stepy, const size_t &stopy, + const size_t &startx, const size_t &stepx, const size_t &stopx); - void setupCoordinates_2(Parameters &pars); - void setupDetector(Parameters &pars); - void setupBeams_2(Parameters &pars); - void createStack_integrate(Parameters &pars); - void setupFourierCoordinates(Parameters &pars); - void transformIndices(Parameters &pars); - void initializeProbes(Parameters &pars); - std::pair >, Prismatic::Array2D< std::complex > > - getSinglePRISMProbe_CPU(Parameters &pars, const PRISMATIC_FLOAT_PRECISION xp, const PRISMATIC_FLOAT_PRECISION yp); - void buildSignal_CPU(Parameters &pars, - const size_t &ay, - const size_t &ax, - PRISMATIC_FFTW_PLAN& plan, - Array2D >& psi); +void setupCoordinates_2(Parameters &pars); +void setupDetector(Parameters &pars); +void setupBeams_2(Parameters &pars); +void createStack_integrate(Parameters &pars); +void setupFourierCoordinates(Parameters &pars); +void transformIndices(Parameters &pars); +void initializeProbes(Parameters &pars); +std::pair>, Prismatic::Array2D>> +getSinglePRISMProbe_CPU(Parameters &pars, const PRISMATIC_FLOAT_PRECISION xp, const PRISMATIC_FLOAT_PRECISION yp); +void buildSignal_CPU(Parameters &pars, + const size_t &ay, + const size_t &ax, + PRISMATIC_FFTW_PLAN &plan, + Array2D> &psi); - void buildPRISMOutput_CPUOnly(Parameters& pars); +void buildPRISMOutput_CPUOnly(Parameters &pars); - void PRISM03_calcOutput(Parameters &pars); -} +void PRISM03_calcOutput(Parameters &pars); +} // namespace Prismatic #endif //PRISMATIC_PRISM03_H diff --git a/include/atom.h b/include/atom.h index e4a85675d..c9e82cbb4 100644 --- a/include/atom.h +++ b/include/atom.h @@ -18,14 +18,16 @@ #include #include -struct atom{ +struct atom +{ double x; double y; double z; size_t species; double sigma; double occ; - void to_string() const{ + void to_string() const + { std::cout << "x = " << x << std::endl; std::cout << "y = " << y << std::endl; std::cout << "z = " << z << std::endl; @@ -35,25 +37,26 @@ struct atom{ } }; -namespace Prismatic { +namespace Prismatic +{ - void to_xyz(const std::vector atoms, const std::string filename, const std::string comment, double a, double b, double c); +void to_xyz(const std::vector atoms, const std::string filename, const std::string comment, double a, double b, double c); - std::vector tileAtoms(const size_t tileX, const size_t tileY, const size_t tileZ, std::vector atoms); +std::vector tileAtoms(const size_t tileX, const size_t tileY, const size_t tileZ, std::vector atoms); - std::vector readAtoms(const std::string& filename); +std::vector readAtoms(const std::string &filename); // std::array peekDims(const std::string& filename); - std::array peekDims_xyz(const std::string& filename); +std::array peekDims_xyz(const std::string &filename); // std::vector readAtoms_csv(const std::string& filename); - std::vector readAtoms_xyz(const std::string& filename); +std::vector readAtoms_xyz(const std::string &filename); - std::string getLowercaseExtension(const std::string filename); +std::string getLowercaseExtension(const std::string filename); - std::vector defaultAtoms(); +std::vector defaultAtoms(); -} +} // namespace Prismatic #endif //PRISM_ATOM_H \ No newline at end of file diff --git a/include/defines.h b/include/defines.h index 237137764..b5389940c 100644 --- a/include/defines.h +++ b/include/defines.h @@ -16,23 +16,28 @@ #include -namespace Prismatic { - - enum class Algorithm { - PRISM, Multislice - }; - - inline void printHeader(){ - std::cout << "\n\n*********************************************************************" << std::endl; - std::cout << " ____ _ __ _ \n" - " / __ \\ _____ (_) _____ ____ ___ ____ _ / /_ (_) _____\n" - " / /_/ / / ___/ / / / ___/ / __ `__ \\ / __ `/ / __/ / / / ___/\n" - " / ____/ / / / / (__ ) / / / / / // /_/ / / /_ / / / /__ \n" - "/_/ /_/ /_/ /____/ /_/ /_/ /_/ \\__,_/ \\__/ /_/ \\___/\n\n"; - std::cout << "*********************************************************************\n\n"; - std::cout << "Author: Alan \"AJ\" Pryor (apryor6@gmail.com)\nbased on work by Colin Ophus (clophus@lbl.gov)\n" << std::endl; - } +namespace Prismatic +{ + +enum class Algorithm +{ + PRISM, + Multislice +}; + +inline void printHeader() +{ + std::cout << "\n\n*********************************************************************" << std::endl; + std::cout << " ____ _ __ _ \n" + " / __ \\ _____ (_) _____ ____ ___ ____ _ / /_ (_) _____\n" + " / /_/ / / ___/ / / / ___/ / __ `__ \\ / __ `/ / __/ / / / ___/\n" + " / ____/ / / / / (__ ) / / / / / // /_/ / / /_ / / / /__ \n" + "/_/ /_/ /_/ /____/ /_/ /_/ /_/ \\__,_/ \\__/ /_/ \\___/\n\n"; + std::cout << "*********************************************************************\n\n"; + std::cout << "Author: Alan \"AJ\" Pryor (apryor6@gmail.com)\nbased on work by Colin Ophus (clophus@lbl.gov)\n" + << std::endl; } +} // namespace Prismatic #ifdef PRISMATIC_ENABLE_GPU #include "cuComplex.h" #include @@ -46,22 +51,32 @@ namespace Prismatic { #define PI 3.14159265359 // helpful function for checking CUDA errors. // Source: http://stackoverflow.com/questions/14038589/what-is-the-canonical-way-to-check-for-errors-using-the-cuda-runtime-api -#define cudaErrchk(ans) { GPUAssert((ans), __FILE__, __LINE__); } -inline void GPUAssert(cudaError_t code, const char *file, int line, bool abort=true){ +#define cudaErrchk(ans) \ + { \ + GPUAssert((ans), __FILE__, __LINE__); \ + } +inline void GPUAssert(cudaError_t code, const char *file, int line, bool abort = true) +{ if (code != cudaSuccess) { - fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line); - if (abort) exit(code); + fprintf(stderr, "GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line); + if (abort) + exit(code); } } // helpful function for checking cuFFT errors -#define cufftErrchk(ans) { GPUAssert_cufft((ans), __FILE__, __LINE__); } -inline void GPUAssert_cufft(int code, const char *file, int line, bool abort=true){ +#define cufftErrchk(ans) \ + { \ + GPUAssert_cufft((ans), __FILE__, __LINE__); \ + } +inline void GPUAssert_cufft(int code, const char *file, int line, bool abort = true) +{ if (code != CUFFT_SUCCESS) { - fprintf(stderr,"GPUassert: %i %s %d\n", code, file, line); - if (abort) exit(code); + fprintf(stderr, "GPUassert: %i %s %d\n", code, file, line); + if (abort) + exit(code); } } @@ -81,12 +96,10 @@ typedef cuFloatComplex PRISMATIC_CUDA_COMPLEX_FLOAT; #endif //PRISMATIC_ENABLE_GPU - - //#define PRISMATIC_ENABLE_DOUBLE_PRECISION #ifdef PRISMATIC_ENABLE_DOUBLE_PRECISION #define MESSAGE "DOUBLE PRECISION" - typedef double PRISMATIC_FLOAT_PRECISION; +typedef double PRISMATIC_FLOAT_PRECISION; #define PRISMATIC_FFTW_PLAN fftw_plan #define PRISMATIC_FFTW_PLAN_DFT_2D fftw_plan_dft_2d #define PRISMATIC_FFTW_PLAN_DFT_BATCH fftw_plan_many_dft @@ -94,11 +107,11 @@ typedef cuFloatComplex PRISMATIC_CUDA_COMPLEX_FLOAT; #define PRISMATIC_FFTW_DESTROY_PLAN fftw_destroy_plan #define PRISMATIC_FFTW_COMPLEX fftw_complex #define PRISMATIC_FFTW_INIT_THREADS fftw_init_threads -#define PRISMATIC_FFTW_PLAN_WITH_NTHREADS fftw_plan_with_nthreads +#define PRISMATIC_FFTW_PLAN_WITH_NTHREADS fftw_plan_with_nthreads #define PRISMATIC_FFTW_CLEANUP_THREADS fftw_cleanup_threads #else - typedef float PRISMATIC_FLOAT_PRECISION; +typedef float PRISMATIC_FLOAT_PRECISION; #define MESSAGE "FLOAT PRECISION" #define PRISMATIC_FFTW_PLAN fftwf_plan #define PRISMATIC_FFTW_PLAN_DFT_2D fftwf_plan_dft_2d @@ -107,7 +120,7 @@ typedef cuFloatComplex PRISMATIC_CUDA_COMPLEX_FLOAT; #define PRISMATIC_FFTW_DESTROY_PLAN fftwf_destroy_plan #define PRISMATIC_FFTW_COMPLEX fftwf_complex #define PRISMATIC_FFTW_INIT_THREADS fftwf_init_threads -#define PRISMATIC_FFTW_PLAN_WITH_NTHREADS fftwf_plan_with_nthreads +#define PRISMATIC_FFTW_PLAN_WITH_NTHREADS fftwf_plan_with_nthreads #define PRISMATIC_FFTW_CLEANUP_THREADS fftwf_cleanup_threads #endif //PRISMATIC_ENABLE_DOUBLE_PRECISION diff --git a/include/go.h b/include/go.h index 430ffd545..6da7e113c 100644 --- a/include/go.h +++ b/include/go.h @@ -18,15 +18,16 @@ #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && defined(BUILDING_CUPRISMATIC) #ifdef CUPRISMATIC_EXPORT - #define PRISMATIC_API __declspec(dllexport) - #else - #define PRISMATIC_API __declspec(dllimport) - #endif //CUPRISMATIC_BUILDING +#define PRISMATIC_API __declspec(dllexport) +#else +#define PRISMATIC_API __declspec(dllimport) +#endif //CUPRISMATIC_BUILDING #else #define PRISMATIC_API #endif -namespace Prismatic{ - PRISMATIC_API void go(Metadata meta); +namespace Prismatic +{ +PRISMATIC_API void go(Metadata meta); } #endif //PRISM_GO_H diff --git a/include/parseInput.h b/include/parseInput.h index f23ffb3f0..39a8fbaf3 100644 --- a/include/parseInput.h +++ b/include/parseInput.h @@ -16,15 +16,16 @@ #include "defines.h" #include "meta.h" -namespace Prismatic{ - bool parseInputs(Metadata& meta, - int& argc, const char*** argv); - bool parseInput(Metadata& meta, - int& argc, const char*** argv); - bool parseParamFile(Metadata& meta, - const std::string param_filename); - bool writeParamFile(Metadata& meta, - const std::string param_filename); - void printHelp(); -} +namespace Prismatic +{ +bool parseInputs(Metadata &meta, + int &argc, const char ***argv); +bool parseInput(Metadata &meta, + int &argc, const char ***argv); +bool parseParamFile(Metadata &meta, + const std::string param_filename); +bool writeParamFile(Metadata &meta, + const std::string param_filename); +void printHelp(); +} // namespace Prismatic #endif //PRISM_PARSEINPUT_H diff --git a/include/prism_qthreads.h b/include/prism_qthreads.h index 1363dce74..1adf7a0e1 100644 --- a/include/prism_qthreads.h +++ b/include/prism_qthreads.h @@ -22,41 +22,46 @@ // defines QThread derived classes for running work from the PRISM GUI //class PRISMMainWindow; -class PotentialThread : public QThread { +class PotentialThread : public QThread +{ Q_OBJECT void run() Q_DECL_OVERRIDE; friend class PRISMMainWindow; + public: explicit PotentialThread(PRISMMainWindow *_parent, prism_progressbar *progressbar); virtual ~PotentialThread(); -//signals: -// void potentialCalculated(); + //signals: + // void potentialCalculated(); private: Prismatic::Metadata meta; PRISMMainWindow *parent; prism_progressbar *progressbar; }; - -class SMatrixThread : public QThread { +class SMatrixThread : public QThread +{ Q_OBJECT void run() Q_DECL_OVERRIDE; friend class PRISMMainWindow; + public: explicit SMatrixThread(PRISMMainWindow *_parent, prism_progressbar *progressbar); virtual ~SMatrixThread(); -//signals: -// void ScompactCalculated(); + //signals: + // void ScompactCalculated(); private: Prismatic::Metadata meta; PRISMMainWindow *parent; prism_progressbar *progressbar; }; -class FullCalcThread : public QThread { +class FullCalcThread : public QThread +{ Q_OBJECT void run() Q_DECL_OVERRIDE; friend class PRISMMainWindow; + public: explicit FullCalcThread(PRISMMainWindow *_parent, prism_progressbar *progressbar); virtual ~FullCalcThread(); @@ -64,6 +69,7 @@ class FullCalcThread : public QThread { void potentialCalculated(); void ScompactCalculated(); void stackCalculated(); + private: Prismatic::Metadata meta; PRISMMainWindow *parent; diff --git a/include/utility.h b/include/utility.h index 27277900d..4f51da639 100644 --- a/include/utility.h +++ b/include/utility.h @@ -24,130 +24,142 @@ #include "fftw3.h" #include "configure.h" -namespace Prismatic { - //inline void printTime(){ - //auto t = std::time(nullptr); - //auto tm = *std::localtime(&t); - //std::cout << "Current time: " << std::put_time(&tm, "%F %H:%M:%S") << std::endl; - //} - extern std::mutex fftw_plan_lock; // for synchronizing access to shared FFTW resources - - - template - std::vector vecFromRange(const T &start, const T &step, const T &stop) { - std::vector result; - for (auto i = start; i <= stop; i += step) { - result.push_back(i); - } - if (result.empty())result.push_back(start); - return result; - }; - - template - Array1D makeFourierCoords(const size_t &N, const T &pixel_size) { - Array1D result = zeros_ND<1, T>({{N}}); - long long nc = (size_t) floor((T) N / 2); - - T dp = 1 / (N * pixel_size); - for (auto i = 0; i < N; ++i) { - result[(nc + (size_t) i) % N] = (i - nc) * dp; - } - return result; - }; - - template - Array2D fftshift2(Array2D arr) { - Array2D result(arr); - const long sj = std::floor(arr.get_dimj() / 2); - const long si = std::floor(arr.get_dimi() / 2); - for (auto j = 0; j < arr.get_dimj(); ++j) { - for (auto i = 0; i < arr.get_dimi(); ++i) { - result.at((j + sj) % arr.get_dimj(),(i + si) % arr.get_dimi()) = arr.at(j,i); - } - } - return result; - }; - - template - Array1D fftshift(Array1D arr) { - Array1D result(arr); - const long si = std::floor(arr.get_dimi() / 2); - for (auto i = 0; i < arr.get_dimi(); ++i) { - result.at((i + si) % arr.get_dimi()) = arr.at(i); +namespace Prismatic +{ +//inline void printTime(){ +//auto t = std::time(nullptr); +//auto tm = *std::localtime(&t); +//std::cout << "Current time: " << std::put_time(&tm, "%F %H:%M:%S") << std::endl; +//} +extern std::mutex fftw_plan_lock; // for synchronizing access to shared FFTW resources + +template +std::vector vecFromRange(const T &start, const T &step, const T &stop) +{ + std::vector result; + for (auto i = start; i <= stop; i += step) + { + result.push_back(i); + } + if (result.empty()) + result.push_back(start); + return result; +}; + +template +Array1D makeFourierCoords(const size_t &N, const T &pixel_size) +{ + Array1D result = zeros_ND<1, T>({{N}}); + long long nc = (size_t)floor((T)N / 2); + + T dp = 1 / (N * pixel_size); + for (auto i = 0; i < N; ++i) + { + result[(nc + (size_t)i) % N] = (i - nc) * dp; + } + return result; +}; + +template +Array2D fftshift2(Array2D arr) +{ + Array2D result(arr); + const long sj = std::floor(arr.get_dimj() / 2); + const long si = std::floor(arr.get_dimi() / 2); + for (auto j = 0; j < arr.get_dimj(); ++j) + { + for (auto i = 0; i < arr.get_dimi(); ++i) + { + result.at((j + sj) % arr.get_dimj(), (i + si) % arr.get_dimi()) = arr.at(j, i); } - return result; - }; + } + return result; +}; + +template +Array1D fftshift(Array1D arr) +{ + Array1D result(arr); + const long si = std::floor(arr.get_dimi() / 2); + for (auto i = 0; i < arr.get_dimi(); ++i) + { + result.at((i + si) % arr.get_dimi()) = arr.at(i); + } + return result; +}; - template - std::string generateFilename(const Parameters &pars, const size_t currentSlice, const size_t ay, const size_t ax) { - std::string result = pars.meta.outputFolder + pars.meta.filenameOutput.substr(0, pars.meta.filenameOutput.find_last_of(".")); - std::stringstream ss; +template +std::string generateFilename(const Parameters &pars, const size_t currentSlice, const size_t ay, const size_t ax) +{ + std::string result = pars.meta.outputFolder + pars.meta.filenameOutput.substr(0, pars.meta.filenameOutput.find_last_of(".")); + std::stringstream ss; - if( (pars.meta.algorithm == Algorithm::PRISM) || (pars.meta.numSlices == 0) ) { + if ((pars.meta.algorithm == Algorithm::PRISM) || (pars.meta.numSlices == 0)) + { ss << "_X" << ax << "_Y" << ay << "_FP" << pars.meta.fpNum; - }else{ - ss << "_slice" << currentSlice << "_X" << ax << "_Y" << ay << "_FP" << pars.meta.fpNum; - } - //result += "_X" + std::string(ax) + "_Y" + std::string(ay) + "_FP" + std::string(pars.meta.fpNum); - result += ss.str() + pars.meta.filenameOutput.substr(pars.meta.filenameOutput.find_last_of(".")); - return result; - } + else + { + ss << "_slice" << currentSlice << "_X" << ax << "_Y" << ay << "_FP" << pars.meta.fpNum; + } + //result += "_X" + std::string(ax) + "_Y" + std::string(ay) + "_FP" + std::string(pars.meta.fpNum); + result += ss.str() + pars.meta.filenameOutput.substr(pars.meta.filenameOutput.find_last_of(".")); + return result; +} - std::pair >, Prismatic::Array2D > > - upsamplePRISMProbe(Prismatic::Array2D > probe, - const long dimj, const long dimi, long ys=0, long xs=0); +std::pair>, Prismatic::Array2D>> +upsamplePRISMProbe(Prismatic::Array2D> probe, + const long dimj, const long dimi, long ys = 0, long xs = 0); - PRISMATIC_FLOAT_PRECISION computePearsonCorrelation(Prismatic::Array2D > left, - Prismatic::Array2D > right); - PRISMATIC_FLOAT_PRECISION computeRfactor(Prismatic::Array2D > left, - Prismatic::Array2D > right); +PRISMATIC_FLOAT_PRECISION computePearsonCorrelation(Prismatic::Array2D> left, + Prismatic::Array2D> right); +PRISMATIC_FLOAT_PRECISION computeRfactor(Prismatic::Array2D> left, + Prismatic::Array2D> right); - - int nyquistProbes(Prismatic::Parameters pars, size_t dim); +int nyquistProbes(Prismatic::Parameters pars, size_t dim); - std::string remove_extension(const std::string& filename); +std::string remove_extension(const std::string &filename); - int testFilenameOutput(const std::string& filename); - int testWrite(const std::string& filename); - int testExist(const std::string& filename); +int testFilenameOutput(const std::string &filename); +int testWrite(const std::string &filename); +int testExist(const std::string &filename); - void setupOutputFile(Prismatic::Parameters prismatic_pars); +void setupOutputFile(Prismatic::Parameters prismatic_pars); - void setup4DOutput(Prismatic::Parameters pars, const size_t numLayers, const float dummy); +void setup4DOutput(Prismatic::Parameters pars, const size_t numLayers, const float dummy); - void setup4DOutput(Prismatic::Parameters pars, const size_t numLayers, const double dummy); +void setup4DOutput(Prismatic::Parameters pars, const size_t numLayers, const double dummy); - void setupVDOutput(Prismatic::Parameters pars, const size_t numLayers, const float dummy); +void setupVDOutput(Prismatic::Parameters pars, const size_t numLayers, const float dummy); - void setupVDOutput(Prismatic::Parameters pars, const size_t numLayers, const double dummy); +void setupVDOutput(Prismatic::Parameters pars, const size_t numLayers, const double dummy); - void setup2DOutput(Prismatic::Parameters pars, const size_t numLayers, const float dummy); +void setup2DOutput(Prismatic::Parameters pars, const size_t numLayers, const float dummy); - void setup2DOutput(Prismatic::Parameters pars, const size_t numLayers, const double dummy); +void setup2DOutput(Prismatic::Parameters pars, const size_t numLayers, const double dummy); - void setupDPCOutput(Prismatic::Parameters pars, const size_t numLayers, const float dummy); +void setupDPCOutput(Prismatic::Parameters pars, const size_t numLayers, const float dummy); - void setupDPCOutput(Prismatic::Parameters pars, const size_t numLayers, const double dummy); +void setupDPCOutput(Prismatic::Parameters pars, const size_t numLayers, const double dummy); - void writeRealSlice(H5::DataSet dataset, const float* buffer, const hsize_t* mdims); +void writeRealSlice(H5::DataSet dataset, const float *buffer, const hsize_t *mdims); - void writeRealSlice(H5::DataSet dataset, const double* buffer, const hsize_t* mdims); +void writeRealSlice(H5::DataSet dataset, const double *buffer, const hsize_t *mdims); - void writeDatacube3D(H5::DataSet dataset, const float* buffer, const hsize_t* mdims); +void writeDatacube3D(H5::DataSet dataset, const float *buffer, const hsize_t *mdims); - void writeDatacube3D(H5::DataSet dataset, const double* buffer, const hsize_t* mdims); +void writeDatacube3D(H5::DataSet dataset, const double *buffer, const hsize_t *mdims); - void writeDatacube4D(H5::DataSet dataset, float* buffer, const hsize_t* mdims, const hsize_t* offset, const float numFP); - - void writeDatacube4D(H5::DataSet dataset, double* buffer, const hsize_t* mdims, const hsize_t* offset, const double numFP); +void writeDatacube4D(H5::DataSet dataset, float *buffer, const hsize_t *mdims, const hsize_t *offset, const float numFP); - std::string getDigitString(int digit); +void writeDatacube4D(H5::DataSet dataset, double *buffer, const hsize_t *mdims, const hsize_t *offset, const double numFP); - void writeMetadata(Prismatic::Parameters pars, float dummy); +std::string getDigitString(int digit); - void writeMetadata(Prismatic::Parameters pars, double dummy); +void writeMetadata(Prismatic::Parameters pars, float dummy); -} +void writeMetadata(Prismatic::Parameters pars, double dummy); + +} // namespace Prismatic #endif //PRISMATIC_UTILITY_H diff --git a/pyprismatic/__init__.py b/pyprismatic/__init__.py index 7ffda5f0e..8db9db3a5 100644 --- a/pyprismatic/__init__.py +++ b/pyprismatic/__init__.py @@ -9,16 +9,19 @@ # 2. Pryor, Jr., A., Ophus, C., and Miao, J.: A Streaming Multi-GPU # Implementation of Image Simulation Algorithms for Scanning -# Transmission Electron Microscopy. arXiv:1706.08563 (2017) +# Transmission Electron Microscopy. arXiv:1706.08563 (2017) -from . import core -from . import fileio +from . import core # noqa +from . import fileio # noqa from pyprismatic.params import Metadata + def demo(): - import os - with open('temp.XYZ', 'w') as fid: - fid.write("one unit cell of 100 silicon\n\ + import os + + with open("temp.XYZ", "w") as fid: + fid.write( + "one unit cell of 100 silicon\n\ 5.43 5.43 5.43\n\ 14 0.0000 0.0000 0.0000 1.0 0.076\n\ 14 2.7150 2.7150 0.0000 1.0 0.076\n\ @@ -28,16 +31,17 @@ def demo(): 14 0.0000 2.7150 2.7150 1.0 0.076\n\ 14 1.3575 1.3575 4.0725 1.0 0.076\n\ 14 4.0725 4.0725 4.0725 1.0 0.076\n\ --1") - meta = Metadata(filenameAtoms='temp.XYZ', - filenameOutput='output.mrc'); - meta.algorithm = 'multislice' - meta.go() - import numpy as np - from pyprismatic.fileio import readMRC - import matplotlib.pyplot as plt - result = readMRC("output.mrc") - plt.figure() - plt.imshow(np.squeeze(np.sum(result,axis=2))) - plt.show() - os.remove("temp.XYZ") \ No newline at end of file +-1" + ) + meta = Metadata(filenameAtoms="temp.XYZ", filenameOutput="output.mrc") + meta.algorithm = "multislice" + meta.go() + import numpy as np + from pyprismatic.fileio import readMRC + import matplotlib.pyplot as plt + + result = readMRC("output.mrc") + plt.figure() + plt.imshow(np.squeeze(np.sum(result, axis=2))) + plt.show() + os.remove("temp.XYZ") diff --git a/pyprismatic/core.cpp b/pyprismatic/core.cpp index 731b19499..bd1f0100c 100644 --- a/pyprismatic/core.cpp +++ b/pyprismatic/core.cpp @@ -20,181 +20,188 @@ #include "cuprismatic.h" #endif //PRISMATIC_ENABLE_GPU - -static PyObject* pyprismatic_core_go(PyObject *self, PyObject *args){ +static PyObject *pyprismatic_core_go(PyObject *self, PyObject *args) +{ Prismatic::Metadata meta; - int interpolationFactorY = 1; - int interpolationFactorX = 1; + int interpolationFactorY = 1; + int interpolationFactorX = 1; int randomSeed; - int numFP, batchSizeTargetCPU, batchSizeTargetGPU, - tileX, tileY, tileZ, - numGPUs, numStreamsPerGPU, numThreads,includeThermalEffects, alsoDoCPUWork, save2DOutput, - save3DOutput, save4DOutput,saveDPC_CoM,savePotentialSlices,nyquistSampling,numSlices; + int numFP, batchSizeTargetCPU, batchSizeTargetGPU, + tileX, tileY, tileZ, + numGPUs, numStreamsPerGPU, numThreads, includeThermalEffects, alsoDoCPUWork, save2DOutput, + save3DOutput, save4DOutput, saveDPC_CoM, savePotentialSlices, nyquistSampling, numSlices; char *filenameAtoms, *filenameOutput, *algorithm, *transferMode; double realspacePixelSizeX, realspacePixelSizeY, potBound, - sliceThickness, probeStepX, probeStepY, - cellDimX, cellDimY, cellDimZ, earlyCPUStopCount, E0, alphaBeamMax, - detectorAngleStep, probeDefocus, C3, - C5, probeSemiangle, probeXtilt, - probeYtilt, scanWindowXMin, scanWindowXMax, - scanWindowYMin, scanWindowYMax, - integrationAngleMin, integrationAngleMax,zStart,scanWindowXMin_r,scanWindowXMax_r, - scanWindowYMin_r,scanWindowYMax_r; - #ifdef PRISMATIC_ENABLE_GPU - std::cout <<"COMPILED FOR GPU" << std::endl; - #endif //PRISMATIC_ENABLE_GPU + sliceThickness, probeStepX, probeStepY, + cellDimX, cellDimY, cellDimZ, earlyCPUStopCount, E0, alphaBeamMax, + detectorAngleStep, probeDefocus, C3, + C5, probeSemiangle, probeXtilt, + probeYtilt, scanWindowXMin, scanWindowXMax, + scanWindowYMin, scanWindowYMax, + integrationAngleMin, integrationAngleMax, zStart, scanWindowXMin_r, scanWindowXMax_r, + scanWindowYMin_r, scanWindowYMax_r; +#ifdef PRISMATIC_ENABLE_GPU + std::cout << "COMPILED FOR GPU" << std::endl; +#endif //PRISMATIC_ENABLE_GPU if (!PyArg_ParseTuple( - args, "iissdddiddddiiiddiiiiiddddddddddddddispppppddsiiiddddd", - &interpolationFactorX, - &interpolationFactorY, - &filenameAtoms, - &filenameOutput, - &realspacePixelSizeX, - &realspacePixelSizeY, - &potBound, - &numFP, - &sliceThickness, - &cellDimX, - &cellDimY, - &cellDimZ, - &tileX, - &tileY, - &tileZ, - &E0, - &alphaBeamMax, - &numGPUs, - &numStreamsPerGPU, - &numThreads, - &batchSizeTargetCPU, - &batchSizeTargetGPU, - &earlyCPUStopCount, - &probeStepX, - &probeStepY, - &probeDefocus, - &C3, - &C5, - &probeSemiangle, - &detectorAngleStep, - &probeXtilt, - &probeYtilt, - &scanWindowXMin, - &scanWindowXMax, - &scanWindowYMin, - &scanWindowYMax, - &randomSeed, - &algorithm, - &includeThermalEffects, - &alsoDoCPUWork, - &save2DOutput, - &save3DOutput, - &save4DOutput, - &integrationAngleMin, - &integrationAngleMax, - &transferMode, - &saveDPC_CoM, - &savePotentialSlices, - &numSlices, - &zStart, - &scanWindowXMin_r, - &scanWindowXMax_r, - &scanWindowYMin_r, - &scanWindowYMax_r)){ + args, "iissdddiddddiiiddiiiiiddddddddddddddispppppddsiiiddddd", + &interpolationFactorX, + &interpolationFactorY, + &filenameAtoms, + &filenameOutput, + &realspacePixelSizeX, + &realspacePixelSizeY, + &potBound, + &numFP, + &sliceThickness, + &cellDimX, + &cellDimY, + &cellDimZ, + &tileX, + &tileY, + &tileZ, + &E0, + &alphaBeamMax, + &numGPUs, + &numStreamsPerGPU, + &numThreads, + &batchSizeTargetCPU, + &batchSizeTargetGPU, + &earlyCPUStopCount, + &probeStepX, + &probeStepY, + &probeDefocus, + &C3, + &C5, + &probeSemiangle, + &detectorAngleStep, + &probeXtilt, + &probeYtilt, + &scanWindowXMin, + &scanWindowXMax, + &scanWindowYMin, + &scanWindowYMax, + &randomSeed, + &algorithm, + &includeThermalEffects, + &alsoDoCPUWork, + &save2DOutput, + &save3DOutput, + &save4DOutput, + &integrationAngleMin, + &integrationAngleMax, + &transferMode, + &saveDPC_CoM, + &savePotentialSlices, + &numSlices, + &zStart, + &scanWindowXMin_r, + &scanWindowXMax_r, + &scanWindowYMin_r, + &scanWindowYMax_r)) + { return NULL; - } - meta.interpolationFactorX = interpolationFactorX; - meta.interpolationFactorY = interpolationFactorY; - meta.filenameAtoms = filenameAtoms; - meta.filenameOutput = filenameOutput; - meta.realspacePixelSize[0] = realspacePixelSizeY; - meta.realspacePixelSize[1] = realspacePixelSizeX; - meta.potBound = potBound; - meta.numFP = numFP; - meta.sliceThickness = sliceThickness; - meta.numSlices = numSlices; - meta.zStart = zStart; - meta.cellDim[2] = cellDimX; - meta.cellDim[1] = cellDimY; - meta.cellDim[0] = cellDimZ; - meta.tileX = tileX; - meta.tileY = tileY; - meta.tileZ = tileZ; - meta.E0 = E0; - meta.alphaBeamMax = alphaBeamMax; - meta.numGPUs = numGPUs; - meta.numStreamsPerGPU = numStreamsPerGPU; - meta.numThreads = numThreads; - meta.batchSizeTargetCPU = batchSizeTargetCPU; - meta.batchSizeTargetGPU = batchSizeTargetGPU; - meta.earlyCPUStopCount = earlyCPUStopCount; - meta.probeStepX = probeStepX; - meta.probeStepY = probeStepY; - meta.probeDefocus = probeDefocus; - meta.C3 = C3; - meta.C5 = C5; - meta.probeSemiangle = probeSemiangle; - meta.detectorAngleStep = detectorAngleStep; - meta.probeXtilt = probeXtilt; - meta.probeYtilt = probeYtilt; - meta.scanWindowXMin = scanWindowXMin; - meta.scanWindowXMax = scanWindowXMax; - meta.scanWindowYMin = scanWindowYMin; - meta.scanWindowYMax = scanWindowYMax; - meta.scanWindowXMin_r = scanWindowXMin_r; - meta.scanWindowXMax_r = scanWindowXMax_r; - meta.scanWindowYMin_r = scanWindowYMin_r; - meta.scanWindowYMax_r = scanWindowYMax_r; - meta.randomSeed = randomSeed; - if (std::string(algorithm) == "multislice"){ - meta.algorithm = Prismatic::Algorithm::Multislice; - } else { - meta.algorithm = Prismatic::Algorithm::PRISM; } - meta.includeThermalEffects = includeThermalEffects; - meta.alsoDoCPUWork = alsoDoCPUWork; - meta.save2DOutput = save2DOutput; - meta.save3DOutput = save3DOutput; - meta.save4DOutput = save4DOutput; - meta.savePotentialSlices = savePotentialSlices; - meta.saveDPC_CoM = saveDPC_CoM; - meta.integrationAngleMin = integrationAngleMin; - meta.integrationAngleMax = integrationAngleMax; - meta.nyquistSampling = nyquistSampling; - - if (std::string(transferMode) == "singlexfer"){ - meta.transferMode = Prismatic::StreamingMode::SingleXfer; - } else if (std::string(transferMode) == "streaming"){ - meta.transferMode = Prismatic::StreamingMode::Stream; - } else { - meta.transferMode = Prismatic::StreamingMode::Auto; + meta.interpolationFactorX = interpolationFactorX; + meta.interpolationFactorY = interpolationFactorY; + meta.filenameAtoms = filenameAtoms; + meta.filenameOutput = filenameOutput; + meta.realspacePixelSize[0] = realspacePixelSizeY; + meta.realspacePixelSize[1] = realspacePixelSizeX; + meta.potBound = potBound; + meta.numFP = numFP; + meta.sliceThickness = sliceThickness; + meta.numSlices = numSlices; + meta.zStart = zStart; + meta.cellDim[2] = cellDimX; + meta.cellDim[1] = cellDimY; + meta.cellDim[0] = cellDimZ; + meta.tileX = tileX; + meta.tileY = tileY; + meta.tileZ = tileZ; + meta.E0 = E0; + meta.alphaBeamMax = alphaBeamMax; + meta.numGPUs = numGPUs; + meta.numStreamsPerGPU = numStreamsPerGPU; + meta.numThreads = numThreads; + meta.batchSizeTargetCPU = batchSizeTargetCPU; + meta.batchSizeTargetGPU = batchSizeTargetGPU; + meta.earlyCPUStopCount = earlyCPUStopCount; + meta.probeStepX = probeStepX; + meta.probeStepY = probeStepY; + meta.probeDefocus = probeDefocus; + meta.C3 = C3; + meta.C5 = C5; + meta.probeSemiangle = probeSemiangle; + meta.detectorAngleStep = detectorAngleStep; + meta.probeXtilt = probeXtilt; + meta.probeYtilt = probeYtilt; + meta.scanWindowXMin = scanWindowXMin; + meta.scanWindowXMax = scanWindowXMax; + meta.scanWindowYMin = scanWindowYMin; + meta.scanWindowYMax = scanWindowYMax; + meta.scanWindowXMin_r = scanWindowXMin_r; + meta.scanWindowXMax_r = scanWindowXMax_r; + meta.scanWindowYMin_r = scanWindowYMin_r; + meta.scanWindowYMax_r = scanWindowYMax_r; + meta.randomSeed = randomSeed; + if (std::string(algorithm) == "multislice") + { + meta.algorithm = Prismatic::Algorithm::Multislice; + } + else + { + meta.algorithm = Prismatic::Algorithm::PRISM; + } + meta.includeThermalEffects = includeThermalEffects; + meta.alsoDoCPUWork = alsoDoCPUWork; + meta.save2DOutput = save2DOutput; + meta.save3DOutput = save3DOutput; + meta.save4DOutput = save4DOutput; + meta.savePotentialSlices = savePotentialSlices; + meta.saveDPC_CoM = saveDPC_CoM; + meta.integrationAngleMin = integrationAngleMin; + meta.integrationAngleMax = integrationAngleMax; + meta.nyquistSampling = nyquistSampling; + + if (std::string(transferMode) == "singlexfer") + { + meta.transferMode = Prismatic::StreamingMode::SingleXfer; + } + else if (std::string(transferMode) == "streaming") + { + meta.transferMode = Prismatic::StreamingMode::Stream; + } + else + { + meta.transferMode = Prismatic::StreamingMode::Auto; } // print metadata - meta.toString(); + meta.toString(); Prismatic::go(meta); // configure simulation behavior -// Prismatic::configure(meta); + // Prismatic::configure(meta); // execute simulation -// Prismatic::execute_plan(meta); + // Prismatic::execute_plan(meta); Py_RETURN_NONE; - } static PyMethodDef pyprismatic_core_methods[] = { - {"go",(PyCFunction)pyprismatic_core_go, METH_VARARGS, "Execute Prismatic calculation"}, - {NULL, NULL, 0, NULL} -}; - + {"go", (PyCFunction)pyprismatic_core_go, METH_VARARGS, "Execute Prismatic calculation"}, + {NULL, NULL, 0, NULL}}; static struct PyModuleDef module_def = { - PyModuleDef_HEAD_INIT,"pypristmatic.core","Python wrapper for Prismatic\ + PyModuleDef_HEAD_INIT, "pypristmatic.core", "Python wrapper for Prismatic\ package for fast image simulation using the PRISM and multislice\ - algorithms in Scanning Transmission Electron Microscopy (STEM)",-1,pyprismatic_core_methods -}; + algorithms in Scanning Transmission Electron Microscopy (STEM)", + -1, pyprismatic_core_methods}; -PyMODINIT_FUNC PyInit_core(){ +PyMODINIT_FUNC PyInit_core() +{ return PyModule_Create(&module_def); } diff --git a/pyprismatic/fileio.py b/pyprismatic/fileio.py index 2d2a7616d..74f8fc309 100644 --- a/pyprismatic/fileio.py +++ b/pyprismatic/fileio.py @@ -9,9 +9,10 @@ # 2. Pryor, Jr., A., Ophus, C., and Miao, J.: A Streaming Multi-GPU # Implementation of Image Simulation Algorithms for Scanning -# Transmission Electron Microscopy. arXiv:1706.08563 (2017) +# Transmission Electron Microscopy. arXiv:1706.08563 (2017) -def readMRC(filename, dtype=float, order="C"): + +def readMRC(filename: str, dtype: type = float, order: str = "C"): """ * readMRC * @@ -28,27 +29,36 @@ def readMRC(filename, dtype=float, order="C"): """ import numpy as np import struct + headerIntNumber = 56 sizeof_int = 4 headerCharNumber = 800 sizeof_char = 1 - with open(filename,'rb') as fid: - int_header = struct.unpack('=' + 'i'*headerIntNumber, fid.read(headerIntNumber * sizeof_int)) - char_header = struct.unpack('=' + 'c'*headerCharNumber, fid.read(headerCharNumber * sizeof_char)) - dimz, dimy, dimx, data_flag= int_header[:4] - if (data_flag == 0): - datatype='u1' - elif (data_flag ==1): - datatype='i1' - elif (data_flag ==2): - datatype='f4' - elif (data_flag ==3): - datatype='c' - elif (data_flag ==4): - datatype='f4' - elif (data_flag ==6): - datatype='u2' + with open(filename, "rb") as fid: + int_header = struct.unpack( + "=" + "i" * headerIntNumber, fid.read(headerIntNumber * sizeof_int) + ) + char_header = struct.unpack( + "=" + "c" * headerCharNumber, fid.read(headerCharNumber * sizeof_char) + ) + dimz, dimy, dimx, data_flag = int_header[:4] + if data_flag == 0: + datatype = "u1" + elif data_flag == 1: + datatype = "i1" + elif data_flag == 2: + datatype = "f4" + elif data_flag == 3: + datatype = "c" + elif data_flag == 4: + datatype = "f4" + elif data_flag == 6: + datatype = "u2" else: raise ValueError("No supported datatype found!\n") - return np.fromfile(file=fid, dtype=datatype,count=dimx*dimy*dimz).reshape((dimx,dimy,dimz),order=order).astype(dtype) + return ( + np.fromfile(file=fid, dtype=datatype, count=dimx * dimy * dimz) + .reshape((dimx, dimy, dimz), order=order) + .astype(dtype) + ) diff --git a/pyprismatic/params.py b/pyprismatic/params.py index e73cfaaf2..f8613e400 100644 --- a/pyprismatic/params.py +++ b/pyprismatic/params.py @@ -11,8 +11,11 @@ # Implementation of Image Simulation Algorithms for Scanning # Transmission Electron Microscopy. arXiv:1706.08563 (2017) +from typing import List, Any import pyprismatic.core -class Metadata(object): + + +class Metadata: """ "interpolationFactorX" : PRISM interpolation factor in x-direction "interpolationFactorY" : PRISM interpolation factor in y-direction @@ -71,85 +74,110 @@ class Metadata(object): "transferMode" : memory model to use, either "streaming", "singlexfer", or "auto" """ - fields = ["interpolationFactorX", - "interpolationFactorY", - "filenameAtoms", - "filenameOutput", - "realspacePixelSizeX", - "realspacePixelSizeY", - "potBound", - "numFP", - "sliceThickness", - "numSlices", - "zStart", - "cellDimX", - "cellDimY", - "cellDimZ", - "tileX", - "tileY", - "tileZ", - "E0", - "alphaBeamMax", - "numGPUs", - "numStreamsPerGPU", - "numThreads", - "batchSizeTargetCPU", - "batchSizeTargetGPU", - "earlyCPUStopCount", - "probeStepX", - "probeStepY", - "probeDefocus", - "C3", - "C5", - "probeSemiangle", - "detectorAngleStep", - "probeXtilt", - "probeYtilt", - "scanWindowXMin", - "scanWindowXMax", - "scanWindowYMin", - "scanWindowYMax", - "scanWindowXMin_r", - "scanWindowXMax_r", - "scanWindowYMin_r", - "scanWindowYMax_r", - "randomSeed", - "algorithm", - "includeThermalEffects", - "alsoDoCPUWork", - "save2DOutput", - "save3DOutput", - "save4DOutput", - "saveDPC_CoM", - "savePotentialSlices", - "nyquistSampling", - "integrationAngleMin", - "integrationAngleMax", - "transferMode"] + fields: List[str] = [ + "interpolationFactorX", + "interpolationFactorY", + "filenameAtoms", + "filenameOutput", + "realspacePixelSizeX", + "realspacePixelSizeY", + "potBound", + "numFP", + "sliceThickness", + "numSlices", + "zStart", + "cellDimX", + "cellDimY", + "cellDimZ", + "tileX", + "tileY", + "tileZ", + "E0", + "alphaBeamMax", + "numGPUs", + "numStreamsPerGPU", + "numThreads", + "batchSizeTargetCPU", + "batchSizeTargetGPU", + "earlyCPUStopCount", + "probeStepX", + "probeStepY", + "probeDefocus", + "C3", + "C5", + "probeSemiangle", + "detectorAngleStep", + "probeXtilt", + "probeYtilt", + "scanWindowXMin", + "scanWindowXMax", + "scanWindowYMin", + "scanWindowYMax", + "scanWindowXMin_r", + "scanWindowXMax_r", + "scanWindowYMin_r", + "scanWindowYMax_r", + "randomSeed", + "algorithm", + "includeThermalEffects", + "alsoDoCPUWork", + "save2DOutput", + "save3DOutput", + "save4DOutput", + "saveDPC_CoM", + "savePotentialSlices", + "nyquistSampling", + "integrationAngleMin", + "integrationAngleMax", + "transferMode", + ] - str_fields = ['algorithm', 'transferMode'] + str_fields: List[str] = ["algorithm", "transferMode"] - int_fields = ['interpolationFactorX', 'interpolationFactorY', - 'tileX', 'tileY', 'tileZ', 'numFP' - 'numGPUs', 'numStreamsPerGPU', 'numThreads', - 'batchSizeTargetCPU', 'batchSizeTargetGPU', - 'batchSizeCPU', 'batchSizeGPU', - 'numSlices'] + int_fields: List[str] = [ + "interpolationFactorX", + "interpolationFactorY", + "tileX", + "tileY", + "tileZ", + "numFP" "numGPUs", + "numStreamsPerGPU", + "numThreads", + "batchSizeTargetCPU", + "batchSizeTargetGPU", + "batchSizeCPU", + "batchSizeGPU", + "numSlices", + ] - float_fields = ['realspacePixelSizeX', 'realspacePixelSizeY', - 'potBound', 'sliceThickness', - 'E0', 'alphaBeamMax' - 'earlyCPUStopCount', - 'probeStepX', 'probeStepY', - 'probeDefocus', 'C3', 'C5', - 'probeSemiangle', 'detectorAngleStep', - 'probeXtilt', 'probeYtilt', - 'scanWindowXMin', 'scanWindowXMax', - 'scanWindowYMin', 'scanWindowYMax', - 'scanWindowXMin_r', 'scanWindowXMax_r', - 'scanWindowYMin_r', 'scanWindowYMax_r', - 'integrationAngleMin', 'integrationAngleMax', - 'zStart'] + float_fields: List[str] = [ + "realspacePixelSizeX", + "realspacePixelSizeY", + "potBound", + "sliceThickness", + "E0", + "alphaBeamMax" "earlyCPUStopCount", + "probeStepX", + "probeStepY", + "probeDefocus", + "C3", + "C5", + "probeSemiangle", + "detectorAngleStep", + "probeXtilt", + "probeYtilt", + "scanWindowXMin", + "scanWindowXMax", + "scanWindowYMin", + "scanWindowYMax", + "scanWindowXMin_r", + "scanWindowXMax_r", + "scanWindowYMin_r", + "scanWindowYMax_r", + "integrationAngleMin", + "integrationAngleMax", + "zStart", + ] def __init__(self, *args, **kwargs): """ @@ -161,6 +189,7 @@ def __init__(self, *args, **kwargs): values 8 and "test.mrc" """ import numpy as np + self.interpolationFactorX = 4 self.interpolationFactorY = 4 self.filenameAtoms = "" @@ -216,22 +245,24 @@ def __init__(self, *args, **kwargs): self.savePotentialSlices = False self.nyquistSampling = False self.integrationAngleMin = 0 - self.integrationAngleMax = .001 + self.integrationAngleMax = 0.001 self.transferMode = "auto" for k, v in kwargs.items(): if k not in Metadata.fields: - print("Invalid metaparameter \"{}\" provided".format(k)) + print('Invalid metaparameter "{}" provided'.format(k)) else: setattr(self, k, v) - def _setCellDims(self, Fname): + def _setCellDims(self, filename: str): try: - inf = open(Fname, 'r') + inf = open(filename, "r") except IOError: - print('Could not set cell dimensions from file {}'.format(Fname)) + print("Could not set cell dimensions from file {}".format(filename)) return inf.readline() - self.cellDimX, self.cellDimY, self.cellDimZ = [float(i) for i in inf.readline().split()] + self.cellDimX, self.cellDimY, self.cellDimZ = [ + float(i) for i in inf.readline().split() + ] inf.close() @property @@ -240,23 +271,22 @@ def filenameAtoms(self): @filenameAtoms.setter def filenameAtoms(self, filenameAtoms): - if filenameAtoms != "": # do not set cell dimensions for default empty string + if filenameAtoms != "": # do not set cell dimensions for default empty string self._filenameAtoms = filenameAtoms self._setCellDims(filenameAtoms) - - def readParameters(self, Fname): - """Read parameters from ``Fname`` previously stored by ``writeParameters()``. + def readParameters(self, filename: str): + """Read parameters from ``filename`` previously stored by ``writeParameters()``. No input verification is performed. """ try: - inf = open(Fname, 'r') + inf = open(filename, "r") except IOError: - print('Could not open parameter file {}'.format(Fname)) + print("Could not open parameter file {}".format(filename)) line = inf.readline() while line: - field, value = line.split(' = ') + field, value = line.split(" = ") if field in Metadata.str_fields: setattr(self, field, value) elif field in Metadata.int_fields: @@ -268,18 +298,22 @@ def readParameters(self, Fname): line = inf.readline() inf.close() - def writeParameters(self, Fname): - """Write parameters to ``Fname`` but leave out parameters that define + def writeParameters(self, filename: str): + """Write parameters to ``filename`` but leave out parameters that define the input obtained from ``filenameAtoms`` (incuding cell dimensions and number of tiles) as well as the output specific settings. """ try: - outf = open(Fname, 'w') + outf = open(filename, "w") except IOError: - print('Could not open parameter file {}'.format(Fname)) + print("Could not open parameter file {}".format(filename)) for field in Metadata.fields: - if ('save' not in field) and ('filename' not in field) and ('cellDim' not in field): + if ( + ("save" not in field) + and ("filename" not in field) + and ("cellDim" not in field) + ): # # only save parameters that define a calculation but # omit all related to specific input or to what kind of output is @@ -293,7 +327,7 @@ def toString(self): print("{} = {}".format(field, getattr(self, field))) def go(self): - self.algorithm = self.algorithm.lower() - self.transferMode = self.transferMode.lower() - l = [getattr(self, field) for field in Metadata.fields] - pyprismatic.core.go(*(l)) + self.algorithm: str = self.algorithm.lower() + self.transferMode: str = self.transferMode.lower() + l: List[Any] = [getattr(self, field) for field in Metadata.fields] + pyprismatic.core.go(*l) diff --git a/setup.py b/setup.py index 2ce472f49..3b4bd4f1c 100644 --- a/setup.py +++ b/setup.py @@ -17,76 +17,82 @@ import os prismatic_extra_definitions = [] -prismatic_libs = [] +prismatic_libs = [] # In CPU-only mode, all Prismatic C++ source files will be compiled into the Python package. For GPU support, a shared library is # built from the CUDA/C++ sources and the Python package links against it. The C++ files that are potentially compiled into # the shared library (along with CUDA files) are the "extra" sources and in CPU-mode they are added to the Python package sources. # The reason for doing it this way is for user convenience so that compiling/linking to an extra Prismatic library is not required until # enabling for GPU, at which point it is already necessary for the other CUDA libraries. -prismatic_sources_base = ['pyprismatic/core.cpp' - ] +prismatic_sources_base = ["pyprismatic/core.cpp"] prismatic_sources_extra = [ - 'src/go.cpp', - 'src/atom.cpp', - 'src/configure.cpp', - 'src/Multislice_calcOutput.cpp', - 'src/Multislice_entry.cpp',# - 'src/parseInput.cpp',# - 'src/PRISM01_calcPotential.cpp', - 'src/PRISM02_calcSMatrix.cpp',# - 'src/PRISM03_calcOutput.cpp',# - 'src/PRISM_entry.cpp', - 'src/projectedPotential.cpp',# - 'src/utility.cpp',# - 'src/WorkDispatcher.cpp' - ] -prismatic_sources = prismatic_sources_base -prismatic_include_dirs = ["./include"] -prismatic_library_dirs = [] + "src/go.cpp", + "src/atom.cpp", + "src/configure.cpp", + "src/Multislice_calcOutput.cpp", + "src/Multislice_entry.cpp", # + "src/parseInput.cpp", # + "src/PRISM01_calcPotential.cpp", + "src/PRISM02_calcSMatrix.cpp", # + "src/PRISM03_calcOutput.cpp", # + "src/PRISM_entry.cpp", + "src/projectedPotential.cpp", # + "src/utility.cpp", # + "src/WorkDispatcher.cpp", +] +prismatic_sources = prismatic_sources_base +prismatic_include_dirs = ["./include"] +prismatic_library_dirs = [] -if os.name == "nt": #check for Windows OS - prismatic_libs = ["libfftw3f-3"] +if os.name == "nt": # check for Windows OS + prismatic_libs = ["libfftw3f-3"] else: - prismatic_libs = ["fftw3f", "fftw3f_threads"] -prismatic_extra_compile_defs= ['-std=c++11'] + prismatic_libs = ["fftw3f", "fftw3f_threads"] +prismatic_extra_compile_defs = ["-std=c++11"] + class InstallCommand(install): - user_options = install.user_options + [ - ('enable-gpu', None, None), - ] + user_options = install.user_options + [("enable-gpu", None, None)] - def initialize_options(self): - install.initialize_options(self) - self.enable_gpu = False + def initialize_options(self): + install.initialize_options(self) + self.enable_gpu = False - def finalize_options(self): - install.finalize_options(self) + def finalize_options(self): + install.finalize_options(self) - def run(self): - install.run(self) + def run(self): + install.run(self) -if ("--enable-gpu" in sys.argv): # GPU-mode, add some macro definitions and link with the CUDA libraries - prismatic_extra_definitions.extend([("PRISMATIC_ENABLE_GPU",1), ("BUILDING_CUPRISMATIC",1)]) - prismatic_libs.extend(["cuprismatic", "cufft", "cudart"]) -else: # CPU-only mode, add the extra C++ sources to the python package - prismatic_sources.extend(prismatic_sources_extra) +if ( + "--enable-gpu" in sys.argv +): # GPU-mode, add some macro definitions and link with the CUDA libraries + prismatic_extra_definitions.extend( + [("PRISMATIC_ENABLE_GPU", 1), ("BUILDING_CUPRISMATIC", 1)] + ) + prismatic_libs.extend(["cuprismatic", "cufft", "cudart"]) +else: # CPU-only mode, add the extra C++ sources to the python package + prismatic_sources.extend(prismatic_sources_extra) -pyprimsatic_core = Extension('pyprismatic.core', - sources=prismatic_sources, - include_dirs=prismatic_include_dirs, - extra_compile_args=prismatic_extra_compile_defs, - define_macros=prismatic_extra_definitions, - library_dirs=prismatic_library_dirs, - libraries=prismatic_libs) +pyprimsatic_core = Extension( + "pyprismatic.core", + sources=prismatic_sources, + include_dirs=prismatic_include_dirs, + extra_compile_args=prismatic_extra_compile_defs, + define_macros=prismatic_extra_definitions, + library_dirs=prismatic_library_dirs, + libraries=prismatic_libs, +) -setup(name = 'PyPrismatic', - author = 'Alan (AJ) Pryor, Jr.', - author_email='apryor6@gmail.com', - version = '1.1.16', - description="Python wrapper for Prismatic package for fast image simulation using the PRISM and multislice algorithms in Scanning Transmission Electron Microscopy (STEM)", - ext_modules=[pyprimsatic_core], - packages=['pyprismatic'], - install_requires=['numpy>=1.13.0','matplotlib>=2.0.2'], - cmdclass={'install': InstallCommand}) +setup( + name="PyPrismatic", + author="Alan (AJ) Pryor, Jr.", + author_email="apryor6@gmail.com", + version="1.1.16", + description="Python wrapper for Prismatic package for fast image simulation using the PRISM and multislice algorithms in Scanning Transmission Electron Microscopy (STEM)", + ext_modules=[pyprimsatic_core], + packages=["pyprismatic"], + install_requires=["numpy>=1.13.0", "matplotlib>=2.0.2"], + cmdclass={"install": InstallCommand}, +) diff --git a/src/Multislice_entry.cpp b/src/Multislice_entry.cpp index 82b61ea98..450106d0a 100644 --- a/src/Multislice_entry.cpp +++ b/src/Multislice_entry.cpp @@ -21,182 +21,210 @@ #include #include "utility.h" - -namespace Prismatic{ - Parameters Multislice_entry(Metadata& meta){ - Parameters prismatic_pars; - try { // read atomic coordinates - prismatic_pars = Parameters(meta); - } catch(...){ - std::cout << "Terminating" << std::endl; - exit(1); +namespace Prismatic +{ +Parameters Multislice_entry(Metadata &meta) +{ + Parameters prismatic_pars; + try + { // read atomic coordinates + prismatic_pars = Parameters(meta); + } + catch (...) + { + std::cout << "Terminating" << std::endl; + exit(1); + } + prismatic_pars.meta.toString(); + + prismatic_pars.outputFile = H5::H5File(prismatic_pars.meta.filenameOutput.c_str(), H5F_ACC_TRUNC); + setupOutputFile(prismatic_pars); + // compute projected potentials + prismatic_pars.fpFlag = 0; + PRISM01_calcPotential(prismatic_pars); + + prismatic_pars.scale = 1.0; + // compute final output + Multislice_calcOutput(prismatic_pars); + prismatic_pars.outputFile.close(); + + // calculate remaining frozen phonon configurations + //TODO: Clarify the scope issues occuring here. Extraneous copy of prismatic_pars structure? + if (prismatic_pars.meta.numFP > 1) + { + // run the rest of the frozen phonons + Array4D net_output(prismatic_pars.output); + Array4D DPC_CoM_output; + if (prismatic_pars.meta.saveDPC_CoM) + DPC_CoM_output = prismatic_pars.DPC_CoM; + for (auto fp_num = 1; fp_num < prismatic_pars.meta.numFP; ++fp_num) + { + meta.randomSeed = rand() % 100000; + ++meta.fpNum; + Parameters prismatic_pars(meta); + cout << "Frozen Phonon #" << fp_num << endl; + prismatic_pars.meta.toString(); + + prismatic_pars.outputFile = H5::H5File(prismatic_pars.meta.filenameOutput.c_str(), H5F_ACC_RDWR); + prismatic_pars.fpFlag = fp_num; + prismatic_pars.scale = 1.0; + + PRISM01_calcPotential(prismatic_pars); + Multislice_calcOutput(prismatic_pars); + net_output += prismatic_pars.output; + if (meta.saveDPC_CoM) + DPC_CoM_output += prismatic_pars.DPC_CoM; + prismatic_pars.outputFile.close(); } - prismatic_pars.meta.toString(); - - prismatic_pars.outputFile = H5::H5File(prismatic_pars.meta.filenameOutput.c_str(),H5F_ACC_TRUNC); - setupOutputFile(prismatic_pars); - // compute projected potentials - prismatic_pars.fpFlag = 0; - PRISM01_calcPotential(prismatic_pars); - - prismatic_pars.scale = 1.0; - // compute final output - Multislice_calcOutput(prismatic_pars); - prismatic_pars.outputFile.close(); - - // calculate remaining frozen phonon configurations - //TODO: Clarify the scope issues occuring here. Extraneous copy of prismatic_pars structure? - if (prismatic_pars.meta.numFP > 1) { - // run the rest of the frozen phonons - Array4D net_output(prismatic_pars.output); - Array4D DPC_CoM_output; - if(prismatic_pars.meta.saveDPC_CoM) DPC_CoM_output = prismatic_pars.DPC_CoM; - for (auto fp_num = 1; fp_num < prismatic_pars.meta.numFP; ++fp_num){ - meta.randomSeed = rand() % 100000; - ++meta.fpNum; - Parameters prismatic_pars(meta); - cout << "Frozen Phonon #" << fp_num << endl; - prismatic_pars.meta.toString(); - - prismatic_pars.outputFile = H5::H5File(prismatic_pars.meta.filenameOutput.c_str(),H5F_ACC_RDWR); - prismatic_pars.fpFlag = fp_num; - prismatic_pars.scale = 1.0; - - PRISM01_calcPotential(prismatic_pars); - Multislice_calcOutput(prismatic_pars); - net_output += prismatic_pars.output; - if(meta.saveDPC_CoM) DPC_CoM_output += prismatic_pars.DPC_CoM; - prismatic_pars.outputFile.close(); - } - // divide to take average - for (auto&i:net_output) i/=prismatic_pars.meta.numFP; - prismatic_pars.output = net_output; - - if(prismatic_pars.meta.saveDPC_CoM){ - for (auto&j:DPC_CoM_output) j/=prismatic_pars.meta.numFP; //since squared intensities are used to calculate DPC_CoM, this is incoherent averaging - prismatic_pars.DPC_CoM = DPC_CoM_output; - } - + // divide to take average + for (auto &i : net_output) + i /= prismatic_pars.meta.numFP; + prismatic_pars.output = net_output; + + if (prismatic_pars.meta.saveDPC_CoM) + { + for (auto &j : DPC_CoM_output) + j /= prismatic_pars.meta.numFP; //since squared intensities are used to calculate DPC_CoM, this is incoherent averaging + prismatic_pars.DPC_CoM = DPC_CoM_output; } + } - prismatic_pars.outputFile = H5::H5File(prismatic_pars.meta.filenameOutput.c_str(),H5F_ACC_RDWR); - if (prismatic_pars.meta.save3DOutput){ - PRISMATIC_FLOAT_PRECISION dummy = 1.0; - setupVDOutput(prismatic_pars, prismatic_pars.output.get_diml(),dummy); - - //create dummy array to pass to - Array3D slice_image; - slice_image = zeros_ND<3, PRISMATIC_FLOAT_PRECISION>({{prismatic_pars.output.get_dimj(),prismatic_pars.output.get_dimk(),prismatic_pars.output.get_dimi()}}); - - for (auto j = 0; j < prismatic_pars.output.get_diml(); j++){ - std::stringstream nameString; - nameString << "4DSTEM_simulation/data/realslices/virtual_detector_depth" << getDigitString(j); - H5::Group dataGroup = prismatic_pars.outputFile.openGroup(nameString.str()); - hsize_t mdims[3] = {prismatic_pars.xp.size(),prismatic_pars.yp.size(),prismatic_pars.Ndet}; - - std::string dataSetName ="realslice"; - H5::DataSet VD_data = dataGroup.openDataSet(dataSetName); - for (auto b = 0; b < prismatic_pars.output.get_dimi(); ++b){ - for (auto y = 0; y < prismatic_pars.output.get_dimk(); ++y){ - for (auto x = 0; x < prismatic_pars.output.get_dimj();++x){ - slice_image.at(x,y,b) = prismatic_pars.output.at(j,y,x,b); - } + prismatic_pars.outputFile = H5::H5File(prismatic_pars.meta.filenameOutput.c_str(), H5F_ACC_RDWR); + if (prismatic_pars.meta.save3DOutput) + { + PRISMATIC_FLOAT_PRECISION dummy = 1.0; + setupVDOutput(prismatic_pars, prismatic_pars.output.get_diml(), dummy); + + //create dummy array to pass to + Array3D slice_image; + slice_image = zeros_ND<3, PRISMATIC_FLOAT_PRECISION>({{prismatic_pars.output.get_dimj(), prismatic_pars.output.get_dimk(), prismatic_pars.output.get_dimi()}}); + + for (auto j = 0; j < prismatic_pars.output.get_diml(); j++) + { + std::stringstream nameString; + nameString << "4DSTEM_simulation/data/realslices/virtual_detector_depth" << getDigitString(j); + H5::Group dataGroup = prismatic_pars.outputFile.openGroup(nameString.str()); + hsize_t mdims[3] = {prismatic_pars.xp.size(), prismatic_pars.yp.size(), prismatic_pars.Ndet}; + + std::string dataSetName = "realslice"; + H5::DataSet VD_data = dataGroup.openDataSet(dataSetName); + for (auto b = 0; b < prismatic_pars.output.get_dimi(); ++b) + { + for (auto y = 0; y < prismatic_pars.output.get_dimk(); ++y) + { + for (auto x = 0; x < prismatic_pars.output.get_dimj(); ++x) + { + slice_image.at(x, y, b) = prismatic_pars.output.at(j, y, x, b); } } - - writeDatacube3D(VD_data,&slice_image[0],mdims); - VD_data.close(); - //if ( prismatic_pars.meta.numSlices != 0) slice_filename = prismatic_pars.meta.outputFolder + std::string("slice")+std::to_string(j)+std::string("_") + prismatic_pars.meta.filenameOutput; - //slice_image.toMRC_f(slice_filename.c_str()); - dataGroup.close(); } + + writeDatacube3D(VD_data, &slice_image[0], mdims); + VD_data.close(); + //if ( prismatic_pars.meta.numSlices != 0) slice_filename = prismatic_pars.meta.outputFolder + std::string("slice")+std::to_string(j)+std::string("_") + prismatic_pars.meta.filenameOutput; + //slice_image.toMRC_f(slice_filename.c_str()); + dataGroup.close(); } + } - if (prismatic_pars.meta.save2DOutput) { - size_t lower = std::max((size_t)0, (size_t)(prismatic_pars.meta.integrationAngleMin / prismatic_pars.meta.detectorAngleStep)); - size_t upper = std::min(prismatic_pars.detectorAngles.size(), (size_t) (prismatic_pars.meta.integrationAngleMax / prismatic_pars.meta.detectorAngleStep)); - Array2D prism_image; - //std::string image_filename = std::string("multislice_2Doutput")+prismatic_pars.meta.filenameOutput; - PRISMATIC_FLOAT_PRECISION dummy = 1.0; - setup2DOutput(prismatic_pars, prismatic_pars.output.get_diml(),dummy); - - for (auto j = 0; j < prismatic_pars.output.get_diml(); j++){ - //need to initiliaze output image at each slice to prevent overflow of value - prism_image = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>( - {{prismatic_pars.output.get_dimj(), prismatic_pars.output.get_dimk()}}); - - for (auto y = 0; y < prismatic_pars.output.get_dimk(); ++y) { - for (auto x = 0; x < prismatic_pars.output.get_dimj(); ++x) { - for (auto b = lower; b < upper; ++b) { - prism_image.at(x, y) += prismatic_pars.output.at(j, y, x, b); - } - } + if (prismatic_pars.meta.save2DOutput) + { + size_t lower = std::max((size_t)0, (size_t)(prismatic_pars.meta.integrationAngleMin / prismatic_pars.meta.detectorAngleStep)); + size_t upper = std::min(prismatic_pars.detectorAngles.size(), (size_t)(prismatic_pars.meta.integrationAngleMax / prismatic_pars.meta.detectorAngleStep)); + Array2D prism_image; + //std::string image_filename = std::string("multislice_2Doutput")+prismatic_pars.meta.filenameOutput; + PRISMATIC_FLOAT_PRECISION dummy = 1.0; + setup2DOutput(prismatic_pars, prismatic_pars.output.get_diml(), dummy); + + for (auto j = 0; j < prismatic_pars.output.get_diml(); j++) + { + //need to initiliaze output image at each slice to prevent overflow of value + prism_image = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>( + {{prismatic_pars.output.get_dimj(), prismatic_pars.output.get_dimk()}}); + + for (auto y = 0; y < prismatic_pars.output.get_dimk(); ++y) + { + for (auto x = 0; x < prismatic_pars.output.get_dimj(); ++x) + { + for (auto b = lower; b < upper; ++b) + { + prism_image.at(x, y) += prismatic_pars.output.at(j, y, x, b); } - //if (prismatic_pars.meta.numSlices != 0){ - // image_filename = prismatic_pars.meta.outputFolder + (std::string("multislice_2Doutput_slice") + std::to_string(j) + std::string("_")) + prismatic_pars.meta.filenameOutput; - //} - //prism_image.toMRC_f(image_filename.c_str()); - std::stringstream nameString; - nameString << "4DSTEM_simulation/data/realslices/annular_detector_depth" << getDigitString(j); - H5::Group dataGroup = prismatic_pars.outputFile.openGroup(nameString.str()); - H5::DataSet AD_data = dataGroup.openDataSet("realslice"); - hsize_t mdims[2] = {prismatic_pars.xp.size(),prismatic_pars.yp.size()}; - - writeRealSlice(AD_data,&prism_image[0],mdims); - AD_data.close(); - dataGroup.close(); } + } + //if (prismatic_pars.meta.numSlices != 0){ + // image_filename = prismatic_pars.meta.outputFolder + (std::string("multislice_2Doutput_slice") + std::to_string(j) + std::string("_")) + prismatic_pars.meta.filenameOutput; + //} + //prism_image.toMRC_f(image_filename.c_str()); + std::stringstream nameString; + nameString << "4DSTEM_simulation/data/realslices/annular_detector_depth" << getDigitString(j); + H5::Group dataGroup = prismatic_pars.outputFile.openGroup(nameString.str()); + H5::DataSet AD_data = dataGroup.openDataSet("realslice"); + hsize_t mdims[2] = {prismatic_pars.xp.size(), prismatic_pars.yp.size()}; + + writeRealSlice(AD_data, &prism_image[0], mdims); + AD_data.close(); + dataGroup.close(); } + } + if (prismatic_pars.meta.saveDPC_CoM) + { + PRISMATIC_FLOAT_PRECISION dummy = 1.0; + setupDPCOutput(prismatic_pars, prismatic_pars.output.get_diml(), dummy); + + //create dummy array to pass to + Array2D DPC_slice; + DPC_slice = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>({{prismatic_pars.DPC_CoM.get_dimj(), prismatic_pars.DPC_CoM.get_dimk()}}); + + for (auto j = 0; j < prismatic_pars.output.get_diml(); j++) + { + std::stringstream nameString; + nameString << "4DSTEM_simulation/data/realslices/DPC_CoM_depth" << getDigitString(j); + H5::Group dataGroup = prismatic_pars.outputFile.openGroup(nameString.str()); + hsize_t mdims[2] = {prismatic_pars.xp.size(), prismatic_pars.yp.size()}; + + for (auto b = 0; b < prismatic_pars.DPC_CoM.get_dimi(); ++b) + { + std::string endName; + if (b == 0) + { + endName = "x"; + } + else + { + endName = "y"; + } + std::string dataSetName = "DPC_CoM_" + endName; - if (prismatic_pars.meta.saveDPC_CoM){ - PRISMATIC_FLOAT_PRECISION dummy = 1.0; - setupDPCOutput(prismatic_pars,prismatic_pars.output.get_diml(), dummy); - - //create dummy array to pass to - Array2D DPC_slice; - DPC_slice = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>({{prismatic_pars.DPC_CoM.get_dimj(),prismatic_pars.DPC_CoM.get_dimk()}}); - - for (auto j = 0; j < prismatic_pars.output.get_diml(); j++){ - std::stringstream nameString; - nameString << "4DSTEM_simulation/data/realslices/DPC_CoM_depth" << getDigitString(j); - H5::Group dataGroup = prismatic_pars.outputFile.openGroup(nameString.str()); - hsize_t mdims[2] = {prismatic_pars.xp.size(),prismatic_pars.yp.size()}; - - for (auto b = 0; b < prismatic_pars.DPC_CoM.get_dimi(); ++b){ - std::string endName; - if(b == 0){ - endName = "x"; - }else{ - endName = "y"; - } - std::string dataSetName = "DPC_CoM_" + endName; - - H5::DataSet DPC_data = dataGroup.openDataSet(dataSetName); + H5::DataSet DPC_data = dataGroup.openDataSet(dataSetName); - for (auto y = 0; y < prismatic_pars.DPC_CoM.get_dimk(); ++y){ - for (auto x = 0; x < prismatic_pars.DPC_CoM.get_dimj();++x){ - DPC_slice.at(x,y) = prismatic_pars.DPC_CoM.at(j,y,x,b); - } + for (auto y = 0; y < prismatic_pars.DPC_CoM.get_dimk(); ++y) + { + for (auto x = 0; x < prismatic_pars.DPC_CoM.get_dimj(); ++x) + { + DPC_slice.at(x, y) = prismatic_pars.DPC_CoM.at(j, y, x, b); } - //if ( prismatic_pars.meta.numSlices != 0) slice_filename = prismatic_pars.meta.outputFolder + std::string("slice")+std::to_string(j)+std::string("_") + prismatic_pars.meta.filenameOutput; - //slice_image.toMRC_f(slice_filename.c_str()); - writeRealSlice(DPC_data,&DPC_slice[0],mdims); - DPC_data.close(); } - - dataGroup.close(); + //if ( prismatic_pars.meta.numSlices != 0) slice_filename = prismatic_pars.meta.outputFolder + std::string("slice")+std::to_string(j)+std::string("_") + prismatic_pars.meta.filenameOutput; + //slice_image.toMRC_f(slice_filename.c_str()); + writeRealSlice(DPC_data, &DPC_slice[0], mdims); + DPC_data.close(); } + + dataGroup.close(); } + } - PRISMATIC_FLOAT_PRECISION dummy = 1.0; - writeMetadata(prismatic_pars,dummy); - prismatic_pars.outputFile.close(); + PRISMATIC_FLOAT_PRECISION dummy = 1.0; + writeMetadata(prismatic_pars, dummy); + prismatic_pars.outputFile.close(); #ifdef PRISMATIC_ENABLE_GPU - cout << "peak GPU memory usage = " << prismatic_pars.maxGPUMem << '\n'; + cout << "peak GPU memory usage = " << prismatic_pars.maxGPUMem << '\n'; #endif //PRISMATIC_ENABLE_GPU - std::cout << "Calculation complete.\n" << std::endl; - return prismatic_pars; - } + std::cout << "Calculation complete.\n" + << std::endl; + return prismatic_pars; } +} // namespace Prismatic diff --git a/src/PRISM01_calcPotential.cpp b/src/PRISM01_calcPotential.cpp index b5c9026fb..7f6918bfd 100644 --- a/src/PRISM01_calcPotential.cpp +++ b/src/PRISM01_calcPotential.cpp @@ -31,351 +31,403 @@ #include "prism_progressbar.h" #endif - -namespace Prismatic { - using namespace std; - void fetch_potentials(Array3D& potentials, - const vector& atomic_species, - const Array1D& xr, - const Array1D& yr){ - Array2D cur_pot; - for (auto k =0; k < potentials.get_dimk(); ++k){ - Array2D cur_pot = projPot(atomic_species[k], xr, yr); - for (auto j = 0; j < potentials.get_dimj(); ++j){ - for (auto i = 0; i < potentials.get_dimi(); ++i){ - potentials.at(k,j,i) = cur_pot.at(j,i); - } +namespace Prismatic +{ +using namespace std; +void fetch_potentials(Array3D &potentials, + const vector &atomic_species, + const Array1D &xr, + const Array1D &yr) +{ + Array2D cur_pot; + for (auto k = 0; k < potentials.get_dimk(); ++k) + { + Array2D cur_pot = projPot(atomic_species[k], xr, yr); + for (auto j = 0; j < potentials.get_dimj(); ++j) + { + for (auto i = 0; i < potentials.get_dimi(); ++i) + { + potentials.at(k, j, i) = cur_pot.at(j, i); } } } +} - vector get_unique_atomic_species(Parameters& pars){ - // helper function to get the unique atomic species - vector unique_atoms = vector(pars.atoms.size(),0); - for (auto i = 0; i < pars.atoms.size(); ++i)unique_atoms[i] = pars.atoms[i].species; - sort(unique_atoms.begin(), unique_atoms.end()); - vector::iterator it = unique(unique_atoms.begin(), unique_atoms.end()); - unique_atoms.resize(distance(unique_atoms.begin(),it)); - return unique_atoms; - } +vector get_unique_atomic_species(Parameters &pars) +{ + // helper function to get the unique atomic species + vector unique_atoms = vector(pars.atoms.size(), 0); + for (auto i = 0; i < pars.atoms.size(); ++i) + unique_atoms[i] = pars.atoms[i].species; + sort(unique_atoms.begin(), unique_atoms.end()); + vector::iterator it = unique(unique_atoms.begin(), unique_atoms.end()); + unique_atoms.resize(distance(unique_atoms.begin(), it)); + return unique_atoms; +} - void generateProjectedPotentials(Parameters& pars, - const Array3D& potentialLookup, - const vector& unique_species, - const Array1D& xvec, - const Array1D& yvec){ - // splits the atomic coordinates into slices and computes the projected potential for each. - - // create arrays for the coordinates - Array1D x = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{pars.atoms.size()}}); - Array1D y = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{pars.atoms.size()}}); - Array1D z = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{pars.atoms.size()}}); - Array1D ID = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{pars.atoms.size()}}); - Array1D sigma = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{pars.atoms.size()}}); - Array1D occ = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{pars.atoms.size()}}); - - // populate arrays from the atoms structure - for (auto i = 0; i < pars.atoms.size(); ++i){ - x[i] = pars.atoms[i].x * pars.tiledCellDim[2]; - y[i] = pars.atoms[i].y * pars.tiledCellDim[1]; - z[i] = pars.atoms[i].z * pars.tiledCellDim[0]; - ID[i] = pars.atoms[i].species; - sigma[i] = pars.atoms[i].sigma; - occ[i] = pars.atoms[i].occ; - } +void generateProjectedPotentials(Parameters &pars, + const Array3D &potentialLookup, + const vector &unique_species, + const Array1D &xvec, + const Array1D &yvec) +{ + // splits the atomic coordinates into slices and computes the projected potential for each. + + // create arrays for the coordinates + Array1D x = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{pars.atoms.size()}}); + Array1D y = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{pars.atoms.size()}}); + Array1D z = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{pars.atoms.size()}}); + Array1D ID = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{pars.atoms.size()}}); + Array1D sigma = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{pars.atoms.size()}}); + Array1D occ = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{pars.atoms.size()}}); + + // populate arrays from the atoms structure + for (auto i = 0; i < pars.atoms.size(); ++i) + { + x[i] = pars.atoms[i].x * pars.tiledCellDim[2]; + y[i] = pars.atoms[i].y * pars.tiledCellDim[1]; + z[i] = pars.atoms[i].z * pars.tiledCellDim[0]; + ID[i] = pars.atoms[i].species; + sigma[i] = pars.atoms[i].sigma; + occ[i] = pars.atoms[i].occ; + } - // compute the z-slice index for each atom - auto max_z = std::max_element(z.begin(), z.end()); - Array1D zPlane(z); - std::transform(zPlane.begin(), zPlane.end(), zPlane.begin(), [&max_z, &pars](PRISMATIC_FLOAT_PRECISION &t_z) { - return round((-t_z + *max_z) / pars.meta.sliceThickness + 0.5) - 1; // If the +0.5 was to make the first slice z=1 not 0, can drop the +0.5 and -1 - }); - max_z = std::max_element(zPlane.begin(), zPlane.end()); - pars.numPlanes = *max_z + 1; - - //check if intermediate output was specified, if so, create index of output slices - if(pars.meta.numSlices == 0){ - pars.numSlices = pars.numPlanes; - } + // compute the z-slice index for each atom + auto max_z = std::max_element(z.begin(), z.end()); + Array1D zPlane(z); + std::transform(zPlane.begin(), zPlane.end(), zPlane.begin(), [&max_z, &pars](PRISMATIC_FLOAT_PRECISION &t_z) { + return round((-t_z + *max_z) / pars.meta.sliceThickness + 0.5) - 1; // If the +0.5 was to make the first slice z=1 not 0, can drop the +0.5 and -1 + }); + max_z = std::max_element(zPlane.begin(), zPlane.end()); + pars.numPlanes = *max_z + 1; + + //check if intermediate output was specified, if so, create index of output slices + if (pars.meta.numSlices == 0) + { + pars.numSlices = pars.numPlanes; + } #ifdef PRISMATIC_BUILDING_GUI - pars.progressbar->signalPotentialUpdate(0, pars.numPlanes); + pars.progressbar->signalPotentialUpdate(0, pars.numPlanes); #endif - // initialize the potential array - pars.pot = zeros_ND<3, PRISMATIC_FLOAT_PRECISION>({{pars.numPlanes, pars.imageSize[0], pars.imageSize[1]}}); - - // create a key-value map to match the atomic Z numbers with their place in the potential lookup table - map Z_lookup; - for (auto i = 0; i < unique_species.size(); ++i)Z_lookup[unique_species[i]] = i; - - //loop over each plane, perturb the atomic positions, and place the corresponding potential at each location - // using parallel calculation of each individual slice - std::vector workers; - workers.reserve(pars.meta.numThreads); - - WorkDispatcher dispatcher(0, pars.numPlanes); - for (long t = 0; t < pars.meta.numThreads; ++t){ - cout << "Launching thread #" << t << " to compute projected potential slices\n"; - workers.push_back(thread([&pars, &x, &y, &z, &ID, &Z_lookup, &xvec, &sigma, &occ, - &zPlane, &yvec,&potentialLookup, &dispatcher](){ - // create a random number generator to simulate thermal effects - // std::cout<<"random seed = " << pars.meta.randomSeed << std::endl; - // srand(pars.meta.randomSeed); - // std::default_random_engine de(pars.meta.randomSeed); - // normal_distribution randn(0,1); - Array1D xp; - Array1D yp; - - size_t currentSlice, stop; - currentSlice=stop=0; - while (dispatcher.getWork(currentSlice, stop)) { // synchronously get work assignment - Array2D projectedPotential = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>({{pars.imageSize[0], pars.imageSize[1]}}); - const long dim0 = (long) pars.imageSize[0]; - const long dim1 = (long) pars.imageSize[1]; - while (currentSlice != stop) { - - // create a random number generator to simulate thermal effects - std::cout<<"random seed = " << pars.meta.randomSeed + currentSlice*pars.numPlanes << std::endl; - srand(pars.meta.randomSeed +currentSlice*pars.numPlanes); - std::default_random_engine de(pars.meta.randomSeed +currentSlice*pars.numPlanes); - normal_distribution randn(0,1); - - for (auto atom_num = 0; atom_num < x.size(); ++atom_num) { - if (zPlane[atom_num] == currentSlice) { - if (pars.meta.includeOccupancy){ - if (static_cast(rand())/static_cast (RAND_MAX) > occ[atom_num]){ - continue; - } - } -// if ( !pars.meta.includeOccupancy || static_cast(rand())/static_cast (RAND_MAX) <= occ[atom_num]) { - const size_t cur_Z = Z_lookup[ID[atom_num]]; - PRISMATIC_FLOAT_PRECISION X, Y; - if (pars.meta.includeThermalEffects) { // apply random perturbations - X = round((x[atom_num] + randn(de) * sigma[atom_num]) / pars.pixelSize[1]); - Y = round((y[atom_num] + randn(de) * sigma[atom_num]) / pars.pixelSize[0]); - } else { - X = round((x[atom_num]) / pars.pixelSize[1]); // this line uses no thermal factor - Y = round((y[atom_num]) / pars.pixelSize[0]); // this line uses no thermal factor - } - xp = xvec + (long) X; - for (auto &i:xp)i = (i % dim1 + dim1) % dim1; // make sure to get a positive value - - yp = yvec + (long) Y; - for (auto &i:yp) i = (i % dim0 + dim0) % dim0;// make sure to get a positive value - for (auto ii = 0; ii < xp.size(); ++ii) { - for (auto jj = 0; jj < yp.size(); ++jj) { - // fill in value with lookup table - projectedPotential.at(yp[jj], xp[ii]) += potentialLookup.at(cur_Z, jj, ii); - } - } -// } + // initialize the potential array + pars.pot = zeros_ND<3, PRISMATIC_FLOAT_PRECISION>({{pars.numPlanes, pars.imageSize[0], pars.imageSize[1]}}); + + // create a key-value map to match the atomic Z numbers with their place in the potential lookup table + map Z_lookup; + for (auto i = 0; i < unique_species.size(); ++i) + Z_lookup[unique_species[i]] = i; + + //loop over each plane, perturb the atomic positions, and place the corresponding potential at each location + // using parallel calculation of each individual slice + std::vector workers; + workers.reserve(pars.meta.numThreads); + + WorkDispatcher dispatcher(0, pars.numPlanes); + for (long t = 0; t < pars.meta.numThreads; ++t) + { + cout << "Launching thread #" << t << " to compute projected potential slices\n"; + workers.push_back(thread([&pars, &x, &y, &z, &ID, &Z_lookup, &xvec, &sigma, &occ, + &zPlane, &yvec, &potentialLookup, &dispatcher]() { + // create a random number generator to simulate thermal effects + // std::cout<<"random seed = " << pars.meta.randomSeed << std::endl; + // srand(pars.meta.randomSeed); + // std::default_random_engine de(pars.meta.randomSeed); + // normal_distribution randn(0,1); + Array1D xp; + Array1D yp; + + size_t currentSlice, stop; + currentSlice = stop = 0; + while (dispatcher.getWork(currentSlice, stop)) + { // synchronously get work assignment + Array2D projectedPotential = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>({{pars.imageSize[0], pars.imageSize[1]}}); + const long dim0 = (long)pars.imageSize[0]; + const long dim1 = (long)pars.imageSize[1]; + while (currentSlice != stop) + { + + // create a random number generator to simulate thermal effects + std::cout << "random seed = " << pars.meta.randomSeed + currentSlice * pars.numPlanes << std::endl; + srand(pars.meta.randomSeed + currentSlice * pars.numPlanes); + std::default_random_engine de(pars.meta.randomSeed + currentSlice * pars.numPlanes); + normal_distribution randn(0, 1); + + for (auto atom_num = 0; atom_num < x.size(); ++atom_num) + { + if (zPlane[atom_num] == currentSlice) + { + if (pars.meta.includeOccupancy) + { + if (static_cast(rand()) / static_cast(RAND_MAX) > occ[atom_num]) + { + continue; + } + } + // if ( !pars.meta.includeOccupancy || static_cast(rand())/static_cast (RAND_MAX) <= occ[atom_num]) { + const size_t cur_Z = Z_lookup[ID[atom_num]]; + PRISMATIC_FLOAT_PRECISION X, Y; + if (pars.meta.includeThermalEffects) + { // apply random perturbations + X = round((x[atom_num] + randn(de) * sigma[atom_num]) / pars.pixelSize[1]); + Y = round((y[atom_num] + randn(de) * sigma[atom_num]) / pars.pixelSize[0]); + } + else + { + X = round((x[atom_num]) / pars.pixelSize[1]); // this line uses no thermal factor + Y = round((y[atom_num]) / pars.pixelSize[0]); // this line uses no thermal factor } + xp = xvec + (long)X; + for (auto &i : xp) + i = (i % dim1 + dim1) % dim1; // make sure to get a positive value + + yp = yvec + (long)Y; + for (auto &i : yp) + i = (i % dim0 + dim0) % dim0; // make sure to get a positive value + for (auto ii = 0; ii < xp.size(); ++ii) + { + for (auto jj = 0; jj < yp.size(); ++jj) + { + // fill in value with lookup table + projectedPotential.at(yp[jj], xp[ii]) += potentialLookup.at(cur_Z, jj, ii); + } + } + // } } - // copy the result to the full array - copy(projectedPotential.begin(), projectedPotential.end(),&pars.pot.at(currentSlice,0,0)); + } + // copy the result to the full array + copy(projectedPotential.begin(), projectedPotential.end(), &pars.pot.at(currentSlice, 0, 0)); #ifdef PRISMATIC_BUILDING_GUI - pars.progressbar->signalPotentialUpdate(currentSlice, pars.numPlanes); + pars.progressbar->signalPotentialUpdate(currentSlice, pars.numPlanes); #endif //PRISMATIC_BUILDING_GUI - ++currentSlice; - } + ++currentSlice; } - })); - } - cout << "Waiting for threads...\n"; - for (auto &t:workers)t.join(); + } + })); + } + cout << "Waiting for threads...\n"; + for (auto &t : workers) + t.join(); #ifdef PRISMATIC_BUILDING_GUI - pars.progressbar->setProgress(100); + pars.progressbar->setProgress(100); #endif //PRISMATIC_BUILDING_GUI - }; +}; + +void PRISM01_calcPotential(Parameters &pars) +{ + //builds projected, sliced potential + + // setup some coordinates + cout << "Entering PRISM01_calcPotential" << endl; + PRISMATIC_FLOAT_PRECISION yleng = std::ceil(pars.meta.potBound / pars.pixelSize[0]); + PRISMATIC_FLOAT_PRECISION xleng = std::ceil(pars.meta.potBound / pars.pixelSize[1]); + ArrayND<1, vector> xvec(vector(2 * (size_t)xleng + 1, 0), {{2 * (size_t)xleng + 1}}); + ArrayND<1, vector> yvec(vector(2 * (size_t)yleng + 1, 0), {{2 * (size_t)yleng + 1}}); + { + PRISMATIC_FLOAT_PRECISION tmpx = -xleng; + PRISMATIC_FLOAT_PRECISION tmpy = -yleng; + for (auto &i : xvec) + i = tmpx++; + for (auto &j : yvec) + j = tmpy++; + } + Array1D xr(vector(2 * (size_t)xleng + 1, 0), {{2 * (size_t)xleng + 1}}); + Array1D yr(vector(2 * (size_t)yleng + 1, 0), {{2 * (size_t)yleng + 1}}); + for (auto i = 0; i < xr.size(); ++i) + xr[i] = (PRISMATIC_FLOAT_PRECISION)xvec[i] * pars.pixelSize[1]; + for (auto j = 0; j < yr.size(); ++j) + yr[j] = (PRISMATIC_FLOAT_PRECISION)yvec[j] * pars.pixelSize[0]; + + vector unique_species = get_unique_atomic_species(pars); + + // initialize the lookup table + Array3D potentialLookup = zeros_ND<3, PRISMATIC_FLOAT_PRECISION>({{unique_species.size(), 2 * (size_t)yleng + 1, 2 * (size_t)xleng + 1}}); + + // precompute the unique potentials + fetch_potentials(potentialLookup, unique_species, xr, yr); + + // populate the slices with the projected potentials + generateProjectedPotentials(pars, potentialLookup, unique_species, xvec, yvec); + + if (pars.meta.savePotentialSlices) + { + //create new datacube group + H5::Group realslices = pars.outputFile.openGroup("4DSTEM_simulation/data/realslices"); + std::string groupName = "ppotential"; + H5::Group ppotential; + if (pars.fpFlag == 0) + { + ppotential = realslices.createGroup(groupName); + + H5::DataSpace attr_dataspace(H5S_SCALAR); + + int group_type = 2; + H5::Attribute emd_group_type = ppotential.createAttribute("emd_group_type", H5::PredType::NATIVE_INT, attr_dataspace); + emd_group_type.write(H5::PredType::NATIVE_INT, &group_type); + + H5::Attribute metadata_group = ppotential.createAttribute("metadata", H5::PredType::NATIVE_INT, attr_dataspace); + int mgroup = 0; + metadata_group.write(H5::PredType::NATIVE_INT, &mgroup); + + //write dimensions + H5::DataSpace str_name_ds(H5S_SCALAR); + H5::StrType strdatatype(H5::PredType::C_S1, 256); + + hsize_t x_size[1] = {pars.imageSize[1]}; + hsize_t y_size[1] = {pars.imageSize[0]}; + hsize_t z_size[1] = {pars.numPlanes}; + + Array1D x_dim_data = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{pars.imageSize[1]}}); + Array1D y_dim_data = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{pars.imageSize[0]}}); + Array1D z_dim_data = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{pars.numPlanes}}); + + for (auto i = 0; i < pars.imageSize[1]; i++) + x_dim_data[i] = i * pars.pixelSize[1]; + for (auto i = 0; i < pars.imageSize[0]; i++) + y_dim_data[i] = i * pars.pixelSize[0]; + for (auto i = 0; i < pars.numPlanes; i++) + z_dim_data[i] = i * pars.meta.sliceThickness; + + H5::DataSpace dim1_mspace(1, x_size); + H5::DataSpace dim2_mspace(1, y_size); + H5::DataSpace dim3_mspace(1, z_size); + + H5::DataSet dim1; + H5::DataSet dim2; + H5::DataSet dim3; + + if (sizeof(PRISMATIC_FLOAT_PRECISION) == sizeof(float)) + { + dim1 = ppotential.createDataSet("dim1", H5::PredType::NATIVE_FLOAT, dim1_mspace); + dim2 = ppotential.createDataSet("dim2", H5::PredType::NATIVE_FLOAT, dim2_mspace); + dim3 = ppotential.createDataSet("dim3", H5::PredType::NATIVE_FLOAT, dim3_mspace); + + H5::DataSpace dim1_fspace = dim1.getSpace(); + H5::DataSpace dim2_fspace = dim2.getSpace(); + H5::DataSpace dim3_fspace = dim3.getSpace(); + + dim1.write(&x_dim_data[0], H5::PredType::NATIVE_FLOAT, dim1_mspace, dim1_fspace); + dim2.write(&y_dim_data[0], H5::PredType::NATIVE_FLOAT, dim2_mspace, dim2_fspace); + dim3.write(&z_dim_data[0], H5::PredType::NATIVE_FLOAT, dim3_mspace, dim3_fspace); + } + else + { + dim1 = ppotential.createDataSet("dim1", H5::PredType::NATIVE_DOUBLE, dim1_mspace); + dim2 = ppotential.createDataSet("dim2", H5::PredType::NATIVE_DOUBLE, dim2_mspace); + dim3 = ppotential.createDataSet("dim3", H5::PredType::NATIVE_DOUBLE, dim3_mspace); + + H5::DataSpace dim1_fspace = dim1.getSpace(); + H5::DataSpace dim2_fspace = dim2.getSpace(); + H5::DataSpace dim3_fspace = dim3.getSpace(); + + dim1.write(&x_dim_data[0], H5::PredType::NATIVE_DOUBLE, dim1_mspace, dim1_fspace); + dim2.write(&y_dim_data[0], H5::PredType::NATIVE_DOUBLE, dim2_mspace, dim2_fspace); + dim3.write(&z_dim_data[0], H5::PredType::NATIVE_DOUBLE, dim3_mspace, dim3_fspace); + } + + //dimension attributes + const H5std_string dim1_name_str("R_x"); + const H5std_string dim2_name_str("R_y"); + const H5std_string dim3_name_str("R_z"); + + H5::Attribute dim1_name = dim1.createAttribute("name", strdatatype, str_name_ds); + H5::Attribute dim2_name = dim2.createAttribute("name", strdatatype, str_name_ds); + H5::Attribute dim3_name = dim3.createAttribute("name", strdatatype, str_name_ds); - void PRISM01_calcPotential(Parameters& pars){ - //builds projected, sliced potential + dim1_name.write(strdatatype, dim1_name_str); + dim2_name.write(strdatatype, dim2_name_str); + dim3_name.write(strdatatype, dim3_name_str); - // setup some coordinates - cout << "Entering PRISM01_calcPotential" << endl; - PRISMATIC_FLOAT_PRECISION yleng = std::ceil(pars.meta.potBound / pars.pixelSize[0]); - PRISMATIC_FLOAT_PRECISION xleng = std::ceil(pars.meta.potBound / pars.pixelSize[1]); - ArrayND<1, vector > xvec(vector(2*(size_t)xleng + 1, 0),{{2*(size_t)xleng + 1}}); - ArrayND<1, vector > yvec(vector(2*(size_t)yleng + 1, 0),{{2*(size_t)yleng + 1}}); + const H5std_string dim1_unit_str("[n_m]"); + const H5std_string dim2_unit_str("[n_m]"); + const H5std_string dim3_unit_str("[n_m]"); + + H5::Attribute dim1_unit = dim1.createAttribute("units", strdatatype, str_name_ds); + H5::Attribute dim2_unit = dim2.createAttribute("units", strdatatype, str_name_ds); + H5::Attribute dim3_unit = dim3.createAttribute("units", strdatatype, str_name_ds); + + dim1_unit.write(strdatatype, dim1_unit_str); + dim2_unit.write(strdatatype, dim2_unit_str); + dim3_unit.write(strdatatype, dim3_unit_str); + } + else { - PRISMATIC_FLOAT_PRECISION tmpx = -xleng; - PRISMATIC_FLOAT_PRECISION tmpy = -yleng; - for (auto &i : xvec)i = tmpx++; - for (auto &j : yvec)j = tmpy++; + ppotential = realslices.openGroup(groupName); } - Array1D xr(vector(2*(size_t)xleng + 1, 0),{{2*(size_t)xleng + 1}}); - Array1D yr(vector(2*(size_t)yleng + 1, 0),{{2*(size_t)yleng + 1}}); - for (auto i=0; i < xr.size(); ++i)xr[i] = (PRISMATIC_FLOAT_PRECISION)xvec[i] * pars.pixelSize[1]; - for (auto j=0; j < yr.size(); ++j)yr[j] = (PRISMATIC_FLOAT_PRECISION)yvec[j] * pars.pixelSize[0]; - - vector unique_species = get_unique_atomic_species(pars); - - // initialize the lookup table - Array3D potentialLookup = zeros_ND<3, PRISMATIC_FLOAT_PRECISION>({{unique_species.size(), 2*(size_t)yleng + 1, 2*(size_t)xleng + 1}}); - - // precompute the unique potentials - fetch_potentials(potentialLookup, unique_species, xr, yr); - - // populate the slices with the projected potentials - generateProjectedPotentials(pars, potentialLookup, unique_species, xvec, yvec); - - if(pars.meta.savePotentialSlices){ - //create new datacube group - H5::Group realslices = pars.outputFile.openGroup("4DSTEM_simulation/data/realslices"); - std::string groupName = "ppotential"; - H5::Group ppotential; - if(pars.fpFlag == 0){ - ppotential = realslices.createGroup(groupName); - - H5::DataSpace attr_dataspace(H5S_SCALAR); - - int group_type = 2; - H5::Attribute emd_group_type = ppotential.createAttribute("emd_group_type",H5::PredType::NATIVE_INT,attr_dataspace); - emd_group_type.write(H5::PredType::NATIVE_INT, &group_type); - - H5::Attribute metadata_group = ppotential.createAttribute("metadata",H5::PredType::NATIVE_INT,attr_dataspace); - int mgroup = 0; - metadata_group.write(H5::PredType::NATIVE_INT, &mgroup); - - //write dimensions - H5::DataSpace str_name_ds(H5S_SCALAR); - H5::StrType strdatatype(H5::PredType::C_S1,256); - - hsize_t x_size[1] = {pars.imageSize[1]}; - hsize_t y_size[1] = {pars.imageSize[0]}; - hsize_t z_size[1] = {pars.numPlanes}; - - Array1D x_dim_data = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({ {pars.imageSize[1]} }); - Array1D y_dim_data = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({ {pars.imageSize[0]} }); - Array1D z_dim_data = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({ {pars.numPlanes} }); - - for(auto i = 0; i < pars.imageSize[1]; i++) x_dim_data[i] = i * pars.pixelSize[1]; - for(auto i = 0; i < pars.imageSize[0]; i++) y_dim_data[i] = i * pars.pixelSize[0]; - for(auto i = 0; i < pars.numPlanes; i++) z_dim_data[i] = i * pars.meta.sliceThickness; - - H5::DataSpace dim1_mspace(1,x_size); - H5::DataSpace dim2_mspace(1,y_size); - H5::DataSpace dim3_mspace(1,z_size); - - H5::DataSet dim1; - H5::DataSet dim2; - H5::DataSet dim3; - - if(sizeof(PRISMATIC_FLOAT_PRECISION) == sizeof(float)){ - dim1 = ppotential.createDataSet("dim1",H5::PredType::NATIVE_FLOAT,dim1_mspace); - dim2 = ppotential.createDataSet("dim2",H5::PredType::NATIVE_FLOAT,dim2_mspace); - dim3 = ppotential.createDataSet("dim3",H5::PredType::NATIVE_FLOAT,dim3_mspace); - - H5::DataSpace dim1_fspace = dim1.getSpace(); - H5::DataSpace dim2_fspace = dim2.getSpace(); - H5::DataSpace dim3_fspace = dim3.getSpace(); - - dim1.write(&x_dim_data[0],H5::PredType::NATIVE_FLOAT,dim1_mspace,dim1_fspace); - dim2.write(&y_dim_data[0],H5::PredType::NATIVE_FLOAT,dim2_mspace,dim2_fspace); - dim3.write(&z_dim_data[0],H5::PredType::NATIVE_FLOAT,dim3_mspace,dim3_fspace); - - }else{ - dim1 = ppotential.createDataSet("dim1",H5::PredType::NATIVE_DOUBLE,dim1_mspace); - dim2 = ppotential.createDataSet("dim2",H5::PredType::NATIVE_DOUBLE,dim2_mspace); - dim3 = ppotential.createDataSet("dim3",H5::PredType::NATIVE_DOUBLE,dim3_mspace); - - H5::DataSpace dim1_fspace = dim1.getSpace(); - H5::DataSpace dim2_fspace = dim2.getSpace(); - H5::DataSpace dim3_fspace = dim3.getSpace(); - - dim1.write(&x_dim_data[0],H5::PredType::NATIVE_DOUBLE,dim1_mspace,dim1_fspace); - dim2.write(&y_dim_data[0],H5::PredType::NATIVE_DOUBLE,dim2_mspace,dim2_fspace); - dim3.write(&z_dim_data[0],H5::PredType::NATIVE_DOUBLE,dim3_mspace,dim3_fspace); - } - - //dimension attributes - const H5std_string dim1_name_str("R_x"); - const H5std_string dim2_name_str("R_y"); - const H5std_string dim3_name_str("R_z"); - - H5::Attribute dim1_name = dim1.createAttribute("name",strdatatype,str_name_ds); - H5::Attribute dim2_name = dim2.createAttribute("name",strdatatype,str_name_ds); - H5::Attribute dim3_name = dim3.createAttribute("name",strdatatype,str_name_ds); - - dim1_name.write(strdatatype,dim1_name_str); - dim2_name.write(strdatatype,dim2_name_str); - dim3_name.write(strdatatype,dim3_name_str); - - const H5std_string dim1_unit_str("[n_m]"); - const H5std_string dim2_unit_str("[n_m]"); - const H5std_string dim3_unit_str("[n_m]"); - - H5::Attribute dim1_unit = dim1.createAttribute("units",strdatatype,str_name_ds); - H5::Attribute dim2_unit = dim2.createAttribute("units",strdatatype,str_name_ds); - H5::Attribute dim3_unit = dim3.createAttribute("units",strdatatype,str_name_ds); - - dim1_unit.write(strdatatype,dim1_unit_str); - dim2_unit.write(strdatatype,dim2_unit_str); - dim3_unit.write(strdatatype,dim3_unit_str); - - }else{ - ppotential = realslices.openGroup(groupName); - } - //read in potential array and stride; also, divide by number of FP to do averaging - Array3D writeBuffer = zeros_ND<3, PRISMATIC_FLOAT_PRECISION>({{pars.imageSize[1],pars.imageSize[0],pars.numPlanes}}); - for(auto x = 0; x < pars.imageSize[1]; x++){ - for(auto y = 0; y < pars.imageSize[0]; y++){ - for(auto z = 0; z < pars.numPlanes; z++){ - writeBuffer.at(x,y,z) = pars.pot.at(z,y,x)/pars.meta.numFP; - } + //read in potential array and stride; also, divide by number of FP to do averaging + Array3D writeBuffer = zeros_ND<3, PRISMATIC_FLOAT_PRECISION>({{pars.imageSize[1], pars.imageSize[0], pars.numPlanes}}); + for (auto x = 0; x < pars.imageSize[1]; x++) + { + for (auto y = 0; y < pars.imageSize[0]; y++) + { + for (auto z = 0; z < pars.numPlanes; z++) + { + writeBuffer.at(x, y, z) = pars.pot.at(z, y, x) / pars.meta.numFP; } } + } - H5::DataSet potSliceData; //declare out here to avoid scoping - std::string slice_name = "realslice"; - if(pars.fpFlag == 0){ - - //create dataset - //imageSize[1] is the x dimension - hsize_t dataDims[3] = {pars.imageSize[1], pars.imageSize[0],pars.numPlanes}; - H5::DataSpace mspace(3,dataDims); + H5::DataSet potSliceData; //declare out here to avoid scoping + std::string slice_name = "realslice"; + if (pars.fpFlag == 0) + { - //switch between float and double, maybe not the best way to do so - if(sizeof(PRISMATIC_FLOAT_PRECISION) == sizeof(float)){ - potSliceData = ppotential.createDataSet(slice_name,H5::PredType::NATIVE_FLOAT,mspace); - }else{ - potSliceData = ppotential.createDataSet(slice_name,H5::PredType::NATIVE_DOUBLE,mspace); - } - }else{ - potSliceData = ppotential.openDataSet(slice_name); - - PRISMATIC_FLOAT_PRECISION* readBuffer = (PRISMATIC_FLOAT_PRECISION*) malloc(pars.imageSize[0]*pars.imageSize[1]*pars.numPlanes*sizeof(PRISMATIC_FLOAT_PRECISION)); - H5::DataSpace rfspace = potSliceData.getSpace(); - hsize_t rmdims[3] = {pars.imageSize[1],pars.imageSize[0],pars.numPlanes}; - H5::DataSpace rmspace(3,rmdims); - - if(sizeof(PRISMATIC_FLOAT_PRECISION) == sizeof(float)){ - potSliceData.read(&readBuffer[0],H5::PredType::NATIVE_FLOAT,rmspace,rfspace); - }else{ - potSliceData.read(&readBuffer[0],H5::PredType::NATIVE_DOUBLE,rmspace,rfspace); - } - - for(auto i = 0; i < pars.imageSize[0]*pars.imageSize[1]*pars.numPlanes; i++) writeBuffer[i] += readBuffer[i]; + //create dataset + //imageSize[1] is the x dimension + hsize_t dataDims[3] = {pars.imageSize[1], pars.imageSize[0], pars.numPlanes}; + H5::DataSpace mspace(3, dataDims); - free(readBuffer); - rfspace.close(); - rmspace.close(); + //switch between float and double, maybe not the best way to do so + if (sizeof(PRISMATIC_FLOAT_PRECISION) == sizeof(float)) + { + potSliceData = ppotential.createDataSet(slice_name, H5::PredType::NATIVE_FLOAT, mspace); } + else + { + potSliceData = ppotential.createDataSet(slice_name, H5::PredType::NATIVE_DOUBLE, mspace); + } + } + else + { + potSliceData = ppotential.openDataSet(slice_name); - hsize_t wmdims[3] = {pars.imageSize[1],pars.imageSize[0],pars.numPlanes}; - H5::DataSpace wfspace = potSliceData.getSpace(); - H5::DataSpace wmspace(3,wmdims); + PRISMATIC_FLOAT_PRECISION *readBuffer = (PRISMATIC_FLOAT_PRECISION *)malloc(pars.imageSize[0] * pars.imageSize[1] * pars.numPlanes * sizeof(PRISMATIC_FLOAT_PRECISION)); + H5::DataSpace rfspace = potSliceData.getSpace(); + hsize_t rmdims[3] = {pars.imageSize[1], pars.imageSize[0], pars.numPlanes}; + H5::DataSpace rmspace(3, rmdims); - if(sizeof(PRISMATIC_FLOAT_PRECISION) == sizeof(float)){ - potSliceData.write(&writeBuffer[0],H5::PredType::NATIVE_FLOAT,wmspace,wfspace); - }else{ - potSliceData.write(&writeBuffer[0],H5::PredType::NATIVE_DOUBLE,wmspace,wfspace); + if (sizeof(PRISMATIC_FLOAT_PRECISION) == sizeof(float)) + { + potSliceData.read(&readBuffer[0], H5::PredType::NATIVE_FLOAT, rmspace, rfspace); } - potSliceData.close(); - + else + { + potSliceData.read(&readBuffer[0], H5::PredType::NATIVE_DOUBLE, rmspace, rfspace); + } + + for (auto i = 0; i < pars.imageSize[0] * pars.imageSize[1] * pars.numPlanes; i++) + writeBuffer[i] += readBuffer[i]; + + free(readBuffer); + rfspace.close(); + rmspace.close(); } + hsize_t wmdims[3] = {pars.imageSize[1], pars.imageSize[0], pars.numPlanes}; + H5::DataSpace wfspace = potSliceData.getSpace(); + H5::DataSpace wmspace(3, wmdims); + + if (sizeof(PRISMATIC_FLOAT_PRECISION) == sizeof(float)) + { + potSliceData.write(&writeBuffer[0], H5::PredType::NATIVE_FLOAT, wmspace, wfspace); + } + else + { + potSliceData.write(&writeBuffer[0], H5::PredType::NATIVE_DOUBLE, wmspace, wfspace); + } + potSliceData.close(); } } +} // namespace Prismatic diff --git a/src/PRISM02_calcSMatrix.cpp b/src/PRISM02_calcSMatrix.cpp index 30a91dc27..1c95cc514 100644 --- a/src/PRISM02_calcSMatrix.cpp +++ b/src/PRISM02_calcSMatrix.cpp @@ -27,425 +27,473 @@ #include "prism_progressbar.h" #endif -namespace Prismatic { - - using namespace std; - const PRISMATIC_FLOAT_PRECISION pi = acos(-1); - const std::complex i(0, 1); - - void setupCoordinates(Parameters &pars) { - - // setup some Fourier coordinates and propagators - pars.imageSize[0] = pars.pot.get_dimj(); - pars.imageSize[1] = pars.pot.get_dimi(); - Array1D qx = makeFourierCoords(pars.imageSize[1], pars.pixelSize[1]); - Array1D qy = makeFourierCoords(pars.imageSize[0], pars.pixelSize[0]); - - pair, Array2D > mesh = meshgrid(qy, qx); - pars.qya = mesh.first; - pars.qxa = mesh.second; - Array2D q2(pars.qya); - transform(pars.qxa.begin(), pars.qxa.end(), - pars.qya.begin(), q2.begin(), [](const PRISMATIC_FLOAT_PRECISION &a, const PRISMATIC_FLOAT_PRECISION &b) { - return a * a + b * b; - }); - pars.q2 = q2; - - // get qMax - long long ncx = (long long) floor((PRISMATIC_FLOAT_PRECISION) pars.imageSize[1] / 2); - PRISMATIC_FLOAT_PRECISION dpx = 1.0 / ((PRISMATIC_FLOAT_PRECISION)pars.imageSize[1] * pars.meta.realspacePixelSize[1]); - long long ncy = (long long) floor((PRISMATIC_FLOAT_PRECISION) pars.imageSize[0] / 2); - PRISMATIC_FLOAT_PRECISION dpy = 1.0 / ((PRISMATIC_FLOAT_PRECISION)pars.imageSize[0] * pars.meta.realspacePixelSize[0]); - pars.qMax = std::min(dpx*(ncx), dpy*(ncy)) / 2; - - // construct anti-aliasing mask - pars.qMask = zeros_ND<2, unsigned int>({{pars.imageSize[0], pars.imageSize[1]}}); +namespace Prismatic +{ + +using namespace std; +const PRISMATIC_FLOAT_PRECISION pi = acos(-1); +const std::complex i(0, 1); + +void setupCoordinates(Parameters &pars) +{ + + // setup some Fourier coordinates and propagators + pars.imageSize[0] = pars.pot.get_dimj(); + pars.imageSize[1] = pars.pot.get_dimi(); + Array1D qx = makeFourierCoords(pars.imageSize[1], pars.pixelSize[1]); + Array1D qy = makeFourierCoords(pars.imageSize[0], pars.pixelSize[0]); + + pair, Array2D> mesh = meshgrid(qy, qx); + pars.qya = mesh.first; + pars.qxa = mesh.second; + Array2D q2(pars.qya); + transform(pars.qxa.begin(), pars.qxa.end(), + pars.qya.begin(), q2.begin(), [](const PRISMATIC_FLOAT_PRECISION &a, const PRISMATIC_FLOAT_PRECISION &b) { + return a * a + b * b; + }); + pars.q2 = q2; + + // get qMax + long long ncx = (long long)floor((PRISMATIC_FLOAT_PRECISION)pars.imageSize[1] / 2); + PRISMATIC_FLOAT_PRECISION dpx = 1.0 / ((PRISMATIC_FLOAT_PRECISION)pars.imageSize[1] * pars.meta.realspacePixelSize[1]); + long long ncy = (long long)floor((PRISMATIC_FLOAT_PRECISION)pars.imageSize[0] / 2); + PRISMATIC_FLOAT_PRECISION dpy = 1.0 / ((PRISMATIC_FLOAT_PRECISION)pars.imageSize[0] * pars.meta.realspacePixelSize[0]); + pars.qMax = std::min(dpx * (ncx), dpy * (ncy)) / 2; + + // construct anti-aliasing mask + pars.qMask = zeros_ND<2, unsigned int>({{pars.imageSize[0], pars.imageSize[1]}}); + { + long offset_x = pars.qMask.get_dimi() / 4; + long offset_y = pars.qMask.get_dimj() / 4; + long ndimy = (long)pars.qMask.get_dimj(); + long ndimx = (long)pars.qMask.get_dimi(); + for (long y = 0; y < pars.qMask.get_dimj() / 2; ++y) { - long offset_x = pars.qMask.get_dimi() / 4; - long offset_y = pars.qMask.get_dimj() / 4; - long ndimy = (long) pars.qMask.get_dimj(); - long ndimx = (long) pars.qMask.get_dimi(); - for (long y = 0; y < pars.qMask.get_dimj() / 2; ++y) { - for (long x = 0; x < pars.qMask.get_dimi() / 2; ++x) { - pars.qMask.at(((y - offset_y) % ndimy + ndimy) % ndimy, - ((x - offset_x) % ndimx + ndimx) % ndimx) = 1; - } + for (long x = 0; x < pars.qMask.get_dimi() / 2; ++x) + { + pars.qMask.at(((y - offset_y) % ndimy + ndimy) % ndimy, + ((x - offset_x) % ndimx + ndimx) % ndimx) = 1; } } + } - // build propagators - pars.prop = zeros_ND<2, std::complex >({{pars.imageSize[0], pars.imageSize[1]}}); - pars.propBack = zeros_ND<2, std::complex >({{pars.imageSize[0], pars.imageSize[1]}}); - for (auto y = 0; y < pars.qMask.get_dimj(); ++y) { - for (auto x = 0; x < pars.qMask.get_dimi(); ++x) { - if (pars.qMask.at(y, x) == 1) { - pars.prop.at(y, x) = exp(-i * pi * complex(pars.lambda, 0) * - complex(pars.meta.sliceThickness, 0) * - complex(pars.q2.at(y, x), 0)); - pars.propBack.at(y, x) = exp(i * pi * complex(pars.lambda, 0) * - complex(pars.tiledCellDim[0], 0) * - complex(pars.q2.at(y, x), 0)); - } + // build propagators + pars.prop = zeros_ND<2, std::complex>({{pars.imageSize[0], pars.imageSize[1]}}); + pars.propBack = zeros_ND<2, std::complex>({{pars.imageSize[0], pars.imageSize[1]}}); + for (auto y = 0; y < pars.qMask.get_dimj(); ++y) + { + for (auto x = 0; x < pars.qMask.get_dimi(); ++x) + { + if (pars.qMask.at(y, x) == 1) + { + pars.prop.at(y, x) = exp(-i * pi * complex(pars.lambda, 0) * + complex(pars.meta.sliceThickness, 0) * + complex(pars.q2.at(y, x), 0)); + pars.propBack.at(y, x) = exp(i * pi * complex(pars.lambda, 0) * + complex(pars.tiledCellDim[0], 0) * + complex(pars.q2.at(y, x), 0)); } } } +} - inline void setupBeams(Parameters &pars) { - // determine which beams (AKA plane waves, or Fourier components) are relevant for the calculation - - Array1D xv = makeFourierCoords(pars.imageSize[1], - (PRISMATIC_FLOAT_PRECISION) 1 / pars.imageSize[1]); - Array1D yv = makeFourierCoords(pars.imageSize[0], - (PRISMATIC_FLOAT_PRECISION) 1 / pars.imageSize[0]); - pair, Array2D > mesh_a = meshgrid(yv, xv); - - // create beam mask and count beams - Prismatic::Array2D mask; - mask = zeros_ND<2, unsigned int>({{pars.imageSize[0], pars.imageSize[1]}}); - pars.numberBeams = 0; - long interp_fx = (long) pars.meta.interpolationFactorX; - long interp_fy = (long) pars.meta.interpolationFactorY; - for (auto y = 0; y < pars.qMask.get_dimj(); ++y) { - for (auto x = 0; x < pars.qMask.get_dimi(); ++x) { - if (pars.q2.at(y, x) < pow(pars.meta.alphaBeamMax / pars.lambda, 2) && - pars.qMask.at(y, x) == 1 && - (long) round(mesh_a.first.at(y, x)) % interp_fy == 0 && - (long) round(mesh_a.second.at(y, x)) % interp_fx == 0) { - mask.at(y, x) = 1; - ++pars.numberBeams; - } +inline void setupBeams(Parameters &pars) +{ + // determine which beams (AKA plane waves, or Fourier components) are relevant for the calculation + + Array1D xv = makeFourierCoords(pars.imageSize[1], + (PRISMATIC_FLOAT_PRECISION)1 / pars.imageSize[1]); + Array1D yv = makeFourierCoords(pars.imageSize[0], + (PRISMATIC_FLOAT_PRECISION)1 / pars.imageSize[0]); + pair, Array2D> mesh_a = meshgrid(yv, xv); + + // create beam mask and count beams + Prismatic::Array2D mask; + mask = zeros_ND<2, unsigned int>({{pars.imageSize[0], pars.imageSize[1]}}); + pars.numberBeams = 0; + long interp_fx = (long)pars.meta.interpolationFactorX; + long interp_fy = (long)pars.meta.interpolationFactorY; + for (auto y = 0; y < pars.qMask.get_dimj(); ++y) + { + for (auto x = 0; x < pars.qMask.get_dimi(); ++x) + { + if (pars.q2.at(y, x) < pow(pars.meta.alphaBeamMax / pars.lambda, 2) && + pars.qMask.at(y, x) == 1 && + (long)round(mesh_a.first.at(y, x)) % interp_fy == 0 && + (long)round(mesh_a.second.at(y, x)) % interp_fx == 0) + { + mask.at(y, x) = 1; + ++pars.numberBeams; } } + } - // number the beams - pars.beams = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>({{pars.imageSize[0], pars.imageSize[1]}}); + // number the beams + pars.beams = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>({{pars.imageSize[0], pars.imageSize[1]}}); + { + int beam_count = 1; + for (auto y = 0; y < pars.qMask.get_dimj(); ++y) { - int beam_count = 1; - for (auto y = 0; y < pars.qMask.get_dimj(); ++y) { - for (auto x = 0; x < pars.qMask.get_dimi(); ++x) { - if (mask.at(y, x) == 1) { - pars.beamsIndex.push_back((size_t) y * pars.qMask.get_dimi() + (size_t) x); - pars.beams.at(y, x) = beam_count++; - } + for (auto x = 0; x < pars.qMask.get_dimi(); ++x) + { + if (mask.at(y, x) == 1) + { + pars.beamsIndex.push_back((size_t)y * pars.qMask.get_dimi() + (size_t)x); + pars.beams.at(y, x) = beam_count++; } } } } +} - inline void setupSMatrixCoordinates(Parameters &pars) { - - // get the indices for the compact S-matrix - pars.qxInd = zeros_ND<1, size_t>({{pars.imageSize[1] / 2}}); - pars.qyInd = zeros_ND<1, size_t>({{pars.imageSize[0] / 2}}); +inline void setupSMatrixCoordinates(Parameters &pars) +{ + + // get the indices for the compact S-matrix + pars.qxInd = zeros_ND<1, size_t>({{pars.imageSize[1] / 2}}); + pars.qyInd = zeros_ND<1, size_t>({{pars.imageSize[0] / 2}}); + { + long n_0 = pars.imageSize[0]; + long n_1 = pars.imageSize[1]; + long n_quarter0 = pars.imageSize[0] / 4; + long n_quarter1 = pars.imageSize[1] / 4; + for (auto i = 0; i < n_quarter0; ++i) { - long n_0 = pars.imageSize[0]; - long n_1 = pars.imageSize[1]; - long n_quarter0 = pars.imageSize[0] / 4; - long n_quarter1 = pars.imageSize[1] / 4; - for (auto i = 0; i < n_quarter0; ++i) { - pars.qyInd[i] = i; - pars.qyInd[i + n_quarter0] = (i - n_quarter0) + n_0; - } - for (auto i = 0; i < n_quarter1; ++i) { - pars.qxInd[i] = i; - pars.qxInd[i + n_quarter1] = (i - n_quarter1) + n_1; - } + pars.qyInd[i] = i; + pars.qyInd[i + n_quarter0] = (i - n_quarter0) + n_0; + } + for (auto i = 0; i < n_quarter1; ++i) + { + pars.qxInd[i] = i; + pars.qxInd[i + n_quarter1] = (i - n_quarter1) + n_1; } } +} - inline void downsampleFourierComponents(Parameters &pars) { - // downsample Fourier components to only keep relevant/nonzero values - pars.imageSizeOutput = pars.imageSize; - pars.imageSizeOutput[0] /= 2; - pars.imageSizeOutput[1] /= 2; - pars.pixelSizeOutput = pars.pixelSize; - pars.pixelSizeOutput[0] *= 2; - pars.pixelSizeOutput[1] *= 2; - - pars.qxaOutput = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>({{pars.qyInd.size(), pars.qxInd.size()}}); - pars.qyaOutput = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>({{pars.qyInd.size(), pars.qxInd.size()}}); - pars.beamsOutput = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>({{pars.qyInd.size(), pars.qxInd.size()}}); - for (auto y = 0; y < pars.qyInd.size(); ++y) { - for (auto x = 0; x < pars.qxInd.size(); ++x) { - pars.qxaOutput.at(y, x) = pars.qxa.at(pars.qyInd[y], pars.qxInd[x]); - pars.qyaOutput.at(y, x) = pars.qya.at(pars.qyInd[y], pars.qxInd[x]); - pars.beamsOutput.at(y, x) = pars.beams.at(pars.qyInd[y], pars.qxInd[x]); - } +inline void downsampleFourierComponents(Parameters &pars) +{ + // downsample Fourier components to only keep relevant/nonzero values + pars.imageSizeOutput = pars.imageSize; + pars.imageSizeOutput[0] /= 2; + pars.imageSizeOutput[1] /= 2; + pars.pixelSizeOutput = pars.pixelSize; + pars.pixelSizeOutput[0] *= 2; + pars.pixelSizeOutput[1] *= 2; + + pars.qxaOutput = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>({{pars.qyInd.size(), pars.qxInd.size()}}); + pars.qyaOutput = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>({{pars.qyInd.size(), pars.qxInd.size()}}); + pars.beamsOutput = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>({{pars.qyInd.size(), pars.qxInd.size()}}); + for (auto y = 0; y < pars.qyInd.size(); ++y) + { + for (auto x = 0; x < pars.qxInd.size(); ++x) + { + pars.qxaOutput.at(y, x) = pars.qxa.at(pars.qyInd[y], pars.qxInd[x]); + pars.qyaOutput.at(y, x) = pars.qya.at(pars.qyInd[y], pars.qxInd[x]); + pars.beamsOutput.at(y, x) = pars.beams.at(pars.qyInd[y], pars.qxInd[x]); } } +} - void propagatePlaneWave_CPU(Parameters &pars, - size_t currentBeam, - Array2D > &psi, - const PRISMATIC_FFTW_PLAN &plan_forward, - const PRISMATIC_FFTW_PLAN &plan_inverse, - mutex &fftw_plan_lock) { - // propagates a single plan wave and fills in the corresponding section of compact S-matrix, very similar to multislice - - psi[pars.beamsIndex[currentBeam]] = 1; - const PRISMATIC_FLOAT_PRECISION slice_size= (PRISMATIC_FLOAT_PRECISION) psi.size(); - PRISMATIC_FFTW_EXECUTE(plan_inverse); - for (auto &i : psi)i /= slice_size; // fftw scales by N, need to correct - const complex *trans_t = &pars.transmission[0]; // pointer to beginning of the transmission array - for (auto a2 = 0; a2 < pars.numPlanes; ++a2) { - for (auto &p:psi)p *= (*trans_t++); // transmit - PRISMATIC_FFTW_EXECUTE(plan_forward); // FFT - for (auto i = psi.begin(), j = pars.prop.begin(); i != psi.end(); ++i, ++j)*i *= (*j); // propagate - PRISMATIC_FFTW_EXECUTE(plan_inverse); // IFFT - for (auto &i : psi)i /= slice_size; // fftw scales by N, need to correct - } - PRISMATIC_FFTW_EXECUTE(plan_forward); // final FFT to get result at detector plane - - // only keep the necessary plane waves - Array2D > psi_small = zeros_ND<2, complex >( - {{pars.qyInd.size(), pars.qxInd.size()}}); - - - unique_lock gatekeeper(fftw_plan_lock); - PRISMATIC_FFTW_PLAN plan_final = PRISMATIC_FFTW_PLAN_DFT_2D(psi_small.get_dimj(), psi_small.get_dimi(), - reinterpret_cast(&psi_small[0]), - reinterpret_cast(&psi_small[0]), - FFTW_BACKWARD, FFTW_ESTIMATE); - gatekeeper.unlock(); - for (auto y = 0; y < pars.qyInd.size(); ++y) { - for (auto x = 0; x < pars.qxInd.size(); ++x) { - psi_small.at(y, x) = psi.at(pars.qyInd[y], pars.qxInd[x]); - } +void propagatePlaneWave_CPU(Parameters &pars, + size_t currentBeam, + Array2D> &psi, + const PRISMATIC_FFTW_PLAN &plan_forward, + const PRISMATIC_FFTW_PLAN &plan_inverse, + mutex &fftw_plan_lock) +{ + // propagates a single plan wave and fills in the corresponding section of compact S-matrix, very similar to multislice + + psi[pars.beamsIndex[currentBeam]] = 1; + const PRISMATIC_FLOAT_PRECISION slice_size = (PRISMATIC_FLOAT_PRECISION)psi.size(); + PRISMATIC_FFTW_EXECUTE(plan_inverse); + for (auto &i : psi) + i /= slice_size; // fftw scales by N, need to correct + const complex *trans_t = &pars.transmission[0]; // pointer to beginning of the transmission array + for (auto a2 = 0; a2 < pars.numPlanes; ++a2) + { + for (auto &p : psi) + p *= (*trans_t++); // transmit + PRISMATIC_FFTW_EXECUTE(plan_forward); // FFT + for (auto i = psi.begin(), j = pars.prop.begin(); i != psi.end(); ++i, ++j) + *i *= (*j); // propagate + PRISMATIC_FFTW_EXECUTE(plan_inverse); // IFFT + for (auto &i : psi) + i /= slice_size; // fftw scales by N, need to correct + } + PRISMATIC_FFTW_EXECUTE(plan_forward); // final FFT to get result at detector plane + + // only keep the necessary plane waves + Array2D> psi_small = zeros_ND<2, complex>( + {{pars.qyInd.size(), pars.qxInd.size()}}); + + unique_lock gatekeeper(fftw_plan_lock); + PRISMATIC_FFTW_PLAN plan_final = PRISMATIC_FFTW_PLAN_DFT_2D(psi_small.get_dimj(), psi_small.get_dimi(), + reinterpret_cast(&psi_small[0]), + reinterpret_cast(&psi_small[0]), + FFTW_BACKWARD, FFTW_ESTIMATE); + gatekeeper.unlock(); + for (auto y = 0; y < pars.qyInd.size(); ++y) + { + for (auto x = 0; x < pars.qxInd.size(); ++x) + { + psi_small.at(y, x) = psi.at(pars.qyInd[y], pars.qxInd[x]); } + } - // final FFT to get the cropped plane wave result in real space - PRISMATIC_FFTW_EXECUTE(plan_final); - gatekeeper.lock(); - PRISMATIC_FFTW_DESTROY_PLAN(plan_final); - gatekeeper.unlock(); + // final FFT to get the cropped plane wave result in real space + PRISMATIC_FFTW_EXECUTE(plan_final); + gatekeeper.lock(); + PRISMATIC_FFTW_DESTROY_PLAN(plan_final); + gatekeeper.unlock(); + + // insert the cropped/propagated plane wave into the relevant slice of the compact S-matrix + complex *S_t = &pars.Scompact[currentBeam * pars.Scompact.get_dimj() * pars.Scompact.get_dimi()]; + const PRISMATIC_FLOAT_PRECISION N_small = (PRISMATIC_FLOAT_PRECISION)psi_small.size(); + for (auto &jj : psi_small) + { + *S_t++ = jj / N_small; + } +} - // insert the cropped/propagated plane wave into the relevant slice of the compact S-matrix - complex *S_t = &pars.Scompact[currentBeam * pars.Scompact.get_dimj() * pars.Scompact.get_dimi()]; - const PRISMATIC_FLOAT_PRECISION N_small = (PRISMATIC_FLOAT_PRECISION) psi_small.size(); - for (auto &jj:psi_small) { - *S_t++ = jj / N_small; +void propagatePlaneWave_CPU_batch(Parameters &pars, + size_t currentBeam, + size_t stopBeam, + Array1D> &psi_stack, + const PRISMATIC_FFTW_PLAN &plan_forward, + const PRISMATIC_FFTW_PLAN &plan_inverse, + mutex &fftw_plan_lock) +{ + // propagates a batch of plane waves and fills in the corresponding sections of compact S-matrix + const size_t slice_size = pars.imageSize[0] * pars.imageSize[1]; + const PRISMATIC_FLOAT_PRECISION slice_size_f = (PRISMATIC_FLOAT_PRECISION)slice_size; + { + int beam_count = 0; + for (auto jj = currentBeam; jj < stopBeam; ++jj) + { + psi_stack[beam_count * slice_size + pars.beamsIndex[jj]] = 1; + ++beam_count; } } - void propagatePlaneWave_CPU_batch(Parameters &pars, - size_t currentBeam, - size_t stopBeam, - Array1D > &psi_stack, - const PRISMATIC_FFTW_PLAN &plan_forward, - const PRISMATIC_FFTW_PLAN &plan_inverse, - mutex &fftw_plan_lock) { - // propagates a batch of plane waves and fills in the corresponding sections of compact S-matrix - const size_t slice_size = pars.imageSize[0]*pars.imageSize[1]; - const PRISMATIC_FLOAT_PRECISION slice_size_f = (PRISMATIC_FLOAT_PRECISION) slice_size; + PRISMATIC_FFTW_EXECUTE(plan_inverse); + for (auto &i : psi_stack) + i /= slice_size_f; // fftw scales by N, need to correct + complex *slice_ptr = &pars.transmission[0]; + for (auto a2 = 0; a2 < pars.numPlanes; ++a2) + { + // transmit each of the probes in the batch + for (auto batch_idx = 0; batch_idx < min(pars.meta.batchSizeCPU, stopBeam - currentBeam); ++batch_idx) { - int beam_count = 0; - for (auto jj = currentBeam; jj < stopBeam; ++jj) { - psi_stack[beam_count*slice_size + pars.beamsIndex[jj]] = 1; - ++beam_count; + auto t_ptr = slice_ptr; // start at the beginning of the current slice + auto psi_ptr = &psi_stack[batch_idx * slice_size]; + for (auto jj = 0; jj < slice_size; ++jj) + { + *psi_ptr++ *= (*t_ptr++); // transmit } } + slice_ptr += slice_size; // advance to point to the beginning of the next potential slice + PRISMATIC_FFTW_EXECUTE(plan_forward); // FFT - PRISMATIC_FFTW_EXECUTE(plan_inverse); - for (auto &i : psi_stack)i /= slice_size_f; // fftw scales by N, need to correct - complex* slice_ptr = &pars.transmission[0]; - for (auto a2 = 0; a2 < pars.numPlanes; ++a2) { - // transmit each of the probes in the batch - for (auto batch_idx = 0; batch_idx < min(pars.meta.batchSizeCPU, stopBeam - currentBeam); ++batch_idx){ - auto t_ptr = slice_ptr; // start at the beginning of the current slice - auto psi_ptr = &psi_stack[batch_idx * slice_size]; - for (auto jj = 0; jj < slice_size; ++jj){ - *psi_ptr++ *= (*t_ptr++);// transmit - } - } - slice_ptr += slice_size; // advance to point to the beginning of the next potential slice - PRISMATIC_FFTW_EXECUTE(plan_forward); // FFT - - // propagate each of the probes in the batch - for (auto batch_idx = 0; batch_idx < min(pars.meta.batchSizeCPU, stopBeam - currentBeam); ++batch_idx) { - auto p_ptr = pars.prop.begin(); - auto psi_ptr = &psi_stack[batch_idx * slice_size]; - for (auto jj = 0; jj < slice_size; ++jj){ - *psi_ptr++ *= (*p_ptr++);// propagate - } + // propagate each of the probes in the batch + for (auto batch_idx = 0; batch_idx < min(pars.meta.batchSizeCPU, stopBeam - currentBeam); ++batch_idx) + { + auto p_ptr = pars.prop.begin(); + auto psi_ptr = &psi_stack[batch_idx * slice_size]; + for (auto jj = 0; jj < slice_size; ++jj) + { + *psi_ptr++ *= (*p_ptr++); // propagate } - PRISMATIC_FFTW_EXECUTE(plan_inverse); // IFFT - for (auto &i : psi_stack)i /= slice_size_f; // fftw scales by N, need to correct } - PRISMATIC_FFTW_EXECUTE(plan_forward); - - // only keep the necessary plane waves - Array2D > psi_small = zeros_ND<2, complex >( - {{pars.qyInd.size(), pars.qxInd.size()}}); - const PRISMATIC_FLOAT_PRECISION N_small = (PRISMATIC_FLOAT_PRECISION) psi_small.size(); - unique_lock gatekeeper(fftw_plan_lock); - PRISMATIC_FFTW_PLAN plan_final = PRISMATIC_FFTW_PLAN_DFT_2D(psi_small.get_dimj(), psi_small.get_dimi(), - reinterpret_cast(&psi_small[0]), - reinterpret_cast(&psi_small[0]), - FFTW_BACKWARD, FFTW_ESTIMATE); - gatekeeper.unlock(); - int batch_idx = 0; - while (currentBeam < stopBeam) { - for (auto y = 0; y < pars.qyInd.size(); ++y) { - for (auto x = 0; x < pars.qxInd.size(); ++x) { - psi_small.at(y, x) = psi_stack[batch_idx*slice_size + pars.qyInd[y]*pars.imageSize[1] + pars.qxInd[x]]; - } - } - PRISMATIC_FFTW_EXECUTE(plan_final); - complex *S_t = &pars.Scompact[currentBeam * pars.Scompact.get_dimj() * pars.Scompact.get_dimi()]; - for (auto &jj:psi_small) { - *S_t++ = jj / N_small; + PRISMATIC_FFTW_EXECUTE(plan_inverse); // IFFT + for (auto &i : psi_stack) + i /= slice_size_f; // fftw scales by N, need to correct + } + PRISMATIC_FFTW_EXECUTE(plan_forward); + + // only keep the necessary plane waves + Array2D> psi_small = zeros_ND<2, complex>( + {{pars.qyInd.size(), pars.qxInd.size()}}); + const PRISMATIC_FLOAT_PRECISION N_small = (PRISMATIC_FLOAT_PRECISION)psi_small.size(); + unique_lock gatekeeper(fftw_plan_lock); + PRISMATIC_FFTW_PLAN plan_final = PRISMATIC_FFTW_PLAN_DFT_2D(psi_small.get_dimj(), psi_small.get_dimi(), + reinterpret_cast(&psi_small[0]), + reinterpret_cast(&psi_small[0]), + FFTW_BACKWARD, FFTW_ESTIMATE); + gatekeeper.unlock(); + int batch_idx = 0; + while (currentBeam < stopBeam) + { + for (auto y = 0; y < pars.qyInd.size(); ++y) + { + for (auto x = 0; x < pars.qxInd.size(); ++x) + { + psi_small.at(y, x) = psi_stack[batch_idx * slice_size + pars.qyInd[y] * pars.imageSize[1] + pars.qxInd[x]]; } - ++currentBeam; - ++batch_idx; } - gatekeeper.lock(); - PRISMATIC_FFTW_DESTROY_PLAN(plan_final); - gatekeeper.unlock(); - } - - void fill_Scompact_CPUOnly(Parameters &pars) { - // populates the compact S-matrix using CPU resources - - - extern mutex fftw_plan_lock; // lock for protecting FFTW plans - - // initialize arrays - pars.Scompact = zeros_ND<3, complex >( - {{pars.numberBeams, pars.imageSize[0] / 2, pars.imageSize[1] / 2}}); - pars.transmission = zeros_ND<3, complex >( - {{pars.pot.get_dimk(), pars.pot.get_dimj(), pars.pot.get_dimi()}}); + PRISMATIC_FFTW_EXECUTE(plan_final); + complex *S_t = &pars.Scompact[currentBeam * pars.Scompact.get_dimj() * pars.Scompact.get_dimi()]; + for (auto &jj : psi_small) { - auto p = pars.pot.begin(); - for (auto &j:pars.transmission)j = exp(i * pars.sigma * (*p++)); + *S_t++ = jj / N_small; } + ++currentBeam; + ++batch_idx; + } + gatekeeper.lock(); + PRISMATIC_FFTW_DESTROY_PLAN(plan_final); + gatekeeper.unlock(); +} + +void fill_Scompact_CPUOnly(Parameters &pars) +{ + // populates the compact S-matrix using CPU resources + + extern mutex fftw_plan_lock; // lock for protecting FFTW plans + + // initialize arrays + pars.Scompact = zeros_ND<3, complex>( + {{pars.numberBeams, pars.imageSize[0] / 2, pars.imageSize[1] / 2}}); + pars.transmission = zeros_ND<3, complex>( + {{pars.pot.get_dimk(), pars.pot.get_dimj(), pars.pot.get_dimi()}}); + { + auto p = pars.pot.begin(); + for (auto &j : pars.transmission) + j = exp(i * pars.sigma * (*p++)); + } - // prepare to launch the calculation - vector workers; - workers.reserve(pars.meta.numThreads); // prevents multiple reallocations - const size_t PRISMATIC_PRINT_FREQUENCY_BEAMS = max((size_t)1,pars.numberBeams / 10); // for printing status - WorkDispatcher dispatcher(0, pars.numberBeams); - pars.meta.batchSizeCPU = min(pars.meta.batchSizeTargetCPU, max((size_t)1,pars.numberBeams / pars.meta.numThreads)); - - // initialize FFTW threads - PRISMATIC_FFTW_INIT_THREADS(); - PRISMATIC_FFTW_PLAN_WITH_NTHREADS(pars.meta.numThreads); - for (auto t = 0; t < pars.meta.numThreads; ++t) { - cout << "Launching thread #" << t << " to compute beams\n"; - workers.push_back(thread([&pars, &dispatcher, &PRISMATIC_PRINT_FREQUENCY_BEAMS]() { - // allocate array for psi just once per thread -// Array2D > psi = zeros_ND<2, complex >( -// {{pars.imageSize[0], pars.imageSize[1]}}); - size_t currentBeam, stopBeam; - currentBeam = stopBeam = 0; - if (dispatcher.getWork(currentBeam, stopBeam, pars.meta.batchSizeCPU)) { - Array1D > psi_stack = zeros_ND<1, complex >( - {{pars.imageSize[0] * pars.imageSize[1] * pars.meta.batchSizeCPU}}); -// PRISMATIC_FFTW_PLAN plan_forward = PRISMATIC_FFTW_PLAN_DFT_2D(psi.get_dimj(), psi.get_dimi(), -// reinterpret_cast(&psi[0]), -// reinterpret_cast(&psi[0]), -// FFTW_FORWARD, FFTW_MEASURE); -// PRISMATIC_FFTW_PLAN plan_inverse = PRISMATIC_FFTW_PLAN_DFT_2D(psi.get_dimj(), psi.get_dimi(), -// reinterpret_cast(&psi[0]), -// reinterpret_cast(&psi[0]), -// FFTW_BACKWARD, FFTW_MEASURE); - - - // setup batch FFTW parameters - const int rank = 2; - int n[] = {(int) pars.imageSize[0], (int) pars.imageSize[1]}; - const int howmany = pars.meta.batchSizeCPU; - int idist = n[0] * n[1]; - int odist = n[0] * n[1]; - int istride = 1; - int ostride = 1; - int *inembed = n; - int *onembed = n; - - unique_lock gatekeeper(fftw_plan_lock); - PRISMATIC_FFTW_PLAN plan_forward = PRISMATIC_FFTW_PLAN_DFT_BATCH(rank, n, howmany, - reinterpret_cast(&psi_stack[0]), - inembed, - istride, idist, - reinterpret_cast(&psi_stack[0]), - onembed, - ostride, odist, - FFTW_FORWARD, FFTW_MEASURE); - PRISMATIC_FFTW_PLAN plan_inverse = PRISMATIC_FFTW_PLAN_DFT_BATCH(rank, n, howmany, - reinterpret_cast(&psi_stack[0]), - inembed, - istride, idist, - reinterpret_cast(&psi_stack[0]), - onembed, - ostride, odist, - FFTW_BACKWARD, FFTW_MEASURE); - gatekeeper.unlock(); // unlock it so we only block as long as necessary to deal with plans - - // main work loop - do { // synchronously get work assignment - while (currentBeam < stopBeam) { - if (currentBeam % PRISMATIC_PRINT_FREQUENCY_BEAMS < pars.meta.batchSizeCPU | - currentBeam == 100) { - cout << "Computing Plane Wave #" << currentBeam << "/" << pars.numberBeams << endl; - } - - // re-zero psi each iteration - memset((void *) &psi_stack[0], 0, - psi_stack.size() * sizeof(complex)); -// propagatePlaneWave_CPU(pars, currentBeam, psi, plan_forward, plan_inverse, fftw_plan_lock); - propagatePlaneWave_CPU_batch(pars, currentBeam, stopBeam, psi_stack, plan_forward, - plan_inverse, fftw_plan_lock); + // prepare to launch the calculation + vector workers; + workers.reserve(pars.meta.numThreads); // prevents multiple reallocations + const size_t PRISMATIC_PRINT_FREQUENCY_BEAMS = max((size_t)1, pars.numberBeams / 10); // for printing status + WorkDispatcher dispatcher(0, pars.numberBeams); + pars.meta.batchSizeCPU = min(pars.meta.batchSizeTargetCPU, max((size_t)1, pars.numberBeams / pars.meta.numThreads)); + + // initialize FFTW threads + PRISMATIC_FFTW_INIT_THREADS(); + PRISMATIC_FFTW_PLAN_WITH_NTHREADS(pars.meta.numThreads); + for (auto t = 0; t < pars.meta.numThreads; ++t) + { + cout << "Launching thread #" << t << " to compute beams\n"; + workers.push_back(thread([&pars, &dispatcher, &PRISMATIC_PRINT_FREQUENCY_BEAMS]() { + // allocate array for psi just once per thread + // Array2D > psi = zeros_ND<2, complex >( + // {{pars.imageSize[0], pars.imageSize[1]}}); + size_t currentBeam, stopBeam; + currentBeam = stopBeam = 0; + if (dispatcher.getWork(currentBeam, stopBeam, pars.meta.batchSizeCPU)) + { + Array1D> psi_stack = zeros_ND<1, complex>( + {{pars.imageSize[0] * pars.imageSize[1] * pars.meta.batchSizeCPU}}); + // PRISMATIC_FFTW_PLAN plan_forward = PRISMATIC_FFTW_PLAN_DFT_2D(psi.get_dimj(), psi.get_dimi(), + // reinterpret_cast(&psi[0]), + // reinterpret_cast(&psi[0]), + // FFTW_FORWARD, FFTW_MEASURE); + // PRISMATIC_FFTW_PLAN plan_inverse = PRISMATIC_FFTW_PLAN_DFT_2D(psi.get_dimj(), psi.get_dimi(), + // reinterpret_cast(&psi[0]), + // reinterpret_cast(&psi[0]), + // FFTW_BACKWARD, FFTW_MEASURE); + + // setup batch FFTW parameters + const int rank = 2; + int n[] = {(int)pars.imageSize[0], (int)pars.imageSize[1]}; + const int howmany = pars.meta.batchSizeCPU; + int idist = n[0] * n[1]; + int odist = n[0] * n[1]; + int istride = 1; + int ostride = 1; + int *inembed = n; + int *onembed = n; + + unique_lock gatekeeper(fftw_plan_lock); + PRISMATIC_FFTW_PLAN plan_forward = PRISMATIC_FFTW_PLAN_DFT_BATCH(rank, n, howmany, + reinterpret_cast(&psi_stack[0]), + inembed, + istride, idist, + reinterpret_cast(&psi_stack[0]), + onembed, + ostride, odist, + FFTW_FORWARD, FFTW_MEASURE); + PRISMATIC_FFTW_PLAN plan_inverse = PRISMATIC_FFTW_PLAN_DFT_BATCH(rank, n, howmany, + reinterpret_cast(&psi_stack[0]), + inembed, + istride, idist, + reinterpret_cast(&psi_stack[0]), + onembed, + ostride, odist, + FFTW_BACKWARD, FFTW_MEASURE); + gatekeeper.unlock(); // unlock it so we only block as long as necessary to deal with plans + + // main work loop + do + { // synchronously get work assignment + while (currentBeam < stopBeam) + { + if (currentBeam % PRISMATIC_PRINT_FREQUENCY_BEAMS < pars.meta.batchSizeCPU | + currentBeam == 100) + { + cout << "Computing Plane Wave #" << currentBeam << "/" << pars.numberBeams << endl; + } + + // re-zero psi each iteration + memset((void *)&psi_stack[0], 0, + psi_stack.size() * sizeof(complex)); + // propagatePlaneWave_CPU(pars, currentBeam, psi, plan_forward, plan_inverse, fftw_plan_lock); + propagatePlaneWave_CPU_batch(pars, currentBeam, stopBeam, psi_stack, plan_forward, + plan_inverse, fftw_plan_lock); #ifdef PRISMATIC_BUILDING_GUI - pars.progressbar->signalScompactUpdate(currentBeam, pars.numberBeams); + pars.progressbar->signalScompactUpdate(currentBeam, pars.numberBeams); #endif - currentBeam = stopBeam; - } - } while (dispatcher.getWork(currentBeam, stopBeam, pars.meta.batchSizeCPU)); - - // clean up plans - gatekeeper.lock(); - PRISMATIC_FFTW_DESTROY_PLAN(plan_forward); - PRISMATIC_FFTW_DESTROY_PLAN(plan_inverse); - gatekeeper.unlock(); - } - })); - } - cout << "Waiting for threads...\n"; - for (auto &t:workers)t.join(); - PRISMATIC_FFTW_CLEANUP_THREADS(); + currentBeam = stopBeam; + } + } while (dispatcher.getWork(currentBeam, stopBeam, pars.meta.batchSizeCPU)); + + // clean up plans + gatekeeper.lock(); + PRISMATIC_FFTW_DESTROY_PLAN(plan_forward); + PRISMATIC_FFTW_DESTROY_PLAN(plan_inverse); + gatekeeper.unlock(); + } + })); + } + cout << "Waiting for threads...\n"; + for (auto &t : workers) + t.join(); + PRISMATIC_FFTW_CLEANUP_THREADS(); #ifdef PRISMATIC_BUILDING_GUI - pars.progressbar->setProgress(100); - pars.progressbar->signalCalcStatusMessage(QString("Plane Wave ") + - QString::number(pars.numberBeams) + - QString("/") + - QString::number(pars.numberBeams)); + pars.progressbar->setProgress(100); + pars.progressbar->signalCalcStatusMessage(QString("Plane Wave ") + + QString::number(pars.numberBeams) + + QString("/") + + QString::number(pars.numberBeams)); #endif //PRISMATIC_BUILDING_GUI - } +} - void PRISM02_calcSMatrix(Parameters &pars) { - // propagate plane waves to construct compact S-matrix +void PRISM02_calcSMatrix(Parameters &pars) +{ + // propagate plane waves to construct compact S-matrix - cout << "Entering PRISM02_calcSMatrix" << endl; + cout << "Entering PRISM02_calcSMatrix" << endl; - // setup some coordinates - setupCoordinates(pars); + // setup some coordinates + setupCoordinates(pars); - // setup the beams and their indices - setupBeams(pars); + // setup the beams and their indices + setupBeams(pars); - // setup coordinates for nonzero values of compact S-matrix - setupSMatrixCoordinates(pars); + // setup coordinates for nonzero values of compact S-matrix + setupSMatrixCoordinates(pars); - cout << "Computing compact S matrix" << endl; + cout << "Computing compact S matrix" << endl; #ifdef PRISMATIC_BUILDING_GUI - pars.progressbar->signalDescriptionMessage("Computing compact S-matrix"); - pars.progressbar->signalScompactUpdate(-1, pars.numberBeams); + pars.progressbar->signalDescriptionMessage("Computing compact S-matrix"); + pars.progressbar->signalScompactUpdate(-1, pars.numberBeams); #endif //PRISMATIC_BUILDING_GUI - // populate compact S-matrix - fill_Scompact(pars); + // populate compact S-matrix + fill_Scompact(pars); - // only keep the relevant/nonzero Fourier components - downsampleFourierComponents(pars); - } + // only keep the relevant/nonzero Fourier components + downsampleFourierComponents(pars); } +} // namespace Prismatic diff --git a/src/PRISM03_calcOutput.cpp b/src/PRISM03_calcOutput.cpp index 77626f3d5..38084fbc5 100644 --- a/src/PRISM03_calcOutput.cpp +++ b/src/PRISM03_calcOutput.cpp @@ -30,60 +30,68 @@ #include "prism_progressbar.h" #endif - -namespace Prismatic { - extern std::mutex fftw_plan_lock; // for synchronizing access to shared FFTW resources - extern std::mutex HDF5_lock; - using namespace std; - const static std::complex i(0, 1); - // this might seem a strange way to get pi, but it's slightly more future proof - const static PRISMATIC_FLOAT_PRECISION pi = std::acos(-1); - Array2D array2D_subset(const Array2D &arr, - const size_t &starty, const size_t &stepy, const size_t &stopy, - const size_t &startx, const size_t &stepx, const size_t &stopx) { - vector _d; - size_t dimx = 0; - size_t dimy = 0; - for (auto y = starty; y < stopy; y += stepy) { - for (auto x = startx; x < stopx; x += stepx) { - _d.push_back(arr.at(y, x)); - } - ++dimy; +namespace Prismatic +{ +extern std::mutex fftw_plan_lock; // for synchronizing access to shared FFTW resources +extern std::mutex HDF5_lock; +using namespace std; +const static std::complex i(0, 1); +// this might seem a strange way to get pi, but it's slightly more future proof +const static PRISMATIC_FLOAT_PRECISION pi = std::acos(-1); +Array2D array2D_subset(const Array2D &arr, + const size_t &starty, const size_t &stepy, const size_t &stopy, + const size_t &startx, const size_t &stepx, const size_t &stopx) +{ + vector _d; + size_t dimx = 0; + size_t dimy = 0; + for (auto y = starty; y < stopy; y += stepy) + { + for (auto x = startx; x < stopx; x += stepx) + { + _d.push_back(arr.at(y, x)); } - for (auto x = startx; x < stopx; x += stepx)++dimx; - Array2D result(_d, {{dimy, dimx}}); - return result; + ++dimy; } + for (auto x = startx; x < stopx; x += stepx) + ++dimx; + Array2D result(_d, {{dimy, dimx}}); + return result; +} - void setupCoordinates_2(Parameters &pars) { - Array1D xR = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{2}}); - xR[0] = pars.scanWindowXMin * pars.tiledCellDim[2]; - xR[1] = pars.scanWindowXMax * pars.tiledCellDim[2]; - Array1D yR = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{2}}); - yR[0] = pars.scanWindowYMin * pars.tiledCellDim[1]; - yR[1] = pars.scanWindowYMax * pars.tiledCellDim[1]; - - PRISMATIC_FLOAT_PRECISION probeStepX; - PRISMATIC_FLOAT_PRECISION probeStepY; - if(pars.meta.nyquistSampling){ - int numX = nyquistProbes(pars,2); //x is dim 2 - int numY = nyquistProbes(pars,1); //y is dim 1 - probeStepX = pars.tiledCellDim[2]/numX; - probeStepY = pars.tiledCellDim[1]/numY; - }else{ - probeStepX = pars.meta.probeStepX; - probeStepY = pars.meta.probeStepY; - } - - vector xp_d = vecFromRange(xR[0], probeStepX, xR[1]); - vector yp_d = vecFromRange(yR[0], probeStepY, yR[1]); -// vector xp_d = vecFromRange(xR[0] + pars.meta.probeStepX / 2, pars.meta.probeStepX, xR[1] - pars.meta.probeStepX / 2); -// vector yp_d = vecFromRange(yR[0] + pars.meta.probeStepY / 2, pars.meta.probeStepY, yR[1] - pars.meta.probeStepY / 2); +void setupCoordinates_2(Parameters &pars) +{ + Array1D xR = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{2}}); + xR[0] = pars.scanWindowXMin * pars.tiledCellDim[2]; + xR[1] = pars.scanWindowXMax * pars.tiledCellDim[2]; + Array1D yR = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{2}}); + yR[0] = pars.scanWindowYMin * pars.tiledCellDim[1]; + yR[1] = pars.scanWindowYMax * pars.tiledCellDim[1]; + + PRISMATIC_FLOAT_PRECISION probeStepX; + PRISMATIC_FLOAT_PRECISION probeStepY; + if (pars.meta.nyquistSampling) + { + int numX = nyquistProbes(pars, 2); //x is dim 2 + int numY = nyquistProbes(pars, 1); //y is dim 1 + probeStepX = pars.tiledCellDim[2] / numX; + probeStepY = pars.tiledCellDim[1] / numY; + } + else + { + probeStepX = pars.meta.probeStepX; + probeStepY = pars.meta.probeStepY; + } + + vector xp_d = vecFromRange(xR[0], probeStepX, xR[1]); + vector yp_d = vecFromRange(yR[0], probeStepY, yR[1]); + // vector xp_d = vecFromRange(xR[0] + pars.meta.probeStepX / 2, pars.meta.probeStepX, xR[1] - pars.meta.probeStepX / 2); + // vector yp_d = vecFromRange(yR[0] + pars.meta.probeStepY / 2, pars.meta.probeStepY, yR[1] - pars.meta.probeStepY / 2); - Array1D xp(xp_d, {{xp_d.size()}}); - Array1D yp(yp_d, {{yp_d.size()}}); + Array1D xp(xp_d, {{xp_d.size()}}); + Array1D yp(yp_d, {{yp_d.size()}}); - /* + /* if(pars.meta.saveRealSpaceCoords){ pair< Array2D, Array2D > real_mesh = meshgrid(xp,yp); std::string x_name = pars.meta.outputFolder + "real_space_x.mrc"; @@ -93,442 +101,474 @@ namespace Prismatic { } */ - pars.xp = xp; - pars.yp = yp; - } + pars.xp = xp; + pars.yp = yp; +} - void setupDetector(Parameters &pars) { - // setup coordinates related to detector size, angles, and image output - - pars.alphaMax = pars.qMax * pars.lambda; - - vector detectorAngles_d = vecFromRange(pars.meta.detectorAngleStep / 2, pars.meta.detectorAngleStep, - pars.alphaMax - pars.meta.detectorAngleStep / 2); - Array1D detectorAngles(detectorAngles_d, {{detectorAngles_d.size()}}); - pars.detectorAngles = detectorAngles; - PRISMATIC_FLOAT_PRECISION r_0 = pars.imageSizeOutput[0] / pars.meta.interpolationFactorY / 2; - PRISMATIC_FLOAT_PRECISION r_1 = pars.imageSizeOutput[1] / pars.meta.interpolationFactorX / 2; - vector yVec_d = vecFromRange(-r_0, (PRISMATIC_FLOAT_PRECISION) 1.0, r_0 - 1); - vector xVec_d = vecFromRange(-r_1, (PRISMATIC_FLOAT_PRECISION) 1.0, r_1 - 1); - Array1D yVec(yVec_d, {{yVec_d.size()}}); - Array1D xVec(xVec_d, {{xVec_d.size()}}); - pars.yVec = yVec; - pars.xVec = xVec; - pars.Ndet = pars.detectorAngles.size(); - } +void setupDetector(Parameters &pars) +{ + // setup coordinates related to detector size, angles, and image output + + pars.alphaMax = pars.qMax * pars.lambda; + + vector detectorAngles_d = vecFromRange(pars.meta.detectorAngleStep / 2, pars.meta.detectorAngleStep, + pars.alphaMax - pars.meta.detectorAngleStep / 2); + Array1D detectorAngles(detectorAngles_d, {{detectorAngles_d.size()}}); + pars.detectorAngles = detectorAngles; + PRISMATIC_FLOAT_PRECISION r_0 = pars.imageSizeOutput[0] / pars.meta.interpolationFactorY / 2; + PRISMATIC_FLOAT_PRECISION r_1 = pars.imageSizeOutput[1] / pars.meta.interpolationFactorX / 2; + vector yVec_d = vecFromRange(-r_0, (PRISMATIC_FLOAT_PRECISION)1.0, r_0 - 1); + vector xVec_d = vecFromRange(-r_1, (PRISMATIC_FLOAT_PRECISION)1.0, r_1 - 1); + Array1D yVec(yVec_d, {{yVec_d.size()}}); + Array1D xVec(xVec_d, {{xVec_d.size()}}); + pars.yVec = yVec; + pars.xVec = xVec; + pars.Ndet = pars.detectorAngles.size(); +} - void setupBeams_2(Parameters &pars) { - // setup some coordinates for the beams - - Array2D beamsReduce = array2D_subset(pars.beamsOutput, - 0, pars.meta.interpolationFactorY, - pars.beamsOutput.get_dimj(), - 0, pars.meta.interpolationFactorX, - pars.beamsOutput.get_dimi()); - - vector imageSizeReduce{beamsReduce.get_dimj(), beamsReduce.get_dimi()}; - pars.imageSizeReduce = imageSizeReduce; - pars.xyBeams = zeros_ND<2, long>({{pars.beamsIndex.size(), 2}}); - - for (auto a0 = 1; a0 <= pars.beamsIndex.size(); ++a0) { - for (auto y = 0; y < beamsReduce.get_dimj(); ++y) { - for (auto x = 0; x < beamsReduce.get_dimi(); ++x) { - if (beamsReduce.at(y, x) == a0) { - pars.xyBeams.at(a0 - 1, 0) = y; - pars.xyBeams.at(a0 - 1, 1) = x; - } +void setupBeams_2(Parameters &pars) +{ + // setup some coordinates for the beams + + Array2D beamsReduce = array2D_subset(pars.beamsOutput, + 0, pars.meta.interpolationFactorY, + pars.beamsOutput.get_dimj(), + 0, pars.meta.interpolationFactorX, + pars.beamsOutput.get_dimi()); + + vector imageSizeReduce{beamsReduce.get_dimj(), beamsReduce.get_dimi()}; + pars.imageSizeReduce = imageSizeReduce; + pars.xyBeams = zeros_ND<2, long>({{pars.beamsIndex.size(), 2}}); + + for (auto a0 = 1; a0 <= pars.beamsIndex.size(); ++a0) + { + for (auto y = 0; y < beamsReduce.get_dimj(); ++y) + { + for (auto x = 0; x < beamsReduce.get_dimi(); ++x) + { + if (beamsReduce.at(y, x) == a0) + { + pars.xyBeams.at(a0 - 1, 0) = y; + pars.xyBeams.at(a0 - 1, 1) = x; } } } } +} - void createStack_integrate(Parameters &pars) { - // create output of a size corresponding to 3D mode (integration) +void createStack_integrate(Parameters &pars) +{ + // create output of a size corresponding to 3D mode (integration) + + pars.output = zeros_ND<4, PRISMATIC_FLOAT_PRECISION>({{1, pars.yp.size(), pars.xp.size(), pars.Ndet}}); + size_t numLayers = 1; + PRISMATIC_FLOAT_PRECISION dummy = 1.0; + if (pars.meta.saveDPC_CoM) + pars.DPC_CoM = zeros_ND<4, PRISMATIC_FLOAT_PRECISION>({{1, pars.yp.size(), pars.xp.size(), 2}}); + if (pars.meta.save4DOutput && (pars.fpFlag == 0)) + setup4DOutput(pars, numLayers, dummy); +} - pars.output = zeros_ND<4, PRISMATIC_FLOAT_PRECISION>({{1, pars.yp.size(), pars.xp.size(), pars.Ndet}}); - size_t numLayers = 1; - PRISMATIC_FLOAT_PRECISION dummy = 1.0; - if(pars.meta.saveDPC_CoM) pars.DPC_CoM = zeros_ND<4, PRISMATIC_FLOAT_PRECISION>({{1,pars.yp.size(),pars.xp.size(),2}}); - if(pars.meta.save4DOutput && (pars.fpFlag == 0)) setup4DOutput(pars, numLayers, dummy); - } +void setupFourierCoordinates(Parameters &pars) +{ + // create Fourier space coordinates + + pars.qxaReduce = array2D_subset(pars.qxaOutput, + 0, pars.meta.interpolationFactorY, pars.qxaOutput.get_dimj(), + 0, pars.meta.interpolationFactorX, pars.qxaOutput.get_dimi()); + pars.qyaReduce = array2D_subset(pars.qyaOutput, + 0, pars.meta.interpolationFactorY, pars.qyaOutput.get_dimj(), + 0, pars.meta.interpolationFactorX, pars.qyaOutput.get_dimi()); + + //grabbing 1D vector of fourier coordinates for output storage + Array1D qx = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{pars.qxaReduce.get_dimi()}}); + Array1D qy = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{pars.qyaReduce.get_dimj()}}); + for (auto i = 0; i < pars.qxaReduce.get_dimi(); i++) + qx[i] = pars.qxaReduce.at(0, i); + for (auto j = 0; j < pars.qyaReduce.get_dimj(); j++) + qy[j] = pars.qyaReduce.at(j, 0); + + pars.qx = qx; + pars.qy = qy; + + pars.q1 = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>({{pars.imageSizeReduce[0], pars.imageSizeReduce[1]}}); + pars.q2 = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>({{pars.imageSizeReduce[0], pars.imageSizeReduce[1]}}); +} - void setupFourierCoordinates(Parameters &pars) { - // create Fourier space coordinates - - pars.qxaReduce = array2D_subset(pars.qxaOutput, - 0, pars.meta.interpolationFactorY, pars.qxaOutput.get_dimj(), - 0, pars.meta.interpolationFactorX, pars.qxaOutput.get_dimi()); - pars.qyaReduce = array2D_subset(pars.qyaOutput, - 0, pars.meta.interpolationFactorY, pars.qyaOutput.get_dimj(), - 0, pars.meta.interpolationFactorX, pars.qyaOutput.get_dimi()); - - //grabbing 1D vector of fourier coordinates for output storage - Array1D qx = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{pars.qxaReduce.get_dimi()}}); - Array1D qy = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{pars.qyaReduce.get_dimj()}}); - for(auto i = 0; i < pars.qxaReduce.get_dimi(); i++) qx[i] = pars.qxaReduce.at(0,i); - for(auto j = 0; j < pars.qyaReduce.get_dimj(); j++) qy[j] = pars.qyaReduce.at(j,0); - - pars.qx = qx; - pars.qy = qy; - - pars.q1 = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>({{pars.imageSizeReduce[0], pars.imageSizeReduce[1]}}); - pars.q2 = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>({{pars.imageSizeReduce[0], pars.imageSizeReduce[1]}}); - } +std::pair>, Array2D>> +getSinglePRISMProbe_CPU(Parameters &pars, const PRISMATIC_FLOAT_PRECISION xp, const PRISMATIC_FLOAT_PRECISION yp) +{ + // compute a single probe (for comparison with multislice in the GUI) - std::pair >, Array2D< std::complex > > - getSinglePRISMProbe_CPU(Parameters &pars, const PRISMATIC_FLOAT_PRECISION xp, const PRISMATIC_FLOAT_PRECISION yp){ - - // compute a single probe (for comparison with multislice in the GUI) - - Array2D< std::complex > realspace_probe; - Array2D< std::complex > kspace_probe; - - Array2D > psi = Prismatic::zeros_ND<2, std::complex > ( - {{pars.imageSizeReduce[0], pars.imageSizeReduce[1]}}); - unique_lock gatekeeper(fftw_plan_lock); - PRISMATIC_FFTW_PLAN plan = PRISMATIC_FFTW_PLAN_DFT_2D(psi.get_dimj(), psi.get_dimi(), - reinterpret_cast(&psi[0]), - reinterpret_cast(&psi[0]), - FFTW_FORWARD, FFTW_ESTIMATE); - gatekeeper.unlock(); - const static std::complex i(0, 1); - const static PRISMATIC_FLOAT_PRECISION pi = std::acos(-1); - - // setup some coordinates - PRISMATIC_FLOAT_PRECISION x0 = xp / pars.pixelSizeOutput[1]; - PRISMATIC_FLOAT_PRECISION y0 = yp / pars.pixelSizeOutput[0]; - - Array1D x = pars.xVec + round(x0); - // the second call to fmod here is to make sure the result is positive - transform(x.begin(), x.end(), x.begin(), [&pars](PRISMATIC_FLOAT_PRECISION &a) { - return fmod((PRISMATIC_FLOAT_PRECISION) pars.imageSizeOutput[1] + - fmod(a, (PRISMATIC_FLOAT_PRECISION) pars.imageSizeOutput[1]), - (PRISMATIC_FLOAT_PRECISION) pars.imageSizeOutput[1]); - - }); - Array1D y = pars.yVec + round(y0); - transform(y.begin(), y.end(), y.begin(), [&pars](PRISMATIC_FLOAT_PRECISION &a) { - return fmod((PRISMATIC_FLOAT_PRECISION) pars.imageSizeOutput[0] + - fmod(a, (PRISMATIC_FLOAT_PRECISION) pars.imageSizeOutput[0]), - (PRISMATIC_FLOAT_PRECISION) pars.imageSizeOutput[0]); - - }); - Array2D intOutput = Prismatic::zeros_ND<2, PRISMATIC_FLOAT_PRECISION>( - {{pars.imageSizeReduce[0], pars.imageSizeReduce[1]}}); - - memset(&psi[0], 0, sizeof(std::complex)*psi.size()); - for (auto a4 = 0; a4 < pars.beamsIndex.size(); ++a4) { - PRISMATIC_FLOAT_PRECISION yB = pars.xyBeams.at(a4, 0); - PRISMATIC_FLOAT_PRECISION xB = pars.xyBeams.at(a4, 1); - - if (abs(pars.psiProbeInit.at(yB, xB)) > 0) { - PRISMATIC_FLOAT_PRECISION q0_0 = pars.qxaReduce.at(yB, xB); - PRISMATIC_FLOAT_PRECISION q0_1 = pars.qyaReduce.at(yB, xB); - std::complex phaseShift = exp( - -2 * pi * i * (q0_0 * (xp + pars.xTiltShift) + - q0_1 * (yp + pars.yTiltShift))); - const std::complex tmp_const = pars.psiProbeInit.at(yB, xB) * phaseShift; - auto psi_ptr = psi.begin(); - for (auto j = 0; j < y.size(); ++j) { - for (auto i = 0; i < x.size(); ++i) { - *psi_ptr++ += (tmp_const * pars.Scompact.at(a4, y[j], x[i])); - } + Array2D> realspace_probe; + Array2D> kspace_probe; + + Array2D> psi = Prismatic::zeros_ND<2, std::complex>( + {{pars.imageSizeReduce[0], pars.imageSizeReduce[1]}}); + unique_lock gatekeeper(fftw_plan_lock); + PRISMATIC_FFTW_PLAN plan = PRISMATIC_FFTW_PLAN_DFT_2D(psi.get_dimj(), psi.get_dimi(), + reinterpret_cast(&psi[0]), + reinterpret_cast(&psi[0]), + FFTW_FORWARD, FFTW_ESTIMATE); + gatekeeper.unlock(); + const static std::complex i(0, 1); + const static PRISMATIC_FLOAT_PRECISION pi = std::acos(-1); + + // setup some coordinates + PRISMATIC_FLOAT_PRECISION x0 = xp / pars.pixelSizeOutput[1]; + PRISMATIC_FLOAT_PRECISION y0 = yp / pars.pixelSizeOutput[0]; + + Array1D x = pars.xVec + round(x0); + // the second call to fmod here is to make sure the result is positive + transform(x.begin(), x.end(), x.begin(), [&pars](PRISMATIC_FLOAT_PRECISION &a) { + return fmod((PRISMATIC_FLOAT_PRECISION)pars.imageSizeOutput[1] + + fmod(a, (PRISMATIC_FLOAT_PRECISION)pars.imageSizeOutput[1]), + (PRISMATIC_FLOAT_PRECISION)pars.imageSizeOutput[1]); + }); + Array1D y = pars.yVec + round(y0); + transform(y.begin(), y.end(), y.begin(), [&pars](PRISMATIC_FLOAT_PRECISION &a) { + return fmod((PRISMATIC_FLOAT_PRECISION)pars.imageSizeOutput[0] + + fmod(a, (PRISMATIC_FLOAT_PRECISION)pars.imageSizeOutput[0]), + (PRISMATIC_FLOAT_PRECISION)pars.imageSizeOutput[0]); + }); + Array2D intOutput = Prismatic::zeros_ND<2, PRISMATIC_FLOAT_PRECISION>( + {{pars.imageSizeReduce[0], pars.imageSizeReduce[1]}}); + + memset(&psi[0], 0, sizeof(std::complex) * psi.size()); + for (auto a4 = 0; a4 < pars.beamsIndex.size(); ++a4) + { + PRISMATIC_FLOAT_PRECISION yB = pars.xyBeams.at(a4, 0); + PRISMATIC_FLOAT_PRECISION xB = pars.xyBeams.at(a4, 1); + + if (abs(pars.psiProbeInit.at(yB, xB)) > 0) + { + PRISMATIC_FLOAT_PRECISION q0_0 = pars.qxaReduce.at(yB, xB); + PRISMATIC_FLOAT_PRECISION q0_1 = pars.qyaReduce.at(yB, xB); + std::complex phaseShift = exp( + -2 * pi * i * (q0_0 * (xp + pars.xTiltShift) + q0_1 * (yp + pars.yTiltShift))); + const std::complex tmp_const = pars.psiProbeInit.at(yB, xB) * phaseShift; + auto psi_ptr = psi.begin(); + for (auto j = 0; j < y.size(); ++j) + { + for (auto i = 0; i < x.size(); ++i) + { + *psi_ptr++ += (tmp_const * pars.Scompact.at(a4, y[j], x[i])); } } } - realspace_probe = psi; - PRISMATIC_FFTW_EXECUTE(plan); - kspace_probe = psi; - gatekeeper.lock(); - PRISMATIC_FFTW_DESTROY_PLAN(plan); - gatekeeper.unlock(); - return std::make_pair(realspace_probe, kspace_probe); } - void buildPRISMOutput_CPUOnly(Parameters &pars){ - - // launch threads to compute results for batches of xp, yp - // I do this by dividing the xp points among threads, and each computes - // all of the relevant yp for each of its xp. This seems an okay strategy - // as long as the number of xp and yp are similar. - // If that is not the case - // this may need to be adapted - - - // initialize FFTW threads - PRISMATIC_FFTW_INIT_THREADS(); - PRISMATIC_FFTW_PLAN_WITH_NTHREADS(pars.meta.numThreads); - vector workers; - workers.reserve(pars.meta.numThreads); // prevents multiple reallocations - const size_t PRISMATIC_PRINT_FREQUENCY_PROBES = max((size_t)1,pars.xp.size() * pars.yp.size() / 10); // for printing status - WorkDispatcher dispatcher(0, pars.xp.size() * pars.yp.size()); - for (auto t = 0; t < pars.meta.numThreads; ++t) { - cout << "Launching CPU worker thread #" << t << " to compute partial PRISM result\n"; - workers.push_back(thread([&pars, &dispatcher, &PRISMATIC_PRINT_FREQUENCY_PROBES]() { - size_t Nstart, Nstop, ay, ax; - Nstart=Nstop=0; - if(dispatcher.getWork(Nstart, Nstop)) { // synchronously get work assignment - Array2D > psi = Prismatic::zeros_ND<2, std::complex > ( - {{pars.imageSizeReduce[0], pars.imageSizeReduce[1]}}); - unique_lock gatekeeper(fftw_plan_lock); - PRISMATIC_FFTW_PLAN plan = PRISMATIC_FFTW_PLAN_DFT_2D(psi.get_dimj(), psi.get_dimi(), - reinterpret_cast(&psi[0]), - reinterpret_cast(&psi[0]), - FFTW_FORWARD, FFTW_MEASURE); - gatekeeper.unlock(); - - // main work loop - do { - while (Nstart < Nstop) { - if (Nstart % PRISMATIC_PRINT_FREQUENCY_PROBES == 0 | Nstart == 100){ - cout << "Computing Probe Position5 #" << Nstart << "/" << pars.xp.size() * pars.yp.size() << endl; - } - ay = Nstart / pars.xp.size(); - ax = Nstart % pars.xp.size(); - buildSignal_CPU(pars, ay, ax, plan, psi); + realspace_probe = psi; + PRISMATIC_FFTW_EXECUTE(plan); + kspace_probe = psi; + gatekeeper.lock(); + PRISMATIC_FFTW_DESTROY_PLAN(plan); + gatekeeper.unlock(); + return std::make_pair(realspace_probe, kspace_probe); +} +void buildPRISMOutput_CPUOnly(Parameters &pars) +{ + + // launch threads to compute results for batches of xp, yp + // I do this by dividing the xp points among threads, and each computes + // all of the relevant yp for each of its xp. This seems an okay strategy + // as long as the number of xp and yp are similar. + // If that is not the case + // this may need to be adapted + + // initialize FFTW threads + PRISMATIC_FFTW_INIT_THREADS(); + PRISMATIC_FFTW_PLAN_WITH_NTHREADS(pars.meta.numThreads); + vector workers; + workers.reserve(pars.meta.numThreads); // prevents multiple reallocations + const size_t PRISMATIC_PRINT_FREQUENCY_PROBES = max((size_t)1, pars.xp.size() * pars.yp.size() / 10); // for printing status + WorkDispatcher dispatcher(0, pars.xp.size() * pars.yp.size()); + for (auto t = 0; t < pars.meta.numThreads; ++t) + { + cout << "Launching CPU worker thread #" << t << " to compute partial PRISM result\n"; + workers.push_back(thread([&pars, &dispatcher, &PRISMATIC_PRINT_FREQUENCY_PROBES]() { + size_t Nstart, Nstop, ay, ax; + Nstart = Nstop = 0; + if (dispatcher.getWork(Nstart, Nstop)) + { // synchronously get work assignment + Array2D> psi = Prismatic::zeros_ND<2, std::complex>( + {{pars.imageSizeReduce[0], pars.imageSizeReduce[1]}}); + unique_lock gatekeeper(fftw_plan_lock); + PRISMATIC_FFTW_PLAN plan = PRISMATIC_FFTW_PLAN_DFT_2D(psi.get_dimj(), psi.get_dimi(), + reinterpret_cast(&psi[0]), + reinterpret_cast(&psi[0]), + FFTW_FORWARD, FFTW_MEASURE); + gatekeeper.unlock(); + + // main work loop + do + { + while (Nstart < Nstop) + { + if (Nstart % PRISMATIC_PRINT_FREQUENCY_PROBES == 0 | Nstart == 100) + { + cout << "Computing Probe Position5 #" << Nstart << "/" << pars.xp.size() * pars.yp.size() << endl; + } + ay = Nstart / pars.xp.size(); + ax = Nstart % pars.xp.size(); + buildSignal_CPU(pars, ay, ax, plan, psi); #ifdef PRISMATIC_BUILDING_GUI - pars.progressbar->signalOutputUpdate(Nstart, pars.xp.size() * pars.yp.size()); + pars.progressbar->signalOutputUpdate(Nstart, pars.xp.size() * pars.yp.size()); #endif - ++Nstart; - } - } while(dispatcher.getWork(Nstart, Nstop)); - gatekeeper.lock(); - PRISMATIC_FFTW_DESTROY_PLAN(plan); - gatekeeper.unlock(); - } - })); - } - // synchronize - cout << "Waiting for threads...\n"; - for (auto &t:workers)t.join(); - PRISMATIC_FFTW_CLEANUP_THREADS(); + ++Nstart; + } + } while (dispatcher.getWork(Nstart, Nstop)); + gatekeeper.lock(); + PRISMATIC_FFTW_DESTROY_PLAN(plan); + gatekeeper.unlock(); + } + })); } + // synchronize + cout << "Waiting for threads...\n"; + for (auto &t : workers) + t.join(); + PRISMATIC_FFTW_CLEANUP_THREADS(); +} +void buildSignal_CPU(Parameters &pars, + const size_t &ay, + const size_t &ax, + PRISMATIC_FFTW_PLAN &plan, + Array2D> &psi) +{ + // build the output for a single probe position using CPU resources - void buildSignal_CPU(Parameters &pars, - const size_t &ay, - const size_t &ax, - PRISMATIC_FFTW_PLAN& plan, - Array2D >& psi){ - // build the output for a single probe position using CPU resources - - const static std::complex i(0, 1); - const static PRISMATIC_FLOAT_PRECISION pi = std::acos(-1); - - // setup some coordinates - PRISMATIC_FLOAT_PRECISION x0 = pars.xp[ax] / pars.pixelSizeOutput[1]; - PRISMATIC_FLOAT_PRECISION y0 = pars.yp[ay] / pars.pixelSizeOutput[0]; - Array1D x = pars.xVec + round(x0); - - // the second call to fmod here is to make sure the result is positive - transform(x.begin(), x.end(), x.begin(), [&pars](PRISMATIC_FLOAT_PRECISION &a) { - return fmod((PRISMATIC_FLOAT_PRECISION) pars.imageSizeOutput[1] + - fmod(a, (PRISMATIC_FLOAT_PRECISION) pars.imageSizeOutput[1]), - (PRISMATIC_FLOAT_PRECISION) pars.imageSizeOutput[1]); - - }); - Array1D y = pars.yVec + round(y0); - transform(y.begin(), y.end(), y.begin(), [&pars](PRISMATIC_FLOAT_PRECISION &a) { - return fmod((PRISMATIC_FLOAT_PRECISION) pars.imageSizeOutput[0] + - fmod(a, (PRISMATIC_FLOAT_PRECISION) pars.imageSizeOutput[0]), - (PRISMATIC_FLOAT_PRECISION) pars.imageSizeOutput[0]); - - }); - Array2D intOutput = Prismatic::zeros_ND<2, PRISMATIC_FLOAT_PRECISION>( - {{pars.imageSizeReduce[0], pars.imageSizeReduce[1]}}); - - memset(&psi[0], 0, sizeof(std::complex)*psi.size()); - - for (auto a4 = 0; a4 < pars.beamsIndex.size(); ++a4) { - PRISMATIC_FLOAT_PRECISION yB = pars.xyBeams.at(a4, 0); - PRISMATIC_FLOAT_PRECISION xB = pars.xyBeams.at(a4, 1); - - if (abs(pars.psiProbeInit.at(yB, xB)) > 0) { - PRISMATIC_FLOAT_PRECISION q0_0 = pars.qxaReduce.at(yB, xB); - PRISMATIC_FLOAT_PRECISION q0_1 = pars.qyaReduce.at(yB, xB); - std::complex phaseShift = exp( - -2 * pi * i * (q0_0 * (pars.xp[ax] + pars.xTiltShift) + - q0_1 * (pars.yp[ay] + pars.yTiltShift))); - - - const std::complex tmp_const = pars.psiProbeInit.at(yB, xB) * phaseShift; - auto psi_ptr = psi.begin(); - for (auto j = 0; j < y.size(); ++j) { - for (auto i = 0; i < x.size(); ++i) { - *psi_ptr++ += (tmp_const * pars.Scompact.at(a4, y[j], x[i])); - } + const static std::complex i(0, 1); + const static PRISMATIC_FLOAT_PRECISION pi = std::acos(-1); + + // setup some coordinates + PRISMATIC_FLOAT_PRECISION x0 = pars.xp[ax] / pars.pixelSizeOutput[1]; + PRISMATIC_FLOAT_PRECISION y0 = pars.yp[ay] / pars.pixelSizeOutput[0]; + Array1D x = pars.xVec + round(x0); + + // the second call to fmod here is to make sure the result is positive + transform(x.begin(), x.end(), x.begin(), [&pars](PRISMATIC_FLOAT_PRECISION &a) { + return fmod((PRISMATIC_FLOAT_PRECISION)pars.imageSizeOutput[1] + + fmod(a, (PRISMATIC_FLOAT_PRECISION)pars.imageSizeOutput[1]), + (PRISMATIC_FLOAT_PRECISION)pars.imageSizeOutput[1]); + }); + Array1D y = pars.yVec + round(y0); + transform(y.begin(), y.end(), y.begin(), [&pars](PRISMATIC_FLOAT_PRECISION &a) { + return fmod((PRISMATIC_FLOAT_PRECISION)pars.imageSizeOutput[0] + + fmod(a, (PRISMATIC_FLOAT_PRECISION)pars.imageSizeOutput[0]), + (PRISMATIC_FLOAT_PRECISION)pars.imageSizeOutput[0]); + }); + Array2D intOutput = Prismatic::zeros_ND<2, PRISMATIC_FLOAT_PRECISION>( + {{pars.imageSizeReduce[0], pars.imageSizeReduce[1]}}); + + memset(&psi[0], 0, sizeof(std::complex) * psi.size()); + + for (auto a4 = 0; a4 < pars.beamsIndex.size(); ++a4) + { + PRISMATIC_FLOAT_PRECISION yB = pars.xyBeams.at(a4, 0); + PRISMATIC_FLOAT_PRECISION xB = pars.xyBeams.at(a4, 1); + + if (abs(pars.psiProbeInit.at(yB, xB)) > 0) + { + PRISMATIC_FLOAT_PRECISION q0_0 = pars.qxaReduce.at(yB, xB); + PRISMATIC_FLOAT_PRECISION q0_1 = pars.qyaReduce.at(yB, xB); + std::complex phaseShift = exp( + -2 * pi * i * (q0_0 * (pars.xp[ax] + pars.xTiltShift) + q0_1 * (pars.yp[ay] + pars.yTiltShift))); + + const std::complex tmp_const = pars.psiProbeInit.at(yB, xB) * phaseShift; + auto psi_ptr = psi.begin(); + for (auto j = 0; j < y.size(); ++j) + { + for (auto i = 0; i < x.size(); ++i) + { + *psi_ptr++ += (tmp_const * pars.Scompact.at(a4, y[j], x[i])); } } } + } - PRISMATIC_FFTW_EXECUTE(plan); - for (auto jj = 0; jj < intOutput.get_dimj(); ++jj) { - for (auto ii = 0; ii < intOutput.get_dimi(); ++ii) { - intOutput.at(jj, ii) += pow(abs(psi.at(jj, ii)), 2) * pars.scale; - } - } - - - if (pars.meta.saveDPC_CoM){ - //calculate center of mass; qxa, qya are the fourier coordinates, should have 0 components at boundaries - for (long y = 0; y < intOutput.get_dimj(); ++y){ - for (long x = 0; x < intOutput.get_dimi(); ++x){ - pars.DPC_CoM.at(0,ay,ax,0) += pars.qxaReduce.at(y,x) * intOutput.at(y,x); - pars.DPC_CoM.at(0,ay,ax,1) += pars.qyaReduce.at(y,x) * intOutput.at(y,x); - } - } - //divide by sum of intensity - PRISMATIC_FLOAT_PRECISION intensitySum = 0; - for (auto iter = intOutput.begin(); iter != intOutput.end(); ++iter){ - intensitySum += *iter; - } - pars.DPC_CoM.at(0,ay,ax,0) /= intensitySum; - pars.DPC_CoM.at(0,ay,ax,1) /= intensitySum; + PRISMATIC_FFTW_EXECUTE(plan); + for (auto jj = 0; jj < intOutput.get_dimj(); ++jj) + { + for (auto ii = 0; ii < intOutput.get_dimi(); ++ii) + { + intOutput.at(jj, ii) += pow(abs(psi.at(jj, ii)), 2) * pars.scale; } + } - // update output -- ax,ay are unique per thread so this write is thread-safe without a lock - auto idx = pars.alphaInd.begin(); - for (auto counts = intOutput.begin(); counts != intOutput.end(); ++counts) { - if (*idx <= pars.Ndet) { - pars.output.at(0, ay, ax, (*idx) - 1) += *counts; + if (pars.meta.saveDPC_CoM) + { + //calculate center of mass; qxa, qya are the fourier coordinates, should have 0 components at boundaries + for (long y = 0; y < intOutput.get_dimj(); ++y) + { + for (long x = 0; x < intOutput.get_dimi(); ++x) + { + pars.DPC_CoM.at(0, ay, ax, 0) += pars.qxaReduce.at(y, x) * intOutput.at(y, x); + pars.DPC_CoM.at(0, ay, ax, 1) += pars.qyaReduce.at(y, x) * intOutput.at(y, x); } - ++idx; - }; - - //save 4D output if applicable - if (pars.meta.save4DOutput) { - //std::string section4DFilename = generateFilename(pars, 0, ay, ax); - unique_lock HDF5_gatekeeper(HDF5_lock); - std::stringstream nameString; - nameString << "4DSTEM_simulation/data/datacubes/CBED_array_depth" << getDigitString(0); - - H5::Group dataGroup = pars.outputFile.openGroup(nameString.str()); - H5::DataSet CBED_data = dataGroup.openDataSet("datacube"); - - hsize_t offset[4] = {ax,ay,0,0}; //order by ax, ay so that aligns with py4DSTEM - hsize_t mdims[4] = {1,1,intOutput.get_dimi(),intOutput.get_dimj()}; - - intOutput = fftshift2(intOutput); - PRISMATIC_FLOAT_PRECISION numFP = pars.meta.numFP; - writeDatacube4D(CBED_data, &intOutput[0],mdims,offset,numFP); - - CBED_data.close(); - dataGroup.close(); - HDF5_gatekeeper.unlock(); - //intOutput.toMRC_f(section4DFilename.c_str()); } + //divide by sum of intensity + PRISMATIC_FLOAT_PRECISION intensitySum = 0; + for (auto iter = intOutput.begin(); iter != intOutput.end(); ++iter) + { + intensitySum += *iter; + } + pars.DPC_CoM.at(0, ay, ax, 0) /= intensitySum; + pars.DPC_CoM.at(0, ay, ax, 1) /= intensitySum; } - void transformIndices(Parameters &pars){ - // setup some relevant coordinates - - pars.dq = (pars.qxaReduce.at(0, 1) + pars.qyaReduce.at(1, 0)) / 2; - PRISMATIC_FLOAT_PRECISION scale = pow(pars.meta.interpolationFactorX, 2) * pow(pars.meta.interpolationFactorY, 2); - - pars.scale = scale; - -// The operators +, -, /, * return PRISM arrays by value, so to avoid unnecessary memory -// allocations/copies for chained operations I try to do things like create variables -// initially with at most one operation, and then perform in-place transforms if more is needed - Array2D qxaShift = pars.qxaReduce - (pars.meta.probeXtilt / pars.lambda); - Array2D qyaShift = pars.qyaReduce - (pars.meta.probeYtilt / pars.lambda); - transform(qxaShift.begin(), qxaShift.end(), - qyaShift.begin(), pars.q2.begin(), - [](const PRISMATIC_FLOAT_PRECISION &a, const PRISMATIC_FLOAT_PRECISION &b) { return a * a + b * b; }); - transform(pars.q2.begin(), pars.q2.end(), - pars.q1.begin(), - [](const PRISMATIC_FLOAT_PRECISION &a) { return sqrt(a); }); -// Array2D alphaInd(pars.q1); // copy constructor more efficient than assignment - pars.alphaInd = pars.q1; - transform(pars.alphaInd.begin(), pars.alphaInd.end(), - pars.alphaInd.begin(), - [&pars](const PRISMATIC_FLOAT_PRECISION &a) { - return 1 + round((a * pars.lambda - pars.detectorAngles[0]) / pars.meta.detectorAngleStep); - }); - transform(pars.alphaInd.begin(), pars.alphaInd.end(), - pars.alphaInd.begin(), - [](const PRISMATIC_FLOAT_PRECISION &a) { return a < 1 ? 1 : a; }); - Prismatic::ArrayND<2, std::vector > alphaMask( - std::vector(pars.alphaInd.size(), 0), - {{pars.alphaInd.get_dimj(), pars.alphaInd.get_dimi()}}); - transform(pars.alphaInd.begin(), pars.alphaInd.end(), - alphaMask.begin(), - [&pars](const PRISMATIC_FLOAT_PRECISION &a) { return (a < pars.Ndet) ? 1 : 0; }); + // update output -- ax,ay are unique per thread so this write is thread-safe without a lock + auto idx = pars.alphaInd.begin(); + for (auto counts = intOutput.begin(); counts != intOutput.end(); ++counts) + { + if (*idx <= pars.Ndet) + { + pars.output.at(0, ay, ax, (*idx) - 1) += *counts; + } + ++idx; + }; + + //save 4D output if applicable + if (pars.meta.save4DOutput) + { + //std::string section4DFilename = generateFilename(pars, 0, ay, ax); + unique_lock HDF5_gatekeeper(HDF5_lock); + std::stringstream nameString; + nameString << "4DSTEM_simulation/data/datacubes/CBED_array_depth" << getDigitString(0); + + H5::Group dataGroup = pars.outputFile.openGroup(nameString.str()); + H5::DataSet CBED_data = dataGroup.openDataSet("datacube"); + + hsize_t offset[4] = {ax, ay, 0, 0}; //order by ax, ay so that aligns with py4DSTEM + hsize_t mdims[4] = {1, 1, intOutput.get_dimi(), intOutput.get_dimj()}; + + intOutput = fftshift2(intOutput); + PRISMATIC_FLOAT_PRECISION numFP = pars.meta.numFP; + writeDatacube4D(CBED_data, &intOutput[0], mdims, offset, numFP); + + CBED_data.close(); + dataGroup.close(); + HDF5_gatekeeper.unlock(); + //intOutput.toMRC_f(section4DFilename.c_str()); } +} - void initializeProbes(Parameters &pars){ - // initialize the probe - - pars.psiProbeInit = zeros_ND<2, complex >( - {{pars.imageSizeReduce[0], pars.imageSizeReduce[1]}}); - PRISMATIC_FLOAT_PRECISION qProbeMax = pars.meta.probeSemiangle / pars.lambda; - transform(pars.psiProbeInit.begin(), pars.psiProbeInit.end(), - pars.q1.begin(), pars.psiProbeInit.begin(), - [&pars, &qProbeMax](std::complex &a, PRISMATIC_FLOAT_PRECISION &q1_t) { - a.real(erf((qProbeMax - q1_t) / (0.5 * pars.dq)) * 0.5 + 0.5); - a.imag(0); - return a; - }); - - - transform(pars.psiProbeInit.begin(), pars.psiProbeInit.end(), - pars.q2.begin(), pars.psiProbeInit.begin(), - [&pars](std::complex &a, PRISMATIC_FLOAT_PRECISION &q2_t) { - std::complex chi{ - (PRISMATIC_FLOAT_PRECISION) (pi * pars.lambda * pars.meta.probeDefocus * q2_t + - pi/2 * pow(pars.lambda,3) * pars.meta.C3 * pow(q2_t,2)+ - pi/3 * pow(pars.lambda,5) * pars.meta.C5 * pow(q2_t,3)), (PRISMATIC_FLOAT_PRECISION)0.0}; - a = a * exp(-i * chi); - return a; - }); - - PRISMATIC_FLOAT_PRECISION norm_constant = sqrt(accumulate(pars.psiProbeInit.begin(), pars.psiProbeInit.end(), - (PRISMATIC_FLOAT_PRECISION) 0.0, - [](PRISMATIC_FLOAT_PRECISION accum, - std::complex &a) { - return accum + abs(a) * abs(a); - })); // make sure to initialize with 0.0 and NOT 0 or it won't be a float and answer will be wrong - PRISMATIC_FLOAT_PRECISION a = 0; - for (auto &i : pars.psiProbeInit) { a += i.real(); }; - transform(pars.psiProbeInit.begin(), pars.psiProbeInit.end(), - pars.psiProbeInit.begin(), [&norm_constant](std::complex &a) { - return a / norm_constant; - }); +void transformIndices(Parameters &pars) +{ + // setup some relevant coordinates + + pars.dq = (pars.qxaReduce.at(0, 1) + pars.qyaReduce.at(1, 0)) / 2; + PRISMATIC_FLOAT_PRECISION scale = pow(pars.meta.interpolationFactorX, 2) * pow(pars.meta.interpolationFactorY, 2); + + pars.scale = scale; + + // The operators +, -, /, * return PRISM arrays by value, so to avoid unnecessary memory + // allocations/copies for chained operations I try to do things like create variables + // initially with at most one operation, and then perform in-place transforms if more is needed + Array2D qxaShift = pars.qxaReduce - (pars.meta.probeXtilt / pars.lambda); + Array2D qyaShift = pars.qyaReduce - (pars.meta.probeYtilt / pars.lambda); + transform(qxaShift.begin(), qxaShift.end(), + qyaShift.begin(), pars.q2.begin(), + [](const PRISMATIC_FLOAT_PRECISION &a, const PRISMATIC_FLOAT_PRECISION &b) { return a * a + b * b; }); + transform(pars.q2.begin(), pars.q2.end(), + pars.q1.begin(), + [](const PRISMATIC_FLOAT_PRECISION &a) { return sqrt(a); }); + // Array2D alphaInd(pars.q1); // copy constructor more efficient than assignment + pars.alphaInd = pars.q1; + transform(pars.alphaInd.begin(), pars.alphaInd.end(), + pars.alphaInd.begin(), + [&pars](const PRISMATIC_FLOAT_PRECISION &a) { + return 1 + round((a * pars.lambda - pars.detectorAngles[0]) / pars.meta.detectorAngleStep); + }); + transform(pars.alphaInd.begin(), pars.alphaInd.end(), + pars.alphaInd.begin(), + [](const PRISMATIC_FLOAT_PRECISION &a) { return a < 1 ? 1 : a; }); + Prismatic::ArrayND<2, std::vector> alphaMask( + std::vector(pars.alphaInd.size(), 0), + {{pars.alphaInd.get_dimj(), pars.alphaInd.get_dimi()}}); + transform(pars.alphaInd.begin(), pars.alphaInd.end(), + alphaMask.begin(), + [&pars](const PRISMATIC_FLOAT_PRECISION &a) { return (a < pars.Ndet) ? 1 : 0; }); +} - } +void initializeProbes(Parameters &pars) +{ + // initialize the probe + + pars.psiProbeInit = zeros_ND<2, complex>( + {{pars.imageSizeReduce[0], pars.imageSizeReduce[1]}}); + PRISMATIC_FLOAT_PRECISION qProbeMax = pars.meta.probeSemiangle / pars.lambda; + transform(pars.psiProbeInit.begin(), pars.psiProbeInit.end(), + pars.q1.begin(), pars.psiProbeInit.begin(), + [&pars, &qProbeMax](std::complex &a, PRISMATIC_FLOAT_PRECISION &q1_t) { + a.real(erf((qProbeMax - q1_t) / (0.5 * pars.dq)) * 0.5 + 0.5); + a.imag(0); + return a; + }); + + transform(pars.psiProbeInit.begin(), pars.psiProbeInit.end(), + pars.q2.begin(), pars.psiProbeInit.begin(), + [&pars](std::complex &a, PRISMATIC_FLOAT_PRECISION &q2_t) { + std::complex chi{ + (PRISMATIC_FLOAT_PRECISION)(pi * pars.lambda * pars.meta.probeDefocus * q2_t + + pi / 2 * pow(pars.lambda, 3) * pars.meta.C3 * pow(q2_t, 2) + + pi / 3 * pow(pars.lambda, 5) * pars.meta.C5 * pow(q2_t, 3)), + (PRISMATIC_FLOAT_PRECISION)0.0}; + a = a * exp(-i * chi); + return a; + }); + + PRISMATIC_FLOAT_PRECISION norm_constant = sqrt(accumulate(pars.psiProbeInit.begin(), pars.psiProbeInit.end(), + (PRISMATIC_FLOAT_PRECISION)0.0, + [](PRISMATIC_FLOAT_PRECISION accum, + std::complex &a) { + return accum + abs(a) * abs(a); + })); // make sure to initialize with 0.0 and NOT 0 or it won't be a float and answer will be wrong + PRISMATIC_FLOAT_PRECISION a = 0; + for (auto &i : pars.psiProbeInit) + { + a += i.real(); + }; + transform(pars.psiProbeInit.begin(), pars.psiProbeInit.end(), + pars.psiProbeInit.begin(), [&norm_constant](std::complex &a) { + return a / norm_constant; + }); +} - void PRISM03_calcOutput(Parameters &pars) { - // compute final image +void PRISM03_calcOutput(Parameters &pars) +{ + // compute final image - cout << "Entering PRISM03_calcOutput" << endl; + cout << "Entering PRISM03_calcOutput" << endl; - // setup necessary coordinates - setupCoordinates_2(pars); + // setup necessary coordinates + setupCoordinates_2(pars); - // setup angles of detector and image sizes - setupDetector(pars); + // setup angles of detector and image sizes + setupDetector(pars); - // setup coordinates and indices for the beams - setupBeams_2(pars); + // setup coordinates and indices for the beams + setupBeams_2(pars); - // setup Fourier coordinates for the S-matrix - setupFourierCoordinates(pars); + // setup Fourier coordinates for the S-matrix + setupFourierCoordinates(pars); - // initialize the output to the correct size for the output mode - createStack_integrate(pars); + // initialize the output to the correct size for the output mode + createStack_integrate(pars); - // perform some necessary setup transformations of the data - transformIndices(pars); + // perform some necessary setup transformations of the data + transformIndices(pars); - // initialize/compute the probes - initializeProbes(pars); + // initialize/compute the probes + initializeProbes(pars); #ifdef PRISMATIC_BUILDING_GUI - pars.progressbar->signalDescriptionMessage("Computing final output (PRISM)"); - pars.progressbar->signalOutputUpdate(0, pars.xp.size() * pars.yp.size()); + pars.progressbar->signalDescriptionMessage("Computing final output (PRISM)"); + pars.progressbar->signalOutputUpdate(0, pars.xp.size() * pars.yp.size()); #endif - // compute the final PRISM output - buildPRISMOutput(pars); - } + // compute the final PRISM output + buildPRISMOutput(pars); } +} // namespace Prismatic diff --git a/src/PRISM_entry.cpp b/src/PRISM_entry.cpp index f34356bd8..d1dd05d40 100644 --- a/src/PRISM_entry.cpp +++ b/src/PRISM_entry.cpp @@ -25,189 +25,216 @@ #include "H5Cpp.h" #include "utility.h" - -namespace Prismatic{ - using namespace std; - Parameters PRISM_entry(Metadata& meta){ - Parameters prismatic_pars; - try { // read atomic coordinates - prismatic_pars = Parameters(meta); - } catch(...){ - std::cout << "Terminating" << std::endl; - exit(1); +namespace Prismatic +{ +using namespace std; +Parameters PRISM_entry(Metadata &meta) +{ + Parameters prismatic_pars; + try + { // read atomic coordinates + prismatic_pars = Parameters(meta); + } + catch (...) + { + std::cout << "Terminating" << std::endl; + exit(1); + } + prismatic_pars.meta.toString(); + + // to_xyz(prismatic_pars.atoms, "/Users/ajpryor/Documents/MATLAB/multislice/PRISM/build/test.XYZ", "comment", 5.43,5.43,5.43); + + prismatic_pars.outputFile = H5::H5File(prismatic_pars.meta.filenameOutput.c_str(), H5F_ACC_TRUNC); + setupOutputFile(prismatic_pars); + // compute projected potentials + prismatic_pars.fpFlag = 0; + PRISM01_calcPotential(prismatic_pars); + + // prismatic_pars.pot.toMRC_f("debug_potential.mrc"); + + // compute compact S-matrix + PRISM02_calcSMatrix(prismatic_pars); + + // Array3D tmp = zeros_ND<3, PRISMATIC_FLOAT_PRECISION>({{prismatic_pars.Scompact.get_dimk(),prismatic_pars.Scompact.get_dimj(),prismatic_pars.Scompact.get_dimi()}}); + // Array3D tmp_r = zeros_ND<3, PRISMATIC_FLOAT_PRECISION>({{prismatic_pars.Scompact.get_dimk(),prismatic_pars.Scompact.get_dimj(),prismatic_pars.Scompact.get_dimi()}}); + // Array3D tmp_i = zeros_ND<3, PRISMATIC_FLOAT_PRECISION>({{prismatic_pars.Scompact.get_dimk(),prismatic_pars.Scompact.get_dimj(),prismatic_pars.Scompact.get_dimi()}}); + // auto tmp_ptr = tmp.begin(); + // auto tmp_r_ptr = tmp_r.begin(); + // auto tmp_i_ptr = tmp_i.begin(); + // for (auto&i : prismatic_pars.Scompact)*tmp_ptr++ = abs(i); + // for (auto&i : prismatic_pars.Scompact)*tmp_r_ptr++ = i.real(); + // for (auto&i : prismatic_pars.Scompact)*tmp_i_ptr++ = i.imag(); + // std::complex ssum{0,0}; + // for (auto& i:prismatic_pars.Scompact)ssum+=i; + // cout <<"S compact sum = " << ssum << endl; + // tmp.toMRC_f("debug_scompact.mrc"); + // tmp_r.toMRC_f("debug_scompact_r.mrc"); + // tmp_i.toMRC_f("debug_scompact_i.mrc"); + + // compute final output + PRISM03_calcOutput(prismatic_pars); + prismatic_pars.outputFile.close(); + + // calculate remaining frozen phonon configurations + if (prismatic_pars.meta.numFP > 1) + { + // run the rest of the frozen phonons + Array4D net_output(prismatic_pars.output); + Array4D DPC_CoM_output; + if (prismatic_pars.meta.saveDPC_CoM) + DPC_CoM_output = prismatic_pars.DPC_CoM; + for (auto fp_num = 1; fp_num < prismatic_pars.meta.numFP; ++fp_num) + { + meta.randomSeed = rand() % 100000; + ++meta.fpNum; + Parameters prismatic_pars(meta); + cout << "Frozen Phonon #" << fp_num << endl; + prismatic_pars.meta.toString(); + + prismatic_pars.outputFile = H5::H5File(prismatic_pars.meta.filenameOutput.c_str(), H5F_ACC_RDWR); + prismatic_pars.fpFlag = fp_num; + + PRISM01_calcPotential(prismatic_pars); + PRISM02_calcSMatrix(prismatic_pars); + PRISM03_calcOutput(prismatic_pars); + net_output += prismatic_pars.output; + if (meta.saveDPC_CoM) + DPC_CoM_output += prismatic_pars.DPC_CoM; + prismatic_pars.outputFile.close(); } - prismatic_pars.meta.toString(); - -// to_xyz(prismatic_pars.atoms, "/Users/ajpryor/Documents/MATLAB/multislice/PRISM/build/test.XYZ", "comment", 5.43,5.43,5.43); - - prismatic_pars.outputFile = H5::H5File(prismatic_pars.meta.filenameOutput.c_str(),H5F_ACC_TRUNC); - setupOutputFile(prismatic_pars); - // compute projected potentials - prismatic_pars.fpFlag = 0; - PRISM01_calcPotential(prismatic_pars); - -// prismatic_pars.pot.toMRC_f("debug_potential.mrc"); - - // compute compact S-matrix - PRISM02_calcSMatrix(prismatic_pars); - -// Array3D tmp = zeros_ND<3, PRISMATIC_FLOAT_PRECISION>({{prismatic_pars.Scompact.get_dimk(),prismatic_pars.Scompact.get_dimj(),prismatic_pars.Scompact.get_dimi()}}); -// Array3D tmp_r = zeros_ND<3, PRISMATIC_FLOAT_PRECISION>({{prismatic_pars.Scompact.get_dimk(),prismatic_pars.Scompact.get_dimj(),prismatic_pars.Scompact.get_dimi()}}); -// Array3D tmp_i = zeros_ND<3, PRISMATIC_FLOAT_PRECISION>({{prismatic_pars.Scompact.get_dimk(),prismatic_pars.Scompact.get_dimj(),prismatic_pars.Scompact.get_dimi()}}); -// auto tmp_ptr = tmp.begin(); -// auto tmp_r_ptr = tmp_r.begin(); -// auto tmp_i_ptr = tmp_i.begin(); -// for (auto&i : prismatic_pars.Scompact)*tmp_ptr++ = abs(i); -// for (auto&i : prismatic_pars.Scompact)*tmp_r_ptr++ = i.real(); -// for (auto&i : prismatic_pars.Scompact)*tmp_i_ptr++ = i.imag(); -// std::complex ssum{0,0}; -// for (auto& i:prismatic_pars.Scompact)ssum+=i; -// cout <<"S compact sum = " << ssum << endl; -// tmp.toMRC_f("debug_scompact.mrc"); -// tmp_r.toMRC_f("debug_scompact_r.mrc"); -// tmp_i.toMRC_f("debug_scompact_i.mrc"); - - // compute final output - PRISM03_calcOutput(prismatic_pars); - prismatic_pars.outputFile.close(); - - // calculate remaining frozen phonon configurations - if (prismatic_pars.meta.numFP > 1) { - // run the rest of the frozen phonons - Array4D net_output(prismatic_pars.output); - Array4D DPC_CoM_output; - if(prismatic_pars.meta.saveDPC_CoM) DPC_CoM_output = prismatic_pars.DPC_CoM; - for (auto fp_num = 1; fp_num < prismatic_pars.meta.numFP; ++fp_num){ - meta.randomSeed = rand() % 100000; - ++meta.fpNum; - Parameters prismatic_pars(meta); - cout << "Frozen Phonon #" << fp_num << endl; - prismatic_pars.meta.toString(); - - prismatic_pars.outputFile = H5::H5File(prismatic_pars.meta.filenameOutput.c_str(),H5F_ACC_RDWR); - prismatic_pars.fpFlag = fp_num; - - PRISM01_calcPotential(prismatic_pars); - PRISM02_calcSMatrix(prismatic_pars); - PRISM03_calcOutput(prismatic_pars); - net_output += prismatic_pars.output; - if(meta.saveDPC_CoM) DPC_CoM_output += prismatic_pars.DPC_CoM; - prismatic_pars.outputFile.close(); - } - // divide to take average - for (auto&i:net_output) i/=prismatic_pars.meta.numFP; - prismatic_pars.output = net_output; - - if(prismatic_pars.meta.saveDPC_CoM){ - for (auto&j:DPC_CoM_output) j/=prismatic_pars.meta.numFP; //since squared intensities are used to calculate DPC_CoM, this is incoherent averaging - prismatic_pars.DPC_CoM = DPC_CoM_output; - } - } - - prismatic_pars.outputFile = H5::H5File(prismatic_pars.meta.filenameOutput.c_str(),H5F_ACC_RDWR); - - if (prismatic_pars.meta.save3DOutput){ - PRISMATIC_FLOAT_PRECISION dummy = 1.0; - setupVDOutput(prismatic_pars, prismatic_pars.output.get_diml(),dummy); - Array3D output_image = zeros_ND<3, PRISMATIC_FLOAT_PRECISION>({{prismatic_pars.output.get_dimj(),prismatic_pars.output.get_dimk(),prismatic_pars.output.get_dimi()}}); - - std::stringstream nameString; - nameString << "4DSTEM_simulation/data/realslices/virtual_detector_depth" << getDigitString(0); - H5::Group dataGroup = prismatic_pars.outputFile.openGroup(nameString.str()); - - std::string dataSetName = "realslice"; - H5::DataSet VD_data = dataGroup.openDataSet(dataSetName); - hsize_t mdims[3] = {prismatic_pars.xp.size(),prismatic_pars.yp.size(),prismatic_pars.Ndet}; - - for(auto b = 0; b < prismatic_pars.Ndet; b++){ - for (auto y = 0; y < prismatic_pars.output.get_dimk(); ++y){ - for (auto x = 0; x < prismatic_pars.output.get_dimj();++x){ - output_image.at(x,y,b) = prismatic_pars.output.at(0,y,x,b); - } + // divide to take average + for (auto &i : net_output) + i /= prismatic_pars.meta.numFP; + prismatic_pars.output = net_output; + + if (prismatic_pars.meta.saveDPC_CoM) + { + for (auto &j : DPC_CoM_output) + j /= prismatic_pars.meta.numFP; //since squared intensities are used to calculate DPC_CoM, this is incoherent averaging + prismatic_pars.DPC_CoM = DPC_CoM_output; + } + } + + prismatic_pars.outputFile = H5::H5File(prismatic_pars.meta.filenameOutput.c_str(), H5F_ACC_RDWR); + + if (prismatic_pars.meta.save3DOutput) + { + PRISMATIC_FLOAT_PRECISION dummy = 1.0; + setupVDOutput(prismatic_pars, prismatic_pars.output.get_diml(), dummy); + Array3D output_image = zeros_ND<3, PRISMATIC_FLOAT_PRECISION>({{prismatic_pars.output.get_dimj(), prismatic_pars.output.get_dimk(), prismatic_pars.output.get_dimi()}}); + + std::stringstream nameString; + nameString << "4DSTEM_simulation/data/realslices/virtual_detector_depth" << getDigitString(0); + H5::Group dataGroup = prismatic_pars.outputFile.openGroup(nameString.str()); + + std::string dataSetName = "realslice"; + H5::DataSet VD_data = dataGroup.openDataSet(dataSetName); + hsize_t mdims[3] = {prismatic_pars.xp.size(), prismatic_pars.yp.size(), prismatic_pars.Ndet}; + + for (auto b = 0; b < prismatic_pars.Ndet; b++) + { + for (auto y = 0; y < prismatic_pars.output.get_dimk(); ++y) + { + for (auto x = 0; x < prismatic_pars.output.get_dimj(); ++x) + { + output_image.at(x, y, b) = prismatic_pars.output.at(0, y, x, b); } } - - writeDatacube3D(VD_data,&output_image[0],mdims); - VD_data.close(); - dataGroup.close(); } - if (prismatic_pars.meta.save2DOutput) { - size_t lower = std::max((size_t)0, (size_t)(prismatic_pars.meta.integrationAngleMin / prismatic_pars.meta.detectorAngleStep)); - size_t upper = std::min((size_t)prismatic_pars.detectorAngles.size(), (size_t)(prismatic_pars.meta.integrationAngleMax / prismatic_pars.meta.detectorAngleStep)); - Array2D prism_image; - prism_image = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>( - {{prismatic_pars.output.get_dimj(), prismatic_pars.output.get_dimk()}}); - PRISMATIC_FLOAT_PRECISION dummy = 1.0; - setup2DOutput(prismatic_pars, prismatic_pars.output.get_diml(),dummy); - - for (auto y = 0; y < prismatic_pars.output.get_dimk(); ++y) { - for (auto x = 0; x < prismatic_pars.output.get_dimj(); ++x) { - for (auto b = lower; b < upper; ++b) { - prism_image.at(x, y) += prismatic_pars.output.at(0, y, x, b); - } + writeDatacube3D(VD_data, &output_image[0], mdims); + VD_data.close(); + dataGroup.close(); + } + + if (prismatic_pars.meta.save2DOutput) + { + size_t lower = std::max((size_t)0, (size_t)(prismatic_pars.meta.integrationAngleMin / prismatic_pars.meta.detectorAngleStep)); + size_t upper = std::min((size_t)prismatic_pars.detectorAngles.size(), (size_t)(prismatic_pars.meta.integrationAngleMax / prismatic_pars.meta.detectorAngleStep)); + Array2D prism_image; + prism_image = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>( + {{prismatic_pars.output.get_dimj(), prismatic_pars.output.get_dimk()}}); + PRISMATIC_FLOAT_PRECISION dummy = 1.0; + setup2DOutput(prismatic_pars, prismatic_pars.output.get_diml(), dummy); + + for (auto y = 0; y < prismatic_pars.output.get_dimk(); ++y) + { + for (auto x = 0; x < prismatic_pars.output.get_dimj(); ++x) + { + for (auto b = lower; b < upper; ++b) + { + prism_image.at(x, y) += prismatic_pars.output.at(0, y, x, b); } } - std::stringstream nameString; - nameString << "4DSTEM_simulation/data/realslices/annular_detector_depth" << getDigitString(0); - H5::Group dataGroup = prismatic_pars.outputFile.openGroup(nameString.str()); - H5::DataSet AD_data = dataGroup.openDataSet("realslice"); - hsize_t mdims[2] = {prismatic_pars.xp.size(),prismatic_pars.yp.size()}; - - writeRealSlice(AD_data,&prism_image[0],mdims); - AD_data.close(); - dataGroup.close(); - - //std::string image_filename = prismatic_pars.meta.outputFolder + std::string("prism_2Doutput_") + prismatic_pars.meta.filenameOutput; - //prism_image.toMRC_f(image_filename.c_str()); } + std::stringstream nameString; + nameString << "4DSTEM_simulation/data/realslices/annular_detector_depth" << getDigitString(0); + H5::Group dataGroup = prismatic_pars.outputFile.openGroup(nameString.str()); + H5::DataSet AD_data = dataGroup.openDataSet("realslice"); + hsize_t mdims[2] = {prismatic_pars.xp.size(), prismatic_pars.yp.size()}; + + writeRealSlice(AD_data, &prism_image[0], mdims); + AD_data.close(); + dataGroup.close(); + + //std::string image_filename = prismatic_pars.meta.outputFolder + std::string("prism_2Doutput_") + prismatic_pars.meta.filenameOutput; + //prism_image.toMRC_f(image_filename.c_str()); + } - if (prismatic_pars.meta.saveDPC_CoM){ - PRISMATIC_FLOAT_PRECISION dummy = 1.0; - setupDPCOutput(prismatic_pars,prismatic_pars.output.get_diml(), dummy); - - //create dummy array to pass to - Array2D DPC_slice; - - std::stringstream nameString; - nameString << "4DSTEM_simulation/data/realslices/DPC_CoM_depth" << getDigitString(0); - H5::Group dataGroup = prismatic_pars.outputFile.openGroup(nameString.str()); - hsize_t mdims[2] = {prismatic_pars.xp.size(),prismatic_pars.yp.size()}; - - for (auto b = 0; b < prismatic_pars.DPC_CoM.get_dimi(); ++b){ - DPC_slice = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>({{prismatic_pars.DPC_CoM.get_dimj(),prismatic_pars.DPC_CoM.get_dimk()}}); - std::string endName; - if(b == 0){ - endName = "x"; - }else{ - endName = "y"; - } - std::string dataSetName = "DPC_CoM_" + endName; + if (prismatic_pars.meta.saveDPC_CoM) + { + PRISMATIC_FLOAT_PRECISION dummy = 1.0; + setupDPCOutput(prismatic_pars, prismatic_pars.output.get_diml(), dummy); + + //create dummy array to pass to + Array2D DPC_slice; + + std::stringstream nameString; + nameString << "4DSTEM_simulation/data/realslices/DPC_CoM_depth" << getDigitString(0); + H5::Group dataGroup = prismatic_pars.outputFile.openGroup(nameString.str()); + hsize_t mdims[2] = {prismatic_pars.xp.size(), prismatic_pars.yp.size()}; + + for (auto b = 0; b < prismatic_pars.DPC_CoM.get_dimi(); ++b) + { + DPC_slice = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>({{prismatic_pars.DPC_CoM.get_dimj(), prismatic_pars.DPC_CoM.get_dimk()}}); + std::string endName; + if (b == 0) + { + endName = "x"; + } + else + { + endName = "y"; + } + std::string dataSetName = "DPC_CoM_" + endName; - H5::DataSet DPC_data = dataGroup.openDataSet(dataSetName); + H5::DataSet DPC_data = dataGroup.openDataSet(dataSetName); - for (auto y = 0; y < prismatic_pars.DPC_CoM.get_dimk(); ++y){ - for (auto x = 0; x < prismatic_pars.DPC_CoM.get_dimj();++x){ - DPC_slice.at(x,y) = prismatic_pars.DPC_CoM.at(0,y,x,b); - } + for (auto y = 0; y < prismatic_pars.DPC_CoM.get_dimk(); ++y) + { + for (auto x = 0; x < prismatic_pars.DPC_CoM.get_dimj(); ++x) + { + DPC_slice.at(x, y) = prismatic_pars.DPC_CoM.at(0, y, x, b); } - //if ( prismatic_pars.meta.numSlices != 0) slice_filename = prismatic_pars.meta.outputFolder + std::string("slice")+std::to_string(j)+std::string("_") + prismatic_pars.meta.filenameOutput; - //slice_image.toMRC_f(slice_filename.c_str()); - writeRealSlice(DPC_data,&DPC_slice[0],mdims); - DPC_data.close(); } - - dataGroup.close(); + //if ( prismatic_pars.meta.numSlices != 0) slice_filename = prismatic_pars.meta.outputFolder + std::string("slice")+std::to_string(j)+std::string("_") + prismatic_pars.meta.filenameOutput; + //slice_image.toMRC_f(slice_filename.c_str()); + writeRealSlice(DPC_data, &DPC_slice[0], mdims); + DPC_data.close(); } - PRISMATIC_FLOAT_PRECISION dummy = 1.0; - writeMetadata(prismatic_pars,dummy); - prismatic_pars.outputFile.close(); + dataGroup.close(); + } + + PRISMATIC_FLOAT_PRECISION dummy = 1.0; + writeMetadata(prismatic_pars, dummy); + prismatic_pars.outputFile.close(); #ifdef PRISMATIC_ENABLE_GPU - cout << "peak GPU memory usage = " << prismatic_pars.maxGPUMem << '\n'; + cout << "peak GPU memory usage = " << prismatic_pars.maxGPUMem << '\n'; #endif //PRISMATIC_ENABLE_GPU - std::cout << "PRISM Calculation complete.\n" << std::endl; - return prismatic_pars; - } + std::cout << "PRISM Calculation complete.\n" + << std::endl; + return prismatic_pars; } +} // namespace Prismatic diff --git a/src/WorkDispatcher.cpp b/src/WorkDispatcher.cpp index 9ea3e7b50..d530b33f7 100644 --- a/src/WorkDispatcher.cpp +++ b/src/WorkDispatcher.cpp @@ -15,18 +15,20 @@ #include // helper function for dispatching work -namespace Prismatic { - WorkDispatcher::WorkDispatcher(size_t _current, - size_t _stop) : - current(_current), - stop(_stop){}; +namespace Prismatic +{ +WorkDispatcher::WorkDispatcher(size_t _current, + size_t _stop) : current(_current), + stop(_stop){}; - bool WorkDispatcher::getWork(size_t& job_start, size_t& job_stop, size_t num_requested, size_t early_cpu_stop){ - std::lock_guard gatekeeper(lock); - if (job_start >= stop | current>=early_cpu_stop) return false; // all jobs done, terminate - job_start = current; - job_stop = std::min(stop, current + num_requested); - current = job_stop; - return true; - } +bool WorkDispatcher::getWork(size_t &job_start, size_t &job_stop, size_t num_requested, size_t early_cpu_stop) +{ + std::lock_guard gatekeeper(lock); + if (job_start >= stop | current >= early_cpu_stop) + return false; // all jobs done, terminate + job_start = current; + job_stop = std::min(stop, current + num_requested); + current = job_stop; + return true; } +} // namespace Prismatic diff --git a/src/atom.cpp b/src/atom.cpp index bb651c085..4b660dfd8 100644 --- a/src/atom.cpp +++ b/src/atom.cpp @@ -22,200 +22,234 @@ #include #include "kirkland_params.h" -namespace Prismatic { - std::string atomReadError(size_t line_num, const std::string str){ - std::string msg(" \n\nPrismatic: Error getting atomic species from"); - std::stringstream ssError; - msg += " line "; - ssError << line_num; - msg += ssError.str(); - msg += ":\n "; - msg += str; - msg += " \n"; - return msg; - } - std::vector tileAtoms(const size_t tileX, const size_t tileY, const size_t tileZ, std::vector atoms) { - if (tileX == 1 & tileY == 1 & tileZ == 1)return atoms; // case where no tiling is necessary - std::vector tiled_atoms; - tiled_atoms.reserve(atoms.size() * tileX * tileY * tileZ); - for (auto tz = 0; tz < tileZ; ++tz) { - for (auto ty = 0; ty < tileY; ++ty) { - for (auto tx = 0; tx < tileX; ++tx) { - for (auto i = 0; i < atoms.size(); ++i){ - tiled_atoms.emplace_back(atom{(atoms[i].x + tx) / tileX, (atoms[i].y + ty) / tileY, (atoms[i].z + tz) / tileZ, atoms[i].species, atoms[i].sigma, atoms[i].occ}); - } +namespace Prismatic +{ +std::string atomReadError(size_t line_num, const std::string str) +{ + std::string msg(" \n\nPrismatic: Error getting atomic species from"); + std::stringstream ssError; + msg += " line "; + ssError << line_num; + msg += ssError.str(); + msg += ":\n "; + msg += str; + msg += " \n"; + return msg; +} +std::vector tileAtoms(const size_t tileX, const size_t tileY, const size_t tileZ, std::vector atoms) +{ + if (tileX == 1 & tileY == 1 & tileZ == 1) + return atoms; // case where no tiling is necessary + std::vector tiled_atoms; + tiled_atoms.reserve(atoms.size() * tileX * tileY * tileZ); + for (auto tz = 0; tz < tileZ; ++tz) + { + for (auto ty = 0; ty < tileY; ++ty) + { + for (auto tx = 0; tx < tileX; ++tx) + { + for (auto i = 0; i < atoms.size(); ++i) + { + tiled_atoms.emplace_back(atom{(atoms[i].x + tx) / tileX, (atoms[i].y + ty) / tileY, (atoms[i].z + tz) / tileZ, atoms[i].species, atoms[i].sigma, atoms[i].occ}); } } } - return tiled_atoms; } + return tiled_atoms; +} - void to_xyz(const std::vector atoms, const std::string filename, const std::string comment, double a, double b, double c){ - std::ofstream f(filename, std::ios::out); - if (f){ -// std::stringstream ss; - std::stringstream ss; - ss.precision(8); - f << comment << '\n'; - ss << '\t'; - ss << a << '\t'; - ss << b << '\t'; - ss << c << '\n'; - f << ss.str(); -// atoms[0].to_string(); - for (auto& atom:atoms){ - ss.str("\t"); - ss << atom.species << '\t'; - ss << atom.x * a << '\t'; - ss << atom.y * b << '\t'; - ss << atom.z * c << '\t'; - ss << atom.occ << '\t'; - ss << atom.sigma << '\n'; -// std::cout << "ss.str() = " << ss.str() << std::endl; - f << ss.str(); - } - f << "-1\n"; - } - } - - std::vector readAtoms_xyz(const std::string& filename){ - std::vector atoms; - std::ifstream f(filename); - if (!f)throw std::runtime_error("Unable to open file.\n"); - std::string line; - std::string token; - size_t line_num = 2; - size_t atom_count = 0; - if (!std::getline(f,line)) throw std::runtime_error("Error reading comment line.\n"); - if (!std::getline(f,line)) throw std::runtime_error("Error reading unit cell params.\n"); - double a,b,c; // unit cell params +void to_xyz(const std::vector atoms, const std::string filename, const std::string comment, double a, double b, double c) +{ + std::ofstream f(filename, std::ios::out); + if (f) + { + std::stringstream ss; + ss.precision(8); + f << comment << '\n'; + ss << '\t'; + ss << a << '\t'; + ss << b << '\t'; + ss << c << '\n'; + f << ss.str(); + for (auto &atom : atoms) { -// std::stringstream ss(line); - std::stringstream ss; - ss.precision(8); - ss << line; - if (!(ss >> a) || (a <= 0)) - throw std::domain_error( - "Bad input data for unit cell dimension a.\n"); - if (!(ss >> b) || (b <= 0)) - throw std::domain_error( - "Bad input data for unit cell dimension b.\n"); - if (!(ss >> c) || (c <= 0)) - throw std::domain_error( - "Bad input data for unit cell dimension c.\n"); + ss.str("\t"); + ss << atom.species << '\t'; + ss << atom.x * a << '\t'; + ss << atom.y * b << '\t'; + ss << atom.z * c << '\t'; + ss << atom.occ << '\t'; + ss << atom.sigma << '\n'; + f << ss.str(); } - while (std::getline(f, line)) { - line = line.substr(line.find_first_not_of(" \n\t"), line.find_last_not_of(" \n\t")); - if (line.size() <=3){ - break; - } - ++atom_count; - ++line_num; - double tx, ty, tz, occ, sigma; - size_t tspecies; -// std::stringstream ss(line); - std::stringstream ss; - ss.precision(8); - ss << line; - if (!(ss >> tspecies) || (tspecies > NUM_SPECIES_KIRKLAND)){ - throw std::domain_error(atomReadError(line_num, line)); - } - if (ss.peek() == ',')ss.ignore(); -// std::cout << ss.str() << std::endl; - if (!(ss >> tx)){ - throw std::domain_error(atomReadError(line_num, line)); - } - if (ss.peek() == ',')ss.ignore(); - if (!(ss >> ty)){ - throw std::domain_error(atomReadError(line_num, line)); - } - if (ss.peek() == ',')ss.ignore(); - if (!(ss >> tz)){ - throw std::domain_error(atomReadError(line_num, line)); - } - if (ss.peek() == ',')ss.ignore(); - if (!(ss >> occ)){ - throw std::domain_error(atomReadError(line_num, line)); - } - if (ss.peek() == ',')ss.ignore(); - if (!(ss >> sigma)){ - throw std::domain_error(atomReadError(line_num, line)); - } - if (ss.peek() == ',')ss.ignore(); - atoms.emplace_back(atom{tx / a, ty / b , tz / c, tspecies, sigma, occ}); -// atoms.emplace_back(atom{tx / a, ty / b , tz / c, tspecies}); - } - if (atom_count == 0) { - std::domain_error("Bad input data. No atoms were found in this file.\n"); - } else { - std::cout << "extracted " << atom_count << " atoms from " << line_num << " lines in " << filename - << std::endl; - } - return atoms; - }; + f << "-1\n"; + } +} - std::array peekDims_xyz(const std::string& filename){ - std::ifstream f(filename); - if (!f)throw std::runtime_error("Unable to open file.\n"); - std::string line; - std::string token; - if (!std::getline(f,line)) throw std::runtime_error("Error reading comment line.\n"); - if (!std::getline(f,line)) throw std::runtime_error("Error reading unit cell params.\n"); - double a,b,c; // unit cell params +std::vector readAtoms_xyz(const std::string &filename) +{ + std::vector atoms; + std::ifstream f(filename); + if (!f) + throw std::runtime_error("Unable to open file.\n"); + std::string line; + std::string token; + size_t line_num = 2; + size_t atom_count = 0; + if (!std::getline(f, line)) + throw std::runtime_error("Error reading comment line.\n"); + if (!std::getline(f, line)) + throw std::runtime_error("Error reading unit cell params.\n"); + double a, b, c; // unit cell params + { + std::stringstream ss; + ss.precision(8); + ss << line; + if (!(ss >> a) || (a <= 0)) + throw std::domain_error( + "Bad input data for unit cell dimension a.\n"); + if (!(ss >> b) || (b <= 0)) + throw std::domain_error( + "Bad input data for unit cell dimension b.\n"); + if (!(ss >> c) || (c <= 0)) + throw std::domain_error( + "Bad input data for unit cell dimension c.\n"); + } + while (std::getline(f, line)) + { + line = line.substr(line.find_first_not_of(" \n\t"), line.find_last_not_of(" \n\t")); + if (line.size() <= 3) + { + break; + } + ++atom_count; + ++line_num; + double tx, ty, tz, occ, sigma; + size_t tspecies; + std::stringstream ss; + ss.precision(8); + ss << line; + if (!(ss >> tspecies) || (tspecies > NUM_SPECIES_KIRKLAND)) + { + throw std::domain_error(atomReadError(line_num, line)); + } + if (ss.peek() == ',') + ss.ignore(); + if (!(ss >> tx)) + { + throw std::domain_error(atomReadError(line_num, line)); + } + if (ss.peek() == ',') + ss.ignore(); + if (!(ss >> ty)) + { + throw std::domain_error(atomReadError(line_num, line)); + } + if (ss.peek() == ',') + ss.ignore(); + if (!(ss >> tz)) + { + throw std::domain_error(atomReadError(line_num, line)); + } + if (ss.peek() == ',') + ss.ignore(); + if (!(ss >> occ)) + { + throw std::domain_error(atomReadError(line_num, line)); + } + if (ss.peek() == ',') + ss.ignore(); + if (!(ss >> sigma)) { -// std::stringstream ss(line); - std::stringstream ss; - ss.precision(8); - ss << line; - if (!(ss >> a)) - throw std::domain_error("Bad input data for unit cell dimension a.\n"); - if (!(ss >> b)) - throw std::domain_error("Bad input data for unit cell dimension b.\n"); - if (!(ss >> c)) - throw std::domain_error("Bad input data for unit cell dimension c.\n"); + throw std::domain_error(atomReadError(line_num, line)); } -// return {a,b,c}; - return {c,b,a}; + if (ss.peek() == ',') + ss.ignore(); + atoms.emplace_back(atom{tx / a, ty / b, tz / c, tspecies, sigma, occ}); + // atoms.emplace_back(atom{tx / a, ty / b , tz / c, tspecies}); + } + if (atom_count == 0) + { + std::domain_error("Bad input data. No atoms were found in this file.\n"); + } + else + { + std::cout << "extracted " << atom_count << " atoms from " << line_num << " lines in " << filename + << std::endl; + } + return atoms; +}; + +std::array peekDims_xyz(const std::string &filename) +{ + std::ifstream f(filename); + if (!f) + throw std::runtime_error("Unable to open file.\n"); + std::string line; + std::string token; + if (!std::getline(f, line)) + throw std::runtime_error("Error reading comment line.\n"); + if (!std::getline(f, line)) + throw std::runtime_error("Error reading unit cell params.\n"); + double a, b, c; // unit cell params + { + std::stringstream ss; + ss.precision(8); + ss << line; + if (!(ss >> a)) + throw std::domain_error("Bad input data for unit cell dimension a.\n"); + if (!(ss >> b)) + throw std::domain_error("Bad input data for unit cell dimension b.\n"); + if (!(ss >> c)) + throw std::domain_error("Bad input data for unit cell dimension c.\n"); } + // return {a,b,c}; + return {c, b, a}; +} - std::string getLowercaseExtension(const std::string filename){ - std::string::size_type idx; - idx = filename.rfind('.'); - if(idx != std::string::npos) { - std::string ext = filename.substr(idx + 1); - std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); - return ext; - } else { - return ""; - } - } +std::string getLowercaseExtension(const std::string filename) +{ + std::string::size_type idx; + idx = filename.rfind('.'); + if (idx != std::string::npos) + { + std::string ext = filename.substr(idx + 1); + std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); + return ext; + } + else + { + return ""; + } +} - std::vector defaultAtoms(){ - // returns the unit cell of 100 Silicon from the file SI100.XYZ. This is sometimes used as a default input in case - // the user hasn't provided one, for example in the GUI +std::vector defaultAtoms() +{ + // returns the unit cell of 100 Silicon from the file SI100.XYZ. This is sometimes used as a default input in case + // the user hasn't provided one, for example in the GUI -// one unit cell of 100 silicon -// 5.43 5.43 5.43 -// 14 0.0000 0.0000 0.0000 1.0 0.076 -// 14 2.7150 2.7150 0.0000 1.0 0.076 -// 14 1.3575 4.0725 1.3575 1.0 0.076 -// 14 4.0725 1.3575 1.3575 1.0 0.076 -// 14 2.7150 0.0000 2.7150 1.0 0.076 -// 14 0.0000 2.7150 2.7150 1.0 0.076 -// 14 1.3575 1.3575 4.0725 1.0 0.076 -// 14 4.0725 4.0725 4.0725 1.0 0.076 -// -1 + // one unit cell of 100 silicon + // 5.43 5.43 5.43 + // 14 0.0000 0.0000 0.0000 1.0 0.076 + // 14 2.7150 2.7150 0.0000 1.0 0.076 + // 14 1.3575 4.0725 1.3575 1.0 0.076 + // 14 4.0725 1.3575 1.3575 1.0 0.076 + // 14 2.7150 0.0000 2.7150 1.0 0.076 + // 14 0.0000 2.7150 2.7150 1.0 0.076 + // 14 1.3575 1.3575 4.0725 1.0 0.076 + // 14 4.0725 4.0725 4.0725 1.0 0.076 + // -1 - std::vector result; - result.resize(8); - result.emplace_back(atom{0.0000 / 5.43, 0.0000 / 5.43 , 0.0000 / 5.43, 14, 0.076}); - result.emplace_back(atom{2.7150 / 5.43, 2.7150 / 5.43 , 0.0000 / 5.43, 14, 0.076}); - result.emplace_back(atom{1.3575 / 5.43, 4.0725 / 5.43 , 1.3575 / 5.43, 14, 0.076}); - result.emplace_back(atom{4.0725 / 5.43, 1.3575 / 5.43 , 1.3575 / 5.43, 14, 0.076}); - result.emplace_back(atom{2.7150 / 5.43, 0.0000 / 5.43 , 2.7150 / 5.43, 14, 0.076}); - result.emplace_back(atom{0.0000 / 5.43, 2.7150 / 5.43 , 2.7150 / 5.43, 14, 0.076}); - result.emplace_back(atom{1.3575 / 5.43, 1.3575 / 5.43 , 4.0725 / 5.43, 14, 0.076}); - result.emplace_back(atom{4.0725 / 5.43, 4.0725 / 5.43 , 4.0725 / 5.43, 14, 0.076}); - return result; - } -} \ No newline at end of file + std::vector result; + result.resize(8); + result.emplace_back(atom{0.0000 / 5.43, 0.0000 / 5.43, 0.0000 / 5.43, 14, 0.076}); + result.emplace_back(atom{2.7150 / 5.43, 2.7150 / 5.43, 0.0000 / 5.43, 14, 0.076}); + result.emplace_back(atom{1.3575 / 5.43, 4.0725 / 5.43, 1.3575 / 5.43, 14, 0.076}); + result.emplace_back(atom{4.0725 / 5.43, 1.3575 / 5.43, 1.3575 / 5.43, 14, 0.076}); + result.emplace_back(atom{2.7150 / 5.43, 0.0000 / 5.43, 2.7150 / 5.43, 14, 0.076}); + result.emplace_back(atom{0.0000 / 5.43, 2.7150 / 5.43, 2.7150 / 5.43, 14, 0.076}); + result.emplace_back(atom{1.3575 / 5.43, 1.3575 / 5.43, 4.0725 / 5.43, 14, 0.076}); + result.emplace_back(atom{4.0725 / 5.43, 4.0725 / 5.43, 4.0725 / 5.43, 14, 0.076}); + return result; +} +} // namespace Prismatic \ No newline at end of file diff --git a/src/configure.cpp b/src/configure.cpp index 7eec568b6..c9a08d9f7 100644 --- a/src/configure.cpp +++ b/src/configure.cpp @@ -19,10 +19,6 @@ #include "PRISM01_calcPotential.h" #include "PRISM02_calcSMatrix.h" #include "PRISM03_calcOutput.h" -//#define PRISMATIC_ENABLE_GPU - - - #ifdef PRISMATIC_ENABLE_GPU #include "Multislice_calcOutput.cuh" #include "PRISM02_calcSMatrix.cuh" @@ -30,209 +26,230 @@ #include "utility.cuh" #include "Multislice_entry.h" #endif //PRISMATIC_ENABLE_GPU -namespace Prismatic { - entry_func execute_plan; - ms_output_func buildMultisliceOutput; - prism_output_func buildPRISMOutput; - format_output_func formatOutput_CPU; - fill_Scompact_func fill_Scompact; +namespace Prismatic +{ +entry_func execute_plan; +ms_output_func buildMultisliceOutput; +prism_output_func buildPRISMOutput; +format_output_func formatOutput_CPU; +fill_Scompact_func fill_Scompact; #ifdef PRISMATIC_ENABLE_GPU - template - StreamingMode transferMethodAutoChooser(Prismatic::Metadata& meta) { - // query all devices and choose based on the minimum compute capability - // get the total memory on device - // based on whether the algo is prism or multislice, estimate the largest array and trigger streaming - // safely far from this limit - // For PRISM: limit should be the maximum size of the potential or Scompact - // which is imageSize/2 x imageSize/2 * numberBeams of type complex float/double - // numberBeams can be calculated again here... not a big deal - // For Multislice: limit is the potential array which is imageSize x imageSize x numPlanes of type float/double - constexpr double memoryThreshholdFraction = 0.5; // if estimated size of largest array is greater than this fraction - // times the amount of available memory on a GPU, streaming mode will be triggered - size_t estimatedMaxMemoryUsage; - - T f_x = 4 * meta.interpolationFactorX; - T f_y = 4 * meta.interpolationFactorY; - Array1D imageSize({{(size_t)(meta.cellDim[1] * meta.tileY), (size_t)(meta.cellDim[2] * meta.tileX)}}, {{2}}); - imageSize[0] = (size_t)std::max((PRISMATIC_FLOAT_PRECISION)4.0, (f_y * round(((T)imageSize[0]) / meta.realspacePixelSize[0] / f_y))); - imageSize[1] = (size_t)std::max((PRISMATIC_FLOAT_PRECISION)4.0, (f_x * round(((T)imageSize[1]) / meta.realspacePixelSize[1] / f_x))); - - - - size_t estimatedPotentialSize = (meta.cellDim[0] * meta.tileZ / meta.sliceThickness) * imageSize[0] * imageSize[1] * - sizeof(std::complex); - - - // Estimate the amount of memory needed for the various buffers. This is affected by the batch size, which is inputted - // by the user but will be adjusted if it is inappropriate (i.e. not enough work per thread). - // Figure out the scan configuration to determine how many probes there are to compute - size_t scanWindowXMin; - size_t scanWindowXMax; - size_t scanWindowYMin; - size_t scanWindowYMax; - if(meta.realSpaceWindow_x){ - scanWindowXMin = meta.scanWindowXMin_r / (meta.cellDim[2] * meta.tileX); - scanWindowXMax = meta.scanWindowXMax_r / (meta.cellDim[2] * meta.tileX); - }else{ - scanWindowXMin = meta.scanWindowXMin; - scanWindowXMax = meta.scanWindowXMax; - } +template +StreamingMode transferMethodAutoChooser(Prismatic::Metadata &meta) +{ + // query all devices and choose based on the minimum compute capability + // get the total memory on device + // based on whether the algo is prism or multislice, estimate the largest array and trigger streaming + // safely far from this limit + // For PRISM: limit should be the maximum size of the potential or Scompact + // which is imageSize/2 x imageSize/2 * numberBeams of type complex float/double + // numberBeams can be calculated again here... not a big deal + // For Multislice: limit is the potential array which is imageSize x imageSize x numPlanes of type float/double + constexpr double memoryThreshholdFraction = 0.5; // if estimated size of largest array is greater than this fraction + // times the amount of available memory on a GPU, streaming mode will be triggered + size_t estimatedMaxMemoryUsage; + + T f_x = 4 * meta.interpolationFactorX; + T f_y = 4 * meta.interpolationFactorY; + Array1D imageSize({{(size_t)(meta.cellDim[1] * meta.tileY), (size_t)(meta.cellDim[2] * meta.tileX)}}, {{2}}); + imageSize[0] = (size_t)std::max((PRISMATIC_FLOAT_PRECISION)4.0, (f_y * round(((T)imageSize[0]) / meta.realspacePixelSize[0] / f_y))); + imageSize[1] = (size_t)std::max((PRISMATIC_FLOAT_PRECISION)4.0, (f_x * round(((T)imageSize[1]) / meta.realspacePixelSize[1] / f_x))); + + size_t estimatedPotentialSize = (meta.cellDim[0] * meta.tileZ / meta.sliceThickness) * imageSize[0] * imageSize[1] * + sizeof(std::complex); + + // Estimate the amount of memory needed for the various buffers. This is affected by the batch size, which is inputted + // by the user but will be adjusted if it is inappropriate (i.e. not enough work per thread). + // Figure out the scan configuration to determine how many probes there are to compute + size_t scanWindowXMin; + size_t scanWindowXMax; + size_t scanWindowYMin; + size_t scanWindowYMax; + if (meta.realSpaceWindow_x) + { + scanWindowXMin = meta.scanWindowXMin_r / (meta.cellDim[2] * meta.tileX); + scanWindowXMax = meta.scanWindowXMax_r / (meta.cellDim[2] * meta.tileX); + } + else + { + scanWindowXMin = meta.scanWindowXMin; + scanWindowXMax = meta.scanWindowXMax; + } - if(meta.realSpaceWindow_y){ - scanWindowYMin = meta.scanWindowYMin_r / (meta.cellDim[1] * meta.tileY); - scanWindowYMax = meta.scanWindowYMax_r / (meta.cellDim[1] * meta.tileY); - }else{ - scanWindowYMin = meta.scanWindowYMin; - scanWindowYMax = meta.scanWindowYMax; - } + if (meta.realSpaceWindow_y) + { + scanWindowYMin = meta.scanWindowYMin_r / (meta.cellDim[1] * meta.tileY); + scanWindowYMax = meta.scanWindowYMax_r / (meta.cellDim[1] * meta.tileY); + } + else + { + scanWindowYMin = meta.scanWindowYMin; + scanWindowYMax = meta.scanWindowYMax; + } - Array1D xR = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{2}}); - xR[0] = scanWindowXMin * meta.cellDim[2] * meta.tileX; - xR[1] = scanWindowXMax * meta.cellDim[2] * meta.tileX; - Array1D yR = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{2}}); - yR[0] = scanWindowYMin * meta.cellDim[1] * meta.tileY; - yR[1] = scanWindowYMax * meta.cellDim[1] * meta.tileY; - vector xp_d = vecFromRange(xR[0], meta.probeStepX, xR[1]); - vector yp_d = vecFromRange(yR[0], meta.probeStepY, yR[1]); - - // determine the batch size - size_t batch_size = std::min(meta.batchSizeTargetGPU, max((size_t)1, xp_d.size()*yp_d.size()/ max((size_t)1,(meta.numStreamsPerGPU*meta.numGPUs)))); // make sure the batch is small enough to spread work to all threads - - // estimate the amount of buffer memory needed. The factor of 3 is because there are two arrays that must be allocated space that scales - // with the batch size, and the cuFFT plans also allocate internal buffers. - size_t estimatedBatchBufferSize = meta.numStreamsPerGPU*3*batch_size*imageSize[0]*imageSize[1]*sizeof(std::complex); - estimatedMaxMemoryUsage = 3*estimatedPotentialSize + estimatedBatchBufferSize; // factor of 3 is because there is a complex array of the same size created - - cout << "Estimated potential array size = " << estimatedPotentialSize << '\n'; - cout << "Estimated buffer memory needed = " << estimatedBatchBufferSize << '\n'; - cout << "meta.numStreamsPerGPU*2*batch_size*imageSize[0]*imageSize[1]= " << meta.numStreamsPerGPU*2*batch_size*imageSize[0]*imageSize[1] << '\n'; - - if (meta.algorithm == Prismatic::Algorithm::PRISM) { - Array1D xv = makeFourierCoords(imageSize[1], - (PRISMATIC_FLOAT_PRECISION) 1 / imageSize[1]); - Array1D yv = makeFourierCoords(imageSize[0], - (PRISMATIC_FLOAT_PRECISION) 1 / imageSize[0]); - pair, Array2D > mesh_a = meshgrid(yv, xv); - - Array1D qx = makeFourierCoords(imageSize[1], meta.realspacePixelSize[1]); - Array1D qy = makeFourierCoords(imageSize[0], meta.realspacePixelSize[0]); - - pair, Array2D > mesh = meshgrid(qy, qx); - Array2D q2(mesh.first); - transform(mesh.second.begin(), mesh.second.end(), - mesh.first.begin(), q2.begin(), - [](const PRISMATIC_FLOAT_PRECISION &a, const PRISMATIC_FLOAT_PRECISION &b) { - return a * a + b * b; - }); - - - Array2D qMask = zeros_ND<2, unsigned int>({{imageSize[0], imageSize[1]}}); + Array1D xR = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{2}}); + xR[0] = scanWindowXMin * meta.cellDim[2] * meta.tileX; + xR[1] = scanWindowXMax * meta.cellDim[2] * meta.tileX; + Array1D yR = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{2}}); + yR[0] = scanWindowYMin * meta.cellDim[1] * meta.tileY; + yR[1] = scanWindowYMax * meta.cellDim[1] * meta.tileY; + vector xp_d = vecFromRange(xR[0], meta.probeStepX, xR[1]); + vector yp_d = vecFromRange(yR[0], meta.probeStepY, yR[1]); + + // determine the batch size + size_t batch_size = std::min(meta.batchSizeTargetGPU, max((size_t)1, xp_d.size() * yp_d.size() / max((size_t)1, (meta.numStreamsPerGPU * meta.numGPUs)))); // make sure the batch is small enough to spread work to all threads + + // estimate the amount of buffer memory needed. The factor of 3 is because there are two arrays that must be allocated space that scales + // with the batch size, and the cuFFT plans also allocate internal buffers. + size_t estimatedBatchBufferSize = meta.numStreamsPerGPU * 3 * batch_size * imageSize[0] * imageSize[1] * sizeof(std::complex); + estimatedMaxMemoryUsage = 3 * estimatedPotentialSize + estimatedBatchBufferSize; // factor of 3 is because there is a complex array of the same size created + + cout << "Estimated potential array size = " << estimatedPotentialSize << '\n'; + cout << "Estimated buffer memory needed = " << estimatedBatchBufferSize << '\n'; + cout << "meta.numStreamsPerGPU*2*batch_size*imageSize[0]*imageSize[1]= " << meta.numStreamsPerGPU * 2 * batch_size * imageSize[0] * imageSize[1] << '\n'; + + if (meta.algorithm == Prismatic::Algorithm::PRISM) + { + Array1D xv = makeFourierCoords(imageSize[1], + (PRISMATIC_FLOAT_PRECISION)1 / imageSize[1]); + Array1D yv = makeFourierCoords(imageSize[0], + (PRISMATIC_FLOAT_PRECISION)1 / imageSize[0]); + pair, Array2D> mesh_a = meshgrid(yv, xv); + + Array1D qx = makeFourierCoords(imageSize[1], meta.realspacePixelSize[1]); + Array1D qy = makeFourierCoords(imageSize[0], meta.realspacePixelSize[0]); + + pair, Array2D> mesh = meshgrid(qy, qx); + Array2D q2(mesh.first); + transform(mesh.second.begin(), mesh.second.end(), + mesh.first.begin(), q2.begin(), + [](const PRISMATIC_FLOAT_PRECISION &a, const PRISMATIC_FLOAT_PRECISION &b) { + return a * a + b * b; + }); + + Array2D qMask = zeros_ND<2, unsigned int>({{imageSize[0], imageSize[1]}}); + { + long offset_x = qMask.get_dimi() / 4; + long offset_y = qMask.get_dimj() / 4; + long ndimy = (long)qMask.get_dimj(); + long ndimx = (long)qMask.get_dimi(); + for (long y = 0; y < qMask.get_dimj() / 2; ++y) { - long offset_x = qMask.get_dimi() / 4; - long offset_y = qMask.get_dimj() / 4; - long ndimy = (long) qMask.get_dimj(); - long ndimx = (long) qMask.get_dimi(); - for (long y = 0; y < qMask.get_dimj() / 2; ++y) { - for (long x = 0; x < qMask.get_dimi() / 2; ++x) { - qMask.at(((y - offset_y) % ndimy + ndimy) % ndimy, - ((x - offset_x) % ndimx + ndimx) % ndimx) = 1; - } + for (long x = 0; x < qMask.get_dimi() / 2; ++x) + { + qMask.at(((y - offset_y) % ndimy + ndimy) % ndimy, + ((x - offset_x) % ndimx + ndimx) % ndimx) = 1; } } + } - constexpr double m = 9.109383e-31; - constexpr double e = 1.602177e-19; - constexpr double c = 299792458; - constexpr double h = 6.62607e-34; - T lambda = (T) (h / sqrt(2 * m * e * meta.E0) / sqrt(1 + e * meta.E0 / 2 / m / c / c) * 1e10); - - // create beam mask and count beams - Prismatic::Array2D mask; - mask = zeros_ND<2, unsigned int>({{imageSize[0], imageSize[1]}}); - size_t numberBeams = 0; - long interp_fx = (long) meta.interpolationFactorX; - long interp_fy = (long) meta.interpolationFactorY; - for (auto y = 0; y < qMask.get_dimj(); ++y) { - for (auto x = 0; x < qMask.get_dimi(); ++x) { - if (q2.at(y, x) < pow(meta.alphaBeamMax / lambda, 2) && - qMask.at(y, x) == 1 && - (long) round(mesh_a.first.at(y, x)) % interp_fy == 0 && - (long) round(mesh_a.second.at(y, x)) % interp_fx == 0) { - mask.at(y, x) = 1; - ++numberBeams; - } + constexpr double m = 9.109383e-31; + constexpr double e = 1.602177e-19; + constexpr double c = 299792458; + constexpr double h = 6.62607e-34; + T lambda = (T)(h / sqrt(2 * m * e * meta.E0) / sqrt(1 + e * meta.E0 / 2 / m / c / c) * 1e10); + + // create beam mask and count beams + Prismatic::Array2D mask; + mask = zeros_ND<2, unsigned int>({{imageSize[0], imageSize[1]}}); + size_t numberBeams = 0; + long interp_fx = (long)meta.interpolationFactorX; + long interp_fy = (long)meta.interpolationFactorY; + for (auto y = 0; y < qMask.get_dimj(); ++y) + { + for (auto x = 0; x < qMask.get_dimi(); ++x) + { + if (q2.at(y, x) < pow(meta.alphaBeamMax / lambda, 2) && + qMask.at(y, x) == 1 && + (long)round(mesh_a.first.at(y, x)) % interp_fy == 0 && + (long)round(mesh_a.second.at(y, x)) % interp_fx == 0) + { + mask.at(y, x) = 1; + ++numberBeams; } } + } - size_t estimatedSMatrixSize = + size_t estimatedSMatrixSize = numberBeams * imageSize[0] * imageSize[1] / 4 * sizeof(std::complex); - estimatedMaxMemoryUsage = std::max(estimatedSMatrixSize, estimatedMaxMemoryUsage); - } + estimatedMaxMemoryUsage = std::max(estimatedSMatrixSize, estimatedMaxMemoryUsage); + } #ifdef PRISMATIC_ENABLE_GPU - size_t available_memory; - cudaErrchk(cudaMemGetInfo(&available_memory, NULL)); - cout << "Available GPU memory = " << available_memory << '\n'; - cout << "Estimated GPU memory usage for single transfer method = " << estimatedMaxMemoryUsage << '\n'; - return (estimatedMaxMemoryUsage > memoryThreshholdFraction * available_memory) ? - Prismatic::StreamingMode::Stream : Prismatic::StreamingMode::SingleXfer; + size_t available_memory; + cudaErrchk(cudaMemGetInfo(&available_memory, NULL)); + cout << "Available GPU memory = " << available_memory << '\n'; + cout << "Estimated GPU memory usage for single transfer method = " << estimatedMaxMemoryUsage << '\n'; + return (estimatedMaxMemoryUsage > memoryThreshholdFraction * available_memory) ? Prismatic::StreamingMode::Stream : Prismatic::StreamingMode::SingleXfer; #else - return Prismatic::StreamingMode::SingleXfer; + return Prismatic::StreamingMode::SingleXfer; #endif //PRISMATIC_ENABLE_GPU - } - format_output_func_GPU formatOutput_GPU; +} +format_output_func_GPU formatOutput_GPU; #endif - void configure(Metadata& meta) { - // std::cout << "Formatting" << std::endl; - formatOutput_CPU = formatOutput_CPU_integrate; +void configure(Metadata &meta) +{ + // std::cout << "Formatting" << std::endl; + formatOutput_CPU = formatOutput_CPU_integrate; #ifdef PRISMATIC_ENABLE_GPU - formatOutput_GPU = formatOutput_GPU_integrate; + formatOutput_GPU = formatOutput_GPU_integrate; #endif - if (meta.algorithm == Algorithm::PRISM) { - std::cout << "Execution plan: PRISM\n"; - execute_plan = PRISM_entry; + if (meta.algorithm == Algorithm::PRISM) + { + std::cout << "Execution plan: PRISM\n"; + execute_plan = PRISM_entry; #ifdef PRISMATIC_ENABLE_GPU - if (meta.transferMode == Prismatic::StreamingMode::Auto){ - meta.transferMode = transferMethodAutoChooser(meta); - } - std::cout << "Using GPU codes" << '\n'; - if (meta.transferMode == Prismatic::StreamingMode::Stream) { - cout << "Using streaming method\n"; - fill_Scompact = fill_Scompact_GPU_streaming; - buildPRISMOutput = buildPRISMOutput_GPU_streaming; - } else { - cout << "Using single transfer method\n"; - fill_Scompact = fill_Scompact_GPU_singlexfer; - buildPRISMOutput = buildPRISMOutput_GPU_singlexfer; - } + if (meta.transferMode == Prismatic::StreamingMode::Auto) + { + meta.transferMode = transferMethodAutoChooser(meta); + } + std::cout << "Using GPU codes" << '\n'; + if (meta.transferMode == Prismatic::StreamingMode::Stream) + { + cout << "Using streaming method\n"; + fill_Scompact = fill_Scompact_GPU_streaming; + buildPRISMOutput = buildPRISMOutput_GPU_streaming; + } + else + { + cout << "Using single transfer method\n"; + fill_Scompact = fill_Scompact_GPU_singlexfer; + buildPRISMOutput = buildPRISMOutput_GPU_singlexfer; + } #else - fill_Scompact = fill_Scompact_CPUOnly; - buildPRISMOutput = buildPRISMOutput_CPUOnly; + fill_Scompact = fill_Scompact_CPUOnly; + buildPRISMOutput = buildPRISMOutput_CPUOnly; #endif //PRISMATIC_ENABLE_GPU - } else if (meta.algorithm == Algorithm::Multislice) { - std::cout << "Execution plan: Multislice\n"; - execute_plan = Multislice_entry; + } + else if (meta.algorithm == Algorithm::Multislice) + { + std::cout << "Execution plan: Multislice\n"; + execute_plan = Multislice_entry; #ifdef PRISMATIC_ENABLE_GPU - std::cout << "Using GPU codes" << '\n'; - if (meta.transferMode == Prismatic::StreamingMode::Auto){ - meta.transferMode = transferMethodAutoChooser(meta); - } - if (meta.transferMode == Prismatic::StreamingMode::Stream) { - cout << "Using streaming method\n"; - buildMultisliceOutput = buildMultisliceOutput_GPU_streaming; - } else { - cout << "Using single transfer method\n"; - buildMultisliceOutput = buildMultisliceOutput_GPU_singlexfer; - } + std::cout << "Using GPU codes" << '\n'; + if (meta.transferMode == Prismatic::StreamingMode::Auto) + { + meta.transferMode = transferMethodAutoChooser(meta); + } + if (meta.transferMode == Prismatic::StreamingMode::Stream) + { + cout << "Using streaming method\n"; + buildMultisliceOutput = buildMultisliceOutput_GPU_streaming; + } + else + { + cout << "Using single transfer method\n"; + buildMultisliceOutput = buildMultisliceOutput_GPU_singlexfer; + } #else - buildMultisliceOutput = buildMultisliceOutput_CPUOnly; + buildMultisliceOutput = buildMultisliceOutput_CPUOnly; #endif //PRISMATIC_ENABLE_GPU - } } } +} // namespace Prismatic diff --git a/src/driver.cpp b/src/driver.cpp index 55fbabeba..8705f959e 100644 --- a/src/driver.cpp +++ b/src/driver.cpp @@ -18,23 +18,25 @@ #include "utility.h" using namespace std; -int main(int argc, const char** argv) { +int main(int argc, const char **argv) +{ Prismatic::Metadata meta; // parse command line options - if (!Prismatic::parseInputs(meta, argc, &argv))return 1; + if (!Prismatic::parseInputs(meta, argc, &argv)) + return 1; Prismatic::printHeader(); -// Prismatic::printTime(); + // Prismatic::printTime(); // print metadata -// meta.toString(); + // meta.toString(); // execute simulation Prismatic::go(meta); -// Prismatic::printTime(); + // Prismatic::printTime(); return 0; } diff --git a/src/go.cpp b/src/go.cpp index 1d617db0f..536389df9 100644 --- a/src/go.cpp +++ b/src/go.cpp @@ -23,21 +23,22 @@ #include "go.h" #include "parseInput.h" -namespace Prismatic{ - void go(Metadata meta){ - // configure simulation behavior - Prismatic::configure(meta); +namespace Prismatic +{ +void go(Metadata meta) +{ + // configure simulation behavior + Prismatic::configure(meta); - // execute simulation - Prismatic::execute_plan(meta); + // execute simulation + Prismatic::execute_plan(meta); #ifdef _WIN32 - char* appdata = getenv("APPDATA"); - Prismatic::writeParamFile(meta, std::string(appdata) + "\\prismatic_gui_params.txt"); + char *appdata = getenv("APPDATA"); + Prismatic::writeParamFile(meta, std::string(appdata) + "\\prismatic_gui_params.txt"); #else - char* appdata = getenv("HOME"); - Prismatic::writeParamFile(meta, std::string(appdata) + "/prismatic_gui_params.txt"); + char *appdata = getenv("HOME"); + Prismatic::writeParamFile(meta, std::string(appdata) + "/prismatic_gui_params.txt"); #endif //_WIN32 - - } } +} // namespace Prismatic diff --git a/src/parseInput.cpp b/src/parseInput.cpp index 183ce0611..8ac50204e 100644 --- a/src/parseInput.cpp +++ b/src/parseInput.cpp @@ -25,1201 +25,1370 @@ #endif //_WIN32 #include "atom.h" +namespace Prismatic +{ +using namespace std; + +void printHelp() +{ + Metadata defaults; + // bool parseInput(Metadata& meta, + // int& argc, const char*** argv); + std::cout << "Basic usage is prismatic -i filename [other options]" << std::endl; + std::cout << "The following options are available with prismatic, each documented as long form (short form) *parameters* : description\n" + "\n" + "* --input-file (-i) filename : filename containing the atomic coordinates, see www.prism-em.com/about for details (default: " + << defaults.filenameAtoms << ")\n" + << "* --param-file (-pf) filename : filename containing simulation parameters. This optional file can contain any number of parameters in the form of a text file with one entry per line of the form param:value.\n" + << "* --output-file(-o) filename : output filename (default: " << defaults.filenameOutput << ")\n" + << "* --interp-factor (-f) number : PRISM interpolation factor, used for both X and Y (default: " << defaults.interpolationFactorX << ")\n" + << "* --interp-factor-x (-fx) number : PRISM interpolation factor in X (default: " << defaults.interpolationFactorX << ")\n" + << "* --interp-factor-y (-fy) number : PRISM interpolation factor in Y (default: " << defaults.interpolationFactorY << ")\n" + << "* --num-threads (-j) value : number of CPU threads to use (default: " << defaults.numThreads << ")\n" + << "* --num-streams (-S) value : number of CUDA streams to create per GPU (default: " << defaults.numStreamsPerGPU << ")\n" + << "* --num-gpus (-g) value : number of GPUs to use. A runtime check is performed to check how many are actually available, and the minimum of these two numbers is used. (default: " << defaults.numGPUs << ")\n" + << "* --slice-thickness (-s) thickness : thickness of each slice of projected potential (in Angstroms) (default: " << defaults.sliceThickness << ")\n" + << "* --num-slices (-ns) number of slices: in multislice mode, number of slices before intermediate output is given (default: " << defaults.numSlices << ")\n" + << "* --zstart-slices (-zs) value: in multislice mode, depth Z at which to begin intermediate output (default: " << defaults.zStart << ")\n" + << "* --batch-size (-b) value : number of probes/beams to propagate simultaneously for both CPU and GPU workers. (default: " << defaults.batchSizeCPU << ")\n" + << "* --batch-size-cpu (-bc) value : number of probes/beams to propagate simultaneously for CPU workers. (default: " << defaults.batchSizeCPU << ")\n" + << "* --batch-size-gpu (-bg) value : number of probes/beams to propagate simultaneously for GPU workers. (default: " << defaults.batchSizeGPU << ")\n" + << "* --help(-h) : print information about the available options\n" + "* --pixel-size (-p) pixel_size : size of simulated potential/probe X/Y pixel size (default: " + << defaults.realspacePixelSize[0] << "). Note this is different from the size of a pixel in the output, which is determined by probe_stepX(Y)\n" + << "* --pixel-size-x (-px) pixel_size : size of simulated potential/probe X pixel size (default: " << defaults.realspacePixelSize[1] << "). Note this is different from the size of a pixel in the output, which is determined by probe_stepX(Y)\n" + << "* --pixel-size-y (-py) pixel_size : size of simulated potential/probe Y pixel size (default: " << defaults.realspacePixelSize[0] << "). Note this is different from the size of a pixel in the output, which is determined by probe_stepX(Y)\n" + << "* --detector-angle-step (-d) step_size : angular step size for detector integration bins (in mrad) (default: " << (1000 * defaults.detectorAngleStep) << ")\n" + << "* --cell-dimension (-c) x y z : size of sample in x, y, z directions (in Angstroms) (default: " << defaults.cellDim[2] << " " << defaults.cellDim[1] << " " << defaults.cellDim[0] << ")\n" + << "* --tile-uc (-t) x y z : tile the unit cell x, y, z number of times in x, y, z directions, respectively (default: " << defaults.tileX << " " << defaults.tileY << " " << defaults.tileZ << ")\n" + << "* --algorithm (-a) p/m : the simulation algorithm to use, either (p)rism or (m)ultislice (default: PRISM)\n" + << "* --energy (-E) value : the energy of the electron beam (in keV) (default: " << defaults.E0 / 1000 << ")\n" + << "* --alpha-max (-A) angle : the maximum probe angle to consider (in mrad) (default: " << 1000 * defaults.alphaBeamMax << ")\n" + << "* --potential-bound (-P) value : the maximum radius from the center of each atom to compute the potental (in Angstroms) (default: " << defaults.potBound << ")\n" + << "* --also-do-cpu-work (-C) bool=true : boolean value used to determine whether or not to also create CPU workers in addition to GPU ones (default: 1)\n" + << "* --streaming-mode 0/1 : boolean value to force code to use (true) or not use (false) streaming versions of GPU codes. The default behavior is to estimate the needed memory from input parameters and choose automatically. (default: Auto)\n" + << "* --probe-step (-r) step_size : step size of the probe for both X and Y directions (in Angstroms) (default: " << defaults.probeStepX << ")\n" + << "* --probe-step-x (-rx) step_size : step size of the probe in X direction (in Angstroms) (default: " << defaults.probeStepX << ")\n" + << "* --probe-step-y (-ry) step_size : step size of the probe in Y direction (in Angstroms) (default: " << defaults.probeStepY << ")\n" + << "* --random-seed (-rs) step_size : random integer number seed\n" + "* --probe-xtilt (-tx) value : probe X tilt (in mrad) (default: " + << defaults.probeXtilt << ")\n" + << "* --probe-ytilt (-ty) value : probe X tilt (in mrad) (default: " << defaults.probeYtilt << ")\n" + << "* --probe-defocus (-df) value : probe defocus (in mrad) (default: " << defaults.probeDefocus << ")\n" + << "* -C3 value : microscope C3 aberration constant (in Angstrom) (default: " << defaults.C3 << ")\n" + << "* -C5 value : microscope C5 aberration constant (in Angstrom) (default: " << defaults.C5 << ")\n" + << "* --probe-semiangle (-sa) value : maximum probe semiangle (in mrad) (default: " << 1000 * defaults.probeSemiangle << ")\n" + << "* --scan-window-x (-wx) min max : size of the window to scan the probe in X (in fractional coordinates between 0 and 1) (default: " << defaults.scanWindowXMin << " " << defaults.scanWindowXMax << ")\n" + << "* --scan-window-y (-wy) min max : size of the window to scan the probe in Y (in fractional coordinates between 0 and 1) (default: " << defaults.scanWindowYMin << " " << defaults.scanWindowYMax << ")\n" + << "* --scan-window-xr (-wxr) min max : size of the window to scan the probe in X (in Angstroms) (defaults to fractional coordinates) " + << ")\n" + << "* --scan-window-yr (-wyr) min max : size of the window to scan the probe in Y (in Angstroms) (defaults to fractional coordiantes) " + << ")\n" + << "* --num-FP (-F) value : number of frozen phonon configurations to calculate (default: " << defaults.numFP << ")\n" + << "* --thermal-effects (-te) bool : whether or not to include Debye-Waller factors (thermal effects) (default: True)\n" + << "* --occupancy (-oc) bool : whether or not to consider occupancy values for likelihood of atoms existing at each site (default: True)\n" + << "* --save-2D-output (-2D) ang_min ang_max : save the 2D STEM image integrated between ang_min and ang_max (in mrads) (default: Off)\n" + << "* --save-3D-output (-3D) bool=true : Also save the 3D output at the detector for each probe (3D output mode) (default: On)\n" + << "* --save-4D-output (-4D) bool=false : Also save the 4D output at the detector for each probe (4D output mode) (default: Off)\n" + << "* --save-DPC-CoM (-DPC) bool=false : Also save the DPC Center of Mass calculation (default: Off)\n" + << "* --save-real-space-coords (-rsc) bool=false : Also save the real space coordinates of the probe dimensions (default: Off)\n" + << "* --save-potential-slices (-ps) bool=false : Also save the calculated potential slices (default: Off)\n" + << "* --nyquist-sampling (-nqs) bool=false : Set number of probe positions at Nyquist sampling limit (default: Off)]\n"; +} -namespace Prismatic { - using namespace std; - - void printHelp() { - Metadata defaults; - // bool parseInput(Metadata& meta, - // int& argc, const char*** argv); - std::cout << "Basic usage is prismatic -i filename [other options]" << std::endl; - std::cout << "The following options are available with prismatic, each documented as long form (short form) *parameters* : description\n" - "\n" - "* --input-file (-i) filename : filename containing the atomic coordinates, see www.prism-em.com/about for details (default: " << defaults.filenameAtoms << ")\n" << - "* --param-file (-pf) filename : filename containing simulation parameters. This optional file can contain any number of parameters in the form of a text file with one entry per line of the form param:value.\n" << - "* --output-file(-o) filename : output filename (default: " << defaults.filenameOutput << ")\n" << - "* --interp-factor (-f) number : PRISM interpolation factor, used for both X and Y (default: " << defaults.interpolationFactorX << ")\n" << - "* --interp-factor-x (-fx) number : PRISM interpolation factor in X (default: " << defaults.interpolationFactorX << ")\n" << - "* --interp-factor-y (-fy) number : PRISM interpolation factor in Y (default: " << defaults.interpolationFactorY << ")\n" << - "* --num-threads (-j) value : number of CPU threads to use (default: " << defaults.numThreads << ")\n" << - "* --num-streams (-S) value : number of CUDA streams to create per GPU (default: " << defaults.numStreamsPerGPU << ")\n" << - "* --num-gpus (-g) value : number of GPUs to use. A runtime check is performed to check how many are actually available, and the minimum of these two numbers is used. (default: " << defaults.numGPUs << ")\n" << - "* --slice-thickness (-s) thickness : thickness of each slice of projected potential (in Angstroms) (default: " << defaults.sliceThickness << ")\n" << - "* --num-slices (-ns) number of slices: in multislice mode, number of slices before intermediate output is given (default: " << defaults.numSlices << ")\n" << - "* --zstart-slices (-zs) value: in multislice mode, depth Z at which to begin intermediate output (default: " << defaults.zStart << ")\n" << - "* --batch-size (-b) value : number of probes/beams to propagate simultaneously for both CPU and GPU workers. (default: " << defaults.batchSizeCPU << ")\n" << - "* --batch-size-cpu (-bc) value : number of probes/beams to propagate simultaneously for CPU workers. (default: " << defaults.batchSizeCPU << ")\n" << - "* --batch-size-gpu (-bg) value : number of probes/beams to propagate simultaneously for GPU workers. (default: " << defaults.batchSizeGPU << ")\n" << - "* --help(-h) : print information about the available options\n" - "* --pixel-size (-p) pixel_size : size of simulated potential/probe X/Y pixel size (default: " << defaults.realspacePixelSize[0] << "). Note this is different from the size of a pixel in the output, which is determined by probe_stepX(Y)\n" << - "* --pixel-size-x (-px) pixel_size : size of simulated potential/probe X pixel size (default: " << defaults.realspacePixelSize[1] << "). Note this is different from the size of a pixel in the output, which is determined by probe_stepX(Y)\n" << - "* --pixel-size-y (-py) pixel_size : size of simulated potential/probe Y pixel size (default: " << defaults.realspacePixelSize[0] << "). Note this is different from the size of a pixel in the output, which is determined by probe_stepX(Y)\n" << - "* --detector-angle-step (-d) step_size : angular step size for detector integration bins (in mrad) (default: " << (1000 * defaults.detectorAngleStep) << ")\n" << - "* --cell-dimension (-c) x y z : size of sample in x, y, z directions (in Angstroms) (default: " << defaults.cellDim[2] << " " << defaults.cellDim[1] << " " << defaults.cellDim[0] << ")\n" << - "* --tile-uc (-t) x y z : tile the unit cell x, y, z number of times in x, y, z directions, respectively (default: " << defaults.tileX << " " << defaults.tileY << " " << defaults.tileZ << ")\n" << - "* --algorithm (-a) p/m : the simulation algorithm to use, either (p)rism or (m)ultislice (default: PRISM)\n" << - "* --energy (-E) value : the energy of the electron beam (in keV) (default: " << defaults.E0/1000 << ")\n" << - "* --alpha-max (-A) angle : the maximum probe angle to consider (in mrad) (default: " << 1000*defaults.alphaBeamMax << ")\n" << - "* --potential-bound (-P) value : the maximum radius from the center of each atom to compute the potental (in Angstroms) (default: " << defaults.potBound << ")\n" << - "* --also-do-cpu-work (-C) bool=true : boolean value used to determine whether or not to also create CPU workers in addition to GPU ones (default: 1)\n" << - "* --streaming-mode 0/1 : boolean value to force code to use (true) or not use (false) streaming versions of GPU codes. The default behavior is to estimate the needed memory from input parameters and choose automatically. (default: Auto)\n" << - "* --probe-step (-r) step_size : step size of the probe for both X and Y directions (in Angstroms) (default: " << defaults.probeStepX << ")\n" << - "* --probe-step-x (-rx) step_size : step size of the probe in X direction (in Angstroms) (default: " << defaults.probeStepX << ")\n" << - "* --probe-step-y (-ry) step_size : step size of the probe in Y direction (in Angstroms) (default: " << defaults.probeStepY << ")\n" << - "* --random-seed (-rs) step_size : random integer number seed\n" - "* --probe-xtilt (-tx) value : probe X tilt (in mrad) (default: " << defaults.probeXtilt << ")\n" << - "* --probe-ytilt (-ty) value : probe X tilt (in mrad) (default: " << defaults.probeYtilt << ")\n" << - "* --probe-defocus (-df) value : probe defocus (in mrad) (default: " << defaults.probeDefocus << ")\n" << - "* -C3 value : microscope C3 aberration constant (in Angstrom) (default: " << defaults.C3 << ")\n" << - "* -C5 value : microscope C5 aberration constant (in Angstrom) (default: " << defaults.C5 << ")\n" << - "* --probe-semiangle (-sa) value : maximum probe semiangle (in mrad) (default: " << 1000*defaults.probeSemiangle << ")\n" << - "* --scan-window-x (-wx) min max : size of the window to scan the probe in X (in fractional coordinates between 0 and 1) (default: " << defaults.scanWindowXMin << " " << defaults.scanWindowXMax << ")\n" << - "* --scan-window-y (-wy) min max : size of the window to scan the probe in Y (in fractional coordinates between 0 and 1) (default: " << defaults.scanWindowYMin << " " << defaults.scanWindowYMax << ")\n" << - "* --scan-window-xr (-wxr) min max : size of the window to scan the probe in X (in Angstroms) (defaults to fractional coordinates) " << ")\n" << - "* --scan-window-yr (-wyr) min max : size of the window to scan the probe in Y (in Angstroms) (defaults to fractional coordiantes) " << ")\n" << - "* --num-FP (-F) value : number of frozen phonon configurations to calculate (default: " << defaults.numFP << ")\n" << - "* --thermal-effects (-te) bool : whether or not to include Debye-Waller factors (thermal effects) (default: True)\n" << - "* --occupancy (-oc) bool : whether or not to consider occupancy values for likelihood of atoms existing at each site (default: True)\n" << - "* --save-2D-output (-2D) ang_min ang_max : save the 2D STEM image integrated between ang_min and ang_max (in mrads) (default: Off)\n" << - "* --save-3D-output (-3D) bool=true : Also save the 3D output at the detector for each probe (3D output mode) (default: On)\n" << - "* --save-4D-output (-4D) bool=false : Also save the 4D output at the detector for each probe (4D output mode) (default: Off)\n" << - "* --save-DPC-CoM (-DPC) bool=false : Also save the DPC Center of Mass calculation (default: Off)\n" << - "* --save-real-space-coords (-rsc) bool=false : Also save the real space coordinates of the probe dimensions (default: Off)\n" << - "* --save-potential-slices (-ps) bool=false : Also save the calculated potential slices (default: Off)\n" << - "* --nyquist-sampling (-nqs) bool=false : Set number of probe positions at Nyquist sampling limit (default: Off)]\n"; - } - - - // string white-space trimming utility functions courtesy of https://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring - // trim from start (in place) - static inline void ltrim(std::string &s) { - s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { - return !std::isspace(ch); - })); - } - - // trim from end (in place) - static inline void rtrim(std::string &s) { - s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { - return !std::isspace(ch); - }).base(), s.end()); - } - - // trim from both ends (in place) - static inline std::string trim(std::string s) { - ltrim(s); - rtrim(s); - return std::string(s); - } - - bool parseParamLine(Metadata& meta, - std::string param_line){ - param_line = trim(param_line); - size_t colon_pos = param_line.find(':'); - if ((colon_pos == param_line.npos) | (colon_pos==0)){ - std::cout << "Invalid parameter file entry found in line\n" << param_line << std::endl; - return false; - } - std::string option = trim(param_line.substr(0, colon_pos)); - std::string args = trim(param_line.substr(colon_pos+1)); - const std::string command = option + " " + args; - int argc = 1 + std::count(command.begin(), command.end(), ' '); - std::istringstream iss(command); - std::vector tokens{std::istream_iterator{iss}, - std::istream_iterator{}}; - const char** command_c = new const char*[tokens.size()]; - for (int i = 0; i < tokens.size(); ++i){ - command_c[i] = tokens[i].c_str(); - } +// string white-space trimming utility functions courtesy of https://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring +// trim from start (in place) +static inline void ltrim(std::string &s) +{ + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { + return !std::isspace(ch); + })); +} - // because the pointer is incremented during parseInput, we - // save the location so that it can be deleted at the end to avoid a memory leak - const char** command_c_saved = command_c; - if (!parseInput(meta, argc, (const char***)&command_c)){ - delete[] command_c_saved; - return false; - } - delete[] command_c_saved; - return true; +// trim from end (in place) +static inline void rtrim(std::string &s) +{ + s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { + return !std::isspace(ch); + }) + .base(), + s.end()); +} + +// trim from both ends (in place) +static inline std::string trim(std::string s) +{ + ltrim(s); + rtrim(s); + return std::string(s); +} + +bool parseParamLine(Metadata &meta, + std::string param_line) +{ + param_line = trim(param_line); + size_t colon_pos = param_line.find(':'); + if ((colon_pos == param_line.npos) | (colon_pos == 0)) + { + std::cout << "Invalid parameter file entry found in line\n" + << param_line << std::endl; + return false; + } + std::string option = trim(param_line.substr(0, colon_pos)); + std::string args = trim(param_line.substr(colon_pos + 1)); + const std::string command = option + " " + args; + int argc = 1 + std::count(command.begin(), command.end(), ' '); + std::istringstream iss(command); + std::vector tokens{std::istream_iterator{iss}, + std::istream_iterator{}}; + const char **command_c = new const char *[tokens.size()]; + for (int i = 0; i < tokens.size(); ++i) + { + command_c[i] = tokens[i].c_str(); } - bool validateFilename(const std::string str){ - std::ifstream f(str); - return f.good(); + // because the pointer is incremented during parseInput, we + // save the location so that it can be deleted at the end to avoid a memory leak + const char **command_c_saved = command_c; + if (!parseInput(meta, argc, (const char ***)&command_c)) + { + delete[] command_c_saved; + return false; } + delete[] command_c_saved; + return true; +} - bool writeParamFile(Metadata& meta, - const std::string param_filename){ - std::cout << "Writing simulation parameters to file " << param_filename << std::endl; - std::ofstream f(param_filename); - if (!f)throw std::runtime_error("Unable to open file.\n"); - std::string line; - if (meta.algorithm == Algorithm::Multislice){ - f << "--algorithm:" << 'm' << '\n'; - } else { - f << "--algorithm:" << 'p' << '\n'; - } - if (validateFilename(meta.filenameAtoms))f << "--input-file:" << meta.filenameAtoms << '\n'; - f << "--output-file:" << meta.filenameOutput << '\n'; - f << "--num-threads:" << meta.numThreads << '\n'; - f << "--pixel-size-x:" << meta.realspacePixelSize[1] << '\n'; - f << "--pixel-size-y:" << meta.realspacePixelSize[0] << '\n'; - f << "--potential-bound:" << meta.potBound << '\n'; - f << "--num-FP:" << meta.numFP << '\n'; - f << "--slice-thickness:" << meta.sliceThickness<< '\n'; - f << "--num-slices:" << meta.numSlices<< '\n'; - f << "--zstart-slices:" << meta.zStart<< '\n'; - f << "--energy:" << meta.E0 / 1000 << '\n'; - f << "--alpha-max:" << meta.alphaBeamMax * 1000 << '\n'; - f << "--batch-size-cpu:" << meta.batchSizeTargetCPU << '\n'; - f << "--probe-step-x:" << meta.probeStepX << '\n'; - f << "--probe-step-y:" << meta.probeStepY << '\n'; - if (meta.userSpecifiedCelldims == true){ - f << "--cell-dimension:" << meta.cellDim[2] << ' ' << meta.cellDim[1] << ' ' << meta.cellDim[0] << '\n'; - } else { - try{ - std::array cell_dims = peekDims_xyz(meta.filenameAtoms); - f << "--cell-dimension:" << cell_dims[2] << ' ' << cell_dims[1] << ' ' << cell_dims[0] << '\n'; - } catch(std::runtime_error){ - - } - } - f << "--tile-uc:" << meta.tileX << ' ' << meta.tileY << ' ' << meta.tileZ << '\n'; - f << "--probe-defocus:" << meta.probeDefocus << '\n'; - f << "-C3:" << meta.C3 << '\n'; - f << "-C5:" << meta.C5 << '\n'; - f << "--probe-semiangle:" << meta.probeSemiangle * 1000 << '\n'; - f << "--detector-angle-step:" << meta.detectorAngleStep * 1000 << '\n'; - //The probe tilt variables are stored internally in units of radians - //but displayed in units of milliradians, a factor of 1000 converts - //between the two. - f << "--probe-xtilt:" << meta.probeXtilt * 1000 << '\n'; - f << "--probe-ytilt:" << meta.probeYtilt * 1000 << '\n'; - f << "--scan-window-x:" << meta.scanWindowXMin << ' ' << meta.scanWindowXMax << '\n'; - f << "--scan-window-y:" << meta.scanWindowYMin << ' ' << meta.scanWindowYMax << '\n'; - f << "--scan-window-xr:" << meta.scanWindowXMin_r << ' ' << meta.scanWindowXMax_r << '\n'; - f << "--scan-window-yr:" << meta.scanWindowYMin_r << ' ' << meta.scanWindowYMax_r << '\n'; - f << "--random-seed:" << meta.randomSeed << '\n'; - if (meta.includeThermalEffects){ - f << "--thermal-effects:1\n"; - } else { - f << "--thermal-effects:0\n"; - } - if (meta.save2DOutput){ - f << "--save-2D-output:" << meta.integrationAngleMin * 1000 << ' ' << meta.integrationAngleMax * 1000<< '\n'; - } - if (meta.save3DOutput){ - f << "--save-3D-output:1\n"; - } else { - f << "--save-3D-output:0\n"; - } - if (meta.save4DOutput){ - f << "--save-4D-output:1\n"; - } else { - f << "--save-4D-output:0\n"; - } - if (meta.saveDPC_CoM){ - f << "--save-4D-output:1\n"; - } else { - f << "--save-4D-output:0\n"; - } - if (meta.savePotentialSlices){ - f << "--save-potential-slices:1\n"; - } else { - f << "--save-potential-slices:0\n"; - } - if (meta.saveRealSpaceCoords){ - f << "--save-real-space-coords:1\n"; - } else { - f << "--save-real-space-coords:0\n"; - } - if (meta.includeOccupancy) { - f << "--occupancy:1\n"; - } else { - f << "--occupancy:0\n"; +bool validateFilename(const std::string str) +{ + std::ifstream f(str); + return f.good(); +} + +bool writeParamFile(Metadata &meta, + const std::string param_filename) +{ + std::cout << "Writing simulation parameters to file " << param_filename << std::endl; + std::ofstream f(param_filename); + if (!f) + throw std::runtime_error("Unable to open file.\n"); + std::string line; + if (meta.algorithm == Algorithm::Multislice) + { + f << "--algorithm:" << 'm' << '\n'; + } + else + { + f << "--algorithm:" << 'p' << '\n'; + } + if (validateFilename(meta.filenameAtoms)) + f << "--input-file:" << meta.filenameAtoms << '\n'; + f << "--output-file:" << meta.filenameOutput << '\n'; + f << "--num-threads:" << meta.numThreads << '\n'; + f << "--pixel-size-x:" << meta.realspacePixelSize[1] << '\n'; + f << "--pixel-size-y:" << meta.realspacePixelSize[0] << '\n'; + f << "--potential-bound:" << meta.potBound << '\n'; + f << "--num-FP:" << meta.numFP << '\n'; + f << "--slice-thickness:" << meta.sliceThickness << '\n'; + f << "--num-slices:" << meta.numSlices << '\n'; + f << "--zstart-slices:" << meta.zStart << '\n'; + f << "--energy:" << meta.E0 / 1000 << '\n'; + f << "--alpha-max:" << meta.alphaBeamMax * 1000 << '\n'; + f << "--batch-size-cpu:" << meta.batchSizeTargetCPU << '\n'; + f << "--probe-step-x:" << meta.probeStepX << '\n'; + f << "--probe-step-y:" << meta.probeStepY << '\n'; + if (meta.userSpecifiedCelldims == true) + { + f << "--cell-dimension:" << meta.cellDim[2] << ' ' << meta.cellDim[1] << ' ' << meta.cellDim[0] << '\n'; + } + else + { + try + { + std::array cell_dims = peekDims_xyz(meta.filenameAtoms); + f << "--cell-dimension:" << cell_dims[2] << ' ' << cell_dims[1] << ' ' << cell_dims[0] << '\n'; } - if (meta.nyquistSampling) { - f << "--nyquist-sampling:1\n"; - } else { - f << "--nyquist-sampling:0\n"; + catch (std::runtime_error) + { } + } + f << "--tile-uc:" << meta.tileX << ' ' << meta.tileY << ' ' << meta.tileZ << '\n'; + f << "--probe-defocus:" << meta.probeDefocus << '\n'; + f << "-C3:" << meta.C3 << '\n'; + f << "-C5:" << meta.C5 << '\n'; + f << "--probe-semiangle:" << meta.probeSemiangle * 1000 << '\n'; + f << "--detector-angle-step:" << meta.detectorAngleStep * 1000 << '\n'; + //The probe tilt variables are stored internally in units of radians + //but displayed in units of milliradians, a factor of 1000 converts + //between the two. + f << "--probe-xtilt:" << meta.probeXtilt * 1000 << '\n'; + f << "--probe-ytilt:" << meta.probeYtilt * 1000 << '\n'; + f << "--scan-window-x:" << meta.scanWindowXMin << ' ' << meta.scanWindowXMax << '\n'; + f << "--scan-window-y:" << meta.scanWindowYMin << ' ' << meta.scanWindowYMax << '\n'; + f << "--scan-window-xr:" << meta.scanWindowXMin_r << ' ' << meta.scanWindowXMax_r << '\n'; + f << "--scan-window-yr:" << meta.scanWindowYMin_r << ' ' << meta.scanWindowYMax_r << '\n'; + f << "--random-seed:" << meta.randomSeed << '\n'; + if (meta.includeThermalEffects) + { + f << "--thermal-effects:1\n"; + } + else + { + f << "--thermal-effects:0\n"; + } + if (meta.save2DOutput) + { + f << "--save-2D-output:" << meta.integrationAngleMin * 1000 << ' ' << meta.integrationAngleMax * 1000 << '\n'; + } + if (meta.save3DOutput) + { + f << "--save-3D-output:1\n"; + } + else + { + f << "--save-3D-output:0\n"; + } + if (meta.save4DOutput) + { + f << "--save-4D-output:1\n"; + } + else + { + f << "--save-4D-output:0\n"; + } + if (meta.saveDPC_CoM) + { + f << "--save-4D-output:1\n"; + } + else + { + f << "--save-4D-output:0\n"; + } + if (meta.savePotentialSlices) + { + f << "--save-potential-slices:1\n"; + } + else + { + f << "--save-potential-slices:0\n"; + } + if (meta.saveRealSpaceCoords) + { + f << "--save-real-space-coords:1\n"; + } + else + { + f << "--save-real-space-coords:0\n"; + } + if (meta.includeOccupancy) + { + f << "--occupancy:1\n"; + } + else + { + f << "--occupancy:0\n"; + } + if (meta.nyquistSampling) + { + f << "--nyquist-sampling:1\n"; + } + else + { + f << "--nyquist-sampling:0\n"; + } #ifdef PRISMATIC_ENABLE_GPU - if (meta.alsoDoCPUWork) { - f << "--also-do-cpu-work:1\n"; - } else { - f << "--also-do-cpu-work:0\n"; - } - f << "--batch-size-gpu:" << meta.batchSizeTargetGPU << '\n'; - f << "--num-gpus:" << meta.numGPUs << '\n'; - f << "--num-streams:" << meta.numStreamsPerGPU << '\n'; + if (meta.alsoDoCPUWork) + { + f << "--also-do-cpu-work:1\n"; + } + else + { + f << "--also-do-cpu-work:0\n"; + } + f << "--batch-size-gpu:" << meta.batchSizeTargetGPU << '\n'; + f << "--num-gpus:" << meta.numGPUs << '\n'; + f << "--num-streams:" << meta.numStreamsPerGPU << '\n'; #endif //PRISMATIC_ENABLE_GPU - return true; - } - - bool parseParamFile(Metadata& meta, - const std::string param_filename){ - std::cout << "Parsing parameter file " << param_filename << std::endl; - std::ifstream f(param_filename); - if (!f)throw std::runtime_error("Unable to open file.\n"); - std::string line; - while (std::getline(f, line)) { - if(!parseParamLine(meta, line)) return false; - } - return true; - } - - - - bool parse_a(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No algorithm provided for -a (syntax is -a algorithm). Choices are (m)ultislice or (p)rism\n"; - return false; - } - std::string algo = std::string((*argv)[1]); - if (algo == "m" | algo == "multislice"){ - meta.algorithm = Prismatic::Algorithm::Multislice; - } else if (algo == "p" | algo == "prism"){ - meta.algorithm = Prismatic::Algorithm::PRISM; - } else { - cout << "Unrecognized algorithm \"" << (*argv)[1] << "\"\n"; - return false; - } - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_A(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No maximum probe angle provided for -A (syntax is -A angle (in mrad))\n"; - return false; - } - if ( (meta.alphaBeamMax = ( (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1])) / 1000 ) == 0){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for maximum probe angle (syntax is -A angle (in mrad))\n"; - return false; - } - argc-=2; - argv[0]+=2; - return true; - }; - - - bool parse_b(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No batch size provided for -b (syntax is -b batch_size)\n"; - return false; - } - if ( (meta.batchSizeTargetCPU = atoi((*argv)[1])) == 0){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for batch size (syntax is -b batch_size)\n"; - return false; - } - meta.batchSizeTargetGPU = meta.batchSizeTargetCPU; - meta.batchSizeGPU = meta.batchSizeTargetGPU; - meta.batchSizeCPU = meta.batchSizeTargetCPU; - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_bc(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No batch size provided for -bc (syntax is -bc batch_size)\n"; - return false; - } - if ( (meta.batchSizeTargetCPU = atoi((*argv)[1])) == 0){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for CPU batch size (syntax is -bc batch_size)\n"; - return false; - } - meta.batchSizeCPU = meta.batchSizeTargetCPU; - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_bg(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No batch size provided for -bg (syntax is -bg batch_size)\n"; - return false; - } - if ( (meta.batchSizeTargetGPU = atoi((*argv)[1])) == 0){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for GPU batch size (syntax is -bg batch_size)\n"; - return false; - } - meta.batchSizeGPU = meta.batchSizeTargetGPU; - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_c(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 4){ - cout << "Insufficient cell dimensions provided (syntax is -c x y z)\n"; - return false; - } - - // the indexing in PRISM stores the cell dimensions as Z, Y, X so we must rearrange the - // order of the inputs which are X, Y, Z - if ( (meta.cellDim[2] = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1])) == 0){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for X cell dimension (syntax is -c x, y, z)\n"; - return false; - } - if ( (meta.cellDim[1] = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[2])) == 0){ - cout << "Invalid value \"" << (*argv)[2] << "\" provided for Y cell dimension (syntax is -c x, y, z)\n"; - return false; - } - if ( (meta.cellDim[0] = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[3])) == 0){ - cout << "Invalid value \"" << (*argv)[3] << "\" provided for Z cell dimension (syntax is -c x, y, z)\n"; - return false; - } - meta.userSpecifiedCelldims = true; - argc-=4; - argv[0]+=4; - return true; - }; + return true; +} - bool parse_C(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No state provided for -C (syntax is -f 0/1)\n"; - return false; - } - meta.alsoDoCPUWork = std::string((*argv)[1]) == "0" ? false : true; - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_d(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No detector angle step provided for -d (syntax is -d detector_step (in mrad))\n"; - return false; - } - if ( (meta.detectorAngleStep = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1]) / 1000) == 0){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for potential bound (syntax is -d detector_step (in mrad)\n"; - return false; - } - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_streaming_mode(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No state provided for -C (syntax is -f 0/1)\n"; +bool parseParamFile(Metadata &meta, + const std::string param_filename) +{ + std::cout << "Parsing parameter file " << param_filename << std::endl; + std::ifstream f(param_filename); + if (!f) + throw std::runtime_error("Unable to open file.\n"); + std::string line; + while (std::getline(f, line)) + { + if (!parseParamLine(meta, line)) return false; - } - meta.transferMode = std::string((*argv)[1]) == "0" ? Prismatic::StreamingMode::SingleXfer : Prismatic::StreamingMode::Stream; - argc-=2; - argv[0]+=2; - return true; - }; + } + return true; +} - bool parse_h(Metadata& meta, - int& argc, const char*** argv){ - printHelp(); +bool parse_a(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No algorithm provided for -a (syntax is -a algorithm). Choices are (m)ultislice or (p)rism\n"; return false; - }; - - bool parse_i(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No filename provided for -i (syntax is -i filename)\n"; - return false; - } - meta.filenameAtoms = std::string((*argv)[1]); - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_pf(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No filename provided for -pf (syntax is -pf filename)\n"; - return false; - } - if (!parseParamFile(meta, std::string((*argv)[1]))) return false; - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_f(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No interpolation factor provided for -f (syntax is -f interpolation_factor)\n"; - return false; - } - if ( (meta.interpolationFactorX = atoi((*argv)[1])) == 0){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for PRISM interpolation factors (syntax is -f interpolation_factor)\n"; - return false; - } - meta.interpolationFactorY = meta.interpolationFactorX; - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_fx(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No interpolation factor provided for -fx (syntax is -fx interpolation_factor_x)\n"; - return false; - } - if ( (meta.interpolationFactorX = atoi((*argv)[1])) == 0){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for PRISM interpolation factor (syntax is -fx interpolation_factor_x)\n"; - return false; - } - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_fy(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No interpolation factor provided for -fy (syntax is -fy interpolation_factor_y)\n"; - return false; - } - if ( (meta.interpolationFactorY = atoi((*argv)[1])) == 0){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for PRISM interpolation factor (syntax is -fy interpolation_factor_y)\n"; - return false; - } - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_j(Metadata& meta, - int& argc, const char*** argv){ - - if (argc < 2){ - cout << "No number of threads provided (syntax is -j numThreads)\n"; - return false; - } - if ( (meta.numThreads = atoi((*argv)[1])) == 0){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for number of threads (syntax is -j numThreads)\n"; - return false; - } - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_E(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No energy provided for -E (syntax is -E energy (in keV))\n"; - return false; - } - if ( (meta.E0 = 1000 * (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1])) == 0){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for energy (syntax is -E energy (in keV))\n"; - return false; - } - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_F(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No number of frozen phonon configurations provided for -F (syntax is -F #)\n"; - return false; - } - if ( (meta.numFP = atoi((*argv)[1])) == 0){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for number of frozen phonon configurations (syntax is -F #)\n"; - return false; - } - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_g(Metadata& meta, - int& argc, const char*** argv){ - - if (argc < 2){ - cout << "No number of GPUs provided (syntax is -g numGPUs)\n"; - return false; - } - if ( (string((*argv)[1]) == "0") ){ - meta.numGPUs = 0; - argc-=2; - argv[0]+=2; - return true; - } - if ( (meta.numGPUs = atoi((*argv)[1])) == 0){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for number of GPUs (syntax is -g numGPUs)\n"; - return false; - } - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_s(Metadata& meta, - int& argc, const char*** argv){ - - if (argc < 2){ - cout << "No slice thickness provided (syntax is -s slice_thickness (in Angstroms))\n"; - return false; - } - if ( (meta.sliceThickness = atof((*argv)[1])) == 0){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for slice_thickness (syntax is -s slice_thickness (in Angstroms))\n"; - return false; - } - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_ns(Metadata& meta, - int& argc, const char*** argv){ - - if (argc < 2){ - cout << "No number of slices provided for intermediate output (syntax is -ns num_slices)\n"; - return false; - } - if ( (meta.numSlices = atoi((*argv)[1])) < 0){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for number of slices (syntax is -ns num_slices)\n"; - return false; - } - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_zs(Metadata& meta, - int& argc, const char*** argv){ - - if (argc < 2){ - cout << "No value for beginning intermediate output depth provided (syntax is -zs z_start (in Angstroms))\n"; - return false; - } - if ( (meta.zStart = atoi((*argv)[1])) < 0){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for beginning intermediate output depth (syntax is -zs z_start (in Angstroms))\n"; - return false; - } - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_S(Metadata& meta, - int& argc, const char*** argv){ - - if (argc < 2){ - cout << "No number of CUDA streams per GPU provided (syntax is -S num_streams)\n"; - return false; - } - if ( (meta.numStreamsPerGPU = atoi((*argv)[1])) == 0){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for number of streams (syntax is -S num_streams)\n"; - return false; - } - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_t(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 4){ - cout << "Insufficient arguments provided for unit cell tiling (syntax is --tile-uc x y z)\n"; - return false; - } - - // the indexing in PRISM stores the cell dimensions as Z, Y, X so we must rearrange the - // order of the inputs which are X, Y, Z - if ( (meta.tileX = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1])) == 0){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for unit cell tiling in X (syntax is --tile-uc x y z)\n"; - return false; - } - if ( (meta.tileY = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[2])) == 0){ - cout << "Invalid value \"" << (*argv)[2] << "\" provided for unit cell tiling in Y (syntax is --tile-uc x y z)\n"; - return false; - } - if ( (meta.tileZ = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[3])) == 0){ - cout << "Invalid value \"" << (*argv)[3] << "\" provided for unit cell tiling in Z (syntax is --tile-uc x y z)\n"; - return false; - } - - argc-=4; - argv[0]+=4; - return true; - - - }; - - bool parse_o(Metadata& meta, - int& argc, const char*** argv){ - - if (argc < 2){ - cout << "No filename provided for -o (syntax is -o filename)\n"; - return false; - } - meta.filenameOutput = std::string((*argv)[1]); - //cout <<"meta.filenameAtoms = " << meta.filenameAtoms << endl; - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_of(Metadata& meta, - int& argc, const char*** argv){ - - if (argc < 2){ - cout << "No folder provided for -of (syntax is -of /path/)\n"; - return false; - } - meta.outputFolder = std::string((*argv)[1]); - //cout <<"meta.filenameAtoms = " << meta.filenameAtoms << endl; - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_p(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No pixel size provided for -p (syntax is -p pixel_size)\n"; - return false; - } - if ( (meta.realspacePixelSize[0] = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1])) == 0){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for pixel size (syntax is -p pixel_size)\n"; - return false; - } - meta.realspacePixelSize[1] = meta.realspacePixelSize[0]; - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_px(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No pixel size provided for -px (syntax is -px pixel_size)\n"; - return false; - } - if ( (meta.realspacePixelSize[1] = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1])) == 0){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for X pixel size (syntax is -px pixel_size)\n"; - return false; - } - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_py(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No pixel size provided for -py (syntax is -py pixel_size)\n"; - return false; - } - if ( (meta.realspacePixelSize[0] = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1])) == 0){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for Y pixel size (syntax is -py pixel_size)\n"; - return false; - } - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_P(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No bounding potential radius provided for -P (syntax is -P potential_bound (in Angstroms))\n"; - return false; - } - if ( (meta.potBound = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1])) == 0){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for potential bound (syntax is -P potential_bound (in Angstroms))\n"; - return false; - } - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_r(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No probe step provided for -r (syntax is -r probe_step (in Angstroms))\n"; - return false; - } - if ( (meta.probeStepX = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1])) == 0){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for probe_step (syntax is -r probe_step (in Angstroms))\n"; - return false; - } - meta.probeStepY = meta.probeStepX; - argc-=2; - argv[0]+=2; - return true; - }; - - - bool parse_rx(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No probe step provided for -rx (syntax is -rx probe_step (in Angstroms))\n"; - return false; - } - if ( (meta.probeStepX = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1])) == 0){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for probe_step (syntax is -rx probe_step (in Angstroms))\n"; - return false; - } - meta.probeStepY = meta.probeStepX; - argc-=2; - argv[0]+=2; - return true; - }; - - - - bool parse_ry(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No probe step provided for -ry (syntax is -ry probe_step (in Angstroms))\n"; - return false; - } - if ( (meta.probeStepY = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1])) == 0){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for probe_step (syntax is -ry probe_step (in Angstroms))\n"; - return false; - } - argc-=2; - argv[0]+=2; - return true; - }; - - - bool parse_rs(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No random seed provided for -rs (syntax is -rs integer)\n"; - return false; - } - if ( ((meta.randomSeed = atoi((*argv)[1])) == 0) & std::string(((*argv)[1]))!="0"){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for random seed (syntax is -rs integer)\n"; - return false; - } - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_tx(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No probe tilt provided for -tx (syntax is -tx probe_tilt)\n"; - return false; - } - if ( ((meta.probeXtilt = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1]) / 1000) == 0) & (std::string((*argv)[1]) != "0")){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for -tx (syntax is -tx probe_tilt\n"; - return false; - } - argc-=2; - argv[0]+=2; - return true; - }; + } + std::string algo = std::string((*argv)[1]); + if (algo == "m" | algo == "multislice") + { + meta.algorithm = Prismatic::Algorithm::Multislice; + } + else if (algo == "p" | algo == "prism") + { + meta.algorithm = Prismatic::Algorithm::PRISM; + } + else + { + cout << "Unrecognized algorithm \"" << (*argv)[1] << "\"\n"; + return false; + } + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_A(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No maximum probe angle provided for -A (syntax is -A angle (in mrad))\n"; + return false; + } + if ((meta.alphaBeamMax = ((PRISMATIC_FLOAT_PRECISION)atof((*argv)[1])) / 1000) == 0) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for maximum probe angle (syntax is -A angle (in mrad))\n"; + return false; + } + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_b(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No batch size provided for -b (syntax is -b batch_size)\n"; + return false; + } + if ((meta.batchSizeTargetCPU = atoi((*argv)[1])) == 0) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for batch size (syntax is -b batch_size)\n"; + return false; + } + meta.batchSizeTargetGPU = meta.batchSizeTargetCPU; + meta.batchSizeGPU = meta.batchSizeTargetGPU; + meta.batchSizeCPU = meta.batchSizeTargetCPU; + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_bc(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No batch size provided for -bc (syntax is -bc batch_size)\n"; + return false; + } + if ((meta.batchSizeTargetCPU = atoi((*argv)[1])) == 0) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for CPU batch size (syntax is -bc batch_size)\n"; + return false; + } + meta.batchSizeCPU = meta.batchSizeTargetCPU; + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_bg(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No batch size provided for -bg (syntax is -bg batch_size)\n"; + return false; + } + if ((meta.batchSizeTargetGPU = atoi((*argv)[1])) == 0) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for GPU batch size (syntax is -bg batch_size)\n"; + return false; + } + meta.batchSizeGPU = meta.batchSizeTargetGPU; + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_c(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 4) + { + cout << "Insufficient cell dimensions provided (syntax is -c x y z)\n"; + return false; + } - bool parse_ty(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No probe tilt provided for -ty (syntax is -ty probe_tilt)\n"; - return false; - } - if ( ((meta.probeYtilt = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1]) / 1000) == 0) & (std::string((*argv)[1]) != "0")){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for -ty (syntax is -ty probe_tilt\n"; - return false; - } - argc-=2; - argv[0]+=2; + // the indexing in PRISM stores the cell dimensions as Z, Y, X so we must rearrange the + // order of the inputs which are X, Y, Z + if ((meta.cellDim[2] = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1])) == 0) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for X cell dimension (syntax is -c x, y, z)\n"; + return false; + } + if ((meta.cellDim[1] = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[2])) == 0) + { + cout << "Invalid value \"" << (*argv)[2] << "\" provided for Y cell dimension (syntax is -c x, y, z)\n"; + return false; + } + if ((meta.cellDim[0] = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[3])) == 0) + { + cout << "Invalid value \"" << (*argv)[3] << "\" provided for Z cell dimension (syntax is -c x, y, z)\n"; + return false; + } + meta.userSpecifiedCelldims = true; + argc -= 4; + argv[0] += 4; + return true; +}; + +bool parse_C(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No state provided for -C (syntax is -f 0/1)\n"; + return false; + } + meta.alsoDoCPUWork = std::string((*argv)[1]) == "0" ? false : true; + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_d(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No detector angle step provided for -d (syntax is -d detector_step (in mrad))\n"; + return false; + } + if ((meta.detectorAngleStep = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1]) / 1000) == 0) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for potential bound (syntax is -d detector_step (in mrad)\n"; + return false; + } + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_streaming_mode(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No state provided for -C (syntax is -f 0/1)\n"; + return false; + } + meta.transferMode = std::string((*argv)[1]) == "0" ? Prismatic::StreamingMode::SingleXfer : Prismatic::StreamingMode::Stream; + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_h(Metadata &meta, + int &argc, const char ***argv) +{ + printHelp(); + return false; +}; + +bool parse_i(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No filename provided for -i (syntax is -i filename)\n"; + return false; + } + meta.filenameAtoms = std::string((*argv)[1]); + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_pf(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No filename provided for -pf (syntax is -pf filename)\n"; + return false; + } + if (!parseParamFile(meta, std::string((*argv)[1]))) + return false; + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_f(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No interpolation factor provided for -f (syntax is -f interpolation_factor)\n"; + return false; + } + if ((meta.interpolationFactorX = atoi((*argv)[1])) == 0) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for PRISM interpolation factors (syntax is -f interpolation_factor)\n"; + return false; + } + meta.interpolationFactorY = meta.interpolationFactorX; + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_fx(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No interpolation factor provided for -fx (syntax is -fx interpolation_factor_x)\n"; + return false; + } + if ((meta.interpolationFactorX = atoi((*argv)[1])) == 0) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for PRISM interpolation factor (syntax is -fx interpolation_factor_x)\n"; + return false; + } + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_fy(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No interpolation factor provided for -fy (syntax is -fy interpolation_factor_y)\n"; + return false; + } + if ((meta.interpolationFactorY = atoi((*argv)[1])) == 0) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for PRISM interpolation factor (syntax is -fy interpolation_factor_y)\n"; + return false; + } + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_j(Metadata &meta, + int &argc, const char ***argv) +{ + + if (argc < 2) + { + cout << "No number of threads provided (syntax is -j numThreads)\n"; + return false; + } + if ((meta.numThreads = atoi((*argv)[1])) == 0) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for number of threads (syntax is -j numThreads)\n"; + return false; + } + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_E(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No energy provided for -E (syntax is -E energy (in keV))\n"; + return false; + } + if ((meta.E0 = 1000 * (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1])) == 0) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for energy (syntax is -E energy (in keV))\n"; + return false; + } + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_F(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No number of frozen phonon configurations provided for -F (syntax is -F #)\n"; + return false; + } + if ((meta.numFP = atoi((*argv)[1])) == 0) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for number of frozen phonon configurations (syntax is -F #)\n"; + return false; + } + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_g(Metadata &meta, + int &argc, const char ***argv) +{ + + if (argc < 2) + { + cout << "No number of GPUs provided (syntax is -g numGPUs)\n"; + return false; + } + if ((string((*argv)[1]) == "0")) + { + meta.numGPUs = 0; + argc -= 2; + argv[0] += 2; return true; - }; + } + if ((meta.numGPUs = atoi((*argv)[1])) == 0) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for number of GPUs (syntax is -g numGPUs)\n"; + return false; + } + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_s(Metadata &meta, + int &argc, const char ***argv) +{ + + if (argc < 2) + { + cout << "No slice thickness provided (syntax is -s slice_thickness (in Angstroms))\n"; + return false; + } + if ((meta.sliceThickness = atof((*argv)[1])) == 0) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for slice_thickness (syntax is -s slice_thickness (in Angstroms))\n"; + return false; + } + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_ns(Metadata &meta, + int &argc, const char ***argv) +{ + + if (argc < 2) + { + cout << "No number of slices provided for intermediate output (syntax is -ns num_slices)\n"; + return false; + } + if ((meta.numSlices = atoi((*argv)[1])) < 0) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for number of slices (syntax is -ns num_slices)\n"; + return false; + } + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_zs(Metadata &meta, + int &argc, const char ***argv) +{ + + if (argc < 2) + { + cout << "No value for beginning intermediate output depth provided (syntax is -zs z_start (in Angstroms))\n"; + return false; + } + if ((meta.zStart = atoi((*argv)[1])) < 0) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for beginning intermediate output depth (syntax is -zs z_start (in Angstroms))\n"; + return false; + } + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_S(Metadata &meta, + int &argc, const char ***argv) +{ + + if (argc < 2) + { + cout << "No number of CUDA streams per GPU provided (syntax is -S num_streams)\n"; + return false; + } + if ((meta.numStreamsPerGPU = atoi((*argv)[1])) == 0) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for number of streams (syntax is -S num_streams)\n"; + return false; + } + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_t(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 4) + { + cout << "Insufficient arguments provided for unit cell tiling (syntax is --tile-uc x y z)\n"; + return false; + } - bool parse_df(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No defocus value provided for -df (syntax is -df defocus_value (in Angstroms))\n"; - return false; - } - if ( ((meta.probeDefocus = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1])) == 0) & (std::string((*argv)[1]) != "0")){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for -df (syntax is -df defocus_value (in Angstroms)\n"; - return false; - } - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_C3(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No C3 value provided for -C3 (syntax is -C3 value (in Angstroms))\n"; - return false; - } - if ( ((meta.C3 = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1])) == 0) & (std::string((*argv)[1]) != "0")){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for -C3 (syntax is -C3 value (in Angstroms)\n"; - return false; - } - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_C5(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No C5 value provided for -C5 (syntax is -C5 value (in Angstroms))\n"; - return false; - } - if ( ((meta.C5 = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1])) == 0) & (std::string((*argv)[1]) != "0")){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for -C5 (syntax is -C5 value (in Angstroms)\n"; - return false; - } - argc-=2; - argv[0]+=2; - return true; - }; - - - bool parse_sa(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No probe semiangle provided for -sa (syntax is -sa probe_semiangle in mrads)\n"; - return false; - } - if ( (meta.probeSemiangle = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1]) / 1000) == 0){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for -sa (syntax is -sa probe_semiangle in mrads)\n"; - return false; - } - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_wx(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 3){ - cout << "Invalid window provided for -wx (syntax is -wx min max (in fractional coordinates))\n"; - return false; - } - PRISMATIC_FLOAT_PRECISION minval, maxval; - minval = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1]); - maxval = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[2]); - - if ( (minval == 0) & (std::string((*argv)[1]) != "0")){ - cout << "Invalid lower bound \"" << (*argv)[1] << "\" provided for scan window X (syntax is -wx min max (in fractional coordinates))\n"; - return false; - } - if ( (maxval == 0) & (std::string((*argv)[2]) != "0")){ - cout << "Invalid upper bound \"" << (*argv)[2] << "\" provided for scan window X (syntax is -wx min max (in fractional coordinates))\n"; - return false; - } - if (maxval < minval){ - cout << "The provided lower bound(" << minval << ") for the X scan is greater than the maximum(" << maxval <<")." << endl; - return false; - } - meta.scanWindowXMin = minval; - meta.scanWindowXMax = maxval; - argc-=3; - argv[0]+=3; - return true; - }; - - bool parse_wy(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 3){ - cout << "Invalid window provided for -wy (syntax is -wy min max (in fractional coordinates))\n"; - return false; - } - PRISMATIC_FLOAT_PRECISION minval, maxval; - minval = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1]); - maxval = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[2]); - if ( (minval == 0) & (std::string((*argv)[1]) != "0")){ - cout << "Invalid lower bound \"" << (*argv)[1] << "\" provided for scan window y (syntax is -wx min max (in fractional coordinates))\n"; - return false; - } - if ( (maxval == 0) & (std::string((*argv)[2]) != "0")){ - cout << "Invalid upper bound \"" << (*argv)[2] << "\" provided for scan window y (syntax is -wy min max (in fractional coordinates))\n"; - return false; - } - if (maxval < minval){ - cout << "The provided lower bound(" << minval << ") for the X scan is greater than the maximum(" << maxval <<")." << endl; - return false; - } - meta.scanWindowYMin = minval; - meta.scanWindowYMax = maxval; - argc-=3; - argv[0]+=3; - return true; - }; - - bool parse_wxr(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 3){ - cout << "Invalid window provided for -wxr (syntax is -wxr min max (in Angstroms))\n"; - return false; - } - PRISMATIC_FLOAT_PRECISION minval, maxval; - minval = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1]); - maxval = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[2]); - - if ( (minval == 0) & (std::string((*argv)[1]) != "0")){ - cout << "Invalid lower bound \"" << (*argv)[1] << "\" provided for scan window Xr (syntax is -wxr min max (in Angstroms))\n"; - return false; - } - if ( (maxval == 0) & (std::string((*argv)[2]) != "0")){ - cout << "Invalid upper bound \"" << (*argv)[2] << "\" provided for scan window Xr (syntax is -wxr min max (in Angstroms))\n"; - return false; - } - if (maxval < minval){ - cout << "The provided lower bound(" << minval << ") for the X scan in real space is greater than the maximum(" << maxval <<")." << endl; - return false; - } - meta.scanWindowXMin_r = minval; - meta.scanWindowXMax_r = maxval; - if(maxval){ - meta.realSpaceWindow_x = true; - } - argc-=3; - argv[0]+=3; - return true; - }; - - bool parse_wyr(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 3){ - cout << "Invalid window provided for -wyr (syntax is -wyr min max (in Angstroms))\n"; - return false; - } - PRISMATIC_FLOAT_PRECISION minval, maxval; - minval = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1]); - maxval = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[2]); - - if ( (minval == 0) & (std::string((*argv)[1]) != "0")){ - cout << "Invalid lower bound \"" << (*argv)[1] << "\" provided for scan window Yr (syntax is -wyr min max (in Angstroms))\n"; - return false; - } - if ( (maxval == 0) & (std::string((*argv)[2]) != "0")){ - cout << "Invalid upper bound \"" << (*argv)[2] << "\" provided for scan window Yr (syntax is -wyr min max (in Angstroms))\n"; - return false; - } - if (maxval < minval){ - cout << "The provided lower bound(" << minval << ") for the Y scan in real space is greater than the maximum(" << maxval <<")." << endl; - return false; - } - meta.scanWindowYMin_r = minval; - meta.scanWindowYMax_r = maxval; - if(maxval){ - meta.realSpaceWindow_y = true; - } - argc-=3; - argv[0]+=3; - return true; - }; - - bool parse_te(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No value provided for -te (syntax is -te bool)\n"; - return false; - } - meta.includeThermalEffects = std::string((*argv)[1]) == "0" ? false : true; - argc-=2; - argv[0]+=2; - return true; - }; - - bool parse_oc(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No value provided for -oc (syntax is -oc bool)\n"; - return false; - } - meta.includeOccupancy = std::string((*argv)[1]) == "0" ? false : true; - argc-=2; - argv[0]+=2; - return true; - }; + // the indexing in PRISM stores the cell dimensions as Z, Y, X so we must rearrange the + // order of the inputs which are X, Y, Z + if ((meta.tileX = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1])) == 0) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for unit cell tiling in X (syntax is --tile-uc x y z)\n"; + return false; + } + if ((meta.tileY = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[2])) == 0) + { + cout << "Invalid value \"" << (*argv)[2] << "\" provided for unit cell tiling in Y (syntax is --tile-uc x y z)\n"; + return false; + } + if ((meta.tileZ = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[3])) == 0) + { + cout << "Invalid value \"" << (*argv)[3] << "\" provided for unit cell tiling in Z (syntax is --tile-uc x y z)\n"; + return false; + } - bool parse_2D(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 3){ - cout << "Not enough arguments for -2D (syntax is -2D ang_min ang_max)\n"; - return false; - } - meta.save2DOutput = true; - if ( (string((*argv)[1]) != "0") & ((meta.integrationAngleMin = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1]) / 1000) == 0)){ - cout << "Invalid value \"" << (*argv)[1] << "\" provided for minimum integration angle (syntax is -2D ang_min ang_max (in mrad)\n"; - return false; - } - if ( (meta.integrationAngleMax = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[2]) / 1000) == 0){ - cout << "Invalid value \"" << (*argv)[2] << "\" provided for maximum integration angle (syntax is -2D ang_min ang_max (in mrad))\n"; - return false; - } - argc-=3; - argv[0]+=3; - return true; - }; + argc -= 4; + argv[0] += 4; + return true; +}; - bool parse_3D(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No value provided for -3D (syntax is -3D bool)\n"; - return false; - } - meta.save3DOutput = std::string((*argv)[1]) == "0" ? false : true; - argc-=2; - argv[0]+=2; - return true; - }; +bool parse_o(Metadata &meta, + int &argc, const char ***argv) +{ - bool parse_4D(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No value provided for -4D (syntax is -4D bool)\n"; - return false; - } - meta.save4DOutput = std::string((*argv)[1]) == "0" ? false : true; - argc-=2; - argv[0]+=2; - return true; - }; + if (argc < 2) + { + cout << "No filename provided for -o (syntax is -o filename)\n"; + return false; + } + meta.filenameOutput = std::string((*argv)[1]); + //cout <<"meta.filenameAtoms = " << meta.filenameAtoms << endl; + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_of(Metadata &meta, + int &argc, const char ***argv) +{ + + if (argc < 2) + { + cout << "No folder provided for -of (syntax is -of /path/)\n"; + return false; + } + meta.outputFolder = std::string((*argv)[1]); + //cout <<"meta.filenameAtoms = " << meta.filenameAtoms << endl; + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_p(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No pixel size provided for -p (syntax is -p pixel_size)\n"; + return false; + } + if ((meta.realspacePixelSize[0] = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1])) == 0) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for pixel size (syntax is -p pixel_size)\n"; + return false; + } + meta.realspacePixelSize[1] = meta.realspacePixelSize[0]; + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_px(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No pixel size provided for -px (syntax is -px pixel_size)\n"; + return false; + } + if ((meta.realspacePixelSize[1] = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1])) == 0) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for X pixel size (syntax is -px pixel_size)\n"; + return false; + } + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_py(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No pixel size provided for -py (syntax is -py pixel_size)\n"; + return false; + } + if ((meta.realspacePixelSize[0] = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1])) == 0) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for Y pixel size (syntax is -py pixel_size)\n"; + return false; + } + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_P(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No bounding potential radius provided for -P (syntax is -P potential_bound (in Angstroms))\n"; + return false; + } + if ((meta.potBound = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1])) == 0) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for potential bound (syntax is -P potential_bound (in Angstroms))\n"; + return false; + } + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_r(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No probe step provided for -r (syntax is -r probe_step (in Angstroms))\n"; + return false; + } + if ((meta.probeStepX = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1])) == 0) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for probe_step (syntax is -r probe_step (in Angstroms))\n"; + return false; + } + meta.probeStepY = meta.probeStepX; + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_rx(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No probe step provided for -rx (syntax is -rx probe_step (in Angstroms))\n"; + return false; + } + if ((meta.probeStepX = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1])) == 0) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for probe_step (syntax is -rx probe_step (in Angstroms))\n"; + return false; + } + meta.probeStepY = meta.probeStepX; + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_ry(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No probe step provided for -ry (syntax is -ry probe_step (in Angstroms))\n"; + return false; + } + if ((meta.probeStepY = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1])) == 0) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for probe_step (syntax is -ry probe_step (in Angstroms))\n"; + return false; + } + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_rs(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No random seed provided for -rs (syntax is -rs integer)\n"; + return false; + } + if (((meta.randomSeed = atoi((*argv)[1])) == 0) & std::string(((*argv)[1])) != "0") + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for random seed (syntax is -rs integer)\n"; + return false; + } + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_tx(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No probe tilt provided for -tx (syntax is -tx probe_tilt)\n"; + return false; + } + if (((meta.probeXtilt = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1]) / 1000) == 0) & (std::string((*argv)[1]) != "0")) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for -tx (syntax is -tx probe_tilt\n"; + return false; + } + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_ty(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No probe tilt provided for -ty (syntax is -ty probe_tilt)\n"; + return false; + } + if (((meta.probeYtilt = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1]) / 1000) == 0) & (std::string((*argv)[1]) != "0")) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for -ty (syntax is -ty probe_tilt\n"; + return false; + } + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_df(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No defocus value provided for -df (syntax is -df defocus_value (in Angstroms))\n"; + return false; + } + if (((meta.probeDefocus = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1])) == 0) & (std::string((*argv)[1]) != "0")) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for -df (syntax is -df defocus_value (in Angstroms)\n"; + return false; + } + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_C3(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No C3 value provided for -C3 (syntax is -C3 value (in Angstroms))\n"; + return false; + } + if (((meta.C3 = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1])) == 0) & (std::string((*argv)[1]) != "0")) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for -C3 (syntax is -C3 value (in Angstroms)\n"; + return false; + } + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_C5(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No C5 value provided for -C5 (syntax is -C5 value (in Angstroms))\n"; + return false; + } + if (((meta.C5 = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1])) == 0) & (std::string((*argv)[1]) != "0")) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for -C5 (syntax is -C5 value (in Angstroms)\n"; + return false; + } + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_sa(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No probe semiangle provided for -sa (syntax is -sa probe_semiangle in mrads)\n"; + return false; + } + if ((meta.probeSemiangle = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1]) / 1000) == 0) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for -sa (syntax is -sa probe_semiangle in mrads)\n"; + return false; + } + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_wx(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 3) + { + cout << "Invalid window provided for -wx (syntax is -wx min max (in fractional coordinates))\n"; + return false; + } + PRISMATIC_FLOAT_PRECISION minval, maxval; + minval = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1]); + maxval = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[2]); - bool parse_dpc(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No value provided for -DPC (syntax is -DPC bool)\n"; - return false; - } - meta.saveDPC_CoM = std::string((*argv)[1]) == "0" ? false : true; - argc-=2; - argv[0]+=2; - return true; - }; + if ((minval == 0) & (std::string((*argv)[1]) != "0")) + { + cout << "Invalid lower bound \"" << (*argv)[1] << "\" provided for scan window X (syntax is -wx min max (in fractional coordinates))\n"; + return false; + } + if ((maxval == 0) & (std::string((*argv)[2]) != "0")) + { + cout << "Invalid upper bound \"" << (*argv)[2] << "\" provided for scan window X (syntax is -wx min max (in fractional coordinates))\n"; + return false; + } + if (maxval < minval) + { + cout << "The provided lower bound(" << minval << ") for the X scan is greater than the maximum(" << maxval << ")." << endl; + return false; + } + meta.scanWindowXMin = minval; + meta.scanWindowXMax = maxval; + argc -= 3; + argv[0] += 3; + return true; +}; + +bool parse_wy(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 3) + { + cout << "Invalid window provided for -wy (syntax is -wy min max (in fractional coordinates))\n"; + return false; + } + PRISMATIC_FLOAT_PRECISION minval, maxval; + minval = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1]); + maxval = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[2]); + if ((minval == 0) & (std::string((*argv)[1]) != "0")) + { + cout << "Invalid lower bound \"" << (*argv)[1] << "\" provided for scan window y (syntax is -wx min max (in fractional coordinates))\n"; + return false; + } + if ((maxval == 0) & (std::string((*argv)[2]) != "0")) + { + cout << "Invalid upper bound \"" << (*argv)[2] << "\" provided for scan window y (syntax is -wy min max (in fractional coordinates))\n"; + return false; + } + if (maxval < minval) + { + cout << "The provided lower bound(" << minval << ") for the X scan is greater than the maximum(" << maxval << ")." << endl; + return false; + } + meta.scanWindowYMin = minval; + meta.scanWindowYMax = maxval; + argc -= 3; + argv[0] += 3; + return true; +}; + +bool parse_wxr(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 3) + { + cout << "Invalid window provided for -wxr (syntax is -wxr min max (in Angstroms))\n"; + return false; + } + PRISMATIC_FLOAT_PRECISION minval, maxval; + minval = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1]); + maxval = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[2]); - bool parse_nqs(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No value provided for -nqs (syntax is -nqs bool)\n"; - return false; - } - meta.nyquistSampling = std::string((*argv)[1]) == "0" ? false : true; - argc-=2; - argv[0]+=2; - return true; - }; + if ((minval == 0) & (std::string((*argv)[1]) != "0")) + { + cout << "Invalid lower bound \"" << (*argv)[1] << "\" provided for scan window Xr (syntax is -wxr min max (in Angstroms))\n"; + return false; + } + if ((maxval == 0) & (std::string((*argv)[2]) != "0")) + { + cout << "Invalid upper bound \"" << (*argv)[2] << "\" provided for scan window Xr (syntax is -wxr min max (in Angstroms))\n"; + return false; + } + if (maxval < minval) + { + cout << "The provided lower bound(" << minval << ") for the X scan in real space is greater than the maximum(" << maxval << ")." << endl; + return false; + } + meta.scanWindowXMin_r = minval; + meta.scanWindowXMax_r = maxval; + if (maxval) + { + meta.realSpaceWindow_x = true; + } + argc -= 3; + argv[0] += 3; + return true; +}; + +bool parse_wyr(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 3) + { + cout << "Invalid window provided for -wyr (syntax is -wyr min max (in Angstroms))\n"; + return false; + } + PRISMATIC_FLOAT_PRECISION minval, maxval; + minval = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1]); + maxval = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[2]); - bool parse_rsc(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No value provided for -rsc (syntax is -rsc bool)\n"; - return false; - } - meta.saveRealSpaceCoords = std::string((*argv)[1]) == "0" ? false : true; - argc-=2; - argv[0]+=2; - return true; - }; + if ((minval == 0) & (std::string((*argv)[1]) != "0")) + { + cout << "Invalid lower bound \"" << (*argv)[1] << "\" provided for scan window Yr (syntax is -wyr min max (in Angstroms))\n"; + return false; + } + if ((maxval == 0) & (std::string((*argv)[2]) != "0")) + { + cout << "Invalid upper bound \"" << (*argv)[2] << "\" provided for scan window Yr (syntax is -wyr min max (in Angstroms))\n"; + return false; + } + if (maxval < minval) + { + cout << "The provided lower bound(" << minval << ") for the Y scan in real space is greater than the maximum(" << maxval << ")." << endl; + return false; + } + meta.scanWindowYMin_r = minval; + meta.scanWindowYMax_r = maxval; + if (maxval) + { + meta.realSpaceWindow_y = true; + } + argc -= 3; + argv[0] += 3; + return true; +}; + +bool parse_te(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No value provided for -te (syntax is -te bool)\n"; + return false; + } + meta.includeThermalEffects = std::string((*argv)[1]) == "0" ? false : true; + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_oc(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No value provided for -oc (syntax is -oc bool)\n"; + return false; + } + meta.includeOccupancy = std::string((*argv)[1]) == "0" ? false : true; + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_2D(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 3) + { + cout << "Not enough arguments for -2D (syntax is -2D ang_min ang_max)\n"; + return false; + } + meta.save2DOutput = true; + if ((string((*argv)[1]) != "0") & ((meta.integrationAngleMin = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[1]) / 1000) == 0)) + { + cout << "Invalid value \"" << (*argv)[1] << "\" provided for minimum integration angle (syntax is -2D ang_min ang_max (in mrad)\n"; + return false; + } + if ((meta.integrationAngleMax = (PRISMATIC_FLOAT_PRECISION)atof((*argv)[2]) / 1000) == 0) + { + cout << "Invalid value \"" << (*argv)[2] << "\" provided for maximum integration angle (syntax is -2D ang_min ang_max (in mrad))\n"; + return false; + } + argc -= 3; + argv[0] += 3; + return true; +}; + +bool parse_3D(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No value provided for -3D (syntax is -3D bool)\n"; + return false; + } + meta.save3DOutput = std::string((*argv)[1]) == "0" ? false : true; + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_4D(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No value provided for -4D (syntax is -4D bool)\n"; + return false; + } + meta.save4DOutput = std::string((*argv)[1]) == "0" ? false : true; + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_dpc(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No value provided for -DPC (syntax is -DPC bool)\n"; + return false; + } + meta.saveDPC_CoM = std::string((*argv)[1]) == "0" ? false : true; + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_nqs(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No value provided for -nqs (syntax is -nqs bool)\n"; + return false; + } + meta.nyquistSampling = std::string((*argv)[1]) == "0" ? false : true; + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_rsc(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No value provided for -rsc (syntax is -rsc bool)\n"; + return false; + } + meta.saveRealSpaceCoords = std::string((*argv)[1]) == "0" ? false : true; + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parse_ps(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc < 2) + { + cout << "No value provided for -ps (syntax is -ps bool)\n"; + return false; + } + meta.savePotentialSlices = std::string((*argv)[1]) == "0" ? false : true; + argc -= 2; + argv[0] += 2; + return true; +}; + +bool parseInputs(Metadata &meta, + int &argc, const char ***argv) +{ + if (argc == 1) + return true; // case of no inputs to parse + --argc; + ++(argv[0]); + do + { + if (argc == 0) + return true; // successfully parsed all inputs + } while (parseInput(meta, argc, argv)); + return false; +} - bool parse_ps(Metadata& meta, - int& argc, const char*** argv){ - if (argc < 2){ - cout << "No value provided for -ps (syntax is -ps bool)\n"; - return false; - } - meta.savePotentialSlices = std::string((*argv)[1]) == "0" ? false : true; - argc-=2; - argv[0]+=2; - return true; - }; - - bool parseInputs(Metadata &meta, - int &argc, const char ***argv) { - if (argc==1)return true; // case of no inputs to parse - --argc;++(argv[0]); - do { - if (argc==0) return true; // successfully parsed all inputs - } while (parseInput(meta, argc, argv)); - return false; - } - - // use a lookup table to map the option switches to the corresponding function that validates/handles the arguments - using parseFunction = bool (*)(Metadata& meta, - int& argc, const char*** argv); - static std::map parser{ - {"--input-file", parse_i}, {"-i", parse_i}, - {"--param-file", parse_pf}, {"-pf", parse_pf}, - {"--interp-factor", parse_f}, {"-f", parse_f}, - {"--interp-factor-x", parse_fx}, {"-fx", parse_fx}, - {"--interp-factor-y", parse_fy}, {"-fy", parse_fy}, - {"--output-file", parse_o}, {"-o", parse_o}, - {"--output-folder", parse_of}, {"-of", parse_of}, - {"--num-threads", parse_j}, {"-j", parse_j}, - {"--num-streams", parse_S}, {"-S", parse_S}, - {"--slice-thickness", parse_s}, {"-s", parse_s}, - {"--num-slices",parse_ns},{"-ns",parse_ns}, - {"--zstart-slices",parse_zs},{"-zs",parse_zs}, - {"--num-gpus", parse_g}, {"-g", parse_g}, - {"--batch-size", parse_b}, {"-b", parse_b}, - {"--batch-size-cpu", parse_bc}, {"-bc", parse_bc}, - {"--batch-size-gpu", parse_bg}, {"-bg", parse_bg}, - {"--help", parse_h}, {"-h", parse_h}, - {"--pixel-size", parse_p}, {"-p", parse_p}, - {"--pixel-size-x", parse_px}, {"-px", parse_px}, - {"--pixel-size-y", parse_py}, {"-py", parse_py}, - {"--detector-angle-step", parse_d}, {"-d", parse_d}, - {"--cell-dimension", parse_c}, {"-c", parse_c}, - {"--algorithm", parse_a}, {"-a", parse_a}, - {"--energy", parse_E}, {"-E", parse_E}, - {"--alpha-max", parse_A}, {"-A", parse_A}, - {"--potential-bound", parse_P}, {"-P", parse_P}, - {"--also-do-cpu-work", parse_C}, {"-C", parse_C}, - {"--streaming-mode", parse_streaming_mode}, - {"--probe-step", parse_r}, {"-r", parse_r}, - {"--probe-step-x", parse_rx}, {"-rx", parse_rx}, - {"--probe-step-y", parse_ry}, {"-ry", parse_ry}, - {"--random-seed", parse_rs}, {"-rs", parse_rs}, - {"--probe-xtilt", parse_tx}, {"-tx", parse_tx}, - {"--probe-ytilt", parse_ty}, {"-ty", parse_ty}, - {"--probe-defocus", parse_df}, {"-df", parse_df}, - {"-C3", parse_C3}, - {"-C5", parse_C5}, - {"--probe-semiangle", parse_sa}, {"-sa", parse_sa}, - {"--scan-window-y", parse_wy}, {"-wy", parse_wy}, - {"--scan-window-x", parse_wx}, {"-wx", parse_wx}, - {"--scan-window-yr", parse_wyr}, {"-wyr", parse_wyr}, - {"--scan-window-xr", parse_wxr}, {"-wxr", parse_wxr}, - {"--tile-uc", parse_t}, {"-t", parse_t}, - {"--num-FP", parse_F}, {"-F", parse_F}, - {"--thermal-effects", parse_te}, {"-te", parse_te}, - {"--occupancy", parse_oc}, {"-oc", parse_oc}, - {"--save-2D-output", parse_2D}, {"-2D", parse_2D}, - {"--save-3D-output", parse_3D}, {"-3D", parse_3D}, - {"--save-4D-output", parse_4D}, {"-4D", parse_4D}, - {"--save-DPC_CoM", parse_dpc}, {"-DPC", parse_dpc}, - {"--save-real-space-coords",parse_rsc}, {"-rsc",parse_rsc}, - {"--save-potential-slices",parse_ps},{"-ps",parse_ps}, - {"--nyquist-sampling",parse_nqs},{"-nqs",parse_nqs} - }; - bool parseInput(Metadata& meta, - int& argc, const char*** argv){ - parseFunction f = parser[std::string((*argv)[0])]; - if (f != NULL){ - return f(meta, argc, argv); - } else{ - cout << "Invalid option \"" << (*argv)[0] << "\" provided\n"; - return false; - } +// use a lookup table to map the option switches to the corresponding function that validates/handles the arguments +using parseFunction = bool (*)(Metadata &meta, + int &argc, const char ***argv); +static std::map parser{ + {"--input-file", parse_i}, {"-i", parse_i}, {"--param-file", parse_pf}, {"-pf", parse_pf}, {"--interp-factor", parse_f}, {"-f", parse_f}, {"--interp-factor-x", parse_fx}, {"-fx", parse_fx}, {"--interp-factor-y", parse_fy}, {"-fy", parse_fy}, {"--output-file", parse_o}, {"-o", parse_o}, {"--output-folder", parse_of}, {"-of", parse_of}, {"--num-threads", parse_j}, {"-j", parse_j}, {"--num-streams", parse_S}, {"-S", parse_S}, {"--slice-thickness", parse_s}, {"-s", parse_s}, {"--num-slices", parse_ns}, {"-ns", parse_ns}, {"--zstart-slices", parse_zs}, {"-zs", parse_zs}, {"--num-gpus", parse_g}, {"-g", parse_g}, {"--batch-size", parse_b}, {"-b", parse_b}, {"--batch-size-cpu", parse_bc}, {"-bc", parse_bc}, {"--batch-size-gpu", parse_bg}, {"-bg", parse_bg}, {"--help", parse_h}, {"-h", parse_h}, {"--pixel-size", parse_p}, {"-p", parse_p}, {"--pixel-size-x", parse_px}, {"-px", parse_px}, {"--pixel-size-y", parse_py}, {"-py", parse_py}, {"--detector-angle-step", parse_d}, {"-d", parse_d}, {"--cell-dimension", parse_c}, {"-c", parse_c}, {"--algorithm", parse_a}, {"-a", parse_a}, {"--energy", parse_E}, {"-E", parse_E}, {"--alpha-max", parse_A}, {"-A", parse_A}, {"--potential-bound", parse_P}, {"-P", parse_P}, {"--also-do-cpu-work", parse_C}, {"-C", parse_C}, {"--streaming-mode", parse_streaming_mode}, {"--probe-step", parse_r}, {"-r", parse_r}, {"--probe-step-x", parse_rx}, {"-rx", parse_rx}, {"--probe-step-y", parse_ry}, {"-ry", parse_ry}, {"--random-seed", parse_rs}, {"-rs", parse_rs}, {"--probe-xtilt", parse_tx}, {"-tx", parse_tx}, {"--probe-ytilt", parse_ty}, {"-ty", parse_ty}, {"--probe-defocus", parse_df}, {"-df", parse_df}, {"-C3", parse_C3}, {"-C5", parse_C5}, {"--probe-semiangle", parse_sa}, {"-sa", parse_sa}, {"--scan-window-y", parse_wy}, {"-wy", parse_wy}, {"--scan-window-x", parse_wx}, {"-wx", parse_wx}, {"--scan-window-yr", parse_wyr}, {"-wyr", parse_wyr}, {"--scan-window-xr", parse_wxr}, {"-wxr", parse_wxr}, {"--tile-uc", parse_t}, {"-t", parse_t}, {"--num-FP", parse_F}, {"-F", parse_F}, {"--thermal-effects", parse_te}, {"-te", parse_te}, {"--occupancy", parse_oc}, {"-oc", parse_oc}, {"--save-2D-output", parse_2D}, {"-2D", parse_2D}, {"--save-3D-output", parse_3D}, {"-3D", parse_3D}, {"--save-4D-output", parse_4D}, {"-4D", parse_4D}, {"--save-DPC_CoM", parse_dpc}, {"-DPC", parse_dpc}, {"--save-real-space-coords", parse_rsc}, {"-rsc", parse_rsc}, {"--save-potential-slices", parse_ps}, {"-ps", parse_ps}, {"--nyquist-sampling", parse_nqs}, {"-nqs", parse_nqs}}; +bool parseInput(Metadata &meta, + int &argc, const char ***argv) +{ + parseFunction f = parser[std::string((*argv)[0])]; + if (f != NULL) + { + return f(meta, argc, argv); + } + else + { + cout << "Invalid option \"" << (*argv)[0] << "\" provided\n"; + return false; } } +} // namespace Prismatic diff --git a/src/projectedPotential.cpp b/src/projectedPotential.cpp index 9c3a01e6c..b1487783d 100644 --- a/src/projectedPotential.cpp +++ b/src/projectedPotential.cpp @@ -23,134 +23,150 @@ #include "defines.h" #include "configure.h" -namespace Prismatic { - - PRISMATIC_FLOAT_PRECISION get_potMin(const Array2D& pot, - const Array1D& xr, - const Array1D& yr){ - // I am assuming that xr and yr are symmetric about 0 - const size_t xInd = std::floor(xr.size()/2); - const size_t yInd = std::floor(yr.size()/2); - const PRISMATIC_FLOAT_PRECISION dx = round(sqrt(2*(xInd + 1)- 1)); - const PRISMATIC_FLOAT_PRECISION dy = round(sqrt(2*(yInd + 1) - 1)); - const PRISMATIC_FLOAT_PRECISION xv[] = {xInd-dx, xInd+dx, xInd-dx, xInd+dx, 0, 0, (PRISMATIC_FLOAT_PRECISION)xr.size()-1, (PRISMATIC_FLOAT_PRECISION)xr.size()-1}; - const PRISMATIC_FLOAT_PRECISION yv[] = {0, 0, (PRISMATIC_FLOAT_PRECISION)yr.size()-1, (PRISMATIC_FLOAT_PRECISION)yr.size()-1, yInd-dy, yInd+dy, yInd-dy, yInd+dy}; - - PRISMATIC_FLOAT_PRECISION potMin = 0; - for (auto i=0; i < 8; ++i)potMin = (pot.at(yv[i],xv[i]) > potMin) ? pot.at(yv[i],xv[i]) : potMin; - return potMin; - } +namespace Prismatic +{ + +PRISMATIC_FLOAT_PRECISION get_potMin(const Array2D &pot, + const Array1D &xr, + const Array1D &yr) +{ + // I am assuming that xr and yr are symmetric about 0 + const size_t xInd = std::floor(xr.size() / 2); + const size_t yInd = std::floor(yr.size() / 2); + const PRISMATIC_FLOAT_PRECISION dx = round(sqrt(2 * (xInd + 1) - 1)); + const PRISMATIC_FLOAT_PRECISION dy = round(sqrt(2 * (yInd + 1) - 1)); + const PRISMATIC_FLOAT_PRECISION xv[] = {xInd - dx, xInd + dx, xInd - dx, xInd + dx, 0, 0, (PRISMATIC_FLOAT_PRECISION)xr.size() - 1, (PRISMATIC_FLOAT_PRECISION)xr.size() - 1}; + const PRISMATIC_FLOAT_PRECISION yv[] = {0, 0, (PRISMATIC_FLOAT_PRECISION)yr.size() - 1, (PRISMATIC_FLOAT_PRECISION)yr.size() - 1, yInd - dy, yInd + dy, yInd - dy, yInd + dy}; + + PRISMATIC_FLOAT_PRECISION potMin = 0; + for (auto i = 0; i < 8; ++i) + potMin = (pot.at(yv[i], xv[i]) > potMin) ? pot.at(yv[i], xv[i]) : potMin; + return potMin; +} - using namespace std; - - Array2D projPot(const size_t &Z, - const Array1D &xr, - const Array1D &yr) { - // compute the projected potential for a given atomic number following Kirkland - - // setup some constants - static const PRISMATIC_FLOAT_PRECISION pi = std::acos(-1); - PRISMATIC_FLOAT_PRECISION ss = 8; - PRISMATIC_FLOAT_PRECISION a0 = 0.5292; - PRISMATIC_FLOAT_PRECISION e = 14.4; - PRISMATIC_FLOAT_PRECISION term1 = 4*pi*pi*a0*e; - PRISMATIC_FLOAT_PRECISION term2 = 2*pi*pi*a0*e; - - // initialize array - ArrayND<2, std::vector > result = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>({{yr.size(), xr.size()}}); - - // setup some coordinates - const PRISMATIC_FLOAT_PRECISION dx = xr[1] - xr[0]; - const PRISMATIC_FLOAT_PRECISION dy = yr[1] - yr[0]; - - PRISMATIC_FLOAT_PRECISION start = -(ss-1)/ss/2; - const PRISMATIC_FLOAT_PRECISION step = 1/ss; - const PRISMATIC_FLOAT_PRECISION end = -start; - vector sub_data; - while (start <= end){ - sub_data.push_back(start); - start+=step; - } - ArrayND<1, std::vector > sub(sub_data,{{sub_data.size()}}); +using namespace std; + +Array2D projPot(const size_t &Z, + const Array1D &xr, + const Array1D &yr) +{ + // compute the projected potential for a given atomic number following Kirkland + + // setup some constants + static const PRISMATIC_FLOAT_PRECISION pi = std::acos(-1); + PRISMATIC_FLOAT_PRECISION ss = 8; + PRISMATIC_FLOAT_PRECISION a0 = 0.5292; + PRISMATIC_FLOAT_PRECISION e = 14.4; + PRISMATIC_FLOAT_PRECISION term1 = 4 * pi * pi * a0 * e; + PRISMATIC_FLOAT_PRECISION term2 = 2 * pi * pi * a0 * e; + + // initialize array + ArrayND<2, std::vector> result = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>({{yr.size(), xr.size()}}); + + // setup some coordinates + const PRISMATIC_FLOAT_PRECISION dx = xr[1] - xr[0]; + const PRISMATIC_FLOAT_PRECISION dy = yr[1] - yr[0]; + + PRISMATIC_FLOAT_PRECISION start = -(ss - 1) / ss / 2; + const PRISMATIC_FLOAT_PRECISION step = 1 / ss; + const PRISMATIC_FLOAT_PRECISION end = -start; + vector sub_data; + while (start <= end) + { + sub_data.push_back(start); + start += step; + } + ArrayND<1, std::vector> sub(sub_data, {{sub_data.size()}}); - std::pair, Array2D > meshx = meshgrid(xr, sub*dx); - std::pair, Array2D > meshy = meshgrid(yr, sub*dy); + std::pair, Array2D> meshx = meshgrid(xr, sub * dx); + std::pair, Array2D> meshy = meshgrid(yr, sub * dy); - ArrayND<1, std::vector > xv = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{meshx.first.size()}}); - ArrayND<1, std::vector > yv = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{meshy.first.size()}}); + ArrayND<1, std::vector> xv = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{meshx.first.size()}}); + ArrayND<1, std::vector> yv = zeros_ND<1, PRISMATIC_FLOAT_PRECISION>({{meshy.first.size()}}); + { + auto t_x = xv.begin(); + for (auto j = 0; j < meshx.first.get_dimj(); ++j) { - auto t_x = xv.begin(); - for (auto j = 0; j < meshx.first.get_dimj(); ++j) { - for (auto i = 0; i < meshx.first.get_dimi(); ++i) { - *t_x++ = meshx.first.at(j, i) + meshx.second.at(j, i); - } + for (auto i = 0; i < meshx.first.get_dimi(); ++i) + { + *t_x++ = meshx.first.at(j, i) + meshx.second.at(j, i); } } + } + { + auto t_y = yv.begin(); + for (auto j = 0; j < meshy.first.get_dimj(); ++j) { - auto t_y = yv.begin(); - for (auto j = 0; j < meshy.first.get_dimj(); ++j) { - for (auto i = 0; i < meshy.first.get_dimi(); ++i) { - *t_y++ = meshy.first.at(j, i) + meshy.second.at(j, i); - } + for (auto i = 0; i < meshy.first.get_dimi(); ++i) + { + *t_y++ = meshy.first.at(j, i) + meshy.second.at(j, i); } } + } - std::pair, Array2D > meshxy = meshgrid(yv, xv); - ArrayND<2, std::vector > r2 = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>({{yv.size(), xv.size()}}); - ArrayND<2, std::vector > r = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>({{yv.size(), xv.size()}}); + std::pair, Array2D> meshxy = meshgrid(yv, xv); + ArrayND<2, std::vector> r2 = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>({{yv.size(), xv.size()}}); + ArrayND<2, std::vector> r = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>({{yv.size(), xv.size()}}); + { + auto t_y = r2.begin(); + for (auto j = 0; j < meshxy.first.get_dimj(); ++j) { - auto t_y = r2.begin(); - for (auto j = 0; j < meshxy.first.get_dimj(); ++j) { - for (auto i = 0; i < meshxy.first.get_dimi(); ++i) { - *t_y++ = pow(meshxy.first.at(j,i),2) + pow(meshxy.second.at(j,i),2); - } + for (auto i = 0; i < meshxy.first.get_dimi(); ++i) + { + *t_y++ = pow(meshxy.first.at(j, i), 2) + pow(meshxy.second.at(j, i), 2); } } + } - for (auto i = 0; i < r.size(); ++i)r[i] = sqrt(r2[i]); - // construct potential - ArrayND<2, std::vector > potSS = ones_ND<2, PRISMATIC_FLOAT_PRECISION>({{r2.get_dimj(), r2.get_dimi()}}); - - // get the relevant table values - std::vector ap; - ap.resize(NUM_PARAMETERS); - for (auto i = 0; i < NUM_PARAMETERS; ++i){ - ap[i] = fparams[(Z-1)*NUM_PARAMETERS + i]; - } + for (auto i = 0; i < r.size(); ++i) + r[i] = sqrt(r2[i]); + // construct potential + ArrayND<2, std::vector> potSS = ones_ND<2, PRISMATIC_FLOAT_PRECISION>({{r2.get_dimj(), r2.get_dimi()}}); + + // get the relevant table values + std::vector ap; + ap.resize(NUM_PARAMETERS); + for (auto i = 0; i < NUM_PARAMETERS; ++i) + { + ap[i] = fparams[(Z - 1) * NUM_PARAMETERS + i]; + } - // compute the potential - using namespace boost::math; - std::transform(r.begin(), r.end(), - r2.begin(), potSS.begin(), [&ap, &term1, &term2](const PRISMATIC_FLOAT_PRECISION& r_t, const PRISMATIC_FLOAT_PRECISION& r2_t){ - - return term1*(ap[0] * - cyl_bessel_k(0,2*pi*sqrt(ap[1])*r_t) + - ap[2]*cyl_bessel_k(0,2*pi*sqrt(ap[3])*r_t) + - ap[4]*cyl_bessel_k(0,2*pi*sqrt(ap[5])*r_t)) + - term2*(ap[6]/ap[7]*exp(-pow(pi,2)/ap[7]*r2_t) + - ap[8]/ap[9]*exp(-pow(pi,2)/ap[9]*r2_t) + - ap[10]/ap[11]*exp(-pow(pi,2)/ap[11]*r2_t)); - }); - - // integrate - ArrayND<2, std::vector > pot = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>({{yr.size(), xr.size()}}); - for (auto sy = 0; sy < ss; ++sy){ - for (auto sx = 0; sx < ss; ++sx) { - for (auto j = 0; j < pot.get_dimj(); ++j) { - for (auto i = 0; i < pot.get_dimi(); ++i) { - pot.at(j, i) += potSS.at(j*ss + sy, i*ss + sx); - } + // compute the potential + using namespace boost::math; + std::transform(r.begin(), r.end(), + r2.begin(), potSS.begin(), [&ap, &term1, &term2](const PRISMATIC_FLOAT_PRECISION &r_t, const PRISMATIC_FLOAT_PRECISION &r2_t) { + return term1 * (ap[0] * + cyl_bessel_k(0, 2 * pi * sqrt(ap[1]) * r_t) + + ap[2] * cyl_bessel_k(0, 2 * pi * sqrt(ap[3]) * r_t) + + ap[4] * cyl_bessel_k(0, 2 * pi * sqrt(ap[5]) * r_t)) + + term2 * (ap[6] / ap[7] * exp(-pow(pi, 2) / ap[7] * r2_t) + + ap[8] / ap[9] * exp(-pow(pi, 2) / ap[9] * r2_t) + + ap[10] / ap[11] * exp(-pow(pi, 2) / ap[11] * r2_t)); + }); + + // integrate + ArrayND<2, std::vector> pot = zeros_ND<2, PRISMATIC_FLOAT_PRECISION>({{yr.size(), xr.size()}}); + for (auto sy = 0; sy < ss; ++sy) + { + for (auto sx = 0; sx < ss; ++sx) + { + for (auto j = 0; j < pot.get_dimj(); ++j) + { + for (auto i = 0; i < pot.get_dimi(); ++i) + { + pot.at(j, i) += potSS.at(j * ss + sy, i * ss + sx); } } } - pot/=(ss*ss); + } + pot /= (ss * ss); - PRISMATIC_FLOAT_PRECISION potMin = get_potMin(pot,xr,yr); - pot -= potMin; - transform(pot.begin(),pot.end(),pot.begin(),[](PRISMATIC_FLOAT_PRECISION& a){return a<0?0:a;}); + PRISMATIC_FLOAT_PRECISION potMin = get_potMin(pot, xr, yr); + pot -= potMin; + transform(pot.begin(), pot.end(), pot.begin(), [](PRISMATIC_FLOAT_PRECISION &a) { return a < 0 ? 0 : a; }); - return pot; - } + return pot; } +} // namespace Prismatic diff --git a/src/utility.cpp b/src/utility.cpp index 596125824..99311e89c 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -19,1437 +19,1507 @@ #include #include #ifdef _WIN32 - #include - #define access _access_s +#include +#define access _access_s #else - #include +#include #endif +namespace Prismatic +{ + +std::pair>, Prismatic::Array2D>> +upsamplePRISMProbe(Prismatic::Array2D> probe, + const long dimj, const long dimi, long ys, long xs) +{ + Array2D> realspace_probe; + Array2D> buffer_probe; + Array2D> kspace_probe; + + buffer_probe = zeros_ND<2, std::complex>({{(size_t)dimj, (size_t)dimi}}); + // std::cout << "dimj = " << dimj << std::endl; + long ncy = probe.get_dimj() / 2; + long ncx = probe.get_dimi() / 2; + for (auto j = 0; j < probe.get_dimj(); ++j) + { + for (auto i = 0; i < probe.get_dimi(); ++i) + { + buffer_probe.at((dimj + ((j - ncy + ys) % dimj)) % dimj, + (dimi + ((i - ncx + xs) % dimi)) % dimi) = probe.at(j, i); + // std::cout << "(dimj + ((j - ncy) % dimj)) % dimj= " << (dimj + ((j - ncy) % dimj)) % dimj<< std::endl; + // std::cout << "(j - ncy)= " << (j - ncy) << std::endl; + // std::cout << "(j - ncy) % dimj)= " << (j - ncy) % dimj<< std::endl; + + // buffer_probe.at( (dimj + ((j - ncy) % dimj)) % dimj, + // (dimi + ((i - ncx) % dimi)) % dimi) = probe.at(j, i); + } + } + std::unique_lock gatekeeper(fftw_plan_lock); + PRISMATIC_FFTW_PLAN plan = PRISMATIC_FFTW_PLAN_DFT_2D(buffer_probe.get_dimj(), buffer_probe.get_dimi(), + reinterpret_cast(&buffer_probe[0]), + reinterpret_cast(&buffer_probe[0]), + FFTW_FORWARD, FFTW_ESTIMATE); + gatekeeper.unlock(); + realspace_probe = buffer_probe; + PRISMATIC_FFTW_EXECUTE(plan); + kspace_probe = buffer_probe; + gatekeeper.lock(); + PRISMATIC_FFTW_DESTROY_PLAN(plan); + gatekeeper.unlock(); + return std::make_pair(realspace_probe, kspace_probe); +} -namespace Prismatic { - - +PRISMATIC_FLOAT_PRECISION computePearsonCorrelation(Prismatic::Array2D> left, + Prismatic::Array2D> right) +{ + PRISMATIC_FLOAT_PRECISION m1, m2, sigma1, sigma2, R; + m1 = m2 = sigma1 = sigma2 = R = 0; + + for (auto &i : left) + m1 += std::abs(i); + for (auto &i : right) + m2 += std::abs(i); + + m1 /= (left.size()); + m2 /= (right.size()); + + for (auto &i : left) + sigma1 += pow(std::abs(i) - m1, 2); + for (auto &i : right) + sigma2 += pow(std::abs(i) - m2, 2); + + sigma1 /= (left.size()); + sigma2 /= (right.size()); + + sigma1 = sqrt(sigma1); + sigma2 = sqrt(sigma2); + for (auto i = 0; i < std::min(left.size(), right.size()); ++i) + { + R = R + (std::abs(left[i]) - m1) * (std::abs(right[i]) - m2); + } + R /= sqrt(left.size() * right.size()); + return R / (sigma1 * sigma2); +} +PRISMATIC_FLOAT_PRECISION computeRfactor(Prismatic::Array2D> left, + Prismatic::Array2D> right) +{ + PRISMATIC_FLOAT_PRECISION accum, diffs; + accum = diffs = 0; + for (auto i = 0; i < std::min(left.size(), right.size()); ++i) + { + diffs += std::abs(left[i] - right[i]); + accum += std::abs(left[i]); + } + return diffs / accum; +} - std::pair >, Prismatic::Array2D > > - upsamplePRISMProbe(Prismatic::Array2D > probe, - const long dimj, const long dimi, long ys, long xs) { - Array2D > realspace_probe; - Array2D > buffer_probe; - Array2D > kspace_probe; +int nyquistProbes(Prismatic::Parameters pars, size_t dim) +{ + int nProbes = ceil(4 * (pars.meta.probeSemiangle / pars.lambda) * pars.tiledCellDim[dim]); + return nProbes; +} - buffer_probe = zeros_ND<2, std::complex >({{(size_t)dimj, (size_t)dimi}}); -// std::cout << "dimj = " << dimj << std::endl; - long ncy = probe.get_dimj() / 2; - long ncx = probe.get_dimi() / 2; - for (auto j = 0; j < probe.get_dimj(); ++j) { - for (auto i = 0; i < probe.get_dimi(); ++i) { - buffer_probe.at( (dimj + ((j - ncy + ys) % dimj)) % dimj, - (dimi + ((i - ncx + xs) % dimi)) % dimi) = probe.at(j, i); -// std::cout << "(dimj + ((j - ncy) % dimj)) % dimj= " << (dimj + ((j - ncy) % dimj)) % dimj<< std::endl; -// std::cout << "(j - ncy)= " << (j - ncy) << std::endl; -// std::cout << "(j - ncy) % dimj)= " << (j - ncy) % dimj<< std::endl; +std::string remove_extension(const std::string &filename) +{ + size_t lastdot = filename.find_last_of("."); + if (lastdot == std::string::npos) + return filename; + return filename.substr(0, lastdot); +} -// buffer_probe.at( (dimj + ((j - ncy) % dimj)) % dimj, -// (dimi + ((i - ncx) % dimi)) % dimi) = probe.at(j, i); - } +int testFilenameOutput(const std::string &filename) +{ + bool exists = !testExist(filename); + bool write_ok = !testWrite(filename); + //Check if file already exists and if we can write to it + if (exists && write_ok) + { + std::cout << "Warning " << filename << " already exists and will be overwritten" << std::endl; + return 2; + } + else if (exists && !write_ok) + { + std::cout << filename << " isn't an accessible write destination" << std::endl; + return 0; + } + else + { + //If the file does not exist, check to see if we can open a file of that name + std::ofstream f(filename, std::ios::binary | std::ios::out); + if (f) + { + //If we can open such a file, close the file and delete it. + f.close(); + std::remove(filename.c_str()); + return 1; + } + else + { + std::cout << filename << " isn't an accessible write destination" << std::endl; + return 0; } - std::unique_lock gatekeeper(fftw_plan_lock); - PRISMATIC_FFTW_PLAN plan = PRISMATIC_FFTW_PLAN_DFT_2D(buffer_probe.get_dimj(), buffer_probe.get_dimi(), - reinterpret_cast(&buffer_probe[0]), - reinterpret_cast(&buffer_probe[0]), - FFTW_FORWARD, FFTW_ESTIMATE); - gatekeeper.unlock(); - realspace_probe = buffer_probe; - PRISMATIC_FFTW_EXECUTE(plan); - kspace_probe = buffer_probe; - gatekeeper.lock(); - PRISMATIC_FFTW_DESTROY_PLAN(plan); - gatekeeper.unlock(); - return std::make_pair(realspace_probe, kspace_probe); } +} - PRISMATIC_FLOAT_PRECISION computePearsonCorrelation(Prismatic::Array2D > left, - Prismatic::Array2D > right){ - PRISMATIC_FLOAT_PRECISION m1, m2, sigma1, sigma2, R; - m1=m2=sigma1=sigma2=R=0; +int testWrite(const std::string &filename) +{ + int answer = access(filename.c_str(), 02); //W_OK = 02 + return answer; +} - for (auto &i:left) m1 += std::abs(i); - for (auto &i:right)m2 += std::abs(i); +int testExist(const std::string &filename) +{ + int answer = access(filename.c_str(), 00); //F_OK == 00 + return answer; +} - m1 /= (left.size()); - m2 /= (right.size()); +void setupOutputFile(Prismatic::Parameters pars) +{ + //set version attributes + int maj_data = 0; + int min_data = 4; + + H5::DataSpace attr_dataspace(H5S_SCALAR); + + H5::Attribute maj_attr = pars.outputFile.createAttribute("version_major", H5::PredType::NATIVE_INT, attr_dataspace); + H5::Attribute min_attr = pars.outputFile.createAttribute("version_minor", H5::PredType::NATIVE_INT, attr_dataspace); + + maj_attr.write(H5::PredType::NATIVE_INT, &maj_data); + min_attr.write(H5::PredType::NATIVE_INT, &min_data); + + //create main groups + H5::Group simulation(pars.outputFile.createGroup("/4DSTEM_simulation")); + + //data groups + H5::Group data(simulation.createGroup("data")); + H5::Group datacubes(data.createGroup("datacubes")); + H5::Group dslices(data.createGroup("diffractionslices")); + H5::Group rslices(data.createGroup("realslices")); + H5::Group pointlists(data.createGroup("pointlists")); //point lists and point list arrays are not used in prismatic + H5::Group plarrays(data.createGroup("pointlistarrays")); //included here to maintain consistency with format + + //log group + H5::Group log(simulation.createGroup("log")); + + //metadata groups + H5::Group metadata(simulation.createGroup("metadata")); + H5::Group metadata_0(metadata.createGroup("metadata_0")); //for consistency with py4DSTEM v0.4 + + H5::Group original(metadata_0.createGroup("original")); + H5::Group shortlist(original.createGroup("shortlist")); + H5::Group all(original.createGroup("all")); + H5::Group microscope(metadata_0.createGroup("microscope")); + H5::Group sample(metadata_0.createGroup("sample")); + H5::Group user(metadata_0.createGroup("user")); + H5::Group calibration(metadata_0.createGroup("calibration")); + H5::Group comments(metadata_0.createGroup("comments")); +} - for (auto &i:left)sigma1 += pow(std::abs(i)-m1, 2); - for (auto &i:right)sigma2 += pow(std::abs(i)-m2, 2); +//use dummy variable to overload float/double dependence +void setup4DOutput(Prismatic::Parameters pars, const size_t numLayers, const float dummy) +{ + H5::Group datacubes = pars.outputFile.openGroup("4DSTEM_simulation/data/datacubes"); + + //shared properties + std::string base_name = "CBED_array_depth"; + hsize_t attr_dims[1] = {1}; + hsize_t data_dims[4]; + data_dims[0] = {pars.xp.size()}; + data_dims[1] = {pars.yp.size()}; + hsize_t chunkDims[4]; + chunkDims[0] = chunkDims[1] = {1}; + hsize_t rx_dim[1] = {pars.xp.size()}; + hsize_t ry_dim[1] = {pars.yp.size()}; + hsize_t qx_dim[1]; + hsize_t qy_dim[1]; + + Prismatic::Array1D qx; + Prismatic::Array1D qy; + long offset_qx; + long offset_qy; + + if (pars.meta.algorithm == Prismatic::Algorithm::Multislice) + { + data_dims[2] = {pars.psiProbeInit.get_dimi() / 2}; + data_dims[3] = {pars.psiProbeInit.get_dimj() / 2}; + qx_dim[0] = {pars.psiProbeInit.get_dimi() / 2}; + qy_dim[0] = {pars.psiProbeInit.get_dimj() / 2}; + qx = fftshift(pars.qx); + qy = fftshift(pars.qy); + offset_qx = pars.psiProbeInit.get_dimi() / 4; + offset_qy = pars.psiProbeInit.get_dimj() / 4; + chunkDims[2] = {pars.psiProbeInit.get_dimi() / 2}; + chunkDims[3] = {pars.psiProbeInit.get_dimj() / 2}; + } + else + { + data_dims[2] = {pars.qx.get_dimi()}; + data_dims[3] = {pars.qy.get_dimi()}; + qx_dim[0] = {pars.qx.get_dimi()}; + qy_dim[0] = {pars.qy.get_dimi()}; + qx = pars.qx; + qy = pars.qy; + offset_qx = 0; + offset_qy = 0; + chunkDims[2] = {pars.qx.get_dimi()}; + chunkDims[3] = {pars.qy.get_dimi()}; + //std::cout << "Probe size: " << pars.psiProbeInit.get_dimi() << std::endl; + } - sigma1 /= (left.size()); - sigma2 /= (right.size()); + for (auto n = 0; n < numLayers; n++) + { + //create slice group + std::string nth_name = base_name + getDigitString(n); + H5::Group CBED_slice_n(datacubes.createGroup(nth_name.c_str())); + + //write group type attribute + H5::DataSpace attr1_dataspace(H5S_SCALAR); + H5::Attribute emd_group_type = CBED_slice_n.createAttribute("emd_group_type", H5::PredType::NATIVE_INT, attr1_dataspace); + int group_type = 2; + emd_group_type.write(H5::PredType::NATIVE_INT, &group_type); + + //write metadata attribute + H5::DataSpace attr2_dataspace(H5S_SCALAR); + H5::Attribute metadata_group = CBED_slice_n.createAttribute("metadata", H5::PredType::NATIVE_INT, attr2_dataspace); + int mgroup = 0; + metadata_group.write(H5::PredType::NATIVE_INT, &mgroup); + + //setup data set chunking properties + H5::DSetCreatPropList plist; + plist.setChunk(4, chunkDims); + + //create dataset + H5::DataSpace mspace(4, data_dims); //rank is 4 + H5::DataSet CBED_data = CBED_slice_n.createDataSet("datacube", H5::PredType::NATIVE_FLOAT, mspace, plist); + mspace.close(); - sigma1 = sqrt(sigma1); - sigma2 = sqrt(sigma2); - for (auto i = 0; i < std::min(left.size(), right.size()); ++i){ - R = R + (std::abs(left[i]) - m1) * (std::abs(right[i]) - m2); - } - R/=sqrt(left.size()*right.size()); - return R / (sigma1 * sigma2); - } - PRISMATIC_FLOAT_PRECISION computeRfactor(Prismatic::Array2D > left, - Prismatic::Array2D > right){ - PRISMATIC_FLOAT_PRECISION accum, diffs; - accum = diffs = 0; - for (auto i = 0; i < std::min(left.size(), right.size()); ++i){ - diffs += std::abs(left[i] - right[i]); - accum += std::abs(left[i]); - } - return diffs / accum; + //write dimensions + //TODO: fftshift the dimensions so they are consistent with image; write the fftshift1 function to do so + //TODO: qx, qy truncate so that match antialiasing filter if using multislice + H5::DataSpace str_name_ds(H5S_SCALAR); + H5::StrType strdatatype(H5::PredType::C_S1, 256); + + H5::DataSpace dim1_mspace(1, rx_dim); + H5::DataSpace dim2_mspace(1, ry_dim); + H5::DataSpace dim3_mspace(1, qx_dim); + H5::DataSpace dim4_mspace(1, qy_dim); + + H5::DataSet dim1 = CBED_slice_n.createDataSet("dim1", H5::PredType::NATIVE_FLOAT, dim1_mspace); + H5::DataSet dim2 = CBED_slice_n.createDataSet("dim2", H5::PredType::NATIVE_FLOAT, dim2_mspace); + H5::DataSet dim3 = CBED_slice_n.createDataSet("dim3", H5::PredType::NATIVE_FLOAT, dim3_mspace); + H5::DataSet dim4 = CBED_slice_n.createDataSet("dim4", H5::PredType::NATIVE_FLOAT, dim4_mspace); + + H5::DataSpace dim1_fspace = dim1.getSpace(); + H5::DataSpace dim2_fspace = dim2.getSpace(); + H5::DataSpace dim3_fspace = dim3.getSpace(); + H5::DataSpace dim4_fspace = dim4.getSpace(); + + dim1.write(&pars.xp[0], H5::PredType::NATIVE_FLOAT, dim1_mspace, dim1_fspace); + dim2.write(&pars.yp[0], H5::PredType::NATIVE_FLOAT, dim2_mspace, dim2_fspace); + dim3.write(&qx[offset_qx], H5::PredType::NATIVE_FLOAT, dim3_mspace, dim3_fspace); + dim4.write(&qy[offset_qy], H5::PredType::NATIVE_FLOAT, dim4_mspace, dim4_fspace); + + //dimension attributes + const H5std_string dim1_name_str("R_x"); + const H5std_string dim2_name_str("R_y"); + const H5std_string dim3_name_str("Q_x"); + const H5std_string dim4_name_str("Q_y"); + + H5::Attribute dim1_name = dim1.createAttribute("name", strdatatype, str_name_ds); + H5::Attribute dim2_name = dim2.createAttribute("name", strdatatype, str_name_ds); + H5::Attribute dim3_name = dim3.createAttribute("name", strdatatype, str_name_ds); + H5::Attribute dim4_name = dim4.createAttribute("name", strdatatype, str_name_ds); + + dim1_name.write(strdatatype, dim1_name_str); + dim2_name.write(strdatatype, dim2_name_str); + dim3_name.write(strdatatype, dim3_name_str); + dim4_name.write(strdatatype, dim4_name_str); + + const H5std_string dim1_unit_str("[n_m]"); + const H5std_string dim2_unit_str("[n_m]"); + const H5std_string dim3_unit_str("[n_m^-1]"); + const H5std_string dim4_unit_str("[n_m^-1]"); + + H5::Attribute dim1_unit = dim1.createAttribute("units", strdatatype, str_name_ds); + H5::Attribute dim2_unit = dim2.createAttribute("units", strdatatype, str_name_ds); + H5::Attribute dim3_unit = dim3.createAttribute("units", strdatatype, str_name_ds); + H5::Attribute dim4_unit = dim4.createAttribute("units", strdatatype, str_name_ds); + + dim1_unit.write(strdatatype, dim1_unit_str); + dim2_unit.write(strdatatype, dim2_unit_str); + dim3_unit.write(strdatatype, dim3_unit_str); + dim4_unit.write(strdatatype, dim4_unit_str); + + CBED_slice_n.close(); } - int nyquistProbes(Prismatic::Parameters pars, size_t dim){ - int nProbes = ceil(4*(pars.meta.probeSemiangle / pars.lambda) * pars.tiledCellDim[dim]); - return nProbes; + datacubes.close(); +}; + +//use dummy variable to overload float/double dependence +void setup4DOutput(Prismatic::Parameters pars, const size_t numLayers, const double dummy) +{ + H5::Group datacubes = pars.outputFile.openGroup("4DSTEM_simulation/data/datacubes"); + + //shared properties + std::string base_name = "CBED_array_depth"; + hsize_t attr_dims[1] = {1}; + hsize_t data_dims[4]; + data_dims[0] = {pars.xp.size()}; + data_dims[1] = {pars.yp.size()}; + hsize_t chunkDims[4]; + chunkDims[0] = chunkDims[1] = {1}; + hsize_t rx_dim[1] = {pars.xp.size()}; + hsize_t ry_dim[1] = {pars.yp.size()}; + hsize_t qx_dim[1]; + hsize_t qy_dim[1]; + Prismatic::Array1D qx; + Prismatic::Array1D qy; + long offset_qx; + long offset_qy; + + if (pars.meta.algorithm == Prismatic::Algorithm::Multislice) + { + data_dims[2] = {pars.psiProbeInit.get_dimi() / 2}; + data_dims[3] = {pars.psiProbeInit.get_dimj() / 2}; + qx_dim[0] = {pars.psiProbeInit.get_dimi() / 2}; + qy_dim[0] = {pars.psiProbeInit.get_dimj() / 2}; + qx = fftshift(pars.qx); + qy = fftshift(pars.qy); + offset_qx = pars.psiProbeInit.get_dimi() / 4; + offset_qy = pars.psiProbeInit.get_dimj() / 4; + chunkDims[2] = {pars.psiProbeInit.get_dimi() / 2}; + chunkDims[3] = {pars.psiProbeInit.get_dimj() / 2}; } + else + { + data_dims[2] = {pars.qx.get_dimi()}; + data_dims[3] = {pars.qy.get_dimi()}; + qx_dim[0] = {pars.qx.get_dimi()}; + qy_dim[0] = {pars.qy.get_dimi()}; + qx = pars.qx; + qy = pars.qy; + offset_qx = 0; + offset_qy = 0; + chunkDims[2] = {pars.qx.get_dimi()}; + chunkDims[3] = {pars.qy.get_dimi()}; + //std::cout << "Probe size: " << pars.psiProbeInit.get_dimi() << std::endl; + } + + for (auto n = 0; n < numLayers; n++) + { + //create slice group + std::string nth_name = base_name + getDigitString(n); + H5::Group CBED_slice_n(datacubes.createGroup(nth_name.c_str())); + + //write group type attribute + H5::DataSpace attr1_dataspace(H5S_SCALAR); + H5::Attribute emd_group_type = CBED_slice_n.createAttribute("emd_group_type", H5::PredType::NATIVE_INT, attr1_dataspace); + int group_type = 2; + emd_group_type.write(H5::PredType::NATIVE_INT, &group_type); + + //write metadata attribute + H5::DataSpace attr2_dataspace(H5S_SCALAR); + H5::Attribute metadata_group = CBED_slice_n.createAttribute("metadata", H5::PredType::NATIVE_INT, attr2_dataspace); + int mgroup = 0; + metadata_group.write(H5::PredType::NATIVE_INT, &mgroup); + + //set chunk properties + H5::DSetCreatPropList plist; + plist.setChunk(4, chunkDims); + + //create dataset + H5::DataSpace mspace(4, data_dims); //rank is 4 + H5::DataSet CBED_data = CBED_slice_n.createDataSet("datacube", H5::PredType::NATIVE_DOUBLE, mspace, plist); + mspace.close(); - std::string remove_extension(const std::string& filename) { - size_t lastdot = filename.find_last_of("."); - if (lastdot == std::string::npos) return filename; - return filename.substr(0, lastdot); + //write dimensions + //TODO: fftshift the dimensions so they are consistent with image; write the fftshift1 function to do so + //TODO: qx, qy truncate so that match antialiasing filter if using multislice + H5::DataSpace str_name_ds(H5S_SCALAR); + H5::StrType strdatatype(H5::PredType::C_S1, 256); + + H5::DataSpace dim1_mspace(1, rx_dim); + H5::DataSpace dim2_mspace(1, ry_dim); + H5::DataSpace dim3_mspace(1, qx_dim); + H5::DataSpace dim4_mspace(1, qy_dim); + + H5::DataSet dim1 = CBED_slice_n.createDataSet("dim1", H5::PredType::NATIVE_DOUBLE, dim1_mspace); + H5::DataSet dim2 = CBED_slice_n.createDataSet("dim2", H5::PredType::NATIVE_DOUBLE, dim2_mspace); + H5::DataSet dim3 = CBED_slice_n.createDataSet("dim3", H5::PredType::NATIVE_DOUBLE, dim3_mspace); + H5::DataSet dim4 = CBED_slice_n.createDataSet("dim4", H5::PredType::NATIVE_DOUBLE, dim4_mspace); + + H5::DataSpace dim1_fspace = dim1.getSpace(); + H5::DataSpace dim2_fspace = dim2.getSpace(); + H5::DataSpace dim3_fspace = dim3.getSpace(); + H5::DataSpace dim4_fspace = dim4.getSpace(); + + dim1.write(&pars.xp[0], H5::PredType::NATIVE_DOUBLE, dim1_mspace, dim1_fspace); + dim2.write(&pars.yp[0], H5::PredType::NATIVE_DOUBLE, dim2_mspace, dim2_fspace); + dim3.write(&qx[offset_qx], H5::PredType::NATIVE_DOUBLE, dim3_mspace, dim3_fspace); + dim4.write(&qy[offset_qy], H5::PredType::NATIVE_DOUBLE, dim4_mspace, dim4_fspace); + + //dimension attributes + const H5std_string dim1_name_str("R_x"); + const H5std_string dim2_name_str("R_y"); + const H5std_string dim3_name_str("Q_x"); + const H5std_string dim4_name_str("Q_y"); + + H5::Attribute dim1_name = dim1.createAttribute("name", strdatatype, str_name_ds); + H5::Attribute dim2_name = dim2.createAttribute("name", strdatatype, str_name_ds); + H5::Attribute dim3_name = dim3.createAttribute("name", strdatatype, str_name_ds); + H5::Attribute dim4_name = dim4.createAttribute("name", strdatatype, str_name_ds); + + dim1_name.write(strdatatype, dim1_name_str); + dim2_name.write(strdatatype, dim2_name_str); + dim3_name.write(strdatatype, dim3_name_str); + dim4_name.write(strdatatype, dim4_name_str); + + const H5std_string dim1_unit_str("[n_m]"); + const H5std_string dim2_unit_str("[n_m]"); + const H5std_string dim3_unit_str("[n_m^-1]"); + const H5std_string dim4_unit_str("[n_m^-1]"); + + H5::Attribute dim1_unit = dim1.createAttribute("units", strdatatype, str_name_ds); + H5::Attribute dim2_unit = dim2.createAttribute("units", strdatatype, str_name_ds); + H5::Attribute dim3_unit = dim3.createAttribute("units", strdatatype, str_name_ds); + H5::Attribute dim4_unit = dim4.createAttribute("units", strdatatype, str_name_ds); + + dim1_unit.write(strdatatype, dim1_unit_str); + dim2_unit.write(strdatatype, dim2_unit_str); + dim3_unit.write(strdatatype, dim3_unit_str); + dim4_unit.write(strdatatype, dim4_unit_str); + + CBED_slice_n.close(); } - int testFilenameOutput(const std::string& filename){ - bool exists = !testExist(filename); - bool write_ok = !testWrite(filename); - //Check if file already exists and if we can write to it - if(exists && write_ok){ - std::cout<<"Warning "< pars, const size_t numLayers, const float dummy) +{ + H5::Group realslices = pars.outputFile.openGroup("4DSTEM_simulation/data/realslices"); + + //shared properties + std::string base_name = "virtual_detector_depth"; + hsize_t attr_dims[1] = {1}; + hsize_t data_dims[3]; + data_dims[0] = {pars.xp.size()}; + data_dims[1] = {pars.yp.size()}; + data_dims[2] = {pars.Ndet}; + + hsize_t rx_dim[1] = {pars.xp.size()}; + hsize_t ry_dim[1] = {pars.yp.size()}; + hsize_t bin_dim[1] = {pars.Ndet}; + + for (auto n = 0; n < numLayers; n++) + { + //create slice group + std::string nth_name = base_name + getDigitString(n); + H5::Group VD_slice_n(realslices.createGroup(nth_name.c_str())); + + //write group type attribute + H5::DataSpace attr1_dataspace(H5S_SCALAR); + H5::Attribute emd_group_type = VD_slice_n.createAttribute("emd_group_type", H5::PredType::NATIVE_INT, attr1_dataspace); + int group_type = 2; + emd_group_type.write(H5::PredType::NATIVE_INT, &group_type); + + //write metadata attribute + H5::DataSpace attr2_dataspace(H5S_SCALAR); + H5::Attribute metadata_group = VD_slice_n.createAttribute("metadata", H5::PredType::NATIVE_INT, attr2_dataspace); + int mgroup = 0; + metadata_group.write(H5::PredType::NATIVE_INT, &mgroup); + + //write depth attribute + + //create datasets + H5::DataSpace mspace(3, data_dims); //rank is 2 for each realslice + H5::DataSet VD_data = VD_slice_n.createDataSet("realslice", H5::PredType::NATIVE_FLOAT, mspace); + VD_data.close(); + mspace.close(); + + //write dimensions + H5::DataSpace str_name_ds(H5S_SCALAR); + H5::StrType strdatatype(H5::PredType::C_S1, 256); + + H5::DataSpace dim1_mspace(1, rx_dim); + H5::DataSpace dim2_mspace(1, ry_dim); + H5::DataSpace dim3_mspace(1, bin_dim); + + H5::DataSet dim1 = VD_slice_n.createDataSet("dim1", H5::PredType::NATIVE_FLOAT, dim1_mspace); + H5::DataSet dim2 = VD_slice_n.createDataSet("dim2", H5::PredType::NATIVE_FLOAT, dim2_mspace); + H5::DataSet dim3 = VD_slice_n.createDataSet("dim3", H5::PredType::NATIVE_FLOAT, dim3_mspace); + + H5::DataSpace dim1_fspace = dim1.getSpace(); + H5::DataSpace dim2_fspace = dim2.getSpace(); + H5::DataSpace dim3_fspace = dim3.getSpace(); + + dim1.write(&pars.xp[0], H5::PredType::NATIVE_FLOAT, dim1_mspace, dim1_fspace); + dim2.write(&pars.yp[0], H5::PredType::NATIVE_FLOAT, dim2_mspace, dim2_fspace); + dim3.write(&pars.detectorAngles[0], H5::PredType::NATIVE_FLOAT, dim3_mspace, dim3_fspace); + std::cout << "Dimension written" << std::endl; + //dimension attributes + const H5std_string dim1_name_str("R_x"); + const H5std_string dim2_name_str("R_y"); + const H5std_string dim3_name_str("bin_outer_angle"); + H5::Attribute dim1_name = dim1.createAttribute("name", strdatatype, str_name_ds); + H5::Attribute dim2_name = dim2.createAttribute("name", strdatatype, str_name_ds); + H5::Attribute dim3_name = dim3.createAttribute("name", strdatatype, str_name_ds); + dim1_name.write(strdatatype, dim1_name_str); + dim2_name.write(strdatatype, dim2_name_str); + dim3_name.write(strdatatype, dim3_name_str); + + const H5std_string dim1_unit_str("[n_m]"); + const H5std_string dim2_unit_str("[n_m]"); + const H5std_string dim3_unit_str("[mrad]"); + + H5::Attribute dim1_unit = dim1.createAttribute("units", strdatatype, str_name_ds); + H5::Attribute dim2_unit = dim2.createAttribute("units", strdatatype, str_name_ds); + H5::Attribute dim3_unit = dim3.createAttribute("units", strdatatype, str_name_ds); + + dim1_unit.write(strdatatype, dim1_unit_str); + dim2_unit.write(strdatatype, dim2_unit_str); + dim3_unit.write(strdatatype, dim3_unit_str); + + VD_slice_n.close(); } - int testWrite(const std::string& filename){ - int answer = access(filename.c_str(),02); //W_OK = 02 - return answer; - } + realslices.close(); +}; + +void setupVDOutput(Prismatic::Parameters pars, const size_t numLayers, const double dummy) +{ + H5::Group realslices = pars.outputFile.openGroup("4DSTEM_simulation/data/realslices"); + + //shared properties + std::string base_name = "virtual_detector_depth"; + hsize_t attr_dims[1] = {1}; + hsize_t data_dims[3]; + data_dims[0] = {pars.xp.size()}; + data_dims[1] = {pars.yp.size()}; + data_dims[2] = {pars.Ndet}; + + hsize_t rx_dim[1] = {pars.xp.size()}; + hsize_t ry_dim[1] = {pars.yp.size()}; + hsize_t bin_dim[1] = {pars.Ndet}; + + for (auto n = 0; n < numLayers; n++) + { + //create slice group + std::string nth_name = base_name + getDigitString(n); + H5::Group VD_slice_n(realslices.createGroup(nth_name.c_str())); + + //write group type attribute + H5::DataSpace attr1_dataspace(H5S_SCALAR); + H5::Attribute emd_group_type = VD_slice_n.createAttribute("emd_group_type", H5::PredType::NATIVE_INT, attr1_dataspace); + int group_type = 2; + emd_group_type.write(H5::PredType::NATIVE_INT, &group_type); + + //write metadata attribute + H5::DataSpace attr2_dataspace(H5S_SCALAR); + H5::Attribute metadata_group = VD_slice_n.createAttribute("metadata", H5::PredType::NATIVE_INT, attr2_dataspace); + int mgroup = 0; + metadata_group.write(H5::PredType::NATIVE_INT, &mgroup); + + //write depth attribute + + //create datasets + H5::DataSpace mspace(3, data_dims); //rank is 2 for each realslice + H5::DataSet VD_data = VD_slice_n.createDataSet("realslice", H5::PredType::NATIVE_DOUBLE, mspace); + VD_data.close(); + mspace.close(); + + //write dimensions + H5::DataSpace str_name_ds(H5S_SCALAR); + H5::StrType strdatatype(H5::PredType::C_S1, 256); - int testExist(const std::string& filename){ - int answer = access(filename.c_str(),00); //F_OK == 00 - return answer; - } + H5::DataSpace dim1_mspace(1, rx_dim); + H5::DataSpace dim2_mspace(1, ry_dim); + H5::DataSpace dim3_mspace(1, bin_dim); - void setupOutputFile(Prismatic::Parameters pars){ - //set version attributes - int maj_data = 0; - int min_data = 4; + H5::DataSet dim1 = VD_slice_n.createDataSet("dim1", H5::PredType::NATIVE_DOUBLE, dim1_mspace); + H5::DataSet dim2 = VD_slice_n.createDataSet("dim2", H5::PredType::NATIVE_DOUBLE, dim2_mspace); + H5::DataSet dim3 = VD_slice_n.createDataSet("dim3", H5::PredType::NATIVE_DOUBLE, dim3_mspace); - H5::DataSpace attr_dataspace(H5S_SCALAR); + H5::DataSpace dim1_fspace = dim1.getSpace(); + H5::DataSpace dim2_fspace = dim2.getSpace(); + H5::DataSpace dim3_fspace = dim3.getSpace(); - H5::Attribute maj_attr = pars.outputFile.createAttribute("version_major",H5::PredType::NATIVE_INT,attr_dataspace); - H5::Attribute min_attr = pars.outputFile.createAttribute("version_minor",H5::PredType::NATIVE_INT,attr_dataspace); + dim1.write(&pars.xp[0], H5::PredType::NATIVE_DOUBLE, dim1_mspace, dim1_fspace); + dim2.write(&pars.yp[0], H5::PredType::NATIVE_DOUBLE, dim2_mspace, dim2_fspace); + dim3.write(&pars.detectorAngles[0], H5::PredType::NATIVE_DOUBLE, dim3_mspace, dim3_fspace); - maj_attr.write(H5::PredType::NATIVE_INT, &maj_data); - min_attr.write(H5::PredType::NATIVE_INT, &min_data); + //dimension attributes + const H5std_string dim1_name_str("R_x"); + const H5std_string dim2_name_str("R_y"); + const H5std_string dim3_name_str("bin_outer_angle"); - //create main groups - H5::Group simulation(pars.outputFile.createGroup("/4DSTEM_simulation")); + H5::Attribute dim1_name = dim1.createAttribute("name", strdatatype, str_name_ds); + H5::Attribute dim2_name = dim2.createAttribute("name", strdatatype, str_name_ds); + H5::Attribute dim3_name = dim3.createAttribute("name", strdatatype, str_name_ds); - //data groups - H5::Group data(simulation.createGroup("data")); - H5::Group datacubes(data.createGroup("datacubes")); - H5::Group dslices(data.createGroup("diffractionslices")); - H5::Group rslices(data.createGroup("realslices")); - H5::Group pointlists(data.createGroup("pointlists")); //point lists and point list arrays are not used in prismatic - H5::Group plarrays(data.createGroup("pointlistarrays")); //included here to maintain consistency with format + dim1_name.write(strdatatype, dim1_name_str); + dim2_name.write(strdatatype, dim2_name_str); + dim3_name.write(strdatatype, dim3_name_str); - //log group - H5::Group log(simulation.createGroup("log")); + const H5std_string dim1_unit_str("[n_m]"); + const H5std_string dim2_unit_str("[n_m]"); + const H5std_string dim3_unit_str("[mrad]"); - //metadata groups - H5::Group metadata(simulation.createGroup("metadata")); - H5::Group metadata_0(metadata.createGroup("metadata_0")); //for consistency with py4DSTEM v0.4 + H5::Attribute dim1_unit = dim1.createAttribute("units", strdatatype, str_name_ds); + H5::Attribute dim2_unit = dim2.createAttribute("units", strdatatype, str_name_ds); + H5::Attribute dim3_unit = dim3.createAttribute("units", strdatatype, str_name_ds); - H5::Group original(metadata_0.createGroup("original")); - H5::Group shortlist(original.createGroup("shortlist")); - H5::Group all(original.createGroup("all")); - H5::Group microscope(metadata_0.createGroup("microscope")); - H5::Group sample(metadata_0.createGroup("sample")); - H5::Group user(metadata_0.createGroup("user")); - H5::Group calibration(metadata_0.createGroup("calibration")); - H5::Group comments(metadata_0.createGroup("comments")); + dim1_unit.write(strdatatype, dim1_unit_str); + dim2_unit.write(strdatatype, dim2_unit_str); + dim3_unit.write(strdatatype, dim3_unit_str); + VD_slice_n.close(); } - //use dummy variable to overload float/double dependence - void setup4DOutput(Prismatic::Parameters pars, const size_t numLayers, const float dummy){ - H5::Group datacubes = pars.outputFile.openGroup("4DSTEM_simulation/data/datacubes"); - - //shared properties - std::string base_name = "CBED_array_depth"; - hsize_t attr_dims[1] = {1}; - hsize_t data_dims[4]; - data_dims[0] = {pars.xp.size()}; - data_dims[1] = {pars.yp.size()}; - hsize_t chunkDims[4]; - chunkDims[0] = chunkDims[1]= {1}; - hsize_t rx_dim[1] = {pars.xp.size()}; - hsize_t ry_dim[1] = {pars.yp.size()}; - hsize_t qx_dim[1]; - hsize_t qy_dim[1]; - - Prismatic::Array1D qx; - Prismatic::Array1D qy; - long offset_qx; - long offset_qy; - - if(pars.meta.algorithm == Prismatic::Algorithm::Multislice){ - data_dims[2] = {pars.psiProbeInit.get_dimi()/2}; - data_dims[3] = {pars.psiProbeInit.get_dimj()/2}; - qx_dim[0] = {pars.psiProbeInit.get_dimi()/2}; - qy_dim[0] = {pars.psiProbeInit.get_dimj()/2}; - qx = fftshift(pars.qx); - qy = fftshift(pars.qy); - offset_qx = pars.psiProbeInit.get_dimi()/4; - offset_qy = pars.psiProbeInit.get_dimj()/4; - chunkDims[2] = {pars.psiProbeInit.get_dimi()/2}; - chunkDims[3] = {pars.psiProbeInit.get_dimj()/2}; - - }else{ - data_dims[2] = {pars.qx.get_dimi()}; - data_dims[3] = {pars.qy.get_dimi()}; - qx_dim[0] = {pars.qx.get_dimi()}; - qy_dim[0] = {pars.qy.get_dimi()}; - qx = pars.qx; - qy = pars.qy; - offset_qx = 0; - offset_qy = 0; - chunkDims[2] = {pars.qx.get_dimi()}; - chunkDims[3] = {pars.qy.get_dimi()}; - //std::cout << "Probe size: " << pars.psiProbeInit.get_dimi() << std::endl; - } + realslices.close(); +}; + +void setup2DOutput(Prismatic::Parameters pars, const size_t numLayers, const float dummy) +{ + H5::Group realslices = pars.outputFile.openGroup("4DSTEM_simulation/data/realslices"); + + //shared properties + std::string base_name = "annular_detector_depth"; + hsize_t attr_dims[1] = {1}; + hsize_t data_dims[2]; + data_dims[0] = {pars.xp.size()}; + data_dims[1] = {pars.yp.size()}; + + hsize_t rx_dim[1] = {pars.xp.size()}; + hsize_t ry_dim[1] = {pars.yp.size()}; + //TODO: get data about detecor bin dimensions + + for (auto n = 0; n < numLayers; n++) + { + //create slice group + std::string nth_name = base_name + getDigitString(n); + H5::Group annular_slice_n(realslices.createGroup(nth_name.c_str())); + + //write group type attribute + H5::DataSpace attr1_dataspace(H5S_SCALAR); + H5::Attribute emd_group_type = annular_slice_n.createAttribute("emd_group_type", H5::PredType::NATIVE_INT, attr1_dataspace); + int group_type = 2; + emd_group_type.write(H5::PredType::NATIVE_INT, &group_type); + + //write metadata attribute + H5::DataSpace attr2_dataspace(H5S_SCALAR); + H5::Attribute metadata_group = annular_slice_n.createAttribute("metadata", H5::PredType::NATIVE_INT, attr2_dataspace); + int mgroup = 0; + metadata_group.write(H5::PredType::NATIVE_INT, &mgroup); + + //write depth attribute + int depth = 1; + H5::DataSpace attr3_dataspace(H5S_SCALAR); + H5::Attribute depth_attr = annular_slice_n.createAttribute("depth", H5::PredType::NATIVE_INT, attr3_dataspace); + depth_attr.write(H5::PredType::NATIVE_INT, &depth); + + //create dataset + H5::DataSpace mspace(2, data_dims); //rank is 3 + H5::DataSet CBED_data = annular_slice_n.createDataSet("realslice", H5::PredType::NATIVE_FLOAT, mspace); + mspace.close(); - for(auto n = 0; n < numLayers; n++){ - //create slice group - std::string nth_name = base_name + getDigitString(n); - H5::Group CBED_slice_n(datacubes.createGroup(nth_name.c_str())); - - //write group type attribute - H5::DataSpace attr1_dataspace(H5S_SCALAR); - H5::Attribute emd_group_type = CBED_slice_n.createAttribute("emd_group_type",H5::PredType::NATIVE_INT,attr1_dataspace); - int group_type = 2; - emd_group_type.write(H5::PredType::NATIVE_INT, &group_type); - - //write metadata attribute - H5::DataSpace attr2_dataspace(H5S_SCALAR); - H5::Attribute metadata_group = CBED_slice_n.createAttribute("metadata",H5::PredType::NATIVE_INT,attr2_dataspace); - int mgroup = 0; - metadata_group.write(H5::PredType::NATIVE_INT, &mgroup); - - //setup data set chunking properties - H5::DSetCreatPropList plist; - plist.setChunk(4,chunkDims); - - //create dataset - H5::DataSpace mspace(4,data_dims); //rank is 4 - H5::DataSet CBED_data = CBED_slice_n.createDataSet("datacube",H5::PredType::NATIVE_FLOAT,mspace,plist); - mspace.close(); - - //write dimensions - //TODO: fftshift the dimensions so they are consistent with image; write the fftshift1 function to do so - //TODO: qx, qy truncate so that match antialiasing filter if using multislice - H5::DataSpace str_name_ds(H5S_SCALAR); - H5::StrType strdatatype(H5::PredType::C_S1,256); - - H5::DataSpace dim1_mspace(1,rx_dim); - H5::DataSpace dim2_mspace(1,ry_dim); - H5::DataSpace dim3_mspace(1,qx_dim); - H5::DataSpace dim4_mspace(1,qy_dim); - - H5::DataSet dim1 = CBED_slice_n.createDataSet("dim1",H5::PredType::NATIVE_FLOAT,dim1_mspace); - H5::DataSet dim2 = CBED_slice_n.createDataSet("dim2",H5::PredType::NATIVE_FLOAT,dim2_mspace); - H5::DataSet dim3 = CBED_slice_n.createDataSet("dim3",H5::PredType::NATIVE_FLOAT,dim3_mspace); - H5::DataSet dim4 = CBED_slice_n.createDataSet("dim4",H5::PredType::NATIVE_FLOAT,dim4_mspace); - - H5::DataSpace dim1_fspace = dim1.getSpace(); - H5::DataSpace dim2_fspace = dim2.getSpace(); - H5::DataSpace dim3_fspace = dim3.getSpace(); - H5::DataSpace dim4_fspace = dim4.getSpace(); - - - dim1.write(&pars.xp[0],H5::PredType::NATIVE_FLOAT,dim1_mspace,dim1_fspace); - dim2.write(&pars.yp[0],H5::PredType::NATIVE_FLOAT,dim2_mspace,dim2_fspace); - dim3.write(&qx[offset_qx],H5::PredType::NATIVE_FLOAT,dim3_mspace,dim3_fspace); - dim4.write(&qy[offset_qy],H5::PredType::NATIVE_FLOAT,dim4_mspace,dim4_fspace); - - //dimension attributes - const H5std_string dim1_name_str("R_x"); - const H5std_string dim2_name_str("R_y"); - const H5std_string dim3_name_str("Q_x"); - const H5std_string dim4_name_str("Q_y"); - - H5::Attribute dim1_name = dim1.createAttribute("name",strdatatype,str_name_ds); - H5::Attribute dim2_name = dim2.createAttribute("name",strdatatype,str_name_ds); - H5::Attribute dim3_name = dim3.createAttribute("name",strdatatype,str_name_ds); - H5::Attribute dim4_name = dim4.createAttribute("name",strdatatype,str_name_ds); - - dim1_name.write(strdatatype,dim1_name_str); - dim2_name.write(strdatatype,dim2_name_str); - dim3_name.write(strdatatype,dim3_name_str); - dim4_name.write(strdatatype,dim4_name_str); - - const H5std_string dim1_unit_str("[n_m]"); - const H5std_string dim2_unit_str("[n_m]"); - const H5std_string dim3_unit_str("[n_m^-1]"); - const H5std_string dim4_unit_str("[n_m^-1]"); - - H5::Attribute dim1_unit = dim1.createAttribute("units",strdatatype,str_name_ds); - H5::Attribute dim2_unit = dim2.createAttribute("units",strdatatype,str_name_ds); - H5::Attribute dim3_unit = dim3.createAttribute("units",strdatatype,str_name_ds); - H5::Attribute dim4_unit = dim4.createAttribute("units",strdatatype,str_name_ds); - - dim1_unit.write(strdatatype,dim1_unit_str); - dim2_unit.write(strdatatype,dim2_unit_str); - dim3_unit.write(strdatatype,dim3_unit_str); - dim4_unit.write(strdatatype,dim4_unit_str); - - CBED_slice_n.close(); - } + //write dimensions + H5::DataSpace str_name_ds(H5S_SCALAR); + H5::StrType strdatatype(H5::PredType::C_S1, 256); - datacubes.close(); - }; - - //use dummy variable to overload float/double dependence - void setup4DOutput(Prismatic::Parameters pars, const size_t numLayers, const double dummy){ - H5::Group datacubes = pars.outputFile.openGroup("4DSTEM_simulation/data/datacubes"); - - //shared properties - std::string base_name = "CBED_array_depth"; - hsize_t attr_dims[1] = {1}; - hsize_t data_dims[4]; - data_dims[0] = {pars.xp.size()}; - data_dims[1] = {pars.yp.size()}; - hsize_t chunkDims[4]; - chunkDims[0] = chunkDims[1]= {1}; - hsize_t rx_dim[1] = {pars.xp.size()}; - hsize_t ry_dim[1] = {pars.yp.size()}; - hsize_t qx_dim[1]; - hsize_t qy_dim[1]; - Prismatic::Array1D qx; - Prismatic::Array1D qy; - long offset_qx; - long offset_qy; - - if(pars.meta.algorithm == Prismatic::Algorithm::Multislice){ - data_dims[2] = {pars.psiProbeInit.get_dimi()/2}; - data_dims[3] = {pars.psiProbeInit.get_dimj()/2}; - qx_dim[0] = {pars.psiProbeInit.get_dimi()/2}; - qy_dim[0] = {pars.psiProbeInit.get_dimj()/2}; - qx = fftshift(pars.qx); - qy = fftshift(pars.qy); - offset_qx = pars.psiProbeInit.get_dimi()/4; - offset_qy = pars.psiProbeInit.get_dimj()/4; - chunkDims[2] = {pars.psiProbeInit.get_dimi()/2}; - chunkDims[3] = {pars.psiProbeInit.get_dimj()/2}; - - }else{ - data_dims[2] = {pars.qx.get_dimi()}; - data_dims[3] = {pars.qy.get_dimi()}; - qx_dim[0] = {pars.qx.get_dimi()}; - qy_dim[0] = {pars.qy.get_dimi()}; - qx = pars.qx; - qy = pars.qy; - offset_qx = 0; - offset_qy = 0; - chunkDims[2] = {pars.qx.get_dimi()}; - chunkDims[3] = {pars.qy.get_dimi()}; - //std::cout << "Probe size: " << pars.psiProbeInit.get_dimi() << std::endl; - } + H5::DataSpace dim1_mspace(1, rx_dim); + H5::DataSpace dim2_mspace(1, ry_dim); - for(auto n = 0; n < numLayers; n++){ - //create slice group - std::string nth_name = base_name + getDigitString(n); - H5::Group CBED_slice_n(datacubes.createGroup(nth_name.c_str())); - - //write group type attribute - H5::DataSpace attr1_dataspace(H5S_SCALAR); - H5::Attribute emd_group_type = CBED_slice_n.createAttribute("emd_group_type",H5::PredType::NATIVE_INT,attr1_dataspace); - int group_type = 2; - emd_group_type.write(H5::PredType::NATIVE_INT, &group_type); - - //write metadata attribute - H5::DataSpace attr2_dataspace(H5S_SCALAR); - H5::Attribute metadata_group = CBED_slice_n.createAttribute("metadata",H5::PredType::NATIVE_INT,attr2_dataspace); - int mgroup = 0; - metadata_group.write(H5::PredType::NATIVE_INT, &mgroup); - - //set chunk properties - H5::DSetCreatPropList plist; - plist.setChunk(4,chunkDims); - - //create dataset - H5::DataSpace mspace(4,data_dims); //rank is 4 - H5::DataSet CBED_data = CBED_slice_n.createDataSet("datacube",H5::PredType::NATIVE_DOUBLE,mspace,plist); - mspace.close(); - - //write dimensions - //TODO: fftshift the dimensions so they are consistent with image; write the fftshift1 function to do so - //TODO: qx, qy truncate so that match antialiasing filter if using multislice - H5::DataSpace str_name_ds(H5S_SCALAR); - H5::StrType strdatatype(H5::PredType::C_S1,256); - - H5::DataSpace dim1_mspace(1,rx_dim); - H5::DataSpace dim2_mspace(1,ry_dim); - H5::DataSpace dim3_mspace(1,qx_dim); - H5::DataSpace dim4_mspace(1,qy_dim); - - H5::DataSet dim1 = CBED_slice_n.createDataSet("dim1",H5::PredType::NATIVE_DOUBLE,dim1_mspace); - H5::DataSet dim2 = CBED_slice_n.createDataSet("dim2",H5::PredType::NATIVE_DOUBLE,dim2_mspace); - H5::DataSet dim3 = CBED_slice_n.createDataSet("dim3",H5::PredType::NATIVE_DOUBLE,dim3_mspace); - H5::DataSet dim4 = CBED_slice_n.createDataSet("dim4",H5::PredType::NATIVE_DOUBLE,dim4_mspace); - - H5::DataSpace dim1_fspace = dim1.getSpace(); - H5::DataSpace dim2_fspace = dim2.getSpace(); - H5::DataSpace dim3_fspace = dim3.getSpace(); - H5::DataSpace dim4_fspace = dim4.getSpace(); - - dim1.write(&pars.xp[0],H5::PredType::NATIVE_DOUBLE,dim1_mspace,dim1_fspace); - dim2.write(&pars.yp[0],H5::PredType::NATIVE_DOUBLE,dim2_mspace,dim2_fspace); - dim3.write(&qx[offset_qx],H5::PredType::NATIVE_DOUBLE,dim3_mspace,dim3_fspace); - dim4.write(&qy[offset_qy],H5::PredType::NATIVE_DOUBLE,dim4_mspace,dim4_fspace); - - //dimension attributes - const H5std_string dim1_name_str("R_x"); - const H5std_string dim2_name_str("R_y"); - const H5std_string dim3_name_str("Q_x"); - const H5std_string dim4_name_str("Q_y"); - - H5::Attribute dim1_name = dim1.createAttribute("name",strdatatype,str_name_ds); - H5::Attribute dim2_name = dim2.createAttribute("name",strdatatype,str_name_ds); - H5::Attribute dim3_name = dim3.createAttribute("name",strdatatype,str_name_ds); - H5::Attribute dim4_name = dim4.createAttribute("name",strdatatype,str_name_ds); - - dim1_name.write(strdatatype,dim1_name_str); - dim2_name.write(strdatatype,dim2_name_str); - dim3_name.write(strdatatype,dim3_name_str); - dim4_name.write(strdatatype,dim4_name_str); - - const H5std_string dim1_unit_str("[n_m]"); - const H5std_string dim2_unit_str("[n_m]"); - const H5std_string dim3_unit_str("[n_m^-1]"); - const H5std_string dim4_unit_str("[n_m^-1]"); - - H5::Attribute dim1_unit = dim1.createAttribute("units",strdatatype,str_name_ds); - H5::Attribute dim2_unit = dim2.createAttribute("units",strdatatype,str_name_ds); - H5::Attribute dim3_unit = dim3.createAttribute("units",strdatatype,str_name_ds); - H5::Attribute dim4_unit = dim4.createAttribute("units",strdatatype,str_name_ds); - - dim1_unit.write(strdatatype,dim1_unit_str); - dim2_unit.write(strdatatype,dim2_unit_str); - dim3_unit.write(strdatatype,dim3_unit_str); - dim4_unit.write(strdatatype,dim4_unit_str); - - CBED_slice_n.close(); - } + H5::DataSet dim1 = annular_slice_n.createDataSet("dim1", H5::PredType::NATIVE_FLOAT, dim1_mspace); + H5::DataSet dim2 = annular_slice_n.createDataSet("dim2", H5::PredType::NATIVE_FLOAT, dim2_mspace); - datacubes.close(); - }; - - void setupVDOutput(Prismatic::Parameters pars, const size_t numLayers, const float dummy){ - H5::Group realslices = pars.outputFile.openGroup("4DSTEM_simulation/data/realslices"); - - //shared properties - std::string base_name = "virtual_detector_depth"; - hsize_t attr_dims[1] = {1}; - hsize_t data_dims[3]; - data_dims[0] = {pars.xp.size()}; - data_dims[1] = {pars.yp.size()}; - data_dims[2] = {pars.Ndet}; - - hsize_t rx_dim[1] = {pars.xp.size()}; - hsize_t ry_dim[1] = {pars.yp.size()}; - hsize_t bin_dim[1] = {pars.Ndet}; - - for(auto n = 0; n < numLayers; n++){ - //create slice group - std::string nth_name = base_name + getDigitString(n); - H5::Group VD_slice_n(realslices.createGroup(nth_name.c_str())); - - //write group type attribute - H5::DataSpace attr1_dataspace(H5S_SCALAR); - H5::Attribute emd_group_type = VD_slice_n.createAttribute("emd_group_type",H5::PredType::NATIVE_INT,attr1_dataspace); - int group_type = 2; - emd_group_type.write(H5::PredType::NATIVE_INT, &group_type); - - //write metadata attribute - H5::DataSpace attr2_dataspace(H5S_SCALAR); - H5::Attribute metadata_group = VD_slice_n.createAttribute("metadata",H5::PredType::NATIVE_INT,attr2_dataspace); - int mgroup = 0; - metadata_group.write(H5::PredType::NATIVE_INT, &mgroup); - - //write depth attribute - - //create datasets - H5::DataSpace mspace(3,data_dims); //rank is 2 for each realslice - H5::DataSet VD_data = VD_slice_n.createDataSet("realslice",H5::PredType::NATIVE_FLOAT,mspace); - VD_data.close(); - mspace.close(); - - //write dimensions - H5::DataSpace str_name_ds(H5S_SCALAR); - H5::StrType strdatatype(H5::PredType::C_S1,256); - - H5::DataSpace dim1_mspace(1,rx_dim); - H5::DataSpace dim2_mspace(1,ry_dim); - H5::DataSpace dim3_mspace(1,bin_dim); - - H5::DataSet dim1 = VD_slice_n.createDataSet("dim1",H5::PredType::NATIVE_FLOAT,dim1_mspace); - H5::DataSet dim2 = VD_slice_n.createDataSet("dim2",H5::PredType::NATIVE_FLOAT,dim2_mspace); - H5::DataSet dim3 = VD_slice_n.createDataSet("dim3",H5::PredType::NATIVE_FLOAT,dim3_mspace); - - H5::DataSpace dim1_fspace = dim1.getSpace(); - H5::DataSpace dim2_fspace = dim2.getSpace(); - H5::DataSpace dim3_fspace = dim3.getSpace(); - - dim1.write(&pars.xp[0],H5::PredType::NATIVE_FLOAT,dim1_mspace,dim1_fspace); - dim2.write(&pars.yp[0],H5::PredType::NATIVE_FLOAT,dim2_mspace,dim2_fspace); - dim3.write(&pars.detectorAngles[0],H5::PredType::NATIVE_FLOAT,dim3_mspace,dim3_fspace); - std::cout << "Dimension written" << std::endl; - //dimension attributes - const H5std_string dim1_name_str("R_x"); - const H5std_string dim2_name_str("R_y"); - const H5std_string dim3_name_str("bin_outer_angle"); - - H5::Attribute dim1_name = dim1.createAttribute("name",strdatatype,str_name_ds); - H5::Attribute dim2_name = dim2.createAttribute("name",strdatatype,str_name_ds); - H5::Attribute dim3_name = dim3.createAttribute("name",strdatatype,str_name_ds); - - dim1_name.write(strdatatype,dim1_name_str); - dim2_name.write(strdatatype,dim2_name_str); - dim3_name.write(strdatatype,dim3_name_str); - - const H5std_string dim1_unit_str("[n_m]"); - const H5std_string dim2_unit_str("[n_m]"); - const H5std_string dim3_unit_str("[mrad]"); - - H5::Attribute dim1_unit = dim1.createAttribute("units",strdatatype,str_name_ds); - H5::Attribute dim2_unit = dim2.createAttribute("units",strdatatype,str_name_ds); - H5::Attribute dim3_unit = dim3.createAttribute("units",strdatatype,str_name_ds); - - dim1_unit.write(strdatatype,dim1_unit_str); - dim2_unit.write(strdatatype,dim2_unit_str); - dim3_unit.write(strdatatype,dim3_unit_str); - - VD_slice_n.close(); - } + H5::DataSpace dim1_fspace = dim1.getSpace(); + H5::DataSpace dim2_fspace = dim2.getSpace(); - realslices.close(); - }; - - void setupVDOutput(Prismatic::Parameters pars, const size_t numLayers, const double dummy){ - H5::Group realslices = pars.outputFile.openGroup("4DSTEM_simulation/data/realslices"); - - //shared properties - std::string base_name = "virtual_detector_depth"; - hsize_t attr_dims[1] = {1}; - hsize_t data_dims[3]; - data_dims[0] = {pars.xp.size()}; - data_dims[1] = {pars.yp.size()}; - data_dims[2] = {pars.Ndet}; - - hsize_t rx_dim[1] = {pars.xp.size()}; - hsize_t ry_dim[1] = {pars.yp.size()}; - hsize_t bin_dim[1] = {pars.Ndet}; - - for(auto n = 0; n < numLayers; n++){ - //create slice group - std::string nth_name = base_name + getDigitString(n); - H5::Group VD_slice_n(realslices.createGroup(nth_name.c_str())); - - //write group type attribute - H5::DataSpace attr1_dataspace(H5S_SCALAR); - H5::Attribute emd_group_type = VD_slice_n.createAttribute("emd_group_type",H5::PredType::NATIVE_INT,attr1_dataspace); - int group_type = 2; - emd_group_type.write(H5::PredType::NATIVE_INT, &group_type); - - //write metadata attribute - H5::DataSpace attr2_dataspace(H5S_SCALAR); - H5::Attribute metadata_group = VD_slice_n.createAttribute("metadata",H5::PredType::NATIVE_INT,attr2_dataspace); - int mgroup = 0; - metadata_group.write(H5::PredType::NATIVE_INT, &mgroup); - - //write depth attribute - - //create datasets - H5::DataSpace mspace(3,data_dims); //rank is 2 for each realslice - H5::DataSet VD_data = VD_slice_n.createDataSet("realslice",H5::PredType::NATIVE_DOUBLE,mspace); - VD_data.close(); - mspace.close(); - - //write dimensions - H5::DataSpace str_name_ds(H5S_SCALAR); - H5::StrType strdatatype(H5::PredType::C_S1,256); - - H5::DataSpace dim1_mspace(1,rx_dim); - H5::DataSpace dim2_mspace(1,ry_dim); - H5::DataSpace dim3_mspace(1,bin_dim); - - H5::DataSet dim1 = VD_slice_n.createDataSet("dim1",H5::PredType::NATIVE_DOUBLE,dim1_mspace); - H5::DataSet dim2 = VD_slice_n.createDataSet("dim2",H5::PredType::NATIVE_DOUBLE,dim2_mspace); - H5::DataSet dim3 = VD_slice_n.createDataSet("dim3",H5::PredType::NATIVE_DOUBLE,dim3_mspace); - - H5::DataSpace dim1_fspace = dim1.getSpace(); - H5::DataSpace dim2_fspace = dim2.getSpace(); - H5::DataSpace dim3_fspace = dim3.getSpace(); - - dim1.write(&pars.xp[0],H5::PredType::NATIVE_DOUBLE,dim1_mspace,dim1_fspace); - dim2.write(&pars.yp[0],H5::PredType::NATIVE_DOUBLE,dim2_mspace,dim2_fspace); - dim3.write(&pars.detectorAngles[0],H5::PredType::NATIVE_DOUBLE,dim3_mspace,dim3_fspace); - - //dimension attributes - const H5std_string dim1_name_str("R_x"); - const H5std_string dim2_name_str("R_y"); - const H5std_string dim3_name_str("bin_outer_angle"); - - H5::Attribute dim1_name = dim1.createAttribute("name",strdatatype,str_name_ds); - H5::Attribute dim2_name = dim2.createAttribute("name",strdatatype,str_name_ds); - H5::Attribute dim3_name = dim3.createAttribute("name",strdatatype,str_name_ds); - - dim1_name.write(strdatatype,dim1_name_str); - dim2_name.write(strdatatype,dim2_name_str); - dim3_name.write(strdatatype,dim3_name_str); - - const H5std_string dim1_unit_str("[n_m]"); - const H5std_string dim2_unit_str("[n_m]"); - const H5std_string dim3_unit_str("[mrad]"); - - H5::Attribute dim1_unit = dim1.createAttribute("units",strdatatype,str_name_ds); - H5::Attribute dim2_unit = dim2.createAttribute("units",strdatatype,str_name_ds); - H5::Attribute dim3_unit = dim3.createAttribute("units",strdatatype,str_name_ds); - - dim1_unit.write(strdatatype,dim1_unit_str); - dim2_unit.write(strdatatype,dim2_unit_str); - dim3_unit.write(strdatatype,dim3_unit_str); - - VD_slice_n.close(); - } + dim1.write(&pars.xp[0], H5::PredType::NATIVE_FLOAT, dim1_mspace, dim1_fspace); + dim2.write(&pars.yp[0], H5::PredType::NATIVE_FLOAT, dim2_mspace, dim2_fspace); - realslices.close(); - }; - - void setup2DOutput(Prismatic::Parameters pars, const size_t numLayers, const float dummy){ - H5::Group realslices = pars.outputFile.openGroup("4DSTEM_simulation/data/realslices"); - - //shared properties - std::string base_name = "annular_detector_depth"; - hsize_t attr_dims[1] = {1}; - hsize_t data_dims[2]; - data_dims[0] = {pars.xp.size()}; - data_dims[1] = {pars.yp.size()}; - - hsize_t rx_dim[1] = {pars.xp.size()}; - hsize_t ry_dim[1] = {pars.yp.size()}; - //TODO: get data about detecor bin dimensions - - for(auto n = 0; n < numLayers; n++){ - //create slice group - std::string nth_name = base_name + getDigitString(n); - H5::Group annular_slice_n(realslices.createGroup(nth_name.c_str())); - - //write group type attribute - H5::DataSpace attr1_dataspace(H5S_SCALAR); - H5::Attribute emd_group_type = annular_slice_n.createAttribute("emd_group_type",H5::PredType::NATIVE_INT,attr1_dataspace); - int group_type = 2; - emd_group_type.write(H5::PredType::NATIVE_INT, &group_type); - - //write metadata attribute - H5::DataSpace attr2_dataspace(H5S_SCALAR); - H5::Attribute metadata_group = annular_slice_n.createAttribute("metadata",H5::PredType::NATIVE_INT,attr2_dataspace); - int mgroup = 0; - metadata_group.write(H5::PredType::NATIVE_INT, &mgroup); - - //write depth attribute - int depth = 1; - H5::DataSpace attr3_dataspace(H5S_SCALAR); - H5::Attribute depth_attr = annular_slice_n.createAttribute("depth",H5::PredType::NATIVE_INT,attr3_dataspace); - depth_attr.write(H5::PredType::NATIVE_INT, &depth); - - //create dataset - H5::DataSpace mspace(2,data_dims); //rank is 3 - H5::DataSet CBED_data = annular_slice_n.createDataSet("realslice",H5::PredType::NATIVE_FLOAT,mspace); - mspace.close(); - - //write dimensions - H5::DataSpace str_name_ds(H5S_SCALAR); - H5::StrType strdatatype(H5::PredType::C_S1,256); - - H5::DataSpace dim1_mspace(1,rx_dim); - H5::DataSpace dim2_mspace(1,ry_dim); - - H5::DataSet dim1 = annular_slice_n.createDataSet("dim1",H5::PredType::NATIVE_FLOAT,dim1_mspace); - H5::DataSet dim2 = annular_slice_n.createDataSet("dim2",H5::PredType::NATIVE_FLOAT,dim2_mspace); - - H5::DataSpace dim1_fspace = dim1.getSpace(); - H5::DataSpace dim2_fspace = dim2.getSpace(); - - dim1.write(&pars.xp[0],H5::PredType::NATIVE_FLOAT,dim1_mspace,dim1_fspace); - dim2.write(&pars.yp[0],H5::PredType::NATIVE_FLOAT,dim2_mspace,dim2_fspace); - - //dimension attributes - const H5std_string dim1_name_str("R_x"); - const H5std_string dim2_name_str("R_y"); - - H5::Attribute dim1_name = dim1.createAttribute("name",strdatatype,str_name_ds); - H5::Attribute dim2_name = dim2.createAttribute("name",strdatatype,str_name_ds); - - dim1_name.write(strdatatype,dim1_name_str); - dim2_name.write(strdatatype,dim2_name_str); - - const H5std_string dim1_unit_str("[n_m]"); - const H5std_string dim2_unit_str("[n_m]"); - - H5::Attribute dim1_unit = dim1.createAttribute("units",strdatatype,str_name_ds); - H5::Attribute dim2_unit = dim2.createAttribute("units",strdatatype,str_name_ds); - - dim1_unit.write(strdatatype,dim1_unit_str); - dim2_unit.write(strdatatype,dim2_unit_str); - - annular_slice_n.close(); - } + //dimension attributes + const H5std_string dim1_name_str("R_x"); + const H5std_string dim2_name_str("R_y"); - realslices.close(); - }; - - void setup2DOutput(Prismatic::Parameters pars, const size_t numLayers, const double dummy){ - H5::Group realslices = pars.outputFile.openGroup("4DSTEM_simulation/data/realslices"); - - //shared properties - std::string base_name = "annular_detector_depth"; - hsize_t attr_dims[1] = {1}; - hsize_t data_dims[2]; - data_dims[0] = {pars.xp.size()}; - data_dims[1] = {pars.yp.size()}; - - hsize_t rx_dim[1] = {pars.xp.size()}; - hsize_t ry_dim[1] = {pars.yp.size()}; - //TODO: get data about detecor bin dimensions - - for(auto n = 0; n < numLayers; n++){ - //create slice group - std::string nth_name = base_name + getDigitString(n); - H5::Group annular_slice_n(realslices.createGroup(nth_name.c_str())); - - //write group type attribute - H5::DataSpace attr1_dataspace(H5S_SCALAR); - H5::Attribute emd_group_type = annular_slice_n.createAttribute("emd_group_type",H5::PredType::NATIVE_INT,attr1_dataspace); - int group_type = 2; - emd_group_type.write(H5::PredType::NATIVE_INT, &group_type); - - //write metadata attribute - H5::DataSpace attr2_dataspace(H5S_SCALAR); - H5::Attribute metadata_group = annular_slice_n.createAttribute("metadata",H5::PredType::NATIVE_INT,attr2_dataspace); - int mgroup = 0; - metadata_group.write(H5::PredType::NATIVE_INT, &mgroup); - - //write depth attribute - int depth = 1; - H5::DataSpace attr3_dataspace(H5S_SCALAR); - H5::Attribute depth_attr = annular_slice_n.createAttribute("depth",H5::PredType::NATIVE_INT,attr3_dataspace); - depth_attr.write(H5::PredType::NATIVE_INT, &depth); - - //create dataset - H5::DataSpace mspace(2,data_dims); //rank is 2 - H5::DataSet CBED_data = annular_slice_n.createDataSet("realslice",H5::PredType::NATIVE_DOUBLE,mspace); - mspace.close(); - - //write dimensions - H5::DataSpace str_name_ds(H5S_SCALAR); - H5::StrType strdatatype(H5::PredType::C_S1,256); - - H5::DataSpace dim1_mspace(1,rx_dim); - H5::DataSpace dim2_mspace(1,ry_dim); - - H5::DataSet dim1 = annular_slice_n.createDataSet("dim1",H5::PredType::NATIVE_DOUBLE,dim1_mspace); - H5::DataSet dim2 = annular_slice_n.createDataSet("dim2",H5::PredType::NATIVE_DOUBLE,dim2_mspace); - - H5::DataSpace dim1_fspace = dim1.getSpace(); - H5::DataSpace dim2_fspace = dim2.getSpace(); - - dim1.write(&pars.xp[0],H5::PredType::NATIVE_DOUBLE,dim1_mspace,dim1_fspace); - dim2.write(&pars.yp[0],H5::PredType::NATIVE_DOUBLE,dim2_mspace,dim2_fspace); - - //dimension attributes - const H5std_string dim1_name_str("R_x"); - const H5std_string dim2_name_str("R_y"); - - H5::Attribute dim1_name = dim1.createAttribute("name",strdatatype,str_name_ds); - H5::Attribute dim2_name = dim2.createAttribute("name",strdatatype,str_name_ds); - - dim1_name.write(strdatatype,dim1_name_str); - dim2_name.write(strdatatype,dim2_name_str); - - const H5std_string dim1_unit_str("[n_m]"); - const H5std_string dim2_unit_str("[n_m]"); - - H5::Attribute dim1_unit = dim1.createAttribute("units",strdatatype,str_name_ds); - H5::Attribute dim2_unit = dim2.createAttribute("units",strdatatype,str_name_ds); - - dim1_unit.write(strdatatype,dim1_unit_str); - dim2_unit.write(strdatatype,dim2_unit_str); - - annular_slice_n.close(); - } + H5::Attribute dim1_name = dim1.createAttribute("name", strdatatype, str_name_ds); + H5::Attribute dim2_name = dim2.createAttribute("name", strdatatype, str_name_ds); - realslices.close(); - }; + dim1_name.write(strdatatype, dim1_name_str); + dim2_name.write(strdatatype, dim2_name_str); - void setupDPCOutput(Prismatic::Parameters pars, const size_t numLayers, const float dummy){ - H5::Group realslices = pars.outputFile.openGroup("4DSTEM_simulation/data/realslices"); + const H5std_string dim1_unit_str("[n_m]"); + const H5std_string dim2_unit_str("[n_m]"); - //shared properties - std::string base_name = "DPC_CoM_depth"; - hsize_t attr_dims[1] = {1}; - hsize_t data_dims[2]; - data_dims[0] = {pars.xp.size()}; - data_dims[1] = {pars.yp.size()}; + H5::Attribute dim1_unit = dim1.createAttribute("units", strdatatype, str_name_ds); + H5::Attribute dim2_unit = dim2.createAttribute("units", strdatatype, str_name_ds); - hsize_t rx_dim[1] = {pars.xp.size()}; - hsize_t ry_dim[1] = {pars.yp.size()}; + dim1_unit.write(strdatatype, dim1_unit_str); + dim2_unit.write(strdatatype, dim2_unit_str); - for(auto n = 0; n < numLayers; n++){ - //create slice group - std::string nth_name = base_name + getDigitString(n); - H5::Group DPC_CoM_slice_n(realslices.createGroup(nth_name.c_str())); - - //write group type attribute - H5::DataSpace attr1_dataspace(H5S_SCALAR); - H5::Attribute emd_group_type = DPC_CoM_slice_n.createAttribute("emd_group_type",H5::PredType::NATIVE_INT,attr1_dataspace); - int group_type = 2; - emd_group_type.write(H5::PredType::NATIVE_INT, &group_type); + annular_slice_n.close(); + } - //write metadata attribute - H5::DataSpace attr2_dataspace(H5S_SCALAR); - H5::Attribute metadata_group = DPC_CoM_slice_n.createAttribute("metadata",H5::PredType::NATIVE_INT,attr2_dataspace); - int mgroup = 0; - metadata_group.write(H5::PredType::NATIVE_INT, &mgroup); + realslices.close(); +}; + +void setup2DOutput(Prismatic::Parameters pars, const size_t numLayers, const double dummy) +{ + H5::Group realslices = pars.outputFile.openGroup("4DSTEM_simulation/data/realslices"); + + //shared properties + std::string base_name = "annular_detector_depth"; + hsize_t attr_dims[1] = {1}; + hsize_t data_dims[2]; + data_dims[0] = {pars.xp.size()}; + data_dims[1] = {pars.yp.size()}; + + hsize_t rx_dim[1] = {pars.xp.size()}; + hsize_t ry_dim[1] = {pars.yp.size()}; + //TODO: get data about detecor bin dimensions + + for (auto n = 0; n < numLayers; n++) + { + //create slice group + std::string nth_name = base_name + getDigitString(n); + H5::Group annular_slice_n(realslices.createGroup(nth_name.c_str())); + + //write group type attribute + H5::DataSpace attr1_dataspace(H5S_SCALAR); + H5::Attribute emd_group_type = annular_slice_n.createAttribute("emd_group_type", H5::PredType::NATIVE_INT, attr1_dataspace); + int group_type = 2; + emd_group_type.write(H5::PredType::NATIVE_INT, &group_type); + + //write metadata attribute + H5::DataSpace attr2_dataspace(H5S_SCALAR); + H5::Attribute metadata_group = annular_slice_n.createAttribute("metadata", H5::PredType::NATIVE_INT, attr2_dataspace); + int mgroup = 0; + metadata_group.write(H5::PredType::NATIVE_INT, &mgroup); + + //write depth attribute + int depth = 1; + H5::DataSpace attr3_dataspace(H5S_SCALAR); + H5::Attribute depth_attr = annular_slice_n.createAttribute("depth", H5::PredType::NATIVE_INT, attr3_dataspace); + depth_attr.write(H5::PredType::NATIVE_INT, &depth); + + //create dataset + H5::DataSpace mspace(2, data_dims); //rank is 2 + H5::DataSet CBED_data = annular_slice_n.createDataSet("realslice", H5::PredType::NATIVE_DOUBLE, mspace); + mspace.close(); - //write depth attribute - H5::DataSpace attr3_dataspace(H5S_SCALAR); - H5::Attribute depth_attr = DPC_CoM_slice_n.createAttribute("depth",H5::PredType::NATIVE_INT,attr3_dataspace); - int depth = 2; - depth_attr.write(H5::PredType::NATIVE_INT, &depth); + //write dimensions + H5::DataSpace str_name_ds(H5S_SCALAR); + H5::StrType strdatatype(H5::PredType::C_S1, 256); - //create dataset - H5::DataSpace mspace(2, data_dims); //rank is 2 - H5::DataSet DPC_x = DPC_CoM_slice_n.createDataSet("DPC_CoM_x",H5::PredType::NATIVE_FLOAT,mspace); - H5::DataSet DPC_y = DPC_CoM_slice_n.createDataSet("DPC_CoM_y",H5::PredType::NATIVE_FLOAT,mspace); + H5::DataSpace dim1_mspace(1, rx_dim); + H5::DataSpace dim2_mspace(1, ry_dim); - mspace.close(); + H5::DataSet dim1 = annular_slice_n.createDataSet("dim1", H5::PredType::NATIVE_DOUBLE, dim1_mspace); + H5::DataSet dim2 = annular_slice_n.createDataSet("dim2", H5::PredType::NATIVE_DOUBLE, dim2_mspace); - //write dimensions - H5::DataSpace str_name_ds(H5S_SCALAR); - H5::StrType strdatatype(H5::PredType::C_S1,256); + H5::DataSpace dim1_fspace = dim1.getSpace(); + H5::DataSpace dim2_fspace = dim2.getSpace(); - H5::DataSpace dim1_mspace(1,rx_dim); - H5::DataSpace dim2_mspace(1,ry_dim); + dim1.write(&pars.xp[0], H5::PredType::NATIVE_DOUBLE, dim1_mspace, dim1_fspace); + dim2.write(&pars.yp[0], H5::PredType::NATIVE_DOUBLE, dim2_mspace, dim2_fspace); - H5::DataSet dim1 = DPC_CoM_slice_n.createDataSet("dim1",H5::PredType::NATIVE_FLOAT,dim1_mspace); - H5::DataSet dim2 = DPC_CoM_slice_n.createDataSet("dim2",H5::PredType::NATIVE_FLOAT,dim2_mspace); + //dimension attributes + const H5std_string dim1_name_str("R_x"); + const H5std_string dim2_name_str("R_y"); - H5::DataSpace dim1_fspace = dim1.getSpace(); - H5::DataSpace dim2_fspace = dim2.getSpace(); + H5::Attribute dim1_name = dim1.createAttribute("name", strdatatype, str_name_ds); + H5::Attribute dim2_name = dim2.createAttribute("name", strdatatype, str_name_ds); + dim1_name.write(strdatatype, dim1_name_str); + dim2_name.write(strdatatype, dim2_name_str); - dim1.write(&pars.xp[0],H5::PredType::NATIVE_FLOAT,dim1_mspace,dim1_fspace); - dim2.write(&pars.yp[0],H5::PredType::NATIVE_FLOAT,dim2_mspace,dim2_fspace); - - //dimension attributes - const H5std_string dim1_name_str("R_x"); - const H5std_string dim2_name_str("R_y"); + const H5std_string dim1_unit_str("[n_m]"); + const H5std_string dim2_unit_str("[n_m]"); - H5::Attribute dim1_name = dim1.createAttribute("name",strdatatype,str_name_ds); - H5::Attribute dim2_name = dim2.createAttribute("name",strdatatype,str_name_ds); + H5::Attribute dim1_unit = dim1.createAttribute("units", strdatatype, str_name_ds); + H5::Attribute dim2_unit = dim2.createAttribute("units", strdatatype, str_name_ds); - dim1_name.write(strdatatype,dim1_name_str); - dim2_name.write(strdatatype,dim2_name_str); + dim1_unit.write(strdatatype, dim1_unit_str); + dim2_unit.write(strdatatype, dim2_unit_str); - const H5std_string dim1_unit_str("[n_m]"); - const H5std_string dim2_unit_str("[n_m]"); + annular_slice_n.close(); + } - H5::Attribute dim1_unit = dim1.createAttribute("units",strdatatype,str_name_ds); - H5::Attribute dim2_unit = dim2.createAttribute("units",strdatatype,str_name_ds); + realslices.close(); +}; + +void setupDPCOutput(Prismatic::Parameters pars, const size_t numLayers, const float dummy) +{ + H5::Group realslices = pars.outputFile.openGroup("4DSTEM_simulation/data/realslices"); + + //shared properties + std::string base_name = "DPC_CoM_depth"; + hsize_t attr_dims[1] = {1}; + hsize_t data_dims[2]; + data_dims[0] = {pars.xp.size()}; + data_dims[1] = {pars.yp.size()}; + + hsize_t rx_dim[1] = {pars.xp.size()}; + hsize_t ry_dim[1] = {pars.yp.size()}; + + for (auto n = 0; n < numLayers; n++) + { + //create slice group + std::string nth_name = base_name + getDigitString(n); + H5::Group DPC_CoM_slice_n(realslices.createGroup(nth_name.c_str())); + + //write group type attribute + H5::DataSpace attr1_dataspace(H5S_SCALAR); + H5::Attribute emd_group_type = DPC_CoM_slice_n.createAttribute("emd_group_type", H5::PredType::NATIVE_INT, attr1_dataspace); + int group_type = 2; + emd_group_type.write(H5::PredType::NATIVE_INT, &group_type); + + //write metadata attribute + H5::DataSpace attr2_dataspace(H5S_SCALAR); + H5::Attribute metadata_group = DPC_CoM_slice_n.createAttribute("metadata", H5::PredType::NATIVE_INT, attr2_dataspace); + int mgroup = 0; + metadata_group.write(H5::PredType::NATIVE_INT, &mgroup); + + //write depth attribute + H5::DataSpace attr3_dataspace(H5S_SCALAR); + H5::Attribute depth_attr = DPC_CoM_slice_n.createAttribute("depth", H5::PredType::NATIVE_INT, attr3_dataspace); + int depth = 2; + depth_attr.write(H5::PredType::NATIVE_INT, &depth); + + //create dataset + H5::DataSpace mspace(2, data_dims); //rank is 2 + H5::DataSet DPC_x = DPC_CoM_slice_n.createDataSet("DPC_CoM_x", H5::PredType::NATIVE_FLOAT, mspace); + H5::DataSet DPC_y = DPC_CoM_slice_n.createDataSet("DPC_CoM_y", H5::PredType::NATIVE_FLOAT, mspace); - dim1_unit.write(strdatatype,dim1_unit_str); - dim2_unit.write(strdatatype,dim2_unit_str); + mspace.close(); - DPC_CoM_slice_n.close(); - } + //write dimensions + H5::DataSpace str_name_ds(H5S_SCALAR); + H5::StrType strdatatype(H5::PredType::C_S1, 256); - realslices.close(); - }; + H5::DataSpace dim1_mspace(1, rx_dim); + H5::DataSpace dim2_mspace(1, ry_dim); - void setupDPCOutput(Prismatic::Parameters pars, const size_t numLayers, const double dummy){ - H5::Group realslices = pars.outputFile.openGroup("4DSTEM_simulation/data/realslices"); + H5::DataSet dim1 = DPC_CoM_slice_n.createDataSet("dim1", H5::PredType::NATIVE_FLOAT, dim1_mspace); + H5::DataSet dim2 = DPC_CoM_slice_n.createDataSet("dim2", H5::PredType::NATIVE_FLOAT, dim2_mspace); - //shared properties - std::string base_name = "DPC_CoM_depth"; - hsize_t attr_dims[1] = {1}; - hsize_t data_dims[2]; - data_dims[0] = {pars.xp.size()}; - data_dims[1] = {pars.yp.size()}; + H5::DataSpace dim1_fspace = dim1.getSpace(); + H5::DataSpace dim2_fspace = dim2.getSpace(); - hsize_t rx_dim[1] = {pars.xp.size()}; - hsize_t ry_dim[1] = {pars.yp.size()}; + dim1.write(&pars.xp[0], H5::PredType::NATIVE_FLOAT, dim1_mspace, dim1_fspace); + dim2.write(&pars.yp[0], H5::PredType::NATIVE_FLOAT, dim2_mspace, dim2_fspace); - for(auto n = 0; n < numLayers; n++){ - //create slice group - std::string nth_name = base_name + getDigitString(n); - H5::Group DPC_CoM_slice_n(realslices.createGroup(nth_name.c_str())); - - //write group type attribute - H5::DataSpace attr1_dataspace(H5S_SCALAR); - H5::Attribute emd_group_type = DPC_CoM_slice_n.createAttribute("emd_group_type",H5::PredType::NATIVE_INT,attr1_dataspace); - int group_type = 2; - emd_group_type.write(H5::PredType::NATIVE_INT, &group_type); + //dimension attributes + const H5std_string dim1_name_str("R_x"); + const H5std_string dim2_name_str("R_y"); - //write metadata attribute - H5::DataSpace attr2_dataspace(H5S_SCALAR); - H5::Attribute metadata_group = DPC_CoM_slice_n.createAttribute("metadata",H5::PredType::NATIVE_INT,attr2_dataspace); - int mgroup = 0; - metadata_group.write(H5::PredType::NATIVE_INT, &mgroup); + H5::Attribute dim1_name = dim1.createAttribute("name", strdatatype, str_name_ds); + H5::Attribute dim2_name = dim2.createAttribute("name", strdatatype, str_name_ds); - //write depth attribute - H5::DataSpace attr3_dataspace(H5S_SCALAR); - H5::Attribute depth_attr = DPC_CoM_slice_n.createAttribute("depth",H5::PredType::NATIVE_INT,attr3_dataspace); - int depth = 2; - depth_attr.write(H5::PredType::NATIVE_INT, &depth); + dim1_name.write(strdatatype, dim1_name_str); + dim2_name.write(strdatatype, dim2_name_str); - //create dataset - H5::DataSpace mspace(2, data_dims); //rank is 2 - H5::DataSet DPC_x = DPC_CoM_slice_n.createDataSet("DPC_CoM_x",H5::PredType::NATIVE_DOUBLE,mspace); - H5::DataSet DPC_y = DPC_CoM_slice_n.createDataSet("DPC_CoM_y",H5::PredType::NATIVE_DOUBLE,mspace); + const H5std_string dim1_unit_str("[n_m]"); + const H5std_string dim2_unit_str("[n_m]"); - mspace.close(); + H5::Attribute dim1_unit = dim1.createAttribute("units", strdatatype, str_name_ds); + H5::Attribute dim2_unit = dim2.createAttribute("units", strdatatype, str_name_ds); - //write dimensions - H5::DataSpace str_name_ds(H5S_SCALAR); - H5::StrType strdatatype(H5::PredType::C_S1,256); + dim1_unit.write(strdatatype, dim1_unit_str); + dim2_unit.write(strdatatype, dim2_unit_str); - H5::DataSpace dim1_mspace(1,rx_dim); - H5::DataSpace dim2_mspace(1,ry_dim); + DPC_CoM_slice_n.close(); + } - H5::DataSet dim1 = DPC_CoM_slice_n.createDataSet("dim1",H5::PredType::NATIVE_DOUBLE,dim1_mspace); - H5::DataSet dim2 = DPC_CoM_slice_n.createDataSet("dim2",H5::PredType::NATIVE_DOUBLE,dim2_mspace); + realslices.close(); +}; + +void setupDPCOutput(Prismatic::Parameters pars, const size_t numLayers, const double dummy) +{ + H5::Group realslices = pars.outputFile.openGroup("4DSTEM_simulation/data/realslices"); + + //shared properties + std::string base_name = "DPC_CoM_depth"; + hsize_t attr_dims[1] = {1}; + hsize_t data_dims[2]; + data_dims[0] = {pars.xp.size()}; + data_dims[1] = {pars.yp.size()}; + + hsize_t rx_dim[1] = {pars.xp.size()}; + hsize_t ry_dim[1] = {pars.yp.size()}; + + for (auto n = 0; n < numLayers; n++) + { + //create slice group + std::string nth_name = base_name + getDigitString(n); + H5::Group DPC_CoM_slice_n(realslices.createGroup(nth_name.c_str())); + + //write group type attribute + H5::DataSpace attr1_dataspace(H5S_SCALAR); + H5::Attribute emd_group_type = DPC_CoM_slice_n.createAttribute("emd_group_type", H5::PredType::NATIVE_INT, attr1_dataspace); + int group_type = 2; + emd_group_type.write(H5::PredType::NATIVE_INT, &group_type); + + //write metadata attribute + H5::DataSpace attr2_dataspace(H5S_SCALAR); + H5::Attribute metadata_group = DPC_CoM_slice_n.createAttribute("metadata", H5::PredType::NATIVE_INT, attr2_dataspace); + int mgroup = 0; + metadata_group.write(H5::PredType::NATIVE_INT, &mgroup); + + //write depth attribute + H5::DataSpace attr3_dataspace(H5S_SCALAR); + H5::Attribute depth_attr = DPC_CoM_slice_n.createAttribute("depth", H5::PredType::NATIVE_INT, attr3_dataspace); + int depth = 2; + depth_attr.write(H5::PredType::NATIVE_INT, &depth); + + //create dataset + H5::DataSpace mspace(2, data_dims); //rank is 2 + H5::DataSet DPC_x = DPC_CoM_slice_n.createDataSet("DPC_CoM_x", H5::PredType::NATIVE_DOUBLE, mspace); + H5::DataSet DPC_y = DPC_CoM_slice_n.createDataSet("DPC_CoM_y", H5::PredType::NATIVE_DOUBLE, mspace); - H5::DataSpace dim1_fspace = dim1.getSpace(); - H5::DataSpace dim2_fspace = dim2.getSpace(); + mspace.close(); + //write dimensions + H5::DataSpace str_name_ds(H5S_SCALAR); + H5::StrType strdatatype(H5::PredType::C_S1, 256); - dim1.write(&pars.xp[0],H5::PredType::NATIVE_DOUBLE,dim1_mspace,dim1_fspace); - dim2.write(&pars.yp[0],H5::PredType::NATIVE_DOUBLE,dim2_mspace,dim2_fspace); - - //dimension attributes - const H5std_string dim1_name_str("R_x"); - const H5std_string dim2_name_str("R_y"); + H5::DataSpace dim1_mspace(1, rx_dim); + H5::DataSpace dim2_mspace(1, ry_dim); - H5::Attribute dim1_name = dim1.createAttribute("name",strdatatype,str_name_ds); - H5::Attribute dim2_name = dim2.createAttribute("name",strdatatype,str_name_ds); + H5::DataSet dim1 = DPC_CoM_slice_n.createDataSet("dim1", H5::PredType::NATIVE_DOUBLE, dim1_mspace); + H5::DataSet dim2 = DPC_CoM_slice_n.createDataSet("dim2", H5::PredType::NATIVE_DOUBLE, dim2_mspace); - dim1_name.write(strdatatype,dim1_name_str); - dim2_name.write(strdatatype,dim2_name_str); + H5::DataSpace dim1_fspace = dim1.getSpace(); + H5::DataSpace dim2_fspace = dim2.getSpace(); - const H5std_string dim1_unit_str("[n_m]"); - const H5std_string dim2_unit_str("[n_m]"); + dim1.write(&pars.xp[0], H5::PredType::NATIVE_DOUBLE, dim1_mspace, dim1_fspace); + dim2.write(&pars.yp[0], H5::PredType::NATIVE_DOUBLE, dim2_mspace, dim2_fspace); - H5::Attribute dim1_unit = dim1.createAttribute("units",strdatatype,str_name_ds); - H5::Attribute dim2_unit = dim2.createAttribute("units",strdatatype,str_name_ds); + //dimension attributes + const H5std_string dim1_name_str("R_x"); + const H5std_string dim2_name_str("R_y"); - dim1_unit.write(strdatatype,dim1_unit_str); - dim2_unit.write(strdatatype,dim2_unit_str); + H5::Attribute dim1_name = dim1.createAttribute("name", strdatatype, str_name_ds); + H5::Attribute dim2_name = dim2.createAttribute("name", strdatatype, str_name_ds); - DPC_CoM_slice_n.close(); - } + dim1_name.write(strdatatype, dim1_name_str); + dim2_name.write(strdatatype, dim2_name_str); - realslices.close(); - }; + const H5std_string dim1_unit_str("[n_m]"); + const H5std_string dim2_unit_str("[n_m]"); - void writeRealSlice(H5::DataSet dataset, const float* buffer, const hsize_t* mdims){ - H5::DataSpace fspace = dataset.getSpace(); //all realslices have data written all at once - H5::DataSpace mspace(2,mdims); //rank = 2 + H5::Attribute dim1_unit = dim1.createAttribute("units", strdatatype, str_name_ds); + H5::Attribute dim2_unit = dim2.createAttribute("units", strdatatype, str_name_ds); - dataset.write(buffer,H5::PredType::NATIVE_FLOAT,mspace,fspace); + dim1_unit.write(strdatatype, dim1_unit_str); + dim2_unit.write(strdatatype, dim2_unit_str); - fspace.close(); - mspace.close(); + DPC_CoM_slice_n.close(); } - - void writeRealSlice(H5::DataSet dataset, const double* buffer, const hsize_t* mdims){ - H5::DataSpace fspace = dataset.getSpace(); //all realslices have data written all at once - H5::DataSpace mspace(2,mdims); //rank = 2 - dataset.write(buffer,H5::PredType::NATIVE_DOUBLE,mspace,fspace); + realslices.close(); +}; - fspace.close(); - mspace.close(); - } +void writeRealSlice(H5::DataSet dataset, const float *buffer, const hsize_t *mdims) +{ + H5::DataSpace fspace = dataset.getSpace(); //all realslices have data written all at once + H5::DataSpace mspace(2, mdims); //rank = 2 - void writeDatacube3D(H5::DataSet dataset, const float* buffer, const hsize_t* mdims){ - //set up file and memory spaces - H5::DataSpace fspace = dataset.getSpace(); //all 3D cubes will write full buffer at once - H5::DataSpace mspace(3,mdims); //rank = 3 + dataset.write(buffer, H5::PredType::NATIVE_FLOAT, mspace, fspace); - dataset.write(buffer,H5::PredType::NATIVE_FLOAT,mspace,fspace); + fspace.close(); + mspace.close(); +} - fspace.close(); - mspace.close(); - }; +void writeRealSlice(H5::DataSet dataset, const double *buffer, const hsize_t *mdims) +{ + H5::DataSpace fspace = dataset.getSpace(); //all realslices have data written all at once + H5::DataSpace mspace(2, mdims); //rank = 2 - void writeDatacube3D(H5::DataSet dataset, const double* buffer, const hsize_t* mdims){ - //set up file and memory spaces - H5::DataSpace fspace = dataset.getSpace(); //all 3D cubes will write full buffer at once - H5::DataSpace mspace(3,mdims); //rank = 3 + dataset.write(buffer, H5::PredType::NATIVE_DOUBLE, mspace, fspace); - dataset.write(buffer,H5::PredType::NATIVE_DOUBLE,mspace,fspace); + fspace.close(); + mspace.close(); +} - fspace.close(); - mspace.close(); - }; - - //for 4D writes, need to first read the data set and then add; this way, FP are accounted for - void writeDatacube4D(H5::DataSet dataset, float* buffer, const hsize_t* mdims, const hsize_t* offset, const float numFP){ - //set up file and memory spaces - H5::DataSpace fspace = dataset.getSpace(); - H5::DataSpace mspace(4,mdims); //rank = 4 - - fspace.selectHyperslab(H5S_SELECT_SET,mdims,offset); - - //divide by num FP - for(auto i = 0; i < mdims[0]*mdims[1]*mdims[2]*mdims[3]; i++) buffer[i] /= numFP; - - //add frozen phonon set - float* readBuffer = (float*) malloc(mdims[0]*mdims[1]*mdims[2]*mdims[3]*sizeof(float)); - dataset.read(&readBuffer[0],H5::PredType::NATIVE_FLOAT,mspace,fspace); - for(auto i = 0; i < mdims[0]*mdims[1]*mdims[2]*mdims[3]; i++) buffer[i] += readBuffer[i]; - free(readBuffer); - - //restride the dataset so that qx and qy are flipped - float* finalBuffer = (float*) malloc(mdims[0]*mdims[1]*mdims[2]*mdims[3]*sizeof(float)); - for(auto i = 0; i < mdims[2]; i++){ - for(auto j = 0; j < mdims[3]; j++){ - finalBuffer[i*mdims[3]+j] = buffer[j*mdims[2]+i]; - } +void writeDatacube3D(H5::DataSet dataset, const float *buffer, const hsize_t *mdims) +{ + //set up file and memory spaces + H5::DataSpace fspace = dataset.getSpace(); //all 3D cubes will write full buffer at once + H5::DataSpace mspace(3, mdims); //rank = 3 + + dataset.write(buffer, H5::PredType::NATIVE_FLOAT, mspace, fspace); + + fspace.close(); + mspace.close(); +}; + +void writeDatacube3D(H5::DataSet dataset, const double *buffer, const hsize_t *mdims) +{ + //set up file and memory spaces + H5::DataSpace fspace = dataset.getSpace(); //all 3D cubes will write full buffer at once + H5::DataSpace mspace(3, mdims); //rank = 3 + + dataset.write(buffer, H5::PredType::NATIVE_DOUBLE, mspace, fspace); + + fspace.close(); + mspace.close(); +}; + +//for 4D writes, need to first read the data set and then add; this way, FP are accounted for +void writeDatacube4D(H5::DataSet dataset, float *buffer, const hsize_t *mdims, const hsize_t *offset, const float numFP) +{ + //set up file and memory spaces + H5::DataSpace fspace = dataset.getSpace(); + H5::DataSpace mspace(4, mdims); //rank = 4 + + fspace.selectHyperslab(H5S_SELECT_SET, mdims, offset); + + //divide by num FP + for (auto i = 0; i < mdims[0] * mdims[1] * mdims[2] * mdims[3]; i++) + buffer[i] /= numFP; + + //add frozen phonon set + float *readBuffer = (float *)malloc(mdims[0] * mdims[1] * mdims[2] * mdims[3] * sizeof(float)); + dataset.read(&readBuffer[0], H5::PredType::NATIVE_FLOAT, mspace, fspace); + for (auto i = 0; i < mdims[0] * mdims[1] * mdims[2] * mdims[3]; i++) + buffer[i] += readBuffer[i]; + free(readBuffer); + + //restride the dataset so that qx and qy are flipped + float *finalBuffer = (float *)malloc(mdims[0] * mdims[1] * mdims[2] * mdims[3] * sizeof(float)); + for (auto i = 0; i < mdims[2]; i++) + { + for (auto j = 0; j < mdims[3]; j++) + { + finalBuffer[i * mdims[3] + j] = buffer[j * mdims[2] + i]; } + } - dataset.write(finalBuffer,H5::PredType::NATIVE_FLOAT,mspace,fspace); - free(finalBuffer); - fspace.close(); - mspace.close(); - }; - - void writeDatacube4D(H5::DataSet dataset, double* buffer, const hsize_t* mdims, const hsize_t* offset, const double numFP){ - //set up file and memory spaces - H5::DataSpace fspace = dataset.getSpace(); - H5::DataSpace mspace(4,mdims); //rank = 4 - - fspace.selectHyperslab(H5S_SELECT_SET,mdims,offset); - - //divide by num FP - for(auto i = 0; i < mdims[0]*mdims[1]*mdims[2]*mdims[3]; i++) buffer[i] /= numFP; - - //add frozen phonon set - double * readBuffer = (double*) malloc(mdims[0]*mdims[1]*mdims[2]*mdims[3]*sizeof(double)); - dataset.read(&readBuffer[0],H5::PredType::NATIVE_DOUBLE,mspace,fspace); - for(auto i = 0; i < mdims[0]*mdims[1]*mdims[2]*mdims[3]; i++) buffer[i] += readBuffer[i]; - free(readBuffer); - - //restride the dataset so that qx and qy are flipped - double* finalBuffer = (double*) malloc(mdims[0]*mdims[1]*mdims[2]*mdims[3]*sizeof(double)); - for(auto i = 0; i < mdims[2]; i++){ - for(auto j = 0; j < mdims[3]; j++){ - finalBuffer[i*mdims[3]+j] = buffer[j*mdims[2]+i]; - } + dataset.write(finalBuffer, H5::PredType::NATIVE_FLOAT, mspace, fspace); + free(finalBuffer); + fspace.close(); + mspace.close(); +}; + +void writeDatacube4D(H5::DataSet dataset, double *buffer, const hsize_t *mdims, const hsize_t *offset, const double numFP) +{ + //set up file and memory spaces + H5::DataSpace fspace = dataset.getSpace(); + H5::DataSpace mspace(4, mdims); //rank = 4 + + fspace.selectHyperslab(H5S_SELECT_SET, mdims, offset); + + //divide by num FP + for (auto i = 0; i < mdims[0] * mdims[1] * mdims[2] * mdims[3]; i++) + buffer[i] /= numFP; + + //add frozen phonon set + double *readBuffer = (double *)malloc(mdims[0] * mdims[1] * mdims[2] * mdims[3] * sizeof(double)); + dataset.read(&readBuffer[0], H5::PredType::NATIVE_DOUBLE, mspace, fspace); + for (auto i = 0; i < mdims[0] * mdims[1] * mdims[2] * mdims[3]; i++) + buffer[i] += readBuffer[i]; + free(readBuffer); + + //restride the dataset so that qx and qy are flipped + double *finalBuffer = (double *)malloc(mdims[0] * mdims[1] * mdims[2] * mdims[3] * sizeof(double)); + for (auto i = 0; i < mdims[2]; i++) + { + for (auto j = 0; j < mdims[3]; j++) + { + finalBuffer[i * mdims[3] + j] = buffer[j * mdims[2] + i]; } + } - dataset.write(finalBuffer,H5::PredType::NATIVE_DOUBLE,mspace,fspace); - free(finalBuffer); - fspace.close(); - mspace.close(); - }; - - std::string getDigitString(int digit){ - char buffer[20]; - sprintf(buffer,"%04d",digit); - std::string output = buffer; - return output; - }; - - void writeMetadata(Prismatic::Parameters pars, float dummy){ - //set up group - H5::Group metadata = pars.outputFile.openGroup("4DSTEM_simulation/metadata/metadata_0/original"); - H5::Group sim_params = metadata.createGroup("simulation_parameters"); - - //write all parameters as attributes - - //create common dataspaces - H5::DataSpace str_name_ds(H5S_SCALAR); //string dataspaces and types - H5::StrType strdatatype(H5::PredType::C_S1,256); - H5::DataSpace scalar_attr(H5S_SCALAR); - - //initialize string parameter data - H5std_string algorithm; - if(pars.meta.algorithm == Prismatic::Algorithm::Multislice){ - algorithm = "m"; - }else{ - algorithm = "p"; - } + dataset.write(finalBuffer, H5::PredType::NATIVE_DOUBLE, mspace, fspace); + free(finalBuffer); + fspace.close(); + mspace.close(); +}; + +std::string getDigitString(int digit) +{ + char buffer[20]; + sprintf(buffer, "%04d", digit); + std::string output = buffer; + return output; +}; + +void writeMetadata(Prismatic::Parameters pars, float dummy) +{ + //set up group + H5::Group metadata = pars.outputFile.openGroup("4DSTEM_simulation/metadata/metadata_0/original"); + H5::Group sim_params = metadata.createGroup("simulation_parameters"); + + //write all parameters as attributes + + //create common dataspaces + H5::DataSpace str_name_ds(H5S_SCALAR); //string dataspaces and types + H5::StrType strdatatype(H5::PredType::C_S1, 256); + H5::DataSpace scalar_attr(H5S_SCALAR); + + //initialize string parameter data + H5std_string algorithm; + if (pars.meta.algorithm == Prismatic::Algorithm::Multislice) + { + algorithm = "m"; + } + else + { + algorithm = "p"; + } - const H5std_string filenameAtoms(pars.meta.filenameAtoms); - - //create string attributes - H5::Attribute atoms_attr = sim_params.createAttribute("i",strdatatype,str_name_ds); - H5::Attribute alg_attr = sim_params.createAttribute("a",strdatatype,str_name_ds); - - //create scalar logical/integer attributes - H5::Attribute fx_attr = sim_params.createAttribute("fx",H5::PredType::NATIVE_INT,scalar_attr); - H5::Attribute fy_attr = sim_params.createAttribute("fy",H5::PredType::NATIVE_INT,scalar_attr); - H5::Attribute numFP_attr = sim_params.createAttribute("F",H5::PredType::NATIVE_INT,scalar_attr); - H5::Attribute numSlices_attr = sim_params.createAttribute("ns",H5::PredType::NATIVE_INT,scalar_attr); - H5::Attribute te_attr = sim_params.createAttribute("te",H5::PredType::NATIVE_INT,scalar_attr); - H5::Attribute oc_attr = sim_params.createAttribute("oc",H5::PredType::NATIVE_INT,scalar_attr); - H5::Attribute save3D_attr = sim_params.createAttribute("3D",H5::PredType::NATIVE_INT,scalar_attr); - H5::Attribute save4D_attr = sim_params.createAttribute("4D",H5::PredType::NATIVE_INT,scalar_attr); - H5::Attribute saveDPC_attr = sim_params.createAttribute("DPC",H5::PredType::NATIVE_INT,scalar_attr); - H5::Attribute savePS_attr = sim_params.createAttribute("ps",H5::PredType::NATIVE_INT,scalar_attr); - H5::Attribute nyquist_attr = sim_params.createAttribute("nqs",H5::PredType::NATIVE_INT,scalar_attr); - - //create scalar float/double attributes (changes based on prismatic float precision) - H5::Attribute px_attr = sim_params.createAttribute("px",H5::PredType::NATIVE_FLOAT,scalar_attr); - H5::Attribute py_attr = sim_params.createAttribute("py",H5::PredType::NATIVE_FLOAT,scalar_attr); - H5::Attribute potBound_attr = sim_params.createAttribute("P",H5::PredType::NATIVE_FLOAT,scalar_attr); - H5::Attribute sliceThickness_attr = sim_params.createAttribute("s",H5::PredType::NATIVE_FLOAT,scalar_attr); - H5::Attribute zStart_attr = sim_params.createAttribute("zs",H5::PredType::NATIVE_FLOAT,scalar_attr); - H5::Attribute E0_attr = sim_params.createAttribute("E",H5::PredType::NATIVE_FLOAT,scalar_attr); - H5::Attribute alphaMax_attr = sim_params.createAttribute("A",H5::PredType::NATIVE_FLOAT,scalar_attr); - H5::Attribute rx_attr = sim_params.createAttribute("rx",H5::PredType::NATIVE_FLOAT,scalar_attr); - H5::Attribute ry_attr = sim_params.createAttribute("ry",H5::PredType::NATIVE_FLOAT,scalar_attr); - H5::Attribute df_attr = sim_params.createAttribute("df",H5::PredType::NATIVE_FLOAT,scalar_attr); - H5::Attribute C3_attr = sim_params.createAttribute("C3",H5::PredType::NATIVE_FLOAT,scalar_attr); - H5::Attribute C5_attr = sim_params.createAttribute("C5",H5::PredType::NATIVE_FLOAT,scalar_attr); - H5::Attribute semiangle_attr = sim_params.createAttribute("sa",H5::PredType::NATIVE_FLOAT,scalar_attr); - H5::Attribute detector_attr = sim_params.createAttribute("d",H5::PredType::NATIVE_FLOAT,scalar_attr); - H5::Attribute tx_attr = sim_params.createAttribute("tx",H5::PredType::NATIVE_FLOAT,scalar_attr); - H5::Attribute ty_attr = sim_params.createAttribute("ty",H5::PredType::NATIVE_FLOAT,scalar_attr); - - //create vector spaces - hsize_t two[1] = {2}; - hsize_t three[1] = {3}; - H5::DataSpace v_two_dataspace(1,two); - H5::DataSpace v_three_dataspace(1,three); - - H5::Attribute cell_dim_attr = sim_params.createAttribute("c",H5::PredType::NATIVE_FLOAT,v_three_dataspace); - H5::Attribute tile_attr = sim_params.createAttribute("t",H5::PredType::NATIVE_FLOAT,v_three_dataspace); - H5::Attribute scanWindow_x_attr = sim_params.createAttribute("wx",H5::PredType::NATIVE_FLOAT,v_two_dataspace); - H5::Attribute scanWindow_y_attr = sim_params.createAttribute("wy",H5::PredType::NATIVE_FLOAT,v_two_dataspace); - - H5::Attribute scanWindow_x_r_attr; - H5::Attribute scanWindow_y_r_attr; - if(pars.meta.realSpaceWindow_x) scanWindow_x_r_attr = sim_params.createAttribute("wxr",H5::PredType::NATIVE_FLOAT,v_two_dataspace); - if(pars.meta.realSpaceWindow_y) scanWindow_y_r_attr = sim_params.createAttribute("wyr",H5::PredType::NATIVE_FLOAT,v_two_dataspace); - - H5::Attribute save2D_attr; - if(pars.meta.save2DOutput){ - save2D_attr = sim_params.createAttribute("2D",H5::PredType::NATIVE_FLOAT,v_two_dataspace); - } - - //write data - //strings - atoms_attr.write(strdatatype,filenameAtoms); - alg_attr.write(strdatatype,algorithm); - - //scalar logical/integers - fx_attr.write(H5::PredType::NATIVE_INT, &pars.meta.interpolationFactorX); - fy_attr.write(H5::PredType::NATIVE_INT, &pars.meta.interpolationFactorY); - numFP_attr.write(H5::PredType::NATIVE_INT, &pars.meta.numFP); - numSlices_attr.write(H5::PredType::NATIVE_INT, &pars.meta.numSlices); - - //logicals first need to be cast to ints - int tmp_te = {pars.meta.includeThermalEffects}; - int tmp_oc = {pars.meta.includeOccupancy}; - int tmp_3D = {pars.meta.save3DOutput}; - int tmp_4D = {pars.meta.save4DOutput}; - int tmp_DPC = {pars.meta.saveDPC_CoM}; - int tmp_PS = {pars.meta.savePotentialSlices}; - int tmp_nqs = {pars.meta.nyquistSampling}; - - te_attr.write(H5::PredType::NATIVE_INT, &tmp_te); - oc_attr.write(H5::PredType::NATIVE_INT, &tmp_oc); - save3D_attr.write(H5::PredType::NATIVE_INT, &tmp_3D); - save4D_attr.write(H5::PredType::NATIVE_INT, &tmp_4D); - saveDPC_attr.write(H5::PredType::NATIVE_INT, &tmp_DPC); - savePS_attr.write(H5::PredType::NATIVE_INT, &tmp_PS); - nyquist_attr.write(H5::PredType::NATIVE_INT, &tmp_nqs); - - //scalar floats/doubles - px_attr.write(H5::PredType::NATIVE_FLOAT, &pars.meta.realspacePixelSize[1]); - py_attr.write(H5::PredType::NATIVE_FLOAT, &pars.meta.realspacePixelSize[0]); - potBound_attr.write(H5::PredType::NATIVE_FLOAT, &pars.meta.potBound); - sliceThickness_attr.write(H5::PredType::NATIVE_FLOAT, &pars.meta.sliceThickness); - zStart_attr.write(H5::PredType::NATIVE_FLOAT, &pars.meta.zStart); - rx_attr.write(H5::PredType::NATIVE_FLOAT, &pars.meta.probeStepX); - ry_attr.write(H5::PredType::NATIVE_FLOAT, &pars.meta.probeStepY); - df_attr.write(H5::PredType::NATIVE_FLOAT, &pars.meta.probeDefocus); - C3_attr.write(H5::PredType::NATIVE_FLOAT, &pars.meta.C3); - C5_attr.write(H5::PredType::NATIVE_FLOAT, &pars.meta.C5); - - //scalars with unit adjustments - PRISMATIC_FLOAT_PRECISION tmp_tx[1] = {pars.meta.probeXtilt * 1000}; - PRISMATIC_FLOAT_PRECISION tmp_ty[1] = {pars.meta.probeYtilt * 1000}; - PRISMATIC_FLOAT_PRECISION tmp_E0[1] = {pars.meta.E0 / 1000}; - PRISMATIC_FLOAT_PRECISION tmp_alphaMax[1] = {pars.meta.alphaBeamMax * 1000}; - PRISMATIC_FLOAT_PRECISION tmp_sa[1] = {pars.meta.probeSemiangle * 1000}; - PRISMATIC_FLOAT_PRECISION tmp_d[1] = {pars.meta.detectorAngleStep * 1000}; - - tx_attr.write(H5::PredType::NATIVE_FLOAT, &tmp_tx); - ty_attr.write(H5::PredType::NATIVE_FLOAT, &tmp_ty); - E0_attr.write(H5::PredType::NATIVE_FLOAT, &tmp_E0); - alphaMax_attr.write(H5::PredType::NATIVE_FLOAT, &tmp_alphaMax); - semiangle_attr.write(H5::PredType::NATIVE_FLOAT, &tmp_sa); - detector_attr.write(H5::PredType::NATIVE_FLOAT, &tmp_d); - - //vector spaces - PRISMATIC_FLOAT_PRECISION tmp_buffer[2]; - - if(pars.meta.save2DOutput){ - tmp_buffer[0] = pars.meta.integrationAngleMin * 1000; - tmp_buffer[1] = pars.meta.integrationAngleMax * 1000; - save2D_attr.write(H5::PredType::NATIVE_FLOAT,tmp_buffer); - } + const H5std_string filenameAtoms(pars.meta.filenameAtoms); + + //create string attributes + H5::Attribute atoms_attr = sim_params.createAttribute("i", strdatatype, str_name_ds); + H5::Attribute alg_attr = sim_params.createAttribute("a", strdatatype, str_name_ds); + + //create scalar logical/integer attributes + H5::Attribute fx_attr = sim_params.createAttribute("fx", H5::PredType::NATIVE_INT, scalar_attr); + H5::Attribute fy_attr = sim_params.createAttribute("fy", H5::PredType::NATIVE_INT, scalar_attr); + H5::Attribute numFP_attr = sim_params.createAttribute("F", H5::PredType::NATIVE_INT, scalar_attr); + H5::Attribute numSlices_attr = sim_params.createAttribute("ns", H5::PredType::NATIVE_INT, scalar_attr); + H5::Attribute te_attr = sim_params.createAttribute("te", H5::PredType::NATIVE_INT, scalar_attr); + H5::Attribute oc_attr = sim_params.createAttribute("oc", H5::PredType::NATIVE_INT, scalar_attr); + H5::Attribute save3D_attr = sim_params.createAttribute("3D", H5::PredType::NATIVE_INT, scalar_attr); + H5::Attribute save4D_attr = sim_params.createAttribute("4D", H5::PredType::NATIVE_INT, scalar_attr); + H5::Attribute saveDPC_attr = sim_params.createAttribute("DPC", H5::PredType::NATIVE_INT, scalar_attr); + H5::Attribute savePS_attr = sim_params.createAttribute("ps", H5::PredType::NATIVE_INT, scalar_attr); + H5::Attribute nyquist_attr = sim_params.createAttribute("nqs", H5::PredType::NATIVE_INT, scalar_attr); + + //create scalar float/double attributes (changes based on prismatic float precision) + H5::Attribute px_attr = sim_params.createAttribute("px", H5::PredType::NATIVE_FLOAT, scalar_attr); + H5::Attribute py_attr = sim_params.createAttribute("py", H5::PredType::NATIVE_FLOAT, scalar_attr); + H5::Attribute potBound_attr = sim_params.createAttribute("P", H5::PredType::NATIVE_FLOAT, scalar_attr); + H5::Attribute sliceThickness_attr = sim_params.createAttribute("s", H5::PredType::NATIVE_FLOAT, scalar_attr); + H5::Attribute zStart_attr = sim_params.createAttribute("zs", H5::PredType::NATIVE_FLOAT, scalar_attr); + H5::Attribute E0_attr = sim_params.createAttribute("E", H5::PredType::NATIVE_FLOAT, scalar_attr); + H5::Attribute alphaMax_attr = sim_params.createAttribute("A", H5::PredType::NATIVE_FLOAT, scalar_attr); + H5::Attribute rx_attr = sim_params.createAttribute("rx", H5::PredType::NATIVE_FLOAT, scalar_attr); + H5::Attribute ry_attr = sim_params.createAttribute("ry", H5::PredType::NATIVE_FLOAT, scalar_attr); + H5::Attribute df_attr = sim_params.createAttribute("df", H5::PredType::NATIVE_FLOAT, scalar_attr); + H5::Attribute C3_attr = sim_params.createAttribute("C3", H5::PredType::NATIVE_FLOAT, scalar_attr); + H5::Attribute C5_attr = sim_params.createAttribute("C5", H5::PredType::NATIVE_FLOAT, scalar_attr); + H5::Attribute semiangle_attr = sim_params.createAttribute("sa", H5::PredType::NATIVE_FLOAT, scalar_attr); + H5::Attribute detector_attr = sim_params.createAttribute("d", H5::PredType::NATIVE_FLOAT, scalar_attr); + H5::Attribute tx_attr = sim_params.createAttribute("tx", H5::PredType::NATIVE_FLOAT, scalar_attr); + H5::Attribute ty_attr = sim_params.createAttribute("ty", H5::PredType::NATIVE_FLOAT, scalar_attr); + + //create vector spaces + hsize_t two[1] = {2}; + hsize_t three[1] = {3}; + H5::DataSpace v_two_dataspace(1, two); + H5::DataSpace v_three_dataspace(1, three); + + H5::Attribute cell_dim_attr = sim_params.createAttribute("c", H5::PredType::NATIVE_FLOAT, v_three_dataspace); + H5::Attribute tile_attr = sim_params.createAttribute("t", H5::PredType::NATIVE_FLOAT, v_three_dataspace); + H5::Attribute scanWindow_x_attr = sim_params.createAttribute("wx", H5::PredType::NATIVE_FLOAT, v_two_dataspace); + H5::Attribute scanWindow_y_attr = sim_params.createAttribute("wy", H5::PredType::NATIVE_FLOAT, v_two_dataspace); + + H5::Attribute scanWindow_x_r_attr; + H5::Attribute scanWindow_y_r_attr; + if (pars.meta.realSpaceWindow_x) + scanWindow_x_r_attr = sim_params.createAttribute("wxr", H5::PredType::NATIVE_FLOAT, v_two_dataspace); + if (pars.meta.realSpaceWindow_y) + scanWindow_y_r_attr = sim_params.createAttribute("wyr", H5::PredType::NATIVE_FLOAT, v_two_dataspace); + + H5::Attribute save2D_attr; + if (pars.meta.save2DOutput) + { + save2D_attr = sim_params.createAttribute("2D", H5::PredType::NATIVE_FLOAT, v_two_dataspace); + } - if(pars.meta.realSpaceWindow_x){ - tmp_buffer[0] = pars.meta.scanWindowXMin_r; - tmp_buffer[1] = pars.meta.scanWindowXMax_r; - scanWindow_x_r_attr.write(H5::PredType::NATIVE_FLOAT,tmp_buffer); - } + //write data + //strings + atoms_attr.write(strdatatype, filenameAtoms); + alg_attr.write(strdatatype, algorithm); + + //scalar logical/integers + fx_attr.write(H5::PredType::NATIVE_INT, &pars.meta.interpolationFactorX); + fy_attr.write(H5::PredType::NATIVE_INT, &pars.meta.interpolationFactorY); + numFP_attr.write(H5::PredType::NATIVE_INT, &pars.meta.numFP); + numSlices_attr.write(H5::PredType::NATIVE_INT, &pars.meta.numSlices); + + //logicals first need to be cast to ints + int tmp_te = {pars.meta.includeThermalEffects}; + int tmp_oc = {pars.meta.includeOccupancy}; + int tmp_3D = {pars.meta.save3DOutput}; + int tmp_4D = {pars.meta.save4DOutput}; + int tmp_DPC = {pars.meta.saveDPC_CoM}; + int tmp_PS = {pars.meta.savePotentialSlices}; + int tmp_nqs = {pars.meta.nyquistSampling}; + + te_attr.write(H5::PredType::NATIVE_INT, &tmp_te); + oc_attr.write(H5::PredType::NATIVE_INT, &tmp_oc); + save3D_attr.write(H5::PredType::NATIVE_INT, &tmp_3D); + save4D_attr.write(H5::PredType::NATIVE_INT, &tmp_4D); + saveDPC_attr.write(H5::PredType::NATIVE_INT, &tmp_DPC); + savePS_attr.write(H5::PredType::NATIVE_INT, &tmp_PS); + nyquist_attr.write(H5::PredType::NATIVE_INT, &tmp_nqs); + + //scalar floats/doubles + px_attr.write(H5::PredType::NATIVE_FLOAT, &pars.meta.realspacePixelSize[1]); + py_attr.write(H5::PredType::NATIVE_FLOAT, &pars.meta.realspacePixelSize[0]); + potBound_attr.write(H5::PredType::NATIVE_FLOAT, &pars.meta.potBound); + sliceThickness_attr.write(H5::PredType::NATIVE_FLOAT, &pars.meta.sliceThickness); + zStart_attr.write(H5::PredType::NATIVE_FLOAT, &pars.meta.zStart); + rx_attr.write(H5::PredType::NATIVE_FLOAT, &pars.meta.probeStepX); + ry_attr.write(H5::PredType::NATIVE_FLOAT, &pars.meta.probeStepY); + df_attr.write(H5::PredType::NATIVE_FLOAT, &pars.meta.probeDefocus); + C3_attr.write(H5::PredType::NATIVE_FLOAT, &pars.meta.C3); + C5_attr.write(H5::PredType::NATIVE_FLOAT, &pars.meta.C5); + + //scalars with unit adjustments + PRISMATIC_FLOAT_PRECISION tmp_tx[1] = {pars.meta.probeXtilt * 1000}; + PRISMATIC_FLOAT_PRECISION tmp_ty[1] = {pars.meta.probeYtilt * 1000}; + PRISMATIC_FLOAT_PRECISION tmp_E0[1] = {pars.meta.E0 / 1000}; + PRISMATIC_FLOAT_PRECISION tmp_alphaMax[1] = {pars.meta.alphaBeamMax * 1000}; + PRISMATIC_FLOAT_PRECISION tmp_sa[1] = {pars.meta.probeSemiangle * 1000}; + PRISMATIC_FLOAT_PRECISION tmp_d[1] = {pars.meta.detectorAngleStep * 1000}; + + tx_attr.write(H5::PredType::NATIVE_FLOAT, &tmp_tx); + ty_attr.write(H5::PredType::NATIVE_FLOAT, &tmp_ty); + E0_attr.write(H5::PredType::NATIVE_FLOAT, &tmp_E0); + alphaMax_attr.write(H5::PredType::NATIVE_FLOAT, &tmp_alphaMax); + semiangle_attr.write(H5::PredType::NATIVE_FLOAT, &tmp_sa); + detector_attr.write(H5::PredType::NATIVE_FLOAT, &tmp_d); + + //vector spaces + PRISMATIC_FLOAT_PRECISION tmp_buffer[2]; + + if (pars.meta.save2DOutput) + { + tmp_buffer[0] = pars.meta.integrationAngleMin * 1000; + tmp_buffer[1] = pars.meta.integrationAngleMax * 1000; + save2D_attr.write(H5::PredType::NATIVE_FLOAT, tmp_buffer); + } - if(pars.meta.realSpaceWindow_y){ - tmp_buffer[0] = pars.meta.scanWindowYMin_r; - tmp_buffer[1] = pars.meta.scanWindowYMax_r; - scanWindow_y_r_attr.write(H5::PredType::NATIVE_FLOAT,tmp_buffer); - } - - tmp_buffer[0] = pars.meta.scanWindowXMin; - tmp_buffer[1] = pars.meta.scanWindowXMax; - scanWindow_x_attr.write(H5::PredType::NATIVE_FLOAT,tmp_buffer); - - tmp_buffer[0] = pars.meta.scanWindowYMin; - tmp_buffer[1] = pars.meta.scanWindowYMax; - scanWindow_y_attr.write(H5::PredType::NATIVE_FLOAT,tmp_buffer); - - int tile_buffer[3]; - tile_buffer[0] = pars.meta.tileX; - tile_buffer[1] = pars.meta.tileY; - tile_buffer[2] = pars.meta.tileZ; - tile_attr.write(H5::PredType::NATIVE_INT,tile_buffer); - - PRISMATIC_FLOAT_PRECISION cellBuffer[3]; - cellBuffer[0] = pars.meta.cellDim[0]; - cellBuffer[1] = pars.meta.cellDim[1]; - cellBuffer[2] = pars.meta.cellDim[2]; - cell_dim_attr.write(H5::PredType::NATIVE_FLOAT,cellBuffer); - - metadata.close(); - }; - - void writeMetadata(Prismatic::Parameters pars, double dummy){ - //set up group - H5::Group metadata = pars.outputFile.openGroup("4DSTEM_simulation/metadata/metadata_0/original"); - H5::Group sim_params = metadata.createGroup("simulation_parameters"); - - //write all parameters as attributes - - //create common dataspaces - H5::DataSpace str_name_ds(H5S_SCALAR); //string dataspaces and types - H5::StrType strdatatype(H5::PredType::C_S1,256); - H5::DataSpace scalar_attr(H5S_SCALAR); - - //initialize string parameter data - H5std_string algorithm; - if(pars.meta.algorithm == Prismatic::Algorithm::Multislice){ - algorithm = "m"; - }else{ - algorithm = "p"; - } + if (pars.meta.realSpaceWindow_x) + { + tmp_buffer[0] = pars.meta.scanWindowXMin_r; + tmp_buffer[1] = pars.meta.scanWindowXMax_r; + scanWindow_x_r_attr.write(H5::PredType::NATIVE_FLOAT, tmp_buffer); + } - const H5std_string filenameAtoms(pars.meta.filenameAtoms); - - //create string attributes - H5::Attribute atoms_attr = sim_params.createAttribute("i",strdatatype,str_name_ds); - H5::Attribute alg_attr = sim_params.createAttribute("a",strdatatype,str_name_ds); - - //create scalar logical/integer attributes - H5::Attribute fx_attr = sim_params.createAttribute("fx",H5::PredType::NATIVE_INT,scalar_attr); - H5::Attribute fy_attr = sim_params.createAttribute("fy",H5::PredType::NATIVE_INT,scalar_attr); - H5::Attribute numFP_attr = sim_params.createAttribute("F",H5::PredType::NATIVE_INT,scalar_attr); - H5::Attribute numSlices_attr = sim_params.createAttribute("ns",H5::PredType::NATIVE_INT,scalar_attr); - H5::Attribute te_attr = sim_params.createAttribute("te",H5::PredType::NATIVE_INT,scalar_attr); - H5::Attribute oc_attr = sim_params.createAttribute("oc",H5::PredType::NATIVE_INT,scalar_attr); - H5::Attribute save3D_attr = sim_params.createAttribute("3D",H5::PredType::NATIVE_INT,scalar_attr); - H5::Attribute save4D_attr = sim_params.createAttribute("4D",H5::PredType::NATIVE_INT,scalar_attr); - H5::Attribute saveDPC_attr = sim_params.createAttribute("DPC",H5::PredType::NATIVE_INT,scalar_attr); - H5::Attribute savePS_attr = sim_params.createAttribute("ps",H5::PredType::NATIVE_INT,scalar_attr); - H5::Attribute nyquist_attr = sim_params.createAttribute("nqs",H5::PredType::NATIVE_INT,scalar_attr); - - //create scalar float/double attributes (changes based on prismatic float precision) - H5::Attribute px_attr = sim_params.createAttribute("px",H5::PredType::NATIVE_DOUBLE,scalar_attr); - H5::Attribute py_attr = sim_params.createAttribute("py",H5::PredType::NATIVE_DOUBLE,scalar_attr); - H5::Attribute potBound_attr = sim_params.createAttribute("P",H5::PredType::NATIVE_DOUBLE,scalar_attr); - H5::Attribute sliceThickness_attr = sim_params.createAttribute("s",H5::PredType::NATIVE_DOUBLE,scalar_attr); - H5::Attribute zStart_attr = sim_params.createAttribute("zs",H5::PredType::NATIVE_DOUBLE,scalar_attr); - H5::Attribute E0_attr = sim_params.createAttribute("E",H5::PredType::NATIVE_DOUBLE,scalar_attr); - H5::Attribute alphaMax_attr = sim_params.createAttribute("A",H5::PredType::NATIVE_DOUBLE,scalar_attr); - H5::Attribute rx_attr = sim_params.createAttribute("rx",H5::PredType::NATIVE_DOUBLE,scalar_attr); - H5::Attribute ry_attr = sim_params.createAttribute("ry",H5::PredType::NATIVE_DOUBLE,scalar_attr); - H5::Attribute df_attr = sim_params.createAttribute("df",H5::PredType::NATIVE_DOUBLE,scalar_attr); - H5::Attribute C3_attr = sim_params.createAttribute("C3",H5::PredType::NATIVE_DOUBLE,scalar_attr); - H5::Attribute C5_attr = sim_params.createAttribute("C5",H5::PredType::NATIVE_DOUBLE,scalar_attr); - H5::Attribute semiangle_attr = sim_params.createAttribute("sa",H5::PredType::NATIVE_DOUBLE,scalar_attr); - H5::Attribute detector_attr = sim_params.createAttribute("d",H5::PredType::NATIVE_DOUBLE,scalar_attr); - H5::Attribute tx_attr = sim_params.createAttribute("tx",H5::PredType::NATIVE_DOUBLE,scalar_attr); - H5::Attribute ty_attr = sim_params.createAttribute("ty",H5::PredType::NATIVE_DOUBLE,scalar_attr); - - //create vector spaces - hsize_t two[1] = {2}; - hsize_t three[1] = {3}; - H5::DataSpace v_two_dataspace(1,two); - H5::DataSpace v_three_dataspace(1,three); - - H5::Attribute cell_dim_attr = sim_params.createAttribute("c",H5::PredType::NATIVE_DOUBLE,v_three_dataspace); - H5::Attribute tile_attr = sim_params.createAttribute("t",H5::PredType::NATIVE_DOUBLE,v_three_dataspace); - H5::Attribute scanWindow_x_attr = sim_params.createAttribute("wx",H5::PredType::NATIVE_DOUBLE,v_two_dataspace); - H5::Attribute scanWindow_y_attr = sim_params.createAttribute("wy",H5::PredType::NATIVE_DOUBLE,v_two_dataspace); - - H5::Attribute scanWindow_x_r_attr; - H5::Attribute scanWindow_y_r_attr; - if(pars.meta.realSpaceWindow_x) scanWindow_x_r_attr = sim_params.createAttribute("wxr",H5::PredType::NATIVE_DOUBLE,v_two_dataspace); - if(pars.meta.realSpaceWindow_y) scanWindow_y_r_attr = sim_params.createAttribute("wyr",H5::PredType::NATIVE_DOUBLE,v_two_dataspace); - - H5::Attribute save2D_attr; - if(pars.meta.save2DOutput){ - save2D_attr = sim_params.createAttribute("2D",H5::PredType::NATIVE_DOUBLE,v_two_dataspace); - } - - //write data - //strings - atoms_attr.write(strdatatype,filenameAtoms); - alg_attr.write(strdatatype,algorithm); - - //scalar integers - fx_attr.write(H5::PredType::NATIVE_INT, &pars.meta.interpolationFactorX); - fy_attr.write(H5::PredType::NATIVE_INT, &pars.meta.interpolationFactorY); - numFP_attr.write(H5::PredType::NATIVE_INT, &pars.meta.numFP); - numSlices_attr.write(H5::PredType::NATIVE_INT, &pars.meta.numSlices); - - //logicals first need to be cast to ints - int tmp_te = {pars.meta.includeThermalEffects}; - int tmp_oc = {pars.meta.includeOccupancy}; - int tmp_3D = {pars.meta.save3DOutput}; - int tmp_4D = {pars.meta.save4DOutput}; - int tmp_DPC = {pars.meta.saveDPC_CoM}; - int tmp_PS = {pars.meta.savePotentialSlices}; - int tmp_nqs = {pars.meta.nyquistSampling}; - - te_attr.write(H5::PredType::NATIVE_INT, &tmp_te); - oc_attr.write(H5::PredType::NATIVE_INT, &tmp_oc); - save3D_attr.write(H5::PredType::NATIVE_INT, &tmp_3D); - save4D_attr.write(H5::PredType::NATIVE_INT, &tmp_4D); - saveDPC_attr.write(H5::PredType::NATIVE_INT, &tmp_DPC); - savePS_attr.write(H5::PredType::NATIVE_INT, &tmp_PS); - nyquist_attr.write(H5::PredType::NATIVE_INT, &tmp_nqs); - - //scalar floats/doubles - px_attr.write(H5::PredType::NATIVE_DOUBLE, &pars.meta.realspacePixelSize[1]); - py_attr.write(H5::PredType::NATIVE_DOUBLE, &pars.meta.realspacePixelSize[0]); - potBound_attr.write(H5::PredType::NATIVE_DOUBLE, &pars.meta.potBound); - sliceThickness_attr.write(H5::PredType::NATIVE_DOUBLE, &pars.meta.sliceThickness); - zStart_attr.write(H5::PredType::NATIVE_DOUBLE, &pars.meta.zStart); - rx_attr.write(H5::PredType::NATIVE_DOUBLE, &pars.meta.probeStepX); - ry_attr.write(H5::PredType::NATIVE_DOUBLE, &pars.meta.probeStepY); - df_attr.write(H5::PredType::NATIVE_DOUBLE, &pars.meta.probeDefocus); - C3_attr.write(H5::PredType::NATIVE_DOUBLE, &pars.meta.C3); - C5_attr.write(H5::PredType::NATIVE_DOUBLE, &pars.meta.C5); - - //scalars with unit adjustments - PRISMATIC_FLOAT_PRECISION tmp_tx[1] = {pars.meta.probeXtilt * 1000}; - PRISMATIC_FLOAT_PRECISION tmp_ty[1] = {pars.meta.probeYtilt * 1000}; - PRISMATIC_FLOAT_PRECISION tmp_E0[1] = {pars.meta.E0 / 1000}; - PRISMATIC_FLOAT_PRECISION tmp_alphaMax[1] = {pars.meta.alphaBeamMax * 1000}; - PRISMATIC_FLOAT_PRECISION tmp_sa[1] = {pars.meta.probeSemiangle * 1000}; - PRISMATIC_FLOAT_PRECISION tmp_d[1] = {pars.meta.detectorAngleStep * 1000}; - - tx_attr.write(H5::PredType::NATIVE_DOUBLE, &tmp_tx); - ty_attr.write(H5::PredType::NATIVE_DOUBLE, &tmp_ty); - E0_attr.write(H5::PredType::NATIVE_DOUBLE, &tmp_E0); - alphaMax_attr.write(H5::PredType::NATIVE_DOUBLE, &tmp_alphaMax); - semiangle_attr.write(H5::PredType::NATIVE_DOUBLE, &tmp_sa); - detector_attr.write(H5::PredType::NATIVE_DOUBLE, &tmp_d); - - //vector spaces - PRISMATIC_FLOAT_PRECISION tmp_buffer[2]; - - if(pars.meta.save2DOutput){ - tmp_buffer[0] = pars.meta.integrationAngleMin * 1000; - tmp_buffer[1] = pars.meta.integrationAngleMax * 1000; - save2D_attr.write(H5::PredType::NATIVE_DOUBLE,tmp_buffer); - } + if (pars.meta.realSpaceWindow_y) + { + tmp_buffer[0] = pars.meta.scanWindowYMin_r; + tmp_buffer[1] = pars.meta.scanWindowYMax_r; + scanWindow_y_r_attr.write(H5::PredType::NATIVE_FLOAT, tmp_buffer); + } - if(pars.meta.realSpaceWindow_x){ - tmp_buffer[0] = pars.meta.scanWindowXMin_r; - tmp_buffer[1] = pars.meta.scanWindowXMax_r; - scanWindow_x_r_attr.write(H5::PredType::NATIVE_DOUBLE,tmp_buffer); - } + tmp_buffer[0] = pars.meta.scanWindowXMin; + tmp_buffer[1] = pars.meta.scanWindowXMax; + scanWindow_x_attr.write(H5::PredType::NATIVE_FLOAT, tmp_buffer); + + tmp_buffer[0] = pars.meta.scanWindowYMin; + tmp_buffer[1] = pars.meta.scanWindowYMax; + scanWindow_y_attr.write(H5::PredType::NATIVE_FLOAT, tmp_buffer); + + int tile_buffer[3]; + tile_buffer[0] = pars.meta.tileX; + tile_buffer[1] = pars.meta.tileY; + tile_buffer[2] = pars.meta.tileZ; + tile_attr.write(H5::PredType::NATIVE_INT, tile_buffer); + + PRISMATIC_FLOAT_PRECISION cellBuffer[3]; + cellBuffer[0] = pars.meta.cellDim[0]; + cellBuffer[1] = pars.meta.cellDim[1]; + cellBuffer[2] = pars.meta.cellDim[2]; + cell_dim_attr.write(H5::PredType::NATIVE_FLOAT, cellBuffer); + + metadata.close(); +}; + +void writeMetadata(Prismatic::Parameters pars, double dummy) +{ + //set up group + H5::Group metadata = pars.outputFile.openGroup("4DSTEM_simulation/metadata/metadata_0/original"); + H5::Group sim_params = metadata.createGroup("simulation_parameters"); + + //write all parameters as attributes + + //create common dataspaces + H5::DataSpace str_name_ds(H5S_SCALAR); //string dataspaces and types + H5::StrType strdatatype(H5::PredType::C_S1, 256); + H5::DataSpace scalar_attr(H5S_SCALAR); + + //initialize string parameter data + H5std_string algorithm; + if (pars.meta.algorithm == Prismatic::Algorithm::Multislice) + { + algorithm = "m"; + } + else + { + algorithm = "p"; + } - if(pars.meta.realSpaceWindow_y){ - tmp_buffer[0] = pars.meta.scanWindowYMin_r; - tmp_buffer[1] = pars.meta.scanWindowYMax_r; - scanWindow_y_r_attr.write(H5::PredType::NATIVE_DOUBLE,tmp_buffer); - } - - tmp_buffer[0] = pars.meta.scanWindowXMin; - tmp_buffer[1] = pars.meta.scanWindowXMax; - scanWindow_x_attr.write(H5::PredType::NATIVE_DOUBLE,tmp_buffer); - - tmp_buffer[0] = pars.meta.scanWindowYMin; - tmp_buffer[1] = pars.meta.scanWindowYMax; - scanWindow_y_attr.write(H5::PredType::NATIVE_DOUBLE,tmp_buffer); - - int tile_buffer[3]; - tile_buffer[0] = pars.meta.tileX; - tile_buffer[1] = pars.meta.tileY; - tile_buffer[2] = pars.meta.tileZ; - tile_attr.write(H5::PredType::NATIVE_INT,tile_buffer); - - PRISMATIC_FLOAT_PRECISION cellBuffer[3]; - cellBuffer[0] = pars.meta.cellDim[0]; - cellBuffer[1] = pars.meta.cellDim[1]; - cellBuffer[2] = pars.meta.cellDim[2]; - cell_dim_attr.write(H5::PredType::NATIVE_DOUBLE,cellBuffer); - - metadata.close(); - }; -} + const H5std_string filenameAtoms(pars.meta.filenameAtoms); + + //create string attributes + H5::Attribute atoms_attr = sim_params.createAttribute("i", strdatatype, str_name_ds); + H5::Attribute alg_attr = sim_params.createAttribute("a", strdatatype, str_name_ds); + + //create scalar logical/integer attributes + H5::Attribute fx_attr = sim_params.createAttribute("fx", H5::PredType::NATIVE_INT, scalar_attr); + H5::Attribute fy_attr = sim_params.createAttribute("fy", H5::PredType::NATIVE_INT, scalar_attr); + H5::Attribute numFP_attr = sim_params.createAttribute("F", H5::PredType::NATIVE_INT, scalar_attr); + H5::Attribute numSlices_attr = sim_params.createAttribute("ns", H5::PredType::NATIVE_INT, scalar_attr); + H5::Attribute te_attr = sim_params.createAttribute("te", H5::PredType::NATIVE_INT, scalar_attr); + H5::Attribute oc_attr = sim_params.createAttribute("oc", H5::PredType::NATIVE_INT, scalar_attr); + H5::Attribute save3D_attr = sim_params.createAttribute("3D", H5::PredType::NATIVE_INT, scalar_attr); + H5::Attribute save4D_attr = sim_params.createAttribute("4D", H5::PredType::NATIVE_INT, scalar_attr); + H5::Attribute saveDPC_attr = sim_params.createAttribute("DPC", H5::PredType::NATIVE_INT, scalar_attr); + H5::Attribute savePS_attr = sim_params.createAttribute("ps", H5::PredType::NATIVE_INT, scalar_attr); + H5::Attribute nyquist_attr = sim_params.createAttribute("nqs", H5::PredType::NATIVE_INT, scalar_attr); + + //create scalar float/double attributes (changes based on prismatic float precision) + H5::Attribute px_attr = sim_params.createAttribute("px", H5::PredType::NATIVE_DOUBLE, scalar_attr); + H5::Attribute py_attr = sim_params.createAttribute("py", H5::PredType::NATIVE_DOUBLE, scalar_attr); + H5::Attribute potBound_attr = sim_params.createAttribute("P", H5::PredType::NATIVE_DOUBLE, scalar_attr); + H5::Attribute sliceThickness_attr = sim_params.createAttribute("s", H5::PredType::NATIVE_DOUBLE, scalar_attr); + H5::Attribute zStart_attr = sim_params.createAttribute("zs", H5::PredType::NATIVE_DOUBLE, scalar_attr); + H5::Attribute E0_attr = sim_params.createAttribute("E", H5::PredType::NATIVE_DOUBLE, scalar_attr); + H5::Attribute alphaMax_attr = sim_params.createAttribute("A", H5::PredType::NATIVE_DOUBLE, scalar_attr); + H5::Attribute rx_attr = sim_params.createAttribute("rx", H5::PredType::NATIVE_DOUBLE, scalar_attr); + H5::Attribute ry_attr = sim_params.createAttribute("ry", H5::PredType::NATIVE_DOUBLE, scalar_attr); + H5::Attribute df_attr = sim_params.createAttribute("df", H5::PredType::NATIVE_DOUBLE, scalar_attr); + H5::Attribute C3_attr = sim_params.createAttribute("C3", H5::PredType::NATIVE_DOUBLE, scalar_attr); + H5::Attribute C5_attr = sim_params.createAttribute("C5", H5::PredType::NATIVE_DOUBLE, scalar_attr); + H5::Attribute semiangle_attr = sim_params.createAttribute("sa", H5::PredType::NATIVE_DOUBLE, scalar_attr); + H5::Attribute detector_attr = sim_params.createAttribute("d", H5::PredType::NATIVE_DOUBLE, scalar_attr); + H5::Attribute tx_attr = sim_params.createAttribute("tx", H5::PredType::NATIVE_DOUBLE, scalar_attr); + H5::Attribute ty_attr = sim_params.createAttribute("ty", H5::PredType::NATIVE_DOUBLE, scalar_attr); + + //create vector spaces + hsize_t two[1] = {2}; + hsize_t three[1] = {3}; + H5::DataSpace v_two_dataspace(1, two); + H5::DataSpace v_three_dataspace(1, three); + + H5::Attribute cell_dim_attr = sim_params.createAttribute("c", H5::PredType::NATIVE_DOUBLE, v_three_dataspace); + H5::Attribute tile_attr = sim_params.createAttribute("t", H5::PredType::NATIVE_DOUBLE, v_three_dataspace); + H5::Attribute scanWindow_x_attr = sim_params.createAttribute("wx", H5::PredType::NATIVE_DOUBLE, v_two_dataspace); + H5::Attribute scanWindow_y_attr = sim_params.createAttribute("wy", H5::PredType::NATIVE_DOUBLE, v_two_dataspace); + + H5::Attribute scanWindow_x_r_attr; + H5::Attribute scanWindow_y_r_attr; + if (pars.meta.realSpaceWindow_x) + scanWindow_x_r_attr = sim_params.createAttribute("wxr", H5::PredType::NATIVE_DOUBLE, v_two_dataspace); + if (pars.meta.realSpaceWindow_y) + scanWindow_y_r_attr = sim_params.createAttribute("wyr", H5::PredType::NATIVE_DOUBLE, v_two_dataspace); + + H5::Attribute save2D_attr; + if (pars.meta.save2DOutput) + { + save2D_attr = sim_params.createAttribute("2D", H5::PredType::NATIVE_DOUBLE, v_two_dataspace); + } + + //write data + //strings + atoms_attr.write(strdatatype, filenameAtoms); + alg_attr.write(strdatatype, algorithm); + + //scalar integers + fx_attr.write(H5::PredType::NATIVE_INT, &pars.meta.interpolationFactorX); + fy_attr.write(H5::PredType::NATIVE_INT, &pars.meta.interpolationFactorY); + numFP_attr.write(H5::PredType::NATIVE_INT, &pars.meta.numFP); + numSlices_attr.write(H5::PredType::NATIVE_INT, &pars.meta.numSlices); + + //logicals first need to be cast to ints + int tmp_te = {pars.meta.includeThermalEffects}; + int tmp_oc = {pars.meta.includeOccupancy}; + int tmp_3D = {pars.meta.save3DOutput}; + int tmp_4D = {pars.meta.save4DOutput}; + int tmp_DPC = {pars.meta.saveDPC_CoM}; + int tmp_PS = {pars.meta.savePotentialSlices}; + int tmp_nqs = {pars.meta.nyquistSampling}; + + te_attr.write(H5::PredType::NATIVE_INT, &tmp_te); + oc_attr.write(H5::PredType::NATIVE_INT, &tmp_oc); + save3D_attr.write(H5::PredType::NATIVE_INT, &tmp_3D); + save4D_attr.write(H5::PredType::NATIVE_INT, &tmp_4D); + saveDPC_attr.write(H5::PredType::NATIVE_INT, &tmp_DPC); + savePS_attr.write(H5::PredType::NATIVE_INT, &tmp_PS); + nyquist_attr.write(H5::PredType::NATIVE_INT, &tmp_nqs); + + //scalar floats/doubles + px_attr.write(H5::PredType::NATIVE_DOUBLE, &pars.meta.realspacePixelSize[1]); + py_attr.write(H5::PredType::NATIVE_DOUBLE, &pars.meta.realspacePixelSize[0]); + potBound_attr.write(H5::PredType::NATIVE_DOUBLE, &pars.meta.potBound); + sliceThickness_attr.write(H5::PredType::NATIVE_DOUBLE, &pars.meta.sliceThickness); + zStart_attr.write(H5::PredType::NATIVE_DOUBLE, &pars.meta.zStart); + rx_attr.write(H5::PredType::NATIVE_DOUBLE, &pars.meta.probeStepX); + ry_attr.write(H5::PredType::NATIVE_DOUBLE, &pars.meta.probeStepY); + df_attr.write(H5::PredType::NATIVE_DOUBLE, &pars.meta.probeDefocus); + C3_attr.write(H5::PredType::NATIVE_DOUBLE, &pars.meta.C3); + C5_attr.write(H5::PredType::NATIVE_DOUBLE, &pars.meta.C5); + + //scalars with unit adjustments + PRISMATIC_FLOAT_PRECISION tmp_tx[1] = {pars.meta.probeXtilt * 1000}; + PRISMATIC_FLOAT_PRECISION tmp_ty[1] = {pars.meta.probeYtilt * 1000}; + PRISMATIC_FLOAT_PRECISION tmp_E0[1] = {pars.meta.E0 / 1000}; + PRISMATIC_FLOAT_PRECISION tmp_alphaMax[1] = {pars.meta.alphaBeamMax * 1000}; + PRISMATIC_FLOAT_PRECISION tmp_sa[1] = {pars.meta.probeSemiangle * 1000}; + PRISMATIC_FLOAT_PRECISION tmp_d[1] = {pars.meta.detectorAngleStep * 1000}; + + tx_attr.write(H5::PredType::NATIVE_DOUBLE, &tmp_tx); + ty_attr.write(H5::PredType::NATIVE_DOUBLE, &tmp_ty); + E0_attr.write(H5::PredType::NATIVE_DOUBLE, &tmp_E0); + alphaMax_attr.write(H5::PredType::NATIVE_DOUBLE, &tmp_alphaMax); + semiangle_attr.write(H5::PredType::NATIVE_DOUBLE, &tmp_sa); + detector_attr.write(H5::PredType::NATIVE_DOUBLE, &tmp_d); + + //vector spaces + PRISMATIC_FLOAT_PRECISION tmp_buffer[2]; + + if (pars.meta.save2DOutput) + { + tmp_buffer[0] = pars.meta.integrationAngleMin * 1000; + tmp_buffer[1] = pars.meta.integrationAngleMax * 1000; + save2D_attr.write(H5::PredType::NATIVE_DOUBLE, tmp_buffer); + } + + if (pars.meta.realSpaceWindow_x) + { + tmp_buffer[0] = pars.meta.scanWindowXMin_r; + tmp_buffer[1] = pars.meta.scanWindowXMax_r; + scanWindow_x_r_attr.write(H5::PredType::NATIVE_DOUBLE, tmp_buffer); + } + + if (pars.meta.realSpaceWindow_y) + { + tmp_buffer[0] = pars.meta.scanWindowYMin_r; + tmp_buffer[1] = pars.meta.scanWindowYMax_r; + scanWindow_y_r_attr.write(H5::PredType::NATIVE_DOUBLE, tmp_buffer); + } + + tmp_buffer[0] = pars.meta.scanWindowXMin; + tmp_buffer[1] = pars.meta.scanWindowXMax; + scanWindow_x_attr.write(H5::PredType::NATIVE_DOUBLE, tmp_buffer); + + tmp_buffer[0] = pars.meta.scanWindowYMin; + tmp_buffer[1] = pars.meta.scanWindowYMax; + scanWindow_y_attr.write(H5::PredType::NATIVE_DOUBLE, tmp_buffer); + + int tile_buffer[3]; + tile_buffer[0] = pars.meta.tileX; + tile_buffer[1] = pars.meta.tileY; + tile_buffer[2] = pars.meta.tileZ; + tile_attr.write(H5::PredType::NATIVE_INT, tile_buffer); + + PRISMATIC_FLOAT_PRECISION cellBuffer[3]; + cellBuffer[0] = pars.meta.cellDim[0]; + cellBuffer[1] = pars.meta.cellDim[1]; + cellBuffer[2] = pars.meta.cellDim[2]; + cell_dim_attr.write(H5::PredType::NATIVE_DOUBLE, cellBuffer); + + metadata.close(); +}; +} // namespace Prismatic