| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #include "alphafold3/structure/cpp/mmcif_altlocs.h" |
|
|
| #include <cstddef> |
| #include <cstdint> |
| #include <functional> |
| #include <iterator> |
| #include <limits> |
| #include <string> |
| #include <utility> |
| #include <vector> |
|
|
| #include "absl/algorithm/container.h" |
| #include "absl/log/log.h" |
| #include "absl/strings/numbers.h" |
| #include "absl/strings/string_view.h" |
| #include "absl/types/span.h" |
| #include "alphafold3/structure/cpp/mmcif_layout.h" |
|
|
| namespace alphafold3 { |
| namespace { |
|
|
| float OccupancyToFloat(absl::string_view occupancy) { |
| float result = 0.0f; |
| LOG_IF(ERROR, !absl::SimpleAtof(occupancy, &result)) |
| << "Invalid Occupancy: " << occupancy; |
| return result; |
| } |
|
|
| |
| bool AtomEquiv(absl::string_view lhs, absl::string_view rhs) { |
| if (lhs == rhs) return true; |
| if (lhs.empty() != rhs.empty()) return false; |
| |
| char first_lhs = lhs.front(); |
| char second_rhs = rhs.front(); |
| if ((first_lhs == 'H' && second_rhs == 'D') || |
| (first_lhs == 'D' && second_rhs == 'H')) { |
| lhs.remove_prefix(1); |
| rhs.remove_prefix(1); |
| return lhs == rhs; |
| } |
| return false; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| template <typename GroupCallback, |
| typename IsEqual = std::equal_to<absl::string_view>> |
| void GroupBy(absl::Span<const std::string> values, std::size_t start, |
| std::size_t count, GroupCallback&& group_callback, |
| IsEqual&& is_equal = std::equal_to<absl::string_view>{}) { |
| std::size_t span_start = start; |
| if (count > 0) { |
| for (std::size_t i = start + 1; i < start + count; ++i) { |
| if (!is_equal(values[i], values[span_start])) { |
| group_callback(span_start, i - span_start); |
| span_start = i; |
| } |
| } |
| group_callback(span_start, start + count - span_start); |
| } |
| } |
|
|
| void ProcessAltLocGroupsWhole(std::size_t alt_loc_start, |
| std::size_t alt_loc_count, |
| absl::Span<const std::string> comp_ids, |
| absl::Span<const std::string> atom_ids, |
| absl::Span<const std::string> alt_ids, |
| absl::Span<const std::string> occupancies, |
| std::vector<std::uint64_t>& in_out_keep_indices) { |
| std::pair<std::size_t, std::size_t> best_split = {alt_loc_start, |
| alt_loc_count}; |
| std::vector<char> alt_loc_groups; |
| float best_occupancy = -std::numeric_limits<float>::infinity(); |
| char best_group = alt_ids[alt_loc_start].front(); |
| std::vector<std::pair<std::size_t, float>> occupancy_stats; |
|
|
| |
| GroupBy(comp_ids, alt_loc_start, alt_loc_count, |
| [&](std::size_t start, std::size_t count) { |
| |
| |
| alt_loc_groups.clear(); |
| occupancy_stats.clear(); |
| |
| for (std::size_t i = 0; i < count; ++i) { |
| char alt_loc_id = alt_ids[start + i].front(); |
| float occupancy = OccupancyToFloat(occupancies[start + i]); |
| if (auto loc = absl::c_find(alt_loc_groups, alt_loc_id); |
| loc == alt_loc_groups.end()) { |
| occupancy_stats.emplace_back(1, occupancy); |
| alt_loc_groups.push_back(alt_loc_id); |
| } else { |
| auto& stat = |
| occupancy_stats[std::distance(alt_loc_groups.begin(), loc)]; |
| ++stat.first; |
| stat.second += occupancy; |
| } |
| } |
| float total_occupancy = 0.0; |
| for (auto& stat : occupancy_stats) { |
| total_occupancy += stat.second / stat.first; |
| } |
| char group = *absl::c_min_element(alt_loc_groups); |
| |
| |
| if (total_occupancy > best_occupancy || |
| (total_occupancy == best_occupancy && group < best_group)) { |
| |
| best_group = alt_loc_groups.front(); |
| float best_amount = occupancy_stats.front().second / |
| occupancy_stats.front().first; |
| for (std::size_t i = 1; i < occupancy_stats.size(); ++i) { |
| float amount = |
| occupancy_stats[i].second / occupancy_stats[i].first; |
| char group = alt_loc_groups[i]; |
| if (amount > best_amount || |
| (amount == best_amount && group < best_group)) { |
| best_amount = amount; |
| best_group = group; |
| } |
| } |
| best_occupancy = total_occupancy; |
| best_split = {start, count}; |
| } |
| }); |
|
|
| |
| |
| |
| auto [split_start, split_count] = best_split; |
| GroupBy( |
| atom_ids, split_start, split_count, |
| [&in_out_keep_indices, &alt_ids, best_group](std::size_t start, |
| std::size_t count) { |
| |
| |
| std::size_t best_index = start; |
| for (std::size_t i = 1; i < count; ++i) { |
| if (alt_ids[start + i].front() == best_group) { |
| best_index = start + i; |
| break; |
| } |
| } |
| in_out_keep_indices.push_back(best_index); |
| }, |
| AtomEquiv); |
| } |
|
|
| |
| |
| void ProcessAltLocGroupPartial( |
| std::size_t alt_loc_start, std::size_t alt_loc_count, |
| absl::Span<const std::string> atom_ids, |
| absl::Span<const std::string> alt_ids, |
| absl::Span<const std::string> occupancies, |
| std::vector<std::uint64_t>& in_out_keep_indices) { |
| GroupBy( |
| atom_ids, alt_loc_start, alt_loc_count, |
| [&](std::size_t start, std::size_t count) { |
| if (count == 1) { |
| in_out_keep_indices.push_back(start); |
| } else { |
| float best_occ = OccupancyToFloat(occupancies[start]); |
| std::size_t best_index = start; |
| char best_group = alt_ids[start].front(); |
| for (std::size_t i = 0; i < count; ++i) { |
| float occ = OccupancyToFloat(occupancies[start + i]); |
| char group = alt_ids[start + i].front(); |
| if (occ > best_occ || (occ == best_occ && group < best_group)) { |
| best_group = group; |
| best_index = start + i; |
| best_occ = occ; |
| } |
| } |
| in_out_keep_indices.push_back(best_index); |
| } |
| }, |
| AtomEquiv); |
| } |
|
|
| } |
|
|
| |
| std::vector<std::uint64_t> ResolveMmcifAltLocs( |
| const MmcifLayout& layout, absl::Span<const std::string> comp_ids, |
| absl::Span<const std::string> atom_ids, |
| absl::Span<const std::string> alt_ids, |
| absl::Span<const std::string> occupancies, |
| absl::Span<const std::size_t> chain_indices) { |
| std::vector<std::uint64_t> keep_indices; |
| keep_indices.reserve(layout.num_atoms()); |
| std::size_t alt_loc_start = 0; |
| for (std::size_t chain_index : chain_indices) { |
| auto [residues_start, residues_end] = layout.residue_range(chain_index); |
| for (std::size_t residue = residues_start; residue < residues_end; |
| ++residue) { |
| std::size_t alt_loc_count = 0; |
| auto [atom_start, atom_end] = layout.atom_range(residue); |
| for (std::size_t i = atom_start; i < atom_end; ++i) { |
| char alt_loc_id = alt_ids[i].front(); |
| if (alt_loc_id == '.' || alt_loc_id == '?') { |
| if (alt_loc_count > 0) { |
| ProcessAltLocGroupPartial(alt_loc_start, alt_loc_count, atom_ids, |
| alt_ids, occupancies, keep_indices); |
| alt_loc_count = 0; |
| } |
| keep_indices.push_back(i); |
| } else { |
| if (alt_loc_count == 0) { |
| alt_loc_start = i; |
| } |
| ++alt_loc_count; |
| } |
| } |
| if (alt_loc_count > 0) { |
| if (atom_end - atom_start == alt_loc_count) { |
| ProcessAltLocGroupsWhole(alt_loc_start, alt_loc_count, comp_ids, |
| atom_ids, alt_ids, occupancies, |
| keep_indices); |
| } else { |
| ProcessAltLocGroupPartial(alt_loc_start, alt_loc_count, atom_ids, |
| alt_ids, occupancies, keep_indices); |
| } |
| } |
| } |
| } |
|
|
| return keep_indices; |
| } |
|
|
| } |
|
|