Skip to content

Commit 8701baf

Browse files
trisyoungsTristan Youngs
andauthored
feat: Multiple inputs to vector-type parameters (#2189)
Co-authored-by: Tristan Youngs <trisyoungs@googlemail.com>
1 parent bf8cff4 commit 8701baf

22 files changed

Lines changed: 629 additions & 348 deletions

src/expression/value.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ class ExpressionValue : public Serialisable<>
3535
ValueType type_;
3636
// Whether current result type is fixed
3737
bool typeFixed_;
38-
// Integer value (if type_ == IntegerType)
38+
// Integer value (if storedDataType_ == IntegerType)
3939
int valueI_;
40-
// Double value (if type_ == DoubleType)
40+
// Double value (if storedDataType_ == DoubleType)
4141
double valueD_;
4242

4343
public:

src/gui/models/nodeGraph/parameterModel.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,15 @@ QVariant ParameterModel::data(const QModelIndex &index, int role) const
2929
case DESCRIPTION:
3030
return QString::fromStdString(std::string(it->second->description()));
3131
case DATA:
32-
if (it->second->type() == typeid(Number))
33-
return QVariant::fromValue(it->second->upcast<Number>()->get().asInteger());
34-
if (it->second->type() == typeid(bool))
35-
return QVariant::fromValue(it->second->upcast<bool>()->get());
32+
if (it->second->storedDataType() == typeid(Number))
33+
return QVariant::fromValue(it->second->get<Number>().asInteger());
34+
if (it->second->storedDataType() == typeid(bool))
35+
return QVariant::fromValue(it->second->get<bool>());
3636
return QString::fromStdString("Unrepresentable");
3737
case TYPE:
38-
if (it->second->type() == typeid(Number))
38+
if (it->second->storedDataType() == typeid(Number))
3939
return "number";
40-
if (it->second->type() == typeid(bool))
40+
if (it->second->storedDataType() == typeid(bool))
4141
return "bool";
4242
return "unknown";
4343

src/nodes/atomicSpecies.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ AtomicSpeciesNode::AtomicSpeciesNode(Graph *parentGraph, Elements::Element Z) :
77
at->interactionPotential().setFormAndParameters(ShortRangeFunctions::Form::LennardJones, "epsilon=0.3 sigma=2.0");
88
species_.addAtom(Z, {}, 0.0, at);
99

10-
addPointerOutput<const Species *>("Species", "Atomic species", species_);
10+
addPointerOutput<const Species>("Species", "Atomic species", species_);
1111
}
1212

1313
std::string_view AtomicSpeciesNode::type() const { return "AtomicSpecies"; }

src/nodes/configuration.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
ConfigurationNode::ConfigurationNode(Graph *parentGraph) : Node(parentGraph)
77
{
8-
addPointerOutput<Configuration *>("Configuration", "Configuration object", configuration_);
8+
addPointerOutput<Configuration>("Configuration", "Configuration object", configuration_);
99
}
1010

1111
std::string_view ConfigurationNode::type() const { return "Configuration"; }

src/nodes/edge.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ std::unique_ptr<Edge> Edge::create(Graph *parent, const EdgeDefinition &definiti
112112
}
113113

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

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

137137
// Return target input parameter
138-
const ParameterBase &Edge::targetInput() const { return targetInput_; }
138+
ParameterBase &Edge::targetInput() const { return targetInput_; }
139139

140140
// Return definition for the edge
141141
EdgeDefinition Edge::definition() const
@@ -223,11 +223,18 @@ NodeConstants::ProcessResult Edge::pull()
223223
return NodeConstants::ProcessResult::Unchanged;
224224
}
225225

226+
// Ensure next call to pull() will retrieve the data from the source node
227+
void Edge::forceNextPull() { sourceNodeVersionIndex_ = NodeConstants::InvalidVersion; }
228+
229+
/*
230+
* I/O
231+
*/
232+
226233
// Express as a serialisable value
227234
SerialisedValue Edge::serialise() const { return definition().serialise(); }
228235

229-
// Read values from a serialisable value This is required for the
230-
// SerialableValue type implementation, but we actually deserialise
236+
// Read values from a serialisable value. This is required for the
237+
// SerialisableValue type implementation, but we actually deserialise
231238
// Edges through an EdgeConnection. I've added this error to
232239
// immediately alert us in case this function is ever called.
233240
void Edge::deserialise(const SerialisedValue &node)

