Observational Sequence Construction¶
After the construction of astrophysical scenes to observe, the next stage in a PanCAKE simulation is to describe a sequence of observations that should be simulated. In the Basic Tutorial we briefly covered this, however, this page inclues much more detail on the construction steps and tunable parameters.
Important - If you are running PanCAKE within a python script, you will need to place all imports and function calls within the following statement:if __name__ == '__main__':
At first we need to create some scenes to observe, and initialise an empty sequence of observations:
import pancake # Define the target scene target = pancake.scene.Scene('Target') target.add_source('HIP 65426', kind='simbad') # Define the reference scene reference = pancake.scene.Scene('Reference') reference.add_source('HIP 68245', kind='simbad') # Initialise Observational Sequence seq = pancake.sequence.Sequence()
Target // Adding Source: HIP 65426 WARNING: Couldn't determine magnitude system, assuming Vega magnitudes. WARNING: Spectral type 'a2v' not compatible with Pandeia grid, using spectral type 'a1v' instead. Reference // Adding Source: HIP 68245 WARNING: Couldn't determine magnitude system, assuming Vega magnitudes. WARNING: Spectral type 'b2iv' not compatible with Pandeia grid, using spectral type 'b1v' instead.
With an empty sequence defined, we can start to add observations of our scenes in the chronological order in which we want them to be performed using the
seq.add_observation(target, exposures=[('F444W', 'DEEP8', 10, 10)])
Here all we have done is specify the scene we want to observe (the target) and the readout parameters for an exposure we wish to perform. Note that there is no need to specify the instrument, subarray, or even coronagraphic mask and default estimations will be used. In some cases this may not be preferred, in which case see the Observation Specifics section below for further information.
The exposure has been split into four properties: Filter, Readout Pattern, Number of Groups, and Number of Integrations. Be careful which way round you put the groups and integrations (Tip: they are ordered alphabetically)! If you have no idea what a readout pattern, groups, or integrations are take a look at the JWST Documentation. If you know what these are, but don’t know what pattern / numbers to choose, see the Optimising Readout Parameters section below.
If we want to add more exposures, or exposures for different scenes, we simply need to specify calls to
add_observation() until the desired observing sequence is complete.
It’s a little inefficient to list the exposures one by one, so the
add_observation() method also has the functionality to intake multiple exposures simultaneously:
seq.add_observation(target, exposures=[('F444W', 'DEEP8', 10, 10), ('F1065C', 'FASTR1', 100, 10)])
Where we have simply added a another tuple which describes a second exposure we’d like to perform. Note that the ‘F444W’ filter is from NIRCam, and the ‘F1065C’ filter is from MIRI - there is no inherent need to separate your NIRCam and MIRI exposures. However, for a given
add_observation() call, the default behaviour of PanCAKE is to add exposures to the observational sequence by the coronagraph they are using. In some cases, this will lead to the observations not being simulated in the
order that they are listed, and if this is not what you would like you will need to separate the exposures in to distinct
Optimising Readout Parameters¶
With multiple readout patterns varying from instrument to instrument, with further varying limits on the number of allowable groups and integrations, it isn’t straightforward to determine what parameters to actually use to optimise the SNR you would get for a given amount of time. The correct way to perform such an assessment would be to perform simulations across the readout parameters of interest to identify what maximises the SNR at a desired location within the simulated FOV. Unfortunately, this is too computationally intensive for PanCAKE in its current form.
Nevertheless, there is an optimisation routine available which can be used to estimate close to optimal readout parameters relatively quickly. To use it, simply replace the readout parameters for an
'optimise' string and the number of seconds you want to observe the object for:
seq.add_observation(target, exposures=[('F444W', 'optimise', 3600), ('F1065C', 'optimise', 3600)])
Optimising Readout // Target // Exposure: F444W, 3600 seconds --> Pattern: DEEP8, Number of Groups: 20, Number of Integrations: 9 = 3743s Optimising Readout // Target // Exposure: F1065C, 3600 seconds --> Pattern: FAST, Number of Groups: 1251, Number of Integrations: 12 = 3598s
As can be seen, the readout parameters have been automatically determined and assigned for future observations. If you want to re-run your code multiple times, you can now replace the optimisation request for these calculated parameters to save time.
It is important to understand that this optimisation scheme is built upon the assumption that the best overall contrast will be achieved with a combination of readout parameters that results in the largest amount of time between detector resets (i.e. the longest integration times) whilst simultaneously avoiding detector saturation and/or significant cosmic ray contamination. Such constraints maximise the number of photons detected from the astrophysical scene, whilst also minimising the impact of read noise. In reality, it may be that better contrasts can be obtained at wider separations by allowing the innermost regions of the image to saturate, and shorter integrations may be desirable if there is a need to obtain more integration-level images within a defined timescale. Use of this optimisation procedure should be considered carefully; however, in standard circumstances this assumption is unlikely to result in readout parameter estimations that are significantly different from the true optimal values. For futher information on the optimisation, see Section 3.2 and Figure 1 from the PanCAKE SPIE Paper.
When observing a reference object, in many cases we want to match the number of photons received during our science object, and not necessarily match the exposure time. If our reference is much brighter than the target, we can reduce the exposure time significantly and make our observations much more efficient.
In PanCAKE, this “scaling” of the exposure time can be performed automatically, and new readout parameters can be optimised:
seq.add_observation(reference, exposures=[('F444W', 'DEEP8', 10, 10)], scale_exposures=target) seq.add_observation(reference, exposures=[('F1065C', 'optimise', 3600)], scale_exposures=target)
--> Scaling provided exposure times by relative flux of: "Target" ---> Pattern: DEEP8, Number of Groups: 7, Number of Integrations: 2 = 276s Optimising Readout // Reference // Exposure: F1065C, 3600 seconds --> Scaling provided exposure times by relative flux of: "Target" --> Pattern: FAST, Number of Groups: 1116, Number of Integrations: 2 = 535s
Here we have demonstrated that the scaling can be performed on a precise set of readout parameters, or one that needs to be optimised. Note that the user input no longer defines the exposure to be performed, but rather a baseline exposure to scale from. To scale the exposure, we have provided the target scene object, in this case the routine will identify the brightest object in both the
target scenes and scale relative to their flux ratio in the provided exposure filter.
If you’d prefer to scale things based on a fraction of the defined exposure time, a float or integer can also be used instead:
seq.add_observation(reference, exposures=[('F444W', 'DEEP8', 10, 10)], scale_exposures=0.33) seq.add_observation(reference, exposures=[('F1065C', 'optimise', 3600)], scale_exposures=0.33)
--> Scaling provided exposure times by 0.33 ---> Pattern: DEEP8, Number of Groups: 7, Number of Integrations: 5 = 690s Optimising Readout // Reference // Exposure: F1065C, 3600 seconds --> Scaling provided exposure times by 0.33 --> Pattern: FAST, Number of Groups: 1214, Number of Integrations: 4 = 1164s
Whilst the optimisation routine is relatively inflexible, there exists some level of fine-tunability through the maximum allowable saturation fraction, and the optimisation margin.
By default the optimisation routine will only select readout parameters that correspond to a fraction of full well saturation (for any pixel in the image) of 0.95. If you want to be less strict, or potentially even allow some level of saturation, this value can be adjusted:
seq.add_observation(reference, exposures=[('F444W', 'optimise', 3600)], max_sat=0.5) seq.add_observation(reference, exposures=[('F444W', 'optimise', 3600)], max_sat=1.5)
Optimising Readout // Reference // Exposure: F444W, 3600 seconds --> Pattern: DEEP8, Number of Groups: 4, Number of Integrations: 50 = 3689s Optimising Readout // Reference // Exposure: F444W, 3600 seconds --> Pattern: DEEP8, Number of Groups: 10, Number of Integrations: 18 = 3637s
You may have noticed in the above examples that even though an exposure time of 3600 seconds has been specified, the adopted readout parameters correspond to slightly different exposure durations. Selecting readout parameters that are optimal and provide a precise exposure time is very difficult owing to the JWST readout structure. Instead PanCAKE looks for potential exposures within a margin of the provided exposure time. By default this margin is 5% of the provided exposure, but can also be defined manually:
seq.add_observation(reference, exposures=[('F444W', 'optimise', 3600)], optimise_margin=0.01) seq.add_observation(reference, exposures=[('F444W', 'optimise', 3600)], optimise_margin=0.2)
Optimising Readout // Reference // Exposure: F444W, 3600 seconds --> Pattern: DEEP8, Number of Groups: 6, Number of Integrations: 31 = 3613s Optimising Readout // Reference // Exposure: F444W, 3600 seconds --> Pattern: DEEP8, Number of Groups: 8, Number of Integrations: 23 = 3664s
and we can see that the wider margin of 20% allows for more optimal readout parameters using an exposure only 50 seconds longer than that found with a restrictive margin of 1%.
In many cases, it will be necessary to provide further specific information to PanCAKE to further define or expand a desired observation.
PanCAKE has differing behaviour with respect to selecting coronagraphic masks depending on whether the NIRCam or MIRI instrument is being used. For MIRI, the coronagraphic mask is intrinsically tied to the observational filter, and there is no need (or capability) to manually define this. However, for NIRCam there are five independent coronagraphic masks, and most filters are compatible with more than one mask (NIRCam Filters For Coronagraphy). The default allocations depending on the input filter are as follows:
However, the NIRCam mask can also be explictly defined using the
nircam_mask parameter if necesssary:
seq.add_observation(target, exposures=[('F444W', 'DEEP8', 10, 10)], nircam_mask='MASK430R')
To facilitate ADI PSF subtraction routines, it is necessary to roll the telescope and perform an additional observation of our target scene. By default PanCAKE will perform a single roll; however, additional rolls can be defined using the
seq.add_observation(target, exposures=[('F444W', 'DEEP8', 10, 10)], rolls='max') seq.add_observation(target, exposures=[('F444W', 'DEEP8', 10, 10)], rolls=[0,5,10,20])
WARNING: Roll differences of more than 15 degrees are extremely difficult/impossible to schedule.
Here we have demonstrated the two different input options. A list of precise roll values in degrees can be provided, or alternatively a
max string can be provided to request two rolls at 0 and 14 degrees. Note that the maximum roll possible for JWST to avoid significant solar illumination is ~14 degrees, and whilst rolls larger than this can be simulated, they will not be schedulable.
Similarly to performing multiple target rolls, it can be beneficial to obtain multiple dithered observations of reference scenes to improve the PSF diversity and facilitate better RDI PSF subtraction routines (more info here). For coronagraphy, these “small-grid dithers” can be applied using the
seq.add_observation(reference, exposures=[('F444W', 'DEEP8', 10, 10)], nircam_sgd='5-POINT-BOX') seq.add_observation(reference, exposures=[('F1065C', 'FASTR1', 100, 10)], miri_sgd='5-POINT-SMALL-GRID')
where we have provided the name of the dither pattern to be used. As NIRCam and MIRI have different dither offsets / nomenclature, the desired small grid dither pattern must be described individually for each instrument. Information on the NIRCam dithers can be found here and the MIRI dithers here.
NIRCam: F182M, F187N, F200W, F210M, F212N, F250M, F277W, F300M, F322W2, F335M, F356W, F360M, F410M, F430M, F444W, F460M, F480M
MIRI: F1065C, F1140C, F1550C
NIRCam: MASKSWB, MASKLWB, MASK210R, MASK335R, MASK430R
MIRI: Tied to the chosen filter.
Compatible Dither Patterns:¶
NIRCam: 5-POINT-BOX, 5-POINT-DIAMOND, 9-POINT-CIRCLE, 3-POINT-BAR, 5-POINT-BAR
MIRI: 5-POINT-SMALL-GRID, 9-POINT-SMALL-GRID