Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/nodes/graph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ void Graph::deserialise(const SerialisedValue &node)
}

/*
*Mermaid processing code
* Mermaid processing code
*/

// Node types that represent data sources
Expand Down
18 changes: 16 additions & 2 deletions src/nodes/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -356,15 +356,29 @@ void Node::deserialise(const SerialisedValue &node)
[this](const auto &k, const auto &v)
{
if (inputs_.contains(k))
inputs_[k]->deserialise(v);
try
{
inputs_[k]->deserialise(v);
}
catch (std::exception &ex)
{
Messenger::exception("Error reading input {} in node {} ({}).", k, name(), ex.what());
}
else
Messenger::exception("Node {} does not contain a parameter {}", name(), k);
});
toMap(node, "options",
[this](const auto &k, const auto &v)
{
if (options_.contains(k))
options_[k]->deserialise(v);
try
{
options_[k]->deserialise(v);
}
catch (std::exception &ex)
{
Messenger::exception("Error reading option {} in node {} ({}).", k, name(), ex.what());
}
else
Messenger::exception("Node {} does not contain an option {}", name(), k);
});
Expand Down
8 changes: 4 additions & 4 deletions src/nodes/parameter.h
Original file line number Diff line number Diff line change
Expand Up @@ -606,23 +606,23 @@ template <typename DataClass> class SerialisableParameter : public Parameter<Dat
else if constexpr (HasEnumOptions<DataClass>)
{
DataClass proxy; // Fake T value to get the correct overload
Parameter<DataClass>::data_ = getEnumOptions(proxy).deserialise(node);
Parameter<DataClass>::data_ = getEnumOptions(proxy).enumeration(toml::find<std::string>(node, "data"));
}
else if constexpr (std::is_convertible<DataClass, std::optional<double>>::value)
else if constexpr (std::is_same_v<DataClass, std::optional<double>>)
{
if (node.contains("data"))
Parameter<DataClass>::data_ = toml::find<double>(node, "data");
else
Parameter<DataClass>::data_ = {};
}
else if constexpr (std::is_convertible<DataClass, std::optional<Number>>::value)
else if constexpr (std::is_same_v<DataClass, std::optional<Number>>)
{
if (node.contains("data"))
Parameter<DataClass>::data_ = toml::find<Number>(node, "data");
else
Parameter<DataClass>::data_ = {};
}
else if constexpr (std::is_convertible<DataClass, std::optional<Data1D>>::value)
else if constexpr (std::is_same_v<DataClass, std::optional<Data1D>>)
{
if (node.contains("data"))
Parameter<DataClass>::data_ = toml::find<Data1D>(node, "data");
Expand Down
2 changes: 1 addition & 1 deletion tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ function(dissolve_add_test)

endfunction()

add_library(testing testing.cpp testGraph.cpp testing.h testGraph.h)
add_library(testing testing.cpp testGraph.cpp testing.h testGraph.h testGraphFixture.h)
target_link_libraries(testing PRIVATE GTest::gtest_main)
target_include_directories(
testing PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_BINARY_DIR}/src ${PROJECT_SOURCE_DIR} ${CONAN_INCLUDE_DIRS_GTEST}
Expand Down
167 changes: 110 additions & 57 deletions tests/nodes/sdf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,69 +5,122 @@
#include "nodes/importDLPUtilsPDens.h"
#include "nodes/iterableGraph.h"
#include "nodes/species.h"
#include "tests/testGraph.h"
#include "tests/testGraphFixture.h"

namespace UnitTest
{
TEST(SDFNodeTest, Water)
class SDFNodeWaterTest : public TestGraphFixture
{
// Set up the test graph
TestGraph testGraph;
testGraph.createConfiguration("Box", {{"species/water-dlpoly.toml", 267}}, 0.1);
private:
Data3D referenceData_;

// Create trajectory iterator
auto iterator = testGraph.appendTrajectoryIterator("ImportXYZTrajectory", "dlpoly/water267-analysis/water-267-298K.xyz");
EXPECT_TRUE(iterator);
protected:
// Prepare any necessary test data
void prepareTestData() override
{
ASSERT_TRUE(ImportDLPUtilsPDensNode::read(referenceData_, "dlpoly/water267-analysis/water-267-298K.11.pdens"));
}
// Perform graph construction
void constructGraph() override
{
testGraph_.createConfiguration("Box", {{"species/water-dlpoly.toml", 267}}, 0.1);

// Add the analysis module to the iterator
auto sdf = dynamic_cast<SDFNode *>(iterator->createNode("SDF"));
ASSERT_TRUE(sdf);
auto *water = testGraph.findNode("Water")->getOutputValue<const Species *>("Species");
ASSERT_TRUE(water);
ASSERT_TRUE(sdf->setOption<SpeciesSites>("SiteA", {{water->findSite("HMidpoint")}}));
ASSERT_TRUE(sdf->setOption<SpeciesSites>("SiteB", {{water->findSite("HMidpoint")}}));
ASSERT_TRUE(sdf->setOption<Vector3>("RangeX", {-10.25, 10.25, 0.5}));
ASSERT_TRUE(sdf->setOption<Vector3>("RangeY", {-10.25, 10.25, 0.5}));
ASSERT_TRUE(sdf->setOption<Vector3>("RangeZ", {-10.25, 10.25, 0.5}));
ASSERT_TRUE(iterator->addEdge({testGraph.fetchHeadName(), "Configuration", "SDF", "Configuration"}));
// Create trajectory iterator
auto iterator =
testGraph_.appendTrajectoryIterator("ImportXYZTrajectory", "dlpoly/water267-analysis/water-267-298K.xyz");
ASSERT_TRUE(iterator);
ASSERT_TRUE(iterator->setOption<Number>("N", 95));

// Run from the iterator node explicitly
ASSERT_TRUE(iterator->setOption<Number>("N", 95));
ASSERT_EQ(iterator->run(), NodeConstants::ProcessResult::Success);
// Add the analysis module to the iterator
auto sdf = dynamic_cast<SDFNode *>(iterator->createNode("SDF"));
ASSERT_TRUE(sdf);
auto *water = testGraph_.findNode("Water")->getOutputValue<const Species *>("Species");
ASSERT_TRUE(water);
ASSERT_TRUE(sdf->setOption<SpeciesSites>("SiteA", {{water->findSite("HMidpoint")}}));
ASSERT_TRUE(sdf->setOption<SpeciesSites>("SiteB", {{water->findSite("HMidpoint")}}));
ASSERT_TRUE(sdf->setOption<Vector3>("RangeX", {-10.25, 10.25, 0.5}));
ASSERT_TRUE(sdf->setOption<Vector3>("RangeY", {-10.25, 10.25, 0.5}));
ASSERT_TRUE(sdf->setOption<Vector3>("RangeZ", {-10.25, 10.25, 0.5}));
ASSERT_TRUE(iterator->addEdge({testGraph_.fetchHeadName(), "Configuration", "SDF", "Configuration"}));
}
// Run the graph
void runGraph() override
{
auto *iterator = findNode<IterableGraph>("Iterator");
ASSERT_TRUE(iterator);
ASSERT_EQ(iterator->run(), NodeConstants::ProcessResult::Success);
}
// Perform tests on generated data
void performTests() override
{
auto *iterator = findNode<IterableGraph>("Iterator");
ASSERT_TRUE(iterator);
auto *sdf = dynamic_cast<SDFNode *>(iterator->findNode("SDF"));
ASSERT_TRUE(sdf);
EXPECT_TRUE(testData3D(sdf->sdf(), "SDF", referenceData_, "dlpoly/water267-analysis/water-267-298K.11.pdens", 0.13));
}
};

Data3D referenceData;
EXPECT_TRUE(ImportDLPUtilsPDensNode::read(referenceData, "dlpoly/water267-analysis/water-267-298K.11.pdens"));
EXPECT_TRUE(testData3D(sdf->sdf(), "SDF", referenceData, "dlpoly/water267-analysis/water-267-298K.11.pdens", 0.13));
}

TEST(SDFNodeTest, Benzene)
{
// Set up the test graph
TestGraph testGraph;
testGraph.createConfiguration("Box", {{"species/benzene.toml", 181}}, {29.925089931000, 29.925089931000, 29.925089931000});

// Create trajectory iterator
auto iterator = testGraph.appendTrajectoryIterator("ImportXYZTrajectory", "dlpoly/benzene181/benzene181.xyz");
EXPECT_TRUE(iterator);

// Add the analysis module to the iterator
auto sdf = dynamic_cast<SDFNode *>(iterator->createNode("SDF"));
ASSERT_TRUE(sdf);
auto *benzene = testGraph.findNode("Benzene")->getOutputValue<const Species *>("Species");
ASSERT_TRUE(benzene);
ASSERT_TRUE(sdf->setOption<SpeciesSites>("SiteA", {{benzene->findSite("Ring")}}));
ASSERT_TRUE(sdf->setOption<SpeciesSites>("SiteB", {{benzene->findSite("Ring")}}));
ASSERT_TRUE(sdf->setOption<Vector3>("RangeX", {-10.25, 10.25, 0.5}));
ASSERT_TRUE(sdf->setOption<Vector3>("RangeY", {-10.25, 10.25, 0.5}));
ASSERT_TRUE(sdf->setOption<Vector3>("RangeZ", {-10.25, 10.25, 0.5}));
ASSERT_TRUE(iterator->addEdge({testGraph.fetchHeadName(), "Configuration", "SDF", "Configuration"}));

// Run from the iterator node explicitly
ASSERT_TRUE(iterator->setOption<Number>("N", 80));
ASSERT_EQ(iterator->run(), NodeConstants::ProcessResult::Success);

Data3D referenceData;
EXPECT_TRUE(ImportDLPUtilsPDensNode::read(referenceData, "dlpoly/benzene181/benzene181.11.pdens"));
EXPECT_TRUE(testData3D(sdf->sdf(), "SDF", referenceData, "dlpoly/benzene181/benzene181.11.pdens", 0.3));
}
TEST_F(SDFNodeWaterTest, Water) { go(); }
// {
// // Set up the test graph
// TestGraph testGraph;
// testGraph.createConfiguration("Box", {{"species/water-dlpoly.toml", 267}}, 0.1);
//
// // Create trajectory iterator
// auto iterator = testGraph.appendTrajectoryIterator("ImportXYZTrajectory", "dlpoly/water267-analysis/water-267-298K.xyz");
// EXPECT_TRUE(iterator);
//
// // Add the analysis module to the iterator
// auto sdf = dynamic_cast<SDFNode *>(iterator->createNode("SDF"));
// ASSERT_TRUE(sdf);
// auto *water = testGraph.findNode("Water")->getOutputValue<const Species *>("Species");
// ASSERT_TRUE(water);
// ASSERT_TRUE(sdf->setOption<SpeciesSites>("SiteA", {{water->findSite("HMidpoint")}}));
// ASSERT_TRUE(sdf->setOption<SpeciesSites>("SiteB", {{water->findSite("HMidpoint")}}));
// ASSERT_TRUE(sdf->setOption<Vector3>("RangeX", {-10.25, 10.25, 0.5}));
// ASSERT_TRUE(sdf->setOption<Vector3>("RangeY", {-10.25, 10.25, 0.5}));
// ASSERT_TRUE(sdf->setOption<Vector3>("RangeZ", {-10.25, 10.25, 0.5}));
// ASSERT_TRUE(iterator->addEdge({testGraph.fetchHeadName(), "Configuration", "SDF", "Configuration"}));
//
// // Run from the iterator node explicitly
// ASSERT_TRUE(iterator->setOption<Number>("N", 95));
// ASSERT_EQ(iterator->run(), NodeConstants::ProcessResult::Success);
//
// Data3D referenceData;
// EXPECT_TRUE(ImportDLPUtilsPDensNode::read(referenceData, "dlpoly/water267-analysis/water-267-298K.11.pdens"));
// EXPECT_TRUE(testData3D(sdf->sdf(), "SDF", referenceData, "dlpoly/water267-analysis/water-267-298K.11.pdens", 0.13));
// }
//
// TEST(SDFNodeTest, Benzene)
// {
// // Set up the test graph
// TestGraph testGraph;
// testGraph.createConfiguration("Box", {{"species/benzene.toml", 181}},
// {29.925089931000, 29.925089931000, 29.925089931000});
//
// // Create trajectory iterator
// auto iterator = testGraph.appendTrajectoryIterator("ImportXYZTrajectory", "dlpoly/benzene181/benzene181.xyz");
// EXPECT_TRUE(iterator);
//
// // Add the analysis module to the iterator
// auto sdf = dynamic_cast<SDFNode *>(iterator->createNode("SDF"));
// ASSERT_TRUE(sdf);
// auto *benzene = testGraph.findNode("Benzene")->getOutputValue<const Species *>("Species");
// ASSERT_TRUE(benzene);
// ASSERT_TRUE(sdf->setOption<SpeciesSites>("SiteA", {{benzene->findSite("Ring")}}));
// ASSERT_TRUE(sdf->setOption<SpeciesSites>("SiteB", {{benzene->findSite("Ring")}}));
// ASSERT_TRUE(sdf->setOption<Vector3>("RangeX", {-10.25, 10.25, 0.5}));
// ASSERT_TRUE(sdf->setOption<Vector3>("RangeY", {-10.25, 10.25, 0.5}));
// ASSERT_TRUE(sdf->setOption<Vector3>("RangeZ", {-10.25, 10.25, 0.5}));
// ASSERT_TRUE(iterator->addEdge({testGraph.fetchHeadName(), "Configuration", "SDF", "Configuration"}));
//
// // Run from the iterator node explicitly
// ASSERT_TRUE(iterator->setOption<Number>("N", 80));
// ASSERT_EQ(iterator->run(), NodeConstants::ProcessResult::Success);
//
// Data3D referenceData;
// EXPECT_TRUE(ImportDLPUtilsPDensNode::read(referenceData, "dlpoly/benzene181/benzene181.11.pdens"));
// EXPECT_TRUE(testData3D(sdf->sdf(), "SDF", referenceData, "dlpoly/benzene181/benzene181.11.pdens", 0.3));
// }
} // namespace UnitTest
2 changes: 1 addition & 1 deletion tests/testGraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ IterableGraph *TestGraph::appendTrajectoryIterator(std::string trajectoryImportN
auto oldGraph = currentGraph_;

// Add iterator node and make it the current graph
currentGraph_ = dynamic_cast<IterableGraph *>(appendNode("Iterator", "Iterator"));
currentGraph_ = dynamic_cast<IterableGraph *>(appendNode("Iterator"));
EXPECT_TRUE(currentGraph_);
head_ = nullptr;

Expand Down
93 changes: 93 additions & 0 deletions tests/testGraphFixture.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2026 Team Dissolve and contributors

#pragma once

#include "tests/testGraph.h"

namespace UnitTest
{
class TestGraphFixture : public testing::Test
{
public:
TestGraphFixture() = default;
~TestGraphFixture() override = default;

private:
// Serialised graph TOML
SerialisedValue graphTOML_;

protected:
// Test graph
TestGraph testGraph_;
// Deserialised graph
DissolveGraph deserialisedGraph_;
// Current graph target
OptionalReferenceWrapper<DissolveGraph> currentGraph_;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps go ahead and make this DissolveGraph* ? I know that eventually removing OptionalReferenceWrapper is on the RoadMap.


private:
// Serialise the current graph to a TOML
bool serialiseGraphToTOML()
{
try
{
testGraph_.serialise("graph", graphTOML_);
}
catch (std::exception &ex)
{
std::cout << std::format("Failed to serialise graph TOML for test {}:\n", "TODO");
std::cout << ex.what() << std::endl;
return false;
}

return true;
}

protected:
// Prepare any necessary test data
virtual void prepareTestData() = 0;
// Perform graph construction
virtual void constructGraph() = 0;
// Run graph
virtual void runGraph() = 0;
// Perform tests on generated data
virtual void performTests() = 0;
// Find specified node
template <class NodeClass> NodeClass *findNode(std::string nodeName)
{
EXPECT_TRUE(currentGraph_.has_value());
auto *node = currentGraph_.value().get().findNode(nodeName);
EXPECT_TRUE(node);
return dynamic_cast<NodeClass *>(node);
}

// Go
void go()
{
// Prepare test data
ASSERT_NO_THROW(prepareTestData());

// Set the initial graph target to the test graph
currentGraph_ = testGraph_;

// Construct the graph
ASSERT_NO_THROW(constructGraph());
// Run the graph
ASSERT_NO_THROW(runGraph());
// Run the tests
ASSERT_NO_THROW(performTests());
// Serialise graph to TOML
ASSERT_TRUE(serialiseGraphToTOML());

// Switch to the deserialised graph target
currentGraph_ = deserialisedGraph_;

// Deserialise from the stored TOML
ASSERT_NO_THROW(deserialisedGraph_.deserialise(graphTOML_["graph"]));
// Run the graph
ASSERT_NO_THROW(runGraph());
// Run the tests
ASSERT_NO_THROW(performTests());
}
};
}; // namespace UnitTest