diff --git a/CMakeLists.txt b/CMakeLists.txt
index 993fe5264d5e7b09528188eef249fd8eb8988dbe..782ece679bf4cb4e6afec10a5fb1b882911e6f95 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -9,7 +9,7 @@
 cmake_minimum_required( VERSION 3.1 FATAL_ERROR )
 
 # visimpl project and version
-project( visimpl VERSION 1.2.1 )
+project( visimpl VERSION 1.3.0 )
 set( visimpl_VERSION_ABI 6 )
 
 SET( VISIMPL_LICENSE "GPL")
diff --git a/visimpl/DomainManager.cpp b/visimpl/DomainManager.cpp
index 0e88bcc10c5da8e8bb2f21e1f1cde075b59cda09..17284074b88458205441d8a9f2c4094beed1bef9 100644
--- a/visimpl/DomainManager.cpp
+++ b/visimpl/DomainManager.cpp
@@ -422,7 +422,6 @@ namespace visimpl
       group->source( )->active( state );
   }
 
-
   void DomainManager::_updateGroupsModels( void )
   {
     for( auto group : _groups )
@@ -626,7 +625,7 @@ namespace visimpl
   {
     for( auto group : _groups )
     {
-      if( !group->dirty( ))
+      if( !group->dirty( ) || !group->active())
         continue;
 
       if( group->cached( ))
diff --git a/visimpl/MainWindow.cpp b/visimpl/MainWindow.cpp
index 1681deb1c43b4bc24ea2f9c215bdde70d9b4b28d..a0dfebece0027059e41a109cfcbd70a8f9a33f96 100644
--- a/visimpl/MainWindow.cpp
+++ b/visimpl/MainWindow.cpp
@@ -59,6 +59,9 @@
 #include <QPushButton>
 #include <QToolBox>
 #include <QtGlobal>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
 
 #include <thread>
 
@@ -115,6 +118,8 @@ namespace visimpl
     , _circuitScaleZ( nullptr )
     , _buttonImportGroups( nullptr )
     , _buttonClearGroups( nullptr )
+    , _buttonLoadGroups{nullptr}
+    , _buttonSaveGroups{nullptr}
     , _buttonAddGroup( nullptr )
     , _buttonClearSelection( nullptr )
     , _selectionSizeLabel( nullptr )
@@ -917,6 +922,11 @@ namespace visimpl
     _buttonImportGroups = new QPushButton( "Import from..." );
     _buttonClearGroups = new QPushButton( "Clear" );
     _buttonClearGroups->setEnabled( false );
+    _buttonLoadGroups = new QPushButton("Load");
+    _buttonLoadGroups->setToolTip(tr("Load Groups from disk"));
+    _buttonSaveGroups = new QPushButton("Save");
+    _buttonSaveGroups->setToolTip(tr("Save Groups to disk"));
+    _buttonSaveGroups->setEnabled(false);
 
     {
       QWidget* groupContainer = new QWidget( );
@@ -934,8 +944,10 @@ namespace visimpl
       groupOuterLayout->setMargin( 0 );
       groupOuterLayout->addWidget( _buttonImportGroups, 0, 0, 1, 1 );
       groupOuterLayout->addWidget( _buttonClearGroups, 0, 1, 1, 1 );
+      groupOuterLayout->addWidget( _buttonLoadGroups, 1, 0, 1, 1 );
+      groupOuterLayout->addWidget( _buttonSaveGroups, 1, 1, 1, 1 );
 
-      groupOuterLayout->addWidget( scrollGroups, 1, 0, 1, 2 );
+      groupOuterLayout->addWidget( scrollGroups, 2, 0, 1, 2 );
 
       groupBoxGroups->setLayout( groupOuterLayout );
     }
@@ -1130,6 +1142,9 @@ namespace visimpl
     connect( _buttonClearGroups, SIGNAL( clicked( void ) ), this,
              SLOT( clearGroups( void ) ) );
 
+    connect( _buttonLoadGroups, SIGNAL( clicked(void)), this, SLOT(loadGroups()));
+    connect( _buttonSaveGroups, SIGNAL( clicked(void)), this, SLOT(saveGroups()));
+
     _alphaNormalButton->setChecked( true );
   }
 
@@ -1665,7 +1680,7 @@ namespace visimpl
              this,     SLOT(onGroupPreview()));
 
     QCheckBox* visibilityCheckbox = new QCheckBox( "active" );
-    visibilityCheckbox->setChecked( true );
+    visibilityCheckbox->setChecked( group->active() );
 
     connect( visibilityCheckbox, SIGNAL( clicked( ) ), this,
              SLOT( checkGroupsVisibility( ) ) );
@@ -1693,6 +1708,7 @@ namespace visimpl
     _groupLayout->addWidget( container );
 
     _buttonClearGroups->setEnabled( true );
+    _buttonSaveGroups->setEnabled(true);
   }
 
   void MainWindow::Stop( bool notify )
