Skip to content
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
3 changes: 1 addition & 2 deletions .github/workflows/pull_request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ jobs:
with:
config_file: '.github/.licenserc.yaml'
source_dir: 'include src tests'
compilers: '["gcc-11"]'
compilers: '["gcc-11", "clang-14"]'
doc_target: 'nux_cxx_api'
build_fail_on_warning: false
secrets: inherit
3 changes: 3 additions & 0 deletions src/cxx/nux/nux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ namespace nux {
void load_modules(pluginplay::ModuleManager& mm) {
mm.add_module<BOApproximation>("Born-Oppenheimer Approximation");
mm.add_module<XYZToMolecule>("XYZ To Molecule");
mm.add_module<XYZFileToMolecule>("XYZ File to Molecule");

set_defaults(mm);
}

} // namespace nux
5 changes: 5 additions & 0 deletions src/cxx/nux/nux_modules.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,10 @@ namespace nux {

DECLARE_MODULE(BOApproximation);
DECLARE_MODULE(XYZToMolecule);
DECLARE_MODULE(XYZFileToMolecule);

inline void set_defaults(pluginplay::ModuleManager& mm) {
mm.change_submod("XYZ File To Molecule", "XYZ to molecule",
"XYZ To Molecule");
}
} // namespace nux
50 changes: 50 additions & 0 deletions src/cxx/nux/xyz_file_to_mol.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@

/*
* Copyright 2025 NWChemEx-Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "nux_modules.hpp"
#include <fstream>
#include <sstream>

namespace nux {

MODULE_CTOR(XYZFileToMolecule) {
satisfies_property_type<simde::MoleculeFromString>();
add_submodule<simde::MoleculeFromString>("XYZ to molecule");
}

MODULE_RUN(XYZFileToMolecule) {
const auto& [xyz_filename] =
simde::MoleculeFromString::unwrap_inputs(inputs);

auto& xyz_parser = submods.at("XYZ to molecule");

std::ifstream file(xyz_filename);
std::stringstream buffer;

if(!file.is_open()) {
throw std::runtime_error("File not found: " + xyz_filename);
}
buffer << file.rdbuf();
file.close();

chemist::Molecule mol =
xyz_parser.run_as<simde::MoleculeFromString>(buffer.str());

auto rv = results();
return simde::MoleculeFromString::wrap_results(rv, mol);
}
} // namespace nux
72 changes: 40 additions & 32 deletions src/cxx/nux/xyz_to_mol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,8 @@
*/

#include "nux_modules.hpp"
#include <fstream>
#include <sstream>
#include <string>
#include <vector>

