Skip to content

Commit e76d6cc

Browse files
Add frequency/wavelength utilities (#2036)
Signed-off-by: Lucas Heitzmann Gabrielli <[email protected]>
1 parent b0a92fd commit e76d6cc

File tree

4 files changed

+254
-1
lines changed

4 files changed

+254
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2727
### Changed
2828
- Improved autograd tracer handling in `DataArray`, resulting in significant speedups for differentiation involving large monitors.
2929
- Triangulation of `PolySlab` polygons now supports polygons with collinear vertices.
30+
- Frequency and wavelength utilities under `tidy3d.frequencies` and `tidy3d.wavelengths`.
3031

3132
### Fixed
3233
- Minor gradient direction and normalization fixes for polyslab, field monitors, and diffraction monitors in autograd.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import numpy as np
2+
import pytest
3+
import tidy3d as td
4+
5+
6+
def test_classification():
7+
assert td.frequencies.classification(1) == ("near static",)
8+
assert td.wavelengths.classification(td.C_0) == ("near static",)
9+
assert td.frequencies.classification(td.C_0 / 1.55) == ("infrared", "NIR")
10+
assert td.wavelengths.classification(1.55) == ("infrared", "NIR")
11+
12+
13+
@pytest.mark.parametrize("band", ["O", "E", "S", "C", "L", "U"])
14+
def test_bands(band):
15+
freqs = getattr(td.frequencies, band.lower() + "_band")()
16+
ldas = getattr(td.wavelengths, band.lower() + "_band")()
17+
assert np.allclose(freqs, td.C_0 / np.array(ldas))

tidy3d/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,14 +105,16 @@
105105
EMEMonitor,
106106
)
107107

108-
# EME
109108
# EME
110109
from .components.eme.simulation import EMESimulation
111110
from .components.eme.sweep import EMEFreqSweep, EMELengthSweep, EMEModeSweep
112111

113112
# field projection
114113
from .components.field_projection import FieldProjector
115114

115+
# frequency conversion utilities
116+
from .components.frequencies import frequencies, wavelengths
117+
116118
# geometry
117119
from .components.geometry.base import Box, ClipOperation, Geometry, GeometryGroup, Transformed
118120
from .components.geometry.mesh import TriangleMesh
@@ -424,6 +426,8 @@ def set_logging_level(level: str) -> None:
424426
"Q_e",
425427
"K_B",
426428
"inf",
429+
"frequencies",
430+
"wavelengths",
427431
"material_library",
428432
"Graphene",
429433
"AbstractMedium",

