Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Analysis Refactoring - Part 2 #1795

Merged
merged 121 commits into from Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
121 commits
Select commit Hold shift + click to select a range
d11827b
Cleanout procedure 'stuff'.
jws-1 Feb 4, 2024
2a1a8ec
Compute coordination histogram.
jws-1 Feb 4, 2024
52f82c5
Reinventing SiteRDF
jws-1 Feb 11, 2024
4065218
Normalise by value.
jws-1 Feb 4, 2024
3fcb72c
Perform normalisation.
jws-1 Feb 4, 2024
305ea2b
Render correct data.
jws-1 Feb 4, 2024
1478043
Drop default bools.
jws-1 Feb 4, 2024
5e3a994
Use excludeSameMolecule flag and drop unused map.
jws-1 Feb 4, 2024
d6619ae
std::
jws-1 Feb 4, 2024
bf5d246
Update test.
jws-1 Feb 4, 2024
df7c80e
Remember to initialise the histogram.
jws-1 Feb 4, 2024
e60a671
Some commenting and renaming.
jws-1 Feb 4, 2024
efb21ba
Drop procedure analysis.
jws-1 Feb 4, 2024
5fe3361
Basic processing logic.
jws-1 Feb 4, 2024
36f8e26
Reinventing SiteRDF
jws-1 Feb 11, 2024
2724011
DataNormaliser3D.
jws-1 Feb 4, 2024
f156b5c
Normalise.
jws-1 Feb 4, 2024
77dacbc
3D grid normalisation.
jws-1 Feb 4, 2024
2e655a0
It's not a pointer..
jws-1 Feb 4, 2024
b069f8f
Can't pass Vec3s, and remember parenthesis.
jws-1 Feb 4, 2024
730530e
Fix var.
jws-1 Feb 4, 2024
ec6635d
Fixup export.
jws-1 Feb 4, 2024
26890d1
Update test.
jws-1 Feb 4, 2024
ab996ba
Fixup test.
jws-1 Feb 4, 2024
7b6b583
Remember to zero the bins and use excludeSameMolecule flag.
jws-1 Feb 4, 2024
20a2e2f
Setup.
jws-1 Feb 4, 2024
efc637d
Started implementing processing logic.
jws-1 Feb 4, 2024
d7f7619
Reinventing SDF
jws-1 Feb 11, 2024
bff18c7
Normalisation.
jws-1 Feb 4, 2024
b0ef42c
Correct export.
jws-1 Feb 4, 2024
980bb91
Update tests.
jws-1 Feb 4, 2024
20884f0
Renderable.
jws-1 Feb 4, 2024
7f3ab38
Exclude same site and same molecule.
jws-1 Feb 4, 2024
a0aec3c
Fix data name.
jws-1 Feb 4, 2024
ef8432a
Drop procedure 'stuff'.
jws-1 Feb 4, 2024
e736860
Setup.
jws-1 Feb 4, 2024
2c3c858
Process logic.
jws-1 Feb 4, 2024
c8bb3dc
Reinventing HistogramCN
jws-1 Feb 11, 2024
e45efb0
Perform normalisation.
jws-1 Feb 4, 2024
cdf8d0a
Fixup constructor.!
jws-1 Feb 4, 2024
19979ba
Normalise by value.
jws-1 Feb 4, 2024
5367a6d
Update test.
jws-1 Feb 4, 2024
b7efe60
Reference!
jws-1 Feb 4, 2024
aa64a2d
Get rid of procedure.
jws-1 Feb 4, 2024
a8f7505
Calculate angle.
jws-1 Feb 4, 2024
a1d2da0
Reinventing OrientedSDF
jws-1 Feb 11, 2024
7ce1c90
This is 1D, not 3D!
jws-1 Feb 4, 2024
5763ca9
Correct site loop.
jws-1 Feb 4, 2024
bc8bd8e
Normalise by expression.
jws-1 Feb 4, 2024
cbf485d
Fix normalisation by expression.
jws-1 Feb 4, 2024
6dbf86d
Cleaning up, using proper ranges and exporting.
jws-1 Feb 4, 2024
6965128
Fix var name.
jws-1 Feb 4, 2024
801e2c5
Fix data name for renderable.
jws-1 Feb 11, 2024
2f6ff60
Fix dodgy rebase.
jws-1 Feb 11, 2024
510c075
Not sure how this got through...?
jws-1 Feb 11, 2024
01dbc6f
WIP.
jws-1 Feb 11, 2024
c4a38c3
That was harder than it should've been!
jws-1 Feb 11, 2024
7aabf57
Add IntraAngle system test.
jws-1 Feb 11, 2024
8418ec4
Don't zero, bin, accumulate for every A-B-C.
jws-1 Feb 18, 2024
e44871e
Fixup keyword ranges.
jws-1 Feb 18, 2024
a65324c
Update test input.
jws-1 Feb 18, 2024
4bbb324
Output CN.
jws-1 Feb 18, 2024
cd70228
Export.
jws-1 Feb 18, 2024
eaa2c31
Cleanup includes.
jws-1 Feb 18, 2024
aabaeef
Cleaning up (1)
jws-1 Feb 18, 2024
0dd2edf
Cleaning up (2)
jws-1 Feb 18, 2024
e58725b
Cleaning up (3)
jws-1 Feb 18, 2024
dde8be5
DataNormaliserBase.
jws-1 Feb 18, 2024
a8baef9
Implement common methods in the base class.
jws-1 Feb 18, 2024
3ba0917
Warnings for non-implemented overrides.
jws-1 Feb 18, 2024
e06f598
Initial setup for Angle.
jws-1 Feb 11, 2024
fd1c642
DataNormaliser2D.
jws-1 Feb 11, 2024
7cb5e19
WIP: angle.
jws-1 Feb 11, 2024
5b48288
Fix constructor.
jws-1 Feb 11, 2024
9eaa5a2
Fix includes.
jws-1 Feb 11, 2024
03e8f8b
processingModuleData.
jws-1 Feb 11, 2024
d1054a3
fix DataNormaliser2D.
jws-1 Feb 11, 2024
4469245
Cleanup
jws-1 Feb 11, 2024
4896782
Drop procedure-stuff from dAngle.
jws-1 Feb 11, 2024
b2590d4
Fix constructor.
jws-1 Feb 11, 2024
bf7be64
WIP: dAngle.
jws-1 Feb 11, 2024
454d4b6
Still WIP - something strange going on with RDF....?
jws-1 Feb 12, 2024
62e2d2d
Correctly perform normalisation, by keeping track of selection statis…
jws-1 Feb 18, 2024
4a19cdb
Optimisations and properly normalise.
jws-1 Feb 18, 2024
1ec66df
Update GUI.
jws-1 Feb 18, 2024
b8220a6
WIP AxisAngle.
jws-1 Feb 18, 2024
cdca5bb
Refactor AxisAngle completely.
jws-1 Feb 18, 2024
3d854ba
Make DataNormaliser2D inherit from DataNormaliserBase.
jws-1 Feb 18, 2024
0590f46
Warning for non-implemented overrides.
jws-1 Feb 18, 2024
006b992
Drop override.
jws-1 Feb 18, 2024
51a4de4
WIP angle system test.
jws-1 Feb 18, 2024
63106c2
Merge branch 'develop' into dev-analysis-02
jws-1 Feb 19, 2024
2343eb8
Fixup merge.
jws-1 Feb 19, 2024
504f471
Fixup merge, again.
jws-1 Feb 19, 2024
89e9d56
Update DataNormaliser2D and usage.
jws-1 Feb 25, 2024
066fd32
Pass x/y/z deltas to custom normalisation functions, update usage.
jws-1 Feb 25, 2024
7a5338c
Capture this.
jws-1 Feb 25, 2024
0de23c0
Fix includes.
jws-1 Feb 25, 2024
6124a0b
Fix intraDistance GUI.
jws-1 Feb 25, 2024
523d7a3
Update CMake.
jws-1 Feb 25, 2024
839f29f
Update input file for Angle system test.
jws-1 Feb 25, 2024
2828cbd
Update system test to check RDF(B-C).
jws-1 Feb 25, 2024
2829051
Remove unnecessary include.
jws-1 Feb 25, 2024
501e384
Tidy and export for AxisAngle.
jws-1 Feb 25, 2024
f39949b
Tidy and export for DAngle.
jws-1 Feb 25, 2024
7440cb1
Tidy and export for Angle.
jws-1 Feb 25, 2024
020bb45
Update Angle system test input.
jws-1 Feb 26, 2024
5408127
Capitalisation.
jws-1 Feb 26, 2024
7d92714
Fix calculations.
jws-1 Feb 26, 2024
d29ea52
DataExporter.
jws-1 Feb 26, 2024
36d5e13
Use DataExporter.
jws-1 Feb 26, 2024
b96956c
Remove accidentally added test input file.
jws-1 Feb 26, 2024
f562e77
Use DataNormaliser1D, and correct export.
jws-1 Feb 26, 2024
46c1701
Clang format.
jws-1 Feb 26, 2024
56b16b7
Clang format, again.
jws-1 Feb 26, 2024
3a1acf7
System test for AxisAngle.
jws-1 Mar 3, 2024
cfb5cc6
Cleanup xyz.
jws-1 Mar 3, 2024
8727fb3
Apply suggestions from code review
jws-1 Mar 11, 2024
5f7cc67
Make implementation consistent.
jws-1 Mar 11, 2024
c77ba55
Prefix increment.
jws-1 Mar 11, 2024
5ee95ba
Declare variables.
jws-1 Mar 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions 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
Expand Down
31 changes: 31 additions & 0 deletions 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 <typename DataND, typename DataNDExportFileFormat> 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)
jws-1 marked this conversation as resolved.
Show resolved Hide resolved
{
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;
}
};
8 changes: 4 additions & 4 deletions src/analyser/dataNormaliser1D.cpp
Expand Up @@ -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"

