diff --git a/modules/io/pymod/export_omf_io.cc b/modules/io/pymod/export_omf_io.cc index 43355a4a908f97ae3f567daba57f5f0e739c34c5..d2df0e47501ed6b716c0e5207ce91b0e53703aeb 100644 --- a/modules/io/pymod/export_omf_io.cc +++ b/modules/io/pymod/export_omf_io.cc @@ -61,6 +61,7 @@ void export_omf_io() { .value("ROUND_BFACTORS", OMF::ROUND_BFACTORS) .value("SKIP_SS", OMF::SKIP_SS) .value("INFER_PEP_BONDS", OMF::INFER_PEP_BONDS) + .value("INFER_AA_POS", OMF::INFER_AA_POS) ; class_<OMF, OMFPtr>("OMF",no_init) diff --git a/modules/io/src/mol/omf.cc b/modules/io/src/mol/omf.cc index 91fdca413267c42bf41582e4422a37149bdbb59c..822c24ca9a190bc3188f781f6be96eafa97e5e23 100644 --- a/modules/io/src/mol/omf.cc +++ b/modules/io/src/mol/omf.cc @@ -1,3 +1,5 @@ +#include <algorithm> + #include <ost/mol/atom_handle.hh> #include <ost/mol/residue_handle.hh> #include <ost/mol/chain_handle.hh> @@ -7,6 +9,72 @@ namespace{ + void ConstructOPos(const geom::Vec3& ca_pos, const geom::Vec3& c_pos, + const geom::Vec3& n_next_pos, geom::Vec3& o_pos) { + geom::Vec3 o_vec = geom::Normalize(geom::Normalize(c_pos - ca_pos) + + geom::Normalize(c_pos - n_next_pos)); + o_pos = c_pos + 1.230*o_vec; + } + + void ConstructAtomPos(const geom::Vec3& A, const geom::Vec3& B, + const geom::Vec3& C, Real bond_length, Real angle, + Real dihedral, geom::Vec3& pos) { + + Real x,xx,x1; + x = std::tan((Real(M_PI)-angle)*Real(0.5)); + xx = x*x; + x1 = Real(1.0)+xx; + Real bond_length_x_sin_angle = bond_length*Real(2.0)*x/x1; + Real bond_length_x_cos_angle = bond_length*(Real(1.0)-xx)/x1; + + x = std::tan(Real(0.5)*dihedral); + xx = x*x; + x1 = Real(1.0)+xx; + Real sin_dihedral = Real(2.0)*x/x1; + Real cos_dihedral = (Real(1.0)-xx)/x1; + + Real ab[] = {B[0]-A[0],B[1]-A[1],B[2]-A[2]}; + + Real norm_bc[] = {C[0]-B[0],C[1]-B[1],C[2]-B[2]}; + Real a = norm_bc[0] * norm_bc[0]; + a += norm_bc[1] * norm_bc[1]; + a += norm_bc[2] * norm_bc[2]; + a = Real(1.0) / std::sqrt(a); + + norm_bc[0]*=a; + norm_bc[1]*=a; + norm_bc[2]*=a; + + Real norm_n[] = {ab[1]*norm_bc[2]-norm_bc[1]*ab[2], + ab[2]*norm_bc[0]-norm_bc[2]*ab[0], + ab[0]*norm_bc[1]-norm_bc[0]*ab[1]}; + + a = norm_n[0]*norm_n[0]; + a += norm_n[1]*norm_n[1]; + a += norm_n[2]*norm_n[2]; + a = Real(1.0) / std::sqrt(a); + + norm_n[0]*=a; + norm_n[1]*=a; + norm_n[2]*=a; + + Real n_x_bc[] = {norm_n[1]*norm_bc[2]-norm_bc[1]*norm_n[2], + norm_n[2]*norm_bc[0]-norm_bc[2]*norm_n[0], + norm_n[0]*norm_bc[1]-norm_bc[0]*norm_n[1]}; + + pos[0] = norm_bc[0]*bond_length_x_cos_angle + + n_x_bc[0]*cos_dihedral*bond_length_x_sin_angle + + norm_n[0]*sin_dihedral*bond_length_x_sin_angle + C[0]; + + pos[1] = norm_bc[1]*bond_length_x_cos_angle + + n_x_bc[1]*cos_dihedral*bond_length_x_sin_angle + + norm_n[1]*sin_dihedral*bond_length_x_sin_angle + C[1]; + + pos[2] = norm_bc[2]*bond_length_x_cos_angle + + n_x_bc[2]*cos_dihedral*bond_length_x_sin_angle + + norm_n[2]*sin_dihedral*bond_length_x_sin_angle + C[2]; + } + // 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 { @@ -20,10 +88,10 @@ namespace{ } }; - // define hash function, so we can use ResidueDefinition as key in an unordered - // map. The used hash function is overly simple and gives a hash collision - // whenever we have two residues of same name but different atom composition. - // That's hopefully rare... + // define hash function, so we can use ResidueDefinition as key in an + // unordered map. The used hash function is overly simple and gives a hash + // collision whenever we have two residues of same name but different atom + // composition. That's hopefully rare... struct ResidueDefinitionHash { std::size_t operator()(const ost::io::ResidueDefinition& r) const { return std::hash<String>()(r.name); @@ -320,14 +388,14 @@ namespace{ std::map<String, ost::io::ChainDataPtr>& map, const std::vector<ost::io::ResidueDefinition>& res_def, int version, bool lossy, bool avg_bfactors, bool round_bfactors, - bool skip_ss) { + bool skip_ss, bool infer_aa_pos) { uint32_t size; stream.read(reinterpret_cast<char*>(&size), sizeof(uint32_t)); map.clear(); for(uint i = 0; i < size; ++i) { ost::io::ChainDataPtr p(new ost::io::ChainData); p->FromStream(stream, res_def, version, lossy, avg_bfactors, - round_bfactors, skip_ss); + round_bfactors, skip_ss, infer_aa_pos); map[p->ch_name] = p; } } @@ -336,14 +404,14 @@ namespace{ const std::map<String, ost::io::ChainDataPtr>& map, const std::vector<ost::io::ResidueDefinition>& res_def, bool lossy, bool avg_bfactors, bool round_bfactors, - bool skip_ss) { + bool skip_ss, bool infer_aa_pos) { uint32_t size = map.size(); stream.write(reinterpret_cast<char*>(&size), sizeof(uint32_t)); for(auto it = map.begin(); it != map.end(); ++it) { // we don't dump the key (chain name), that's an attribute of the // chain itself anyway it->second->ToStream(stream, res_def, lossy, avg_bfactors, - round_bfactors, skip_ss); + round_bfactors, skip_ss, infer_aa_pos); } } @@ -460,7 +528,8 @@ namespace{ } } - void Dump(std::ostream& stream, const std::vector<ost::io::ResidueDefinition>& vec) { + void Dump(std::ostream& stream, + const std::vector<ost::io::ResidueDefinition>& vec) { uint32_t size = vec.size(); stream.write(reinterpret_cast<char*>(&size), sizeof(uint32_t)); for(uint i = 0; i < size; ++i) { @@ -469,7 +538,8 @@ namespace{ } // dump and load vectors with BioUnitDefinition - void Load(std::istream& stream, std::vector<ost::io::BioUnitDefinition>& vec) { + void Load(std::istream& stream, + std::vector<ost::io::BioUnitDefinition>& vec) { uint32_t size; stream.read(reinterpret_cast<char*>(&size), sizeof(uint32_t)); vec.resize(size); @@ -478,7 +548,8 @@ namespace{ } } - void Dump(std::ostream& stream, const std::vector<ost::io::BioUnitDefinition>& vec) { + void Dump(std::ostream& stream, + const std::vector<ost::io::BioUnitDefinition>& vec) { uint32_t size = vec.size(); stream.write(reinterpret_cast<char*>(&size), sizeof(uint32_t)); for(uint i = 0; i < size; ++i) { @@ -500,7 +571,8 @@ namespace{ uint32_t size = vec.size(); stream.write(reinterpret_cast<char*>(&size), sizeof(uint32_t)); for(uint i = 0; i < size; ++i) { - stream.write(reinterpret_cast<const char*>(vec[i].Data()), 16*sizeof(Real)); + stream.write(reinterpret_cast<const char*>(vec[i].Data()), + 16*sizeof(Real)); } } @@ -598,6 +670,23 @@ 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) { @@ -664,7 +753,8 @@ namespace{ IntToRealVec(int_vec, occupancies, 0.01); } - void DumpOccupancies(std::ostream& stream, const std::vector<Real>& occupancies) { + void DumpOccupancies(std::ostream& stream, + const std::vector<Real>& occupancies) { std::vector<int> int_vec; RealToIntVec(occupancies, int_vec, 100.0); std::vector<int> run_length_encoded; @@ -680,7 +770,8 @@ namespace{ ins_codes.assign(int_vec.begin(), int_vec.end()); } - void DumpInsertionCodes(std::ostream& stream, const std::vector<char>& ins_codes) { + void DumpInsertionCodes(std::ostream& stream, + const std::vector<char>& ins_codes) { std::vector<int> int_vec(ins_codes.begin(), ins_codes.end()); std::vector<int> run_length_encoded; RunLengthEncoding(int_vec, run_length_encoded); @@ -707,7 +798,8 @@ namespace{ Load(stream, indices); } - void DumpResDefIndices(std::ostream& stream, const std::vector<int>& indices) { + void DumpResDefIndices(std::ostream& stream, + const std::vector<int>& indices) { Dump(stream, indices); } @@ -838,6 +930,7 @@ ResidueDefinition::ResidueDefinition(const ost::mol::ResidueHandle& res) { olc = res.GetOneLetterCode(); chem_type = char(res.GetChemType()); chem_class = char(res.GetChemClass()); + rotamer_setup = false; ost::mol::AtomHandleList at_list = res.GetAtomList(); for(auto it = at_list.begin(); it != at_list.end(); ++it) { anames.push_back(it->GetName()); @@ -859,7 +952,8 @@ ResidueDefinition::ResidueDefinition(const ost::mol::ResidueHandle& res) { std::unordered_map<std::pair<int, int>, int, pair_hash> bond_order_map; for(auto at_it = at_list.begin(); at_it != at_list.end(); ++at_it) { ost::mol::BondHandleList bond_list = at_it->GetBondList(); - for(auto bond_it = bond_list.begin(); bond_it != bond_list.end(); ++bond_it) { + for(auto bond_it = bond_list.begin(); bond_it != bond_list.end(); + ++bond_it) { // The atom represented by at_it is either first OR second atom of that // bond. So we check whether BOTH are in hash_idx_mapper to exclude bonds // to other residues. @@ -917,17 +1011,457 @@ void ResidueDefinition::ToStream(std::ostream& stream) const{ DumpBondOrders(stream, bond_orders); } +int ResidueDefinition::GetIdx(const String& aname) const { + if(idx_mapper.empty()){ + _InitIdxMapper(); + } + auto it = idx_mapper.find(aname); + if(it == idx_mapper.end()) { + return -1; + } else { + return it->second; + } +} + +const std::set<int>& ResidueDefinition::GetRotamericAtoms() const { + if(!rotamer_setup) { + _InitRotamer(); + } + return rotameric_atoms; +} + +const std::vector<ChiDefinition>& ResidueDefinition::GetChiDefinitions() const { + if(!rotamer_setup) { + _InitRotamer(); + } + return chi_definitions; +} + +const std::vector<SidechainAtomRule>& +ResidueDefinition::GetSidechainAtomRules() const { + if(!rotamer_setup) { + _InitRotamer(); + } + return sidechain_atom_rules; +} + +int ResidueDefinition::GetNChiAngles() const { + if(!rotamer_setup) { + _InitRotamer(); + } + return chi_definitions.size(); +} + +void ResidueDefinition::_InitIdxMapper() const { + idx_mapper.clear(); + for(size_t i = 0; i < anames.size(); ++i) { + idx_mapper[anames[i]] = i; + } +} + +void ResidueDefinition::_InitRotamer() const { + rotameric_atoms.clear(); + if(chem_type == 'A') { + int n_idx = GetIdx("N"); + int ca_idx = GetIdx("CA"); + int cb_idx = GetIdx("CB"); + if(n_idx!=-1 && ca_idx!=-1 && cb_idx!=-1) { + switch(olc) { + case 'R': { + int cg_idx = GetIdx("CG"); + int cd_idx = GetIdx("CD"); + int ne_idx = GetIdx("NE"); + int cz_idx = GetIdx("CZ"); + int nh1_idx = GetIdx("NH1"); + int nh2_idx = GetIdx("NH2"); + if(cg_idx!=-1 && cd_idx!=-1 && ne_idx!=-1 && cz_idx!=-1 && + nh1_idx!=-1 && nh2_idx!=-1) { + _AddChiDefinition(n_idx, ca_idx, cb_idx, cg_idx); + _AddChiDefinition(ca_idx, cb_idx, cg_idx, cd_idx); + _AddChiDefinition(cb_idx, cg_idx, cd_idx, ne_idx); + _AddChiDefinition(cg_idx, cd_idx, ne_idx, cz_idx); + _AddAtomRule(cg_idx, n_idx, ca_idx, cb_idx, + Real(1.5475), Real(2.02370926769), 0, Real(0.0)); + _AddAtomRule(cd_idx, ca_idx, cb_idx, cg_idx, + Real(1.5384), Real(1.9898498802), 1, Real(0.0)); + _AddAtomRule(ne_idx, cb_idx, cg_idx, cd_idx, + Real(1.5034), Real(1.86907309596), 2, Real(0.0)); + _AddAtomRule(cz_idx, cg_idx, cd_idx, ne_idx, + Real(1.3401), Real(2.14762764458), 3, Real(0.0)); + _AddAtomRule(nh1_idx, cd_idx, ne_idx, cz_idx, + Real(1.3311), Real(2.0605357149), 4, Real(0.0)); + _AddAtomRule(nh2_idx, cd_idx, ne_idx, cz_idx, + Real(1.3292), Real(2.13174514839), 4, Real(M_PI)); + } + break; + } + case 'N': { + int cg_idx = GetIdx("CG"); + int od1_idx = GetIdx("OD1"); + int nd2_idx = GetIdx("ND2"); + if(cg_idx!=-1 && od1_idx!=-1 && nd2_idx!=-1) { + _AddChiDefinition(n_idx, ca_idx, cb_idx, cg_idx); + _AddChiDefinition(ca_idx, cb_idx, cg_idx, od1_idx); + _AddAtomRule(cg_idx, n_idx, ca_idx, cb_idx, + Real(1.5319), Real(1.99491133503), 0, Real(0.0)); + _AddAtomRule(od1_idx, ca_idx, cb_idx, cg_idx, + Real(1.2323), Real(2.13907553124), 1, Real(0.0)); + _AddAtomRule(nd2_idx, ca_idx, cb_idx, cg_idx, + Real(1.3521), Real(2.02719992619), 1, Real(M_PI)); + } + break; + } + case 'D': { + int cg_idx = GetIdx("CG"); + int od1_idx = GetIdx("OD1"); + int od2_idx = GetIdx("OD2"); + if(cg_idx!=-1 && od1_idx!=-1 && od2_idx!=-1) { + _AddChiDefinition(n_idx, ca_idx, cb_idx, cg_idx); + _AddChiDefinition(ca_idx, cb_idx, cg_idx, od1_idx); + _AddAtomRule(cg_idx, n_idx, ca_idx, cb_idx, + Real(1.5218), Real(1.96524073775), 0, Real(0.0)); + _AddAtomRule(od1_idx, ca_idx, cb_idx, cg_idx, + Real(1.2565), Real(2.05931398443), 1, Real(0.0)); + _AddAtomRule(od2_idx, ca_idx, cb_idx, cg_idx, + Real(1.2541), Real(2.0542525296), 1, Real(M_PI)); + } + break; + } + case 'Q': { + int cg_idx = GetIdx("CG"); + int cd_idx = GetIdx("CD"); + int oe1_idx = GetIdx("OE1"); + int ne2_idx = GetIdx("NE2"); + if(cg_idx!=-1 && cd_idx!=-1 && oe1_idx!=-1 && ne2_idx!=-1) { + _AddChiDefinition(n_idx, ca_idx, cb_idx, cg_idx); + _AddChiDefinition(ca_idx, cb_idx, cg_idx, cd_idx); + _AddChiDefinition(cb_idx, cg_idx, cd_idx, oe1_idx); + _AddAtomRule(cg_idx, n_idx, ca_idx, cb_idx, + Real(1.5534), Real(2.0162043519), 0, Real(0.0)); + _AddAtomRule(cd_idx, ca_idx, cb_idx, cg_idx, + Real(1.532), Real(1.96349540849), 1, Real(0.0)); + _AddAtomRule(oe1_idx, cb_idx, cg_idx, cd_idx, + Real(1.2294), Real(2.12092410702), 2, Real(0.0)); + _AddAtomRule(ne2_idx, cb_idx, cg_idx, cd_idx, + Real(1.353), Real(2.03924269803), 2, Real(M_PI)); + } + break; + } + case 'E': { + int cg_idx = GetIdx("CG"); + int cd_idx = GetIdx("CD"); + int oe1_idx = GetIdx("OE1"); + int oe2_idx = GetIdx("OE2"); + if(cg_idx!=-1 && cd_idx!=-1 && oe1_idx!=-1 && oe2_idx!=-1) { + _AddChiDefinition(n_idx, ca_idx, cb_idx, cg_idx); + _AddChiDefinition(ca_idx, cb_idx, cg_idx, cd_idx); + _AddChiDefinition(cb_idx, cg_idx, cd_idx, oe1_idx); + _AddAtomRule(cg_idx, n_idx, ca_idx, cb_idx, + Real(1.5557), Real(2.01917141163), 0, Real(0.0)); + _AddAtomRule(cd_idx, ca_idx, cb_idx, cg_idx, + Real(1.5307), Real(2.01986954333), 1, Real(0.0)); + _AddAtomRule(oe1_idx, cb_idx, cg_idx, cd_idx, + Real(1.259), Real(2.00695410687), 2, Real(0.0)); + _AddAtomRule(oe2_idx, cb_idx, cg_idx, cd_idx, + Real(1.2532), Real(2.09579136579), 2, Real(M_PI)); + } + break; + } + case 'K': { + int cg_idx = GetIdx("CG"); + int cd_idx = GetIdx("CD"); + int ce_idx = GetIdx("CE"); + int nz_idx = GetIdx("NZ"); + if(cg_idx!=-1 && cd_idx!=-1 && ce_idx!=-1 && nz_idx!=-1) { + _AddChiDefinition(n_idx, ca_idx, cb_idx, cg_idx); + _AddChiDefinition(ca_idx, cb_idx, cg_idx, cd_idx); + _AddChiDefinition(cb_idx, cg_idx, cd_idx, ce_idx); + _AddChiDefinition(cg_idx, cd_idx, ce_idx, nz_idx); + _AddAtomRule(cg_idx, n_idx, ca_idx, cb_idx, + Real(1.5435), Real(2.02039314211), 0, Real(0.0)); + _AddAtomRule(cd_idx, ca_idx, cb_idx, cg_idx, + Real(1.5397), Real(1.97710897666), 1, Real(0.0)); + _AddAtomRule(ce_idx, cb_idx, cg_idx, cd_idx, + Real(1.535), Real(1.96052834877), 2, Real(0.0)); + _AddAtomRule(nz_idx, cg_idx, cd_idx, ce_idx, + Real(1.4604), Real(1.92789069175), 3, Real(0.0)); + } + break; + } + case 'S': { + int og_idx = GetIdx("OG"); + if(og_idx!=-1) { + _AddChiDefinition(n_idx, ca_idx, cb_idx, og_idx); + _AddAtomRule(og_idx, n_idx, ca_idx, cb_idx, + Real(1.4341), Real(1.96262274387), 0, Real(0.0)); + } + break; + } + case 'C': { + int sg_idx = GetIdx("SG"); + if(sg_idx!=-1) { + _AddChiDefinition(n_idx, ca_idx, cb_idx, sg_idx); + _AddAtomRule(sg_idx, n_idx, ca_idx, cb_idx, + Real(1.8359), Real(1.98740641925), 0, Real(0.0)); + } + break; + } + case 'M': { + int cg_idx = GetIdx("CG"); + int sd_idx = GetIdx("SD"); + int ce_idx = GetIdx("CE"); + if(cg_idx!=-1 && sd_idx!=-1 && ce_idx!=-1) { + _AddChiDefinition(n_idx, ca_idx, cb_idx, cg_idx); + _AddChiDefinition(ca_idx, cb_idx, cg_idx, sd_idx); + _AddChiDefinition(cb_idx, cg_idx, sd_idx, ce_idx); + _AddAtomRule(cg_idx, n_idx, ca_idx, cb_idx, + Real(1.546), Real(2.02318566891), 0, Real(0.0)); + _AddAtomRule(sd_idx, ca_idx, cb_idx, cg_idx, + Real(1.8219), Real(1.9247490991), 1, Real(0.0)); + _AddAtomRule(ce_idx, cb_idx, cg_idx, sd_idx, + Real(1.8206), Real(1.72682876192), 2, Real(0.0)); + } + break; + } + case 'W': { + int cg_idx = GetIdx("CG"); + int cd1_idx = GetIdx("CD1"); + int cd2_idx = GetIdx("CD2"); + int ne1_idx = GetIdx("NE1"); + int ce2_idx = GetIdx("CE2"); + int ce3_idx = GetIdx("CE3"); + int cz2_idx = GetIdx("CZ2"); + int cz3_idx = GetIdx("CZ3"); + int ch2_idx = GetIdx("CH2"); + if(cg_idx!=-1 && cd1_idx!=-1 && cd2_idx!=-1 && ne1_idx!=-1 && + ce2_idx!=-1 && ce3_idx!=-1 && cz2_idx!=-1 && cz3_idx!=-1 && + ch2_idx!=-1) { + _AddChiDefinition(n_idx, ca_idx, cb_idx, cg_idx); + _AddChiDefinition(ca_idx, cb_idx, cg_idx, cd1_idx); + _AddAtomRule(cg_idx, n_idx, ca_idx, cb_idx, + Real(1.5233), Real(2.00957210075), 0, Real(0.0)); + _AddAtomRule(cd1_idx, ca_idx, cb_idx, cg_idx, + Real(1.3679), Real(2.25461632773), 1, Real(0.0)); + _AddAtomRule(cd2_idx, ca_idx, cb_idx, cg_idx, + Real(1.4407), Real(2.16333560785), 1, Real(M_PI)); + _AddAtomRule(ce2_idx, cd1_idx, cg_idx, cd2_idx, + Real(1.4126), Real(1.86139364725), 4, Real(0.0)); + _AddAtomRule(ne1_idx, cg_idx, cd2_idx, ce2_idx, + Real(1.3746), Real(1.88268666413), 4, Real(0.0)); + _AddAtomRule(ce3_idx, cd1_idx, cg_idx, cd2_idx, + Real(1.4011), Real(2.31325939059), 4, Real(M_PI)); + _AddAtomRule(cz3_idx, ce2_idx, cd2_idx, ce3_idx, + Real(1.4017), Real(2.06228104416), 4, Real(0.0)); + _AddAtomRule(ch2_idx, cd2_idx, ce3_idx, cz3_idx, + Real(1.4019), Real(2.11132479614), 4, Real(0.0)); + _AddAtomRule(cz2_idx, ce3_idx, cz3_idx, ch2_idx, + Real(1.403), Real(2.10957946689), 4, Real(0.0)); + } + break; + } + case 'Y': { + int cg_idx = GetIdx("CG"); + int cd1_idx = GetIdx("CD1"); + int cd2_idx = GetIdx("CD2"); + int ce1_idx = GetIdx("CE1"); + int ce2_idx = GetIdx("CE2"); + int cz_idx = GetIdx("CZ"); + int oh_idx = GetIdx("OH"); + if(cg_idx!=-1 && cd1_idx!=-1 && cd2_idx!=-1 && ce1_idx!=-1 && + ce2_idx!=-1 && cz_idx!=-1 && oh_idx!=-1) { + _AddChiDefinition(n_idx, ca_idx, cb_idx, cg_idx); + _AddChiDefinition(ca_idx, cb_idx, cg_idx, cd1_idx); + _AddAtomRule(cg_idx, n_idx, ca_idx, cb_idx, + Real(1.5113), Real(1.9711748572), 0, Real(0.0)); + _AddAtomRule(cd1_idx, ca_idx, cb_idx, cg_idx, + Real(1.4064), Real(2.10294721573), 1, Real(0.0)); + _AddAtomRule(cd2_idx, ca_idx, cb_idx, cg_idx, + Real(1.4068), Real(2.10242361695), 1, Real(M_PI)); + _AddAtomRule(ce1_idx, cd2_idx, cg_idx, cd1_idx, + Real(1.4026), Real(2.1013764194), 4, Real(0.0)); + _AddAtomRule(ce2_idx, cd1_idx, cg_idx, cd2_idx, + Real(1.4022), Real(2.1041689462), 4, Real(0.0)); + _AddAtomRule(cz_idx, cg_idx, cd1_idx, ce1_idx, + Real(1.3978), Real(2.09596589872), 4, Real(0.0)); + _AddAtomRule(oh_idx, cd2_idx, ce2_idx, cz_idx, + Real(1.4063), Real(2.09875842552), 4, Real(M_PI)); + } + break; + } + case 'T': { + int og1_idx = GetIdx("OG1"); + int cg2_idx = GetIdx("CG1"); + if(og1_idx!=-1 && cg2_idx!=-1) { + _AddChiDefinition(n_idx, ca_idx, cb_idx, og1_idx); + _AddAtomRule(og1_idx, n_idx, ca_idx, cb_idx, + Real(1.4252), Real(1.95756128904), 0, Real(0.0)); + _AddAtomRule(cg2_idx, og1_idx, ca_idx, cb_idx, + Real(1.5324), Real(2.02301113599), 4, Real(-2.1665)); + } + break; + } + + case 'V': { + int cg1_idx = GetIdx("CG1"); + int cg2_idx = GetIdx("CG2"); + if(cg1_idx!=-1 && cg2_idx!=-1) { + _AddChiDefinition(n_idx, ca_idx, cb_idx, cg1_idx); + _AddAtomRule(cg1_idx, n_idx, ca_idx, cb_idx, + Real(1.5441), Real(1.9891517485), 0, Real(0.0)); + _AddAtomRule(cg2_idx, cg1_idx, ca_idx, cb_idx, + Real(1.5414), Real(1.95773582196), 4, Real(2.1640)); + } + break; + } + case 'I': { + int cg1_idx = GetIdx("CG1"); + int cg2_idx = GetIdx("CG2"); + int cd1_idx = GetIdx("CD1"); + if(cg1_idx!=-1 && cg2_idx!=-1 && cd1_idx!=-1) { + _AddChiDefinition(n_idx, ca_idx, cb_idx, cg1_idx); + _AddChiDefinition(ca_idx, cb_idx, cg1_idx, cd1_idx); + _AddAtomRule(cg1_idx, n_idx, ca_idx, cb_idx, + Real(1.5498), Real(1.98321762904), 0, Real(0.0)); + _AddAtomRule(cg2_idx, cg1_idx, ca_idx, cb_idx, + Real(1.5452), Real(1.9884536168), 4, Real(-2.2696)); + _AddAtomRule(cd1_idx, ca_idx, cb_idx, cg1_idx, + Real(1.5381), Real(1.9912461436), 1, Real(0.0)); + } + break; + } + case 'L': { + int cg_idx = GetIdx("CG"); + int cd1_idx = GetIdx("CD1"); + int cd2_idx = GetIdx("CD2"); + if(cg_idx!=-1 && cd1_idx!=-1 && cd2_idx!=-1) { + _AddChiDefinition(n_idx, ca_idx, cb_idx, cg_idx); + _AddChiDefinition(ca_idx, cb_idx, cg_idx, cd1_idx); + _AddAtomRule(cg_idx, n_idx, ca_idx, cb_idx, + Real(1.5472), Real(2.05006373939), 0, Real(0.0)); + _AddAtomRule(cd1_idx, ca_idx, cb_idx, cg_idx, + Real(1.5361), Real(1.9282397576), 1, Real(0.0)); + _AddAtomRule(cd2_idx, cd1_idx, cb_idx, cg_idx, + Real(1.536), Real(1.96471713897), 4, Real(2.0944)); + + } + break; + } + case 'P': { + int cg_idx = GetIdx("CG"); + int cd_idx = GetIdx("CD"); + if(cg_idx!=-1 && cd_idx!=-1) { + _AddChiDefinition(n_idx, ca_idx, cb_idx, cg_idx); + _AddChiDefinition(ca_idx, cb_idx, cg_idx, cd_idx); + _AddAtomRule(cg_idx, n_idx, ca_idx, cb_idx, + Real(1.5322), Real(1.82194920616), 0, Real(0.0)); + _AddAtomRule(cd_idx, ca_idx, cb_idx, cg_idx, + Real(1.5317), Real(1.80135432098), 1, Real(0.0)); + } + break; + } + case 'H': { + int cg_idx = GetIdx("CG"); + int nd1_idx = GetIdx("ND1"); + int cd2_idx = GetIdx("CD1"); + int ce1_idx = GetIdx("CE2"); + int ne2_idx = GetIdx("NE2"); + + if(cg_idx!=-1 && nd1_idx!=-1 && cd2_idx!=-1 && ce1_idx!=-1 && + ne2_idx!=-1) { + _AddChiDefinition(n_idx, ca_idx, cb_idx, cg_idx); + _AddChiDefinition(ca_idx, cb_idx, cg_idx, nd1_idx); + _AddAtomRule(cg_idx, n_idx, ca_idx, cb_idx, + Real(1.5109), Real(2.04098802728), 0, Real(0.0)); + _AddAtomRule(nd1_idx, ca_idx, cb_idx, cg_idx, + Real(1.3859), Real(2.09736216212), 1, Real(0.0)); + _AddAtomRule(cd2_idx, ca_idx, cb_idx, cg_idx, + Real(1.3596), Real(2.26386657276), 1, Real(M_PI)); + _AddAtomRule(ce1_idx, cd2_idx, cg_idx, nd1_idx, + Real(1.317), Real(1.8360863731), 4, Real(0.0)); + _AddAtomRule(ne2_idx, nd1_idx, cg_idx, cd2_idx, + Real(1.3782), Real(1.84655834861), 4, Real(0.0)); + } + break; + } + case 'F': { + int cg_idx = GetIdx("CG"); + int cd1_idx = GetIdx("CD1"); + int cd2_idx = GetIdx("CD2"); + int ce1_idx = GetIdx("CE1"); + int ce2_idx = GetIdx("CE2"); + int cz_idx = GetIdx("CZ"); + if(cg_idx!=-1 && cd1_idx!=-1 && cd2_idx!=-1 && ce1_idx!=-1 && + ce2_idx!=-1 && cz_idx!=-1) { + _AddChiDefinition(n_idx, ca_idx, cb_idx, cg_idx); + _AddChiDefinition(ca_idx, cb_idx, cg_idx, cd1_idx); + _AddAtomRule(cg_idx, n_idx, ca_idx, cb_idx, + Real(1.5109), Real(1.96803326455), 0, Real(0.0)); + _AddAtomRule(cd1_idx, ca_idx, cb_idx, cg_idx, + Real(1.4059), Real(2.099980156), 1, Real(0.0)); + _AddAtomRule(cd2_idx, ca_idx, cb_idx, cg_idx, + Real(1.4062), Real(2.10765960471), 1, Real(M_PI)); + _AddAtomRule(ce1_idx, cd2_idx, cg_idx, cd1_idx, + Real(1.4006), Real(2.10539067668), 4, Real(0.0)); + _AddAtomRule(ce2_idx, cd1_idx, cg_idx, cd2_idx, + Real(1.4002), Real(2.10521614376), 4, Real(0.0)); + _AddAtomRule(cz_idx, cg_idx, cd1_idx, ce1_idx, + Real(1.4004), Real(2.09317337192), 4, Real(0.0)); + } + break; + } + } + } + } + + for(auto it = sidechain_atom_rules.begin(); + it != sidechain_atom_rules.end(); ++it) { + rotameric_atoms.insert(it->sidechain_atom_idx); + } + + rotamer_setup = true; +} + +void ResidueDefinition::_AddChiDefinition(int idx_one, int idx_two, + int idx_three, int idx_four) const { + ChiDefinition chi_definition; + chi_definition.idx_one = idx_one; + chi_definition.idx_two = idx_two; + chi_definition.idx_three = idx_three; + chi_definition.idx_four = idx_four; + chi_definitions.push_back(chi_definition); + +} + +void ResidueDefinition::_AddAtomRule(int a_idx, int anch_one_idx, + int anch_two_idx, int anch_three_idx, + Real bond_length, Real angle, + int dihedral_idx, + Real base_dihedral) const { + SidechainAtomRule rule; + rule.sidechain_atom_idx = a_idx; + rule.anchor_idx[0] = anch_one_idx; + rule.anchor_idx[1] = anch_two_idx; + rule.anchor_idx[2] = anch_three_idx; + rule.bond_length = bond_length; + rule.angle = angle; + rule.dihedral_idx = dihedral_idx; + rule.base_dihedral = base_dihedral; + sidechain_atom_rules.push_back(rule); +} + BioUnitDefinition::BioUnitDefinition(const ost::io::MMCifInfoBioUnit& bu) { au_chains = bu.GetChainList(); - const std::vector<std::pair<int, int> >& bu_ch_intvl = bu.GetChainIntervalList(); + const std::vector<std::pair<int, int> >& bu_ch_intvl = + bu.GetChainIntervalList(); for(auto it = bu_ch_intvl.begin(); it != bu_ch_intvl.end(); ++it) { chain_intvl.push_back(it->first); chain_intvl.push_back(it->second); } - const std::vector<std::vector<MMCifInfoTransOpPtr> >& bu_op_list = bu.GetOperations(); + const std::vector<std::vector<MMCifInfoTransOpPtr> >& bu_op_list = + bu.GetOperations(); for(auto i = bu_op_list.begin(); i != bu_op_list.end(); ++i) { std::vector<geom::Mat4> mat_list; for(auto j = i->begin(); j != i->end(); ++j) { @@ -939,7 +1473,8 @@ BioUnitDefinition::BioUnitDefinition(const ost::io::MMCifInfoBioUnit& bu) { operations.push_back(mat_list); } - const std::vector<std::pair<int, int> >& bu_op_intvl = bu.GetOperationsIntervalList(); + const std::vector<std::pair<int, int> >& bu_op_intvl = + bu.GetOperationsIntervalList(); for(auto it = bu_op_intvl.begin(); it != bu_op_intvl.end(); ++it) { op_intvl.push_back(it->first); op_intvl.push_back(it->second); @@ -972,7 +1507,8 @@ void BioUnitDefinition::FromStream(std::istream& stream) { ChainData::ChainData(const ost::mol::ChainHandle& chain, const std::vector<ResidueDefinition>& residue_definitions, const std::unordered_map<unsigned long, int>& res_idx_map, - const std::vector<std::pair<unsigned long, unsigned long> >& + const std::vector<std::pair<unsigned long, + unsigned long> >& inter_residue_bonds, const std::vector<int>& inter_residue_bond_orders, std::unordered_map<long, int>& atom_idx_mapper) { @@ -1025,7 +1561,8 @@ ChainData::ChainData(const ost::mol::ChainHandle& chain, void ChainData::ToStream(std::ostream& stream, const std::vector<ResidueDefinition>& res_def, bool lossy, bool avg_bfactors, - bool round_bfactors, bool skip_ss) const { + bool round_bfactors, bool skip_ss, + bool infer_aa_pos) const { Dump(stream, ch_name); if(chain_type > std::numeric_limits<int8_t>::max()) { throw ost::Error("ChainType out of bounds"); @@ -1057,7 +1594,53 @@ void ChainData::ToStream(std::ostream& stream, DumpBFactors(stream, bfactors, round_bfactors); } - DumpPositions(stream, positions, lossy); + if(infer_aa_pos) { + geom::Vec3List positions_to_dump; + std::vector<Real> chi_angles; + positions_to_dump.reserve(positions.size()); + int res_start_idx = 0; + int n_res = res_def_indices.size(); + 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 = def.GetRotamericAtoms(); + int o_idx = -1; + if(def.chem_type == 'A') { + // can reconstruct O if there is CA, C, no OXT, its not the last + // residue (res_idx < res_def_indices.size()-1) and the next residue is + // an amino acid too and has N. + if(def.GetIdx("CA") != -1 && def.GetIdx("C") != -1 && + def.GetIdx("OXT") == -1 && res_idx < n_res-1) { + const ResidueDefinition next_def = res_def[res_def_indices[res_idx+1]]; + if(next_def.chem_type == 'A' && next_def.GetIdx("N") != -1) { + o_idx = def.GetIdx("O"); + } + } + } + if(o_idx != -1) { + skip_indices.insert(o_idx); + } + int n_atoms = res_def[res_def_indices[res_idx]].anames.size(); + for(int a_idx = 0; a_idx < 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]); + } + } + const std::vector<ChiDefinition>& chi_vec = def.GetChiDefinitions(); + for(auto it = chi_vec.begin(); it != chi_vec.end(); ++it) { + Real a = geom::DihedralAngle(positions[res_start_idx + it->idx_one], + positions[res_start_idx + it->idx_two], + positions[res_start_idx + it->idx_three], + positions[res_start_idx + it->idx_four]); + chi_angles.push_back(a); + } + res_start_idx += n_atoms; + } + DumpPositions(stream, positions_to_dump, lossy); + DumpDihedrals(stream, chi_angles); + } else { + DumpPositions(stream, positions, lossy); + } DumpBonds(stream, bonds); DumpBondOrders(stream, bond_orders); if(!skip_ss) { @@ -1068,7 +1651,8 @@ void ChainData::ToStream(std::ostream& stream, void ChainData::FromStream(std::istream& stream, const std::vector<ResidueDefinition>& res_def, int version, bool lossy, bool avg_bfactors, - bool round_bfactors, bool skip_ss) { + bool round_bfactors, bool skip_ss, + bool infer_aa_pos) { Load(stream, ch_name); if(version >= 2) { @@ -1092,6 +1676,94 @@ void ChainData::FromStream(std::istream& stream, LoadBFactors(stream, bfactors, round_bfactors); } LoadPositions(stream, positions, lossy); + if(infer_aa_pos) { + std::vector<Real> chi_angles; + LoadDihedrals(stream, chi_angles); + + 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(); + } + geom::Vec3List full_positions(n_at); + + int pos_idx = 0; + int full_pos_idx = 0; + std::vector<bool> infer_rotamer(n_res, false); + std::vector<bool> infer_oxygen(n_res, false); + 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(); + std::set<int> inferred_indices = def.GetRotamericAtoms(); + if(!inferred_indices.empty()) { + infer_rotamer[res_idx] = true; + } + if(def.chem_type == 'A') { + // can reconstruct O if there is CA, C no OXT, its not the last residue + // (res_idx < res_def.size()-1) and 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(o_idx != -1 && ca_idx != -1 && c_idx != -1 && oxt_idx == -1 && + res_idx < n_res-1) { + const ResidueDefinition& next = res_def[res_def_indices[res_idx+1]]; + if(next.chem_type == 'A' && next.GetIdx("N") != -1) { + inferred_indices.insert(o_idx); + infer_oxygen[res_idx] = true; + } + } + } + // transfer positions + for(int i = 0; i < n_res_at; ++i) { + if(inferred_indices.find(i) == inferred_indices.end()) { + full_positions[full_pos_idx++] = positions[pos_idx++]; + } else { + ++full_pos_idx; // skip + } + } + } + + // infer + + int start_idx = 0; + int chi_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_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_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(chi_angles[chi_idx++]); + } + 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(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]); + } + } + start_idx += n_res_atoms; + } + std::swap(positions, full_positions); + } LoadBonds(stream, bonds); LoadBondOrders(stream, bond_orders); if(skip_ss) { @@ -1158,8 +1830,9 @@ DefaultPepLib::DefaultPepLib() { print(" residue_definitions.push_back(res_def);") print() lib = conop.GetDefaultLib() - anames = ["ALA", "ARG", "ASN", "ASP", "GLN", "GLU", "LYS", "SER", "CYS", "MET", - "TRP", "TYR", "THR", "VAL", "ILE", "LEU", "GLY", "PRO", "HIS", "PHE"] + anames = ["ALA", "ARG", "ASN", "ASP", "GLN", "GLU", "LYS", "SER", "CYS", + "MET", "TRP", "TYR", "THR", "VAL", "ILE", "LEU", "GLY", "PRO", + "HIS", "PHE"] print(" ResidueDefinition res_def;") for aname in anames: ProcessCompound(aname, lib) @@ -3470,7 +4143,7 @@ OMFPtr OMF::FromEntity(const ost::mol::EntityHandle& ent, } int idx = chain_idx_map[at_one.GetResidue().GetChain().GetHashCode()]; inter_residue_bonds[idx].push_back(std::make_pair(at_one.GetHashCode(), - at_two.GetHashCode())); + at_two.GetHashCode())); inter_residue_bond_orders[idx].push_back(bond_it->GetBondOrder()); } } else { @@ -3682,7 +4355,8 @@ void OMF::ToStream(std::ostream& stream) const { Dump(stream, biounit_definitions_); Dump(stream, chain_data_, residue_definitions_, OptionSet(LOSSY), - OptionSet(AVG_BFACTORS), OptionSet(ROUND_BFACTORS), OptionSet(SKIP_SS)); + OptionSet(AVG_BFACTORS), OptionSet(ROUND_BFACTORS), OptionSet(SKIP_SS), + OptionSet(INFER_AA_POS)); Dump(stream, bond_chain_names_); Dump(stream, bond_atoms_); Dump(stream, bond_orders_); @@ -3725,7 +4399,8 @@ void OMF::FromStream(std::istream& stream) { Load(stream, biounit_definitions_); Load(stream, chain_data_, residue_definitions_, version_, OptionSet(LOSSY), - OptionSet(AVG_BFACTORS), OptionSet(ROUND_BFACTORS), OptionSet(SKIP_SS)); + OptionSet(AVG_BFACTORS), OptionSet(ROUND_BFACTORS), OptionSet(SKIP_SS), + OptionSet(INFER_AA_POS)); Load(stream, bond_chain_names_); Load(stream, bond_atoms_); Load(stream, bond_orders_); @@ -3799,8 +4474,9 @@ void OMF::FillChain(ost::mol::ChainHandle& chain, ost::mol::XCSEditor& ed, Real d = geom::Distance(c.GetPos(), n.GetPos()); if(d > 0.991 && d < 1.681) { // mean (1.336) +- 15 stds (0.023) - // This is an extremely loose threshold but makes sure to also handle - // inaccuracies that have been introduced with lossy compression + // This is an extremely loose threshold but makes sure to also + // handle inaccuracies that have been introduced with lossy + // compression ed.Connect(c, n); } } diff --git a/modules/io/src/mol/omf.hh b/modules/io/src/mol/omf.hh index 9503b1f541a928b1a3f3730a39fffc60bbab36c4..e832b829c5b867c3ff07c1aafe835f1919d55250 100644 --- a/modules/io/src/mol/omf.hh +++ b/modules/io/src/mol/omf.hh @@ -39,9 +39,29 @@ typedef boost::shared_ptr<OMF> OMFPtr; typedef boost::shared_ptr<ChainData> ChainDataPtr; typedef boost::shared_ptr<BioUnitData> BioUnitDataPtr; +struct SidechainAtomRule { + int sidechain_atom_idx; + int anchor_idx[3]; + Real bond_length; + Real angle; + // 0: chi1, 1: chi2, 2: chi3, 3: chi4, 4: 0.0 + int dihedral_idx; + // the value of the dihedral above will be added to base_dihedral to get + // the final diheral angle. If you want to have the effect of chi3 + M_PI + // you define dihedral_idx as 2 and base_dihedral = M_PI. + Real base_dihedral; +}; + +struct ChiDefinition{ + int idx_one; + int idx_two; + int idx_three; + int idx_four; +}; + struct ResidueDefinition { - ResidueDefinition() { }; + ResidueDefinition(): rotamer_setup(false) { }; ResidueDefinition(const ost::mol::ResidueHandle& res); @@ -65,6 +85,28 @@ struct ResidueDefinition { void FromStream(std::istream& stream); + int GetIdx(const String& aname) const; + + const std::set<int>& GetRotamericAtoms() const; + + const std::vector<ChiDefinition>& GetChiDefinitions() const; + + const std::vector<SidechainAtomRule>& GetSidechainAtomRules() const; + + int GetNChiAngles() const; + + void _InitIdxMapper() const; + + void _InitRotamer() const; + + void _AddChiDefinition(int idx_one, int idx_two, int idx_three, + int idx_four) const; + + void _AddAtomRule(int a_idx, int anch_one_idx, + int anch_two_idx, int anch_three_idx, + Real bond_length, Real angle, int dihedral_idx, + Real base_dihedral) const; + String name; char olc; char chem_type; @@ -74,6 +116,11 @@ struct ResidueDefinition { std::vector<bool> is_hetatm; std::vector<int> bonds; std::vector<int> bond_orders; + mutable bool rotamer_setup; + mutable std::map<String, int> idx_mapper; + mutable std::set<int> rotameric_atoms; + mutable std::vector<ChiDefinition> chi_definitions; + mutable std::vector<SidechainAtomRule> sidechain_atom_rules; }; @@ -108,12 +155,12 @@ struct ChainData { void ToStream(std::ostream& stream, const std::vector<ResidueDefinition>& res_def, bool lossy, bool avg_bfactors, bool round_bfactors, - bool skip_ss) const; + bool skip_ss, bool infer_aa_pos) const; void FromStream(std::istream& stream, const std::vector<ResidueDefinition>& res_def, int version, bool lossy, bool avg_bfactors, - bool round_bfactors, bool skip_ss); + bool round_bfactors, bool skip_ss, bool infer_aa_pos); // chain features String ch_name; @@ -151,13 +198,13 @@ private: DefaultPepLib& operator=(DefaultPepLib const& copy); }; - class OMF { public: enum OMFOption {DEFAULT_PEPLIB = 1, LOSSY = 2, AVG_BFACTORS = 4, - ROUND_BFACTORS = 8, SKIP_SS = 16, INFER_PEP_BONDS = 32}; + ROUND_BFACTORS = 8, SKIP_SS = 16, INFER_PEP_BONDS = 32, + INFER_AA_POS = 64}; bool OptionSet(OMFOption opt) const { return (opt & options_) == opt;