diff --git a/IO/IOSS/vtkIOSSModel.cxx b/IO/IOSS/vtkIOSSModel.cxx
index 528a8a76f1122902665b23a0b02f3fbd38d81cad..fa1af080a8b0f084928a7e319a8c46545fba2b8a 100644
--- a/IO/IOSS/vtkIOSSModel.cxx
+++ b/IO/IOSS/vtkIOSSModel.cxx
@@ -153,7 +153,7 @@ bool HandleGlobalIds(vtkPartitionedDataSetCollection* pdc, int association,
 }
 
 //=============================================================================
-std::set<unsigned int> GetDatasetIndices(vtkDataAssembly* assembly, std::vector<std::string> paths)
+std::set<unsigned int> GetDatasetIndices(vtkDataAssembly* assembly, std::set<std::string> paths)
 {
   if (assembly && assembly->GetRootNodeName())
   {
@@ -246,7 +246,8 @@ std::map<unsigned char, int64_t> GetElementCounts(
     vtkSMPTools::For(0, ds->GetNumberOfCells(), [&](vtkIdType start, vtkIdType end) {
       for (vtkIdType cc = start; cc < end; ++cc)
       {
-        // memory_order_relaxed is safe here, since we're not using the atomics for synchronization.
+        // memory_order_relaxed is safe here, since we're not using the atomics for
+        // synchronization.
         elementCounts[ds->GetCellType(cc)].fetch_add(1, std::memory_order_relaxed);
       }
     });
@@ -411,7 +412,7 @@ struct PutFieldWorker
         array->GetTypedTuple((*this->SourceIds)[cc], tuple.data());
         for (size_t comp = 0; comp < this->Data.size(); ++comp)
         {
-          this->Data[comp][this->Offset + cc] = tuple[comp];
+          this->Data[comp][this->Offset + cc] = static_cast<T>(tuple[comp]);
         }
       }
     });
@@ -430,7 +431,7 @@ struct PutFieldWorker
         ds->GetPoint((*this->SourceIds)[cc], tuple.data());
         for (size_t comp = 0; comp < this->Data.size(); ++comp)
         {
-          this->Data[comp][this->Offset + cc] = tuple[comp];
+          this->Data[comp][this->Offset + cc] = static_cast<T>(tuple[comp]);
         }
       }
     });
@@ -482,7 +483,21 @@ struct vtkGroupingEntity
   {
   }
   virtual ~vtkGroupingEntity() = default;
-  virtual Ioss::EntityType GetEntityType() const = 0;
+  virtual Ioss::EntityType GetIOSSEntityType() const
+  {
+    try
+    {
+      return vtkIOSSUtilities::GetIOSSEntityType(this->GetEntityType());
+    }
+    catch (std::runtime_error&)
+    {
+      return Ioss::EntityType::INVALID_TYPE;
+    }
+  }
+  virtual vtkIOSSWriter::EntityType GetEntityType() const
+  {
+    return vtkIOSSWriter::EntityType::NUMBER_OF_ENTITY_TYPES;
+  }
   virtual void DefineModel(Ioss::Region& region) const = 0;
   virtual void Model(Ioss::Region& region) const = 0;
   virtual void DefineTransient(Ioss::Region& region) const = 0;
@@ -636,11 +651,14 @@ struct vtkNodeBlock : vtkGroupingEntity
     }
 
     assert(this->DataSets.size() == this->IdsRaw.size());
-    this->Fields = ::GetFields(vtkDataObject::POINT, writer->GetNodeBlockFieldSelection(),
+    this->Fields = ::GetFields(vtkDataObject::POINT, writer->GetChooseFieldsToWrite(),
       writer->GetNodeBlockFieldSelection(), pdc, controller);
   }
 
-  Ioss::EntityType GetEntityType() const override { return Ioss::EntityType::NODEBLOCK; }
+  vtkIOSSWriter::EntityType GetEntityType() const override
+  {
+    return vtkIOSSWriter::EntityType::NODEBLOCK;
+  }
 
   void AppendMD5(vtksysMD5* md5) const override
   {
@@ -727,27 +745,27 @@ struct vtkNodeBlock : vtkGroupingEntity
 };
 
 /**
- * Builds a Ioss::ElementBlock from a vtkPartitionedDataSet. The differences
+ * Builds an Ioss::(*)Block from a vtkPartitionedDataSet. The differences
  * between the Ioss and VTK data model for the two are handled as follows:
  *
  * * We only support vtkPartitionedDataSet comprising of one or more vtkDataSets.
  *   All other dataset types are simply ignored.
  *
- * * An ElementBlock cannot have multiple "pieces" in the same file. So if a
+ * * A Block cannot have multiple "pieces" in the same file. So if a
  *   vtkPartitionedDataSet has multiple datasets, we need to "combine" them into
  *   one.
  *
- * * An ElementBlock cannot have elements of different types. However,
+ * * A Block cannot have elements of different types. However,
  *   vtkDataSet supports heterogeneous cells. So if all
  *   vtkDataSets in the vtkPartitionedDataSet have more than 1 cell type,
- *   we create multiple element blocks. Each ElementBlock is uniquely named by
+ *   we create multiple blocks. Each Block is uniquely named by
  *   using the given block name and the element type as a suffix.
  *
  *   In MPI world, the cell types are gathered across all ranks to ensure each
  *   ranks creates identical blocks / block names.
  *
  */
-struct vtkElementBlock : public vtkGroupingEntity
+struct vtkEntityBlock : public vtkGroupingEntity
 {
   const std::vector<vtkDataSet*> DataSets;
   std::string RootName;
@@ -757,8 +775,9 @@ struct vtkElementBlock : public vtkGroupingEntity
   std::map<unsigned char, int64_t> ElementCounts;
   std::vector<std::tuple<std::string, Ioss::Field::BasicType, int>> Fields;
 
-  vtkElementBlock(vtkPartitionedDataSet* pds, const std::string& name, const int blockId,
-    int startSplitElementBlockId, vtkMultiProcessController* controller, vtkIOSSWriter* writer)
+  vtkEntityBlock(vtkPartitionedDataSet* pds, vtkIOSSWriter::EntityType entityType,
+    const std::string& name, const int blockId, int startSplitElementBlockId,
+    vtkMultiProcessController* controller, vtkIOSSWriter* writer)
     : vtkGroupingEntity(writer)
     , DataSets(vtkCompositeDataSet::GetDataSets<vtkDataSet>(pds))
     , RootName(name)
@@ -776,12 +795,10 @@ struct vtkElementBlock : public vtkGroupingEntity
     }
 
     this->ElementCounts = ::GetElementCounts(pds, controller);
-    this->Fields = ::GetFields(vtkDataObject::CELL, writer->GetChooseElementBlockFieldsToWrite(),
-      writer->GetElementBlockFieldSelection(), pds, controller);
+    this->Fields = ::GetFields(vtkDataObject::CELL, writer->GetChooseFieldsToWrite(),
+      writer->GetFieldSelection(entityType), pds, controller);
   }
 
-  Ioss::EntityType GetEntityType() const override { return Ioss::EntityType::ELEMENTBLOCK; }
-
   void AppendMD5(vtksysMD5* md5) const override
   {
     vtksysMD5_Append(md5, reinterpret_cast<const unsigned char*>(this->RootName.c_str()), -1);
@@ -814,6 +831,14 @@ struct vtkElementBlock : public vtkGroupingEntity
     }
   }
 
+  virtual Ioss::EntityBlock* CreateEntity(Ioss::DatabaseIO* db, const std::string& blockName,
+    const std::string& elementType, int64_t elementCount) const = 0;
+
+  virtual void AddEntity(Ioss::Region& region, Ioss::EntityBlock* entityBlock) const = 0;
+
+  virtual Ioss::EntityBlock* GetEntity(
+    Ioss::Region& region, const std::string& blockName) const = 0;
+
   void DefineModel(Ioss::Region& region) const override
   {
     for (const auto& element : this->ElementCounts)
@@ -825,15 +850,15 @@ struct vtkElementBlock : public vtkGroupingEntity
       const auto& elementType = elementTopology->name();
       const auto blockInfo = this->GetSubElementBlockInfo(vtk_cell_type, elementType);
 
-      auto* elementBlock =
-        new Ioss::ElementBlock(region.get_database(), blockInfo.second, elementType, elementCount);
-      elementBlock->property_add(Ioss::Property("id", blockInfo.first));
+      auto entityBlock =
+        this->CreateEntity(region.get_database(), blockInfo.second, elementType, elementCount);
+      entityBlock->property_add(Ioss::Property("id", blockInfo.first));
       if (this->Writer->GetPreserveOriginalIds())
       {
-        elementBlock->property_add(
+        entityBlock->property_add(
           Ioss::Property("original_id", this->BlockId, Ioss::Property::ATTRIBUTE));
       }
-      region.add(elementBlock);
+      this->AddEntity(region, entityBlock);
     }
   }
 
@@ -848,8 +873,8 @@ struct vtkElementBlock : public vtkGroupingEntity
       const auto& elementType = elementTopology->name();
       const auto blockName = this->GetSubElementBlockInfo(vtk_cell_type, elementType).second;
 
-      auto* elementBlock = region.get_element_block(blockName);
-      this->DefineFields(elementBlock, this->Fields, Ioss::Field::TRANSIENT, elementCount);
+      auto* entityBlock = this->GetEntity(region, blockName);
+      this->DefineFields(entityBlock, this->Fields, Ioss::Field::TRANSIENT, elementCount);
     }
   }
 
