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

minor fix bloch validator #1930

Merged
merged 1 commit into from
Aug 30, 2024
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Error in `CustomSourceTime` when evaluating at a list of times entirely outside of the range of the envelope definition times.
- Improved passivity enforcement near high-Q poles in `FastDispersionFitter`. Failed passivity enforcement could lead to simulation divergences.
- More helpful error and suggestion if users try to differentiate w.r.t. unsupported `FluxMonitor` output.
- Removed positive warnings in Simulation validators for Bloch boundary conditions.

## [2.7.2] - 2024-08-07

Expand Down
2 changes: 1 addition & 1 deletion docs/notebooks
Submodule notebooks updated 116 files
9 changes: 9 additions & 0 deletions tests/test_components/test_simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,14 @@ def test_validate_plane_wave_boundaries(log_capture):
angle_theta=np.pi / 4,
)

mnt = td.DiffractionMonitor(
center=(0, 0, 0),
size=(td.inf, td.inf, 0),
freqs=[250e12, 300e12],
name="monitor_diffraction",
normal_dir="+",
)

bspec1 = td.BoundarySpec(
x=td.Boundary.pml(),
y=td.Boundary.absorber(),
Expand Down Expand Up @@ -480,6 +488,7 @@ def test_validate_plane_wave_boundaries(log_capture):
run_time=1e-12,
sources=[src2],
boundary_spec=bspec3,
monitors=[mnt],
)

# angled incidence plane wave with wrong Bloch vector should warn
Expand Down
53 changes: 49 additions & 4 deletions tidy3d/components/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2323,6 +2323,46 @@ def plane_wave_boundaries(cls, val, values):
)
return val

@pydantic.validator("monitors", always=True)
@skip_if_fields_missing(["boundary_spec", "medium", "size", "structures", "sources"])
def bloch_boundaries_diff_mnt(cls, val, values):
"""Error if there are diffraction monitors incompatible with boundary conditions."""

monitors = val

if not val or not any(isinstance(mnt, DiffractionMonitor) for mnt in monitors):
return val

boundaries = values.get("boundary_spec").to_list
sources = values.get("sources")
size = values.get("size")
sim_medium = values.get("medium")
structures = values.get("structures")
for source_ind, source in enumerate(sources):
if not isinstance(source, PlaneWave):
continue

_, tan_dirs = cls.pop_axis([0, 1, 2], axis=source.injection_axis)
medium_set = Scene.intersecting_media(source, structures)
medium = medium_set.pop() if medium_set else sim_medium

for tan_dir in tan_dirs:
boundary = boundaries[tan_dir]

# check the Bloch boundary + angled plane wave case
num_bloch = sum(isinstance(bnd, (Periodic, BlochBoundary)) for bnd in boundary)
if num_bloch > 0:
cls._check_bloch_vec(
source=source,
source_ind=source_ind,
bloch_vec=boundary[0].bloch_vec,
dim=tan_dir,
medium=medium,
domain_size=size[tan_dir],
has_diff_mnt=True,
)
return val

@pydantic.validator("boundary_spec", always=True)
@skip_if_fields_missing(["medium", "center", "size", "structures", "sources"])
def tfsf_boundaries(cls, val, values):
Expand Down Expand Up @@ -3774,6 +3814,7 @@ def _check_bloch_vec(
dim: Axis,
medium: MediumType,
domain_size: float,
has_diff_mnt: bool = False,
):
"""Helper to check if a given Bloch vector is consistent with a given source."""

Expand All @@ -3786,23 +3827,27 @@ def _check_bloch_vec(
if bloch_vec != expected_bloch_vec:
test_val = np.real(expected_bloch_vec - bloch_vec)

if np.isclose(test_val % 1, 0) and not np.isclose(test_val, 0):
test_val_is_int = np.isclose(test_val, np.round(test_val))
src_name = f" '{source.name}'" if source.name else ""

if has_diff_mnt and test_val_is_int and not np.isclose(test_val, 0):
# the given Bloch vector is offset by an integer
log.warning(
tylerflex marked this conversation as resolved.
Show resolved Hide resolved
f"The wave vector of source '{source.name}' along dimension "
f"The wave vector of source{src_name} along dimension "
f"'{dim}' is equal to the Bloch vector of the simulation "
"boundaries along that dimension plus an integer reciprocal "
"lattice vector. If using a 'DiffractionMonitor', diffraction "
"order 0 will not correspond to the angle of propagation "
"of the source. Consider using 'BlochBoundary.from_source()'.",
custom_loc=["boundary_spec", "xyz"[dim]],
)
elif not np.isclose(test_val % 1, 0):

if not test_val_is_int:
# the given Bloch vector is neither equal to the expected value, nor
# off by an integer
log.warning(
f"The Bloch vector along dimension '{dim}' may be incorrectly "
f"set with respect to the source '{source.name}'. The absolute "
f"set with respect to the source{src_name}. The absolute "
tylerflex marked this conversation as resolved.
Show resolved Hide resolved
"difference between the expected and provided values in "
"bandstructure units, up to an integer offset, is greater than "
"1e-6. Consider using ``BlochBoundary.from_source()``, or "
Expand Down
Loading