Salome HOME
Addition of MEDFileMesh.zipFamilies
[tools/medcoupling.git] / src / MEDLoader / MEDFileMesh.cxx
index 35babc642440272ab5955b70892eb36be5ea9dc4..49d4755ae2f54b4e6003d36b6e23d7c220aabc70 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2007-2016  CEA/DEN, EDF R&D
+// Copyright (C) 2007-2019  CEA/DEN, EDF R&D
 //
 // This library is free software; you can redistribute it and/or
 // modify it under the terms of the GNU Lesser General Public
@@ -513,7 +513,7 @@ std::vector<std::string> MEDFileMesh::getFamiliesNames() const
  * Returns names of all families of \a this mesh but like they would be in file.
  * This method is here only for MED file families gurus. If you are a kind user forget this method :-)
  * This method is only useful for aggressive users that want to have in their file a same family lying both on cells and on nodes. This is not a good idea for lisibility !
- * For your information internaly in memory such families are renamed to have a nicer API.
+ * For your information internally in memory such families are renamed to have a nicer API.
  */
 std::vector<std::string> MEDFileMesh::getFamiliesNamesWithFilePointOfView() const
 {
@@ -673,6 +673,35 @@ std::vector<std::string> MEDFileMesh::removeEmptyGroups()
   return ret;
 }
 
