Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a30b1d9
Add simple NumberNode for testing.
trisyoungs Jun 26, 2025
2f1386c
Better template argument name.
trisyoungs Jun 26, 2025
d2e155f
Remove old bounded parameter inputs.
trisyoungs Jun 26, 2025
1a1f608
Update TestNode and Parameters test.
trisyoungs Jun 26, 2025
224cb59
Rename type var.
trisyoungs Jun 26, 2025
747c759
Pause for thought.
trisyoungs Jun 27, 2025
0493cd4
Start the next journey.
trisyoungs Jun 27, 2025
6b0fd3f
Working.
trisyoungs Jun 27, 2025
7cc5438
Add ParameterBase::acceptsOutput() pure virtual, update unit test.
trisyoungs Jun 27, 2025
24b7a06
Some lunacy with std::conditional_t.
trisyoungs Jun 30, 2025
71acfc5
Start constructing a singular class for Parameters.
trisyoungs Jun 30, 2025
e778c8a
Working for custom getters.
trisyoungs Jun 30, 2025
fd38c67
Default getter function, do the same for setter.
trisyoungs Jun 30, 2025
1189f6c
Fix comment.
trisyoungs Jun 30, 2025
94422d2
Constify function.
trisyoungs Jun 30, 2025
b0d8096
Fix setData() logic.
trisyoungs Jun 30, 2025
efa80e9
Tidy comment.
trisyoungs Jun 30, 2025
0d4a2cc
Same for optional pointer retrieval.
trisyoungs Jun 30, 2025
fa76617
Virtual dtor on ParameterBase.
trisyoungs Jun 30, 2025
01401b5
Still need to default it.
Jul 1, 2025
a75e818
Rework EdgeMap into a vector.
Jul 1, 2025
669041e
Remove blank line.
Jul 1, 2025
d95c8d8
Extend unit test.
Jul 1, 2025
10663de
Update comment.
Jul 1, 2025
e3c275b
Clear vector and re-flag input edges when upstream data changes.
Jul 1, 2025
123f251
Remove old upcast() function.
Jul 1, 2025
41cad4b
Move template functions to algorithms.
Jul 1, 2025
999f20f
Remove old forward decs.
Jul 1, 2025
ebf177a
Flag updates / vector invalidation on edge removal.
Jul 1, 2025
3d4ca4a
Better enum.
Jul 4, 2025
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
4 changes: 2 additions & 2 deletions src/expression/value.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ class ExpressionValue : public Serialisable<>
ValueType type_;
// Whether current result type is fixed
bool typeFixed_;
// Integer value (if type_ == IntegerType)
// Integer value (if storedDataType_ == IntegerType)
int valueI_;
// Double value (if type_ == DoubleType)
// Double value (if storedDataType_ == DoubleType)
double valueD_;

public:
Expand Down
12 changes: 6 additions & 6 deletions src/gui/models/nodeGraph/parameterModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ QVariant ParameterModel::data(const QModelIndex &index, int role) const
case DESCRIPTION:
return QString::fromStdString(std::string(it->second->description()));
case DATA:
if (it->second->type() == typeid(Number))
return QVariant::fromValue(it->second->upcast<Number>()->get().asInteger());
if (it->second->type() == typeid(bool))
return QVariant::fromValue(it->second->upcast<bool>()->get());
if (it->second->storedDataType() == typeid(Number))
return QVariant::fromValue(it->second->get<Number>().asInteger());
if (it->second->storedDataType() == typeid(bool))
return QVariant::fromValue(it->second->get<bool>());
return QString::fromStdString("Unrepresentable");
case TYPE:
if (it->second->type() == typeid(Number))
if (it->second->storedDataType() == typeid(Number))
return "number";
if (it->second->type() == typeid(bool))
if (it->second->storedDataType() == typeid(bool))
return "bool";
return "unknown";

Expand Down
2 changes: 1 addition & 1 deletion src/nodes/atomicSpecies.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ AtomicSpeciesNode::AtomicSpeciesNode(Graph *parentGraph, Elements::Element Z) :
at->interactionPotential().setFormAndParameters(ShortRangeFunctions::Form::LennardJones, "epsilon=0.3 sigma=2.0");
species_.addAtom(Z, {}, 0.0, at);

addPointerOutput<const Species *>("Species", "Atomic species", species_);
addPointerOutput<const Species>("Species", "Atomic species", species_);
}

std::string_view AtomicSpeciesNode::type() const { return "AtomicSpecies"; }
Expand Down
2 changes: 1 addition & 1 deletion src/nodes/configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

ConfigurationNode::ConfigurationNode(Graph *parentGraph) : Node(parentGraph)
{
addPointerOutput<Configuration *>("Configuration", "Configuration object", configuration_);
addPointerOutput<Configuration>("Configuration", "Configuration object", configuration_);
}

std::string_view ConfigurationNode::type() const { return "Configuration"; }
Expand Down
15 changes: 11 additions & 4 deletions src/nodes/edge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ std::unique_ptr<Edge> Edge::create(Graph *parent, const EdgeDefinition &definiti
}