@@ -865,7 +890,7 @@ struct vtkElementBlock : public vtkGroupingEntity
       const int nodeCount = elementTopology->number_nodes();
       const auto blockName = this->GetSubElementBlockInfo(vtk_cell_type, elementType).second;
 
-      auto* elementBlock = region.get_element_block(blockName);
+      auto* entityBlock = this->GetEntity(region, blockName);
 
       // populate ids.
       std::vector<int32_t> elementIds; // these are global ids.
@@ -903,8 +928,8 @@ struct vtkElementBlock : public vtkGroupingEntity
       }
       assert(elementIds.size() == static_cast<size_t>(elementCount));
       assert(connectivity.size() == static_cast<size_t>(elementCount * nodeCount));
-      elementBlock->put_field_data("ids", elementIds);
-      elementBlock->put_field_data("connectivity", connectivity);
+      entityBlock->put_field_data("ids", elementIds);
+      entityBlock->put_field_data("connectivity", connectivity);
     }
   }
 
@@ -918,7 +943,7 @@ struct vtkElementBlock : public vtkGroupingEntity
       const auto& elementType = elementTopology->name();
       const auto blockName = this->GetSubElementBlockInfo(vtk_cell_type, elementType).second;
 
-      auto* elementBlock = region.get_element_block(blockName);
+      auto* entityBlock = this->GetEntity(region, blockName);
 
       // populate ids.
       std::vector<std::vector<vtkIdType>> lIds; // these are local ids.
@@ -939,11 +964,108 @@ struct vtkElementBlock : public vtkGroupingEntity
       }
 
       // add fields.
-      this->PutFields(elementBlock, this->Fields, lIds, this->DataSets, vtkDataObject::CELL);
+      this->PutFields(entityBlock, this->Fields, lIds, this->DataSets, vtkDataObject::CELL);
     }
   }
 };
 
+//=============================================================================
+struct vtkEdgeBlock : public vtkEntityBlock
+{
+  vtkEdgeBlock(vtkPartitionedDataSet* pds, const std::string& name, const int blockId,
+    int startSplitElementBlockId, vtkMultiProcessController* controller, vtkIOSSWriter* writer)
+    : vtkEntityBlock(
+        pds, this->GetEntityType(), name, blockId, startSplitElementBlockId, controller, writer)
+  {
+  }
+
+  vtkIOSSWriter::EntityType GetEntityType() const override
+  {
+    return vtkIOSSWriter::EntityType::EDGEBLOCK;
+  }
+
+  Ioss::EntityBlock* CreateEntity(Ioss::DatabaseIO* db, const std::string& blockName,
+    const std::string& elementType, const int64_t elementCount) const override
+  {
+    return new Ioss::EdgeBlock(db, blockName, elementType, elementCount);
+  }
+
+  void AddEntity(Ioss::Region& region, Ioss::EntityBlock* entityBlock) const override
+  {
+    region.add(dynamic_cast<Ioss::EdgeBlock*>(entityBlock));
+  }
+
+  Ioss::EntityBlock* GetEntity(Ioss::Region& region, const std::string& blockName) const override
+  {
+    return region.get_edge_block(blockName);
+  }
+};
+
+//=============================================================================
+struct vtkFaceBlock : public vtkEntityBlock
+{
+  vtkFaceBlock(vtkPartitionedDataSet* pds, const std::string& name, const int blockId,
+    int startSplitElementBlockId, vtkMultiProcessController* controller, vtkIOSSWriter* writer)
+    : vtkEntityBlock(
+        pds, this->GetEntityType(), name, blockId, startSplitElementBlockId, controller, writer)
+  {
+  }
+
+  vtkIOSSWriter::EntityType GetEntityType() const override
+  {
+    return vtkIOSSWriter::EntityType::FACEBLOCK;
+  }
+
+  Ioss::EntityBlock* CreateEntity(Ioss::DatabaseIO* db, const std::string& blockName,
+    const std::string& elementType, const int64_t elementCount) const override
+  {
+    return new Ioss::FaceBlock(db, blockName, elementType, elementCount);
+  }
+
+  void AddEntity(Ioss::Region& region, Ioss::EntityBlock* entityBlock) const override
+  {
+    region.add(dynamic_cast<Ioss::FaceBlock*>(entityBlock));
+  }
+
+  Ioss::EntityBlock* GetEntity(Ioss::Region& region, const std::string& blockName) const override
+  {
+    return region.get_face_block(blockName);
+  }
+};
+
+//=============================================================================
+struct vtkElementBlock : public vtkEntityBlock
+{
+  vtkElementBlock(vtkPartitionedDataSet* pds, const std::string& name, const int blockId,
+    int startSplitElementBlockId, vtkMultiProcessController* controller, vtkIOSSWriter* writer)
+    : vtkEntityBlock(
+        pds, this->GetEntityType(), name, blockId, startSplitElementBlockId, controller, writer)
+  {
+  }
+
+  vtkIOSSWriter::EntityType GetEntityType() const override
+  {
+    return vtkIOSSWriter::EntityType::ELEMENTBLOCK;
+  }
+
+  Ioss::EntityBlock* CreateEntity(Ioss::DatabaseIO* db, const std::string& blockName,
+    const std::string& elementType, const int64_t elementCount) const override
+  {
+    return new Ioss::ElementBlock(db, blockName, elementType, elementCount);
+  }
+
+  void AddEntity(Ioss::Region& region, Ioss::EntityBlock* entityBlock) const override
+  {
+    region.add(dynamic_cast<Ioss::ElementBlock*>(entityBlock));
+  }
+
+  Ioss::EntityBlock* GetEntity(Ioss::Region& region, const std::string& blockName) const override
+  {
+    return region.get_element_block(blockName);
+  }
+};
+
+//=============================================================================
 struct vtkNodeSet : public vtkGroupingEntity
 {
   const std::vector<vtkDataSet*> DataSets;
@@ -974,11 +1096,14 @@ struct vtkNodeSet : public vtkGroupingEntity
     }
 
     // in a nodeSet, number of points == number of cells, because cells are vertices
-    this->Fields = ::GetFields(vtkDataObject::CELL, writer->GetChooseNodeSetFieldsToWrite(),
+    this->Fields = ::GetFields(vtkDataObject::CELL, writer->GetChooseFieldsToWrite(),
       writer->GetNodeSetFieldSelection(), pds, controller);
   }
 
-  Ioss::EntityType GetEntityType() const override { return Ioss::EntityType::NODESET; }
+  vtkIOSSWriter::EntityType GetEntityType() const override
+  {
+    return vtkIOSSWriter::EntityType::NODESET;
+  }
 
   void AppendMD5(vtksysMD5* md5) const override
   {
@@ -1037,7 +1162,8 @@ struct vtkNodeSet : public vtkGroupingEntity
   }
 };
 
-struct vtkSideSet : public vtkGroupingEntity
+//=============================================================================
+struct vtkEntitySet : public vtkGroupingEntity
 {
   const std::vector<vtkDataSet*> DataSets;
   std::string Name;
@@ -1046,8 +1172,9 @@ struct vtkSideSet : public vtkGroupingEntity
 
   std::vector<std::tuple<std::string, Ioss::Field::BasicType, int>> Fields;
 
-  vtkSideSet(vtkPartitionedDataSet* pds, const std::string& name, int blockId,
-    vtkMultiProcessController* controller, vtkIOSSWriter* writer)
+  vtkEntitySet(vtkPartitionedDataSet* pds, vtkIOSSWriter::EntityType entityType,
+    const std::string& name, int blockId, vtkMultiProcessController* controller,
+    vtkIOSSWriter* writer)
     : vtkGroupingEntity(writer)
     , DataSets(vtkCompositeDataSet::GetDataSets<vtkDataSet>(pds))
     , Name(name)
@@ -1056,25 +1183,18 @@ struct vtkSideSet : public vtkGroupingEntity
   {
     for (auto& ds : this->DataSets)
     {
-      auto* cd = ds->GetCellData();
-      if (vtkIdTypeArray::SafeDownCast(cd->GetGlobalIds()) != nullptr)
-      {
-        throw std::runtime_error("cell global ids present, must not be a sideset.");
-      }
-
-      if (vtkIntArray::SafeDownCast(cd->GetArray("element_side")) == nullptr)
+      // no need to check for global ids
+      if (vtkIntArray::SafeDownCast(ds->GetCellData()->GetArray("element_side")) == nullptr)
       {
         throw std::runtime_error("missing 'element_side' cell array.");
       }
 
       this->Count += ds->GetNumberOfCells();
     }
-    this->Fields = ::GetFields(vtkDataObject::CELL, writer->GetChooseSideSetFieldsToWrite(),
-      writer->GetSideSetFieldSelection(), pds, controller);
+    this->Fields = ::GetFields(vtkDataObject::CELL, writer->GetChooseFieldsToWrite(),
+      writer->GetFieldSelection(entityType), pds, controller);
   }
 
-  Ioss::EntityType GetEntityType() const override { return Ioss::EntityType::SIDESET; }
-
   void AppendMD5(vtksysMD5* md5) const override
   {
     vtksysMD5_Append(md5, reinterpret_cast<const unsigned char*>(this->Name.c_str()), -1);
@@ -1082,31 +1202,30 @@ struct vtkSideSet : public vtkGroupingEntity
       static_cast<int>(sizeof(this->Count)));
   }
 
