from shiny import ui
from paos import PSD, Zernike
from paos.gui.core.shared import (
CARD_HEADER_CLASS,
ICONS,
nested_div,
output_text_verbatim,
vspace,
)
Placeholder = {
"Par1": {
"INIT": "",
"Coordinate Break": "Xdecenter",
"Standard": "",
"Paraxial Lens": "Focal length",
"ABCD": "Ax",
"Zernike": "Wavelength",
"PSD": "A",
"Grid Sag": "Wavelength",
},
"Par2": {
"INIT": "",
"Coordinate Break": "Ydecenter",
"Standard": "",
"Paraxial Lens": "",
"ABCD": "Bx",
"Zernike": "Ordering",
"PSD": "B",
"Grid Sag": "Nx",
},
"Par3": {
"INIT": "",
"Coordinate Break": "Xtilt",
"Standard": "",
"Paraxial Lens": "",
"ABCD": "Cx",
"Zernike": "Normalization",
"PSD": "C",
"Grid Sag": "Ny",
},
"Par4": {
"INIT": "",
"Coordinate Break": "Ytilt",
"Standard": "",
"Paraxial Lens": "",
"ABCD": "Dx",
"Zernike": "Radius of S.A.",
"PSD": "fknee",
"Grid Sag": "Dx",
},
"Par5": {
"INIT": "",
"Coordinate Break": "",
"Standard": "",
"Paraxial Lens": "",
"ABCD": "Ay",
"Zernike": "Origin",
"PSD": "fmin",
"Grid Sag": "Dy",
},
"Par6": {
"INIT": "",
"Coordinate Break": "",
"Standard": "",
"Paraxial Lens": "",
"ABCD": "By",
"Zernike": "Orthonorm",
"PSD": "fmax",
"Grid Sag": "Xdecenter (pix)",
},
"Par7": {
"INIT": "",
"Coordinate Break": "",
"Standard": "",
"Paraxial Lens": "",
"ABCD": "Cy",
"Zernike": "",
"PSD": "SR",
"Grid Sag": "Ydecenter (pix)",
},
"Par8": {
"INIT": "",
"Coordinate Break": "",
"Standard": "",
"Paraxial Lens": "",
"ABCD": "Dy",
"Zernike": "",
"PSD": "units",
"Grid Sag": "Errormap filepath",
},
}
[docs]
def app_elems(config):
general_elems = [
ui.input_text(
"project",
"Project name",
value=config["general"].get("project", "Template"),
),
ui.input_text("comment", "Comment", value=config["general"].get("comment", "")),
ui.input_text(
"version",
"Version",
value=config["general"].get("version", "0.1"),
),
ui.input_text(
"grid_size",
"Grid size",
value=config["general"].getint("grid_size", "512"),
),
ui.input_text("zoom", "Zoom", value=config["general"].getint("zoom", "4")),
ui.input_text(
"tambient",
"Ambient temperature [°C]",
value=config["general"].getfloat("tambient", "20.0"),
),
ui.input_text(
"pambient",
"Ambient pressure [atm]",
value=config["general"].getfloat("pambient", "1.0"),
),
ui.tags.div(
ui.tags.label("Wavelength unit", class_="form-label"),
ui.tags.input(
id="wavelength_unit",
type="text",
value=config["general"].get("wavelength_unit", "micron"),
readonly=True,
class_="form-control",
),
),
vspace,
ui.tags.div(
ui.tags.label("Angle unit", class_="form-label"),
ui.tags.input(
id="angle_unit",
type="text",
value=config["general"].get("angle_unit", "deg"),
readonly=True,
class_="form-control",
),
),
vspace,
ui.tags.div(
ui.tags.label("Lens unit", class_="form-label"),
ui.tags.input(
id="lens_unit",
type="text",
value=config["general"].get("lens_unit", "m"),
readonly=True,
class_="form-control",
),
),
]
field_elems = {}
for n, (key, value) in enumerate(config.items("fields")):
n += 1
field_elems[n] = {}
field_elems[n]["#"] = {
"f": ui.p,
"width": 5,
"value": n,
}
field_elems[n]["Field"] = {
"f": ui.input_text,
"id": key,
"value": value,
"width": 7,
}
wl_elems = {}
for n, (key, value) in enumerate(config.items("wavelengths")):
n += 1
wl_elems[n] = {}
wl_elems[n]["#"] = {
"f": ui.p,
"width": 5,
"value": n,
}
wl_elems[n]["Wavelength"] = {
"f": ui.input_text,
"id": key,
"value": value,
"width": 7,
}
lens_elems = {}
for key in config.sections():
if not key.startswith("lens_"):
continue
n = int(key.split("_")[1])
item = config[key]
lens_elems[n] = {}
lens_elems[n]["#"] = {
"f": ui.p,
"width": 1,
"value": n,
}
surface_type = item.get("surfacetype")
lens_elems[n]["SurfaceType"] = {
"f": ui.tags.input,
# "choices": [
# "INIT",
# "Coordinate Break",
# "Standard",
# "Paraxial Lens",
# "ABCD",
# "Zernike",
# "PSD",
# "Grid Sag",
# ],
"width": 1,
"value": surface_type,
# "selected": surface_type,
"prefix": "lens_",
"readonly": True,
}
lens_elems[n]["Comment"] = {
"f": ui.input_text,
"value": item.get("comment"),
"width": 1,
"prefix": "lens_",
}
lens_elems[n]["Radius"] = {
"f": ui.input_text,
"value": item.get("radius", ""),
"width": 1,
"prefix": "lens_",
}
lens_elems[n]["Thickness"] = {
"f": ui.input_text,
"value": item.get("thickness", ""),
"width": 1,
"prefix": "lens_",
}
lens_elems[n]["Material"] = {
"f": ui.input_select,
"choices": [
"",
"MIRROR",
"BK7",
"BAF2",
"CAF2",
"SAPPHIRE",
"SF11",
"ZNSE",
],
"width": 1,
"selected": item.get("material", ""),
"prefix": "lens_",
}
lens_elems[n]["Save"] = {
"f": ui.input_checkbox,
"value": item.getboolean("save", True),
"width": 1,
"prefix": "lens_",
}
lens_elems[n]["Ignore"] = {
"f": ui.input_checkbox,
"value": item.getboolean("ignore", False),
"width": 1,
"prefix": "lens_",
}
lens_elems[n]["Stop"] = {
"f": ui.input_checkbox,
"value": item.getboolean("stop", False),
"width": 1,
"prefix": "lens_",
}
aperture = item.get("aperture", "")
if aperture != "":
aperture = " ".join(aperture.split())
aperture = aperture.split(",")
aperture_type = str(aperture[0])
xhw = float(aperture[1])
yhw = float(aperture[2])
xdecenter = float(aperture[3])
ydecenter = float(aperture[4])
else:
aperture_type = xhw = yhw = xdecenter = ydecenter = ""
lens_elems[n]["Aperture"] = {
"title": "Settings",
"f": ui.popover,
"width": 1,
"value": {
"Aperture_Type": {
"f": ui.input_select,
"selected": aperture_type,
"choices": [
"",
"rectangular aperture",
"rectangular obscuration",
"elliptical aperture",
"elliptical obscuration",
],
"label": "Type",
"prefix": "lens_",
},
"Aperture_xhw": {
"f": ui.input_text,
"value": xhw,
"label": "X-Half Width",
"prefix": "lens_",
},
"Aperture_yhw": {
"f": ui.input_text,
"value": yhw,
"label": "Y-Half Width",
"prefix": "lens_",
},
"Aperture_xdecenter": {
"f": ui.input_text,
"value": xdecenter,
"label": "X-Decenter",
"prefix": "lens_",
},
"Aperture_ydecenter": {
"f": ui.input_text,
"value": ydecenter,
"label": "Y-Decenter",
"prefix": "lens_",
},
},
}
lens_elems[n]["Par1"] = {
"f": ui.input_text,
"value": item.get("par1", ""),
"width": 1,
"prefix": "lens_",
"placeholder": Placeholder["Par1"][surface_type],
}
lens_elems[n]["Par2"] = {
"f": ui.input_text,
"value": item.get("par2", ""),
"width": 1,
"prefix": "lens_",
"placeholder": Placeholder["Par2"][surface_type],
}
lens_elems[n]["Par3"] = {
"f": ui.input_text,
"value": item.get("par3", ""),
"width": 1,
"prefix": "lens_",
"placeholder": Placeholder["Par3"][surface_type],
}
lens_elems[n]["Par4"] = {
"f": ui.input_text,
"value": item.get("par4", ""),
"width": 1,
"prefix": "lens_",
"placeholder": Placeholder["Par4"][surface_type],
}
lens_elems[n]["Par5"] = {
"f": ui.input_text,
"value": item.get("par5", ""),
"width": 1,
"prefix": "lens_",
"placeholder": Placeholder["Par5"][surface_type],
}
lens_elems[n]["Par6"] = {
"f": ui.input_text,
"value": item.get("par6", ""),
"width": 1,
"prefix": "lens_",
"placeholder": Placeholder["Par6"][surface_type],
}
lens_elems[n]["Par7"] = {
"f": ui.input_text,
"value": item.get("par7", ""),
"width": 1,
"prefix": "lens_",
"placeholder": Placeholder["Par7"][surface_type],
}
lens_elems[n]["Par8"] = {
"f": ui.input_text,
"value": item.get("par8", ""),
"width": 1,
"prefix": "lens_",
"placeholder": Placeholder["Par8"][surface_type],
}
zernike_elems = {}
for key in config.sections():
if not key.startswith("lens_"):
continue
item = config[key]
if not item.get("surfacetype") == "Zernike":
continue
n = int(key.split("_")[1])
zernike_elems[n] = {}
zindex = item.get("zindex", "").split(",")
zcoeff = item.get("z", "").split(",")
ordering = item.get("par2")
azimuthal, radial = Zernike.j2mn(N=len(zindex), ordering=ordering)
for zi, zc in zip(zindex, zcoeff):
zernike_elems[n][zi] = {}
zernike_elems[n][zi]["n"] = {
"f": ui.p,
"width": 3,
"value": radial[int(zi)],
}
zernike_elems[n][zi]["m"] = {
"f": ui.p,
"width": 3,
"value": azimuthal[int(zi)],
}
zernike_elems[n][zi]["Zindex"] = {
"f": ui.p,
"width": 3,
"value": zi,
}
zernike_elems[n][zi]["Zcoeff"] = {
"f": ui.input_text,
"width": 3,
"value": zc,
"prefix": f"lens_{n}_",
}
zernike_sidebar_elems = []
zernike_tab_elems = []
if zernike_elems:
zernike_choices = [f"S{n}" for n in zernike_elems]
zernike_sidebar_elems = [
*[
ui.input_select(
id=f"select_{name}", label=f"Select {name}", choices=choice
)
for name, choice in zip(["Zernike"], [zernike_choices])
],
]
zernike_tab_elems = [
ui.layout_column_wrap(
ui.card(
ui.card_header("Explorer"),
ui.card_body(
output_text_verbatim("zernike_inputs"),
nested_div("zernike"),
),
max_height="45vw",
),
ui.card(
ui.card_header("Plot"),
ui.card_footer(
ui.input_action_button(
"do_plot_zernike",
"Run",
icon=ICONS["run"],
class_="btn-success",
),
ui.input_action_button(
"download_plot_zernike",
"Save",
icon=ICONS["save"],
),
),
ui.card_body(
output_text_verbatim("plot_zernike_inputs"),
ui.output_plot("plot_zernike"),
),
min_height="45vw",
),
),
]
psd_elems = {}
for key in config.sections():
if not key.startswith("lens_"):
continue
item = config[key]
if not item.get("surfacetype") == "PSD":
continue
n = int(key.split("_")[1])
psd_elems[n] = {}
psd_elems[n][0] = {}
A = float(item.get("Par1"))
B = float(item.get("Par2"))
C = float(item.get("Par3"))
fknee = float(item.get("Par4"))
fmin = float(item.get("Par5"))
fmax = float(item.get("Par6"))
SR = item.get("Par7")
units = item.get("Par8")
sfe_rms = PSD.sfe_rms(A, B, C, fknee, fmin, fmax)
psd_elems[n][0]["PSD SFE RMS"] = {
"f": ui.p,
"width": 2,
"value": f"{sfe_rms:.3f}",
}
psd_elems[n][0]["SR RMS"] = {
"f": ui.p,
"width": 2,
"value": f"{SR}",
}
psd_elems[n][0]["SFE units"] = {
"f": ui.p,
"width": 2,
"value": f"{units}",
}
psd_sidebar_elems = []
psd_tab_elems = []
if psd_elems:
psd_choices = [f"S{n}" for n in psd_elems]
psd_sidebar_elems = [
*[
ui.input_select(
id=f"select_{name}", label=f"Select {name}", choices=choice
)
for name, choice in zip(["PSD"], [psd_choices])
],
]
psd_tab_elems = [
ui.layout_column_wrap(
ui.card(
ui.card_header("Explorer"),
ui.card_body(
output_text_verbatim("psd_inputs"),
output_text_verbatim("psd_output"),
nested_div("PSD"),
),
ui.card_footer(
ui.tags.div(
ui.input_action_button(
"calc_PSD",
"Run",
icon=ICONS["run"],
class_="btn-success",
),
),
),
max_height="45vh",
),
ui.card(
ui.card_header("Plot"),
ui.card_footer(
ui.input_action_button(
"do_plot_PSD",
"Run",
icon=ICONS["run"],
class_="btn-success",
),
ui.input_action_button(
"download_plot_PSD",
"Save",
icon=ICONS["save"],
),
),
ui.card_body(
output_text_verbatim("plot_psd_inputs"),
ui.output_plot("plot_PSD"),
),
min_height="45vw",
),
),
]
gridsag_elems = {}
for key in config.sections():
if not key.startswith("lens_"):
continue
item = config[key]
if not item.get("surfacetype") == "Grid Sag":
continue
n = int(key.split("_")[1])
gridsag_elems[n] = {}
gridsag_elems[n][0] = {}
filename = item.get("Par8")
gridsag_elems[n][0]["Filename"] = {
"f": ui.p,
"width": 2,
"value": f"{filename}",
}
gridsag_sidebar_elems = []
gridsag_tab_elems = []
if gridsag_elems:
gridsag_choices = [f"S{n}" for n in gridsag_elems]
gridsag_sidebar_elems = [
*[
ui.input_select(
id=f"select_{name}", label=f"Select {name}", choices=choice
)
for name, choice in zip(["gridsag"], [gridsag_choices])
],
]
gridsag_tab_elems = [
ui.layout_column_wrap(
ui.card(
ui.card_header("Explorer"),
ui.card_body(
output_text_verbatim("gridsag_inputs"),
output_text_verbatim("gridsag_output"),
nested_div("gridsag"),
),
max_height="45vh",
),
ui.card(
ui.card_header(
"Plot",
),
ui.card_footer(
ui.input_action_button(
"do_plot_gridsag",
"Run",
icon=ICONS["run"],
class_="btn-success",
),
ui.input_action_button(
"download_plot_gridsag", "Save", icon=ICONS["save"]
),
),
ui.card_body(
output_text_verbatim("plot_gridsag_inputs"),
ui.output_plot("plot_gridsag"),
),
min_height="45vw",
),
),
]
wfe_elems = (
zernike_sidebar_elems,
zernike_elems,
zernike_tab_elems,
psd_sidebar_elems,
psd_elems,
psd_tab_elems,
gridsag_sidebar_elems,
gridsag_elems,
gridsag_tab_elems,
)
surface_choices = [
f"S{key}" for key, item in lens_elems.items() if item["Save"]["value"]
]
wl_choices = [f"w{n}" for n in wl_elems]
field_choices = [f"f{n}" for n in field_elems]
analysis_sidebar_elems = [
*[
ui.input_select(id=f"select_{name}", label=f"Select {name}", choices=choice)
for name, choice in zip(["field", "wl"], [field_choices, wl_choices])
],
]
analysis_elems = [
ui.layout_column_wrap(
ui.navset_card_tab(
ui.nav_panel(
"Fresnel POP",
ui.card(
ui.card_body(
output_text_verbatim("pop_inputs"),
output_text_verbatim("pop_output"),
),
ui.card_footer(
ui.tags.div(
ui.input_action_button(
"calc_pop",
"Run",
icon=ICONS["run"],
class_="btn-success",
),
ui.input_action_button(
"download_pop", "Save", icon=ICONS["save"]
),
),
),
max_height="45vh",
),
),
ui.nav_panel(
"Ray Tracing",
ui.card(
ui.card_body(
output_text_verbatim("raytrace_inputs"),
output_text_verbatim("raytrace_output"),
),
ui.card_footer(
ui.tags.div(
ui.input_action_button(
"calc_raytrace",
"Run",
icon=ICONS["run"],
class_="btn-success",
),
ui.input_action_button(
"download_raytrace", "Save", icon=ICONS["save"]
),
),
),
max_height="45vh",
),
),
),
ui.card(
ui.card_header(
ui.tags.div(
"Plot",
ui.popover(
ICONS["gear"],
*[
ui.input_select(
id="plot_select_surface",
label="Select surface",
choices=surface_choices,
selected=surface_choices[-1],
),
ui.input_select(
id="plot_select_scale",
label="Select scale",
choices=["log", "linear"],
),
ui.input_text(
id="plot_select_zoom",
label="Select zoom",
value=1.0,
),
ui.input_checkbox(
id="plot_select_dark_rings",
label="Dark rings",
value=True,
),
],
title="Settings",
placement="top",
),
class_=CARD_HEADER_CLASS,
),
),
ui.card_footer(
ui.tags.div(
ui.input_action_button(
"do_plot",
"Run",
icon=ICONS["run"],
class_="btn-success",
),
ui.input_action_button(
"download_plot", "Save", icon=ICONS["save"]
),
),
),
ui.card_body(
output_text_verbatim("plot_inputs"),
ui.output_plot("plot"),
),
min_height="45vw",
),
fillable=False,
),
]
units_elems = []
return (
general_elems,
field_elems,
wl_elems,
lens_elems,
wfe_elems,
analysis_sidebar_elems,
analysis_elems,
units_elems,
)