// Check that types are compatible
if (sourceOutput->type() != targetInput->type())
if (!targetInput->acceptsOutput(sourceOutput.get()))
return {};

// Create the edge
Expand All @@ -135,7 +135,7 @@ const ParameterBase &Edge::sourceOutput() const { return sourceOutput_; }
Node &Edge::targetNode() const { return targetNode_; }

// Return target input parameter
const ParameterBase &Edge::targetInput() const { return targetInput_; }
ParameterBase &Edge::targetInput() const { return targetInput_; }

// Return definition for the edge
EdgeDefinition Edge::definition() const
Expand Down Expand Up @@ -223,11 +223,18 @@ NodeConstants::ProcessResult Edge::pull()
return NodeConstants::ProcessResult::Unchanged;
}

// Ensure next call to pull() will retrieve the data from the source node
void Edge::forceNextPull() { sourceNodeVersionIndex_ = NodeConstants::InvalidVersion; }

/*
* I/O
*/

// Express as a serialisable value
SerialisedValue Edge::serialise() const { return definition().serialise(); }

// Read values from a serialisable value This is required for the
// SerialableValue type implementation, but we actually deserialise
// Read values from a serialisable value. This is required for the
// SerialisableValue type implementation, but we actually deserialise
// Edges through an EdgeConnection. I've added this error to
// immediately alert us in case this function is ever called.
void Edge::deserialise(const SerialisedValue &node)
Expand Down
4 changes: 3 additions & 1 deletion src/nodes/edge.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,13 @@ class Edge : public Serialisable<>
// Return target node
Node &targetNode() const;
// Return target input parameter
const ParameterBase &targetInput() const;
ParameterBase &targetInput() const;
// Return definition for the edge
EdgeDefinition definition() const;
// Pull the data from the source node to the target, returning a ProcessResult
NodeConstants::ProcessResult pull();
// Ensure next call to pull() will retrieve the data from the source node
void forceNextPull();

