def randomize_parameters(self, ptp=<NUM_LIT>, fourier=False, vmin=None, vmax=None):

if vmin is not None and vmax is not None:<EOL><INDENT>ptp = vmax - vmin<EOL><DEDENT>elif vmax is not None and vmin is None:<EOL><INDENT>vmin = vmax - ptp<EOL><DEDENT>elif vmin is not None and vmax is None:<EOL><INDENT>vmax = vmin + ptp<EOL><DEDENT>else:<EOL><INDENT>vmax = <NUM_LIT:1.0><EOL>vmin = vmax - ptp<EOL><DEDENT>self.set_values(self.category+'<STR_LIT>', <NUM_LIT:1.0>)<EOL>self.set_values(self.category+'<STR_LIT>', <NUM_LIT:0.0>)<EOL>for k, v in iteritems(self.poly_params):<EOL><INDENT>norm = (self.zorder + <NUM_LIT:1.0>)*<NUM_LIT:2><EOL>self.set_values(k, ptp*(np.random.rand() - <NUM_LIT:0.5>) / norm)<EOL><DEDENT>for i, p in enumerate(self.barnes_params):<EOL><INDENT>N = len(p)<EOL>if fourier:<EOL><INDENT>t = ((np.random.rand(N)-<NUM_LIT:0.5>) + <NUM_LIT>*(np.random.rand(N)-<NUM_LIT:0.5>))/(np.arange(N)+<NUM_LIT:1>)<EOL>q = np.real(np.fft.ifftn(t)) / (i+<NUM_LIT:1>)<EOL><DEDENT>else:<EOL><INDENT>t = ptp*np.sqrt(N)*(np.random.rand(N)-<NUM_LIT:0.5>)<EOL>q = np.cumsum(t) / (i+<NUM_LIT:1>)<EOL><DEDENT>q = ptp * q / q.ptp() / len(self.barnes_params)<EOL>q -= q.mean()<EOL>self.set_values(p, q)<EOL><DEDENT>self._norm_stat = [ptp, vmin]<EOL>if self.shape:<EOL><INDENT>self.initialize()<EOL><DEDENT>if self._parent:<EOL><INDENT>param = self.category+'<STR_LIT>'<EOL>self.trigger_update(param, self.get_values(param))<EOL><DEDENT>

Create random parameters for this ILM that mimic experiments
as closely as possible without real assumptions.


def __init__(self, npts=(<NUM_LIT>,<NUM_LIT:20>), zorder=<NUM_LIT:7>, op='<STR_LIT:*>', barnes_dist=<NUM_LIT>,<EOL>barnes_clip_size=<NUM_LIT:3>, category='<STR_LIT>', shape=None,<EOL>float_precision=np.float64):

super(BarnesXYLegPolyZ, self).__init__(npts=npts, zorder=zorder,<EOL>op=op, barnes_dist=barnes_dist, barnes_clip_size=barnes_clip_size,<EOL>local_updates=True, category=category, shape=shape,<EOL>float_precision=float_precision)<EOL>

A Barnes interpolant. This one is of the form

.. math::
    I = \\left[1 + B(x,y) \\right]  (1 + z q(z)) + c

where B is a Barnes interpolants and q is a polynomial strictly in z.
Additionally.... operation multiply or x for poly?.
Always uses local updates, since evaluating a 2D barnes for an entire
image can be slow.

Parameters
----------
shape : iterable
    size of the field in pixels, needs to be padded shape

npts : 2-element tuple of ints, optional
    Number of control points used for the Barnes interpolant
    in x & y. Default is (40,20)

zorder : integer
    Number of orders for the z-polynomial.

op : string
    The operation to perform between Barnes and LegPoly, '*' or '+'.

barnes_dist : float
    Fractional distance to use for the barnes interpolator

local_updates : boolean
    Whether to perform local updates on the ILM

float_precision : numpy float datatype
    One of numpy.float16, numpy.float32, numpy.float64; precision
    for precomputed arrays. Default is np.float64; make it 16 or 32
    to save memory.


b_in = self.b_in<EOL>dist = lambda x: np.sqrt(np.dot(x,x))<EOL>sz = self.npts[<NUM_LIT:1>]<EOL>coeffs = self.get_values(self.barnes_params)<EOL>b = BarnesInterpolationND(<EOL>b_in, coeffs, filter_size=self.filtsize, damp=<NUM_LIT>, iterations=<NUM_LIT:3>,<EOL>clip=self.local_updates, clipsize=self.barnes_clip_size,<EOL>blocksize=<NUM_LIT:100>  <EOL>)<EOL>return b(pos)<EOL>

Creates a barnes interpolant & calculates its values


return self._barnes(self.b_out)<EOL>

Returns the raveled values of the barnes on the field


return np.reshape(self._barnes_val(), self.shape.shape[<NUM_LIT:1>:])[None, :, :]<EOL>

Returns the shaped values of the barnes on the (x,y)


if order == <NUM_LIT:1>:<EOL><INDENT>return (v*p).sum()<EOL><DEDENT>elif order == <NUM_LIT:2>:<EOL><INDENT>return np.sqrt( ((v**<NUM_LIT:2>)*p).sum() - (v*p).sum()**<NUM_LIT:2> )<EOL><DEDENT>

Calculates the moments of the probability distribution p with vector v


def __init__(self, shape=None, zrange=None, laser_wavelength=<NUM_LIT>,<EOL>zslab=<NUM_LIT:0.>, zscale=<NUM_LIT:1.0>, kfki=<NUM_LIT>, n2n1=<NUM_LIT>/<NUM_LIT>, alpha=<NUM_LIT>,<EOL>polar_angle=<NUM_LIT:0.>, pxsize=<NUM_LIT>, support_factor=<NUM_LIT:2>, normalize=False,<EOL>sigkf=<NUM_LIT:0.0>, nkpts=None, cutoffval=None, measurement_iterations=None,<EOL>k_dist='<STR_LIT>', use_J1=True, sph6_ab=None, global_zscale=False,<EOL>cutbyval=False, cutfallrate=<NUM_LIT>, cutedgeval=<NUM_LIT>,<EOL>pinhole_width=None, do_pinhole=False, *args, **kwargs):

self.pxsize = pxsize<EOL>self.polar_angle = polar_angle<EOL>self.support_factor = support_factor<EOL>self.normalize = normalize<EOL>self.measurement_iterations = measurement_iterations or <NUM_LIT:11><EOL>self.global_zscale = global_zscale<EOL>self.polychromatic = False<EOL>self.sigkf = sigkf<EOL>self.nkpts = nkpts<EOL>self.cutoffval = cutoffval<EOL>self.cutbyval = cutbyval<EOL>self.cutfallrate = cutfallrate<EOL>self.cutedgeval = cutedgeval<EOL>self.k_dist = k_dist<EOL>self.use_J1 = use_J1<EOL>self.do_pinhole = do_pinhole<EOL>if self.sigkf is not None:<EOL><INDENT>self.nkpts = self.nkpts or <NUM_LIT:3><EOL>self.polychromatic = True<EOL><DEDENT>elif self.nkpts is not None:<EOL><INDENT>self.sigkf = <NUM_LIT:0.0><EOL>self.polychromatic = True<EOL><DEDENT>else:<EOL><INDENT>self.sigkf = sigkf = <NUM_LIT:0.0><EOL>self.polychromatic = False<EOL><DEDENT>if (sph6_ab is not None) and (not np.isnan(sph6_ab)):<EOL><INDENT>self.use_sph6_ab = True <EOL><DEDENT>else:<EOL><INDENT>self.use_sph6_ab = False<EOL><DEDENT>if shape and zrange is None:<EOL><INDENT>zrange = (<NUM_LIT:0>, shape.shape[<NUM_LIT:0>])<EOL><DEDENT>self.zrange = zrange<EOL>params = [<EOL>'<STR_LIT>', '<STR_LIT>', '<STR_LIT>', '<STR_LIT>', '<STR_LIT>', '<STR_LIT>',<EOL>'<STR_LIT>', '<STR_LIT>'<EOL>]<EOL>values = np.array([<EOL>kfki,   zslab,   zscale,   alpha,   n2n1,   laser_wavelength,<EOL>sigkf,    sph6_ab<EOL>])<EOL>if not self.polychromatic:<EOL><INDENT>ind = params.index('<STR_LIT>')<EOL>params.pop(ind)<EOL>values = np.delete(values, ind)<EOL><DEDENT>if not self.use_sph6_ab:<EOL><INDENT>ind = params.index('<STR_LIT>')<EOL>params.pop(ind)<EOL>values = np.delete(values, ind)<EOL><DEDENT>for i in range(len(params)):<EOL><INDENT>if params[i] is '<STR_LIT>' and self.global_zscale:<EOL><INDENT>continue<EOL><DEDENT>params[i] = '<STR_LIT>' + params[i]<EOL><DEDENT>super(ExactPSF, self).__init__(<EOL>*args, params=params, values=values, shape=shape, **kwargs<EOL>)<EOL>

Superclass for all the exact PSFs, i.e. any PSF that is based on
physical properties of the imaging system such as the laser
wavelength.

This PSF functions by calculating the local PSF for every z layer
in the image, and convolving each layer independently (numerically
the exact model of image formation).

Parameters
----------
shape : tuple
    Shape of the image in (z,y,x) pixel numbers (to be deprecated)

zrange : tuple
    range of z pixels over which we should calculate the psf, good pixels
    being zrange[0] <= z <= zrange[1]. currently must be set to the interior
    of the image, so [state.pad, state.image.shape[0] - state.pad]

laser_wavelength : float
    wavelength of light in um of the incoming laser light

zslab : float
    Pixel position of the optical interface where 0 is the edge of the
    image in the z direction

zscale : float
    Scaling of the z pixel size, so that each pixel is located at
    zscale*(z - zint), such that the interface does not move with zscale

kfki : float
    Ratio of outgoing to incoming light wavevectors, 2\pi/\lambda

n2n1 : float
    Ratio of the index mismatch across the optical interface. For typical
    glass with glycerol/water 80/20 mixture this is 1.4/1.518

alpha : float
    Aperture of the lens in radians, set by arcsin(n2n1)?

polar_angle : float
    the angle of the light polarization with respect to the scanning axis

pxsize : float
    the size of a xy pixel in um, defaults to cohen group size 0.125 um

support_factor : integer
    size of the support

normalize : boolean
    if True, use the normalization as calculated by the PSF instead of
    unit normalization

sigkf : float
    Width of wavelengths to use in the polychromatic psf, None is
    monochromatic. Values for kfki = kfki +- sigkf, unitless

nkpts : integer
    number of integration points to use for the polychromatic psf

cutoffval : float
    Percentage of peak psf value to cutoff using a 3-axis
    exp(-(r-r0)**4) function where r0 is determined by cutoffval. A
    good value for this is the bit depth of the camera, or possibly the
    SNR, so 1/2**8 or 1/50.

measurement_iterations : int
    number of interations used when trying to find the center of mass
    of the psf in a certain slice

k_dist : str
    Eithe ['gaussian', 'gamma'] which control the wavevector
    distribution for the polychromatic detection psf. Default
    is Gaussian.

use_J1 : boolean
    Which hdet confocal model to use. Set to True to include the
    J1 term corresponding to a large-NA focusing lens, False to
    exclude it. Default is True

cutbyval : boolean
    If True, cuts the PSF based on the actual value instead of the
    position associated with the nearest value.

cutfallrate : float
    The relative value of the cutoffval over which to damp the
    remaining values of the psf. 0.3 seems a good fit now.

cutedgeval : float
    The value with which to determine the edge of the psf, typically
    taken around floating point, 1e-12

pinhole_width : Float
    The width of the line illumination, in 1/k units. Default is 1.0.

do_pinhole : Bool
    Whether or not to include pinhole line width in the sampling.
    Default is False.


raise NotImplementedError('<STR_LIT>')<EOL>

The function to evaluate the exact psf. Syntax must be:

    psf = psffunc(x, y, z, \**kwargs)

and return a ``[x.size, y.size, z.size]`` numpy.ndarray, where x,y,z
are 1D arrays.
Implement in subclass.


def psf_slice(self, zint, size=<NUM_LIT:11>, zoffset=<NUM_LIT:0.>, getextent=False):

<EOL>zint = max(self._p2k(self._tz(zint)), <NUM_LIT:0>)<EOL>offset = np.array([zoffset*(zint><NUM_LIT:0>), <NUM_LIT:0>, <NUM_LIT:0>])<EOL>scale = [self.param_dict[self.zscale], <NUM_LIT:1.0>, <NUM_LIT:1.0>]<EOL>tile = util.Tile(left=<NUM_LIT:0>, size=size, centered=True)<EOL>vecs = tile.coords(form='<STR_LIT>')<EOL>vecs = [self._p2k(s*i+o) for i,s,o in zip(vecs, scale, offset)]<EOL>psf = self.psffunc(*vecs[::-<NUM_LIT:1>], zint=zint, **self.pack_args()).T<EOL>vec = tile.coords(form='<STR_LIT>')<EOL>if self.cutoffval is not None and not self.cutbyval:<EOL><INDENT>edge = psf > psf.max() * self.cutoffval<EOL>dd = nd.morphology.distance_transform_edt(~edge)<EOL>psf = psf * np.exp(-dd**<NUM_LIT:4>)<EOL>psf /= psf.sum()<EOL>if getextent:<EOL><INDENT>size = np.array([<EOL>(vec*edge).min(axis=(<NUM_LIT:1>,<NUM_LIT:2>,<NUM_LIT:3>))-<NUM_LIT:2>,<EOL>(vec*edge).max(axis=(<NUM_LIT:1>,<NUM_LIT:2>,<NUM_LIT:3>))+<NUM_LIT:2>,<EOL>]).T<EOL>return psf, vec, size<EOL><DEDENT>return psf, vec<EOL><DEDENT>if self.cutoffval is not None and self.cutbyval:<EOL><INDENT>cutval = self.cutoffval * psf.max()<EOL>dd = (psf - cutval) / cutval<EOL>dd[dd > <NUM_LIT:0>] = <NUM_LIT:0.><EOL>psf = psf * np.exp(-(dd / self.cutfallrate)**<NUM_LIT:4>)<EOL>psf /= psf.sum()<EOL>edge = psf > cutval * self.cutedgeval<EOL>if getextent:<EOL><INDENT>size = np.array([<EOL>(vec*edge).min(axis=(<NUM_LIT:1>,<NUM_LIT:2>,<NUM_LIT:3>))-<NUM_LIT:2>,<EOL>(vec*edge).max(axis=(<NUM_LIT:1>,<NUM_LIT:2>,<NUM_LIT:3>))+<NUM_LIT:2>,<EOL>]).T<EOL>return psf, vec, size<EOL><DEDENT>return psf, vec<EOL><DEDENT>return psf, vec<EOL>

