Skip to content

Commit

Permalink
Modified esis.optics.Filter to use `optika.materials.ThinFilmFilter…
Browse files Browse the repository at this point in the history
…`. (#10)
  • Loading branch information
byrdie authored Aug 1, 2024
1 parent de04fb2 commit 30138d6
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 137 deletions.
9 changes: 2 additions & 7 deletions esis/flights/f1/optics/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import esis
from . import primaries
from . import gratings
from . import filters

__all__ = [
"design_full",
Expand Down Expand Up @@ -200,15 +201,9 @@ def design_full(
)

filter = esis.optics.Filter(
material=None,
material_oxide=None,
material_mesh=None,
ratio_mesh=82 * u.percent,
frequency_mesh=70 / u.imperial.inch,
material=filters.materials.thin_film_design(),
radius_clear=15 * u.mm,
width_border=0 * u.mm,
thickness=100 * u.nm,
thickness_oxide=4 * u.nm,
distance_radial=95.9 * u.mm,
azimuth=angle_channel.copy(),
translation=na.Cartesian3dVectorArray(
Expand Down
82 changes: 11 additions & 71 deletions esis/optics/_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,8 @@ class AbstractFilter(
):
@property
@abc.abstractmethod
def material(self) -> None | optika.materials.AbstractMaterial:
"""the nominal material of the filter"""

@property
@abc.abstractmethod
def material_oxide(self) -> None | optika.materials.AbstractMaterial:
"""material representing the oxidation layer"""

@property
@abc.abstractmethod
def material_mesh(self) -> None | optika.materials.AbstractMaterial:
"""name of the material that the mesh is made from"""

@property
@abc.abstractmethod
def ratio_mesh(self) -> float | na.AbstractScalar:
"""the fraction of light that the mesh blocks"""

@property
@abc.abstractmethod
def frequency_mesh(self) -> u.Quantity | na.AbstractScalar:
"""the number of mesh lines per unit length"""
def material(self) -> optika.materials.AbstractThinFilmFilter:
"""A model of the filter material including the mesh and oxide."""

@property
@abc.abstractmethod
Expand All @@ -55,60 +35,20 @@ def width_border(self) -> u.Quantity | na.AbstractScalar:
"""width of the frame around the clear aperture"""

@property
@abc.abstractmethod
def thickness(self) -> u.Quantity | na.AbstractScalar:
"""nominal physical thickness of the filter"""
def surface(self) -> optika.surfaces.Surface:

@property
@abc.abstractmethod
def thickness_oxide(self) -> u.Quantity | na.AbstractScalar:
"""thickness of the oxide layers on the outside of the filter"""

@property
def surfaces(self) -> list[optika.surfaces.Surface]:
material = self.material
material_oxide = self.material_oxide
radius_clear = self.radius_clear
radius_mech = radius_clear + self.width_border
aperture = optika.apertures.CircularAperture(radius_clear)
aperture_mechanical = optika.apertures.CircularAperture(radius_mech)
thickness = self.thickness
thickness_oxide = self.thickness_oxide
t = self.transformation
return [
optika.surfaces.Surface(
name="filter-oxide-front",
material=material_oxide,
aperture=aperture,
aperture_mechanical=aperture_mechanical,
transformation=t,
),
optika.surfaces.Surface(
name="filter-front",
material=material,
transformation=t
@ na.transformations.Cartesian3dTranslation(
z=thickness_oxide,
),
),
optika.surfaces.Surface(
name="filter-back",
material=material_oxide,
transformation=t
@ na.transformations.Cartesian3dTranslation(
z=thickness_oxide + thickness
),
),
optika.surfaces.Surface(
name="filter-oxide-back",
aperture=aperture,
aperture_mechanical=aperture_mechanical,
transformation=t
@ na.transformations.Cartesian3dTranslation(
z=thickness_oxide + thickness + thickness_oxide
),
),
]

return optika.surfaces.Surface(
name="filter",
material=self.material,
aperture=aperture,
aperture_mechanical=aperture_mechanical,
transformation=self.transformation,
)


@dataclasses.dataclass(eq=False, repr=False)
Expand Down
2 changes: 1 addition & 1 deletion esis/optics/_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ def system(self) -> optika.systems.SequentialSystem:
surfaces += [self.primary_mirror.surface]
surfaces += [self.field_stop.surface]
surfaces += [self.grating.surface]
surfaces += self.filter.surfaces if self.filter is not None else []
surfaces += [self.filter.surface]

wavelength_min = self.wavelength_min
wavelength_max = self.wavelength_max
Expand Down
77 changes: 19 additions & 58 deletions esis/optics/_tests/test_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,39 +22,7 @@ def test_material(
):
result = a.material
if result is not None:
assert isinstance(result, optika.materials.AbstractMaterial)

def test_material_oxide(
self,
a: esis.optics.abc.AbstractFilter,
):
result = a.material_oxide
if result is not None:
assert isinstance(result, optika.materials.AbstractMaterial)

def test_material_mesh(
self,
a: esis.optics.abc.AbstractFilter,
):
result = a.material_mesh
if result is not None:
assert isinstance(result, optika.materials.AbstractMaterial)

def test_ratio_mesh(
self,
a: esis.optics.abc.AbstractFilter,
):
result = a.ratio_mesh
assert na.unit_normalized(result).is_equivalent(u.dimensionless_unscaled)
assert np.all(result >= 0)

def test_frequency_mesh(
self,
a: esis.optics.abc.AbstractFilter,
):
result = a.frequency_mesh
assert na.unit_normalized(result).is_equivalent(1 / u.mm)
assert np.all(result >= 0)
assert isinstance(result, optika.materials.AbstractThinFilmFilter)

def test_radius_clear(
self,
Expand All @@ -72,43 +40,36 @@ def test_width_border(
assert na.unit_normalized(result).is_equivalent(u.mm)
assert np.all(result >= 0)

def test_thickness(
self,
a: esis.optics.abc.AbstractFilter,
):
result = a.thickness
assert na.unit_normalized(result).is_equivalent(u.mm)
assert np.all(result >= 0)

def test_thickness_oxide(
self,
a: esis.optics.abc.AbstractFilter,
):
result = a.thickness_oxide
assert na.unit_normalized(result).is_equivalent(u.mm)
assert np.all(result >= 0)

def test_surfaces(
def test_surface(
self,
a: esis.optics.abc.AbstractFilter,
):
result = a.surfaces
assert isinstance(result, list)
for surface in result:
assert isinstance(surface, optika.surfaces.AbstractSurface)
result = a.surface
assert isinstance(result, optika.surfaces.AbstractSurface)


@pytest.mark.parametrize(
argnames="a",
argvalues=[
esis.optics.Filter(),
esis.optics.Filter(
ratio_mesh=30 * u.percent,
frequency_mesh=10 / u.mm,
material=optika.materials.ThinFilmFilter(
layer=optika.materials.Layer(
chemical="Al",
thickness=100 * u.nm,
),
layer_oxide=optika.materials.Layer(
chemical="Al2O3",
thickness=4 * u.nm,
),
mesh=optika.materials.meshes.Mesh(
chemical="Ni",
efficiency=0.75,
pitch=1 * u.um,
),
),
radius_clear=15 * u.mm,
width_border=1 * u.mm,
thickness=100 * u.nm,
thickness_oxide=10 * u.nm,
distance_radial=100 * u.mm,
azimuth=45 * u.deg,
yaw=15 * u.deg,
Expand Down

0 comments on commit 30138d6

Please sign in to comment.