tidy3d/components/frequencies.py

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
"""Frequency utilities."""
2+
3+
import numpy as np
4+
import pydantic as pd
5+
6+
from ..constants import C_0
7+
from .base import Tidy3dBaseModel
8+
9+
O_BAND = (1.260, 1.360)
10+
E_BAND = (1.360, 1.460)
11+
S_BAND = (1.460, 1.530)
12+
C_BAND = (1.530, 1.565)
13+
L_BAND = (1.565, 1.625)
14+
U_BAND = (1.625, 1.675)
15+
16+
17+
class FrequencyUtils(Tidy3dBaseModel):
18+
"""Class for general frequency/wavelength utilities."""
19+
20+
use_wavelength: bool = pd.Field(
21+
False,
22+
title="Use wavelength",
23+
description="Indicate whether to use wavelengths instead of frequencies for the return "
24+
"values of functions and parameters.",
25+
)
26+
27+
def classification(self, value: float) -> tuple[str]:
28+
"""Band classification for a given frequency/wavelength.
29+
30+
Frequency values must be given in hertz (Hz). Wavelengths must be
31+
given in micrometers (μm).
32+
33+
Parameters
34+
----------
35+
value : float
36+
Value to classify.
37+
38+
Returns
39+
-------
40+
tuple[str]
41+
String tuple with classification.
42+
"""
43+
if self.use_wavelength:
44+
value = C_0 / value
45+
if value < 3:
46+
return ("near static",)
47+
elif value < 300e6:
48+
if value < 30:
49+
return ("radio wave", "ELF")
50+
elif value < 300:
51+
return ("radio wave", "SLF")
52+
elif value < 3e3:
53+
return ("radio wave", "ULF")
54+
elif value < 30e3:
55+
return ("radio wave", "VLF")
56+
elif value < 300e3:
57+
return ("radio wave", "LF")
58+
elif value < 3e6:
59+
return ("radio wave", "MF")
60+
elif value < 30e6:
61+
return ("radio wave", "HF")
62+
return ("radio wave", "VHF")
63+
elif value < 300e9:
64+
if value < 3e9:
65+
return ("microwave", "UHF")
66+
elif value < 30e9:
67+
return ("microwave", "SHF")
68+
return ("microwave", "EHF")
69+
elif value < 400e12:
70+
if value < 6e12:
71+
return ("infrared", "FIR")
72+
elif value < 100e12:
73+
return ("infrared", "MIR")
74+
return ("infrared", "NIR")
75+
elif value < 790e12:
76+
if value < 480e12:
77+
return ("visible", "red")
78+
elif value < 510e12:
79+
return ("visible", "orange")
80+
elif value < 530e12:
81+
return ("visible", "yellow")
82+
elif value < 600e12:
83+
return ("visible", "green")
84+
elif value < 620e12:
85+
return ("visible", "cyan")
86+
elif value < 670e12:
87+
return ("visible", "blue")
88+
return ("visible", "violet")
89+
elif value < 30e15:
90+
if value < 1e15:
91+
return ("ultraviolet", "NUV")
92+
elif value < 1.5e15:
93+
return ("ultraviolet", "MUV")
94+
elif value < 2.47e15:
95+
return ("ultraviolet", "FUV")
96+
return ("ultraviolet", "EUV")
97+
if value < 30e18:
98+
if value < 3e18:
99+
return ("X-ray", "soft X-ray")
100+
return ("X-ray", "hard X-ray")
101+
return ("γ-ray",)
102+
103+
def o_band(self, n: int = 11) -> list[float]:
104+
"""
105+
Optical O band frequencies/wavelengths sorted by wavelength.
106+
107+
The returned samples are equally spaced in wavelength.
108+
109+
Parameters
110+
----------
111+
n : int
112+
Desired number of samples.
113+
114+
Returns
115+
-------
116+
list[float]
117+
Samples list.
118+
"""
119+
values = np.linspace(*O_BAND, n)
120+
if not self.use_wavelength:
121+
values = C_0 / values
122+
return values.tolist()
123+
124+
def e_band(self, n: int = 11) -> list[float]:
125+
"""
126+
Optical E band frequencies/wavelengths sorted by wavelength.
127+
128+
The returned samples are equally spaced in wavelength.
129+
130+
Parameters
131+
----------
132+
n : int
133+
Desired number of samples.
134+
135+
Returns
136+
-------
137+
list[float]
138+
Samples list.
139+
"""
140+
values = np.linspace(*E_BAND, n)
141+
if not self.use_wavelength:
142+
values = C_0 / values
143+
return values.tolist()
144+
145+
def s_band(self, n: int = 15) -> list[float]:
146+
"""
147+
Optical S band frequencies/wavelengths sorted by wavelength.
148+
149+
The returned samples are equally spaced in wavelength.
150+
151+
Parameters
152+
----------
153+
n : int
154+
Desired number of samples.
155+
156+
Returns
157+
-------
158+
list[float]
159+
Samples list.
160+
"""
161+
values = np.linspace(*S_BAND, n)
162+
if not self.use_wavelength:
163+
values = C_0 / values
164+
return values.tolist()
165+
166+
def c_band(self, n: int = 8) -> list[float]:
167+
"""
168+
Optical C band frequencies/wavelengths sorted by wavelength.
169+
170+
The returned samples are equally spaced in wavelength.
171+
172+
Parameters
173+
----------
174+
n : int
175+
Desired number of samples.
176+
177+
Returns
178+
-------
179+
list[float]
180+
Samples list.
181+
"""
182+
values = np.linspace(*C_BAND, n)
183+
if not self.use_wavelength:
184+
values = C_0 / values
185+
return values.tolist()
186+
187+
def l_band(self, n: int = 13) -> list[float]:
188+
"""
189+
Optical L band frequencies/wavelengths sorted by wavelength.
190+
191+
The returned samples are equally spaced in wavelength.
192+
193+
Parameters
194+
----------
195+
n : int
196+
Desired number of samples.
197+
198+
Returns
199+
-------
200+
list[float]
201+
Samples list.
202+
"""
203+
values = np.linspace(*L_BAND, n)
204+
if not self.use_wavelength:
205+
values = C_0 / values
206+
return values.tolist()
207+
208+
def u_band(self, n: int = 11) -> list[float]:
209+
"""
210+
Optical U band frequencies/wavelengths sorted by wavelength.
211+
212+
The returned samples are equally spaced in wavelength.
213+
214+
Parameters
215+
----------
216+
n : int
217+
Desired number of samples.
218+
219+
Returns
220+
-------
221+
list[float]
222+
Samples list.
223+
"""
224+
values = np.linspace(*U_BAND, n)
225+
if not self.use_wavelength:
226+
values = C_0 / values
227+
return values.tolist()
228+
229+
230+
frequencies = FrequencyUtils(use_wavelength=False)
231+
wavelengths = FrequencyUtils(use_wavelength=True)

0 commit comments

Comments
 (0)