Calculates the 3D psf at a particular z pixel height

Parameters
----------
zint : float
    z pixel height in image coordinates , converted to 1/k by the
    function using the slab position as well

size : int, list, tuple
    The size over which to calculate the psf, can be 1 or 3 elements
    for the different axes in image pixel coordinates

zoffset : float
    Offset in pixel units to use in the calculation of the psf

cutval : float
    If not None, the psf will be cut along a curve corresponding to
    p(r) == 0 with exponential damping exp(-d^4)

getextent : boolean
    If True, also return the extent of the psf in pixels for example
    to get the support size. Can only be used with cutval.


Pack the parameters into the form necessary for the integration
routines above in psfcalc.


return <NUM_LIT:2>*np.pi*self.pxsize*v/self.param_dict['<STR_LIT>']<EOL>

Convert from pixel to 1/k_incoming (laser_wavelength/(2\pi)) units


return (z-self.param_dict['<STR_LIT>'])*self.param_dict[self.zscale]<EOL>

Transform z to real-space coordinates from tile coordinates


return np.polyval(self.drift_poly, z)<EOL>

Give the pixel offset at a given z value for the current parameters


def measure_size_drift(self, z, size=<NUM_LIT>, zoffset=<NUM_LIT:0.>):

drift = <NUM_LIT:0.0><EOL>for i in range(self.measurement_iterations):<EOL><INDENT>psf, vec = self.psf_slice(z, size=size, zoffset=zoffset+drift)<EOL>psf = psf / psf.sum()<EOL>drift += moment(psf, vec[<NUM_LIT:0>], order=<NUM_LIT:1>)<EOL>psize = [moment(psf, j, order=<NUM_LIT:2>) for j in vec]<EOL><DEDENT>return np.array(psize), drift<EOL>

Returns the 'size' of the psf in each direction a particular z (px)


<EOL>l,u = max(self.zrange[<NUM_LIT:0>], self.param_dict['<STR_LIT>']), self.zrange[<NUM_LIT:1>]<EOL>size_l, drift_l = self.measure_size_drift(l)<EOL>size_u, drift_u = self.measure_size_drift(u)<EOL>self.support = util.oddify(<NUM_LIT:2>*self.support_factor*size_u.astype('<STR_LIT:int>'))<EOL>self.drift_poly = np.polyfit([l, u], [drift_l, drift_u], <NUM_LIT:1>)<EOL>if self.cutoffval is not None:<EOL><INDENT>psf, vec, size_l = self.psf_slice(l, size=<NUM_LIT>, zoffset=drift_l, getextent=True)<EOL>psf, vec, size_u = self.psf_slice(u, size=<NUM_LIT>, zoffset=drift_u, getextent=True)<EOL>ss = [np.abs(i).sum(axis=-<NUM_LIT:1>) for i in [size_l, size_u]]<EOL>self.support = util.oddify(util.amax(*ss))<EOL><DEDENT>

Get support size and drift polynomial for current set of params


def _kpad(self, field, finalshape, zpad=False, norm=True):

currshape = np.array(field.shape)<EOL>if any(finalshape < currshape):<EOL><INDENT>raise IndexError("<STR_LIT>")<EOL><DEDENT>d = finalshape - currshape<EOL>o = d % <NUM_LIT:2><EOL>d = np.floor_divide(d, <NUM_LIT:2>)<EOL>if not zpad:<EOL><INDENT>o[<NUM_LIT:0>] = <NUM_LIT:0><EOL><DEDENT>axes = None<EOL>pad = tuple((d[i]+o[i],d[i]) for i in [<NUM_LIT:0>,<NUM_LIT:1>,<NUM_LIT:2>])<EOL>rpsf = np.pad(field, pad, mode='<STR_LIT>', constant_values=<NUM_LIT:0>)<EOL>rpsf = np.fft.ifftshift(rpsf, axes=axes)<EOL>kpsf = fft.rfftn(rpsf, **fftkwargs)<EOL>if norm:<EOL><INDENT>kpsf /= kpsf[<NUM_LIT:0>,<NUM_LIT:0>,<NUM_LIT:0>]<EOL><DEDENT>return kpsf<EOL>

fftshift and pad the field with zeros until it has size finalshape.
if zpad is off, then no padding is put on the z direction. returns
the fourier transform of the field


def __init__(self, shape=None, zrange=None, laser_wavelength=<NUM_LIT>, zslab=<NUM_LIT:0.>,<EOL>zscale=<NUM_LIT:1.0>, kfki=<NUM_LIT>, n2n1=<NUM_LIT>/<NUM_LIT>, alpha=<NUM_LIT>, polar_angle=<NUM_LIT:0.>,<EOL>pxsize=<NUM_LIT>, support_factor=<NUM_LIT:2>, normalize=False, sigkf=<NUM_LIT:0.0>,<EOL>nkpts=None, cutoffval=None, measurement_iterations=None,<EOL>k_dist='<STR_LIT>', use_J1=True, sph6_ab=None, global_zscale=False,<EOL>cutbyval=False, cutfallrate=<NUM_LIT>, cutedgeval=<NUM_LIT>,<EOL>pinhole_width=None, do_pinhole=False, *args, **kwargs):

super(ExactLineScanConfocalPSF, self).__init__(*args, shape=shape,<EOL>zrange=zrange, laser_wavelength=laser_wavelength, zslab=zslab,<EOL>zscale=zscale, kfki=kfki, n2n1=n2n1, alpha=alpha, pxsize=<EOL>pxsize, polar_angle=polar_angle, support_factor=support_factor,<EOL>normalize=normalize, sigkf=sigkf, nkpts=nkpts, cutoffval=<EOL>cutoffval, measurement_iterations= measurement_iterations,<EOL>k_dist=k_dist, use_J1=use_J1, sph6_ab=sph6_ab, global_zscale=<EOL>global_zscale, cutbyval=cutbyval, cutfallrate=cutfallrate,<EOL>cutedgeval=cutedgeval, **kwargs)<EOL>self.do_pinhole = do_pinhole<EOL>if (pinhole_width is not None) or do_pinhole:<EOL><INDENT>self.num_line_pts = <NUM_LIT:3><EOL><DEDENT>else:<EOL><INDENT>self.num_line_pts = <NUM_LIT:1><EOL><DEDENT>pinhole_width = pinhole_width if (pinhole_width is not None) else <NUM_LIT:1.0><EOL>if self.do_pinhole:<EOL><INDENT>k = '<STR_LIT>'<EOL>params.append(k)<EOL>self.param_dict.update({k:pinhole_width})<EOL><DEDENT>

PSF for line-scanning confocal microscopes that can be used with the
peri framework.  Calculates the spatially varying point spread
function for confocal microscopes and allows them to be applied to
images as a convolution.

This PSF assumes that the z extent is large compared to the image size
and so calculates the local PSF for every z layer in the image.

Parameters
----------
shape : tuple
    Shape of the image in (z,y,x) pixel numbers (to be deprecated)

zrange : tuple
    range of z pixels over which we should calculate the psf, good pixels
    being zrange[0] <= z <= zrange[1]. currently must be set to the interior
    of the image, so [state.pad, state.image.shape[0] - state.pad]

laser_wavelength : float
    wavelength of light in um of the incoming laser light

zslab : float
    Pixel position of the optical interface where 0 is the edge of the
    image in the z direction

zscale : float
    Scaling of the z pixel size, so that each pixel is located at
    zscale*(z - zint), such that the interface does not move with zscale

kfki : float
    Ratio of outgoing to incoming light wavevectors, 2\pi/\lambda

n2n1 : float
    Ratio of the index mismatch across the optical interface. For typical
    glass with glycerol/water 80/20 mixture this is 1.4/1.518

alpha : float
    Aperture of the lens in radians, set by arcsin(n2n1)?

polar_angle : float
    the angle of the light polarization with respect to the scanning axis

pxsize : float
    the size of a xy pixel in um, defaults to cohen group size 0.125 um

support_factor : integer
    size of the support

normalize : boolean
    if True, use the normalization as calculated by the PSF instead of
    unit normalization

sigkf : float
    Width of wavelengths to use in the polychromatic psf, None is
    monochromatic. Values for kfki = kfki +- sigkf, unitless

nkpts : integer
    number of integration points to use for the polychromatic psf

cutoffval : float
    Percentage of peak psf value to cutoff using a 3-axis
    exp(-(r-r0)**4) function where r0 is determined by cutoffval. A
    good value for this is the bit depth of the camera, or possibly the
    SNR, so 1/2**8 or 1/50.

measurement_iterations : int
    number of interations used when trying to find the center of mass
    of the psf in a certain slice

k_dist : str
    Eithe ['gaussian', 'gamma'] which control the wavevector
    distribution for the polychromatic detection psf. Default
    is Gaussian.

use_J1 : boolean
    Which hdet confocal model to use. Set to True to include the
    J1 term corresponding to a large-NA focusing lens, False to
    exclude it. Default is True

cutbyval : boolean
    If True, cuts the PSF based on the actual value instead of the
    position associated with the nearest value.

cutfallrate : float
    The relative value of the cutoffval over which to damp the
    remaining values of the psf. 0.3 seems a good fit now.

cutedgeval : float
    The value with which to determine the edge of the psf, typically
    taken around floating point, 1e-12

pinhole_width : Float
    The width of the line illumination, in 1/k units. Default is 1.0.

do_pinhole : Bool
    Whether or not to include pinhole line width in the sampling.
    Default is False.

Notes:
    a = ExactLineScanConfocalPSF((64,)*3)
    psf, (z,y,x) = a.psf_slice(1., size=51)
    imshow((psf*r**4)[:,:,25], cmap='bone')


mapper = {<EOL>'<STR_LIT>': '<STR_LIT>',<EOL>'<STR_LIT>': '<STR_LIT>',<EOL>'<STR_LIT>': '<STR_LIT>',<EOL>'<STR_LIT>': '<STR_LIT>',<EOL>'<STR_LIT>': '<STR_LIT>',<EOL>'<STR_LIT>': '<STR_LIT>',<EOL>'<STR_LIT>': '<STR_LIT>'<EOL>}<EOL>bads = [self.zscale, '<STR_LIT>']<EOL>d = {}<EOL>for k,v in iteritems(mapper):<EOL><INDENT>if k in self.param_dict:<EOL><INDENT>d[v] = self.param_dict[k]<EOL><DEDENT><DEDENT>d.update({<EOL>'<STR_LIT>': self.polar_angle,<EOL>'<STR_LIT>': self.normalize,<EOL>'<STR_LIT>':self.use_J1<EOL>})<EOL>if self.polychromatic:<EOL><INDENT>d.update({'<STR_LIT>': self.nkpts})<EOL>d.update({'<STR_LIT>': self.k_dist})<EOL><DEDENT>if self.do_pinhole:<EOL><INDENT>d.update({'<STR_LIT>': self.num_line_pts})<EOL><DEDENT>d.update({'<STR_LIT>': True})<EOL>return d<EOL>

Pack the parameters into the form necessary for the integration
routines above.  For example, packs for calculate_linescan_psf


if self.polychromatic:<EOL><INDENT>func = psfcalc.calculate_polychrome_linescan_psf<EOL><DEDENT>else:<EOL><INDENT>func = psfcalc.calculate_linescan_psf<EOL><DEDENT>return func(*args, **kwargs)<EOL>


def __init__(self, shape=None, zrange=None, laser_wavelength=<NUM_LIT>, zslab=<NUM_LIT:0.>,<EOL>zscale=<NUM_LIT:1.0>, kfki=<NUM_LIT>, n2n1=<NUM_LIT>/<NUM_LIT>, alpha=<NUM_LIT>, polar_angle=<NUM_LIT:0.>,<EOL>pxsize=<NUM_LIT>, support_factor=<NUM_LIT:2>, normalize=False, sigkf=<NUM_LIT:0.0>,<EOL>nkpts=None, cutoffval=None, measurement_iterations=None,<EOL>k_dist='<STR_LIT>', use_J1=True, sph6_ab=None, global_zscale=False,<EOL>cutbyval=False, cutfallrate=<NUM_LIT>, cutedgeval=<NUM_LIT>,<EOL>*args, **kwargs):

super(ExactPinholeConfocalPSF, self).__init__(shape=shape,<EOL>zrange=zrange, laser_wavelength=laser_wavelength, zslab=zslab,<EOL>zscale=zscale, kfki=kfki, n2n1=n2n1, alpha=alpha, pxsize=<EOL>pxsize, polar_angle=polar_angle, support_factor=support_factor,<EOL>normalize=normalize, sigkf=sigkf, nkpts=nkpts, cutoffval=<EOL>cutoffval, measurement_iterations= measurement_iterations,<EOL>k_dist=k_dist, use_J1=use_J1, sph6_ab=sph6_ab, global_zscale=<EOL>global_zscale, cutbyval=cutbyval, cutfallrate=cutfallrate,<EOL>cutedgeval=cutedgeval, *args, **kwargs)<EOL>

PSF for a pinhole confocal microscopes that can be used with the
peri framework.  Calculates the spatially varying point spread
function for confocal microscopes and allows them to be applied to
images as a convolution.

This PSF assumes that the z extent is large compared to the image size
and so calculates the local PSF for every z layer in the image.

Parameters
----------
shape : tuple
    Shape of the image in (z,y,x) pixel numbers (to be deprecated)

zrange : tuple
    range of z pixels over which we should calculate the psf, good pixels
    being zrange[0] <= z <= zrange[1]. currently must be set to the interior
    of the image, so [state.pad, state.image.shape[0] - state.pad]

laser_wavelength : float
    wavelength of light in um of the incoming laser light

zslab : float
    Pixel position of the optical interface where 0 is the edge of the
    image in the z direction

zscale : float
    Scaling of the z pixel size, so that each pixel is located at
    zscale*(z - zint), such that the interface does not move with zscale

kfki : float
    Ratio of outgoing to incoming light wavevectors, 2\pi/\lambda

n2n1 : float
    Ratio of the index mismatch across the optical interface. For typical
    glass with glycerol/water 80/20 mixture this is 1.4/1.518

alpha : float
    Aperture of the lens in radians, set by arcsin(n2n1)?

polar_angle : float
    the angle of the light polarization with respect to the scanning axis

pxsize : float
    the size of a xy pixel in um, defaults to cohen group size 0.125 um

support_factor : integer
    size of the support

normalize : boolean
    if True, use the normalization as calculated by the PSF instead of
    unit normalization

sigkf : float
    Width of wavelengths to use in the polychromatic psf, None is
    monochromatic. Values for kfki = kfki +- sigkf, unitless

nkpts : integer
    number of integration points to use for the polychromatic psf