+  virtual Ioss::GroupingEntity* CreateEntity(
+    Ioss::DatabaseIO* db, const std::string& blockName, int64_t elementCount) const = 0;
+
+  virtual void AddEntity(Ioss::Region& region, Ioss::GroupingEntity* entitySet) const = 0;
+
+  virtual Ioss::GroupingEntity* GetEntity(
+    Ioss::Region& region, const std::string& blockName) const = 0;
+
   void DefineModel(Ioss::Region& region) const override
   {
-    // for mixed topology blocks, IOSS uses "unknown"
-    const auto* mixed_topo = Ioss::ElementTopology::factory("unknown");
-    const auto& elementType = mixed_topo->name();
-    auto* sideBlock = new Ioss::SideBlock(
-      region.get_database(), "sideblock_0", elementType, elementType, this->Count);
-
-    auto* sideSet = new Ioss::SideSet(region.get_database(), this->Name);
-    sideSet->property_add(Ioss::Property("id", this->BlockId));
-    sideSet->add(sideBlock);
-    region.add(sideSet);
+    auto entitySet = this->CreateEntity(region.get_database(), this->Name, this->Count);
+    entitySet->property_add(Ioss::Property("id", this->BlockId));
+    this->AddEntity(region, entitySet);
   }
 
   void DefineTransient(Ioss::Region& region) const override
   {
-    auto* sideSet = region.get_sideset(this->Name);
-    auto* sideBlock = sideSet->get_side_block("sideblock_0");
-    this->DefineFields(sideBlock, this->Fields, Ioss::Field::TRANSIENT, this->Count);
+    auto entity = this->GetEntity(region, this->Name);
+    this->DefineFields(entity, this->Fields, Ioss::Field::TRANSIENT, this->Count);
   }
 
   void Model(Ioss::Region& region) const override
   {
-    auto* sideSet = region.get_sideset(this->Name);
-    auto* sideBlock = sideSet->get_side_block("sideblock_0");
+    auto entity = this->GetEntity(region, this->Name);
 
     std::vector<int32_t> elementSide;
     elementSide.reserve(this->Count * 2);
@@ -1135,13 +1254,12 @@ struct vtkSideSet : public vtkGroupingEntity
     }
 
     assert(elementSide.size() == static_cast<size_t>(this->Count * 2));
-    sideBlock->put_field_data("element_side", elementSide);
+    entity->put_field_data("element_side", elementSide);
   }
 
   void Transient(Ioss::Region& region) const override
   {
-    auto* sideSet = region.get_sideset(this->Name);
-    auto* sideBlock = sideSet->get_side_block("sideblock_0");
+    auto entity = this->GetEntity(region, this->Name);
 
     // populate ids.
     std::vector<std::vector<vtkIdType>> lIds; // these are local ids.
@@ -1162,7 +1280,138 @@ struct vtkSideSet : public vtkGroupingEntity
     }
 
     // add fields.
-    this->PutFields(sideBlock, this->Fields, lIds, this->DataSets, vtkDataObject::CELL);
+    this->PutFields(entity, this->Fields, lIds, this->DataSets, vtkDataObject::CELL);
+  }
+};
+
+//=============================================================================
+struct vtkEdgeSet : public vtkEntitySet
+{
+  vtkEdgeSet(vtkPartitionedDataSet* pds, const std::string& name, int blockId,
+    vtkMultiProcessController* controller, vtkIOSSWriter* writer)
+    : vtkEntitySet(pds, this->GetEntityType(), name, blockId, controller, writer)
+  {
+  }
+
+  vtkIOSSWriter::EntityType GetEntityType() const override
+  {
+    return vtkIOSSWriter::EntityType::EDGESET;
+  }
+
+  Ioss::GroupingEntity* CreateEntity(
+    Ioss::DatabaseIO* db, const std::string& blockName, const int64_t elementCount) const override
+  {
+    return new Ioss::EdgeSet(db, blockName, elementCount);
+  }
+
+  void AddEntity(Ioss::Region& region, Ioss::GroupingEntity* entitySet) const override
+  {
+    region.add(dynamic_cast<Ioss::EdgeSet*>(entitySet));
+  }
+
+  Ioss::GroupingEntity* GetEntity(Ioss::Region& region, const std::string& blockName) const override
+  {
+    return region.get_edgeset(blockName);
+  }
+};
+
+//=============================================================================
+struct vtkFaceSet : public vtkEntitySet
+{
+  vtkFaceSet(vtkPartitionedDataSet* pds, const std::string& name, int blockId,
+    vtkMultiProcessController* controller, vtkIOSSWriter* writer)
+    : vtkEntitySet(pds, this->GetEntityType(), name, blockId, controller, writer)
+  {
+  }
+
+  vtkIOSSWriter::EntityType GetEntityType() const override
+  {
+    return vtkIOSSWriter::EntityType::FACESET;
+  }
+
+  Ioss::GroupingEntity* CreateEntity(
+    Ioss::DatabaseIO* db, const std::string& blockName, const int64_t elementCount) const override
+  {
+    return new Ioss::FaceSet(db, blockName, elementCount);
+  }
+
+  void AddEntity(Ioss::Region& region, Ioss::GroupingEntity* entitySet) const override
+  {
+    region.add(dynamic_cast<Ioss::FaceSet*>(entitySet));
+  }
+
+  Ioss::GroupingEntity* GetEntity(Ioss::Region& region, const std::string& blockName) const override
+  {
+    return region.get_faceset(blockName);
+  }
+};
+
+//=============================================================================
+struct vtkElementSet : public vtkEntitySet
+{
+  vtkElementSet(vtkPartitionedDataSet* pds, const std::string& name, int blockId,
+    vtkMultiProcessController* controller, vtkIOSSWriter* writer)
+    : vtkEntitySet(pds, this->GetEntityType(), name, blockId, controller, writer)
+  {
+  }
+
+  vtkIOSSWriter::EntityType GetEntityType() const override
+  {
+    return vtkIOSSWriter::EntityType::ELEMENTSET;
+  }
+
+  Ioss::GroupingEntity* CreateEntity(
+    Ioss::DatabaseIO* db, const std::string& blockName, const int64_t elementCount) const override
+  {
+    return new Ioss::ElementSet(db, blockName, elementCount);
+  }
+
+  void AddEntity(Ioss::Region& region, Ioss::GroupingEntity* entitySet) const override
+  {
+    region.add(dynamic_cast<Ioss::ElementSet*>(entitySet));
+  }
+
+  Ioss::GroupingEntity* GetEntity(Ioss::Region& region, const std::string& blockName) const override
+  {
+    return region.get_elementset(blockName);
+  }
+};
+
+//=============================================================================
+struct vtkSideSet : public vtkEntitySet
+{
+  vtkSideSet(vtkPartitionedDataSet* pds, const std::string& name, int blockId,
+    vtkMultiProcessController* controller, vtkIOSSWriter* writer)
+    : vtkEntitySet(pds, this->GetEntityType(), name, blockId, controller, writer)
+  {
+  }
+
+  vtkIOSSWriter::EntityType GetEntityType() const override
+  {
+    return vtkIOSSWriter::EntityType::SIDESET;
+  }
+
+  Ioss::GroupingEntity* CreateEntity(
+    Ioss::DatabaseIO* db, const std::string& blockName, const int64_t elementCount) const override
+  {
+    // for mixed topology blocks, IOSS uses "unknown"
+    const auto* mixed_topo = Ioss::ElementTopology::factory("unknown");
+    const auto& elementType = mixed_topo->name();
+    auto* sideBlock =
+      new Ioss::SideBlock(db, "sideblock_0", elementType, elementType, elementCount);
+    auto* sideSet = new Ioss::SideSet(db, blockName);
+    sideSet->add(sideBlock);
+    return sideSet;
+  }
+
+  void AddEntity(Ioss::Region& region, Ioss::GroupingEntity* entitySet) const override
+  {
+    region.add(dynamic_cast<Ioss::SideSet*>(entitySet));
+  }
+
+  Ioss::GroupingEntity* GetEntity(Ioss::Region& region, const std::string& blockName) const override
+  {
+    return region.get_sideset(blockName)->get_side_block("sideblock_0");
   }
 };
 
@@ -1209,26 +1458,18 @@ vtkIOSSModel::vtkIOSSModel(vtkPartitionedDataSetCollection* pdc, vtkIOSSWriter*
       return;
     }
   }
-  std::vector<std::string> elementBlockSelectors(writer->GetNumberOfElementBlockSelectors());
-  for (size_t idx = 0; idx < elementBlockSelectors.size(); ++idx)
-  {
-    elementBlockSelectors[idx] = writer->GetElementBlockSelector(idx);
-  }
-  std::vector<std::string> nodeSetSelectors(writer->GetNumberOfNodeSetSelectors());
-  for (size_t idx = 0; idx < nodeSetSelectors.size(); ++idx)
-  {
-    nodeSetSelectors[idx] = writer->GetNodeSetSelector(idx);
-  }
-  std::vector<std::string> sideSetSelectors(writer->GetNumberOfSideSetSelectors());
-  for (size_t idx = 0; idx < sideSetSelectors.size(); ++idx)
+  using EntityType = vtkIOSSWriter::EntityType;
+  std::map<EntityType, std::set<unsigned int>> entityIndices;
+  for (int i = EntityType::EDGEBLOCK; i < EntityType::NUMBER_OF_ENTITY_TYPES; ++i)
   {
-    sideSetSelectors[idx] = writer->GetSideSetSelector(idx);
+    const auto entityType = static_cast<EntityType>(i);
+    entityIndices[entityType] = ::GetDatasetIndices(assembly, writer->GetSelectors(entityType));
   }
