diff --git a/.gitmodules b/.gitmodules index c51df9e2766afb892e5f8fd513617c44eafa9704..e22e17ff10b1e6b1d8b9880c78b2d9b2086644b7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,3 +22,6 @@ [submodule "ThirdParty/cmake-modules"] path = ThirdParty/cmake-modules url = https://github.com/bilke/cmake-modules.git +[submodule "ThirdParty/pybind11"] + path = ThirdParty/pybind11 + url = https://github.com/pybind/pybind11.git diff --git a/Applications/ApplicationsLib/CMakeLists.txt b/Applications/ApplicationsLib/CMakeLists.txt index 701c66a4c58918e3921934ed248a42f9ba381ba7..9d734bcb839430e8e13bc73f572b84b1cf756a79 100644 --- a/Applications/ApplicationsLib/CMakeLists.txt +++ b/Applications/ApplicationsLib/CMakeLists.txt @@ -22,3 +22,7 @@ endforeach() if(OGS_USE_PCH) cotire(ApplicationsLib) endif() + +if(OGS_USE_PYTHON) + target_link_libraries(ApplicationsLib PRIVATE pybind11::pybind11) +endif() diff --git a/Applications/ApplicationsLib/ProjectData.cpp b/Applications/ApplicationsLib/ProjectData.cpp index 58b077d4a0af2460de993c2f215b890aaed47997..d3d07d07bd6c47cdf07a5820f03a169a120152a5 100644 --- a/Applications/ApplicationsLib/ProjectData.cpp +++ b/Applications/ApplicationsLib/ProjectData.cpp @@ -18,6 +18,10 @@ #include <logog/include/logog.hpp> +#ifdef OGS_USE_PYTHON +#include <pybind11/eval.h> +#endif + #include "BaseLib/Algorithm.h" #include "BaseLib/FileTools.h" @@ -162,6 +166,23 @@ ProjectData::ProjectData(BaseLib::ConfigTree const& project_config, { _mesh_vec = readMeshes(project_config, project_directory); + if (auto const python_script = + //! \ogs_file_param{prj__python_script} + project_config.getConfigParameterOptional<std::string>("python_script")) + { +#ifdef OGS_USE_PYTHON + namespace py = pybind11; + auto const script_path = + BaseLib::copyPathToFileName(*python_script, project_directory); + + // Evaluate in scope of main module + py::object scope = py::module::import("__main__").attr("__dict__"); + py::eval_file(script_path, scope); +#else + OGS_FATAL("OpenGeoSys has not been built with Python support."); +#endif // OGS_USE_PYTHON + } + //! \ogs_file_param{prj__curves} parseCurves(project_config.getConfigSubtreeOptional("curves")); diff --git a/Applications/CLI/CMakeLists.txt b/Applications/CLI/CMakeLists.txt index c2410af0547a690a67d35040746b99cf947c35b4..f15fcb737427d8013770e431caf2a3db3901a992 100644 --- a/Applications/CLI/CMakeLists.txt +++ b/Applications/CLI/CMakeLists.txt @@ -5,6 +5,51 @@ target_link_libraries(ogs PRIVATE BaseLib ApplicationsLib ) +if(OGS_USE_PYTHON) + # Troubleshooting: + # If you get linker errors, such as ogs.cpp:(.text+0xb4): undefined reference to `_Py_ZeroStruct' + # it could be that OGS is compiled with the wrong Python version. + # I (Ch. Leh.) observed the following: The symbol _Py_ZeroStruct could not be found in /usr/lib/libpython3.6m.so (I intended to compile OGS with Python3). + # It's apparently a Python2 symbol (present in /usr/lib/libpython2.7.so) + # The compiler command-line was the following: + # /usr/bin/g++ ... -DvtkRenderingVolume_AUTOINIT="1(vtkRenderingVolumeOpenGL2)" \ + # -I/usr/include/vtk -I/usr/include/python2.7 -I/usr/include/freetype2 \ + # -I/usr/include/libxml2 ... -I/.../BaseLib ... \ + # -isystem /usr/include/python3.6m ... -o CMakeFiles/ogs.dir/ogs.cpp.o \ + # -c /.../Applications/CLI/ogs.cpp + # In particular, the Python2 include path comes before the Python3 include path. + # Compiling OGS with Python2 solved the issue. + # I assume (this is only a guess!) that VTK pulls in Python2 dependencies (on my system). + # I assume that this is related to https://github.com/ufz/ogs/pull/2158. + # Workaround: Always make sure that OGS is compiled with the same Python version as VTK. + # The error described above should be detected automatically by cmake and an + # appropriate message should be presented. The note is kept for the case + # that the automatic detection does not work due to whatever reason. + + add_library(ogs_embedded_python STATIC ogs_embedded_python.cpp) + + # Performance warning from + # https://github.com/pybind/pybind11/blob/master/docs/compiling.rst: + # Since pybind11 is a metatemplate library, it is crucial that certain compiler + # flags are provided to ensure high quality code generation. In contrast to the + # pybind11_add_module() command, the CMake interface library only provides the + # minimal set of parameters to ensure that the code using pybind11 compiles, but + # it does not pass these extra compiler flags (i.e. this is up to you). + # TODO: Enable further compiler/linker flags. + target_link_libraries(ogs_embedded_python PUBLIC pybind11::embed) + target_compile_definitions(ogs_embedded_python PUBLIC OGS_USE_PYTHON) + target_link_libraries(ogs_embedded_python PRIVATE + ProcessLibBoundaryConditionPythonModule) + + target_link_libraries(ogs PRIVATE ogs_embedded_python) + + if(BUILD_SHARED_LIBS) + # Add macro definition, because static libs make special handling necessary + # s.t. the embedded OpenGeoSys Python module won't be removed by the linker. + target_compile_definitions(ogs_embedded_python OGS_BUILD_SHARED_LIBS) + endif() +endif() + if(OGS_USE_PETSC) target_link_libraries(ogs PRIVATE ${PETSC_LIBRARIES}) endif() diff --git a/Applications/CLI/ogs.cpp b/Applications/CLI/ogs.cpp index 1a7dbc096a267bedfe71c3135d74e77dfddc1e36..87694bcc50aa67da5077c77b5e3b087df8213583 100644 --- a/Applications/CLI/ogs.cpp +++ b/Applications/CLI/ogs.cpp @@ -10,8 +10,8 @@ * */ -#include <chrono> #include <tclap/CmdLine.h> +#include <chrono> #ifndef _WIN32 #ifdef __APPLE__ @@ -42,7 +42,11 @@ #include "NumLib/NumericsConfig.h" -int main(int argc, char *argv[]) +#ifdef OGS_USE_PYTHON +#include "ogs_embedded_python.h" +#endif + +int main(int argc, char* argv[]) { // Parse CLI arguments. TCLAP::CmdLine cmd( @@ -65,34 +69,32 @@ int main(int argc, char *argv[]) "PROJECT FILE"); cmd.add(project_arg); - TCLAP::ValueArg<std::string> outdir_arg( - "o", "output-directory", - "the output directory to write to", - false, - "", - "output directory"); + TCLAP::ValueArg<std::string> outdir_arg("o", "output-directory", + "the output directory to write to", + false, "", "output directory"); cmd.add(outdir_arg); - TCLAP::ValueArg<std::string> log_level_arg( - "l", "log-level", - "the verbosity of logging messages: none, error, warn, info, debug, all", - false, + TCLAP::ValueArg<std::string> log_level_arg("l", "log-level", + "the verbosity of logging " + "messages: none, error, warn, " + "info, debug, all", + false, #ifdef NDEBUG - "info", + "info", #else - "all", + "all", #endif - "log level"); + "log level"); cmd.add(log_level_arg); TCLAP::SwitchArg nonfatal_arg("", - "config-warnings-nonfatal", - "warnings from parsing the configuration file will not trigger program abortion"); + "config-warnings-nonfatal", + "warnings from parsing the configuration " + "file will not trigger program abortion"); cmd.add(nonfatal_arg); - TCLAP::SwitchArg unbuffered_cout_arg("", - "unbuffered-std-out", - "use unbuffered standard output"); + TCLAP::SwitchArg unbuffered_cout_arg("", "unbuffered-std-out", + "use unbuffered standard output"); cmd.add(unbuffered_cout_arg); #ifndef _WIN32 // TODO: On windows floating point exceptions are not handled @@ -124,6 +126,11 @@ int main(int argc, char *argv[]) #endif // __APPLE__ #endif // _WIN32 +#ifdef OGS_USE_PYTHON + pybind11::scoped_interpreter guard = ApplicationsLib::setupEmbeddedPython(); + (void)guard; +#endif + BaseLib::RunTime run_time; { @@ -167,7 +174,9 @@ int main(int argc, char *argv[]) if (auto t = project_config->getConfigSubtreeOptional("insitu")) { //! \ogs_file_param{prj__insitu__scripts} - InSituLib::Initialize(t->getConfigSubtree("scripts"), BaseLib::extractPath(project_arg.getValue())); + InSituLib::Initialize( + t->getConfigSubtree("scripts"), + BaseLib::extractPath(project_arg.getValue())); isInsituConfigured = true; } #else @@ -196,11 +205,11 @@ int main(int argc, char *argv[]) #ifdef USE_INSITU if (isInsituConfigured) InSituLib::Finalize(); - #endif +#endif INFO("[time] Execution took %g s.", run_time.elapsed()); #if defined(USE_PETSC) - controller->Finalize(1) ; + controller->Finalize(1); #endif } // This nested scope ensures that everything that could possibly // possess a ConfigTree is destructed before the final check below is @@ -209,7 +218,9 @@ int main(int argc, char *argv[]) BaseLib::ConfigTree::assertNoSwallowedErrors(); ogs_status = solver_succeeded ? EXIT_SUCCESS : EXIT_FAILURE; - } catch (std::exception& e) { + } + catch (std::exception& e) + { ERR(e.what()); ogs_status = EXIT_FAILURE; } diff --git a/Applications/CLI/ogs_embedded_python.cpp b/Applications/CLI/ogs_embedded_python.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2da7a23d1a518b6df3296b914b195bdc6169b530 --- /dev/null +++ b/Applications/CLI/ogs_embedded_python.cpp @@ -0,0 +1,52 @@ +/** + * \copyright + * Copyright (c) 2012-2018, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + */ + +#include <pybind11/embed.h> +#include <logog/include/logog.hpp> + +#include "ogs_embedded_python.h" + +#include "ProcessLib/BoundaryCondition/Python/PythonBoundaryConditionModule.h" + +PYBIND11_EMBEDDED_MODULE(OpenGeoSys, m) +{ + DBUG("Binding Python module OpenGeoSys."); + + ProcessLib::pythonBindBoundaryCondition(m); +} + +#ifndef OGS_BUILD_SHARED_LIBS + +// Hackish trick that hopefully ensures that the linker won't strip the symbol +// pointed to by p from the library being built. +template <typename T> +void mark_used(T p) +{ + volatile T vp = (volatile T)p; + vp = vp; +} + +#endif // OGS_BUILD_SHARED_LIBS + +namespace ApplicationsLib +{ +pybind11::scoped_interpreter setupEmbeddedPython() +{ +#ifndef OGS_BUILD_SHARED_LIBS + // pybind11_init_impl_OpenGeoSys is the function initializing the embedded + // OpenGeoSys Python module. The name is generated by pybind11. If it is not + // obvious that this symbol is actually used, the linker might remove it + // under certain circumstances. + mark_used(&pybind11_init_impl_OpenGeoSys); +#endif + + return pybind11::scoped_interpreter{}; +} + +} // namespace ApplicationsLib diff --git a/Applications/CLI/ogs_embedded_python.h b/Applications/CLI/ogs_embedded_python.h new file mode 100644 index 0000000000000000000000000000000000000000..758718298ba1cb3e00d47338f58901d01ea82074 --- /dev/null +++ b/Applications/CLI/ogs_embedded_python.h @@ -0,0 +1,20 @@ +/** + * \copyright + * Copyright (c) 2012-2018, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + */ + +#pragma once + +#include <pybind11/embed.h> + +namespace ApplicationsLib +{ +/// Sets up an embedded Python interpreter and makes sure that the OpenGeoSys +/// Python module is not removed by the linker. +pybind11::scoped_interpreter setupEmbeddedPython(); + +} // namespace ApplicationsLib diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d87e8ba12738e631d4dfbb34c53744ff2ab4dc4..add4d4668f48f6f2453db13e5984dc560592ff0e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -210,6 +210,8 @@ option(OGS_ENABLE_ELEMENT_PYRAMID "Build FEM elements for pyramids." ON) option(OGS_CHECK_HEADER_COMPILATION "Check header for standalone compilation." OFF) +option(OGS_USE_PYTHON "Interface with Python" OFF) + ################### ### Definitions ### ################### @@ -259,7 +261,6 @@ if(OGS_USE_EIGEN) add_definitions(-DEIGEN_INITIALIZE_MATRICES_BY_NAN) endif() endif() - if(MSVC AND OGS_32_BIT) add_definitions(-DEIGEN_MAX_ALIGN_BYTES=0 -DEIGEN_DONT_ALIGN) endif() diff --git a/Documentation/ProjectFile/prj/process_variables/process_variable/boundary_conditions/boundary_condition/Python/c_Python.md b/Documentation/ProjectFile/prj/process_variables/process_variable/boundary_conditions/boundary_condition/Python/c_Python.md new file mode 100644 index 0000000000000000000000000000000000000000..f0a0824aed56cce00616ae2bb55ffe11123102d2 --- /dev/null +++ b/Documentation/ProjectFile/prj/process_variables/process_variable/boundary_conditions/boundary_condition/Python/c_Python.md @@ -0,0 +1,4 @@ +Boundary condition using a Python script to compute its values. + +See ProcessLib::PythonBoundaryConditionPythonSideInterface for the Python-side +BC interface. diff --git a/Documentation/ProjectFile/prj/process_variables/process_variable/boundary_conditions/boundary_condition/Python/t_bc_object.md b/Documentation/ProjectFile/prj/process_variables/process_variable/boundary_conditions/boundary_condition/Python/t_bc_object.md new file mode 100644 index 0000000000000000000000000000000000000000..5933158c0315078f23036aff899e4e947ff483a3 --- /dev/null +++ b/Documentation/ProjectFile/prj/process_variables/process_variable/boundary_conditions/boundary_condition/Python/t_bc_object.md @@ -0,0 +1,2 @@ +The object (variable name) in the provided Python script that will be used for +this BC. diff --git a/Documentation/ProjectFile/prj/process_variables/process_variable/boundary_conditions/boundary_condition/Python/t_flush_stdout.md b/Documentation/ProjectFile/prj/process_variables/process_variable/boundary_conditions/boundary_condition/Python/t_flush_stdout.md new file mode 100644 index 0000000000000000000000000000000000000000..8edea230fc2f64e19d3923da1df77da3136ca5c4 --- /dev/null +++ b/Documentation/ProjectFile/prj/process_variables/process_variable/boundary_conditions/boundary_condition/Python/t_flush_stdout.md @@ -0,0 +1,3 @@ +Determines if stdout should be flushed each time before and after passing control to the Python script. +This might slow down operation, but ensures the right order of messages printed +to stdout from Python and C++. diff --git a/Documentation/ProjectFile/prj/t_python_script.md b/Documentation/ProjectFile/prj/t_python_script.md new file mode 100644 index 0000000000000000000000000000000000000000..95a9399605d58a98b6796d9d18c6cea5b9dd03bc --- /dev/null +++ b/Documentation/ProjectFile/prj/t_python_script.md @@ -0,0 +1,5 @@ +Path to a Python script file (absolute or relative to the prj file) that is +executed by OpenGeoSys if OpenGeoSys has been built with Python support. + +This script can be used, e.g., to define boundary conditions that can be +referenced from the prj file and used by OpenGeoSys. diff --git a/Jenkinsfile b/Jenkinsfile index dcbbc0b0be74e0743adde3f0cf9f9f68604ec01d..cb9fe4e1f1a7126602833f8752319cedc02c31c1 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -85,7 +85,8 @@ pipeline { cmakeOptions = '-DOGS_USE_CONAN=ON ' + '-DOGS_CPU_ARCHITECTURE=generic ' + - '-DDOCS_GENERATE_LOGFILE=ON ' // redirects to build/DoxygenWarnings.log + '-DDOCS_GENERATE_LOGFILE=ON ' + // redirects to build/DoxygenWarnings.log + '-DOGS_USE_PYTHON=ON ' } build { } build { target="tests" } @@ -240,7 +241,8 @@ pipeline { configure { cmakeOptions = '-DOGS_USE_CONAN=ON ' + - '-DOGS_DOWNLOAD_ADDITIONAL_CONTENT=ON ' + '-DOGS_DOWNLOAD_ADDITIONAL_CONTENT=ON ' + + '-DOGS_USE_PYTHON=ON ' } build { } build { target="tests" } diff --git a/NumLib/DOF/LocalToGlobalIndexMap.cpp b/NumLib/DOF/LocalToGlobalIndexMap.cpp index 40034c2eb29109184704af316912eb01d828b773..d1a91bacf81e8b908e388d2a40d9daf14b5185f0 100644 --- a/NumLib/DOF/LocalToGlobalIndexMap.cpp +++ b/NumLib/DOF/LocalToGlobalIndexMap.cpp @@ -191,7 +191,8 @@ LocalToGlobalIndexMap::LocalToGlobalIndexMap( std::vector<int> const& global_component_ids, std::vector<int> const& variable_component_offsets, std::vector<MeshLib::Element*> const& elements, - NumLib::MeshComponentMap&& mesh_component_map) + NumLib::MeshComponentMap&& mesh_component_map, + LocalToGlobalIndexMap::ConstructorTag) : _mesh_subsets(std::move(mesh_subsets)), _mesh_component_map(std::move(mesh_component_map)), _variable_component_offsets(variable_component_offsets) @@ -249,7 +250,42 @@ LocalToGlobalIndexMap* LocalToGlobalIndexMap::deriveBoundaryConstrainedMap( return new LocalToGlobalIndexMap( std::move(all_mesh_subsets), global_component_ids, - _variable_component_offsets, elements, std::move(mesh_component_map)); + _variable_component_offsets, elements, std::move(mesh_component_map), + ConstructorTag{}); +} + +std::unique_ptr<LocalToGlobalIndexMap> +LocalToGlobalIndexMap::deriveBoundaryConstrainedMap( + MeshLib::MeshSubset&& new_mesh_subset) const +{ + DBUG("Construct reduced local to global index map."); + + // Create a subset of the current mesh component map. + std::vector<int> global_component_ids; + + for (int i = 0; i < getNumberOfComponents(); ++i) + { + global_component_ids.push_back(i); + } + + // Elements of the new_mesh_subset's mesh. + std::vector<MeshLib::Element*> const& elements = + new_mesh_subset.getMesh().getElements(); + + auto mesh_component_map = _mesh_component_map.getSubset( + _mesh_subsets, new_mesh_subset, global_component_ids); + + // Create copies of the new_mesh_subset for each of the global components. + // The last component is moved after the for-loop. + std::vector<MeshLib::MeshSubset> all_mesh_subsets; + for (int i = 0; i < static_cast<int>(global_component_ids.size()) - 1; ++i) + all_mesh_subsets.emplace_back(new_mesh_subset); + all_mesh_subsets.emplace_back(std::move(new_mesh_subset)); + + return std::make_unique<LocalToGlobalIndexMap>( + std::move(all_mesh_subsets), global_component_ids, + _variable_component_offsets, elements, std::move(mesh_component_map), + ConstructorTag{}); } std::size_t LocalToGlobalIndexMap::dofSizeWithGhosts() const diff --git a/NumLib/DOF/LocalToGlobalIndexMap.h b/NumLib/DOF/LocalToGlobalIndexMap.h index 41c1c02456af72fb3db23199b875237a7eb01f3c..8100ac00c155e200f072c91b1115e18e3051eede 100644 --- a/NumLib/DOF/LocalToGlobalIndexMap.h +++ b/NumLib/DOF/LocalToGlobalIndexMap.h @@ -41,6 +41,14 @@ namespace NumLib /// mesh item. class LocalToGlobalIndexMap final { + // Enables using std::make_unique with private constructors from within + // member functions of LocalToGlobalIndexMap. Cf. + // http://seanmiddleditch.com/enabling-make_unique-with-private-constructors/ + struct ConstructorTag + { + explicit ConstructorTag() = default; + }; + public: using RowColumnIndices = MathLib::RowColumnIndices<GlobalIndexType>; using LineIndex = RowColumnIndices::LineIndex; @@ -90,6 +98,13 @@ public: std::vector<int> const& component_ids, MeshLib::MeshSubset&& mesh_subset) const; + /// Derive a LocalToGlobalIndexMap constrained to the mesh subset and mesh + /// subset's elements. A new mesh component map will be constructed using + /// the passed mesh_subset for all variables and components of the current + /// LocalToGlobalIndexMap. + std::unique_ptr<LocalToGlobalIndexMap> deriveBoundaryConstrainedMap( + MeshLib::MeshSubset&& new_mesh_subset) const; + /// Returns total number of degrees of freedom including those located in /// the ghost nodes. std::size_t dofSizeWithGhosts() const; @@ -144,10 +159,14 @@ public: MeshLib::MeshSubset const& getMeshSubset( int const global_component_id) const; -private: - /// Private constructor used by internally created local-to-global index - /// maps. The mesh_component_map is passed as argument instead of being - /// created by the constructor. + /// The global component id for the specific variable (like velocity) and a + /// component (like x, or y, or z). + int getGlobalComponent(int const variable_id, int const component_id) const; + + /// Private constructor (ensured by ConstructorTag) used by internally + /// created local-to-global index maps. The mesh_component_map is passed as + /// argument instead of being created by the constructor. + /// /// \attention The passed mesh_component_map is in undefined state after /// this construtor. explicit LocalToGlobalIndexMap( @@ -155,8 +174,9 @@ private: std::vector<int> const& global_component_ids, std::vector<int> const& variable_component_offsets, std::vector<MeshLib::Element*> const& elements, - NumLib::MeshComponentMap&& mesh_component_map); + NumLib::MeshComponentMap&& mesh_component_map, ConstructorTag); +private: template <typename ElementIterator> void findGlobalIndices(ElementIterator first, ElementIterator last, std::vector<MeshLib::Node*> const& nodes, @@ -169,11 +189,6 @@ private: std::vector<MeshLib::Node*> const& nodes, std::size_t const mesh_id, const int component_id, const int comp_id_write); - /// The global component id for the specific variable (like velocity) and a - /// component (like x, or y, or z). - int getGlobalComponent(int const variable_id, int const component_id) const; - -private: /// A vector of mesh subsets for each process variables' components. std::vector<MeshLib::MeshSubset> _mesh_subsets; NumLib::MeshComponentMap _mesh_component_map; diff --git a/NumLib/Fem/FiniteElement/TemplateIsoparametric.h b/NumLib/Fem/FiniteElement/TemplateIsoparametric.h index 8484ea1ddf26b2aa3fe4af5997cd96514d71e1d5..c54a99aec4226b4f51f340ce779bfe80d45194f7 100644 --- a/NumLib/Fem/FiniteElement/TemplateIsoparametric.h +++ b/NumLib/Fem/FiniteElement/TemplateIsoparametric.h @@ -12,25 +12,21 @@ #pragma once -#include <cassert> #include <boost/math/constants/constants.hpp> +#include <cassert> -#include "NumLib/Fem/CoordinatesMapping/ShapeMatrices.h" #include "NumLib/Fem/CoordinatesMapping/NaturalCoordinatesMapping.h" +#include "NumLib/Fem/CoordinatesMapping/ShapeMatrices.h" namespace NumLib { - /** * \brief Template class for isoparametric elements * * \tparam ShapeFunctionType_ The shape function type. * \tparam ShapeMatrixTypes_ An aggregate of shape matrix types. */ -template < - class ShapeFunctionType_, - class ShapeMatrixTypes_ - > +template <class ShapeFunctionType_, class ShapeMatrixTypes_> class TemplateIsoparametric { public: @@ -44,36 +40,29 @@ public: /// Natural coordinates mapping tools specialization for specific /// MeshElement and ShapeFunction types. - using NaturalCoordsMappingType = NaturalCoordinatesMapping< - MeshElementType, ShapeFunctionType, ShapeMatrices>; + using NaturalCoordsMappingType = + NaturalCoordinatesMapping<MeshElementType, ShapeFunctionType, + ShapeMatrices>; /** - * Constructor without specifying a mesh element. setMeshElement() must be called afterwards. + * Constructor without specifying a mesh element. setMeshElement() must be + * called afterwards. */ - TemplateIsoparametric() - : _ele(nullptr) - { - } + TemplateIsoparametric() : _ele(nullptr) {} /** * Construct this object for the given mesh element. * * @param e Mesh element object */ - TemplateIsoparametric(const MeshElementType &e) - : _ele(&e) - { - } + TemplateIsoparametric(const MeshElementType& e) : _ele(&e) {} ~TemplateIsoparametric() = default; /// return current mesh element - const MeshElementType* getMeshElement() const {return _ele;} + const MeshElementType* getMeshElement() const { return _ele; } /// Sets the mesh element - void setMeshElement(const MeshElementType &e) - { - this->_ele = &e; - } + void setMeshElement(const MeshElementType& e) { this->_ele = &e; } /** * compute shape functions * @@ -112,23 +101,45 @@ public: computeIntegralMeasure(is_axially_symmetric, shape); } + /// Interpolates the x coordinate of the element with the given shape + /// matrix. double interpolateZerothCoordinate( typename ShapeMatrices::ShapeType const& N) const { auto* nodes = _ele->getNodes(); typename ShapeMatrices::ShapeType rs(N.size()); - for (int i=0; i<rs.size(); ++i) { + for (int i = 0; i < rs.size(); ++i) + { rs[i] = (*nodes[i])[0]; } auto const r = N.dot(rs); return r; } + /// Interpolates the coordinates of the element with the given shape matrix. + std::array<double, 3> interpolateCoordinates( + typename ShapeMatrices::ShapeType const& N) const + { + auto* nodes = _ele->getNodes(); + typename ShapeMatrices::ShapeType rs(N.size()); + std::array<double, 3> interpolated_coords; + for (int d = 0; d < 3; ++d) + { + for (int i = 0; i < rs.size(); ++i) + { + rs[i] = (*nodes[i])[d]; + } + interpolated_coords[d] = N.dot(rs); + } + return interpolated_coords; + } + private: void computeIntegralMeasure(bool is_axially_symmetric, ShapeMatrices& shape) const { - if (!is_axially_symmetric) { + if (!is_axially_symmetric) + { shape.integralMeasure = 1.0; return; } @@ -140,11 +151,10 @@ private: // located at edge of the unit triangle, it is possible that // r becomes zero. auto const r = interpolateZerothCoordinate(shape.N); - shape.integralMeasure = - boost::math::constants::two_pi<double>() * r; + shape.integralMeasure = boost::math::constants::two_pi<double>() * r; } const MeshElementType* _ele; }; -} // NumLib +} // namespace NumLib diff --git a/NumLib/ODESolver/NonlinearSolver.cpp b/NumLib/ODESolver/NonlinearSolver.cpp index f1e4c472c962cfe950d9d76d941f3ae5da8517a7..ba32dd836d7c5b19c54abe5f20dd98f2686609af 100644 --- a/NumLib/ODESolver/NonlinearSolver.cpp +++ b/NumLib/ODESolver/NonlinearSolver.cpp @@ -209,7 +209,7 @@ bool NonlinearSolver<NonlinearSolverTag::Newton>::solve( BaseLib::RunTime time_dirichlet; time_dirichlet.start(); - sys.applyKnownSolutionsNewton(J, res, minus_delta_x); + sys.applyKnownSolutionsNewton(J, res, minus_delta_x, x); INFO("[time] Applying Dirichlet BCs took %g s.", time_dirichlet.elapsed()); if (!sys.isLinear() && _convergence_criterion->hasResidualCheck()) @@ -230,8 +230,7 @@ bool NonlinearSolver<NonlinearSolverTag::Newton>::solve( // cf. // http://www.mcs.anl.gov/petsc/petsc-current/docs/manualpages/Vec/VecWAXPY.html auto& x_new = - NumLib::GlobalVectorProvider::provider.getVector( - x, _x_new_id); + NumLib::GlobalVectorProvider::provider.getVector(x, _x_new_id); LinAlg::axpy(x_new, -_damping, minus_delta_x); if (postIterationCallback) @@ -252,16 +251,14 @@ bool NonlinearSolver<NonlinearSolverTag::Newton>::solve( "iteration" " has to be repeated."); // TODO introduce some onDestroy hook. - NumLib::GlobalVectorProvider::provider - .releaseVector(x_new); + NumLib::GlobalVectorProvider::provider.releaseVector(x_new); continue; // That throws the iteration result away. } // TODO could be done via swap. Note: that also requires swapping // the ids. Same for the Picard scheme. LinAlg::copy(x_new, x); // copy new solution to x - NumLib::GlobalVectorProvider::provider.releaseVector( - x_new); + NumLib::GlobalVectorProvider::provider.releaseVector(x_new); } if (!iteration_succeeded) diff --git a/NumLib/ODESolver/NonlinearSystem.h b/NumLib/ODESolver/NonlinearSystem.h index 91269cb7151948a6d1006ce9919977e880651880..b3bf35c59a6a99be5ab17ef1d0f69226b62f9efc 100644 --- a/NumLib/ODESolver/NonlinearSystem.h +++ b/NumLib/ODESolver/NonlinearSystem.h @@ -59,7 +59,8 @@ public: //! Apply known solutions to the linearized equation system //! \f$ \mathit{Jac} \cdot (-\Delta x) = \mathit{res} \f$. virtual void applyKnownSolutionsNewton(GlobalMatrix& Jac, GlobalVector& res, - GlobalVector& minus_delta_x) = 0; + GlobalVector& minus_delta_x, + GlobalVector& x) = 0; }; /*! A System of nonlinear equations to be solved with the Picard fixpoint diff --git a/NumLib/ODESolver/ODESystem.h b/NumLib/ODESolver/ODESystem.h index 8d16dd2b235dc9848eb252feddbef614267eaada..64005e7206fbdf952cf05678ab4030cd3974b0d7 100644 --- a/NumLib/ODESolver/ODESystem.h +++ b/NumLib/ODESolver/ODESystem.h @@ -48,18 +48,19 @@ public: virtual void preAssemble(const double t, GlobalVector const& x) = 0; //! Assemble \c M, \c K and \c b at the provided state (\c t, \c x). - virtual void assemble( - const double t, GlobalVector const& x, GlobalMatrix& M, GlobalMatrix& K, - GlobalVector& b) = 0; + virtual void assemble(const double t, GlobalVector const& x, + GlobalMatrix& M, GlobalMatrix& K, + GlobalVector& b) = 0; using Index = MathLib::MatrixVectorTraits<GlobalMatrix>::Index; //! Provides known solutions (Dirichlet boundary conditions) vector for //! the ode system at the given time \c t. virtual std::vector<NumLib::IndexValueVector<Index>> const* - getKnownSolutions(double const t) const + getKnownSolutions(double const t, GlobalVector const& x) const { (void)t; + (void)x; return nullptr; // by default there are no known solutions } }; @@ -76,7 +77,6 @@ class ODESystem<ODESystemTag::FirstOrderImplicitQuasilinear, NonlinearSolverTag::Picard> { public: - //! Calls process' pre-assembly with the provided state (\c t, \c x). virtual void preAssemble(const double t, GlobalVector const& x) = 0; @@ -124,11 +124,12 @@ public: * \mathtt{Jac} \f$. * \endparblock */ - virtual void assembleWithJacobian( - const double t, GlobalVector const& x, GlobalVector const& xdot, - const double dxdot_dx, const double dx_dx, GlobalMatrix& M, - GlobalMatrix& K, GlobalVector& b, GlobalMatrix& Jac) = 0; + virtual void assembleWithJacobian(const double t, GlobalVector const& x, + GlobalVector const& xdot, + const double dxdot_dx, const double dx_dx, + GlobalMatrix& M, GlobalMatrix& K, + GlobalVector& b, GlobalMatrix& Jac) = 0; }; //! @} -} +} // namespace NumLib diff --git a/NumLib/ODESolver/TimeDiscretizedODESystem.cpp b/NumLib/ODESolver/TimeDiscretizedODESystem.cpp index 5c597a88e5f55e557feb2cf37291a4d7d642ae02..617c722a65fe4f41124d7b7712fcf457b35bb1e7 100644 --- a/NumLib/ODESolver/TimeDiscretizedODESystem.cpp +++ b/NumLib/ODESolver/TimeDiscretizedODESystem.cpp @@ -34,14 +34,14 @@ void applyKnownSolutions(std::vector<Solutions> const* const known_solutions, } MathLib::LinAlg::finalizeAssembly(x); } -} +} // namespace detail namespace NumLib { TimeDiscretizedODESystem<ODESystemTag::FirstOrderImplicitQuasilinear, NonlinearSolverTag::Newton>:: - TimeDiscretizedODESystem(const int process_id, - ODE& ode, TimeDisc& time_discretization) + TimeDiscretizedODESystem(const int process_id, ODE& ode, + TimeDisc& time_discretization) : _ode(ode), _time_disc(time_discretization), _mat_trans(createMatrixTranslator<ODETag>(time_discretization)) @@ -66,9 +66,9 @@ TimeDiscretizedODESystem< NumLib::GlobalVectorProvider::provider.releaseVector(*_b); } -void TimeDiscretizedODESystem<ODESystemTag::FirstOrderImplicitQuasilinear, - NonlinearSolverTag::Newton>:: - assemble(const GlobalVector& x_new_timestep) +void TimeDiscretizedODESystem< + ODESystemTag::FirstOrderImplicitQuasilinear, + NonlinearSolverTag::Newton>::assemble(const GlobalVector& x_new_timestep) { namespace LinAlg = MathLib::LinAlg; @@ -125,16 +125,16 @@ void TimeDiscretizedODESystem< NonlinearSolverTag::Newton>::applyKnownSolutions(GlobalVector& x) const { ::detail::applyKnownSolutions( - _ode.getKnownSolutions(_time_disc.getCurrentTime()), x); + _ode.getKnownSolutions(_time_disc.getCurrentTime(), x), x); } void TimeDiscretizedODESystem<ODESystemTag::FirstOrderImplicitQuasilinear, NonlinearSolverTag::Newton>:: applyKnownSolutionsNewton(GlobalMatrix& Jac, GlobalVector& res, - GlobalVector& minus_delta_x) + GlobalVector& minus_delta_x, GlobalVector& x) { auto const* known_solutions = - _ode.getKnownSolutions(_time_disc.getCurrentTime()); + _ode.getKnownSolutions(_time_disc.getCurrentTime(), x); if (!known_solutions || known_solutions->empty()) return; @@ -149,6 +149,8 @@ void TimeDiscretizedODESystem<ODESystemTag::FirstOrderImplicitQuasilinear, // For the Newton method the values must be zero std::vector<double> values(ids.size(), 0); MathLib::applyKnownSolution(Jac, res, minus_delta_x, ids, values); + + ::detail::applyKnownSolutions(known_solutions, x); } TimeDiscretizedODESystem<ODESystemTag::FirstOrderImplicitQuasilinear, @@ -176,9 +178,9 @@ TimeDiscretizedODESystem< NumLib::GlobalVectorProvider::provider.releaseVector(*_b); } -void TimeDiscretizedODESystem<ODESystemTag::FirstOrderImplicitQuasilinear, - NonlinearSolverTag::Picard>:: - assemble(const GlobalVector& x_new_timestep) +void TimeDiscretizedODESystem< + ODESystemTag::FirstOrderImplicitQuasilinear, + NonlinearSolverTag::Picard>::assemble(const GlobalVector& x_new_timestep) { namespace LinAlg = MathLib::LinAlg; @@ -202,7 +204,7 @@ void TimeDiscretizedODESystem< NonlinearSolverTag::Picard>::applyKnownSolutions(GlobalVector& x) const { ::detail::applyKnownSolutions( - _ode.getKnownSolutions(_time_disc.getCurrentTime()), x); + _ode.getKnownSolutions(_time_disc.getCurrentTime(), x), x); } void TimeDiscretizedODESystem< @@ -212,7 +214,7 @@ void TimeDiscretizedODESystem< GlobalVector& x) { auto const* known_solutions = - _ode.getKnownSolutions(_time_disc.getCurrentTime()); + _ode.getKnownSolutions(_time_disc.getCurrentTime(), x); if (known_solutions) { @@ -229,4 +231,4 @@ void TimeDiscretizedODESystem< } } -} // NumLib +} // namespace NumLib diff --git a/NumLib/ODESolver/TimeDiscretizedODESystem.h b/NumLib/ODESolver/TimeDiscretizedODESystem.h index 30e8ab1948be14bf453a3963ead8c43a36a792f1..59ead507782483e9247ee37c7fd2d91f13e20287 100644 --- a/NumLib/ODESolver/TimeDiscretizedODESystem.h +++ b/NumLib/ODESolver/TimeDiscretizedODESystem.h @@ -92,7 +92,8 @@ public: void applyKnownSolutions(GlobalVector& x) const override; void applyKnownSolutionsNewton(GlobalMatrix& Jac, GlobalVector& res, - GlobalVector& minus_delta_x) override; + GlobalVector& minus_delta_x, + GlobalVector& x) override; bool isLinear() const override { diff --git a/ProcessLib/BoundaryCondition/BoundaryCondition.h b/ProcessLib/BoundaryCondition/BoundaryCondition.h index f20c2ced8db05f9f4f5fd2565e2ba9fd8d6308fd..298609592a53a09b3e72a9911de394f81f7fbd20 100644 --- a/ProcessLib/BoundaryCondition/BoundaryCondition.h +++ b/ProcessLib/BoundaryCondition/BoundaryCondition.h @@ -35,7 +35,8 @@ public: //! Applies natural BCs (i.e. non-Dirichlet BCs) to the stiffness matrix //! \c K and the vector \c b. virtual void applyNaturalBC(const double /*t*/, GlobalVector const& /*x*/, - GlobalMatrix& /*K*/, GlobalVector& /*b*/) + GlobalMatrix& /*K*/, GlobalVector& /*b*/, + GlobalMatrix* /*Jac*/) { // By default it is assumed that the BC is not a natural BC. Therefore // there is nothing to do here. @@ -43,7 +44,7 @@ public: //! Writes the values of essential BCs to \c bc_values. virtual void getEssentialBCValues( - const double /*t*/, + const double /*t*/, GlobalVector const& /*x*/, NumLib::IndexValueVector<GlobalIndexType>& /*bc_values*/) const { // By default it is assumed that the BC is not an essential BC. diff --git a/ProcessLib/BoundaryCondition/BoundaryConditionCollection.cpp b/ProcessLib/BoundaryCondition/BoundaryConditionCollection.cpp index 63293a99ab7efe5da2acb545b45f7fd6c6549afc..f902b7b35ef11acf9ca40287a2ce6e4414de5ef0 100644 --- a/ProcessLib/BoundaryCondition/BoundaryConditionCollection.cpp +++ b/ProcessLib/BoundaryCondition/BoundaryConditionCollection.cpp @@ -14,10 +14,11 @@ namespace ProcessLib void BoundaryConditionCollection::applyNaturalBC(const double t, GlobalVector const& x, GlobalMatrix& K, - GlobalVector& b) + GlobalVector& b, + GlobalMatrix* Jac) { for (auto const& bc : _boundary_conditions) - bc->applyNaturalBC(t, x, K, b); + bc->applyNaturalBC(t, x, K, b, Jac); } void BoundaryConditionCollection::addBCsForProcessVariables( @@ -43,4 +44,4 @@ void BoundaryConditionCollection::addBCsForProcessVariables( // object if needed. _dirichlet_bcs.resize(_boundary_conditions.size()); } -} +} // namespace ProcessLib diff --git a/ProcessLib/BoundaryCondition/BoundaryConditionCollection.h b/ProcessLib/BoundaryCondition/BoundaryConditionCollection.h index 26adc6ae3ee1f781a0047f06ae10aea9f5bc2330..3f4af9810ebe01ae24fa564999af34619e7e183e 100644 --- a/ProcessLib/BoundaryCondition/BoundaryConditionCollection.h +++ b/ProcessLib/BoundaryCondition/BoundaryConditionCollection.h @@ -26,16 +26,16 @@ public: } void applyNaturalBC(const double t, GlobalVector const& x, GlobalMatrix& K, - GlobalVector& b); + GlobalVector& b, GlobalMatrix* Jac); std::vector<NumLib::IndexValueVector<GlobalIndexType>> const* - getKnownSolutions(double const t) const + getKnownSolutions(double const t, GlobalVector const& x) const { auto const n_bcs = _boundary_conditions.size(); for (std::size_t i=0; i<n_bcs; ++i) { auto const& bc = *_boundary_conditions[i]; auto& dirichlet_storage = _dirichlet_bcs[i]; - bc.getEssentialBCValues(t, dirichlet_storage); + bc.getEssentialBCValues(t, x, dirichlet_storage); } return &_dirichlet_bcs; } diff --git a/ProcessLib/BoundaryCondition/BoundaryConditionConfig.h b/ProcessLib/BoundaryCondition/BoundaryConditionConfig.h index f1f7091ea0d7e07a6e5b97de0ad3b1ce5d7e0e3a..4a537eca95272c791cd86df6ec972babba4ee576 100644 --- a/ProcessLib/BoundaryCondition/BoundaryConditionConfig.h +++ b/ProcessLib/BoundaryCondition/BoundaryConditionConfig.h @@ -19,19 +19,21 @@ struct BoundaryConditionConfig final BoundaryConditionConfig(BaseLib::ConfigTree&& config_, MeshLib::Mesh const& mesh_, boost::optional<int> const component_id_) - : config(std::move(config_)), mesh(mesh_), component_id(component_id_) + : config(std::move(config_)), + boundary_mesh(mesh_), + component_id(component_id_) { } BoundaryConditionConfig(BoundaryConditionConfig&& other) : config(std::move(other.config)), - mesh(other.mesh), + boundary_mesh(other.boundary_mesh), component_id(other.component_id) { } BaseLib::ConfigTree config; - MeshLib::Mesh const& mesh; + MeshLib::Mesh const& boundary_mesh; boost::optional<int> const component_id; }; diff --git a/ProcessLib/BoundaryCondition/ConstraintDirichletBoundaryCondition.cpp b/ProcessLib/BoundaryCondition/ConstraintDirichletBoundaryCondition.cpp index acce3726620c45b75644b929f8c681063e7d7717..33567c6e25e44812d3e77c65358d1685b02d91c2 100644 --- a/ProcessLib/BoundaryCondition/ConstraintDirichletBoundaryCondition.cpp +++ b/ProcessLib/BoundaryCondition/ConstraintDirichletBoundaryCondition.cpp @@ -139,7 +139,8 @@ void ConstraintDirichletBoundaryCondition::preTimestep(double t, } void ConstraintDirichletBoundaryCondition::getEssentialBCValues( - const double t, NumLib::IndexValueVector<GlobalIndexType>& bc_values) const + const double t, const GlobalVector& /*x*/, + NumLib::IndexValueVector<GlobalIndexType>& bc_values) const { SpatialPosition pos; diff --git a/ProcessLib/BoundaryCondition/ConstraintDirichletBoundaryCondition.h b/ProcessLib/BoundaryCondition/ConstraintDirichletBoundaryCondition.h index 253abfceb7fddd23c46f41ad64de4d8e63e7b1f8..04d3b4095d6906f3ecb0b7d30441e6df668ad92a 100644 --- a/ProcessLib/BoundaryCondition/ConstraintDirichletBoundaryCondition.h +++ b/ProcessLib/BoundaryCondition/ConstraintDirichletBoundaryCondition.h @@ -63,7 +63,7 @@ public: void preTimestep(double t, GlobalVector const& x) override; void getEssentialBCValues( - const double t, + const double t, const GlobalVector& x, NumLib::IndexValueVector<GlobalIndexType>& bc_values) const override; private: diff --git a/ProcessLib/BoundaryCondition/CreateBoundaryCondition.cpp b/ProcessLib/BoundaryCondition/CreateBoundaryCondition.cpp index 35948a821937ebded3d48920295f9fbc5355a08d..fb9d8e9f091c66ae932fe6f7d2cb82623507bd01 100644 --- a/ProcessLib/BoundaryCondition/CreateBoundaryCondition.cpp +++ b/ProcessLib/BoundaryCondition/CreateBoundaryCondition.cpp @@ -19,14 +19,17 @@ #include "NormalTractionBoundaryCondition.h" #include "PhaseFieldIrreversibleDamageOracleBoundaryCondition.h" #include "RobinBoundaryCondition.h" +#ifdef OGS_USE_PYTHON +#include "Python/PythonBoundaryCondition.h" +#endif namespace ProcessLib { std::unique_ptr<BoundaryCondition> createBoundaryCondition( const BoundaryConditionConfig& config, - const NumLib::LocalToGlobalIndexMap& dof_table, const MeshLib::Mesh& mesh, - const int variable_id, const unsigned integration_order, - const unsigned shapefunction_order, + const NumLib::LocalToGlobalIndexMap& dof_table, + const MeshLib::Mesh& bulk_mesh, const int variable_id, + const unsigned integration_order, const unsigned shapefunction_order, const std::vector<std::unique_ptr<ProcessLib::ParameterBase>>& parameters, const Process& process) { @@ -36,33 +39,47 @@ std::unique_ptr<BoundaryCondition> createBoundaryCondition( if (type == "Dirichlet") { return ProcessLib::createDirichletBoundaryCondition( - config.config, config.mesh, dof_table, mesh.getID(), variable_id, - *config.component_id, parameters); + config.config, config.boundary_mesh, dof_table, bulk_mesh.getID(), + variable_id, *config.component_id, parameters); } if (type == "Neumann") { return ProcessLib::createNeumannBoundaryCondition( - config.config, config.mesh, dof_table, variable_id, - *config.component_id, mesh.isAxiallySymmetric(), integration_order, - shapefunction_order, mesh.getDimension(), parameters); + config.config, config.boundary_mesh, dof_table, variable_id, + *config.component_id, bulk_mesh.isAxiallySymmetric(), + integration_order, shapefunction_order, bulk_mesh.getDimension(), + parameters); } if (type == "Robin") { return ProcessLib::createRobinBoundaryCondition( - config.config, config.mesh, dof_table, variable_id, - *config.component_id, mesh.isAxiallySymmetric(), integration_order, - shapefunction_order, mesh.getDimension(), parameters); + config.config, config.boundary_mesh, dof_table, variable_id, + *config.component_id, bulk_mesh.isAxiallySymmetric(), + integration_order, shapefunction_order, bulk_mesh.getDimension(), + parameters); } if (type == "NonuniformDirichlet") { return ProcessLib::createNonuniformDirichletBoundaryCondition( - config.config, dof_table, variable_id, *config.component_id, mesh); + config.config, dof_table, variable_id, *config.component_id, + bulk_mesh); } if (type == "NonuniformNeumann") { return ProcessLib::createNonuniformNeumannBoundaryCondition( config.config, dof_table, variable_id, *config.component_id, - integration_order, shapefunction_order, mesh); + integration_order, shapefunction_order, bulk_mesh); + } + if (type == "Python") + { +#ifdef OGS_USE_PYTHON + return ProcessLib::createPythonBoundaryCondition( + config.config, config.boundary_mesh, dof_table, bulk_mesh.getID(), + variable_id, *config.component_id, integration_order, + shapefunction_order, bulk_mesh.getDimension()); +#else + OGS_FATAL("OpenGeoSys has not been built with Python support."); +#endif } // @@ -71,22 +88,22 @@ std::unique_ptr<BoundaryCondition> createBoundaryCondition( if (type == "ConstraintDirichlet") { return createConstraintDirichletBoundaryCondition( - config.config, config.mesh, dof_table, variable_id, + config.config, config.boundary_mesh, dof_table, variable_id, integration_order, *config.component_id, parameters, process); } if (type == "NormalTraction") { return ProcessLib::NormalTractionBoundaryCondition:: createNormalTractionBoundaryCondition( - config.config, config.mesh, dof_table, variable_id, - mesh.isAxiallySymmetric(), integration_order, - shapefunction_order, mesh.getDimension(), parameters); + config.config, config.boundary_mesh, dof_table, variable_id, + bulk_mesh.isAxiallySymmetric(), integration_order, + shapefunction_order, bulk_mesh.getDimension(), parameters); } if (type == "PhaseFieldIrreversibleDamageOracleBoundaryCondition") { return ProcessLib:: createPhaseFieldIrreversibleDamageOracleBoundaryCondition( - config.config, dof_table, mesh, variable_id, + config.config, dof_table, bulk_mesh, variable_id, *config.component_id); } OGS_FATAL("Unknown boundary condition type: `%s'.", type.c_str()); diff --git a/ProcessLib/BoundaryCondition/CreateBoundaryCondition.h b/ProcessLib/BoundaryCondition/CreateBoundaryCondition.h index 97478a17d2ccef2d057d9778ffbe13827df352af..683951c8b89d300d6ea31526fcf336043fc17e3d 100644 --- a/ProcessLib/BoundaryCondition/CreateBoundaryCondition.h +++ b/ProcessLib/BoundaryCondition/CreateBoundaryCondition.h @@ -31,9 +31,9 @@ class Process; std::unique_ptr<BoundaryCondition> createBoundaryCondition( const BoundaryConditionConfig& config, - const NumLib::LocalToGlobalIndexMap& dof_table, const MeshLib::Mesh& mesh, - const int variable_id, const unsigned integration_order, - const unsigned shapefunction_order, + const NumLib::LocalToGlobalIndexMap& dof_table, + const MeshLib::Mesh& bulk_mesh, const int variable_id, + const unsigned integration_order, const unsigned shapefunction_order, const std::vector<std::unique_ptr<ProcessLib::ParameterBase>>& parameters, const Process& process); diff --git a/ProcessLib/BoundaryCondition/DirichletBoundaryCondition.cpp b/ProcessLib/BoundaryCondition/DirichletBoundaryCondition.cpp index a81977cdc17c99b7ca976249843cd510cdf8b6f3..38a7e6d08c23e77390792e9855f37e2348c72aac 100644 --- a/ProcessLib/BoundaryCondition/DirichletBoundaryCondition.cpp +++ b/ProcessLib/BoundaryCondition/DirichletBoundaryCondition.cpp @@ -10,14 +10,15 @@ #include "DirichletBoundaryCondition.h" #include <algorithm> -#include <vector> #include <logog/include/logog.hpp> +#include <vector> #include "ProcessLib/Utils/ProcessUtils.h" namespace ProcessLib { void DirichletBoundaryCondition::getEssentialBCValues( - const double t, NumLib::IndexValueVector<GlobalIndexType>& bc_values) const + const double t, GlobalVector const& /*x*/, + NumLib::IndexValueVector<GlobalIndexType>& bc_values) const { SpatialPosition pos; @@ -48,7 +49,8 @@ void DirichletBoundaryCondition::getEssentialBCValues( // and MatZeroRowsColumns, which are called to apply the Dirichlet BC, // the negative index is not accepted like other matrix or vector // PETSc routines. Therefore, the following if-condition is applied. - if (g_idx >= 0) { + if (g_idx >= 0) + { bc_values.ids.emplace_back(g_idx); bc_values.values.emplace_back(_parameter(t, pos).front()); } diff --git a/ProcessLib/BoundaryCondition/DirichletBoundaryCondition.h b/ProcessLib/BoundaryCondition/DirichletBoundaryCondition.h index 721ea5d3ad1d3d4daa67782a1386c2e8720de25a..f00681518cfe05cd57d18b8f6ed1f3e1c7c6ec41 100644 --- a/ProcessLib/BoundaryCondition/DirichletBoundaryCondition.h +++ b/ProcessLib/BoundaryCondition/DirichletBoundaryCondition.h @@ -58,7 +58,7 @@ public: } void getEssentialBCValues( - const double t, + const double t, GlobalVector const& x, NumLib::IndexValueVector<GlobalIndexType>& bc_values) const override; private: diff --git a/ProcessLib/BoundaryCondition/GenericNaturalBoundaryCondition-impl.h b/ProcessLib/BoundaryCondition/GenericNaturalBoundaryCondition-impl.h index b164ed2b8c0f9bb12760060be1aa709f9322b2b2..4da13051714b8404c9380b8f25c46baf3e280c4c 100644 --- a/ProcessLib/BoundaryCondition/GenericNaturalBoundaryCondition-impl.h +++ b/ProcessLib/BoundaryCondition/GenericNaturalBoundaryCondition-impl.h @@ -70,11 +70,12 @@ void GenericNaturalBoundaryCondition< LocalAssemblerImplementation>::applyNaturalBC(const double t, const GlobalVector& x, GlobalMatrix& K, - GlobalVector& b) + GlobalVector& b, + GlobalMatrix* Jac) { GlobalExecutor::executeMemberOnDereferenced( &GenericNaturalBoundaryConditionLocalAssemblerInterface::assemble, - _local_assemblers, *_dof_table_boundary, t, x, K, b); + _local_assemblers, *_dof_table_boundary, t, x, K, b, Jac); } } // ProcessLib diff --git a/ProcessLib/BoundaryCondition/GenericNaturalBoundaryCondition.h b/ProcessLib/BoundaryCondition/GenericNaturalBoundaryCondition.h index f68304dc2f85a46f3d314637f961cf812d25d9f8..90b0d164f9ade9ccea7a830c2359d6803c5dbf62 100644 --- a/ProcessLib/BoundaryCondition/GenericNaturalBoundaryCondition.h +++ b/ProcessLib/BoundaryCondition/GenericNaturalBoundaryCondition.h @@ -9,8 +9,8 @@ #pragma once -#include "MeshLib/MeshSubset.h" #include "BoundaryCondition.h" +#include "MeshLib/MeshSubset.h" namespace ProcessLib { @@ -38,10 +38,8 @@ public: /// Calls local assemblers which calculate their contributions to the global /// matrix and the right-hand-side. - void applyNaturalBC(const double t, - GlobalVector const& x, - GlobalMatrix& K, - GlobalVector& b) override; + void applyNaturalBC(const double t, GlobalVector const& x, GlobalMatrix& K, + GlobalVector& b, GlobalMatrix* Jac) override; private: /// Data used in the assembly of the specific boundary condition. @@ -63,6 +61,6 @@ private: _local_assemblers; }; -} // ProcessLib +} // namespace ProcessLib #include "GenericNaturalBoundaryCondition-impl.h" diff --git a/ProcessLib/BoundaryCondition/GenericNaturalBoundaryConditionLocalAssembler.h b/ProcessLib/BoundaryCondition/GenericNaturalBoundaryConditionLocalAssembler.h index 219085cfc062f1d2a36ed66822919d96fc5f4128..1721ef2589d6f2632c401fce3fda976076932bc0 100644 --- a/ProcessLib/BoundaryCondition/GenericNaturalBoundaryConditionLocalAssembler.h +++ b/ProcessLib/BoundaryCondition/GenericNaturalBoundaryConditionLocalAssembler.h @@ -23,7 +23,8 @@ public: virtual void assemble( std::size_t const id, NumLib::LocalToGlobalIndexMap const& dof_table_boundary, double const t, - const GlobalVector& x, GlobalMatrix& K, GlobalVector& b) = 0; + const GlobalVector& x, GlobalMatrix& K, GlobalVector& b, + GlobalMatrix* Jac) = 0; }; template <typename ShapeFunction, typename IntegrationMethod, @@ -55,4 +56,4 @@ protected: _shape_matrices; }; -} // ProcessLib +} // namespace ProcessLib diff --git a/ProcessLib/BoundaryCondition/GenericNonuniformNaturalBoundaryCondition-impl.h b/ProcessLib/BoundaryCondition/GenericNonuniformNaturalBoundaryCondition-impl.h index a7b50d8957aa117cd9f5483c814f4947151d8775..bf3e122f78bd1fa56384a69dc6b8e4ddf8d6a45a 100644 --- a/ProcessLib/BoundaryCondition/GenericNonuniformNaturalBoundaryCondition-impl.h +++ b/ProcessLib/BoundaryCondition/GenericNonuniformNaturalBoundaryCondition-impl.h @@ -89,12 +89,13 @@ void GenericNonuniformNaturalBoundaryCondition< LocalAssemblerImplementation>::applyNaturalBC(const double t, const GlobalVector& x, GlobalMatrix& K, - GlobalVector& b) + GlobalVector& b, + GlobalMatrix* Jac) { GlobalExecutor::executeMemberOnDereferenced( &GenericNonuniformNaturalBoundaryConditionLocalAssemblerInterface:: assemble, - _local_assemblers, *_dof_table_boundary, t, x, K, b); + _local_assemblers, *_dof_table_boundary, t, x, K, b, Jac); } -} // ProcessLib +} // namespace ProcessLib diff --git a/ProcessLib/BoundaryCondition/GenericNonuniformNaturalBoundaryCondition.h b/ProcessLib/BoundaryCondition/GenericNonuniformNaturalBoundaryCondition.h index d6753d967192bde3de7360f2cb11c9db3b74e56c..37249a52c5a904032091b7f21a64d75602997445 100644 --- a/ProcessLib/BoundaryCondition/GenericNonuniformNaturalBoundaryCondition.h +++ b/ProcessLib/BoundaryCondition/GenericNonuniformNaturalBoundaryCondition.h @@ -33,10 +33,8 @@ public: /// Calls local assemblers which calculate their contributions to the global /// matrix and the right-hand-side. - void applyNaturalBC(const double t, - GlobalVector const& x, - GlobalMatrix& K, - GlobalVector& b) override; + void applyNaturalBC(const double t, GlobalVector const& x, GlobalMatrix& K, + GlobalVector& b, GlobalMatrix* Jac) override; private: void constructDofTable(); diff --git a/ProcessLib/BoundaryCondition/GenericNonuniformNaturalBoundaryConditionLocalAssembler.h b/ProcessLib/BoundaryCondition/GenericNonuniformNaturalBoundaryConditionLocalAssembler.h index 72929d11a5e3fb97a71905bb7c08464627d09230..3cf49814d088bfa59c0da7b0b6c4665d72293ddf 100644 --- a/ProcessLib/BoundaryCondition/GenericNonuniformNaturalBoundaryConditionLocalAssembler.h +++ b/ProcessLib/BoundaryCondition/GenericNonuniformNaturalBoundaryConditionLocalAssembler.h @@ -24,7 +24,8 @@ public: virtual void assemble( std::size_t const id, NumLib::LocalToGlobalIndexMap const& dof_table_boundary, double const t, - const GlobalVector& x, GlobalMatrix& K, GlobalVector& b) = 0; + const GlobalVector& x, GlobalMatrix& K, GlobalVector& b, + GlobalMatrix* Jac) = 0; }; template <typename ShapeFunction, typename IntegrationMethod, @@ -88,4 +89,4 @@ protected: _ns_and_weights; }; -} // ProcessLib +} // namespace ProcessLib diff --git a/ProcessLib/BoundaryCondition/NeumannBoundaryConditionLocalAssembler.h b/ProcessLib/BoundaryCondition/NeumannBoundaryConditionLocalAssembler.h index cface4f4cdb2dfa365e8caccaa5e57ea4e10d080..a88661d70cd8166e41f262d197d3fa9d788e203e 100644 --- a/ProcessLib/BoundaryCondition/NeumannBoundaryConditionLocalAssembler.h +++ b/ProcessLib/BoundaryCondition/NeumannBoundaryConditionLocalAssembler.h @@ -9,9 +9,9 @@ #pragma once +#include "GenericNaturalBoundaryConditionLocalAssembler.h" #include "NumLib/DOF/DOFTableUtil.h" #include "ProcessLib/Parameter/Parameter.h" -#include "GenericNaturalBoundaryConditionLocalAssembler.h" namespace ProcessLib { @@ -42,7 +42,8 @@ public: void assemble(std::size_t const id, NumLib::LocalToGlobalIndexMap const& dof_table_boundary, double const t, const GlobalVector& /*x*/, - GlobalMatrix& /*K*/, GlobalVector& b) override + GlobalMatrix& /*K*/, GlobalVector& b, + GlobalMatrix* /*Jac*/) override { _local_rhs.setZero(); @@ -74,4 +75,4 @@ public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW; }; -} // namespace ProcessLib +} // namespace ProcessLib diff --git a/ProcessLib/BoundaryCondition/NonuniformDirichletBoundaryCondition.h b/ProcessLib/BoundaryCondition/NonuniformDirichletBoundaryCondition.h index 693993742036af5cce389794849d4931638f251c..49ae3aacf7ee8206f61547129c18eece09e785ab 100644 --- a/ProcessLib/BoundaryCondition/NonuniformDirichletBoundaryCondition.h +++ b/ProcessLib/BoundaryCondition/NonuniformDirichletBoundaryCondition.h @@ -60,11 +60,9 @@ public: } void getEssentialBCValues( - const double /*t*/, + const double /*t*/, GlobalVector const& /*x*/, NumLib::IndexValueVector<GlobalIndexType>& bc_values) const override { - SpatialPosition pos; - bc_values.ids.clear(); bc_values.values.clear(); diff --git a/ProcessLib/BoundaryCondition/NonuniformNeumannBoundaryConditionLocalAssembler.h b/ProcessLib/BoundaryCondition/NonuniformNeumannBoundaryConditionLocalAssembler.h index d995082e418215755cbd3a3d3e1e46631eb19339..3666afdcd39006e608152b6ecf6d9d5bcfbfadc7 100644 --- a/ProcessLib/BoundaryCondition/NonuniformNeumannBoundaryConditionLocalAssembler.h +++ b/ProcessLib/BoundaryCondition/NonuniformNeumannBoundaryConditionLocalAssembler.h @@ -57,7 +57,8 @@ public: void assemble(std::size_t const id, NumLib::LocalToGlobalIndexMap const& dof_table_boundary, double const /*t*/, const GlobalVector& /*x*/, - GlobalMatrix& /*K*/, GlobalVector& b) override + GlobalMatrix& /*K*/, GlobalVector& b, + GlobalMatrix* /*Jac*/) override { _local_rhs.setZero(); diff --git a/ProcessLib/BoundaryCondition/NormalTractionBoundaryCondition-impl.h b/ProcessLib/BoundaryCondition/NormalTractionBoundaryCondition-impl.h index c0c26e40d9987eaeac43adf617884de960498eed..ab5fad4556ba652be1e860f72df549005efaac1f 100644 --- a/ProcessLib/BoundaryCondition/NormalTractionBoundaryCondition-impl.h +++ b/ProcessLib/BoundaryCondition/NormalTractionBoundaryCondition-impl.h @@ -65,11 +65,12 @@ void NormalTractionBoundaryCondition< LocalAssemblerImplementation>::applyNaturalBC(const double t, const GlobalVector& x, GlobalMatrix& K, - GlobalVector& b) + GlobalVector& b, + GlobalMatrix* Jac) { GlobalExecutor::executeMemberOnDereferenced( &NormalTractionBoundaryConditionLocalAssemblerInterface::assemble, - _local_assemblers, *_dof_table_boundary, t, x, K, b); + _local_assemblers, *_dof_table_boundary, t, x, K, b, Jac); } std::unique_ptr<NormalTractionBoundaryCondition< @@ -98,4 +99,4 @@ createNormalTractionBoundaryCondition( } } // namespace NormalTractionBoundaryCondition -} // ProcessLib +} // namespace ProcessLib diff --git a/ProcessLib/BoundaryCondition/NormalTractionBoundaryCondition.h b/ProcessLib/BoundaryCondition/NormalTractionBoundaryCondition.h index 9f849aadc890b10fc0c6a523f5a6008d778eb761..14cb196809e7b29dd583e604364f9b1655f1213d 100644 --- a/ProcessLib/BoundaryCondition/NormalTractionBoundaryCondition.h +++ b/ProcessLib/BoundaryCondition/NormalTractionBoundaryCondition.h @@ -44,10 +44,8 @@ public: /// Calls local assemblers which calculate their contributions to the global /// matrix and the right-hand-side. - void applyNaturalBC(const double t, - GlobalVector const& x, - GlobalMatrix& K, - GlobalVector& b) override; + void applyNaturalBC(const double t, GlobalVector const& x, GlobalMatrix& K, + GlobalVector& b, GlobalMatrix* Jac) override; private: MeshLib::Mesh const& _bc_mesh; diff --git a/ProcessLib/BoundaryCondition/NormalTractionBoundaryConditionLocalAssembler.h b/ProcessLib/BoundaryCondition/NormalTractionBoundaryConditionLocalAssembler.h index e296fa1071d80b2852adfbbc4f6a750fd32ea4a4..cfe97bbc449dd8988a1fdf16e79ab8efae58ae1f 100644 --- a/ProcessLib/BoundaryCondition/NormalTractionBoundaryConditionLocalAssembler.h +++ b/ProcessLib/BoundaryCondition/NormalTractionBoundaryConditionLocalAssembler.h @@ -10,8 +10,8 @@ #pragma once -#include "MeshLib/Elements/FaceRule.h" #include "MathLib/LinAlg/Eigen/EigenMapTools.h" +#include "MeshLib/Elements/FaceRule.h" #include "NumLib/DOF/DOFTableUtil.h" #include "ProcessLib/Parameter/Parameter.h" @@ -45,7 +45,8 @@ public: virtual void assemble( std::size_t const id, NumLib::LocalToGlobalIndexMap const& dof_table_boundary, double const t, - const GlobalVector& /*x*/, GlobalMatrix& /*K*/, GlobalVector& b) = 0; + const GlobalVector& /*x*/, GlobalMatrix& /*K*/, GlobalVector& b, + GlobalMatrix* /*Jac*/) = 0; virtual ~NormalTractionBoundaryConditionLocalAssemblerInterface() = default; }; @@ -126,7 +127,8 @@ public: void assemble(std::size_t const id, NumLib::LocalToGlobalIndexMap const& dof_table_boundary, double const t, const GlobalVector& /*x*/, - GlobalMatrix& /*K*/, GlobalVector& local_rhs) + GlobalMatrix& /*K*/, GlobalVector& local_rhs, + GlobalMatrix* /*Jac*/) override { _local_rhs.setZero(); diff --git a/ProcessLib/BoundaryCondition/PhaseFieldIrreversibleDamageOracleBoundaryCondition.cpp b/ProcessLib/BoundaryCondition/PhaseFieldIrreversibleDamageOracleBoundaryCondition.cpp index bc72d5b47334e51860f4ee502c9b6b6797c7312e..c9c70288dba6c3fc4262a6b48e6012eca0e70910 100644 --- a/ProcessLib/BoundaryCondition/PhaseFieldIrreversibleDamageOracleBoundaryCondition.cpp +++ b/ProcessLib/BoundaryCondition/PhaseFieldIrreversibleDamageOracleBoundaryCondition.cpp @@ -17,7 +17,7 @@ namespace ProcessLib { void PhaseFieldIrreversibleDamageOracleBoundaryCondition::getEssentialBCValues( - const double /*t*/, + const double /*t*/, GlobalVector const& /*x*/, NumLib::IndexValueVector<GlobalIndexType>& bc_values) const { SpatialPosition pos; diff --git a/ProcessLib/BoundaryCondition/PhaseFieldIrreversibleDamageOracleBoundaryCondition.h b/ProcessLib/BoundaryCondition/PhaseFieldIrreversibleDamageOracleBoundaryCondition.h index 9afa9f2426c3ceeb3016736c3ea7f5538cf87788..c14b17452717727fa872ee37070568b68ebbd8b6 100644 --- a/ProcessLib/BoundaryCondition/PhaseFieldIrreversibleDamageOracleBoundaryCondition.h +++ b/ProcessLib/BoundaryCondition/PhaseFieldIrreversibleDamageOracleBoundaryCondition.h @@ -43,7 +43,7 @@ public: } void getEssentialBCValues( - const double t, + const double t, const GlobalVector& x, NumLib::IndexValueVector<GlobalIndexType>& bc_values) const override; void preTimestep(const double t, const GlobalVector& x) override; diff --git a/ProcessLib/BoundaryCondition/Python/CMakeLists.txt b/ProcessLib/BoundaryCondition/Python/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..30cfd3828bd182894f28f97c60a02133533b06c3 --- /dev/null +++ b/ProcessLib/BoundaryCondition/Python/CMakeLists.txt @@ -0,0 +1,22 @@ +add_library(ProcessLibBoundaryConditionPython + PythonBoundaryCondition.cpp + PythonBoundaryCondition.h + PythonBoundaryConditionLocalAssembler.h + PythonBoundaryConditionPythonSideInterface.h) + +target_compile_definitions(ProcessLibBoundaryConditionPython + PUBLIC OGS_USE_PYTHON) + +target_link_libraries(ProcessLibBoundaryConditionPython + PUBLIC BaseLib MathLib MeshLib NumLib logog + PRIVATE pybind11::pybind11) + +# For the embedded Python module +add_library(ProcessLibBoundaryConditionPythonModule + PythonBoundaryConditionModule.cpp + PythonBoundaryConditionModule.h) + +target_link_libraries(ProcessLibBoundaryConditionPythonModule + PUBLIC + ProcessLibBoundaryConditionPython + pybind11::pybind11) diff --git a/ProcessLib/BoundaryCondition/Python/PythonBoundaryCondition.cpp b/ProcessLib/BoundaryCondition/Python/PythonBoundaryCondition.cpp new file mode 100644 index 0000000000000000000000000000000000000000..90989956d84d7191530db60a334e0eeaa48a4158 --- /dev/null +++ b/ProcessLib/BoundaryCondition/Python/PythonBoundaryCondition.cpp @@ -0,0 +1,239 @@ +/** + * \copyright + * Copyright (c) 2012-2018, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + */ + +#include "PythonBoundaryCondition.h" + +#include <pybind11/pybind11.h> +#include <iostream> + +#include "MeshLib/MeshSearch/NodeSearch.h" +#include "ProcessLib/Utils/CreateLocalAssemblers.h" +#include "ProcessLib/Utils/ProcessUtils.h" +#include "PythonBoundaryConditionLocalAssembler.h" + +namespace +{ +//! Optionally flushes the standard output upon creation and destruction. +//! Can be used to improve the debug output readability when printing debug +//! messages both from OGS and from Python. +class FlushStdoutGuard +{ +public: + //! Optionally flushes C++ stdout before running Python code. + explicit FlushStdoutGuard(bool const flush) : _flush(flush) + { + if (!flush) + return; + + LOGOG_COUT << std::flush; + } + + //! Optionally flushes Python's stdout after running Python code. + ~FlushStdoutGuard() + { + if (!_flush) + return; + + using namespace pybind11::literals; + pybind11::print("end"_a = "", "flush"_a = true); + } + +private: + //! To flush or not to flush. + const bool _flush; +}; +} // anonymous namespace + +namespace ProcessLib +{ +PythonBoundaryCondition::PythonBoundaryCondition( + PythonBoundaryConditionData&& bc_data, + unsigned const integration_order, + unsigned const shapefunction_order, + unsigned const global_dim, + bool const flush_stdout) + : _bc_data(std::move(bc_data)), _flush_stdout(flush_stdout) +{ + std::vector<MeshLib::Node*> const& bc_nodes = + _bc_data.boundary_mesh.getNodes(); + MeshLib::MeshSubset bc_mesh_subset(_bc_data.boundary_mesh, bc_nodes); + + // Create local DOF table from the bc mesh subset for the given variable and + // component id. + _dof_table_boundary = _bc_data.dof_table_bulk.deriveBoundaryConstrainedMap( + std::move(bc_mesh_subset)); + + createLocalAssemblers<PythonBoundaryConditionLocalAssembler>( + global_dim, _bc_data.boundary_mesh.getElements(), *_dof_table_boundary, + shapefunction_order, _local_assemblers, + _bc_data.boundary_mesh.isAxiallySymmetric(), integration_order, + _bc_data); +} + +void PythonBoundaryCondition::getEssentialBCValues( + const double t, GlobalVector const& x, + NumLib::IndexValueVector<GlobalIndexType>& bc_values) const +{ + FlushStdoutGuard guard(_flush_stdout); + (void)guard; + + auto const nodes = _bc_data.boundary_mesh.getNodes(); + + auto const& bulk_node_ids_map = + *_bc_data.boundary_mesh.getProperties().getPropertyVector<std::size_t>( + "bulk_node_ids"); + + bc_values.ids.clear(); + bc_values.values.clear(); + + bc_values.ids.reserve(_bc_data.boundary_mesh.getNumberOfNodes()); + bc_values.values.reserve(_bc_data.boundary_mesh.getNumberOfNodes()); + + std::vector<double> primary_variables; + + for (auto const* node : _bc_data.boundary_mesh.getNodes()) + { + auto const boundary_node_id = node->getID(); + auto const bulk_node_id = bulk_node_ids_map[boundary_node_id]; + + // gather primary variable values + primary_variables.clear(); + auto const num_var = _dof_table_boundary->getNumberOfVariables(); + for (int var = 0; var < num_var; ++var) + { + auto const num_comp = + _dof_table_boundary->getNumberOfVariableComponents(var); + for (int comp = 0; comp < num_comp; ++comp) + { + MeshLib::Location loc{_bc_data.bulk_mesh_id, + MeshLib::MeshItemType::Node, + bulk_node_id}; + auto const dof_idx = + _bc_data.dof_table_bulk.getGlobalIndex(loc, var, comp); + + if (dof_idx == NumLib::MeshComponentMap::nop) + { + // TODO extend Python BC to mixed FEM ansatz functions + OGS_FATAL( + "No d.o.f. found for (node=%d, var=%d, comp=%d). " + "That might be due to the use of mixed FEM ansatz " + "functions, which is currently not supported by " + "the implementation of Python BCs. That excludes, " + "e.g., the HM process.", + bulk_node_id, var, comp); + } + + primary_variables.push_back(x[dof_idx]); + } + } + + auto* xs = nodes[boundary_node_id]->getCoords(); // TODO DDC problems? + auto pair_flag_value = _bc_data.bc_object->getDirichletBCValue( + t, {xs[0], xs[1], xs[2]}, boundary_node_id, primary_variables); + if (!_bc_data.bc_object->isOverriddenEssential()) + { + DBUG( + "Method `getDirichletBCValue' not overridden in Python " + "script."); + return; + } + + if (!pair_flag_value.first) + continue; + + MeshLib::Location l(_bc_data.bulk_mesh_id, MeshLib::MeshItemType::Node, + bulk_node_id); + const auto dof_idx = _bc_data.dof_table_bulk.getGlobalIndex( + l, _bc_data.global_component_id); + if (dof_idx == NumLib::MeshComponentMap::nop) + OGS_FATAL( + "Logic error. This error should already have occured while " + "gathering primary variables. Something nasty is going on!"); + + // For the DDC approach (e.g. with PETSc option), the negative + // index of g_idx means that the entry by that index is a ghost + // one, which should be dropped. Especially for PETSc routines + // MatZeroRows and MatZeroRowsColumns, which are called to apply + // the Dirichlet BC, the negative index is not accepted like + // other matrix or vector PETSc routines. Therefore, the + // following if-condition is applied. + if (dof_idx >= 0) + { + bc_values.ids.emplace_back(dof_idx); + bc_values.values.emplace_back(pair_flag_value.second); + } + } +} + +void PythonBoundaryCondition::applyNaturalBC(const double t, + const GlobalVector& x, + GlobalMatrix& K, GlobalVector& b, + GlobalMatrix* Jac) +{ + FlushStdoutGuard guard(_flush_stdout); + + try + { + GlobalExecutor::executeMemberOnDereferenced( + &GenericNaturalBoundaryConditionLocalAssemblerInterface::assemble, + _local_assemblers, *_dof_table_boundary, t, x, K, b, Jac); + } + catch (MethodNotOverriddenInDerivedClassException const& /*e*/) + { + DBUG("Method `getFlux' not overridden in Python script."); + } +} + +std::unique_ptr<PythonBoundaryCondition> createPythonBoundaryCondition( + BaseLib::ConfigTree const& config, MeshLib::Mesh const& boundary_mesh, + NumLib::LocalToGlobalIndexMap const& dof_table, std::size_t bulk_mesh_id, + int const variable_id, int const component_id, + unsigned const integration_order, unsigned const shapefunction_order, + unsigned const global_dim) +{ + //! \ogs_file_param{prj__process_variables__process_variable__boundary_conditions__boundary_condition__type} + config.checkConfigParameter("type", "Python"); + + //! \ogs_file_param{prj__process_variables__process_variable__boundary_conditions__boundary_condition__Python__bc_object} + auto const bc_object = config.getConfigParameter<std::string>("bc_object"); + //! \ogs_file_param{prj__process_variables__process_variable__boundary_conditions__boundary_condition__Python__flush_stdout} + auto const flush_stdout = config.getConfigParameter("flush_stdout", false); + + // Evaluate Python code in scope of main module + pybind11::object scope = + pybind11::module::import("__main__").attr("__dict__"); + + if (!scope.contains(bc_object)) + OGS_FATAL( + "Function `%s' is not defined in the python script file, or there " + "was no python script file specified.", + bc_object.c_str()); + + auto* bc = scope[bc_object.c_str()] + .cast<PythonBoundaryConditionPythonSideInterface*>(); + + if (variable_id >= static_cast<int>(dof_table.getNumberOfVariables()) || + component_id >= dof_table.getNumberOfVariableComponents(variable_id)) + { + OGS_FATAL( + "Variable id or component id too high. Actual values: (%d, %d), " + "maximum values: (%d, %d).", + variable_id, component_id, dof_table.getNumberOfVariables(), + dof_table.getNumberOfVariableComponents(variable_id)); + } + + return std::make_unique<PythonBoundaryCondition>( + PythonBoundaryConditionData{ + bc, dof_table, bulk_mesh_id, + dof_table.getGlobalComponent(variable_id, component_id), + boundary_mesh}, + integration_order, shapefunction_order, global_dim, flush_stdout); +} + +} // namespace ProcessLib diff --git a/ProcessLib/BoundaryCondition/Python/PythonBoundaryCondition.h b/ProcessLib/BoundaryCondition/Python/PythonBoundaryCondition.h new file mode 100644 index 0000000000000000000000000000000000000000..f3ef475ddeeea2050621c40125266b03738d3942 --- /dev/null +++ b/ProcessLib/BoundaryCondition/Python/PythonBoundaryCondition.h @@ -0,0 +1,85 @@ +/** + * \copyright + * Copyright (c) 2012-2018, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + */ + +#pragma once + +#include "NumLib/DOF/LocalToGlobalIndexMap.h" +#include "NumLib/IndexValueVector.h" +#include "ProcessLib/BoundaryCondition/BoundaryCondition.h" +#include "ProcessLib/BoundaryCondition/GenericNaturalBoundaryConditionLocalAssembler.h" + +#include "PythonBoundaryConditionPythonSideInterface.h" + +namespace ProcessLib +{ +//! Groups data used by essential and natural BCs, in particular by the +//! local assemblers of the latter. +struct PythonBoundaryConditionData +{ + //! Python object computing BC values. + PythonBoundaryConditionPythonSideInterface* bc_object; + + //! DOF table of the entire domain. + NumLib::LocalToGlobalIndexMap const& dof_table_bulk; + + //! Mesh ID of the entire domain. + std::size_t const bulk_mesh_id; + + //! Global component ID of the (variable, component) to which this BC is + //! applied. + int const global_component_id; + + //! The boundary mesh, i.e., the domain of this BC. + const MeshLib::Mesh& boundary_mesh; +}; + +//! A boundary condition whose values are computed by a Python script. +class PythonBoundaryCondition final : public BoundaryCondition +{ +public: + PythonBoundaryCondition(PythonBoundaryConditionData&& bc_data, + unsigned const integration_order, + unsigned const shapefunction_order, + unsigned const global_dim, + bool const flush_stdout); + + void getEssentialBCValues( + const double t, const GlobalVector& x, + NumLib::IndexValueVector<GlobalIndexType>& bc_values) const override; + + void applyNaturalBC(const double t, const GlobalVector& x, GlobalMatrix& K, + GlobalVector& b, GlobalMatrix* Jac) override; + +private: + //! Auxiliary data. + PythonBoundaryConditionData _bc_data; + + //! Local dof table for the boundary mesh. + std::unique_ptr<NumLib::LocalToGlobalIndexMap> _dof_table_boundary; + + //! Local assemblers for all elements of the boundary mesh. + std::vector< + std::unique_ptr<GenericNaturalBoundaryConditionLocalAssemblerInterface>> + _local_assemblers; + + //! Whether or not to flush standard output before and after each call to + //! Python code. Ensures right order of output messages and therefore + //! simplifies debugging. + bool const _flush_stdout; +}; + +//! Creates a new PythonBoundaryCondition object. +std::unique_ptr<PythonBoundaryCondition> createPythonBoundaryCondition( + BaseLib::ConfigTree const& config, MeshLib::Mesh const& boundary_mesh, + NumLib::LocalToGlobalIndexMap const& dof_table, std::size_t bulk_mesh_id, + int const variable_id, int const component_id, + unsigned const integration_order, unsigned const shapefunction_order, + unsigned const global_dim); + +} // namespace ProcessLib diff --git a/ProcessLib/BoundaryCondition/Python/PythonBoundaryConditionLocalAssembler.h b/ProcessLib/BoundaryCondition/Python/PythonBoundaryConditionLocalAssembler.h new file mode 100644 index 0000000000000000000000000000000000000000..56ba59325353e9384ccd994645e8a872d7e5a141 --- /dev/null +++ b/ProcessLib/BoundaryCondition/Python/PythonBoundaryConditionLocalAssembler.h @@ -0,0 +1,198 @@ +/** + * \copyright + * Copyright (c) 2012-2018, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + */ + +#pragma once + +#include "PythonBoundaryCondition.h" + +#include "MathLib/LinAlg/Eigen/EigenMapTools.h" +#include "NumLib/DOF/DOFTableUtil.h" +#include "ProcessLib/BoundaryCondition/GenericNaturalBoundaryConditionLocalAssembler.h" + +#include "PythonBoundaryConditionPythonSideInterface.h" + +namespace ProcessLib +{ +//! Can be thrown to indicate that a member function is not overridden in a +//! derived class (in particular, if a Python class inherits from a C++ class). +struct MethodNotOverriddenInDerivedClassException +{ +}; + +template <typename ShapeFunction, typename IntegrationMethod, + unsigned GlobalDim> +class PythonBoundaryConditionLocalAssembler final + : public GenericNaturalBoundaryConditionLocalAssembler< + ShapeFunction, IntegrationMethod, GlobalDim> +{ + using Base = GenericNaturalBoundaryConditionLocalAssembler< + ShapeFunction, IntegrationMethod, GlobalDim>; + +public: + PythonBoundaryConditionLocalAssembler( + MeshLib::Element const& e, + std::size_t const /*local_matrix_size*/, + bool is_axially_symmetric, + unsigned const integration_order, + PythonBoundaryConditionData const& data) + : Base(e, is_axially_symmetric, integration_order), + _data(data), + _element(e) + { + } + + void assemble(std::size_t const boundary_element_id, + NumLib::LocalToGlobalIndexMap const& dof_table_boundary, + double const t, const GlobalVector& x, GlobalMatrix& /*K*/, + GlobalVector& b, GlobalMatrix* Jac) override + { + using ShapeMatricesType = + ShapeMatrixPolicyType<ShapeFunction, GlobalDim>; + using FemType = + NumLib::TemplateIsoparametric<ShapeFunction, ShapeMatricesType>; + FemType fe(*static_cast<const typename ShapeFunction::MeshElement*>( + &_element)); + + unsigned const num_integration_points = + Base::_integration_method.getNumberOfPoints(); + auto const num_var = _data.dof_table_bulk.getNumberOfVariables(); + auto const num_nodes = _element.getNumberOfNodes(); + auto const num_comp_total = + _data.dof_table_bulk.getNumberOfComponents(); + + auto const& bulk_node_ids_map = + *_data.boundary_mesh.getProperties().getPropertyVector<std::size_t>( + "bulk_node_ids"); + + // gather primary variables + Eigen::MatrixXd primary_variables_mat(num_nodes, num_comp_total); + for (int var = 0; var < num_var; ++var) + { + auto const num_comp = + _data.dof_table_bulk.getNumberOfVariableComponents(var); + for (int comp = 0; comp < num_comp; ++comp) + { + auto const global_component = + dof_table_boundary.getGlobalComponent(var, comp); + + for (unsigned element_node_id = 0; element_node_id < num_nodes; + ++element_node_id) + { + auto const* const node = _element.getNode(element_node_id); + auto const boundary_node_id = node->getID(); + auto const bulk_node_id = + bulk_node_ids_map[boundary_node_id]; + MeshLib::Location loc{_data.bulk_mesh_id, + MeshLib::MeshItemType::Node, + bulk_node_id}; + auto const dof_idx = + _data.dof_table_bulk.getGlobalIndex(loc, var, comp); + if (dof_idx == NumLib::MeshComponentMap::nop) + { + // TODO extend Python BC to mixed FEM ansatz functions + OGS_FATAL( + "No d.o.f. found for (node=%d, var=%d, comp=%d). " + "That might be due to the use of mixed FEM ansatz " + "functions, which is currently not supported by " + "the implementation of Python BCs. That excludes, " + "e.g., the HM process.", + bulk_node_id, var, comp); + } + primary_variables_mat(element_node_id, global_component) = + x[dof_idx]; + } + } + } + + Eigen::VectorXd local_rhs = Eigen::VectorXd::Zero(num_nodes); + Eigen::MatrixXd local_Jac = + Eigen::MatrixXd::Zero(num_nodes, num_nodes * num_comp_total); + std::vector<double> prim_vars_data(num_comp_total); + auto prim_vars = MathLib::toVector(prim_vars_data); + + for (unsigned ip = 0; ip < num_integration_points; ip++) + { + auto const& sm = Base::_shape_matrices[ip]; + auto const coords = fe.interpolateCoordinates(sm.N); + prim_vars = + sm.N * + primary_variables_mat; // Assumption: all primary variables + // have same shape functions. + auto const flag_flux_dFlux = + _data.bc_object->getFlux(t, coords, prim_vars_data); + if (!_data.bc_object->isOverriddenNatural()) + { + // getFlux() is not overridden in Python, so we can skip the + // whole BC assembly (i.e., for all boundary elements). + throw MethodNotOverriddenInDerivedClassException{}; + } + + if (!std::get<0>(flag_flux_dFlux)) + { + // No flux value for this integration point. Skip assembly of + // the entire element. + return; + } + auto const flux = std::get<1>(flag_flux_dFlux); + auto const& dFlux = std::get<2>(flag_flux_dFlux); + + auto const& wp = Base::_integration_method.getWeightedPoint(ip); + auto const w = sm.detJ * wp.getWeight() * sm.integralMeasure; + local_rhs.noalias() += sm.N * (flux * w); + + if (static_cast<int>(dFlux.size()) != num_comp_total) + { + // This strict check is technically mandatory only if a Jacobian + // is assembled. However, it is done as a consistency check also + // for cases without Jacobian assembly. + OGS_FATAL( + "The Python BC must return the derivative of the flux " + "w.r.t. each primary variable. %d components expected. %d " + "components returned from Python.", + num_comp_total, dFlux.size()); + } + + if (Jac) + { + for (int comp = 0; comp < num_comp_total; ++comp) + { + auto const top = 0; + auto const left = comp * num_nodes; + auto const width = num_nodes; + auto const height = num_nodes; + // The assignement -= takes into account the sign convention + // of 1st-order in time ODE systems in OpenGeoSys. + local_Jac.block(top, left, width, height).noalias() -= + sm.N.transpose() * (dFlux[comp] * w) * sm.N; + } + } + } + + auto const& indices_specific_component = + dof_table_boundary(boundary_element_id, _data.global_component_id) + .rows; + b.add(indices_specific_component, local_rhs); + + if (Jac) + { + // only assemble a block of the Jacobian, not the whole local matrix + auto const indices_all_components = + NumLib::getIndices(boundary_element_id, dof_table_boundary); + MathLib::RowColumnIndices<GlobalIndexType> rci{ + indices_specific_component, indices_all_components}; + Jac->add(rci, local_Jac); + } + } + +private: + PythonBoundaryConditionData const& _data; + MeshLib::Element const& _element; +}; + +} // namespace ProcessLib diff --git a/ProcessLib/BoundaryCondition/Python/PythonBoundaryConditionModule.cpp b/ProcessLib/BoundaryCondition/Python/PythonBoundaryConditionModule.cpp new file mode 100644 index 0000000000000000000000000000000000000000..45fdfed80c31c225950446f50158fa9aac82a9ff --- /dev/null +++ b/ProcessLib/BoundaryCondition/Python/PythonBoundaryConditionModule.cpp @@ -0,0 +1,63 @@ +/** + * \copyright + * Copyright (c) 2012-2018, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + */ + +#include "PythonBoundaryConditionModule.h" + +#include <pybind11/stl.h> + +#include "PythonBoundaryConditionPythonSideInterface.h" + +namespace ProcessLib +{ +//! Trampoline class allowing methods of class +//! PythonBoundaryConditionPythonSideInterface to be overridden on the Python +//! side. Cf. https://pybind11.readthedocs.io/en/stable/advanced/classes.html +class PythonBoundaryConditionPythonSideInterfaceTrampoline + : public PythonBoundaryConditionPythonSideInterface +{ +public: + using PythonBoundaryConditionPythonSideInterface:: + PythonBoundaryConditionPythonSideInterface; + + std::pair<bool, double> getDirichletBCValue( + double t, std::array<double, 3> x, std::size_t node_id, + std::vector<double> const& primary_variables) const override + { + using Ret = std::pair<bool, double>; + PYBIND11_OVERLOAD(Ret, PythonBoundaryConditionPythonSideInterface, + getDirichletBCValue, t, x, node_id, + primary_variables); + } + + std::tuple<bool, double, std::vector<double>> getFlux( + double t, std::array<double, 3> x, + std::vector<double> const& primary_variables) const override + { + using Ret = std::tuple<bool, double, std::vector<double>>; + PYBIND11_OVERLOAD(Ret, PythonBoundaryConditionPythonSideInterface, + getFlux, t, x, primary_variables); + } +}; + +void pythonBindBoundaryCondition(pybind11::module& m) +{ + namespace py = pybind11; + + py::class_<PythonBoundaryConditionPythonSideInterface, + PythonBoundaryConditionPythonSideInterfaceTrampoline> + pybc(m, "BoundaryCondition"); + + pybc.def(py::init()); + + pybc.def("getDirichletBCValue", + &PythonBoundaryConditionPythonSideInterface::getDirichletBCValue); + pybc.def("getFlux", &PythonBoundaryConditionPythonSideInterface::getFlux); +} + +} // namespace ProcessLib diff --git a/ProcessLib/BoundaryCondition/Python/PythonBoundaryConditionModule.h b/ProcessLib/BoundaryCondition/Python/PythonBoundaryConditionModule.h new file mode 100644 index 0000000000000000000000000000000000000000..82c141a4bc3aa73734b85f315aeb0576874b6cea --- /dev/null +++ b/ProcessLib/BoundaryCondition/Python/PythonBoundaryConditionModule.h @@ -0,0 +1,18 @@ +/** + * \copyright + * Copyright (c) 2012-2018, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + */ + +#pragma once + +#include <pybind11/pybind11.h> + +namespace ProcessLib +{ +//! Creates Python bindings for the Python BC class. +void pythonBindBoundaryCondition(pybind11::module& m); +} // namespace ProcessLib diff --git a/ProcessLib/BoundaryCondition/Python/PythonBoundaryConditionPythonSideInterface.h b/ProcessLib/BoundaryCondition/Python/PythonBoundaryConditionPythonSideInterface.h new file mode 100644 index 0000000000000000000000000000000000000000..43ea47f400eebdf12c8f120c1697826e47cb937d --- /dev/null +++ b/ProcessLib/BoundaryCondition/Python/PythonBoundaryConditionPythonSideInterface.h @@ -0,0 +1,75 @@ +/** + * \copyright + * Copyright (c) 2012-2018, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + */ + +#pragma once + +namespace ProcessLib +{ +//! Base class for boundary conditions. +//! This class will get Python bindings and is intended to be to be derived in +//! Python. +class PythonBoundaryConditionPythonSideInterface +{ +public: + /*! + * Computes Dirichlet boundary condition values for the provided arguments + * (time, position of the node, node id, primary variables at the node). + * + * \return a pair (is_dirichlet, value) indicating if a Dirichlet BC shall + * be set at that node and (if so) the value of the Dirichlet BC at that + * node. + */ + virtual std::pair<bool, double> getDirichletBCValue( + double /*t*/, std::array<double, 3> /*x*/, std::size_t /*node_id*/, + std::vector<double> const& /*primary_variables*/) const + { + _overridden_essential = false; + return {false, std::numeric_limits<double>::quiet_NaN()}; + } + + /*! + * Computes the flux for the provided arguments (time, position, primary + * variables at that position). + * + * \return a pair (is_natural, flux, flux_jacobian) indicating if a natural + * BC shall be set at that position and (if so) the flux at that node and + * the derivative of the flux w.r.t. all primary variables. + */ + virtual std::tuple<bool, double, std::vector<double>> getFlux( + double /*t*/, + std::array<double, 3> /*x*/, + std::vector<double> const& /*primary_variables*/) const + { + _overridden_natural = false; + return std::tuple<bool, double, std::vector<double>>{ + false, std::numeric_limits<double>::quiet_NaN(), {}}; + } + + //! Tells if getDirichletBCValue() has been overridden in the derived class + //! in Python. + //! + //! \pre getDirichletBCValue() must already have been called + //! once. + bool isOverriddenEssential() const { return _overridden_essential; } + + //! Tells if getFlux() has been overridden in the derived class in Python. + //! + //! \pre getFlux() must already have been called once. + bool isOverriddenNatural() const { return _overridden_natural; } + + virtual ~PythonBoundaryConditionPythonSideInterface() = default; + +private: + //! Tells if getDirichletBCValue() has been overridden in the derived class + //! in Python. + mutable bool _overridden_essential = true; + //! Tells if getFlux() has been overridden in the derived class in Python. + mutable bool _overridden_natural = true; +}; +} // namespace ProcessLib diff --git a/ProcessLib/BoundaryCondition/RobinBoundaryConditionLocalAssembler.h b/ProcessLib/BoundaryCondition/RobinBoundaryConditionLocalAssembler.h index f3bdc6181ee504e003ce2cc2167b42aab914b438..1b780d40b04476d9d28ab1be117f40181e11a6bd 100644 --- a/ProcessLib/BoundaryCondition/RobinBoundaryConditionLocalAssembler.h +++ b/ProcessLib/BoundaryCondition/RobinBoundaryConditionLocalAssembler.h @@ -46,7 +46,7 @@ public: void assemble(std::size_t const id, NumLib::LocalToGlobalIndexMap const& dof_table_boundary, double const t, const GlobalVector& /*x*/, GlobalMatrix& K, - GlobalVector& b) override + GlobalVector& b, GlobalMatrix* /*Jac*/) override { _local_K.setZero(); _local_rhs.setZero(); diff --git a/ProcessLib/CMakeLists.txt b/ProcessLib/CMakeLists.txt index 71a28b6b06e69e8b8130ee4be412be452d0eba9b..7d432a84e61352c33031ac05c33735cf8a495afe 100644 --- a/ProcessLib/CMakeLists.txt +++ b/ProcessLib/CMakeLists.txt @@ -25,6 +25,12 @@ target_link_libraries(ProcessLib PUBLIC BaseLib MaterialLib MathLib MeshLib NumLib logog ) +if(OGS_USE_PYTHON) + add_subdirectory(BoundaryCondition/Python) + target_link_libraries(ProcessLib + PUBLIC ProcessLibBoundaryConditionPython) +endif() + if(OGS_INSITU) target_link_libraries(ProcessLib InSituLib) endif() diff --git a/ProcessLib/ComponentTransport/Tests.cmake b/ProcessLib/ComponentTransport/Tests.cmake index b1e59587d21004d65973a056503be1075c72233e..89070f85d7119bed5fb146d0ec8646c7934390bd 100644 --- a/ProcessLib/ComponentTransport/Tests.cmake +++ b/ProcessLib/ComponentTransport/Tests.cmake @@ -295,6 +295,69 @@ AddTest( elder_pcs_0_ts_80_t_21038400.000000_reference.vtu elder_pcs_0_ts_80_t_21038400.000000.vtu conc conc 1e-1 1e-5 ) +AddTest( + NAME LARGE_2D_ComponentTransport_ElderPython + PATH Parabolic/ComponentTransport/elder + EXECUTABLE ogs + EXECUTABLE_ARGS elder-python.prj + WRAPPER time + TESTER vtkdiff + REQUIREMENTS OGS_USE_PYTHON AND NOT OGS_USE_MPI + DIFF_DATA + elder_pcs_0_ts_0_t_0.000000_reference.vtu elder_python_pcs_0_ts_0_t_0.000000.vtu pressure pressure 1e-1 1e-5 + elder_pcs_0_ts_100_t_26298000.000000_reference.vtu elder_python_pcs_0_ts_100_t_26298000.000000.vtu pressure pressure 1e-1 1e-5 + elder_pcs_0_ts_120_t_31557600.000000_reference.vtu elder_python_pcs_0_ts_120_t_31557600.000000.vtu pressure pressure 1e-1 1e-5 + elder_pcs_0_ts_140_t_36817200.000000_reference.vtu elder_python_pcs_0_ts_140_t_36817200.000000.vtu pressure pressure 1e-1 1e-5 + elder_pcs_0_ts_160_t_42076800.000000_reference.vtu elder_python_pcs_0_ts_160_t_42076800.000000.vtu pressure pressure 1e-1 1e-5 + elder_pcs_0_ts_180_t_47336400.000000_reference.vtu elder_python_pcs_0_ts_180_t_47336400.000000.vtu pressure pressure 1e-1 1e-5 + elder_pcs_0_ts_200_t_52596000.000000_reference.vtu elder_python_pcs_0_ts_200_t_52596000.000000.vtu pressure pressure 1e-1 1e-5 + elder_pcs_0_ts_20_t_5259600.000000_reference.vtu elder_python_pcs_0_ts_20_t_5259600.000000.vtu pressure pressure 1e-1 1e-5 + elder_pcs_0_ts_220_t_57855600.000000_reference.vtu elder_python_pcs_0_ts_220_t_57855600.000000.vtu pressure pressure 1e-1 1e-5 + elder_pcs_0_ts_240_t_63115200.000000_reference.vtu elder_python_pcs_0_ts_240_t_63115200.000000.vtu pressure pressure 1e-1 1e-5 + elder_pcs_0_ts_260_t_68374800.000000_reference.vtu elder_python_pcs_0_ts_260_t_68374800.000000.vtu pressure pressure 1e-1 1e-5 + elder_pcs_0_ts_280_t_73634400.000000_reference.vtu elder_python_pcs_0_ts_280_t_73634400.000000.vtu pressure pressure 1e-1 1e-5 + elder_pcs_0_ts_300_t_78894000.000000_reference.vtu elder_python_pcs_0_ts_300_t_78894000.000000.vtu pressure pressure 1e-1 1e-5 + elder_pcs_0_ts_320_t_84153600.000000_reference.vtu elder_python_pcs_0_ts_320_t_84153600.000000.vtu pressure pressure 1e-1 1e-5 + elder_pcs_0_ts_340_t_89413200.000000_reference.vtu elder_python_pcs_0_ts_340_t_89413200.000000.vtu pressure pressure 1e-1 1e-5 + elder_pcs_0_ts_360_t_94672800.000000_reference.vtu elder_python_pcs_0_ts_360_t_94672800.000000.vtu pressure pressure 1e-1 1e-5 + elder_pcs_0_ts_380_t_99932400.000000_reference.vtu elder_python_pcs_0_ts_380_t_99932400.000000.vtu pressure pressure 1e-1 1e-5 + elder_pcs_0_ts_400_t_105192000.000000_reference.vtu elder_python_pcs_0_ts_400_t_105192000.000000.vtu pressure pressure 1e-1 1e-5 + elder_pcs_0_ts_40_t_10519200.000000_reference.vtu elder_python_pcs_0_ts_40_t_10519200.000000.vtu pressure pressure 1e-1 1e-5 + elder_pcs_0_ts_420_t_110451600.000000_reference.vtu elder_python_pcs_0_ts_420_t_110451600.000000.vtu pressure pressure 1e-1 1e-5 + elder_pcs_0_ts_440_t_115711200.000000_reference.vtu elder_python_pcs_0_ts_440_t_115711200.000000.vtu pressure pressure 1e-1 1e-5 + elder_pcs_0_ts_460_t_120970800.000000_reference.vtu elder_python_pcs_0_ts_460_t_120970800.000000.vtu pressure pressure 1e-1 1e-5 + elder_pcs_0_ts_480_t_126230400.000000_reference.vtu elder_python_pcs_0_ts_480_t_126230400.000000.vtu pressure pressure 1e-1 1e-5 + elder_pcs_0_ts_500_t_131490000.000000_reference.vtu elder_python_pcs_0_ts_500_t_131490000.000000.vtu pressure pressure 1e-1 1e-5 + elder_pcs_0_ts_60_t_15778800.000000_reference.vtu elder_python_pcs_0_ts_60_t_15778800.000000.vtu pressure pressure 1e-1 1e-5 + elder_pcs_0_ts_80_t_21038400.000000_reference.vtu elder_python_pcs_0_ts_80_t_21038400.000000.vtu pressure pressure 1e-1 1e-5 + elder_pcs_0_ts_0_t_0.000000_reference.vtu elder_python_pcs_0_ts_0_t_0.000000.vtu conc conc 1e-1 1e-5 + elder_pcs_0_ts_100_t_26298000.000000_reference.vtu elder_python_pcs_0_ts_100_t_26298000.000000.vtu conc conc 1e-1 1e-5 + elder_pcs_0_ts_120_t_31557600.000000_reference.vtu elder_python_pcs_0_ts_120_t_31557600.000000.vtu conc conc 1e-1 1e-5 + elder_pcs_0_ts_140_t_36817200.000000_reference.vtu elder_python_pcs_0_ts_140_t_36817200.000000.vtu conc conc 1e-1 1e-5 + elder_pcs_0_ts_160_t_42076800.000000_reference.vtu elder_python_pcs_0_ts_160_t_42076800.000000.vtu conc conc 1e-1 1e-5 + elder_pcs_0_ts_180_t_47336400.000000_reference.vtu elder_python_pcs_0_ts_180_t_47336400.000000.vtu conc conc 1e-1 1e-5 + elder_pcs_0_ts_200_t_52596000.000000_reference.vtu elder_python_pcs_0_ts_200_t_52596000.000000.vtu conc conc 1e-1 1e-5 + elder_pcs_0_ts_20_t_5259600.000000_reference.vtu elder_python_pcs_0_ts_20_t_5259600.000000.vtu conc conc 1e-1 1e-5 + elder_pcs_0_ts_220_t_57855600.000000_reference.vtu elder_python_pcs_0_ts_220_t_57855600.000000.vtu conc conc 1e-1 1e-5 + elder_pcs_0_ts_240_t_63115200.000000_reference.vtu elder_python_pcs_0_ts_240_t_63115200.000000.vtu conc conc 1e-1 1e-5 + elder_pcs_0_ts_260_t_68374800.000000_reference.vtu elder_python_pcs_0_ts_260_t_68374800.000000.vtu conc conc 1e-1 1e-5 + elder_pcs_0_ts_280_t_73634400.000000_reference.vtu elder_python_pcs_0_ts_280_t_73634400.000000.vtu conc conc 1e-1 1e-5 + elder_pcs_0_ts_300_t_78894000.000000_reference.vtu elder_python_pcs_0_ts_300_t_78894000.000000.vtu conc conc 1e-1 1e-5 + elder_pcs_0_ts_320_t_84153600.000000_reference.vtu elder_python_pcs_0_ts_320_t_84153600.000000.vtu conc conc 1e-1 1e-5 + elder_pcs_0_ts_340_t_89413200.000000_reference.vtu elder_python_pcs_0_ts_340_t_89413200.000000.vtu conc conc 1e-1 1e-5 + elder_pcs_0_ts_360_t_94672800.000000_reference.vtu elder_python_pcs_0_ts_360_t_94672800.000000.vtu conc conc 1e-1 1e-5 + elder_pcs_0_ts_380_t_99932400.000000_reference.vtu elder_python_pcs_0_ts_380_t_99932400.000000.vtu conc conc 1e-1 1e-5 + elder_pcs_0_ts_400_t_105192000.000000_reference.vtu elder_python_pcs_0_ts_400_t_105192000.000000.vtu conc conc 1e-1 1e-5 + elder_pcs_0_ts_40_t_10519200.000000_reference.vtu elder_python_pcs_0_ts_40_t_10519200.000000.vtu conc conc 1e-1 1e-5 + elder_pcs_0_ts_420_t_110451600.000000_reference.vtu elder_python_pcs_0_ts_420_t_110451600.000000.vtu conc conc 1e-1 1e-5 + elder_pcs_0_ts_440_t_115711200.000000_reference.vtu elder_python_pcs_0_ts_440_t_115711200.000000.vtu conc conc 1e-1 1e-5 + elder_pcs_0_ts_460_t_120970800.000000_reference.vtu elder_python_pcs_0_ts_460_t_120970800.000000.vtu conc conc 1e-1 1e-5 + elder_pcs_0_ts_480_t_126230400.000000_reference.vtu elder_python_pcs_0_ts_480_t_126230400.000000.vtu conc conc 1e-1 1e-5 + elder_pcs_0_ts_500_t_131490000.000000_reference.vtu elder_python_pcs_0_ts_500_t_131490000.000000.vtu conc conc 1e-1 1e-5 + elder_pcs_0_ts_60_t_15778800.000000_reference.vtu elder_python_pcs_0_ts_60_t_15778800.000000.vtu conc conc 1e-1 1e-5 + elder_pcs_0_ts_80_t_21038400.000000_reference.vtu elder_python_pcs_0_ts_80_t_21038400.000000.vtu conc conc 1e-1 1e-5 +) + AddTest( NAME 2D_ComponentTransport_HeterogeneousPermeability PATH Elliptic/square_100x100_ComponentTransport diff --git a/ProcessLib/Process.cpp b/ProcessLib/Process.cpp index 7debb42fcfbdc5fea92bdcaad9f7da432aa399b3..8e4ad70fe293efab5e160756b20911ab4b861ecd 100644 --- a/ProcessLib/Process.cpp +++ b/ProcessLib/Process.cpp @@ -187,7 +187,7 @@ void Process::assemble(const double t, GlobalVector const& x, GlobalMatrix& M, const auto pcs_id = (_coupled_solutions) != nullptr ? _coupled_solutions->process_id : 0; - _boundary_conditions[pcs_id].applyNaturalBC(t, x, K, b); + _boundary_conditions[pcs_id].applyNaturalBC(t, x, K, b, nullptr); _source_term_collections[pcs_id].integrateNodalSourceTerms(t, b); } @@ -207,7 +207,7 @@ void Process::assembleWithJacobian(const double t, GlobalVector const& x, // TODO: apply BCs to Jacobian. const auto pcs_id = (_coupled_solutions) != nullptr ? _coupled_solutions->process_id : 0; - _boundary_conditions[pcs_id].applyNaturalBC(t, x, K, b); + _boundary_conditions[pcs_id].applyNaturalBC(t, x, K, b, &Jac); } void Process::constructDofTable() diff --git a/ProcessLib/Process.h b/ProcessLib/Process.h index f4f9e86b641d76f3e7b9ad520eeb3b6b90a67e0c..b887d36dc2360751d5c0a0e9b9bc297f1be1e2d8 100644 --- a/ProcessLib/Process.h +++ b/ProcessLib/Process.h @@ -16,13 +16,13 @@ #include "NumLib/ODESolver/ODESystem.h" #include "NumLib/ODESolver/TimeDiscretization.h" #include "ProcessLib/BoundaryCondition/BoundaryConditionCollection.h" -#include "ProcessLib/SourceTerms/SourceTermCollection.h" #include "ProcessLib/Output/CachedSecondaryVariable.h" #include "ProcessLib/Output/ExtrapolatorData.h" #include "ProcessLib/Output/IntegrationPointWriter.h" #include "ProcessLib/Output/SecondaryVariable.h" #include "ProcessLib/Parameter/Parameter.h" #include "ProcessLib/SourceTerms/NodalSourceTerm.h" +#include "ProcessLib/SourceTerms/SourceTermCollection.h" #include "AbstractJacobianAssembler.h" #include "ProcessVariable.h" @@ -103,11 +103,11 @@ public: GlobalMatrix& Jac) final; std::vector<NumLib::IndexValueVector<GlobalIndexType>> const* - getKnownSolutions(double const t) const final + getKnownSolutions(double const t, GlobalVector const& x) const final { const auto pcs_id = (_coupled_solutions) ? _coupled_solutions->process_id : 0; - return _boundary_conditions[pcs_id].getKnownSolutions(t); + return _boundary_conditions[pcs_id].getKnownSolutions(t, x); } virtual NumLib::LocalToGlobalIndexMap const& getDOFTable( diff --git a/ProcessLib/SmallDeformation/Tests.cmake b/ProcessLib/SmallDeformation/Tests.cmake index 75970c97317dc22b627eb76b4aa71988b328ad07..704d03ba4abcd8d6a29ac9e87d4bf97d304ec2de 100644 --- a/ProcessLib/SmallDeformation/Tests.cmake +++ b/ProcessLib/SmallDeformation/Tests.cmake @@ -299,3 +299,20 @@ AddTest( expected_two_material_gravity.vtu two_material_gravity_pcs_0_ts_1_t_1.000000.vtu sigma sigma 5e-14 0 expected_two_material_gravity.vtu two_material_gravity_pcs_0_ts_1_t_1.000000.vtu epsilon epsilon 5e-14 0 ) + +AddTest( + NAME PythonBCSmallDeformationPiston + PATH Mechanics/Linear/PythonPiston + EXECUTABLE ogs + EXECUTABLE_ARGS piston.prj + WRAPPER time + TESTER vtkdiff + REQUIREMENTS OGS_USE_PYTHON AND NOT OGS_USE_MPI + DIFF_DATA + ref_piston_pcs_0_ts_5_t_5.000000.vtu piston_pcs_0_ts_5_t_5.000000.vtu displacement displacement 1e-16 0 + ref_piston_pcs_0_ts_5_t_5.000000.vtu piston_pcs_0_ts_5_t_5.000000.vtu epsilon epsilon 1e-14 0 + ref_piston_pcs_0_ts_5_t_5.000000.vtu piston_pcs_0_ts_5_t_5.000000.vtu sigma sigma 1e-8 0 + ref_piston_pcs_0_ts_10_t_10.000000.vtu piston_pcs_0_ts_10_t_10.000000.vtu displacement displacement 1e-16 0 + ref_piston_pcs_0_ts_10_t_10.000000.vtu piston_pcs_0_ts_10_t_10.000000.vtu epsilon epsilon 1e-13 0 + ref_piston_pcs_0_ts_10_t_10.000000.vtu piston_pcs_0_ts_10_t_10.000000.vtu sigma sigma 1e-7 0 +) diff --git a/Tests/Data/Elliptic/square_1x1_GroundWaterFlow_Python/bcs_laplace_eq.py b/Tests/Data/Elliptic/square_1x1_GroundWaterFlow_Python/bcs_laplace_eq.py new file mode 100644 index 0000000000000000000000000000000000000000..e6d5c8dd7d848239b73593327590af1705e8bc45 --- /dev/null +++ b/Tests/Data/Elliptic/square_1x1_GroundWaterFlow_Python/bcs_laplace_eq.py @@ -0,0 +1,52 @@ + +import OpenGeoSys +from math import pi, sin, cos, sinh, cosh + +a = 2.0*pi/3.0 + +# analytical solution used to set the Dirichlet BCs +def solution(x, y): + return sin(a*x) * sinh(a*y) + +# gradient of the analytical solution used to set the Neumann BCs +def grad_solution(x, y): + return a * cos(a*x) * sinh(a*y), \ + a * sin(a*x) * cosh(a*y) + +# Dirichlet BCs +class BCTop(OpenGeoSys.BoundaryCondition): + def getDirichletBCValue(self, t, coords, node_id, primary_vars): + x, y, z = coords + assert y == 1.0 and z == 0.0 + value = solution(x, y) + return (True, value) + +class BCLeft(OpenGeoSys.BoundaryCondition): + def getDirichletBCValue(self, t, coords, node_id, primary_vars): + x, y, z = coords + assert x == 0.0 and z == 0.0 + value = solution(x, y) + return (True, value) + +class BCBottom(OpenGeoSys.BoundaryCondition): + def getDirichletBCValue(self, t, coords, node_id, primary_vars): + x, y, z = coords + assert y == 0.0 and z == 0.0 + value = solution(x, y) + return (True, value) + +# Neumann BC +class BCRight(OpenGeoSys.BoundaryCondition): + def getFlux(self, t, coords, primary_vars): + x, y, z = coords + assert x == 1.0 and z == 0.0 + value = grad_solution(x, y)[0] + Jac = [ 0.0 ] # value does not depend on primary variable + return (True, value, Jac) + + +# instantiate BC objects referenced in OpenGeoSys' prj file +bc_top = BCTop() +bc_right = BCRight() +bc_bottom = BCBottom() +bc_left = BCLeft() diff --git a/Tests/Data/Elliptic/square_1x1_GroundWaterFlow_Python/python_laplace_eq_ref.vtu b/Tests/Data/Elliptic/square_1x1_GroundWaterFlow_Python/python_laplace_eq_ref.vtu new file mode 100644 index 0000000000000000000000000000000000000000..ca30c5172424cb4841837fb1c5b3d6c4275dab5b --- /dev/null +++ b/Tests/Data/Elliptic/square_1x1_GroundWaterFlow_Python/python_laplace_eq_ref.vtu @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5afedc3c451f400d58bdc8cefe7d5989bc1a248d80470131f750b2eb4793e49f +size 23684 diff --git a/Tests/Data/Elliptic/square_1x1_GroundWaterFlow_Python/square_1e3_laplace_eq.prj b/Tests/Data/Elliptic/square_1x1_GroundWaterFlow_Python/square_1e3_laplace_eq.prj new file mode 100644 index 0000000000000000000000000000000000000000..5e196baa339be5aa1f184de851a357748fc1dae7 --- /dev/null +++ b/Tests/Data/Elliptic/square_1x1_GroundWaterFlow_Python/square_1e3_laplace_eq.prj @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<OpenGeoSysProject> + <mesh>square_1x1_quad_1e3.vtu</mesh> + <geometry>square_1x1.gml</geometry> + <python_script>bcs_laplace_eq.py</python_script> + <processes> + <process> + <name>GW23</name> + <type>GROUNDWATER_FLOW</type> + <integration_order>2</integration_order> + <hydraulic_conductivity>K</hydraulic_conductivity> + <process_variables> + <process_variable>pressure</process_variable> + </process_variables> + <secondary_variables> + <secondary_variable type="static" internal_name="darcy_velocity" output_name="v"/> + </secondary_variables> + + <jacobian_assembler> + <type>CentralDifferences</type> + </jacobian_assembler> + </process> + </processes> + <time_loop> + <processes> + <process ref="GW23"> + <nonlinear_solver>basic_newton</nonlinear_solver> + <convergence_criterion> + <type>DeltaX</type> + <norm_type>NORM2</norm_type> + <abstol>1.e-6</abstol> + </convergence_criterion> + <time_discretization> + <type>BackwardEuler</type> + </time_discretization> + <output> + <variables> + <variable> pressure </variable> + <variable> v </variable> + </variables> + </output> + <time_stepping> + <type>SingleStep</type> + </time_stepping> + </process> + </processes> + <output> + <type>VTK</type> + <prefix>square_1e3_neumann</prefix> + </output> + </time_loop> + <parameters> + <parameter> + <name>K</name> + <type>Constant</type> + <value>1</value> + </parameter> + <parameter> + <name>zero</name> + <type>Constant</type> + <value>0</value> + </parameter> + </parameters> + <process_variables> + <process_variable> + <name>pressure</name> + <components>1</components> + <order>1</order> + <initial_condition>zero</initial_condition> + <boundary_conditions> + <boundary_condition> + <geometrical_set>square_1x1_geometry</geometrical_set> + <geometry>left</geometry> + <type>Python</type> + <bc_object>bc_left</bc_object> + </boundary_condition> + <boundary_condition> + <geometrical_set>square_1x1_geometry</geometrical_set> + <geometry>right</geometry> + <type>Python</type> + <bc_object>bc_right</bc_object> + </boundary_condition> + <boundary_condition> + <geometrical_set>square_1x1_geometry</geometrical_set> + <geometry>top</geometry> + <type>Python</type> + <bc_object>bc_top</bc_object> + </boundary_condition> + <boundary_condition> + <geometrical_set>square_1x1_geometry</geometrical_set> + <geometry>bottom</geometry> + <type>Python</type> + <bc_object>bc_bottom</bc_object> + </boundary_condition> + </boundary_conditions> + </process_variable> + </process_variables> + <nonlinear_solvers> + <nonlinear_solver> + <name>basic_newton</name> + <type>Newton</type> + <max_iter>10</max_iter> + <linear_solver>general_linear_solver</linear_solver> + </nonlinear_solver> + </nonlinear_solvers> + <linear_solvers> + <linear_solver> + <name>general_linear_solver</name> + <lis>-i cg -p jacobi -tol 1e-16 -maxiter 10000</lis> + <eigen> + <solver_type>CG</solver_type> + <precon_type>DIAGONAL</precon_type> + <max_iteration_step>10000</max_iteration_step> + <error_tolerance>1e-16</error_tolerance> + </eigen> + <petsc> + <prefix>gw</prefix> + <parameters>-gw_ksp_type cg -gw_pc_type bjacobi -gw_ksp_rtol 1e-16 -gw_ksp_max_it 10000</parameters> + </petsc> + </linear_solver> + </linear_solvers> +</OpenGeoSysProject> diff --git a/Tests/Data/Elliptic/square_1x1_GroundWaterFlow_Python/square_1x1.gml b/Tests/Data/Elliptic/square_1x1_GroundWaterFlow_Python/square_1x1.gml new file mode 100644 index 0000000000000000000000000000000000000000..3e5bb2a80a8f6bbdc4b7776b696d7a6af0d43b51 --- /dev/null +++ b/Tests/Data/Elliptic/square_1x1_GroundWaterFlow_Python/square_1x1.gml @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a3f52cd5a8058d967f8ead873e1afa323af39266e538bcd2c35af08683a81dad +size 1083 diff --git a/Tests/Data/Elliptic/square_1x1_GroundWaterFlow_Python/square_1x1_quad_1e3.vtu b/Tests/Data/Elliptic/square_1x1_GroundWaterFlow_Python/square_1x1_quad_1e3.vtu new file mode 100644 index 0000000000000000000000000000000000000000..b56982101f6be9ed1cb17945723023e95d9b9f98 --- /dev/null +++ b/Tests/Data/Elliptic/square_1x1_GroundWaterFlow_Python/square_1x1_quad_1e3.vtu @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3653db864dd917e8d5be84d68ee2de13ba3485b1ef23de4e9894fb6749a1aa11 +size 25692 diff --git a/Tests/Data/Mechanics/Linear/PythonPiston/chamber.py b/Tests/Data/Mechanics/Linear/PythonPiston/chamber.py new file mode 100644 index 0000000000000000000000000000000000000000..2fd26893826c383eaddfaa8f44cacb13e8fd4f22 --- /dev/null +++ b/Tests/Data/Mechanics/Linear/PythonPiston/chamber.py @@ -0,0 +1,19 @@ +# model of the ideal gas inside the chamber + +from math import pi + +R_piston = 0.05 + +h_chamber_0 = 0.1 +V_chamber_0 = pi * R_piston**2 * h_chamber_0 + +p_chamber_0 = 1e5 + + +def p_chamber(displacement_y): + V_chamber = pi * R_piston**2 * (h_chamber_0 + displacement_y) + return p_chamber_0 * V_chamber_0 / V_chamber + +def dp_chamber_du_y(displacement_y): + return -p_chamber_0 * V_chamber_0 / pi / R_piston**2 / \ + (h_chamber_0 + displacement_y)**2 diff --git a/Tests/Data/Mechanics/Linear/PythonPiston/piston.gml b/Tests/Data/Mechanics/Linear/PythonPiston/piston.gml new file mode 100644 index 0000000000000000000000000000000000000000..087abc71cf6e7f1679749c59c2d1cc29eb1fbd35 --- /dev/null +++ b/Tests/Data/Mechanics/Linear/PythonPiston/piston.gml @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:41db3c3cdde67b2ec37281d99840e3fa63ddf6a0cf1059c3be68126ab78fe199 +size 740 diff --git a/Tests/Data/Mechanics/Linear/PythonPiston/piston.prj b/Tests/Data/Mechanics/Linear/PythonPiston/piston.prj new file mode 100644 index 0000000000000000000000000000000000000000..474c0517076216aab1880c8a57abbe07653dd7a7 --- /dev/null +++ b/Tests/Data/Mechanics/Linear/PythonPiston/piston.prj @@ -0,0 +1,152 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<OpenGeoSysProject> + <mesh axially_symmetric="true">piston.vtu</mesh> + <geometry>piston.gml</geometry> + <python_script>piston_bc.py</python_script> + <processes> + <process> + <name>SD</name> + <type>SMALL_DEFORMATION</type> + <integration_order>2</integration_order> + <constitutive_relation> + <type>LinearElasticIsotropic</type> + <youngs_modulus>E</youngs_modulus> + <poissons_ratio>nu</poissons_ratio> + </constitutive_relation> + <solid_density>rho_sr</solid_density> + <specific_body_force>0 0</specific_body_force> + <process_variables> + <process_variable>displacement</process_variable> + </process_variables> + <secondary_variables> + <secondary_variable type="static" internal_name="sigma" output_name="sigma"/> + <secondary_variable type="static" internal_name="epsilon" output_name="epsilon"/> + </secondary_variables> + </process> + </processes> + <time_loop> + <processes> + <process ref="SD"> + <nonlinear_solver>basic_newton</nonlinear_solver> + <convergence_criterion> + <type>DeltaX</type> + <norm_type>INFINITY_N</norm_type> + <abstol>1e-8</abstol> + </convergence_criterion> + <time_discretization> + <type>BackwardEuler</type> + </time_discretization> + <output> + <variables> + <variable>displacement</variable> + <variable>sigma</variable> + <variable>epsilon</variable> + </variables> + </output> + <time_stepping> + <type>FixedTimeStepping</type> + <t_initial>0</t_initial> + <t_end>10</t_end> + <timesteps> + <pair> + <repeat>1</repeat> + <delta_t>1</delta_t> + </pair> + </timesteps> + </time_stepping> + </process> + </processes> + <output> + <type>VTK</type> + <prefix>piston</prefix> + <timesteps> + <pair> + <repeat>1</repeat> + <each_steps>1</each_steps> + </pair> + </timesteps> + <output_iteration_results>false</output_iteration_results> + </output> + </time_loop> + <parameters> + <parameter> + <name>E</name> + <type>Constant</type> + <value>1e6</value> + </parameter> + <parameter> + <name>nu</name> + <type>Constant</type> + <value>.3</value> + </parameter> + <parameter> + <name>rho_sr</name> + <type>Constant</type> + <value>1</value> + </parameter> + <parameter> + <name>displacement0</name> + <type>Constant</type> + <values>0 0</values> + </parameter> + <parameter> + <name>zero</name> + <type>Constant</type> + <value>0</value> + </parameter> + </parameters> + <process_variables> + <process_variable> + <name>displacement</name> + <components>2</components> + <order>1</order> + <initial_condition>displacement0</initial_condition> + <boundary_conditions> + <boundary_condition> + <geometrical_set>geom</geometrical_set> + <!-- The Python BC is set on the whole domain boundary. + It is determined in the Python script where the BC is set specifically. --> + <geometry>whole_domain_boundary</geometry> + <type>Python</type> + <component>0</component> + <bc_object>bc_x</bc_object> + <flush_stdout>false</flush_stdout> <!-- for debugging only --> + </boundary_condition> + <boundary_condition> + <geometrical_set>geom</geometrical_set> + <!-- The Python BC is set on the whole domain boundary. + It is determined in the Python script where the BC is set specifically. --> + <geometry>whole_domain_boundary</geometry> + <type>Python</type> + <component>1</component> + <bc_object>bc_y</bc_object> + <flush_stdout>false</flush_stdout> <!-- for debugging only --> + </boundary_condition> + </boundary_conditions> + </process_variable> + </process_variables> + <nonlinear_solvers> + <nonlinear_solver> + <name>basic_newton</name> + <type>Newton</type> + <max_iter>50</max_iter> + <linear_solver>general_linear_solver</linear_solver> + </nonlinear_solver> + </nonlinear_solvers> + <linear_solvers> + <linear_solver> + <name>general_linear_solver</name> + <lis>-i cg -p jacobi -tol 1e-16 -maxiter 10000</lis> + <eigen> + <solver_type>CG</solver_type> + <precon_type>DIAGONAL</precon_type> + <max_iteration_step>10000</max_iteration_step> + <error_tolerance>1e-16</error_tolerance> + </eigen> + <petsc> + <prefix>sd</prefix> + <parameters>-sd_ksp_type cg -sd_pc_type bjacobi -sd_ksp_rtol 1e-16 -sd_ksp_max_it 10000</parameters> + </petsc> + </linear_solver> + </linear_solvers> +</OpenGeoSysProject> diff --git a/Tests/Data/Mechanics/Linear/PythonPiston/piston.vtu b/Tests/Data/Mechanics/Linear/PythonPiston/piston.vtu new file mode 100644 index 0000000000000000000000000000000000000000..9613afb36e463881d9c2e448ffe7c85e7e13b8fa --- /dev/null +++ b/Tests/Data/Mechanics/Linear/PythonPiston/piston.vtu @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9f057b8c3eb5a6f8ed954a670e57cdb6c2c9b8e8acf48d7b545cea68b763b4d0 +size 45734 diff --git a/Tests/Data/Mechanics/Linear/PythonPiston/piston_bc.py b/Tests/Data/Mechanics/Linear/PythonPiston/piston_bc.py new file mode 100644 index 0000000000000000000000000000000000000000..e06fbf5023832ab57a0b5cb24264cb00383c7eb0 --- /dev/null +++ b/Tests/Data/Mechanics/Linear/PythonPiston/piston_bc.py @@ -0,0 +1,43 @@ +import OpenGeoSys +from chamber import * + + +class BC_X(OpenGeoSys.BoundaryCondition): + def getDirichletBCValue(self, t, coords, node_id, primary_vars): + x, y, z = coords + + if x == 0.0 or x == R_piston: + # no x displacement at outer boundary (x==R_piston) and center (x==0.0) + return (True, 0.0) + + return (False, 0.0) + + +class BC_Y(OpenGeoSys.BoundaryCondition): + def getDirichletBCValue(self, t, coords, node_id, primary_vars): + x, y, z = coords + + if y == 0.04: + # upper boundary -- prescribe displacement + return (True, -t * h_chamber_0 / 20.0) + + return (False, 0.0) + + + def getFlux(self, t, coords, primary_vars): + x, y, z = coords + + if y == 0.0: + # lower boundary -- the chamber + ux, uy = primary_vars + + p = p_chamber(uy) + Jac = [ 0.0, dp_chamber_du_y(uy) ] + return (True, p, Jac) + + return (False, 0.0, [ 0.0, 0.0 ]) + + +# instantiate the BC objects used by OpenGeoSys +bc_x = BC_X() +bc_y = BC_Y() diff --git a/Tests/Data/Mechanics/Linear/PythonPiston/post.py b/Tests/Data/Mechanics/Linear/PythonPiston/post.py new file mode 100755 index 0000000000000000000000000000000000000000..09695a52a3b1d042f740adc8c9567c47c6780073 --- /dev/null +++ b/Tests/Data/Mechanics/Linear/PythonPiston/post.py @@ -0,0 +1,99 @@ +#!/usr/bin/vtkpython + +from __future__ import print_function +import vtk +from vtk.util.numpy_support import vtk_to_numpy, numpy_to_vtk +import numpy as np +from scipy.interpolate import interp1d + +import matplotlib.pyplot as plt +import chamber as ch + +pvd_file = "out/piston_pcs_0.pvd" + + +### helpers ############################################## + +import os +try: + import xml.etree.cElementTree as ET +except: + import xml.etree.ElementTree as ET + + +def relpathfrom(origin, relpath): + if os.path.isabs(relpath): + return relpath + return os.path.join(origin, relpath) + +def read_pvd_file(fn): + try: + path = fn.name + except AttributeError: + path = fn + pathroot = os.path.dirname(path) + pvdtree = ET.parse(fn) + node = pvdtree.getroot() + if node.tag != "VTKFile": return None, None + children = list(node) + if len(children) != 1: return None, None + node = children[0] + if node.tag != "Collection": return None, None + + ts = [] + fs = [] + + for child in node: + if child.tag != "DataSet": return None, None + ts.append(float(child.get("timestep"))) + fs.append(relpathfrom(pathroot, child.get("file"))) + + return ts, fs + +### helpers end ########################################## + + +ts, fns = read_pvd_file(pvd_file) + +reader = vtk.vtkXMLUnstructuredGridReader() + + +loc_points = vtk.vtkPoints() +loc_points.InsertNextPoint([0.0, 0.0, 0.0]) +loc = vtk.vtkPolyData() +loc.SetPoints(loc_points) + +probe = vtk.vtkProbeFilter() +probe.SetSourceConnection(reader.GetOutputPort()) +probe.SetInputData(loc) + +uys = np.zeros(len(ts)) +ps = np.zeros(len(ts)) + +for i, (t, fn) in enumerate(zip(ts, fns)): + print("###### time", t) + reader.SetFileName(fn) + probe.Update() + + grid = probe.GetOutput() + uy = vtk_to_numpy(grid.GetPointData().GetArray("displacement"))[0,1] + p = vtk_to_numpy(grid.GetPointData().GetArray("sigma"))[0,1] + + uys[i] = uy + ps[i] = -p + +uys_ana = np.linspace(min(uys), max(uys), 100) +ps_ana = ch.p_chamber(uys_ana) + + +fig, ax = plt.subplots() +ax.scatter(uys[1:], ps[1:], label="ogs") +ax.plot(uys_ana, ps_ana, label="ref") +ax.legend() + +ax.set_ylabel("pressure inside the chamber $p$ / Pa") +ax.set_xlabel("displacement of the piston $u_y$ / m") + +fig.subplots_adjust(left=0.15, right=0.98) +fig.savefig("pressure-displacement.png") +plt.close(fig) diff --git a/Tests/Data/Mechanics/Linear/PythonPiston/ref_piston_pcs_0_ts_10_t_10.000000.vtu b/Tests/Data/Mechanics/Linear/PythonPiston/ref_piston_pcs_0_ts_10_t_10.000000.vtu new file mode 100644 index 0000000000000000000000000000000000000000..662010e3f2ff2b5f5ad5a7c7508367020f3e3264 --- /dev/null +++ b/Tests/Data/Mechanics/Linear/PythonPiston/ref_piston_pcs_0_ts_10_t_10.000000.vtu @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:03fcf36250d379fee936780f926f19d0b26a783a3ec30826fc7e80f2c48d957a +size 88956 diff --git a/Tests/Data/Mechanics/Linear/PythonPiston/ref_piston_pcs_0_ts_5_t_5.000000.vtu b/Tests/Data/Mechanics/Linear/PythonPiston/ref_piston_pcs_0_ts_5_t_5.000000.vtu new file mode 100644 index 0000000000000000000000000000000000000000..fe837edcca068f6307fdbf60467f4f144361a223 --- /dev/null +++ b/Tests/Data/Mechanics/Linear/PythonPiston/ref_piston_pcs_0_ts_5_t_5.000000.vtu @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d7319534828115a05183c89d15464714dd1ff0e3a29ae3dd4d861f763bae0a5a +size 88869 diff --git a/Tests/Data/Parabolic/ComponentTransport/elder/elder-python-bcs.py b/Tests/Data/Parabolic/ComponentTransport/elder/elder-python-bcs.py new file mode 100644 index 0000000000000000000000000000000000000000..3d815d21b7bc5486b97ffaf7c6e9bb7c147cf67d --- /dev/null +++ b/Tests/Data/Parabolic/ComponentTransport/elder/elder-python-bcs.py @@ -0,0 +1,34 @@ +import OpenGeoSys + + +class BCPressure(OpenGeoSys.BoundaryCondition): + def getDirichletBCValue(self, t, coords, node_id, primary_vars): + x, y, z = coords + + if x == -150 and z == 75: + # prescribe pressure of 0 + return (True, 0.0) + + # no Dirichlet BC + return (False, 0.0) + + +class BCConcentration(OpenGeoSys.BoundaryCondition): + def getDirichletBCValue(self, t, coords, node_id, primary_vars): + x, y, z = coords + + if z == -75: + # prescribe concentration of 0 + return (True, 0.0) + + if z == 75 and x >= 0: + # prescribe concentration of 1 + return (True, 1.0) + + # no Dirichlet BC + return (False, 0.0) + + +# instantiate the BC objects used by OpenGeoSys +bc_p = BCPressure() +bc_c = BCConcentration() diff --git a/Tests/Data/Parabolic/ComponentTransport/elder/elder-python.gml b/Tests/Data/Parabolic/ComponentTransport/elder/elder-python.gml new file mode 100644 index 0000000000000000000000000000000000000000..d22068fc2b35f4f29064dcf89bd42163dcc610b2 --- /dev/null +++ b/Tests/Data/Parabolic/ComponentTransport/elder/elder-python.gml @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b4fb7c7bc4ddbc37a38f1fdbaabc23eb415fe842ae2dbd4a5c9791fa7f93dc83 +size 1282 diff --git a/Tests/Data/Parabolic/ComponentTransport/elder/elder-python.prj b/Tests/Data/Parabolic/ComponentTransport/elder/elder-python.prj new file mode 100644 index 0000000000000000000000000000000000000000..2132e3beecfa76ea1d095e962cca295ae1b376a2 --- /dev/null +++ b/Tests/Data/Parabolic/ComponentTransport/elder/elder-python.prj @@ -0,0 +1,201 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!-- vim: ft=xml sw=2 +--> +<OpenGeoSysProject> + <mesh>elder.vtu</mesh> + <geometry>elder-python.gml</geometry> + <python_script>elder-python-bcs.py</python_script> + <processes> + <process> + <name>HC</name> + <type>ComponentTransport</type> + <integration_order>2</integration_order> + <process_variables> + <concentration>conc</concentration> + <pressure>pressure</pressure> + </process_variables> + <fluid> + <density> + <type>ConcentrationDependent</type> + <reference_density>1000</reference_density> + <reference_concentration>0</reference_concentration> + <fluid_density_difference_ratio>0.2</fluid_density_difference_ratio> + </density> + <viscosity> + <type>Constant</type> + <value>1.0e-3</value> + </viscosity> + </fluid> + <porous_medium> + <porous_medium id="0"> + <permeability> + <permeability_tensor_entries>kappa1</permeability_tensor_entries> + <type>Constant</type> + </permeability> + <porosity> + <type>Constant</type> + <porosity_parameter>constant_porosity_parameter</porosity_parameter> + </porosity> + <storage> + <type>Constant</type> + <value>0.0</value> + </storage> + </porous_medium> + </porous_medium> + <decay_rate>decay_rate</decay_rate> + <fluid_reference_density>rho_fluid</fluid_reference_density> + <retardation_factor>retardation_factor</retardation_factor> + <solute_dispersivity_longitudinal>alpha_l</solute_dispersivity_longitudinal> + <solute_dispersivity_transverse>alpha_t</solute_dispersivity_transverse> + <molecular_diffusion_coefficient>Dm</molecular_diffusion_coefficient> + <specific_body_force>0 0 -9.81</specific_body_force> + <secondary_variables> + <secondary_variable type="static" internal_name="darcy_velocity" output_name="darcy_velocity"/> + </secondary_variables> + </process> + </processes> + <time_loop> + <processes> + <process ref="HC"> + <nonlinear_solver>basic_picard</nonlinear_solver> + <convergence_criterion> + <type>PerComponentDeltaX</type> + <norm_type>NORM2</norm_type> + <reltols>1e-3 1e-3</reltols> + </convergence_criterion> + <time_discretization> + <type>BackwardEuler</type> + </time_discretization> + <time_stepping> + <type>FixedTimeStepping</type> + <t_initial> 0.0 </t_initial> + <t_end>1.3149e8</t_end> + <timesteps> + <pair> + <repeat>10</repeat> + <delta_t>262980</delta_t> + </pair> + </timesteps> + </time_stepping> + <output> + <variables> + <variable>conc</variable> + <variable>pressure</variable> + <variable>darcy_velocity</variable> + </variables> + </output> + </process> + </processes> + <output> + <type>VTK</type> + <prefix>elder_python</prefix> + <timesteps> + <pair> + <repeat>1</repeat> + <each_steps>20</each_steps> + </pair> + </timesteps> + </output> + </time_loop> + <parameters> + <parameter> + <name>rho_fluid</name> + <type>Constant</type> + <value>1000.0</value> + </parameter> + <parameter> + <name>alpha_l</name> + <type>Constant</type> + <value>0</value> + </parameter> + <parameter> + <name>alpha_t</name> + <type>Constant</type> + <value>0</value> + </parameter> + <parameter> + <name>Dm</name> + <type>Constant</type> + <value>3.57e-6</value> + </parameter> + <parameter> + <name>decay_rate</name> + <type>Constant</type> + <value>0.0</value> + </parameter> + <parameter> + <name>retardation_factor</name> + <type>Constant</type> + <value>1</value> + </parameter> + <parameter> + <name>p_ini</name> + <type>MeshNode</type> + <field_name>p_ini</field_name> + </parameter> + <parameter> + <name>c_ini</name> + <type>MeshNode</type> + <field_name>c_ini</field_name> + </parameter> + <parameter> + <name>constant_porosity_parameter</name> + <type>Constant</type> + <value>0.1</value> + </parameter> + <parameter> + <name>kappa1</name> + <type>Constant</type> + <values>4.84404e-13 0 0 0 4.84404e-13 0 0 0 4.84404e-13</values> + </parameter> + </parameters> + <process_variables> + <process_variable> + <name>conc</name> + <components>1</components> + <order>1</order> + <initial_condition>c_ini</initial_condition> + <boundary_conditions> + <boundary_condition> + <geometrical_set>elder</geometrical_set> + <geometry>whole_domain_boundary</geometry> + <type>Python</type> + <bc_object>bc_c</bc_object> + </boundary_condition> + </boundary_conditions> + </process_variable> + <process_variable> + <name>pressure</name> + <components>1</components> + <order>1</order> + <initial_condition>p_ini</initial_condition> + <boundary_conditions> + <boundary_condition> + <geometrical_set>elder</geometrical_set> + <geometry>whole_domain_boundary</geometry> + <type>Python</type> + <bc_object>bc_p</bc_object> + </boundary_condition> + </boundary_conditions> + </process_variable> + </process_variables> + <nonlinear_solvers> + <nonlinear_solver> + <name>basic_picard</name> + <type>Picard</type> + <max_iter>300</max_iter> + <linear_solver>general_linear_solver</linear_solver> + </nonlinear_solver> + </nonlinear_solvers> + <linear_solvers> + <linear_solver> + <name>general_linear_solver</name> + <eigen> + <precon_type>ILUT</precon_type> + <solver_type>BiCGSTAB</solver_type> + <max_iteration_step>5000</max_iteration_step> + <error_tolerance>1e-12</error_tolerance> + </eigen> + </linear_solver> + </linear_solvers> +</OpenGeoSysProject> diff --git a/ThirdParty/CMakeLists.txt b/ThirdParty/CMakeLists.txt index ea686f5ee2fa134513f1f41aa989f790cf94806b..2cdae58ab87fce49132f383bc37f7bda4aa3fea1 100644 --- a/ThirdParty/CMakeLists.txt +++ b/ThirdParty/CMakeLists.txt @@ -45,6 +45,37 @@ if(OGS_BUILD_SWMM) set_target_properties(SWMM SwmmInterface PROPERTIES COMPILE_FLAGS /W0) endif() +if(OGS_USE_PYTHON) + add_subdirectory(pybind11) + + function(check_python_version_compatibility) + if(NOT EXISTS ${VTK_DIR}/VTKConfig.cmake) + return() + endif() + + include(${VTK_DIR}/VTKConfig.cmake) + + if(NOT ${VTK_WRAP_PYTHON}) + return() + endif() + if(NOT EXISTS ${VTK_MODULES_DIR}/vtkPython.cmake) + return() + endif() + + include(${VTK_MODULES_DIR}/vtkPython.cmake) + + if (NOT "${vtkPython_LIBRARIES}" STREQUAL "${PYTHON_LIBRARIES}") + message(SEND_ERROR "Mismatch between VTK's and OpenGeoSys' Python " + "libraries: ${vtkPython_LIBRARIES} vs. ${PYTHON_LIBRARIES}. " + "This will lead to compilation or linking errors. " + "You can fix this error by using the same Python version for " + "OpenGeoSys as VTK is built with.") + endif() + endfunction() + + check_python_version_compatibility() +endif() + if(OGS_BUILD_UTILS) include(${PROJECT_SOURCE_DIR}/scripts/cmake/MetisSetup.cmake) endif() diff --git a/ThirdParty/pybind11 b/ThirdParty/pybind11 new file mode 160000 index 0000000000000000000000000000000000000000..8edc147d67ca85a93ed1f53628004528dc36a04d --- /dev/null +++ b/ThirdParty/pybind11 @@ -0,0 +1 @@ +Subproject commit 8edc147d67ca85a93ed1f53628004528dc36a04d diff --git a/netlify.toml b/netlify.toml index 87d97a136355f0b8fa7a8127a56a65762870985f..286b6ae44489035f8e6e2fbef1410a64ae108333 100644 --- a/netlify.toml +++ b/netlify.toml @@ -9,7 +9,7 @@ hugo""" [context.production.environment] - HUGO_VERSION = "0.42.1" + HUGO_VERSION = "0.47" HUGO_ENV = "production" HUGO_ENABLEGITINFO = "true" @@ -21,7 +21,7 @@ hugo -b $DEPLOY_PRIME_URL""" [context.deploy-preview.environment] - HUGO_VERSION = "0.42.1" + HUGO_VERSION = "0.47" [context.branch-deploy] command = """ @@ -32,4 +32,4 @@ node_modules/.bin/hugo-algolia --toml -s""" [context.branch-deploy.environment] - HUGO_VERSION = "0.42.1" + HUGO_VERSION = "0.47" diff --git a/scripts/cmake/SubmoduleSetup.cmake b/scripts/cmake/SubmoduleSetup.cmake index 09d8d084c1f9ae92197c939cbc2f69bab8629af3..a07dbb57ab1c8ee4c946f65b8a03fb3cc0c2e5a5 100644 --- a/scripts/cmake/SubmoduleSetup.cmake +++ b/scripts/cmake/SubmoduleSetup.cmake @@ -19,6 +19,9 @@ endif() if(OGS_BUILD_SWMM) list(APPEND REQUIRED_SUBMODULES ThirdParty/SwmmInterface) endif() +if(OGS_USE_PYTHON) + list(APPEND REQUIRED_SUBMODULES ThirdParty/pybind11) +endif() # Sync submodules, which is required when a submodule changed its URL if(OGS_SYNC_SUBMODULES) diff --git a/web/config.toml b/web/config.toml index 29f2e488c2674f2ed39c7f89404a7ca2353c2d8a..32f601f5c16f816740afcade65298e8130c9a630 100644 --- a/web/config.toml +++ b/web/config.toml @@ -93,6 +93,10 @@ staticDir = ["dist", "static"] name = "Two-phase Flow" identifier = "two-phase-flow" weight = 8 +[[menu.benchmarks]] + name = "Python Boundary Conditions" + identifier = "python-bc" + weight = 9 # Quickstart sidebar top-level categories [[menu.quickstart]] diff --git a/web/content/docs/benchmarks/python-bc/elder-benchmark/elder.md b/web/content/docs/benchmarks/python-bc/elder-benchmark/elder.md new file mode 100644 index 0000000000000000000000000000000000000000..04b5ba1dde5c0fe7fb7f3219e34caba7d9303309 --- /dev/null +++ b/web/content/docs/benchmarks/python-bc/elder-benchmark/elder.md @@ -0,0 +1,27 @@ ++++ +project = "Parabolic/ComponentTransport/elder/elder-python.prj" +author = "Christoph Lehmann" +date = "2018-08-16T09:18:00+02:00" +title = "Saturated Variable-Density Flow and Mass Transport (Elder) with Python BC" +weight = 3 + +[menu] + [menu.benchmarks] + parent = "python-bc" + ++++ + +{{< data-link >}} + +## Motivation of this test case + +The aim of this test is: + +* to show that it is possible to prescribe BCs only on parts of a given geometry +* to assert that Python BCs work with processes involving more than one physical + field. + +## Details + +This test is a copy of [this test case]({{< ref "../../hydro-component/elder.md" >}}). +Please check the original test case for any details. diff --git a/web/content/docs/benchmarks/python-bc/laplace-equation/python-laplace-eq.md b/web/content/docs/benchmarks/python-bc/laplace-equation/python-laplace-eq.md new file mode 100644 index 0000000000000000000000000000000000000000..0e68c1d6c6a1e218ced82b290e056b4c2fe0f5ba --- /dev/null +++ b/web/content/docs/benchmarks/python-bc/laplace-equation/python-laplace-eq.md @@ -0,0 +1,95 @@ ++++ +project = "https://github.com/ufz/ogs-data/blob/master/Elliptic/square_1x1_GroundWaterFlow_Python/square_1e3_laplace_eq.prj" +author = "Christoph Lehmann" +date = "2018-06-01T14:16:55+02:00" +title = "Manufactured Solution for Laplace's Equation with Python" +weight = 1 + +[menu] + [menu.benchmarks] + parent = "python-bc" + ++++ + +{{< data-link >}} + +## Motivation of this test case + +The aim of this test is: + +* to provide a simple introductory example of how Python BCs can be used +* to show that Python BCs can be used to easily prescribe BCs based on + analytical formulas +* to check whether both essential and natural BCs + are implemented correctly in OpenGeoSys' Python BC. + +## Problem description + +We solve Laplace's Equation in 2D on a $1 \times 1$ square domain. + The boundary conditions will be specified below. + +## Weak form + +Laplace's equation is +$$ +\begin{equation} +- \mathop{\mathrm{div}} (a \mathop{\mathrm{grad}} u) = 0 +\end{equation} +$$ +The weak form is derived as usual by multiplying with a test function $v$ and +integrating over the domain $\Omega$: +$$ +\begin{equation} +- \int\_{\Omega} v \mathop{\mathrm{div}} (a \mathop{\mathrm{grad}} u) \, \mathrm{d}\Omega = 0 +\,, +\end{equation} +$$ +which can be transformed further to +$$ +\begin{equation} +\int\_{\Omega} a \mathop{\mathrm{grad}} v \cdot \mathop{\mathrm{grad}} u \, \mathrm{d}\Omega = \int\_{\Omega} \mathop{\mathrm{div}} (v a \mathop{\mathrm{grad}} u) \, \mathrm{d}\Omega = \int\_{\Gamma\_{\mathrm{N}}} v a \mathop{\mathrm{grad}} u \cdot n \, \mathrm{d}\Gamma \,, +\end{equation} +$$ +where in the second equality Gauss's theorem has been applied. +As usual, the domain boundary $\partial\Omega = \Gamma\_{\mathrm{D}} \cup \Gamma\_{\mathrm{N}}$ is subdivided +into the dirichlet and the Neumann boundary and $v$ vanishes on +$\Gamma\_{\mathrm{D}}$. +The r.h.s. of the above equation is the total flux associated with $u$ flowing +**into** the domain $\Omega$ through $\Gamma\_{\mathrm{N}}$: +$-a \mathop{\mathrm{grad}} u$ is the flux density and $-n$ is the inwards directed surface +normal. + +The weak form just derived is implemented (after FEM discretization) in the +groundwater flow process in OpenGeoSys. +Note that for the application of Neumann boundary conditions, it is necessary to +know whether the flux has to be applied with a positive or a negative sign! + +## Analytial solution + +The coefficient $a$ of Laplace's equation is taken to be unity. +By differentiation it can be easily checked that +$$ +\begin{equation} +u(x, y) = \sin(bx) \sinh(by) +\end{equation} +$$ +solves Laplace's equation inside $\Omega$ for any $b$. +In this example we set $b = \tfrac 23 \pi$. + +As boundary conditions we apply Dirichlet BCs at the top, left and bottom of the +domain with values from $u(x,y)|\_{\Gamma\_{\mathrm{D}}}$. +On the right boundary of the domain a Neumann BC is applied. +There $n = (1, 0)$, which implies that $a \mathop{\mathrm{grad}} u \cdot n += a \, \partial u / \partial x$. + + +## Results + +The numerical result obtained from OpenGeoSys is: + +{{< img src="../python_laplace_eq_solution.png" >}} + +The absolute difference between the analytical and numerical solutions is +smaller than $4 \cdot 10^{-4}$: + +{{< img src="../python_laplace_eq_diff.png" >}} diff --git a/web/content/docs/benchmarks/python-bc/laplace-equation/python_laplace_eq_diff.png b/web/content/docs/benchmarks/python-bc/laplace-equation/python_laplace_eq_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..5c4d28f5451b43ec2d55f2ad98dc69388d216e75 --- /dev/null +++ b/web/content/docs/benchmarks/python-bc/laplace-equation/python_laplace_eq_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:37b86d2f172fa864d6bfecabf99bd203ddaded8210808956325b4a97b3020f25 +size 34437 diff --git a/web/content/docs/benchmarks/python-bc/laplace-equation/python_laplace_eq_solution.png b/web/content/docs/benchmarks/python-bc/laplace-equation/python_laplace_eq_solution.png new file mode 100644 index 0000000000000000000000000000000000000000..ad1bab5c5da68cb72014ef2e44fa6c08dea1a18e --- /dev/null +++ b/web/content/docs/benchmarks/python-bc/laplace-equation/python_laplace_eq_solution.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d6aa873d3c8cb0f6b4e7cbd30cf32c07d1ae261e313d503a15d6e505902626fc +size 39593 diff --git a/web/content/docs/benchmarks/python-bc/piston/load-steps.png b/web/content/docs/benchmarks/python-bc/piston/load-steps.png new file mode 100644 index 0000000000000000000000000000000000000000..c86f2e8257628ea21ca2785128da08f174346bd4 --- /dev/null +++ b/web/content/docs/benchmarks/python-bc/piston/load-steps.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b287e1f87187e948495d595c4d1a1d0b0aaf2f24975d965c251883180b85cd86 +size 24953 diff --git a/web/content/docs/benchmarks/python-bc/piston/piston.md b/web/content/docs/benchmarks/python-bc/piston/piston.md new file mode 100644 index 0000000000000000000000000000000000000000..c0894d69265c9355dadd3b6422a2ca4d72ebe0f8 --- /dev/null +++ b/web/content/docs/benchmarks/python-bc/piston/piston.md @@ -0,0 +1,48 @@ ++++ +project = "Mechanics/Linear/PythonPiston/piston.prj" +author = "Christoph Lehmann" +date = "2018-08-16T12:18:00+02:00" +title = "Ideal Gas Compressed by an Elastic Piston" +weight = 2 + +[menu] + [menu.benchmarks] + parent = "python-bc" + ++++ + +{{< data-link >}} + +## Motivation of this test case + +The aim of this test is: + +* to show that it is possible to prescribe BCs only on parts of a given geometry +* to assert that the computation of fluxes that depend nonlinearly on the + primary variables is implemented correctly. + +## Details + +A chamber filled with ideal gas is sealed tightly with a movable, elastic +piston. The position of the piston is varied between different load steps. +Friction between the piston and the chamber wall is neglected. +For simplicitly, also initially the elastic piston is in an unstressed state. + +{{<img src="../sketch-piston.png" >}} + + +## Results + +{{<img src="../load-steps.png" >}} + +The figure above shows that the piston is being compressed +($y$ displacement has larger negative values at the top) +by the forces acting on it. +The initial position of the top part of the piston is indicated as a wireframe. + +{{<img src="../pressure-displacement.png" >}} + +The plot shows that the relation between the stress in the piston and its +displacement coincides with the pressure-volume relation of the chamber. +That indicates that the Jacobian computation inside the Python BC classes was +correctly implemented. diff --git a/web/content/docs/benchmarks/python-bc/piston/pressure-displacement.png b/web/content/docs/benchmarks/python-bc/piston/pressure-displacement.png new file mode 100644 index 0000000000000000000000000000000000000000..a666eb785b20faa51632f4b43ae48177b1607b81 --- /dev/null +++ b/web/content/docs/benchmarks/python-bc/piston/pressure-displacement.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5f531b45437626649d49c146c830868dad04d6ce51385fb1493f0736de1374bf +size 25290 diff --git a/web/content/docs/benchmarks/python-bc/piston/sketch-piston.png b/web/content/docs/benchmarks/python-bc/piston/sketch-piston.png new file mode 100644 index 0000000000000000000000000000000000000000..1d047c42cab5f708e22d38b81dc257c91044d8a9 --- /dev/null +++ b/web/content/docs/benchmarks/python-bc/piston/sketch-piston.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bd7a9e1658f925dff4f8da98abc106541b61524d9f97d914bfb965ec7cce80de +size 22296 diff --git a/web/content/docs/benchmarks/python-bc/piston/sketch-piston.svg b/web/content/docs/benchmarks/python-bc/piston/sketch-piston.svg new file mode 100644 index 0000000000000000000000000000000000000000..8dbfdaa991928a121eb5ec5d2ce98ed8152c46e2 --- /dev/null +++ b/web/content/docs/benchmarks/python-bc/piston/sketch-piston.svg @@ -0,0 +1,589 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="359.36136" + height="539.42145" + viewBox="0 0 95.081026 142.72192" + version="1.1" + id="svg8" + inkscape:version="0.92.2 2405546, 2018-03-11" + sodipodi:docname="sketch-piston.svg"> + <defs + id="defs2"> + <marker + inkscape:isstock="true" + style="overflow:visible" + id="marker5183" + refX="0" + refY="0" + orient="auto" + inkscape:stockid="Arrow1Mstart"> + <path + transform="matrix(0.4,0,0,0.4,4,0)" + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1" + d="M 0,0 5,-5 -12.5,0 5,5 Z" + id="path5181" + inkscape:connector-curvature="0" /> + </marker> + <marker + inkscape:stockid="Arrow1Mend" + orient="auto" + refY="0" + refX="0" + id="Arrow1Mend" + style="overflow:visible" + inkscape:isstock="true"> + <path + id="path4533" + d="M 0,0 5,-5 -12.5,0 5,5 Z" + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1" + transform="matrix(-0.4,0,0,-0.4,-4,0)" + inkscape:connector-curvature="0" /> + </marker> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="0.7" + inkscape:cx="305.61598" + inkscape:cy="278.35678" + inkscape:document-units="mm" + inkscape:current-layer="layer1" + showgrid="false" + units="px" + inkscape:window-width="1600" + inkscape:window-height="875" + inkscape:window-x="0" + inkscape:window-y="25" + inkscape:window-maximized="1" + fit-margin-top="0" + fit-margin-left="0" + fit-margin-right="0" + fit-margin-bottom="0" /> + <metadata + id="metadata5"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Ebene 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-25.589826,-142.51819)"> + <rect + style="opacity:1;fill:#e6e6e6;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.26458335;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="rect6468" + width="85.963516" + height="60.540501" + x="32.922787" + y="218.64417" /> + <path + style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.26458332;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + d="M 120.53856,153.209 V 280.69656 H 31.270535 V 153.209" + id="rect4518" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cccc" /> + <rect + y="193.83394" + x="32.874153" + height="24.054256" + width="42.898102" + id="rect5168" + style="opacity:1;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.26458332;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <rect + style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.26458332;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="rect4520" + width="86.060783" + height="24.054256" + x="32.874153" + y="193.83394" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.26458332;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:0.26458332, 0.52916663;stroke-dashoffset:0;stroke-opacity:1" + d="M 75.904545,142.51819 V 285.24012" + id="path5170" + inkscape:connector-curvature="0" /> + <path + style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.26458332;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-start:url(#marker5183)" + id="path5173" + sodipodi:type="arc" + sodipodi:cx="75.903915" + sodipodi:cy="150.36656" + sodipodi:rx="10.824416" + sodipodi:ry="4.0090427" + sodipodi:start="5.1836395" + sodipodi:end="4.342102" + d="m 80.81821,146.7945 a 10.824416,4.0090427 0 0 1 5.543899,4.60606 10.824416,4.0090427 0 0 1 -11.004397,2.96994 10.824416,4.0090427 0 0 1 -10.140123,-3.36561 10.824416,4.0090427 0 0 1 6.769154,-4.37565" + sodipodi:open="true" /> + <g + id="g6322" + transform="matrix(0,-0.24041841,0.23377061,0,45.747789,206.26434)" + style="stroke-width:4.21814156"> + <path + transform="matrix(0.73936612,0,0,0.82399862,-6.1239083,26.230728)" + inkscape:transform-center-y="-3.1008631" + d="m -11.910359,148.04422 -26.072139,0 13.03607,-22.57914 z" + inkscape:randomized="0" + inkscape:rounded="0" + inkscape:flatsided="true" + sodipodi:arg2="1.5707963" + sodipodi:arg1="0.52359878" + sodipodi:r2="7.5263782" + sodipodi:r1="15.052756" + sodipodi:cy="140.51784" + sodipodi:cx="-24.946428" + sodipodi:sides="3" + id="path6290" + style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.42985046;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + sodipodi:type="star" /> + <circle + r="3.7797618" + cy="152.27068" + cx="-29.453543" + id="path6292" + style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <circle + style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="circle6294" + cx="-19.683363" + cy="152.27068" + r="3.7797618" /> + <path + inkscape:connector-curvature="0" + id="path6296" + d="m -34.423952,156.31501 h 19.710999" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + inkscape:connector-curvature="0" + id="path6298" + d="m -16.68344,156.45039 -1.992943,2.82356" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -19.734346,156.45039 -1.992942,2.82356" + id="path6300" + inkscape:connector-curvature="0" /> + <path + inkscape:connector-curvature="0" + id="path6302" + d="m -22.785251,156.45039 -1.992942,2.82356" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -25.836157,156.45039 -1.992942,2.82356" + id="path6304" + inkscape:connector-curvature="0" /> + <path + inkscape:connector-curvature="0" + id="path6306" + d="m -28.887062,156.45039 -1.992942,2.82356" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -31.937966,156.45039 -1.992942,2.82356" + id="path6308" + inkscape:connector-curvature="0" /> + </g> + <g + style="stroke-width:4.21814156" + transform="matrix(0,-0.24041841,-0.23377061,0,62.898619,194.68627)" + id="g6344"> + <path + sodipodi:type="star" + style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.42985046;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path6324" + sodipodi:sides="3" + sodipodi:cx="-24.946428" + sodipodi:cy="140.51784" + sodipodi:r1="15.052756" + sodipodi:r2="7.5263782" + sodipodi:arg1="0.52359878" + sodipodi:arg2="1.5707963" + inkscape:flatsided="true" + inkscape:rounded="0" + inkscape:randomized="0" + d="m -11.910359,148.04422 -26.072139,0 13.03607,-22.57914 z" + inkscape:transform-center-y="-3.1008631" + transform="matrix(0.73936612,0,0,0.82399862,-6.1239083,26.230728)" /> + <circle + style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="circle6326" + cx="-29.453543" + cy="152.27068" + r="3.7797618" /> + <circle + r="3.7797618" + cy="152.27068" + cx="-19.683363" + id="circle6328" + style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -34.423952,156.31501 h 19.710999" + id="path6330" + inkscape:connector-curvature="0" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -16.68344,156.45039 -1.992943,2.82356" + id="path6332" + inkscape:connector-curvature="0" /> + <path + inkscape:connector-curvature="0" + id="path6334" + d="m -19.734346,156.45039 -1.992942,2.82356" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -22.785251,156.45039 -1.992942,2.82356" + id="path6336" + inkscape:connector-curvature="0" /> + <path + inkscape:connector-curvature="0" + id="path6338" + d="m -25.836157,156.45039 -1.992942,2.82356" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -28.887062,156.45039 -1.992942,2.82356" + id="path6340" + inkscape:connector-curvature="0" /> + <path + inkscape:connector-curvature="0" + id="path6342" + d="m -31.937966,156.45039 -1.992942,2.82356" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + </g> + <g + style="stroke-width:4.21814156" + transform="matrix(0,-0.24041841,0.23377061,0,45.747789,194.68627)" + id="g6366"> + <path + sodipodi:type="star" + style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.42985046;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path6346" + sodipodi:sides="3" + sodipodi:cx="-24.946428" + sodipodi:cy="140.51784" + sodipodi:r1="15.052756" + sodipodi:r2="7.5263782" + sodipodi:arg1="0.52359878" + sodipodi:arg2="1.5707963" + inkscape:flatsided="true" + inkscape:rounded="0" + inkscape:randomized="0" + d="m -11.910359,148.04422 -26.072139,0 13.03607,-22.57914 z" + inkscape:transform-center-y="-3.1008631" + transform="matrix(0.73936612,0,0,0.82399862,-6.1239083,26.230728)" /> + <circle + style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="circle6348" + cx="-29.453543" + cy="152.27068" + r="3.7797618" /> + <circle + r="3.7797618" + cy="152.27068" + cx="-19.683363" + id="circle6350" + style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -34.423952,156.31501 h 19.710999" + id="path6352" + inkscape:connector-curvature="0" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -16.68344,156.45039 -1.992943,2.82356" + id="path6354" + inkscape:connector-curvature="0" /> + <path + inkscape:connector-curvature="0" + id="path6356" + d="m -19.734346,156.45039 -1.992942,2.82356" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -22.785251,156.45039 -1.992942,2.82356" + id="path6358" + inkscape:connector-curvature="0" /> + <path + inkscape:connector-curvature="0" + id="path6360" + d="m -25.836157,156.45039 -1.992942,2.82356" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -28.887062,156.45039 -1.992942,2.82356" + id="path6362" + inkscape:connector-curvature="0" /> + <path + inkscape:connector-curvature="0" + id="path6364" + d="m -31.937966,156.45039 -1.992942,2.82356" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + </g> + <g + id="g6388" + transform="matrix(0,-0.24041841,-0.23377061,0,62.898619,206.26434)" + style="stroke-width:4.21814156"> + <path + transform="matrix(0.73936612,0,0,0.82399862,-6.1239083,26.230728)" + inkscape:transform-center-y="-3.1008631" + d="m -11.910359,148.04422 -26.072139,0 13.03607,-22.57914 z" + inkscape:randomized="0" + inkscape:rounded="0" + inkscape:flatsided="true" + sodipodi:arg2="1.5707963" + sodipodi:arg1="0.52359878" + sodipodi:r2="7.5263782" + sodipodi:r1="15.052756" + sodipodi:cy="140.51784" + sodipodi:cx="-24.946428" + sodipodi:sides="3" + id="path6368" + style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.42985046;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + sodipodi:type="star" /> + <circle + r="3.7797618" + cy="152.27068" + cx="-29.453543" + id="circle6370" + style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <circle + style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="circle6372" + cx="-19.683363" + cy="152.27068" + r="3.7797618" /> + <path + inkscape:connector-curvature="0" + id="path6374" + d="m -34.423952,156.31501 h 19.710999" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + inkscape:connector-curvature="0" + id="path6376" + d="m -16.68344,156.45039 -1.992943,2.82356" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -19.734346,156.45039 -1.992942,2.82356" + id="path6378" + inkscape:connector-curvature="0" /> + <path + inkscape:connector-curvature="0" + id="path6380" + d="m -22.785251,156.45039 -1.992942,2.82356" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -25.836157,156.45039 -1.992942,2.82356" + id="path6382" + inkscape:connector-curvature="0" /> + <path + inkscape:connector-curvature="0" + id="path6384" + d="m -28.887062,156.45039 -1.992942,2.82356" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -31.937966,156.45039 -1.992942,2.82356" + id="path6386" + inkscape:connector-curvature="0" /> + </g> + <g + id="g6410" + transform="matrix(0.24041841,0,0,-0.23377061,49.374542,223.8584)" + style="stroke-width:4.21814156"> + <path + transform="matrix(0.73936612,0,0,0.82399862,-6.1239083,26.230728)" + inkscape:transform-center-y="-3.1008631" + d="m -11.910359,148.04422 -26.072139,0 13.03607,-22.57914 z" + inkscape:randomized="0" + inkscape:rounded="0" + inkscape:flatsided="true" + sodipodi:arg2="1.5707963" + sodipodi:arg1="0.52359878" + sodipodi:r2="7.5263782" + sodipodi:r1="15.052756" + sodipodi:cy="140.51784" + sodipodi:cx="-24.946428" + sodipodi:sides="3" + id="path6390" + style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.42985046;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + sodipodi:type="star" /> + <circle + r="3.7797618" + cy="152.27068" + cx="-29.453543" + id="circle6392" + style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <circle + style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="circle6394" + cx="-19.683363" + cy="152.27068" + r="3.7797618" /> + <path + inkscape:connector-curvature="0" + id="path6396" + d="m -34.423952,156.31501 h 19.710999" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + inkscape:connector-curvature="0" + id="path6398" + d="m -16.68344,156.45039 -1.992943,2.82356" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -19.734346,156.45039 -1.992942,2.82356" + id="path6400" + inkscape:connector-curvature="0" /> + <path + inkscape:connector-curvature="0" + id="path6402" + d="m -22.785251,156.45039 -1.992942,2.82356" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -25.836157,156.45039 -1.992942,2.82356" + id="path6404" + inkscape:connector-curvature="0" /> + <path + inkscape:connector-curvature="0" + id="path6406" + d="m -28.887062,156.45039 -1.992942,2.82356" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -31.937966,156.45039 -1.992942,2.82356" + id="path6408" + inkscape:connector-curvature="0" /> + </g> + <g + style="stroke-width:4.21814156" + transform="matrix(0.24041841,0,0,-0.23377061,69.629943,223.8584)" + id="g6432"> + <path + sodipodi:type="star" + style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.42985046;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path6412" + sodipodi:sides="3" + sodipodi:cx="-24.946428" + sodipodi:cy="140.51784" + sodipodi:r1="15.052756" + sodipodi:r2="7.5263782" + sodipodi:arg1="0.52359878" + sodipodi:arg2="1.5707963" + inkscape:flatsided="true" + inkscape:rounded="0" + inkscape:randomized="0" + d="m -11.910359,148.04422 -26.072139,0 13.03607,-22.57914 z" + inkscape:transform-center-y="-3.1008631" + transform="matrix(0.73936612,0,0,0.82399862,-6.1239083,26.230728)" /> + <circle + style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="circle6414" + cx="-29.453543" + cy="152.27068" + r="3.7797618" /> + <circle + r="3.7797618" + cy="152.27068" + cx="-19.683363" + id="circle6416" + style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -34.423952,156.31501 h 19.710999" + id="path6418" + inkscape:connector-curvature="0" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -16.68344,156.45039 -1.992943,2.82356" + id="path6420" + inkscape:connector-curvature="0" /> + <path + inkscape:connector-curvature="0" + id="path6422" + d="m -19.734346,156.45039 -1.992942,2.82356" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -22.785251,156.45039 -1.992942,2.82356" + id="path6424" + inkscape:connector-curvature="0" /> + <path + inkscape:connector-curvature="0" + id="path6426" + d="m -25.836157,156.45039 -1.992942,2.82356" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="m -28.887062,156.45039 -1.992942,2.82356" + id="path6428" + inkscape:connector-curvature="0" /> + <path + inkscape:connector-curvature="0" + id="path6430" + d="m -31.937966,156.45039 -1.992942,2.82356" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.11605px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + </g> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82222223px;line-height:6.61458302px;font-family:'Latin Modern Sans';-inkscape-font-specification:'Latin Modern Sans';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + x="40.921589" + y="207.0658" + id="text6436"><tspan + sodipodi:role="line" + id="tspan6434" + x="40.921589" + y="207.0658" + style="font-size:3.52777791px;stroke-width:0.26458332px">simulation domain</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82222223px;line-height:4px;font-family:'Latin Modern Sans';-inkscape-font-specification:'Latin Modern Sans';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + x="93.009796" + y="204.63506" + id="text6440"><tspan + sodipodi:role="line" + id="tspan6438" + x="93.009796" + y="204.63506" + style="font-size:3.52777791px;line-height:4px;stroke-width:0.26458332px">elastic</tspan><tspan + sodipodi:role="line" + x="93.009796" + y="208.85095" + style="font-size:3.52777791px;line-height:4px;stroke-width:0.26458332px" + id="tspan6442">piston</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.82222223px;line-height:6.61458302px;font-family:'Latin Modern Sans';-inkscape-font-specification:'Latin Modern Sans';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + x="54.790409" + y="249.7946" + id="text6472"><tspan + sodipodi:role="line" + id="tspan6470" + x="54.790409" + y="249.7946" + style="font-size:3.52777767px;stroke-width:0.26458332px">chamber filled with ideal gas</tspan></text> + </g> +</svg>