diff --git a/modules/io/pymod/export_mmcif_io.cc b/modules/io/pymod/export_mmcif_io.cc
index 3ca9d9dcd3f19cbb38e2f50f2d24b3f5eeafb0f3..455dc1ed124bd0a3c9334af88b2a1669548dfd5f 100644
--- a/modules/io/pymod/export_mmcif_io.cc
+++ b/modules/io/pymod/export_mmcif_io.cc
@@ -157,6 +157,7 @@ void export_mmcif_io()
 
   class_<MMCifWriterEntity>("MMCifWriterEntity", no_init)
     .def("FromPolymer", &MMCifWriterEntity::FromPolymer).staticmethod("FromPolymer")
+    .def("AddHet", &MMCifWriterEntity::AddHet, (arg("rnum"), arg("mon_id")))
     .add_property("type", &MMCifWriterEntity::type)
     .add_property("poly_type", &MMCifWriterEntity::poly_type)
     .add_property("branch_type", &MMCifWriterEntity::branch_type)
diff --git a/modules/io/src/mol/mmcif_writer.cc b/modules/io/src/mol/mmcif_writer.cc
index c7cfffd4678c817d32b3149742f87158e31c2564..158f17fdc8c57d3dcddcb4ea0e0067a390cb5b2c 100644
--- a/modules/io/src/mol/mmcif_writer.cc
+++ b/modules/io/src/mol/mmcif_writer.cc
@@ -321,11 +321,27 @@ namespace {
                    const ost::io::MMCifWriterEntity& info) {
     // checks if the residue names in res_list are an exact match
     // with mon_ids in info
-    std::vector<String> mon_ids;
-    for(auto res : res_list) {
-      mon_ids.push_back(res.GetName());
+    if(res_list.size() != info.mon_ids.size()) {
+      return false;
+    }
+
+    for(size_t i = 0; i < res_list.size(); ++i) {
+      if(res_list[i].GetName() != info.mon_ids[i]) {
+        bool hetero_match = false;
+        if(info.het.find(i+1) != info.het.end()) {
+          const std::vector<String>& het = info.het.at(i+1);
+          if(std::find(het.begin(), het.end(), res_list[i].GetName()) !=
+             het.end()) {
+            hetero_match = true;
+          }
+        }
+        if(!hetero_match) {
+          return false;
+        }
+      }
     }
-    return mon_ids == info.mon_ids;
+
+    return true;
   }
 
   void AddAsym(const String& asym_chain_name,
@@ -380,6 +396,13 @@ namespace {
           ++n_beyond; // we're basically filling an unknown gap...
         } else if(info.mon_ids[num-1] == res.GetName()) {
           ++n_matches;
+        } else if(info.het.find(num) != info.het.end()) {
+          const std::vector<String>& het = info.het.at(num);
+          if(std::find(het.begin(), het.end(), res.GetName()) != het.end()) {
+            ++n_matches;
+          } else {
+            return false;
+          }
         } else {
           return false;
         }
@@ -682,6 +705,7 @@ namespace {
     desc.Add("entity_id");
     desc.Add("mon_id");
     desc.Add("num");
+    desc.Add("hetero");
     ost::io::StarWriterLoopPtr sl(new ost::io::StarWriterLoop(desc));
     return sl;    
   }
@@ -899,15 +923,29 @@ namespace {
 
   void Feed_entity_poly_seq(ost::io::StarWriterLoopPtr entity_poly_seq_ptr,
                             const std::vector<ost::io::MMCifWriterEntity>& entity_info) {
-    std::vector<ost::io::StarWriterValue> entity_poly_seq_data(3);
+    std::vector<ost::io::StarWriterValue> entity_poly_seq_data(4);
     for(size_t entity_idx = 0; entity_idx < entity_info.size(); ++entity_idx) {
       if(entity_info[entity_idx].is_poly) {
-        const std::vector<String>& mon_ids = entity_info[entity_idx].mon_ids;
+        const ost::io::MMCifWriterEntity& ei = entity_info[entity_idx];
+        const std::vector<String>& mon_ids = ei.mon_ids;
         for(size_t mon_idx = 0; mon_idx < mon_ids.size(); ++mon_idx) {
           entity_poly_seq_data[0] = ost::io::StarWriterValue::FromInt(entity_idx+1);
           entity_poly_seq_data[1] = ost::io::StarWriterValue::FromString(mon_ids[mon_idx]);
           entity_poly_seq_data[2] = ost::io::StarWriterValue::FromInt(mon_idx+1);
+          if(!ei.het.empty() && ei.het.find(mon_idx + 1) != ei.het.end()) {
+            entity_poly_seq_data[3] = ost::io::StarWriterValue::FromString("y");
+          } else {
+            entity_poly_seq_data[3] = ost::io::StarWriterValue::FromString("n");
+          }
           entity_poly_seq_ptr->AddData(entity_poly_seq_data);
+          if(!ei.het.empty() && ei.het.find(mon_idx + 1) != ei.het.end()) {
+            const std::vector<String>& het_mon_ids = ei.het.at(mon_idx + 1);
+            for(auto mon_id: het_mon_ids) {
+              entity_poly_seq_data[1] = ost::io::StarWriterValue::FromString(mon_id);
+              entity_poly_seq_ptr->AddData(entity_poly_seq_data);
+            }
+          }
+
         }
       }
     }
diff --git a/modules/io/src/mol/mmcif_writer.hh b/modules/io/src/mol/mmcif_writer.hh
index 5516360e1431ed3441f8b69896d7b79ec6238fb3..edfd50b7f65c51e14988278ea93ed82dcc312031 100644
--- a/modules/io/src/mol/mmcif_writer.hh
+++ b/modules/io/src/mol/mmcif_writer.hh
@@ -41,6 +41,10 @@ struct MMCifWriterEntity {
 
   int GetAsymIdx(const String& asym_id) const;
 
+  void AddHet(int rnum, const String& mon_id) {
+    het[rnum].push_back(mon_id);
+  }
+
   bool operator==(const MMCifWriterEntity& rhs) const {
        return (type == rhs.type)
            && (poly_type == rhs.poly_type)
@@ -89,6 +93,9 @@ struct MMCifWriterEntity {
   // irrelevant if is_poly is false. The assumption is that aligned residues
   // exactly match with the respective position in mon_ids.
   std::map<String, std::vector<String> > asym_alns; 
+
+  // heterogeneities
+  std::map<int, std::vector<String> > het;
 };
 
 class DLLEXPORT_OST_IO MMCifWriter : public StarWriter {