Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Added support for `tidy3d-extras`, an optional plugin that enables more accurate local mode solving via subpixel averaging.
- Added support for `symlog` and `log` scale plotting in `Scene.plot_eps()` and `Scene.plot_structures_property()` methods. The `symlog` scale provides linear behavior near zero and logarithmic behavior elsewhere, while 'log' is a base 10 logarithmic scale.
- Added `tidy3d-microwave` package for enabling advanced microwave device simulation features.
- Introduced `BroadbandPulse` for exciting simulations across a wide frequency spectrum.

### Changed
- Improved performance of antenna metrics calculation by utilizing cached wave amplitude calculations instead of recomputing wave amplitudes for each port excitation in the `TerminalComponentModelerData`.
Expand Down
1 change: 1 addition & 0 deletions docs/api/sources.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Source Time Dependence

tidy3d.GaussianPulse
tidy3d.ContinuousWave
tidy3d.BroadbandPulse
tidy3d.SourceTime
tidy3d.CustomSourceTime

Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ devsim = { version = "*", optional = true }
cma = { version = "*", optional = true }
openpyxl = { version = "*", optional = true }
# tidy3d-extras = { version = "2.10.0rc3", optional = true }
# tidy3d-microwave = { version = "2.10.0rc3", optional = true }

[tool.poetry.extras]
dev = [
Expand Down Expand Up @@ -212,6 +213,7 @@ heatcharge = ["trimesh", "vtk", "devsim"]
pytorch = ["torch"]
design = ["bayesian-optimization", "pygad", "pyswarms"]
# extras = ["tidy3d-extras"]
# microwave = ["tidy3d-microwave"]

[tool.poetry.scripts]
tidy3d = "tidy3d.web.cli:tidy3d_cli"
Expand Down
21 changes: 21 additions & 0 deletions tests/test_components/test_packaging.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
Tidy3dImportError,
check_import,
supports_local_subpixel,
supports_microwave,
tidy3d_extras,
tidy3d_microwave,
verify_packages_import,
)

Expand Down Expand Up @@ -90,5 +92,24 @@ def get_eps():
get_eps()


def test_tidy3d_microwave():
import importlib

has_tidy3d_microwave = importlib.util.find_spec("tidy3d_microwave") is not None
print(f"has_tidy3d_microwave = {has_tidy3d_microwave}")

if has_tidy3d_microwave:

@supports_microwave
def get_pulse():
assert tidy3d_microwave["mod"] is not None
pulse = tidy3d_microwave["mod"].BroadbandPulse(fmin=0.1, fmax=10)
_ = pulse.frequency_range(2)

get_pulse()
else:
assert tidy3d_microwave["mod"] is None
Comment on lines +95 to +111
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Test only covers the success path. Consider adding a test case that verifies Tidy3dImportError is raised when @supports_microwave decorated functions are called without the package installed, similar to other tests in this file.

