Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated the medium intersection validator to handle 2D projection monitors without raising errors. #1907

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- Stopped raising error when field projection monitor intersecting structures outside of the simulation domain.

## [2.7.3] - 2024-09-12

### Added
Expand Down
32 changes: 32 additions & 0 deletions tests/test_components/test_simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2977,3 +2977,35 @@ def test_validate_sources_monitors_in_bounds():
grid_spec=td.GridSpec(wavelength=1.0),
monitors=[mode_monitor],
)


def test_2d_projection_monitor_out_sim_domain():
# set up a structure interset with far field monitor outside the computational domain
cylinder = td.Structure(
geometry=td.Cylinder(axis=2, radius=0.5, length=2), medium=td.PECMedium()
)

num_phi = 100
theta = np.pi / 2
phis = np.linspace(0, np.pi, num_phi)
monitor_far = td.FieldProjectionAngleMonitor(
size=[2, 2, 0.5],
freqs=[1e12],
name="far_field",
theta=theta,
phi=phis,
)

sim2d = td.Simulation(
size=(4, 4, 0),
run_time=1e-12,
grid_spec=td.GridSpec.uniform(dl=0.025),
sources=[],
structures=[cylinder],
monitors=[monitor_far],
boundary_spec=td.BoundarySpec(
x=td.Boundary.pml(),
y=td.Boundary.pml(),
z=td.Boundary.periodic(),
),
)
52 changes: 51 additions & 1 deletion tidy3d/components/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@
# height of the PML plotting boxes along any dimensions where sim.size[dim] == 0
PML_HEIGHT_FOR_0_DIMS = inf

# fraction of structure size to monitor along 0 dim in 2D simulations
FRACTION = 0.9

# Minimum size of monitor
MIN_MONITOR_SIZE = 1e-12


class AbstractYeeGridSimulation(AbstractSimulation, ABC):
"""
Expand Down Expand Up @@ -2756,7 +2762,51 @@ def _projection_monitors_homogeneous(cls, val, values):
with log as consolidated_logger:
for monitor_ind, monitor in enumerate(val):
if isinstance(monitor, (AbstractFieldProjectionMonitor, DiffractionMonitor)):
mediums = Scene.intersecting_media(monitor, total_structures)
if monitor.volume() != 0.0:
if not (
all(
monitor_min >= sim_min
for monitor_min, sim_min in zip(
monitor.bounds[0], structure_bg.geometry.bounds[0]
)
)
and all(
monitor_max <= sim_max
for monitor_max, sim_max in zip(
monitor.bounds[1], structure_bg.geometry.bounds[1]
)
)
):
exclude_surfaces = []
dimensions = ["x", "y", "z"]
for i, dim in enumerate(dimensions):
if monitor.bounds[0][i] <= structure_bg.geometry.bounds[0][i]:
exclude_surfaces.append(f"{dim}-")
if monitor.bounds[1][i] >= structure_bg.geometry.bounds[1][i]:
exclude_surfaces.append(f"{dim}+")
new_min = np.maximum(monitor.bounds[0], structure_bg.geometry.bounds[0])
new_max = np.minimum(monitor.bounds[1], structure_bg.geometry.bounds[1])
new_size = tuple(new_max - new_min)
# Add a minimum value to the monitor along 0-dim in 2D simulations
new_size = tuple(
MIN_MONITOR_SIZE if num == 0 else num for num in new_size
)
new_center = tuple((new_max + new_min) / 2)
monitor_test = monitor.updated_copy(center=new_center, size=new_size)
monitor_dict = monitor_test.dict()
# Append exclude surfaces for 2D simulation to existing ones
existing_exclude_surfaces = monitor_dict.get("exclude_surfaces") or []
updated_exclude_surfaces = list(
set(existing_exclude_surfaces + exclude_surfaces)
)
monitor_dict["exclude_surfaces"] = updated_exclude_surfaces
surfaces = monitor_test.surfaces_with_exclusion(**monitor_dict)
mediums = set()
for surface in surfaces:
_mediums = Scene.intersecting_media(surface, total_structures)
mediums.update(_mediums)
else:
mediums = Scene.intersecting_media(monitor, total_structures)
# make sure there is no more than one medium in the returned list
if len(mediums) > 1:
raise SetupError(
Expand Down
Loading