From 84a91a57315faab7e08fafd9c643c71e7ac128e8 Mon Sep 17 00:00:00 2001 From: Weiliang Jin Date: Thu, 9 Oct 2025 16:18:00 -0700 Subject: [PATCH 1/3] feat(microwave): tidy3d_microwave and BroadbandPulse feature --- CHANGELOG.md | 2 + docs/api/sources.rst | 1 + pyproject.toml | 2 + tests/test_components/test_packaging.py | 21 ++++++++ tidy3d/__init__.py | 2 + tidy3d/components/source/time.py | 66 ++++++++++++++++++++++++- tidy3d/packaging.py | 46 +++++++++++++++++ 7 files changed, 139 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5094fd2f58..de8fbc39e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 support for `tidy3d-microwave`, a plugin enabling more microwave simulation features. +- `BroadbandPulse` for injecting significant energy over entire the frequency range of interest. ### 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`. diff --git a/docs/api/sources.rst b/docs/api/sources.rst index 4119e93dc6..b5cc48cdba 100644 --- a/docs/api/sources.rst +++ b/docs/api/sources.rst @@ -34,6 +34,7 @@ Source Time Dependence tidy3d.GaussianPulse tidy3d.ContinuousWave + tidy3d.BroadbandPulse tidy3d.SourceTime tidy3d.CustomSourceTime diff --git a/pyproject.toml b/pyproject.toml index 242ad0003e..d30bc73cba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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 = [ @@ -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" diff --git a/tests/test_components/test_packaging.py b/tests/test_components/test_packaging.py index d51ad23c2b..ac8b12d7fc 100644 --- a/tests/test_components/test_packaging.py +++ b/tests/test_components/test_packaging.py @@ -6,7 +6,9 @@ Tidy3dImportError, check_import, supports_local_subpixel, + supports_microwave, tidy3d_extras, + tidy3d_microwave, verify_packages_import, ) @@ -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 + + if __name__ == "__main__": pytest.main() diff --git a/tidy3d/__init__.py b/tidy3d/__init__.py index 8741cec4fc..629de2f0fb 100644 --- a/tidy3d/__init__.py +++ b/tidy3d/__init__.py @@ -366,6 +366,7 @@ # sources from .components.source.time import ( + BroadbandPulse, ContinuousWave, CustomSourceTime, GaussianPulse, @@ -466,6 +467,7 @@ def set_logging_level(level: str) -> None: "Box", "BroadbandModeABCFitterParam", "BroadbandModeABCSpec", + "BroadbandPulse", "CaugheyThomasMobility", "CellDataArray", "ChargeConductorMedium", diff --git a/tidy3d/components/source/time.py b/tidy3d/components/source/time.py index f2532ea3d7..9f4ad3b915 100644 --- a/tidy3d/components/source/time.py +++ b/tidy3d/components/source/time.py @@ -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 @@ -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] diff --git a/tidy3d/packaging.py b/tidy3d/packaging.py index 930ff36524..624816b085 100644 --- a/tidy3d/packaging.py +++ b/tidy3d/packaging.py @@ -27,6 +27,8 @@ tidy3d_extras = {"mod": None, "use_local_subpixel": None} +tidy3d_microwave = {"mod": None} + def check_import(module_name: str) -> bool: """ @@ -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 From ff66829635a57add7e5b2440965b99adb49c5365 Mon Sep 17 00:00:00 2001 From: Weiliang Jin Date: Thu, 9 Oct 2025 16:31:34 -0700 Subject: [PATCH 2/3] update changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de8fbc39e4..54e50ce525 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,8 +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 support for `tidy3d-microwave`, a plugin enabling more microwave simulation features. -- `BroadbandPulse` for injecting significant energy over entire the frequency range of interest. +- 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`. From 5bab42c13bfbe59e8c6ca50d2905e25b56ba8d3c Mon Sep 17 00:00:00 2001 From: Weiliang Jin Date: Thu, 9 Oct 2025 16:32:29 -0700 Subject: [PATCH 3/3] fix pyproject --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d30bc73cba..f2bfc99cde 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -213,7 +213,7 @@ heatcharge = ["trimesh", "vtk", "devsim"] pytorch = ["torch"] design = ["bayesian-optimization", "pygad", "pyswarms"] # extras = ["tidy3d-extras"] -microwave = ["tidy3d-microwave"] +# microwave = ["tidy3d-microwave"] [tool.poetry.scripts] tidy3d = "tidy3d.web.cli:tidy3d_cli"