import gc
from copy import deepcopy
import numpy as np
from paos import logger
from paos.classes.abcd import ABCD
from paos.classes.wfo import WFO
from paos.core.coordinateBreak import coordinate_break
[docs]
def push_results(wfo):
retval = {
"amplitude": wfo.amplitude,
"wz": wfo.wz,
"distancetofocus": wfo.distancetofocus,
"fratio": wfo.fratio,
"phase": wfo.phase,
"dx": wfo.dx,
"dy": wfo.dy,
"wfo": wfo.wfo,
"wl": wfo.wl,
"extent": wfo.extent,
"propagator": wfo.propagator,
}
return retval
[docs]
def run(pupil_diameter, wavelength, gridsize, zoom, field, opt_chain):
"""
Run the POP.
Parameters
----------
pupil_diameter: scalar
input pupil diameter in meters
wavelength: scalar
wavelength in meters
gridsize: scalar
the size of the simulation grid. It has to be a power of 2
zoom: scalar
zoom factor
field: dictionary
contains the slopes in the tangential and sagittal planes as field={'vt': slopey, 'vs': slopex}
opt_chain: list
the list of the optical elements parsed by paos.core.parseConfig.parse_config
Returns
-------
out: dict
dictionary containing the results of the POP
Examples
--------
>>> from paos.core.parseConfig import parse_config
>>> from paos.core.run import run
>>> from paos.core.plot import simple_plot
>>> pup_diameter, parameters, wavelengths, fields, opt_chains = parse_config('path/to/conf/file')
>>> ret_val = run(pup_diameter, 1.0e-6 * wavelengths[0], parameters['grid_size'], parameters['zoom'], fields[0], opt_chains[0])
"""
assert isinstance(opt_chain, dict), "opt_chain must be a dict"
retval = {}
vt = np.array([0.0, field["ut"]])
vs = np.array([0.0, field["us"]])
ABCDt = ABCD()
ABCDs = ABCD()
wfo = WFO(pupil_diameter, wavelength, gridsize, zoom)
for index, item in opt_chain.items():
logger.trace(f"Surface: {item['name']}")
if item["type"] == "Coordinate Break":
logger.trace("Apply coordinate break.")
vt, vs = coordinate_break(
vt,
vs,
item["xdec"],
item["ydec"],
item["xrot"],
item["yrot"],
0.0,
)
_retval_ = {"aperture": None}
# Check if aperture needs to be applied
if "aperture" in item:
xdec = (
item["aperture"]["xc"] if np.isfinite(item["aperture"]["xc"]) else vs[0]
)
ydec = (
item["aperture"]["yc"] if np.isfinite(item["aperture"]["yc"]) else vt[0]
)
xrad = item["aperture"]["xrad"]
yrad = item["aperture"]["yrad"]
xrad *= np.sqrt(1 / (vs[1] ** 2 + 1))
yrad *= np.sqrt(1 / (vt[1] ** 2 + 1))
xaper = xdec - vs[0]
yaper = ydec - vt[0]
obscuration = False if item["aperture"]["type"] == "aperture" else True
if np.all(np.isfinite([xrad, yrad])):
logger.trace("Apply aperture")
aper = wfo.aperture(
xaper,
yaper,
hx=xrad,
hy=yrad,
shape=item["aperture"]["shape"],
obscuration=obscuration,
)
_retval_["aperture"] = aper
# Check if this is a stop surface
if item["is_stop"]:
logger.trace("Apply stop")
wfo.make_stop()
if item["type"] == "Zernike":
logger.trace("Apply Zernike")
radius = item["Zradius"] if np.isfinite(item["Zradius"]) else wfo.wz
Zmask = False
if item["Zorthonorm"]:
assert "aperture" in item, "Zorthonorm requires aperture"
aperture = _retval_["aperture"]
Zmask = (
~aperture.to_mask(method="exact")
.to_image(wfo._wfo.shape)
.astype(bool)
)
_retval_["wfe"] = wfo.zernikes(
item["Zindex"],
item["Z"],
item["Zordering"],
item["Znormalize"],
radius,
origin=item["Zorigin"],
orthonorm=item["Zorthonorm"],
mask=Zmask,
)
if item["type"] == "Grid Sag":
logger.trace("Apply grid sag")
_retval_["wfe"] = wfo.grid_sag(
item["grid_sag"],
item["nx"],
item["ny"],
item["delx"],
item["dely"],
item["xdec"],
item["ydec"],
)
if item["type"] == "PSD":
logger.trace("Apply PSD")
_retval_["wfe"] = wfo.psd(
item["A"],
item["B"],
item["C"],
item["fknee"],
item["fmin"],
item["fmax"],
item["SR"],
item["units"],
)
_retval_.update(push_results(wfo))
Ms = item["ABCDs"].M
Mt = item["ABCDt"].M
fl = (
np.inf
if (item["ABCDt"].power == 0)
else item["ABCDt"].cout / item["ABCDt"].power
)
T = item["ABCDt"].cout * item["ABCDt"].thickness
n1n2 = item["ABCDt"].n1n2
logger.trace(f"n1n2: {n1n2:.4f}")
if Mt != 1.0 or Ms != 1.0:
logger.trace("Apply magnification")
wfo.Magnification(Mt, Ms)
if np.abs(n1n2) != 1.0:
logger.trace("Apply medium change")
wfo.ChangeMedium(n1n2)
if np.isfinite(fl):
logger.trace("Apply lens")
wfo.lens(fl)
if np.isfinite(T) and np.abs(T) > 1e-10:
logger.trace(f"Apply propagation thickness: T: {T:.4f}")
wfo.propagate(T)
vt = item["ABCDt"]() @ vt
vs = item["ABCDs"]() @ vs
ABCDt = item["ABCDt"] * ABCDt
ABCDs = item["ABCDs"] * ABCDs
logger.trace(
f"F num: {_retval_['fratio']:2f}, distance to focus: {wfo.distancetofocus:.6f}"
)
_retval_["ABCDt"] = ABCDt
_retval_["ABCDs"] = ABCDs
if item["save"]:
logger.trace("Save optical surface to output dict")
retval[item["num"]] = deepcopy(_retval_)
del _retval_
_ = gc.collect()
return retval