Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions pastis/config_pastis.ini
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,10 @@ im_size_lamD_hcipy = 30

[ELT]
; aberration for matrix calculation, in NANOMETERS
calibration_aberration = 10.
calibration_aberration = 30.
; log10 limits of PASTIS validity in nm WFE
valid_range_lower = -4
valid_range_upper = 4
valid_range_lower = -0.5
valid_range_upper = 3.5

; telescope
nb_subapertures = 798
Expand Down
43 changes: 43 additions & 0 deletions pastis/launchers/run_elt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import numpy as np

from pastis.config import CONFIG_PASTIS
from pastis.matrix_generation.matrix_from_efields import MatrixEfieldELT
from pastis.ultra_analysis import MultiModeAnalysis


if __name__ == '__main__':

WHICH_DM = 'seg_mirror' # 'harris_seg_mirror' or 'seg_mirror', or (global) 'zernike_mirror'
C_TARGET = 1e-5
DESIGN = 'HSP1'

# DM_SPEC = tuple or int, specification for the used DM -
# for seg_mirror: int, number of local Zernike modes on each segment
# for harris_seg_mirror: tuple (string, array, bool, bool, bool),
# absolute path to Harris spreadsheet, pad orientations, choice of Harris mode sets (thermal, mechanical, other)
# for zernike_mirror: int, number of global Zernikes

# If using Harris deformable mirror
if WHICH_DM == 'harris_seg_mirror':
fpath = CONFIG_PASTIS.get('LUVOIR', 'harris_data_path') # path to Harris spreadsheet
pad_orientations = np.pi / 2 * np.ones(CONFIG_PASTIS.getint('LUVOIR', 'nb_subapertures'))
DM_SPEC = (fpath, pad_orientations, True, False, False)
NUM_MODES = 5 # TODO: works only for thermal modes currently

# If using Segmented Zernike Mirror
if WHICH_DM in ['seg_mirror', 'zernike_mirror']:
DM_SPEC = 2
NUM_MODES = DM_SPEC

# Calculate sensitivity matrix
run_matrix = MatrixEfieldELT(which_dm=WHICH_DM, dm_spec=DM_SPEC, design=DESIGN,
calc_science=True, calc_wfs=False,
initial_path=CONFIG_PASTIS.get('local', 'local_data_path'),
saveopds=False, norm_one_photon=True)
run_matrix.calc()
dir_run = run_matrix.overall_dir
print(f'All saved to {dir_run}.')

# Run the analysis
analysis = MultiModeAnalysis(C_TARGET, run_matrix.simulator, WHICH_DM, NUM_MODES, dir_run)
analysis.run()
62 changes: 58 additions & 4 deletions pastis/matrix_generation/matrix_from_efields.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import numpy as np

from pastis.config import CONFIG_PASTIS
from pastis.simulators.elt_imaging import ELTHarmoniSPC
from pastis.simulators.luvoir_imaging import LuvoirA_APLC
from pastis.simulators.scda_telescopes import HexRingAPLC
import pastis.simulators.webbpsf_imaging as webbpsf_imaging
Expand Down Expand Up @@ -81,7 +82,8 @@ def calculate_efields(self):
for i in range(self.number_all_modes):
efields = self.calculate_one_mode(i)
if self.calc_science:
self.efields_per_mode.append(efields['efield_science_plane'])
coro_field = efields['efield_science_plane'] if not self.instrument == 'ELT' else efields['efield_before_fpm']
self.efields_per_mode.append(coro_field)
if self.calc_wfs:
self.efields_per_mode_wfs.append(efields['efield_wfs_plane'])
self.efields_per_mode = np.array(self.efields_per_mode)
Expand Down Expand Up @@ -259,8 +261,12 @@ def calculate_ref_efield(self):
self.dh_mask = self.simulator.dh_mask

# Calculate contrast normalization factor from direct PSF (intensity)
unaberrated_coro_psf, direct = self.simulator.calc_psf(ref=True, norm_one_photon=self.norm_one_photon)
unaberrated_coro_psf, direct, inter = self.simulator.calc_psf(ref=True, norm_one_photon=self.norm_one_photon,
return_intermediate='efield')
self.norm = np.max(direct)
if self.instrument == 'ELT':
unaberrated_coro_psf = inter['before_fpm'].intensity
self.norm = np.max(direct.intensity)
hcipy.write_fits(unaberrated_coro_psf / self.norm, os.path.join(self.overall_dir, 'unaberrated_coro_psf.fits'))