Expand All @@ -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."); }
Expand Down Expand Up @@ -57,4 +57,4 @@ void DataNormaliser1D::normaliseTo(double value, bool absolute)
auto sum = absolute ? Integrator::absSum(targetData_) : Integrator::sum(targetData_);
targetData_ /= sum;
targetData_ *= value;
}
}
3 changes: 1 addition & 2 deletions src/analyser/dataNormaliser1D.h
Expand Up @@ -5,9 +5,8 @@

#include "analyser/dataNormaliserBase.h"
#include "math/data1D.h"
#include <string_view>

using NormalisationFunction1D = std::function<double(const double &, const double &)>;
using NormalisationFunction1D = std::function<double(const double &, const double &, const double &)>;
class DataNormaliser1D : public DataNormaliserBase<Data1D, NormalisationFunction1D>
{
public:
Expand Down
72 changes: 72 additions & 0 deletions 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<Data2D, NormalisationFunction2D>(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;
}
24 changes: 24 additions & 0 deletions 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<double(const double &, const double &, const double &, const double &, const double &)>;
class DataNormaliser2D : public DataNormaliserBase<Data2D, NormalisationFunction2D>
{
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;
};
4 changes: 2 additions & 2 deletions src/analyser/dataNormaliser3D.h
Expand Up @@ -5,9 +5,9 @@

#include "analyser/dataNormaliserBase.h"
#include "math/data3D.h"
#include <string_view>

using NormalisationFunction3D = std::function<double(const double &, const double &, const double &, const double &)>;
using NormalisationFunction3D = std::function<double(const double &, const double &, const double &, const double &,
const double &, const double &, const double &)>;
class DataNormaliser3D : public DataNormaliserBase<Data3D, NormalisationFunction3D>
{
public:
Expand Down
139 changes: 9 additions & 130 deletions src/modules/angle/angle.cpp
Expand Up @@ -8,140 +8,19 @@
#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<SelectProcedureNode>("A");
auto &forEachA = selectA_->branch()->get();

// -- Select: Site 'B'
selectB_ = forEachA.create<SelectProcedureNode>("B");
auto &forEachB = selectB_->branch()->get();

// -- -- Calculate: 'rAB'
auto calcAB = forEachB.create<CalculateDistanceProcedureNode>("rAB", selectA_, selectB_);

// -- -- Collect1D: 'RDF(AB)'
collectAB_ =
forEachB.create<Collect1DProcedureNode>({}, calcAB, ProcedureNode::AnalysisContext, rangeAB_.x, rangeAB_.y, rangeAB_.z);

// -- -- Select: Site 'C'
selectC_ = forEachB.create<SelectProcedureNode>("C");
auto &forEachC = selectC_->branch()->get();

// -- -- -- Calculate: 'rBC'
auto calcBC = forEachC.create<CalculateDistanceProcedureNode>({}, selectB_, selectC_);

// -- -- -- Calculate: 'aABC'
calculateAngle_ = forEachC.create<CalculateAngleProcedureNode>({}, selectA_, selectB_, selectC_);
calculateAngle_->keywords().set("Symmetric", symmetric_);

// -- -- -- Collect1D: 'RDF(BC)'
collectBC_ =
forEachC.create<Collect1DProcedureNode>({}, calcBC, ProcedureNode::AnalysisContext, rangeBC_.x, rangeBC_.y, rangeBC_.z);

// -- -- -- Collect1D: 'ANGLE(ABC)'
collectABC_ = forEachC.create<Collect1DProcedureNode>({}, calculateAngle_, ProcedureNode::AnalysisContext, angleRange_.x,
angleRange_.y, angleRange_.z);

// -- -- -- Collect2D: 'DAngle (A-B)-C'
collectDAngleAB_ =
forEachC.create<Collect2DProcedureNode>({}, calcAB, calculateAngle_, ProcedureNode::AnalysisContext, rangeAB_.x,
rangeAB_.y, rangeAB_.z, angleRange_.x, angleRange_.y, angleRange_.z);

// -- -- -- Collect2D: 'DAngle A-(B-C)'
collectDAngleBC_ =
forEachC.create<Collect2DProcedureNode>({}, 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<Collect3DProcedureNode>({}, calcAB, calcBC, calculateAngle_, ProcedureNode::AnalysisContext,
rangeAB_, rangeBC_, angleRange_);

// Process1D: 'RDF(AB)'
processAB_ = analyser_.createRootNode<Process1DProcedureNode>("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<OperateSitePopulationNormaliseProcedureNode>({},
ConstNodeVector<SelectProcedureNode>({selectA_}));
rdfABNormalisation.create<OperateNumberDensityNormaliseProcedureNode>({}, ConstNodeVector<SelectProcedureNode>({selectB_}));
rdfABNormalisation.create<OperateSphericalShellNormaliseProcedureNode>({});

// Process1D: 'RDF(BC)'
processBC_ = analyser_.createRootNode<Process1DProcedureNode>("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<OperateSitePopulationNormaliseProcedureNode>(
{}, ConstNodeVector<SelectProcedureNode>({selectB_, selectA_}));
rdfBCNormalisation.create<OperateNumberDensityNormaliseProcedureNode>({}, ConstNodeVector<SelectProcedureNode>({selectC_}));
rdfBCNormalisation.create<OperateSphericalShellNormaliseProcedureNode>({});

// Process1D: 'ANGLE(ABC)'
processAngle_ = analyser_.createRootNode<Process1DProcedureNode>("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<OperateExpressionProcedureNode>({}, "value/sin(toRad(x))");
angleNormalisation.create<OperateNormaliseProcedureNode>({}, 1.0);

// Process2D: 'DAngle (A-B)-C'
processDAngleAB_ = analyser_.createRootNode<Process2DProcedureNode>("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<OperateExpressionProcedureNode>(
{}, fmt::format("{} * value/sin(toRad(y))/sin(toRad(yDelta))", symmetric_ ? 1.0 : 2.0));
dAngleABNormalisation.create<OperateSitePopulationNormaliseProcedureNode>(
{}, ConstNodeVector<SelectProcedureNode>({selectA_, selectC_}));
dAngleABNormalisation.create<OperateNumberDensityNormaliseProcedureNode>({},
ConstNodeVector<SelectProcedureNode>({selectB_}));
dAngleABNormalisation.create<OperateSphericalShellNormaliseProcedureNode>({});

// Process2D: 'DAngle A-(B-C)'
processDAngleBC_ = analyser_.createRootNode<Process2DProcedureNode>("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<OperateExpressionProcedureNode>(
{}, fmt::format("{} * value/sin(toRad(y))/sin(toRad(yDelta))", symmetric_ ? 1.0 : 2.0));
dAngleBCNormalisation.create<OperateSitePopulationNormaliseProcedureNode>(
{}, ConstNodeVector<SelectProcedureNode>({selectB_, selectA_}));
dAngleBCNormalisation.create<OperateNumberDensityNormaliseProcedureNode>({},
ConstNodeVector<SelectProcedureNode>({selectC_}));
dAngleBCNormalisation.create<OperateSphericalShellNormaliseProcedureNode>({});

/*
* Keywords
*/

keywords_.addTarget<ConfigurationKeyword>("Configuration", "Set target configuration for the module", targetConfiguration_);

keywords_.setOrganisation("Options", "Sites", "Specify sites defining the angle interaction A-B-C.");
keywords_.add<SpeciesSiteVectorKeyword>("SiteA", "Specify site(s) which represent 'A' in the interaction A-B-C",
selectA_->speciesSites(), selectA_->axesRequired());
keywords_.add<SpeciesSiteVectorKeyword>("SiteB", "Specify site(s) which represent 'B' in the interaction A-B-C",
selectB_->speciesSites(), selectB_->axesRequired());
keywords_.add<SpeciesSiteVectorKeyword>("SiteC", "Specify site(s) which represent 'C' in the interaction A-B-C",
selectC_->speciesSites(), selectC_->axesRequired());
keywords_.add<SpeciesSiteVectorKeyword>("SiteA", "Specify site(s) which represent 'A' in the interaction A-B-C", a_);
keywords_.add<SpeciesSiteVectorKeyword>("SiteB", "Specify site(s) which represent 'B' in the interaction A-B-C", b_);
keywords_.add<SpeciesSiteVectorKeyword>("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<Vec3DoubleKeyword>("RangeAB", "Range (min, max, binwidth) of A-B distance binning", rangeAB_,
Expand All @@ -167,16 +46,16 @@ AngleModule::AngleModule() : Module(ModuleTypes::Angle), analyser_(ProcedureNode

keywords_.setOrganisation("Export");
keywords_.add<FileAndFormatKeyword>("ExportAB", "File format and file name under which to save calculated A-B RDF data",
processAB_->exportFileAndFormat(), "EndExportAB");
exportFileAndFormatAB_, "EndExportAB");
keywords_.add<FileAndFormatKeyword>("ExportBC", "File format and file name under which to save calculated B-C RDF data",
processBC_->exportFileAndFormat(), "EndExportBC");
exportFileAndFormatBC_, "EndExportBC");
keywords_.add<FileAndFormatKeyword>("ExportAngle",
"File format and file name under which to save calculated A-B-C angle histogram",
processAngle_->exportFileAndFormat(), "EndExportAngle");
exportFileAndFormatAngle_, "EndExportAngle");
keywords_.add<FileAndFormatKeyword>("ExportDAngleAB",
"File format and file name under which to save calculated (A-B)-C distance-angle map",
processDAngleAB_->exportFileAndFormat(), "EndExportDAngleAB");
exportFileAndFormatDAngleAB_, "EndExportDAngleAB");
keywords_.add<FileAndFormatKeyword>("ExportDAngleBC",
"File format and file name under which to save calculated A-(B-C) distance-angle map",
processDAngleBC_->exportFileAndFormat(), "EndExportDAngleBC");
exportFileAndFormatDAngleBC_, "EndExportDAngleBC");
}