def sequence_model(session, targets, *, block=None, multichain=True, custom_script=None, dist_restraints=None, executable_location=None, fast=False, het_preserve=False, hydrogens=False, license_key=None, num_models=5, show_gui=True, temp_path=None, thorough_opt=False, water_preserve=False): ''' Command to generate a comparative model of one or more chains ''' from chimerax.core.errors import UserError seen = set() for alignment, seq in targets: if alignment in seen: raise UserError("Only one target sequence per alignment allowed;" " multiple targets chosen in alignment %s" % alignment) seen.add(alignment) if block is None: block = session.in_script or not session.ui.is_gui if fast: num_models = 1 from . import comparative try: comparative.model(session, targets, block=block, multichain=multichain, custom_script=custom_script, dist_restraints=dist_restraints, executable_location=executable_location, fast=fast, het_preserve=het_preserve, hydrogens=hydrogens, license_key=license_key, num_models=num_models, show_gui=show_gui, temp_path=temp_path, thorough_opt=thorough_opt, water_preserve=water_preserve) except comparative.ModelingError as e: raise UserError(e)
def check_if_params_exist(session, residue): from chimerax.atomic import Residues from chimerax.core.errors import UserError from chimerax.isolde.openmm.openmm_interface import ( create_openmm_topology, find_residue_templates, ) ffmgr = session.isolde.forcefield_mgr ff_name = session.isolde.sim_params.forcefield ff = ffmgr[ff_name] #ligand_db = ffmgr.ligand_db(ff_name) template_dict = find_residue_templates(Residues([residue]), ff, logger=session.logger) err_str_base = ( 'Residue name {} already maps to MD template {}. If this is a ' 'different chemical species, you will need to choose a new name.') if len(template_dict): err_str = err_str_base.format(residue.name, template_dict[0]) raise UserError(err_str) top, residue_templates = create_openmm_topology(residue.atoms, {}) for r in top.residues(): break assigned, ambiguous, unmatched = ff.assignTemplates( top, ignoreExternalBonds=False) if len(assigned): err_str = err_str_base.format(residue.name, assigned[r][0].name) raise UserError(err_str)
def font_command(self, tokens): # TODO: need to handle platform font families with spaces in them if len(tokens) not in (3, 4): raise UserError( "Expected 'fontFamily pointSize [style]' after %s" % tokens[0]) family = tokens[1].lower() if family in ('times', 'serif'): family = 'SERIF' elif family in ('helvetica', 'sans'): family = 'SANS' elif family in ('courier', 'typewriter'): family = 'TYPEWRITER' else: raise UserError('Unknown font family') size = self.parse_int(tokens[2]) if size < 1: raise UserError('Font size must be at least 1') if len(tokens) == 3: style = 'PLAIN' else: style = tokens[3].lower() if style not in ('plain', 'bold', 'italic', 'bolditalic'): raise UserError('unknown font style') style = style.upper() self.cur_font = [family, size, style]
def view_objects(objects, v, clip, cofr, pad): if objects.empty(): from chimerax.core.errors import UserError raise UserError('No objects specified.') disp = objects.displayed() b = disp.bounds() if b is None: from chimerax.core.errors import UserError raise UserError('No displayed objects specified.') v.view_all(b, pad = pad) c, r = b.center(), b.radius() cp = v.clip_planes if clip: vd = v.camera.view_direction() cp.set_clip_position('near', c - r * vd, v) cp.set_clip_position('far', c + r * vd, v) else: cp.remove_plane('near') cp.remove_plane('far') if cofr: v.center_of_rotation_method = 'center of view' if not clip: v.set_rotation_depth(c)
def name(session, name, text=None, skip_check=False): if name == "all": raise UserError("\"all\" is reserved and cannot be shown or defined") if text is None: from chimerax.core.commands import get_selector_description try: desc = get_selector_description(name, session) except KeyError: raise UserError("\"%s\" is not defined" % name) else: if desc: session.logger.info('\t'.join([name, desc])) else: if _is_reserved(name): raise UserError("\"%s\" is reserved and cannot be redefined" % name) if not skip_check: try: ast, used, unused = AtomSpecArg.parse(text, session) if unused: raise AnnotationError("contains extra trailing text") except AnnotationError as e: raise UserError("\"%s\": %s" % (text, str(e))) def selector(session, models, results, spec=text): objects, used, unused = ObjectsArg.parse(spec, session) results.combine(objects) from chimerax.core.commands import register_selector register_selector(name, selector, session.logger, user=True, desc=text) session.basic_actions.define(name, text)
def _show_axis(session, tf, color, length, radius, coordinate_system): axis, axis_point, angle, axis_shift = tf.axis_center_angle_shift() if angle < 0.1: from chimerax.core.errors import UserError raise UserError('Rotation angle is near zero (%g degrees)' % angle) b = coordinate_system.bounds() if b is None: from chimerax.core.errors import UserError raise UserError('Model %s must be visible to show axis' % coordinate_system) from chimerax.geometry import project_to_axis axis_center = project_to_axis(b.center(), axis, axis_point) axis_length = b.width() if length is None else length hl = 0.5 * axis_length ap1 = axis_center - hl * axis ap2 = axis_center + hl * axis from chimerax.markers import MarkerSet, create_link mset = MarkerSet(session, 'rotation axis') mset.scene_position = coordinate_system.scene_position r = 0.025 * axis_length if radius is None else radius m1 = mset.create_marker(ap1, color, r) m2 = mset.create_marker(ap2, color, r) b = create_link(m1, m2, color, r) b.halfbond = True session.models.add([mset]) return mset
def run_script(session): from chimerax.core.errors import UserError from chimerax.isolde.parmed import install_parmed_if_necessary install_parmed_if_necessary(session) from Qt.QtWidgets import QFileDialog import os filter = "AMBER Parameter files (*.mol2 *.frcmod)" files, _ = QFileDialog.getOpenFileNames(None, "Choose one .mol2 and one .frcmod file", "", filter) if len(files)!=2: raise UserError("Please select exactly one .mol2 and one .frcmod file!") matched_files = {} for f in files: matched_files[os.path.splitext(f)[1].lower()] = f if '.mol2' not in matched_files.keys() or '.frcmod' not in matched_files.keys(): raise UserError('Please select exactly one .mol2 and one .frcmod file!') mol2, frcmod = matched_files['.mol2'], matched_files['.frcmod'] from chimerax.isolde.openmm.amberff.amber_convert import amber_to_ffxml try: ffxml = amber_to_ffxml(frcmod, mol2) except Exception as e: raise UserError(f'ParmEd failed to run with the following error. Do your .mol2 and .frcmod match?\n {str(e)}') session.logger.info(f'Converted AMBER files {mol2} and {frcmod} to OpenMM FFXML file {ffxml}.') if hasattr(session, 'isolde'): ff_name = session.isolde.sim_params.forcefield forcefield = session.isolde.forcefield_mgr[ff_name] forcefield.loadFile(ffxml, resname_prefix='USER_') session.logger.info('This has been loaded into ISOLDE\'s forcefield for this session. For ' 'future sessions you should add it using the "Load residue MD definition(s)" button.') else: session.logger.info('On starting ISOLDE you can add it to the forcefield using the ' '"Load residue MD definition(s)" button.')
def cmd_torsion(session, atoms, value=None, *, move="small"): """Wrapper called by command line.""" if len(atoms) != 4: raise UserError( "Must specify exactly 4 atoms for 'torsion' command; you specified %d" % len(atoms)) a1, a2, a3, a4 = atoms from chimerax.geometry import dihedral cur_torsion = dihedral(*[a.scene_coord for a in atoms]) if value is None: session.logger.info( "Torsion angle for atoms %s %s %s %s is %g\N{DEGREE SIGN}" % (a1, a2.string(relative_to=a1), a3.string(relative_to=a2), a4.string(relative_to=a3), cur_torsion)) return for nb, bond in zip(a2.neighbors, a2.bonds): if nb == a3: break else: raise UserError( "To set torsion, middle two atoms (%s %s) must be bonded;they aren't" % (a2, a3.string(relative_to=a2))) move_smaller = move == "small" mgr = session.bond_rotations from .manager import BondRotationError try: rotater = mgr.new_rotation(bond, move_smaller_side=move_smaller) except BondRotationError as e: raise UserError(str(e)) rotater.angle += value - cur_torsion mgr.delete_rotation(rotater)
def name(session, name, text=None): if name == "all": raise UserError("\"all\" is reserved and cannot be redefined") if text is None: from chimerax.core.commands import get_selector try: sel = get_selector(name) except KeyError: raise UserError("\"%s\" is not defined" % name) else: value = _get_name_desc(sel, True) session.logger.info('\t'.join([name, value])) else: try: ast, used, unused = AtomSpecArg.parse(text, session) if unused: raise AnnotationError("contains extra trailing text") except AnnotationError as e: raise UserError("\"%s\": %s" % (text, str(e))) def selector(session, models, results, spec=text): objects, used, unused = ObjectsArg.parse(spec, session) results.combine(objects) selector.name_text = text from chimerax.core.commands import register_selector register_selector(name, selector, session.logger)
def minimize_link_lengths(mols, pbonds, iterations, frames, session): if len(mols) == 0: from chimerax.core.errors import UserError raise UserError('No structures specified for minimizing crosslinks.') mol_links, mol_pbonds = links_by_molecule(pbonds, mols) if len(mol_links) == 0: from chimerax.core.errors import UserError raise UserError('No pseudobonds between molecules.') if len(mols) == 1: iterations = min(1, iterations) if not frames is None: pos0 = dict((m, m.position) for m in mols) from numpy import array, float64 from chimerax.geometry import align_points for i in range(iterations): for m in mols: if m in mol_links: atom_pairs = mol_links[m] moving = array([a1.scene_coord for a1, a2 in atom_pairs], float64) fixed = array([a2.scene_coord for a1, a2 in atom_pairs], float64) tf, rms = align_points(moving, fixed) m.position = tf * m.position lengths = [pb.length for pb in mol_pbonds] lengths.sort(reverse=True) lentext = ', '.join('%.1f' % d for d in lengths) session.logger.info('%d crosslinks, lengths: %s' % (len(mol_pbonds), lentext)) if not frames is None: for m in mols: interpolate_position(m, pos0[m], m.position, frames, session.triggers)
def crosslinks_histogram(session, pbonds, coordsets=None): ''' Show histogram of crosslink lengths. Parameters ---------- pbonds : Pseudobonds Crosslinks to show in histogram coordsets : AtomicStructure If a single pseudobond is specified plot a histogram of its length across the specified coordinate sets is produced. ''' if len(pbonds) == 0: from chimerax.core.errors import UserError raise UserError('No pseudobonds specified.') if coordsets: if len(pbonds) == 1: from .lengths import EnsemblePlot EnsemblePlot(session, pbonds[0], coordsets) else: from chimerax.core.errors import UserError raise UserError( 'Plotting coordset lengths requires exactly one crosslink, got %d.' % len(pbonds)) else: from .lengths import LengthsPlot LengthsPlot(session, pbonds)
def write_phenix_refine_cmd(session, model, model_file_name=None, param_file_name=None, restrain_coordination_sites=False, include_hydrogens=False, num_processors=1, num_macrocycles=6, nqh_flips=True, scattering_type='xray'): from chimerax.clipper import get_symmetry_handler sh = get_symmetry_handler(model, create=False) crystal_error_str = 'Model must be a crystal structure initialised in Clipper with experimental data!' if sh is None: raise UserError(crystal_error_str) if not len(sh.map_mgr.xmapsets): raise UserError(crystal_error_str) xmapset = sh.map_mgr.xmapsets[0] from .refine_input import write_phenix_refine_defaults write_phenix_refine_defaults( session, model, xmapset, model_file_name=model_file_name, param_file_name=param_file_name, restrain_coordination_sites=restrain_coordination_sites, include_hydrogens=include_hydrogens, num_processors=num_processors, num_macrocycles=num_macrocycles, nqh_flips=nqh_flips, scattering_type=scattering_type)
def paths_by_time_and_channel(paths): tc_paths = [] reg_paths = [] from os.path import split for path in paths: dir, filename = split(path) if '{t}' in filename or '{c}' in filename or '{z}' in filename: tc_zpaths = parse_path_tcz(dir, filename) if len(tc_zpaths) == 0: from chimerax.core.errors import UserError raise UserError('No file matching pattern not found: %s' % path) tc_paths.extend(tc_zpaths) else: from glob import glob gpaths = glob(path) if len(gpaths) == 0: from chimerax.core.errors import UserError raise UserError('File not found: %s' % path) reg_paths.extend(gpaths) if reg_paths: tc_paths.append((None, None, reg_paths)) return tc_paths
def run_script(session): from chimerax.atomic import selected_atoms from chimerax.build_structure import modify_atom from chimerax.build_structure.mod import ParamError from chimerax.core.errors import UserError sel = selected_atoms(session) if len(sel) != 1: raise UserError('Please select a single atom!') sel = sel[0] current_num_bonds = len(sel.neighbors) current_color = sel.color try: modify_atom(sel, sel.element, current_num_bonds + 1, connect_back=False, res_name=sel.residue.name) sel.color = current_color except ParamError as e: # If modify_atom throws an error at the previous step, it will have deleted # any attached hydrogens and not put them back. We need to put them back here. modify_atom(sel, sel.element, current_num_bonds, connect_back=False, res_name=sel.residue.name) sel.color = current_color raise UserError(str(e))
def launch_modeller(self): from chimerax.core.commands import run, FileNameArg, StringArg from chimerax.core.errors import UserError alignments = self.alignment_list.value if not alignments: raise UserError("No alignments chosen for modeling") aln_seq_args = [] for aln in alignments: seq_menu = self.seq_menu[aln] seq = seq_menu.value if not seq: raise UserError("No target sequence chosen for alignment %s" % aln.ident) aln_seq_args.append( StringArg.unparse("%s:%d" % (aln.ident, aln.seqs.index(seq) + 1))) from .settings import get_settings settings = get_settings(self.session) run( self.session, "modeller comparative %s multichain %s numModels %d fast %s hetPreserve %s" " hydrogens %s%s waterPreserve %s" % (" ".join(aln_seq_args), repr( settings.multichain).lower(), settings.num_models, repr(settings.fast).lower(), repr(settings.het_preserve).lower(), repr(settings.hydrogens).lower(), " tempPath %s" % FileNameArg.unparse(settings.temp_path) if settings.temp_path else "", repr(settings.water_preserve).lower())) self.delete()
def run_script(session): from chimerax.atomic import selected_residues from chimerax.build_structure import modify_atom from chimerax.build_structure.mod import ParamError from chimerax.core.errors import UserError sel = selected_residues(session) if len(sel) != 1 or sel[0].name not in ('ASP', 'GLU'): raise UserError('Please select a single ASP or GLU residue!') sel = sel[0] if sel.name == 'ASP': pos = 'D' else: pos = 'E' o_atom = sel.find_atom(f'O{pos}2') other_o = sel.find_atom(f'O{pos}1') if o_atom is None or other_o is None: raise UserError( 'Selected acid sidechain is missing one or both of its oxygen atoms!' ) if len(o_atom.neighbors) != 1 or len(other_o.neighbors) != 1: raise UserError( 'Selected acid sidechain already has a substituent on its carboxyl group!' ) new_h = modify_atom(o_atom, o_atom.element, 2, connect_back=False, res_name=sel.name)[1] new_h.color = [255, 255, 255, 255]
def polygon_command(self, tokens): # TODO: use GLU to tesselate polygon # for now, find center and make a triangle fan if len(tokens) % 3 != 1: raise UserError("Expected 'x1 y1 z1 ... xN yN zN' after %s" % tokens[0]) data = [self.parse_float(x) for x in tokens[1:]] vertices = array(data, dtype=float32) n = len(data) // 3 vertices.shape = (n, 3) if n < 3: raise UserError("Need at least 3 vertices in a polygon") self.num_objects += 1 if self.cur_description is not None: description = self.cur_description else: description = 'object %d: polygon' % self.num_objects from chimerax.geometry import Plane plane = Plane(vertices) loops = ((0, len(vertices) - 1), ) t = surface.triangulate_polygon(loops, plane.normal, vertices) normals = empty(vertices.shape, dtype=float32) normals[:] = plane.normal triangles = array(t, dtype=int32) shape = AtomicShapeInfo(vertices, normals, triangles, _cvt_color(self.cur_color), self.cur_atoms, description) self.shapes.append(shape)
def restrain_torsions(session, residues, template_residues=None, backbone=True, sidechains=True, angle_range=None, alpha=None, spring_constant=250, identical_sidechains_only=True, adjust_for_confidence=False, confidence_type='plddt'): if angle_range is None: if adjust_for_confidence: angle_range = 150 else: angle_range = 60 if alpha is None: if adjust_for_confidence: alpha = 0.5 else: alpha = 0.2 logger = session.logger if not residues: raise UserError('Must specify residues to restrain') from chimerax.atomic import Residue pres = residues[residues.polymer_types == Residue.PT_AMINO] if not len(pres): raise UserError( 'This command is only applicable to amino acids, but the ' 'selection contains none.') if len(pres) != len(residues): logger.warning( 'The "isolde restrain torsions" command only applies to ' 'protein chains. Other residues have been ignored.') if template_residues: tres = template_residues[template_residues.polymer_types == Residue.PT_AMINO] else: tres = pres from math import radians from ..molobject import AdaptiveDihedralRestraintMgr kappa = AdaptiveDihedralRestraintMgr.angle_range_to_kappa( radians(angle_range)) from .restraint_utils import restrain_torsions_to_template restrain_torsions_to_template( session, tres, pres, restrain_backbone=backbone, restrain_sidechains=sidechains, kappa=kappa, alpha=alpha, spring_constant=spring_constant, identical_sidechains_only=identical_sidechains_only, adjust_for_confidence=adjust_for_confidence, confidence_type=confidence_type)
def name_frozen(session, name, objects): if _is_reserved(name): raise UserError("\"%s\" is reserved and cannot be redefined" % name) if objects.empty(): raise UserError("nothing is selected by specifier") from chimerax.core.commands import register_selector register_selector(name, objects, session.logger, user=True) session.basic_actions.define(name, objects)
def isolde_step(session, residue=None, view_distance=None, interpolate_frames=None, polymeric_only=True, select=True): from chimerax.atomic import Residues, Residue if isinstance(residue, Residues): if len(residue) == 0: raise UserError('Selection contains no residues!') if len(residue) > 1: session.logger.warning( 'Multiple residues selected! Going to the first...') residue = residue[0] m = residue.structure else: if hasattr(session, 'isolde'): m = session.isolde.selected_model else: from chimerax.atomic import AtomicStructure atomic_structures = session.models.list(type=AtomicStructure) if len(atomic_structures): m = atomic_structures[0] else: m = None if m is None: raise UserError('No atomic structures are open!') from ..navigate import get_stepper rs = get_stepper(m) if view_distance is not None: rs.view_distance = view_distance if interpolate_frames is not None: rs.interpolate_frames = interpolate_frames if select: session.selection.clear() if residue is None: rs.incr_residue() elif isinstance(residue, Residue): rs.step_to(residue) else: # Assume residue is a string argument rarg = residue.lower() if rarg == 'first': rs.first_residue(polymeric_only) rs.step_direction = 'next' elif rarg == 'last': rs.last_residue(polymeric_only) rs.step_direction = 'previous' elif rarg in ('next', 'prev'): rs.incr_residue(rarg, polymeric_only) else: raise UserError( 'Unrecognised residue argument! If specified, must ' 'be either a residue, "first", "last", "next" or "prev"') if select: atoms = rs.current_residue.atoms atoms.selected = True atoms.intra_bonds.selected = True
def segmentation_colors( session, segmentations, color=None, map=None, surfaces=None, by_attribute=None, outside_color=None, step=None, # Step is just used for surface coloring interpolation. max_segment_id=None): if len(segmentations) == 0: from chimerax.core.errors import UserError raise UserError('No segmentations specified') if max_segment_id is not None: for seg in segmentations: seg._max_segment_id = max_segment_id if color is not None: color = color.uint8x4() if outside_color is not None: outside_color = outside_color.uint8x4() if map is None and surfaces is None: for seg in segmentations: _color_segmentation(seg, by_attribute, color, outside_color) if map is not None: if len(segmentations) != 1: from chimerax.core.errors import UserError raise UserError( 'segmentation colors: Can only specify one segmentation' ' when coloring a map, got %d' % len(segmentations)) seg = segmentations[0] if tuple(map.data.size) != tuple(seg.data.size): from chimerax.core.errors import UserError raise UserError('segmentation colors: Volume size %d,%d,%d' % tuple(map.data.size) + ' does not match segmentation size %d,%d,%d' % tuple(seg.data.size)) _color_map(map, seg, by_attribute, color, outside_color) if surfaces is not None: if len(segmentations) != 1: from chimerax.core.errors import UserError raise UserError( 'segmentation colors: Can only specify one segmentation' ' when coloring a surface, got %d' % len(segmentations)) seg = segmentations[0] for surface in surfaces: _color_surface(surface, seg, by_attribute, color=color, outside_color=outside_color, step=step)
def parse(text, session): token, text, rest = next_token(text) try: s = tuple(float(f) for f in token.split(',')) except Exception: raise UserError('Must specify 2 comma-separated floats, got %s' % token) if len(s) != 2: raise UserError('Must specify 2 comma-separated floats, got %s' % token) return s, text, rest
def __init__(self, session, file_name, format_name): self.file_name = file_name self.data_format = file_format(session, file_name, format_name) if self.data_format is None: from os.path import splitext from chimerax import io ext = splitext(io.remove_compression_suffix(file_name))[1] if ext: raise UserError("Unrecognized file suffix '%s'" % ext) raise UserError("'%s' has no suffix" % file_name)
def _save_info(self): from chimerax.core.errors import UserError from chimerax.core.commands import run dist_grp = self.session.pb_manager.get_group("distances", create=False) if not dist_grp: raise UserError("No distances to save!") pbs = dist_grp.pseudobonds if not pbs: raise UserError("No distances to save!") run(self.session, "distance save browse")
def map_to_fit(selection): from chimerax.map import Volume vlist = [m for m in selection.models if isinstance(m, Volume)] if len(vlist) == 0: raise UserError('No atoms or maps for %s' % selection.spec) elif len(vlist) > 1: raise UserError('Multiple maps for %s' % selection.spec) v = vlist[0] return v
def check_fit_options(atoms_or_map, volume, metric, resolution, symmetric, move_whole_molecules, search, sequence): if volume is None: raise UserError('Must specify "in" keyword, e.g. fit #1 in #2') if sequence > 0 and search > 0: raise UserError('Cannot use "sequence" and "search" options together.') if sequence > 0 and symmetric: raise UserError( 'Cannot use "sequence" and "symmetric" options together.') if search and symmetric: raise UserError('Symmetric fitting not available with fit search') if symmetric: if not metric in ('correlation', 'cam', None): raise UserError('Only "correlation" and "cam" metrics are' ' supported with symmetric fitting') if len(volume.data.symmetries) == 0: raise UserError('Volume %s has not symmetry assigned' % volume.name) if len(atoms_or_map.atoms) > 0 and resolution is None: if symmetric: raise UserError('Must specify a map resolution for' ' symmetric fitting of an atomic model') elif metric in ('correlation', 'cam'): raise UserError('Must specify a map resolution when' ' fitting an atomic model using correlation') if sequence > 0 and not move_whole_molecules: raise UserError('Fit sequence does not support' ' moving partial molecules')
def swap_aa(session, residues, res_type, *, angle_slop=None, bfactor=None, criteria=default_criteria, density=None, dist_slop=None, hbond_allowance=None, ignore_other_models=False, rot_lib=None, log=True, preserve=None, relax=True, retain=False, score_method="num", overlap_cutoff=None): ''' Command to swap amino acid side chains ''' residues = _check_residues(residues) if type(criteria) == str: for c in criteria: if c not in "dchp": raise UserError("Unknown criteria: '%s'" % c) elif preserve is not None: raise UserError( "'preserve' not compatible with Nth-most-probable criteria") if rot_lib is None: rot_lib = session.rotamers.default_command_library_name if log: session.logger.info("Using %s library" % rot_lib) from . import swap_res swap_res.swap_aa(session, residues, res_type, bfactor=bfactor, clash_hbond_allowance=hbond_allowance, clash_score_method=score_method, clash_overlap_cutoff=overlap_cutoff, criteria=criteria, density=density, hbond_angle_slop=angle_slop, hbond_dist_slop=dist_slop, ignore_other_models=ignore_other_models, rot_lib=rot_lib, log=log, preserve=preserve, hbond_relax=relax, retain=retain)
def parse(text, session): token, text, rest = next_token(text) try: s = tuple(int(f) for f in token.split(',')) except Exception: raise UserError('Must specify integer or 3 comma-separated integers, got %s' % token) if len(s) == 1: s = (s[0],s[0],s[0]) if len(s) != 3: raise UserError('Must specify integer or 3 comma-separated integers, got %s' % token) return s, text, rest
def verify_residue(value): try: res = int(value) except ValueError: raise UserError( 'The residue nr. %s specified in the additional distance restraints file' ' is not an integer.' % value) if res <= 0: raise UserError( 'The residue nr. %d specified in the additional distance restraints file' ' needs to be greater than 0.' % res) return res
def save(self, session, path, *, alignment=None, **kw): if not alignment: alignments = session.alignments.alignments from chimerax.core.errors import UserError if not alignments: raise UserError("No alignments open!") elif len(alignments) != 1: raise UserError( "More than one alignment open;" " use 'alignment' keyword to specify one") alignment = alignments[0] alignment.save(path, format_name=name)