diff --git a/CHANGELOG.rst b/CHANGELOG.rst
new file mode 100644
index 0000000..0bf7cfc
--- /dev/null
+++ b/CHANGELOG.rst
@@ -0,0 +1,3 @@
+Changes from v0.0.0 to v0.0.1
+=========================
+Made repo public.
\ No newline at end of file
diff --git a/config/README.md b/config/README.md
deleted file mode 100644
index bc79c08..0000000
--- a/config/README.md
+++ /dev/null
@@ -1,41 +0,0 @@
-## Config imsim
-
-`was.yaml`: _euclidlike_imsim exemple_ config file.
-You will want to update the following entries:
-
-- `input.obseq_data.file_name`: path to the observing sequence. Link to `euclidlike/data/euclid_obseq.fits`
-- `input.sky_catalog.file_name`: path to the skyCatalog to use
-- `output.dir`: path to the output directory for the simulated images
-- `output.truth.dir`: path to the ouput directory for the true catalogs
-
-To run the code:
-```bash
-galsim was.yaml
-```
-
-You might want to specify some config entries in the command line, likes:
-```bash
-galsim was.yaml input.obseq_data.visit=33690 image.CCD=1
-```
-
-## Config SLURM
-
-`slurm_runner.sh` contain the SLURM configuration to run "large scale" simulations.
-You will want to update the following line:
-- `#!/bin/zsh`: depending on the shell you are using you might want to change it to: `#!/bin/bash`
-- `#SBATCH --output=/path/to/slurm-%A-%a.out`: slurm stdout file
-- `#SBATCH --error=/path/to/slurm-%A-%a.err`: slurm stderr file
-- `source activate [env_name]`: conda environment to use
-- `file_list='/path/to/run_list.txt'`: file containing the pointings to simulate (see note below)
-
-the `run_list.txt` is a 2 columns file with the pointing and the CCD_ID to simulate. It should lookq like:
-```
-33688 0
-33688 1
-33688 2
-[...]
-33688 35
-33689 0
-33689 1
-[...]
-```
diff --git a/config/README.rst b/config/README.rst
new file mode 100644
index 0000000..fb823c3
--- /dev/null
+++ b/config/README.rst
@@ -0,0 +1,49 @@
+Config imsim
+============
+
+``was.yaml``: *euclidlike_imsim example* config file.
+You will want to update the following entries:
+
+- ``input.obseq_data.file_name``: path to the observing sequence. Link to ``euclidlike/data/euclid_obseq.fits``
+- ``input.sky_catalog.file_name``: path to the skyCatalog to use
+- ``output.dir``: path to the output directory for the simulated images
+- ``output.truth.dir``: path to the output directory for the true catalogs
+
+To run the code:
+
+.. code-block:: bash
+
+ galsim was.yaml
+
+You might want to specify some config entries on the command line, like:
+
+.. code-block:: bash
+
+ galsim was.yaml input.obseq_data.visit=33690 image.CCD=1
+
+
+Config SLURM
+============
+
+``slurm_runner.sh`` contains the SLURM configuration to run "large scale" simulations.
+You will want to update the following lines:
+
+- ``#!/bin/zsh``: depending on the shell you are using, you might want to change it to: ``#!/bin/bash``
+- ``#SBATCH --output=/path/to/slurm-%A-%a.out``: SLURM stdout file
+- ``#SBATCH --error=/path/to/slurm-%A-%a.err``: SLURM stderr file
+- ``source activate [env_name]``: conda environment to use
+- ``file_list='/path/to/run_list.txt'``: file containing the pointings to simulate (see note below)
+
+The ``run_list.txt`` is a 2-column file with the pointing and the ``CCD_ID`` to simulate. It should look like:
+
+.. code-block:: text
+
+ 33688 0
+ 33688 1
+ 33688 2
+ [...]
+ 33688 35
+ 33689 0
+ 33689 1
+ [...]
+
diff --git a/docs/_build/doctrees/environment.pickle b/docs/_build/doctrees/environment.pickle
index 2d9ec1c..d38556f 100644
Binary files a/docs/_build/doctrees/environment.pickle and b/docs/_build/doctrees/environment.pickle differ
diff --git a/docs/_build/doctrees/euclidlike.doctree b/docs/_build/doctrees/euclidlike.doctree
index 3147a25..a6e6f7e 100644
Binary files a/docs/_build/doctrees/euclidlike.doctree and b/docs/_build/doctrees/euclidlike.doctree differ
diff --git a/docs/_build/doctrees/euclidlike_imsim.doctree b/docs/_build/doctrees/euclidlike_imsim.doctree
index 590b7be..f46c640 100644
Binary files a/docs/_build/doctrees/euclidlike_imsim.doctree and b/docs/_build/doctrees/euclidlike_imsim.doctree differ
diff --git a/docs/_build/doctrees/examples.doctree b/docs/_build/doctrees/examples.doctree
new file mode 100644
index 0000000..f39ec92
Binary files /dev/null and b/docs/_build/doctrees/examples.doctree differ
diff --git a/docs/_build/doctrees/history.doctree b/docs/_build/doctrees/history.doctree
new file mode 100644
index 0000000..68423cc
Binary files /dev/null and b/docs/_build/doctrees/history.doctree differ
diff --git a/docs/_build/doctrees/index.doctree b/docs/_build/doctrees/index.doctree
index e619b1a..79b0e2b 100644
Binary files a/docs/_build/doctrees/index.doctree and b/docs/_build/doctrees/index.doctree differ
diff --git a/docs/_build/doctrees/install.doctree b/docs/_build/doctrees/install.doctree
new file mode 100644
index 0000000..895b106
Binary files /dev/null and b/docs/_build/doctrees/install.doctree differ
diff --git a/docs/_build/doctrees/modules.doctree b/docs/_build/doctrees/modules.doctree
index 186a5a8..696c7e8 100644
Binary files a/docs/_build/doctrees/modules.doctree and b/docs/_build/doctrees/modules.doctree differ
diff --git a/docs/_build/doctrees/overview.doctree b/docs/_build/doctrees/overview.doctree
new file mode 100644
index 0000000..6efbd28
Binary files /dev/null and b/docs/_build/doctrees/overview.doctree differ
diff --git a/docs/_build/doctrees/scripts.doctree b/docs/_build/doctrees/scripts.doctree
index 13bc54c..37d1451 100644
Binary files a/docs/_build/doctrees/scripts.doctree and b/docs/_build/doctrees/scripts.doctree differ
diff --git a/docs/_build/html/.buildinfo b/docs/_build/html/.buildinfo
index acd1aed..cbbaf3d 100644
--- a/docs/_build/html/.buildinfo
+++ b/docs/_build/html/.buildinfo
@@ -1,4 +1,4 @@
# Sphinx build info version 1
# This file records the configuration used when building these files. When it is not found, a full rebuild will be done.
-config: cec21682d6cc0df78936fbd198ac0c57
+config: 11d168bc3ba21763cf541d100b9a19c3
tags: 645f666f9bcd5a90fca523b33c5a78b7
diff --git a/docs/_build/html/_modules/euclidlike/backgrounds.html b/docs/_build/html/_modules/euclidlike/backgrounds.html
index 1041421..82899f6 100644
--- a/docs/_build/html/_modules/euclidlike/backgrounds.html
+++ b/docs/_build/html/_modules/euclidlike/backgrounds.html
@@ -38,7 +38,7 @@
Source code for euclidlike.backgrounds
importgalsim
-[docs]
+[docs]defgetSkyLevel(bandpass,world_pos=None,exptime=None,epoch=2025,date=None):""" Get the expected sky level for a Euclid observation due to zodiacal light for this bandpass
@@ -121,8 +121,6 @@
Source code for euclidlike.backgrounds
returnsky_val
-
-[docs]defgetZodiBackground(ecl_lat,ecl_dlon,lambda_min,lambda_max,Tlambda,T):""" This helper routine is used with permission from Chris Hirata's Exposure Time Calculator and enables the
@@ -258,8 +256,7 @@
Source code for euclidlike.backgrounds
5.03411747e10*lambda_*np.interp(lambda_,Tlambda,T)# We now have the zodi level in photons/m^2/sr/sec. Convert to photons/m^2/arcsec^2/sec.
- return(zodi_tot/4.2545170296152206e10)
-[docs]
+[docs]defgetBandpasses(AB_zeropoint=True,default_thin_trunc=True,full_bandpass=False,**kwargs):""" Function to get the bandpass information for the Euclid VIS band and the three Euclid NISP passbands.
@@ -185,6 +185,14 @@
-[docs]
+[docs]defgetWCS(world_pos,PA=None,date=None,CCDs=None,PA_is_FPA=False,SAA=None):""" This routine returns a dict containing a WCS for each of the Euclid CCDs.
@@ -246,8 +246,6 @@
Source code for euclidlike.euclidlike_wcs
-
-[docs]defconvertCenter(world_pos,CCD,PA=None,date=None,SAA=None,PA_is_FPA=False,tol=0.5*coord.arcsec):""" This is a simple helper routine that takes an input position ``world_pos`` that is meant to
@@ -316,12 +314,11 @@
-[docs]
+[docs]deffindCCD(wcs_dict,world_pos,include_border=False):""" This is a subroutine to take a dict of WCS (one per CCD) from euclidlike.getWCS() and query
@@ -583,8 +580,6 @@
Source code for euclidlike.euclidlike_wcs
returndate,CCDs,pa_fpa,pa_obsy
-
-[docs]defallowedPos(world_pos,date,SAA=None):""" This routine can be used to check whether Euclid would be allowed to look at a particular
@@ -623,12 +618,9 @@
Source code for euclidlike.euclidlike_wcs
# Check if it's within tolerance.min_ang=90.-min_sun_angle/coord.degreesmax_ang=90.+max_sun_angle/coord.degrees
- returnmin_ang<=angle_deg<=max_ang
-
+ returnmin_ang<=angle_deg<=max_ang
-
-[docs]defbestPA(world_pos,date,SAA=None):""" This routine determines the best position angle for the observatory for a given observation date
@@ -671,8 +663,7 @@
Source code for euclidlike.euclidlike_wcs
# celestial north pole. It is defined as position angle east of north.north=coord.CelestialCoord(y_obs.ra,90.*coord.degrees)obs_pa=world_pos.angleBetween(y_obs,north)
- returnobs_pa
+[docs]
+classEuclidlikeBandpassBuilder(BandpassBuilder):
+"""A class for loading a Bandpass from a file
+
+ FileBandpass expected the following parameter:
+
+ name (str) The name of the Euclid filter to get. (required)
+ """
+
+[docs]
+ defbuildBandpass(self,config,base,logger):
+"""Build the Bandpass based on the specifications in the config dict.
+
+ Parameters:
+ config: The configuration dict for the bandpass type.
+ base: The base configuration dict.
+ logger: If provided, a logger for logging debug statements.
+
+ Returns:
+ the constructed Bandpass object.
+ """
+ req={'name':str}
+ kwargs,safe=GetAllParams(config,base,req=req)
+
+ name=kwargs['name']
+ # Hard set the limit due to PSF definition
+ bandpass=euclidlike.getBandpasses()[name]
+
+ returnbandpass,safe
+[docs]
+ defsetup(self,config,base,image_num,obj_num,ignore,logger):
+"""Do the initialization and setup for building the image.
+
+ This figures out the size that the image will be, but doesn't actually build it yet.
+
+ Parameters:
+ config: The configuration dict for the image field.
+ base: The base configuration dict.
+ image_num: The current image number.
+ obj_num: The first object number in the image.
+ ignore: A list of parameters that are allowed to be in config that we can
+ ignore here. i.e. it won't be an error if these parameters are present.
+ logger: If given, a logger object to log progress.
+
+ Returns:
+ xsize, ysize
+ """
+ logger.debug(
+ "image %d: Building EuclidlikeCCD: image, obj = %d,%d",
+ image_num,
+ image_num,
+ obj_num,
+ )
+
+ self.nobjects=self.getNObj(config,base,image_num,logger=logger)
+ logger.debug("image %d: nobj = %d",image_num,self.nobjects)
+
+ # These are allowed for Scattered, but we don't use them here.
+ extra_ignore=[
+ "image_pos",
+ "world_pos",
+ "stamp_size",
+ "stamp_xsize",
+ "stamp_ysize",
+ "nobjects",
+ ]
+ req={"CCD":int,"filter":str,"mjd":float,"exptime":float}
+ opt={
+ "draw_method":str,
+ }
+ opt.update({key:boolforkeyincfg_noise_key})
+ params=galsim.config.GetAllParams(
+ config,base,req=req,opt=opt,ignore=ignore+extra_ignore
+ )[0]
+
+ self.ccd=params["CCD"]
+ base["CCD"]=self.ccd
+ self.filter=params["filter"]
+ self.mjd=params["mjd"]
+ self.exptime=params["exptime"]
+
+ self.cfg_noise=parse_noise_config(params)
+
+ # If draw_method isn't in image field, it may be in stamp. Check.
+ self.draw_method=params.get(
+ "draw_method",base.get("stamp",{}).get("draw_method","auto")
+ )
+
+ # If user hasn't overridden the bandpass to use, get the standard one.
+ if"bandpass"notinconfig:
+ base["bandpass"]=galsim.config.BuildBandpass(
+ base["image"],"bandpass",base,logger=logger
+ )
+
+ returneuclidlike.n_pix_col,euclidlike.n_pix_row
+
+
+
+[docs]
+ defbuildImage(self,config,base,image_num,obj_num,logger):
+"""Build an Image containing multiple objects placed at arbitrary locations.
+
+ Parameters:
+ config: The configuration dict for the image field.
+ base: The base configuration dict.
+ image_num: The current image number.
+ obj_num: The first object number in the image.
+ logger: If given, a logger object to log progress.
+
+ Returns:
+ the final image and the current noise variance in the image as a tuple
+ """
+ full_xsize=base["image_xsize"]
+ full_ysize=base["image_ysize"]
+ wcs=base["wcs"]
+
+ full_image=Image(full_xsize,full_ysize,dtype=float)
+ full_image.setOrigin(base["image_origin"])
+ full_image.wcs=wcs
+ full_image.setZero()
+
+ full_image.header=galsim.FitsHeader()
+ full_image.header["EXPTIME"]=self.exptime
+ full_image.header["MJD-OBS"]=self.mjd
+ full_image.header["DATE-OBS"]=str(Time(self.mjd,format="mjd").datetime)
+ full_image.header["FILTER"]=self.filter
+ full_image.header["GAIN"]=gain
+ full_image.header["ZPTMAG"]=2.5*np.log10(
+ self.exptime*euclidlike.collecting_area
+ )
+
+ base["current_image"]=full_image
+
+ if"image_pos"inconfigand"world_pos"inconfig:
+ raiseGalSimConfigValueError(
+ "Both image_pos and world_pos specified for Scattered image.",
+ (config["image_pos"],config["world_pos"]),
+ )
+
+ if"image_pos"notinconfigand"world_pos"notinconfig:
+ xmin=base["image_origin"].x
+ xmax=xmin+full_xsize-1
+ ymin=base["image_origin"].y
+ ymax=ymin+full_ysize-1
+ config["image_pos"]={
+ "type":"XY",
+ "x":{"type":"Random","min":xmin,"max":xmax},
+ "y":{"type":"Random","min":ymin,"max":ymax},
+ }
+
+ nbatch=self.nobjects//1000+1
+ forbatchinrange(nbatch):
+ start_obj_num=self.nobjects*batch//nbatch
+ end_obj_num=self.nobjects*(batch+1)//nbatch
+ nobj_batch=end_obj_num-start_obj_num
+ ifnbatch>1:
+ logger.warning(
+ "Start batch %d/%d with %d objects [%d, %d)",
+ batch+1,
+ nbatch,
+ nobj_batch,
+ start_obj_num,
+ end_obj_num,
+ )
+ stamps,current_vars=galsim.config.BuildStamps(
+ nobj_batch,base,logger=logger,obj_num=start_obj_num,do_noise=False
+ )
+ base["index_key"]="image_num"
+
+ forkinrange(nobj_batch):
+ # This is our signal that the object was skipped.
+ ifstamps[k]isNone:
+ continue
+ bounds=stamps[k].bounds&full_image.bounds
+ ifnotbounds.isDefined():# pragma: no cover
+ # These noramlly show up as stamp==None, but technically it is possible
+ # to get a stamp that is off the main image, so check for that here to
+ # avoid an error. But this isn't covered in the imsim test suite.
+ continue
+
+ logger.debug(
+ "image %d: full bounds = %s",image_num,str(full_image.bounds)
+ )
+ logger.debug(
+ "image %d: stamp %d bounds = %s",
+ image_num,
+ k+start_obj_num,
+ str(stamps[k].bounds),
+ )
+ logger.debug("image %d: Overlap = %s",image_num,str(bounds))
+ full_image[bounds]+=stamps[k][bounds]
+ stamps=None
+
+ returnfull_image,None
+
+
+
+[docs]
+ defaddNoise(self,image,config,base,image_num,obj_num,current_var,logger):
+"""Add the final noise to a Scattered image
+
+ Parameters:
+ image: The image onto which to add the noise.
+ config: The configuration dict for the image field.
+ base: The base configuration dict.
+ image_num: The current image number.
+ obj_num: The first object number in the image.
+ current_var: The current noise variance in each postage stamps.
+ logger: If given, a logger object to log progress.
+ """
+ # check ignore noise
+ ifself.cfg_noise["ignore_noise"]:
+ return
+
+ if"noise_image"notinbase.keys():
+ get_noise(self.cfg_noise,config,base,logger)
+
+ # We first have to apply gain and quantize the image
+ image/=gain
+ image.quantize()
+
+ image+=base["noise_image"]
+
+ # Apply saturation
+ saturation_ADU=np.round(saturation/gain)
+ mask_saturated=image.array>saturation_ADU
+ base["saturated_mask"]=mask_saturated
+ image.array[mask_saturated]=saturation_ADU
+
+ ifself.cfg_noise["sky_subtract"]:
+ image-=base["sky_image"]
+
+
+
+
+# Register this as a valid type
+RegisterImageType("euclidlike_ccd",EuclidlikeCCDImageBuilder())
+
+importnumpyasnp
+
+fromastropy.timeimportTime
+
+importgalsim
+fromgalsim.errorsimportGalSimConfigError
+
+importeuclidlike
+fromeuclidlike.instrument_paramsimportgain,read_noise
+
+# Some place holder stuff, will need to update later in euclidlike
+# This is just to avoid changing the code and don't have "errors"
+stray_light_fraction=0.
+dark_current=0.
+
+cfg_noise_key=[
+ "ignore_noise",
+ "stray_light",
+ "thermal_background",
+ "reciprocity_failure",
+ "dark_current",
+ "nonlinearity",
+ "ipc",
+ "read_noise",
+ "sky_subtract",
+]
+
+
+
+[docs]
+defget_noise(cfg_noise,cfg_image,base,logger):
+
+ noise_img=base["current_image"].copy()
+ noise_img.fill(0)
+
+ wcs=base["wcs"]
+ bp=base["bandpass"]
+ rng=galsim.config.GetRNG(cfg_image,base)
+ logger.info(
+ "image %d: Start EuclidlikeCCD detector effects",base.get("image_num",0)
+ )
+
+ # Things that will eventually be subtracted (if sky_subtract) will have their expectation
+ # value added to sky_image. So technically, this includes things that aren't just sky.
+ # E.g. includes dark_current and thermal backgrounds.
+ sky_image=noise_img.copy()
+ sky_level=euclidlike.getSkyLevel(
+ bp,
+ world_pos=wcs.toWorld(noise_img.true_center),
+ date=Time(cfg_noise["mjd"],format="mjd").datetime,
+ )
+ logger.debug("Adding sky_level = %s",sky_level)
+ ifcfg_noise["stray_light"]:
+ logger.debug("Stray light fraction = %s",stray_light_fraction)
+ sky_level*=1.0+stray_light_fraction
+ wcs.makeSkyImage(sky_image,sky_level)
+
+ # The image up to here is an expectation value.
+ # Realize it as an integer number of photons.
+ poisson_noise=galsim.noise.PoissonNoise(rng)
+ logger.debug("Adding poisson noise to sky photons")
+ sky_image1=sky_image.copy()
+ sky_image1.addNoise(poisson_noise)
+ noise_img.quantize()
+ # the image won't necessarily be integers.
+ noise_img+=sky_image1
+
+ # Apply the detector effects here. Not all of these are "noise" per se, but they
+ # happen interspersed with various noise effects, so apply them all in this step.
+
+ ifcfg_noise["dark_current"]:
+ dc=dark_current*cfg_noise["exptime"]
+ logger.debug("Adding dark current: %s",dc)
+ sky_image+=dc
+ dark_noise=galsim.noise.DeviateNoise(
+ galsim.random.PoissonDeviate(rng,dc)
+ )
+ noise_img.addNoise(dark_noise)
+
+ ifcfg_noise["read_noise"]:
+ logger.debug("Adding read noise %s",read_noise)
+ noise_img.addNoise(galsim.GaussianNoise(rng,sigma=read_noise))
+
+ logger.debug("Applying gain %s",gain)
+ noise_img/=gain
+
+ # Make integer ADU now.
+ noise_img.quantize()
+
+ sky_image/=gain
+ sky_image.quantize()
+
+ base["noise_image"]=noise_img
+ base["sky_image"]=sky_image
+[docs]
+ definitialize(self,data,scratch,config,base,logger):
+"""Do any initial setup for this builder at the start of a new output file.
+
+ The base class implementation saves two work space items into self.data and self.scratch
+ that can be used to safely communicate across multiple processes.
+
+ Parameters:
+ data: An empty list of length nimages to use as work space.
+ scratch: An empty dict that can be used as work space.
+ config: The configuration field for this output object.
+ base: The base configuration dict.
+ logger: If given, a logger object to log progress. [default: None]
+ """
+ req={"CCD":int,"filter":str,"mjd":float,"exptime":float}
+ opt={key:boolforkeyincfg_noise_key}
+ ignore=galsim.config.image.image_ignore
+ extra_ignore=[
+ "image_pos",
+ "world_pos",
+ "stamp_size",
+ "stamp_xsize",
+ "stamp_ysize",
+ "nobjects",
+ ]
+ params=galsim.config.GetAllParams(
+ base["image"],base,req=req,opt=opt,ignore=ignore+extra_ignore,
+ )[0]
+ self.cfg_noise=parse_noise_config(params)
+
+ self._check_input()
+
+ self.data=data
+ self.scratch=scratch
+ self.final_data=None
+
+
+ def_check_input(self):
+ ifself.cfg_noise["ignore_noise"]:
+ raiseGalSimConfigError(
+ "You cannot ignore the noise and request the noise image at the same time."
+ " Either active the noise or remove the output noise image."
+ )
+
+
+[docs]
+ defprocessImage(self,index,obj_nums,config,base,logger):
+"""Perform any necessary processing at the end of each image construction.
+
+ This function will be called after each full image is built.
+
+ Compute the noise for the current image and add it to the image.
+ It will also create an independent noise image.
+ The code optionally subtract the background if requested.
+
+ Parameters:
+ index: The index in self.data to use for this image. This isn't the image_num
+ (which can be accessed at base['image_num'] if needed), but rather
+ an index that starts at 0 for the first image being worked on and
+ goes up to nimages-1.
+ obj_nums: The object numbers that were used for this image.
+ config: The configuration field for this output object.
+ base: The base configuration dict.
+ logger: If given, a logger object to log progress. [default: None]
+ """
+ if"noise_image"notinbase.keys():
+ get_noise(self.cfg_noise,config,base,logger)
+
+ noise_image=base["noise_image"].copy()
+
+ ifself.cfg_noise["sky_subtract"]:
+ noise_image-=base["sky_image"]
+
+ self.data[index]=noise_image
+
+
+
+
+
+[docs]
+classSkyImageBuilder(NoiseImageBuilder):
+
+ def_check_input(self):
+ ifself.cfg_noise["ignore_noise"]:
+ raiseGalSimConfigError(
+ "You cannot ignore the noise and request the sky image at the same time."
+ " Either activate the noise or remove the output sky image."
+ )
+
+
+[docs]
+ defprocessImage(self,index,obj_nums,config,base,logger):
+"""Perform any necessary processing at the end of each image construction.
+
+ This function will be called after each full image is built.
+
+ Compute the sky background and return it in an image.
+
+ Parameters:
+ index: The index in self.data to use for this image. This isn't the image_num
+ (which can be accessed at base['image_num'] if needed), but rather
+ an index that starts at 0 for the first image being worked on and
+ goes up to nimages-1.
+ obj_nums: The object numbers that were used for this image.
+ config: The configuration field for this output object.
+ base: The base configuration dict.
+ logger: If given, a logger object to log progress. [default: None]
+ """
+ if"noise_image"notinbase.keys():
+ get_noise(self.cfg_noise,config,base,logger)
+
+ self.data[index]=base["sky_image"].copy()
+
+
+
+
+
+[docs]
+classWeightImageBuilder(NoiseImageBuilder):
+
+ def_check_input(self):
+ ifself.cfg_noise["ignore_noise"]:
+ raiseGalSimConfigError(
+ "You cannot ignore the noise and request the weight image at the same time."
+ " Either activate the noise or remove the output sky image."
+ )
+
+
+[docs]
+ defprocessImage(self,index,obj_nums,config,base,logger):
+"""Perform any necessary processing at the end of each image construction.
+
+ This function will be called after each full image is built.
+
+ Compute the weight map from the noise image and return it in an image.
+
+ Parameters:
+ index: The index in self.data to use for this image. This isn't the image_num
+ (which can be accessed at base['image_num'] if needed), but rather
+ an index that starts at 0 for the first image being worked on and
+ goes up to nimages-1.
+ obj_nums: The object numbers that were used for this image.
+ config: The configuration field for this output object.
+ base: The base configuration dict.
+ logger: If given, a logger object to log progress. [default: None]
+ """
+ if"noise_image"notinbase.keys():
+ get_noise(self.cfg_noise,config,base,logger)
+
+ noise_var=np.var(base["noise_image"].array)
+
+ weight_image=galsim.ImageF(base["image_bounds"])
+ weight_image+=noise_var
+ weight_image.invertSelf()
+
+ self.data[index]=weight_image
+[docs]
+classChargeDiff(PhotonOp):
+"""A photon operator that applies the effect of charge diffusion via a probablistic model limit.
+ """
+ def__init__(self,rng=None,**kwargs):
+
+ self.ud=UniformDeviate(rng)
+ self.gd1=GaussianDeviate(rng,sigma=_s1)
+ self.gd2=GaussianDeviate(rng,sigma=_s2)
+ self.gd3=GaussianDeviate(rng,sigma=_s3)
+
+
+[docs]
+ defapplyTo(self,photon_array,local_wcs=None,rng=None):
+"""Apply the charge diffusion effect to the photons
+
+ Parameters:
+ photon_array: A `PhotonArray` to apply the operator to.
+ local_wcs: A `LocalWCS` instance defining the local WCS for the current photon
+ bundle in case the operator needs this information. [default: None]
+ rng: A random number generator to use if needed. [default: None]
+ """
+
+ # Choose which weighted Gausian to use in sech model approximation
+ u=np.empty(len(photon_array.x))
+ self.ud.generate(u)
+
+ # Selects appropriate fraction of photons corresponding to the first gaussian in the sech model
+ mask=u<_w1
+ dx=np.empty(np.sum(mask))
+ dy=np.empty(np.sum(mask))
+ # Generate and apply the 2D gaussian shifts corresponding to the first gaussian
+ self.gd1.generate(dx)
+ self.gd1.generate(dy)
+ photon_array.x[mask]+=dx
+ photon_array.y[mask]+=dy
+
+ # Selects appropriate fraction of photons corresponding to the second gaussian in the sech model
+ mask=(u>=_w1)&(u<=(1.-_w3))
+ dx=np.empty(np.sum(mask))
+ dy=np.empty(np.sum(mask))
+ # Generate and apply the 2D gaussian shifts corresponding to the second gaussian
+ self.gd2.generate(dx)
+ self.gd2.generate(dy)
+ photon_array.x[mask]+=dx
+ photon_array.y[mask]+=dy
+
+ # Selects appropriate fraction of photons corresponding to the third gaussian in the sech model
+ mask=u>(1.-_w3)
+ dx=np.empty(np.sum(mask))
+ dy=np.empty(np.sum(mask))
+ # Generate and apply the 2D gaussian shifts corresponding to the second gaussian
+ self.gd3.generate(dx)
+ self.gd3.generate(dy)
+ photon_array.x[mask]+=dx
+ photon_array.y[mask]+=dy
+[docs]
+classEuclidlikePSF(object):
+"""Class building needed Euclidlike PSFs."""
+
+ def__init__(
+ self,
+ CCD=None,
+ WCS=None,
+ n_waves=None,
+ bpass=None,
+ extra_aberrations=None,
+ logger=None,
+ ):
+
+ logger=galsim.config.LoggerWrapper(logger)
+ # n_waves parameter is only used for treatment of bright objects. In here we use n_waves = 5
+ # to speed up computation of the ChromaticOpticalPSF used for bright objects.
+ ifn_waves==-1:
+ n_waves=5
+
+ corners=[
+ galsim.PositionD(1,1),
+ galsim.PositionD(1,euclidlike.n_pix_row),
+ galsim.PositionD(euclidlike.n_pix_col,1),
+ galsim.PositionD(euclidlike.n_pix_col,euclidlike.n_pix_row),
+ ]
+ cc=galsim.PositionD(euclidlike.n_pix_col/2,euclidlike.n_pix_row/2)
+ tags=["ll","lu","ul","uu"]
+ self.PSF={}
+ pupil_bin=8
+ self.PSF[pupil_bin]={}
+ fortag,CCD_posintuple(zip(tags,corners)):
+ self.PSF[pupil_bin][tag]=self._psf_call(
+ CCD,bpass,CCD_pos,WCS,pupil_bin,n_waves,logger,extra_aberrations
+ )
+ forpupil_binin[4,2,"achromatic"]:
+ self.PSF[pupil_bin]=self._psf_call(
+ CCD,bpass,cc,WCS,pupil_bin,n_waves,logger,extra_aberrations
+ )
+
+ def_parse_pupil_bin(self,pupil_bin):
+ ifpupil_bin=="achromatic":
+ return8
+ else:
+ returnpupil_bin
+
+ def_psf_call(
+ self,CCD,bpass,CCD_pos,WCS,pupil_bin,n_waves,logger,extra_aberrations
+ ):
+
+ ifpupil_bin==8:
+ psf=euclidlike.getPSF(
+ CCD,
+ bpass.name,
+ ccd_pos=CCD_pos,
+ wcs=WCS,
+ # pupil_bin=pupil_bin,
+ # n_waves=n_waves,
+ logger=logger,
+ # Don't set wavelength for this one.
+ # We want this to be chromatic for photon shooting.
+ # wavelength = bpass.effective_wavelength,
+ # extra_aberrations=extra_aberrations,
+ )
+ else:
+ psf=euclidlike.getBrightPSF(
+ CCD,
+ bpass.name,
+ ccd_pos=CCD_pos,
+ wcs=WCS,
+ pupil_bin=self._parse_pupil_bin(pupil_bin),
+ n_waves=n_waves,
+ logger=logger,
+ # Note: setting wavelength makes it achromatic.
+ # We only use pupil_bin = 2,4 for FFT objects.
+ wavelength=bpass.effective_wavelength,
+ )
+
+ # psf = euclidlike.getPSF(
+ # CCD,
+ # bpass.name,
+ # ccd_pos=CCD_pos,
+ # wcs=WCS,
+ # # pupil_bin=self._parse_pupil_bin(pupil_bin),
+ # # n_waves=n_waves,
+ # logger=logger,
+ # # Note: setting wavelength makes it achromatic.
+ # # We only use pupil_bin = 2,4 for FFT objects.
+ # wavelength=bpass.effective_wavelength,
+ # # extra_aberrations=extra_aberrations,
+ # )
+ ifpupil_bin==4:
+ returnpsf.withGSParams(maximum_fft_size=16384,folding_threshold=1e-3)
+ elifpupil_bin==2:
+ returnpsf.withGSParams(maximum_fft_size=16384,folding_threshold=1e-4)
+ else:
+ returnpsf.withGSParams(maximum_fft_size=16384)
+
+
+[docs]
+ defgetPSF(self,pupil_bin,pos):
+"""
+ Return a PSF to be convolved with sources.
+ PSF is sampled at 4 quadrants for each CCD. Returned PSF
+ corresponds to that of the quadrant of the CCD position.
+
+ @param [in] what pupil binning to request.
+ """
+ ## For now no interpolation is used
+ #wll = (euclidlike.n_pix_col - pos.x) * (euclidlike.n_pix_row - pos.y)
+ #wlu = (euclidlike.n_pix_col - pos.x) * (pos.y - 1)
+ #wul = (pos.x - 1) * (euclidlike.n_pix_row - pos.y)
+ #wuu = (pos.x - 1) * (pos.y - 1)
+ #return (
+ # wll * psf["ll"] + wlu * psf["lu"] + wul * psf["ul"] + wuu * psf["uu"]
+ #) / ((euclidlike.n_pix_row - 1) * (euclidlike.n_pix_col - 1))
+
+
+ psf=self.PSF[pupil_bin]
+ ifpupil_bin!=8:
+ returnpsf
+
+ quad_row='l'
+ quad_col='l'
+ ifpos.y>euclidlike.n_pix_row/2:
+ quad_row='u'
+ ifpos.x>euclidlike.n_pix_col/2:
+ quad_col='u'
+ quad_pos=quad_col+quad_row
+ returnpsf[quad_pos]
+
+
+
+
+
+
+
+[docs]
+classPSFLoader(InputLoader):
+"""PSF loader."""
+
+ def__init__(self):
+ # Override some defaults in the base init.
+ super().__init__(init_func=EuclidlikePSF,takes_logger=True,use_proxy=False)
+
+
+[docs]
+ defgetKwargs(self,config,base,logger):
+ logger.debug("Get kwargs for PSF")
+
+ req={}
+ opt={
+ "n_waves":int,
+ }
+ ignore=["extra_aberrations"]
+
+ # If CCD is in base, then don't require it in the config file.
+ # (Presumably because using Euclidlike image type, which sets it there for convenience.)
+ if"CCD"inbase:
+ opt["CCD"]=int
+ else:
+ req["CCD"]=int
+
+ kwargs,safe=galsim.config.GetAllParams(
+ config,base,req=req,opt=opt,ignore=ignore
+ )
+
+ # If not given in kwargs, then it must have been in base, so this is ok.
+ if"CCD"notinkwargs:
+ kwargs["CCD"]=base["CCD"]
+
+ kwargs["extra_aberrations"]=galsim.config.ParseAberrations(
+ "extra_aberrations",config,base,"EuclidlikePSF"
+ )
+ kwargs["WCS"]=galsim.config.BuildWCS(
+ base["image"],"wcs",base,logger=logger
+ )
+ kwargs["bpass"]=galsim.config.BuildBandpass(
+ base["image"],"bandpass",base,logger
+ )[0]
+
+ logger.debug("kwargs = %s",kwargs)
+
+ returnkwargs,False
+
+
+
+
+# Register this as a valid type
+RegisterInputType("euclidlike_psf",PSFLoader())
+# RegisterObjectType('euclidlike_psf', BuildEuclidlikePSF, input_type='euclidlikepsf_loader')
+
+[docs]
+ defgetNObjects(self):
+"""
+ Return the number of GSObjects to render
+ """
+ returnlen(self.objects)
+
+
+
+[docs]
+ defgetApproxNObjects(self):
+"""
+ Return the approximate number of GSObjects to render, as set in
+ the class initializer.
+ """
+ returnself.getNObjects()
+
+
+
+[docs]
+ defgetWorldPos(self,index):
+"""
+ Return the sky coordinates of the skyCatalog object
+ corresponding to the specified index.
+
+ Parameters
+ ----------
+ index : int
+ Index of the (object_index, subcomponent) combination.
+
+ Returns
+ -------
+ galsim.CelestialCoord
+ """
+ skycat_obj=self.objects[index]
+ ra,dec=skycat_obj.ra,skycat_obj.dec
+ returngalsim.CelestialCoord(ra*galsim.degrees,dec*galsim.degrees)
+
+
+
+[docs]
+ defgetFlux(self,index,filter=None,mjd=None,exptime=None):
+"""
+ Return the flux associated to an object.
+
+ Parameters
+ ----------
+ index : int
+ Index of the object in the self.objects catalog.
+ filter : str, optional
+ Name of the filter for which the flux is computed. If None, use the
+ filter provided during initialization. [Default: None]
+ mjd : float, optional
+ Date of the observation in MJD format. If None, use the
+ mjd provided during initialization. [Default: None]
+ exptime : int or float, optional
+ Exposure time of the observation. If None, use the
+ exptime provided during initialization. [Default: None]
+
+ Returns
+ -------
+ flux
+ Computer flux at the given date for the requested exposure time and
+ filter.
+ """
+
+ iffilterisNone:
+ filter=self.bandpass.name
+ ifmjdisNone:
+ mjd=self.mjd
+ ifexptimeisNone:
+ exptime=self.exptime
+
+ skycat_obj=self.objects[index]
+ # We cache the SEDs for potential later use
+ self._seds=skycat_obj.get_observer_sed_components()
+ fori,sedinenumerate(self._seds.values()):
+ ifi==0:
+ sed_sum=sed
+ else:
+ sed_sum+=sed
+ raw_flux=skycat_obj.get_euclid_flux(
+ filter,
+ sed_sum,
+ mjd=mjd,
+ cache=False
+ )
+ ifhasattr(skycat_obj,"get_wl_params"):
+ _,_,mu=skycat_obj.get_wl_params()
+ else:
+ mu=1.
+ flux=raw_flux*mu*exptime*euclidlike.collecting_area
+
+ returnflux
+
+
+
+[docs]
+ defgetValue(self,index,field):
+"""
+ Return a skyCatalog value for the an object.
+
+ Parameters
+ ----------
+ index : int
+ Index of the object in the self.objects catalog.
+ field : str
+ Name of the field for which you want the value.
+
+ Returns
+ -------
+ int or float or str or None
+ The value associated to the field or None if the field do not exist.
+ """
+
+ skycat_obj=self.objects[index]
+
+ iffieldnotinself._dtype_dict:
+ # We cannot raise an error because one could have a field for snana
+ # in the config and we don't want to crash because there are no SN
+ # in this particular image. We then default to False which might not
+ # be the right type for the required column but we have no way of knowing
+ # the correct type if the column do not exist.
+ self.logger.warning(f"The field {field} was not found in skyCatalog.")
+ returnNone
+ eliffieldnotinskycat_obj.native_columns:
+ ifself._dtype_dict[field]isint:
+ # There are no "special value" for integer so we default to
+ # hopefully something completely off
+ return-9999
+ elifself._dtype_dict[field]isfloat:
+ returnnp.nan
+ elifself._dtype_dict[field]isstr:
+ returnNone
+ else:
+ returnskycat_obj.get_native_attribute(field)
+
+
+
+[docs]
+ defgetObj(self,index,gsparams=None,rng=None,exptime=30):
+"""
+ Return the galsim object for the skyCatalog object
+ corresponding to the specified index. If the skyCatalog
+ object is a galaxy, the returned galsim object will be
+ a galsim.Sum.
+
+ Parameters
+ ----------
+ index : int
+ Index of the object in the self.objects catalog.
+
+ Returns
+ -------
+ galsim.GSObject
+ """
+ ifnotself.objects:
+ raiseRuntimeError("Trying to get an object from an empty sky catalog")
+
+ faint=False
+ skycat_obj=self.objects[index]
+ gsobjs=skycat_obj.get_gsobject_components(gsparams)
+
+ # Compute the flux or get the cached value.
+ flux=self.getFlux(index)
+ ifnp.isnan(flux):
+ returnNone
+
+ # Set up simple SED if too faint
+ ifflux<40:
+ faint=True
+ ifnotfaint:
+ seds=skycat_obj.get_observer_sed_components(mjd=self.mjd)
+
+ gs_obj_list=[]
+ forcomponentingsobjs:
+ iffaint:
+ gsobjs[component]=gsobjs[component].evaluateAtWavelength(
+ self.bandpass
+ )
+ gs_obj_list.append(
+ gsobjs[component]
+ *self._trivial_sed
+ *self.exptime
+ *euclidlike.collecting_area
+ )
+ else:
+ ifcomponentinseds:
+ gs_obj_list.append(
+ gsobjs[component]
+ *seds[component]
+ *self.exptime
+ *euclidlike.collecting_area
+ )
+
+ ifnotgs_obj_list:
+ returnNone
+
+ iflen(gs_obj_list)==1:
+ gs_object=gs_obj_list[0]
+ else:
+ gs_object=galsim.Add(gs_obj_list)
+
+ # Give the object the right flux
+ gs_object.flux=flux
+ gs_object.withFlux(gs_object.flux,self.bandpass)
+
+ # Get the object type
+ if(skycat_obj.object_type=="diffsky_galaxy")|(
+ skycat_obj.object_type=="galaxy"
+ ):
+ gs_object.object_type="galaxy"
+ ifskycat_obj.object_type=="star":
+ gs_object.object_type="star"
+ ifskycat_obj.object_type=="snana":
+ gs_object.object_type="transient"
+
+ returngs_object
+
+
+
+
+
+[docs]
+classSkyCatalogLoader(InputLoader):
+"""
+ Class to load SkyCatalogInterface object.
+ """
+
+
+[docs]
+ defgetKwargs(self,config,base,logger):
+ req={"file_name":str,"exptime":float}
+ opt={
+ "edge_pix":float,
+ "obj_types":list,
+ "mjd":float,
+ }
+ kwargs,safe=galsim.config.GetAllParams(config,base,req=req,opt=opt)
+ wcs=galsim.config.BuildWCS(base["image"],"wcs",base,logger=logger)
+ kwargs["wcs"]=wcs
+ kwargs["logger"]=logger
+
+ if"bandpass"notinconfig:
+ base["bandpass"]=galsim.config.BuildBandpass(
+ base["image"],"bandpass",base,logger=logger
+ )[0]
+
+ kwargs["bandpass"]=base["bandpass"]
+ # Sky catalog object lists are created per CCD, so they are
+ # not safe to reuse.
+ safe=False
+ returnkwargs,safe
+
+
+
+
+
+[docs]
+defSkyCatObj(config,base,ignore,gsparams,logger):
+"""
+ Build an object according to info in the sky catalog.
+ """
+ skycat=galsim.config.GetInputObj("sky_catalog",config,base,"SkyCatObj")
+
+ # Ensure that this sky catalog matches the CCD being simulated by
+ # comparing center locations on the sky.
+ world_center=base["world_center"]
+ ccd_center=skycat.get_ccd_center()
+ sep=ccd_center.distanceTo(base["world_center"])/galsim.arcsec
+ # Centers must agree to within at least 1 arcsec:
+ ifsep>1.0:
+ message=(
+ "skyCatalogs selection and CCD center do not agree: \n"
+ "skycat.ccd_center: "
+ f"{ccd_center.ra/galsim.degrees:.5f}, "
+ f"{ccd_center.dec/galsim.degrees:.5f}\n"
+ "world_center: "
+ f"{world_center.ra/galsim.degrees:.5f}, "
+ f"{world_center.dec/galsim.degrees:.5f}\n"
+ f"Separation: {sep:.2e} arcsec"
+ )
+ raiseRuntimeError(message)
+
+ # Setup the indexing sequence if it hasn't been specified. The
+ # normal thing with a catalog is to just use each object in order,
+ # so we don't require the user to specify that by hand. We can do
+ # it for them.
+ galsim.config.SetDefaultIndex(config,skycat.getNObjects())
+
+ req={"index":int}
+ opt={"num":int}
+ kwargs,safe=galsim.config.GetAllParams(config,base,req=req,opt=opt)
+ index=kwargs["index"]
+
+ rng=galsim.config.GetRNG(config,base,logger,"SkyCatObj")
+
+ obj=skycat.getObj(index,gsparams=gsparams,rng=rng)
+ base["object_id"]=skycat.objects[index].id
+
+ returnobj,safe
+
+
+
+
+[docs]
+defSkyCatWorldPos(config,base,value_type):
+"""Return a value from the object part of the skyCatalog"""
+ skycat=galsim.config.GetInputObj("sky_catalog",config,base,"SkyCatWorldPos")
+
+ # Setup the indexing sequence if it hasn't been specified. The
+ # normal thing with a catalog is to just use each object in order,
+ # so we don't require the user to specify that by hand. We can do
+ # it for them.
+ galsim.config.SetDefaultIndex(config,skycat.getNObjects())
+
+ req={"index":int}
+ opt={"num":int}
+ kwargs,safe=galsim.config.GetAllParams(config,base,req=req,opt=opt)
+ index=kwargs["index"]
+
+ pos=skycat.getWorldPos(index)
+ returnpos,safe
+
+
+
+
+[docs]
+defSkyCatValue(config,base,value_type):
+"""Return a value from the object part of the skyCatalog
+ """
+
+ skycat=galsim.config.GetInputObj("sky_catalog",config,base,"SkyCatValue")
+
+ # Setup the indexing sequence if it hasn't been specified. The
+ # normal thing with a catalog is to just use each object in order,
+ # so we don't require the user to specify that by hand. We can do
+ # it for them.
+ galsim.config.SetDefaultIndex(config,skycat.getNObjects())
+
+ req={"field":str,"index":int}
+ opt={"obs_kind":str}
+ params,safe=galsim.config.GetAllParams(config,base,req=req,opt=opt)
+ field=params["field"]
+ index=params["index"]
+ obs_kind=params.get("obs_kind",None)
+
+ iffield=="flux":
+ ifobs_kindisNone:
+ val=skycat.getFlux(index)
+ else:
+ pointing=galsim.config.GetInputObj("obseq_data",config,base,"OpSeqDataLoader")
+ filter=pointing.get("filter",obs_kind=obs_kind)
+ exptime=pointing.get("exptime",obs_kind=obs_kind)
+ mjd=pointing.get("mjd",obs_kind=obs_kind)
+ val=skycat.getFlux(index,filter=filter,exptime=exptime,mjd=mjd)
+ else:
+ val=skycat.getValue(index,field)
+
+ returnval,safe
+
+
+
+
+RegisterInputType("sky_catalog",SkyCatalogLoader(SkyCatalogInterface,has_nobj=True))
+RegisterObjectType("SkyCatObj",SkyCatObj,input_type="sky_catalog")
+RegisterValueType(
+ "SkyCatWorldPos",SkyCatWorldPos,[galsim.CelestialCoord],input_type="sky_catalog"
+)
+
+# Here we have to provide None as a type otherwise Galsim complains but I don't know why..
+RegisterValueType(
+ "SkyCatValue",SkyCatValue,[float,int,str,None]# , input_type="sky_catalog"
+)
+
+importnumpyasnp
+importgalsim
+importeuclidlike
+importgalsim.config
+fromgalsim.configimportRegisterStampType,StampBuilder
+# import os, psutil
+# process = psutil.Process()
+
+# Parameter below determines the flux at which we switch from using Euclid-like PSF to the bright star PSF
+# This is done to avoid visual artifacts from using the Euclid-like PSF for very bright objects.
+# Tests to determine this value can be found here: https://github.com/GalSim-developers/GalSim-Euclid-Like/issues/46
+psf_switch_limit=8e5
+
+
+[docs]
+classEuclidlike_stamp(StampBuilder):
+"""This performs the tasks necessary for building the stamp for a single object.
+
+ It uses the regular Basic functions for most things.
+ It specializes the quickSkip, buildProfile, and draw methods.
+ """
+ _trivial_sed=galsim.SED(
+ galsim.LookupTable(
+ [100,2600],
+ [1,1],
+ interpolant='linear'
+ ),
+ wave_type='nm',flux_type='fphotons'
+ )
+
+
+[docs]
+ defsetup(self,config,base,xsize,ysize,ignore,logger):
+"""
+ Do the initialization and setup for building a postage stamp.
+
+ In the base class, we check for and parse the appropriate size and position values in
+ config (aka base['stamp'] or base['image'].
+
+ Values given in base['stamp'] take precedence if these are given in both places (which
+ would be confusing, so probably shouldn't do that, but there might be a use case where it
+ would make sense).
+
+ Parameters:
+ config: The configuration dict for the stamp field.
+ base: The base configuration dict.
+ xsize: The xsize of the image to build (if known).
+ ysize: The ysize of the image to build (if known).
+ ignore: A list of parameters that are allowed to be in config that we can
+ ignore here. i.e. it won't be an error if these parameters are present.
+ logger: A logger object to log progress.
+
+ Returns:
+ xsize, ysize, image_pos, world_pos
+ """
+ # print('stamp setup',process.memory_info().rss)
+
+ gal=galsim.config.BuildGSObject(base,'gal',logger=logger)[0]
+ ifgalisNone:
+ raisegalsim.config.SkipThisObject('gal is None (invalid parameters)')
+ base['object_type']=gal.object_type
+ bandpass=base['bandpass']
+ ifnothasattr(gal,'flux'):
+ # In this case, the object flux has not been precomputed
+ # or cached by the skyCatalogs code.
+ gal.flux=gal.calculateFlux(bandpass)
+ self.flux=gal.flux
+ # Cap (star) flux at 30M photons to avoid gross artifacts when trying to draw the Euclid PSF in finite time and memory
+ flux_cap=3e7
+ ifself.flux>flux_cap:
+ if(hasattr(gal,'original')andhasattr(gal.original,'original')andisinstance(gal.original.original,galsim.DeltaFunction))or(isinstance(gal,galsim.DeltaFunction)):
+ gal=gal.withFlux(flux_cap,bandpass)
+ self.flux=flux_cap
+ gal.flux=flux_cap
+ base['flux']=gal.flux
+ base['mag']=-2.5*np.log10(gal.flux)+bandpass.zeropoint
+ # print('stamp setup2',process.memory_info().rss)
+
+ # Compute or retrieve the realized flux.
+ self.rng=galsim.config.GetRNG(config,base,logger,"Euclidlike_stamp")
+ self.realized_flux=galsim.PoissonDeviate(self.rng,mean=self.flux)()
+ base['realized_flux']=self.realized_flux
+
+ # Check if the realized flux is 0.
+ ifself.realized_flux==0:
+ # If so, we'll skip everything after this.
+ # The mechanism within GalSim to do this is to raise a special SkipThisObject class.
+ raisegalsim.config.SkipThisObject('realized flux=0')
+
+ # Otherwise figure out the stamp size
+ ifself.flux<10:
+ # For really faint things, don't try too hard. Just use 32x32.
+ image_size=32
+ self.pupil_bin='achromatic'
+
+ else:
+ gal_achrom=gal.evaluateAtWavelength(bandpass.effective_wavelength)
+ if(hasattr(gal_achrom,'original')andisinstance(gal_achrom.original,galsim.DeltaFunction)):
+ # For bright stars, set the following stamp size limits
+ ifself.flux<psf_switch_limit:
+ image_size=500
+ self.pupil_bin=8
+ elifself.flux<6e6:
+ image_size=1000
+ self.pupil_bin=4
+ else:
+ image_size=1600
+ self.pupil_bin=2
+ else:
+ self.pupil_bin=8
+ # # Get storead achromatic PSF
+ # psf = galsim.config.BuildGSObject(base, 'psf', logger=logger)[0]['achromatic']
+ # obj = galsim.Convolve(gal_achrom, psf).withFlux(self.flux)
+ obj=gal_achrom.withGSParams(galsim.GSParams(stepk_minimum_hlr=20))
+ image_size=obj.getGoodImageSize(euclidlike.pixel_scale)
+
+ # print('stamp setup3',process.memory_info().rss)
+ base['pupil_bin']=self.pupil_bin
+ logger.info('Object flux is %d',self.flux)
+ logger.info('Object %d will use stamp size = %s',base.get('obj_num',0),image_size)
+
+ # Determine where this object is going to go:
+ # This is the same as what the base StampBuilder does:
+ if'image_pos'inconfig:
+ image_pos=galsim.config.ParseValue(config,'image_pos',base,galsim.PositionD)[0]
+ else:
+ image_pos=None
+
+ if'world_pos'inconfig:
+ world_pos=galsim.config.ParseWorldPos(config,'world_pos',base,logger)
+ else:
+ world_pos=None
+
+ returnimage_size,image_size,image_pos,world_pos
+
+
+
+[docs]
+ defbuildPSF(self,config,base,gsparams,logger):
+"""Build the PSF object.
+
+ For the Basic stamp type, this builds a PSF from the base['psf'] dict, if present,
+ else returns None.
+
+ Parameters:
+ config: The configuration dict for the stamp field.
+ base: The base configuration dict.
+ gsparams: A dict of kwargs to use for a GSParams. More may be added to this
+ list by the galaxy object.
+ logger: A logger object to log progress.
+
+ Returns:
+ the PSF
+ """
+ ifbase.get('psf',{}).get('type','euclidlike_psf')!='euclidlike_psf':
+ returngalsim.config.BuildGSObject(base,'psf',gsparams=gsparams,logger=logger)[0]
+
+ euclidlike_psf=galsim.config.GetInputObj('euclidlike_psf',config,base,'buildPSF')
+ psf=euclidlike_psf.getPSF(self.pupil_bin,base['image_pos'])
+ returnpsf
+
+
+
+[docs]
+ defgetDrawMethod(self,config,base,logger):
+"""Determine the draw method to use.
+
+ @param config The configuration dict for the stamp field.
+ @param base The base configuration dict.
+ @param logger A logger object to log progress.
+
+ @returns method
+ """
+ method=galsim.config.ParseValue(config,'draw_method',base,str)[0]
+ ifmethodnotingalsim.config.valid_draw_methods:
+ raisegalsim.GalSimConfigValueError("Invalid draw_method.",method,
+ galsim.config.valid_draw_methods)
+ ifmethod=='auto':
+ ifself.pupil_binin[4,2]:
+ logger.info('Auto -> Use FFT drawing for object %d.',base['obj_num'])
+ return'fft'
+ else:
+ logger.info('Auto -> Use photon shooting for object %d.',base['obj_num'])
+ return'phot'
+ else:
+ # If user sets something specific for the method, rather than auto,
+ # then respect their wishes.
+ logger.info('Use specified method=%s for object %d.',method,base['obj_num'])
+ returnmethod
+
+
+
+[docs]
+ @classmethod
+ deffix_seds(cls,prof,bandpass):
+ # If any SEDs are not currently using a LookupTable for the function or if they are
+ # using spline interpolation, then the codepath is quite slow.
+ # Better to fix them before doing WavelengthSampler.
+
+ if(isinstance(prof,galsim.SimpleChromaticTransformation)and
+ (notisinstance(prof._flux_ratio._spec,galsim.LookupTable)
+ orprof._flux_ratio._spec.interpolant!='linear')):
+ original=prof._original
+ sed=prof._flux_ratio
+ wave_list,_,_=galsim.utilities.combine_wave_list(sed,bandpass)
+ f=np.broadcast_to(sed(wave_list),wave_list.shape)
+ new_spec=galsim.LookupTable(wave_list,f,interpolant='linear')
+ new_sed=galsim.SED(
+ new_spec,
+ 'nm',
+ 'fphotons'ifsed.spectralelse'1'
+ )
+ prof._flux_ratio=new_sed
+
+ # Also recurse onto any components.
+ ifisinstance(prof,galsim.ChromaticObject):
+ ifhasattr(prof,'obj_list'):
+ forobjinprof.obj_list:
+ cls.fix_seds(obj,bandpass)
+ ifhasattr(prof,'original'):
+ cls.fix_seds(prof.original,bandpass)
+
+
+
+[docs]
+ defdraw(self,prof,image,method,offset,config,base,logger):
+"""Draw the profile on the postage stamp image.
+
+ Parameters:
+ prof: The profile to draw.
+ image: The image onto which to draw the profile (which may be None).
+ method: The method to use in drawImage.
+ offset: The offset to apply when drawing.
+ config: The configuration dict for the stamp field.
+ base: The base configuration dict.
+ logger: A logger object to log progress.
+
+ Returns:
+ the resulting image
+ """
+ ifprofisNone:
+ # If was decide to do any rejection steps, this could be set to None, in which case,
+ # don't draw anything.
+ returnimage
+
+ # Prof is normally a convolution here with obj_list being [gal, psf1, psf2,...]
+ # for some number of component PSFs.
+ # print('stamp draw',process.memory_info().rss)
+
+ gal,*psfs=prof.obj_listifhasattr(prof,'obj_list')else[prof]
+
+ faint=self.flux<40
+ bandpass=base['bandpass']
+ iffaint:
+ logger.info("Flux = %.0f Using trivial sed",self.flux)
+ else:
+ self.fix_seds(gal,bandpass)
+
+ image.wcs=base['wcs']
+
+ # Set limit on the size of photons batches to consider when
+ # calling gsobject.drawImage.
+ maxN=int(1e6)
+ if'maxN'inconfig:
+ maxN=galsim.config.ParseValue(config,'maxN',base,int)[0]
+ # print('stamp draw2',process.memory_info().rss)
+
+ ifmethod=='fft':
+ fft_image=image.copy()
+ fft_offset=offset
+ kwargs=dict(
+ method='fft',
+ offset=fft_offset,
+ image=fft_image,
+ )
+ ifnotfaintandconfig.get('fft_photon_ops'):
+ kwargs.update({
+ "photon_ops":galsim.config.BuildPhotonOps(config,'fft_photon_ops',base,logger),
+ "maxN":maxN,
+ "rng":self.rng,
+ "n_subsample":1,
+ })
+
+ # Go back to a combined convolution for fft drawing.
+ gal=gal.withFlux(self.flux,bandpass)
+ prof=galsim.Convolve([gal]+psfs)
+ try:
+ prof.drawImage(bandpass,**kwargs)
+ exceptgalsim.errors.GalSimFFTSizeErrorase:
+ # I think this shouldn't happen with the updates I made to how the image size
+ # is calculated, even for extremely bright things. So it should be ok to
+ # just report what happened, give some extra information to diagonose the problem
+ # and raise the error.
+ logger.error('Caught error trying to draw using FFT:')
+ logger.error('%s',e)
+ logger.error('You may need to add a gsparams field with maximum_fft_size to')
+ logger.error('either the psf or gal field to allow larger FFTs.')
+ logger.info('prof = %r',prof)
+ logger.info('fft_image = %s',fft_image)
+ logger.info('offset = %r',offset)
+ logger.info('wcs = %r',image.wcs)
+ raise
+ # Some pixels can end up negative from FFT numerics. Just set them to 0.
+ fft_image.array[fft_image.array<0]=0.
+ fft_image.addNoise(galsim.PoissonNoise(rng=self.rng))
+ # In case we had to make a bigger image, just copy the part we need.
+ image+=fft_image[image.bounds]
+
+ else:
+ # We already calculated realized_flux above. Use that now and tell GalSim not
+ # recalculate the Poisson realization of the flux.
+ gal=gal.withFlux(self.realized_flux,bandpass)
+ # print('stamp draw3b ',process.memory_info().rss)
+
+ ifnotfaintand'photon_ops'inconfig:
+ photon_ops=galsim.config.BuildPhotonOps(config,'photon_ops',base,logger)
+ else:
+ photon_ops=[]
+
+ # Put the psfs at the start of the photon_ops.
+ # Probably a little better to put them a bit later than the start in some cases
+ # (e.g. after TimeSampler, PupilAnnulusSampler), but leave that as a todo for now.
+ photon_ops=psfs+photon_ops
+
+ # prof = galsim.Convolve([gal] + psfs)
+
+ # print('-------- gal ----------',gal)
+ # print('-------- psf ----------',psfs)
+
+ # print('stamp draw3a',process.memory_info().rss)
+ gal.drawImage(bandpass,
+ method='phot',
+ offset=offset,
+ rng=self.rng,
+ maxN=maxN,
+ n_photons=self.realized_flux,
+ image=image,
+ photon_ops=photon_ops,
+ sensor=None,
+ add_to_image=True,
+ poisson_flux=False)
+ # print('stamp draw3',process.memory_info().rss)
+
+ returnimage
+
+
+
+
+# Register this as a valid type
+RegisterStampType('Euclidlike_stamp',Euclidlike_stamp())
+
+[docs]
+classroman_utils(object):
+"""
+ Class to contain a variety of helper routines to work with the simulation data.
+ """
+ def__init__(self,config_file,visit=None,sca=None,image_name=None,setup_skycat=False):
+"""
+ Setup information about a simulated Roman image.
+ Parameters:
+ config_file: the GalSim config file that produced the simulation
+ visit: the visit (observation sequence) number of the pointing
+ sca: the SCA number
+ image_name: the filename of the image (can be used instead of visit, sca)
+ setup_skycat: setup the skycatalog information to have access to
+ """
+ config=galsim.config.ReadConfig(config_file)[0]
+
+ self.visit,self.sca=self.check_input(visit,sca,image_name)
+
+ ifnotsetup_skycat:
+ delconfig['input']['sky_catalog']
+ config['input']['obseq_data']['visit']=self.visit
+ config['image']['SCA']=self.sca
+ galsim.config.ProcessInput(config)
+ ifsetup_skycat:
+ self.skycat=galsim.config.GetInputObj('sky_catalog',config['input']['sky_catalog'],config,'sky_catalog')
+ self.PSF=galsim.config.GetInputObj('roman_psf',config['input']['roman_psf'],config,'roman_psf')
+ self.wcs=galsim.config.BuildWCS(config['image'],'wcs',config)
+ self.bpass=galsim.config.BuildBandpass(config['image'],'bandpass',config,None)[0]
+ self.photon_ops=galsim.config.BuildPhotonOps(config['stamp'],'photon_ops',config,None)
+ self.rng=galsim.config.GetRNG(config,config['image'],None,"psf_image")
+
+
+[docs]
+ defcheck_input(self,visit,sca,image_name):
+ ifimage_nameisnotNone:
+ print('Inferring visit and sca from image_name.')
+ start=21
+ end=-5
+ if'simple_model'inimage_name:
+ start=28
+ if'gz'inimage_name:
+ end=-8
+ tmp=np.array(image_name[start:end].split('_')).astype(int)
+ returntmp[0],tmp[1]
+ if(visitisNone)|(scaisNone):
+ raiseValueError('Insufficient information to construct visit info - all inputs are None.')
+ returnvisit,sca
+
+
+
+[docs]
+ defgetPSF(self,x=None,y=None,pupil_bin=8):
+"""
+ Return Roman PSF for some image position.
+ Parameters:
+ x: x-position in SCA
+ y: y-position in SCA
+ pupil_bin: pupil image binning factor
+ Returns:
+ the chromatic GalSim PSF model object (does not include additional effects like charge diffusion!)
+ """
+ ifpupil_bin!=8:
+ if(xisnotNone)|(yisnotNone):
+ raiseValueError('x,y position for pupil_bin values other than 8 not supported. Using SCA center.')
+ returnself.PSF.getPSF(pupil_bin,galsim.PositionD(roman.n_pix/2,roman.n_pix/2))
+ if(xisNone)|(yisNone):
+ returnself.PSF.getPSF(8,galsim.PositionD(roman.n_pix/2,roman.n_pix/2))
+ returnself.PSF.getPSF(8,galsim.PositionD(x,y))
+
+
+
+[docs]
+ defgetWCS(self):
+"""
+ Return Roman WCS for image
+ """
+ returnself.wcs
+
+
+
+[docs]
+ defgetLocalWCS(self,x,y):
+"""
+ Return Roman WCS for image
+ """
+ returnself.wcs.local(galsim.PositionD(x,y))
+
+
+
+[docs]
+ defgetBandpass(self):
+"""
+ Return Roman bandpass for image
+ """
+ returnself.bpass
+
+
+
+[docs]
+ defgetPSF_Image(self,stamp_size,x=None,y=None,pupil_bin=8,sed=None,
+ oversampling_factor=1,include_photonOps=False,n_phot=1e6):
+"""
+ Return a Roman PSF image for some image position
+ Parameters:
+ stamp_size: size of output PSF model stamp in native roman pixel_scale (oversampling_factor=1)
+ x: x-position in SCA
+ y: y-position in SCA
+ pupil_bin: pupil image binning factor
+ sed: SED to be used to draw the PSF - default is a flat SED.
+ oversampling_factor: factor by which to oversample native roman pixel_scale
+ include_photonOps: include additional contributions from other photon operators in effective psf image
+ Returns:
+ the PSF GalSim image object (use image.array to get a numpy array representation)
+ """
+ ifsedisNone:
+ sed=galsim.SED(galsim.LookupTable([100,2600],[1,1],interpolant='linear'),
+ wave_type='nm',flux_type='fphotons')
+ point=galsim.DeltaFunction()*sed
+ point=point.withFlux(1,self.bpass)
+ local_wcs=self.getLocalWCS(x,y)
+ wcs=galsim.JacobianWCS(dudx=local_wcs.dudx/oversampling_factor,
+ dudy=local_wcs.dudy/oversampling_factor,
+ dvdx=local_wcs.dvdx/oversampling_factor,
+ dvdy=local_wcs.dvdy/oversampling_factor)
+ stamp=galsim.Image(stamp_size*oversampling_factor,stamp_size*oversampling_factor,wcs=wcs)
+ ifnotinclude_photonOps:
+ psf=galsim.Convolve(point,self.getPSF(x,y,pupil_bin))
+ returnpsf.drawImage(self.bpass,image=stamp,wcs=wcs,method='no_pixel')
+ photon_ops=[self.getPSF(x,y,pupil_bin)]+self.photon_ops
+ returnpoint.drawImage(self.bpass,
+ method='phot',
+ rng=self.rng,
+ maxN=1e6,
+ n_photons=1e6,
+ image=stamp,
+ photon_ops=photon_ops,
+ poisson_flux=False)
+
+"""
+A program to download the Euclid-like PSF sampled across the focal plane for discrete wavelengths.
+The code below was adapted from: https://github.com/GalSim-developers/GalSim/blob/releases/2.5/galsim/download_cosmos.py
+"""
+
+importos,sys,zipfile,subprocess,shutil,json
+importargparse
+importlogging
+fromurllib.requestimporturlopen
+
+fromgalsim._versionimport__version__asversion
+#from .meta_data import share_dir
+fromgalsim.utilitiesimportensure_dir
+fromgalsim.mainimportmake_logger
+
+script_name='euclidlike_download_psf'
+script_dir=os.path.dirname(__file__)
+share_dir=os.path.join(script_dir,'..','euclidlike','data')
+
+
+[docs]
+defparse_args(command_args):
+"""Handle the command line arguments using either argparse (if available) or optparse.
+ """
+ # Another potential option we might want to add is to download the smaller training sample
+ # rather than the full 4 GB file. Right now, this just downloads the larger catalog.
+
+ # Short description strings common to both parsing mechanisms
+ description="This program will download the Euclid-like PSF \n"
+ description+="and place them in the GalSim-Euclid-Like data directory so they can be used as\n "
+ description+="the default files for the euclid-like PSF.\n"
+ description+="See https://github.com/GalSim-developers/GalSim-Euclid-Like/wiki/Euclid-Like-PSF\n"
+ description+="for more details about the files being downloaded."
+ epilog="Note: The unpacked files total almost 4 GB in size!\n"
+
+ # Build the parser and add arguments
+ parser=argparse.ArgumentParser(description=description,epilog=epilog,add_help=True)
+ parser.add_argument(
+ '-v','--verbosity',type=int,action='store',default=2,choices=(0,1,2,3),
+ help='Integer verbosity level: min=0, max=3 [default=2]')
+ parser.add_argument(
+ '-f','--force',action='store_const',default=False,const=True,
+ help='Force overwriting the current file if one exists')
+ parser.add_argument(
+ '-q','--quiet',action='store_const',default=False,const=True,
+ help="Don't ask about re-downloading an existing file. (implied by verbosity=0)")
+ parser.add_argument(
+ '-u','--unpack',action='store_const',default=False,const=True,
+ help='Re-unpack the tar file if not downloading')
+ parser.add_argument(
+ '--save',action='store_const',default=False,const=True,
+ help="Save the tarball after unpacking.")
+ parser.add_argument(
+ '-d','--dir',action='store',default=None,
+ help="Install into an alternate directory and link from the euclidlike/data directory")
+ parser.add_argument(
+ '--nolink',action='store_const',default=False,const=True,
+ help="Don't link to the alternate directory from euclidlike/data")
+ args=parser.parse_args(command_args)
+ args.log_file=None
+ args.log_format=None
+
+ # Return the args
+ returnargs
+
+
+
+[docs]
+defget_input():# pragma: no cover
+ # A level of indirection to make it easier to test functions using input.
+ # This one isn't covered, since we always mock it.
+ returninput()
+
+
+# Based on recipe 577058: http://code.activestate.com/recipes/577058/
+
+[docs]
+defquery_yes_no(question,default="yes"):
+"""Ask a yes/no question via input() and return their answer.
+
+ "question" is a string that is presented to the user.
+ "default" is the presumed answer if the user just hits <Enter>.
+ It must be "yes" (the default), "no" or None (meaning
+ an answer is required of the user).
+
+ The "answer" return value is one of "yes" or "no".
+ """
+ valid={"yes":"yes","y":"yes","ye":"yes",
+ "no":"no","n":"no"}
+ ifdefault==None:
+ prompt=" [y/n] "
+ elifdefault=="yes":
+ prompt=" [Y/n] "
+ elifdefault=="no":
+ prompt=" [y/N] "
+ else:
+ raiseValueError("invalid default answer: '%s'"%default)
+
+ while1:
+ sys.stdout.write(question+prompt)
+ choice=get_input().lower()
+ ifdefaultisnotNoneandchoice=='':
+ choice=default
+ break
+ elifchoiceinvalid.keys():
+ break
+ else:
+ sys.stdout.write("Please respond with 'yes' or 'no' "\
+ "(or 'y' or 'n').\n")
+ returnvalid[choice]
+[docs]
+defget_meta(url,args,logger):
+ logger.info('')
+
+ # See how large the file to be downloaded is.
+ u=urlopen(url)
+ meta=u.info()
+ logger.debug("Meta information about url:\n%s",str(meta))
+ file_name=os.path.basename(url)
+ file_size=int(meta.get("Content-Length"))
+ logger.info("Size of %s: %d MBytes",file_name,file_size/1024**2)
+
+ returnmeta
+
+
+
+[docs]
+defcheck_existing(target,unpack_dir,meta,args,logger):
+ # Make sure the directory we want to put this file exists.
+ ensure_dir(target)
+
+ do_download=True
+ # If file already exists
+ ifos.path.isfile(target):
+ file_size=int(meta.get("Content-Length"))
+ logger.info("")
+ existing_file_size=os.path.getsize(target)
+ ifargs.force:
+ logger.info("Target file already exists. Size = %d MBytes. Forced re-download.",
+ existing_file_size/1024**2)
+ eliffile_size==existing_file_size:
+ ifargs.quiet:
+ logger.info("Target file already exists. Not re-downloading.")
+ do_download=False
+ else:
+ q="Target file already exists. Overwrite?"
+ yn=query_yes_no(q,default='no')
+ ifyn=='no':
+ do_download=False
+ else:
+ logger.warning("Target file already exists, but it seems to be either incomplete, "
+ "corrupt, or obsolete")
+ ifargs.quiet:
+ logger.info("Size of existing file = %d MBytes. Re-downloading.",
+ existing_file_size/1024**2)
+ else:
+ q="Size of existing file = %d MBytes. Re-download?"%(existing_file_size/1024**2)
+ yn=query_yes_no(q,default='yes')
+ ifyn=='no':
+ do_download=False
+ elifunpack_dirisnotNoneandos.path.isdir(unpack_dir):
+ logger.info("")
+
+ # Check that this is the current version.
+ meta_file=os.path.join(unpack_dir,'meta.json')
+ logger.debug('meta_file = %s',meta_file)
+ ifos.path.isfile(meta_file):
+ logger.debug('meta_file exists')
+ withopen(meta_file)asfp:
+ saved_meta_dict=json.load(fp)
+ # Get rid of the unicode
+ saved_meta_dict=dict([(str(k),str(v))fork,vinsaved_meta_dict.items()])
+ saved_meta_dict={k.lower():vfork,vinsaved_meta_dict.items()}
+ logger.debug("current meta information is %s",saved_meta_dict)
+ meta_dict=dict(meta)
+ meta_dict={k.lower():vfork,vinmeta_dict.items()}
+ logger.debug("url's meta information is %s",meta_dict)
+
+ # There are several checksum tags. If any of them are present and match,
+ # then file is current. If they don't match, file is obsolete.
+ # If none are present, then they changed something, so do a longer check.
+ # (And try to get around to updating this list of checksum keys.)
+ checksum_keys=['oc-checksum','content-md5','etag']
+ obsolete=None
+ forkinchecksum_keys:
+ ifkinsaved_meta_dictandkinmeta_dict:
+ ifsaved_meta_dict[k]==meta_dict[k]:
+ logger.info("Checksum key %s matches. File is up to date."%k)
+ obsolete=False
+ else:
+ logger.info("Checksum key %s doesn't matches. File is obsolete."%k)
+ obsolete=True
+ break
+
+ ifobsoleteisNone:
+ # Check all meta data. If it all matches, then it's fine.
+ # Otherwise, the file is obsolete.
+ obsolete=False
+ forkinmeta_dict:
+ # Skip some keys that don't imply obselescence.
+ ifk.startswith('x-')ork.startswith('retry')ork.startswith('set-cookie'):
+ continue
+ ifk=='date'ork=='last-modified'ork=='server':
+ continue
+ # Others that are missing or different imply obsolete
+ ifknotinsaved_meta_dict:
+ logger.debug("key %s is missing in saved meta information",k)
+ obsolete=True
+ elifmeta_dict[k]!=saved_meta_dict[k]:
+ logger.debug("key %s differs: %s != %s",k,meta_dict[k],saved_meta_dict[k])
+ obsolete=True
+ else:
+ logger.debug("key %s matches",k)
+ else:
+ logger.debug('meta_file does not exist')
+ obsolete=True
+
+ ifobsolete:
+ ifargs.quietorargs.force:
+ logger.warning("The version currently on disk is obsolete. "
+ "Downloading new version.")
+ else:
+ q="The version currently on disk is obsolete. Download new version?"
+ yn=query_yes_no(q,default='yes')
+ ifyn=='no':
+ do_download=False
+ elifargs.force:
+ logger.info("Target file has already been downloaded and unpacked. "
+ "Forced re-download.")
+ elifargs.quiet:
+ logger.info("Target file has already been downloaded and unpacked. "
+ "Not re-downloading.")
+ args.save=True# Don't delete it!
+ do_download=False
+ else:
+ q="Target file has already been downloaded and unpacked. Re-download?"
+ yn=query_yes_no(q,default='no')
+ ifyn=='no':
+ args.save=True
+ do_download=False
+ returndo_download
+
+
+
+[docs]
+defdownload(do_download,url,target,meta,args,logger):
+ ifnotdo_download:return
+ logger.info("")
+ u=urlopen(url)
+ # This bit is based on one of the answers here: (by PabloG)
+ # http://stackoverflow.com/questions/22676/how-do-i-download-a-file-over-http-using-python
+ # The progress feature in that answer is important here, since downloading such a large file
+ # will take a while.
+ file_size=int(meta.get("Content-Length"))
+ try:
+ withopen(target,'wb')asf:
+ file_size_dl=0
+ block_sz=32*1024
+ next_dot=file_size/100.# For verbosity==1, the next size for writing a dot.
+ whileTrue:
+ buffer=u.read(block_sz)
+ ifnotbuffer:
+ break
+
+ file_size_dl+=len(buffer)
+ f.write(buffer)
+
+ # Status bar
+ ifargs.verbosity>=2:
+ status=r"Downloading: %5d / %d MBytes [%3.2f%%]"%(
+ file_size_dl/1024**2,file_size/1024**2,file_size_dl*100./file_size)
+ status=status+'\b'*len(status)
+ sys.stdout.write(status)
+ sys.stdout.flush()
+ elifargs.verbosity>=1andfile_size_dl>next_dot:
+ sys.stdout.write('.')
+ sys.stdout.flush()
+ next_dot+=file_size/100.
+ logger.info("Download complete.")
+ logger.info("")
+ exceptOSErrorase:
+ # Try to give a reasonable suggestion for some common OSErrors.
+ logger.error("\n\nOSError: %s",str(e))
+ if'Permission denied'instr(e):
+ logger.error("Rerun using sudo %s",script_name)
+ logger.error("If this is not possible, you can download to an alternate location:")
+ logger.error(" %s -d dir_name --nolink\n",script_name)
+ elif'Disk quota'instr(e)or'No space'instr(e):
+ logger.error("You might need to download this in an alternate location and link:")
+ logger.error(" %s -d dir_name\n",script_name)
+ raise
+
+
+
+[docs]
+defcheck_unpack(do_download,unpack_dir,target,args):
+ # Usually we unpack if we downloaded the tarball or if specified by the command line option.
+ do_unpack=do_downloadorargs.unpack
+
+ # If the unpack dir is missing, then need to unpack
+ ifnotos.path.exists(unpack_dir):
+ do_unpack=True
+
+ # But of course if there is no tarball, we can't unpack it
+ ifnotos.path.isfile(target):
+ do_unpack=False
+
+ # If we have a downloaded tar file, ask if it should be re-unpacked.
+ ifnotdo_unpackandnotargs.quietandos.path.isfile(target):
+ q="Zip file is already unpacked. Re-unpack?"
+ yn=query_yes_no(q,default='no')
+ ifyn=='yes':
+ do_unpack=True
+ returndo_unpack
+
+
+
+[docs]
+defunpack(do_unpack,target,target_dir,unpack_dir,meta,args,logger):
+ ifnotdo_unpack:return
+ logger.info("Unpacking the zip file...")
+ withzipfile.ZipFile(target,'r')aszip_ref:
+ ifargs.verbosity>=3:
+ zip_ref.printdir()
+ elifargs.verbosity>=2:
+ zip_ref.printdir()
+
+ defis_within_directory(directory,target):
+ abs_directory=os.path.abspath(directory)
+ abs_target=os.path.abspath(target)
+ prefix=os.path.commonprefix([abs_directory,abs_target])
+ returnprefix==abs_directory
+
+ defsafe_extract(zip_ref,path=".",members=None):
+ # This is somewhat gratuitous for our use case, since we directly control the zip file,
+ # but it's a good practice to avoid security vulnerabilities.
+ members=[]
+ formemberinzip_ref.namelist():
+ ifmember!='VIS_response.fits.gz':# not interested in downloading this file
+ members.append(member)
+ member_path=os.path.join(path,member)
+ ifnotis_within_directory(path,member_path):# pragma: no cover
+ raiseException("Attempted Path Traversal in Zip File")
+
+ zip_ref.extractall(path,members)
+
+ safe_extract(zip_ref,target_dir)
+
+ # Write the meta information to a file, meta.json to mark what version this all is.
+ meta_file=os.path.join(unpack_dir,'meta.json')
+ withopen(meta_file,'w')asfp:
+ json.dump(dict(meta),fp)
+
+ logger.info("Extracted contents of zip file.")
+ logger.info("")
+
+
+
+
+[docs]
+defcheck_remove(do_unpack,target,args):
+ # Usually, we remove the tarball if we unpacked it and command line doesn't specify to save it.
+ do_remove=do_unpackandnotargs.save
+
+ # But if we didn't unpack it, and they didn't say to save it, ask if we should remove it.
+ ifos.path.isfile(target)andnotdo_removeandnotargs.saveandnotargs.quiet:
+ q="Remove the zipfile?"
+ yn=query_yes_no(q,default='no')
+ ifyn=='yes':
+ do_remove=True
+ returndo_remove
+
+
+
+[docs]
+defremove_tarball(do_remove,target,logger):
+ ifdo_remove:
+ logger.info("Removing the zipfile to save space")
+ os.remove(target)
+
+
+
+[docs]
+defmake_link(do_link,unpack_dir,link_dir,args,logger):
+ ifnotdo_link:return
+ logger.debug("Linking to %s from %s",unpack_dir,link_dir)
+ ifos.path.lexists(link_dir):
+ ifos.path.islink(link_dir):
+ # If it exists and is a link, we just remove it and relink without any fanfare.
+ logger.debug("Removing existing link")
+ os.unlink(link_dir)
+ else:
+ # If it is not a link, we need to figure out what to do with it.
+ ifos.path.isdir(link_dir):
+ # If it's a directory, probably want to keep it.
+ logger.warning("%s already exists and is a directory.",link_dir)
+ ifargs.force:
+ logger.warning("Removing the existing files to make the link.")
+ elifargs.quiet:
+ logger.warning("Link cannot be made. (Use -f to force removal of existing dir.)")
+ return
+ else:
+ q="Remove the existing files to make the link?"
+ yn=query_yes_no(q,default='no')
+ ifyn=='no':
+ return
+ shutil.rmtree(link_dir)
+ else:
+ # If it's not a directory, it's probably corrupt, so the default is to remove it.
+ logger.warning("%s already exists, but strangely isn't a directory.",link_dir)
+ ifargs.forceorargs.quiet:
+ logger.warning("Removing the existing file.")
+ else:
+ q="Remove the existing file?"
+ yn=query_yes_no(q,default='yes')
+ ifyn=='no':
+ return
+ os.remove(link_dir)
+ os.symlink(os.path.abspath(unpack_dir),link_dir)
+ logger.info("Made link to %s from %s",unpack_dir,link_dir)
+
+
+
+
+[docs]
+defdownload_psf(args,logger):
+"""The main script given the ArgParsed args and a logger
+ """
+ # Give diagnostic about GalSim version
+ logger.debug("GalSim version: %s",version)
+ logger.debug("This download script is: %s",__file__)
+ logger.info("Type %s -h to see command line options.\n",script_name)
+
+ # Some definitions:
+ # share_dir is the base galsim share directory, e.g. /usr/local/share/galsim/
+ # url is the url from which we will download the tarball.
+ # target is the full path of the downloaded tarball
+ # target_dir is where we will put the downloaded file, usually == share_dir.
+ # link_dir is the directory where this would normally have been unpacked.
+ # unpack_dir is the directory that the tarball will unpack into.
+
+ # caution about storage and ask user if they want to proceed.
+ q="You are about to download 4Gb, do you want to proceed?"
+ yn=query_yes_no(q,default='no')
+ ifyn=='no':
+ return
+
+ url,target,target_dir,link_dir,unpack_dir,do_link=get_names(args,logger)
+
+ meta=get_meta(url,args,logger)
+
+ # Check if the file already exists and if it is the right size
+ do_download=check_existing(target,unpack_dir,meta,args,logger)
+
+ # Download the tarball
+ download(do_download,url,target,meta,args,logger)
+
+ # Unpack the tarball
+ do_unpack=check_unpack(do_download,unpack_dir,target,args)
+ unpack(do_unpack,target,target_dir,unpack_dir,meta,args,logger)
+
+ # Remove the tarball
+ do_remove=check_remove(do_unpack,target,args)
+ remove_tarball(do_remove,target,logger)
+
+ # If we are downloading to an alternate directory, we (usually) link to it from share/galsim
+ make_link(do_link,unpack_dir,link_dir,args,logger)
+
+
+
+[docs]
+defmain(command_args):
+"""The whole process given command-line parameters in their native (non-ArgParse) form.
+ """
+ args=parse_args(command_args)
+ logger=make_logger(args)
+ download_psf(args,logger)
+
+
+
+[docs]
+defrun_main():
+"""Kick off the process grabbing the command-line parameters from sys.argv
+ """
+ main(sys.argv[1:])
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/_build/html/_sources/euclidlike.rst.txt b/docs/_build/html/_sources/euclidlike.rst.txt
index c09a526..d4f2d47 100644
--- a/docs/_build/html/_sources/euclidlike.rst.txt
+++ b/docs/_build/html/_sources/euclidlike.rst.txt
@@ -1,53 +1,153 @@
-euclidlike package
-==================
+The Euclid-like Module
+################################
-Submodules
-----------
+The ``euclidlike`` module contains telescope information and functionality needed for image simulations.
+The demo script end_to_end_demo.py shows how to use many of the atrributes and functions described here.
-euclidlike.backgrounds module
------------------------------
-.. automodule:: euclidlike.backgrounds
- :members:
- :undoc-members:
- :show-inheritance:
+Module-level Attributes
+=======================
-euclidlike.bandpass module
---------------------------
+There are several of attributes of the ``euclidlike`` module which define some numerical
+parameters related to a Euclid-like geometry. Some of these parameters relate to the entire
+wide-field imager. Others, especially the return values of the functions to get the
+PSF and WCS, are specific to each CCD and therefore are indexed based on the detector number.
+All detector-related arrays are 0-indexed, which might differ from the CCD indices for Euclid,
+which run 1-1 to n_row-n_col.
-.. automodule:: euclidlike.bandpass
- :members:
- :undoc-members:
- :show-inheritance:
+gain
+ The gain for all CCDs is expected to be the roughly the same.
-euclidlike.euclidlike\_psf module
----------------------------------
+pixel_scale
+ The pixel scale in units of arcsec/pixel.
-.. automodule:: euclidlike.euclidlike_psf
- :members:
- :undoc-members:
- :show-inheritance:
+diameter
+ The telescope diameter in meters.
-euclidlike.euclidlike\_wcs module
----------------------------------
+obscuration
+ The linear obscuration of the telescope, expressed as a fraction of the diameter.
-.. automodule:: euclidlike.euclidlike_wcs
- :members:
- :undoc-members:
- :show-inheritance:
+collecting_area
+ The actual collecting area after accounting for obscuration, struts, etc. in
+ units of cm^2.
-euclidlike.instrument\_params module
-------------------------------------
+long_exptime
+ The typical exposure time for the longer exposures used for VIS images, in units of seconds.
+ The number that is stored is for a single dither.
-.. automodule:: euclidlike.instrument_params
- :members:
- :undoc-members:
- :show-inheritance:
+short_exptime_nisp
+ The typical exposure time for the shorter NISP imaging exposures used for VIS images, in units of seconds.
+ The number that is stored is for a single dither.
-Module contents
----------------
+short_exptime_vis
+ The typical exposure time for the shorter exposures with VIS taken in parallel with NISP imaging, in units of seconds.
+ The number that is stored is for a single dither.
-.. automodule:: euclidlike
- :members:
- :undoc-members:
- :show-inheritance:
+n_dithers
+ The number of dithers per filter.
+
+n_ccd
+ The number of CCDs in the focal plane.
+
+n_ccd_row
+ The number of CCDs in the each focal plane row.
+
+n_ccd_col
+ The number of CCDs in the each focal plane column.
+
+n_pix_row
+ Each CCD has n_pix_row total pixels in each row.
+n_pix_col
+ Each CCD has n_pix_col total pixels in each column.
+
+pixel_scale_mm
+ The physical pixel size, in units of mm.
+
+plate_scale
+ The plate scale, in units of arcsec / mm
+
+read_noise
+ Total readout noise, in units of e-.
+
+saturation
+ Pixel saturation, in units of e-.
+
+det2ccd
+ Mapping from DETID to CCDID.
+
+ccd2det
+ Mapping from CCDID to DETID.
+
+min_sun_angle
+ Minimum allowed angle from the telescope solar panels to the sun, in degrees.
+
+max_sun_angle
+ Maximum allowed angle from the telescope solar panels to the sun, in degrees.
+
+vis_bands
+ List of available VIS bands
+nisp_bands
+ List of available NISP bands
+
+vis_blue_limit
+ Bandpass blue limit needed for consistency with the wavelength range covered by ur tabulated
+ PSF images, in nm.
+
+vis_red_limit
+ Bandpass red limit needed for consistency with the wavelength range covered by ur tabulated
+ PSF images, in nm.
+
+For example, to get the gain value, use euclidlike.gain.
+
+#.. automodule:: euclidlike.instrument_params
+# :members:
+# :undoc-members:
+
+
+Euclid-like Functions
+===============
+
+This module also contains the following routines:
+
+`euclidlike.getBandpasses`
+ A utility to get a dictionary containing galsim.Bandpass objects for each of
+ the Euclid-like imaging bandpasses, which by default have AB zeropoints given using
+ the GalSim zeropoint convention (see `getBandpasses` docstring for more details).
+
+`euclidlike.getSkyLevel`
+ A utility to find the expected sky level due to zodiacal light at a given
+ position, in a given band.
+
+`euclidlike.getZodiBackground`
+ This helper routine is enables the calculation of the zodiacal light, in photons/m^2/arcsec^2/sec
+
+`euclidlike.getPSF`
+ A routine to get a chromatic representation of the PSF in a single CCD.
+ PSFs are based on precomputed, oversampled (by 3x) PSF images on a grid in wavelength and
+ focal plane position.
+
+`euclidlike.getBrightPSF`
+ Get a fake optical PSF for very bright objects.
+
+`euclidlike.getWCS`
+ This routine returns a dict containing a WCS for each of the Euclid CCDs.
+
+`euclidlike.findCCD`
+ This is a helper routine to calculate the minimum and maximum pixel values that should be
+ considered within a CCD.
+
+
+
+.. autofunction:: euclidlike.getBandpasses
+
+.. autofunction:: euclidlike.getSkyLevel
+
+.. autofunction:: euclidlike.getZodiBackground
+
+.. autofunction:: euclidlike.getPSF
+
+.. autofunction:: euclidlike.getBrightPSF
+
+.. autofunction:: euclidlike.getWCS
+
+.. autofunction:: euclidlike.findCCD
diff --git a/docs/_build/html/_sources/euclidlike_imsim.rst.txt b/docs/_build/html/_sources/euclidlike_imsim.rst.txt
index bc6626f..244a9b5 100644
--- a/docs/_build/html/_sources/euclidlike_imsim.rst.txt
+++ b/docs/_build/html/_sources/euclidlike_imsim.rst.txt
@@ -1,109 +1,64 @@
-euclidlike\_imsim package
-=========================
+The Euclid-like ImSim Module
+################################
-Submodules
-----------
+This module contains configuration scripts to produce large-scale Euclid-like simulation runs based on the information in ``euclidlike``.
+It is based heavily on `roman_imsim `_.
-euclidlike\_imsim.bandpass module
----------------------------------
+Classes and Functions
+=====================
.. automodule:: euclidlike_imsim.bandpass
:members:
:undoc-members:
- :show-inheritance:
-
-euclidlike\_imsim.ccd module
-----------------------------
.. automodule:: euclidlike_imsim.ccd
:members:
:undoc-members:
- :show-inheritance:
-
-euclidlike\_imsim.detector\_physics module
-------------------------------------------
.. automodule:: euclidlike_imsim.detector_physics
:members:
:undoc-members:
- :show-inheritance:
-
-euclidlike\_imsim.noise module
-------------------------------
.. automodule:: euclidlike_imsim.noise
:members:
:undoc-members:
- :show-inheritance:
-
-euclidlike\_imsim.obseq module
-------------------------------
.. automodule:: euclidlike_imsim.obseq
:members:
:undoc-members:
- :show-inheritance:
-
-euclidlike\_imsim.photonOps module
-----------------------------------
.. automodule:: euclidlike_imsim.photonOps
:members:
:undoc-members:
- :show-inheritance:
-
-euclidlike\_imsim.psf module
-----------------------------
.. automodule:: euclidlike_imsim.psf
:members:
:undoc-members:
- :show-inheritance:
-
-euclidlike\_imsim.scafile module
---------------------------------
.. automodule:: euclidlike_imsim.scafile
:members:
:undoc-members:
- :show-inheritance:
-
-euclidlike\_imsim.skycat module
--------------------------------
.. automodule:: euclidlike_imsim.skycat
:members:
:undoc-members:
- :show-inheritance:
-
-euclidlike\_imsim.stamp module
-------------------------------
.. automodule:: euclidlike_imsim.stamp
:members:
:undoc-members:
- :show-inheritance:
-
-euclidlike\_imsim.utils module
-------------------------------
.. automodule:: euclidlike_imsim.utils
:members:
:undoc-members:
- :show-inheritance:
-
-euclidlike\_imsim.wcs module
-----------------------------
.. automodule:: euclidlike_imsim.wcs
:members:
:undoc-members:
- :show-inheritance:
-Module contents
----------------
-.. automodule:: euclidlike_imsim
- :members:
- :undoc-members:
- :show-inheritance:
+Use
+====
+Example files needed for large-scale Euclid-like simulation runs are included in ``GalSim-Euclid-Like/config``.
+
+.. include:: ../config/README.rst
+
diff --git a/docs/_build/html/_sources/examples.rst.txt b/docs/_build/html/_sources/examples.rst.txt
new file mode 100644
index 0000000..07f898e
--- /dev/null
+++ b/docs/_build/html/_sources/examples.rst.txt
@@ -0,0 +1,29 @@
+Examples
+=========
+
+The ``GalSim-Euclid-Like/examples`` directory contains example files for how use the euclidlike module.
+
+End-to-end demo
+---------------
+
+`end_to_end_demo.py.py <../examples/end_to_end_demo.py>`_
+
+This first demo is the euclidlike-equivalent of `demo 13 `_ in ``GalSim``. This demo uses the Euclid-like PSF, WCS, and background noise to produce a realistic scene of galaxies and stars as observed from a Euclid-like Telescope.
+
+** Features introduced in the Python file**:
+
+- euclidlike.getBandpasses(AB_zeropoint)
+- euclidlike.getWCS(world_pos, CCDs_CCD, date)
+- euclidlike.getPSF(use_CCD, filter_name, wcs)
+- euclidlike.getSkyLevel(bandpass, world_pos)
+
+The output generated from this file can be visualized by running the script `end_to_end_demo.py.py <../examples/end_to_end_demo.py>`_
+
+
+Focal Plane Layout
+------------------
+
+`plot_VIS.py <../examples/plot_VIS.py>`_
+
+This Jupyter Notebook shows the display of the focal plane used in the euclidlike package, along with the CCD centers and ID convention.
+
diff --git a/docs/_build/html/_sources/history.rst.txt b/docs/_build/html/_sources/history.rst.txt
new file mode 100644
index 0000000..d35e38d
--- /dev/null
+++ b/docs/_build/html/_sources/history.rst.txt
@@ -0,0 +1,11 @@
+Revision History
+################
+
+.. include:: ../CHANGELOG.rst
+
+
+Older Versions
+==============
+
+.. toctree::
+ older
\ No newline at end of file
diff --git a/docs/_build/html/_sources/index.rst.txt b/docs/_build/html/_sources/index.rst.txt
index 55d881e..3f160f3 100644
--- a/docs/_build/html/_sources/index.rst.txt
+++ b/docs/_build/html/_sources/index.rst.txt
@@ -1,63 +1,24 @@
-GalSim-Euclid-Like
-==================
-
-Helper functions to generate simulations of Euclid-like images using GalSim.
-
-This repository contains information about the Euclid space telescope and survey that is needed to
-produce simulations using `GalSim `_. Some of the
-information provided is approximate, aimed towards fast simulations rather than full accuracy in
-representation of Euclid images. Places where the information is only approximate are flagged and
-described in the docstring, and we particularly highlight that the PSF is only approximate;
-for details, see the docstring of the ``getPSF()`` method. This library should enable generation of
-Euclid-like images of sufficient fidelity for preliminary exploration of object detection,
-photometry, deblending, and joint analysis with ground-based observatories. For
-applications requiring high precision such as weak lensing, the higher fidelity simulations
-available within the Euclid Consortium should be used.
-
-This repository includes two distinct packages:
-
-* ``euclidlike``: has basic observatory, instrumentation, and survey information for Euclid.
- This package can be used on its own along with GalSim to produce Euclid-like simulations.
-
-* ``euclidlike_imsim``: has configuration scripts to produce large-scale Euclid-like simulation runs
- based on the information in ``euclidlike``. It is based heavily on `roman_imsim `_.
-
-
-References
-==================
+.. GalSim-Euclid-Like documentation master file
+
-For more information about `GalSim `_, please see its README and documentation.
+GalSim-Euclid-Like: Euclid-like images using GalSim
+==============================================
-For more information about Euclid, please see the `Euclid Consortium website `_ and papers linked from there.
+.. toctree::
+ :maxdepth: 2
-Attribution for software and data used by particular routines in this library is given in the docstring for the relevant routine.
+ overview
+ install
+ euclidlike
+ euclidlike_imsim
+ examples
+ history
-Installation
-==================
-
-Please view the `installation instructions `_ for details on how to install GalSim-Euclid-Like.
-
-Downloading relevant data
-==================
-The Euclid-like PSF is constructed from precomputed oversampled images on a grid in focal plane position and wavelength. To use the full FOV PSF within GalSim-Euclid-Like, the images must be downloaded by running::
-
- $ euclidlike_download_psf
-
-in the terminal after installation of GalSim-Euclid-Like. To install in an alternative directory to the default, use the ``--dir`` argument. Refer to the ``getPSF`` documentation for further details about the PSF.
-Getting started
-==================
-Please see the examples/ directory for demos illustrating the use of this code.
-
-Communicating with the developers
+Indices and tables
==================
-Feel free to `open a GitHub issue `_ to reach the developers with questions, comments, and bug reports. New contributors are also welcome and can indicate their interest in developing this code base through the Issues.
-
-Attribution
-==================
-
-This software is open source and may be used according to the terms of its `license `_.
-
-When using this software, please provide the URL to the repository in the resulting paper or note. Once there is a Zenodo DOI or journal article, this README will be updated and we will ask those using the code in their research to cite the relevant journal article.
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/docs/_build/html/_sources/install.rst.txt b/docs/_build/html/_sources/install.rst.txt
new file mode 100644
index 0000000..63b62ca
--- /dev/null
+++ b/docs/_build/html/_sources/install.rst.txt
@@ -0,0 +1,4 @@
+Installation
+########
+
+.. include:: ../INSTALL.rst
\ No newline at end of file
diff --git a/docs/_build/html/_sources/modules.rst.txt b/docs/_build/html/_sources/modules.rst.txt
index 269fdea..dc909a1 100644
--- a/docs/_build/html/_sources/modules.rst.txt
+++ b/docs/_build/html/_sources/modules.rst.txt
@@ -1,9 +1,6 @@
-GalSim-Euclid-Like
-==================
-
.. toctree::
:maxdepth: 4
euclidlike
euclidlike_imsim
- scripts
+ scripts
\ No newline at end of file
diff --git a/docs/_build/html/_sources/overview.rst.txt b/docs/_build/html/_sources/overview.rst.txt
new file mode 100644
index 0000000..803a9ee
--- /dev/null
+++ b/docs/_build/html/_sources/overview.rst.txt
@@ -0,0 +1,4 @@
+Overview
+########
+
+.. include:: ../README.rst
\ No newline at end of file
diff --git a/docs/_build/html/_sources/scripts.rst.txt b/docs/_build/html/_sources/scripts.rst.txt
index 28a2437..11b271d 100644
--- a/docs/_build/html/_sources/scripts.rst.txt
+++ b/docs/_build/html/_sources/scripts.rst.txt
@@ -1,6 +1,11 @@
scripts package
===============
+.. automodule:: scripts
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
Submodules
----------
@@ -19,11 +24,3 @@ scripts.make\_euclidlike\_pupil\_plane module
:members:
:undoc-members:
:show-inheritance:
-
-Module contents
----------------
-
-.. automodule:: scripts
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/_build/html/euclidlike.html b/docs/_build/html/euclidlike.html
index ae374c6..f539977 100644
--- a/docs/_build/html/euclidlike.html
+++ b/docs/_build/html/euclidlike.html
@@ -5,7 +5,7 @@
- euclidlike package — GalSim-Euclid-Like 0.0.1 documentation
+ The Euclid-like Module — GalSim-Euclid-Like 0.0.1 documentation
@@ -14,6 +14,8 @@
+
+
@@ -31,17 +33,142 @@
The euclidlike module contains telescope information and functionality needed for image simulations.
+The demo script end_to_end_demo.py shows how to use many of the atrributes and functions described here.
There are several of attributes of the euclidlike module which define some numerical
+parameters related to a Euclid-like geometry. Some of these parameters relate to the entire
+wide-field imager. Others, especially the return values of the functions to get the
+PSF and WCS, are specific to each CCD and therefore are indexed based on the detector number.
+All detector-related arrays are 0-indexed, which might differ from the CCD indices for Euclid,
+which run 1-1 to n_row-n_col.
+
+
gain
The gain for all CCDs is expected to be the roughly the same.
+
+
pixel_scale
The pixel scale in units of arcsec/pixel.
+
+
diameter
The telescope diameter in meters.
+
+
obscuration
The linear obscuration of the telescope, expressed as a fraction of the diameter.
+
+
collecting_area
+
The actual collecting area after accounting for obscuration, struts, etc. in
units of cm^2.
+
+
+
+
long_exptime
The typical exposure time for the longer exposures used for VIS images, in units of seconds.
+The number that is stored is for a single dither.
+
+
short_exptime_nisp
The typical exposure time for the shorter NISP imaging exposures used for VIS images, in units of seconds.
+The number that is stored is for a single dither.
+
+
short_exptime_vis
The typical exposure time for the shorter exposures with VIS taken in parallel with NISP imaging, in units of seconds.
+The number that is stored is for a single dither.
+
+
n_dithers
The number of dithers per filter.
+
+
n_ccd
The number of CCDs in the focal plane.
+
+
n_ccd_row
The number of CCDs in the each focal plane row.
+
+
n_ccd_col
The number of CCDs in the each focal plane column.
+
+
n_pix_row
Each CCD has n_pix_row total pixels in each row.
+
+
n_pix_col
Each CCD has n_pix_col total pixels in each column.
+
+
pixel_scale_mm
The physical pixel size, in units of mm.
+
+
plate_scale
The plate scale, in units of arcsec / mm
+
+
read_noise
Total readout noise, in units of e-.
+
+
saturation
Pixel saturation, in units of e-.
+
+
det2ccd
Mapping from DETID to CCDID.
+
+
ccd2det
Mapping from CCDID to DETID.
+
+
min_sun_angle
Minimum allowed angle from the telescope solar panels to the sun, in degrees.
+
+
max_sun_angle
Maximum allowed angle from the telescope solar panels to the sun, in degrees.
+
+
vis_bands
List of available VIS bands
+
+
nisp_bands
List of available NISP bands
+
+
vis_blue_limit
Bandpass blue limit needed for consistency with the wavelength range covered by ur tabulated
+PSF images, in nm.
+
+
vis_red_limit
Bandpass red limit needed for consistency with the wavelength range covered by ur tabulated
+PSF images, in nm.
+
+
+
For example, to get the gain value, use euclidlike.gain.
This file includes any routines needed to define the background level, for which the main contribution (currently the only one implemented) is zodiacal light.
A utility to get a dictionary containing galsim.Bandpass objects for each of
+the Euclid-like imaging bandpasses, which by default have AB zeropoints given using
+the GalSim zeropoint convention (see getBandpasses docstring for more details).
+
+
euclidlike.getSkyLevel
A utility to find the expected sky level due to zodiacal light at a given
+position, in a given band.
+
+
euclidlike.getZodiBackground
This helper routine is enables the calculation of the zodiacal light, in photons/m^2/arcsec^2/sec
+
+
euclidlike.getPSF
A routine to get a chromatic representation of the PSF in a single CCD.
+PSFs are based on precomputed, oversampled (by 3x) PSF images on a grid in wavelength and
+focal plane position.
+
+
euclidlike.getBrightPSF
Get a fake optical PSF for very bright objects.
+
+
euclidlike.getWCS
This routine returns a dict containing a WCS for each of the Euclid CCDs.
+
+
euclidlike.findCCD
This is a helper routine to calculate the minimum and maximum pixel values that should be
+considered within a CCD.
Function to get the bandpass information for the Euclid VIS band and the three Euclid NISP passbands.
+
This routine reads in files containing a list of wavelengths and
+transmission values for the Euclid bands. The files are located in the
+euclidlike.data directory. The routine then creates a Bandpass object
+using the LookupTable class from the GalSim package, and returns a dict with bandpasses for the
+keys.
These are relatively old files that do not include the latest estimates of system response.
+They correspond to end-of-life estimates, with some expected degradation of the QE and filter
+transmission over time. This can lead to flux estimates that are suppressed by 5-10% from
+beginning-of-life flux estimates.
+
The VIS bandpass red and blue limits are set not by the transmission curve but by the range of
+wavelengths over which we have tabulated PSF images. The wavelength range is read in from the
+instrument parameter file.
+
Args:
+AB_zeropoint (bool) : If True, set the zeropoint of the bandpass to the AB magnitude system. [default: True]
+default_thin_trunc (bool) : If True, use the default thinning and truncation parameters. [default: True]
+full_bandpass (bool): if True, use the full bandpass without red/blue limits needed for PSF
+
+
calculations. [default: False]
+
+
kwargs : Additional keyword arguments to pass to either Bandpass.thin or Bandpass.truncate.
This helper routine is used with permission from Chris Hirata’s Exposure Time Calculator and enables the
-calculation of the zodiacal light in photons/m^2/arcsec^2/sec. The ETC may be found here:
The code was ported from C to python by Michael Troxel (matroxel on GitHub). The units are
-exactly as in the original C code, and we convert to any other needed units outside of this
-routine, in user-facing code.
-
-
Parameters:
-
-
ecl_lat – Ecliptic latitude (degrees)
-
ecl_dlon – Ecliptic longitude (degrees)
-
lambda_min – Minimum wavelength for the bandpass (microns)
-
lambda_max – Maximum wavelength for the bandpass (microns)
-
Tlambda – Numpy array containing a grid of wavelength values for the bandpass (microns)
-
T – Numpy array containing the throughput (normalized to be between 0-1) for the
-bandpass
-
-
-
Returns:
-
A floating point value for the zodiacal light in photons/m^2/arcsec^2/sec
Function to get the bandpass information for the Euclid VIS band and the three Euclid NISP passbands.
-
This routine reads in files containing a list of wavelengths and
-transmission values for the Euclid bands. The files are located in the
-euclidlike.data directory. The routine then creates a Bandpass object
-using the LookupTable class from the GalSim package, and returns a dict with bandpasses for the
-keys.
These are relatively old files that do not include the latest estimates of system response.
-They correspond to end-of-life estimates, with some expected degradation of the QE and filter
-transmission over time. This can lead to flux estimates that are suppressed by 5-10% from
-beginning-of-life flux estimates.
-
The VIS bandpass red and blue limits are set not by the transmission curve but by the range of
-wavelengths over which we have tabulated PSF images. The wavelength range is read in from the
-instrument parameter file.
-
Args:
-AB_zeropoint (bool) : If True, set the zeropoint of the bandpass to the AB magnitude system. [default: True]
-default_thin_trunc (bool) : If True, use the default thinning and truncation parameters. [default: True]
-full_bandpass (bool): if True, use the full bandpass without red/blue limits needed for PSF
-
-
calculations. [default: False]
-
-
kwargs : Additional keyword arguments to pass to either Bandpass.thin or Bandpass.truncate.
Get a fake optical PSF for very bright objects in Euclid-like simulations.
-Depending on the inputs, this routine returns a chromatic or achromatic PSF using the
-Euclid telescope diameter and Euclid-like aperture.
-
Args:
-ccd (int): Single value specifying the CCD for which the PSF should be
-
-
loaded.
-
-
-
bandpass (str): Single string specifying the bandpass to use when
defining the pupil plane configuration and/or interpolation of
-chromatic PSFs.
-
-
ccd_pos: Single galsim.PositionD indicating the position within the CCD
for which the PSF should be created. If None, the exact center of the
-CCD is chosen. [default: None]
-
-
wcs: The WCS to use to project the PSF into world coordinates. [default:
galsim.PixelScale(euclidlike.pixel_scale)]
-
-
n_waves (int): Number of wavelengths to use for setting up interpolation of the
chromatic PSF objects, which can lead to much faster image
-rendering. If None, then no interpolation is used. Note that
-users who want to interpolate can always set up the interpolation
-later on even if they do not do so when calling getPSF.
-[default: None]
-
-
wavelength (float): An option to get an achromatic PSF for a single
wavelength, for users who do not care about chromaticity of the PSF. If
-None, then the fully chromatic PSF is returned as an
-InterpolatedChromaticObject. Alternatively the user should supply
-either (a) a wavelength in nanometers, and they will get an
-InterpolatedImage object for that wavelength, or (b) a bandpass object,
-in which case they will get an InterpolatedImage objects defined at the
-effective wavelength of that bandpass. [default: None]
-
-
gsparams: An optional GSParams argument. See the docstring for GSParams
for details. [default: None]
-
-
-
-
Returns:
-
A single PSF object (either an InterpolatedChromaticObject or an
-InterpolatedImage depending on the inputs).
These PSFs are based on precomputed, oversampled (by 3x) PSF images on a grid in wavelength and
focal plane position. These images were provided by Lance Miller and Chris Duncan. They are
@@ -276,152 +288,54 @@
This file includes any routines needed to define and use the Euclid-like WCS.
-Current version is based on the focal plane description detailed in
-Scaramella et al. (Fig. 2 and Table 1).
-The distortion coefficients were derived from the ERO release.
This routine can be used to check whether Euclid would be allowed to look at a particular
-position (world_pos) on a given date. This is determined by the angle of this position
-relative to the Sun.
-
In general, Euclid can point at angles relative to the Sun in the range 90+20 & 90-3 degrees.
-Obviously, pointing too close to the Sun would result in overly high sky backgrounds. It is
-less obvious why Euclid cannot look at a spot directly opposite from the Sun (180 degrees on the
-sky). The reason is that the observatory is aligned such that if the observer is looking at
-some sky position, the solar panels are oriented at 90 degrees from that position. So it’s
-always optimal for the observatory to be pointing at an angle of 90 degrees relative to the
-Sun. It is also permitted to look within [-3, + 20] degrees of that optimal position.
-
-
Parameters:
-
-
world_pos – A galsim.CelestialCoord indicating the position at which the observer
-wishes to look.
-
date – A python datetime object indicating the desired date of observation.
-
SAA – A galsim.Angle representing the Solar Aspect Angle
-of the telescope for the observation. If not provided,
-it will be computed internally.
Get a fake optical PSF for very bright objects in Euclid-like simulations.
+Depending on the inputs, this routine returns a chromatic or achromatic PSF using the
+Euclid telescope diameter and Euclid-like aperture.
+
Args:
+ccd (int): Single value specifying the CCD for which the PSF should be
+
+
loaded.
+
+
+
bandpass (str): Single string specifying the bandpass to use when
defining the pupil plane configuration and/or interpolation of
+chromatic PSFs.
-
Returns:
-
True or False, indicating whether it is permitted to look at this position on this date.
+
ccd_pos: Single galsim.PositionD indicating the position within the CCD
for which the PSF should be created. If None, the exact center of the
+CCD is chosen. [default: None]
This routine determines the best position angle for the observatory for a given observation date
-and position on the sky.
-
The best/optimal position angle is determined by the fact that the solar panels are at 90
-degrees to the position being observed, and it is best to have those facing the Sun as directly
-as possible. Note that if a given world_pos is not actually observable on the given
-date, then this routine will return None.
-
-
Parameters:
-
-
world_pos – A galsim.CelestialCoord indicating the position at which the observer
-wishes to look.
-
date – A python datetime object indicating the desired date of observation.
-
SAA – A galsim.Angle representing the Solar Aspect Angle
-of the telescope for the observation. If not provided,
-it will be computed internally.
-
+
wcs: The WCS to use to project the PSF into world coordinates. [default:
galsim.PixelScale(euclidlike.pixel_scale)]
-
Returns:
-
the best position angle for the observatory, as a galsim.Angle, or None if the position
-is not observable.
+
n_waves (int): Number of wavelengths to use for setting up interpolation of the
chromatic PSF objects, which can lead to much faster image
+rendering. If None, then no interpolation is used. Note that
+users who want to interpolate can always set up the interpolation
+later on even if they do not do so when calling getPSF.
+[default: None]
This is a simple helper routine that takes an input position world_pos that is meant to
-correspond to the position of the center of an CCD, and tells where the center of the focal
-plane array should be. The goal is to provide a position that can be used as an input to
-getWCS(), which wants the center of the focal plane array.
-
The results of the calculation are deterministic if given a fixed position angle (PA). If it’s
-not given one, it will try to determine the best one for this location and date, like getWCS()
-does.
-
Because of distortions varying across the focal plane, this routine has to iteratively correct
-its initial result based on empirical tests. The tol kwarg can be used to adjust how
-careful it will be, but it always does at least one iteration.
-
-
Parameters:
-
-
world_pos – A galsim.CelestialCoord indicating the position to observe at the center of the
-given CCD. Note that if the given position is not observable on
-the given date, then the routine will raise an exception.
-
CCD – A single number giving the CCD for which the center should be located at
-world_pos.
-
PA – galsim.Angle representing the position angle of the observatory +Y axis, unless
-PA_is_FPA=True, in which case it’s the position angle of the FPA. For
-users to do not care about this, then leaving this as None will result in the
-routine using the supplied date and world_pos to select the optimal
-orientation for the observatory. Note that if a user supplies a PA value,
-the routine does not check whether this orientation is actually allowed.
-[default: None]
-
SAA – A galsim.Angle representing the Solar Aspect Angle
-of the telescope for the observation. If not provided,
-it will be computed internally.
-
date – The date of the observation, as a python datetime object. If None, then the
-vernal equinox in 2025 will be used. [default: None]
-
PA_is_FPA – If True, then the position angle that was provided was the PA of the focal
-plane array, not the observatory. [default: False]
-
tol – Tolerance for errors due to distortions, as a galsim.Angle.
-[default: 0.5*galsim.arcsec]
-
+
wavelength (float): An option to get an achromatic PSF for a single
wavelength, for users who do not care about chromaticity of the PSF. If
+None, then the fully chromatic PSF is returned as an
+InterpolatedChromaticObject. Alternatively the user should supply
+either (a) a wavelength in nanometers, and they will get an
+InterpolatedImage object for that wavelength, or (b) a bandpass object,
+in which case they will get an InterpolatedImage objects defined at the
+effective wavelength of that bandpass. [default: None]
-
Returns:
-
A CelestialCoord object indicating the center of the focal plane array.
+
gsparams: An optional GSParams argument. See the docstring for GSParams
This is a subroutine to take a dict of WCS (one per CCD) from euclidlike.getWCS() and query
-which CCD a particular real-world coordinate would be located on. The position (world_pos)
-should be specified as a galsim.CelestialCoord. If the position is not located on any of the
-CCDs, the result will be None. Note that if wcs_dict does not include all CCDs in it, then
-it’s possible the position might lie on one of the CCDs that was not included.
-
Depending on what the user wants to do with the results, they may wish to use the
-include_border keyword. This keyword determines whether or not to include an additional
-border corresponding to half of the gaps between CCDs. For example, if a user is drawing a
-single image they may wish to only know whether a given position falls onto a CCD, and if so,
-which one (ignoring everything in the gaps). In contrast, a user who plans to make a sequence
-of dithered images might find it most useful to know whether the position is either on a CCD or
-close enough that in a small dither sequence it might appear on the CCD at some point. Use of
-include_border switches between these scenarios.
-
Parameters:
-
-
wcs_dict – The dict of WCS’s output from euclidlike.getWCS().
-
world_pos – A galsim.CelestialCoord indicating the sky position of interest.
-
include_border – If True, then include the half-border around CCD to cover the gap
-between each sensor. [default: False]
-
-
-
Returns:
-
an integer value of the CCD on which the position falls, or None if the position is not
-on any CCD.
+
Returns:
+
A single PSF object (either an InterpolatedChromaticObject or an
+InterpolatedImage depending on the inputs).
This routine returns a dict containing a WCS for each of the Euclid CCDs.
The Euclid CCDs are labeled 0-35, so these numbers are used as the keys in
the dict. Alternatively the user can request a subset of the CCDs using
@@ -475,14 +389,38 @@
This is a subroutine to take a dict of WCS (one per CCD) from euclidlike.getWCS() and query
+which CCD a particular real-world coordinate would be located on. The position (world_pos)
+should be specified as a galsim.CelestialCoord. If the position is not located on any of the
+CCDs, the result will be None. Note that if wcs_dict does not include all CCDs in it, then
+it’s possible the position might lie on one of the CCDs that was not included.
+
Depending on what the user wants to do with the results, they may wish to use the
+include_border keyword. This keyword determines whether or not to include an additional
+border corresponding to half of the gaps between CCDs. For example, if a user is drawing a
+single image they may wish to only know whether a given position falls onto a CCD, and if so,
+which one (ignoring everything in the gaps). In contrast, a user who plans to make a sequence
+of dithered images might find it most useful to know whether the position is either on a CCD or
+close enough that in a small dither sequence it might appear on the CCD at some point. Use of
+include_border switches between these scenarios.
+
+
Parameters:
+
+
wcs_dict – The dict of WCS’s output from euclidlike.getWCS().
+
world_pos – A galsim.CelestialCoord indicating the sky position of interest.
+
include_border – If True, then include the half-border around CCD to cover the gap
+between each sensor. [default: False]
+
+
+
Returns:
+
an integer value of the CCD on which the position falls, or None if the position is not
+on any CCD.
This module contains configuration scripts to produce large-scale Euclid-like simulation runs based on the information in euclidlike.
+It is based heavily on roman_imsim.
Do any initial setup for this builder at the start of a new output file.
+
The base class implementation saves two work space items into self.data and self.scratch
+that can be used to safely communicate across multiple processes.
+
+
Parameters:
+
+
data – An empty list of length nimages to use as work space.
+
scratch – An empty dict that can be used as work space.
+
config – The configuration field for this output object.
+
base – The base configuration dict.
+
logger – If given, a logger object to log progress. [default: None]
Perform any necessary processing at the end of each image construction.
+
This function will be called after each full image is built.
+
Compute the noise for the current image and add it to the image.
+It will also create an independent noise image.
+The code optionally subtract the background if requested.
+
+
Parameters:
+
+
index – The index in self.data to use for this image. This isn’t the image_num
+(which can be accessed at base[‘image_num’] if needed), but rather
+an index that starts at 0 for the first image being worked on and
+goes up to nimages-1.
+
obj_nums – The object numbers that were used for this image.
+
config – The configuration field for this output object.
+
base – The base configuration dict.
+
logger – If given, a logger object to log progress. [default: None]
Perform any necessary processing at the end of each image construction.
+
This function will be called after each full image is built.
+
Compute the sky background and return it in an image.
+
+
Parameters:
+
+
index – The index in self.data to use for this image. This isn’t the image_num
+(which can be accessed at base[‘image_num’] if needed), but rather
+an index that starts at 0 for the first image being worked on and
+goes up to nimages-1.
+
obj_nums – The object numbers that were used for this image.
+
config – The configuration field for this output object.
+
base – The base configuration dict.
+
logger – If given, a logger object to log progress. [default: None]
Perform any necessary processing at the end of each image construction.
+
This function will be called after each full image is built.
+
Compute the weight map from the noise image and return it in an image.
+
+
Parameters:
+
+
index – The index in self.data to use for this image. This isn’t the image_num
+(which can be accessed at base[‘image_num’] if needed), but rather
+an index that starts at 0 for the first image being worked on and
+goes up to nimages-1.
+
obj_nums – The object numbers that were used for this image.
+
config – The configuration field for this output object.
+
base – The base configuration dict.
+
logger – If given, a logger object to log progress. [default: None]
Return a PSF to be convolved with sources.
+PSF is sampled at 4 quadrants for each CCD. Returned PSF
+corresponds to that of the quadrant of the CCD position.
Parse the config dict and return the kwargs needed to build the input object.
+
The default implementation looks for special class attributes called:
+
+
_req_params
A dict of required parameters and their types.
+
+
_opt_params
A dict of optional parameters and their types.
+
+
_single_params
A list of dicts of parameters such that one and only one of
+parameter in each dict is required.
+
+
_takes_rng
A bool value saying whether an rng object is required.
+
+
+
See galsim.Catalog for an example of a class that sets these attributes.
+
In addition to the kwargs, we also return a bool value, safe, that indicates whether
+the constructed object will be safe to keep around for multiple files (True) of if
+it will need to be rebuilt for each output file (False).
+
+
Parameters:
+
+
config – The config dict for this input item
+
base – The base config dict
+
logger – If given, a logger object to log progress. [default: None]
Return the galsim object for the skyCatalog object
+corresponding to the specified index. If the skyCatalog
+object is a galaxy, the returned galsim object will be
+a galsim.Sum.
+
+
Parameters:
+
index (int) – Index of the object in the self.objects catalog.
Parse the config dict and return the kwargs needed to build the input object.
+
The default implementation looks for special class attributes called:
+
+
_req_params
A dict of required parameters and their types.
+
+
_opt_params
A dict of optional parameters and their types.
+
+
_single_params
A list of dicts of parameters such that one and only one of
+parameter in each dict is required.
+
+
_takes_rng
A bool value saying whether an rng object is required.
+
+
+
See galsim.Catalog for an example of a class that sets these attributes.
+
In addition to the kwargs, we also return a bool value, safe, that indicates whether
+the constructed object will be safe to keep around for multiple files (True) of if
+it will need to be rebuilt for each output file (False).
+
+
Parameters:
+
+
config – The config dict for this input item
+
base – The base config dict
+
logger – If given, a logger object to log progress. [default: None]
Do the initialization and setup for building a postage stamp.
+
In the base class, we check for and parse the appropriate size and position values in
+config (aka base[‘stamp’] or base[‘image’].
+
Values given in base[‘stamp’] take precedence if these are given in both places (which
+would be confusing, so probably shouldn’t do that, but there might be a use case where it
+would make sense).
+
+
Parameters:
+
+
config – The configuration dict for the stamp field.
+
base – The base configuration dict.
+
xsize – The xsize of the image to build (if known).
+
ysize – The ysize of the image to build (if known).
+
ignore – A list of parameters that are allowed to be in config that we can
+ignore here. i.e. it won’t be an error if these parameters are present.
Return a Roman PSF image for some image position
+:param stamp_size: size of output PSF model stamp in native roman pixel_scale (oversampling_factor=1)
+:param x: x-position in SCA
+:param y: y-position in SCA
+:param pupil_bin: pupil image binning factor
+:param sed: SED to be used to draw the PSF - default is a flat SED.
+:param oversampling_factor: factor by which to oversample native roman pixel_scale
+:param include_photonOps: include additional contributions from other photon operators in effective psf image
+
+
Returns:
+
the PSF GalSim image object (use image.array to get a numpy array representation)
This first demo is the euclidlike-equivalent of demo 13 in GalSim. This demo uses the Euclid-like PSF, WCS, and background noise to produce a realistic scene of galaxies and stars as observed from a Euclid-like Telescope.
+
** Features introduced in the Python file**:
+
+
euclidlike.getBandpasses(AB_zeropoint)
+
euclidlike.getWCS(world_pos, CCDs_CCD, date)
+
euclidlike.getPSF(use_CCD, filter_name, wcs)
+
euclidlike.getSkyLevel(bandpass, world_pos)
+
+
The output generated from this file can be visualized by running the script end_to_end_demo.py.py
Helper functions to generate simulations of Euclid-like images using GalSim.
-
This repository contains information about the Euclid space telescope and survey that is needed to
-produce simulations using GalSim. Some of the
-information provided is approximate, aimed towards fast simulations rather than full accuracy in
-representation of Euclid images. Places where the information is only approximate are flagged and
-described in the docstring, and we particularly highlight that the PSF is only approximate;
-for details, see the docstring of the getPSF() method. This library should enable generation of
-Euclid-like images of sufficient fidelity for preliminary exploration of object detection,
-photometry, deblending, and joint analysis with ground-based observatories. For
-applications requiring high precision such as weak lensing, the higher fidelity simulations
-available within the Euclid Consortium should be used.
-
This repository includes two distinct packages:
-
-
euclidlike: has basic observatory, instrumentation, and survey information for Euclid.
-This package can be used on its own along with GalSim to produce Euclid-like simulations.
-
euclidlike_imsim: has configuration scripts to produce large-scale Euclid-like simulation runs
-based on the information in euclidlike. It is based heavily on roman_imsim.
+
+
GalSim-Euclid-Like: Euclid-like images using GalSim¶
The Euclid-like PSF is constructed from precomputed oversampled images on a grid in focal plane position and wavelength. To use the full FOV PSF within GalSim-Euclid-Like, the images must be downloaded by running:
-
$ euclidlike_download_psf
-
-
in the terminal after installation of GalSim-Euclid-Like. To install in an alternative directory to the default, use the --dir argument. Refer to the getPSF documentation for further details about the PSF.
Feel free to open a GitHub issue to reach the developers with questions, comments, and bug reports. New contributors are also welcome and can indicate their interest in developing this code base through the Issues.
This software is open source and may be used according to the terms of its license.
-
When using this software, please provide the URL to the repository in the resulting paper or note. Once there is a Zenodo DOI or journal article, this README will be updated and we will ask those using the code in their research to cite the relevant journal article.
The GalSim-Euclid-Like repository contains two python libraries and currently supports Python versions 3.10 and above.
+
System requirements: Given the heavy dependance on GalSim, GalSim-Euclid-Like currently only supports Linux and Mac OSX. For
+further details on system requirements for GalSim see GalSim Installation.
Helper functions to generate simulations of Euclid-like images using GalSim.
+
This repository contains information about the Euclid space telescope and survey that is needed to
+produce simulations using GalSim. Some of the
+information provided is approximate, aimed towards fast simulations rather than full accuracy in
+representation of Euclid images. Places where the information is only approximate are flagged and
+described in the docstring, and we particularly highlight that the PSF is only approximate;
+for details, see the docstring of the getPSF() method. This library should enable generation of
+Euclid-like images of sufficient fidelity for preliminary exploration of object detection,
+photometry, deblending, and joint analysis with ground-based observatories. For
+applications requiring high precision such as weak lensing, the higher fidelity simulations
+available within the Euclid Consortium should be used.
+
This repository includes two distinct packages:
+
+
euclidlike: has basic observatory, instrumentation, and survey information for Euclid.
+This package can be used on its own along with GalSim to produce Euclid-like simulations.
+
euclidlike_imsim: has configuration scripts to produce large-scale Euclid-like simulation runs
+based on the information in euclidlike. It is based heavily on roman_imsim.
The Euclid-like PSF is constructed from precomputed oversampled images on a grid in focal plane position and wavelength. To use the full FOV PSF within GalSim-Euclid-Like, the images must be downloaded by running:
+
$ euclidlike_download_psf
+
+
+
in the terminal after installation of GalSim-Euclid-Like. To install in an alternative directory to the default, use the --dir argument. Refer to the getPSF documentation for further details about the PSF.
Feel free to open a GitHub issue to reach the developers with questions, comments, and bug reports. New contributors are also welcome and can indicate their interest in developing this code base through the Issues.
This software is open source and may be used according to the terms of its license.
+
When using this software, please provide the URL to the repository in the resulting paper or note. Once there is a Zenodo DOI or journal article, this README will be updated and we will ask those using the code in their research to cite the relevant journal article.