namespace nux {

Expand All @@ -29,47 +27,57 @@ MODULE_CTOR(XYZToMolecule) {
}

MODULE_RUN(XYZToMolecule) {
const auto& [filename] = simde::MoleculeFromString::unwrap_inputs(inputs);

auto& z_from_sym = submods.at("Z from symbol");
auto& atom_from_z = submods.at("Atom from z");
const auto& [xyz_data] = simde::MoleculeFromString::unwrap_inputs(inputs);
auto& z_from_sym = submods.at("Z from symbol");
auto& atom_from_z = submods.at("Atom from z");

std::string line;
chemist::Molecule mol;

std::ifstream xyz_file(filename);

if(!xyz_file) { throw std::runtime_error("File not found: " + filename); }
std::stringstream buffer;

std::string line;
buffer << xyz_data;
if(buffer.str().empty()) {
throw std::runtime_error("File or XYZ data not valid, empty string");
}

// Skips the 1st line of the file, associated with the number of
// atoms in the file, and the 2nd line, the comment line.
std::getline(xyz_file, line);
std::getline(xyz_file, line);
long unsigned int num_atoms;
int i = 0;

while(std::getline(xyz_file, line)) {
while(std::getline(buffer, line)) {
i++;
if(i == 1) {
int num = std::stoi(line);
if(num < 0) {
throw std::out_of_range("Negative number for atom count: " +
line);
}
num_atoms = static_cast<long unsigned int>(num);
continue;
}
if(i == 2) { continue; }
std::istringstream iss(line);
std::vector<std::string> tokens;
std::string token;
while(std::getline(iss, token, ' ')) {
if(token.size()) { tokens.emplace_back(token); }
std::string atom_string;
double x, y, z;
if(!(iss >> atom_string >> x >> y >> z)) {
throw std::runtime_error("Incorrect XYZ Coordinate format: " +
line);
}
auto Z = z_from_sym.run_as<simde::ZFromSymbol>(tokens.at(0));
auto x = std::stod(tokens.at(1));
auto y = std::stod(tokens.at(2));
auto z = std::stod(tokens.at(3));

auto atm = atom_from_z.run_as<simde::AtomFromZ>(Z);
atm.x() = x;
atm.y() = y;
atm.z() = z;
mol.push_back(atm);
}

xyz_file.close();
auto Z = z_from_sym.run_as<simde::ZFromSymbol>(atom_string);
auto atom = atom_from_z.run_as<simde::AtomFromZ>(Z);
atom.x() = x;
atom.y() = y;
atom.z() = z;

mol.push_back(atom);
}
if(!(mol.size() == num_atoms)) {
throw std::runtime_error("Incorrect format for XYZ file: "
"Number of atoms and number of atom "
"coordinates do not match");
}
auto rv = results();
return simde::MoleculeFromString::wrap_results(rv, mol);
}

} // namespace nux
74 changes: 74 additions & 0 deletions tests/cxx/unit_tests/xyz_file_to_mol.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@

/*
* Copyright 2024 NWChemEx-Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "../test_nux.hpp"
#include <nux/nux.hpp>

TEST_CASE("XYZFileToMolecule") {
pluginplay::ModuleManager mm;
nux::load_modules(mm);

auto make_atoms = [=](auto&& Z) {
REQUIRE(Z == 1);
double h_mass = 1837.4260218693814;
return simde::type::atom{"H", 1, h_mass, 0.0, 0.0, 0.0};
};

auto atom_mod = pluginplay::make_lambda<simde::AtomFromZ>(make_atoms);

auto z_mod = pluginplay::make_lambda<simde::ZFromSymbol>(
[=](auto&& symbol) { return simde::type::atomic_number{1}; });

auto& xyz_file_mod = mm.at("XYZ File To Molecule");
auto& xyz_mod = mm.at("XYZ to Molecule");
xyz_mod.change_submod("Z from symbol", z_mod);
xyz_mod.change_submod("Atom from z", atom_mod);

SECTION("XYZ File Non-existent") {
std::string file = "file.xyz";
REQUIRE_THROWS_AS(xyz_file_mod.run_as<simde::MoleculeFromString>(file),
std::runtime_error);
}

SECTION("Full XYZ To Molecule run: File") {
auto atom0{make_atoms(1)};
auto atom1{make_atoms(1)};
atom1.z() = 1;

simde::type::molecule test_mol{atom0, atom1};

std::ofstream xyz_file;
xyz_file.open("h2.xyz");

xyz_file << "2\n";
xyz_file << "This is a comment!\n";
xyz_file << "H 0 0 0\n";
xyz_file << "H 0 0 1\n";
xyz_file.close();

std::string filename = "h2.xyz";

auto& xyz_file_mod = mm.at("XYZ File to Molecule");
auto mol = xyz_file_mod.run_as<simde::MoleculeFromString>(filename);

remove("h2.xyz");
std::ifstream del_file("h2.xyz");

REQUIRE(mol == test_mol);
REQUIRE(del_file.good() == false);
}
}
35 changes: 16 additions & 19 deletions tests/cxx/unit_tests/xyz_to_mol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,36 +36,33 @@ TEST_CASE("XYZToMolecule") {
xyz_mod.change_submod("Z from symbol", z_mod);
xyz_mod.change_submod("Atom from z", atom_mod);

SECTION("No XYZ file found") {
std::string file = "file.xyz";
REQUIRE_THROWS_AS(xyz_mod.run_as<simde::MoleculeFromString>(file),
SECTION("XYZ Atom Count Incorrect") {
std::string data = "2\nComment\nH 0 0 0";
REQUIRE_THROWS_AS(xyz_mod.run_as<simde::MoleculeFromString>(data),
std::runtime_error);
}

SECTION("Full XYZ To Molecule run") {
SECTION("XYZ Atom Coordinate Data Incorrect") {
std::string data = "2\nComment\nH 0 0 K\nH 0 0 1";
REQUIRE_THROWS_AS(xyz_mod.run_as<simde::MoleculeFromString>(data),
std::runtime_error);
}

SECTION("Full XYZ To Molecule run: Data") {
auto atom0{make_atoms(1)};
auto atom1{make_atoms(1)};
atom1.z() = 1;

simde::type::molecule test_mol{atom0, atom1};

std::ofstream xyz_file;
xyz_file.open("h2.xyz");

xyz_file << "2\n";
xyz_file << "This is a comment!\n";
xyz_file << "H 0 0 0\n";
xyz_file << "H 0 0 1\n";
xyz_file.close();

std::string filename = "h2.xyz";

auto mol = xyz_mod.run_as<simde::MoleculeFromString>(filename);
std::stringstream xyz_data;
xyz_data << "2\n";
xyz_data << "This is a comment!\n";
xyz_data << "H 0 0 0\n";
xyz_data << "H 0 0 1\n";

remove("h2.xyz");
std::ifstream del_file("h2.xyz");
auto mol = xyz_mod.run_as<simde::MoleculeFromString>(xyz_data.str());

REQUIRE(mol == test_mol);
REQUIRE(del_file.good() == false);
}
}