diff --git a/tools/lammps-gui/CMakeLists.txt b/tools/lammps-gui/CMakeLists.txt index 81c4c35975..19c56c97bd 100644 --- a/tools/lammps-gui/CMakeLists.txt +++ b/tools/lammps-gui/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16) -project(lammps-gui VERSION 1.0.0 LANGUAGES CXX) +project(lammps-gui VERSION 1.1.0 LANGUAGES CXX) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) @@ -13,10 +13,6 @@ option(LAMMPS_GUI_USE_PLUGIN "Load LAMMPS library dynamically at runtime" OFF) mark_as_advanced(LAMMPS_GUI_USE_PLUGIN) # checks -if(BUILD_MPI) - message(FATAL_ERROR "Must disable BUILD_MPI for building the LAMMPS GUI") -endif() - # when this file is included as subdirectory in the LAMMPS build, many settings are directly imported if(LAMMPS_DIR) set(LAMMPS_HEADER_DIR ${LAMMPS_SOURCE_DIR}) @@ -62,7 +58,7 @@ if(LAMMPS_GUI_USE_PLUGIN) endif() # we require Qt 5 and at least version 5.12 at that. -find_package(Qt5 5.12 REQUIRED COMPONENTS Widgets) +find_package(Qt5 5.12 REQUIRED COMPONENTS Widgets Charts) set(PROJECT_SOURCES main.cpp @@ -72,6 +68,8 @@ set(PROJECT_SOURCES highlighter.h imageviewer.cpp imageviewer.h + chartviewer.cpp + chartviewer.h lammpsgui.cpp lammpsgui.h lammpsgui.ui @@ -109,7 +107,7 @@ else() endif() target_include_directories(lammps-gui PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_compile_definitions(lammps-gui PRIVATE LAMMPS_GUI_VERSION="${PROJECT_VERSION}") -target_link_libraries(lammps-gui PRIVATE Qt5::Widgets) +target_link_libraries(lammps-gui PRIVATE Qt5::Widgets Qt5::Charts) if(BUILD_OMP) find_package(OpenMP COMPONENTS CXX REQUIRED) target_link_libraries(lammps-gui PRIVATE OpenMP::OpenMP_CXX) diff --git a/tools/lammps-gui/chartviewer.cpp b/tools/lammps-gui/chartviewer.cpp new file mode 100644 index 0000000000..676a658f95 --- /dev/null +++ b/tools/lammps-gui/chartviewer.cpp @@ -0,0 +1,85 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#include "chartviewer.h" + +#include +#include + +using namespace QtCharts; + +ChartViewer::ChartViewer(QWidget *parent) : + QChartView(parent), last_step(-1), chart(new QChart), series(new QLineSeries), + xaxis(new QValueAxis), yaxis(new QValueAxis) +{ + chart->legend()->hide(); + chart->addAxis(xaxis,Qt::AlignBottom); + chart->addAxis(yaxis,Qt::AlignLeft); + chart->addSeries(series); + series->attachAxis(xaxis); + series->attachAxis(yaxis); + xaxis->setTitleText("Time step"); + xaxis->setTickCount(5); + yaxis->setTickCount(5); + xaxis->setMinorTickCount(5); + yaxis->setMinorTickCount(5); + + setRenderHint(QPainter::Antialiasing); + setChart(chart); + setRubberBand(QChartView::RectangleRubberBand); + + QSettings settings; + resize(settings.value("chartx", 500).toInt(), settings.value("charty", 320).toInt()); +} + +void ChartViewer::add_column(const QString &title) +{ + yaxis->setTitleText(title); + series->setName(title); +} + +void ChartViewer::add_data(int step, int column, double data) +{ + if (last_step < step) { + last_step = step; + series->append(step, data); + auto points = series->pointsVector(); + + qreal xmin = 1.0e100; + qreal xmax = -1.0e100; + qreal ymin = 1.0e100; + qreal ymax = -1.0e100; + for (auto &p : points) { + xmin = qMin(xmin, p.x()); + xmax = qMax(xmax, p.x()); + ymin = qMin(ymin, p.y()); + ymax = qMax(ymax, p.y()); + } + xaxis->setRange(xmin, xmax); + yaxis->setRange(ymin, ymax); + } +} + +void ChartViewer::closeEvent(QCloseEvent *event) +{ + QSettings settings; + if (!isMaximized()) { + settings.setValue("chartx", width()); + settings.setValue("charty", height()); + } + QChartView::closeEvent(event); +} + +// Local Variables: +// c-basic-offset: 4 +// End: diff --git a/tools/lammps-gui/chartviewer.h b/tools/lammps-gui/chartviewer.h new file mode 100644 index 0000000000..2ee95788fc --- /dev/null +++ b/tools/lammps-gui/chartviewer.h @@ -0,0 +1,43 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + https://www.lammps.org/, Sandia National Laboratories + LAMMPS development team: developers@lammps.org + + Copyright (2003) Sandia Corporation. Under the terms of Contract + DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains + certain rights in this software. This software is distributed under + the GNU General Public License. + + See the README file in the top-level LAMMPS directory. +------------------------------------------------------------------------- */ + +#ifndef CHARTVIEWER_H +#define CHARTVIEWER_H + +#include + +class ChartViewer : public QtCharts::QChartView { + Q_OBJECT + +public: + ChartViewer(QWidget *parent = nullptr); + bool has_columns() const { return last_step >= 0; } + void add_column(const QString &title); + void add_data(int step, int column, double data); + int get_last_step() const; + +protected: + void closeEvent(QCloseEvent *event) override; + +private: + int last_step; + QtCharts::QChart *chart; + QtCharts::QLineSeries *series; + QtCharts::QValueAxis *xaxis; + QtCharts::QValueAxis *yaxis; +}; +#endif + +// Local Variables: +// c-basic-offset: 4 +// End: diff --git a/tools/lammps-gui/lammpsgui.cpp b/tools/lammps-gui/lammpsgui.cpp index fc5a9c5d50..964215ac1c 100644 --- a/tools/lammps-gui/lammpsgui.cpp +++ b/tools/lammps-gui/lammpsgui.cpp @@ -13,6 +13,7 @@ #include "lammpsgui.h" +#include "chartviewer.h" #include "highlighter.h" #include "imageviewer.h" #include "lammpsrunner.h" @@ -64,8 +65,8 @@ static char *mystrdup(const std::string &text) LammpsGui::LammpsGui(QWidget *parent, const char *filename) : QMainWindow(parent), ui(new Ui::LammpsGui), highlighter(nullptr), capturer(nullptr), - status(nullptr), logwindow(nullptr), imagewindow(nullptr), logupdater(nullptr), - dirstatus(nullptr), progress(nullptr), prefdialog(nullptr) + status(nullptr), logwindow(nullptr), imagewindow(nullptr), chartwindow(nullptr), + logupdater(nullptr), dirstatus(nullptr), progress(nullptr), prefdialog(nullptr) { ui->setupUi(this); this->setCentralWidget(ui->textEdit); @@ -203,6 +204,7 @@ LammpsGui::~LammpsGui() delete status; delete logwindow; delete imagewindow; + delete chartwindow; delete dirstatus; } @@ -384,6 +386,7 @@ void LammpsGui::logupdate() double t_elapsed, t_remain, t_total; int completed = 1000; + // estimate completion percentage if (lammps.is_running()) { t_elapsed = lammps.get_thermo("cpu"); t_remain = lammps.get_thermo("cpuremain"); @@ -400,6 +403,28 @@ void LammpsGui::logupdate() logwindow->textCursor().deleteChar(); } } + + // extract chache thermo data + if (chartwindow) { + void *ptr = lammps.last_thermo("step", 0); + if (ptr) { + int step = 0; + if (lammps.extract_setting("bigint") == 4) + step = *(int *)ptr; + else + step = (int)*(int64_t *)ptr; + int ncols = *(int *)lammps.last_thermo("num", 0); + if (!chartwindow->has_columns()) { + // for (int i = 0; i < ncols; ++i) { + chartwindow->add_column((const char *)lammps.last_thermo("keyword", 1)); + // } + } + + // for (int i = 0; i < ncols; ++i) { + chartwindow->add_data(step, 0, *(double *)lammps.last_thermo("data", 1)); + // } + } + } } void LammpsGui::modified() @@ -424,6 +449,27 @@ void LammpsGui::run_done() logwindow->insertPlainText(log.c_str()); logwindow->moveCursor(QTextCursor::End); + if (chartwindow) { + void *ptr = lammps.last_thermo("step", 0); + if (ptr) { + int step = 0; + if (lammps.extract_setting("bigint") == 4) + step = *(int *)ptr; + else + step = (int)*(int64_t *)ptr; + int ncols = *(int *)lammps.last_thermo("num", 0); + if (!chartwindow->has_columns()) { + // for (int i = 0; i < ncols; ++i) { + chartwindow->add_column((const char *)lammps.last_thermo("keyword", 1)); + // } + } + + // for (int i = 0; i < ncols; ++i) { + chartwindow->add_data(step, 0, *(double *)lammps.last_thermo("data", 1)); + // } + } + } + bool success = true; constexpr int BUFLEN = 1024; char errorbuf[BUFLEN]; @@ -458,7 +504,8 @@ void LammpsGui::run_buffer() int nthreads = settings.value("nthreads", 1).toInt(); int accel = settings.value("accelerator", AcceleratorTab::None).toInt(); if ((accel != AcceleratorTab::OpenMP) && (accel != AcceleratorTab::Intel) && - (accel != AcceleratorTab::Kokkos)) nthreads = 1; + (accel != AcceleratorTab::Kokkos)) + nthreads = 1; if (nthreads > 1) status->setText(QString("Running LAMMPS with %1 thread(s)...").arg(nthreads)); else @@ -503,9 +550,22 @@ void LammpsGui::run_buffer() QObject::connect(shortcut, &QShortcut::activated, this, &LammpsGui::stop_run); logwindow->show(); + // if configured, delete old log window before opening new one + if (settings.value("chartreplace", false).toBool()) delete chartwindow; + chartwindow = new ChartViewer(); + chartwindow->setWindowTitle("LAMMPS-GUI - Thermo charts from running LAMMPS on buffer - " + + current_file); + chartwindow->setWindowIcon(QIcon(":/lammps-icon-128x128.png")); + chartwindow->setMinimumSize(400, 300); + shortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_W), chartwindow); + QObject::connect(shortcut, &QShortcut::activated, chartwindow, &ChartViewer::close); + shortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Slash), chartwindow); + QObject::connect(shortcut, &QShortcut::activated, this, &LammpsGui::stop_run); + chartwindow->show(); + logupdater = new QTimer(this); connect(logupdater, &QTimer::timeout, this, &LammpsGui::logupdate); - logupdater->start(500); + logupdater->start(200); } void LammpsGui::view_image() diff --git a/tools/lammps-gui/lammpsgui.h b/tools/lammps-gui/lammpsgui.h index 78e518609e..158e4e86ac 100644 --- a/tools/lammps-gui/lammpsgui.h +++ b/tools/lammps-gui/lammpsgui.h @@ -39,6 +39,7 @@ class Highlighter; class StdCapture; class Preferences; class ImageViewer; +class ChartViewer; class LammpsGui : public QMainWindow { Q_OBJECT @@ -85,6 +86,7 @@ private: QLabel *status; QPlainTextEdit *logwindow; ImageViewer *imagewindow; + ChartViewer *chartwindow; QTimer *logupdater; QLabel *dirstatus; QProgressBar *progress; diff --git a/tools/lammps-gui/lammpswrapper.cpp b/tools/lammps-gui/lammpswrapper.cpp index 3292217d77..7d5b54a634 100644 --- a/tools/lammps-gui/lammpswrapper.cpp +++ b/tools/lammps-gui/lammpswrapper.cpp @@ -58,6 +58,19 @@ double LammpsWrapper::get_thermo(const char *keyword) return val; } +void *LammpsWrapper::last_thermo(const char *keyword, int index) +{ + void *ptr = nullptr; + if (lammps_handle) { +#if defined(LAMMPS_GUI_USE_PLUGIN) + ptr = ((liblammpsplugin_t *)plugin_handle)->last_thermo(lammps_handle, keyword, index); +#else + ptr = lammps_last_thermo(lammps_handle, keyword, index); +#endif + } + return ptr; +} + bool LammpsWrapper::is_running() { int val = 0; diff --git a/tools/lammps-gui/lammpswrapper.h b/tools/lammps-gui/lammpswrapper.h index db8a0ba3d6..9aa75b2b54 100644 --- a/tools/lammps-gui/lammpswrapper.h +++ b/tools/lammps-gui/lammpswrapper.h @@ -30,6 +30,7 @@ public: int extract_setting(const char *keyword); double get_thermo(const char *keyword); + void *last_thermo(const char *keyword, int idx); bool is_open() const { return lammps_handle != nullptr; } bool is_running(); bool has_error() const; diff --git a/tools/lammps-gui/logwindow.cpp b/tools/lammps-gui/logwindow.cpp index 3304802d62..7091b34f43 100644 --- a/tools/lammps-gui/logwindow.cpp +++ b/tools/lammps-gui/logwindow.cpp @@ -13,7 +13,6 @@ #include "logwindow.h" #include -#include LogWindow::LogWindow(QWidget *parent) : QPlainTextEdit(parent) { @@ -23,7 +22,6 @@ LogWindow::LogWindow(QWidget *parent) : QPlainTextEdit(parent) void LogWindow::closeEvent(QCloseEvent *event) { - fprintf(stderr, "log closing\n"); QSettings settings; if (!isMaximized()) { settings.setValue("logx", width());