From 5230452d1a6765fd4426dd286efe32732bbcb74e Mon Sep 17 00:00:00 2001
From: xymiao <miaoxingyuan@live.cn>
Date: Mon, 8 May 2017 19:16:45 +0200
Subject: [PATCH] added thermo-mechanical process and test.

---
 Applications/ApplicationsLib/ProjectData.cpp  |  21 +
 ProcessLib/CMakeLists.txt                     |   2 +
 .../ThermoMechanics/CreateLocalAssemblers.h   | 100 ++++
 .../CreateThermoMechanicsProcess.cpp          | 196 ++++++
 .../CreateThermoMechanicsProcess.h            |  28 +
 .../ThermoMechanics/LocalDataInitializer.h    | 306 ++++++++++
 ProcessLib/ThermoMechanics/Tests.cmake        |  16 +
 .../ThermoMechanics/ThermoMechanicsFEM.h      | 558 ++++++++++++++++++
 .../ThermoMechanicsProcess-fwd.h              |  15 +
 .../ThermoMechanicsProcess.cpp                |  21 +
 .../ThermoMechanics/ThermoMechanicsProcess.h  | 220 +++++++
 .../ThermoMechanicsProcessData.h              |  80 +++
 12 files changed, 1563 insertions(+)
 create mode 100644 ProcessLib/ThermoMechanics/CreateLocalAssemblers.h
 create mode 100644 ProcessLib/ThermoMechanics/CreateThermoMechanicsProcess.cpp
 create mode 100644 ProcessLib/ThermoMechanics/CreateThermoMechanicsProcess.h
 create mode 100644 ProcessLib/ThermoMechanics/LocalDataInitializer.h
 create mode 100644 ProcessLib/ThermoMechanics/Tests.cmake
 create mode 100644 ProcessLib/ThermoMechanics/ThermoMechanicsFEM.h
 create mode 100644 ProcessLib/ThermoMechanics/ThermoMechanicsProcess-fwd.h
 create mode 100644 ProcessLib/ThermoMechanics/ThermoMechanicsProcess.cpp
 create mode 100644 ProcessLib/ThermoMechanics/ThermoMechanicsProcess.h
 create mode 100644 ProcessLib/ThermoMechanics/ThermoMechanicsProcessData.h

diff --git a/Applications/ApplicationsLib/ProjectData.cpp b/Applications/ApplicationsLib/ProjectData.cpp
index ecfbbc76185..765fd9cebb4 100644
--- a/Applications/ApplicationsLib/ProjectData.cpp
+++ b/Applications/ApplicationsLib/ProjectData.cpp
@@ -46,6 +46,7 @@
 #include "ProcessLib/SmallDeformation/CreateSmallDeformationProcess.h"
 #include "ProcessLib/TES/CreateTESProcess.h"
 #include "ProcessLib/ThermalTwoPhaseFlowWithPP/CreateThermalTwoPhaseFlowWithPPProcess.h"
+#include "ProcessLib/ThermoMechanics/CreateThermoMechanicsProcess.h"
 #include "ProcessLib/TwoPhaseFlowWithPP/CreateTwoPhaseFlowWithPPProcess.h"
 #include "ProcessLib/TwoPhaseFlowWithPrho/CreateTwoPhaseFlowWithPrhoProcess.h"
 
@@ -434,6 +435,26 @@ void ProjectData::parseProcesses(BaseLib::ConfigTree const& processes_config,
                         "given dimension");
             }
         }
