PyDIET 0.9.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pydiet/__init__.py +12 -0
- pydiet/api_client/__init__.py +6 -0
- pydiet/api_client/client.py +57 -0
- pydiet/cmd/__init__.py +9 -0
- pydiet/cmd/start.py +107 -0
- pydiet/data/data_config.toml +242 -0
- pydiet/data/description.txt +1 -0
- pydiet/data/instruments/description.txt +1 -0
- pydiet/data/instruments/megacam/default +0 -0
- pydiet/data/instruments/megacam/description.txt +1 -0
- pydiet/data/instruments/megacam/detector/description.txt +1 -0
- pydiet/data/instruments/megacam/detector/qe/MegaCam_QE.average.fits +0 -0
- pydiet/data/instruments/megacam/detector/qe/description.txt +2 -0
- pydiet/data/instruments/megacam/filters/CaHK.MP9303.fits +0 -0
- pydiet/data/instruments/megacam/filters/Ha.MP9603.fits +0 -0
- pydiet/data/instruments/megacam/filters/HaOFF.MP9604.fits +0 -0
- pydiet/data/instruments/megacam/filters/M4112.MP9403.fits +0 -0
- pydiet/data/instruments/megacam/filters/M4376.MP9404.fits +0 -0
- pydiet/data/instruments/megacam/filters/OIII.MP9501.fits +0 -0
- pydiet/data/instruments/megacam/filters/OIIIOFF.MP9502.fits +0 -0
- pydiet/data/instruments/megacam/filters/description.txt +1 -0
- pydiet/data/instruments/megacam/filters/g.MP9402.fits +0 -0
- pydiet/data/instruments/megacam/filters/gri.MP9605.fits +0 -0
- pydiet/data/instruments/megacam/filters/i.MP9703.fits +0 -0
- pydiet/data/instruments/megacam/filters/r.MP9602.fits +0 -0
- pydiet/data/instruments/megacam/filters/u.MP9302.fits +0 -0
- pydiet/data/instruments/megacam/filters/z.MP9901.fits +0 -0
- pydiet/data/instruments/megacam/optics/description.txt +2 -0
- pydiet/data/instruments/megacam/optics/transmission/MegaPrime_transmission.fits +0 -0
- pydiet/data/instruments/megacam/optics/transmission/description.txt +2 -0
- pydiet/data/instruments/wircam/description.txt +1 -0
- pydiet/data/instruments/wircam/detector/description.txt +1 -0
- pydiet/data/instruments/wircam/detector/qe/WIRCam_QE.average.fits +0 -0
- pydiet/data/instruments/wircam/detector/qe/description.txt +2 -0
- pydiet/data/instruments/wircam/filters/BrG.WC8305.fits +0 -0
- pydiet/data/instruments/wircam/filters/CH4Off.WC8204.fits +0 -0
- pydiet/data/instruments/wircam/filters/CH4On.WC8203.fits +0 -0
- pydiet/data/instruments/wircam/filters/CO.WC8306.fits +0 -0
- pydiet/data/instruments/wircam/filters/H.WC8201.fits +0 -0
- pydiet/data/instruments/wircam/filters/H.WC8202.fits +0 -0
- pydiet/data/instruments/wircam/filters/H2.WC8304.fits +0 -0
- pydiet/data/instruments/wircam/filters/J.WC8101.fits +0 -0
- pydiet/data/instruments/wircam/filters/J.WC8103.fits +0 -0
- pydiet/data/instruments/wircam/filters/Kcont.WC8303.fits +0 -0
- pydiet/data/instruments/wircam/filters/Ks.WC8301.fits +0 -0
- pydiet/data/instruments/wircam/filters/Ks.WC8302.fits +0 -0
- pydiet/data/instruments/wircam/filters/LowOH1.WC8104.fits +0 -0
- pydiet/data/instruments/wircam/filters/LowOH2.WC8102.fits +0 -0
- pydiet/data/instruments/wircam/filters/W.WC8105.fits +0 -0
- pydiet/data/instruments/wircam/filters/Y.WC8002.fits +0 -0
- pydiet/data/instruments/wircam/filters/description.txt +1 -0
- pydiet/data/instruments/wircam/optics/description.txt +2 -0
- pydiet/data/instruments/wircam/optics/transmission/WIRCam_transmission.fits +0 -0
- pydiet/data/instruments/wircam/optics/transmission/description.txt +2 -0
- pydiet/data/sites/description.txt +1 -0
- pydiet/data/sites/mko/default +0 -0
- pydiet/data/sites/mko/description.txt +2 -0
- pydiet/data/sites/mko/emission/MKO_emission.bright.AM1.0.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.bright.AM1.1.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.bright.AM1.2.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.bright.AM1.3.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.bright.AM1.4.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.bright.AM1.5.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.bright.AM1.6.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.bright.AM1.7.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.bright.AM1.8.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.bright.AM1.9.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.bright.AM2.0.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.bright.AM2.5.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.bright.AM3.0.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.dark.AM1.0.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.dark.AM1.1.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.dark.AM1.2.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.dark.AM1.3.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.dark.AM1.4.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.dark.AM1.5.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.dark.AM1.6.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.dark.AM1.7.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.dark.AM1.8.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.dark.AM1.9.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.dark.AM2.0.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.dark.AM2.5.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.dark.AM3.0.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.grey.AM1.0.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.grey.AM1.1.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.grey.AM1.2.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.grey.AM1.3.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.grey.AM1.4.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.grey.AM1.5.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.grey.AM1.6.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.grey.AM1.7.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.grey.AM1.8.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.grey.AM1.9.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.grey.AM2.0.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.grey.AM2.5.fits +0 -0
- pydiet/data/sites/mko/emission/MKO_emission.grey.AM3.0.fits +0 -0
- pydiet/data/sites/mko/emission/description.txt +5 -0
- pydiet/data/sites/mko/transmission/MKO_transmission.AM1.0.fits +0 -0
- pydiet/data/sites/mko/transmission/MKO_transmission.AM1.1.fits +0 -0
- pydiet/data/sites/mko/transmission/MKO_transmission.AM1.2.fits +0 -0
- pydiet/data/sites/mko/transmission/MKO_transmission.AM1.3.fits +0 -0
- pydiet/data/sites/mko/transmission/MKO_transmission.AM1.4.fits +0 -0
- pydiet/data/sites/mko/transmission/MKO_transmission.AM1.5.fits +0 -0
- pydiet/data/sites/mko/transmission/MKO_transmission.AM1.6.fits +0 -0
- pydiet/data/sites/mko/transmission/MKO_transmission.AM1.7.fits +0 -0
- pydiet/data/sites/mko/transmission/MKO_transmission.AM1.8.fits +0 -0
- pydiet/data/sites/mko/transmission/MKO_transmission.AM1.9.fits +0 -0
- pydiet/data/sites/mko/transmission/MKO_transmission.AM2.0.fits +0 -0
- pydiet/data/sites/mko/transmission/MKO_transmission.AM2.5.fits +0 -0
- pydiet/data/sites/mko/transmission/MKO_transmission.AM3.0.fits +0 -0
- pydiet/data/sites/mko/transmission/MKO_transmission.AM3.5.fits +0 -0
- pydiet/data/sites/mko/transmission/MKO_transmission.AM4.0.fits +0 -0
- pydiet/data/sites/mko/transmission/MKO_transmission.AM4.5.fits +0 -0
- pydiet/data/sites/mko/transmission/MKO_transmission.AM5.0.fits +0 -0
- pydiet/data/sites/mko/transmission/description.txt +5 -0
- pydiet/data/telescopes/cfht/default +0 -0
- pydiet/data/telescopes/cfht/description.txt +1 -0
- pydiet/data/telescopes/cfht/emission/description.txt +2 -0
- pydiet/data/telescopes/cfht/transmission/CFHT_M1_transmission.fits +0 -0
- pydiet/data/telescopes/cfht/transmission/description.txt +1 -0
- pydiet/data/telescopes/description.txt +1 -0
- pydiet/package.py +55 -0
- pydiet/py.typed +0 -0
- pydiet/server/__init__.py +9 -0
- pydiet/server/app.py +369 -0
- pydiet/server/config/__init__.py +51 -0
- pydiet/server/config/config.py +330 -0
- pydiet/server/config/fields.py +49 -0
- pydiet/server/config/settings.py +166 -0
- pydiet/server/data.py +31 -0
- pydiet/server/datafiles.py +367 -0
- pydiet/server/image.py +342 -0
- pydiet/server/models/__init__.py +34 -0
- pydiet/server/models/dataconfig.py +195 -0
- pydiet/server/models/default.py +9 -0
- pydiet/server/models/exceptions.py +9 -0
- pydiet/server/models/instrument.py +314 -0
- pydiet/server/models/query.py +172 -0
- pydiet/server/models/response.py +97 -0
- pydiet/server/models/types.py +35 -0
- pydiet/server/photsys.py +71 -0
- pydiet/server/response.py +237 -0
- pydiet/server/types/__init__.py +8 -0
- pydiet/server/types/quantity.py +532 -0
- pydiet/server/types/string.py +318 -0
- pydiet/templates/common/base.html +80 -0
- pydiet/templates/common/plot_filter.html +17 -0
- pydiet/templates/common/privacy.html +132 -0
- pydiet/templates/common/settings.html +23 -0
- pydiet/templates/common/terms.html +101 -0
- pydiet/templates/megacam/etc_form.html +319 -0
- pydiet/templates/megacam/etc_results.html +190 -0
- pydiet/templates/wircam/etc_form.html +319 -0
- pydiet/templates/wircam/etc_results.html +190 -0
- pydiet/web_client/css/style.css +221 -0
- pydiet/web_client/dist/pydiet.js +31 -0
- pydiet/web_client/images/logo.svg +6 -0
- pydiet/web_client/images/megacam/background.jpg +0 -0
- pydiet/web_client/images/megacam/logo.png +0 -0
- pydiet/web_client/images/wircam/background.jpg +0 -0
- pydiet/web_client/images/wircam/logo.png +0 -0
- pydiet/web_client/js/dom.js +51 -0
- pydiet/web_client/js/etc.js +63 -0
- pydiet/web_client/js/fetch.js +49 -0
- pydiet/web_client/js/instrument.js +62 -0
- pydiet/web_client/js/main.js +15 -0
- pydiet/web_client/js/plot.js +88 -0
- pydiet/web_client/js/settings.js +57 -0
- pydiet/web_client/js/theme.js +43 -0
- pydiet/web_client/js/url.js +12 -0
- pydiet/web_client/jsdoc.json +20 -0
- pydiet/web_client/package.json +83 -0
- pydiet-0.9.3.dist-info/METADATA +118 -0
- pydiet-0.9.3.dist-info/RECORD +177 -0
- pydiet-0.9.3.dist-info/WHEEL +4 -0
- pydiet-0.9.3.dist-info/entry_points.txt +5 -0
- pydiet-0.9.3.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Data models
|
|
3
|
+
"""
|
|
4
|
+
# Copyright CFHT/CNRS/CEA/UParisSaclay
|
|
5
|
+
# Licensed under the MIT licence
|
|
6
|
+
|
|
7
|
+
from .dataconfig import (
|
|
8
|
+
DataConfigModel,
|
|
9
|
+
DetectorConfigModel,
|
|
10
|
+
EmissionConfigModel,
|
|
11
|
+
FileConfigModel,
|
|
12
|
+
FiltersConfigModel,
|
|
13
|
+
InstrumentConfigModel,
|
|
14
|
+
OpticsConfigModel,
|
|
15
|
+
SiteConfigModel,
|
|
16
|
+
TelescopeConfigModel,
|
|
17
|
+
TransmissionConfigModel
|
|
18
|
+
)
|
|
19
|
+
from .exceptions import ETCValidationError
|
|
20
|
+
from .query import ETCQueryModel
|
|
21
|
+
from .response import ETCResponseModel
|
|
22
|
+
from .instrument import (
|
|
23
|
+
DetectorModel,
|
|
24
|
+
FiltersModel,
|
|
25
|
+
InstrumentModel,
|
|
26
|
+
OpticsModel,
|
|
27
|
+
SBSEDModel,
|
|
28
|
+
SEDModel,
|
|
29
|
+
SiteModel,
|
|
30
|
+
TelescopeModel,
|
|
31
|
+
TransmissionModel,
|
|
32
|
+
spectral_to_arrays
|
|
33
|
+
)
|
|
34
|
+
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Data models
|
|
3
|
+
"""
|
|
4
|
+
# Copyright CFHT/CNRS/CEA/UParisSaclay
|
|
5
|
+
# Licensed under the MIT licence
|
|
6
|
+
|
|
7
|
+
from astropy import units as u #type: ignore[import-untyped]
|
|
8
|
+
from pydantic import BaseModel, DirectoryPath
|
|
9
|
+
|
|
10
|
+
from ..types import AnnotatedQuantity
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class FileConfigModel(BaseModel):
|
|
14
|
+
'''
|
|
15
|
+
Pydantic model for transmission curve configuration.
|
|
16
|
+
'''
|
|
17
|
+
default: bool = False
|
|
18
|
+
id: str = ''
|
|
19
|
+
name: str = ""
|
|
20
|
+
description: str = ""
|
|
21
|
+
vars: dict[str, float|str] = {}
|
|
22
|
+
file: str
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class EmissionConfigModel(BaseModel):
|
|
27
|
+
'''
|
|
28
|
+
Pydantic model for emission curve set configuration.
|
|
29
|
+
'''
|
|
30
|
+
path: str = ""
|
|
31
|
+
temperatures: list[AnnotatedQuantity( #type: ignore[valid-type]
|
|
32
|
+
unit = "K",
|
|
33
|
+
gt = 0. * u.K,
|
|
34
|
+
decimals = 2,
|
|
35
|
+
description = "Device temperature."
|
|
36
|
+
)] = [283 * u.K]
|
|
37
|
+
areas: list[AnnotatedQuantity( #type: ignore[valid-type]
|
|
38
|
+
unit = "m2",
|
|
39
|
+
ge = 0. * u.m**2,
|
|
40
|
+
decimals = 3,
|
|
41
|
+
description = "Emissive area."
|
|
42
|
+
)] = [0. * u.m**2]
|
|
43
|
+
files: list[FileConfigModel] = []
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class TransmissionConfigModel(BaseModel):
|
|
48
|
+
'''
|
|
49
|
+
Pydantic model for transmission curve set configuration.
|
|
50
|
+
'''
|
|
51
|
+
path: str = ""
|
|
52
|
+
files: list[FileConfigModel] = []
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class SiteConfigModel(BaseModel):
|
|
57
|
+
'''
|
|
58
|
+
Pydantic model for the observing site data configuration model.
|
|
59
|
+
'''
|
|
60
|
+
default: bool = False
|
|
61
|
+
id: str
|
|
62
|
+
name: str
|
|
63
|
+
description: str
|
|
64
|
+
path: str
|
|
65
|
+
altitude: AnnotatedQuantity( #type: ignore[valid-type]
|
|
66
|
+
unit = "m",
|
|
67
|
+
ge = -1000. * u.m,
|
|
68
|
+
decimals = 3,
|
|
69
|
+
description = "Altitude of the observation site."
|
|
70
|
+
)
|
|
71
|
+
transmission: TransmissionConfigModel
|
|
72
|
+
emission: EmissionConfigModel
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class TelescopeConfigModel(BaseModel):
|
|
77
|
+
'''
|
|
78
|
+
Pydantic model for the telescope data configuration model.
|
|
79
|
+
'''
|
|
80
|
+
default: bool = False
|
|
81
|
+
id: str
|
|
82
|
+
name: str
|
|
83
|
+
description: str
|
|
84
|
+
path: str
|
|
85
|
+
collecting_area: AnnotatedQuantity( #type: ignore[valid-type]
|
|
86
|
+
unit = "m2",
|
|
87
|
+
gt = 0. * u.m**2,
|
|
88
|
+
decimals = 3,
|
|
89
|
+
description = "Full collecting area, including possible mirror hole and spider branches."
|
|
90
|
+
)
|
|
91
|
+
obstruction_area: AnnotatedQuantity( #type: ignore[valid-type]
|
|
92
|
+
unit = "m2",
|
|
93
|
+
gt = 0. * u.m**2,
|
|
94
|
+
decimals = 3,
|
|
95
|
+
description = "Default obstruction area (only used if not specified for the instrument)."
|
|
96
|
+
)
|
|
97
|
+
transmission: TransmissionConfigModel
|
|
98
|
+
emission: EmissionConfigModel
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class DetectorConfigModel(BaseModel):
|
|
103
|
+
'''
|
|
104
|
+
Pydantic model for the detector data configuration model.
|
|
105
|
+
'''
|
|
106
|
+
path: str
|
|
107
|
+
gain: AnnotatedQuantity( #type: ignore[valid-type]
|
|
108
|
+
unit = "electron/adu",
|
|
109
|
+
gt = 0. * u.electron / u.adu,
|
|
110
|
+
decimals = 3,
|
|
111
|
+
description = "Average gain (conversion factor) in electrons per ADU."
|
|
112
|
+
)
|
|
113
|
+
ron: AnnotatedQuantity( #type: ignore[valid-type]
|
|
114
|
+
unit = "electron",
|
|
115
|
+
ge = 0. * u.electron,
|
|
116
|
+
decimals = 3,
|
|
117
|
+
description = "Total readout noise in electrons."
|
|
118
|
+
)
|
|
119
|
+
scale: AnnotatedQuantity( #type: ignore[valid-type]
|
|
120
|
+
unit = "arcsec/pix",
|
|
121
|
+
gt = [0., 0.] * u.arcsec / u.pix,
|
|
122
|
+
min_shape = (2),
|
|
123
|
+
max_shape = (2),
|
|
124
|
+
decimals = 4,
|
|
125
|
+
description = "Angular pixel scale along each axis."
|
|
126
|
+
)
|
|
127
|
+
transmission: TransmissionConfigModel
|
|
128
|
+
emission: EmissionConfigModel
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class OpticsConfigModel(BaseModel):
|
|
133
|
+
'''
|
|
134
|
+
Pydantic model for the intrument optics data configuration model.
|
|
135
|
+
'''
|
|
136
|
+
path: str
|
|
137
|
+
transmission: TransmissionConfigModel
|
|
138
|
+
emission: EmissionConfigModel
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class FiltersConfigModel(OpticsConfigModel):
|
|
143
|
+
'''
|
|
144
|
+
Pydantic model for the intrument filters data configuration model.
|
|
145
|
+
'''
|
|
146
|
+
pass
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class InstrumentConfigModel(BaseModel):
|
|
151
|
+
'''
|
|
152
|
+
Pydantic model for the instrument data configuration model.
|
|
153
|
+
'''
|
|
154
|
+
default: bool = False
|
|
155
|
+
id: str
|
|
156
|
+
name: str
|
|
157
|
+
description: str
|
|
158
|
+
path: str
|
|
159
|
+
wavelength_range: AnnotatedQuantity( #type: ignore[valid-type]
|
|
160
|
+
unit = "nm",
|
|
161
|
+
gt = [0., 0.] *u. nm,
|
|
162
|
+
min_shape = (2),
|
|
163
|
+
max_shape = (2),
|
|
164
|
+
decimals = 4,
|
|
165
|
+
description = "Instrument minimum and maximum wavelengths."
|
|
166
|
+
)
|
|
167
|
+
obstruction_area: AnnotatedQuantity( #type: ignore[valid-type]
|
|
168
|
+
unit = "m2",
|
|
169
|
+
gt = 0. * u.m**2,
|
|
170
|
+
decimals = 3,
|
|
171
|
+
description = "Obstruction area for this particular instrument."
|
|
172
|
+
)
|
|
173
|
+
overhead: AnnotatedQuantity( #type: ignore[valid-type]
|
|
174
|
+
unit = "s",
|
|
175
|
+
ge = 0. * u.second,
|
|
176
|
+
decimals = 3,
|
|
177
|
+
description = "Total instrument time overhead between exposures."
|
|
178
|
+
)
|
|
179
|
+
site_id: str
|
|
180
|
+
telescope_id: str
|
|
181
|
+
optics: OpticsConfigModel
|
|
182
|
+
filters: FiltersConfigModel
|
|
183
|
+
detector: DetectorConfigModel
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class DataConfigModel(BaseModel):
|
|
188
|
+
'''
|
|
189
|
+
Pydantic model for the data configuration
|
|
190
|
+
'''
|
|
191
|
+
path: DirectoryPath
|
|
192
|
+
sites: list['SiteConfigModel']
|
|
193
|
+
telescopes: list['TelescopeConfigModel']
|
|
194
|
+
instruments: list['InstrumentConfigModel']
|
|
195
|
+
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Data models
|
|
3
|
+
"""
|
|
4
|
+
# Copyright CFHT/CNRS/CEA/UParisSaclay
|
|
5
|
+
# Licensed under the MIT licence
|
|
6
|
+
|
|
7
|
+
from typing import Annotated, Optional, Tuple
|
|
8
|
+
|
|
9
|
+
from astropy import units as u #type: ignore[import-untyped]
|
|
10
|
+
import numpy as np
|
|
11
|
+
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
12
|
+
from synphot import ( #type: ignore[import-untyped]
|
|
13
|
+
BlackBody1D,
|
|
14
|
+
ConstFlux1D,
|
|
15
|
+
Observation,
|
|
16
|
+
SourceSpectrum,
|
|
17
|
+
SpectralElement
|
|
18
|
+
)
|
|
19
|
+
from synphot.spectrum import BaseSpectrum #type: ignore[import-untyped]
|
|
20
|
+
|
|
21
|
+
from ... import package
|
|
22
|
+
from ..types import AnnotatedQuantity
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def spectral_to_arrays(spectral: BaseSpectrum) -> Tuple[np.ndarray, np.ndarray]:
|
|
26
|
+
w = spectral.waveset
|
|
27
|
+
x = spectral(w)
|
|
28
|
+
|
|
29
|
+
# Trim extra 0 values at beginning and at the end
|
|
30
|
+
idx = np.where(x.value != 0.)[0]
|
|
31
|
+
if len(idx):
|
|
32
|
+
start = idx[0] - 1 if idx[0] > 0 else 0
|
|
33
|
+
end = idx[-1] + 2 if idx[-1] < w.size - 1 else w.size
|
|
34
|
+
w = w[start:end]
|
|
35
|
+
x = x[start:end]
|
|
36
|
+
|
|
37
|
+
return w, x
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class DetectorModel(BaseModel):
|
|
42
|
+
'''
|
|
43
|
+
Pydantic model for an instrument detector (e.g., CMOS or CCD).
|
|
44
|
+
'''
|
|
45
|
+
gain: AnnotatedQuantity( #type: ignore[valid-type]
|
|
46
|
+
unit = "electron / adu",
|
|
47
|
+
gt = 0. * u.electron / u.adu,
|
|
48
|
+
decimals = 4,
|
|
49
|
+
description = "Detector conversion factor."
|
|
50
|
+
)
|
|
51
|
+
ron: AnnotatedQuantity( #type: ignore[valid-type]
|
|
52
|
+
unit = "electron",
|
|
53
|
+
ge = 0. * u.electron,
|
|
54
|
+
decimals = 4,
|
|
55
|
+
description = "Read-out noise RMS amplitude."
|
|
56
|
+
)
|
|
57
|
+
scale: AnnotatedQuantity( #type: ignore[valid-type]
|
|
58
|
+
unit = "arcsec/pix",
|
|
59
|
+
gt = [0., 0.] * u.arcsec / u.pix,
|
|
60
|
+
min_shape = (2),
|
|
61
|
+
max_shape = (2),
|
|
62
|
+
decimals = 4,
|
|
63
|
+
description = "Angular pixel scale along each axis."
|
|
64
|
+
)
|
|
65
|
+
transmissions: dict[str, 'TransmissionModel']
|
|
66
|
+
emissions: dict[str, 'SBSEDModel']
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class InstrumentModel(BaseModel):
|
|
71
|
+
'''
|
|
72
|
+
Pydantic model for a PyDIET instrument.
|
|
73
|
+
'''
|
|
74
|
+
id: str
|
|
75
|
+
name: str
|
|
76
|
+
description: str
|
|
77
|
+
wavelength_range: AnnotatedQuantity( #type: ignore[valid-type]
|
|
78
|
+
unit = "nm",
|
|
79
|
+
gt = [0., 0.] * u.nm,
|
|
80
|
+
min_shape = (2),
|
|
81
|
+
max_shape = (2),
|
|
82
|
+
decimals = 4,
|
|
83
|
+
description = "Instrument minimum and maximum wavelengths."
|
|
84
|
+
)
|
|
85
|
+
obstruction_area: AnnotatedQuantity( #type: ignore[valid-type]
|
|
86
|
+
unit = "m2",
|
|
87
|
+
gt = 0. * u.m**2,
|
|
88
|
+
decimals = 3,
|
|
89
|
+
description = "Default obstruction area (only used if not specified for the instrument)."
|
|
90
|
+
)
|
|
91
|
+
overhead: AnnotatedQuantity( #type: ignore[valid-type]
|
|
92
|
+
unit = "s",
|
|
93
|
+
ge = 0. * u.second,
|
|
94
|
+
decimals = 3,
|
|
95
|
+
description = "Total instrument time overhead between exposures."
|
|
96
|
+
)
|
|
97
|
+
filters: 'FiltersModel'
|
|
98
|
+
optics: 'OpticsModel'
|
|
99
|
+
detector: 'DetectorModel'
|
|
100
|
+
telescope: 'TelescopeModel'
|
|
101
|
+
site: 'SiteModel'
|
|
102
|
+
default: bool = False
|
|
103
|
+
|
|
104
|
+
transmissions: Optional[dict] = Field(default=None) #type: ignore[annotation-unchecked]
|
|
105
|
+
emissions_ct: Optional[dict] = Field(default=None) #type: ignore[annotation-unchecked]
|
|
106
|
+
|
|
107
|
+
@model_validator(mode="after")
|
|
108
|
+
def _update_transmissions(self):
|
|
109
|
+
# Compute extra parameters during initialization
|
|
110
|
+
area = self.telescope.collecting_area - self.obstruction_area
|
|
111
|
+
# Filter emissions and transmissions
|
|
112
|
+
upstream_transmission = 1.
|
|
113
|
+
upstream_emission = SourceSpectrum(ConstFlux1D, amplitude=0.)
|
|
114
|
+
# Pre-filter list of transmissions
|
|
115
|
+
transmissions = [
|
|
116
|
+
*self.telescope.transmissions.values(),
|
|
117
|
+
*self.optics.transmissions.values()
|
|
118
|
+
]
|
|
119
|
+
emissions = [
|
|
120
|
+
*self.telescope.emissions.values(),
|
|
121
|
+
*self.optics.emissions.values()
|
|
122
|
+
]
|
|
123
|
+
for i, v in enumerate(transmissions):
|
|
124
|
+
emission = emissions[i].spectral
|
|
125
|
+
transmission = transmissions[i].spectral
|
|
126
|
+
upstream_transmission *= transmission
|
|
127
|
+
upstream_emission = upstream_emission * transmission + emission
|
|
128
|
+
self.transmissions : dict[str, TransmissionModel] = {} #type: ignore[annotation-unchecked]
|
|
129
|
+
self.emissions_ct : dict[str, u.Quantity[u.ct/u.s]] = {} #type: ignore[annotation-unchecked]
|
|
130
|
+
filter_transmissions = self.filters.transmissions
|
|
131
|
+
filter_emissions = self.filters.emissions
|
|
132
|
+
for f in filter_transmissions:
|
|
133
|
+
filter = filter_transmissions[f]
|
|
134
|
+
filter_transmission = filter.spectral
|
|
135
|
+
filter_emission = filter_emissions[f].spectral
|
|
136
|
+
transmission = upstream_transmission * filter_transmission
|
|
137
|
+
emission = upstream_emission * transmission + filter_emission
|
|
138
|
+
transmission *= self.detector.transmissions["0"].spectral
|
|
139
|
+
emission *= self.detector.transmissions["0"].spectral
|
|
140
|
+
wave, response = spectral_to_arrays(transmission)
|
|
141
|
+
self.transmissions[f] = TransmissionModel(
|
|
142
|
+
id = filter.id,
|
|
143
|
+
name = filter.name,
|
|
144
|
+
description = filter.description,
|
|
145
|
+
vars = filter.vars,
|
|
146
|
+
wave = wave,
|
|
147
|
+
response = response,
|
|
148
|
+
spectral = transmission
|
|
149
|
+
)
|
|
150
|
+
# Compute countrate
|
|
151
|
+
if transmission.tpeak() > 0.:
|
|
152
|
+
observation = Observation(emission, transmission, force='taper')
|
|
153
|
+
self.emissions_ct[f] = observation.countrate(area=area).value
|
|
154
|
+
else:
|
|
155
|
+
self.emissions_ct[f] = 0.
|
|
156
|
+
return self
|
|
157
|
+
|
|
158
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class OpticsModel(BaseModel):
|
|
163
|
+
'''
|
|
164
|
+
Pydantic model for optics.
|
|
165
|
+
'''
|
|
166
|
+
transmissions: dict[str, 'TransmissionModel']
|
|
167
|
+
emissions: dict[str, 'SBSEDModel']
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class FiltersModel(OpticsModel):
|
|
172
|
+
'''
|
|
173
|
+
Pydantic model for a filter set.
|
|
174
|
+
'''
|
|
175
|
+
pass
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class SBSEDModel(BaseModel):
|
|
180
|
+
'''
|
|
181
|
+
Pydantic model for a Surface Brightness Spectral Energy Distribution (SBSED).
|
|
182
|
+
'''
|
|
183
|
+
id: str
|
|
184
|
+
name: str
|
|
185
|
+
description: str
|
|
186
|
+
vars: dict[str, float|str] = {}
|
|
187
|
+
wave: AnnotatedQuantity( #type: ignore[valid-type]
|
|
188
|
+
unit = "nm",
|
|
189
|
+
ge = 100. * u.nm,
|
|
190
|
+
le = 100. * u.micron,
|
|
191
|
+
min_shape = (2),
|
|
192
|
+
max_shape = (100000),
|
|
193
|
+
decimals = 4
|
|
194
|
+
) | None = None
|
|
195
|
+
sbsed: AnnotatedQuantity( #type: ignore[valid-type]
|
|
196
|
+
unit = "Jy / arcsec2",
|
|
197
|
+
ge = 0. * u.Jy / u.arcsec**2,
|
|
198
|
+
min_shape = (2),
|
|
199
|
+
max_shape = (100000),
|
|
200
|
+
decimals = 6
|
|
201
|
+
) | None = None
|
|
202
|
+
spectral: SourceSpectrum = Field(exclude=True)
|
|
203
|
+
default: bool = False
|
|
204
|
+
|
|
205
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
class SEDModel(BaseModel):
|
|
210
|
+
'''
|
|
211
|
+
Pydantic model for a Spectral Energy Distribution (SED).
|
|
212
|
+
'''
|
|
213
|
+
id: str
|
|
214
|
+
name: str
|
|
215
|
+
description: str
|
|
216
|
+
vars: dict[str, float] = {}
|
|
217
|
+
wave: AnnotatedQuantity( #type: ignore[valid-type]
|
|
218
|
+
unit = "nm",
|
|
219
|
+
ge = 100. * u.nm,
|
|
220
|
+
le = 100. * u.micron,
|
|
221
|
+
min_shape = (2),
|
|
222
|
+
max_shape = (100000),
|
|
223
|
+
decimals = 4
|
|
224
|
+
) | None = None
|
|
225
|
+
sed: AnnotatedQuantity( #type: ignore[valid-type]
|
|
226
|
+
unit = "Jy",
|
|
227
|
+
ge = 0. * u.Jy,
|
|
228
|
+
min_shape = (2),
|
|
229
|
+
max_shape = (100000),
|
|
230
|
+
decimals = 6
|
|
231
|
+
) | None = None
|
|
232
|
+
spectral: SourceSpectrum = Field(exclude=True)
|
|
233
|
+
default: bool = False
|
|
234
|
+
|
|
235
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
class SiteModel(BaseModel):
|
|
240
|
+
'''
|
|
241
|
+
Pydantic model for an observing site.
|
|
242
|
+
'''
|
|
243
|
+
id: str
|
|
244
|
+
name: str
|
|
245
|
+
description: str
|
|
246
|
+
sky_transmissions: dict[str, 'TransmissionModel']
|
|
247
|
+
sky_emissions: dict[str, 'SBSEDModel']
|
|
248
|
+
default: bool = False
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class TelescopeModel(BaseModel):
|
|
253
|
+
'''
|
|
254
|
+
Pydantic model for a telescope.
|
|
255
|
+
'''
|
|
256
|
+
id: str
|
|
257
|
+
name: str
|
|
258
|
+
description: str
|
|
259
|
+
collecting_area: AnnotatedQuantity( #type: ignore[valid-type]
|
|
260
|
+
unit = "m**2",
|
|
261
|
+
gt = 0. * u.m**2,
|
|
262
|
+
decimals = 4,
|
|
263
|
+
description = "Full collecting area, ignoring obstructions."
|
|
264
|
+
)
|
|
265
|
+
obstruction_area: AnnotatedQuantity( #type: ignore[valid-type]
|
|
266
|
+
unit = "m**2",
|
|
267
|
+
gt = 0. * u.m**2,
|
|
268
|
+
decimals = 4,
|
|
269
|
+
description = "Minimum obstruction area."
|
|
270
|
+
)
|
|
271
|
+
transmissions: dict[str, 'TransmissionModel']
|
|
272
|
+
emissions: dict[str, 'SBSEDModel']
|
|
273
|
+
default: bool = False
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
class TransmissionModel(BaseModel):
|
|
278
|
+
'''
|
|
279
|
+
Pydantic model for a transmission curve (with wavelength).
|
|
280
|
+
'''
|
|
281
|
+
id: str
|
|
282
|
+
name: str
|
|
283
|
+
description: str = ""
|
|
284
|
+
vars: Optional[dict[str, float | str]] = None
|
|
285
|
+
wave_range: AnnotatedQuantity( #type: ignore[valid-type]
|
|
286
|
+
unit = "nm",
|
|
287
|
+
gt = [0., 0.] * u.nm,
|
|
288
|
+
min_shape = (2),
|
|
289
|
+
max_shape = (2),
|
|
290
|
+
decimals = 4,
|
|
291
|
+
description = "Instrument minimum and maximum wavelengths."
|
|
292
|
+
) | None = None
|
|
293
|
+
wave: AnnotatedQuantity( #type: ignore[valid-type]
|
|
294
|
+
unit = "nm",
|
|
295
|
+
ge = 100. * u.nm,
|
|
296
|
+
le = 100. * u.micron,
|
|
297
|
+
min_shape = (2),
|
|
298
|
+
max_shape = (100000),
|
|
299
|
+
decimals = 3
|
|
300
|
+
) | None = None
|
|
301
|
+
response: AnnotatedQuantity( #type: ignore[valid-type]
|
|
302
|
+
unit = "",
|
|
303
|
+
ge = -100.,
|
|
304
|
+
le = 100.,
|
|
305
|
+
min_shape = (2),
|
|
306
|
+
max_shape = (100000),
|
|
307
|
+
decimals = 4
|
|
308
|
+
) | None = None
|
|
309
|
+
spectral: Optional[SpectralElement] = Field(default=None, exclude=True)
|
|
310
|
+
default: bool = False
|
|
311
|
+
|
|
312
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
313
|
+
|
|
314
|
+
|