From 6e06e823edace0956b8532bebde82058f0f3a14f Mon Sep 17 00:00:00 2001 From: Timo Koch <timo.koch@iws.uni-stuttgart.de> Date: Wed, 15 Apr 2020 22:30:34 +0200 Subject: [PATCH] [python] Add first Python bindings for the TimeLoop and the GridGeometry --- CMakeLists.txt | 6 + dumux/python/CMakeLists.txt | 2 + dumux/python/common/CMakeLists.txt | 3 + dumux/python/common/timeloop.hh | 48 ++++++++ dumux/python/discretization/gridgeometry.hh | 128 ++++++++++++++++++++ python/CMakeLists.txt | 2 + python/dumux/CMakeLists.txt | 2 + python/dumux/common/CMakeLists.txt | 5 + python/dumux/common/__init__.py | 1 + python/dumux/common/_common.cc | 27 +++++ python/dumux/discretization/CMakeLists.txt | 3 + python/dumux/discretization/__init__.py | 21 ++++ python/setup.py.in | 11 ++ 13 files changed, 259 insertions(+) create mode 100644 dumux/python/CMakeLists.txt create mode 100644 dumux/python/common/CMakeLists.txt create mode 100644 dumux/python/common/timeloop.hh create mode 100644 dumux/python/discretization/gridgeometry.hh create mode 100644 python/CMakeLists.txt create mode 100644 python/dumux/CMakeLists.txt create mode 100644 python/dumux/common/CMakeLists.txt create mode 100644 python/dumux/common/__init__.py create mode 100644 python/dumux/common/_common.cc create mode 100644 python/dumux/discretization/CMakeLists.txt create mode 100644 python/dumux/discretization/__init__.py create mode 100644 python/setup.py.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 8abf0ab4a7..ae7dcbcbef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,5 +33,11 @@ add_subdirectory(dumux) add_subdirectory(test EXCLUDE_FROM_ALL) add_subdirectory(examples EXCLUDE_FROM_ALL) +# if Python bindings are enabled, include necessary sub directories. +if(DUNE_ENABLE_PYTHONBINDINGS) + add_subdirectory("python") + dune_python_install_package(PATH "python") +endif() + # finalize the dune project, e.g. generating config.h etc. finalize_dune_project(GENERATE_CONFIG_H_CMAKE) diff --git a/dumux/python/CMakeLists.txt b/dumux/python/CMakeLists.txt new file mode 100644 index 0000000000..4f95771a66 --- /dev/null +++ b/dumux/python/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(common) +add_subdirectory(discretization) diff --git a/dumux/python/common/CMakeLists.txt b/dumux/python/common/CMakeLists.txt new file mode 100644 index 0000000000..b507d026f0 --- /dev/null +++ b/dumux/python/common/CMakeLists.txt @@ -0,0 +1,3 @@ +install(FILES +timeloop.hh +DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dumux/python/common) diff --git a/dumux/python/common/timeloop.hh b/dumux/python/common/timeloop.hh new file mode 100644 index 0000000000..03a165a154 --- /dev/null +++ b/dumux/python/common/timeloop.hh @@ -0,0 +1,48 @@ +#ifndef DUMUX_PYTHON_COMMON_TIMELOOP_HH +#define DUMUX_PYTHON_COMMON_TIMELOOP_HH + +#include <dumux/common/timeloop.hh> + +#include <dune/python/pybind11/pybind11.h> +#include <dune/python/pybind11/stl.h> + +namespace Dumux::Python { + +template <class Scalar, class... options> +void registerTimeLoop(pybind11::handle scope, + pybind11::class_<CheckPointTimeLoop<Scalar>, options...> cls) +{ + using pybind11::operator""_a; + + using TimeLoop = CheckPointTimeLoop<Scalar>; + cls.def(pybind11::init([](Scalar startTime, Scalar dt, Scalar endTime, bool verbose){ + return new TimeLoop(startTime, dt, endTime, verbose); + }), "startTime"_a, "dt"_a, "endTime"_a, "verbose"_a=true); + + cls.def("time", &TimeLoop::time); + cls.def("timeStepSize", &TimeLoop::timeStepSize); + cls.def("start", &TimeLoop::start); + cls.def("reset", &TimeLoop::reset, "startTime"_a, "dt"_a, "endTime"_a, "verbose"_a=true); + cls.def("advanceTimeStep", &TimeLoop::advanceTimeStep); + cls.def("setTimeStepSize", &TimeLoop::setTimeStepSize, "dt"_a); + cls.def("setMaxTimeStepSize", &TimeLoop::setMaxTimeStepSize, "dt"_a); + cls.def("finished", &TimeLoop::finished); + cls.def("reportTimeStep", &TimeLoop::reportTimeStep); + cls.def("finalize", [](TimeLoop& self){ self.finalize(); }); + cls.def("setPeriodicCheckPoint", &TimeLoop::setPeriodicCheckPoint, "interval"_a, "offset"_a=0.0); + cls.def("isCheckPoint", &TimeLoop::isCheckPoint); + cls.def("setCheckPoints", [](TimeLoop& self, const std::vector<double>& checkPoints) { + self.setCheckPoint(checkPoints); + }); +} + +template<class Scalar> +void registerTimeLoop(pybind11::handle scope, const char *clsName = "TimeLoop") +{ + pybind11::class_<CheckPointTimeLoop<Scalar>> cls(scope, clsName); + registerTimeLoop(scope, cls); +} + +} // namespace Dumux::Python + +#endif diff --git a/dumux/python/discretization/gridgeometry.hh b/dumux/python/discretization/gridgeometry.hh new file mode 100644 index 0000000000..619b1e468c --- /dev/null +++ b/dumux/python/discretization/gridgeometry.hh @@ -0,0 +1,128 @@ +#ifndef DUMUX_PYTHON_DISCRETIZATION_GRIDGEOMETRY_HH +#define DUMUX_PYTHON_DISCRETIZATION_GRIDGEOMETRY_HH + +#include <memory> +#include <dune/python/pybind11/pybind11.h> +#include <dune/python/common/typeregistry.hh> + +namespace Dumux::Python { + +namespace Impl { + +template<class SCV> +void registerSubControlVolume(pybind11::handle scope) +{ + using namespace Dune::Python; + + auto [cls, addedToRegistry] = insertClass<SCV>( + scope, "SubControlVolume", + GenerateTypeName("SubControlVolume"), + IncludeFiles{"dumux/python/discretization/gridgeometry.hh"} + ); + + if (addedToRegistry) + { + using pybind11::operator""_a; + + cls.def("center", &SCV::center); + cls.def("volume", &SCV::volume); + cls.def("dofIndex", &SCV::dofIndex); + cls.def("localDofIndex", &SCV::localDofIndex); + cls.def("dofPosition", &SCV::dofPosition); + cls.def("elementIndex", &SCV::elementIndex); + + } +} + +template<class SCVF> +void registerSubControlVolumeFace(pybind11::handle scope) +{ + using namespace Dune::Python; + + auto [cls, addedToRegistry] = insertClass<SCVF>( + scope, "SubControlVolumeFace", + GenerateTypeName("SubControlVolumeFace"), + IncludeFiles{"dumux/python/discretization/gridgeometry.hh"} + ); + + if (addedToRegistry) + { + using pybind11::operator""_a; + + cls.def("center", &SCVF::center); + cls.def("area", &SCVF::area); + cls.def("ipGlobal", &SCVF::ipGlobal); + cls.def("boundary", &SCVF::boundary); + cls.def("unitOuterNormal", &SCVF::unitOuterNormal); + cls.def("insideScvIdx", &SCVF::insideScvIdx); + cls.def("outsideScvIdx", [](SCVF& self){ return self.outsideScvIdx(); }); + cls.def("index", &SCVF::index); + } +} + +template<class FVEG> +void registerFVElementGeometry(pybind11::handle scope) +{ + using namespace Dune::Python; + + auto [cls, addedToRegistry] = insertClass<FVEG>( + scope, "FVElementGeometry", + GenerateTypeName("FVElementGeometry"), + IncludeFiles{"dumux/python/discretization/gridgeometry.hh"} + ); + + if (addedToRegistry) + { + using pybind11::operator""_a; + + cls.def("numScvf", &FVEG::numScv); + cls.def("numScv", &FVEG::numScv); + cls.def("bind", &FVEG::bind, "element"_a); + cls.def("hasBoundaryScvf", &FVEG::hasBoundaryScvf); + cls.def("scvs", [](FVEG& self){ + const auto range = scvs(self); + return pybind11::make_iterator(range.begin(), range.end()); + }, pybind11::keep_alive<0, 1>()); + cls.def("scvfs", [](FVEG& self){ + const auto range = scvfs(self); + return pybind11::make_iterator(range.begin(), range.end()); + }, pybind11::keep_alive<0, 1>()); + } +} + +} // end namespace Impl + +// see python/dumux/discretization/__init__.py for how this is used for JIT compilation +template <class GG, class... Options> +void registerGridGeometry(pybind11::handle scope, pybind11::class_<GG, Options...> cls) +{ + using pybind11::operator""_a; + using namespace Dune::Python; + + using GridView = typename GG::GridView; + + cls.def(pybind11::init([](GridView gridView){ + return std::make_unique<GG>(gridView); + }), "gridView"_a); + + cls.def("update", &GG::update); + cls.def("numDofs", &GG::numDofs); + cls.def("numScv", &GG::numScv); + cls.def("numScvf", &GG::numScvf); + + using SubControlVolume = typename GG::SubControlVolume; + Impl::registerSubControlVolume<SubControlVolume>(scope); + + using SubControlVolumeFace = typename GG::SubControlVolumeFace; + Impl::registerSubControlVolumeFace<SubControlVolumeFace>(scope); + + // also compile the corresponding local view + using LocalView = typename GG::LocalView; + Impl::registerFVElementGeometry<LocalView>(scope); + // and make it accessible + cls.def("localView", [](GG& self){ return localView(self); }); +} + +} // namespace Dumux::Python + +#endif diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt new file mode 100644 index 0000000000..0c4ddccf90 --- /dev/null +++ b/python/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(dumux) +configure_file(setup.py.in setup.py) diff --git a/python/dumux/CMakeLists.txt b/python/dumux/CMakeLists.txt new file mode 100644 index 0000000000..4f95771a66 --- /dev/null +++ b/python/dumux/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(common) +add_subdirectory(discretization) diff --git a/python/dumux/common/CMakeLists.txt b/python/dumux/common/CMakeLists.txt new file mode 100644 index 0000000000..38d491fa22 --- /dev/null +++ b/python/dumux/common/CMakeLists.txt @@ -0,0 +1,5 @@ +add_python_targets(common + __init__ +) +dune_add_pybind11_module(NAME _common) +set_property(TARGET _common PROPERTY LINK_LIBRARIES dunecommon dunegrid APPEND) diff --git a/python/dumux/common/__init__.py b/python/dumux/common/__init__.py new file mode 100644 index 0000000000..c897d03434 --- /dev/null +++ b/python/dumux/common/__init__.py @@ -0,0 +1 @@ +from ._common import * diff --git a/python/dumux/common/_common.cc b/python/dumux/common/_common.cc new file mode 100644 index 0000000000..49bca34533 --- /dev/null +++ b/python/dumux/common/_common.cc @@ -0,0 +1,27 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program 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 of the License, or * + * (at your option) any later version. * + * * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ + +#include <dune/python/pybind11/pybind11.h> +#include <dumux/python/common/timeloop.hh> + +PYBIND11_MODULE(_common, module) +{ + // export time loop + Dumux::Python::registerTimeLoop<double>(module); +} diff --git a/python/dumux/discretization/CMakeLists.txt b/python/dumux/discretization/CMakeLists.txt new file mode 100644 index 0000000000..22b582c564 --- /dev/null +++ b/python/dumux/discretization/CMakeLists.txt @@ -0,0 +1,3 @@ +add_python_targets(discretization + __init__ +) diff --git a/python/dumux/discretization/__init__.py b/python/dumux/discretization/__init__.py new file mode 100644 index 0000000000..e4d73fb277 --- /dev/null +++ b/python/dumux/discretization/__init__.py @@ -0,0 +1,21 @@ +from dune.generator.generator import SimpleGenerator +from dune.common.hashit import hashIt + +# construct a GridGeometry from a gridView +# the grid geometry is JIT compiled +def GridGeometry(gridView, discMethod="cctpfa"): + includes = gridView._includes + ["dumux/python/discretization/gridgeometry.hh"] + + if discMethod == "cctpfa": + includes += ["dumux/discretization/cellcentered/tpfa/fvgridgeometry.hh"] + typeName = "Dumux::CCTpfaFVGridGeometry<" + gridView._typeName + ">" + elif discMethod == "box": + includes += ["dumux/discretization/box/fvgridgeometry.hh"] + typeName = "Dumux::BoxFVGridGeometry<double, " + gridView._typeName + ">" + else: + raise ValueError("Unknown discMethod {}".format(discMethod)) + + moduleName = "gridgeometry_" + hashIt(typeName) + generator = SimpleGenerator("GridGeometry", "Dumux::Python") + module = generator.load(includes, typeName, moduleName) + return module.GridGeometry(gridView) diff --git a/python/setup.py.in b/python/setup.py.in new file mode 100644 index 0000000000..8cb1413ea2 --- /dev/null +++ b/python/setup.py.in @@ -0,0 +1,11 @@ +from setuptools import setup, find_namespace_packages + +setup(name="dumux", + description="Python lib for dumux", + version="${DUMUX_VERSION}", + author="Timo Koch", + packages = find_namespace_packages(include=['dumux.*']), + zip_safe = 0, + package_data = {'': ['*.so']}, + install_requires = ['portalocker'], +) -- GitLab