cutoffval : float
    Percentage of peak psf value to cutoff using a 3-axis
    exp(-(r-r0)**4) function where r0 is determined by cutoffval. A
    good value for this is the bit depth of the camera, or possibly the
    SNR, so 1/2**8 or 1/50.

measurement_iterations : int
    number of interations used when trying to find the center of mass
    of the psf in a certain slice

k_dist : str
    Eithe ['gaussian', 'gamma'] which control the wavevector
    distribution for the polychromatic detection psf. Default
    is Gaussian.

use_J1 : boolean
    Which hdet confocal model to use. Set to True to include the
    J1 term corresponding to a large-NA focusing lens, False to
    exclude it. Default is True

cutbyval : boolean
    If True, cuts the PSF based on the actual value instead of the
    position associated with the nearest value.

cutfallrate : float
    The relative value of the cutoffval over which to damp the
    remaining values of the psf. 0.3 seems a good fit now.

cutedgeval : float
    The value with which to determine the edge of the psf, typically
    taken around floating point, 1e-12

Notes:
    a = ExactLineScanConfocalPSF((64,)*3)
    psf, (z,y,x) = a.psf_slice(1., size=51)
    imshow((psf*r**4)[:,:,25], cmap='bone')


mapper = {<EOL>'<STR_LIT>': '<STR_LIT>',<EOL>'<STR_LIT>': '<STR_LIT>',<EOL>'<STR_LIT>': '<STR_LIT>',<EOL>'<STR_LIT>': '<STR_LIT>',<EOL>'<STR_LIT>': '<STR_LIT>',<EOL>'<STR_LIT>': '<STR_LIT>'<EOL>}<EOL>bads = [self.zscale, '<STR_LIT>']<EOL>d = {}<EOL>for k,v in iteritems(mapper):<EOL><INDENT>if k in self.param_dict:<EOL><INDENT>d[v] = self.param_dict[k]<EOL><DEDENT><DEDENT>d.update({<EOL>'<STR_LIT>': self.polar_angle,<EOL>'<STR_LIT>': self.normalize,<EOL>'<STR_LIT>':self.use_J1<EOL>})<EOL>if self.polychromatic:<EOL><INDENT>d.update({'<STR_LIT>': self.nkpts})<EOL>d.update({'<STR_LIT>': self.k_dist})<EOL><DEDENT>if self.do_pinhole:<EOL><INDENT>d.update({'<STR_LIT>': self.num_line_pts})<EOL><DEDENT>return d<EOL>


<EOL>if self.polychromatic:<EOL><INDENT>func = psfcalc.calculate_polychrome_pinhole_psf<EOL><DEDENT>else:<EOL><INDENT>func = psfcalc.calculate_pinhole_psf<EOL><DEDENT>x0, y0 = [psfcalc.vec_to_halfvec(v) for v in [x,y]]<EOL>vls = psfcalc.wrap_and_calc_psf(x0, y0, z, func, **kwargs)<EOL>return vls / vls.sum()<EOL>


def __init__(self, cheb_degree=<NUM_LIT:6>, cheb_evals=<NUM_LIT:8>, *args, **kwargs):

self.cheb_degree = cheb_degree<EOL>self.cheb_evals = cheb_evals<EOL>super(ChebyshevPSF, self).__init__(*args, **kwargs)<EOL>

Same as ExactPSF, except that the convolution is performed in
the 4th dimension by employing fast Chebyshev approximates to
how the PSF varies with depth into the sample. For help, see
ExactPSF.

Other parameters
----------------
cheb_degree : integer
    degree of the Chebyshev approximant

cheb_evals : integer
    number of interpolation points used to create the coefficient matrix

See also
--------
:class:`peri.comp.exactpsf.ExactPSF`


def __init__(self, support_size=[<NUM_LIT>,<NUM_LIT>,<NUM_LIT>], *args, **kwargs):

self.cutoffval = None<EOL>self.support = np.array(support_size)<EOL>super(FixedSSChebPSF, self).__init__(*args, **kwargs)<EOL>

ChebyshevPSF with a fixed support size

See also
--------
:class:`peri.comp.exactpsf.ChebyshevPSF`


l,u = max(self.zrange[<NUM_LIT:0>], self.param_dict['<STR_LIT>']), self.zrange[<NUM_LIT:1>]<EOL>size_l, drift_l = self.measure_size_drift(l, size=self.support)<EOL>size_u, drift_u = self.measure_size_drift(u, size=self.support)<EOL>self.drift_poly = np.polyfit([l, u], [drift_l, drift_u], <NUM_LIT:1>)<EOL>


def __init__(self, params=None, values=None, ordered=True, category='<STR_LIT>'):

gen = OrderedDict if ordered else dict<EOL>if params is not None and values is not None:<EOL><INDENT>self.param_dict = gen()<EOL>for p,v in zip(params, values):<EOL><INDENT>self.param_dict[p] = v<EOL><DEDENT><DEDENT>elif params is not None and values is None:<EOL><INDENT>self.param_dict = params<EOL><DEDENT>else:<EOL><INDENT>self.param_dict = gen()<EOL><DEDENT>self.category = category<EOL>self.ordered = ordered<EOL>

Any object which computes something based on parameters and values can
be considered a ``ParameterGroup``. This class provides a common
interface since ``ParameterGroup`` appears throughout ``PERI``
including ``Components``, ``Priors``, ``States``. In the very basic
form, a ``ParameterGroup`` is a ``dict`` or ``OrderedDict`` of::

    { parameter_name: parameter_value, ... }

The use of a dictionary is strictly optional -- as long as the following
methods are provided, the parameters and values may be stored in any
format that is convenient:

    * :func:`~peri.comp.comp.ParameterGroup.params`
    * :func:`~peri.comp.comp.ParameterGroup.values`
    * :func:`~peri.comp.comp.ParameterGroup.get_values`
    * :func:`~peri.comp.comp.ParameterGroup.set_values`
    * :func:`~peri.comp.comp.ParameterGroup.update`

Parameters
----------
params : string, list of strings
    The names of the parameters, in the proper order

values : number, list of numbers
    The values corresponding to the parameter names

ordered : boolean (default: True)
    If True, uses an OrderedDict so that parameter order is
    deterministic independent of number of parameters

category : string (default: 'param')
    Name of the category associated with this ParameterGroup.

    .. warning::

        FIXME : should only be a property of Component


self.set_values(params, values)<EOL>return True<EOL>

Update the calculation of the class based on a pair or pairs
of parameters and associated values.

Parameters
----------
params : string, list of strings
    name of parameters to update

values : number, list of numbers
    cooresponding values to update


return util.delistify(<EOL>[self.param_dict[p] for p in util.listify(params)], params<EOL>)<EOL>

Get the value of a list or single parameter.

Parameters
----------
params : string, list of string
    name of parameters which to retrieve


for p, v in zip(util.listify(params), util.listify(values)):<EOL><INDENT>self.param_dict[p] = v<EOL><DEDENT>

Directly set the values corresponding to certain parameters.
This does not necessarily trigger and update of the calculation,

See also
--------
:func:`~peri.comp.comp.ParameterGroup.update` : full update func


return list(self.param_dict.keys())<EOL>


return list(self.param_dict.values())<EOL>


return []<EOL>

Elements of the class that should not be included in pickled objects.
If inheriting a new class, should be::

    super(Class, self).nopickle() + ['other1', 'other2', ...]

Returns
-------
elements : list of strings
    The name of class member variables which should not be pickled


return {"<STR_LIT>": self.params, "<STR_LIT>": self.values, "<STR_LIT>": self.ordered}<EOL>

Pickling helper method which returns a dictionary of function
parameters which get passed to pickle via `__getinitargs__
<https://docs.python.org/2/library/pickle.html#object.__getinitargs__>`_

Returns
-------
arg_dict : dictionary
    ``**kwargs`` to be passed to the __init__ func after unpickling


def __init__(self, params, values, ordered=True, category='<STR_LIT>'):

for attr in ['<STR_LIT>', '<STR_LIT>', '<STR_LIT>']:<EOL><INDENT>if not hasattr(self, attr):<EOL><INDENT>setattr(self, attr, None)  <EOL><DEDENT><DEDENT>super(Component, self).__init__(<EOL>params, values, ordered=ordered, category=category<EOL>)<EOL>

A :class:`~peri.comp.comp.ParameterGroup` which specifically computes
over sections of an image for an :class:`~peri.states.ImageState`. To
this end, we require the implementation of several new member functions:

    * :func:`~peri.comp.comp.Component.initialize`
    * :func:`~peri.comp.comp.Component.get_update_tile`
    * :func:`~peri.comp.comp.Component.get_padding_size`
    * :func:`~peri.comp.comp.Component.set_shape`
    * :func:`~peri.comp.comp.Component.set_tile`
    * :func:`~peri.comp.comp.Component.get`

In order to facilitate optimizations such as caching and local updates,
we must incorporate tiling in this object.

Parameters
----------
params : string, list of strings
    The names of the parameters, in the proper order

values : number, list of numbers
    The values corresponding to the parameter names

ordered : boolean (default: True)
    If True, uses an OrderedDict so that parameter order is
    deterministic independent of number of parameters

category : string (default: 'param')
    Name of the category associated with this ParameterGroup.


pass<EOL>


def get_update_tile(self, params, values):

pass<EOL>

This method returns a :class:`~peri.util.Tile` object defining the
region of a field that has to be modified by the update of (params,
values). For example, if this Component is the point-spread-function,
it might return a tile of entire image since every parameter affects
the entire image::

    return self.shape

Parameters
-----------
params : single param, list of params
    A single parameter or list of parameters to be updated

values : single value, list of values
    The values corresponding to the params

Returns
-------
tile : :class:`~peri.util.Tile`
    A tile corresponding to the image region


pass<EOL>

Get the amount of padding required for this object when calculating
about a tile `tile`. Padding size is the total size, so half that
on each side. For example, if this Component is a Gaussian point spread
function, then the padding returned might be::

    peri.util.Tile(np.ceil(2*self.sigma))

Parameters
-----------
tile : :class:`~peri.util.Tile`
    A tile defining the region of interest

Returns
-------
pad : :class:`~peri.util.Tile`
    A tile corresponding to the required padding size


self.tile = tile<EOL>

Set the currently active tile region for the calculation


if self.shape != shape or self.inner != inner:<EOL><INDENT>self.shape = shape<EOL>self.inner = inner<EOL>self.initialize()<EOL><DEDENT>

Set the overall shape of the calculation area. The total shape of that
the calculation can possibly occupy, in pixels. The second, inner, is
the region of interest within the image.


pass<EOL>

Perform its routine, whatever that may be


pass<EOL>

Return the `natural` part of the model. In the case of most elements it
is the calculated field, for others it is the object itself.


return []<EOL>

Which methods a class wants to expose to parent classes


return ['<STR_LIT>']<EOL>

Class attributes which should not be included in a pickle object


self._parent = obj<EOL>

Registery a parent object so that communication maybe happen upwards


if self._parent:<EOL><INDENT>self._parent.trigger_parameter_change()<EOL><DEDENT>


def trigger_update(self, params, values):

if self._parent:<EOL><INDENT>self._parent.trigger_update(params, values)<EOL><DEDENT>else:<EOL><INDENT>self.update(params, values)<EOL><DEDENT>


def __init__(self, comps, field_reduce_func=None, category='<STR_LIT>'):

self.comps = comps<EOL>self.category = category<EOL>if not field_reduce_func:<EOL><INDENT>field_reduce_func = reduce_add<EOL><DEDENT>self.field_reduce_func = field_reduce_func<EOL>self.setup_params()<EOL>self.setup_passthroughs()<EOL>self._parent = None<EOL>

Group a number of components into a single coherent object which a
single interface for each function. Obvious reductions are performed in
places such as get_update_tile which takes the bounding tile of all
constituent get_update_tile.  The only reduction which is not
straight-forward is get, but by default it adds all fields. This
class has the same interface as Component itself.

Parameters
-----------
comps : list of :class:`~peri.comp.comp.Component`
    The components to group together

field_reduce_func : function(list of ndarrays)
    Reduction function for get object of collection, what happens when
    all comps are reduced to a single field (etc). For example, nested
    point spread functions may want convolution to be the reduce func::

        def reduce_conv(psfs):
            ffts = np.array([fft.fftn(p) for p in psfs])
            return fft.ifftn(np.prod(ffts, axis=0))

    Or, if it is just a simple field that needs to be added together::

        def reduce_add(x):
            return reduce(add, x)

category : string
    Name of the category associated with this ``ComponentCollection``


return {<EOL>'<STR_LIT>': self.comps,<EOL>'<STR_LIT>': self.category,<EOL>'<STR_LIT>': self.field_reduce_func,<EOL>}<EOL>

Return arguments that are passed to init to setup the class again


def split_params(self, params, values=None):

pc, vc = [], []<EOL>returnvalues = values is not None<EOL>if values is None:<EOL><INDENT>values = [<NUM_LIT:0>]*len(util.listify(params))<EOL><DEDENT>for c in self.comps:<EOL><INDENT>tp, tv = [], []<EOL>for p,v in zip(util.listify(params), util.listify(values)):<EOL><INDENT>if not p in self.lmap:<EOL><INDENT>raise NotAParameterError("<STR_LIT>" % (p, self))<EOL><DEDENT>if c in self.pmap[p]:<EOL><INDENT>tp.append(p)<EOL>tv.append(v)<EOL><DEDENT><DEDENT>pc.append(tp)<EOL>vc.append(tv)<EOL><DEDENT>if returnvalues:<EOL><INDENT>return pc, vc<EOL><DEDENT>return pc<EOL>

Split params, values into groups that correspond to the ordering in
self.comps. For example, given a sphere collection and slab::

    [
        (spheres) [pos rad etc] [pos val, rad val, etc]
        (slab) [slab params] [slab vals]
    ]


fields = [c.get() for c in self.comps]<EOL>return self.field_reduce_func(fields)<EOL>


for c in self.comps:<EOL><INDENT>c.set_tile(tile)<EOL><DEDENT>

Set the current working tile for components


for c in self.comps:<EOL><INDENT>c.set_shape(shape, inner)<EOL><DEDENT>


def _normalize(comps, param):<EOL><INDENT>vals = [c.get_values(param) for c in comps]<EOL>diff = any([vals[i] != vals[i+<NUM_LIT:1>] for i in range(len(vals)-<NUM_LIT:1>)])<EOL>if diff:<EOL><INDENT>for c in comps:<EOL><INDENT>c.set_values(param, vals[<NUM_LIT:0>])<EOL><DEDENT><DEDENT><DEDENT>for param, comps in iteritems(self.lmap):<EOL><INDENT>if isinstance(comps, list) and len(comps) > <NUM_LIT:1>:<EOL><INDENT>_normalize(comps, param)<EOL><DEDENT><DEDENT>

