diff --git a/.github/workflows/tidy3d-python-client-tests.yml b/.github/workflows/tidy3d-python-client-tests.yml
index 879f8d6b8e..ce0a430965 100644
--- a/.github/workflows/tidy3d-python-client-tests.yml
+++ b/.github/workflows/tidy3d-python-client-tests.yml
@@ -166,7 +166,7 @@ jobs:
persist-credentials: false
- uses: astral-sh/ruff-action@57714a7c8a2e59f32539362ba31877a1957dded1 # v3.5.1
with:
- version: 0.11.11
+ version: 0.14.1
- name: Run ruff format
run: ruff format --check --diff
- name: Run ruff check
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 51d74be302..c4f6d58236 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -4,7 +4,7 @@ default_install_hook_types:
- commit-msg
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
- rev: "v0.11.11"
+ rev: "v0.14.1"
hooks:
- id: ruff-check
args: [ --fix ]
diff --git a/pyproject.toml b/pyproject.toml
index 18b9f01acc..1287ca4ca9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -52,7 +52,7 @@ typing-extensions = "*"
### Optional dependencies ###
# development core
-ruff = { version = "0.11.11", optional = true }
+ruff = { version = "0.14.1", optional = true }
coverage = { version = "*", optional = true }
dill = { version = "*", optional = true }
ipython = { version = "*", optional = true }
@@ -236,38 +236,38 @@ requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
[tool.ruff]
-target-version = "py39"
+target-version = "py310"
line-length = 100
extend-exclude = ["docs/faq/", "docs/notebooks/"]
[tool.ruff.lint]
extend-select = [
- "E", # pycodestyle errors
- "F", # pyflakes
- "B", # bugbear
- "I", # isort
- "UP", # pyupgrade
- "W", # pycodestyle
- "C4", # flake8-comprehensions
- "NPY", # numpy-specific rules
- "RUF", # ruff builtins
- "ISC", # implicit string concatenation
- "PIE", # flake8-pie
- "RSE", # unnecessary parantheses on raised exceptions
- "TID", # no relative imports from parent modules
- "PLE", # pylint errors
- "PLC", # pylint conventions
+ "E", # pycodestyle errors
+ "F", # pyflakes
+ "B", # bugbear
+ "I", # isort
+ "UP", # pyupgrade
+ "W", # pycodestyle
+ "C4", # flake8-comprehensions
+ "NPY", # numpy-specific rules
+ "RUF", # ruff builtins
+ "ISC", # implicit string concatenation
+ "PIE", # flake8-pie
+ "RSE", # unnecessary parantheses on raised exceptions
+ "TID", # no relative imports from parent modules
+ "PLE", # pylint errors
+ "PLC", # pylint conventions
]
extend-ignore = [
- "RUF001", # ambiguous unicode characters
- "RUF002", # ambiguous unicode characters
- "RUF003", # ambiguous unicode characters
- "RUF012", # type hints for mutable defaults
- "RUF015", # next(iter(...)) instead of list(...)[0]
- "E501", # line too long
- "B905", # `zip()` without an explicit `strict=` parameter
- "UP007", # TODO: Remove once Python >= 3.10
- "NPY002", # TODO: Revisit RNG handling
+ "RUF001", # ambiguous unicode characters
+ "RUF002", # ambiguous unicode characters
+ "RUF003", # ambiguous unicode characters
+ "RUF012", # type hints for mutable defaults
+ "RUF015", # next(iter(...)) instead of list(...)[0]
+ "E501", # line too long
+ "B905", # `zip()` without an explicit `strict=` parameter
+ "NPY002", # TODO: Revisit RNG handling
+ "PLC0415", # allow imports not at top level
]
[tool.ruff.lint.isort]
diff --git a/tests/_test_local/_test_fit_web.py b/tests/_test_local/_test_fit_web.py
index b135514017..b7984f77a8 100644
--- a/tests/_test_local/_test_fit_web.py
+++ b/tests/_test_local/_test_fit_web.py
@@ -3,7 +3,6 @@
from math import isclose
import numpy as np
-
from tidy3d.plugins.fitter import AdvancedFitterParam, StableDispersionFitter
np.random.seed(4)
@@ -37,7 +36,7 @@ def test_dispersion_lossless():
num_poles = 3
num_tries = 10
tolerance_rms = 1e-3
- best_medium, best_rms = fitter.fit(
+ best_medium, _best_rms = fitter.fit(
num_tries=num_tries, num_poles=num_poles, tolerance_rms=tolerance_rms
)
diff --git a/tests/_test_local/_test_plugins_web.py b/tests/_test_local/_test_plugins_web.py
index a475889cff..42c0e4e66d 100644
--- a/tests/_test_local/_test_plugins_web.py
+++ b/tests/_test_local/_test_plugins_web.py
@@ -1,7 +1,6 @@
from __future__ import annotations
import numpy as np
-
from tidy3d.plugins.fitter import DispersionFitter, StableDispersionFitter
diff --git a/tests/_test_local/_test_web.py b/tests/_test_local/_test_web.py
index 5662a27853..80a3c4042e 100644
--- a/tests/_test_local/_test_web.py
+++ b/tests/_test_local/_test_web.py
@@ -5,9 +5,10 @@
import os
from unittest import TestCase, mock
-import tidy3d.web as web
from tidy3d.web.auth import encode_password, get_credentials
+import tidy3d.web as web
+
from ..utils import SIM_FULL as sim_original
CALLBACK_URL = "https://callbackurl"
diff --git a/tests/test_components/autograd/numerical/test_autograd_box_polyslab_numerical.py b/tests/test_components/autograd/numerical/test_autograd_box_polyslab_numerical.py
index 7db87a3f63..c6fd27064c 100644
--- a/tests/test_components/autograd/numerical/test_autograd_box_polyslab_numerical.py
+++ b/tests/test_components/autograd/numerical/test_autograd_box_polyslab_numerical.py
@@ -368,8 +368,8 @@ def test_box_and_polyslab_gradients_match(is_3d, infinite_dim_2d, shift_box_cent
local_gradient=False,
)
- box_value, box_grad = value_and_grad(box_objective)([initial_params])
- polyslab_value, polyslab_grad = value_and_grad(polyslab_objective)([initial_params])
+ _box_value, box_grad = value_and_grad(box_objective)([initial_params])
+ _polyslab_value, polyslab_grad = value_and_grad(polyslab_objective)([initial_params])
box_grad_np = box_grad
polyslab_grad_np = polyslab_grad
diff --git a/tests/test_components/autograd/numerical/test_autograd_conductivity_numerical.py b/tests/test_components/autograd/numerical/test_autograd_conductivity_numerical.py
index f49547422e..f05a050523 100644
--- a/tests/test_components/autograd/numerical/test_autograd_conductivity_numerical.py
+++ b/tests/test_components/autograd/numerical/test_autograd_conductivity_numerical.py
@@ -114,7 +114,7 @@ def make_base_sim(
]
)
- src_size = sim_size_um[0:2] + (0,)
+ src_size = (*sim_size_um[0:2], 0)
wl_min_src_um = 0.9 * adj_wvl_um
wl_max_src_um = 1.1 * adj_wvl_um
@@ -409,10 +409,10 @@ def test_finite_difference_conductivity_data(
sim_geometry = get_sim_geometry(mesh_wvl_um)
box_for_override = td.Box(
- center=(0, 0, 0), size=sim_geometry.size[0:2] + (thickness_um + mesh_wvl_um,)
+ center=(0, 0, 0), size=(*sim_geometry.size[0:2], thickness_um + mesh_wvl_um)
)
- eval_fns, eval_fn_names = make_eval_fns(monitor_size_wvl)
+ eval_fns, _eval_fn_names = make_eval_fns(monitor_size_wvl)
sim_path_dir = tmp_path / f"test{test_number}"
sim_path_dir.mkdir()
@@ -443,7 +443,7 @@ def test_finite_difference_conductivity_data(
)
conductivity_init = overall_conductivity_scaling * np.ones((dim, dim, Nz))
- obj, adj_grad = obj_val_and_grad([conductivity_init])
+ _obj, adj_grad = obj_val_and_grad([conductivity_init])
# empirical step size for finite difference set as 1/10 the size of the conductivity seed value
fd_step = 0.1 * overall_conductivity_scaling
diff --git a/tests/test_components/autograd/numerical/test_autograd_mode_polyslab_numerical.py b/tests/test_components/autograd/numerical/test_autograd_mode_polyslab_numerical.py
index bcb8127c67..bf6edb47a4 100644
--- a/tests/test_components/autograd/numerical/test_autograd_mode_polyslab_numerical.py
+++ b/tests/test_components/autograd/numerical/test_autograd_mode_polyslab_numerical.py
@@ -72,7 +72,7 @@ def make_base_sim(
]
)
- src_size = sim_size_um[0:2] + (0,)
+ src_size = (*sim_size_um[0:2], 0)
wl_min_src_um = 0.9 * adj_wvl_um
wl_max_src_um = 1.1 * adj_wvl_um
@@ -425,7 +425,7 @@ def eval_fn(sim_data):
vertex_centers_x = 1.1 * mesh_wvl_um * np.cos(angles)
vertex_centers_y = 0.8 * mesh_wvl_um * np.sin(angles)
- obj, adj_grad = obj_val_and_grad([list(vertex_centers_x) + list(vertex_centers_y)])
+ _obj, adj_grad = obj_val_and_grad([list(vertex_centers_x) + list(vertex_centers_y)])
for fd_idx in range(NUM_FINITE_DIFFERENCE):
# Create random perturbation of vertices to check against the computed adjoint gradient.
diff --git a/tests/test_components/autograd/numerical/test_autograd_numerical.py b/tests/test_components/autograd/numerical/test_autograd_numerical.py
index 836bf9edde..77b039ffd0 100644
--- a/tests/test_components/autograd/numerical/test_autograd_numerical.py
+++ b/tests/test_components/autograd/numerical/test_autograd_numerical.py
@@ -72,7 +72,7 @@ def make_base_sim(
]
)
- src_size = sim_size_um[0:2] + (0,)
+ src_size = (*sim_size_um[0:2], 0)
wl_min_src_um = 0.9 * adj_wvl_um
wl_max_src_um = 1.1 * adj_wvl_um
@@ -269,10 +269,10 @@ def test_finite_difference_field_data(field_data_test_parameters, rng, tmp_path,
sim_geometry = get_sim_geometry(mesh_wvl_um)
box_for_override = td.Box(
- center=(0, 0, 0), size=sim_geometry.size[0:2] + (thickness_um + mesh_wvl_um,)
+ center=(0, 0, 0), size=(*sim_geometry.size[0:2], thickness_um + mesh_wvl_um)
)
- eval_fns, eval_fn_names = make_eval_fns(monitor_size_wvl)
+ eval_fns, _eval_fn_names = make_eval_fns(monitor_size_wvl)
sim_path_dir = tmp_path / f"test{test_number}"
sim_path_dir.mkdir()
@@ -298,7 +298,7 @@ def test_finite_difference_field_data(field_data_test_parameters, rng, tmp_path,
perm_init = FINITE_DIFF_PERM_SEED * np.ones((dim, dim, Nz))
- obj, adj_grad = obj_val_and_grad([perm_init])
+ _obj, adj_grad = obj_val_and_grad([perm_init])
# empirical step size from running other finite difference tests for field
# cases with permittivity
diff --git a/tests/test_components/autograd/numerical/test_autograd_periodic_numerical.py b/tests/test_components/autograd/numerical/test_autograd_periodic_numerical.py
index a71a34292a..be7b6819af 100644
--- a/tests/test_components/autograd/numerical/test_autograd_periodic_numerical.py
+++ b/tests/test_components/autograd/numerical/test_autograd_periodic_numerical.py
@@ -70,7 +70,7 @@ def make_base_sim(
]
)
- src_size = sim_size_um[0:2] + (0,)
+ src_size = (*sim_size_um[0:2], 0)
wl_min_src_um = 0.9 * adj_wvl_um
wl_max_src_um = 1.1 * adj_wvl_um
@@ -324,10 +324,10 @@ def test_finite_difference_diffraction_data(
box_for_override = td.Box(
center=(sim_geometry.center[0], sim_geometry.center[1], 0),
- size=sim_geometry.size[0:2] + (thickness_um + mesh_wvl_um,),
+ size=(*sim_geometry.size[0:2], thickness_um + mesh_wvl_um),
)
- eval_fns, eval_fn_names = make_eval_fns(
+ _eval_fns, _eval_fn_names = make_eval_fns(
orders_x=order_x, orders_y=order_y, polarization=polarization
)
@@ -357,7 +357,7 @@ def test_finite_difference_diffraction_data(
perm_init = FINITE_DIFF_PERM_SEED * np.ones((dim, dim, Nz))
- obj, adj_grad = obj_val_and_grad([perm_init])
+ _obj, adj_grad = obj_val_and_grad([perm_init])
# empirical step size from running other finite difference tests for field
# cases with permittivity
diff --git a/tests/test_components/autograd/numerical/test_autograd_polyslab_sidewall_numerical.py b/tests/test_components/autograd/numerical/test_autograd_polyslab_sidewall_numerical.py
index 9c74da257c..9c1db6bf76 100644
--- a/tests/test_components/autograd/numerical/test_autograd_polyslab_sidewall_numerical.py
+++ b/tests/test_components/autograd/numerical/test_autograd_polyslab_sidewall_numerical.py
@@ -96,7 +96,7 @@ def test_autograd_polyslab_sidewall_vs_fd(axis, reference_plane, theta0_deg, tmp
# objective and adjoint grad
obj_fun = lambda tdeg: _objective(tdeg, axis, reference_plane, tmp_path, verbose)
- obj, grad_adj = value_and_grad(obj_fun)(anp.array(theta0_deg))
+ _obj, grad_adj = value_and_grad(obj_fun)(anp.array(theta0_deg))
# centered finite difference
uid = f"axis{axis}_ref{reference_plane}_t{float(theta0_deg):+0.3f}"
diff --git a/tests/test_components/autograd/numerical/test_autograd_symmetry_numerical.py b/tests/test_components/autograd/numerical/test_autograd_symmetry_numerical.py
index 4153de9c1f..45de4facf0 100644
--- a/tests/test_components/autograd/numerical/test_autograd_symmetry_numerical.py
+++ b/tests/test_components/autograd/numerical/test_autograd_symmetry_numerical.py
@@ -72,7 +72,7 @@ def make_base_sim(
]
)
- src_size = sim_size_um[0:2] + (0,)
+ src_size = (*sim_size_um[0:2], 0)
wl_min_src_um = 0.9 * adj_wvl_um
wl_max_src_um = 1.1 * adj_wvl_um
@@ -168,7 +168,7 @@ def make_eval_fns(monitor_size_wvl):
def intensity(sim_data):
field_data = sim_data["monitor_fields"]
- shape_x, shape_y, shape_z, *_ = field_data.Ex.values.shape
+ _shape_x, _shape_y, _shape_z, *_ = field_data.Ex.values.shape
return np.sum(np.abs(field_data.Ex.values) ** 2 + np.abs(field_data.Ey.values) ** 2)
@@ -273,10 +273,10 @@ def test_adjoint_difference_symmetry(
sim_geometry = get_sim_geometry(mesh_wvl_um)
box_for_override = td.Box(
- center=(0, 0, 0), size=sim_geometry.size[0:2] + (thickness_um + mesh_wvl_um,)
+ center=(0, 0, 0), size=(*sim_geometry.size[0:2], thickness_um + mesh_wvl_um)
)
- eval_fns, eval_fn_names = make_eval_fns(monitor_size_wvl)
+ eval_fns, _eval_fn_names = make_eval_fns(monitor_size_wvl)
sim_path_dir = tmp_path / f"test{test_number}"
sim_path_dir.mkdir()
diff --git a/tests/test_components/autograd/test_autograd.py b/tests/test_components/autograd/test_autograd.py
index 898ff330a8..f78494ef22 100644
--- a/tests/test_components/autograd/test_autograd.py
+++ b/tests/test_components/autograd/test_autograd.py
@@ -617,7 +617,7 @@ def plot_sim(sim: td.Simulation, plot_eps: bool = True) -> None:
plot_fn = sim.plot_eps if plot_eps else sim.plot
- f, (ax1, ax2, ax3) = plt.subplots(1, 3, tight_layout=True)
+ _f, (ax1, ax2, ax3) = plt.subplots(1, 3, tight_layout=True)
plot_fn(x=0, ax=ax1)
plot_fn(y=0, ax=ax2)
plot_fn(z=0, ax=ax3)
@@ -949,7 +949,7 @@ def obj(center: tuple, size: tuple) -> float:
return objval
d_power = ag.value_and_grad(obj, argnum=(0, 1))
- val, (dp_dcenter, dp_dsize) = d_power(self.center0, self.size0)
+ _val, (dp_dcenter, dp_dsize) = d_power(self.center0, self.size0)
assert len(dp_dcenter) == 3
assert len(dp_dsize) == 3
@@ -980,7 +980,7 @@ def objective(*args):
values.append(postprocess(sim_data))
return min(values)
- val, grad = ag.value_and_grad(objective)(params0)
+ _val, grad = ag.value_and_grad(objective)(params0)
assert anp.all(grad != 0.0), "some gradients are 0"
@@ -1034,7 +1034,7 @@ def objective(*args):
# if speed test, get the profile
with cProfile.Profile() as pr:
t = time.time()
- val, grad = ag.value_and_grad(objective)(params0)
+ _val, _grad = ag.value_and_grad(objective)(params0)
t2 = time.time() - t
pr.print_stats(sort="cumtime")
pr.dump_stats("results.prof")
@@ -1050,7 +1050,7 @@ def test_autograd_polyslab_cylinder(use_emulated_run, monitor_key):
num_pts = 819
- monitor, postprocess = make_monitors()[monitor_key]
+ monitor, _postprocess = make_monitors()[monitor_key]
def make_cylinder(radius, x0, y0, t):
return td.Cylinder(
@@ -1122,7 +1122,7 @@ def objective(*args):
value = postprocess(data)
return value
- val, grad = ag.value_and_grad(objective)(params0)
+ _val, grad = ag.value_and_grad(objective)(params0)
assert np.all(np.abs(grad) > 0), "some gradients are 0"
@@ -1144,7 +1144,7 @@ def objective(*args):
value = value + postprocess(sim_data)
return value
- val, grad = ag.value_and_grad(objective)(params0)
+ _val, grad = ag.value_and_grad(objective)(params0)
assert np.all(np.abs(grad) > 0), "some gradients are 0"
@@ -2838,7 +2838,8 @@ def rotated_grad(angle: float, axis: int) -> np.ndarray:
if expect_exception:
with pytest.raises(
- AttributeError, match=".*'Transformed' object has no attribute 'vertices'.*"
+ AttributeError,
+ match=r".*'Transformed' object has no attribute 'vertices'.*",
):
rotated_grad(theta, axis)
else:
@@ -2984,7 +2985,7 @@ def objective(params):
)
return postprocess(data, data[monitor.name])
- val, grad = ag.value_and_grad(objective)(params0)
+ _val, grad = ag.value_and_grad(objective)(params0)
assert anp.all(grad != 0.0), "some gradients are 0 for conductivity-only test"
diff --git a/tests/test_components/autograd/test_autograd_polyslab.py b/tests/test_components/autograd/test_autograd_polyslab.py
index 3aa372aabc..79da8cacae 100644
--- a/tests/test_components/autograd/test_autograd_polyslab.py
+++ b/tests/test_components/autograd/test_autograd_polyslab.py
@@ -13,8 +13,6 @@
from __future__ import annotations
-from typing import Optional
-
import numpy as np
import numpy.testing as npt
import pytest
@@ -95,8 +93,8 @@ def __init__(
paths,
axis: int,
coeffs: dict[str, float],
- bounds_intersect: Optional[tuple[tuple[float, ...], tuple[float, ...]]] = None,
- simulation_bounds: Optional[tuple[tuple[float, ...], tuple[float, ...]]] = None,
+ bounds_intersect: tuple[tuple[float, ...], tuple[float, ...]] | None = None,
+ simulation_bounds: tuple[tuple[float, ...], tuple[float, ...]] | None = None,
) -> None:
self.paths = paths
self.axis = axis
diff --git a/tests/test_components/autograd/test_autograd_polyslab_sidewall.py b/tests/test_components/autograd/test_autograd_polyslab_sidewall.py
index a0490afca7..512cbc73fc 100644
--- a/tests/test_components/autograd/test_autograd_polyslab_sidewall.py
+++ b/tests/test_components/autograd/test_autograd_polyslab_sidewall.py
@@ -52,7 +52,7 @@ def sim_bounds():
def test_constant_g_zero(geom, sim_bounds):
- ps, H, theta, P = geom
+ ps, H, _theta, P = geom
sim_min, sim_max = sim_bounds
g = lambda xyz: np.ones(xyz.shape[0], dtype=float)
di = FakeDerivativeInfo(g, dx=H / 50)
@@ -76,7 +76,7 @@ def test_linear_z_matches_closed_form(geom, sim_bounds):
def test_2d_returns_zero(geom, sim_bounds):
- ps, H, theta, P = geom
+ ps, H, _theta, _P = geom
sim_min, sim_max = sim_bounds
z0 = ps.center_axis
g = lambda xyz: (xyz[:, 2] - z0)
@@ -88,7 +88,7 @@ def test_2d_returns_zero(geom, sim_bounds):
def test_vertex_order_invariance(geom, sim_bounds):
- ps, H, theta, P = geom
+ ps, H, _theta, _P = geom
sim_min, sim_max = sim_bounds
z0 = ps.center_axis
g = lambda xyz: (xyz[:, 2] - z0)
diff --git a/tests/test_components/autograd/test_autograd_rf_box.py b/tests/test_components/autograd/test_autograd_rf_box.py
index 9f88a136ff..e2eb2b667d 100644
--- a/tests/test_components/autograd/test_autograd_rf_box.py
+++ b/tests/test_components/autograd/test_autograd_rf_box.py
@@ -77,7 +77,7 @@ def make_base_sim(
]
)
- src_size = sim_size_um[0:2] + (0,)
+ src_size = (*sim_size_um[0:2], 0)
wl_min_src_um = 0.9 * adj_wvl_um
wl_max_src_um = 1.1 * adj_wvl_um
@@ -268,7 +268,7 @@ def make_eval_fns(monitor_size_wvl):
def intensity(sim_data):
field_data = sim_data["monitor_fields"]
- shape_x, shape_y, shape_z, *_ = field_data.Ex.values.shape
+ _shape_x, _shape_y, _shape_z, *_ = field_data.Ex.values.shape
proj_pol_rotation = field_data.Ex.values - field_data.Ey.values
return np.sum(np.abs(proj_pol_rotation) ** 2)
@@ -478,10 +478,10 @@ def test_finite_difference_2d_box_pec(rf_2d_test_parameters, rng, tmp_path, crea
box_for_override = td.Box(
center=(0, 0, 0),
- size=sim_geometry.size[0:2] + (thickness_box_placement_um + 0.3 * mesh_wvl_um,),
+ size=(*sim_geometry.size[0:2], thickness_box_placement_um + 0.3 * mesh_wvl_um),
)
- eval_fns, eval_fn_names = make_eval_fns(monitor_size_wvl)
+ _eval_fns, _eval_fn_names = make_eval_fns(monitor_size_wvl)
sim_path_dir = tmp_path / f"test{test_number}"
sim_path_dir.mkdir()
@@ -521,7 +521,7 @@ def test_finite_difference_2d_box_pec(rf_2d_test_parameters, rng, tmp_path, crea
obj_val_and_grad = ag.value_and_grad(objective)
- obj, adj_grad = obj_val_and_grad([all_box_parameters])
+ _obj, adj_grad = obj_val_and_grad([all_box_parameters])
fd_grad, valid_mask = run_and_process_fd(
all_box_parameters=all_box_parameters, fd_step=fd_step, objective=objective
@@ -658,10 +658,10 @@ def test_finite_difference_3d_box_pec(rf_3d_test_parameters, rng, tmp_path, crea
box_for_override = td.Box(
center=(0, 0, 0),
- size=sim_geometry.size[0:2] + (thickness_box_placement_um + 0.3 * mesh_wvl_um,),
+ size=(*sim_geometry.size[0:2], thickness_box_placement_um + 0.3 * mesh_wvl_um),
)
- eval_fns, eval_fn_names = make_eval_fns(monitor_size_wvl)
+ _eval_fns, _eval_fn_names = make_eval_fns(monitor_size_wvl)
sim_path_dir = tmp_path / f"test{test_number}"
sim_path_dir.mkdir()
@@ -701,7 +701,7 @@ def test_finite_difference_3d_box_pec(rf_3d_test_parameters, rng, tmp_path, crea
obj_val_and_grad = ag.value_and_grad(objective)
- obj, adj_grad = obj_val_and_grad([all_box_parameters])
+ _obj, adj_grad = obj_val_and_grad([all_box_parameters])
fd_grad, valid_mask = run_and_process_fd(
all_box_parameters=all_box_parameters, fd_step=fd_step, objective=objective
diff --git a/tests/test_components/autograd/test_autograd_rf_polyslab.py b/tests/test_components/autograd/test_autograd_rf_polyslab.py
index 30ede80272..09cc7747be 100644
--- a/tests/test_components/autograd/test_autograd_rf_polyslab.py
+++ b/tests/test_components/autograd/test_autograd_rf_polyslab.py
@@ -72,7 +72,7 @@ def make_base_sim(
]
)
- src_size = sim_size_um[0:2] + (0,)
+ src_size = (*sim_size_um[0:2], 0)
wl_min_src_um = 0.9 * adj_wvl_um
wl_max_src_um = 1.1 * adj_wvl_um
@@ -220,7 +220,7 @@ def objective(polyslab_param_arrays):
def make_eval_fns(monitor_size_wvl):
def intensity(sim_data):
field_data = sim_data["monitor_fields"]
- shape_x, shape_y, shape_z, *_ = field_data.Ex.values.shape
+ _shape_x, _shape_y, _shape_z, *_ = field_data.Ex.values.shape
proj_pol_rotation = field_data.Ex.values - field_data.Ey.values
return np.sum(np.abs(proj_pol_rotation) ** 2)
@@ -428,10 +428,10 @@ def test_finite_difference_2d_polyslab_pec(rf_2d_test_parameters, rng, tmp_path,
box_for_override = td.Box(
center=(0, 0, 0),
- size=sim_geometry.size[0:2] + (thickness_box_placement_um + 0.3 * mesh_wvl_um,),
+ size=(*sim_geometry.size[0:2], thickness_box_placement_um + 0.3 * mesh_wvl_um),
)
- eval_fns, eval_fn_names = make_eval_fns(monitor_size_wvl)
+ _eval_fns, _eval_fn_names = make_eval_fns(monitor_size_wvl)
sim_path_dir = tmp_path / f"test{test_number}"
sim_path_dir.mkdir()
@@ -468,7 +468,7 @@ def test_finite_difference_2d_polyslab_pec(rf_2d_test_parameters, rng, tmp_path,
obj_val_and_grad = ag.value_and_grad(objective)
- obj, adj_grad = obj_val_and_grad([polyslab])
+ _obj, adj_grad = obj_val_and_grad([polyslab])
fd_grad, valid_mask = run_and_process_fd(
polyslab_parameters=polyslab, fd_step=fd_step, objective=objective
@@ -585,10 +585,10 @@ def test_finite_difference_3d_polyslab_pec(rf_3d_test_parameters, rng, tmp_path,
box_for_override = td.Box(
center=(0, 0, 0),
- size=sim_geometry.size[0:2] + (thickness_box_placement_um,),
+ size=(*sim_geometry.size[0:2], thickness_box_placement_um),
)
- eval_fns, eval_fn_names = make_eval_fns(monitor_size_wvl)
+ _eval_fns, _eval_fn_names = make_eval_fns(monitor_size_wvl)
sim_path_dir = tmp_path / f"test{test_number}"
sim_path_dir.mkdir()
@@ -624,7 +624,7 @@ def test_finite_difference_3d_polyslab_pec(rf_3d_test_parameters, rng, tmp_path,
obj_val_and_grad = ag.value_and_grad(objective)
- obj, adj_grad = obj_val_and_grad([polyslab])
+ _obj, adj_grad = obj_val_and_grad([polyslab])
fd_grad, valid_mask = run_and_process_fd(
polyslab_parameters=polyslab, fd_step=fd_step, objective=objective
diff --git a/tests/test_components/test_apodization.py b/tests/test_components/test_apodization.py
index 4fadd253d3..ba4e46efea 100644
--- a/tests/test_components/test_apodization.py
+++ b/tests/test_components/test_apodization.py
@@ -49,6 +49,6 @@ def test_plot():
a.plot(times)
plt.close()
- fig, ax = plt.subplots(1, 1)
+ _fig, ax = plt.subplots(1, 1)
a.plot(times, ax=ax)
plt.close()
diff --git a/tests/test_components/test_frequencies.py b/tests/test_components/test_frequencies.py
index ae8288e3e5..899cb882e1 100644
--- a/tests/test_components/test_frequencies.py
+++ b/tests/test_components/test_frequencies.py
@@ -305,12 +305,14 @@ def test_sweep_decade_edge_cases():
# Negative points per decade
with pytest.raises(
- ValueError, match="'num_points_per_decade' must be strictly positive, got -1."
+ ValueError,
+ match=r"'num_points_per_decade' must be strictly positive, got -1.",
):
freq_range.sweep_decade(-1)
# Zero points per decade
with pytest.raises(
- ValueError, match="'num_points_per_decade' must be strictly positive, got 0."
+ ValueError,
+ match=r"'num_points_per_decade' must be strictly positive, got 0.",
):
freq_range.sweep_decade(0)
diff --git a/tests/test_components/test_geometry.py b/tests/test_components/test_geometry.py
index 8bf9347753..860c89863a 100644
--- a/tests/test_components/test_geometry.py
+++ b/tests/test_components/test_geometry.py
@@ -1303,7 +1303,7 @@ def negative_height_func(x, y):
with pytest.raises(
ValueError,
- match="All height values must be non-negative.",
+ match=r"All height values must be non-negative.",
):
td.TriangleMesh.from_height_function(
axis=axis,
diff --git a/tests/test_components/test_heat.py b/tests/test_components/test_heat.py
index 4b4ab27338..ccf8146d85 100644
--- a/tests/test_components/test_heat.py
+++ b/tests/test_components/test_heat.py
@@ -114,7 +114,7 @@ def make_heat_bcs():
def test_heat_bcs():
- bc_temp, bc_flux, bc_conv = make_heat_bcs()
+ _bc_temp, _bc_flux, _bc_conv = make_heat_bcs()
with pytest.raises(pd.ValidationError):
_ = TemperatureBC(temperature=-10)
@@ -314,7 +314,7 @@ def test_heat_source():
def make_heat_sim(include_custom_source: bool = True):
- fluid_medium, solid_medium = make_heat_mediums()
+ fluid_medium, _solid_medium = make_heat_mediums()
fluid_structure, solid_structure = make_heat_structures()
bc_temp, bc_flux, bc_conv = make_heat_bcs()
sources = [make_heat_source()]
diff --git a/tests/test_components/test_heat_charge.py b/tests/test_components/test_heat_charge.py
index f6764390f2..6394d353c2 100644
--- a/tests/test_components/test_heat_charge.py
+++ b/tests/test_components/test_heat_charge.py
@@ -872,7 +872,7 @@ def test_heat_charge_structures_creation(structures):
def test_heat_charge_bcs_validation(boundary_conditions):
"""Tests the validators for boundary conditions."""
- bc_temp, bc_flux, bc_conv, bc_volt, bc_current = boundary_conditions
+ _bc_temp, _bc_flux, _bc_conv, _bc_volt, _bc_current = boundary_conditions
# Invalid TemperatureBC
with pytest.raises(pd.ValidationError):
@@ -968,7 +968,7 @@ def test_freqs_validation():
# Test that freqs without SSACVoltageSource raises error
with pytest.raises(
pd.ValidationError,
- match="If 'freqs' is provided and not empty, at least one 'SSACVoltageSource' must be present in the boundary conditions.",
+ match=r"If 'freqs' is provided and not empty, at least one 'SSACVoltageSource' must be present in the boundary conditions.",
):
sim.updated_copy(
boundary_spec=[
@@ -984,10 +984,10 @@ def test_freqs_validation():
assert np.isclose(freqs, freqs_input).all()
assert np.isclose(1e-3, amplitude)
- with pytest.raises(pd.ValidationError, match="'freqs' cannot contain infinite frequencies."):
+ with pytest.raises(pd.ValidationError, match=r"'freqs' cannot contain infinite frequencies."):
sim.updated_copy(analysis_spec=sim.analysis_spec.updated_copy(freqs=[1e2, np.inf]))
- with pytest.raises(pd.ValidationError, match="'freqs' cannot contain negative frequencies."):
+ with pytest.raises(pd.ValidationError, match=r"'freqs' cannot contain negative frequencies."):
sim.updated_copy(analysis_spec=sim.analysis_spec.updated_copy(freqs=[1e2, -1e2]))
@@ -1315,7 +1315,7 @@ def test_heat_charge_simulation(simulation_data):
def test_sim_data_plotting(simulation_data):
"""Tests whether simulation data can be plotted and appropriate errors are raised."""
- heat_sim_data, cond_sim_data, cap_sim_data, fc_sim_data, mesh_data = simulation_data
+ heat_sim_data, cond_sim_data, _cap_sim_data, _fc_sim_data, _mesh_data = simulation_data
# Plotting temperature data
heat_sim_data.plot_field("test", z=0)
@@ -1356,7 +1356,7 @@ def test_sim_data_plotting(simulation_data):
def test_mesh_plotting(simulation_data):
"""Tests whether mesh can be plotted and appropriate errors are raised."""
- heat_sim_data, cond_sim_data, cap_sim_data, fc_sim_data, mesh_data = simulation_data
+ heat_sim_data, cond_sim_data, _cap_sim_data, _fc_sim_data, mesh_data = simulation_data
# Plotting mesh from unstructured temperature data
heat_sim_data.plot_mesh("tri")
@@ -1579,7 +1579,8 @@ def test_charge_simulation(
condition_ssac_p = td.VoltageBC(source=td.SSACVoltageSource(voltage=[0, 1], amplitude=1e-3))
# Two AC sources cannot be defined
with pytest.raises(
- pd.ValidationError, match="Only a single 'SSACVoltageSource' source can be supplied."
+ pd.ValidationError,
+ match=r"Only a single 'SSACVoltageSource' source can be supplied.",
):
analysis = td.IsothermalSSACAnalysis(freqs=[1e2, 1e3], temperature=300)
sim.updated_copy(
@@ -1592,7 +1593,8 @@ def test_charge_simulation(
# Test SSACAnalysis as well
with pytest.raises(
- pd.ValidationError, match="Only a single 'SSACVoltageSource' source can be supplied."
+ pd.ValidationError,
+ match=r"Only a single 'SSACVoltageSource' source can be supplied.",
):
analysis_ssac = td.SSACAnalysis(freqs=[1e2, 1e3], tolerance_settings=charge_tolerance)
sim.updated_copy(
@@ -2026,7 +2028,7 @@ def test_dynamic_simulation_updates(heat_simulation):
def test_plotting_functions(simulation_data):
"""Test plotting functions with various data."""
- heat_sim_data, cond_sim_data, cap_sim_data, fc_sim_data, mesh_data = simulation_data
+ heat_sim_data, cond_sim_data, _cap_sim_data, _fc_sim_data, _mesh_data = simulation_data
# Valid plotting
try:
diff --git a/tests/test_components/test_lumped_element.py b/tests/test_components/test_lumped_element.py
index 957a4e8aeb..390e45a995 100644
--- a/tests/test_components/test_lumped_element.py
+++ b/tests/test_components/test_lumped_element.py
@@ -366,7 +366,7 @@ def test_distribution_variants(dist_type, width):
else:
assert structure is None
network = linear_element.to_structure(grid)
- L, C = linear_element.estimate_parasitic_elements(grid)
+ _L, C = linear_element.estimate_parasitic_elements(grid)
assert C >= 0
# Grid is fine enough that there are two connections made along x
@@ -379,5 +379,5 @@ def test_distribution_variants(dist_type, width):
else:
assert structure is None
network = linear_element.to_structure(grid)
- L, C = linear_element.estimate_parasitic_elements(grid)
+ _L, C = linear_element.estimate_parasitic_elements(grid)
assert C >= 0
diff --git a/tests/test_components/test_microwave.py b/tests/test_components/test_microwave.py
index b8a882f1b3..19ff6e10e4 100644
--- a/tests/test_components/test_microwave.py
+++ b/tests/test_components/test_microwave.py
@@ -676,7 +676,7 @@ def test_mode_plane_analyzer_canonical_shapes(colocate, tline_type):
size=modal_plane.size,
field_data_colocated=mode_monitor.colocate,
)
- bounding_boxes, geos = mode_plane_analyzer.get_conductor_bounding_boxes(
+ bounding_boxes, _geos = mode_plane_analyzer.get_conductor_bounding_boxes(
sim.structures,
sim.grid,
sim.symmetry,
@@ -725,7 +725,7 @@ def test_mode_plane_analyzer_advanced(use_2D, symmetry):
size=modal_plane.size,
field_data_colocated=mode_monitor.colocate,
)
- bounding_boxes, geos = mode_plane_analyzer.get_conductor_bounding_boxes(
+ bounding_boxes, _geos = mode_plane_analyzer.get_conductor_bounding_boxes(
sim.structures,
sim.grid,
sim.symmetry,
diff --git a/tests/test_components/test_monitor.py b/tests/test_components/test_monitor.py
index b8b9faa60b..050d1ca4e1 100644
--- a/tests/test_components/test_monitor.py
+++ b/tests/test_components/test_monitor.py
@@ -55,7 +55,7 @@ def test_time_inds():
def test_downsampled():
M = td.FieldMonitor(size=(1, 1, 1), name="f", freqs=[1e12], interval_space=(1, 2, 3))
num_cells = (10, 10, 10)
- downsampled_num_cells = a, b, c = M.downsampled_num_cells(num_cells=(10, 10, 10))
+ downsampled_num_cells = _a, _b, _c = M.downsampled_num_cells(num_cells=(10, 10, 10))
assert downsampled_num_cells != num_cells
diff --git a/tests/test_components/test_scene.py b/tests/test_components/test_scene.py
index ff5178d182..e7772b97fc 100644
--- a/tests/test_components/test_scene.py
+++ b/tests/test_components/test_scene.py
@@ -362,7 +362,7 @@ def test_max_geometry_validation():
medium=td.Medium(permittivity=2.0),
),
]
- with pytest.raises(pd.ValidationError, match=f" {MAX_GEOMETRY_COUNT + 2} "):
+ with pytest.raises(pd.ValidationError, match=rf" {MAX_GEOMETRY_COUNT + 2} "):
_ = td.Scene(structures=not_fine)
@@ -521,20 +521,21 @@ def test_log_scale_with_custom_limits():
_ = scene.plot_eps(x=0, scale="log", eps_lim=(1e-5, 100))
plt.close()
- with pytest.raises(SetupError, match="Log scale cannot be used with non-positive values."):
+ with pytest.raises(SetupError, match=r"Log scale cannot be used with non-positive values."):
_ = scene.plot_eps(x=0, scale="log", eps_lim=(-1e-2, 100))
plt.close()
_ = scene.plot_structures_property(x=0, property="eps", scale="log", limits=(1e-2, 100))
plt.close()
- with pytest.raises(SetupError, match="Log scale cannot be used with non-positive values."):
+ with pytest.raises(SetupError, match=r"Log scale cannot be used with non-positive values."):
_ = scene.plot_structures_property(x=0, property="eps", scale="log", limits=(-2e-2, 100))
plt.close()
# Test that invalid scale raises error
with pytest.raises(
- SetupError, match="The scale 'invalid' is not supported for plotting structures property."
+ SetupError,
+ match=r"The scale 'invalid' is not supported for plotting structures property.",
):
_ = scene.plot_structures_property(x=0, property="eps", scale="invalid")
plt.close()
diff --git a/tests/test_components/test_structure.py b/tests/test_components/test_structure.py
index 04553e69d8..65da77ee72 100644
--- a/tests/test_components/test_structure.py
+++ b/tests/test_components/test_structure.py
@@ -63,7 +63,7 @@ def test_lower_dimension_custom_medium_to_gds(tmp_path):
y = np.array([0.0])
z = np.linspace(-1, 1, nz)
f = np.array([td.C_0])
- mx, my, mz, _ = np.meshgrid(x, y, z, f, indexing="ij", sparse=True)
+ mx, _my, mz, _ = np.meshgrid(x, y, z, f, indexing="ij", sparse=True)
data = 1 + 1 / (1 + (mx - 1) ** 2 + mz**2)
eps_diagonal_data = td.ScalarFieldDataArray(data, coords={"x": x, "y": y, "z": z, "f": f})
eps_components = {f"eps_{d}{d}": eps_diagonal_data for d in "xyz"}
diff --git a/tests/test_components/test_viz.py b/tests/test_components/test_viz.py
index 4a93435b53..27eaed797c 100644
--- a/tests/test_components/test_viz.py
+++ b/tests/test_components/test_viz.py
@@ -289,14 +289,14 @@ def test_sim_plot_fill_structures():
run_time=1e-12,
)
- fig1, ax1 = plt.subplots()
+ _fig1, ax1 = plt.subplots()
sim.plot(x=0, fill_structures=False, ax=ax1)
structure_patches = [p for p in ax1.patches if isinstance(p, mpl.patches.PathPatch)]
for patch in structure_patches[:1]: # only one structure, rest is PML etc
assert not patch.get_fill(), "Should be unfilled when False"
assert patch.get_edgecolor() != "none"
- fig2, ax2 = plt.subplots()
+ _fig2, ax2 = plt.subplots()
sim.plot(x=0, fill_structures=True, ax=ax2)
structure_patches = [p for p in ax2.patches if isinstance(p, mpl.patches.PathPatch)]
for patch in structure_patches[:1]:
@@ -314,7 +314,7 @@ def test_sim_plot_structures_fill():
run_time=1e-12,
)
- fig1, ax1 = plt.subplots()
+ _fig1, ax1 = plt.subplots()
sim.plot_structures(x=0, fill=False, ax=ax1)
structure_patches = [p for p in ax1.patches if isinstance(p, mpl.patches.PathPatch)]
assert len(structure_patches) > 0, "No structures plotted"
@@ -324,7 +324,7 @@ def test_sim_plot_structures_fill():
assert patch.get_edgecolor() != "none", "Edges should be visible"
assert patch.get_linewidth() > 0, "Edge width should be positive"
- fig2, ax2 = plt.subplots()
+ _fig2, ax2 = plt.subplots()
sim.plot_structures(x=0, fill=True, ax=ax2)
structure_patches = [p for p in ax2.patches if isinstance(p, mpl.patches.PathPatch)]
assert len(structure_patches) > 0, "No structures plotted"
diff --git a/tests/test_data/test_data_arrays.py b/tests/test_data/test_data_arrays.py
index 5e3108187b..6c531aa16d 100644
--- a/tests/test_data/test_data_arrays.py
+++ b/tests/test_data/test_data_arrays.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Optional
-
import autograd as ag
import autograd.numpy as np
import numpy
@@ -145,7 +143,7 @@ def get_xyz(
return x, y, z
-def make_scalar_field_data_array(grid_key: str, symmetry=True, colocate: Optional[bool] = None):
+def make_scalar_field_data_array(grid_key: str, symmetry=True, colocate: bool | None = None):
monitor = FIELD_MONITOR
if colocate is not None:
monitor = monitor.updated_copy(colocate=colocate)
@@ -160,13 +158,11 @@ def make_scalar_field_time_data_array(grid_key: str, symmetry=True):
return td.ScalarFieldTimeDataArray(values, coords={"x": XS, "y": YS, "z": ZS, "t": TS})
-def make_scalar_mode_field_data_array(
- grid_key: str, symmetry=True, colocate: Optional[bool] = None
-):
+def make_scalar_mode_field_data_array(grid_key: str, symmetry=True, colocate: bool | None = None):
monitor = MODE_MONITOR_WITH_FIELDS
if colocate is not None:
monitor = monitor.updated_copy(colocate=colocate)
- XS, YS, ZS = get_xyz(monitor, grid_key, symmetry)
+ XS, _YS, ZS = get_xyz(monitor, grid_key, symmetry)
values = (1 + 0.1j) * np.random.random((len(XS), 1, len(ZS), len(FS), len(MODE_INDICES)))
return td.ScalarModeFieldDataArray(
@@ -175,7 +171,7 @@ def make_scalar_mode_field_data_array(
def make_scalar_mode_field_data_array_smooth(grid_key: str, symmetry=True, rot: float = 0):
- XS, YS, ZS = get_xyz(MODE_MONITOR_WITH_FIELDS, grid_key, symmetry)
+ XS, _YS, ZS = get_xyz(MODE_MONITOR_WITH_FIELDS, grid_key, symmetry)
values = np.array([1 + 0.1j])[None, :, None, None, None] * np.sin(
0.5
diff --git a/tests/test_data/test_monitor_data.py b/tests/test_data/test_monitor_data.py
index ab0598f8c0..cc628184c6 100644
--- a/tests/test_data/test_monitor_data.py
+++ b/tests/test_data/test_monitor_data.py
@@ -475,7 +475,7 @@ def test_directivity_data_from_projected_fields():
monitor, proj_angle_data = make_field_dataset_using_power_density(
values, theta, phi, freqs, r_proj
)
- with pytest.raises(ValueError, match="Chosen limits for `theta` are not appropriate"):
+ with pytest.raises(ValueError, match=r"Chosen limits for `theta` are not appropriate"):
dir_data = td.DirectivityData.from_spherical_field_dataset(monitor, proj_angle_data)
# Test invalid phi range
@@ -485,7 +485,7 @@ def test_directivity_data_from_projected_fields():
monitor, proj_angle_data = make_field_dataset_using_power_density(
values, theta, phi, freqs, r_proj
)
- with pytest.raises(ValueError, match="Chosen limits for `phi` are not appropriate"):
+ with pytest.raises(ValueError, match=r"Chosen limits for `phi` are not appropriate"):
dir_data = td.DirectivityData.from_spherical_field_dataset(monitor, proj_angle_data)
# Test too coarse sampling
@@ -495,7 +495,7 @@ def test_directivity_data_from_projected_fields():
monitor, proj_angle_data = make_field_dataset_using_power_density(
values, theta, phi, freqs, r_proj
)
- with pytest.raises(ValueError, match="There are not enough sampling points"):
+ with pytest.raises(ValueError, match=r"There are not enough sampling points"):
dir_data = td.DirectivityData.from_spherical_field_dataset(monitor, proj_angle_data)
# Test unsorted
@@ -505,7 +505,7 @@ def test_directivity_data_from_projected_fields():
monitor, proj_angle_data = make_field_dataset_using_power_density(
values, theta, phi, freqs, r_proj
)
- with pytest.raises(ValueError, match="theta was not provided as a sorted array."):
+ with pytest.raises(ValueError, match=r"theta was not provided as a sorted array."):
dir_data = td.DirectivityData.from_spherical_field_dataset(monitor, proj_angle_data)
# Test success case with proper sampling
diff --git a/tests/test_plugins/autograd/invdes/test_filters.py b/tests/test_plugins/autograd/invdes/test_filters.py
index ddab528e81..9d4e898692 100644
--- a/tests/test_plugins/autograd/invdes/test_filters.py
+++ b/tests/test_plugins/autograd/invdes/test_filters.py
@@ -30,7 +30,8 @@ def test_get_kernel_size(radius, dl, size_px, expected):
def test_get_kernel_size_invalid_arguments():
with pytest.raises(
- ValueError, match="Either 'size_px' or both 'radius' and 'dl' must be provided."
+ ValueError,
+ match=r"Either 'size_px' or both 'radius' and 'dl' must be provided.",
):
_get_kernel_size(None, None, None)
diff --git a/tests/test_plugins/expressions/test_variables.py b/tests/test_plugins/expressions/test_variables.py
index da74b03dc0..781768314e 100644
--- a/tests/test_plugins/expressions/test_variables.py
+++ b/tests/test_plugins/expressions/test_variables.py
@@ -37,19 +37,19 @@ def test_variable_evaluate_named(value):
def test_variable_missing_positional():
variable = Variable()
- with pytest.raises(ValueError, match="No positional argument provided for unnamed variable."):
+ with pytest.raises(ValueError, match=r"No positional argument provided for unnamed variable."):
variable.evaluate()
def test_variable_missing_named():
variable = Variable(name="x")
- with pytest.raises(ValueError, match="Variable 'x' not provided."):
+ with pytest.raises(ValueError, match=r"Variable 'x' not provided."):
variable.evaluate()
def test_variable_wrong_named():
variable = Variable(name="x")
- with pytest.raises(ValueError, match="Variable 'x' not provided."):
+ with pytest.raises(ValueError, match=r"Variable 'x' not provided."):
variable.evaluate(y=5)
@@ -89,9 +89,9 @@ def test_variable_missing_args():
variable_unnamed = Variable()
variable_named = Variable(name="x")
expr = variable_unnamed + variable_named
- with pytest.raises(ValueError, match="No positional argument provided for unnamed variable."):
+ with pytest.raises(ValueError, match=r"No positional argument provided for unnamed variable."):
expr(x=3)
- with pytest.raises(ValueError, match="Variable 'x' not provided."):
+ with pytest.raises(ValueError, match=r"Variable 'x' not provided."):
expr(5)
@@ -99,14 +99,14 @@ def test_variable_multiple_positional_args():
variable1 = Variable()
variable2 = Variable()
expr = variable1 + variable2
- with pytest.raises(ValueError, match="Multiple positional arguments"):
+ with pytest.raises(ValueError, match=r"Multiple positional arguments"):
expr(5, 3)
def test_single_unnamed_variable_multiple_args():
variable = Variable()
expr = variable * 2
- with pytest.raises(ValueError, match="Multiple positional arguments"):
+ with pytest.raises(ValueError, match=r"Multiple positional arguments"):
expr(5, 3)
diff --git a/tests/test_plugins/smatrix/terminal_component_modeler_def.py b/tests/test_plugins/smatrix/terminal_component_modeler_def.py
index be118f036b..a95850a09b 100644
--- a/tests/test_plugins/smatrix/terminal_component_modeler_def.py
+++ b/tests/test_plugins/smatrix/terminal_component_modeler_def.py
@@ -1,7 +1,5 @@
from __future__ import annotations
-from typing import Optional, Union
-
import numpy as np
import tidy3d as td
@@ -36,9 +34,7 @@
Router = 1.0 * mm
-def make_simulation(
- planar_pec: bool, length: Optional[float] = None, grid_spec: td.GridSpec = None
-):
+def make_simulation(planar_pec: bool, length: float | None = None, grid_spec: td.GridSpec = None):
if length:
strip_length = length
else:
@@ -114,7 +110,7 @@ def make_simulation(
def make_component_modeler(
planar_pec: bool,
reference_impedance: complex = 50,
- length: Optional[float] = None,
+ length: float | None = None,
port_refinement: bool = True,
port_snapping: bool = True,
grid_spec: td.GridSpec = None,
@@ -172,7 +168,7 @@ def make_component_modeler(
return modeler
-def make_coaxial_simulation(length: Optional[float] = None, grid_spec: td.GridSpec = None):
+def make_coaxial_simulation(length: float | None = None, grid_spec: td.GridSpec = None):
if not length:
length = default_strip_length
@@ -249,10 +245,10 @@ def make_coaxial_simulation(length: Optional[float] = None, grid_spec: td.GridSp
def make_coaxial_component_modeler(
reference_impedance: complex = 50,
- length: Optional[float] = None,
+ length: float | None = None,
port_refinement: bool = True,
grid_spec: td.GridSpec = None,
- port_types: tuple[Union[CoaxialLumpedPort, WavePort], Union[CoaxialLumpedPort, WavePort]] = (
+ port_types: tuple[CoaxialLumpedPort | WavePort, CoaxialLumpedPort | WavePort] = (
CoaxialLumpedPort,
CoaxialLumpedPort,
),
@@ -265,7 +261,7 @@ def make_coaxial_component_modeler(
sim = make_coaxial_simulation(length=length, grid_spec=grid_spec)
- def make_port(center, direction, type, name) -> Union[CoaxialLumpedPort, WavePort]:
+ def make_port(center, direction, type, name) -> CoaxialLumpedPort | WavePort:
if type is CoaxialLumpedPort:
port_cells = None
if port_refinement:
diff --git a/tests/test_plugins/test_design.py b/tests/test_plugins/test_design.py
index b35ed5bb84..4b3990d1e5 100644
--- a/tests/test_plugins/test_design.py
+++ b/tests/test_plugins/test_design.py
@@ -3,7 +3,6 @@
from __future__ import annotations
import sys
-from typing import Optional
import matplotlib.pyplot as plt
import numpy as np
@@ -40,7 +39,7 @@
}
-def emulated_batch_run(simulations, path_dir: Optional[str] = None, **kwargs):
+def emulated_batch_run(simulations, path_dir: str | None = None, **kwargs):
data_dict = {task_name: run_emulated(sim) for task_name, sim in simulations.simulations.items()}
task_ids = dict(zip(simulations.simulations.keys(), data_dict.keys()))
task_paths = {key: f"/path/to/{key}" for key in simulations.simulations.keys()}
diff --git a/tests/test_plugins/test_dispersion_fitter.py b/tests/test_plugins/test_dispersion_fitter.py
index fbdf541c4e..f470c13c77 100644
--- a/tests/test_plugins/test_dispersion_fitter.py
+++ b/tests/test_plugins/test_dispersion_fitter.py
@@ -91,20 +91,20 @@ def test_lossless_dispersion(random_data, mock_remote_api):
with pytest.raises(SetupError):
fitter = DispersionFitter(wvl_um=[1.0], n_data=(1.0), wvl_range=(2, 3))
- medium, rms = fitter.fit(num_tries=2)
+ _medium, _rms = fitter.fit(num_tries=2)
wvl_um, n_data, _ = random_data
fitter = DispersionFitter(wvl_um=wvl_um.tolist(), n_data=tuple(n_data))
- medium, rms = fitter._fit_single()
- medium, rms = fitter.fit(num_tries=2)
- medium, rms = run_fitter(fitter)
+ _medium, _rms = fitter._fit_single()
+ _medium, _rms = fitter.fit(num_tries=2)
+ _medium, _rms = run_fitter(fitter)
fitter = FastDispersionFitter(wvl_um=wvl_um.tolist(), n_data=tuple(n_data))
- medium, rms = fitter.fit(advanced_param=advanced_param)
+ _medium, _rms = fitter.fit(advanced_param=advanced_param)
# from permittivity data
fitter = FastDispersionFitter.from_complex_permittivity(wvl_um, n_data**2)
- medium2, rms2 = fitter.fit(advanced_param=advanced_param)
+ _medium2, _rms2 = fitter.fit(advanced_param=advanced_param)
@responses.activate
@@ -112,25 +112,25 @@ def test_lossy_dispersion(random_data, mock_remote_api):
"""perform fitting on random lossy data"""
wvl_um, n_data, k_data = random_data
fitter = DispersionFitter(wvl_um=wvl_um, n_data=n_data, k_data=k_data)
- medium, rms = fitter._fit_single()
- medium, rms = fitter.fit(num_tries=2)
- medium, rms = run_fitter(fitter)
+ _medium, _rms = fitter._fit_single()
+ _medium, _rms = fitter.fit(num_tries=2)
+ _medium, _rms = run_fitter(fitter)
fitter = FastDispersionFitter(wvl_um=wvl_um.tolist(), n_data=n_data, k_data=k_data)
- medium, rms = fitter.fit(advanced_param=advanced_param)
+ _medium, _rms = fitter.fit(advanced_param=advanced_param)
# from permittivity data
eps_complex = (n_data + 1j * k_data) ** 2
fitter = FastDispersionFitter.from_complex_permittivity(
wvl_um, eps_complex.real, eps_complex.imag
)
- medium2, rms2 = fitter.fit(advanced_param=advanced_param)
+ _medium2, _rms2 = fitter.fit(advanced_param=advanced_param)
# from loss tangent
fitter = FastDispersionFitter.from_loss_tangent(
wvl_um, eps_complex.real, eps_complex.imag / eps_complex.real
)
- medium3, rms3 = fitter.fit(advanced_param=advanced_param)
+ _medium3, _rms3 = fitter.fit(advanced_param=advanced_param)
# test that poles can be close but not exactly equal to provided freqs
N = 2
@@ -139,7 +139,7 @@ def test_lossy_dispersion(random_data, mock_remote_api):
k_data = np.ones(N) * 0.5
fitter = FastDispersionFitter(wvl_um=wvl_um, n_data=n_data, k_data=k_data)
- medium, rms_error = fitter.fit(max_num_poles=2, tolerance_rms=1e-3)
+ _medium, _rms_error = fitter.fit(max_num_poles=2, tolerance_rms=1e-3)
def test_constant_loss_tangent():
@@ -193,25 +193,25 @@ def _test_nk(mock_data):
# n only
mock_data = [b"wl,n", b"1,2", b"2,2"]
fitter = _test_nk(mock_data)
- medium, rms = fitter.fit(num_tries=10)
+ _medium, _rms = fitter.fit(num_tries=10)
# n and k
mock_data = [b"wl,n", b"1,2", b"3,2.1", b"wl,k", b"1,0", b"3,0.1"]
fitter = _test_nk(mock_data)
- medium, rms = fitter.fit(num_tries=2)
+ _medium, _rms = fitter.fit(num_tries=2)
def test_dispersion_load_file():
"""loads dispersion model from nk data file"""
fitter = DispersionFitter.from_file("tests/data/nk_data.csv", skiprows=1, delimiter=",")
- medium, rms = fitter.fit(num_tries=2)
+ _medium, _rms = fitter.fit(num_tries=2)
fitter = DispersionFitter.from_file("tests/data/n_data.csv", skiprows=1, delimiter=",")
- medium, rms = fitter.fit(num_tries=20)
+ _medium, _rms = fitter.fit(num_tries=20)
fitter = FastDispersionFitter.from_file("tests/data/nk_data.csv", skiprows=1, delimiter=",")
- medium, rms = fitter.fit(advanced_param=advanced_param)
+ _medium, _rms = fitter.fit(advanced_param=advanced_param)
def test_dispersion_plot(random_data):
@@ -221,14 +221,14 @@ def test_dispersion_plot(random_data):
fitter = DispersionFitter(wvl_um=wvl_um, n_data=n_data)
fitter.plot(ax=AX)
plt.close()
- medium, rms = fitter.fit(num_tries=2)
+ medium, _rms = fitter.fit(num_tries=2)
fitter.plot(medium, ax=AX)
plt.close()
fitter = DispersionFitter(wvl_um=wvl_um, n_data=n_data, k_data=k_data)
fitter.plot()
plt.close()
- medium, rms = fitter.fit(num_tries=2)
+ medium, _rms = fitter.fit(num_tries=2)
fitter.plot(medium, ax=AX)
plt.close()
@@ -242,44 +242,44 @@ def test_dispersion_set_wvg_range(random_data):
wvl_range = [1.2, 1.8]
fitter = fitter.copy(update={"wvl_range": wvl_range})
assert len(fitter.freqs) == 7
- medium, rms = fitter.fit(num_tries=2)
+ _medium, _rms = fitter.fit(num_tries=2)
fastfitter = fastfitter.copy(update={"wvl_range": wvl_range})
assert len(fastfitter.freqs) == 7
- medium, rms = fastfitter.fit(advanced_param=advanced_param)
+ _medium, _rms = fastfitter.fit(advanced_param=advanced_param)
wvl_range = [1.2, 2.8]
fitter = fitter.copy(update={"wvl_range": wvl_range, "k_data": k_data})
assert len(fitter.freqs) == 9
- medium, rms = fitter.fit(num_tries=2)
+ _medium, _rms = fitter.fit(num_tries=2)
fastfitter = fastfitter.copy(update={"wvl_range": wvl_range})
assert len(fastfitter.freqs) == 9
- medium, rms = fastfitter.fit(advanced_param=advanced_param)
+ _medium, _rms = fastfitter.fit(advanced_param=advanced_param)
wvl_range = [0.2, 1.8]
fitter = fitter.copy(update={"wvl_range": wvl_range})
assert len(fitter.freqs) == 9
- medium, rms = fitter.fit(num_tries=2)
+ _medium, _rms = fitter.fit(num_tries=2)
fastfitter = fastfitter.copy(update={"wvl_range": wvl_range})
assert len(fastfitter.freqs) == 9
- medium, rms = fastfitter.fit(advanced_param=advanced_param)
+ _medium, _rms = fastfitter.fit(advanced_param=advanced_param)
wvl_range = [0.2, 2.8]
fitter = fitter.copy(update={"wvl_range": wvl_range, "k_data": k_data})
assert len(fitter.freqs) == 11
- medium, rms = fitter.fit(num_tries=2)
+ _medium, _rms = fitter.fit(num_tries=2)
fastfitter = fastfitter.copy(update={"wvl_range": wvl_range})
assert len(fastfitter.freqs) == 11
- medium, rms = fastfitter.fit(advanced_param=advanced_param)
+ _medium, _rms = fastfitter.fit(advanced_param=advanced_param)
def test_dispersion_guess(random_data):
"""plots a medium fit from file"""
- wvl_um, n_data, k_data = random_data
+ wvl_um, n_data, _k_data = random_data
fitter = DispersionFitter(wvl_um=wvl_um, n_data=n_data)
- medium, rms = fitter.fit(num_tries=2)
+ medium, _rms = fitter.fit(num_tries=2)
- medium_new, rms_new = fitter.fit(num_tries=1, guess=medium)
+ _medium_new, _rms_new = fitter.fit(num_tries=1, guess=medium)
def test_dispersion_loss_samples():
diff --git a/tests/test_plugins/test_polyslab.py b/tests/test_plugins/test_polyslab.py
index 267020d36b..bcb399fcae 100644
--- a/tests/test_plugins/test_polyslab.py
+++ b/tests/test_plugins/test_polyslab.py
@@ -175,7 +175,7 @@ def make_coupler(
wg_height = 0.3
dilation = 0.02
- [substrate_geo] = ComplexPolySlab.from_gds(
+ [_substrate_geo] = ComplexPolySlab.from_gds(
coup_cell_loaded,
gds_layer=0,
gds_dtype=0,
diff --git a/tests/test_web/test_s3utils.py b/tests/test_web/test_s3utils.py
index 175e860b6d..3b8c016c49 100644
--- a/tests/test_web/test_s3utils.py
+++ b/tests/test_web/test_s3utils.py
@@ -75,7 +75,7 @@ def simulate_download_success(Bucket, Key, Filename, Callback, Config, **kwargs)
# Check that mock_s3_client.download_file() was invoked with the correct arguments.
mock_s3_client.download_file.assert_called_once()
- call_args, call_kwargs = mock_s3_client.download_file.call_args
+ _call_args, call_kwargs = mock_s3_client.download_file.call_args
assert call_kwargs["Bucket"] == "test-bucket"
assert call_kwargs["Key"] == "test-key"
assert call_kwargs["Filename"].endswith(s3utils.IN_TRANSIT_SUFFIX)
@@ -112,7 +112,7 @@ def simulate_download_failure(Bucket, Key, Filename, Callback, Config, **kwargs)
# Check that mock_s3_client.download_file() was invoked with the correct arguments.
mock_s3_client.download_file.assert_called_once()
- call_args, call_kwargs = mock_s3_client.download_file.call_args
+ _call_args, call_kwargs = mock_s3_client.download_file.call_args
assert call_kwargs["Bucket"] == "test-bucket"
assert call_kwargs["Key"] == "test-key"
assert call_kwargs["Filename"].endswith(s3utils.IN_TRANSIT_SUFFIX)
diff --git a/tests/test_web/test_webapi.py b/tests/test_web/test_webapi.py
index 3d4c7ce949..2d08b0ec5d 100644
--- a/tests/test_web/test_webapi.py
+++ b/tests/test_web/test_webapi.py
@@ -365,7 +365,7 @@ def test_start_with_valid_priority(mock_start, priority):
@pytest.mark.parametrize("priority", [0, -1, 11, 15])
def test_start_with_invalid_priority(mock_start, priority):
"""Test start with invalid priority values."""
- with pytest.raises(ValueError, match="Priority must be between '1' and '10' if specified."):
+ with pytest.raises(ValueError, match=r"Priority must be between '1' and '10' if specified."):
start(TASK_ID, priority=priority)
@@ -383,7 +383,7 @@ def test_run_with_valid_priority(mock_webapi, monkeypatch, priority):
def test_run_with_invalid_priority(mock_webapi, priority):
"""Test run with invalid priority values."""
sim = make_sim()
- with pytest.raises(ValueError, match="Priority must be between '1' and '10' if specified."):
+ with pytest.raises(ValueError, match=r"Priority must be between '1' and '10' if specified."):
run(sim, TASK_NAME, folder_name=PROJECT_NAME, priority=priority)
@@ -718,7 +718,7 @@ def mock_start_interrupt(self, *args, **kwargs):
monkeypatch.setattr(Batch, "start", mock_start_interrupt)
# run should save the batch file after upload, even if interrupted
- with pytest.raises(RuntimeError, match="Simulated interruption"):
+ with pytest.raises(RuntimeError, match=r"Simulated interruption"):
batch.run(path_dir=str(tmp_path))
@@ -897,7 +897,7 @@ def test_load_invalid_task_raises(mock_webapi):
json={"error": "Task not found"},
status=404,
)
- with pytest.raises(WebNotFoundError, match="Resource not found"):
+ with pytest.raises(WebNotFoundError, match=r"Resource not found"):
load(INVALID_TASK_ID, replace_existing=True)
diff --git a/tests/test_web/test_webapi_extra.py b/tests/test_web/test_webapi_extra.py
index 5e16ef42e3..85ef286284 100644
--- a/tests/test_web/test_webapi_extra.py
+++ b/tests/test_web/test_webapi_extra.py
@@ -14,7 +14,7 @@ def test_get_info_not_found(monkeypatch):
monkeypatch.setattr(
"tidy3d.web.core.task_core.SimulationTask.get", lambda *args, **kwargs: None
)
- with pytest.raises(ValueError, match="Task not found."):
+ with pytest.raises(ValueError, match=r"Task not found."):
get_info("non_existent_task_id")
@@ -24,13 +24,13 @@ def test_start_not_found(monkeypatch):
monkeypatch.setattr(
"tidy3d.web.core.task_core.SimulationTask.get", lambda *args, **kwargs: None
)
- with pytest.raises(ValueError, match="Task not found."):
+ with pytest.raises(ValueError, match=r"Task not found."):
start("non_existent_task_id")
def test_delete_not_found():
"""Tests that delete raises a ValueError when the task id is not found."""
- with pytest.raises(ValueError, match="Task id not found."):
+ with pytest.raises(ValueError, match=r"Task id not found."):
delete("")
diff --git a/tests/utils.py b/tests/utils.py
index 4f67ddf5c0..bba08c7208 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -2,7 +2,7 @@
import dataclasses
from pathlib import Path
-from typing import Any, Optional, Union
+from typing import Any
import numpy as np
import pydantic.v1 as pd
@@ -57,9 +57,9 @@ def cartesian_to_unstructured(
array: td.SpatialDataArray,
pert: float = 0.1,
method: str = "linear",
- seed: Optional[int] = None,
+ seed: int | None = None,
same_bounds: bool = True,
-) -> Union[td.TriangularGridDataset, td.TetrahedralGridDataset]:
+) -> td.TriangularGridDataset | td.TetrahedralGridDataset:
"""Convert a SpatialDataArray into TriangularGridDataset/TetrahedralGridDataset with
an optional perturbation of point coordinates.
@@ -1532,7 +1532,7 @@ def run_async_emulated(simulations: dict[str, td.Simulation], **kwargs) -> Batch
def assert_log_level(
- records: list[tuple[int, str]], log_level_expected: str, contains_str: Optional[str] = None
+ records: list[tuple[int, str]], log_level_expected: str, contains_str: str | None = None
) -> None:
"""Testing tool: Raises error if a log was not recorded as expected.
@@ -1600,8 +1600,8 @@ def assert_log_level(
def assert_str_in_log(
records: list[tuple[int, str]],
log_level_test: str,
- excludes_str: Optional[str] = None,
- contains_str: Optional[str] = None,
+ excludes_str: str | None = None,
+ contains_str: str | None = None,
) -> None:
"""Testing tool: Raises error if `excludes_str` appears , or `contains_str` doesn't appear at the test log level.
Unlike ``assert_log_level``, we don't raise error if the ``log_level_test`` is not present in the records.
@@ -1664,7 +1664,7 @@ def handle(self, level, level_name, message):
class AbstractAssertLog:
"""Context manager to check logs."""
- log_level_expected: Union[str, None]
+ log_level_expected: str | None
contains_str: str = None
@property
diff --git a/tidy3d/components/autograd/boxes.py b/tidy3d/components/autograd/boxes.py
index 78aa52289a..a7a5392d44 100644
--- a/tidy3d/components/autograd/boxes.py
+++ b/tidy3d/components/autograd/boxes.py
@@ -3,7 +3,8 @@
from __future__ import annotations
import importlib
-from typing import Any, Callable
+from collections.abc import Callable
+from typing import Any
import autograd.numpy as anp
from autograd.extend import VJPNode, defjvp, register_notrace
diff --git a/tidy3d/components/autograd/derivative_utils.py b/tidy3d/components/autograd/derivative_utils.py
index fba1b54ba4..1ff1fe1711 100644
--- a/tidy3d/components/autograd/derivative_utils.py
+++ b/tidy3d/components/autograd/derivative_utils.py
@@ -2,8 +2,8 @@
from __future__ import annotations
+from collections.abc import Callable
from dataclasses import dataclass, field, replace
-from typing import Callable, Optional, Union
import numpy as np
import xarray as xr
@@ -19,7 +19,7 @@
FieldData = dict[str, ScalarFieldDataArray]
PermittivityData = dict[str, ScalarFieldDataArray]
-EpsType = Union[tidycomplex, FreqDataArray]
+EpsType = tidycomplex | FreqDataArray
class LazyInterpolator:
@@ -115,36 +115,36 @@ class DerivativeInfo:
frequencies: ArrayLike
"""Frequencies at which the adjoint gradient should be computed."""
- H_der_map: Optional[FieldData] = None
+ H_der_map: FieldData | None = None
"""Magnetic field gradient map.
Dataset where the field components ("Hx", "Hy", "Hz") store the multiplication
of the forward and adjoint magnetic fields. The tangential component of this
dataset is used when computing adjoint gradients for shifting boundaries of
structures composed of PEC mediums."""
- H_fwd: Optional[FieldData] = None
+ H_fwd: FieldData | None = None
"""Forward magnetic fields.
Dataset where the field components ("Hx", "Hy", "Hz") represent the forward
magnetic fields used for computing gradients for a given structure."""
- H_adj: Optional[FieldData] = None
+ H_adj: FieldData | None = None
"""Adjoint magnetic fields.
Dataset where the field components ("Hx", "Hy", "Hz") represent the adjoint
magnetic fields used for computing gradients for a given structure."""
# Optional fields with defaults
- eps_background: Optional[EpsType] = None
+ eps_background: EpsType | None = None
"""Permittivity in background.
Permittivity outside of the Structure as manually specified by
Structure.background_medium."""
- eps_no_structure: Optional[ScalarFieldDataArray] = None
+ eps_no_structure: ScalarFieldDataArray | None = None
"""Permittivity without structure.
The permittivity of the original simulation without the structure that is
being differentiated with respect to. Used to approximate permittivity
outside of the structure for shape optimization."""
- eps_inf_structure: Optional[ScalarFieldDataArray] = None
+ eps_inf_structure: ScalarFieldDataArray | None = None
"""Permittivity with infinite structure.
The permittivity of the original simulation where the structure being
differentiated with respect to is infinitely large. Used to approximate
@@ -162,7 +162,7 @@ class DerivativeInfo:
If True, the structure contains a PEC material which changes the gradient
formulation at the boundary compared to the dielectric case."""
- interpolators: Optional[dict] = None
+ interpolators: dict | None = None
"""Pre-computed interpolators.
Optional pre-computed interpolators for field components and permittivity data.
When provided, avoids redundant interpolator creation for multiple geometries
@@ -216,7 +216,7 @@ def _evaluate_with_interpolators(
coords = coords.astype(float_dtype, copy=False)
return {name: interp(coords) for name, interp in interpolators.items()}
- def create_interpolators(self, dtype: Optional[np.dtype] = None) -> dict:
+ def create_interpolators(self, dtype: np.dtype | None = None) -> dict:
"""Create interpolators for field components and permittivity data.
Creates and caches ``RegularGridInterpolator`` objects for all field components
@@ -342,7 +342,7 @@ def evaluate_gradient_at_points(
normals: np.ndarray,
perps1: np.ndarray,
perps2: np.ndarray,
- interpolators: Optional[dict] = None,
+ interpolators: dict | None = None,
) -> np.ndarray:
"""Compute adjoint gradients at surface points for shape optimization.
@@ -716,8 +716,8 @@ def _project_in_basis(
def adaptive_vjp_spacing(
self,
- wl_fraction: Optional[float] = None,
- min_allowed_spacing_fraction: Optional[float] = None,
+ wl_fraction: float | None = None,
+ min_allowed_spacing_fraction: float | None = None,
) -> float:
"""Compute adaptive spacing for finite-difference gradient evaluation.
diff --git a/tidy3d/components/autograd/field_map.py b/tidy3d/components/autograd/field_map.py
index 101e0b56bd..ab6341375b 100644
--- a/tidy3d/components/autograd/field_map.py
+++ b/tidy3d/components/autograd/field_map.py
@@ -3,7 +3,8 @@
from __future__ import annotations
import json
-from typing import Any, Callable
+from collections.abc import Callable
+from typing import Any
import pydantic.v1 as pydantic
diff --git a/tidy3d/components/autograd/types.py b/tidy3d/components/autograd/types.py
index bb41935695..f01629a99c 100644
--- a/tidy3d/components/autograd/types.py
+++ b/tidy3d/components/autograd/types.py
@@ -26,20 +26,20 @@
Box.__deepcopy__ = lambda v, memo: _deepcopy(v, memo)
# Types for floats, or collections of floats that can also be autograd tracers
-TracedFloat = typing.Union[float, Box]
-TracedPositiveFloat = typing.Union[pd.PositiveFloat, Box]
-TracedSize1D = typing.Union[Size1D, Box]
-TracedSize = typing.Union[tuple[TracedSize1D, TracedSize1D, TracedSize1D], Box]
-TracedCoordinate = typing.Union[tuple[TracedFloat, TracedFloat, TracedFloat], Box]
-TracedVertices = typing.Union[ArrayFloat2D, Box]
+TracedFloat = float | Box
+TracedPositiveFloat = pd.PositiveFloat | Box
+TracedSize1D = Size1D | Box
+TracedSize = tuple[TracedSize1D, TracedSize1D, TracedSize1D] | Box
+TracedCoordinate = tuple[TracedFloat, TracedFloat, TracedFloat] | Box
+TracedVertices = ArrayFloat2D | Box
# poles
-TracedComplex = typing.Union[Complex, Box]
+TracedComplex = Complex | Box
TracedPoleAndResidue = tuple[TracedComplex, TracedComplex]
# The data type that we pass in and out of the web.run() @autograd.primitive
-AutogradTraced = typing.Union[Box, ArrayLike]
-PathType = tuple[typing.Union[int, str], ...]
+AutogradTraced = Box | ArrayLike
+PathType = tuple[int | str, ...]
AutogradFieldMap = dict_ag[PathType, AutogradTraced]
InterpolationType = typing.Literal["nearest", "linear"]
diff --git a/tidy3d/components/base.py b/tidy3d/components/base.py
index 5498cf1738..b567a1cc6c 100644
--- a/tidy3d/components/base.py
+++ b/tidy3d/components/base.py
@@ -9,9 +9,10 @@
import os
import pathlib
import tempfile
+from collections.abc import Callable
from functools import wraps
from math import ceil
-from typing import Any, Callable, Literal, Optional, Union
+from typing import Any, Literal
import h5py
import numpy as np
@@ -284,7 +285,7 @@ def copy(self, deep: bool = True, validate: bool = True, **kwargs) -> Self:
return new_copy
def updated_copy(
- self, path: Optional[str] = None, deep: bool = True, validate: bool = True, **kwargs
+ self, path: str | None = None, deep: bool = True, validate: bool = True, **kwargs
) -> Self:
"""Make copy of a component instance with ``**kwargs`` indicating updated field values.
@@ -365,9 +366,9 @@ def help(self, methods: bool = False) -> None:
def from_file(
cls,
fname: str,
- group_path: Optional[str] = None,
+ group_path: str | None = None,
lazy: bool = False,
- on_load: Optional[Callable] = None,
+ on_load: Callable | None = None,
**parse_obj_kwargs,
) -> Self:
"""Loads a :class:`Tidy3dBaseModel` from .yaml, .json, .hdf5, or .hdf5.gz file.
@@ -409,7 +410,7 @@ def from_file(
return obj
@classmethod
- def dict_from_file(cls, fname: str, group_path: Optional[str] = None) -> dict:
+ def dict_from_file(cls, fname: str, group_path: str | None = None) -> dict:
"""Loads a dictionary containing the model from a .yaml, .json, .hdf5, or .hdf5.gz file.
Parameters
@@ -668,7 +669,7 @@ def _json_string_from_hdf5(cls, fname: str) -> str:
@classmethod
def dict_from_hdf5(
- cls, fname: str, group_path: str = "", custom_decoders: Optional[list[Callable]] = None
+ cls, fname: str, group_path: str = "", custom_decoders: list[Callable] | None = None
) -> dict:
"""Loads a dictionary containing the model contents from a .hdf5 file.
@@ -746,7 +747,7 @@ def from_hdf5(
cls,
fname: str,
group_path: str = "",
- custom_decoders: Optional[list[Callable]] = None,
+ custom_decoders: list[Callable] | None = None,
**parse_obj_kwargs,
) -> Self:
"""Loads :class:`Tidy3dBaseModel` instance to .hdf5 file.
@@ -779,7 +780,7 @@ def from_hdf5(
def to_hdf5(
self,
fname: str,
- custom_encoders: Optional[list[Callable]] = None,
+ custom_encoders: list[Callable] | None = None,
) -> None:
"""Exports :class:`Tidy3dBaseModel` instance to .hdf5 file.
@@ -840,7 +841,7 @@ def add_data_to_file(data_dict: dict, group_path: str = "") -> None:
@classmethod
def dict_from_hdf5_gz(
- cls, fname: str, group_path: str = "", custom_decoders: Optional[list[Callable]] = None
+ cls, fname: str, group_path: str = "", custom_decoders: list[Callable] | None = None
) -> dict:
"""Loads a dictionary containing the model contents from a .hdf5.gz file.
@@ -881,7 +882,7 @@ def from_hdf5_gz(
cls,
fname: str,
group_path: str = "",
- custom_decoders: Optional[list[Callable]] = None,
+ custom_decoders: list[Callable] | None = None,
**parse_obj_kwargs,
) -> Self:
"""Loads :class:`Tidy3dBaseModel` instance to .hdf5.gz file.
@@ -911,7 +912,7 @@ def from_hdf5_gz(
)
return cls.parse_obj(model_dict, **parse_obj_kwargs)
- def to_hdf5_gz(self, fname: str, custom_encoders: Optional[list[Callable]] = None) -> None:
+ def to_hdf5_gz(self, fname: str, custom_encoders: list[Callable] | None = None) -> None:
"""Exports :class:`Tidy3dBaseModel` instance to .hdf5.gz file.
Parameters
@@ -1116,7 +1117,7 @@ def insert_value(x, path: tuple[str, ...], sub_dict: dict):
def _serialized_traced_field_keys(
self, field_mapping: AutogradFieldMap | None = None
- ) -> Optional[str]:
+ ) -> str | None:
"""Return a serialized, order-independent representation of traced field paths."""
if field_mapping is None:
@@ -1229,7 +1230,7 @@ def generate_docstring(cls) -> str:
doc += "\n"
cls.__doc__ = doc
- def get_submodels_by_hash(self) -> dict[int, list[Union[str, tuple[str, int]]]]:
+ def get_submodels_by_hash(self) -> dict[int, list[str | tuple[str, int]]]:
"""Return a dictionary of this object's sub-models indexed by their hash values."""
fields = {}
for key in self.__fields__:
@@ -1300,7 +1301,7 @@ def to_sci(value: float, exponent: int, precision: int) -> str:
def _make_lazy_proxy(
target_cls: type,
- on_load: Optional[Callable[[Any], None]] = None,
+ on_load: Callable[[Any], None] | None = None,
) -> type:
"""
Return a lazy-loading proxy subclass of ``target_cls``.
@@ -1322,7 +1323,7 @@ def _make_lazy_proxy(
class _LazyProxy(target_cls):
def __init__(
- self, fname: str, group_path: Optional[str], parse_obj_kwargs: Optional[dict[str, Any]]
+ self, fname: str, group_path: str | None, parse_obj_kwargs: dict[str, Any] | None
):
object.__setattr__(self, "_lazy_fname", fname)
object.__setattr__(self, "_lazy_group_path", group_path)
diff --git a/tidy3d/components/base_sim/data/sim_data.py b/tidy3d/components/base_sim/data/sim_data.py
index 86e752cc55..074bfebaff 100644
--- a/tidy3d/components/base_sim/data/sim_data.py
+++ b/tidy3d/components/base_sim/data/sim_data.py
@@ -3,7 +3,6 @@
from __future__ import annotations
from abc import ABC
-from typing import Union
import numpy as np
import pydantic.v1 as pd
@@ -86,7 +85,7 @@ def validate_no_ambiguity(cls, val, values):
@staticmethod
def _field_component_value(
- field_component: Union[xr.DataArray, UnstructuredGridDatasetType], val: FieldVal
+ field_component: xr.DataArray | UnstructuredGridDatasetType, val: FieldVal
) -> xr.DataArray:
"""return the desired value of a field component.
diff --git a/tidy3d/components/base_sim/simulation.py b/tidy3d/components/base_sim/simulation.py
index ea4aee26eb..31547e932e 100644
--- a/tidy3d/components/base_sim/simulation.py
+++ b/tidy3d/components/base_sim/simulation.py
@@ -3,7 +3,6 @@
from __future__ import annotations
from abc import ABC, abstractmethod
-from typing import Optional
import autograd.numpy as anp
import pydantic.v1 as pd
@@ -115,7 +114,7 @@ class AbstractSimulation(Box, ABC):
description="String specifying the front end version number.",
)
- plot_length_units: Optional[LengthUnit] = pd.Field(
+ plot_length_units: LengthUnit | None = pd.Field(
"μm",
title="Plot Units",
description="When set to a supported ``LengthUnit``, "
@@ -242,14 +241,14 @@ def simulation_structure(self) -> Structure:
@add_ax_if_none
def plot(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
- source_alpha: Optional[float] = None,
- monitor_alpha: Optional[float] = None,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
+ source_alpha: float | None = None,
+ monitor_alpha: float | None = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
fill_structures: bool = True,
**patch_kwargs,
) -> Ax:
@@ -307,12 +306,12 @@ def plot(
@add_ax_if_none
def plot_sources(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
- alpha: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
+ alpha: float | None = None,
ax: Ax = None,
) -> Ax:
"""Plot each of simulation's sources on a plane defined by one nonzero x,y,z coordinate.
@@ -355,12 +354,12 @@ def plot_sources(
@add_ax_if_none
def plot_monitors(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
- alpha: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
+ alpha: float | None = None,
ax: Ax = None,
) -> Ax:
"""Plot each of simulation's monitors on a plane defined by one nonzero x,y,z coordinate.
@@ -403,11 +402,11 @@ def plot_monitors(
@add_ax_if_none
def plot_symmetries(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
ax: Ax = None,
) -> Ax:
"""Plot each of simulation's symmetries on a plane defined by one nonzero x,y,z coordinate.
@@ -478,9 +477,9 @@ def _make_symmetry_box(self, sym_axis: Axis) -> Box:
@add_ax_if_none
def plot_boundaries(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
**kwargs,
) -> Ax:
@@ -512,12 +511,12 @@ def plot_boundaries(
@add_ax_if_none
def plot_structures(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
fill: bool = True,
) -> Ax:
"""Plot each of simulation's structures on a plane defined by one nonzero x,y,z coordinate.
@@ -556,16 +555,16 @@ def plot_structures(
@add_ax_if_none
def plot_structures_eps(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
- freq: Optional[float] = None,
- alpha: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
+ freq: float | None = None,
+ alpha: float | None = None,
cbar: bool = True,
reverse: bool = False,
ax: Ax = None,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
) -> Ax:
"""Plot each of simulation's structures on a plane defined by one nonzero x,y,z coordinate.
The permittivity is plotted in grayscale based on its value at the specified frequency.
@@ -623,15 +622,15 @@ def plot_structures_eps(
@add_ax_if_none
def plot_structures_heat_conductivity(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
- alpha: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
+ alpha: float | None = None,
cbar: bool = True,
reverse: bool = False,
ax: Ax = None,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
) -> Ax:
"""Plot each of simulation's structures on a plane defined by one nonzero x,y,z coordinate.
The permittivity is plotted in grayscale based on its value at the specified frequency.
diff --git a/tidy3d/components/bc_placement.py b/tidy3d/components/bc_placement.py
index 1f48a94fbf..1ae86a560d 100644
--- a/tidy3d/components/bc_placement.py
+++ b/tidy3d/components/bc_placement.py
@@ -3,7 +3,6 @@
from __future__ import annotations
from abc import ABC
-from typing import Union
import pydantic.v1 as pd
@@ -110,10 +109,10 @@ class StructureSimulationBoundary(AbstractBCPlacement):
)
-BCPlacementType = Union[
- StructureBoundary,
- StructureStructureInterface,
- MediumMediumInterface,
- SimulationBoundary,
- StructureSimulationBoundary,
-]
+BCPlacementType = (
+ StructureBoundary
+ | StructureStructureInterface
+ | MediumMediumInterface
+ | SimulationBoundary
+ | StructureSimulationBoundary
+)
diff --git a/tidy3d/components/beam.py b/tidy3d/components/beam.py
index ff42abf31b..0c97243e88 100644
--- a/tidy3d/components/beam.py
+++ b/tidy3d/components/beam.py
@@ -4,7 +4,7 @@
from __future__ import annotations
from abc import abstractmethod
-from typing import Literal, Optional, Union
+from typing import Literal
import autograd.numpy as np
import pydantic.v1 as pd
@@ -263,7 +263,7 @@ class PlaneWaveBeamProfile(BeamProfile):
See also :class:`.PlaneWave`.
"""
- angular_spec: Union[FixedInPlaneKSpec, FixedAngleSpec] = pd.Field(
+ angular_spec: FixedInPlaneKSpec | FixedAngleSpec = pd.Field(
FixedAngleSpec(),
title="Angular Dependence Specification",
description="Specification of plane wave propagation direction dependence on wavelength.",
@@ -278,7 +278,7 @@ class PlaneWaveBeamProfile(BeamProfile):
"switch between waves with fixed angle and fixed in-plane k.",
)
- angle_theta_frequency: Optional[float] = pd.Field(
+ angle_theta_frequency: float | None = pd.Field(
None,
title="Frequency at Which Angle Theta is Defined",
description="Frequency for which ``angle_theta`` is set. This only has an effect for "
diff --git a/tidy3d/components/boundary.py b/tidy3d/components/boundary.py
index 974f3ba8ec..f8d9347292 100644
--- a/tidy3d/components/boundary.py
+++ b/tidy3d/components/boundary.py
@@ -3,7 +3,6 @@
from __future__ import annotations
from abc import ABC
-from typing import Optional, Union
import numpy as np
import pydantic.v1 as pd
@@ -101,7 +100,7 @@ class ABCBoundary(AbstractABCBoundary):
See, for example, John B. Schneider, Understanding the Finite-Difference Time-Domain Method, Chapter 6.
"""
- permittivity: Optional[float] = pd.Field(
+ permittivity: float | None = pd.Field(
None,
title="Effective Permittivity",
description="Effective permittivity for determining propagation constant. "
@@ -110,7 +109,7 @@ class ABCBoundary(AbstractABCBoundary):
ge=1.0,
)
- conductivity: Optional[pd.NonNegativeFloat] = pd.Field(
+ conductivity: pd.NonNegativeFloat | None = pd.Field(
None,
title="Effective Conductivity",
description="Effective conductivity for determining propagation constant. "
@@ -263,7 +262,7 @@ class ModeABCBoundary(AbstractABCBoundary):
"``num_modes`` in the solver will be set to ``mode_index + 1``.",
)
- freq_spec: Optional[Union[pd.PositiveFloat, BroadbandModeABCSpec]] = pd.Field(
+ freq_spec: pd.PositiveFloat | BroadbandModeABCSpec | None = pd.Field(
None,
title="Absorption Frequency Specification",
description="Specifies the frequency at which field is absorbed. If ``None``, then the central frequency of the source is used. If ``BroadbandModeABCSpec``, then the field is absorbed over the specified frequency range.",
@@ -288,7 +287,7 @@ def is_plane(cls, val):
def from_source(
cls,
source: ModeSource,
- freq_spec: Optional[Union[pd.PositiveFloat, BroadbandModeABCSpec]] = None,
+ freq_spec: pd.PositiveFloat | BroadbandModeABCSpec | None = None,
) -> ModeABCBoundary:
"""Instantiate from a ``ModeSource``.
@@ -325,9 +324,9 @@ def from_source(
@classmethod
def from_monitor(
cls,
- monitor: Union[ModeMonitor, ModeSolverMonitor],
+ monitor: ModeMonitor | ModeSolverMonitor,
mode_index: pd.NonNegativeInt = 0,
- freq_spec: Optional[Union[pd.PositiveFloat, BroadbandModeABCSpec]] = None,
+ freq_spec: pd.PositiveFloat | BroadbandModeABCSpec | None = None,
) -> ModeABCBoundary:
"""Instantiate from a ``ModeMonitor`` or ``ModeSolverMonitor``.
@@ -379,7 +378,7 @@ class InternalAbsorber(Box):
"one can use the same `size` and `center` as for the source and simply set `shift` to 1.",
)
- boundary_spec: Union[ModeABCBoundary, ABCBoundary] = pd.Field(
+ boundary_spec: ModeABCBoundary | ABCBoundary = pd.Field(
...,
title="Boundary Specification",
description="Boundary specification for defining effective propagation index in the one-way wave equation.",
@@ -412,9 +411,9 @@ def plot_params(self) -> PlotParams:
def plot(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
**patch_kwargs,
) -> Ax:
@@ -449,7 +448,7 @@ def plot(
# """ Bloch boundary """
# sources from which Bloch boundary conditions can be defined
-BlochSourceType = Union[GaussianBeam, ModeSource, PlaneWave, TFSF]
+BlochSourceType = GaussianBeam | ModeSource | PlaneWave | TFSF
class BlochBoundary(BoundaryEdge):
@@ -895,24 +894,24 @@ class Absorber(AbsorberSpec):
# pml types allowed in simulation init
-PMLTypes = Union[PML, StablePML, Absorber, None]
+PMLTypes = PML | StablePML | Absorber | None
# """ boundary specification classes """
# types of boundaries that can be used in Simulation
-BoundaryEdgeType = Union[
- Periodic,
- PECBoundary,
- PMCBoundary,
- PML,
- StablePML,
- Absorber,
- BlochBoundary,
- ABCBoundary,
- ModeABCBoundary,
-]
+BoundaryEdgeType = (
+ Periodic
+ | PECBoundary
+ | PMCBoundary
+ | PML
+ | StablePML
+ | Absorber
+ | BlochBoundary
+ | ABCBoundary
+ | ModeABCBoundary
+)
class Boundary(Tidy3dBaseModel):
@@ -1096,8 +1095,8 @@ def pmc(cls):
@classmethod
def abc(
cls,
- permittivity: Optional[pd.PositiveFloat] = None,
- conductivity: Optional[pd.NonNegativeFloat] = None,
+ permittivity: pd.PositiveFloat | None = None,
+ conductivity: pd.NonNegativeFloat | None = None,
):
"""ABC boundary specification on both sides along a dimension.
@@ -1121,7 +1120,7 @@ def mode_abc(
plane: Box,
mode_spec: ModeSpecType = DEFAULT_MODE_SPEC_MODE_ABC,
mode_index: pd.NonNegativeInt = 0,
- freq_spec: Optional[Union[pd.PositiveFloat, BroadbandModeABCSpec]] = None,
+ freq_spec: pd.PositiveFloat | BroadbandModeABCSpec | None = None,
):
"""One-way wave equation mode ABC boundary specification on both sides along a dimension.
@@ -1161,7 +1160,7 @@ def mode_abc(
def mode_abc_from_source(
cls,
source: ModeSource,
- freq_spec: Optional[Union[pd.PositiveFloat, BroadbandModeABCSpec]] = None,
+ freq_spec: pd.PositiveFloat | BroadbandModeABCSpec | None = None,
):
"""One-way wave equation mode ABC boundary specification on both sides along a dimension constructed from a mode source.
@@ -1186,9 +1185,9 @@ def mode_abc_from_source(
@classmethod
def mode_abc_from_monitor(
cls,
- monitor: Union[ModeMonitor, ModeSolverMonitor],
+ monitor: ModeMonitor | ModeSolverMonitor,
mode_index: pd.NonNegativeInt = 0,
- freq_spec: Optional[Union[pd.PositiveFloat, BroadbandModeABCSpec]] = None,
+ freq_spec: pd.PositiveFloat | BroadbandModeABCSpec | None = None,
):
"""One-way wave equation mode ABC boundary specification on both sides along a dimension constructed from a mode monitor.
diff --git a/tidy3d/components/data/data_array.py b/tidy3d/components/data/data_array.py
index 74304a2283..1d6fc8d709 100644
--- a/tidy3d/components/data/data_array.py
+++ b/tidy3d/components/data/data_array.py
@@ -4,7 +4,7 @@
from abc import ABC
from collections.abc import Mapping
-from typing import Any, Optional, Union
+from typing import Any
import autograd.numpy as anp
import h5py
@@ -122,7 +122,7 @@ def assign_data_attrs(cls, val):
val.attrs[attr_name] = attr
return val
- def _interp_validator(self, field_name: Optional[str] = None) -> None:
+ def _interp_validator(self, field_name: str | None = None) -> None:
"""Ensure the data can be interpolated or selected by checking for duplicate coordinates.
NOTE
@@ -220,7 +220,7 @@ def is_uniform(self):
raw_data = self.data.ravel()
return np.allclose(raw_data, raw_data[0])
- def to_hdf5(self, fname: Union[str, h5py.File], group_path: str) -> None:
+ def to_hdf5(self, fname: str | h5py.File, group_path: str) -> None:
"""Save an xr.DataArray to the hdf5 file or file handle with a given path to the group."""
# file name passed
@@ -329,10 +329,10 @@ def interp(
def _ag_interp(
self,
- coords: Union[Mapping[Any, Any], None] = None,
+ coords: Mapping[Any, Any] | None = None,
method: InterpOptions = "linear",
assume_sorted: bool = False,
- kwargs: Union[Mapping[str, Any], None] = None,
+ kwargs: Mapping[str, Any] | None = None,
**coords_kwargs: Any,
) -> Self:
"""Autograd interp override when tracing over self.data.
@@ -1614,21 +1614,15 @@ def _make_impedance_data_array(result: DataArray) -> ImpedanceResultType:
]
DATA_ARRAY_MAP = {data_array.__name__: data_array for data_array in DATA_ARRAY_TYPES}
-IndexedDataArrayTypes = Union[
- IndexedDataArray,
- IndexedVoltageDataArray,
- IndexedTimeDataArray,
- IndexedFieldVoltageDataArray,
- PointDataArray,
-]
+IndexedDataArrayTypes = (
+ IndexedDataArray
+ | IndexedVoltageDataArray
+ | IndexedTimeDataArray
+ | IndexedFieldVoltageDataArray
+ | PointDataArray
+)
-IntegralResultType = Union[FreqDataArray, FreqModeDataArray, TimeDataArray]
-VoltageIntegralResultType = Union[
- VoltageFreqDataArray, VoltageFreqModeDataArray, VoltageTimeDataArray
-]
-CurrentIntegralResultType = Union[
- CurrentFreqDataArray, CurrentFreqModeDataArray, CurrentTimeDataArray
-]
-ImpedanceResultType = Union[
- ImpedanceFreqDataArray, ImpedanceFreqModeDataArray, ImpedanceTimeDataArray
-]
+IntegralResultType = FreqDataArray | FreqModeDataArray | TimeDataArray
+VoltageIntegralResultType = VoltageFreqDataArray | VoltageFreqModeDataArray | VoltageTimeDataArray
+CurrentIntegralResultType = CurrentFreqDataArray | CurrentFreqModeDataArray | CurrentTimeDataArray
+ImpedanceResultType = ImpedanceFreqDataArray | ImpedanceFreqModeDataArray | ImpedanceTimeDataArray
diff --git a/tidy3d/components/data/dataset.py b/tidy3d/components/data/dataset.py
index f36c0c9fe3..158a888ad8 100644
--- a/tidy3d/components/data/dataset.py
+++ b/tidy3d/components/data/dataset.py
@@ -3,7 +3,8 @@
from __future__ import annotations
from abc import ABC, abstractmethod
-from typing import Any, Callable, Optional, Union, get_args
+from collections.abc import Callable
+from typing import Any, get_args
import numpy as np
import pydantic.v1 as pd
@@ -148,45 +149,45 @@ def colocate(self, x=None, y=None, z=None) -> xr.Dataset:
return self.package_colocate_results(centered_fields)
-EMScalarFieldType = Union[
- ScalarFieldDataArray,
- ScalarFieldTimeDataArray,
- ScalarModeFieldDataArray,
- ScalarModeFieldCylindricalDataArray,
- EMEScalarModeFieldDataArray,
- EMEScalarFieldDataArray,
-]
+EMScalarFieldType = (
+ ScalarFieldDataArray
+ | ScalarFieldTimeDataArray
+ | ScalarModeFieldDataArray
+ | ScalarModeFieldCylindricalDataArray
+ | EMEScalarModeFieldDataArray
+ | EMEScalarFieldDataArray
+)
class ElectromagneticFieldDataset(AbstractFieldDataset, ABC):
"""Stores a collection of E and H fields with x, y, z components."""
- Ex: Optional[EMScalarFieldType] = pd.Field(
+ Ex: EMScalarFieldType | None = pd.Field(
None,
title="Ex",
description="Spatial distribution of the x-component of the electric field.",
)
- Ey: Optional[EMScalarFieldType] = pd.Field(
+ Ey: EMScalarFieldType | None = pd.Field(
None,
title="Ey",
description="Spatial distribution of the y-component of the electric field.",
)
- Ez: Optional[EMScalarFieldType] = pd.Field(
+ Ez: EMScalarFieldType | None = pd.Field(
None,
title="Ez",
description="Spatial distribution of the z-component of the electric field.",
)
- Hx: Optional[EMScalarFieldType] = pd.Field(
+ Hx: EMScalarFieldType | None = pd.Field(
None,
title="Hx",
description="Spatial distribution of the x-component of the magnetic field.",
)
- Hy: Optional[EMScalarFieldType] = pd.Field(
+ Hy: EMScalarFieldType | None = pd.Field(
None,
title="Hy",
description="Spatial distribution of the y-component of the magnetic field.",
)
- Hz: Optional[EMScalarFieldType] = pd.Field(
+ Hz: EMScalarFieldType | None = pd.Field(
None,
title="Hz",
description="Spatial distribution of the z-component of the magnetic field.",
@@ -238,32 +239,32 @@ class FieldDataset(ElectromagneticFieldDataset):
>>> data = FieldDataset(Ex=scalar_field, Hz=scalar_field)
"""
- Ex: Optional[ScalarFieldDataArray] = pd.Field(
+ Ex: ScalarFieldDataArray | None = pd.Field(
None,
title="Ex",
description="Spatial distribution of the x-component of the electric field.",
)
- Ey: Optional[ScalarFieldDataArray] = pd.Field(
+ Ey: ScalarFieldDataArray | None = pd.Field(
None,
title="Ey",
description="Spatial distribution of the y-component of the electric field.",
)
- Ez: Optional[ScalarFieldDataArray] = pd.Field(
+ Ez: ScalarFieldDataArray | None = pd.Field(
None,
title="Ez",
description="Spatial distribution of the z-component of the electric field.",
)
- Hx: Optional[ScalarFieldDataArray] = pd.Field(
+ Hx: ScalarFieldDataArray | None = pd.Field(
None,
title="Hx",
description="Spatial distribution of the x-component of the magnetic field.",
)
- Hy: Optional[ScalarFieldDataArray] = pd.Field(
+ Hy: ScalarFieldDataArray | None = pd.Field(
None,
title="Hy",
description="Spatial distribution of the y-component of the magnetic field.",
)
- Hz: Optional[ScalarFieldDataArray] = pd.Field(
+ Hz: ScalarFieldDataArray | None = pd.Field(
None,
title="Hz",
description="Spatial distribution of the z-component of the magnetic field.",
@@ -370,32 +371,32 @@ class FieldTimeDataset(ElectromagneticFieldDataset):
>>> data = FieldTimeDataset(Ex=scalar_field, Hz=scalar_field)
"""
- Ex: Optional[ScalarFieldTimeDataArray] = pd.Field(
+ Ex: ScalarFieldTimeDataArray | None = pd.Field(
None,
title="Ex",
description="Spatial distribution of the x-component of the electric field.",
)
- Ey: Optional[ScalarFieldTimeDataArray] = pd.Field(
+ Ey: ScalarFieldTimeDataArray | None = pd.Field(
None,
title="Ey",
description="Spatial distribution of the y-component of the electric field.",
)
- Ez: Optional[ScalarFieldTimeDataArray] = pd.Field(
+ Ez: ScalarFieldTimeDataArray | None = pd.Field(
None,
title="Ez",
description="Spatial distribution of the z-component of the electric field.",
)
- Hx: Optional[ScalarFieldTimeDataArray] = pd.Field(
+ Hx: ScalarFieldTimeDataArray | None = pd.Field(
None,
title="Hx",
description="Spatial distribution of the x-component of the magnetic field.",
)
- Hy: Optional[ScalarFieldTimeDataArray] = pd.Field(
+ Hy: ScalarFieldTimeDataArray | None = pd.Field(
None,
title="Hy",
description="Spatial distribution of the y-component of the magnetic field.",
)
- Hz: Optional[ScalarFieldTimeDataArray] = pd.Field(
+ Hz: ScalarFieldTimeDataArray | None = pd.Field(
None,
title="Hz",
description="Spatial distribution of the z-component of the magnetic field.",
@@ -413,19 +414,19 @@ def apply_phase(self, phase: float) -> AbstractFieldDataset:
class AuxFieldDataset(AbstractFieldDataset, ABC):
"""Stores a collection of aux fields with x, y, z components."""
- Nfx: Optional[EMScalarFieldType] = pd.Field(
+ Nfx: EMScalarFieldType | None = pd.Field(
None,
title="Nfx",
description="Spatial distribution of the free carrier density for "
"polarization in the x-direction.",
)
- Nfy: Optional[EMScalarFieldType] = pd.Field(
+ Nfy: EMScalarFieldType | None = pd.Field(
None,
title="Nfy",
description="Spatial distribution of the free carrier density for "
"polarization in the y-direction.",
)
- Nfz: Optional[EMScalarFieldType] = pd.Field(
+ Nfz: EMScalarFieldType | None = pd.Field(
None,
title="Nfz",
description="Spatial distribution of the free carrier density for "
@@ -472,19 +473,19 @@ class AuxFieldTimeDataset(AuxFieldDataset):
>>> data = AuxFieldTimeDataset(Nfx=scalar_field)
"""
- Nfx: Optional[ScalarFieldTimeDataArray] = pd.Field(
+ Nfx: ScalarFieldTimeDataArray | None = pd.Field(
None,
title="Nfx",
description="Spatial distribution of the free carrier density for polarization "
"in the x-direction.",
)
- Nfy: Optional[ScalarFieldTimeDataArray] = pd.Field(
+ Nfy: ScalarFieldTimeDataArray | None = pd.Field(
None,
title="Nfy",
description="Spatial distribution of the free carrier density for polarization "
"in the y-direction.",
)
- Nfz: Optional[ScalarFieldTimeDataArray] = pd.Field(
+ Nfz: ScalarFieldTimeDataArray | None = pd.Field(
None,
title="Nfz",
description="Spatial distribution of the free carrier density for polarization "
@@ -518,32 +519,32 @@ class ModeSolverDataset(ElectromagneticFieldDataset):
... )
"""
- Ex: Optional[ScalarModeFieldDataArray] = pd.Field(
+ Ex: ScalarModeFieldDataArray | None = pd.Field(
None,
title="Ex",
description="Spatial distribution of the x-component of the electric field of the mode.",
)
- Ey: Optional[ScalarModeFieldDataArray] = pd.Field(
+ Ey: ScalarModeFieldDataArray | None = pd.Field(
None,
title="Ey",
description="Spatial distribution of the y-component of the electric field of the mode.",
)
- Ez: Optional[ScalarModeFieldDataArray] = pd.Field(
+ Ez: ScalarModeFieldDataArray | None = pd.Field(
None,
title="Ez",
description="Spatial distribution of the z-component of the electric field of the mode.",
)
- Hx: Optional[ScalarModeFieldDataArray] = pd.Field(
+ Hx: ScalarModeFieldDataArray | None = pd.Field(
None,
title="Hx",
description="Spatial distribution of the x-component of the magnetic field of the mode.",
)
- Hy: Optional[ScalarModeFieldDataArray] = pd.Field(
+ Hy: ScalarModeFieldDataArray | None = pd.Field(
None,
title="Hy",
description="Spatial distribution of the y-component of the magnetic field of the mode.",
)
- Hz: Optional[ScalarModeFieldDataArray] = pd.Field(
+ Hz: ScalarModeFieldDataArray | None = pd.Field(
None,
title="Hz",
description="Spatial distribution of the z-component of the magnetic field of the mode.",
@@ -555,14 +556,14 @@ class ModeSolverDataset(ElectromagneticFieldDataset):
description="Complex-valued effective propagation constants associated with the mode.",
)
- n_group_raw: Optional[GroupIndexDataArray] = pd.Field(
+ n_group_raw: GroupIndexDataArray | None = pd.Field(
None,
alias="n_group", # This is for backwards compatibility only when loading old data
title="Group Index",
description="Index associated with group velocity of the mode.",
)
- dispersion_raw: Optional[ModeDispersionDataArray] = pd.Field(
+ dispersion_raw: ModeDispersionDataArray | None = pd.Field(
None,
title="Dispersion",
description="Dispersion parameter for the mode.",
diff --git a/tidy3d/components/data/monitor_data.py b/tidy3d/components/data/monitor_data.py
index b8f310561a..464dea3a95 100644
--- a/tidy3d/components/data/monitor_data.py
+++ b/tidy3d/components/data/monitor_data.py
@@ -5,8 +5,9 @@
import struct
import warnings
from abc import ABC
+from collections.abc import Callable
from math import isclose
-from typing import Any, Callable, Literal, Optional, Union, get_args
+from typing import Any, Literal, get_args
import autograd.numpy as np
import pydantic.v1 as pd
@@ -125,7 +126,7 @@ def normalize(self, source_spectrum_fn: Callable[[float], complex]) -> Dataset:
return self.copy()
def scale_fields_by_freq_array(
- self, freq_array: FreqDataArray, method: Optional[str] = None
+ self, freq_array: FreqDataArray, method: str | None = None
) -> MonitorData:
"""Scale fields in :class:`.MonitorData` by an array of values stored in a :class:`.FreqDataArray`.
@@ -173,7 +174,7 @@ def _make_adjoint_sources(self, dataset_names: list[str], fwidth: float) -> list
return []
@staticmethod
- def flip_direction(direction: Union[str, DataArray]) -> str:
+ def flip_direction(direction: str | DataArray) -> str:
"""Flip the direction of a string ``('+', '-') -> ('-', '+')``."""
if isinstance(direction, DataArray):
@@ -197,14 +198,14 @@ def get_amplitude(x) -> complex:
class AbstractFieldData(MonitorData, AbstractFieldDataset, ABC):
"""Collection of scalar fields with some symmetry properties."""
- monitor: Union[
- FieldMonitor,
- FieldTimeMonitor,
- AuxFieldTimeMonitor,
- PermittivityMonitor,
- ModeMonitor,
- MediumMonitor,
- ]
+ monitor: (
+ FieldMonitor
+ | FieldTimeMonitor
+ | AuxFieldTimeMonitor
+ | PermittivityMonitor
+ | ModeMonitor
+ | MediumMonitor
+ )
symmetry: tuple[Symmetry, Symmetry, Symmetry] = pd.Field(
(0, 0, 0),
@@ -403,13 +404,9 @@ def at_coords(self, coords: Coords) -> xr.Dataset:
class ElectromagneticFieldData(AbstractFieldData, ElectromagneticFieldDataset, ABC):
"""Collection of electromagnetic fields."""
- grid_primal_correction: Union[
- float,
- FreqDataArray,
- TimeDataArray,
- FreqModeDataArray,
- EMEFreqModeDataArray,
- ] = pd.Field(
+ grid_primal_correction: (
+ float | FreqDataArray | TimeDataArray | FreqModeDataArray | EMEFreqModeDataArray
+ ) = pd.Field(
1.0,
title="Field correction factor",
description="Correction factor that needs to be applied for data corresponding to a 2D "
@@ -417,13 +414,9 @@ class ElectromagneticFieldData(AbstractFieldData, ElectromagneticFieldDataset, A
"which the data was computed. The factor is applied to fields defined on the primal grid "
"locations along the normal direction.",
)
- grid_dual_correction: Union[
- float,
- FreqDataArray,
- TimeDataArray,
- FreqModeDataArray,
- EMEFreqModeDataArray,
- ] = pd.Field(
+ grid_dual_correction: (
+ float | FreqDataArray | TimeDataArray | FreqModeDataArray | EMEFreqModeDataArray
+ ) = pd.Field(
1.0,
title="Field correction factor",
description="Correction factor that needs to be applied for data corresponding to a 2D "
@@ -737,7 +730,7 @@ def mode_area(self) -> FreqModeDataArray:
return FreqModeDataArray(area)
def dot(
- self, field_data: Union[FieldData, ModeData, ModeSolverData], conjugate: bool = True
+ self, field_data: FieldData | ModeData | ModeSolverData, conjugate: bool = True
) -> ModeAmpsDataArray:
r"""Dot product (modal overlap) with another :class:`.FieldData` object. Both datasets have
to be frequency-domain data associated with a 2D monitor. Along the tangential directions,
@@ -829,7 +822,7 @@ def _interpolated_tangential_fields(self, coords: ArrayFloat2D) -> dict[str, Dat
return fields
def outer_dot(
- self, field_data: Union[FieldData, ModeData], conjugate: bool = True
+ self, field_data: FieldData | ModeData, conjugate: bool = True
) -> MixedModeDataArray:
r"""Dot product (modal overlap) with another :class:`.FieldData` object.
@@ -1084,10 +1077,10 @@ def to_zbf(
fname: str,
units: UnitsZBF = "mm",
background_refractive_index: float = 1,
- n_x: Optional[int] = None,
- n_y: Optional[int] = None,
- freq: Optional[float] = None,
- mode_index: Optional[int] = None,
+ n_x: int | None = None,
+ n_y: int | None = None,
+ freq: float | None = None,
+ mode_index: int | None = None,
r_x: float = 0,
r_y: float = 0,
z_x: float = 0,
@@ -1813,7 +1806,7 @@ def _assign_coords(self, **assign_coords_kwargs):
def _find_ordering_one_freq(
self,
data_to_sort: ModeData,
- overlap_thresh: Union[float, np.array],
+ overlap_thresh: float | np.array,
) -> tuple[Numpy, Numpy]:
"""Find new ordering of modes in data_to_sort based on their similarity to own modes."""
num_modes = self.n_complex.sizes["mode_index"]
@@ -2231,7 +2224,7 @@ def _apply_mode_reorder(self, sort_inds_2d):
return self.updated_copy(**modify_data)
def sort_modes(
- self, sort_spec: Optional[ModeSortSpec] = None, track_freq: Optional[TrackFreq] = None
+ self, sort_spec: ModeSortSpec | None = None, track_freq: TrackFreq | None = None
) -> ModeSolverData:
"""Sort modes per frequency according to ``sort_spec``.
@@ -2459,7 +2452,7 @@ class FluxData(MonitorData):
def _make_adjoint_sources(
self, dataset_names: list[str], fwidth: float
- ) -> list[Union[CustomCurrentSource, PointDipole]]:
+ ) -> list[CustomCurrentSource | PointDipole]:
"""Converts a :class:`.FieldData` to a list of adjoint current or point sources."""
# avoids error in edge case where there are extraneous flux monitors not used in objective
@@ -2512,20 +2505,20 @@ class FluxTimeData(MonitorData):
)
-ProjFieldType = Union[
- FieldProjectionAngleDataArray,
- FieldProjectionCartesianDataArray,
- FieldProjectionKSpaceDataArray,
- DiffractionDataArray,
-]
+ProjFieldType = (
+ FieldProjectionAngleDataArray
+ | FieldProjectionCartesianDataArray
+ | FieldProjectionKSpaceDataArray
+ | DiffractionDataArray
+)
-ProjMonitorType = Union[
- FieldProjectionAngleMonitor,
- FieldProjectionCartesianMonitor,
- FieldProjectionKSpaceMonitor,
- DiffractionMonitor,
- DirectivityMonitor,
-]
+ProjMonitorType = (
+ FieldProjectionAngleMonitor
+ | FieldProjectionCartesianMonitor
+ | FieldProjectionKSpaceMonitor
+ | DiffractionMonitor
+ | DirectivityMonitor
+)
class AbstractFieldProjectionData(MonitorData):
@@ -2691,7 +2684,7 @@ def eta(self) -> complex:
return ETA_0 / np.sqrt(eps_complex)
@staticmethod
- def propagation_factor(dist: Union[float, None], k: complex, is_2d_simulation: bool) -> complex:
+ def propagation_factor(dist: float | None, k: complex, is_2d_simulation: bool) -> complex:
"""A normalization factor that includes both phase and amplitude decay associated with propagation over a distance with a given wavenumber."""
if dist is None:
return 1.0
@@ -2799,7 +2792,7 @@ def radar_cross_section(self) -> DataArray:
def _make_adjoint_sources(
self, dataset_names: list[str], fwidth: float
- ) -> list[Union[CustomCurrentSource, PointDipole]]:
+ ) -> list[CustomCurrentSource | PointDipole]:
"""Error if server-side field projection is used for autograd"""
raise NotImplementedError(
@@ -3369,7 +3362,7 @@ class DiffractionData(AbstractFieldProjectionData):
units=MICROMETER,
)
- bloch_vecs: Union[tuple[float, float], tuple[ArrayFloat1D, ArrayFloat1D]] = pd.Field(
+ bloch_vecs: tuple[float, float] | tuple[ArrayFloat1D, ArrayFloat1D] = pd.Field(
...,
title="Bloch vectors",
description="Bloch vectors along the local x and y directions in units of "
@@ -3377,7 +3370,7 @@ class DiffractionData(AbstractFieldProjectionData):
)
@staticmethod
- def shifted_orders(orders: tuple[int, ...], bloch_vec: Union[float, np.ndarray]) -> np.ndarray:
+ def shifted_orders(orders: tuple[int, ...], bloch_vec: float | np.ndarray) -> np.ndarray:
"""Diffraction orders shifted by the Bloch vector."""
return bloch_vec + np.atleast_2d(orders).T
@@ -3385,7 +3378,7 @@ def shifted_orders(orders: tuple[int, ...], bloch_vec: Union[float, np.ndarray])
def reciprocal_coords(
orders: np.ndarray,
size: float,
- bloch_vec: Union[float, np.ndarray],
+ bloch_vec: float | np.ndarray,
f: float,
medium: MediumType,
) -> np.ndarray:
@@ -3760,7 +3753,7 @@ def _check_valid_pol_basis(pol_basis: PolarizationBasis, tilt_angle: float):
raise ValueError("'tilt_angle' is only defined for linear polarization.")
def partial_radiation_intensity(
- self, pol_basis: PolarizationBasis = "linear", tilt_angle: Optional[float] = None
+ self, pol_basis: PolarizationBasis = "linear", tilt_angle: float | None = None
) -> xr.Dataset:
"""Partial radiation intensity in the frequency domain as a function of angles theta and phi.
The partial radiation intensities are computed in the ``linear`` or ``circular`` polarization
@@ -3838,7 +3831,7 @@ def radiated_power(self) -> FreqDataArray:
return FreqDataArray(sign * self.flux.values, {"f": self.f})
def partial_directivity(
- self, pol_basis: PolarizationBasis = "linear", tilt_angle: Optional[float] = None
+ self, pol_basis: PolarizationBasis = "linear", tilt_angle: float | None = None
) -> xr.Dataset:
"""Directivity in the frequency domain as a function of angles theta and phi.
The partial directivities are computed in the ``linear`` or ``circular`` polarization
@@ -3907,7 +3900,7 @@ def calc_partial_gain(
self,
power_in: FreqDataArray,
pol_basis: PolarizationBasis = "linear",
- tilt_angle: Optional[float] = None,
+ tilt_angle: float | None = None,
) -> xr.Dataset:
"""The partial gain figures of merit for antennas. The partial gains are computed
in the ``linear`` or ``circular`` polarization bases. If ``tilt_angle`` is not ``None``,
diff --git a/tidy3d/components/data/sim_data.py b/tidy3d/components/data/sim_data.py
index 9c81bdbfae..9f47d6f395 100644
--- a/tidy3d/components/data/sim_data.py
+++ b/tidy3d/components/data/sim_data.py
@@ -7,7 +7,7 @@
import re
from abc import ABC
from collections import defaultdict
-from typing import Callable, Optional, Union
+from collections.abc import Callable
import h5py
import numpy as np
@@ -57,7 +57,7 @@ class AdjointSourceInfo(Tidy3dBaseModel):
description="Set of processed sources to include in the adjoint simulation.",
)
- post_norm: Union[float, FreqDataArray] = pd.Field(
+ post_norm: float | FreqDataArray = pd.Field(
...,
title="Post Normalization Values",
description="Factor to multiply the adjoint fields by after running "
@@ -428,7 +428,7 @@ def mnt_data_from_file(cls, fname: str, mnt_name: str, **parse_obj_kwargs) -> Mo
raise ValueError(f"No monitor with name '{mnt_name}' found in data file.")
@staticmethod
- def apply_phase(data: Union[xr.DataArray, xr.Dataset], phase: float = 0.0) -> xr.DataArray:
+ def apply_phase(data: xr.DataArray | xr.Dataset, phase: float = 0.0) -> xr.DataArray:
"""Apply a phase to xarray data."""
if phase != 0.0:
if np.any(np.iscomplex(data.values)):
@@ -449,8 +449,8 @@ def plot_field_monitor_data(
eps_alpha: float = 0.2,
phase: float = 0.0,
robust: bool = True,
- vmin: Optional[float] = None,
- vmax: Optional[float] = None,
+ vmin: float | None = None,
+ vmax: float | None = None,
ax: Ax = None,
shading: str = "flat",
**sel_kwargs,
@@ -665,8 +665,8 @@ def plot_field(
eps_alpha: float = 0.2,
phase: float = 0.0,
robust: bool = True,
- vmin: Optional[float] = None,
- vmax: Optional[float] = None,
+ vmin: float | None = None,
+ vmax: float | None = None,
ax: Ax = None,
shading: str = "flat",
**sel_kwargs,
@@ -743,11 +743,11 @@ def plot_scalar_array(
field_data: xr.DataArray,
axis: Axis,
position: float,
- freq: Optional[float] = None,
+ freq: float | None = None,
eps_alpha: float = 0.2,
robust: bool = True,
- vmin: Optional[float] = None,
- vmax: Optional[float] = None,
+ vmin: float | None = None,
+ vmax: float | None = None,
cmap_type: ColormapType = "divergent",
ax: Ax = None,
**kwargs,
diff --git a/tidy3d/components/data/unstructured/base.py b/tidy3d/components/data/unstructured/base.py
index b5866fd2d6..18d094bd87 100644
--- a/tidy3d/components/data/unstructured/base.py
+++ b/tidy3d/components/data/unstructured/base.py
@@ -4,7 +4,7 @@
import numbers
from abc import ABC, abstractmethod
-from typing import Literal, Optional, Union
+from typing import Literal
import numpy as np
import pydantic.v1 as pd
@@ -500,7 +500,7 @@ def _read_vtkLegacyFile(fname: str):
def _from_vtk_obj(
cls,
vtk_obj,
- field: Optional[str] = None,
+ field: str | None = None,
remove_degenerate_cells: bool = False,
remove_unused_points: bool = False,
values_type=IndexedDataArray,
@@ -533,7 +533,7 @@ def _from_vtk_obj_internal(
def from_vtu(
cls,
file: str,
- field: Optional[str] = None,
+ field: str | None = None,
remove_degenerate_cells: bool = False,
remove_unused_points: bool = False,
ignore_invalid_cells: bool = False,
@@ -572,7 +572,7 @@ def from_vtu(
def from_vtk(
cls,
file: str,
- field: Optional[str] = None,
+ field: str | None = None,
remove_degenerate_cells: bool = False,
remove_unused_points: bool = False,
ignore_invalid_cells: bool = False,
@@ -641,7 +641,7 @@ def _get_values_from_vtk(
cls,
vtk_obj,
num_points: pd.PositiveInt,
- field: Optional[str] = None,
+ field: str | None = None,
values_type=IndexedDataArray,
expect_complex=None,
) -> IndexedDataArray:
@@ -774,7 +774,7 @@ def _plane_slice_raw(self, axis: Axis, pos: float):
@abstractmethod
@requires_vtk
- def plane_slice(self, axis: Axis, pos: float) -> Union[XrDataArray, UnstructuredGridDataset]:
+ def plane_slice(self, axis: Axis, pos: float) -> XrDataArray | UnstructuredGridDataset:
"""Slice data with a plane and return the Tidy3D representation of the result
(``UnstructuredGridDataset``).
@@ -870,12 +870,12 @@ def reflect(
def interp(
self,
- x: Union[float, ArrayLike] = None,
- y: Union[float, ArrayLike] = None,
- z: Union[float, ArrayLike] = None,
- fill_value: Optional[
- Union[float, Literal["extrapolate"]]
- ] = None, # TODO: an array if multiple fields?
+ x: float | ArrayLike = None,
+ y: float | ArrayLike = None,
+ z: float | ArrayLike = None,
+ fill_value: float
+ | Literal["extrapolate"]
+ | None = None, # TODO: an array if multiple fields?
use_vtk: bool = False,
method: Literal["linear", "nearest"] = "linear",
max_samples_per_step: int = DEFAULT_MAX_SAMPLES_PER_STEP,
@@ -992,12 +992,12 @@ def _non_spatial_interp(self, method="linear", fill_value=np.nan, **coords_kwarg
def _spatial_interp(
self,
- x: Union[float, ArrayLike],
- y: Union[float, ArrayLike],
- z: Union[float, ArrayLike],
- fill_value: Optional[
- Union[float, Literal["extrapolate"]]
- ] = None, # TODO: an array if multiple fields?
+ x: float | ArrayLike,
+ y: float | ArrayLike,
+ z: float | ArrayLike,
+ fill_value: float
+ | Literal["extrapolate"]
+ | None = None, # TODO: an array if multiple fields?
use_vtk: bool = False,
method: Literal["linear", "nearest"] = "linear",
max_samples_per_step: int = DEFAULT_MAX_SAMPLES_PER_STEP,
@@ -1269,7 +1269,7 @@ def _interp_py_general(
max_samples_per_step: int,
max_cells_per_step: int,
rel_tol: float,
- axis_ignore: Union[Axis, None],
+ axis_ignore: Axis | None,
) -> ArrayLike:
"""A general function (2D and 3D) to interpolate data at provided x, y, and z using
vectorized python implementation.
@@ -1697,12 +1697,12 @@ def _interp_py_chunk(
@requires_vtk
def sel(
self,
- x: Union[float, ArrayLike] = None,
- y: Union[float, ArrayLike] = None,
- z: Union[float, ArrayLike] = None,
- method: Optional[Literal["None", "nearest", "pad", "ffill", "backfill", "bfill"]] = None,
+ x: float | ArrayLike = None,
+ y: float | ArrayLike = None,
+ z: float | ArrayLike = None,
+ method: Literal["None", "nearest", "pad", "ffill", "backfill", "bfill"] | None = None,
**sel_kwargs,
- ) -> Union[UnstructuredGridDataset, XrDataArray]:
+ ) -> UnstructuredGridDataset | XrDataArray:
"""Extract/interpolate data along one or more spatial or non-spatial directions. Must provide at least one argument
among 'x', 'y', 'z' or non-spatial dimensions through additional arguments. Along spatial dimensions a suitable slicing of
grid is applied (plane slice, line slice, or interpolation). Selection along non-spatial dimensions is forwarded to
diff --git a/tidy3d/components/data/unstructured/tetrahedral.py b/tidy3d/components/data/unstructured/tetrahedral.py
index 42ac4b58b1..17186db11a 100644
--- a/tidy3d/components/data/unstructured/tetrahedral.py
+++ b/tidy3d/components/data/unstructured/tetrahedral.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Union
-
import numpy as np
import pydantic.v1 as pd
from xarray import DataArray as XrDataArray
@@ -298,12 +296,12 @@ def _interp_py(
@requires_vtk
def sel(
self,
- x: Union[float, ArrayLike] = None,
- y: Union[float, ArrayLike] = None,
- z: Union[float, ArrayLike] = None,
+ x: float | ArrayLike = None,
+ y: float | ArrayLike = None,
+ z: float | ArrayLike = None,
method=None,
**sel_kwargs,
- ) -> Union[TriangularGridDataset, XrDataArray]:
+ ) -> TriangularGridDataset | XrDataArray:
"""Extract/interpolate data along one or more spatial or non-spatial directions. Must provide at least one argument
among 'x', 'y', 'z' or non-spatial dimensions through additional arguments. Along spatial dimensions a suitable slicing of
grid is applied (plane slice, line slice, or interpolation). Selection along non-spatial dimensions is forwarded to
diff --git a/tidy3d/components/data/unstructured/triangular.py b/tidy3d/components/data/unstructured/triangular.py
index 32d54f259b..6d68790796 100644
--- a/tidy3d/components/data/unstructured/triangular.py
+++ b/tidy3d/components/data/unstructured/triangular.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-from typing import Literal, Optional, Union
+from typing import Literal
import numpy as np
import pydantic.v1 as pd
@@ -311,10 +311,10 @@ def reflect(
def _spatial_interp(
self,
- x: Union[float, ArrayLike],
- y: Union[float, ArrayLike],
- z: Union[float, ArrayLike],
- fill_value: Optional[Union[float, Literal["extrapolate"]]] = None,
+ x: float | ArrayLike,
+ y: float | ArrayLike,
+ z: float | ArrayLike,
+ fill_value: float | Literal["extrapolate"] | None = None,
use_vtk: bool = False,
method: Literal["linear", "nearest"] = "linear",
ignore_normal_pos: bool = True,
@@ -450,10 +450,10 @@ def _interp_py(
@requires_vtk
def sel(
self,
- x: Union[float, ArrayLike] = None,
- y: Union[float, ArrayLike] = None,
- z: Union[float, ArrayLike] = None,
- method: Optional[Literal["None", "nearest", "pad", "ffill", "backfill", "bfill"]] = None,
+ x: float | ArrayLike = None,
+ y: float | ArrayLike = None,
+ z: float | ArrayLike = None,
+ method: Literal["None", "nearest", "pad", "ffill", "backfill", "bfill"] | None = None,
**sel_kwargs,
) -> XrDataArray:
"""Extract/interpolate data along one or more spatial or non-spatial directions. Must provide at least one argument
@@ -584,11 +584,11 @@ def plot(
grid: bool = True,
cbar: bool = True,
cmap: str = "viridis",
- vmin: Optional[float] = None,
- vmax: Optional[float] = None,
+ vmin: float | None = None,
+ vmax: float | None = None,
shading: Literal["gourand", "flat"] = "gouraud",
- cbar_kwargs: Optional[dict] = None,
- pcolor_kwargs: Optional[dict] = None,
+ cbar_kwargs: dict | None = None,
+ pcolor_kwargs: dict | None = None,
) -> Ax:
"""Plot the data field and/or the unstructured grid.
diff --git a/tidy3d/components/data/utils.py b/tidy3d/components/data/utils.py
index e2c96c0031..d3c32ebcee 100644
--- a/tidy3d/components/data/utils.py
+++ b/tidy3d/components/data/utils.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Union
-
import numpy as np
import xarray as xr
@@ -14,13 +12,13 @@
from .unstructured.tetrahedral import TetrahedralGridDataset
from .unstructured.triangular import TriangularGridDataset
-UnstructuredGridDatasetType = Union[TriangularGridDataset, TetrahedralGridDataset]
+UnstructuredGridDatasetType = TriangularGridDataset | TetrahedralGridDataset
-CustomSpatialDataType = Union[SpatialDataArray, UnstructuredGridDatasetType]
-CustomSpatialDataTypeAnnotated = Union[SpatialDataArray, annotate_type(UnstructuredGridDatasetType)]
+CustomSpatialDataType = SpatialDataArray | UnstructuredGridDatasetType
+CustomSpatialDataTypeAnnotated = SpatialDataArray | annotate_type(UnstructuredGridDatasetType)
-def _get_numpy_array(data_array: Union[ArrayLike, DataArray, UnstructuredGridDataset]) -> ArrayLike:
+def _get_numpy_array(data_array: ArrayLike | DataArray | UnstructuredGridDataset) -> ArrayLike:
"""Get numpy representation of dataarray/dataset values."""
if isinstance(data_array, UnstructuredGridDataset):
return data_array.values.values
@@ -30,8 +28,8 @@ def _get_numpy_array(data_array: Union[ArrayLike, DataArray, UnstructuredGridDat
def _zeros_like(
- data_array: Union[ArrayLike, xr.DataArray, UnstructuredGridDataset],
-) -> Union[ArrayLike, xr.DataArray, UnstructuredGridDataset]:
+ data_array: ArrayLike | xr.DataArray | UnstructuredGridDataset,
+) -> ArrayLike | xr.DataArray | UnstructuredGridDataset:
"""Get a zeroed replica of dataarray/dataset."""
if isinstance(data_array, UnstructuredGridDataset):
return data_array.updated_copy(values=xr.zeros_like(data_array.values))
@@ -41,8 +39,8 @@ def _zeros_like(
def _ones_like(
- data_array: Union[ArrayLike, xr.DataArray, UnstructuredGridDataset],
-) -> Union[ArrayLike, xr.DataArray, UnstructuredGridDataset]:
+ data_array: ArrayLike | xr.DataArray | UnstructuredGridDataset,
+) -> ArrayLike | xr.DataArray | UnstructuredGridDataset:
"""Get a unity replica of dataarray/dataset."""
if isinstance(data_array, UnstructuredGridDataset):
return data_array.updated_copy(values=xr.ones_like(data_array.values))
@@ -52,8 +50,8 @@ def _ones_like(
def _check_same_coordinates(
- a: Union[ArrayLike, xr.DataArray, UnstructuredGridDataset],
- b: Union[ArrayLike, xr.DataArray, UnstructuredGridDataset],
+ a: ArrayLike | xr.DataArray | UnstructuredGridDataset,
+ b: ArrayLike | xr.DataArray | UnstructuredGridDataset,
) -> bool:
"""Check whether two array are defined at the same coordinates."""
diff --git a/tidy3d/components/data/validators.py b/tidy3d/components/data/validators.py
index 4988c36044..875043aa48 100644
--- a/tidy3d/components/data/validators.py
+++ b/tidy3d/components/data/validators.py
@@ -1,8 +1,6 @@
# special validators for Datasets
from __future__ import annotations
-from typing import Optional
-
import numpy as np
import pydantic.v1 as pd
@@ -23,7 +21,7 @@ def no_nans(cls, val):
if val is None:
return val
- def error_if_has_nans(value, identifier: Optional[str] = None) -> None:
+ def error_if_has_nans(value, identifier: str | None = None) -> None:
"""Recursively check if value (or iterable) has nans and error if so."""
def has_nans(values) -> bool:
diff --git a/tidy3d/components/dispersion_fitter.py b/tidy3d/components/dispersion_fitter.py
index 8c1be3a827..527ee53ed3 100644
--- a/tidy3d/components/dispersion_fitter.py
+++ b/tidy3d/components/dispersion_fitter.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Optional
-
import numpy as np
from pydantic.v1 import Field, NonNegativeFloat, PositiveFloat, PositiveInt, validator
@@ -144,20 +142,20 @@ class AdvancedFastFitterParam(Tidy3dBaseModel):
description="Whether to show unweighted RMS error in addition to the default weighted "
'RMS error. Requires ``td.config.logging_level = "INFO"``.',
)
- relaxed: Optional[bool] = Field(
+ relaxed: bool | None = Field(
None,
title="Relaxed",
description="Whether to use relaxed fitting algorithm, which "
"has better pole relocation properties. If ``None``, will try both original and relaxed "
"algorithms.",
)
- smooth: Optional[bool] = Field(
+ smooth: bool | None = Field(
None,
title="Smooth",
description="Whether to use real starting poles, which can help when fitting smooth data. "
"If ``None``, will try both real and complex starting poles.",
)
- logspacing: Optional[bool] = Field(
+ logspacing: bool | None = Field(
None,
title="Log spacing",
description="Whether to space the poles logarithmically. "
@@ -223,14 +221,14 @@ class FastFitterData(AdvancedFastFitterParam):
title="eps_inf",
description="Value of ``eps_inf``.",
)
- poles: Optional[ArrayComplex1D] = Field(
+ poles: ArrayComplex1D | None = Field(
None, title="Pole frequencies in eV", description="Pole frequencies in eV"
)
- residues: Optional[ArrayComplex1D] = Field(
+ residues: ArrayComplex1D | None = Field(
None, title="Residues in eV", description="Residues in eV"
)
- passivity_optimized: Optional[bool] = Field(
+ passivity_optimized: bool | None = Field(
False,
title="Passivity optimized",
description="Whether the fit was optimized to enforce passivity. If None, "
@@ -758,7 +756,7 @@ def fit(
resp_data: ArrayComplex1D,
min_num_poles: PositiveInt = 1,
max_num_poles: PositiveInt = DEFAULT_MAX_POLES,
- resp_inf: Optional[float] = None,
+ resp_inf: float | None = None,
tolerance_rms: NonNegativeFloat = DEFAULT_TOLERANCE_RMS,
advanced_param: AdvancedFastFitterParam = None,
scale_factor: PositiveFloat = 1,
diff --git a/tidy3d/components/eme/data/monitor_data.py b/tidy3d/components/eme/data/monitor_data.py
index 558023c0e8..234a06963e 100644
--- a/tidy3d/components/eme/data/monitor_data.py
+++ b/tidy3d/components/eme/data/monitor_data.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Union
-
import pydantic.v1 as pd
from tidy3d.components.base_sim.data.monitor_data import AbstractMonitorData
@@ -50,11 +48,11 @@ class EMECoefficientData(AbstractMonitorData, EMECoefficientDataset):
)
-EMEMonitorDataType = Union[
- EMEModeSolverData,
- EMEFieldData,
- EMECoefficientData,
- ModeSolverData,
- PermittivityData,
- MediumData,
-]
+EMEMonitorDataType = (
+ EMEModeSolverData
+ | EMEFieldData
+ | EMECoefficientData
+ | ModeSolverData
+ | PermittivityData
+ | MediumData
+)
diff --git a/tidy3d/components/eme/data/sim_data.py b/tidy3d/components/eme/data/sim_data.py
index 03d16e65d9..1d6992ecbd 100644
--- a/tidy3d/components/eme/data/sim_data.py
+++ b/tidy3d/components/eme/data/sim_data.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-from typing import Literal, Optional, Union
+from typing import Literal
import numpy as np
import pydantic.v1 as pd
@@ -35,11 +35,11 @@ class EMESimulationData(AbstractYeeGridSimulationData):
"associated with the monitors of the original :class:`.EMESimulation`.",
)
- smatrix: Optional[EMESMatrixDataset] = pd.Field(
+ smatrix: EMESMatrixDataset | None = pd.Field(
None, title="S Matrix", description="Scattering matrix of the EME simulation."
)
- port_modes_raw: Optional[EMEModeSolverData] = pd.Field(
+ port_modes_raw: EMEModeSolverData | None = pd.Field(
None,
title="Port Modes",
description="Modes associated with the two ports of the EME device. "
@@ -57,7 +57,7 @@ def port_modes(self):
return self.port_modes_raw.symmetry_expanded_copy
def _extract_mode_solver_data(
- self, data: EMEModeSolverData, eme_cell_index: int, sweep_index: Optional[int] = None
+ self, data: EMEModeSolverData, eme_cell_index: int, sweep_index: int | None = None
) -> ModeSolverData:
"""Extract :class:`.ModeSolverData` at a given ``eme_cell_index``.
Assumes the :class:`.EMEModeSolverMonitor` spans the entire simulation and has
@@ -162,7 +162,7 @@ def port_modes_list_sweep(self) -> list[tuple[ModeSolverData, ModeSolverData]]:
return port_modes_list
def smatrix_in_basis(
- self, modes1: Union[FieldData, ModeData] = None, modes2: Union[FieldData, ModeData] = None
+ self, modes1: FieldData | ModeData = None, modes2: FieldData | ModeData = None
) -> EMESMatrixDataset:
"""Express the scattering matrix in the provided basis.
Change of basis is done by computing overlaps between provided modes and port modes.
@@ -366,7 +366,7 @@ def smatrix_in_basis(
def field_in_basis(
self,
field: EMEFieldData,
- modes: Union[FieldData, ModeData] = None,
+ modes: FieldData | ModeData = None,
port_index: Literal[0, 1] = 0,
) -> EMEFieldData:
"""Express the electromagnetic field in the provided basis.
diff --git a/tidy3d/components/eme/grid.py b/tidy3d/components/eme/grid.py
index d197c60677..be10d5e6a1 100644
--- a/tidy3d/components/eme/grid.py
+++ b/tidy3d/components/eme/grid.py
@@ -3,7 +3,7 @@
from __future__ import annotations
from abc import ABC, abstractmethod
-from typing import Literal, Optional, Union
+from typing import Literal
import numpy as np
import pydantic.v1 as pd
@@ -26,7 +26,7 @@
class EMEModeSpec(ModeSpec):
"""Mode spec for EME cells. Overrides some of the defaults and allowed values."""
- track_freq: Union[TrackFreq, None] = pd.Field(
+ track_freq: TrackFreq | None = pd.Field(
None,
title="Mode Tracking Frequency",
description="Parameter that turns on/off mode tracking based on their similarity. "
@@ -106,7 +106,7 @@ class EMEGridSpec(Tidy3dBaseModel, ABC):
"the EME solver to reuse the modes and cell interface scattering matrices.",
)
- name: Optional[str] = pd.Field(
+ name: str | None = pd.Field(
None, title="Name", description="Name of this 'EMEGridSpec'. Used in 'EMEPeriodicitySweep'."
)
@@ -378,9 +378,6 @@ def num_real_cells(self) -> int:
return len(self.mode_specs)
-EMESubgridType = Union[EMEUniformGrid, EMEExplicitGrid, "EMECompositeGrid"]
-
-
class EMECompositeGrid(EMEGridSpec):
"""EME grid made out of multiple subgrids.
@@ -397,7 +394,7 @@ class EMECompositeGrid(EMEGridSpec):
... )
"""
- subgrids: list[EMESubgridType] = pd.Field(
+ subgrids: list[EMEUniformGrid | EMEExplicitGrid | EMECompositeGrid] = pd.Field(
..., title="Subgrids", description="Subgrids in the composite grid."
)
@@ -536,8 +533,8 @@ def from_structure_groups(
structure_groups: list[list[Structure]],
axis: Axis,
mode_specs: list[EMEModeSpec],
- names: Optional[list[str]] = None,
- num_reps: Optional[list[pd.PositiveInt]] = None,
+ names: list[str] | None = None,
+ num_reps: list[pd.PositiveInt] | None = None,
) -> EMECompositeGrid:
"""Create a composite EME grid with boundaries aligned with
structure bounding boxes.
@@ -817,4 +814,4 @@ def cell_indices_in_box(self, box: Box) -> list[pd.NonNegativeInteger]:
return indices
-EMEGridSpecType = Union[EMEUniformGrid, EMECompositeGrid, EMEExplicitGrid]
+EMEGridSpecType = EMEUniformGrid | EMECompositeGrid | EMEExplicitGrid
diff --git a/tidy3d/components/eme/monitor.py b/tidy3d/components/eme/monitor.py
index bee336a3c7..6ce31e0d73 100644
--- a/tidy3d/components/eme/monitor.py
+++ b/tidy3d/components/eme/monitor.py
@@ -3,7 +3,7 @@
from __future__ import annotations
from abc import ABC, abstractmethod
-from typing import Literal, Optional, Union
+from typing import Literal
import pydantic.v1 as pd
@@ -22,7 +22,7 @@
class EMEMonitor(AbstractMonitor, ABC):
"""Abstract EME monitor."""
- freqs: Optional[FreqArray] = pd.Field(
+ freqs: FreqArray | None = pd.Field(
None,
title="Monitor Frequencies",
description="Frequencies at which the monitor will record. "
@@ -30,7 +30,7 @@ class EMEMonitor(AbstractMonitor, ABC):
"A value of 'None' will record at all simulation 'freqs'.",
)
- num_modes: Optional[pd.NonNegativeInt] = pd.Field(
+ num_modes: pd.NonNegativeInt | None = pd.Field(
None,
title="Number of Modes",
description="Maximum number of modes for the monitor to record. "
@@ -38,7 +38,7 @@ class EMEMonitor(AbstractMonitor, ABC):
"A value of 'None' will record all modes.",
)
- num_sweep: Optional[pd.NonNegativeInt] = pd.Field(
+ num_sweep: pd.NonNegativeInt | None = pd.Field(
1,
title="Number of Sweep Indices",
description="Number of sweep indices for the monitor to record. "
@@ -228,7 +228,7 @@ class EMEFieldMonitor(EMEMonitor, AbstractFieldMonitor):
"primal grid nodes). Default (False) is used internally in EME propagation.",
)
- num_modes: Optional[pd.NonNegativeInt] = pd.Field(
+ num_modes: pd.NonNegativeInt | None = pd.Field(
None,
title="Number of Modes",
description="Maximum number of modes for the monitor to record. "
@@ -302,11 +302,11 @@ def storage_size(
return bytes_single
-EMEMonitorType = Union[
- EMEModeSolverMonitor,
- EMEFieldMonitor,
- EMECoefficientMonitor,
- ModeSolverMonitor,
- PermittivityMonitor,
- MediumMonitor,
-]
+EMEMonitorType = (
+ EMEModeSolverMonitor
+ | EMEFieldMonitor
+ | EMECoefficientMonitor
+ | ModeSolverMonitor
+ | PermittivityMonitor
+ | MediumMonitor
+)
diff --git a/tidy3d/components/eme/simulation.py b/tidy3d/components/eme/simulation.py
index b01b928e4f..a8c603c75f 100644
--- a/tidy3d/components/eme/simulation.py
+++ b/tidy3d/components/eme/simulation.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-from typing import Literal, Optional, Union
+from typing import Literal
try:
import matplotlib as mpl
@@ -247,7 +247,7 @@ class EMESimulation(AbstractYeeGridSimulation):
"along the propagation axis.",
)
- sweep_spec: Optional[EMESweepSpecType] = pd.Field(
+ sweep_spec: EMESweepSpecType | None = pd.Field(
None,
title="EME Sweep Specification",
description="Specification for a parameter sweep to be performed during the EME "
@@ -255,7 +255,7 @@ class EMESimulation(AbstractYeeGridSimulation):
"in 'sim_data.smatrix'. Other simulation monitor data is not included in the sweep.",
)
- constraint: Optional[Literal["passive", "unitary"]] = pd.Field(
+ constraint: Literal["passive", "unitary"] | None = pd.Field(
"passive",
title="EME Constraint",
description="Constraint for EME propagation, imposed at cell interfaces. "
@@ -308,12 +308,12 @@ def _validate_structures(cls, val):
@add_ax_if_none
def plot_eme_ports(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
**kwargs,
) -> Ax:
"""Plot the EME ports."""
@@ -352,12 +352,12 @@ def plot_eme_ports(
def plot_eme_subgrid_boundaries(
self,
eme_grid_spec: EMEGridSpec,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
**kwargs,
) -> Ax:
"""Plot the EME subgrid boundaries.
@@ -404,12 +404,12 @@ def plot_eme_subgrid_boundaries(
@add_ax_if_none
def plot_eme_grid(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
**kwargs,
) -> Ax:
"""Plot the EME grid."""
@@ -445,14 +445,14 @@ def plot_eme_grid(
@add_ax_if_none
def plot(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
- source_alpha: Optional[float] = None,
- monitor_alpha: Optional[float] = None,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
+ source_alpha: float | None = None,
+ monitor_alpha: float | None = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
**patch_kwargs,
) -> Ax:
"""Plot each of simulation's components on a plane defined by one nonzero x,y,z coordinate.
@@ -1109,11 +1109,11 @@ def _to_fdtd_sim(self) -> Simulation:
def subsection(
self,
region: Box,
- grid_spec: Union[GridSpec, Literal["identical"]] = None,
- eme_grid_spec: Union[EMEGridSpec, Literal["identical"]] = None,
- symmetry: Optional[tuple[Symmetry, Symmetry, Symmetry]] = None,
+ grid_spec: GridSpec | Literal["identical"] = None,
+ eme_grid_spec: EMEGridSpec | Literal["identical"] = None,
+ symmetry: tuple[Symmetry, Symmetry, Symmetry] | None = None,
warn_symmetry_expansion: bool = True,
- monitors: Optional[tuple[MonitorType, ...]] = None,
+ monitors: tuple[MonitorType, ...] | None = None,
remove_outside_structures: bool = True,
remove_outside_custom_mediums: bool = False,
**kwargs,
diff --git a/tidy3d/components/eme/sweep.py b/tidy3d/components/eme/sweep.py
index 6ded35c645..ea661cf872 100644
--- a/tidy3d/components/eme/sweep.py
+++ b/tidy3d/components/eme/sweep.py
@@ -3,7 +3,6 @@
from __future__ import annotations
from abc import ABC, abstractmethod
-from typing import Union
import pydantic.v1 as pd
@@ -126,4 +125,4 @@ def num_sweep(self) -> pd.PositiveInt:
return len(self.num_reps)
-EMESweepSpecType = Union[EMELengthSweep, EMEModeSweep, EMEFreqSweep, EMEPeriodicitySweep]
+EMESweepSpecType = EMELengthSweep | EMEModeSweep | EMEFreqSweep | EMEPeriodicitySweep
diff --git a/tidy3d/components/field_projection.py b/tidy3d/components/field_projection.py
index 4346a85d50..460421af99 100644
--- a/tidy3d/components/field_projection.py
+++ b/tidy3d/components/field_projection.py
@@ -3,7 +3,6 @@
from __future__ import annotations
from collections.abc import Iterable
-from typing import Union
import autograd.numpy as anp
import numpy as np
@@ -46,7 +45,7 @@
# Numpy float array and related array types
-ArrayLikeN2F = Union[float, tuple[float, ...], ArrayComplex4D]
+ArrayLikeN2F = float | tuple[float, ...] | ArrayComplex4D
class FieldProjector(Tidy3dBaseModel):
@@ -79,7 +78,7 @@ class FieldProjector(Tidy3dBaseModel):
"near field.",
)
- pts_per_wavelength: Union[int, type(None)] = pydantic.Field(
+ pts_per_wavelength: int | type(None) = pydantic.Field(
PTS_PER_WVL,
title="Points per wavelength",
description="Number of points per wavelength in the background medium with which "
@@ -355,8 +354,8 @@ def _resample_surface_currents(
@staticmethod
def trapezoid(
ary: np.ndarray,
- pts: Union[Iterable[np.ndarray], np.ndarray],
- axes: Union[Iterable[int], int] = 0,
+ pts: Iterable[np.ndarray] | np.ndarray,
+ axes: Iterable[int] | int = 0,
):
"""Trapezoidal integration in n dimensions.
diff --git a/tidy3d/components/frequency_extrapolation.py b/tidy3d/components/frequency_extrapolation.py
index e2768bba36..51c532aaad 100644
--- a/tidy3d/components/frequency_extrapolation.py
+++ b/tidy3d/components/frequency_extrapolation.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Optional
-
import pydantic.v1 as pydantic
from tidy3d.components.base import Tidy3dBaseModel
@@ -34,7 +32,7 @@ class AbstractLowFrequencySmoothingSpec(Tidy3dBaseModel):
le=3,
)
- max_deviation: Optional[float] = pydantic.Field(
+ max_deviation: float | None = pydantic.Field(
0.5,
title="Maximum Deviation",
description="The maximum deviation (in fraction of the trusted values) to allow for the low frequency smoothing.",
diff --git a/tidy3d/components/geometry/base.py b/tidy3d/components/geometry/base.py
index bc9912541b..059dc3c085 100644
--- a/tidy3d/components/geometry/base.py
+++ b/tidy3d/components/geometry/base.py
@@ -5,7 +5,8 @@
import functools
import pathlib
from abc import ABC, abstractmethod
-from typing import Any, Callable, Optional, Union
+from collections.abc import Callable
+from typing import Any
import autograd.numpy as np
import pydantic.v1 as pydantic
@@ -249,7 +250,7 @@ def intersections_tilted_plane(
"""
def intersections_plane(
- self, x: Optional[float] = None, y: Optional[float] = None, z: Optional[float] = None
+ self, x: float | None = None, y: float | None = None, z: float | None = None
) -> list[Shapely]:
"""Returns list of shapely geometries at plane specified by one non-None value of x,y,z.
@@ -372,7 +373,7 @@ def contains(
return True
def intersects_plane(
- self, x: Optional[float] = None, y: Optional[float] = None, z: Optional[float] = None
+ self, x: float | None = None, y: float | None = None, z: float | None = None
) -> bool:
"""Whether self intersects plane specified by one non-None value of x,y,z.
@@ -503,9 +504,9 @@ def _update_from_bounds(self, bounds: tuple[float, float], axis: Axis) -> Geomet
@add_ax_if_none
def plot(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
plot_length_units: LengthUnit = None,
viz_spec: VisualizationSpec = None,
@@ -539,7 +540,7 @@ def plot(
"""
# find shapes that intersect self at plane
- axis, position = self.parse_xyz_kwargs(x=x, y=y, z=z)
+ axis, _position = self.parse_xyz_kwargs(x=x, y=y, z=z)
shapes_intersect = self.intersections_plane(x=x, y=y, z=z)
plot_params = self.plot_params
@@ -668,9 +669,9 @@ def add_ax_lims(self, axis: Axis, ax: Ax, buffer: float = PLOT_BUFFER) -> Ax:
@staticmethod
def add_ax_labels_and_title(
ax: Ax,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
plot_length_units: LengthUnit = None,
) -> Ax:
"""Sets the axis labels, tick labels, and title based on ``axis``
@@ -967,7 +968,7 @@ def scaled(self, x: float = 1.0, y: float = 1.0, z: float = 1.0) -> Geometry:
"""
return Transformed(geometry=self, transform=Transformed.scaling(x, y, z))
- def rotated(self, angle: float, axis: Union[Axis, Coordinate]) -> Geometry:
+ def rotated(self, angle: float, axis: Axis | Coordinate) -> Geometry:
"""Return a rotated copy of this geometry.
Parameters
@@ -1158,7 +1159,7 @@ def kspace_2_sph(ux: float, uy: float, axis: Axis) -> tuple[float, float]:
def load_gds_vertices_gdstk(
gds_cell,
gds_layer: int,
- gds_dtype: Optional[int] = None,
+ gds_dtype: int | None = None,
gds_scale: pydantic.PositiveFloat = 1.0,
) -> list[ArrayFloat2D]:
"""Load polygon vertices from a ``gdstk.Cell``.
@@ -1212,7 +1213,7 @@ def from_gds(
axis: Axis,
slab_bounds: tuple[float, float],
gds_layer: int,
- gds_dtype: Optional[int] = None,
+ gds_dtype: int | None = None,
gds_scale: pydantic.PositiveFloat = 1.0,
dilation: float = 0.0,
sidewall_angle: float = 0,
@@ -1323,9 +1324,9 @@ def from_shapely(
@verify_packages_import(["gdstk"])
def to_gdstk(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
gds_layer: pydantic.NonNegativeInt = 0,
gds_dtype: pydantic.NonNegativeInt = 0,
) -> list:
@@ -1373,9 +1374,9 @@ def to_gdstk(
def to_gds(
self,
cell,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
gds_layer: pydantic.NonNegativeInt = 0,
gds_dtype: pydantic.NonNegativeInt = 0,
) -> None:
@@ -1413,9 +1414,9 @@ def to_gds(
def to_gds_file(
self,
fname: str,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
gds_layer: pydantic.NonNegativeInt = 0,
gds_dtype: pydantic.NonNegativeInt = 0,
gds_cell_name: str = "MAIN",
@@ -1696,7 +1697,7 @@ def reference_axis_pos(self) -> float:
return self.center_axis
def intersections_plane(
- self, x: Optional[float] = None, y: Optional[float] = None, z: Optional[float] = None
+ self, x: float | None = None, y: float | None = None, z: float | None = None
):
"""Returns shapely geometry at plane specified by one non None value of x,y,z.
@@ -2063,7 +2064,7 @@ def _do_intersections_tilted_plane(
return path.polygons_full
def intersections_plane(
- self, x: Optional[float] = None, y: Optional[float] = None, z: Optional[float] = None
+ self, x: float | None = None, y: float | None = None, z: float | None = None
):
"""Returns shapely geometry at plane specified by one non None value of x,y,z.
@@ -2220,12 +2221,12 @@ def _update_from_bounds(self, bounds: tuple[float, float], axis: Axis) -> Box:
def _plot_arrow(
self,
direction: tuple[float, float, float],
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
- color: Optional[str] = None,
- alpha: Optional[float] = None,
- bend_radius: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
+ color: str | None = None,
+ alpha: float | None = None,
+ bend_radius: float | None = None,
bend_axis: Axis = None,
both_dirs: bool = False,
ax: Ax = None,
@@ -2561,8 +2562,8 @@ def _arr_at_face(arr: xr.DataArray, interp_point: float, dim_normal: str) -> xr.
@staticmethod
def _integrate_face(
arr_at_face: xr.DataArray,
- integration_dims: Union[tuple[str], tuple[str, str]],
- integration_bounds: Union[tuple[Coordinate2D], tuple[Coordinate2D, Coordinate2D]],
+ integration_dims: tuple[str] | tuple[str, str],
+ integration_bounds: tuple[Coordinate2D] | tuple[Coordinate2D, Coordinate2D],
) -> complex:
"""Perform the integration of the surface and sum over the frequency component of the gradient."""
@@ -2778,7 +2779,7 @@ def _derivative_face_pec(
Surface gradient vjp value
"""
- fld_H_normal, flds_H_perp = self.pop_axis(("Hx", "Hy", "Hz"), axis=axis_normal)
+ _fld_H_normal, flds_H_perp = self.pop_axis(("Hx", "Hy", "Hz"), axis=axis_normal)
Hs_perp = tuple(H_der_map[key] for key in flds_H_perp)
is_2d, zero_dimension, do_singularity_correction = self._check_singularity_correction_pec(
@@ -3047,7 +3048,7 @@ def scaling(x: float = 1.0, y: float = 1.0, z: float = 1.0) -> MatrixReal4x4:
)
@staticmethod
- def rotation(angle: float, axis: Union[Axis, Coordinate]) -> MatrixReal4x4:
+ def rotation(angle: float, axis: Axis | Coordinate) -> MatrixReal4x4:
"""Return a rotation matrix.
Parameters
@@ -3259,7 +3260,7 @@ def intersections_tilted_plane(
)
def intersections_plane(
- self, x: Optional[float] = None, y: Optional[float] = None, z: Optional[float] = None
+ self, x: float | None = None, y: float | None = None, z: float | None = None
) -> list[Shapely]:
"""Returns list of shapely geometries at plane specified by one non-None value of x,y,z.
@@ -3470,7 +3471,7 @@ def intersections_tilted_plane(
]
def intersections_plane(
- self, x: Optional[float] = None, y: Optional[float] = None, z: Optional[float] = None
+ self, x: float | None = None, y: float | None = None, z: float | None = None
) -> list[Shapely]:
"""Returns list of shapely geometries at plane specified by one non-None value of x,y,z.
diff --git a/tidy3d/components/geometry/mesh.py b/tidy3d/components/geometry/mesh.py
index ccf6305c8f..7e019ab145 100644
--- a/tidy3d/components/geometry/mesh.py
+++ b/tidy3d/components/geometry/mesh.py
@@ -3,7 +3,8 @@
from __future__ import annotations
from abc import ABC
-from typing import Callable, Literal, Optional, Union
+from collections.abc import Callable
+from typing import Literal
import numpy as np
import pydantic.v1 as pydantic
@@ -34,7 +35,7 @@ class TriangleMesh(base.Geometry, ABC):
>>> stl_geom = TriangleMesh.from_vertices_faces(vertices, faces)
"""
- mesh_dataset: Optional[TriangleMeshDataset] = pydantic.Field(
+ mesh_dataset: TriangleMeshDataset | None = pydantic.Field(
...,
title="Surface mesh data",
description="Surface mesh data.",
@@ -152,9 +153,9 @@ def from_stl(
filename: str,
scale: float = 1.0,
origin: tuple[float, float, float] = (0, 0, 0),
- solid_index: Optional[int] = None,
+ solid_index: int | None = None,
**kwargs,
- ) -> Union[TriangleMesh, base.GeometryGroup]:
+ ) -> TriangleMesh | base.GeometryGroup:
"""Load a :class:`.TriangleMesh` directly from an STL file.
The ``solid_index`` parameter can be used to select a single solid from the file.
Otherwise, if the file contains a single solid, it will be loaded as a
@@ -554,7 +555,7 @@ def intersections_tilted_plane(
return path.polygons_full
def intersections_plane(
- self, x: Optional[float] = None, y: Optional[float] = None, z: Optional[float] = None
+ self, x: float | None = None, y: float | None = None, z: float | None = None
) -> list[Shapely]:
"""Returns list of shapely geometries at plane specified by one non-None value of x,y,z.
@@ -656,9 +657,9 @@ def inside(
@add_ax_if_none
def plot(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
**patch_kwargs,
) -> Ax:
diff --git a/tidy3d/components/geometry/polyslab.py b/tidy3d/components/geometry/polyslab.py
index 2c74efba9c..b787965db3 100644
--- a/tidy3d/components/geometry/polyslab.py
+++ b/tidy3d/components/geometry/polyslab.py
@@ -5,7 +5,6 @@
import math
from copy import copy
from functools import lru_cache
-from typing import Optional, Union
import autograd.numpy as np
import pydantic.v1 as pydantic
@@ -265,7 +264,7 @@ def from_gds(
axis: Axis,
slab_bounds: tuple[float, float],
gds_layer: int,
- gds_dtype: Optional[int] = None,
+ gds_dtype: int | None = None,
gds_scale: pydantic.PositiveFloat = 1.0,
dilation: float = 0.0,
sidewall_angle: float = 0,
@@ -327,7 +326,7 @@ def from_gds(
def _load_gds_vertices(
gds_cell,
gds_layer: int,
- gds_dtype: Optional[int] = None,
+ gds_dtype: int | None = None,
gds_scale: pydantic.PositiveFloat = 1.0,
) -> list[ArrayFloat2D]:
"""Import :class:`PolySlab` from a ``gdstk.Cell``.
@@ -1531,7 +1530,7 @@ def _z_slices(self, sim_min: np.ndarray, sim_max: np.ndarray, is_2d: bool, dx: f
@staticmethod
def _clip_edge_to_bounds_t(
v0_3d: np.ndarray, v1_3d: np.ndarray, sim_min: np.ndarray, sim_max: np.ndarray
- ) -> Optional[tuple[float, float]]:
+ ) -> tuple[float, float] | None:
"""Parametric bounds [t0,t1] of segment within [sim_min, sim_max]."""
t_start, t_end = 0.0, 1.0
edge_clip_tolerance = config.adjoint.edge_clip_tolerance
@@ -1633,7 +1632,7 @@ def _collect_sidewall_patches(
axis_vec[self.axis] = 1.0
# densify along axis as |theta| grows: dz scales with cos(theta)
- z_centers, dz, z0, z1 = self._z_slices(sim_min, sim_max, is_2d=is_2d, dx=dx * cos_th)
+ z_centers, dz, _z0, _z1 = self._z_slices(sim_min, sim_max, is_2d=is_2d, dx=dx * cos_th)
# early exit: no slices
if (not is_2d) and len(z_centers) == 0:
@@ -1817,7 +1816,7 @@ def _compute_derivative_sidewall_angle(
sim_min: np.ndarray,
sim_max: np.ndarray,
is_2d: bool = False,
- interpolators: Optional[dict] = None,
+ interpolators: dict | None = None,
) -> float:
"""VJP for dJ/dtheta where theta = sidewall_angle.
@@ -2092,7 +2091,7 @@ def _compute_derivative_vertices(
sim_min: np.ndarray,
sim_max: np.ndarray,
is_2d: bool = False,
- interpolators: Optional[dict] = None,
+ interpolators: dict | None = None,
) -> np.ndarray:
"""VJP for the vertices of a ``PolySlab``.
@@ -2288,7 +2287,7 @@ def scaled(self, x: float = 1.0, y: float = 1.0, z: float = 1.0) -> PolySlab:
scaled_slab_bounds = tuple(scale_normal * bound for bound in self.slab_bounds)
return self.updated_copy(vertices=scaled_vertices, slab_bounds=scaled_slab_bounds)
- def rotated(self, angle: float, axis: Union[Axis, Coordinate]) -> PolySlab:
+ def rotated(self, angle: float, axis: Axis | Coordinate) -> PolySlab:
"""Return a rotated copy of this geometry.
Parameters
@@ -2362,7 +2361,7 @@ def from_gds(
axis: Axis,
slab_bounds: tuple[float, float],
gds_layer: int,
- gds_dtype: Optional[int] = None,
+ gds_dtype: int | None = None,
gds_scale: pydantic.PositiveFloat = 1.0,
dilation: float = 0.0,
sidewall_angle: float = 0,
diff --git a/tidy3d/components/geometry/primitives.py b/tidy3d/components/geometry/primitives.py
index b75304c2ab..6f2b343ba1 100644
--- a/tidy3d/components/geometry/primitives.py
+++ b/tidy3d/components/geometry/primitives.py
@@ -3,7 +3,6 @@
from __future__ import annotations
from math import isclose
-from typing import Optional
import autograd.numpy as anp
import numpy as np
@@ -111,7 +110,7 @@ def intersections_tilted_plane(
return [shapely.Polygon(vertices[:, :2])]
def intersections_plane(
- self, x: Optional[float] = None, y: Optional[float] = None, z: Optional[float] = None
+ self, x: float | None = None, y: float | None = None, z: float | None = None
):
"""Returns shapely geometry at plane specified by one non None value of x,y,z.
diff --git a/tidy3d/components/geometry/utils.py b/tidy3d/components/geometry/utils.py
index 8f4c556cf0..f91cfa0954 100644
--- a/tidy3d/components/geometry/utils.py
+++ b/tidy3d/components/geometry/utils.py
@@ -6,7 +6,7 @@
from collections.abc import Iterable
from enum import Enum
from math import isclose
-from typing import Any, Optional, Union
+from typing import Any
import numpy as np
import pydantic.v1 as pydantic
@@ -37,21 +37,21 @@
from . import base, mesh, polyslab, primitives
-GeometryType = Union[
- base.Box,
- base.Transformed,
- base.ClipOperation,
- base.GeometryGroup,
- primitives.Sphere,
- primitives.Cylinder,
- polyslab.PolySlab,
- polyslab.ComplexPolySlabBase,
- mesh.TriangleMesh,
-]
+GeometryType = (
+ base.Box
+ | base.Transformed
+ | base.ClipOperation
+ | base.GeometryGroup
+ | primitives.Sphere
+ | primitives.Cylinder
+ | polyslab.PolySlab
+ | polyslab.ComplexPolySlabBase
+ | mesh.TriangleMesh
+)
def flatten_shapely_geometries(
- geoms: Union[Shapely, Iterable[Shapely]], keep_types: tuple[type, ...] = (Polygon,)
+ geoms: Shapely | Iterable[Shapely], keep_types: tuple[type, ...] = (Polygon,)
) -> list[Shapely]:
"""
Flatten nested geometries into a flat list, while only keeping the specified types.
@@ -184,7 +184,7 @@ def flatten_groups(
*geometries: GeometryType,
flatten_nonunion_type: bool = False,
flatten_transformed: bool = False,
- transform: Optional[MatrixReal4x4] = None,
+ transform: MatrixReal4x4 | None = None,
) -> GeometryType:
"""Iterates over all geometries, flattening groups and unions.
@@ -449,9 +449,9 @@ class SnappingSpec(Tidy3dBaseModel):
description="Describes how snapping positions will be chosen.",
)
- margin: Optional[
- tuple[pydantic.NonNegativeInt, pydantic.NonNegativeInt, pydantic.NonNegativeInt]
- ] = pydantic.Field(
+ margin: (
+ tuple[pydantic.NonNegativeInt, pydantic.NonNegativeInt, pydantic.NonNegativeInt] | None
+ ) = pydantic.Field(
(0, 0, 0),
title="Margin",
description="Number of additional grid points to consider when expanding or contracting "
@@ -711,7 +711,7 @@ def _shift_value_signed(
bounds: Bound,
direction: Direction,
shift: int,
- name: Optional[str] = None,
+ name: str | None = None,
) -> float:
"""Calculate the signed distance corresponding to moving the object by ``shift`` number
of cells in the positive or negative ``direction`` along the dimension given by
diff --git a/tidy3d/components/grid/corner_finder.py b/tidy3d/components/grid/corner_finder.py
index 6c71cd0c46..56fd4703bc 100644
--- a/tidy3d/components/grid/corner_finder.py
+++ b/tidy3d/components/grid/corner_finder.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-from typing import Any, Literal, Optional
+from typing import Any, Literal
import numpy as np
import pydantic.v1 as pd
@@ -39,28 +39,28 @@ class CornerFinderSpec(Tidy3dBaseModel):
lt=np.pi,
)
- distance_threshold: Optional[pd.PositiveFloat] = pd.Field(
+ distance_threshold: pd.PositiveFloat | None = pd.Field(
None,
title="Distance Threshold In Corner Identification",
description="If not ``None`` and the distance of the vertex to its neighboring vertices "
"is below the threshold value based on Douglas-Peucker algorithm, the vertex is disqualified as a corner.",
)
- concave_resolution: Optional[pd.PositiveInt] = pd.Field(
+ concave_resolution: pd.PositiveInt | None = pd.Field(
None,
title="Concave Region Resolution.",
description="Specifies number of steps to use for determining `dl_min` based on concave featues."
"If set to ``None``, then the corresponding `dl_min` reduction is not applied.",
)
- convex_resolution: Optional[pd.PositiveInt] = pd.Field(
+ convex_resolution: pd.PositiveInt | None = pd.Field(
None,
title="Convex Region Resolution.",
description="Specifies number of steps to use for determining `dl_min` based on convex featues."
"If set to ``None``, then the corresponding `dl_min` reduction is not applied.",
)
- mixed_resolution: Optional[pd.PositiveInt] = pd.Field(
+ mixed_resolution: pd.PositiveInt | None = pd.Field(
None,
title="Mixed Region Resolution.",
description="Specifies number of steps to use for determining `dl_min` based on mixed featues."
diff --git a/tidy3d/components/grid/grid.py b/tidy3d/components/grid/grid.py
index 73f8485a9b..718eb2dbab 100644
--- a/tidy3d/components/grid/grid.py
+++ b/tidy3d/components/grid/grid.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-from typing import Literal, Union
+from typing import Literal
import numpy as np
import pydantic.v1 as pd
@@ -93,10 +93,10 @@ def cell_size_meshgrid(self):
def _interp_from_xarray(
self,
- array: Union[SpatialDataArray, ScalarFieldDataArray],
+ array: SpatialDataArray | ScalarFieldDataArray,
interp_method: InterpMethod,
- fill_value: Union[Literal["extrapolate"], float] = "extrapolate",
- ) -> Union[SpatialDataArray, ScalarFieldDataArray]:
+ fill_value: Literal["extrapolate"] | float = "extrapolate",
+ ) -> SpatialDataArray | ScalarFieldDataArray:
"""
Similar to ``xarrray.DataArray.interp`` with 2 enhancements:
@@ -174,7 +174,7 @@ def _interp_from_unstructured(
self,
array: UnstructuredGridDatasetType,
interp_method: InterpMethod,
- fill_value: Union[Literal["extrapolate"], float] = "extrapolate",
+ fill_value: Literal["extrapolate"] | float = "extrapolate",
) -> SpatialDataArray:
"""
Interpolate from untructured grid onto a Cartesian one.
@@ -207,10 +207,10 @@ def _interp_from_unstructured(
def spatial_interp(
self,
- array: Union[SpatialDataArray, ScalarFieldDataArray, UnstructuredGridDatasetType],
+ array: SpatialDataArray | ScalarFieldDataArray | UnstructuredGridDatasetType,
interp_method: InterpMethod,
- fill_value: Union[Literal["extrapolate"], float] = "extrapolate",
- ) -> Union[SpatialDataArray, ScalarFieldDataArray]:
+ fill_value: Literal["extrapolate"] | float = "extrapolate",
+ ) -> SpatialDataArray | ScalarFieldDataArray:
"""
Similar to ``xarrray.DataArray.interp`` with 2 enhancements:
diff --git a/tidy3d/components/grid/grid_spec.py b/tidy3d/components/grid/grid_spec.py
index 93a945eac8..2e7b6a6cf6 100644
--- a/tidy3d/components/grid/grid_spec.py
+++ b/tidy3d/components/grid/grid_spec.py
@@ -3,7 +3,7 @@
from __future__ import annotations
from abc import ABC, abstractmethod
-from typing import Any, Literal, Optional, Union
+from typing import Any, Literal
import numpy as np
import pydantic.v1 as pd
@@ -880,7 +880,7 @@ def estimated_min_dl(
return self._filtered_dl(min_dl, sim_size)
-GridType = Union[UniformGrid, CustomGrid, AutoGrid, CustomGridBoundaries, QuasiUniformGrid]
+GridType = UniformGrid | CustomGrid | AutoGrid | CustomGridBoundaries | QuasiUniformGrid
class GridRefinement(Tidy3dBaseModel):
@@ -900,13 +900,13 @@ class GridRefinement(Tidy3dBaseModel):
"""
- refinement_factor: Optional[pd.PositiveFloat] = pd.Field(
+ refinement_factor: pd.PositiveFloat | None = pd.Field(
None,
title="Mesh Refinement Factor",
description="Refine grid step size in vacuum by this factor.",
)
- dl: Optional[pd.PositiveFloat] = pd.Field(
+ dl: pd.PositiveFloat | None = pd.Field(
None,
title="Grid Size",
description="Grid step size in the refined region.",
@@ -1013,14 +1013,14 @@ class LayerRefinementSpec(Box):
description="Specifies dimension of the layer normal axis (0,1,2) -> (x,y,z).",
)
- min_steps_along_axis: Optional[pd.PositiveFloat] = pd.Field(
+ min_steps_along_axis: pd.PositiveFloat | None = pd.Field(
None,
title="Minimal Number Of Steps Along Axis",
description="If not ``None`` and the thickness of the layer is nonzero, set minimal "
"number of steps discretizing the layer thickness.",
)
- bounds_refinement: Optional[GridRefinement] = pd.Field(
+ bounds_refinement: GridRefinement | None = pd.Field(
None,
title="Mesh Refinement Factor Around Layer Bounds",
description="If not ``None``, refine mesh around minimum and maximum positions "
@@ -1028,14 +1028,14 @@ class LayerRefinementSpec(Box):
"refinement here is only applied if it sets a smaller grid size.",
)
- bounds_snapping: Optional[Literal["bounds", "lower", "upper", "center"]] = pd.Field(
+ bounds_snapping: Literal["bounds", "lower", "upper", "center"] | None = pd.Field(
"lower",
title="Placing Grid Snapping Point Along Axis",
description="If not ``None``, enforcing grid boundaries to pass through ``lower``, "
"``center``, or ``upper`` position of the layer; or both ``lower`` and ``upper`` with ``bounds``.",
)
- corner_finder: Optional[CornerFinderSpec] = pd.Field(
+ corner_finder: CornerFinderSpec | None = pd.Field(
CornerFinderSpec(),
title="Inplane Corner Detection Specification",
description="Specification for inplane corner detection. Inplane mesh refinement "
@@ -1049,7 +1049,7 @@ class LayerRefinementSpec(Box):
"grid boundaries to pass through corners of geometries specified by ``corner_finder``.",
)
- corner_refinement: Optional[GridRefinement] = pd.Field(
+ corner_refinement: GridRefinement | None = pd.Field(
GridRefinement(),
title="Inplane Mesh Refinement Factor Around Corners",
description="If not ``None`` and ``corner_finder`` is not ``None``, refine mesh around "
@@ -1103,9 +1103,9 @@ def from_layer_bounds(
min_steps_along_axis: np.PositiveFloat = None,
bounds_refinement: GridRefinement = None,
bounds_snapping: Literal["bounds", "lower", "upper", "center"] = "lower",
- corner_finder: Union[CornerFinderSpec, None, object] = Undefined,
+ corner_finder: CornerFinderSpec | None | object = Undefined,
corner_snapping: bool = True,
- corner_refinement: Union[GridRefinement, None, object] = Undefined,
+ corner_refinement: GridRefinement | None | object = Undefined,
refinement_inside_sim_only: bool = True,
gap_meshing_iters: pd.NonNegativeInt = 1,
dl_min_from_gap_width: bool = True,
@@ -2346,7 +2346,7 @@ def all_snapping_points(
self,
structures: list[Structure],
lumped_elements: list[LumpedElementType],
- internal_snapping_points: Optional[list[CoordinateOptional]] = None,
+ internal_snapping_points: list[CoordinateOptional] | None = None,
) -> list[CoordinateOptional]:
"""Internal and external snapping points. External snapping points take higher priority.
So far, internal snapping points are generated by `layer_refinement_specs`.
@@ -2436,7 +2436,7 @@ def all_override_structures(
sim_size: tuple[float, 3],
lumped_elements: list[LumpedElementType],
structure_priority_mode: PriorityMode = "equal",
- internal_override_structures: Optional[list[MeshOverrideStructure]] = None,
+ internal_override_structures: list[MeshOverrideStructure] | None = None,
) -> list[StructureType]:
"""Internal and external mesh override structures sorted based on their priority. By default,
the priority of internal override structures is -1, and 0 for external ones.
@@ -2535,8 +2535,8 @@ def make_grid(
sources: list[SourceType],
num_pml_layers: list[tuple[pd.NonNegativeInt, pd.NonNegativeInt]],
lumped_elements: list[LumpedElementType] = (),
- internal_override_structures: Optional[list[MeshOverrideStructure]] = None,
- internal_snapping_points: Optional[list[CoordinateOptional]] = None,
+ internal_override_structures: list[MeshOverrideStructure] | None = None,
+ internal_snapping_points: list[CoordinateOptional] | None = None,
boundary_types: tuple[tuple[str, str], tuple[str, str], tuple[str, str]] = [
[None, None],
[None, None],
@@ -2601,8 +2601,8 @@ def _make_grid_and_snapping_lines(
sources: list[SourceType],
num_pml_layers: list[tuple[pd.NonNegativeInt, pd.NonNegativeInt]],
lumped_elements: list[LumpedElementType] = (),
- internal_override_structures: Optional[list[MeshOverrideStructure]] = None,
- internal_snapping_points: Optional[list[CoordinateOptional]] = None,
+ internal_override_structures: list[MeshOverrideStructure] | None = None,
+ internal_snapping_points: list[CoordinateOptional] | None = None,
boundary_types: tuple[tuple[str, str], tuple[str, str], tuple[str, str]] = [
[None, None],
[None, None],
@@ -2721,8 +2721,8 @@ def _make_grid_one_iteration(
sources: list[SourceType],
num_pml_layers: list[tuple[pd.NonNegativeInt, pd.NonNegativeInt]],
lumped_elements: list[LumpedElementType] = (),
- internal_override_structures: Optional[list[MeshOverrideStructure]] = None,
- internal_snapping_points: Optional[list[CoordinateOptional]] = None,
+ internal_override_structures: list[MeshOverrideStructure] | None = None,
+ internal_snapping_points: list[CoordinateOptional] | None = None,
dl_min_from_gaps: pd.PositiveFloat = inf,
structure_priority_mode: PriorityMode = "equal",
) -> Grid:
diff --git a/tidy3d/components/grid/mesher.py b/tidy3d/components/grid/mesher.py
index f66be82606..3c36e68a2b 100644
--- a/tidy3d/components/grid/mesher.py
+++ b/tidy3d/components/grid/mesher.py
@@ -7,7 +7,6 @@
from abc import ABC, abstractmethod
from itertools import compress
from math import isclose
-from typing import Union
import numpy as np
import pydantic.v1 as pd
@@ -558,12 +557,12 @@ def reorder_structures(
)
)
- ordered_structures = (
- [structures_others[0]]
- + structures_unshadowed
- + structures_others[1:]
- + structures_enforced
- )
+ ordered_structures = [
+ structures_others[0],
+ *structures_unshadowed,
+ *structures_others[1:],
+ *structures_enforced,
+ ]
return len(structures_others) + len(structures_unshadowed), ordered_structures
@staticmethod
@@ -1416,4 +1415,4 @@ def grid_type_in_interval(
return 2
-MesherType = Union[GradedMesher]
+MesherType = GradedMesher
diff --git a/tidy3d/components/lumped_element.py b/tidy3d/components/lumped_element.py
index bf8a71b039..8d79e5d033 100644
--- a/tidy3d/components/lumped_element.py
+++ b/tidy3d/components/lumped_element.py
@@ -4,7 +4,7 @@
from abc import ABC, abstractmethod
from math import isclose
-from typing import Annotated, Literal, Optional, Union
+from typing import Annotated, Literal
import numpy as np
import pydantic.v1 as pd
@@ -60,7 +60,7 @@ class LumpedElement(MicrowaveBaseModel, ABC):
min_length=1,
)
- num_grid_cells: Optional[pd.PositiveInt] = pd.Field(
+ num_grid_cells: pd.PositiveInt | None = pd.Field(
DEFAULT_LUMPED_ELEMENT_NUM_CELLS,
title="Lumped element grid cells",
description="Number of mesh grid cells associated with the lumped element along each direction. "
@@ -573,21 +573,21 @@ class RLCNetwork(MicrowaveBaseModel):
"""
- resistance: Optional[pd.PositiveFloat] = pd.Field(
+ resistance: pd.PositiveFloat | None = pd.Field(
None,
title="Resistance",
description="Resistance value in ohms.",
unit=OHM,
)
- capacitance: Optional[pd.PositiveFloat] = pd.Field(
+ capacitance: pd.PositiveFloat | None = pd.Field(
None,
title="Capacitance",
description="Capacitance value in farads.",
unit=FARAD,
)
- inductance: Optional[pd.PositiveFloat] = pd.Field(
+ inductance: pd.PositiveFloat | None = pd.Field(
None,
title="Inductance",
description="Inductance value in henrys.",
@@ -914,7 +914,7 @@ class LinearLumpedElement(RectangularLumpedElement):
* `Using lumped elements in Tidy3D simulations <../../notebooks/LinearLumpedElements.html>`_
"""
- network: Union[RLCNetwork, AdmittanceNetwork] = pd.Field(
+ network: RLCNetwork | AdmittanceNetwork = pd.Field(
...,
title="Network",
description="The linear element produces an equivalent medium that emulates the "
@@ -979,9 +979,7 @@ def _create_box_for_network(self, grid: Grid) -> Box:
return snap_box_to_grid(grid, cell_box, snap_spec=snap_spec)
- def _create_connection_boxes(
- self, cell_box: Box, grid: Grid
- ) -> tuple[Optional[Box], Optional[Box]]:
+ def _create_connection_boxes(self, cell_box: Box, grid: Grid) -> tuple[Box | None, Box | None]:
"""Creates PEC structures that connect the network portion of the lumped element to the
boundaries of the lumped element.
"""
@@ -1031,7 +1029,7 @@ def to_structure(self, grid) -> Structure:
medium=Medium2D(**medium_dict),
)
- def to_PEC_connection(self, grid) -> Optional[Structure]:
+ def to_PEC_connection(self, grid) -> Structure | None:
"""Converts the :class:`LinearLumpedElement` object to a :class:`.Structure`,
representing any PEC connections.
"""
@@ -1169,10 +1167,6 @@ def impedance(self, freqs: np.ndarray) -> np.ndarray:
# lumped elements allowed in Simulation.lumped_elements
LumpedElementType = Annotated[
- Union[
- LumpedResistor,
- CoaxialLumpedResistor,
- LinearLumpedElement,
- ],
+ LumpedResistor | CoaxialLumpedResistor | LinearLumpedElement,
pd.Field(discriminator=TYPE_TAG_STR),
]
diff --git a/tidy3d/components/material/multi_physics.py b/tidy3d/components/material/multi_physics.py
index 7e26a476b9..5e946e83c8 100644
--- a/tidy3d/components/material/multi_physics.py
+++ b/tidy3d/components/material/multi_physics.py
@@ -1,7 +1,5 @@
from __future__ import annotations
-from typing import Optional
-
import pydantic.v1 as pd
from tidy3d.components.base import Tidy3dBaseModel
@@ -81,9 +79,9 @@ class MultiPhysicsMedium(Tidy3dBaseModel):
... )
"""
- name: Optional[str] = pd.Field(None, title="Name", description="Medium name")
+ name: str | None = pd.Field(None, title="Name", description="Medium name")
- optical: Optional[OpticalMediumType] = pd.Field(
+ optical: OpticalMediumType | None = pd.Field(
None,
title="Optical properties",
description="Specifies optical properties.",
@@ -96,14 +94,14 @@ class MultiPhysicsMedium(Tidy3dBaseModel):
# description="Specifies electrical properties for RF simulations. This is currently not in use.",
# )
- heat: Optional[HeatMediumType] = pd.Field(
+ heat: HeatMediumType | None = pd.Field(
None,
title="Heat properties",
description="Specifies properties for Heat simulations.",
discriminator=TYPE_TAG_STR,
)
- charge: Optional[ChargeMediumType] = pd.Field(
+ charge: ChargeMediumType | None = pd.Field(
None,
title="Charge properties",
description="Specifies properties for Charge simulations.",
diff --git a/tidy3d/components/material/solver_types.py b/tidy3d/components/material/solver_types.py
index f597276aa1..edddf50a49 100644
--- a/tidy3d/components/material/solver_types.py
+++ b/tidy3d/components/material/solver_types.py
@@ -4,8 +4,6 @@
from __future__ import annotations
-from typing import Union
-
from tidy3d.components.material.tcad.charge import (
ChargeConductorMedium,
ChargeInsulatorMedium,
@@ -17,6 +15,6 @@
OpticalMediumType = MediumType
ElectricalMediumType = MediumType
HeatMediumType = ThermalSpecType
-ChargeMediumType = Union[ChargeConductorMedium, ChargeInsulatorMedium, SemiconductorMedium]
+ChargeMediumType = ChargeConductorMedium | ChargeInsulatorMedium | SemiconductorMedium
OpticalMediumType3D = ElectricalMediumType3D = ChargeMediumType3D = MediumType3D
diff --git a/tidy3d/components/material/tcad/charge.py b/tidy3d/components/material/tcad/charge.py
index aee8d1a5e6..8af782361a 100644
--- a/tidy3d/components/material/tcad/charge.py
+++ b/tidy3d/components/material/tcad/charge.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Union
-
import pydantic.v1 as pd
from tidy3d.components.data.data_array import SpatialDataArray
@@ -256,21 +254,21 @@ class SemiconductorMedium(AbstractChargeMedium):
"""
- N_c: Union[EffectiveDOSModelType, pd.PositiveFloat] = pd.Field(
+ N_c: EffectiveDOSModelType | pd.PositiveFloat = pd.Field(
...,
title="Effective density of electron states",
description=":math:`N_c` Effective density of states in the conduction band.",
units=PERCMCUBE,
)
- N_v: Union[EffectiveDOSModelType, pd.PositiveFloat] = pd.Field(
+ N_v: EffectiveDOSModelType | pd.PositiveFloat = pd.Field(
...,
title="Effective density of hole states",
description=":math:`N_v` Effective density of states in the valence band.",
units=PERCMCUBE,
)
- E_g: Union[EnergyBandGapModelType, pd.PositiveFloat] = pd.Field(
+ E_g: EnergyBandGapModelType | pd.PositiveFloat = pd.Field(
...,
title="Band-gap energy",
description=":math:`E_g` Band-gap energy",
@@ -302,7 +300,7 @@ class SemiconductorMedium(AbstractChargeMedium):
units=ELECTRON_VOLT,
)
- N_a: Union[pd.NonNegativeFloat, SpatialDataArray, tuple[DopingBoxType, ...]] = pd.Field(
+ N_a: pd.NonNegativeFloat | SpatialDataArray | tuple[DopingBoxType, ...] = pd.Field(
(),
title="Doping: Acceptor concentration",
description="Concentration of acceptor impurities, which create mobile holes, resulting in p-type material. "
@@ -311,7 +309,7 @@ class SemiconductorMedium(AbstractChargeMedium):
units=PERCMCUBE,
)
- N_d: Union[pd.NonNegativeFloat, SpatialDataArray, tuple[DopingBoxType, ...]] = pd.Field(
+ N_d: pd.NonNegativeFloat | SpatialDataArray | tuple[DopingBoxType, ...] = pd.Field(
(),
title="Doping: Donor concentration",
description="Concentration of donor impurities, which create mobile electrons, resulting in n-type material. "
diff --git a/tidy3d/components/material/tcad/heat.py b/tidy3d/components/material/tcad/heat.py
index 47014cbdfd..6de9a855fd 100644
--- a/tidy3d/components/material/tcad/heat.py
+++ b/tidy3d/components/material/tcad/heat.py
@@ -3,7 +3,6 @@
from __future__ import annotations
from abc import ABC
-from typing import Union
import pydantic.v1 as pd
@@ -198,5 +197,5 @@ class SolidSpec(SolidMedium):
"""Solid medium class for backwards compatibility"""
-ThermalSpecType = Union[FluidSpec, SolidSpec, SolidMedium, FluidMedium]
+ThermalSpecType = FluidSpec | SolidSpec | SolidMedium | FluidMedium
# Note this needs to remain here to avoid circular imports in the new medium structure.
diff --git a/tidy3d/components/material/types.py b/tidy3d/components/material/types.py
index 3ea694c581..43cb611839 100644
--- a/tidy3d/components/material/types.py
+++ b/tidy3d/components/material/types.py
@@ -1,7 +1,5 @@
from __future__ import annotations
-from typing import Union
-
from .multi_physics import MultiPhysicsMedium
from .solver_types import (
ChargeMediumType,
@@ -13,14 +11,14 @@
OpticalMediumType3D,
)
-StructureMediumType = Union[
- MultiPhysicsMedium,
- OpticalMediumType,
- ElectricalMediumType,
- HeatMediumType,
- ChargeMediumType,
-]
+StructureMediumType = (
+ MultiPhysicsMedium
+ | OpticalMediumType
+ | ElectricalMediumType
+ | HeatMediumType
+ | ChargeMediumType
+)
-MultiPhysicsMediumType3D = Union[
- MultiPhysicsMedium, OpticalMediumType3D, ElectricalMediumType3D, ChargeMediumType3D
-]
+MultiPhysicsMediumType3D = (
+ MultiPhysicsMedium | OpticalMediumType3D | ElectricalMediumType3D | ChargeMediumType3D
+)
diff --git a/tidy3d/components/medium.py b/tidy3d/components/medium.py
index e733df186c..b882e30f9a 100644
--- a/tidy3d/components/medium.py
+++ b/tidy3d/components/medium.py
@@ -4,8 +4,9 @@
import functools
from abc import ABC, abstractmethod
+from collections.abc import Callable
from math import isclose
-from typing import Callable, Literal, Optional, Union
+from typing import Literal
import autograd.numpy as np
@@ -402,7 +403,7 @@ class TwoPhotonAbsorption(NonlinearModel):
"with Tidy3D version < 2.8 and may be removed in a future release.",
)
- beta: Union[float, Complex] = pd.Field(
+ beta: float | Complex = pd.Field(
0,
title="TPA coefficient",
description="Coefficient for two-photon absorption (TPA).",
@@ -446,7 +447,7 @@ class TwoPhotonAbsorption(NonlinearModel):
units=f"{MICROMETER}^(3 e_h)",
)
- n0: Optional[Complex] = pd.Field(
+ n0: Complex | None = pd.Field(
None,
title="Complex linear refractive index",
description="Complex linear refractive index of the medium, computed for instance using "
@@ -454,7 +455,7 @@ class TwoPhotonAbsorption(NonlinearModel):
"frequencies of the simulation sources (as long as these are all equal).",
)
- freq0: Optional[pd.PositiveFloat] = pd.Field(
+ freq0: pd.PositiveFloat | None = pd.Field(
None,
title="Central frequency",
description="Central frequency, used to calculate the energy of the free-carriers "
@@ -584,7 +585,7 @@ class KerrNonlinearity(NonlinearModel):
units=f"{MICROMETER}^2 / {WATT}",
)
- n0: Optional[Complex] = pd.Field(
+ n0: Complex | None = pd.Field(
None,
title="Complex linear refractive index",
description="Complex linear refractive index of the medium, computed for instance using "
@@ -647,7 +648,7 @@ def complex_fields(self) -> bool:
return self.use_complex_fields
-NonlinearModelType = Union[NonlinearSusceptibility, TwoPhotonAbsorption, KerrNonlinearity]
+NonlinearModelType = NonlinearSusceptibility | TwoPhotonAbsorption | KerrNonlinearity
class NonlinearSpec(ABC, Tidy3dBaseModel):
@@ -768,7 +769,7 @@ class AbstractMedium(ABC, Tidy3dBaseModel):
"useful in some cases.",
)
- nonlinear_spec: Union[NonlinearSpec, NonlinearSusceptibility] = pd.Field(
+ nonlinear_spec: NonlinearSpec | NonlinearSusceptibility = pd.Field(
None,
title="Nonlinear Spec",
description="Nonlinear spec applied on top of the base medium properties.",
@@ -780,7 +781,7 @@ class AbstractMedium(ABC, Tidy3dBaseModel):
description="Modulation spec applied on top of the base medium properties.",
)
- viz_spec: Optional[VisualizationSpec] = pd.Field(
+ viz_spec: VisualizationSpec | None = pd.Field(
None,
title="Visualization Specification",
description="Plotting specification for visualizing medium.",
@@ -860,7 +861,7 @@ def _validate_modulation_spec_post_init(self):
"Time modulation is not currently supported for the components of a 2D medium."
)
- heat_spec: Optional[ThermalSpecType] = pd.Field(
+ heat_spec: ThermalSpecType | None = pd.Field(
None,
title="Heat Specification",
description="DEPRECATED: Use :class:`MultiPhysicsMedium`. Specification of the medium heat properties. They are "
@@ -1079,7 +1080,7 @@ def eps_comp(self, row: Axis, col: Axis, frequency: float) -> complex:
return 0j
def _eps_plot(
- self, frequency: float, eps_component: Optional[PermittivityComponent] = None
+ self, frequency: float, eps_component: PermittivityComponent | None = None
) -> float:
"""Returns real part of epsilon for plotting. A specific component of the epsilon tensor can
be selected for anisotropic medium.
@@ -1601,8 +1602,8 @@ def _get_real_vals(self, x: np.ndarray) -> np.ndarray:
def _eps_bounds(
self,
- frequency: Optional[float] = None,
- eps_component: Optional[PermittivityComponent] = None,
+ frequency: float | None = None,
+ eps_component: PermittivityComponent | None = None,
) -> tuple[float, float]:
"""Returns permittivity bounds for setting the color bounds when plotting.
@@ -2063,7 +2064,7 @@ class CustomIsotropicMedium(AbstractCustomMedium, Medium):
units=PERMITTIVITY,
)
- conductivity: Optional[CustomSpatialDataTypeAnnotated] = pd.Field(
+ conductivity: CustomSpatialDataTypeAnnotated | None = pd.Field(
None,
title="Conductivity",
description="Electric conductivity. Defined such that the imaginary part of the complex "
@@ -2228,7 +2229,7 @@ class CustomMedium(AbstractCustomMedium):
>>> eps = dielectric.eps_model(200e12)
"""
- eps_dataset: Optional[PermittivityDataset] = pd.Field(
+ eps_dataset: PermittivityDataset | None = pd.Field(
None,
title="Permittivity Dataset",
description="[To be deprecated] User-supplied dataset containing complex-valued "
@@ -2236,14 +2237,14 @@ class CustomMedium(AbstractCustomMedium):
"will be interpolated based on ``interp_method``.",
)
- permittivity: Optional[CustomSpatialDataTypeAnnotated] = pd.Field(
+ permittivity: CustomSpatialDataTypeAnnotated | None = pd.Field(
None,
title="Permittivity",
description="Spatial profile of relative permittivity.",
units=PERMITTIVITY,
)
- conductivity: Optional[CustomSpatialDataTypeAnnotated] = pd.Field(
+ conductivity: CustomSpatialDataTypeAnnotated | None = pd.Field(
None,
title="Conductivity",
description="Spatial profile Electric conductivity. Defined such "
@@ -2643,8 +2644,8 @@ def eps_model(self, frequency: float) -> complex:
@classmethod
def from_eps_raw(
cls,
- eps: Union[ScalarFieldDataArray, CustomSpatialDataType],
- freq: Optional[float] = None,
+ eps: ScalarFieldDataArray | CustomSpatialDataType,
+ freq: float | None = None,
interp_method: InterpMethod = "nearest",
**kwargs,
) -> CustomMedium:
@@ -2712,9 +2713,9 @@ def from_eps_raw(
@classmethod
def from_nk(
cls,
- n: Union[ScalarFieldDataArray, CustomSpatialDataType],
- k: Optional[Union[ScalarFieldDataArray, CustomSpatialDataType]] = None,
- freq: Optional[float] = None,
+ n: ScalarFieldDataArray | CustomSpatialDataType,
+ k: ScalarFieldDataArray | CustomSpatialDataType | None = None,
+ freq: float | None = None,
interp_method: InterpMethod = "nearest",
**kwargs,
) -> CustomMedium:
@@ -2805,7 +2806,7 @@ def grids(self, bounds: Bound) -> dict[str, Grid]:
pt_mins = dict(zip("xyz", rmin))
pt_maxs = dict(zip("xyz", rmax))
- def make_grid(scalar_field: Union[ScalarFieldDataArray, SpatialDataArray]) -> Grid:
+ def make_grid(scalar_field: ScalarFieldDataArray | SpatialDataArray) -> Grid:
"""Make a grid for a single dataset."""
def make_bound_coords(coords: np.ndarray, pt_min: float, pt_max: float) -> list[float]:
@@ -3621,8 +3622,8 @@ def loss_upper_bound(self) -> float:
@staticmethod
def _get_vjps_from_params(
- dJ_deps_complex: Union[complex, np.ndarray],
- poles_vals: list[tuple[Union[complex, np.ndarray], Union[complex, np.ndarray]]],
+ dJ_deps_complex: complex | np.ndarray,
+ poles_vals: list[tuple[complex | np.ndarray, complex | np.ndarray]],
omega: float,
requested_paths: list[tuple],
project_real: bool = False,
@@ -6101,7 +6102,7 @@ def roughness_correction_factor(
return correction
-SurfaceRoughnessType = Union[HammerstadSurfaceRoughness, HuraySurfaceRoughness]
+SurfaceRoughnessType = HammerstadSurfaceRoughness | HuraySurfaceRoughness
class LossyMetalMedium(Medium):
@@ -6313,19 +6314,15 @@ def plot(
return ax
-IsotropicUniformMediumFor2DType = Union[
- Medium, LossyMetalMedium, PoleResidue, Sellmeier, Lorentz, Debye, Drude, PECMedium
-]
-IsotropicUniformMediumType = Union[IsotropicUniformMediumFor2DType, PMCMedium]
-IsotropicCustomMediumType = Union[
- CustomPoleResidue,
- CustomSellmeier,
- CustomLorentz,
- CustomDebye,
- CustomDrude,
-]
-IsotropicCustomMediumInternalType = Union[IsotropicCustomMediumType, CustomIsotropicMedium]
-IsotropicMediumType = Union[IsotropicCustomMediumType, IsotropicUniformMediumType]
+IsotropicUniformMediumFor2DType = (
+ Medium | LossyMetalMedium | PoleResidue | Sellmeier | Lorentz | Debye | Drude | PECMedium
+)
+IsotropicUniformMediumType = IsotropicUniformMediumFor2DType | PMCMedium
+IsotropicCustomMediumType = (
+ CustomPoleResidue | CustomSellmeier | CustomLorentz | CustomDebye | CustomDrude
+)
+IsotropicCustomMediumInternalType = IsotropicCustomMediumType | CustomIsotropicMedium
+IsotropicMediumType = IsotropicCustomMediumType | IsotropicUniformMediumType
class AnisotropicMedium(AbstractMedium):
@@ -6464,7 +6461,7 @@ def eps_comp(self, row: Axis, col: Axis, frequency: float) -> complex:
return self.components[field_name].eps_model(frequency)
def _eps_plot(
- self, frequency: float, eps_component: Optional[PermittivityComponent] = None
+ self, frequency: float, eps_component: PermittivityComponent | None = None
) -> float:
"""Returns real part of epsilon for plotting. A specific component of the epsilon tensor can
be selected for anisotropic medium.
@@ -6793,7 +6790,7 @@ def eps_comp(self, row: Axis, col: Axis, frequency: float) -> complex:
return AbstractMedium.eps_sigma_to_eps_complex(eps, sig, frequency)
def _eps_plot(
- self, frequency: float, eps_component: Optional[PermittivityComponent] = None
+ self, frequency: float, eps_component: PermittivityComponent | None = None
) -> float:
"""Returns real part of epsilon for plotting. A specific component of the epsilon tensor can
be selected for anisotropic medium.
@@ -6887,28 +6884,28 @@ class CustomAnisotropicMedium(AbstractCustomMedium, AnisotropicMedium):
* `Defining fully anisotropic materials <../../notebooks/FullyAnisotropic.html>`_
"""
- xx: Union[IsotropicCustomMediumType, CustomMedium] = pd.Field(
+ xx: IsotropicCustomMediumType | CustomMedium = pd.Field(
...,
title="XX Component",
description="Medium describing the xx-component of the diagonal permittivity tensor.",
discriminator=TYPE_TAG_STR,
)
- yy: Union[IsotropicCustomMediumType, CustomMedium] = pd.Field(
+ yy: IsotropicCustomMediumType | CustomMedium = pd.Field(
...,
title="YY Component",
description="Medium describing the yy-component of the diagonal permittivity tensor.",
discriminator=TYPE_TAG_STR,
)
- zz: Union[IsotropicCustomMediumType, CustomMedium] = pd.Field(
+ zz: IsotropicCustomMediumType | CustomMedium = pd.Field(
...,
title="ZZ Component",
description="Medium describing the zz-component of the diagonal permittivity tensor.",
discriminator=TYPE_TAG_STR,
)
- interp_method: Optional[InterpMethod] = pd.Field(
+ interp_method: InterpMethod | None = pd.Field(
None,
title="Interpolation method",
description="When the value is ``None`` each component will follow its own "
@@ -7030,8 +7027,8 @@ def eps_dataarray_freq(
def _eps_bounds(
self,
- frequency: Optional[float] = None,
- eps_component: Optional[PermittivityComponent] = None,
+ frequency: float | None = None,
+ eps_component: PermittivityComponent | None = None,
) -> tuple[float, float]:
"""Returns permittivity bounds for setting the color bounds when plotting.
@@ -7093,21 +7090,21 @@ class CustomAnisotropicMediumInternal(CustomAnisotropicMedium):
>>> anisotropic_dielectric = CustomAnisotropicMedium(xx=medium_xx, yy=medium_yy, zz=medium_zz)
"""
- xx: Union[IsotropicCustomMediumInternalType, CustomMedium] = pd.Field(
+ xx: IsotropicCustomMediumInternalType | CustomMedium = pd.Field(
...,
title="XX Component",
description="Medium describing the xx-component of the diagonal permittivity tensor.",
discriminator=TYPE_TAG_STR,
)
- yy: Union[IsotropicCustomMediumInternalType, CustomMedium] = pd.Field(
+ yy: IsotropicCustomMediumInternalType | CustomMedium = pd.Field(
...,
title="YY Component",
description="Medium describing the yy-component of the diagonal permittivity tensor.",
discriminator=TYPE_TAG_STR,
)
- zz: Union[IsotropicCustomMediumInternalType, CustomMedium] = pd.Field(
+ zz: IsotropicCustomMediumInternalType | CustomMedium = pd.Field(
...,
title="ZZ Component",
description="Medium describing the zz-component of the diagonal permittivity tensor.",
@@ -7131,7 +7128,7 @@ class AbstractPerturbationMedium(ABC, Tidy3dBaseModel):
"have an effect.",
)
- perturbation_spec: Optional[Union[PermittivityPerturbation, IndexPerturbation]] = pd.Field(
+ perturbation_spec: PermittivityPerturbation | IndexPerturbation | None = pd.Field(
None,
title="Perturbation Spec",
description="Specification of medium perturbation as one of predefined types.",
@@ -7145,7 +7142,7 @@ def perturbed_copy(
electron_density: CustomSpatialDataType = None,
hole_density: CustomSpatialDataType = None,
interp_method: InterpMethod = "linear",
- ) -> Union[AbstractMedium, AbstractCustomMedium]:
+ ) -> AbstractMedium | AbstractCustomMedium:
"""Sample perturbations on provided heat and/or charge data and create a custom medium.
Any of ``temperature``, ``electron_density``, and ``hole_density`` can be ``None``.
If all passed arguments are ``None`` then a non-custom medium is returned.
@@ -7184,9 +7181,9 @@ def perturbed_copy(
@classmethod
def from_unperturbed(
cls,
- medium: Union[Medium, DispersiveMedium],
+ medium: Medium | DispersiveMedium,
subpixel: bool = True,
- perturbation_spec: Union[PermittivityPerturbation, IndexPerturbation] = None,
+ perturbation_spec: PermittivityPerturbation | IndexPerturbation = None,
**kwargs,
) -> AbstractPerturbationMedium:
"""Construct a medium with pertubation models from an unpertubed one.
@@ -7244,14 +7241,14 @@ class PerturbationMedium(Medium, AbstractPerturbationMedium):
... )
"""
- permittivity_perturbation: Optional[ParameterPerturbation] = pd.Field(
+ permittivity_perturbation: ParameterPerturbation | None = pd.Field(
None,
title="Permittivity Perturbation",
description="List of heat and/or charge perturbations to permittivity.",
units=PERMITTIVITY,
)
- conductivity_perturbation: Optional[ParameterPerturbation] = pd.Field(
+ conductivity_perturbation: ParameterPerturbation | None = pd.Field(
None,
title="Permittivity Perturbation",
description="List of heat and/or charge perturbations to permittivity.",
@@ -7337,7 +7334,7 @@ def perturbed_copy(
electron_density: CustomSpatialDataType = None,
hole_density: CustomSpatialDataType = None,
interp_method: InterpMethod = "linear",
- ) -> Union[Medium, CustomMedium]:
+ ) -> Medium | CustomMedium:
"""Sample perturbations on provided heat and/or charge data and return 'CustomMedium'.
Any of temperature, electron_density, and hole_density can be 'None'. If all passed
arguments are 'None' then a 'Medium' object is returned. All provided fields must have
@@ -7460,7 +7457,7 @@ class PerturbationPoleResidue(PoleResidue, AbstractPerturbationMedium):
... )
"""
- eps_inf_perturbation: Optional[ParameterPerturbation] = pd.Field(
+ eps_inf_perturbation: ParameterPerturbation | None = pd.Field(
None,
title="Perturbation of Epsilon at Infinity",
description="Perturbations to relative permittivity at infinite frequency "
@@ -7468,9 +7465,9 @@ class PerturbationPoleResidue(PoleResidue, AbstractPerturbationMedium):
units=PERMITTIVITY,
)
- poles_perturbation: Optional[
- tuple[tuple[Optional[ParameterPerturbation], Optional[ParameterPerturbation]], ...]
- ] = pd.Field(
+ poles_perturbation: (
+ tuple[tuple[ParameterPerturbation | None, ParameterPerturbation | None], ...] | None
+ ) = pd.Field(
None,
title="Perturbations of Poles",
description="Perturbations to poles of the model.",
@@ -7548,7 +7545,7 @@ def perturbed_copy(
electron_density: CustomSpatialDataType = None,
hole_density: CustomSpatialDataType = None,
interp_method: InterpMethod = "linear",
- ) -> Union[PoleResidue, CustomPoleResidue]:
+ ) -> PoleResidue | CustomPoleResidue:
"""Sample perturbations on provided heat and/or charge data and return 'CustomPoleResidue'.
Any of temperature, electron_density, and hole_density can be 'None'. If all passed
arguments are 'None' then a 'PoleResidue' object is returned. All provided fields must have
@@ -7646,28 +7643,28 @@ def perturbed_copy(
# types of mediums that can be used in Simulation and Structures
-MediumType3D = Union[
- Medium,
- AnisotropicMedium,
- PECMedium,
- PMCMedium,
- PoleResidue,
- Sellmeier,
- Lorentz,
- Debye,
- Drude,
- FullyAnisotropicMedium,
- CustomMedium,
- CustomPoleResidue,
- CustomSellmeier,
- CustomLorentz,
- CustomDebye,
- CustomDrude,
- CustomAnisotropicMedium,
- PerturbationMedium,
- PerturbationPoleResidue,
- LossyMetalMedium,
-]
+MediumType3D = (
+ Medium
+ | AnisotropicMedium
+ | PECMedium
+ | PMCMedium
+ | PoleResidue
+ | Sellmeier
+ | Lorentz
+ | Debye
+ | Drude
+ | FullyAnisotropicMedium
+ | CustomMedium
+ | CustomPoleResidue
+ | CustomSellmeier
+ | CustomLorentz
+ | CustomDebye
+ | CustomDrude
+ | CustomAnisotropicMedium
+ | PerturbationMedium
+ | PerturbationPoleResidue
+ | LossyMetalMedium
+)
class Medium2D(AbstractMedium):
@@ -7731,7 +7728,7 @@ def _validate_inplane_pec(cls, val, values):
@classmethod
def _weighted_avg(
cls, meds: list[IsotropicUniformMediumFor2DType], weights: list[float]
- ) -> Union[PoleResidue, PECMedium]:
+ ) -> PoleResidue | PECMedium:
"""Average ``meds`` with weights ``weights``."""
eps_inf = 1
poles = []
@@ -8075,11 +8072,11 @@ def is_comp_pec_2d(self, comp: Axis, axis: Axis):
# types of mediums that can be used in Simulation and Structures
-MediumType = Union[MediumType3D, Medium2D, AnisotropicMediumFromMedium2D]
+MediumType = MediumType3D | Medium2D | AnisotropicMediumFromMedium2D
# Utility function
-def medium_from_nk(n: float, k: float, freq: float, **kwargs) -> Union[Medium, Lorentz]:
+def medium_from_nk(n: float, k: float, freq: float, **kwargs) -> Medium | Lorentz:
"""Convert ``n`` and ``k`` values at frequency ``freq`` to :class:`.Medium` if ``Re[epsilon]>=1``,
or :class:`Lorentz` if if ``Re[epsilon]<1``.
diff --git a/tidy3d/components/microwave/data/monitor_data.py b/tidy3d/components/microwave/data/monitor_data.py
index 0a31435689..6fce884c06 100644
--- a/tidy3d/components/microwave/data/monitor_data.py
+++ b/tidy3d/components/microwave/data/monitor_data.py
@@ -4,8 +4,6 @@
from __future__ import annotations
-from typing import Optional
-
import pydantic.v1 as pd
import xarray as xr
@@ -123,7 +121,7 @@ def reflection_efficiency(self) -> FreqDataArray:
return reflection_efficiency
def partial_gain(
- self, pol_basis: PolarizationBasis = "linear", tilt_angle: Optional[float] = None
+ self, pol_basis: PolarizationBasis = "linear", tilt_angle: float | None = None
) -> xr.Dataset:
"""The partial gain figures of merit for antennas. The partial gains are computed
in the ``linear`` or ``circular`` polarization bases. If ``tilt_angle`` is not ``None``,
@@ -165,7 +163,7 @@ def gain(self) -> FieldProjectionAngleDataArray:
return partial_G.Gtheta + partial_G.Gphi
def partial_realized_gain(
- self, pol_basis: PolarizationBasis = "linear", tilt_angle: Optional[float] = None
+ self, pol_basis: PolarizationBasis = "linear", tilt_angle: float | None = None
) -> xr.Dataset:
"""The partial realized gain figures of merit for antennas. The partial gains are computed
in the ``linear`` or ``circular`` polarization bases. If ``tilt_angle`` is not ``None``,
@@ -264,7 +262,7 @@ class MicrowaveModeData(ModeData, MicrowaveBaseModel):
..., title="Monitor", description="Mode monitor associated with the data."
)
- transmission_line_data: Optional[TransmissionLineDataset] = pd.Field(
+ transmission_line_data: TransmissionLineDataset | None = pd.Field(
None,
title="Transmission Line Data",
description="Additional data relevant to transmission lines in RF and microwave applications, "
diff --git a/tidy3d/components/microwave/impedance_calculator.py b/tidy3d/components/microwave/impedance_calculator.py
index 8f80c57320..f912abdbbe 100644
--- a/tidy3d/components/microwave/impedance_calculator.py
+++ b/tidy3d/components/microwave/impedance_calculator.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Optional, Union
-
import numpy as np
import pydantic.v1 as pd
@@ -33,10 +31,10 @@
from tidy3d.components.monitor import ModeMonitor, ModeSolverMonitor
from tidy3d.exceptions import ValidationError
-VoltageIntegralType = Union[AxisAlignedVoltageIntegral, Custom2DVoltageIntegral]
-CurrentIntegralType = Union[
- AxisAlignedCurrentIntegral, Custom2DCurrentIntegral, CompositeCurrentIntegral
-]
+VoltageIntegralType = AxisAlignedVoltageIntegral | Custom2DVoltageIntegral
+CurrentIntegralType = (
+ AxisAlignedCurrentIntegral | Custom2DCurrentIntegral | CompositeCurrentIntegral
+)
class ImpedanceCalculator(MicrowaveBaseModel):
@@ -67,13 +65,13 @@ class ImpedanceCalculator(MicrowaveBaseModel):
>>> _ = ImpedanceCalculator(voltage_integral=v_int)
"""
- voltage_integral: Optional[VoltageIntegralType] = pd.Field(
+ voltage_integral: VoltageIntegralType | None = pd.Field(
None,
title="Voltage Integral",
description="Definition of path integral for computing voltage.",
)
- current_integral: Optional[CurrentIntegralType] = pd.Field(
+ current_integral: CurrentIntegralType | None = pd.Field(
None,
title="Current Integral",
description="Definition of contour integral for computing current.",
@@ -81,10 +79,10 @@ class ImpedanceCalculator(MicrowaveBaseModel):
def compute_impedance(
self, em_field: IntegrableMonitorDataType, return_voltage_and_current=False
- ) -> Union[
- ImpedanceResultType,
- tuple[ImpedanceResultType, VoltageIntegralResultType, CurrentIntegralResultType],
- ]:
+ ) -> (
+ ImpedanceResultType
+ | tuple[ImpedanceResultType, VoltageIntegralResultType, CurrentIntegralResultType]
+ ):
"""Compute impedance for the supplied ``em_field`` using ``voltage_integral`` and
``current_integral``. If only a single integral has been defined, impedance is
computed using the total flux in ``em_field``.
diff --git a/tidy3d/components/microwave/mode_spec.py b/tidy3d/components/microwave/mode_spec.py
index ee7dcddeb6..0e6c5dad83 100644
--- a/tidy3d/components/microwave/mode_spec.py
+++ b/tidy3d/components/microwave/mode_spec.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Optional, Union
-
import pydantic.v1 as pd
from tidy3d.components.base import cached_property
@@ -46,10 +44,9 @@ class MicrowaveModeSpec(AbstractModeSpec, MicrowaveBaseModel):
... )
"""
- impedance_specs: Union[
- annotate_type(ImpedanceSpecType),
- tuple[Optional[annotate_type(ImpedanceSpecType)], ...],
- ] = pd.Field(
+ impedance_specs: (
+ annotate_type(ImpedanceSpecType) | tuple[annotate_type(ImpedanceSpecType) | None, ...]
+ ) = pd.Field(
default_factory=AutoImpedanceSpec._default_without_license_warning,
title="Impedance Specifications",
description="Field controls how the impedance is calculated for each mode calculated by the mode solver. "
@@ -60,9 +57,9 @@ class MicrowaveModeSpec(AbstractModeSpec, MicrowaveBaseModel):
)
@cached_property
- def _impedance_specs_as_tuple(self) -> tuple[Optional[ImpedanceSpecType]]:
+ def _impedance_specs_as_tuple(self) -> tuple[ImpedanceSpecType | None]:
"""Gets the impedance_specs field converted to a tuple."""
- if isinstance(self.impedance_specs, Union[tuple, list]):
+ if isinstance(self.impedance_specs, tuple | list):
return tuple(self.impedance_specs)
return (self.impedance_specs,)
@@ -79,7 +76,7 @@ def check_impedance_specs_consistent_with_num_modes(cls, val, values):
"""Check that the number of impedance specifications is equal to the number of modes.
A single impedance spec is also permitted."""
num_modes = values.get("num_modes")
- if isinstance(val, Union[tuple, list]):
+ if isinstance(val, tuple | list):
num_impedance_specs = len(val)
else:
return val
diff --git a/tidy3d/components/microwave/path_integrals/factory.py b/tidy3d/components/microwave/path_integrals/factory.py
index 9100dcc206..a1df88a62e 100644
--- a/tidy3d/components/microwave/path_integrals/factory.py
+++ b/tidy3d/components/microwave/path_integrals/factory.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Optional
-
from tidy3d.components.microwave.impedance_calculator import (
CurrentIntegralType,
VoltageIntegralType,
@@ -89,8 +87,8 @@ def make_current_integral(path_spec: CurrentPathSpecType) -> CurrentIntegralType
def make_path_integrals(
- microwave_mode_spec: MicrowaveModeSpec, auto_spec: Optional[CustomImpedanceSpec] = None
-) -> tuple[tuple[Optional[VoltageIntegralType]], tuple[Optional[CurrentIntegralType]]]:
+ microwave_mode_spec: MicrowaveModeSpec, auto_spec: CustomImpedanceSpec | None = None
+) -> tuple[tuple[VoltageIntegralType | None], tuple[CurrentIntegralType | None]]:
"""
Given a microwave mode specification and monitor, create the voltage and
current path integrals used for the impedance computation.
diff --git a/tidy3d/components/microwave/path_integrals/integrals/base.py b/tidy3d/components/microwave/path_integrals/integrals/base.py
index 04fa762596..4db2d6e87e 100644
--- a/tidy3d/components/microwave/path_integrals/integrals/base.py
+++ b/tidy3d/components/microwave/path_integrals/integrals/base.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-from typing import Literal, Union
+from typing import Literal
import numpy as np
import xarray as xr
@@ -22,8 +22,8 @@
from tidy3d.constants import fp_eps
from tidy3d.exceptions import DataError
-IntegrableMonitorDataType = Union[FieldData, FieldTimeData, ModeData, ModeSolverData]
-EMScalarFieldType = Union[ScalarFieldDataArray, ScalarFieldTimeDataArray, ScalarModeFieldDataArray]
+IntegrableMonitorDataType = FieldData | FieldTimeData | ModeData | ModeSolverData
+EMScalarFieldType = ScalarFieldDataArray | ScalarFieldTimeDataArray | ScalarModeFieldDataArray
FieldParameter = Literal["E", "H"]
diff --git a/tidy3d/components/microwave/path_integrals/integrals/current.py b/tidy3d/components/microwave/path_integrals/integrals/current.py
index d6ffddd332..d045e77a7b 100644
--- a/tidy3d/components/microwave/path_integrals/integrals/current.py
+++ b/tidy3d/components/microwave/path_integrals/integrals/current.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Union
-
import numpy as np
import xarray as xr
@@ -133,7 +131,7 @@ class CompositeCurrentIntegral(CompositeCurrentIntegralSpec):
@cached_property
def current_integrals(
self,
- ) -> tuple[Union[AxisAlignedCurrentIntegral, Custom2DCurrentIntegral], ...]:
+ ) -> tuple[AxisAlignedCurrentIntegral | Custom2DCurrentIntegral, ...]:
""" "Collection of closed current path integrals."""
from tidy3d.components.microwave.path_integrals.factory import (
make_current_integral,
@@ -218,7 +216,7 @@ def compute_current(self, em_field: IntegrableMonitorDataType) -> IntegralResult
def _check_phase_sign_consistency(
self,
- phase_difference: Union[FreqDataArray, FreqModeDataArray],
+ phase_difference: FreqDataArray | FreqModeDataArray,
) -> bool:
"""
Check that the provided current data has a consistent phase with respect to the reference
@@ -260,8 +258,8 @@ def _check_phase_sign_consistency(
def _check_phase_amplitude_consistency(
self,
- current_in_phase: Union[FreqDataArray, FreqModeDataArray],
- current_out_phase: Union[FreqDataArray, FreqModeDataArray],
+ current_in_phase: FreqDataArray | FreqModeDataArray,
+ current_out_phase: FreqDataArray | FreqModeDataArray,
) -> bool:
"""
Check that the summed in phase and out of phase components of current have a consistent relative amplitude.
diff --git a/tidy3d/components/microwave/path_integrals/specs/current.py b/tidy3d/components/microwave/path_integrals/specs/current.py
index 8447902e47..8030ef522b 100644
--- a/tidy3d/components/microwave/path_integrals/specs/current.py
+++ b/tidy3d/components/microwave/path_integrals/specs/current.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-from typing import Literal, Optional, Union
+from typing import Literal
import numpy as np
import pydantic.v1 as pd
@@ -147,9 +147,9 @@ def _to_path_integral_specs(
@add_ax_if_none
def plot(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
**path_kwargs,
) -> Ax:
@@ -242,9 +242,9 @@ class Custom2DCurrentIntegralSpec(Custom2DPathIntegralSpec):
@add_ax_if_none
def plot(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
**path_kwargs,
) -> Ax:
@@ -310,12 +310,10 @@ class CompositeCurrentIntegralSpec(MicrowaveBaseModel):
... )
"""
- path_specs: tuple[Union[AxisAlignedCurrentIntegralSpec, Custom2DCurrentIntegralSpec], ...] = (
- pd.Field(
- ...,
- title="Path Specifications",
- description="Definition of the disjoint path specifications for each isolated contour integral.",
- )
+ path_specs: tuple[AxisAlignedCurrentIntegralSpec | Custom2DCurrentIntegralSpec, ...] = pd.Field(
+ ...,
+ title="Path Specifications",
+ description="Definition of the disjoint path specifications for each isolated contour integral.",
)
sum_spec: Literal["sum", "split"] = pd.Field(
@@ -331,9 +329,9 @@ class CompositeCurrentIntegralSpec(MicrowaveBaseModel):
@add_ax_if_none
def plot(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
**path_kwargs,
) -> Ax:
diff --git a/tidy3d/components/microwave/path_integrals/specs/impedance.py b/tidy3d/components/microwave/path_integrals/specs/impedance.py
index cdffbdffba..28fb43d727 100644
--- a/tidy3d/components/microwave/path_integrals/specs/impedance.py
+++ b/tidy3d/components/microwave/path_integrals/specs/impedance.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Optional, Union
-
import pydantic.v1 as pd
from tidy3d.components.base import skip_if_fields_missing
@@ -50,13 +48,13 @@ class CustomImpedanceSpec(MicrowaveBaseModel):
... )
"""
- voltage_spec: Optional[VoltagePathSpecType] = pd.Field(
+ voltage_spec: VoltagePathSpecType | None = pd.Field(
None,
title="Voltage Integration Path",
description="Path specification for computing the voltage associated with a mode profile.",
)
- current_spec: Optional[CurrentPathSpecType] = pd.Field(
+ current_spec: CurrentPathSpecType | None = pd.Field(
None,
title="Current Integration Path",
description="Path specification for computing the current associated with a mode profile.",
@@ -79,4 +77,4 @@ def check_path_spec_combinations(cls, val, values):
return val
-ImpedanceSpecType = Union[AutoImpedanceSpec, CustomImpedanceSpec]
+ImpedanceSpecType = AutoImpedanceSpec | CustomImpedanceSpec
diff --git a/tidy3d/components/microwave/path_integrals/specs/voltage.py b/tidy3d/components/microwave/path_integrals/specs/voltage.py
index 340940f20b..6bb0a9cd85 100644
--- a/tidy3d/components/microwave/path_integrals/specs/voltage.py
+++ b/tidy3d/components/microwave/path_integrals/specs/voltage.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Optional
-
import numpy as np
import pydantic.v1 as pd
from typing_extensions import Self
@@ -38,9 +36,9 @@ def from_terminal_positions(
cls,
plus_terminal: float,
minus_terminal: float,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
extrapolate_to_endpoints: bool = True,
snap_path_to_grid: bool = True,
) -> Self:
@@ -94,9 +92,9 @@ def from_terminal_positions(
@add_ax_if_none
def plot(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
**path_kwargs,
) -> Ax:
@@ -159,9 +157,9 @@ class Custom2DVoltageIntegralSpec(Custom2DPathIntegralSpec):
@add_ax_if_none
def plot(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
**path_kwargs,
) -> Ax:
diff --git a/tidy3d/components/microwave/path_integrals/types.py b/tidy3d/components/microwave/path_integrals/types.py
index 0bfda5285b..9f7095a282 100644
--- a/tidy3d/components/microwave/path_integrals/types.py
+++ b/tidy3d/components/microwave/path_integrals/types.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Union
-
from tidy3d.components.microwave.path_integrals.specs.current import (
AxisAlignedCurrentIntegralSpec,
CompositeCurrentIntegralSpec,
@@ -14,7 +12,7 @@
Custom2DVoltageIntegralSpec,
)
-VoltagePathSpecType = Union[AxisAlignedVoltageIntegralSpec, Custom2DVoltageIntegralSpec]
-CurrentPathSpecType = Union[
- AxisAlignedCurrentIntegralSpec, Custom2DCurrentIntegralSpec, CompositeCurrentIntegralSpec
-]
+VoltagePathSpecType = AxisAlignedVoltageIntegralSpec | Custom2DVoltageIntegralSpec
+CurrentPathSpecType = (
+ AxisAlignedCurrentIntegralSpec | Custom2DCurrentIntegralSpec | CompositeCurrentIntegralSpec
+)
diff --git a/tidy3d/components/mode/data/sim_data.py b/tidy3d/components/mode/data/sim_data.py
index 7647d220e7..f906dd3e4a 100644
--- a/tidy3d/components/mode/data/sim_data.py
+++ b/tidy3d/components/mode/data/sim_data.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-from typing import Literal, Optional, Union
+from typing import Literal
import pydantic.v1 as pd
@@ -14,7 +14,7 @@
from tidy3d.components.types import TYPE_TAG_STR, Ax, PlotScale
from tidy3d.components.types.monitor_data import ModeSolverDataType
-ModeSimulationMonitorDataType = Union[PermittivityData, MediumData]
+ModeSimulationMonitorDataType = PermittivityData | MediumData
class ModeSimulationData(AbstractYeeGridSimulationData):
@@ -50,8 +50,8 @@ def plot_field(
scale: PlotScale = "lin",
eps_alpha: float = 0.2,
robust: bool = True,
- vmin: Optional[float] = None,
- vmax: Optional[float] = None,
+ vmin: float | None = None,
+ vmax: float | None = None,
ax: Ax = None,
**sel_kwargs,
) -> Ax:
diff --git a/tidy3d/components/mode/mode_solver.py b/tidy3d/components/mode/mode_solver.py
index f703ca812e..e9d95cbe95 100644
--- a/tidy3d/components/mode/mode_solver.py
+++ b/tidy3d/components/mode/mode_solver.py
@@ -6,7 +6,7 @@
from functools import wraps
from math import isclose
-from typing import Literal, Optional, Union, get_args
+from typing import Literal, get_args
import numpy as np
import pydantic.v1 as pydantic
@@ -101,9 +101,9 @@
# Maximum allowed size of the field data produced by the mode solver
MAX_MODES_DATA_SIZE_GB = 20
-MODE_SIMULATION_TYPE = Union[Simulation, EMESimulation]
-MODE_SIMULATION_DATA_TYPE = Union[SimulationData, EMESimulationData]
-MODE_PLANE_TYPE = Union[Box, ModeSource, ModeMonitor, ModeSolverMonitor]
+MODE_SIMULATION_TYPE = Simulation | EMESimulation
+MODE_SIMULATION_DATA_TYPE = SimulationData | EMESimulationData
+MODE_PLANE_TYPE = Box | ModeSource | ModeMonitor | ModeSolverMonitor
# When using ``angle_rotation`` without a bend, use a very large effective radius
EFFECTIVE_RADIUS_FACTOR = 10_000
@@ -756,7 +756,7 @@ def rotated_bend_center(self) -> list:
def _car_2_cyn(
self, mode_solver_data: ModeSolverData
- ) -> dict[Union[ScalarModeFieldCylindricalDataArray, ModeIndexDataArray]]:
+ ) -> dict[ScalarModeFieldCylindricalDataArray | ModeIndexDataArray]:
"""Convert cartesian fields to cylindrical fields centered at the
rotated bend center."""
@@ -956,9 +956,7 @@ def _car_2_cyn(
def _mode_rotation(
self,
- solver_ref_data_cylindrical: dict[
- Union[ScalarModeFieldCylindricalDataArray, ModeIndexDataArray]
- ],
+ solver_ref_data_cylindrical: dict[ScalarModeFieldCylindricalDataArray | ModeIndexDataArray],
solver: ModeSolver,
) -> ModeSolverData:
"""Rotate the mode solver solution from the reference plane in cylindrical coordinates
@@ -1392,7 +1390,7 @@ def _filter_polarization(self, mode_solver_data: ModeSolverData):
def _make_path_integrals(
self,
- ) -> tuple[tuple[Optional[VoltageIntegralType]], tuple[Optional[CurrentIntegralType]]]:
+ ) -> tuple[tuple[VoltageIntegralType | None], tuple[CurrentIntegralType | None]]:
"""Wrapper for making path integrals from the MicrowaveModeSpec. Note: overriden in the backend to support
auto creation of path integrals."""
if not self._has_microwave_mode_spec:
@@ -2004,9 +2002,7 @@ def to_source(
**kwargs,
)
- def to_monitor(
- self, freqs: Optional[list[float]] = None, name: Optional[str] = None
- ) -> ModeMonitor:
+ def to_monitor(self, freqs: list[float] | None = None, name: str | None = None) -> ModeMonitor:
"""Creates :class:`ModeMonitor` from a :class:`.ModeSolver` instance plus additional
specifications.
@@ -2048,9 +2044,7 @@ def to_monitor(
name=name,
)
- def to_mode_solver_monitor(
- self, name: str, colocate: Optional[bool] = None
- ) -> ModeSolverMonitor:
+ def to_mode_solver_monitor(self, name: str, colocate: bool | None = None) -> ModeSolverMonitor:
"""Creates :class:`ModeSolverMonitor` from a :class:`.ModeSolver` instance.
Parameters
@@ -2123,8 +2117,8 @@ def sim_with_source(
@require_fdtd_simulation
def sim_with_monitor(
self,
- freqs: Optional[list[float]] = None,
- name: Optional[str] = None,
+ freqs: list[float] | None = None,
+ name: str | None = None,
) -> Simulation:
"""Creates :class:`.Simulation` from a :class:`.ModeSolver`. Creates a copy of
the ModeSolver's original simulation with a mode monitor added corresponding to
@@ -2181,8 +2175,8 @@ def plot_field(
scale: PlotScale = "lin",
eps_alpha: float = 0.2,
robust: bool = True,
- vmin: Optional[float] = None,
- vmax: Optional[float] = None,
+ vmin: float | None = None,
+ vmax: float | None = None,
ax: Ax = None,
**sel_kwargs,
) -> Ax:
@@ -2241,8 +2235,8 @@ def plot_field(
def plot(
self,
ax: Ax = None,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
fill_structures: bool = True,
**patch_kwargs,
) -> Ax:
@@ -2299,8 +2293,8 @@ def plot(
def plot_eps(
self,
- freq: Optional[float] = None,
- alpha: Optional[float] = None,
+ freq: float | None = None,
+ alpha: float | None = None,
ax: Ax = None,
) -> Ax:
"""Plot the mode plane simulation's components.
@@ -2353,8 +2347,8 @@ def plot_eps(
def plot_structures_eps(
self,
- freq: Optional[float] = None,
- alpha: Optional[float] = None,
+ freq: float | None = None,
+ alpha: float | None = None,
cbar: bool = True,
reverse: bool = False,
ax: Ax = None,
diff --git a/tidy3d/components/mode/simulation.py b/tidy3d/components/mode/simulation.py
index d0190747af..c91aa9c9db 100644
--- a/tidy3d/components/mode/simulation.py
+++ b/tidy3d/components/mode/simulation.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Optional, Union
-
import numpy as np
import pydantic.v1 as pd
@@ -33,13 +31,13 @@
from .mode_solver import ModeSolver
-ModeSimulationMonitorType = Union[PermittivityMonitor, MediumMonitor]
+ModeSimulationMonitorType = PermittivityMonitor | MediumMonitor
# dummy run time for conversion to FDTD sim
# should be very small -- otherwise, generating tmesh will fail or take a long time
RUN_TIME = 1e-30
-MODE_PLANE_TYPE = Union[Box, ModeSource, ModeMonitor, ModeSolverMonitor]
+MODE_PLANE_TYPE = Box | ModeSource | ModeMonitor | ModeSolverMonitor
# attributes shared between ModeSimulation class and ModeSolver class
@@ -319,7 +317,7 @@ def _as_fdtd_sim(self) -> Simulation:
def from_simulation(
cls,
simulation: AbstractYeeGridSimulation,
- wavelength: Optional[pd.PositiveFloat] = None,
+ wavelength: pd.PositiveFloat | None = None,
**kwargs,
) -> ModeSimulation:
"""Creates :class:`.ModeSimulation` from a :class:`.AbstractYeeGridSimulation`.
@@ -376,7 +374,7 @@ def reduced_simulation_copy(self) -> ModeSimulation:
@classmethod
def from_mode_solver(
- cls, mode_solver: ModeSolver, wavelength: Optional[pd.PositiveFloat] = None
+ cls, mode_solver: ModeSolver, wavelength: pd.PositiveFloat | None = None
) -> ModeSimulation:
"""Creates :class:`.ModeSimulation` from a :class:`.ModeSolver`.
@@ -403,15 +401,15 @@ def from_mode_solver(
def plot(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
- source_alpha: Optional[float] = 0,
- monitor_alpha: Optional[float] = 0,
- lumped_element_alpha: Optional[float] = 0,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
+ source_alpha: float | None = 0,
+ monitor_alpha: float | None = 0,
+ lumped_element_alpha: float | None = 0,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
fill_structures: bool = True,
**patch_kwargs,
) -> Ax:
@@ -498,8 +496,8 @@ def plot_mode_plane(
def plot_eps_mode_plane(
self,
- freq: Optional[float] = None,
- alpha: Optional[float] = None,
+ freq: float | None = None,
+ alpha: float | None = None,
ax: Ax = None,
) -> Ax:
"""Plot the mode plane simulation's components.
@@ -531,8 +529,8 @@ def plot_eps_mode_plane(
def plot_structures_eps_mode_plane(
self,
- freq: Optional[float] = None,
- alpha: Optional[float] = None,
+ freq: float | None = None,
+ alpha: float | None = None,
cbar: bool = True,
reverse: bool = False,
ax: Ax = None,
diff --git a/tidy3d/components/mode/solver.py b/tidy3d/components/mode/solver.py
index afb4fc9671..4823100e8a 100644
--- a/tidy3d/components/mode/solver.py
+++ b/tidy3d/components/mode/solver.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-from typing import TYPE_CHECKING, Optional
+from typing import TYPE_CHECKING
import numpy as np
@@ -53,7 +53,7 @@ def compute_modes(
symmetry=(0, 0),
direction="+",
solver_basis_fields=None,
- plane_center: Optional[tuple[float, float]] = None,
+ plane_center: tuple[float, float] | None = None,
) -> tuple[Numpy, Numpy, EpsSpecType]:
"""
Solve for the modes of a waveguide cross-section.
diff --git a/tidy3d/components/mode_spec.py b/tidy3d/components/mode_spec.py
index c0d37a0bb1..a70d2694f8 100644
--- a/tidy3d/components/mode_spec.py
+++ b/tidy3d/components/mode_spec.py
@@ -4,7 +4,7 @@
from abc import ABC
from math import isclose
-from typing import Literal, Optional, Union
+from typing import Literal
import numpy as np
import pydantic.v1 as pd
@@ -39,7 +39,7 @@ class ModeSortSpec(Tidy3dBaseModel):
"""
# Filtering stage
- filter_key: Optional[MODE_DATA_KEYS] = pd.Field(
+ filter_key: MODE_DATA_KEYS | None = pd.Field(
None,
title="Filtering key",
description="Quantity used to filter modes into two groups before sorting.",
@@ -56,13 +56,13 @@ class ModeSortSpec(Tidy3dBaseModel):
)
# Sorting stage
- sort_key: Optional[MODE_DATA_KEYS] = pd.Field(
+ sort_key: MODE_DATA_KEYS | None = pd.Field(
None,
title="Sorting key",
description="Quantity used to sort modes within each filtered group. If ``None``, "
"sorting is by descending effective index.",
)
- sort_reference: Optional[float] = pd.Field(
+ sort_reference: float | None = pd.Field(
None,
title="Sorting reference",
description=(
@@ -76,7 +76,7 @@ class ModeSortSpec(Tidy3dBaseModel):
)
# Frequency tracking - applied after sorting and filtering
- track_freq: Optional[TrackFreq] = pd.Field(
+ track_freq: TrackFreq | None = pd.Field(
"central",
title="Tracking base frequency",
description="If provided, enables cross-frequency mode tracking. Can be 'lowest', "
@@ -176,13 +176,13 @@ class AbstractModeSpec(Tidy3dBaseModel, ABC):
"Note: currently only supported when 'angle_phi' is a multiple of 'np.pi'.",
)
- track_freq: Optional[TrackFreq] = pd.Field(
+ track_freq: TrackFreq | None = pd.Field(
None,
title="Mode Tracking Frequency (deprecated)",
description="Deprecated. Use 'sort_spec.track_freq' instead.",
)
- group_index_step: Union[pd.PositiveFloat, bool] = pd.Field(
+ group_index_step: pd.PositiveFloat | bool = pd.Field(
False,
title="Frequency step for group index computation",
description="Control the computation of the group index alongside the effective index. If "
@@ -311,7 +311,7 @@ def _track_freq_deprecated(cls, val):
return val
@property
- def _track_freq(self) -> Optional[TrackFreq]:
+ def _track_freq(self) -> TrackFreq | None:
"""Private resolver for tracking frequency: prefers ModeSpec.track_freq if set,
otherwise falls back to ModeSortSpec.track_freq."""
if self.track_freq is not None:
diff --git a/tidy3d/components/monitor.py b/tidy3d/components/monitor.py
index 64386a5d09..08e1b2ee62 100644
--- a/tidy3d/components/monitor.py
+++ b/tidy3d/components/monitor.py
@@ -3,7 +3,7 @@
from __future__ import annotations
from abc import ABC, abstractmethod
-from typing import Literal, Optional
+from typing import Literal
import numpy as np
import pydantic.v1 as pydantic
@@ -360,9 +360,9 @@ class AbstractModeMonitor(PlanarMonitor, FreqMonitor):
def plot(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
**patch_kwargs,
) -> Ax:
diff --git a/tidy3d/components/parameter_perturbation.py b/tidy3d/components/parameter_perturbation.py
index 8b4cf51392..c427676e35 100644
--- a/tidy3d/components/parameter_perturbation.py
+++ b/tidy3d/components/parameter_perturbation.py
@@ -4,7 +4,7 @@
import functools
from abc import ABC, abstractmethod
-from typing import Callable, Optional, Union
+from collections.abc import Callable
import numpy as np
import pydantic.v1 as pd
@@ -41,7 +41,7 @@ class AbstractPerturbation(ABC, Tidy3dBaseModel):
@cached_property
@abstractmethod
- def perturbation_range(self) -> Union[tuple[float, float], tuple[Complex, Complex]]:
+ def perturbation_range(self) -> tuple[float, float] | tuple[Complex, Complex]:
"""Perturbation range."""
@cached_property
@@ -50,7 +50,7 @@ def is_complex(self) -> bool:
"""Whether perturbation is complex valued."""
@staticmethod
- def _linear_range(interval: tuple[float, float], ref: float, coeff: Union[float, Complex]):
+ def _linear_range(interval: tuple[float, float], ref: float, coeff: float | Complex):
"""Find value range for a linear perturbation."""
if coeff in (0, 0j): # to avoid 0*inf
return np.array([0, 0])
@@ -58,8 +58,8 @@ def _linear_range(interval: tuple[float, float], ref: float, coeff: Union[float,
@staticmethod
def _get_val(
- field: Union[ArrayLike[float], ArrayLike[Complex], CustomSpatialDataType], val: FieldVal
- ) -> Union[ArrayLike[float], ArrayLike[Complex], CustomSpatialDataType]:
+ field: ArrayLike[float] | ArrayLike[Complex] | CustomSpatialDataType, val: FieldVal
+ ) -> ArrayLike[float] | ArrayLike[Complex] | CustomSpatialDataType:
"""Get specified value from a field."""
if val == "real":
@@ -88,19 +88,19 @@ def _get_val(
def ensure_temp_in_range(
sample: Callable[
- Union[ArrayLike[float], CustomSpatialDataType],
- Union[ArrayLike[float], ArrayLike[Complex], CustomSpatialDataType],
+ ArrayLike[float] | CustomSpatialDataType,
+ ArrayLike[float] | ArrayLike[Complex] | CustomSpatialDataType,
],
) -> Callable[
- Union[ArrayLike[float], CustomSpatialDataType],
- Union[ArrayLike[float], ArrayLike[Complex], CustomSpatialDataType],
+ ArrayLike[float] | CustomSpatialDataType,
+ ArrayLike[float] | ArrayLike[Complex] | CustomSpatialDataType,
]:
"""Decorate ``sample`` to log warning if temperature supplied is out of bounds."""
@functools.wraps(sample)
def _sample(
- self, temperature: Union[ArrayLike[float], CustomSpatialDataType]
- ) -> Union[ArrayLike[float], ArrayLike[Complex], CustomSpatialDataType]:
+ self, temperature: ArrayLike[float] | CustomSpatialDataType
+ ) -> ArrayLike[float] | ArrayLike[Complex] | CustomSpatialDataType:
"""New sample function."""
if np.iscomplexobj(temperature):
@@ -130,8 +130,8 @@ class HeatPerturbation(AbstractPerturbation):
@abstractmethod
def sample(
- self, temperature: Union[ArrayLike[float], CustomSpatialDataType]
- ) -> Union[ArrayLike[float], ArrayLike[Complex], CustomSpatialDataType]:
+ self, temperature: ArrayLike[float] | CustomSpatialDataType
+ ) -> ArrayLike[float] | ArrayLike[Complex] | CustomSpatialDataType:
"""Sample perturbation.
Parameters
@@ -231,7 +231,7 @@ class LinearHeatPerturbation(HeatPerturbation):
units=KELVIN,
)
- coeff: Union[float, Complex] = pd.Field(
+ coeff: float | Complex = pd.Field(
...,
title="Thermo-optic Coefficient",
description="Sensitivity (derivative) of perturbation with respect to temperature.",
@@ -239,14 +239,14 @@ class LinearHeatPerturbation(HeatPerturbation):
)
@cached_property
- def perturbation_range(self) -> Union[tuple[float, float], tuple[Complex, Complex]]:
+ def perturbation_range(self) -> tuple[float, float] | tuple[Complex, Complex]:
"""Range of possible perturbation values in the provided ``temperature_range``."""
return self._linear_range(self.temperature_range, self.temperature_ref, self.coeff)
@ensure_temp_in_range
def sample(
- self, temperature: Union[ArrayLike[float], CustomSpatialDataType]
- ) -> Union[ArrayLike[float], ArrayLike[Complex], CustomSpatialDataType]:
+ self, temperature: ArrayLike[float] | CustomSpatialDataType
+ ) -> ArrayLike[float] | ArrayLike[Complex] | CustomSpatialDataType:
"""Sample perturbation at temperature points.
Parameters
@@ -336,7 +336,7 @@ class CustomHeatPerturbation(HeatPerturbation):
_no_nans = validate_no_nans("perturbation_values")
@cached_property
- def perturbation_range(self) -> Union[tuple[float, float], tuple[Complex, Complex]]:
+ def perturbation_range(self) -> tuple[float, float] | tuple[Complex, Complex]:
"""Range of possible parameter perturbation values."""
return np.min(self.perturbation_values).item(), np.max(self.perturbation_values).item()
@@ -368,8 +368,8 @@ def compute_temperature_range(cls, values):
@ensure_temp_in_range
def sample(
- self, temperature: Union[ArrayLike[float], CustomSpatialDataType]
- ) -> Union[ArrayLike[float], ArrayLike[Complex], CustomSpatialDataType]:
+ self, temperature: ArrayLike[float] | CustomSpatialDataType
+ ) -> ArrayLike[float] | ArrayLike[Complex] | CustomSpatialDataType:
"""Sample perturbation at provided temperature points.
Parameters
@@ -418,7 +418,7 @@ def is_complex(self) -> bool:
return np.iscomplexobj(self.perturbation_values)
-HeatPerturbationType = Union[LinearHeatPerturbation, CustomHeatPerturbation]
+HeatPerturbationType = LinearHeatPerturbation | CustomHeatPerturbation
""" Elementary charge perturbation classes """
@@ -427,26 +427,26 @@ def is_complex(self) -> bool:
def ensure_charge_in_range(
sample: Callable[
[
- Union[ArrayLike[float], CustomSpatialDataType],
- Union[ArrayLike[float], CustomSpatialDataType],
+ ArrayLike[float] | CustomSpatialDataType,
+ ArrayLike[float] | CustomSpatialDataType,
],
- Union[ArrayLike[float], ArrayLike[Complex], CustomSpatialDataType],
+ ArrayLike[float] | ArrayLike[Complex] | CustomSpatialDataType,
],
) -> Callable[
[
- Union[ArrayLike[float], CustomSpatialDataType],
- Union[ArrayLike[float], CustomSpatialDataType],
+ ArrayLike[float] | CustomSpatialDataType,
+ ArrayLike[float] | CustomSpatialDataType,
],
- Union[ArrayLike[float], ArrayLike[Complex], CustomSpatialDataType],
+ ArrayLike[float] | ArrayLike[Complex] | CustomSpatialDataType,
]:
"""Decorate ``sample`` to log warning if charge supplied is out of bounds."""
@functools.wraps(sample)
def _sample(
self,
- electron_density: Union[ArrayLike[float], CustomSpatialDataType],
- hole_density: Union[ArrayLike[float], CustomSpatialDataType],
- ) -> Union[ArrayLike[float], ArrayLike[Complex], CustomSpatialDataType]:
+ electron_density: ArrayLike[float] | CustomSpatialDataType,
+ hole_density: ArrayLike[float] | CustomSpatialDataType,
+ ) -> ArrayLike[float] | ArrayLike[Complex] | CustomSpatialDataType:
"""New sample function."""
# disable complex input
@@ -498,9 +498,9 @@ class ChargePerturbation(AbstractPerturbation):
@abstractmethod
def sample(
self,
- electron_density: Union[ArrayLike[float], CustomSpatialDataType],
- hole_density: Union[ArrayLike[float], CustomSpatialDataType],
- ) -> Union[ArrayLike[float], ArrayLike[Complex], CustomSpatialDataType]:
+ electron_density: ArrayLike[float] | CustomSpatialDataType,
+ hole_density: ArrayLike[float] | CustomSpatialDataType,
+ ) -> ArrayLike[float] | ArrayLike[Complex] | CustomSpatialDataType:
"""Sample perturbation.
Parameters
@@ -661,7 +661,7 @@ class LinearChargePerturbation(ChargePerturbation):
)
@cached_property
- def perturbation_range(self) -> Union[tuple[float, float], tuple[Complex, Complex]]:
+ def perturbation_range(self) -> tuple[float, float] | tuple[Complex, Complex]:
"""Range of possible perturbation values within provided ``electron_range`` and
``hole_range``.
"""
@@ -676,9 +676,9 @@ def perturbation_range(self) -> Union[tuple[float, float], tuple[Complex, Comple
@ensure_charge_in_range
def sample(
self,
- electron_density: Union[ArrayLike[float], CustomSpatialDataType],
- hole_density: Union[ArrayLike[float], CustomSpatialDataType],
- ) -> Union[ArrayLike[float], ArrayLike[Complex], CustomSpatialDataType]:
+ electron_density: ArrayLike[float] | CustomSpatialDataType,
+ hole_density: ArrayLike[float] | CustomSpatialDataType,
+ ) -> ArrayLike[float] | ArrayLike[Complex] | CustomSpatialDataType:
"""Sample perturbation at electron and hole density points.
Parameters
@@ -822,7 +822,7 @@ class CustomChargePerturbation(ChargePerturbation):
_no_nans = validate_no_nans("perturbation_values")
@cached_property
- def perturbation_range(self) -> Union[tuple[float, float], tuple[complex, complex]]:
+ def perturbation_range(self) -> tuple[float, float] | tuple[complex, complex]:
"""Range of possible parameter perturbation values."""
return np.min(self.perturbation_values).item(), np.max(self.perturbation_values).item()
@@ -865,9 +865,9 @@ def compute_eh_ranges(cls, values):
@ensure_charge_in_range
def sample(
self,
- electron_density: Union[ArrayLike[float], CustomSpatialDataType],
- hole_density: Union[ArrayLike[float], CustomSpatialDataType],
- ) -> Union[ArrayLike[float], ArrayLike[Complex], CustomSpatialDataType]:
+ electron_density: ArrayLike[float] | CustomSpatialDataType,
+ hole_density: ArrayLike[float] | CustomSpatialDataType,
+ ) -> ArrayLike[float] | ArrayLike[Complex] | CustomSpatialDataType:
"""Sample perturbation at electron and hole density points.
Parameters
@@ -970,9 +970,9 @@ def is_complex(self) -> bool:
return np.iscomplexobj(self.perturbation_values)
-ChargePerturbationType = Union[LinearChargePerturbation, CustomChargePerturbation]
+ChargePerturbationType = LinearChargePerturbation | CustomChargePerturbation
-PerturbationType = Union[HeatPerturbationType, ChargePerturbationType]
+PerturbationType = HeatPerturbationType | ChargePerturbationType
class ParameterPerturbation(Tidy3dBaseModel):
@@ -1037,7 +1037,7 @@ def perturbation_list(self) -> list[PerturbationType]:
return perturb_list
@cached_property
- def perturbation_range(self) -> Union[tuple[float, float], tuple[Complex, Complex]]:
+ def perturbation_range(self) -> tuple[float, float] | tuple[Complex, Complex]:
"""Range of possible parameter perturbation values due to both heat and charge effects."""
prange = np.zeros(2)
@@ -1158,13 +1158,13 @@ class PermittivityPerturbation(Tidy3dBaseModel):
>>> permittivity_pb = PermittivityPerturbation(delta_eps=delta_eps, delta_sigma=delta_sigma)
"""
- delta_eps: Optional[ParameterPerturbation] = pd.Field(
+ delta_eps: ParameterPerturbation | None = pd.Field(
None,
title="Permittivity Perturbation",
description="Perturbation model for permittivity.",
)
- delta_sigma: Optional[ParameterPerturbation] = pd.Field(
+ delta_sigma: ParameterPerturbation | None = pd.Field(
None,
title="Conductivity Perturbation",
description="Perturbation model for conductivity.",
@@ -1647,13 +1647,13 @@ class IndexPerturbation(Tidy3dBaseModel):
>>> index_pb = IndexPerturbation(delta_n=dn_pb, delta_k=dk_pb, freq=C_0)
"""
- delta_n: Optional[ParameterPerturbation] = pd.Field(
+ delta_n: ParameterPerturbation | None = pd.Field(
None,
title="Refractive Index Perturbation",
description="Perturbation of the real part of refractive index.",
)
- delta_k: Optional[ParameterPerturbation] = pd.Field(
+ delta_k: ParameterPerturbation | None = pd.Field(
None,
title="Exctinction Coefficient Perturbation",
description="Perturbation of the imaginary part of refractive index.",
diff --git a/tidy3d/components/scene.py b/tidy3d/components/scene.py
index 85b643188c..3cc78056e2 100644
--- a/tidy3d/components/scene.py
+++ b/tidy3d/components/scene.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-from typing import Literal, Optional, Union
+from typing import Literal
import autograd.numpy as np
@@ -138,7 +138,7 @@ class Scene(Tidy3dBaseModel):
"`PECMedium` to 100, and others to 0.",
)
- plot_length_units: Optional[LengthUnit] = pd.Field(
+ plot_length_units: LengthUnit | None = pd.Field(
"μm",
title="Plot Units",
description="When set to a supported ``LengthUnit``, "
@@ -421,11 +421,11 @@ def intersecting_structures(
@staticmethod
def _get_plot_lims(
bounds: Bound,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
) -> tuple[tuple[float, float], tuple[float, float]]:
# if no hlim and/or vlim given, the bounds will then be the usual pml bounds
axis, _ = Box.parse_xyz_kwargs(x=x, y=y, z=z)
@@ -456,12 +456,12 @@ def _get_plot_lims(
@add_ax_if_none
def plot(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
fill_structures: bool = True,
**patch_kwargs,
) -> Ax:
@@ -500,12 +500,12 @@ def plot(
@add_ax_if_none
def plot_structures(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
fill: bool = True,
) -> Ax:
"""Plot each of scene's structures on a plane defined by one nonzero x,y,z coordinate.
@@ -657,11 +657,11 @@ def _add_cbar(
def _set_plot_bounds(
bounds: Bound,
ax: Ax,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
) -> Ax:
"""Sets the xy limits of the scene at a plane, useful after plotting.
@@ -693,11 +693,11 @@ def _set_plot_bounds(
def _get_structures_2dbox(
self,
structures: list[Structure],
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
) -> list[tuple[Medium, Shapely]]:
"""Compute list of shapes to plot on 2d box specified by (x_min, x_max), (y_min, y_max).
@@ -809,15 +809,15 @@ def _filter_structures_plane(
@add_ax_if_none
def plot_eps(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
- freq: Optional[float] = None,
- alpha: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
+ freq: float | None = None,
+ alpha: float | None = None,
ax: Ax = None,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
- eps_lim: tuple[Union[float, None], Union[float, None]] = (None, None),
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
+ eps_lim: tuple[float | None, float | None] = (None, None),
scale: PlotScale = "lin",
) -> Ax:
"""Plot each of scene's components on a plane defined by one nonzero x,y,z coordinate.
@@ -876,20 +876,20 @@ def plot_eps(
@add_ax_if_none
def plot_structures_eps(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
- freq: Optional[float] = None,
- alpha: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
+ freq: float | None = None,
+ alpha: float | None = None,
cbar: bool = True,
reverse: bool = False,
- eps_lim: tuple[Union[float, None], Union[float, None]] = (None, None),
+ eps_lim: tuple[float | None, float | None] = (None, None),
scale: PlotScale = "lin",
ax: Ax = None,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
grid: Grid = None,
- eps_component: Optional[PermittivityComponent] = None,
+ eps_component: PermittivityComponent | None = None,
) -> Ax:
"""Plot each of scene's structures on a plane defined by one nonzero x,y,z coordinate.
The permittivity is plotted in grayscale based on its value at the specified frequency.
@@ -956,21 +956,21 @@ def plot_structures_eps(
@add_ax_if_none
def plot_structures_property(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
- freq: Optional[float] = None,
- alpha: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
+ freq: float | None = None,
+ alpha: float | None = None,
cbar: bool = True,
reverse: bool = False,
- limits: tuple[Union[float, None], Union[float, None]] = (None, None),
+ limits: tuple[float | None, float | None] = (None, None),
scale: PlotScale = "lin",
ax: Ax = None,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
grid: Grid = None,
property: Literal["eps", "doping", "N_a", "N_d"] = "eps",
- eps_component: Optional[PermittivityComponent] = None,
+ eps_component: PermittivityComponent | None = None,
) -> Ax:
"""Plot each of scene's structures on a plane defined by one nonzero x,y,z coordinate.
The permittivity is plotted in grayscale based on its value at the specified frequency.
@@ -1197,7 +1197,7 @@ def _add_cbar_eps(
eps_max: float,
ax: Ax = None,
reverse: bool = False,
- norm: Optional[mpl.colors.Normalize] = None,
+ norm: mpl.colors.Normalize | None = None,
) -> None:
"""Add a permittivity colorbar to plot."""
Scene._add_cbar(
@@ -1212,8 +1212,8 @@ def _add_cbar_eps(
@staticmethod
def _eps_bounds(
medium_list: list[Medium],
- freq: Optional[float] = None,
- eps_component: Optional[PermittivityComponent] = None,
+ freq: float | None = None,
+ eps_component: PermittivityComponent | None = None,
) -> tuple[float, float]:
"""Compute range of (real) permittivity present in the mediums at frequency "freq"."""
medium_list = [medium for medium in medium_list if not medium.is_pec]
@@ -1230,7 +1230,7 @@ def _eps_bounds(
return eps_min, eps_max
def eps_bounds(
- self, freq: Optional[float] = None, eps_component: Optional[str] = None
+ self, freq: float | None = None, eps_component: str | None = None
) -> tuple[float, float]:
"""Compute range of (real) permittivity present in the scene at frequency "freq".
@@ -1267,7 +1267,7 @@ def _pcolormesh_shape_custom_medium_structure_eps(
shape: Shapely,
ax: Ax,
grid: Grid,
- eps_component: Optional[PermittivityComponent] = None,
+ eps_component: PermittivityComponent | None = None,
norm: mpl.colors.Normalize = None,
):
"""
@@ -1418,9 +1418,9 @@ def _get_structure_eps_plot_params(
eps_min: float,
eps_max: float,
reverse: bool = False,
- alpha: Optional[float] = None,
- eps_component: Optional[PermittivityComponent] = None,
- norm: Optional[mpl.colors.Normalize] = None,
+ alpha: float | None = None,
+ eps_component: PermittivityComponent | None = None,
+ norm: mpl.colors.Normalize | None = None,
) -> PlotParams:
"""Constructs the plot parameters for a given medium in scene.plot_eps()."""
@@ -1467,9 +1467,9 @@ def _plot_shape_structure_eps(
eps_max: float,
ax: Ax,
reverse: bool = False,
- alpha: Optional[float] = None,
- eps_component: Optional[PermittivityComponent] = None,
- norm: Optional[mpl.colors.Normalize] = None,
+ alpha: float | None = None,
+ eps_component: PermittivityComponent | None = None,
+ norm: mpl.colors.Normalize | None = None,
) -> Ax:
"""Plot a structure's cross section shape for a given medium, grayscale for permittivity."""
plot_params = self._get_structure_eps_plot_params(
@@ -1491,15 +1491,15 @@ def _plot_shape_structure_eps(
@add_ax_if_none
def plot_heat_charge_property(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
- alpha: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
+ alpha: float | None = None,
cbar: bool = True,
property: str = "heat_conductivity",
ax: Ax = None,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
) -> Ax:
"""Plot each of scebe's components on a plane defined by one nonzero x,y,z coordinate.
The thermal conductivity is plotted in grayscale based on its value.
@@ -1545,15 +1545,15 @@ def plot_heat_charge_property(
@add_ax_if_none
def plot_structures_heat_conductivity(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
- alpha: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
+ alpha: float | None = None,
cbar: bool = True,
reverse: bool = False,
ax: Ax = None,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
) -> Ax:
"""Plot each of scene's structures on a plane defined by one nonzero x,y,z coordinate.
The thermal conductivity is plotted in grayscale based on its value.
@@ -1610,16 +1610,16 @@ def plot_structures_heat_conductivity(
@add_ax_if_none
def plot_structures_heat_charge_property(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
- alpha: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
+ alpha: float | None = None,
cbar: bool = True,
property: str = "heat_conductivity",
reverse: bool = False,
ax: Ax = None,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
) -> Ax:
"""Plot each of scene's structures on a plane defined by one nonzero x,y,z coordinate.
The thermal conductivity is plotted in grayscale based on its value.
@@ -1762,7 +1762,7 @@ def _get_structure_heat_charge_property_plot_params(
property_val_min: float,
property_val_max: float,
reverse: bool = False,
- alpha: Optional[float] = None,
+ alpha: float | None = None,
property: str = "heat_conductivity",
) -> PlotParams:
"""Constructs the plot parameters for a given medium in
@@ -1809,7 +1809,7 @@ def _plot_shape_structure_heat_charge_property(
property: str,
ax: Ax,
reverse: bool = False,
- alpha: Optional[float] = None,
+ alpha: float | None = None,
) -> Ax:
"""Plot a structure's cross section shape for a given medium, grayscale for thermal
conductivity.
@@ -1829,14 +1829,14 @@ def _plot_shape_structure_heat_charge_property(
@add_ax_if_none
def plot_heat_conductivity(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
- alpha: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
+ alpha: float | None = None,
cbar: bool = True,
ax: Ax = None,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
):
"""Plot each of scebe's components on a plane defined by one nonzero x,y,z coordinate.
The thermal conductivity is plotted in grayscale based on its value.
@@ -2103,7 +2103,7 @@ def _pcolormesh_shape_doping_box(
"""
coords = "xyz"
normal_axis_ind, normal_position = Box.parse_xyz_kwargs(x=x, y=y, z=z)
- normal_axis, plane_axes = Box.pop_axis(coords, normal_axis_ind)
+ _normal_axis, _plane_axes = Box.pop_axis(coords, normal_axis_ind)
# make grid for eps interpolation
# we will do this by combining shape bounds and points where custom eps is provided
diff --git a/tidy3d/components/simulation.py b/tidy3d/components/simulation.py
index b654b935aa..7f2e04045a 100644
--- a/tidy3d/components/simulation.py
+++ b/tidy3d/components/simulation.py
@@ -6,7 +6,7 @@
import pathlib
from abc import ABC, abstractmethod
from collections import defaultdict
-from typing import Literal, Optional, Union, get_args
+from typing import Literal, get_args
import autograd.numpy as np
@@ -302,7 +302,7 @@ class AbstractYeeGridSimulation(AbstractSimulation, ABC):
* `Using automatic nonuniform meshing <../../notebooks/AutoGrid.html>`_
"""
- subpixel: Union[bool, SubpixelSpec] = pydantic.Field(
+ subpixel: bool | SubpixelSpec = pydantic.Field(
SubpixelSpec(),
title="Subpixel Averaging",
description="Apply subpixel averaging methods of the permittivity on structure interfaces "
@@ -356,7 +356,7 @@ class AbstractYeeGridSimulation(AbstractSimulation, ABC):
* `Dielectric constant assignment on Yee grids `_
"""
- simulation_type: Optional[Literal["autograd_fwd", "autograd_bwd", "tidy3d", None]] = (
+ simulation_type: Literal["autograd_fwd", "autograd_bwd", "tidy3d", None] | None = (
pydantic.Field(
"tidy3d",
title="Simulation Type",
@@ -365,7 +365,7 @@ class AbstractYeeGridSimulation(AbstractSimulation, ABC):
)
)
- post_norm: Union[float, FreqDataArray] = pydantic.Field(
+ post_norm: float | FreqDataArray = pydantic.Field(
1.0,
title="Post Normalization Values",
description="Factor to multiply the fields by after running, "
@@ -491,12 +491,12 @@ def _shifted_internal_absorbers(self) -> list[InternalAbsorber]:
@add_ax_if_none
def plot_absorbers(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
- alpha: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
+ alpha: float | None = None,
ax: Ax = None,
shifted: bool = False,
) -> Ax:
@@ -541,17 +541,17 @@ def plot_absorbers(
@add_ax_if_none
def plot(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
- source_alpha: Optional[float] = None,
- monitor_alpha: Optional[float] = None,
- lumped_element_alpha: Optional[float] = None,
- absorber_alpha: Optional[float] = None,
+ source_alpha: float | None = None,
+ monitor_alpha: float | None = None,
+ lumped_element_alpha: float | None = None,
+ absorber_alpha: float | None = None,
absorber_actual_placement: bool = False,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
fill_structures: bool = True,
**patch_kwargs,
) -> Ax:
@@ -638,21 +638,21 @@ def plot(
@add_ax_if_none
def plot_eps(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
- freq: Optional[float] = None,
- alpha: Optional[float] = None,
- source_alpha: Optional[float] = None,
- monitor_alpha: Optional[float] = None,
- lumped_element_alpha: Optional[float] = None,
- absorber_alpha: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
+ freq: float | None = None,
+ alpha: float | None = None,
+ source_alpha: float | None = None,
+ monitor_alpha: float | None = None,
+ lumped_element_alpha: float | None = None,
+ absorber_alpha: float | None = None,
absorber_actual_placement: bool = False,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
ax: Ax = None,
- eps_component: Optional[PermittivityComponent] = None,
- eps_lim: tuple[Union[float, None], Union[float, None]] = (None, None),
+ eps_component: PermittivityComponent | None = None,
+ eps_lim: tuple[float | None, float | None] = (None, None),
) -> Ax:
"""Plot each of simulation's components on a plane defined by one nonzero x,y,z coordinate.
The permittivity is plotted in grayscale based on its value at the specified frequency.
@@ -761,18 +761,18 @@ def plot_eps(
@add_ax_if_none
def plot_structures_eps(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
- freq: Optional[float] = None,
- alpha: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
+ freq: float | None = None,
+ alpha: float | None = None,
cbar: bool = True,
reverse: bool = False,
ax: Ax = None,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
- eps_component: Optional[PermittivityComponent] = None,
- eps_lim: tuple[Union[float, None], Union[float, None]] = (None, None),
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
+ eps_component: PermittivityComponent | None = None,
+ eps_lim: tuple[float | None, float | None] = (None, None),
) -> Ax:
"""Plot each of simulation's structures on a plane defined by one nonzero x,y,z coordinate.
The permittivity is plotted in grayscale based on its value at the specified frequency.
@@ -852,11 +852,11 @@ def plot_structures_eps(
@add_ax_if_none
def plot_pml(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
ax: Ax = None,
) -> Ax:
"""Plot each of simulation's absorbing boundaries
@@ -948,7 +948,7 @@ def _make_pml_box(self, pml_axis: Axis, pml_height: float, sign: int) -> Box:
return pml_box
# candidate for removal in 3.0
- def eps_bounds(self, freq: Optional[float] = None) -> tuple[float, float]:
+ def eps_bounds(self, freq: float | None = None) -> tuple[float, float]:
"""Compute range of (real) permittivity present in the simulation at frequency "freq"."""
log.warning(
@@ -1026,12 +1026,12 @@ def internal_snapping_points(self) -> list[CoordinateOptional]:
@add_ax_if_none
def plot_lumped_elements(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
- alpha: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
+ alpha: float | None = None,
ax: Ax = None,
) -> Ax:
"""Plot each of simulation's lumped elements on a plane defined by one
@@ -1071,12 +1071,12 @@ def plot_lumped_elements(
@add_ax_if_none
def plot_grid(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
override_structures_alpha: float = 1,
snapping_points_alpha: float = 1,
**kwargs,
@@ -1219,9 +1219,9 @@ def plot_grid(
@add_ax_if_none
def plot_boundaries(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
**kwargs,
) -> Ax:
@@ -1514,9 +1514,7 @@ def _discretize_grid(self, box: Box, grid: Grid, extend: bool = False) -> Grid:
span_inds = grid.discretize_inds(box=box, extend=extend)
return self._subgrid(span_inds=span_inds, grid=grid)
- def _discretize_inds_monitor(
- self, monitor: Union[Monitor, Box], colocate: Optional[bool] = None
- ):
+ def _discretize_inds_monitor(self, monitor: Monitor | Box, colocate: bool | None = None):
"""Start and stopping indexes for the cells where data needs to be recorded to fully cover
a ``monitor``. This is used during the solver run. The final grid on which a monitor data
lives is computed in ``discretize_monitor``, with the difference being that 0-sized
@@ -1575,7 +1573,7 @@ def epsilon(
self,
box: Box,
coord_key: str = "centers",
- freq: Optional[float] = None,
+ freq: float | None = None,
) -> xr.DataArray:
"""Get array of permittivity at volume specified by box and freq.
@@ -1617,7 +1615,7 @@ def epsilon_on_grid(
self,
grid: Grid,
coord_key: str = "centers",
- freq: Optional[float] = None,
+ freq: float | None = None,
) -> xr.DataArray:
"""Get array of permittivity at a given freq on a given grid.
@@ -1856,17 +1854,17 @@ def subsection(
self,
region: Box,
boundary_spec: BoundarySpec = None,
- grid_spec: Union[GridSpec, Literal["identical"]] = None,
- symmetry: Optional[tuple[Symmetry, Symmetry, Symmetry]] = None,
+ grid_spec: GridSpec | Literal["identical"] = None,
+ symmetry: tuple[Symmetry, Symmetry, Symmetry] | None = None,
warn_symmetry_expansion: bool = True,
- sources: Optional[tuple[SourceType, ...]] = None,
- monitors: Optional[tuple[MonitorType, ...]] = None,
+ sources: tuple[SourceType, ...] | None = None,
+ monitors: tuple[MonitorType, ...] | None = None,
remove_outside_structures: bool = True,
remove_outside_custom_mediums: bool = False,
include_pml_cells: bool = False,
validate_geometries: bool = True,
deep_copy: bool = True,
- internal_absorbers: Optional[tuple[InternalAbsorber, ...]] = None,
+ internal_absorbers: tuple[InternalAbsorber, ...] | None = None,
**kwargs,
) -> AbstractYeeGridSimulation:
"""Generate a simulation instance containing only the ``region``.
@@ -2146,7 +2144,7 @@ def validate_pre_upload(self) -> None:
self._validate_finalized()
log.end_capture(self)
- def _make_pec_frame(self, obj: Union[ModeSource, InternalAbsorber]) -> Structure:
+ def _make_pec_frame(self, obj: ModeSource | InternalAbsorber) -> Structure:
"""Make a pec frame around a mode source or an internal absorber. For mode sources,
the frame is added around the injection plane. For internal absorbers, a backing pec
plate is also added on the non-absorbing side.
@@ -2174,7 +2172,7 @@ def _make_pec_frame(self, obj: Union[ModeSource, InternalAbsorber]) -> Structure
return structure
def _pec_frame_box(
- self, obj: Union[ModeSource, InternalAbsorber], expand: bool = False
+ self, obj: ModeSource | InternalAbsorber, expand: bool = False
) -> tuple[Box, int, str]:
"""Return pec bounding box, frame axis and object's direction"""
@@ -2714,7 +2712,7 @@ class Simulation(AbstractYeeGridSimulation):
"""
- normalize_index: Union[pydantic.NonNegativeInt, None] = pydantic.Field(
+ normalize_index: pydantic.NonNegativeInt | None = pydantic.Field(
0,
title="Normalization index",
description="Index of the source in the tuple of sources whose spectrum will be used to "
@@ -2889,7 +2887,7 @@ class Simulation(AbstractYeeGridSimulation):
"""
# TODO: at a later time (once well tested) we could consider making default of RunTimeSpec()
- run_time: Union[pydantic.PositiveFloat, RunTimeSpec] = pydantic.Field(
+ run_time: pydantic.PositiveFloat | RunTimeSpec = pydantic.Field(
...,
title="Run Time",
description="Total electromagnetic evolution time in seconds. "
@@ -2950,7 +2948,7 @@ class Simulation(AbstractYeeGridSimulation):
"""
- low_freq_smoothing: Optional[LowFrequencySmoothingSpec] = pydantic.Field(
+ low_freq_smoothing: LowFrequencySmoothingSpec | None = pydantic.Field(
None,
title="Low Frequency Smoothing",
description="The low frequency smoothing parameters for the simulation.",
@@ -4240,7 +4238,7 @@ def _validate_mode_objects(self) -> None:
"""Create a ModeSolver for each mode object in order to validate."""
from .mode.mode_solver import ModeSolver
- def validate_mode_object(mode_obj: Union[ModeSource, AbstractModeMonitor], msg_prefix: str):
+ def validate_mode_object(mode_obj: ModeSource | AbstractModeMonitor, msg_prefix: str):
# Warn if pml is too thick
ModeSolver._warn_thick_pml(
simulation=self,
@@ -5091,14 +5089,15 @@ def _check_bloch_vec(
def to_gdstk(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
permittivity_threshold: pydantic.NonNegativeFloat = 1,
frequency: pydantic.PositiveFloat = 0,
- gds_layer_dtype_map: Optional[
- dict[AbstractMedium, tuple[pydantic.NonNegativeInt, pydantic.NonNegativeInt]]
- ] = None,
+ gds_layer_dtype_map: dict[
+ AbstractMedium, tuple[pydantic.NonNegativeInt, pydantic.NonNegativeInt]
+ ]
+ | None = None,
) -> list:
"""Convert a simulation's planar slice to a .gds type polygon list.
@@ -5162,14 +5161,15 @@ def to_gdstk(
def to_gds(
self,
cell,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
permittivity_threshold: pydantic.NonNegativeFloat = 1,
frequency: pydantic.PositiveFloat = 0,
- gds_layer_dtype_map: Optional[
- dict[AbstractMedium, tuple[pydantic.NonNegativeInt, pydantic.NonNegativeInt]]
- ] = None,
+ gds_layer_dtype_map: dict[
+ AbstractMedium, tuple[pydantic.NonNegativeInt, pydantic.NonNegativeInt]
+ ]
+ | None = None,
) -> None:
"""Append the simulation structures to a .gds cell.
@@ -5216,14 +5216,15 @@ def to_gds(
def to_gds_file(
self,
fname: str,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
permittivity_threshold: pydantic.NonNegativeFloat = 1,
frequency: pydantic.PositiveFloat = 0,
- gds_layer_dtype_map: Optional[
- dict[AbstractMedium, tuple[pydantic.NonNegativeInt, pydantic.NonNegativeInt]]
- ] = None,
+ gds_layer_dtype_map: dict[
+ AbstractMedium, tuple[pydantic.NonNegativeInt, pydantic.NonNegativeInt]
+ ]
+ | None = None,
gds_cell_name: str = "MAIN",
) -> None:
"""Append the simulation structures to a .gds cell.
diff --git a/tidy3d/components/source/base.py b/tidy3d/components/source/base.py
index c94366ed3e..100f4ea9df 100644
--- a/tidy3d/components/source/base.py
+++ b/tidy3d/components/source/base.py
@@ -3,7 +3,6 @@
from __future__ import annotations
from abc import ABC
-from typing import Optional
import pydantic.v1 as pydantic
@@ -70,9 +69,9 @@ def _freqs_lower_bound(cls, val):
def plot(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
**patch_kwargs,
) -> Ax:
diff --git a/tidy3d/components/source/current.py b/tidy3d/components/source/current.py
index a768a94e59..9f777a7a41 100644
--- a/tidy3d/components/source/current.py
+++ b/tidy3d/components/source/current.py
@@ -4,10 +4,9 @@
from abc import ABC
from math import cos, isclose, sin
-from typing import Optional
+from typing import Literal
import pydantic.v1 as pydantic
-from typing_extensions import Literal
from tidy3d.components.base import cached_property
from tidy3d.components.data.dataset import FieldDataset
@@ -208,7 +207,7 @@ class CustomCurrentSource(ReverseInterpolatedSource):
* `Defining spatially-varying sources <../../notebooks/CustomFieldSource.html>`_
"""
- current_dataset: Optional[FieldDataset] = pydantic.Field(
+ current_dataset: FieldDataset | None = pydantic.Field(
...,
title="Current Dataset",
description=":class:`.FieldDataset` containing the desired frequency-domain "
diff --git a/tidy3d/components/source/field.py b/tidy3d/components/source/field.py
index 1632edcaa8..25e491a249 100644
--- a/tidy3d/components/source/field.py
+++ b/tidy3d/components/source/field.py
@@ -3,7 +3,6 @@
from __future__ import annotations
from abc import ABC
-from typing import Optional, Union
import numpy as np
import pydantic.v1 as pydantic
@@ -215,7 +214,7 @@ class CustomFieldSource(FieldSource, PlanarSource):
* `Defining spatially-varying sources <../../notebooks/CustomFieldSource.html>`_
"""
- field_dataset: Optional[FieldDataset] = pydantic.Field(
+ field_dataset: FieldDataset | None = pydantic.Field(
...,
title="Field Dataset",
description=":class:`.FieldDataset` containing the desired frequency-domain "
@@ -408,7 +407,7 @@ class ModeSource(DirectionalSource, PlanarSource, BroadbandSource):
"``num_modes`` in the solver will be set to ``mode_index + 1``.",
)
- frame: Optional[PECFrame] = pydantic.Field(
+ frame: PECFrame | None = pydantic.Field(
None,
title="Source Frame",
description="Add a thin frame around the source during the FDTD run to improve "
@@ -492,7 +491,7 @@ class PlaneWave(AngledFieldSource, PlanarSource, BroadbandSource):
* `Using FDTD to Compute a Transmission Spectrum `__
"""
- angular_spec: Union[FixedInPlaneKSpec, FixedAngleSpec] = pydantic.Field(
+ angular_spec: FixedInPlaneKSpec | FixedAngleSpec = pydantic.Field(
FixedInPlaneKSpec(),
title="Angular Dependence Specification",
description="Specification of plane wave propagation direction dependence on wavelength.",
@@ -533,7 +532,7 @@ def _post_init_validators(self) -> None:
the source frequency range is entirely below ``f_crit * CRITICAL_FREQUENCY_FACTOR."""
if self._is_fixed_angle or self.num_freqs == 1:
return
- freq_min, freq_max = self.source_time.frequency_range_sigma(sigma=CHEB_GRID_WIDTH)
+ _freq_min, freq_max = self.source_time.frequency_range_sigma(sigma=CHEB_GRID_WIDTH)
f_crit = self.source_time._freq0 * np.sin(self.angle_theta)
if f_crit * CRITICAL_FREQUENCY_FACTOR > freq_max:
raise SetupError(
@@ -721,9 +720,9 @@ def injection_plane_center(self) -> Coordinate:
def plot(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
**patch_kwargs,
) -> Ax:
diff --git a/tidy3d/components/source/time.py b/tidy3d/components/source/time.py
index f2532ea3d7..dca7da79c1 100644
--- a/tidy3d/components/source/time.py
+++ b/tidy3d/components/source/time.py
@@ -4,7 +4,6 @@
import logging
from abc import ABC, abstractmethod
-from typing import Optional, Union
import numpy as np
import pydantic.v1 as pydantic
@@ -86,7 +85,7 @@ def _frequency_range_sigma_cached(self) -> FreqBound:
return self.frequency_range_sigma(sigma=DEFAULT_SIGMA)
@abstractmethod
- def end_time(self) -> Optional[float]:
+ def end_time(self) -> float | None:
"""Time after which the source is effectively turned off / close to zero amplitude."""
@cached_property
@@ -218,7 +217,7 @@ def amp_time(self, time: float) -> complex:
return pulse_amp
- def end_time(self) -> Optional[float]:
+ def end_time(self) -> float | None:
"""Time after which the source is effectively turned off / close to zero amplitude."""
# TODO: decide if we should continue to return an end_time if the DC component remains
@@ -422,7 +421,7 @@ def amp_time(self, time: float) -> complex:
return const * offset * oscillation * amp
- def end_time(self) -> Optional[float]:
+ def end_time(self) -> float | None:
"""Time after which the source is effectively turned off / close to zero amplitude."""
return None
@@ -467,7 +466,7 @@ class CustomSourceTime(Pulse):
description="Time delay of the envelope in units of 1 / (``2pi * fwidth``).",
)
- source_time_dataset: Optional[TimeDataset] = pydantic.Field(
+ source_time_dataset: TimeDataset | None = pydantic.Field(
...,
title="Source time dataset",
description="Dataset for storing the envelope of the custom source time. "
@@ -590,7 +589,7 @@ def amp_time(self, time: float) -> complex:
return offset * oscillation * amp * envelope
- def end_time(self) -> Optional[float]:
+ def end_time(self) -> float | None:
"""Time after which the source is effectively turned off / close to zero amplitude."""
if self.source_time_dataset is None:
@@ -605,4 +604,4 @@ def end_time(self) -> Optional[float]:
return np.max(t_non_zero)
-SourceTimeType = Union[GaussianPulse, ContinuousWave, CustomSourceTime]
+SourceTimeType = GaussianPulse | ContinuousWave | CustomSourceTime
diff --git a/tidy3d/components/source/utils.py b/tidy3d/components/source/utils.py
index 4996138c13..a2c61be880 100644
--- a/tidy3d/components/source/utils.py
+++ b/tidy3d/components/source/utils.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Union
-
from .current import CustomCurrentSource, PointDipole, UniformCurrentSource
from .field import (
TFSF,
@@ -15,14 +13,14 @@
)
# sources allowed in Simulation.sources
-SourceType = Union[
- UniformCurrentSource,
- PointDipole,
- GaussianBeam,
- AstigmaticGaussianBeam,
- ModeSource,
- PlaneWave,
- CustomFieldSource,
- CustomCurrentSource,
- TFSF,
-]
+SourceType = (
+ UniformCurrentSource
+ | PointDipole
+ | GaussianBeam
+ | AstigmaticGaussianBeam
+ | ModeSource
+ | PlaneWave
+ | CustomFieldSource
+ | CustomCurrentSource
+ | TFSF
+)
diff --git a/tidy3d/components/spice/sources/ac.py b/tidy3d/components/spice/sources/ac.py
index 17af5fd9ff..2584a34e7d 100644
--- a/tidy3d/components/spice/sources/ac.py
+++ b/tidy3d/components/spice/sources/ac.py
@@ -1,7 +1,5 @@
from __future__ import annotations
-from typing import Optional
-
import pydantic.v1 as pd
from tidy3d.components.base import Tidy3dBaseModel
@@ -33,7 +31,7 @@ class SSACVoltageSource(Tidy3dBaseModel):
... )
"""
- name: Optional[str] = pd.Field(
+ name: str | None = pd.Field(
None,
title="Name",
description="Unique name for the SSAC voltage source.",
diff --git a/tidy3d/components/spice/sources/dc.py b/tidy3d/components/spice/sources/dc.py
index dcc1eb0e44..70ca86a450 100644
--- a/tidy3d/components/spice/sources/dc.py
+++ b/tidy3d/components/spice/sources/dc.py
@@ -21,7 +21,7 @@
from __future__ import annotations
-from typing import Literal, Optional
+from typing import Literal
import pydantic.v1 as pd
@@ -48,7 +48,7 @@ class DCVoltageSource(Tidy3dBaseModel):
>>> voltage_source = td.DCVoltageSource(voltage=voltages)
"""
- name: Optional[str] = pd.Field(
+ name: str | None = pd.Field(
None,
title="Name",
description="Unique name for the DC voltage source",
@@ -108,7 +108,7 @@ class DCCurrentSource(Tidy3dBaseModel):
>>> current_source = td.DCCurrentSource(current=0.4)
"""
- name: Optional[str] = pd.Field(
+ name: str | None = pd.Field(
None,
title="Name",
description="Unique name for the DC current source",
diff --git a/tidy3d/components/spice/sources/types.py b/tidy3d/components/spice/sources/types.py
index 071e4c2ced..84b48cf7da 100644
--- a/tidy3d/components/spice/sources/types.py
+++ b/tidy3d/components/spice/sources/types.py
@@ -1,9 +1,7 @@
from __future__ import annotations
-from typing import Union
-
from .ac import SSACVoltageSource
from .dc import DCCurrentSource, DCVoltageSource, GroundVoltage
-VoltageSourceType = Union[DCVoltageSource, SSACVoltageSource, GroundVoltage]
-CurrentSourceType = Union[DCCurrentSource]
+VoltageSourceType = DCVoltageSource | SSACVoltageSource | GroundVoltage
+CurrentSourceType = DCCurrentSource
diff --git a/tidy3d/components/spice/types.py b/tidy3d/components/spice/types.py
index a46d7ddebd..73e2e825ca 100644
--- a/tidy3d/components/spice/types.py
+++ b/tidy3d/components/spice/types.py
@@ -1,13 +1,14 @@
from __future__ import annotations
-from typing import Union
-
from tidy3d.components.spice.analysis.ac import IsothermalSSACAnalysis, SSACAnalysis
from tidy3d.components.spice.analysis.dc import (
IsothermalSteadyChargeDCAnalysis,
SteadyChargeDCAnalysis,
)
-ElectricalAnalysisType = Union[
- SteadyChargeDCAnalysis, IsothermalSteadyChargeDCAnalysis, SSACAnalysis, IsothermalSSACAnalysis
-]
+ElectricalAnalysisType = (
+ SteadyChargeDCAnalysis
+ | IsothermalSteadyChargeDCAnalysis
+ | SSACAnalysis
+ | IsothermalSSACAnalysis
+)
diff --git a/tidy3d/components/structure.py b/tidy3d/components/structure.py
index f091622eaf..ddf9d2a159 100644
--- a/tidy3d/components/structure.py
+++ b/tidy3d/components/structure.py
@@ -5,7 +5,6 @@
import pathlib
from collections import defaultdict
from functools import cmp_to_key
-from typing import Optional, Union
import autograd.numpy as anp
import numpy as np
@@ -145,9 +144,9 @@ def viz_spec(self):
@add_ax_if_none
def plot(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
**patch_kwargs,
) -> Ax:
@@ -405,9 +404,9 @@ def eps_comp(self, row: Axis, col: Axis, frequency: float, coords: Coords) -> co
def to_gdstk(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
permittivity_threshold: pydantic.NonNegativeFloat = 1,
frequency: pydantic.PositiveFloat = 0,
gds_layer: pydantic.NonNegativeInt = 0,
@@ -474,9 +473,9 @@ def to_gdstk(
def to_gds(
self,
cell,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
permittivity_threshold: pydantic.NonNegativeFloat = 1,
frequency: pydantic.PositiveFloat = 0,
gds_layer: pydantic.NonNegativeInt = 0,
@@ -526,9 +525,9 @@ def to_gds(
def to_gds_file(
self,
fname: str,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
permittivity_threshold: pydantic.NonNegativeFloat = 1,
frequency: pydantic.PositiveFloat = 0,
gds_layer: pydantic.NonNegativeInt = 0,
@@ -661,9 +660,9 @@ class MeshOverrideStructure(AbstractStructure):
"""
dl: tuple[
- Optional[pydantic.PositiveFloat],
- Optional[pydantic.PositiveFloat],
- Optional[pydantic.PositiveFloat],
+ pydantic.PositiveFloat | None,
+ pydantic.PositiveFloat | None,
+ pydantic.PositiveFloat | None,
] = pydantic.Field(
...,
title="Grid Size",
@@ -724,4 +723,4 @@ def _unshadowed_cannot_be_enforced(cls, val, values):
return val
-StructureType = Union[Structure, MeshOverrideStructure]
+StructureType = Structure | MeshOverrideStructure
diff --git a/tidy3d/components/subpixel_spec.py b/tidy3d/components/subpixel_spec.py
index 89d0ceba8c..11a065d70a 100644
--- a/tidy3d/components/subpixel_spec.py
+++ b/tidy3d/components/subpixel_spec.py
@@ -1,8 +1,6 @@
# Defines specifications for subpixel averaging
from __future__ import annotations
-from typing import Union
-
import pydantic.v1 as pd
from .base import Tidy3dBaseModel, cached_property
@@ -65,7 +63,7 @@ class ContourPathAveraging(AbstractSubpixelAveragingMethod):
"""
-DielectricSubpixelType = Union[Staircasing, PolarizedAveraging, ContourPathAveraging]
+DielectricSubpixelType = Staircasing | PolarizedAveraging | ContourPathAveraging
class VolumetricAveraging(AbstractSubpixelAveragingMethod):
@@ -83,7 +81,7 @@ class VolumetricAveraging(AbstractSubpixelAveragingMethod):
)
-MetalSubpixelType = Union[Staircasing, VolumetricAveraging]
+MetalSubpixelType = Staircasing | VolumetricAveraging
class HeuristicPECStaircasing(AbstractSubpixelAveragingMethod):
@@ -136,8 +134,8 @@ def courant_ratio(self) -> float:
return 1 - self.timestep_reduction
-PECSubpixelType = Union[Staircasing, HeuristicPECStaircasing, PECConformal]
-PMCSubpixelType = Union[Staircasing, HeuristicPECStaircasing]
+PECSubpixelType = Staircasing | HeuristicPECStaircasing | PECConformal
+PMCSubpixelType = Staircasing | HeuristicPECStaircasing
class SurfaceImpedance(PECConformal):
@@ -156,7 +154,7 @@ class SurfaceImpedance(PECConformal):
)
-LossyMetalSubpixelType = Union[Staircasing, VolumetricAveraging, SurfaceImpedance]
+LossyMetalSubpixelType = Staircasing | VolumetricAveraging | SurfaceImpedance
class SubpixelSpec(Tidy3dBaseModel):
diff --git a/tidy3d/components/tcad/boundary/heat.py b/tidy3d/components/tcad/boundary/heat.py
index 7430cc22f2..da6d2f6974 100644
--- a/tidy3d/components/tcad/boundary/heat.py
+++ b/tidy3d/components/tcad/boundary/heat.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Union
-
import pydantic.v1 as pd
from tidy3d.components.base import Tidy3dBaseModel
@@ -162,7 +160,7 @@ class ConvectionBC(HeatChargeBC):
units=KELVIN,
)
- transfer_coeff: Union[pd.NonNegativeFloat, VerticalNaturalConvectionCoeffModel] = pd.Field(
+ transfer_coeff: pd.NonNegativeFloat | VerticalNaturalConvectionCoeffModel = pd.Field(
title="Heat Transfer Coefficient",
description="Heat transfer coefficient value.",
units=HEAT_TRANSFER_COEFF,
diff --git a/tidy3d/components/tcad/data/monitor_data/abstract.py b/tidy3d/components/tcad/data/monitor_data/abstract.py
index 147141974d..4ea9effb4f 100644
--- a/tidy3d/components/tcad/data/monitor_data/abstract.py
+++ b/tidy3d/components/tcad/data/monitor_data/abstract.py
@@ -4,7 +4,6 @@
import copy
from abc import ABC, abstractmethod
-from typing import Union
import numpy as np
import pydantic.v1 as pd
@@ -21,10 +20,8 @@
from tidy3d.constants import MICROMETER
from tidy3d.log import log
-FieldDataset = Union[
- SpatialDataArray, annotate_type(Union[TriangularGridDataset, TetrahedralGridDataset])
-]
-UnstructuredFieldType = Union[TriangularGridDataset, TetrahedralGridDataset]
+FieldDataset = SpatialDataArray | annotate_type(TriangularGridDataset | TetrahedralGridDataset)
+UnstructuredFieldType = TriangularGridDataset | TetrahedralGridDataset
class HeatChargeMonitorData(AbstractMonitorData, ABC):
diff --git a/tidy3d/components/tcad/data/monitor_data/charge.py b/tidy3d/components/tcad/data/monitor_data/charge.py
index 9e056696f1..1d6d8b216e 100644
--- a/tidy3d/components/tcad/data/monitor_data/charge.py
+++ b/tidy3d/components/tcad/data/monitor_data/charge.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Union
-
import numpy as np
import pydantic.v1 as pd
@@ -29,11 +27,9 @@
from tidy3d.components.viz import add_ax_if_none
from tidy3d.exceptions import DataError
-FieldDataset = Union[
- SpatialDataArray, annotate_type(Union[TriangularGridDataset, TetrahedralGridDataset])
-]
+FieldDataset = SpatialDataArray | annotate_type(TriangularGridDataset | TetrahedralGridDataset)
-UnstructuredFieldType = Union[TriangularGridDataset, TetrahedralGridDataset]
+UnstructuredFieldType = TriangularGridDataset | TetrahedralGridDataset
class SteadyPotentialData(HeatChargeMonitorData):
diff --git a/tidy3d/components/tcad/data/monitor_data/heat.py b/tidy3d/components/tcad/data/monitor_data/heat.py
index 9734735ec9..d3cc7380b4 100644
--- a/tidy3d/components/tcad/data/monitor_data/heat.py
+++ b/tidy3d/components/tcad/data/monitor_data/heat.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Optional, Union
-
import pydantic.v1 as pd
from tidy3d.components.data.data_array import (
@@ -19,12 +17,12 @@
from tidy3d.components.types import annotate_type
from tidy3d.constants import KELVIN
-FieldDataset = Union[
- SpatialDataArray,
- ScalarFieldTimeDataArray,
- annotate_type(Union[TriangularGridDataset, TetrahedralGridDataset]),
-]
-UnstructuredFieldType = Union[TriangularGridDataset, TetrahedralGridDataset]
+FieldDataset = (
+ SpatialDataArray
+ | ScalarFieldTimeDataArray
+ | annotate_type(TriangularGridDataset | TetrahedralGridDataset)
+)
+UnstructuredFieldType = TriangularGridDataset | TetrahedralGridDataset
class TemperatureData(HeatChargeMonitorData):
@@ -48,7 +46,7 @@ class TemperatureData(HeatChargeMonitorData):
..., title="Monitor", description="Temperature monitor associated with the data."
)
- temperature: Optional[FieldDataset] = pd.Field(
+ temperature: FieldDataset | None = pd.Field(
...,
title="Temperature",
description="Spatial temperature field.",
diff --git a/tidy3d/components/tcad/data/monitor_data/mesh.py b/tidy3d/components/tcad/data/monitor_data/mesh.py
index af3a4ec3f3..26d6eb2027 100644
--- a/tidy3d/components/tcad/data/monitor_data/mesh.py
+++ b/tidy3d/components/tcad/data/monitor_data/mesh.py
@@ -2,15 +2,13 @@
from __future__ import annotations
-from typing import Union
-
import pydantic.v1 as pd
from tidy3d.components.data.utils import TetrahedralGridDataset, TriangularGridDataset
from tidy3d.components.tcad.data.monitor_data.abstract import HeatChargeMonitorData
from tidy3d.components.tcad.monitors.mesh import VolumeMeshMonitor
-UnstructuredFieldType = Union[TriangularGridDataset, TetrahedralGridDataset]
+UnstructuredFieldType = TriangularGridDataset | TetrahedralGridDataset
class VolumeMeshData(HeatChargeMonitorData):
diff --git a/tidy3d/components/tcad/data/sim_data.py b/tidy3d/components/tcad/data/sim_data.py
index 1c6c86fb9d..f6976a62d1 100644
--- a/tidy3d/components/tcad/data/sim_data.py
+++ b/tidy3d/components/tcad/data/sim_data.py
@@ -3,7 +3,7 @@
from __future__ import annotations
from abc import ABC
-from typing import Literal, Optional
+from typing import Literal
import numpy as np
import pydantic.v1 as pd
@@ -57,27 +57,27 @@ class DeviceCharacteristics(Tidy3dBaseModel):
"""
- steady_dc_hole_capacitance: Optional[SteadyVoltageDataArray] = pd.Field(
+ steady_dc_hole_capacitance: SteadyVoltageDataArray | None = pd.Field(
None,
title="Steady DC hole capacitance",
description="Device steady DC capacitance data based on holes. If the simulation "
"has converged, these result should be close to that of electrons.",
)
- steady_dc_electron_capacitance: Optional[SteadyVoltageDataArray] = pd.Field(
+ steady_dc_electron_capacitance: SteadyVoltageDataArray | None = pd.Field(
None,
title="Steady DC electron capacitance",
description="Device steady DC capacitance data based on electrons. If the simulation "
"has converged, these result should be close to that of holes.",
)
- steady_dc_current_voltage: Optional[SteadyVoltageDataArray] = pd.Field(
+ steady_dc_current_voltage: SteadyVoltageDataArray | None = pd.Field(
None,
title="Steady DC current-voltage",
description="Device steady DC current-voltage relation for the device.",
)
- steady_dc_resistance_voltage: Optional[SteadyVoltageDataArray] = pd.Field(
+ steady_dc_resistance_voltage: SteadyVoltageDataArray | None = pd.Field(
None,
title="Small signal resistance",
description="Steady DC computation of the small signal resistance. This is computed "
@@ -85,7 +85,7 @@ class DeviceCharacteristics(Tidy3dBaseModel):
"is given in Ohms. Note that in 2D the resistance is given in :math:`\\Omega \\mu`.",
)
- ac_current_voltage: Optional[FreqVoltageDataArray] = pd.Field(
+ ac_current_voltage: FreqVoltageDataArray | None = pd.Field(
None,
title="Small-signal AC current-voltage",
description="Small-signal AC current as a function of DC bias voltage and frequency. "
@@ -104,7 +104,7 @@ class AbstractHeatChargeSimulationData(AbstractSimulationData, ABC):
)
@staticmethod
- def _get_field_by_name(monitor_data: TCADMonitorDataType, field_name: Optional[str] = None):
+ def _get_field_by_name(monitor_data: TCADMonitorDataType, field_name: str | None = None):
"""Return a field data based on a monitor dataset and a specified field name."""
if field_name is None:
if len(monitor_data.field_components) > 1:
@@ -127,7 +127,7 @@ def _get_field_by_name(monitor_data: TCADMonitorDataType, field_name: Optional[s
def plot_mesh(
self,
monitor_name: str,
- field_name: Optional[str] = None,
+ field_name: str | None = None,
structures_fill: bool = True,
ax: Ax = None,
**sel_kwargs,
@@ -262,7 +262,7 @@ class HeatChargeSimulationData(AbstractHeatChargeSimulationData):
"associated with the monitors of the original :class:`.Simulation`.",
)
- device_characteristics: Optional[DeviceCharacteristics] = pd.Field(
+ device_characteristics: DeviceCharacteristics | None = pd.Field(
None,
title="Device characteristics",
description="Data characterizing the device :class:`DeviceCharacteristics`.",
@@ -273,13 +273,13 @@ class HeatChargeSimulationData(AbstractHeatChargeSimulationData):
def plot_field(
self,
monitor_name: str,
- field_name: Optional[Literal["temperature", "potential"]] = None,
+ field_name: Literal["temperature", "potential"] | None = None,
val: RealFieldVal = "real",
scale: Literal["lin", "log"] = "lin",
structures_alpha: float = 0.2,
robust: bool = True,
- vmin: Optional[float] = None,
- vmax: Optional[float] = None,
+ vmin: float | None = None,
+ vmax: float | None = None,
ax: Ax = None,
**sel_kwargs,
) -> Ax:
diff --git a/tidy3d/components/tcad/data/types.py b/tidy3d/components/tcad/data/types.py
index f072f11cd7..8646d799f7 100644
--- a/tidy3d/components/tcad/data/types.py
+++ b/tidy3d/components/tcad/data/types.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Union
-
from tidy3d.components.tcad.data.monitor_data.charge import (
SteadyCapacitanceData,
SteadyCurrentDensityData,
@@ -14,12 +12,12 @@
)
from tidy3d.components.tcad.data.monitor_data.heat import TemperatureData
-TCADMonitorDataType = Union[
- TemperatureData,
- SteadyPotentialData,
- SteadyFreeCarrierData,
- SteadyElectricFieldData,
- SteadyEnergyBandData,
- SteadyCapacitanceData,
- SteadyCurrentDensityData,
-]
+TCADMonitorDataType = (
+ TemperatureData
+ | SteadyPotentialData
+ | SteadyFreeCarrierData
+ | SteadyElectricFieldData
+ | SteadyEnergyBandData
+ | SteadyCapacitanceData
+ | SteadyCurrentDensityData
+)
diff --git a/tidy3d/components/tcad/doping.py b/tidy3d/components/tcad/doping.py
index 3925b0440f..2293994520 100644
--- a/tidy3d/components/tcad/doping.py
+++ b/tidy3d/components/tcad/doping.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Union
-
import numpy as np
import pydantic.v1 as pd
import xarray as xr
@@ -314,7 +312,7 @@ class CustomDoping(AbstractDopingBox):
def _get_contrib(self, coords: dict, meshgrid: bool = True):
"""Returns the contribution to the doping a the locations specified in coords"""
- indices_in_box, X, Y, Z = self._get_indices_in_box(coords=coords, meshgrid=meshgrid)
+ indices_in_box, X, _Y, _Z = self._get_indices_in_box(coords=coords, meshgrid=meshgrid)
contrib = np.zeros(X.shape)
# interpolate
@@ -339,4 +337,4 @@ def _get_contrib(self, coords: dict, meshgrid: bool = True):
return contrib.squeeze()
-DopingBoxType = Union[ConstantDoping, GaussianDoping, CustomDoping]
+DopingBoxType = ConstantDoping | GaussianDoping | CustomDoping
diff --git a/tidy3d/components/tcad/generation_recombination.py b/tidy3d/components/tcad/generation_recombination.py
index 51bb5013c9..680ffcf948 100644
--- a/tidy3d/components/tcad/generation_recombination.py
+++ b/tidy3d/components/tcad/generation_recombination.py
@@ -1,7 +1,5 @@
from __future__ import annotations
-from typing import Union
-
import numpy as np
import pydantic.v1 as pd
@@ -64,7 +62,7 @@ class FossumCarrierLifetime(Tidy3dBaseModel):
alpha: float = pd.Field(..., title="Exponent constant", description="Exponent constant")
-CarrierLifetimeType = Union[FossumCarrierLifetime]
+CarrierLifetimeType = FossumCarrierLifetime
class AugerRecombination(Tidy3dBaseModel):
@@ -168,11 +166,11 @@ class ShockleyReedHallRecombination(Tidy3dBaseModel):
- This model represents mid-gap traps Shockley-Reed-Hall recombination.
"""
- tau_n: Union[pd.PositiveFloat, CarrierLifetimeType] = pd.Field(
+ tau_n: pd.PositiveFloat | CarrierLifetimeType = pd.Field(
..., title="Electron lifetime", description="Electron lifetime", units=SECOND
)
- tau_p: Union[pd.PositiveFloat, CarrierLifetimeType] = pd.Field(
+ tau_p: pd.PositiveFloat | CarrierLifetimeType = pd.Field(
..., title="Hole lifetime", description="Hole lifetime", units=SECOND
)
diff --git a/tidy3d/components/tcad/grid.py b/tidy3d/components/tcad/grid.py
index fcf0cf6f27..c7b88d85f2 100644
--- a/tidy3d/components/tcad/grid.py
+++ b/tidy3d/components/tcad/grid.py
@@ -3,7 +3,6 @@
from __future__ import annotations
from abc import ABC
-from typing import Union
import numpy as np
import pydantic.v1 as pd
@@ -215,7 +214,7 @@ class DistanceUnstructuredGrid(UnstructuredGrid):
"``dl_bulk`` is used instead.",
)
- mesh_refinements: tuple[annotate_type(Union[GridRefinementRegion, GridRefinementLine]), ...] = (
+ mesh_refinements: tuple[annotate_type(GridRefinementRegion | GridRefinementLine), ...] = (
pd.Field(
(),
title="Mesh refinement structures",
@@ -234,4 +233,4 @@ def names_exist_bcs(cls, val, values):
return val
-UnstructuredGridType = Union[UniformUnstructuredGrid, DistanceUnstructuredGrid]
+UnstructuredGridType = UniformUnstructuredGrid | DistanceUnstructuredGrid
diff --git a/tidy3d/components/tcad/simulation/heat.py b/tidy3d/components/tcad/simulation/heat.py
index ace1d8a855..27a8c7276f 100644
--- a/tidy3d/components/tcad/simulation/heat.py
+++ b/tidy3d/components/tcad/simulation/heat.py
@@ -3,8 +3,6 @@
from __future__ import annotations
-from typing import Optional
-
import pydantic.v1 as pd
from tidy3d.components.tcad.simulation.heat_charge import HeatChargeSimulation
@@ -60,16 +58,16 @@ def issue_warning_deprecated(cls, values):
@add_ax_if_none
def plot_heat_conductivity(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
- alpha: Optional[float] = None,
- source_alpha: Optional[float] = None,
- monitor_alpha: Optional[float] = None,
+ alpha: float | None = None,
+ source_alpha: float | None = None,
+ monitor_alpha: float | None = None,
colorbar: str = "conductivity",
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
) -> Ax:
"""Plot each of simulation's components on a plane defined by one nonzero x,y,z coordinate.
diff --git a/tidy3d/components/tcad/simulation/heat_charge.py b/tidy3d/components/tcad/simulation/heat_charge.py
index 5598cfa48a..18834e6f94 100644
--- a/tidy3d/components/tcad/simulation/heat_charge.py
+++ b/tidy3d/components/tcad/simulation/heat_charge.py
@@ -1,10 +1,8 @@
-# ruff: noqa: W293, W291
"""Defines heat simulation class"""
from __future__ import annotations
from enum import Enum
-from typing import Optional, Union
import numpy as np
import pydantic.v1 as pd
@@ -110,7 +108,7 @@
ChargeSourceTypes = ()
ElectricBCTypes = (VoltageBC, CurrentBC, InsulatingBC)
-AnalysisSpecType = Union[ElectricalAnalysisType, UnsteadyHeatAnalysis]
+AnalysisSpecType = ElectricalAnalysisType | UnsteadyHeatAnalysis
# define some limits for transient heat simulations
TRANSIENT_HEAT_MAX_STEPS = 1000
@@ -303,12 +301,10 @@ class HeatChargeSimulation(AbstractSimulation):
description="Monitors in the simulation.",
)
- boundary_spec: tuple[annotate_type(Union[HeatChargeBoundarySpec, HeatBoundarySpec]), ...] = (
- pd.Field(
- (),
- title="Boundary Condition Specifications",
- description="List of boundary condition specifications.",
- )
+ boundary_spec: tuple[annotate_type(HeatChargeBoundarySpec | HeatBoundarySpec), ...] = pd.Field(
+ (),
+ title="Boundary Condition Specifications",
+ description="List of boundary condition specifications.",
)
# NOTE: creating a union with HeatBoundarySpec for backwards compatibility
@@ -1155,16 +1151,16 @@ def check_non_isothermal_is_possible(cls, values):
@add_ax_if_none
def plot_property(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
- alpha: Optional[float] = None,
- source_alpha: Optional[float] = None,
- monitor_alpha: Optional[float] = None,
+ alpha: float | None = None,
+ source_alpha: float | None = None,
+ monitor_alpha: float | None = None,
property: str = "heat_conductivity",
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
) -> Ax:
"""Plot each of simulation's components on a plane defined by one nonzero x,y,z coordinate.
@@ -1254,16 +1250,16 @@ def plot_property(
@add_ax_if_none
def plot_heat_conductivity(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
- alpha: Optional[float] = None,
- source_alpha: Optional[float] = None,
- monitor_alpha: Optional[float] = None,
+ alpha: float | None = None,
+ source_alpha: float | None = None,
+ monitor_alpha: float | None = None,
colorbar: str = "conductivity",
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
**kwargs,
) -> Ax:
"""
@@ -1301,7 +1297,7 @@ def plot_heat_conductivity(
The supplied or created matplotlib axes.
"""
log.warning(
- """This function `plot_heat_conductivity` is
+ """This function `plot_heat_conductivity` is
deprecated and will be discontinued. In its place you can use
`plot_property(property="heat_conductivity")`"""
)
@@ -1329,9 +1325,9 @@ def plot_heat_conductivity(
@add_ax_if_none
def plot_boundaries(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
property: str = "heat_conductivity",
ax: Ax = None,
) -> Ax:
@@ -1595,7 +1591,7 @@ def _construct_reverse_boundaries(
boundaries_reverse = []
for name, _, shape, bounds in shapes[:0:-1]:
- minx, miny, maxx, maxy = bounds
+ _minx, _miny, _maxx, _maxy = bounds
# intersect existing boundaries
for index, (_bc_spec, _name, _bdry, _bounds, _completed) in enumerate(
@@ -1712,13 +1708,13 @@ def _construct_heat_charge_boundaries(
@add_ax_if_none
def plot_sources(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
property: str = "heat_conductivity",
- hlim: Optional[tuple[float, float]] = None,
- vlim: Optional[tuple[float, float]] = None,
- alpha: Optional[float] = None,
+ hlim: tuple[float, float] | None = None,
+ vlim: tuple[float, float] | None = None,
+ alpha: float | None = None,
ax: Ax = None,
) -> Ax:
"""Plot each of simulation's sources on a plane defined by one nonzero x,y,z coordinate.
@@ -1837,7 +1833,7 @@ def _get_structure_source_plot_params(
source: HeatChargeSourceType,
source_min: float,
source_max: float,
- alpha: Optional[float] = None,
+ alpha: float | None = None,
) -> PlotParams:
"""Constructs the plot parameters for a given medium in simulation.plot_eps()."""
@@ -1864,7 +1860,7 @@ def _plot_shape_structure_source(
source_min: float,
source_max: float,
ax: Ax,
- alpha: Optional[float] = None,
+ alpha: float | None = None,
) -> Ax:
"""Plot a structure's cross section shape for a given medium, grayscale for permittivity."""
plot_params = self._get_structure_source_plot_params(
diff --git a/tidy3d/components/tcad/source/heat.py b/tidy3d/components/tcad/source/heat.py
index e47de2963d..c4f0ec90d1 100644
--- a/tidy3d/components/tcad/source/heat.py
+++ b/tidy3d/components/tcad/source/heat.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Union
-
import pydantic.v1 as pd
from tidy3d.components.data.data_array import SpatialDataArray
@@ -21,7 +19,7 @@ class HeatSource(StructureBasedHeatChargeSource):
>>> heat_source = HeatSource(rate=1, structures=["box"])
"""
- rate: Union[float, SpatialDataArray] = pd.Field(
+ rate: float | SpatialDataArray = pd.Field(
title="Volumetric Heat Rate",
description="Volumetric rate of heating or cooling (if negative).",
units=VOLUMETRIC_HEAT_RATE,
diff --git a/tidy3d/components/tcad/types.py b/tidy3d/components/tcad/types.py
index cf9b5c658f..c6411a532c 100644
--- a/tidy3d/components/tcad/types.py
+++ b/tidy3d/components/tcad/types.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Union
-
from tidy3d.components.tcad.bandgap import SlotboomBandGapNarrowing
from tidy3d.components.tcad.bandgap_energy import (
ConstantEnergyBandGap,
@@ -40,27 +38,28 @@
from tidy3d.components.tcad.source.coupled import HeatFromElectricSource
from tidy3d.components.tcad.source.heat import HeatSource, UniformHeatSource
-EffectiveDOSModelType = Union[
- ConstantEffectiveDOS, IsotropicEffectiveDOS, MultiValleyEffectiveDOS, DualValleyEffectiveDOS
-]
-EnergyBandGapModelType = Union[ConstantEnergyBandGap, VarshniEnergyBandGap]
-MobilityModelType = Union[CaugheyThomasMobility, ConstantMobilityModel]
-RecombinationModelType = Union[
- AugerRecombination, DistributedGeneration, RadiativeRecombination, ShockleyReedHallRecombination
-]
-BandGapNarrowingModelType = Union[SlotboomBandGapNarrowing]
+EffectiveDOSModelType = (
+ ConstantEffectiveDOS | IsotropicEffectiveDOS | MultiValleyEffectiveDOS | DualValleyEffectiveDOS
+)
+EnergyBandGapModelType = ConstantEnergyBandGap | VarshniEnergyBandGap
+MobilityModelType = CaugheyThomasMobility | ConstantMobilityModel
+RecombinationModelType = (
+ AugerRecombination
+ | DistributedGeneration
+ | RadiativeRecombination
+ | ShockleyReedHallRecombination
+)
+BandGapNarrowingModelType = SlotboomBandGapNarrowing
# types of monitors that are accepted by heat simulation
-HeatChargeMonitorType = Union[
- TemperatureMonitor,
- SteadyPotentialMonitor,
- SteadyFreeCarrierMonitor,
- SteadyEnergyBandMonitor,
- SteadyElectricFieldMonitor,
- SteadyCapacitanceMonitor,
- SteadyCurrentDensityMonitor,
-]
-HeatChargeSourceType = Union[HeatSource, HeatFromElectricSource, UniformHeatSource]
-HeatChargeBCType = Union[
- TemperatureBC, HeatFluxBC, ConvectionBC, VoltageBC, CurrentBC, InsulatingBC
-]
+HeatChargeMonitorType = (
+ TemperatureMonitor
+ | SteadyPotentialMonitor
+ | SteadyFreeCarrierMonitor
+ | SteadyEnergyBandMonitor
+ | SteadyElectricFieldMonitor
+ | SteadyCapacitanceMonitor
+ | SteadyCurrentDensityMonitor
+)
+HeatChargeSourceType = HeatSource | HeatFromElectricSource | UniformHeatSource
+HeatChargeBCType = TemperatureBC | HeatFluxBC | ConvectionBC | VoltageBC | CurrentBC | InsulatingBC
diff --git a/tidy3d/components/time_modulation.py b/tidy3d/components/time_modulation.py
index a4d82d7384..f0592b4183 100644
--- a/tidy3d/components/time_modulation.py
+++ b/tidy3d/components/time_modulation.py
@@ -4,7 +4,6 @@
from abc import ABC, abstractmethod
from math import isclose
-from typing import Union
import numpy as np
import pydantic.v1 as pd
@@ -79,7 +78,7 @@ def max_modulation(self) -> float:
return abs(self.amplitude)
-TimeModulationType = Union[ContinuousWaveTimeModulation]
+TimeModulationType = ContinuousWaveTimeModulation
class AbstractSpaceModulation(ABC, Tidy3dBaseModel):
@@ -128,14 +127,14 @@ class SpaceModulation(AbstractSpaceModulation):
>>> space = SpaceModulation(amplitude=amp, phase=phase)
"""
- amplitude: Union[float, SpatialDataArray] = pd.Field(
+ amplitude: float | SpatialDataArray = pd.Field(
1,
title="Amplitude of modulation in space",
description="Amplitude of modulation that can vary spatially. "
"It takes the unit of whatever is being modulated.",
)
- phase: Union[float, SpatialDataArray] = pd.Field(
+ phase: float | SpatialDataArray = pd.Field(
0,
title="Phase of modulation in space",
description="Phase of modulation that can vary spatially.",
@@ -199,7 +198,7 @@ def sel_inside(self, bounds: Bound) -> SpaceModulation:
return self.updated_copy(amplitude=amp_reduced, phase=phase_reduced)
-SpaceModulationType = Union[SpaceModulation]
+SpaceModulationType = SpaceModulation
class SpaceTimeModulation(Tidy3dBaseModel):
diff --git a/tidy3d/components/transformation.py b/tidy3d/components/transformation.py
index 4e2643a9ae..633b02ff3d 100644
--- a/tidy3d/components/transformation.py
+++ b/tidy3d/components/transformation.py
@@ -3,7 +3,6 @@
from __future__ import annotations
from abc import ABC, abstractmethod
-from typing import Union
import numpy as np
import pydantic.v1 as pd
@@ -74,7 +73,7 @@ def rotate_tensor(self, tensor: TensorReal) -> TensorReal:
class RotationAroundAxis(AbstractRotation):
"""Rotation of vectors and tensors around a given vector."""
- axis: Union[Axis, Coordinate] = pd.Field(
+ axis: Axis | Coordinate = pd.Field(
0,
title="Axis of Rotation",
description="A vector that specifies the axis of rotation, or a single int: 0, 1, or 2, "
@@ -201,5 +200,5 @@ def matrix(self) -> TensorReal:
return R
-RotationType = Union[RotationAroundAxis]
-ReflectionType = Union[ReflectionFromPlane]
+RotationType = RotationAroundAxis
+ReflectionType = ReflectionFromPlane
diff --git a/tidy3d/components/types/base.py b/tidy3d/components/types/base.py
index 60c8c8ea03..9f125c9408 100644
--- a/tidy3d/components/types/base.py
+++ b/tidy3d/components/types/base.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-from typing import Literal, Optional, Union
+from typing import Literal
import autograd.numpy as np
import pydantic.v1 as pydantic
@@ -104,9 +104,9 @@ def __modify_schema__(cls, field_schema):
def constrained_array(
- dtype: Optional[type] = None,
- ndim: Optional[int] = None,
- shape: Optional[tuple[pydantic.NonNegativeInt, ...]] = None,
+ dtype: type | None = None,
+ ndim: int | None = None,
+ shape: tuple[pydantic.NonNegativeInt, ...] | None = None,
) -> type:
"""Generate an ArrayLike sub-type with constraints built in."""
@@ -189,10 +189,10 @@ def __modify_schema__(cls, field_schema):
Size1D = pydantic.NonNegativeFloat
Size = tuple[Size1D, Size1D, Size1D]
Coordinate = tuple[float, float, float]
-CoordinateOptional = tuple[Optional[float], Optional[float], Optional[float]]
+CoordinateOptional = tuple[float | None, float | None, float | None]
Coordinate2D = tuple[float, float]
Bound = tuple[Coordinate, Coordinate]
-GridSize = Union[pydantic.PositiveFloat, tuple[pydantic.PositiveFloat, ...]]
+GridSize = pydantic.PositiveFloat | tuple[pydantic.PositiveFloat, ...]
Axis = Literal[0, 1, 2]
Axis2D = Literal[0, 1]
Shapely = BaseGeometry
@@ -207,8 +207,8 @@ def __modify_schema__(cls, field_schema):
# custom medium
InterpMethod = Literal["nearest", "linear"]
-# Complex = Union[complex, ComplexNumber]
-Complex = Union[tidycomplex, ComplexNumber]
+# Complex = complex | ComplexNumber
+Complex = tidycomplex | ComplexNumber
PoleAndResidue = tuple[Complex, Complex]
# PoleAndResidue = Tuple[Tuple[float, float], Tuple[float, float]]
@@ -227,8 +227,8 @@ def __modify_schema__(cls, field_schema):
EMField = Literal["Ex", "Ey", "Ez", "Hx", "Hy", "Hz"]
FieldType = Literal["Ex", "Ey", "Ez", "Hx", "Hy", "Hz"]
-FreqArray = Union[tuple[float, ...], ArrayFloat1D]
-ObsGridArray = Union[tuple[float, ...], ArrayFloat1D]
+FreqArray = tuple[float, ...] | ArrayFloat1D
+ObsGridArray = tuple[float, ...] | ArrayFloat1D
PolarizationBasis = Literal["linear", "circular"]
AuxField = Literal["Nfx", "Nfy", "Nfz"]
diff --git a/tidy3d/components/types/mode_spec.py b/tidy3d/components/types/mode_spec.py
index 1e11a69122..566e887f91 100644
--- a/tidy3d/components/types/mode_spec.py
+++ b/tidy3d/components/types/mode_spec.py
@@ -2,10 +2,8 @@
from __future__ import annotations
-from typing import Union
-
from tidy3d.components.microwave.mode_spec import MicrowaveModeSpec
from tidy3d.components.mode_spec import ModeSpec
# Type aliases
-ModeSpecType = Union[ModeSpec, MicrowaveModeSpec]
+ModeSpecType = ModeSpec | MicrowaveModeSpec
diff --git a/tidy3d/components/types/monitor.py b/tidy3d/components/types/monitor.py
index d8585cab51..8ce5d50433 100644
--- a/tidy3d/components/types/monitor.py
+++ b/tidy3d/components/types/monitor.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Union
-
from tidy3d.components.microwave.monitor import MicrowaveModeMonitor, MicrowaveModeSolverMonitor
from tidy3d.components.monitor import (
AuxFieldTimeMonitor,
@@ -23,21 +21,21 @@
)
# types of monitors that are accepted by simulation
-MonitorType = Union[
- FieldMonitor,
- FieldTimeMonitor,
- AuxFieldTimeMonitor,
- MediumMonitor,
- PermittivityMonitor,
- FluxMonitor,
- FluxTimeMonitor,
- ModeMonitor,
- ModeSolverMonitor,
- FieldProjectionAngleMonitor,
- FieldProjectionCartesianMonitor,
- FieldProjectionKSpaceMonitor,
- DiffractionMonitor,
- DirectivityMonitor,
- MicrowaveModeMonitor,
- MicrowaveModeSolverMonitor,
-]
+MonitorType = (
+ FieldMonitor
+ | FieldTimeMonitor
+ | AuxFieldTimeMonitor
+ | MediumMonitor
+ | PermittivityMonitor
+ | FluxMonitor
+ | FluxTimeMonitor
+ | ModeMonitor
+ | ModeSolverMonitor
+ | FieldProjectionAngleMonitor
+ | FieldProjectionCartesianMonitor
+ | FieldProjectionKSpaceMonitor
+ | DiffractionMonitor
+ | DirectivityMonitor
+ | MicrowaveModeMonitor
+ | MicrowaveModeSolverMonitor
+)
diff --git a/tidy3d/components/types/monitor_data.py b/tidy3d/components/types/monitor_data.py
index 50f5188865..9d7d253133 100644
--- a/tidy3d/components/types/monitor_data.py
+++ b/tidy3d/components/types/monitor_data.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Union
-
from tidy3d.components.data.monitor_data import (
AuxFieldTimeData,
DiffractionData,
@@ -23,8 +21,8 @@
from tidy3d.components.microwave.data.monitor_data import MicrowaveModeData, MicrowaveModeSolverData
# Type aliases
-ModeDataType = Union[ModeData, MicrowaveModeData]
-ModeSolverDataType = Union[ModeSolverData, MicrowaveModeSolverData]
+ModeDataType = ModeData | MicrowaveModeData
+ModeSolverDataType = ModeSolverData | MicrowaveModeSolverData
MonitorDataTypes = (
FieldData,
FieldTimeData,
@@ -43,4 +41,21 @@
MicrowaveModeData,
MicrowaveModeSolverData,
)
-MonitorDataType = Union[MonitorDataTypes]
+MonitorDataType = (
+ FieldData
+ | FieldTimeData
+ | PermittivityData
+ | MediumData
+ | ModeSolverData
+ | ModeData
+ | FluxData
+ | FluxTimeData
+ | AuxFieldTimeData
+ | FieldProjectionKSpaceData
+ | FieldProjectionCartesianData
+ | FieldProjectionAngleData
+ | DiffractionData
+ | DirectivityData
+ | MicrowaveModeData
+ | MicrowaveModeSolverData
+)
diff --git a/tidy3d/components/types/simulation.py b/tidy3d/components/types/simulation.py
index 1194b5cfbd..75df93b39f 100644
--- a/tidy3d/components/types/simulation.py
+++ b/tidy3d/components/types/simulation.py
@@ -1,7 +1,5 @@
from __future__ import annotations
-from typing import Union
-
from tidy3d.components.data.monitor_data import ModeSolverData
from tidy3d.components.data.sim_data import SimulationData
from tidy3d.components.eme.data.sim_data import EMESimulationData
@@ -20,22 +18,22 @@
from tidy3d.components.tcad.simulation.heat_charge import HeatChargeSimulation
from tidy3d.plugins.mode.mode_solver import ModeSolver
-SimulationType = Union[
- Simulation,
- HeatChargeSimulation,
- HeatSimulation,
- EMESimulation,
- ModeSolver,
- ModeSimulation,
- VolumeMesher,
-]
-SimulationDataType = Union[
- SimulationData,
- HeatChargeSimulationData,
- HeatSimulationData,
- EMESimulationData,
- MicrowaveModeSolverData,
- ModeSolverData,
- ModeSimulationData,
- VolumeMesherData,
-]
+SimulationType = (
+ Simulation
+ | HeatChargeSimulation
+ | HeatSimulation
+ | EMESimulation
+ | ModeSolver
+ | ModeSimulation
+ | VolumeMesher
+)
+SimulationDataType = (
+ SimulationData
+ | HeatChargeSimulationData
+ | HeatSimulationData
+ | EMESimulationData
+ | MicrowaveModeSolverData
+ | ModeSolverData
+ | ModeSimulationData
+ | VolumeMesherData
+)
diff --git a/tidy3d/components/types/workflow.py b/tidy3d/components/types/workflow.py
index 5527270c0d..d15c1113ad 100644
--- a/tidy3d/components/types/workflow.py
+++ b/tidy3d/components/types/workflow.py
@@ -1,7 +1,5 @@
from __future__ import annotations
-from typing import Union
-
from tidy3d.components.types.simulation import SimulationDataType, SimulationType
from tidy3d.plugins.smatrix.component_modelers.modal import (
ModalComponentModeler,
@@ -16,13 +14,5 @@
TerminalComponentModelerData,
)
-WorkflowType = Union[
- SimulationType,
- ModalComponentModeler,
- TerminalComponentModeler,
-]
-WorkflowDataType = Union[
- SimulationDataType,
- ModalComponentModelerData,
- TerminalComponentModelerData,
-]
+WorkflowType = SimulationType | ModalComponentModeler | TerminalComponentModeler
+WorkflowDataType = SimulationDataType | ModalComponentModelerData | TerminalComponentModelerData
diff --git a/tidy3d/components/validators.py b/tidy3d/components/validators.py
index 4116a7e2ca..61f8c94c61 100644
--- a/tidy3d/components/validators.py
+++ b/tidy3d/components/validators.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-from typing import Any, Optional
+from typing import Any
import numpy as np
import pydantic.v1 as pydantic
@@ -374,7 +374,7 @@ def validate_parameter_perturbation(
field_name: str,
base_field_name: str,
allowed_real_range: tuple[tuple[float, float], ...],
- allowed_imag_range: Optional[tuple[tuple[float, float], ...]] = None,
+ allowed_imag_range: tuple[tuple[float, float], ...] | None = None,
allowed_complex: bool = True,
):
"""Assert perturbations do not drive a parameter out of physical bounds."""
diff --git a/tidy3d/components/viz/axes_utils.py b/tidy3d/components/viz/axes_utils.py
index 85007e773c..93f79d85c3 100644
--- a/tidy3d/components/viz/axes_utils.py
+++ b/tidy3d/components/viz/axes_utils.py
@@ -1,7 +1,6 @@
from __future__ import annotations
from functools import wraps
-from typing import Optional
from tidy3d.components.types import Ax, Axis, LengthUnit
from tidy3d.constants import UnitScaling
@@ -142,7 +141,7 @@ def set_default_labels_and_title(
axis: Axis,
position: float,
ax: Ax,
- plot_length_units: Optional[LengthUnit] = None,
+ plot_length_units: LengthUnit | None = None,
) -> Ax:
"""Adds axis labels and title to plots involving spatial dimensions.
When the ``plot_length_units`` are specified, the plot axes are scaled, and
diff --git a/tidy3d/components/viz/visualization_spec.py b/tidy3d/components/viz/visualization_spec.py
index abe22ed7cf..8f20ccc1db 100644
--- a/tidy3d/components/viz/visualization_spec.py
+++ b/tidy3d/components/viz/visualization_spec.py
@@ -1,6 +1,6 @@
from __future__ import annotations
-from typing import Any, Optional
+from typing import Any
import pydantic.v1 as pd
@@ -38,13 +38,13 @@ class VisualizationSpec(Tidy3dBaseModel):
description="Color applied to the faces in visualization.",
)
- edgecolor: Optional[str] = pd.Field(
+ edgecolor: str | None = pd.Field(
"",
title="Edge color",
description="Color applied to the edges in visualization.",
)
- alpha: Optional[pd.confloat(ge=0.0, le=1.0)] = pd.Field(
+ alpha: pd.confloat(ge=0.0, le=1.0) | None = pd.Field(
1.0,
title="Opacity",
description="Opacity/alpha value in plotting between 0 and 1.",
diff --git a/tidy3d/config/legacy.py b/tidy3d/config/legacy.py
index adc5a10436..36eeba6f59 100644
--- a/tidy3d/config/legacy.py
+++ b/tidy3d/config/legacy.py
@@ -9,7 +9,7 @@
import os
import ssl
from pathlib import Path
-from typing import Any, Optional
+from typing import Any
import toml
@@ -130,17 +130,17 @@ class LegacyEnvironmentConfig:
def __init__(
self,
- manager: Optional[ConfigManager] = None,
- name: Optional[str] = None,
+ manager: ConfigManager | None = None,
+ name: str | None = None,
*,
- web_api_endpoint: Optional[str] = None,
- website_endpoint: Optional[str] = None,
- s3_region: Optional[str] = None,
- ssl_verify: Optional[bool] = None,
- enable_caching: Optional[bool] = None,
- ssl_version: Optional[ssl.TLSVersion] = None,
- env_vars: Optional[dict[str, str]] = None,
- environment: Optional[LegacyEnvironment] = None,
+ web_api_endpoint: str | None = None,
+ website_endpoint: str | None = None,
+ s3_region: str | None = None,
+ ssl_verify: bool | None = None,
+ enable_caching: bool | None = None,
+ ssl_version: ssl.TLSVersion | None = None,
+ env_vars: dict[str, str] | None = None,
+ environment: LegacyEnvironment | None = None,
) -> None:
if name is None:
raise ValueError("Environment name is required")
@@ -165,7 +165,7 @@ def __init__(
self._overrides["env_vars"] = dict(env_vars)
@property
- def manager(self) -> Optional[ConfigManager]:
+ def manager(self) -> ConfigManager | None:
return self._manager
def active(self) -> None:
@@ -181,17 +181,17 @@ def active(self) -> None:
environment.set_current(self)
@property
- def web_api_endpoint(self) -> Optional[str]:
+ def web_api_endpoint(self) -> str | None:
value = self._value("api_endpoint")
return _maybe_str(value)
@property
- def website_endpoint(self) -> Optional[str]:
+ def website_endpoint(self) -> str | None:
value = self._value("website_endpoint")
return _maybe_str(value)
@property
- def s3_region(self) -> Optional[str]:
+ def s3_region(self) -> str | None:
return self._value("s3_region")
@property
@@ -266,7 +266,7 @@ class LegacyEnvironment:
"""Legacy Env wrapper that maps to profiles."""
def __init__(self, manager: ConfigManager):
- self._previous_env_vars: dict[str, Optional[str]] = {}
+ self._previous_env_vars: dict[str, str | None] = {}
self.reset_manager(manager)
def reset_manager(self, manager: ConfigManager) -> None:
@@ -341,7 +341,7 @@ def _restore_env_vars(self) -> None:
self._previous_env_vars = {}
-def _maybe_str(value: Any) -> Optional[str]:
+def _maybe_str(value: Any) -> str | None:
if value is None:
return None
return str(value)
diff --git a/tidy3d/config/loader.py b/tidy3d/config/loader.py
index b536701caa..b29a565704 100644
--- a/tidy3d/config/loader.py
+++ b/tidy3d/config/loader.py
@@ -7,7 +7,7 @@
import tempfile
from copy import deepcopy
from pathlib import Path
-from typing import Any, Optional
+from typing import Any
import toml
import tomlkit
@@ -21,7 +21,7 @@
class ConfigLoader:
"""Handle reading and writing configuration files."""
- def __init__(self, config_dir: Optional[Path] = None):
+ def __init__(self, config_dir: Path | None = None):
self.config_dir = config_dir or resolve_config_directory()
self.config_dir.mkdir(mode=0o700, parents=True, exist_ok=True)
self._docs: dict[Path, tomlkit.TOMLDocument] = {}
@@ -160,7 +160,7 @@ def load_environment_overrides() -> dict[str, Any]:
if not segments:
continue
if segments[0] == "auth":
- segments = ("web",) + segments[1:]
+ segments = ("web", *segments[1:])
_assign_path(overrides, segments, value)
return overrides
diff --git a/tidy3d/config/manager.py b/tidy3d/config/manager.py
index 7919aafefc..a371737b71 100644
--- a/tidy3d/config/manager.py
+++ b/tidy3d/config/manager.py
@@ -10,7 +10,7 @@
from enum import Enum
from io import StringIO
from pathlib import Path
-from typing import Any, Optional, get_args, get_origin
+from typing import Any, get_args, get_origin
from pydantic import BaseModel
from rich.console import Console
@@ -109,8 +109,8 @@ class ConfigManager:
def __init__(
self,
- profile: Optional[str] = None,
- config_dir: Optional[os.PathLike[str]] = None,
+ profile: str | None = None,
+ config_dir: os.PathLike[str] | None = None,
):
loader_path = None if config_dir is None else Path(config_dir)
self._loader = ConfigLoader(loader_path)
@@ -124,7 +124,7 @@ def __init__(
self._raw_tree: dict[str, Any] = {}
self._effective_tree: dict[str, Any] = {}
self._env_overrides: dict[str, Any] = load_environment_overrides()
- self._web_env_previous: dict[str, Optional[str]] = {}
+ self._web_env_previous: dict[str, str | None] = {}
attach_manager(self)
self._reload()
@@ -298,7 +298,7 @@ def on_section_registered(self, section: str) -> None:
def on_handler_registered(self, section: str) -> None:
self._apply_handlers(section=section)
- def _resolve_initial_profile(self, profile: Optional[str]) -> str:
+ def _resolve_initial_profile(self, profile: str | None) -> str:
if profile:
return normalize_profile_name(str(profile))
@@ -354,13 +354,13 @@ def _build_models(self) -> None:
self._section_models = new_sections
self._plugin_models = new_plugins
- def _get_model(self, name: str) -> Optional[BaseModel]:
+ def _get_model(self, name: str) -> BaseModel | None:
if name.startswith("plugins."):
plugin = name.split(".", 1)[1]
return self._plugin_models.get(plugin)
return self._section_models.get(name)
- def _apply_handlers(self, section: Optional[str] = None) -> None:
+ def _apply_handlers(self, section: str | None = None) -> None:
handlers = get_handlers()
targets = [section] if section else handlers.keys()
for target in targets:
@@ -445,7 +445,7 @@ def __str__(self) -> str:
return self.format()
-def _deep_get(tree: dict[str, Any], path: Iterable[str]) -> Optional[dict[str, Any]]:
+def _deep_get(tree: dict[str, Any], path: Iterable[str]) -> dict[str, Any] | None:
node: Any = tree
for segment in path:
if not isinstance(node, dict):
@@ -456,7 +456,7 @@ def _deep_get(tree: dict[str, Any], path: Iterable[str]) -> Optional[dict[str, A
return node if isinstance(node, dict) else None
-def _resolve_model_type(annotation: Any) -> Optional[type[BaseModel]]:
+def _resolve_model_type(annotation: Any) -> type[BaseModel] | None:
"""Return the first BaseModel subclass found in an annotation (if any)."""
if isinstance(annotation, type) and issubclass(annotation, BaseModel):
diff --git a/tidy3d/config/registry.py b/tidy3d/config/registry.py
index 8ee8e216a5..42c6d1d9a5 100644
--- a/tidy3d/config/registry.py
+++ b/tidy3d/config/registry.py
@@ -2,7 +2,8 @@
from __future__ import annotations
-from typing import Callable, Optional, TypeVar
+from collections.abc import Callable
+from typing import TypeVar
from pydantic import BaseModel
@@ -10,7 +11,7 @@
_SECTIONS: dict[str, type[BaseModel]] = {}
_HANDLERS: dict[str, Callable[[BaseModel], None]] = {}
-_MANAGER: Optional[ConfigManagerProtocol] = None
+_MANAGER: ConfigManagerProtocol | None = None
class ConfigManagerProtocol:
@@ -30,7 +31,7 @@ def attach_manager(manager: ConfigManagerProtocol) -> None:
_MANAGER = manager
-def get_manager() -> Optional[ConfigManagerProtocol]:
+def get_manager() -> ConfigManagerProtocol | None:
"""Return the currently attached configuration manager, if any."""
return _MANAGER
diff --git a/tidy3d/config/sections.py b/tidy3d/config/sections.py
index 62e5b8a473..7234155cfe 100644
--- a/tidy3d/config/sections.py
+++ b/tidy3d/config/sections.py
@@ -4,7 +4,7 @@
import ssl
from pathlib import Path
-from typing import Any, Literal, Optional
+from typing import Any, Literal
from urllib.parse import urlparse
import numpy as np
@@ -75,7 +75,7 @@ def apply_logging(config: LoggingConfig) -> None:
class SimulationConfig(ConfigSection):
"""Simulation-related configuration."""
- use_local_subpixel: Optional[bool] = Field(
+ use_local_subpixel: bool | None = Field(
None,
title="Use local subpixel",
description=(
@@ -209,7 +209,7 @@ class AdjointConfig(ConfigSection):
ge=0.0,
)
- solver_freq_chunk_size: Optional[PositiveInt] = Field(
+ solver_freq_chunk_size: PositiveInt | None = Field(
None,
title="Adjoint frequency chunk size",
description=(
@@ -277,7 +277,7 @@ def apply_adjoint(config: AdjointConfig) -> None:
class WebConfig(ConfigSection):
"""Web/HTTP configuration."""
- apikey: Optional[SecretStr] = Field(
+ apikey: SecretStr | None = Field(
None,
title="API key",
description="Tidy3D API key.",
@@ -323,7 +323,7 @@ class WebConfig(ConfigSection):
le=300,
)
- ssl_version: Optional[ssl.TLSVersion] = Field(
+ ssl_version: ssl.TLSVersion | None = Field(
None,
title="SSL/TLS version",
description="Optional SSL/TLS version to enforce for requests.",
diff --git a/tidy3d/exceptions.py b/tidy3d/exceptions.py
index 96b137d749..5629fc2a05 100644
--- a/tidy3d/exceptions.py
+++ b/tidy3d/exceptions.py
@@ -2,15 +2,13 @@
from __future__ import annotations
-from typing import Optional
-
from .log import log
class Tidy3dError(ValueError):
"""Any error in tidy3d"""
- def __init__(self, message: Optional[str] = None):
+ def __init__(self, message: str | None = None):
"""Log just the error message and then raise the Exception."""
super().__init__(message)
log.error(message)
diff --git a/tidy3d/log.py b/tidy3d/log.py
index 3cfa825773..07ecdb2be6 100644
--- a/tidy3d/log.py
+++ b/tidy3d/log.py
@@ -3,18 +3,18 @@
from __future__ import annotations
import inspect
+from collections.abc import Callable
from contextlib import contextmanager
from datetime import datetime
-from typing import Callable, Optional, Union
+from typing import Literal
from rich.console import Console
from rich.text import Text
-from typing_extensions import Literal
# Note: "SUPPORT" and "USER" levels are meant for backend runs only.
# Logging in frontend code should just use the standard debug/info/warning/error/critical.
LogLevel = Literal["DEBUG", "SUPPORT", "USER", "INFO", "WARNING", "ERROR", "CRITICAL"]
-LogValue = Union[int, LogLevel]
+LogValue = int | LogLevel
# Logging levels compatible with logging module
_level_value = {
@@ -245,7 +245,7 @@ def _log(
message: str,
*args,
log_once: bool = False,
- custom_loc: Optional[list] = None,
+ custom_loc: list | None = None,
capture: bool = True,
) -> None:
"""Distribute log messages to all handlers"""
@@ -314,7 +314,7 @@ def warning(
message: str,
*args,
log_once: bool = False,
- custom_loc: Optional[list] = None,
+ custom_loc: list | None = None,
capture: bool = True,
) -> None:
"""Log (message) % (args) at warning level"""
diff --git a/tidy3d/material_library/material_library.py b/tidy3d/material_library/material_library.py
index 37bb932b27..6d09e27079 100644
--- a/tidy3d/material_library/material_library.py
+++ b/tidy3d/material_library/material_library.py
@@ -3,7 +3,6 @@
from __future__ import annotations
import json
-from typing import Union
import pydantic.v1 as pd
@@ -84,7 +83,7 @@ class AbstractVariantItem(Tidy3dBaseModel):
)
@property
- def summarize_mediums(self) -> dict[str, Union[PoleResidue, Medium2D, MultiPhysicsMedium]]:
+ def summarize_mediums(self) -> dict[str, PoleResidue | Medium2D | MultiPhysicsMedium]:
return {}
def __str__(self):
@@ -100,14 +99,14 @@ def _repr_pretty_(self, p, cycle):
class VariantItem(AbstractVariantItem):
"""Reference, data_source, and material model for a variant of a material."""
- medium: Union[PoleResidue, MultiPhysicsMedium] = pd.Field(
+ medium: PoleResidue | MultiPhysicsMedium = pd.Field(
...,
title="Material dispersion model",
description="A dispersive medium described by the pole-residue pair model.",
)
@property
- def summarize_mediums(self) -> dict[str, Union[PoleResidue, Medium2D, MultiPhysicsMedium]]:
+ def summarize_mediums(self) -> dict[str, PoleResidue | Medium2D | MultiPhysicsMedium]:
return {"medium": self.medium}
@@ -171,7 +170,7 @@ class VariantItem2D(AbstractVariantItem):
)
@property
- def summarize_mediums(self) -> dict[str, Union[PoleResidue, Medium2D, MultiPhysicsMedium]]:
+ def summarize_mediums(self) -> dict[str, PoleResidue | Medium2D | MultiPhysicsMedium]:
return {"medium": self.medium}
@@ -222,7 +221,7 @@ def medium(self, optical_axis: Axis) -> AnisotropicMedium:
return AnisotropicMedium.parse_obj(mat_dict)
@property
- def summarize_mediums(self) -> dict[str, Union[PoleResidue, Medium2D, MultiPhysicsMedium]]:
+ def summarize_mediums(self) -> dict[str, PoleResidue | Medium2D | MultiPhysicsMedium]:
return {"ordinary": self.ordinary, "extraordinary": self.extraordinary}
diff --git a/tidy3d/plugins/autograd/differential_operators.py b/tidy3d/plugins/autograd/differential_operators.py
index 3bd92356eb..f6ab754229 100644
--- a/tidy3d/plugins/autograd/differential_operators.py
+++ b/tidy3d/plugins/autograd/differential_operators.py
@@ -1,6 +1,6 @@
from __future__ import annotations
-from typing import Callable
+from collections.abc import Callable
from autograd.builtins import tuple as atuple
from autograd.core import make_vjp
diff --git a/tidy3d/plugins/autograd/functions.py b/tidy3d/plugins/autograd/functions.py
index 34516e89a8..db11958c23 100644
--- a/tidy3d/plugins/autograd/functions.py
+++ b/tidy3d/plugins/autograd/functions.py
@@ -1,7 +1,7 @@
from __future__ import annotations
-from collections.abc import Iterable
-from typing import Callable, Literal, Union
+from collections.abc import Callable, Iterable
+from typing import Literal
import autograd.numpy as np
import numpy as onp
@@ -88,10 +88,10 @@ def _get_pad_indices(
def pad(
array: NDArray,
- pad_width: Union[int, tuple[int, int]],
+ pad_width: int | tuple[int, int],
*,
mode: PaddingType = "constant",
- axis: Union[int, Iterable[int], None] = None,
+ axis: int | Iterable[int] | None = None,
constant_value: float = 0.0,
) -> NDArray:
"""Pad an array along specified axes with a given mode and padding width.
@@ -157,7 +157,7 @@ def convolve(
kernel: NDArray,
*,
padding: PaddingType = "constant",
- axes: Union[tuple[list[int], list[int]], None] = None,
+ axes: tuple[list[int], list[int]] | None = None,
mode: Literal["full", "valid", "same"] = "same",
) -> NDArray:
"""Convolve an array with a given kernel.
@@ -226,8 +226,8 @@ def _get_footprint(size, structure, maxval):
@primitive
def grey_dilation(
array: NDArray,
- size: Union[int, tuple[int, int], None] = None,
- structure: Union[NDArray, None] = None,
+ size: int | tuple[int, int] | None = None,
+ structure: NDArray | None = None,
*,
mode: PaddingType = "reflect",
maxval: float = 1e4,
@@ -341,8 +341,8 @@ def vjp(g):
def grey_erosion(
array: NDArray,
- size: Union[Union[int, tuple[int, int]], None] = None,
- structure: Union[NDArray, None] = None,
+ size: int | tuple[int, int] | None = None,
+ structure: NDArray | None = None,
*,
mode: PaddingType = "reflect",
maxval: float = 1e4,
@@ -383,8 +383,8 @@ def grey_erosion(
def grey_opening(
array: NDArray,
- size: Union[Union[int, tuple[int, int]], None] = None,
- structure: Union[NDArray, None] = None,
+ size: int | tuple[int, int] | None = None,
+ structure: NDArray | None = None,
*,
mode: PaddingType = "reflect",
maxval: float = 1e4,
@@ -416,8 +416,8 @@ def grey_opening(
def grey_closing(
array: NDArray,
- size: Union[Union[int, tuple[int, int]], None] = None,
- structure: Union[NDArray, None] = None,
+ size: int | tuple[int, int] | None = None,
+ structure: NDArray | None = None,
*,
mode: PaddingType = "reflect",
maxval: float = 1e4,
@@ -449,8 +449,8 @@ def grey_closing(
def morphological_gradient(
array: NDArray,
- size: Union[Union[int, tuple[int, int]], None] = None,
- structure: Union[NDArray, None] = None,
+ size: int | tuple[int, int] | None = None,
+ structure: NDArray | None = None,
*,
mode: PaddingType = "reflect",
maxval: float = 1e4,
@@ -482,8 +482,8 @@ def morphological_gradient(
def morphological_gradient_internal(
array: NDArray,
- size: Union[Union[int, tuple[int, int]], None] = None,
- structure: Union[NDArray, None] = None,
+ size: int | tuple[int, int] | None = None,
+ structure: NDArray | None = None,
*,
mode: PaddingType = "reflect",
maxval: float = 1e4,
@@ -513,8 +513,8 @@ def morphological_gradient_internal(
def morphological_gradient_external(
array: NDArray,
- size: Union[Union[int, tuple[int, int]], None] = None,
- structure: Union[NDArray, None] = None,
+ size: int | tuple[int, int] | None = None,
+ structure: NDArray | None = None,
*,
mode: PaddingType = "reflect",
maxval: float = 1e4,
@@ -582,7 +582,7 @@ def rescale(
def threshold(
- array: NDArray, vmin: float = 0.0, vmax: float = 1.0, level: Union[float, None] = None
+ array: NDArray, vmin: float = 0.0, vmax: float = 1.0, level: float | None = None
) -> NDArray:
"""Apply a threshold to an array, setting values below the threshold to `vmin` and values above to `vmax`.
@@ -618,9 +618,7 @@ def threshold(
return np.where(array < level, vmin, vmax)
-def smooth_max(
- x: NDArray, tau: float = 1.0, axis: Union[int, tuple[int, ...], None] = None
-) -> float:
+def smooth_max(x: NDArray, tau: float = 1.0, axis: int | tuple[int, ...] | None = None) -> float:
"""Compute the smooth maximum of an array using temperature parameter tau.
Parameters
@@ -640,9 +638,7 @@ def smooth_max(
return tau * logsumexp(x / tau, axis=axis)
-def smooth_min(
- x: NDArray, tau: float = 1.0, axis: Union[int, tuple[int, ...], None] = None
-) -> float:
+def smooth_min(x: NDArray, tau: float = 1.0, axis: int | tuple[int, ...] | None = None) -> float:
"""Compute the smooth minimum of an array using temperature parameter tau.
Parameters
diff --git a/tidy3d/plugins/autograd/invdes/filters.py b/tidy3d/plugins/autograd/invdes/filters.py
index b8bb019563..52f205b03b 100644
--- a/tidy3d/plugins/autograd/invdes/filters.py
+++ b/tidy3d/plugins/autograd/invdes/filters.py
@@ -1,9 +1,9 @@
from __future__ import annotations
import abc
-from collections.abc import Iterable
+from collections.abc import Callable, Iterable
from functools import lru_cache, partial
-from typing import Annotated, Callable, Optional, Union
+from typing import Annotated
import numpy as np
import pydantic.v1 as pd
@@ -20,7 +20,7 @@
class AbstractFilter(Tidy3dBaseModel, abc.ABC):
"""An abstract class for creating and applying convolution filters."""
- kernel_size: Union[pd.PositiveInt, tuple[pd.PositiveInt, ...]] = pd.Field(
+ kernel_size: pd.PositiveInt | tuple[pd.PositiveInt, ...] = pd.Field(
..., title="Kernel Size", description="Size of the kernel in pixels for each dimension."
)
normalize: bool = pd.Field(
@@ -32,7 +32,7 @@ class AbstractFilter(Tidy3dBaseModel, abc.ABC):
@classmethod
def from_radius_dl(
- cls, radius: Union[float, tuple[float, ...]], dl: Union[float, tuple[float, ...]], **kwargs
+ cls, radius: float | tuple[float, ...], dl: float | tuple[float, ...], **kwargs
) -> AbstractFilter:
"""Create a filter from radius and grid spacing.
@@ -125,9 +125,9 @@ def get_kernel(size_px: Iterable[int], normalize: bool) -> NDArray:
def _get_kernel_size(
- radius: Union[float, tuple[float, ...]],
- dl: Union[float, tuple[float, ...]],
- size_px: Union[int, tuple[int, ...]],
+ radius: float | tuple[float, ...],
+ dl: float | tuple[float, ...],
+ size_px: int | tuple[int, ...],
) -> tuple[int, ...]:
"""Determine the kernel size based on the provided radius, grid spacing, or size in pixels.
@@ -163,10 +163,10 @@ def _get_kernel_size(
def make_filter(
- radius: Optional[Union[float, tuple[float, ...]]] = None,
- dl: Optional[Union[float, tuple[float, ...]]] = None,
+ radius: float | tuple[float, ...] | None = None,
+ dl: float | tuple[float, ...] | None = None,
*,
- size_px: Optional[Union[int, tuple[int, ...]]] = None,
+ size_px: int | tuple[int, ...] | None = None,
normalize: bool = True,
padding: PaddingType = "reflect",
filter_type: KernelType,
@@ -225,4 +225,4 @@ def make_filter(
:func:`~filters.make_filter` : Function to create a filter based on the specified kernel type and size.
"""
-FilterType = Annotated[Union[ConicFilter, CircularFilter], pd.Field(discriminator=TYPE_TAG_STR)]
+FilterType = Annotated[ConicFilter | CircularFilter, pd.Field(discriminator=TYPE_TAG_STR)]
diff --git a/tidy3d/plugins/autograd/invdes/parametrizations.py b/tidy3d/plugins/autograd/invdes/parametrizations.py
index a21e22c1d0..973e3ca9bc 100644
--- a/tidy3d/plugins/autograd/invdes/parametrizations.py
+++ b/tidy3d/plugins/autograd/invdes/parametrizations.py
@@ -1,7 +1,8 @@
from __future__ import annotations
from collections import deque
-from typing import Callable, Literal, Optional, Union
+from collections.abc import Callable
+from typing import Literal
import autograd.numpy as np
import pydantic.v1 as pd
@@ -22,13 +23,13 @@
class FilterAndProject(Tidy3dBaseModel):
"""A class that combines filtering and projection operations."""
- radius: Union[float, tuple[float, ...]] = pd.Field(
+ radius: float | tuple[float, ...] = pd.Field(
..., title="Radius", description="The radius of the kernel."
)
- dl: Union[float, tuple[float, ...]] = pd.Field(
+ dl: float | tuple[float, ...] = pd.Field(
..., title="Grid Spacing", description="The grid spacing."
)
- size_px: Union[int, tuple[int, ...]] = pd.Field(
+ size_px: int | tuple[int, ...] = pd.Field(
None, title="Size in Pixels", description="The size of the kernel in pixels."
)
beta: pd.NonNegativeFloat = pd.Field(
@@ -45,7 +46,7 @@ class FilterAndProject(Tidy3dBaseModel):
)
def __call__(
- self, array: NDArray, beta: Optional[float] = None, eta: Optional[float] = None
+ self, array: NDArray, beta: float | None = None, eta: float | None = None
) -> NDArray:
"""Apply the filter and projection to an input array.
@@ -78,10 +79,10 @@ def __call__(
def make_filter_and_project(
- radius: Optional[Union[float, tuple[float, ...]]] = None,
- dl: Optional[Union[float, tuple[float, ...]]] = None,
+ radius: float | tuple[float, ...] | None = None,
+ dl: float | tuple[float, ...] | None = None,
*,
- size_px: Optional[Union[int, tuple[int, ...]]] = None,
+ size_px: int | tuple[int, ...] | None = None,
beta: float = BETA_DEFAULT,
eta: float = ETA_DEFAULT,
filter_type: KernelType = "conic",
@@ -109,10 +110,10 @@ def initialize_params_from_simulation(
param_to_structure: Callable[..., td.Structure],
params0: np.ndarray,
*,
- freq: Optional[float] = None,
+ freq: float | None = None,
outside_handling: Literal["extrapolate", "mask", "nan"] = "mask",
maxiter: int = 100,
- bounds: tuple[Optional[float], Optional[float]] = (0.0, 1.0),
+ bounds: tuple[float | None, float | None] = (0.0, 1.0),
rel_improve_tol: float = 1e-3,
verbose: bool = False,
**param_kwargs,
diff --git a/tidy3d/plugins/autograd/invdes/penalties.py b/tidy3d/plugins/autograd/invdes/penalties.py
index 92eaac6e79..4616055a19 100644
--- a/tidy3d/plugins/autograd/invdes/penalties.py
+++ b/tidy3d/plugins/autograd/invdes/penalties.py
@@ -1,6 +1,6 @@
from __future__ import annotations
-from typing import Callable, Optional, Union
+from collections.abc import Callable
import autograd.numpy as np
import pydantic.v1 as pd
@@ -16,13 +16,13 @@
class ErosionDilationPenalty(Tidy3dBaseModel):
"""A class that computes a penalty for erosion/dilation of a parameter map not being unity."""
- radius: Union[float, tuple[float, ...]] = pd.Field(
+ radius: float | tuple[float, ...] = pd.Field(
..., title="Radius", description="The radius of the kernel."
)
- dl: Union[float, tuple[float, ...]] = pd.Field(
+ dl: float | tuple[float, ...] = pd.Field(
..., title="Grid Spacing", description="The grid spacing."
)
- size_px: Union[int, tuple[int, ...]] = pd.Field(
+ size_px: int | tuple[int, ...] = pd.Field(
None, title="Size in Pixels", description="The size of the kernel in pixels."
)
beta: pd.NonNegativeFloat = pd.Field(
@@ -90,10 +90,10 @@ def _close(arr: NDArray):
def make_erosion_dilation_penalty(
- radius: Union[float, tuple[float, ...]],
- dl: Union[float, tuple[float, ...]],
+ radius: float | tuple[float, ...],
+ dl: float | tuple[float, ...],
*,
- size_px: Optional[Union[int, tuple[int, ...]]] = None,
+ size_px: int | tuple[int, ...] | None = None,
beta: float = 20.0,
eta: float = 0.5,
delta_eta: float = 0.01,
@@ -169,7 +169,7 @@ def bezier_with_grads(
return b, dbdt, dbd2t
-def bezier_curvature(x: NDArray, y: NDArray, t: Union[NDArray, float] = 0.5) -> NDArray:
+def bezier_curvature(x: NDArray, y: NDArray, t: NDArray | float = 0.5) -> NDArray:
"""
Calculate the curvature of a Bezier curve at a given parameter t.
diff --git a/tidy3d/plugins/autograd/primitives/interpolate.py b/tidy3d/plugins/autograd/primitives/interpolate.py
index ac5b6c2d4c..da9318bf0f 100644
--- a/tidy3d/plugins/autograd/primitives/interpolate.py
+++ b/tidy3d/plugins/autograd/primitives/interpolate.py
@@ -1,7 +1,5 @@
from __future__ import annotations
-from typing import Optional
-
import numpy as np
from autograd.extend import defvjp, primitive
from numpy.typing import NDArray
@@ -164,7 +162,7 @@ def get_linear_derivative_wrt_y(
def compute_quadratic_coefficients(
x: NDArray,
y: NDArray,
- left_deriv: Optional[float] = None,
+ left_deriv: float | None = None,
) -> tuple[NDArray, NDArray, NDArray]:
"""Compute quadratic spline coefficients.
@@ -239,7 +237,7 @@ def evaluate_quadratic_spline(
def get_quadratic_derivative_wrt_y(
x: NDArray,
y: NDArray,
- left_deriv: Optional[float] = None,
+ left_deriv: float | None = None,
) -> tuple[NDArray, NDArray, NDArray]:
"""Compute derivative of quadratic spline coefficients wrt ``y``.
@@ -292,7 +290,7 @@ def get_quadratic_derivative_wrt_y(
def setup_cubic_tridiagonal(
x: NDArray,
y: NDArray,
- endpoint_derivs: tuple[Optional[float], Optional[float]] = (None, None),
+ endpoint_derivs: tuple[float | None, float | None] = (None, None),
) -> tuple[NDArray, NDArray, NDArray, NDArray, NDArray]:
"""Return (lower, diag, upper, rhs, h) for the cubic spline system.
@@ -397,7 +395,7 @@ def _solve_tridiagonal_multi(lower: NDArray, diag: NDArray, upper: NDArray, B: N
np.ndarray
Solution matrix
"""
- n, k = B.shape
+ n, _k = B.shape
c = upper.copy()
d = diag.copy()
a = lower.copy()
@@ -484,7 +482,7 @@ def evaluate_cubic_spline(
def compute_spline_coefficients(
x: NDArray,
y: NDArray,
- endpoint_derivs: tuple[Optional[float], Optional[float]] = (None, None),
+ endpoint_derivs: tuple[float | None, float | None] = (None, None),
) -> tuple[NDArray, NDArray, NDArray, NDArray]:
"""Compute the cubic spline coefficients ``(a, b, c, d)``.
@@ -510,7 +508,7 @@ def compute_spline_coefficients(
def get_cubic_derivative_wrt_y(
x: NDArray,
y: NDArray,
- endpoint_derivs: tuple[Optional[float], Optional[float]] = (None, None),
+ endpoint_derivs: tuple[float | None, float | None] = (None, None),
) -> tuple[NDArray, NDArray, NDArray, NDArray, NDArray]:
"""Compute derivatives of cubic spline coefficients ``(a, b, c, d)``
wrt ``y`` values.
@@ -584,7 +582,7 @@ def get_cubic_derivative_wrt_y(
def compute_spline_coeffs(
x_points: NDArray,
y_points: NDArray,
- endpoint_derivatives: tuple[Optional[float], Optional[float]] = (None, None),
+ endpoint_derivatives: tuple[float | None, float | None] = (None, None),
order: int = 3,
) -> tuple:
"""Compute spline coefficients for the given order.
@@ -649,7 +647,7 @@ def get_spline_derivatives_wrt_y(
order: int,
x_points: NDArray,
y_points: NDArray,
- endpoint_derivatives: tuple[Optional[float], Optional[float]] = (None, None),
+ endpoint_derivatives: tuple[float | None, float | None] = (None, None),
):
"""Returns a tuple of derivative arrays for the given spline order.
@@ -687,7 +685,7 @@ def _interpolate_spline(
y_points: NDArray,
num_points: int,
order: int,
- endpoint_derivatives: tuple[Optional[float], Optional[float]] = (None, None),
+ endpoint_derivatives: tuple[float | None, float | None] = (None, None),
) -> tuple[NDArray, NDArray]:
"""Primitive function to perform spline interpolation of a given order
with optional endpoint derivatives.
@@ -781,7 +779,7 @@ def interpolate_spline(
y_points: NDArray,
num_points: int,
order: int,
- endpoint_derivatives: tuple[Optional[float], Optional[float]] = (None, None),
+ endpoint_derivatives: tuple[float | None, float | None] = (None, None),
) -> tuple[NDArray, NDArray]:
"""Differentiable spline interpolation of a given order
with optional endpoint derivatives.
diff --git a/tidy3d/plugins/autograd/utilities.py b/tidy3d/plugins/autograd/utilities.py
index 7a7b5f83a8..0d7cab605b 100644
--- a/tidy3d/plugins/autograd/utilities.py
+++ b/tidy3d/plugins/autograd/utilities.py
@@ -1,8 +1,8 @@
from __future__ import annotations
-from collections.abc import Iterable
+from collections.abc import Callable, Iterable
from functools import reduce, wraps
-from typing import Any, Callable, Optional, Union
+from typing import Any
import autograd.numpy as anp
import numpy as np
@@ -86,9 +86,9 @@ def make_kernel(kernel_type: KernelType, size: Iterable[int], normalize: bool =
def get_kernel_size_px(
- radius: Optional[Union[float, Iterable[float]]] = None,
- dl: Optional[Union[float, Iterable[float]]] = None,
-) -> Union[int, list[int]]:
+ radius: float | Iterable[float] | None = None,
+ dl: float | Iterable[float] | None = None,
+) -> int | list[int]:
"""Calculate the kernel size in pixels based on the provided radius and grid spacing.
Parameters
@@ -124,7 +124,7 @@ def get_kernel_size_px(
)
-def chain(*funcs: Union[Callable, Iterable[Callable]]):
+def chain(*funcs: Callable | Iterable[Callable]):
"""Chain multiple functions together to apply them sequentially to an array.
Parameters
@@ -168,7 +168,7 @@ def chained(array: NDArray):
return chained
-def scalar_objective(func: Optional[Callable] = None, *, has_aux: bool = False) -> Callable:
+def scalar_objective(func: Callable | None = None, *, has_aux: bool = False) -> Callable:
"""Decorator to ensure the objective function returns a real scalar value.
This decorator wraps an objective function to ensure that its return value is a real scalar.
diff --git a/tidy3d/plugins/design/design.py b/tidy3d/plugins/design/design.py
index cee82953e9..8010d511af 100644
--- a/tidy3d/plugins/design/design.py
+++ b/tidy3d/plugins/design/design.py
@@ -3,7 +3,8 @@
from __future__ import annotations
import inspect
-from typing import Any, Callable, Optional, Union
+from collections.abc import Callable
+from typing import Any
import pydantic.v1 as pd
@@ -112,9 +113,9 @@ def _package_run_results(
fn_args: list[dict[str, Any]],
fn_values: list[Any],
fn_source: str,
- task_names: Optional[tuple[str]] = None,
- task_paths: Optional[list] = None,
- aux_values: Optional[list[Any]] = None,
+ task_names: tuple[str] | None = None,
+ task_paths: list | None = None,
+ aux_values: list[Any] | None = None,
opt_output: Any = None,
) -> Result:
"""How to package results from ``method.run`` and ``method.run_batch``"""
@@ -144,7 +145,7 @@ def get_fn_source(function: Callable) -> str:
except (TypeError, OSError):
return None
- def run(self, fn: Callable, fn_post: Optional[Callable] = None, verbose: bool = True) -> Result:
+ def run(self, fn: Callable, fn_post: Callable | None = None, verbose: bool = True) -> Result:
"""Explore a parameter space with a supplied method using the user supplied function.
Supplied functions are used to evaluate the design space and are called within the method.
For optimization methods these functions act as the fitness function. A single function can be
@@ -314,7 +315,7 @@ def fn_combined(self, args_list: list[dict[str, Any]]) -> list[Any]:
def _fn_mid(
self, pre_out: dict[int, Any], sim_counter: int, console: Console
- ) -> Union[dict[int, Any], BatchData]:
+ ) -> dict[int, Any] | BatchData:
"""A function of the output of ``fn_pre`` that gives the input to ``fn_post``."""
# Keep copy of original to use if no tidy3d simulation required
@@ -456,10 +457,8 @@ def _remove_or_replace(search_dict: dict, attr_name: str) -> dict:
def run_batch(
self,
- fn_pre: Callable[Any, Union[Simulation, list[Simulation], dict[str, Simulation]]],
- fn_post: Callable[
- Union[SimulationData, list[SimulationData], dict[str, SimulationData]], Any
- ],
+ fn_pre: Callable[Any, Simulation | list[Simulation] | dict[str, Simulation]],
+ fn_post: Callable[SimulationData | list[SimulationData] | dict[str, SimulationData], Any],
path_dir: str = ".",
**batch_kwargs,
) -> Result:
@@ -564,7 +563,7 @@ def _estimate_sim_cost(sim):
return None
return round(per_run_estimate * run_count, 3)
- def summarize(self, fn_pre: Optional[Callable] = None, verbose: bool = True) -> dict[str, Any]:
+ def summarize(self, fn_pre: Callable | None = None, verbose: bool = True) -> dict[str, Any]:
"""Summarize the setup of the DesignSpace
Prints a summary of the DesignSpace including the method and associated args, the parameters,
diff --git a/tidy3d/plugins/design/method.py b/tidy3d/plugins/design/method.py
index 22b0745bce..3a212a92bb 100644
--- a/tidy3d/plugins/design/method.py
+++ b/tidy3d/plugins/design/method.py
@@ -3,7 +3,8 @@
from __future__ import annotations
from abc import ABC, abstractmethod
-from typing import TYPE_CHECKING, Any, Callable, Literal, Optional, Union
+from collections.abc import Callable
+from typing import TYPE_CHECKING, Any, Literal
import numpy as np
import pydantic.v1 as pd
@@ -27,7 +28,7 @@ def _run(self, parameters: tuple[ParameterType, ...], run_fn: Callable) -> tuple
"""Defines the search algorithm."""
@abstractmethod
- def _get_run_count(self, parameters: Optional[list] = None) -> int:
+ def _get_run_count(self, parameters: list | None = None) -> int:
"""Return the maximum number of runs for the method based on current method arguments."""
def _force_int(self, next_point: dict, parameters: list) -> None:
@@ -231,7 +232,7 @@ class MethodBayOpt(MethodOptimize, ABC):
description="The Xi coefficient used by the ``ei`` and ``poi`` acquisition functions. More detail available in the `package docs `_.",
)
- def _get_run_count(self, parameters: Optional[list] = None) -> int:
+ def _get_run_count(self, parameters: list | None = None) -> int:
"""Return the maximum number of runs for the method based on current method arguments."""
return self.initial_iter + self.n_iter
@@ -382,24 +383,22 @@ class MethodGenAlg(MethodOptimize, ABC):
description="The style of parent selector. See the `PyGAD docs `_ for more details.",
)
- keep_parents: Union[pd.PositiveInt, Literal[-1, 0]] = pd.Field(
+ keep_parents: pd.PositiveInt | Literal[-1, 0] = pd.Field(
default=-1,
title="Keep Parents",
description="The number of parents to keep unaltered in the population of the next generation. Default value of -1 keeps all current parents for the next generation. This value is overwritten if ``keep_parents`` is > 0. See the `PyGAD docs `_ for more details.",
)
- keep_elitism: Union[pd.PositiveInt, Literal[0]] = pd.Field(
+ keep_elitism: pd.PositiveInt | Literal[0] = pd.Field(
default=1,
title="Keep Elitism",
description="The number of top solutions to be included in the population of the next generation. Overwrites ``keep_parents`` if value is > 0. See the `PyGAD docs `_ for more details.",
)
- crossover_type: Union[None, Literal["single_point", "two_points", "uniform", "scattered"]] = (
- pd.Field(
- default="single_point",
- title="Crossover Type",
- description="The style of crossover operation. See the `PyGAD docs `_ for more details.",
- )
+ crossover_type: None | Literal["single_point", "two_points", "uniform", "scattered"] = pd.Field(
+ default="single_point",
+ title="Crossover Type",
+ description="The style of crossover operation. See the `PyGAD docs `_ for more details.",
)
crossover_prob: pd.confloat(ge=0, le=1) = pd.Field(
@@ -408,15 +407,13 @@ class MethodGenAlg(MethodOptimize, ABC):
description="The probability of performing a crossover between two parents.",
)
- mutation_type: Union[None, Literal["random", "swap", "inversion", "scramble", "adaptive"]] = (
- pd.Field(
- default="random",
- title="Mutation Type",
- description="The style of gene mutation. See the `PyGAD docs `_ for more details.",
- )
+ mutation_type: None | Literal["random", "swap", "inversion", "scramble", "adaptive"] = pd.Field(
+ default="random",
+ title="Mutation Type",
+ description="The style of gene mutation. See the `PyGAD docs `_ for more details.",
)
- mutation_prob: Union[pd.confloat(ge=0, le=1), Literal[None]] = pd.Field(
+ mutation_prob: pd.confloat(ge=0, le=1) | Literal[None] = pd.Field(
default=0.2,
title="Mutation Probability",
description="The probability of mutating a gene.",
@@ -430,7 +427,7 @@ class MethodGenAlg(MethodOptimize, ABC):
# TODO: See if anyone is interested in having the full suite of PyGAD options - there's a lot!
- def _get_run_count(self, parameters: Optional[list] = None) -> int:
+ def _get_run_count(self, parameters: list | None = None) -> int:
"""Return the maximum number of runs for the method based on current method arguments."""
# +1 to generations as pygad creates an initial population which is effectively "Generation 0"
run_count = self.solutions_per_pop * (self.n_generations + 1)
@@ -663,7 +660,7 @@ class MethodParticleSwarm(MethodOptimize, ABC):
description="The weight or inertia of particles in the optimization.",
)
- ftol: Union[pd.confloat(ge=0, le=1), Literal[-inf]] = pd.Field(
+ ftol: pd.confloat(ge=0, le=1) | Literal[-inf] = pd.Field(
default=-inf,
title="Relative Error for Convergence",
description="Relative error in ``objective_func(best_solution)`` acceptable for convergence. See the `PySwarms docs `_ for details. Off by default.",
@@ -681,7 +678,7 @@ class MethodParticleSwarm(MethodOptimize, ABC):
description="Set the initial positions of the swarm using a numpy array of appropriate size.",
)
- def _get_run_count(self, parameters: Optional[list] = None) -> int:
+ def _get_run_count(self, parameters: list | None = None) -> int:
"""Return the maximum number of runs for the method based on current method arguments."""
return self.n_particles * self.n_iter
@@ -789,7 +786,7 @@ class AbstractMethodRandom(MethodSample, ABC):
def _get_sampler(self, parameters: tuple[ParameterType, ...]) -> qmc_type.QMCEngine:
"""Sampler for this ``Method`` class. If ``None``, sets a default."""
- def _get_run_count(self, parameters: Optional[list] = None) -> int:
+ def _get_run_count(self, parameters: list | None = None) -> int:
"""Return the maximum number of runs for the method based on current method arguments."""
return self.num_points
@@ -831,10 +828,4 @@ def _get_sampler(self, parameters: tuple[ParameterType, ...]) -> qmc_type.QMCEng
return qmc.LatinHypercube(d=d, seed=self.seed)
-MethodType = Union[
- MethodMonteCarlo,
- MethodGrid,
- MethodBayOpt,
- MethodGenAlg,
- MethodParticleSwarm,
-]
+MethodType = MethodMonteCarlo | MethodGrid | MethodBayOpt | MethodGenAlg | MethodParticleSwarm
diff --git a/tidy3d/plugins/design/parameter.py b/tidy3d/plugins/design/parameter.py
index 4d96646c2e..b3d33362a7 100644
--- a/tidy3d/plugins/design/parameter.py
+++ b/tidy3d/plugins/design/parameter.py
@@ -3,7 +3,7 @@
from __future__ import annotations
from abc import ABC, abstractmethod
-from typing import Any, Union
+from typing import Any
import numpy as np
import pydantic.v1 as pd
@@ -59,7 +59,7 @@ def sample_first(self) -> Any:
class ParameterNumeric(Parameter, ABC):
"""A variable with numeric values."""
- span: tuple[Union[float, int], Union[float, int]] = pd.Field(
+ span: tuple[float | int, float | int] = pd.Field(
...,
title="Span",
description="(min, max) range within which are allowed values for the variable. Is inclusive of max value.",
@@ -214,4 +214,4 @@ def sample_first(self) -> Any:
return self.allowed_values[0]
-ParameterType = Union[ParameterInt, ParameterFloat, ParameterAny]
+ParameterType = ParameterInt | ParameterFloat | ParameterAny
diff --git a/tidy3d/plugins/design/result.py b/tidy3d/plugins/design/result.py
index abf843183d..62c963ab57 100644
--- a/tidy3d/plugins/design/result.py
+++ b/tidy3d/plugins/design/result.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-from typing import Any, Optional
+from typing import Any
import numpy as np
import pandas
@@ -246,7 +246,7 @@ def to_dataframe(self, include_aux: bool = False) -> pandas.DataFrame:
return df
@classmethod
- def from_dataframe(cls, df: pandas.DataFrame, dims: Optional[list[str]] = None) -> Result:
+ def from_dataframe(cls, df: pandas.DataFrame, dims: list[str] | None = None) -> Result:
"""Load a result directly from a `pandas.DataFrame` object.
Parameters
diff --git a/tidy3d/plugins/dispersion/fit.py b/tidy3d/plugins/dispersion/fit.py
index b9977e08b2..c64408ac28 100644
--- a/tidy3d/plugins/dispersion/fit.py
+++ b/tidy3d/plugins/dispersion/fit.py
@@ -4,7 +4,6 @@
import codecs
import csv
-from typing import Optional
import numpy as np
import requests
@@ -44,7 +43,7 @@ class DispersionFitter(Tidy3dBaseModel):
description="Imaginary part of the complex index of refraction.",
)
- wvl_range: tuple[Optional[float], Optional[float]] = Field(
+ wvl_range: tuple[float | None, float | None] = Field(
(None, None),
title="Wavelength range [wvl_min,wvl_max] for fitting",
description="Truncate the wavelength, n and k data to the wavelength range '[wvl_min, "
@@ -752,7 +751,7 @@ def from_complex_permittivity(
wvl_um: ArrayFloat1D,
eps_real: ArrayFloat1D,
eps_imag: ArrayFloat1D = None,
- wvl_range: tuple[Optional[float], Optional[float]] = (None, None),
+ wvl_range: tuple[float | None, float | None] = (None, None),
) -> DispersionFitter:
"""Loads :class:`DispersionFitter` from wavelength and complex relative permittivity data
@@ -784,7 +783,7 @@ def from_loss_tangent(
wvl_um: ArrayFloat1D,
eps_real: ArrayFloat1D,
loss_tangent: ArrayFloat1D,
- wvl_range: tuple[Optional[float], Optional[float]] = (None, None),
+ wvl_range: tuple[float | None, float | None] = (None, None),
) -> DispersionFitter:
"""Loads :class:`DispersionFitter` from wavelength and loss tangent data.
diff --git a/tidy3d/plugins/dispersion/fit_fast.py b/tidy3d/plugins/dispersion/fit_fast.py
index 356dbde0a9..d999004dae 100644
--- a/tidy3d/plugins/dispersion/fit_fast.py
+++ b/tidy3d/plugins/dispersion/fit_fast.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Optional
-
import numpy as np
from pydantic.v1 import NonNegativeFloat, PositiveInt
@@ -46,7 +44,7 @@ def fit(
self,
min_num_poles: PositiveInt = 1,
max_num_poles: PositiveInt = DEFAULT_MAX_POLES,
- eps_inf: Optional[float] = None,
+ eps_inf: float | None = None,
tolerance_rms: NonNegativeFloat = DEFAULT_TOLERANCE_RMS,
advanced_param: AdvancedFastFitterParam = None,
) -> tuple[PoleResidue, float]:
diff --git a/tidy3d/plugins/dispersion/web.py b/tidy3d/plugins/dispersion/web.py
index 186eb0f782..c0e5516e39 100644
--- a/tidy3d/plugins/dispersion/web.py
+++ b/tidy3d/plugins/dispersion/web.py
@@ -4,7 +4,7 @@
import ssl
from enum import Enum
-from typing import Literal, Optional
+from typing import Literal
import pydantic.v1 as pydantic
import requests
@@ -83,7 +83,7 @@ class AdvancedFitterParam(Tidy3dBaseModel):
title="Number of inner iterations",
description="Number of iterations in each inner optimization.",
)
- random_seed: Optional[int] = Field(
+ random_seed: int | None = Field(
0,
title="Random seed for starting coefficients",
description="The fitting tool performs global optimizations with random "
diff --git a/tidy3d/plugins/expressions/__init__.py b/tidy3d/plugins/expressions/__init__.py
index 616aac08e8..2da541efee 100644
--- a/tidy3d/plugins/expressions/__init__.py
+++ b/tidy3d/plugins/expressions/__init__.py
@@ -1,5 +1,14 @@
from __future__ import annotations
+import importlib
+import inspect
+from typing import Annotated
+
+from pydantic.v1 import Field
+
+from tidy3d.components.types import TYPE_TAG_STR
+
+from . import types as _types
from .base import Expression
from .functions import Cos, Exp, Log, Log10, Sin, Sqrt, Tan
from .metrics import ModeAmp, ModePower, generate_validation_data
@@ -22,18 +31,14 @@
]
# The following code dynamically collects all classes that are subclasses of Expression
-# from the specified modules and updates their forward references. This is necessary to handle
-# cases where classes reference each other before they are fully defined. The local_vars dictionary
-# is used to store these classes and any other necessary types for the forward reference updates.
-
-import importlib
-import inspect
-
-from .types import ExpressionType
+# from the specified modules, builds a discriminated union for serialization, and updates their
+# forward references. This is necessary to handle cases where classes reference each other before
+# they are fully defined. The local_vars dictionary is used to store these classes and any other
+# necessary types for the forward reference updates.
_module_names = ["base", "variables", "functions", "metrics", "operators"]
_model_classes = set()
-_local_vars = {"ExpressionType": ExpressionType}
+_local_vars: dict[str, type[Expression]] = {}
for module_name in _module_names:
module = importlib.import_module(f".{module_name}", package=__name__)
@@ -42,5 +47,24 @@
_model_classes.add(obj)
_local_vars[name] = obj
+_concrete_classes = [
+ cls
+ for cls in sorted(_model_classes, key=lambda candidate: candidate.__name__)
+ if not inspect.isabstract(cls)
+]
+
+_expression_union = Expression
+if _concrete_classes:
+ _expression_union = _concrete_classes[0]
+ for cls in _concrete_classes[1:]:
+ _expression_union = _expression_union | cls
+
+ExpressionType = Annotated[_expression_union, Field(discriminator=TYPE_TAG_STR)]
+_types.ExpressionType = ExpressionType
+_types.NumberOrExpression = _types.NumberType | ExpressionType
+
+__all__.append("ExpressionType")
+_local_vars["ExpressionType"] = ExpressionType
+
for cls in _model_classes:
cls.update_forward_refs(**_local_vars)
diff --git a/tidy3d/plugins/expressions/base.py b/tidy3d/plugins/expressions/base.py
index ff52b648ef..6d827f716b 100644
--- a/tidy3d/plugins/expressions/base.py
+++ b/tidy3d/plugins/expressions/base.py
@@ -2,7 +2,7 @@
from abc import ABC, abstractmethod
from collections.abc import Generator
-from typing import TYPE_CHECKING, Any, Optional
+from typing import TYPE_CHECKING, Any
from tidy3d.components.base import Tidy3dBaseModel
from tidy3d.components.types import TYPE_TAG_STR
@@ -63,7 +63,7 @@ def parse_obj(cls, obj: dict[str, Any]) -> ExpressionType:
return subclass(**obj)
def filter(
- self, target_type: type[Expression], target_field: Optional[str] = None
+ self, target_type: type[Expression], target_field: str | None = None
) -> Generator[Expression, None, None]:
"""
Find all instances of a given type or field in the expression.
diff --git a/tidy3d/plugins/expressions/metrics.py b/tidy3d/plugins/expressions/metrics.py
index 277085bfe7..c0b7f39751 100644
--- a/tidy3d/plugins/expressions/metrics.py
+++ b/tidy3d/plugins/expressions/metrics.py
@@ -1,7 +1,7 @@
from __future__ import annotations
from abc import ABC, abstractmethod
-from typing import Any, Optional, Union
+from typing import Any
import autograd.numpy as np
import pydantic.v1 as pd
@@ -69,7 +69,7 @@ class ModeAmp(Metric):
title="Monitor Name",
description="The name of the mode monitor. This needs to match the name of the monitor in the simulation.",
)
- f: Optional[Union[float, FreqArray]] = pd.Field( # type: ignore
+ f: float | FreqArray | None = pd.Field( # type: ignore
None,
title="Frequency Array",
description="The frequency array. If None, all frequencies in the monitor will be used.",
diff --git a/tidy3d/plugins/expressions/types.py b/tidy3d/plugins/expressions/types.py
index 1f6a12e9fe..bc355f8b55 100644
--- a/tidy3d/plugins/expressions/types.py
+++ b/tidy3d/plugins/expressions/types.py
@@ -1,73 +1,11 @@
from __future__ import annotations
-from typing import TYPE_CHECKING, Annotated, Union
+from typing import ForwardRef, Union
-from pydantic.v1 import Field
+from tidy3d.components.types import ArrayLike, Complex
-from tidy3d.components.types import TYPE_TAG_STR, ArrayLike, Complex
+NumberType = int | float | Complex | ArrayLike
-if TYPE_CHECKING:
- from .functions import Cos, Exp, Log, Log10, Sin, Sqrt, Tan
- from .metrics import ModeAmp, ModePower
- from .operators import (
- Abs,
- Add,
- Divide,
- FloorDivide,
- MatMul,
- Modulus,
- Multiply,
- Negate,
- Power,
- Subtract,
- )
- from .variables import Constant, Variable
+ExpressionType = ForwardRef("ExpressionType")
-NumberType = Union[int, float, Complex, ArrayLike]
-
-OperatorType = Annotated[
- Union[
- "Add",
- "Subtract",
- "Multiply",
- "Divide",
- "Power",
- "Modulus",
- "FloorDivide",
- "MatMul",
- "Negate",
- "Abs",
- ],
- Field(discriminator=TYPE_TAG_STR),
-]
-
-FunctionType = Annotated[
- Union[
- "Sin",
- "Cos",
- "Tan",
- "Exp",
- "Log",
- "Log10",
- "Sqrt",
- ],
- Field(discriminator=TYPE_TAG_STR),
-]
-
-MetricType = Annotated[
- Union[
- "Constant",
- "Variable",
- "ModeAmp",
- "ModePower",
- ],
- Field(discriminator=TYPE_TAG_STR),
-]
-
-ExpressionType = Union[
- OperatorType,
- FunctionType,
- MetricType,
-]
-
-NumberOrExpression = Union[NumberType, ExpressionType]
+NumberOrExpression = Union[NumberType, ExpressionType] # noqa: UP007
diff --git a/tidy3d/plugins/expressions/variables.py b/tidy3d/plugins/expressions/variables.py
index 13cd5534b8..0801e05e3b 100644
--- a/tidy3d/plugins/expressions/variables.py
+++ b/tidy3d/plugins/expressions/variables.py
@@ -1,6 +1,6 @@
from __future__ import annotations
-from typing import Any, Optional
+from typing import Any
import pydantic.v1 as pd
@@ -39,7 +39,7 @@ class Variable(Expression):
10
"""
- name: Optional[str] = pd.Field(
+ name: str | None = pd.Field(
None,
title="Name",
description="The name of the variable used for lookup during evaluation.",
diff --git a/tidy3d/plugins/invdes/design.py b/tidy3d/plugins/invdes/design.py
index 737698b9b6..c2a2ba3e6f 100644
--- a/tidy3d/plugins/invdes/design.py
+++ b/tidy3d/plugins/invdes/design.py
@@ -3,7 +3,7 @@
from __future__ import annotations
import abc
-import typing
+from collections.abc import Callable
import autograd.numpy as anp
import numpy as np
@@ -19,7 +19,7 @@
from .region import DesignRegionType
from .validators import check_pixel_size
-PostProcessFnType = typing.Callable[[td.SimulationData], float]
+PostProcessFnType = Callable[[td.SimulationData], float]
class AbstractInverseDesign(InvdesBaseModel, abc.ABC):
@@ -43,15 +43,15 @@ class AbstractInverseDesign(InvdesBaseModel, abc.ABC):
description="If ``True``, will print the regular output from ``web`` functions.",
)
- metric: typing.Optional[ExpressionType] = pd.Field(
+ metric: ExpressionType | None = pd.Field(
None,
title="Objective Metric",
description="Serializable expression defining the objective function.",
)
def make_objective_fn(
- self, post_process_fn: typing.Optional[typing.Callable] = None, maximize: bool = True
- ) -> typing.Callable[[anp.ndarray], tuple[float, dict]]:
+ self, post_process_fn: Callable | None = None, maximize: bool = True
+ ) -> Callable[[anp.ndarray], tuple[float, dict]]:
"""Construct the objective function for this InverseDesign object."""
if (post_process_fn is None) and (self.metric is None):
@@ -62,7 +62,7 @@ def make_objective_fn(
direction_multiplier = 1 if maximize else -1
- def objective_fn(params: anp.ndarray, aux_data: typing.Optional[dict] = None) -> float:
+ def objective_fn(params: anp.ndarray, aux_data: dict | None = None) -> float:
"""Full objective function."""
data = self.to_simulation_data(params=params)
@@ -265,7 +265,7 @@ class InverseDesignMulti(AbstractInverseDesign):
description="Set of simulation without the design regions or monitors used in the objective fn.",
)
- output_monitor_names: tuple[typing.Union[tuple[str, ...], None], ...] = pd.Field(
+ output_monitor_names: tuple[tuple[str, ...] | None, ...] = pd.Field(
None,
title="Output Monitor Names",
description="Optional names of monitors whose data the differentiable output depends on."
@@ -328,4 +328,4 @@ def to_simulation_data(self, params: anp.ndarray, **kwargs) -> web.BatchData: #
return self.run_async(simulations, **kwargs)
-InverseDesignType = typing.Union[InverseDesign, InverseDesignMulti]
+InverseDesignType = InverseDesign | InverseDesignMulti
diff --git a/tidy3d/plugins/invdes/initialization.py b/tidy3d/plugins/invdes/initialization.py
index 3acd297d25..e61768e30e 100644
--- a/tidy3d/plugins/invdes/initialization.py
+++ b/tidy3d/plugins/invdes/initialization.py
@@ -3,7 +3,6 @@
from __future__ import annotations
from abc import ABC, abstractmethod
-from typing import Optional, Union
import numpy as np
import pydantic.v1 as pd
@@ -43,7 +42,7 @@ class RandomInitializationSpec(AbstractInitializationSpec):
title="Maximum Value",
description="Maximum value for the random parameters (exclusive).",
)
- seed: Optional[pd.NonNegativeInt] = pd.Field(
+ seed: pd.NonNegativeInt | None = pd.Field(
None, description="Seed for the random number generator."
)
@@ -126,8 +125,6 @@ def create_parameters(self, shape: tuple[int, ...]) -> NDArray:
return params
-InitializationSpecType = Union[
- RandomInitializationSpec,
- UniformInitializationSpec,
- CustomInitializationSpec,
-]
+InitializationSpecType = (
+ RandomInitializationSpec | UniformInitializationSpec | CustomInitializationSpec
+)
diff --git a/tidy3d/plugins/invdes/optimizer.py b/tidy3d/plugins/invdes/optimizer.py
index a43ba47e23..403404a307 100644
--- a/tidy3d/plugins/invdes/optimizer.py
+++ b/tidy3d/plugins/invdes/optimizer.py
@@ -84,9 +84,7 @@ def display_fn(self, result: InverseDesignResult, step_index: int) -> None:
print(f"\tpost_process_val = {result.post_process_val[-1]:.3e}")
print(f"\tpenalty = {result.penalty[-1]:.3e}")
- def initialize_result(
- self, params0: typing.Optional[anp.ndarray] = None
- ) -> InverseDesignResult:
+ def initialize_result(self, params0: anp.ndarray | None = None) -> InverseDesignResult:
"""
Create an initially empty `InverseDesignResult` from the starting parameters.
@@ -111,8 +109,8 @@ def initialize_result(
def run(
self,
- post_process_fn: typing.Optional[typing.Callable] = None,
- callback: typing.Optional[typing.Callable] = None,
+ post_process_fn: typing.Callable | None = None,
+ callback: typing.Callable | None = None,
params0: anp.ndarray = None,
) -> InverseDesignResult:
"""Run this inverse design problem from an optional initial set of parameters.
@@ -140,9 +138,9 @@ def run(
def continue_run(
self,
result: InverseDesignResult,
- num_steps: typing.Optional[int] = None,
- post_process_fn: typing.Optional[typing.Callable] = None,
- callback: typing.Optional[typing.Callable] = None,
+ num_steps: int | None = None,
+ post_process_fn: typing.Callable | None = None,
+ callback: typing.Callable | None = None,
) -> InverseDesignResult:
"""Run optimizer for a series of steps with an initialized state.
@@ -178,7 +176,7 @@ def continue_run(
# main optimization loop
for step_index in range(done_steps, done_steps + num_steps):
aux_data = {}
- val, grad = val_and_grad_fn(params, aux_data=aux_data)
+ _val, grad = val_and_grad_fn(params, aux_data=aux_data)
if anp.allclose(grad, 0.0):
td.log.warning(
@@ -230,9 +228,9 @@ def continue_run(
def continue_run_from_file(
self,
fname: str,
- num_steps: typing.Optional[int] = None,
- post_process_fn: typing.Optional[typing.Callable] = None,
- callback: typing.Optional[typing.Callable] = None,
+ num_steps: int | None = None,
+ post_process_fn: typing.Callable | None = None,
+ callback: typing.Callable | None = None,
) -> InverseDesignResult:
"""Continue the optimization run from a ``.pkl`` file with an ``InverseDesignResult``."""
result = InverseDesignResult.from_file(fname)
@@ -245,9 +243,9 @@ def continue_run_from_file(
def continue_run_from_history(
self,
- num_steps: typing.Optional[int] = None,
- post_process_fn: typing.Optional[typing.Callable] = None,
- callback: typing.Optional[typing.Callable] = None,
+ num_steps: int | None = None,
+ post_process_fn: typing.Callable | None = None,
+ callback: typing.Callable | None = None,
) -> InverseDesignResult:
"""Continue the optimization run from a ``.pkl`` file with an ``InverseDesignResult``."""
return self.continue_run_from_file(
@@ -289,7 +287,7 @@ def initial_state(self, parameters: np.ndarray) -> dict:
return {"m": zeros, "v": zeros, "t": 0}
def update(
- self, parameters: np.ndarray, gradient: np.ndarray, state: typing.Optional[dict] = None
+ self, parameters: np.ndarray, gradient: np.ndarray, state: dict | None = None
) -> tuple[np.ndarray, dict]:
if state is None:
state = self.initial_state(parameters)
diff --git a/tidy3d/plugins/invdes/penalty.py b/tidy3d/plugins/invdes/penalty.py
index 1683f577fd..f48eb3daef 100644
--- a/tidy3d/plugins/invdes/penalty.py
+++ b/tidy3d/plugins/invdes/penalty.py
@@ -2,7 +2,6 @@
from __future__ import annotations
import abc
-import typing
import autograd.numpy as anp
import pydantic.v1 as pd
@@ -98,4 +97,4 @@ def evaluate(self, x: anp.ndarray, pixel_size: float) -> float:
return self.weight * penalty_unweighted
-PenaltyType = typing.Union[ErosionDilationPenalty]
+PenaltyType = ErosionDilationPenalty
diff --git a/tidy3d/plugins/invdes/region.py b/tidy3d/plugins/invdes/region.py
index 17fa5009c4..9f97f07cf7 100644
--- a/tidy3d/plugins/invdes/region.py
+++ b/tidy3d/plugins/invdes/region.py
@@ -2,8 +2,8 @@
from __future__ import annotations
import abc
-import typing
import warnings
+from typing import Literal
import autograd.numpy as anp
import numpy as np
@@ -167,7 +167,7 @@ class TopologyDesignRegion(DesignRegion):
"inside of the penalties directly through the ``.weight`` field.",
)
- override_structure_dl: typing.Union[pd.PositiveFloat, typing.Literal[False]] = pd.Field(
+ override_structure_dl: pd.PositiveFloat | Literal[False] = pd.Field(
None,
title="Design Region Override Structure",
description="Defines grid size when adding an ``override_structure`` to the "
@@ -366,4 +366,4 @@ def evaluate_penalty(self, penalty: PenaltyType, material_density: anp.ndarray)
return penalty.evaluate(x=material_density, pixel_size=self.pixel_size)
-DesignRegionType = typing.Union[TopologyDesignRegion]
+DesignRegionType = TopologyDesignRegion
diff --git a/tidy3d/plugins/invdes/result.py b/tidy3d/plugins/invdes/result.py
index 3a000ab80c..3f135a5b45 100644
--- a/tidy3d/plugins/invdes/result.py
+++ b/tidy3d/plugins/invdes/result.py
@@ -137,20 +137,20 @@ def get_last(self, key: str) -> typing.Any:
"""Get the last value from the history."""
return self.get(key=key, index=-1)
- def get_sim(self, index: int = -1) -> typing.Union[td.Simulation, list[td.Simulation]]:
+ def get_sim(self, index: int = -1) -> td.Simulation | list[td.Simulation]:
"""Get the simulation at a specific index in the history (list of sims if multi)."""
params = np.array(self.get(key="params", index=index))
return self.design.to_simulation(params=params)
def get_sim_data(
self, index: int = -1, **kwargs
- ) -> typing.Union[td.SimulationData, list[td.SimulationData]]:
+ ) -> td.SimulationData | list[td.SimulationData]:
"""Get the simulation data at a specific index in the history (list of simdata if multi)."""
params = np.array(self.get(key="params", index=index))
return self.design.to_simulation_data(params=params, **kwargs)
@property
- def sim_last(self) -> typing.Union[td.Simulation, list[td.Simulation]]:
+ def sim_last(self) -> td.Simulation | list[td.Simulation]:
"""The last simulation."""
return self.get_sim(index=-1)
diff --git a/tidy3d/plugins/invdes/transformation.py b/tidy3d/plugins/invdes/transformation.py
index c5060209cf..472778bff9 100644
--- a/tidy3d/plugins/invdes/transformation.py
+++ b/tidy3d/plugins/invdes/transformation.py
@@ -2,7 +2,6 @@
from __future__ import annotations
import abc
-import typing
import autograd.numpy as anp
import pydantic.v1 as pd
@@ -82,4 +81,4 @@ def evaluate(self, spatial_data: anp.ndarray, design_region_dl: float) -> anp.nd
return data_projected
-TransformationType = typing.Union[FilterProject]
+TransformationType = FilterProject
diff --git a/tidy3d/plugins/invdes/validators.py b/tidy3d/plugins/invdes/validators.py
index 07a522b42e..947b15889e 100644
--- a/tidy3d/plugins/invdes/validators.py
+++ b/tidy3d/plugins/invdes/validators.py
@@ -33,7 +33,7 @@ def check_pixel_size(sim_field_name: str):
"""make validator to check the pixel size of sim or list of sims in an ``InverseDesign``."""
def check_pixel_size_sim(
- sim: td.Simulation, pixel_size: float, index: typing.Optional[int] = None
+ sim: td.Simulation, pixel_size: float, index: int | None = None
) -> None:
"""Check a pixel size compared to the simulation min wvl in material."""
if not sim.sources:
diff --git a/tidy3d/plugins/klayout/drc/drc.py b/tidy3d/plugins/klayout/drc/drc.py
index c219cff4ca..7c680b18f0 100644
--- a/tidy3d/plugins/klayout/drc/drc.py
+++ b/tidy3d/plugins/klayout/drc/drc.py
@@ -5,7 +5,6 @@
import re
from pathlib import Path
from subprocess import run
-from typing import Union
import pydantic.v1 as pd
from pydantic.v1 import validator
@@ -118,7 +117,7 @@ class DRCRunner(Tidy3dBaseModel):
def run(
self,
- source: Union[Geometry, Structure, Simulation, Path],
+ source: Geometry | Structure | Simulation | Path,
td_object_gds_savefile: Path = DEFAULT_GDSFILE,
resultsfile: Path = DEFAULT_RESULTSFILE,
**to_gds_file_kwargs,
diff --git a/tidy3d/plugins/klayout/drc/results.py b/tidy3d/plugins/klayout/drc/results.py
index f13713f986..cef3247619 100644
--- a/tidy3d/plugins/klayout/drc/results.py
+++ b/tidy3d/plugins/klayout/drc/results.py
@@ -5,7 +5,6 @@
import re
import xml.etree.ElementTree as ET
from pathlib import Path
-from typing import Union
import pydantic.v1 as pd
@@ -154,7 +153,7 @@ def parse_polygons(value: str) -> MultiPolygonMarker:
return MultiPolygonMarker(polygons=tuple(polygons))
-def parse_violation_value(value: str) -> Union[EdgeMarker, EdgePairMarker, MultiPolygonMarker]:
+def parse_violation_value(value: str) -> EdgeMarker | EdgePairMarker | MultiPolygonMarker:
"""
Parse a violation value based on its type (edge, edge-pair, or polygon).
@@ -211,7 +210,7 @@ class MultiPolygonMarker(Tidy3dBaseModel):
)
-DRCMarker = Union[EdgeMarker, EdgePairMarker, MultiPolygonMarker]
+DRCMarker = EdgeMarker | EdgePairMarker | MultiPolygonMarker
class DRCViolation(Tidy3dBaseModel):
@@ -290,7 +289,7 @@ def __str__(self) -> str:
return summary
@classmethod
- def load(cls, resultsfile: Union[str, Path]) -> DRCResults:
+ def load(cls, resultsfile: str | Path) -> DRCResults:
"""Create a :class:`.DRCResults` instance from a results file.
Parameters
@@ -319,7 +318,7 @@ def load(cls, resultsfile: Union[str, Path]) -> DRCResults:
return cls(violations_by_category=violations_from_file(resultsfile=resultsfile))
-def violations_from_file(resultsfile: Union[str, Path]) -> dict[str, DRCViolation]:
+def violations_from_file(resultsfile: str | Path) -> dict[str, DRCViolation]:
"""Loads a KLayout DRC results file and returns the results as a dictionary of :class:`.DRCViolation` objects.
Parameters
diff --git a/tidy3d/plugins/klayout/util.py b/tidy3d/plugins/klayout/util.py
index 5139d64fc0..83b532118b 100644
--- a/tidy3d/plugins/klayout/util.py
+++ b/tidy3d/plugins/klayout/util.py
@@ -1,12 +1,11 @@
from __future__ import annotations
from shutil import which
-from typing import Union
import tidy3d as td
-def check_installation(raise_error: bool = False) -> Union[str, None]:
+def check_installation(raise_error: bool = False) -> str | None:
"""Checks if KLayout is installed and added to the system PATH.
If it is, this returns the path to the executable. Otherwise, returns ``None``.
Equivalent to $which("klayout") in the terminal.
diff --git a/tidy3d/plugins/microwave/array_factor.py b/tidy3d/plugins/microwave/array_factor.py
index b312649bb8..213b2c679f 100644
--- a/tidy3d/plugins/microwave/array_factor.py
+++ b/tidy3d/plugins/microwave/array_factor.py
@@ -3,7 +3,6 @@
from __future__ import annotations
from abc import ABC, abstractmethod
-from typing import Optional, Union
import numpy as np
import pydantic.v1 as pd
@@ -33,7 +32,7 @@
class AbstractAntennaArrayCalculator(MicrowaveBaseModel, ABC):
"""Abstract base for phased array calculators."""
- taper: Union[RectangularTaper, RadialTaper] = pd.Field(
+ taper: RectangularTaper | RadialTaper = pd.Field(
None,
discriminator=TYPE_TAG_STR,
title="Antenna Array Taper",
@@ -173,7 +172,7 @@ def _try_to_expand_geometry(
def _duplicate_or_expand_list_of_objects(
self,
objects: tuple[
- Union[Structure, MeshOverrideStructure, LayerRefinementSpec, LumpedElement], ...
+ Structure | MeshOverrideStructure | LayerRefinementSpec | LumpedElement, ...
],
old_sim_bounds: Bound,
new_sim_bounds: Bound,
@@ -467,9 +466,9 @@ def make_antenna_array(self, simulation: Simulation) -> Simulation:
@abstractmethod
def array_factor(
self,
- theta: Union[float, ArrayLike],
- phi: Union[float, ArrayLike],
- frequency: Union[NonNegativeFloat, ArrayLike],
+ theta: float | ArrayLike,
+ phi: float | ArrayLike,
+ frequency: NonNegativeFloat | ArrayLike,
) -> ArrayLike:
"""
Compute the array factor for an antenna array.
@@ -747,12 +746,10 @@ class RectangularAntennaArrayCalculator(AbstractAntennaArrayCalculator):
description="Phase-shifts between antennas along x, y, and z directions.",
)
- amp_multipliers: tuple[Optional[ArrayLike], Optional[ArrayLike], Optional[ArrayLike]] = (
- pd.Field(
- (None, None, None),
- title="Amplitude Multipliers",
- description="Amplitude multipliers spatially distributed along x, y, and z directions.",
- )
+ amp_multipliers: tuple[ArrayLike | None, ArrayLike | None, ArrayLike | None] = pd.Field(
+ (None, None, None),
+ title="Amplitude Multipliers",
+ description="Amplitude multipliers spatially distributed along x, y, and z directions.",
)
@pd.validator("amp_multipliers", pre=True, always=True)
@@ -836,9 +833,9 @@ def _extend_dims(self) -> tuple[Axis, ...]:
def array_factor(
self,
- theta: Union[float, ArrayLike],
- phi: Union[float, ArrayLike],
- frequency: Union[NonNegativeFloat, ArrayLike],
+ theta: float | ArrayLike,
+ phi: float | ArrayLike,
+ frequency: NonNegativeFloat | ArrayLike,
medium: MediumType3D = Undefined,
) -> ArrayLike:
"""
@@ -1151,15 +1148,15 @@ def _get_weights_continuous(self, p_vec: ArrayLike) -> ArrayLike:
# define a list of acceptable rectangular windows
-RectangularWindowType = Union[
- HammingWindow,
- HannWindow,
- KaiserWindow,
- TaylorWindow,
- ChebWindow,
- BlackmanWindow,
- BlackmanHarrisWindow,
-]
+RectangularWindowType = (
+ HammingWindow
+ | HannWindow
+ | KaiserWindow
+ | TaylorWindow
+ | ChebWindow
+ | BlackmanWindow
+ | BlackmanHarrisWindow
+)
class AbstractTaper(MicrowaveBaseModel, ABC):
@@ -1182,21 +1179,21 @@ def amp_multipliers(
class RectangularTaper(AbstractTaper):
"""Class for rectangular taper."""
- window_x: Optional[RectangularWindowType] = pd.Field(
+ window_x: RectangularWindowType | None = pd.Field(
None,
title="X Axis Window",
description="Window type used to taper array antenna along x axis.",
discriminator=TYPE_TAG_STR,
)
- window_y: Optional[RectangularWindowType] = pd.Field(
+ window_y: RectangularWindowType | None = pd.Field(
None,
title="Y Axis Window",
description="Window type used to taper array antenna along y axis.",
discriminator=TYPE_TAG_STR,
)
- window_z: Optional[RectangularWindowType] = pd.Field(
+ window_z: RectangularWindowType | None = pd.Field(
None,
title="Z Axis Window",
description="Window type used to taper array antenna along z axis.",
diff --git a/tidy3d/plugins/microwave/lobe_measurer.py b/tidy3d/plugins/microwave/lobe_measurer.py
index 18985ac7c9..24ba221e49 100644
--- a/tidy3d/plugins/microwave/lobe_measurer.py
+++ b/tidy3d/plugins/microwave/lobe_measurer.py
@@ -3,7 +3,6 @@
from __future__ import annotations
from math import isclose, isnan
-from typing import Optional
import numpy as np
import pydantic.v1 as pd
@@ -297,7 +296,7 @@ def side_lobes(self) -> DataFrame:
return side_lobes
@property
- def sidelobe_level(self) -> Optional[float]:
+ def sidelobe_level(self) -> float | None:
"""The sidelobe level returned on a linear scale."""
if self.side_lobes.empty:
return None
diff --git a/tidy3d/plugins/resonance/resonance.py b/tidy3d/plugins/resonance/resonance.py
index 0f49c0f0c9..b0141d650d 100644
--- a/tidy3d/plugins/resonance/resonance.py
+++ b/tidy3d/plugins/resonance/resonance.py
@@ -3,7 +3,6 @@
from __future__ import annotations
from functools import partial
-from typing import Union
import numpy as np
import xarray as xr
@@ -111,7 +110,7 @@ def _check_freq_window(cls, val):
)
return val
- def run(self, signals: Union[FieldTimeData, tuple[FieldTimeData, ...]]) -> xr.Dataset:
+ def run(self, signals: FieldTimeData | tuple[FieldTimeData, ...]) -> xr.Dataset:
"""Finds resonances in a :class:`.FieldTimeData` or a Tuple of such.
The time coordinates must be uniformly spaced, and the spacing must be the same
across all supplied data. The resonance finder runs on the sum of the
@@ -262,7 +261,7 @@ def _aggregate_field_time_comps(
)
def _aggregate_field_time(
- self, signals: Union[FieldTimeData, tuple[FieldTimeData, ...]]
+ self, signals: FieldTimeData | tuple[FieldTimeData, ...]
) -> ScalarFieldTimeDataArray:
"""Aggregates several :class:`.FieldTimeData` into a single
:class:`.ScalarFieldTimeDataArray`."""
diff --git a/tidy3d/plugins/smatrix/analysis/antenna.py b/tidy3d/plugins/smatrix/analysis/antenna.py
index bc1701bd4d..605be534a2 100644
--- a/tidy3d/plugins/smatrix/analysis/antenna.py
+++ b/tidy3d/plugins/smatrix/analysis/antenna.py
@@ -1,7 +1,5 @@
from __future__ import annotations
-from typing import Optional
-
import numpy as np
from tidy3d.components.microwave.data.monitor_data import AntennaMetricsData
@@ -13,8 +11,8 @@
def get_antenna_metrics_data(
terminal_component_modeler_data: TerminalComponentModelerData,
- port_amplitudes: Optional[dict[NetworkIndex, complex]] = None,
- monitor_name: Optional[str] = None,
+ port_amplitudes: dict[NetworkIndex, complex] | None = None,
+ monitor_name: str | None = None,
) -> AntennaMetricsData:
"""Calculate antenna parameters using superposition of fields from multiple port excitations.
diff --git a/tidy3d/plugins/smatrix/analysis/terminal.py b/tidy3d/plugins/smatrix/analysis/terminal.py
index 54dbab3086..76c4a3d0e6 100644
--- a/tidy3d/plugins/smatrix/analysis/terminal.py
+++ b/tidy3d/plugins/smatrix/analysis/terminal.py
@@ -196,7 +196,7 @@ def _compute_port_voltages_currents(
I_matrix = V_matrix.copy(deep=True)
for network_index in network_indices:
- port, mode_index = modeler.network_dict[network_index]
+ port, _mode_index = modeler.network_dict[network_index]
V_out, I_out = compute_port_VI(port, sim_data)
indexer = {"port": network_index}
V_matrix = V_matrix._with_updated_data(data=V_out.data, coords=indexer)
diff --git a/tidy3d/plugins/smatrix/component_modelers/base.py b/tidy3d/plugins/smatrix/component_modelers/base.py
index 3bc63233b3..666ef81a38 100644
--- a/tidy3d/plugins/smatrix/component_modelers/base.py
+++ b/tidy3d/plugins/smatrix/component_modelers/base.py
@@ -3,7 +3,7 @@
from __future__ import annotations
from abc import ABC, abstractmethod
-from typing import TYPE_CHECKING, Literal, Optional, Union
+from typing import TYPE_CHECKING, Literal
import pydantic.v1 as pd
@@ -34,8 +34,8 @@
FWIDTH_FRAC = 1.0 / 10
DEFAULT_DATA_DIR = "."
-IndexType = Union[MatrixIndex, NetworkIndex]
-ElementType = Union[Element, NetworkElement]
+IndexType = MatrixIndex | NetworkIndex
+ElementType = Element | NetworkElement
TaskNameFormat = Literal["RF", "PF"]
@@ -53,7 +53,7 @@ class AbstractComponentModeler(ABC, Tidy3dBaseModel):
description="Simulation describing the device without any sources present.",
)
- ports: tuple[Union[Port, TerminalPortType], ...] = pd.Field(
+ ports: tuple[Port | TerminalPortType, ...] = pd.Field(
(),
title="Ports",
description="Collection of ports describing the scattering matrix elements. "
@@ -78,7 +78,7 @@ class AbstractComponentModeler(ABC, Tidy3dBaseModel):
"pulse spectrum which can have a nonzero DC component.",
)
- run_only: Optional[tuple[IndexType, ...]] = pd.Field(
+ run_only: tuple[IndexType, ...] | None = pd.Field(
None,
title="Run Only",
description="Set of matrix indices that define the simulations to run. "
@@ -95,7 +95,7 @@ class AbstractComponentModeler(ABC, Tidy3dBaseModel):
"matrix element. If all elements of a given column of the scattering matrix are defined "
"by ``element_mappings``, the simulation corresponding to this column is skipped automatically.",
)
- custom_source_time: Optional[SourceTimeType] = pd.Field(
+ custom_source_time: SourceTimeType | None = pd.Field(
None,
title="Custom Source Time",
description="If provided, this will be used as specification of the source time-dependence in simulations. "
@@ -183,7 +183,7 @@ def _freqs_in_custom_source_time(cls, val, values):
@staticmethod
def get_task_name(
- port: Port, mode_index: Optional[int] = None, format: Optional[TaskNameFormat] = "RF"
+ port: Port, mode_index: int | None = None, format: TaskNameFormat | None = "RF"
) -> str:
"""Generates a standardized task name from a port object.
@@ -195,7 +195,7 @@ def get_task_name(
----------
port : Port
The port object from which to derive the base name.
- mode_index : Optional[int], optional
+ mode_index : int, optional
If provided, this index is appended to the port name (e.g., 'port_1@1'),
overriding the `format` argument. Defaults to None.
format : TaskNameFormat, optional
@@ -293,7 +293,7 @@ def matrix_indices_run_sim(self) -> tuple[IndexType, ...]:
return source_indices_needed
- def _shift_value_signed(self, port: Union[Port, WavePort]) -> float:
+ def _shift_value_signed(self, port: Port | WavePort) -> float:
"""How far (signed) to shift the source from the monitor."""
return _shift_value_signed(
@@ -312,13 +312,13 @@ def run(
path_dir: str = DEFAULT_DATA_DIR,
*,
folder_name: str = "default",
- callback_url: Optional[str] = None,
+ callback_url: str | None = None,
verbose: bool = True,
- solver_version: Optional[str] = None,
- pay_type: Union[PayType, str] = "AUTO",
- priority: Optional[int] = None,
+ solver_version: str | None = None,
+ pay_type: PayType | str = "AUTO",
+ priority: int | None = None,
local_gradient: bool = False,
- max_num_adjoint_per_fwd: Optional[int] = None,
+ max_num_adjoint_per_fwd: int | None = None,
):
log.warning(
"'ComponentModeler.run()' is deprecated and will be removed in a future release. "
diff --git a/tidy3d/plugins/smatrix/component_modelers/modal.py b/tidy3d/plugins/smatrix/component_modelers/modal.py
index 699d7e09f2..b5c603d3c0 100644
--- a/tidy3d/plugins/smatrix/component_modelers/modal.py
+++ b/tidy3d/plugins/smatrix/component_modelers/modal.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Optional
-
import autograd.numpy as np
import pydantic.v1 as pd
@@ -41,7 +39,7 @@ class ModalComponentModeler(AbstractComponentModeler):
"For each input mode, one simulation will be run with a modal source.",
)
- run_only: Optional[tuple[MatrixIndex, ...]] = pd.Field(
+ run_only: tuple[MatrixIndex, ...] | None = pd.Field(
None,
title="Run Only",
description="Set of matrix indices that define the simulations to run. "
@@ -245,9 +243,9 @@ def shift_port(self, port: Port) -> Port:
@add_ax_if_none
def plot_sim(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
) -> Ax:
"""Plots the simulation with all sources added for troubleshooting.
@@ -283,9 +281,9 @@ def plot_sim(
@add_ax_if_none
def plot_sim_eps(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
**kwargs,
) -> Ax:
diff --git a/tidy3d/plugins/smatrix/component_modelers/terminal.py b/tidy3d/plugins/smatrix/component_modelers/terminal.py
index 20d1420270..1c1a56cd19 100644
--- a/tidy3d/plugins/smatrix/component_modelers/terminal.py
+++ b/tidy3d/plugins/smatrix/component_modelers/terminal.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Optional, Union
-
import numpy as np
import pydantic.v1 as pd
@@ -85,7 +83,7 @@ class TerminalComponentModeler(AbstractComponentModeler, MicrowaveBaseModel):
"For each port, one simulation will be run with a source that is associated with the port.",
)
- run_only: Optional[tuple[NetworkIndex, ...]] = pd.Field(
+ run_only: tuple[NetworkIndex, ...] | None = pd.Field(
None,
title="Run Only",
description="Set of matrix indices that define the simulations to run. "
@@ -127,7 +125,7 @@ class TerminalComponentModeler(AbstractComponentModeler, MicrowaveBaseModel):
description="Whether to compute scattering parameters using the 'pseudo' or 'power' wave definitions.",
)
- low_freq_smoothing: Optional[ModelerLowFrequencySmoothingSpec] = pd.Field(
+ low_freq_smoothing: ModelerLowFrequencySmoothingSpec | None = pd.Field(
DEFAULT_LOW_FREQUENCY_SMOOTHING_SPEC,
title="Low Frequency Smoothing",
description="The low frequency smoothing parameters for the terminal component simulation.",
@@ -157,9 +155,9 @@ def _sim_with_sources(self) -> Simulation:
@add_ax_if_none
def plot_sim(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
**kwargs,
) -> Ax:
@@ -192,9 +190,9 @@ def plot_sim(
@add_ax_if_none
def plot_sim_eps(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
**kwargs,
) -> Ax:
@@ -225,7 +223,7 @@ def plot_sim_eps(
return self._sim_with_sources.plot_eps(x=x, y=y, z=z, ax=ax, **kwargs)
@staticmethod
- def network_index(port: TerminalPortType, mode_index: Optional[int] = None) -> NetworkIndex:
+ def network_index(port: TerminalPortType, mode_index: int | None = None) -> NetworkIndex:
"""Converts the port, and a ``mode_index`` when the port is a :class:`.WavePort`, to a unique string specifier.
Parameters
@@ -471,7 +469,7 @@ def _validate_radiation_monitors(cls, val, values):
@staticmethod
def _check_grid_size_at_ports(
- simulation: Simulation, ports: list[Union[LumpedPort, CoaxialLumpedPort]]
+ simulation: Simulation, ports: list[LumpedPort | CoaxialLumpedPort]
):
"""Raises :class:`.SetupError` if the grid is too coarse at port locations"""
yee_grid = simulation.grid.yee
diff --git a/tidy3d/plugins/smatrix/component_modelers/types.py b/tidy3d/plugins/smatrix/component_modelers/types.py
index 4500ab171a..65f9ee94fc 100644
--- a/tidy3d/plugins/smatrix/component_modelers/types.py
+++ b/tidy3d/plugins/smatrix/component_modelers/types.py
@@ -1,8 +1,6 @@
from __future__ import annotations
-from typing import Union
-
from .modal import ModalComponentModeler
from .terminal import TerminalComponentModeler
-ComponentModelerType = Union[ModalComponentModeler, TerminalComponentModeler]
+ComponentModelerType = ModalComponentModeler | TerminalComponentModeler
diff --git a/tidy3d/plugins/smatrix/data/terminal.py b/tidy3d/plugins/smatrix/data/terminal.py
index 7e92e26967..8f749edaf2 100644
--- a/tidy3d/plugins/smatrix/data/terminal.py
+++ b/tidy3d/plugins/smatrix/data/terminal.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Optional, Union
-
import numpy as np
import pydantic.v1 as pd
@@ -38,7 +36,7 @@
class MicrowaveSMatrixData(MicrowaveBaseModel):
"""Stores the computed S-matrix and reference impedances for the terminal ports."""
- port_reference_impedances: Optional[PortDataArray] = pd.Field(
+ port_reference_impedances: PortDataArray | None = pd.Field(
None,
title="Port Reference Impedances",
description="Reference impedance for each port used in the S-parameter calculation. This is optional and may not be present if not specified or computed.",
@@ -87,8 +85,8 @@ class TerminalComponentModelerData(AbstractComponentModelerData, MicrowaveBaseMo
def smatrix(
self,
- assume_ideal_excitation: Optional[bool] = None,
- s_param_def: Optional[SParamDef] = None,
+ assume_ideal_excitation: bool | None = None,
+ s_param_def: SParamDef | None = None,
) -> MicrowaveSMatrixData:
"""Computes and returns the S-matrix and port reference impedances.
@@ -238,7 +236,7 @@ def _monitor_data_at_port_amplitude(
self,
port: TerminalPortType,
monitor_name: str,
- a_port: Union[FreqDataArray, complex],
+ a_port: FreqDataArray | complex,
a_raw_port: FreqDataArray,
) -> MonitorData:
"""Normalize monitor data to a desired complex amplitude at a specific port.
@@ -276,8 +274,8 @@ def _monitor_data_at_port_amplitude(
def get_antenna_metrics_data(
self,
- port_amplitudes: Optional[dict[NetworkIndex, complex]] = None,
- monitor_name: Optional[str] = None,
+ port_amplitudes: dict[NetworkIndex, complex] | None = None,
+ monitor_name: str | None = None,
) -> AntennaMetricsData:
"""Calculate antenna parameters using superposition of fields from multiple port excitations.
@@ -341,7 +339,7 @@ def port_reference_impedances(self) -> PortDataArray:
def compute_wave_amplitudes_at_each_port(
self,
sim_data: SimulationData,
- port_reference_impedances: Optional[PortDataArray] = None,
+ port_reference_impedances: PortDataArray | None = None,
s_param_def: SParamDef = "pseudo",
) -> tuple[PortDataArray, PortDataArray]:
"""Compute the incident and reflected amplitudes at each port.
@@ -381,7 +379,7 @@ def compute_wave_amplitudes_at_each_port(
def compute_power_wave_amplitudes_at_each_port(
self,
sim_data: SimulationData,
- port_reference_impedances: Optional[PortDataArray] = None,
+ port_reference_impedances: PortDataArray | None = None,
) -> tuple[PortDataArray, PortDataArray]:
"""Compute the incident and reflected power wave amplitudes at each port.
The computed amplitudes have not been normalized.
@@ -405,8 +403,8 @@ def compute_power_wave_amplitudes_at_each_port(
def s_to_z(
self,
- reference: Union[complex, PortDataArray],
- assume_ideal_excitation: Optional[bool] = None,
+ reference: complex | PortDataArray,
+ assume_ideal_excitation: bool | None = None,
s_param_def: SParamDef = "pseudo",
) -> TerminalPortDataArray:
"""Converts the S-matrix to the Z-matrix using a specified reference impedance.
diff --git a/tidy3d/plugins/smatrix/data/types.py b/tidy3d/plugins/smatrix/data/types.py
index cdb6f92fa0..784e9691f9 100644
--- a/tidy3d/plugins/smatrix/data/types.py
+++ b/tidy3d/plugins/smatrix/data/types.py
@@ -1,8 +1,6 @@
from __future__ import annotations
-from typing import Union
-
from tidy3d.plugins.smatrix.data.modal import ModalComponentModelerData
from tidy3d.plugins.smatrix.data.terminal import TerminalComponentModelerData
-ComponentModelerDataType = Union[TerminalComponentModelerData, ModalComponentModelerData]
+ComponentModelerDataType = TerminalComponentModelerData | ModalComponentModelerData
diff --git a/tidy3d/plugins/smatrix/ports/base_lumped.py b/tidy3d/plugins/smatrix/ports/base_lumped.py
index 2871da304a..9d306fc424 100644
--- a/tidy3d/plugins/smatrix/ports/base_lumped.py
+++ b/tidy3d/plugins/smatrix/ports/base_lumped.py
@@ -3,7 +3,6 @@
from __future__ import annotations
from abc import abstractmethod
-from typing import Optional
import pydantic.v1 as pd
@@ -31,7 +30,7 @@ class AbstractLumpedPort(AbstractTerminalPort):
units=OHM,
)
- num_grid_cells: Optional[pd.PositiveInt] = pd.Field(
+ num_grid_cells: pd.PositiveInt | None = pd.Field(
DEFAULT_PORT_NUM_CELLS,
title="Port grid cells",
description="Number of mesh grid cells associated with the port along each direction, "
@@ -67,23 +66,23 @@ def snapped_center(self, grid: Grid) -> Coordinate:
@cached_property
@abstractmethod
- def to_load(self, snap_center: Optional[float] = None) -> LumpedElementType:
+ def to_load(self, snap_center: float | None = None) -> LumpedElementType:
"""Create a load from the lumped port."""
@abstractmethod
def to_voltage_monitor(
- self, freqs: FreqArray, snap_center: Optional[float] = None
+ self, freqs: FreqArray, snap_center: float | None = None
) -> FieldMonitor:
"""Field monitor to compute port voltage."""
@abstractmethod
def to_current_monitor(
- self, freqs: FreqArray, snap_center: Optional[float] = None
+ self, freqs: FreqArray, snap_center: float | None = None
) -> FieldMonitor:
"""Field monitor to compute port current."""
def to_monitors(
- self, freqs: FreqArray, snap_center: Optional[float] = None, grid: Grid = None
+ self, freqs: FreqArray, snap_center: float | None = None, grid: Grid = None
) -> list[FieldMonitor]:
"""Field monitors to compute port voltage and current."""
return [
diff --git a/tidy3d/plugins/smatrix/ports/base_terminal.py b/tidy3d/plugins/smatrix/ports/base_terminal.py
index 086ac36b91..d6a4fdc0d4 100644
--- a/tidy3d/plugins/smatrix/ports/base_terminal.py
+++ b/tidy3d/plugins/smatrix/ports/base_terminal.py
@@ -3,7 +3,6 @@
from __future__ import annotations
from abc import ABC, abstractmethod
-from typing import Optional, Union
import pydantic.v1 as pd
@@ -39,13 +38,13 @@ def injection_axis(self):
@abstractmethod
def to_source(
- self, source_time: GaussianPulse, snap_center: Optional[float] = None, grid: Grid = None
+ self, source_time: GaussianPulse, snap_center: float | None = None, grid: Grid = None
) -> Source:
"""Create a current source from a terminal-based port."""
def to_field_monitors(
- self, freqs: FreqArray, snap_center: Optional[float] = None, grid: Grid = None
- ) -> Union[list[FieldMonitor], list[ModeMonitor]]:
+ self, freqs: FreqArray, snap_center: float | None = None, grid: Grid = None
+ ) -> list[FieldMonitor] | list[ModeMonitor]:
"""DEPRECATED: Monitors used to compute the port voltage and current."""
log.warning(
"'to_field_monitors' method name is deprecated and will be removed in the future. Please use "
@@ -55,8 +54,8 @@ def to_field_monitors(
@abstractmethod
def to_monitors(
- self, freqs: FreqArray, snap_center: Optional[float] = None, grid: Grid = None
- ) -> Union[list[FieldMonitor], list[ModeMonitor]]:
+ self, freqs: FreqArray, snap_center: float | None = None, grid: Grid = None
+ ) -> list[FieldMonitor] | list[ModeMonitor]:
"""Monitors used to compute the port voltage and current."""
@abstractmethod
diff --git a/tidy3d/plugins/smatrix/ports/coaxial_lumped.py b/tidy3d/plugins/smatrix/ports/coaxial_lumped.py
index ed93899fe3..3402a95337 100644
--- a/tidy3d/plugins/smatrix/ports/coaxial_lumped.py
+++ b/tidy3d/plugins/smatrix/ports/coaxial_lumped.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Optional
-
import numpy as np
import pydantic.v1 as pd
@@ -112,7 +110,7 @@ def _ensure_inner_diameter_is_smaller(cls, val, values):
return val
def to_source(
- self, source_time: GaussianPulse, snap_center: Optional[float] = None, grid: Grid = None
+ self, source_time: GaussianPulse, snap_center: float | None = None, grid: Grid = None
) -> CustomCurrentSource:
"""Create a current source from the lumped port."""
# Discretized source amps are manually zeroed out later if they
@@ -201,7 +199,7 @@ def compute_coax_current(rin, rout, x, y):
current_dataset=dataset_E,
)
- def to_load(self, snap_center: Optional[float] = None) -> CoaxialLumpedResistor:
+ def to_load(self, snap_center: float | None = None) -> CoaxialLumpedResistor:
"""Create a load resistor from the lumped port."""
# 2D materials are currently snapped to the grid, so snapping here is not needed.
# Snapping is done here so plots of the simulation will more accurately portray the setup.
@@ -220,7 +218,7 @@ def to_load(self, snap_center: Optional[float] = None) -> CoaxialLumpedResistor:
)
def to_voltage_monitor(
- self, freqs: FreqArray, snap_center: Optional[float] = None, grid: Grid = None
+ self, freqs: FreqArray, snap_center: float | None = None, grid: Grid = None
) -> FieldMonitor:
"""Field monitor to compute port voltage."""
center = list(self.center)
@@ -244,7 +242,7 @@ def to_voltage_monitor(
)
def to_current_monitor(
- self, freqs: FreqArray, snap_center: Optional[float] = None, grid: Grid = None
+ self, freqs: FreqArray, snap_center: float | None = None, grid: Grid = None
) -> FieldMonitor:
"""Field monitor to compute port current."""
center = list(self.center)
diff --git a/tidy3d/plugins/smatrix/ports/rectangular_lumped.py b/tidy3d/plugins/smatrix/ports/rectangular_lumped.py
index b81974dbd8..76e0d6f871 100644
--- a/tidy3d/plugins/smatrix/ports/rectangular_lumped.py
+++ b/tidy3d/plugins/smatrix/ports/rectangular_lumped.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Optional
-
import numpy as np
import pydantic.v1 as pd
@@ -98,7 +96,7 @@ def current_axis(self) -> Axis:
return 3 - self.injection_axis - self.voltage_axis
def to_source(
- self, source_time: GaussianPulse, snap_center: Optional[float] = None, grid: Grid = None
+ self, source_time: GaussianPulse, snap_center: float | None = None, grid: Grid = None
) -> UniformCurrentSource:
"""Create a current source from the lumped port."""
if grid:
@@ -125,7 +123,7 @@ def to_source(
confine_to_bounds=True,
)
- def to_load(self, snap_center: Optional[float] = None) -> LumpedResistor:
+ def to_load(self, snap_center: float | None = None) -> LumpedResistor:
"""Create a load resistor from the lumped port."""
# 2D materials are currently snapped to the grid, so snapping here is not needed.
# It is done here so plots of the simulation will more accurately portray the setup
@@ -147,7 +145,7 @@ def to_load(self, snap_center: Optional[float] = None) -> LumpedResistor:
)
def to_voltage_monitor(
- self, freqs: FreqArray, snap_center: Optional[float] = None, grid: Grid = None
+ self, freqs: FreqArray, snap_center: float | None = None, grid: Grid = None
) -> FieldMonitor:
"""Field monitor to compute port voltage."""
if grid:
@@ -175,7 +173,7 @@ def to_voltage_monitor(
)
def to_current_monitor(
- self, freqs: FreqArray, snap_center: Optional[float] = None, grid: Grid = None
+ self, freqs: FreqArray, snap_center: float | None = None, grid: Grid = None
) -> FieldMonitor:
"""Field monitor to compute port current."""
if grid:
diff --git a/tidy3d/plugins/smatrix/ports/types.py b/tidy3d/plugins/smatrix/ports/types.py
index 3d2dfb0774..f2e30a0bbb 100644
--- a/tidy3d/plugins/smatrix/ports/types.py
+++ b/tidy3d/plugins/smatrix/ports/types.py
@@ -1,7 +1,5 @@
from __future__ import annotations
-from typing import Union
-
from tidy3d.components.data.data_array import (
CurrentFreqDataArray,
CurrentFreqModeDataArray,
@@ -12,7 +10,7 @@
from tidy3d.plugins.smatrix.ports.rectangular_lumped import LumpedPort
from tidy3d.plugins.smatrix.ports.wave import WavePort
-LumpedPortType = Union[LumpedPort, CoaxialLumpedPort]
-TerminalPortType = Union[LumpedPortType, WavePort]
-PortVoltageType = Union[VoltageFreqDataArray, VoltageFreqModeDataArray]
-PortCurrentType = Union[CurrentFreqDataArray, CurrentFreqModeDataArray]
+LumpedPortType = LumpedPort | CoaxialLumpedPort
+TerminalPortType = LumpedPortType | WavePort
+PortVoltageType = VoltageFreqDataArray | VoltageFreqModeDataArray
+PortCurrentType = CurrentFreqDataArray | CurrentFreqModeDataArray
diff --git a/tidy3d/plugins/smatrix/ports/wave.py b/tidy3d/plugins/smatrix/ports/wave.py
index 890c906446..b5e1598968 100644
--- a/tidy3d/plugins/smatrix/ports/wave.py
+++ b/tidy3d/plugins/smatrix/ports/wave.py
@@ -2,8 +2,6 @@
from __future__ import annotations
-from typing import Optional, Union
-
import numpy as np
import pydantic.v1 as pd
@@ -62,19 +60,19 @@ class WavePort(AbstractTerminalPort, Box):
"``num_modes`` in the solver will be set to ``mode_index + 1``.",
)
- voltage_integral: Optional[VoltageIntegralType] = pd.Field(
+ voltage_integral: VoltageIntegralType | None = pd.Field(
None,
title="Voltage Integral",
description="Definition of voltage integral used to compute voltage and the characteristic impedance.",
)
- current_integral: Optional[CurrentIntegralType] = pd.Field(
+ current_integral: CurrentIntegralType | None = pd.Field(
None,
title="Current Integral",
description="Definition of current integral used to compute current and the characteristic impedance.",
)
- num_grid_cells: Optional[int] = pd.Field(
+ num_grid_cells: int | None = pd.Field(
DEFAULT_WAVE_PORT_NUM_CELLS,
ge=MIN_WAVE_PORT_NUM_CELLS,
title="Number of Grid Cells",
@@ -89,13 +87,13 @@ class WavePort(AbstractTerminalPort, Box):
description="Use conjugated or non-conjugated dot product for mode decomposition.",
)
- frame: Optional[PECFrame] = pd.Field(
+ frame: PECFrame | None = pd.Field(
DEFAULT_WAVE_PORT_FRAME,
title="Source Frame",
description="Add a thin frame around the source during FDTD run for an improved injection.",
)
- absorber: Union[bool, ABCBoundary, ModeABCBoundary] = pd.Field(
+ absorber: bool | ABCBoundary | ModeABCBoundary = pd.Field(
True,
title="Absorber",
description="Place a mode absorber in the port. If ``True``, an automatically generated mode absorber is placed in the port. "
@@ -150,9 +148,7 @@ def _mode_monitor_name(self) -> str:
"""Return the name of the :class:`.ModeMonitor` associated with this port."""
return f"{self.name}_mode"
- def to_source(
- self, source_time: GaussianPulse, snap_center: Optional[float] = None
- ) -> ModeSource:
+ def to_source(self, source_time: GaussianPulse, snap_center: float | None = None) -> ModeSource:
"""Create a mode source from the wave port."""
center = list(self.center)
if snap_center:
@@ -169,7 +165,7 @@ def to_source(
)
def to_monitors(
- self, freqs: FreqArray, snap_center: Optional[float] = None, grid: Grid = None
+ self, freqs: FreqArray, snap_center: float | None = None, grid: Grid = None
) -> list[ModeMonitor]:
"""The wave port uses a :class:`.ModeMonitor` to compute the characteristic impedance
and the port voltages and currents."""
@@ -201,7 +197,7 @@ def to_mode_solver(self, simulation: Simulation, freqs: FreqArray) -> ModeSolver
return mode_solver
def to_absorber(
- self, snap_center: Optional[float] = None, freq_spec: Optional[pd.NonNegativeFloat] = None
+ self, snap_center: float | None = None, freq_spec: pd.NonNegativeFloat | None = None
) -> InternalAbsorber:
"""Create an internal absorber from the wave port."""
center = list(self.center)
@@ -249,9 +245,7 @@ def compute_current(self, sim_data: SimulationData) -> FreqDataArray:
sign = -1.0
return sign * current_coeffs * (fwd_amps - bwd_amps)
- def compute_port_impedance(
- self, sim_mode_data: Union[SimulationData, ModeData]
- ) -> FreqModeDataArray:
+ def compute_port_impedance(self, sim_mode_data: SimulationData | ModeData) -> FreqModeDataArray:
"""Helper to compute impedance of port. The port impedance is computed from the
transmission line mode, which should be TEM or at least quasi-TEM."""
impedance_calc = ImpedanceCalculator(
diff --git a/tidy3d/plugins/smatrix/utils.py b/tidy3d/plugins/smatrix/utils.py
index e53f469508..3f28d013e5 100644
--- a/tidy3d/plugins/smatrix/utils.py
+++ b/tidy3d/plugins/smatrix/utils.py
@@ -7,8 +7,6 @@
from __future__ import annotations
-from typing import Union
-
import numpy as np
from tidy3d.components.data.data_array import (
@@ -216,7 +214,7 @@ def compute_power_delivered_by_port(
def s_to_z(
s_matrix: TerminalPortDataArray,
- reference: Union[complex, PortDataArray],
+ reference: complex | PortDataArray,
s_param_def: SParamDef = "pseudo",
) -> DataArray:
"""Get the impedance matrix given the scattering matrix and a reference impedance.
diff --git a/tidy3d/plugins/waveguide/rectangular_dielectric.py b/tidy3d/plugins/waveguide/rectangular_dielectric.py
index 73c4f9b88e..e49c9bfeb6 100644
--- a/tidy3d/plugins/waveguide/rectangular_dielectric.py
+++ b/tidy3d/plugins/waveguide/rectangular_dielectric.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-from typing import Annotated, Any, Literal, Optional, Union
+from typing import Annotated, Any, Literal
import numpy
import pydantic.v1 as pydantic
@@ -44,14 +44,14 @@ class RectangularDielectric(Tidy3dBaseModel):
- Coupled waveguides
"""
- wavelength: Union[float, ArrayFloat1D] = pydantic.Field(
+ wavelength: float | ArrayFloat1D = pydantic.Field(
...,
title="Wavelength",
description="Wavelength(s) at which to calculate modes (in μm).",
units=MICROMETER,
)
- core_width: Union[Size1D, ArrayFloat1D] = pydantic.Field(
+ core_width: Size1D | ArrayFloat1D = pydantic.Field(
...,
title="Core width",
description="Core width at the top of the waveguide. If set to an array, defines "
@@ -73,14 +73,14 @@ class RectangularDielectric(Tidy3dBaseModel):
discriminator=TYPE_TAG_STR,
)
- clad_medium: Union[AnnotatedMedium, tuple[AnnotatedMedium, ...]] = pydantic.Field(
+ clad_medium: AnnotatedMedium | tuple[AnnotatedMedium, ...] = pydantic.Field(
...,
title="Clad Medium",
description="Medium associated with the upper cladding layer. A sequence of mediums can "
"be used to create a layered clad.",
)
- box_medium: Union[AnnotatedMedium, tuple[AnnotatedMedium, ...]] = pydantic.Field(
+ box_medium: AnnotatedMedium | tuple[AnnotatedMedium, ...] = pydantic.Field(
None,
title="Box Medium",
description="Medium associated with the lower cladding layer. A sequence of mediums can "
@@ -94,7 +94,7 @@ class RectangularDielectric(Tidy3dBaseModel):
units=MICROMETER,
)
- clad_thickness: Union[Size1D, ArrayFloat1D] = pydantic.Field(
+ clad_thickness: Size1D | ArrayFloat1D = pydantic.Field(
None,
title="Clad Thickness",
description="Domain size above the core layer. An array can be used to define a layered "
@@ -102,7 +102,7 @@ class RectangularDielectric(Tidy3dBaseModel):
units=MICROMETER,
)
- box_thickness: Union[Size1D, ArrayFloat1D] = pydantic.Field(
+ box_thickness: Size1D | ArrayFloat1D = pydantic.Field(
None,
title="Box Thickness",
description="Domain size below the core layer. An array can be used to define a layered "
@@ -126,7 +126,7 @@ class RectangularDielectric(Tidy3dBaseModel):
units=RADIAN,
)
- gap: Union[float, ArrayFloat1D] = pydantic.Field(
+ gap: float | ArrayFloat1D = pydantic.Field(
0.0,
title="Gap",
description="Distance between adjacent waveguides, measured at the top core edges. "
@@ -816,12 +816,12 @@ def mode_area(self) -> FreqModeDataArray:
def plot(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
- source_alpha: Optional[float] = None,
- monitor_alpha: Optional[float] = None,
+ source_alpha: float | None = None,
+ monitor_alpha: float | None = None,
**patch_kwargs,
) -> Ax:
"""Plot each of simulation's components on a plane defined by one nonzero x,y,z coordinate.
@@ -858,13 +858,13 @@ def plot(
def plot_eps(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
- freq: Optional[float] = None,
- alpha: Optional[float] = None,
- source_alpha: Optional[float] = None,
- monitor_alpha: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
+ freq: float | None = None,
+ alpha: float | None = None,
+ source_alpha: float | None = None,
+ monitor_alpha: float | None = None,
ax: Ax = None,
) -> Ax:
"""Plot each of simulation's components on a plane defined by one nonzero x,y,z coordinate.
@@ -909,9 +909,9 @@ def plot_eps(
def plot_structures(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
) -> Ax:
"""Plot each of simulation's structures on a plane defined by one nonzero x,y,z coordinate.
@@ -941,11 +941,11 @@ def plot_structures(
def plot_structures_eps(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
- freq: Optional[float] = None,
- alpha: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
+ freq: float | None = None,
+ alpha: float | None = None,
cbar: bool = True,
reverse: bool = False,
ax: Ax = None,
@@ -993,9 +993,9 @@ def plot_structures_eps(
def plot_grid(
self,
- x: Optional[float] = None,
- y: Optional[float] = None,
- z: Optional[float] = None,
+ x: float | None = None,
+ y: float | None = None,
+ z: float | None = None,
ax: Ax = None,
**kwargs,
) -> Ax:
@@ -1095,10 +1095,10 @@ def plot_field(
val: Literal["real", "imag", "abs"] = "real",
eps_alpha: float = 0.2,
robust: bool = True,
- vmin: Optional[float] = None,
- vmax: Optional[float] = None,
+ vmin: float | None = None,
+ vmax: float | None = None,
ax: Ax = None,
- geometry_edges: Optional[str] = None,
+ geometry_edges: str | None = None,
**sel_kwargs,
) -> Ax:
"""Plot the field for a :class:`.ModeSolverData` with :class:`.Simulation` plot overlaid.
diff --git a/tidy3d/updater.py b/tidy3d/updater.py
index bc7f017fab..b048f44cef 100644
--- a/tidy3d/updater.py
+++ b/tidy3d/updater.py
@@ -4,7 +4,7 @@
import functools
import json
-from typing import Callable, Optional
+from collections.abc import Callable
import pydantic.v1 as pd
import yaml
@@ -24,7 +24,7 @@ class Version(pd.BaseModel):
minor: int
@classmethod
- def from_string(cls, string: Optional[str] = None) -> Version:
+ def from_string(cls, string: str | None = None) -> Version:
"""Return Version from a version string."""
if string is None:
return cls.from_string(string=__version__)
diff --git a/tidy3d/web/api/asynchronous.py b/tidy3d/web/api/asynchronous.py
index c5a346abdd..606d922286 100644
--- a/tidy3d/web/api/asynchronous.py
+++ b/tidy3d/web/api/asynchronous.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-from typing import Literal, Optional, Union
+from typing import Literal
from tidy3d.components.types.workflow import WorkflowType
from tidy3d.log import log
@@ -12,18 +12,18 @@
def run_async(
- simulations: Union[dict[str, WorkflowType], tuple[WorkflowType], list[WorkflowType]],
+ simulations: dict[str, WorkflowType] | tuple[WorkflowType] | list[WorkflowType],
folder_name: str = "default",
path_dir: str = DEFAULT_DATA_DIR,
- callback_url: Optional[str] = None,
- num_workers: Optional[int] = None,
+ callback_url: str | None = None,
+ num_workers: int | None = None,
verbose: bool = True,
simulation_type: str = "tidy3d",
- solver_version: Optional[str] = None,
- parent_tasks: Optional[dict[str, list[str]]] = None,
+ solver_version: str | None = None,
+ parent_tasks: dict[str, list[str]] | None = None,
reduce_simulation: Literal["auto", True, False] = "auto",
- pay_type: Union[PayType, str] = PayType.AUTO,
- priority: Optional[int] = None,
+ pay_type: PayType | str = PayType.AUTO,
+ priority: int | None = None,
lazy: bool = False,
) -> BatchData:
"""Submits a set of Union[:class:`.Simulation`, :class:`.HeatSimulation`, :class:`.EMESimulation`] objects to server,
diff --git a/tidy3d/web/api/autograd/autograd.py b/tidy3d/web/api/autograd/autograd.py
index 9a29d71af6..18660a97ef 100644
--- a/tidy3d/web/api/autograd/autograd.py
+++ b/tidy3d/web/api/autograd/autograd.py
@@ -50,7 +50,7 @@
)
-def _resolve_local_gradient(value: typing.Optional[bool]) -> bool:
+def _resolve_local_gradient(value: bool | None) -> bool:
if value is not None:
return bool(value)
@@ -100,23 +100,23 @@ def is_valid_for_autograd_async(simulations: dict[str, td.Simulation]) -> bool:
def run(
simulation: WorkflowType,
- task_name: typing.Optional[str] = None,
+ task_name: str | None = None,
folder_name: str = "default",
path: str = "simulation_data.hdf5",
- callback_url: typing.Optional[str] = None,
+ callback_url: str | None = None,
verbose: bool = True,
- progress_callback_upload: typing.Optional[typing.Callable[[float], None]] = None,
- progress_callback_download: typing.Optional[typing.Callable[[float], None]] = None,
- solver_version: typing.Optional[str] = None,
- worker_group: typing.Optional[str] = None,
+ progress_callback_upload: typing.Callable[[float], None] | None = None,
+ progress_callback_download: typing.Callable[[float], None] | None = None,
+ solver_version: str | None = None,
+ worker_group: str | None = None,
simulation_type: str = "tidy3d",
- parent_tasks: typing.Optional[list[str]] = None,
- local_gradient: typing.Optional[bool] = None,
- max_num_adjoint_per_fwd: typing.Optional[int] = None,
+ parent_tasks: list[str] | None = None,
+ local_gradient: bool | None = None,
+ max_num_adjoint_per_fwd: int | None = None,
reduce_simulation: typing.Literal["auto", True, False] = "auto",
- pay_type: typing.Union[PayType, str] = PayType.AUTO,
- priority: typing.Optional[int] = None,
- lazy: typing.Optional[bool] = None,
+ pay_type: PayType | str = PayType.AUTO,
+ priority: int | None = None,
+ lazy: bool | None = None,
) -> WorkflowDataType:
"""
Submits a :class:`.Simulation` to server, starts running, monitors progress, downloads,
@@ -285,21 +285,21 @@ def run(
def run_async(
- simulations: typing.Union[dict[str, td.Simulation], tuple[td.Simulation], list[td.Simulation]],
+ simulations: dict[str, td.Simulation] | tuple[td.Simulation] | list[td.Simulation],
folder_name: str = "default",
path_dir: str = DEFAULT_DATA_DIR,
- callback_url: typing.Optional[str] = None,
- num_workers: typing.Optional[int] = None,
+ callback_url: str | None = None,
+ num_workers: int | None = None,
verbose: bool = True,
simulation_type: str = "tidy3d",
- solver_version: typing.Optional[str] = None,
- parent_tasks: typing.Optional[dict[str, list[str]]] = None,
- local_gradient: typing.Optional[bool] = None,
- max_num_adjoint_per_fwd: typing.Optional[int] = None,
+ solver_version: str | None = None,
+ parent_tasks: dict[str, list[str]] | None = None,
+ local_gradient: bool | None = None,
+ max_num_adjoint_per_fwd: int | None = None,
reduce_simulation: typing.Literal["auto", True, False] = "auto",
- pay_type: typing.Union[PayType, str] = PayType.AUTO,
- priority: typing.Optional[int] = None,
- lazy: typing.Optional[bool] = None,
+ pay_type: PayType | str = PayType.AUTO,
+ priority: int | None = None,
+ lazy: bool | None = None,
) -> BatchData:
"""Submits a set of Union[:class:`.Simulation`, :class:`.HeatSimulation`, :class:`.EMESimulation`] objects to server,
starts running, monitors progress, downloads, and loads results as a :class:`.BatchData` object.
@@ -416,7 +416,7 @@ def _run(
simulation: td.Simulation,
task_name: str,
local_gradient: bool = False,
- max_num_adjoint_per_fwd: typing.Optional[int] = None,
+ max_num_adjoint_per_fwd: int | None = None,
**run_kwargs,
) -> td.SimulationData:
"""User-facing ``web.run`` function, compatible with ``autograd`` differentiation."""
@@ -459,7 +459,7 @@ def _run(
def _run_async(
simulations: dict[str, td.Simulation],
local_gradient: bool = False,
- max_num_adjoint_per_fwd: typing.Optional[int] = None,
+ max_num_adjoint_per_fwd: int | None = None,
**run_async_kwargs,
) -> dict[str, td.SimulationData]:
"""User-facing ``web.run_async`` function, compatible with ``autograd`` differentiation."""
diff --git a/tidy3d/web/api/autograd/utils.py b/tidy3d/web/api/autograd/utils.py
index f58f2f3c34..aff97eeacd 100644
--- a/tidy3d/web/api/autograd/utils.py
+++ b/tidy3d/web/api/autograd/utils.py
@@ -1,8 +1,6 @@
# utility functions for autograd web API
from __future__ import annotations
-import typing
-
import numpy as np
import tidy3d as td
@@ -58,11 +56,11 @@ def E_to_D(fld_data: td.FieldData, eps_data: td.PermittivityData) -> td.FieldDat
def multiply_field_data(
- fld_1: td.FieldData, fld_2: typing.Union[td.FieldData, td.PermittivityData], fld_key: str
+ fld_1: td.FieldData, fld_2: td.FieldData | td.PermittivityData, fld_key: str
) -> td.FieldData:
"""Elementwise multiply two field data objects, writes data into ``fld_1`` copy."""
- def get_field_key(dim: str, fld_data: typing.Union[td.FieldData, td.PermittivityData]) -> str:
+ def get_field_key(dim: str, fld_data: td.FieldData | td.PermittivityData) -> str:
"""Get the key corresponding to the scalar field along this dimension."""
return f"{fld_key}{dim}" if isinstance(fld_data, td.FieldData) else f"eps_{dim}{dim}"
diff --git a/tidy3d/web/api/connect_util.py b/tidy3d/web/api/connect_util.py
index 5f10d3af3e..4ef9c9f333 100644
--- a/tidy3d/web/api/connect_util.py
+++ b/tidy3d/web/api/connect_util.py
@@ -4,7 +4,6 @@
import time
from functools import wraps
-from typing import Optional
from requests import ReadTimeout
from requests.exceptions import ConnectionError as ConnErr
@@ -17,7 +16,7 @@
from tidy3d.web.common import REFRESH_TIME
-def wait_for_connection(decorated_fn=None, wait_time_sec: Optional[float] = None):
+def wait_for_connection(decorated_fn=None, wait_time_sec: float | None = None):
"""Causes function to ignore connection errors and retry for ``wait_time_sec`` secs."""
def decorator(web_fn, wait_time_sec=wait_time_sec):
diff --git a/tidy3d/web/api/container.py b/tidy3d/web/api/container.py
index ed9cb775cc..b375a8c210 100644
--- a/tidy3d/web/api/container.py
+++ b/tidy3d/web/api/container.py
@@ -8,7 +8,7 @@
from abc import ABC
from collections.abc import Mapping
from concurrent.futures import ThreadPoolExecutor
-from typing import Literal, Optional, Union
+from typing import Literal
import pydantic.v1 as pd
from rich.progress import BarColumn, Progress, TaskProgressColumn, TextColumn, TimeElapsedColumn
@@ -256,9 +256,7 @@ def to_file(self, fname: str) -> None:
self = self.updated_copy(task_id_cached=task_id_cached)
super(Job, self).to_file(fname=fname) # noqa: UP008
- def run(
- self, path: str = DEFAULT_DATA_PATH, priority: Optional[int] = None
- ) -> WorkflowDataType:
+ def run(self, path: str = DEFAULT_DATA_PATH, priority: int | None = None) -> WorkflowDataType:
"""Run :class:`Job` all the way through and return data.
Parameters
@@ -333,7 +331,7 @@ def postprocess_status(self):
)
return
- def start(self, priority: Optional[int] = None) -> None:
+ def start(self, priority: int | None = None) -> None:
"""Start running a :class:`Job`.
Parameters
@@ -446,7 +444,7 @@ def estimate_cost(self, verbose: bool = True) -> float:
"""
return web.estimate_cost(self.task_id, verbose=verbose, solver_version=self.solver_version)
- def postprocess_start(self, worker_group: Optional[str] = None, verbose: bool = True) -> None:
+ def postprocess_start(self, worker_group: str | None = None, verbose: bool = True) -> None:
"""
If the job is a modeler batch, checks if the run is complete and starts
the postprocess phase.
@@ -618,9 +616,9 @@ class Batch(WebContainer):
* `Inverse taper edge coupler <../../notebooks/EdgeCoupler.html>`_
"""
- simulations: Union[
- dict[TaskName, annotate_type(WorkflowType)], tuple[annotate_type(WorkflowType), ...]
- ] = pd.Field(
+ simulations: (
+ dict[TaskName, annotate_type(WorkflowType)] | tuple[annotate_type(WorkflowType), ...]
+ ) = pd.Field(
...,
title="Simulations",
description="Mapping of task names to Simulations to run as a batch.",
@@ -663,7 +661,7 @@ class Batch(WebContainer):
description="Collection of parent task ids for each job in batch, used internally only.",
)
- num_workers: Optional[pd.PositiveInt] = pd.Field(
+ num_workers: pd.PositiveInt | None = pd.Field(
DEFAULT_NUM_WORKERS,
title="Number of Workers",
description="Number of workers for multi-threading upload and download of batch. "
@@ -704,7 +702,7 @@ class Batch(WebContainer):
def run(
self,
path_dir: str = DEFAULT_DATA_DIR,
- priority: Optional[int] = None,
+ priority: int | None = None,
) -> BatchData:
"""Upload and run each simulation in :class:`Batch`.
@@ -858,7 +856,7 @@ def get_info(self) -> dict[TaskName, TaskInfo]:
def start(
self,
- priority: Optional[int] = None,
+ priority: int | None = None,
) -> None:
"""Start running all tasks in the :class:`Batch`.
@@ -897,7 +895,7 @@ def get_run_info(self) -> dict[TaskName, RunInfo]:
run_info_dict[task_name] = run_info
return run_info_dict
- def postprocess_start(self, worker_group: Optional[str] = None, verbose: bool = True) -> None:
+ def postprocess_start(self, worker_group: str | None = None, verbose: bool = True) -> None:
"""
Start the postprocess phase for all applicable jobs in the batch.
@@ -918,7 +916,7 @@ def monitor(
download_on_success: bool = False,
path_dir: str = DEFAULT_DATA_DIR,
replace_existing: bool = False,
- postprocess_worker_group: Optional[str] = None,
+ postprocess_worker_group: str | None = None,
) -> None:
"""
Monitor progress of each running task.
@@ -943,7 +941,7 @@ def monitor(
# ----- download scheduling ---------------------------------------------------
downloads_started: set[str] = set()
download_futures: dict[TaskId, concurrent.futures.Future] = {}
- download_executor: Optional[ThreadPoolExecutor] = None
+ download_executor: ThreadPoolExecutor | None = None
if download_on_success:
self._check_path_dir(path_dir=path_dir)
diff --git a/tidy3d/web/api/material_fitter.py b/tidy3d/web/api/material_fitter.py
index f66dce6471..1a12c470cb 100644
--- a/tidy3d/web/api/material_fitter.py
+++ b/tidy3d/web/api/material_fitter.py
@@ -5,7 +5,6 @@
import os
import tempfile
from enum import Enum
-from typing import Optional
from uuid import uuid4
import numpy as np
@@ -27,14 +26,14 @@ class ConstraintEnum(str, Enum):
class FitterOptions(BaseModel):
"""Fitter Options."""
- num_poles: Optional[int] = 1
- num_tries: Optional[int] = 50
- tolerance_rms: Optional[float] = 1e-2
- min_wvl: Optional[float] = None
- max_wvl: Optional[float] = None
- bound_amp: Optional[float] = None
- bound_eps_inf: Optional[float] = 1.0
- bound_f: Optional[float] = None
+ num_poles: int | None = 1
+ num_tries: int | None = 50
+ tolerance_rms: float | None = 1e-2
+ min_wvl: float | None = None
+ max_wvl: float | None = None
+ bound_amp: float | None = None
+ bound_eps_inf: float | None = 1.0
+ bound_f: float | None = None
constraint: ConstraintEnum = ConstraintEnum.HARD
nlopt_maxeval: int = 5000
diff --git a/tidy3d/web/api/material_libray.py b/tidy3d/web/api/material_libray.py
index 98bfeaa0b2..606fe079db 100644
--- a/tidy3d/web/api/material_libray.py
+++ b/tidy3d/web/api/material_libray.py
@@ -4,7 +4,6 @@
import builtins
import json
-from typing import Optional
from pydantic.v1 import Field, parse_obj_as, validator
@@ -18,11 +17,11 @@ class MaterialLibray(Queryable, smart_union=True):
id: str = Field(title="Material Library ID", description="Material Library ID")
name: str = Field(title="Material Library Name", description="Material Library Name")
- medium: Optional[MediumType] = Field(title="medium", description="medium", alias="calcResult")
- medium_type: Optional[str] = Field(
+ medium: MediumType | None = Field(title="medium", description="medium", alias="calcResult")
+ medium_type: str | None = Field(
title="medium type", description="medium type", alias="mediumType"
)
- json_input: Optional[dict] = Field(
+ json_input: dict | None = Field(
title="json input", description="original input", alias="jsonInput"
)
diff --git a/tidy3d/web/api/mode.py b/tidy3d/web/api/mode.py
index f69be5cd26..f7ceec5519 100644
--- a/tidy3d/web/api/mode.py
+++ b/tidy3d/web/api/mode.py
@@ -6,8 +6,9 @@
import pathlib
import tempfile
import time
+from collections.abc import Callable
from datetime import datetime
-from typing import Callable, Literal, Optional, Union
+from typing import Literal
import pydantic.v1 as pydantic
from botocore.exceptions import ClientError
@@ -52,10 +53,10 @@ def run(
folder_name: str = "Mode Solver",
results_file: str = "mode_solver.hdf5",
verbose: bool = True,
- progress_callback_upload: Optional[Callable[[float], None]] = None,
- progress_callback_download: Optional[Callable[[float], None]] = None,
+ progress_callback_upload: Callable[[float], None] | None = None,
+ progress_callback_download: Callable[[float], None] | None = None,
reduce_simulation: Literal["auto", True, False] = "auto",
- pay_type: Union[PayType, str] = PayType.AUTO,
+ pay_type: PayType | str = PayType.AUTO,
) -> ModeSolverData:
"""Submits a :class:`.ModeSolver` to server, starts running, monitors progress, downloads,
and loads results as a :class:`.ModeSolverData` object.
@@ -149,13 +150,13 @@ def run_batch(
mode_solvers: list[ModeSolver],
task_name: str = "BatchModeSolver",
folder_name: str = "BatchModeSolvers",
- results_files: Optional[list[str]] = None,
+ results_files: list[str] | None = None,
verbose: bool = True,
max_workers: int = DEFAULT_NUM_WORKERS,
max_retries: int = DEFAULT_MAX_RETRIES,
retry_delay: float = DEFAULT_RETRY_DELAY,
- progress_callback_upload: Optional[Callable[[float], None]] = None,
- progress_callback_download: Optional[Callable[[float], None]] = None,
+ progress_callback_upload: Callable[[float], None] | None = None,
+ progress_callback_download: Callable[[float], None] | None = None,
) -> list[ModeSolverData]:
"""
Submits a batch of ModeSolver to the server concurrently, manages progress, and retrieves results.
@@ -273,7 +274,7 @@ class ModeSolverTask(ResourceLifecycle, Submittable, extra=pydantic.Extra.allow)
None, title="real FlexCredits", description="Billed FlexCredits.", alias="charge"
)
- created_at: Optional[datetime] = pydantic.Field(
+ created_at: datetime | None = pydantic.Field(
title="created_at", description="Time at which this task was created.", alias="createdAt"
)
@@ -375,7 +376,7 @@ def get(
to_file: str = "mode_solver.hdf5",
sim_file: str = "simulation.hdf5",
verbose: bool = True,
- progress_callback: Optional[Callable[[float], None]] = None,
+ progress_callback: Callable[[float], None] | None = None,
) -> ModeSolverTask:
"""Get mode solver task from the server by id.
@@ -417,7 +418,7 @@ def get_info(self) -> ModeSolverTask:
return ModeSolverTask(**resp, mode_solver=self.mode_solver)
def upload(
- self, verbose: bool = True, progress_callback: Optional[Callable[[float], None]] = None
+ self, verbose: bool = True, progress_callback: Callable[[float], None] | None = None
) -> None:
"""Upload this task's 'mode_solver' to the server.
@@ -464,7 +465,7 @@ def upload(
def submit(
self,
- pay_type: Union[PayType, str] = PayType.AUTO,
+ pay_type: PayType | str = PayType.AUTO,
):
"""Start the execution of this task.
@@ -500,7 +501,7 @@ def get_modesolver(
to_file: str = "mode_solver.hdf5",
sim_file: str = "simulation.hdf5",
verbose: bool = True,
- progress_callback: Optional[Callable[[float], None]] = None,
+ progress_callback: Callable[[float], None] | None = None,
) -> ModeSolver:
"""Get mode solver associated with this task from the server.
@@ -584,7 +585,7 @@ def get_result(
self,
to_file: str = "mode_solver_data.hdf5",
verbose: bool = True,
- progress_callback: Optional[Callable[[float], None]] = None,
+ progress_callback: Callable[[float], None] | None = None,
) -> ModeSolverData:
"""Get mode solver results for this task from the server.
@@ -647,7 +648,7 @@ def get_log(
self,
to_file: str = "mode_solver.log",
verbose: bool = True,
- progress_callback: Optional[Callable[[float], None]] = None,
+ progress_callback: Callable[[float], None] | None = None,
) -> pathlib.Path:
"""Get execution log for this task from the server.
diff --git a/tidy3d/web/api/run.py b/tidy3d/web/api/run.py
index c65f8e94a3..5a4b0904d2 100644
--- a/tidy3d/web/api/run.py
+++ b/tidy3d/web/api/run.py
@@ -9,19 +9,16 @@
from tidy3d.web.api.container import DEFAULT_DATA_DIR, DEFAULT_DATA_PATH
from tidy3d.web.core.types import PayType
-RunInput: typing.TypeAlias = typing.Union[
- WorkflowType,
- list["RunInput"],
- tuple["RunInput", ...],
- dict[typing.Hashable, "RunInput"],
-]
+RunInput: typing.TypeAlias = (
+ WorkflowType | list["RunInput"] | tuple["RunInput", ...] | dict[typing.Hashable, "RunInput"]
+)
-RunOutput: typing.TypeAlias = typing.Union[
- WorkflowDataType,
- list["RunOutput"],
- tuple["RunOutput", ...],
- dict[typing.Hashable, "RunOutput"],
-]
+RunOutput: typing.TypeAlias = (
+ WorkflowDataType
+ | list["RunOutput"]
+ | tuple["RunOutput", ...]
+ | dict[typing.Hashable, "RunOutput"]
+)
def _collect_by_hash(
@@ -79,24 +76,24 @@ def _recur(n: RunInput) -> RunOutput:
def run(
simulation: RunInput,
- task_name: typing.Optional[str] = None,
+ task_name: str | None = None,
folder_name: str = "default",
- path: typing.Optional[str] = None,
- callback_url: typing.Optional[str] = None,
+ path: str | None = None,
+ callback_url: str | None = None,
verbose: bool = True,
- progress_callback_upload: typing.Optional[typing.Callable[[float], None]] = None,
- progress_callback_download: typing.Optional[typing.Callable[[float], None]] = None,
- solver_version: typing.Optional[str] = None,
- worker_group: typing.Optional[str] = None,
+ progress_callback_upload: typing.Callable[[float], None] | None = None,
+ progress_callback_download: typing.Callable[[float], None] | None = None,
+ solver_version: str | None = None,
+ worker_group: str | None = None,
simulation_type: str = "tidy3d",
- parent_tasks: typing.Optional[list[str]] = None,
- local_gradient: typing.Optional[bool] = None,
- max_num_adjoint_per_fwd: typing.Optional[int] = None,
+ parent_tasks: list[str] | None = None,
+ local_gradient: bool | None = None,
+ max_num_adjoint_per_fwd: int | None = None,
reduce_simulation: typing.Literal["auto", True, False] = "auto",
- pay_type: typing.Union[PayType, str] = PayType.AUTO,
- priority: typing.Optional[int] = None,
- max_workers: typing.Optional[int] = None,
- lazy: typing.Optional[bool] = None,
+ pay_type: PayType | str = PayType.AUTO,
+ priority: int | None = None,
+ max_workers: int | None = None,
+ lazy: bool | None = None,
) -> RunOutput:
"""
Submit one or many simulations and return results in the same container shape.
diff --git a/tidy3d/web/api/tidy3d_stub.py b/tidy3d/web/api/tidy3d_stub.py
index 22152e21ab..34b477ab45 100644
--- a/tidy3d/web/api/tidy3d_stub.py
+++ b/tidy3d/web/api/tidy3d_stub.py
@@ -3,8 +3,8 @@
from __future__ import annotations
import json
+from collections.abc import Callable
from datetime import datetime
-from typing import Callable, Optional
import pydantic.v1 as pd
from pydantic.v1 import BaseModel
@@ -145,7 +145,7 @@ def to_file(
"""
self.simulation.to_file(file_path)
- def to_hdf5_gz(self, fname: str, custom_encoders: Optional[list[Callable]] = None) -> None:
+ def to_hdf5_gz(self, fname: str, custom_encoders: list[Callable] | None = None) -> None:
"""Exports Union[:class:`.Simulation`, :class:`.HeatSimulation`, :class:`.EMESimulation`] instance to .hdf5.gz file.
Parameters
@@ -210,7 +210,7 @@ class Tidy3dStubData(BaseModel, TaskStubData):
@classmethod
def from_file(
- cls, file_path: str, lazy: bool = False, on_load: Optional[Callable] = None
+ cls, file_path: str, lazy: bool = False, on_load: Callable | None = None
) -> WorkflowDataType:
"""Loads a Union[:class:`.SimulationData`, :class:`.HeatSimulationData`, :class:`.EMESimulationData`]
from .yaml, .json, or .hdf5 file.
diff --git a/tidy3d/web/api/webapi.py b/tidy3d/web/api/webapi.py
index 7da3e9f7ca..d7d00180b1 100644
--- a/tidy3d/web/api/webapi.py
+++ b/tidy3d/web/api/webapi.py
@@ -6,7 +6,8 @@
import os
import tempfile
import time
-from typing import Callable, Literal, Optional, Union
+from collections.abc import Callable
+from typing import Literal
from requests import HTTPError
from rich.progress import BarColumn, Progress, TaskProgressColumn, TextColumn, TimeElapsedColumn
@@ -95,7 +96,7 @@ def _batch_detail(resource_id: str):
return BatchTask(resource_id).detail(batch_type="RF_SWEEP")
-def _batch_detail_error(resource_id: str) -> Optional[WebError]:
+def _batch_detail_error(resource_id: str) -> WebError | None:
"""Processes a failed batch job to generate a detailed error.
This function inspects the status of a batch detail object. If the status
@@ -156,7 +157,7 @@ def _batch_detail_error(resource_id: str) -> Optional[WebError]:
def _upload_component_modeler_subtasks(
- resource_id: str, verbose: bool = True, solver_version: Optional[str] = None
+ resource_id: str, verbose: bool = True, solver_version: str | None = None
):
"""Kicks off and monitors the split and validation of component modeler tasks.
@@ -319,20 +320,20 @@ def _task_dict_to_url_bullet_list(data_dict: dict) -> str:
@wait_for_connection
def run(
simulation: WorkflowType,
- task_name: Optional[str] = None,
+ task_name: str | None = None,
folder_name: str = "default",
path: str = "simulation_data.hdf5",
- callback_url: Optional[str] = None,
+ callback_url: str | None = None,
verbose: bool = True,
- progress_callback_upload: Optional[Callable[[float], None]] = None,
- progress_callback_download: Optional[Callable[[float], None]] = None,
- solver_version: Optional[str] = None,
- worker_group: Optional[str] = None,
+ progress_callback_upload: Callable[[float], None] | None = None,
+ progress_callback_download: Callable[[float], None] | None = None,
+ solver_version: str | None = None,
+ worker_group: str | None = None,
simulation_type: str = "tidy3d",
- parent_tasks: Optional[list[str]] = None,
+ parent_tasks: list[str] | None = None,
reduce_simulation: Literal["auto", True, False] = "auto",
- pay_type: Union[PayType, str] = PayType.AUTO,
- priority: Optional[int] = None,
+ pay_type: PayType | str = PayType.AUTO,
+ priority: int | None = None,
lazy: bool = False,
) -> WorkflowDataType:
"""
@@ -455,15 +456,15 @@ def run(
@wait_for_connection
def upload(
simulation: WorkflowType,
- task_name: Optional[str] = None,
+ task_name: str | None = None,
folder_name: str = "default",
- callback_url: Optional[str] = None,
+ callback_url: str | None = None,
verbose: bool = True,
- progress_callback: Optional[Callable[[float], None]] = None,
+ progress_callback: Callable[[float], None] | None = None,
simulation_type: str = "tidy3d",
- parent_tasks: Optional[list[str]] = None,
+ parent_tasks: list[str] | None = None,
source_required: bool = True,
- solver_version: Optional[str] = None,
+ solver_version: str | None = None,
reduce_simulation: Literal["auto", True, False] = "auto",
) -> TaskId:
"""
@@ -696,10 +697,10 @@ def get_info(task_id: TaskId, verbose: bool = True) -> TaskInfo | BatchDetail:
def start(
task_id: TaskId,
verbose: bool = True,
- solver_version: Optional[str] = None,
- worker_group: Optional[str] = None,
- pay_type: Union[PayType, str] = PayType.AUTO,
- priority: Optional[int] = None,
+ solver_version: str | None = None,
+ worker_group: str | None = None,
+ pay_type: PayType | str = PayType.AUTO,
+ priority: int | None = None,
) -> None:
"""Start running the simulation associated with task.
@@ -759,7 +760,7 @@ def start(
@wait_for_connection
-def get_run_info(task_id: TaskId) -> tuple[Optional[float], Optional[float]]:
+def get_run_info(task_id: TaskId) -> tuple[float | None, float | None]:
"""Gets the % done and field_decay for a running task.
Parameters
@@ -826,7 +827,7 @@ def get_status(task_id) -> str:
return status
-def monitor(task_id: TaskId, verbose: bool = True, worker_group: Optional[str] = None) -> None:
+def monitor(task_id: TaskId, verbose: bool = True, worker_group: str | None = None) -> None:
"""
Print the real time task progress until completion.
@@ -1043,7 +1044,7 @@ def download(
task_id: TaskId,
path: str = "simulation_data.hdf5",
verbose: bool = True,
- progress_callback: Optional[Callable[[float], None]] = None,
+ progress_callback: Callable[[float], None] | None = None,
) -> None:
"""Download results of task to file.
@@ -1173,7 +1174,7 @@ def download_log(
task_id: TaskId,
path: str = "tidy3d.log",
verbose: bool = True,
- progress_callback: Optional[Callable[[float], None]] = None,
+ progress_callback: Callable[[float], None] | None = None,
) -> None:
"""Download the tidy3d log file associated with a task.
@@ -1202,7 +1203,7 @@ def load(
path: str = "simulation_data.hdf5",
replace_existing: bool = True,
verbose: bool = True,
- progress_callback: Optional[Callable[[float], None]] = None,
+ progress_callback: Callable[[float], None] | None = None,
lazy: bool = False,
) -> WorkflowDataType:
"""
@@ -1268,7 +1269,7 @@ def _monitor_modeler_batch(
batch_id: str,
verbose: bool = True,
max_detail_tasks: int = 20,
- worker_group: Optional[str] = None,
+ worker_group: str | None = None,
) -> None:
"""Monitor modeler batch progress with aggregate and per-task views."""
console = get_logging_console() if verbose else None
@@ -1461,7 +1462,7 @@ def download_simulation(
task_id: TaskId,
path: str = SIM_FILE_HDF5,
verbose: bool = True,
- progress_callback: Optional[Callable[[float], None]] = None,
+ progress_callback: Callable[[float], None] | None = None,
) -> None:
"""Download the ``.hdf5`` file associated with the :class:`.Simulation` of a given task.
@@ -1492,7 +1493,7 @@ def download_simulation(
@wait_for_connection
def get_tasks(
- num_tasks: Optional[int] = None, order: Literal["new", "old"] = "new", folder: str = "default"
+ num_tasks: int | None = None, order: Literal["new", "old"] = "new", folder: str = "default"
) -> list[dict]:
"""Get a list with the metadata of the last ``num_tasks`` tasks.
@@ -1524,9 +1525,7 @@ def get_tasks(
@wait_for_connection
-def estimate_cost(
- task_id: str, verbose: bool = True, solver_version: Optional[str] = None
-) -> float:
+def estimate_cost(task_id: str, verbose: bool = True, solver_version: str | None = None) -> float:
"""Compute the maximum FlexCredit charge for a given task.
Parameters
@@ -1790,7 +1789,7 @@ def account(verbose=True) -> Account:
def postprocess_start(
batch_id: str,
verbose: bool = True,
- worker_group: Optional[str] = None,
+ worker_group: str | None = None,
) -> None:
"""
Checks if a batch run is complete and starts the postprocess phase.
diff --git a/tidy3d/web/cli/develop/documentation.py b/tidy3d/web/cli/develop/documentation.py
index 9da6926577..4520fb533e 100644
--- a/tidy3d/web/cli/develop/documentation.py
+++ b/tidy3d/web/cli/develop/documentation.py
@@ -17,7 +17,6 @@
import json
import os
-from typing import Optional
import click
@@ -299,7 +298,7 @@ def build_documentation_from_remote_notebooks(args=None):
help="Recursively find and replace strings in files based on a JSON configuration.",
)
def replace_in_files_command(
- directory: str, json_dictionary: Optional[str], selected_version: Optional[str], dry_run: bool
+ directory: str, json_dictionary: str | None, selected_version: str | None, dry_run: bool
):
"""
Recursively finds and replaces strings in files within a directory based on a given dictionary loaded from a JSON
diff --git a/tidy3d/web/core/account.py b/tidy3d/web/core/account.py
index 07cef4caaa..54a0f4e682 100644
--- a/tidy3d/web/core/account.py
+++ b/tidy3d/web/core/account.py
@@ -3,7 +3,6 @@
from __future__ import annotations
from datetime import datetime
-from typing import Optional
from pydantic.v1 import Extra, Field
@@ -14,34 +13,34 @@
class Account(Tidy3DResource, extra=Extra.allow):
"""Tidy3D User Account."""
- allowance_cycle_type: Optional[str] = Field(
+ allowance_cycle_type: str | None = Field(
None,
title="AllowanceCycleType",
description="Daily or Monthly",
alias="allowanceCycleType",
)
- credit: Optional[float] = Field(
+ credit: float | None = Field(
0, title="credit", description="Current FlexCredit balance", alias="credit"
)
- credit_expiration: Optional[datetime] = Field(
+ credit_expiration: datetime | None = Field(
None,
title="creditExpiration",
description="Expiration date",
alias="creditExpiration",
)
- allowance_current_cycle_amount: Optional[float] = Field(
+ allowance_current_cycle_amount: float | None = Field(
0,
title="allowanceCurrentCycleAmount",
description="Daily/Monthly free simulation balance",
alias="allowanceCurrentCycleAmount",
)
- allowance_current_cycle_end_date: Optional[datetime] = Field(
+ allowance_current_cycle_end_date: datetime | None = Field(
None,
title="allowanceCurrentCycleEndDate",
description="Daily/Monthly free simulation balance expiration date",
alias="allowanceCurrentCycleEndDate",
)
- daily_free_simulation_counts: Optional[int] = Field(
+ daily_free_simulation_counts: int | None = Field(
0,
title="dailyFreeSimulationCounts",
description="Daily free simulation counts",
diff --git a/tidy3d/web/core/exceptions.py b/tidy3d/web/core/exceptions.py
index 4061a8c6f8..fd53ea850e 100644
--- a/tidy3d/web/core/exceptions.py
+++ b/tidy3d/web/core/exceptions.py
@@ -2,15 +2,13 @@
from __future__ import annotations
-from typing import Optional
-
from .core_config import get_logger
class WebError(Exception):
"""Any error in tidy3d"""
- def __init__(self, message: Optional[str] = None):
+ def __init__(self, message: str | None = None):
"""Log just the error message and then raise the Exception."""
log = get_logger()
super().__init__(message)
diff --git a/tidy3d/web/core/s3utils.py b/tidy3d/web/core/s3utils.py
index cb87a69a22..4f172cec24 100644
--- a/tidy3d/web/core/s3utils.py
+++ b/tidy3d/web/core/s3utils.py
@@ -6,10 +6,9 @@
import pathlib
import tempfile
import urllib
-from collections.abc import Mapping
+from collections.abc import Callable, Mapping
from datetime import datetime
from enum import Enum
-from typing import Callable, Optional
import boto3
from boto3.s3.transfer import TransferConfig
@@ -184,7 +183,7 @@ def _get_progress(action: _S3Action):
def get_s3_sts_token(
- resource_id: str, file_name: str, extra_arguments: Optional[Mapping[str, str]] = None
+ resource_id: str, file_name: str, extra_arguments: Mapping[str, str] | None = None
) -> _S3STSToken:
"""Get s3 sts token for the given resource id and file name.
@@ -218,8 +217,8 @@ def upload_file(
path: str,
remote_filename: str,
verbose: bool = True,
- progress_callback: Optional[Callable[[float], None]] = None,
- extra_arguments: Optional[Mapping[str, str]] = None,
+ progress_callback: Callable[[float], None] | None = None,
+ extra_arguments: Mapping[str, str] | None = None,
):
"""Upload a file to S3.
@@ -284,9 +283,9 @@ def _callback(bytes_in_chunk):
def download_file(
resource_id: str,
remote_filename: str,
- to_file: Optional[str] = None,
+ to_file: str | None = None,
verbose: bool = True,
- progress_callback: Optional[Callable[[float], None]] = None,
+ progress_callback: Callable[[float], None] | None = None,
) -> pathlib.Path:
"""Download file from S3.
@@ -374,9 +373,9 @@ def _callback(bytes_in_chunk):
def download_gz_file(
resource_id: str,
remote_filename: str,
- to_file: Optional[str] = None,
+ to_file: str | None = None,
verbose: bool = True,
- progress_callback: Optional[Callable[[float], None]] = None,
+ progress_callback: Callable[[float], None] | None = None,
) -> pathlib.Path:
"""Download a ``.gz`` file and unzip it into ``to_file``, unless ``to_file`` itself
ends in .gz
diff --git a/tidy3d/web/core/task_core.py b/tidy3d/web/core/task_core.py
index 619ff4bc8d..3d86c1d602 100644
--- a/tidy3d/web/core/task_core.py
+++ b/tidy3d/web/core/task_core.py
@@ -6,8 +6,8 @@
import pathlib
import tempfile
import time
+from collections.abc import Callable
from datetime import datetime
-from typing import Callable, Optional, Union
from botocore.exceptions import ClientError
from pydantic.v1 import Extra, Field, parse_obj_as
@@ -146,33 +146,33 @@ def list_tasks(self, projects_endpoint: str = "tidy3d/projects") -> list[Tidy3DR
class SimulationTask(ResourceLifecycle, Submittable, extra=Extra.allow):
"""Interface for managing the running of a :class:`.Simulation` task on server."""
- task_id: Optional[str] = Field(
+ task_id: str | None = Field(
...,
title="task_id",
description="Task ID number, set when the task is uploaded, leave as None.",
alias="taskId",
)
- folder_id: Optional[str] = Field(
+ folder_id: str | None = Field(
None,
title="folder_id",
description="Folder ID number, set when the task is uploaded, leave as None.",
alias="folderId",
)
- status: Optional[str] = Field(title="status", description="Simulation task status.")
+ status: str | None = Field(title="status", description="Simulation task status.")
real_flex_unit: float = Field(
None, title="real FlexCredits", description="Billed FlexCredits.", alias="realCost"
)
- created_at: Optional[datetime] = Field(
+ created_at: datetime | None = Field(
title="created_at", description="Time at which this task was created.", alias="createdAt"
)
- task_type: Optional[str] = Field(
+ task_type: str | None = Field(
title="task_type", description="The type of task.", alias="taskType"
)
- folder_name: Optional[str] = Field(
+ folder_name: str | None = Field(
"default",
title="Folder Name",
description="Name of the folder associated with this task.",
@@ -205,11 +205,11 @@ def create(
task_type: str,
task_name: str,
folder_name: str = "default",
- callback_url: Optional[str] = None,
+ callback_url: str | None = None,
simulation_type: str = "tidy3d",
- parent_tasks: Optional[list[str]] = None,
+ parent_tasks: list[str] | None = None,
file_type: str = "Gz",
- port_name_list: Optional[list[str]] = None,
+ port_name_list: list[str] | None = None,
projects_endpoint: str = "tidy3d/projects",
) -> SimulationTask:
"""Create a new task on the server.
@@ -358,7 +358,7 @@ def upload_simulation(
self,
stub: TaskStub,
verbose: bool = True,
- progress_callback: Optional[Callable[[float], None]] = None,
+ progress_callback: Callable[[float], None] | None = None,
remote_sim_file: str = SIM_FILE_HDF5_GZ,
) -> None:
"""Upload :class:`.Simulation` object to Server.
@@ -398,7 +398,7 @@ def upload_file(
local_file: str,
remote_filename: str,
verbose: bool = True,
- progress_callback: Optional[Callable[[float], None]] = None,
+ progress_callback: Callable[[float], None] | None = None,
) -> None:
"""
Upload file to platform. Using this method when the json file is too large to parse
@@ -427,10 +427,10 @@ def upload_file(
def submit(
self,
- solver_version: Optional[str] = None,
- worker_group: Optional[str] = None,
- pay_type: Union[PayType, str] = PayType.AUTO,
- priority: Optional[int] = None,
+ solver_version: str | None = None,
+ worker_group: str | None = None,
+ pay_type: PayType | str = PayType.AUTO,
+ priority: int | None = None,
):
"""Kick off this task.
@@ -504,7 +504,7 @@ def get_sim_data_hdf5(
self,
to_file: str,
verbose: bool = True,
- progress_callback: Optional[Callable[[float], None]] = None,
+ progress_callback: Callable[[float], None] | None = None,
remote_data_file: str = SIMULATION_DATA_HDF5_GZ,
) -> pathlib.Path:
"""Get simulation data file from Server.
@@ -561,7 +561,7 @@ def get_simulation_hdf5(
self,
to_file: str,
verbose: bool = True,
- progress_callback: Optional[Callable[[float], None]] = None,
+ progress_callback: Callable[[float], None] | None = None,
remote_sim_file: str = SIM_FILE_HDF5_GZ,
) -> pathlib.Path:
"""Get simulation.hdf5 file from Server.
@@ -615,7 +615,7 @@ def get_log(
self,
to_file: str,
verbose: bool = True,
- progress_callback: Optional[Callable[[float], None]] = None,
+ progress_callback: Callable[[float], None] | None = None,
) -> pathlib.Path:
"""Get log file from Server.
@@ -678,7 +678,7 @@ def abort(self):
"tidy3d/tasks/abort", json={"taskType": self.task_type, "taskId": self.task_id}
)
- def validate_post_upload(self, parent_tasks: Optional[list[str]] = None):
+ def validate_post_upload(self, parent_tasks: list[str] | None = None):
"""Perform checks after task is uploaded and metadata is processed."""
if self.task_type == "HEAT_CHARGE" and parent_tasks:
try:
@@ -780,8 +780,8 @@ def detail(self, batch_type: str) -> BatchDetail:
def check(
self,
- solver_version: Optional[str] = None,
- protocol_version: Optional[str] = None,
+ solver_version: str | None = None,
+ protocol_version: str | None = None,
batch_type: str = "",
):
"""Submits a request to validate the batch configuration on the server.
@@ -813,9 +813,9 @@ def check(
def submit(
self,
- solver_version: Optional[str] = None,
- protocol_version: Optional[str] = None,
- worker_group: Optional[str] = None,
+ solver_version: str | None = None,
+ protocol_version: str | None = None,
+ worker_group: str | None = None,
batch_type: str = "",
):
"""Submits the batch for execution on the server.
@@ -850,9 +850,9 @@ def submit(
def postprocess(
self,
- solver_version: Optional[str] = None,
- protocol_version: Optional[str] = None,
- worker_group: Optional[str] = None,
+ solver_version: str | None = None,
+ protocol_version: str | None = None,
+ worker_group: str | None = None,
batch_type: str = "",
):
"""Initiates post-processing for a completed batch run.
@@ -885,9 +885,7 @@ def postprocess(
},
)
- def wait_for_validate(
- self, timeout: Optional[float] = None, batch_type: str = ""
- ) -> BatchDetail:
+ def wait_for_validate(self, timeout: float | None = None, batch_type: str = "") -> BatchDetail:
"""Waits for the batch to complete the validation stage by polling its status.
Parameters
@@ -920,7 +918,7 @@ def wait_for_validate(
return d
time.sleep(REFRESH_TIME)
- def wait_for_run(self, timeout: Optional[float] = None, batch_type: str = "") -> BatchDetail:
+ def wait_for_run(self, timeout: float | None = None, batch_type: str = "") -> BatchDetail:
"""Waits for the batch to complete the execution stage by polling its status.
Parameters
@@ -963,7 +961,7 @@ def get_data_hdf5(
remote_data_file_gz: str,
to_file: str,
verbose: bool = True,
- progress_callback: Optional[Callable[[float], None]] = None,
+ progress_callback: Callable[[float], None] | None = None,
) -> pathlib.Path:
"""Downloads a batch data artifact, with a fallback mechanism.
diff --git a/tidy3d/web/core/task_info.py b/tidy3d/web/core/task_info.py
index 93a68fb4ce..53b5290af1 100644
--- a/tidy3d/web/core/task_info.py
+++ b/tidy3d/web/core/task_info.py
@@ -5,7 +5,6 @@
from abc import ABC
from datetime import datetime
from enum import Enum
-from typing import Optional
import pydantic.v1 as pydantic
@@ -87,7 +86,7 @@ class TaskInfo(TaskBase):
nodeSize: int = None
"""Size of the node allocated for the task."""
- completedAt: Optional[datetime] = None
+ completedAt: datetime | None = None
"""Timestamp when the task was completed."""
status: str = None
@@ -102,7 +101,7 @@ class TaskInfo(TaskBase):
solverVersion: str = None
"""Version of the solver used for the task."""
- createAt: Optional[datetime] = None
+ createAt: datetime | None = None
"""Timestamp when the task was created."""
estCostMin: float = None
@@ -132,10 +131,10 @@ class TaskInfo(TaskBase):
s3Storage: float = None
"""Amount of S3 storage used by the task."""
- startSolverTime: Optional[datetime] = None
+ startSolverTime: datetime | None = None
"""Timestamp when the solver started."""
- finishSolverTime: Optional[datetime] = None
+ finishSolverTime: datetime | None = None
"""Timestamp when the solver finished."""
totalSolverTime: int = None
@@ -263,8 +262,8 @@ class BatchMember(TaskBase):
replaceData: str = None
protocolVersion: str = None
variable: str = None
- createdAt: Optional[datetime] = None
- updatedAt: Optional[datetime] = None
+ createdAt: datetime | None = None
+ updatedAt: datetime | None = None
denormalizeStatus: str = None
summary: dict = None
@@ -343,13 +342,13 @@ class AsyncJobDetail(TaskBase):
asyncId: str
status: str
- progress: Optional[float] = None
- createdAt: Optional[datetime] = None
- completedAt: Optional[datetime] = None
- tasks: Optional[dict[str, str]] = None
- result: Optional[str] = None
- taskBlockInfo: Optional[TaskBlockInfo] = None
- message: Optional[str] = None
+ progress: float | None = None
+ createdAt: datetime | None = None
+ completedAt: datetime | None = None
+ tasks: dict[str, str] | None = None
+ result: str | None = None
+ taskBlockInfo: TaskBlockInfo | None = None
+ message: str | None = None
AsyncJobDetail.update_forward_refs()