Skip to content

Commit

Permalink
add stop_when_energy_decayed termination condition (#2021)
Browse files Browse the repository at this point in the history
* add stop_when_energy_decayed termination condition

* call field_energy_in_box at every dt time units rather than at each timestep
  • Loading branch information
oskooi authored Apr 7, 2022
1 parent d379c0c commit d1167e4
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 35 deletions.
87 changes: 54 additions & 33 deletions doc/docs/Python_User_Interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,40 +87,40 @@ control various parameters of the Meep computation.

```python
def __init__(self,
cell_size:Union[meep.geom.Vector3, Tuple[float, ...], NoneType]=None,
resolution:float=None,
geometry:Union[List[meep.geom.GeometricObject], NoneType]=None,
sources:Union[List[meep.source.Source], NoneType]=None,
eps_averaging:bool=True,
dimensions:int=3,
boundary_layers:Union[List[meep.simulation.PML], NoneType]=None,
symmetries:Union[List[meep.simulation.Symmetry], NoneType]=None,
force_complex_fields:bool=False,
default_material:meep.geom.Medium=Medium(),
m:float=0,
k_point:Union[meep.geom.Vector3, Tuple[float, ...], bool]=False,
kz_2d:str='complex',
extra_materials:Union[List[meep.geom.Medium], NoneType]=None,
material_function:Union[Callable[[Union[meep.geom.Vector3, Tuple[float, ...]]], meep.geom.Medium], NoneType]=None,
epsilon_func:Union[Callable[[Union[meep.geom.Vector3, Tuple[float, ...]]], float], NoneType]=None,
epsilon_input_file:str='',
progress_interval:float=4,
subpixel_tol:float=0.0001,
subpixel_maxeval:int=100000,
loop_tile_base_db:int=0,
loop_tile_base_eh:int=0,
ensure_periodicity:bool=True,
num_chunks:int=0,
Courant:float=0.5,
accurate_fields_near_cylorigin:bool=False,
filename_prefix:Union[str, NoneType]=None,
output_volume:Union[meep.simulation.Volume, NoneType]=None,
output_single_precision:bool=False,
geometry_center:Union[meep.geom.Vector3, Tuple[float, ...]]=Vector3<0.0, 0.0, 0.0>,
force_all_components:bool=False,
split_chunks_evenly:bool=True,
cell_size: Union[meep.geom.Vector3, Tuple[float, ...], NoneType] = None,
resolution: float = None,
geometry: Optional[List[meep.geom.GeometricObject]] = None,
sources: Optional[List[meep.source.Source]] = None,
eps_averaging: bool = True,
dimensions: int = 3,
boundary_layers: Optional[List[meep.simulation.PML]] = None,
symmetries: Optional[List[meep.simulation.Symmetry]] = None,
force_complex_fields: bool = False,
default_material: meep.geom.Medium = Medium(),
m: float = 0,
k_point: Union[meep.geom.Vector3, Tuple[float, ...], bool] = False,
kz_2d: str = 'complex',
extra_materials: Optional[List[meep.geom.Medium]] = None,
material_function: Optional[Callable[[Union[meep.geom.Vector3, Tuple[float, ...]]], meep.geom.Medium]] = None,
epsilon_func: Optional[Callable[[Union[meep.geom.Vector3, Tuple[float, ...]]], float]] = None,
epsilon_input_file: str = '',
progress_interval: float = 4,
subpixel_tol: float = 0.0001,
subpixel_maxeval: int = 100000,
loop_tile_base_db: int = 0,
loop_tile_base_eh: int = 0,
ensure_periodicity: bool = True,
num_chunks: int = 0,
Courant: float = 0.5,
accurate_fields_near_cylorigin: bool = False,
filename_prefix: Optional[str] = None,
output_volume: Optional[meep.simulation.Volume] = None,
output_single_precision: bool = False,
geometry_center: Union[meep.geom.Vector3, Tuple[float, ...]] = Vector3<0.0, 0.0, 0.0>,
force_all_components: bool = False,
split_chunks_evenly: bool = True,
chunk_layout=None,
collect_stats:bool=False):
collect_stats: bool = False):
```

<div class="method_docstring" markdown="1">
Expand Down Expand Up @@ -2843,6 +2843,27 @@ slow group velocities and are absorbed poorly by [PML](Perfectly_Matched_Layer.m

</div>

<a id="stop_when_energy_decayed"></a>

```python
def stop_when_energy_decayed(dt=None, decay_by=None):
```

<div class="function_docstring" markdown="1">

Return a `condition` function, suitable for passing to `Simulation.run` as the `until`
or `until_after_sources` parameter, that examines the field energy over the entire
cell volume at every `dt` time units and keeps incrementing the run time by `dt` until
its absolute value has decayed by at least `decay_by` from its maximum recorded value.

Note that, if you make `decay_by` very small, you may need to increase the `cutoff`
property of your source(s), to decrease the amplitude of the small high-frequency
field components that are excited when the source turns off. High frequencies near the
[Nyquist frequency](https://en.wikipedia.org/wiki/Nyquist_frequency) of the grid have
slow group velocities and are absorbed poorly by [PML](Perfectly_Matched_Layer.md).

</div>

<a id="stop_when_dft_decayed"></a>

```python
Expand Down
1 change: 1 addition & 0 deletions doc/docs/Python_User_Interface.md.in
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,7 @@ A common point of confusion is described in [The Run Function Is Not A Loop](The
In particular, a useful value for `until_after_sources` or `until` is often `stop_when_field_decayed`, which is demonstrated in [Tutorial/Basics](Python_Tutorials/Basics.md#transmittance-spectrum-of-a-waveguide-bend). These top-level functions are available:

@@ stop_when_fields_decayed @@
@@ stop_when_energy_decayed @@
@@ stop_when_dft_decayed @@
@@ stop_after_walltime @@
@@ stop_on_interrupt @@
Expand Down
1 change: 1 addition & 0 deletions python/meep.i
Original file line number Diff line number Diff line change
Expand Up @@ -1833,6 +1833,7 @@ PyObject *_get_array_slice_dimensions(meep::fields *f, const meep::volume &where
stop_on_interrupt,
stop_when_dft_decayed,
stop_when_fields_decayed,
stop_when_energy_decayed,
synchronized_magnetic,
to_appended,
vec,
Expand Down
36 changes: 36 additions & 0 deletions python/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -4514,6 +4514,42 @@ def _stop(sim):
return _stop


def stop_when_energy_decayed(dt=None, decay_by=None):
"""
Return a `condition` function, suitable for passing to `Simulation.run` as the `until`
or `until_after_sources` parameter, that examines the field energy over the entire
cell volume at every `dt` time units and keeps incrementing the run time by `dt` until
its absolute value has decayed by at least `decay_by` from its maximum recorded value.
Note that, if you make `decay_by` very small, you may need to increase the `cutoff`
property of your source(s), to decrease the amplitude of the small high-frequency
field components that are excited when the source turns off. High frequencies near the
[Nyquist frequency](https://en.wikipedia.org/wiki/Nyquist_frequency) of the grid have
slow group velocities and are absorbed poorly by [PML](Perfectly_Matched_Layer.md).
"""
if (dt is None) or (decay_by is None):
raise ValueError("dt and decay_by are all required.")

closure = {
'max_abs': 0,
't0': 0,
}

def _stop(sim):
if sim.round_time() <= dt + closure['t0']:
return False
else:
cell_volume = mp.Volume(center=sim.geometry_center, size=sim.cell_size)
cur_abs = abs(sim.field_energy_in_box(box=cell_volume))
closure['max_abs'] = max(closure['max_abs'], cur_abs)
closure['t0'] = sim.round_time()
if closure['max_abs'] != 0 and verbosity.meep > 0:
fmt = "energy decay(t = {}): {} / {} = {}"
print(fmt.format(sim.meep_time(), cur_abs, closure['max_abs'], cur_abs / closure['max_abs']))
return cur_abs <= closure['max_abs'] * decay_by
return _stop


def stop_after_walltime(t):
"""
Return a `condition` function, suitable for passing to `Simulation.run` as the `until`
Expand Down
4 changes: 2 additions & 2 deletions python/tests/test_bend_flux.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def init(self, no_bend=False, gdsii=False):
def run_bend_flux(self, from_gdsii_file):
# Normalization run
self.init(no_bend=True, gdsii=from_gdsii_file)
self.sim.run(until_after_sources=mp.stop_when_fields_decayed(50, mp.Ez, self.pt, 1e-3))
self.sim.run(until_after_sources=mp.stop_when_energy_decayed(100, 1e-3))
# Save flux data for use in real run below
fdata = self.sim.get_flux_data(self.refl)
fdata_decimated = self.sim.get_flux_data(self.refl_decimated)
Expand Down Expand Up @@ -126,7 +126,7 @@ def run_bend_flux(self, from_gdsii_file):
# Load flux data obtained from normalization run
self.sim.load_minus_flux_data(self.refl, fdata)
self.sim.load_minus_flux_data(self.refl_decimated, fdata_decimated)
self.sim.run(until_after_sources=mp.stop_when_fields_decayed(50, mp.Ez, self.pt, 1e-3))
self.sim.run(until_after_sources=mp.stop_when_energy_decayed(100, 1e-3))

expected = [
(0.09999999999999999, 1.8392235204829767e-5, -7.259467687598002e-6),
Expand Down

0 comments on commit d1167e4

Please sign in to comment.