Commit 91bc4ada authored by Tobias WEBER's avatar Tobias WEBER
Browse files

started with structure factor tool; forked from private project

parent c343727c
......@@ -9,7 +9,7 @@ project(pol)
cmake_minimum_required(VERSION 3.0)
set(CMAKE_VERBOSE_MAKEFILE TRUE)
set(BUILD_LIB TRUE)
set(BUILD_LIB FALSE)
find_package(Boost REQUIRED)
find_package(Qt5 REQUIRED COMPONENTS Core Gui Widgets OpenGL)
......
......@@ -455,6 +455,7 @@ public:
// calculate final polarisation vector and intensity
auto [I, P_f] = m::blume_maleev_indir<t_mat, t_vec, t_cplx>(Pi, Mperp, N);
//auto [I, P_f] = m::blume_maleev<t_vec, t_cplx>(Pi, Mperp, N);
// set final polarisation
m_editPfX->setText(std::to_string(P_f[0].real()).c_str());
......
#
# @author Tobias Weber
# @date dec-2018
# @license GPLv3, see 'LICENSE' file
#
project(structfact)
cmake_minimum_required(VERSION 3.0)
find_package(Boost REQUIRED)
find_package(Qt5 REQUIRED COMPONENTS Core Gui Widgets OpenGL)
set(CMAKE_AUTOUIC TRUE)
set(CMAKE_AUTOMOC TRUE)
set(CMAKE_CXX_STANDARD 17)
add_definitions(-std=c++17 -fconcepts)
add_definitions(${Boost_CXX_FLAGS})
include_directories("${PROJECT_SOURCE_DIR}" "${Boost_INCLUDE_DIRS}/.." "../..")
add_executable(structfact structfact.cpp structfact.h)
target_link_libraries(structfact ${Boost_LIBRARIES})
qt5_use_modules(structfact Core Gui Widgets OpenGL)
/**
* structure factor tool
* @author Tobias Weber <tweber@ill.fr>
* @date Dec-2018
* @license GPLv3, see 'LICENSE' file
* @desc The present version was forked on 28-Dec-2018 from the privately developed "misc" project (https://github.com/t-weber/misc).
*/
#include "structfact.h"
#include <QtWidgets/QApplication>
#include <QtWidgets/QGridLayout>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QTabWidget>
#include <QtWidgets/QLabel>
#include <QtWidgets/QToolButton>
#include <locale>
#include <iostream>
#include <boost/version.hpp>
#include <boost/config.hpp>
#include <boost/algorithm/string/replace.hpp>
namespace algo = boost::algorithm;
#include "libs/_cxx20/math_algos.h"
//using namespace m;
using namespace m_ops;
using t_vec = std::vector<t_real>;
using t_vec_cplx = std::vector<t_cplx>;
using t_mat = m::mat<t_real, std::vector>;
using t_mat_cplx = m::mat<t_cplx, std::vector>;
enum : int
{
COL_NAME = 0,
COL_SCATLEN_RE = 1,
COL_SCATLEN_IM = 2,
COL_X = 3,
COL_Y = 4,
COL_Z = 5,
};
struct PowderLine
{
t_real Q;
t_real I;
std::string peaks;
};
// ----------------------------------------------------------------------------
StructFactDlg::StructFactDlg(QWidget* pParent) : QDialog{pParent},
m_sett{new QSettings{"tobis_stuff", "structfact"}}
{
setWindowTitle("Structure Factors");
setSizeGripEnabled(true);
setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont));
auto tabs = new QTabWidget(this);
{
m_nucleipanel = new QWidget(this);
m_nuclei = new QTableWidget(m_nucleipanel);
m_nuclei->setShowGrid(true);
m_nuclei->setSortingEnabled(true);
m_nuclei->setMouseTracking(true);
m_nuclei->setSelectionBehavior(QTableWidget::SelectRows);
m_nuclei->setSelectionMode(QTableWidget::ContiguousSelection);
m_nuclei->setContextMenuPolicy(Qt::CustomContextMenu);
m_nuclei->verticalHeader()->setDefaultSectionSize(fontMetrics().lineSpacing() + 4);
m_nuclei->verticalHeader()->setVisible(false);
m_nuclei->setColumnCount(6);
m_nuclei->setHorizontalHeaderItem(COL_NAME, new QTableWidgetItem{"Name"});
m_nuclei->setHorizontalHeaderItem(COL_SCATLEN_RE, new QTableWidgetItem{"Re{b} (fm)"});
m_nuclei->setHorizontalHeaderItem(COL_SCATLEN_IM, new QTableWidgetItem{"Im{b} (fm)"});
m_nuclei->setHorizontalHeaderItem(COL_X, new QTableWidgetItem{"x (frac.)"});
m_nuclei->setHorizontalHeaderItem(COL_Y, new QTableWidgetItem{"y (frac.)"});
m_nuclei->setHorizontalHeaderItem(COL_Z, new QTableWidgetItem{"z (frac.)"});
m_nuclei->setColumnWidth(COL_NAME, 100);
m_nuclei->setColumnWidth(COL_SCATLEN_RE, 75);
m_nuclei->setColumnWidth(COL_SCATLEN_IM, 75);
m_nuclei->setColumnWidth(COL_X, 75);
m_nuclei->setColumnWidth(COL_Y, 75);
m_nuclei->setColumnWidth(COL_Z, 75);
QToolButton *m_pTabBtnAdd = new QToolButton(m_nucleipanel);
QToolButton *m_pTabBtnDel = new QToolButton(m_nucleipanel);
QToolButton *m_pTabBtnUp = new QToolButton(m_nucleipanel);
QToolButton *m_pTabBtnDown = new QToolButton(m_nucleipanel);
m_pTabBtnAdd->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Fixed});
m_pTabBtnDel->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Fixed});
m_pTabBtnUp->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Fixed});
m_pTabBtnDown->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Fixed});
m_pTabBtnAdd->setText("Add Nucleus");
m_pTabBtnDel->setText("Delete Nuclei");
m_pTabBtnUp->setText("Move Nuclei Up");
m_pTabBtnDown->setText("Move Nuclei Down");
m_pTabBtnAdd->setToolTip("Add nucleus.");
m_pTabBtnDel->setToolTip("Delete selected nuclei.");
m_pTabBtnUp->setToolTip("Move selected nuclei up.");
m_pTabBtnDown->setToolTip("Move selected nuclei down.");
m_editA = new QLineEdit("5", m_nucleipanel);
m_editB = new QLineEdit("5", m_nucleipanel);
m_editC = new QLineEdit("5", m_nucleipanel);
m_editAlpha = new QLineEdit("90", m_nucleipanel);
m_editBeta = new QLineEdit("90", m_nucleipanel);
m_editGamma = new QLineEdit("90", m_nucleipanel);
// table grid
auto pTabGrid = new QGridLayout(m_nucleipanel);
pTabGrid->setSpacing(2);
pTabGrid->setContentsMargins(4,4,4,4);
pTabGrid->addWidget(m_nuclei, 0,0,1,4);
pTabGrid->addWidget(m_pTabBtnAdd, 1,0,1,1);
pTabGrid->addWidget(m_pTabBtnDel, 1,1,1,1);
pTabGrid->addItem(new QSpacerItem(4, 4, QSizePolicy::Expanding, QSizePolicy::Minimum), 1,2,1,1);
pTabGrid->addWidget(m_pTabBtnUp, 1,2,1,1);
pTabGrid->addWidget(m_pTabBtnDown, 1,3,1,1);
auto sep1 = new QFrame(m_nucleipanel); sep1->setFrameStyle(QFrame::HLine);
pTabGrid->addWidget(sep1, 2,0, 1,4);
pTabGrid->addWidget(new QLabel("Lattice (A):"), 3,0,1,1);
pTabGrid->addWidget(m_editA, 3,1,1,1);
pTabGrid->addWidget(m_editB, 3,2,1,1);
pTabGrid->addWidget(m_editC, 3,3,1,1);
pTabGrid->addWidget(new QLabel("Angles (deg):"), 4,0,1,1);
pTabGrid->addWidget(m_editAlpha, 4,1,1,1);
pTabGrid->addWidget(m_editBeta, 4,2,1,1);
pTabGrid->addWidget(m_editGamma, 4,3,1,1);
// table context CustomContextMenu
m_pTabContextMenu = new QMenu(m_nuclei);
m_pTabContextMenu->addAction("Add Nucleus Before", this, [this]() { this->AddTabItem(-2); });
m_pTabContextMenu->addAction("Add Nucleus After", this, [this]() { this->AddTabItem(-3); });
m_pTabContextMenu->addAction("Delete Nucleus", this, &StructFactDlg::DelTabItem);
// signals
connect(m_pTabBtnAdd, &QToolButton::clicked, this, [this]() { this->AddTabItem(-1); });
connect(m_pTabBtnDel, &QToolButton::clicked, this, &StructFactDlg::DelTabItem);
connect(m_pTabBtnUp, &QToolButton::clicked, this, &StructFactDlg::MoveTabItemUp);
connect(m_pTabBtnDown, &QToolButton::clicked, this, &StructFactDlg::MoveTabItemDown);
connect(m_nuclei, &QTableWidget::currentCellChanged, this, &StructFactDlg::TableCurCellChanged);
connect(m_nuclei, &QTableWidget::entered, this, &StructFactDlg::TableCellEntered);
connect(m_nuclei, &QTableWidget::itemChanged, this, &StructFactDlg::TableItemChanged);
connect(m_nuclei, &QTableWidget::customContextMenuRequested, this, &StructFactDlg::ShowTableContextMenu);
tabs->addTab(m_nucleipanel, "Nuclei");
}
{ // structure factors panel
auto sfactpanel = new QWidget(this);
auto pGrid = new QGridLayout(sfactpanel);
pGrid->setSpacing(4);
pGrid->setContentsMargins(4,4,4,4);
m_structfacts = new QPlainTextEdit(sfactpanel);
m_structfacts->setReadOnly(true);
m_structfacts->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
m_maxBZ = new QSpinBox(sfactpanel);
m_maxBZ->setMinimum(0);
m_maxBZ->setMaximum(99);
m_maxBZ->setValue(4);
pGrid->addWidget(m_structfacts, 0,0, 1,4);
pGrid->addWidget(new QLabel("Max. Order::"), 1,0,1,1);
pGrid->addWidget(m_maxBZ, 1,1, 1,1);
// signals
connect(m_maxBZ, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, [this]() { this->Calc(); });
tabs->addTab(sfactpanel, "Structure Factors");
}
{ // powder lines panel
auto powderpanel = new QWidget(this);
auto pGrid = new QGridLayout(powderpanel);
pGrid->setSpacing(4);
pGrid->setContentsMargins(4,4,4,4);
m_powderlines = new QPlainTextEdit(powderpanel);
m_powderlines->setReadOnly(true);
m_powderlines->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
m_powderlines->setLineWrapMode(QPlainTextEdit::NoWrap);
pGrid->addWidget(m_powderlines, 0,0, 1,4);
tabs->addTab(powderpanel, "Powder Lines");
}
{ // info panel
auto infopanel = new QWidget(this);
auto pGrid = new QGridLayout(infopanel);
pGrid->setSpacing(4);
pGrid->setContentsMargins(4,4,4,4);
auto sep1 = new QFrame(infopanel); sep1->setFrameStyle(QFrame::HLine);
auto sep2 = new QFrame(infopanel); sep2->setFrameStyle(QFrame::HLine);
auto sep3 = new QFrame(infopanel); sep3->setFrameStyle(QFrame::HLine);
std::string strBoost = BOOST_LIB_VERSION;
algo::replace_all(strBoost, "_", ".");
auto labelTitle = new QLabel("Structure Factors", infopanel);
auto fontTitle = labelTitle->font();
fontTitle.setBold(true);
labelTitle->setFont(fontTitle);
labelTitle->setAlignment(Qt::AlignHCenter);
auto labelAuthor = new QLabel("Written by Tobias Weber <tweber@ill.fr>.", infopanel);
labelAuthor->setAlignment(Qt::AlignHCenter);
auto labelDate = new QLabel("December 2018.", infopanel);
labelDate->setAlignment(Qt::AlignHCenter);
int y = 0;
pGrid->addWidget(labelTitle, y++,0, 1,1);
pGrid->addWidget(labelAuthor, y++,0, 1,1);
pGrid->addWidget(labelDate, y++,0, 1,1);
pGrid->addItem(new QSpacerItem(16,16, QSizePolicy::Minimum, QSizePolicy::Fixed), y++,0, 1,1);
pGrid->addWidget(sep1, y++,0, 1,1);
pGrid->addWidget(new QLabel(QString("Compiler: ") + QString(BOOST_COMPILER) + ".", infopanel), y++,0, 1,1);
pGrid->addWidget(new QLabel(QString("C++ Library: ") + QString(BOOST_STDLIB) + ".", infopanel), y++,0, 1,1);
pGrid->addWidget(new QLabel(QString("Build Date: ") + QString(__DATE__) + ", " + QString(__TIME__) + ".", infopanel), y++,0, 1,1);
pGrid->addWidget(sep2, y++,0, 1,1);
pGrid->addWidget(new QLabel(QString("Qt Version: ") + QString(QT_VERSION_STR) + ".", infopanel), y++,0, 1,1);
pGrid->addWidget(new QLabel(QString("Boost Version: ") + strBoost.c_str() + ".", infopanel), y++,0, 1,1);
pGrid->addWidget(sep3, y++,0, 1,1);
pGrid->addItem(new QSpacerItem(16,16, QSizePolicy::Minimum, QSizePolicy::Expanding), y++,0, 1,1);
tabs->addTab(infopanel, "Infos");
}
// main grid
auto pmainGrid = new QGridLayout(this);
pmainGrid->setSpacing(4);
pmainGrid->setContentsMargins(4,4,4,4);
pmainGrid->addWidget(tabs, 0,0, 1,1);
// restory window size and position
if(m_sett && m_sett->contains("geo"))
restoreGeometry(m_sett->value("geo").toByteArray());
else
resize(600, 500);
m_ignoreChanges = 0;
}
void StructFactDlg::AddTabItem(int row)
{
m_ignoreChanges = 1;
if(row == -1) // append to end of table
row = m_nuclei->rowCount();
else if(row == -2 && m_iCursorRow >= 0) // use row from member variable
row = m_iCursorRow;
else if(row == -3 && m_iCursorRow >= 0) // use row from member variable +1
row = m_iCursorRow + 1;
//bool sorting = m_nuclei->isSortingEnabled();
m_nuclei->setSortingEnabled(false);
m_nuclei->insertRow(row);
m_nuclei->setItem(row, COL_NAME, new QTableWidgetItem("n/a"));
m_nuclei->setItem(row, COL_SCATLEN_RE, new NumericTableWidgetItem<t_real>(0));
m_nuclei->setItem(row, COL_SCATLEN_IM, new NumericTableWidgetItem<t_real>(0));
m_nuclei->setItem(row, COL_X, new NumericTableWidgetItem<t_real>(0));
m_nuclei->setItem(row, COL_Y, new NumericTableWidgetItem<t_real>(0));
m_nuclei->setItem(row, COL_Z, new NumericTableWidgetItem<t_real>(0));
m_nuclei->scrollToItem(m_nuclei->item(row, 0));
m_nuclei->setCurrentCell(row, 0);
m_nuclei->setSortingEnabled(/*sorting*/ true);
m_ignoreChanges = 0;
Calc();
}
void StructFactDlg::DelTabItem()
{
m_ignoreChanges = 1;
// if nothing is selected, clear all items
if(m_nuclei->selectedItems().count() == 0)
{
m_nuclei->clearContents();
m_nuclei->setRowCount(0);
}
for(int row : GetSelectedRows(true))
m_nuclei->removeRow(row);
m_ignoreChanges = 0;
Calc();
}
void StructFactDlg::MoveTabItemUp()
{
m_ignoreChanges = 1;
m_nuclei->setSortingEnabled(false);
auto selected = GetSelectedRows(false);
for(int row : selected)
{
if(row == 0)
continue;
auto *item = m_nuclei->item(row, 0);
if(!item || !item->isSelected())
continue;
m_nuclei->insertRow(row-1);
for(int col=0; col<m_nuclei->columnCount(); ++col)
m_nuclei->setItem(row-1, col, m_nuclei->item(row+1, col)->clone());
m_nuclei->removeRow(row+1);
}
for(int row=0; row<m_nuclei->rowCount(); ++row)
{
if(auto *item = m_nuclei->item(row, 0);
item && std::find(selected.begin(), selected.end(), row+1) != selected.end())
{
for(int col=0; col<m_nuclei->columnCount(); ++col)
m_nuclei->item(row, col)->setSelected(true);
}
}
m_ignoreChanges = 0;
}
void StructFactDlg::MoveTabItemDown()
{
m_ignoreChanges = 1;
m_nuclei->setSortingEnabled(false);
auto selected = GetSelectedRows(true);
for(int row : selected)
{
if(row == m_nuclei->rowCount()-1)
continue;
auto *item = m_nuclei->item(row, 0);
if(!item || !item->isSelected())
continue;
m_nuclei->insertRow(row+2);
for(int col=0; col<m_nuclei->columnCount(); ++col)
m_nuclei->setItem(row+2, col, m_nuclei->item(row, col)->clone());
m_nuclei->removeRow(row);
}
for(int row=0; row<m_nuclei->rowCount(); ++row)
{
if(auto *item = m_nuclei->item(row, 0);
item && std::find(selected.begin(), selected.end(), row-1) != selected.end())
{
for(int col=0; col<m_nuclei->columnCount(); ++col)
m_nuclei->item(row, col)->setSelected(true);
}
}
m_ignoreChanges = 0;
}
std::vector<int> StructFactDlg::GetSelectedRows(bool sort_reversed) const
{
std::vector<int> vec;
vec.reserve(m_nuclei->selectedItems().size());
for(int row=0; row<m_nuclei->rowCount(); ++row)
{
if(auto *item = m_nuclei->item(row, 0); item && item->isSelected())
vec.push_back(row);
}
if(sort_reversed)
{
std::stable_sort(vec.begin(), vec.end(), [](int row1, int row2)
{ return row1 > row2; });
}
return vec;
}
/**
* selected a new row
*/
void StructFactDlg::TableCurCellChanged(int rowNew, int colNew, int rowOld, int colOld)
{
}
/**
* hovered over new row
*/
void StructFactDlg::TableCellEntered(const QModelIndex& idx)
{
}
/**
* item contents changed
*/
void StructFactDlg::TableItemChanged(QTableWidgetItem *item)
{
if(!m_ignoreChanges)
Calc();
}
void StructFactDlg::ShowTableContextMenu(const QPoint& pt)
{
const auto* item = m_nuclei->itemAt(pt);
if(!item)
return;
m_iCursorRow = item->row();
auto ptGlob = m_nuclei->mapToGlobal(pt);
ptGlob.setY(ptGlob.y() + m_pTabContextMenu->sizeHint().height()/2);
m_pTabContextMenu->popup(ptGlob);
}
/**
* reads nuclei positions from table
*/
std::vector<NuclPos> StructFactDlg::GetNuclei() const
{
std::vector<NuclPos> vec;
for(int row=0; row<m_nuclei->rowCount(); ++row)
{
auto *name = m_nuclei->item(row, COL_NAME);
auto *bRe = m_nuclei->item(row, COL_SCATLEN_RE);
auto *bIm = m_nuclei->item(row, COL_SCATLEN_IM);
auto *x = m_nuclei->item(row, COL_X);
auto *y = m_nuclei->item(row, COL_Y);
auto *z = m_nuclei->item(row, COL_Z);
if(!name || !bRe || !bIm || !x || !y || !z)
{
std::cerr << "Invalid entry in row " << row << "." << std::endl;
continue;
}
NuclPos nucl;
t_real _bRe, _bIm;
nucl.name = name->text().toStdString();
std::istringstream{bRe->text().toStdString()} >> _bRe;
std::istringstream{bIm->text().toStdString()} >> _bIm;
std::istringstream{x->text().toStdString()} >> nucl.pos[0];
std::istringstream{y->text().toStdString()} >> nucl.pos[1];
std::istringstream{z->text().toStdString()} >> nucl.pos[2];
nucl.b = t_cplx{_bRe, _bIm};
vec.emplace_back(std::move(nucl));
}
return vec;
}
/**
* calculate structure factors
*/
void StructFactDlg::Calc()
{
const t_real eps = 1e-6;
const int prec = 6;
const auto maxBZ = m_maxBZ->value();
// powder lines
std::vector<PowderLine> powderlines;
auto add_powderline = [&powderlines, eps](t_real Q, t_real I,
t_real h, t_real k, t_real l)
{
std::ostringstream ostrPeak;
ostrPeak << "(" << h << "," << k << "," << l << "); ";
// is this Q value already in the vector?
bool foundQ = false;
for(auto& line : powderlines)
{
if(m::equals<t_real>(line.Q, Q, eps))
{
line.I += I;
line.peaks += ostrPeak.str();
foundQ = true;
break;
}
}
// start a new line
if(!foundQ)
{
PowderLine line;
line.Q = Q;
line.I = I;
line.peaks = ostrPeak.str();
powderlines.emplace_back(std::move(line));
}
};
// lattice B matrix
t_real a,b,c, alpha,beta,gamma;
std::istringstream{m_editA->text().toStdString()} >> a;
std::istringstream{m_editB->text().toStdString()} >> b;
std::istringstream{m_editC->text().toStdString()} >> c;
std::istringstream{m_editAlpha->text().toStdString()} >> alpha;
std::istringstream{m_editBeta->text().toStdString()} >> beta;
std::istringstream{m_editGamma->text().toStdString()} >> gamma;
//auto crystB = m::unit<t_mat>(3);
auto crystB = m::B_matrix<t_mat>(a, b, c,
alpha/180.*m::pi<t_real>, beta/180.*m::pi<t_real>, gamma/180.*m::pi<t_real>);
std::vector<t_cplx> bs;
std::vector<t_vec> pos;