diff --git a/Applications/ApplicationsLib/ProjectData.cpp b/Applications/ApplicationsLib/ProjectData.cpp
index 4e6d2d880ff4a64b49f0bcaaa426eb225a3c5006..2408ccafed5d48a6753284401ef76d4b9a62a2ea 100644
--- a/Applications/ApplicationsLib/ProjectData.cpp
+++ b/Applications/ApplicationsLib/ProjectData.cpp
@@ -26,6 +26,7 @@
 #include "BaseLib/FileTools.h"
 
 #include "GeoLib/GEOObjects.h"
+#include "MaterialLib/MPL/CreateMedium.h"
 #include "MathLib/Curve/CreatePiecewiseLinearCurve.h"
 #include "MeshGeoToolsLib/ConstructMeshesFromGeometries.h"
 #include "MeshGeoToolsLib/CreateSearchLength.h"
@@ -233,6 +234,9 @@ ProjectData::ProjectData(BaseLib::ConfigTree const& project_config,
     //! \ogs_file_param{prj__process_variables}
     parseProcessVariables(project_config.getConfigSubtree("process_variables"));
 
+    //! \ogs_file_param{prj__media}
+    parseMedia(project_config.getConfigSubtreeOptional("media"));
+
     //! \ogs_file_param{prj__processes}
     parseProcesses(project_config.getConfigSubtree("processes"),
                    project_directory, output_directory);
@@ -309,6 +313,45 @@ void ProjectData::parseParameters(BaseLib::ConfigTree const& parameters_config)
         parameter->initialize(_parameters);
 }
 
+void ProjectData::parseMedia(
+        boost::optional<BaseLib::ConfigTree> const& media_config)
+{
+    if (!media_config)
+        return;
+
+    DBUG("Reading media:");
+
+    if (_mesh_vec.empty() || _mesh_vec[0] == nullptr)
+    {
+        ERR("A mesh is required to define medium materials.");
+        return;
+    }
+
+    for (auto const& medium_config :
+         //! \ogs_file_param{prj__media__medium}
+         media_config->getConfigSubtreeList("medium"))
+    {
+        //! \ogs_file_attr{prj__media__medium__id}
+        auto const material_id = medium_config.getConfigAttribute<int>("id", 0);
+
+        if (_media.find(material_id) != _media.end())
+        {
+            OGS_FATAL(
+                "Multiple media were specified for the same material id %d. "
+                "Keep in mind, that if no material id is specified, it is "
+                "assumed to be 0 by default.",
+                material_id);
+        }
+
+        _media[material_id] = MaterialPropertyLib::createMedium(medium_config);
+    }
+
+    if (_media.empty())
+    {
+        OGS_FATAL("No entity is found inside <media>.");
+    }
+}
+
 void ProjectData::parseProcesses(BaseLib::ConfigTree const& processes_config,
                                  std::string const& project_directory,
                                  std::string const& output_directory)
diff --git a/Applications/ApplicationsLib/ProjectData.h b/Applications/ApplicationsLib/ProjectData.h
index 8fc9248dd189444dcfa1532d09d7886603512644..1ce3fcbd01aaeb034879d09e77954ac1a6b54ae0 100644
--- a/Applications/ApplicationsLib/ProjectData.h
+++ b/Applications/ApplicationsLib/ProjectData.h
@@ -18,7 +18,9 @@
 #include <string>
 
 #include "BaseLib/ConfigTree.h"
+#include "MaterialLib/MPL/Medium.h"
 #include "MathLib/InterpolationAlgorithms/PiecewiseLinearInterpolation.h"
+
 #include "ProcessLib/Parameter/Parameter.h"
 #include "ProcessLib/Process.h"
 #include "ProcessLib/ProcessVariable.h"
@@ -89,6 +91,9 @@ private:
     /// Checks if a parameter has name tag.
     void parseParameters(BaseLib::ConfigTree const& parameters_config);
 
+    /// Parses media configuration and saves them in an object.
+    void parseMedia(boost::optional<BaseLib::ConfigTree> const& media_config);
+
     /// Parses the processes configuration and creates new processes for each
     /// process entry passing the corresponding subtree to the process
     /// constructor.
@@ -106,6 +111,7 @@ private:
 
     void parseCurves(boost::optional<BaseLib::ConfigTree> const& config);
 
+    std::unique_ptr<MaterialPropertyLib::Medium> _medium;
     std::vector<std::unique_ptr<MeshLib::Mesh>> _mesh_vec;
     std::map<std::string, std::unique_ptr<ProcessLib::Process>> _processes;
     std::vector<ProcessLib::ProcessVariable> _process_variables;
@@ -113,6 +119,8 @@ private:
     /// Buffer for each parameter config passed to the process.
     std::vector<std::unique_ptr<ProcessLib::ParameterBase>> _parameters;
 
+    std::map<int, std::unique_ptr<MaterialPropertyLib::Medium>> _media;
+
     /// The time loop used to solve this project's processes.
     std::unique_ptr<TimeLoop> _time_loop;
 
diff --git a/BaseLib/ConfigTree-impl.h b/BaseLib/ConfigTree-impl.h
index bb391cd4c2dccbb13bbdc8c05047b575676696c1..87ddab369adb363b933ea5b660af5425fdd7a8a4 100644
--- a/BaseLib/ConfigTree-impl.h
+++ b/BaseLib/ConfigTree-impl.h
@@ -27,6 +27,7 @@ public:
     Iterator begin() const { return _begin; }
     Iterator end()   const { return _end; }
     std::size_t size() const { return std::distance(_begin, _end); }
+    bool empty() const { return size() == 0; }
 
 private:
     Iterator _begin;