npx = unaberrated_coro_psf.shaped.shape[0]
Expand All @@ -278,7 +284,7 @@ def calculate_ref_efield(self):

# Calculate reference E-field in focal plane, without any aberrations applied
unaberrated_ref_efield, _inter = self.simulator.calc_psf(return_intermediate='efield', norm_one_photon=self.norm_one_photon)
self.efield_ref = unaberrated_ref_efield.electric_field
self.efield_ref = unaberrated_ref_efield.electric_field if not self.instrument == 'ELT' else _inter['before_fpm'].electric_field

# Save unaberrated electric field at the science plane
if self.save_efields:
Expand Down Expand Up @@ -475,6 +481,53 @@ def setup_single_mode_function(self):
self.rst_cgi, self.resDir, self.saveopds)


class MatrixEfieldELT(MatrixEfieldInternalSimulator):
"""Calculate a PASTIS matrix HARMONI on the ELT, using E-fields."""

instrument = 'ELT'

def __init__(self, which_dm, dm_spec, design, calc_science=True, calc_wfs=False,
initial_path='', saveefields=True, saveopds=True, norm_one_photon=True):
"""
Parameters
----------
which_dm : string
which DM to calculate the matrix for - "seg_mirror", "harris_seg_mirror", "zernike_mirror"
dm_spec : tuple or int
Specification for the used DM:
for seg_mirror : int, number of local Zernike modes on each segment
for harris_seg_mirror : tuple (string, array, bool, bool, bool), absolute path to Harris spreadsheet, pad orientations, choice of Harris mode sets
for zernike_mirror : int, number of global Zernikes
design : string
Which SPC design to use in the coronagraph, HSP1 ro SHP2.
calc_science : bool, default True
whether to calculate the Efields in the science focal plane
calc_wfs : bool, default False
whether to calculate the Efields in the out-of-band Zernike WFS plane
initial_path : string
path to top-level directory where result folder should be saved to
saveefields : bool, default True
whether to save E-fields as fits file to disk or not
saveopds : bool, default True
whether to save images of pair-wise aberrated pupils to disk or not
norm_one_photon : bool, default True
whether to normalize the returned E-fields and intensities to one photon in the entrance pupil
"""
nb_seg = 798
seglist = np.arange(nb_seg) + 1
self.design = design
super().__init__(which_dm=which_dm, dm_spec=dm_spec, nb_seg=nb_seg, seglist=seglist, calc_science=calc_science, calc_wfs=calc_wfs,
initial_path=initial_path, saveefields=saveefields, saveopds=saveopds, norm_one_photon=norm_one_photon)

def instantiate_simulator(self):
sampling = CONFIG_PASTIS.getfloat('ELT', 'sampling')
optics_input = os.path.join(util.find_repo_location(), CONFIG_PASTIS.get('ELT', 'optics_path_in_repo'))
fpm_px = CONFIG_PASTIS.getint('ELT', 'fpm_px')
wvln = CONFIG_PASTIS.getfloat('ELT', 'lambda') * 1e-9

self.simulator = ELTHarmoniSPC(input_dir=optics_input, sampling=sampling, wvln=wvln, spc_design=self.design, fpm_rad=fpm_px)


def _simulator_matrix_single_mode(which_dm, number_all_modes, wfe_aber, simulator, calc_science, calc_wfs,
norm_one_photon, resDir, saveopds, mode_no):
"""Calculate the mean E-field of one aberrated mode on one of the internal simulator instances; for PastisMatrixEfields().
Expand Down Expand Up @@ -544,7 +597,8 @@ def _simulator_matrix_single_mode(which_dm, number_all_modes, wfe_aber, simulato

# Format returned Efields
efields = {'efield_science_plane': efield_focal_plane.electric_field,
'efield_wfs_plane': efield_wfs_plane}
'efield_wfs_plane': efield_wfs_plane,
'efield_before_fpm': inter['before_fpm'].electric_field}

return efields

Expand Down