def main(args): log = logging.getLogger('root') hdlr = logging.StreamHandler(sys.stdout) log.addHandler(hdlr) log.setLevel(logging.getLevelName(args.loglevel.upper())) if args.boxsize is None: log.error("Please specify box size") return 1 df = star.parse_star(args.input, keep_index=False) if args.cls is not None: df = star.select_classes(df, args.cls) if args.apix is None: args.apix = star.calculate_apix(df) if args.sym is not None: args.sym = util.relion_symmetry_group(args.sym) df[star.Relion.ANGLEPSI] = 0 rots = geom.e2r_vec(np.deg2rad(df[star.Relion.ANGLES].values)) dfs = [star.transform_star(df, op, rots=rots) for op in args.sym] dfi = pd.concat(dfs, axis=0, keys=[0, 1, 2, 3]) newrots = np.array([ geom.e2r_vec(np.deg2rad(x[star.Relion.ANGLES].values)) for x in dfs ]) mag = np.array([geom.phi5(r) for r in newrots.reshape(-1, 3, 3)]).reshape(4, -1) idx = np.argmin(mag, axis=0) midx = [(i, a) for a, i in enumerate(idx)] df = dfi.loc[midx] nside = 2**args.healpix_order angular_sampling = np.sqrt(3 / np.pi) * 60 / nside theta, phi = pix2ang(nside, np.arange(12 * nside**2)) phi = np.pi - phi hp = np.column_stack((np.sin(theta) * np.cos(phi), np.sin(theta) * np.sin(phi), np.cos(theta))) kdtree = cKDTree(hp) st = np.sin(np.deg2rad(df[star.Relion.ANGLETILT])) ct = np.cos(np.deg2rad(df[star.Relion.ANGLETILT])) sp = np.sin(np.deg2rad(df[star.Relion.ANGLEROT])) cp = np.cos(np.deg2rad(df[star.Relion.ANGLEROT])) ptcls = np.column_stack((st * cp, st * sp, ct)) _, idx = kdtree.query(ptcls) cnts = np.bincount(idx, minlength=theta.size) frac = cnts / np.max(cnts).astype(np.float64) mu = np.mean(frac) sigma = np.std(frac) color_scale = (frac - mu) / sigma color_scale[color_scale > 5] = 5 color_scale[color_scale < -1] = -1 color_scale /= 6 color_scale += 1 / 6. r = args.boxsize * args.apix / 2 rp = np.reshape(r + r * frac * args.height_scale, (-1, 1)) base1 = hp * r base2 = hp * rp base1 = base1[:, [0, 1, 2]] + np.array([r] * 3) base2 = base2[:, [0, 1, 2]] + np.array([r] * 3) height = np.squeeze(np.abs(rp - r)) idx = np.where(height >= 0.01)[0] width = args.width_scale * np.pi * r * angular_sampling / 360 bild = np.hstack((base1, base2, np.ones((base1.shape[0], 1)) * width)) fmt_color = ".color %f 0 %f\n" fmt_cyl = ".cylinder %f %f %f %f %f %f %f\n" with open(args.output, "w") as f: for i in idx: f.write(fmt_color % (color_scale[i], 1 - color_scale[i])) f.write(fmt_cyl % tuple(bild[i])) return 0
def main(args): log = logging.getLogger(__name__) log.setLevel(logging.INFO) hdlr = logging.StreamHandler(sys.stdout) if args.quiet: hdlr.setLevel(logging.WARNING) else: hdlr.setLevel(logging.INFO) log.addHandler(hdlr) if args.markers is None and args.target is None and args.sym is None: log.error( "A marker or symmetry group must be provided via --target, --markers, or --sym" ) return 1 elif args.sym is None and args.markers is None and args.boxsize is None and args.origin is None: log.error( "An origin must be provided via --boxsize, --origin, or --markers") return 1 elif args.sym is not None and args.markers is None and args.target is None and \ (args.boxsize is not None or args.origin is not None): log.warn("Symmetry expansion alone will ignore --target or --origin") if args.target is not None: try: args.target = np.array( [np.double(tok) for tok in args.target.split(",")]) except: log.error( "Target must be comma-separated list of x,y,z coordinates") return 1 if args.origin is not None: if args.boxsize is not None: logger.warn("--origin supersedes --boxsize") try: args.origin = np.array( [np.double(tok) for tok in args.origin.split(",")]) except: log.error( "Origin must be comma-separated list of x,y,z coordinates") return 1 if args.marker_sym is not None: args.marker_sym = relion_symmetry_group(args.marker_sym) star = parse_star(args.input, keep_index=False) if args.apix is None: args.apix = calculate_apix(star) if args.apix is None: logger.warn( "Could not compute pixel size, default is 1.0 Angstroms per pixel" ) args.apix = 1.0 if args.cls is not None: star = select_classes(star, args.cls) cmms = [] if args.markers is not None: cmmfiles = glob.glob(args.markers) for cmmfile in cmmfiles: for cmm in parse_cmm(cmmfile): cmms.append(cmm / args.apix) if args.target is not None: cmms.append(args.target / args.apix) stars = [] if len(cmms) > 0: if args.origin is not None: args.origin /= args.apix elif args.boxsize is not None: args.origin = np.ones(3) * args.boxsize / 2 else: log.warn("Using first marker as origin") if len(cmms) == 1: log.error( "Using first marker as origin, expected at least two markers" ) return 1 args.origin = cmms[0] cmms = cmms[1:] markers = [cmm - args.origin for cmm in cmms] if args.marker_sym is not None and len(markers) == 1: markers = [op.dot(markers[0]) for op in args.marker_sym] elif args.marker_sym is not None: log.error( "Exactly one marker is required for symmetry-derived subparticles" ) return 1 rots = [euler2rot(*np.deg2rad(r[1])) for r in star[ANGLES].iterrows()] #origins = star[ORIGINS].copy() for m in markers: d = np.linalg.norm(m) ax = m / d op = euler2rot( *np.array([np.arctan2(ax[1], ax[0]), np.arccos(ax[2]), 0.])) stars.append(transform_star(star, op.T, -d, rots=rots)) if args.sym is not None: args.sym = relion_symmetry_group(args.sym) if len(stars) > 0: stars = [ se for se in subparticle_expansion( s, args.sym, -args.displacement / args.apix) for s in stars ] else: stars = list( subparticle_expansion(star, args.sym, -args.displacement / args.apix)) if args.recenter: for s in stars: recenter(s, inplace=True) if args.suffix is None and not args.skip_join: if len(stars) > 1: star = interleave(stars) else: star = stars[0] write_star(args.output, star) else: for i, star in enumerate(stars): write_star(os.path.join(args.output, args.suffix + "_%d" % i), star) return 0
def main(args): log = logging.getLogger(__name__) log.setLevel(logging.INFO) hdlr = logging.StreamHandler(sys.stdout) if args.quiet: hdlr.setLevel(logging.WARNING) else: hdlr.setLevel(logging.INFO) log.addHandler(hdlr) if args.target is None and args.sym is None: log.error( "At least a target or symmetry group must be provided via --target or --sym" ) return 1 elif args.target is not None and args.boxsize is None and args.origin is None: log.error("An origin must be provided via --boxsize or --origin") return 1 if args.target is not None: try: args.target = np.array( [np.double(tok) for tok in args.target.split(",")]) except: log.error( "Target must be comma-separated list of x,y,z coordinates") return 1 if args.origin is not None: if args.boxsize is not None: log.warn("--origin supersedes --boxsize") try: args.origin = np.array( [np.double(tok) for tok in args.origin.split(",")]) except: log.error( "Origin must be comma-separated list of x,y,z coordinates") return 1 if args.sym is not None: args.sym = util.relion_symmetry_group(args.sym) df = star.parse_star(args.input) if args.apix is None: args.apix = star.calculate_apix(df) if args.apix is None: log.warn( "Could not compute pixel size, default is 1.0 Angstroms per pixel" ) args.apix = 1.0 df[star.Relion.MAGNIFICATION] = 10000 df[star.DETECTORPIXELSIZE] = 1.0 if args.cls is not None: df = star.select_classes(df, args.cls) if args.target is not None: if args.origin is not None: args.origin /= args.apix elif args.boxsize is not None: args.origin = np.ones(3) * args.boxsize / 2 args.target /= args.apix c = args.target - args.origin c = np.where(np.abs(c) < 1, 0, c) # Ignore very small coordinates. d = np.linalg.norm(c) ax = c / d cm = util.euler2rot(*np.array( [np.arctan2(ax[1], ax[0]), np.arccos(ax[2]), np.deg2rad(args.psi)])) ops = [op.dot(cm) for op in args.sym] if args.sym is not None else [cm] dfs = [ star.transform_star(df, op.T, -d, rotate=args.shift_only, invert=args.target_invert, adjust_defocus=args.adjust_defocus) for op in ops ] elif args.sym is not None: dfs = list( subparticle_expansion(df, args.sym, -args.displacement / args.apix)) else: log.error( "At least a target or symmetry group must be provided via --target or --sym" ) return 1 if args.recenter: for s in dfs: star.recenter(s, inplace=True) if args.suffix is None and not args.skip_join: if len(dfs) > 1: df = util.interleave(dfs) else: df = dfs[0] star.write_star(args.output, df) else: for i, s in enumerate(dfs): star.write_star(os.path.join(args.output, args.suffix + "_%d" % i), s) return 0
def main(args): log = logging.getLogger(__name__) hdlr = logging.StreamHandler(sys.stdout) log.addHandler(hdlr) log.setLevel(logging.getLevelName(args.loglevel.upper())) if args.target is None and args.sym is None and args.transform is None and args.euler is None: log.error("At least a target, transformation matrix, Euler angles, or a symmetry group must be provided") return 1 elif (args.target is not None or args.transform is not None) and args.boxsize is None and args.origin is None: log.error("An origin must be provided via --boxsize or --origin") return 1 if args.apix is None: df = star.parse_star(args.input, nrows=1) args.apix = star.calculate_apix(df) if args.apix is None: log.warn("Could not compute pixel size, default is 1.0 Angstroms per pixel") args.apix = 1.0 df[star.Relion.MAGNIFICATION] = 10000 df[star.Relion.DETECTORPIXELSIZE] = 1.0 if args.target is not None: try: args.target = np.array([np.double(tok) for tok in args.target.split(",")]) except: log.error("Target must be comma-separated list of x,y,z coordinates") return 1 if args.euler is not None: try: args.euler = np.deg2rad(np.array([np.double(tok) for tok in args.euler.split(",")])) args.transform = np.zeros((3, 4)) args.transform[:, :3] = geom.euler2rot(*args.euler) if args.target is not None: args.transform[:, -1] = args.target except: log.error("Euler angles must be comma-separated list of rotation, tilt, skew in degrees") return 1 if args.transform is not None and not hasattr(args.transform, "dtype"): if args.target is not None: log.warn("--target supersedes --transform") try: args.transform = np.array(json.loads(args.transform)) except: log.error("Transformation matrix must be in JSON/Numpy format") return 1 if args.origin is not None: if args.boxsize is not None: log.warn("--origin supersedes --boxsize") try: args.origin = np.array([np.double(tok) for tok in args.origin.split(",")]) args.origin /= args.apix except: log.error("Origin must be comma-separated list of x,y,z coordinates") return 1 elif args.boxsize is not None: args.origin = np.ones(3) * args.boxsize / 2 if args.sym is not None: args.sym = util.relion_symmetry_group(args.sym) df = star.parse_star(args.input) if star.calculate_apix(df) != args.apix: log.warn("Using specified pixel size of %f instead of calculated size %f" % (args.apix, star.calculate_apix(df))) if args.cls is not None: df = star.select_classes(df, args.cls) if args.target is not None: args.target /= args.apix c = args.target - args.origin c = np.where(np.abs(c) < 1, 0, c) # Ignore very small coordinates. d = np.linalg.norm(c) ax = c / d r = geom.euler2rot(*np.array([np.arctan2(ax[1], ax[0]), np.arccos(ax[2]), np.deg2rad(args.psi)])) d = -d elif args.transform is not None: r = args.transform[:, :3] if args.transform.shape[1] == 4: d = args.transform[:, -1] / args.apix d = r.dot(args.origin) + d - args.origin else: d = 0 elif args.sym is not None: r = np.identity(3) d = -args.displacement / args.apix else: log.error("At least a target or symmetry group must be provided via --target or --sym") return 1 log.debug("Final rotation: %s" % str(r).replace("\n", "\n" + " " * 16)) ops = [op.dot(r.T) for op in args.sym] if args.sym is not None else [r.T] log.debug("Final translation: %s (%f px)" % (str(d), np.linalg.norm(d))) dfs = list(subparticle_expansion(df, ops, d, rotate=args.shift_only, invert=args.invert, adjust_defocus=args.adjust_defocus)) if args.recenter: for s in dfs: star.recenter(s, inplace=True) if args.suffix is None and not args.skip_join: if len(dfs) > 1: df = util.interleave(dfs) else: df = dfs[0] df = star.compatible(df, relion2=args.relion2, inplace=True) star.write_star(args.output, df, optics=(not args.relion2)) else: for i, s in enumerate(dfs): s = star.compatible(s, relion2=args.relion2, inplace=True) star.write_star(os.path.join(args.output, args.suffix + "_%d" % i), s, optics=(not args.relion2)) return 0