Ensure that shared parameters are the same value everywhere


self._nopickle = []<EOL>for c in self.comps:<EOL><INDENT>funcs = inspect.getmembers(c, predicate=inspect.ismethod)<EOL>for func in funcs:<EOL><INDENT>if func[<NUM_LIT:0>].startswith('<STR_LIT>'):<EOL><INDENT>setattr(self, func[<NUM_LIT:0>], func[<NUM_LIT:1>])<EOL>self._nopickle.append(func[<NUM_LIT:0>])<EOL><DEDENT><DEDENT>funcs = c.exports()<EOL>for func in funcs:<EOL><INDENT>newname = c.category + '<STR_LIT:_>' + func.__func__.__name__<EOL>setattr(self, newname, func)<EOL>self._nopickle.append(newname)<EOL><DEDENT><DEDENT>

Inherit some functions from the components that we own. In particular,
let's grab all functions that begin with `param_` so the super class
knows how to get parameter groups. Also, take anything that is listed
under Component.exports and rename with the category type, i.e.,
SphereCollection.add_particle -> Component.obj_add_particle


return (<NUM_LIT:1>-np.clip((dr+alpha) / (<NUM_LIT:2>*alpha), <NUM_LIT:0>, <NUM_LIT:1>))<EOL>

Linearly interpolate the pixels for the platonic object


return <NUM_LIT:1.0>/(<NUM_LIT:1.0> + np.exp(alpha*dr))<EOL>


p0 = (dr+alpha)**<NUM_LIT:2>/(<NUM_LIT:2>*alpha**<NUM_LIT:2>)*(<NUM_LIT:0> > dr)*(dr>-alpha)<EOL>p1 = <NUM_LIT:1>*(dr><NUM_LIT:0>)-(alpha-dr)**<NUM_LIT:2>/(<NUM_LIT:2>*alpha**<NUM_LIT:2>)*(<NUM_LIT:0><dr)*(dr<alpha)<EOL>return (<NUM_LIT:1>-np.clip(p0+p1, <NUM_LIT:0>, <NUM_LIT:1>))<EOL>

Cumulative distribution function for the traingle distribution


def sphere_analytical_gaussian(dr, a, alpha=<NUM_LIT>):

term1 = <NUM_LIT:0.5>*(erf((dr+<NUM_LIT:2>*a)/(alpha*np.sqrt(<NUM_LIT:2>))) + erf(-dr/(alpha*np.sqrt(<NUM_LIT:2>))))<EOL>term2 = np.sqrt(<NUM_LIT:0.5>/np.pi)*(alpha/(dr+a+<NUM_LIT>)) * (<EOL>np.exp(-<NUM_LIT:0.5>*dr**<NUM_LIT:2>/alpha**<NUM_LIT:2>) - np.exp(-<NUM_LIT:0.5>*(dr+<NUM_LIT:2>*a)**<NUM_LIT:2>/alpha**<NUM_LIT:2>)<EOL>)<EOL>return term1 - term2<EOL>

Analytically calculate the sphere's functional form by convolving the
Heavyside function with first order approximation to the sinc, a Gaussian.
The alpha parameters controls the width of the approximation -- should be
1, but is fit to be roughly 0.2765


def sphere_analytical_gaussian_trim(dr, a, alpha=<NUM_LIT>, cut=<NUM_LIT>):

m = np.abs(dr) <= cut<EOL>rr = dr[m]<EOL>t = -rr/(alpha*np.sqrt(<NUM_LIT:2>))<EOL>q = <NUM_LIT:0.5>*(<NUM_LIT:1> + erf(t)) - np.sqrt(<NUM_LIT:0.5>/np.pi)*(alpha/(rr+a+<NUM_LIT>)) * np.exp(-t*t)<EOL>ans = <NUM_LIT:0>*dr<EOL>ans[m] = q<EOL>ans[dr >  cut] = <NUM_LIT:0><EOL>ans[dr < -cut] = <NUM_LIT:1><EOL>return ans<EOL>

See sphere_analytical_gaussian_exact.

I trimmed to terms from the functional form that are essentially zero (1e-8)
for r0 > cut (~1.5), a fine approximation for these platonic anyway.


def sphere_analytical_gaussian_fast(dr, a, alpha=<NUM_LIT>, cut=<NUM_LIT>):

code = """<STR_LIT>"""<EOL>shape = r.shape<EOL>r = r.flatten()<EOL>N = self.N<EOL>ans = r*<NUM_LIT:0><EOL>pi = np.pi<EOL>inline(code, arg_names=['<STR_LIT>', '<STR_LIT:a>', '<STR_LIT>', '<STR_LIT>', '<STR_LIT>', '<STR_LIT>', '<STR_LIT:N>'],<EOL>support_code=functions, verbose=<NUM_LIT:0>)<EOL>return ans.reshape(shape)<EOL>

See sphere_analytical_gaussian_trim, but implemented in C with
fast erf and exp approximations found at
    Abramowitz and Stegun: Handbook of Mathematical Functions
    A Fast, Compact Approximation of the Exponential Function

The default cut 1.25 was chosen based on the accuracy of fast_erf


def sphere_constrained_cubic(dr, a, alpha):

sqrt3 = np.sqrt(<NUM_LIT:3>)<EOL>b_coeff = a*<NUM_LIT:0.5>/sqrt3*(<NUM_LIT:1> - <NUM_LIT>*sqrt3*alpha)/(<NUM_LIT> + a*a)<EOL>rscl = np.clip(dr, -<NUM_LIT:0.5>*sqrt3, <NUM_LIT:0.5>*sqrt3)<EOL>a, d = rscl + <NUM_LIT:0.5>*sqrt3, rscl - <NUM_LIT:0.5>*sqrt3<EOL>return alpha*d*a*rscl + b_coeff*d*a - d/sqrt3<EOL>

Sphere generated by a cubic interpolant constrained to be (1,0) on
(r0-sqrt(3)/2, r0+sqrt(3)/2), the size of the cube in the (111) direction.


def exact_volume_sphere(rvec, pos, radius, zscale=<NUM_LIT:1.0>, volume_error=<NUM_LIT>,<EOL>function=sphere_analytical_gaussian, max_radius_change=<NUM_LIT>, args=()):

vol_goal = <NUM_LIT>/<NUM_LIT:3>*np.pi*radius**<NUM_LIT:3> / zscale<EOL>rprime = radius<EOL>dr = inner(rvec, pos, rprime, zscale=zscale)<EOL>t = function(dr, rprime, *args)<EOL>for i in range(MAX_VOLUME_ITERATIONS):<EOL><INDENT>vol_curr = np.abs(t.sum())<EOL>if np.abs(vol_goal - vol_curr)/vol_goal < volume_error:<EOL><INDENT>break<EOL><DEDENT>rprime = rprime + <NUM_LIT:1.0>*(vol_goal - vol_curr) / (<NUM_LIT:4>*np.pi*rprime**<NUM_LIT:2>)<EOL>if np.abs(rprime - radius)/radius > max_radius_change:<EOL><INDENT>break<EOL><DEDENT>dr = inner(rvec, pos, rprime, zscale=zscale)<EOL>t = function(dr, rprime, *args)<EOL><DEDENT>return t<EOL>

Perform an iterative method to calculate the effective sphere that perfectly
(up to the volume_error) conserves volume.  Return the resulting image


def __init__(self, pos, shape=None, param_prefix='<STR_LIT>', category='<STR_LIT>',<EOL>support_pad=<NUM_LIT:4>, float_precision=np.float64):

if pos.ndim != <NUM_LIT:2>:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>self.category = category<EOL>self.support_pad = support_pad<EOL>self.pos = pos.astype('<STR_LIT:float>')<EOL>self.param_prefix = param_prefix<EOL>if float_precision not in (np.float64, np.float32, np.float16):<EOL><INDENT>raise ValueError('<STR_LIT>' +<EOL>'<STR_LIT>')<EOL><DEDENT>self.float_precision = float_precision<EOL>self.shape = shape<EOL>self.setup_variables()<EOL>if self.shape:<EOL><INDENT>self.inner = self.shape.copy()<EOL>self.tile = self.inner.copy()<EOL>self.initialize()<EOL><DEDENT>

Parent class for a large collection of particles, such as spheres or
points or ellipsoids or rods.

This class is good for a collection of objects which each have a
position as well as (possibly) some other parameters, like particle
radius, aspect ratio, or brightness. Its .get() method returns a
field of the drawn particles, selected on the current tile. Any
daughter classes need the following methods:

* _draw_particle
* _update_type
* setup_variables
* get_values
* set_values
* add_particle
* remove_particle

In addition, the following methods should be modified for particles
with more parameters than just positions:

* _drawargs
* _tile
* param_particle
* exports
* _p2i

If you have a few objects to group, like 2 or 3 slabs, group them
with a `peri.comp.ComponentCollection` instead.

Parameters
----------
pos : ndarray [N,d]
    Initial positions of the particles. Re-cast as float internally

shape : ``peri.util.Tile``, optional
    Shape of the field over which to draw the platonic spheres.
    Default is None.

param_prefix : string, optional
    Prefix for the particle parameter names. Default is `'sph'`

category : string, optional
    Category, as in comp.Component. Default is `'obj'`.

support_pad : Int, optional
    How much to pad the boundary of particles when calculating the
    support so that particles do not leak out the edges. Default is 4

float_precision : numpy float datatype, optional
    One of numpy.float16, numpy.float32, numpy.float64; precision
    for precomputed arrays. Default is np.float64; make it 16 or 32
    to save memory.


def _draw_particle(self, pos, sign=<NUM_LIT:1>):

raise NotImplementedError('<STR_LIT>')<EOL>

Updates ``self.particles`` by drawing a particle at position ``pos``,
with possible additional unnamed arguments between ``pos`` and
``sign``. If ``sign`` is -1, un-draws the particle instead.

To be able to fit this component in a model, _draw_particle must
create an image that is numerically continuous as pos changes --
i.e. the edge of the particle must alias smoothly to 0.


Given a list of parameters, returns a bool of whether or not any of
the parameters require a global update, and a list of particle indices
which are included in ``params``, e.g.
``return doglobal, particles``


Creates an ordered list of parameters and stores in self._params


<EOL>raise NotImplementedError('<STR_LIT>')<EOL>

Returns a util.delisty-d assortment of values and parameters, e.g.
(get values for the parameters, both particle positions and globals)
(return delistify(values, params))


return [[] for p in self.pos]<EOL>

Returns a list of arguments for self._draw_particle, of the same
length as `self.pos`. For example, if drawing a sphere, _drawargs
could return a list of radii.


pos = self._trans(self.pos[n])<EOL>return Tile(pos, pos).pad(self.support_pad)<EOL>

Get the update tile surrounding particle `n`


g = param.split('<STR_LIT:->')<EOL>if len(g) == <NUM_LIT:3>:<EOL><INDENT>return g[<NUM_LIT:2>], int(g[<NUM_LIT:1>])<EOL><DEDENT>else:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>

Parameter to indices, returns (coord, index), e.g. for a pos
pos     : ('x', 100)


self.particles = np.zeros(self.shape.shape, dtype=self.float_precision)<EOL>for p0, arg0 in zip(self.pos, self._drawargs()):<EOL><INDENT>self._draw_particle(p0, *listify(arg0))<EOL><DEDENT>

Start from scratch and initialize all objects / draw self.particles


return [j for j in inds if j >= <NUM_LIT:0> and j < self.N]<EOL>

Clips a list of inds to be on [0, self.N]


return self.param_particle_pos(list(range(self.N)))<EOL>


<EOL>ind = self._vps(listify(ind))<EOL>return [self._i2p(i, j) for i in ind for j in ['<STR_LIT:z>', '<STR_LIT:y>', '<STR_LIT:x>']]<EOL>


return (((self.pos - x)**<NUM_LIT:2>).sum(axis=-<NUM_LIT:1>)).argmin()<EOL>

Get the index of the particle closest to vector `x`


return '<STR_LIT:->'.join([self.param_prefix, str(ind), coord])<EOL>


doglobal, particles = self._update_type(params)<EOL>if doglobal:<EOL><INDENT>return self.shape.copy()<EOL><DEDENT>values0 = self.get_values(params)<EOL>tiles0 = [self._tile(n) for n in particles]<EOL>self.set_values(params, values)<EOL>tiles1 = [self._tile(n) for n in particles]<EOL>self.set_values(params, values0)<EOL>return Tile.boundingtile(tiles0 + tiles1)<EOL>

Get the amount of support size required for a particular update.


<EOL>global_update, particles = self._update_type(params)<EOL>if global_update:<EOL><INDENT>self.set_values(params, values)<EOL>self.initialize()<EOL>return<EOL><DEDENT>oldargs = self._drawargs()<EOL>for n in particles:<EOL><INDENT>self._draw_particle(self.pos[n], *listify(oldargs[n]), sign=-<NUM_LIT:1>)<EOL><DEDENT>self.set_values(params, values)<EOL>newargs = self._drawargs()<EOL>for n in particles:<EOL><INDENT>self._draw_particle(self.pos[n], *listify(newargs[n]), sign=+<NUM_LIT:1>)<EOL><DEDENT>

Update the particles field given new parameter values


def __init__(self, pos, rad, shape=None, zscale=<NUM_LIT:1.0>, support_pad=<NUM_LIT:4>,<EOL>method='<STR_LIT>', alpha=None, user_method=None,<EOL>exact_volume=True, volume_error=<NUM_LIT>, max_radius_change=<NUM_LIT>,<EOL>param_prefix='<STR_LIT>', grouping='<STR_LIT>', category='<STR_LIT>',<EOL>float_precision=np.float64):

if isinstance(rad, (float, int)):<EOL><INDENT>rad = rad*np.ones(pos.shape[<NUM_LIT:0>])<EOL><DEDENT>if rad.size != pos.shape[<NUM_LIT:0>]:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>if pos.ndim != <NUM_LIT:2>:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>self.rad = rad.astype('<STR_LIT:float>')<EOL>self.zscale = zscale<EOL>self.exact_volume = exact_volume<EOL>self.volume_error = volume_error<EOL>self.max_radius_change = max_radius_change<EOL>self.user_method = user_method<EOL>self.grouping = grouping<EOL>self.set_draw_method(method=method, alpha=alpha, user_method=user_method)<EOL>super(PlatonicSpheresCollection, self).__init__(pos=pos, shape=shape,<EOL>param_prefix=param_prefix, category=category, support_pad=<EOL>support_pad, float_precision=float_precision)<EOL>

