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

feat: addition of potentialSet class #1991

Open
wants to merge 18 commits into
base: develop
Choose a base branch
from
Open
2 changes: 2 additions & 0 deletions src/classes/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ add_library(
pairPotential.cpp
pairPotentialOverride.cpp
potentialMap.cpp
potentialSet.cpp
pairIterator.cpp
partialSet.cpp
partialSetAccumulator.cpp
Expand Down Expand Up @@ -109,6 +110,7 @@ add_library(
potentialMap.h
partialSet.h
partialSetAccumulator.h
potentialSet.h
region.h
regionalDistributor.h
scatteringMatrix.h
Expand Down
104 changes: 104 additions & 0 deletions src/classes/potentialSet.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2024 Team Dissolve and contributors

#include "classes/potentialSet.h"
#include "base/lineParser.h"
#include "classes/atomType.h"
#include "classes/box.h"
#include "classes/configuration.h"
#include "io/export/data1D.h"
#include "items/deserialisers.h"
#include "items/serialisers.h"
#include "templates/algorithms.h"

PotentialSet::PotentialSet() { fingerprint_ = "NO_FINGERPRINT"; }

PotentialSet::~PotentialSet() { potentials_.clear(); }

// Reset partial arrays
void PotentialSet::reset()
{
potentials_.clear();
fingerprint_ = "NO_FINGERPRINT";
}

// Set new fingerprint
void PotentialSet::setFingerprint(std::string_view fingerprint) { fingerprint_ = fingerprint; }

// Return full map of potentials specified
std::map<std::string, PotentialSet::EPData> &PotentialSet::potentialMap() { return potentials_; }
const std::map<std::string, PotentialSet::EPData> &PotentialSet::potentialMap() const { return potentials_; }

/*
* Operators
*/

PotentialSet &PotentialSet::operator+=(const double delta)
{
for (auto &[key, potential] : potentials_)
potential.ep += delta;
return (*this);
}

PotentialSet &PotentialSet::operator+=(const PotentialSet &source)
{
for (auto &[key, potential] : source.potentialMap())
potentials_[key].ep += potential.ep;
return (*this);
}

PotentialSet &PotentialSet::operator*=(const double factor)
{
for (auto &[key, potential] : potentials_)
potential.ep *= factor;
return (*this);
}

/*
* Serialisation
*/

// Read data through specified LineParser
bool PotentialSet::deserialise(LineParser &parser, const CoreData &coreData)
{
if (parser.readNextLine(LineParser::Defaults, fingerprint_) != LineParser::Success)
return false;

Danbr4d marked this conversation as resolved.
Show resolved Hide resolved
if (parser.getArgsDelim(LineParser::Defaults) != LineParser::Success)
return false;
auto size = parser.argli(0);
for (auto n = 0; n < size; ++n)
{
if (parser.getArgsDelim(LineParser::Defaults) != LineParser::Success)
return false;
EPData value;
auto key = parser.args(0);
value.count = parser.argi(1);
value.at1 = coreData.findAtomType(parser.args(2));
value.at2 = coreData.findAtomType(parser.args(3));

if (!value.ep.deserialise(parser))
return false;

potentials_[key] = value;
}

return true;
}

// Write data through specified LineParser
bool PotentialSet::serialise(LineParser &parser) const
{
if (!parser.writeLineF("{}\n", fingerprint_))
return false;
if (!parser.writeLineF("{}\n", potentials_.size()))
return false;
for (auto &[key, value] : potentials_)
{
if (!parser.writeLineF("{} {} {} {}\n", key, value.count, value.at1->name(), value.at2->name()))
return false;
if (!value.ep.serialise(parser))
return false;
}
return true;
}
67 changes: 67 additions & 0 deletions src/classes/potentialSet.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2024 Team Dissolve and contributors

#pragma once

#include "classes/atomTypeMix.h"
#include "classes/neutronWeights.h"
#include "math/data1D.h"
#include "math/histogram1D.h"
#include "modules/epsr/epsr.h"
#include "modules/epsrManager/epsrManager.h"
#include "templates/array2D.h"

// Forward Declarations
class Configuration;
class Interpolator;

// Set of Partials
class PotentialSet
{
public:
PotentialSet();
~PotentialSet();

/*
* Potentials Data
*/
private:
// Fingerprint for these partials (e.g. reflecting Configuration indices at which they were calculated)
std::string fingerprint_;
struct EPData
{
Data1D ep;
double count{0};
std::shared_ptr<AtomType> at1, at2;
};
// Pair matrix, containing full atom-atom partial
std::map<std::string, EPData> potentials_;

public:
// Reset partial arrays
void reset();
// Set new fingerprint
void setFingerprint(std::string_view fingerprint);
// Return fingerprint of partials
std::string_view fingerprint() const;
// Return full atom-atom partial specified
std::map<std::string, EPData> &potentialMap();
const std::map<std::string, EPData> &potentialMap() const;

/*
* Operators
*/
public:
PotentialSet &operator+=(const double delta);
PotentialSet &operator+=(const PotentialSet &source);
PotentialSet &operator*=(const double factor);

/*
* Serialisation
*/
public:
// Read data through specified LineParser
bool deserialise(LineParser &parser, const CoreData &coreData);
// Write data through specified LineParser
bool serialise(LineParser &parser) const;
};
2 changes: 2 additions & 0 deletions src/items/deserialisers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "classes/neutronWeights.h"
#include "classes/partialSet.h"
#include "classes/partialSetAccumulator.h"
#include "classes/potentialSet.h"
#include "classes/xRayWeights.h"
#include "items/legacy.h"
#include "math/data1D.h"
Expand Down Expand Up @@ -168,6 +169,7 @@ GenericItemDeserialiser::GenericItemDeserialiser()
registerDeserialiser<NeutronWeights>(simpleDeserialiseCore<NeutronWeights>);
registerDeserialiser<PartialSet>(simpleDeserialiseCore<PartialSet>);
registerDeserialiser<PartialSetAccumulator>(simpleDeserialise<PartialSetAccumulator>);
registerDeserialiser<PotentialSet>(simpleDeserialiseCore<PotentialSet>);
registerDeserialiser<SampledData1D>(simpleDeserialise<SampledData1D>);
registerDeserialiser<SampledDouble>(simpleDeserialise<SampledDouble>);
registerDeserialiser<SampledVector>(simpleDeserialise<SampledVector>);
Expand Down
2 changes: 2 additions & 0 deletions src/items/producers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "classes/neutronWeights.h"
#include "classes/partialSet.h"
#include "classes/partialSetAccumulator.h"
#include "classes/potentialSet.h"
#include "classes/xRayWeights.h"
#include "items/legacy.h"
#include "math/data1D.h"
Expand Down Expand Up @@ -46,6 +47,7 @@ GenericItemProducer::GenericItemProducer()
registerProducer<NeutronWeights>("NeutronWeights");
registerProducer<PartialSet>("PartialSet");
registerProducer<PartialSetAccumulator>("PartialSetAccumulator");
registerProducer<PotentialSet>("PotentialSet");
registerProducer<SampledData1D>("SampledData1D");
registerProducer<SampledDouble>("SampledDouble");
registerProducer<SampledVector>("SampledVector");
Expand Down
2 changes: 2 additions & 0 deletions src/items/serialisers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "classes/neutronWeights.h"
#include "classes/partialSet.h"
#include "classes/partialSetAccumulator.h"
#include "classes/potentialSet.h"
#include "classes/xRayWeights.h"
#include "math/data1D.h"
#include "math/data2D.h"
Expand Down Expand Up @@ -111,6 +112,7 @@ GenericItemSerialiser::GenericItemSerialiser()
registerSerialiser<NeutronWeights>(simpleSerialise<NeutronWeights>);
registerSerialiser<PartialSet>(simpleSerialise<PartialSet>);
registerSerialiser<PartialSetAccumulator>(simpleSerialise<PartialSetAccumulator>);
registerSerialiser<PotentialSet>(simpleSerialise<PotentialSet>);
registerSerialiser<SampledData1D>(simpleSerialise<SampledData1D>);
registerSerialiser<SampledDouble>(simpleSerialise<SampledDouble>);
registerSerialiser<SampledVector>(simpleSerialise<SampledVector>);
Expand Down
6 changes: 6 additions & 0 deletions src/modules/epsrManager/epsrManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,10 @@ EPSRManagerModule::EPSRManagerModule() : Module(ModuleTypes::EPSRManager)
keywords_.setOrganisation("Options", "Potential Scaling");
keywords_.add<StringKeyword>("PotScale", "Comma-separated list of atom type pairs and scaling factors in the form A-B=1.0",
potentialScalings_);
keywords_.setOrganisation("Options", "Averaging");
keywords_.add<OptionalIntegerKeyword>("Averaging", "Number of historical potential sets to combine into final potentials",
averagingLength_, 1, std::nullopt, 1, "Off");
keywords_.add<EnumOptionsKeyword<Averaging::AveragingScheme>>("AveragingScheme",
"Weighting scheme to use when averaging potentials",
averagingScheme_, Averaging::averagingSchemes());
}
7 changes: 7 additions & 0 deletions src/modules/epsrManager/epsrManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "classes/scatteringMatrix.h"
#include "generator/generator.h"
#include "math/averaging.h"
#include "module/groups.h"
#include "module/module.h"
#include <tuple>
Expand Down Expand Up @@ -35,6 +36,12 @@ class EPSRManagerModule : public Module
};
// Potential scalings
std::string potentialScalings_;
// Number of historical partial sets to combine into final partials
std::optional<int> averagingLength_;
// Weighting scheme to use when averaging partials
Averaging::AveragingScheme averagingScheme_{Averaging::LinearAveraging};
// Vector of averaged potentials over multiple iterations
std::vector<std::map<std::string, EPData>> averagedPotentialsStore;