Context Used: Rule from dashboard - Add tests for all public constructors and methods to confirm expected behavior, including effects on... (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: tests/test_components/test_packaging.py
Line: 95:111

Comment:
**style:** Test only covers the success path. Consider adding a test case that verifies `Tidy3dImportError` is raised when `@supports_microwave` decorated functions are called without the package installed, similar to other tests in this file.

**Context Used:** Rule from `dashboard` - Add tests for all public constructors and methods to confirm expected behavior, including effects on... ([source](https://app.greptile.com/review/custom-context?memory=40c78813-75a7-47ea-a080-1509e6e686a9))

How can I resolve this? If you propose a fix, please make it concise.



if __name__ == "__main__":
pytest.main()
2 changes: 2 additions & 0 deletions tidy3d/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@

# sources
from .components.source.time import (
BroadbandPulse,
ContinuousWave,
CustomSourceTime,
GaussianPulse,
Expand Down Expand Up @@ -466,6 +467,7 @@ def set_logging_level(level: str) -> None:
"Box",
"BroadbandModeABCFitterParam",
"BroadbandModeABCSpec",
"BroadbandPulse",
"CaugheyThomasMobility",
"CellDataArray",
"ChargeConductorMedium",
Expand Down
66 changes: 65 additions & 1 deletion tidy3d/components/source/time.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from tidy3d.constants import HERTZ
from tidy3d.exceptions import ValidationError
from tidy3d.log import log
from tidy3d.packaging import supports_microwave, tidy3d_microwave

# how many units of ``twidth`` from the ``offset`` until a gaussian pulse is considered "off"
END_TIME_FACTOR_GAUSSIAN = 10
Expand Down Expand Up @@ -605,4 +606,67 @@ def end_time(self) -> Optional[float]:
return np.max(t_non_zero)


SourceTimeType = Union[GaussianPulse, ContinuousWave, CustomSourceTime]
class BroadbandPulse(SourceTime):
"""A source time injecting significant energy in the entire custom frequency range."""

freq_range: FreqBound = pydantic.Field(
...,
title="Frequency Range",
description="Frequency range where the pulse should have significant energy.",
units=HERTZ,
)
minimum_amplitude: float = pydantic.Field(
0.3,
title="Minimum Amplitude",
description="Minimum amplitude of the pulse relative to the peak amplitude in the frequency range.",
gt=0.05,
lt=0.5,
)
offset: float = pydantic.Field(
5.0,
title="Offset",
description="Time delay of the maximum value of the "
"pulse in units of 1 / (``2pi * fwidth``).",
ge=2.5,
)

@cached_property
@supports_microwave
def _source(self):
"""Implementation of broadband pulse."""
return tidy3d_microwave["mod"].BroadbandPulse(
fmin=self.freq_range[0],
fmax=self.freq_range[1],
minRelAmp=self.minimum_amplitude,
amp=self.amplitude,
phase=self.phase,
offset=self.offset,
)

def end_time(self) -> float:
"""Time after which the source is effectively turned off / close to zero amplitude."""
return self._source.end_time(END_TIME_FACTOR_GAUSSIAN)

def amp_time(self, time: float) -> complex:
"""Complex-valued source amplitude as a function of time."""
return self._source.amp_time(time)

def amp_freq(self, freq: float) -> complex:
"""Complex-valued source amplitude as a function of frequency."""
return self._source.amp_freq(freq)

def frequency_range_sigma(self, sigma: float = DEFAULT_SIGMA) -> FreqBound:
"""Frequency range where the source amplitude is within ``exp(-sigma**2/2)`` of the peak amplitude."""
return self._source.frequency_range(sigma)

def frequency_range(self, num_fwidth: float = DEFAULT_SIGMA) -> FreqBound:
"""Frequency range where the source amplitude is within ``exp(-sigma**2/2)`` of the peak amplitude."""
return self.frequency_range_sigma(num_fwidth)

@cached_property
def _freq0(self) -> float:
"""Central frequency from frequency range."""
return np.mean(self.freq_range)


SourceTimeType = Union[GaussianPulse, ContinuousWave, CustomSourceTime, BroadbandPulse]
46 changes: 46 additions & 0 deletions tidy3d/packaging.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

tidy3d_extras = {"mod": None, "use_local_subpixel": None}

tidy3d_microwave = {"mod": None}


def check_import(module_name: str) -> bool:
"""
Expand Down Expand Up @@ -249,3 +251,47 @@ def _fn(*args, **kwargs):
return result

return _fn


def supports_microwave(fn):
"""When decorating a method, checks that 'tidy3d-microwave' is available."""

@functools.wraps(fn)
def _fn(*args, **kwargs):
# first try to import the module
if tidy3d_microwave["mod"] is None:
try:
import tidy3d_microwave as tidy3d_microwave_mod

except ImportError as exc:
tidy3d_microwave["mod"] = None
raise Tidy3dImportError(
"The package 'tidy3d-microwave' is required for this "
"operation. "
"Please install the 'tidy3d-microwave' package using, for "
"example, 'pip install tidy3d[microwave]'."
) from exc

else:
version = tidy3d_microwave_mod.__version__

if version is None:
tidy3d_microwave["mod"] = None
raise Tidy3dImportError(
"The package 'tidy3d-microwave' did not initialize correctly, "
"likely due to an invalid API key."
)

if version != __version__:
log.warning(
"The package 'tidy3d-microwave' is required for this "
"operation. The version of 'tidy3d-microwave' should match "
"the version of 'tidy3d'. You can install the correct "
"version using 'pip install tidy3d[microwave]'."
)

tidy3d_microwave["mod"] = tidy3d_microwave_mod

return fn(*args, **kwargs)

return _fn