A collection of spheres in real-space with positions and radii, drawn
not necessarily on a uniform grid (i.e. scale factor associated with
z-direction).  There are many ways to draw the sphere, currently
supported  methods can be one of::

    [
        'bool', 'lerp', 'logistic', 'triangle', 'constrained-cubic',
        'exact-gaussian', 'exact-gaussian-trim', 'exact-gaussian-fast',
        'user-defined'
    ]

Parameters
----------
pos : ndarray [N,3]
    Initial positions of the spheres

rad : ndarray [N] or float
    Initial radii of the spheres

shape : tuple
    Shape of the field over which to draw the platonic spheres

zscale : float
    scaling of z-pixels in the platonic image

support_pad : int
    how much to pad the boundary of particles when calculating
    support so that there is not more contribution

method : string
    The sphere drawing function to use, see above.

alpha : float
    Parameter supplied to sphere drawing function, set to value to
    override default value

user_method : tuple (function, parameters)
    Provide your own sphere function to the drawing method. First
    element of tuple is function with call signature `func(dr, a, *args)`
    where the second element is the `*args` that are not the distance
    to edge (dr) or particles radius (a). `method` must be set to
    'user-defined'.

exact_volume : boolean
    whether to iterate effective particle size until exact volume
    (within volume_error) is achieved

volume_error : float
    relative volume error tolerance in iteration steps

max_radius_change : float
    maximum relative radius change allowed during iteration (due to
    edge particles and other confounding factors)

grouping : string
    Either 'particle' or 'parameter' parameter grouping. If 'particle'
    then grouped by xyza,xyza if 'parameter' then xyz,xyz,a,a

float_precision : numpy float datatype
    One of numpy.float16, numpy.float32, numpy.float64; precision
    for precomputed arrays. Default is np.float64; make it 16 or 32
    to save memory.


return [self._i2p(i, '<STR_LIT:a>') for i in range(self.N)]<EOL>


ind = self._vps(listify(ind))<EOL>return [self._i2p(i, j) for i in ind for j in ['<STR_LIT:z>', '<STR_LIT:y>', '<STR_LIT:x>', '<STR_LIT:a>']]<EOL>

Get position and radius of one or more particles


ind = self._vps(listify(ind))<EOL>return [self._i2p(i, j) for i in ind for j in ['<STR_LIT:z>', '<STR_LIT:y>', '<STR_LIT:x>']]<EOL>


ind = self._vps(listify(ind))<EOL>return [self._i2p(i, '<STR_LIT:a>') for i in ind]<EOL>


rad = listify(rad)<EOL>inds = np.arange(self.N, self.N+len(rad))<EOL>self.pos = np.vstack([self.pos, pos])<EOL>self.rad = np.hstack([self.rad, np.zeros(len(rad))])<EOL>self.setup_variables()<EOL>self.trigger_parameter_change()<EOL>params = self.param_particle_rad(inds)<EOL>self.trigger_update(params, rad)<EOL>return inds<EOL>

Add a particle or list of particles given by a list of positions and
radii, both need to be array-like.

Parameters
----------
pos : array-like [N, 3]
    Positions of all new particles

rad : array-like [N]
    Corresponding radii of new particles

Returns
-------
inds : N-element numpy.ndarray.
    Indices of the added particles.


if self.rad.shape[<NUM_LIT:0>] == <NUM_LIT:0>:<EOL><INDENT>return<EOL><DEDENT>inds = listify(inds)<EOL>pos = self.pos[inds].copy()<EOL>rad = self.rad[inds].copy()<EOL>self.trigger_update(self.param_particle_rad(inds), np.zeros(len(inds)))<EOL>self.pos = np.delete(self.pos, inds, axis=<NUM_LIT:0>)<EOL>self.rad = np.delete(self.rad, inds, axis=<NUM_LIT:0>)<EOL>self.setup_variables()<EOL>self.trigger_parameter_change()<EOL>return np.array(pos).reshape(-<NUM_LIT:1>,<NUM_LIT:3>), np.array(rad).reshape(-<NUM_LIT:1>)<EOL>

Remove the particle at index `inds`, may be a list.
Returns [3,N], [N] element numpy.ndarray of pos, rad.


g = param.split('<STR_LIT:->')<EOL>if len(g) == <NUM_LIT:1>:<EOL><INDENT>return '<STR_LIT>', None<EOL><DEDENT>if len(g) == <NUM_LIT:3>:<EOL><INDENT>return g[<NUM_LIT:2>], int(g[<NUM_LIT:1>])<EOL><DEDENT>

