diff --git a/modules/io/src/mol/omf.cc b/modules/io/src/mol/omf.cc
index 09eb7c1d7131bbb1b3bf3a01df125ef4c01ddc99..dd3f579989f784545855f10c24f14fbc134f749a 100644
--- a/modules/io/src/mol/omf.cc
+++ b/modules/io/src/mol/omf.cc
@@ -75,6 +75,773 @@ namespace{
              norm_n[2]*sin_dihedral*bond_length_x_sin_angle + C[2];
   }
 
+  inline Real PeptideBond() {
+    return 1.33;
+  }
+
+  inline Real N_CA_Bond(char olc) {
+    return 1.46;
+  }
+
+  inline Real CA_C_Bond(char olc) {
+    return 1.52;
+  }
+
+  inline Real CA_CB_Bond(char olc) {
+    return 1.52;
+  }
+
+  inline Real C_CA_CB_Angle(char olc) {
+    return 1.9111;
+  }
+
+  inline Real CA_C_N_Angle(char olc) {
+    return 2.0358;
+  }
+
+  inline Real C_N_CA_Angle(char olc) {
+    return 2.1185;
+  }
+
+  inline Real N_CA_C_Angle(char olc) {
+    switch(olc) {
+      case 'G': {
+        return 1.9354;
+      }
+      case 'A': {
+        return 1.9385;
+      }
+      case 'S': {
+        return 1.9422;
+      }
+      case 'C': {
+        return 1.9353;
+      }
+      case 'V': {
+        return 1.9158;
+      }
+      case 'I': {
+        return 1.9150;
+      }
+      case 'L': {
+        return 1.9350;
+      }
+      case 'T': {
+        return 1.9321;
+      }
+      case 'R': {
+        return 1.9370;
+      }
+      case 'K': {
+        return 1.9387;
+      }
+      case 'D': {
+        return 1.9378;
+      }
+      case 'N': {
+        return 1.9460;
+      }
+      case 'E': {
+        return 1.9403;
+      }
+      case 'Q': {
+        return 1.9388;
+      }
+      case 'M': {
+        return 1.9363;
+      }
+      case 'H': {
+        return 1.9388;
+      }
+      case 'P': {
+        return 1.9679;
+      }
+      case 'F': {
+        return 1.9330;
+      }
+      case 'Y': {
+        return 1.9361;
+      }
+      case 'W': {
+        return 1.9354;
+      }
+      default: {
+        return 1.94;
+      }
+    }
+  }
+
+
+  inline Real N_C_CA_CB_DiAngle(char olc) {
+    switch(olc) {
+      case 'A': {
+        return 2.1413;
+      }
+      case 'S': {
+        return 2.1409;
+      }
+      case 'C': {
+        return 2.1381;
+      }
+      case 'V': {
+        return 2.1509;
+      }
+      case 'I': {
+        return 2.1509;
+      }
+      case 'L': {
+        return 2.1379;
+      }
+      case 'T': {
+        return 2.1484;
+      }
+      case 'R': {
+        return 2.1426;
+      }
+      case 'K': {
+        return 2.1426;
+      }
+      case 'D': {
+        return 2.1436;
+      }
+      case 'N': {
+        return 2.1507;
+      }
+      case 'E': {
+        return 2.1445;
+      }
+      case 'Q': {
+        return 2.1435;
+      }
+      case 'M': {
+        return 2.1411;
+      }
+      case 'H': {
+        return 2.1410;
+      }
+      case 'P': {
+        return 2.0123;
+      }
+      case 'F': {
+        return 2.1399;
+      }
+      case 'Y': {
+        return 2.1398;
+      }
+      case 'W': {
+        return 2.1400;
+      }
+      default: {
+        return 2.14;
+      }
+    }
+  }
+
+  void FillInferredTriPeptideIndices(const ost::io::ResidueDefinition& def_one,
+                                     const ost::io::ResidueDefinition& def_two,
+                                     const ost::io::ResidueDefinition& def_three,
+                                     int res_idx,
+                                     std::vector<std::set<int> >& indices) {
+    indices[res_idx].insert(def_one.GetIdx("N"));
+    indices[res_idx].insert(def_one.GetIdx("C"));
+    int cb_idx = def_one.GetIdx("CB");
+    if(cb_idx != -1) {
+      indices[res_idx].insert(cb_idx);
+    }
+    indices[res_idx+1].insert(def_two.GetIdx("N"));
+    indices[res_idx+1].insert(def_two.GetIdx("C"));
+    cb_idx = def_two.GetIdx("CB");
+    if(cb_idx != -1) {
+      indices[res_idx+1].insert(cb_idx);
+    }
+    indices[res_idx+2].insert(def_three.GetIdx("N"));
+    indices[res_idx+2].insert(def_three.GetIdx("C"));
+    cb_idx = def_three.GetIdx("CB");
+    if(cb_idx != -1) {
+      indices[res_idx+2].insert(cb_idx);
+    }
+  }
+
+  void FillInferredRotIndices(const ost::io::ResidueDefinition& def,
+                              int res_idx,
+                              std::vector<std::set<int> >& inferred_indices) {
+    const std::vector<ost::io::SidechainAtomRule>& at_rules =
+    def.GetSidechainAtomRules();
+    for(auto it = at_rules.begin(); it != at_rules.end(); ++it) {
+      inferred_indices[res_idx].insert(it->sidechain_atom_idx);
+    }
+  }
+
+  bool EncodeTriPeptide(const ost::io::ResidueDefinition& def_one,
+                        const ost::io::ResidueDefinition& def_two,
+                        const ost::io::ResidueDefinition& def_three,
+                        Real error_thresh,
+                        const std::vector<geom::Vec3>& ref_positions,
+                        int res_idx,
+                        int res_start_idx,
+                        std::vector<std::set<int> >& skip_indices,
+                        std::vector<geom::Vec3>& positions,
+                        std::vector<uint8_t>& data) {
+
+    // extracts data required to reconstruct positions
+    // if max reconstruction error is below specified threshold,
+    // the reconstructed positions are directly fed back into
+    // positions. res_idx, res_start_idx and skip_indices are updated.
+    // That means: in case of successful reconstruction res_idx is
+    // incremented by 3, 1 otherwise. res_start_idx is updated too to
+    // point to the start atom of res_idx
+
+    Real max_error = 0.0;
+
+    int n_one_idx = def_one.GetIdx("N");
+    int ca_one_idx = def_one.GetIdx("CA");
+    int c_one_idx = def_one.GetIdx("C");
+    int n_two_idx = def_two.GetIdx("N");
+    int ca_two_idx = def_two.GetIdx("CA");
+    int c_two_idx = def_two.GetIdx("C");
+    int n_three_idx = def_three.GetIdx("N");
+    int ca_three_idx = def_three.GetIdx("CA");
+    int c_three_idx = def_three.GetIdx("C");
+
+    if(n_one_idx == -1 || ca_one_idx == -1 || c_one_idx == -1 ||
+       n_two_idx == -1 || ca_two_idx == -1 || c_two_idx == -1 ||
+       n_three_idx == -1 || ca_three_idx == -1 || c_three_idx == -1) {
+      return false;
+    }
+
+    // CBeta are optional
+    int cb_one_idx = def_one.GetIdx("CB");
+    int cb_two_idx = def_two.GetIdx("CB");
+    int cb_three_idx = def_three.GetIdx("CB");
+
+    int def_one_size = def_one.anames.size();
+    int def_two_size = def_two.anames.size();
+
+    n_one_idx += res_start_idx;
+    ca_one_idx += res_start_idx;
+    c_one_idx += res_start_idx;
+    n_two_idx += (res_start_idx + def_one_size); 
+    ca_two_idx += (res_start_idx + def_one_size); 
+    c_two_idx += (res_start_idx + def_one_size); 
+    n_three_idx += (res_start_idx + def_one_size + def_two_size); 
+    ca_three_idx += (res_start_idx + def_one_size + def_two_size); 
+    c_three_idx += (res_start_idx + def_one_size + def_two_size);
+
+    if(cb_one_idx != -1) {
+      cb_one_idx += res_start_idx;
+    }
+
+    if(cb_two_idx != -1) {
+      cb_two_idx += (res_start_idx + def_one_size);
+    }
+
+    if(cb_three_idx != -1) {
+      cb_three_idx += (res_start_idx + def_one_size + def_two_size);
+    }
+
+    // derive parameters to reconstruct c_two
+    /////////////////////////////////////////
+    Real da_c_two = geom::DihedralAngle(positions[ca_one_idx],
+                                        positions[ca_three_idx],
+                                        positions[ca_two_idx],
+                                        ref_positions[c_two_idx]);
+    Real a_c_two = geom::Angle(positions[ca_three_idx] - positions[ca_two_idx],
+                               ref_positions[c_two_idx] - positions[ca_two_idx]);
+    uint8_t int_da_c_two = round((da_c_two + M_PI)/(2*M_PI)*255);
+    uint8_t int_a_c_two = round((a_c_two)/(M_PI)*255);
+    geom::Vec3 reconstructed_c_two;
+    ConstructAtomPos(positions[ca_one_idx], positions[ca_three_idx],
+                     positions[ca_two_idx], CA_C_Bond(def_two.olc),
+                     static_cast<Real>(int_a_c_two)/255*M_PI,
+                     static_cast<Real>(int_da_c_two)/255*2*M_PI-M_PI,
+                     reconstructed_c_two);
+    max_error = std::max(max_error, geom::Distance(reconstructed_c_two,
+                                                   ref_positions[c_two_idx]));
+
+    // derive parameters to reconstruct n_two
+    /////////////////////////////////////////
+    Real da_n_two = geom::DihedralAngle(positions[ca_one_idx],
+                                        positions[ca_three_idx],
+                                        positions[ca_two_idx],
+                                        ref_positions[n_two_idx]);
+    Real a_n_two = geom::Angle(positions[ca_three_idx] - positions[ca_two_idx],
+                               ref_positions[n_two_idx] - positions[ca_two_idx]);
+    uint8_t int_da_n_two = round((da_n_two + M_PI)/(2*M_PI)*255);
+    uint8_t int_a_n_two = round((a_n_two)/(M_PI)*255);
+    geom::Vec3 reconstructed_n_two;
+    ConstructAtomPos(positions[ca_one_idx], positions[ca_three_idx],
+                     positions[ca_two_idx], N_CA_Bond(def_two.olc),
+                     static_cast<Real>(int_a_n_two)/255*M_PI,
+                     static_cast<Real>(int_da_n_two)/255*2*M_PI-M_PI,
+                     reconstructed_n_two);
+    max_error = std::max(max_error, geom::Distance(reconstructed_n_two,
+                                                   ref_positions[n_two_idx]));
+
+    // derive parameters to reconstruct n_three
+    ///////////////////////////////////////////
+    Real da_n_three = geom::DihedralAngle(reconstructed_n_two,
+                                          positions[ca_two_idx],
+                                          reconstructed_c_two,
+                                          ref_positions[n_three_idx]);
+    Real a_n_three = geom::Angle(positions[ca_two_idx] - reconstructed_c_two,
+                                 ref_positions[n_three_idx] - reconstructed_c_two);
+    uint8_t int_da_n_three = round((da_n_three + M_PI)/(2*M_PI)*255);
+    // store angle as diff to ideal angle
+    Real diff = a_n_three-CA_C_N_Angle(def_two.olc);
+    // quantization by 0.5 degrees => 0.0087 in radians
+    int int_diff = round(diff/0.0087);
+    // make it fit in 4 bits
+    uint8_t int_a_n_three = std::min(8, std::max(-7, int_diff)) + 7;
+    
+    geom::Vec3 reconstructed_n_three;
+    ConstructAtomPos(reconstructed_n_two,
+                     positions[ca_two_idx],
+                     reconstructed_c_two, PeptideBond(),
+                     CA_C_N_Angle(def_two.olc) + (int_a_n_three-7)*0.0087,
+                     static_cast<Real>(int_da_n_three)/255*2*M_PI-M_PI,
+                     reconstructed_n_three);
+    max_error = std::max(max_error, geom::Distance(reconstructed_n_three,
+                                                   ref_positions[n_three_idx]));
+
+    // derive parameters to reconstruct c_three
+    ///////////////////////////////////////////
+    Real da_c_three = geom::DihedralAngle(reconstructed_c_two,
+                                          reconstructed_n_three,
+                                          positions[ca_three_idx],
+                                          ref_positions[c_three_idx]);
+    Real a_c_three = geom::Angle(reconstructed_n_three - positions[ca_three_idx],
+                                 ref_positions[c_three_idx] - positions[ca_three_idx]);
+    uint8_t int_da_c_three = round((da_c_three + M_PI)/(2*M_PI)*255);
+    // store angle as diff to ideal angle derived from N_CA_C_Angle function
+    diff = a_c_three-N_CA_C_Angle(def_three.olc);
+    // quantization by 0.5 degrees => 0.0087 in radians
+    int_diff = round(diff/0.0087);
+    // make it fit in 4 bits
+    uint8_t int_a_c_three = std::min(8, std::max(-7, int_diff)) + 7;
+    
+    geom::Vec3 reconstructed_c_three;
+    ConstructAtomPos(reconstructed_c_two,
+                     reconstructed_n_three,
+                     positions[ca_three_idx], CA_C_Bond(def_three.olc),
+                     N_CA_C_Angle(def_three.olc) + (int_a_c_three-7)*0.0087,
+                     static_cast<Real>(int_da_c_three)/255*2*M_PI-M_PI,
+                     reconstructed_c_three);
+    max_error = std::max(max_error, geom::Distance(reconstructed_c_three,
+                                                   ref_positions[c_three_idx]));
+
+    // derive parameters to reconstruct c_one
+    /////////////////////////////////////////
+    Real da_c_one = geom::DihedralAngle(reconstructed_c_two,
+                                        positions[ca_two_idx],
+                                        reconstructed_n_two,
+                                        ref_positions[c_one_idx]);
+    Real a_c_one = geom::Angle(positions[ca_two_idx] - reconstructed_n_two,
+                               ref_positions[c_one_idx] - reconstructed_n_two);
+    uint8_t int_da_c_one = round((da_c_one + M_PI)/(2*M_PI)*255);
+    // store angle as diff to ideal peptide angle
+    diff = a_c_one-C_N_CA_Angle(def_two.olc);
+    // quantization by 0.5 degrees => 0.0087 in radians
+    int_diff = round(diff/0.0087);
+    // make it fit in 4 bits
+    uint8_t int_a_c_one = std::min(8, std::max(-7, int_diff)) + 7;
+    
+    geom::Vec3 reconstructed_c_one;
+    ConstructAtomPos(reconstructed_c_two,
+                     positions[ca_two_idx],
+                     reconstructed_n_two, PeptideBond(),
+                     C_N_CA_Angle(def_two.olc) + (int_a_c_one-7)*0.0087,
+                     static_cast<Real>(int_da_c_one)/255*2*M_PI-M_PI,
+                     reconstructed_c_one);
+    max_error = std::max(max_error, geom::Distance(reconstructed_c_one,
+                                                   ref_positions[c_one_idx]));
+
+    // derive parameters to reconstruct n_one
+    /////////////////////////////////////////
+    Real da_n_one = geom::DihedralAngle(reconstructed_n_two,
+                                        reconstructed_c_one,
+                                        positions[ca_one_idx],
+                                        ref_positions[n_one_idx]);
+    Real a_n_one = geom::Angle(reconstructed_c_one - positions[ca_one_idx],
+                               ref_positions[n_one_idx] - positions[ca_one_idx]);
+    uint8_t int_da_n_one = round((da_n_one + M_PI)/(2*M_PI)*255);
+    // store angle as diff to ideal angle derived from N_CA_C_Angle function
+    diff = a_n_one-N_CA_C_Angle(def_one.olc);
+    // quantization by 0.5 degrees => 0.0087 in radians
+    int_diff = round(diff/0.0087);
+    // make it fit in 4 bits
+    uint8_t int_a_n_one = std::min(8, std::max(-7, int_diff)) + 7;
+    
+    geom::Vec3 reconstructed_n_one;
+    ConstructAtomPos(reconstructed_n_two,
+                     reconstructed_c_one,
+                     positions[ca_one_idx], N_CA_Bond(def_one.olc),
+                     N_CA_C_Angle(def_one.olc) + (int_a_n_one-7)*0.0087,
+                     static_cast<Real>(int_da_n_one)/255*2*M_PI-M_PI,
+                     reconstructed_n_one);
+    max_error = std::max(max_error, geom::Distance(reconstructed_n_one,
+                                                   ref_positions[n_one_idx]));
+
+    // derive parameters to reconstruct cbetas
+    //////////////////////////////////////////
+    std::vector<uint8_t> cb_data;
+    geom::Vec3 reconstructed_cb_one;
+    geom::Vec3 reconstructed_cb_two;
+    geom::Vec3 reconstructed_cb_three;
+    if(cb_one_idx != -1) {
+      Real da_cb = geom::DihedralAngle(reconstructed_n_one,
+                                       reconstructed_c_one,
+                                       positions[ca_one_idx],
+                                       ref_positions[cb_one_idx]);
+      diff = da_cb - N_C_CA_CB_DiAngle(def_one.olc);
+      // quantization by 0.5 degrees => 0.0087 in radians
+      int_diff = round(diff/0.0087);
+      uint8_t int_da_cb = std::min(8, std::max(-7, int_diff)) + 7;
+
+      Real a_cb = geom::Angle(reconstructed_c_one - positions[ca_one_idx],
+                              ref_positions[cb_one_idx] - positions[ca_one_idx]);
+      diff = a_cb - C_CA_CB_Angle(def_one.olc);
+      // quantization by 0.5 degrees => 0.0087 in radians
+      int_diff = round(diff/0.0087);
+      uint8_t int_a_cb = std::min(8, std::max(-7, int_diff)) + 7;
+
+      ConstructAtomPos(reconstructed_n_one,
+                       reconstructed_c_one,
+                       positions[ca_one_idx], CA_CB_Bond(def_one.olc),
+                       C_CA_CB_Angle(def_one.olc) + (static_cast<int>(int_a_cb)-7)*0.0087,
+                       N_C_CA_CB_DiAngle(def_one.olc) + (int_da_cb-7) * 0.0087,
+                       reconstructed_cb_one);
+      max_error = std::max(max_error, geom::Distance(reconstructed_cb_one,
+                                                     ref_positions[cb_one_idx]));
+      cb_data.push_back(int_a_cb);
+      cb_data.back() += (int_da_cb << 4);
+    }
+
+    if(cb_two_idx != -1) {
+      Real da_cb = geom::DihedralAngle(reconstructed_n_two,
+                                       reconstructed_c_two,
+                                       positions[ca_two_idx],
+                                       ref_positions[cb_two_idx]);
+      diff = da_cb - N_C_CA_CB_DiAngle(def_two.olc);
+      // quantization by 0.5 degrees => 0.0087 in radians
+      int_diff = round(diff/0.0087);
+      uint8_t int_da_cb = std::min(8, std::max(-7, int_diff)) + 7;
+
+      Real a_cb = geom::Angle(reconstructed_c_two - positions[ca_two_idx],
+                              ref_positions[cb_two_idx] - positions[ca_two_idx]);
+      diff = a_cb - C_CA_CB_Angle(def_two.olc);
+      // quantization by 0.5 degrees => 0.0087 in radians
+      int_diff = round(diff/0.0087);
+      uint8_t int_a_cb = std::min(8, std::max(-7, int_diff)) + 7;
+
+      ConstructAtomPos(reconstructed_n_two,
+                       reconstructed_c_two,
+                       positions[ca_two_idx], CA_CB_Bond(def_two.olc),
+                       C_CA_CB_Angle(def_two.olc) + (int_a_cb-7)*0.0087,
+                       N_C_CA_CB_DiAngle(def_two.olc) + (int_da_cb-7) * 0.0087,
+                       reconstructed_cb_two);
+      max_error = std::max(max_error, geom::Distance(reconstructed_cb_two,
+                                                     ref_positions[cb_two_idx]));
+      cb_data.push_back(int_a_cb);
+      cb_data.back() += (int_da_cb << 4);
+    } 
+
+    if(cb_three_idx != -1) {
+      Real da_cb = geom::DihedralAngle(reconstructed_n_three,
+                                       reconstructed_c_three,
+                                       positions[ca_three_idx],
+                                       ref_positions[cb_three_idx]);
+      diff = da_cb - N_C_CA_CB_DiAngle(def_three.olc);
+      // quantization by 0.5 degrees => 0.0087 in radians
+      int_diff = round(diff/0.0087);
+      uint8_t int_da_cb = std::min(8, std::max(-7, int_diff)) + 7;
+
+      Real a_cb = geom::Angle(reconstructed_c_three - positions[ca_three_idx],
+                              ref_positions[cb_three_idx] - positions[ca_three_idx]);
+      diff = a_cb - C_CA_CB_Angle(def_three.olc);
+      // quantization by 0.5 degrees => 0.0087 in radians
+      int_diff = round(diff/0.0087);
+      uint8_t int_a_cb = std::min(8, std::max(-7, int_diff)) + 7;
+
+      ConstructAtomPos(reconstructed_n_three,
+                       reconstructed_c_three,
+                       positions[ca_three_idx], CA_CB_Bond(def_three.olc),
+                       C_CA_CB_Angle(def_three.olc) + (int_a_cb-7)*0.0087,
+                       N_C_CA_CB_DiAngle(def_three.olc) + (int_da_cb-7) * 0.0087,
+                       reconstructed_cb_three);
+      max_error = std::max(max_error, geom::Distance(reconstructed_cb_three,
+                                                     ref_positions[cb_three_idx]));
+      cb_data.push_back(int_a_cb);
+      cb_data.back() += (int_da_cb << 4);
+    } 
+
+    if(max_error < error_thresh) {
+      positions[n_one_idx] = reconstructed_n_one;
+      positions[c_one_idx] = reconstructed_c_one;
+      positions[n_two_idx] = reconstructed_n_two;
+      positions[c_two_idx] = reconstructed_c_two;
+      positions[n_three_idx] = reconstructed_n_three;
+      positions[c_three_idx] = reconstructed_c_three;
+
+      if(cb_one_idx != -1) {
+        positions[cb_one_idx] = reconstructed_cb_one;
+      }
+
+      if(cb_two_idx != -1) {
+        positions[cb_two_idx] = reconstructed_cb_two;
+      }
+
+      if(cb_three_idx != -1) {
+        positions[cb_three_idx] = reconstructed_cb_three;
+      }
+
+      FillInferredTriPeptideIndices(def_one, def_two, def_three, res_idx,
+                                    skip_indices);
+
+      // push back dihedrals to data
+      data.push_back(int_da_c_two);
+      data.push_back(int_da_n_two);
+      data.push_back(int_da_n_three);
+      data.push_back(int_da_c_three);
+      data.push_back(int_da_c_one);
+      data.push_back(int_da_n_one);
+
+      // push back angles to data
+      data.push_back(int_a_c_two);
+      data.push_back(int_a_n_two);
+
+      // angle diffs have some bit shifting magic
+      data.push_back(int_a_n_three + (int_a_c_three << 4));
+      data.push_back(int_a_c_one + (int_a_n_one << 4));
+
+      // add cb data
+      data.insert(data.end(), cb_data.begin(), cb_data.end());
+
+      return true;
+    }
+    return false;
+  }
+
+  bool EncodePepRotamer(const ost::io::ResidueDefinition& def, Real error_thresh,
+                        const std::vector<geom::Vec3>& ref_positions,
+                        int res_idx, int res_start_idx,
+                        std::vector<std::set<int> >& skip_indices,
+                        std::vector<geom::Vec3>& positions,
+                        std::vector<uint8_t>& chi_angles) {
+
+    int res_n_atoms = def.anames.size();
+    std::vector<geom::Vec3> res_ref_positions(ref_positions.begin() + res_start_idx,
+                                              ref_positions.begin() + res_start_idx + res_n_atoms);
+    std::vector<geom::Vec3> res_positions(positions.begin() + res_start_idx,
+                                          positions.begin() + res_start_idx + res_n_atoms);
+    std::vector<bool> angle_set(def.chi_definitions.size(), false);
+    std::vector<uint8_t> comp_angles(def.chi_definitions.size(), 0);
+
+    const std::vector<ost::io::SidechainAtomRule>& at_rules =
+    def.GetSidechainAtomRules();
+    for(auto it = at_rules.begin(); it != at_rules.end(); ++it) {
+      Real dihedral = it->base_dihedral;
+      if(it->dihedral_idx != 4) {
+        if(!angle_set[it->dihedral_idx]) {
+          const ost::io::ChiDefinition& chi_def = def.chi_definitions[it->dihedral_idx];
+          Real a = geom::DihedralAngle(res_positions[chi_def.idx_one],
+                                       res_positions[chi_def.idx_two],
+                                       res_positions[chi_def.idx_three],
+                                       res_ref_positions[chi_def.idx_four]);
+          comp_angles[it->dihedral_idx] = std::round((a + M_PI)/(2*M_PI)*255);
+          angle_set[it->dihedral_idx] = true;
+        }
+        uint8_t comp_angle = comp_angles[it->dihedral_idx];
+        dihedral += static_cast<Real>(comp_angle)/255*2*M_PI-M_PI;
+      }
+      ConstructAtomPos(res_positions[it->anchor_idx[0]],
+                       res_positions[it->anchor_idx[1]],
+                       res_positions[it->anchor_idx[2]],
+                       it->bond_length, it->angle, dihedral,
+                       res_positions[it->sidechain_atom_idx]);
+      if(geom::Distance(res_positions[it->sidechain_atom_idx],
+                        res_ref_positions[it->sidechain_atom_idx]) > error_thresh) {
+        return false;
+      }
+    }
+
+    chi_angles.insert(chi_angles.end(), comp_angles.begin(),
+                      comp_angles.end());
+    FillInferredRotIndices(def, res_idx, skip_indices);
+    for(auto it = at_rules.begin(); it != at_rules.end(); ++it) {
+      positions[res_start_idx + it->sidechain_atom_idx] =
+      res_positions[it->sidechain_atom_idx];
+    }
+    return true;
+  }
+
+  void DecodeTriPeptide(const ost::io::ResidueDefinition& def_one,
+                        const ost::io::ResidueDefinition& def_two,
+                        const ost::io::ResidueDefinition& def_three,
+                        int res_start_idx,
+                        uint8_t*& data,
+                        std::vector<geom::Vec3>& positions) {
+
+    int n_one_idx = def_one.GetIdx("N");
+    int ca_one_idx = def_one.GetIdx("CA");
+    int c_one_idx = def_one.GetIdx("C");
+    int n_two_idx = def_two.GetIdx("N");
+    int ca_two_idx = def_two.GetIdx("CA");
+    int c_two_idx = def_two.GetIdx("C");
+    int n_three_idx = def_three.GetIdx("N");
+    int ca_three_idx = def_three.GetIdx("CA");
+    int c_three_idx = def_three.GetIdx("C");
+    int cb_one_idx = def_one.GetIdx("CB");
+    int cb_two_idx = def_two.GetIdx("CB");
+    int cb_three_idx = def_three.GetIdx("CB");
+
+    int def_one_size = def_one.anames.size();
+    int def_two_size = def_two.anames.size();
+
+    n_one_idx += res_start_idx;
+    ca_one_idx += res_start_idx;
+    c_one_idx += res_start_idx;
+    n_two_idx += (res_start_idx + def_one_size);
+    ca_two_idx += (res_start_idx + def_one_size);
+    c_two_idx += (res_start_idx + def_one_size);
+    n_three_idx += (res_start_idx + def_one_size + def_two_size);
+    ca_three_idx += (res_start_idx + def_one_size + def_two_size);
+    c_three_idx += (res_start_idx + def_one_size + def_two_size);
+
+    if(cb_one_idx != -1) {
+      cb_one_idx += res_start_idx;
+    }
+
+    if(cb_two_idx != -1) {
+      cb_two_idx += (res_start_idx + def_one_size);
+    }
+
+    if(cb_three_idx != -1) {
+      cb_three_idx += (res_start_idx + def_one_size + def_two_size);
+    }
+
+    int int_da_c_two = data[0];
+    int int_da_n_two = data[1];
+    int int_da_n_three = data[2];
+    int int_da_c_three = data[3];
+    int int_da_c_one = data[4];
+    int int_da_n_one = data[5];
+
+    int int_a_c_two = data[6];
+    int int_a_n_two = data[7];
+
+    int int_a_n_three = data[8] & 15; // 15 => 0000 1111
+    int int_a_c_three = (data[8] & 240) >> 4; // 240 => 1111 0000
+
+    int int_a_c_one = data[9] & 15;
+    int int_a_n_one = (data[9] & 240) >> 4;
+
+    // update data ptr
+    data += 10;
+
+    ConstructAtomPos(positions[ca_one_idx],
+                     positions[ca_three_idx],
+                     positions[ca_two_idx], CA_C_Bond(def_two.olc),
+                     static_cast<Real>(int_a_c_two)/255*M_PI,
+                     static_cast<Real>(int_da_c_two)/255*2*M_PI-M_PI,
+                     positions[c_two_idx]);
+
+    ConstructAtomPos(positions[ca_one_idx],
+                     positions[ca_three_idx],
+                     positions[ca_two_idx], N_CA_Bond(def_two.olc),
+                     static_cast<Real>(int_a_n_two)/255*M_PI,
+                     static_cast<Real>(int_da_n_two)/255*2*M_PI-M_PI,
+                     positions[n_two_idx]);
+
+    ConstructAtomPos(positions[n_two_idx],
+                     positions[ca_two_idx],
+                     positions[c_two_idx], PeptideBond(),
+                     CA_C_N_Angle(def_two.olc) + (int_a_n_three-7)*0.0087,
+                     static_cast<Real>(int_da_n_three)/255*2*M_PI-M_PI,
+                     positions[n_three_idx]);
+
+    ConstructAtomPos(positions[c_two_idx],
+                     positions[n_three_idx],
+                     positions[ca_three_idx], CA_C_Bond(def_three.olc),
+                     N_CA_C_Angle(def_three.olc) + (int_a_c_three-7)*0.0087,
+                     static_cast<Real>(int_da_c_three)/255*2*M_PI-M_PI,
+                     positions[c_three_idx]);
+
+    ConstructAtomPos(positions[c_two_idx],
+                     positions[ca_two_idx],
+                     positions[n_two_idx], PeptideBond(),
+                     C_N_CA_Angle(def_two.olc) + (int_a_c_one-7)*0.0087,
+                     static_cast<Real>(int_da_c_one)/255*2*M_PI-M_PI,
+                     positions[c_one_idx]);
+
+    ConstructAtomPos(positions[n_two_idx],
+                     positions[c_one_idx],
+                     positions[ca_one_idx], N_CA_Bond(def_one.olc),
+                     N_CA_C_Angle(def_one.olc) + (int_a_n_one-7)*0.0087,
+                     static_cast<Real>(int_da_n_one)/255*2*M_PI-M_PI,
+                     positions[n_one_idx]);
+
+    if(cb_one_idx != -1) {
+      int int_a_cb = *data & 15;
+      int int_da_cb = (*data & 240) >> 4;
+      ++data;
+      ConstructAtomPos(positions[n_one_idx],
+                       positions[c_one_idx],
+                       positions[ca_one_idx], CA_CB_Bond(def_one.olc),
+                       C_CA_CB_Angle(def_one.olc) + (int_a_cb-7)*0.0087,
+                       N_C_CA_CB_DiAngle(def_one.olc) + (int_da_cb-7) * 0.0087,
+                       positions[cb_one_idx]); 
+    }
+
+    if(cb_two_idx != -1) {
+      int int_a_cb = *data & 15;
+      int int_da_cb = (*data & 240) >> 4;
+      ++data;
+      ConstructAtomPos(positions[n_two_idx],
+                       positions[c_two_idx],
+                       positions[ca_two_idx], CA_CB_Bond(def_two.olc),
+                       C_CA_CB_Angle(def_two.olc) + (int_a_cb-7)*0.0087,
+                       N_C_CA_CB_DiAngle(def_two.olc) + (int_da_cb-7) * 0.0087,
+                       positions[cb_two_idx]); 
+    }
+
+    if(cb_three_idx != -1) {
+      int int_a_cb = *data & 15;
+      int int_da_cb = (*data & 240) >> 4;
+      ++data;
+      ConstructAtomPos(positions[n_three_idx],
+                       positions[c_three_idx],
+                       positions[ca_three_idx], CA_CB_Bond(def_three.olc),
+                       C_CA_CB_Angle(def_three.olc) + (int_a_cb-7)*0.0087,
+                       N_C_CA_CB_DiAngle(def_three.olc) + (int_da_cb-7) * 0.0087,
+                       positions[cb_three_idx]); 
+    }
+  }
+
+  void DecodePepRotamer(const ost::io::ResidueDefinition& def,
+                        int res_start_idx, uint8_t*& data_ptr,
+                        std::vector<geom::Vec3>& positions) {
+    const std::vector<ost::io::SidechainAtomRule>& at_rules =
+    def.GetSidechainAtomRules();
+
+    std::vector<Real> dihedral_angles;
+    for(int i = 0; i < def.GetNChiAngles(); ++i) {
+      dihedral_angles.push_back(static_cast<Real>(*data_ptr)/255*2*M_PI-M_PI);
+      ++data_ptr;
+    }
+
+    for(auto it = at_rules.begin(); it != at_rules.end(); ++it) {
+      Real dihedral = it->base_dihedral;
+      if(it->dihedral_idx != 4) {
+        dihedral += dihedral_angles[it->dihedral_idx];
+      }
+      ConstructAtomPos(positions[res_start_idx+it->anchor_idx[0]],
+                       positions[res_start_idx+it->anchor_idx[1]],
+                       positions[res_start_idx+it->anchor_idx[2]],
+                       it->bond_length, it->angle, dihedral,
+                       positions[res_start_idx+it->sidechain_atom_idx]);
+    }
+  }
+
   // some hash function we need for an unordered_map
   // stolen from https://stackoverflow.com/questions/32685540/why-cant-i-compile-an-unordered-map-with-a-pair-as-key
   struct pair_hash {
@@ -697,23 +1464,6 @@ namespace{
     DumpPosVec(stream, z_pos, lossy);
   }
 
-  void DumpDihedrals(std::ostream& stream, const std::vector<Real>& dihedrals) {
-    std::vector<int> int_vec(dihedrals.size());
-    for(size_t i = 0; i < dihedrals.size(); ++i) {
-      int_vec[i] = round((dihedrals[i] + M_PI)/(2*M_PI)*255);
-    }
-    Dump(stream, int_vec);
-  }
-
-  void LoadDihedrals(std::istream& stream, std::vector<Real>& dihedrals) {
-    std::vector<int> int_vec;
-    Load(stream, int_vec);
-    dihedrals.resize(int_vec.size());
-    for(size_t i = 0; i < int_vec.size(); ++i) {
-      dihedrals[i] = static_cast<Real>(int_vec[i])/255*2*M_PI-M_PI;
-    }
-  }
-
   void LoadBFactors(std::istream& stream, std::vector<Real>& bfactors,
                     bool round_bfactors) {
 
@@ -1246,150 +1996,170 @@ void ChainData::ToStream(std::ostream& stream,
   }
 
   if(infer_pos) {
-    geom::Vec3List positions_to_dump;
-    std::vector<Real> pep_chi_angles;
-    std::vector<bool> pep_oxygen_compression;
-    std::vector<bool> pep_rotamer_compression;
-    positions_to_dump.reserve(positions.size());
-    int res_start_idx = 0;
+
     int n_res = res_def_indices.size();
+
+    // required info for peptide specific compression
+    std::vector<uint8_t> pep_chi_data;
+    std::vector<uint8_t> pep_bb_data;
+    std::vector<bool> pep_rotamer_compression;
+    std::vector<bool> pep_bb_compression;
+    std::vector<bool> pep_o_compression;
+
+    // all indices that can be inferred come in here and won't be dumped
+    std::vector<std::set<int> > skip_indices(n_res, std::set<int>());
+
+    // check if we have any peptide residue
+    bool pep_present = false;
     for(int res_idx = 0; res_idx < n_res; ++res_idx) {
       const ResidueDefinition& def = res_def[res_def_indices[res_idx]];
-      std::set<int> skip_indices;
-      int res_n_atoms = def.anames.size();
       if(def.chem_type == 'A') {
-        pep_oxygen_compression.push_back(false);
-        pep_rotamer_compression.push_back(false);
-
-        // can reconstruct O if there is CA, C, no OXT, its not the last
-        // residue (res_idx < res_def_indices.size()-1), the next residue is
-        // an amino acid too and has N.
-        int ca_idx = def.GetIdx("CA");
-        int c_idx = def.GetIdx("C");
-        int o_idx = def.GetIdx("O");
-        int oxt_idx = def.GetIdx("OXT");
-        if(ca_idx != -1 && c_idx != -1 && oxt_idx == -1 && res_idx < n_res-1 &&
-           o_idx != -1) {
-          const ResidueDefinition next_def = res_def[res_def_indices[res_idx+1]];
-          int n_idx_next = next_def.GetIdx("N");
-          if(next_def.chem_type == 'A' && n_idx_next != -1) {
-            // compute error when anchor atoms have reduced accuracy
-            geom::Vec3 ca_pos = positions[res_start_idx + ca_idx];
-            geom::Vec3 c_pos = positions[res_start_idx + c_idx];
-            geom::Vec3 n_pos = positions[res_start_idx + res_n_atoms +
-                                         n_idx_next];
-            geom::Vec3 o_pos = positions[res_start_idx + o_idx];
-            if(lossy) {
-              // mimic lossy behaviour and reconstruct with reduced accuracy
-              // so we can still guarantee a hard threshold of 0.5A
-              ca_pos[0] = 0.1*std::round(ca_pos[0]*10);
-              ca_pos[1] = 0.1*std::round(ca_pos[1]*10);
-              ca_pos[2] = 0.1*std::round(ca_pos[2]*10);
-              c_pos[0] = 0.1*std::round(c_pos[0]*10);
-              c_pos[1] = 0.1*std::round(c_pos[1]*10);
-              c_pos[2] = 0.1*std::round(c_pos[2]*10);
-              n_pos[0] = 0.1*std::round(n_pos[0]*10);
-              n_pos[1] = 0.1*std::round(n_pos[1]*10);
-              n_pos[2] = 0.1*std::round(n_pos[2]*10);
-            }
-            geom::Vec3 reconstructed_o_pos;
-            ConstructOPos(ca_pos, c_pos, n_pos, reconstructed_o_pos);
-            if(geom::Length2(reconstructed_o_pos - o_pos) <= Real(0.25)) {
-              pep_oxygen_compression.back() = true;
-              skip_indices.insert(o_idx);
-            }
-          }
+        pep_present = true;
+        break;
+      }
+    }
+
+    if(pep_present) {
+      // check for peptide specific compressions
+      // same as positions but possibly with reduced accuracy
+      std::vector<geom::Vec3> comp_positions = positions;
+      if(lossy) {
+        for(auto it = comp_positions.begin(); it != comp_positions.end(); ++it) {
+          (*it)[0] = 0.1*std::round((*it)[0]*10);
+          (*it)[1] = 0.1*std::round((*it)[1]*10);
+          (*it)[2] = 0.1*std::round((*it)[2]*10);
         }
-        if(!def.GetRotamericAtoms().empty()) {
-          std::vector<geom::Vec3> res_pos(positions.begin() + res_start_idx,
-                                          positions.begin() + res_start_idx +
-                                          res_n_atoms);
-          std::vector<geom::Vec3> comp_res_pos;
-          if(lossy) {
-            // mimic lossy behaviour and reconstruct with reduced accuracy
-            // so we can still guarantee a hard threshold of 0.5A
-            for(auto it = res_pos.begin(); it != res_pos.end(); ++it) {
-              int x = std::round((*it)[0]*10);
-              int y = std::round((*it)[1]*10);
-              int z = std::round((*it)[2]*10);
-              comp_res_pos.push_back(geom::Vec3(0.1*x, 0.1*y, 0.1*z));
-            }
+      }
+
+      // check tripeptides that can reconstruct with error < 0.5A
+      int res_idx = 0;
+      int res_start_idx = 0;
+      while(res_idx < n_res-2) {
+
+        const ResidueDefinition& res_def_one = res_def[res_def_indices[res_idx]];
+        const ResidueDefinition& res_def_two = res_def[res_def_indices[res_idx+1]];
+        const ResidueDefinition& res_def_three = res_def[res_def_indices[res_idx+2]];
+        if(res_def_one.chem_type == 'A' && res_def_two.chem_type == 'A' &&
+           res_def_three.chem_type == 'A') {
+          pep_bb_compression.push_back(EncodeTriPeptide(res_def_one,
+                                                        res_def_two,
+                                                        res_def_three,
+                                                        0.5, positions,
+                                                        res_idx,
+                                                        res_start_idx,
+                                                        skip_indices,
+                                                        comp_positions,
+                                                        pep_bb_data));
+          if(pep_bb_compression.back()) {
+            res_idx += 3;
+            res_start_idx += res_def_one.anames.size();
+            res_start_idx += res_def_two.anames.size();
+            res_start_idx += res_def_three.anames.size();
           } else {
-            comp_res_pos = res_pos;
+            ++res_idx;
+            res_start_idx += res_def_one.anames.size();
           }
+        } else {
+          // just jump by one residue
+          ++res_idx;
+          res_start_idx += res_def_one.anames.size();
+        }
+      }
 
-          std::vector<Real> angles;
-          const std::vector<ChiDefinition>& chi_defs = def.GetChiDefinitions();
-          for(auto it = chi_defs.begin(); it != chi_defs.end(); ++it) {
-            angles.push_back(geom::DihedralAngle(res_pos[it->idx_one],
-                                                 res_pos[it->idx_two],
-                                                 res_pos[it->idx_three],
-                                                 res_pos[it->idx_four]));
-          }
-          // angles are compressed anyways, independent if lossy or not
-          std::vector<Real> comp_angles;
-          for(auto it = angles.begin(); it != angles.end(); ++it) {
-            int tmp = std::round((*it + M_PI)/(2*M_PI)*255);
-            comp_angles.push_back(static_cast<Real>(tmp)/255*2*M_PI-M_PI);
-          }
+      // check which residues fulfill 0.5A threshold when applying rotamer
+      // compression
+      res_start_idx = 0;
+      for(res_idx = 0; res_idx < n_res; ++res_idx) {
+        const ResidueDefinition& def = res_def[res_def_indices[res_idx]];
+        if(def.chem_type == 'A' && !def.GetRotamericAtoms().empty()) {
+          pep_rotamer_compression.push_back(EncodePepRotamer(def, 0.5,
+                                                             positions,
+                                                             res_idx,
+                                                             res_start_idx,
+                                                             skip_indices,
+                                                             comp_positions,
+                                                             pep_chi_data));
+        }
+        res_start_idx += def.anames.size();
+      }
 
-          const std::vector<SidechainAtomRule>& at_rules =
-          def.GetSidechainAtomRules();
-          for(auto it = at_rules.begin(); it != at_rules.end(); ++it) {
-            Real dihedral = it->base_dihedral;
-            if(it->dihedral_idx != 4) {
-              dihedral += comp_angles[it->dihedral_idx];
+      // check which oxygens can be reconstructed within 0.5A
+      res_start_idx = 0;
+      for(res_idx = 0; res_idx < n_res-1; ++res_idx) {
+        const ResidueDefinition& res_def_one = res_def[res_def_indices[res_idx]];
+        const ResidueDefinition& res_def_two = res_def[res_def_indices[res_idx+1]];
+        int n_atoms = res_def_one.anames.size();
+        if(res_def_one.chem_type == 'A' and res_def_two.chem_type == 'A') {
+          pep_o_compression.push_back(false);
+          int ca_idx = res_def_one.GetIdx("CA");
+          int c_idx = res_def_one.GetIdx("C");
+          int o_idx = res_def_one.GetIdx("O");
+          int oxt_idx = res_def_one.GetIdx("OXT");
+          int n_next_idx = res_def_two.GetIdx("N");
+          if(ca_idx!=-1 && c_idx!=-1 && o_idx!=-1 && n_next_idx!=-1 &&
+             oxt_idx==-1) {
+            geom::Vec3 reconstructed_o;
+            ConstructOPos(comp_positions[res_start_idx + ca_idx],
+                          comp_positions[res_start_idx + c_idx],
+                          comp_positions[res_start_idx + n_atoms + n_next_idx],
+                          reconstructed_o);
+            if(geom::Distance(positions[res_start_idx + o_idx],
+                              reconstructed_o) < 0.5) {
+              pep_o_compression.back() = true;
+              skip_indices[res_idx].insert(o_idx);
             }
-            ConstructAtomPos(comp_res_pos[it->anchor_idx[0]],
-                             comp_res_pos[it->anchor_idx[1]],
-                             comp_res_pos[it->anchor_idx[2]],
-                             it->bond_length, it->angle, dihedral,
-                             comp_res_pos[it->sidechain_atom_idx]);
-          }
-          Real max_d = 0.0;
-          for(size_t i = 0; i < res_pos.size(); ++i) {
-            max_d = std::max(max_d, geom::Length2(res_pos[i]-
-                                                  comp_res_pos[i]));
-          }
-          if(std::sqrt(max_d) <= Real(0.5)) {
-            pep_rotamer_compression.back() = true;
-            for(auto it = at_rules.begin(); it != at_rules.end(); ++it) {
-              skip_indices.insert(it->sidechain_atom_idx);
-            }
-            pep_chi_angles.insert(pep_chi_angles.end(), angles.begin(),
-                                  angles.end());
           }
         }
+        res_start_idx += n_atoms;
       }
-      for(int a_idx = 0; a_idx < res_n_atoms; ++a_idx) {
-        // skips atoms in skip_indices
-        if(skip_indices.find(a_idx) == skip_indices.end()) {
-          positions_to_dump.push_back(positions[res_start_idx+a_idx]);
-        }
-      }
-      res_start_idx += res_n_atoms;
-    }
+    } // done peptide compression
+
     int8_t flags = 0;
-    if(!pep_chi_angles.empty()) {
+    if(!pep_chi_data.empty()) {
       flags += 1;
     }
-    if(!pep_oxygen_compression.empty()) {
+    if(!pep_bb_data.empty()) {
       flags += 2;
     }
-    if(!pep_rotamer_compression.empty()) {
+    if(!pep_o_compression.empty()) {
       flags += 4;
     }
+
     stream.write(reinterpret_cast<char*>(&flags), sizeof(uint8_t));
-    if(!pep_chi_angles.empty()) {
-      DumpDihedrals(stream, pep_chi_angles);
+    if(!pep_chi_data.empty()) {
+      DumpIntVec(stream, pep_chi_data);
+      Dump(stream, pep_rotamer_compression);
     }
-    if(!pep_oxygen_compression.empty()) {
-      Dump(stream, pep_oxygen_compression);
+    if(!pep_bb_data.empty()) {
+      DumpIntVec(stream, pep_bb_data);
+      Dump(stream, pep_bb_compression);
     }
-    if(!pep_rotamer_compression.empty()) {
-      Dump(stream, pep_rotamer_compression);
+    if(!pep_o_compression.empty()) {
+      Dump(stream, pep_o_compression);
+    }
+
+    // construct vector containing all positions that cannot be inferred
+    geom::Vec3List positions_to_dump;
+    int res_start_idx = 0;
+    for(int res_idx = 0; res_idx < n_res; ++res_idx) {
+      const ResidueDefinition& def = res_def[res_def_indices[res_idx]];
+      int res_n_atoms = def.anames.size();
+
+      if(skip_indices[res_idx].empty()) {
+        positions_to_dump.insert(positions_to_dump.end(),
+                                 positions.begin() + res_start_idx,
+                                 positions.begin() + res_start_idx + res_n_atoms);
+      } else {
+        for(int at_idx = 0; at_idx < res_n_atoms; ++at_idx) {
+          if(skip_indices[res_idx].find(at_idx) == skip_indices[res_idx].end()) {
+            positions_to_dump.push_back(positions[res_start_idx + at_idx]);
+          }
+        }
+      }
+      res_start_idx += res_n_atoms;
     }
     DumpPositions(stream, positions_to_dump, lossy);
+
   } else {
     DumpPositions(stream, positions, lossy);
   }
@@ -1428,101 +2198,168 @@ void ChainData::FromStream(std::istream& stream,
   }
   
   if(infer_pos) {
-    std::vector<Real> pep_chi_angles;
-    std::vector<bool> pep_oxygen_compression;
+
+    int n_res = res_def_indices.size();
+    int n_at = 0;
+    for(auto it = res_def_indices.begin(); it != res_def_indices.end(); ++it) {
+      n_at += res_def[*it].anames.size();
+    }
+
+    std::vector<uint8_t> pep_chi_data;
+    std::vector<uint8_t> pep_bb_data;
+    std::vector<bool> pep_bb_compression;
     std::vector<bool> pep_rotamer_compression;
+    std::vector<bool> pep_o_compression;
+
     int8_t flags = 0;
     stream.read(reinterpret_cast<char*>(&flags), sizeof(uint8_t));
     if(flags & 1) {
-      LoadDihedrals(stream, pep_chi_angles);
+      LoadIntVec(stream, pep_chi_data);
+      Load(stream, pep_rotamer_compression);
     }
     if(flags & 2) {
-      Load(stream, pep_oxygen_compression);
+      LoadIntVec(stream, pep_bb_data);
+      Load(stream, pep_bb_compression);
     }
     if(flags & 4) {
-      Load(stream, pep_rotamer_compression);
+      Load(stream, pep_o_compression);
     }
 
-    LoadPositions(stream, positions, lossy);
+    // Check which atoms are inferred from the different compression strategies
+    std::vector<std::set<int> > inferred_indices(n_res);
+
+    if(!pep_bb_compression.empty()) {
+      int res_idx = 0;
+      int bb_comp_idx = 0;
+      while(res_idx < n_res-2) {
+        const ResidueDefinition& res_def_one = res_def[res_def_indices[res_idx]];
+        const ResidueDefinition& res_def_two = res_def[res_def_indices[res_idx+1]];
+        const ResidueDefinition& res_def_three = res_def[res_def_indices[res_idx+2]];
+        if(res_def_one.chem_type == 'A' && res_def_two.chem_type == 'A' &&
+           res_def_three.chem_type == 'A') {
+          if(pep_bb_compression[bb_comp_idx++]) {
+            FillInferredTriPeptideIndices(res_def_one, res_def_two, res_def_three,
+                                          res_idx, inferred_indices);
+            res_idx += 3;
+          } else {
+            res_idx += 1;
+          }
+        }
+      }
+    }
 
-    int n_res = res_def_indices.size();
-    int n_at = 0;
-    for(auto it = res_def_indices.begin(); it != res_def_indices.end(); ++it) {
-      n_at += res_def[*it].anames.size();
+    if(!pep_rotamer_compression.empty()) {
+      int rot_comp_idx = 0;
+      for(int res_idx = 0; res_idx < n_res; ++res_idx) {
+        const ResidueDefinition& def = res_def[res_def_indices[res_idx]];
+        if(def.chem_type == 'A' && !def.GetRotamericAtoms().empty() &&
+           pep_rotamer_compression[rot_comp_idx++]) {
+          FillInferredRotIndices(def, res_idx, inferred_indices);
+        }
+      }
     }
+
+    if(!pep_o_compression.empty()) {
+      int o_comp_idx = 0;
+      for(int res_idx = 0; res_idx < n_res-1; ++res_idx) {
+        const ResidueDefinition& res_def_one = res_def[res_def_indices[res_idx]];
+        const ResidueDefinition& res_def_two = res_def[res_def_indices[res_idx+1]];
+        if(res_def_one.chem_type == 'A' && res_def_two.chem_type == 'A' &&
+           pep_o_compression[o_comp_idx++]) {
+          inferred_indices[res_idx].insert(res_def_one.GetIdx("O"));
+        }
+      }
+    }
+
+    // fill the positions we have
+    LoadPositions(stream, positions, lossy);
     geom::Vec3List full_positions(n_at);
-    std::vector<bool> infer_pep_oxygen(n_res, false);
-    std::vector<bool> infer_pep_rotamer(n_res, false);
 
     int pos_idx = 0;
     int full_pos_idx = 0;
-    int pep_oxygen_compression_idx = 0;
-    int pep_rotamer_compression_idx = 0;
     for(int res_idx = 0; res_idx < n_res; ++res_idx) {
       const ResidueDefinition& def = res_def[res_def_indices[res_idx]];
       int n_res_at = def.anames.size();
-      if(def.chem_type == 'A') {
-        std::set<int> inferred_indices;
-        if(pep_oxygen_compression[pep_oxygen_compression_idx++]) {
-          inferred_indices.insert(def.GetIdx("O"));
-          infer_pep_oxygen[res_idx] = true;
-        }
-        if(pep_rotamer_compression[pep_rotamer_compression_idx++]) {
-          inferred_indices.insert(def.rotameric_atoms.begin(),
-                                  def.rotameric_atoms.end());
-          infer_pep_rotamer[res_idx] = true;
+      if(inferred_indices[res_idx].empty()) {
+        for(int i = 0; i < n_res_at; ++i) {
+          full_positions[full_pos_idx++] = positions[pos_idx++];
         }
+      } else {
         for(int i = 0; i < n_res_at; ++i) {
-          if(inferred_indices.find(i) == inferred_indices.end()) {
+          if(inferred_indices[res_idx].find(i) == inferred_indices[res_idx].end()) {
             full_positions[full_pos_idx++] = positions[pos_idx++];
           } else {
             ++full_pos_idx; // skip
           }
         }
-      } else {
-        // transfer all positions
-        for(int i = 0; i < n_res_at; ++i) {
-          full_positions[full_pos_idx++] = positions[pos_idx++];
-        }
       }
     }
 
-    // infer
-    int start_idx = 0;
-    int pep_chi_angles_idx = 0;
-    for(int res_idx = 0; res_idx < n_res; ++res_idx) {
-      const ResidueDefinition& def = res_def[res_def_indices[res_idx]];
-      int n_res_atoms = def.anames.size();
-      if(infer_pep_oxygen[res_idx]) {
-        const ResidueDefinition& next_def = res_def[res_def_indices[res_idx+1]];
-        int ca_idx = start_idx + def.GetIdx("CA");
-        int c_idx = start_idx + def.GetIdx("C");
-        int o_idx = start_idx + def.GetIdx("O");
-        int n_next_idx = start_idx + n_res_atoms + next_def.GetIdx("N");
-        ConstructOPos(full_positions[ca_idx], full_positions[c_idx],
-                      full_positions[n_next_idx], full_positions[o_idx]);
-      }
-      if(infer_pep_rotamer[res_idx]) {
-        const std::vector<SidechainAtomRule>& at_rules =
-        def.GetSidechainAtomRules();
-        std::vector<Real> dihedral_angles;
-        for(int i = 0; i < def.GetNChiAngles(); ++i) {
-          dihedral_angles.push_back(pep_chi_angles[pep_chi_angles_idx++]);
+    // reconstruct the rest
+
+    if(!pep_bb_compression.empty()) {
+      int res_idx = 0;
+      int bb_comp_idx = 0;
+      int res_start_idx = 0;
+      uint8_t* data_ptr = &pep_bb_data[0]; // ptr is updated in Decode calls
+      while(res_idx < n_res-2) {
+        const ResidueDefinition& res_def_one = res_def[res_def_indices[res_idx]];
+        const ResidueDefinition& res_def_two = res_def[res_def_indices[res_idx+1]];
+        const ResidueDefinition& res_def_three = res_def[res_def_indices[res_idx+2]];
+        if(res_def_one.chem_type == 'A' && res_def_two.chem_type == 'A' &&
+           res_def_three.chem_type == 'A') {
+          if(pep_bb_compression[bb_comp_idx++]) {
+            DecodeTriPeptide(res_def_one, res_def_two, res_def_three,
+                             res_start_idx, data_ptr, full_positions);
+            res_idx += 3;
+            res_start_idx += res_def_one.anames.size();
+            res_start_idx += res_def_two.anames.size();
+            res_start_idx += res_def_three.anames.size();
+          } else {
+            res_idx += 1;
+            res_start_idx += res_def_one.anames.size();
+          }
         }
-        for(auto it = at_rules.begin(); it != at_rules.end(); ++it) {
-          Real dihedral = it->base_dihedral;
-          if(it->dihedral_idx != 4) {
-            dihedral += dihedral_angles[it->dihedral_idx];
+      }
+    }
+
+    if(!pep_rotamer_compression.empty()) {
+      int res_start_idx = 0;
+      int rot_comp_idx = 0;
+      uint8_t* data_ptr = &pep_chi_data[0]; // ptr is updated in Decode calls
+      for(int res_idx = 0; res_idx < n_res; ++res_idx) {
+        const ResidueDefinition& def = res_def[res_def_indices[res_idx]];
+        if(def.chem_type == 'A' && !def.GetRotamericAtoms().empty()) {
+          if(pep_rotamer_compression[rot_comp_idx++]) {
+            DecodePepRotamer(def, res_start_idx, data_ptr, full_positions);
           }
-          ConstructAtomPos(full_positions[start_idx+it->anchor_idx[0]],
-                           full_positions[start_idx+it->anchor_idx[1]],
-                           full_positions[start_idx+it->anchor_idx[2]],
-                           it->bond_length, it->angle, dihedral,
-                           full_positions[start_idx+it->sidechain_atom_idx]);
         }
+        res_start_idx += def.anames.size();
+      }
+    }
+
+    if(!pep_o_compression.empty()) {
+      int res_start_idx = 0;
+      int o_comp_idx = 0;
+      for(int res_idx = 0; res_idx < n_res-1; ++res_idx) {
+        const ResidueDefinition& res_def_one = res_def[res_def_indices[res_idx]];
+        const ResidueDefinition& res_def_two = res_def[res_def_indices[res_idx+1]];
+        int n_atoms = res_def_one.anames.size();
+        if(res_def_one.chem_type == 'A' && res_def_two.chem_type == 'A' &&
+           pep_o_compression[o_comp_idx++]) {
+          int ca_idx = res_def_one.GetIdx("CA");
+          int c_idx = res_def_one.GetIdx("C");
+          int o_idx = res_def_one.GetIdx("O");
+          int n_next_idx = res_def_two.GetIdx("N");
+          ConstructOPos(full_positions[res_start_idx + ca_idx],
+                        full_positions[res_start_idx + c_idx],
+                        full_positions[res_start_idx + n_atoms + n_next_idx],
+                        full_positions[res_start_idx + o_idx]);
+        }
+        res_start_idx += n_atoms;
       }
-      start_idx += n_res_atoms;
     }
+
     std::swap(positions, full_positions);
   } else {
     LoadPositions(stream, positions, lossy);
@@ -1541,6 +2378,7 @@ void ChainData::FromStream(std::istream& stream,
 }
 
 DefaultPepLib::DefaultPepLib() {
+
   ResidueDefinition res_def;
   res_def = ResidueDefinition();
   res_def.name = "ALA";
@@ -1679,12 +2517,12 @@ DefaultPepLib::DefaultPepLib() {
   res_def._AddChiDefinition(1, 2, 4, 3);
   res_def._AddChiDefinition(2, 4, 3, 7);
   res_def._AddChiDefinition(4, 3, 7, 5);
-  res_def._AddAtomRule(4, 6, 1, 2, 1.5475, 2.0237, 0, 0.0);
-  res_def._AddAtomRule(3, 1, 2, 4, 1.5384, 1.9898, 1, 0.0);
-  res_def._AddAtomRule(7, 2, 4, 3, 1.5034, 1.8691, 2, 0.0);
-  res_def._AddAtomRule(5, 4, 3, 7, 1.3401, 2.1476, 3, 0.0);
-  res_def._AddAtomRule(8, 3, 7, 5, 1.3311, 2.0605, 4, 0.0);
-  res_def._AddAtomRule(9, 3, 7, 5, 1.3292, 2.1317, 4, M_PI);
+  res_def._AddAtomRule(4, 6, 1, 2, 1.52, 1.9867, 0, 0.0); // CG
+  res_def._AddAtomRule(3, 1, 2, 4, 1.52, 1.9511, 1, 0.0); // CD
+  res_def._AddAtomRule(7, 2, 4, 3, 1.46, 1.9492, 2, 0.0); // NE
+  res_def._AddAtomRule(5, 4, 3, 7, 1.33, 2.1780, 3, 0.0); // CZ
+  res_def._AddAtomRule(8, 3, 7, 5, 1.33, 2.1056, 4, 0.0); // NH1
+  res_def._AddAtomRule(9, 3, 7, 5, 1.33, 2.0879, 4, M_PI); // NH2
   res_def.rotameric_atoms.insert({4, 3, 7, 5, 8, 9});
   residue_definitions.push_back(res_def);
 
@@ -1812,9 +2650,9 @@ DefaultPepLib::DefaultPepLib() {
   res_def.bond_orders.push_back(2);
   res_def._AddChiDefinition(4, 1, 2, 3);
   res_def._AddChiDefinition(1, 2, 3, 7);
-  res_def._AddAtomRule(3, 4, 1, 2, 1.5319, 1.9949, 0, 0.0);
-  res_def._AddAtomRule(7, 1, 2, 3, 1.2323, 2.1391, 1, 0.0);
-  res_def._AddAtomRule(5, 1, 2, 3, 1.3521, 2.0272, 1, M_PI);
+  res_def._AddAtomRule(3, 4, 1, 2, 1.52, 1.9656, 0, 0.0); // CG
+  res_def._AddAtomRule(7, 1, 2, 3, 1.23, 2.1092, 1, 0.0); // OD1
+  res_def._AddAtomRule(5, 1, 2, 3, 1.33, 2.0330, 1, M_PI); // ND2
   res_def.rotameric_atoms.insert({3, 7, 5});
   residue_definitions.push_back(res_def);
 
@@ -1927,9 +2765,9 @@ DefaultPepLib::DefaultPepLib() {
   res_def.bond_orders.push_back(1);
   res_def._AddChiDefinition(4, 1, 2, 3);
   res_def._AddChiDefinition(1, 2, 3, 6);
-  res_def._AddAtomRule(3, 4, 1, 2, 1.5218, 1.9652, 0, 0.0);
-  res_def._AddAtomRule(6, 1, 2, 3, 1.2565, 2.0593, 1, 0.0);
-  res_def._AddAtomRule(7, 1, 2, 3, 1.2541, 2.0543, 1, M_PI);
+  res_def._AddAtomRule(3, 4, 1, 2, 1.52, 1.9733, 0, 0.0); // CG
+  res_def._AddAtomRule(6, 1, 2, 3, 1.25, 2.0808, 1, 0.0); // OD1
+  res_def._AddAtomRule(7, 1, 2, 3, 1.25, 2.0633, 1, M_PI); // OD2
   res_def.rotameric_atoms.insert({3, 6, 7});
   residue_definitions.push_back(res_def);
 
@@ -2048,10 +2886,10 @@ DefaultPepLib::DefaultPepLib() {
   res_def._AddChiDefinition(5, 1, 2, 4);
   res_def._AddChiDefinition(1, 2, 4, 3);
   res_def._AddChiDefinition(2, 4, 3, 8);
-  res_def._AddAtomRule(4, 5, 1, 2, 1.5534, 2.0162, 0, 0.0);
-  res_def._AddAtomRule(3, 1, 2, 4, 1.5320, 1.9635, 1, 0.0);
-  res_def._AddAtomRule(8, 2, 4, 3, 1.2294, 2.1209, 2, 0.0);
-  res_def._AddAtomRule(6, 2, 4, 3, 1.3530, 2.0392, 2, M_PI);
+  res_def._AddAtomRule(4, 5, 1, 2, 1.52, 1.9853, 0, 0.0); // CG
+  res_def._AddAtomRule(3, 1, 2, 4, 1.52, 1.9684, 1, 0.0); // CD
+  res_def._AddAtomRule(8, 2, 4, 3, 1.24, 2.1094, 2, 0.0); // OE1
+  res_def._AddAtomRule(6, 2, 4, 3, 1.33, 2.0333, 2, M_PI); // NE2
   res_def.rotameric_atoms.insert({4, 3, 8, 6});
   residue_definitions.push_back(res_def);
 
@@ -2175,10 +3013,10 @@ DefaultPepLib::DefaultPepLib() {
   res_def._AddChiDefinition(5, 1, 2, 4);
   res_def._AddChiDefinition(1, 2, 4, 3);
   res_def._AddChiDefinition(2, 4, 3, 7);
-  res_def._AddAtomRule(4, 5, 1, 2, 1.5557, 2.0192, 0, 0.0);
-  res_def._AddAtomRule(3, 1, 2, 4, 1.5307, 2.0199, 1, 0.0);
-  res_def._AddAtomRule(7, 2, 4, 3, 1.2590, 2.0070, 2, 0.0);
-  res_def._AddAtomRule(8, 2, 4, 3, 1.2532, 2.0958, 2, M_PI);
+  res_def._AddAtomRule(4, 5, 1, 2, 1.52, 1.9865, 0, 0.0); // CG
+  res_def._AddAtomRule(3, 1, 2, 4, 1.52, 1.9776, 1, 0.0); // CD
+  res_def._AddAtomRule(7, 2, 4, 3, 1.25, 2.0773, 2, 0.0); // OE1
+  res_def._AddAtomRule(8, 2, 4, 3, 1.25, 2.0609, 2, M_PI); // OE2
   res_def.rotameric_atoms.insert({4, 3, 7, 8});
   residue_definitions.push_back(res_def);
 
@@ -2303,10 +3141,10 @@ DefaultPepLib::DefaultPepLib() {
   res_def._AddChiDefinition(1, 2, 5, 3);
   res_def._AddChiDefinition(2, 5, 3, 4);
   res_def._AddChiDefinition(5, 3, 4, 7);
-  res_def._AddAtomRule(5, 6, 1, 2, 1.5435, 2.0204, 0, 0.0);
-  res_def._AddAtomRule(3, 1, 2, 5, 1.5397, 1.9771, 1, 0.0);
-  res_def._AddAtomRule(4, 2, 5, 3, 1.5350, 1.9605, 2, 0.0);
-  res_def._AddAtomRule(7, 5, 3, 4, 1.4604, 1.9279, 3, 0.0);
+  res_def._AddAtomRule(5, 6, 1, 2, 1.52, 1.9867, 0, 0.0); // CG
+  res_def._AddAtomRule(3, 1, 2, 5, 1.52, 1.9511, 1, 0.0); // CD
+  res_def._AddAtomRule(4, 2, 5, 3, 1.46, 1.9492, 2, 0.0); // CE
+  res_def._AddAtomRule(7, 5, 3, 4, 1.33, 2.1780, 3, 0.0); // NZ
   res_def.rotameric_atoms.insert({5, 3, 4, 7});
   residue_definitions.push_back(res_def);
 
@@ -2413,7 +3251,7 @@ DefaultPepLib::DefaultPepLib() {
   res_def.bond_orders.push_back(1);
   res_def.bond_orders.push_back(1);
   res_def._AddChiDefinition(3, 1, 2, 5);
-  res_def._AddAtomRule(5, 3, 1, 2, 1.4341, 1.9626, 0, 0.0);
+  res_def._AddAtomRule(5, 3, 1, 2, 1.417, 1.9334, 0, 0.0); // OG
   res_def.rotameric_atoms.insert(5);
   residue_definitions.push_back(res_def);
 
@@ -2505,7 +3343,7 @@ DefaultPepLib::DefaultPepLib() {
   res_def.bond_orders.push_back(1);
   res_def.bond_orders.push_back(1);
   res_def._AddChiDefinition(3, 1, 2, 5);
-  res_def._AddAtomRule(5, 3, 1, 2, 1.8359, 1.9874, 0, 0.0);
+  res_def._AddAtomRule(5, 3, 1, 2, 1.808, 1.9865, 0, 0.0); // SG
   res_def.rotameric_atoms.insert(5);
   residue_definitions.push_back(res_def);
 
@@ -2548,7 +3386,7 @@ DefaultPepLib::DefaultPepLib() {
   res_def.bond_orders.push_back(1);
   res_def.bond_orders.push_back(1);
   res_def._AddChiDefinition(3, 1, 2, 6);
-  res_def._AddAtomRule(6, 3, 1, 2, 1.8359, 1.9874, 0, 0.0);
+  res_def._AddAtomRule(6, 3, 1, 2, 1.808, 1.9865, 0, 0.0); // SG
   res_def.rotameric_atoms.insert(6);
   residue_definitions.push_back(res_def);
 
@@ -2608,9 +3446,9 @@ DefaultPepLib::DefaultPepLib() {
   res_def._AddChiDefinition(5, 1, 2, 4);
   res_def._AddChiDefinition(1, 2, 4, 7);
   res_def._AddChiDefinition(2, 4, 7, 3);
-  res_def._AddAtomRule(4, 5, 1, 2, 1.5460, 2.0232, 0, 0.0);
-  res_def._AddAtomRule(7, 1, 2, 4, 1.8219, 1.9247, 1, 0.0);
-  res_def._AddAtomRule(3, 2, 4, 7, 1.8206, 1.7268, 2, 0.0);
+  res_def._AddAtomRule(4, 5, 1, 2, 1.52, 1.9841, 0, 0.0); // CG
+  res_def._AddAtomRule(7, 1, 2, 4, 1.81, 1.9668, 1, 0.0); // SD
+  res_def._AddAtomRule(3, 2, 4, 7, 1.79, 1.7560, 2, 0.0); // CE
   res_def.rotameric_atoms.insert({4, 7, 3});
   residue_definitions.push_back(res_def);
 
@@ -2665,9 +3503,9 @@ DefaultPepLib::DefaultPepLib() {
   res_def._AddChiDefinition(5, 1, 2, 4);
   res_def._AddChiDefinition(1, 2, 4, 8);
   res_def._AddChiDefinition(2, 4, 8, 3);
-  res_def._AddAtomRule(4, 5, 1, 2, 1.5460, 2.0232, 0, 0.0);
-  res_def._AddAtomRule(8, 1, 2, 4, 1.8219, 1.9247, 1, 0.0);
-  res_def._AddAtomRule(3, 2, 4, 8, 1.8206, 1.7268, 2, 0.0);
+  res_def._AddAtomRule(4, 5, 1, 2, 1.52, 1.9841, 0, 0.0); // CG
+  res_def._AddAtomRule(8, 1, 2, 4, 1.81, 1.9668, 1, 0.0); // SD
+  res_def._AddAtomRule(3, 2, 4, 8, 1.79, 1.7560, 2, 0.0); // CE
   res_def.rotameric_atoms.insert({4, 8, 3});
   residue_definitions.push_back(res_def);
 
@@ -2762,15 +3600,15 @@ DefaultPepLib::DefaultPepLib() {
   res_def.bond_orders.push_back(1);
   res_def._AddChiDefinition(11, 1, 2, 7);
   res_def._AddChiDefinition(1, 2, 7, 3);
-  res_def._AddAtomRule(7, 11, 1, 2, 1.5233, 2.0096, 0, 0.0);
-  res_def._AddAtomRule(3, 1, 2, 7,  1.3679, 2.2546, 1, 0.0);
-  res_def._AddAtomRule(4, 1, 2, 7,  1.4407, 2.1633, 1, M_PI);
-  res_def._AddAtomRule(5, 3, 7, 4,  1.4126, 1.8614, 4, 0.0);
-  res_def._AddAtomRule(12, 7, 4, 5, 1.3746, 1.8827, 4, 0.0);
-  res_def._AddAtomRule(6, 3, 7, 4,  1.4011, 2.3133, 4, M_PI);
-  res_def._AddAtomRule(10, 5, 4, 6, 1.4017, 2.0623, 4, 0.0);
-  res_def._AddAtomRule(8, 4, 6, 10, 1.4019, 2.1113, 4, 0.0);
-  res_def._AddAtomRule(9, 6, 10, 8, 1.4030, 2.1096, 4, 0.0);
+  res_def._AddAtomRule(7, 11, 1, 2, 1.50, 1.9914, 0, 0.0); // CG
+  res_def._AddAtomRule(3, 1, 2, 7,  1.37, 2.2178, 1, 0.0); // CD1
+  res_def._AddAtomRule(4, 1, 2, 7,  1.43, 2.2106, 1, M_PI); // CD2
+  res_def._AddAtomRule(5, 3, 7, 4,  1.40, 1.8937, 4, 0.0); // CE2
+  res_def._AddAtomRule(12, 2, 7, 3, 1.38, 1.8937, 4, M_PI); // NE1 => changed atoms
+  res_def._AddAtomRule(6, 3, 7, 4,  1.40, 2.3358, 4, M_PI); // CE3
+  res_def._AddAtomRule(10, 5, 4, 6, 1.40, 2.0944, 4, 0.0); // CZ3
+  res_def._AddAtomRule(8, 4, 6, 10, 1.40, 2.0944, 4, 0.0); // CH2
+  res_def._AddAtomRule(9, 6, 10, 8, 1.40, 2.0944, 4, 0.0); // CZ2
   res_def.rotameric_atoms.insert({7, 3, 4, 5, 12, 6, 10, 8, 9});
   residue_definitions.push_back(res_def);
 
@@ -2942,13 +3780,13 @@ DefaultPepLib::DefaultPepLib() {
   res_def.bond_orders.push_back(1);
   res_def._AddChiDefinition(9, 1, 2, 7);
   res_def._AddChiDefinition(1, 2, 7, 3);
-  res_def._AddAtomRule(7, 9, 1, 2, 1.5113, 1.9712, 0, 0.0);
-  res_def._AddAtomRule(3, 1, 2, 7, 1.4064, 2.1029, 1, 0.0);
-  res_def._AddAtomRule(4, 1, 2, 7, 1.4068, 2.1024, 1, M_PI);
-  res_def._AddAtomRule(5, 4, 7, 3, 1.4026, 2.1014, 4, 0.0);
-  res_def._AddAtomRule(6, 3, 7, 4, 1.4022, 2.1042, 4, 0.0);
-  res_def._AddAtomRule(8, 7, 3, 5, 1.3978, 2.0960, 4, 0.0);
-  res_def._AddAtomRule(11, 4, 6, 8, 1.4063, 2.0988, 4, M_PI);
+  res_def._AddAtomRule(7, 9, 1, 2, 1.51, 1.9862, 0, 0.0); // CG
+  res_def._AddAtomRule(3, 1, 2, 7, 1.39, 2.1115, 1, 0.0); // CD1
+  res_def._AddAtomRule(4, 1, 2, 7, 1.39, 2.1087, 1, M_PI); // CD2
+  res_def._AddAtomRule(5, 4, 7, 3, 1.39, 2.0944, 4, 0.0); // CE1
+  res_def._AddAtomRule(6, 3, 7, 4, 1.39, 2.0944, 4, 0.0); // CE2
+  res_def._AddAtomRule(8, 7, 3, 5, 1.39, 2.0944, 4, 0.0); // CZ
+  res_def._AddAtomRule(11, 3, 5, 8, 1.39, 2.0906, 4, M_PI); // OH
   res_def.rotameric_atoms.insert({7, 3, 4, 5, 6, 8, 11});
   residue_definitions.push_back(res_def);
 
@@ -3078,8 +3916,8 @@ DefaultPepLib::DefaultPepLib() {
   res_def.bond_orders.push_back(1);
   res_def.bond_orders.push_back(1);
   res_def._AddChiDefinition(4, 1, 2, 6);
-  res_def._AddAtomRule(6, 4, 1, 2, 1.4252, 1.9576, 0, 0.0);
-  res_def._AddAtomRule(3, 6, 1, 2, 1.5324, 2.0230, 4, -2.1665);
+  res_def._AddAtomRule(6, 4, 1, 2, 1.43, 1.9056, 0, 0.0); // OG1
+  res_def._AddAtomRule(3, 4, 1, 2, 1.53, 1.9396, 0, -2.0944); // CG2
   res_def.rotameric_atoms.insert({6, 3});
   residue_definitions.push_back(res_def);
 
@@ -3181,8 +4019,13 @@ DefaultPepLib::DefaultPepLib() {
   res_def.bond_orders.push_back(1);
   res_def.bond_orders.push_back(1);
   res_def._AddChiDefinition(5, 1, 2, 3);
-  res_def._AddAtomRule(3, 5, 1, 2, 1.5441, 1.9892, 0, 0.0);
-  res_def._AddAtomRule(4, 3, 1, 2, 1.5414, 1.9577, 4, 2.1640);
+  res_def._AddAtomRule(3, 5, 1, 2, 1.527, 1.9321, 0, 0.0); // CG1
+  //res_def._AddAtomRule(4, 3, 1, 2, 1.527, 1.9268, 4, 2.1640); // CG2
+  res_def._AddAtomRule(4, 3, 1, 2, 1.527, 1.9268, 4, 2.0857); // CG2
+
+
+
+
   res_def.rotameric_atoms.insert({3, 4});
   residue_definitions.push_back(res_def);
 
@@ -3290,9 +4133,10 @@ DefaultPepLib::DefaultPepLib() {
   res_def.bond_orders.push_back(1);
   res_def._AddChiDefinition(6, 1, 2, 4);
   res_def._AddChiDefinition(1, 2, 4, 3);
-  res_def._AddAtomRule(4, 6, 1, 2, 1.5498, 1.9832, 0, 0.0);
-  res_def._AddAtomRule(5, 4, 1, 2, 1.5452, 1.9885, 4, -2.2696);
-  res_def._AddAtomRule(3, 1, 2, 4, 1.5381, 1.9912, 1, 0.0);
+  res_def._AddAtomRule(4, 6, 1, 2, 1.527, 1.9321, 0, 0.0); // CG1
+  //res_def._AddAtomRule(5, 4, 1, 2, 1.527, 1.9268, 4, -2.2696); // CG2
+  res_def._AddAtomRule(5, 4, 1, 2, 1.527, 1.9268, 4, -2.1174); // CG2
+  res_def._AddAtomRule(3, 1, 2, 4, 1.52, 1.9892, 1, 0.0); // CD1
   res_def.rotameric_atoms.insert({4, 5, 3});
   residue_definitions.push_back(res_def);
 
@@ -3405,9 +4249,12 @@ DefaultPepLib::DefaultPepLib() {
   res_def.bond_orders.push_back(1);
   res_def._AddChiDefinition(6, 1, 2, 5);
   res_def._AddChiDefinition(1, 2, 5, 3);
-  res_def._AddAtomRule(5, 6, 1, 2, 1.5472, 2.0501, 0, 0.0);
-  res_def._AddAtomRule(3, 1, 2, 5, 1.5361, 1.9282, 1, 0.0);
-  res_def._AddAtomRule(4, 3, 2, 5, 1.5360, 1.9647, 4, 2.0944);
+  res_def._AddAtomRule(5, 6, 1, 2, 1.53, 2.0263, 0, 0.0); // CG
+  res_def._AddAtomRule(3, 1, 2, 5, 1.524, 1.9246, 1, 0.0); // CD1
+  //res_def._AddAtomRule(4, 3, 2, 5, 1.525, 1.9300, 4, 2.0944); // CD2
+  res_def._AddAtomRule(4, 3, 2, 5, 1.525, 1.9300, 4, 1.8895); // CD2
+
+
   res_def.rotameric_atoms.insert({5, 3, 4});
   residue_definitions.push_back(res_def);
 
@@ -3583,8 +4430,8 @@ DefaultPepLib::DefaultPepLib() {
   res_def.bond_orders.push_back(1);
   res_def._AddChiDefinition(5, 1, 2, 4);
   res_def._AddChiDefinition(1, 2, 4, 3);
-  res_def._AddAtomRule(4, 5, 1, 2, 1.5322, 1.8219, 0, 0.0);
-  res_def._AddAtomRule(3, 1, 2, 4, 1.5317, 1.8014, 1, 0.0);
+  res_def._AddAtomRule(4, 5, 1, 2, 1.49, 1.8188, 0, 0.0); // CG
+  res_def._AddAtomRule(3, 1, 2, 4, 1.50, 1.8331, 1, 0.0); // CD
   res_def.rotameric_atoms.insert({4, 3});
   residue_definitions.push_back(res_def);
 
@@ -3708,11 +4555,11 @@ DefaultPepLib::DefaultPepLib() {
   res_def.bond_orders.push_back(1);
   res_def._AddChiDefinition(6, 1, 2, 5);
   res_def._AddChiDefinition(1, 2, 5, 7);
-  res_def._AddAtomRule(5, 6, 1, 2, 1.5109, 2.0410, 0, 0.0);
-  res_def._AddAtomRule(7, 1, 2, 5, 1.3859, 2.0974, 1, 0.0);
-  res_def._AddAtomRule(3, 1, 2, 5, 1.3596, 2.2639, 1, M_PI);
-  res_def._AddAtomRule(4, 3, 5, 7, 1.3170, 1.8361, 4, 0.0);
-  res_def._AddAtomRule(8, 7, 5, 3, 1.3782, 1.8466, 4, 0.0);
+  res_def._AddAtomRule(5, 6, 1, 2, 1.49, 1.9851, 0, 0.0); // CG
+  res_def._AddAtomRule(7, 1, 2, 5, 1.38, 2.1441, 1, 0.0); // ND1
+  res_def._AddAtomRule(3, 1, 2, 5, 1.35, 2.2796, 1, M_PI); // CD2
+  res_def._AddAtomRule(4, 3, 5, 7, 1.32, 1.8937, 4, 0.0); // CE1
+  res_def._AddAtomRule(8, 7, 5, 3, 1.35, 1.8937, 4, 0.0); // NE2
   res_def.rotameric_atoms.insert({5, 7, 3, 4, 8});
   residue_definitions.push_back(res_def);
 
@@ -3856,12 +4703,12 @@ DefaultPepLib::DefaultPepLib() {
   res_def.bond_orders.push_back(1);
   res_def._AddChiDefinition(9, 1, 2, 7);
   res_def._AddChiDefinition(1, 2, 7, 3);
-  res_def._AddAtomRule(7, 9, 1, 2, 1.5109, 1.9680, 0, 0.0);
-  res_def._AddAtomRule(3, 1, 2, 7, 1.4059, 2.0100, 1, 0.0);
-  res_def._AddAtomRule(4, 1, 2, 7, 1.4062, 2.1077, 1, M_PI);
-  res_def._AddAtomRule(5, 4, 7, 3, 1.4006, 2.1054, 4, 0.0);
-  res_def._AddAtomRule(6, 3, 7, 4, 1.4002, 2.1052, 4, 0.0);
-  res_def._AddAtomRule(8, 7, 3, 5, 1.4004, 2.0932, 4, 0.0);
+  res_def._AddAtomRule(7, 9, 1, 2, 1.50, 1.9871, 0, 0.0); // CG
+  res_def._AddAtomRule(3, 1, 2, 7, 1.39, 2.0944, 1, 0.0); // CD1
+  res_def._AddAtomRule(4, 1, 2, 7, 1.39, 2.0944, 1, M_PI); // CD2
+  res_def._AddAtomRule(5, 4, 7, 3, 1.39, 2.0944, 4, 0.0); // CE1
+  res_def._AddAtomRule(6, 3, 7, 4, 1.39, 2.0944, 4, 0.0); // CE2
+  res_def._AddAtomRule(8, 7, 3, 5, 1.39, 2.0944, 4, 0.0); // CZ
   res_def.rotameric_atoms.insert({7, 3, 4, 5, 6, 8});
   residue_definitions.push_back(res_def);