+void MEDFileMesh::removeGroupAtLevel(int meshDimRelToMaxExt, const std::string& name)
+{
+  std::map<std::string, std::vector<std::string> >::iterator it(_groups.find(name));
+  std::vector<std::string> grps(getGroupsNames());
+  if(it==_groups.end())
+    {
+      std::ostringstream oss; oss << "No such groupname \"" << name << "\" !\nAvailable groups are :";
+      std::copy(grps.begin(),grps.end(),std::ostream_iterator<std::string>(oss," "));
+      throw INTERP_KERNEL::Exception(oss.str().c_str());
+    }
+  const std::vector<std::string> &famsOnGrp((*it).second);
+  std::vector<int> famIds(getFamiliesIdsOnGroup(name));
+  const DataArrayInt *famArr(getFamilyFieldAtLevel(meshDimRelToMaxExt));
+  if(!famArr)
+    return ;
+  MCAuto<DataArrayInt> vals(famArr->getDifferentValues());
+  MCAuto<DataArrayInt> famIds2(DataArrayInt::NewFromStdVector(famIds));
+  MCAuto<DataArrayInt> idsToKill(famIds2->buildIntersection(vals));
+  if(idsToKill->empty())
+    return ;
+  std::vector<std::string> newFamsOnGrp;
+  for(std::vector<std::string>::const_iterator it=famsOnGrp.begin();it!=famsOnGrp.end();it++)
+    {
+      if(!idsToKill->presenceOfValue(getFamilyId(*it)))
+         newFamsOnGrp.push_back(*it);
+    }
+  (*it).second=newFamsOnGrp;
+}
+
 /*!
  * Removes a group from \a this mesh.
  *  \param [in] name - the name of the group to remove.
@@ -680,9 +709,8 @@ std::vector<std::string> MEDFileMesh::removeEmptyGroups()
  */
 void MEDFileMesh::removeGroup(const std::string& name)
 {
-  std::string oname(name);
-  std::map<std::string, std::vector<std::string> >::iterator it=_groups.find(oname);
-  std::vector<std::string> grps=getGroupsNames();
+  std::map<std::string, std::vector<std::string> >::iterator it=_groups.find(name);
+  std::vector<std::string> grps(getGroupsNames());
   if(it==_groups.end())
     {
       std::ostringstream oss; oss << "No such groupname \"" << name << "\" !\nAvailable groups are :";
@@ -738,7 +766,7 @@ std::vector<std::string> MEDFileMesh::removeOrphanGroups()
  * family field whatever its level. Groups are updated in consequence, that is to say all groups lying on orphan family, will see their families list modified.
  * 
  * \return - The list of removed families names.
- * \sa MEDFileMesh::removeOrphanGroups.
+ * \sa MEDFileMesh::removeOrphanGroups , MEDFileMesh::removeFamiliesReferedByNoGroups
  */
 std::vector<std::string> MEDFileMesh::removeOrphanFamilies()
 {
@@ -779,6 +807,7 @@ std::vector<std::string> MEDFileMesh::removeOrphanFamilies()
  * this family is orphan or not.
  *
  * \warning this method is different from removeOrphanFamilies that scans family field array to find orphan families.
+ * \sa MEDFileMesh::removeOrphanFamilies
  */
 void MEDFileMesh::removeFamiliesReferedByNoGroups()
 {
@@ -795,11 +824,13 @@ void MEDFileMesh::removeFamiliesReferedByNoGroups()
 }
 
 /*!
- * This method has no impact on groups. This method only works on families. This method firstly removes families not refered by any groups in \a this, then all unused entities
+ * This method has no impact on groups. This method only works on families. This method firstly removes families not referred by any groups in \a this, then all unused entities
  * are put as belonging to family 0 ("FAMILLE_ZERO"). Finally, all orphanFamilies are killed.
  * This method raises an exception if "FAMILLE_ZERO" is already belonging to a group.
+ * 
+ * This method also raises an exception if a family belonging to a group has also id 0 (which is not right in MED file format). You should never encounter this case using addGroup method.
  *
- * \sa MEDFileMesh::removeOrphanFamilies
+ * \sa MEDFileMesh::removeOrphanFamilies, MEDFileMesh::zipFamilies
  */
 void MEDFileMesh::rearrangeFamilies()
 {
@@ -809,7 +840,17 @@ void MEDFileMesh::rearrangeFamilies()
   std::vector<int> levels(getNonEmptyLevelsExt());
   std::set<int> idsRefed;
   for(std::map<std::string,int>::const_iterator it=_families.begin();it!=_families.end();it++)
-    idsRefed.insert((*it).second);
+    {
+      idsRefed.insert((*it).second);
+      if((*it).second==0)
+        {
+          if(!getGroupsOnFamily((*it).first).empty())
+            {
+              std::ostringstream oss; oss << "MEDFileMesh::rearrangeFamilies : Not orphan family \"" << (*it).first << "\" has id 0 ! This method may alterate groups in this for such a case !";
+              throw INTERP_KERNEL::Exception(oss.str());
+            }
+        }
+    }
   for(std::vector<int>::const_iterator it=levels.begin();it!=levels.end();it++)
     {
       const DataArrayInt *fams(0);
@@ -834,6 +875,83 @@ void MEDFileMesh::rearrangeFamilies()
   removeOrphanFamilies();
 }
 
+/*!
+ * This method has no impact on existing groups. This method has only impact on families behind the groups.
+ * This method is especially useful for MED file structures having used too much families to define their groups and that need to be merged without modification of their groups.
+ * To zip families, firstly this method first removes families refered by no groups (see MEDFileMesh::removeFamiliesReferedByNoGroups), then this method puts together families lying on a same set of groups. If the set of families having same groups has a length higher than 1, the families are merged into a single family
+ * having the name of the first family appearing in family definition and with the corresponding family ID.
+ */
+void MEDFileMesh::zipFamilies()
+{
+  checkOrphanFamilyZero();
+  removeFamiliesReferedByNoGroups();
+  std::map< std::set<std::string> , std::vector<std::string> > setOfFamilies;
+  // firstly, store in setOfFamilies as key the common set of groups, and as value the families having such same set of groups
+  for(auto fam : _families)
+    {
+      std::vector<std::string> grps( this->getGroupsOnFamily( fam.first ) );
+      std::set<std::string> sgrps(grps.begin(),grps.end());
+      setOfFamilies[sgrps].push_back(fam.first);
+    }
+  //
+  std::map<std::string, std::vector<std::string> > newGroups(_groups);
+  std::map<std::string,int> newFams(_families);
+  std::vector<int> levels(getNonEmptyLevelsExt());
+  std::map<int, std::vector<int> > famIdsToSubstitute;
+  // iterate on all different set of groups
+  std::set<std::string> familiesToKill;
+  for(auto setOfCommonGrp : setOfFamilies)
+    {
+      if( setOfCommonGrp.second.size()<=1 )
+        continue;
+      for(auto fam=setOfCommonGrp.second.begin()+1 ; fam != setOfCommonGrp.second.end() ; fam++)
+        familiesToKill.insert(*fam);
+    }
+  // iterate on all different set of groups
+  for(auto setOfCommonGrp : setOfFamilies)
+    {
+      if( setOfCommonGrp.second.size()<=1 )
+        continue;
+      std::string newFamName(setOfCommonGrp.second[0]);
+      auto newFamID(_families[newFamName]);
+      for(auto grpToBeModified : setOfCommonGrp.first)
+        {
+          std::vector<std::string> newFamiliesForCurGrp(1,newFamName);
+          const std::vector<std::string>& familiesOnCurGrp(_groups[grpToBeModified]);
+          const std::vector<std::string>& familiesToZip(setOfCommonGrp.second);
+          std::for_each(familiesToZip.begin(),familiesToZip.end(),[&famIdsToSubstitute,this,newFamID](const std::string& elt) { famIdsToSubstitute[newFamID].push_back(this->getFamilyId(elt)); });
+          // for each family shared by the current group only keep those not sharing setOfCommonGrp.second
+          std::for_each(familiesOnCurGrp.begin(),familiesOnCurGrp.end(),[&familiesToKill,&newFamiliesForCurGrp](const std::string& elt)
+                        { if( familiesToKill.find(elt) == familiesToKill.end() ) { newFamiliesForCurGrp.push_back(elt); } });
+          newGroups[grpToBeModified] = newFamiliesForCurGrp;
+        }
+      for(auto familyToKill = setOfCommonGrp.second.begin()+1 ; familyToKill != setOfCommonGrp.second.end(); ++familyToKill)
+        {
+          newFams.erase( newFams.find(*familyToKill) );
+        }
+      
+    }
+  // apply modifications in datastructure
+  for(auto famIdsSubstSession : famIdsToSubstitute)
+    {
+      for(std::vector<int>::const_iterator it=levels.begin();it!=levels.end();it++)
+        {
+          DataArrayInt *fams(nullptr);
+          try
+            {
+              fams=getFamilyFieldAtLevel(*it);
+            }
+          catch(INTERP_KERNEL::Exception& e) { }
+          if(!fams)
+            continue;
+          MCAuto<DataArrayInt> idsToModif(fams->findIdsEqualList(famIdsSubstSession.second.data(),famIdsSubstSession.second.data()+famIdsSubstSession.second.size()));
+          fams->setPartOfValuesSimple3(famIdsSubstSession.first,idsToModif->begin(),idsToModif->end(),0,1,1);
+        }
+    }
+  _groups = newGroups;
+  _families = newFams;
+}
+
 /*!
  * This method only checks that "FAMILLE_ZERO" is orphan (not belonging to a group).
  */
@@ -1256,6 +1374,19 @@ void MEDFileMesh::addFamilyOnAllGroupsHaving(const std::string& famName, const s
     }
 }
 
+void MEDFileMesh::checkNoGroupClash(const DataArrayInt *famArr, const std::string& grpName) const
+{
+  std::vector<std::string> grpsNames(getGroupsNames());
+  if(std::find(grpsNames.begin(),grpsNames.end(),grpName)==grpsNames.end())
+    return ;
+  std::vector<int> famIds(getFamiliesIdsOnGroup(grpName));
+  if(famArr->presenceOfValue(famIds))
+    {
+      std::ostringstream oss; oss << "MEDFileUMesh::addGroup : Group with name \"" << grpName << "\" already exists at specified level ! Destroy it before calling this method !";
+      throw INTERP_KERNEL::Exception(oss.str().c_str());
+    }
+}
+
 /*!
  * \param [in] ids ids and group name of the new group to add. The ids should be sorted and different each other (MED file norm).
  * \parma [in,out] famArr family array on level of interest to be renumbered. The input pointer should be not \c NULL (no check of that will be performed)
@@ -1268,13 +1399,8 @@ void MEDFileMesh::addGroupUnderground(bool isNodeGroup, const DataArrayInt *ids,
   if(grpName.empty())
     throw INTERP_KERNEL::Exception("MEDFileUMesh::addGroup : empty group name ! MED file format do not accept empty group name !");
   ids->checkStrictlyMonotonic(true);
-  famArr->incrRef(); MCAuto<DataArrayInt> famArrTmp(famArr);
-  std::vector<std::string> grpsNames=getGroupsNames();
-  if(std::find(grpsNames.begin(),grpsNames.end(),grpName)!=grpsNames.end())
-    {
-      std::ostringstream oss; oss << "MEDFileUMesh::addGroup : Group with name \"" << grpName << "\" already exists ! Destroy it before calling this method !";
-      throw INTERP_KERNEL::Exception(oss.str().c_str());
-    }
+  checkNoGroupClash(famArr,grpName);
+  MCAuto<DataArrayInt> famArrTmp; famArrTmp.takeRef(famArr);
   std::list< MCAuto<DataArrayInt> > allFamIds(getAllNonNullFamilyIds());
   allFamIds.erase(std::find(allFamIds.begin(),allFamIds.end(),famArrTmp));
   MCAuto<DataArrayInt> famIds=famArr->selectByTupleIdSafe(ids->begin(),ids->end());
@@ -1297,7 +1423,7 @@ void MEDFileMesh::addGroupUnderground(bool isNodeGroup, const DataArrayInt *ids,
           bool isFamPresent=false;
           for(std::list< MCAuto<DataArrayInt> >::const_iterator itl=allFamIds.begin();itl!=allFamIds.end() && !isFamPresent;itl++)
             isFamPresent=(*itl)->presenceOfValue(*famId);
-          if(!isFamPresent)
+          if(!isFamPresent && *famId!=0)
             { familyIds.push_back(*famId); idsPerfamiliyIds.push_back(ret0); fams.push_back(FindOrCreateAndGiveFamilyWithId(families,*famId,created)); } // adding *famId in grp
           else
             {
@@ -1331,8 +1457,15 @@ void MEDFileMesh::addGroupUnderground(bool isNodeGroup, const DataArrayInt *ids,
       famArr->setPartOfValuesSimple3(familyIds[i],da->begin(),da->end(),0,1,1);
     }
   _families=families;
+  std::map<std::string, std::vector<std::string> >::iterator itt(groups.find(grpName));
+  if(itt!=groups.end())
+    {
+      std::vector<std::string>& famsOnGrp((*itt).second);
+      famsOnGrp.insert(famsOnGrp.end(),fams.begin(),fams.end());
+    }
+  else
+    groups[grpName]=fams;
   _groups=groups;
-  _groups[grpName]=fams;
 }
 
 void MEDFileMesh::changeAllGroupsContainingFamily(const std::string& familyNameToChange, const std::vector<std::string>& newFamiliesNames)
@@ -2282,7 +2415,7 @@ MEDFileUMesh *MEDFileUMesh::New(med_idt fid, MEDFileMeshReadSelector *mrs)
 }
 
 /*!
- * \b WARNING this implementation is dependant from MEDCouplingMappedExtrudedMesh::buildUnstructured !
+ * \b WARNING this implementation is dependent from MEDCouplingMappedExtrudedMesh::buildUnstructured !
  * \sa MEDCouplingMappedExtrudedMesh::buildUnstructured , MEDCouplingMappedExtrudedMesh::build3DUnstructuredMesh
  */
 MEDFileUMesh *MEDFileUMesh::New(const MEDCouplingMappedExtrudedMesh *mem)
@@ -2776,7 +2909,7 @@ void MEDFileMesh::checkCartesian() const
   if(getAxisType()!=AX_CART)
     {
       std::ostringstream oss; oss << "MEDFileMesh::checkCartesian : request for method that is dedicated to a cartesian convention ! But you are not in cartesian convention (" << DataArray::GetAxisTypeRepr(getAxisType()) << ").";
-      oss << std::endl << "To perform operation you have two possiblities :" << std::endl;
+      oss << std::endl << "To perform operation you have two possibilities :" << std::endl;
       oss << " - call setAxisType(AX_CART)" << std::endl;
       oss << " - call cartesianize()";
       throw INTERP_KERNEL::Exception(oss.str().c_str());
@@ -3635,7 +3768,7 @@ MEDCouplingUMesh *MEDFileUMesh::getLevelM3Mesh(bool renum) const
 /*!
  * This method is for advanced users. There is two storing strategy of mesh in \a this.
  * Either MEDCouplingUMesh, or vector of MEDCoupling1GTUMesh instances.
- * When assignement is done the first one is done, which is not optimal in write mode for MED file.
+ * When assignment is done the first one is done, which is not optimal in write mode for MED file.
  * This method allows to switch from MEDCouplingUMesh mode to MEDCoupling1GTUMesh mode.
  */
 void MEDFileUMesh::forceComputationOfParts() const
@@ -3944,7 +4077,7 @@ void MEDFileUMesh::buildInnerBoundaryAlongM1Group(const std::string& grpNameM1,
 
   std::vector<int> levs=getNonEmptyLevels();
   if(std::find(levs.begin(),levs.end(),0)==levs.end() || std::find(levs.begin(),levs.end(),-1)==levs.end())
-    throw INTERP_KERNEL::Exception("MEDFileUMesh::buildInnerBoundaryAlongM1Group : This method works only for mesh definied on level 0 and -1 !");
+    throw INTERP_KERNEL::Exception("MEDFileUMesh::buildInnerBoundaryAlongM1Group : This method works only for mesh defined on level 0 and -1 !");
   MUMesh m0=getMeshAtLevel(0);
   MUMesh m1=getMeshAtLevel(-1);
   int nbNodes=m0->getNumberOfNodes();
@@ -4133,12 +4266,12 @@ struct MEDLoaderAccVisit1
 /*! \endcond */
 
 /*!
- * Array returned is the correspondance in \b old \b to \b new format. The returned array is newly created and should be dealt by the caller.
+ * Array returned is the correspondence in \b old \b to \b new format. The returned array is newly created and should be dealt by the caller.
  * The maximum value stored in returned array is the number of nodes of \a this minus 1 after call of this method.
  * The size of returned array is the number of nodes of the old (previous to the call of this method) number of nodes.
  * -1 values in returned array means that the corresponding old node is no more used.
  *
- * \return newly allocated array containing correspondance in \b old \b to \b new format. If all nodes in \a this are fetched \c NULL pointer is returned and nothing
+ * \return newly allocated array containing correspondence in \b old \b to \b new format. If all nodes in \a this are fetched \c NULL pointer is returned and nothing
  *         is modified in \a this.
  * \throw If no coordinates are set in \a this or if there is in any available mesh in \a this a cell having a nodal connectivity containing a node id not in the range of
  *  set coordinates.
@@ -4611,6 +4744,11 @@ MCAuto<MEDFileUMesh> MEDFileUMesh::symmetry3DPlane(const double point[3], const
   return ret;
 }
 
+/*!
+ * Aggregate the given MEDFileUMesh objects into a single mesh. When groups are present, those are
+ * merged in such a way that the final mesh contain all of them.
+ * \return a new object.
+ */
 MCAuto<MEDFileUMesh> MEDFileUMesh::Aggregate(const std::vector<const MEDFileUMesh *>& meshes)
 {
   if(meshes.empty())
@@ -4618,7 +4756,7 @@ MCAuto<MEDFileUMesh> MEDFileUMesh::Aggregate(const std::vector<const MEDFileUMes
   std::size_t sz(meshes.size()),i(0);
   std::vector<const DataArrayDouble *> coos(sz);
   std::vector<const DataArrayInt *> fam_coos(sz),num_coos(sz);
-  for(std::vector<const MEDFileUMesh *>::const_iterator it=meshes.begin();it!=meshes.end();it++,i++)
+  for(auto it=meshes.begin();it!=meshes.end();it++,i++)
     {
       if(!(*it))
         throw INTERP_KERNEL::Exception("MEDFileUMesh::Aggregate : presence of NULL pointer in input vector !");
@@ -4632,29 +4770,123 @@ MCAuto<MEDFileUMesh> MEDFileUMesh::Aggregate(const std::vector<const MEDFileUMes
   std::map<int, std::vector<const DataArrayInt *> > m_fam,m_renum;
   std::map<int, std::vector< MCAuto< MEDCouplingUMesh > > > m_mesh2;
   std::map<int, std::vector<const MEDCouplingUMesh *> > m_mesh;
-  std::map<std::string,int> map1;
-  std::map<std::string, std::vector<std::string> > map2;
-  for(std::vector<const MEDFileUMesh *>::const_iterator it=meshes.begin();it!=meshes.end();it++,i++)
+  std::map<std::string,int> famNumMap;
+  std::map<int, std::string> famNumMap_rev;
+  std::map<std::string, std::vector<std::string> > grpFamMap;
+  std::set< MCAuto<DataArrayInt> > mem_cleanup;   // Memory clean-up. At set deletion (end of method), arrays will be deallocated.
+
+  // Identify min family number used:
+  int min_fam_num(0);
+  for(const auto& msh : meshes)
     {
-      if((*it)->getSpaceDimension()!=spaceDim)
+      const std::map<std::string,int>& locMap1(msh->getFamilyInfo());
+      for(const auto& it3 : locMap1)
+        if(it3.second < min_fam_num)
+          min_fam_num = it3.second;
+    }
+
+  for(const auto& msh : meshes)
+    {
+      if(msh->getSpaceDimension()!=spaceDim)
         throw INTERP_KERNEL::Exception("MEDFileUMesh::Aggregate : space dimension must be homogeneous !");
-      if((*it)->getMeshDimension()!=meshDim)
+      if(msh->getMeshDimension()!=meshDim)
         throw INTERP_KERNEL::Exception("MEDFileUMesh::Aggregate : mesh dimension must be homogeneous !");
-      if((*it)->getNonEmptyLevels()!=levs)
+      if(msh->getNonEmptyLevels()!=levs)
         throw INTERP_KERNEL::Exception("MEDFileUMesh::Aggregate : levels must be the same for elements in input vector !");
-      for(std::vector<int>::const_iterator it2=levs.begin();it2!=levs.end();it2++)
+
+      const std::map<std::string,int>& locMap1(msh->getFamilyInfo());
+      std::map<std::string, std::string> substitute;
+      std::map<int, int> substituteN;
+      bool fam_conflict(false);
+      for(const auto& it3 : locMap1)
+        {
+          const std::string& famName = it3.first;
+          int famNum = it3.second;
+          if (famNumMap_rev.find(famNum) != famNumMap_rev.end()) // Family number is already used!
+            {
+              // Is it used by a group of the current mesh or a group from a previous mesh?
+              // If not, this is OK (typically -1 familly).
+              bool used = false;
+              //    Current mesh
+              const std::map<std::string, std::vector<std::string> >& locMap2(msh->getGroupInfo());
+              for(const auto& it4 : locMap2)
+                {
+                  const auto& famLst = it4.second;
+                  if (std::find(famLst.begin(), famLst.end(), famName) != famLst.end())
+                    { used = true; break; }
+                }
+              //    Previous meshes ...
+              if (!used)
+                for(const auto& it4 : grpFamMap)
+                  {
+                    const auto& famLst = it4.second;
+                    if (std::find(famLst.begin(), famLst.end(), famName) != famLst.end())
+                      { used = true; break; }
+                  }
+
+              if(used)
+                { // Generate a new family name, and a new family number
+                  fam_conflict = true;
+                  std::ostringstream oss;
+                  oss << "Family_" << --min_fam_num;  // New ID
+                  std::string new_name(oss.str());
+                  substitute[famName] = new_name;
+                  substituteN[famNum] = min_fam_num;
+                  famNumMap[new_name] = min_fam_num;
+                  famNumMap_rev[min_fam_num] = new_name;
+                }
+            }
+          famNumMap[famName] = famNum;
+          famNumMap_rev[famNum] = famName;
+        }
+
+      for(const auto& level : levs)
+        {
+          MCAuto<MEDCouplingUMesh> locMesh(msh->getMeshAtLevel(level));
+          m_mesh[level].push_back(locMesh); m_mesh2[level].push_back(locMesh);
+          m_renum[level].push_back(msh->getNumberFieldAtLevel(level));
+
+          // Family field - substitute new family number if needed:
+          if(fam_conflict)
+            {
+              DataArrayInt* dai(msh->getFamilyFieldAtLevel(level)->deepCopy());  // Need a copy
+              mem_cleanup.insert(MCAuto<DataArrayInt>(dai));      // Make sure array will decrRef() at end of method
+              for (const auto& subN : substituteN)
+                dai->changeValue(subN.first, subN.second);
+              m_fam[level].push_back(dai);
+            }
+          else
+            m_fam[level].push_back(msh->getFamilyFieldAtLevel(level));      // No copy needed
+        }
+
+      const std::map<std::string, std::vector<std::string> >& locMap2(msh->getGroupInfo());
+      for(const auto& grpItem : locMap2)
         {
-          MCAuto<MEDCouplingUMesh> locMesh((*it)->getMeshAtLevel(*it2));
-          m_mesh[*it2].push_back(locMesh); m_mesh2[*it2].push_back(locMesh);
-          m_fam[*it2].push_back((*it)->getFamilyFieldAtLevel(*it2));
-          m_renum[*it2].push_back((*it)->getNumberFieldAtLevel(*it2));
+          const std::string& grpName = grpItem.first;
+          std::vector<std::string> famLst;
+          // Substitute family name in group description if needed:
+          if (fam_conflict)
+            {
+              famLst = grpItem.second;
+              for (const auto& sub : substitute)
+                std::replace(famLst.begin(), famLst.end(), sub.first, sub.second);
+            }
+          else
+            famLst = grpItem.second;
+
+          // Potentially merge groups (if same name):
+          const auto& it = grpFamMap.find(grpName);
+          if (it != grpFamMap.end())
+            {
+              // Group already exists, merge should be done. Normally we whould never
+              // have twice the same family name in famLstCur and famLst since we dealt with family number
+              // conflict just above ...
+              std::vector<std::string>& famLstCur = (*it).second;
+              famLstCur.insert(famLstCur.end(), famLst.begin(), famLst.end());
+            }
+          else
+            grpFamMap[grpName] = famLst;
         }
-      const std::map<std::string,int>& locMap1((*it)->getFamilyInfo());
-      for(std::map<std::string,int>::const_iterator it3=locMap1.begin();it3!=locMap1.end();it3++)
-        map1[(*it3).first]=(*it3).second;
-      const std::map<std::string, std::vector<std::string> >& locMap2((*it)->getGroupInfo());
-      for(std::map<std::string, std::vector<std::string> >::const_iterator it4=locMap2.begin();it4!=locMap2.end();it4++)
-        map2[(*it4).first]=(*it4).second;
     }
   // Easy part : nodes
   MCAuto<MEDFileUMesh> ret(MEDFileUMesh::New());
@@ -4671,40 +4903,40 @@ MCAuto<MEDFileUMesh> MEDFileUMesh::Aggregate(const std::vector<const MEDFileUMes
       ret->setRenumFieldArr(1,num_coo);
     }
   // cells
-  for(std::vector<int>::const_iterator it=levs.begin();it!=levs.end();it++)
+  for(const auto& level : levs)
     {
-      std::map<int, std::vector<const MEDCouplingUMesh *> >::const_iterator it2(m_mesh.find(*it));
+      auto it2(m_mesh.find(level));
       if(it2==m_mesh.end())
         throw INTERP_KERNEL::Exception("MEDFileUMesh::Aggregate : internal error 1 !");
       MCAuto<MEDCouplingUMesh> mesh(MEDCouplingUMesh::MergeUMeshes((*it2).second));
       mesh->setCoords(coo); mesh->setName(ref->getName());
       MCAuto<DataArrayInt> renum(mesh->sortCellsInMEDFileFrmt());
-      ret->setMeshAtLevel(*it,mesh);
-      std::map<int, std::vector<const DataArrayInt *> >::const_iterator it3(m_fam.find(*it)),it4(m_renum.find(*it));
-      if(it3!=m_fam.end())
+      ret->setMeshAtLevel(level,mesh);
+      auto it3(m_fam.find(level)),it4(m_renum.find(level));
+      if(it3==m_fam.end()) // Should never happen (all levels exist for all meshes)
+        throw INTERP_KERNEL::Exception("MEDFileUMesh::Aggregate : internal error 2!");
+      if(it4==m_renum.end())
+        throw INTERP_KERNEL::Exception("MEDFileUMesh::Aggregate : internal error 3!");
+      // Set new family field if it was defined for all input meshes
+      const std::vector<const DataArrayInt *>& fams((*it3).second);
+      if(std::find(fams.begin(),fams.end(),(const DataArrayInt *)0)==fams.end())
         {
-          const std::vector<const DataArrayInt *>& fams((*it3).second);
-          if(std::find(fams.begin(),fams.end(),(const DataArrayInt *)0)==fams.end())
-            {
-              MCAuto<DataArrayInt> famm(DataArrayInt::Aggregate(fams));
-              famm->renumberInPlace(renum->begin());
-              ret->setFamilyFieldArr(*it,famm);
-            }
+          MCAuto<DataArrayInt> famm(DataArrayInt::Aggregate(fams));
+          famm->renumberInPlace(renum->begin());
+          ret->setFamilyFieldArr(level,famm);
         }
-      if(it4!=m_renum.end())
+      // Set optional number field if defined for all input meshes:
+      const std::vector<const DataArrayInt *>& renums((*it4).second);
+      if(std::find(renums.begin(),renums.end(),(const DataArrayInt *)0)==renums.end())
         {
-          const std::vector<const DataArrayInt *>& renums((*it4).second);
-          if(std::find(renums.begin(),renums.end(),(const DataArrayInt *)0)==renums.end())
-            {
-              MCAuto<DataArrayInt> renumm(DataArrayInt::Aggregate(renums));
-              renumm->renumberInPlace(renum->begin());
-              ret->setRenumFieldArr(*it,renumm);
-            }
+          MCAuto<DataArrayInt> renumm(DataArrayInt::Aggregate(renums));
+          renumm->renumberInPlace(renum->begin());
+          ret->setRenumFieldArr(level,renumm);
         }
     }
   //
-  ret->setFamilyInfo(map1);
-  ret->setGroupInfo(map2);
+  ret->setFamilyInfo(famNumMap);
+  ret->setGroupInfo(grpFamMap);
   ret->setName(ref->getName());
   return ret;
 }
@@ -4874,7 +5106,7 @@ void MEDFileUMesh::addNodeGroup(const DataArrayInt *ids)
   if(!coords)
     throw INTERP_KERNEL::Exception("MEDFileUMesh::addNodeGroup : no coords set !");
   int nbOfNodes(coords->getNumberOfTuples());
-  if(!((DataArrayInt *)_fam_coords))
+  if(_fam_coords.isNull())
     { _fam_coords=DataArrayInt::New(); _fam_coords->alloc(nbOfNodes,1); _fam_coords->fillWithZero(); }
   //
   addGroupUnderground(true,ids,_fam_coords);
@@ -5005,7 +5237,7 @@ MCAuto<MEDFileUMeshSplitL1>& MEDFileUMesh::checkAndGiveEntryInSplitL1(int meshDi
  *
  * \param [in] ms - List of unstructured meshes lying on the same coordinates and having different mesh dimesnion.
  * \param [in] renum - the parameter (set to false by default) that tells the beheviour if there is a mesh on \a ms that is not geo type sorted.
- *                     If false, an exception ois thrown. If true the mesh is reordered automatically. It is highly recommanded to let this parameter to false.
+ *                     If false, an exception is thrown. If true the mesh is reordered automatically. It is highly recommended to let this parameter to false.
  *
  * \throw If \a there is a null pointer in \a ms.
  * \sa MEDFileUMesh::setMeshAtLevel
@@ -5782,7 +6014,7 @@ void MEDFileStructuredMesh::setNameFieldAtLevel(int meshDimRelToMaxExt, DataArra
       {
         int nbCells=mesh->getNumberOfCellsOfSubLevelMesh();
         nameArr->checkNbOfTuplesAndComp(nbCells,MED_SNAME_SIZE,"MEDFileStructuredMesh::setNameFieldAtLevel : Problem in size of names arr ! Mismatch with number of faces of mesh !");
-        _names_cells=nameArr;
+        _names_faces=nameArr;
       }
     default:
       throw INTERP_KERNEL::Exception("MEDFileStructuredMesh::setNameFieldAtLevel : Only available for levels 0 or 1 or -1 !");
@@ -6095,7 +6327,7 @@ void MEDFileStructuredMesh::deepCpyAttributes()
 }
 
 /*!
- * Returns a pointer to mesh at the specified level (here 0 is compulsary for cartesian mesh).
+ * Returns a pointer to mesh at the specified level (here 0 is compulsory for cartesian mesh).
  * 
  * \return a pointer to cartesian mesh that need to be managed by the caller.
  * \warning the returned pointer has to be managed by the caller.