Skip to content

Commit a9418a6

Browse files
caseyflexmomchil-flex
authored andcommitted
Fix bug in subsection with nonlinear materials source detection
1 parent d0bd196 commit a9418a6

File tree

6 files changed

+75
-3
lines changed

6 files changed

+75
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2020

2121
### Fixed
2222
- Ensure `path` argument in `run()` function is respected when running under autograd or the adjoint plugin.
23+
- Bug in `Simulation.subsection` (used in the mode solver) when nonlinear materials rely on information about sources outside of the region.
2324

2425

2526
## [2.7.3] - 2024-09-12

tests/test_components/test_medium.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,15 @@ def test_nonlinear_medium(log_capture):
616616
assert n0 == nonlinear_spec.models[0]._get_n0(n0=None, medium=medium, freqs=[freq0])
617617
assert freq0 == nonlinear_spec.models[0]._get_freq0(freq0=None, freqs=[freq0])
618618

619+
# subsection with nonlinear materials needs to hardcode source info
620+
sim2 = sim.updated_copy(center=(-4, -4, -4), path="sources/0")
621+
sim2 = sim2.updated_copy(
622+
models=[td.TwoPhotonAbsorption(beta=1)], path="structures/0/medium/nonlinear_spec"
623+
)
624+
sim2 = sim2.subsection(region=td.Box(center=(0, 0, 0), size=(1, 1, 0)))
625+
assert sim2.structures[0].medium.nonlinear_spec.models[0].n0 == n0
626+
assert sim2.structures[0].medium.nonlinear_spec.models[0].freq0 == freq0
627+
619628
# can't detect n0 with different source freqs
620629
source_time2 = source_time.updated_copy(freq0=2 * freq0)
621630
source2 = source.updated_copy(source_time=source_time2)

tests/test_components/test_simulation.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2452,7 +2452,10 @@ def test_sim_subsection(unstructured, nz):
24522452
sim_red = SIM_FULL.subsection(region=region, monitors=[])
24532453
assert len(sim_red.monitors) == 0
24542454
sim_red = SIM_FULL.subsection(region=region, remove_outside_structures=False)
2455-
assert sim_red.structures == SIM_FULL.structures
2455+
assert len(sim_red.structures) == len(SIM_FULL.structures)
2456+
for strc_red, strc in zip(sim_red.structures, SIM_FULL.structures):
2457+
if strc.medium.nonlinear_spec is None:
2458+
assert strc == strc_red
24562459
sim_red = SIM_FULL.subsection(region=region, remove_outside_custom_mediums=True)
24572460

