-
Notifications
You must be signed in to change notification settings - Fork 22
SKETCH-2782: get_residue_number_for_new_monomer now uses one residue number per monomer, even for nucleic acids #372
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
fad48f7
9e7f9f9
a5a02da
6364721
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -712,12 +712,11 @@ get_monomer_type_from_chain_type(const rdkit_extensions::ChainType chain_type, | |
| } | ||
|
|
||
| /** | ||
| * @return a set of all residue numbers for monomers of the specified type that | ||
| * are in the same polymer as the given atom. | ||
| * @return a set of all residue numbers for monomers that are in the same | ||
| * polymer as the given atom. | ||
| */ | ||
| static std::unordered_set<int> | ||
| get_all_residue_numbers_of_monomer_type_in_polymer( | ||
| const MonomerType monomer_type, const RDKit::Atom* const atom_in_polymer) | ||
| get_all_residue_numbers_in_polymer(const RDKit::Atom* const atom_in_polymer) | ||
| { | ||
| // we make a copy of the molecule so we can flag it as monomeric | ||
| auto mol = atom_in_polymer->getOwningMol(); | ||
|
|
@@ -728,67 +727,59 @@ get_all_residue_numbers_of_monomer_type_in_polymer( | |
| std::unordered_set<int> residue_numbers; | ||
| for (auto atom_idx : atom_idxs) { | ||
| auto* atom = mol.getAtomWithIdx(atom_idx); | ||
| if (get_monomer_type(atom) == monomer_type) { | ||
| auto res_num = rdkit_extensions::get_residue_number(atom); | ||
| residue_numbers.insert(res_num); | ||
| } | ||
| auto res_num = rdkit_extensions::get_residue_number(atom); | ||
| residue_numbers.insert(res_num); | ||
| } | ||
| return residue_numbers; | ||
| } | ||
|
|
||
| /** | ||
| * Determine the appropriate residue number to use for a new monomer that will | ||
| * be connected to an existing monomer. | ||
| * @param res_name The residue name of the monomer to be added | ||
| * @param chain_type The type of the monomer to be added | ||
| * @param new_monomer_ap_name The new monomer's attachment point that will be | ||
| * used to connect it to bound_to_monomer | ||
| * @param bound_to_monomer The existing monomer that the new monomer will be | ||
| * bound to | ||
| * @return the residue number, which is guaranteed to be: | ||
| * - non-negative (since get_residue_number returns unsigned ints) | ||
| * - unique amongst all residues with the same monomer type (This allows, | ||
| * e.g., an RNA base and sugar to have the same residue number, but ensures | ||
| * that peptide monomers are uniquely numbered.) | ||
| */ | ||
| static int | ||
| get_residue_number_for_new_monomer(const std::string_view res_name, | ||
| const rdkit_extensions::ChainType chain_type, | ||
| const std::string_view new_monomer_ap_name, | ||
| const RDKit::Atom* const bound_to_monomer) | ||
| int get_residue_number_for_new_monomer( | ||
| const std::string_view res_name, | ||
| const rdkit_extensions::ChainType chain_type, | ||
| const std::string_view new_monomer_ap_name, | ||
| const RDKit::Atom* const bound_to_monomer) | ||
| { | ||
| using rdkit_extensions::ChainType; | ||
|
|
||
| auto monomer_type = get_monomer_type_from_chain_type(chain_type, res_name); | ||
| auto existing_res_nums = get_all_residue_numbers_of_monomer_type_in_polymer( | ||
| monomer_type, bound_to_monomer); | ||
| auto existing_res_nums = | ||
| get_all_residue_numbers_in_polymer(bound_to_monomer); | ||
|
|
||
| // first, determine what the ideal residue number would be based on the | ||
| // residue number of the monomer we're binding to | ||
| int res_num_offset = 1; | ||
|
ethan-schrodinger marked this conversation as resolved.
Outdated
|
||
| auto bound_to_monomer_chain_type = | ||
| rdkit_extensions::getChainType(*bound_to_monomer); | ||
| if (chain_type == ChainType::PEPTIDE && | ||
| bound_to_monomer_chain_type == ChainType::PEPTIDE && | ||
| new_monomer_ap_name == ap_model_name_for(PeptideAP::N)) { | ||
| // a new C terminal peptide residue | ||
| res_num_offset = -1; | ||
| } else if (chain_type == ChainType::RNA && | ||
| bound_to_monomer_chain_type == ChainType::RNA) { | ||
| if (monomer_type == MonomerType::NA_SUGAR && | ||
| new_monomer_ap_name == ap_model_name_for(NASugarAP::THREE_PRIME)) { | ||
| // sugars can link to the previous residue | ||
| } else if (chain_type == ChainType::RNA) { | ||
| if (monomer_type == MonomerType::NA_SUGAR) { | ||
| if (new_monomer_ap_name == | ||
| ap_model_name_for(NASugarAP::THREE_PRIME)) { | ||
| // a new sugar bound to the next phosphate. We skip a number | ||
| // since the base is typically given the number immediately | ||
| // after the sugar | ||
| res_num_offset = -2; | ||
|
Comment on lines
+766
to
+771
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we be worried about this "typically"?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not really. This would only be an issue if the user is intentionally leaving an abasic site (i.e. a missing base), and even then, the only result of this would be that the residues aren't numbered sequentially, which shouldn't have any real consequences. If we want a guarantee that we have the ideal residue numbering, i.e. starting at one and continuing sequentially in the correct order, we'll need to renumber the entire strand after it's been built. That's probably not worth worrying about unless we know of something downstream that's requires it. |
||
| } else if (new_monomer_ap_name == | ||
| ap_model_name_for(NASugarAP::ONE_PRIME)) { | ||
| // a new sugar bound to its base | ||
| res_num_offset = -1; | ||
| } | ||
| } else if (monomer_type == MonomerType::NA_BASE && | ||
| new_monomer_ap_name == ap_model_name_for(NA_BASE_AP_N1_9)) { | ||
| res_num_offset = -1; | ||
| } else if (monomer_type == MonomerType::NA_PHOSPHATE && | ||
| new_monomer_ap_name == | ||
| ap_model_name_for(NAPhosphateAP::TO_PREV_SUGAR)) { | ||
| res_num_offset = -1; | ||
| } else if (!((monomer_type == MonomerType::NA_PHOSPHATE && | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wouldn't these offsets be +1 and +2? Totally possible I am not understanding the MonomerType enum correctly - is this a base connecting to a sugar then a phosphate connecting to the same sugar? if the sugar is 0, I would think that the base is 1 then the phosphate is 2
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, it looks like a bunch of the numbers here are off. Good catch! I'll fix that and update the tests, both to check for the correct results and hopefully to make those types of issues more obvious. (I also temporarily tweaked monomer drawing locally so it includes the residue numbers to verify that the current numbering is wrong, since I kept getting myself massively confused trying to reason through the numbering here.) |
||
| new_monomer_ap_name == | ||
| ap_model_name_for(NAPhosphateAP::TO_NEXT_SUGAR)) || | ||
| (monomer_type == MonomerType::NA_BASE && | ||
| new_monomer_ap_name == NA_BASE_AP_PAIR))) { | ||
| // phosphates and bases can link to the next residue, but all other | ||
| // linkages are probably within the same residue | ||
| res_num_offset = 0; | ||
| } | ||
| } | ||
| auto bound_to_res_num = | ||
| rdkit_extensions::get_residue_number(bound_to_monomer); | ||
| int new_res_num = bound_to_res_num + res_num_offset; | ||
|
|
||
| // if the ideal residue number is not available, just use one more than the | ||
| // highest current residue number, since we know that's available | ||
| if (new_res_num < 0 || existing_res_nums.contains(new_res_num)) { | ||
|
ethan-schrodinger marked this conversation as resolved.
Outdated
|
||
| new_res_num = *std::ranges::max_element(existing_res_nums) + 1; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
General comment but not urgent - I'm not sure whether you will always be able to determine whether something is a base/sugar/phosphate based off residue name. I'm not sure how common it will be but there are customers with modified phosphate linkers and sugars that won't adhere to a certain naming convention - you maybe need to deduce by a substructure match, or just rely completely on the attachment points used
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, agreed. We might want to record information about the monomer type when the monomer is added, since Sketcher and/or the HELM converter would presumably be in the best position to figure that out (and the Sketcher would likely have additional information about the monomer based on the tool being used). I've filed SKETCH-2796 for this.