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: (#1890) Support extended trajectory export #1899

Merged
merged 4 commits into from
May 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ tests/epsr/H2O-weighted-total.gr.broad
tests/epsr/H2OX-weighted-total.gr.broad
tests/epsr/HDO-ReferenceData.q
tests/epsr/HDO-ReferenceData.r
tests/data/dissolve/input/TestOutput_*

# Visual Studio
.vscode/
Expand Down
27 changes: 20 additions & 7 deletions src/io/export/trajectory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,16 @@ TrajectoryExportFileFormat::TrajectoryExportFileFormat(std::string_view filename
: FileAndFormat(formats_, filename, (int)format)
{
formats_ = EnumOptions<TrajectoryExportFileFormat::TrajectoryExportFormat>(
"TrajectoryExportFileFormat", {{TrajectoryExportFormat::XYZ, "xyz", "XYZ Trajectory"}});
"TrajectoryExportFileFormat", {{TrajectoryExportFormat::XYZ, "xyz", "XYZ Trajectory"},
{TrajectoryExportFormat::XYZExtended, "xyzExt", "XYZ Trajectory Extended"}});
}

/*
* Export Functions
*/

// Append XYZ frame to trajectory
bool TrajectoryExportFileFormat::exportXYZ(LineParser &parser, Configuration *cfg)
bool TrajectoryExportFileFormat::exportXYZ(LineParser &parser, Configuration *cfg, bool extended)
{
// Write number of atoms and title
if (!parser.writeLineF("{}\n", cfg->nAtoms()))
Expand All @@ -30,9 +31,18 @@ bool TrajectoryExportFileFormat::exportXYZ(LineParser &parser, Configuration *cf

// Write Atoms
for (const auto &i : cfg->atoms())
if (!parser.writeLineF("{:<3} {:15.9f} {:15.9f} {:15.9f}\n", Elements::symbol(i.speciesAtom()->Z()), i.r().x,
i.r().y, i.r().z))
return false;
if (extended)
{
if (!parser.writeLineF("{:<3} {:15.9f} {:15.9f} {:15.9f} {:<6d} {}\n", Elements::symbol(i.speciesAtom()->Z()),
i.r().x, i.r().y, i.r().z, i.localTypeIndex(), i.speciesAtom()->atomType()->name()))
return false;
}
else
{
if (!parser.writeLineF("{:<3} {:15.9f} {:15.9f} {:15.9f}\n", Elements::symbol(i.speciesAtom()->Z()), i.r().x,
i.r().y, i.r().z))
return false;
}

return true;
}
Expand Down Expand Up @@ -60,7 +70,7 @@ bool TrajectoryExportFileFormat::exportData(Configuration *cfg)
{
auto headerResult = false;

if (formats_.enumerationByIndex(*formatIndex_) == TrajectoryExportFormat::XYZ)
if (*formatIndex_ >= 0 && *formatIndex_ < formats_.nOptions())
headerResult = true;
else
headerResult = Messenger::error("Unrecognised trajectory format so can't write header.\nKnown formats are:\n");
Expand All @@ -75,7 +85,10 @@ bool TrajectoryExportFileFormat::exportData(Configuration *cfg)
switch (formats_.enumerationByIndex(*formatIndex_))
{
case (TrajectoryExportFormat::XYZ):
frameResult = exportXYZ(parser, cfg);
frameResult = exportXYZ(parser, cfg, false);
break;
case (TrajectoryExportFormat::XYZExtended):
frameResult = exportXYZ(parser, cfg, true);
break;
default:
throw(std::runtime_error(fmt::format("Trajectory format '{}' export has not been implemented.\n",
Expand Down
5 changes: 3 additions & 2 deletions src/io/export/trajectory.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ class TrajectoryExportFileFormat : public FileAndFormat
// Trajectory Export Formats
enum class TrajectoryExportFormat
{
XYZ
XYZ,
XYZExtended
};
TrajectoryExportFileFormat(std::string_view filename = "", TrajectoryExportFormat format = TrajectoryExportFormat::XYZ);
~TrajectoryExportFileFormat() override = default;
Expand All @@ -39,7 +40,7 @@ class TrajectoryExportFileFormat : public FileAndFormat
*/
private:
// Append XYZ frame to trajectory
bool exportXYZ(LineParser &parser, Configuration *cfg);
bool exportXYZ(LineParser &parser, Configuration *cfg, bool extended);

public:
// Append trajectory using current filename and format
Expand Down
1 change: 1 addition & 0 deletions tests/io/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
dissolve_add_test(SRC cif.cpp)
dissolve_add_test(SRC exportTrajectory.cpp)
dissolve_add_test(SRC intraParameterParse.cpp)
dissolve_add_test(SRC version.cpp)
112 changes: 112 additions & 0 deletions tests/io/exportTrajectory.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2024 Team Dissolve and contributors

#include "base/lineParser.h"
#include "base/sysFunc.h"
#include "data/elements.h"
#include "io/export/trajectory.h"
#include "tests/testData.h"
#include <fstream>
#include <gtest/gtest.h>
#include <vector>

namespace UnitTest
{
class ExportTrajectoryTest : public ::testing::Test
{
protected:
DissolveSystemTest systemTest;
};

std::string exportFile(DissolveSystemTest &systemTest, std::string outfile,
TrajectoryExportFileFormat::TrajectoryExportFormat format)
{
std::string inputFile = "dissolve/input/angle.txt";

systemTest.setUp(inputFile);

auto *cfg = systemTest.coreData().configuration(0);

auto output_path = fmt::format("{}/{}", DissolveSys::beforeLastChar(inputFile, '/'), outfile);
TrajectoryExportFileFormat exporter(output_path, format);
EXPECT_TRUE(exporter.exportData(cfg));

return output_path;
}

TEST_F(ExportTrajectoryTest, XYZ)
{
auto output_path = exportFile(systemTest, "TestOutput_exportTrajectory.basic.xyz",
TrajectoryExportFileFormat::TrajectoryExportFormat::XYZ);
auto *cfg = systemTest.coreData().configuration(0);

std::ifstream result(output_path);
ASSERT_TRUE(result.is_open());

// Size Header
int size;
result >> size;
EXPECT_EQ(size, cfg->nAtoms());

// Configuration Header
std::string name, at;
int version;
result >> name >> at >> version;
EXPECT_EQ(name, cfg->name());
EXPECT_EQ(at, "@");
EXPECT_EQ(version, cfg->contentsVersion());

// Line by line analysis
for (auto atom : cfg->atoms())
{
std::string elem;
double x, y, z;
result >> elem >> x >> y >> z;
EXPECT_EQ(elem, Elements::symbol(atom.speciesAtom()->Z()));
EXPECT_NEAR(atom.x(), x, 1e-9);
EXPECT_NEAR(atom.y(), y, 1e-9);
EXPECT_NEAR(atom.z(), z, 1e-9);
break;
}
}

TEST_F(ExportTrajectoryTest, XYZExport)
{
auto output_path = exportFile(systemTest, "TestOutput_exportTrajectory.extended.xyz",
TrajectoryExportFileFormat::TrajectoryExportFormat::XYZExtended);
auto *cfg = systemTest.coreData().configuration(0);

std::ifstream result(output_path);
ASSERT_TRUE(result.is_open());

// Size Header
int size;
result >> size;
EXPECT_EQ(size, cfg->nAtoms());

// Configuration Header
std::string name, at;
int version;
result >> name >> at >> version;
EXPECT_EQ(name, cfg->name());
EXPECT_EQ(at, "@");
EXPECT_EQ(version, cfg->contentsVersion());

// Line by line analysis
for (auto atom : cfg->atoms())
{
std::string elem, atomType;
double x, y, z;
int index;
result >> elem >> x >> y >> z >> index >> atomType;
EXPECT_EQ(elem, Elements::symbol(atom.speciesAtom()->Z()));
EXPECT_NEAR(atom.x(), x, 1e-9);
EXPECT_NEAR(atom.y(), y, 1e-9);
EXPECT_NEAR(atom.z(), z, 1e-9);
EXPECT_EQ(atomType, atom.speciesAtom()->atomType()->name());
EXPECT_EQ(index, atom.localTypeIndex());
break;
}
}

} // namespace UnitTest
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ description: Trajectory import/export
|Keyword|Description|
|:---:|-----------|
|`xyz`|Appended XMol-style xyz coordinates. Line 1 contains the number of atoms N. Line 2 contains a title string. The next N lines contain "element rx ry rz". This format is repeated for each frame.|
|`xyzExt`|Extended XMol-style xyz coordinates. Mostly identical to `xyz`, but with two additional columns appended to the line for each atom. These columns contain first the index of the atom within the molecule, followed by the atom type|