24582461
perm = td.SpatialDataArray(

tidy3d/components/medium.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,12 @@ def _validate_medium_freqs(self, medium: AbstractMedium, freqs: List[pd.Positive
159159
"""Any additional validation that depends on the central frequencies of the sources."""
160160
pass
161161

162+
def _hardcode_medium_freqs(
163+
self, medium: AbstractMedium, freqs: List[pd.PositiveFloat]
164+
) -> NonlinearSpec:
165+
"""Update the nonlinear model to hardcode information on medium and freqs."""
166+
return self
167+
162168
def _get_freq0(self, freq0, freqs: List[pd.PositiveFloat]) -> float:
163169
"""Get a single value for freq0."""
164170

@@ -426,6 +432,14 @@ def _validate_medium_freqs(self, medium: AbstractMedium, freqs: List[pd.Positive
426432
"gain medium are unstable, and are likely to diverge."
427433
)
428434

435+
def _hardcode_medium_freqs(
436+
self, medium: AbstractMedium, freqs: List[pd.PositiveFloat]
437+
) -> TwoPhotonAbsorption:
438+
"""Update the nonlinear model to hardcode information on medium and freqs."""
439+
n0 = self._get_n0(n0=self.n0, medium=medium, freqs=freqs)
440+
freq0 = self._get_freq0(freq0=self.freq0, freqs=freqs)
441+
return self.updated_copy(n0=n0, freq0=freq0)
442+
429443
def _validate_medium(self, medium: AbstractMedium):
430444
"""Check that the model is compatible with the medium."""
431445
# if n0 is specified, we can go ahead and validate passivity
@@ -515,6 +529,13 @@ def _validate_medium_freqs(self, medium: AbstractMedium, freqs: List[pd.Positive
515529
"gain medium are unstable, and are likely to diverge."
516530
)
517531

532+
def _hardcode_medium_freqs(
533+
self, medium: AbstractMedium, freqs: List[pd.PositiveFloat]
534+
) -> KerrNonlinearity:
535+
"""Update the nonlinear model to hardcode information on medium and freqs."""
536+
n0 = self._get_n0(n0=self.n0, medium=medium, freqs=freqs)
537+
return self.updated_copy(n0=n0)
538+
518539
def _validate_medium(self, medium: AbstractMedium):
519540
"""Check that the model is compatible with the medium."""
520541
# if n0 is specified, we can go ahead and validate passivity
@@ -584,6 +605,16 @@ def _validate_num_iters(cls, val, values):
584605
)
585606
return val
586607

608+
def _hardcode_medium_freqs(
609+
self, medium: AbstractMedium, freqs: List[pd.PositiveFloat]
610+
) -> NonlinearSpec:
611+
"""Update the nonlinear spec to hardcode information on medium and freqs."""
612+
new_models = []
613+
for model in self.models:
614+
new_model = model._hardcode_medium_freqs(medium=medium, freqs=freqs)
615+
new_models.append(new_model)
616+
return self.updated_copy(models=new_models)
617+
587618

588619
class AbstractMedium(ABC, Tidy3dBaseModel):
589620
"""A medium within which electromagnetic waves propagate."""

tidy3d/components/simulation.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1464,11 +1464,26 @@ def subsection(
14641464
if remove_outside_structures:
14651465
new_structures = [strc for strc in self.structures if new_box.intersects(strc.geometry)]
14661466
else:
1467-
new_structures = self.structures
1467+
new_structures = list(self.structures)
14681468

14691469
if sources is None:
14701470
sources = [src for src in self.sources if new_box.intersects(src)]
14711471

1472+
# some nonlinear materials depend on the central frequency
1473+
# we update them with hardcoded freq0
1474+
freqs = np.array([source.source_time.freq0 for source in self.sources])
1475+
for i, structure in enumerate(new_structures):
1476+
medium = structure.medium
1477+
nonlinear_spec = medium.nonlinear_spec
1478+
if nonlinear_spec is not None:
1479+
new_nonlinear_spec = nonlinear_spec._hardcode_medium_freqs(
1480+
medium=medium, freqs=freqs
1481+
)
1482+
new_structure = structure.updated_copy(
1483+
nonlinear_spec=new_nonlinear_spec, path="medium"
1484+
)
1485+
new_structures[i] = new_structure
1486+
14721487
if monitors is None:
14731488
monitors = [mnt for mnt in self.monitors if new_box.intersects(mnt)]
14741489

tidy3d/plugins/mode/mode_solver.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,20 @@ def data_raw(self) -> ModeSolverData:
341341

342342
def _data_on_yee_grid(self) -> ModeSolverData:
343343
"""Solve for all modes, and construct data with fields on the Yee grid."""
344-
solver = self.reduced_simulation_copy
344+
345+
# we try to do reduced simulation copy for efficiency
346+
# it should never fail -- if it does, this is likely due to an oversight
347+
# in the Simulation.subsection method. but falling back to non-reduced
348+
# simulation prevents unneeded errors in this case
349+
try:
350+
solver = self.reduced_simulation_copy
351+
except Exception as e:
352+
solver = self
353+
log.warning(
354+
"Mode solver reduced_simulation_copy failed. "
355+
"Falling back to non-reduced simulation, which may be slower. "
356+
f"Exception: {str(e)}"
357+
)
345358

346359
_, _solver_coords = solver.plane.pop_axis(
347360
solver._solver_grid.boundaries.to_list, axis=solver.normal_axis

0 commit comments

Comments
 (0)