Source code for paos.core.parseConfig

import configparser
import os
import sys
from typing import List

import astropy.units as u
import numpy as np

from paos import logger
from paos.classes.abcd import ABCD
from paos.util.material import Material


[docs] def getfloat(value): try: return np.float64(value) except: return np.nan
[docs] def getaperture(aperture, _data_): aperture = aperture.split(",") aperture_shape, aperture_type = aperture[0].split() _data_["aperture"] = { "shape": aperture_shape, "type": aperture_type, "xrad": getfloat(aperture[1]), "yrad": getfloat(aperture[2]), "xc": getfloat(aperture[3]), "yc": getfloat(aperture[4]), } return _data_
[docs] def parse_config(filename): """ Parse an ini lens file Parameters ---------- filename: string full path to ini file Returns ------- pup_diameter: float pupil diameter in lens units parameters: dict Dictionary with parameters defined in the section 'general' of the ini file field: List list of fields wavelengths: List list of wavelengths opt_chain_list: List Each list entry is a dictionary of the optical surfaces in the .ini file, estimated at the given wavelength. (Relevant only for diffractive components) Examples -------- >>> from paos.core.parseConfig import parse_config >>> pup_diameter, parameters, wavelengths, fields, opt_chains = parse_config('path/to/ini/file') """ config = configparser.ConfigParser() filename = os.path.expanduser(filename) if not os.path.exists(filename) or not os.path.isfile(filename): logger.error( f"Input file {filename} does not exist or is not a file. Quitting..." ) sys.exit() config.read(filename) # Parse parameters in section 'general' allowed_grid_size = [64, 128, 256, 512, 1024, 2048, 4096] allowed_zoom_val = [1, 2, 4, 8, 16] parameters = { "project": config["general"]["project"], "version": config["general"]["version"], } dtmp = config["general"].getint("grid_size") if dtmp not in allowed_grid_size: logger.error(f"Grid size not allowed. Allowed values are {allowed_grid_size}") raise ValueError( f"Grid size not allowed. Allowed values are {allowed_grid_size}" ) parameters["grid_size"] = dtmp dtmp = config["general"].getint("zoom") if dtmp not in allowed_zoom_val: logger.error(f"Zoom value not allowed. Allowed values are {allowed_zoom_val}") raise ValueError( f"Zoom value not allowed. Allowed values are {allowed_zoom_val}" ) elif dtmp == 1: logger.warning( "Zoom value is 1, i.e. the beam width occupies the whole of the grid width. " "This will result a PSF that is not Nyquist sampled." ) parameters["zoom"] = dtmp lens_unit = config["general"].get("lens_unit", "") if lens_unit != "m": logger.error("Verify lens_unit=m in ini file") raise ValueError("Verify lens_unit=m in ini file") Tambient = config["general"].getfloat("Tambient") parameters["Tambient"] = Tambient Pambient = config["general"].getfloat("Pambient") parameters["Pambient"] = Pambient # Parse section 'wavelengths' wavelengths = [] num = 1 while True: _wl_ = config["wavelengths"].getfloat(f"w{num:d}") if _wl_: wavelengths.append(_wl_) else: break num += 1 # Parse section 'fields' fields = [] num = 1 while True: _fld_ = config["fields"].get(f"f{num:d}") if _fld_: _fld_ = np.fromstring(_fld_, sep=",") _fld_ = np.tan(np.deg2rad(_fld_)) fields.append({"us": _fld_[0], "ut": _fld_[1]}) else: break num += 1 # Parse sections 'lens_??' opt_chain_list = [] pup_diameter = None # input pupil pup_diameter for _wl_ in wavelengths: n1, n2 = None, None # Refractive index glasslib = Material(_wl_, Tambient=Tambient, Pambient=Pambient) opt_chain = {} lens_num = 1 while f"lens_{lens_num:02d}" in config: _data_ = {"num": lens_num} element = config[f"lens_{lens_num:02d}"] lens_num += 1 if element.getboolean("Ignore"): continue _data_["type"] = element.get("SurfaceType", None) _data_["R"] = getfloat(element.get("Radius", "")) _data_["T"] = getfloat(element.get("Thickness", "")) _data_["material"] = element.get("Material", None) _data_["is_stop"] = element.getboolean("Stop", False) _data_["save"] = element.getboolean("Save", False) _data_["name"] = element.get("Comment", "") if _data_["type"] == "INIT": n1 = 1.0 aperture = element.get("aperture", "").split(",") aperture_shape, aperture_type = aperture[0].split() if aperture_shape == "elliptical" and aperture_type == "aperture": xpup = getfloat(aperture[2]) ypup = getfloat(aperture[3]) pup_diameter = 2.0 * max(xpup, ypup) continue if n1 is None or pup_diameter is None: logger.error("INIT is not the first surface in Lens Data.") raise ValueError("INIT is not the first surface in Lens Data.") if _data_["type"] == "Zernike": thickness = 0.0 curvature = 0.0 n2 = n1 wave = 1.0e-6 * getfloat(element.get("Par1", "")) _data_["Zordering"] = element.get("Par2", "").lower() _data_["Znormalize"] = element.getboolean("Par3") _data_["Zradius"] = getfloat(element.get("Par4", "")) _data_["Zorigin"] = element.get("Par5", "x") _data_["Zorthonorm"] = element.get("Par6", "False").lower() == "true" _data_["Zindex"] = np.fromstring( element.get("Zindex", ""), sep=",", dtype=np.int64 ) _data_["Z"] = ( np.fromstring(element.get("Z", ""), sep=",", dtype=np.float64) * wave ) aperture = element.get("aperture", "") if aperture: getaperture(aperture, _data_) _data_["ABCDt"] = ABCD( thickness=thickness, curvature=curvature, n1=n1, n2=n2, M=1.0, ) _data_["ABCDs"] = ABCD( thickness=thickness, curvature=curvature, n1=n1, n2=n2, M=1.0, ) elif _data_["type"] == "Grid Sag": thickness = 0.0 curvature = 0.0 n2 = n1 wave = 1.0e-6 * getfloat(element.get("Par1", "")) _data_["nx"] = getfloat(element.get("Par2", "")) _data_["ny"] = getfloat(element.get("Par3", "")) _data_["delx"] = getfloat(element.get("Par4", "")) _data_["dely"] = getfloat(element.get("Par5", "")) _data_["xdec"] = getfloat(element.get("Par6", "")) _data_["ydec"] = getfloat(element.get("Par7", "")) grid_sag_path = element.get("Par8", "") if not os.path.exists(grid_sag_path): logger.error(f"Grid sag file does not exist: {grid_sag_path}") raise ValueError(f"Grid sag file does not exist: {grid_sag_path}") with open(grid_sag_path, "rb") as f: grid_sag = np.load(f, allow_pickle=True).item() assert ( "data" in grid_sag.keys() ), "The .npy file must contain a dictionary with a 'data' key" def input_params(key, grid_sag, _data_): if key in grid_sag.keys(): logger.debug( f"Setting {key} from grid_sag file: {grid_sag[key]}" ) _data_[key] = grid_sag[key] for par in ["nx", "ny", "delx", "dely", "xdec", "ydec"]: input_params(par, grid_sag, _data_) _data_["grid_sag"] = grid_sag["data"] * wave _data_["ABCDt"] = ABCD( thickness=thickness, curvature=curvature, n1=n1, n2=n2, M=1.0, ) _data_["ABCDs"] = ABCD( thickness=thickness, curvature=curvature, n1=n1, n2=n2, M=1.0, ) elif _data_["type"] == "PSD": thickness = 0.0 curvature = 0.0 n2 = n1 _data_["A"] = getfloat(element.get("Par1", "")) _data_["B"] = getfloat(element.get("Par2", "")) _data_["C"] = getfloat(element.get("Par3", "")) _data_["fknee"] = getfloat(element.get("Par4", "")) _data_["fmin"] = getfloat(element.get("Par5", "")) _data_["fmax"] = getfloat(element.get("Par6", "")) _data_["SR"] = getfloat(element.get("Par7", "")) _data_["units"] = u.Unit(element.get("Par8", "")) _data_["ABCDt"] = ABCD( thickness=thickness, curvature=curvature, n1=n1, n2=n2, M=1.0, ) _data_["ABCDs"] = ABCD( thickness=thickness, curvature=curvature, n1=n1, n2=n2, M=1.0, ) elif _data_["type"] == "Coordinate Break": thickness = _data_["T"] if np.isfinite(_data_["T"]) else 0.0 curvature = 0.0 n2 = n1 _data_["xdec"] = getfloat(element.get("Par1", "")) _data_["ydec"] = getfloat(element.get("Par2", "")) _data_["xrot"] = getfloat(element.get("Par3", "")) _data_["yrot"] = getfloat(element.get("Par4", "")) _data_["ABCDt"] = ABCD( thickness=thickness, curvature=curvature, n1=n1, n2=n2, M=1.0, ) _data_["ABCDs"] = ABCD( thickness=thickness, curvature=curvature, n1=n1, n2=n2, M=1.0, ) elif _data_["type"] == "Paraxial Lens": focal_length = getfloat(element.get("Par1", "")) thickness = _data_["T"] if np.isfinite(_data_["T"]) else 0.0 curvature = 1 / focal_length if np.isfinite(focal_length) else 0.0 n2 = n1 aperture = element.get("aperture", "") if aperture: getaperture(aperture, _data_) _data_["ABCDt"] = ABCD( thickness=thickness, curvature=curvature, n1=n1, n2=n2, M=1.0, ) _data_["ABCDs"] = ABCD( thickness=thickness, curvature=curvature, n1=n1, n2=n2, M=1.0, ) elif _data_["type"] == "ABCD": thickness = _data_["T"] if np.isfinite(_data_["T"]) else 0.0 Ax = getfloat(element.get("Par1", "")) Bx = getfloat(element.get("Par2", "")) Cx = getfloat(element.get("Par3", "")) Dx = getfloat(element.get("Par4", "")) Ay = getfloat(element.get("Par5", "")) By = getfloat(element.get("Par6", "")) Cy = getfloat(element.get("Par7", "")) Dy = getfloat(element.get("Par8", "")) ABCDs = ABCD(thickness=thickness, curvature=0.0, n1=n1, n2=n1, M=1.0) ABCDt = ABCD(thickness=thickness, curvature=0.0, n1=n1, n2=n1, M=1.0) _ABCDs = np.array([[Ax, Bx], [Cx, Dx]]) _ABCDt = np.array([[Ay, By], [Cy, Dy]]) ABCDs.ABCD = ABCDs() @ _ABCDs ABCDt.ABCD = ABCDt() @ _ABCDt aperture = element.get("aperture", "") if aperture: getaperture(aperture, _data_) _data_["ABCDt"] = ABCDt _data_["ABCDs"] = ABCDs elif _data_["type"] == "Standard": thickness = _data_["T"] if np.isfinite(_data_["T"]) else 0.0 curvature = 1 / _data_["R"] if np.isfinite(_data_["R"]) else 0.0 aperture = element.get("aperture", "") if aperture: getaperture(aperture, _data_) if _data_["material"] == "MIRROR": n2 = -n1 elif _data_["material"] in glasslib.materials.keys(): n2 = glasslib.nmat(_data_["material"])[1] * np.sign(n1) else: n2 = 1.0 * np.sign(n1) _data_["ABCDt"] = ABCD( thickness=thickness, curvature=curvature, n1=n1, n2=n2, M=1.0, ) _data_["ABCDs"] = ABCD( thickness=thickness, curvature=curvature, n1=n1, n2=n2, M=1.0, ) else: logger.error(f"Surface Type not recognised: {str(_data_['type']):s}") raise ValueError( f"Surface Type not recognised: {str(_data_['type']):s}" ) opt_chain[_data_["num"]] = _data_ n1 = n2 opt_chain_list.append(opt_chain) return pup_diameter, parameters, wavelengths, fields, opt_chain_list