Skip to content
Merged
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
12 changes: 12 additions & 0 deletions optika/systems/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""
Optical systems consisting of multiple optical surfaces.
"""

from ._systems import AbstractSystem
from ._sequential import AbstractSequentialSystem, SequentialSystem

__all__ = [
"AbstractSystem",
"AbstractSequentialSystem",
"SequentialSystem",
]
43 changes: 2 additions & 41 deletions optika/systems.py → optika/systems/_sequential.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
"""
Optical systems consisting of multiple optical surfaces.
"""

from __future__ import annotations
from typing import Sequence, Callable, Any, ClassVar
import abc
import dataclasses
import functools
from ezdxf.addons.r12writer import R12FastStreamWriter
import astropy.units as u
import astropy.visualization
import numpy as np
Expand All @@ -16,50 +13,14 @@
import matplotlib.pyplot as plt
import named_arrays as na
import optika
from ezdxf.addons.r12writer import R12FastStreamWriter
from . import AbstractSystem

__all__ = [
"AbstractSystem",
"AbstractSequentialSystem",
"SequentialSystem",
]


@dataclasses.dataclass(eq=False, repr=False)
class AbstractSystem(
optika.mixins.DxfWritable,
optika.mixins.Plottable,
optika.mixins.Printable,
optika.mixins.Transformable,
optika.mixins.Shaped,
):
"""
An interface describing an optical system.

Could potentially be sequential or non-sequential.
"""

@abc.abstractmethod
def image(
self,
scene: na.FunctionArray[na.SpectralPositionalVectorArray, na.AbstractScalar],
**kwargs: Any,
) -> na.SpectralPositionalVectorArray:
"""
Forward model of the optical system.
Maps the given spectral radiance of a scene to detector counts.

Parameters
----------
scene
The spectral radiance of the scene as a function of wavelength
and field position.
kwargs
Additional keyword arguments used by subclass implementations
of this method.
"""


@dataclasses.dataclass(eq=False, repr=False)
class AbstractSequentialSystem(
AbstractSystem,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,54 +4,8 @@
import astropy.units as u
import named_arrays as na
import optika
from . import test_mixins


class AbstractTestAbstractSystem(
test_mixins.AbstractTestPlottable,
test_mixins.AbstractTestPrintable,
test_mixins.AbstractTestTransformable,
test_mixins.AbstractTestShaped,
):

@pytest.mark.parametrize(
argnames="scene",
argvalues=[
na.FunctionArray(
inputs=na.SpectralPositionalVectorArray(
wavelength=na.linspace(
start=530 * u.nm,
stop=531 * u.nm,
axis="wavelength",
num=2,
),
position=na.Cartesian2dVectorLinearSpace(
start=-1,
stop=+1,
axis=na.Cartesian2dVectorArray("field_x", "field_y"),
num=11,
),
),
outputs=na.random.uniform(
low=0 * u.photon / u.cm**2 / u.arcsec**2 / u.s / u.nm,
high=100 * u.photon / u.cm**2 / u.arcsec**2 / u.s / u.nm,
shape_random=dict(field_x=10, field_y=10),
),
)
],
)
def test_image(
self,
a: optika.systems.AbstractSystem,
scene: na.FunctionArray[na.SpectralPositionalVectorArray, na.AbstractScalar],
):
result = a.image(scene)
assert isinstance(result, na.FunctionArray)
assert isinstance(result.inputs, na.SpectralPositionalVectorArray)
assert isinstance(result.outputs, na.AbstractScalar)
assert np.all(result.inputs.wavelength > 0 * u.nm)
assert na.unit_normalized(result.inputs.position).is_equivalent(u.mm)
assert result.outputs.sum() != (0 * u.electron)
from .._tests import test_mixins
from ._systems_test import AbstractTestAbstractSystem


class AbstractTestAbstractSequentialSystem(
Expand Down
45 changes: 45 additions & 0 deletions optika/systems/_systems.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from __future__ import annotations
from typing import Any
import abc
import dataclasses
import named_arrays as na
import optika

__all__ = [
"AbstractSystem",
]


@dataclasses.dataclass(eq=False, repr=False)
class AbstractSystem(
optika.mixins.DxfWritable,
optika.mixins.Plottable,
optika.mixins.Printable,
optika.mixins.Transformable,
optika.mixins.Shaped,
):
"""
An interface describing an optical system.

Could potentially be sequential or non-sequential.
"""

@abc.abstractmethod
def image(
self,
scene: na.FunctionArray[na.SpectralPositionalVectorArray, na.AbstractScalar],
**kwargs: Any,
) -> na.SpectralPositionalVectorArray:
"""
Forward model of the optical system.
Maps the given spectral radiance of a scene to detector counts.

Parameters
----------
scene
The spectral radiance of the scene as a function of wavelength
and field position.
kwargs
Additional keyword arguments used by subclass implementations
of this method.
"""
53 changes: 53 additions & 0 deletions optika/systems/_systems_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import pytest
import numpy as np
import astropy.units as u
import named_arrays as na
import optika
from .._tests import test_mixins


class AbstractTestAbstractSystem(
test_mixins.AbstractTestPlottable,
test_mixins.AbstractTestPrintable,
test_mixins.AbstractTestTransformable,
test_mixins.AbstractTestShaped,
):

@pytest.mark.parametrize(
argnames="scene",
argvalues=[
na.FunctionArray(
inputs=na.SpectralPositionalVectorArray(
wavelength=na.linspace(
start=530 * u.nm,
stop=531 * u.nm,
axis="wavelength",
num=2,
),
position=na.Cartesian2dVectorLinearSpace(
start=-1,
stop=+1,
axis=na.Cartesian2dVectorArray("field_x", "field_y"),
num=11,
),
),
outputs=na.random.uniform(
low=0 * u.photon / u.cm**2 / u.arcsec**2 / u.s / u.nm,
high=100 * u.photon / u.cm**2 / u.arcsec**2 / u.s / u.nm,
shape_random=dict(field_x=10, field_y=10),
),
)
],
)
def test_image(
self,
a: optika.systems.AbstractSystem,
scene: na.FunctionArray[na.SpectralPositionalVectorArray, na.AbstractScalar],
):
result = a.image(scene)
assert isinstance(result, na.FunctionArray)
assert isinstance(result.inputs, na.SpectralPositionalVectorArray)
assert isinstance(result.outputs, na.AbstractScalar)
assert np.all(result.inputs.wavelength > 0 * u.nm)
assert na.unit_normalized(result.inputs.position).is_equivalent(u.mm)
assert result.outputs.sum() != (0 * u.electron)
Loading