diff --git a/doc/src/Tools.rst b/doc/src/Tools.rst index a42e7c56a5..49022a4ee9 100644 --- a/doc/src/Tools.rst +++ b/doc/src/Tools.rst @@ -702,11 +702,15 @@ Prerequisites and portability LAMMPS GUI is programmed in C++ based on the C++11 standard and using the `Qt GUI framework `_. Currently, Qt version 5.12 or later is required; Qt 5.15LTS is -recommended; Qt 6.x not (yet) supported. Building LAMMPS with CMake is -required. The LAMMPS GUI has been successfully compiled and tested on: +recommended; support for Qt version 6.x is under active development and +thus far only tested with Qt 6.5LTS on Linux. Building LAMMPS with +CMake is required. + +The LAMMPS GUI has been successfully compiled and tested on: - Ubuntu Linux 20.04LTS x86_64 using GCC 9, Qt version 5.12 - Fedora Linux 38 x86\_64 using GCC 13 and Clang 16, Qt version 5.15LTS +- Fedora Linux 38 x86\_64 using GCC 13, Qt version 6.5LTS - Apple macOS 12 (Monterey) and macOS 13 (Ventura) with Xcode on arm64 and x86\_64, Qt version 5.15LTS - Windows 10 and 11 x86_64 with Visual Studio 2022 and Visual C++ 14.36, Qt version 5.15LTS - Windows 10 and 11 x86_64 with MinGW / GCC 10.0 cross-compiler on Fedora 38, Qt version 5.15LTS @@ -717,7 +721,7 @@ required. The LAMMPS GUI has been successfully compiled and tested on: Pre-compiled executables ^^^^^^^^^^^^^^^^^^^^^^^^ -Pre-compiled LAMMPS executables including the GUI are currently +Pre-compiled LAMMPS executable packages that include the GUI are currently available from https://download.lammps.org/static or https://github.com/lammps/lammps/releases. You can unpack the archives (or mount the macOS disk image) and run the GUI directly in place. The @@ -742,7 +746,10 @@ stored in a location where CMake can find them without additional help. Otherwise, the location of the Qt library installation must be indicated by setting ``-D Qt5_DIR=/path/to/qt5/lib/cmake/Qt5``, which is a path to a folder inside the Qt installation that contains the file -``Qt5Config.cmake``. +``Qt5Config.cmake``. Similarly, for Qt6 the location of the Qt library +installation can be indicated by setting ``-D Qt6_DIR=/path/to/qt6/lib/cmake/Qt6``, +if necessary. When both, Qt5 and Qt6 are available, Qt6 will be preferred +unless ``-D LAMMPS_GUI_USE_QT5=yes`` is set. It should be possible to build the LAMMPS GUI as a standalone compilation (e.g. when LAMMPS has been compiled with traditional make), diff --git a/doc/src/fix_srd.rst b/doc/src/fix_srd.rst index 1fc574a7ad..8bfbcf2387 100644 --- a/doc/src/fix_srd.rst +++ b/doc/src/fix_srd.rst @@ -71,14 +71,15 @@ imbue the SRD particles with fluid-like properties, including an effective viscosity. Thus simulations with large solute particles can be run more quickly, to measure solute properties like diffusivity and viscosity in a background fluid. The usual LAMMPS fixes for such -simulations, such as :doc:`fix deform `, :doc:`fix viscosity `, and :doc:`fix nvt/sllod `, +simulations, such as :doc:`fix deform `, +:doc:`fix viscosity `, and :doc:`fix nvt/sllod `, can be used in conjunction with the SRD model. -For more details on how the SRD model is implemented in LAMMPS, :ref:`this paper ` describes the implementation and usage of pure SRD -fluids. :ref:`This paper `, which is nearly complete, describes -the implementation and usage of mixture systems (solute particles in -an SRD fluid). See the examples/srd directory for sample input -scripts using SRD particles in both settings. +For more details on how the SRD model is implemented in LAMMPS, +:ref:`(Petersen) ` describes the implementation and usage of +pure SRD fluids. See the ``examples/srd`` directory for sample input +scripts using SRD particles for that and for mixture systems (solute +particles in an SRD fluid). This fix does two things: @@ -357,28 +358,28 @@ These are the 12 quantities. All are values for the current timestep, except for quantity 5 and the last three, each of which are cumulative quantities since the beginning of the run. -* (1) # of SRD/big collision checks performed -* (2) # of SRDs which had a collision -* (3) # of SRD/big collisions (including multiple bounces) -* (4) # of SRD particles inside a big particle -* (5) # of SRD particles whose velocity was rescaled to be < Vmax -* (6) # of bins for collision searching -* (7) # of bins for SRD velocity rotation -* (8) # of bins in which SRD temperature was computed -* (9) SRD temperature -* (10) # of SRD particles which have undergone max # of bounces -* (11) max # of bounces any SRD particle has had in a single step -* (12) # of reneighborings due to SRD particles moving too far +(1) # of SRD/big collision checks performed +(2) # of SRDs which had a collision +(3) # of SRD/big collisions (including multiple bounces) +(4) # of SRD particles inside a big particle +(5) # of SRD particles whose velocity was rescaled to be < Vmax +(6) # of bins for collision searching +(7) # of bins for SRD velocity rotation +(8) # of bins in which SRD temperature was computed +(9) SRD temperature +(10) # of SRD particles which have undergone max # of bounces +(11) max # of bounces any SRD particle has had in a single step +(12) # of reneighborings due to SRD particles moving too far No parameter of this fix can be used with the *start/stop* keywords of -the :doc:`run ` command. This fix is not invoked during :doc:`energy minimization `. +the :doc:`run ` command. This fix is not invoked during +:doc:`energy minimization `. Restrictions """""""""""" -This command can only be used if LAMMPS was built with the SRD -package. See the :doc:`Build package ` doc -page for more info. +This command can only be used if LAMMPS was built with the SRD package. +See the :doc:`Build package ` doc page for more info. Related commands """""""""""""""" @@ -403,7 +404,3 @@ no, and rescale = yes. **(Petersen)** Petersen, Lechman, Plimpton, Grest, in' t Veld, Schunk, J Chem Phys, 132, 174106 (2010). - -.. _Lechman: - -**(Lechman)** Lechman, et al, in preparation (2010). diff --git a/doc/src/pair_ilp_tmd.rst b/doc/src/pair_ilp_tmd.rst index 482d75a100..70a4768389 100644 --- a/doc/src/pair_ilp_tmd.rst +++ b/doc/src/pair_ilp_tmd.rst @@ -22,12 +22,12 @@ Examples .. code-block:: LAMMPS pair_style hybrid/overlay ilp/tmd 16.0 1 - pair_coeff * * ilp/tmd TMD.ILP Mo S S + pair_coeff * * ilp/tmd MoS2.ILP Mo S S pair_style hybrid/overlay sw/mod sw/mod ilp/tmd 16.0 pair_coeff * * sw/mod 1 tmd.sw.mod Mo S S NULL NULL NULL pair_coeff * * sw/mod 2 tmd.sw.mod NULL NULL NULL Mo S S - pair_coeff * * ilp/tmd TMD.ILP Mo S S Mo S S + pair_coeff * * ilp/tmd MoS2.ILP Mo S S Mo S S Description """"""""""" @@ -69,7 +69,7 @@ calculating the normals. each atom `i`, its six nearest neighboring atoms belonging to the same sub-layer are chosen to define the normal vector `{\bf n}_i`. -The parameter file (e.g. TMD.ILP), is intended for use with *metal* +The parameter file (e.g. MoS2.ILP), is intended for use with *metal* :doc:`units `, with energies in meV. Two additional parameters, *S*, and *rcut* are included in the parameter file. *S* is designed to facilitate scaling of energies. *rcut* is designed to build the neighbor @@ -77,7 +77,7 @@ list for calculating the normals for each atom pair. .. note:: - The parameters presented in the parameter file (e.g. TMD.ILP), + The parameters presented in the parameter file (e.g. MoS2.ILP), are fitted with taper function by setting the cutoff equal to 16.0 Angstrom. Using different cutoff or taper function should be careful. These parameters provide a good description in both short- and long-range @@ -133,10 +133,10 @@ if LAMMPS was built with that package. See the :doc:`Build package This pair style requires the newton setting to be *on* for pair interactions. -The TMD.ILP potential file provided with LAMMPS (see the potentials +The MoS2.ILP potential file provided with LAMMPS (see the potentials directory) are parameterized for *metal* units. You can use this potential with any LAMMPS units, but you would need to create your own -custom TMD.ILP potential file with coefficients listed in the appropriate +custom MoS2.ILP potential file with coefficients listed in the appropriate units, if your simulation does not use *metal* units. Related commands diff --git a/doc/src/pair_reaxff.rst b/doc/src/pair_reaxff.rst index 4dac9baf85..067eb3afc3 100644 --- a/doc/src/pair_reaxff.rst +++ b/doc/src/pair_reaxff.rst @@ -43,22 +43,22 @@ Examples Description """"""""""" -Style *reaxff* computes the ReaxFF potential of van Duin, Goddard and -co-workers. ReaxFF uses distance-dependent bond-order functions to +Pair style *reaxff* computes the ReaxFF potential of van Duin, Goddard +and co-workers. ReaxFF uses distance-dependent bond-order functions to represent the contributions of chemical bonding to the potential -energy. There is more than one version of ReaxFF. The version +energy. There is more than one version of ReaxFF. The version implemented in LAMMPS uses the functional forms documented in the supplemental information of the following paper: -:ref:`(Chenoweth et al., 2008) `. The version integrated -into LAMMPS matches the version of ReaxFF From Summer 2010. For more -technical details about the pair reaxff implementation of ReaxFF, see -the :ref:`(Aktulga) ` paper. The *reaxff* style was initially -implemented as a stand-alone C code and is now converted to C++ and -integrated into LAMMPS as a package. +:ref:`(Chenoweth et al., 2008) ` and matches the +version of the reference ReaxFF implementation from Summer 2010. For +more technical details about the implementation of ReaxFF in pair style +*reaxff*, see the :ref:`(Aktulga) ` paper. The *reaxff* style +was initially implemented as a stand-alone C code and is now converted +to C++ and integrated into LAMMPS as a package. The *reaxff/kk* style is a Kokkos version of the ReaxFF potential that -is derived from the *reaxff* style. The Kokkos version can run on GPUs -and can also use OpenMP multithreading. For more information about the +is derived from the *reaxff* style. The Kokkos version can run on GPUs +and can also use OpenMP multithreading. For more information about the Kokkos package, see :doc:`Packages details ` and :doc:`Speed kokkos ` doc pages. One important consideration when using the *reaxff/kk* style is the choice of either diff --git a/src/OPENMP/npair_omp.h b/src/OPENMP/npair_omp.h index 318fddfd54..7249c59406 100644 --- a/src/OPENMP/npair_omp.h +++ b/src/OPENMP/npair_omp.h @@ -32,6 +32,7 @@ namespace LAMMPS_NS { // get access to number of threads and per-thread data structures via FixOMP #define NPAIR_OMP_INIT \ const int nthreads = comm->nthreads; \ + omp_set_num_threads(nthreads); \ const int ifix = modify->find_fix("package_omp") // get thread id and then assign each thread a fixed chunk of atoms diff --git a/src/compute_msd_chunk.cpp b/src/compute_msd_chunk.cpp index 07234ecfdb..6e7436d6ad 100644 --- a/src/compute_msd_chunk.cpp +++ b/src/compute_msd_chunk.cpp @@ -27,8 +27,8 @@ using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ ComputeMSDChunk::ComputeMSDChunk(LAMMPS *lmp, int narg, char **arg) : - ComputeChunk(lmp, narg, arg), id_fix(nullptr), massproc(nullptr), masstotal(nullptr), - com(nullptr), comall(nullptr), msd(nullptr) + ComputeChunk(lmp, narg, arg), id_fix(nullptr), fix(nullptr), massproc(nullptr), + masstotal(nullptr), com(nullptr), comall(nullptr), msd(nullptr) { if (narg != 4) error->all(FLERR, "Illegal compute msd/chunk command"); @@ -196,6 +196,12 @@ void ComputeMSDChunk::compute_array() void ComputeMSDChunk::allocate() { ComputeChunk::allocate(); + memory->destroy(massproc); + memory->destroy(masstotal); + memory->destroy(com); + memory->destroy(comall); + memory->destroy(msd); + memory->create(massproc, nchunk, "msd/chunk:massproc"); memory->create(masstotal, nchunk, "msd/chunk:masstotal"); memory->create(com, nchunk, 3, "msd/chunk:com"); diff --git a/tools/lammps-gui/CMakeLists.txt b/tools/lammps-gui/CMakeLists.txt index 44fc45c0e2..edfeeb1128 100644 --- a/tools/lammps-gui/CMakeLists.txt +++ b/tools/lammps-gui/CMakeLists.txt @@ -11,6 +11,42 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) option(LAMMPS_GUI_USE_PLUGIN "Load LAMMPS library dynamically at runtime" OFF) mark_as_advanced(LAMMPS_GUI_USE_PLUGIN) +option(LAMMPS_GUI_USE_QT5 "Prefer using Qt5 over Qt6" OFF) + +include(CheckIncludeFileCXX) +# helper function to check for usable omp.h header +function(check_omp_h_include) + find_package(OpenMP COMPONENTS CXX QUIET) + if(OpenMP_CXX_FOUND) + set(CMAKE_REQUIRED_FLAGS ${OpenMP_CXX_FLAGS}) + set(CMAKE_REQUIRED_INCLUDES ${OpenMP_CXX_INCLUDE_DIRS}) + set(CMAKE_REQUIRED_LINK_OPTIONS ${OpenMP_CXX_FLAGS}) + set(CMAKE_REQUIRED_LIBRARIES ${OpenMP_CXX_LIBRARIES}) + check_include_file_cxx(omp.h _have_omp_h) + else() + set(_have_omp_h FALSE) + endif() + set(HAVE_OMP_H_INCLUDE ${_have_omp_h} PARENT_SCOPE) +endfunction() + +# detect if we may enable OpenMP support by default +set(BUILD_OMP_DEFAULT OFF) +find_package(OpenMP COMPONENTS CXX QUIET) +if(OpenMP_CXX_FOUND) + check_omp_h_include() + if(HAVE_OMP_H_INCLUDE) + set(BUILD_OMP_DEFAULT ON) + endif() +endif() + +option(BUILD_OMP "Build with OpenMP support" ${BUILD_OMP_DEFAULT}) +if(BUILD_OMP) + find_package(OpenMP COMPONENTS CXX REQUIRED) + check_omp_h_include() + if(NOT HAVE_OMP_H_INCLUDE) + message(FATAL_ERROR "Cannot find the 'omp.h' header file required for full OpenMP support") + endif() +endif() # checks # when this file is included as subdirectory in the LAMMPS build, many settings are directly imported @@ -73,7 +109,15 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Windows") endif() # we require Qt 5 and at least version 5.12 at that. -find_package(Qt5 5.12 REQUIRED COMPONENTS Widgets Charts) +if(NOT LAMMPS_GUI_USE_QT5) + find_package(Qt6 6.2 COMPONENTS Widgets Charts) +endif() +if(NOT Qt6_FOUND) + find_package(Qt5 5.12 REQUIRED COMPONENTS Widgets Charts) + set(QT_VERSION_MAJOR "5") +else() + set(QT_VERSION_MAJOR "6") +endif() set(PROJECT_SOURCES main.cpp @@ -105,7 +149,11 @@ set(PROJECT_SOURCES ${PLUGIN_LOADER_SRC} ${ICON_RC_FILE} ) -qt5_add_resources(PROJECT_SOURCES lammpsgui.qrc) +if(QT_VERSION_MAJOR EQUAL 6) + qt6_add_resources(PROJECT_SOURCES lammpsgui.qrc) +else() + qt5_add_resources(PROJECT_SOURCES lammpsgui.qrc) +endif() if(APPLE) set(MACOSX_ICON_FILE ${LAMMPS_DIR}/cmake/packaging/lammps.icns) @@ -113,10 +161,22 @@ if(APPLE) set(MACOSX_BACKGROUND_FILE ${LAMMPS_DIR}/cmake/packaging/LAMMPS_DMG_Background.png) endif() -add_executable(lammps-gui - ${MACOSX_ICON_FILE} - ${PROJECT_SOURCES} -) +if(QT_VERSION_MAJOR EQUAL 6) + qt_add_executable(lammps-gui + MANUAL_FINALIZATION + ${MACOSX_ICON_FILE} + ${PROJECT_SOURCES} + ) +else() + add_executable(lammps-gui + ${MACOSX_ICON_FILE} + ${PROJECT_SOURCES} + ) +endif() + +if(QT_VERSION_MAJOR EQUAL 6) + qt_finalize_executable(lammps-gui) +endif() # compilation settings if(LAMMPS_GUI_USE_PLUGIN) @@ -128,7 +188,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 Qt5::Charts) +target_link_libraries(lammps-gui PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${VERSION_MAJOR}::Charts) if(BUILD_OMP) find_package(OpenMP COMPONENTS CXX REQUIRED) target_link_libraries(lammps-gui PRIVATE OpenMP::OpenMP_CXX) @@ -209,7 +269,7 @@ elseif((CMAKE_SYSTEM_NAME STREQUAL "Windows") AND CMAKE_CROSSCOMPILING) COMMENT "Create zip file with windows binaries" BYPRODUCT LAMMPS-Win10-amd64.zip WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) -elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") +elseif((CMAKE_SYSTEM_NAME STREQUAL "Linux") AND NOT LAMMPS_GUI_USE_PLUGIN) install(TARGETS lammps-gui DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/lammps-gui.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications/) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/lammps-input.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/mime/packages/) diff --git a/tools/lammps-gui/TODO.md b/tools/lammps-gui/TODO.md index e4ca44ba3d..ee05e67225 100644 --- a/tools/lammps-gui/TODO.md +++ b/tools/lammps-gui/TODO.md @@ -23,7 +23,6 @@ LAMMPS-GUI TODO list: # Long term ideas (v2.x) - rewrite entire application to build the App and its layout manually -- port to Qt6 (with compatibility to Qt5?) - also a rewrite should establish consistent naming conventions. now we have a mix of LAMMPS style, Qt style, and others. - add option to attach a debugger to the running program (highly non-portable, need customization support in preferences) - write a "wizard" dialog that can be used for beginners to create an input file template for a few typical use scenarios diff --git a/tools/lammps-gui/chartviewer.cpp b/tools/lammps-gui/chartviewer.cpp index f28625d9dc..fbd888f1cd 100644 --- a/tools/lammps-gui/chartviewer.cpp +++ b/tools/lammps-gui/chartviewer.cpp @@ -15,11 +15,20 @@ #include "lammpsgui.h" +#include +#include +#include #include +#include +#include +#include #include +#include +#include #include #include #include +#include #include using namespace QtCharts; @@ -53,13 +62,13 @@ ChartWindow::ChartWindow(const QString &_filename, QWidget *parent) : file->addSeparator(); stopAct = file->addAction("Stop &Run", this, &ChartWindow::stop_run); stopAct->setIcon(QIcon(":/icons/process-stop.png")); - stopAct->setShortcut(QKeySequence::fromString("Ctrl+/")); + stopAct->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_Slash)); closeAct = file->addAction("&Close", this, &QWidget::close); closeAct->setIcon(QIcon(":/icons/window-close.png")); - closeAct->setShortcut(QKeySequence::fromString("Ctrl+W")); + closeAct->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_W)); quitAct = file->addAction("&Quit", this, &ChartWindow::quit); quitAct->setIcon(QIcon(":/icons/application-exit.png")); - quitAct->setShortcut(QKeySequence::fromString("Ctrl+Q")); + quitAct->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q)); auto *layout = new QVBoxLayout; layout->addLayout(top); setLayout(layout); @@ -76,7 +85,10 @@ int ChartWindow::get_step() const { if (charts.size() > 0) { auto *v = charts[0]; - return (int)v->get_step(v->get_count() - 1); + if (v) + return (int)v->get_step(v->get_count() - 1); + else + return -1; } else { return -1; } @@ -115,10 +127,10 @@ void ChartWindow::add_data(int step, double data, int index) void ChartWindow::quit() { - LammpsGui *main; + LammpsGui *main = nullptr; for (QWidget *widget : QApplication::topLevelWidgets()) if (widget->objectName() == "LammpsGui") main = dynamic_cast(widget); - main->quit(); + if (main) main->quit(); } void ChartWindow::reset_zoom() @@ -129,10 +141,10 @@ void ChartWindow::reset_zoom() void ChartWindow::stop_run() { - LammpsGui *main; + LammpsGui *main = nullptr; for (QWidget *widget : QApplication::topLevelWidgets()) if (widget->objectName() == "LammpsGui") main = dynamic_cast(widget); - main->stop_run(); + if (main) main->stop_run(); } void ChartWindow::saveAs() @@ -288,7 +300,7 @@ void ChartViewer::add_data(int step, double data) if (last_step < step) { last_step = step; series->append(step, data); - auto points = series->pointsVector(); + auto points = series->points(); qreal xmin = 1.0e100; qreal xmax = -1.0e100; @@ -309,7 +321,7 @@ void ChartViewer::add_data(int step, double data) void ChartViewer::reset_zoom() { - auto points = series->pointsVector(); + auto points = series->points(); qreal xmin = 1.0e100; qreal xmax = -1.0e100; diff --git a/tools/lammps-gui/chartviewer.h b/tools/lammps-gui/chartviewer.h index 248fdad7bb..da0468eaf8 100644 --- a/tools/lammps-gui/chartviewer.h +++ b/tools/lammps-gui/chartviewer.h @@ -14,16 +14,17 @@ #ifndef CHARTVIEWER_H #define CHARTVIEWER_H +#include #include #include #include -#include class QAction; class QMenuBar; class QMenu; -class QComboBox; +namespace QtCharts { class ChartViewer; +} class ChartWindow : public QWidget { Q_OBJECT @@ -64,12 +65,18 @@ private: QAction *closeAct, *stopAct, *quitAct; QString filename; - QList charts; + QList charts; }; /* -------------------------------------------------------------------- */ -class ChartViewer : public QtCharts::QChartView { +#include +#include +#include +#include + +namespace QtCharts { +class ChartViewer : public QChartView { Q_OBJECT public: @@ -81,16 +88,17 @@ public: int get_index() const { return index; }; int get_count() const { return series->count(); } const char *get_title() const { return series->name().toLocal8Bit(); } - double get_step(int index) const { return series->at(index).x(); } - double get_data(int index) const { return series->at(index).y(); } + double get_step(int index) const { return (index < 0) ? 0.0 : series->at(index).x(); } + double get_data(int index) const { return (index < 0) ? 0.0 : series->at(index).y(); } private: int last_step, index; - QtCharts::QChart *chart; - QtCharts::QLineSeries *series; - QtCharts::QValueAxis *xaxis; - QtCharts::QValueAxis *yaxis; + QChart *chart; + QLineSeries *series; + QValueAxis *xaxis; + QValueAxis *yaxis; }; +} // namespace QtCharts #endif // Local Variables: diff --git a/tools/lammps-gui/codeeditor.cpp b/tools/lammps-gui/codeeditor.cpp index 34193bc320..e95f576be0 100644 --- a/tools/lammps-gui/codeeditor.cpp +++ b/tools/lammps-gui/codeeditor.cpp @@ -564,14 +564,16 @@ void CodeEditor::keyPressEvent(QKeyEvent *event) // process key event in parent class QPlainTextEdit::keyPressEvent(event); - // if enabled, try pop up completion automatically after 3 characters + // if enabled, try pop up completion automatically after 2 characters if (automatic_completion) { auto cursor = textCursor(); auto line = cursor.block().text(); + if (line.isEmpty()) return; // QTextCursor::WordUnderCursor is unusable here since recognizes '/' as word boundary. // Work around it by manually searching for the location of the beginning of the word. - int begin = cursor.positionInBlock(); + int begin = qMin(cursor.positionInBlock(), line.length() - 1); + while (begin >= 0) { if (line[begin].isSpace()) break; --begin; @@ -748,7 +750,7 @@ void CodeEditor::runCompletion() // QTextCursor::WordUnderCursor is unusable here since it recognizes '/' as word boundary. // Work around it by manually searching for the beginning and end position of the word // under the cursor and then using that substring. - int begin = cursor.positionInBlock(); + int begin = qMin(cursor.positionInBlock(), line.length() - 1); line = cursor.block().text(); while (begin >= 0) { if (line[begin].isSpace()) break; @@ -990,8 +992,26 @@ void CodeEditor::insertCompletedCommand(const QString &completion) { auto *completer = qobject_cast(sender()); if (completer->widget() != this) return; + + // select the entire word (non-space text) under the cursor + // we need to do it in this compicated way, since QTextCursor does not recognize + // special characters as part of a word. auto cursor = textCursor(); - cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor); + auto line = cursor.block().text(); + int begin = cursor.positionInBlock(); + do { + if (line[begin].isSpace()) break; + --begin; + } while (begin >= 0); + + int end = begin + 1; + while (end < line.length()) { + if (line[end].isSpace()) break; + ++end; + } + + cursor.setPosition(cursor.position() - cursor.positionInBlock() + begin + 1); + cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, end - begin - 1); cursor.insertText(completion); setTextCursor(cursor); } diff --git a/tools/lammps-gui/imageviewer.cpp b/tools/lammps-gui/imageviewer.cpp index 86be0b66df..00b08f3f47 100644 --- a/tools/lammps-gui/imageviewer.cpp +++ b/tools/lammps-gui/imageviewer.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -131,7 +132,7 @@ static const QString blank(" "); ImageViewer::ImageViewer(const QString &fileName, LammpsWrapper *_lammps, QWidget *parent) : QDialog(parent), menuBar(new QMenuBar), imageLabel(new QLabel), scrollArea(new QScrollArea), - lammps(_lammps), group("all"), filename(fileName), useelements(false) + lammps(_lammps), group("all"), filename(fileName), useelements(false), usediameter(false) { imageLabel->setBackgroundRole(QPalette::Base); imageLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); @@ -268,7 +269,7 @@ ImageViewer::ImageViewer(const QString &fileName, LammpsWrapper *_lammps, QWidge // properties directly since lookup in reset_view() will have failed dobox->setChecked(showbox); dovdw->setChecked(vdwfactor > 1.0); - dovdw->setEnabled(useelements); + dovdw->setEnabled(useelements || usediameter); doaxes->setChecked(showaxes); dossao->setChecked(usessao); doanti->setChecked(antialias); @@ -435,7 +436,7 @@ void ImageViewer::createImage() dumpcmd += "'" + dumpfile.fileName() + "'"; settings.beginGroup("snapshot"); - int hhrot = (hrot > 180) ? 360 - hrot : hrot; + int hhrot = (hrot > 180) ? 360 - hrot : hrot; // determine elements from masses and set their covalent radii int ntypes = lammps->extract_setting("ntypes"); @@ -454,9 +455,10 @@ void ImageViewer::createImage() adiams += QString("adiam %1 %2 ").arg(i).arg(vdwfactor * pte_vdw_radius[idx]); } } + usediameter = lammps->extract_setting("radius_flag") != 0; // adjust pushbutton state and clear adiams string to disable VDW display, if needed - if (useelements) { + if (useelements || usediameter) { auto *button = findChild("vdw"); if (button) button->setEnabled(true); } else { @@ -469,7 +471,10 @@ void ImageViewer::createImage() dumpcmd += blank + "element"; else dumpcmd += blank + settings.value("color", "type").toString(); - dumpcmd += blank + settings.value("diameter", "type").toString(); + if (usediameter && (vdwfactor > 1.0)) + dumpcmd += blank + "diameter"; + else + dumpcmd += blank + settings.value("diameter", "type").toString(); dumpcmd += QString(" size %1 %2").arg(xsize).arg(ysize); dumpcmd += QString(" zoom %1").arg(zoom); dumpcmd += " shiny 0.5 "; @@ -528,10 +533,10 @@ void ImageViewer::copy() {} void ImageViewer::quit() { - LammpsGui *main; + LammpsGui *main = nullptr; for (QWidget *widget : QApplication::topLevelWidgets()) if (widget->objectName() == "LammpsGui") main = dynamic_cast(widget); - main->quit(); + if (main) main->quit(); } void ImageViewer::saveFile(const QString &fileName) @@ -554,10 +559,10 @@ void ImageViewer::createActions() fileMenu->addSeparator(); QAction *exitAct = fileMenu->addAction("&Close", this, &QWidget::close); exitAct->setIcon(QIcon(":/icons/window-close.png")); - exitAct->setShortcut(QKeySequence::fromString("Ctrl+W")); + exitAct->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_W)); QAction *quitAct = fileMenu->addAction("&Quit", this, &ImageViewer::quit); quitAct->setIcon(QIcon(":/icons/application-exit.png")); - quitAct->setShortcut(QKeySequence::fromString("Ctrl+Q")); + quitAct->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q)); } void ImageViewer::updateActions() diff --git a/tools/lammps-gui/imageviewer.h b/tools/lammps-gui/imageviewer.h index 8946c6cc8b..1be7790666 100644 --- a/tools/lammps-gui/imageviewer.h +++ b/tools/lammps-gui/imageviewer.h @@ -88,7 +88,7 @@ private: int xsize, ysize; int hrot, vrot; double zoom, vdwfactor; - bool showbox, showaxes, antialias, usessao, useelements; + bool showbox, showaxes, antialias, usessao, useelements, usediameter; }; #endif diff --git a/tools/lammps-gui/lammpsgui.cpp b/tools/lammps-gui/lammpsgui.cpp index ba080dbec3..11f2554b55 100644 --- a/tools/lammps-gui/lammpsgui.cpp +++ b/tools/lammps-gui/lammpsgui.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -69,8 +70,10 @@ LammpsGui::LammpsGui(QWidget *parent, const char *filename) : // enforce using the plain ASCII C locale within the GUI. QLocale::setDefault(QLocale("C")); - // register QList +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + // register QList only needed for Qt5 qRegisterMetaTypeStreamOperators>("QList"); +#endif ui->setupUi(this); this->setCentralWidget(ui->textEdit); @@ -81,9 +84,13 @@ LammpsGui::LammpsGui(QWidget *parent, const char *filename) : // use $HOME if we get dropped to "/" like on macOS if (current_dir == "/") current_dir = QDir::homePath(); +#define stringify(x) myxstr(x) +#define myxstr(x) #x QCoreApplication::setOrganizationName("The LAMMPS Developers"); QCoreApplication::setOrganizationDomain("lammps.org"); - QCoreApplication::setApplicationName("LAMMPS GUI"); + QCoreApplication::setApplicationName("LAMMPS GUI - QT" stringify(QT_VERSION_MAJOR)); +#undef stringify +#undef myxstr // restore and initialize settings QSettings settings; @@ -588,7 +595,8 @@ void LammpsGui::open_file(const QString &fileName) if (!file.open(QIODevice::ReadOnly | QFile::Text)) { QMessageBox::warning(this, "Warning", "Cannot open file " + path.absoluteFilePath() + ": " + - file.errorString() + ".\nWill create new file on saving editor buffer."); + file.errorString() + + ".\nWill create new file on saving editor buffer."); ui->textEdit->document()->setPlainText(QString()); } else { QTextStream in(&file); @@ -1039,9 +1047,9 @@ void LammpsGui::do_run(bool use_buffer) logwindow->document()->setDefaultFont(text_font); logwindow->setLineWrapMode(LogWindow::NoWrap); logwindow->setMinimumSize(400, 300); - QShortcut *shortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_W), logwindow); + QShortcut *shortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_W), logwindow); QObject::connect(shortcut, &QShortcut::activated, logwindow, &LogWindow::close); - shortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Slash), logwindow); + shortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_Slash), logwindow); QObject::connect(shortcut, &QShortcut::activated, this, &LammpsGui::stop_run); if (settings.value("viewlog", true).toBool()) logwindow->show(); @@ -1058,9 +1066,9 @@ void LammpsGui::do_run(bool use_buffer) .arg(run_counter)); chartwindow->setWindowIcon(QIcon(":/icons/lammps-icon-128x128.png")); chartwindow->setMinimumSize(400, 300); - shortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_W), chartwindow); + shortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_W), chartwindow); QObject::connect(shortcut, &QShortcut::activated, chartwindow, &ChartWindow::close); - shortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Slash), chartwindow); + shortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_Slash), chartwindow); QObject::connect(shortcut, &QShortcut::activated, this, &LammpsGui::stop_run); if (settings.value("viewchart", true).toBool()) chartwindow->show(); diff --git a/tools/lammps-gui/lammpsgui.h b/tools/lammps-gui/lammpsgui.h index 0c622f0285..0dd34f2c49 100644 --- a/tools/lammps-gui/lammpsgui.h +++ b/tools/lammps-gui/lammpsgui.h @@ -16,8 +16,10 @@ #include +#include #include #include +#include #include #include diff --git a/tools/lammps-gui/logwindow.cpp b/tools/lammps-gui/logwindow.cpp index ab1886f1bd..73ec81d06c 100644 --- a/tools/lammps-gui/logwindow.cpp +++ b/tools/lammps-gui/logwindow.cpp @@ -35,12 +35,14 @@ LogWindow::LogWindow(const QString &_filename, QWidget *parent) : QSettings settings; resize(settings.value("logx", 500).toInt(), settings.value("logy", 320).toInt()); - auto action = new QShortcut(QKeySequence::fromString("Ctrl+S"), this); + auto action = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_S), this); connect(action, &QShortcut::activated, this, &LogWindow::save_as); - action = new QShortcut(QKeySequence::fromString("Ctrl+Q"), this); + action = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q), this); connect(action, &QShortcut::activated, this, &LogWindow::quit); - action = new QShortcut(QKeySequence(Qt::Key_Slash, Qt::CTRL), this); + action = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_Slash), this); connect(action, &QShortcut::activated, this, &LogWindow::stop_run); + + installEventFilter(this); } void LogWindow::closeEvent(QCloseEvent *event) @@ -55,18 +57,18 @@ void LogWindow::closeEvent(QCloseEvent *event) void LogWindow::quit() { - LammpsGui *main; + LammpsGui *main = nullptr; for (QWidget *widget : QApplication::topLevelWidgets()) if (widget->objectName() == "LammpsGui") main = dynamic_cast(widget); - main->quit(); + if (main) main->quit(); } void LogWindow::stop_run() { - LammpsGui *main; + LammpsGui *main = nullptr; for (QWidget *widget : QApplication::topLevelWidgets()) if (widget->objectName() == "LammpsGui") main = dynamic_cast(widget); - main->stop_run(); + if (main) main->stop_run(); } void LogWindow::save_as() @@ -99,15 +101,35 @@ void LogWindow::contextMenuEvent(QContextMenuEvent *event) menu->addSeparator(); auto action = menu->addAction(QString("Save Log to File ...")); action->setIcon(QIcon(":/icons/document-save-as.png")); - action->setShortcut(QKeySequence::fromString("Ctrl+S")); + action->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_S)); connect(action, &QAction::triggered, this, &LogWindow::save_as); action = menu->addAction("&Close Window", this, &QWidget::close); action->setIcon(QIcon(":/icons/window-close.png")); - action->setShortcut(QKeySequence::fromString("Ctrl+W")); + action->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_W)); menu->exec(event->globalPos()); delete menu; } +// event filter to handle "Ambiguous shortcut override" issues +bool LogWindow::eventFilter(QObject *watched, QEvent *event) +{ + if (event->type() == QEvent::ShortcutOverride) { + QKeyEvent *keyEvent = dynamic_cast(event); + if (!keyEvent) return QWidget::eventFilter(watched, event); + if (keyEvent->modifiers().testFlag(Qt::ControlModifier) && keyEvent->key() == '/') { + stop_run(); + event->accept(); + return true; + } + if (keyEvent->modifiers().testFlag(Qt::ControlModifier) && keyEvent->key() == 'W') { + close(); + event->accept(); + return true; + } + } + return QWidget::eventFilter(watched, event); +} + // Local Variables: // c-basic-offset: 4 // End: diff --git a/tools/lammps-gui/logwindow.h b/tools/lammps-gui/logwindow.h index 8923e35ee5..ad0691d0cc 100644 --- a/tools/lammps-gui/logwindow.h +++ b/tools/lammps-gui/logwindow.h @@ -30,6 +30,7 @@ private slots: protected: void closeEvent(QCloseEvent *event) override; void contextMenuEvent(QContextMenuEvent *event) override; + bool eventFilter(QObject *watched, QEvent *event) override; private: QString filename; diff --git a/tools/lammps-gui/preferences.cpp b/tools/lammps-gui/preferences.cpp index c760e6610b..fd01bb5046 100644 --- a/tools/lammps-gui/preferences.cpp +++ b/tools/lammps-gui/preferences.cpp @@ -286,12 +286,12 @@ GeneralTab::GeneralTab(QSettings *_settings, LammpsWrapper *_lammps, QWidget *pa void GeneralTab::updatefonts(const QFont &all, const QFont &text) { - LammpsGui *main; + LammpsGui *main = nullptr; for (QWidget *widget : QApplication::topLevelWidgets()) if (widget->objectName() == "LammpsGui") main = dynamic_cast(widget); QApplication::setFont(all); - main->ui->textEdit->document()->setDefaultFont(text); + if (main) main->ui->textEdit->document()->setDefaultFont(text); } void GeneralTab::newallfont() @@ -410,11 +410,19 @@ AcceleratorTab::AcceleratorTab(QSettings *_settings, LammpsWrapper *_lammps, QWi #endif auto *choices = new QFrame; auto *choiceLayout = new QVBoxLayout; +#if defined(_OPENMP) auto *ntlabel = new QLabel(QString("Number of threads (max %1):").arg(maxthreads)); auto *ntchoice = new QLineEdit(settings->value("nthreads", maxthreads).toString()); +#else + auto *ntlabel = new QLabel(QString("Number of threads (OpenMP not available):")); + auto *ntchoice = new QLineEdit("1"); +#endif auto *intval = new QIntValidator(1, maxthreads, this); ntchoice->setValidator(intval); ntchoice->setObjectName("nthreads"); +#if !defined(_OPENMP) + ntchoice->setEnabled(false); +#endif choiceLayout->addWidget(ntlabel); choiceLayout->addWidget(ntchoice); diff --git a/tools/lammps-gui/slideshow.cpp b/tools/lammps-gui/slideshow.cpp index de7742f22f..140c703ca3 100644 --- a/tools/lammps-gui/slideshow.cpp +++ b/tools/lammps-gui/slideshow.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -50,11 +51,11 @@ SlideShow::SlideShow(const QString &fileName, QWidget *parent) : imageName->setAlignment(Qt::AlignCenter); imageName->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - auto *shortcut = new QShortcut(QKeySequence::fromString("Ctrl+W"), this); + auto *shortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_W), this); QObject::connect(shortcut, &QShortcut::activated, this, &QWidget::close); - shortcut = new QShortcut(QKeySequence::fromString("Ctrl+/"), this); + shortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_Slash), this); QObject::connect(shortcut, &QShortcut::activated, this, &SlideShow::stop_run); - shortcut = new QShortcut(QKeySequence::fromString("Ctrl+Q"), this); + shortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q), this); QObject::connect(shortcut, &QShortcut::activated, this, &SlideShow::quit); buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); @@ -198,18 +199,18 @@ void SlideShow::loadImage(int idx) void SlideShow::quit() { - LammpsGui *main; + LammpsGui *main = nullptr; for (QWidget *widget : QApplication::topLevelWidgets()) if (widget->objectName() == "LammpsGui") main = dynamic_cast(widget); - main->quit(); + if (main) main->quit(); } void SlideShow::stop_run() { - LammpsGui *main; + LammpsGui *main = nullptr; for (QWidget *widget : QApplication::topLevelWidgets()) if (widget->objectName() == "LammpsGui") main = dynamic_cast(widget); - main->stop_run(); + if (main) main->stop_run(); } void SlideShow::movie() diff --git a/tools/lammps-gui/stdcapture.cpp b/tools/lammps-gui/stdcapture.cpp index 428277cc10..b09aebf053 100644 --- a/tools/lammps-gui/stdcapture.cpp +++ b/tools/lammps-gui/stdcapture.cpp @@ -77,6 +77,7 @@ bool StdCapture::EndCapture() int bytesRead; bool fd_blocked; + int maxwait = 100; do { bytesRead = 0; @@ -93,9 +94,10 @@ bool StdCapture::EndCapture() buf[bytesRead] = 0; m_captured += buf; } else if (bytesRead < 0) { - fd_blocked = ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR)); + fd_blocked = ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR)) && (maxwait > 0); if (fd_blocked) std::this_thread::sleep_for(std::chrono::milliseconds(10)); + --maxwait; } } while (fd_blocked || (bytesRead == (bufSize - 1))); m_capturing = false;