diff --git a/CHANGELOG.md b/CHANGELOG.md index d0aefe85bc..6767dfbab1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added support for argument passing to DRC file when running checks with `DRCRunner.run(..., drc_args={key: value})` in klayout plugin. - Added support for `nonlinear_spec` in `CustomMedium` and `CustomDispersiveMedium`. - `tidy3d.plugins.design.DesignSpace.run(..., fn_post=...)` now accepts a `priority` keyword to propagate vGPU queue priority to all automatically batched simulations. +- Introduced `BroadbandPulse` for exciting simulations across a wide frequency spectrum. ### Breaking Changes - Edge singularity correction at PEC and lossy metal edges defaults to `True`. diff --git a/docs/api/sources.rst b/docs/api/sources.rst index c9cd9a54c2..c5924a4b0c 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/schemas/ModeSimulation.json b/schemas/ModeSimulation.json index 7ba56f2805..a6a28125d6 100644 --- a/schemas/ModeSimulation.json +++ b/schemas/ModeSimulation.json @@ -1504,6 +1504,58 @@ ], "type": "object" }, + "BroadbandPulse": { + "additionalProperties": false, + "properties": { + "amplitude": { + "default": 1.0, + "minimum": 0, + "type": "number" + }, + "attrs": { + "default": {}, + "type": "object" + }, + "freq_range": { + "items": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "maxItems": 2, + "minItems": 2, + "type": "array" + }, + "minimum_amplitude": { + "default": 0.3, + "exclusiveMaximum": 0.5, + "exclusiveMinimum": 0.05, + "type": "number" + }, + "offset": { + "default": 0.0, + "type": "number" + }, + "phase": { + "default": 0.0, + "type": "number" + }, + "type": { + "default": "BroadbandPulse", + "enum": [ + "BroadbandPulse" + ], + "type": "string" + } + }, + "required": [ + "freq_range" + ], + "type": "object" + }, "CaugheyThomasMobility": { "additionalProperties": false, "properties": { @@ -8092,6 +8144,7 @@ "source_time": { "discriminator": { "mapping": { + "BroadbandPulse": "#/definitions/BroadbandPulse", "ContinuousWave": "#/definitions/ContinuousWave", "CustomSourceTime": "#/definitions/CustomSourceTime", "GaussianPulse": "#/definitions/GaussianPulse" @@ -8099,6 +8152,9 @@ "propertyName": "type" }, "oneOf": [ + { + "$ref": "#/definitions/BroadbandPulse" + }, { "$ref": "#/definitions/ContinuousWave" }, diff --git a/schemas/Simulation.json b/schemas/Simulation.json index cfc425c478..de54588e30 100644 --- a/schemas/Simulation.json +++ b/schemas/Simulation.json @@ -748,6 +748,7 @@ "source_time": { "discriminator": { "mapping": { + "BroadbandPulse": "#/definitions/BroadbandPulse", "ContinuousWave": "#/definitions/ContinuousWave", "CustomSourceTime": "#/definitions/CustomSourceTime", "GaussianPulse": "#/definitions/GaussianPulse" @@ -755,6 +756,9 @@ "propertyName": "type" }, "oneOf": [ + { + "$ref": "#/definitions/BroadbandPulse" + }, { "$ref": "#/definitions/ContinuousWave" }, @@ -1881,6 +1885,58 @@ ], "type": "object" }, + "BroadbandPulse": { + "additionalProperties": false, + "properties": { + "amplitude": { + "default": 1.0, + "minimum": 0, + "type": "number" + }, + "attrs": { + "default": {}, + "type": "object" + }, + "freq_range": { + "items": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "maxItems": 2, + "minItems": 2, + "type": "array" + }, + "minimum_amplitude": { + "default": 0.3, + "exclusiveMaximum": 0.5, + "exclusiveMinimum": 0.05, + "type": "number" + }, + "offset": { + "default": 0.0, + "type": "number" + }, + "phase": { + "default": 0.0, + "type": "number" + }, + "type": { + "default": "BroadbandPulse", + "enum": [ + "BroadbandPulse" + ], + "type": "string" + } + }, + "required": [ + "freq_range" + ], + "type": "object" + }, "CaugheyThomasMobility": { "additionalProperties": false, "properties": { @@ -3244,6 +3300,7 @@ "source_time": { "discriminator": { "mapping": { + "BroadbandPulse": "#/definitions/BroadbandPulse", "ContinuousWave": "#/definitions/ContinuousWave", "CustomSourceTime": "#/definitions/CustomSourceTime", "GaussianPulse": "#/definitions/GaussianPulse" @@ -3251,6 +3308,9 @@ "propertyName": "type" }, "oneOf": [ + { + "$ref": "#/definitions/BroadbandPulse" + }, { "$ref": "#/definitions/ContinuousWave" }, @@ -3899,6 +3959,7 @@ "source_time": { "discriminator": { "mapping": { + "BroadbandPulse": "#/definitions/BroadbandPulse", "ContinuousWave": "#/definitions/ContinuousWave", "CustomSourceTime": "#/definitions/CustomSourceTime", "GaussianPulse": "#/definitions/GaussianPulse" @@ -3906,6 +3967,9 @@ "propertyName": "type" }, "oneOf": [ + { + "$ref": "#/definitions/BroadbandPulse" + }, { "$ref": "#/definitions/ContinuousWave" }, @@ -8118,6 +8182,7 @@ "source_time": { "discriminator": { "mapping": { + "BroadbandPulse": "#/definitions/BroadbandPulse", "ContinuousWave": "#/definitions/ContinuousWave", "CustomSourceTime": "#/definitions/CustomSourceTime", "GaussianPulse": "#/definitions/GaussianPulse" @@ -8125,6 +8190,9 @@ "propertyName": "type" }, "oneOf": [ + { + "$ref": "#/definitions/BroadbandPulse" + }, { "$ref": "#/definitions/ContinuousWave" }, @@ -12058,6 +12126,7 @@ "source_time": { "discriminator": { "mapping": { + "BroadbandPulse": "#/definitions/BroadbandPulse", "ContinuousWave": "#/definitions/ContinuousWave", "CustomSourceTime": "#/definitions/CustomSourceTime", "GaussianPulse": "#/definitions/GaussianPulse" @@ -12065,6 +12134,9 @@ "propertyName": "type" }, "oneOf": [ + { + "$ref": "#/definitions/BroadbandPulse" + }, { "$ref": "#/definitions/ContinuousWave" }, @@ -13711,6 +13783,7 @@ "source_time": { "discriminator": { "mapping": { + "BroadbandPulse": "#/definitions/BroadbandPulse", "ContinuousWave": "#/definitions/ContinuousWave", "CustomSourceTime": "#/definitions/CustomSourceTime", "GaussianPulse": "#/definitions/GaussianPulse" @@ -13718,6 +13791,9 @@ "propertyName": "type" }, "oneOf": [ + { + "$ref": "#/definitions/BroadbandPulse" + }, { "$ref": "#/definitions/ContinuousWave" }, @@ -13855,6 +13931,7 @@ "source_time": { "discriminator": { "mapping": { + "BroadbandPulse": "#/definitions/BroadbandPulse", "ContinuousWave": "#/definitions/ContinuousWave", "CustomSourceTime": "#/definitions/CustomSourceTime", "GaussianPulse": "#/definitions/GaussianPulse" @@ -13862,6 +13939,9 @@ "propertyName": "type" }, "oneOf": [ + { + "$ref": "#/definitions/BroadbandPulse" + }, { "$ref": "#/definitions/ContinuousWave" }, @@ -15763,6 +15843,7 @@ "source_time": { "discriminator": { "mapping": { + "BroadbandPulse": "#/definitions/BroadbandPulse", "ContinuousWave": "#/definitions/ContinuousWave", "CustomSourceTime": "#/definitions/CustomSourceTime", "GaussianPulse": "#/definitions/GaussianPulse" @@ -15770,6 +15851,9 @@ "propertyName": "type" }, "oneOf": [ + { + "$ref": "#/definitions/BroadbandPulse" + }, { "$ref": "#/definitions/ContinuousWave" }, @@ -16369,6 +16453,7 @@ "source_time": { "discriminator": { "mapping": { + "BroadbandPulse": "#/definitions/BroadbandPulse", "ContinuousWave": "#/definitions/ContinuousWave", "CustomSourceTime": "#/definitions/CustomSourceTime", "GaussianPulse": "#/definitions/GaussianPulse" @@ -16376,6 +16461,9 @@ "propertyName": "type" }, "oneOf": [ + { + "$ref": "#/definitions/BroadbandPulse" + }, { "$ref": "#/definitions/ContinuousWave" }, diff --git a/schemas/TerminalComponentModeler.json b/schemas/TerminalComponentModeler.json index 01d35aaf24..d3fb5bb01a 100644 --- a/schemas/TerminalComponentModeler.json +++ b/schemas/TerminalComponentModeler.json @@ -748,6 +748,7 @@ "source_time": { "discriminator": { "mapping": { + "BroadbandPulse": "#/definitions/BroadbandPulse", "ContinuousWave": "#/definitions/ContinuousWave", "CustomSourceTime": "#/definitions/CustomSourceTime", "GaussianPulse": "#/definitions/GaussianPulse" @@ -755,6 +756,9 @@ "propertyName": "type" }, "oneOf": [ + { + "$ref": "#/definitions/BroadbandPulse" + }, { "$ref": "#/definitions/ContinuousWave" }, @@ -1881,6 +1885,58 @@ ], "type": "object" }, + "BroadbandPulse": { + "additionalProperties": false, + "properties": { + "amplitude": { + "default": 1.0, + "minimum": 0, + "type": "number" + }, + "attrs": { + "default": {}, + "type": "object" + }, + "freq_range": { + "items": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "maxItems": 2, + "minItems": 2, + "type": "array" + }, + "minimum_amplitude": { + "default": 0.3, + "exclusiveMaximum": 0.5, + "exclusiveMinimum": 0.05, + "type": "number" + }, + "offset": { + "default": 0.0, + "type": "number" + }, + "phase": { + "default": 0.0, + "type": "number" + }, + "type": { + "default": "BroadbandPulse", + "enum": [ + "BroadbandPulse" + ], + "type": "string" + } + }, + "required": [ + "freq_range" + ], + "type": "object" + }, "CaugheyThomasMobility": { "additionalProperties": false, "properties": { @@ -3348,6 +3404,7 @@ "source_time": { "discriminator": { "mapping": { + "BroadbandPulse": "#/definitions/BroadbandPulse", "ContinuousWave": "#/definitions/ContinuousWave", "CustomSourceTime": "#/definitions/CustomSourceTime", "GaussianPulse": "#/definitions/GaussianPulse" @@ -3355,6 +3412,9 @@ "propertyName": "type" }, "oneOf": [ + { + "$ref": "#/definitions/BroadbandPulse" + }, { "$ref": "#/definitions/ContinuousWave" }, @@ -4003,6 +4063,7 @@ "source_time": { "discriminator": { "mapping": { + "BroadbandPulse": "#/definitions/BroadbandPulse", "ContinuousWave": "#/definitions/ContinuousWave", "CustomSourceTime": "#/definitions/CustomSourceTime", "GaussianPulse": "#/definitions/GaussianPulse" @@ -4010,6 +4071,9 @@ "propertyName": "type" }, "oneOf": [ + { + "$ref": "#/definitions/BroadbandPulse" + }, { "$ref": "#/definitions/ContinuousWave" }, @@ -8285,6 +8349,7 @@ "source_time": { "discriminator": { "mapping": { + "BroadbandPulse": "#/definitions/BroadbandPulse", "ContinuousWave": "#/definitions/ContinuousWave", "CustomSourceTime": "#/definitions/CustomSourceTime", "GaussianPulse": "#/definitions/GaussianPulse" @@ -8292,6 +8357,9 @@ "propertyName": "type" }, "oneOf": [ + { + "$ref": "#/definitions/BroadbandPulse" + }, { "$ref": "#/definitions/ContinuousWave" }, @@ -12400,6 +12468,7 @@ "source_time": { "discriminator": { "mapping": { + "BroadbandPulse": "#/definitions/BroadbandPulse", "ContinuousWave": "#/definitions/ContinuousWave", "CustomSourceTime": "#/definitions/CustomSourceTime", "GaussianPulse": "#/definitions/GaussianPulse" @@ -12407,6 +12476,9 @@ "propertyName": "type" }, "oneOf": [ + { + "$ref": "#/definitions/BroadbandPulse" + }, { "$ref": "#/definitions/ContinuousWave" }, @@ -14091,6 +14163,7 @@ "source_time": { "discriminator": { "mapping": { + "BroadbandPulse": "#/definitions/BroadbandPulse", "ContinuousWave": "#/definitions/ContinuousWave", "CustomSourceTime": "#/definitions/CustomSourceTime", "GaussianPulse": "#/definitions/GaussianPulse" @@ -14098,6 +14171,9 @@ "propertyName": "type" }, "oneOf": [ + { + "$ref": "#/definitions/BroadbandPulse" + }, { "$ref": "#/definitions/ContinuousWave" }, @@ -14235,6 +14311,7 @@ "source_time": { "discriminator": { "mapping": { + "BroadbandPulse": "#/definitions/BroadbandPulse", "ContinuousWave": "#/definitions/ContinuousWave", "CustomSourceTime": "#/definitions/CustomSourceTime", "GaussianPulse": "#/definitions/GaussianPulse" @@ -14242,6 +14319,9 @@ "propertyName": "type" }, "oneOf": [ + { + "$ref": "#/definitions/BroadbandPulse" + }, { "$ref": "#/definitions/ContinuousWave" }, @@ -16885,6 +16965,7 @@ "source_time": { "discriminator": { "mapping": { + "BroadbandPulse": "#/definitions/BroadbandPulse", "ContinuousWave": "#/definitions/ContinuousWave", "CustomSourceTime": "#/definitions/CustomSourceTime", "GaussianPulse": "#/definitions/GaussianPulse" @@ -16892,6 +16973,9 @@ "propertyName": "type" }, "oneOf": [ + { + "$ref": "#/definitions/BroadbandPulse" + }, { "$ref": "#/definitions/ContinuousWave" }, @@ -17491,6 +17575,7 @@ "source_time": { "discriminator": { "mapping": { + "BroadbandPulse": "#/definitions/BroadbandPulse", "ContinuousWave": "#/definitions/ContinuousWave", "CustomSourceTime": "#/definitions/CustomSourceTime", "GaussianPulse": "#/definitions/GaussianPulse" @@ -17498,6 +17583,9 @@ "propertyName": "type" }, "oneOf": [ + { + "$ref": "#/definitions/BroadbandPulse" + }, { "$ref": "#/definitions/ContinuousWave" }, @@ -17831,6 +17919,9 @@ }, "custom_source_time": { "anyOf": [ + { + "$ref": "#/definitions/BroadbandPulse" + }, { "$ref": "#/definitions/ContinuousWave" }, diff --git a/tests/test_components/test_packaging.py b/tests/test_components/test_packaging.py index dcb40d7240..c24ea2a180 100644 --- a/tests/test_components/test_packaging.py +++ b/tests/test_components/test_packaging.py @@ -8,6 +8,7 @@ from tidy3d.packaging import ( Tidy3dImportError, check_import, + check_tidy3d_extras_licensed_feature, supports_local_subpixel, tidy3d_extras, verify_packages_import, @@ -86,6 +87,8 @@ def get_eps(): assert tidy3d_extras["mod"] is not None features = tidy3d_extras["mod"].extension._features() assert tidy3d_extras["use_local_subpixel"] == ("local_subpixel" in features) + if tidy3d_extras["use_local_subpixel"]: + check_tidy3d_extras_licensed_feature("local_subpixel") else: assert tidy3d_extras["use_local_subpixel"] is False assert tidy3d_extras["mod"] is None @@ -93,6 +96,17 @@ def get_eps(): get_eps() +def test_tidy3d_extras_broadband_feature(): + import importlib + + has_tidy3d_extras = importlib.util.find_spec("tidy3d_extras") is not None + print(f"has_tidy3d_extras = {has_tidy3d_extras}") + if has_tidy3d_extras: + features = tidy3d_extras["mod"].extension._features() + if "BroadbandPulse" in features: + check_tidy3d_extras_licensed_feature("BroadbandPulse") + + def test_supports_local_subpixel_respects_config_false(): reload_config(profile="default") tidy3d_extras["mod"] = object() @@ -106,9 +120,7 @@ def get_flag(): return tidy3d_extras["use_local_subpixel"] assert get_flag() is False - assert tidy3d_extras["mod"] is None finally: - tidy3d_extras["mod"] = None tidy3d_extras["use_local_subpixel"] = None reload_config(profile="default") diff --git a/tidy3d/__init__.py b/tidy3d/__init__.py index 48adeaf149..9c544876da 100644 --- a/tidy3d/__init__.py +++ b/tidy3d/__init__.py @@ -411,6 +411,7 @@ # sources from .components.source.time import ( + BroadbandPulse, ContinuousWave, CustomSourceTime, GaussianPulse, @@ -516,6 +517,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 a3b2f0f95b..503a4490ce 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 check_tidy3d_extras_licensed_feature, tidy3d_extras # how many units of ``twidth`` from the ``offset`` until a gaussian pulse is considered "off" END_TIME_FACTOR_GAUSSIAN = 10 @@ -605,4 +606,80 @@ 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( + 0.0, + title="Offset", + description="An automatic time delay of the peak value of the pulse has been applied under the hood " + "to ensure smooth ramping up of the pulse at time = 0. This offfset is added on top of the automatic time delay " + "in units of 1 / [``2pi * (freq_range[1] - freq_range[0])``].", + ) + + @pydantic.validator("freq_range", always=True) + def _validate_freq_range(cls, val): + """Validate that freq_range is positive and properly ordered.""" + if val[0] <= 0 or val[1] <= 0: + raise ValidationError("Both elements of 'freq_range' must be positive.") + if val[1] <= val[0]: + raise ValidationError( + f"'freq_range[1]' ({val[1]}) must be greater than 'freq_range[0]' ({val[0]})." + ) + return val + + @pydantic.root_validator() + def _check_broadband_pulse_available(cls, values): + """Check if BroadbandPulse is available.""" + check_tidy3d_extras_licensed_feature("BroadbandPulse") + return values + + @cached_property + def _source(self): + """Implementation of broadband pulse.""" + return tidy3d_extras["mod"].extension.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: + """Delegated to `frequency_range_sigma(sigma=num_fwidth)` for computing the frequency range where the source amplitude + is within ``exp(-num_fwidth**2/2)`` of the peak amplitude. + """ + return self.frequency_range_sigma(num_fwidth) + + +SourceTimeType = Union[GaussianPulse, ContinuousWave, CustomSourceTime, BroadbandPulse] diff --git a/tidy3d/packaging.py b/tidy3d/packaging.py index 05234990f8..bc16098ed2 100644 --- a/tidy3d/packaging.py +++ b/tidy3d/packaging.py @@ -16,7 +16,6 @@ from tidy3d.config import config from .exceptions import Tidy3dImportError -from .log import log from .version import __version__ vtk = { @@ -184,6 +183,80 @@ def get_numpy_major_version(module=np): return major_version +def _check_tidy3d_extras_available(): + """Helper function to check if 'tidy3d-extras' is available and version matched. + + Raises + ------ + Tidy3dImportError + If tidy3d-extras is not available or not properly initialized. + """ + if tidy3d_extras["mod"] is not None: + return + + module_exists = find_spec("tidy3d_extras") is not None + if not module_exists: + raise Tidy3dImportError( + "The package 'tidy3d-extras' is absent. " + "Please install the 'tidy3d-extras' package using, for " + "example, 'pip install tidy3d[extras]'." + ) + + try: + import tidy3d_extras as tidy3d_extras_mod + + except ImportError as exc: + raise Tidy3dImportError( + "The package 'tidy3d-extras' did not initialize correctly." + ) from exc + + version = tidy3d_extras_mod.__version__ + + if version is None: + raise Tidy3dImportError( + "The package 'tidy3d-extras' did not initialize correctly, " + "likely due to an invalid API key." + ) + + if version != __version__: + raise Tidy3dImportError( + f"The version of 'tidy3d-extras' is {version}, but the version of 'tidy3d' is {__version__}. " + "They must match. You can install the correct " + "version using 'pip install tidy3d[extras]'." + ) + + tidy3d_extras["mod"] = tidy3d_extras_mod + + +def check_tidy3d_extras_licensed_feature(feature_name: str): + """Helper function to check if a specific feature is licensed in 'tidy3d-extras'. + + Parameters + ---------- + feature_name : str + The name of the feature to check for. + + Raises + ------ + Tidy3dImportError + If the feature is not available with your license. + """ + + try: + _check_tidy3d_extras_available() + except Tidy3dImportError as exc: + raise Tidy3dImportError( + "The package 'tidy3d-extras' is required for this feature '{feature_name}'." + ) from exc + + features = tidy3d_extras["mod"].extension._features() + if feature_name not in features: + raise Tidy3dImportError( + f"The feature '{feature_name}' is not available with your license. " + "Please contact Tidy3D support, or upgrade your license." + ) + + def supports_local_subpixel(fn): """When decorating a method, checks that 'tidy3d-extras' is available, conditioned on 'config.simulation.use_local_subpixel'.""" @@ -194,61 +267,22 @@ def _fn(*args: Any, **kwargs: Any): if preference is False: tidy3d_extras["use_local_subpixel"] = False - tidy3d_extras["mod"] = None - else: - if tidy3d_extras["mod"] is None: - tidy3d_extras["use_local_subpixel"] = False - tidy3d_extras["mod"] = None - module_exists = find_spec("tidy3d_extras") is not None - - if preference is True and not module_exists: - raise Tidy3dImportError( - "The package 'tidy3d-extras' is required for this " - "operation when 'config.simulation.use_local_subpixel' is 'True'. " - "Please install the 'tidy3d-extras' package using, for " - "example, 'pip install tidy3d[extras]'." - ) - - if module_exists: - try: - import tidy3d_extras as tidy3d_extras_mod - - except ImportError as exc: - tidy3d_extras["mod"] = None - tidy3d_extras["use_local_subpixel"] = False - raise Tidy3dImportError( - "The package 'tidy3d-extras' did not initialize correctly. " - "To suppress this error, you can set " - "'config.simulation.use_local_subpixel=False'." - ) from exc - - version = tidy3d_extras_mod.__version__ - - if version is None: - tidy3d_extras["mod"] = None - tidy3d_extras["use_local_subpixel"] = False - raise Tidy3dImportError( - "The package 'tidy3d-extras' did not initialize correctly, " - "likely due to an invalid API key. To suppress this error, " - "you can set 'config.simulation.use_local_subpixel=False'." - ) - - if version != __version__: - log.warning( - "The package 'tidy3d-extras' is required for this " - "operation. The version of 'tidy3d-extras' should match " - "the version of 'tidy3d'. You can install the correct " - "version using 'pip install tidy3d[extras]'." - ) - - features = tidy3d_extras_mod.extension._features() + return fn(*args, **kwargs) - tidy3d_extras["mod"] = tidy3d_extras_mod - tidy3d_extras["use_local_subpixel"] = "local_subpixel" in features - else: - features = tidy3d_extras["mod"].extension._features() - tidy3d_extras["use_local_subpixel"] = "local_subpixel" in features + try: + check_tidy3d_extras_licensed_feature("local_subpixel") + except Tidy3dImportError as exc: + tidy3d_extras["use_local_subpixel"] = False + if preference is True: + raise Tidy3dImportError( + f"{exc!s} To suppress this error, you can set " + "'config.simulation.use_local_subpixel=False'." + ) from exc + # preference is None, so we can just return + return fn(*args, **kwargs) + # local_subpixel is available + tidy3d_extras["use_local_subpixel"] = True return fn(*args, **kwargs) return _fn