-  auto elementBlockIndices = ::GetDatasetIndices(assembly, elementBlockSelectors);
-  auto nodeSetIndices = ::GetDatasetIndices(assembly, nodeSetSelectors);
-  auto sideSetIndices = ::GetDatasetIndices(assembly, sideSetSelectors);
-  bool indicesEmpty =
-    elementBlockIndices.empty() && nodeSetIndices.empty() && sideSetIndices.empty();
+  // write the above for loop with one line
+  const bool indicesEmpty = std::all_of(entityIndices.begin(), entityIndices.end(),
+    [](const std::pair<EntityType, std::set<unsigned int>>& indices) {
+      return indices.second.empty();
+    });
   if (indicesEmpty)
   {
     // if no indices are specified, then all blocks will be processed as element blocks
@@ -1238,18 +1479,28 @@ vtkIOSSModel::vtkIOSSModel(vtkPartitionedDataSetCollection* pdc, vtkIOSSWriter*
       strcmp(dataAssembly->GetRootNodeName(), "IOSS") == 0);
     if (isIOSS)
     {
-      elementBlockIndices = ::GetDatasetIndices(dataAssembly, { "/IOSS/element_blocks" });
-      nodeSetIndices = ::GetDatasetIndices(dataAssembly, { "/IOSS/node_sets" });
-      sideSetIndices = ::GetDatasetIndices(dataAssembly, { "/IOSS/side_sets" });
-      indicesEmpty =
-        elementBlockIndices.empty() && nodeSetIndices.empty() && sideSetIndices.empty();
+      for (int i = EntityType::EDGEBLOCK; i < EntityType::NUMBER_OF_ENTITY_TYPES; ++i)
+      {
+        const auto entityType = static_cast<EntityType>(i);
+        const auto iossEntitySelector =
+          std::string("/IOSS/") + vtkIOSSReader::GetDataAssemblyNodeNameForEntityType(i);
+        entityIndices[entityType] = ::GetDatasetIndices(dataAssembly, { iossEntitySelector });
+      }
+    }
+    else
+    {
+      // all blocks are element blocks
+      entityIndices[EntityType::ELEMENTBLOCK] = ::GetDatasetIndices(assembly, { "/" });
     }
   }
 
-  // merge node sets and side sets indices into a single set of indices.
+  // merge sets indices into a single set of indices.
   std::set<unsigned int> indicesToIgnore;
-  indicesToIgnore.insert(nodeSetIndices.begin(), nodeSetIndices.end());
-  indicesToIgnore.insert(sideSetIndices.begin(), sideSetIndices.end());
+  for (int i = EntityType::SET_START; i < EntityType::SET_END; ++i)
+  {
+    const auto entityType = static_cast<EntityType>(i);
+    indicesToIgnore.insert(entityIndices[entityType].begin(), entityIndices[entityType].end());
+  }
 
   internals.GlobalIdsCreated = false;
   // create global point ids if needed
@@ -1278,27 +1529,24 @@ vtkIOSSModel::vtkIOSSModel(vtkPartitionedDataSetCollection* pdc, vtkIOSSWriter*
       }
     }
   }
-  // this will be used as a start id for split element blocks to ensure uniqueness.
-  int startSplitElementBlockId = *std::max_element(blockIds.begin(), blockIds.end()) + 1;
-  // ensure that all processes have the same startSplitElementBlockId.
+  // this will be used as a start id for split blocks to ensure uniqueness.
+  int startSplitEBlockId = *std::max_element(blockIds.begin(), blockIds.end()) + 1;
+  // ensure that all processes have the same startSplitEBlockId.
   if (controller && controller->GetNumberOfProcesses() > 1)
   {
-    int globalStartSplitElementBlockId;
+    int globalStartSplitBlockId;
     controller->AllReduce(
-      &startSplitElementBlockId, &globalStartSplitElementBlockId, 1, vtkCommunicator::MAX_OP);
-    startSplitElementBlockId = globalStartSplitElementBlockId;
+      &startSplitEBlockId, &globalStartSplitBlockId, 1, vtkCommunicator::MAX_OP);
+    startSplitEBlockId = globalStartSplitBlockId;
   }
 
-  // first things first, determine all information necessary about nodes.
+  // first things first, determine all information necessary about node block.
   // there's just 1 node block for exodus, build that.
   auto nodeBlock = std::make_shared<vtkNodeBlock>(dataset, "nodeblock_1", controller, writer);
-  entityGroups.emplace(nodeBlock->GetEntityType(), nodeBlock);
+  entityGroups.emplace(nodeBlock->GetIOSSEntityType(), nodeBlock);
 
