def fetch_args(self, database_name, *, format_name=None): try: db_formats = self._fetchers[database_name] except KeyError: raise NoOpenerError("No such database '%s'" % database_name) from chimerax.core.commands import commas if format_name: try: provider_info = db_formats[format_name] except KeyError: # for backwards compatibility, try the nicknames of the format try: df = self.session.data_formats[format_name] except KeyError: nicks = [] else: nicks = df.nicknames + df.name for nick in nicks: try: provider_info = db_formats[nick] format_name = nick except KeyError: continue break else: raise NoOpenerError( "Format '%s' not supported for database '%s'." " Supported formats are: %s" % (format_name, database_name, commas([dbf for dbf in db_formats]))) else: for format_name, provider_info in db_formats.items(): if provider_info.is_default: break else: raise NoOpenerError( "No default format for database '%s'." " Possible formats are: %s" % (database_name, commas([dbf for dbf in db_formats]))) try: args = self.open_args(self.session.data_formats[format_name]) except NoOpenerError: # fetch-only type (e.g. cellPACK) args = {} args.update( provider_info.bundle_info.run_provider(self.session, database_name, self).fetch_args) return args
def _fetch_info(mgr, database_name, default_format_name): db_info = mgr.database_info(database_name) from chimerax.core.commands import commas if default_format_name: try: provider_info = db_info[default_format_name] except KeyError: raise UserError("Format '%s' not available for database '%s'. Available" " formats are: %s" % (default_format_name, database_name, commas(db_info.keys()))) else: for default_format_name, provider_info in db_info.items(): if provider_info.is_default: break else: raise UserError("No default format for database '%s'. Possible formats are:" " %s" % (database_name, commas(db_info.keys()))) return (provider_info.bundle_info.run_provider(mgr.session, database_name, mgr), default_format_name)
def cmd_save_formats(session): '''Report file formats and suffixes that the save command knows about.''' from chimerax.core.commands import commas all_formats = session.save_command.save_data_formats by_category = {} for fmt in all_formats: by_category.setdefault(fmt.category.title(), []).append(fmt) titles = list(by_category.keys()) titles.sort() lines = [] for title in titles: formats = by_category[title] if session.ui.is_gui: lines.extend([ '<table border=1 cellspacing=0 cellpadding=2>', '<tr><th colspan="3">%s' % title, '<tr><th>File format<th>Short name(s)<th>Suffixes' ]) else: session.logger.info(title) session.logger.info('File format, Short name(s), Suffixes:') formats.sort(key = lambda f: f.name.lower()) for f in formats: if session.ui.is_gui: from html import escape if f.reference_url: descrip = '<a href="%s">%s</a>' % (f.reference_url, escape(f.synopsis)) else: descrip = escape(f.synopsis) lines.append('<tr><td>%s<td>%s<td>%s' % (descrip, escape(commas(f.nicknames)), escape(', '.join(f.suffixes)))) else: session.logger.info(' %s: %s: %s' % (f.synopsis, commas(f.nicknames), ', '.join(f.suffixes))) if session.ui.is_gui: lines.append('</table>') lines.append('<p></p>') if session.ui.is_gui: msg = '\n'.join(lines) session.logger.info(msg, is_html=True)
def _list_colors(session, colors_dict, kind, url=None): from chimerax.core.commands import commas, plural_form import html logger = session.logger if not colors_dict: logger.info("No %s colors." % kind) return is_html = session.ui.is_gui colors = [] for name, c in colors_dict.items(): if is_html: colors.append(html.escape(name) + html_color_swatch(c)) else: colors.append(name) noun = plural_form(colors, 'color') if url is None or not is_html: label = "%d %s %s: " % (len(colors), kind, noun) else: label = "%d <a href=\"%s\">%s %s</a>: " % (len(colors), url, kind, noun) logger.info(label + commas(colors, 'and') + '.', is_html=is_html)
def _list(session, colormaps, kind, url=None): from chimerax.core.commands import plural_form, commas import html logger = session.logger if not colormaps: logger.info("No %s palettes." % kind) return is_html = session.ui.is_gui palettes = [] for name, cm in colormaps.items(): if cm.name is not None: name = cm.name if is_html: palettes.append(html.escape(name)) else: palettes.append(name) noun = plural_form(palettes, "palette") if url is None or not is_html: label = "%d %s %s: " % (len(palettes), kind, noun) else: label = "%d <a href=\"%s\">%s %s</a>: " % (len(palettes), url, kind, noun) logger.info(label + commas(palettes, "and") + '.', is_html=is_html)
def fetch_info(mgr, file_arg, format_name, database_name): if not database_name and exists_locally(file_arg, format_name): return None if ':' in file_arg: db_name, ident = file_arg.split(':', maxsplit=1) if len(db_name) < 2: return None elif database_name: db_name = database_name ident = file_arg elif likely_pdb_id(file_arg, format_name): db_name = "pdb" ident = file_arg else: return None from .manager import NoOpenerError try: db_formats = list(mgr.database_info(db_name).keys()) except NoOpenerError as e: raise LimitationError(str(e)) if format_name and format_name not in db_formats: # for backwards compatibiity, accept formal format name or nicknames try: df = mgr.session.data_formats[format_name] except KeyError: nicks = [] else: nicks = df.nicknames + [df.name] for nick in nicks: if nick in db_formats: format_name = nick break else: from chimerax.core.commands import commas raise UserError("Format '%s' not supported for database '%s'. Supported" " formats are: %s" % (format_name, db_name, commas([dbf for dbf in db_formats]))) return (ident, db_name, format_name)
def _uninstall_bundle(toolshed, bundle, logger, *, session=None, force_remove=False): """Supported API. Uninstall bundle by removing the corresponding Python distribution. Parameters ---------- bundle : string or :py:class:`BundleInfo` instance or sequence of them If string, path to wheel installer. If instance, should be from the available bundle list. logger : :py:class:`~chimerax.core.logger.Logger` instance Logging object where warning and error messages are sent. Raises ------ ToolshedInstalledError Raised if the bundle is not installed. Notes ----- A :py:const:`TOOLSHED_BUNDLE_UNINSTALLED` trigger is fired after package removal. """ import re _debug("uninstall_bundle", bundle) if isinstance(bundle, (str, BundleInfo)): bundles = [bundle] else: bundles = bundle uninstall_now = [] uninstall_later = [] for bundle in bundles: if isinstance(bundle, str): bundle = toolshed.find_bundle(bundle, logger, installed=True) if bundle is None or not bundle.installed: raise ToolshedInstalledError("bundle %r not installed" % bundle.name) if _can_uninstall(bundle): uninstall_now.append(bundle) else: uninstall_later.append(bundle) if not force_remove: all_bundles = set() all_bundles.update(uninstall_now) all_bundles.update(uninstall_later) for bi in all_bundles: needed_by = bi.dependents(logger) needed_by -= all_bundles if needed_by: from chimerax.core.commands import commas, plural_form other = plural_form(needed_by, "another", "other") bundles = plural_form(needed_by, "bundles") logger.error( "Unable to uninstall %s because it is needed by %s %s: %s" % (bi.name, other, bundles, commas((bi.name for bi in needed_by), 'and'))) return if uninstall_now: for bundle in uninstall_now: bundle.deregister(logger) bundle.unload(logger) results = _pip_uninstall(uninstall_now, logger) uninstalled = re.findall(r"^\s*Successfully uninstalled.*$", results, re.M) if uninstalled: logger.info('\n'.join(uninstalled)) toolshed.reload(logger, rebuild_cache=True, report=True) toolshed.triggers.activate_trigger(TOOLSHED_BUNDLE_UNINSTALLED, bundle) if uninstall_later: for bundle in uninstall_later: bundle.deregister(logger) bundle.unload(logger) message = "ChimeraX must be restarted to finish uninstalling." _add_restart_action("uninstall", uninstall_later, [], logger, message, session)
def _display_bundles(bi_list, toolshed, logger, use_html=False, full=True): def bundle_key(bi): return bi.name info = "" if use_html: from html import escape info = """ <style> table.bundle { border-collapse: collapse; border-spacing: 2px; } th.bundle { font-style: italic; text-align: left; } </style> """ info += "<ul>\n" for bi in sorted(bi_list, key=bundle_key): name = bi.name if full: info += "<p>\n" info += "<li>\n" if full: info += "<dt>\n" info += "<b>%s</b> (%s): <i>%s</i>\n" % ( toolshed.bundle_link(name), bi.version, escape(bi.synopsis)) if full: info += "<dd>\n" info += "%s: %s<p>" % (plural_form( bi.categories, "Category"), commas(bi.categories, 'and')) info += _reSt_to_html(bi.description) if bi.tools or bi.commands or bi.formats: info += "<table class='bundle' border='1'>\n" if bi.tools: info += "<tr><th class='bundle' colspan='3'>%s:</th></tr>\n" % plural_form( bi.tools, "Tool") for t in bi.tools: info += "<tr><td><b>%s</b></td> <td colspan='2'><i>%s</i></td></tr>\n" % ( t.name, escape(t.synopsis)) if bi.commands: info += "<tr><th class='bundle' colspan='3'>%s:</th></tr>\n" % plural_form( bi.commands, "Command") for c in bi.commands: info += "<tr><td><b>%s</b></td> <td colspan='2'><i>%s</i></td></tr>\n" % ( c.name, escape(c.synopsis)) if bi.selectors: info += "<tr><th class='bundle' colspan='3'>%s:</th></tr>\n" % plural_form( bi.selectors, "Selector") for s in bi.selectors: info += "<tr><td><b>%s</b></td> <td colspan='2'><i>%s</i></td></tr>\n" % ( s.name, escape(s.synopsis)) if bi.formats: info += "<tr><th class='bundle' colspan='3'>%s:</th></tr>\n" % plural_form( bi.formats, "Format") for f in bi.formats: can_open = ' open' if f.has_open else '' can_save = ' save' if f.has_save else '' info += "<tr><td><b>%s</b></td> <td><i>%s</i></td><td>%s%s</td></tr>\n" % ( f.name, f.category, can_open, can_save) if bi.tools or bi.commands or bi.formats: info += "</table>\n" info += "</dl>\n" info += "</li>\n" info += "</ul>\n" else: for bi in sorted(bi_list, key=bundle_key): name = bi.name if name.startswith('ChimeraX-'): name = name[len('ChimeraX-'):] info += "%s (%s) [%s]: %s\n" % (name, bi.version, ', '.join( bi.categories), bi.synopsis) if full: if bi.tools: info += " %s:\n" % plural_form(bi.tools, "Tool") for t in bi.tools: info += " %s: %s\n" % (t.name, t.synopsis) if bi.commands: info += " %s:\n" % plural_form(bi.commands, "Command") for c in bi.commands: info += " %s: %s\n" % (c.name, c.synopsis) if bi.selectors: info += " %s:\n" % plural_form(bi.selectors, "Selector") for s in bi.selectors: info += " %s: %s\n" % (s.name, s.synopsis) if bi.formats: info += " %s:\n" % plural_form(bi.formats, "Format") for f in bi.formats: can_open = ' open' if f.has_open else '' can_save = ' save' if f.has_save else '' info += " %s [%s]%s%s\n" % (f.name, f.category, can_open, can_save) logger.info(info, is_html=use_html)
def transparency(session, objects, percent, what=None, target=None): """Set transparency of atoms, ribbons, surfaces, .... Parameters ---------- objects : Objects or None Which objects to set transparency. percent : float Percent transparent from 0 completely opaque, to 100 completely transparent. target : string Characters indicating what to make transparent: a = atoms, b = bonds, p = pseudobonds, c = cartoon, r = cartoon, s = surfaces, A = all """ if objects is None: from chimerax.core.objects import all_objects objects = all_objects(session) from .color import get_targets target, _ = get_targets(target, what, 's') alpha = int(2.56 * (100 - percent)) alpha = min(255, max(0, alpha)) # 0-255 range what = [] if 'a' in target or 's' in target: atoms = objects.atoms if 'a' in target: # atoms c = atoms.colors c[:, 3] = alpha atoms.colors = c what.append('%d atoms' % len(atoms)) if 'b' in target: # bonds bonds = objects.bonds if bonds: c = bonds.colors c[:, 3] = alpha bonds.colors = c what.append('%d bonds' % len(bonds)) if 'p' in target: # pseudobonds bonds = objects.pseudobonds if bonds: c = bonds.colors c[:, 3] = alpha bonds.colors = c what.append('%d pseudobonds' % len(bonds)) if 's' in target: surfs = _set_surface_transparency(atoms, objects.models, session, alpha) what.append('%d surfaces' % len(surfs)) if 'c' in target or 'r' in target: residues = objects.residues c = residues.ribbon_colors c[:, 3] = alpha residues.ribbon_colors = c what.append('%d residues' % len(residues)) if not what: what.append('nothing') from chimerax.core.commands import commas session.logger.status('Set transparency of %s' % commas(what, 'and'))
def defattr(session, file_name, *, log=False, restriction=None): """define attributes on objects Parameters ---------- file_name : string Input file in 'defattr' format log : bool Whether to log assignment info restriction : Structures Collection or None If not None, structures to restrict the assignments to (in addition to any restrictions in the defattr file) """ if restriction is None: from chimerax.atomic import all_structures restriction = all_structures(session) control_defaults = { 'match mode': "any", 'recipient': "atoms", 'none handling': "None" } from chimerax.atomic import Atom, Bond, Pseudobond, Residue, Chain, Structure recipient_info = { "atoms": (Atom, lambda objs: objs.atoms), "bonds": (Bond, lambda objs: objs.bonds), "pseudobonds": (Pseudobond, lambda objs: objs.pseudobonds), "residues": (Residue, lambda objs: objs.residues), "chains": (Chain, lambda objs: objs.chains), # since we always restrict to structures, can just use Objects.models() "molecules": (Structure, lambda objs: objs.models), "structures": (Structure, lambda objs: objs.models), } legal_control_values = { 'match mode': set(["any", "non-zero", "1-to-1"]), 'recipient': set(recipient_info.keys()), 'none handling': set(["None", "string", "delete"]) } all_info = [] def append_all_info(attr_info, data_info, line_num, *, ai=all_info, fn=file_name): if 'attribute' not in attr_info: raise SyntaxError( "No attribute name defined for data lines %d and earlier in %s" % (line_num, fn)) if not data_info: raise SyntaxError("No data lines for attribute '%s' in %s" % (attr_info['attribute'], fn)) ai.append((attr_info, data_info)) from chimerax.core.commands import AtomSpecArg, AttrNameArg, AnnotationError, NoneArg, ColorArg, commas from chimerax.core.commands import IntArg, FloatArg from chimerax.io import open_input with open_input(file_name, encoding="utf-8") as f: data = [] attrs = {} for lnum, raw_line in enumerate(f): # spaces in values could be significant, so instead of stripping just drop the '\n' # (which all line endings are translated to if newline=None [default] for open()) line = raw_line[:-1] if not line.strip() or line[0] == '#': continue if line[0] == '\t': # data line datum = line[1:].split('\t') if len(datum) != 2: raise SyntaxError( "Data line %d in %s not of the form: <tab> atomspec <tab> value" % (lnum + 1, file_name)) data.append((lnum + 1, *datum)) continue # control line try: name, value = line.split(": ") except ValueError: raise SyntaxError( "Line %d in %s is either not of the form 'name: value'" " or is missing initial tab" % (lnum + 1, file_name)) name = name.strip().lower() value = value.strip() if name in attrs: # presumably another set of control/data lines starting append_all_info(attrs, data, lnum + 1) attrs = {} data = [] if name == 'attribute': try: final_value, *args = AttrNameArg.parse(value, session) except AnnotationError as e: raise SyntaxError( "Bad attribute name ('%s') given on line %d of %s: %s" % (value, lnum + 1, file_name, str(e))) elif name not in legal_control_values: raise SyntaxError( "Unrecognized control type ('%s') given on line %d of %s" % (name, lnum + 1, file_name)) elif value not in legal_control_values[name]: raise SyntaxError( "Illegal control value ('%s') for %s given on line %d of %s; legal" " values are: %s" % (value, name, lnum + 1, file_name, commas(legal_control_values[name]))) else: final_value = value attrs[name] = final_value append_all_info(attrs, data, lnum + 1) for attr_info, data_info in all_info: attr_name = attr_info['attribute'] color_attr = attr_name.lower().endswith( 'color') or attr_name.lower().endswith('colour') match_mode = attr_info.get('match mode', control_defaults['match mode']) none_handling = attr_info.get('none handling', control_defaults['none handling']) none_okay = none_handling != 'string' none_seen = False eval_vals = ["true", "false"] if none_okay: eval_vals.append("none") recipient = attr_info.get('recipient', control_defaults['recipient']) recip_class, instance_fetch = recipient_info[recipient] seen_types = set() try: pre_existing_attr = getattr(recip_class, attr_name) except AttributeError: pass else: if callable(pre_existing_attr): raise ValueError( "%s is a method of the %s class and cannot be redefined" % (attr_name, recip_class.__name__)) if attr_name[0].isupper(): raise ValueError( "%s is a constant in the %s class and cannot be redefined" % (attr_name, recip_class.__name__)) for line_num, spec, value_string in data_info: try: atom_spec, *args = AtomSpecArg.parse(spec, session) except AnnotationError as e: raise SyntaxError("Bad atom specifier (%s) on line %d of %s" % (spec, line_num, file_name)) try: objects = atom_spec.evaluate(session, models=restriction) except Exception as e: raise SyntaxError( "Error evaluating atom specifier (%s) on line %d of %s: %s" % (spec, line_num, file_name, str(e))) matches = instance_fetch(objects) if not matches and match_mode != "any": raise SyntaxError( "Selector (%s) on line %d of %s matched nothing" % (spec, line_num, file_name)) if len(matches) > 1 and match_mode == "1-to-1": raise SyntaxError( "Selector (%s) on line %d of %s matched multiple %s" % (spec, line_num, file_name, recipient)) if log: session.logger.info( "Selector %s matched %s" % (spec, commas([str(x) for x in matches], conjunction="and"))) if not value_string: raise SyntaxError("No data value on line %d of %s" % (line_num, file_name)) # Can't just use normal argument parsers willy nilly since strings are allowed to have # leading/trailing whitespace, don't want to accept shorten ed forms of booleans, etc. if color_attr: try: value, text, rest = ColorArg.parse(value_string, session) if rest: raise AnnotationError("trailing text") seen_types.add("color") value = value.uint8x4() except AnnotationError: if none_okay: try: value, text, rest = NoneArg.parse( value_string, session) if rest: raise AnnotationError("trailing text") seen_types.add(None) except AnnotationError: raise SyntaxError( "Value (%s) on line %d of %s is not recognizable as either a" " color value or None" % (value_string, line_num, file_name)) else: raise SyntaxError( "Value (%s) on line %d of %s is not recognizable as a color value" % (value_string, line_num, file_name)) else: if value_string.strip() != value_string: value = value_string seen_types.add(str) elif value_string.startswith('"') and value_string.endswith( '"'): value = value_string[1:-1] seen_types.add(str) elif value_string.lower() in eval_vals: value = eval(value_string.capitalize()) if value is None: seen_types.add(None) else: seen_types.add(bool) else: try: value, text, rest = IntArg.parse(value_string, session) if rest: raise AnnotationError("trailing text") seen_types.add(int) except AnnotationError: try: value, text, rest = FloatArg.parse( value_string, session) if rest: raise AnnotationError("trailing text") seen_types.add(float) except AnnotationError: value = value_string seen_types.add(str) for match in matches: if value is not None or none_handling == "None": setattr(match, attr_name, value) elif hasattr(match, attr_name): if pre_existing_attr: raise RuntimeError( "Cannot remove builtin attribute %s from class %s" % (attr_name, recip_class.__name__)) else: delattr(match, attr_name) can_return_none = None in seen_types seen_types.discard(None) if len(seen_types) == 1: seen_type = seen_types.pop() attr_type = None if seen_type == "color" else seen_type elif seen_types == set([int, float]): attr_type = float else: attr_type = None recip_class.register_attr(session, attr_name, "defattr command", attr_type=attr_type, can_return_none=can_return_none)
def nucleotides_dimensions_list(session): dimensions = NA.list_dimensions() from chimerax.core.commands import commas session.logger.info("Nucleotides dimensions: " + commas(dimensions, 'and'))
def assign_charges(session, uncharged_residues, his_scheme): from chimerax.atomic import Atom Atom.register_attr(session, "charge", "coulombic coloring", attr_type=float) by_structure = {} for r in uncharged_residues: #if r.name == 'HIS': # r._coulombic_his_scheme = his_scheme by_structure.setdefault(r.structure, []).append(r) missing_heavies = [] extra_atoms = [] copy_needed = {} for struct, residue_list in list(by_structure.items()): from chimerax.atomic import Residues by_structure[struct] = residues = Residues(residue_list) missing, extra = check_residues(residues) heavies = [info for info in missing if not info[1].startswith('H')] missing_heavies.extend(heavies) copy_needed[struct] = len(heavies) < len(missing) extra_atoms.extend(extra) if extra_atoms: from chimerax.core.commands import commas if len(extra_atoms) <= 7: atoms_text = commas([str(a) for a in extra_atoms], conjunction="and") else: atoms_text = commas([str(a) for a in extra_atoms[:5]] + ["%d other atoms" % (len(extra_atoms) - 5)], conjunction="and") if len([a for a in extra_atoms if a.element.number == 1]) == len(extra_atoms): hint = " Try deleting all hydrogens first." else: hint = "" raise ChargeError( "Atoms with non-standard names found in standard residues: %s.%s" % (atoms_text, hint)) if missing_heavies: from chimerax.core.commands import commas if len(missing_heavies) <= 7: atoms_text = commas( [str(r) + ' ' + an for r, an in missing_heavies], conjunction="and") else: atoms_text = commas( [str(r) + ' ' + an for r, an in missing_heavies[:5]] + ["%d other atoms" % (len(missing_heavies) - 5)], conjunction="and") session.logger.warning( "The following heavy (non-hydrogen) atoms are missing, which may result" " in inaccurate electrostatics: %s" % atoms_text) for struct, struct_residues in by_structure.items(): if copy_needed[struct]: session.logger.status("Copying %s" % struct, secondary=True) charged_struct = struct.copy(name="copy of " + struct.name) orig_a_to_copy = {} copy_a_to_orig = {} for o_a, c_a in zip(struct.atoms, charged_struct.atoms): orig_a_to_copy[o_a] = c_a copy_a_to_orig[c_a] = o_a orig_r_to_copy = {} copy_r_to_orig = {} for o_r, c_r in zip(struct.residues, charged_struct.residues): orig_r_to_copy[o_r] = c_r copy_r_to_orig[c_r] = o_r from chimerax.addh.cmd import cmd_addh hbond = False if his_scheme is None: if len(struct_residues[struct_residues.names == "HIS"]) > 0: hbond = True from chimerax.atomic import AtomicStructures addh_structures = AtomicStructures([charged_struct]) session.logger.status("Adding hydrogens to copy of %s" % struct, secondary=True) session.silent = True try: cmd_addh(session, addh_structures, hbond=hbond) finally: session.silent = False charged_residues = [orig_r_to_copy[r] for r in struct_residues] session.logger.status("Assigning charges to copy of %s" % struct, secondary=True) else: charged_struct = struct charged_residues = struct_residues # assign charges assign_residue_charges(charged_residues, his_scheme) if copy_needed[struct]: session.logger.status("Copying charges back to %s" % struct, secondary=True) for o_r in struct_residues: for o_a in o_r.atoms: c_a = orig_a_to_copy[o_a] for nb in c_a.neighbors: if nb.residue == c_a.residue and nb not in copy_a_to_orig: c_a.charge += nb.charge o_a.charge = c_a.charge session.logger.status("Destroying copy of %s" % struct, secondary=True) charged_struct.delete()
def cmd_coulombic(session, atoms, *, surfaces=None, his_scheme=None, offset=1.4, spacing=1.0, padding=5.0, map=False, palette=None, range=None, dist_dep=True, dielectric=4.0): if map: session.logger.warning( "Computing electrostatic volume map not yet supported") session.logger.status("Computing Coulombic charge surface%s" % ("/volume" if map else "")) if palette is None: from chimerax.core.colors import BuiltinColormaps cmap = BuiltinColormaps["red-white-blue"] if not cmap.values_specified: rmin, rmax = (-10.0, 10.0) if range is None else range cmap = cmap.linear_range(rmin, rmax) session.logger.status("Matching atoms to surfaces", secondary=True) atoms_per_surf = [] from chimerax.atomic import all_atomic_structures, MolecularSurface, all_atoms if atoms is None: if surfaces is None: # surface all chains for struct in all_atomic_structures(session): # don't create surface until charges checked if struct.num_chains == 0: atoms_per_surf.append((struct.atoms, None, None)) else: for chain in struct.chains: atoms_per_surf.append( (chain.existing_residues.atoms, None, None)) else: for srf in surfaces: if isinstance(srf, MolecularSurface): atoms_per_surf.append((srf.atoms, None, srf)) else: atoms_per_surf.append((all_atoms(session), None, srf)) else: if surfaces is None: # on a per-structure basis, determine if the atoms contain any polymers, and if so then # surface those chains (and not non-polymers); otherwise surface the atoms by_chain = {} for struct, chain_id, chain_atoms in atoms.by_chain: chains = chain_atoms.unique_residues.unique_chains if chains: by_chain.setdefault(struct, {})[chains[0]] = chain_atoms for struct, struct_atoms in atoms.by_structure: try: for chain, shown_atoms in by_chain[struct].items(): chain_atoms = chain.existing_residues.atoms atoms_per_surf.append( (chain_atoms, chain_atoms & shown_atoms, None)) except KeyError: atoms_per_surf.append((struct_atoms, None, None)) else: for srf in surfaces: atoms_per_surf.append((atoms, None, srf)) # check whether the atoms have charges, and if not, that we know how to assign charges # to the requested atoms problem_residues = set() needs_assignment = set() for surf_atoms, shown_atoms, srf in atoms_per_surf: for r in surf_atoms.unique_residues: if getattr(r, '_coulombic_his_scheme', his_scheme) != his_scheme: # should only be set on HIS residues needs_assignment.add(r) else: for a in r.atoms: try: a.charge + 1.0 except (AttributeError, TypeError): if r.name in chargeable_residues: needs_assignment.add(r) else: problem_residues.add(r.name) break if problem_residues: session.logger.status("") from chimerax.core.commands import commas raise UserError( "Don't know how to assign charges to the following residue types: %s" % commas(problem_residues, conjunction='and')) if needs_assignment: session.logger.status("Assigning charges", secondary=True) from .coulombic import assign_charges, ChargeError try: assign_charges(session, needs_assignment, his_scheme) except ChargeError as e: session.logger.status("") raise UserError(str(e)) # Since electrostatics are long range, unlike mlp, don't compute a map (with a distance cutoff) # by default. Instead, compute the values at the surface vertices directly. Only compute a # map afterward if requested. from chimerax.core.undo import UndoState undo_state = UndoState('coulombic') undo_owners = [] undo_old_vals = [] undo_new_vals = [] for surf_atoms, shown_atoms, srf in atoms_per_surf: if srf is None: session.logger.status("Creating surface", secondary=True) from chimerax.surface import surface data = [(surf.atoms, surf) for surf in surface( session, surf_atoms if shown_atoms is None else shown_atoms)] else: data = [(surf_atoms, srf)] session.logger.status("Computing electrostatics", secondary=True) for charged_atoms, target_surface in data: undo_owners.append(target_surface) undo_old_vals.append(target_surface.vertex_colors) if target_surface.normals is None: session.logger.warning( "Surface %s has no vertex normals set, using distance from surface" " of 0 instead of %g" % (target_surface, offset)) target_points = target_surface.vertices else: target_points = target_surface.vertices + offset * target_surface.normals import numpy, os from ._esp import potential_at_points cpu_count = os.cpu_count() vertex_values = potential_at_points( target_surface.scene_position.transform_points(target_points), charged_atoms.scene_coords, numpy.array([a.charge for a in charged_atoms], dtype=numpy.double), dist_dep, dielectric, 1 if cpu_count is None else cpu_count) rgba = cmap.interpolated_rgba(vertex_values) from numpy import uint8 rgba8 = (255 * rgba).astype(uint8) target_surface.vertex_colors = rgba8 undo_new_vals.append(rgba8) undo_state.add(undo_owners, "vertex_colors", undo_old_vals, undo_new_vals, option="S") session.undo.register(undo_state) session.logger.status("", secondary=True) session.logger.status("Finished computing Coulombic charge surface%s" % ("/volume" if map else ""))
def cmd_open_formats(session): '''Report file formats, suffixes and databases that the open command knows about.''' from chimerax.core.commands import commas all_formats = session.open_command.open_data_formats by_category = {} for fmt in all_formats: by_category.setdefault(fmt.category.title(), []).append(fmt) titles = list(by_category.keys()) titles.sort() lines = [] for title in titles: formats = by_category[title] if session.ui.is_gui: lines.extend([ '<table border=1 cellspacing=0 cellpadding=2>', '<tr><th colspan="3">%s' % title, '<tr><th>File format<th>Short name(s)<th>Suffixes' ]) else: session.logger.info(title) session.logger.info('File format, Short name(s), Suffixes:') formats.sort(key = lambda f: f.name.lower()) for f in formats: if session.ui.is_gui: from html import escape if f.reference_url: descrip = '<a href="%s">%s</a>' % (f.reference_url, escape(f.synopsis)) else: descrip = escape(f.synopsis) lines.append('<tr><td>%s<td>%s<td>%s' % (descrip, escape(commas(f.nicknames)), escape(', '.join(f.suffixes)))) else: session.logger.info(' %s: %s: %s' % (f.synopsis, commas(f.nicknames), ', '.join(f.suffixes))) if session.ui.is_gui: lines.append('</table>') lines.append('<p></p>') else: session.logger.info('\n') if session.ui.is_gui: lines.extend(['<table border=1 cellspacing=0 cellpadding=2>', '<tr><th>Database<th>Formats']) else: session.logger.info('Database, Formats:') database_names = session.open_command.database_names database_names.sort(key=lambda dbn: dbn.lower()) for db_name in database_names: db_info = session.open_command.database_info(db_name) if 'web fetch' in db_info.keys() or db_name == 'help': continue for fmt_name, fetcher_info in db_info.items(): if fetcher_info.is_default: default_name = session.data_formats[fmt_name].nicknames[0] break else: continue format_names = [session.data_formats[fmt_name].nicknames[0] for fmt_name in db_info.keys()] format_names.sort() format_names.remove(default_name) format_names.insert(0, default_name) if not session.ui.is_gui: session.logger.info(' %s: %s' % (db_name, ', '.join(format_names))) continue line = '<tr><td>%s<td>%s' % (db_name, ', '.join(format_names)) lines.append(line) if session.ui.is_gui: lines.append('</table>') msg = '\n'.join(lines) session.logger.info(msg, is_html=True)