From 61ac62157d60c3fb71307faecf649ff3252211f7 Mon Sep 17 00:00:00 2001 From: Noella Spitz Date: Wed, 27 Mar 2024 15:56:20 +0000 Subject: [PATCH 01/13] Create GUI setInputFilename function to also set the working directory. --- src/gui/gui.h | 2 ++ src/gui/menu_file.cpp | 20 +++++++++++++------- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/gui/gui.h b/src/gui/gui.h index 5447e10909..29cc52b908 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -126,6 +126,8 @@ class DissolveWindow : public QMainWindow const int recentFileLimit_; private: + // Set the input file path + void setInputFilename(const QString filename); // Check whether current input needs to be saved and, if so, if it saved successfully bool checkSaveCurrentInput(); // Clear all data and start new simulation afresh diff --git a/src/gui/menu_file.cpp b/src/gui/menu_file.cpp index cc598a2d32..54326e6cbc 100644 --- a/src/gui/menu_file.cpp +++ b/src/gui/menu_file.cpp @@ -7,6 +7,15 @@ #include #include +// Set the input filename and the working directory of dissolve +void DissolveWindow::setInputFilename(const QString filename) +{ + QFileInfo inputFileInfo(filename); + QDir::setCurrent(inputFileInfo.absoluteDir().absolutePath()); + + dissolve_.setInputFilename(qPrintable(filename)); +} + // Check whether current input needs to be saved and, if so, if it saved successfully bool DissolveWindow::checkSaveCurrentInput() { @@ -32,7 +41,7 @@ bool DissolveWindow::checkSaveCurrentInput() if (newFile.isEmpty()) return false; - dissolve_.setInputFilename(qPrintable(newFile)); + setInputFilename(newFile); } // Save the file @@ -243,7 +252,7 @@ void DissolveWindow::on_FileSaveAction_triggered(bool checked) if (newFile.isEmpty()) return; - dissolve_.setInputFilename(qPrintable(newFile)); + setInputFilename(newFile); addRecentFile(newFile); } @@ -270,17 +279,14 @@ void DissolveWindow::on_FileSaveAsAction_triggered(bool checked) if (newFile.isEmpty()) return; - dissolve_.setInputFilename(qPrintable(newFile)); - // Attempt to save the file - if (!dissolve_.saveInput(dissolve_.inputFilename())) + if (!dissolve_.saveInput(qPrintable(newFile))) return; modified_ = false; // Update the current working directory to be local to the new input file - QFileInfo inputFileInfo(newFile); - QDir::setCurrent(inputFileInfo.absoluteDir().absolutePath()); + setInputFilename(newFile); addRecentFile(newFile); From 5c18e7033cb9111493e21340c16947dd7c9b7357 Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Tue, 27 Feb 2024 09:05:41 +0000 Subject: [PATCH 02/13] 1806 faster combinations (#1807) Co-authored-by: Tristan Youngs --- flake.nix | 1 - src/math/CMakeLists.txt | 2 ++ src/math/combinations.cpp | 55 +++++++++++--------------------- src/math/combinations.h | 13 ++------ src/math/polynomial.cpp | 19 +++++++++++ src/math/polynomial.h | 16 ++++++++++ src/modules/energy/functions.cpp | 2 +- src/modules/gr/functions.cpp | 2 +- src/templates/polynomial.cpp | 7 ++++ tests/math/CMakeLists.txt | 1 + tests/math/combinations.cpp | 15 +++++---- tests/math/polynomial.cpp | 36 +++++++++++++++++++++ 12 files changed, 112 insertions(+), 57 deletions(-) create mode 100644 src/math/polynomial.cpp create mode 100644 src/math/polynomial.h create mode 100644 src/templates/polynomial.cpp create mode 100644 tests/math/polynomial.cpp diff --git a/flake.nix b/flake.nix index 96c685e495..aed54999b0 100644 --- a/flake.nix +++ b/flake.nix @@ -149,7 +149,6 @@ (onedpl pkgs) - busybox ccache ccls cmakeWithGui diff --git a/src/math/CMakeLists.txt b/src/math/CMakeLists.txt index 9dc000f853..3e6d148721 100644 --- a/src/math/CMakeLists.txt +++ b/src/math/CMakeLists.txt @@ -25,6 +25,7 @@ add_library( mc.cpp praxis.cpp poissonFit.cpp + polynomial.cpp range.cpp regression.cpp sampledData1D.cpp @@ -62,6 +63,7 @@ add_library( matrix3.h matrix4.h mc.h + polynomial.h poissonFit.h praxis.h range.h diff --git a/src/math/combinations.cpp b/src/math/combinations.cpp index 4e371dd36d..d87489c1ff 100644 --- a/src/math/combinations.cpp +++ b/src/math/combinations.cpp @@ -2,49 +2,30 @@ // Copyright (c) 2024 Team Dissolve and contributors #include "math/combinations.h" +#include "math/polynomial.h" +#include std::pair Combinations::nthCombination(int n) const { - auto [a2, remainder] = getCoefficent(n, 2); - auto [a1, remainder_a1] = getCoefficent(remainder, 1); - return {a1, a2}; -} + /* + If we let N_ be the number of items being combined and n be the + index of the pair {x, y}. The smallest value of n for and value + of i is given by the relation: -int Combinations::nChooseK(int n, int k) const -{ - // Function n choose k specialised for either k = 1 or k = 2 - switch (k) - { - case 1: - return n; - case 2: - return (n * (n - 1)) >> 1; - default: - return -1; - } -} + -½x² + (N_ -½1)x = n -Combinations::NthCombination Combinations::getCoefficent(int N, int k) const -{ + By the quadratic formula, we can solve for i for a given n. The + floor of that value is the x value for our pair and, by plugging + it ack into the relation, we can find the offset for the y value + as well. + */ + Quadratic relation(-0.5, N_ - 0.5, -n); - auto at = k - 1; - auto nCt = 0; - if (nCt >= N) - return {at, N - nCt}; + auto roots = relation.roots(); + auto x = std::floor(relation.roots().second); + auto y = x + 1 - relation.at(x); - auto atPrev = at; - auto nCtPrev = nCt; - bool found = false; - while (!found) - { - atPrev = at; - at = atPrev + 1; - nCtPrev = nCt; - nCt = nChooseK(at, k); - if (nCt > N) - found = true; - } - return {atPrev, N - nCtPrev}; + return {x, y}; } -int Combinations::getNumCombinations() const { return nChooseK(N_, k_); } +int Combinations::getNumCombinations() const { return N_ * (N_ - 1) / 2; } diff --git a/src/math/combinations.h b/src/math/combinations.h index 9c3db7c332..689bd74b13 100644 --- a/src/math/combinations.h +++ b/src/math/combinations.h @@ -29,19 +29,12 @@ class Combinations }; public: - Combinations(int N, int k) : N_(N), k_(k) {} + Combinations(int N) : N_(N) {} // Returns the nth combination std::pair nthCombination(int n) const; // returns the number of combinations int getNumCombinations() const; - private: - // returns n choose k (specialised for k = 1 and k = 2) - int nChooseK(int N, int k) const; - // returns the bionomial coefficent, - // see https : // en.wikipedia.org/wiki/Combinatorial_number_system - NthCombination getCoefficent(int n, int k) const; - + // The number of items in the set that we are combining int N_; - int k_; -}; \ No newline at end of file +}; diff --git a/src/math/polynomial.cpp b/src/math/polynomial.cpp new file mode 100644 index 0000000000..14e290cfa7 --- /dev/null +++ b/src/math/polynomial.cpp @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team Dissolve and contributors + +#include "math/polynomial.h" +#include +#include + +Quadratic::Quadratic(double a, double b, double c) : a_(a), b_(b), c_(c) {} + +double Quadratic::at(double x) const { return a_ * x * x + b_ * x + c_; } + +bool Quadratic::hasRoots() const { return b_ * b_ > 4 * a_ * c_; } + +std::pair Quadratic::roots() const +{ + assert(hasRoots()); + auto det = std::sqrt(b_ * b_ - 4 * a_ * c_); + return {(-b_ - det) / (2 * a_), (-b_ + det) / (2 * a_)}; +} diff --git a/src/math/polynomial.h b/src/math/polynomial.h new file mode 100644 index 0000000000..aff32b3b1c --- /dev/null +++ b/src/math/polynomial.h @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team Dissolve and contributors + +#include + +class Quadratic +{ + private: + double a_, b_, c_; + + public: + Quadratic(double a = 0, double b = 0, double c = 0); + bool hasRoots() const; + std::pair roots() const; + double at(double x) const; +}; diff --git a/src/modules/energy/functions.cpp b/src/modules/energy/functions.cpp index 9846ea18d3..5fadf8d02b 100644 --- a/src/modules/energy/functions.cpp +++ b/src/modules/energy/functions.cpp @@ -70,7 +70,7 @@ double EnergyModule::pairPotentialEnergy(const ProcessPool &procPool, const Spec const auto cutoff = potentialMap.range(); // Get start/end for loop - Combinations comb(sp->nAtoms(), 2); + Combinations comb(sp->nAtoms()); auto offset = procPool.interleavedLoopStart(ProcessPool::PoolStrategy); auto nChunks = procPool.interleavedLoopStride(ProcessPool::PoolStrategy); auto [loopStart, loopEnd] = chop_range(0, comb.getNumCombinations(), nChunks, offset); diff --git a/src/modules/gr/functions.cpp b/src/modules/gr/functions.cpp index 8bb274e0ee..570740f82e 100644 --- a/src/modules/gr/functions.cpp +++ b/src/modules/gr/functions.cpp @@ -171,7 +171,7 @@ bool GRModule::calculateGRCells(const ProcessPool &procPool, Configuration *cfg, auto &cellArray = cfg->cells(); // Loop context is to use all processes in Pool as one group - Combinations comb(cellArray.nCells(), 2); + Combinations comb(cellArray.nCells()); auto offset = procPool.interleavedLoopStart(ProcessPool::PoolStrategy); auto nChunks = procPool.interleavedLoopStride(ProcessPool::PoolStrategy); auto [cStart, cEnd] = chop_range(0, comb.getNumCombinations(), nChunks, offset); diff --git a/src/templates/polynomial.cpp b/src/templates/polynomial.cpp new file mode 100644 index 0000000000..ce5287c841 --- /dev/null +++ b/src/templates/polynomial.cpp @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team Dissolve and contributors + +#include "templates/polynomial.h" +#include + +Quadratic::Quadratic(double a, double b, double c) : a_(a), b_(b), c_(c) {} diff --git a/tests/math/CMakeLists.txt b/tests/math/CMakeLists.txt index e9a2281a46..3411e73d22 100644 --- a/tests/math/CMakeLists.txt +++ b/tests/math/CMakeLists.txt @@ -6,5 +6,6 @@ dissolve_add_test(SRC integerHistogram1D.cpp) dissolve_add_test(SRC function1D.cpp) dissolve_add_test(SRC geometryMin.cpp) dissolve_add_test(SRC interpolator.cpp) +dissolve_add_test(SRC polynomial.cpp) dissolve_add_test(SRC sampledValues.cpp) dissolve_add_test(SRC svd.cpp) diff --git a/tests/math/combinations.cpp b/tests/math/combinations.cpp index f6092d5cbe..dabd723a74 100644 --- a/tests/math/combinations.cpp +++ b/tests/math/combinations.cpp @@ -2,6 +2,7 @@ // Copyright (c) 2024 Team Dissolve and contributors #include "math/combinations.h" +#include "templates/algorithms.h" #include #include @@ -33,8 +34,8 @@ TEST(CombinationTest, CheckNumberOfCombinations) } std::vector> combinations; - Combinations comb(numTests, 2); - for (int i = 0; i < comb.getNumCombinations(); i++) + Combinations comb(numTests); + for (auto i = 0; i < comb.getNumCombinations(); ++i) { auto [m, n] = comb.nthCombination(i); @@ -59,20 +60,20 @@ TEST(CombinationTest, CheckCombinations) } std::vector> combinations; - Combinations comb(numTests, 2); - for (int i = 0; i < comb.getNumCombinations(); i++) + Combinations comb(numTests); + for (auto i = 0; i < comb.getNumCombinations(); ++i) { auto [m, n] = comb.nthCombination(i); combinations.emplace_back(m, n); } - for (auto &combination : combinations) + for (auto [computed, expected] : zip(combinations, expectedCombinations)) { - EXPECT_TRUE(combinationInVector(combination, expectedCombinations)); + EXPECT_EQ(computed, expected); } auto it = std::unique(combinations.begin(), combinations.end()); EXPECT_TRUE(it == std::end(combinations)); } -} // namespace UnitTest \ No newline at end of file +} // namespace UnitTest diff --git a/tests/math/polynomial.cpp b/tests/math/polynomial.cpp new file mode 100644 index 0000000000..e767280f07 --- /dev/null +++ b/tests/math/polynomial.cpp @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team Dissolve and contributors + +#include "math/polynomial.h" +#include "templates/algorithms.h" + +#include +#include + +namespace UnitTest +{ + +TEST(QuadraticTest, SimpleTest) +{ + + std::random_device rd; // Will be used to obtain a seed for the random number engine + std::mt19937 gen(rd()); // Standard mersenne_twister_engine seeded with rd() + std::uniform_real_distribution<> dis(-100.0, 100.0); + for (auto i = 0; i < 100; ++i) + { + auto r1 = dis(gen), r2 = dis(gen); + auto a = 1; + auto b = -r1 - r2; + auto c = r1 * r2; + + if (r2 < r1) + std::swap(r1, r2); + + Quadratic poly(a, b, c); + ASSERT_TRUE(poly.hasRoots()); + auto roots = poly.roots(); + EXPECT_NEAR(r1, roots.first, 1e-9); + EXPECT_NEAR(r2, roots.second, 1e-9); + } +} +} // namespace UnitTest From fceb24ad93470aa4476307c3f7b34b12752fc269 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Wed, 28 Feb 2024 08:52:20 +0000 Subject: [PATCH 03/13] 1576 More CIF Unit Tests (#1804) Co-authored-by: Adam Washington Co-authored-by: Jared Swift --- src/classes/CMakeLists.txt | 2 + src/classes/localMolecule.cpp | 62 +++ src/classes/localMolecule.h | 44 ++ src/io/import/cif.cpp | 408 +++++++++++++----- src/io/import/cif.h | 21 +- src/io/import/cifClasses.cpp | 74 +--- src/io/import/cifClasses.h | 46 +- src/neta/neta.cpp | 5 +- src/neta/neta.h | 8 +- .../data/cif/molecule-test-simple-ordered.cif | 62 +++ .../data/cif/molecule-test-simple-ordered.xyz | 38 ++ ...molecule-test-simple-unordered-rotated.cif | 62 +++ ...molecule-test-simple-unordered-rotated.xyz | 38 ++ .../cif/molecule-test-simple-unordered.cif | 62 +++ .../cif/molecule-test-simple-unordered.xyz | 38 ++ tests/io/cif.cpp | 65 +++ 16 files changed, 802 insertions(+), 233 deletions(-) create mode 100644 src/classes/localMolecule.cpp create mode 100644 src/classes/localMolecule.h create mode 100644 tests/data/cif/molecule-test-simple-ordered.cif create mode 100644 tests/data/cif/molecule-test-simple-ordered.xyz create mode 100644 tests/data/cif/molecule-test-simple-unordered-rotated.cif create mode 100644 tests/data/cif/molecule-test-simple-unordered-rotated.xyz create mode 100644 tests/data/cif/molecule-test-simple-unordered.cif create mode 100644 tests/data/cif/molecule-test-simple-unordered.xyz diff --git a/src/classes/CMakeLists.txt b/src/classes/CMakeLists.txt index bcb3205fb9..0fd44c8741 100644 --- a/src/classes/CMakeLists.txt +++ b/src/classes/CMakeLists.txt @@ -37,6 +37,7 @@ add_library( isotopologueSet.cpp isotopologueWeight.cpp kVector.cpp + localMolecule.cpp molecule.cpp moleculeDistributor.cpp neutronWeights.cpp @@ -92,6 +93,7 @@ add_library( isotopologueSet.h isotopologueWeight.h kVector.h + localMolecule.h molecule.h moleculeDistributor.h neutronWeights.h diff --git a/src/classes/localMolecule.cpp b/src/classes/localMolecule.cpp new file mode 100644 index 0000000000..f3dde94a17 --- /dev/null +++ b/src/classes/localMolecule.cpp @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team Dissolve and contributors + +#include "classes/localMolecule.h" +#include "classes/empiricalFormula.h" +#include "classes/species.h" + +LocalMolecule::LocalMolecule(const LocalMolecule ©From) { copyData(copyFrom); } + +LocalMolecule::LocalMolecule(LocalMolecule &&moveFrom) { copyData(moveFrom); } + +LocalMolecule &LocalMolecule::operator=(const LocalMolecule ©From) +{ + copyData(copyFrom); + return *this; +} + +LocalMolecule &LocalMolecule::operator=(LocalMolecule &&moveFrom) +{ + copyData(moveFrom); + return *this; +} + +// Copy data from specified object +void LocalMolecule::copyData(const LocalMolecule &object) +{ + species_ = object.species_; + + localAtoms_ = object.localAtoms_; + atoms_.resize(localAtoms_.size()); + std::transform(localAtoms_.begin(), localAtoms_.end(), atoms_.begin(), [](auto &atom) { return &atom; }); +} + +// Set Species that this Molecule represents +void LocalMolecule::setSpecies(const Species *sp) +{ + species_ = sp; + + localAtoms_.resize(sp->nAtoms()); + atoms_.resize(sp->nAtoms()); + std::transform(localAtoms_.begin(), localAtoms_.end(), atoms_.begin(), [](auto &atom) { return &atom; }); + + for (auto &&[atom, spAtom] : zip(localAtoms_, species_->atoms())) + atom.setSpeciesAtom(&spAtom); +} + +// Add Atom to Molecule +void LocalMolecule::addAtom(Atom *atom) { throw(std::runtime_error("Can't addAtom() in a LocalMolecule.\n")); } + +// Update local atom pointers from main vector +void LocalMolecule::updateAtoms(std::vector &mainAtoms, int offset) +{ + throw(std::runtime_error("Can't updateAtoms() in a LocalMolecule.\n")); +} + +// Return nth local atom +Atom &LocalMolecule::localAtom(int n) { return localAtoms_[n]; } +const Atom &LocalMolecule::localAtom(int n) const { return localAtoms_[n]; } + +// Return local atoms +std::vector &LocalMolecule::localAtoms() { return localAtoms_; } +const std::vector &LocalMolecule::localAtoms() const { return localAtoms_; }; diff --git a/src/classes/localMolecule.h b/src/classes/localMolecule.h new file mode 100644 index 0000000000..33d547d3e3 --- /dev/null +++ b/src/classes/localMolecule.h @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team Dissolve and contributors + +#pragma once + +#include "classes/molecule.h" +#include + +// Forward Declarations +class Species; + +// Local Molecule Definition +class LocalMolecule : public Molecule +{ + public: + LocalMolecule() = default; + ~LocalMolecule() = default; + LocalMolecule(const LocalMolecule ©From); + LocalMolecule(LocalMolecule &&moveFrom); + LocalMolecule &operator=(const LocalMolecule ©From); + LocalMolecule &operator=(LocalMolecule &&moveFrom); + + private: + // Local vector of Atoms that belong to this Molecule and their original unit cell indices + std::vector localAtoms_; + + private: + // Copy data from specified object + void copyData(const LocalMolecule &object); + // Add Atom to Molecule + void addAtom(Atom *i) override; + // Update local atom pointers from main vector + void updateAtoms(std::vector &mainAtoms, int offset) override; + + public: + // Set Species that this Molecule represents + void setSpecies(const Species *sp) override; + // Return nth local atom + Atom &localAtom(int n); + const Atom &localAtom(int n) const; + // Return local atoms + std::vector &localAtoms(); + const std::vector &localAtoms() const; +}; diff --git a/src/io/import/cif.cpp b/src/io/import/cif.cpp index 0d5d095578..33d3dd56ce 100644 --- a/src/io/import/cif.cpp +++ b/src/io/import/cif.cpp @@ -482,7 +482,7 @@ bool CIFHandler::createBasicUnitCell() // Bonding if (useCIFBondingDefinitions_) - applyCIFBonding(unitCellSpecies_, preventMetallicBonds_); + applyCIFBonding(&unitCellSpecies_, preventMetallicBonds_); else unitCellSpecies_.addMissingBonds(bondingTolerance_, preventMetallicBonds_); @@ -591,79 +591,76 @@ bool CIFHandler::detectMolecules() return false; } - std::vector allAtomIndices(cleanedUnitCellSpecies_.nAtoms()); - std::iota(allAtomIndices.begin(), allAtomIndices.end(), 0); + std::vector atomMask(cleanedUnitCellSpecies_.nAtoms(), false); // Find all molecular species, and their instances - auto idx = 0; - while (!allAtomIndices.empty()) + auto indexIterator = atomMask.begin(); + while (indexIterator != atomMask.end()) { - // Select a fragment - auto fragmentIndices = cleanedUnitCellSpecies_.fragment(idx); - std::sort(fragmentIndices.begin(), fragmentIndices.end()); + // Select a fragment from the next available index + auto atomIndex = indexIterator - atomMask.begin(); + auto fragmentIndices = cleanedUnitCellSpecies_.fragment(atomIndex); - // Create a new CIF molecular species + // Create a new CIF molecular species from the fragment auto &cifSp = molecularSpecies_.emplace_back(); auto *sp = cifSp.species().get(); - sp->copyBasic(&cleanedUnitCellSpecies_); + // -- Copy selected atoms + for (auto fragAtomIndex : fragmentIndices) + { + const auto &unitCellAtom = cleanedUnitCellSpecies_.atom(fragAtomIndex); + sp->addAtom(unitCellAtom.Z(), unitCellAtom.r(), 0.0, unitCellAtom.atomType()); + } - // Empty the species of all atoms, except those in the reference instance - std::vector speciesAtomIndices(sp->nAtoms()); - std::iota(speciesAtomIndices.begin(), speciesAtomIndices.end(), 0); - std::vector indicesToRemove; - std::set_difference(speciesAtomIndices.begin(), speciesAtomIndices.end(), fragmentIndices.begin(), - fragmentIndices.end(), std::back_inserter(indicesToRemove)); - sp->removeAtoms(indicesToRemove); + // Give the species a temporary unit cell so we can calculate / apply bonding + sp->createBox(cleanedUnitCellSpecies_.box()->axisLengths(), cleanedUnitCellSpecies_.box()->axisAngles()); + if (useCIFBondingDefinitions_) + applyCIFBonding(sp, preventMetallicBonds_); + else + sp->addMissingBonds(bondingTolerance_, preventMetallicBonds_); + sp->removeBox(); + + // Set up a temporary molecule to unfold the species + LocalMolecule tempMol; + tempMol.setSpecies(sp); + for (auto i = 0; i < sp->nAtoms(); ++i) + tempMol.localAtom(i).setCoordinates(sp->atom(i).r()); + tempMol.unFold(cleanedUnitCellSpecies_.box()); + for (auto &&[molAtom, spAtom] : zip(tempMol.localAtoms(), sp->atoms())) + spAtom.setCoordinates(molAtom.r()); // Give the species a name sp->setName(EmpiricalFormula::formula(sp->atoms(), [&](const auto &at) { return at.Z(); })); - // Fix geometry of the extracted species - fixGeometry(sp, cleanedUnitCellSpecies_.box()); - // Find instances of this fragment. For large fragments that represent > 50% of the remaining atoms we don't even // attempt to create a NETA definition etc. For cases such as framework species this will speed up detection no end. - std::vector instances; - if (fragmentIndices.size() * 2 > allAtomIndices.size()) + std::vector instances; + if (fragmentIndices.size() * 2 > cleanedUnitCellSpecies_.nAtoms()) { // Create an instance of the current fragment auto &mol = instances.emplace_back(); mol.setSpecies(sp); for (auto i = 0; i < sp->nAtoms(); ++i) { - mol.setAtom(i, sp->atom(i).r(), sp->atom(i).index()); + mol.localAtom(i).setCoordinates(sp->atom(i).r()); + atomMask[fragmentIndices[i]] = true; } - - allAtomIndices.erase(std::remove_if(allAtomIndices.begin(), allAtomIndices.end(), - [&](int value) { - return std::find(fragmentIndices.begin(), fragmentIndices.end(), value) != - fragmentIndices.end(); - }), - allAtomIndices.end()); } else { - // Determine a unique NETA definition describing the fragment - auto neta = uniqueNETADefinition(sp); - if (!neta.has_value()) - return Messenger::error("Couldn't generate molecular partitioning for CIF - no unique NETA definition for the " - "fragment {} could be determined.\n", - sp->name()); - - // Find instances of this fragmentIndices - instances = getSpeciesInstances(sp, *neta); - - // Remove the current fragment and all copies - for (const auto &instance : instances) + // Determine the best NETA definition describing the fragment + auto &&[bestNETA, rootAtoms] = bestNETADefinition(sp); + if (rootAtoms.empty()) + return Messenger::error( + "Couldn't generate molecular partitioning for CIF - no suitable NETA definition for the " + "fragment {} could be determined.\n", + sp->name()); + + // Find instances of this fragment + instances = getSpeciesInstances(sp, atomMask, bestNETA, rootAtoms); + if (instances.empty()) { - allAtomIndices.erase(std::remove_if(allAtomIndices.begin(), allAtomIndices.end(), - [&](int value) - { - return std::find(instance.unitCellIndices().begin(), - instance.unitCellIndices().end(), - value) != instance.unitCellIndices().end(); - }), - allAtomIndices.end()); + molecularSpecies_.clear(); + return Messenger::error("Failed to find species instances for fragment '{}'.\n", sp->name()); } } @@ -671,7 +668,7 @@ bool CIFHandler::detectMolecules() cifSp.instances() = instances; // Search for the next valid starting index - idx = *std::min_element(allAtomIndices.begin(), allAtomIndices.end()); + indexIterator = std::find(std::next(indexIterator), atomMask.end(), false); } Messenger::print("Partitioned unit cell into {} distinct molecular species:\n\n", molecularSpecies_.size()); @@ -715,7 +712,7 @@ bool CIFHandler::createSupercell() supercellSpecies_.addAtom(i.Z(), i.r() + deltaR, 0.0, i.atomType()); } if (useCIFBondingDefinitions_) - applyCIFBonding(supercellSpecies_, preventMetallicBonds_); + applyCIFBonding(&supercellSpecies_, preventMetallicBonds_); else supercellSpecies_.addMissingBonds(bondingTolerance_, preventMetallicBonds_); @@ -733,7 +730,7 @@ bool CIFHandler::createSupercell() { const auto *sp = molecularSpecies.species().get(); const auto &coreInstances = molecularSpecies.instances(); - std::vector supercellInstances; + std::vector supercellInstances; supercellInstances.reserve(supercellRepeat_.x * supercellRepeat_.y * supercellRepeat_.z * coreInstances.size()); // Loop over cell images @@ -898,6 +895,9 @@ bool CIFHandler::isValid() const return !molecularSpecies_.empty() || supercellSpecies_.fragment(0).size() != supercellSpecies_.nAtoms(); } +// Return cleaned unit cell species +const Species &CIFHandler::cleanedUnitCellSpecies() const { return cleanedUnitCellSpecies_; } + // Return the detected molecular species const std::vector &CIFHandler::molecularSpecies() const { return molecularSpecies_; } @@ -1010,13 +1010,13 @@ void CIFHandler::finalise(CoreData &coreData, const Flags &flags) c */ // Apply CIF bonding to a given species -void CIFHandler::applyCIFBonding(Species &sp, bool preventMetallicBonding) +void CIFHandler::applyCIFBonding(Species *sp, bool preventMetallicBonding) { if (!hasBondDistances()) return; - auto *box = sp.box(); - auto pairs = PairIterator(sp.nAtoms()); + auto *box = sp->box(); + auto pairs = PairIterator(sp->nAtoms()); for (auto pair : pairs) { // Grab indices and atom references @@ -1024,8 +1024,8 @@ void CIFHandler::applyCIFBonding(Species &sp, bool preventMetallicBonding) if (indexI == indexJ) continue; - auto &i = sp.atom(indexI); - auto &j = sp.atom(indexJ); + auto &i = sp->atom(indexI); + auto &j = sp->atom(indexJ); // Prevent metallic bonding? if (preventMetallicBonding && Elements::isMetallic(i.Z()) && Elements::isMetallic(j.Z())) @@ -1036,90 +1036,282 @@ void CIFHandler::applyCIFBonding(Species &sp, bool preventMetallicBonding) if (!r) continue; else if (fabs(box->minimumDistance(i.r(), j.r()) - r.value()) < 1.0e-2) - sp.addBond(&i, &j); + sp->addBond(&i, &j); } } -// Determine a unique NETA definition corresponding to a given species -std::optional CIFHandler::uniqueNETADefinition(Species *sp) +// Determine the best NETA definition for the supplied species +std::tuple> CIFHandler::bestNETADefinition(Species *sp) { - NETADefinition neta; - auto nMatches = 0, idx = 0; - while (nMatches != 1) + // Set up the return value and bind its contents + std::tuple> result{NETADefinition(), {}}; + auto &&[bestNETA, rootAtoms] = result; + + // Maintain a set of atoms matched by any NETA description we generate + std::set alreadyMatched; + + // Loop over species atoms + for (auto &i : sp->atoms()) { - if (idx >= sp->nAtoms()) - return std::nullopt; - neta.create(&sp->atom(idx++), std::nullopt, + // Skip this atom? + if (alreadyMatched.find(&i) != alreadyMatched.end()) + continue; + + // Create a NETA definition with this atom as the root + NETADefinition neta; + neta.create(&i, std::nullopt, Flags(NETADefinition::NETACreationFlags::ExplicitHydrogens, NETADefinition::NETACreationFlags::IncludeRootElement)); - nMatches = std::count_if(sp->atoms().begin(), sp->atoms().end(), [&](const auto &i) { return neta.matches(&i); }); + + // Apply this match over the whole species + std::vector currentRootAtoms; + for (auto &j : sp->atoms()) + { + if (neta.matches(&j)) + { + currentRootAtoms.push_back(&j); + alreadyMatched.insert(&j); + } + } + + // Is this a better description? + auto better = false; + if (rootAtoms.empty() || currentRootAtoms.size() < rootAtoms.size()) + better = true; + else if (currentRootAtoms.size() == rootAtoms.size()) + { + // Replace the current match if there are more bonds on the current atom. + if (i.nBonds() > rootAtoms.front()->nBonds()) + better = true; + } + + if (better) + { + bestNETA = neta; + rootAtoms = currentRootAtoms; + } } - return neta; + + return result; } // Get instances for the supplied species from the cleaned unit cell -std::vector CIFHandler::getSpeciesInstances(Species *moleculeSpecies, const NETADefinition &neta) +std::vector CIFHandler::getSpeciesInstances(const Species *referenceSpecies, std::vector &atomMask, + const NETADefinition &neta, + const std::vector &referenceRootAtoms) { - std::vector instances; + if (referenceRootAtoms.empty() || !neta.isValid()) + return {}; // Loop over atoms in the unit cell - we'll mark any that we select as an instance so we speed things up and avoid // duplicates - std::vector atomFlags(cleanedUnitCellSpecies_.nAtoms()); - std::fill(atomFlags.begin(), atomFlags.end(), false); const auto &unitCellAtoms = cleanedUnitCellSpecies_.atoms(); - for (auto i = 0; i < atomFlags.size(); ++i) + std::vector instances; + auto atomIndexIterator = std::find(atomMask.begin(), atomMask.end(), false); + while (atomIndexIterator != atomMask.end()) { - if (atomFlags[i]) + // Try to match this atom / fragment + const auto atomIndex = atomIndexIterator - atomMask.begin(); + auto &atom = unitCellAtoms[atomIndex]; + auto matchedUnitCellAtoms = neta.matchedPath(&atom).set(); + if (matchedUnitCellAtoms.empty()) + { + atomIndexIterator = std::find(std::next(atomIndexIterator), atomMask.end(), false); continue; + } - auto &atom = unitCellAtoms[i]; - auto matchedAtoms = neta.matchedPath(&atom).set(); - if (matchedAtoms.empty()) - continue; + // Found a fragment that matches the NETA description - we now create a temporary instance Species which will contain + // the selected fragment atoms, reassembled into a molecule (i.e. unfolded) and with bonding applied / calculated. + // We need to copy the unit cell from the crystal so we detect bonds properly. + Species instanceSpecies; + instanceSpecies.createBox(unitCellSpecies_.box()->axisLengths(), unitCellSpecies_.box()->axisAngles()); + auto rootAtomLocalIndex = -1; + // -- Create species atoms from those matched in the unit cell by the NETA description. + for (auto &matchedAtom : matchedUnitCellAtoms) + { + auto idx = instanceSpecies.addAtom(matchedAtom->Z(), matchedAtom->r(), 0.0, matchedAtom->atomType()); - // Get the (sorted) matched atom indices - std::vector indices(matchedAtoms.size()); - std::transform(matchedAtoms.begin(), matchedAtoms.end(), indices.begin(), - [](const auto matchedAtom) { return matchedAtom->index(); }); + // Store the index of the root atom in match in our instance species when we find it + if (matchedAtom == &atom) + rootAtomLocalIndex = idx; + } + // -- Store the local root atom so we can access its coordinates for the origin translation + auto &instanceSpeciesRootAtom = instanceSpecies.atom(rootAtomLocalIndex); + // -- Calculate / apply bonding + if (useCIFBondingDefinitions_) + applyCIFBonding(&instanceSpecies, preventMetallicBonds_); + else + instanceSpecies.addMissingBonds(bondingTolerance_, preventMetallicBonds_); + + // Create a LocalMolecule as a working area for folding, translation, and rotation of the instance coordinates. + LocalMolecule instanceMolecule; + instanceMolecule.setSpecies(&instanceSpecies); + // -- Copy the coordinates off the matched unit cell atoms to our molecule and flag them as complete + auto count = 0; + for (auto &&[matchedAtom, instanceMolAtom] : zip(matchedUnitCellAtoms, instanceMolecule.localAtoms())) + { + instanceMolAtom.setCoordinates(matchedAtom->r()); + atomMask[matchedAtom->index()] = true; + } + auto &instanceMoleculeRootAtom = instanceMolecule.localAtoms()[rootAtomLocalIndex]; + + // Unfold the molecule and store the unfolded molecule coordinates back into the instance Species. + // This represents our full instance coordinates we will be storing (but not their final order) + instanceMolecule.unFold(unitCellSpecies_.box()); + for (auto &&[molAtom, spAtom] : zip(instanceMolecule.localAtoms(), instanceSpecies.atoms())) + spAtom.setCoordinates(molAtom.r()); + + /* + * Now, we have a root match atom on the current instance and a vector of possible matching sites on the reference + * species (in referenceRootAtoms). For each of the referenceRootAtoms, try to incrementally select along bonds using + * basic NETA connectivity. + */ + + // Generate basic NETA descriptions for each atom in the reference and candidate species + std::map referenceAtomNETA; + for (auto &spAtom : referenceSpecies->atoms()) + referenceAtomNETA[&spAtom] = NETADefinition(&spAtom, 1, {NETADefinition::NETACreationFlags::IncludeRootElement}); + + std::map matchMap; + for (const auto *referenceRootAtom : referenceRootAtoms) + { + // The root atom is the starting point + matchMap = matchAtom(referenceRootAtom, &instanceSpeciesRootAtom, referenceAtomNETA, {}); + if (!matchMap.empty()) + break; + } - // For each match create a CIFLocalMolecule instance - auto &mol = instances.emplace_back(); - mol.setSpecies(moleculeSpecies); - for (auto idx = 0; idx < indices.size(); ++idx) + // Result? + if (matchMap.empty()) + { + Messenger::error("Failed to match connectivity of an instance to the reference molecule.\n"); + return {}; + } + else if (matchMap.size() != referenceSpecies->nAtoms()) { - mol.setAtom(idx, unitCellAtoms[indices[idx]].r(), indices[idx]); + Messenger::error( + "Internal error - failed to match connectivity of all atoms within an instance to the reference molecule.\n"); + return {}; + } - atomFlags[indices[idx]] = true; + // Create the final instance + auto &instance = instances.emplace_back(); + instance.setSpecies(referenceSpecies); + for (const auto &[refSpeciesAtom, instanceSpeciesAtom] : matchMap) + { + instance.localAtom(refSpeciesAtom->index()).setCoordinates(instanceSpeciesAtom->r()); } - // Unfold the molecule - mol.unFold(cleanedUnitCellSpecies_.box()); + // Find the next available atom + atomIndexIterator = std::find(std::next(atomIndexIterator), atomMask.end(), false); } return instances; } -// 'Fix' the geometry of a given species -void CIFHandler::fixGeometry(Species *sp, const Box *box) +// Recursively check NETA description matches between the supplied atoms +std::map +CIFHandler::matchAtom(const SpeciesAtom *referenceAtom, const SpeciesAtom *instanceAtom, + const std::map &refNETA, + const std::map &map) { - // 'Fix' the geometry of the species - // Construct a temporary molecule, which is just the species. - std::shared_ptr mol = std::make_shared(); - std::vector molAtoms(sp->nAtoms()); - for (auto &&[spAtom, atom] : zip(sp->atoms(), molAtoms)) + // If the reference atom NETA doesn't match the instance atom we cannot proceed + if (!refNETA.at(referenceAtom).matches(instanceAtom)) + return {}; + + // Check the map to see if we have already associated the reference atom to an instance atom, or if the instance atom + // is already associated to a different reference atom. + for (auto &&[mappedRefAtom, mappedInstanceAtom] : map) { - atom.setSpeciesAtom(&spAtom); - atom.setCoordinates(spAtom.r()); - mol->addAtom(&atom); + // Found it - double-check to ensure that the current association matches our instance atom. If it does we can return + // the map as it currently stands. If not we return an empty map to indicate failure. + if (mappedRefAtom == referenceAtom) + { + if (mappedInstanceAtom == instanceAtom) + { + return map; + } + else + { + return {}; + } + } + else if (mappedInstanceAtom == instanceAtom) + { + return {}; + } + } + + // Copy the current map, associate our initial pair of atoms and try to extend it + auto newMap = map; + newMap[referenceAtom] = instanceAtom; + + // Cycle over bonds on the reference atom and find + for (const auto &referenceBond : referenceAtom->bonds()) + { + // Get the reference bond partner + auto *referenceBondPartner = referenceBond.get().partner(referenceAtom); + + // Try to find a match over bonds on the instance atom + std::map bondResult; + for (const auto &instanceBond : instanceAtom->bonds()) + { + // Get the instance bond partner + auto *instanceBondPartner = instanceBond.get().partner(instanceAtom); + + // Recurse + bondResult = matchAtom(referenceBondPartner, instanceBondPartner, refNETA, newMap); + if (!bondResult.empty()) + break; + } + + // If we found a suitable match recursing into the bond, store the result into newMap and continue to the next bond. + // If we didn't find a good match, we return now. + if (bondResult.empty()) + { + return {}; + } + else + { + newMap = bondResult; + } } - // Unfold the molecule - mol->unFold(box); + // If we get to here then we succeeded, so return the new map + return newMap; +} - // Update the coordinates of the species atoms - for (auto &&[spAtom, atom] : zip(sp->atoms(), molAtoms)) - spAtom.setCoordinates(atom.r()); +// Calculate difference metric between the supplied species and local molecule +std::pair> CIFHandler::differenceMetric(const Species *species, const LocalMolecule &molecule) +{ + auto difference = 0.0; + std::vector atomIndexMap(species->nAtoms(), -1); + auto nBadAtoms = 0; + for (auto spI = 0; spI < species->nAtoms(); ++spI) + { + auto &spAtom = species->atom(spI); + + // For this species atom find the closest atom in the molecule + auto distanceSq = 1.0e6; + for (auto molI = 0; molI < molecule.nAtoms(); ++molI) + { + auto rABSq = (spAtom.r() - molecule.localAtoms()[molI].r()).magnitudeSq(); + if (rABSq < distanceSq) + { + distanceSq = rABSq; + atomIndexMap[spI] = molI; + } + } + + if (distanceSq > 0.1) + ++nBadAtoms; + + // Update the difference score + const auto &closestMolSpAtom = molecule.species()->atom(atomIndexMap[spI]); + difference += distanceSq; + if (spAtom.Z() != closestMolSpAtom.Z()) + difference += std::max(spAtom.Z(), closestMolSpAtom.Z()) * 10.0; + } - // Set the centre of geometry of the species to be at the origin. - sp->setCentre(box, {0., 0., 0.}); + return {difference, atomIndexMap}; } diff --git a/src/io/import/cif.h b/src/io/import/cif.h index ba11d715e2..7f0732ab83 100644 --- a/src/io/import/cif.h +++ b/src/io/import/cif.h @@ -177,6 +177,8 @@ class CIFHandler bool generate(CIFGenerationStage fromStage = CIFGenerationStage::CreateBasicUnitCell); // Return whether the generated data is valid bool isValid() const; + // Return cleaned unit cell species + const Species &cleanedUnitCellSpecies() const; // Return the detected molecular species const std::vector &molecularSpecies() const; // Return the generated configuration @@ -189,11 +191,18 @@ class CIFHandler */ private: // Apply CIF bonding to a given species - void applyCIFBonding(Species &sp, bool preventMetallicBonding); - // Determine a unique NETA definition corresponding to a given species - std::optional uniqueNETADefinition(Species *sp); + void applyCIFBonding(Species *sp, bool preventMetallicBonding); + // Determine the best NETA definition for the supplied species + std::tuple> bestNETADefinition(Species *sp); // Get instances of species molecules from the supplied NETA definition - std::vector getSpeciesInstances(Species *moleculeSpecies, const NETADefinition &neta); - // 'Fix' the geometry of a given species - void fixGeometry(Species *sp, const Box *box); + std::vector getSpeciesInstances(const Species *referenceSpecies, std::vector &atomMask, + const NETADefinition &neta, + const std::vector &referenceRootAtoms); + // Calculate difference metric between the supplied species and local molecule + static std::pair> differenceMetric(const Species *species, const LocalMolecule &molecule); + // Recursively check NETA description matches between the supplied atoms + std::map matchAtom(const SpeciesAtom *referenceAtom, + const SpeciesAtom *instanceAtom, + const std::map &refNETA, + const std::map &map); }; diff --git a/src/io/import/cifClasses.cpp b/src/io/import/cifClasses.cpp index 7048ef65a9..ffbec5b916 100644 --- a/src/io/import/cifClasses.cpp +++ b/src/io/import/cifClasses.cpp @@ -89,74 +89,6 @@ CIFAtomGroup &CIFAssembly::getGroup(std::string_view groupName) // Return the number of defined groups int CIFAssembly::nGroups() const { return groups_.size(); } -/* - * CIF Local Molecule - */ - -CIFLocalMolecule::CIFLocalMolecule(const CIFLocalMolecule ©From) { copyData(copyFrom); } - -CIFLocalMolecule::CIFLocalMolecule(CIFLocalMolecule &&moveFrom) { copyData(moveFrom); } - -CIFLocalMolecule &CIFLocalMolecule::operator=(const CIFLocalMolecule ©From) -{ - copyData(copyFrom); - return *this; -} - -CIFLocalMolecule &CIFLocalMolecule::operator=(CIFLocalMolecule &&moveFrom) -{ - copyData(moveFrom); - return *this; -} - -// Copy data from specified object -void CIFLocalMolecule::copyData(const CIFLocalMolecule &object) -{ - species_ = object.species_; - - localAtoms_ = object.localAtoms_; - unitCellIndices_ = object.unitCellIndices_; - atoms_.resize(localAtoms_.size()); - std::transform(localAtoms_.begin(), localAtoms_.end(), atoms_.begin(), [](auto &atom) { return &atom; }); -} - -// Set Species that this Molecule represents -void CIFLocalMolecule::setSpecies(const Species *sp) -{ - species_ = sp; - - localAtoms_.resize(sp->nAtoms()); - atoms_.resize(sp->nAtoms()); - unitCellIndices_.resize(sp->nAtoms()); - std::transform(localAtoms_.begin(), localAtoms_.end(), atoms_.begin(), [](auto &atom) { return &atom; }); - - for (auto &&[atom, spAtom] : zip(localAtoms_, species_->atoms())) - atom.setSpeciesAtom(&spAtom); -} - -// Add Atom to Molecule -void CIFLocalMolecule::addAtom(Atom *atom) { throw(std::runtime_error("Can't addAtom() in a LocalMolecule.\n")); } - -// Update local atom pointers from main vector -void CIFLocalMolecule::updateAtoms(std::vector &mainAtoms, int offset) -{ - throw(std::runtime_error("Can't updateAtoms() in a LocalMolecule.\n")); -} - -// Set coordinates and local unit cell index of the specified atom -void CIFLocalMolecule::setAtom(int index, const Vec3 &r, int unitCellIndex) -{ - localAtoms_[index].setCoordinates(r); - unitCellIndices_[index] = unitCellIndex; -} - -// Return local atoms -std::vector &CIFLocalMolecule::localAtoms() { return localAtoms_; } -const std::vector &CIFLocalMolecule::localAtoms() const { return localAtoms_; }; - -// Return local unit cell indices for the atoms -const std::vector &CIFLocalMolecule::unitCellIndices() const { return unitCellIndices_; } - /* * CIF Molecular Species */ @@ -168,11 +100,11 @@ std::shared_ptr &CIFMolecularSpecies::species() { return species_; }; const std::shared_ptr &CIFMolecularSpecies::species() const { return species_; }; // Return molecule instances -const std::vector &CIFMolecularSpecies::instances() const { return instances_; } -std::vector &CIFMolecularSpecies::instances() { return instances_; } +const std::vector &CIFMolecularSpecies::instances() const { return instances_; } +std::vector &CIFMolecularSpecies::instances() { return instances_; } // Append supplied instances to our vector -void CIFMolecularSpecies::appendInstances(const std::vector &newInstances) +void CIFMolecularSpecies::appendInstances(const std::vector &newInstances) { // Increase our reservation instances_.reserve(instances_.size() + newInstances.size()); diff --git a/src/io/import/cifClasses.h b/src/io/import/cifClasses.h index d97b6a5db8..d0c596ed9c 100644 --- a/src/io/import/cifClasses.h +++ b/src/io/import/cifClasses.h @@ -3,7 +3,7 @@ #pragma once -#include "classes/molecule.h" +#include "classes/localMolecule.h" #include "classes/species.h" #include "data/elements.h" #include "templates/vector3.h" @@ -116,42 +116,6 @@ class CIFAssembly int nGroups() const; }; -// CIF Local Molecule Definition -class CIFLocalMolecule : public Molecule -{ - public: - CIFLocalMolecule() = default; - ~CIFLocalMolecule() = default; - CIFLocalMolecule(const CIFLocalMolecule ©From); - CIFLocalMolecule(CIFLocalMolecule &&moveFrom); - CIFLocalMolecule &operator=(const CIFLocalMolecule ©From); - CIFLocalMolecule &operator=(CIFLocalMolecule &&moveFrom); - - private: - // Local vector of Atoms that belong to this Molecule and their original unit cell indices - std::vector localAtoms_; - std::vector unitCellIndices_; - - private: - // Copy data from specified object - void copyData(const CIFLocalMolecule &object); - // Add Atom to Molecule - void addAtom(Atom *i) override; - // Update local atom pointers from main vector - void updateAtoms(std::vector &mainAtoms, int offset) override; - - public: - // Set Species that this Molecule represents - void setSpecies(const Species *sp) override; - // Set coordinates and local unit cell index of the specified atom - void setAtom(int index, const Vec3 &r, int unitCellIndex); - // Return local atoms - std::vector &localAtoms(); - const std::vector &localAtoms() const; - // Return local unit cell indices for the atoms - const std::vector &unitCellIndices() const; -}; - // CIF Repeated Molecular Species class CIFMolecularSpecies { @@ -162,17 +126,17 @@ class CIFMolecularSpecies // Species parent for molecule instances std::shared_ptr species_; // Molecule instances - std::vector instances_; + std::vector instances_; public: // Return species parent for molecule instances std::shared_ptr &species(); const std::shared_ptr &species() const; // Return molecule instances - const std::vector &instances() const; - std::vector &instances(); + const std::vector &instances() const; + std::vector &instances(); // Append supplied instances to our vector - void appendInstances(const std::vector &newInstances); + void appendInstances(const std::vector &newInstances); // Return coordinates for all instances as a vector of vectors std::vector>> allInstanceCoordinates() const; }; diff --git a/src/neta/neta.cpp b/src/neta/neta.cpp index efc0e293b4..431058d87c 100644 --- a/src/neta/neta.cpp +++ b/src/neta/neta.cpp @@ -21,9 +21,10 @@ NETADefinition::NETADefinition(std::string_view definition) : rootNode_(nullptr) create(definition); } -NETADefinition::NETADefinition(const SpeciesAtom *i, const std::optional maxDepth) : rootNode_(nullptr), valid_(false) +NETADefinition::NETADefinition(const SpeciesAtom *i, const std::optional maxDepth, const Flags &flags) + : rootNode_(nullptr), valid_(false) { - create(i, maxDepth); + create(i, maxDepth, flags); } /* diff --git a/src/neta/neta.h b/src/neta/neta.h index a33f649e83..252f2927d6 100644 --- a/src/neta/neta.h +++ b/src/neta/neta.h @@ -15,11 +15,6 @@ class SpeciesAtom; // NETA Definition class NETADefinition { - public: - explicit NETADefinition(std::string_view definition = ""); - NETADefinition(const SpeciesAtom *i, const std::optional maxDepth = 1); - ~NETADefinition() = default; - public: // NETA Definition Creation Flags enum NETACreationFlags @@ -27,6 +22,9 @@ class NETADefinition ExplicitHydrogens, /* Explicitly include Hydrogen atoms in NETA definition string */ IncludeRootElement /* Include the root element in NETA definition string */ }; + explicit NETADefinition(std::string_view definition = ""); + NETADefinition(const SpeciesAtom *i, const std::optional maxDepth = 1, const Flags &flags = {}); + ~NETADefinition() = default; /* * Data diff --git a/tests/data/cif/molecule-test-simple-ordered.cif b/tests/data/cif/molecule-test-simple-ordered.cif new file mode 100644 index 0000000000..b0a15b54c8 --- /dev/null +++ b/tests/data/cif/molecule-test-simple-ordered.cif @@ -0,0 +1,62 @@ +data_Dissolve001 +_journal_name_full 'J. Made Up Cryst.' +_journal_page_first 1 +_journal_page_last 1 +_journal_volume 1 +_journal_year 2024 +_chemical_formula_sum '' +_chemical_name_mineral Fakeabar +_space_group_IT_number 1 +_symmetry_space_group_name_Hall 'P 1' +_symmetry_space_group_name_H-M 'P 1' +_cell_angle_alpha 90 +_cell_angle_beta 90 +_cell_angle_gamma 90 +_cell_length_a 20.0 +_cell_length_b 20.0 +_cell_length_c 20.0 +_cell_volume 8000.0 +loop_ +_symmetry_equiv_pos_as_xyz +x,y,z +loop_ +_atom_site_label +_atom_site_fract_x +_atom_site_fract_y +_atom_site_fract_z +C 0.5 0.6 0.5 +Cl 0.5 0.677 0.5 +H 0.5 0.581974 0.449097 +H 0.455901 0.582013 0.525451 +O 0.557961 0.5763 0.533464 +H 0.557961 0.5263 0.533464 +C 0.3 0.6 0.5 +Cl 0.3 0.677 0.5 +H 0.3 0.581974 0.449097 +H 0.255901 0.582013 0.525451 +O 0.357961 0.5763 0.533464 +H 0.357961 0.5263 0.533464 +C 0.7 0.6 0.5 +Cl 0.7 0.677 0.5 +H 0.7 0.581974 0.449097 +H 0.655901 0.582013 0.525451 +O 0.757961 0.5763 0.533464 +H 0.757961 0.5263 0.533464 +C 0.5 0.3 0.5 +Cl 0.5 0.377 0.5 +H 0.5 0.281974 0.449097 +H 0.455901 0.282013 0.525451 +O 0.557961 0.2763 0.533464 +H 0.557961 0.2263 0.533464 +C 0.3 0.3 0.5 +Cl 0.3 0.377 0.5 +H 0.3 0.281974 0.449097 +H 0.255901 0.282013 0.525451 +O 0.357961 0.2763 0.533464 +H 0.357961 0.2263 0.533464 +C 0.7 0.3 0.5 +Cl 0.7 0.377 0.5 +H 0.7 0.281974 0.449097 +H 0.655901 0.282013 0.525451 +O 0.757961 0.2763 0.533464 +H 0.757961 0.2263 0.533464 diff --git a/tests/data/cif/molecule-test-simple-ordered.xyz b/tests/data/cif/molecule-test-simple-ordered.xyz new file mode 100644 index 0000000000..720b3a17b3 --- /dev/null +++ b/tests/data/cif/molecule-test-simple-ordered.xyz @@ -0,0 +1,38 @@ +36 +Unnamed001 +C 10.000000 12.000000 10.000000 0.000000 +Cl 10.000000 13.540000 10.000000 0.000000 +H 10.000000 11.639489 8.981947 0.000000 +H 9.118027 11.640256 10.509026 0.000000 +O 11.159219 11.525995 10.669276 0.000000 +H 11.159219 10.525995 10.669276 0.000000 +C 6.000000 12.000000 10.000000 0.000000 +Cl 6.000000 13.540000 10.000000 0.000000 +H 6.000000 11.639489 8.981947 0.000000 +H 5.118027 11.640256 10.509026 0.000000 +O 7.159219 11.525995 10.669276 0.000000 +H 7.159219 10.525995 10.669276 0.000000 +C 14.000000 12.000000 10.000000 0.000000 +Cl 14.000000 13.540000 10.000000 0.000000 +H 14.000000 11.639489 8.981947 0.000000 +H 13.118027 11.640256 10.509026 0.000000 +O 15.159219 11.525995 10.669276 0.000000 +H 15.159219 10.525995 10.669276 0.000000 +C 10.000000 6.000000 10.000000 0.000000 +Cl 10.000000 7.540000 10.000000 0.000000 +H 10.000000 5.639489 8.981947 0.000000 +H 9.118027 5.640256 10.509026 0.000000 +O 11.159219 5.525995 10.669276 0.000000 +H 11.159219 4.525995 10.669276 0.000000 +C 6.000000 6.000000 10.000000 0.000000 +Cl 6.000000 7.540000 10.000000 0.000000 +H 6.000000 5.639489 8.981947 0.000000 +H 5.118027 5.640256 10.509026 0.000000 +O 7.159219 5.525995 10.669276 0.000000 +H 7.159219 4.525995 10.669276 0.000000 +C 14.000000 6.000000 10.000000 0.000000 +Cl 14.000000 7.540000 10.000000 0.000000 +H 14.000000 5.639489 8.981947 0.000000 +H 13.118027 5.640256 10.509026 0.000000 +O 15.159219 5.525995 10.669276 0.000000 +H 15.159219 4.525995 10.669276 0.000000 diff --git a/tests/data/cif/molecule-test-simple-unordered-rotated.cif b/tests/data/cif/molecule-test-simple-unordered-rotated.cif new file mode 100644 index 0000000000..82fa3e3e3e --- /dev/null +++ b/tests/data/cif/molecule-test-simple-unordered-rotated.cif @@ -0,0 +1,62 @@ +data_Dissolve001 +_journal_name_full 'J. Made Up Cryst.' +_journal_page_first 1 +_journal_page_last 1 +_journal_volume 1 +_journal_year 2024 +_chemical_formula_sum '' +_chemical_name_mineral Fakeabar +_space_group_IT_number 1 +_symmetry_space_group_name_Hall 'P 1' +_symmetry_space_group_name_H-M 'P 1' +_cell_angle_alpha 90 +_cell_angle_beta 90 +_cell_angle_gamma 90 +_cell_length_a 20.0 +_cell_length_b 20.0 +_cell_length_c 20.0 +_cell_volume 8000.0 +loop_ +_symmetry_equiv_pos_as_xyz +x,y,z +loop_ +_atom_site_label +_atom_site_fract_x +_atom_site_fract_y +_atom_site_fract_z +Cl 0.334617 0.206096 0.508634 +Cl 0.727059 0.205688 0.492114 +H 0.739191 0.369421 0.507415 +H 0.687352 0.279336 0.560086 +H 0.660561 0.289077 0.476648 +H 0.519368 0.241117 0.474399 +H 0.650541 0.534254 0.509395 +C 0.726914 0.598016 0.505856 +O 0.525942 0.546035 0.535954 +H 0.744746 0.579954 0.458194 +H 0.253128 0.600377 0.505184 +H 0.754055 0.573597 0.545643 +C 0.305306 0.595191 0.492285 +Cl 0.514093 0.341667 0.435893 +C 0.505982 0.605739 0.503121 +O 0.544 0.307981 0.548101 +O 0.755106 0.323206 0.496886 +H 0.537676 0.27436 0.584565 +Cl 0.32794 0.656637 0.451775 +C 0.702551 0.276859 0.508328 +Cl 0.737893 0.674106 0.510184 +H 0.357064 0.304329 0.47033 +H 0.253171 0.349094 0.498246 +O 0.343991 0.590364 0.551626 +H 0.34203 0.302029 0.557181 +C 0.326504 0.282665 0.509223 +O 0.657671 0.583663 0.512205 +H 0.546656 0.514874 0.502789 +H 0.452332 0.28857 0.506471 +Cl 0.474082 0.653727 0.554195 +H 0.329294 0.550464 0.577931 +O 0.258439 0.299374 0.497863 +H 0.549154 0.629273 0.480799 +H 0.312165 0.550556 0.462676 +H 0.470005 0.593943 0.464619 +C 0.504353 0.289892 0.492048 diff --git a/tests/data/cif/molecule-test-simple-unordered-rotated.xyz b/tests/data/cif/molecule-test-simple-unordered-rotated.xyz new file mode 100644 index 0000000000..b7e5944b68 --- /dev/null +++ b/tests/data/cif/molecule-test-simple-unordered-rotated.xyz @@ -0,0 +1,38 @@ +36 +Unnamed001 +Cl 6.692337 4.121912 10.172678 0.000000 +Cl 14.541188 4.113754 9.842276 0.000000 +H 14.783827 7.388420 10.148307 0.000000 +H 13.747030 5.586710 11.201716 0.000000 +H 13.211221 5.781545 9.532957 0.000000 +H 10.387369 4.822332 9.487980 0.000000 +H 13.010824 10.685076 10.187907 0.000000 +C 14.538278 11.960324 10.117127 0.000000 +O 10.518845 10.920696 10.719081 0.000000 +H 14.894920 11.599075 9.163873 0.000000 +H 5.062563 12.007537 10.103684 0.000000 +H 15.081105 11.471935 10.912866 0.000000 +C 6.106126 11.903816 9.845703 0.000000 +Cl 10.281850 6.833336 8.717854 0.000000 +C 10.119636 12.114773 10.062425 0.000000 +O 10.880004 6.159612 10.962013 0.000000 +O 15.102123 6.464113 9.937729 0.000000 +H 10.753513 5.487207 11.691309 0.000000 +Cl 6.558794 13.132745 9.035502 0.000000 +C 14.051011 5.537188 10.166566 0.000000 +Cl 14.757862 13.482130 10.203672 0.000000 +H 7.141276 6.086583 9.406596 0.000000 +H 5.063411 6.981885 9.964925 0.000000 +O 6.879811 11.807289 11.032516 0.000000 +H 6.840595 6.040575 11.143612 0.000000 +C 6.530076 5.653294 10.184469 0.000000 +O 13.153411 11.673261 10.244105 0.000000 +H 10.933127 10.297475 10.055782 0.000000 +H 9.046640 5.771409 10.129425 0.000000 +Cl 9.481642 13.074533 11.083906 0.000000 +H 6.585871 11.009284 11.558620 0.000000 +O 5.168775 5.987481 9.957269 0.000000 +H 10.983073 12.585454 9.615972 0.000000 +H 6.243305 11.011128 9.253524 0.000000 +H 9.400107 11.878870 9.292384 0.000000 +C 10.087053 5.797833 9.840970 0.000000 diff --git a/tests/data/cif/molecule-test-simple-unordered.cif b/tests/data/cif/molecule-test-simple-unordered.cif new file mode 100644 index 0000000000..97996b8a14 --- /dev/null +++ b/tests/data/cif/molecule-test-simple-unordered.cif @@ -0,0 +1,62 @@ +data_Dissolve001 +_journal_name_full 'J. Made Up Cryst.' +_journal_page_first 1 +_journal_page_last 1 +_journal_volume 1 +_journal_year 2024 +_chemical_formula_sum '' +_chemical_name_mineral Fakeabar +_space_group_IT_number 1 +_symmetry_space_group_name_Hall 'P 1' +_symmetry_space_group_name_H-M 'P 1' +_cell_angle_alpha 90 +_cell_angle_beta 90 +_cell_angle_gamma 90 +_cell_length_a 20.0 +_cell_length_b 20.0 +_cell_length_c 20.0 +_cell_volume 8000.0 +loop_ +_symmetry_equiv_pos_as_xyz +x,y,z +loop_ +_atom_site_label +_atom_site_fract_x +_atom_site_fract_y +_atom_site_fract_z +C 0.5 0.6 0.5 +Cl 0.5 0.677 0.5 +H 0.5 0.581975 0.449097 +H 0.455901 0.582015 0.52545 +O 0.55796 0.5763 0.533465 +H 0.55796 0.5263 0.533465 +C 0.3 0.6 0.5 +Cl 0.3 0.677 0.5 +H 0.3 0.581975 0.449097 +H 0.255902 0.582015 0.52545 +O 0.357961 0.5763 0.533465 +H 0.357961 0.5263 0.533465 +Cl 0.7 0.677 0.5 +C 0.7 0.6 0.5 +H 0.7 0.581975 0.449097 +H 0.6559 0.582015 0.52545 +O 0.75796 0.5763 0.533465 +H 0.75796 0.5263 0.533465 +O 0.55796 0.2763 0.533465 +C 0.5 0.3 0.5 +Cl 0.5 0.377 0.5 +H 0.5 0.281975 0.449097 +H 0.455901 0.282013 0.52545 +H 0.55796 0.2263 0.533465 +C 0.3 0.3 0.5 +H 0.3 0.281975 0.449097 +Cl 0.3 0.377 0.5 +H 0.255902 0.282013 0.52545 +O 0.357961 0.2763 0.533465 +H 0.357961 0.2263 0.533465 +C 0.7 0.3 0.5 +Cl 0.7 0.377 0.5 +H 0.7 0.281975 0.449097 +H 0.75796 0.2263 0.533465 +H 0.6559 0.282013 0.52545 +O 0.75796 0.2763 0.533465 diff --git a/tests/data/cif/molecule-test-simple-unordered.xyz b/tests/data/cif/molecule-test-simple-unordered.xyz new file mode 100644 index 0000000000..5314e730d0 --- /dev/null +++ b/tests/data/cif/molecule-test-simple-unordered.xyz @@ -0,0 +1,38 @@ +36 +Unnamed001 +C 10.000000 12.000000 10.000000 0.000000 +Cl 10.000000 13.540000 10.000000 0.000000 +H 10.000000 11.639500 8.981950 0.000000 +H 9.118030 11.640300 10.509000 0.000000 +O 11.159200 11.526000 10.669300 0.000000 +H 11.159200 10.526000 10.669300 0.000000 +C 6.000000 12.000000 10.000000 0.000000 +Cl 6.000000 13.540000 10.000000 0.000000 +H 6.000000 11.639500 8.981950 0.000000 +H 5.118030 11.640300 10.509000 0.000000 +O 7.159220 11.526000 10.669300 0.000000 +H 7.159220 10.526000 10.669300 0.000000 +Cl 14.000000 13.540000 10.000000 0.000000 +C 14.000000 12.000000 10.000000 0.000000 +H 14.000000 11.639500 8.981950 0.000000 +H 13.118000 11.640300 10.509000 0.000000 +O 15.159200 11.526000 10.669300 0.000000 +H 15.159200 10.526000 10.669300 0.000000 +O 11.159200 5.525990 10.669300 0.000000 +C 10.000000 6.000000 10.000000 0.000000 +Cl 10.000000 7.540000 10.000000 0.000000 +H 10.000000 5.639490 8.981950 0.000000 +H 9.118030 5.640260 10.509000 0.000000 +H 11.159200 4.525990 10.669300 0.000000 +C 6.000000 6.000000 10.000000 0.000000 +H 6.000000 5.639490 8.981950 0.000000 +Cl 6.000000 7.540000 10.000000 0.000000 +H 5.118030 5.640260 10.509000 0.000000 +O 7.159220 5.525990 10.669300 0.000000 +H 7.159220 4.525990 10.669300 0.000000 +C 14.000000 6.000000 10.000000 0.000000 +Cl 14.000000 7.540000 10.000000 0.000000 +H 14.000000 5.639490 8.981950 0.000000 +H 15.159200 4.525990 10.669300 0.000000 +H 13.118000 5.640260 10.509000 0.000000 +O 15.159200 5.525990 10.669300 0.000000 diff --git a/tests/io/cif.cpp b/tests/io/cif.cpp index 3394faf66c..4738054de4 100644 --- a/tests/io/cif.cpp +++ b/tests/io/cif.cpp @@ -3,6 +3,7 @@ #include "io/import/cif.h" #include "classes/empiricalFormula.h" +#include "io/import/species.h" #include "tests/testData.h" #include @@ -32,6 +33,29 @@ class ImportCIFTest : public ::testing::Test EXPECT_EQ(molSp.instances().size(), std::get<1>(info)); EXPECT_EQ(molSp.species()->nAtoms(), std::get<2>(info)); } + // Check instance consistency with reference coordinates + void testInstanceConsistency(const CIFMolecularSpecies &molSp, const Species &referenceCoordinates) + { + // Get the box from the reference species + const auto *box = referenceCoordinates.box(); + + // Loop over instances and ensure their stored atoms overlap exactly with one in the reference system + for (const auto &instance : molSp.instances()) + { + for (auto &&[instanceAtom, speciesAtom] : zip(instance.localAtoms(), molSp.species()->atoms())) + { + // Locate the atom in the reference system at the instance atom coordinates + auto instanceR = instanceAtom.r(); + auto spAtomIt = std::find_if(referenceCoordinates.atoms().begin(), referenceCoordinates.atoms().end(), + [box, instanceR](const auto &refAtom) + { return box->minimumDistance(refAtom.r(), instanceR) < 0.01; }); + fmt::print("{} {} {} {}\n", Elements::symbol(speciesAtom.Z()), instanceAtom.r().x, instanceAtom.r().y, + instanceAtom.r().z); + ASSERT_NE(spAtomIt, referenceCoordinates.atoms().end()); + EXPECT_EQ(spAtomIt->Z(), speciesAtom.Z()); + } + } + } }; TEST_F(ImportCIFTest, Parse) @@ -143,4 +167,45 @@ TEST_F(ImportCIFTest, CuBTC) cifHandler.setRemoveAtomics(true); EXPECT_EQ(cifHandler.molecularSpecies().size(), 0); } + +TEST_F(ImportCIFTest, MoleculeOrdering) +{ + CIFHandler cifHandler; + const auto cifFiles = {"cif/molecule-test-simple-ordered.cif", "cif/molecule-test-simple-unordered.cif", + "cif/molecule-test-simple-unordered-rotated.cif"}; + for (auto cifFile : cifFiles) + { + // Load the CIF file + ASSERT_TRUE(cifHandler.read(cifFile)); + EXPECT_TRUE(cifHandler.generate()); + + EXPECT_EQ(cifHandler.molecularSpecies().size(), 1); + + auto &cifMolecule = cifHandler.molecularSpecies().front(); + EmpiricalFormula::EmpiricalFormulaMap moleculeFormula = { + {Elements::Cl, 1}, {Elements::O, 1}, {Elements::C, 1}, {Elements::H, 3}}; + testMolecularSpecies(cifMolecule, {EmpiricalFormula::formula(moleculeFormula), 6, 6}); + + testInstanceConsistency(cifMolecule, cifHandler.cleanedUnitCellSpecies()); + } +} + +TEST_F(ImportCIFTest, BigMoleculeOrdering) +{ + CIFHandler cifHandler; + const auto cifFile = "cif/Bisphen_n_arenes_1517789.cif"; + + // Load the CIF file + ASSERT_TRUE(cifHandler.read(cifFile)); + EXPECT_TRUE(cifHandler.generate()); + + EXPECT_EQ(cifHandler.molecularSpecies().size(), 1); + + auto &cifMolecule = cifHandler.molecularSpecies().front(); + EmpiricalFormula::EmpiricalFormulaMap moleculeFormula = {{Elements::O, 6}, {Elements::C, 51}, {Elements::H, 54}}; + testMolecularSpecies(cifMolecule, {EmpiricalFormula::formula(moleculeFormula), 4, 111}); + + testInstanceConsistency(cifMolecule, cifHandler.cleanedUnitCellSpecies()); +} + } // namespace UnitTest From c36915227e86038b84e87dea87eec5e1287a1a70 Mon Sep 17 00:00:00 2001 From: Noella Spitz <101950441+rhinoella@users.noreply.github.com> Date: Thu, 29 Feb 2024 16:55:18 +0000 Subject: [PATCH 04/13] Update Compillation Docs for new test method (#1810) --- web/docs/userguide/get/compilation.md | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/web/docs/userguide/get/compilation.md b/web/docs/userguide/get/compilation.md index be98624689..2ffad2769f 100644 --- a/web/docs/userguide/get/compilation.md +++ b/web/docs/userguide/get/compilation.md @@ -97,7 +97,7 @@ If you have downloaded the ANTLR4 Java tool and need to specify it explicitly, u An example`cmake` command using Ninja to build both the command-line and GUI codes, building ANTLR4 as an external project, and locating the ANTLR4 Java tool in the build directory, would look like this: ``` -cmake ../ -G Ninja -DGUI:bool=true -DBUILD_ANTLR_RUNTIME:bool=true -DANTLR_EXECUTABLE:path=./antlr-4.8-complete.jar +cmake ../ -G Ninja -DGUI:bool=true -DBUILD_ANTLR_RUNTIME:bool=true -DANTLR_EXECUTABLE:path=./antlr-4.13.1-complete.jar ``` ## Run the Build @@ -142,19 +142,11 @@ Usage: `-DBUILD_BECHMARKS:bool=true` Default: `false` -#### `BUILD_SYSTEM_TESTS` +#### `BUILD_TESTS` -In addition to the main build, also build system tests located in the `tests/` directory. The system tests run dissolve on a large number of known data sets to confirm that there are no error in those calculations. +In addition to the main build, also build tests located in the `tests/` directory. The tests check individual portions of the code for problems, and run Dissolve on a number of known data sets to confirm that there are no errors in those calculations. -Example: `-DBUILD_SYSTEM_TESTS:bool=true` - -Default: `false` - -#### `BUILD_UNIT_TESTS` - -In addition to the main build, also build unit tests located in the `unit/` directory. The unit tests check individual portions of the dissolve code base for problems. These tests are not as comprehensive, but they run much quicker than the system tests and provide more precision as to where a bug is arising. - -Example: `-DBUILD_UNIT_TESTS:bool=true` +Example: `-DBUILD_TESTS:bool=true` Default: `false` From 1b60ce2b4c6b5e04b80208bcbaa38b1fc72f406d Mon Sep 17 00:00:00 2001 From: Noella Spitz <101950441+rhinoella@users.noreply.github.com> Date: Tue, 5 Mar 2024 15:22:13 +0000 Subject: [PATCH 05/13] 1620 Compare Module GUI Implementation (#1662) Co-authored-by: Tristan Youngs Co-authored-by: Adam Washington Co-authored-by: Danbr4d Co-authored-by: Jared Swift --- src/classes/CMakeLists.txt | 1 - src/classes/dataSource.cpp | 168 ----- src/classes/dataSource.h | 216 +++++-- src/gui/keywordWidgets/producers.cpp | 4 - src/keywords/CMakeLists.txt | 2 +- src/keywords/dataSource.h | 176 ++--- src/keywords/dataSourceBase.cpp | 11 +- src/keywords/dataSourceBase.h | 22 +- src/math/data1DBase.h | 1 - src/math/data2DBase.h | 2 - src/math/data3DBase.h | 1 - src/math/error.cpp | 2 +- src/modules/compare/compare.cpp | 10 +- src/modules/compare/compare.h | 9 +- src/modules/compare/gui/CMakeLists.txt | 1 + src/modules/compare/gui/compareWidget.h | 47 ++ src/modules/compare/gui/compareWidget.ui | 170 +++++ .../compare/gui/compareWidgetFuncs.cpp | 102 +++ src/modules/compare/process.cpp | 33 +- src/modules/widgetProducer.cpp | 3 + tests/data/dissolve/compare/extvsext.dat | 600 ++++++++++++++++++ tests/data/dissolve/compare/intvsext.dat | 599 +++++++++++++++++ tests/data/dissolve/compare/intvsint.dat | 599 +++++++++++++++++ tests/data/dissolve/input/epsr-benzene-3n.txt | 26 + tests/modules/CMakeLists.txt | 1 + tests/modules/compare.cpp | 94 +++ .../modules/checks&tests/compare/_index.md | 2 +- 27 files changed, 2551 insertions(+), 351 deletions(-) delete mode 100644 src/classes/dataSource.cpp create mode 100644 src/modules/compare/gui/CMakeLists.txt create mode 100644 src/modules/compare/gui/compareWidget.h create mode 100644 src/modules/compare/gui/compareWidget.ui create mode 100644 src/modules/compare/gui/compareWidgetFuncs.cpp create mode 100644 tests/data/dissolve/compare/extvsext.dat create mode 100644 tests/data/dissolve/compare/intvsext.dat create mode 100644 tests/data/dissolve/compare/intvsint.dat create mode 100644 tests/modules/compare.cpp diff --git a/src/classes/CMakeLists.txt b/src/classes/CMakeLists.txt index 0fd44c8741..bf9d140601 100644 --- a/src/classes/CMakeLists.txt +++ b/src/classes/CMakeLists.txt @@ -28,7 +28,6 @@ add_library( configuration_sites.cpp configuration_upkeep.cpp coreData.cpp - dataSource.cpp distributor.cpp empiricalFormula.cpp isotopeData.cpp diff --git a/src/classes/dataSource.cpp b/src/classes/dataSource.cpp deleted file mode 100644 index bade998c27..0000000000 --- a/src/classes/dataSource.cpp +++ /dev/null @@ -1,168 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -// Copyright (c) 2024 Team Dissolve and contributors - -#include "classes/dataSource.h" -#include "templates/optionalRef.h" -#include - -// Return enum options for data source types -EnumOptions DataSource::dataSourceTypes() -{ - return EnumOptions("DataSourceType", {{Internal, "Internal"}, {External, "External"}}); -} - -// Return data name (tag or filename) -std::string_view DataSource::dataName() const { return dataName_; } - -// Return whether or not the data has been sourced and exists -bool DataSource::dataExists() const -{ - return (!internalDataSource_.empty() && dataSourceType_ == Internal) || - (!externalDataSource_.valueless_by_exception() && !data_.valueless_by_exception() && dataSourceType_ == External); -} - -// Return data source type enum -DataSource::DataSourceType DataSource::dataSourceType() const { return dataSourceType_; } - -// Return internal data source -std::optional DataSource::internalDataSource() const -{ - if (!internalDataSource_.empty()) - { - return internalDataSource_; - } - return std::nullopt; -} - -// Return external data source -DataSource::Format &DataSource::externalDataSource() { return externalDataSource_; } - -// Function to source data (only required for internal data sources) -bool DataSource::sourceData(GenericList &processingModuleData) -{ - if (!dataExists()) - { - return false; - } - - // For Internal data sources, try to locate the data now - if (dataSourceType_ == Internal) - { - if (std::holds_alternative(data_) || std::holds_alternative(data_)) - { - // Locate target data from tag and cast to base - auto optData = processingModuleData.searchBase(internalDataSource_); - if (!optData) - { - return Messenger::error("No data with tag '{}' exists.\n", internalDataSource_); - } - // Set data - data_ = optData->get(); - } - else - { - // For other data types just visit the variant - return std::visit( - [=](auto data) - { - // Locate target data from tag - auto optData = processingModuleData.search(internalDataSource_); - if (!optData) - { - return Messenger::error("No data with tag '{}' exists.\n", internalDataSource_); - } - // Set data - data_ = optData->get(); - return true; - }, - data_); - } - } - - // Return whether or not variant contains value - return !data_.valueless_by_exception(); -} - -// Set internal data source -void DataSource::addData(std::string_view internalDataSource) -{ - dataSourceType_ = Internal; - internalDataSource_ = internalDataSource; - // Set data name to be data tag - dataName_ = internalDataSource; -} - -// Write through specified LineParser -bool DataSource::serialise(LineParser &parser, std::string_view keywordName, std::string_view prefix) const -{ - if (!dataExists()) - { - return false; - } - - // Write source: internal/external - if (!parser.writeLineF("{}{} ", prefix, dataSourceTypes().keyword(dataSourceType_))) - { - return false; - } - - // For internal data sources just need to write the tag. Externals write the file and format - if (dataSourceType_ == Internal) - { - if (!parser.writeLineF("'{}'\n", internalDataSource_)) - return false; - } - else - { - return std::visit( - [&](auto &data) - { - // Write filename and format - if (!data.writeFilenameAndFormat(parser, prefix)) - { - return false; - } - - // Write extra keywords - if (!data.writeBlock(parser, prefix)) // Express as a serialisable value - { - return false; - } - - // Write extra keywords - if (!data.writeBlock(parser, prefix)) // Express as a serialisable value - { - return false; - } - - // End the block - if (!parser.writeLineF("End{}", dataSourceTypes().keyword(External))) - { - return false; - } - - return true; - }, - externalDataSource_); - } - - return true; -} - -// Express as a serialisable value -SerialisedValue DataSource::serialise() const -{ - SerialisedValue result; - - result["dataSourceType"] = dataSourceTypes().keyword(dataSourceType_); - if (dataSourceType_ == Internal) - { - result["data"] = internalDataSource_; - } - else - { - result["data"] = std::visit([](auto &data) { return data.serialise(); }, externalDataSource_); - } - - return result; -} diff --git a/src/classes/dataSource.h b/src/classes/dataSource.h index 1b9e668b15..22a04a788f 100644 --- a/src/classes/dataSource.h +++ b/src/classes/dataSource.h @@ -9,86 +9,228 @@ #include "io/import/data2D.h" #include "io/import/data3D.h" #include "items/list.h" -#include "keywords/dataSourceBase.h" #include "math/data1D.h" #include "math/data2D.h" #include "math/data3D.h" #include "math/sampledData1D.h" -#include +#include "module/context.h" -class DataSource +// Template arguments: data class (Data1D, Data2D ...) +template class DataSource : public Serialisable { - public: - using DataType = std::variant; - using Format = std::variant; public: DataSource() = default; ~DataSource() = default; public: - // Data Source Types + // Types of data sources allowed enum DataSourceType { Internal, External }; + + public: // Return enum options for DataSourceType - static EnumOptions dataSourceTypes(); + static EnumOptions dataSourceTypes() + { + return EnumOptions("DataSourceType", {{Internal, "Internal"}, {External, "External"}}); + } // Return data source type enum - DataSourceType dataSourceType() const; + DataSourceType dataSourceType() const { return dataSourceType_; } /* * Data */ + private: // Name of data (tag or filename) std::string dataName_; - // Type of data being stored + // Type of data source being stored DataSourceType dataSourceType_; // String to hold internal data tag (if internal) std::string internalDataSource_; - // Variant to hold file and format of external data (if external) - Format externalDataSource_; - // Variant to hold data + // Formatter object + typename DataType::Formatter externalDataSource_; + // Data object stored DataType data_; public: // Return data name - std::string_view dataName() const; + std::string_view dataName() const { return dataName_; } // Return if data exists and has been initialised - bool dataExists() const; - // Return internal data source - std::optional internalDataSource() const; - // Return external data source - Format &externalDataSource(); - // Source specified data (only required for internal data sources) - bool sourceData(GenericList &processingModuleData); - // Set internal data source - void addData(std::string_view internalDataSource); - // Overloaded function to add external data - template void addData(D data, typename D::Formatter &fileAndFormat) + bool dataExists() const + { + return (!internalDataSource_.empty() && dataSourceType_ == Internal) || + (externalDataSource_.hasFilename() && dataSourceType_ == External); + } + + // Changes data name to full filepath if data is external + void updateNameToPath() { - dataSourceType_ = External; - // Create format object in place in variant - externalDataSource_.emplace(fileAndFormat); - data_ = data; - // Set data name to be base filename - dataName_ = fileAndFormat.filename().substr(fileAndFormat.filename().find_last_of("/\\") + 1); + if (dataSourceType_ == External) + { + dataName_ = externalDataSource_.filename(); + } } - // Returns data in the requested type - template D data() const + + std::string_view getFilepath() { return dataSourceType_ == External ? externalDataSource_.filename() : ""; } + + // Obtain data from the relevant source + bool sourceData(const ProcessPool &procPool, GenericList &processingModuleData) { - assert(dataExists()); - return std::get(data_); + if (!dataExists()) + { + return false; + } + if (dataSourceType_ == Internal) + { + // Locate target data from tag and cast to base + auto optData = processingModuleData.search(internalDataSource_); + if (!optData) + { + return Messenger::error("No data with tag '{}' exists.\n", internalDataSource_); + } + // Set data + data_ = optData->get(); + + return true; + } + else if (dataSourceType_ == External) + { + // For external datatypes, import the data + if (!externalDataSource_.importData(data_, &procPool)) + { + return Messenger::error("Error importing data from '{}'", externalDataSource_.filename()); + } + + return true; + } + + return false; } + // Return the data + const DataType &data() const { return data_; } + /* * I/O */ public: + bool deserialise(LineParser &parser, int startArg, const CoreData &coreData) + { + if (!DataSource::dataSourceTypes().isValid(parser.argsv(0))) + { + return dataSourceTypes().errorAndPrintValid(parser.argsv(0)); + } + + // If data is internal + if (dataSourceTypes().enumeration(parser.argsv(startArg)) == Internal) + { + // Add data to dataSource + dataSourceType_ = Internal; + internalDataSource_ = parser.argsv(startArg + 1); + // Set data name to be data tag + dataName_ = internalDataSource_; + return true; + } + // If data is external + else if (dataSourceTypes().enumeration(parser.argsv(startArg)) == External) + { + // Read the supplied arguments + auto readResult = externalDataSource_.read(parser, startArg + 1, + fmt::format("End{}", dataSourceTypes().keyword(External)), coreData); + if (readResult == FileAndFormat::ReadResult::UnrecognisedFormat || + readResult == FileAndFormat::ReadResult::UnrecognisedOption) + { + return Messenger::error("Failed to read file/format for '{}'.\n", parser.argsv(startArg + 2)); + } + else + { + dataSourceType_ = External; + // Set data name to be base filename + dataName_ = externalDataSource_.filename().substr(externalDataSource_.filename().find_last_of("/\\") + 1); + return true; + } + } + return false; + } + + void deserialise(const SerialisedValue &node, const CoreData &coreData) + { + auto dataSourceType = toml::find(node, "dataSourceType"); + if (dataSourceTypes().enumeration(dataSourceType) == Internal) + { + dataSourceType_ = Internal; + // Set data to be the tag + internalDataSource_ = toml::find(node, "source"); + // Set data name to be data tag + dataName_ = toml::find(node, "source"); + } + // If data source type is external + else if (dataSourceTypes().enumeration(dataSourceType) == External) + { + dataSourceType_ = External; + // Read the file and format + externalDataSource_.deserialise(node.at("source"), coreData); + // Set the data name as root filename + dataName_ = externalDataSource_.filename().substr(externalDataSource_.filename().find_last_of("/\\") + 1); + } + } + // Write through specified LineParser - bool serialise(LineParser &parser, std::string_view keywordName, std::string_view prefix) const; + bool serialise(LineParser &parser, std::string_view keywordName, std::string_view prefix) const + { + // Write source: internal/external + if (!parser.writeLineF(" {}{}", prefix, dataSourceTypes().keyword(dataSourceType_))) + { + return false; + } + + // If data is internal + if (dataSourceType_ == Internal) + { + if (!parser.writeLineF(" '{}'\n", internalDataSource_)) + return false; + } + + else if (dataSourceType_ == External) + { + // Write filename and format + if (!externalDataSource_.writeFilenameAndFormat(parser, " ")) + { + return false; + } + + // Write extra keywords + if (!externalDataSource_.writeBlock(parser, fmt::format(" {}", prefix))) + { + return false; + } + + // End the block + if (!parser.writeLineF(" End{}\n", dataSourceTypes().keyword(External))) + { + return false; + } + + return true; + } + + return true; + } // Express as a serialisable value - SerialisedValue serialise() const; + SerialisedValue serialise() const + { + if (dataSourceType_ == Internal) + { + return {{"dataSourceType", dataSourceTypes().keyword(dataSourceType_)}, {"source", internalDataSource_}}; + } + else + { + return {{"dataSourceType", dataSourceTypes().keyword(dataSourceType_)}, + {"source", externalDataSource_.serialise()}}; + } + } }; diff --git a/src/gui/keywordWidgets/producers.cpp b/src/gui/keywordWidgets/producers.cpp index f07356636d..ca0879d644 100644 --- a/src/gui/keywordWidgets/producers.cpp +++ b/src/gui/keywordWidgets/producers.cpp @@ -41,7 +41,6 @@ #include "io/import/data2D.h" #include "io/import/data3D.h" #include "keywords/dataSource.h" -#include "keywords/dataSourceBase.h" #include "keywords/elementVector.h" #include "keywords/expression.h" #include "keywords/nodeBranch.h" @@ -87,9 +86,6 @@ KeywordWidgetProducer::KeywordWidgetProducer() // Keywords with no widgets registerNullProducer(); - registerNullProducer>(); - registerNullProducer>(); - registerNullProducer>(); registerNullProducer(); registerNullProducer(); registerNullProducer(); diff --git a/src/keywords/CMakeLists.txt b/src/keywords/CMakeLists.txt index d4d4b0083b..0d375e2362 100644 --- a/src/keywords/CMakeLists.txt +++ b/src/keywords/CMakeLists.txt @@ -40,8 +40,8 @@ add_library( bool.h configuration.h configurationVector.h - dataSourceBase.h dataSource.h + dataSourceBase.h double.h elementVector.h enumOptions.h diff --git a/src/keywords/dataSource.h b/src/keywords/dataSource.h index 447baacd24..7e3bdb66c8 100644 --- a/src/keywords/dataSource.h +++ b/src/keywords/dataSource.h @@ -3,6 +3,7 @@ #pragma once +#include "classes/dataSource.h" #include "io/import/data1D.h" #include "io/import/data2D.h" #include "io/import/data3D.h" @@ -13,20 +14,35 @@ #include "templates/optionalRef.h" #include -// Keyword managing a DataSource +// Keyword managing data sources +// Template arguments: data class (Data1D, Data2D ...) template class DataSourceKeyword : public DataSourceKeywordBase { + // Typedef public: - explicit DataSourceKeyword(std::vector &dataSources, std::string_view endKeyword) - : DataSourceKeywordBase(dataSources, endKeyword){}; + using DataPair = std::pair>, std::shared_ptr>>; + + public: + DataSourceKeyword(std::vector &dataSources, std::string_view endKeyword) + : DataSourceKeywordBase(), dataSources_(dataSources), endKeyword_(endKeyword) + { + } ~DataSourceKeyword() override = default; /* * Data */ private: - // Format object for the data - typename DataType::Formatter format_; + // Vector of data source pairs + std::vector &dataSources_; + // End keyword + const std::string endKeyword_; + // Gets path basename + std::string_view getBasename(std::string_view filename) const { return filename.substr(filename.find_last_of("/\\") + 1); } + + public: + // Return data source pairs + std::vector &dataSources() { return dataSources_; } /* * Arguments @@ -40,13 +56,15 @@ template class DataSourceKeyword : public DataSourceKeywordBase bool deserialise(LineParser &parser, int startArg, const CoreData &coreData) override { // Emplacing back on data vector and getting the reference to the objects - DataSource dataSourceA, dataSourceB; + auto &[dataSourceA, dataSourceB] = + dataSources_.emplace_back(std::make_shared>(), std::make_shared>()); // Create a queue for the dataSource objects - std::queue sourceQueue({&dataSourceA, &dataSourceB}); + std::queue>> sourceQueue({dataSourceA, dataSourceB}); // Read the next line if (parser.getArgsDelim(LineParser::Defaults) != LineParser::Success) { + dataSources_.pop_back(); return false; } @@ -58,53 +76,40 @@ template class DataSourceKeyword : public DataSourceKeywordBase break; } // If data source type supplied is valid - if (!DataSource::dataSourceTypes().isValid(parser.argsv(0))) + if (!sourceQueue.front()->deserialise(parser, 0, coreData)) { // If not, print accepted options - return DataSource::dataSourceTypes().errorAndPrintValid(parser.argsv(0)); + dataSources_.pop_back(); + return false; } - // Next parse depends on whether we have internal / external data - if (DataSource::dataSourceTypes().enumeration(parser.argsv(0)) == DataSource::Internal) - { - // Add data to dataSource - sourceQueue.front()->addData(parser.argsv(1)); - // Remove dataSource from queue - sourceQueue.pop(); - } - else if (DataSource::dataSourceTypes().enumeration(parser.argsv(0)) == DataSource::External) + // Check to make sure we don't have the same names + for (auto &[existingSourceA, existingSourceB] : dataSources_) { - // Initialise data and format objects - DataType data; - typename DataType::Formatter format; - - // Read the supplied arguments - if (format.read(parser, 1, fmt::format("End{}", DataSource::dataSourceTypes().keyword(DataSource::External)), - coreData) != FileAndFormat::ReadResult::Success) + if (getBasename(existingSourceA->dataName()) == getBasename(sourceQueue.front()->dataName())) { - return false; + if (existingSourceA->getFilepath() != sourceQueue.front()->getFilepath()) + { + existingSourceA->updateNameToPath(); + sourceQueue.front()->updateNameToPath(); + } } - - // Import the data - if (!format.importData(data, parser.processPool())) + if (getBasename(existingSourceB->dataName()) == getBasename(sourceQueue.front()->dataName())) { - return false; + if (existingSourceB->getFilepath() != sourceQueue.front()->getFilepath()) + { + existingSourceB->updateNameToPath(); + sourceQueue.front()->updateNameToPath(); + } } - - // Add data to dataSource - sourceQueue.front()->addData(data, format); - // Remove dataSource from queue - sourceQueue.pop(); - } - else - { - return Messenger::error("Unsupported data source type '{}' provided to keyword '{}'\n", parser.argsv(0), - name()); } + sourceQueue.pop(); + // Read the next line if (parser.getArgsDelim() != LineParser::Success) { + dataSources_.pop_back(); return false; } @@ -115,8 +120,6 @@ template class DataSourceKeyword : public DataSourceKeywordBase } } - dataSources_.emplace_back(std::make_pair(dataSourceA, dataSourceB)); - return true; } @@ -132,19 +135,19 @@ template class DataSourceKeyword : public DataSourceKeywordBase } // Serialise the first data source - if (!dataSourceA.serialise(parser, keywordName, prefix)) + if (!dataSourceA->serialise(parser, keywordName, prefix)) { return false; } // Skip to next iteration if dataSourceB is undefined - if (!dataSourceB.dataExists()) + if (!dataSourceB->dataExists()) { continue; } // Serialise the second data source (optional) - if (!dataSourceB.serialise(parser, keywordName, prefix)) + if (!dataSourceB->serialise(parser, keywordName, prefix)) { return false; } @@ -164,56 +167,59 @@ template class DataSourceKeyword : public DataSourceKeywordBase return fromVector(dataSources_, [](const auto &item) -> SerialisedValue { - SerialisedValue result = SerialisedValue::array_type{}; auto &[dataSourceA, dataSourceB] = item; - result.push_back(dataSourceA.serialise()); - // If optional second data source exists - if (dataSourceB.dataExists()) + if (dataSourceB->dataExists()) { - result.push_back(dataSourceB.serialise()); + return {{"dataSourceA", dataSourceA->serialise()}, {"dataSourceB", dataSourceB->serialise()}}; + } + else + { + return {{"dataSourceA", dataSourceA->serialise()}}; } - return result; }); } // Read values from a serialisable value void deserialise(const SerialisedValue &node, const CoreData &coreData) override { - // Emplacing back on data vector and getting the reference to the objects - DataSource dataSourceA, dataSourceB; - // Create a queue for the dataSource objects - std::queue sourceQueue({&dataSourceA, &dataSourceB}); - toVector(node, - [this, &coreData, &sourceQueue](const auto &item) + [this, &coreData](const auto &dataPair) { - // If data source type is internal - if (DataSource::dataSourceTypes().enumeration(toml::find(item, "dataSourceType")) == - DataSource::Internal && - !sourceQueue.empty()) - { - // Add data to dataSource - sourceQueue.front()->addData(toml::find(item, "data")); - // Remove dataSource from queue - sourceQueue.pop(); - } - // If data source type is external - else if (!sourceQueue.empty()) - { - DataType data; - typename DataType::Formatter format; - - // Deserialise FileAndFormat - format.deserialise(item.at("data"), coreData); - // Import data - format.importData(data); - - // Add data to dataSource - sourceQueue.front()->addData(toml::find(item, "data")); - // Remove dataSource from queue - sourceQueue.pop(); - } + // Emplacing back on data vector and getting the reference to the objects + auto &[dataSourceA, dataSourceB] = dataSources_.emplace_back(std::make_shared>(), + std::make_shared>()); + // Create a queue for the dataSource objects + std::queue>> sourceQueue({dataSourceA, dataSourceB}); + + toMap(dataPair, + [this, &coreData, &sourceQueue](const auto &key, const auto &dataSource) + { + if (sourceQueue.empty()) + return; + // Add data to dataSource + sourceQueue.front()->deserialise(dataSource, coreData); + // Check to make sure we don't have the same names + for (auto &[existingSourceA, existingSourceB] : dataSources_) + { + if (getBasename(existingSourceA->dataName()) == getBasename(sourceQueue.front()->dataName())) + { + if (existingSourceA->getFilepath() != sourceQueue.front()->getFilepath()) + { + existingSourceA->updateNameToPath(); + sourceQueue.front()->updateNameToPath(); + } + } + if (getBasename(existingSourceB->dataName()) == getBasename(sourceQueue.front()->dataName())) + { + if (existingSourceB->getFilepath() != sourceQueue.front()->getFilepath()) + { + existingSourceB->updateNameToPath(); + sourceQueue.front()->updateNameToPath(); + } + } + } + // Remove dataSource from queue + sourceQueue.pop(); + }); }); - - dataSources_.emplace_back(std::make_pair(dataSourceA, dataSourceB)); } }; diff --git a/src/keywords/dataSourceBase.cpp b/src/keywords/dataSourceBase.cpp index 3528cd5f15..191e76dbec 100644 --- a/src/keywords/dataSourceBase.cpp +++ b/src/keywords/dataSourceBase.cpp @@ -1,10 +1,3 @@ -#include "keywords/dataSource.h" +#include "keywords/dataSourceBase.h" -DataSourceKeywordBase::DataSourceKeywordBase(std::vector &dataSources, - std::string_view endKeyword) - : KeywordBase(typeid(this)), dataSources_(dataSources), endKeyword_(endKeyword) -{ -} - -// Returns reference to module data -std::vector &DataSourceKeywordBase::dataSources() { return dataSources_; } \ No newline at end of file +DataSourceKeywordBase::DataSourceKeywordBase() : KeywordBase(typeid(this)) {} \ No newline at end of file diff --git a/src/keywords/dataSourceBase.h b/src/keywords/dataSourceBase.h index 1f4d0f194b..d3c51e27dc 100644 --- a/src/keywords/dataSourceBase.h +++ b/src/keywords/dataSourceBase.h @@ -6,32 +6,14 @@ #include "base/enumOptions.h" #include "classes/dataSource.h" #include "io/fileAndFormat.h" +#include "keywords/base.h" #include "math/dataBase.h" -// Forward Declarations -class DataSource; - // Base keyword for data source class DataSourceKeywordBase : public KeywordBase { - // Typedef - public: - using DataPair = std::pair; public: - DataSourceKeywordBase(std::vector &dataSources, std::string_view endKeyword); + DataSourceKeywordBase(); ~DataSourceKeywordBase() override = default; - - /* - * Data - */ - protected: - // End keyword - const std::string endKeyword_; - // Reference to module data - std::vector &dataSources_; - - public: - // Returns reference to module data - std::vector &dataSources(); }; \ No newline at end of file diff --git a/src/math/data1DBase.h b/src/math/data1DBase.h index 2424a81f1c..a61073d5a0 100644 --- a/src/math/data1DBase.h +++ b/src/math/data1DBase.h @@ -12,7 +12,6 @@ class Data1DImportFileFormat; // Data 1D Base class Data1DBase : public DataBase { - /* * Type Definitions */ diff --git a/src/math/data2DBase.h b/src/math/data2DBase.h index 8d111ee9db..06004178ea 100644 --- a/src/math/data2DBase.h +++ b/src/math/data2DBase.h @@ -3,7 +3,6 @@ #pragma once -#include "io/import/data2D.h" #include "math/dataBase.h" #include "templates/array2D.h" @@ -13,7 +12,6 @@ class Data2DImportFileFormat; // Data 2D Base class Data2DBase : public DataBase { - /* * Type Definitions */ diff --git a/src/math/data3DBase.h b/src/math/data3DBase.h index 7a76a71152..4f425db29d 100644 --- a/src/math/data3DBase.h +++ b/src/math/data3DBase.h @@ -3,7 +3,6 @@ #pragma once -#include "io/import/data3D.h" #include "math/dataBase.h" #include "templates/array3D.h" diff --git a/src/math/error.cpp b/src/math/error.cpp index fb279087dc..71d4bcf202 100644 --- a/src/math/error.cpp +++ b/src/math/error.cpp @@ -25,7 +25,7 @@ EnumOptions errorTypes() std::string errorReportString(const ErrorReport &errorReport) { - return fmt::format("{} between datasets is {:e} over {:e} < x < {:e} ({} points).\n", + return fmt::format("{} error between datasets is {:e} over {:e} < x < {:e} ({} points).\n", errorTypes().keyword(errorReport.errorType), errorReport.error, errorReport.firstX, errorReport.lastX, errorReport.nPointsConsidered); } diff --git a/src/modules/compare/compare.cpp b/src/modules/compare/compare.cpp index 721fc9988b..cf214d5769 100644 --- a/src/modules/compare/compare.cpp +++ b/src/modules/compare/compare.cpp @@ -8,11 +8,15 @@ CompareModule::CompareModule() : Module(ModuleTypes::Compare) { keywords_.setOrganisation("Options"); - keywords_.add>("Data1D", "Specify one-dimensional test reference data", dataSources_, + keywords_.add>("Data1D", "Specify one-dimensional test reference data", data1dSources_, "EndData1D"); keywords_.add>("ErrorType", "Type of error calculation to use", errorType_, Error::errorTypes()); - keywords_.add("RFactorRanges", "Ranges over which to calculate RFactors", ranges_); + keywords_.add("ErrorRange", "Ranges over which to calculate RFactors", ranges_); } -const std::vector &CompareModule::dataSources() { return dataSources_; } +const std::vector::DataPair> &CompareModule::data1dSources() const { return data1dSources_; } +const std::map::DataPair *, RangeErrorPair> &CompareModule::data1dSourcesErrors() const +{ + return data1dSourcesErrors_; +} diff --git a/src/modules/compare/compare.h b/src/modules/compare/compare.h index 96f6826838..fbbc96ad92 100644 --- a/src/modules/compare/compare.h +++ b/src/modules/compare/compare.h @@ -10,7 +10,6 @@ #include "keywords/dataSource.h" #include "main/dissolve.h" #include "math/data1D.h" -#include "math/data1DBase.h" #include "math/data2D.h" #include "math/data3D.h" #include "math/error.h" @@ -33,15 +32,19 @@ class CompareModule : public Module */ private: // Data stored in the keyword - std::vector dataSources_; + std::vector::DataPair> data1dSources_; // Method of error calculation to use Error::ErrorType errorType_{Error::EuclideanError}; // Ranges to calculate error over std::vector ranges_; + // Mapping from data pair to ranges and error + std::map::DataPair *, RangeErrorPair> data1dSourcesErrors_; public: // Return data sources - const std::vector &dataSources(); + const std::vector::DataPair> &data1dSources() const; + // Return errors + const std::map::DataPair *, RangeErrorPair> &data1dSourcesErrors() const; /* * Processing diff --git a/src/modules/compare/gui/CMakeLists.txt b/src/modules/compare/gui/CMakeLists.txt new file mode 100644 index 0000000000..96d0542fbc --- /dev/null +++ b/src/modules/compare/gui/CMakeLists.txt @@ -0,0 +1 @@ +dissolve_add_module_gui(compare) diff --git a/src/modules/compare/gui/compareWidget.h b/src/modules/compare/gui/compareWidget.h new file mode 100644 index 0000000000..49445b02b9 --- /dev/null +++ b/src/modules/compare/gui/compareWidget.h @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team Dissolve and contributors + +#pragma once + +#include "modules/compare/gui/ui_compareWidget.h" +#include "modules/widget.h" +#include +#include + +// Forward Declarations +class CompareModule; +class DataViewer; + +// Module Widget +class CompareModuleWidget : public ModuleWidget +{ + // All Qt declarations derived from QObject must include this macro + Q_OBJECT + + public: + CompareModuleWidget(QWidget *parent, CompareModule *module, Dissolve &dissolve); + ~CompareModuleWidget() override = default; + + private: + // Associated Module + CompareModule *module_; + + /* + * UI + */ + private: + // Main form declaration + Ui::CompareModuleWidget ui_; + // DataViewers contained within this widget + DataViewer *graph_; + + public: + // Update controls within widget + void updateControls(const Flags &updateFlags = {}) override; + + /* + * Widgets / Functions + */ + private Q_SLOTS: + void on_Data1DButton_clicked(bool checked); +}; diff --git a/src/modules/compare/gui/compareWidget.ui b/src/modules/compare/gui/compareWidget.ui new file mode 100644 index 0000000000..a4f3aa314e --- /dev/null +++ b/src/modules/compare/gui/compareWidget.ui @@ -0,0 +1,170 @@ + + + CompareModuleWidget + + + + 0 + 0 + 929 + 669 + + + + + 0 + 0 + + + + + 8 + + + + Compare Controls + + + + 4 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + 0 + + + + + true + + + true + + + Data1D + + + + + + + true + + + true + + + false + + + Data2D + + + + + + + true + + + true + + + Data3D + + + false + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 0 + 0 + + + + true + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + + + + + + DataWidget + QWidget +
gui/dataWidget.h
+ 1 +
+
+ + +
diff --git a/src/modules/compare/gui/compareWidgetFuncs.cpp b/src/modules/compare/gui/compareWidgetFuncs.cpp new file mode 100644 index 0000000000..05e887ded6 --- /dev/null +++ b/src/modules/compare/gui/compareWidgetFuncs.cpp @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team Dissolve and contributors + +#include "gui/render/renderableData1D.h" +#include "main/dissolve.h" +#include "modules/compare/compare.h" +#include "modules/compare/gui/compareWidget.h" + +CompareModuleWidget::CompareModuleWidget(QWidget *parent, CompareModule *module, Dissolve &dissolve) + : ModuleWidget(parent, dissolve), module_(module) +{ + // Set up user interface + ui_.setupUi(this); + + refreshing_ = true; + + // Check data1D button by default + ui_.Data1DButton->setChecked(true); + + // Set up the main graph + graph_ = ui_.PlotWidget->dataViewer(); + + // -- Set view + graph_->view().setViewType(View::FlatXYView); + graph_->view().axes().setMax(0, 10.0); + graph_->view().axes().setMin(1, -1.0); + graph_->view().axes().setMax(1, 1.0); + graph_->view().setAutoFollowType(View::AllAutoFollow); + graph_->groupManager().setVerticalShiftAmount(RenderableGroupManager::TwoVerticalShift); + + // -- Set group styling + graph_->groupManager().setGroupFixedColour("Delta", StockColours::BlackStockColour); + graph_->groupManager().setGroupColouring("Delta", RenderableGroup::FixedGroupColouring); + graph_->groupManager().setGroupStipple("Delta", LineStipple::QuarterDashStipple); + graph_->groupManager().setGroupVerticalShifting("Delta", RenderableGroup::IndividualVerticalShifting); + + graph_->groupManager().setGroupFixedColour("DataA", StockColours::RedStockColour); + graph_->groupManager().setGroupColouring("DataA", RenderableGroup::FixedGroupColouring); + graph_->groupManager().setGroupVerticalShifting("DataA", RenderableGroup::IndividualVerticalShifting); + + graph_->groupManager().setGroupFixedColour("DataB", StockColours::LightGreenStockColour); + graph_->groupManager().setGroupColouring("DataB", RenderableGroup::FixedGroupColouring); + graph_->groupManager().setGroupVerticalShifting("DataB", RenderableGroup::IndividualVerticalShifting); + + refreshing_ = false; + updateControls(ModuleWidget::RecreateRenderablesFlag); +} + +/* + * UI + */ + +// Update controls within widget +void CompareModuleWidget::updateControls(const Flags &updateFlags) +{ + + if (updateFlags.isSet(ModuleWidget::RecreateRenderablesFlag) || ui_.PlotWidget->dataViewer()->renderables().empty()) + { + refreshing_ = true; + + ui_.PlotWidget->clearRenderableData(); + + // Create the renderables for Data1D + if (ui_.Data1DButton->isChecked()) + { + auto dSourceCount = 1; + for (auto &[dataSourceA, dataSourceB] : module_->data1dSources()) + { + // Render the difference (delta) between the datasets + graph_->createRenderable(fmt::format("{}//Pair{}_Delta", module_->name(), dSourceCount), + "Delta", "Delta"); + // Render DataA in the pair + graph_->createRenderable(fmt::format("{}//Pair{}_DataA", module_->name(), dSourceCount), + fmt::format("{}", dataSourceA->dataName()), "DataA"); + // Render DataB in the pair + graph_->createRenderable(fmt::format("{}//Pair{}_DataB", module_->name(), dSourceCount), + fmt::format("{}", dataSourceB->dataName()), "DataB"); + + // Validate renderables if they need it + graph_->validateRenderables(dissolve_.processingModuleData()); + + graph_->view().axes().setMax(1, std::max(dataSourceA->data().maxValue(), dataSourceB->data().maxValue()) + 5.0); + + ++dSourceCount; + } + } + + ui_.PlotWidget->updateToolbar(); + + graph_->postRedisplay(); + + refreshing_ = false; + } +} + +void CompareModuleWidget::on_Data1DButton_clicked(bool checked) +{ + if (checked) + { + updateControls(ModuleWidget::RecreateRenderablesFlag); + } +} \ No newline at end of file diff --git a/src/modules/compare/process.cpp b/src/modules/compare/process.cpp index 318a6976b6..4f62fb10a3 100644 --- a/src/modules/compare/process.cpp +++ b/src/modules/compare/process.cpp @@ -1,43 +1,46 @@ // SPDX-License-Identifier: GPL-3.0-or-later // Copyright (c) 2024 Team Dissolve and contributors +#include "classes/dataSource.h" #include "math/interpolator.h" #include "modules/compare/compare.h" Module::ExecutionResult CompareModule::process(ModuleContext &moduleContext) { - // Mapping from data pair to ranges and error - std::map dataSourcesErrors; - auto index = 1; - for (auto &dataPair : dataSources_) + for (auto &dataPair : data1dSources_) { /* * Preparing data */ // Check to make sure the data exists - if (!dataPair.first.dataExists() || !dataPair.second.dataExists()) + if (!dataPair.first->dataExists() || !dataPair.second->dataExists()) { + Messenger::print("Skipping {} and {}", dataPair.first->dataName(), dataPair.second->dataName()); continue; } // Set the ranges in the map to reference the ranges_ vector - auto &ranges = dataSourcesErrors[&dataPair].first; + auto &ranges = data1dSourcesErrors_[&dataPair].first; ranges = ranges_; // Source the data - if (!dataPair.first.sourceData(moduleContext.dissolve().processingModuleData())) + if (!dataPair.first->sourceData(moduleContext.processPool(), moduleContext.dissolve().processingModuleData())) { + Messenger::print("Skipping {} and {}: could not source data for {}", dataPair.first->dataName(), + dataPair.second->dataName(), dataPair.first->dataName()); continue; } - if (!dataPair.second.sourceData(moduleContext.dissolve().processingModuleData())) + if (!dataPair.second->sourceData(moduleContext.processPool(), moduleContext.dissolve().processingModuleData())) { + Messenger::print("Skipping {} and {}: could not source data for {}", dataPair.first->dataName(), + dataPair.second->dataName(), dataPair.second->dataName()); continue; } - auto dataA = dataPair.first.data(); - auto dataB = dataPair.second.data(); + auto dataA = dataPair.first->data(); + auto dataB = dataPair.second->data(); /* * Save Data @@ -60,12 +63,12 @@ Module::ExecutionResult CompareModule::process(ModuleContext &moduleContext) ranges.insert(ranges.begin(), totalRange); // Set the error vector to be the same size as the ranges - dataSourcesErrors[&dataPair].second.resize(ranges.size()); + data1dSourcesErrors_[&dataPair].second.resize(ranges.size()); - Messenger::print("Errors between {} and {}", dataPair.first.dataName(), dataPair.second.dataName()); + Messenger::print("Errors between {} and {}", dataPair.first->dataName(), dataPair.second->dataName()); // Loop through the the range & error vectors, calculating and reporting the errors - for (auto &&[range, error] : zip(ranges, dataSourcesErrors[&dataPair].second)) + for (auto &&[range, error] : zip(ranges, data1dSourcesErrors_[&dataPair].second)) { auto errorReport = Error::error(errorType_, dataA, dataB, range); error = errorReport.error; @@ -81,6 +84,8 @@ Module::ExecutionResult CompareModule::process(ModuleContext &moduleContext) auto &delta = moduleContext.dissolve().processingModuleData().realise(fmt::format("Pair{}_Delta", index), name_, GenericItem::InRestartFileFlag); + delta.clear(); + // Get the minimum and maximum x values that exist over both datasets auto rangeMin = std::min(dataA.xAxis().front(), dataB.xAxis().front()); auto rangeMax = std::max(dataA.xAxis().back(), dataB.xAxis().back()); @@ -106,4 +111,4 @@ Module::ExecutionResult CompareModule::process(ModuleContext &moduleContext) } return ExecutionResult::Success; -} \ No newline at end of file +} diff --git a/src/modules/widgetProducer.cpp b/src/modules/widgetProducer.cpp index b90fce94c1..0a1433448c 100644 --- a/src/modules/widgetProducer.cpp +++ b/src/modules/widgetProducer.cpp @@ -18,6 +18,8 @@ #include "modules/benchmark/gui/benchmarkWidget.h" #include "modules/bragg/bragg.h" #include "modules/bragg/gui/braggWidget.h" +#include "modules/compare/compare.h" +#include "modules/compare/gui/compareWidget.h" #include "modules/dAngle/dAngle.h" #include "modules/dAngle/gui/dAngleWidget.h" #include "modules/energy/energy.h" @@ -64,6 +66,7 @@ ModuleWidgetProducer::ModuleWidgetProducer() registerProducer(); registerProducer(); registerProducer(); + registerProducer(); registerProducer(); registerProducer(); registerProducer(); diff --git a/tests/data/dissolve/compare/extvsext.dat b/tests/data/dissolve/compare/extvsext.dat new file mode 100644 index 0000000000..1904b212eb --- /dev/null +++ b/tests/data/dissolve/compare/extvsext.dat @@ -0,0 +1,600 @@ +0.01 1.4272896741046646 +0.05 0.150967 +0.1 0.1362662 +0.15 0.1213561 +0.2 0.11441589999999999 +0.25 0.09772980000000003 +0.3 0.08662519999999999 +0.35 0.076298 +0.4 0.06933649999999997 +0.45 0.06194129999999998 +0.5 0.05521860000000001 +0.55 0.048677499999999985 +0.6 0.04176000000000002 +0.65 0.03784459999999998 +0.7 0.03397549999999999 +0.75 0.030145599999999995 +0.8 0.026427199999999984 +0.85 0.019758899999999996 +0.9 0.005514099999999994 +0.95 0.012998199999999988 +1 0.03696289999999999 +1.05 0.06107772799999975 +1.1 0.07607688300000001 +1.15 0.06876499000000001 +1.2 0.037315600000000476 +1.25 0.011965100000000006 +1.3 0.04662510000000014 +1.35 0.04244310000000001 +1.4 0.007032499999999997 +1.45 0.02496049999999972 +1.5 0.028444200000000003 +1.55 0.004861100000000146 +1.6 0.03740847 +1.65 0.08965523 +1.7 0.14191263999999998 +1.75 0.19100403 +1.8 0.2314101000000001 +1.85 0.2620454 +1.9 0.2785012 +1.95 0.2799334000000001 +2 0.2662565 +2.05 0.2427477 +2.1 0.21349967 +2.15 0.18538298 +2.2 0.15092406 +2.25 0.12263957900000001 +2.3 0.09915632999999999 +2.35 0.0798027 +2.4 0.06404256 +2.45 0.05129439999999999 +2.5 0.04201378 +2.55 0.03155136 +2.6 0.024274820000000003 +2.65 0.011536519999999995 +2.7 9.33699999999954e-05 +2.75 0.01490085 +2.8 0.02930588 +2.85 0.04071513 +2.9 0.055191039000000004 +2.95 0.06695104 +3 0.07393646 +3.05 0.07722066 +3.1 0.07874884 +3.15 0.07604162 +3.2 0.07416583 +3.25 0.06500332 +3.3 0.05832308 +3.35 0.05127606 +3.4 0.03832894 +3.45 0.026576546 +3.5 0.01897634 +3.55 0.006378290000000002 +3.6 0.0007619300000000009 +3.65 0.00932951 +3.7 0.01779729 +3.75 0.01781088 +3.8 0.016843320000000002 +3.85 0.01636712 +3.9 0.010235599999999997 +3.95 0.00016942000000000346 +4 0.010459839999999998 +4.05 0.024993310000000005 +4.1 0.037797239999999996 +4.15 0.05122632 +4.2 0.06687009999999999 +4.25 0.08391599999999999 +4.3 0.09823860000000001 +4.35 0.11612447 +4.4 0.12962387 +4.45 0.1514569 +4.5 0.16463248219999999 +4.55 0.17767428 +4.6 0.18951627 +4.65 0.19828557 +4.7 0.20806335999999997 +4.75 0.21284164 +4.8 0.22148351 +4.85 0.22096364 +4.9 0.22351745 +4.95 0.2261998 +5 0.22193269999999998 +5.05 0.21874612 +5.1 0.21309747 +5.15 0.20173752 +5.2 0.18714910999999998 +5.25 0.17745142 +5.3 0.1609317 +5.35 0.14168492800000002 +5.4 0.124255179 +5.45 0.10556284 +5.5 0.07981442 +5.55 0.06706456 +5.6 0.04364455 +5.65 0.018140210000000004 +5.7 0.0010038900000000073 +5.75 0.019657889999999997 +5.8 0.04198291 +5.85 0.06182647 +5.9 0.07507080000000001 +5.95 0.09474890999999999 +6 0.10899096600000001 +6.05 0.11900635500000001 +6.1 0.128073268 +6.15 0.13979392999999998 +6.2 0.14667304 +6.25 0.14968388999999999 +6.3 0.15360549 +6.35 0.14798666 +6.4 0.15396421 +6.45 0.14988948 +6.5 0.14648875 +6.55 0.14252995000000002 +6.6 0.13652252 +6.65 0.13023547 +6.7 0.1267335 +6.75 0.11580618000000001 +6.8 0.10662144000000001 +6.85 0.09715665 +6.9 0.09412039 +6.95 0.08742271 +7 0.08098680999999999 +7.05 0.073341886 +7.1 0.069812862 +7.15 0.06614809696 +7.2 0.060830843 +7.25 0.052779463 +7.3 0.05016912 +7.35 0.04701729 +7.4 0.04607282 +7.45 0.046312711 +7.5 0.04200778200000001 +7.55 0.037938621 +7.6 0.038958279 +7.65 0.03518322 +7.7 0.032925635 +7.75 0.025855675 +7.8 0.021454130999999998 +7.85 0.01707174 +7.9 0.01775223 +7.95 0.008540302 +8 0.002203749999999999 +8.05 0.0021735008890637706 +8.1 0.004617251000000065 +8.150001 0.008464203291643626 +8.2 0.009216423 +8.25 0.0142783341 +8.3 0.021770614567652953 +8.35 0.025099383000000156 +8.400001 0.027627346244809142 +8.45 0.02837854 +8.5 0.028048540000000004 +8.55 0.03345108033809677 +8.6 0.03379936000000009 +8.650001 0.03481491196353129 +8.7 0.03454483 +8.75 0.032049690000000006 +8.8 0.0321324936827215 +8.85 0.031421360000000176 +8.900001 0.027322811193186573 +8.95 0.026981239000000004 +9 0.026149791999999998 +9.05 0.017616946764486007 +9.1 0.01862417449999978 +9.150001 0.018982451565326235 +9.2 0.014107782 +9.25 0.011421589999999999 +9.3 0.011429355997775711 +9.35 0.0020156699999999955 +9.400001 0.005032223608705911 +9.45 0.0013177100000000014 +9.5 0.0006752299999999989 +9.55 0.008158432250945778 +9.6 0.00314914644751697 +9.650001 0.006339028496455892 +9.7 0.007335838999999999 +9.75 0.008877202 +9.8 0.005454130557504416 +9.85 0.010453064106597128 +9.900001 0.0063208700010367845 +9.95 0.01263972 +10 0.019100343 +10.05 0.0198065746 +10.1 0.022245628999999933 +10.15 0.023572649000000088 +10.2 0.031229459 +10.25 0.033815955 +10.3 0.03562023 +10.35 0.04322533999999985 +10.4 0.04364916999999993 +10.45 0.04891436 +10.5 0.05148374 +10.55 0.055193179999999994 +10.6 0.05500354000000009 +10.65 0.05468938000000044 +10.7 0.05689933 +10.75 0.057301920000000006 +10.8 0.05794805 +10.85 0.05488647999999963 +10.9 0.047153190000000095 +10.95 0.05205526 +11 0.04315277 +11.05 0.03877809 +11.1 0.0335769400000001 +11.15 0.031316619999999865 +11.2 0.02377485 +11.25 0.013336661 +11.3 0.010697408 +11.35 0.005611093300000083 +11.4 0.0013565829999998814 +11.45 0.0075827628 +11.5 0.012237468 +11.55 0.018056413 +11.6 0.02295132500000005 +11.65 0.02606614499999984 +11.7 0.026885024 +11.75 0.029363859000000003 +11.8 0.02874231 +11.85 0.03460464799999993 +11.9 0.03677225199999965 +11.95 0.039724726999999994 +12 0.036356042000000005 +12.05 0.03638675 +12.1 0.03786365399999976 +12.15 0.03358897900000032 +12.2 0.031424264 +12.25 0.032865064 +12.3 0.029362449 +12.35 0.02285942999999993 +12.4 0.026585311 +12.45 0.020082350000000002 +12.5 0.023273732 +12.55 0.022288291000000002 +12.6 0.018642877000000436 +12.65 0.02328343400000009 +12.7 0.017878882000000002 +12.75 0.021362847 +12.8 0.0204828827 +12.85 0.02528007800000004 +12.9 0.025043493000000173 +12.95 0.02216263 +13 0.021030172 +13.05 0.021644340999999997 +13.1 0.026339694000000274 +13.15 0.02084284999999999 +13.2 0.022002707 +13.25 0.0210822427 +13.3 0.019127671 +13.35 0.021842838000000024 +13.4 0.01650447800000036 +13.45 0.019414221000000002 +13.5 0.010844850000000001 +13.55 0.0095917 +13.6 0.0061369300000000335 +13.65 0.0060003500000002895 +13.7 0.003333759999999998 +13.75 0.0010911300000000027 +13.8 0.0029592100000000003 +13.85 0.007940699999999837 +13.9 0.006120610000000479 +13.95 0.00941195 +14 0.015974831999999998 +14.05 0.012104456999999999 +14.1 0.014407050999999806 +14.15 0.013450872999999855 +14.2 0.012848733000000001 +14.25 0.013418392000000001 +14.3 0.012916931 +14.35 0.011418017000000391 +14.4 0.006352077000000258 +14.45 0.010508137 +14.5 0.00553878 +14.55 0.004095066999999999 +14.6 0.004849700000000023 +14.65 0.0046390700000001365 +14.7 0.0012096200000000007 +14.75 0.001692219 +14.8 0.0008385350000000005 +14.85 0.006217661999999787 +14.9 0.007884149000000356 +14.95 0.0092556537 +15 0.011690988 +15.05 0.006676146 +15.1 0.005584106999999865 +15.15 0.0074077219999999 +15.2 0.009751722 +15.25 0.007959576 +15.3 0.008627805 +15.35 0.0035148056999999176 +15.4 0.008467040000000058 +15.45 0.0023821680000000005 +15.5 0.00693742 +15.55 0.0006392279999999999 +15.6 0.00036207000000006715 +15.65 0.004230061999999937 +15.7 0.005813048 +15.75 0.0052422685 +15.8 0.0096805202 +15.85 0.012127449999999932 +15.9 0.014303906999999939 +15.95 0.014847209 +16 0.011983262 +16.05 0.016782816 +16.1 0.021007425 +16.15 0.018415913 +16.2 0.020423124 +16.25 0.01892423 +16.3 0.0171808669 +16.35 0.0204567188 +16.4 0.0214617943 +16.45 0.019203714 +16.5 0.019622422 +16.55 0.014451292000000001 +16.6 0.017767504 +16.65 0.017180924 +16.7 0.013126955999999999 +16.75 0.013765641000000002 +16.8 0.017458867 +16.85 0.016544775999999997 +16.9 0.010699288 +16.95 0.008338602 +17 0.014970318 +17.05 0.011249970000000001 +17.1 0.012205036000000002 +17.15 0.008514627 +17.2 0.008094137999999999 +17.25 0.010565686 +17.3 0.010056646000000002 +17.35 0.0038835099999999997 +17.4 0.014802658 +17.45 0.007043371999999999 +17.5 0.006547606 +17.55 0.0034223610000000005 +17.6 0.0022685789999999997 +17.65 0.0026572880000000007 +17.7 0.0048410020000000005 +17.75 0.0026370718 +17.8 0.0016294275 +17.85 0.0037974410000000004 +17.9 0.006211188 +17.95 0.004356553 +18 0.0098065285 +18.05 0.0107415654 +18.1 0.0090963862 +18.15 0.0132824977 +18.2 0.01193761 +18.25 0.010748767 +18.3 0.015453123000000003 +18.35 0.017590724000000002 +18.4 0.015262925 +18.45 0.015259011 +18.5 0.015835835 +18.55 0.015349346000000002 +18.6 0.017828097 +18.65 0.013998568 +18.7 0.018412049 +18.75 0.018830818 +18.8 0.015539055999999999 +18.85 0.014280967 +18.9 0.014547189999999998 +18.95 0.019546758 +19 0.015913642 +19.05 0.01850169 +19.1 0.011674628999999999 +19.15 0.011719346 +19.2 0.014813412 +19.25 0.014501897 +19.3 0.008903003999999999 +19.35 0.007692752 +19.4 0.009254133 +19.45 0.007172880999999999 +19.5 0.006348998099999999 +19.55 0.0069342541 +19.6 0.00013549616000000002 +19.65 0.0033668795 +19.7 0.0021561339999999997 +19.75 0.003187192 +19.8 0.000970453 +19.85 0.0026329633 +19.9 0.001388075 +19.95 0.002597075 +20 0.0009513589999999997 +20.05 0.0048624089 +20.1 0.001612087 +20.15 0.0022800859999999997 +20.2 0.0010364120000000004 +20.25 0.0025545380000000003 +20.3 0.0031099099999999996 +20.35 0.0023458169999999996 +20.4 0.005889744 +20.45 0.0005384419999999992 +20.5 4.922800000000012e-05 +20.55 0.002247013 +20.6 0.00268591 +20.65 0.002686204 +20.7 0.005069224999999999 +20.75 0.0001499689999999998 +20.8 0.0030867959999999997 +20.85 0.0007746249999999997 +20.9 0.001057751 +20.95 0.0014715970000000003 +21 0.005641750000000001 +21.05 0.0036959120000000004 +21.1 0.004958651 +21.15 0.006625366999999999 +21.2 0.00224301 +21.25 0.0042686037 +21.3 0.010354559499999999 +21.35 0.0068635122 +21.4 0.00595475332 +21.45 0.0110042723 +21.5 0.0088275927 +21.55 0.0085331259 +21.6 0.0112022229 +21.65 0.013187011700000001 +21.7 0.012311595 +21.75 0.014569792 +21.8 0.011542762000000002 +21.85 0.008642928 +21.9 0.009160724 +21.95 0.011920991999999998 +22 0.016031087 +22.05 0.011982836 +22.1 0.014274553 +22.15 0.011680921 +22.2 0.012714633 +22.25 0.012044562 +22.3 0.011984049 +22.35 0.013086312 +22.4 0.012711046 +22.45 0.008765137 +22.5 0.00970292 +22.55 0.008576908 +22.6 0.007531957 +22.65 0.0024040748 +22.7 0.006489366999999999 +22.75 0.004043908 +22.8 0.004918999 +22.85 0.00257496432 +22.9 0.006215827 +22.95 0.0032124909 +23 0.0032666826 +23.05 0.00232206 +23.1 0.0004042440000000002 +23.15 0.001325337 +23.2 0.0049283109999999995 +23.25 0.005542567 +23.3 0.004622864 +23.35 0.004190548 +23.4 0.002201351 +23.45 0.007751841 +23.5 0.010841446999999999 +23.55 0.005454830999999999 +23.6 0.0072750010000000006 +23.65 0.010545243 +23.7 0.006581291 +23.75 0.011648177999999999 +23.8 0.009432959000000001 +23.85 0.010058507 +23.9 0.011098853 +23.95 0.010016969 +24 0.008368002000000001 +24.05 0.011732697 +24.1 0.00998608 +24.15 0.006431455 +24.2 0.012999377000000001 +24.25 0.008270788999999999 +24.3 0.011289344000000002 +24.35 0.0140801155 +24.4 0.011668428699999999 +24.45 0.0102436659 +24.5 0.008569418 +24.55 0.010014419 +24.6 0.013129811 +24.65 0.008585144 +24.7 0.013909223 +24.75 0.010949857 +24.8 0.006233117999999999 +24.85 0.007964121 +24.9 0.009637603 +24.95 0.0007959899999999999 +25 0.0031835413000000003 +25.05 0.005183545 +25.1 0.0029723814 +25.15 0.0029870438 +25.2 0.0016603930000000005 +25.25 0.00113803 +25.3 0.0029546999999999993 +25.35 0.0007570420000000003 +25.4 0.0014927630000000002 +25.45 0.0018362120000000003 +25.5 0.001429773 +25.55 0.00045611 +25.6 0.005269658 +25.65 0.005686952 +25.7 0.007263825999999999 +25.75 0.008246509999999999 +25.8 0.004178249 +25.85 0.001983591 +25.9 0.0008463703 +25.95 0.000529839 +26 0.005595454 +26.05 0.002519834 +26.1 0.003406492 +26.15 0.0061931405 +26.2 0.0069711598 +26.25 0.0080349407 +26.3 0.0070874727 +26.35 0.005358622 +26.4 0.007383547 +26.45 0.008228332 +26.5 0.007004195999999999 +26.55 0.006667747999999999 +26.6 0.0013171220000000002 +26.65 0.006830266 +26.7 0.005779018 +26.75 0.004071997 +26.8 0.006219921 +26.85 0.00499583 +26.9 0.00027471000000000006 +26.95 0.0021369993 +27 0.0023253207 +27.05 0.004693919 +27.1 0.006347135 +27.15 0.0019563199 +27.2 0.0041666329999999994 +27.25 0.0027645439000000003 +27.3 0.0025391873 +27.35 0.004200996 +27.4 0.0012432240000000002 +27.45 0.006589091 +27.5 0.006635554 +27.55 0.007910907 +27.6 0.005352198 +27.65 0.0012112985 +27.7 0.00022509799999999996 +27.75 0.0014098632 +27.8 0.004241174 +27.85 0.002989733 +27.9 0.0035277489999999997 +27.95 0.0022892779999999996 +28 0.0019021416999999998 +28.05 0.0020422398000000002 +28.1 0.0047234307 +28.15 0.0035752796999999996 +28.2 0.0038601367000000004 +28.25 0.0030690493400000003 +28.3 0.0022895863000000002 +28.35 0.005421504 +28.4 0.0034406104 +28.45 0.0010289472 +28.5 0.0011859117 +28.55 0.0022182332 +28.6 2.204689999999994e-05 +28.65 0.0014290287 +28.7 0.0023564031 +28.75 0.0015890118 +28.8 0.0046571449 +28.85 0.0053254148 +28.9 0.0037088945 +28.95 0.0009565528 +29 0.00259293274 +29.05 0.0006545504 +29.1 0.0009784364999999998 +29.15 0.0023309833999999997 +29.2 0.00042023790000000004 +29.25 0.003875334498 +29.3 0.00045073840000000003 +29.35 0.0042205681 +29.4 0.0074752596 +29.45 0.0066543592 +29.5 0.0039794717 +29.55 0.0052012009 +29.6 0.0050245781000000005 +29.65 0.0016873999 +29.7 0.002605181 +29.75 0.0025126225 +29.8 0.0018146177 +29.85 0.002664494 +29.9 0.0016564458 +29.95 0.0017259082 \ No newline at end of file diff --git a/tests/data/dissolve/compare/intvsext.dat b/tests/data/dissolve/compare/intvsext.dat new file mode 100644 index 0000000000..3ff24adcc6 --- /dev/null +++ b/tests/data/dissolve/compare/intvsext.dat @@ -0,0 +1,599 @@ +0.01 13.602864736195416 +0.060000000000000005 0.035033556960007206 +0.11 0.10833951130806949 +0.16000000000000003 0.06354862442385018 +0.21000000000000002 0.06302878125699748 +0.26 0.016339131374277616 +0.31000000000000005 0.02664588028746262 +0.36000000000000004 0.004513097748752526 +0.41000000000000003 0.020670506907289832 +0.46 0.018616826207360104 +0.51 0.011925187323138797 +0.56 0.0049121308774071615 +0.6100000000000001 0.001272775532647763 +0.66 0.011941087543069023 +0.7100000000000001 0.02844264155348139 +0.76 0.03911417325177774 +0.81 0.04787005678089046 +0.8600000000000001 0.04485619736338356 +0.91 0.04183187700423116 +0.9600000000000001 0.03431461452186045 +1.01 0.026452640349827326 +1.06 0.018200390587620578 +1.11 0.021021414687265372 +1.1600000000000001 0.05169468447110179 +1.2100000000000002 0.11411740465509596 +1.26 0.18910482346958724 +1.31 0.22696298705809104 +1.36 0.19229919753795333 +1.4100000000000001 0.11365022896353316 +1.4600000000000002 0.037344011078399186 +1.51 0.013329409677495963 +1.56 0.0350433610766866 +1.61 0.03633867566938624 +1.6600000000000001 0.0238323707521705 +1.7100000000000002 0.016004958841671135 +1.76 0.0002328081345119859 +1.81 0.008965866667455927 +1.86 0.01741174247946195 +1.9100000000000001 0.026358713397200972 +1.9600000000000002 0.027607521606134305 +2.01 0.03497033723697296 +2.06 0.04327767771667873 +2.11 0.043737423642790776 +2.1599999999999997 0.04051721313251666 +2.21 0.040210463252928985 +2.26 0.04372197087673982 +2.31 0.04281776318987714 +2.36 0.04357184469261477 +2.41 0.047031534574662484 +2.46 0.04922598706836681 +2.51 0.05307546779420054 +2.56 0.051882674928210394 +2.61 0.05468960046380705 +2.66 0.04970328344558081 +2.71 0.04590900282811214 +2.76 0.03211612534268793 +2.81 0.021641210595350523 +2.86 0.016603962151299362 +2.91 0.01309900654194789 +2.96 0.009260749425352252 +3.01 0.01200888747916936 +3.06 0.02295843426316918 +3.11 0.024706858481094454 +3.16 0.025570743293563902 +3.21 0.014994641136423778 +3.26 0.002118305406358316 +3.31 0.010753418391101995 +3.36 0.013863729979487309 +3.41 0.027974208809559555 +3.46 0.034366845959269254 +3.51 0.041268843077300714 +3.56 0.04651872506379399 +3.61 0.04613492046512607 +3.66 0.04852732011192354 +3.71 0.04909120266360148 +3.76 0.05263866455310637 +3.81 0.04525681032855838 +3.86 0.04134417352094555 +3.91 0.04288944322664677 +3.96 0.03572617550297463 +4.01 0.03756534542188443 +4.06 0.03704403647735757 +4.11 0.03826748865967303 +4.16 0.0425327679767576 +4.21 0.04168475720020659 +4.26 0.0368820593302174 +4.31 0.028071707802634627 +4.36 0.02497361169775638 +4.41 0.02378028091476198 +4.46 0.01778909821128998 +4.51 0.021729534581618592 +4.56 0.018492894549918905 +4.61 0.014213334888894367 +4.66 0.009627939231251895 +4.71 0.0059327303134897105 +4.76 0.003931446164941718 +4.8100000000000005 0.0012692153406391876 +4.86 0.0045797317908148755 +4.91 0.0046385882240466 +4.96 0.003377374886995327 +5.01 0.005250787150465036 +5.0600000000000005 0.0015423725774138392 +5.11 0.004799925788874282 +5.16 0.002018291693329316 +5.21 0.0023496912785714863 +5.26 0.0022189144675459904 +5.3100000000000005 0.0037864045468638807 +5.36 0.005249559941665705 +5.41 0.005034585663925201 +5.46 0.00226906998303035 +5.51 0.0026966999905676664 +5.5600000000000005 0.0020762707720291818 +5.61 0.0033259202271896104 +5.66 0.0007655638509228013 +5.71 0.0027405947098616407 +5.76 0.0008507191453836146 +5.8100000000000005 0.01220960110888461 +5.86 0.014139163752766706 +5.91 0.012680174714254915 +5.96 0.013077492877057544 +6.01 0.010361080630291392 +6.0600000000000005 0.02548460017983557 +6.11 0.024636721456781495 +6.16 0.016525411127986243 +6.21 0.023828764337234347 +6.26 0.020264678931289803 +6.3100000000000005 0.013982204969406532 +6.36 0.023135625011439617 +6.41 0.02349363498959343 +6.46 0.012070851971518567 +6.51 0.020443719537502736 +6.5600000000000005 0.011939340945374563 +6.61 0.010710321173627808 +6.66 0.00877027808468097 +6.71 0.019958793898687675 +6.76 0.013954408718471957 +6.8100000000000005 0.010026253407942477 +6.86 0.015298078782285299 +6.91 0.011957208428525337 +6.96 0.009674810228731005 +7.01 0.0021886360134996813 +7.0600000000000005 0.005595770652727636 +7.11 0.00538446156870076 +7.16 0.0027439256855053273 +7.21 0.0025765600559381485 +7.26 0.00047185836966477657 +7.3100000000000005 0.0032381862026673175 +7.36 0.002127324639907509 +7.41 0.009507714561877753 +7.46 0.0003205623636713881 +7.51 0.0031914222362299377 +7.5600000000000005 0.007370078298712529 +7.61 0.0009907144002998455 +7.66 0.003649053615192993 +7.71 0.0035813946725188686 +7.76 0.0040166827576537355 +7.8100000000000005 0.007170552230889544 +7.86 0.010256345570927625 +7.91 0.005606668793983648 +7.96 0.007118529083701508 +8.01 0.007194411328790279 +8.06 0.010249793946922832 +8.11 0.008231269567917691 +8.16 0.00962365465975534 +8.21 0.011045663772231259 +8.26 0.012071059306576318 +8.31 0.005965426063009869 +8.36 0.008346608103164895 +8.41 0.01082742911752494 +8.46 0.005355151454840191 +8.51 0.013406949624235389 +8.56 0.004186359914209884 +8.61 0.011276241699395867 +8.66 0.0074555783649615445 +8.71 0.008329185331283587 +8.76 0.008098421051726175 +8.81 0.014794397322327613 +8.86 0.01724023066799077 +8.91 0.014579628917535645 +8.96 0.011306163763003533 +9.01 0.010085595489110463 +9.06 0.0057314812880847 +9.11 0.01673331152208533 +9.16 0.011210200870699846 +9.21 0.011036146945326954 +9.26 0.008311735474090106 +9.31 0.00981131999157279 +9.36 0.014255532051027092 +9.41 0.006188792093816807 +9.46 0.002617423243813666 +9.51 0.011043435778990118 +9.56 0.00533870095925401 +9.610000000000001 0.005360587327942805 +9.66 0.009960989922882805 +9.71 0.008799519863839103 +9.76 0.009718075426622579 +9.81 0.006966375929566247 +9.860000000000001 0.004899924711236851 +9.91 0.0019384145783674853 +9.96 0.0050855416753940366 +10.01 0.0006761104812520805 +10.06 0.004415791825785394 +10.110000000000001 0.004378348331430838 +10.16 0.006510293727028285 +10.21 0.0011833534795770533 +10.26 0.01759934039890243 +10.31 0.012329916788550072 +10.360000000000001 0.01269117270182189 +10.41 0.009884864458167497 +10.46 0.016205223393603828 +10.51 0.02002758787897749 +10.56 0.021629587559051894 +10.610000000000001 0.022171518730399666 +10.66 0.02079161777472942 +10.71 0.024162215710763366 +10.76 0.021656288824045657 +10.81 0.01648778053013608 +10.860000000000001 0.028190870291780988 +10.91 0.01798109662745956 +10.96 0.021149981148878675 +11.01 0.02573664086728323 +11.06 0.013107854809875533 +11.110000000000001 0.022414480581444376 +11.16 0.021621442220899213 +11.21 0.016191424048268052 +11.26 0.018182686310693176 +11.31 0.014368440518071091 +11.360000000000001 0.01822009224593193 +11.41 0.009277133168117839 +11.46 0.0057949156575152734 +11.51 0.014616114332273342 +11.56 0.009180633563430858 +11.610000000000001 0.00709880192176973 +11.66 0.007578116363399995 +11.71 0.0031642158545820524 +11.76 0.001665608009875471 +11.81 0.0030636619403154423 +11.860000000000001 0.001691337920548909 +11.91 0.001461179846676871 +11.96 0.0032240265091535297 +12.01 0.002148711453780039 +12.06 0.0024229642886421585 +12.110000000000001 0.012807668921781132 +12.16 0.00032909782109387387 +12.21 0.007224146256642604 +12.26 0.003498889952253198 +12.31 0.004975906351125513 +12.360000000000001 0.005687249669766935 +12.41 0.004896896619105619 +12.46 0.012507962245946636 +12.51 0.00943018801107412 +12.56 0.021565203162490287 +12.610000000000001 0.01265862649475184 +12.66 0.011596936108958823 +12.71 0.018888562999404224 +12.76 0.019502788286403325 +12.81 0.021604047605270613 +12.860000000000001 0.03246050720324263 +12.91 0.019707133015633937 +12.96 0.018932276832148282 +13.01 0.023039633530328778 +13.06 0.0223975945982975 +13.110000000000001 0.020925306685318813 +13.16 0.020361321491420867 +13.21 0.013496667716044077 +13.26 0.02021905726346966 +13.31 0.013426440586680308 +13.360000000000001 0.023859409933629366 +13.41 0.009115775006818806 +13.46 0.008319250813973778 +13.51 0.014478924906356706 +13.56 0.013847320425182325 +13.610000000000001 0.011922526355394869 +13.66 0.005712230702688642 +13.71 0.004224087281321887 +13.76 0.010788778989467536 +13.81 0.0004907989971852621 +13.860000000000001 0.0008622620544054445 +13.91 0.0013090559681797342 +13.96 0.004580612775095309 +14.01 0.004125736944545454 +14.06 0.0022315242743405246 +14.110000000000001 0.000686429833201916 +14.16 0.0069720908882909376 +14.21 0.006289613241430736 +14.26 0.0066980250372682335 +14.31 0.006270200859165478 +14.360000000000001 0.004875932659613228 +14.41 0.008607463520068446 +14.46 0.005546773602767653 +14.51 0.015966512798547224 +14.56 0.005146358339296176 +14.610000000000001 0.014907936317054648 +14.66 0.009347800069271873 +14.71 0.0067676895705624344 +14.76 0.00032606374600420256 +14.81 0.0035818791097213475 +14.860000000000001 0.007074230983065374 +14.91 0.002769834654401316 +14.96 0.0035892091911505896 +15.01 0.002794483485854155 +15.06 0.00030039602834430203 +15.110000000000001 0.008200064508122086 +15.16 0.0074495093246571015 +15.21 0.0020785458762042886 +15.26 0.005056945376557269 +15.31 0.005723670839071628 +15.360000000000001 0.0032456313638966356 +15.41 0.00918954623035961 +15.46 0.004301289224759275 +15.51 0.004898153889095148 +15.56 0.011169925191309198 +15.610000000000001 0.006745534744917485 +15.66 0.006868021263759435 +15.71 0.0063460075397035395 +15.76 0.014373443817836977 +15.81 0.0047142010378745935 +15.860000000000001 0.008900039670717317 +15.91 0.011119566554754684 +15.96 0.016732046388220588 +16.01 0.014475737762982168 +16.060000000000002 0.008589239270896769 +16.110000000000003 0.008570857045461573 +16.160000000000004 0.010644615676138612 +16.21 0.01098271137390101 +16.26 0.0037851876537728533 +16.310000000000002 0.00920004711138895 +16.360000000000003 0.010432204523467914 +16.410000000000004 0.0061296998860700894 +16.46 0.008480028877833267 +16.51 0.006566260330610186 +16.560000000000002 0.010147920119502104 +16.610000000000003 0.00561919085297998 +16.660000000000004 0.007527113081355701 +16.71 0.006403485698439293 +16.76 0.011863567601222272 +16.810000000000002 0.00266255894336868 +16.860000000000003 0.0012756936179034012 +16.910000000000004 0.001748368708386813 +16.96 0.0011291076863307404 +17.01 0.0053424290500156514 +17.060000000000002 0.005941976698177425 +17.110000000000003 0.0051736690802213 +17.160000000000004 0.0021305356596875305 +17.21 0.009174194367026366 +17.26 0.011936038746707161 +17.310000000000002 0.014160260346089616 +17.360000000000003 0.0059711981420648656 +17.410000000000004 0.00042221769298496706 +17.46 0.012543614787035445 +17.51 0.013860529590008226 +17.560000000000002 0.003609593068993463 +17.610000000000003 0.011006707705243065 +17.660000000000004 0.012209593569041473 +17.71 0.008339396120140802 +17.76 0.010470235339028343 +17.810000000000002 0.005866714892858458 +17.860000000000003 0.014313145889914213 +17.910000000000004 0.009845529234907384 +17.96 0.007893732596711726 +18.01 0.012574143768177228 +18.060000000000002 0.0028362045461970526 +18.110000000000003 0.003651893450449189 +18.160000000000004 0.006147604555873614 +18.21 0.005173549716659193 +18.26 0.003504206835237598 +18.310000000000002 0.0071768045533218945 +18.360000000000003 0.005421233759956785 +18.410000000000004 0.0067007998830716994 +18.46 0.007771365544382399 +18.51 0.0010757384021301778 +18.560000000000002 0.003474451687218696 +18.610000000000003 0.003077351798880878 +18.660000000000004 0.0019079751740131547 +18.71 0.003494794256402053 +18.76 0.001679808431217433 +18.810000000000002 0.0005259972068864501 +18.860000000000003 0.005036212543507852 +18.910000000000004 0.003983919261921371 +18.96 0.0014818467730838359 +19.01 0.004962860392158496 +19.060000000000002 0.0015772867130150123 +19.110000000000003 0.0009261934539085605 +19.160000000000004 0.007053913629806118 +19.210000000000004 0.010927609133298372 +19.26 0.0014653929860302898 +19.310000000000002 0.005332840170746066 +19.360000000000003 0.0029656442415449185 +19.410000000000004 0.004753633733856996 +19.460000000000004 0.008149160096279875 +19.51 0.005716053826952233 +19.560000000000002 0.0019646082617094944 +19.610000000000003 0.002545870144710298 +19.660000000000004 0.0006987396348636834 +19.710000000000004 0.0008475053206705347 +19.76 0.005300141195751468 +19.810000000000002 0.00363372348470255 +19.860000000000003 0.0019458212922952123 +19.910000000000004 0.0012757740574278736 +19.960000000000004 0.003981393667522447 +20.01 0.00205134101022879 +20.060000000000002 0.0007318652295530267 +20.110000000000003 0.0033526153081139404 +20.160000000000004 0.0058873972666695406 +20.210000000000004 0.001862386484839963 +20.26 0.004366944291721997 +20.310000000000002 0.005119994985794376 +20.360000000000003 0.009730784280490064 +20.410000000000004 0.0058682603276100555 +20.460000000000004 0.0033642511042212457 +20.51 0.009870993549335412 +20.560000000000002 0.0005297512518476831 +20.610000000000003 0.010368428558656207 +20.660000000000004 0.0027522266792380685 +20.710000000000004 0.0018563696616977993 +20.76 0.003656541160898394 +20.810000000000002 0.004178438130731609 +20.860000000000003 0.005971098803655161 +20.910000000000004 0.0030102284299370957 +20.960000000000004 0.006631112596547669 +21.01 0.0038496838915100936 +21.060000000000002 0.004043424469354558 +21.110000000000003 0.002134557786297145 +21.160000000000004 0.0014500780781610584 +21.210000000000004 0.005533153859546245 +21.26 0.006637096504552975 +21.310000000000002 0.0036878619055687517 +21.360000000000003 0.0015773109660117716 +21.410000000000004 0.007171273479938314 +21.460000000000004 0.00010573951142188455 +21.51 0.005359787332971899 +21.560000000000002 0.007584726692758195 +21.610000000000003 0.0017987922187947228 +21.660000000000004 0.0028796878302534664 +21.710000000000004 0.0003941124990757131 +21.76 0.005553170438268423 +21.810000000000002 0.005995539749386457 +21.860000000000003 0.0022768995096451947 +21.910000000000004 0.00091964846888406 +21.960000000000004 0.00370071287448039 +22.01 0.003608394255945538 +22.060000000000002 0.004717842657597893 +22.110000000000003 0.005035921380028846 +22.160000000000004 0.0010132595167231526 +22.210000000000004 0.006477375962822929 +22.26 0.0030545803471505846 +22.310000000000002 0.0051850632412505735 +22.360000000000003 0.002713307973113438 +22.410000000000004 0.0004572956596669631 +22.460000000000004 0.0017767826603784255 +22.51 0.005041945282946729 +22.560000000000002 0.0013638282867504121 +22.610000000000003 0.000697696129290654 +22.660000000000004 0.004194621649862009 +22.710000000000004 0.007921188336996064 +22.76 0.00956019654790016 +22.810000000000002 0.0013296560222385108 +22.860000000000003 0.0020528811898921496 +22.910000000000004 0.001591633204302037 +22.960000000000004 0.0004809632476872375 +23.01 0.0010755098923963629 +23.060000000000002 0.004365491991168221 +23.110000000000003 0.0038260435573047617 +23.160000000000004 0.006838528841717931 +23.210000000000004 0.011803168805401587 +23.26 0.006617805106912408 +23.310000000000002 0.0020996197396572366 +23.360000000000003 0.006699570076806124 +23.410000000000004 0.00639498692153359 +23.460000000000004 0.004304179755199856 +23.51 0.005772205164503493 +23.560000000000002 0.014120847262629313 +23.610000000000003 0.011689518512498597 +23.660000000000004 0.0012980400770460443 +23.710000000000004 0.0038930904596947784 +23.76 0.009728960804446964 +23.810000000000002 0.00828913011666642 +23.860000000000003 0.007855102739966776 +23.910000000000004 0.0024236270466846126 +23.960000000000004 0.003766828357622206 +24.01 0.0015137857303021158 +24.060000000000002 0.0015068223358060307 +24.110000000000003 0.00046173239384108373 +24.160000000000004 0.002843676437016396 +24.210000000000004 1.9129357669178454e-05 +24.26 0.00206031798859404 +24.310000000000002 0.0012317738158366277 +24.360000000000003 0.0044362025776901555 +24.410000000000004 0.001329675122975743 +24.460000000000004 0.0026117115765297164 +24.51 0.0014631156827780646 +24.560000000000002 0.0013230861041076476 +24.610000000000003 0.004942676891412367 +24.660000000000004 0.0006531893607049587 +24.710000000000004 0.00016464786391815617 +24.76 0.007357493951245762 +24.810000000000002 0.004048300927559238 +24.860000000000003 0.006772720677219622 +24.910000000000004 0.006164469135686626 +24.960000000000004 0.0046608678738523784 +25.01 0.004875497671623455 +25.060000000000002 0.0010240620871833522 +25.110000000000003 0.003415795546931703 +25.160000000000004 0.003117644079616496 +25.210000000000004 0.00184192269432878 +25.26 0.001411499435531237 +25.310000000000002 0.002612485409201784 +25.360000000000003 0.0025260615041144594 +25.410000000000004 0.0014994228431538833 +25.460000000000004 0.0006560085476025827 +25.51 0.004688102935161918 +25.560000000000002 0.006136912771648051 +25.610000000000003 0.0021974392027350265 +25.660000000000004 0.003224168605070803 +25.710000000000004 0.010107915621035076 +25.76 0.008792550161262465 +25.810000000000002 0.005743300886406658 +25.860000000000003 0.004224795718017102 +25.910000000000004 0.003119864433089474 +25.960000000000004 0.010055603593221912 +26.01 0.006431935640240944 +26.060000000000002 0.009797464235474014 +26.110000000000003 0.008554372410334039 +26.160000000000004 0.005808564195478792 +26.210000000000004 0.0017483394294555941 +26.26 0.006177594700869473 +26.310000000000002 0.013183449389835341 +26.360000000000003 0.009368411154564455 +26.410000000000004 0.007159664703656666 +26.460000000000004 0.007864853026460134 +26.51 0.008597601444931992 +26.560000000000002 0.00365433119985373 +26.610000000000003 0.002514023373989213 +26.660000000000004 0.004147541048731116 +26.710000000000004 0.009923834134752634 +26.76 0.009753722367742027 +26.810000000000002 0.01034827966922832 +26.860000000000003 0.0007547936140257475 +26.910000000000004 0.0008005634788841134 +26.960000000000004 0.005834450309495217 +27.01 0.0014661043731860515 +27.060000000000002 0.007545323385782318 +27.110000000000003 0.0010069262205654231 +27.160000000000004 0.003694114108784628 +27.210000000000004 0.002915608738517916 +27.26 0.006688624205405263 +27.310000000000002 0.007045251974393098 +27.360000000000003 0.003974186114562003 +27.410000000000004 0.0050822797499005395 +27.460000000000004 0.00400214877341173 +27.51 0.0073941274635738 +27.560000000000002 0.009839740149169388 +27.610000000000003 0.008215611954023889 +27.660000000000004 0.00812390650415449 +27.710000000000004 0.0026739538366103268 +27.76 0.00022062794209454098 +27.810000000000002 0.0029410417129714502 +27.860000000000003 0.006653690568687193 +27.910000000000004 0.008024565269625358 +27.960000000000004 0.007696955857740324 +28.01 0.0070377731253631285 +28.060000000000002 0.0076518857740038755 +28.110000000000003 0.0076269129576990525 +28.160000000000004 0.011107449618969995 +28.210000000000004 0.009010658180124715 +28.26 0.015282283567905326 +28.310000000000002 0.006976214549766831 +28.360000000000003 0.005985834430836702 +28.410000000000004 0.0006204350342589289 +28.460000000000004 0.004968056087811945 +28.51 0.01069906072327566 +28.560000000000002 0.005230558227039795 +28.610000000000003 0.0027126373133174245 +28.660000000000004 0.0047773883322962335 +28.710000000000004 0.004786979573217508 +28.76 0.005347646634994841 +28.810000000000002 0.0020362490175422797 +28.860000000000003 0.0018708822099803462 +28.910000000000004 0.006262346010579821 +28.960000000000004 0.002113549738305402 +29.01 0.007740180437059261 +29.060000000000002 0.0049794436747087325 +29.110000000000003 0.0014732501981752067 +29.160000000000004 0.005987419617214626 +29.210000000000004 0.002650993127916284 +29.26 0.003622576584045665 +29.310000000000002 0.0056426792787837195 +29.360000000000003 0.006917579291528027 +29.410000000000004 0.001631262003070526 +29.460000000000004 0.0012716600013443797 +29.51 0.0016168618876284762 +29.560000000000002 0.009573827600319055 +29.610000000000003 0.0059203677090907195 +29.660000000000004 0.00015311723097776505 +29.710000000000004 0.0018487164375394888 +29.76 0.0035992946452436214 +29.810000000000002 0.006623353330732501 +29.860000000000003 0.0017657403942469728 +29.910000000000004 0.0035674584459472803 \ No newline at end of file diff --git a/tests/data/dissolve/compare/intvsint.dat b/tests/data/dissolve/compare/intvsint.dat new file mode 100644 index 0000000000..3ff24adcc6 --- /dev/null +++ b/tests/data/dissolve/compare/intvsint.dat @@ -0,0 +1,599 @@ +0.01 13.602864736195416 +0.060000000000000005 0.035033556960007206 +0.11 0.10833951130806949 +0.16000000000000003 0.06354862442385018 +0.21000000000000002 0.06302878125699748 +0.26 0.016339131374277616 +0.31000000000000005 0.02664588028746262 +0.36000000000000004 0.004513097748752526 +0.41000000000000003 0.020670506907289832 +0.46 0.018616826207360104 +0.51 0.011925187323138797 +0.56 0.0049121308774071615 +0.6100000000000001 0.001272775532647763 +0.66 0.011941087543069023 +0.7100000000000001 0.02844264155348139 +0.76 0.03911417325177774 +0.81 0.04787005678089046 +0.8600000000000001 0.04485619736338356 +0.91 0.04183187700423116 +0.9600000000000001 0.03431461452186045 +1.01 0.026452640349827326 +1.06 0.018200390587620578 +1.11 0.021021414687265372 +1.1600000000000001 0.05169468447110179 +1.2100000000000002 0.11411740465509596 +1.26 0.18910482346958724 +1.31 0.22696298705809104 +1.36 0.19229919753795333 +1.4100000000000001 0.11365022896353316 +1.4600000000000002 0.037344011078399186 +1.51 0.013329409677495963 +1.56 0.0350433610766866 +1.61 0.03633867566938624 +1.6600000000000001 0.0238323707521705 +1.7100000000000002 0.016004958841671135 +1.76 0.0002328081345119859 +1.81 0.008965866667455927 +1.86 0.01741174247946195 +1.9100000000000001 0.026358713397200972 +1.9600000000000002 0.027607521606134305 +2.01 0.03497033723697296 +2.06 0.04327767771667873 +2.11 0.043737423642790776 +2.1599999999999997 0.04051721313251666 +2.21 0.040210463252928985 +2.26 0.04372197087673982 +2.31 0.04281776318987714 +2.36 0.04357184469261477 +2.41 0.047031534574662484 +2.46 0.04922598706836681 +2.51 0.05307546779420054 +2.56 0.051882674928210394 +2.61 0.05468960046380705 +2.66 0.04970328344558081 +2.71 0.04590900282811214 +2.76 0.03211612534268793 +2.81 0.021641210595350523 +2.86 0.016603962151299362 +2.91 0.01309900654194789 +2.96 0.009260749425352252 +3.01 0.01200888747916936 +3.06 0.02295843426316918 +3.11 0.024706858481094454 +3.16 0.025570743293563902 +3.21 0.014994641136423778 +3.26 0.002118305406358316 +3.31 0.010753418391101995 +3.36 0.013863729979487309 +3.41 0.027974208809559555 +3.46 0.034366845959269254 +3.51 0.041268843077300714 +3.56 0.04651872506379399 +3.61 0.04613492046512607 +3.66 0.04852732011192354 +3.71 0.04909120266360148 +3.76 0.05263866455310637 +3.81 0.04525681032855838 +3.86 0.04134417352094555 +3.91 0.04288944322664677 +3.96 0.03572617550297463 +4.01 0.03756534542188443 +4.06 0.03704403647735757 +4.11 0.03826748865967303 +4.16 0.0425327679767576 +4.21 0.04168475720020659 +4.26 0.0368820593302174 +4.31 0.028071707802634627 +4.36 0.02497361169775638 +4.41 0.02378028091476198 +4.46 0.01778909821128998 +4.51 0.021729534581618592 +4.56 0.018492894549918905 +4.61 0.014213334888894367 +4.66 0.009627939231251895 +4.71 0.0059327303134897105 +4.76 0.003931446164941718 +4.8100000000000005 0.0012692153406391876 +4.86 0.0045797317908148755 +4.91 0.0046385882240466 +4.96 0.003377374886995327 +5.01 0.005250787150465036 +5.0600000000000005 0.0015423725774138392 +5.11 0.004799925788874282 +5.16 0.002018291693329316 +5.21 0.0023496912785714863 +5.26 0.0022189144675459904 +5.3100000000000005 0.0037864045468638807 +5.36 0.005249559941665705 +5.41 0.005034585663925201 +5.46 0.00226906998303035 +5.51 0.0026966999905676664 +5.5600000000000005 0.0020762707720291818 +5.61 0.0033259202271896104 +5.66 0.0007655638509228013 +5.71 0.0027405947098616407 +5.76 0.0008507191453836146 +5.8100000000000005 0.01220960110888461 +5.86 0.014139163752766706 +5.91 0.012680174714254915 +5.96 0.013077492877057544 +6.01 0.010361080630291392 +6.0600000000000005 0.02548460017983557 +6.11 0.024636721456781495 +6.16 0.016525411127986243 +6.21 0.023828764337234347 +6.26 0.020264678931289803 +6.3100000000000005 0.013982204969406532 +6.36 0.023135625011439617 +6.41 0.02349363498959343 +6.46 0.012070851971518567 +6.51 0.020443719537502736 +6.5600000000000005 0.011939340945374563 +6.61 0.010710321173627808 +6.66 0.00877027808468097 +6.71 0.019958793898687675 +6.76 0.013954408718471957 +6.8100000000000005 0.010026253407942477 +6.86 0.015298078782285299 +6.91 0.011957208428525337 +6.96 0.009674810228731005 +7.01 0.0021886360134996813 +7.0600000000000005 0.005595770652727636 +7.11 0.00538446156870076 +7.16 0.0027439256855053273 +7.21 0.0025765600559381485 +7.26 0.00047185836966477657 +7.3100000000000005 0.0032381862026673175 +7.36 0.002127324639907509 +7.41 0.009507714561877753 +7.46 0.0003205623636713881 +7.51 0.0031914222362299377 +7.5600000000000005 0.007370078298712529 +7.61 0.0009907144002998455 +7.66 0.003649053615192993 +7.71 0.0035813946725188686 +7.76 0.0040166827576537355 +7.8100000000000005 0.007170552230889544 +7.86 0.010256345570927625 +7.91 0.005606668793983648 +7.96 0.007118529083701508 +8.01 0.007194411328790279 +8.06 0.010249793946922832 +8.11 0.008231269567917691 +8.16 0.00962365465975534 +8.21 0.011045663772231259 +8.26 0.012071059306576318 +8.31 0.005965426063009869 +8.36 0.008346608103164895 +8.41 0.01082742911752494 +8.46 0.005355151454840191 +8.51 0.013406949624235389 +8.56 0.004186359914209884 +8.61 0.011276241699395867 +8.66 0.0074555783649615445 +8.71 0.008329185331283587 +8.76 0.008098421051726175 +8.81 0.014794397322327613 +8.86 0.01724023066799077 +8.91 0.014579628917535645 +8.96 0.011306163763003533 +9.01 0.010085595489110463 +9.06 0.0057314812880847 +9.11 0.01673331152208533 +9.16 0.011210200870699846 +9.21 0.011036146945326954 +9.26 0.008311735474090106 +9.31 0.00981131999157279 +9.36 0.014255532051027092 +9.41 0.006188792093816807 +9.46 0.002617423243813666 +9.51 0.011043435778990118 +9.56 0.00533870095925401 +9.610000000000001 0.005360587327942805 +9.66 0.009960989922882805 +9.71 0.008799519863839103 +9.76 0.009718075426622579 +9.81 0.006966375929566247 +9.860000000000001 0.004899924711236851 +9.91 0.0019384145783674853 +9.96 0.0050855416753940366 +10.01 0.0006761104812520805 +10.06 0.004415791825785394 +10.110000000000001 0.004378348331430838 +10.16 0.006510293727028285 +10.21 0.0011833534795770533 +10.26 0.01759934039890243 +10.31 0.012329916788550072 +10.360000000000001 0.01269117270182189 +10.41 0.009884864458167497 +10.46 0.016205223393603828 +10.51 0.02002758787897749 +10.56 0.021629587559051894 +10.610000000000001 0.022171518730399666 +10.66 0.02079161777472942 +10.71 0.024162215710763366 +10.76 0.021656288824045657 +10.81 0.01648778053013608 +10.860000000000001 0.028190870291780988 +10.91 0.01798109662745956 +10.96 0.021149981148878675 +11.01 0.02573664086728323 +11.06 0.013107854809875533 +11.110000000000001 0.022414480581444376 +11.16 0.021621442220899213 +11.21 0.016191424048268052 +11.26 0.018182686310693176 +11.31 0.014368440518071091 +11.360000000000001 0.01822009224593193 +11.41 0.009277133168117839 +11.46 0.0057949156575152734 +11.51 0.014616114332273342 +11.56 0.009180633563430858 +11.610000000000001 0.00709880192176973 +11.66 0.007578116363399995 +11.71 0.0031642158545820524 +11.76 0.001665608009875471 +11.81 0.0030636619403154423 +11.860000000000001 0.001691337920548909 +11.91 0.001461179846676871 +11.96 0.0032240265091535297 +12.01 0.002148711453780039 +12.06 0.0024229642886421585 +12.110000000000001 0.012807668921781132 +12.16 0.00032909782109387387 +12.21 0.007224146256642604 +12.26 0.003498889952253198 +12.31 0.004975906351125513 +12.360000000000001 0.005687249669766935 +12.41 0.004896896619105619 +12.46 0.012507962245946636 +12.51 0.00943018801107412 +12.56 0.021565203162490287 +12.610000000000001 0.01265862649475184 +12.66 0.011596936108958823 +12.71 0.018888562999404224 +12.76 0.019502788286403325 +12.81 0.021604047605270613 +12.860000000000001 0.03246050720324263 +12.91 0.019707133015633937 +12.96 0.018932276832148282 +13.01 0.023039633530328778 +13.06 0.0223975945982975 +13.110000000000001 0.020925306685318813 +13.16 0.020361321491420867 +13.21 0.013496667716044077 +13.26 0.02021905726346966 +13.31 0.013426440586680308 +13.360000000000001 0.023859409933629366 +13.41 0.009115775006818806 +13.46 0.008319250813973778 +13.51 0.014478924906356706 +13.56 0.013847320425182325 +13.610000000000001 0.011922526355394869 +13.66 0.005712230702688642 +13.71 0.004224087281321887 +13.76 0.010788778989467536 +13.81 0.0004907989971852621 +13.860000000000001 0.0008622620544054445 +13.91 0.0013090559681797342 +13.96 0.004580612775095309 +14.01 0.004125736944545454 +14.06 0.0022315242743405246 +14.110000000000001 0.000686429833201916 +14.16 0.0069720908882909376 +14.21 0.006289613241430736 +14.26 0.0066980250372682335 +14.31 0.006270200859165478 +14.360000000000001 0.004875932659613228 +14.41 0.008607463520068446 +14.46 0.005546773602767653 +14.51 0.015966512798547224 +14.56 0.005146358339296176 +14.610000000000001 0.014907936317054648 +14.66 0.009347800069271873 +14.71 0.0067676895705624344 +14.76 0.00032606374600420256 +14.81 0.0035818791097213475 +14.860000000000001 0.007074230983065374 +14.91 0.002769834654401316 +14.96 0.0035892091911505896 +15.01 0.002794483485854155 +15.06 0.00030039602834430203 +15.110000000000001 0.008200064508122086 +15.16 0.0074495093246571015 +15.21 0.0020785458762042886 +15.26 0.005056945376557269 +15.31 0.005723670839071628 +15.360000000000001 0.0032456313638966356 +15.41 0.00918954623035961 +15.46 0.004301289224759275 +15.51 0.004898153889095148 +15.56 0.011169925191309198 +15.610000000000001 0.006745534744917485 +15.66 0.006868021263759435 +15.71 0.0063460075397035395 +15.76 0.014373443817836977 +15.81 0.0047142010378745935 +15.860000000000001 0.008900039670717317 +15.91 0.011119566554754684 +15.96 0.016732046388220588 +16.01 0.014475737762982168 +16.060000000000002 0.008589239270896769 +16.110000000000003 0.008570857045461573 +16.160000000000004 0.010644615676138612 +16.21 0.01098271137390101 +16.26 0.0037851876537728533 +16.310000000000002 0.00920004711138895 +16.360000000000003 0.010432204523467914 +16.410000000000004 0.0061296998860700894 +16.46 0.008480028877833267 +16.51 0.006566260330610186 +16.560000000000002 0.010147920119502104 +16.610000000000003 0.00561919085297998 +16.660000000000004 0.007527113081355701 +16.71 0.006403485698439293 +16.76 0.011863567601222272 +16.810000000000002 0.00266255894336868 +16.860000000000003 0.0012756936179034012 +16.910000000000004 0.001748368708386813 +16.96 0.0011291076863307404 +17.01 0.0053424290500156514 +17.060000000000002 0.005941976698177425 +17.110000000000003 0.0051736690802213 +17.160000000000004 0.0021305356596875305 +17.21 0.009174194367026366 +17.26 0.011936038746707161 +17.310000000000002 0.014160260346089616 +17.360000000000003 0.0059711981420648656 +17.410000000000004 0.00042221769298496706 +17.46 0.012543614787035445 +17.51 0.013860529590008226 +17.560000000000002 0.003609593068993463 +17.610000000000003 0.011006707705243065 +17.660000000000004 0.012209593569041473 +17.71 0.008339396120140802 +17.76 0.010470235339028343 +17.810000000000002 0.005866714892858458 +17.860000000000003 0.014313145889914213 +17.910000000000004 0.009845529234907384 +17.96 0.007893732596711726 +18.01 0.012574143768177228 +18.060000000000002 0.0028362045461970526 +18.110000000000003 0.003651893450449189 +18.160000000000004 0.006147604555873614 +18.21 0.005173549716659193 +18.26 0.003504206835237598 +18.310000000000002 0.0071768045533218945 +18.360000000000003 0.005421233759956785 +18.410000000000004 0.0067007998830716994 +18.46 0.007771365544382399 +18.51 0.0010757384021301778 +18.560000000000002 0.003474451687218696 +18.610000000000003 0.003077351798880878 +18.660000000000004 0.0019079751740131547 +18.71 0.003494794256402053 +18.76 0.001679808431217433 +18.810000000000002 0.0005259972068864501 +18.860000000000003 0.005036212543507852 +18.910000000000004 0.003983919261921371 +18.96 0.0014818467730838359 +19.01 0.004962860392158496 +19.060000000000002 0.0015772867130150123 +19.110000000000003 0.0009261934539085605 +19.160000000000004 0.007053913629806118 +19.210000000000004 0.010927609133298372 +19.26 0.0014653929860302898 +19.310000000000002 0.005332840170746066 +19.360000000000003 0.0029656442415449185 +19.410000000000004 0.004753633733856996 +19.460000000000004 0.008149160096279875 +19.51 0.005716053826952233 +19.560000000000002 0.0019646082617094944 +19.610000000000003 0.002545870144710298 +19.660000000000004 0.0006987396348636834 +19.710000000000004 0.0008475053206705347 +19.76 0.005300141195751468 +19.810000000000002 0.00363372348470255 +19.860000000000003 0.0019458212922952123 +19.910000000000004 0.0012757740574278736 +19.960000000000004 0.003981393667522447 +20.01 0.00205134101022879 +20.060000000000002 0.0007318652295530267 +20.110000000000003 0.0033526153081139404 +20.160000000000004 0.0058873972666695406 +20.210000000000004 0.001862386484839963 +20.26 0.004366944291721997 +20.310000000000002 0.005119994985794376 +20.360000000000003 0.009730784280490064 +20.410000000000004 0.0058682603276100555 +20.460000000000004 0.0033642511042212457 +20.51 0.009870993549335412 +20.560000000000002 0.0005297512518476831 +20.610000000000003 0.010368428558656207 +20.660000000000004 0.0027522266792380685 +20.710000000000004 0.0018563696616977993 +20.76 0.003656541160898394 +20.810000000000002 0.004178438130731609 +20.860000000000003 0.005971098803655161 +20.910000000000004 0.0030102284299370957 +20.960000000000004 0.006631112596547669 +21.01 0.0038496838915100936 +21.060000000000002 0.004043424469354558 +21.110000000000003 0.002134557786297145 +21.160000000000004 0.0014500780781610584 +21.210000000000004 0.005533153859546245 +21.26 0.006637096504552975 +21.310000000000002 0.0036878619055687517 +21.360000000000003 0.0015773109660117716 +21.410000000000004 0.007171273479938314 +21.460000000000004 0.00010573951142188455 +21.51 0.005359787332971899 +21.560000000000002 0.007584726692758195 +21.610000000000003 0.0017987922187947228 +21.660000000000004 0.0028796878302534664 +21.710000000000004 0.0003941124990757131 +21.76 0.005553170438268423 +21.810000000000002 0.005995539749386457 +21.860000000000003 0.0022768995096451947 +21.910000000000004 0.00091964846888406 +21.960000000000004 0.00370071287448039 +22.01 0.003608394255945538 +22.060000000000002 0.004717842657597893 +22.110000000000003 0.005035921380028846 +22.160000000000004 0.0010132595167231526 +22.210000000000004 0.006477375962822929 +22.26 0.0030545803471505846 +22.310000000000002 0.0051850632412505735 +22.360000000000003 0.002713307973113438 +22.410000000000004 0.0004572956596669631 +22.460000000000004 0.0017767826603784255 +22.51 0.005041945282946729 +22.560000000000002 0.0013638282867504121 +22.610000000000003 0.000697696129290654 +22.660000000000004 0.004194621649862009 +22.710000000000004 0.007921188336996064 +22.76 0.00956019654790016 +22.810000000000002 0.0013296560222385108 +22.860000000000003 0.0020528811898921496 +22.910000000000004 0.001591633204302037 +22.960000000000004 0.0004809632476872375 +23.01 0.0010755098923963629 +23.060000000000002 0.004365491991168221 +23.110000000000003 0.0038260435573047617 +23.160000000000004 0.006838528841717931 +23.210000000000004 0.011803168805401587 +23.26 0.006617805106912408 +23.310000000000002 0.0020996197396572366 +23.360000000000003 0.006699570076806124 +23.410000000000004 0.00639498692153359 +23.460000000000004 0.004304179755199856 +23.51 0.005772205164503493 +23.560000000000002 0.014120847262629313 +23.610000000000003 0.011689518512498597 +23.660000000000004 0.0012980400770460443 +23.710000000000004 0.0038930904596947784 +23.76 0.009728960804446964 +23.810000000000002 0.00828913011666642 +23.860000000000003 0.007855102739966776 +23.910000000000004 0.0024236270466846126 +23.960000000000004 0.003766828357622206 +24.01 0.0015137857303021158 +24.060000000000002 0.0015068223358060307 +24.110000000000003 0.00046173239384108373 +24.160000000000004 0.002843676437016396 +24.210000000000004 1.9129357669178454e-05 +24.26 0.00206031798859404 +24.310000000000002 0.0012317738158366277 +24.360000000000003 0.0044362025776901555 +24.410000000000004 0.001329675122975743 +24.460000000000004 0.0026117115765297164 +24.51 0.0014631156827780646 +24.560000000000002 0.0013230861041076476 +24.610000000000003 0.004942676891412367 +24.660000000000004 0.0006531893607049587 +24.710000000000004 0.00016464786391815617 +24.76 0.007357493951245762 +24.810000000000002 0.004048300927559238 +24.860000000000003 0.006772720677219622 +24.910000000000004 0.006164469135686626 +24.960000000000004 0.0046608678738523784 +25.01 0.004875497671623455 +25.060000000000002 0.0010240620871833522 +25.110000000000003 0.003415795546931703 +25.160000000000004 0.003117644079616496 +25.210000000000004 0.00184192269432878 +25.26 0.001411499435531237 +25.310000000000002 0.002612485409201784 +25.360000000000003 0.0025260615041144594 +25.410000000000004 0.0014994228431538833 +25.460000000000004 0.0006560085476025827 +25.51 0.004688102935161918 +25.560000000000002 0.006136912771648051 +25.610000000000003 0.0021974392027350265 +25.660000000000004 0.003224168605070803 +25.710000000000004 0.010107915621035076 +25.76 0.008792550161262465 +25.810000000000002 0.005743300886406658 +25.860000000000003 0.004224795718017102 +25.910000000000004 0.003119864433089474 +25.960000000000004 0.010055603593221912 +26.01 0.006431935640240944 +26.060000000000002 0.009797464235474014 +26.110000000000003 0.008554372410334039 +26.160000000000004 0.005808564195478792 +26.210000000000004 0.0017483394294555941 +26.26 0.006177594700869473 +26.310000000000002 0.013183449389835341 +26.360000000000003 0.009368411154564455 +26.410000000000004 0.007159664703656666 +26.460000000000004 0.007864853026460134 +26.51 0.008597601444931992 +26.560000000000002 0.00365433119985373 +26.610000000000003 0.002514023373989213 +26.660000000000004 0.004147541048731116 +26.710000000000004 0.009923834134752634 +26.76 0.009753722367742027 +26.810000000000002 0.01034827966922832 +26.860000000000003 0.0007547936140257475 +26.910000000000004 0.0008005634788841134 +26.960000000000004 0.005834450309495217 +27.01 0.0014661043731860515 +27.060000000000002 0.007545323385782318 +27.110000000000003 0.0010069262205654231 +27.160000000000004 0.003694114108784628 +27.210000000000004 0.002915608738517916 +27.26 0.006688624205405263 +27.310000000000002 0.007045251974393098 +27.360000000000003 0.003974186114562003 +27.410000000000004 0.0050822797499005395 +27.460000000000004 0.00400214877341173 +27.51 0.0073941274635738 +27.560000000000002 0.009839740149169388 +27.610000000000003 0.008215611954023889 +27.660000000000004 0.00812390650415449 +27.710000000000004 0.0026739538366103268 +27.76 0.00022062794209454098 +27.810000000000002 0.0029410417129714502 +27.860000000000003 0.006653690568687193 +27.910000000000004 0.008024565269625358 +27.960000000000004 0.007696955857740324 +28.01 0.0070377731253631285 +28.060000000000002 0.0076518857740038755 +28.110000000000003 0.0076269129576990525 +28.160000000000004 0.011107449618969995 +28.210000000000004 0.009010658180124715 +28.26 0.015282283567905326 +28.310000000000002 0.006976214549766831 +28.360000000000003 0.005985834430836702 +28.410000000000004 0.0006204350342589289 +28.460000000000004 0.004968056087811945 +28.51 0.01069906072327566 +28.560000000000002 0.005230558227039795 +28.610000000000003 0.0027126373133174245 +28.660000000000004 0.0047773883322962335 +28.710000000000004 0.004786979573217508 +28.76 0.005347646634994841 +28.810000000000002 0.0020362490175422797 +28.860000000000003 0.0018708822099803462 +28.910000000000004 0.006262346010579821 +28.960000000000004 0.002113549738305402 +29.01 0.007740180437059261 +29.060000000000002 0.0049794436747087325 +29.110000000000003 0.0014732501981752067 +29.160000000000004 0.005987419617214626 +29.210000000000004 0.002650993127916284 +29.26 0.003622576584045665 +29.310000000000002 0.0056426792787837195 +29.360000000000003 0.006917579291528027 +29.410000000000004 0.001631262003070526 +29.460000000000004 0.0012716600013443797 +29.51 0.0016168618876284762 +29.560000000000002 0.009573827600319055 +29.610000000000003 0.0059203677090907195 +29.660000000000004 0.00015311723097776505 +29.710000000000004 0.0018487164375394888 +29.76 0.0035992946452436214 +29.810000000000002 0.006623353330732501 +29.860000000000003 0.0017657403942469728 +29.910000000000004 0.0035674584459472803 \ No newline at end of file diff --git a/tests/data/dissolve/input/epsr-benzene-3n.txt b/tests/data/dissolve/input/epsr-benzene-3n.txt index aab4463faa..acb766ca78 100755 --- a/tests/data/dissolve/input/epsr-benzene-3n.txt +++ b/tests/data/dissolve/input/epsr-benzene-3n.txt @@ -192,3 +192,29 @@ Module EPSR 'EPSR01' EndModule EndLayer + +Layer 'Analysis' + Frequency 1 + +Module Compare 'Compare01' + Data1D + Internal 'C6H6//WeightedSQ//Total' + Internal 'C6H6//ReferenceData' + EndData1D + Data1D + Internal 'C6H6//WeightedSQ//Total' + External mint 'epsr25/benzene200-neutron/C6H6.mint01' + EndExternal + EndData1D + Data1D + External xy 'epsr25/benzene200-neutron/benzene.EPSR.u01' + Y 2 + EndExternal + External mint 'epsr25/benzene200-neutron/C6D6.mint01' + EndExternal + EndData1D + ErrorType RFactor + ErrorRange 0.0 10.0 +EndModule + +EndLayer diff --git a/tests/modules/CMakeLists.txt b/tests/modules/CMakeLists.txt index 8583985c75..14b94fbc2e 100644 --- a/tests/modules/CMakeLists.txt +++ b/tests/modules/CMakeLists.txt @@ -3,6 +3,7 @@ dissolve_add_test(SRC atomShake.cpp) dissolve_add_test(SRC avgMol.cpp) dissolve_add_test(SRC bragg.cpp) dissolve_add_test(SRC broadening.cpp) +dissolve_add_test(SRC compare.cpp) dissolve_add_test(SRC dAngle.cpp) dissolve_add_test(SRC energy.cpp) dissolve_add_test(SRC epsr.cpp) diff --git a/tests/modules/compare.cpp b/tests/modules/compare.cpp new file mode 100644 index 0000000000..bb05929a3a --- /dev/null +++ b/tests/modules/compare.cpp @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team Dissolve and contributors + +#include "modules/compare/compare.h" +#include "tests/testData.h" +#include + +namespace UnitTest +{ +using RangeErrorPair = std::pair, std::vector>; + +// Water Molecule +class CompareModuleTest : public ::testing::Test +{ + protected: + DissolveSystemTest systemTest; + + void SetUp() override + { + ASSERT_NO_THROW(systemTest.setUp("dissolve/input/epsr-benzene-3n.txt")); + ASSERT_TRUE(systemTest.dissolve().iterate(1)); + } +}; + +TEST_F(CompareModuleTest, IntVsIntErrors) +{ + auto *compareModule = systemTest.getModule("Compare01"); + ASSERT_TRUE(compareModule); + + const auto &ranges = compareModule->data1dSourcesErrors().at(&compareModule->data1dSources()[0]).first; + const auto &errors = compareModule->data1dSourcesErrors().at(&compareModule->data1dSources()[0]).second; + + EXPECT_NEAR(ranges[0].minimum(), 0, 1.0e-2); + EXPECT_NEAR(ranges[0].maximum(), 50, 1.0e-4); + EXPECT_NEAR(ranges[1].minimum(), 0, 1.0e-4); + EXPECT_NEAR(ranges[1].maximum(), 10, 1.0e-4); + + EXPECT_NEAR(errors[0], 3.0942e-1, 1.0e-4); + EXPECT_NEAR(errors[1], 9.2657e-1, 1.0e-4); +} + +TEST_F(CompareModuleTest, IntVsExtErrors) +{ + auto *compareModule = systemTest.getModule("Compare01"); + ASSERT_TRUE(compareModule); + + const auto &ranges = compareModule->data1dSourcesErrors().at(&compareModule->data1dSources()[1]).first; + const auto &errors = compareModule->data1dSourcesErrors().at(&compareModule->data1dSources()[1]).second; + + EXPECT_NEAR(ranges[0].minimum(), 0, 1.0e-2); + EXPECT_NEAR(ranges[0].maximum(), 50, 1.0e-4); + EXPECT_NEAR(ranges[1].minimum(), 0, 1.0e-4); + EXPECT_NEAR(ranges[1].maximum(), 10, 1.0e-4); + + EXPECT_NEAR(errors[0], 3.0942e-1, 1.0e-4); + EXPECT_NEAR(errors[1], 9.2657e-1, 1.0e-4); +} + +TEST_F(CompareModuleTest, ExtVsExtErrors) +{ + auto *compareModule = systemTest.getModule("Compare01"); + ASSERT_TRUE(compareModule); + + const auto &ranges = compareModule->data1dSourcesErrors().at(&compareModule->data1dSources()[2]).first; + const auto &errors = compareModule->data1dSourcesErrors().at(&compareModule->data1dSources()[2]).second; + + EXPECT_NEAR(ranges[0].minimum(), 0, 1.0e-2); + EXPECT_NEAR(ranges[0].maximum(), 50, 1.0e-4); + EXPECT_NEAR(ranges[1].minimum(), 0, 1.0e-4); + EXPECT_NEAR(ranges[1].maximum(), 10, 1.0e-4); + + EXPECT_NEAR(errors[0], 7.0069e-3, 1.0e-4); + EXPECT_NEAR(errors[1], 2.0418e-2, 1.0e-4); +} + +TEST_F(CompareModuleTest, IntVsIntDelta) +{ + EXPECT_TRUE(systemTest.checkData1D( + "Compare01//Pair1_Delta", {"dissolve/compare/intvsint.dat", Data1DImportFileFormat::Data1DImportFormat::XY, 1, 2})); +} + +TEST_F(CompareModuleTest, IntVsExtDelta) +{ + EXPECT_TRUE(systemTest.checkData1D( + "Compare01//Pair2_Delta", {"dissolve/compare/intvsext.dat", Data1DImportFileFormat::Data1DImportFormat::XY, 1, 2})); +} + +TEST_F(CompareModuleTest, ExtVsExtDelta) +{ + EXPECT_TRUE(systemTest.checkData1D( + "Compare01//Pair3_Delta", {"dissolve/compare/extvsext.dat", Data1DImportFileFormat::Data1DImportFormat::XY, 1, 2})); +} + +} // namespace UnitTest diff --git a/web/docs/userguide/modules/checks&tests/compare/_index.md b/web/docs/userguide/modules/checks&tests/compare/_index.md index ce577aa35e..1774c496b3 100644 --- a/web/docs/userguide/modules/checks&tests/compare/_index.md +++ b/web/docs/userguide/modules/checks&tests/compare/_index.md @@ -23,4 +23,4 @@ Two data sets must be provided, from internal Dissolve data or external data fro |Keyword|Arguments|Default|Description| |:------|:--:|:-----:|-----------| |`ErrorType`|[`ErrorType`]({{< ref "errortype" >}})|`Euclidean`|Error metric to calculate between the two datasets.| -|`ErrorRanges`|`double`
`double`|--|Minumum and maximum range bounds over which to calculate the error metric.| +|`ErrorRange`|`double`
`double`|--|Minumum and maximum range bounds over which to calculate the error metric.| From 6e7411774289220a4063a37dc2fd6e61ce7f182a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 6 Mar 2024 13:16:19 +0000 Subject: [PATCH 06/13] Update website release info (v1.4.3) (#1811) Co-authored-by: trisyoungs --- README.md | 2 +- web/main.toml | 4 ++-- web/static/include/old_releases.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 621350c5ab..c5f6f498d4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ![Dissolve's Logo](icon/logo.png) -_Last Release: 1.4.2, Monday 29th January 2024_ +_Last Release: 1.4.3, Wednesday 6th March 2024_ _Release Build::_ [![Release Build Status](https://github.com/disorderedmaterials/dissolve/actions/workflows/release.yml/badge.svg)](https://github.com/disorderedmaterials/dissolve/actions/workflows/release.yml) _Development Build::_ [![Development Build Status](https://github.com/disorderedmaterials/dissolve/actions/workflows/continuous.yml/badge.svg)](https://github.com/disorderedmaterials/dissolve/actions/workflows/continuous.yml) diff --git a/web/main.toml b/web/main.toml index 42e68e06c7..3b99955af3 100644 --- a/web/main.toml +++ b/web/main.toml @@ -47,8 +47,8 @@ latexDashes = true # General Parameters [params] copyright = "Team Dissolve and contributors. Built using Hugo/Docsy." -releaseVersion = "1.4.2" -releaseDate = "29th January 2024" +releaseVersion = "1.4.3" +releaseDate = "6th March 2024" devVersion = "1.4.0" github_repo = "https://github.com/disorderedmaterials/dissolve" github_subdir = "web" diff --git a/web/static/include/old_releases.md b/web/static/include/old_releases.md index bdd0852dd3..919e3b6080 100644 --- a/web/static/include/old_releases.md +++ b/web/static/include/old_releases.md @@ -1,3 +1,4 @@ +- [Version 1.4.2, released 29 January 2024](https://github.com/disorderedmaterials/dissolve/releases/tag/1.4.2) - [Version 1.4.1, released 17 January 2024](https://github.com/disorderedmaterials/dissolve/releases/tag/1.4.1) - [Version 1.4.0, released 12 January 2024](https://github.com/disorderedmaterials/dissolve/releases/tag/1.4.0) - [Version 1.3.3, released 15 August 2023](https://github.com/disorderedmaterials/dissolve/releases/tag/1.3.3) @@ -25,4 +26,3 @@ - [Version 0.9.6, released 11 August 2022](https://github.com/disorderedmaterials/dissolve/releases/tag/0.9.6) - [Version 0.9.5, released 1 August 2022](https://github.com/disorderedmaterials/dissolve/releases/tag/0.9.5) - [Version 0.9.4, released 31 July 2022](https://github.com/disorderedmaterials/dissolve/releases/tag/0.9.4) -- [Version 0.9.3, released 25 July 2022](https://github.com/disorderedmaterials/dissolve/releases/tag/0.9.3) From d12152930cac88d554a4d0ceb112c01f6a06a072 Mon Sep 17 00:00:00 2001 From: RobBuchanan <106311829+RobBuchananCompPhys@users.noreply.github.com> Date: Thu, 7 Mar 2024 16:52:25 +0000 Subject: [PATCH 07/13] refactor: Simulation data manager as QML TableView (#1809) Co-authored-by: Adam Washington Rewrite the Data Manager dialog to use QML. --- src/gui/CMakeLists.txt | 1 - src/gui/dataManagerDialog.cpp | 41 ++-- src/gui/dataManagerDialog.h | 15 +- src/gui/dataManagerDialog.ui | 185 ------------------ src/gui/main.qrc | 1 + src/gui/models/dataManagerSimulationModel.h | 4 + .../SimulationDataManager.qml | 91 +++++++++ src/gui/types.cpp | 3 +- 8 files changed, 112 insertions(+), 229 deletions(-) delete mode 100644 src/gui/dataManagerDialog.ui create mode 100644 src/gui/qml/simulationDataManager/SimulationDataManager.qml diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index f92cdb909c..6181fe9d2f 100755 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -81,7 +81,6 @@ add_library( copySpeciesTermsDialog.ui dataManagerDialog.cpp dataManagerDialog.h - dataManagerDialog.ui editSpeciesDialog.cpp editSpeciesDialog.h editSpeciesDialog.ui diff --git a/src/gui/dataManagerDialog.cpp b/src/gui/dataManagerDialog.cpp index 7315e1f8c6..9f671d7219 100644 --- a/src/gui/dataManagerDialog.cpp +++ b/src/gui/dataManagerDialog.cpp @@ -5,43 +5,26 @@ #include "main/dissolve.h" #include "templates/variantPointer.h" #include +#include #include #include +#include #include DataManagerDialog::DataManagerDialog(QWidget *parent, Dissolve &dissolve, GenericList &items) : QDialog(parent), dissolve_(dissolve), simModel_(dissolve, items) { - ui_.setupUi(this); - + view_ = new QQuickWidget(this); simProxy_.setSourceModel(&simModel_); - ui_.SimulationDataTable->setModel(&simProxy_); - ui_.SimulationDataTable->setSortingEnabled(true); - - updateControls(); -} - -/* - * UI - */ + view_->rootContext()->setContextProperty("simProxy", &simProxy_); + view_->rootContext()->setContextProperty("simModel", &simModel_); + view_->setSource(QUrl("qrc:/dialogs/qml/simulationDataManager/SimulationDataManager.qml")); -// Update the specified table of GenericItems, optionally filtering them by name and description -void DataManagerDialog::filterTable(QString filterText) -{ - simProxy_.setFilterRegularExpression( - QRegularExpression(filterText.replace("*", ".*"), QRegularExpression::CaseInsensitiveOption)); - simProxy_.setFilterKeyColumn(0); -} - -// Update controls -void DataManagerDialog::updateControls() -{ - // Clear and re-populate simulation data table - ui_.SimulationDataTable->resizeColumnsToContents(); -} + view_->setResizeMode(QQuickWidget::SizeRootObjectToView); -// Simulation Data -void DataManagerDialog::on_SimulationDataFilterEdit_textChanged(const QString &text) { filterTable(text); } + auto *topLeftLayout = new QHBoxLayout; + topLeftLayout->addWidget(view_); + setLayout(topLeftLayout); -// Dialog -void DataManagerDialog::on_CloseButton_clicked(bool checked) { accept(); } + QObject::connect(&simModel_, SIGNAL(closeClicked()), this, SLOT(accept())); +} \ No newline at end of file diff --git a/src/gui/dataManagerDialog.h b/src/gui/dataManagerDialog.h index 70fa007cde..1f9d7eaea8 100644 --- a/src/gui/dataManagerDialog.h +++ b/src/gui/dataManagerDialog.h @@ -4,9 +4,9 @@ #pragma once #include "gui/models/dataManagerSimulationModel.h" -#include "gui/ui_dataManagerDialog.h" #include "items/list.h" #include +#include #include #include @@ -34,16 +34,5 @@ class DataManagerDialog : public QDialog * UI */ private: - // Main form declaration - Ui::DataManagerDialog ui_; - - private: - // Update the specified table of GenericItems, optionally filtering them by name and description - void filterTable(QString filterText); - // Update controls - void updateControls(); - - private Q_SLOTS: - void on_SimulationDataFilterEdit_textChanged(const QString &text); - void on_CloseButton_clicked(bool checked); + QQuickWidget *view_{nullptr}; }; diff --git a/src/gui/dataManagerDialog.ui b/src/gui/dataManagerDialog.ui deleted file mode 100644 index 2808526088..0000000000 --- a/src/gui/dataManagerDialog.ui +++ /dev/null @@ -1,185 +0,0 @@ - - - DataManagerDialog - - - Qt::ApplicationModal - - - - 0 - 0 - 871 - 514 - - - - - 10 - - - - Data Manager - - - - :/general/icons/data.svg:/general/icons/data.svg - - - - 4 - - - 4 - - - 4 - - - 4 - - - 4 - - - - - - - - 1 - 0 - - - - Current Module Data - - - - 4 - - - 4 - - - 4 - - - 4 - - - 4 - - - - - 4 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 20 - 20 - - - - - - - :/general/icons/filter.svg - - - true - - - - - - - - 0 - 0 - - - - Filter templates by name / description - - - - - - - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - true - - - false - - - false - - - - - - - - - - - - 4 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - true - - - &Close - - - - - - - - - - - - diff --git a/src/gui/main.qrc b/src/gui/main.qrc index 1a8a8329bf..913b9fd61e 100644 --- a/src/gui/main.qrc +++ b/src/gui/main.qrc @@ -194,6 +194,7 @@ qml/modifyCharges/Loader.qml qml/modifyCharges/Layout.qml qml/modifyCharges/ScaleLayout.qml + qml/simulationDataManager/SimulationDataManager.qml qml/DropDownLabel.qml qml/ForceFieldAssign.qml qml/ForceFieldAtomTypes.qml diff --git a/src/gui/models/dataManagerSimulationModel.h b/src/gui/models/dataManagerSimulationModel.h index 569deea8ab..2e04a92341 100644 --- a/src/gui/models/dataManagerSimulationModel.h +++ b/src/gui/models/dataManagerSimulationModel.h @@ -6,6 +6,7 @@ #include "main/dissolve.h" #include #include +#include #include #include @@ -30,4 +31,7 @@ class DataManagerSimulationModel : public QAbstractTableModel // Register all changes to the model void update(); + + Q_SIGNALS: + void closeClicked(); }; diff --git a/src/gui/qml/simulationDataManager/SimulationDataManager.qml b/src/gui/qml/simulationDataManager/SimulationDataManager.qml new file mode 100644 index 0000000000..6862fe0a8b --- /dev/null +++ b/src/gui/qml/simulationDataManager/SimulationDataManager.qml @@ -0,0 +1,91 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Dissolve 1.0 +import "../widgets" as D + +Page { + id: root + height: 500 + visible: true + width: 670 + + function filterByRegExp(proxy, text) { + proxy.filterRegularExpression = RegExp(text); + } + function getHeaderStringArray(model) { + var headerArray = []; + for (var i = 0; i < model.columnCount(); ++i) { + headerArray.push(qsTr(model.headerData(i, Qt.Horizontal))); + } + return headerArray; + } + + D.GroupBox { + id: gb + anchors.fill: parent + title: "Current Module Data" + + ColumnLayout { + anchors.fill: parent + + TextField { + id: searchBox + Layout.alignment: Qt.AlignRight + Layout.preferredWidth: gb.width / 4 + placeholderText: qsTr("Search...") + + onEditingFinished: { + filterByRegExp(simProxy, searchBox.text); + if (simProxy.rowCount() == 0) { + filterByRegExp(simProxy, ""); + } + } + } + HorizontalHeaderView { + id: header + Layout.fillWidth: true + Layout.preferredHeight: contentHeight + Layout.preferredWidth: contentWidth + clip: true + enabled: simModel.rowCount() == 0 ? false : true + model: getHeaderStringArray(simModel) + syncView: table + } + TableView { + id: table + property variant colWidths: [300, 300, 50] + + Layout.fillHeight: true + Layout.fillWidth: true + boundsBehavior: Flickable.StopAtBounds + clip: true + columnSpacing: 1 + columnWidthProvider: function (column) { + return colWidths[column]; + } + model: simProxy + rowSpacing: 1 + + delegate: Rectangle { + color: "white" + implicitHeight: tableText.height + implicitWidth: tableText.width + + D.Text { + id: tableText + padding: 12 + text: display + } + } + } + D.Button { + id: closeButton + Layout.alignment: Qt.AlignRight + text: "Close" + + onClicked: simModel.closeClicked() + } + } + } +} diff --git a/src/gui/types.cpp b/src/gui/types.cpp index ffd19f4858..044c98bb6c 100644 --- a/src/gui/types.cpp +++ b/src/gui/types.cpp @@ -5,6 +5,7 @@ #include "gui/models/addForcefieldDialogModel.h" #include "gui/models/atomTypeModel.h" #include "gui/models/configurationModel.h" +#include "gui/models/dataManagerSimulationModel.h" #include "gui/models/dissolveModel.h" #include "gui/models/masterTermTreeModel.h" #include "gui/models/modifyChargesModel.h" @@ -14,8 +15,8 @@ void Types::registerDissolveQmlTypes() { - qmlRegisterType(PROJECT, 1, 0, "AddForcefieldDialogModel"); + qmlRegisterType(PROJECT, 1, 0, "DataManagerSimulationModel"); qmlRegisterType(PROJECT, 1, 0, "DissolveModel"); qmlRegisterType(PROJECT, 1, 0, "SpeciesModel"); qmlRegisterType(PROJECT, 1, 0, "ConfigurationModel"); From 59d7a56f324e15cf710719e67c44313549606180 Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Mon, 11 Mar 2024 16:28:33 +0000 Subject: [PATCH 08/13] feat: wrap QSortFilterProxy so it is accessible from QML (#1813) This enables immediate search in the Data Manager Dialog (and other tables as QML support expands). This also officially adds support for regular expression search in these tables. --- src/gui/dataManagerDialog.cpp | 4 +--- src/gui/dataManagerDialog.h | 3 --- src/gui/models/CMakeLists.txt | 3 +++ src/gui/models/sortFilterProxy.cpp | 8 ++++++++ src/gui/models/sortFilterProxy.h | 17 +++++++++++++++++ .../SimulationDataManager.qml | 14 ++++++-------- src/gui/types.cpp | 5 ++++- 7 files changed, 39 insertions(+), 15 deletions(-) create mode 100644 src/gui/models/sortFilterProxy.cpp create mode 100644 src/gui/models/sortFilterProxy.h diff --git a/src/gui/dataManagerDialog.cpp b/src/gui/dataManagerDialog.cpp index 9f671d7219..ea21e5d30d 100644 --- a/src/gui/dataManagerDialog.cpp +++ b/src/gui/dataManagerDialog.cpp @@ -15,8 +15,6 @@ DataManagerDialog::DataManagerDialog(QWidget *parent, Dissolve &dissolve, Generi : QDialog(parent), dissolve_(dissolve), simModel_(dissolve, items) { view_ = new QQuickWidget(this); - simProxy_.setSourceModel(&simModel_); - view_->rootContext()->setContextProperty("simProxy", &simProxy_); view_->rootContext()->setContextProperty("simModel", &simModel_); view_->setSource(QUrl("qrc:/dialogs/qml/simulationDataManager/SimulationDataManager.qml")); @@ -27,4 +25,4 @@ DataManagerDialog::DataManagerDialog(QWidget *parent, Dissolve &dissolve, Generi setLayout(topLeftLayout); QObject::connect(&simModel_, SIGNAL(closeClicked()), this, SLOT(accept())); -} \ No newline at end of file +} diff --git a/src/gui/dataManagerDialog.h b/src/gui/dataManagerDialog.h index 1f9d7eaea8..c3b4240ee3 100644 --- a/src/gui/dataManagerDialog.h +++ b/src/gui/dataManagerDialog.h @@ -7,7 +7,6 @@ #include "items/list.h" #include #include -#include #include // Forward Declarations @@ -27,8 +26,6 @@ class DataManagerDialog : public QDialog Dissolve &dissolve_; // Simulation Model DataManagerSimulationModel simModel_; - // Simulation Proxy - QSortFilterProxyModel simProxy_; /* * UI diff --git a/src/gui/models/CMakeLists.txt b/src/gui/models/CMakeLists.txt index 8551d1255c..a4d67ffc75 100644 --- a/src/gui/models/CMakeLists.txt +++ b/src/gui/models/CMakeLists.txt @@ -31,6 +31,7 @@ set(models_MOC_HDRS renderableGroupManagerModel.h sitesFilterProxy.h sitesModel.h + sortFilterProxy.h speciesFilterProxy.h speciesModel.h speciesSiteFilterProxy.h @@ -95,6 +96,7 @@ set(models_SRCS renderableGroupManagerModel.cpp sitesFilterProxy.cpp sitesModel.cpp + sortFilterProxy.cpp speciesAngleModel.cpp speciesAtomModel.cpp speciesBondModel.cpp @@ -127,6 +129,7 @@ qt6_wrap_cpp( isotopologueSetModel.h pairPotentialModel.h rangeVectorModel.h + sortFilterProxy.h speciesAngleModel.h speciesAtomModel.h speciesBondModel.h diff --git a/src/gui/models/sortFilterProxy.cpp b/src/gui/models/sortFilterProxy.cpp new file mode 100644 index 0000000000..4bdd18b41d --- /dev/null +++ b/src/gui/models/sortFilterProxy.cpp @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team Dissolve and contributors + +#include "gui/models/sortFilterProxy.h" + +SortFilterProxy::SortFilterProxy(QObject *parent) : QSortFilterProxyModel(parent) {} + +void SortFilterProxy::setModel(const QAbstractItemModel *model) { setSourceModel(const_cast(model)); } diff --git a/src/gui/models/sortFilterProxy.h b/src/gui/models/sortFilterProxy.h new file mode 100644 index 0000000000..51637e670c --- /dev/null +++ b/src/gui/models/sortFilterProxy.h @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team Dissolve and contributors + +#pragma once + +#include + +class SortFilterProxy : public QSortFilterProxyModel +{ + Q_OBJECT + // The model to proxy + Q_PROPERTY(const QAbstractItemModel *model WRITE setModel NOTIFY read) + + public: + SortFilterProxy(QObject *parent = nullptr); + void setModel(const QAbstractItemModel *model); +}; diff --git a/src/gui/qml/simulationDataManager/SimulationDataManager.qml b/src/gui/qml/simulationDataManager/SimulationDataManager.qml index 6862fe0a8b..c52c8b0b39 100644 --- a/src/gui/qml/simulationDataManager/SimulationDataManager.qml +++ b/src/gui/qml/simulationDataManager/SimulationDataManager.qml @@ -21,6 +21,11 @@ Page { return headerArray; } + SortFilterProxy { + id: proxy + filterRegularExpression: RegExp(searchBox.text) + model: simModel + } D.GroupBox { id: gb anchors.fill: parent @@ -34,13 +39,6 @@ Page { Layout.alignment: Qt.AlignRight Layout.preferredWidth: gb.width / 4 placeholderText: qsTr("Search...") - - onEditingFinished: { - filterByRegExp(simProxy, searchBox.text); - if (simProxy.rowCount() == 0) { - filterByRegExp(simProxy, ""); - } - } } HorizontalHeaderView { id: header @@ -64,7 +62,7 @@ Page { columnWidthProvider: function (column) { return colWidths[column]; } - model: simProxy + model: proxy rowSpacing: 1 delegate: Rectangle { diff --git a/src/gui/types.cpp b/src/gui/types.cpp index 044c98bb6c..f268b9f7b0 100644 --- a/src/gui/types.cpp +++ b/src/gui/types.cpp @@ -10,8 +10,10 @@ #include "gui/models/masterTermTreeModel.h" #include "gui/models/modifyChargesModel.h" #include "gui/models/moduleLayersModel.h" +#include "gui/models/sortFilterProxy.h" #include "gui/models/speciesModel.h" #include +#include void Types::registerDissolveQmlTypes() { @@ -27,4 +29,5 @@ void Types::registerDissolveQmlTypes() qmlRegisterType(PROJECT, 1, 0, "MasterImproperModel"); qmlRegisterType(PROJECT, 1, 0, "MasterTorsionModel"); qmlRegisterType(PROJECT, 1, 0, "ModifyChargesModel"); -} \ No newline at end of file + qmlRegisterType(PROJECT, 1, 0, "SortFilterProxy"); +} From 6692aab2850adc5fcc0be26f468fd5295fbda585 Mon Sep 17 00:00:00 2001 From: Jared Swift Date: Mon, 11 Mar 2024 22:08:28 +0000 Subject: [PATCH 09/13] Analysis Refactoring - Part 2 (#1795) Co-authored-by: Tristan Youngs --- src/analyser/CMakeLists.txt | 3 + src/analyser/dataExporter.h | 31 +++ src/analyser/dataNormaliser1D.cpp | 8 +- src/analyser/dataNormaliser1D.h | 3 +- src/analyser/dataNormaliser2D.cpp | 72 +++++ src/analyser/dataNormaliser2D.h | 24 ++ src/analyser/dataNormaliser3D.h | 4 +- src/modules/angle/angle.cpp | 139 +--------- src/modules/angle/angle.h | 56 +--- src/modules/angle/gui/angleWidgetFuncs.cpp | 13 +- src/modules/angle/process.cpp | 255 +++++++++++++++--- src/modules/axisAngle/axisAngle.cpp | 95 +------ src/modules/axisAngle/axisAngle.h | 43 +-- .../axisAngle/gui/axisAngleWidgetFuncs.cpp | 7 +- src/modules/axisAngle/process.cpp | 129 +++++++-- src/modules/dAngle/dAngle.cpp | 95 +------ src/modules/dAngle/dAngle.h | 42 +-- src/modules/dAngle/gui/dAngleWidgetFuncs.cpp | 7 +- src/modules/dAngle/process.cpp | 151 +++++++++-- src/modules/histogramCN/process.cpp | 20 +- src/modules/intraAngle/process.cpp | 23 +- .../gui/intraDistanceWidgetFuncs.cpp | 2 +- src/modules/intraDistance/process.cpp | 21 +- src/modules/orientedSDF/process.cpp | 21 +- src/modules/qSpecies/process.cpp | 22 +- src/modules/sdf/process.cpp | 20 +- src/modules/siteRDF/process.cpp | 23 +- tests/data/dissolve/input/angle.txt | 117 ++++++++ .../data/dissolve/input/axisAngle-benzene.txt | 177 ++++++++++++ .../data/dissolve/input/axisAngle-benzene.xyz | 38 +++ tests/modules/CMakeLists.txt | 2 + tests/modules/angle.cpp | 30 +++ tests/modules/axisAngle.cpp | 28 ++ tests/modules/dAngle.cpp | 4 +- 34 files changed, 1082 insertions(+), 643 deletions(-) create mode 100644 src/analyser/dataExporter.h create mode 100644 src/analyser/dataNormaliser2D.cpp create mode 100644 src/analyser/dataNormaliser2D.h create mode 100644 tests/data/dissolve/input/angle.txt create mode 100644 tests/data/dissolve/input/axisAngle-benzene.txt create mode 100644 tests/data/dissolve/input/axisAngle-benzene.xyz create mode 100644 tests/modules/angle.cpp create mode 100644 tests/modules/axisAngle.cpp diff --git a/src/analyser/CMakeLists.txt b/src/analyser/CMakeLists.txt index b0e50003c4..9d08555c6f 100644 --- a/src/analyser/CMakeLists.txt +++ b/src/analyser/CMakeLists.txt @@ -1,7 +1,10 @@ add_library( analyser + dataExporter.h dataNormaliser1D.cpp dataNormaliser1D.h + dataNormaliser2D.cpp + dataNormaliser2D.h dataNormaliser3D.cpp dataNormaliser3D.h dataNormaliserBase.h diff --git a/src/analyser/dataExporter.h b/src/analyser/dataExporter.h new file mode 100644 index 0000000000..1588e52c1e --- /dev/null +++ b/src/analyser/dataExporter.h @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team Dissolve and contributors + +#pragma once + +#include "base/processPool.h" + +template class DataExporter +{ + public: + // Try to export the specified data, if a valid filename has been provided + static bool exportData(const DataND &targetData, DataNDExportFileFormat &fileAndFormat, const ProcessPool &procPool) + { + if (fileAndFormat.hasFilename()) + { + if (procPool.isMaster()) + { + if (fileAndFormat.exportData(targetData)) + procPool.decideTrue(); + else + { + procPool.decideFalse(); + return false; + } + } + else if (!procPool.decision()) + return false; + } + return true; + } +}; \ No newline at end of file diff --git a/src/analyser/dataNormaliser1D.cpp b/src/analyser/dataNormaliser1D.cpp index e3f9219aa8..1dcad0a956 100644 --- a/src/analyser/dataNormaliser1D.cpp +++ b/src/analyser/dataNormaliser1D.cpp @@ -2,8 +2,6 @@ // Copyright (c) 2024 Team Dissolve and contributors #include "analyser/dataNormaliser1D.h" -#include "expression/expression.h" -#include "expression/variable.h" #include "math/data1D.h" #include "math/integrator.h" @@ -14,8 +12,10 @@ void DataNormaliser1D::normalise(NormalisationFunction1D normalisationFunction) const auto &xs = targetData_.xAxis(); auto &values = targetData_.values(); + const auto xDelta = xs.size() > 1 ? xs[1] - xs[0] : 1.0; + for (auto i = 0; i < xs.size(); ++i) - values.at(i) = normalisationFunction(xs[i], values.at(i)); + values.at(i) = normalisationFunction(xs[i], xDelta, values.at(i)); } void DataNormaliser1D::normaliseByGrid() { Messenger::warn("Grid normalisation not implemented for 1D data."); } @@ -57,4 +57,4 @@ void DataNormaliser1D::normaliseTo(double value, bool absolute) auto sum = absolute ? Integrator::absSum(targetData_) : Integrator::sum(targetData_); targetData_ /= sum; targetData_ *= value; -} \ No newline at end of file +} diff --git a/src/analyser/dataNormaliser1D.h b/src/analyser/dataNormaliser1D.h index 9777b27d9e..6433012ff9 100644 --- a/src/analyser/dataNormaliser1D.h +++ b/src/analyser/dataNormaliser1D.h @@ -5,9 +5,8 @@ #include "analyser/dataNormaliserBase.h" #include "math/data1D.h" -#include -using NormalisationFunction1D = std::function; +using NormalisationFunction1D = std::function; class DataNormaliser1D : public DataNormaliserBase { public: diff --git a/src/analyser/dataNormaliser2D.cpp b/src/analyser/dataNormaliser2D.cpp new file mode 100644 index 0000000000..ce292c285d --- /dev/null +++ b/src/analyser/dataNormaliser2D.cpp @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team Dissolve and contributors + +#include "analyser/dataNormaliser2D.h" +#include "math/data2D.h" +#include "math/integrator.h" + +DataNormaliser2D::DataNormaliser2D(Data2D &targetData) : DataNormaliserBase(targetData) {} + +void DataNormaliser2D::normalise(NormalisationFunction2D normalisationFunction) +{ + const auto &xs = targetData_.xAxis(); + const auto &ys = targetData_.yAxis(); + auto &values = targetData_.values(); + + const auto xDelta = xs.size() > 1 ? xs[1] - xs[0] : 1.0; + const auto yDelta = ys.size() > 1 ? ys[1] - ys[0] : 1.0; + + for (auto i = 0; i < xs.size(); ++i) + { + for (auto j = 0; j < ys.size(); ++j) + { + values[{i, j}] = normalisationFunction(xs[i], xDelta, ys[j], yDelta, values[{i, j}]); + } + } +} + +void DataNormaliser2D::normaliseByGrid() { Messenger::warn("Grid normalisation not implemented for 2D data."); } + +void DataNormaliser2D::normaliseBySphericalShell() +{ + // We expect x values to be centre-bin values, and regularly spaced + const auto &xAxis = targetData_.xAxis(); + const auto &yAxis = targetData_.yAxis(); + auto &values = targetData_.values(); + + if (xAxis.size() < 2) + return; + + // Derive first left-bin boundary from the delta between points 0 and 1 + auto leftBin = xAxis[0] - (xAxis[1] - xAxis[0]) * 0.5; + auto r1Cubed = pow(leftBin, 3); + for (auto n = 0; n < xAxis.size(); ++n) + { + auto r2Cubed = 0.0, rightBin = 0.0; + for (auto m = 0; m < yAxis.size(); ++m) + { + // Get new right-bin from existing left bin boundary and current bin centre + auto rightBin = leftBin + 2 * (xAxis[n] - leftBin); + auto r2Cubed = pow(rightBin, 3); + + // Calculate divisor for normalisation + auto divisor = (4.0 / 3.0) * PI * (r2Cubed - r1Cubed); + + // Peform normalisation step + values[{n, m}] /= divisor; + if (targetData_.valuesHaveErrors()) + targetData_.error(n, m) /= divisor; + } + + // Overwrite old values + r1Cubed = r2Cubed; + leftBin = rightBin; + } +} + +void DataNormaliser2D::normaliseTo(double value, bool absolute) +{ + auto sum = absolute ? Integrator::absSum(targetData_) : Integrator::sum(targetData_); + targetData_ /= sum; + targetData_ *= value; +} diff --git a/src/analyser/dataNormaliser2D.h b/src/analyser/dataNormaliser2D.h new file mode 100644 index 0000000000..62217612ab --- /dev/null +++ b/src/analyser/dataNormaliser2D.h @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team Dissolve and contributors + +#pragma once + +#include "analyser/dataNormaliserBase.h" +#include "math/data2D.h" + +using NormalisationFunction2D = + std::function; +class DataNormaliser2D : public DataNormaliserBase +{ + public: + DataNormaliser2D(Data2D &targetData); + + /* + * Normalisation Functions + */ + public: + void normalise(NormalisationFunction2D normalisationFunction) override; + void normaliseByGrid() override; + void normaliseBySphericalShell() override; + void normaliseTo(double value = 1.0, bool absolute = true) override; +}; \ No newline at end of file diff --git a/src/analyser/dataNormaliser3D.h b/src/analyser/dataNormaliser3D.h index 70b69abba1..6de437a5c0 100644 --- a/src/analyser/dataNormaliser3D.h +++ b/src/analyser/dataNormaliser3D.h @@ -5,9 +5,9 @@ #include "analyser/dataNormaliserBase.h" #include "math/data3D.h" -#include -using NormalisationFunction3D = std::function; +using NormalisationFunction3D = std::function; class DataNormaliser3D : public DataNormaliserBase { public: diff --git a/src/modules/angle/angle.cpp b/src/modules/angle/angle.cpp index 4cb8580954..50216fd24f 100644 --- a/src/modules/angle/angle.cpp +++ b/src/modules/angle/angle.cpp @@ -8,127 +8,9 @@ #include "keywords/speciesSiteVector.h" #include "keywords/vec3Double.h" #include "module/context.h" -#include "procedure/nodes/calculateAngle.h" -#include "procedure/nodes/calculateDistance.h" -#include "procedure/nodes/collect1D.h" -#include "procedure/nodes/collect2D.h" -#include "procedure/nodes/collect3D.h" -#include "procedure/nodes/operateExpression.h" -#include "procedure/nodes/operateNormalise.h" -#include "procedure/nodes/operateNumberDensityNormalise.h" -#include "procedure/nodes/operateSitePopulationNormalise.h" -#include "procedure/nodes/operateSphericalShellNormalise.h" -#include "procedure/nodes/process1D.h" -#include "procedure/nodes/process2D.h" -#include "procedure/nodes/process3D.h" -#include "procedure/nodes/select.h" -AngleModule::AngleModule() : Module(ModuleTypes::Angle), analyser_(ProcedureNode::AnalysisContext) +AngleModule::AngleModule() : Module(ModuleTypes::Angle) { - // Select: Site 'A' - selectA_ = analyser_.createRootNode("A"); - auto &forEachA = selectA_->branch()->get(); - - // -- Select: Site 'B' - selectB_ = forEachA.create("B"); - auto &forEachB = selectB_->branch()->get(); - - // -- -- Calculate: 'rAB' - auto calcAB = forEachB.create("rAB", selectA_, selectB_); - - // -- -- Collect1D: 'RDF(AB)' - collectAB_ = - forEachB.create({}, calcAB, ProcedureNode::AnalysisContext, rangeAB_.x, rangeAB_.y, rangeAB_.z); - - // -- -- Select: Site 'C' - selectC_ = forEachB.create("C"); - auto &forEachC = selectC_->branch()->get(); - - // -- -- -- Calculate: 'rBC' - auto calcBC = forEachC.create({}, selectB_, selectC_); - - // -- -- -- Calculate: 'aABC' - calculateAngle_ = forEachC.create({}, selectA_, selectB_, selectC_); - calculateAngle_->keywords().set("Symmetric", symmetric_); - - // -- -- -- Collect1D: 'RDF(BC)' - collectBC_ = - forEachC.create({}, calcBC, ProcedureNode::AnalysisContext, rangeBC_.x, rangeBC_.y, rangeBC_.z); - - // -- -- -- Collect1D: 'ANGLE(ABC)' - collectABC_ = forEachC.create({}, calculateAngle_, ProcedureNode::AnalysisContext, angleRange_.x, - angleRange_.y, angleRange_.z); - - // -- -- -- Collect2D: 'DAngle (A-B)-C' - collectDAngleAB_ = - forEachC.create({}, calcAB, calculateAngle_, ProcedureNode::AnalysisContext, rangeAB_.x, - rangeAB_.y, rangeAB_.z, angleRange_.x, angleRange_.y, angleRange_.z); - - // -- -- -- Collect2D: 'DAngle A-(B-C)' - collectDAngleBC_ = - forEachC.create({}, calcBC, calculateAngle_, ProcedureNode::AnalysisContext, rangeBC_.x, - rangeBC_.y, rangeBC_.z, angleRange_.x, angleRange_.y, angleRange_.z); - - // -- -- -- Collect3D: 'rAB vs rBC vs aABC' - collectDDA_ = forEachC.create({}, calcAB, calcBC, calculateAngle_, ProcedureNode::AnalysisContext, - rangeAB_, rangeBC_, angleRange_); - - // Process1D: 'RDF(AB)' - processAB_ = analyser_.createRootNode("RDF(AB)", collectAB_); - processAB_->keywords().set("LabelValue", std::string("g\\sub{AB}(r)")); - processAB_->keywords().set("LabelX", std::string("r, \\symbol{Angstrom}")); - auto &rdfABNormalisation = processAB_->branch()->get(); - rdfABNormalisation.create({}, - ConstNodeVector({selectA_})); - rdfABNormalisation.create({}, ConstNodeVector({selectB_})); - rdfABNormalisation.create({}); - - // Process1D: 'RDF(BC)' - processBC_ = analyser_.createRootNode("RDF(BC)", collectBC_); - processBC_->keywords().set("LabelValue", std::string("g\\sub{BC}(r)")); - processBC_->keywords().set("LabelX", std::string("r, \\symbol{Angstrom}")); - auto &rdfBCNormalisation = processBC_->branch()->get(); - rdfBCNormalisation.create( - {}, ConstNodeVector({selectB_, selectA_})); - rdfBCNormalisation.create({}, ConstNodeVector({selectC_})); - rdfBCNormalisation.create({}); - - // Process1D: 'ANGLE(ABC)' - processAngle_ = analyser_.createRootNode("Angle(ABC)", collectABC_); - processAngle_->keywords().set("LabelValue", std::string("Normalised Frequency")); - processAngle_->keywords().set("LabelX", std::string("\\symbol{theta}, \\symbol{degrees}")); - auto &angleNormalisation = processAngle_->branch()->get(); - angleNormalisation.create({}, "value/sin(toRad(x))"); - angleNormalisation.create({}, 1.0); - - // Process2D: 'DAngle (A-B)-C' - processDAngleAB_ = analyser_.createRootNode("DAngle((A-B)-C)", collectDAngleAB_); - processDAngleAB_->keywords().set("LabelValue", std::string("g\\sub{AB}(r)")); - processDAngleAB_->keywords().set("LabelX", std::string("r, \\symbol{Angstrom}")); - processDAngleAB_->keywords().set("LabelY", std::string("\\symbol{theta}, \\symbol{degrees}")); - auto &dAngleABNormalisation = processDAngleAB_->branch()->get(); - dAngleABNormalisationExpression_ = dAngleABNormalisation.create( - {}, fmt::format("{} * value/sin(toRad(y))/sin(toRad(yDelta))", symmetric_ ? 1.0 : 2.0)); - dAngleABNormalisation.create( - {}, ConstNodeVector({selectA_, selectC_})); - dAngleABNormalisation.create({}, - ConstNodeVector({selectB_})); - dAngleABNormalisation.create({}); - - // Process2D: 'DAngle A-(B-C)' - processDAngleBC_ = analyser_.createRootNode("DAngle(A-(B-C))", collectDAngleBC_); - processDAngleBC_->keywords().set("LabelValue", std::string("g\\sub{BC}(r)")); - processDAngleBC_->keywords().set("LabelX", std::string("r, \\symbol{Angstrom}")); - processDAngleBC_->keywords().set("LabelY", std::string("\\symbol{theta}, \\symbol{degrees}")); - auto &dAngleBCNormalisation = processDAngleBC_->branch()->get(); - dAngleBCNormalisationExpression_ = dAngleBCNormalisation.create( - {}, fmt::format("{} * value/sin(toRad(y))/sin(toRad(yDelta))", symmetric_ ? 1.0 : 2.0)); - dAngleBCNormalisation.create( - {}, ConstNodeVector({selectB_, selectA_})); - dAngleBCNormalisation.create({}, - ConstNodeVector({selectC_})); - dAngleBCNormalisation.create({}); - /* * Keywords */ @@ -136,12 +18,9 @@ AngleModule::AngleModule() : Module(ModuleTypes::Angle), analyser_(ProcedureNode keywords_.addTarget("Configuration", "Set target configuration for the module", targetConfiguration_); keywords_.setOrganisation("Options", "Sites", "Specify sites defining the angle interaction A-B-C."); - keywords_.add("SiteA", "Specify site(s) which represent 'A' in the interaction A-B-C", - selectA_->speciesSites(), selectA_->axesRequired()); - keywords_.add("SiteB", "Specify site(s) which represent 'B' in the interaction A-B-C", - selectB_->speciesSites(), selectB_->axesRequired()); - keywords_.add("SiteC", "Specify site(s) which represent 'C' in the interaction A-B-C", - selectC_->speciesSites(), selectC_->axesRequired()); + keywords_.add("SiteA", "Specify site(s) which represent 'A' in the interaction A-B-C", a_); + keywords_.add("SiteB", "Specify site(s) which represent 'B' in the interaction A-B-C", b_); + keywords_.add("SiteC", "Specify site(s) which represent 'C' in the interaction A-B-C", c_); keywords_.setOrganisation("Options", "Ranges", "Ranges over which to bin quantities from the calculation."); keywords_.add("RangeAB", "Range (min, max, binwidth) of A-B distance binning", rangeAB_, @@ -167,16 +46,16 @@ AngleModule::AngleModule() : Module(ModuleTypes::Angle), analyser_(ProcedureNode keywords_.setOrganisation("Export"); keywords_.add("ExportAB", "File format and file name under which to save calculated A-B RDF data", - processAB_->exportFileAndFormat(), "EndExportAB"); + exportFileAndFormatAB_, "EndExportAB"); keywords_.add("ExportBC", "File format and file name under which to save calculated B-C RDF data", - processBC_->exportFileAndFormat(), "EndExportBC"); + exportFileAndFormatBC_, "EndExportBC"); keywords_.add("ExportAngle", "File format and file name under which to save calculated A-B-C angle histogram", - processAngle_->exportFileAndFormat(), "EndExportAngle"); + exportFileAndFormatAngle_, "EndExportAngle"); keywords_.add("ExportDAngleAB", "File format and file name under which to save calculated (A-B)-C distance-angle map", - processDAngleAB_->exportFileAndFormat(), "EndExportDAngleAB"); + exportFileAndFormatDAngleAB_, "EndExportDAngleAB"); keywords_.add("ExportDAngleBC", "File format and file name under which to save calculated A-(B-C) distance-angle map", - processDAngleBC_->exportFileAndFormat(), "EndExportDAngleBC"); + exportFileAndFormatDAngleBC_, "EndExportDAngleBC"); } diff --git a/src/modules/angle/angle.h b/src/modules/angle/angle.h index dd264f6ad3..ff4bc12e0e 100644 --- a/src/modules/angle/angle.h +++ b/src/modules/angle/angle.h @@ -3,20 +3,9 @@ #pragma once +#include "io/export/data1D.h" +#include "io/export/data2D.h" #include "module/module.h" -#include "procedure/procedure.h" - -// Forward Declarations -class CalculateAngleProcedureNode; -class Collect1DProcedureNode; -class Collect2DProcedureNode; -class Collect3DProcedureNode; -class OperateExpressionProcedureNode; -class Process1DProcedureNode; -class Process2DProcedureNode; -class Process3DProcedureNode; -class SelectProcedureNode; -class SpeciesSite; // Calculate Angle Module class AngleModule : public Module @@ -45,42 +34,11 @@ class AngleModule : public Module Vec3 angleRange_{0.0, 180.0, 1.0}; // Whether the angular range should be considered symmetric about 90 bool symmetric_{false}; - // Analysis procedure to be run - Procedure analyser_; - // SelectNode for site A - std::shared_ptr selectA_; - // SelectNode for site B - std::shared_ptr selectB_; - // SelectNode for site C - std::shared_ptr selectC_; - // CalculateAngle node for A-B-C angle - std::shared_ptr calculateAngle_; - // Collect1DNode for A-B RDF - std::shared_ptr collectAB_; - // Collect1DNode for B-C RDF - std::shared_ptr collectBC_; - // Collect1DNode for A-B-C angle histogram - std::shared_ptr collectABC_; - // Collect2DNode for (A-B)-C distance-angle data - std::shared_ptr collectDAngleAB_; - // Collect2DNode for A-(B-C) distance-angle data - std::shared_ptr collectDAngleBC_; - // Collect3DNode for A-B vs B-C vs A-B-C distance-distance-angle data - std::shared_ptr collectDDA_; - // Process1DNode for A-B RDF - std::shared_ptr processAB_; - // Process1DNode for B-c RDF - std::shared_ptr processBC_; - // Process1DNode for A-B-C angle histogram - std::shared_ptr processAngle_; - // Process2DNode for (A-B)-C distance-angle data - std::shared_ptr processDAngleAB_; - // Process2DNode for A-(B-C) distance-angle data - std::shared_ptr processDAngleBC_; - // Normalisation expressions for (A-B)-C and A-(B-C) maps - std::shared_ptr dAngleABNormalisationExpression_, dAngleBCNormalisationExpression_; - // Process3DNode for A-B vs B-C vs A-B-C distance-distance-angle data - std::shared_ptr processDDA_; + // Target SpeciesSite definitions + std::vector a_, b_, c_; + // Export targets + Data1DExportFileFormat exportFileAndFormatAB_, exportFileAndFormatBC_, exportFileAndFormatAngle_; + Data2DExportFileFormat exportFileAndFormatDAngleAB_, exportFileAndFormatDAngleBC_; /* * Processing diff --git a/src/modules/angle/gui/angleWidgetFuncs.cpp b/src/modules/angle/gui/angleWidgetFuncs.cpp index eebba7ba83..3b232f35dc 100644 --- a/src/modules/angle/gui/angleWidgetFuncs.cpp +++ b/src/modules/angle/gui/angleWidgetFuncs.cpp @@ -95,29 +95,26 @@ void AngleModuleWidget::updateControls(const Flags &u // Calculated A...B RDF if (rdfABGraph_->renderables().empty()) - rdfABGraph_->createRenderable(fmt::format("{}//Process1D//RDF(AB)", module_->name()), "A...B g(r)") + rdfABGraph_->createRenderable(fmt::format("{}//RDF(AB)", module_->name()), "A...B g(r)") ->setColour(StockColours::BlueStockColour); // Calculated B...C RDF if (rdfBCGraph_->renderables().empty()) - rdfBCGraph_->createRenderable(fmt::format("{}//Process1D//RDF(BC)", module_->name()), "B...C g(r)") + rdfBCGraph_->createRenderable(fmt::format("{}//RDF(BC)", module_->name()), "B...C g(r)") ->setColour(StockColours::BlueStockColour); // Calculated angle histogram if (angleGraph_->renderables().empty()) - angleGraph_ - ->createRenderable(fmt::format("{}//Process1D//Angle(ABC)", module_->name()), "A-B...C Angle") + angleGraph_->createRenderable(fmt::format("{}//Angle(ABC)", module_->name()), "A-B...C Angle") ->setColour(StockColours::RedStockColour); // Calculated (A-B)-C distance-angle map if (dAngleABGraph_->renderables().empty()) - dAngleABGraph_->createRenderable(fmt::format("{}//Process2D//DAngle((A-B)-C)", module_->name()), - "A-B vs A-B-C"); + dAngleABGraph_->createRenderable(fmt::format("{}//DAngle((A-B)-C)", module_->name()), "A-B vs A-B-C"); // Calculated A-(B-C) distance-angle map if (dAngleBCGraph_->renderables().empty()) - dAngleBCGraph_->createRenderable(fmt::format("{}//Process2D//DAngle(A-(B-C))", module_->name()), - "B-C vs A-B-C"); + dAngleBCGraph_->createRenderable(fmt::format("{}//DAngle(A-(B-C))", module_->name()), "B-C vs A-B-C"); rdfABGraph_->validateRenderables(dissolve_.processingModuleData()); rdfBCGraph_->validateRenderables(dissolve_.processingModuleData()); diff --git a/src/modules/angle/process.cpp b/src/modules/angle/process.cpp index 814882591e..687bfe963d 100644 --- a/src/modules/angle/process.cpp +++ b/src/modules/angle/process.cpp @@ -1,16 +1,18 @@ // SPDX-License-Identifier: GPL-3.0-or-later // Copyright (c) 2024 Team Dissolve and contributors -#include "expression/variable.h" +#include "analyser/dataExporter.h" +#include "analyser/dataNormaliser1D.h" +#include "analyser/dataNormaliser2D.h" +#include "analyser/dataNormaliser3D.h" +#include "analyser/siteSelector.h" #include "main/dissolve.h" +#include "math/histogram1D.h" +#include "math/histogram2D.h" +#include "math/histogram3D.h" +#include "math/range.h" #include "module/context.h" #include "modules/angle/angle.h" -#include "procedure/nodes/calculateAngle.h" -#include "procedure/nodes/collect1D.h" -#include "procedure/nodes/collect2D.h" -#include "procedure/nodes/collect3D.h" -#include "procedure/nodes/operateExpression.h" -#include "procedure/nodes/select.h" // Run main processing Module::ExecutionResult AngleModule::process(ModuleContext &moduleContext) @@ -22,45 +24,210 @@ Module::ExecutionResult AngleModule::process(ModuleContext &moduleContext) return ExecutionResult::Failed; } - // Ensure any parameters in our nodes are set correctly - selectB_->setDistanceReferenceSite(selectA_); - selectB_->setInclusiveDistanceRange({rangeAB_.x, rangeAB_.y}); - selectC_->setDistanceReferenceSite(selectB_); - selectC_->setInclusiveDistanceRange({rangeBC_.x, rangeBC_.y}); - calculateAngle_->keywords().set("Symmetric", symmetric_); - dAngleABNormalisationExpression_->setExpression( - fmt::format("{} * value/sin(toRad(y))/sin(toRad(yDelta))", symmetric_ ? 1.0 : 2.0)); - dAngleBCNormalisationExpression_->setExpression( - fmt::format("{} * value/sin(toRad(y))/sin(toRad(yDelta))", symmetric_ ? 1.0 : 2.0)); - collectDDA_->keywords().set("RangeX", rangeAB_); - collectDDA_->keywords().set("RangeY", rangeBC_); - collectDDA_->keywords().set("RangeZ", angleRange_); - collectAB_->keywords().set("RangeX", rangeAB_); - collectBC_->keywords().set("RangeX", rangeBC_); - collectABC_->keywords().set("RangeX", angleRange_); - collectDAngleAB_->keywords().set("RangeX", rangeAB_); - collectDAngleAB_->keywords().set("RangeY", angleRange_); - collectDAngleBC_->keywords().set("RangeX", rangeBC_); - collectDAngleBC_->keywords().set("RangeY", angleRange_); - if (excludeSameMoleculeAB_) - selectB_->keywords().set("ExcludeSameMolecule", ConstNodeVector{selectA_}); - else - selectB_->keywords().set("ExcludeSameMolecule", ConstNodeVector{}); - if (excludeSameMoleculeBC_) - selectC_->keywords().set("ExcludeSameMolecule", ConstNodeVector{selectB_}); - else - selectC_->keywords().set("ExcludeSameMolecule", ConstNodeVector{}); - if (excludeSameSiteAC_) - selectC_->keywords().set("ExcludeSameSite", ConstNodeVector{selectA_}); - else - selectC_->keywords().set("ExcludeSameSite", ConstNodeVector{}); - - // Execute the analysis - if (!analyser_.execute({moduleContext.dissolve(), targetConfiguration_, name()})) + auto &processingData = moduleContext.dissolve().processingModuleData(); + + // Select site A + SiteSelector a(targetConfiguration_, a_); + + // Select site B + SiteSelector b(targetConfiguration_, b_); + + // Select site C + SiteSelector c(targetConfiguration_, c_); + + // RDF(A-B) + auto [rAB, rABStatus] = processingData.realiseIf("rAB", name(), GenericItem::InRestartFileFlag); + if (rABStatus == GenericItem::ItemStatus::Created) + rAB.initialise(rangeAB_.x, rangeAB_.y, rangeAB_.z); + + // RDF(B-C) + auto [rBC, rBCStatus] = processingData.realiseIf("rBC", name(), GenericItem::InRestartFileFlag); + if (rBCStatus == GenericItem::ItemStatus::Created) + rBC.initialise(rangeBC_.x, rangeBC_.y, rangeBC_.z); + + // Angle(A-B-C) + auto [aABC, aABCStatus] = processingData.realiseIf("aABC", name(), GenericItem::InRestartFileFlag); + if (aABCStatus == GenericItem::ItemStatus::Created) + aABC.initialise(angleRange_.x, angleRange_.y, angleRange_.z); + + // DAngle((A-B)-C) + auto [dAngleAB, dAngleABStatus] = processingData.realiseIf("DAngleAB", name(), GenericItem::InRestartFileFlag); + if (dAngleABStatus == GenericItem::ItemStatus::Created) + dAngleAB.initialise(rangeAB_.x, rangeAB_.y, rangeAB_.z, angleRange_.x, angleRange_.y, angleRange_.z); + + // DAngle(A-(B-C)) + auto [dAngleBC, dAngleBCStatus] = processingData.realiseIf("DAngleBC", name(), GenericItem::InRestartFileFlag); + if (dAngleBCStatus == GenericItem::ItemStatus::Created) + dAngleBC.initialise(rangeBC_.x, rangeBC_.y, rangeBC_.z, angleRange_.x, angleRange_.y, angleRange_.z); + + // DDAngle(A-B-C) + auto [dAngleABC, dAngleABCStatus] = + processingData.realiseIf("DAngleABC", name(), GenericItem::InRestartFileFlag); + if (dAngleABCStatus == GenericItem::ItemStatus::Created) + dAngleABC.initialise(rangeAB_.x, rangeAB_.y, rangeAB_.z, rangeBC_.x, rangeBC_.y, rangeBC_.z, angleRange_.x, + angleRange_.y, angleRange_.z); + + rAB.zeroBins(); + rBC.zeroBins(); + aABC.zeroBins(); + dAngleAB.zeroBins(); + dAngleBC.zeroBins(); + dAngleABC.zeroBins(); + + auto nAAvailable = a.sites().size(), nACumulative = a.sites().size(); + auto nASelections = 1; + auto nBAvailable = 0, nBCumulative = 0, nBSelections = 0; + auto nCAvailable = 0, nCCumulative = 0, nCSelections = 0; + + for (const auto &[siteA, indexA] : a.sites()) { - Messenger::error("Angle experienced problems with its analysis.\n"); - return ExecutionResult::Failed; + ++nBSelections; + for (const auto &[siteB, indexB] : b.sites()) + { + + if (excludeSameMoleculeAB_ && (siteB->molecule() == siteA->molecule())) + continue; + + auto distAB = targetConfiguration_->box()->minimumDistance(siteA->origin(), siteB->origin()); + + ++nBAvailable; + + if (!Range(rangeAB_.x, rangeAB_.y).contains(distAB)) + continue; + + rAB.bin(distAB); + + ++nBCumulative; + ++nCSelections; + + for (const auto &[siteC, indexC] : c.sites()) + { + + if (excludeSameMoleculeBC_ && (siteC->molecule() == siteB->molecule())) + continue; + + if (excludeSameSiteAC_ && (siteC == siteA)) + continue; + + ++nCAvailable; + + auto distBC = targetConfiguration_->box()->minimumDistance(siteB->origin(), siteC->origin()); + + if (!Range(rangeBC_.x, rangeBC_.y).contains(distBC)) + continue; + + ++nCCumulative; + + auto angle = targetConfiguration_->box()->angleInDegrees(siteA->origin(), siteB->origin(), siteC->origin()); + if (symmetric_ && angle > 90.0) + angle = 180.0 - angle; + + rBC.bin(distBC); + aABC.bin(angle); + dAngleAB.bin(distAB, angle); + dAngleBC.bin(distBC, angle); + dAngleABC.bin(distAB, distBC, angle); + } + } } + // Accumulate histograms + rAB.accumulate(); + rBC.accumulate(); + aABC.accumulate(); + dAngleAB.accumulate(); + dAngleBC.accumulate(); + dAngleABC.accumulate(); + + // RDF(A-B) + auto &normalisedAB = processingData.realise("RDF(AB)", name(), GenericItem::InRestartFileFlag); + normalisedAB = rAB.accumulatedData(); + DataNormaliser1D normaliserAB(normalisedAB); + // Normalise by A site population + normaliserAB.normaliseDivide(double(nACumulative) / nASelections); + // Normalise by B site population density + normaliserAB.normaliseDivide((double(nBCumulative) / nBSelections) / targetConfiguration_->box()->volume()); + // Normalise by spherical shell + normaliserAB.normaliseBySphericalShell(); + + // RDF(B-C) + auto &normalisedBC = processingData.realise("RDF(BC)", name(), GenericItem::InRestartFileFlag); + normalisedBC = rBC.accumulatedData(); + DataNormaliser1D normaliserBC(normalisedBC); + // Normalise by A site population + normaliserBC.normaliseDivide(double(nACumulative) / nASelections); + // Normalise by B site population + normaliserBC.normaliseDivide(double(nBCumulative) / nBSelections); + // Normalise by C site population density + normaliserBC.normaliseDivide((double(nCAvailable) / nCSelections) / targetConfiguration_->box()->volume()); + // Normalise by spherical shell + normaliserBC.normaliseBySphericalShell(); + + // Angle(A-B-C) + auto &normalisedAngle = processingData.realise("Angle(ABC)", name(), GenericItem::InRestartFileFlag); + normalisedAngle = aABC.accumulatedData(); + DataNormaliser1D normaliserAngle(normalisedAngle); + // Normalise by value / sin(x) + normaliserAngle.normalise([](const auto &x, const auto &xDelta, const auto &value) { return value / sin(x / DEGRAD); }); + // Normalise to 1.0 + normaliserAngle.normaliseTo(); + + // DAngle((A-B)-C) + auto &normalisedDAngleAB = processingData.realise("DAngle((A-B)-C)", name(), GenericItem::InRestartFileFlag); + normalisedDAngleAB = dAngleAB.accumulatedData(); + DataNormaliser2D normaliserDAngleAB(normalisedDAngleAB); + // Normalise by value / sin(y) / sin(yDelta) + normaliserDAngleAB.normalise([&](const auto &x, const auto &xDelta, const auto &y, const auto &yDelta, const auto &value) + { return (symmetric_ ? value : value * 2.0) / sin(y / DEGRAD) / sin(yDelta / DEGRAD); }); + // Normalise by A site population + normaliserDAngleAB.normaliseDivide(double(nACumulative) / nASelections); + // Normalise by C site population + normaliserDAngleAB.normaliseDivide(double(nCCumulative) / nCSelections); + // Normalise by B site population density + normaliserDAngleAB.normaliseDivide((double(nBAvailable) / nBSelections) / targetConfiguration_->box()->volume()); + // Normalise by spherical shell + normaliserDAngleAB.normaliseBySphericalShell(); + + // DAngle(A-(B-C)) + auto &normalisedDAngleBC = processingData.realise("DAngle(A-(B-C))", name(), GenericItem::InRestartFileFlag); + normalisedDAngleBC = dAngleBC.accumulatedData(); + DataNormaliser2D normaliserDAngleBC(normalisedDAngleBC); + // Normalise by value / sin(y) / sin(yDelta) + normaliserDAngleBC.normalise([&](const auto &x, const auto &xDelta, const auto &y, const auto &yDelta, const auto &value) + { return (symmetric_ ? value : value * 2.0) / sin(y / DEGRAD) / sin(yDelta / DEGRAD); }); + // Normalise by A site population + normaliserDAngleBC.normaliseDivide(double(nACumulative) / nASelections); + // Normalise by B site population + normaliserDAngleBC.normaliseDivide(double(nBCumulative) / nBSelections); + // Normalise by C site population density + normaliserDAngleBC.normaliseDivide((double(nCAvailable) / nCSelections) / targetConfiguration_->box()->volume()); + // Normalise by spherical shell + normaliserDAngleBC.normaliseBySphericalShell(); + + // Save RDF(A-B) data? + if (!DataExporter::exportData(normalisedAB, exportFileAndFormatAB_, + moduleContext.processPool())) + return ExecutionResult::Failed; + + // Save RDF(B-C) data? + if (!DataExporter::exportData(normalisedBC, exportFileAndFormatBC_, + moduleContext.processPool())) + return ExecutionResult::Failed; + + // Save Angle(A-B-C) data? + if (!DataExporter::exportData(normalisedAngle, exportFileAndFormatAngle_, + moduleContext.processPool())) + return ExecutionResult::Failed; + + // Save DAngle((A-B)-C) data? + if (!DataExporter::exportData(normalisedDAngleAB, exportFileAndFormatDAngleAB_, + moduleContext.processPool())) + return ExecutionResult::Failed; + + // Save DAngle(A-(B-C)) data? + if (!DataExporter::exportData(normalisedDAngleBC, exportFileAndFormatDAngleBC_, + moduleContext.processPool())) + return ExecutionResult::Failed; + return ExecutionResult::Success; } diff --git a/src/modules/axisAngle/axisAngle.cpp b/src/modules/axisAngle/axisAngle.cpp index f96800ba4a..1ebf7859df 100644 --- a/src/modules/axisAngle/axisAngle.cpp +++ b/src/modules/axisAngle/axisAngle.cpp @@ -7,86 +7,9 @@ #include "keywords/fileAndFormat.h" #include "keywords/speciesSiteVector.h" #include "keywords/vec3Double.h" -#include "procedure/nodes/calculateAxisAngle.h" -#include "procedure/nodes/calculateDistance.h" -#include "procedure/nodes/collect1D.h" -#include "procedure/nodes/collect2D.h" -#include "procedure/nodes/operateExpression.h" -#include "procedure/nodes/operateNormalise.h" -#include "procedure/nodes/operateNumberDensityNormalise.h" -#include "procedure/nodes/operateSitePopulationNormalise.h" -#include "procedure/nodes/operateSphericalShellNormalise.h" -#include "procedure/nodes/process1D.h" -#include "procedure/nodes/process2D.h" -#include "procedure/nodes/select.h" -AxisAngleModule::AxisAngleModule() : Module(ModuleTypes::AxisAngle), analyser_(ProcedureNode::AnalysisContext) +AxisAngleModule::AxisAngleModule() : Module(ModuleTypes::AxisAngle) { - // Select: Site 'A' - selectA_ = analyser_.createRootNode("A", std::vector{}, - ProcedureNode::NodeContext::AnalysisContext, true); - auto &forEachA = selectA_->branch()->get(); - - // -- Select: Site 'B' - selectB_ = forEachA.create("B", std::vector{}, - ProcedureNode::NodeContext::AnalysisContext, true); - selectB_->keywords().set("ExcludeSameMolecule", ConstNodeVector{selectA_}); - auto &forEachB = selectB_->branch()->get(); - - // -- -- Calculate: 'rAB' - auto calcDistance = forEachB.create({}, selectA_, selectB_); - - // -- -- Calculate: 'axisAngle' - calculateAxisAngle_ = - forEachB.create({}, selectA_, OrientedSite::XAxis, selectB_, OrientedSite::XAxis); - calculateAxisAngle_->keywords().set("Symmetric", symmetric_); - - // -- -- Collect2D: 'rAB vs axisAngle)' - collectDAngle_ = forEachB.create({}, calcDistance, calculateAxisAngle_, - ProcedureNode::AnalysisContext, distanceRange_.x, distanceRange_.y, - distanceRange_.z, angleRange_.x, angleRange_.y, angleRange_.z); - auto &subCollection = collectDAngle_->branch()->get(); - - // -- -- -- Collect1D: 'RDF(AB)' - collectDistance_ = subCollection.create({}, calcDistance, ProcedureNode::AnalysisContext, - distanceRange_.x, distanceRange_.y, distanceRange_.z); - - // -- -- -- Collect1D: 'ANGLE(axis-axis)' - collectAngle_ = subCollection.create({}, calculateAxisAngle_, ProcedureNode::AnalysisContext, - angleRange_.x, angleRange_.y, angleRange_.z); - - // Process1D: 'RDF(AB)' - processDistance_ = - analyser_.createRootNode("RDF(AB)", collectDistance_, ProcedureNode::OperateContext); - processDistance_->keywords().set("LabelValue", std::string("g(r)")); - processDistance_->keywords().set("LabelX", std::string("r, \\symbol{Angstrom}")); - - auto &rdfNormalisation = processDistance_->branch()->get(); - rdfNormalisation.create({}, ConstNodeVector{selectA_}); - rdfNormalisation.create({}, ConstNodeVector{selectB_}); - rdfNormalisation.create({}); - - // Process1D: 'ANGLE(axis)' - processAngle_ = analyser_.createRootNode("AxisAngle(AB)", collectAngle_); - processAngle_->keywords().set("LabelValue", std::string("Normalised Frequency")); - processAngle_->keywords().set("LabelX", std::string("\\symbol{theta}, \\symbol{degrees}")); - auto &angleNormalisation = processAngle_->branch()->get(); - angleNormalisation.create({}, "value/sin(toRad(x))"); - angleNormalisation.create({}, 1.0); - - // Process2D: 'DAngle' - processDAngle_ = analyser_.createRootNode("DAxisAngle", collectDAngle_); - processDAngle_->keywords().set("LabelX", std::string("\\it{r}, \\symbol{Angstrom}")); - processDAngle_->keywords().set("LabelY", std::string("\\symbol{theta}, \\symbol{degrees}")); - processDAngle_->keywords().set("LabelValue", std::string("g(r)")); - auto &dAngleNormalisation = processDAngle_->branch()->get(); - dAngleNormalisationExpression_ = dAngleNormalisation.create( - {}, fmt::format("{} * value/sin(toRad(y))/sin(toRad(yDelta))", symmetric_ ? 1.0 : 2.0)); - dAngleNormalisation.create({}, - ConstNodeVector({selectA_})); - dAngleNormalisation.create({}, ConstNodeVector{selectB_}); - dAngleNormalisation.create({}); - /* * Keywords */ @@ -94,13 +17,11 @@ AxisAngleModule::AxisAngleModule() : Module(ModuleTypes::AxisAngle), analyser_(P keywords_.addTarget("Configuration", "Set target configuration for the module", targetConfiguration_); keywords_.setOrganisation("Options", "Sites", "Specify sites defining the axis angle interaction A-B...C."); - keywords_.add("SiteA", "Specify site(s) which represent 'A' in the interaction A-B...C", - selectA_->speciesSites(), selectA_->axesRequired()); - keywords_.add>("AxisA", "Axis to use from site A", calculateAxisAngle_->axis(0), + keywords_.add("SiteA", "Specify site(s) which represent 'A' in the interaction A-B...C", a_); + keywords_.add>("AxisA", "Axis to use from site A", axisA_, OrientedSite::siteAxis()); - keywords_.add("SiteB", "Specify site(s) which represent 'B' in the interaction A-B...C", - selectB_->speciesSites(), selectB_->axesRequired()); - keywords_.add>("AxisB", "Axis to use from site B", calculateAxisAngle_->axis(1), + keywords_.add("SiteB", "Specify site(s) which represent 'B' in the interaction A-B...C", b_); + keywords_.add>("AxisB", "Axis to use from site B", axisB_, OrientedSite::siteAxis()); keywords_.setOrganisation("Options", "Ranges", "Ranges over which to bin quantities from the calculation."); @@ -120,11 +41,11 @@ AxisAngleModule::AxisAngleModule() : Module(ModuleTypes::AxisAngle), analyser_(P keywords_.setOrganisation("Export"); keywords_.add("ExportRDF", "File format and file name under which to save calculated B-C RDF", - processDistance_->exportFileAndFormat(), "EndExportRDF"); + exportFileAndFormatRDF_, "EndExportRDF"); keywords_.add( "ExportAngle", "File format and file name under which to save calculated A-B...C angle histogram to disk", - processAngle_->exportFileAndFormat(), "EndExportAngle"); + exportFileAndFormatAxisAngle_, "EndExportAngle"); keywords_.add("ExportDAngle", "File format and file name under which to save calculated A-B...C angle map to disk", - processDAngle_->exportFileAndFormat(), "EndExportDAngle"); + exportFileAndFormatDAxisAngle_, "EndExportDAngle"); } diff --git a/src/modules/axisAngle/axisAngle.h b/src/modules/axisAngle/axisAngle.h index 8f042c7ad9..582f9e0750 100644 --- a/src/modules/axisAngle/axisAngle.h +++ b/src/modules/axisAngle/axisAngle.h @@ -3,17 +3,12 @@ #pragma once +#include "classes/site.h" +#include "io/export/data1D.h" +#include "io/export/data2D.h" #include "module/module.h" -#include "procedure/procedure.h" // Forward Declarations -class CalculateAxisAngleProcedureNode; -class Collect1DProcedureNode; -class Collect2DProcedureNode; -class OperateExpressionProcedureNode; -class Process1DProcedureNode; -class Process2DProcedureNode; -class SelectProcedureNode; class SpeciesSite; // Calculate Axis Angle Module @@ -37,28 +32,16 @@ class AxisAngleModule : public Module Vec3 angleRange_{0.0, 180.0, 10.0}; // Whether the angular range should be considered symmetric about 90 bool symmetric_{false}; - // Analysis procedure to be run - Procedure analyser_; - // SelectNode for site A - std::shared_ptr selectA_; - // SelectNode for site B - std::shared_ptr selectB_; - // CalculateAxisAngle node - std::shared_ptr calculateAxisAngle_; - // Collect1DNode for A-B RDF - std::shared_ptr collectDistance_; - // Collect1DNode for A-B angle histogram - std::shared_ptr collectAngle_; - // Collect2DNode for distance-angle data - std::shared_ptr collectDAngle_; - // Process1DNode for A-B RDF - std::shared_ptr processDistance_; - // Process1DNode for A-B-C angle histogram - std::shared_ptr processAngle_; - // Process2DNode for distance-angle data - std::shared_ptr processDAngle_; - // Normalisation expression for distance-angle map - std::shared_ptr dAngleNormalisationExpression_; + // Target SpeciesSite definitions + std::vector a_, b_; + // Axes to use for sites + OrientedSite::SiteAxis axisA_{OrientedSite::SiteAxis::XAxis}, axisB_{OrientedSite::SiteAxis::XAxis}; + // Export file and format for RDF + Data1DExportFileFormat exportFileAndFormatRDF_; + // Export file and format for AxisAngle + Data1DExportFileFormat exportFileAndFormatAxisAngle_; + // Export file and format for DAxisAngle + Data2DExportFileFormat exportFileAndFormatDAxisAngle_; /* * Processing diff --git a/src/modules/axisAngle/gui/axisAngleWidgetFuncs.cpp b/src/modules/axisAngle/gui/axisAngleWidgetFuncs.cpp index e1d6c0c679..bcc87f58a3 100644 --- a/src/modules/axisAngle/gui/axisAngleWidgetFuncs.cpp +++ b/src/modules/axisAngle/gui/axisAngleWidgetFuncs.cpp @@ -68,19 +68,18 @@ void AxisAngleModuleWidget::updateControls(const Flagsrenderables().empty()) - rdfGraph_->createRenderable(fmt::format("{}//Process1D//RDF(AB)", module_->name()), "A...B g(r)") + rdfGraph_->createRenderable(fmt::format("{}//RDF(AB)", module_->name()), "A...B g(r)") ->setColour(StockColours::BlueStockColour); // Calculated angle histogram if (angleGraph_->renderables().empty()) - angleGraph_ - ->createRenderable(fmt::format("{}//Process1D//AxisAngle(AB)", module_->name()), "Axis Angle") + angleGraph_->createRenderable(fmt::format("{}//AxisAngle(AB)", module_->name()), "Axis Angle") ->setColour(StockColours::RedStockColour); // Calculated distance-angle map if (dAngleGraph_->renderables().empty()) { - auto x = dAngleGraph_->createRenderable(fmt::format("{}//Process2D//DAxisAngle", module_->name()), + auto x = dAngleGraph_->createRenderable(fmt::format("{}//DAxisAngle", module_->name()), "A...B vs Axis Angle"); x->colour().setStyle(ColourDefinition::HSVGradientStyle); } diff --git a/src/modules/axisAngle/process.cpp b/src/modules/axisAngle/process.cpp index 8bac9ba8c3..36c1ca7f5d 100644 --- a/src/modules/axisAngle/process.cpp +++ b/src/modules/axisAngle/process.cpp @@ -1,15 +1,16 @@ // SPDX-License-Identifier: GPL-3.0-or-later // Copyright (c) 2024 Team Dissolve and contributors +#include "analyser/dataExporter.h" +#include "analyser/dataNormaliser1D.h" +#include "analyser/dataNormaliser2D.h" +#include "analyser/siteSelector.h" #include "base/sysFunc.h" #include "main/dissolve.h" +#include "math/histogram1D.h" +#include "math/histogram2D.h" #include "module/context.h" #include "modules/axisAngle/axisAngle.h" -#include "procedure/nodes/calculateAxisAngle.h" -#include "procedure/nodes/collect1D.h" -#include "procedure/nodes/collect2D.h" -#include "procedure/nodes/operateExpression.h" -#include "procedure/nodes/select.h" // Run main processing Module::ExecutionResult AxisAngleModule::process(ModuleContext &moduleContext) @@ -21,25 +22,109 @@ Module::ExecutionResult AxisAngleModule::process(ModuleContext &moduleContext) return ExecutionResult::Failed; } - // Ensure any parameters in our nodes are set correctly - calculateAxisAngle_->keywords().set("Symmetric", symmetric_); - dAngleNormalisationExpression_->setExpression( - fmt::format("{} * value/sin(toRad(y))/sin(toRad(yDelta))", symmetric_ ? 1.0 : 2.0)); - collectDistance_->keywords().set("RangeX", distanceRange_); - collectAngle_->keywords().set("RangeX", angleRange_); - collectDAngle_->keywords().set("RangeX", distanceRange_); - collectDAngle_->keywords().set("RangeY", angleRange_); - if (excludeSameMolecule_) - selectB_->keywords().set("ExcludeSameMolecule", ConstNodeVector{selectA_}); - else - selectB_->keywords().set("ExcludeSameMolecule", ConstNodeVector{}); - - // Execute the analysis - if (!analyser_.execute({moduleContext.dissolve(), targetConfiguration_, name()})) + auto &processingData = moduleContext.dissolve().processingModuleData(); + + // Select site A + SiteSelector a(targetConfiguration_, a_); + + // Select site B + SiteSelector b(targetConfiguration_, b_); + + // RDF(A-B) + auto [rAB, rABStatus] = processingData.realiseIf("rAB", name(), GenericItem::InRestartFileFlag); + if (rABStatus == GenericItem::ItemStatus::Created) + rAB.initialise(distanceRange_.x, distanceRange_.y, distanceRange_.z); + + // AxisAngle(A-B) + auto [aAB, aABStatus] = processingData.realiseIf("aAB", name(), GenericItem::InRestartFileFlag); + if (aABStatus == GenericItem::ItemStatus::Created) + aAB.initialise(angleRange_.x, angleRange_.y, angleRange_.z); + + // DAxisAngle(A-B) + auto [dAxisAngle, dAxisAngleStatus] = + processingData.realiseIf("dAxisAngle", name(), GenericItem::InRestartFileFlag); + if (dAxisAngleStatus == GenericItem::ItemStatus::Created) + dAxisAngle.initialise(distanceRange_.x, distanceRange_.y, distanceRange_.z, angleRange_.x, angleRange_.y, + angleRange_.z); + + rAB.zeroBins(); + aAB.zeroBins(); + dAxisAngle.zeroBins(); + + for (const auto &[siteA, indexA] : a.sites()) { - Messenger::error("AxisAngle experienced problems with its analysis.\n"); - return ExecutionResult::Failed; + for (const auto &[siteB, indexB] : b.sites()) + { + if (excludeSameMolecule_ && (siteA->molecule() == siteB->molecule())) + continue; + + auto distanceAB = targetConfiguration_->box()->minimumDistance(siteA->origin(), siteB->origin()); + auto axisAngle = Box::angleInDegrees(siteA->axes().columnAsVec3(axisA_), siteB->axes().columnAsVec3(axisB_)); + if (symmetric_ && axisAngle > 90.0) + axisAngle = 180.0 - axisAngle; + + if (dAxisAngle.bin(distanceAB, axisAngle)) + { + rAB.bin(distanceAB); + aAB.bin(axisAngle); + } + } } + // Accumulate histograms + rAB.accumulate(); + aAB.accumulate(); + dAxisAngle.accumulate(); + + // RDF(A-B) + auto &rABNormalised = processingData.realise("RDF(AB)", name(), GenericItem::InRestartFileFlag); + rABNormalised = rAB.accumulatedData(); + DataNormaliser1D rABNormaliser(rABNormalised); + + // Normalise by A site population + rABNormaliser.normaliseDivide(double(a.sites().size())); + // Normalise by B site population density + rABNormaliser.normaliseDivide(double(b.sites().size()) / targetConfiguration_->box()->volume()); + // Normalise by spherical shell + rABNormaliser.normaliseBySphericalShell(); + + // AxisAngle(A-B) + auto &aABNormalised = processingData.realise("AxisAngle(AB)", name(), GenericItem::InRestartFileFlag); + aABNormalised = aAB.accumulatedData(); + DataNormaliser1D aABNormaliser(aABNormalised); + // Normalise by value / sin(x) + aABNormaliser.normalise([](const auto &x, const auto &xDelta, const auto &value) { return value / sin(x / DEGRAD); }); + // Normalise to 1.0 + aABNormaliser.normaliseTo(); + + // DAxisAngle(A-B) + auto &dAxisAngleNormalised = processingData.realise("DAxisAngle", name(), GenericItem::InRestartFileFlag); + dAxisAngleNormalised = dAxisAngle.accumulatedData(); + DataNormaliser2D dAxisAngleNormaliser(dAxisAngleNormalised); + // Normalise by value / sin(y) / sin(yDelta) + dAxisAngleNormaliser.normalise([&](const auto &x, const auto &xDelta, const auto &y, const auto &yDelta, const auto &value) + { return (symmetric_ ? value : value * 2.0) / sin(y / DEGRAD) / sin(yDelta / DEGRAD); }); + // Normalise by A site population + dAxisAngleNormaliser.normaliseDivide(double(a.sites().size())); + // Normalise by B site population density + dAxisAngleNormaliser.normaliseDivide(double(b.sites().size()) / targetConfiguration_->box()->volume()); + // Normalise by spherical shell + dAxisAngleNormaliser.normaliseBySphericalShell(); + + // Save RDF(A-B) data? + if (!DataExporter::exportData(rABNormalised, exportFileAndFormatRDF_, + moduleContext.processPool())) + return ExecutionResult::Failed; + + // Save AxisAngle(A-B) data? + if (!DataExporter::exportData(aABNormalised, exportFileAndFormatAxisAngle_, + moduleContext.processPool())) + return ExecutionResult::Failed; + + // Save DAxisAngle(A-B) data? + if (!DataExporter::exportData(dAxisAngleNormalised, exportFileAndFormatDAxisAngle_, + moduleContext.processPool())) + return ExecutionResult::Failed; + return ExecutionResult::Success; } diff --git a/src/modules/dAngle/dAngle.cpp b/src/modules/dAngle/dAngle.cpp index 73824b6399..30b7ece9ad 100644 --- a/src/modules/dAngle/dAngle.cpp +++ b/src/modules/dAngle/dAngle.cpp @@ -7,87 +7,9 @@ #include "keywords/fileAndFormat.h" #include "keywords/speciesSiteVector.h" #include "keywords/vec3Double.h" -#include "procedure/nodes/calculateAngle.h" -#include "procedure/nodes/calculateDistance.h" -#include "procedure/nodes/collect1D.h" -#include "procedure/nodes/collect2D.h" -#include "procedure/nodes/operateExpression.h" -#include "procedure/nodes/operateNormalise.h" -#include "procedure/nodes/operateNumberDensityNormalise.h" -#include "procedure/nodes/operateSitePopulationNormalise.h" -#include "procedure/nodes/operateSphericalShellNormalise.h" -#include "procedure/nodes/process1D.h" -#include "procedure/nodes/process2D.h" -#include "procedure/nodes/select.h" -DAngleModule::DAngleModule() : Module(ModuleTypes::DAngle), analyser_(ProcedureNode::AnalysisContext) +DAngleModule::DAngleModule() : Module(ModuleTypes::DAngle) { - // Select: Site 'A' - selectA_ = analyser_.createRootNode("A"); - auto &forEachA = selectA_->branch()->get(); - - // -- Select: Site 'B' - selectB_ = forEachA.create("B"); - selectB_->keywords().set("SameMoleculeAsSite", selectA_); - auto &forEachB = selectB_->branch()->get(); - - // -- -- Select: Site 'C' - selectC_ = forEachB.create("C"); - selectC_->keywords().set("ExcludeSameMolecule", ConstNodeVector{selectA_}); - auto &forEachC = selectC_->branch()->get(); - - // -- -- -- Calculate: 'rBC' - auto calcDistance = forEachC.create({}, selectB_, selectC_); - - // -- -- -- Calculate: 'aABC' - calculateAngle_ = forEachC.create({}, selectA_, selectB_, selectC_); - calculateAngle_->keywords().set("Symmetric", symmetric_); - - // -- -- -- Collect2D: 'Distance-Angle(B...C vs A-B...C)' - collectDAngle_ = forEachC.create({}, calcDistance, calculateAngle_, ProcedureNode::AnalysisContext, - 0.0, 10.0, 0.05, 0.0, 180.0, 1.0); - auto &subCollection = collectDAngle_->branch()->get(); - - // -- -- -- -- Collect1D: 'RDF(BC)' - collectDistance_ = - subCollection.create({}, calcDistance, ProcedureNode::AnalysisContext, 0.0, 10.0, 0.05); - - // -- -- -- -- Collect1D: 'ANGLE(ABC)' - collectAngle_ = - subCollection.create({}, calculateAngle_, ProcedureNode::AnalysisContext, 0.0, 180.0, 1.0); - - // Process1D: 'RDF(BC)' - processDistance_ = analyser_.createRootNode("RDF(BC)", collectDistance_); - processDistance_->keywords().set("LabelValue", std::string("g(r)")); - processDistance_->keywords().set("LabelX", std::string("r, \\symbol{Angstrom}")); - - auto &rdfNormalisation = processDistance_->branch()->get(); - rdfNormalisation.create( - {}, ConstNodeVector{selectA_, selectB_}); - rdfNormalisation.create({}, ConstNodeVector{selectC_}); - rdfNormalisation.create({}); - - // Process1D: 'ANGLE(ABC)' - processAngle_ = analyser_.createRootNode("Angle(ABC)", collectAngle_); - processAngle_->keywords().set("LabelValue", std::string("Normalised Frequency")); - processAngle_->keywords().set("LabelX", std::string("\\symbol{theta}, \\symbol{degrees}")); - auto &angleNormalisation = processAngle_->branch()->get(); - angleNormalisation.create({}, "value/sin(toRad(x))"); - angleNormalisation.create({}, 1.0); - - // Process2D: 'DAngle' - processDAngle_ = analyser_.createRootNode("DAngle(A-BC)", collectDAngle_); - processDAngle_->keywords().set("LabelValue", std::string("g(r)")); - processDAngle_->keywords().set("LabelX", std::string("r, \\symbol{Angstrom}")); - processDAngle_->keywords().set("LabelY", std::string("\\symbol{theta}, \\symbol{degrees}")); - auto &dAngleNormalisation = processDAngle_->branch()->get(); - dAngleNormalisationExpression_ = dAngleNormalisation.create( - {}, fmt::format("{} * value/sin(toRad(y))/sin(toRad(yDelta))", symmetric_ ? 1.0 : 2.0)); - dAngleNormalisation.create({}, - ConstNodeVector({selectA_})); - dAngleNormalisation.create({}, ConstNodeVector{selectB_}); - dAngleNormalisation.create({}); - /* * Keywords */ @@ -95,12 +17,9 @@ DAngleModule::DAngleModule() : Module(ModuleTypes::DAngle), analyser_(ProcedureN keywords_.addTarget("Configuration", "Set target configuration for the module", targetConfiguration_); keywords_.setOrganisation("Options", "Sites", "Specify sites defining the angle interaction A-B...C"); - keywords_.add("SiteA", "Add site(s) which represent 'A' in the interaction A-B...C", - selectA_->speciesSites(), selectA_->axesRequired()); - keywords_.add("SiteB", "Add site(s) which represent 'B' in the interaction A-B...C", - selectB_->speciesSites(), selectB_->axesRequired()); - keywords_.add("SiteC", "Add site(s) which represent 'C' in the interaction A-B...C", - selectC_->speciesSites(), selectC_->axesRequired()); + keywords_.add("SiteA", "Add site(s) which represent 'A' in the interaction A-B...C", a_); + keywords_.add("SiteB", "Add site(s) which represent 'B' in the interaction A-B...C", b_); + keywords_.add("SiteC", "Add site(s) which represent 'C' in the interaction A-B...C", c_); keywords_.setOrganisation("Options", "Ranges", "Ranges over which to bin quantities from the calculation."); keywords_.add("DistanceRange", "Range (min, max, binwidth) of distance binning", distanceRange_, @@ -115,11 +34,11 @@ DAngleModule::DAngleModule() : Module(ModuleTypes::DAngle), analyser_(ProcedureN keywords_.setOrganisation("Export"); keywords_.add("ExportRDF", "File format and file name under which to save calculated B-C RDF", - processDistance_->exportFileAndFormat(), "EndExportRDF"); + exportFileAndFormatRDF_, "EndExportRDF"); keywords_.add("ExportAngle", "File format and file name under which to save calculated A-B...C angle histogram", - processAngle_->exportFileAndFormat(), "EndExportAngle"); + exportFileAndFormatAngle_, "EndExportAngle"); keywords_.add("ExportDAngle", "File format and file name under which to save calculated A-B...C angle map", - processDAngle_->exportFileAndFormat(), "EndExportDAngle"); + exportFileAndFormatDAngle_, "EndExportDAngle"); } diff --git a/src/modules/dAngle/dAngle.h b/src/modules/dAngle/dAngle.h index ee6bd6c929..f44f0c3977 100644 --- a/src/modules/dAngle/dAngle.h +++ b/src/modules/dAngle/dAngle.h @@ -3,17 +3,11 @@ #pragma once +#include "io/export/data1D.h" +#include "io/export/data2D.h" #include "module/module.h" -#include "procedure/procedure.h" // Forward Declarations -class CalculateAngleProcedureNode; -class Collect1DProcedureNode; -class Collect2DProcedureNode; -class OperateExpressionProcedureNode; -class Process1DProcedureNode; -class Process2DProcedureNode; -class SelectProcedureNode; class SpeciesSite; // Calculate Distance-Angle Module @@ -37,30 +31,14 @@ class DAngleModule : public Module Vec3 angleRange_{0.0, 180.0, 1.0}; // Whether the angular range should be considered symmetric about 90 bool symmetric_{false}; - // Analysis procedure to be run - Procedure analyser_; - // SelectNode for site A - std::shared_ptr selectA_; - // SelectNode for site B - std::shared_ptr selectB_; - // SelectNode for site C - std::shared_ptr selectC_; - // CalculateAngle node - std::shared_ptr calculateAngle_; - // Collect1DNode for B-C RDF - std::shared_ptr collectDistance_; - // Collect1DNode for A-B-C angle histogram - std::shared_ptr collectAngle_; - // Collect2DNode for distance-angle data - std::shared_ptr collectDAngle_; - // Process1DNode for B-C RDF - std::shared_ptr processDistance_; - // Process1DNode for A-B-C angle histogram - std::shared_ptr processAngle_; - // Process2DNode for distance-angle data - std::shared_ptr processDAngle_; - // Normalisation expression for distance-angle map - std::shared_ptr dAngleNormalisationExpression_; + // Target SpeciesSite definitions + std::vector a_, b_, c_; + // Export file and format for RDF + Data1DExportFileFormat exportFileAndFormatRDF_; + // Export file and format for Angle + Data1DExportFileFormat exportFileAndFormatAngle_; + // Export file and format for DAngle + Data2DExportFileFormat exportFileAndFormatDAngle_; /* * Processing diff --git a/src/modules/dAngle/gui/dAngleWidgetFuncs.cpp b/src/modules/dAngle/gui/dAngleWidgetFuncs.cpp index 96205a37d9..fe63a19e09 100644 --- a/src/modules/dAngle/gui/dAngleWidgetFuncs.cpp +++ b/src/modules/dAngle/gui/dAngleWidgetFuncs.cpp @@ -65,19 +65,18 @@ void DAngleModuleWidget::updateControls(const Flags & // Calculated B...C RDF if (rdfGraph_->renderables().empty()) - rdfGraph_->createRenderable(fmt::format("{}//Process1D//RDF(BC)", module_->name()), "B...C g(r)") + rdfGraph_->createRenderable(fmt::format("{}//RDF(BC)", module_->name()), "B...C g(r)") ->setColour(StockColours::BlueStockColour); // Calculated angle histogram if (angleGraph_->renderables().empty()) - angleGraph_ - ->createRenderable(fmt::format("{}//Process1D//Angle(ABC)", module_->name()), "A-B...C Angle") + angleGraph_->createRenderable(fmt::format("{}//Angle(ABC)", module_->name()), "A-B...C Angle") ->setColour(StockColours::RedStockColour); // Calculated distance-angle map if (dAngleGraph_->renderables().empty()) { - auto x = dAngleGraph_->createRenderable(fmt::format("{}//Process2D//DAngle(A-BC)", module_->name()), + auto x = dAngleGraph_->createRenderable(fmt::format("{}//DAngle(A-BC)", module_->name()), "B...C vs A-B...C"); x->colour().setStyle(ColourDefinition::HSVGradientStyle); } diff --git a/src/modules/dAngle/process.cpp b/src/modules/dAngle/process.cpp index 3c9a551a00..75c5ef8de5 100644 --- a/src/modules/dAngle/process.cpp +++ b/src/modules/dAngle/process.cpp @@ -1,15 +1,16 @@ // SPDX-License-Identifier: GPL-3.0-or-later // Copyright (c) 2024 Team Dissolve and contributors +#include "analyser/dataExporter.h" +#include "analyser/dataNormaliser1D.h" +#include "analyser/dataNormaliser2D.h" +#include "analyser/siteSelector.h" #include "base/sysFunc.h" #include "main/dissolve.h" +#include "math/histogram1D.h" +#include "math/histogram2D.h" #include "module/context.h" #include "modules/dAngle/dAngle.h" -#include "procedure/nodes/calculateAngle.h" -#include "procedure/nodes/collect1D.h" -#include "procedure/nodes/collect2D.h" -#include "procedure/nodes/operateExpression.h" -#include "procedure/nodes/select.h" // Run main processing Module::ExecutionResult DAngleModule::process(ModuleContext &moduleContext) @@ -21,25 +22,131 @@ Module::ExecutionResult DAngleModule::process(ModuleContext &moduleContext) return ExecutionResult::Failed; } - // Ensure any parameters in our nodes are set correctly - calculateAngle_->keywords().set("Symmetric", symmetric_); - dAngleNormalisationExpression_->setExpression( - fmt::format("{} * value/sin(toRad(y))/sin(toRad(yDelta))", symmetric_ ? 1.0 : 2.0)); - collectDistance_->keywords().set("RangeX", distanceRange_); - collectAngle_->keywords().set("RangeX", angleRange_); - collectDAngle_->keywords().set("RangeX", distanceRange_); - collectDAngle_->keywords().set("RangeY", angleRange_); - if (excludeSameMolecule_) - selectC_->keywords().set("ExcludeSameMolecule", ConstNodeVector{selectA_}); - else - selectC_->keywords().set("ExcludeSameMolecule", ConstNodeVector{}); - - // Execute the analysis - if (!analyser_.execute({moduleContext.dissolve(), targetConfiguration_, name()})) + auto &processingData = moduleContext.dissolve().processingModuleData(); + + // Select site A + SiteSelector a(targetConfiguration_, a_); + + // Select site B + SiteSelector b(targetConfiguration_, b_); + + // Select site C + SiteSelector c(targetConfiguration_, c_); + + // RDF(B-C) + auto [rBC, rABStatus] = processingData.realiseIf("rBC", name(), GenericItem::InRestartFileFlag); + if (rABStatus == GenericItem::ItemStatus::Created) + rBC.initialise(distanceRange_.x, distanceRange_.y, distanceRange_.z); + + // Angle(A-B-C) + auto [aABC, aABCStatus] = processingData.realiseIf("aABC", name(), GenericItem::InRestartFileFlag); + if (aABCStatus == GenericItem::ItemStatus::Created) + aABC.initialise(angleRange_.x, angleRange_.y, angleRange_.z); + + // DAngle(A-BC) + auto [dAngle, dAngleStatus] = processingData.realiseIf("dAngle", name(), GenericItem::InRestartFileFlag); + if (dAngleStatus == GenericItem::ItemStatus::Created) + dAngle.initialise(distanceRange_.x, distanceRange_.y, distanceRange_.z, angleRange_.x, angleRange_.y, angleRange_.z); + + dAngle.zeroBins(); + rBC.zeroBins(); + aABC.zeroBins(); + + // Site statistics + auto nASelections = 1; + auto nAAvailable = a.sites().size(), nACumulative = a.sites().size(); + auto nBSelections = nAAvailable; + auto nBAvailable = 0, nBCumulative = 0; + auto nCSelections = 0, nCAvailable = 0, nCCumulative = 0; + + for (const auto &[siteA, indexA] : a.sites()) { - Messenger::error("CalculateDAngle experienced problems with its analysis.\n"); - return ExecutionResult::Failed; + for (const auto &[siteB, indexB] : b.sites()) + { + + if (siteB->molecule() != siteA->molecule()) + continue; + + ++nBCumulative; + ++nCSelections; + ++nBAvailable; + + for (const auto &[siteC, indexC] : c.sites()) + { + + if (excludeSameMolecule_ && (siteC->molecule() == siteB->molecule())) + continue; + + ++nCCumulative; + ++nCAvailable; + + auto distanceBC = targetConfiguration_->box()->minimumDistance(siteB->origin(), siteC->origin()); + auto angle = targetConfiguration_->box()->angleInDegrees(siteA->origin(), siteB->origin(), siteC->origin()); + if (symmetric_ && angle > 90.0) + angle = 180.0 - angle; + + if (dAngle.bin(distanceBC, angle)) + { + rBC.bin(distanceBC); + aABC.bin(angle); + } + } + } } + // Accumulate histograms + dAngle.accumulate(); + rBC.accumulate(); + aABC.accumulate(); + + // RDF(B-C) + auto &rBCNormalised = processingData.realise("RDF(BC)", name(), GenericItem::InRestartFileFlag); + rBCNormalised = rBC.accumulatedData(); + DataNormaliser1D rBCNormaliser(rBCNormalised); + // Normalise by A site population + rBCNormaliser.normaliseDivide(double(nACumulative) / nASelections); + // Normalise by B site population + rBCNormaliser.normaliseDivide(double(nBCumulative) / nBSelections); + // Normalise by C site population density + rBCNormaliser.normaliseDivide((double(nCAvailable) / nCSelections) / targetConfiguration_->box()->volume()); + // Normalise by spherical shell + rBCNormaliser.normaliseBySphericalShell(); + + auto &aABCNormalised = processingData.realise("Angle(ABC)", name(), GenericItem::InRestartFileFlag); + aABCNormalised = aABC.accumulatedData(); + DataNormaliser1D aABCNormaliser(aABCNormalised); + // Normalise by value / sin(x) + aABCNormaliser.normalise([](const auto &x, const auto &xDelta, const auto &value) { return value / sin(x / DEGRAD); }); + // Normalise to 1.0 + aABCNormaliser.normaliseTo(); + + auto &dAngleNormalised = processingData.realise("DAngle(A-BC)", name(), GenericItem::InRestartFileFlag); + dAngleNormalised = dAngle.accumulatedData(); + DataNormaliser2D dAngleNormaliser(dAngleNormalised); + // Normalise by value / sin(y) / sin(yDelta) + dAngleNormaliser.normalise([&](const auto &x, const auto &xDelta, const auto &y, const auto &yDelta, const auto &value) + { return (symmetric_ ? value : value * 2.0) / sin(y / DEGRAD) / sin(yDelta / DEGRAD); }); + // Normalise by A site population + dAngleNormaliser.normaliseDivide(double(nACumulative) / nASelections); + // Normalise by B site population density + dAngleNormaliser.normaliseDivide((double(nBAvailable) / nBSelections) / targetConfiguration_->box()->volume()); + // Normalise by spherical shell + dAngleNormaliser.normaliseBySphericalShell(); + + // Save RDF(A-B) data? + if (!DataExporter::exportData(rBCNormalised, exportFileAndFormatRDF_, + moduleContext.processPool())) + return ExecutionResult::Failed; + + // Save Angle(A-B-C) data? + if (!DataExporter::exportData(aABCNormalised, exportFileAndFormatAngle_, + moduleContext.processPool())) + return ExecutionResult::Failed; + + // Save DAngle(A-(B-C)) data? + if (!DataExporter::exportData(dAngleNormalised, exportFileAndFormatDAngle_, + moduleContext.processPool())) + return ExecutionResult::Failed; + return ExecutionResult::Success; } diff --git a/src/modules/histogramCN/process.cpp b/src/modules/histogramCN/process.cpp index d76a53e57a..46fe406ab2 100644 --- a/src/modules/histogramCN/process.cpp +++ b/src/modules/histogramCN/process.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later // Copyright (c) 2024 Team Dissolve and contributors +#include "analyser/dataExporter.h" #include "analyser/dataNormaliser1D.h" #include "analyser/siteSelector.h" #include "base/sysFunc.h" @@ -62,22 +63,9 @@ Module::ExecutionResult HistogramCNModule::process(ModuleContext &moduleContext) // Normalise by value normaliserCN.normaliseTo(); - // Save data? - if (exportFileAndFormat_.hasFilename()) - { - if (moduleContext.processPool().isMaster()) - { - if (exportFileAndFormat_.exportData(dataCN)) - moduleContext.processPool().decideTrue(); - else - { - moduleContext.processPool().decideFalse(); - return ExecutionResult::Failed; - } - } - else if (!moduleContext.processPool().decision()) - return ExecutionResult::Failed; - } + // Save CN data? + if (!DataExporter::exportData(dataCN, exportFileAndFormat_, moduleContext.processPool())) + return ExecutionResult::Failed; return ExecutionResult::Success; } diff --git a/src/modules/intraAngle/process.cpp b/src/modules/intraAngle/process.cpp index 736b9ad400..9dcada8b2e 100644 --- a/src/modules/intraAngle/process.cpp +++ b/src/modules/intraAngle/process.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later // Copyright (c) 2024 Team Dissolve and contributors +#include "analyser/dataExporter.h" #include "analyser/dataNormaliser1D.h" #include "analyser/siteSelector.h" #include "expression/variable.h" @@ -83,26 +84,14 @@ Module::ExecutionResult IntraAngleModule::process(ModuleContext &moduleContext) // Normalise DataNormaliser1D normaliser(dataNormalisedHisto); // Normalise by sin(x) - normaliser.normalise([](const auto &x, const auto &value) { return value / sin(x / DEGRAD); }); + normaliser.normalise([](const auto &x, const auto &xDelta, const auto &value) { return value / sin(x / DEGRAD); }); // Normalise by value normaliser.normaliseTo(); - // Save data? - if (exportFileAndFormat_.hasFilename()) - { - if (moduleContext.processPool().isMaster()) - { - if (exportFileAndFormat_.exportData(dataNormalisedHisto)) - moduleContext.processPool().decideTrue(); - else - { - moduleContext.processPool().decideFalse(); - return ExecutionResult::Failed; - } - } - else if (!moduleContext.processPool().decision()) - return ExecutionResult::Failed; - } + // Save Angle(A-B-C) data? + if (!DataExporter::exportData(dataNormalisedHisto, exportFileAndFormat_, + moduleContext.processPool())) + return ExecutionResult::Failed; return ExecutionResult::Success; } diff --git a/src/modules/intraDistance/gui/intraDistanceWidgetFuncs.cpp b/src/modules/intraDistance/gui/intraDistanceWidgetFuncs.cpp index f2c51225c3..a057a56676 100644 --- a/src/modules/intraDistance/gui/intraDistanceWidgetFuncs.cpp +++ b/src/modules/intraDistance/gui/intraDistanceWidgetFuncs.cpp @@ -38,7 +38,7 @@ void IntraDistanceModuleWidget::updateControls(const Flagskeywords().getConfiguration("Configuration"); if (cfg) rdfGraph_ - ->createRenderable(fmt::format("{}//Process1D//NormalisedHistogram", module_->name()), + ->createRenderable(fmt::format("{}//NormalisedHistogram", module_->name()), fmt::format("RDF//{}", cfg->niceName()), cfg->niceName()) ->setColour(StockColours::BlueStockColour); } diff --git a/src/modules/intraDistance/process.cpp b/src/modules/intraDistance/process.cpp index 695ca39a3d..12562327d8 100644 --- a/src/modules/intraDistance/process.cpp +++ b/src/modules/intraDistance/process.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later // Copyright (c) 2024 Team Dissolve and contributors +#include "analyser/dataExporter.h" #include "analyser/dataNormaliser1D.h" #include "analyser/siteSelector.h" #include "io/export/data1D.h" @@ -57,22 +58,10 @@ Module::ExecutionResult IntraDistanceModule::process(ModuleContext &moduleContex // Normalise by value histogramNormaliser.normaliseTo(); - // Save data? - if (exportFileAndFormat_.hasFilename()) - { - if (moduleContext.processPool().isMaster()) - { - if (exportFileAndFormat_.exportData(dataNormalisedHisto)) - moduleContext.processPool().decideTrue(); - else - { - moduleContext.processPool().decideFalse(); - return ExecutionResult::Failed; - } - } - else if (!moduleContext.processPool().decision()) - return ExecutionResult::Failed; - } + // Save Distance(A-B) data? + if (!DataExporter::exportData(dataNormalisedHisto, exportFileAndFormat_, + moduleContext.processPool())) + return ExecutionResult::Failed; return ExecutionResult::Success; } diff --git a/src/modules/orientedSDF/process.cpp b/src/modules/orientedSDF/process.cpp index e6256237f5..96749dca57 100644 --- a/src/modules/orientedSDF/process.cpp +++ b/src/modules/orientedSDF/process.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later // Copyright (c) 2024 Team Dissolve and contributors +#include "analyser/dataExporter.h" #include "analyser/dataNormaliser3D.h" #include "analyser/siteSelector.h" #include "main/dissolve.h" @@ -69,22 +70,10 @@ Module::ExecutionResult OrientedSDFModule::process(ModuleContext &moduleContext) // Normalise by grid normaliserOrientedSDF.normaliseByGrid(); - // Save data? - if (sdfFileAndFormat_.hasFilename()) - { - if (moduleContext.processPool().isMaster()) - { - if (sdfFileAndFormat_.exportData(dataOrientedSDF)) - moduleContext.processPool().decideTrue(); - else - { - moduleContext.processPool().decideFalse(); - return ExecutionResult::Failed; - } - } - else if (!moduleContext.processPool().decision()) - return ExecutionResult::Failed; - } + // Save SDF data? + if (!DataExporter::exportData(dataOrientedSDF, sdfFileAndFormat_, + moduleContext.processPool())) + return ExecutionResult::Failed; return ExecutionResult::Success; } diff --git a/src/modules/qSpecies/process.cpp b/src/modules/qSpecies/process.cpp index 8914dfe16e..f3304844c8 100644 --- a/src/modules/qSpecies/process.cpp +++ b/src/modules/qSpecies/process.cpp @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-or-later // Copyright (c) 2024 Team Dissolve and contributors +#include "analyser/dataExporter.h" +#include "analyser/dataNormaliser1D.h" #include "analyser/siteFilter.h" #include "analyser/siteSelector.h" #include "main/dissolve.h" @@ -80,8 +82,8 @@ Module::ExecutionResult QSpeciesModule::process(ModuleContext &moduleContext) // Averaged values for Q-Species Data1D accumulatedQData = qSpeciesHistogram.accumulatedData(); - auto sum = Integrator::absSum(qSpeciesHistogram.data()); - accumulatedQData /= sum; + DataNormaliser1D normaliserQ(accumulatedQData); + normaliserQ.normaliseTo(); // Averaged values for oxygen sites auto freeOxygens = oxygenSitesHistogram.accumulatedData().value(0); @@ -90,17 +92,9 @@ Module::ExecutionResult QSpeciesModule::process(ModuleContext &moduleContext) processingData.realise("QSpecies", name(), GenericItem::InRestartFileFlag) = accumulatedQData; // Save data? - if (exportFileAndFormat_.hasFilename()) - { - if (moduleContext.processPool().isMaster()) - { - if (exportFileAndFormat_.exportData(qSpeciesHistogram.accumulatedData())) - moduleContext.processPool().decideTrue(); - else - { - moduleContext.processPool().decideFalse(); - } - } - } + if (!DataExporter::exportData(accumulatedQData, exportFileAndFormat_, + moduleContext.processPool())) + return ExecutionResult::Failed; + return ExecutionResult::Success; } diff --git a/src/modules/sdf/process.cpp b/src/modules/sdf/process.cpp index c7c3cd0ded..8640c59f0b 100644 --- a/src/modules/sdf/process.cpp +++ b/src/modules/sdf/process.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later // Copyright (c) 2024 Team Dissolve and contributors +#include "analyser/dataExporter.h" #include "analyser/dataNormaliser3D.h" #include "analyser/siteSelector.h" #include "base/sysFunc.h" @@ -61,22 +62,9 @@ Module::ExecutionResult SDFModule::process(ModuleContext &moduleContext) // Normalise by grid normaliserSDF.normaliseByGrid(); - // Save data? - if (sdfFileAndFormat_.hasFilename()) - { - if (moduleContext.processPool().isMaster()) - { - if (sdfFileAndFormat_.exportData(dataSDF)) - moduleContext.processPool().decideTrue(); - else - { - moduleContext.processPool().decideFalse(); - return ExecutionResult::Failed; - } - } - else if (!moduleContext.processPool().decision()) - return ExecutionResult::Failed; - } + // Save SDF data? + if (!DataExporter::exportData(dataSDF, sdfFileAndFormat_, moduleContext.processPool())) + return ExecutionResult::Failed; return ExecutionResult::Success; } diff --git a/src/modules/siteRDF/process.cpp b/src/modules/siteRDF/process.cpp index e2207dbf2e..9c55ca6723 100644 --- a/src/modules/siteRDF/process.cpp +++ b/src/modules/siteRDF/process.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later // Copyright (c) 2024 Team Dissolve and contributors +#include "analyser/dataExporter.h" #include "analyser/dataNormaliser1D.h" #include "base/sysFunc.h" #include "io/export/data1D.h" @@ -86,7 +87,8 @@ Module::ExecutionResult SiteRDFModule::process(ModuleContext &moduleContext) if (exportInstantaneous_) { Data1DExportFileFormat exportFormat(fmt::format("{}_Sum{}.txt", name(), rangeNames[i])); - if (!exportFormat.exportData(sumNInst)) + if (!DataExporter::exportData(sumNInst, exportFormat, + moduleContext.processPool())) { Messenger::error("Failed to write instantaneous coordination number data for range {}.\n", rangeNames[i]); @@ -96,22 +98,9 @@ Module::ExecutionResult SiteRDFModule::process(ModuleContext &moduleContext) } } - // Save data? - if (exportFileAndFormat_.hasFilename()) - { - if (moduleContext.processPool().isMaster()) - { - if (exportFileAndFormat_.exportData(dataCN)) - moduleContext.processPool().decideTrue(); - else - { - moduleContext.processPool().decideFalse(); - return ExecutionResult::Failed; - } - } - else if (!moduleContext.processPool().decision()) - return ExecutionResult::Failed; - } + // Save RDF data? + if (!DataExporter::exportData(dataRDF, exportFileAndFormat_, moduleContext.processPool())) + return ExecutionResult::Failed; return ExecutionResult::Success; } diff --git a/tests/data/dissolve/input/angle.txt b/tests/data/dissolve/input/angle.txt new file mode 100644 index 0000000000..c3d5ae1008 --- /dev/null +++ b/tests/data/dissolve/input/angle.txt @@ -0,0 +1,117 @@ +# Bulk Water Properties +# Trajectory calculated using DL_POLY Classic v1.9 + +# Potential uses SPC/Fw parameters: +# +# Yujie Wu, Harald L. Tepper and Gregory A. Voth +# "Flexible simple point-charge water model with improved liquid-state properties", Journal of Chemical Physics 124 024503 (2006) +# http://dx.doi.org/10.1063/1.2136877 +# +# sigma(O) = 3.165492 Angstroms +# epsilon(O) = 0.6503 kJ/mol +# charge(O) = -0.82 e +# charge(H) = 0.41 e +# k(O-H) = 4431.53 kJ mol-1 Angstrom-2 +# r(O-H) = 1.0 Angstroms +# k(H-O-H) = 317.5656 kJ mol-1 radian-2 +# theta(H-O-H) = 113.24 degrees + +#------------------------# +# Define Master Terms # +#------------------------# + +Master + Bond OH Harmonic 4431.53 1.0 + Angle HOH Harmonic 317.5656 113.24 +EndMaster + +#------------------------# +# Define Species # +#------------------------# + +Species 'Water' + # Atoms + Atom 1 H 0.757 0.013 0.217 'HW' + Atom 2 O 0.015 -0.009 -0.373 'OW' + Atom 3 H -0.771 -0.003 0.157 'HW' + + # Intramolecular Terms + Bond 1 2 @OH + Bond 3 2 @OH + Angle 1 2 3 @HOH + + # Isotopologues + Isotopologue 'Deuteriated' OW=0 HW=2 + + # Analysis Sites + Site 'origin' + Origin 2 + XAxis 1 3 + YAxis 3 + EndSite + Site 'O' + Origin 2 + EndSite + Site 'H' + Dynamic + Element H + EndSite + Site 'COM' + Origin 1 2 3 + OriginMassWeighted True + EndSite +EndSpecies + +#------------------------# +# Pair Potentials # +#------------------------# + +PairPotentials + Range 9.000000 + Delta 0.050000 + Parameters 'OW' O -0.82 LJGeometric 0.65 3.165492 + Parameters 'HW' H 0.41 LJGeometric 0.0 0.0 +EndPairPotentials + +#------------------------# +# Define Configuration # +#------------------------# + +Configuration 'Bulk' + Generator + Add + Density 9.99999642E-02 atoms/A3 + Population 267 + Species 'Water' + EndAdd + EndGenerator +EndConfiguration + +#------------------------# +# Define Processing # +#------------------------# + +Layer 'Processing' + +# Trajectory Processing +Module ImportTrajectory + Configuration 'Bulk' + + # Trajectory file contains 95 frames + Format xyz 'dlpoly/water267-analysis/water-267-298K.xyz' + EndFormat +EndModule + +Module Angle 'Angle(X-H...O)' + Configuration 'Bulk' + SiteA Water 'O' + SiteB Water 'H' + SiteC Water 'O' + RangeAB 0.9 1.1 0.01 + RangeBC 0.0 5.0 0.01 + AngleRange 0.0 180.0 1.0 + ExcludeSameMoleculeAB false + ExcludeSameMoleculeBC true + ExcludeSameSiteAC false +EndModule +EndLayer \ No newline at end of file diff --git a/tests/data/dissolve/input/axisAngle-benzene.txt b/tests/data/dissolve/input/axisAngle-benzene.txt new file mode 100644 index 0000000000..496083ae93 --- /dev/null +++ b/tests/data/dissolve/input/axisAngle-benzene.txt @@ -0,0 +1,177 @@ +# Input file written by Dissolve v1.5.0 at 13:46:14 on 03-03-2024. + +#==============================================================================# +# Master Terms # +#==============================================================================# + +Master + Bond 'CA-CA' Harmonic k=3924.59 eq=1.4 + Bond 'CA-HA' Harmonic k=3071.06 eq=1.08 + Angle 'CA-CA-CA' Harmonic k=527.184 eq=120 + Angle 'CA-CA-HA' Harmonic k=292.88 eq=120 + Torsion 'CA-CA-CA-CA' Cos3 k1=0 k2=30.334 k3=0 + Torsion 'CA-CA-CA-HA' Cos3 k1=0 k2=30.334 k3=0 + Torsion 'HA-CA-CA-HA' Cos3 k1=0 k2=30.334 k3=0 +EndMaster + +#==============================================================================# +# Species # +#==============================================================================# + +Species 'Benzene' + # Atoms + NAtoms 12 + Atom 1 C -1.399000e+00 1.600000e-01 0.000000e+00 'CA' -1.150000e-01 + Atom 2 C -5.610000e-01 1.293000e+00 0.000000e+00 'CA' -1.150000e-01 + Atom 3 C 8.390000e-01 1.132000e+00 0.000000e+00 'CA' -1.150000e-01 + Atom 4 C 1.399000e+00 -1.600000e-01 0.000000e+00 'CA' -1.150000e-01 + Atom 5 C 5.600000e-01 -1.293000e+00 0.000000e+00 'CA' -1.150000e-01 + Atom 6 C -8.390000e-01 -1.132000e+00 0.000000e+00 'CA' -1.150000e-01 + Atom 7 H 1.483000e+00 2.001000e+00 0.000000e+00 'HA' 1.150000e-01 + Atom 8 H 2.472000e+00 -2.840000e-01 0.000000e+00 'HA' 1.150000e-01 + Atom 9 H 9.910000e-01 -2.284000e+00 0.000000e+00 'HA' 1.150000e-01 + Atom 10 H -1.483000e+00 -2.000000e+00 0.000000e+00 'HA' 1.150000e-01 + Atom 11 H -2.472000e+00 2.820000e-01 0.000000e+00 'HA' 1.150000e-01 + Atom 12 H -9.900000e-01 2.284000e+00 0.000000e+00 'HA' 1.150000e-01 + + # Bonds + NBonds 12 + Bond 1 2 @CA-CA + Bond 2 3 @CA-CA + Bond 3 4 @CA-CA + Bond 4 5 @CA-CA + Bond 5 6 @CA-CA + Bond 6 1 @CA-CA + Bond 7 3 @CA-HA + Bond 4 8 @CA-HA + Bond 5 9 @CA-HA + Bond 6 10 @CA-HA + Bond 1 11 @CA-HA + Bond 2 12 @CA-HA + + # Angles + NAngles 18 + Angle 1 2 3 @CA-CA-CA + Angle 2 3 4 @CA-CA-CA + Angle 3 4 5 @CA-CA-CA + Angle 4 5 6 @CA-CA-CA + Angle 6 1 2 @CA-CA-CA + Angle 5 6 1 @CA-CA-CA + Angle 2 3 7 @CA-CA-HA + Angle 7 3 4 @CA-CA-HA + Angle 3 4 8 @CA-CA-HA + Angle 8 4 5 @CA-CA-HA + Angle 4 5 9 @CA-CA-HA + Angle 9 5 6 @CA-CA-HA + Angle 5 6 10 @CA-CA-HA + Angle 10 6 1 @CA-CA-HA + Angle 11 1 2 @CA-CA-HA + Angle 6 1 11 @CA-CA-HA + Angle 1 2 12 @CA-CA-HA + Angle 12 2 3 @CA-CA-HA + + # Torsions + NTorsions 24 + Torsion 1 2 3 4 @CA-CA-CA-CA + Torsion 2 3 4 5 @CA-CA-CA-CA + Torsion 3 4 5 6 @CA-CA-CA-CA + Torsion 6 1 2 3 @CA-CA-CA-CA + Torsion 4 5 6 1 @CA-CA-CA-CA + Torsion 5 6 1 2 @CA-CA-CA-CA + Torsion 1 2 3 7 @CA-CA-CA-HA + Torsion 7 3 4 5 @CA-CA-CA-HA + Torsion 2 3 4 8 @CA-CA-CA-HA + Torsion 7 3 4 8 @HA-CA-CA-HA + Torsion 8 4 5 6 @CA-CA-CA-HA + Torsion 3 4 5 9 @CA-CA-CA-HA + Torsion 8 4 5 9 @HA-CA-CA-HA + Torsion 9 5 6 1 @CA-CA-CA-HA + Torsion 4 5 6 10 @CA-CA-CA-HA + Torsion 9 5 6 10 @HA-CA-CA-HA + Torsion 10 6 1 2 @CA-CA-CA-HA + Torsion 11 1 2 3 @CA-CA-CA-HA + Torsion 5 6 1 11 @CA-CA-CA-HA + Torsion 10 6 1 11 @HA-CA-CA-HA + Torsion 6 1 2 12 @CA-CA-CA-HA + Torsion 11 1 2 12 @HA-CA-CA-HA + Torsion 12 2 3 4 @CA-CA-CA-HA + Torsion 12 2 3 7 @HA-CA-CA-HA + + # Isotopologues + Isotopologue 'Deuterated' HA=2 + + # Sites + Site 'COG' + Origin 1 3 4 5 6 2 + XAxis 4 + YAxis 2 3 + EndSite +EndSpecies + +#==============================================================================# +# Pair Potentials # +#==============================================================================# + +PairPotentials + # Atom Type Parameters + Parameters CA C -1.150000e-01 LJGeometric epsilon=0.29288 sigma=3.55 + Parameters HA H 1.150000e-01 LJGeometric epsilon=0.12552 sigma=2.42 + Range 12 + Delta 0.005 + CoulombTruncation Shifted + ShortRangeTruncation Shifted +EndPairPotentials + +#==============================================================================# +# Configurations # +#==============================================================================# + +Configuration 'Bulk' + + # Generator + Generator + Parameters 'Parameters01' + Parameter rho 0.876 + EndParameters + Box 'Box01' + Lengths 30 30 30 + Angles 90 90 90 + NonPeriodic False + EndBox + Add 'Add01' + Species 'Benzene' + Population '3' + Density 'rho' g/cm3 + Positioning Random + Rotate True + BoxAction None + EndAdd + ImportCoordinates 'ImportCoordinates01' + File xyz 'dissolve/input/axisAngle-benzene.xyz' + EndFile + EndImportCoordinates + EndGenerator + + Temperature 300 + +EndConfiguration + +#==============================================================================# +# Processing Layers # +#==============================================================================# + +Layer 'Untitled Layer' + Frequency 1 + + Module AxisAngle 'AxisAngle' + Frequency 1 + Configuration 'Bulk' + SiteA 'Benzene' 'COG' + AxisA Z + SiteB 'Benzene' 'COG' + AxisB Z + DistanceRange 0.000000e+00 1.000000e+01 1.000000e-01 + AngleRange 0.000000e+00 9.000000e+01 1.000000e+01 + ExcludeSameMolecule True + EndModule +EndLayer diff --git a/tests/data/dissolve/input/axisAngle-benzene.xyz b/tests/data/dissolve/input/axisAngle-benzene.xyz new file mode 100644 index 0000000000..037075ded1 --- /dev/null +++ b/tests/data/dissolve/input/axisAngle-benzene.xyz @@ -0,0 +1,38 @@ +36 +AxisAngle test +Benzene 13.610000 15.000000 15.000000 +Benzene 14.305000 15.851197 14.148803 +Benzene 15.695000 15.851197 14.148803 +Benzene 16.390000 15.000000 15.000000 +Benzene 15.695000 14.148803 15.851197 +Benzene 14.305000 14.148803 15.851197 +Benzene 16.195000 16.463570 13.536430 +Benzene 17.390000 15.000000 15.000000 +Benzene 16.195000 13.536430 16.463570 +Benzene 13.805000 13.536430 16.463570 +Benzene 12.610000 15.000000 15.000000 +Benzene 13.805000 16.463570 13.536430 +Benzene 13.610000 15.000000 15.000000 +Benzene 14.305000 16.203775 15.000000 +Benzene 15.695000 16.203775 15.000000 +Benzene 16.390000 15.000000 15.000000 +Benzene 15.695000 13.796225 15.000000 +Benzene 14.305000 13.796225 15.000000 +Benzene 16.195000 17.069801 15.000000 +Benzene 17.390000 15.000000 15.000000 +Benzene 16.195000 12.930199 15.000000 +Benzene 13.805000 12.930199 15.000000 +Benzene 12.610000 15.000000 15.000000 +Benzene 13.805000 17.069801 15.000000 +Benzene 13.610000 15.000000 15.000000 +Benzene 14.305000 15.851197 15.851197 +Benzene 15.695000 15.851197 15.851197 +Benzene 16.390000 15.000000 15.000000 +Benzene 15.695000 14.148803 14.148803 +Benzene 14.305000 14.148803 14.148803 +Benzene 16.195000 16.463570 16.463570 +Benzene 17.390000 15.000000 15.000000 +Benzene 16.195000 13.536430 13.536430 +Benzene 13.805000 13.536430 13.536430 +Benzene 12.610000 15.000000 15.000000 +Benzene 13.805000 16.463570 16.463570 diff --git a/tests/modules/CMakeLists.txt b/tests/modules/CMakeLists.txt index 14b94fbc2e..b982c506ff 100644 --- a/tests/modules/CMakeLists.txt +++ b/tests/modules/CMakeLists.txt @@ -1,6 +1,8 @@ +dissolve_add_test(SRC angle.cpp) dissolve_add_test(SRC accumulate.cpp) dissolve_add_test(SRC atomShake.cpp) dissolve_add_test(SRC avgMol.cpp) +dissolve_add_test(SRC axisAngle.cpp) dissolve_add_test(SRC bragg.cpp) dissolve_add_test(SRC broadening.cpp) dissolve_add_test(SRC compare.cpp) diff --git a/tests/modules/angle.cpp b/tests/modules/angle.cpp new file mode 100644 index 0000000000..f1dbdc6ba3 --- /dev/null +++ b/tests/modules/angle.cpp @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team Dissolve and contributors + +#include "tests/testData.h" +#include + +namespace UnitTest +{ +class AngleModuleTest : public ::testing::Test +{ + protected: + DissolveSystemTest systemTest; +}; + +TEST_F(AngleModuleTest, Water) +{ + ASSERT_NO_THROW(systemTest.setUp("dissolve/input/angle.txt")); + ASSERT_TRUE(systemTest.iterateRestart(95)); + + EXPECT_TRUE(systemTest.checkData1D( + "Angle(X-H...O)//RDF(BC)", + {"dlpoly/water267-analysis/water-267-298K.aardf_21_23_inter_sum", Data1DImportFileFormat::Data1DImportFormat::XY}, + 3.0e-3)); + EXPECT_TRUE(systemTest.checkData1D("Angle(X-H...O)//Angle(ABC)", + {"dlpoly/water267-analysis/water-267-298K.dahist1_02_1_01_02.angle.norm", + Data1DImportFileFormat::Data1DImportFormat::XY}, + 6.0e-5)); +} + +} // namespace UnitTest diff --git a/tests/modules/axisAngle.cpp b/tests/modules/axisAngle.cpp new file mode 100644 index 0000000000..4a601af994 --- /dev/null +++ b/tests/modules/axisAngle.cpp @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team Dissolve and contributors + +#include "tests/testData.h" +#include + +namespace UnitTest +{ +class AxisAngleModuleTest : public ::testing::Test +{ + protected: + DissolveSystemTest systemTest; + + void SetUp() override + { + ASSERT_NO_THROW(systemTest.setUp("dissolve/input/axisAngle-benzene.txt")); + ASSERT_TRUE(systemTest.iterateRestart(100)); + } +}; + +TEST_F(AxisAngleModuleTest, Benzene) +{ + const auto &data = systemTest.dissolve().processingModuleData().search("AxisAngle//AxisAngle(AB)")->get(); + ASSERT_EQ(data.value(4), 1.0); + ASSERT_EQ(std::accumulate(data.values().begin(), data.values().end(), 0.0), 1.0); +} + +} // namespace UnitTest diff --git a/tests/modules/dAngle.cpp b/tests/modules/dAngle.cpp index 154d8eb6ea..2dbc048d64 100644 --- a/tests/modules/dAngle.cpp +++ b/tests/modules/dAngle.cpp @@ -19,14 +19,14 @@ TEST_F(DAngleModuleTest, Water) ASSERT_TRUE(systemTest.iterateRestart(95)); EXPECT_TRUE(systemTest.checkData1D( - "DAngle(X-H...O)//Process1D//RDF(BC)", + "DAngle(X-H...O)//RDF(BC)", {"dlpoly/water267-analysis/water-267-298K.aardf_21_23_inter_sum", Data1DImportFileFormat::Data1DImportFormat::XY}, 3.0e-3)); EXPECT_TRUE(systemTest.checkData1D( "DAngle(X-H...O)-Analyser//Process1D//RDF", {"dlpoly/water267-analysis/water-267-298K.aardf_21_23_inter_sum", Data1DImportFileFormat::Data1DImportFormat::XY}, 3.0e-3)); - EXPECT_TRUE(systemTest.checkData1D("DAngle(X-H...O)//Process1D//Angle(ABC)", + EXPECT_TRUE(systemTest.checkData1D("DAngle(X-H...O)//Angle(ABC)", {"dlpoly/water267-analysis/water-267-298K.dahist1_02_1_01_02.angle.norm", Data1DImportFileFormat::Data1DImportFormat::XY}, 6.0e-5)); From fd086efa76520363e2b933b3398643175ee4bef0 Mon Sep 17 00:00:00 2001 From: Dan Bradley <115464393+Danbr4d@users.noreply.github.com> Date: Tue, 12 Mar 2024 23:28:35 +0000 Subject: [PATCH 10/13] Modifier Oxygen Sites Module setup (#1793) --- src/module/module.cpp | 1 + src/module/types.h | 1 + src/modules/modifierOSites/CMakeLists.txt | 1 + src/modules/modifierOSites/gui/CMakeLists.txt | 1 + .../modifierOSites/gui/modifierOSitesWidget.h | 40 ++++++ .../gui/modifierOSitesWidget.ui | 72 +++++++++++ .../gui/modifierOSitesWidgetFuncs.cpp | 67 ++++++++++ src/modules/modifierOSites/modifierOSites.cpp | 52 ++++++++ src/modules/modifierOSites/modifierOSites.h | 40 ++++++ src/modules/modifierOSites/process.cpp | 107 ++++++++++++++++ src/modules/registry.cpp | 3 + src/modules/widgetProducer.cpp | 3 + .../dissolve/input/modifierOSites-test2.dat | 3 + .../dissolve/input/modifierOSites-test2.txt | 117 ++++++++++++++++++ .../dissolve/input/modifierOSites-test2.xyz | 26 ++++ tests/data/dissolve/input/modifierOSites.dat | 3 + tests/data/dissolve/input/modifierOSites.txt | 117 ++++++++++++++++++ tests/data/dissolve/input/modifierOSites.xyz | 32 +++++ .../input/modifierTotalOSites-test2.dat | 4 + .../dissolve/input/modifierTotalOSites.dat | 1 + tests/modules/CMakeLists.txt | 1 + tests/modules/modifierOSites.cpp | 38 ++++++ 22 files changed, 730 insertions(+) create mode 100644 src/modules/modifierOSites/CMakeLists.txt create mode 100644 src/modules/modifierOSites/gui/CMakeLists.txt create mode 100644 src/modules/modifierOSites/gui/modifierOSitesWidget.h create mode 100644 src/modules/modifierOSites/gui/modifierOSitesWidget.ui create mode 100644 src/modules/modifierOSites/gui/modifierOSitesWidgetFuncs.cpp create mode 100644 src/modules/modifierOSites/modifierOSites.cpp create mode 100644 src/modules/modifierOSites/modifierOSites.h create mode 100644 src/modules/modifierOSites/process.cpp create mode 100644 tests/data/dissolve/input/modifierOSites-test2.dat create mode 100755 tests/data/dissolve/input/modifierOSites-test2.txt create mode 100644 tests/data/dissolve/input/modifierOSites-test2.xyz create mode 100644 tests/data/dissolve/input/modifierOSites.dat create mode 100755 tests/data/dissolve/input/modifierOSites.txt create mode 100644 tests/data/dissolve/input/modifierOSites.xyz create mode 100644 tests/data/dissolve/input/modifierTotalOSites-test2.dat create mode 100644 tests/data/dissolve/input/modifierTotalOSites.dat create mode 100644 tests/modules/modifierOSites.cpp diff --git a/src/module/module.cpp b/src/module/module.cpp index fa13ce2478..69cec5583f 100644 --- a/src/module/module.cpp +++ b/src/module/module.cpp @@ -39,6 +39,7 @@ EnumOptions moduleTypes_("ModuleType", {{ModuleTypes::A {ModuleTypes::IntraDistance, "IntraDistance"}, {ModuleTypes::IntraShake, "IntraShake"}, {ModuleTypes::MD, "MD"}, + {ModuleTypes::ModifierOSites, "ModifierOSites"}, {ModuleTypes::MolShake, "MolShake"}, {ModuleTypes::NeutronSQ, "NeutronSQ"}, {ModuleTypes::OrientedSDF, "OrientedSDF"}, diff --git a/src/module/types.h b/src/module/types.h index b914f69be1..f1fb25dbad 100644 --- a/src/module/types.h +++ b/src/module/types.h @@ -35,6 +35,7 @@ enum ModuleType IntraDistance, IntraShake, MD, + ModifierOSites, MolShake, NeutronSQ, OrientedSDF, diff --git a/src/modules/modifierOSites/CMakeLists.txt b/src/modules/modifierOSites/CMakeLists.txt new file mode 100644 index 0000000000..0558fe8d5a --- /dev/null +++ b/src/modules/modifierOSites/CMakeLists.txt @@ -0,0 +1 @@ +dissolve_add_module(modifierOSites.h modifierOSites) diff --git a/src/modules/modifierOSites/gui/CMakeLists.txt b/src/modules/modifierOSites/gui/CMakeLists.txt new file mode 100644 index 0000000000..fecae39718 --- /dev/null +++ b/src/modules/modifierOSites/gui/CMakeLists.txt @@ -0,0 +1 @@ +dissolve_add_module_gui(modifierOSites) diff --git a/src/modules/modifierOSites/gui/modifierOSitesWidget.h b/src/modules/modifierOSites/gui/modifierOSitesWidget.h new file mode 100644 index 0000000000..72317cb7f7 --- /dev/null +++ b/src/modules/modifierOSites/gui/modifierOSitesWidget.h @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team Dissolve and contributors + +#pragma once + +#include "modules/modifierOSites/gui/ui_modifierOSitesWidget.h" +#include "modules/widget.h" + +// Forward Declarations +class ModifierOSitesModule; +class DataViewer; + +// Module Widget +class ModifierOSitesModuleWidget : public ModuleWidget +{ + // All Qt declarations derived from QObject must include this macro + Q_OBJECT + + private: + // Associated Module + ModifierOSitesModule *module_; + + public: + ModifierOSitesModuleWidget(QWidget *parent, ModifierOSitesModule *module, Dissolve &dissolve); + + /* + * UI + */ + private: + // Main form declaration + Ui::ModifierOSitesModuleWidget ui_; + + // DataViewer contained within this widget + DataViewer *oSitesGraph_; + DataViewer *modifierSitesGraph_; + + public: + // Update controls within widget + void updateControls(const Flags &updateFlags = {}) override; +}; diff --git a/src/modules/modifierOSites/gui/modifierOSitesWidget.ui b/src/modules/modifierOSites/gui/modifierOSitesWidget.ui new file mode 100644 index 0000000000..8f3e9a8282 --- /dev/null +++ b/src/modules/modifierOSites/gui/modifierOSitesWidget.ui @@ -0,0 +1,72 @@ + + + ModifierOSitesModuleWidget + + + + 0 + 0 + 597 + 422 + + + + + 0 + 0 + + + + + 8 + + + + QSpecies Controls + + + + 4 + + + 4 + + + 4 + + + 4 + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + + + + + DataWidget + QWidget +
gui/dataWidget.h
+ 1 +
+
+ + +
diff --git a/src/modules/modifierOSites/gui/modifierOSitesWidgetFuncs.cpp b/src/modules/modifierOSites/gui/modifierOSitesWidgetFuncs.cpp new file mode 100644 index 0000000000..ffdb6c3ca0 --- /dev/null +++ b/src/modules/modifierOSites/gui/modifierOSitesWidgetFuncs.cpp @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team Dissolve and contributors + +#include "classes/configuration.h" +#include "gui/render/renderableData1D.h" +#include "main/dissolve.h" +#include "math/integerHistogram1D.h" +#include "modules/modifierOSites/gui/modifierOSitesWidget.h" +#include "modules/modifierOSites/modifierOSites.h" + +ModifierOSitesModuleWidget::ModifierOSitesModuleWidget(QWidget *parent, ModifierOSitesModule *module, Dissolve &dissolve) + : ModuleWidget(parent, dissolve), module_(module) +{ + // Set up user interface + ui_.setupUi(this); + + // Set up Oxygen Sites Graph + oSitesGraph_ = ui_.OSitesPlotWidget->dataViewer(); + modifierSitesGraph_ = ui_.ModifierPlotWidget->dataViewer(); + + auto &view = oSitesGraph_->view(); + view.setViewType(View::FlatXYView); + view.axes().setTitle(0, "NF bound to O"); + view.axes().setMax(0, 10.0); + view.axes().setTitle(1, "Normalised Frequency"); + view.axes().setMin(1, 0.0); + view.axes().setMax(1, 1.0); + view.setAutoFollowType(View::AllAutoFollow); + + auto &view2 = modifierSitesGraph_->view(); + view2.setViewType(View::FlatXYView); + view2.axes().setTitle(0, "Total O bound to SiteA"); + view2.axes().setMax(0, 10.0); + view2.axes().setTitle(1, "Normalised Frequency"); + view2.axes().setMin(1, 0.0); + view2.axes().setMax(1, 1.0); + view2.setAutoFollowType(View::AllAutoFollow); + + refreshing_ = false; +} + +// Update controls within widget +void ModifierOSitesModuleWidget::updateControls(const Flags &updateFlags) +{ + if (updateFlags.isSet(ModuleWidget::RecreateRenderablesFlag)) + { + oSitesGraph_->clearRenderables(); + modifierSitesGraph_->clearRenderables(); + } + + if (oSitesGraph_->renderables().empty()) + oSitesGraph_->createRenderable(fmt::format("{}//OTypes", module_->name()), "O-Types"); + + if (modifierSitesGraph_->renderables().empty()) + modifierSitesGraph_->createRenderable(fmt::format("{}//TotalOSites", module_->name()), + "TotalO-Sites"); + + // Validate renderables if they need it + oSitesGraph_->validateRenderables(dissolve_.processingModuleData()); + modifierSitesGraph_->validateRenderables(dissolve_.processingModuleData()); + + ui_.OSitesPlotWidget->updateToolbar(); + ui_.ModifierPlotWidget->updateToolbar(); + + oSitesGraph_->postRedisplay(); + modifierSitesGraph_->postRedisplay(); +} diff --git a/src/modules/modifierOSites/modifierOSites.cpp b/src/modules/modifierOSites/modifierOSites.cpp new file mode 100644 index 0000000000..632239b3e5 --- /dev/null +++ b/src/modules/modifierOSites/modifierOSites.cpp @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team Dissolve and contributors + +#include "modules/modifierOSites/modifierOSites.h" +#include "keywords/bool.h" +#include "keywords/configuration.h" +#include "keywords/fileAndFormat.h" +#include "keywords/range.h" +#include "keywords/speciesSiteVector.h" +#include "keywords/vec3Double.h" +#include "procedure/nodes/calculateExpression.h" +#include "procedure/nodes/ifValueInRange.h" +#include "procedure/nodes/integerCollect1D.h" +#include "procedure/nodes/iterateData1D.h" +#include "procedure/nodes/operateNormalise.h" +#include "procedure/nodes/process1D.h" +#include "procedure/nodes/select.h" + +ModifierOSitesModule::ModifierOSitesModule() : Module(ModuleTypes::ModifierOSites) +{ + /* + * Keywords + */ + + keywords_.addTarget("Configuration", "Set target configuration for the module", targetConfiguration_); + + keywords_.setOrganisation("Options", "Sites", + "Specify sites representing the bonding oxygen (BO) and network forming (NF) sites."); + keywords_.add("BondingOxygen", "Set the site(s) 'BO' which are to represent the bonding oxygen", + bridgingOxygenSpeciesSites_); + keywords_.add( + "NetworkFormer", "Set the site(s) 'NF' for which the distribution around the origin sites 'A' should be calculated", + networkFormerSpeciesSites_); + keywords_.add( + "SiteA", "Set the modifier site(s) for which the distribution of oxygens should be calculated", modifierSpeciesSites_); + + keywords_.setOrganisation("Options", "Ranges", "Ranges over which to bin quantities from the calculation."); + keywords_.add("DistanceRange", + "Distance range (min, max) over which to calculate Q-Species from central site", distanceRange_, + 0.0, std::nullopt, Vec3Labels::MinMaxDeltaLabels); + keywords_.add("ModifierDistanceRange", + "Distance range (min, max) over which to calculate Modifier from central site", + modifierDistanceRange_, 0.0, std::nullopt, Vec3Labels::MinMaxDeltaLabels); + + keywords_.setOrganisation("Export"); + keywords_.add("ExportModifierData", + "File format and file name under which to save calculated modifier to oxygen data", + exportFileAndFormatOType_, "EndExportModifierData"); + keywords_.add( + "ExportOxygenTypes", "File format and file name under which to save calculated total oygens to modifier atoms", + exportFileAndFormatTotalOSites_, "EndExportOxygenTypes"); +} diff --git a/src/modules/modifierOSites/modifierOSites.h b/src/modules/modifierOSites/modifierOSites.h new file mode 100644 index 0000000000..02b4ab69d8 --- /dev/null +++ b/src/modules/modifierOSites/modifierOSites.h @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team Dissolve and contributors + +#pragma once + +#include "analyser/siteSelector.h" +#include "io/export/data1D.h" +#include "math/range.h" +#include "module/module.h" + +// ModifierOSites Module +class ModifierOSitesModule : public Module +{ + public: + ModifierOSitesModule(); + ~ModifierOSitesModule() override = default; + + /* + * Definition + */ + private: + // Target configuration + Configuration *targetConfiguration_{nullptr}; + // Target SpeciesSite definitions + std::vector bridgingOxygenSpeciesSites_, networkFormerSpeciesSites_, modifierSpeciesSites_; + // Whether to exclude correlations between sites on the same molecule + bool excludeSameMolecule_{false}; + // Distance range for calculation + Range distanceRange_{0.0, 2.5}, modifierDistanceRange_{0.0, 2.0}; + // Export target + Data1DExportFileFormat exportFileAndFormatOType_; + Data1DExportFileFormat exportFileAndFormatTotalOSites_; + + /* + * Processing + */ + private: + // Run main processing + Module::ExecutionResult process(ModuleContext &moduleContext) override; +}; diff --git a/src/modules/modifierOSites/process.cpp b/src/modules/modifierOSites/process.cpp new file mode 100644 index 0000000000..8cb203f74e --- /dev/null +++ b/src/modules/modifierOSites/process.cpp @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team Dissolve and contributors + +#include "analyser/siteFilter.h" +#include "analyser/siteSelector.h" +#include "main/dissolve.h" +#include "math/integerHistogram1D.h" +#include "math/integrator.h" +#include "module/context.h" +#include "modules/modifierOSites/modifierOSites.h" + +// Run main processing +Module::ExecutionResult ModifierOSitesModule::process(ModuleContext &moduleContext) +{ + // Check for zero Configuration targets + if (!targetConfiguration_) + { + Messenger::error("No configuration target set for module '{}'.\n", name()); + return ExecutionResult::Failed; + } + auto &processingData = moduleContext.dissolve().processingModuleData(); + + // Select all potential bridging oxygen sites - we will determine which actually are + // involved in NF-BO-NF interactions once we have the available NF sites + SiteSelector allOxygenSites(targetConfiguration_, bridgingOxygenSpeciesSites_); + + // Select all NF centres + SiteSelector NF(targetConfiguration_, networkFormerSpeciesSites_); + + // Select all modifier centres + SiteSelector modifier(targetConfiguration_, modifierSpeciesSites_); + + // Filter the oxygen sites into those surrounded by up to two NF sites + SiteFilter ofilter(targetConfiguration_, allOxygenSites.sites()); + auto &&[filteredOSites, neighbourMap] = ofilter.filterBySiteProximity(NF.sites(), distanceRange_, 0, 2); + + SiteFilter mfilter(targetConfiguration_, modifier.sites()); + auto &&[filteredMSites, mNeighbourMapO] = + mfilter.filterBySiteProximity(allOxygenSites.sites(), modifierDistanceRange_, 0, 99); + + // Retrieve storage for the Mofifier to Oxygen Type Sites histogram + auto [oxygenSitesHistogram, status] = + processingData.realiseIf("OTypeSitesHistogram", name(), GenericItem::InRestartFileFlag); + if (status == GenericItem::ItemStatus::Created) + oxygenSitesHistogram.initialise(); + + // Retrieve storage for the Total O Sites histogram + auto [modifierHistogram, status2] = + processingData.realiseIf("TotalOHistogram", name(), GenericItem::InRestartFileFlag); + if (status2 == GenericItem::ItemStatus::Created) + modifierHistogram.initialise(); + + // Clear the temporary bins + modifierHistogram.zeroBins(); + oxygenSitesHistogram.zeroBins(); + + // For each modifier site, bin the number of neighbour oxygens, then for each of those oxygen bin its type + std::map qSpecies; + std::map oxygenSites; + for (const auto &[siteM, nearO] : mNeighbourMapO) + { + modifierHistogram.bin(nearO.size()); + for (auto &&[oSite, index] : nearO) + { + oxygenSitesHistogram.bin(neighbourMap[oSite].size()); + } + } + + // Accumulate histogram averages + oxygenSitesHistogram.accumulate(); + modifierHistogram.accumulate(); + + // Averaged values for OSites + Data1D accumulatedData = oxygenSitesHistogram.accumulatedData(); + auto sum = Integrator::absSum(oxygenSitesHistogram.data()); + accumulatedData /= sum; + + // Average values for total O sites + Data1D accumulatedModifierData = modifierHistogram.accumulatedData(); + auto totalOSites = Integrator::absSum(modifierHistogram.data()); + accumulatedModifierData /= totalOSites; + + // Store the normalised data + processingData.realise("OTypes", name(), GenericItem::InRestartFileFlag) = accumulatedData; + processingData.realise("TotalOSites", name(), GenericItem::InRestartFileFlag) = accumulatedModifierData; + + // Save data? + if (exportFileAndFormatOType_.hasFilename()) + if (moduleContext.processPool().isMaster()) + if (exportFileAndFormatOType_.exportData(accumulatedData)) + moduleContext.processPool().decideTrue(); + else + { + moduleContext.processPool().decideFalse(); + } + + if (exportFileAndFormatTotalOSites_.hasFilename()) + if (moduleContext.processPool().isMaster()) + if (exportFileAndFormatTotalOSites_.exportData(accumulatedModifierData)) + moduleContext.processPool().decideTrue(); + else + { + moduleContext.processPool().decideFalse(); + } + + return ExecutionResult::Success; +} diff --git a/src/modules/registry.cpp b/src/modules/registry.cpp index 176dd2a15b..999120f57f 100644 --- a/src/modules/registry.cpp +++ b/src/modules/registry.cpp @@ -26,6 +26,7 @@ #include "modules/intraDistance/intraDistance.h" #include "modules/intraShake/intraShake.h" #include "modules/md/md.h" +#include "modules/modifierOSites/modifierOSites.h" #include "modules/molShake/molShake.h" #include "modules/neutronSQ/neutronSQ.h" #include "modules/orientedSDF/orientedSDF.h" @@ -79,6 +80,8 @@ ModuleRegistry::ModuleRegistry() registerProducer( ModuleTypes::OrientedSDF, "Calculate spatial density functions around oriented sites, restricted by relative molecule orientation", "Analysis"); + registerProducer(ModuleTypes::ModifierOSites, + "Calculate the percentage FO, BO and NBO bonded to a modifier atom", "Analysis"); registerProducer(ModuleTypes::QSpecies, "Calculate QSpecies of a network former", "Analysis"); registerProducer(ModuleTypes::SDF, "Calculate spatial density functions around oriented sites", "Analysis"); registerProducer(ModuleTypes::SiteRDF, "Calculate radial distribution functions between sites", "Analysis"); diff --git a/src/modules/widgetProducer.cpp b/src/modules/widgetProducer.cpp index 0a1433448c..c965535260 100644 --- a/src/modules/widgetProducer.cpp +++ b/src/modules/widgetProducer.cpp @@ -36,6 +36,8 @@ #include "modules/intraAngle/intraAngle.h" #include "modules/intraDistance/gui/intraDistanceWidget.h" #include "modules/intraDistance/intraDistance.h" +#include "modules/modifierOSites/gui/modifierOSitesWidget.h" +#include "modules/modifierOSites/modifierOSites.h" #include "modules/neutronSQ/gui/neutronSQWidget.h" #include "modules/neutronSQ/neutronSQ.h" #include "modules/orientedSDF/gui/orientedSDFWidget.h" @@ -75,6 +77,7 @@ ModuleWidgetProducer::ModuleWidgetProducer() registerProducer(); registerProducer(); registerProducer(); + registerProducer(); registerProducer(); registerProducer(); registerProducer(); diff --git a/tests/data/dissolve/input/modifierOSites-test2.dat b/tests/data/dissolve/input/modifierOSites-test2.dat new file mode 100644 index 0000000000..5cac77e393 --- /dev/null +++ b/tests/data/dissolve/input/modifierOSites-test2.dat @@ -0,0 +1,3 @@ +0 0.1538 0.01538 +1 0.6154 0.06154 +2 0.2308 0.02308 diff --git a/tests/data/dissolve/input/modifierOSites-test2.txt b/tests/data/dissolve/input/modifierOSites-test2.txt new file mode 100755 index 0000000000..32d737a727 --- /dev/null +++ b/tests/data/dissolve/input/modifierOSites-test2.txt @@ -0,0 +1,117 @@ +# Input file written by Dissolve v1.5.0 at 15:57:24 on 09-02-2024. + +#==============================================================================# +# Species # +#==============================================================================# + +Species 'Si' + # Atoms + NAtoms 1 + Atom 1 Si 0.000000e+00 0.000000e+00 0.000000e+00 'Si' 0.000000e+00 + + # Sites + Site 'X' + Origin 1 + EndSite +EndSpecies + +Species 'O' + # Atoms + NAtoms 1 + Atom 1 O 0.000000e+00 0.000000e+00 0.000000e+00 'O' 0.000000e+00 + + # Sites + Site 'Y' + Origin 1 + EndSite +EndSpecies + +Species 'P' + # Atoms + NAtoms 1 + Atom 1 P 0.000000e+00 0.000000e+00 0.000000e+00 'P' 0.000000e+00 + + # Sites + Site 'Z' + Origin 1 + EndSite +EndSpecies + +#==============================================================================# +# Pair Potentials # +#==============================================================================# + +PairPotentials + # Atom Type Parameters + Parameters Si Si 0.000000e+00 LJ epsilon=0.175 sigma=1.03 + Parameters O O 0.000000e+00 LJ epsilon=0.165 sigma=3.5 + Parameters P P 0.000000e+00 LJ epsilon=0.8 sigma=0.75 + Range 10 + Delta 0.005 + ManualChargeSource True + IncludeCoulomb True + ForceChargeSource True + CoulombTruncation Shifted + ShortRangeTruncation Shifted +EndPairPotentials + +#==============================================================================# +# Configurations # +#==============================================================================# + +Configuration 'Bulk' + + # Generator + Generator + Box 'Box01' + Lengths 30 30 30 + Angles 90 90 90 + EndBox + Add 'Add01' + Species 'Si' + Population '5' + Density '0.1' atoms/A3 + Positioning Random + BoxAction None + EndAdd + Add 'Add02' + Species 'O' + Population '9' + Density '0.1' atoms/A3 + Positioning Random + BoxAction None + EndAdd + Add 'Add03' + Species 'P' + Population '10' + Density '0.1' atoms/A3 + Positioning Random + BoxAction None + EndAdd + ImportCoordinates 'ImportCoordinates01' + File xyz 'dissolve/input/modifierOSites-test2.xyz' + EndFile + EndImportCoordinates + EndGenerator + + Temperature 298 + +EndConfiguration + +#==============================================================================# +# Processing Layers # +#==============================================================================# + +Layer 'Processing' + Frequency 1 + + Module ModifierOSites 'M' + Frequency 1 + Configuration 'Bulk' + BondingOxygen 'O' 'Y' + NetworkFormer 'Si' 'X' + SiteA 'P' 'Z' + DistanceRange 0.000000e+00 2.100000e+00 + ModifierDistanceRange 0.000000e+00 2.100000e+00 + EndModule +EndLayer diff --git a/tests/data/dissolve/input/modifierOSites-test2.xyz b/tests/data/dissolve/input/modifierOSites-test2.xyz new file mode 100644 index 0000000000..eeacf99f23 --- /dev/null +++ b/tests/data/dissolve/input/modifierOSites-test2.xyz @@ -0,0 +1,26 @@ +24 +Simple network test +Si 4.000000 0.000000 0.000000 0.000000 +Si 8.000000 0.000000 0.000000 0.000000 +Si -2.000000 0.000000 -12.000000 0.000000 +Si -2.000000 0.000000 -16.000000 0.000000 +Si -2.000000 0.000000 -8.000000 0.000000 +O 6.000000 0.000000 0.000000 0.000000 +O 2.000000 0.000000 0.000000 0.000000 +O 2.000000 6.000000 8.000000 0.000000 +O -2.000000 0.000000 -10.000000 0.000000 +O -2.000000 0.000000 -14.000000 0.000000 +O -4.000000 0.000000 -16.000000 0.000000 +O -4.000000 0.000000 -8.000000 0.000000 +O -4.000000 0.000000 -12.000000 0.000000 +O 10.000000 10.000000 12.000000 0.000000 +P 2.000000 2.000000 0.000000 0.000000 +P 6.000000 2.000000 0.000000 0.000000 +P 4.000000 6.000000 8.000000 0.000000 +P 8.000000 8.000000 8.000000 0.000000 +P -4.000000 0.000000 -10.000000 0.000000 +P -4.000000 0.000000 -14.000000 0.000000 +P -6.000000 0.000000 -12.000000 0.000000 +P -6.000000 0.000000 -16.000000 0.000000 +P -6.000000 0.000000 -8.000000 0.000000 +P 10.000000 10.000000 10.000000 0.000000 \ No newline at end of file diff --git a/tests/data/dissolve/input/modifierOSites.dat b/tests/data/dissolve/input/modifierOSites.dat new file mode 100644 index 0000000000..0c774ddf59 --- /dev/null +++ b/tests/data/dissolve/input/modifierOSites.dat @@ -0,0 +1,3 @@ +0 0.25 0.025 +1 0.375 0.0375 +2 0.375 0.0375 diff --git a/tests/data/dissolve/input/modifierOSites.txt b/tests/data/dissolve/input/modifierOSites.txt new file mode 100755 index 0000000000..618122d111 --- /dev/null +++ b/tests/data/dissolve/input/modifierOSites.txt @@ -0,0 +1,117 @@ +# Input file written by Dissolve v1.5.0 at 15:57:24 on 09-02-2024. + +#==============================================================================# +# Species # +#==============================================================================# + +Species 'Si' + # Atoms + NAtoms 1 + Atom 1 Si 0.000000e+00 0.000000e+00 0.000000e+00 'Si' 0.000000e+00 + + # Sites + Site 'X' + Origin 1 + EndSite +EndSpecies + +Species 'O' + # Atoms + NAtoms 1 + Atom 1 O 0.000000e+00 0.000000e+00 0.000000e+00 'O' 0.000000e+00 + + # Sites + Site 'Y' + Origin 1 + EndSite +EndSpecies + +Species 'P' + # Atoms + NAtoms 1 + Atom 1 P 0.000000e+00 0.000000e+00 0.000000e+00 'P' 0.000000e+00 + + # Sites + Site 'Z' + Origin 1 + EndSite +EndSpecies + +#==============================================================================# +# Pair Potentials # +#==============================================================================# + +PairPotentials + # Atom Type Parameters + Parameters Si Si 0.000000e+00 LJ epsilon=0.175 sigma=1.03 + Parameters O O 0.000000e+00 LJ epsilon=0.165 sigma=3.5 + Parameters P P 0.000000e+00 LJ epsilon=0.8 sigma=0.75 + Range 10 + Delta 0.005 + ManualChargeSource True + IncludeCoulomb True + ForceChargeSource True + CoulombTruncation Shifted + ShortRangeTruncation Shifted +EndPairPotentials + +#==============================================================================# +# Configurations # +#==============================================================================# + +Configuration 'Bulk' + + # Generator + Generator + Box 'Box01' + Lengths 30 30 30 + Angles 90 90 90 + EndBox + Add 'Add01' + Species 'Si' + Population '9' + Density '0.1' atoms/A3 + Positioning Random + BoxAction None + EndAdd + Add 'Add02' + Species 'O' + Population '13' + Density '0.1' atoms/A3 + Positioning Random + BoxAction None + EndAdd + Add 'Add03' + Species 'P' + Population '8' + Density '0.1' atoms/A3 + Positioning Random + BoxAction None + EndAdd + ImportCoordinates 'ImportCoordinates01' + File xyz 'dissolve/input/modifierOSites.xyz' + EndFile + EndImportCoordinates + EndGenerator + + Temperature 298 + +EndConfiguration + +#==============================================================================# +# Processing Layers # +#==============================================================================# + +Layer 'Processing' + Frequency 1 + + Module ModifierOSites 'M' + Frequency 1 + Configuration 'Bulk' + BondingOxygen 'O' 'Y' + NetworkFormer 'Si' 'X' + SiteA 'P' 'Z' + DistanceRange 0.000000e+00 2.100000e+00 + ModifierDistanceRange 0.000000e+00 2.100000e+00 + EndModule +EndLayer diff --git a/tests/data/dissolve/input/modifierOSites.xyz b/tests/data/dissolve/input/modifierOSites.xyz new file mode 100644 index 0000000000..5df162d221 --- /dev/null +++ b/tests/data/dissolve/input/modifierOSites.xyz @@ -0,0 +1,32 @@ +30 +Simple network test +Si 0.000000 -4.000000 0.000000 0.000000 +Si 0.000000 0.000000 0.000000 0.000000 +Si 0.000000 4.000000 0.000000 0.000000 +Si 0.000000 0.000000 -4.000000 0.000000 +Si 0.000000 4.000000 -4.000000 0.000000 +Si 4.000000 0.000000 0.000000 0.000000 +Si 4.000000 0.000000 -4.000000 0.000000 +Si 8.000000 0.000000 0.000000 0.000000 +Si 8.000000 0.000000 -4.000000 0.000000 +O 0.000000 2.000000 0.000000 0.000000 +O 0.000000 -2.000000 0.000000 0.000000 +O -2.000000 0.000000 0.000000 0.000000 +O 2.000000 0.000000 0.000000 0.000000 +O 0.000000 0.000000 -2.000000 0.000000 +O 0.000000 0.000000 2.000000 0.000000 +O 0.000000 4.000000 -2.000000 0.000000 +O 0.000000 2.000000 -4.000000 0.000000 +O 2.000000 0.000000 -4.000000 0.000000 +O 6.000000 0.000000 0.000000 0.000000 +O 8.000000 2.000000 -4.000000 0.000000 +O 10.000000 10.000000 10.000000 0.000000 +O 6.000000 6.000000 6.000000 0.000000 +P 0.000000 2.000000 -6.000000 0.000000 +P 10.00000 2.000000 -4.000000 0.000000 +P 0.000000 0.000000 4.000000 0.000000 +P 6.000000 2.000000 0.000000 0.000000 +P -4.000000 0.000000 -0.000000 0.000000 +P 2.000000 0.000000 -6.000000 0.000000 +P 10.000000 12.000000 10.000000 0.000000 +P 4.000000 6.000000 6.000000 0.000000 diff --git a/tests/data/dissolve/input/modifierTotalOSites-test2.dat b/tests/data/dissolve/input/modifierTotalOSites-test2.dat new file mode 100644 index 0000000000..8c26c56092 --- /dev/null +++ b/tests/data/dissolve/input/modifierTotalOSites-test2.dat @@ -0,0 +1,4 @@ +0 0.1 0.01 +1 0.7 0.07 +2 0.0 0.0 +3 0.2 0.02 \ No newline at end of file diff --git a/tests/data/dissolve/input/modifierTotalOSites.dat b/tests/data/dissolve/input/modifierTotalOSites.dat new file mode 100644 index 0000000000..b62ac36d44 --- /dev/null +++ b/tests/data/dissolve/input/modifierTotalOSites.dat @@ -0,0 +1 @@ +1 1.0 0.1 diff --git a/tests/modules/CMakeLists.txt b/tests/modules/CMakeLists.txt index b982c506ff..e30eb6ea60 100644 --- a/tests/modules/CMakeLists.txt +++ b/tests/modules/CMakeLists.txt @@ -22,3 +22,4 @@ dissolve_add_test(SRC sdf.cpp) dissolve_add_test(SRC siteRDF.cpp) dissolve_add_test(SRC sq.cpp) dissolve_add_test(SRC xRaySQ.cpp) +dissolve_add_test(SRC modifierOSites.cpp) diff --git a/tests/modules/modifierOSites.cpp b/tests/modules/modifierOSites.cpp new file mode 100644 index 0000000000..e8ff6b8911 --- /dev/null +++ b/tests/modules/modifierOSites.cpp @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team Dissolve and contributors + +#include "tests/testData.h" +#include +#include + +namespace UnitTest +{ +class ModifierOSitesModuleTest : public ::testing::Test +{ + protected: + DissolveSystemTest systemTest; +}; + +TEST_F(ModifierOSitesModuleTest, Simple) +{ + ASSERT_NO_THROW(systemTest.setUp("dissolve/input/modifierOSites.txt")); + ASSERT_TRUE(systemTest.dissolve().iterate(1)); + + EXPECT_TRUE(systemTest.checkData1D( + "M//OTypes", {"dissolve/input/modifierOSites.dat", Data1DImportFileFormat::Data1DImportFormat::XY, 1, 2}, 1.0e-8)); + EXPECT_TRUE(systemTest.checkData1D( + "M//TotalOSites", {"dissolve/input/modifierTotalOSites.dat", Data1DImportFileFormat::Data1DImportFormat::XY, 1, 2}, + 1.0e-8)); +} +TEST_F(ModifierOSitesModuleTest, TotalOSites) +{ + ASSERT_NO_THROW(systemTest.setUp("dissolve/input/modifierOSites-test2.txt")); + ASSERT_TRUE(systemTest.dissolve().iterate(1)); + EXPECT_TRUE(systemTest.checkData1D( + "M//OTypes", {"dissolve/input/modifierOSites-test2.dat", Data1DImportFileFormat::Data1DImportFormat::XY, 1, 2}, + 1.0e-8)); + EXPECT_TRUE(systemTest.checkData1D( + "M//TotalOSites", + {"dissolve/input/modifierTotalOSites-test2.dat", Data1DImportFileFormat::Data1DImportFormat::XY, 1, 2}, 1.0e-8)); +} +} // namespace UnitTest From dbdcb95a44699ad655305d0f755e215434e8d66c Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Fri, 22 Mar 2024 13:56:23 +0000 Subject: [PATCH 11/13] fix: include final line of multi-line error messages (#1822) (#1823) --- src/base/messenger.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/base/messenger.cpp b/src/base/messenger.cpp index b185122115..4c2c0befb0 100644 --- a/src/base/messenger.cpp +++ b/src/base/messenger.cpp @@ -26,15 +26,15 @@ void Messenger::splitAndPrint(std::string_view s) { // The created text may be over multiple lines (separated by '\n') so split it, prepending the prefix and/or process id to // each line as necessary - size_t pos = s.find('\n'); + size_t pos; do { + pos = s.find('\n'); // Output the current line (up to the next newline delimiter) outputText(s.substr(0, pos)); // Adjust our view of the working string and find the next newline (if any) s = s.substr(pos + 1); - pos = s.find('\n'); } while (pos != std::string::npos); } From 2b69884fc43fa534789e92cbbc9d3754cef021d4 Mon Sep 17 00:00:00 2001 From: Noella Spitz <101950441+rhinoella@users.noreply.github.com> Date: Tue, 2 Apr 2024 15:04:20 +0100 Subject: [PATCH 12/13] ci: Remove -user flag from pip install (#1830) --- .github/workflows/build/osx/action.yml | 2 +- .github/workflows/package/osx/action.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build/osx/action.yml b/.github/workflows/build/osx/action.yml index 6445fcf8d0..44e32c7a97 100644 --- a/.github/workflows/build/osx/action.yml +++ b/.github/workflows/build/osx/action.yml @@ -29,7 +29,7 @@ runs: - name: Install Python Dependencies shell: bash run: | - pip3 install --user aqtinstall conan==1.* + pip3 install aqtinstall conan==1.* --break-system-packages - name: Retrieve Qt Cache id: cache-qt diff --git a/.github/workflows/package/osx/action.yml b/.github/workflows/package/osx/action.yml index c38e318ba5..9925c89de4 100644 --- a/.github/workflows/package/osx/action.yml +++ b/.github/workflows/package/osx/action.yml @@ -20,7 +20,7 @@ runs: - name: Install Python Dependencies shell: bash run: | - pip3 install --user dmgbuild biplist + pip3 install dmgbuild biplist --break-system-packages - name: Install Custom Dependencies shell: bash From 46fe9129137a448622eff1fd1fc1aeabc367d1b9 Mon Sep 17 00:00:00 2001 From: Noella Spitz <101950441+rhinoella@users.noreply.github.com> Date: Tue, 2 Apr 2024 15:09:48 +0100 Subject: [PATCH 13/13] feat: pass strings as const-references Co-authored-by: Tristan Youngs --- src/gui/gui.h | 2 +- src/gui/menu_file.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/gui.h b/src/gui/gui.h index 29cc52b908..9baaaa0f6e 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -127,7 +127,7 @@ class DissolveWindow : public QMainWindow private: // Set the input file path - void setInputFilename(const QString filename); + void setInputFilename(const QString &filename); // Check whether current input needs to be saved and, if so, if it saved successfully bool checkSaveCurrentInput(); // Clear all data and start new simulation afresh diff --git a/src/gui/menu_file.cpp b/src/gui/menu_file.cpp index 54326e6cbc..e5d7965cc2 100644 --- a/src/gui/menu_file.cpp +++ b/src/gui/menu_file.cpp @@ -8,7 +8,7 @@ #include // Set the input filename and the working directory of dissolve -void DissolveWindow::setInputFilename(const QString filename) +void DissolveWindow::setInputFilename(const QString &filename) { QFileInfo inputFileInfo(filename); QDir::setCurrent(inputFileInfo.absoluteDir().absolutePath());