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: drivenMD setup and testing #1971

Draft
wants to merge 13 commits into
base: develop
Choose a base branch
from
1 change: 1 addition & 0 deletions src/module/module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ EnumOptions<ModuleTypes::ModuleType> moduleTypes_("ModuleType", {{ModuleTypes::A
{ModuleTypes::Compare, "Compare"},
{ModuleTypes::DAngle, "DAngle"},
{ModuleTypes::DataTest, "DataTest"},
{ModuleTypes::DrivenMD, "DrivenMD"},
{ModuleTypes::Energy, "Energy"},
{ModuleTypes::EPSR, "EPSR"},
{ModuleTypes::EPSRManager, "EPSRManager"},
Expand Down
1 change: 1 addition & 0 deletions src/module/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ enum ModuleType
Compare,
DAngle,
DataTest,
DrivenMD,
Energy,
EPSR,
EPSRManager,
Expand Down
1 change: 1 addition & 0 deletions src/modules/drivenMD/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dissolve_add_module(drivenMD.h drivenMD)
18 changes: 18 additions & 0 deletions src/modules/drivenMD/drivenMD.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2024 Team Dissolve and contributors

#include "modules/drivenMD/drivenMD.h"
#include "keywords/configuration.h"
#include "keywords/fileAndFormat.h"
#include "keywords/moduleVector.h"
#include "keywords/speciesSiteVector.h"
#include "keywords/weightedModuleVector.h"

DrivenMDModule::DrivenMDModule() : Module(ModuleTypes::DrivenMD)
{
keywords_.addTarget<ConfigurationKeyword>("Configuration", "Set target configuration for the module", targetConfiguration_)
->setEditSignals({KeywordBase::ClearModuleData, KeywordBase::RecreateRenderables});
keywords_.addTarget<ModuleVectorKeyword>("Target", "Add specified Module (and it's Reference data) as a refinement target",
targets_,
std::vector<ModuleTypes::ModuleType>{ModuleTypes::NeutronSQ, ModuleTypes::XRaySQ});
}
52 changes: 52 additions & 0 deletions src/modules/drivenMD/drivenMD.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2024 Team Dissolve and contributors

#pragma once

#include "base/enumOptions.h"
#include "classes/scatteringMatrix.h"
#include "math/data1D.h"
#include "math/range.h"
#include "module/groups.h"
#include "module/module.h"
#include "templates/array3D.h"
#include <tuple>

// DrivenMD Module
class DrivenMDModule : public Module
{
public:
DrivenMDModule();
~DrivenMDModule() override = default;

/*
* Definition
*/
public:
private:
// Vector storing atom pairs and associated potentials
std::vector<std::tuple<std::shared_ptr<AtomType>, std::shared_ptr<AtomType>, Data1D>> empiricalPotentials_;
// Target Modules containing data to refine against
std::vector<Module *> targets_;

public:
// Return list of target Modules / data for refinement
const std::vector<Module *> &targets() const;
// Set whether to apply this module's generated potentials to the global pair potentials
void setApplyPotentials(bool b);

/*
* Functions
*/
private:
// Target Configuration (determined from target modules)
Configuration *targetConfiguration_{nullptr};
// Calculate partial g(r) in serial with simple double-loop
void calculateGRTestSerial(Configuration *cfg, PartialSet &partialSet);
/*
* Processing
*/
private:
// Run main processing
Module::ExecutionResult process(ModuleContext &moduleContext) override;
};
28 changes: 28 additions & 0 deletions src/modules/drivenMD/functions.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2024 Team Dissolve and contributors

#include "main/dissolve.h"
#include "module/context.h"
#include "modules/drivenMD/drivenMD.h"
#include "templates/algorithms.h"

// Calculate partial g(r) in serial with simple double-loop
void DrivenMDModule::calculateGRTestSerial(Configuration *cfg, PartialSet &partialSet)
{
// Calculate radial distribution functions with a simple double loop, in serial then return the partial set!!!!!!!
const auto *box = cfg->box();

dissolve::for_each_pair(
ParallelPolicies::seq, cfg->atoms().begin(), cfg->atoms().end(),
[box, &partialSet](auto i, auto &ii, auto j, auto &jj)
{
if (&ii != &jj)
partialSet.fullHistogram(ii.localTypeIndex(), jj.localTypeIndex()).bin(box->minimumDistance(ii.r(), jj.r()));
});

// Transform histogram data into radial distribution functions
partialSet.formPartials(box->volume());

// Sum total functions
partialSet.formTotals(true);
}
1 change: 1 addition & 0 deletions src/modules/drivenMD/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dissolve_add_module_gui(drivenMD)
15 changes: 15 additions & 0 deletions src/modules/drivenMD/gui/drivenMDFuncs.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2024 Team Dissolve and contributors

#include "main/dissolve.h"
#include "modules/drivenMD/drivenMD.h"
#include "modules/drivenMD/gui/drivenMDWidget.h"

DrivenMDModuleWidget::DrivenMDModuleWidget(QWidget *parent, DrivenMDModule *module, Dissolve &dissolve)
: ModuleWidget(parent, dissolve), module_(module)
{
// Set up user interface
ui_.setupUi(this);

refreshing_ = false;
}
34 changes: 34 additions & 0 deletions src/modules/drivenMD/gui/drivenMDWidget.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2024 Team Dissolve and contributors

#pragma once

#include "modules/drivenMD/gui/ui_drivenMDWidget.h"
#include "modules/widget.h"

// Forward Declarations
class DrivenMDModule;
class Dissolve;
class Module;

// Module Widget
class DrivenMDModuleWidget : public ModuleWidget
{
// All Qt declarations derived from QObject must include this macro
Q_OBJECT

public:
DrivenMDModuleWidget(QWidget *parent, DrivenMDModule *module, Dissolve &dissolve);
~DrivenMDModuleWidget() override = default;

private:
// Associated Module
DrivenMDModule *module_;

/*
* UI
*/
private:
// Main form declaration
Ui::DrivenMDModuleWidget ui_;
};
47 changes: 47 additions & 0 deletions src/modules/drivenMD/gui/drivenMDWidget.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DrivenMDModuleWidget</class>
<widget class="QWidget" name="DrivenMDModuleWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>473</width>
<height>395</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="windowTitle">
<string>AnalyseModule Controls</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>4</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
<resources/>
<connections/>
</ui>
159 changes: 159 additions & 0 deletions src/modules/drivenMD/process.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2024 Team Dissolve and contributors

#include "base/sysFunc.h"
#include "keywords/module.h"
#include "main/dissolve.h"
#include "math/error.h"
#include "math/ft.h"
#include "module/context.h"
#include "module/group.h"
#include "modules/drivenMD/drivenMD.h"
#include "modules/gr/gr.h"
#include <functional>

// Run main processing
Module::ExecutionResult DrivenMDModule::process(ModuleContext &moduleContext)
{
auto &processingData = moduleContext.dissolve().processingModuleData();

/*
* 1) Loop over a site
* 2) Move in the x, y and z direction
* 3) Calculate gr at each step
* 4) FT gr-1 to F(Q) and weight by scattering length
* 5) Take the negative gradient of the difference between the gr and the F(Q) which corresponds to the derived force
* that we then apply to MD simulation
*/

std::vector<Vec3<double>> forces;

// Does a PartialSet already exist for this Configuration?
auto originalGRObject = processingData.realiseIf<PartialSet>(
fmt::format("{}//OriginalGR", targetConfiguration_->niceName()), name_, GenericItem::InRestartFileFlag);
auto &originalgr = originalGRObject.first;
auto rdfRange = targetConfiguration_->box()->inscribedSphereRadius();
auto binWidth = 0.025;
rdfRange = int(rdfRange / binWidth) * binWidth;
if (originalGRObject.second == GenericItem::ItemStatus::Created)
originalgr.setUp(targetConfiguration_->atomTypes(), rdfRange, binWidth);

originalgr.setUpHistograms(rdfRange, binWidth);
originalgr.reset();

double delta{2};
auto atoms = targetConfiguration_->atoms();
forces.reserve(atoms.size());

for (auto *module : targets_)
{
// Retrieve the ReferenceData
if (!processingData.contains("ReferenceData", module->name()))
{
Messenger::error("Reference data not found for target '{}'.\n", module->name());
return ExecutionResult::Failed;
}
const auto &originalReferenceData = processingData.value<Data1D>("ReferenceData", module->name());

for (auto &i : atoms)
{
fmt::print("X coord = {}, Y coord = {}, Z coord = {}\n", i.x(), i.y(), i.z());
auto &f = forces.emplace_back(Vec3<double>(0.0, 0.0, 0.0));
fmt::print("Atom {}\n", i.globalIndex());
for (auto n = 0; n < 3; ++n)
{
switch (n)
{
// Move x
case 0:
// Get position, change via delta then set
fmt::print("X coord = {}\n", i.x());
i.translateCoordinates(-delta, 0, 0);
fmt::print("X coord = {}\n", i.x());
// Reset GR
originalgr.reset();
// Calculate GR
calculateGRTestSerial(targetConfiguration_, originalgr);
// FT to structure factor
Fourier::sineFT(originalgr.total(), 1.0 / (2.0 * PI * PI * 1.39), 0.05, 0.05, 30.0,
WindowFunction::Form::Lorch0);
// Store the error in a vector
f.x = Error::rmse(originalgr.total(), originalReferenceData).error;
fmt::print("X+ = {}\n", Error::rmse(originalgr.total(), originalReferenceData).error);
// Set new position (needs 2x to get +x from original)
i.translateCoordinates(2 * delta, 0, 0);
fmt::print("X coord = {}\n", i.x());
originalgr.reset();
calculateGRTestSerial(targetConfiguration_, originalgr);
Fourier::sineFT(originalgr.total(), 1.0 / (2.0 * PI * PI * 1.39), 0.05, 0.05, 30.0,
WindowFunction::Form::Lorch0);
// Calculate new error
f.x -= Error::rmse(originalgr.total(), originalReferenceData).error;
fmt::print("X+ = {}\n", Error::rmse(originalgr.total(), originalReferenceData).error);
// Reset position
i.translateCoordinates(-delta, 0, 0);
fmt::print("X coord = {}\n", i.x());
break;
// Move y and repeat
case 1:
fmt::print("Y coord = {}\n", i.y());
i.translateCoordinates(0, -delta, 0);
fmt::print("Y coord = {}\n", i.y());
originalgr.reset();
calculateGRTestSerial(targetConfiguration_, originalgr);
Fourier::sineFT(originalgr.total(), 1.0 / (2.0 * PI * PI * 1.39), 0.05, 0.05, 30.0,
WindowFunction::Form::Lorch0);
f.y = Error::rmse(originalgr.total(), originalReferenceData).error;
fmt::print("Y+ = {}\n", Error::rmse(originalgr.total(), originalReferenceData).error);
i.translateCoordinates(0, 2 * delta, 0);
fmt::print("Y coord = {}\n", i.y());
originalgr.reset();
calculateGRTestSerial(targetConfiguration_, originalgr);
Fourier::sineFT(originalgr.total(), 1.0 / (2.0 * PI * PI * 1.39), 0.05, 0.05, 30.0,
WindowFunction::Form::Lorch0);
f.y -= Error::rmse(originalgr.total(), originalReferenceData).error;
fmt::print("Y+ = {}\n", Error::rmse(originalgr.total(), originalReferenceData).error);
i.translateCoordinates(0, -delta, 0);
fmt::print("Y coord = {}\n", i.y());
break;
// Move z
case 2:
fmt::print("Z coord = {}\n", i.z());
i.translateCoordinates(0, 0, -delta);
fmt::print("Z coord = {}\n", i.z());
originalgr.reset();
calculateGRTestSerial(targetConfiguration_, originalgr);
Fourier::sineFT(originalgr.total(), 1.0 / (2.0 * PI * PI * 1.39), 0.05, 0.05, 30.0,
WindowFunction::Form::Lorch0);
f.z = Error::rmse(originalgr.total(), originalReferenceData).error;
fmt::print("Z+ = {}\n", Error::rmse(originalgr.total(), originalReferenceData).error);
i.translateCoordinates(0, 0, 2 * delta);
fmt::print("Z coord = {}\n", i.z());
originalgr.reset();
calculateGRTestSerial(targetConfiguration_, originalgr);
Fourier::sineFT(originalgr.total(), 1.0 / (2.0 * PI * PI * 1.39), 0.05, 0.05, 30.0,
WindowFunction::Form::Lorch0);
f.z -= Error::rmse(originalgr.total(), originalReferenceData).error;
fmt::print("Z+ = {}\n", Error::rmse(originalgr.total(), originalReferenceData).error);
i.translateCoordinates(0, 0, -delta);
fmt::print("Z coord = {}\n", i.z());
break;
}
}
}
}

for (auto &&[i, f] : zip(atoms, forces))
{
fmt::print("X coord = {}, Y coord = {}, Z coord = {}\n", i.x(), i.y(), i.z());
i.translateCoordinates(f);
f.print();
printf("We've moved\n");
fmt::print("X coord = {}, Y coord = {}, Z coord = {}\n", i.x(), i.y(), i.z());
}

targetConfiguration_->incrementContentsVersion();
targetConfiguration_->updateObjectRelationships();

return ExecutionResult::Success;
}
2 changes: 2 additions & 0 deletions src/modules/registry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "modules/bragg/bragg.h"
#include "modules/compare/compare.h"
#include "modules/dAngle/dAngle.h"
#include "modules/drivenMD/drivenMD.h"
#include "modules/energy/energy.h"
#include "modules/epsr/epsr.h"
#include "modules/epsrManager/epsrManager.h"
Expand Down Expand Up @@ -53,6 +54,7 @@ ModuleRegistry::ModuleRegistry()
registerProducer<BraggModule>(ModuleTypes::Bragg, "Calculate Bragg intensities", "Correlation Functions");
registerProducer<CompareModule>(ModuleTypes::Compare, "Compare data sets and calculate errors", "Checks & Tests");
registerProducer<DAngleModule>(ModuleTypes::DAngle, "Calculate distance/angle maps", "Analysis");
registerProducer<DrivenMDModule>(ModuleTypes::DrivenMD, "SAXS/SANS driven molecular dynamics test", "Forcefield");
registerProducer<EnergyModule>(ModuleTypes::Energy, "Calculate the total energy of a Configuration", "Forcefield");
registerProducer<EPSRModule>(ModuleTypes::EPSR, "Refine interatomic potentials in a manner consistent with EPSR",
"Forcefield");
Expand Down
Loading