Skip to content

Commit

Permalink
use mode solver fields as default args in conversion methods
Browse files Browse the repository at this point in the history
  • Loading branch information
tylerflex committed Nov 6, 2023
1 parent c5aec95 commit 511b956
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 8 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed
- Indent for the json string of Tidy3D models has been changed to `None` when used internally; kept as `indent=4` for writing to `json` and `yaml` files.
- `freqs` and `direction` are optional in `ModeSolver` methods converting to monitor and source, respectively. If not supplied, uses the values from the `ModeSolver` instance calling the method.

### Fixed
- Fixed the duplication of log messages in Jupyter when `set_logging_file` is used.
Expand Down
53 changes: 53 additions & 0 deletions tests/test_plugins/test_mode_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -653,3 +653,56 @@ def test_mode_solver_nan_pol_fraction():
md = ms.solve()

assert list(np.where(np.isnan(md.pol_fraction.te))[1]) == [8, 9]


def test_mode_solver_method_defaults():
"""Test that changes to mode solver default values in methods work."""

simulation = td.Simulation(
medium=td.Medium(permittivity=2),
size=SIM_SIZE,
grid_spec=td.GridSpec.auto(wavelength=1.55, min_steps_per_wvl=15),
run_time=1e-12,
symmetry=(0, 0, 1),
boundary_spec=td.BoundarySpec.all_sides(boundary=td.Periodic()),
sources=[SRC],
)

mode_spec = td.ModeSpec(
num_modes=10,
target_neff=3.48,
filter_pol="tm",
precision="single",
track_freq="central",
)

freqs = [td.C_0 / 1.55]

ms = ModeSolver(
simulation=simulation,
plane=td.Box(center=(0, 0, 0), size=(2, 0, 1.1)),
mode_spec=mode_spec,
freqs=freqs,
direction="-",
)

# test defaults
st = td.GaussianPulse(freq0=1.0, fwidth=1.0)

src = ms.to_source(source_time=st)
assert src.direction == ms.direction

src = ms.to_source(source_time=st, direction="+")
assert src.direction != ms.direction

mnt = ms.to_monitor(name="mode_mnt")
assert np.allclose(mnt.freqs, ms.freqs)

mnt = ms.to_monitor(name="mode_mnt", freqs=[2e14])
assert not np.allclose(mnt.freqs, ms.freqs)

sim = ms.sim_with_source(source_time=st)
assert sim.sources[-1].direction == ms.direction

sim = ms.sim_with_monitor(name="test")
assert np.allclose(sim.monitors[-1].freqs, ms.freqs)
34 changes: 26 additions & 8 deletions tidy3d/plugins/mode/mode_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,7 @@ def _grid_correction(
def to_source(
self,
source_time: SourceTime,
direction: Direction,
direction: Direction = None,
mode_index: pydantic.NonNegativeInt = 0,
) -> ModeSource:
"""Creates :class:`.ModeSource` from a :class:`ModeSolver` instance plus additional
Expand All @@ -583,8 +583,9 @@ def to_source(
----------
source_time: :class:`.SourceTime`
Specification of the source time-dependence.
direction : Direction
direction : Direction = None
Whether source will inject in ``"+"`` or ``"-"`` direction relative to plane normal.
If not specified, uses the direction from the mode solver.
mode_index : int = 0
Index into the list of modes returned by mode solver to use in source.
Expand All @@ -595,6 +596,9 @@ def to_source(
inputs.
"""

if direction is None:
direction = self.direction

return ModeSource(
center=self.plane.center,
size=self.plane.size,
Expand All @@ -604,14 +608,15 @@ def to_source(
direction=direction,
)

def to_monitor(self, freqs: List[float], name: str) -> ModeMonitor:
def to_monitor(self, freqs: List[float] = None, name: str = None) -> ModeMonitor:
"""Creates :class:`ModeMonitor` from a :class:`ModeSolver` instance plus additional
specifications.
Parameters
----------
freqs : List[float]
Frequencies to include in Monitor (Hz).
If not specified, passes ``self.freqs``.
name : str
Required name of monitor.
Expand All @@ -622,6 +627,15 @@ def to_monitor(self, freqs: List[float], name: str) -> ModeMonitor:
inputs.
"""

if freqs is None:
freqs = self.freqs

if name is None:
raise ValueError(
"A 'name' must be passed to 'ModeSolver.to_monitor'. "
"The default value of 'None' is for backwards compatibility and is not accepted."
)

return ModeMonitor(
center=self.plane.center,
size=self.plane.size,
Expand Down Expand Up @@ -663,7 +677,7 @@ def to_mode_solver_monitor(self, name: str, colocate: bool = None) -> ModeSolver
def sim_with_source(
self,
source_time: SourceTime,
direction: Direction,
direction: Direction = None,
mode_index: pydantic.NonNegativeInt = 0,
) -> Simulation:
"""Creates :class:`Simulation` from a :class:`ModeSolver`. Creates a copy of
Expand All @@ -674,8 +688,9 @@ def sim_with_source(
----------
source_time: :class:`.SourceTime`
Specification of the source time-dependence.
direction : Direction
direction : Direction = None
Whether source will inject in ``"+"`` or ``"-"`` direction relative to plane normal.
If not specified, uses the direction from the mode solver.
mode_index : int = 0
Index into the list of modes returned by mode solver to use in source.
Expand All @@ -685,6 +700,7 @@ def sim_with_source(
Copy of the simulation with a :class:`.ModeSource` with specifications taken
from the ModeSolver instance and the method inputs.
"""

mode_source = self.to_source(
mode_index=mode_index, direction=direction, source_time=source_time
)
Expand All @@ -694,17 +710,18 @@ def sim_with_source(

def sim_with_monitor(
self,
freqs: List[float],
name: str,
freqs: List[float] = None,
name: str = 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
the ModeSolver parameters.
Parameters
----------
freqs : List[float]
freqs : List[float] = None
Frequencies to include in Monitor (Hz).
If not specified, uses the frequencies from the mode solver.
name : str
Required name of monitor.
Expand All @@ -714,6 +731,7 @@ def sim_with_monitor(
Copy of the simulation with a :class:`.ModeMonitor` with specifications taken
from the ModeSolver instance and the method inputs.
"""

mode_monitor = self.to_monitor(freqs=freqs, name=name)
new_monitors = list(self.simulation.monitors) + [mode_monitor]
new_sim = self.simulation.updated_copy(monitors=new_monitors)
Expand Down

0 comments on commit 511b956

Please sign in to comment.