diff --git a/Documentation/ProjectFile/prj/media/i_media.md b/Documentation/ProjectFile/prj/media/i_media.md
new file mode 100644
index 0000000000000000000000000000000000000000..d82c2910650307c7373b9e05cea858d9c007eb4c
--- /dev/null
+++ b/Documentation/ProjectFile/prj/media/i_media.md
@@ -0,0 +1 @@
+Group of media.
diff --git a/Documentation/ProjectFile/prj/media/medium/a_id.md b/Documentation/ProjectFile/prj/media/medium/a_id.md
new file mode 100644
index 0000000000000000000000000000000000000000..75c19f5def9654250dbdd772ecf8ea4540275267
--- /dev/null
+++ b/Documentation/ProjectFile/prj/media/medium/a_id.md
@@ -0,0 +1,3 @@
+An optional id of the medium corresponding to the MaterialIDs. If not given id
+is set to 0. The id is used to distinguish different media based on the
+MaterialIDs.
diff --git a/Documentation/ProjectFile/prj/media/medium/i_medium.md b/Documentation/ProjectFile/prj/media/medium/i_medium.md
new file mode 100644
index 0000000000000000000000000000000000000000..45a640b0b23d529501f4630aecb3885383645660
--- /dev/null
+++ b/Documentation/ProjectFile/prj/media/medium/i_medium.md
@@ -0,0 +1 @@
+A specific medium with optional id corresponding to the MaterialIDs.
diff --git a/Documentation/ProjectFile/prj/media/medium/phases/i_phases.md b/Documentation/ProjectFile/prj/media/medium/phases/i_phases.md
new file mode 100644
index 0000000000000000000000000000000000000000..68a0c35fea904361b37e1c59948bf1495c1e7eeb
--- /dev/null
+++ b/Documentation/ProjectFile/prj/media/medium/phases/i_phases.md
@@ -0,0 +1 @@
+Group of phases.
diff --git a/Documentation/ProjectFile/prj/media/medium/phases/phase/components/component/i_component.md b/Documentation/ProjectFile/prj/media/medium/phases/phase/components/component/i_component.md
new file mode 100644
index 0000000000000000000000000000000000000000..2a2cf8838169ef01d71d6e54a609b6d83cde84e8
--- /dev/null
+++ b/Documentation/ProjectFile/prj/media/medium/phases/phase/components/component/i_component.md
@@ -0,0 +1 @@
+Pure chemical substance, or an invariable composition like air or wood.
diff --git a/Documentation/ProjectFile/prj/media/medium/phases/phase/components/component/properties b/Documentation/ProjectFile/prj/media/medium/phases/phase/components/component/properties
new file mode 120000
index 0000000000000000000000000000000000000000..8432643edc2e017d513dc8e210649029f32c0599
--- /dev/null
+++ b/Documentation/ProjectFile/prj/media/medium/phases/phase/components/component/properties
@@ -0,0 +1 @@
+../../../../../../../properties
\ No newline at end of file
diff --git a/Documentation/ProjectFile/prj/media/medium/phases/phase/components/component/t_name.md b/Documentation/ProjectFile/prj/media/medium/phases/phase/components/component/t_name.md
new file mode 100644
index 0000000000000000000000000000000000000000..ac823994317b8929d95b9c84ac23ed9d56d0bb05
--- /dev/null
+++ b/Documentation/ProjectFile/prj/media/medium/phases/phase/components/component/t_name.md
@@ -0,0 +1 @@
+Name of the substance.
diff --git a/Documentation/ProjectFile/prj/media/medium/phases/phase/components/i_components.md b/Documentation/ProjectFile/prj/media/medium/phases/phase/components/i_components.md
new file mode 100644
index 0000000000000000000000000000000000000000..ece92b8101f0eec7abe47cbce3c32386337fdaba
--- /dev/null
+++ b/Documentation/ProjectFile/prj/media/medium/phases/phase/components/i_components.md
@@ -0,0 +1 @@
+Set of components residing in current phase.
diff --git a/Documentation/ProjectFile/prj/media/medium/phases/phase/i_phase.md b/Documentation/ProjectFile/prj/media/medium/phases/phase/i_phase.md
new file mode 100644
index 0000000000000000000000000000000000000000..d8f9c5970b0f6d31b45bcb2f998a2e3b41b4f04e
--- /dev/null
+++ b/Documentation/ProjectFile/prj/media/medium/phases/phase/i_phase.md
@@ -0,0 +1 @@
+Coherent material with homogeneous properties.
diff --git a/Documentation/ProjectFile/prj/media/medium/phases/phase/properties b/Documentation/ProjectFile/prj/media/medium/phases/phase/properties
new file mode 120000
index 0000000000000000000000000000000000000000..6fb56412662ff2315d9d75b7d6d92774584b7563
--- /dev/null
+++ b/Documentation/ProjectFile/prj/media/medium/phases/phase/properties
@@ -0,0 +1 @@
+../../../../../properties
\ No newline at end of file
diff --git a/Documentation/ProjectFile/prj/media/medium/phases/phase/t_type.md b/Documentation/ProjectFile/prj/media/medium/phases/phase/t_type.md
new file mode 100644
index 0000000000000000000000000000000000000000..9580b58de9a3c6cb80592e748d3f25e73da37da0
--- /dev/null
+++ b/Documentation/ProjectFile/prj/media/medium/phases/phase/t_type.md
@@ -0,0 +1 @@
+One of "Gas", "Solid", "AqueousLiquid", and "NonAqueousLiquid".
diff --git a/Documentation/ProjectFile/prj/media/medium/properties b/Documentation/ProjectFile/prj/media/medium/properties
new file mode 120000
index 0000000000000000000000000000000000000000..0ec566f4704010cd949fb347131bd49664e213d6
--- /dev/null
+++ b/Documentation/ProjectFile/prj/media/medium/properties
@@ -0,0 +1 @@
+../../../properties
\ No newline at end of file
diff --git a/Documentation/ProjectFile/properties/i_properties.md b/Documentation/ProjectFile/properties/i_properties.md
new file mode 100644
index 0000000000000000000000000000000000000000..92f09a04176cafabf3ee139afb4ad12563a3e2e3
--- /dev/null
+++ b/Documentation/ProjectFile/properties/i_properties.md
@@ -0,0 +1 @@
+Group of properties for the current subsection like medium, phase, or component.
diff --git a/Documentation/ProjectFile/properties/property/Constant/c_Constant.md b/Documentation/ProjectFile/properties/property/Constant/c_Constant.md
new file mode 100644
index 0000000000000000000000000000000000000000..a6ab02476be3e115a6269f10feca41734af240c6
--- /dev/null
+++ b/Documentation/ProjectFile/properties/property/Constant/c_Constant.md
@@ -0,0 +1 @@
+A constant property with given value.
diff --git a/Documentation/ProjectFile/properties/property/Constant/t_value.md b/Documentation/ProjectFile/properties/property/Constant/t_value.md
new file mode 100644
index 0000000000000000000000000000000000000000..7111dc3dce4743e5884afeacf942558b07702e47
--- /dev/null
+++ b/Documentation/ProjectFile/properties/property/Constant/t_value.md
@@ -0,0 +1 @@
+A numeric value of one of the implemented data types as given in MaterialPropertyLib::PropertyDataType.
diff --git a/Documentation/ProjectFile/properties/property/i_property.md b/Documentation/ProjectFile/properties/property/i_property.md
new file mode 100644
index 0000000000000000000000000000000000000000..de198f700a3692e2d778b9aa5b832c512e455d03
--- /dev/null
+++ b/Documentation/ProjectFile/properties/property/i_property.md
@@ -0,0 +1 @@
+A constitutive property.
diff --git a/Documentation/ProjectFile/properties/property/t_name.md b/Documentation/ProjectFile/properties/property/t_name.md
new file mode 100644
index 0000000000000000000000000000000000000000..b99cb0cdbadef6db5ef8688aefa4425eecbb34a5
--- /dev/null
+++ b/Documentation/ProjectFile/properties/property/t_name.md
@@ -0,0 +1 @@
+One of the implemented properties as given in MaterialPropertyLib::PropertyType.
diff --git a/Documentation/ProjectFile/properties/property/t_type.md b/Documentation/ProjectFile/properties/property/t_type.md
new file mode 100644
index 0000000000000000000000000000000000000000..821856747c23c0e08662f52649fd39707b880a8f
--- /dev/null
+++ b/Documentation/ProjectFile/properties/property/t_type.md
@@ -0,0 +1 @@
+Either a "Constant" or a specific constitutive relationship type like "BilinearTemperaturePressure".
diff --git a/MaterialLib/CMakeLists.txt b/MaterialLib/CMakeLists.txt
index 2275711f39a896cee82aa70e7376f37346bf57b6..89f4ed54c2866a3cbf9a431e26c8930778f9a08d 100644
--- a/MaterialLib/CMakeLists.txt
+++ b/MaterialLib/CMakeLists.txt
@@ -13,6 +13,10 @@ append_source_files(SOURCES Fluid/SpecificHeatCapacity)
 append_source_files(SOURCES Fluid/ThermalConductivity)
 append_source_files(SOURCES Fluid/WaterVaporProperties)
 
+append_source_files(SOURCES MPL)
+append_source_files(SOURCES MPL/Properties)
+append_source_files(SOURCES MPL/Components)
+
 append_source_files(SOURCES PorousMedium)
 append_source_files(SOURCES PorousMedium/Porosity)
 append_source_files(SOURCES PorousMedium/Storage)
