diff --git a/language_bindings/python/bind_py_cell.cc b/language_bindings/python/bind_py_cell.cc index 3e6abcb..11dbb4a 100644 --- a/language_bindings/python/bind_py_cell.cc +++ b/language_bindings/python/bind_py_cell.cc @@ -1,207 +1,220 @@ /** * @file bind_py_cell.cc * * @author Till Junge * * @date 09 Jan 2018 * * @brief Python bindings for the cell factory function * * Copyright © 2018 Till Junge * * µSpectre is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3, or (at * your option) any later version. * * µSpectre is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Emacs; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "common/common.hh" #include "common/ccoord_operations.hh" #include "cell/cell_factory.hh" #include "cell/cell_base.hh" #ifdef WITH_FFTWMPI #include "fft/fftwmpi_engine.hh" #endif #ifdef WITH_PFFT #include "fft/pfft_engine.hh" #endif #include #include #include "pybind11/eigen.h" #include #include using namespace muSpectre; namespace py=pybind11; using namespace pybind11::literals; /** * cell factory for specific FFT engine */ template void add_parallel_cell_factory_helper(py::module & mod, const char *name) { using Ccoord = Ccoord_t; using Rcoord = Rcoord_t; +#ifdef WITH_MPI mod.def (name, [](Ccoord res, Rcoord lens, Formulation form, size_t comm) { return make_parallel_cell , FFTEngine> (std::move(res), std::move(lens), std::move(form), std::move(Communicator(MPI_Comm(comm)))); }, "resolutions"_a, "lengths"_a=CcoordOps::get_cube(1.), "formulation"_a=Formulation::finite_strain, - "communicator"_a=size_t(MPI_COMM_NULL)); + "communicator"_a=size_t(MPI_COMM_SELF)); +#else + mod.def + (name, + [](Ccoord res, Rcoord lens, Formulation form) { + return make_parallel_cell + , FFTEngine> + (std::move(res), std::move(lens), std::move(form)); + }, + "resolutions"_a, + "lengths"_a=CcoordOps::get_cube(1.), + "formulation"_a=Formulation::finite_strain); +#endif } /** * the cell factory is only bound for default template parameters */ template void add_cell_factory_helper(py::module & mod) { using Ccoord = Ccoord_t; using Rcoord = Rcoord_t; mod.def ("CellFactory", [](Ccoord res, Rcoord lens, Formulation form) { return make_cell(std::move(res), std::move(lens), std::move(form)); }, "resolutions"_a, "lengths"_a=CcoordOps::get_cube(1.), "formulation"_a=Formulation::finite_strain); #ifdef WITH_FFTWMPI add_parallel_cell_factory_helper>( mod, "FFTWMPICellFactory"); #endif #ifdef WITH_PFFT add_parallel_cell_factory_helper>( mod, "PFFTCellFactory"); #endif } void add_cell_factory(py::module & mod) { add_cell_factory_helper(mod); add_cell_factory_helper(mod); } /** * CellBase for which the material and spatial dimension are identical */ template void add_cell_base_helper(py::module & mod) { std::stringstream name_stream{}; name_stream << "CellBase" << dim << 'd'; const std::string name = name_stream.str(); using sys_t = CellBase; py::class_(mod, name.c_str()) .def("__len__", &sys_t::size) .def("__iter__", [](sys_t & s) { return py::make_iterator(s.begin(), s.end()); }) .def("initialise", &sys_t::initialise, "flags"_a=FFT_PlanFlags::estimate) .def("directional_stiffness", [](sys_t& cell, py::EigenDRef& v) { if ((size_t(v.cols()) != cell.size() || size_t(v.rows()) != dim*dim)) { std::stringstream err{}; err << "need array of shape (" << dim*dim << ", " << cell.size() << ") but got (" << v.rows() << ", " << v.cols() << ")."; throw std::runtime_error(err.str()); } if (!cell.is_initialised()) { cell.initialise(); } const std::string out_name{"temp output for directional stiffness"}; const std::string in_name{"temp input for directional stiffness"}; constexpr bool create_tangent{true}; auto & K = cell.get_tangent(create_tangent); auto & input = cell.get_managed_field(in_name); auto & output = cell.get_managed_field(out_name); input.eigen() = v; cell.directional_stiffness(K, input, output); return output.eigen(); }, "δF"_a) .def("project", [](sys_t& cell, py::EigenDRef& v) { if ((size_t(v.cols()) != cell.size() || size_t(v.rows()) != dim*dim)) { std::stringstream err{}; err << "need array of shape (" << dim*dim << ", " << cell.size() << ") but got (" << v.rows() << ", " << v.cols() << ")."; throw std::runtime_error(err.str()); } if (!cell.is_initialised()) { cell.initialise(); } const std::string in_name{"temp input for projection"}; auto & input = cell.get_managed_field(in_name); input.eigen() = v; cell.project(input); return input.eigen(); }, "field"_a) .def("get_strain",[](sys_t & s) { return Eigen::ArrayXXd(s.get_strain().eigen()); }) .def("get_stress",[](sys_t & s) { return Eigen::ArrayXXd(s.get_stress().eigen()); }) .def("size", &sys_t::size) .def("evaluate_stress_tangent", [](sys_t& cell, py::EigenDRef& v ) { if ((size_t(v.cols()) != cell.size() || size_t(v.rows()) != dim*dim)) { std::stringstream err{}; err << "need array of shape (" << dim*dim << ", " << cell.size() << ") but got (" << v.rows() << ", " << v.cols() << ")."; throw std::runtime_error(err.str()); } auto & strain{cell.get_strain()}; strain.eigen() = v; cell.evaluate_stress_tangent(strain); }, "strain"_a) .def("get_G", &sys_t::get_projection); } void add_cell_base(py::module & mod) { add_cell_base_helper (mod); add_cell_base_helper(mod); } void add_cell(py::module & mod) { add_cell_factory(mod); auto cell{mod.def_submodule("cell")}; cell.doc() = "bindings for cells and cell factories"; cell.def("scale_by_2", [](py::EigenDRef& v) { v *= 2; }); add_cell_base(cell); } diff --git a/language_bindings/python/bind_py_fftengine.cc b/language_bindings/python/bind_py_fftengine.cc index 71070cb..2cfb82a 100644 --- a/language_bindings/python/bind_py_fftengine.cc +++ b/language_bindings/python/bind_py_fftengine.cc @@ -1,86 +1,106 @@ /** * @file bind_py_fftengine.cc * * @author Till Junge * * @date 17 Jan 2018 * * @brief Python bindings for the FFT engines * * Copyright © 2018 Till Junge * * µSpectre is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3, or (at * your option) any later version. * * µSpectre is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Emacs; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "fft/fftw_engine.hh" +#ifdef WITH_FFTWMPI +#include "fft/fftwmpi_engine.hh" +#endif +#ifdef WITH_PFFT +#include "fft/pfft_engine.hh" +#endif #include "bind_py_declarations.hh" #include #include #include using namespace muSpectre; namespace py=pybind11; using namespace pybind11::literals; template void add_engine_helper(py::module & mod, std::string name) { using Ccoord = Ccoord_t; using Rcoord = Rcoord_t; using ArrayXXc = Eigen::Array; py::class_(mod, name.c_str()) +#ifdef WITH_MPI + .def(py::init([](Ccoord res, Rcoord lengths, size_t comm) { + return new Engine(res, lengths, + std::move(Communicator(MPI_Comm(comm)))); + }), + "resolutions"_a, + "lengths"_a, + "communicator"_a=size_t(MPI_COMM_SELF)) +#else .def(py::init()) +#endif .def("fft", [](Engine & eng, py::EigenDRef v) { using Coll_t = typename Engine::GFieldCollection_t; using Field_t = typename Engine::Field_t; Coll_t coll{}; coll.initialise(eng.get_resolutions(), eng.get_locations()); Field_t & temp{make_field("temp_field", coll)}; temp.eigen() = v; return ArrayXXc{eng.fft(temp).eigen()}; }, "array"_a) .def("ifft", [](Engine & eng, py::EigenDRef v) { using Coll_t = typename Engine::GFieldCollection_t; using Field_t = typename Engine::Field_t; Coll_t coll{}; coll.initialise(eng.get_resolutions(), eng.get_locations()); Field_t & temp{make_field("temp_field", coll)}; - eng.get_work_space().eigen()=v; + eng.get_work_space().eigen() = v; eng.ifft(temp); return Eigen::ArrayXXd{temp.eigen()}; }, "array"_a) .def("initialise", &Engine::initialise, "flags"_a=FFT_PlanFlags::estimate) .def("normalisation", &Engine::normalisation); } -void add_engine(py::module & mod) { - add_engine_helper< twoD, FFTWEngine< twoD, twoD>>(mod, "FFTW_2d"); - add_engine_helper>(mod, "FFTW_3d"); -} - void add_fft_engines(py::module & mod) { auto fft{mod.def_submodule("fft")}; fft.doc() = "bindings for µSpectre's fft engines"; - add_engine(fft); + add_engine_helper< twoD, FFTWEngine< twoD, twoD>>(fft, "FFTW_2d"); + add_engine_helper>(fft, "FFTW_3d"); +#ifdef WITH_FFTWMPI + add_engine_helper< twoD, FFTWMPIEngine< twoD, twoD>>(fft, "FFTWMPI_2d"); + add_engine_helper>(fft, "FFTWMPI_3d"); +#endif +#ifdef WITH_PFFT + add_engine_helper< twoD, PFFTEngine< twoD, twoD>>(fft, "PFFT_2d"); + add_engine_helper>(fft, "PFFT_3d"); +#endif add_projections(fft); } diff --git a/language_bindings/python/muSpectre/__init__.py b/language_bindings/python/muSpectre/__init__.py index 1761156..88867e8 100644 --- a/language_bindings/python/muSpectre/__init__.py +++ b/language_bindings/python/muSpectre/__init__.py @@ -1,85 +1,88 @@ # # @file __init__.py # # @author Lars Pastewka # # @date 21 Mar 2018 # # @brief Main entry point for muSpectre Python module # # Copyright © 2018 Till Junge # # µSpectre is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation, either version 3, or (at # your option) any later version. # # µSpectre is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNU Emacs; see the file COPYING. If not, write to the # Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. # import mpi4py import _muSpectre -from _muSpectre import (fft, get_domain_ccoord, get_domain_index, +from _muSpectre import (get_domain_ccoord, get_domain_index, get_hermitian_sizes, material, solvers, Formulation) +import muSpectre.fft _factories = { 'fftw': ('CellFactory', False), 'fftwmpi': ('FFTWMPICellFactory', True), 'pfft': ('PFFTCellFactory', True), 'p3dfft': ('P3DFFTCellFactory', True), } def Cell(resolutions, lengths, formulation=Formulation.finite_strain, fft='fftw', communicator=None): """ Instantiate a muSpectre Cell class. Parameters ---------- resolutions: list Grid resolutions in the Cartesian directions. lengths: list Physical size of the cell in the Cartesian directions. formulation: Formulation Formulation for strains and stresses used by the solver. Options are `Formulation.finite_strain` and `Formulation.small_strain`. Finite strain formulation is the default. fft: string FFT engine to use. Options are 'fftw', 'fftwmpi', 'pfft' and 'p3dfft'. Default is 'fftw'. communicator: mpi4py communicator mpi4py communicator object passed to parallel FFT engines. Note that the default 'fftw' engine does not support parallel execution. Returns ------- cell: object Return a muSpectre Cell object. """ try: factory_name, is_parallel = _factories[fft] except KeyError: raise KeyError("Unknown FFT engine '{}'.".format(fft)) try: factory = _muSpectre.__dict__[factory_name] except KeyError: raise KeyError("FFT engine '{}' has not been compiled into the " "muSpectre library.".format(fft)) if is_parallel: + if communicator is None: + communicator = mpi4py.MPI.COMM_SELF return factory(resolutions, lengths, formulation, mpi4py.MPI._handleof(communicator)) else: if communicator is not None: raise ValueError("FFT engine '{}' does not support parallel " "execution.".format(fft)) return factory(resolutions, lengths, formulation) diff --git a/language_bindings/python/muSpectre/__init__.py b/language_bindings/python/muSpectre/fft.py similarity index 64% copy from language_bindings/python/muSpectre/__init__.py copy to language_bindings/python/muSpectre/fft.py index 1761156..fa79da0 100644 --- a/language_bindings/python/muSpectre/__init__.py +++ b/language_bindings/python/muSpectre/fft.py @@ -1,85 +1,90 @@ # -# @file __init__.py +# @file fft.py # # @author Lars Pastewka # -# @date 21 Mar 2018 +# @date 27 Mar 2018 # -# @brief Main entry point for muSpectre Python module +# @brief Wrapper for muSpectre's FFT engines # # Copyright © 2018 Till Junge # # µSpectre is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation, either version 3, or (at # your option) any later version. # # µSpectre is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNU Emacs; see the file COPYING. If not, write to the # Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. # import mpi4py import _muSpectre -from _muSpectre import (fft, get_domain_ccoord, get_domain_index, - get_hermitian_sizes, material, solvers, Formulation) _factories = { - 'fftw': ('CellFactory', False), - 'fftwmpi': ('FFTWMPICellFactory', True), - 'pfft': ('PFFTCellFactory', True), - 'p3dfft': ('P3DFFTCellFactory', True), + 'fftw': ('FFTW_2d', 'FFTW_3d', False), + 'fftwmpi': ('FFTWMPI_2d', 'FFTWMPI_3d', True), + 'pfft': ('PFFT_2d', 'PFFT_3d', True), + 'p3dfft': ('P3DFFT_2d', 'P3DFFT_3d', True), } -def Cell(resolutions, lengths, formulation=Formulation.finite_strain, - fft='fftw', communicator=None): +def FFT(resolutions, lengths, fft='fftw', communicator=None): """ - Instantiate a muSpectre Cell class. + Instantiate a muSpectre FFT class. Parameters ---------- resolutions: list Grid resolutions in the Cartesian directions. lengths: list Physical size of the cell in the Cartesian directions. - formulation: Formulation - Formulation for strains and stresses used by the solver. Options are - `Formulation.finite_strain` and `Formulation.small_strain`. Finite - strain formulation is the default. fft: string FFT engine to use. Options are 'fftw', 'fftwmpi', 'pfft' and 'p3dfft'. Default is 'fftw'. communicator: mpi4py communicator mpi4py communicator object passed to parallel FFT engines. Note that the default 'fftw' engine does not support parallel execution. Returns ------- cell: object Return a muSpectre Cell object. """ + if len(resolutions) != len(lengths): + raise ValueError("'resolutions' and 'lengths' must have identical " + "lengths.") try: - factory_name, is_parallel = _factories[fft] + factory_name_2d, factory_name_3d, is_parallel = _factories[fft] except KeyError: raise KeyError("Unknown FFT engine '{}'.".format(fft)) + if len(resolutions) == 2: + factory_name = factory_name_2d + elif len(resolutions) == 3: + factory_name = factory_name_3d + else: + raise ValueError('{}-d transforms are not supported' + .format(len(resolutions))) try: - factory = _muSpectre.__dict__[factory_name] + factory = _muSpectre.fft.__dict__[factory_name] except KeyError: raise KeyError("FFT engine '{}' has not been compiled into the " "muSpectre library.".format(fft)) if is_parallel: - return factory(resolutions, lengths, formulation, + if communicator is None: + communicator = mpi4py.MPI.COMM_SELF + return factory(resolutions, lengths, mpi4py.MPI._handleof(communicator)) else: if communicator is not None: raise ValueError("FFT engine '{}' does not support parallel " "execution.".format(fft)) - return factory(resolutions, lengths, formulation) + return factory(resolutions, lengths)