diff --git a/GUI/Model/Data/ApplicationModels.cpp b/GUI/Model/Data/ApplicationModels.cpp
index aaaf1338c91561c249a1d605972aa6f61f81af3e..b6b1a083daa90a6de3315accd8bceca3b1051d50 100644
--- a/GUI/Model/Data/ApplicationModels.cpp
+++ b/GUI/Model/Data/ApplicationModels.cpp
@@ -16,28 +16,24 @@
 #include "GUI/Model/Data/RealDataModel.h"
 #include "GUI/Model/Instrument/InstrumentModel.h"
 #include "GUI/Model/Job/JobModel.h"
-#include "GUI/Model/Material/MaterialModel.h"
 #include "GUI/Model/Sample/SampleModel.h"
 #include "GUI/Model/Session/SimulationOptionsItem.h"
 #include "GUI/Util/DeserializationException.h"
-#include <QtCore/QXmlStreamWriter>
+#include <QXmlStreamWriter>
 
 ApplicationModels::ApplicationModels(QObject* parent)
     : QObject(parent)
-    , m_materialModel(nullptr)
     , m_instrumentModel(nullptr)
     , m_sampleModel(nullptr)
     , m_realDataModel(nullptr)
     , m_jobModel(nullptr)
 {
     //! creates and initializes models, order is important
-    m_materialModel = new MaterialModel(this);
     m_sampleModel = new SampleModel(this);
     m_instrumentModel = new InstrumentModel(this);
     m_realDataModel = new RealDataModel(this);
     m_jobModel = new JobModel(this);
 
-    connectModel(m_materialModel);
     connectModel(m_sampleModel);
     connectModel(m_instrumentModel);
     connectModel(m_realDataModel);
@@ -48,11 +44,6 @@ ApplicationModels::ApplicationModels(QObject* parent)
 
 ApplicationModels::~ApplicationModels() = default;
 
-MaterialModel* ApplicationModels::materialModel() const
-{
-    return m_materialModel;
-}
-
 InstrumentModel* ApplicationModels::instrumentModel() const
 {
     return m_instrumentModel;
@@ -76,12 +67,6 @@ JobModel* ApplicationModels::jobModel() const
 //! reset all models to initial state
 void ApplicationModels::resetModels()
 {
-    m_materialModel->clear();
-    m_materialModel->addRefractiveMaterial("Default", 1e-3, 1e-5);
-    m_materialModel->addRefractiveMaterial("Vacuum", 0.0, 0.0);
-    m_materialModel->addRefractiveMaterial("Particle", 6e-4, 2e-8);
-    m_materialModel->addRefractiveMaterial("Substrate", 6e-6, 2e-8);
-
     m_sampleModel->clear();
     m_realDataModel->clear();
     m_jobModel->clear();
@@ -104,6 +89,10 @@ void ApplicationModels::readFrom(QXmlStreamReader* reader, MessageService* messa
                 reader->skipCurrentElement();
                 break;
             }
+            if (reader->name().toString() == "MaterialModel") {
+                reader->skipCurrentElement();
+                break;
+            }
 
             if (model->getModelTag() == reader->name()) {
                 model->readFrom(reader, messageService);
@@ -121,7 +110,6 @@ void ApplicationModels::readFrom(QXmlStreamReader* reader, MessageService* messa
 QList<SessionModel*> ApplicationModels::modelList()
 {
     QList<SessionModel*> result;
-    result.append(m_materialModel);
     result.append(m_instrumentModel);
     result.append(m_sampleModel);
     result.append(m_realDataModel);
diff --git a/GUI/Model/Data/ApplicationModels.h b/GUI/Model/Data/ApplicationModels.h
index 500878004c388283e94dbb82740d7c2f2c3612dc..a127bf60511b900f84cfaf9778aaed41efbe0284 100644
--- a/GUI/Model/Data/ApplicationModels.h
+++ b/GUI/Model/Data/ApplicationModels.h
@@ -20,7 +20,6 @@
 class SessionModel;
 class SessionItem;
 class DocumentModel;
-class MaterialModel;
 class InstrumentModel;
 class RealDataModel;
 class SampleModel;
@@ -33,7 +32,6 @@ public:
     explicit ApplicationModels(QObject* parent = nullptr);
     ~ApplicationModels() override;
 
-    MaterialModel* materialModel() const;
     InstrumentModel* instrumentModel() const;
     SampleModel* sampleModel() const;
     RealDataModel* realDataModel() const;
@@ -56,7 +54,6 @@ private:
     void connectModel(SessionModel* model) const;
 
     DocumentModel* m_documentModel;
-    MaterialModel* m_materialModel;
     InstrumentModel* m_instrumentModel;
     SampleModel* m_sampleModel;
     RealDataModel* m_realDataModel;
diff --git a/GUI/Model/Fit/FitParameterContainerItem.cpp b/GUI/Model/Fit/FitParameterContainerItem.cpp
index e4673cdc8d34ee3f3e15153d2e12ad107d332d8c..8165853658a145525193ed18f71c312c426c24e2 100644
--- a/GUI/Model/Fit/FitParameterContainerItem.cpp
+++ b/GUI/Model/Fit/FitParameterContainerItem.cpp
@@ -15,6 +15,7 @@
 #include "GUI/Model/Fit/FitParameterContainerItem.h"
 #include "Base/Util/Assert.h"
 #include "Fit/Param/Parameters.h"
+#include "GUI/Model/Fit/FitParameterHelper.h"
 #include "GUI/Model/Fit/FitParameterItem.h"
 #include "GUI/Model/Fit/FitParameterLinkItem.h"
 #include "GUI/Model/Fit/ParameterTreeItems.h"
@@ -29,7 +30,7 @@ FitParameterContainerItem::FitParameterContainerItem() : SessionItem(M_TYPE)
 
 //! returns FitParameterItem for given link (path in model)
 
-FitParameterItem* FitParameterContainerItem::fitParameterItem(const QString& link)
+FitParameterItem* FitParameterContainerItem::fitParameterItem(const QString& link) const
 {
     for (FitParameterItem* item : items<FitParameterItem>(T_FIT_PARAMETERS)) {
         for (FitParameterLinkItem* linkItem : item->linkItems()) {
@@ -40,6 +41,12 @@ FitParameterItem* FitParameterContainerItem::fitParameterItem(const QString& lin
     return nullptr;
 }
 
+FitParameterItem*
+FitParameterContainerItem::fitParameterItem(const ParameterItem* parameterItem) const
+{
+    return fitParameterItem(FitParameterHelper::getParameterItemPath(parameterItem));
+}
+
 QVector<FitParameterItem*> FitParameterContainerItem::fitParameterItems()
 {
     return items<FitParameterItem>(T_FIT_PARAMETERS);
diff --git a/GUI/Model/Fit/FitParameterContainerItem.h b/GUI/Model/Fit/FitParameterContainerItem.h
index 7d059150169d3c2c5beb6db9421ec3b9a4729439..0e9c6de103cc7a5b6979e864ef4e19f9417648f0 100644
--- a/GUI/Model/Fit/FitParameterContainerItem.h
+++ b/GUI/Model/Fit/FitParameterContainerItem.h
@@ -21,6 +21,7 @@ namespace mumufit {
 class Parameters;
 }
 class FitParameterItem;
+class ParameterItem;
 
 //! The FitParameterContainerItem class is a collection of all defined fit parameters in JobItem.
 
@@ -32,7 +33,15 @@ public:
     static constexpr auto M_TYPE{"FitParameterContainer"};
 
     FitParameterContainerItem();
-    FitParameterItem* fitParameterItem(const QString& link);
+
+    //! get the fit parameter item whose link matches the given link.
+    //!
+    //! The link is a ParameterItem's path
+    FitParameterItem* fitParameterItem(const QString& link) const;
+
+    //! get the fit parameter item which links the given parameterItem.
+    FitParameterItem* fitParameterItem(const ParameterItem* parameterItem) const;
+
     QVector<FitParameterItem*> fitParameterItems();
     bool isEmpty();
     void setValuesInParameterContainer(const QVector<double>& values,
diff --git a/GUI/Model/Fit/FitParameterHelper.cpp b/GUI/Model/Fit/FitParameterHelper.cpp
index 1331f7956626cc100dde94a49d95b41e8048f185..0a61bdcdff32805ae7c07ae38bbced52982d54be 100644
--- a/GUI/Model/Fit/FitParameterHelper.cpp
+++ b/GUI/Model/Fit/FitParameterHelper.cpp
@@ -13,8 +13,8 @@
 //  ************************************************************************************************
 
 #include "GUI/Model/Fit/FitParameterHelper.h"
-#include "GUI/Model/Fit/FitParameterItem.h"
 #include "GUI/Model/Fit/FitParameterContainerItem.h"
+#include "GUI/Model/Fit/FitParameterItem.h"
 #include "GUI/Model/Fit/FitParameterLinkItem.h"
 #include "GUI/Model/Fit/ParameterTreeItems.h"
 #include "GUI/Model/Job/JobItem.h"
@@ -39,7 +39,7 @@ void FitParameterHelper::createFitParameter(FitParameterContainerItem* container
     fitPar->setStartValue(parameterItem->value().toDouble());
     link->setLink(getParameterItemPath(parameterItem));
 
-    fitPar->initMinMaxValues(parameterItem->linkedItem()->limits());
+    fitPar->initMinMaxValues(parameterItem->limitsOfLink());
 }
 
 //! Removes link to given parameterItem from fit parameters
@@ -47,7 +47,7 @@ void FitParameterHelper::createFitParameter(FitParameterContainerItem* container
 void FitParameterHelper::removeFromFitParameters(FitParameterContainerItem* container,
                                                  ParameterItem* parameterItem)
 {
-    FitParameterItem* fitParItem = getFitParameterItem(container, parameterItem);
+    FitParameterItem* fitParItem = container->fitParameterItem(parameterItem);
 
     if (fitParItem) {
         for (auto* linkItem : fitParItem->linkItems()) {
@@ -77,15 +77,6 @@ void FitParameterHelper::addToFitParameter(FitParameterContainerItem* container,
     }
 }
 
-//! Returns fFitParameterItem corresponding to given ParameterItem
-
-FitParameterItem* FitParameterHelper::getFitParameterItem(FitParameterContainerItem* container,
-                                                          ParameterItem* parameterItem)
-{
-    ASSERT(container);
-    return container->fitParameterItem(getParameterItemPath(parameterItem));
-}
-
 //! Returns list of fit parameter display names
 
 QStringList FitParameterHelper::getFitParameterNames(FitParameterContainerItem* container)
diff --git a/GUI/Model/Fit/FitParameterHelper.h b/GUI/Model/Fit/FitParameterHelper.h
index 75f52c226f4de9172fa8dae070e41bd55279dccc..5d7d7e5547e5251b2f82f04a1dfd291436e142e5 100644
--- a/GUI/Model/Fit/FitParameterHelper.h
+++ b/GUI/Model/Fit/FitParameterHelper.h
@@ -31,9 +31,6 @@ void removeFromFitParameters(FitParameterContainerItem* container, ParameterItem
 void addToFitParameter(FitParameterContainerItem* container, ParameterItem* parameterItem,
                        const QString& fitParName);
 
-FitParameterItem* getFitParameterItem(FitParameterContainerItem* container,
-                                      ParameterItem* parameterItem);
-
 QStringList getFitParameterNames(FitParameterContainerItem* container);
 QString getParameterItemPath(const ParameterItem* parameterItem);
 ParameterItem* getParameterItem(FitParameterContainerItem* container, const QString& link);
diff --git a/GUI/Model/Fit/ParameterTreeItems.cpp b/GUI/Model/Fit/ParameterTreeItems.cpp
index 98dd051f8482d4e2caf97a7eca9d7bca4a6d0207..d1107ff06fce27ffe46ca7cc04eb01b04f606c3c 100644
--- a/GUI/Model/Fit/ParameterTreeItems.cpp
+++ b/GUI/Model/Fit/ParameterTreeItems.cpp
@@ -14,8 +14,12 @@
 
 #include "GUI/Model/Fit/ParameterTreeItems.h"
 #include "GUI/Model/Job/JobItem.h"
+#include "GUI/Model/Job/JobModelFunctions.h"
+#include "GUI/Model/Material/MaterialItem.h"
+#include "GUI/Model/Material/MaterialModel.h"
 #include "GUI/Model/Session/ModelPath.h"
 #include "GUI/Model/Session/SessionXML.h"
+#include "GUI/Model/Types/VectorDescriptor.h"
 #include "GUI/Util/DeserializationException.h"
 #include <QXmlStreamReader>
 #include <QXmlStreamWriter>
@@ -50,23 +54,29 @@ void ParameterItem::propagateValueToLink(double newValue)
 {
     setValue(newValue);
 
-    if (SessionItem* item = linkedItem())
-        item->setValue(newValue);
+    m_d.set(newValue);
 }
 
-//! Returns corresponding linked item in MultiLayerItem/InstrumentItem
+void ParameterItem::linkToDescriptor(DoubleDescriptor d)
+{
+    m_link = d.path();
+    m_d = d;
+}
+
+void ParameterItem::linkToSessionItem(SessionItem* item)
+{
+    m_link = GUI::Model::Path::getPathFromIndex(item->index());
+    m_d = DoubleDescriptor(item, "");
+}
 
-SessionItem* ParameterItem::linkedItem()
+RealLimits ParameterItem::limitsOfLink() const
 {
-    const SessionItem* jobItem = GUI::Model::Path::ancestor(this, JobItem::M_TYPE);
-    ASSERT(jobItem);
-    QString link = jobItem->itemName() + "/" + m_link;
-    return model()->itemForIndex(GUI::Model::Path::getIndexFromPath(model(), link));
+    return m_d.limits;
 }
 
-void ParameterItem::setLink(const QString& link)
+int ParameterItem::decimalsOfLink() const
 {
-    m_link = link;
+    return m_d.decimals;
 }
 
 QString ParameterItem::link() const
diff --git a/GUI/Model/Fit/ParameterTreeItems.h b/GUI/Model/Fit/ParameterTreeItems.h
index 78f8bf46a23a4c83529e7e3b356adf2b7c32b211..07a591c2d0cef64a0e1da2cca2afa23504f98da1 100644
--- a/GUI/Model/Fit/ParameterTreeItems.h
+++ b/GUI/Model/Fit/ParameterTreeItems.h
@@ -16,6 +16,9 @@
 #define BORNAGAIN_GUI_MODEL_FIT_PARAMETERTREEITEMS_H
 
 #include "GUI/Model/Session/SessionItem.h"
+#include "GUI/Model/Types/DoubleDescriptor.h"
+
+class MaterialItem;
 
 //! ParameterTreeItems is a collection of items necessary to form a tuning tree for
 //! real time widget.
@@ -39,12 +42,28 @@ public:
     ParameterItem();
 
     void propagateValueToLink(double newValue);
-    SessionItem* linkedItem();
-    void setLink(const QString& link);
+
+    //! Unique string to identify this ParameterItem.
+    //!
+    //! The link is arbitrary. It can't be used for finding the linked item (therefore it does
+    //! not have to be a model path). However, it is used for comparison, also across project
+    //! load/save. Therefore the link is always the same, not e.g. an always generated UUID.
+    //! This link is used for setting backup values and for finding this ParameterItem when
+    //! referring from fit parameters.
     QString link() const;
 
+    //! Links this item to the given value defined by a descriptor.
+    void linkToDescriptor(DoubleDescriptor d);
+
+    //! Links this item to the given session item.
+    void linkToSessionItem(SessionItem* item);
+
+    RealLimits limitsOfLink() const;
+    int decimalsOfLink() const;
+
 private:
-    QString m_link; //!< Link to original PropertyItem
+    QString m_link;       //!< See docu of link()
+    DoubleDescriptor m_d; //!< The linked double value
 };
 
 //! The ParameterContainerItem is a top item to hold all ParameterItem, represents an entry
diff --git a/GUI/Model/Group/ItemCatalog.cpp b/GUI/Model/Group/ItemCatalog.cpp
index 9b9a3c560caecbb5502be6ffb828374757307dbc..ecdc4f3610a7b03a4f8e42ca8d142dfb624675b5 100644
--- a/GUI/Model/Group/ItemCatalog.cpp
+++ b/GUI/Model/Group/ItemCatalog.cpp
@@ -40,9 +40,6 @@
 #include "GUI/Model/Instrument/SpecularBeamInclinationItem.h"
 #include "GUI/Model/Instrument/SphericalDetectorItem.h"
 #include "GUI/Model/Job/JobItem.h"
-#include "GUI/Model/Material/MaterialDataItems.h"
-#include "GUI/Model/Material/MaterialItem.h"
-#include "GUI/Model/Material/MaterialItemContainer.h"
 #include "GUI/Model/Sample/FTDecayFunctionItems.h"
 #include "GUI/Model/Sample/FTDistributionItems.h"
 #include "GUI/Model/Sample/FormFactorItems.h"
@@ -260,12 +257,6 @@ ItemCatalog::ItemCatalog()
     addItem<SquareLattice2DItem>("Square");
     addItem<HexagonalLattice2DItem>("Hexagonal");
 
-    addItem<MaterialItem>();
-    addItem<MaterialItemContainer>();
-
-    addItem<MaterialRefractiveDataItem>();
-    addItem<MaterialSLDDataItem>();
-
     addItem<JobItem>();
 
     addItem<IntensityDataItem>();
diff --git a/GUI/Model/Job/JobItem.cpp b/GUI/Model/Job/JobItem.cpp
index 90df6955523c3e83b4307d5ce0f27930e556ec9f..23ca2f32d1abcd5a6decc7dcb960212440fa6b1f 100644
--- a/GUI/Model/Job/JobItem.cpp
+++ b/GUI/Model/Job/JobItem.cpp
@@ -23,7 +23,6 @@
 #include "GUI/Model/IO/ItemFileNameUtils.h"
 #include "GUI/Model/Instrument/InstrumentItems.h"
 #include "GUI/Model/Job/JobItemUtils.h"
-#include "GUI/Model/Material/MaterialItemContainer.h"
 #include "GUI/Model/Sample/ItemWithMaterial.h"
 #include "GUI/Model/Sample/MultiLayerItem.h"
 #include "GUI/Model/Session/SessionXML.h"
@@ -35,6 +34,7 @@ using namespace GUI::Session::XML;
 
 namespace {
 const QString SimulationOptionsTag("SimulationOptions");
+const QString MaterialsTag("Materials");
 } // namespace
 
 
@@ -56,7 +56,6 @@ JobItem::JobItem() : SessionItem(M_TYPE)
     addProperty(P_PRESENTATION_TYPE, QVariant::Type::Invalid)->setVisible(false);
 
     registerTag(T_SAMPLE, 1, 1, {MultiLayerItem::M_TYPE});
-    registerTag(T_MATERIAL_CONTAINER, 1, 1, {MaterialItemContainer::M_TYPE});
     registerTag(T_INSTRUMENT, 1, 1,
                 {GISASInstrumentItem::M_TYPE, OffSpecularInstrumentItem::M_TYPE,
                  SpecularInstrumentItem::M_TYPE, DepthProbeInstrumentItem::M_TYPE});
@@ -252,7 +251,7 @@ MultiLayerItem* JobItem::copySampleIntoJob(const MultiLayerItem* sample)
     // initialize finder-function of items with material
     for (auto* itemWithMaterial : sampleItem()->itemsWithMaterial()) {
         itemWithMaterial->setMaterialFinder(
-            [=](const QString& id) { return materialContainerItem()->findMaterialById(id); });
+            [=](const QString& id) { return materialItems().materialFromIdentifier(id); });
     }
 
     return sampleItem();
@@ -339,16 +338,6 @@ RealDataItem* JobItem::createRealDataItem()
     return model()->insertItem<RealDataItem>(this, -1, T_REALDATA);
 }
 
-MaterialItemContainer* JobItem::materialContainerItem() const
-{
-    return dynamic_cast<MaterialItemContainer*>(getItem(JobItem::T_MATERIAL_CONTAINER));
-}
-
-MaterialItemContainer* JobItem::createMaterialContainer()
-{
-    return model()->insertItem<MaterialItemContainer>(this, -1, T_MATERIAL_CONTAINER);
-}
-
 Data1DViewItem* JobItem::dataItemView()
 {
     return dynamic_cast<Data1DViewItem*>(getItem(JobItem::T_DATAVIEW));
@@ -401,6 +390,10 @@ void JobItem::writeNonSessionItems(QXmlStreamWriter* writer) const
     writer->writeStartElement(SimulationOptionsTag);
     m_simulationOptionsItem.writeContentTo(writer);
     writer->writeEndElement();
+
+    writer->writeStartElement(MaterialsTag);
+    m_materials.writeContentTo(writer);
+    writer->writeEndElement();
 }
 
 void JobItem::readNonSessionItems(QXmlStreamReader* reader)
@@ -417,10 +410,28 @@ void JobItem::readNonSessionItems(QXmlStreamReader* reader)
         if (reader->name() == SimulationOptionsTag) {
             m_simulationOptionsItem.readContentFrom(reader);
             reader->skipCurrentElement();
+        } else if (reader->name() == MaterialsTag) {
+            m_materials.readContentFrom(reader);
+            reader->skipCurrentElement();
         }
     }
 }
 
+const MaterialModel& JobItem::materialItems() const
+{
+    return m_materials;
+}
+
+MaterialModel& JobItem::materialItems()
+{
+    return m_materials;
+}
+
+bool JobItem::isSpecularJob() const
+{
+    return instrumentItem()->is<SpecularInstrumentItem>();
+}
+
 //! Updates the name of file to store intensity data.
 
 void JobItem::updateIntensityDataFileName()
diff --git a/GUI/Model/Job/JobItem.h b/GUI/Model/Job/JobItem.h
index 16d34064e0ec0db3afcdfa3d88cedf3d44008d44..8d0230c359dd8a817938f696e176041e62b24c2a 100644
--- a/GUI/Model/Job/JobItem.h
+++ b/GUI/Model/Job/JobItem.h
@@ -16,6 +16,7 @@
 #define BORNAGAIN_GUI_MODEL_JOB_JOBITEM_H
 
 #include "GUI/Model/Job/JobStatus.h" // enum cannot be forward declared
+#include "GUI/Model/Material/MaterialModel.h"
 #include "GUI/Model/Session/SessionItem.h"
 #include "GUI/Model/Session/SessionModel.h" // call to model() from templated fct
 #include "GUI/Model/Session/SimulationOptionsItem.h"
@@ -130,9 +131,6 @@ public:
     void addRealDataItem(RealDataItem* real_data);
     RealDataItem* createRealDataItem();
 
-    MaterialItemContainer* materialContainerItem() const;
-    MaterialItemContainer* createMaterialContainer();
-
     Data1DViewItem* dataItemView();
     void addDataViewItem(Data1DViewItem* data_view);
     Data1DViewItem* createDataViewItem();
@@ -149,10 +147,15 @@ public:
     void writeNonSessionItems(QXmlStreamWriter* writer) const override;
     void readNonSessionItems(QXmlStreamReader* reader) override;
 
+    const MaterialModel& materialItems() const;
+    MaterialModel& materialItems();
+    bool isSpecularJob() const;
+
 private:
     void updateIntensityDataFileName();
 
     SimulationOptionsItem m_simulationOptionsItem;
+    MaterialModel m_materials;
 };
 
 template <typename T> T* JobItem::setDataType()
diff --git a/GUI/Model/Job/JobModel.cpp b/GUI/Model/Job/JobModel.cpp
index 9f882e1d76d093c9e86bd948209b5db9583c0a08..8130489f427dd85ff244fa85fa2264f82dcf8942 100644
--- a/GUI/Model/Job/JobModel.cpp
+++ b/GUI/Model/Job/JobModel.cpp
@@ -23,7 +23,6 @@
 #include "GUI/Model/Job/JobModelFunctions.h"
 #include "GUI/Model/Job/JobQueueData.h"
 #include "GUI/Model/Job/ParameterTreeUtils.h"
-#include "GUI/Model/Material/MaterialItemContainer.h"
 #include "GUI/Model/Sample/ItemWithMaterial.h"
 #include "GUI/Model/Sample/MultiLayerItem.h"
 #include "GUI/Util/Path.h"
@@ -170,7 +169,7 @@ void JobModel::readFrom(QXmlStreamReader* reader, MessageService* messageService
         // initialize the material finder functions
         for (auto* itemWithMaterial : jobItem->sampleItem()->itemsWithMaterial())
             itemWithMaterial->setMaterialFinder([=](const QString& id) {
-                return jobItem->materialContainerItem()->findMaterialById(id);
+                return jobItem->materialItems().materialFromIdentifier(id);
             });
 
         // Create the not stored parameter tuning tree
diff --git a/GUI/Model/Job/JobModelFunctions.cpp b/GUI/Model/Job/JobModelFunctions.cpp
index e7f99ef7ed3fb615d0d41a87a846e7b35cc20a5b..b57df4b4f5b64afbbb32087038a43d12568f1b08 100644
--- a/GUI/Model/Job/JobModelFunctions.cpp
+++ b/GUI/Model/Job/JobModelFunctions.cpp
@@ -29,7 +29,6 @@
 #include "GUI/Model/Job/JobItem.h"
 #include "GUI/Model/Job/JobItemUtils.h"
 #include "GUI/Model/Material/MaterialItem.h"
-#include "GUI/Model/Material/MaterialItemContainer.h"
 #include "GUI/Model/Sample/ItemWithMaterial.h"
 #include "GUI/Model/Sample/MultiLayerItem.h"
 #include "GUI/Util/Error.h"
@@ -130,14 +129,10 @@ void GUI::Model::JobFunctions::setupJobItemSampleData(JobItem* jobItem,
     multilayer->setItemName(MultiLayerItem::M_TYPE);
 
     // copy used materials into material container
-    MaterialItemContainer* container = jobItem->createMaterialContainer();
-
     for (auto* itemWithMaterial : sampleItem->itemsWithMaterial()) {
         auto* material = itemWithMaterial->materialItem();
-        if (!container->findMaterialById(material->identifier())) {
-            auto* materialCopy = container->insertCopy(material);
-            materialCopy->setIdentifier(material->identifier()); // insertCopy() doesn't do this
-        }
+        if (!jobItem->materialItems().findMaterialItem(material->identifier()))
+            jobItem->materialItems().addMaterial(new MaterialItem(*material));
     }
 }
 
@@ -206,10 +201,6 @@ void GUI::Model::JobFunctions::setupJobItemForFit(JobItem* jobItem,
 
 void GUI::Model::JobFunctions::muteMagnetizationData(JobItem* jobItem)
 {
-    MaterialItemContainer* container = jobItem->materialContainerItem();
-    for (MaterialItem* material : container->getMaterials())
-        material->hideMagnetization();
-
     MultiLayerItem* sample = jobItem->sampleItem();
     sample->externalFieldItem()->setVisible(false);
 }
diff --git a/GUI/Model/Job/ParameterTreeUtils.cpp b/GUI/Model/Job/ParameterTreeUtils.cpp
index 7a7a2ca73c92b64f342682cf6fbbd028aee19fe5..f64a7423aba98cc1717addea2cfa9adab1f1028d 100644
--- a/GUI/Model/Job/ParameterTreeUtils.cpp
+++ b/GUI/Model/Job/ParameterTreeUtils.cpp
@@ -18,9 +18,11 @@
 #include "GUI/Model/Group/PropertyItem.h"
 #include "GUI/Model/Instrument/InstrumentItems.h"
 #include "GUI/Model/Job/JobItem.h"
-#include "GUI/Model/Material/MaterialItemContainer.h"
+#include "GUI/Model/Material/MaterialItem.h"
 #include "GUI/Model/Sample/MultiLayerItem.h"
 #include "GUI/Model/Session/ModelPath.h"
+#include "GUI/Model/Types/VectorDescriptor.h"
+
 #include "GUI/Util/Error.h"
 #include <QStack>
 
@@ -29,7 +31,7 @@ using boost::polymorphic_downcast;
 
 namespace {
 
-void handleItem(ParameterContainerItem* container, SessionItem* tree, const SessionItem* source,
+void handleItem(ParameterContainerItem* container, SessionItem* tree, SessionItem* source,
                 bool recreateBackupValues)
 {
     if (tree->hasModelType<ParameterLabelItem>())
@@ -38,13 +40,10 @@ void handleItem(ParameterContainerItem* container, SessionItem* tree, const Sess
     else if (tree->hasModelType<ParameterItem>()) {
         tree->setDisplayName(source->itemName());
 
-        double sourceValue = source->value().toDouble();
+        const double sourceValue = source->value().toDouble();
         tree->setValue(QVariant(sourceValue));
-        QString path = GUI::Model::Path::getPathFromIndex(source->index());
-        int firstSlash = path.indexOf('/');
-        path = path.mid(firstSlash + 1);
         auto* parItem = polymorphic_downcast<ParameterItem*>(tree);
-        parItem->setLink(path);
+        parItem->linkToSessionItem(source);
         if (recreateBackupValues)
             container->setBackupValue(parItem->link(), sourceValue);
         return;
@@ -78,7 +77,7 @@ void handleItem(ParameterContainerItem* container, SessionItem* tree, const Sess
 //! Populates ParameterContainer with ParameterItem's corresponding to all properties found
 //! in a source item.
 
-void populateParameterContainer(ParameterContainerItem* container, const SessionItem* source,
+void populateParameterContainer(ParameterContainerItem* container, SessionItem* source,
                                 bool recreateBackupValues)
 {
     auto* sourceLabel = container->model()->insertItem<ParameterLabelItem>(container);
@@ -94,9 +93,37 @@ void GUI::Model::ParameterTreeUtils::createParameterTree(JobItem* jobItem,
     if (!container)
         container = jobItem->createParameterContainerItem();
 
-    populateParameterContainer(container, jobItem->materialContainerItem(), recreateBackupValues);
+    // add the job's materials
+    auto* materialTopLabel = container->model()->insertItem<ParameterLabelItem>(container);
+    materialTopLabel->setDisplayName("Materials");
+    for (auto* material : jobItem->materialItems().materialItems()) {
+        auto* materialLabel = container->model()->insertItem<ParameterLabelItem>(container);
+        materialLabel->setDisplayName(material->materialName());
+
+        DoubleDescriptors descriptors;
+        if (material->hasRefractiveIndex())
+            descriptors << material->delta() << material->beta();
+        else
+            descriptors << material->sldRe() << material->sldIm();
+
+        // TODO: remove when specular instrument is ready for magnetization
+        if (!jobItem->isSpecularJob())
+            descriptors << material->magnetizationVector().x << material->magnetizationVector().y
+                        << material->magnetizationVector().z;
+
+        for (auto d : descriptors) {
+            auto* materialValue = materialLabel->model()->insertItem<ParameterItem>(materialLabel);
+            materialValue->setDisplayName(d.label);
+            materialValue->setValue(QVariant(d.get()));
+            materialValue->linkToDescriptor(d);
+            if (recreateBackupValues)
+                container->setBackupValue(materialValue->link(), d.get());
+        }
+    }
 
+    // add sample
     populateParameterContainer(container, jobItem->sampleItem(), recreateBackupValues);
 
+    // add instrument
     populateParameterContainer(container, jobItem->instrumentItem(), recreateBackupValues);
 }
diff --git a/GUI/Model/Material/MaterialDataItems.cpp b/GUI/Model/Material/MaterialDataItems.cpp
deleted file mode 100644
index ebd044005cd1ac27dd506bb7006e11145e996649..0000000000000000000000000000000000000000
--- a/GUI/Model/Material/MaterialDataItems.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-//  ************************************************************************************************
-//
-//  BornAgain: simulate and fit reflection and scattering
-//
-//! @file      GUI/Model/Material/MaterialDataItems.cpp
-//! @brief     Implements MaterialDataItems classes
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-//  ************************************************************************************************
-
-#include "GUI/Model/Material/MaterialDataItems.h"
-
-MaterialDataItem::MaterialDataItem(const QString& modelType) : SessionItem(modelType)
-{
-    setEditable(false); // empty label, non-editable
-}
-
-// ------------------------------------------------------------------------------------------------
-
-MaterialRefractiveDataItem::MaterialRefractiveDataItem() : MaterialDataItem(M_TYPE)
-{
-    addProperty(P_DELTA, 0.0)
-        ->setEditorType("ScientificDouble")
-        .setLimits(RealLimits::limitless())
-        .setToolTip("Delta of refractive index (n = 1 - delta + i*beta)");
-    addProperty(P_BETA, 0.0)
-        ->setEditorType("ScientificDouble")
-        .setLimits(RealLimits::limitless())
-        .setToolTip("Beta of refractive index (n = 1 - delta + i*beta)");
-}
-
-// ------------------------------------------------------------------------------------------------
-
-MaterialSLDDataItem::MaterialSLDDataItem() : MaterialDataItem(M_TYPE)
-{
-    addProperty(P_SLD_REAL, 0.0)
-        ->setEditorType("ScientificDouble")
-        .setLimits(RealLimits::limitless())
-        .setToolTip("Real part of SLD (SLD = real - i*imag), AA^{-2}");
-    addProperty(P_SLD_IMAG, 0.0)
-        ->setEditorType("ScientificDouble")
-        .setLimits(RealLimits::limitless())
-        .setToolTip("Imaginary part of SLD (SLD = real - i*imag), AA^{-2}");
-}
diff --git a/GUI/Model/Material/MaterialDataItems.h b/GUI/Model/Material/MaterialDataItems.h
deleted file mode 100644
index ea25e70dd7583d41a8f4d4e20589076ed3f44651..0000000000000000000000000000000000000000
--- a/GUI/Model/Material/MaterialDataItems.h
+++ /dev/null
@@ -1,51 +0,0 @@
-//  ************************************************************************************************
-//
-//  BornAgain: simulate and fit reflection and scattering
-//
-//! @file      GUI/Model/Material/MaterialDataItems.h
-//! @brief     Defines MaterialDataItems classes
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-//  ************************************************************************************************
-
-#ifndef BORNAGAIN_GUI_MODEL_MATERIAL_MATERIALDATAITEMS_H
-#define BORNAGAIN_GUI_MODEL_MATERIAL_MATERIALDATAITEMS_H
-
-#include "GUI/Model/Session/SessionItem.h"
-
-class BA_CORE_API_ MaterialDataItem : public SessionItem {
-protected:
-    MaterialDataItem(const QString& modelType);
-};
-
-class MaterialRefractiveDataItem : public MaterialDataItem {
-private:
-    static constexpr auto P_DELTA{"Delta"};
-    static constexpr auto P_BETA{"Beta"};
-
-public:
-    static constexpr auto M_TYPE{"MaterialRefractiveData"};
-
-    MaterialRefractiveDataItem();
-
-    friend class MaterialItem;
-};
-
-class MaterialSLDDataItem : public MaterialDataItem {
-private:
-    static constexpr auto P_SLD_REAL{"SLD, real"};
-    static constexpr auto P_SLD_IMAG{"SLD, imaginary"};
-
-public:
-    static constexpr auto M_TYPE{"MaterialSLDData"};
-
-    MaterialSLDDataItem();
-
-    friend class MaterialItem;
-};
-
-#endif // BORNAGAIN_GUI_MODEL_MATERIAL_MATERIALDATAITEMS_H
diff --git a/GUI/Model/Material/MaterialItem.cpp b/GUI/Model/Material/MaterialItem.cpp
index 03257f1983719f6e9d2d76e126b794dde353e06f..a9defd62f76b1838020d34f8560a0692382951b0 100644
--- a/GUI/Model/Material/MaterialItem.cpp
+++ b/GUI/Model/Material/MaterialItem.cpp
@@ -14,179 +14,293 @@
 
 #include "GUI/Model/Material/MaterialItem.h"
 #include "Base/Util/Assert.h"
-#include "GUI/Model/Group/GroupInfo.h"
-#include "GUI/Model/Material/MaterialDataItems.h"
+#include "GUI/Model/Session/ModelPath.h"
+#include "GUI/Model/Session/SessionXML.h"
 #include "GUI/Model/Types/VectorDescriptor.h"
+#include "GUI/Util/DeserializationException.h"
 #include "Sample/Material/MaterialFactoryFuncs.h"
 #include <QColor>
 #include <QUuid>
+#include <QXmlStreamReader>
 
-namespace {
-
-const QString magnetization_tooltip = "Magnetization (A/m)";
+using namespace GUI::Session::XML;
 
+namespace {
+namespace Tags {
+const QString Color("Color");
+const QString Id("Id");
+const QString Name("Name");
+const QString Delta("Delta");
+const QString Beta("Beta");
+const QString Sld("Sld");
+const QString Magnetization("Magnetization");
+} // namespace Tags
+} // namespace
+
+#define m_delta std::get<Refractive>(m_data).delta
+#define m_beta std::get<Refractive>(m_data).beta
+
+#define m_real std ::get<complex_t>(m_data).real()
+#define m_imag std ::get<complex_t>(m_data).imag()
+
+MaterialItem::MaterialItem() : m_data(Refractive(0.0, 0.0))
+{
+    m_color = Qt::red;
+    m_id = QUuid::createUuid().toString();
 }
 
-
-MaterialItem::MaterialItem() : SessionItem(M_TYPE)
+MaterialItem::MaterialItem(const MaterialItem& other)
+    : m_name(other.m_name)
+    , m_id(other.m_id)
+    , m_color(other.m_color)
+    , m_data(other.m_data)
+    , m_magnetization(other.m_magnetization)
 {
-    setItemName("Material");
-
-    addProperty(P_COLOR, QColor(Qt::red));
-
-    GroupInfo info;
-    info.add(MaterialRefractiveDataItem::M_TYPE, "Refractive index based");
-    info.add(MaterialSLDDataItem::M_TYPE, "SLD based");
-    info.setDefaultType(MaterialRefractiveDataItem::M_TYPE);
-    addGroupProperty(P_MATERIAL_DATA, info);
-
-    addProperty<VectorItem>(P_MAGNETIZATION)->setToolTip(magnetization_tooltip);
-    addProperty(P_IDENTIFIER, QUuid::createUuid().toString());
-    getItem(P_IDENTIFIER)->setVisible(false);
 }
 
-//! Turns material into refractive index material.
-
 void MaterialItem::setRefractiveIndex(const double delta, const double beta)
 {
-    auto* refractiveData = setGroupProperty(P_MATERIAL_DATA, MaterialRefractiveDataItem::M_TYPE);
-    refractiveData->setItemValue(MaterialRefractiveDataItem::P_DELTA, delta);
-    refractiveData->setItemValue(MaterialRefractiveDataItem::P_BETA, beta);
-}
+    const Refractive r(delta, beta);
+    if (hasRefractiveIndex() && std::get<Refractive>(m_data) == r)
+        return;
 
-//! Turns material into SLD based material.
+    m_data = r;
+    emit dataChanged();
+}
 
 void MaterialItem::setScatteringLengthDensity(const complex_t sld)
 {
-    auto* sldData = setGroupProperty(P_MATERIAL_DATA, MaterialSLDDataItem::M_TYPE);
-    sldData->setItemValue(MaterialSLDDataItem::P_SLD_REAL, sld.real());
-    sldData->setItemValue(MaterialSLDDataItem::P_SLD_IMAG, sld.imag());
+    if (!hasRefractiveIndex() && std::get<complex_t>(m_data) == sld)
+        return;
+
+    m_data = sld;
+    emit dataChanged();
 }
 
 DoubleDescriptor MaterialItem::delta()
 {
-    return DoubleDescriptor(
-        getGroupItem(P_MATERIAL_DATA)->getItem(MaterialRefractiveDataItem::P_DELTA),
-        Unit::unitless);
+    ASSERT(hasRefractiveIndex());
+    const auto setter = [=](double v) {
+        if (m_delta != v) {
+            m_delta = v;
+            emit dataChanged();
+        }
+    };
+
+    DoubleDescriptor d(
+        "Delta", "Delta of refractive index (n = 1 - delta + i*beta)", 3, RealLimits::limitless(),
+        setter, [=] { return m_delta; }, Unit::unitless);
+    d.path = [=] { return GUI::Model::Path::getPathFromItem(this) + "/delta"; };
+    return d;
 }
 
 DoubleDescriptor MaterialItem::beta()
 {
-    return DoubleDescriptor(
-        getGroupItem(P_MATERIAL_DATA)->getItem(MaterialRefractiveDataItem::P_BETA), Unit::unitless);
+    ASSERT(hasRefractiveIndex());
+    const auto setter = [=](double v) {
+        if (m_beta != v) {
+            m_beta = v;
+            emit dataChanged();
+        }
+    };
+
+    DoubleDescriptor d(
+        "Beta", "Beta of refractive index (n = 1 - delta + i*beta)", 3, RealLimits::limitless(),
+        setter, [=] { return m_beta; }, Unit::unitless);
+    d.path = [=] { return GUI::Model::Path::getPathFromItem(this) + "/beta"; };
+    return d;
 }
 
 DoubleDescriptor MaterialItem::sldRe()
 {
-    return DoubleDescriptor(getGroupItem(P_MATERIAL_DATA)->getItem(MaterialSLDDataItem::P_SLD_REAL),
-                            Unit::unitless);
+    ASSERT(!hasRefractiveIndex());
+    const auto setter = [=](double v) {
+        if (m_real != v) {
+            std::get<complex_t>(m_data).real(v);
+            emit dataChanged();
+        }
+    };
+
+    DoubleDescriptor d(
+        "SLD, real", "Real part of SLD (SLD = real - i*imag), AA^{-2}", 3, RealLimits::limitless(),
+        setter, [=] { return m_real; }, Unit::unitless);
+    d.path = [=] { return GUI::Model::Path::getPathFromItem(this) + "/re"; };
+    return d;
 }
 
 DoubleDescriptor MaterialItem::sldIm()
 {
-    return DoubleDescriptor(getGroupItem(P_MATERIAL_DATA)->getItem(MaterialSLDDataItem::P_SLD_IMAG),
-                            Unit::unitless);
+    ASSERT(!hasRefractiveIndex());
+    const auto setter = [=](double v) {
+        if (m_imag != v) {
+            std::get<complex_t>(m_data).imag(v);
+            emit dataChanged();
+        }
+    };
+
+    DoubleDescriptor d(
+        "SLD, imaginary", "Imaginary part of SLD (SLD = real - i*imag), AA^{-2}", 3,
+        RealLimits::limitless(), setter, [=] { return m_imag; }, Unit::unitless);
+    d.path = [=] { return GUI::Model::Path::getPathFromItem(this) + "/im"; };
+    return d;
 }
 
 VectorDescriptor MaterialItem::magnetizationVector()
 {
-    return VectorDescriptor(item<VectorItem>(P_MAGNETIZATION), "A/m");
+    VectorDescriptor d("Magnetization", "Magnetization (A/m)", "A/m");
+
+    d.x.set = [=](double v) {
+        if (m_magnetization.x() != v) {
+            m_magnetization.setX(v);
+            emit dataChanged();
+        }
+    };
+    d.x.get = [=]() { return m_magnetization.x(); };
+    d.x.path = [=] { return GUI::Model::Path::getPathFromItem(this) + "/mx"; };
+
+    d.y.set = [=](double v) {
+        if (m_magnetization.y() != v) {
+            m_magnetization.setY(v);
+            emit dataChanged();
+        }
+    };
+    d.y.get = [=]() { return m_magnetization.y(); };
+    d.y.path = [=] { return GUI::Model::Path::getPathFromItem(this) + "/my"; };
+
+    d.z.set = [=](double v) {
+        if (m_magnetization.z() != v) {
+            m_magnetization.setZ(v);
+            emit dataChanged();
+        }
+    };
+    d.z.get = [=]() { return m_magnetization.z(); };
+    d.z.path = [=] { return GUI::Model::Path::getPathFromItem(this) + "/mz"; };
+
+    return d;
 }
 
 bool MaterialItem::hasRefractiveIndex() const
 {
-    SessionItem* data = getGroupItem(P_MATERIAL_DATA);
-    ASSERT(data);
-    return dynamic_cast<MaterialRefractiveDataItem*>(data) != nullptr;
-}
-
-double MaterialItem::refractiveIndexDelta() const
-{
-    ASSERT(hasRefractiveIndex());
-    return getGroupItem(P_MATERIAL_DATA)
-        ->getItemValue(MaterialRefractiveDataItem::P_DELTA)
-        .toDouble();
-}
-
-double MaterialItem::refractiveIndexBeta() const
-{
-    ASSERT(hasRefractiveIndex());
-    return getGroupItem(P_MATERIAL_DATA)
-        ->getItemValue(MaterialRefractiveDataItem::P_BETA)
-        .toDouble();
-}
-
-complex_t MaterialItem::scatteringLengthDensity() const
-{
-    ASSERT(!hasRefractiveIndex());
-    SessionItem* ri = getGroupItem(P_MATERIAL_DATA);
-    double real = ri->getItemValue(MaterialSLDDataItem::P_SLD_REAL).toDouble();
-    double imag = ri->getItemValue(MaterialSLDDataItem::P_SLD_IMAG).toDouble();
-    return complex_t(real, imag);
+    return std::holds_alternative<Refractive>(m_data);
 }
 
 QString MaterialItem::materialName() const
 {
-    return itemName();
+    return m_name;
 }
 
 void MaterialItem::setMaterialName(const QString& name)
 {
-    setItemName(name);
+    if (m_name != name) {
+        m_name = name;
+        emit dataChanged();
+    }
 }
 
 QString MaterialItem::identifier() const
 {
-    return getItemValue(P_IDENTIFIER).toString();
+    return m_id;
 }
 
 void MaterialItem::setIdentifier(const QString& id)
 {
-    setItemValue(P_IDENTIFIER, id);
+    m_id = id;
+    // no "emit dataChanged()" here
+}
+
+void MaterialItem::createNewIdentifier()
+{
+    m_id = QUuid::createUuid().toString();
+    // no "emit dataChanged()" here
 }
 
 QColor MaterialItem::color() const
 {
-    return getItemValue(P_COLOR).value<QColor>();
+    return m_color;
 }
 
 void MaterialItem::setColor(const QColor& color)
 {
-    setItemValue(P_COLOR, color);
+    if (m_color != color) {
+        m_color = color;
+        emit dataChanged();
+    }
 }
 
 R3 MaterialItem::magnetization() const
 {
-    return item<VectorItem>(P_MAGNETIZATION)->getVector();
+    return m_magnetization;
 }
 
 void MaterialItem::setMagnetization(const R3& magnetization)
 {
-    item<VectorItem>(P_MAGNETIZATION)->setVector(magnetization);
+    if (m_magnetization != magnetization) {
+        m_magnetization = magnetization;
+        emit dataChanged();
+    }
 }
 
-void MaterialItem::hideMagnetization()
+std::unique_ptr<Material> MaterialItem::createMaterial() const
 {
-    getItem(MaterialItem::P_MAGNETIZATION)->setVisible(false);
+    if (hasRefractiveIndex())
+        return std::make_unique<Material>(
+            HomogeneousMaterial(materialName().toStdString(), m_delta, m_beta, m_magnetization));
+
+    return std::make_unique<Material>(
+        MaterialBySLD(materialName().toStdString(), m_real, m_imag, m_magnetization));
 }
 
-std::unique_ptr<Material> MaterialItem::createMaterial() const
+void MaterialItem::writeContentTo(QXmlStreamWriter* writer) const
 {
-    auto* dataItem = getGroupItem(P_MATERIAL_DATA);
-    auto magnetization = item<VectorItem>(P_MAGNETIZATION)->getVector();
-    auto name = itemName().toStdString();
+    writeAttribute(writer, GUI::Session::XML::Version, int(1));
+
+    writeAttribute(writer, Tags::Id, m_id);
+    writeAttribute(writer, Tags::Color, m_color);
+    writeAttribute(writer, Tags::Name, m_name);
+    writeAttribute(writer, Tags::Magnetization, m_magnetization);
 
-    if (dataItem->modelType() == MaterialRefractiveDataItem::M_TYPE) {
-        double delta = dataItem->getItemValue(MaterialRefractiveDataItem::P_DELTA).toDouble();
-        double beta = dataItem->getItemValue(MaterialRefractiveDataItem::P_BETA).toDouble();
-        return std::make_unique<Material>(HomogeneousMaterial(name, delta, beta, magnetization));
+    if (hasRefractiveIndex()) {
+        writeAttribute(writer, Tags::Delta, m_delta);
+        writeAttribute(writer, Tags::Beta, m_beta);
+    } else
+        writeAttribute(writer, Tags::Sld, std::get<complex_t>(m_data));
+}
+
+void MaterialItem::readContentFrom(QXmlStreamReader* reader)
+{
+    const int version = reader->attributes().value(GUI::Session::XML::Version).toInt();
+
+    if (version < 1)
+        throw DeserializationException::tooOld();
+
+    if (version > 1)
+        throw DeserializationException::tooNew();
+
+    readAttribute(reader, Tags::Id, &m_id);
+    readAttribute(reader, Tags::Color, &m_color);
+    readAttribute(reader, Tags::Name, &m_name);
+    readAttribute(reader, Tags::Magnetization, &m_magnetization);
+    if (reader->attributes().hasAttribute(Tags::Delta)) {
+        double d, b;
+        readAttribute(reader, Tags::Delta, &d);
+        readAttribute(reader, Tags::Beta, &b);
+        m_data = Refractive(d, b);
+    } else {
+        complex_t c;
+        readAttribute(reader, Tags::Sld, &c);
+        m_data = c;
     }
-    if (dataItem->modelType() == MaterialSLDDataItem::M_TYPE) {
-        double sld_real = dataItem->getItemValue(MaterialSLDDataItem::P_SLD_REAL).toDouble();
-        double sld_imag = dataItem->getItemValue(MaterialSLDDataItem::P_SLD_IMAG).toDouble();
-        return std::make_unique<Material>(MaterialBySLD(name, sld_real, sld_imag, magnetization));
+}
+
+void MaterialItem::updateFrom(const MaterialItem& other)
+{
+    if (operator!=(other)) {
+        m_name = other.m_name;
+        m_color = other.m_color;
+        m_data = other.m_data;
+        m_magnetization = other.m_magnetization;
+        emit dataChanged();
     }
-    ASSERT(0);
 }
 
 bool MaterialItem::operator!=(const MaterialItem& other) const
@@ -196,27 +310,13 @@ bool MaterialItem::operator!=(const MaterialItem& other) const
 
 bool MaterialItem::operator==(const MaterialItem& other) const
 {
-    if (identifier() != other.identifier())
-        return false;
-    if (itemName() != other.itemName())
-        return false;
-    if (color() != other.color())
-        return false;
-    if (magnetization() != other.magnetization())
-        return false;
-
-    if (hasRefractiveIndex() != other.hasRefractiveIndex())
-        return false;
-
     if (hasRefractiveIndex()) {
-        if (refractiveIndexBeta() != other.refractiveIndexBeta())
-            return false;
-        if (refractiveIndexDelta() != other.refractiveIndexDelta())
-            return false;
-    } else {
-        if (scatteringLengthDensity() != other.scatteringLengthDensity())
+        if (std::get<Refractive>(m_data) != std::get<Refractive>(other.m_data))
             return false;
-    }
+    } else if (std::get<complex_t>(m_data) != std::get<complex_t>(other.m_data))
+        return false;
+
 
-    return true;
+    return (m_id == other.m_id) && (m_name == other.m_name) && (m_color == other.m_color)
+           && (m_magnetization == other.m_magnetization);
 }
diff --git a/GUI/Model/Material/MaterialItem.h b/GUI/Model/Material/MaterialItem.h
index af486c7f1fbf43b5894f2197495d5d5ccd7f7755..106cf4b2cb712683ea590d0ff359e78d33997747 100644
--- a/GUI/Model/Material/MaterialItem.h
+++ b/GUI/Model/Material/MaterialItem.h
@@ -15,29 +15,34 @@
 #ifndef BORNAGAIN_GUI_MODEL_MATERIAL_MATERIALITEM_H
 #define BORNAGAIN_GUI_MODEL_MATERIAL_MATERIALITEM_H
 
-#include "GUI/Model/Session/SessionItem.h"
-
+#include <QColor>
+#include <QObject>
 #include <heinz/Complex.h>
 #include <heinz/Vectors3D.h>
+#include <memory>
+#include <variant>
 
 class Material;
 class DoubleDescriptor;
 class VectorDescriptor;
+class QXmlStreamReader;
+class QXmlStreamWriter;
 
-class BA_CORE_API_ MaterialItem : public SessionItem {
-private:
-    static constexpr auto P_COLOR{"Color"};
-    static constexpr auto P_MATERIAL_DATA{"Material data"};
-    static constexpr auto P_MAGNETIZATION{"Magnetization"};
-    static constexpr auto P_IDENTIFIER{"Identifier"};
+class MaterialItem : public QObject {
+    Q_OBJECT
 
 public:
-    static constexpr auto M_TYPE{"Material"};
-
     MaterialItem();
 
-    /// set refractive index as in 1 - delta + i * beta
+    //! Creates a complete copy, also the identifier is the same.
+    MaterialItem(const MaterialItem& other);
+
+    //! Turns material into refractive index material.
+    //!
+    //! Set refractive index as in 1 - delta + i * beta
     void setRefractiveIndex(double delta, double beta);
+
+    //! Turns material into SLD based material.
     void setScatteringLengthDensity(complex_t sld);
 
     DoubleDescriptor delta();
@@ -49,17 +54,12 @@ public:
     /// \return true if refractive index was given, otherwise SLD was given
     bool hasRefractiveIndex() const;
 
-    double refractiveIndexDelta() const;
-    double refractiveIndexBeta() const;
-
-    /// \pre ! hasRefractiveIndex
-    complex_t scatteringLengthDensity() const;
-
     QString materialName() const;
     void setMaterialName(const QString& name);
 
     QString identifier() const;
     void setIdentifier(const QString& id);
+    void createNewIdentifier();
 
     QColor color() const;
     void setColor(const QColor& color);
@@ -67,14 +67,40 @@ public:
     R3 magnetization() const;
     void setMagnetization(const R3& magnetization);
 
-    void hideMagnetization();
-
     std::unique_ptr<Material> createMaterial() const;
 
     //! Compares all contents. The inactive contents (e.g. SLD in case of refractive) are not taken
     //! into account.
     bool operator==(const MaterialItem& other) const;
     bool operator!=(const MaterialItem& other) const;
+
+    void writeContentTo(QXmlStreamWriter* writer) const;
+    void readContentFrom(QXmlStreamReader* reader);
+
+    //! Updates content from the other material.
+    //!
+    //! Does NOT change the identifier.
+    //! emits dataChanged, if differences exist.
+    void updateFrom(const MaterialItem& other);
+
+signals:
+    void dataChanged() const;
+
+private:
+    QString m_name;
+    QString m_id;
+    QColor m_color;
+
+    struct Refractive {
+        Refractive(double d, double b) : delta(d), beta(b) {}
+        double delta = 0.0;
+        double beta = 0.0;
+        bool operator==(const Refractive& o) const { return delta == o.delta && beta == o.beta; }
+        bool operator!=(const Refractive& o) const { return !operator==(o); }
+    };
+
+    std::variant<Refractive, complex_t> m_data;
+    R3 m_magnetization;
 };
 
 #endif // BORNAGAIN_GUI_MODEL_MATERIAL_MATERIALITEM_H
diff --git a/GUI/Model/Material/MaterialItemContainer.cpp b/GUI/Model/Material/MaterialItemContainer.cpp
deleted file mode 100644
index 8e0c528b79d50f8d3b89de9ab20f14cbfea60c20..0000000000000000000000000000000000000000
--- a/GUI/Model/Material/MaterialItemContainer.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-//  ************************************************************************************************
-//
-//  BornAgain: simulate and fit reflection and scattering
-//
-//! @file      GUI/Model/Material/MaterialItemContainer.cpp
-//! @brief     Implements class MaterialItemContainer
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-//  ************************************************************************************************
-
-#include "GUI/Model/Material/MaterialItemContainer.h"
-#include "GUI/Model/Material/MaterialItem.h"
-#include "GUI/Model/Session/SessionModel.h"
-#include <QUuid>
-
-MaterialItemContainer::MaterialItemContainer() : SessionItem(M_TYPE)
-{
-    setItemName("Materials");
-    registerTag(T_MATERIALS, 0, -1, {MaterialItem::M_TYPE});
-}
-
-MaterialItem* MaterialItemContainer::insertCopy(MaterialItem* material_item)
-{
-    MaterialItem* item_copy = model()->copyItem(material_item, this, T_MATERIALS);
-    item_copy->setIdentifier(QUuid::createUuid().toString());
-
-    return item_copy;
-}
-
-MaterialItem* MaterialItemContainer::findMaterialById(QString id)
-{
-    return const_cast<MaterialItem*>(
-        static_cast<const MaterialItemContainer*>(this)->findMaterialById(id));
-}
-
-const MaterialItem* MaterialItemContainer::findMaterialById(QString id) const
-{
-    auto materials = getItems(T_MATERIALS);
-    for (auto* item : materials) {
-        auto* material = dynamic_cast<MaterialItem*>(item);
-        if (material->identifier() == id)
-            return material;
-    }
-    return nullptr;
-}
-
-QVector<MaterialItem*> MaterialItemContainer::getMaterials()
-{
-    return items<MaterialItem>(T_MATERIALS);
-}
diff --git a/GUI/Model/Material/MaterialItemContainer.h b/GUI/Model/Material/MaterialItemContainer.h
deleted file mode 100644
index 8ef61c64c4336ba509a6ced5862906ae6fd99b63..0000000000000000000000000000000000000000
--- a/GUI/Model/Material/MaterialItemContainer.h
+++ /dev/null
@@ -1,44 +0,0 @@
-//  ************************************************************************************************
-//
-//  BornAgain: simulate and fit reflection and scattering
-//
-//! @file      GUI/Model/Material/MaterialItemContainer.h
-//! @brief     Defines class MaterialItemContainer
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-//  ************************************************************************************************
-
-#ifndef BORNAGAIN_GUI_MODEL_MATERIAL_MATERIALITEMCONTAINER_H
-#define BORNAGAIN_GUI_MODEL_MATERIAL_MATERIALITEMCONTAINER_H
-
-#include "GUI/Model/Session/SessionItem.h"
-
-class MaterialItem;
-
-class MaterialItemContainer : public SessionItem {
-private:
-    static constexpr auto T_MATERIALS{"MaterialVector"};
-
-public:
-    static constexpr auto M_TYPE{"MaterialContainer"};
-
-    MaterialItemContainer();
-
-    //! Copies MaterialItem, inserts it into the container
-    //! and returns a pointer to the copy.
-    MaterialItem* insertCopy(MaterialItem* material_item);
-
-    //! Can be nullptr if not found
-    const MaterialItem* findMaterialById(QString id) const;
-
-    //! Can be nullptr if not found
-    MaterialItem* findMaterialById(QString id);
-
-    QVector<MaterialItem*> getMaterials();
-};
-
-#endif // BORNAGAIN_GUI_MODEL_MATERIAL_MATERIALITEMCONTAINER_H
diff --git a/GUI/Model/Material/MaterialModel.cpp b/GUI/Model/Material/MaterialModel.cpp
index 9a6eb9099dbab6cf7afd33d19a8a3b51e05de04d..b0ccfbb5aa025183f7322bd0527bb833118f38fa 100644
--- a/GUI/Model/Material/MaterialModel.cpp
+++ b/GUI/Model/Material/MaterialModel.cpp
@@ -13,11 +13,18 @@
 //  ************************************************************************************************
 
 #include "GUI/Model/Material/MaterialModel.h"
+#include "Base/Util/Assert.h"
 #include "GUI/Model/Material/MaterialItem.h"
+#include "GUI/Model/Session/SessionXML.h"
+#include "GUI/Util/DeserializationException.h"
 #include <QColor>
 #include <QUuid>
+#include <QXmlStreamWriter>
 #include <random>
 
+using namespace GUI::Session::XML;
+
+
 namespace {
 
 QColor suggestMaterialColor(const QString& name)
@@ -42,45 +49,70 @@ QColor suggestMaterialColor(const QString& name)
 } // namespace
 
 
-MaterialModel::MaterialModel(QObject* parent)
-    : SessionModel(GUI::Session::XML::MaterialModelTag, parent)
+MaterialModel::MaterialModel(QObject* parent) : QObject(parent) {}
+
+MaterialModel::~MaterialModel()
 {
-    setObjectName(GUI::Session::XML::MaterialModelTag);
+    clear();
 }
 
 void MaterialModel::clear()
 {
-    SessionModel::clear();
+    qDeleteAll(m_materialItems);
+    m_materialItems.clear();
+}
+
+void MaterialModel::addMaterial(MaterialItem* material)
+{
+    addMaterial(material, true);
 }
 
-MaterialModel* MaterialModel::createCopy(SessionItem* parent)
+void MaterialModel::addMaterial(MaterialItem* material, bool signalAdding)
+{
+    ASSERT(material);
+    material->disconnect(this);
+    m_materialItems << material;
+    connect(material, &MaterialItem::dataChanged, this, [=] { emit materialChanged(material); });
+
+    if (signalAdding)
+        emit materialAddedOrRemoved();
+}
+
+MaterialModel* MaterialModel::createCopy() const
 {
     auto* result = new MaterialModel();
-    result->initFrom(this, parent);
+
+    for (const auto* m : m_materialItems)
+        result->addMaterial(new MaterialItem(*m));
+
     return result;
 }
 
 MaterialItem* MaterialModel::addRefractiveMaterial(const QString& name, double delta, double beta)
 {
-    auto* materialItem = insertItem<MaterialItem>();
+    auto* materialItem = new MaterialItem();
     materialItem->setMaterialName(name);
     materialItem->setColor(suggestMaterialColor(name));
     materialItem->setRefractiveIndex(delta, beta);
+    addMaterial(materialItem);
+
     return materialItem;
 }
 
 MaterialItem* MaterialModel::addSLDMaterial(const QString& name, double sld, double abs_term)
 {
-    auto* materialItem = insertItem<MaterialItem>();
+    auto* materialItem = new MaterialItem();
     materialItem->setMaterialName(name);
     materialItem->setColor(suggestMaterialColor(name));
     materialItem->setScatteringLengthDensity(complex_t(sld, abs_term));
+    addMaterial(materialItem);
+
     return materialItem;
 }
 
 MaterialItem* MaterialModel::materialFromName(const QString& name) const
 {
-    for (auto* materialItem : topItems<MaterialItem>())
+    for (auto* materialItem : m_materialItems)
         if (materialItem->materialName() == name)
             return materialItem;
 
@@ -89,33 +121,31 @@ MaterialItem* MaterialModel::materialFromName(const QString& name) const
 
 MaterialItem* MaterialModel::materialFromIdentifier(const QString& identifier) const
 {
-    for (auto* materialItem : topItems<MaterialItem>())
+    for (auto* materialItem : m_materialItems)
         if (materialItem->identifier() == identifier)
             return materialItem;
 
     return nullptr;
 }
 
-MaterialItem* MaterialModel::cloneMaterial(MaterialItem* material)
+MaterialItem* MaterialModel::insertCopy(const MaterialItem& material)
 {
-    if (!material)
-        return nullptr;
+    auto* newMaterial = new MaterialItem(material);
+    newMaterial->createNewIdentifier();
+    newMaterial->setMaterialName(material.materialName() + " (copy)");
+    addMaterial(newMaterial);
 
-    auto* clonedMaterial = copyItem(material, nullptr);
-    clonedMaterial->setIdentifier(QUuid::createUuid().toString());
-    clonedMaterial->setMaterialName(material->materialName() + " (copy)");
-    return clonedMaterial;
+    return newMaterial;
 }
 
-QVector<MaterialItem*> MaterialModel::materialItems() const
+const QVector<MaterialItem*>& MaterialModel::materialItems() const
 {
-    return topItems<MaterialItem>();
+    return m_materialItems;
 }
 
-
 MaterialItem* MaterialModel::findMaterialItem(const QString& identifier) const
 {
-    for (auto* m : materialItems())
+    for (auto* m : m_materialItems)
         if (m->identifier() == identifier)
             return m;
 
@@ -135,69 +165,84 @@ void MaterialModel::removeMaterial(const QString& identifier)
 
 void MaterialModel::removeMaterial(MaterialItem* materialItem)
 {
-    if (materialItem != nullptr)
-        if (const auto index = indexOfItem(materialItem); index.isValid())
-            removeRows(index.row(), 1, index.parent());
+    m_materialItems.removeAll(materialItem);
+    delete materialItem;
+    emit materialAddedOrRemoved();
 }
 
-void MaterialModel::readFrom(QXmlStreamReader* reader, MessageService* messageService /*= 0*/)
+void MaterialModel::writeContentTo(QXmlStreamWriter* writer) const
 {
-    // do not send added-notifications until completely read - otherwise partially
-    // initialized items will be notified
-    disconnect(this, &SessionModel::rowsInserted, this, &MaterialModel::onRowsChange);
-
-    SessionModel::readFrom(reader, messageService);
-
-    connect(this, &SessionModel::rowsInserted, this, &MaterialModel::onRowsChange);
-
-    for (auto* materialItem : materialItems()) {
-        materialItem->mapper()->setOnAnyChildChange(
-            [this, materialItem](SessionItem*) { materialChanged(materialItem); }, this);
+    writeAttribute(writer, GUI::Session::XML::Version, int(1));
+    for (auto m : m_materialItems) {
+        writer->writeStartElement("Material");
+        m->writeContentTo(writer);
+        writer->writeEndElement();
     }
 }
 
-void MaterialModel::initFrom(SessionModel* model, SessionItem* parent)
+void MaterialModel::readContentFrom(QXmlStreamReader* reader)
 {
-    // since initFrom does not send materialChanged signals, this has to be done manually.
+    const int version = reader->attributes().value(GUI::Session::XML::Version).toInt();
 
-    QStringList identifiersOfChangedMaterials;
-    auto* sourceModel = dynamic_cast<MaterialModel*>(model);
-    for (auto* destItem : materialItems()) {
-        auto* srcItem = sourceModel->findMaterialItem(destItem->identifier());
-        if (srcItem && (*srcItem != *destItem))
-            identifiersOfChangedMaterials << destItem->identifier();
-    }
+    if (version < 1)
+        throw DeserializationException::tooOld();
 
-    clear();
-    SessionModel::initFrom(model, parent);
+    if (version > 1)
+        throw DeserializationException::tooNew();
 
-    for (const auto& identifier : identifiersOfChangedMaterials)
-        emit materialChanged(findMaterialItem(identifier));
+    while (reader->readNextStartElement()) {
+        if (reader->name() == "Material") {
+            MaterialItem* m = new MaterialItem;
+            m->readContentFrom(reader);
+            addMaterial(m, false);
+            reader->skipCurrentElement();
+        }
+    }
 }
 
-void MaterialModel::onRowsChange(const QModelIndex& parent, int, int)
+void MaterialModel::initFrom(const MaterialModel& from)
 {
-    // valid parent means not a material (which is top level item) but something below
-    if (parent.isValid())
-        return;
+    // update existing to new contents (do not delete and recreate to keep references valid)
+    for (auto* destItem : m_materialItems)
+        if (auto* fromItem = from.findMaterialItem(destItem->identifier()))
+            destItem->updateFrom(*fromItem);
 
-    for (auto* materialItem : materialItems()) {
-        materialItem->mapper()->unsubscribe(this);
+    bool anyAddedOrRemoved = false;
 
-        materialItem->mapper()->setOnAnyChildChange(
-            [this, materialItem](SessionItem*) { materialChanged(materialItem); }, this);
-    }
+    // remove non-existing
+    auto iter = m_materialItems.begin();
+    while (iter != m_materialItems.end())
+        if (!from.findMaterialItem((*iter)->identifier())) {
+            delete *iter;
+            iter = m_materialItems.erase(iter);
+            anyAddedOrRemoved = true;
+        } else
+            iter++;
+
+    // copy new ones
+    for (const auto* m : from.materialItems())
+        if (!findMaterialItem(m->identifier())) {
+            addMaterial(new MaterialItem(*m), false);
+            anyAddedOrRemoved = true;
+        }
+
+    // copy order
+    QVector<MaterialItem*> tmp;
+    for (const auto* m : from.materialItems())
+        tmp << findMaterialItem(m->identifier());
+    m_materialItems = tmp;
+
+    if (anyAddedOrRemoved)
+        emit materialAddedOrRemoved();
 }
 
 bool MaterialModel::operator==(const MaterialModel& other) const
 {
-    const auto myItems = materialItems();
-    const auto otherItems = other.materialItems();
-    if (myItems.size() != otherItems.size())
+    if (m_materialItems.size() != other.m_materialItems.size())
         return false;
 
-    for (int i = 0; i < myItems.size(); i++)
-        if (*myItems[i] != *otherItems[i])
+    for (int i = 0; i < m_materialItems.size(); i++)
+        if (*m_materialItems[i] != *other.m_materialItems[i])
             return false;
 
     return true;
diff --git a/GUI/Model/Material/MaterialModel.h b/GUI/Model/Material/MaterialModel.h
index cc7d36761850ec8c659bbdb37a2593bdbf736819..96ff5b68f7214ef40b3364750463dab798b2a696 100644
--- a/GUI/Model/Material/MaterialModel.h
+++ b/GUI/Model/Material/MaterialModel.h
@@ -15,19 +15,26 @@
 #ifndef BORNAGAIN_GUI_MODEL_MATERIAL_MATERIALMODEL_H
 #define BORNAGAIN_GUI_MODEL_MATERIAL_MATERIALMODEL_H
 
-#include "GUI/Model/Session/SessionModel.h"
+#include <QObject>
+#include <QVector>
 
 class MaterialItem;
+class QXmlStreamReader;
+class QXmlStreamWriter;
 
-class MaterialModel : public SessionModel {
+class MaterialModel : public QObject {
     Q_OBJECT
 
 public:
     explicit MaterialModel(QObject* parent = nullptr);
+    ~MaterialModel();
 
-    void clear() override;
+    void clear();
 
-    MaterialModel* createCopy(SessionItem* parent = nullptr) override;
+    //! Add the material and take ownership of it.
+    void addMaterial(MaterialItem* material);
+
+    MaterialModel* createCopy() const;
 
     MaterialItem* addRefractiveMaterial(const QString& name, double delta, double beta);
     MaterialItem* addSLDMaterial(const QString& name, double sld, double abs_term);
@@ -35,10 +42,12 @@ public:
     MaterialItem* materialFromName(const QString& name) const;
     MaterialItem* materialFromIdentifier(const QString& identifier) const;
 
-    //! Returns clone of given material. Clone will get the same material identifier!
-    MaterialItem* cloneMaterial(MaterialItem* material);
+    //! Inserts a copy of the given material and returns the newly inserted item.
+    //!
+    //! The copy will have a different material identifier and a different name.
+    MaterialItem* insertCopy(const MaterialItem& material);
 
-    QVector<MaterialItem*> materialItems() const;
+    const QVector<MaterialItem*>& materialItems() const;
     MaterialItem* findMaterialItem(const QString& identifier) const;
 
     MaterialItem* defaultMaterial() const;
@@ -46,18 +55,28 @@ public:
     void removeMaterial(const QString& identifier);
     void removeMaterial(MaterialItem* materialItem);
 
-    void readFrom(QXmlStreamReader* reader, MessageService* messageService = nullptr) override;
-    void initFrom(SessionModel* model, SessionItem* parent) override;
+    void writeContentTo(QXmlStreamWriter* writer) const;
+    void readContentFrom(QXmlStreamReader* reader);
+
+    //! Copies the complete content, emitting signals for existing and changed materials
+    void initFrom(const MaterialModel& from);
 
     //! Compares for complete equality (same material identifiers, same order of materials,...)
     bool operator==(const MaterialModel& other) const;
     bool operator!=(const MaterialModel& other) const;
 
 signals:
+    void materialAddedOrRemoved();
     void materialChanged(MaterialItem* materialItem);
 
 private:
-    void onRowsChange(const QModelIndex& parent, int, int);
+    //! Add the material and take ownership of it.
+    //!
+    //! In the given flag the signal "materialAddedOrRemoved" can be suppressed.
+    void addMaterial(MaterialItem* material, bool signalAdding);
+
+private:
+    QVector<MaterialItem*> m_materialItems; //!< all materials (owned by this class)
 };
 
 #endif // BORNAGAIN_GUI_MODEL_MATERIAL_MATERIALMODEL_H
diff --git a/GUI/Model/Project/ProjectDocument.cpp b/GUI/Model/Project/ProjectDocument.cpp
index 8b1c182f678c9eb01ae243cea9c022b08da5a7dd..4dc755ab5faa9f23459f106f8821f6473fc440cf 100644
--- a/GUI/Model/Project/ProjectDocument.cpp
+++ b/GUI/Model/Project/ProjectDocument.cpp
@@ -16,6 +16,7 @@
 #include "GUI/Model/IO/ProjectUtils.h"
 #include "GUI/Model/Instrument/LinkInstrumentManager.h"
 #include "GUI/Model/Job/JobModel.h"
+#include "GUI/Model/Material/MaterialItem.h"
 #include "GUI/Model/Material/MaterialModel.h"
 #include "GUI/Model/Project/OutputDataIOService.h"
 #include "GUI/Model/Sample/ItemWithMaterial.h"
@@ -39,6 +40,7 @@ const QString BornAgainVersionAttribute("Version");
 const QString InfoTag("DocumentInfo");
 const QString InfoNameAttribute("ProjectName");
 const QString SimulationOptionsTag("SimulationOptions");
+const QString MaterialsTag("Materials");
 
 } // namespace
 
@@ -50,6 +52,17 @@ ProjectDocument::ProjectDocument(const QString& projectFileName)
     , m_singleSampleMode(false)
     , m_functionalities(All)
 {
+    m_materials.addRefractiveMaterial("Default", 1e-3, 1e-5);
+    m_materials.addRefractiveMaterial("Vacuum", 0.0, 0.0);
+    m_materials.addRefractiveMaterial("Particle", 6e-4, 2e-8);
+    m_materials.addRefractiveMaterial("Substrate", 6e-6, 2e-8);
+
+
+    connect(&m_materials, &MaterialModel::materialAddedOrRemoved, this,
+            &ProjectDocument::onModelChanged, Qt::UniqueConnection);
+    connect(&m_materials, &MaterialModel::materialChanged, this, &ProjectDocument::onModelChanged,
+            Qt::UniqueConnection);
+
     m_linkManager = std::make_unique<LinkInstrumentManager>(instrumentModel(), realDataModel());
     setObjectName("ProjectDocument");
     if (!projectFileName.isEmpty())
@@ -123,9 +136,9 @@ SampleModel* ProjectDocument::sampleModel() const
     return m_applicationModels.sampleModel();
 }
 
-MaterialModel* ProjectDocument::materialModel() const
+MaterialModel* ProjectDocument::materialModel()
 {
-    return m_applicationModels.materialModel();
+    return &m_materials;
 }
 
 RealDataModel* ProjectDocument::realDataModel() const
@@ -326,6 +339,10 @@ ProjectDocument::ReadResult ProjectDocument::readProject(QIODevice* device,
                     else if (reader.name() == SimulationOptionsTag) {
                         m_simulationOptionsItem.readContentFrom(&reader);
                         reader.skipCurrentElement();
+                        ASSERT(reader.name() == SimulationOptionsTag);
+                    } else if (reader.name() == MaterialsTag) {
+                        m_materials.readContentFrom(&reader);
+                        ASSERT(reader.name() == MaterialsTag);
                     } else
                         m_applicationModels.readFrom(&reader, &messageService);
             }
@@ -335,7 +352,17 @@ ProjectDocument::ReadResult ProjectDocument::readProject(QIODevice* device,
     // initialize all items with materials to find the materials
     for (auto multiLayerItem : m_applicationModels.sampleModel()->multiLayerItems())
         for (auto itemWithMaterial : multiLayerItem->itemsWithMaterial())
-            itemWithMaterial->setMaterialModel(m_applicationModels.materialModel());
+            itemWithMaterial->setMaterialModel(materialModel());
+
+    // make a sanity check whether all materials are present
+    for (auto multiLayerItem : m_applicationModels.sampleModel()->multiLayerItems())
+        for (auto itemWithMaterial : multiLayerItem->itemsWithMaterial())
+            if (itemWithMaterial->materialItem() == nullptr) {
+                QString message = QString("Material link is broken (id: '%1')")
+                                      .arg(itemWithMaterial->materialIdentifier());
+                messageService.addError(this, message);
+                return ReadResult::error;
+            }
 
     if (reader.hasError()) {
         QString message = QString("Format error '%1'").arg(reader.errorString());
@@ -365,6 +392,10 @@ void ProjectDocument::writeTo(QIODevice* device)
     m_simulationOptionsItem.writeContentTo(&writer);
     writer.writeEndElement();
 
+    writer.writeStartElement(MaterialsTag);
+    m_materials.writeContentTo(&writer);
+    writer.writeEndElement();
+
     m_applicationModels.writeTo(&writer);
 
     writer.writeEndElement(); // BornAgain tag
diff --git a/GUI/Model/Project/ProjectDocument.h b/GUI/Model/Project/ProjectDocument.h
index fcac92afdac9cd0fdf9df828365b6b2a4ec129fe..40d02a5c9c11b01c47ea8ed462000409fe96b595 100644
--- a/GUI/Model/Project/ProjectDocument.h
+++ b/GUI/Model/Project/ProjectDocument.h
@@ -16,6 +16,7 @@
 #define BORNAGAIN_GUI_MODEL_PROJECT_PROJECTDOCUMENT_H
 
 #include "GUI/Model/Data/ApplicationModels.h"
+#include "GUI/Model/Material/MaterialModel.h"
 #include "GUI/Model/Session/SimulationOptionsItem.h"
 #include <QObject>
 #include <QVariant>
@@ -30,7 +31,6 @@ class MaterialModel;
 class RealDataModel;
 class JobModel;
 class LinkInstrumentManager;
-class SimulationOptionsItem;
 
 //! Project document class handles all data related to the opened project
 //! (sample, jobModel, project specific windows settings)
@@ -72,7 +72,7 @@ public:
 
     InstrumentModel* instrumentModel() const;
     SampleModel* sampleModel() const;
-    MaterialModel* materialModel() const;
+    MaterialModel* materialModel();
     RealDataModel* realDataModel() const;
     JobModel* jobModel() const;
     SimulationOptionsItem* simulationOptionsItem();
@@ -136,6 +136,7 @@ private:
     bool m_singleSampleMode;
     Functionalities m_functionalities;
     SimulationOptionsItem m_simulationOptionsItem;
+    MaterialModel m_materials;
 };
 
 Q_DECLARE_OPERATORS_FOR_FLAGS(ProjectDocument::Functionalities)
diff --git a/GUI/Model/Sample/ItemWithMaterial.cpp b/GUI/Model/Sample/ItemWithMaterial.cpp
index 2a78c677afcf4a2f773076efd08c012b3b69f5b3..e84498d867dff5429ece43c550eb0b97ec1f0dd4 100644
--- a/GUI/Model/Sample/ItemWithMaterial.cpp
+++ b/GUI/Model/Sample/ItemWithMaterial.cpp
@@ -16,7 +16,6 @@
 #include "GUI/Model/Job/JobItem.h"
 #include "GUI/Model/Job/JobModelFunctions.h"
 #include "GUI/Model/Material/MaterialItem.h"
-#include "GUI/Model/Material/MaterialItemContainer.h"
 #include "GUI/Model/Material/MaterialModel.h"
 #include <QColor>
 
diff --git a/GUI/Model/Session/ModelPath.cpp b/GUI/Model/Session/ModelPath.cpp
index 905a5b8487ff1165117b8a4d33a35a24a6df9f9b..5f74fd2790d485371e88551adbdbb5d397c47a65 100644
--- a/GUI/Model/Session/ModelPath.cpp
+++ b/GUI/Model/Session/ModelPath.cpp
@@ -15,6 +15,7 @@
 #include "GUI/Model/Session/ModelPath.h"
 #include "GUI/Model/Instrument/InstrumentItems.h"
 #include "GUI/Model/Job/JobItem.h"
+#include "GUI/Model/Material/MaterialItem.h"
 
 QString GUI::Model::Path::getPathFromIndex(const QModelIndex& index)
 {
@@ -97,3 +98,8 @@ QString GUI::Model::Path::getPathFromItem(SessionItem* item)
     ASSERT(item->model()); // if assert, item is not completely initialized
     return getPathFromIndex(item->model()->indexOfItem(item));
 }
+
+QString GUI::Model::Path::getPathFromItem(const MaterialItem* item)
+{
+    return "material/" + item->identifier();
+}
diff --git a/GUI/Model/Session/ModelPath.h b/GUI/Model/Session/ModelPath.h
index b1bfa2206dc72a99161a6664ffca293a0ec2ad34..6959793cd73445105bb7baee9681eabf2768539a 100644
--- a/GUI/Model/Session/ModelPath.h
+++ b/GUI/Model/Session/ModelPath.h
@@ -22,6 +22,7 @@ class SessionItem;
 class QModelIndex;
 class SessionModel;
 class QString;
+class MaterialItem;
 
 namespace GUI::Model::Path {
 
@@ -29,6 +30,8 @@ QString getPathFromIndex(const QModelIndex& index);
 
 QString getPathFromItem(SessionItem* item);
 
+QString getPathFromItem(const MaterialItem* item);
+
 QModelIndex getIndexFromPath(const SessionModel* model, const QString& path);
 
 SessionItem* getItemFromPath(const QString& relPath, const SessionItem* parent);
diff --git a/GUI/Model/Session/SessionXML.cpp b/GUI/Model/Session/SessionXML.cpp
index ce963bbd23242cec0c12c5b1789c5f285f546b4b..bca4da3f09bda2cc48a745326ac136952ad81176 100644
--- a/GUI/Model/Session/SessionXML.cpp
+++ b/GUI/Model/Session/SessionXML.cpp
@@ -157,7 +157,7 @@ void GUI::Session::XML::writeAttribute(QXmlStreamWriter* writer, const QString&
 void GUI::Session::XML::writeAttribute(QXmlStreamWriter* writer, const QString& attributeName,
                                        double d)
 {
-    writer->writeAttribute(attributeName, QString::number(d, 'e', 12));
+    writer->writeAttribute(attributeName, d == 0.0 ? "0" : QString::number(d, 'e', 12));
 }
 
 void GUI::Session::XML::writeAttribute(QXmlStreamWriter* writer, const QString& attributeName,
diff --git a/GUI/Model/Types/DoubleDescriptor.cpp b/GUI/Model/Types/DoubleDescriptor.cpp
index 361f0bd374800ee633afaa4c2a3d56617c6b73f0..7e0bbb8eb351455da8af4f15f8543a842bba071e 100644
--- a/GUI/Model/Types/DoubleDescriptor.cpp
+++ b/GUI/Model/Types/DoubleDescriptor.cpp
@@ -70,6 +70,14 @@ DoubleDescriptor::DoubleDescriptor(const QString& label, const QString& tooltip,
 {
 }
 
+DoubleDescriptor::DoubleDescriptor(const QString& label, double* var,
+                                   const variant<QString, Unit>& unit)
+    : label(label), decimals(3), limits(RealLimits::nonnegative()), unit(unit)
+{
+    set = [=](double v) { *var = v; };
+    get = [=]() { return *var; };
+}
+
 DoubleDescriptor::operator double() const
 {
     return get();
diff --git a/GUI/Model/Types/DoubleDescriptor.h b/GUI/Model/Types/DoubleDescriptor.h
index 80c0aeb5a88837eb90b8cf5ad84d7b26759797c5..3fd1f54416d0e530fdac8ac8d9d23f91b6b91081 100644
--- a/GUI/Model/Types/DoubleDescriptor.h
+++ b/GUI/Model/Types/DoubleDescriptor.h
@@ -35,6 +35,8 @@ using std::variant;
 class DoubleDescriptor {
 
 public:
+    DoubleDescriptor(const DoubleDescriptor& other) = default;
+
     //! Operates on a session item. The settings (like decimals, limits) are taken from the session
     //! item.
     //! Only for easier migration. Should be removed after SessionItem refactoring.
@@ -45,6 +47,15 @@ public:
     //! Only for easier migration. Should be removed after SessionItem refactoring.
     DoubleDescriptor(const QString& label, SessionItem* item, const variant<QString, Unit>& unit);
 
+    DoubleDescriptor(const QString& label, double* var, const variant<QString, Unit>& unit);
+
+    //! Operates on any kind of storage (e.g. session items), by using setter/getter methods
+    DoubleDescriptor(QString label, QString tooltip, int decimals, const RealLimits& limits,
+                     function<void(double)> setter, function<double()> getter,
+                     const variant<QString, Unit>& unit);
+
+    DoubleDescriptor() = default;
+
 private: // private as long as path initialization is not included in params (to be done after
          // SessionItem migration)
     //! Operates on a double value (e.g a member variable).
@@ -56,11 +67,6 @@ private: // private as long as path initialization is not included in params (to
     DoubleDescriptor(const QString& label, const QString& tooltip, double* var,
                      const variant<QString, Unit>& unit);
 
-    //! Operates on any kind of storage (e.g. session items), by using setter/getter methods
-    DoubleDescriptor(QString label, QString tooltip, int decimals, const RealLimits& limits,
-                     function<void(double)> setter, function<double()> getter,
-                     const variant<QString, Unit>& unit);
-
     //! Operates on any kind of storage (e.g. session items), by using setter/getter methods
     //! decimals is set to 3, limits is set to nonnegative
     DoubleDescriptor(const QString& label, const QString& tooltip, function<void(double)> setter,
diff --git a/GUI/Model/Types/VectorDescriptor.h b/GUI/Model/Types/VectorDescriptor.h
index 46eb6b17ba21f61e133e9d4d286c52697857a4f7..567acf7369b6826ba8679a85c63ea9c4ba4d050c 100644
--- a/GUI/Model/Types/VectorDescriptor.h
+++ b/GUI/Model/Types/VectorDescriptor.h
@@ -38,6 +38,39 @@ public:
     {
     }
 
+    VectorDescriptor(const QString& label, const QString& tooltip, double* xVar, double* yVar,
+                     double* zVar, const variant<QString, Unit>& unit)
+        : label(label), tooltip(tooltip), x("X", xVar, unit), y("Y", yVar, unit), z("Z", zVar, unit)
+    {
+        x.limits = RealLimits::limitless();
+        y.limits = RealLimits::limitless();
+        z.limits = RealLimits::limitless();
+        x.decimals = 3;
+        y.decimals = 3;
+        z.decimals = 3;
+        x.tooltip = tooltip;
+        y.tooltip = tooltip;
+        z.tooltip = tooltip;
+    }
+
+    VectorDescriptor(const QString& label, const QString& tooltip,
+                     const variant<QString, Unit>& unit)
+        : label(label), tooltip(tooltip)
+    {
+        x.limits = RealLimits::limitless();
+        y.limits = RealLimits::limitless();
+        z.limits = RealLimits::limitless();
+        x.decimals = 3;
+        y.decimals = 3;
+        z.decimals = 3;
+        x.tooltip = tooltip;
+        y.tooltip = tooltip;
+        z.tooltip = tooltip;
+        x.unit = unit;
+        y.unit = unit;
+        z.unit = unit;
+    }
+
     QString label;   //!< A label text (short, no trailing colon)
     QString tooltip; //!< Tooltip text
     DoubleDescriptor x;
diff --git a/GUI/View/Fit/FitParameterWidget.cpp b/GUI/View/Fit/FitParameterWidget.cpp
index 8197ff867e0fb507e8bf57f793ee0c74bbf7e5e2..c0b3b7d99e918a0433620a54749cb2429c194ec1 100644
--- a/GUI/View/Fit/FitParameterWidget.cpp
+++ b/GUI/View/Fit/FitParameterWidget.cpp
@@ -13,9 +13,9 @@
 //  ************************************************************************************************
 
 #include "GUI/View/Fit/FitParameterWidget.h"
+#include "GUI/Model/Fit/FitParameterContainerItem.h"
 #include "GUI/Model/Fit/FitParameterHelper.h"
 #include "GUI/Model/Fit/FitParameterItem.h"
-#include "GUI/Model/Fit/FitParameterContainerItem.h"
 #include "GUI/Model/Fit/FitParameterLinkItem.h"
 #include "GUI/Model/Fit/FitParameterProxyModel.h"
 #include "GUI/Model/Fit/FitSuiteItem.h"
@@ -151,10 +151,8 @@ void FitParameterWidget::onFitParametersSelectionChanged(const QItemSelection& s
 void FitParameterWidget::onCreateFitParAction()
 {
     for (auto* item : m_tuningWidget->getSelectedParameters()) {
-        if (!FitParameterHelper::getFitParameterItem(jobItem()->fitParameterContainerItem(),
-                                                     item)) {
+        if (!jobItem()->fitParameterContainerItem()->fitParameterItem(item))
             FitParameterHelper::createFitParameter(jobItem()->fitParameterContainerItem(), item);
-        }
     }
 }
 
@@ -164,10 +162,9 @@ void FitParameterWidget::onCreateFitParAction()
 void FitParameterWidget::onRemoveFromFitParAction()
 {
     for (auto* item : m_tuningWidget->getSelectedParameters()) {
-        if (FitParameterHelper::getFitParameterItem(jobItem()->fitParameterContainerItem(), item)) {
+        if (jobItem()->fitParameterContainerItem()->fitParameterItem(item))
             FitParameterHelper::removeFromFitParameters(jobItem()->fitParameterContainerItem(),
                                                         item);
-        }
     }
 }
 
@@ -313,8 +310,7 @@ bool FitParameterWidget::canCreateFitParameter()
 {
     QVector<ParameterItem*> selected = m_tuningWidget->getSelectedParameters();
     for (auto* item : selected) {
-        if (FitParameterHelper::getFitParameterItem(jobItem()->fitParameterContainerItem(), item)
-            == nullptr)
+        if (jobItem()->fitParameterContainerItem()->fitParameterItem(item) == nullptr)
             return true;
     }
     return false;
@@ -327,7 +323,7 @@ bool FitParameterWidget::canRemoveFromFitParameters()
 {
     QVector<ParameterItem*> selected = m_tuningWidget->getSelectedParameters();
     for (auto* item : selected) {
-        if (FitParameterHelper::getFitParameterItem(jobItem()->fitParameterContainerItem(), item))
+        if (jobItem()->fitParameterContainerItem()->fitParameterItem(item))
             return true;
     }
     return false;
diff --git a/GUI/View/Fit/ParameterTuningDelegate.cpp b/GUI/View/Fit/ParameterTuningDelegate.cpp
index cc3d3758fe5b7edefdd7210f08a12ebdfd60052e..16521fca7febf33c90797e02d5e471d3697e3195 100644
--- a/GUI/View/Fit/ParameterTuningDelegate.cpp
+++ b/GUI/View/Fit/ParameterTuningDelegate.cpp
@@ -147,7 +147,7 @@ QWidget* ParameterTuningDelegate::createEditor(QWidget* parent, const QStyleOpti
         return nullptr;
 
     double value = data.toDouble();
-    RealLimits limits = m_currentItem->linkedItem()->limits();
+    RealLimits limits = m_currentItem->limitsOfLink();
     m_tuning_info.setItemLimits(limits);
     m_tuning_info.value_to_slider(value);
 
@@ -155,7 +155,7 @@ QWidget* ParameterTuningDelegate::createEditor(QWidget* parent, const QStyleOpti
     m_valueBox = new ScientificSpinBox();
     m_valueBox->setKeyboardTracking(false);
     m_valueBox->setFixedWidth(105);
-    m_valueBox->setDecimals(m_currentItem->linkedItem()->decimals());
+    m_valueBox->setDecimals(m_currentItem->decimalsOfLink());
     m_valueBox->setSingleStep(m_tuning_info.step());
 
     if (limits.hasLowerLimit()) {
diff --git a/GUI/View/MaterialEditor/MaterialEditorDialog.cpp b/GUI/View/MaterialEditor/MaterialEditorDialog.cpp
index 2299291f3e6977de61ca1e0f23c8666f462239eb..c499ca1c72704416a2fef26d23e8e56700c77119 100644
--- a/GUI/View/MaterialEditor/MaterialEditorDialog.cpp
+++ b/GUI/View/MaterialEditor/MaterialEditorDialog.cpp
@@ -19,6 +19,7 @@
 #include "GUI/Model/Project/ProjectDocument.h"
 #include "GUI/Model/Sample/ItemWithMaterial.h"
 #include "GUI/Model/Sample/SampleModel.h"
+#include "GUI/Model/Types/DoubleDescriptor.h"
 #include "GUI/Model/Types/VectorItem.h"
 #include "GUI/View/MaterialEditor/MaterialEditorModel.h"
 #include "GUI/View/Tool/EditUtil.h"
@@ -145,12 +146,9 @@ MaterialEditorDialog::~MaterialEditorDialog()
     appSettings->saveWindowSizeAndPos(this);
 }
 
-//! replaces original material model with the model modified by MaterialEditor
 void MaterialEditorDialog::accept()
 {
-    if (*m_document->materialModel() != *m_tmpMaterialModel)
-        m_document->materialModel()->initFrom(m_tmpMaterialModel.get(), nullptr);
-
+    m_document->materialModel()->initFrom(*m_tmpMaterialModel);
     QDialog::accept();
 }
 
@@ -251,13 +249,11 @@ void MaterialEditorDialog::fill()
     m_ui->selectColorButton->setIcon(pixmap);
 
     if (materialItem->hasRefractiveIndex()) {
-        m_ui->deltaEdit->setText(QString::number(materialItem->refractiveIndexDelta(), 'g'));
-        m_ui->betaEdit->setText(QString::number(materialItem->refractiveIndexBeta(), 'g'));
+        m_ui->deltaEdit->setText(QString::number(materialItem->delta().get(), 'g'));
+        m_ui->betaEdit->setText(QString::number(materialItem->beta().get(), 'g'));
     } else {
-        m_ui->realEdit->setText(
-            QString::number(materialItem->scatteringLengthDensity().real(), 'g'));
-        m_ui->imaginaryEdit->setText(
-            QString::number(materialItem->scatteringLengthDensity().imag(), 'g'));
+        m_ui->realEdit->setText(QString::number(materialItem->sldRe().get(), 'g'));
+        m_ui->imaginaryEdit->setText(QString::number(materialItem->sldIm().get(), 'g'));
     }
 
     m_ui->xSpinBox->setValue(materialItem->magnetization().x());
diff --git a/GUI/View/MaterialEditor/MaterialEditorDialog.h b/GUI/View/MaterialEditor/MaterialEditorDialog.h
index 11f0e7e9e0129fbeba099bd3274857e28d590d45..bf37d20260d359f32624ff68bcb17c5bc2b93834 100644
--- a/GUI/View/MaterialEditor/MaterialEditorDialog.h
+++ b/GUI/View/MaterialEditor/MaterialEditorDialog.h
@@ -49,6 +49,7 @@ private:
     MaterialEditorDialog(ProjectDocument* document, QWidget* parent = nullptr);
     ~MaterialEditorDialog() override;
 
+    //! updates original material model with the edited model
     void accept() override;
 
     void addRefractiveMaterial();
diff --git a/GUI/View/MaterialEditor/MaterialEditorModel.cpp b/GUI/View/MaterialEditor/MaterialEditorModel.cpp
index 607cab48710de4a33fb1309b4ff142f4ee1f0807..6bf828f20ccf03a95ef834e2ced6350cc1a95859 100644
--- a/GUI/View/MaterialEditor/MaterialEditorModel.cpp
+++ b/GUI/View/MaterialEditor/MaterialEditorModel.cpp
@@ -15,6 +15,7 @@
 #include "GUI/View/MaterialEditor/MaterialEditorModel.h"
 #include "GUI/Model/Material/MaterialItem.h"
 #include "GUI/Model/Material/MaterialModel.h"
+#include "GUI/Model/Types/DoubleDescriptor.h"
 #include "GUI/Model/Types/VectorItem.h"
 #include <QApplication>
 #include <QFontMetrics>
@@ -69,13 +70,9 @@ QVariant MaterialEditorModel::data(const QModelIndex& index, int role /*= Qt::Di
 
         case PARAMETERS:
             if (material->hasRefractiveIndex())
-                return QString("delta: %1, beta: %2")
-                    .arg(material->refractiveIndexDelta())
-                    .arg(material->refractiveIndexBeta());
+                return QString("delta: %1, beta: %2").arg(material->delta()).arg(material->beta());
             else
-                return QString("re: %1, im: %2")
-                    .arg(material->scatteringLengthDensity().real())
-                    .arg(material->scatteringLengthDensity().imag());
+                return QString("re: %1, im: %2").arg(material->sldRe()).arg(material->sldIm());
 
         case MAGNETIZATION:
             return QString("%1/%2/%3")
@@ -142,7 +139,7 @@ void MaterialEditorModel::setZ(const QModelIndex& index, double value)
 void MaterialEditorModel::setDelta(const QModelIndex& index, double value)
 {
     auto* m = materialFromIndex(index);
-    m->setRefractiveIndex(value, m->refractiveIndexBeta());
+    m->setRefractiveIndex(value, m->beta());
     const auto paramIndex = this->index(index.row(), PARAMETERS);
     emit dataChanged(paramIndex, paramIndex);
 }
@@ -150,7 +147,7 @@ void MaterialEditorModel::setDelta(const QModelIndex& index, double value)
 void MaterialEditorModel::setBeta(const QModelIndex& index, double value)
 {
     auto* m = materialFromIndex(index);
-    m->setRefractiveIndex(m->refractiveIndexDelta(), value);
+    m->setRefractiveIndex(m->delta(), value);
     const auto paramIndex = this->index(index.row(), PARAMETERS);
     emit dataChanged(paramIndex, paramIndex);
 }
@@ -158,7 +155,7 @@ void MaterialEditorModel::setBeta(const QModelIndex& index, double value)
 void MaterialEditorModel::setRe(const QModelIndex& index, double value)
 {
     auto* m = materialFromIndex(index);
-    m->setScatteringLengthDensity(complex_t(value, m->scatteringLengthDensity().imag()));
+    m->setScatteringLengthDensity(complex_t(value, m->sldIm()));
     auto paramIndex = this->index(index.row(), PARAMETERS);
     emit dataChanged(paramIndex, paramIndex);
 }
@@ -166,7 +163,7 @@ void MaterialEditorModel::setRe(const QModelIndex& index, double value)
 void MaterialEditorModel::setIm(const QModelIndex& index, double value)
 {
     auto* m = materialFromIndex(index);
-    m->setScatteringLengthDensity(complex_t(m->scatteringLengthDensity().real(), value));
+    m->setScatteringLengthDensity(complex_t(m->sldRe(), value));
     auto paramIndex = this->index(index.row(), PARAMETERS);
     emit dataChanged(paramIndex, paramIndex);
 }
@@ -219,7 +216,7 @@ MaterialItem* MaterialEditorModel::addSLDMaterial(const QString& name, double sl
 MaterialItem* MaterialEditorModel::cloneMaterial(const QModelIndex& index)
 {
     beginInsertRows(QModelIndex(), rowCount(), rowCount());
-    auto* m = m_model->cloneMaterial(materialFromIndex(index));
+    auto* m = m_model->insertCopy(*materialFromIndex(index));
     endInsertRows();
     return m;
 }
diff --git a/GUI/View/MaterialEditor/MaterialEditorModel.h b/GUI/View/MaterialEditor/MaterialEditorModel.h
index 8b24d45d31439e02e7ed7a0cfc873dda7913a874..92486aac46beba13d95556deca2688126962c23c 100644
--- a/GUI/View/MaterialEditor/MaterialEditorModel.h
+++ b/GUI/View/MaterialEditor/MaterialEditorModel.h
@@ -21,9 +21,7 @@ class MaterialItem;
 class MaterialModel;
 
 //! Model for list of materials, used in MaterialEditorDialog.
-//! This model is not derived from QAbstractProxyModel, despite the handled material list is
-//! of type SessionModel and therefore a QAbstractItemModel. The reason is to ease a later
-//! migration away from SessionModel.
+//!
 //! This model is also used for changing values of a material, therefore the list can be
 //! updated accordingly.
 class MaterialEditorModel : public QAbstractTableModel {
diff --git a/GUI/View/SampleDesigner/LayerForm.cpp b/GUI/View/SampleDesigner/LayerForm.cpp
index 8d424affb24ef23aec8ba8b49000e59ae2836f3b..32237a2958fc4ee52c893e6a77ae6b3094ff06e3 100644
--- a/GUI/View/SampleDesigner/LayerForm.cpp
+++ b/GUI/View/SampleDesigner/LayerForm.cpp
@@ -13,6 +13,8 @@
 //  ************************************************************************************************
 
 #include "GUI/View/SampleDesigner/LayerForm.h"
+#include "GUI/Model/Material/MaterialItem.h"
+#include "GUI/Model/Material/MaterialModel.h"
 #include "GUI/Model/Sample/LayerItem.h"
 #include "GUI/Model/Sample/MultiLayerItem.h"
 #include "GUI/Model/Types/UIntDescriptor.h"
@@ -90,6 +92,10 @@ LayerForm::LayerForm(QWidget* parent, LayerItem* layer, SampleEditorController*
     m_structureEditingWidgets << btn;
     m_layouter->addStructureEditingRow(btn);
 
+    // listen to changes in materials to update the title (contains the material name). Necessary
+    // to reflect e.g. a name change done in the material editor.
+    connect(ec->materialModel(), &MaterialModel::materialChanged, this, &LayerForm::updateTitle);
+
     updateLayerPositionDependentElements();
 }
 
diff --git a/GUI/View/SampleDesigner/LayerOrientedSampleEditor.cpp b/GUI/View/SampleDesigner/LayerOrientedSampleEditor.cpp
index 0cb348c5e0ae2956d9bb7f8975663bf7483e57b3..485f3bfb6dfd80f31ada4398a8cb28e6baf7f52d 100644
--- a/GUI/View/SampleDesigner/LayerOrientedSampleEditor.cpp
+++ b/GUI/View/SampleDesigner/LayerOrientedSampleEditor.cpp
@@ -110,9 +110,8 @@ void LayerOrientedSampleEditor::setCurrentSample(MultiLayerItem* multiLayerItem)
     }
 
     if (!m_editControllers.contains(m_currentMultiLayerItem))
-        m_editControllers.insert(
-            m_currentMultiLayerItem,
-            new SampleEditorController(m_document->materialModel(), m_currentMultiLayerItem));
+        m_editControllers.insert(m_currentMultiLayerItem,
+                                 new SampleEditorController(m_document, m_currentMultiLayerItem));
     auto* ec = m_editControllers[m_currentMultiLayerItem];
     connect(ec, &SampleEditorController::requestViewInRealSpace, this,
             &LayerOrientedSampleEditor::requestViewInRealSpace);
diff --git a/GUI/View/SampleDesigner/MaterialInplaceForm.cpp b/GUI/View/SampleDesigner/MaterialInplaceForm.cpp
index b5e638a814dc0b68e07bdfe4362c59f4af39bc58..7a123f8a54c847645211b4f0e3f6a284c5077ab9 100644
--- a/GUI/View/SampleDesigner/MaterialInplaceForm.cpp
+++ b/GUI/View/SampleDesigner/MaterialInplaceForm.cpp
@@ -17,7 +17,6 @@
 #include "GUI/Model/Material/MaterialModel.h"
 #include "GUI/Model/Sample/ItemWithMaterial.h"
 #include "GUI/Model/Session/ModelPath.h"
-#include "GUI/Model/State/SessionData.h"
 #include "GUI/Model/Types/DoubleDescriptor.h"
 #include "GUI/Model/Types/VectorDescriptor.h"
 #include "GUI/View/Edit/DoubleLineEdit.h"
@@ -40,7 +39,7 @@ MaterialInplaceForm::MaterialInplaceForm(QWidget* parent, ItemWithMaterial* item
     m_layout->setContentsMargins(0, 0, 0, 0);
     createWidgets();
 
-    connect(gSessionData->projectDocument->materialModel(), &MaterialModel::materialChanged, this,
+    connect(itemWithMaterial()->materialItem(), &MaterialItem::dataChanged, this,
             &MaterialInplaceForm::onMaterialChanged);
 }
 
@@ -64,12 +63,15 @@ void MaterialInplaceForm::updateValues()
 void MaterialInplaceForm::selectMaterial()
 {
     const QString newMaterialIdentifier = MaterialEditorDialog::chooseMaterial(
-        baWin, gSessionData->projectDocument, m_item->materialIdentifier());
+        baWin, m_ec->projectDocument(), m_item->materialIdentifier());
 
     if (!newMaterialIdentifier.isEmpty() && newMaterialIdentifier != m_item->materialIdentifier()) {
+        itemWithMaterial()->materialItem()->disconnect(this);
         GUI::Util::Layout::clearLayout(m_layout, true);
         m_ec->selectMaterial(m_item, newMaterialIdentifier);
         createWidgets();
+        connect(itemWithMaterial()->materialItem(), &MaterialItem::dataChanged, this,
+                &MaterialInplaceForm::onMaterialChanged);
     } else
         updateValues(); // necessary, since in the material editor the values could have been
                         // changed without selecting a different material
@@ -77,44 +79,15 @@ void MaterialInplaceForm::selectMaterial()
 
 void MaterialInplaceForm::createWidgets()
 {
-    // We can not use the DoubleDescriptors from the MaterialItem itself, because e.g. when editing
-    // a material in the material editor, the whole material model will be created from scratch.
-    // This means, that all SessionItems will be deleted. If we would use these SessionItems, a
-    // crash will happen once an access takes place.
-    // What not changes is the material identifier (it is not changed when
-    // material is edited in the material editor dialog). Therefore DoubleDescriptors have to be
-    // created which use the material identifier for every access.
-    // In the following, first the descriptors are copied
-    // from the MaterialItem descriptors to get the labels, tooltips etc. Then for set, get &
-    // path the functions are redefined to use the material identifier.
+    auto* material = m_item->materialItem();
+    ASSERT(material);
 
     // -- Create UI for delta/beta resp. sldRe/sldIm
     DoubleDescriptors values;
-    if (material()->hasRefractiveIndex()) {
-        DoubleDescriptor delta = material()->delta();
-        delta.set = [=](double value) { material()->delta().set(value); };
-        delta.get = [=]() { return material()->delta(); };
-        delta.path = [=] { return GUI::Model::Path::getPathFromItem(m_item) + "/delta"; };
-
-        DoubleDescriptor beta = material()->beta();
-        beta.set = [=](double value) { material()->beta().set(value); };
-        beta.get = [=]() { return material()->beta(); };
-        beta.path = [=] { return GUI::Model::Path::getPathFromItem(m_item) + "/beta"; };
-
-        values << delta << beta;
-    } else {
-        DoubleDescriptor re = material()->sldRe();
-        re.set = [=](double value) { material()->sldRe().set(value); };
-        re.get = [=]() { return material()->sldRe(); };
-        re.path = [=] { return GUI::Model::Path::getPathFromItem(m_item) + "/re"; };
-
-        DoubleDescriptor im = material()->sldIm();
-        im.set = [=](double value) { material()->sldIm().set(value); };
-        im.get = [=]() { return material()->sldIm(); };
-        im.path = [=] { return GUI::Model::Path::getPathFromItem(m_item) + "/im"; };
-
-        values << re << im;
-    }
+    if (material->hasRefractiveIndex())
+        values << material->delta() << material->beta();
+    else
+        values << material->sldRe() << material->sldIm();
 
     int col = 0;
     for (const auto& d : values) {
@@ -130,23 +103,10 @@ void MaterialInplaceForm::createWidgets()
     }
 
     // -- Create UI for magnetization vector
-    VectorDescriptor mag = material()->magnetizationVector();
-    mag.x.set = [=](double value) { material()->magnetizationVector().x.set(value); };
-    mag.x.get = [=]() { return material()->magnetizationVector().x; };
-    mag.x.path = [=] { return GUI::Model::Path::getPathFromItem(m_item) + "/x"; };
-
-    mag.y.set = [=](double value) { material()->magnetizationVector().y.set(value); };
-    mag.y.get = [=]() { return material()->magnetizationVector().y; };
-    mag.y.path = [=] { return GUI::Model::Path::getPathFromItem(m_item) + "/y"; };
-
-    mag.z.set = [=](double value) { material()->magnetizationVector().z.set(value); };
-    mag.z.get = [=]() { return material()->magnetizationVector().z; };
-    mag.z.path = [=] { return GUI::Model::Path::getPathFromItem(m_item) + "/z"; };
-
+    VectorDescriptor mag = material->magnetizationVector();
     const auto setNewValue = [=](double value, DoubleDescriptor d) {
         m_ec->setMaterialValue(m_item, value, d);
     };
-
     LayerEditorUtils::addVectorToGrid(m_layout, col, mag, setNewValue, true, false);
 
     // -- Create UI for material selection button
@@ -158,13 +118,7 @@ void MaterialInplaceForm::createWidgets()
     m_layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding), 0, m_layout->columnCount());
 }
 
-MaterialItem* MaterialInplaceForm::material()
-{
-    return m_item->materialItem();
-}
-
-void MaterialInplaceForm::onMaterialChanged(MaterialItem* materialItem)
+void MaterialInplaceForm::onMaterialChanged()
 {
-    if (materialItem->identifier() == m_item->materialIdentifier())
-        updateValues();
+    updateValues();
 }
diff --git a/GUI/View/SampleDesigner/MaterialInplaceForm.h b/GUI/View/SampleDesigner/MaterialInplaceForm.h
index 0d1faa4fc1256e84660e5dc611b468e8bf4ae4fb..db0fd6b063cc45b1df8c457018ff5e2b668b4272 100644
--- a/GUI/View/SampleDesigner/MaterialInplaceForm.h
+++ b/GUI/View/SampleDesigner/MaterialInplaceForm.h
@@ -34,8 +34,7 @@ public:
 private:
     void selectMaterial();
     void createWidgets();
-    MaterialItem* material();
-    void onMaterialChanged(MaterialItem* materialItem);
+    void onMaterialChanged();
 
     ItemWithMaterial* m_item;
     SampleEditorController* m_ec;
diff --git a/GUI/View/SampleDesigner/SampleEditorController.cpp b/GUI/View/SampleDesigner/SampleEditorController.cpp
index 6f9cabb02146b474e9a134cbfa16bf66fea24a75..b7a2376ecc31a46f87aa4543060636127a879cfb 100644
--- a/GUI/View/SampleDesigner/SampleEditorController.cpp
+++ b/GUI/View/SampleDesigner/SampleEditorController.cpp
@@ -14,6 +14,7 @@
 
 #include "GUI/View/SampleDesigner/SampleEditorController.h"
 #include "GUI/Model/Material/MaterialModel.h"
+#include "GUI/Model/Project/ProjectDocument.h"
 #include "GUI/Model/Sample/InterferenceItems.h"
 #include "GUI/Model/Sample/LayerItem.h"
 #include "GUI/Model/Sample/MesoCrystalItem.h"
@@ -35,8 +36,8 @@
 #include "GUI/View/SampleDesigner/ParticleLayoutForm.h"
 #include "GUI/View/SampleDesigner/SampleEditorCommands.h"
 
-SampleEditorController::SampleEditorController(MaterialModel* materialModel, MultiLayerItem* multi)
-    : m_multiLayerItem(multi), m_multiLayerForm(nullptr), m_materialModel(materialModel)
+SampleEditorController::SampleEditorController(ProjectDocument* document, MultiLayerItem* multi)
+    : m_multiLayerItem(multi), m_multiLayerForm(nullptr), m_document(document)
 {
 }
 
@@ -96,8 +97,8 @@ void SampleEditorController::addLayer(LayerItem* before)
 
     // - create new layer
     LayerItem* layer = m_multiLayerItem->addLayer(rowInMultiLayer);
-    layer->setMaterialModel(m_materialModel);
-    layer->setMaterial(m_materialModel->defaultMaterial());
+    layer->setMaterialModel(materialModel());
+    layer->setMaterial(materialModel()->defaultMaterial());
     layer->setColor(color);
 
     ASSERT(m_multiLayerForm);
@@ -129,9 +130,9 @@ void SampleEditorController::addParticle(ParticleLayoutItem* layoutItem, const Q
     SessionItem* newItem = nullptr;
     if (ItemCatalog::isFormFactorModelType(classname)) {
         auto* new_particle = layoutItem->model()->insertItem<ParticleItem>(layoutItem);
-        new_particle->setMaterialModel(m_materialModel);
+        new_particle->setMaterialModel(materialModel());
         new_particle->setFormFactor(classname);
-        new_particle->setMaterial(m_materialModel->defaultMaterial());
+        new_particle->setMaterial(materialModel()->defaultMaterial());
         newItem = new_particle;
     } else
         newItem = layoutItem->model()->insertNewItem(classname, layoutItem);
@@ -155,9 +156,9 @@ void SampleEditorController::addParticle(ParticleCompositionItem* compositionIte
     SessionItem* newItem = nullptr;
     if (ItemCatalog::isFormFactorModelType(classname)) {
         auto* new_particle = compositionItem->model()->insertItem<ParticleItem>(compositionItem);
-        new_particle->setMaterialModel(m_materialModel);
+        new_particle->setMaterialModel(materialModel());
         new_particle->setFormFactor(classname);
-        new_particle->setMaterial(m_materialModel->defaultMaterial());
+        new_particle->setMaterial(materialModel()->defaultMaterial());
         newItem = new_particle;
     } else
         newItem = compositionItem->model()->insertNewItem(classname, compositionItem);
@@ -293,7 +294,12 @@ QUndoStack* SampleEditorController::undoStack()
 
 MaterialModel* SampleEditorController::materialModel() const
 {
-    return m_materialModel;
+    return m_document->materialModel();
+}
+
+ProjectDocument* SampleEditorController::projectDocument() const
+{
+    return m_document;
 }
 
 void SampleEditorController::selectMaterial(ItemWithMaterial* item,
diff --git a/GUI/View/SampleDesigner/SampleEditorController.h b/GUI/View/SampleDesigner/SampleEditorController.h
index 0988bb028797766227e736f05cb864a4e51cbcf0..d8884055c0e092df7e715a20d0ab0c72262d187a 100644
--- a/GUI/View/SampleDesigner/SampleEditorController.h
+++ b/GUI/View/SampleDesigner/SampleEditorController.h
@@ -37,6 +37,7 @@ class InterferenceItem;
 class AbstractSelectionContainerForm;
 class LatticeTypeSelectionForm;
 class MaterialModel;
+class ProjectDocument;
 
 //! Class to modify a sample from the layer oriented sample editor.
 //!
@@ -46,7 +47,7 @@ class MaterialModel;
 class SampleEditorController : public QObject {
     Q_OBJECT
 public:
-    SampleEditorController(MaterialModel* materialModel, MultiLayerItem* multi);
+    SampleEditorController(ProjectDocument* document, MultiLayerItem* multi);
 
     //! Set the current form.
     //!
@@ -66,6 +67,9 @@ public:
     //! The materials of the current document
     MaterialModel* materialModel() const;
 
+    //! The current document
+    ProjectDocument* projectDocument() const;
+
     void addLayer(LayerItem* before);
     void removeLayer(LayerItem* layerItem);
     void addLayout(LayerForm* layerItem);
@@ -109,7 +113,7 @@ private:
     QUndoStack m_undoStack;
     MultiLayerItem* m_multiLayerItem;
     MultiLayerForm* m_multiLayerForm;
-    MaterialModel* m_materialModel;
+    ProjectDocument* m_document;
 };
 
 
diff --git a/GUI/View/SampleDesigner/SampleListView.cpp b/GUI/View/SampleDesigner/SampleListView.cpp
index 1b322e941459a6cd00caa67ac0358167fe9b57e5..7021d7b8d8b968d15baba0dcad14b452a80592db 100644
--- a/GUI/View/SampleDesigner/SampleListView.cpp
+++ b/GUI/View/SampleDesigner/SampleListView.cpp
@@ -15,9 +15,9 @@
 #include "GUI/View/SampleDesigner/SampleListView.h"
 #include "GUI/Application/ApplicationSettings.h"
 #include "GUI/Model/From/GUIExamplesFactory.h"
-#include "GUI/Model/Sample/MultiLayerItem.h"
 #include "GUI/Model/From/SampleListModel.h"
-#include "GUI/Model/State/SessionData.h"
+#include "GUI/Model/Project/ProjectDocument.h"
+#include "GUI/Model/Sample/MultiLayerItem.h"
 #include "GUI/View/Common/ItemViewOverlayButtons.h"
 #include "GUI/View/Tool/ItemDelegateForHTML.h"
 #include <QAction>
@@ -63,9 +63,10 @@ protected:
 
 } // namespace
 
-SampleListView::SampleListView(QWidget* parent, SampleModel* sampleModel) : QListView(parent)
+SampleListView::SampleListView(QWidget* parent, ProjectDocument* document)
+    : QListView(parent), m_document(document)
 {
-    m_model = new SampleListModel(this, sampleModel);
+    m_model = new SampleListModel(this, document->sampleModel());
 
     setContextMenuPolicy(Qt::CustomContextMenu);
     setModel(m_model);
@@ -143,8 +144,8 @@ void SampleListView::createNewSample()
 void SampleListView::createSampleFromLibrary(const QString& classname, const QString& title,
                                              const QString& description)
 {
-    const QModelIndex newIndex = m_model->createSampleFromExamples(
-        classname, title, description, gSessionData->projectDocument->materialModel());
+    const QModelIndex newIndex = m_model->createSampleFromExamples(classname, title, description,
+                                                                   m_document->materialModel());
     setCurrentIndex(newIndex);
 }
 
diff --git a/GUI/View/SampleDesigner/SampleListView.h b/GUI/View/SampleDesigner/SampleListView.h
index 1a39f4c031a731624e00ca1c0ced1627ff2f2345..1c5ce889ef1fb0c25431632c387ef10e862c4a8c 100644
--- a/GUI/View/SampleDesigner/SampleListView.h
+++ b/GUI/View/SampleDesigner/SampleListView.h
@@ -17,15 +17,15 @@
 
 #include <QListView>
 
-class SampleModel;
 class SampleListModel;
 class MultiLayerItem;
+class ProjectDocument;
 
 //! List view to select one sample (left side of layer-oriented sample editor)
 class SampleListView : public QListView {
     Q_OBJECT
 public:
-    SampleListView(QWidget* parent, SampleModel* sampleModel);
+    SampleListView(QWidget* parent, ProjectDocument* document);
 
     void setCurrentSample(MultiLayerItem* multiLayer);
     MultiLayerItem* currentSample();
@@ -53,6 +53,7 @@ private:
 
 private:
     SampleListModel* m_model;
+    ProjectDocument* m_document;
     QAction* m_newSampleAction;
     QAction* m_chooseFromLibraryAction;
 };
diff --git a/GUI/View/SampleDesigner/SampleView.cpp b/GUI/View/SampleDesigner/SampleView.cpp
index 8b86b36743956e90ee40a6e76d370f535b3ec39c..6ccb40759cc433f9df350d0ff066051279b9f4bd 100644
--- a/GUI/View/SampleDesigner/SampleView.cpp
+++ b/GUI/View/SampleDesigner/SampleView.cpp
@@ -47,7 +47,7 @@ SampleView::SampleView(QWidget* parent, ProjectDocument* document)
     sampleSelectionLayout->setSpacing(0);
 
     auto* sampleSelectionToolbar = new StyledToolBar(sampleSelectionPane);
-    auto* sampleSelectionView = new SampleListView(this, m_document->sampleModel());
+    auto* sampleSelectionView = new SampleListView(this, m_document);
     sampleSelectionToolbar->addAction(sampleSelectionView->newSampleAction());
     sampleSelectionToolbar->addAction(sampleSelectionView->chooseFromLibraryAction());
     if (auto* btn = dynamic_cast<QToolButton*>(sampleSelectionToolbar->widgetForAction(
diff --git a/GUI/View/Toplevel/SessionModelView.cpp b/GUI/View/Toplevel/SessionModelView.cpp
index df58eb8ce370d97c0cbc1d55552a1c77fcbfd6f6..bba330a1fcc2dc7e406fd76a5b34094dd40717c2 100644
--- a/GUI/View/Toplevel/SessionModelView.cpp
+++ b/GUI/View/Toplevel/SessionModelView.cpp
@@ -105,8 +105,7 @@ SessionModelView::SessionModelView(QWidget* parent, ProjectDocument* document) :
     layout->addWidget(tabs);
 
     QList<SessionModel*> models{document->instrumentModel(), document->sampleModel(),
-                                document->realDataModel(), document->materialModel(),
-                                document->jobModel()};
+                                document->realDataModel(), document->jobModel()};
 
     for (auto* model : models) {
         auto* treeView = new QTreeView(this);
diff --git a/Tests/Unit/GUI/TestMaterialModel.cpp b/Tests/Unit/GUI/TestMaterialModel.cpp
index c71d7bff5d3268861568266693f1cd4e7161616a..28c755a7e975e25e58dcc3cf1762f053273a27fb 100644
--- a/Tests/Unit/GUI/TestMaterialModel.cpp
+++ b/Tests/Unit/GUI/TestMaterialModel.cpp
@@ -1,6 +1,6 @@
-#include "GUI/Model/Material/MaterialDataItems.h"
 #include "GUI/Model/Material/MaterialItem.h"
 #include "GUI/Model/Material/MaterialModel.h"
+#include "GUI/Model/Types/DoubleDescriptor.h"
 #include "Tests/GTestWrapper/google_test.h"
 #include <memory>
 
@@ -18,12 +18,11 @@ TEST_F(TestMaterialModel, addRefractiveMaterial)
     MaterialItem* material = model->addRefractiveMaterial(name, delta, beta);
 
     EXPECT_EQ(model->materialItems().size(), 1);
-    EXPECT_EQ(model->itemForIndex(material->index()), material);
 
     EXPECT_EQ(material->materialName(), name);
     EXPECT_TRUE(material->hasRefractiveIndex());
-    EXPECT_EQ(material->refractiveIndexDelta(), delta);
-    EXPECT_EQ(material->refractiveIndexBeta(), beta);
+    EXPECT_EQ(material->delta(), delta);
+    EXPECT_EQ(material->beta(), beta);
 }
 
 TEST_F(TestMaterialModel, addSLDMaterial)
@@ -37,11 +36,11 @@ TEST_F(TestMaterialModel, addSLDMaterial)
     MaterialItem* material = model->addSLDMaterial(name, sld_real, sld_imag);
 
     EXPECT_EQ(model->materialItems().size(), 1);
-    EXPECT_EQ(model->itemForIndex(material->index()), material);
 
     EXPECT_EQ(material->materialName(), name);
     EXPECT_FALSE(material->hasRefractiveIndex());
-    EXPECT_EQ(material->scatteringLengthDensity(), complex_t(sld_real, sld_imag));
+    EXPECT_EQ(material->sldRe(), sld_real);
+    EXPECT_EQ(material->sldIm(), sld_imag);
 }
 
 TEST_F(TestMaterialModel, cloneMaterialRefractive)
@@ -55,7 +54,7 @@ TEST_F(TestMaterialModel, cloneMaterialRefractive)
     MaterialItem* material = model->addRefractiveMaterial(name, delta, beta);
     const QString origIdentifier = material->identifier();
 
-    MaterialItem* clonedMaterial = model->cloneMaterial(material);
+    MaterialItem* clonedMaterial = model->insertCopy(*material);
     EXPECT_EQ(model->materialItems().size(), 2);
 
     // clone should not change identifier of original material (as it once happened)
@@ -67,8 +66,8 @@ TEST_F(TestMaterialModel, cloneMaterialRefractive)
     // checking name of cloned material
     EXPECT_EQ(material->materialName() + " (copy)", clonedMaterial->materialName());
     EXPECT_EQ(material->hasRefractiveIndex(), clonedMaterial->hasRefractiveIndex());
-    EXPECT_EQ(material->refractiveIndexDelta(), delta);
-    EXPECT_EQ(material->refractiveIndexBeta(), beta);
+    EXPECT_EQ(material->delta(), delta);
+    EXPECT_EQ(material->beta(), beta);
 }
 
 TEST_F(TestMaterialModel, cloneMaterialSLD)
@@ -82,7 +81,7 @@ TEST_F(TestMaterialModel, cloneMaterialSLD)
     MaterialItem* material = model->addSLDMaterial(name, real, imag);
     const QString origIdentifier = material->identifier();
 
-    MaterialItem* clonedMaterial = model->cloneMaterial(material);
+    MaterialItem* clonedMaterial = model->insertCopy(*material);
     EXPECT_EQ(model->materialItems().size(), 2);
 
     // clone should not change identifier of original material (as it once happened)
@@ -94,7 +93,8 @@ TEST_F(TestMaterialModel, cloneMaterialSLD)
     // checking name of cloned material
     EXPECT_EQ(material->materialName() + " (copy)", clonedMaterial->materialName());
     EXPECT_EQ(material->hasRefractiveIndex(), clonedMaterial->hasRefractiveIndex());
-    EXPECT_EQ(material->scatteringLengthDensity(), complex_t(real, imag));
+    EXPECT_EQ(material->sldRe(), real);
+    EXPECT_EQ(material->sldIm(), imag);
 }
 
 //! Checks the method which returns MaterialItem from known identifier.
diff --git a/Tests/Unit/GUI/TestRealSpaceBuilderUtils.cpp b/Tests/Unit/GUI/TestRealSpaceBuilderUtils.cpp
index 2ab7fb5f9dca93d1a49bd194f2f47a7282fad8f6..94999baa23debefc6290a828750d7286a98330a8 100644
--- a/Tests/Unit/GUI/TestRealSpaceBuilderUtils.cpp
+++ b/Tests/Unit/GUI/TestRealSpaceBuilderUtils.cpp
@@ -154,11 +154,13 @@ TEST_F(TestRealSpaceBuilderUtils, Particle3DContainer)
 TEST_F(TestRealSpaceBuilderUtils, singleParticle3DContainer)
 {
     ApplicationModels models;
+    MaterialModel materialModel;
+    materialModel.addRefractiveMaterial("Default", 1e-3, 1e-5);
     SampleModel* sampleModel = models.sampleModel();
 
     auto* particleItem = sampleModel->insertItem<ParticleItem>();
-    particleItem->setMaterial(models.materialModel()->defaultMaterial());
-    particleItem->setMaterialModel(models.materialModel());
+    particleItem->setMaterial(materialModel.defaultMaterial());
+    particleItem->setMaterialModel(&materialModel);
     EXPECT_EQ(particleItem->abundance(), 1.0);
     EXPECT_TRUE(particleItem->formFactor()->hasModelType<CylinderItem>());
 
@@ -170,8 +172,8 @@ TEST_F(TestRealSpaceBuilderUtils, singleParticle3DContainer)
     // Create a 3D particle from particleItem and associate it to a Particle3DContainer object
     const auto* pItem = dynamic_cast<const ParticleItem*>(particleItem);
     auto particle = pItem->createParticle();
-    auto singleParticle3DContainer = GUI::RealSpace::BuilderUtils(models.materialModel())
-                                         .singleParticle3DContainer(*particle, 8);
+    auto singleParticle3DContainer =
+        GUI::RealSpace::BuilderUtils(&materialModel).singleParticle3DContainer(*particle, 8);
 
     EXPECT_EQ(singleParticle3DContainer.containerSize(), 1u);
     EXPECT_EQ(singleParticle3DContainer.cumulativeAbundance(), 1);
@@ -182,18 +184,20 @@ TEST_F(TestRealSpaceBuilderUtils, singleParticle3DContainer)
 TEST_F(TestRealSpaceBuilderUtils, particle3DContainerVector)
 {
     ApplicationModels models;
+    MaterialModel materialModel;
+    materialModel.addRefractiveMaterial("Default", 1e-3, 1e-5);
     SampleModel* sampleModel = models.sampleModel();
 
     auto* layout = sampleModel->insertItem<ParticleLayoutItem>();
     auto* particle1 = sampleModel->insertItem<ParticleItem>();
     auto* particle2 = sampleModel->insertItem<ParticleItem>();
     auto* particle3 = sampleModel->insertItem<ParticleItem>();
-    particle1->setMaterial(models.materialModel()->defaultMaterial());
-    particle2->setMaterial(models.materialModel()->defaultMaterial());
-    particle3->setMaterial(models.materialModel()->defaultMaterial());
-    particle1->setMaterialModel(models.materialModel());
-    particle2->setMaterialModel(models.materialModel());
-    particle3->setMaterialModel(models.materialModel());
+    particle1->setMaterial(materialModel.defaultMaterial());
+    particle2->setMaterial(materialModel.defaultMaterial());
+    particle3->setMaterial(materialModel.defaultMaterial());
+    particle1->setMaterialModel(&materialModel);
+    particle2->setMaterialModel(&materialModel);
+    particle3->setMaterialModel(&materialModel);
 
     layout->addParticle(particle1);
     layout->addParticle(particle2);
@@ -203,9 +207,8 @@ TEST_F(TestRealSpaceBuilderUtils, particle3DContainerVector)
     particle2->setAbundance(3.0);
     particle3->setAbundance(2.0);
 
-    double total_abundance = GUI::RealSpace::BuilderUtils(models.materialModel())
-                                 .computeCumulativeAbundances(*layout)
-                                 .last();
+    double total_abundance =
+        GUI::RealSpace::BuilderUtils(&materialModel).computeCumulativeAbundances(*layout).last();
     EXPECT_EQ(total_abundance, 10.0);
 
     particle1->setFormFactorType<BoxItem>();
@@ -213,7 +216,7 @@ TEST_F(TestRealSpaceBuilderUtils, particle3DContainerVector)
     particle3->setFormFactorType<PyramidItem>();
 
     auto particle3DContainer_vector =
-        GUI::RealSpace::BuilderUtils(models.materialModel()).particle3DContainerVector(*layout);
+        GUI::RealSpace::BuilderUtils(&materialModel).particle3DContainerVector(*layout);
 
     EXPECT_EQ(particle3DContainer_vector.size(), static_cast<size_t>(3));