+        else if (type == "THERMOMECHANICS")
+        {
+            switch (process_config.getConfigParameter<int>("dimension"))
+            {
+                case 2:
+                    process = ProcessLib::ThermoMechanics::
+                        createThermoMechanicsProcess<2>(
+                            *_mesh_vec[0], std::move(jacobian_assembler),
+                            _process_variables, _parameters, integration_order,
+                            process_config);
+                    break;
+                case 3:
+                    process = ProcessLib::ThermoMechanics::
+                        createThermoMechanicsProcess<3>(
+                            *_mesh_vec[0], std::move(jacobian_assembler),
+                            _process_variables, _parameters, integration_order,
+                            process_config);
+                    break;
+            }
+        }
         else if (type == "RICHARDS_FLOW")
         {
             process = ProcessLib::RichardsFlow::createRichardsFlowProcess(
diff --git a/ProcessLib/CMakeLists.txt b/ProcessLib/CMakeLists.txt
index daafa478bf5..e64cf8f6782 100644
--- a/ProcessLib/CMakeLists.txt
+++ b/ProcessLib/CMakeLists.txt
@@ -20,6 +20,7 @@ APPEND_SOURCE_FILES(SOURCES Parameter)
 APPEND_SOURCE_FILES(SOURCES RichardsFlow)
 APPEND_SOURCE_FILES(SOURCES SmallDeformation)
 APPEND_SOURCE_FILES(SOURCES TES)
+APPEND_SOURCE_FILES(SOURCES ThermoMechanics)
 APPEND_SOURCE_FILES(SOURCES TwoPhaseFlowWithPP)
 APPEND_SOURCE_FILES(SOURCES TwoPhaseFlowWithPrho)
 APPEND_SOURCE_FILES(SOURCES ThermalTwoPhaseFlowWithPP)
@@ -60,6 +61,7 @@ include(LiquidFlow/Tests.cmake)
 include(RichardsFlow/Tests.cmake)
 include(SmallDeformation/Tests.cmake)
 include(TES/Tests.cmake)
+include(ThermoMechanics/Tests.cmake)
 include(TwoPhaseFlowWithPP/Tests.cmake)
 include(TwoPhaseFlowWithPrho/Tests.cmake)
 include(ThermalTwoPhaseFlowWithPP/Tests.cmake)
diff --git a/ProcessLib/ThermoMechanics/CreateLocalAssemblers.h b/ProcessLib/ThermoMechanics/CreateLocalAssemblers.h
new file mode 100644
index 00000000000..7a4103b84f1
--- /dev/null
+++ b/ProcessLib/ThermoMechanics/CreateLocalAssemblers.h
@@ -0,0 +1,100 @@
+/**
+ * \copyright
+ * Copyright (c) 2012-2017, 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 <vector>
+
+#include <logog/include/logog.hpp>
+
+#include "NumLib/DOF/LocalToGlobalIndexMap.h"
+
+#include "LocalDataInitializer.h"
+
+namespace ProcessLib
+{
+namespace ThermoMechanics
+{
+namespace detail
+{
+template <unsigned GlobalDim, int DisplacementDim,
+          template <typename, typename, unsigned, int>
+          class LocalAssemblerImplementation,
+          typename LocalAssemblerInterface, typename... ExtraCtorArgs>
+void createLocalAssemblers(
+    NumLib::LocalToGlobalIndexMap const& dof_table,
+    std::vector<MeshLib::Element*> const& mesh_elements,
+    std::vector<std::unique_ptr<LocalAssemblerInterface>>& local_assemblers,
+    ExtraCtorArgs&&... extra_ctor_args)
+{
+    // Shape matrices initializer
+    using LocalDataInitializer =
+        LocalDataInitializer<LocalAssemblerInterface,
+                             LocalAssemblerImplementation, GlobalDim,
+                             DisplacementDim, ExtraCtorArgs...>;
+
+    DBUG("Create local assemblers.");
+    // Populate the vector of local assemblers.
+    local_assemblers.resize(mesh_elements.size());
+
+    LocalDataInitializer initializer(dof_table);
+
+    DBUG("Calling local assembler builder for all mesh elements.");
+    GlobalExecutor::transformDereferenced(
+        initializer, mesh_elements, local_assemblers,
+        std::forward<ExtraCtorArgs>(extra_ctor_args)...);
+}
+
+}  // namespace detail
+
+/*! Creates local assemblers for each element of the given \c mesh.
+ *
+ * \tparam LocalAssemblerImplementation the individual local assembler type
+ * \tparam LocalAssemblerInterface the general local assembler interface
+ * \tparam ExtraCtorArgs types of additional constructor arguments.
+ *         Those arguments will be passed to the constructor of
+ *         \c LocalAssemblerImplementation.
+ *
+ * The first two template parameters cannot be deduced from the arguments.
+ * Therefore they always have to be provided manually.
+ */
+template <int DisplacementDim, template <typename, typename, unsigned, int>
+                               class LocalAssemblerImplementation,
+          typename LocalAssemblerInterface, typename... ExtraCtorArgs>
+void createLocalAssemblers(
+    const unsigned dimension,
+    std::vector<MeshLib::Element*> const& mesh_elements,
+    NumLib::LocalToGlobalIndexMap const& dof_table,
+    std::vector<std::unique_ptr<LocalAssemblerInterface>>& local_assemblers,
+    ExtraCtorArgs&&... extra_ctor_args)
+{
+    DBUG("Create local assemblers.");
+
+    switch (dimension)
+    {
+        case 2:
+            detail::createLocalAssemblers<2, DisplacementDim,
+                                          LocalAssemblerImplementation>(
+                dof_table, mesh_elements, local_assemblers,
+                std::forward<ExtraCtorArgs>(extra_ctor_args)...);
+            break;
+        case 3:
+            detail::createLocalAssemblers<3, DisplacementDim,
+                                          LocalAssemblerImplementation>(
+                dof_table, mesh_elements, local_assemblers,
+                std::forward<ExtraCtorArgs>(extra_ctor_args)...);
+            break;
+        default:
+            OGS_FATAL(
+                "Meshes with dimension different than two and three are not "
+                "supported.");
+    }
+}
+}  // ThermoMechanics
+
+}  // ProcessLib
diff --git a/ProcessLib/ThermoMechanics/CreateThermoMechanicsProcess.cpp b/ProcessLib/ThermoMechanics/CreateThermoMechanicsProcess.cpp
new file mode 100644
index 00000000000..12320a2d4f9
--- /dev/null
+++ b/ProcessLib/ThermoMechanics/CreateThermoMechanicsProcess.cpp
@@ -0,0 +1,196 @@
+/**
+ * \copyright
+ * Copyright (c) 2012-2017, 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 "CreateThermoMechanicsProcess.h"
+
+#include <cassert>
+
+#include "MaterialLib/SolidModels/CreateLinearElasticIsotropic.h"
+#include "ProcessLib/Utils/ParseSecondaryVariables.h"
+
+#include "ThermoMechanicsProcess.h"
+#include "ThermoMechanicsProcessData.h"
+
+namespace ProcessLib
+{
+namespace ThermoMechanics
+{
+template <int DisplacementDim>
+class ThermoMechanicsProcess;
+
+extern template class ThermoMechanicsProcess<2>;
+extern template class ThermoMechanicsProcess<3>;
+
+template <int DisplacementDim>
+std::unique_ptr<Process> createThermoMechanicsProcess(
+    MeshLib::Mesh& mesh,
+    std::unique_ptr<ProcessLib::AbstractJacobianAssembler>&& jacobian_assembler,
+    std::vector<ProcessVariable> const& variables,
+    std::vector<std::unique_ptr<ParameterBase>> const& parameters,
+    unsigned const integration_order,
+    BaseLib::ConfigTree const& config)
+{
+    //! \ogs_file_param{process__type}
+    config.checkConfigParameter("type", "THERMOMECHANICS");
+    DBUG("Create ThermoMechanicsProcess.");
+
+    // Process variable.
+
+    //! \ogs_file_param{prj__processes__process__THERMOMECHANICS__process_variables}
+    auto const pv_config = config.getConfigSubtree("process_variables");
+
+    auto process_variables = findProcessVariables(
+        variables, pv_config,
+        {//! \ogs_file_param_special{prj__processes__process__THERMOMECHANICS__process_variables__temperature}
+         "temperature",
+         //! \ogs_file_param_special{prj__processes__process__THERMOMECHANICS__process_variables__displacement}
+         "displacement"});
+
+    DBUG("Associate displacement with process variable \'%s\'.",
+         process_variables[1].get().getName().c_str());
+
+    if (process_variables[1].get().getNumberOfComponents() != DisplacementDim)
+    {
+        OGS_FATAL(
+            "Number of components of the process variable '%s' is different "
+            "from the displacement dimension: got %d, expected %d",
+            process_variables[1].get().getName().c_str(),
+            process_variables[1].get().getNumberOfComponents(),
+            DisplacementDim);
+    }
+
+    DBUG("Associate temperature with process variable \'%s\'.",
+         process_variables[0].get().getName().c_str());
+    if (process_variables[0].get().getNumberOfComponents() != 1)
+    {
+        OGS_FATAL(
+            "Temperature process variable '%s' is not a scalar variable but has"
+            "%d components.",
+            process_variables[0].get().getName().c_str(),
+            process_variables[0].get().getNumberOfComponents(),
+            DisplacementDim);
+    }
+
+    // Constitutive relation.
+    // read type;
+    auto const constitutive_relation_config =
+        //! \ogs_file_param{process__THERMOMECHANICS_constitutive_relation}
+        config.getConfigSubtree("constitutive_relation");
+
+    auto const type =
+        //! \ogs_file_param{prj__processes__process__THERMOMECHANICS__constitutive_relation__type}
+        constitutive_relation_config.peekConfigParameter<std::string>("type");
+
+    std::unique_ptr<MaterialLib::Solids::MechanicsBase<DisplacementDim>>
+        material = nullptr;
+    if (type == "LinearElasticIsotropic")
+    {
+        material =
+            MaterialLib::Solids::createLinearElasticIsotropic<DisplacementDim>(
+                parameters, constitutive_relation_config);
+    }
+    else
+    {
+        OGS_FATAL(
+            "Cannot construct constitutive relation of given type \'%s\'.",
+            type.c_str());
+    }
+
+    // Solid density
+    auto& solid_density = findParameter<double>(
+        config,
+        //! \ogs_file_param_special{process__THERMOMECHANICS_solid_density}
+        "solid_density", parameters, 1);
+    DBUG("Use \'%s\' as solid density parameter.", solid_density.name.c_str());
+
+    // Linear thermal expansion coefficient
+    auto& linear_thermal_expansion_coefficient = findParameter<double>(
+        config,
+        //! \ogs_file_param_special{process__THERMOMECHANICS_linear_thermal_expansion_coefficient}
+        "linear_thermal_expansion_coefficient", parameters, 1);
+    DBUG("Use \'%s\' as linear thermal expansion coefficient.",
+         linear_thermal_expansion_coefficient.name.c_str());
+    // Specific heat capacity
+    auto& specific_heat_capacity = findParameter<double>(
+        config,
+        //! \ogs_file_param_special{process__THERMOMECHANICS_specific_heat_capacity}
+        "specific_heat_capacity", parameters, 1);
+    DBUG("Use \'%s\' as specific heat capacity parameter.",
+         specific_heat_capacity.name.c_str());
+    // Thermal conductivity
+    auto& thermal_conductivity = findParameter<double>(
+        config,
+        //! \ogs_file_param_special{process__THERMOMECHANICS_thermal_conductivity}
+        "thermal_conductivity", parameters, 1);
+    DBUG("Use \'%s\' as thermal conductivity parameter.",
+         thermal_conductivity.name.c_str());
+    // Reference temperature
+    const double reference_temperature =
+        //! \ogs_file_param_special{process__THERMOMECHANICS_reference_temperature}
+        config.getConfigParameter<double>("reference_temperature");
+
+    // Specific body force
+    Eigen::Matrix<double, DisplacementDim, 1> specific_body_force;
+    {
+        std::vector<double> const b =
+            //! \ogs_file_param{prj__processes__process__HYDRO_MECHANICS__specific_body_force}
+            config.getConfigParameter<std::vector<double>>(
+                "specific_body_force");
+        if (specific_body_force.size() != DisplacementDim)
+            OGS_FATAL(
+                "The size of the specific body force vector does not match the "
+                "displacement dimension. Vector size is %d, displacement "
+                "dimension is %d",
+                specific_body_force.size(), DisplacementDim);
+
+        std::copy_n(b.data(), b.size(), specific_body_force.data());
+    }
+
+    ThermoMechanicsProcessData<DisplacementDim> process_data{
+        std::move(material),
+        solid_density,
+        linear_thermal_expansion_coefficient,
+        specific_heat_capacity,
+        thermal_conductivity,
+        reference_temperature,
+        specific_body_force};
+
+    SecondaryVariableCollection secondary_variables;
+
+    NumLib::NamedFunctionCaller named_function_caller(
+        {"ThermoMechanics_temperature_displacement"});
+
+    ProcessLib::parseSecondaryVariables(config, secondary_variables,
+                                        named_function_caller);
+
+    return std::unique_ptr<ThermoMechanicsProcess<DisplacementDim>>{
+        new ThermoMechanicsProcess<DisplacementDim>{
+            mesh, std::move(jacobian_assembler), parameters, integration_order,
+            std::move(process_variables), std::move(process_data),
+            std::move(secondary_variables), std::move(named_function_caller)}};
+}
+
+template std::unique_ptr<Process> createThermoMechanicsProcess<2>(
+    MeshLib::Mesh& mesh,
+    std::unique_ptr<ProcessLib::AbstractJacobianAssembler>&& jacobian_assembler,
+    std::vector<ProcessVariable> const& variables,
+    std::vector<std::unique_ptr<ParameterBase>> const& parameters,
+    unsigned const integration_order,
+    BaseLib::ConfigTree const& config);
+
+template std::unique_ptr<Process> createThermoMechanicsProcess<3>(
+    MeshLib::Mesh& mesh,
+    std::unique_ptr<ProcessLib::AbstractJacobianAssembler>&& jacobian_assembler,
+    std::vector<ProcessVariable> const& variables,
+    std::vector<std::unique_ptr<ParameterBase>> const& parameters,
+    unsigned const integration_order,
+    BaseLib::ConfigTree const& config);
+
+}  // namespace ThermoMechanics
+}  // namespace ProcessLib
diff --git a/ProcessLib/ThermoMechanics/CreateThermoMechanicsProcess.h b/ProcessLib/ThermoMechanics/CreateThermoMechanicsProcess.h
new file mode 100644
index 00000000000..5b2849eedd9
--- /dev/null
+++ b/ProcessLib/ThermoMechanics/CreateThermoMechanicsProcess.h
@@ -0,0 +1,28 @@
+/**
+ * \copyright
+ * Copyright (c) 2012-2017, 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 "ProcessLib/Process.h"
+
+namespace ProcessLib
+{
+namespace ThermoMechanics
+{
+template <int DisplacementDim>
+std::unique_ptr<Process> createThermoMechanicsProcess(
+    MeshLib::Mesh& mesh,
+    std::unique_ptr<ProcessLib::AbstractJacobianAssembler>&& jacobian_assembler,
+    std::vector<ProcessVariable> const& variables,
+    std::vector<std::unique_ptr<ParameterBase>> const& parameters,
+    unsigned const integration_order,
+    BaseLib::ConfigTree const& config);
+
+}  // namespace ThermoMechanics
+}  // namespace ProcessLib
diff --git a/ProcessLib/ThermoMechanics/LocalDataInitializer.h b/ProcessLib/ThermoMechanics/LocalDataInitializer.h
new file mode 100644
index 00000000000..3e766a49756
--- /dev/null
+++ b/ProcessLib/ThermoMechanics/LocalDataInitializer.h
@@ -0,0 +1,306 @@
+/**
+ * \copyright
+ * Copyright (c) 2012-2017, 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 <functional>
+#include <memory>
+#include <type_traits>
+#include <typeindex>
+#include <typeinfo>
+#include <unordered_map>
+
+#include "MeshLib/Elements/Elements.h"
+#include "NumLib/DOF/LocalToGlobalIndexMap.h"
+#include "NumLib/Fem/Integration/GaussIntegrationPolicy.h"
+
+#ifndef OGS_MAX_ELEMENT_DIM
+static_assert(false, "The macro OGS_MAX_ELEMENT_DIM is undefined.");
+#endif
+
+#ifndef OGS_MAX_ELEMENT_ORDER
+static_assert(false, "The macro OGS_MAX_ELEMENT_ORDER is undefined.");
+#endif
+
+// The following macros decide which element types will be compiled, i.e.
+// which element types will be available for use in simulations.
+
+#ifdef OGS_ENABLE_ELEMENT_SIMPLEX
+#define ENABLED_ELEMENT_TYPE_SIMPLEX 1u
+#else
+#define ENABLED_ELEMENT_TYPE_SIMPLEX 0u
+#endif
+
+#ifdef OGS_ENABLE_ELEMENT_CUBOID
+#define ENABLED_ELEMENT_TYPE_CUBOID 1u << 1
+#else
+#define ENABLED_ELEMENT_TYPE_CUBOID 0u
+#endif
+
+#ifdef OGS_ENABLE_ELEMENT_PRISM
+#define ENABLED_ELEMENT_TYPE_PRISM 1u << 2
+#else
+#define ENABLED_ELEMENT_TYPE_PRISM 0u
+#endif
+
+#ifdef OGS_ENABLE_ELEMENT_PYRAMID
+#define ENABLED_ELEMENT_TYPE_PYRAMID 1u << 3
+#else
+#define ENABLED_ELEMENT_TYPE_PYRAMID 0u
+#endif
+
+// Dependent element types.
+// Faces of tets, pyramids and prisms are triangles
+#define ENABLED_ELEMENT_TYPE_TRI                                       \
+    ((ENABLED_ELEMENT_TYPE_SIMPLEX) | (ENABLED_ELEMENT_TYPE_PYRAMID) | \
+     (ENABLED_ELEMENT_TYPE_PRISM))
+// Faces of hexes, pyramids and prisms are quads
+#define ENABLED_ELEMENT_TYPE_QUAD                                     \
+    ((ENABLED_ELEMENT_TYPE_CUBOID) | (ENABLED_ELEMENT_TYPE_PYRAMID) | \
+     (ENABLED_ELEMENT_TYPE_PRISM))
+
+// All enabled element types
+#define OGS_ENABLED_ELEMENTS                                          \
+    ((ENABLED_ELEMENT_TYPE_SIMPLEX) | (ENABLED_ELEMENT_TYPE_CUBOID) | \
+     (ENABLED_ELEMENT_TYPE_PYRAMID) | (ENABLED_ELEMENT_TYPE_PRISM))
+
+// Include only what is needed (Well, the conditions are not sharp).
+#if (OGS_ENABLED_ELEMENTS & ENABLED_ELEMENT_TYPE_SIMPLEX) != 0
+#include "NumLib/Fem/ShapeFunction/ShapeTet10.h"
+#include "NumLib/Fem/ShapeFunction/ShapeTet4.h"
+#endif
+
+#if (OGS_ENABLED_ELEMENTS & ENABLED_ELEMENT_TYPE_TRI) != 0
+#include "NumLib/Fem/ShapeFunction/ShapeTri3.h"
+#include "NumLib/Fem/ShapeFunction/ShapeTri6.h"
+#endif
+
+#if (OGS_ENABLED_ELEMENTS & ENABLED_ELEMENT_TYPE_CUBOID) != 0
+#include "NumLib/Fem/ShapeFunction/ShapeHex20.h"
+#include "NumLib/Fem/ShapeFunction/ShapeHex8.h"
+#endif
+
+#if (OGS_ENABLED_ELEMENTS & ENABLED_ELEMENT_TYPE_QUAD) != 0
+#include "NumLib/Fem/ShapeFunction/ShapeQuad4.h"
+#include "NumLib/Fem/ShapeFunction/ShapeQuad8.h"
+#include "NumLib/Fem/ShapeFunction/ShapeQuad9.h"
+#endif
+
+#if (OGS_ENABLED_ELEMENTS & ENABLED_ELEMENT_TYPE_PRISM) != 0
+#include "NumLib/Fem/ShapeFunction/ShapePrism15.h"
+#include "NumLib/Fem/ShapeFunction/ShapePrism6.h"
+#endif
+
+#if (OGS_ENABLED_ELEMENTS & ENABLED_ELEMENT_TYPE_PYRAMID) != 0
+#include "NumLib/Fem/ShapeFunction/ShapePyra13.h"
+#include "NumLib/Fem/ShapeFunction/ShapePyra5.h"
+#endif
+
+namespace ProcessLib
+{
+namespace ThermoMechanics
+{
+/// The LocalDataInitializer is a functor creating a local assembler data with
+/// corresponding to the mesh element type shape functions and calling
+/// initialization of the new local assembler data.
+/// For example for MeshLib::Quad a local assembler data with template argument
+/// NumLib::ShapeQuad4 is created.
+template <typename LocalAssemblerInterface,
+          template <typename, typename, unsigned, int> class LocalAssemblerData,
+          unsigned GlobalDim, int DisplacementDim, typename... ConstructorArgs>
+class LocalDataInitializer final
+{
+public:
+    using LADataIntfPtr = std::unique_ptr<LocalAssemblerInterface>;
+
+    explicit LocalDataInitializer(
+        NumLib::LocalToGlobalIndexMap const& dof_table)
+        : _dof_table(dof_table)
+    {
+// /// Quads and Hexahedra ///////////////////////////////////
+
+#if (OGS_ENABLED_ELEMENTS & ENABLED_ELEMENT_TYPE_QUAD) != 0 && \
+    OGS_MAX_ELEMENT_DIM >= 2 && OGS_MAX_ELEMENT_ORDER >= 1
+        _builder[std::type_index(typeid(MeshLib::Quad))] =
+            makeLocalAssemblerBuilder<NumLib::ShapeQuad4>();
+#endif
+
+#if (OGS_ENABLED_ELEMENTS & ENABLED_ELEMENT_TYPE_CUBOID) != 0 && \
+    OGS_MAX_ELEMENT_DIM >= 3 && OGS_MAX_ELEMENT_ORDER >= 1
+        _builder[std::type_index(typeid(MeshLib::Hex))] =
+            makeLocalAssemblerBuilder<NumLib::ShapeHex8>();
+#endif
+
+#if (OGS_ENABLED_ELEMENTS & ENABLED_ELEMENT_TYPE_QUAD) != 0 && \
+    OGS_MAX_ELEMENT_DIM >= 2 && OGS_MAX_ELEMENT_ORDER >= 2
+        _builder[std::type_index(typeid(MeshLib::Quad8))] =
+            makeLocalAssemblerBuilder<NumLib::ShapeQuad8>();
+        _builder[std::type_index(typeid(MeshLib::Quad9))] =
+            makeLocalAssemblerBuilder<NumLib::ShapeQuad9>();
+#endif
+
+#if (OGS_ENABLED_ELEMENTS & ENABLED_ELEMENT_TYPE_CUBOID) != 0 && \
+    OGS_MAX_ELEMENT_DIM >= 3 && OGS_MAX_ELEMENT_ORDER >= 2
+        _builder[std::type_index(typeid(MeshLib::Hex20))] =
+            makeLocalAssemblerBuilder<NumLib::ShapeHex20>();
+#endif
+
+// /// Simplices ////////////////////////////////////////////////
+
+#if (OGS_ENABLED_ELEMENTS & ENABLED_ELEMENT_TYPE_TRI) != 0 && \
+    OGS_MAX_ELEMENT_DIM >= 2 && OGS_MAX_ELEMENT_ORDER >= 1
+        _builder[std::type_index(typeid(MeshLib::Tri))] =
+            makeLocalAssemblerBuilder<NumLib::ShapeTri3>();
+#endif
+
+#if (OGS_ENABLED_ELEMENTS & ENABLED_ELEMENT_TYPE_SIMPLEX) != 0 && \
+    OGS_MAX_ELEMENT_DIM >= 3 && OGS_MAX_ELEMENT_ORDER >= 1
+        _builder[std::type_index(typeid(MeshLib::Tet))] =
+            makeLocalAssemblerBuilder<NumLib::ShapeTet4>();
+#endif
+
+#if (OGS_ENABLED_ELEMENTS & ENABLED_ELEMENT_TYPE_TRI) != 0 && \
+    OGS_MAX_ELEMENT_DIM >= 2 && OGS_MAX_ELEMENT_ORDER >= 2
+        _builder[std::type_index(typeid(MeshLib::Tri6))] =
+            makeLocalAssemblerBuilder<NumLib::ShapeTri6>();
+#endif
+
+#if (OGS_ENABLED_ELEMENTS & ENABLED_ELEMENT_TYPE_SIMPLEX) != 0 && \
+    OGS_MAX_ELEMENT_DIM >= 3 && OGS_MAX_ELEMENT_ORDER >= 2
+        _builder[std::type_index(typeid(MeshLib::Tet10))] =
+            makeLocalAssemblerBuilder<NumLib::ShapeTet10>();
+#endif
+
+// /// Prisms ////////////////////////////////////////////////////
+
+#if (OGS_ENABLED_ELEMENTS & ENABLED_ELEMENT_TYPE_PRISM) != 0 && \
+    OGS_MAX_ELEMENT_DIM >= 3 && OGS_MAX_ELEMENT_ORDER >= 1
+        _builder[std::type_index(typeid(MeshLib::Prism))] =
+            makeLocalAssemblerBuilder<NumLib::ShapePrism6>();
+#endif
+
+#if (OGS_ENABLED_ELEMENTS & ENABLED_ELEMENT_TYPE_PRISM) != 0 && \
+    OGS_MAX_ELEMENT_DIM >= 3 && OGS_MAX_ELEMENT_ORDER >= 2
+        _builder[std::type_index(typeid(MeshLib::Prism15))] =
+            makeLocalAssemblerBuilder<NumLib::ShapePrism15>();
+#endif
+
+// /// Pyramids //////////////////////////////////////////////////
+
+#if (OGS_ENABLED_ELEMENTS & ENABLED_ELEMENT_TYPE_PYRAMID) != 0 && \
+    OGS_MAX_ELEMENT_DIM >= 3 && OGS_MAX_ELEMENT_ORDER >= 1
+        _builder[std::type_index(typeid(MeshLib::Pyramid))] =
+            makeLocalAssemblerBuilder<NumLib::ShapePyra5>();
+#endif
+
+#if (OGS_ENABLED_ELEMENTS & ENABLED_ELEMENT_TYPE_PYRAMID) != 0 && \
+    OGS_MAX_ELEMENT_DIM >= 3 && OGS_MAX_ELEMENT_ORDER >= 2
+        _builder[std::type_index(typeid(MeshLib::Pyramid13))] =
+            makeLocalAssemblerBuilder<NumLib::ShapePyra13>();
+#endif
+    }
+
+    /// Sets the provided \c data_ptr to the newly created local assembler data.
+    ///
+    /// \attention
+    /// The index \c id is not necessarily the mesh item's id. Especially when
+    /// having multiple meshes it will differ from the latter.
+    void operator()(std::size_t const id,
+                    MeshLib::Element const& mesh_item,
+                    LADataIntfPtr& data_ptr,
+                    ConstructorArgs&&... args) const
+    {
+        auto const type_idx = std::type_index(typeid(mesh_item));
+        auto const it = _builder.find(type_idx);
+
+        if (it != _builder.end())
+        {
+            auto const num_local_dof = _dof_table.getNumberOfElementDOF(id);
+            data_ptr = it->second(mesh_item, num_local_dof,
+                                  std::forward<ConstructorArgs>(args)...);
+        }
+        else
+        {
+            OGS_FATAL(
+                "You are trying to build a local assembler for an unknown mesh "
+                "element type (%s)."
+                " Maybe you have disabled this mesh element type in your build "
+                "configuration or this process requires higher order elements.",
+                type_idx.name());
+        }
+    }
+
+private:
+    using LADataBuilder =
+        std::function<LADataIntfPtr(MeshLib::Element const& e,
+                                    std::size_t const local_matrix_size,
+                                    ConstructorArgs&&...)>;
+
+    template <typename ShapeFunction>
+    using IntegrationMethod = typename NumLib::GaussIntegrationPolicy<
+        typename ShapeFunction::MeshElement>::IntegrationMethod;
+
+    template <typename ShapeFunction>
+    using LAData =
+        LocalAssemblerData<ShapeFunction, IntegrationMethod<ShapeFunction>,
+                           GlobalDim, DisplacementDim>;
+
+    /// A helper forwarding to the correct version of makeLocalAssemblerBuilder
+    /// depending whether the global dimension is less than the shape function's
+    /// dimension or not.
+    template <typename ShapeFunction>
+    static LADataBuilder makeLocalAssemblerBuilder()
+    {
+        return makeLocalAssemblerBuilder<ShapeFunction>(
+            static_cast<std::integral_constant<
+                bool, (GlobalDim >= ShapeFunction::DIM)>*>(nullptr));
+    }
+
+    /// Mapping of element types to local assembler constructors.
+    std::unordered_map<std::type_index, LADataBuilder> _builder;
+
+    NumLib::LocalToGlobalIndexMap const& _dof_table;
+
+    // local assembler builder implementations.
+private:
+    /// Generates a function that creates a new LocalAssembler of type
+    /// LAData<ShapeFunction>. Only functions with shape function's dimension
+    /// less or equal to the global dimension are instantiated, e.g.  following
+    /// combinations of shape functions and global dimensions: (Line2, 1),
+    /// (Line2, 2), (Line2, 3), (Hex20, 3) but not (Hex20, 2) or (Hex20, 1).
+    template <typename ShapeFunction>
+    static LADataBuilder makeLocalAssemblerBuilder(std::true_type*)
+    {
+        return [](MeshLib::Element const& e,
+                  std::size_t const local_matrix_size,
+                  ConstructorArgs&&... args) {
+            return LADataIntfPtr{new LAData<ShapeFunction>{
+                e, local_matrix_size, std::forward<ConstructorArgs>(args)...}};
+        };
+    }
+
+    /// Returns nullptr for shape functions whose dimensions are less than the
+    /// global dimension.
+    template <typename ShapeFunction>
+    static LADataBuilder makeLocalAssemblerBuilder(std::false_type*)
+    {
+        return nullptr;
+    }
+};
+
+}  // namespace ThermoMechanics
+}  // namespace ProcessLib
+
+#undef ENABLED_ELEMENT_TYPE_SIMPLEX
+#undef ENABLED_ELEMENT_TYPE_CUBOID
+#undef ENABLED_ELEMENT_TYPE_PYRAMID
+#undef ENABLED_ELEMENT_TYPE_PRISM
+#undef ENABLED_ELEMENT_TYPE_TRI
+#undef ENABLED_ELEMENT_TYPE_QUAD
+#undef OGS_ENABLED_ELEMENTS
diff --git a/ProcessLib/ThermoMechanics/Tests.cmake b/ProcessLib/ThermoMechanics/Tests.cmake
new file mode 100644
index 00000000000..4236f2a2cca
--- /dev/null
+++ b/ProcessLib/ThermoMechanics/Tests.cmake
@@ -0,0 +1,16 @@
+AddTest(
+    NAME 3D_ThermoElastic_Stress_Analysis
+    PATH ThermoMechanics
+    EXECUTABLE ogs
+    EXECUTABLE_ARGS cube_1e3.prj
+    WRAPPER time
+    TESTER vtkdiff
+    REQUIREMENTS NOT (OGS_USE_LIS OR OGS_USE_MPI)
+    ABSTOL 1e-10 RELTOL 1e-12
+    DIFF_DATA
+    stress_analytical.vtu cube_1e3_tm_pcs_0_ts_80_t_72000.000000.vtu sigma_xx sigma_xx
+    stress_analytical.vtu cube_1e3_tm_pcs_0_ts_80_t_72000.000000.vtu sigma_yy sigma_yy
+    stress_analytical.vtu cube_1e3_tm_pcs_0_ts_80_t_72000.000000.vtu sigma_zz sigma_zz
+    expected_cube_1e3_tm_pcs_0_ts_80_t_72000.000000.vtu cube_1e3_tm_pcs_0_ts_80_t_72000.000000.vtu displacement displacement
+    expected_cube_1e3_tm_pcs_0_ts_80_t_72000.000000.vtu cube_1e3_tm_pcs_0_ts_80_t_72000.000000.vtu temperature temperature
+)
diff --git a/ProcessLib/ThermoMechanics/ThermoMechanicsFEM.h b/ProcessLib/ThermoMechanics/ThermoMechanicsFEM.h
new file mode 100644
index 00000000000..48acb1e296f
--- /dev/null
+++ b/ProcessLib/ThermoMechanics/ThermoMechanicsFEM.h
@@ -0,0 +1,558 @@
+/**
+ * \copyright
+ * Copyright (c) 2012-2017, 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 <memory>
+#include <vector>
+
+#include "MaterialLib/SolidModels/KelvinVector.h"
+#include "MaterialLib/SolidModels/LinearElasticIsotropic.h"
+#include "MathLib/LinAlg/Eigen/EigenMapTools.h"
+#include "NumLib/Extrapolation/ExtrapolatableElement.h"
+#include "NumLib/Fem/FiniteElement/TemplateIsoparametric.h"
+#include "NumLib/Fem/ShapeMatrixPolicy.h"
+#include "NumLib/Function/Interpolation.h"
+#include "ProcessLib/Deformation/BMatrixPolicy.h"
+#include "ProcessLib/Deformation/LinearBMatrix.h"
+#include "ProcessLib/LocalAssemblerInterface.h"
+#include "ProcessLib/LocalAssemblerTraits.h"
+#include "ProcessLib/Parameter/Parameter.h"
+#include "ProcessLib/Utils/InitShapeMatrices.h"
+
+#include "ThermoMechanicsProcessData.h"
+
+namespace ProcessLib
+{
+namespace ThermoMechanics
+{
+template <typename BMatricesType, typename ShapeMatrixType, int DisplacementDim>
+struct IntegrationPointData final
+{
+    explicit IntegrationPointData(
+        MaterialLib::Solids::MechanicsBase<DisplacementDim>& solid_material)
+        : solid_material(solid_material),
+          material_state_variables(
+              solid_material.createMaterialStateVariables())
+    {
+    }
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+    // The default generated move-ctor is correctly generated for other
+    // compilers.
+    explicit IntegrationPointData(IntegrationPointData&& other)
+        : sigma(std::move(other.sigma)),
+          sigma_prev(std::move(other.sigma_prev)),
+          eps(std::move(other.eps)),
+          eps_m(std::move(other.eps_m)),
+          eps_m_prev(std::move(other.eps_m_prev)),
+          solid_material(other.solid_material),
+          material_state_variables(std::move(other.material_state_variables)),
+          integration_weight(std::move(other.integration_weight)),
+    {
+    }
+#endif  // _MSC_VER
+
+    typename ShapeMatrixType::NodalRowVectorType N;
+    typename ShapeMatrixType::GlobalDimNodalMatrixType dNdx;
+    typename BMatricesType::KelvinVectorType sigma, sigma_prev;
+    typename BMatricesType::KelvinVectorType eps;
+    typename BMatricesType::KelvinVectorType eps_m, eps_m_prev;
+
+    MaterialLib::Solids::MechanicsBase<DisplacementDim>& solid_material;
+    std::unique_ptr<typename MaterialLib::Solids::MechanicsBase<
+        DisplacementDim>::MaterialStateVariables>
+        material_state_variables;
+
+    double integration_weight;
+
+    void pushBackState()
+    {
+        eps_m_prev = eps_m;
+        sigma_prev = sigma;
+        material_state_variables->pushBackState();
+    }
+
+    static const int kelvin_vector_size =
+        KelvinVectorDimensions<DisplacementDim>::value;
+    using Invariants = MaterialLib::SolidModels::Invariants<kelvin_vector_size>;
+
+    template <typename DisplacementVectorType>
+    typename BMatricesType::KelvinMatrixType updateConstitutiveRelation(
+        double const t,
+        SpatialPosition const& x_position,
+        double const dt,
+        DisplacementVectorType const& u,
+        double const linear_thermal_strain)
+    {
+        // assume isotropic thermal expansion
+        eps_m.noalias() = eps - linear_thermal_strain * Invariants::identity2;
+        auto&& solution = solid_material.integrateStress(
+            t, x_position, dt, eps_m_prev, eps_m, sigma_prev,
+            *material_state_variables);
+
+        if (!solution)
+            OGS_FATAL("Computation of local constitutive relation failed.");
+
+        KelvinMatrixType<DisplacementDim> C;
+        std::tie(sigma, material_state_variables, C) = std::move(*solution);
+
+        return C;
+    }
+};
+
+/// Used by for extrapolation of the integration point values. It is ordered
+/// (and stored) by integration points.
+template <typename ShapeMatrixType>
+struct SecondaryData
+{
+    std::vector<ShapeMatrixType> N;
+};
+
+struct ThermoMechanicsLocalAssemblerInterface
+    : public ProcessLib::LocalAssemblerInterface,
+      public NumLib::ExtrapolatableElement
+{
+    virtual std::vector<double> const& getIntPtSigmaXX(
+        std::vector<double>& cache) const = 0;
+
+    virtual std::vector<double> const& getIntPtSigmaYY(
+        std::vector<double>& cache) const = 0;
+
+    virtual std::vector<double> const& getIntPtSigmaZZ(
+        std::vector<double>& cache) const = 0;
+
+    virtual std::vector<double> const& getIntPtSigmaXY(
+        std::vector<double>& cache) const = 0;
+
+    virtual std::vector<double> const& getIntPtSigmaXZ(
+        std::vector<double>& cache) const = 0;
+
+    virtual std::vector<double> const& getIntPtSigmaYZ(
+        std::vector<double>& cache) const = 0;
+
+    virtual std::vector<double> const& getIntPtEpsilonXX(
+        std::vector<double>& cache) const = 0;
+
+    virtual std::vector<double> const& getIntPtEpsilonYY(
+        std::vector<double>& cache) const = 0;
+
+    virtual std::vector<double> const& getIntPtEpsilonZZ(
+        std::vector<double>& cache) const = 0;
+
+    virtual std::vector<double> const& getIntPtEpsilonXY(
+        std::vector<double>& cache) const = 0;
+
+    virtual std::vector<double> const& getIntPtEpsilonXZ(
+        std::vector<double>& cache) const = 0;
+
+    virtual std::vector<double> const& getIntPtEpsilonYZ(
+        std::vector<double>& cache) const = 0;
+};
+
+template <typename ShapeFunction, typename IntegrationMethod,
+          int DisplacementDim>
+class ThermoMechanicsLocalAssembler
+    : public ThermoMechanicsLocalAssemblerInterface
+{
+public:
+    using ShapeMatricesType =
+        ShapeMatrixPolicyType<ShapeFunction, DisplacementDim>;
+
+    // Types for displacement.
+    // (Higher order elements = ShapeFunction).
+    using ShapeMatrices = typename ShapeMatricesType::ShapeMatrices;
+    using BMatricesType = BMatrixPolicyType<ShapeFunction, DisplacementDim>;
+
+    using NodalForceVectorType = typename BMatricesType::NodalForceVectorType;
+    using RhsVector = typename ShapeMatricesType::template VectorType<
+        ShapeFunction::NPOINTS + ShapeFunction::NPOINTS * DisplacementDim>;
+    using JacobianMatrix = typename ShapeMatricesType::template MatrixType<
+        ShapeFunction::NPOINTS + ShapeFunction::NPOINTS * DisplacementDim,
+        ShapeFunction::NPOINTS + ShapeFunction::NPOINTS * DisplacementDim>;
+
+    ThermoMechanicsLocalAssembler(ThermoMechanicsLocalAssembler const&) =
+        delete;
+    ThermoMechanicsLocalAssembler(ThermoMechanicsLocalAssembler&&) = delete;
+
+    ThermoMechanicsLocalAssembler(
+        MeshLib::Element const& e,
+        std::size_t const /*local_matrix_size*/,
+        bool const is_axially_symmetric,
+        unsigned const integration_order,
+        ThermoMechanicsProcessData<DisplacementDim>& process_data)
+        : _process_data(process_data),
+          _integration_method(integration_order),
+          _element(e),
+          _is_axially_symmetric(is_axially_symmetric)
+    {
+        unsigned const n_integration_points =
+            _integration_method.getNumberOfPoints();
+
+        _ip_data.reserve(n_integration_points);
+        _secondary_data.N.resize(n_integration_points);
+
+        auto const shape_matrices =
+            initShapeMatrices<ShapeFunction, ShapeMatricesType,
+                              IntegrationMethod, DisplacementDim>(
+                e, is_axially_symmetric, _integration_method);
+
+        for (unsigned ip = 0; ip < n_integration_points; ip++)
+        {
+            // displacement (subscript u)
+            _ip_data.emplace_back(*_process_data.material);
+            auto& ip_data = _ip_data[ip];
+            _ip_data[ip].integration_weight =
+                _integration_method.getWeightedPoint(ip).getWeight() *
+                shape_matrices[ip].integralMeasure * shape_matrices[ip].detJ;
+
+            ip_data.sigma.resize(kelvin_vector_size);
+            ip_data.sigma_prev.resize(kelvin_vector_size);
+            ip_data.eps.resize(kelvin_vector_size);
+            ip_data.eps_m.resize(kelvin_vector_size);
+            ip_data.eps_m_prev.resize(kelvin_vector_size);
+
+            ip_data.N = shape_matrices[ip].N;
+            ip_data.dNdx = shape_matrices[ip].dNdx;
+
+            _secondary_data.N[ip] = shape_matrices[ip].N;
+        }
+    }
+
+    void assemble(double const /*t*/, std::vector<double> const& /*local_x*/,
+                  std::vector<double>& /*local_M_data*/,
+                  std::vector<double>& /*local_K_data*/,
+                  std::vector<double>& /*local_rhs_data*/) override
+    {
+        OGS_FATAL(
+            "ThermoMechanicsLocalAssembler: assembly without jacobian is not "
+            "implemented.");
+    }
+
+    void assembleWithJacobian(double const t,
+                              std::vector<double> const& local_x,
+                              std::vector<double> const& local_xdot,
+                              const double /*dxdot_dx*/, const double /*dx_dx*/,
+                              std::vector<double>& /*local_M_data*/,
+                              std::vector<double>& /*local_K_data*/,
+                              std::vector<double>& local_rhs_data,
+                              std::vector<double>& local_Jac_data) override
+    {
+        auto const local_matrix_size = local_x.size();
+        assert(local_matrix_size == temperature_size + displacement_size);
+
+        auto T = Eigen::Map<typename ShapeMatricesType::template VectorType<
+            temperature_size> const>(local_x.data() + temperature_index,
+                                     temperature_size);
+
+        auto u = Eigen::Map<typename ShapeMatricesType::template VectorType<
+            displacement_size> const>(local_x.data() + displacement_index,
+                                      displacement_size);
+
+        auto T_dot = Eigen::Map<typename ShapeMatricesType::template VectorType<
+            temperature_size> const>(local_xdot.data() + temperature_index,
+                                     temperature_size);
+
+        auto local_Jac = MathLib::createZeroedMatrix<JacobianMatrix>(
+            local_Jac_data, local_matrix_size, local_matrix_size);
+
+        auto local_rhs = MathLib::createZeroedVector<RhsVector>(
+            local_rhs_data, local_matrix_size);
+
+        typename ShapeMatricesType::template MatrixType<displacement_size,
+                                                        temperature_size>
+            KuT;
+        KuT.setZero(displacement_size, temperature_size);
+
+        typename ShapeMatricesType::NodalMatrixType KTT;
+        KTT.setZero(temperature_size, temperature_size);
+
+        typename ShapeMatricesType::NodalMatrixType DTT;
+        DTT.setZero(temperature_size, temperature_size);
+
+        double const& dt = _process_data.dt;
+
+        SpatialPosition x_position;
+        x_position.setElementID(_element.getID());
+
+        unsigned const n_integration_points =
+            _integration_method.getNumberOfPoints();
+        for (unsigned ip = 0; ip < n_integration_points; ip++)
+        {
+            x_position.setIntegrationPoint(ip);
+            auto const& w = _ip_data[ip].integration_weight;
+
+            auto const& dNdx = _ip_data[ip].dNdx;
+            auto const& N = _ip_data[ip].N;
+
+            auto const x_coord =
+                interpolateXCoordinate<ShapeFunction, ShapeMatricesType>(
+                    _element, N);
+            auto const& B = LinearBMatrix::computeBMatrix<
+                DisplacementDim, ShapeFunction::NPOINTS,
+                typename BMatricesType::BMatrixType>(dNdx, N, x_coord,
+                                                     _is_axially_symmetric);
+            auto const& sigma = _ip_data[ip].sigma;
+            auto& eps = _ip_data[ip].eps;
+
+            double const delta_T =
+                N.dot(T) - _process_data.reference_temperature;
+            // calculate thermally induced strain
+            // assume isotropic thermal expansion
+            auto const alpha =
+                _process_data.linear_thermal_expansion_coefficient(
+                    t, x_position)[0];
+            double const linear_thermal_strain = alpha * delta_T;
+
+            //
+            // displacement equation, displacement part
+            //
+            eps.noalias() = B * u;
+            auto C = _ip_data[ip].updateConstitutiveRelation(
+                t, x_position, dt, u, linear_thermal_strain);
+
+            local_Jac
+                .template block<displacement_size, displacement_size>(
+                    displacement_index, displacement_index)
+                .noalias() += B.transpose() * C * B * w;
+
+            typename ShapeMatricesType::template MatrixType<DisplacementDim,
+                                                            displacement_size>
+                N_u = ShapeMatricesType::template MatrixType<
+                    DisplacementDim,
+                    displacement_size>::Zero(DisplacementDim,
+                                             displacement_size);
+
+            for (int i = 0; i < DisplacementDim; ++i)
+                N_u.template block<1, displacement_size / DisplacementDim>(
+                       i, i * displacement_size / DisplacementDim)
+                    .noalias() = N;
+
+            using Invariants =
+                MaterialLib::SolidModels::Invariants<kelvin_vector_size>;
+
+            // calculate real density
+            auto const rho_sr = _process_data.solid_density(t, x_position)[0];
+            double const rho_s = rho_sr * (1 - 3 * linear_thermal_strain);
+
+            auto const& b = _process_data.specific_body_force;
+            local_rhs
+                .template block<displacement_size, 1>(displacement_index, 0)
+                .noalias() -=
+                (B.transpose() * sigma - N_u.transpose() * rho_s * b) * w;
+            //
+            // displacement equation, temperature part
+            //
+            KuT.noalias() +=
+                B.transpose() * C * alpha * Invariants::identity2 * N * w;
+
+            //
+            // temperature equation, temperature part;
+            //
+            auto const lambda =
+                _process_data.thermal_conductivity(t, x_position)[0];
+            KTT.noalias() += dNdx.transpose() * lambda * dNdx * w;
+
+            auto const c =
+                _process_data.specific_heat_capacity(t, x_position)[0];
+            DTT.noalias() += N.transpose() * rho_s * c * N * w;
+        }
+        // temperature equation, temperature part
+        local_Jac
+            .template block<temperature_size, temperature_size>(
+                temperature_index, temperature_index)
+            .noalias() += KTT + DTT / dt;
+
+        // displacement equation, temperature part
+        local_Jac
+            .template block<displacement_size, temperature_size>(
+                displacement_index, temperature_index)
+            .noalias() -= KuT;
+
+        local_rhs.template block<temperature_size, 1>(temperature_index, 0)
+            .noalias() -= KTT * T + DTT * T_dot;
+    }
+
+    void preTimestepConcrete(std::vector<double> const& /*local_x*/,
+                             double const /*t*/,
+                             double const /*delta_t*/) override
+    {
+        unsigned const n_integration_points =
+            _integration_method.getNumberOfPoints();
+
+        for (unsigned ip = 0; ip < n_integration_points; ip++)
+        {
+            _ip_data[ip].pushBackState();
+        }
+    }
+
+    Eigen::Map<const Eigen::RowVectorXd> getShapeMatrix(
+        const unsigned integration_point) const override
+    {
+        auto const& N = _secondary_data.N[integration_point];
+
+        // assumes N is stored contiguously in memory
+        return Eigen::Map<const Eigen::RowVectorXd>(N.data(), N.size());
+    }
+
+    std::vector<double> const& getIntPtSigmaXX(
+        std::vector<double>& cache) const override
+    {
+        return getIntPtSigma(cache, 0);
+    }
+
+    std::vector<double> const& getIntPtSigmaYY(
+        std::vector<double>& cache) const override
+    {
+        return getIntPtSigma(cache, 1);
+    }
+
+    std::vector<double> const& getIntPtSigmaZZ(
+        std::vector<double>& cache) const override
+    {
+        return getIntPtSigma(cache, 2);
+    }
+
+    std::vector<double> const& getIntPtSigmaXY(
+        std::vector<double>& cache) const override
+    {
+        return getIntPtSigma(cache, 3);
+    }
+
+    std::vector<double> const& getIntPtSigmaYZ(
+        std::vector<double>& cache) const override
+    {
+        assert(DisplacementDim == 3);
+        return getIntPtSigma(cache, 4);
+    }
+
+    std::vector<double> const& getIntPtSigmaXZ(
+        std::vector<double>& cache) const override
+    {
+        assert(DisplacementDim == 3);
+        return getIntPtSigma(cache, 5);
+    }
+
+    std::vector<double> const& getIntPtEpsilonXX(
+        std::vector<double>& cache) const override
+    {
+        return getIntPtEpsilon(cache, 0);
+    }
+
+    std::vector<double> const& getIntPtEpsilonYY(
+        std::vector<double>& cache) const override
+    {
+        return getIntPtEpsilon(cache, 1);
+    }
+
+    std::vector<double> const& getIntPtEpsilonZZ(
+        std::vector<double>& cache) const override
+    {
+        return getIntPtEpsilon(cache, 2);
+    }
+
+    std::vector<double> const& getIntPtEpsilonXY(
+        std::vector<double>& cache) const override
+    {
+        return getIntPtEpsilon(cache, 3);
+    }
+
+    std::vector<double> const& getIntPtEpsilonYZ(
+        std::vector<double>& cache) const override
+    {
+        assert(DisplacementDim == 3);
+        return getIntPtEpsilon(cache, 4);
+    }
+
+    std::vector<double> const& getIntPtEpsilonXZ(
+        std::vector<double>& cache) const override
+    {
+        assert(DisplacementDim == 3);
+        return getIntPtEpsilon(cache, 5);
+    }
+
+private:
+    std::vector<double> const& getIntPtSigma(std::vector<double>& cache,
+                                             std::size_t const component) const
+    {
+        cache.clear();
+        cache.reserve(_ip_data.size());
+
+        for (auto const& ip_data : _ip_data)
+        {
+            if (component < 3)  // xx, yy, zz components
+                cache.push_back(ip_data.sigma[component]);
+            else  // mixed xy, yz, xz components
+                cache.push_back(ip_data.sigma[component] / std::sqrt(2));
+        }
+
+        return cache;
+    }
+    std::vector<double> const& getIntPtEpsilon(
+        std::vector<double>& cache, std::size_t const component) const
+    {
+        cache.clear();
+        cache.reserve(_ip_data.size());
+
+        for (auto const& ip_data : _ip_data)
+        {
+            if (component < 3)  // xx, yy, zz components
+                cache.push_back(ip_data.eps[component]);
+            else  // mixed xy, yz, xz components
+                cache.push_back(ip_data.eps[component] / std::sqrt(2));
+        }
+
+        return cache;
+    }
+
+    ThermoMechanicsProcessData<DisplacementDim>& _process_data;
+
+    std::vector<IntegrationPointData<BMatricesType, ShapeMatricesType,
+                                     DisplacementDim>> _ip_data;
+
+    IntegrationMethod _integration_method;
+    MeshLib::Element const& _element;
+    bool const _is_axially_symmetric;
+    SecondaryData<typename ShapeMatrices::ShapeType> _secondary_data;
+
+    static const int temperature_index = 0;
+    static const int temperature_size = ShapeFunction::NPOINTS;
+    static const int displacement_index = ShapeFunction::NPOINTS;
+    static const int displacement_size =
+        ShapeFunction::NPOINTS * DisplacementDim;
+    static const int kelvin_vector_size =
+        KelvinVectorDimensions<DisplacementDim>::value;
+};
+
+template <typename ShapeFunction, typename IntegrationMethod,
+          unsigned GlobalDim, int DisplacementDim>
+class LocalAssemblerData final
+    : public ThermoMechanicsLocalAssembler<ShapeFunction, IntegrationMethod,
+                                           DisplacementDim>
+{
+public:
+    LocalAssemblerData(LocalAssemblerData const&) = delete;
+    LocalAssemblerData(LocalAssemblerData&&) = delete;
+
+    LocalAssemblerData(
+        MeshLib::Element const& e,
+        std::size_t const local_matrix_size,
+        bool is_axially_symmetric,
+        unsigned const integration_order,
+        ThermoMechanicsProcessData<DisplacementDim>& process_data)
+        : ThermoMechanicsLocalAssembler<ShapeFunction, IntegrationMethod,
+                                        DisplacementDim>(
+              e, local_matrix_size, is_axially_symmetric, integration_order,
+              process_data)
+    {
+    }
+};
+
+}  // namespace ThermoMechanics
+}  // namespace ProcessLib
diff --git a/ProcessLib/ThermoMechanics/ThermoMechanicsProcess-fwd.h b/ProcessLib/ThermoMechanics/ThermoMechanicsProcess-fwd.h
new file mode 100644
index 00000000000..fc48ba4a62a
--- /dev/null
+++ b/ProcessLib/ThermoMechanics/ThermoMechanicsProcess-fwd.h
@@ -0,0 +1,15 @@
+/**
+ * \copyright
+ * Copyright (c) 2012-2017, 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 "ThermoMechanicsProcess.h"
+
+extern template class ProcessLib::ThermoMechanics::ThermoMechanicsProcess<2>;
+extern template class ProcessLib::ThermoMechanics::ThermoMechanicsProcess<3>;
diff --git a/ProcessLib/ThermoMechanics/ThermoMechanicsProcess.cpp b/ProcessLib/ThermoMechanics/ThermoMechanicsProcess.cpp
new file mode 100644
index 00000000000..55fddd02e3d
--- /dev/null
+++ b/ProcessLib/ThermoMechanics/ThermoMechanicsProcess.cpp
@@ -0,0 +1,21 @@
+/**
+ * \copyright
+ * Copyright (c) 2012-2017, 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 "ThermoMechanicsProcess.h"
+#include "ThermoMechanicsProcess-fwd.h"
+
+namespace ProcessLib
+{
+namespace ThermoMechanics
+{
+template class ThermoMechanicsProcess<2>;
+template class ThermoMechanicsProcess<3>;
+
+}  // namespace ThermoMechanics
+}  // namespace ProcessLib
diff --git a/ProcessLib/ThermoMechanics/ThermoMechanicsProcess.h b/ProcessLib/ThermoMechanics/ThermoMechanicsProcess.h
new file mode 100644
index 00000000000..7e6c4c290da
--- /dev/null
+++ b/ProcessLib/ThermoMechanics/ThermoMechanicsProcess.h
@@ -0,0 +1,220 @@
+/**
+ * \copyright
+ * Copyright (c) 2012-2017, 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 <cassert>
+
+#include "NumLib/DOF/DOFTableUtil.h"
+#include "ProcessLib/Process.h"
+#include "ProcessLib/ThermoMechanics/CreateLocalAssemblers.h"
+
+#include "ThermoMechanicsFEM.h"
+#include "ThermoMechanicsProcessData.h"
+
+namespace ProcessLib
+{
+namespace ThermoMechanics
+{
+template <int DisplacementDim>
+class ThermoMechanicsProcess final : public Process
+{
+    using Base = Process;
+
+public:
+    ThermoMechanicsProcess(
+        MeshLib::Mesh& mesh,
+        std::unique_ptr<ProcessLib::AbstractJacobianAssembler>&&
+            jacobian_assembler,
+        std::vector<std::unique_ptr<ParameterBase>> const& parameters,
+        unsigned const integration_order,
+        std::vector<std::reference_wrapper<ProcessVariable>>&&
+            process_variables,
+        ThermoMechanicsProcessData<DisplacementDim>&& process_data,
+        SecondaryVariableCollection&& secondary_variables,
+        NumLib::NamedFunctionCaller&& named_function_caller)
+        : Process(mesh, std::move(jacobian_assembler), parameters,
+                  integration_order, std::move(process_variables),
+                  std::move(secondary_variables),
+                  std::move(named_function_caller)),
+          _process_data(std::move(process_data))
+    {
+    }
+
+    //! \name ODESystem interface
+    //! @{
+
+    bool isLinear() const override { return false; }
+    //! @}
+
+private:
+    using LocalAssemblerInterface = ThermoMechanicsLocalAssemblerInterface;
+
+    void initializeConcreteProcess(
+        NumLib::LocalToGlobalIndexMap const& dof_table,
+        MeshLib::Mesh const& mesh,
+        unsigned const integration_order) override
+    {
+        ProcessLib::ThermoMechanics::createLocalAssemblers<DisplacementDim,
+                                                           LocalAssemblerData>(
+            mesh.getDimension(), mesh.getElements(), dof_table,
+            _local_assemblers, mesh.isAxiallySymmetric(), integration_order,
+            _process_data);
+
+        // TODO move the two data members somewhere else.
+        // for extrapolation of secondary variables
+        std::vector<MeshLib::MeshSubsets> all_mesh_subsets_single_component;
+        all_mesh_subsets_single_component.emplace_back(
+            _mesh_subset_all_nodes.get());
+        _local_to_global_index_map_single_component.reset(
+            new NumLib::LocalToGlobalIndexMap(
+                std::move(all_mesh_subsets_single_component),
+                // by location order is needed for output
+                NumLib::ComponentOrder::BY_LOCATION));
+
+        Base::_secondary_variables.addSecondaryVariable(
+            "sigma_xx", 1,
+            makeExtrapolator(
+                getExtrapolator(), _local_assemblers,
+                &ThermoMechanicsLocalAssemblerInterface::getIntPtSigmaXX));
+
+        Base::_secondary_variables.addSecondaryVariable(
+            "sigma_yy", 1,
+            makeExtrapolator(
+                getExtrapolator(), _local_assemblers,
+                &ThermoMechanicsLocalAssemblerInterface::getIntPtSigmaYY));
+
+        Base::_secondary_variables.addSecondaryVariable(
+            "sigma_zz", 1,
+            makeExtrapolator(
+                getExtrapolator(), _local_assemblers,
+                &ThermoMechanicsLocalAssemblerInterface::getIntPtSigmaZZ));
+
+        Base::_secondary_variables.addSecondaryVariable(
+            "sigma_xy", 1,
+            makeExtrapolator(
+                getExtrapolator(), _local_assemblers,
+                &ThermoMechanicsLocalAssemblerInterface::getIntPtSigmaXY));
+
+        if (DisplacementDim == 3)
+        {
+            Base::_secondary_variables.addSecondaryVariable(
+                "sigma_xz", 1,
+                makeExtrapolator(
+                    getExtrapolator(), _local_assemblers,
+                    &ThermoMechanicsLocalAssemblerInterface::getIntPtSigmaXZ));
+
+            Base::_secondary_variables.addSecondaryVariable(
+                "sigma_yz", 1,
+                makeExtrapolator(
+                    getExtrapolator(), _local_assemblers,
+                    &ThermoMechanicsLocalAssemblerInterface::getIntPtSigmaYZ));
+        }
+        Base::_secondary_variables.addSecondaryVariable(
+            "epsilon_xx", 1,
+            makeExtrapolator(
+                getExtrapolator(), _local_assemblers,
+                &ThermoMechanicsLocalAssemblerInterface::getIntPtEpsilonXX));
+
+        Base::_secondary_variables.addSecondaryVariable(
+            "epsilon_yy", 1,
+            makeExtrapolator(
+                getExtrapolator(), _local_assemblers,
+                &ThermoMechanicsLocalAssemblerInterface::getIntPtEpsilonYY));
+
+        Base::_secondary_variables.addSecondaryVariable(
+            "epsilon_zz", 1,
+            makeExtrapolator(
+                getExtrapolator(), _local_assemblers,
+                &ThermoMechanicsLocalAssemblerInterface::getIntPtEpsilonZZ));
+
+        Base::_secondary_variables.addSecondaryVariable(
+            "epsilon_xy", 1,
+            makeExtrapolator(
+                getExtrapolator(), _local_assemblers,
+                &ThermoMechanicsLocalAssemblerInterface::getIntPtEpsilonXY));
+        if (DisplacementDim == 3)
+        {
+            Base::_secondary_variables.addSecondaryVariable(
+                "epsilon_yz", 1,
+                makeExtrapolator(getExtrapolator(), _local_assemblers,
+                                 &ThermoMechanicsLocalAssemblerInterface::
+                                     getIntPtEpsilonYZ));
+
+            Base::_secondary_variables.addSecondaryVariable(
+                "epsilon_xz", 1,
+                makeExtrapolator(getExtrapolator(), _local_assemblers,
+                                 &ThermoMechanicsLocalAssemblerInterface::
+                                     getIntPtEpsilonXZ));
+        }
+    }
+
+    void assembleConcreteProcess(
+        const double t, GlobalVector const& x, GlobalMatrix& M, GlobalMatrix& K,
+        GlobalVector& b, StaggeredCouplingTerm const& coupling_term) override
+    {
+        DBUG("Assemble ThermoMechanicsProcess.");
+
+        // Call global assembler for each local assembly item.
+        GlobalExecutor::executeMemberDereferenced(
+            _global_assembler, &VectorMatrixAssembler::assemble,
+            _local_assemblers, *_local_to_global_index_map, t, x, M, K, b,
+            coupling_term);
+    }
+
+    void assembleWithJacobianConcreteProcess(
+        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,
+        StaggeredCouplingTerm const& coupling_term) override
+    {
+        DBUG("AssembleJacobian ThermoMechanicsProcess.");
+
+        // Call global assembler for each local assembly item.
+        GlobalExecutor::executeMemberDereferenced(
+            _global_assembler, &VectorMatrixAssembler::assembleWithJacobian,
+            _local_assemblers, *_local_to_global_index_map, t, x, xdot,
+            dxdot_dx, dx_dx, M, K, b, Jac, coupling_term);
+    }
+
+    void preTimestepConcreteProcess(GlobalVector const& x, double const t,
+                                    double const dt) override
+    {
+        DBUG("PreTimestep ThermoMechanicsProcess.");
+
+        _process_data.dt = dt;
+        _process_data.t = t;
+
+        GlobalExecutor::executeMemberOnDereferenced(
+            &ThermoMechanicsLocalAssemblerInterface::preTimestep,
+            _local_assemblers, *_local_to_global_index_map, x, t, dt);
+    }
+
+    void postTimestepConcreteProcess(GlobalVector const& x) override
+    {
+        DBUG("PostTimestep ThermoMechanicsProcess.");
+
+        GlobalExecutor::executeMemberOnDereferenced(
+            &ThermoMechanicsLocalAssemblerInterface::postTimestep,
+            _local_assemblers, *_local_to_global_index_map, x);
+    }
+
+private:
+    std::vector<MeshLib::Node*> _base_nodes;
+    std::unique_ptr<MeshLib::MeshSubset const> _mesh_subset_base_nodes;
+    ThermoMechanicsProcessData<DisplacementDim> _process_data;
+
+    std::vector<std::unique_ptr<LocalAssemblerInterface>> _local_assemblers;
+
+    std::unique_ptr<NumLib::LocalToGlobalIndexMap>
+        _local_to_global_index_map_single_component;
+};
+
+}  // namespace ThermoMechanics
+}  // namespace ProcessLib
diff --git a/ProcessLib/ThermoMechanics/ThermoMechanicsProcessData.h b/ProcessLib/ThermoMechanics/ThermoMechanicsProcessData.h
new file mode 100644
index 00000000000..432db7b1927
--- /dev/null
+++ b/ProcessLib/ThermoMechanics/ThermoMechanicsProcessData.h
@@ -0,0 +1,80 @@
+/**
+ * \copyright
+ * Copyright (c) 2012-2017, 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 MeshLib
+{
+class Element;
+}
+
+namespace ProcessLib
+{
+namespace ThermoMechanics
+{
+template <int DisplacementDim>
+struct ThermoMechanicsProcessData
+{
+    ThermoMechanicsProcessData(
+        std::unique_ptr<MaterialLib::Solids::MechanicsBase<DisplacementDim>>&&
+            material_,
+        Parameter<double> const& solid_density_,
+        Parameter<double> const& linear_thermal_expansion_coefficient_,
+        Parameter<double> const& specific_heat_capacity_,
+        Parameter<double> const& thermal_conductivity_,
+        double const reference_temperature_,
+        Eigen::Matrix<double, DisplacementDim, 1> const& specific_body_force_)
+        : material{std::move(material_)},
+          solid_density(solid_density_),
+          linear_thermal_expansion_coefficient(
+              linear_thermal_expansion_coefficient_),
+          specific_heat_capacity(specific_heat_capacity_),
+          thermal_conductivity(thermal_conductivity_),
+          reference_temperature(reference_temperature_),
+          specific_body_force(specific_body_force_)
+    {
+    }
+
+    ThermoMechanicsProcessData(ThermoMechanicsProcessData&& other)
+        : material{std::move(other.material)},
+          solid_density(other.solid_density),
+          linear_thermal_expansion_coefficient(
+              other.linear_thermal_expansion_coefficient),
+          specific_heat_capacity(other.specific_heat_capacity),
+          thermal_conductivity(other.thermal_conductivity),
+          reference_temperature(other.reference_temperature),
+          specific_body_force(other.specific_body_force),
+          dt(other.dt),
+          t(other.t)
+    {
+    }
+
+    //! Copies are forbidden.
+    ThermoMechanicsProcessData(ThermoMechanicsProcessData const&) = delete;
+
+    //! Assignments are not needed.
+    void operator=(ThermoMechanicsProcessData const&) = delete;
+
+    //! Assignments are not needed.
+    void operator=(ThermoMechanicsProcessData&&) = delete;
+
+    std::unique_ptr<MaterialLib::Solids::MechanicsBase<DisplacementDim>>
+        material;
+    Parameter<double> const& solid_density;
+    Parameter<double> const& linear_thermal_expansion_coefficient;
+    Parameter<double> const& specific_heat_capacity;
+    Parameter<double> const& thermal_conductivity;
+    double const reference_temperature;
+    Eigen::Matrix<double, DisplacementDim, 1> const specific_body_force;
+    double dt;
+    double t;
+};
+
+}  // namespace ThermoMechanics
+}  // namespace ProcessLib
-- 
GitLab