/*
* Functions
Expand Down
20 changes: 20 additions & 0 deletions src/modules/epsrManager/process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,26 @@ Module::ExecutionResult EPSRManagerModule::process(ModuleContext &moduleContext)
for (auto &&[key, epData] : potentials)
epData.ep /= epData.count;

std::map<std::string, EPData> averagedPotentials = potentials;

averagedPotentialsStore.emplace_back(potentials);
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
averagedPotentialsStore.emplace_back(potentials);

// Check if ran the right amount of iterations before averaging
if (averagedPotentialsStore.size() > averagingLength_)
{
averagedPotentialsStore.pop_back();
}
Comment on lines +59 to +63
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// Check if ran the right amount of iterations before averaging
if (averagedPotentialsStore.size() > averagingLength_)
{
averagedPotentialsStore.pop_back();
}
// Limit data to average to the most recent N-1 sets
while (averagedPotentialsStore.size() >= averagingLength_)
{
averagedPotentialsStore.pop_front();
}


// Average the potentials and replace the map with the new averaged
for (const auto &pots : averagedPotentialsStore)
{
for (auto &&[key, epData] : pots)
{
averagedPotentials[key].ep += epData.ep;
averagedPotentials[key].ep /= averagingLength_.value();
Copy link
Member

Choose a reason for hiding this comment

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

You definitely don't want to do this division every time you add a set of potentials, just once at the end!

}
}
potentials = averagedPotentials;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
potentials = averagedPotentials;
// Add the current potentials (before averaging) to the average store before we overwrite them
averagePotentialsStore.emplace_back(potentials);
// Set new averaged potentials
potentials = averagedPotentials;


Comment on lines +56 to +75
Copy link
Member

Choose a reason for hiding this comment

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

Could this section be the issue? The logic here doesn't quite add up, since you store the current potentials as the beginning of averagePotentials, then add potentials to the store, then immediately remove it if you have exceeded your averaging length (which past a certain point you always will) and then do the sum over all the store potentials. That will give you N+1 sets of potentials contributing to the average, rather than the N you want, and which will always contain the first N potential sets you ever generate at the beginning of the simulation (since you're doing pop_back rather than pop_front. The Averaging class would sort all this out for you, but I have suggested the necessary modifications to clean this up anyway.

// Apply potential scalings
auto scalings = DissolveSys::splitString(potentialScalings_, ",");
for (const auto &scaling : scalings)
Expand Down
1 change: 1 addition & 0 deletions tests/classes/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ dissolve_add_test(SRC flags.cpp)
dissolve_add_test(SRC genericList.cpp)
dissolve_add_test(SRC interactionPotential.cpp)
dissolve_add_test(SRC neutronWeights.cpp)
dissolve_add_test(SRC potentialSet.cpp)
dissolve_add_test(SRC spaceGroups.cpp)
dissolve_add_test(SRC species.cpp)
dissolve_add_test(SRC speciesSite.cpp)
Expand Down
66 changes: 66 additions & 0 deletions tests/classes/potentialSet.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2024 Team Dissolve and contributors

#include "classes/potentialSet.h"
#include "math/data1D.h"
#include "tests/testData.h"
#include <gtest/gtest.h>

namespace UnitTest
{
TEST(PotentialSetTest, SimpleAddition)
{
PotentialSet pots;
Data1D x;
const auto value = 2.0;
x.addPoint(1, value);
pots.potentialMap()["A-A"].ep = x;
pots.potentialMap()["A-B"].ep = x;
pots.potentialMap()["A-C"].ep = x;

pots += pots;
EXPECT_EQ(4.0, pots.potentialMap()["A-A"].ep.value(0));
EXPECT_EQ(4.0, pots.potentialMap()["A-B"].ep.value(0));
EXPECT_EQ(4.0, pots.potentialMap()["A-C"].ep.value(0));
}

TEST(PotentialSetTest, Multiplication)
{
PotentialSet pots;
Data1D x;
const auto value = 3.0;
x.addPoint(1, value);
pots.potentialMap()["A-A"].ep = x;
pots.potentialMap()["A-B"].ep = x;
pots.potentialMap()["A-C"].ep = x;

pots *= 2;
EXPECT_EQ(6.0, pots.potentialMap()["A-A"].ep.value(0));
EXPECT_EQ(6.0, pots.potentialMap()["A-B"].ep.value(0));
EXPECT_EQ(6.0, pots.potentialMap()["A-C"].ep.value(0));
}

TEST(PotentialSetTest, ComplexAddition)
{
PotentialSet pots;
PotentialSet pots2;
Data1D x;
const auto value = 2.0;
x.addPoint(1, value);
pots.potentialMap()["A-A"].ep = x;
pots.potentialMap()["A-B"].ep = x;
pots.potentialMap()["A-C"].ep = x;

pots2.potentialMap()["A-A"].ep = x;
pots2.potentialMap()["A-B"].ep = x;
pots2.potentialMap()["A-C"].ep = x;
pots2.potentialMap()["A-D"].ep = x;

pots += pots2;
EXPECT_EQ(4.0, pots.potentialMap()["A-A"].ep.value(0));
EXPECT_EQ(4.0, pots.potentialMap()["A-B"].ep.value(0));
EXPECT_EQ(4.0, pots.potentialMap()["A-C"].ep.value(0));
EXPECT_EQ(2.0, pots.potentialMap()["A-D"].ep.value(0));
}

} // namespace UnitTest