diff --git a/src/scf/xc/grid_from_file.cpp b/src/scf/xc/grid_from_file.cpp new file mode 100644 index 0000000..75057e0 --- /dev/null +++ b/src/scf/xc/grid_from_file.cpp @@ -0,0 +1,87 @@ +/* + * 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 "xc.hpp" +#include +#include +#include + +namespace scf::xc { +namespace { +const auto desc = R"( + +GridFromFile +------------ + +Reads a grid from a file. The file is expected to be a text file with four +columns: x, y, z, weight. Each row of the file corresponds to a grid point. +The columns should be space separated. An example of the file format is: + +.. code-block:: text + + 1.0 2.0 3.0 4.0 + 5.0 6.0 7.0 8.0 + +This would create two grid points: (1.0, 2.0, 3.0) with weight 4.0 and +(5.0, 6.0, 7.0) with weight 8.0. +)"; + +} + +using pt = simde::MolecularGrid; + +MODULE_CTOR(GridFromFile) { + satisfies_property_type(); + + add_input("Path to Grid File"); + + description(desc); +} + +MODULE_RUN(GridFromFile) { + // const auto& [molecule] = pt::unwrap_inputs(inputs); + + const auto& path = + inputs.at("Path to Grid File").value(); + + std::ifstream file(path); + if(!file.is_open()) { + throw std::runtime_error("Unable to open file: " + path.string()); + } + + // Create the grid + std::string line; + std::vector grid_points; + while(std::getline(file, line)) { + auto first_character = line.find_first_not_of(" \t\r\n"); + if(first_character == std::string::npos) continue; + double x, y, z, weight; + std::stringstream ss(line); + if(!(ss >> x >> y >> z >> weight) || !(ss >> std::ws).eof()) { + throw std::runtime_error("Malformed grid file line: '" + line + + "'"); + } + grid_points.emplace_back(weight, x, y, z); + } + chemist::Grid grid(grid_points.begin(), grid_points.end()); + + file.close(); + + // Return the grid + auto rv = results(); + return pt::wrap_results(rv, std::move(grid)); +} +} // namespace scf::xc diff --git a/src/scf/xc/xc.cpp b/src/scf/xc/xc.cpp index e74036e..d09bf11 100644 --- a/src/scf/xc/xc.cpp +++ b/src/scf/xc/xc.cpp @@ -18,7 +18,10 @@ #include "xc.hpp" namespace scf::xc { -void load_modules(pluginplay::ModuleManager& mm) { gauxc::load_modules(mm); } +void load_modules(pluginplay::ModuleManager& mm) { + gauxc::load_modules(mm); + mm.add_module("Grid From File"); +} void set_defaults(pluginplay::ModuleManager& mm) { gauxc::set_defaults(mm); } } // namespace scf::xc diff --git a/src/scf/xc/xc.hpp b/src/scf/xc/xc.hpp index 74ade90..9eb684e 100644 --- a/src/scf/xc/xc.hpp +++ b/src/scf/xc/xc.hpp @@ -19,6 +19,8 @@ namespace scf::xc { +DECLARE_MODULE(GridFromFile); + void set_defaults(pluginplay::ModuleManager& mm); void load_modules(pluginplay::ModuleManager& mm); diff --git a/tests/cxx/unit_tests/xc/grid_from_file.cpp b/tests/cxx/unit_tests/xc/grid_from_file.cpp new file mode 100644 index 0000000..30c7e56 --- /dev/null +++ b/tests/cxx/unit_tests/xc/grid_from_file.cpp @@ -0,0 +1,111 @@ +/* + * 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 "../../test_scf.hpp" +#include +#include + +using namespace scf; + +using pt = simde::MolecularGrid; + +TEST_CASE("GridFromFile") { + pluginplay::ModuleManager mm; + scf::load_modules(mm); + auto& mod = mm.at("Grid From File"); + + auto h2 = test_scf::make_h2(); + + SECTION("file doesn't exist") { + std::filesystem::path p = "non_existent_file.txt"; + mod.change_input("Path to Grid File", p); + REQUIRE_THROWS_AS(mod.run_as(h2), std::runtime_error); + } + + SECTION("only three points") { + std::filesystem::path p = "unit_test_pretend_h2_grid.txt"; + std::ofstream file(p); + file << "1.0 2.0 3.0\n"; + file << "5.0 6.0 7.0 8.0\n"; + file.close(); + mod.change_input("Path to Grid File", p); + REQUIRE_THROWS_AS(mod.run_as(h2), std::runtime_error); + std::filesystem::remove(p); + } + + SECTION("five numbers points") { + std::filesystem::path p = "unit_test_pretend_h2_grid.txt"; + std::ofstream file(p); + file << "1.0 2.0 3.0 4.0 5.0\n"; + file << "5.0 6.0 7.0 8.0\n"; + file.close(); + mod.change_input("Path to Grid File", p); + REQUIRE_THROWS_AS(mod.run_as(h2), std::runtime_error); + std::filesystem::remove(p); + } + + SECTION("non-mumeric data") { + std::filesystem::path p = "unit_test_pretend_h2_grid.txt"; + std::ofstream file(p); + file << "1.0 2.0 3.0 hello\n"; + file << "5.0 6.0 7.0 8.0\n"; + file.close(); + mod.change_input("Path to Grid File", p); + REQUIRE_THROWS_AS(mod.run_as(h2), std::runtime_error); + std::filesystem::remove(p); + } + + SECTION("good file") { + std::filesystem::path p = "unit_test_pretend_h2_grid.txt"; + std::ofstream file(p); + file << "1.0 2.0 3.0 4.0\n"; + file << "5.0 6.0 7.0 8.0\n"; + file.close(); + mod.change_input("Path to Grid File", p); + auto grid = mod.run_as(h2); + REQUIRE(grid.size() == 2); + REQUIRE(grid.at(0).weight() == 4.0); + REQUIRE(grid.at(0).point().x() == 1.0); + REQUIRE(grid.at(0).point().y() == 2.0); + REQUIRE(grid.at(0).point().z() == 3.0); + REQUIRE(grid.at(1).weight() == 8.0); + REQUIRE(grid.at(1).point().x() == 5.0); + REQUIRE(grid.at(1).point().y() == 6.0); + REQUIRE(grid.at(1).point().z() == 7.0); + std::filesystem::remove(p); + } + + SECTION("skip blank line") { + std::filesystem::path p = "unit_test_pretend_h2_grid.txt"; + std::ofstream file(p); + file << "1.0 2.0 3.0 4.0\n"; + file << "\n"; + file << "5.0 6.0 7.0 8.0\n"; + file.close(); + mod.change_input("Path to Grid File", p); + auto grid = mod.run_as(h2); + REQUIRE(grid.size() == 2); + REQUIRE(grid.at(0).weight() == 4.0); + REQUIRE(grid.at(0).point().x() == 1.0); + REQUIRE(grid.at(0).point().y() == 2.0); + REQUIRE(grid.at(0).point().z() == 3.0); + REQUIRE(grid.at(1).weight() == 8.0); + REQUIRE(grid.at(1).point().x() == 5.0); + REQUIRE(grid.at(1).point().y() == 6.0); + REQUIRE(grid.at(1).point().z() == 7.0); + std::filesystem::remove(p); + } +}