Factories unified
This commit is contained in:
parent
8aab5f2f24
commit
f951f5c636
@ -34,7 +34,7 @@ The method ``propagate()`` is used to model the propagation of incoming radiatio
|
||||
Therefore, this method receives as parameter the incoming radiation as ``SpectralQty``-object and must return the manipulated radiation as ``SpectralQty``-object.
|
||||
|
||||
The optional second task consists of modifying the factory method ``create()`` of the class ``OpticalComponentFactory`` in order to properly initialize the new optical component from the configuration.
|
||||
This is only necessary if the new optical component needs additional parameters besides the attributes of the corresponding configuration tag of if the new component provides multiple constructors (see class ``Filter``).
|
||||
This is only necessary if the new optical component needs additional parameters besides the attributes of the corresponding configuration tag.
|
||||
|
||||
Adding Detector Components
|
||||
--------------------------
|
||||
@ -46,5 +46,4 @@ Thereby, the new class must implement the three methods ``calcSNR()``, ``calcExp
|
||||
All three methods obtain the incoming background and signal radiation as well as the obstruction factor as parameters apart from some specific parameters and must return the corresponding calculated value.
|
||||
Additionally, all three methods must be able to calculate multiple SNRs, exposure times or sensitivities at once if an array of specific parameters is provided.
|
||||
|
||||
Besides the implementation of the detector, the factory method ``create()`` of the class ``SensorFactory`` must be modified.
|
||||
In detail, a new if-branch must be added which assembles all necessary parameters for the constructor of the new component and calls the constructor.
|
||||
Besides the implementation of the detector, the factory method ``create()`` of the class ``SensorFactory`` can be modified.
|
||||
|
@ -4,7 +4,7 @@ from ..IRadiant import IRadiant
|
||||
from ...lib.logger import logger
|
||||
from ..Entry import Entry
|
||||
from astropy import units as u
|
||||
from typing import Union, Callable
|
||||
from typing import Union
|
||||
import numpy as np
|
||||
|
||||
|
||||
@ -21,10 +21,7 @@ class Filter(AHotOpticalComponent):
|
||||
L=dict(cwl=3600 * u.nm, bw=1200 * u.nm), M=dict(cwl=4800 * u.nm, bw=800 * u.nm),
|
||||
N=dict(cwl=10200 * u.nm, bw=2500 * u.nm))
|
||||
|
||||
@u.quantity_input(temp=[u.Kelvin, u.Celsius], obstructor_temp=[u.Kelvin, u.Celsius])
|
||||
def __init__(self, parent: IRadiant, transmittance: Union[SpectralQty, Callable[[u.Quantity], u.Quantity]],
|
||||
emissivity: Union[str, float] = 1, temp: u.Quantity = 0 * u.K, obstruction: float = 0,
|
||||
obstructor_temp: u.Quantity = 0 * u.K, obstructor_emissivity: float = 1):
|
||||
def __init__(self, **kwargs):
|
||||
"""
|
||||
Instantiate a new filter model
|
||||
|
||||
@ -32,8 +29,15 @@ class Filter(AHotOpticalComponent):
|
||||
----------
|
||||
parent : IRadiant
|
||||
The parent element of the optical component from which the electromagnetic radiation is received.
|
||||
transmittance : Union[SpectralQty, Callable]
|
||||
The spectral transmittance coefficients of the filter.
|
||||
transmittance : str
|
||||
Path to the file containing the spectral transmittance-coefficients of the filter element.
|
||||
The format of the file will be guessed by `astropy.io.ascii.read()`.
|
||||
band : str
|
||||
The spectral band of the filter. Can be one of [U, B, V, R, I, J, H, K].
|
||||
start : length-quantity
|
||||
Start wavelength of the pass-band
|
||||
end : length-quantity
|
||||
End wavelength of the pass-band
|
||||
emissivity : Union[str, float]
|
||||
The spectral emissivity coefficient for the optical surface.
|
||||
temp: Quantity in Kelvin / Celsius
|
||||
@ -48,14 +52,22 @@ class Filter(AHotOpticalComponent):
|
||||
obstructor_emissivity : float
|
||||
Emissivity of the obstructing component.
|
||||
"""
|
||||
super().__init__(parent, emissivity, temp, obstruction, obstructor_temp, obstructor_emissivity)
|
||||
self._transmittance = transmittance
|
||||
args = dict()
|
||||
if "band" in kwargs:
|
||||
args = self._fromBand(**kwargs)
|
||||
elif "transmittance" in kwargs:
|
||||
args = self._fromFile(**kwargs)
|
||||
elif "start" in kwargs and "end" in kwargs:
|
||||
args = self._fromRange(**kwargs)
|
||||
else:
|
||||
logger.error("Wrong parameters for filter.")
|
||||
self._transmittance = args.pop("transmittance")
|
||||
super().__init__(**args)
|
||||
|
||||
@classmethod
|
||||
# @u.quantity_input(temp=[u.Kelvin, u.Celsius], obstructor_temp=[u.Kelvin, u.Celsius])
|
||||
def fromBand(cls, parent: IRadiant, band: str, emissivity: Union[str, float] = 1, temp: u.Quantity = 0 * u.K,
|
||||
obstruction: float = 0, obstructor_temp: u.Quantity = 0 * u.K,
|
||||
obstructor_emissivity: float = 1) -> "Filter":
|
||||
def _fromBand(self, parent: IRadiant, band: str, emissivity: Union[str, float] = 1, temp: u.Quantity = 0 * u.K,
|
||||
obstruction: float = 0, obstructor_temp: u.Quantity = 0 * u.K,
|
||||
obstructor_emissivity: float = 1) -> dict:
|
||||
"""
|
||||
Instantiate a new filter model from a spectral band. The filter will be modelled as bandpass filter of
|
||||
infinite order and therefore similar to a hat-function.
|
||||
@ -82,20 +94,19 @@ class Filter(AHotOpticalComponent):
|
||||
|
||||
Returns
|
||||
-------
|
||||
filter : Filter
|
||||
The instantiated filter object.
|
||||
args : dict
|
||||
The arguments for the class instantiation.
|
||||
"""
|
||||
if band not in cls._band.keys():
|
||||
logger.error("Band has to be one of '[" + ", ".join(list(cls._band.keys())) + "]'")
|
||||
return cls.fromRange(parent, cls._band[band]["cwl"] - cls._band[band]["bw"] / 2,
|
||||
cls._band[band]["cwl"] + cls._band[band]["bw"] / 2, emissivity, temp, obstruction,
|
||||
obstructor_temp, obstructor_emissivity)
|
||||
if band not in self._band.keys():
|
||||
logger.error("Band has to be one of '[" + ", ".join(list(self._band.keys())) + "]'")
|
||||
return self._fromRange(parent, self._band[band]["cwl"] - self._band[band]["bw"] / 2,
|
||||
self._band[band]["cwl"] + self._band[band]["bw"] / 2, emissivity, temp, obstruction,
|
||||
obstructor_temp, obstructor_emissivity)
|
||||
|
||||
@classmethod
|
||||
# @u.quantity_input(temp=[u.Kelvin, u.Celsius], obstructor_temp=[u.Kelvin, u.Celsius])
|
||||
def fromFile(cls, parent: IRadiant, transmittance: str, emissivity: Union[str, float] = 1,
|
||||
temp: u.Quantity = 0 * u.K, obstruction: float = 0, obstructor_temp: u.Quantity = 0 * u.K,
|
||||
obstructor_emissivity: float = 1) -> "Filter":
|
||||
def _fromFile(self, parent: IRadiant, transmittance: str, emissivity: Union[str, float] = 1,
|
||||
temp: u.Quantity = 0 * u.K, obstruction: float = 0, obstructor_temp: u.Quantity = 0 * u.K,
|
||||
obstructor_emissivity: float = 1) -> dict:
|
||||
"""
|
||||
Instantiate a new filter model from a file containing the spectral transmittance coefficients.
|
||||
|
||||
@ -122,17 +133,17 @@ class Filter(AHotOpticalComponent):
|
||||
|
||||
Returns
|
||||
-------
|
||||
filter : Filter
|
||||
The instantiated filter object.
|
||||
args : dict
|
||||
The arguments for the class instantiation.
|
||||
"""
|
||||
return cls(parent, SpectralQty.fromFile(transmittance, u.nm, u.dimensionless_unscaled), emissivity, temp,
|
||||
obstruction, obstructor_temp, obstructor_emissivity)
|
||||
return {"parent": parent, "transmittance": SpectralQty.fromFile(transmittance, u.nm, u.dimensionless_unscaled),
|
||||
"emissivity": emissivity, "temp": temp, "obstruction": obstruction, "obstructor_temp": obstructor_temp,
|
||||
"obstructor_emissivity": obstructor_emissivity}
|
||||
|
||||
@classmethod
|
||||
# @u.quantity_input(start="length", end="length", temp=[u.Kelvin, u.Celsius], obstructor_temp=[u.Kelvin, u.Celsius])
|
||||
def fromRange(cls, parent: IRadiant, start: u.Quantity, end: u.Quantity, emissivity: Union[str, float] = 1,
|
||||
temp: u.Quantity = 0 * u.K, obstruction: float = 0, obstructor_temp: u.Quantity = 0 * u.K,
|
||||
obstructor_emissivity: float = 1) -> "Filter":
|
||||
def _fromRange(self, parent: IRadiant, start: u.Quantity, end: u.Quantity, emissivity: Union[str, float] = 1,
|
||||
temp: u.Quantity = 0 * u.K, obstruction: float = 0, obstructor_temp: u.Quantity = 0 * u.K,
|
||||
obstructor_emissivity: float = 1) -> dict:
|
||||
"""
|
||||
Instantiate a new filter model from a spectral range. The filter will be modelled as bandpass filter of
|
||||
infinite order and therefore similar to a hat-function.
|
||||
@ -161,11 +172,12 @@ class Filter(AHotOpticalComponent):
|
||||
|
||||
Returns
|
||||
-------
|
||||
filter : Filter
|
||||
The instantiated filter object.
|
||||
args : dict
|
||||
The arguments for the class instantiation.
|
||||
"""
|
||||
return cls(parent, cls.__filter_factory(start, end), emissivity, temp,
|
||||
obstruction, obstructor_temp, obstructor_emissivity)
|
||||
return {"parent": parent, "transmittance": self.__filter_factory(start, end),
|
||||
"emissivity": emissivity, "temp": temp, "obstruction": obstruction, "obstructor_temp": obstructor_temp,
|
||||
"obstructor_emissivity": obstructor_emissivity}
|
||||
|
||||
def _propagate(self, sqty: SpectralQty) -> SpectralQty:
|
||||
"""
|
||||
@ -201,8 +213,8 @@ class Filter(AHotOpticalComponent):
|
||||
lambda : Callable[[u.Quantity], u.Quantity]
|
||||
The filter function
|
||||
"""
|
||||
return lambda wl: np.logical_and(np.greater_equal(wl, start), np.greater_equal(end, wl)).astype(int) *\
|
||||
u.dimensionless_unscaled
|
||||
return lambda wl: np.logical_and(np.greater_equal(wl, start), np.greater_equal(end, wl)).astype(
|
||||
int) * u.dimensionless_unscaled
|
||||
|
||||
@staticmethod
|
||||
def check_config(conf: Entry) -> Union[None, str]:
|
||||
|
@ -37,26 +37,16 @@ class OpticalComponentFactory(ARadiantFactory):
|
||||
obj : AOpticalComponent
|
||||
The created optical component
|
||||
"""
|
||||
opts = self.collectOptions(options)
|
||||
if parent is not None:
|
||||
# New component is of type Optical Component
|
||||
opts = self.collectOptions(options)
|
||||
opts["parent"] = parent
|
||||
class_ = getattr(oc, options.type)
|
||||
if options.type in ["Atmosphere", "StrayLight", "CosmicBackground", "Mirror", "Lens", "BeamSplitter"]:
|
||||
if hasattr(oc, options.type):
|
||||
class_ = getattr(oc, options.type)
|
||||
return class_(**opts)
|
||||
elif options.type == "Filter":
|
||||
if hasattr(options, "band"):
|
||||
return oc.Filter.fromBand(**opts)
|
||||
elif hasattr(options, "transmittance"):
|
||||
return oc.Filter.fromFile(**opts)
|
||||
elif hasattr(options, "start") and hasattr(options, "end"):
|
||||
return oc.Filter.fromRange(**opts)
|
||||
else:
|
||||
logger.error("Wrong parameters for filter.")
|
||||
else:
|
||||
logger.error("Unknown optical component type: '" + options.type + "'")
|
||||
else:
|
||||
logger.error("No parent given for optical component.")
|
||||
logger.error("Parent object is required for optical component.")
|
||||
|
||||
def fromConfigBatch(self, conf: Entry, parent: IRadiant) -> AOpticalComponent:
|
||||
"""
|
||||
|
@ -2,8 +2,7 @@ from ..AFactory import AFactory
|
||||
from ..IRadiant import IRadiant
|
||||
from ..Entry import Entry
|
||||
from .ASensor import ASensor
|
||||
from .Imager import Imager
|
||||
from .Heterodyne import Heterodyne
|
||||
from ...classes import sensor as sensor
|
||||
from ...lib.logger import logger
|
||||
|
||||
|
||||
@ -38,29 +37,13 @@ class SensorFactory(AFactory):
|
||||
obj : ASensor
|
||||
The created sensor object
|
||||
"""
|
||||
opts = self.collectOptions(options)
|
||||
|
||||
if options.type == "Imager":
|
||||
if parent is not None:
|
||||
opts = self.collectOptions(options)
|
||||
args = dict(parent=parent, **opts, common_conf=self._common_conf)
|
||||
if hasattr(options, "center_offset"):
|
||||
# noinspection PyCallingNonCallable
|
||||
args["center_offset"] = options.center_offset()
|
||||
if hasattr(options, "photometric_aperture"):
|
||||
if hasattr(options.photometric_aperture, "shape") and isinstance(
|
||||
options.photometric_aperture.shape, Entry):
|
||||
args["shape"] = options.photometric_aperture.shape()
|
||||
if hasattr(options.photometric_aperture, "contained_energy") and isinstance(
|
||||
options.photometric_aperture.contained_energy, Entry):
|
||||
args["contained_energy"] = options.photometric_aperture.contained_energy()
|
||||
if hasattr(options.photometric_aperture, "aperture_size") and isinstance(
|
||||
options.photometric_aperture.aperture_size, Entry):
|
||||
args["aperture_size"] = options.photometric_aperture.aperture_size()
|
||||
return Imager(**args)
|
||||
elif options.type == "Heterodyne":
|
||||
args = dict(parent=parent, **opts, common_conf=self._common_conf)
|
||||
if hasattr(options, "n_on"):
|
||||
# noinspection PyCallingNonCallable
|
||||
args["n_on"] = options.n_on()
|
||||
return Heterodyne(**args)
|
||||
if hasattr(sensor, options.type):
|
||||
class_ = getattr(sensor, options.type)
|
||||
return class_(**args)
|
||||
else:
|
||||
logger.error("Unknown sensor type: '" + options.type + "'")
|
||||
else:
|
||||
logger.error("Wrong sensor type: " + options.type)
|
||||
logger.error("Parent object is required for sensor.")
|
||||
|
@ -1,4 +1,3 @@
|
||||
import astropy.units as u
|
||||
from ..ARadiantFactory import ARadiantFactory
|
||||
from ..Entry import Entry
|
||||
from ..IRadiant import IRadiant
|
||||
@ -38,18 +37,13 @@ class TargetFactory(ARadiantFactory):
|
||||
obj : ATarget
|
||||
The created target object
|
||||
"""
|
||||
opts = self.collectOptions(options)
|
||||
|
||||
if parent is None:
|
||||
# New component is of type target
|
||||
opts = self.collectOptions(options)
|
||||
opts["wl_bins"] = self._common_conf.wl_bins.val
|
||||
if options.type == "BlackBodyTarget":
|
||||
# Black Body Target
|
||||
if "mag" in opts and type(opts["mag"]) == str:
|
||||
opts["mag"] = float(opts["mag"]) * u.mag
|
||||
return tg.BlackBodyTarget(**opts)
|
||||
elif options.type == "FileTarget":
|
||||
# File Target
|
||||
return getattr(tg, options.type)(**opts)
|
||||
if hasattr(tg, options.type):
|
||||
class_ = getattr(tg, options.type)
|
||||
return class_(**opts)
|
||||
else:
|
||||
logger.error("Unknown target type: '" + options.type + "'")
|
||||
else:
|
||||
|
@ -8,14 +8,14 @@ class TestFilter(TestCase):
|
||||
def test_fromBand(self):
|
||||
wl = np.array([400, 500, 501, 545, 589, 590, 600]) << u.nm
|
||||
target = BlackBodyTarget(wl, temp=5778 * u.K, mag=10 * u.mag, band="U")
|
||||
filt = Filter.fromBand(target, "V")
|
||||
filt = Filter(parent=target, band="V")
|
||||
self.assertEqual(filt.calcSignal()[0], SpectralQty(wl, np.array([0.0, 0.0, 0.0, 5.52730709e-15, 5.29671115e-15,
|
||||
5.29030718e-15, 0.0]) << u.W /
|
||||
(u.m ** 2 * u.nm)))
|
||||
5.29030718e-15, 0.0]) << u.W /
|
||||
(u.m ** 2 * u.nm)))
|
||||
|
||||
def test_fromFile(self):
|
||||
target = FileTarget("tests/data/target/target_demo_1.csv", np.arange(200, 210, 1) << u.nm)
|
||||
filt = Filter.fromFile(target, "tests/data/filter/filter_transmittance.csv")
|
||||
filt = Filter(parent=target, transmittance="tests/data/filter/filter_transmittance.csv")
|
||||
self.assertEqual(filt.calcSignal()[0],
|
||||
SpectralQty(np.arange(200, 210, 1) << u.nm, np.array([1.10e-15, 1.20e-15, 1.30e-15, 1.40e-15,
|
||||
1.35e-15, 1.44e-15, 1.53e-15, 1.44e-15,
|
||||
|
@ -25,7 +25,8 @@ class TestRadiantFactory(TestCase):
|
||||
"tests/data/mirror/emissivity.csv", 70 * u.K)
|
||||
parent_2 = oc.Mirror(parent_2, "tests/data/mirror/reflectance.csv",
|
||||
"tests/data/mirror/emissivity.csv", 70 * u.K)
|
||||
parent_2 = oc.Filter.fromRange(parent_2, 400 * u.nm, 480 * u.nm, "tests/data/filter/emissivity.csv", 70 * u.K)
|
||||
parent_2 = oc.Filter(parent=parent_2, start=400 * u.nm, end=480 * u.nm,
|
||||
emissivity="tests/data/filter/emissivity.csv", temp=70 * u.K)
|
||||
parent_2 = oc.Lens(parent_2, "tests/data/lens/transmittance.csv", "tests/data/lens/emissivity.csv", 70 * u.K)
|
||||
|
||||
self.assertEqual(parent.calcSignal()[0], parent_2.calcSignal()[0])
|
||||
|
Loading…
x
Reference in New Issue
Block a user