def exercise_map_utils () : # # UNSTABLE # hierarchy, fmodel = get_1yjp_pdb_and_fmodel() sel_cache = hierarchy.atom_selection_cache() sele = sel_cache.selection("resseq 5 and (name CD or name OE1 or name NE2)") sele_all = sel_cache.selection("resseq 5") fmodel.xray_structure.scale_adp(factor=0.5, selection=sele) fmodel.update_xray_structure(update_f_calc=True) two_fofc_map, fofc_map = building.get_difference_maps(fmodel) map_stats = building.local_density_quality( fofc_map=fofc_map, two_fofc_map=two_fofc_map, atom_selection=sele_all, xray_structure=fmodel.xray_structure) out = StringIO() map_stats.show_atoms_outside_density(out=out, two_fofc_cutoff=3.0) pdb_strs = [ l.split(":")[0] for l in out.getvalue().splitlines() ] assert len(pdb_strs) > 0 # XXX there seems to be a stochastic effect here #assert (pdb_strs == ['pdb=" CA GLN A 5 "', 'pdb=" CB GLN A 5 "', # 'pdb=" CD GLN A 5 "', 'pdb=" NE2 GLN A 5 "']), \ # pdb_strs fc_map = fmodel.map_coefficients(map_type="Fc").fft_map( resolution_factor=0.25).apply_sigma_scaling().real_map_unpadded() assert (map_stats.number_of_atoms_below_fofc_map_level() == 3) assert (map_stats.fraction_of_nearby_grid_points_above_cutoff() > 0.0) stats = building.get_model_map_stats( selection=sele_all, target_map=two_fofc_map, model_map=fc_map, unit_cell=fmodel.xray_structure.unit_cell(), sites_cart=fmodel.xray_structure.sites_cart(), pdb_atoms=hierarchy.atoms())
def exercise_map_utils () : # # UNSTABLE 2x # hierarchy, fmodel = get_1yjp_pdb_and_fmodel() sel_cache = hierarchy.atom_selection_cache() sele = sel_cache.selection("resseq 5 and (name CD or name OE1 or name NE2)") sele_all = sel_cache.selection("resseq 5") fmodel.xray_structure.scale_adp(factor=0.5, selection=sele) fmodel.update_xray_structure(update_f_calc=True) two_fofc_map, fofc_map = building.get_difference_maps(fmodel) map_stats = building.local_density_quality( fofc_map=fofc_map, two_fofc_map=two_fofc_map, atom_selection=sele_all, xray_structure=fmodel.xray_structure) out = StringIO() map_stats.show_atoms_outside_density(out=out, two_fofc_cutoff=3.0) pdb_strs = [ l.split(":")[0] for l in out.getvalue().splitlines() ] assert len(pdb_strs) > 0 # XXX there seems to be a stochastic effect here #assert (pdb_strs == ['pdb=" CA GLN A 5 "', 'pdb=" CB GLN A 5 "', # 'pdb=" CD GLN A 5 "', 'pdb=" NE2 GLN A 5 "']), \ # pdb_strs fc_map = fmodel.map_coefficients(map_type="Fc").fft_map( resolution_factor=0.25).apply_sigma_scaling().real_map_unpadded() assert (map_stats.number_of_atoms_below_fofc_map_level() == 3) assert (map_stats.fraction_of_nearby_grid_points_above_cutoff() > 0.0) stats = building.get_model_map_stats( selection=sele_all, target_map=two_fofc_map, model_map=fc_map, unit_cell=fmodel.xray_structure.unit_cell(), sites_cart=fmodel.xray_structure.sites_cart(), pdb_atoms=hierarchy.atoms())
def process_results(pdb_hierarchy, fmodel, residues_in, building_trials, params, verbose=False, log=sys.stdout): assert (len(residues_in) == len(building_trials)) from mmtbx.rotamer import rotamer_eval n_alternates = 0 unit_cell = fmodel.xray_structure.unit_cell() two_fofc_map, fofc_map = building.get_difference_maps(fmodel) rot_eval = rotamer_eval.RotamerEval(data_version="8000") for main_conf, trials in zip(residues_in, building_trials): if (trials is None): print("WARNING: error building %s" % main_conf.id_str(), file=log) continue if (len(trials) == 0): continue res_log = StringIO() print(" %s:" % main_conf.id_str(), file=res_log) main_rotamer = alt_rotamer = None if (not main_conf.resname in ["GLY", "PRO", "ALA"]): main_rotamer = rot_eval.evaluate_residue(main_conf) assert (main_rotamer != "OUTLIER") best_trial = pick_best_alternate(trials=trials, params=params, rotamer=main_rotamer, log=res_log) if (best_trial is None): continue new_conf = best_trial.as_atom_group() changed_rotamer = (best_trial.rotamer != main_rotamer) skip = False flag = "" stats = best_trial.stats # FIXME this needs to be made more consistent with the filtering criteria # in disorder/__init__.py if ((stats.rmsd < params.rmsd_min) and (stats.max_dev < params.rmsd_min) and (not changed_rotamer)): skip = True print(" selected conformer (occ=%.2f):" % \ best_trial.occupancy, file=res_log) res_log2 = StringIO() density_quality = building.residue_density_quality( atom_group=new_conf, unit_cell=unit_cell, two_fofc_map=two_fofc_map, fofc_map=fofc_map) if (main_conf.resname in ["CYS", "MET", "MSE"]): # XXX if we have a heavier atom (S or SE) in the residue, some additional # sanity checks insure that it has slightly stronger density than we # require for lighter elements. multipliers are just guesses, taking # into account rad damage and partial SeMet incorporation. heavy_atom = { "CYS": "SG", "MET": "SD", "MSE": "SE" }[main_conf.resname] mult = {"CYS": 1.6, "MET": 1.6, "MSE": 2.0}[main_conf.resname] map_levels = density_quality.density_at_atom(heavy_atom) if (map_levels is None): # this probably shouldn't even happen skip = True if ((map_levels.fofc < params.map_thresholds.fofc_min * mult) or (map_levels.two_fofc < params.map_thresholds.two_fofc_min * mult)): skip = True n_atoms_outside_density = density_quality.show_atoms_outside_density( two_fofc_cutoff=params.map_thresholds.two_fofc_min, fofc_cutoff=params.map_thresholds.fofc_min, out=res_log2, prefix=" ") fofc_max = density_quality.max_fofc_value() if (n_atoms_outside_density > 0): skip = True if (not skip) and (verbose): flag = " ***" print(" RMSD=%5.3f max. change=%.2f max(Fo-Fc)=%.1f%s" \ % (stats.rmsd, stats.max_dev, fofc_max, flag), file=res_log) if (changed_rotamer): print(" starting rotamer=%s new rotamer=%s" % \ (main_rotamer, best_trial.rotamer), file=res_log) if (n_atoms_outside_density != 0): print(" atoms outside density:", file=res_log) print(res_log2.getvalue(), file=res_log) else: print("", file=res_log) if (not skip) or (verbose): log.write(res_log.getvalue()) if (skip): continue residue_group = main_conf.parent() main_conf.altloc = 'A' new_occ = 0.5 if (params.expected_occupancy is not None): new_occ = max(0.2, min(0.8, params.expected_occupancy)) for atom in main_conf.atoms(): atom.occ = 1.0 - new_occ atom.segid = alt_confs.SEGID_MAIN new_conf = new_conf.detached_copy() new_conf.altloc = 'B' for atom in new_conf.atoms(): atom.segid = alt_confs.SEGID_NEW_REBUILT atom.occ = new_occ residue_group.append_atom_group(new_conf) n_alternates += 1 return n_alternates
def filter_before_build ( pdb_hierarchy, fmodel, geometry_restraints_manager, selection=None, params=None, verbose=True, log=sys.stdout) : """ Pick residues suitable for building alternate conformations - by default, this means no MolProbity/geometry outliers, good fit to map, no missing atoms, and no pre-existing alternates, but with significant difference density nearby. """ from mmtbx.validation import molprobity from mmtbx.rotamer import rotamer_eval import mmtbx.monomer_library.server from mmtbx import building from iotbx.pdb import common_residue_names_get_class from scitbx.array_family import flex if (selection is None) : selection = flex.bool(fmodel.xray_structure.scatterers().size(), True) pdb_atoms = pdb_hierarchy.atoms() assert (pdb_atoms.size() == fmodel.xray_structure.scatterers().size()) pdb_atoms.reset_i_seq() full_validation = molprobity.molprobity( pdb_hierarchy=pdb_hierarchy, fmodel=fmodel, geometry_restraints_manager=geometry_restraints_manager, outliers_only=False, rotamer_library="8000") if (verbose) : full_validation.show(out=log) multi_criterion = full_validation.as_multi_criterion_view() if (params is None) : params = libtbx.phil.parse(filter_params_str).extract() mon_lib_srv = mmtbx.monomer_library.server.server() two_fofc_map, fofc_map = building.get_difference_maps(fmodel=fmodel) residues = [] filters = params.discard_outliers make_sub_header("Identifying candidates for building", out=log) # TODO parallelize for chain in pdb_hierarchy.only_model().chains() : if (not chain.is_protein()) : continue for residue_group in chain.residue_groups() : atom_groups = residue_group.atom_groups() id_str = residue_group.id_str() i_seqs = residue_group.atoms().extract_i_seq() residue_sel = selection.select(i_seqs) if (not residue_sel.all_eq(True)) : continue if (len(atom_groups) > 1) : print >> log, " %s is already multi-conformer" % id_str continue atom_group = atom_groups[0] res_class = common_residue_names_get_class(atom_group.resname) if (res_class != "common_amino_acid") : print >> log, " %s: non-standard residue" % id_str continue missing_atoms = rotamer_eval.eval_residue_completeness( residue=atom_group, mon_lib_srv=mon_lib_srv, ignore_hydrogens=True) if (len(missing_atoms) > 0) : # residues modeled as pseudo-ALA are allowed by default; partially # missing sidechains are more problematic if ((building.is_stub_residue(atom_group)) and (not params.ignore_stub_residues)) : pass else : print >> log, " %s: missing or incomplete sidechain" % \ (id_str, len(missing_atoms)) continue validation = multi_criterion.get_residue_group_data(residue_group) is_outlier = is_validation_outlier(validation, params) if (is_outlier) : print >> log, " %s" % str(validation) continue if (params.use_difference_map) : i_seqs_no_hd = building.get_non_hydrogen_atom_indices(residue_group) map_stats = building.local_density_quality( fofc_map=fofc_map, two_fofc_map=two_fofc_map, atom_selection=i_seqs_no_hd, xray_structure=fmodel.xray_structure, radius=params.sampling_radius) if ((map_stats.number_of_atoms_below_fofc_map_level() == 0) and (map_stats.fraction_of_nearby_grid_points_above_cutoff()==0)) : if (verbose) : print >> log, " no difference density for %s" % id_str continue residues.append(residue_group.only_atom_group()) if (len(residues) == 0) : raise Sorry("No residues passed the filtering criteria.") print >> log, "" print >> log, "Alternate conformations will be tried for %d residue(s):" % \ len(residues) building.show_chain_resseq_ranges(residues, out=log, prefix=" ") print >> log, "" return residues
def process_results ( pdb_hierarchy, fmodel, residues_in, building_trials, params, verbose=False, log=sys.stdout) : assert (len(residues_in) == len(building_trials)) from mmtbx.rotamer import rotamer_eval n_alternates = 0 unit_cell = fmodel.xray_structure.unit_cell() two_fofc_map, fofc_map = building.get_difference_maps(fmodel) rot_eval = rotamer_eval.RotamerEval(data_version="8000") for main_conf, trials in zip(residues_in, building_trials) : if (trials is None) : print >> log, "WARNING: error building %s" % main_conf.id_str() continue if (len(trials) == 0) : continue res_log = StringIO() print >> res_log, " %s:" % main_conf.id_str() main_rotamer = alt_rotamer = None if (not main_conf.resname in ["GLY","PRO","ALA"]) : main_rotamer = rot_eval.evaluate_residue(main_conf) assert (main_rotamer != "OUTLIER") best_trial = pick_best_alternate( trials=trials, params=params, rotamer=main_rotamer, log=res_log) if (best_trial is None) : continue new_conf = best_trial.as_atom_group() changed_rotamer = (best_trial.rotamer != main_rotamer) skip = False flag = "" stats = best_trial.stats # FIXME this needs to be made more consistent with the filtering criteria # in disorder/__init__.py if ((stats.rmsd < params.rmsd_min) and (stats.max_dev < params.rmsd_min) and (not changed_rotamer)) : skip = True print >> res_log, " selected conformer (occ=%.2f):" % \ best_trial.occupancy res_log2 = StringIO() density_quality = building.residue_density_quality( atom_group=new_conf, unit_cell=unit_cell, two_fofc_map=two_fofc_map, fofc_map=fofc_map) if (main_conf.resname in ["CYS", "MET", "MSE"]) : # XXX if we have a heavier atom (S or SE) in the residue, some additional # sanity checks insure that it has slightly stronger density than we # require for lighter elements. multipliers are just guesses, taking # into account rad damage and partial SeMet incorporation. heavy_atom = {"CYS":"SG", "MET":"SD", "MSE":"SE"}[main_conf.resname] mult = {"CYS":1.6, "MET":1.6, "MSE":2.0}[main_conf.resname] map_levels = density_quality.density_at_atom(heavy_atom) if (map_levels is None) : # this probably shouldn't even happen skip = True if ((map_levels.fofc < params.map_thresholds.fofc_min*mult) or (map_levels.two_fofc < params.map_thresholds.two_fofc_min*mult)) : skip = True n_atoms_outside_density = density_quality.show_atoms_outside_density( two_fofc_cutoff=params.map_thresholds.two_fofc_min, fofc_cutoff=params.map_thresholds.fofc_min, out=res_log2, prefix=" ") fofc_max = density_quality.max_fofc_value() if (n_atoms_outside_density > 0) : skip = True if (not skip) and (verbose) : flag = " ***" print >> res_log, \ " RMSD=%5.3f max. change=%.2f max(Fo-Fc)=%.1f%s" \ % (stats.rmsd, stats.max_dev, fofc_max, flag) if (changed_rotamer) : print >> res_log, " starting rotamer=%s new rotamer=%s" % \ (main_rotamer, best_trial.rotamer) if (n_atoms_outside_density != 0) : print >> res_log, " atoms outside density:" print >> res_log, res_log2.getvalue() else : print >> res_log, "" if (not skip) or (verbose) : log.write(res_log.getvalue()) if (skip) : continue residue_group = main_conf.parent() main_conf.altloc = 'A' new_occ = 0.5 if (params.expected_occupancy is not None) : new_occ = max(0.2, min(0.8, params.expected_occupancy)) for atom in main_conf.atoms() : atom.occ = 1.0 - new_occ atom.segid = alt_confs.SEGID_MAIN new_conf = new_conf.detached_copy() new_conf.altloc = 'B' for atom in new_conf.atoms() : atom.segid = alt_confs.SEGID_NEW_REBUILT atom.occ = new_occ residue_group.append_atom_group(new_conf) n_alternates += 1 return n_alternates