/*
* I/O
Expand Down
2 changes: 1 addition & 1 deletion src/nodes/gr/gr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ GRNode::GRNode(Graph *parentGraph) : Node(parentGraph)
internalTest_);
addOption<GRNode::PartialsMethod>("Method", "Calculation method for partial radial distribution functions",
partialsMethod_);
addOptionalPointerOutput<PartialSet *>("UnweightedGR", "Unweighted partials for target configuration", unweightedGR_);
addOptionalPointerOutput<PartialSet>("UnweightedGR", "Unweighted partials for target configuration", unweightedGR_);
}

// Return enum option info for NormalisationType
Expand Down
9 changes: 9 additions & 0 deletions src/nodes/graph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,16 @@ bool Graph::removeEdge(Edge *edgeToRemove)
std::find_if(edges_.begin(), edges_.end(), [edgeToRemove](const auto &edge) { return edge.get() == edgeToRemove; });
if (it == edges_.end())
return Messenger::error("Edge pointer doesn't exist, so can't remove it.\n");

// Need to flag the node containing the connected input that it is now out-of-date
auto &input = it->get()->targetInput();
input.setParentUpdateRequired();
if (input.isVector())
input.invalidateVector();

// Can now erase it
edges_.erase(it);

return true;
}

Expand Down
108 changes: 74 additions & 34 deletions src/nodes/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,20 @@ void Node::setUpdateRequired()
upToDate_ = false;

// Make sure all output edges propagate this information down
for (auto &&[outputName, edge] : outputEdges())
if (!edge->targetInput().flags().isSet(ParameterBase::ParameterFlags::NoUpdate))
edge->targetInput().setParentUpdateRequired();
for (auto &&[outputName, edges] : outputEdges())
for (auto edge : edges)
{
auto &input = edge->targetInput();

if (input.flags().isSet(ParameterBase::ParameterFlags::NoUpdate))
continue;

input.setParentUpdateRequired();

// If the target input is a vector, all edges to it must be marked for re-pull and its data cleared
if (input.isVector())
input.invalidateVector();
}
}

// Return whether the node's data is up-to-date
Expand All @@ -82,8 +93,9 @@ bool Node::inputsAreValid() const
// Does this input have a link or links?
if (inputEdges_.contains(inputName))
{
if (!inputEdges_.at(inputName)->sourceOutput().parent()->inputsAreValid())
return false;
for (const auto edge : inputEdges_.at(inputName))
if (!edge->sourceOutput().parent()->inputsAreValid())
return false;
}
else if (parameter->flags().isSet(ParameterBase::ParameterFlags::Required))
return false;
Expand All @@ -95,18 +107,20 @@ bool Node::inputsAreValid() const
// Run the node, retrieving dependent inputs as necessary
NodeConstants::ProcessResult Node::run()
{
// Check our input links - if any are out-of-date we must retrieve new values. This will automatically unset upToDate_
for (auto &[inputName, edge] : inputEdges_)
// Pull all input edges. If any are out-of-date and get re-set this will automatically unset upToDate_
for (auto &[inputName, edges] : inputEdges_)
{
auto edgeResult = edge->pull();
switch (edgeResult)
for (const auto edge : edges)
{
case (NodeConstants::ProcessResult::Failed):
case (NodeConstants::ProcessResult::InputsNotSatisfied):
return NodeConstants::ProcessResult::Failed;
case (NodeConstants::ProcessResult::Success):
case (NodeConstants::ProcessResult::Unchanged):
break;
switch (edge->pull())
{
case (NodeConstants::ProcessResult::Failed):
case (NodeConstants::ProcessResult::InputsNotSatisfied):
return NodeConstants::ProcessResult::Failed;
case (NodeConstants::ProcessResult::Success):
case (NodeConstants::ProcessResult::Unchanged):
break;
}
}
}

Expand Down Expand Up @@ -143,58 +157,74 @@ NodeConstants::ProcessResult Node::process() { return NodeConstants::ProcessResu
// Link edge, returning whether we accept it
bool Node::linkEdge(Edge *edge)
{

// The supplied Edge was created via our parent Graph, but we will still check to see whether we accept it
if (&edge->targetNode() == this)
{
// We are the target node, so we will double-check the specified input to see if it can accept the connection
// Simple check at present, we accept at most one connection per input, so if one already exists we complain
// We accept one connection per input in the case of non-vector parameters, so if one already exists we complain.
// Vector inputs are currently unbounded.
if (inputEdges_.contains(edge->targetInput().name()))
return Messenger::error("Node '{}' refusing to accept Edge connecting to input '{}' as one already exists.\n",
name(), edge->targetInput().name());
{
// Already have input edges to this parameter, so check current size and type
if (!inputEdges_.at(edge->targetInput().name()).empty())
{
if (edge->targetInput().nAllowedInputEdges() != ParameterBase::AllowedEdgeCount::AnyNumber)
return Messenger::error("Node '{}' refusing to accept Edge connecting to input '{}' as it already has the "
"maximum permissible.\n",
name(), edge->targetInput().name());
}
}

// All good, so add the input to our list
inputEdges_[edge->targetInput().name()] = edge;
inputEdges_[edge->targetInput().name()].push_back(edge);

// Adding an Edge to an input always invalidates the target
invalidate();
}
else if (&edge->sourceNode() == this)
{
// We are the source node - add the outgoing edge to our list
outputEdges_[edge->sourceOutput().name()] = edge;
outputEdges_[edge->sourceOutput().name()].push_back(edge);
}
else
return Messenger::error("Node '{}' is neither the source nor the target for the supplied Edge.\n", name());

return true;
}

// Erase the specified edge from the given map, returning if it was found and erased
bool Node::eraseEdge(EdgeMap &map, Edge *edge)
{
auto mapIt = std::find_if(map.begin(), map.end(),
[&](auto &edges)
{
auto edgeIt = std::find(edges.second.begin(), edges.second.end(), edge);
if (edgeIt != edges.second.end())
{
edges.second.erase(edgeIt);
return true;
}
return edgeIt != edges.second.end();
});
return mapIt != map.end();
}

// Unlink edge
void Node::unlinkEdge(Edge *edge)
{
// If we are the Edge's targetNode_ then we should have its pointer in inputEdges_
if (&edge->targetNode() == this)
{
auto it = std::find_if(inputEdges_.begin(), inputEdges_.end(),
[edge](const auto &inputEdge) { return edge == inputEdge.second; });
if (it == inputEdges_.end())
Messenger::error("Tried to unlink an incoming edge to target node '{}' which knew nothing about it.\n", name());
else
{
inputEdges_.erase(it);
if (eraseEdge(inputEdges_, edge))
invalidate();
}
else
Messenger::error("Tried to unlink an incoming edge to target node '{}' which knew nothing about it.\n", name());
}
else if (&edge->sourceNode() == this)
{
// We are the source node for the edge...
auto it = std::find_if(outputEdges_.begin(), outputEdges_.end(),
[edge](const auto &outputEdge) { return edge == outputEdge.second; });
if (it == outputEdges_.end())
if (!eraseEdge(outputEdges_, edge))
Messenger::error("Tried to unlink an outgoing edge from source node '{}' which knew nothing about it.\n", name());
else
outputEdges_.erase(it);
}
else
Messenger::error("Node '{}' is neither the source nor the target for the Edge being unlinked.\n", name());
Expand Down Expand Up @@ -256,6 +286,16 @@ Node::EdgeMap &Node::inputEdges() { return inputEdges_; }
// Get the outgoing edges from this node
Node::EdgeMap &Node::outputEdges() { return outputEdges_; }

// Mark incoming edges to the specified parameter as needing a re-pull
void Node::markIncomingEdgesForPull(const ParameterBase *toParameter) const
{
if (!inputEdges_.contains(toParameter->name()))
return;

for (const auto edge : inputEdges_.at(toParameter->name()))
edge->forceNextPull();
}

// Returns the node parent graph
Graph *Node::parentGraph() const { return parentGraph_; }

Expand Down
Loading
Loading