diff --git a/modules/io/src/mol/mmcif_writer.cc b/modules/io/src/mol/mmcif_writer.cc index 9322195764a44290d6e60a3164c508599d47d204..496957c8514639bf036b6327252c7703413b5014 100644 --- a/modules/io/src/mol/mmcif_writer.cc +++ b/modules/io/src/mol/mmcif_writer.cc @@ -329,10 +329,13 @@ namespace { } void AddAsym(const String& asym_chain_name, - ost::io::MMCifWriterEntity& info) { + ost::io::MMCifWriterEntity& info, + bool skip_asym_id=false) { // adds asym_chain_name to info under the assumption that mon_ids // exactly match => just add a copy of mon_ids to asym_alns - info.asym_ids.push_back(asym_chain_name); + if(!skip_asym_id) { + info.asym_ids.push_back(asym_chain_name); + } info.asym_alns.push_back(info.mon_ids); } @@ -385,7 +388,7 @@ namespace { if(n_matches == 0) { return false; } else { - return n_beyond / n_matches <= beyond_frac; + return static_cast<Real>(n_beyond) / n_matches <= beyond_frac; } } @@ -393,7 +396,8 @@ namespace { template<class T> void AddAsymResnum(const String& asym_chain_name, const T& res_list, - ost::io::MMCifWriterEntity& info) { + ost::io::MMCifWriterEntity& info, + bool skip_asym_id=false) { if(!info.is_poly) { // no need for SEQRES alignment vodoo @@ -446,7 +450,9 @@ namespace { } // finalize - info.asym_ids.push_back(asym_chain_name); + if(!skip_asym_id) { + info.asym_ids.push_back(asym_chain_name); + } info.asym_alns.push_back(aln_mon_ids); } @@ -1181,6 +1187,130 @@ namespace { atom_site, pdbx_poly_seq_scheme); } + template <class T> + void ProcessEntmmCIF(const T& ent, + ost::conop::CompoundLibPtr compound_lib, + std::vector<ost::io::MMCifWriterEntity>& entity_info, + ost::io::StarWriterLoopPtr atom_site, + ost::io::StarWriterLoopPtr pdbx_poly_seq_scheme) { + + // deal with preset asym_ids in entity_info + // need to properly setup alignments in these cases + std::set<String> preprocessed_chains; + for(size_t ei_idx = 0; ei_idx < entity_info.size(); ++ei_idx) { + if(!entity_info[ei_idx].asym_ids.empty()) { + // Assumption that the code below fills all alignments + if(!entity_info[ei_idx].asym_alns.empty()) { + std::stringstream ss; + throw ost::io::IOException("Expect alignments to be empty in " + "MMCifWriterEntity when predefining asym " + "ids"); + } + } + for(auto ai: entity_info[ei_idx].asym_ids) { + // Plenty of checks that chain really matches with specified + // MMCifWriterEntity + if(preprocessed_chains.find(ai) != preprocessed_chains.end()) { + std::stringstream ss; + ss << "Tried to predefine entity for chain \"" << ai; + ss << "\" multiple times"; + throw ost::io::IOException(ss.str()); + } + auto chain = ent.FindChain(ai); + if(!chain.IsValid()) { + std::stringstream ss; + ss << "Tried to predefine entity for chain \"" << ai; + ss << "\" but couldnt find that chain in provided entity"; + throw ost::io::IOException(ss.str()); + } + String type = ost::mol::EntityTypeFromChainType(chain.GetType()); + if(type != entity_info[ei_idx].type) { + std::stringstream ss; + ss << "Expected predefined chain \"" << ai << "\" to be of type \""; + ss << entity_info[ei_idx].type <<"\", got \"" << type << "\""; + throw ost::io::IOException(ss.str()); + } + if(entity_info[ei_idx].type == "polymer") { + String poly_type = ost::mol::EntityPolyTypeFromChainType(chain.GetType()); + if(poly_type != entity_info[ei_idx].poly_type) { + std::stringstream ss; + ss << "Expected predefined chain \"" << ai << "\" to be of "; + ss << "poly_type \"" << entity_info[ei_idx].poly_type <<"\", got \"" << poly_type; + ss << "\""; + throw ost::io::IOException(ss.str()); + } + auto res_list = chain.GetResidueList(); + if(!MatchEntityResnum(res_list, entity_info[ei_idx], 0.0)) { + std::stringstream ss; + ss << "Failed to match predefined chain \"" << ai; + ss << "\" to respective entity"; + throw ost::io::IOException(ss.str()); + } + AddAsymResnum(ai, res_list, entity_info[ei_idx], true); + Feed_atom_site(atom_site, ai, ei_idx, entity_info[ei_idx], res_list); + Feed_pdbx_poly_seq_scheme(pdbx_poly_seq_scheme, ai, ei_idx, + entity_info[ei_idx], res_list); + preprocessed_chains.insert(ai); + } else if(entity_info[ei_idx].type == "branched") { + String branch_type = ost::mol::BranchedTypeFromChainType(chain.GetType()); + if(branch_type != entity_info[ei_idx].branch_type) { + std::stringstream ss; + ss << "Expected predefined chain \"" << ai << "\" to be of "; + ss << "branched_type \"" << entity_info[ei_idx].branch_type; + ss << "\", got \"" << branch_type << "\""; + throw ost::io::IOException(ss.str()); + } + auto res_list = chain.GetResidueList(); + if(!MatchEntity(res_list, entity_info[ei_idx])) { + std::stringstream ss; + ss << "Failed to match predefined chain \"" << ai; + ss << "\" to respective entity"; + throw ost::io::IOException(ss.str()); + } + AddAsym(ai, entity_info[ei_idx], true); + Feed_atom_site(atom_site, ai, ei_idx, entity_info[ei_idx], res_list); + preprocessed_chains.insert(ai); + } else if (entity_info[ei_idx].type == "water") { + auto res_list = chain.GetResidueList(); + AddAsym(ai, entity_info[ei_idx], true); + Feed_atom_site(atom_site, ai, ei_idx, entity_info[ei_idx], res_list); + preprocessed_chains.insert(ai); + } else { + auto res_list = chain.GetResidueList(); + if(!MatchEntity(res_list, entity_info[ei_idx])) { + std::stringstream ss; + ss << "Failed to match predefined chain \"" << ai; + ss << "\" to respective entity"; + throw ost::io::IOException(ss.str()); + } + AddAsym(ai, entity_info[ei_idx], true); + Feed_atom_site(atom_site, ai, ei_idx, entity_info[ei_idx], res_list); + preprocessed_chains.insert(ai); + } + } + } + + auto chain_list = ent.GetChainList(); + for(auto ch: chain_list) { + if(preprocessed_chains.find(ch.GetName()) != preprocessed_chains.end()) { + continue; // already done + } + auto res_list = ch.GetResidueList(); + String chain_name = ch.GetName(); + int entity_id = SetupEntity(chain_name, + ch.GetType(), + res_list, + true, + entity_info); + Feed_atom_site(atom_site, chain_name, entity_id, entity_info[entity_id], + res_list); + if(entity_info[entity_id].is_poly) { + Feed_pdbx_poly_seq_scheme(pdbx_poly_seq_scheme, chain_name, + entity_id, entity_info[entity_id], res_list); + } + } + } + // template to allow ost::mol::EntityHandle and ost::mol::EntityView template<class T> void ProcessEnt(const T& ent, @@ -1191,23 +1321,18 @@ namespace { ost::io::StarWriterLoopPtr pdbx_poly_seq_scheme) { if(mmcif_conform) { - auto chain_list = ent.GetChainList(); - for(auto ch: chain_list) { - auto res_list = ch.GetResidueList(); - String chain_name = ch.GetName(); - int entity_id = SetupEntity(chain_name, - ch.GetType(), - res_list, - true, - entity_info); - Feed_atom_site(atom_site, chain_name, entity_id+1, entity_info[entity_id], - res_list); - if(entity_info[entity_id].is_poly) { - Feed_pdbx_poly_seq_scheme(pdbx_poly_seq_scheme, chain_name, - entity_id+1, entity_info[entity_id], res_list); + ProcessEntmmCIF(ent, compound_lib, entity_info, atom_site, + pdbx_poly_seq_scheme); + } else { + // cannot predefine asym_ids in entity_info when mmcif_conform is + // False. You're welcome to implement it... But its a bit awkward... + // I warned you... + for(auto ei: entity_info) { + if(!ei.asym_ids.empty()) { + throw ost::io::IOException("Predefine chains to entities not " + "supported when mmcif_conform is False"); } } - } else { // delegate to more complex ProcessEntmmCIFify ProcessEntmmCIFify(ent, compound_lib, entity_info, atom_site, pdbx_poly_seq_scheme);