src/nodes/edge.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,13 @@ class Edge : public Serialisable<>
6666
// Return target node
6767
Node &targetNode() const;
6868
// Return target input parameter
69-
const ParameterBase &targetInput() const;
69+
ParameterBase &targetInput() const;
7070
// Return definition for the edge
7171
EdgeDefinition definition() const;
7272
// Pull the data from the source node to the target, returning a ProcessResult
7373
NodeConstants::ProcessResult pull();
74+
// Ensure next call to pull() will retrieve the data from the source node
75+
void forceNextPull();
7476

7577
/*
7678
* I/O

src/nodes/gr/gr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ GRNode::GRNode(Graph *parentGraph) : Node(parentGraph)
2525
internalTest_);
2626
addOption<GRNode::PartialsMethod>("Method", "Calculation method for partial radial distribution functions",
2727
partialsMethod_);
28-
addOptionalPointerOutput<PartialSet *>("UnweightedGR", "Unweighted partials for target configuration", unweightedGR_);
28+
addOptionalPointerOutput<PartialSet>("UnweightedGR", "Unweighted partials for target configuration", unweightedGR_);
2929
}
3030

3131
// Return enum option info for NormalisationType

src/nodes/graph.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,16 @@ bool Graph::removeEdge(Edge *edgeToRemove)
192192
std::find_if(edges_.begin(), edges_.end(), [edgeToRemove](const auto &edge) { return edge.get() == edgeToRemove; });
193193
if (it == edges_.end())
194194
return Messenger::error("Edge pointer doesn't exist, so can't remove it.\n");
195+
196+
// Need to flag the node containing the connected input that it is now out-of-date
197+
auto &input = it->get()->targetInput();
198+
input.setParentUpdateRequired();
199+
if (input.isVector())
200+
input.invalidateVector();
201+
202+
// Can now erase it
195203
edges_.erase(it);
204+
196205
return true;
197206
}
198207

src/nodes/node.cpp

Lines changed: 74 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,20 @@ void Node::setUpdateRequired()
6666
upToDate_ = false;
6767

6868
// Make sure all output edges propagate this information down
69-
for (auto &&[outputName, edge] : outputEdges())
70-
if (!edge->targetInput().flags().isSet(ParameterBase::ParameterFlags::NoUpdate))
71-
edge->targetInput().setParentUpdateRequired();
69+
for (auto &&[outputName, edges] : outputEdges())
70+
for (auto edge : edges)
71+
{
72+
auto &input = edge->targetInput();
73+
74+
if (input.flags().isSet(ParameterBase::ParameterFlags::NoUpdate))
75+
continue;
76+
77+
input.setParentUpdateRequired();
78+
79+
// If the target input is a vector, all edges to it must be marked for re-pull and its data cleared
80+
if (input.isVector())
81+
input.invalidateVector();
82+
}
7283
}
7384

7485
// Return whether the node's data is up-to-date
@@ -82,8 +93,9 @@ bool Node::inputsAreValid() const
8293
// Does this input have a link or links?
8394
if (inputEdges_.contains(inputName))
8495
{
85-
if (!inputEdges_.at(inputName)->sourceOutput().parent()->inputsAreValid())
86-
return false;
96+
for (const auto edge : inputEdges_.at(inputName))
97+
if (!edge->sourceOutput().parent()->inputsAreValid())
98+
return false;
8799
}
88100
else if (parameter->flags().isSet(ParameterBase::ParameterFlags::Required))
89101
return false;
@@ -95,18 +107,20 @@ bool Node::inputsAreValid() const
95107
// Run the node, retrieving dependent inputs as necessary
96108
NodeConstants::ProcessResult Node::run()
97109
{
98-
// Check our input links - if any are out-of-date we must retrieve new values. This will automatically unset upToDate_
99-
for (auto &[inputName, edge] : inputEdges_)
110+
// Pull all input edges. If any are out-of-date and get re-set this will automatically unset upToDate_
111+
for (auto &[inputName, edges] : inputEdges_)
100112
{
101-
auto edgeResult = edge->pull();
102-
switch (edgeResult)
113+
for (const auto edge : edges)
103114
{
104-
case (NodeConstants::ProcessResult::Failed):
105-
case (NodeConstants::ProcessResult::InputsNotSatisfied):
106-
return NodeConstants::ProcessResult::Failed;
107-
case (NodeConstants::ProcessResult::Success):
108-
case (NodeConstants::ProcessResult::Unchanged):
109-
break;
115+
switch (edge->pull())
116+
{
117+
case (NodeConstants::ProcessResult::Failed):
118+
case (NodeConstants::ProcessResult::InputsNotSatisfied):
119+
return NodeConstants::ProcessResult::Failed;
120+
case (NodeConstants::ProcessResult::Success):
121+
case (NodeConstants::ProcessResult::Unchanged):
122+
break;
123+
}
110124
}
111125
}
112126

@@ -143,58 +157,74 @@ NodeConstants::ProcessResult Node::process() { return NodeConstants::ProcessResu
143157
// Link edge, returning whether we accept it
144158
bool Node::linkEdge(Edge *edge)
145159
{
146-
147160
// The supplied Edge was created via our parent Graph, but we will still check to see whether we accept it
148161
if (&edge->targetNode() == this)
149162
{
150163
// We are the target node, so we will double-check the specified input to see if it can accept the connection
151-
// Simple check at present, we accept at most one connection per input, so if one already exists we complain
164+
// We accept one connection per input in the case of non-vector parameters, so if one already exists we complain.
165+
// Vector inputs are currently unbounded.
152166
if (inputEdges_.contains(edge->targetInput().name()))
153-
return Messenger::error("Node '{}' refusing to accept Edge connecting to input '{}' as one already exists.\n",
154-
name(), edge->targetInput().name());
167+
{
168+
// Already have input edges to this parameter, so check current size and type
169+
if (!inputEdges_.at(edge->targetInput().name()).empty())
170+
{
171+
if (edge->targetInput().nAllowedInputEdges() != ParameterBase::AllowedEdgeCount::AnyNumber)
172+
return Messenger::error("Node '{}' refusing to accept Edge connecting to input '{}' as it already has the "
173+
"maximum permissible.\n",
174+
name(), edge->targetInput().name());
175+
}
176+
}
155177

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

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

170192
return true;
171193
}
172194

195+
// Erase the specified edge from the given map, returning if it was found and erased
196+
bool Node::eraseEdge(EdgeMap &map, Edge *edge)
197+
{
198+
auto mapIt = std::find_if(map.begin(), map.end(),
199+
[&](auto &edges)
200+
{
201+
auto edgeIt = std::find(edges.second.begin(), edges.second.end(), edge);
202+
if (edgeIt != edges.second.end())
203+
{
204+
edges.second.erase(edgeIt);
205+
return true;
206+
}
207+
return edgeIt != edges.second.end();
208+
});
209+
return mapIt != map.end();
210+
}
211+
173212
// Unlink edge
174213
void Node::unlinkEdge(Edge *edge)
175214
{
176215
// If we are the Edge's targetNode_ then we should have its pointer in inputEdges_
177216
if (&edge->targetNode() == this)
178217
{
179-
auto it = std::find_if(inputEdges_.begin(), inputEdges_.end(),
180-
[edge](const auto &inputEdge) { return edge == inputEdge.second; });
181-
if (it == inputEdges_.end())
182-
Messenger::error("Tried to unlink an incoming edge to target node '{}' which knew nothing about it.\n", name());
183-
else
184-
{
185-
inputEdges_.erase(it);
218+
if (eraseEdge(inputEdges_, edge))
186219
invalidate();
187-
}
220+
else
221+
Messenger::error("Tried to unlink an incoming edge to target node '{}' which knew nothing about it.\n", name());
188222
}
189223
else if (&edge->sourceNode() == this)
190224
{
191225
// We are the source node for the edge...
192-
auto it = std::find_if(outputEdges_.begin(), outputEdges_.end(),
193-
[edge](const auto &outputEdge) { return edge == outputEdge.second; });
194-
if (it == outputEdges_.end())
226+
if (!eraseEdge(outputEdges_, edge))
195227
Messenger::error("Tried to unlink an outgoing edge from source node '{}' which knew nothing about it.\n", name());
196-
else
197-
outputEdges_.erase(it);
198228
}
199229
else
200230
Messenger::error("Node '{}' is neither the source nor the target for the Edge being unlinked.\n", name());
@@ -256,6 +286,16 @@ Node::EdgeMap &Node::inputEdges() { return inputEdges_; }
256286
// Get the outgoing edges from this node
257287
Node::EdgeMap &Node::outputEdges() { return outputEdges_; }
258288

289+
// Mark incoming edges to the specified parameter as needing a re-pull
290+
void Node::markIncomingEdgesForPull(const ParameterBase *toParameter) const
291+
{
292+
if (!inputEdges_.contains(toParameter->name()))
293+
return;
294+
295+
for (const auto edge : inputEdges_.at(toParameter->name()))
296+
edge->forceNextPull();
297+
}
298+
259299
// Returns the node parent graph
260300
Graph *Node::parentGraph() const { return parentGraph_; }
261301

0 commit comments

Comments
 (0)