diff --git a/src/cxx/nux/bo_approximation.cpp b/src/cxx/nux/bo_approximation.cpp new file mode 100644 index 0000000..abc6502 --- /dev/null +++ b/src/cxx/nux/bo_approximation.cpp @@ -0,0 +1,89 @@ +/* + * 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 "nux_modules.hpp" + +namespace nux { + +using simde::type::chemical_system; +using simde::type::hamiltonian; +using simde::type::many_electrons; +using pt = simde::Convert; + +const auto desc = R"( +Born-Oppenheimer Hamiltonian Driver +----------------------------------- + +This module defines the algorithm used throughout NWChemEx to convert a +ChemicalSystem object into a Hamiltonian object using the Born-Oppenheimer +approximation. + +Specifically this driver will convert: +- ManyElectrons to a T_e and V_ee terms. +- Nuclei to a V_nn +- ManyElectrons + Nuclei to a V_en term. + +The terms will be added following the "usual" ordering: + +- Electronic Hamiltonian + - Electronic kinetic energy + - Electron-nucleus attraction + - Electron-electron repulsion +- Nuclear-nuclear repulsion + +Terms that are zero will not appear in the Hamiltonian. +)"; + +MODULE_CTOR(BOApproximation) { + satisfies_property_type(); + description(desc); +} + +MODULE_RUN(BOApproximation) { + const auto &[sys] = pt::unwrap_inputs(inputs); + + // Step 0: Decompose system into pieces + const auto electrons = sys.molecule().electrons(); + const auto nuclei = sys.molecule().nuclei().as_nuclei(); + bool has_electrons = electrons.size(); + bool has_nuclei = nuclei.size(); + + using simde::type::T_e_type; + using simde::type::V_ee_type; + using simde::type::V_en_type; + using simde::type::V_nn_type; + + // Step 1: Create non-zero terms and add them to the Hamiltonian + // N.b. the logic is a bit convoluted to conform to "usual" ordering (see + // module description) + + hamiltonian H; + if (has_electrons) { + H.emplace_back(1.0, std::make_unique(electrons)); + if (has_nuclei) + H.emplace_back(1.0, std::make_unique(electrons, nuclei)); + if (electrons.size() > 1) + H.emplace_back(1.0, std::make_unique(electrons, electrons)); + } + if (nuclei.size() > 1) { + H.emplace_back(1.0, std::make_unique(nuclei, nuclei)); + } + + auto rv = results(); + return pt::wrap_results(rv, H); +} + +} // namespace nux \ No newline at end of file diff --git a/src/cxx/nux/nux.cpp b/src/cxx/nux/nux.cpp index 5cead9b..368a69e 100644 --- a/src/cxx/nux/nux.cpp +++ b/src/cxx/nux/nux.cpp @@ -14,10 +14,13 @@ * limitations under the License. */ +#include "nux_modules.hpp" #include namespace nux { -void load_modules(pluginplay::ModuleManager &mm) {} +void load_modules(pluginplay::ModuleManager &mm) { + mm.add_module("Born-Oppenheimer Approximation"); +} } // namespace nux \ No newline at end of file diff --git a/src/cxx/nux/nux_modules.hpp b/src/cxx/nux/nux_modules.hpp new file mode 100644 index 0000000..a223e16 --- /dev/null +++ b/src/cxx/nux/nux_modules.hpp @@ -0,0 +1,24 @@ +/* + * 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. + */ + +#pragma once +#include + +namespace nux { + +DECLARE_MODULE(BOApproximation); + +} \ No newline at end of file diff --git a/tests/cxx/test_nux.hpp b/tests/cxx/test_nux.hpp index 0c9b662..69a6e17 100644 --- a/tests/cxx/test_nux.hpp +++ b/tests/cxx/test_nux.hpp @@ -17,5 +17,30 @@ #include #include #include +#include -namespace test_nux {} \ No newline at end of file +namespace test_nux { + +inline auto h_nucleus(double x, double y, double z) { + return simde::type::nucleus("H", 1ul, 1836.15, x, y, z); +} + +template ResultType make_h2() { + using simde::type::chemical_system; + using simde::type::molecule; + using simde::type::nuclei; + if constexpr (std::is_same_v) { + auto h0 = h_nucleus(0.0, 0.0, 0.0); + auto h1 = h_nucleus(0.0, 0.0, 1.3984); + return nuclei{h0, h1}; + } else if constexpr (std::is_same_v) { + return molecule(0, 1, make_h2()); + } else if constexpr (std::is_same_v) { + return chemical_system(make_h2()); + } else { + // We know this assert fails if we're in the else statement + // Getting here means you provided a bad type. + static_assert(std::is_same_v); + } +} +} // namespace test_nux \ No newline at end of file diff --git a/tests/cxx/unit_tests/bo_approximation.cpp b/tests/cxx/unit_tests/bo_approximation.cpp new file mode 100644 index 0000000..8264233 --- /dev/null +++ b/tests/cxx/unit_tests/bo_approximation.cpp @@ -0,0 +1,97 @@ +/* + * 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 + +using namespace nux; + +TEST_CASE("BOApproximation") { + pluginplay::ModuleManager mm; + load_modules(mm); + + using simde::type::chemical_system; + using simde::type::hamiltonian; + using simde::type::T_e_type; + using simde::type::V_ee_type; + using simde::type::V_en_type; + using simde::type::V_nn_type; + + using pt = simde::Convert; + + auto &mod = mm.at("Born-Oppenheimer Approximation"); + + SECTION("Empty chemical system") { + chemical_system sys; + auto H = mod.run_as(sys); + REQUIRE(H == hamiltonian{}); + } + + SECTION("H2") { + auto h2 = test_nux::make_h2(); + auto electrons = h2.molecule().electrons(); + auto nuclei = h2.molecule().nuclei().as_nuclei(); + + auto H = mod.run_as(h2); + hamiltonian H_corr; + H_corr.emplace_back(1.0, std::make_unique(electrons)); + H_corr.emplace_back(1.0, std::make_unique(electrons, nuclei)); + H_corr.emplace_back(1.0, std::make_unique(electrons, electrons)); + H_corr.emplace_back(1.0, std::make_unique(nuclei, nuclei)); + REQUIRE(H == H_corr); + } + + SECTION("H atom") { + simde::type::nuclei nuclei{test_nux::h_nucleus(0.0, 0.0, 0.0)}; + simde::type::chemical_system h(simde::type::molecule(0, 2, nuclei)); + + auto electrons = h.molecule().electrons(); + + auto H = mod.run_as(h); + hamiltonian H_corr; + H_corr.emplace_back(1.0, std::make_unique(electrons)); + H_corr.emplace_back(1.0, std::make_unique(electrons, nuclei)); + REQUIRE(H == H_corr); + } + + SECTION("H anion") { + simde::type::nuclei nuclei{test_nux::h_nucleus(0.0, 0.0, 0.0)}; + simde::type::chemical_system h(simde::type::molecule(-1, 1, nuclei)); + + auto electrons = h.molecule().electrons(); + + auto H = mod.run_as(h); + hamiltonian H_corr; + H_corr.emplace_back(1.0, std::make_unique(electrons)); + H_corr.emplace_back(1.0, std::make_unique(electrons, nuclei)); + H_corr.emplace_back(1.0, std::make_unique(electrons, electrons)); + REQUIRE(H == H_corr); + } + + SECTION("H2 cation") { + auto nuclei = test_nux::make_h2(); + simde::type::chemical_system h(simde::type::molecule(1, 2, nuclei)); + + auto electrons = h.molecule().electrons(); + + auto H = mod.run_as(h); + hamiltonian H_corr; + H_corr.emplace_back(1.0, std::make_unique(electrons)); + H_corr.emplace_back(1.0, std::make_unique(electrons, nuclei)); + H_corr.emplace_back(1.0, std::make_unique(nuclei, nuclei)); + REQUIRE(H == H_corr); + } +} \ No newline at end of file diff --git a/tests/cxx/unit_tests/nux.cpp b/tests/cxx/unit_tests/nux.cpp index faa865e..2064ae1 100644 --- a/tests/cxx/unit_tests/nux.cpp +++ b/tests/cxx/unit_tests/nux.cpp @@ -16,7 +16,9 @@ #include "../test_nux.hpp" #include + TEST_CASE("load_plugin") { pluginplay::ModuleManager mm; nux::load_modules(mm); + REQUIRE(mm.size() > 0); } \ No newline at end of file diff --git a/tests/python/integration_tests/test_nux/test_plugin.py b/tests/python/integration_tests/test_nux/test_plugin.py index 1828946..c5a2d23 100644 --- a/tests/python/integration_tests/test_nux/test_plugin.py +++ b/tests/python/integration_tests/test_nux/test_plugin.py @@ -19,6 +19,9 @@ class TestLoadModules(unittest.TestCase): + def test_load_modules(self): + self.assertGreater(self.mm.size(), 0) + def setUp(self): self.mm = ModuleManager() nux.load_modules(self.mm) diff --git a/tests/python/test_nux.py b/tests/python/test_nux.py new file mode 100644 index 0000000..e17015e --- /dev/null +++ b/tests/python/test_nux.py @@ -0,0 +1,26 @@ +# 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. + +import chemist + + +def h_nucleus(x, y, z): + return chemist.Nucleus('H', 1, 1836.15, x, y, z) + + +def h2_nuclei(): + h0 = h_nucleus(0.0, 0.0, 0.0) + h1 = h_nucleus(0.0, 0.0, 1.3984) + + return chemist.Nuclei(h0, h1) diff --git a/tests/python/unit_tests/test_nux/test_bo_approximation.py b/tests/python/unit_tests/test_nux/test_bo_approximation.py new file mode 100644 index 0000000..cd3c616 --- /dev/null +++ b/tests/python/unit_tests/test_nux/test_bo_approximation.py @@ -0,0 +1,34 @@ +# 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. + +from pluginplay import ModuleManager +import nux +import simde +import chemist +import unittest + + +class TestBOApproximation(unittest.TestCase): + + def test_empty_chemical_system(self): + sys = chemist.ChemicalSystem() + # Hamiltonian isn't exposed to Python yet... + #H = self.mm.run_as(self.pt, 'Born-Oppenheimer Approximation', sys) + #H_corr = chemist.qm_operator.Hamiltonian() + #self.assertEqual(H, H_corr) + + def setUp(self): + self.mm = ModuleManager() + nux.load_modules(self.mm) + self.pt = simde.HamiltonianFromChemicalSystem() diff --git a/tests/python/unit_tests/test_nux/test_plugin.py b/tests/python/unit_tests/test_nux/test_plugin.py index 1828946..c5a2d23 100644 --- a/tests/python/unit_tests/test_nux/test_plugin.py +++ b/tests/python/unit_tests/test_nux/test_plugin.py @@ -19,6 +19,9 @@ class TestLoadModules(unittest.TestCase): + def test_load_modules(self): + self.assertGreater(self.mm.size(), 0) + def setUp(self): self.mm = ModuleManager() nux.load_modules(self.mm)