-  // process element blocks.
-  // now, if input is not coming for IOSS reader, then all blocks are simply
-  // treated as element blocks, there's no way for us to deduce otherwise.
-  // let's start by simply doing that.
-  int elementBlockCounter = 0;
+  // process group entities.
+  int blockCounter = 0;
   for (unsigned int pidx = 0; pidx < dataset->GetNumberOfPartitionedDataSets(); ++pidx)
   {
     const std::string& blockName = blockNames[pidx];
@@ -1307,20 +1555,21 @@ vtkIOSSModel::vtkIOSSModel(vtkPartitionedDataSetCollection* pdc, vtkIOSSWriter*
 
     // now create each type of GroupingEntity.
 
-    // if indices are empty, all blocks will be processed as element blocks.
-    if (indicesEmpty || elementBlockIndices.find(pidx) != elementBlockIndices.end())
+    // edge block
+    const bool edgeBlockFound =
+      entityIndices[EntityType::EDGEBLOCK].find(pidx) != entityIndices[EntityType::EDGEBLOCK].end();
+    if (edgeBlockFound)
     {
       try
       {
-        if (elementBlockCounter != 0)
+        if (blockCounter++ != 0)
         {
           // add the number of cell types to the block id to ensure uniqueness.
-          startSplitElementBlockId += VTK_NUMBER_OF_CELL_TYPES;
+          startSplitEBlockId += VTK_NUMBER_OF_CELL_TYPES;
         }
-        auto elementBlock = std::make_shared<vtkElementBlock>(
-          pds, blockName, blockId, startSplitElementBlockId, controller, writer);
-        entityGroups.emplace(elementBlock->GetEntityType(), elementBlock);
-        ++elementBlockCounter;
+        auto edgeBlock = std::make_shared<vtkEdgeBlock>(
+          pds, blockName, blockId, startSplitEBlockId, controller, writer);
+        entityGroups.emplace(edgeBlock->GetIOSSEntityType(), edgeBlock);
         continue;
       }
       catch (std::runtime_error&)
@@ -1329,12 +1578,44 @@ vtkIOSSModel::vtkIOSSModel(vtkPartitionedDataSetCollection* pdc, vtkIOSSWriter*
       }
     }
 
-    if (sideSetIndices.find(pidx) != sideSetIndices.end())
+    // face block
+    const bool faceBlockFound =
+      entityIndices[EntityType::FACEBLOCK].find(pidx) != entityIndices[EntityType::FACEBLOCK].end();
+    if (faceBlockFound)
     {
       try
       {
-        auto sideSet = std::make_shared<vtkSideSet>(pds, blockName, blockId, controller, writer);
-        entityGroups.emplace(sideSet->GetEntityType(), sideSet);
+        if (blockCounter++ != 0)
+        {
+          // add the number of cell types to the block id to ensure uniqueness.
+          startSplitEBlockId += VTK_NUMBER_OF_CELL_TYPES;
+        }
+        auto faceBlock = std::make_shared<vtkFaceBlock>(
+          pds, blockName, blockId, startSplitEBlockId, controller, writer);
+        entityGroups.emplace(faceBlock->GetIOSSEntityType(), faceBlock);
+        continue;
+      }
+      catch (std::runtime_error&)
+      {
+        break;
+      }
+    }
+
+    // element block
+    const bool elementBlockFound = entityIndices[EntityType::ELEMENTBLOCK].find(pidx) !=
+      entityIndices[EntityType::ELEMENTBLOCK].end();
+    if (elementBlockFound)
+    {
+      try
+      {
+        if (blockCounter++ != 0)
+        {
+          // add the number of cell types to the block id to ensure uniqueness.
+          startSplitEBlockId += VTK_NUMBER_OF_CELL_TYPES;
+        }
+        auto elementBlock = std::make_shared<vtkElementBlock>(
+          pds, blockName, blockId, startSplitEBlockId, controller, writer);
+        entityGroups.emplace(elementBlock->GetIOSSEntityType(), elementBlock);
         continue;
       }
       catch (std::runtime_error&)
@@ -1343,13 +1624,84 @@ vtkIOSSModel::vtkIOSSModel(vtkPartitionedDataSetCollection* pdc, vtkIOSSWriter*
       }
     }
 
-    // nodesets are the most forgiving kind, so let's do them last.
-    if (nodeSetIndices.find(pidx) != nodeSetIndices.end())
+    // node set
+    const bool nodeSetFound =
+      entityIndices[EntityType::NODESET].find(pidx) != entityIndices[EntityType::NODESET].end();
+    if (nodeSetFound)
     {
       try
       {
         auto nodeSet = std::make_shared<vtkNodeSet>(pds, blockName, blockId, controller, writer);
-        entityGroups.emplace(nodeSet->GetEntityType(), nodeSet);
+        entityGroups.emplace(nodeSet->GetIOSSEntityType(), nodeSet);
+        continue;
+      }
+      catch (std::runtime_error&)
+      {
+        break;
+      }
+    }
+
+    // edge set
+    const bool edgeSetFound =
+      entityIndices[EntityType::EDGESET].find(pidx) != entityIndices[EntityType::EDGESET].end();
+    if (edgeSetFound)
+    {
+      try
+      {
+        auto edgeSet = std::make_shared<vtkEdgeSet>(pds, blockName, blockId, controller, writer);
+        entityGroups.emplace(edgeSet->GetIOSSEntityType(), edgeSet);
+        continue;
+      }
+      catch (std::runtime_error&)
+      {
+        break;
+      }
+    }
+
+    // face set
+    const bool faceSetFound =
+      entityIndices[EntityType::FACESET].find(pidx) != entityIndices[EntityType::FACESET].end();
+    if (faceSetFound)
+    {
+      try
+      {
+        auto faceSet = std::make_shared<vtkFaceSet>(pds, blockName, blockId, controller, writer);
+        entityGroups.emplace(faceSet->GetIOSSEntityType(), faceSet);
+        continue;
+      }
+      catch (std::runtime_error&)
+      {
+        break;
+      }
+    }
+
+    // element set
+    const bool elementSetFound = entityIndices[EntityType::ELEMENTSET].find(pidx) !=
+      entityIndices[EntityType::ELEMENTSET].end();
+    if (elementSetFound)
+    {
+      try
+      {
+        auto elementSet =
+          std::make_shared<vtkElementSet>(pds, blockName, blockId, controller, writer);
+        entityGroups.emplace(elementSet->GetIOSSEntityType(), elementSet);
+        continue;
+      }
+      catch (std::runtime_error&)
+      {
+        break;
+      }
+    }
+
+    // side set
+    const bool sideSetFound =
+      entityIndices[EntityType::SIDESET].find(pidx) != entityIndices[EntityType::SIDESET].end();
+    if (sideSetFound)
+    {
+      try
+      {
+        auto sideSet = std::make_shared<vtkSideSet>(pds, blockName, blockId, controller, writer);
+        entityGroups.emplace(sideSet->GetIOSSEntityType(), sideSet);
         continue;
       }
       catch (std::runtime_error&)
diff --git a/IO/IOSS/vtkIOSSWriter.cxx b/IO/IOSS/vtkIOSSWriter.cxx
index 739f7946281802d8152f29ffdd57280e0bbc00b4..d2b25dd6fe7266243c2e13d1e8e972a204306604 100644
--- a/IO/IOSS/vtkIOSSWriter.cxx
+++ b/IO/IOSS/vtkIOSSWriter.cxx
@@ -71,9 +71,6 @@ public:
   int CurrentTimeStepIndex{ 0 };
   int RestartIndex{ 0 };
   std::string LastMD5;
-  std::set<std::string> ElementBlockSelectors;
-  std::set<std::string> NodeSetSelectors;
-  std::set<std::string> SideSetSelectors;
 
   vtkInternals() = default;
   ~vtkInternals() = default;
@@ -87,6 +84,7 @@ vtkIOSSWriter::vtkIOSSWriter()
   , Controller(nullptr)
   , FileName(nullptr)
   , AssemblyName(nullptr)
+  , ChooseFieldsToWrite(false)
   , RemoveGhosts(true)
   , OffsetGlobalIds(false)
   , PreserveOriginalIds(false)
@@ -97,11 +95,9 @@ vtkIOSSWriter::vtkIOSSWriter()
 {
   this->SetController(vtkMultiProcessController::GetGlobalController());
   this->SetAssemblyName(vtkDataAssemblyUtilities::HierarchyName());
-  std::fill_n(this->ChooseEntityFieldsToWrite, EntityType::NUMBER_OF_ENTITY_TYPES, false);
   for (int i = 0; i < EntityType::NUMBER_OF_ENTITY_TYPES; ++i)
   {
-    this->EntityFieldSelection[i]->AddObserver(
-      vtkCommand::ModifiedEvent, this, &vtkIOSSWriter::Modified);
+    this->FieldSelection[i]->AddObserver(vtkCommand::ModifiedEvent, this, &vtkIOSSWriter::Modified);
   }
 }
 
@@ -114,39 +110,11 @@ vtkIOSSWriter::~vtkIOSSWriter()
 }
 
 //----------------------------------------------------------------------------
-void vtkIOSSWriter::SetChooseEntityFieldsToWrite(EntityType type, bool val)
+vtkDataArraySelection* vtkIOSSWriter::GetFieldSelection(EntityType type)
 {
   if (type < EntityType::NUMBER_OF_ENTITY_TYPES)
   {
-    this->ChooseEntityFieldsToWrite[type] = val;
-    this->Modified();
-  }
-  else
-  {
-    vtkErrorMacro("Invalid entity type: " << type);
-  }
-}
-
-//----------------------------------------------------------------------------
-bool vtkIOSSWriter::GetChooseEntityFieldsToWrite(vtkIOSSWriter::EntityType type) const
-{
-  if (type < EntityType::NUMBER_OF_ENTITY_TYPES)
-  {
-    return this->ChooseEntityFieldsToWrite[type];
-  }
-  else
-  {
-    vtkErrorMacro("Invalid entity type: " << type);
-    return false;
-  }
-}
-
-//----------------------------------------------------------------------------
-vtkDataArraySelection* vtkIOSSWriter::GetEntityFieldSelection(vtkIOSSWriter::EntityType type)
-{
-  if (type < EntityType::NUMBER_OF_ENTITY_TYPES)
-  {
-    return this->EntityFieldSelection[type];
+    return this->FieldSelection[type];
   }
   else
   {
@@ -156,12 +124,11 @@ vtkDataArraySelection* vtkIOSSWriter::GetEntityFieldSelection(vtkIOSSWriter::Ent
 }
 
 //------------------------------------------------------------------------------
-bool vtkIOSSWriter::AddElementBlockSelector(const char* selector)
+bool vtkIOSSWriter::AddSelector(vtkIOSSWriter::EntityType entityType, const char* selector)
 {
   if (selector)
   {
-    auto& internals = *this->Internals;
-    if (internals.ElementBlockSelectors.insert(selector).second)
+    if (this->Selectors[entityType].insert(selector).second)
     {
       this->Modified();
       return true;
@@ -171,178 +138,61 @@ bool vtkIOSSWriter::AddElementBlockSelector(const char* selector)
 }
 
 //------------------------------------------------------------------------------
-void vtkIOSSWriter::ClearElementBlockSelectors()
+void vtkIOSSWriter::ClearSelectors(vtkIOSSWriter::EntityType entityType)
 {
-  auto& internals = *this->Internals;
-  if (!internals.ElementBlockSelectors.empty())
+  if (!this->Selectors[entityType].empty())
   {
-    internals.ElementBlockSelectors.clear();
+    this->Selectors[entityType].clear();
     this->Modified();
   }
 }
 
 //------------------------------------------------------------------------------
-void vtkIOSSWriter::SetElementBlockSelector(const char* selector)
+void vtkIOSSWriter::SetSelector(vtkIOSSWriter::EntityType entity, const char* selector)
 {
   if (selector)
   {
-    auto& internals = *this->Internals;
-    if (internals.ElementBlockSelectors.size() == 1 &&
-      *internals.ElementBlockSelectors.begin() == selector)
+    if (this->Selectors[entity].size() == 1 && *this->Selectors[entity].begin() == selector)
     {
       return;
     }
-    internals.ElementBlockSelectors.clear();
-    internals.ElementBlockSelectors.insert(selector);
+    this->Selectors[entity].clear();
+    this->Selectors[entity].insert(selector);
     this->Modified();
   }
 }
 
 //------------------------------------------------------------------------------
-int vtkIOSSWriter::GetNumberOfElementBlockSelectors() const
+int vtkIOSSWriter::GetNumberOfSelectors(vtkIOSSWriter::EntityType entity) const
 {
-  auto& internals = *this->Internals;
-  return static_cast<int>(internals.ElementBlockSelectors.size());
-}
-
-//------------------------------------------------------------------------------
-const char* vtkIOSSWriter::GetElementBlockSelector(int index) const
-{
-  const auto& internals = *this->Internals;
-  if (index >= 0 && index < static_cast<int>(internals.ElementBlockSelectors.size()))
+  if (entity < EntityType::NUMBER_OF_ENTITY_TYPES)
   {
-    auto iter = std::next(internals.ElementBlockSelectors.begin(), index);
-    return iter->c_str();
+    return static_cast<int>(this->Selectors[entity].size());
   }
-
-  vtkErrorMacro("Invalid index '" << index << "'.");
-  return nullptr;
+  return 0;
 }
 
 //------------------------------------------------------------------------------
-bool vtkIOSSWriter::AddNodeSetSelector(const char* selector)
+const char* vtkIOSSWriter::GetSelector(vtkIOSSWriter::EntityType entityType, int index) const
 {
-  if (selector)
-  {
-    auto& internals = *this->Internals;
-    if (internals.NodeSetSelectors.insert(selector).second)
-    {
-      this->Modified();
-      return true;
-    }
-  }
-  return false;
-}
-
-//------------------------------------------------------------------------------
-void vtkIOSSWriter::ClearNodeSetSelectors()
-{
-  auto& internals = *this->Internals;
-  if (!internals.NodeSetSelectors.empty())
-  {
-    internals.NodeSetSelectors.clear();
-    this->Modified();
-  }
-}
-
-//------------------------------------------------------------------------------
-void vtkIOSSWriter::SetNodeSetSelector(const char* selector)
-{
-  if (selector)
+  if (index >= 0 && index < this->GetNumberOfSelectors(entityType))
   {
-    auto& internals = *this->Internals;
-    if (internals.NodeSetSelectors.size() == 1 && *internals.NodeSetSelectors.begin() == selector)
-    {
-      return;
-    }
-    internals.NodeSetSelectors.clear();
-    internals.NodeSetSelectors.insert(selector);
-    this->Modified();
+    auto& selectors = this->Selectors[entityType];
+    auto it = selectors.begin();
+    std::advance(it, index);
+    return it->c_str();
   }
-}
-
-//------------------------------------------------------------------------------
-int vtkIOSSWriter::GetNumberOfNodeSetSelectors() const
-{
-  auto& internals = *this->Internals;
-  return static_cast<int>(internals.NodeSetSelectors.size());
-}
-
-//------------------------------------------------------------------------------
-const char* vtkIOSSWriter::GetNodeSetSelector(int index) const
-{
-  const auto& internals = *this->Internals;
-  if (index >= 0 && index < static_cast<int>(internals.NodeSetSelectors.size()))
-  {
-    auto iter = std::next(internals.NodeSetSelectors.begin(), index);
-    return iter->c_str();
-  }
-
-  vtkErrorMacro("Invalid index '" << index << "'.");
   return nullptr;
 }
 
 //------------------------------------------------------------------------------
-bool vtkIOSSWriter::AddSideSetSelector(const char* selector)
-{
-  if (selector)
-  {
-    auto& internals = *this->Internals;
-    if (internals.SideSetSelectors.insert(selector).second)
-    {
-      this->Modified();
-      return true;
-    }
-  }
-  return false;
-}
-
-//------------------------------------------------------------------------------
-void vtkIOSSWriter::ClearSideSetSelectors()
-{
-  auto& internals = *this->Internals;
-  if (!internals.SideSetSelectors.empty())
-  {
-    internals.SideSetSelectors.clear();
-    this->Modified();
-  }
-}
-
-//------------------------------------------------------------------------------
-void vtkIOSSWriter::SetSideSetSelector(const char* selector)
-{
-  if (selector)
-  {
-    auto& internals = *this->Internals;
-    if (internals.SideSetSelectors.size() == 1 && *internals.SideSetSelectors.begin() == selector)
-    {
-      return;
-    }
-    internals.SideSetSelectors.clear();
-    internals.SideSetSelectors.insert(selector);
-    this->Modified();
-  }
-}
-
-//------------------------------------------------------------------------------
-int vtkIOSSWriter::GetNumberOfSideSetSelectors() const
+std::set<std::string> vtkIOSSWriter::GetSelectors(vtkIOSSWriter::EntityType entityType) const
 {
-  auto& internals = *this->Internals;
-  return static_cast<int>(internals.SideSetSelectors.size());
-}
-
-//------------------------------------------------------------------------------
-const char* vtkIOSSWriter::GetSideSetSelector(int index) const
-{
-  const auto& internals = *this->Internals;
-  if (index >= 0 && index < static_cast<int>(internals.SideSetSelectors.size()))
+  if (entityType < EntityType::NUMBER_OF_ENTITY_TYPES)
   {
-    auto iter = std::next(internals.SideSetSelectors.begin(), index);
-    return iter->c_str();
+    return this->Selectors[entityType];
   }
-
-  vtkErrorMacro("Invalid index '" << index << "'.");
-  return nullptr;
+  return std::set<std::string>();
 }
 
 //------------------------------------------------------------------------------
@@ -599,39 +449,29 @@ void vtkIOSSWriter::PrintSelf(ostream& os, vtkIndent indent)
   os << indent << "FileName: " << (this->FileName ? this->FileName : "(nullptr)") << endl;
   os << indent << "AssemblyName: " << (this->AssemblyName ? this->AssemblyName : "(nullptr)")
      << endl;
-  os << indent
-     << "ChooseNodeBlockFieldsToWrite: " << (this->GetChooseNodeBlockFieldsToWrite() ? "On" : "Off")
-     << endl;
-  this->GetNodeSetFieldSelection()->PrintSelf(os, indent.GetNextIndent());
-  os << indent << "ElementBlockSelectors: " << endl;
-  for (const auto& selector : this->Internals->ElementBlockSelectors)
-  {
-    os << indent << selector << "  ";
-  }
-  os << endl;
-  os << indent << "ChooseElementBlockFieldsToWrite: "
-     << (this->GetChooseElementBlockFieldsToWrite() ? "On" : "Off") << endl;
-  this->GetElementBlockFieldSelection()->PrintSelf(os, indent.GetNextIndent());
-  os << indent << "NodeSetSelectors: " << endl;
-  for (const auto& selector : this->Internals->NodeSetSelectors)
+
+  os << indent << "ChooseFieldsToWrite: " << (this->ChooseFieldsToWrite ? "On" : "Off") << endl;
+  // Skip NodeBlock for selectors
+  for (int i = EntityType::EDGEBLOCK; i < EntityType::NUMBER_OF_ENTITY_TYPES; ++i)
   {
-    os << indent << selector << "  ";
+    os << indent << vtkIOSSReader::GetDataAssemblyNodeNameForEntityType(i)
+       << " selectors: " << endl;
+    for (const auto& selector : this->Selectors[i])
+    {
+      os << indent << selector << "  ";
+    }
+    os << endl;
   }
-  os << endl;
-  os << indent
-     << "ChooseNodeSetFieldsToWrite: " << (this->GetChooseNodeSetFieldsToWrite() ? "On" : "Off")
-     << endl;
-  this->GetNodeSetFieldSelection()->PrintSelf(os, indent.GetNextIndent());
-  os << indent << "SideSetSelectors: " << endl;
-  for (const auto& selector : this->Internals->SideSetSelectors)
+  if (this->ChooseFieldsToWrite)
   {
-    os << indent << selector << "  ";
+    for (int i = EntityType::NODEBLOCK; i < EntityType::NUMBER_OF_ENTITY_TYPES; ++i)
+    {
+      os << indent << vtkIOSSReader::GetDataAssemblyNodeNameForEntityType(i)
+         << " fields to write: " << endl;
+      this->FieldSelection[i]->PrintSelf(os, indent.GetNextIndent());
+      os << endl;
+    }
   }
-  os << endl;
-  os << indent
-     << "ChooseSideSetFieldsToWrite: " << (this->GetChooseSideSetFieldsToWrite() ? "On" : "Off")
-     << endl;
-  this->GetSideSetFieldSelection()->PrintSelf(os, indent.GetNextIndent());
   os << indent << "RemoveGhosts: " << (this->RemoveGhosts ? "On" : "Off") << endl;
   os << indent << "Controller: " << this->Controller << endl;
   os << indent << "OffsetGlobalIds: " << OffsetGlobalIds << endl;
diff --git a/IO/IOSS/vtkIOSSWriter.h b/IO/IOSS/vtkIOSSWriter.h
index 15f7697a2c46515c74fbd18c8c081da995971e3b..19285b0894e82b1614ed72a8d588502a6eb63ac8 100644
--- a/IO/IOSS/vtkIOSSWriter.h
+++ b/IO/IOSS/vtkIOSSWriter.h
@@ -32,6 +32,8 @@
 #include "vtkWriter.h"
 
 #include <memory> // for std::unique_ptr
+#include <set>    // for std::set
+#include <string> // for std::string
 
 VTK_ABI_NAMESPACE_BEGIN
 class vtkDataArraySelection;
@@ -67,146 +69,216 @@ public:
 
   using EntityType = vtkIOSSReader::EntityType;
 
-  /////////////////////////////////////////////////////////////////////////////////////////
-  //                                   Node Block API                                    //
-  /////////////////////////////////////////////////////////////////////////////////////////
   ///@{
   /**
-   * Choose which node block fields to write. If this is true, then only the
+   * Choose which fields to write. If this is true, then only the
    * arrays selected will be written. If this is false, then all arrays will be
    * written.
    *
    * The default is false.
    */
-  void SetChooseNodeBlockFieldsToWrite(bool value)
-  {
-    this->SetChooseEntityFieldsToWrite(EntityType::NODEBLOCK, value);
-  }
-  bool GetChooseNodeBlockFieldsToWrite() const
-  {
-    return this->GetChooseEntityFieldsToWrite(EntityType::NODEBLOCK);
-  }
-  vtkBooleanMacro(ChooseNodeBlockFieldsToWrite, bool);
+  vtkSetMacro(ChooseFieldsToWrite, bool);
+  vtkGetMacro(ChooseFieldsToWrite, bool);
+  vtkBooleanMacro(ChooseFieldsToWrite, bool);
   ///@}
 
-  /**
-   * Returns the field selection object for the element block arrays.
-   */
-  vtkDataArraySelection* GetNodeBlockFieldSelection()
-  {
-    return this->GetEntityFieldSelection(EntityType::NODEBLOCK);
-  }
-  /////////////////////////////////////////////////////////////////////////////////////////
-
   /////////////////////////////////////////////////////////////////////////////////////////
-  //                                 Element Block API                                   //
+  //                                  Generic Entity API                                 //
   /////////////////////////////////////////////////////////////////////////////////////////
   ///@{
   /**
-   * API to set element block selectors. Multiple selectors can be added using
-   * `AddElementBlockSelector`. The order in which selectors are specified is not preserved
+   * API to set entity selectors. Multiple selectors can be added using
+   * `AddSelector`. The order in which selectors are specified is not preserved
    * and has no impact on the result.
    *
-   * `AddElementBlockSelector` returns true if the selector was added, false if the selector
+   * `AddSelector` returns true if the selector was added, false if the selector
    * was already specified and hence not added.
    *
    * @sa vtkDataAssembly::SelectNodes
    */
-  bool AddElementBlockSelector(const char* selector);
-  void ClearElementBlockSelectors();
+  bool AddSelector(EntityType entity, const char* selector);
+  void ClearSelectors(EntityType entity);
   ///@}
 
   /**
-   * Convenience method to set a single element block selector.
+   * Convenience method to set a single entity selector.
    * This clears any other existing selectors.
    */
-  void SetElementBlockSelector(const char* selector);
+  void SetSelector(EntityType entity, const char* selector);
 
   ///@{
   /**
-   * API to access element block selectors.
+   * API to access entity selectors.
    */
-  int GetNumberOfElementBlockSelectors() const;
-  const char* GetElementBlockSelector(int index) const;
+  int GetNumberOfSelectors(EntityType entity) const;
+  const char* GetSelector(EntityType entity, int index) const;
+  std::set<std::string> GetSelectors(EntityType entity) const;
   ///@}
 
+  /**
+   * Get the selection object for the given entity type. This can be used to
+   * select which fields to write.
+   */
+  vtkDataArraySelection* GetFieldSelection(EntityType type);
+  /////////////////////////////////////////////////////////////////////////////////////////
+
+  /////////////////////////////////////////////////////////////////////////////////////////
+  //                                   Node Block API                                    //
+  /////////////////////////////////////////////////////////////////////////////////////////
+  /**
+   * Returns the field selection object for the element block arrays.
+   */
+  vtkDataArraySelection* GetNodeBlockFieldSelection()
+  {
+    return this->GetFieldSelection(EntityType::NODEBLOCK);
+  }
+  /////////////////////////////////////////////////////////////////////////////////////////
+
+  /////////////////////////////////////////////////////////////////////////////////////////
+  //                                  Edge Block API                                     //
+  /////////////////////////////////////////////////////////////////////////////////////////
   ///@{
   /**
-   * Choose which element block fields to write. If this is true, then only the
-   * arrays selected will be written. If this is false, then all arrays will be
-   * written.
-   *
-   * The default is false.
+   * Add/Clear/Set/Get edge block selectors
    */
-  void SetChooseElementBlockFieldsToWrite(bool value)
+  bool AddEdgeBlockSelector(const char* selector)
   {
-    this->SetChooseEntityFieldsToWrite(EntityType::ELEMENTBLOCK, value);
+    return this->AddSelector(EntityType::EDGEBLOCK, selector);
   }
-  bool GetChooseElementBlockFieldsToWrite() const
+  void ClearEdgeBlockSelectors() { this->ClearSelectors(EntityType::EDGEBLOCK); }
+  void SetEdgeBlockSelector(const char* selector)
   {
-    return this->GetChooseEntityFieldsToWrite(EntityType::ELEMENTBLOCK);
+    this->SetSelector(EntityType::EDGEBLOCK, selector);
+  }
+  int GetNumberOfEdgeBlockSelectors() const
+  {
+    return this->GetNumberOfSelectors(EntityType::EDGEBLOCK);
+  }
+  const char* GetEdgeBlockSelector(int index) const
+  {
+    return this->GetSelector(EntityType::EDGEBLOCK, index);
+  }
+  std::set<std::string> GetEdgeBlockSelectors() const
+  {
+    return this->GetSelectors(EntityType::EDGEBLOCK);
   }
-  vtkBooleanMacro(ChooseElementBlockFieldsToWrite, bool);
   ///@}
 
   /**
-   * Returns the field selection object for the element block arrays.
+   * Returns the field selection object for the edge block arrays.
    */
-  vtkDataArraySelection* GetElementBlockFieldSelection()
+  vtkDataArraySelection* GetEdgeBlockFieldSelection()
   {
-    return this->GetEntityFieldSelection(EntityType::ELEMENTBLOCK);
+    return this->GetFieldSelection(EntityType::EDGEBLOCK);
   }
   /////////////////////////////////////////////////////////////////////////////////////////
 
   /////////////////////////////////////////////////////////////////////////////////////////
-  //                                 Node Set API                                        //
+  //                                  Face Block API                                     //
   /////////////////////////////////////////////////////////////////////////////////////////
   ///@{
   /**
-   * API to set node set selectors. Multiple selectors can be added using
-   * `AddNodeSetSelector`. The order in which selectors are specified is not preserved
-   * and has no impact on the result.
-   *
-   * `AddNodeSetSelector` returns true if the selector was added, false if the selector
-   * was already specified and hence not added.
-   *
-   * @sa vtkDataAssembly::SelectNodes
+   * Add/Clear/Set/Get face block selectors
    */
-  bool AddNodeSetSelector(const char* selector);
-  void ClearNodeSetSelectors();
+  bool AddFaceBlockSelector(const char* selector)
+  {
+    return this->AddSelector(EntityType::FACEBLOCK, selector);
+  }
+  void ClearFaceBlockSelectors() { this->ClearSelectors(EntityType::FACEBLOCK); }
+  void SetFaceBlockSelector(const char* selector)
+  {
+    this->SetSelector(EntityType::FACEBLOCK, selector);
+  }
+  int GetNumberOfFaceBlockSelectors() const
+  {
+    return this->GetNumberOfSelectors(EntityType::FACEBLOCK);
+  }
+  const char* GetFaceBlockSelector(int index) const
+  {
+    return this->GetSelector(EntityType::FACEBLOCK, index);
+  }
+  std::set<std::string> GetFaceBlockSelectors() const
+  {
+    return this->GetSelectors(EntityType::FACEBLOCK);
+  }
   ///@}
 
   /**
-   * Convenience method to set a single node set selector.
-   * This clears any other existing selectors.
+   * Returns the field selection object for the face block arrays.
    */
-  void SetNodeSetSelector(const char* selector);
+  vtkDataArraySelection* GetFaceBlockFieldSelection()
+  {
+    return this->GetFieldSelection(EntityType::FACEBLOCK);
+  }
+  /////////////////////////////////////////////////////////////////////////////////////////
 
+  /////////////////////////////////////////////////////////////////////////////////////////
+  //                                 Element Block API                                   //
+  /////////////////////////////////////////////////////////////////////////////////////////
   ///@{
   /**
-   * API to access node set selectors.
+   * Add/Clear/Set/Get element block selectors
    */
-  int GetNumberOfNodeSetSelectors() const;
-  const char* GetNodeSetSelector(int index) const;
+  bool AddElementBlockSelector(const char* selector)
+  {
+    return this->AddSelector(EntityType::ELEMENTBLOCK, selector);
+  }
+  void ClearElementBlockSelectors() { this->ClearSelectors(EntityType::ELEMENTBLOCK); }
+  void SetElementBlockSelector(const char* selector)
+  {
+    this->SetSelector(EntityType::ELEMENTBLOCK, selector);
+  }
+  int GetNumberOfElementBlockSelectors() const
+  {
+    return this->GetNumberOfSelectors(EntityType::ELEMENTBLOCK);
+  }
+  const char* GetElementBlockSelector(int index) const
+  {
+    return this->GetSelector(EntityType::ELEMENTBLOCK, index);
+  }
+  std::set<std::string> GetElementBlockSelectors() const
+  {
+    return this->GetSelectors(EntityType::ELEMENTBLOCK);
+  }
   ///@}
 
+  /**
+   * Returns the field selection object for the element block arrays.
+   */
+  vtkDataArraySelection* GetElementBlockFieldSelection()
+  {
+    return this->GetFieldSelection(EntityType::ELEMENTBLOCK);
+  }
+  /////////////////////////////////////////////////////////////////////////////////////////
+
+  /////////////////////////////////////////////////////////////////////////////////////////
+  //                                 Node Set API                                        //
+  /////////////////////////////////////////////////////////////////////////////////////////
   ///@{
   /**
-   * Choose which node set fields to write. If this is true, then only the
-   * arrays selected will be written. If this is false, then all arrays will be
-   * written.
-   *
-   * The default is false.
+   * Add/Clear/Set/Get node set selectors
    */
-  void SetChooseNodeSetFieldsToWrite(bool value)
+  bool AddNodeSetSelector(const char* selector)
+  {
+    return this->AddSelector(EntityType::NODESET, selector);
+  }
+  void ClearNodeSetSelectors() { this->ClearSelectors(EntityType::NODESET); }
+  void SetNodeSetSelector(const char* selector)
   {
-    this->SetChooseEntityFieldsToWrite(EntityType::NODESET, value);
+    this->SetSelector(EntityType::NODESET, selector);
   }
-  bool GetChooseNodeSetFieldsToWrite() const
+  int GetNumberOfNodeSetSelectors() const
   {
-    return this->GetChooseEntityFieldsToWrite(EntityType::NODESET);
+    return this->GetNumberOfSelectors(EntityType::NODESET);
+  }
+  const char* GetNodeSetSelector(int index) const
+  {
+    return this->GetSelector(EntityType::NODESET, index);
+  }
+  std::set<std::string> GetNodeSetSelectors() const
+  {
+    return this->GetSelectors(EntityType::NODESET);
   }
-  vtkBooleanMacro(ChooseNodeSetFieldsToWrite, bool);
   ///@}
 
   /**
@@ -214,61 +286,155 @@ public:
    */
   vtkDataArraySelection* GetNodeSetFieldSelection()
   {
-    return this->GetEntityFieldSelection(EntityType::NODESET);
+    return this->GetFieldSelection(EntityType::NODESET);
   }
   /////////////////////////////////////////////////////////////////////////////////////////
 
   /////////////////////////////////////////////////////////////////////////////////////////
-  //                                 Side Set API                                        //
+  //                                 Edge Set API                                        //
   /////////////////////////////////////////////////////////////////////////////////////////
   ///@{
   /**
-   * API to set side set selectors. Multiple selectors can be added using
-   * `AddSideSetSelector`. The order in which selectors are specified is not preserved
-   * and has no impact on the result.
-   *
-   * `AddSideSetSelector` returns true if the selector was added, false if the selector
-   * was already specified and hence not added.
-   *
-   * @sa vtkDataAssembly::SelectNodes
-   *
-   * Default: /IOSS/side_sets
+   * Add/Clear/Set/Get edge set selectors
    */
-  bool AddSideSetSelector(const char* selector);
-  void ClearSideSetSelectors();
+  bool AddEdgeSetSelector(const char* selector)
+  {
+    return this->AddSelector(EntityType::SIDESET, selector);
+  }
+  void ClearEdgeSetSelectors() { this->ClearSelectors(EntityType::SIDESET); }
+  void SetEdgeSetSelector(const char* selector)
+  {
+    this->SetSelector(EntityType::SIDESET, selector);
+  }
+  int GetNumberOfEdgeSetSelectors() const
+  {
+    return this->GetNumberOfSelectors(EntityType::SIDESET);
+  }
+  const char* GetEdgeSetSelector(int index) const
+  {
+    return this->GetSelector(EntityType::SIDESET, index);
+  }
+  std::set<std::string> GetEdgeSetSelectors() const
+  {
+    return this->GetSelectors(EntityType::SIDESET);
+  }
   ///@}
 
   /**
-   * Convenience method to set a single side set selector.
-   * This clears any other existing selectors.
+   * Returns the field selection object for the edge set arrays.
+   */
+  vtkDataArraySelection* GetEdgeSetFieldSelection()
+  {
+    return this->GetFieldSelection(EntityType::SIDESET);
+  }
+  /////////////////////////////////////////////////////////////////////////////////////////
+
+  /////////////////////////////////////////////////////////////////////////////////////////
+  //                                 Face Set API                                        //
+  /////////////////////////////////////////////////////////////////////////////////////////
+  ///@{
+  /**
+   * Add/Clear/Set/Get edge set selectors
+   */
+  bool AddFaceSetSelector(const char* selector)
+  {
+    return this->AddSelector(EntityType::SIDESET, selector);
+  }
+  void ClearFaceSetSelectors() { this->ClearSelectors(EntityType::SIDESET); }
+  void SetFaceSetSelector(const char* selector)
+  {
+    this->SetSelector(EntityType::SIDESET, selector);
+  }
+  int GetNumberOfFaceSetSelectors() const
+  {
+    return this->GetNumberOfSelectors(EntityType::SIDESET);
+  }
+  const char* GetFaceSetSelector(int index) const
+  {
+    return this->GetSelector(EntityType::SIDESET, index);
+  }
+  std::set<std::string> GetFaceSetSelectors() const
+  {
+    return this->GetSelectors(EntityType::SIDESET);
+  }
+  ///@}
+
+  /**
+   * Returns the field selection object for the edge set arrays.
    */
-  void SetSideSetSelector(const char* selector);
+  vtkDataArraySelection* GetFaceSetFieldSelection()
+  {
+    return this->GetFieldSelection(EntityType::SIDESET);
+  }
+  /////////////////////////////////////////////////////////////////////////////////////////
 
+  /////////////////////////////////////////////////////////////////////////////////////////
+  //                               Element Set API                                       //
+  /////////////////////////////////////////////////////////////////////////////////////////
   ///@{
   /**
-   * API to access side set selectors.
+   * Add/Clear/Set/Get element set selectors
    */
-  int GetNumberOfSideSetSelectors() const;
-  const char* GetSideSetSelector(int index) const;
+  bool AddElementSetSelector(const char* selector)
+  {
+    return this->AddSelector(EntityType::SIDESET, selector);
+  }
+  void ClearElementSetSelectors() { this->ClearSelectors(EntityType::SIDESET); }
+  void SetElementSetSelector(const char* selector)
+  {
+    this->SetSelector(EntityType::SIDESET, selector);
+  }
+  int GetNumberOfElementSetSelectors() const
+  {
+    return this->GetNumberOfSelectors(EntityType::SIDESET);
+  }
+  const char* GetElementSetSelector(int index) const
+  {
+    return this->GetSelector(EntityType::SIDESET, index);
+  }
+  std::set<std::string> GetElementSetSelectors() const
+  {
+    return this->GetSelectors(EntityType::SIDESET);
+  }
   ///@}
 
+  /**
+   * Returns the field selection object for the element set arrays.
+   */
+  vtkDataArraySelection* GetElementSetFieldSelection()
+  {
+    return this->GetFieldSelection(EntityType::SIDESET);
+  }
+  /////////////////////////////////////////////////////////////////////////////////////////
+
+  /////////////////////////////////////////////////////////////////////////////////////////
+  //                                 Side Set API                                        //
+  /////////////////////////////////////////////////////////////////////////////////////////
   ///@{
   /**
-   * Choose which side set fields to write. If this is true, then only the
-   * arrays selected will be written. If this is false, then all arrays will be
-   * written.
-   *
-   * The default is false.
+   * Add/Clear/Set/Get side set selectors
    */
-  void SetChooseSideSetFieldsToWrite(bool value)
+  bool AddSideSetSelector(const char* selector)
+  {
+    return this->AddSelector(EntityType::SIDESET, selector);
+  }
+  void ClearSideSetSelectors() { this->ClearSelectors(EntityType::SIDESET); }
+  void SetSideSetSelector(const char* selector)
+  {
+    this->SetSelector(EntityType::SIDESET, selector);
+  }
+  int GetNumberOfSideSetSelectors() const
   {
-    this->SetChooseEntityFieldsToWrite(EntityType::SIDESET, value);
+    return this->GetNumberOfSelectors(EntityType::SIDESET);
   }
-  bool GetChooseSideSetFieldsToWrite() const
+  const char* GetSideSetSelector(int index) const
   {
-    return this->GetChooseEntityFieldsToWrite(EntityType::SIDESET);
+    return this->GetSelector(EntityType::SIDESET, index);
+  }
+  std::set<std::string> GetSideSetSelectors() const
+  {
+    return this->GetSelectors(EntityType::SIDESET);
   }
-  vtkBooleanMacro(ChooseSideSetFieldsToWrite, bool);
   ///@}
 
   /**
@@ -276,7 +442,7 @@ public:
    */
   vtkDataArraySelection* GetSideSetFieldSelection()
   {
-    return this->GetEntityFieldSelection(EntityType::SIDESET);
+    return this->GetFieldSelection(EntityType::SIDESET);
   }
   /////////////////////////////////////////////////////////////////////////////////////////
 
@@ -430,14 +596,10 @@ private:
   class vtkInternals;
   std::unique_ptr<vtkInternals> Internals;
 
-  void SetChooseEntityFieldsToWrite(EntityType type, bool val);
-  bool GetChooseEntityFieldsToWrite(EntityType type) const;
-
-  vtkDataArraySelection* GetEntityFieldSelection(EntityType type);
-
   vtkMultiProcessController* Controller;
   char* FileName;
   char* AssemblyName;
+  bool ChooseFieldsToWrite;
   bool RemoveGhosts;
   bool OffsetGlobalIds;
   bool PreserveOriginalIds;
@@ -445,8 +607,9 @@ private:
   double DisplacementMagnitude;
   int TimeStepRange[2];
   int TimeStepStride;
-  bool ChooseEntityFieldsToWrite[EntityType::NUMBER_OF_ENTITY_TYPES];
-  vtkNew<vtkDataArraySelection> EntityFieldSelection[EntityType::NUMBER_OF_ENTITY_TYPES];
+
+  std::set<std::string> Selectors[EntityType::NUMBER_OF_ENTITY_TYPES];
+  vtkNew<vtkDataArraySelection> FieldSelection[EntityType::NUMBER_OF_ENTITY_TYPES];
 };
 VTK_ABI_NAMESPACE_END