def make_sample(outfile, noise=0.0, pixsize=1.0):
    '''Create a sample data file for mpi-mapmaker-test/

    Creates a small rectangular map of a circular Gaussian and scans
    row of detectors across the map in two orientations.

    Optionally add noise.
    '''

    # create a fake map (from a Gaussian)
    nx = 32
    ny = 16
    gaussfwhm = 4
    xx = (np.arange(nx)-nx/2)**2
    yy = (np.arange(ny)-ny/2)**2
    rr = xx[:,np.newaxis] + yy
    gausssig = gaussfwhm / 2.354
    image = np.exp(-rr/2.0/gausssig**2)

    # scan once horizontally and twice vertically
    ndet = 16
    nsamp = 64

    # allocate arrays
    det_x = np.zeros([ndet, nsamp], dtype=np.int32)
    det_y = np.zeros_like(det_x)
    det_s = np.zeros_like(det_x, dtype=np.float64)

    # horizontal scan
    offs = 0
    for i in range(nx):
        for j in range(ndet):
            det_x[j,offs+i] = i
            det_y[j,offs+i] = j
            det_s[j,offs+i] = image[det_x[j,offs+i],det_y[j,offs+i]]

    # first half vertical
    offs += nx
    for i in range(ny):
        for j in range(ndet):
            det_x[j,offs+i] = j
            det_y[j,offs+i] = i
            det_s[j,offs+i] = image[det_x[j,offs+i],det_y[j,offs+i]]

    # second half vertical
    offs += ny
    for i in range(ny):
        for j in range(ndet):
            det_x[j,offs+i] = nx/2+j
            det_y[j,offs+i] = i
            det_s[j,offs+i] = image[det_x[j,offs+i],det_y[j,offs+i]]

    # add noise
    det_s += np.random.randn(ndet, nsamp) * noise

    # create data file
    # initialize data class
    out_data = MapData(outfile, ndet, nsamp)

    # write attributes
    out_data.set_nx(nx)
    out_data.set_ny(ny)
    out_data.set_pixsize(pixsize)

    # set data arrays
    out_data.x[...] = det_x
    out_data.y[...] = det_y
    out_data.s[...] = det_s
    out_data.q[...] = np.zeros([ndet,nsamp], dtype=np.uint32)

    # done
    out_data.close()
def convert_data(infile, outfile, pixsize, verbose=False):
    '''convert data file for use by mpi_makemap

    Convert input data file, as created by Jack Sayer's simulator, to
    format used as input to mpi_makemap. Calculates tangent-plane
    projection and saves pixel position for each sample.

    Arguments:
      input:   name of input file (as created from Jack Sayer's simulator)
      output:  name of output file
      pixsize: size of pixels (in degrees)
      verbose: print progress messages [False]
    '''

    # get data from file
    f = h5py.File(infile)
    det_ra  = f[det_ra_name].value
    det_dec = f[det_dec_name].value
    tel_ra  = f[tel_ra_name].value
    tel_dec = f[tel_dec_name].value
    scannum = f[scannum_name].value

    # check that coordinate system is supported
    coordsys = f[det_ra_name].attrs["coordinate_system"][0]
    if coordsys != "radec":
        sys.stderr.write("error: coordinate system must be 'radec'")
        sys.exit()

    # get data lengths
    nsamp,ndet = f[signal_name].shape

    # use mean telescope pointing as tangent point
    tan_ra  = tel_ra.mean()
    tan_dec = tel_dec.mean()

    # WCS for coordinate transformations
    w = wcs.WCS(naxis=2)
    w.wcs.crpix = [0, 0]
    w.wcs.cdelt = np.array([-pixsize, pixsize])
    w.wcs.crval = [tan_ra, tan_dec]
    w.wcs.ctype = ["RA---TAN", "DEC--TAN"]

    # initialize min/max collectors
    minx =  np.infty
    maxx = -np.infty
    miny =  np.infty
    maxy = -np.infty

    if verbose:
        print "Calculating map extent:"

    # loop over detectors and calculate pixel position to get map range
    for d in range(ndet):
        if verbose:
            print "\tdetector {} of {}".format(d, ndet)

        # project into tangent plane
        x,y = w.wcs_world2pix(tel_ra-det_ra[d]/np.cos(tel_dec*np.pi/180),
                              tel_dec+det_dec[d], 0)

        # convert to integers
        x = np.floor(x)
        y = np.floor(y)
        minx = min(x.min(), minx)
        maxx = max(x.max(), maxx)
        miny = min(y.min(), miny)
        maxy = max(y.max(), maxy)

    # determine map range
    nx = maxx - minx + 1
    ny = maxy - miny + 1

    if verbose:
        print "opening output file '{}'".format(outfile)

    # set up output data file
    out_data = MapData(outfile, ndet, nsamp)
    out_data.set_nx(nx)
    out_data.set_ny(ny)
    out_data.set_pixsize(pixsize)

    if verbose:
        print "writing data to file:"

    # figure out block dimensions (powers of two, assuming blocksize is
    # power of two)
    ndata_block = blocksize / out_data.s.dtype.itemsize
    blocksize_sqrt = np.sqrt(ndata_block)
    ndet_block = np.int(np.exp2(np.floor(np.log2(blocksize_sqrt))))
    ndet_block = min(ndet_block, ndet)
    nsamp_block = np.int(ndata_block / ndet_block)
    nsamp_block = min(nsamp_block, nsamp)

    # loop over data set and write to output
    this_det0 = 0
    while this_det0 < ndet:

        # number of detectors in this block
        this_ndet = min(ndet_block, ndet-this_det0)
        this_det1 = this_det0 + this_ndet

        if verbose:
            print "Read/write block: det {}-{} of {}".format(
                this_det0, this_det1, ndet)

        this_samp0 = 0
        while this_samp0 < nsamp:

            # number of samples in this block
            this_nsamp = min(nsamp_block, nsamp-this_samp0)
            this_samp1 = this_samp0 + this_nsamp

            # create ra/det blocks
            ra_block = tel_ra[this_samp0:this_samp1] - \
                det_ra[this_det0:this_det1,np.newaxis] / \
                np.cos(tel_dec[this_samp0:this_samp1]*np.pi/180)
            dec_block = tel_dec[this_samp0:this_samp1] + \
                det_dec[this_det0:this_det1,np.newaxis]

            x,y = w.wcs_world2pix(ra_block,dec_block, 0)
            x = np.floor(x) - minx
            y = np.floor(y) - miny

            # write to file
            out_data.x[this_det0:this_det1,this_samp0:this_samp1] = x
            out_data.y[this_det0:this_det1,this_samp0:this_samp1] = y
            out_data.s[this_det0:this_det1,this_samp0:this_samp1] = \
                np.transpose(f[signal_name][this_samp0:this_samp1,
                                            this_det0:this_det1])

            # quality (0 -> good when scannum >= 0)
            thisqual = (scannum[this_samp0:this_samp1] < 0).astype(np.uint32)
            out_data.q[this_det0:this_det1,this_samp0:this_samp1] = thisqual

            # increment samp pointer
            this_samp0 = this_samp1

        # increment det pointer
        this_det0 = this_det1

    if verbose:
        print "closing files"

    # done
    f.close()
    out_data.close()