{"action":"created","comment":{"url":"https://api.github.com/repos/GalSim-developers/GalSim/pulls/comments/25573963","id":25573963,"diff_hunk":"@@ -0,0 +1,378 @@\n+# Copyright (c) 2012-2014 by the GalSim developers team on GitHub\n+# https://github.com/GalSim-developers\n+#\n+# This file is part of GalSim: The modular galaxy image simulation toolkit.\n+# https://github.com/GalSim-developers/GalSim\n+#\n+# GalSim is free software: redistribution and use in source and binary forms,\n+# with or without modification, are permitted provided that the following\n+# conditions are met:\n+#\n+# 1. Redistributions of source code must retain the above copyright notice, this\n+#    list of conditions, and the disclaimer given in the accompanying LICENSE\n+#    file.\n+# 2. Redistributions in binary form must reproduce the above copyright notice,\n+#    this list of conditions, and the disclaimer given in the documentation\n+#    and/or other materials provided with the distribution.\n+#\n+\"\"\"\n+Demo #13\n+\n+The thirteenth script in our tutorial about using Galsim in python scripts: examples/demo*.py.\n+(This file is designed to be viewed in a window 100 characters wide.)\n+\n+This script currently doesn't have an equivalent demo*.yaml or demo*.json file.\n+\n+This script introduces non-idealities arising from NIR detectors, in particular those that will be\n+observed and accounted for in the WFIRST surveys. Three such non-ideal effects are demonstrated, in\n+the order in which they are introduced in the detectors:\n+\n+1) Reciprocity failure: Flux-dependent sensitivity of the detector.\n+2) Non-linearity: Charge-dependent gain in converting from units of electrons to ADU.  Non-linearity\n+   in some form is also relevant for CCDs in addition to NIR detectors.\n+3) Interpixel capacitance: Influence of charge in a pixel on the voltage reading of neighboring\n+   ones.\n+\n+The purpose of the demo is two-fold: (1) to show the effects of detector non-idealities on images\n+from NIR detectors, and (2) to illustrate the full image generation process, including all sources\n+of noise at appropriate stages.\n+\n+New features introduced in this demo:\n+- galsim.makeCOSMOSCatalog(...)\n+- galsim.makeCOSMOSObj(...)\n+- Adding sky level and dark current.\n+- image.addReciprocityFailure(exp_time, alpha, base_flux)\n+- image.quantize()\n+- image.applyNonlinearity(NLfunc, *args)\n+- image.applyIPC(IPC_kernel, edge_treatment, fill_value, kernel_nonnegativity,\n+                 kernel_normalization)\n+- readnoise.setReadNoise(readnoise_level)\n+- All functionality in the galsim.wfirst module.\n+\"\"\"\n+\n+import numpy\n+import sys, os\n+import math\n+import logging\n+import time\n+import galsim as galsim\n+import galsim.wfirst as wfirst\n+\n+def main(argv):\n+    # Where to find and output data.\n+    path, filename = os.path.split(__file__)\n+    datapath = os.path.abspath(os.path.join(path, \"data/\"))\n+    outpath = os.path.abspath(os.path.join(path, \"output/\"))\n+\n+    # In non-script code, use getLogger(__name__) at module scope instead.\n+    logging.basicConfig(format=\"%(message)s\", level=logging.INFO, stream=sys.stdout)\n+    logger = logging.getLogger(\"demo13\")\n+\n+    # Initialize (pseudo-)random number generator.\n+    random_seed = 1234567\n+    rng = galsim.BaseDeviate(random_seed)\n+\n+    # Generate a Poisson noise model.\n+    poisson_noise = galsim.PoissonNoise(rng) \n+    logger.info('Poisson noise model created.')\n+\n+    # Read in the WFIRST filters, setting an AB zeropoint appropriate for this telescope given its\n+    # diameter and (since we didn't use any keyword arguments to modify this) using the typical\n+    # exposure time for WFIRST images.\n+    filters = wfirst.getBandpasses(AB_zeropoint=True)\n+    # We care about the NIR imaging, not the prism and grism, so let's remove them from the dict\n+    # containing the filters:\n+    del filters['SNPrism']\n+    del filters['BAO-Grism']\n+    logger.debug('Read in WFIRST imaging filters.')\n+\n+    logger.info('Reading from a parametric COSMOS catalog.')\n+    # Read in a galaxy catalog - just a random subsample of 500 galaxies for F814W<23.5 from COSMOS.\n+    cat_file_name = 'real_galaxy_23.5_500_fits.fits'\n+    dir = 'data'\n+    # Use the routine that can take COSMOS real or parametric galaxy information, and tell it we\n+    # want parametric galaxies.\n+    cat = galsim.makeCOSMOSCatalog(cat_file_name, dir=dir, use_real=False)\n+    logger.info('Read in %d galaxies from catalog'%len(cat))\n+    # Just use a few galaxies, to save time.  Note that we are going to put 4000 galaxy images into\n+    # our big image, so if we have n_use=10, each galaxy will appear 400 times.  Users who want a\n+    # more interesting image with greater variation in the galaxy population can change `n_use` to\n+    # something larger that evenly divides 4000 (but it should be <=500, which is the number of\n+    # galaxies in the catalog).  The code is not set up to handle values of n_use that do not evenly\n+    # divide n_tot, though it would not be very hard to do so.  With 4000 galaxies in a 4k x 4k\n+    # image with the WFIRST pixel scale, the effective galaxy number density is 74/arcmin^2\n+    n_use = 10\n+    n_tot = 4000\n+    n_per_gal = n_tot / n_use\n+\n+    # Here we carry out the initial steps that are necessary to get a fully chromatic PSF.  We use\n+    # the getPSF() routine in the WFIRST module, which knows all about the telescope parameters\n+    # (diameter, bandpasses, obscuration, etc.).  Note that we are going to arbitrarily choose a\n+    # single SCA (Sensor Chip Assembly) rather than all of them, for faster calculations, and we're\n+    # going to use a simple representation of the struts for faster calculations.  To do a more\n+    # exact calculation of the chromaticity and pupil plane configuration, remove the\n+    # `approximate_struts` and the `n_waves` keyword from the call to getPSF():\n+    use_SCA = 7 # This could be any number from 1...18\n+    logger.info('Doing expensive pre-computation of PSF.')\n+    t1 = time.time()\n+    PSFs = wfirst.getPSF(SCAs=use_SCA, approximate_struts=True, n_waves=10)\n+    PSF = PSFs[use_SCA]\n+    t2 = time.time()\n+    logger.info('Done PSF precomputation in %.1f seconds!'%(t2-t1))\n+\n+    # Define the size of the postage stamp that we will use for each individual galaxy within the\n+    # larger image, and for the PSF images.\n+    stamp_size = 128\n+\n+    # We are going to choose a particular (RA, dec) location on the sky for our observation.\n+    ra_targ = 30.*galsim.degrees\n+    dec_targ = -10.*galsim.degrees\n+    targ_pos = galsim.CelestialCoord(ra=ra_targ, dec=dec_targ)\n+    ang = 0.*galsim.degrees\n+    # Get the WCS for an observation at this position, with the focal plane array oriented at an\n+    # angle of `ang` with respect to North.  The output of this routine is a list of WCS objects,\n+    # one for each SCA.  We will then take the WCS for the SCA that we are going to use.\n+    wcs_list = wfirst.getWCS(ang, pos=targ_pos, PA_is_FPA=True)\n+    wcs = wcs_list[use_SCA]\n+    # We need to find the center position for this SCA.  We'll tell it to give us a CelestialCoord\n+    # corresponding to (X, Y) = (wfirst.n_pix/2, wfirst.n_pix/2).\n+    SCA_cent_pos = wcs.toWorld(galsim.PositionD(wfirst.n_pix/2, wfirst.n_pix/2))\n+\n+    # We are going to just randomly distribute points in (X, Y).\n+    # If we had a real galaxy catalog with positions in terms of RA, dec we could use wcs.toImage()\n+    # to find where those objects should be in terms of (X, Y).\n+    pos_rng = galsim.UniformDeviate(random_seed)\n+    # Make a list of (X, Y) values, eliminating the 10% of the edge pixels as the object centroids.\n+    x_stamp = []\n+    y_stamp = []\n+    for i_gal in xrange(n_tot):\n+        x_stamp.append(pos_rng()*0.8*wfirst.n_pix + 0.1*wfirst.n_pix)\n+        y_stamp.append(pos_rng()*0.8*wfirst.n_pix + 0.1*wfirst.n_pix)\n+        # Note that we could use wcs.toWorld() to get the (RA, dec) for these (x, y) positions.  Or,\n+        # if we had started with (RA, dec) positions, we could have used wcs.toImage() to get the\n+        # CCD coordinates for those positions.\n+\n+    # Make the 2-component parametric GSObjects for each object, including chromaticity (roughly\n+    # appropriate SEDs per galaxy component, at the appropriate galaxy redshift).  Note that since\n+    # the PSF is position-independent within the SCA, we can simply do the convolution with that PSF\n+    # now instead of using a different one for each position.\n+    gal_list = []\n+    for i_gal in xrange(n_use):\n+        logger.info('Processing the object at row %d in the input catalog.'%i_gal)\n+\n+        # Use built-in routines for constructing COSMOS objects:\n+        gal = galsim.makeCOSMOSObj(cat, i_gal, chromatic=True)\n+\n+        # Convolve the chromatic galaxy and the chromatic PSF\n+        final = galsim.Convolve(gal, PSF)\n+        logger.debug('Pre-processing for galaxy %d completed.'%i_gal)\n+        gal_list.append(final)\n+\n+    # Calculate the sky level for each filter, and draw the PSF and the galaxies through the\n+    # filters.\n+    for filter_name, filter_ in filters.iteritems():\n+        logger.info('Beginning work for {0}.'.format(filter_name))\n+\n+        # Drawing PSF.  Note that the PSF object intrinsically has a flat SED, so if we\n+        # convolve it with a galaxy, it will properly take on the SED of the galaxy.  However,\n+        # this does mean that the PSF image being drawn here is not quite the right PSF for\n+        # the galaxy.  Indeed, the PSF for the galaxy effectively varies within it, since it\n+        # differs for the bulge and the disk.  To make a real image, one would have to choose SEDs\n+        # for stars and convolve with a star that has a reasonable SED, but we are just going to\n+        # draw with a flat SED for this demo.\n+        out_filename = os.path.join(outpath, 'demo13_PSF_{0}.fits'.format(filter_name))\n+        img_psf = galsim.ImageF(64,64)\n+        PSF.drawImage(bandpass=filter_, image=img_psf, scale=wfirst.pixel_scale)\n+        # Artificially normalize to a total flux of 1 for display purposes.\n+        img_psf /= img_psf.array.sum()\n+        img_psf.write(out_filename)\n+        logger.debug('Created PSF with flat SED for {0}-band'.format(filter_name))\n+\n+        # Set up the full image that will contain all the individual galaxy images, with information\n+        # about WCS:\n+        final_image = galsim.ImageF(wfirst.n_pix,wfirst.n_pix, wcs=wcs)\n+\n+        # Draw the galaxies into the image.\n+        for i_gal in xrange(n_use):\n+            logger.info('Drawing image for the object at row %d in the input catalog'%i_gal)\n+\n+            # We want to only draw the galaxy once (for speed), not over and over with different\n+            # sub-pixel offsets.  For this reason we will ignore the sub-pixel offset entirely.\n+            stamp = galsim.Image(stamp_size, stamp_size)\n+            gal_list[i_gal].drawImage(filter_, image=stamp)\n+\n+            # Have to find where to place it:\n+            for i_gal_use in range(i_gal*n_per_gal, i_gal*(n_per_gal+1)):\n+                # Account for the fractional part of the position:\n+                ix = int(math.floor(x_stamp[i_gal_use]+0.5))\n+                iy = int(math.floor(y_stamp[i_gal_use]+0.5))\n+                # We're not going to actually use this offset.\n+                offset = galsim.PositionD(x_stamp[i_gal]-ix, y_stamp[i_gal]-iy)\n+\n+                # Create a nominal bound for the postage stamp given the integer part of the\n+                # position.\n+                stamp_bounds = galsim.BoundsI(ix-0.5*stamp_size, ix+0.5*stamp_size-1, \n+                                              iy-0.5*stamp_size, iy+0.5*stamp_size-1)\n+                stamp.setOrigin(galsim.PositionI(stamp_bounds.xmin, stamp_bounds.ymin))\n+\n+                # Find the overlapping bounds between the large image and the individual postage\n+                # stamp.\n+                bounds = stamp_bounds & final_image.bounds\n+\n+                # Copy the image into the right place in the big image.  There is a difference in\n+                # normalization between COSMOS images and these that we account for using a\n+                # numerical factor.\n+                final_image[bounds] += stamp[bounds]/0.03**2\n+\n+        # Now we're done with the per-galaxy drawing for this image.  The rest will be done for the\n+        # entire image at once.\n+        logger.info('Postage stamps of all galaxies drawn on a single big image for this filter.')\n+        logger.info('Adding the sky level, noise and detector non-idealities.')\n+\n+        # First we get the amount of zodaical light for a position corresponding to the center of\n+        # this SCA.  Since we have supplied an exposure time, the results will be returned to us in\n+        # e-/pix.  Then we multiply this by a factor >1 to account for the amount of stray light\n+        # that is expected.  Technically one should make a position-dependent sky level, but this\n+        # will be a fairly flat function of position, so using a constant is not too bad.\n+        sky_level_pix = wfirst.getSkyLevel(filters[filter_name], position=SCA_cent_pos,\n+                                           exp_time=wfirst.exptime)\n+        sky_level_pix *= (1.0 + wfirst.stray_light_fraction)\n+        # Finally we add the expected thermal backgrounds in this band.  These are provided in\n+        # e-/pix/s, so we have to multiply by the exposure time.\n+        sky_level_pix += wfirst.thermal_backgrounds[filter_name]*wfirst.exptime\n+        # Adding sky level to the image.  \n+        final_image += sky_level_pix\n+\n+        # Now that all sources of signal (from astronomical objects and background) have been added,\n+        # we can include the expected Poisson noise:\n+        final_image.addNoise(poisson_noise)\n+\n+        # The subsequent steps account for the non-ideality of the detectors.\n+\n+        # 1) Reciprocity failure:\n+        # Reciprocity, in the context of photography, is the inverse relationship between the\n+        # incident flux (I) of a source object and the exposure time (t) required to produce a\n+        # given response(p) in the detector, i.e., p = I*t. However, in NIR detectors, this\n+        # relation does not hold always. The pixel response to a high flux is larger than its\n+        # response to a low flux. This flux-dependent non-linearity is known as 'reciprocity\n+        # failure', and the approximate amount of reciprocity failure for the WFIRST detectors is\n+        # known, so we can include this detector effect in our images.\n+\n+        # Save the image before applying the transformation to see the difference\n+        final_image_orig = final_image.copy()\n+\n+        final_image.addReciprocityFailure(exp_time=wfirst.exptime, alpha=wfirst.reciprocity_alpha,\n+                                          base_flux=1.0)\n+        logger.debug('Included reciprocity failure in {0}-band image'.format(filter_name))\n+        final_image_2 = final_image.copy()\n+        # Isolate the changes due to reciprocity failure.\n+        diff = final_image_2-final_image_orig\n+\n+        out_filename = os.path.join(outpath,'demo13_RecipFail_{0}.fits'.format(filter_name))\n+        final_image_2.write(out_filename)\n+        out_filename = os.path.join(outpath,'demo13_diff_RecipFail_{0}.fits'.format(filter_name))\n+        diff.write(out_filename)\n+\n+        # At this point in the image generation process, an integer number of photons gets\n+        # detected, hence we have to round the pixel values to integers:\n+        final_image.quantize()\n+\n+        # 2) Adding dark current to the image:\n+        # Even when the detector is unexposed to any radiation, the electron-hole pairs that\n+        # are generated within the depletion region due to finite temperature are swept by the\n+        # high electric field at the junction of the photodiode. This small reverse bias\n+        # leakage current is referred to as 'Dark current'. It is specified by the average\n+        # number of electrons reaching the detectors per unit time and has an associated\n+        # Poisson noise since it is a random event.\n+        dark_img = galsim.ImageF(bounds=final_image.bounds,\n+                                 init_value=wfirst.dark_current*wfirst.exptime)\n+        dark_img.addNoise(poisson_noise)\n+        final_image += dark_img\n+\n+        # NOTE: Sky level and dark current might appear like a constant background that can be\n+        # simply subtracted. However, these contribute to the shot noise and matter for the\n+        # non-linear effects that follow. Hence, these must be included at this stage of the \n+        # image generation process. We subtract these backgrounds in the end.\n+\n+        # 3) Applying a quadratic non-linearity:\n+        # In order to convert the units from electrons to ADU, we must multiply the image by a\n+        # gain factor. The gain has a weak dependency on the charge present in each pixel. This\n+        # dependency is accounted for by changing the pixel values (in electrons) and applying\n+        # a constant nominal gain later, which is unity in our demo.\n+\n+        # Save the image before applying the transformation to see the difference:\n+        final_image_3 = final_image.copy()\n+\n+        NLfunc = wfirst.NLfunc        # a quadratic non-linear function\n+        final_image.applyNonlinearity(NLfunc=NLfunc)\n+        logger.debug('Applied nonlinearity to {0}-band image'.format(filter_name))\n+        final_image_4 = final_image.copy()\n+        # Isolate the changes due to non-linear gain.\n+        diff = final_image_4-final_image_3\n+\n+        out_filename = os.path.join(outpath,'demo13_NL_{0}.fits'.format(filter_name))\n+        final_image_4.write(out_filename)\n+        out_filename = os.path.join(outpath,'demo13_diff_NL_{0}.fits'.format(filter_name))\n+        diff.write(out_filename)\n+\n+        # 4) Including Interpixel capacitance:\n+        # The voltage read at a given pixel location is influenced by the charges present in\n+        # the neighboring pixel locations due to capacitive coupling of sense nodes. This\n+        # interpixel capacitance effect is modeled as a linear effect that is described as a\n+        # convolution of a 3x3 kernel with the image. The WFIRST kernel is not normalized to\n+        # have the entries add to unity and hence must be normalized inside the routine.\n+\n+        final_image.applyIPC(IPC_kernel=wfirst.ipc_kernel,edge_treatment='extend',\n+                             kernel_normalization=True)\n+        # Here, we use `edge_treatment='extend'`, which pads the image with zeros before\n+        # applying the kernel. The central part of the image is retained.\n+        logger.debug('Applied interpixel capacitance to {0}-band image'.format(filter_name))\n+        final_image_5 = final_image.copy()\n+        # Isolate the changes due to the interpixel capacitance effect.\n+        diff = final_image_5-final_image_4\n+\n+        out_filename = os.path.join(outpath,'demo13_IPC_{0}.fits'.format(filter_name))\n+        final_image_5.write(out_filename)\n+        out_filename = os.path.join(outpath,'demo13_diff_IPC_{0}.fits'.format(filter_name))\n+        diff.write(out_filename)\n+\n+        # 5) Adding read noise:\n+        # Read noise is the noise due to the on-chip amplifier that converts the charge into an\n+        # analog voltage.\n+        read_noise = galsim.CCDNoise(rng)\n+        read_noise.setReadNoise(wfirst.read_noise)\n+        final_image.addNoise(read_noise)\n+        final_image_6 = final_image.copy()\n+\n+        logger.debug('Added readnoise to {0}-band image'.format(filter_name))\n+\n+        # Technically we have to apply the gain, dividing the signal in e- by the voltage gain\n+        # in e-/ADU to get a signal in ADU.  For WFIRST the gain is expected to be around 1,\n+        # so it doesn't really matter, but for completeness we include this step.","path":"examples/demo13.py","position":351,"original_position":351,"commit_id":"6c0d994af69b4f48c198ceb2d9f714043df8a94e","original_commit_id":"6c0d994af69b4f48c198ceb2d9f714043df8a94e","user":{"login":"rmjarvis","id":623887,"avatar_url":"https://avatars.githubusercontent.com/u/623887?v=3","gravatar_id":"","url":"https://api.github.com/users/rmjarvis","html_url":"https://github.com/rmjarvis","followers_url":"https://api.github.com/users/rmjarvis/followers","following_url":"https://api.github.com/users/rmjarvis/following{/other_user}","gists_url":"https://api.github.com/users/rmjarvis/gists{/gist_id}","starred_url":"https://api.github.com/users/rmjarvis/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rmjarvis/subscriptions","organizations_url":"https://api.github.com/users/rmjarvis/orgs","repos_url":"https://api.github.com/users/rmjarvis/repos","events_url":"https://api.github.com/users/rmjarvis/events{/privacy}","received_events_url":"https://api.github.com/users/rmjarvis/received_events","type":"User","site_admin":false},"body":"Probably shouldn't be so flippant here about the gain when you are being so careful about all these other effects.  Gains for real detectors are almost never exactly 1, even if they aim to make them approximately 1.  Typically gains vary from one amp to another etc.  So the text here should probably be more along the lines of:\r\n\r\nWe divide by the gain to convert from e- to ADU.  Currently, the gain value in the wfirst module is just set to 1, since we don't know what the exact gain will be, although it is expected to be approximately 1.  Eventually, this may change when the camera is finished being assembled, and there may be a different value for each SCA.  For now, there is just a single number, which is equal to 1.","created_at":"2015-03-02T00:20:00Z","updated_at":"2015-03-02T00:20:00Z","html_url":"https://github.com/GalSim-developers/GalSim/pull/651#discussion_r25573963","pull_request_url":"https://api.github.com/repos/GalSim-developers/GalSim/pulls/651","_links":{"self":{"href":"https://api.github.com/repos/GalSim-developers/GalSim/pulls/comments/25573963"},"html":{"href":"https://github.com/GalSim-developers/GalSim/pull/651#discussion_r25573963"},"pull_request":{"href":"https://api.github.com/repos/GalSim-developers/GalSim/pulls/651"}}},"pull_request":{"url":"https://api.github.com/repos/GalSim-developers/GalSim/pulls/651","id":30180104,"html_url":"https://github.com/GalSim-developers/GalSim/pull/651","diff_url":"https://github.com/GalSim-developers/GalSim/pull/651.diff","patch_url":"https://github.com/GalSim-developers/GalSim/pull/651.patch","issue_url":"https://api.github.com/repos/GalSim-developers/GalSim/issues/651","number":651,"state":"open","locked":false,"title":"#590 - WFIRST module","user":{"login":"rmandelb","id":1454906,"avatar_url":"https://avatars.githubusercontent.com/u/1454906?v=3","gravatar_id":"","url":"https://api.github.com/users/rmandelb","html_url":"https://github.com/rmandelb","followers_url":"https://api.github.com/users/rmandelb/followers","following_url":"https://api.github.com/users/rmandelb/following{/other_user}","gists_url":"https://api.github.com/users/rmandelb/gists{/gist_id}","starred_url":"https://api.github.com/users/rmandelb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rmandelb/subscriptions","organizations_url":"https://api.github.com/users/rmandelb/orgs","repos_url":"https://api.github.com/users/rmandelb/repos","events_url":"https://api.github.com/users/rmandelb/events{/privacy}","received_events_url":"https://api.github.com/users/rmandelb/received_events","type":"User","site_admin":false},"body":"This pull request is for the new WFIRST module (#590), though it also addressed #635 (partially) and #614.  It includes work mostly by @arunkannawadi and me, though with some small updates from @rmjarvis and @jmeyers314 as well.\r\n\r\nTo summarize, it includes:\r\n* All software for the WFIRST module, which knows a lot about WFIRST and can be used to simulate its images easily.\r\n* A demo (demo13.py) that demonstrates use of all routines in the WFIRST module, and steps through the image generation process in detail including IR detector effects.  I encourage those who are checking out this PR to run the demo, in addition to reviewing the code and running unit tests.\r\n* Some software to easily make images of galaxies from the COSMOS dataset in either a real (HST image-based) version or a parametric (Sersic-based) version.\r\n* Smaller updates to other routines, like how FITS headers are handled and some sign conventions in the CelestialCoord class.\r\n\r\nTo be honest, I feel the WFIRST module and the new COSMOS catalog routines do not have enough unit tests.  However, a lot of the routines are meant to do things like read in stored information and pass it to the user in some convenient form, so I had a hard time coming up with unit tests for those.  The demo itself should illustrate whether that functionality is still working, so I'm uncertain to what degree I should work harder to make up unit tests vs. planning to make sure demo13 still runs when evaluating future PRs.  Suggestions for useful unit tests would be very welcome, though!  At minimum, I suppose I could just put in unit tests making sure that all the keyword arguments do what they are supposed to.\r\n\r\n@jasondrhodes and @chazshapiro, take a look!","created_at":"2015-02-27T16:37:06Z","updated_at":"2015-03-02T00:20:00Z","closed_at":null,"merged_at":null,"merge_commit_sha":"949e68a12f4b4d9bc3e8411f3b6240a2aee3e90e","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/GalSim-developers/GalSim/pulls/651/commits","review_comments_url":"https://api.github.com/repos/GalSim-developers/GalSim/pulls/651/comments","review_comment_url":"https://api.github.com/repos/GalSim-developers/GalSim/pulls/comments{/number}","comments_url":"https://api.github.com/repos/GalSim-developers/GalSim/issues/651/comments","statuses_url":"https://api.github.com/repos/GalSim-developers/GalSim/statuses/6c0d994af69b4f48c198ceb2d9f714043df8a94e","head":{"label":"GalSim-developers:#590","ref":"#590","sha":"6c0d994af69b4f48c198ceb2d9f714043df8a94e","user":{"login":"GalSim-developers","id":1462196,"avatar_url":"https://avatars.githubusercontent.com/u/1462196?v=3","gravatar_id":"","url":"https://api.github.com/users/GalSim-developers","html_url":"https://github.com/GalSim-developers","followers_url":"https://api.github.com/users/GalSim-developers/followers","following_url":"https://api.github.com/users/GalSim-developers/following{/other_user}","gists_url":"https://api.github.com/users/GalSim-developers/gists{/gist_id}","starred_url":"https://api.github.com/users/GalSim-developers/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/GalSim-developers/subscriptions","organizations_url":"https://api.github.com/users/GalSim-developers/orgs","repos_url":"https://api.github.com/users/GalSim-developers/repos","events_url":"https://api.github.com/users/GalSim-developers/events{/privacy}","received_events_url":"https://api.github.com/users/GalSim-developers/received_events","type":"Organization","site_admin":false},"repo":{"id":3510804,"name":"GalSim","full_name":"GalSim-developers/GalSim","owner":{"login":"GalSim-developers","id":1462196,"avatar_url":"https://avatars.githubusercontent.com/u/1462196?v=3","gravatar_id":"","url":"https://api.github.com/users/GalSim-developers","html_url":"https://github.com/GalSim-developers","followers_url":"https://api.github.com/users/GalSim-developers/followers","following_url":"https://api.github.com/users/GalSim-developers/following{/other_user}","gists_url":"https://api.github.com/users/GalSim-developers/gists{/gist_id}","starred_url":"https://api.github.com/users/GalSim-developers/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/GalSim-developers/subscriptions","organizations_url":"https://api.github.com/users/GalSim-developers/orgs","repos_url":"https://api.github.com/users/GalSim-developers/repos","events_url":"https://api.github.com/users/GalSim-developers/events{/privacy}","received_events_url":"https://api.github.com/users/GalSim-developers/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/GalSim-developers/GalSim","description":"The modular galaxy image simulation toolkit","fork":false,"url":"https://api.github.com/repos/GalSim-developers/GalSim","forks_url":"https://api.github.com/repos/GalSim-developers/GalSim/forks","keys_url":"https://api.github.com/repos/GalSim-developers/GalSim/keys{/key_id}","collaborators_url":"https://api.github.com/repos/GalSim-developers/GalSim/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/GalSim-developers/GalSim/teams","hooks_url":"https://api.github.com/repos/GalSim-developers/GalSim/hooks","issue_events_url":"https://api.github.com/repos/GalSim-developers/GalSim/issues/events{/number}","events_url":"https://api.github.com/repos/GalSim-developers/GalSim/events","assignees_url":"https://api.github.com/repos/GalSim-developers/GalSim/assignees{/user}","branches_url":"https://api.github.com/repos/GalSim-developers/GalSim/branches{/branch}","tags_url":"https://api.github.com/repos/GalSim-developers/GalSim/tags","blobs_url":"https://api.github.com/repos/GalSim-developers/GalSim/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/GalSim-developers/GalSim/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/GalSim-developers/GalSim/git/refs{/sha}","trees_url":"https://api.github.com/repos/GalSim-developers/GalSim/git/trees{/sha}","statuses_url":"https://api.github.com/repos/GalSim-developers/GalSim/statuses/{sha}","languages_url":"https://api.github.com/repos/GalSim-developers/GalSim/languages","stargazers_url":"https://api.github.com/repos/GalSim-developers/GalSim/stargazers","contributors_url":"https://api.github.com/repos/GalSim-developers/GalSim/contributors","subscribers_url":"https://api.github.com/repos/GalSim-developers/GalSim/subscribers","subscription_url":"https://api.github.com/repos/GalSim-developers/GalSim/subscription","commits_url":"https://api.github.com/repos/GalSim-developers/GalSim/commits{/sha}","git_commits_url":"https://api.github.com/repos/GalSim-developers/GalSim/git/commits{/sha}","comments_url":"https://api.github.com/repos/GalSim-developers/GalSim/comments{/number}","issue_comment_url":"https://api.github.com/repos/GalSim-developers/GalSim/issues/comments{/number}","contents_url":"https://api.github.com/repos/GalSim-developers/GalSim/contents/{+path}","compare_url":"https://api.github.com/repos/GalSim-developers/GalSim/compare/{base}...{head}","merges_url":"https://api.github.com/repos/GalSim-developers/GalSim/merges","archive_url":"https://api.github.com/repos/GalSim-developers/GalSim/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/GalSim-developers/GalSim/downloads","issues_url":"https://api.github.com/repos/GalSim-developers/GalSim/issues{/number}","pulls_url":"https://api.github.com/repos/GalSim-developers/GalSim/pulls{/number}","milestones_url":"https://api.github.com/repos/GalSim-developers/GalSim/milestones{/number}","notifications_url":"https://api.github.com/repos/GalSim-developers/GalSim/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/GalSim-developers/GalSim/labels{/name}","releases_url":"https://api.github.com/repos/GalSim-developers/GalSim/releases{/id}","created_at":"2012-02-22T02:51:45Z","updated_at":"2015-01-27T06:46:41Z","pushed_at":"2015-03-01T22:01:01Z","git_url":"git://github.com/GalSim-developers/GalSim.git","ssh_url":"git@github.com:GalSim-developers/GalSim.git","clone_url":"https://github.com/GalSim-developers/GalSim.git","svn_url":"https://github.com/GalSim-developers/GalSim","homepage":"","size":92432,"stargazers_count":29,"watchers_count":29,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":19,"mirror_url":null,"open_issues_count":56,"forks":19,"open_issues":56,"watchers":29,"default_branch":"releases/1.2"}},"base":{"label":"GalSim-developers:master","ref":"master","sha":"4bcd1b35c8f57c56cf07fb3ecdc9188d2c878ba7","user":{"login":"GalSim-developers","id":1462196,"avatar_url":"https://avatars.githubusercontent.com/u/1462196?v=3","gravatar_id":"","url":"https://api.github.com/users/GalSim-developers","html_url":"https://github.com/GalSim-developers","followers_url":"https://api.github.com/users/GalSim-developers/followers","following_url":"https://api.github.com/users/GalSim-developers/following{/other_user}","gists_url":"https://api.github.com/users/GalSim-developers/gists{/gist_id}","starred_url":"https://api.github.com/users/GalSim-developers/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/GalSim-developers/subscriptions","organizations_url":"https://api.github.com/users/GalSim-developers/orgs","repos_url":"https://api.github.com/users/GalSim-developers/repos","events_url":"https://api.github.com/users/GalSim-developers/events{/privacy}","received_events_url":"https://api.github.com/users/GalSim-developers/received_events","type":"Organization","site_admin":false},"repo":{"id":3510804,"name":"GalSim","full_name":"GalSim-developers/GalSim","owner":{"login":"GalSim-developers","id":1462196,"avatar_url":"https://avatars.githubusercontent.com/u/1462196?v=3","gravatar_id":"","url":"https://api.github.com/users/GalSim-developers","html_url":"https://github.com/GalSim-developers","followers_url":"https://api.github.com/users/GalSim-developers/followers","following_url":"https://api.github.com/users/GalSim-developers/following{/other_user}","gists_url":"https://api.github.com/users/GalSim-developers/gists{/gist_id}","starred_url":"https://api.github.com/users/GalSim-developers/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/GalSim-developers/subscriptions","organizations_url":"https://api.github.com/users/GalSim-developers/orgs","repos_url":"https://api.github.com/users/GalSim-developers/repos","events_url":"https://api.github.com/users/GalSim-developers/events{/privacy}","received_events_url":"https://api.github.com/users/GalSim-developers/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/GalSim-developers/GalSim","description":"The modular galaxy image simulation toolkit","fork":false,"url":"https://api.github.com/repos/GalSim-developers/GalSim","forks_url":"https://api.github.com/repos/GalSim-developers/GalSim/forks","keys_url":"https://api.github.com/repos/GalSim-developers/GalSim/keys{/key_id}","collaborators_url":"https://api.github.com/repos/GalSim-developers/GalSim/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/GalSim-developers/GalSim/teams","hooks_url":"https://api.github.com/repos/GalSim-developers/GalSim/hooks","issue_events_url":"https://api.github.com/repos/GalSim-developers/GalSim/issues/events{/number}","events_url":"https://api.github.com/repos/GalSim-developers/GalSim/events","assignees_url":"https://api.github.com/repos/GalSim-developers/GalSim/assignees{/user}","branches_url":"https://api.github.com/repos/GalSim-developers/GalSim/branches{/branch}","tags_url":"https://api.github.com/repos/GalSim-developers/GalSim/tags","blobs_url":"https://api.github.com/repos/GalSim-developers/GalSim/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/GalSim-developers/GalSim/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/GalSim-developers/GalSim/git/refs{/sha}","trees_url":"https://api.github.com/repos/GalSim-developers/GalSim/git/trees{/sha}","statuses_url":"https://api.github.com/repos/GalSim-developers/GalSim/statuses/{sha}","languages_url":"https://api.github.com/repos/GalSim-developers/GalSim/languages","stargazers_url":"https://api.github.com/repos/GalSim-developers/GalSim/stargazers","contributors_url":"https://api.github.com/repos/GalSim-developers/GalSim/contributors","subscribers_url":"https://api.github.com/repos/GalSim-developers/GalSim/subscribers","subscription_url":"https://api.github.com/repos/GalSim-developers/GalSim/subscription","commits_url":"https://api.github.com/repos/GalSim-developers/GalSim/commits{/sha}","git_commits_url":"https://api.github.com/repos/GalSim-developers/GalSim/git/commits{/sha}","comments_url":"https://api.github.com/repos/GalSim-developers/GalSim/comments{/number}","issue_comment_url":"https://api.github.com/repos/GalSim-developers/GalSim/issues/comments{/number}","contents_url":"https://api.github.com/repos/GalSim-developers/GalSim/contents/{+path}","compare_url":"https://api.github.com/repos/GalSim-developers/GalSim/compare/{base}...{head}","merges_url":"https://api.github.com/repos/GalSim-developers/GalSim/merges","archive_url":"https://api.github.com/repos/GalSim-developers/GalSim/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/GalSim-developers/GalSim/downloads","issues_url":"https://api.github.com/repos/GalSim-developers/GalSim/issues{/number}","pulls_url":"https://api.github.com/repos/GalSim-developers/GalSim/pulls{/number}","milestones_url":"https://api.github.com/repos/GalSim-developers/GalSim/milestones{/number}","notifications_url":"https://api.github.com/repos/GalSim-developers/GalSim/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/GalSim-developers/GalSim/labels{/name}","releases_url":"https://api.github.com/repos/GalSim-developers/GalSim/releases{/id}","created_at":"2012-02-22T02:51:45Z","updated_at":"2015-01-27T06:46:41Z","pushed_at":"2015-03-01T22:01:01Z","git_url":"git://github.com/GalSim-developers/GalSim.git","ssh_url":"git@github.com:GalSim-developers/GalSim.git","clone_url":"https://github.com/GalSim-developers/GalSim.git","svn_url":"https://github.com/GalSim-developers/GalSim","homepage":"","size":92432,"stargazers_count":29,"watchers_count":29,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":19,"mirror_url":null,"open_issues_count":56,"forks":19,"open_issues":56,"watchers":29,"default_branch":"releases/1.2"}},"_links":{"self":{"href":"https://api.github.com/repos/GalSim-developers/GalSim/pulls/651"},"html":{"href":"https://github.com/GalSim-developers/GalSim/pull/651"},"issue":{"href":"https://api.github.com/repos/GalSim-developers/GalSim/issues/651"},"comments":{"href":"https://api.github.com/repos/GalSim-developers/GalSim/issues/651/comments"},"review_comments":{"href":"https://api.github.com/repos/GalSim-developers/GalSim/pulls/651/comments"},"review_comment":{"href":"https://api.github.com/repos/GalSim-developers/GalSim/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/GalSim-developers/GalSim/pulls/651/commits"},"statuses":{"href":"https://api.github.com/repos/GalSim-developers/GalSim/statuses/6c0d994af69b4f48c198ceb2d9f714043df8a94e"}}}}