@@ -1735,6 +1751,7 @@ void MainWindow::clearGroups( void )
 
     _buttonImportGroups->setEnabled( true );
     _buttonClearGroups->setEnabled( false );
+    _buttonSaveGroups->setEnabled(false);
   }
 
   void MainWindow::importVisualGroups( void )
@@ -2083,6 +2100,7 @@ void MainWindow::clearGroups( void )
       m_type = type;
       m_subsetEventFile = subsetEventFile;
       _openGLWidget->loadData( arg_1, type, simType, arg_2 );
+      _lastOpenedNetworkFileName = QString::fromStdString(arg_1);
     }
     catch(const std::exception &e)
     {
@@ -2156,6 +2174,307 @@ void MainWindow::clearGroups( void )
     }
   }
 
+  void MainWindow::loadGroups()
+  {
+    const auto title = tr("Load Groups");
+
+    if(!_domainManager->groups().empty())
+    {
+      const auto message = tr("Loading groups from disk will erase the "
+                              "current groups. Do you want to continue?");
+
+      QMessageBox msgbox(this);
+      msgbox.setWindowTitle(title);
+      msgbox.setText(message);
+      msgbox.setIcon(QMessageBox::Icon::Question);
+      msgbox.setWindowIcon(QIcon(":/visimpl.png"));
+      msgbox.setStandardButtons(QMessageBox::Cancel|QMessageBox::Ok);
+      msgbox.setDefaultButton(QMessageBox::Button::Ok);
+
+      if(QMessageBox::Ok != msgbox.exec())
+        return;
+    }
+
+    QFileInfo lastFile{_lastOpenedNetworkFileName};
+    const auto fileName = QFileDialog::getOpenFileName(this, title, lastFile.path(),
+                                                       tr("Json files (*.json)"),
+                                                       nullptr, QFileDialog::ReadOnly|QFileDialog::DontUseNativeDialog);
+    if(fileName.isEmpty()) return;
+
+    QFile file{fileName};
+    if(!file.open(QIODevice::ReadOnly))
+    {
+      const auto message = tr("Couldn't open file %1").arg(fileName);
+
+      QMessageBox msgbox(this);
+      msgbox.setWindowTitle(title);
+      msgbox.setIcon(QMessageBox::Icon::Critical);
+      msgbox.setText(message);
+      msgbox.setWindowIcon(QIcon(":/visimpl.png"));
+      msgbox.setStandardButtons(QMessageBox::Ok);
+      msgbox.exec();
+      return;
+    }
+
+    const auto contents = file.readAll();
+    QJsonParseError jsonError;
+    const auto  jsonDoc = QJsonDocument::fromJson(contents, &jsonError);;
+    if(jsonDoc.isNull() || !jsonDoc.isObject())
+    {
+      const auto message = tr("Couldn't read the contents of %1 or error at parsing.").arg(fileName);
+
+      QMessageBox msgbox{this};
+      msgbox.setWindowTitle(title);
+      msgbox.setIcon(QMessageBox::Icon::Critical);
+      msgbox.setText(message);
+      msgbox.setWindowIcon(QIcon(":/visimpl.png"));
+      msgbox.setStandardButtons(QMessageBox::Ok);
+      msgbox.setDetailedText(jsonError.errorString());
+      msgbox.exec();
+      return;
+    }
+
+    const auto jsonObj = jsonDoc.object();
+    if(jsonObj.isEmpty())
+    {
+      const auto message = tr("Error at parsing.").arg(fileName);
+
+      QMessageBox msgbox{this};
+      msgbox.setWindowTitle(title);
+      msgbox.setIcon(QMessageBox::Icon::Critical);
+      msgbox.setText(message);
+      msgbox.setWindowIcon(QIcon(":/visimpl.png"));
+      msgbox.setStandardButtons(QMessageBox::Ok);
+      msgbox.exec();
+      return;
+    }
+
+    const QFileInfo currentFile{_lastOpenedNetworkFileName};
+    const QString jsonGroupsFile = jsonObj.value("filename").toString();
+    if(jsonGroupsFile.compare(currentFile.fileName(), Qt::CaseInsensitive) != 0)
+    {
+      const auto message = tr("This groups definitions are from file %1. Current file"
+                              " is %2. Do you want to continue?").arg(jsonGroupsFile).arg(currentFile.fileName());
+
+      QMessageBox msgbox{this};
+      msgbox.setWindowTitle(title);
+      msgbox.setIcon(QMessageBox::Icon::Question);
+      msgbox.setText(message);
+      msgbox.setWindowIcon(QIcon(":/visimpl.png"));
+      msgbox.setStandardButtons(QMessageBox::Cancel|QMessageBox::Ok);
+      msgbox.setDefaultButton(QMessageBox::Ok);
+
+      if(QMessageBox::Ok != msgbox.exec())
+        return;
+    }
+
+    QApplication::setOverrideCursor(Qt::WaitCursor);
+
+    clearGroups();
+    const auto jsonGroups = jsonObj.value("groups").toArray();
+
+    std::vector<VisualGroup *> groupsList;
+    const auto createGroup = [&groupsList, this](const QJsonValue &v)
+    {
+      const auto o = v.toObject();
+
+      const auto name = o.value("name").toString();
+      const auto overrideGIDS = o.value("override").toBool(false);
+      const auto gidsStrings = o.value("gids").toString().split(",");
+
+      GIDUSet gids;
+      auto addGids = [&gids](const QString s)
+      {
+        if(s.contains(":"))
+        {
+         auto limits = s.split(":");
+         for(unsigned int id = limits.first().toUInt(); id <= limits.last().toUInt(); ++id)
+           gids.insert(id);
+        }
+        else
+        {
+          gids.insert(s.toUInt());
+        }
+      };
+      std::for_each(gidsStrings.cbegin(), gidsStrings.cend(), addGids);
+
+      auto group = _domainManager->addVisualGroup(gids, name.toStdString(), overrideGIDS);
+      auto idx = _domainManager->groups().size()-1;
+      addGroupControls(group, idx, gids.size());
+      const auto active = o.value("active").toBool(true);
+      auto checkbox = std::get< gr_checkbox >(_groupsVisButtons.at(idx));
+      checkbox->setChecked(active);
+
+      _domainManager->setVisualGroupState( idx, active );
+
+      const auto functionPairs = o.value("function").toString().split(";");
+      TTransferFunction function;
+      auto addFunctionPair = [&function](const QString &s)
+      {
+        const auto parts = s.split(",");
+        Q_ASSERT(parts.size() == 2);
+        const auto value = parts.first().toFloat();
+        const auto color = QColor(parts.last());
+        function.emplace_back(value, color);
+      };
+      std::for_each(functionPairs.cbegin(), functionPairs.cend(), addFunctionPair);
+
+      const auto sizePairs = o.value("sizes").toString().split(";");
+      TSizeFunction sizes;
+      auto addSizes = [&sizes](const QString &s)
+      {
+        const auto parts = s.split(",");
+        Q_ASSERT(parts.size() == 2);
+        const auto a = parts.first().toFloat();
+        const auto b = parts.last().toFloat();
+        sizes.emplace_back(a, b);
+      };
+      std::for_each(sizePairs.cbegin(), sizePairs.cend(), addSizes);
+
+      updateGroupColors(idx, function, sizes);
+      auto container = std::get< gr_container >(_groupsVisButtons.at(idx));
+      auto tfw = qobject_cast<TransferFunctionWidget*>(container->layout()->itemAt(0)->widget());
+      if(tfw)
+      {
+        tfw->setColorPoints(function, true);
+        tfw->setSizeFunction(sizes);
+        tfw->colorChanged();
+        tfw->sizeChanged();
+      }
+    };
+    std::for_each(jsonGroups.constBegin(), jsonGroups.constEnd(), createGroup);
+
+    _groupLayout->update();
+    checkGroupsVisibility();
+
+    const auto groups = _domainManager->groups();
+    _buttonClearGroups->setEnabled( !groups.empty() );
+    _buttonSaveGroups->setEnabled( !groups.empty() );
+
+    QApplication::restoreOverrideCursor();
+  }
+
+  void MainWindow::saveGroups()
+  {
+    const auto &groups = _domainManager->groups();
+    if(groups.empty()) return;
+
+    const auto dateTime = QDateTime::currentDateTime();
+    QFileInfo lastFile{_lastOpenedNetworkFileName};
+    QString filename = lastFile.dir().absoluteFilePath(lastFile.baseName() + "_groups_" + dateTime.toString("yyyy-MM-dd-hh-mm") + ".json");
+    filename = QFileDialog::getSaveFileName(this, tr("Save Groups"), filename, tr("Json files (*.json)"), nullptr, QFileDialog::DontUseNativeDialog);
+
+    if(filename.isEmpty()) return;
+
+    QFile wFile{filename};
+    if(!wFile.open(QIODevice::WriteOnly|QIODevice::Text|QIODevice::Truncate))
+    {
+      const auto message = tr("Unable to open file %1 for writing.").arg(filename);
+
+      QMessageBox msgbox{this};
+      msgbox.setWindowTitle(tr("Save Groups"));
+      msgbox.setIcon(QMessageBox::Icon::Critical);
+      msgbox.setText(message);
+      msgbox.setWindowIcon(QIcon(":/visimpl.png"));
+      msgbox.setDefaultButton(QMessageBox::Ok);
+      msgbox.exec();
+      return;
+    }
+
+    QApplication::setOverrideCursor(Qt::WaitCursor);
+
+    QJsonObject obj;
+    obj.insert("filename", QFileInfo{_lastOpenedNetworkFileName}.fileName());
+    obj.insert("date", dateTime.toString());
+
+    QJsonArray groupsObjs;
+
+    auto insertGroup = [&groupsObjs, this](const VisualGroup *g)
+    {
+      QJsonObject groupObj;
+      groupObj.insert("name", QString::fromStdString(g->name()));
+      groupObj.insert("active", g->active());
+
+      QStringList tfList;
+      const auto tf = g->colorMapping();
+      auto addColors = [&tfList](const TTFColor &c)
+      {
+        tfList << QString("%1,%2").arg(c.first).arg(c.second.name(QColor::HexArgb));
+      };
+      std::for_each(tf.cbegin(), tf.cend(), addColors);
+      groupObj.insert("function", tfList.join(";"));
+
+      QStringList sizesList;
+      const auto sizes = g->sizeFunction();
+      auto addSizes = [&sizesList](const TSize &s)
+      {
+        sizesList << QString("%1,%2").arg(s.first).arg(s.second);
+      };
+      std::for_each(sizes.cbegin(), sizes.cend(), addSizes);
+      groupObj.insert("sizes", sizesList.join(";"));
+
+      const auto &gids = g->gids();
+      std::vector<unsigned int> gidsVec;
+      std::for_each(gids.cbegin(), gids.cend(), [&gidsVec](unsigned int v){ gidsVec.push_back(v); });
+      std::sort(gidsVec.begin(), gidsVec.end());
+      QStringList gidsStrings;
+      std::pair<unsigned int, unsigned int> range = std::make_pair(std::numeric_limits<unsigned int>::max() - 1,std::numeric_limits<unsigned int>::max() - 1);
+      auto enterNumber = [&range, &gidsStrings]()
+      {
+          if(range.first == range.second)
+            gidsStrings << QString::number(range.first);
+          else
+            gidsStrings << QString("%1:%2").arg(range.first).arg(range.second);
+      };
+
+      for(auto i = gidsVec.begin(); i != gidsVec.end(); ++i)
+      {
+        auto num = *i;
+        if(num != range.second + 1)
+        {
+          if(range.first != std::numeric_limits<unsigned int>::max() - 1)
+          {
+            enterNumber();
+          }
+          range.first = num;
+        }
+
+        range.second = num;
+      }
+      enterNumber();
+
+      groupObj.insert("gids", gidsStrings.join(","));
+
+      groupsObjs << groupObj;
+    };
+    std::for_each(groups.cbegin(), groups.cend(), insertGroup);
+
+    obj.insert("groups", groupsObjs);
+
+    QJsonDocument doc{obj};
+    const auto temp = doc.toJson().toStdString();
+    wFile.write(doc.toJson());
+
+    QApplication::restoreOverrideCursor();
+
+    if(wFile.error() != QFile::NoError)
+    {
+      const auto message = tr("Error saving file %1.").arg(filename);
+
+      QMessageBox msgbox{this};
+      msgbox.setWindowTitle(tr("Save Groups"));
+      msgbox.setIcon(QMessageBox::Icon::Critical);
+      msgbox.setText(message);
+      msgbox.setDetailedText(wFile.errorString());
+      msgbox.setWindowIcon(QIcon(":/visimpl.png"));
+      msgbox.setDefaultButton(QMessageBox::Ok);
+      msgbox.exec();
+    }
+
+    wFile.flush();
+    wFile.close();
+  }
+
   void MainWindow::sendZeroEQPlaybackOperation(const unsigned int op)
   {
 #ifdef SIMIL_USE_ZEROEQ
diff --git a/visimpl/MainWindow.h b/visimpl/MainWindow.h
index baee250be5384fd0e8ef635cd3dbe14206273d08..8945f62bd112406a8484611daa295c9db4b3dcc9 100644
--- a/visimpl/MainWindow.h
+++ b/visimpl/MainWindow.h
@@ -194,6 +194,16 @@ namespace visimpl
      */
     void onDataLoaded();
 
+    /** \brief Loads groups and its properties from a file on disk.
+     *
+     */
+    void loadGroups();
+
+    /** \brief Saves current groups and its properties to a file on disk.
+     *
+     */
+    void saveGroups();
+
   protected:
     void _initSimControlDock( void );
     void _initPlaybackDock( void );
@@ -293,6 +303,8 @@ namespace visimpl
 
     QPushButton* _buttonImportGroups;
     QPushButton* _buttonClearGroups;
+    QPushButton* _buttonLoadGroups;
+    QPushButton* _buttonSaveGroups;
     QPushButton* _buttonAddGroup;
     QPushButton* _buttonClearSelection;
     QLabel* _selectionSizeLabel;
diff --git a/visimpl/SelectionManagerWidget.cpp b/visimpl/SelectionManagerWidget.cpp
index 5793d21c4242b572e629be94ca0fccb27fb751c6..9e3175e7b7f27bd853ea33524e092bbd83086cff 100644
--- a/visimpl/SelectionManagerWidget.cpp
+++ b/visimpl/SelectionManagerWidget.cpp
@@ -51,12 +51,12 @@ namespace visimpl
 
   void SelectionManagerWidget::init( void )
   {
-    QVBoxLayout* layoutTop = new QVBoxLayout( );
+    auto layoutTop = new QVBoxLayout( );
     this->setLayout( layoutTop );
 
     _tabWidget = new QTabWidget( );
     QWidget* containerFoot = new QWidget( );
-    QGridLayout* layoutFoot = new QGridLayout( );
+    auto layoutFoot = new QGridLayout( );
     containerFoot->setLayout( layoutFoot );
 
     layoutTop->addWidget( _tabWidget );
@@ -84,10 +84,9 @@ namespace visimpl
 
   void SelectionManagerWidget::_initTabSelection( void )
   {
+    auto containerSelection = new QWidget( );
 
-    QWidget* containerSelection = new QWidget( );
-
-    QGridLayout* layoutSelection = new QGridLayout( );
+    auto layoutSelection = new QGridLayout( );
     containerSelection->setLayout( layoutSelection );
 
     _labelAvailable = new QLabel( "Available GIDs: 0" );
@@ -132,8 +131,8 @@ namespace visimpl
 
   void SelectionManagerWidget::_initTabExport( void )
   {
-    QWidget* containerExport = new QWidget( );
-    QGridLayout* layoutExport = new QGridLayout( );
+    auto containerExport = new QWidget( );
+    auto layoutExport = new QGridLayout( );
     containerExport->setLayout( layoutExport );
 
     _pathExportDefault = QDir::currentPath();
@@ -151,16 +150,16 @@ namespace visimpl
     _buttonBrowse = new QPushButton( "Browse..." );
     _buttonSave = new QPushButton( "Save" );
 
-    QGroupBox* groupBoxPrefix = new QGroupBox( "Prefix/Suffix" );
-    QGridLayout* layoutPrefix = new QGridLayout( );
+    auto groupBoxPrefix = new QGroupBox( "Prefix/Suffix" );
+    auto layoutPrefix = new QGridLayout( );
     groupBoxPrefix->setLayout( layoutPrefix );
     layoutPrefix->addWidget( new QLabel( "Prefix:" ), 0, 0, 1, 1 );
     layoutPrefix->addWidget( _lineEditPrefix, 0, 1, 1, 1 );
     layoutPrefix->addWidget( new QLabel( "Suffix:" ), 1, 0, 1, 1 );
     layoutPrefix->addWidget( _lineEditSuffix, 1, 1, 1, 1 );
 
-    QGroupBox* groupBoxSeparator = new QGroupBox( "Separator" );
-    QGridLayout* layoutSeparator = new QGridLayout( );
+    auto groupBoxSeparator = new QGroupBox( "Separator" );
+    auto layoutSeparator = new QGridLayout( );
     layoutSeparator->addWidget( _radioNewLine, 0, 0, 1, 2 );
     layoutSeparator->addWidget( _radioSpace, 1, 0, 1, 2 );
     layoutSeparator->addWidget( _radioTab, 2, 0, 1, 2 );
@@ -171,8 +170,6 @@ namespace visimpl
 
     groupBoxSeparator->setLayout( layoutSeparator );
 
-
-
     layoutExport->addWidget( new QLabel( "File path:"), 0, 0, 1, 1 );
     layoutExport->addWidget( _lineEditFilePath, 0, 1, 1, 4 );
     layoutExport->addWidget( _buttonBrowse, 0, 5, 1, 1 );
@@ -180,8 +177,6 @@ namespace visimpl
     layoutExport->addWidget( groupBoxSeparator, 1, 2, 4, 3 );
     layoutExport->addWidget( _buttonSave, 4, 5, 1, 1 );
 
-
-
     connect( _buttonBrowse, SIGNAL( clicked( void )),
              this, SLOT( _buttonBrowseClicked( void )));
 
@@ -352,11 +347,22 @@ namespace visimpl
                                             const QString& prefix,
                                             const QString& suffix )
   {
-    QFile file;
+    if(prefix.contains(separator) || suffix.contains(separator))
+    {
+      QMessageBox msgBox( this );
+      msgBox.setWindowTitle("Selection save");
+      msgBox.setText( "The prefix and the suffix cannot contain the separator character." );
+      msgBox.setStandardButtons( QMessageBox::Ok );
+      msgBox.exec( );
+
+      return;
+    }
 
+    QFile file;
     if( file.exists( filePath ))
     {
       QMessageBox msgBox( this );
+      msgBox.setWindowTitle("Selection save");
       msgBox.setText( "The selected file already exists." );
       msgBox.setInformativeText( "Do you want to overwrite?" );
       msgBox.setStandardButtons( QMessageBox::Save | QMessageBox::Cancel );
@@ -375,7 +381,7 @@ namespace visimpl
     std::copy( _gidsSelected.begin( ), _gidsSelected.end( ), gids.begin( ));
     std::sort( gids.begin( ), gids.end( ));
 
-    for( auto gid : gids )
+    for( auto &gid : gids )
     {
       outStream << prefix << gid << suffix << separator;
     }