def check_matching_sizes(v1, v2, step, subregion, operation): if v1.data.size != v2.data.size: raise CommandError('Cannot %s grids of different size' % operation) if step or subregion: if not v1.same_region(v1.region, v2.region): raise CommandError('Cannot %s grids with different subregions' % operation)
def show_axis(tf, color, os): import Matrix axis, axis_point, angle, axis_shift = Matrix.axis_center_angle_shift(tf) if angle < 0.1: raise CommandError('Rotation angle is near zero (%g degrees)' % angle) have_box, box = os.bbox() if not have_box: # TODO: Chimera does not provide bounding box of full model. raise CommandError('First model must be visible to show axis') axis_center = Matrix.project_to_axis(box.center().data(), axis, axis_point) axis_length = max((box.urb - box.llf).data()) hl = 0.5 * axis_length ap1 = map(lambda a, b: a - hl * b, axis_center, axis) ap2 = map(lambda a, b: a + hl * b, axis_center, axis) from VolumePath import Marker_Set, Link from VolumePath.markerset import chimera_color m = Marker_Set('rotation axis') mm = m.marker_model() mm.openState.xform = os.xform if color: mm.color = chimera_color(color) radius = 0.025 * axis_length m1 = m.place_marker(ap1, None, radius) m2 = m.place_marker(ap2, None, radius) Link(m1, m2, None, radius)
def parse_arg(fields, spec, cmd_name, session, akw): name, parse, pkw, multiple = arg_spec(spec) n = len(parse) if isinstance(parse, tuple) else 1 if len(fields) < n: raise CommandError('%s: Missing argument for keyword "%s"' % (cmd_name, name)) try: if isinstance(parse, tuple): value = True if n == 0 else tuple(p(a, session, **pkw) for p,a in zip(parse,fields[:n])) else: value = parse(fields[0], session, **pkw) except CommandError as e: args = ' '.join(f for f in fields[:n]) raise CommandError('%s invalid %s argument "%s": %s' % (cmd_name, name, args, str(e))) except: args = ' '.join(f for f in fields[:n]) raise raise CommandError('%s invalid %s argument "%s"' % (cmd_name, name, args)) if multiple: if name in akw: akw[name].append(value) else: akw[name] = [value] else: akw[name] = value return n
def spine(operation, regions, spacing=None, tipLength=None, color=None, showDiameter=False): sel = parse_object_specifier(regions, 'segmentation region') import Surface plist = Surface.selected_surface_pieces(sel, include_outline_boxes=False) from Segger.regions import Segmentation rlist = [ p.region for p in plist if (hasattr(p, 'region') and isinstance(p.model, Segmentation)) ] if len(rlist) == 0: raise CommandError('No segmentation regions specified: "%s"' % regions) if not (spacing is None or isinstance(spacing, (int, float)) and spacing > 0): raise CommandError('spacing must be positive numeric value') if not (tipLength is None or isinstance(tipLength, (int, float)) and tipLength > 0): raise CommandError('tipLength must be positive numeric value') if not color is None: from Commands import parse_color color = parse_color(color) if showDiameter: from _surface import SurfaceModel diam_model = SurfaceModel() diam_model.name = 'Diameters' from chimera import openModels openModels.add([diam_model], sameAs=rlist[0].segmentation) else: diam_model = None import spine from chimera import replyobj from PathLength import path_length for r in rlist: mset = spine.trace_spine(r, spacing, tipLength, color) slen = path_length([l.bond for l in mset.links()]) r.set_attribute('spine length', slen) msg = 'Spine length for region %d is %.4g' % (r.rid, slen) dmax, dmin = spine.measure_diameter(r, mset, diam_model) if not dmax is None: r.set_attribute('diameter1', dmax) r.set_attribute('diameter2', dmin) msg += ', diameters %.4g, %.4g' % (dmax, dmin) kave, kmin, kmax = spine.measure_curvature(mset) if not kmax is None: r.set_attribute('curvature average', kave) r.set_attribute('curvature minimum', kmin) r.set_attribute('curvature maximum', kmax) msg += ', curvature %.4g (ave), %.4g (max), %.4g (min)' % ( kave, kmax, kmin) replyobj.info(msg + '\n')
def color_arg(color, session): if isinstance(color, (tuple, list)): if len(color) == 4: return tuple(color) elif len(color) == 3: return tuple(color) + (1,) elif isinstance(color, str): fields = color.split(',') if len(fields) == 1: from webcolors import name_to_rgb try: rgb = name_to_rgb(color) except ValueError: raise CommandError('Unknown color: "%s"' % repr(color)) return (rgb[0]/255.0,rgb[1]/255.0,rgb[2]/255.0,1) else: try: rgba = tuple(float(c) for c in fields) except: raise CommandError('Unrecognized color: "%s"' % repr(color)) if len(rgba) == 3: rgba = rgba + (1.0,) if len(rgba) == 4: return rgba raise CommandError('Unrecognized color: "%s"' % repr(color))
def float_arg(s, session, min = None, max = None): x = float(s) if not min is None and x < min: raise CommandError('Value must be >= %g, got %g' % (min, x)) if not max is None and x > max: raise CommandError('Value must be <= %g, got %g' % (max, x)) return x
def chain_arg(s, session): sel = parse_specifier(s, session) clist = sel.chains() if len(clist) == 0: raise CommandError('No chains specified') elif len(clist) > 1: raise CommandError('Multiple chains specified') return clist[0]
def model_arg(s, session): sel = parse_specifier(s, session) mlist = sel.models() if len(mlist) == 0: raise CommandError('No models specified') elif len(mlist) > 1: raise CommandError('Multiple models specified') return mlist[0]
def model_id_arg(s, session): if len(s) == 0 or s[0] != '#': raise CommandError('model id argument must start with "#", got "%s"' % s) try: mid = tuple(int(i) for i in s[1:].split('.')) except ValueError: raise CommandError('model id argument be integers separated by ".", got "%s"' % s) return mid
def openstate_arg(s, session): sel = parse_specifier(s, session) oslist = set([m.openState for m in sel.models()]) if len(oslist) == 0: raise CommandError('No models specified') elif len(oslist) > 1: raise CommandError('Multiple coordinate systems specified') return oslist.pop()
def molecules_arg(s, session, min = 0): sel = parse_specifier(s, session) mlist = sel.molecules() if len(mlist) < min: if len(mlist) == 0: raise CommandError('No molecule specified, require %d' % min) else: raise CommandError('%d molecules specified, require at least %d' % (len(mlist), min)) return mlist
def check_number(value, name, type = (float,int), allow_none = False, positive = False, nonnegative = False): if allow_none and value is None: return if not isinstance(value, type): raise CommandError('%s must be number, got "%s"' % (name, str(value))) if positive and value <= 0: raise CommandError('%s must be > 0' % name) if nonnegative and value < 0: raise CommandError('%s must be >= 0' % name)
def parse_arguments(cmd_name, arg_string, session, required_args = (), optional_args = (), keyword_args = ()): fields = split_fields(arg_string) n = len(fields) akw = {} a = 0 spec = None args = [] # Make keyword abbreviation table. kw_args = tuple(optional_args) + tuple(keyword_args) kwnames = [arg_specifier_name(s) for s in kw_args] kwt = abbreviation_table(kwnames) # Parse keyword arguments. keyword_spec = dict([(arg_specifier_name(s),s) for s in kw_args]) while a < n: f = fields[a] fl = f.lower() if not fl in kwt: args.append(f) a += 1 continue spec = keyword_spec[kwt[fl]] a += 1 + parse_arg(fields[a+1:], spec, cmd_name, session, akw) # Parse required arguments. a = 0 na = len(args) for spec in required_args: if a >= na: raise CommandError('%s: Missing required argument "%s"' % (cmd_name, arg_specifier_name(spec))) a += parse_arg(args[a:], spec, cmd_name, session, akw) # Parse optional arguments. for spec in optional_args: if a >= na: break a += parse_arg(args[a:], spec, cmd_name, session, akw) # Allow last positional argument to have multiple values. if spec: multiple = arg_spec(spec)[3] if multiple: while a < na: a += parse_arg(args[a:], spec, cmd_name, session, akw) if a < na: raise CommandError('%s: Extra argument "%s"' % (cmd_name, args[a])) return akw
def distance(operation, object1, object2, multiple=False, show=False, color=(0, 1, 1, 1)): a1 = object1.atoms() import Surface as s s1 = s.selected_surface_pieces(object1, include_outline_boxes=False) if len(a1) == 0 and len(s1) == 0: raise CommandError('No atoms or surfaces specified') a2 = object2.atoms() s2 = s.selected_surface_pieces(object2, include_outline_boxes=False) if len(a2) == 0 and len(s2) == 0: raise CommandError('No target atoms or surfaces') # Remove near stuff. if a1: a2 = list(set(a2).difference(a1)) if s1: s2 = list(set(s2).difference(s1)) name2 = object_name(a2, s2) xyz2 = point_array(a2, s2) if show: from Commands import parse_color color = parse_color(color) from _surface import SurfaceModel surf = SurfaceModel() surf.name = 'Distance measurement' from chimera import openModels openModels.add([surf]) else: surf = None if multiple: pairs = [([a], []) for a in a1] + [([], [s]) for s in s1] else: pairs = [(a1, s1)] for a, s in pairs: name = object_name(a, s) xyz = point_array(a, s) report_distance(xyz, xyz2, name, name2, surf, color)
def contact_area(operation, surf1, surf2, distance, show=True, color=(1, 0, 0, 1), offset=1, slab=None, smooth=False, optimize=True): plist = [] import Surface for spec in (surf1, surf2): s = parse_object_specifier(spec, 'surface') p = Surface.selected_surface_pieces(s, include_outline_boxes=False) if len(p) == 0: raise CommandError('%s has no surface pieces' % spec) elif len(p) > 1: raise CommandError('%s has %d surface pieces, require 1' % (spec, len(p))) plist.append(p[0]) p1, p2 = plist from Commands import parse_color color = parse_color(color) if not show: color = None from Commands import check_number check_number(offset, 'offset') if not slab is None: if isinstance(slab, (float, int)): slab = (-0.5 * slab, 0.5 * slab) else: from Commands import parse_floats slab = parse_floats(slab, 'slab', 2) offset = None import contactarea as c area = c.contact_area(p1, p2, distance, color, offset, slab, smooth, optimize) from chimera import replyobj replyobj.info('Contact area on %s within distance %.4g\nof %s = %.4g\n' % (p1.model.name, distance, p2.model.name, area)) replyobj.status('Contact area = %.4g' % area)
def field_lines(operation, volume, lines=1000, startAbove=None, step=0.5, color=(.7, .7, .7, 1), lineWidth=1, tubeRadius=None, circleSubdivisions=12, markers=False, modelId=None): from VolumeViewer import Volume vlist = [m for m in volume if isinstance(m, Volume)] if len(vlist) == 0: raise CommandError('No volume specified') import Commands as CD rgba = CD.parse_color(color) model_id = None if modelId is None else CD.parse_model_id(modelId) import fieldlines for v in vlist: fieldlines.show_field_lines(v, lines, startAbove, step, rgba, lineWidth, tubeRadius, circleSubdivisions, markers, model_id)
def parse_value_color(vc): err = 'Colormap entry must be value,color: got "%s"' % vc svc = vc.split(',',1) if len(svc) != 2: raise CommandError(err) try: v = float(svc[0]) except ValueError: raise CommandError(err) from Commands import convertColor try: c = convertColor(svc[1]).rgba() except: raise CommandError(err) return v, c
def symmetry(operation, volume, minimumCorrelation=0.99, nMax=8, helix=None, points=10000, set=True): from VolumeViewer import Volume vlist = [m for m in volume if isinstance(m, Volume)] if len(vlist) == 0: raise CommandError('No volume specified') if not helix is None: rise, angle, n, optimize = parse_helix_option(helix) import symmetry as S for v in vlist: if helix: syms, msg = S.find_helix_symmetry(v, rise, angle, n, optimize, nMax, minimumCorrelation, points) else: syms, msg = S.find_point_symmetry(v, nMax, minimumCorrelation, points) if set and syms: v.data.symmetries = syms from chimera.replyobj import info, status status(msg) info(msg + '\n')
def map_sum(operation, volume, aboveThreshold=None, step=1, subregion='all'): from VolumeViewer import Volume vlist = [m for m in volume if isinstance(m, Volume)] if len(vlist) == 0: raise CommandError('No volume specified') from Commands import parse_step, parse_subregion subregion_arg = subregion subregion = parse_subregion(subregion) step_arg = step step = parse_step(step) import numpy import VolumeStatistics as VS for v in vlist: m = v.matrix(step=step, subregion=subregion) if aboveThreshold is None: s = m.sum(dtype=numpy.float64) else: ma = (m >= aboveThreshold) na = ma.sum(dtype=numpy.int64) mt = ma.astype(m.dtype) mt *= m s = mt.sum(dtype=numpy.float64) descrip = v.name if step_arg != 1: descrip += ', step %s' % step_arg if subregion_arg != 'all': descrip += ', subregion %s' % subregion_arg if not aboveThreshold is None: descrip += ', level >= %.5g, npoints %d' % (aboveThreshold, na) msg = '%s: sum = %.5g' % (descrip, s) VS.message(msg, show_reply_log=True)
def filter_surfaces(surfaces): from ..molecule import Molecule surfs = set([s for s in surfaces if not isinstance(s, Molecule)]) if len(surfs) == 0: raise CommandError('No surfaces specified') return surfs
def enum_arg(s, session, values, multiple = False, abbrev = False): val = abbreviation_table(values) if abbrev else dict((v,v) for v in values) if multiple: vlist = s.split(',') for v in vlist: if not v in val: raise CommandError('Values must be in %s, got "%s"' % (', '.join(values), s)) e = [val[v] for v in vlist] elif s in val: e = val[s] else: raise CommandError('Value must be one of %s, got "%s"' % (', '.join(values), s)) return e
def volumes_arg(v, session): sel = parse_specifier(v, session) vlist = sel.maps() if len(vlist) == 0: raise CommandError('No volumes specified') return vlist
def map_values(operation, volume, atoms, name=None, report=10): from VolumeViewer import Volume vlist = [v for v in volume if isinstance(v, Volume)] if len(vlist) != 1: raise CommandError('No volume model specified') v = vlist[0] import AtomDensity if name is None: name = 'value_' + v.name name = AtomDensity.replace_special_characters(name, '_') AtomDensity.set_atom_volume_values(atoms, v, name) if report: arep = atoms[:report] if isinstance(report, int) else atoms values = '\n'.join( ['%s %.5g' % (a.oslIdent(), getattr(a, name)) for a in arep]) n = len(atoms) t = '%s map values at %d atom positions\n%s\n' % (v.name, n, values) if n > len(arep): t += '...\n' if n == 1: s = '%s map value at atom %s = %.5g' % ( v.name, atoms[0].oslIdent(), getattr(a, name)) else: maxa = 3 values = ', '.join( ['%.5g' % getattr(a, name) for a in atoms[:maxa]]) if n > maxa: values += ', ...' s = '%s map values at %d atoms: %s' % (v.name, n, values) from chimera import replyobj replyobj.info(t) replyobj.status(s)
def map_statistics(operation, volume, step=1, subregion='all'): from VolumeViewer import Volume vlist = [m for m in volume if isinstance(m, Volume)] if len(vlist) == 0: raise CommandError('No volume specified') from Commands import parse_step, parse_subregion subregion_arg = subregion subregion = parse_subregion(subregion) step_arg = step step = parse_step(step) import VolumeStatistics as VS for v in vlist: m = v.matrix(step=step, subregion=subregion) mean, sd, rms = VS.mean_sd_rms(m) descrip = v.name if step_arg != 1: descrip += ', step %s' % step_arg if subregion_arg != 'all': descrip += ', subregion %s' % subregion_arg msg = '%s: mean = %.5g, SD = %.5g, RMS = %.5g' % (descrip, mean, sd, rms) VS.message(msg, show_reply_log=True)
def single_volume(mlist): if mlist is None: return mlist vlist = filter_volumes(mlist) if len(vlist) != 1: raise CommandError('Must specify only one volume') return vlist[0]
def check_in_place(inPlace, volumes): if not inPlace: return nwv = [v for v in volumes if not v.data.writable] if nwv: names = ', '.join([v.name for v in nwv]) raise CommandError("Can't modify volume in place: %s" % names)
def parse_object_specifier(spec, name='object'): from chimera import specifier try: sel = specifier.evalSpec(spec) except: raise CommandError('Bad %s specifier "%s"' % (name, spec)) return sel
def float3_arg(s, session): fl = [float(x) for x in s.split(',')] if len(fl) != 3: raise CommandError('Require 3 comma-separated values, got %d' % len(fl)) from numpy import array, float32 a = array(fl, float32) return a
def ints_arg(s, session, allowed_counts = None): il = [int(x) for x in s.split(',')] if not allowed_counts is None and not len(il) in allowed_counts: allowed = ','.join('%d' % c for c in allowed_counts) raise CommandError('Wrong number of values, require %s, got %d' % (allowed, len(il))) return il
def filter_volumes(models, keyword = ''): if keyword: keyword += ' ' if isinstance(models, str): raise CommandError('No %svolumes specified by "%s"' % (keyword, models)) from ..map import Volume from _volume import Volume_Model vids = set([v.id for v in models if isinstance(v, (Volume, Volume_Model))]) for v in models: if not isinstance(v, (Volume, Volume_Model)) and not v.id in vids: raise CommandError('Model %s is not a volume' % v.name) volumes = [v for v in models if isinstance(v, Volume)] if len(volumes) == 0: raise CommandError('No %svolumes specified' % keyword) return volumes