diff --git a/CHANGELOG.md b/CHANGELOG.md index cfdadf5d05..20d539bf8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ 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 `LowFrequencySmoothingSpec` and `ModelerLowFrequencySmoothingSpec` for automatic smoothing of mode monitor data at low frequencies where DFT sampling is insufficient. ### 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/schemas/Simulation.json b/schemas/Simulation.json index 0e4e86b99c..dfa7c93beb 100644 --- a/schemas/Simulation.json +++ b/schemas/Simulation.json @@ -9350,6 +9350,53 @@ ], "type": "object" }, + "LowFrequencySmoothingSpec": { + "additionalProperties": false, + "properties": { + "attrs": { + "default": {}, + "type": "object" + }, + "max_deviation": { + "default": 0.5, + "minimum": 0, + "type": "number" + }, + "max_sampling_time": { + "default": 5.0, + "minimum": 0, + "type": "number" + }, + "min_sampling_time": { + "default": 1.0, + "minimum": 0, + "type": "number" + }, + "monitors": { + "items": { + "type": "string" + }, + "type": "array" + }, + "order": { + "default": 1, + "maximum": 3, + "minimum": 0, + "type": "integer" + }, + "type": { + "default": "LowFrequencySmoothingSpec", + "enum": [ + "LowFrequencySmoothingSpec" + ], + "type": "string" + } + }, + "required": [ + "monitors" + ], + "type": "object" + }, "LumpedResistor": { "additionalProperties": false, "properties": { @@ -15539,6 +15586,13 @@ }, "type": "array" }, + "low_freq_smoothing": { + "allOf": [ + { + "$ref": "#/definitions/LowFrequencySmoothingSpec" + } + ] + }, "lumped_elements": { "default": [], "items": { diff --git a/schemas/TerminalComponentModeler.json b/schemas/TerminalComponentModeler.json index 8fe6ce3d51..0c732778e0 100644 --- a/schemas/TerminalComponentModeler.json +++ b/schemas/TerminalComponentModeler.json @@ -9658,6 +9658,53 @@ ], "type": "object" }, + "LowFrequencySmoothingSpec": { + "additionalProperties": false, + "properties": { + "attrs": { + "default": {}, + "type": "object" + }, + "max_deviation": { + "default": 0.5, + "minimum": 0, + "type": "number" + }, + "max_sampling_time": { + "default": 5.0, + "minimum": 0, + "type": "number" + }, + "min_sampling_time": { + "default": 1.0, + "minimum": 0, + "type": "number" + }, + "monitors": { + "items": { + "type": "string" + }, + "type": "array" + }, + "order": { + "default": 1, + "maximum": 3, + "minimum": 0, + "type": "integer" + }, + "type": { + "default": "LowFrequencySmoothingSpec", + "enum": [ + "LowFrequencySmoothingSpec" + ], + "type": "string" + } + }, + "required": [ + "monitors" + ], + "type": "object" + }, "LumpedPort": { "additionalProperties": false, "properties": { @@ -11484,6 +11531,44 @@ }, "type": "object" }, + "ModelerLowFrequencySmoothingSpec": { + "additionalProperties": false, + "properties": { + "attrs": { + "default": {}, + "type": "object" + }, + "max_deviation": { + "default": 0.5, + "minimum": 0, + "type": "number" + }, + "max_sampling_time": { + "default": 5.0, + "minimum": 0, + "type": "number" + }, + "min_sampling_time": { + "default": 1.0, + "minimum": 0, + "type": "number" + }, + "order": { + "default": 1, + "maximum": 3, + "minimum": 0, + "type": "integer" + }, + "type": { + "default": "ModelerLowFrequencySmoothingSpec", + "enum": [ + "ModelerLowFrequencySmoothingSpec" + ], + "type": "string" + } + }, + "type": "object" + }, "ModulationSpec": { "additionalProperties": false, "properties": { @@ -14240,6 +14325,13 @@ }, "type": "array" }, + "low_freq_smoothing": { + "allOf": [ + { + "$ref": "#/definitions/LowFrequencySmoothingSpec" + } + ] + }, "lumped_elements": { "default": [], "items": { @@ -16922,6 +17014,21 @@ } ] }, + "low_freq_smoothing": { + "allOf": [ + { + "$ref": "#/definitions/ModelerLowFrequencySmoothingSpec" + } + ], + "default": { + "attrs": {}, + "max_deviation": 0.5, + "max_sampling_time": 5.0, + "min_sampling_time": 1.0, + "order": 1, + "type": "ModelerLowFrequencySmoothingSpec" + } + }, "name": { "default": "", "type": "string" diff --git a/tests/test_components/test_low_freq_smoothing.py b/tests/test_components/test_low_freq_smoothing.py new file mode 100644 index 0000000000..341e8be992 --- /dev/null +++ b/tests/test_components/test_low_freq_smoothing.py @@ -0,0 +1,50 @@ +"""Test low frequency smoothing specification.""" + +from __future__ import annotations + +import pydantic.v1 as pydantic +import pytest + +import tidy3d as td + + +def test_low_freq_smoothing_spec_initialization_default_values(): + """Test that LowFrequencySmoothingSpec initializes with correct default values.""" + spec = td.LowFrequencySmoothingSpec(monitors=["monitor1"]) + assert spec.min_sampling_time == 1 + assert spec.max_sampling_time == 5 + assert spec.order == 1 + assert spec.max_deviation == 0.5 + + +def test_empty_monitors(): + """Test that LowFrequencySmoothingSpec raises an error if monitors are not provided.""" + with pytest.raises(pydantic.ValidationError): + td.LowFrequencySmoothingSpec(monitors=[]) + + +def test_monitors_exist(): + """Test that LowFrequencySmoothingSpec raises an error if monitors do not exist.""" + sim = td.Simulation( + size=(1, 1, 1), + monitors=[ + td.ModeMonitor( + name="monitor1", + mode_spec=td.ModeSpec(num_modes=1), + freqs=[td.C_0], + size=(0.5, 0.5, 0), + ) + ], + run_time=1e-12, + grid_spec=td.GridSpec.auto(min_steps_per_wvl=20, wavelength=1), + low_freq_smoothing=td.LowFrequencySmoothingSpec(monitors=["monitor1"]), + ) + + with pytest.raises(pydantic.ValidationError): + sim = td.Simulation( + size=(1, 1, 1), + monitors=[], + run_time=1e-12, + grid_spec=td.GridSpec.auto(min_steps_per_wvl=20, wavelength=1), + low_freq_smoothing=td.LowFrequencySmoothingSpec(monitors=["monitor1"]), + ) diff --git a/tests/test_plugins/smatrix/test_terminal_component_modeler.py b/tests/test_plugins/smatrix/test_terminal_component_modeler.py index e7ca3b3f00..bc0eccb36a 100644 --- a/tests/test_plugins/smatrix/test_terminal_component_modeler.py +++ b/tests/test_plugins/smatrix/test_terminal_component_modeler.py @@ -1424,3 +1424,115 @@ def test_wave_port_to_absorber(tmp_path): sim = list(modeler.sim_dict.values())[0] absorber = sim.internal_absorbers[0] assert absorber.boundary_spec == custom_boundary_spec + + +def test_low_freq_smoothing_spec_initialization_default_values(): + """Test that LowFrequencySmoothingSpec initializes with correct default values.""" + from tidy3d.plugins.smatrix.component_modelers.terminal import ModelerLowFrequencySmoothingSpec + + spec = ModelerLowFrequencySmoothingSpec() + assert spec.min_sampling_time == 1 + assert spec.max_sampling_time == 5 + assert spec.order == 1 + assert spec.max_deviation == 0.5 + + +def test_low_freq_smoothing_spec_initialization_custom_values(): + """Test that LowFrequencySmoothingSpec initializes with custom values.""" + from tidy3d.plugins.smatrix.component_modelers.terminal import ModelerLowFrequencySmoothingSpec + + spec = ModelerLowFrequencySmoothingSpec( + min_sampling_time=2, max_sampling_time=8, order=2, max_deviation=0.3 + ) + assert spec.min_sampling_time == 2 + assert spec.max_sampling_time == 8 + assert spec.order == 2 + assert spec.max_deviation == 0.3 + + +def test_low_freq_smoothing_spec_edge_cases(): + """Test edge cases and boundary conditions.""" + from tidy3d.plugins.smatrix.component_modelers.terminal import ModelerLowFrequencySmoothingSpec + + # Test with order 0 (constant fit) + spec = ModelerLowFrequencySmoothingSpec(order=0) + assert spec.order == 0 + + # Test with maximum order + spec = ModelerLowFrequencySmoothingSpec(order=3) + assert spec.order == 3 + + # Test with zero max_deviation + spec = ModelerLowFrequencySmoothingSpec(max_deviation=0.0) + assert spec.max_deviation == 0.0 + + # Test with maximum max_deviation + spec = ModelerLowFrequencySmoothingSpec(max_deviation=1.0) + assert spec.max_deviation == 1.0 + + +def test_low_freq_smoothing_spec_validation_sampling_times_invalid(): + """Test validation of sampling time parameters.""" + from tidy3d.plugins.smatrix.component_modelers.terminal import ModelerLowFrequencySmoothingSpec + + # Test invalid range where min_sampling_time >= max_sampling_time + with pytest.raises( + ValueError, match="The minimum sampling time must be less than the maximum sampling time" + ): + ModelerLowFrequencySmoothingSpec(min_sampling_time=5, max_sampling_time=3) + + with pytest.raises( + ValueError, match="The minimum sampling time must be less than the maximum sampling time" + ): + ModelerLowFrequencySmoothingSpec(min_sampling_time=3, max_sampling_time=3) + + +def test_low_freq_smoothing_spec_validation_order_bounds(): + """Test validation of order parameter bounds.""" + from tidy3d.plugins.smatrix.component_modelers.terminal import ModelerLowFrequencySmoothingSpec + + # Test valid orders + ModelerLowFrequencySmoothingSpec(order=0) + ModelerLowFrequencySmoothingSpec(order=3) + + # Test invalid orders + with pytest.raises(pd.ValidationError): + ModelerLowFrequencySmoothingSpec(order=-1) + + with pytest.raises(pd.ValidationError): + ModelerLowFrequencySmoothingSpec(order=4) + + +def test_low_freq_smoothing_spec_validation_max_deviation_bounds(): + """Test validation of max_deviation parameter bounds.""" + from tidy3d.plugins.smatrix.component_modelers.terminal import ModelerLowFrequencySmoothingSpec + + # Test valid max_deviation + ModelerLowFrequencySmoothingSpec(max_deviation=0.0) + ModelerLowFrequencySmoothingSpec(max_deviation=1.0) + + # Test invalid max_deviation + with pytest.raises(pd.ValidationError): + ModelerLowFrequencySmoothingSpec(max_deviation=-0.1) + + +def test_low_freq_smoothing_spec_sim_dict(): + """Test that LowFrequencySmoothingSpec is correctly added to the sim_dict.""" + from tidy3d.plugins.smatrix.component_modelers.terminal import ModelerLowFrequencySmoothingSpec + + spec = ModelerLowFrequencySmoothingSpec( + min_sampling_time=2, max_sampling_time=8, order=2, max_deviation=0.3 + ) + + modeler = make_coaxial_component_modeler(port_types=(WavePort, WavePort)) + modeler = modeler.updated_copy(low_freq_smoothing=spec) + for sim in modeler.sim_dict.values(): + assert spec.min_sampling_time == sim.low_freq_smoothing.min_sampling_time + assert spec.max_sampling_time == sim.low_freq_smoothing.max_sampling_time + assert spec.order == sim.low_freq_smoothing.order + assert spec.max_deviation == sim.low_freq_smoothing.max_deviation + assert sim.low_freq_smoothing.monitors == tuple(mnt.name for mnt in sim.monitors[-2:]) + + modeler = modeler.updated_copy(low_freq_smoothing=None) + for sim in modeler.sim_dict.values(): + assert sim.low_freq_smoothing is None diff --git a/tidy3d/__init__.py b/tidy3d/__init__.py index 8741cec4fc..27fd3f0445 100644 --- a/tidy3d/__init__.py +++ b/tidy3d/__init__.py @@ -4,6 +4,7 @@ from tidy3d.components.boundary import BroadbandModeABCFitterParam, BroadbandModeABCSpec from tidy3d.components.data.index import SimulationDataMap +from tidy3d.components.frequency_extrapolation import LowFrequencySmoothingSpec from tidy3d.components.index import SimulationMap from tidy3d.components.material.multi_physics import MultiPhysicsMedium from tidy3d.components.material.tcad.charge import ( @@ -619,6 +620,7 @@ def set_logging_level(level: str) -> None: "LinearLumpedElement", "Lorentz", "LossyMetalMedium", + "LowFrequencySmoothingSpec", "LumpedElement", "LumpedResistor", "Medium", diff --git a/tidy3d/components/frequency_extrapolation.py b/tidy3d/components/frequency_extrapolation.py new file mode 100644 index 0000000000..e2768bba36 --- /dev/null +++ b/tidy3d/components/frequency_extrapolation.py @@ -0,0 +1,83 @@ +"""Extrapolation into low frequencies specification.""" + +from __future__ import annotations + +from typing import Optional + +import pydantic.v1 as pydantic + +from tidy3d.components.base import Tidy3dBaseModel + + +class AbstractLowFrequencySmoothingSpec(Tidy3dBaseModel): + """Abstract base class for low frequency smoothing specifications.""" + + min_sampling_time: pydantic.NonNegativeFloat = pydantic.Field( + 1.0, + title="Minimum Sampling Time (periods)", + description="The minimum simulation time in periods of the corresponding frequency for which frequency domain results will be used to fit the polynomial for the low frequency extrapolation. " + "Results below this threshold will be completely discarded.", + ) + + max_sampling_time: pydantic.NonNegativeFloat = pydantic.Field( + 5.0, + title="Maximum Sampling Time (periods)", + description="The maximum simulation time in periods of the corresponding frequency for which frequency domain results will be used to fit the polynomial for the low frequency extrapolation. " + "Results above this threshold will be not be modified.", + ) + + order: int = pydantic.Field( + 1, + title="Extrapolation Order", + description="The order of the polynomial to use for the low frequency extrapolation.", + ge=0, + le=3, + ) + + max_deviation: Optional[float] = pydantic.Field( + 0.5, + title="Maximum Deviation", + description="The maximum deviation (in fraction of the trusted values) to allow for the low frequency smoothing.", + ge=0, + ) + + @pydantic.root_validator(skip_on_failure=True) + def _validate_sampling_times(cls, values): + min_sampling_time = values.get("min_sampling_time") + max_sampling_time = values.get("max_sampling_time") + if min_sampling_time >= max_sampling_time: + raise ValueError( + "The minimum sampling time must be less than the maximum sampling time." + ) + return values + + +class LowFrequencySmoothingSpec(AbstractLowFrequencySmoothingSpec): + """Specifies the low frequency smoothing parameters for the simulation. + This specification affects only results recorded in mode monitors. Specifically, the mode decomposition data + for frequencies for which the total simulation time in units of the corresponding period (T = 1/f) is less than + the specified minimum sampling time will be overridden by extrapolation from the data in the trusted frequency range. + The trusted frequency range is defined in terms of minimum and maximum sampling times (the total simulation time divided by the corresponding period). + Example + ------- + >>> low_freq_smoothing = LowFrequencySmoothingSpec( + ... min_sampling_time=3, + ... max_sampling_time=6, + ... order=1, + ... max_deviation=0.5, + ... monitors=("monitor1", "monitor2"), + ... ) + """ + + monitors: tuple[str, ...] = pydantic.Field( + ..., + title="Monitors", + description="The names of monitors to which low frequency smoothing will be applied.", + ) + + @pydantic.validator("monitors", always=True) + def _validate_monitors(cls, val, values): + """Validate the monitors list is not empty.""" + if not val: + raise ValueError("The monitors list must not be empty.") + return val diff --git a/tidy3d/components/mode/mode_solver.py b/tidy3d/components/mode/mode_solver.py index 12f511383f..012fd778fb 100644 --- a/tidy3d/components/mode/mode_solver.py +++ b/tidy3d/components/mode/mode_solver.py @@ -2614,6 +2614,7 @@ def reduced_simulation_copy(self): include_pml_cells=True, validate_geometries=False, deep_copy=False, + low_freq_smoothing=None, ) # Let's only validate mode solver where geometry validation is skipped: geometry replaced by its bounding # box diff --git a/tidy3d/components/simulation.py b/tidy3d/components/simulation.py index 4dbde67f38..fd69097382 100644 --- a/tidy3d/components/simulation.py +++ b/tidy3d/components/simulation.py @@ -50,6 +50,7 @@ from .data.unstructured.tetrahedral import TetrahedralGridDataset from .data.unstructured.triangular import TriangularGridDataset from .data.utils import CustomSpatialDataType +from .frequency_extrapolation import LowFrequencySmoothingSpec from .geometry.base import Box, Geometry, GeometryGroup from .geometry.mesh import TriangleMesh from .geometry.utils import _shift_object, flatten_groups, traverse_geometries @@ -2930,6 +2931,12 @@ class Simulation(AbstractYeeGridSimulation): """ + low_freq_smoothing: Optional[LowFrequencySmoothingSpec] = pydantic.Field( + None, + title="Low Frequency Smoothing", + description="The low frequency smoothing parameters for the simulation.", + ) + """ Validating setup """ @pydantic.root_validator(pre=True) @@ -4145,6 +4152,23 @@ def _check_normalize_index(cls, val, values): return val + @pydantic.validator("low_freq_smoothing", always=True) + def _validate_low_freq_smoothing(cls, val, values): + """Validate the low frequency smoothing parameters.""" + # check that all monitors are present and they are mode monitors + if val is None: + return val + monitors = values.get("monitors") + present_mode_monitor_names = [ + monitor.name for monitor in monitors if isinstance(monitor, ModeMonitor) + ] + for monitor in val.monitors: + if monitor not in present_mode_monitor_names: + raise SetupError( + f"Low frequency smoothing specification refers to monitor '{monitor}' which either does not exist or is not a mode monitor." + ) + return val + """ Post-init validators """ def _post_init_validators(self) -> None: diff --git a/tidy3d/plugins/autograd/invdes/parametrizations.py b/tidy3d/plugins/autograd/invdes/parametrizations.py index 4962c1a672..a21e22c1d0 100644 --- a/tidy3d/plugins/autograd/invdes/parametrizations.py +++ b/tidy3d/plugins/autograd/invdes/parametrizations.py @@ -233,8 +233,8 @@ def initialize_params_from_simulation( ).data if outside_handling == "mask": # build mask from coverage bounds of base epsilon coordinates - xb, yb, zb = [np.array(eps_base_da.coords[d]) for d in ("x", "y", "z")] - xd, yd, zd = [np.array(design_eps_da.coords[d]) for d in ("x", "y", "z")] + xb, yb, zb = (np.array(eps_base_da.coords[d]) for d in ("x", "y", "z")) + xd, yd, zd = (np.array(design_eps_da.coords[d]) for d in ("x", "y", "z")) mask_x = (xd >= np.min(xb)) & (xd <= np.max(xb)) mask_y = (yd >= np.min(yb)) & (yd <= np.max(yb)) mask_z = (zd >= np.min(zb)) & (zd <= np.max(zb)) diff --git a/tidy3d/plugins/smatrix/__init__.py b/tidy3d/plugins/smatrix/__init__.py index cf93a2545a..f38f21825e 100644 --- a/tidy3d/plugins/smatrix/__init__.py +++ b/tidy3d/plugins/smatrix/__init__.py @@ -8,7 +8,10 @@ AbstractComponentModeler, ) from tidy3d.plugins.smatrix.component_modelers.modal import ModalComponentModeler -from tidy3d.plugins.smatrix.component_modelers.terminal import TerminalComponentModeler +from tidy3d.plugins.smatrix.component_modelers.terminal import ( + ModelerLowFrequencySmoothingSpec, + TerminalComponentModeler, +) from tidy3d.plugins.smatrix.component_modelers.types import ComponentModelerType from tidy3d.plugins.smatrix.data.data_array import ( ModalPortDataArray, @@ -16,7 +19,10 @@ TerminalPortDataArray, ) from tidy3d.plugins.smatrix.data.modal import ModalComponentModelerData -from tidy3d.plugins.smatrix.data.terminal import MicrowaveSMatrixData, TerminalComponentModelerData +from tidy3d.plugins.smatrix.data.terminal import ( + MicrowaveSMatrixData, + TerminalComponentModelerData, +) from tidy3d.plugins.smatrix.data.types import ComponentModelerDataType from tidy3d.plugins.smatrix.ports.coaxial_lumped import CoaxialLumpedPort from tidy3d.plugins.smatrix.ports.modal import Port @@ -44,6 +50,7 @@ "ModalComponentModeler", "ModalComponentModelerData", "ModalPortDataArray", + "ModelerLowFrequencySmoothingSpec", "Port", "PortDataArray", "TerminalComponentModeler", diff --git a/tidy3d/plugins/smatrix/component_modelers/terminal.py b/tidy3d/plugins/smatrix/component_modelers/terminal.py index 82348a43f1..693cd831ce 100644 --- a/tidy3d/plugins/smatrix/component_modelers/terminal.py +++ b/tidy3d/plugins/smatrix/component_modelers/terminal.py @@ -9,9 +9,13 @@ from tidy3d.components.base import cached_property from tidy3d.components.boundary import BroadbandModeABCSpec +from tidy3d.components.frequency_extrapolation import ( + AbstractLowFrequencySmoothingSpec, + LowFrequencySmoothingSpec, +) from tidy3d.components.geometry.utils_2d import snap_coordinate_to_grid from tidy3d.components.index import SimulationMap -from tidy3d.components.monitor import DirectivityMonitor +from tidy3d.components.monitor import DirectivityMonitor, ModeMonitor from tidy3d.components.simulation import Simulation from tidy3d.components.source.time import GaussianPulse from tidy3d.components.types import Ax, Complex @@ -32,6 +36,27 @@ from tidy3d.plugins.smatrix.types import NetworkElement, NetworkIndex, SParamDef +class ModelerLowFrequencySmoothingSpec(AbstractLowFrequencySmoothingSpec): + """Specifies the low frequency smoothing parameters for the terminal component simulation. + This specification affects only results at wave ports. Specifically, the mode decomposition data + for frequencies for which the total simulation time in units of the corresponding period (T = 1/f) is less than + the specified minimum sampling time will be overridden by extrapolation from the data in the trusted frequency range. + The trusted frequency range is defined in terms of minimum and maximum sampling times (the total simulation time divided by the corresponding period). + + Example + ------- + >>> low_freq_smoothing = ModelerLowFrequencySmoothingSpec( + ... min_sampling_time=3, + ... max_sampling_time=6, + ... order=1, + ... max_deviation=0.5, + ... ) + """ + + +DEFAULT_LOW_FREQUENCY_SMOOTHING_SPEC = ModelerLowFrequencySmoothingSpec() + + class TerminalComponentModeler(AbstractComponentModeler): """ Tool for modeling two-terminal multiport devices and computing port parameters @@ -99,6 +124,12 @@ class TerminalComponentModeler(AbstractComponentModeler): description="Whether to compute scattering parameters using the 'pseudo' or 'power' wave definitions.", ) + low_freq_smoothing: Optional[ModelerLowFrequencySmoothingSpec] = pd.Field( + DEFAULT_LOW_FREQUENCY_SMOOTHING_SPEC, + title="Low Frequency Smoothing", + description="The low frequency smoothing parameters for the terminal component simulation.", + ) + @pd.root_validator(pre=False) def _warn_rf_license(cls, values): log.warning( @@ -344,6 +375,17 @@ def base_sim(self) -> Simulation: "internal_absorbers": new_absorbers, } + # propagate the low frequency smoothing specification to the simulation + mode_monitors = [mnt.name for mnt in field_monitors if isinstance(mnt, ModeMonitor)] + if mode_monitors and self.low_freq_smoothing is not None: + update_dict["low_freq_smoothing"] = LowFrequencySmoothingSpec( + monitors=mode_monitors, + min_sampling_time=self.low_freq_smoothing.min_sampling_time, + max_sampling_time=self.low_freq_smoothing.max_sampling_time, + order=self.low_freq_smoothing.order, + max_deviation=self.low_freq_smoothing.max_deviation, + ) + # This is the new default simulation will all shared components added return sim_wo_source.copy(update=update_dict)