Parameter to indices, returns (coord, index). Therefore, for a
pos    : ('x', 100)
rad    : ('a', 100)
zscale : ('zscale, None)


dozscale = False<EOL>particles = []<EOL>for p in listify(params):<EOL><INDENT>typ, ind = self._p2i(p)<EOL>particles.append(ind)<EOL>dozscale = dozscale or typ == '<STR_LIT>'<EOL><DEDENT>particles = set(particles)<EOL>return dozscale, particles<EOL>

Returns dozscale and particle list of update


zsc = np.array([<NUM_LIT:1.0>/self.zscale, <NUM_LIT:1>, <NUM_LIT:1>])<EOL>pos, rad = self.pos[n], self.rad[n]<EOL>pos = self._trans(pos)<EOL>return Tile(pos - zsc*rad, pos + zsc*rad).pad(self.support_pad)<EOL>


<EOL>params = listify(params)<EOL>values = listify(values)<EOL>for i, p in enumerate(params):<EOL><INDENT>if (p[-<NUM_LIT:2>:] == '<STR_LIT>') and (values[i] < <NUM_LIT:0>):<EOL><INDENT>values[i] = <NUM_LIT:0.0><EOL><DEDENT><DEDENT>super(PlatonicSpheresCollection, self).update(params, values)<EOL>

Calls an update, but clips radii to be > 0


def __init__(self, zpos=<NUM_LIT:0>, angles=(<NUM_LIT:0>,<NUM_LIT:0>), param_prefix='<STR_LIT>', shape=None,<EOL>float_precision=np.float64, category='<STR_LIT>'):

self.lbl_zpos = param_prefix+'<STR_LIT>'<EOL>self.lbl_theta = param_prefix+'<STR_LIT>'<EOL>self.lbl_phi = param_prefix+'<STR_LIT>'<EOL>if float_precision not in (np.float64, np.float32, np.float16):<EOL><INDENT>raise ValueError('<STR_LIT>' +<EOL>'<STR_LIT>')<EOL><DEDENT>self.float_precision = float_precision<EOL>params = [self.lbl_zpos, self.lbl_theta, self.lbl_phi]<EOL>values = [float(i) for i in [zpos, angles[<NUM_LIT:0>], angles[<NUM_LIT:1>]]]<EOL>super(Slab, self).__init__(params, values, ordered=False,<EOL>category=category)<EOL>if shape:<EOL><INDENT>inner = shape.copy()  <EOL>self.set_shape(shape, inner)<EOL><DEDENT>self.set_tile(self.shape)<EOL>if self.shape:<EOL><INDENT>self.initialize()<EOL><DEDENT>

A half plane corresponding to a cover-slip.

Parameters
----------
shape : tuple
    field shape over which to calculate

zpos : float
    position of the center of the slab in pixels

angles : tuple of float (2,), optional
    Euler-like Angles of rotation of the normal with respect to the
    z-axis, i.e. ``angles=(0., 0.)`` gives a slab with a normal
    along z. The first angle theta is the rotation about the x-axis;
    the second angle phi is the rotation about the y-axis. Default
    is (0,0).

float_precision : numpy float datatype
    One of numpy.float16, numpy.float32, numpy.float64; precision
    for precomputed arrays. Default is np.float64; make it 16 or 32
    to save memory.


t = self.param_dict[self.lbl_theta]<EOL>r0 = np.array([ [np.cos(t),  -np.sin(t), <NUM_LIT:0>],<EOL>[np.sin(t), np.cos(t), <NUM_LIT:0>],<EOL>[<NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:1>]])<EOL>p = self.param_dict[self.lbl_phi]<EOL>r1 = np.array([ [np.cos(p), <NUM_LIT:0>, np.sin(p)],<EOL>[<NUM_LIT:0>, <NUM_LIT:1>, <NUM_LIT:0>],<EOL>[-np.sin(p), <NUM_LIT:0>, np.cos(p)]])<EOL>return np.dot(r1, r0)<EOL>

Generate the composite rotation matrix that rotates the slab normal.

The rotation is a rotation about the x-axis, followed by a rotation
about the z-axis.


def create_configuration(N, tile, radius=<NUM_LIT>, **kwargs):

pos, rad, tile = initialize_particles(N, tile, radius=radius, **kwargs)<EOL>sim = BrownianHardSphereSimulation(pos, rad, tile)<EOL>sim.relax(<NUM_LIT:1000>)<EOL>sim.step(<NUM_LIT:1000>)<EOL>sim.relax(<NUM_LIT:1000>)<EOL>return sim.pos, sim.rad, tile<EOL>

Quick configuration creation with `N` particles in a tile of size `tile`.


def initialize_particles(N=None, tile=None, phi=None, radius=<NUM_LIT>, polydispersity=<NUM_LIT:0.>):

vparticle = <NUM_LIT>/<NUM_LIT:3>*np.pi*radius**<NUM_LIT:3><EOL>if phi and tile is not None:<EOL><INDENT>vbox = np.prod(tile.shape)<EOL>N = int(phi * vbox / vparticle)<EOL><DEDENT>elif N and phi:<EOL><INDENT>vbox = N * vparticle / phi<EOL>side = vbox**(<NUM_LIT:1.>/<NUM_LIT:3>)<EOL>tile = util.Tile(side)<EOL><DEDENT>elif N and tile is not None:<EOL><INDENT>pass<EOL><DEDENT>else:<EOL><INDENT>raise AttributeError("<STR_LIT>")<EOL><DEDENT>pos = np.random.rand(N, <NUM_LIT:3>)*tile.shape + tile.l<EOL>rad = np.abs(np.random.normal(<EOL>loc=radius, scale=np.abs(polydispersity*radius)+<NUM_LIT>, size=N<EOL>))<EOL>return pos, rad, tile<EOL>

Creates an initial (overlapping) particle configuration by 3 different ways:
    (N, phi) : creates an appropriate box
    (N, tile) : uses a box and determines phi
    (tile, phi): uses both to determine N


def __init__(self, pos, rad, tile, D=<NUM_LIT>, epsilon=<NUM_LIT>, dt=<NUM_LIT>):

self.pos = pos<EOL>self.rad = rad<EOL>self.tile = tile<EOL>self.N = self.pos.shape[<NUM_LIT:0>]<EOL>self.dim = self.pos.shape[<NUM_LIT:1>]<EOL>self.forces = <NUM_LIT:0>*self.pos<EOL>self.D = D<EOL>self.dt = dt<EOL>self.epsilon = epsilon<EOL>

Creates a hard sphere brownian dynamics simulation in ND as specified
by the pos, rad and tile supplied to initializer.

Parameters:
-----------
pos : ndarray [N, dim]
    Positions of particles in the simulation.

rad : ndarray [N]
    Radii of particles in the simulation.

tile : `peri.util.Tile`
    The simulation box defined by a tile with left and right bounds.

D : float
    diffusion constant in proper units

epsilon : float
    Force constant for the soft-sphere potential f = \epsilon (1-d/d_0)^{3/2}.
    This parameter is really a ratio of epsilon / eta (the damping parameter)

dt : float
    timestep for the integrator


coeff = np.sqrt(<NUM_LIT:2>*self.D)<EOL>return coeff * np.random.randn(*self.pos.shape)<EOL>

Calculate the effective force of the Langevin dynamics


for i in range(self.dim):<EOL><INDENT>mask = (self.pos[:,i] < self.tile.l[i])<EOL>self.pos[mask,i] = <NUM_LIT:2>*self.tile.l[i]-self.pos[mask,i]<EOL>mask = (self.pos[:,i] > self.tile.r[i])<EOL>self.pos[mask,i] = <NUM_LIT:2>*self.tile.r[i]-self.pos[mask,i]<EOL><DEDENT>

Apply hard reflective boundary conditions to particles


self.pos += forces*self.dt<EOL>

Integrate the equations of motion. For this simple integrator, we are
using the simplest sympletic integrator, NSV where

    v_{n+1} = v_n + f*dt
    x_{n+1} = x_n + v_{n+1}*dt

Parameters:
-----------
forces : ndarray[N,2]
    the forces on each particle


def step(self, steps=<NUM_LIT:100>, mask=None):

if mask is None:<EOL><INDENT>mask = np.ones_like(self.rad).astype('<STR_LIT:bool>')<EOL><DEDENT>for step in range(steps):<EOL><INDENT>self.forces = self.force_hardsphere(mask) + self.force_noise()<EOL>self.forces[~mask, :] = <NUM_LIT:0.><EOL>self.integrate(self.forces)<EOL>self.boundary_condition()<EOL><DEDENT>

Perform a set of integration / BC steps and update plot

Parameters:
-----------
steps : int
    number of time steps of size self.dt to perform

mask : boolean ndarray[N]
    if provided, only simulate the motion of certain particles given
    by the boolean array mask


for step in range(steps):<EOL><INDENT>self.forces = self.force_hardsphere()<EOL>self.integrate(self.forces)<EOL>self.boundary_condition()<EOL><DEDENT>

Relax the current configuration using just pair wise forces (no noise)


N, pos, rad = self.N, self.pos, self.rad<EOL>if mask is None:<EOL><INDENT>mask = np.ones_like(self.rad).astype('<STR_LIT:bool>')<EOL><DEDENT>f = np.zeros_like(pos)<EOL>for i in np.arange(N)[mask]:<EOL><INDENT>rij = pos[i] - pos<EOL>dist = np.sqrt((rij**<NUM_LIT:2>).sum(axis=-<NUM_LIT:1>))<EOL>dia = rad + rad[i]<EOL>mask = (dist > <NUM_LIT>)&(dist < dia)<EOL>dia = dia[mask]<EOL>rij = rij[mask]<EOL>dist = dist[mask][:,None]<EOL>if len(rij) > <NUM_LIT:0>:<EOL><INDENT>forces = self.epsilon*(<NUM_LIT:1>-dist/dia[:,None])**<NUM_LIT:2> * rij/dist<EOL>f[i] += forces.sum(axis=<NUM_LIT:0>)<EOL><DEDENT><DEDENT>return f<EOL>

Calculate 'hard shere' forces between all particles. In this case,
hard sphere means Hertzian potential

    V(r_{ij}) = \epsilon (1 - r_{ij} / 2a)^{5/2}


def neighbors(self, cutoff, indices=None):

indices = indices if indices is not None else np.arange(self.N)<EOL>indices = util.listify(indices)<EOL>neighs = []<EOL>for i in indices:<EOL><INDENT>rij = self.pos[i] - self.pos<EOL>dist = np.sqrt((rij**<NUM_LIT:2>).sum(axis=-<NUM_LIT:1>))<EOL>neighs.append(np.arange(self.N)[dist <= cutoff])<EOL><DEDENT>return neighs<EOL>

Returns neighbors within a cutoff for certain particles


def track(pos, rad, maxdisp=<NUM_LIT>, threshold=None):

threshold = threshold or len(pos)<EOL>z,y,x,a,f = [],[],[],[],[]<EOL>for i,(p,r) in enumerate(zip(pos, rad)):<EOL><INDENT>z.extend(p[:,<NUM_LIT:0>].tolist())<EOL>y.extend(p[:,<NUM_LIT:1>].tolist())<EOL>x.extend(p[:,<NUM_LIT:2>].tolist())<EOL>a.extend(r.tolist())<EOL>f.extend([i]*r.shape[<NUM_LIT:0>])<EOL><DEDENT>df = DataFrame({'<STR_LIT:x>': x, '<STR_LIT:y>': y, '<STR_LIT:z>': z, '<STR_LIT>': a,'<STR_LIT>': f})<EOL>linked = tp.filter_stubs(<EOL>tp.link_df(df, maxdisp, pos_columns=['<STR_LIT:x>','<STR_LIT:y>','<STR_LIT:z>']),<EOL>threshold=threshold<EOL>)<EOL>return linked<EOL>

Tracks a set of positions and radii using trackpy.

Accepts positions and radii as a list of frames in the shape [N, D] where
N in the number of particles (can vary by frame) and D the number of
dimensions. Returns a linked pandas.DataFrame from trackpy.link_df that
is filtered py trackpy.filter_stubs.

Paramters
---------
    pos : list
        N-element list of numpy.ndarrays of the particle positions,
        each of shape [ni, d] where ni is the number of particles in
        that frame and d the number of positional dimensions.
    rad : list
        N-element list of numpy.ndarrays of the particle radii, each
        of shape [ni]
    maxdisp : Float, optional
        The maximum displacement of a particle from frame-to-frame.
        Default is 2.
    threshold : Float or None, optional
        A threshold to filter out trajectories of particles appearing
        in very few frames. Default is None.

Returns
-------
    linked : `pandas.DataFrame`
        Tracked set of particle positions and radii.

See Also
--------
    `trackpy.locate`
    `trackpy.filter_stubs`


ind = df['<STR_LIT>'] == particle_ind<EOL>rads = np.array(df['<STR_LIT>'][ind].copy())<EOL>z = np.array(df['<STR_LIT:z>'][ind].copy())<EOL>x = np.array(df['<STR_LIT:x>'][ind].copy())<EOL>y = np.array(df['<STR_LIT:y>'][ind].copy())<EOL>return x,y,z,rads<EOL>

Returns the x,y,z, radii values of a dataframe for a given particle index.

Parameters
----------
    df : DataFrame
    particle_ind : Int
Returns
-------
    x, y, z, rads : numpy.ndarray


def msd(df, maxlag, pxsize=<NUM_LIT>, scantime=<NUM_LIT:0.1>):

drift = tp.compute_drift(df, pos_columns=['<STR_LIT:x>','<STR_LIT:y>','<STR_LIT:z>'])<EOL>tm = tp.subtract_drift(df, drift)<EOL>em = tp.emsd(tm, pxsize, scantime, max_lagtime=maxlag, pos_columns=['<STR_LIT:x>','<STR_LIT:y>','<STR_LIT:z>'])<EOL>return em<EOL>

Calculates the mean-square displacement of a dataframe using trackpy


def calculate_state_radii_fluctuations(state_list, inbox=True, fullinbox=True,<EOL>inboxrad=False, maxdisp=<NUM_LIT>, threshold=None, return_all=False):

pos, rad = [], []<EOL>for s in state_list:<EOL><INDENT>m = analyze.good_particles(s, inbox=inbox, inboxrad=inboxrad,<EOL>fullinbox=fullinbox)<EOL>pos.append(s.obj_get_positions()[m])<EOL>rad.append(s.obj_get_radii()[m])<EOL><DEDENT>df = track(pos, rad, maxdisp=maxdisp, threshold=threshold)<EOL>zyxr_t = []<EOL>for i in np.unique(df['<STR_LIT>']):<EOL><INDENT>x,y,z,r = get_xyzr_t(df, <NUM_LIT:1>*i)<EOL>if z.size > <NUM_LIT:1>:<EOL><INDENT>zyxr_t.append([x.mean(), y.mean(), z.mean(), r.mean(), r.std()])<EOL><DEDENT><DEDENT>z, y, x, rm, rs = np.transpose(zyxr_t)<EOL>if return_all:<EOL><INDENT>return z, y, x, rm, rs<EOL><DEDENT>else:<EOL><INDENT>return rs<EOL><DEDENT>

Calculates the radii fluctuations throughout a series of images.

Parameters
----------
    state_list : iterable
        List or any iterator/generator of peri.states objects to analyze.

    inbox : Bool
        Set to False to include all particles, not just the ones in the
        image. Default is True (only particles in image).
    fullinbox : Bool
        Set to True to only include particles which are
    inboxrad : Bool
        Set to True to include all particles which overlap at all with the
        image. Default False

    maxdisp : Float
        The maximum displacement a particle can do between frames.
    threshold : Int
        Threshold for trackpy.filter_stubs

    return_all : Bool
        Set to True to return the mean z,y,x,r for all the particles
        as well as r.std(). Default is False (just returns r.std()

Returns
-------
    [z,] : np.ndarray
        The mean z-positions of each particle. Returned if `return_all`
        is True.
    [y,] : np.ndarray
        The mean y-positions of each particle. Returned if `return_all`
        is True.
    [x,] : np.ndarray
        The mean x-positions of each particle. Returned if `return_all`
        is True.
    [rm,] : np.ndarray
        The mean radii of each particle. Returned if `return_all` is
        True.
    rs : np.ndarray
        The standard deviation of each particle's radius across all
        states.


x, y, z, r, t = [[] for a in range(<NUM_LIT:5>)]<EOL>frame = <NUM_LIT:0><EOL>for nm in filenames:<EOL><INDENT>this_pos, this_rad = analyze.parse_json(nm, **kwargs)<EOL>z.extend(this_pos[:,<NUM_LIT:0>])<EOL>y.extend(this_pos[:,<NUM_LIT:1>])<EOL>x.extend(this_pos[:,<NUM_LIT:2>])<EOL>r.extend(this_rad)<EOL>t.extend(np.zeros(this_rad.size) + frame)<EOL>frame += <NUM_LIT:1><EOL><DEDENT>df = DataFrame({'<STR_LIT:x>':x, '<STR_LIT:y>':y, '<STR_LIT:z>':z, '<STR_LIT:a>':r, '<STR_LIT>':t})<EOL>return df<EOL>

Transforms a group of json dictionaries into a single DataFrame.

Parameters
----------
    filenames : Iterable
        A list or other iterable/generator of filenames to load, time
        ordered.

Other Parameters
----------------
    inbox : Bool, optional
        Whether to only return particles inside the image. Requires a
        key ``'image.tile'`` in the saved json dictionary. Default is
        True.
    inboxrad : Bool, optional
        Whether to only return particles that at least partially
        overlap the image. Requires a key ``'image.tile'`` in the saved
        json dictionary. Default is False.
    fullinbox : Bool, optional
        Whether to only return particles completely inside the image.
        Requires a key ``'image.tile'`` in the saved json dictionary.
        Default is False.

Returns
-------
    ``pandas.DataFrame``
        Trackable ``DataFrame`` of the positions and radii, with keys
        ``'x'``, ``'y'``, ``'z'``, ``'a'``, and ``'frame'``

See Also
--------
    ``peri.test.analyze.parse_json``


from peri.test import analyze<EOL>f0 = analyze.sorted_files(start_prefix, return_num=True)<EOL>n0 = [t[-<NUM_LIT:2>] for t in f0]<EOL>f1 = analyze.sorted_files(end_prefix, return_num=True)<EOL>n1 = [t[-<NUM_LIT:2>] for t in f1]<EOL>nums = list(set(n0).difference(set(n1)))<EOL>if len(nums) > <NUM_LIT:0>:<EOL><INDENT>next_num = min(nums)<EOL>next_file = [t[-<NUM_LIT:1>] for t in f0 if t[-<NUM_LIT:2>] == next_num][<NUM_LIT:0>]<EOL>return next_file<EOL><DEDENT>return None<EOL>

Given two globs, figure out the the difference in the file lists to
determine which job to run next


def watch(func, file_pattern, postfix='<STR_LIT>'):

import argparse<EOL>parser = argparse.ArgumentParser(description="<STR_LIT>")<EOL>parser.add_argument('<STR_LIT>', '<STR_LIT>', type=int, help="<STR_LIT>")<EOL>args = vars(parser.parse_args())<EOL>proc = int(args.get('<STR_LIT>') or <NUM_LIT:1>)<EOL>def _watch(func, file_pattern, postfix, index):<EOL><INDENT>sys.stdout = open('<STR_LIT>'.format(index), "<STR_LIT:w>", buffering=<NUM_LIT:0>)<EOL>sys.stderr = open('<STR_LIT>'.format(index), "<STR_LIT:w>", buffering=<NUM_LIT:0>)<EOL>def mark_done(f, postfix):<EOL><INDENT>return '<STR_LIT>'.format(f, postfix)<EOL><DEDENT>def next_file():<EOL><INDENT>return get_next_job(<EOL>file_pattern,  mark_done(file_pattern, postfix)<EOL>)<EOL><DEDENT>filename = next_file()<EOL>while filename:<EOL><INDENT>open(mark_done(filename, postfix), '<STR_LIT:w>').close()<EOL>func(filename)<EOL>filename = next_file()<EOL><DEDENT><DEDENT>log.info('<STR_LIT>')<EOL>for i in range(proc):<EOL><INDENT>log.info('<STR_LIT:{}>'.format(i))<EOL>Process(target=_watch, args=(func, file_pattern, postfix, i)).start()<EOL><DEDENT>log.info('<STR_LIT:.>')<EOL>

Watch the filesystem for files that match `file_pattern` and run the ones
that haven't been completed yet with function `func`


def launch_watchers(script, hosts, nprocs=<NUM_LIT:1>):

def launch(script, host):<EOL><INDENT>cmd = '<STR_LIT>'.format(script, nprocs)<EOL>log = '<STR_LIT>'<EOL>sub = '<STR_LIT>'.format(cmd=cmd, log=log)<EOL>ssh = sub if host == LOCALHOST else '<STR_LIT>'.format(name=host, sub=sub)<EOL>check_output(ssh, shell=True)<EOL><DEDENT>procs = {}<EOL>log.info('<STR_LIT>')<EOL>for i, host in enumerate(hosts):<EOL><INDENT>log.info('<STR_LIT>'.format(host))<EOL>procs[i] = Process(target=launch, args=(script, host))<EOL>procs[i].start()<EOL><DEDENT>def signal_handler():<EOL><INDENT>log.info("<STR_LIT>")<EOL>for p in procs.values():<EOL><INDENT>p.join(timeout=<NUM_LIT>)<EOL><DEDENT>sys.exit(<NUM_LIT:1>)<EOL><DEDENT>try:<EOL><INDENT>while True:<EOL><INDENT>for i in procs.keys():<EOL><INDENT>procs[i].join(timeout=<NUM_LIT:1.0>)<EOL><DEDENT><DEDENT><DEDENT>except (KeyboardInterrupt, SystemExit):<EOL><INDENT>signal_handler()<EOL><DEDENT>

Launch the watch script on a bunch of workers which have uniform environment
and filesystem for example via EC2 or any group of computers sharing home
filesystem.

Parameters:
-----------
script : string
    The filename of the script to run

hosts : list of strings
    Names of the hosts on which to launch the watcher processes

nprocs : integer
    Number of processes per host to run


import argparse<EOL>import beanstalkc as bean<EOL>parser = argparse.ArgumentParser(description="<STR_LIT>")<EOL>parser.add_argument('<STR_LIT>', '<STR_LIT>', type=int, help="<STR_LIT>")<EOL>parser.add_argument('<STR_LIT>', '<STR_LIT>', type=int, help="<STR_LIT>")<EOL>args = vars(parser.parse_args())<EOL>port = int(args.get('<STR_LIT:port>') or DEFAULT_BEANSTALKD)<EOL>proc = int(args.get('<STR_LIT>') or <NUM_LIT:1>)<EOL>def _listen(func, index):<EOL><INDENT>sys.stdout = open('<STR_LIT>'.format(index), "<STR_LIT:w>", buffering=<NUM_LIT:0>)<EOL>sys.stderr = open('<STR_LIT>'.format(index), "<STR_LIT:w>", buffering=<NUM_LIT:0>)<EOL>bsd = bean.Connection(host=LOCALHOST, port=port)<EOL>while True:<EOL><INDENT>job = bsd.reserve()<EOL>func(json.loads(job.body))<EOL><DEDENT><DEDENT>log.info('<STR_LIT>')<EOL>for i in range(proc):<EOL><INDENT>log.info('<STR_LIT:{}>'.format(i))<EOL>Process(target=_listen, args=(func,i)).start()<EOL><DEDENT>

This function is for use in external scripts in order to listen on the
queue and perform tasks. In particular, create a function which takes the
arguments you would pass to `launch_all`s jobs parameter, then at the
bottom of the script add:

if __name__ == '__main__':
    peri.test.beanstalk.listen(my_function_name)


def launch_all(script, hosts, jobs, bean_port=DEFAULT_BEANSTALKD, docopy=True):

import beanstalkc as bean<EOL>def beanstalk(bean_port=DEFAULT_BEANSTALKD):<EOL><INDENT>bean = '<STR_LIT>'.format(LOCALHOST, bean_port)<EOL>check_output(bean, shell=True)<EOL><DEDENT>def copy_script(script, host):<EOL><INDENT>name = host.get('<STR_LIT:host>')<EOL>fldr = host.get('<STR_LIT>', '<STR_LIT>')<EOL>proc = host.get('<STR_LIT>', <NUM_LIT:1>)<EOL>env = host.get('<STR_LIT>', {})<EOL>tmpfile = '<STR_LIT>'.format(os.path.join(fldr, uuid.uuid4().hex))<EOL>ssh = '<STR_LIT>'.format(<EOL>script=script, name=name, tmpfile=tmpfile<EOL>)<EOL>check_output(ssh, shell=True)<EOL>return tmpfile<EOL><DEDENT>def sshtunnel(host, bean_port=DEFAULT_BEANSTALKD):<EOL><INDENT>ssh = '<STR_LIT>'.format(forward=fwd, name=name)<EOL>check_output(ssh, shell=True)<EOL><DEDENT>def launch(script, host, bean_port=DEFAULT_BEANSTALKD, index=<NUM_LIT:0>):<EOL><INDENT>name = host.get('<STR_LIT:host>')<EOL>fldr = host.get('<STR_LIT>', '<STR_LIT>')<EOL>proc = host.get('<STR_LIT>', <NUM_LIT:1>)<EOL>env = host.get('<STR_LIT>', {})<EOL>var = '<STR_LIT:U+0020>'.join(['<STR_LIT>'.format(k, v, k) for k,v in env.items()])<EOL>env = '<STR_LIT>'.format(var, fldr)<EOL>fwd = '<STR_LIT>'.format(bean_port, bean_port)<EOL>log = '<STR_LIT>'.format(index)<EOL>cmd = '<STR_LIT>'.format(env, script, bean_port, proc)<EOL>ssh = '<STR_LIT>'.format(forward=fwd, name=name, cmd=cmd, log=log)<EOL>check_output(ssh, shell=True)<EOL><DEDENT>clean_hosts = []<EOL>for h in hosts:<EOL><INDENT>clean_hosts.append({'<STR_LIT:host>': h} if isinstance(h, str) else h)<EOL><DEDENT>hosts = clean_hosts<EOL>procs = {}<EOL>log.info('<STR_LIT>')<EOL>procs[-<NUM_LIT:1>] = Process(target=beanstalk, args=(bean_port,))<EOL>procs[-<NUM_LIT:1>].start()<EOL>time.sleep(<NUM_LIT:1>)<EOL>log.info('<STR_LIT>')<EOL>for i, host in enumerate(hosts):<EOL><INDENT>log.info('<STR_LIT>'.format(host))<EOL>o = len(hosts)<EOL>tmpscript = script if not docopy else copy_script(script, host)<EOL>procs[i] = Process(target=launch, args=(tmpscript, host, bean_port, i))<EOL>procs[i].start()<EOL><DEDENT>time.sleep(<NUM_LIT:10>)<EOL>def signal_handler():<EOL><INDENT>log.info("<STR_LIT>")<EOL>for p in procs.values():<EOL><INDENT>p.join(timeout=<NUM_LIT>)<EOL><DEDENT>sys.exit(<NUM_LIT:1>)<EOL><DEDENT>log.info('<STR_LIT>')<EOL>bsd = bean.Connection(host=LOCALHOST, port=bean_port, connect_timeout=<NUM_LIT:10>)<EOL>for job in jobs:<EOL><INDENT>log.info('<STR_LIT>'.format(job))<EOL>bsd.put(json.dumps(job))<EOL><DEDENT>try:<EOL><INDENT>while True:<EOL><INDENT>for i in procs.keys():<EOL><INDENT>procs[i].join(timeout=<NUM_LIT:1.0>)<EOL><DEDENT><DEDENT><DEDENT>except (KeyboardInterrupt, SystemExit):<EOL><INDENT>signal_handler()<EOL><DEDENT>

Launch a group of workers spread across a group of machines which listen on
a beanstalkd queue and perform tasks. This main process controls the others
by launching the script through ssh and killing them when this main process
is killed. Note that this launch happens over ssh, so it is recommended
that you have ssh-agent running to avoid a ridiculous number of password
entries.

Parameters:
-----------
script : string
    The filename of the script to run

hosts : list of dictionaries or list of strings
    Each entry in the list should describe a host, containing the keys:
        "host" : either user@host or host (no default)
        "proc" : number of processes to launch (default 1)
        "fldr" : working directory (default /tmp)
        "env" : extra environment variables which need to be set
    If list of strings, is simply hostnames

bean_port : int
    beanstalk port which to open

docopy : boolean
    whether to remotely copy the script to the new machine


def __init__(self, N=<NUM_LIT>, phi=<NUM_LIT>, radius=<NUM_LIT>, polydispersity=<NUM_LIT:0.0>, beta=<NUM_LIT:1>,<EOL>epsilon=<NUM_LIT>, T=<NUM_LIT:1>, dt=<NUM_LIT>, dim=<NUM_LIT:3>, box_side_ratio=<NUM_LIT:1.0>):

self.N = N<EOL>self.phi = phi<EOL>self.beta = beta<EOL>self.epsilon = epsilon<EOL>self.T = T<EOL>self.dt = dt<EOL>self.radius = radius<EOL>self.polydispersity = polydispersity<EOL>self.box_side_ratio = box_side_ratio<EOL>self.dim = int(dim)<EOL>if self.dim == <NUM_LIT:2>:<EOL><INDENT>self.box_side = (self.N*np.pi*self.radius**<NUM_LIT:2> / self.phi)**(<NUM_LIT:1.>/<NUM_LIT:2>)<EOL>self.box = np.array([self.box_side]*self.dim)<EOL><DEDENT>if self.dim == <NUM_LIT:3>:<EOL><INDENT>self.box_side = (self.N*<NUM_LIT>/<NUM_LIT:3>*np.pi*self.radius**<NUM_LIT:3> / self.phi)**(<NUM_LIT:1.>/<NUM_LIT:3>)<EOL>sxy = self.box_side/np.sqrt(self.box_side_ratio)<EOL>sz  = self.box_side*self.box_side_ratio<EOL>self.box = np.array([sz, sxy, sxy])<EOL><DEDENT>self.init_random()<EOL>

Creates a simulation of soft sphere particles. By default, creates
particles in a random uniform (perhaps overlapping) distribution of
particles inside a box dimension `dim` given a packing fraction

Parameters:
-----------
N : integer [default: 500]
    the number of particles in the simulation

phi : float
    packing fraction to use while initializing the particles

radius : float [default: 5]
    mean radius of the particles, see polydispersity

polydispersity : float [default: 0]
    relative polydispersity goal sqrt(<(a-<a>)^2>)/<a>

beta : float
    damping parameter f = -beta v

epsilon : float
    force constant for the soft-sphere potential f = \epsilon (1-d/d_0)^{3/2}

T : float
    temperature of the system

dt : float
    timestep for the integrator

dim : integer [default: 3]
    number of dimensions for the simulation

box_side_ratio : float [default: 1]
    ratio of the box's z height to original z height before scaling.
    therefore, you can squish the box by half (while elongating the
    sides) by setting box_side_ratio=0.5


self.pos = self.box[None,:]*np.random.rand(self.N,self.dim)<EOL>self.rad = self.radius*(<NUM_LIT:1> + self.polydispersity*np.random.randn(self.N))<EOL>self.rad = np.clip(self.rad, <NUM_LIT>, <NUM_LIT>)<EOL>self.vel = <NUM_LIT:0>*self.pos<EOL>

Place the current particles into a stationary, random distribution
and create a polydispersity for the radii


return - self.beta * self.vel<EOL>


coeff = np.sqrt(<NUM_LIT:2>*self.T*self.beta/self.dt)<EOL>return coeff * np.random.randn(*self.pos.shape)<EOL>

Calculate the effective force of the Langevin dynamics


for i in range(self.dim):<EOL><INDENT>mask = (self.pos[:,i] < <NUM_LIT:0>)<EOL>self.pos[mask,i] = <NUM_LIT:2>*<NUM_LIT:0>-self.pos[mask,i]<EOL>self.vel[mask,i] *= -<NUM_LIT:1><EOL>mask = (self.pos[:,i] > self.box[i])<EOL>self.pos[mask,i] = <NUM_LIT:2>*self.box[i]-self.pos[mask,i]<EOL>self.vel[mask,i] *= -<NUM_LIT:1><EOL><DEDENT>

Apply hard reflective boundary conditions to particles


self.vel += forces*self.dt<EOL>self.pos += self.vel*self.dt<EOL>

Integrate the equations of motion. For this simple integrator, we are
using the simplest sympletic integrator, NSV where

    v_{n+1} = v_n + f*dt
    x_{n+1} = x_n + v_{n+1}*dt

Parameters:
-----------
forces : ndarray[N,dim]
    the forces on each particle


for step in range(steps):<EOL><INDENT>self.forces = self.force_hertzian() + self.force_damp() + self.force_noise()<EOL>self.integrate(self.forces)<EOL>self.boundary_condition()<EOL><DEDENT>

Perform a set of integration / BC steps including finite temperature

Parameters:
-----------
steps : int
    number of time steps of size self.dt to perform


for step in range(steps):<EOL><INDENT>self.forces = self.force_hertzian() + self.force_damp()<EOL>self.integrate(self.forces)<EOL>self.boundary_condition()<EOL><DEDENT>

Relax the current configuration using just pair wise forces (no noise)


N = self.N<EOL>pos, rad = self.pos, self.rad<EOL>f = np.zeros_like(pos)<EOL>for i in range(N):<EOL><INDENT>rij = pos[i] - pos<EOL>dij = rad[i] + rad<EOL>dist = np.sqrt((rij**<NUM_LIT:2>).sum(axis=-<NUM_LIT:1>))<EOL>mask = (dist > <NUM_LIT:0>)&(dist < dij)<EOL>rij = rij[mask]<EOL>dij = dij[mask][:,None]<EOL>dist = dist[mask][:,None]<EOL>if len(rij) > <NUM_LIT:0>:<EOL><INDENT>forces = self.epsilon*(<NUM_LIT:1>-dist/dij)**<NUM_LIT:2> * rij/dist<EOL>f[i] += forces.sum(axis=<NUM_LIT:0>)<EOL><DEDENT><DEDENT>return f<EOL>

Return the force on all spheres, do naive all-all interaction, so as
to not introduce new dependencies in the code (like anaconda jit)


def create_state(image, pos, rad, slab=None, sigma=<NUM_LIT>, conf=conf_simple):

<EOL>if not hasattr(rad, '<STR_LIT>'):<EOL><INDENT>rad = rad*np.ones(pos.shape[<NUM_LIT:0>])<EOL><DEDENT>model = models.models[conf.get('<STR_LIT>')]()<EOL>components = []<EOL>for k,v in iteritems(conf.get('<STR_LIT>', {})):<EOL><INDENT>args = conf.get('<STR_LIT:args>').get(k, {})<EOL>comp = model.registry[k][v](**args)<EOL>components.append(comp)<EOL><DEDENT>sphs = objs.PlatonicSpheresCollection(pos, rad)<EOL>if slab is not None:<EOL><INDENT>sphs = ComponentCollection([sphs, objs.Slab(zpos=slab+pad)], category='<STR_LIT>')<EOL><DEDENT>components.append(sphs)<EOL>s = states.ImageState(image, components, sigma=sigma)<EOL>if isinstance(image, util.NullImage):<EOL><INDENT>s.model_to_data()<EOL><DEDENT>return s<EOL>

Create a state from a blank image, set of pos and radii

Parameters:
-----------
image : `peri.util.Image` object
    raw confocal image with which to compare.

pos : initial conditions for positions (in raw image coordinates)
rad : initial conditions for radii array (can be scalar)
sigma : float, noise level

slab : float
    z-position of the microscope slide in the image (pixel units)


def create_many_particle_state(imsize=None, N=None, phi=None, radius=<NUM_LIT>,<EOL>polydispersity=<NUM_LIT:0.0>, seed=None, **kwargs):

_seed_or_not(seed)<EOL>if imsize is not None:<EOL><INDENT>imsize = _toarr(imsize)<EOL>tile = util.Tile(imsize)<EOL><DEDENT>else:<EOL><INDENT>tile = None<EOL><DEDENT>pos, rad, tile = nbody.create_configuration(<EOL>N, tile, radius=radius, phi=phi, polydispersity=polydispersity<EOL>)<EOL>s = create_state(util.NullImage(shape=tile.shape), pos, rad, **kwargs)<EOL>if isinstance(s.get('<STR_LIT>'), ilms.BarnesStreakLegPoly2P1D):<EOL><INDENT>ilm = s.get('<STR_LIT>')<EOL>ilm.randomize_parameters()<EOL>s.reset()<EOL>s.model_to_data(s.sigma)<EOL><DEDENT>return s<EOL>

Creates a random packing of spheres and generates the state. In order to
specify the state, either (phi, imsize) or (N, phi) or (N, imsize) must be
given. Any more than that and it would be over-specified.

Parameters:
-----------
imsize : tuple, array_like, or integer
    the unpadded image size to fill with particles

N : integer
    number of particles

phi : float
    packing fraction

radius : float
    radius of particles to add

N : integer
    Number of particles

seed : integer
    set the seed if desired

*args, **kwargs : see create_state


def create_single_particle_state(imsize, radius=<NUM_LIT>, seed=None, **kwargs):

_seed_or_not(seed)<EOL>imsize = _toarr(imsize)<EOL>pos = imsize.reshape(-<NUM_LIT:1>,<NUM_LIT:3>)/<NUM_LIT><EOL>rad = radius<EOL>return create_state(util.NullImage(shape=imsize), pos, rad, **kwargs)<EOL>

Creates a single particle state

Parameters:
-----------
imsize : tuple, array_like, or integer
    the unpadded image size to fill with particles

radius : float
    radius of particles to add

seed : integer
    set the seed if desired

*args, **kwargs : see create_state


def create_two_particle_state(imsize, radius=<NUM_LIT>, delta=<NUM_LIT:1.0>, seed=None, axis='<STR_LIT:x>', **kwargs):

_seed_or_not(seed)<EOL>imsize = _toarr(imsize)<EOL>comp = {'<STR_LIT:x>': <NUM_LIT:2>, '<STR_LIT:y>': <NUM_LIT:1>, '<STR_LIT:z>': <NUM_LIT:0>}<EOL>t = float(radius)+float(delta)/<NUM_LIT:2><EOL>d = np.array([<NUM_LIT:0.0>, <NUM_LIT:0.0>, <NUM_LIT:0.0>])<EOL>d[comp[axis]] = t<EOL>pos = np.array([imsize/<NUM_LIT> - d, imsize/<NUM_LIT> + d]).reshape(-<NUM_LIT:1>,<NUM_LIT:3>)<EOL>rad = np.array([radius, radius])<EOL>return create_state(util.NullImage(shape=imsize), pos, rad, **kwargs)<EOL>

Creates a two particle state

Parameters:
-----------
imsize : tuple, array_like, or integer
    the unpadded image size to fill with particles

radius : float
    radius of particles to add

delta : float
    separation between the two particles

seed : integer
    set the seed if desired

*args, **kwargs : see create_state


def sorted_files(globber, num_sort=True, num_indices=None, return_num=False):

files = glob.glob(globber)<EOL>files.sort()<EOL>if not num_sort:<EOL><INDENT>return files<EOL><DEDENT>num_indices = num_indices or np.s_[:]<EOL>allfiles = []<EOL>for fn in files:<EOL><INDENT>nums = re.findall(r'<STR_LIT>', fn)<EOL>data = [int(n) for n in nums[num_indices]] + [fn]<EOL>allfiles.append(data)<EOL><DEDENT>allfiles = sorted(allfiles)<EOL>if return_num:<EOL><INDENT>return allfiles<EOL><DEDENT>return [f[-<NUM_LIT:1>] for f in allfiles]<EOL>

Give a globbing expression of files to find. They will be sorted upon return.
This function is most useful when sorting does not provide numerical order,
e.g.:
    9 -> 12 returned as 10 11 12 9 by string sort

In this case use num_sort=True, and it will be sorted by numbers whose index
is given by num_indices (possibly None for all numbers) then by string.


p, r = [], []<EOL>for i in itertools.count():<EOL><INDENT>try:<EOL><INDENT>p.append([d['<STR_LIT>'.format(i, c)] for c in '<STR_LIT>'])<EOL>r.append(d['<STR_LIT>'.format(i)])<EOL><DEDENT>except KeyError:<EOL><INDENT>break<EOL><DEDENT><DEDENT>return np.array(p), np.array(r)<EOL>

Given a dictionary of a states params:values, returns the pos & rad.


od = OrderedDict()<EOL>for p in st.params:<EOL><INDENT>od.update({p:st.state[p]})<EOL><DEDENT>if include_iminfo:<EOL><INDENT>od.update({ '<STR_LIT>':st.image.filename,<EOL>'<STR_LIT>':str(st.image.tile)})<EOL><DEDENT>return od<EOL>

Represents a state as an OrderedDict

    Parameters
    ----------
        st : :class:``peri.states.State``
            The state to represent.
        include_iminfo : Bool, optional
            If set, includes two additional keys, ``'image.filename'`` and
            ``'image.tile'`` with corresponding info about the image.
            Default is True.

    Returns
    -------
        ``collections.OrderedDict``


def save_as_dict(st, save_name, include_iminfo=True, align_text=True):

if align_text:<EOL><INDENT>separators=('<STR_LIT>', '<STR_LIT>')<EOL><DEDENT>else:<EOL><INDENT>separators=('<STR_LIT:U+002CU+0020>', '<STR_LIT>')<EOL><DEDENT>with open(save_name, '<STR_LIT:wb>') as f:<EOL><INDENT>json.dump(state_to_ordereddict(st, include_iminfo=include_iminfo),<EOL>f, separators=separators)<EOL><DEDENT>

Saves a state as a json dict file, in a human-readable order.

    Parameters
    ---------
        st : :class:``peri.states.State``
            The state to save.
        save_name : string
            Complete filename to save as.
        include_iminfo : Bool, optional
            If set, includes two additional keys, ``'image.filename'`` and
            ``'image.tile'`` with corresponding info about the image.
            Default is True.
        align_text : Bool, optional
            Changes json separators to include a newline and tab, to make
            the saved dict easier to read by humans. Default is True.

    See Also
    --------
    state_to_ordereddict
    batch_saveasdict


def batch_saveasdict(load_dir, load_names, save_dir, align_text=True,<EOL>include_iminfo=True):

os.chdir(load_dir)<EOL>for nm in load_names:<EOL><INDENT>save_name = os.path.join(save_dir, nm + '<STR_LIT>')<EOL>try:<EOL><INDENT>st = states.load(nm+'<STR_LIT>')<EOL><DEDENT>except IOError:<EOL><INDENT>log.error('<STR_LIT>'.format(nm))<EOL>continue<EOL><DEDENT>log.error('<STR_LIT>'.format(nm))<EOL>save_as_dict(st, save_name, include_iminfo=include_iminfo, align_text=<EOL>align_text)<EOL><DEDENT>

Batch loads state, transforms to an OrderedDict, saves as a json with
extension ``.json``.

Parameters
---------
    load_dir : String
        The name of the directory to load the states from.
    load_names : Iterable
        Names of the states to load, without the ``.pkl`` extension.
    save_dir: String
        The name of the directory to save the json dicts to.
    align_text : Bool, optional
        Changes json separators to include a newline and tab, to make
        the saved dict easier to read by humans. Default is True.
    include_iminfo : Bool, optional
        If set, includes two additional keys, ``'image.filename'`` and
        ``'image.tile'`` with corresponding info about the image.
        Default is True.


def parse_json(filename, inbox=True, inboxrad=False, fullinbox=False):

dct = json.load(open(filename, '<STR_LIT:rb>'))<EOL>zyxr = []<EOL>for a in itertools.count():<EOL><INDENT>try:<EOL><INDENT>zyxr.append([dct['<STR_LIT>'.format(a, c)] for c in '<STR_LIT>'])<EOL><DEDENT>except KeyError:<EOL><INDENT>break<EOL><DEDENT><DEDENT>zyxr = np.array(zyxr)<EOL>pos = zyxr[:,:<NUM_LIT:3>].copy()<EOL>rad = zyxr[:,-<NUM_LIT:1>].copy()<EOL>if inbox or inboxrad or fullinbox:<EOL><INDENT>try:<EOL><INDENT>imtile = dct['<STR_LIT>']<EOL>ishape_l = eval(imtile.split('<STR_LIT:(>')[-<NUM_LIT:1>].split('<STR_LIT:)>')[<NUM_LIT:0>])<EOL>ishape = np.array(ishape_l)<EOL><DEDENT>except KeyError:<EOL><INDENT>raise KeyError('<STR_LIT>')<EOL><DEDENT>mask = good_particles(None, inbox=inbox, inboxrad=inboxrad,<EOL>fullinbox=fullinbox, pos=pos, rad=rad, ishape=ishape)<EOL>pos = pos[mask].copy()<EOL>rad = rad[mask].copy()<EOL><DEDENT>return pos, rad<EOL>

Parse a json file as saved by batch_saveasdict into positions & radii

Parameters
----------
    filename : String
        The file name of the json dict to load.
    inbox : Bool, optional
        Whether to only return particles inside the image. Requires a
        key ``'image.tile'`` in the saved json dictionary. Default is
        True.
    inboxrad : Bool, optional
        Whether to only return particles that at least partially
        overlap the image. Requires a key ``'image.tile'`` in the saved
        json dictionary. Default is False.
    fullinbox : Bool, optional
        Whether to only return particles completely inside the image.
        Requires a key ``'image.tile'`` in the saved json dictionary.
        Default is False.

Returns
-------
    pos, rad : numpy.ndarray
        The particle positions [N, [z,y,x]] and radii [N,].


def good_particles(state, inbox=True, inboxrad=False, fullinbox=False,<EOL>pos=None, rad=None, ishape=None):

if pos is None:<EOL><INDENT>pos = state.obj_get_positions()<EOL><DEDENT>if rad is None:<EOL><INDENT>rad = state.obj_get_radii()<EOL><DEDENT>mask = rad > <NUM_LIT:0><EOL>if (inbox | inboxrad | fullinbox):<EOL><INDENT>if fullinbox:<EOL><INDENT>mask &= trim_box(state, pos, rad=-rad, ishape=ishape)<EOL><DEDENT>elif inboxrad:<EOL><INDENT>mask &= trim_box(state, pos, rad=rad, ishape=ishape)<EOL><DEDENT>else:<EOL><INDENT>mask &= trim_box(state, pos, rad=None, ishape=ishape)<EOL><DEDENT><DEDENT>return mask<EOL>

Returns a mask of `good' particles as defined by
    * radius > 0
    * position inside box

Parameters
----------
    state : :class:`peri.states.ImageState`
        The state to identify the good particles. If pos, rad, and ishape
        are provided, then this does not need to be passed.
    inbox : Bool
        Whether to only count particle centers within the image. Default
        is True.
    inboxrad : Bool
        Whether to only count particles that overlap the image at all.
        Default is False.
    fullinbox : Bool
        Whether to only include particles which are entirely in the
        image. Default is False
    pos : [3,N] np.ndarray or None
        If not None, the particles' positions.
    rad : [N] element numpy.ndarray or None
        If not None, the particles' radii.
    ishape : 3-element list-like or None
        If not None, the inner region of the state.

Returns
-------
    mask : np.ndarray of bools
        A boolean mask of which particles are good (True) or bad.
See Also
--------
    trim_box


def trim_box(state, p, rad=None, ishape=None):

if ishape is None:<EOL><INDENT>ishape = state.ishape.shape<EOL><DEDENT>if rad is None:<EOL><INDENT>return ((p > <NUM_LIT:0>) & (p < np.array(ishape))).all(axis=-<NUM_LIT:1>)<EOL><DEDENT>return ((p+rad[:,None] > <NUM_LIT:0>) & (p-rad[:,None] < np.array(ishape))).all(axis=-<NUM_LIT:1>)<EOL>

Returns particles within the image.

If rad is provided, then particles that intersect the image at all
(p-r) > edge are returned.

Parameters
----------
    state : :class:`peri.states.ImageState`
        The state to analyze.
    p : numpy.ndarray
        The particle positions
    rad : numpy.ndarray or None, optional
        Set to a numpy.ndarray to include all particles within `rad`
        of the image edge. Default is None, only including particles
        within the image.
    ishape : list-like or None, optional
        3-element list of the region of the interior image. Default is
        None, which uses state.ishape.shape (the interior image shape)

Returns
-------
    numpy.ndarray
        Boolean mask, True for indices of good particle positions.

See Also
--------
    good_particles


ind0, ind1 = [], []<EOL>for i in range(len(p0)):<EOL><INDENT>dist = np.sqrt(((p0[i] - p1)**<NUM_LIT:2>).sum(axis=-<NUM_LIT:1>))<EOL>if cutoff is None:<EOL><INDENT>ind1.append(dist.argmin())<EOL><DEDENT>elif dist.min() < cutoff:<EOL><INDENT>ind0.append(i)<EOL>ind1.append(dist.argmin())<EOL><DEDENT><DEDENT>if cutoff is None:<EOL><INDENT>return ind1<EOL><DEDENT>return ind0, ind1<EOL>

Correlate closest particles with each other (within cutoff).

Returns ind0, ind1 so that p0[ind0] is close to p1[ind1].

Parameters
----------
    p0, p1 : numpy.ndarray
        The particle positions.
    cutoff : Float or None, optional
        If not None, only returns particle indices with distance less
        than `cutoff`. Default is None.

Returns
-------
    ind0, ind1 : List
        The lists of particle indices, p0[ind0] is close to p1[ind1].


def gofr(pos, rad, zscale, diameter=None, resolution=<NUM_LIT>, rmax=<NUM_LIT:10>, method='<STR_LIT>',<EOL>normalize=None, mask_start=None, phi_method='<STR_LIT>', phi=None, state=None):

diameter = diameter or <NUM_LIT:2>*rad.mean()<EOL>vol_particle = <NUM_LIT>/<NUM_LIT:3>*np.pi*(diameter)**<NUM_LIT:3><EOL>if phi_method == '<STR_LIT>':<EOL><INDENT>phi = phi or <NUM_LIT:1><EOL><DEDENT>if phi_method == '<STR_LIT:state>':<EOL><INDENT>phi = packing_fraction_state(state)<EOL><DEDENT>num_density = phi / vol_particle<EOL>if method == '<STR_LIT>':<EOL><INDENT>normalize = normalize or True<EOL>o = gofr_normal(pos, rad, zscale)<EOL>rmin = <NUM_LIT:0><EOL><DEDENT>if method == '<STR_LIT>':<EOL><INDENT>normalize = normalize or False<EOL>o = diameter*gofr_surfaces(pos, rad, zscale)<EOL>rmin = -<NUM_LIT:1><EOL><DEDENT>bins = np.linspace(rmin, diameter*rmax, diameter*rmax/resolution, endpoint=False)<EOL>y,x = np.histogram(o, bins=bins)<EOL>x = (x[<NUM_LIT:1>:] + x[:-<NUM_LIT:1>])/<NUM_LIT:2><EOL>if mask_start is not None:<EOL><INDENT>mask = x > mask_start<EOL>x = x[mask]<EOL>y = y[mask]<EOL><DEDENT>if normalize:<EOL><INDENT>y = y/(<NUM_LIT:4>*np.pi*x**<NUM_LIT:2>)<EOL><DEDENT>return x/diameter, y/(resolution * num_density * float(len(rad)))<EOL>

Pair correlation function calculation from 0 to rmax particle diameters

method : str ['normal', 'surface']
    represents the gofr calculation method

normalize : boolean
    if None, determined by method, otherwise 1/r^2 norm

phi_method : str ['obj', 'state']
    which data to use to calculate the packing_fraction.
        -- 'pos' : (not stable) calculate based on fractional spheres in
            a cube, do not use

        -- 'const' : the volume fraction is provided by the user via
            the variable phi

        -- 'state' : the volume is calculated by using the platonic sphere
            image of a given state. must provide argument state


def packing_fraction_obj(pos, rad, shape, inner, zscale=<NUM_LIT:1>):

obj = PlatonicSpheresCollection(pos, rad, shape=shape, zscale=zscale)<EOL>return obj.get()[inner].mean()<EOL>

Calculates the packing fraction of a group of spheres.

Operates by creating an accurate, volume-conserving image of spheres
and finding the volume fraction in that image. This correctly deals
with edge-effects.

Parameters
----------
    pos : numpy.ndarray
        [N,3] array of particle positions. Only the ones inside
        shape[inner] are counted.
    rad : numpy.ndarray
        N-element array of particle radii.
    shape : List-like
        3-element list-like of the image shape.
    inner :

Returns
-------
    Float
        The volume fraction


return state.get('<STR_LIT>').get()[state.inner].mean()<EOL>

Calculates the packing fraction of a state.

Parameters
----------
    state : :class:`peri.states.ImageState`

Returns
-------
    Float
        The volume fraction


phi = []<EOL>for p,r in iter_pos_rad(state, samples):<EOL><INDENT>phi.append(packing_fraction(p,r,state=state))<EOL><DEDENT>phi = np.array(phi)<EOL>return phi.mean(axis=<NUM_LIT:0>)[<NUM_LIT:0>], phi.std(axis=<NUM_LIT:0>)[<NUM_LIT:0>]/np.sqrt(len(phi))<EOL>

Calculates the packing fraction of a state with a collection of sampled
positions and radii.

Using a collection of sampled radii alows an estimate of the error in
the packing fraction.

Parameters
----------
    state : :class:`peri.states.ImageState`
    samples : Iterable
        List/iterator/generator of the positions and radii at each
        independent sample. samples[i] = [pos, rad]

Returns
-------
    phi : Float
        The mean volume fraction across all samples.
    err : Float
        The standard error of the mean from the sampling;
        phis.std() / sqrt(len(phis))