diff --git a/MaterialLib/MPL/Component.cpp b/MaterialLib/MPL/Component.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8d3135a9aa01d2b29549074331bb2dc721f928df
--- /dev/null
+++ b/MaterialLib/MPL/Component.cpp
@@ -0,0 +1,48 @@
+/**
+ * \file
+ * \author Norbert Grunwald
+ * \date   Sep 7, 2017
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 "Component.h"
+#include "Components/Components.h"
+#include "Properties/Properties.h"
+
+namespace MaterialPropertyLib
+{
+Component::Component()
+{
+    // Some properties can be initialized by other default properties:
+    _properties[PropertyType::name] = std::make_unique<Constant>("no_name");
+}
+
+Component::Component(std::string const& component_name,
+                     std::unique_ptr<PropertyArray>&& properties)
+{
+    // Some properties can be initialized by other default properties:
+    _properties[PropertyType::name] =
+        std::make_unique<Constant>(component_name);
+
+    if (properties)
+    {
+        overwriteExistingProperties(_properties, *properties);
+    }
+}
+
+Property const& Component::property(PropertyType const& p) const
+{
+    return *_properties[p];
+}
+
+std::string Component::name() const
+{
+    return boost::get<std::string>(_properties[PropertyType::name]->value());
+}
+}  // namespace MaterialPropertyLib
diff --git a/MaterialLib/MPL/Component.h b/MaterialLib/MPL/Component.h
new file mode 100644
index 0000000000000000000000000000000000000000..98c264760d265f5542697c74a4bb20f6de99397b
--- /dev/null
+++ b/MaterialLib/MPL/Component.h
@@ -0,0 +1,87 @@
+/**
+ * \file
+ * \author Norbert Grunwald
+ * \date   Sep 7, 2017
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 "Property.h"
+
+namespace MaterialPropertyLib
+{
+/// \brief This class defines components (substances).
+/// \details The Component class is a base class used for not further specified
+/// components. Components are specified by the property 'name'. For specified
+/// components we derive special classes from this class (for clarity they are
+/// located in the 'components' subfolder).
+class Component
+{
+public:
+    /// Default constructor of Component. This constructor is used
+    /// when the component is not specified via the 'name'-tag.
+    Component();
+
+    /// Constructor for a custom component
+    Component(std::string const& component_name,
+              std::unique_ptr<PropertyArray>&& properties);
+    virtual ~Component() = default;
+
+    /// A get-function for retrieving a certain property.
+    Property const& property(PropertyType const& /*p*/) const;
+
+    template <typename T>
+    T value(PropertyType const p) const
+    {
+        return property(p).template value<T>();
+    }
+
+    template <typename T>
+    T value(PropertyType const p, VariableArray const& variable_array) const
+    {
+        return property(p).template value<T>(variable_array);
+    }
+
+    template <typename T>
+    T dValue(PropertyType const p,
+             VariableArray const& variable_array,
+             Variables const variables) const
+    {
+        return property(p).template dValue<T>(variable_array, variables);
+    }
+
+    template <typename T>
+    T d2Value(PropertyType const p,
+              VariableArray const& variable_array,
+              Variables const variables1,
+              Variables const variables2) const
+    {
+        return property(p).template d2Value<T>(variable_array, variables1,
+                                               variables2);
+    }
+
+    std::string name() const;
+
+protected:
+    /// The property array of the component.
+    PropertyArray _properties;
+};
+
+/// Method for creating a new component based on the specified component name.
+///
+/// This function creates a new component based on the (optional) component name
+/// that is given in the prj-file.
+///
+/// The method evaluates the string in the 'name'-object and calls the
+/// constructors of the derived component classes (if found) or that of the base
+/// class (if no name is specified).
+std::unique_ptr<Component> newComponent(std::string const& component_name,
+                                        bool& isCustomComponent);
+
+}  // namespace MaterialPropertyLib
diff --git a/MaterialLib/MPL/Components/Components.h b/MaterialLib/MPL/Components/Components.h
new file mode 100644
index 0000000000000000000000000000000000000000..2d243ee441f549e1f051b7c7adae4b4133352d25
--- /dev/null
+++ b/MaterialLib/MPL/Components/Components.h
@@ -0,0 +1,15 @@
+/**
+ * \file
+ * \author Norbert Grunwald
+ * \date   Sep 7, 2017
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 "Water.h"
diff --git a/MaterialLib/MPL/Components/Water.cpp b/MaterialLib/MPL/Components/Water.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b51f36127e2bc6753ed4b36fe38409e1ad2dba10
--- /dev/null
+++ b/MaterialLib/MPL/Components/Water.cpp
@@ -0,0 +1,28 @@
+/**
+ * \file
+ * \author Norbert Grunwald
+ * \date   Sep 7, 2017
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 "Water.h"
+#include "MaterialLib/MPL/Properties/Properties.h"
+
+namespace MaterialPropertyLib
+{
+Water::Water(std::unique_ptr<PropertyArray>&& properties)
+{
+    _properties[PropertyType::name] = std::make_unique<Constant>("Water");
+
+    if (properties)
+    {
+        overwriteExistingProperties(_properties, *properties);
+    }
+}
+}  // namespace MaterialPropertyLib
diff --git a/MaterialLib/MPL/Components/Water.h b/MaterialLib/MPL/Components/Water.h
new file mode 100644
index 0000000000000000000000000000000000000000..de14d8c24b6321ee171858d7f14ea22e3c5ebe0b
--- /dev/null
+++ b/MaterialLib/MPL/Components/Water.h
@@ -0,0 +1,27 @@
+/**
+ * \file
+ * \author Norbert Grunwald
+ * \date   Sep 7, 2017
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 "MaterialLib/MPL/Component.h"
+
+namespace MaterialPropertyLib
+{
+/// A class for Water derived from Component.
+///
+/// This class can holds material constants and default properties of ordinary
+/// water.
+struct Water final : public Component
+{
+    explicit Water(std::unique_ptr<PropertyArray>&& properties);
+};
+}  // namespace MaterialPropertyLib
diff --git a/MaterialLib/MPL/CreateComponent.cpp b/MaterialLib/MPL/CreateComponent.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1364eb9c45ca0415b21d5f732d44026d0fc2ef23
--- /dev/null
+++ b/MaterialLib/MPL/CreateComponent.cpp
@@ -0,0 +1,99 @@
+/**
+ * \file
+ * \author Norbert Grunwald
+ * \date   Sep 7, 2017
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 "CreateComponent.h"
+
+#include "BaseLib/ConfigTree.h"
+
+#include "Components/Components.h"
+#include "CreateProperty.h"
+
+namespace
+{
+std::unique_ptr<MaterialPropertyLib::Component> createComponent(
+    BaseLib::ConfigTree const& config)
+{
+    using namespace MaterialPropertyLib;
+    // Parsing the component name
+    //! \ogs_file_param{prj__media__medium__phases__phase__components__component__name}
+    auto const& component_name = config.getConfigParameter<std::string>("name");
+
+    // Check whether a name is given or not
+    if (component_name.empty())
+    {
+        OGS_FATAL("Component name is a mandatory field and cannot be empty.");
+    }
+
+    // Parsing component properties. If a component name is given and this
+    // component is predefined in the class implementation, properties
+    // become optional. The default values of properties will be overwritten
+    // if specified.
+    std::unique_ptr<PropertyArray> properties =
+        //! \ogs_file_param{prj__media__medium__phases__phase__components__component__properties}
+        createProperties(config.getConfigSubtreeOptional("properties"));
+
+    // If a name is given, it must conform with one of the derived component
+    // names in the following list:
+    if (boost::iequals(component_name, "water"))
+    {
+        return std::make_unique<Water>(std::move(properties));
+    }
+
+    if (!properties)
+    {
+        // No component properties are provided. If the component is not
+        // specified, this results in a fatal error, since an unspecified
+        // component has no properties.
+        OGS_FATAL("No Properties defined for unspecified component");
+    }
+
+    return std::make_unique<Component>(component_name, std::move(properties));
+}
+}
+
+namespace MaterialPropertyLib
+{
+std::vector<std::unique_ptr<Component>> createComponents(
+    boost::optional<BaseLib::ConfigTree> const& config)
+{
+    if (!config)
+    {
+        return {};
+    }
+
+    std::vector<std::unique_ptr<Component>> components;
+
+    // Collect component's names to avoid duplicate components.
+    std::set<std::string> component_names;
+
+    for (
+        auto const& component_config :
+        //! \ogs_file_param{prj__media__medium__phases__phase__components__component}
+        config->getConfigSubtreeList("component"))
+    {
+        auto component = createComponent(component_config);
+
+        bool new_insertion = false;
+        std::tie(std::ignore, new_insertion) =
+            component_names.insert(component->name());
+        if (!new_insertion)
+        {
+            OGS_FATAL("Found duplicates with the same component name tag '%s'.",
+                      component->name().c_str());
+        }
+
+        components.push_back(std::move(component));
+    }
+    return components;
+}
+}  // namespace MaterialPropertyLib
diff --git a/MaterialLib/MPL/CreateComponent.h b/MaterialLib/MPL/CreateComponent.h
new file mode 100644
index 0000000000000000000000000000000000000000..1109c58e69b786b69cfacb57582a0b7cb31be6f8
--- /dev/null
+++ b/MaterialLib/MPL/CreateComponent.h
@@ -0,0 +1,37 @@
+/**
+ * \file
+ * \author Norbert Grunwald
+ * \date   Sep 7, 2017
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 <boost/optional.hpp>
+#include <memory>
+
+#include "Component.h"
+
+namespace BaseLib
+{
+class ConfigTree;
+}
+
+namespace MaterialPropertyLib
+{
+/// The method creates components based on config subtree.
+///
+/// Just like a phase, a component can have a name. But, in this case, the name
+/// has an important task. If a name is given, a specific component class
+/// referring to that name with corresponding physical material properties is
+/// created.
+/// Assigning a name is optional; If no name is given, a custom component
+/// without predefined properties is created.
+std::vector<std::unique_ptr<Component>> createComponents(
+    boost::optional<BaseLib::ConfigTree> const& config);
+
+}  // namespace MaterialPropertyLib
diff --git a/MaterialLib/MPL/CreateMaterialSpatialDistributionMap.cpp b/MaterialLib/MPL/CreateMaterialSpatialDistributionMap.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9eb5510763ca0e9a7815eff5472254e8359377d4
--- /dev/null
+++ b/MaterialLib/MPL/CreateMaterialSpatialDistributionMap.cpp
@@ -0,0 +1,50 @@
+/**
+ * \file
+ * \date   Nov 28, 2017
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 "CreateMaterialSpatialDistributionMap.h"
+#include "MaterialSpatialDistributionMap.h"
+#include "MeshLib/Mesh.h"
+
+namespace MaterialPropertyLib
+{
+std::unique_ptr<MaterialSpatialDistributionMap>
+createMaterialSpatialDistributionMap(
+    std::map<int, std::unique_ptr<Medium>> const& media,
+    MeshLib::Mesh const& mesh)
+{
+    auto const material_ids = materialIDs(mesh);
+
+    int const max_material_id =
+        !material_ids
+            ? 0
+            : *std::max_element(begin(*material_ids), end(*material_ids));
+
+    if (max_material_id > static_cast<int>(media.size() - 1))
+    {
+        OGS_FATAL(
+            "The maximum value of MaterialIDs in mesh is %d. As the given "
+            "number of porous media definitions in the project file is %d, the "
+            "maximum value of MaterialIDs in mesh must be %d (index starts "
+            "with zero).",
+            max_material_id, media.size(), max_material_id - 1);
+    }
+
+    if (max_material_id < static_cast<int>(media.size() - 1))
+        WARN(
+            "There are %d porous medium definitions in the project file but "
+            "only %d different values in the MaterialIDs vector/data_array in "
+            "the mesh.",
+            media.size(), max_material_id - 1);
+
+    return std::make_unique<MaterialSpatialDistributionMap>(media,
+                                                            material_ids);
+}
+}  // namespace MaterialPropertyLib
diff --git a/MaterialLib/MPL/CreateMaterialSpatialDistributionMap.h b/MaterialLib/MPL/CreateMaterialSpatialDistributionMap.h
new file mode 100644
index 0000000000000000000000000000000000000000..aad1ea6d4aa622d3b74f66f11b3ad62869197032
--- /dev/null
+++ b/MaterialLib/MPL/CreateMaterialSpatialDistributionMap.h
@@ -0,0 +1,32 @@
+/**
+ * \file
+ * \date   Nov 28, 2017
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 <map>
+#include <memory>
+
+namespace MeshLib
+{
+class Mesh;
+}
+
+namespace MaterialPropertyLib
+{
+class MaterialSpatialDistributionMap;
+
+class Medium;
+
+std::unique_ptr<MaterialSpatialDistributionMap>
+createMaterialSpatialDistributionMap(
+    std::map<int, std::unique_ptr<Medium>> const& media,
+    MeshLib::Mesh const& mesh);
+}  // namespace MaterialPropertyLib
diff --git a/MaterialLib/MPL/CreateMedium.cpp b/MaterialLib/MPL/CreateMedium.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b6c8507b38c7669b69b2771b494b8cf6bd05d0ff
--- /dev/null
+++ b/MaterialLib/MPL/CreateMedium.cpp
@@ -0,0 +1,46 @@
+/**
+ * \file
+ * \author Norbert Grunwald
+ * \date   07.09.2017
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 "Medium.h"
+
+#include <string>
+#include "BaseLib/ConfigTree.h"
+
+#include "Properties/Properties.h"
+
+#include "CreatePhase.h"
+#include "CreateProperty.h"
+
+namespace MaterialPropertyLib
+{
+std::unique_ptr<Medium> createMedium(BaseLib::ConfigTree const& config)
+{
+    // Parsing the phases
+    // Properties of phases may be not required in all the cases.
+    //! \ogs_file_param{prj__media__medium__phases}
+    auto&& phases = createPhases(config.getConfigSubtreeOptional("phases"));
+
+    // Parsing medium properties, overwriting the defaults.
+    auto&& properties =
+        //! \ogs_file_param{prj__media__medium__properties}
+        createProperties(config.getConfigSubtreeOptional("properties"));
+
+    if (phases.empty() && !properties)
+    {
+        OGS_FATAL("Neither tag <phases> nor tag <properties> has been found.");
+    }
+
+    return std::make_unique<Medium>(std::move(phases), std::move(properties));
+}
+
+}  // namespace MaterialPropertyLib
diff --git a/MaterialLib/MPL/CreateMedium.h b/MaterialLib/MPL/CreateMedium.h
new file mode 100644
index 0000000000000000000000000000000000000000..6b5c7b94c0c34d80d71cf939146d00f2e296d038
--- /dev/null
+++ b/MaterialLib/MPL/CreateMedium.h
@@ -0,0 +1,32 @@
+/**
+ * \file
+ * \author Norbert Grunwald
+ * \date   07.09.2017
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 <memory>
+
+namespace BaseLib
+{
+class ConfigTree;
+}
+namespace MaterialPropertyLib
+{
+class Medium;
+}
+
+namespace MaterialPropertyLib
+{
+/// This function parses the "phases" and "properties" subtrees of the config
+/// tree and calls create methods for the phase vector and the properties array.
+/// Medium properties are optional. If not defined, default properties are
+/// assigned.
+std::unique_ptr<Medium> createMedium(BaseLib::ConfigTree const& config);
+}  // namespace MaterialPropertyLib
diff --git a/MaterialLib/MPL/CreatePhase.cpp b/MaterialLib/MPL/CreatePhase.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8423aef48119571a0f193053888e7eda99e31c28
--- /dev/null
+++ b/MaterialLib/MPL/CreatePhase.cpp
@@ -0,0 +1,112 @@
+/**
+ * \file
+ * \author Norbert Grunwald
+ * \date   07.09.2017
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 "CreatePhase.h"
+
+#include <set>
+#include <string>
+
+#include "BaseLib/ConfigTree.h"
+
+#include "CreateComponent.h"
+#include "CreateProperty.h"
+#include "Phase.h"
+
+namespace
+{
+std::unique_ptr<MaterialPropertyLib::Phase> createPhase(
+    BaseLib::ConfigTree const& config)
+{
+    using namespace MaterialPropertyLib;
+
+    //! \ogs_file_param{prj__media__medium__phases__phase__type}
+    auto&& phase_type = config.getConfigParameter<std::string>("type");
+
+    if (phase_type.empty())
+    {
+        OGS_FATAL("Phase type is a mandatory field and cannot be empty.");
+    }
+
+    std::array<std::string, 4> const allowed_phase_types = {
+        {"Solid", "AqueousLiquid", "NonAqueousLiquid", "Gas"}};
+
+    if (std::none_of(allowed_phase_types.begin(),
+                     allowed_phase_types.end(),
+                     [&phase_type](std::string const& type) {
+                         return phase_type == type;
+                     }))
+    {
+        ERR("Phase type should be one of:");
+        for (auto const type : allowed_phase_types)
+        {
+            ERR(type.c_str());
+        }
+        OGS_FATAL("Wrong phase type '%s' given.", phase_type.c_str());
+    }
+
+    // Parsing of optional components.
+    auto components =
+        //! \ogs_file_param{prj__media__medium__phases__phase__components}
+        createComponents(config.getConfigSubtreeOptional("components"));
+
+    // Properties of optional properties.
+    auto properties =
+        //! \ogs_file_param{prj__media__medium__phases__phase__properties}
+        createProperties(config.getConfigSubtreeOptional("properties"));
+
+    if (components.empty() && !properties)
+    {
+        OGS_FATAL(
+            "Neither tag <components> nor tag <properties> has been set for "
+            "the phase '%s'.",
+            phase_type.c_str());
+    }
+
+    return std::make_unique<Phase>(
+        std::move(phase_type), std::move(components), std::move(properties));
+}
+}
+
+namespace MaterialPropertyLib
+{
+std::vector<std::unique_ptr<Phase>> createPhases(
+    boost::optional<BaseLib::ConfigTree> const& config)
+{
+    if (!config)
+    {
+        return {};
+    }
+
+    std::vector<std::unique_ptr<Phase>> phases;
+
+    std::set<std::string> phase_names;
+
+    //! \ogs_file_param{prj__media__medium__phases__phase}
+    for (auto phase_config : config->getConfigSubtreeList("phase"))
+    {
+        auto phase = createPhase(phase_config);
+        bool new_insertion = false;
+        std::tie(std::ignore, new_insertion) =
+            phase_names.insert(phase->name());
+
+        if (!new_insertion)
+        {
+            OGS_FATAL("Found duplicates with the same phase name tag '%s'.",
+                      phase->name().c_str());
+        }
+
+        phases.push_back(std::move(phase));
+    }
+    return phases;
+}
+}  // namespace MaterialPropertyLib
diff --git a/MaterialLib/MPL/CreatePhase.h b/MaterialLib/MPL/CreatePhase.h
new file mode 100644
index 0000000000000000000000000000000000000000..4ea426ef6ce64853ff7a305980b5fb4fbe425798
--- /dev/null
+++ b/MaterialLib/MPL/CreatePhase.h
@@ -0,0 +1,41 @@
+/**
+ * \file
+ * \author Norbert Grunwald
+ * \date   07.09.2017
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 <boost/optional.hpp>
+#include <memory>
+#include <vector>
+
+namespace BaseLib
+{
+class ConfigTree;
+}
+namespace MaterialPropertyLib
+{
+class Phase;
+}
+
+namespace MaterialPropertyLib
+{
+/// A method that parses the phase details and stores them in the private
+/// _phases member.
+///
+/// This method creates the phases of the medium. Unlike a medium, a phase may
+/// have a name. However, this is silly at the moment since this name still has
+/// no effect (except of some benefits in regard of readability).
+/// Phase components are required (a phase consists of at least one component).
+/// Phase properties are optional. If not given, default properties are
+/// assigned. These default properties average the component properties,
+/// weighted by mole fraction.
+std::vector<std::unique_ptr<Phase>> createPhases(
+    boost::optional<BaseLib::ConfigTree> const& config);
+}  // namespace MaterialPropertyLib
diff --git a/MaterialLib/MPL/CreateProperty.cpp b/MaterialLib/MPL/CreateProperty.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..44b3258fce42ba138e1a4b7c12d68b1f6493e892
--- /dev/null
+++ b/MaterialLib/MPL/CreateProperty.cpp
@@ -0,0 +1,151 @@
+/**
+ * \file
+ * \author Norbert Grunwald
+ * \date   Sep 7, 2017
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 "CreateProperty.h"
+
+#include <string>
+#include <vector>
+#include "BaseLib/ConfigTree.h"
+
+#include "Properties/Properties.h"
+
+#include "Component.h"
+#include "Medium.h"
+#include "Phase.h"
+
+namespace
+{
+std::unique_ptr<MaterialPropertyLib::Property> createProperty(
+    BaseLib::ConfigTree const& config)
+{
+    using namespace MaterialPropertyLib;
+    // Parsing the property type:
+    //! \ogs_file_param{properties__property__type}
+    auto const property_type = config.getConfigParameter<std::string>("type");
+
+    // If (and only if) the given property type is 'constant', a
+    // corresponding value is needed.
+    if (property_type == "Constant")
+    {
+        //! \ogs_file_param{properties__property__Constant__value}
+        std::vector<double> const values =
+            config.getConfigParameter<std::vector<double>>("value");
+
+        switch (values.size())
+        {
+            case 1:
+            {
+                // scalar
+                PropertyDataType property_value = values[0];
+                return std::make_unique<Constant>(property_value);
+            }
+            case 2:
+            {
+                // Pair
+                PropertyDataType property_value = Pair{values[0], values[1]};
+                return std::make_unique<Constant>(property_value);
+            }
+            case 3:
+            {
+                // Vector
+                PropertyDataType property_value =
+                    Vector{values[0], values[1], values[2]};
+                return std::make_unique<Constant>(property_value);
+            }
+            case 6:
+            {
+                // Symmetric Tensor - xx, yy, zz, xy, xz, yz
+                PropertyDataType property_value =
+                    SymmTensor{values[0], values[1], values[2],
+                               values[3], values[4], values[5]};
+                return std::make_unique<Constant>(property_value);
+            }
+            case 9:
+            {
+                // Tensor
+                PropertyDataType property_value = Tensor{
+                    values[0], values[1], values[2], values[3], values[4],
+                    values[5], values[6], values[7], values[8]};
+                return std::make_unique<Constant>(property_value);
+            }
+
+            default:
+            {
+                OGS_FATAL(
+                    "Creation of a constant property with %i components is not "
+                    "implemented.",
+                    values.size());
+            }
+        }
+
+        PropertyDataType property_value;
+        return std::make_unique<Constant>(property_value);
+    }
+    // Properties can be medium, phase, or component properties.
+    // Some of them require information about the respective material.
+    // Thus, we pass a pointer to the material that requests the property.
+    // In this method, this pointer is realized via typename MaterialType, which
+    // replaces either Medium*, Phase*, or Component*.
+    // Note that most property constructors (only those that request material
+    // pointers) must be overloaded for any type of material.
+
+    /* TODO Additional properties go here, for example:
+    if (boost::iequals(property_type, "BilinearTemperaturePressure"))
+    {
+        return createBilinearTemperaturePressure(config, material_type);
+    }
+    */
+
+    // If none of the above property types are found, OGS throws an error.
+    OGS_FATAL("The specified component property type '%s' was not recognized",
+              property_type.c_str());
+}
+}  // namespace
+
+namespace MaterialPropertyLib
+{
+std::unique_ptr<PropertyArray> createProperties(
+    boost::optional<BaseLib::ConfigTree> const& config)
+{
+    if (!config)
+    {
+        return nullptr;
+    }
+
+    //! \ogs_file_param{properties__property}
+    auto const& property_configs = config->getConfigSubtreeList("property");
+    if (property_configs.empty())
+    {
+        return nullptr;
+    }
+
+    auto properties = std::make_unique<PropertyArray>();
+
+    for (auto property_config : property_configs)
+    {
+        // Parsing the property name:
+        auto const property_name =
+            //! \ogs_file_param{properties__property__name}
+            property_config.getConfigParameter<std::string>("name");
+        // Create a new property based on the configuration subtree:
+        auto property = createProperty(property_config);
+
+        // Insert the new property at the right position into the components
+        // private PropertyArray:
+        (*properties)[convertStringToProperty(property_name)] =
+            std::move(property);
+    }
+    return properties;
+}
+
+}  // namespace MaterialPropertyLib
diff --git a/MaterialLib/MPL/CreateProperty.h b/MaterialLib/MPL/CreateProperty.h
new file mode 100644
index 0000000000000000000000000000000000000000..777e9181c67149e1f333105d43b2d439e2a71998
--- /dev/null
+++ b/MaterialLib/MPL/CreateProperty.h
@@ -0,0 +1,43 @@
+/**
+ * \file
+ * \author Norbert Grunwald
+ * \date   Sep 7, 2017
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 <boost/optional/optional.hpp>
+#include <memory>
+#include "PropertyType.h"
+
+namespace BaseLib
+{
+class ConfigTree;
+}
+
+namespace MaterialPropertyLib
+{
+class Property;
+
+/// This data type is based on a std::array. It can hold pointers to objects of
+/// class Property or its inheritors. The size of this array is determined by
+/// the number of entries of the PropertyType enumerator.
+using PropertyArray =
+    std::array<std::unique_ptr<Property>, PropertyType::number_of_properties>;
+
+/// The method reads the 'properties' tag in the prj-file and creates component
+/// properties accordingly.
+///
+/// First, a new property iy created based on the specified property type.
+/// Then, the property name is evaluated and the property is copied into the
+/// properties array.
+std::unique_ptr<PropertyArray> createProperties(
+    boost::optional<BaseLib::ConfigTree> const& config);
+
+}  // namespace MaterialPropertyLib
diff --git a/MaterialLib/MPL/MaterialSpatialDistributionMap.cpp b/MaterialLib/MPL/MaterialSpatialDistributionMap.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7e791a0b8d9839b8512e69034549bd03b5f2d8cb
--- /dev/null
+++ b/MaterialLib/MPL/MaterialSpatialDistributionMap.cpp
@@ -0,0 +1,24 @@
+/**
+ * \file
+ * \date   Nov 28, 2017
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 "MaterialSpatialDistributionMap.h"
+#include "MeshLib/Mesh.h"
+
+namespace MaterialPropertyLib
+{
+Medium* MaterialSpatialDistributionMap::getMedium(std::size_t const element_id)
+{
+    auto const material_id =
+        _material_ids == nullptr ? 0 : (*_material_ids)[element_id];
+
+    return _media.at(material_id).get();
+}
+}  // namespace MaterialPropertyLib
diff --git a/MaterialLib/MPL/MaterialSpatialDistributionMap.h b/MaterialLib/MPL/MaterialSpatialDistributionMap.h
new file mode 100644
index 0000000000000000000000000000000000000000..fd9976226fb9e9162222fc09b118e07df744f9d1
--- /dev/null
+++ b/MaterialLib/MPL/MaterialSpatialDistributionMap.h
@@ -0,0 +1,44 @@
+/**
+ * \file
+ * \date   Nov 28, 2017
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 <map>
+#include <memory>
+#include <vector>
+
+namespace MeshLib
+{
+template <typename PROP_VAL_TYPE>
+class PropertyVector;
+}  // namespace MeshLib
+
+namespace MaterialPropertyLib
+{
+class Medium;
+
+class MaterialSpatialDistributionMap
+{
+public:
+    MaterialSpatialDistributionMap(
+        std::map<int, std::unique_ptr<Medium>> const& media,
+        MeshLib::PropertyVector<int> const* const material_ids)
+        : _media(media), _material_ids(material_ids)
+    {
+    }
+
+    Medium* getMedium(std::size_t element_id);
+
+private:
+    std::map<int, std::unique_ptr<Medium>> const& _media;
+    MeshLib::PropertyVector<int> const* const _material_ids;
+};
+}  // namespace MaterialPropertyLib
diff --git a/MaterialLib/MPL/Medium.cpp b/MaterialLib/MPL/Medium.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7830cb85bb91d687bc40ed3d5b588886a2407306
--- /dev/null
+++ b/MaterialLib/MPL/Medium.cpp
@@ -0,0 +1,55 @@
+/**
+ * \file
+ * \author Norbert Grunwald
+ * \date   07.09.2017
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 "Medium.h"
+
+#include "BaseLib/Algorithm.h"
+#include "Properties/Properties.h"
+
+namespace MaterialPropertyLib
+{
+Medium::Medium(std::vector<std::unique_ptr<Phase>>&& phases,
+               std::unique_ptr<PropertyArray>&& properties)
+    : _phases(std::move(phases))
+{
+    if (properties)
+    {
+        overwriteExistingProperties(_properties, *properties);
+    }
+}
+
+Phase const& Medium::phase(std::size_t const index) const
+{
+    return *_phases[index];
+}
+
+Phase const& Medium::phase(std::string const& name) const
+{
+    return *BaseLib::findElementOrError(
+        _phases.begin(), _phases.end(),
+        [&name](std::unique_ptr<MaterialPropertyLib::Phase> const& phase) {
+            return phase->name() == name;
+        },
+        "Could not find phase name '" + name + "'.");
+}
+
+Property const& Medium::property(PropertyType const& p) const
+{
+    return *_properties[p];
+}
+
+std::size_t Medium::numberOfPhases() const
+{
+    return _phases.size();
+}
+}  // namespace MaterialPropertyLib
diff --git a/MaterialLib/MPL/Medium.h b/MaterialLib/MPL/Medium.h
new file mode 100644
index 0000000000000000000000000000000000000000..49c5eeec8e00b60cae395fb50d4ca3bfc83759fa
--- /dev/null
+++ b/MaterialLib/MPL/Medium.h
@@ -0,0 +1,91 @@
+/**
+ * \file
+ * \author Norbert Grunwald
+ * \date   07.09.2017
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 <string>
+#include <vector>
+
+#include "Phase.h"
+
+namespace MaterialPropertyLib
+{
+class Property;
+}
+namespace MaterialPropertyLib
+{
+/// This class is for material objects on the Medium scale.
+///
+/// A Medium consists of an arbitrarily long vector of phases and an array of
+/// properties.
+class Medium final
+{
+public:
+    Medium(std::vector<std::unique_ptr<Phase>>&& phases,
+           std::unique_ptr<PropertyArray>&& properties);
+
+    /// A get-function for a particular phase. The ul argument specifies the
+    /// index within the _phases vector.
+    Phase const& phase(std::size_t index) const;
+    /// A get-function for a particular phase by phase name.
+    Phase const& phase(std::string const& phase_name) const;
+    /// A get-function for a property. The argument refers to the name of the
+    /// property.
+    Property const& property(PropertyType const& p) const;
+
+    /// A simple get-function for retrieving the number of phases the medium
+    /// consists of.
+    std::size_t numberOfPhases() const;
+
+    template <typename T>
+    T value(PropertyType const p) const
+    {
+        return property(p).template value<T>();
+    }
+
+    template <typename T>
+    T value(PropertyType const p, VariableArray const& variable_array) const
+    {
+        return property(p).template value<T>(variable_array);
+    }
+
+    template <typename T>
+    T dValue(PropertyType const p,
+             VariableArray const& variable_array,
+             Variables const variables) const
+    {
+        return property(p).template dValue<T>(variable_array, variables);
+    }
+
+    template <typename T>
+    T d2Value(PropertyType const p,
+              VariableArray const& variable_array,
+              Variables const variables1,
+              Variables const variables2) const
+    {
+        return property(p).template d2Value<T>(variable_array, variables1,
+                                               variables2);
+    }
+
+private:
+    /// The vector that holds the phases.
+    std::vector<std::unique_ptr<Phase>> const _phases;
+    /// The array that holds the medium properties.
+    ///
+    /// Currently, these defaults is the volume fraction average.
+    ///
+    /// Most properties are fine with the volume fraction average, but
+    /// special-defaults are allowed as well...
+    PropertyArray _properties;
+};
+}  // namespace MaterialPropertyLib
diff --git a/MaterialLib/MPL/Phase.cpp b/MaterialLib/MPL/Phase.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..203bd92f51b0098ae8fc3d2f4cb092c62b9259d2
--- /dev/null
+++ b/MaterialLib/MPL/Phase.cpp
@@ -0,0 +1,64 @@
+/**
+ * \file
+ * \author Norbert Grunwald
+ * \date   Sep 7, 2017
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 "Phase.h"
+#include "BaseLib/Algorithm.h"
+#include "Properties/Properties.h"
+
+#include "Component.h"
+
+namespace MaterialPropertyLib
+{
+Phase::Phase(std::string&& phase_name,
+             std::vector<std::unique_ptr<Component>>&& components,
+             std::unique_ptr<PropertyArray>&& properties)
+    : _components(std::move(components))
+{
+    _properties[PropertyType::name] = std::make_unique<Constant>(phase_name);
+
+    if (properties)
+    {
+        overwriteExistingProperties(_properties, *properties);
+    }
+}
+
+Component const& Phase::component(const std::size_t& index) const
+{
+    return *_components[index];
+}
+
+Component const& Phase::component(std::string const& name) const
+{
+    return *BaseLib::findElementOrError(
+        _components.begin(), _components.end(),
+        [&name](std::unique_ptr<Component> const& component) {
+            return component->name() == name;
+        },
+        "Could not find component name '" + name + "'.");
+}
+
+Property const& Phase::property(PropertyType const& p) const
+{
+    return *_properties[p];
+}
+
+std::size_t Phase::numberOfComponents() const
+{
+    return _components.size();
+}
+
+std::string Phase::name() const
+{
+    return boost::get<std::string>(_properties[PropertyType::name]->value());
+}
+}  // namespace MaterialPropertyLib
diff --git a/MaterialLib/MPL/Phase.h b/MaterialLib/MPL/Phase.h
new file mode 100644
index 0000000000000000000000000000000000000000..12eddbe4b81ee1bdf747583cf8b5f97cf0b498c4
--- /dev/null
+++ b/MaterialLib/MPL/Phase.h
@@ -0,0 +1,66 @@
+/**
+ * \file
+ * \author Norbert Grunwald
+ * \date   Sep 7, 2017
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 <string>
+#include <vector>
+
+#include "Component.h"
+
+namespace MaterialPropertyLib
+{
+class Property;
+}
+namespace MaterialPropertyLib
+{
+/// This class defines material phases.
+///
+/// The Phase class consists of a vector of components and an array of
+/// properties.
+class Phase final
+{
+public:
+    /// The Phase constructor is called with the optional phase name.
+    Phase(std::string&& phase_name,
+          std::vector<std::unique_ptr<Component>>&& components,
+          std::unique_ptr<PropertyArray>&& properties);
+
+    /// A simple get-function for a component. The argument refers to the
+    /// Index of the component in the components vector.
+    Component const& component(std::size_t const& index) const;
+
+    /// A get-function for a component by component name.
+    Component const& component(std::string const& name) const;
+
+    /// A get-function for a property. The argument refers to the name of the
+    /// property.
+    Property const& property(PropertyType const& p) const;
+
+    /// A get-function for retrieving the number of components in this phase.
+    std::size_t numberOfComponents() const;
+
+    std::string name() const;
+
+private:
+    std::vector<std::unique_ptr<Component>> const _components;
+
+    /// Here, all for all properties listed in the Properties enumerator are
+    /// initialized by mole average functions of value zero. However,
+    /// 'special-default' properties are allowed to be set.
+    ///
+    /// After this, other special properties can be set as exceptional defaults.
+    PropertyArray _properties;
+};
+
+}  // namespace MaterialPropertyLib
diff --git a/MaterialLib/MPL/Properties/Constant.cpp b/MaterialLib/MPL/Properties/Constant.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fe63a021bc22253c87e1976763eb1f5fcd83c525
--- /dev/null
+++ b/MaterialLib/MPL/Properties/Constant.cpp
@@ -0,0 +1,25 @@
+/**
+ * \file
+ * \author Norbert Grunwald
+ * \date   Sep 7, 2017
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 "Constant.h"
+
+namespace MaterialPropertyLib
+{
+Constant::Constant(PropertyDataType const& v)
+{
+    _value = v;
+    _dvalue = boost::apply_visitor(
+        [](auto const& value) -> PropertyDataType { return decltype(value){}; },
+        v);
+};
+}  // namespace MaterialPropertyLib
diff --git a/MaterialLib/MPL/Properties/Constant.h b/MaterialLib/MPL/Properties/Constant.h
new file mode 100644
index 0000000000000000000000000000000000000000..880806d5441aea41fb44f31c61444f98685e726b
--- /dev/null
+++ b/MaterialLib/MPL/Properties/Constant.h
@@ -0,0 +1,30 @@
+/**
+ * \file
+ * \author Norbert Grunwald
+ * \date   Sep 7, 2017
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 "MaterialLib/MPL/Property.h"
+
+namespace MaterialPropertyLib
+{
+/// The constant property class. This property simply retrieves the stored
+/// constant value. It accepts all datatypes defined in PropertyDataType
+/// (currently: double, Vector, Tensor, std::string)
+class Constant final : public Property
+{
+public:
+    /// This constructor accepts single values of any data type defined in the
+    /// PropertyDataType definition and sets the protected attribute _value of
+    /// the base class Property to that value.
+    explicit Constant(PropertyDataType const& v);
+};
+}  // namespace MaterialPropertyLib
diff --git a/MaterialLib/MPL/Properties/Properties.h b/MaterialLib/MPL/Properties/Properties.h
new file mode 100644
index 0000000000000000000000000000000000000000..d56c81083a7768eeca174ffcc0cbb64e6b5c7efc
--- /dev/null
+++ b/MaterialLib/MPL/Properties/Properties.h
@@ -0,0 +1,15 @@
+/**
+ * \file
+ * \author Norbert Grunwald
+ * \date   Sep 7, 2017
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 "Constant.h"
diff --git a/MaterialLib/MPL/Property.cpp b/MaterialLib/MPL/Property.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..53c0e46c072f6b7c2f927788345b0b2bf367bf80
--- /dev/null
+++ b/MaterialLib/MPL/Property.cpp
@@ -0,0 +1,54 @@
+/**
+ * \file
+ * \author Norbert Grunwald
+ * \date   Sep 7, 2017
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 "Property.h"
+
+#include <string>
+
+namespace MaterialPropertyLib
+{
+
+PropertyDataType Property::value() const
+{
+    return _value;
+}
+/// The default implementation of this method only returns the property value
+/// without altering it.
+PropertyDataType Property::value(VariableArray const& /*variable_array*/) const
+{
+    return _value;
+}
+
+/// The default implementation of this method only returns the
+/// property value derivative without altering it.
+PropertyDataType Property::dValue(VariableArray const& /*variable_array*/,
+                                  Variables const /*variables*/) const
+{
+    return _dvalue;
+}
+
+/// Default implementation: 2nd derivative of any constant property is zero.
+PropertyDataType Property::d2Value(VariableArray const& /*variable_array*/,
+                                   Variables const /*variables*/,
+                                   Variables const /*variables*/) const
+{
+    return 0.0;
+}
+
+void Property::notImplemented(const std::string& property,
+                              const std::string& material) const
+{
+    OGS_FATAL("The property '%s' is not available on the '%s' scale",
+              property.c_str(), material.c_str());
+}
+}  // namespace MaterialPropertyLib
diff --git a/MaterialLib/MPL/Property.h b/MaterialLib/MPL/Property.h
new file mode 100644
index 0000000000000000000000000000000000000000..cc8f9c45f610d17d772043e5f300a37427aa122c
--- /dev/null
+++ b/MaterialLib/MPL/Property.h
@@ -0,0 +1,116 @@
+/**
+ * \file
+ * \author Norbert Grunwald
+ * \date   Sep 7, 2017
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 <array>
+#include <boost/variant.hpp>
+#include <string>
+
+#include "PropertyType.h"
+#include "VariableType.h"
+
+namespace MaterialPropertyLib
+{
+/// This is a custom data type for arbitrary properties, based on the
+/// boost::variant container. It can hold scalars, vectors, tensors, and so
+/// forth.
+enum PropertyDataTypeName
+{
+    nScalar,
+    nPair,
+    nVector,
+    nSymmTensor,
+    nTensor
+};
+
+using PropertyDataType =
+    boost::variant<double, Pair, Vector, SymmTensor, Tensor, std::string>;
+
+/// This class is the base class for any material property of any
+/// scale (i.e. components, phases, media, ...). The single value of
+/// that Property can hold scalars, vectors, tensors, strings, etc.
+class Property
+{
+public:
+    virtual ~Property() = default;
+    /// This method is called when a property is used for the wrong kind of
+    /// material, or if the property is not implemented on this kind of material
+    /// yet.
+    void notImplemented(const std::string& property,
+                        const std::string& material) const;
+    /// This virtual method simply returns the private _value attribute without
+    /// changing it.
+    virtual PropertyDataType value() const;
+    /// This virtual method will compute the property value based on the primary
+    /// variables that are passed as arguments.
+    virtual PropertyDataType value(VariableArray const& variable_array) const;
+    /// This virtual method will compute the derivative of a property
+    /// with respect to the given variables pv.
+    virtual PropertyDataType dValue(VariableArray const& variable_array,
+                                    Variables const variables) const;
+    /// This virtual method will compute the second derivative of a
+    /// property with respect to the given variables pv1 and pv2.
+    virtual PropertyDataType d2Value(VariableArray const& variable_array,
+                                     Variables const variables1,
+                                     Variables const variables2) const;
+
+    template <typename T>
+    T value() const
+    {
+        return boost::get<T>(value());
+    }
+    template <typename T>
+    T value(VariableArray const& variable_array) const
+    {
+        return boost::get<T>(value(variable_array));
+    }
+    template <typename T>
+    T dValue(VariableArray const& variable_array,
+             Variables const variables) const
+    {
+        return boost::get<T>(dValue(variable_array, variables));
+    }
+    template <typename T>
+    T d2Value(VariableArray const& variable_array,
+              Variables const& variables1,
+              Variables const& variables2) const
+    {
+        return boost::get<T>(d2Value(variable_array, variables1, variables2));
+    }
+
+protected:
+    /// The single value of a property.
+    PropertyDataType _value;
+    PropertyDataType _dvalue;
+};
+
+/// This method returns the 0-based index of the variant data types. Can be
+/// enhanced by using enums.
+inline std::size_t getType(Property const& p)
+{
+    return p.value().which();
+}
+
+inline void overwriteExistingProperties(PropertyArray& properties,
+                                        PropertyArray& new_properties)
+{
+    for (std::size_t i = 0; i < properties.size(); ++i)
+    {
+        if (new_properties[i] != nullptr)
+        {
+            properties[i] = std::move(new_properties[i]);
+        }
+    }
+}
+
+}  // namespace MaterialPropertyLib
diff --git a/MaterialLib/MPL/PropertyType.h b/MaterialLib/MPL/PropertyType.h
new file mode 100644
index 0000000000000000000000000000000000000000..0ab8534019f071cb31d506261dc46031ef6c3cca
--- /dev/null
+++ b/MaterialLib/MPL/PropertyType.h
@@ -0,0 +1,274 @@
+/**
+ * \file
+ * \author Norbert Grunwald
+ * \date   Sep 7, 2017
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 <array>
+#include <boost/algorithm/string/predicate.hpp>
+#include <memory>
+#include <string>
+#include "BaseLib/Error.h"
+
+namespace MaterialPropertyLib
+{
+class Property;
+}
+
+namespace MaterialPropertyLib
+{
+/// PropertyType is an enumerator list of all known properties of a substance.
+/// This includes all properties on all scales (i.e. component, phase, and
+/// medium scales). It is used as an index for the PropertyArray of the
+/// materials. If a necessary property is not in the list, simply add a new one
+/// in alphabetical order (of course, except for the last entry). Please note
+/// that any of these entries must also appear in below convert functions.
+enum PropertyType : int
+{
+    acentric_factor,
+    binary_interaction_coefficient,
+    biot_coefficient,
+    brooks_corey_exponent,
+    bulk_modulus,
+    critical_density,
+    critical_pressure,
+    critical_temperature,
+    compressibility,
+    density,
+    drhodT,
+    effective_stress,
+    entry_pressure,
+    fredlund_parameters,
+    heat_capacity,
+    longitudinal_dispersivity,
+    molar_mass,
+    mole_fraction,
+    molecular_diffusion,
+    name,
+    permeability,
+    phase_velocity,
+    porosity,
+    reference_density,
+    reference_temperature,
+    reference_pressure,
+    relative_permeability,
+    residual_gas_saturation,
+    residual_liquid_saturation,
+    saturation,
+    specific_heat_capacity,
+    thermal_conductivity,
+    thermal_expansivity,
+    transversal_dispersivity,
+    viscosity,
+    number_of_properties
+};
+
+/// This function converts a string (e.g. a string from the configuration-tree)
+/// into one of the entries of the PropertyType enumerator. To avoid confusion,
+/// I suggest that the syntax of the properties in the input-files (i.e. the
+/// strings) have to be identical to the syntax of the entries in the
+/// enumerator.
+inline PropertyType convertStringToProperty(std::string const& inString)
+{
+    if (boost::iequals(inString, "acentric_factor"))
+    {
+        return PropertyType::acentric_factor;
+    }
+    if (boost::iequals(inString, "binary_interaction_coefficient"))
+    {
+        return PropertyType::binary_interaction_coefficient;
+    }
+    if (boost::iequals(inString, "biot_coefficient"))
+    {
+        return PropertyType::biot_coefficient;
+    }
+    if (boost::iequals(inString, "brooks_corey_exponent"))
+    {
+        return PropertyType::brooks_corey_exponent;
+    }
+    if (boost::iequals(inString, "bulk_modulus"))
+    {
+        return PropertyType::bulk_modulus;
+    }
+    if (boost::iequals(inString, "critical_density"))
+    {
+        return PropertyType::critical_density;
+    }
+    if (boost::iequals(inString, "critical_pressure"))
+    {
+        return PropertyType::critical_pressure;
+    }
+    if (boost::iequals(inString, "critical_temperature"))
+    {
+        return PropertyType::critical_temperature;
+    }
+    if (boost::iequals(inString, "compressibility"))
+    {
+        return PropertyType::compressibility;
+    }
+    if (boost::iequals(inString, "density"))
+    {
+        return PropertyType::density;
+    }
+    if (boost::iequals(inString, "drhodT"))
+    {
+        return PropertyType::drhodT;
+    }
+    if (boost::iequals(inString, "effective_stress"))
+    {
+        return PropertyType::effective_stress;
+    }
+    if (boost::iequals(inString, "entry_pressure"))
+    {
+        return PropertyType::entry_pressure;
+    }
+    if (boost::iequals(inString, "fredlund_parameters"))
+    {
+        return PropertyType::fredlund_parameters;
+    }
+    if (boost::iequals(inString, "heat_capacity"))
+    {
+        return PropertyType::heat_capacity;
+    }
+    if (boost::iequals(inString, "longitudinal_dispersivity"))
+    {
+        return PropertyType::longitudinal_dispersivity;
+    }
+    if (boost::iequals(inString, "molar_mass"))
+    {
+        return PropertyType::molar_mass;
+    }
+    if (boost::iequals(inString, "mole_fraction"))
+    {
+        return PropertyType::mole_fraction;
+    }
+    if (boost::iequals(inString, "molecular_diffusion"))
+    {
+        return PropertyType::molecular_diffusion;
+    }
+    if (boost::iequals(inString, "name"))
+    {
+        return PropertyType::name;
+    }
+    if (boost::iequals(inString, "permeability"))
+    {
+        return PropertyType::permeability;
+    }
+    if (boost::iequals(inString, "porosity"))
+    {
+        return PropertyType::porosity;
+    }
+    if (boost::iequals(inString, "phase_velocity"))
+    {
+        return PropertyType::phase_velocity;
+    }
+    if (boost::iequals(inString, "reference_density"))
+    {
+        return PropertyType::reference_density;
+    }
+    if (boost::iequals(inString, "reference_temperature"))
+    {
+        return PropertyType::reference_temperature;
+    }
+    if (boost::iequals(inString, "reference_pressure"))
+    {
+        return PropertyType::reference_pressure;
+    }
+    if (boost::iequals(inString, "relative_permeability"))
+    {
+        return PropertyType::relative_permeability;
+    }
+    if (boost::iequals(inString, "residual_gas_saturation"))
+    {
+        return PropertyType::residual_gas_saturation;
+    }
+    if (boost::iequals(inString, "residual_liquid_saturation"))
+    {
+        return PropertyType::residual_liquid_saturation;
+    }
+    if (boost::iequals(inString, "saturation"))
+    {
+        return PropertyType::saturation;
+    }
+    if (boost::iequals(inString, "specific_heat_capacity"))
+    {
+        return PropertyType::specific_heat_capacity;
+    }
+    if (boost::iequals(inString, "thermal_conductivity"))
+    {
+        return PropertyType::thermal_conductivity;
+    }
+    if (boost::iequals(inString, "thermal_expansivity"))
+    {
+        return PropertyType::thermal_expansivity;
+    }
+    if (boost::iequals(inString, "transversal_dispersivity"))
+    {
+        return PropertyType::transversal_dispersivity;
+    }
+    if (boost::iequals(inString, "viscosity"))
+    {
+        return PropertyType::viscosity;
+    }
+
+    OGS_FATAL(
+        "The property name '%s' does not correspond to any known property",
+        inString.c_str());
+
+    return PropertyType::number_of_properties;  // to avoid the 'no return'
+                                                // warning
+}
+
+static const std::array<std::string, PropertyType::number_of_properties>
+    property_enum_to_string{{"acentric_factor",
+                             "binary_interaction_coefficient",
+                             "biot_coefficient",
+                             "brooks_corey_exponent",
+                             "bulk_modulus",
+                             "critical_density",
+                             "critical_pressure",
+                             "critical_temperature",
+                             "compressibility",
+                             "density",
+                             "drhodT",
+                             "effective_stress",
+                             "entry_pressure",
+                             "fredlund_parameters",
+                             "heat_capacity",
+                             "longitudinal_dispersivity",
+                             "molar_mass",
+                             "mole_fraction",
+                             "molecular_diffusion",
+                             "name",
+                             "permeability",
+                             "phase_velocity",
+                             "porosity",
+                             "reference_density",
+                             "reference_temperature",
+                             "reference_pressure",
+                             "relative_permeability",
+                             "residual_gas_saturation",
+                             "residual_liquid_saturation",
+                             "saturation",
+                             "specific_heat_capacity",
+                             "thermal_conductivity",
+                             "thermal_expansivity",
+                             "transversal_dispersivity",
+                             "viscosity"}};
+
+/// This data type is based on a std::array. It can hold pointers to objects of
+/// class Property or its inheritors. The size of this array is determined by
+/// the number of entries of the PropertyType enumerator.
+using PropertyArray =
+    std::array<std::unique_ptr<Property>, PropertyType::number_of_properties>;
+
+}  // namespace MaterialPropertyLib
diff --git a/MaterialLib/MPL/VariableType.h b/MaterialLib/MPL/VariableType.h
new file mode 100644
index 0000000000000000000000000000000000000000..539a94ca036e7b01210de8626904db1d1c456e7f
--- /dev/null
+++ b/MaterialLib/MPL/VariableType.h
@@ -0,0 +1,66 @@
+/**
+ * \file
+ * \author Norbert Grunwald
+ * \date   Sep 7, 2017
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 <array>
+#include <boost/variant.hpp>
+
+namespace MaterialPropertyLib
+{
+/// Very simple vector data type for holding
+/// a pair of values.
+using Pair = std::array<double, 2>;
+
+/// Very simple vector data type for holding
+/// vector components.
+using Vector = std::array<double, 3>;
+
+/// Simple symmetric tensor data type for holding
+/// xx, yy, zz, xy, xz, yz tensor components.
+using SymmTensor = std::array<double, 6>;
+
+/// Very simple tensor data type for holding
+/// tensor components.
+using Tensor = std::array<double, 9>;
+
+/// Variables is simply a list of all commonly used variables that are used to
+/// determine the size of the VariableArray. If the variable of your choice is
+/// missing, simply add it somewhere at the list, but above the last entry.
+enum Variables : int
+{
+    phase_pressure,
+    capillary_pressure,
+    gas_density,
+    liquid_density,
+    temperature,
+    liquid_saturation,
+    u,
+    number_of_variables
+};
+
+/// Data type for primary variables, designed to contain both scalar and vector
+/// data.
+using VariableType = boost::variant<double, Vector>;
+
+/// The VariableArray is a std::array of fixed size. Its size is determined by
+/// the Variables enumerator list. Data type of that array is defined by the
+/// VariableType definition.
+using VariableArray = std::array<VariableType, Variables::number_of_variables>;
+
+/// This method returns a value of type double from the variables array
+inline double getScalar(VariableType pv)
+{
+    return boost::get<double>(pv);
+}
+}  // namespace MaterialPropertyLib
diff --git a/Tests/MaterialLib/TestMPL.cpp b/Tests/MaterialLib/TestMPL.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3885b74a2402d60827495cbd0b3bf1ab82b2a390
--- /dev/null
+++ b/Tests/MaterialLib/TestMPL.cpp
@@ -0,0 +1,28 @@
+/**
+ * \file
+ * \author Norbert Grunwald
+ * \date   Oct 22, 2018
+ * \brief
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 "TestMPL.h"
+
+#include "BaseLib/ConfigTree.h"
+#include "MaterialLib/MPL/CreateMedium.h"
+
+std::unique_ptr<MPL::Medium> createTestMaterial(std::string const& xml)
+{
+    auto const ptree = readXml(xml.c_str());
+    BaseLib::ConfigTree conf(ptree, "", BaseLib::ConfigTree::onerror,
+                             BaseLib::ConfigTree::onwarning);
+    auto const& config = conf.getConfigSubtree("medium");
+
+    return MPL::createMedium(config);
+}
diff --git a/Tests/MaterialLib/TestMPL.h b/Tests/MaterialLib/TestMPL.h
new file mode 100644
index 0000000000000000000000000000000000000000..1d59754903b571a8c49d51c070e4f2e383eb3b83
--- /dev/null
+++ b/Tests/MaterialLib/TestMPL.h
@@ -0,0 +1,21 @@
+/**
+ * \file
+ * \author Norbert Grunwald
+ * \date   Oct 22, 2018
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 "Tests/TestTools.h"
+
+#include "MaterialLib/MPL/Medium.h"
+namespace MPL = MaterialPropertyLib;
+
+std::unique_ptr<MPL::Medium> createTestMaterial(std::string const& xml);
diff --git a/Tests/MaterialLib/TestMPLParseMaterial.cpp b/Tests/MaterialLib/TestMPLParseMaterial.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ab25e27913678667b82709034eb93a8eb6554e57
--- /dev/null
+++ b/Tests/MaterialLib/TestMPLParseMaterial.cpp
@@ -0,0 +1,272 @@
+/**
+ * \file
+ * \author Norbert Grunwald
+ * \date   Sep 22, 2017
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, 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 <gtest/gtest.h>
+
+#include <sstream>
+
+#include "TestMPL.h"
+#include "Tests/TestTools.h"
+
+#include "MaterialLib/MPL/Medium.h"
+
+namespace MPL = MaterialPropertyLib;
+
+/// A simple structure for a string-based component used to create
+/// an XML-Structure.
+struct Component
+{
+    std::vector<std::string> property;
+    Component() : property(MPL::number_of_properties) {}
+};
+/// A simple structure for a string-based phase used to create
+/// an XML-Structure.
+struct Phase
+{
+    std::vector<Component> component;
+    std::vector<std::string> property;
+    Phase(std::size_t const componentNumber)
+        : component(componentNumber), property(MPL::number_of_properties)
+    {
+    }
+};
+
+/// A simple structure for a string-based medium used to create
+/// an XML-Structure
+struct Medium
+{
+    std::vector<Phase> phases;
+    std::vector<std::string> property;
+    Medium(std::vector<std::size_t> const& phases_)
+        : property(MPL::number_of_properties)
+    {
+        for (auto p : phases_)
+        {
+            phases.push_back(Phase(p));
+        }
+    }
+};
+
+/// Short method creating a number of blanks used for indentation.
+std::string indent(std::size_t const index)
+{
+    return std::string(index, ' ');
+}
+
+/// This method creates an XML-snippet of a single property.
+std::string makeProperty(std::size_t const ind, std::size_t const index,
+                         std::string const& property)
+{
+    std::stringstream sProperty;
+
+    if (property.empty())
+    {
+        return "";
+    }
+
+    sProperty << indent(ind) << "<property>\n";
+    sProperty << indent(ind + 2) << "<name>"
+              << MPL::property_enum_to_string[index] << "</name>\n";
+
+    if (double const value = std::atof(property.c_str()))
+    {
+        sProperty << indent(ind + 2) << "<type>Constant</type>\n";
+        sProperty << indent(ind + 2) << "<value>" << value << "</value>\n";
+    }
+    else
+    {
+        sProperty << indent(ind + 2) << "<type>" << property << "</type>\n";
+    }
+
+    sProperty << indent(ind) << "</property>\n";
+    return sProperty.str();
+}
+
+/// This method creates an XML-snippet of a property section.
+std::string makeProperties(std::size_t const ind,
+                           std::vector<std::string> const& properties)
+{
+    std::vector<std::string> temp;
+
+    for (std::size_t p = 0; p < properties.size(); ++p)
+    {
+        std::string property = makeProperty(ind + 2, p, properties[p]);
+        if ((!property.empty()) && (p != static_cast<std::size_t>(MPL::name)))
+        {
+            temp.push_back(property);
+        }
+        // If a property is not given, a default takes its place. Note also that
+        // 'name' is an exceptional property, since it determines specific
+        // components (thus it is defined outside of the properties- tag).
+    }
+
+    if (temp.empty())
+    {
+        return "";
+    }
+    std::stringstream sProperties;
+    sProperties << indent(ind) << "<properties>\n";
+    for (auto const& str : temp)
+    {
+        sProperties << str;
+    }
+    sProperties << indent(ind) << "</properties>\n";
+    return sProperties.str();
+}
+
+/// This method creates an XML-snippet of a single component.
+std::string makeComponent(Component const& c)
+{
+    std::stringstream component;
+    component << indent(12) << "<component>\n";
+    component << indent(14) << "<name>" << c.property[MPL::name] << "</name>\n";
+    component << makeProperties(14, c.property);
+    component << indent(12) << "</component>\n";
+    return component.str();
+}
+
+/// This method creates an XML-snippet of the phase components.
+std::string makeComponents(std::vector<Component> const& components)
+{
+    std::stringstream sComponents;
+    sComponents << indent(10) << "<components>\n";
+    for (auto const& c : components)
+    {
+        sComponents << makeComponent(c);
+    }
+    sComponents << indent(10) << "</components>\n";
+    return sComponents.str();
+}
+
+/// This method creates an XML-snippet of a single material phase.
+std::string makePhase(Phase const& p)
+{
+    std::stringstream phase;
+
+    phase << indent(8) << "<phase>\n";
+    phase << indent(10) << "<type>" << p.property[MPL::name] << "</type>\n";
+    phase << makeComponents(p.component);
+    phase << makeProperties(8, p.property);
+    phase << indent(8) << "</phase>\n";
+    return phase.str();
+}
+
+/// This method creates an XML-snippet of the material phases.
+std::string makePhases(std::vector<Phase> const& phases)
+{
+    std::stringstream sPhases;
+
+    sPhases << indent(6) << "<phases>\n";
+    for (auto const& p : phases)
+    {
+        sPhases << makePhase(p);
+    }
+    sPhases << indent(6) << "</phases>\n";
+    return sPhases.str();
+}
+
+/// This method creates the entire XML-tree structure from the string- based
+/// medium specification object. I know, indentation is not necessary for this,
+/// but my OCD kicked in...
+std::string makeMedium(Medium const& m)
+{
+    std::stringstream medium;
+    medium << indent(2) << "<medium>\n";
+    medium << makePhases(m.phases);
+    medium << makeProperties(6, m.property);
+    medium << indent(2) << "</medium>\n";
+    return medium.str();
+}
+
+/// A method used to obtain the name of a medium, phase, or component of a
+/// material or of a specifier and to store them in two vectors for later
+/// comparison.
+void getNames(std::string const& observed_name, std::string const& expectation,
+              std::string const& defaultName, std::vector<std::string>& obs,
+              std::vector<std::string>& exp)
+{
+    obs.push_back(observed_name);
+    if (expectation.empty())
+    {
+        exp.push_back(defaultName);
+    }
+    else
+    {
+        exp.push_back(expectation);
+    }
+}
+
+// This test represents an invariant test. First, several phases, components,
+// and properties are generated (more or less randomly).
+// Then, an XML-tree is generated from those information. The XML tree is then
+// parsed into a configTree, from which an MPL::MaterialObject is generated.
+// Last step compares the names (as well as the topology) of the Material object
+// with the specified parameters.
+TEST(Material, parseMaterials)
+{
+    // This is the topology of our new material: The size of the
+    // topology vector determines the number of phases, while each
+    // vector component refers to the number of components of that
+    // phase.
+    // The number of properties is fixed in each case and is
+    // determined by the size of the PropertyEnum enumerator.
+    std::vector<std::size_t> const mediumTopology = {1, 1, 1};
+
+    Medium medium(mediumTopology);
+
+    // the omnivagant medium:
+    medium.property[MPL::name] = "luminiferous_aether";
+
+    medium.phases[0].property[MPL::name] = "Solid";
+    medium.phases[1].property[MPL::name] = "AqueousLiquid";
+    medium.phases[2].property[MPL::name] = "Gas";
+
+    medium.phases[0].component[0].property[MPL::thermal_conductivity] = "0.654";
+    medium.phases[0].component[0].property[MPL::reference_temperature] = "333";
+    medium.phases[0].component[0].property[MPL::reference_density] = "2100.0";
+    medium.phases[0].component[0].property[MPL::drhodT] = "-0.4";
+
+    medium.phases[0].component[0].property[MPL::name] = "VerySolid";
+    medium.phases[1].component[0].property[MPL::name] = "Water";
+    medium.phases[2].component[0].property[MPL::name] = "SuperFluid";
+    medium.phases[2].component[0].property[MPL::thermal_conductivity] = "1";
+    medium.property[MPL::permeability] = "1.0e-12";
+
+    // create an actual MaterialProperty-Medium out of the specifier object
+    auto const m = createTestMaterial(makeMedium(medium));
+
+    // those two vectors will actually be compared
+    std::vector<std::string> expected;
+    std::vector<std::string> observed;
+
+    // now we roam through all phases and components, finding their names
+    // and storing them in the two vectors
+    for (std::size_t p = 0; p < m->numberOfPhases(); ++p)
+    {
+        const auto& phase = m->phase(p);
+        getNames(phase.name(), medium.phases[p].property[MPL::name], "no_name",
+                 observed, expected);
+
+        for (std::size_t c = 0; c < phase.numberOfComponents(); ++c)
+        {
+            const auto& component = phase.component(c);
+            getNames(component.name(),
+                     medium.phases[p].component[c].property[MPL::name],
+                     "no_name", observed, expected);
+        }
+    }
+
+    // Now, the two vectors are compared. If there is some derivation,
+    // we can easily locate the problem.
+    ASSERT_EQ(expected, observed);
+}