diff --git a/doc/docs/Python_User_Interface.md b/doc/docs/Python_User_Interface.md
index 4724e8af2..31e212d08 100644
--- a/doc/docs/Python_User_Interface.md
+++ b/doc/docs/Python_User_Interface.md
@@ -2685,18 +2685,18 @@ In particular, a useful value for `until_after_sources` or `until` is often `sto
```python
-def stop_when_fields_decayed(dt, c, pt, decay_by):
+def stop_when_fields_decayed(dt=None, c=None, pt=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 component `c` (e.g. `Ex`, etc.)
+or `until_after_sources` parameter, that examines the component `c` (e.g. `meep.Ex`, etc.)
at the point `pt` (a `Vector3`) and keeps running until its absolute value *squared*
has decayed by at least `decay_by` from its maximum previous value. In particular, it
-keeps incrementing the run time by `dT` (in Meep units) and checks the maximum value
+keeps incrementing the run time by `dt` (in Meep units) and checks the maximum value
over that time period — in this way, it won't be fooled just because the field
-happens to go through 0 at some instant.
+happens to go through zero at some instant.
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
@@ -2706,6 +2706,26 @@ slow group velocities and are absorbed poorly by [PML](Perfectly_Matched_Layer.m
+
+
+```python
+def stop_when_dft_decayed(tol=1e-11,
+ minimum_run_time=0,
+ maximum_run_time=None):
+```
+
+
+
+Return a `condition` function, suitable for passing to `Simulation.run` as the `until`
+or `until_after_sources` parameter, that checks the `Simulation`'s dft objects every `dt`
+timesteps, and stops the simulation once all the field components and frequencies of *every*
+dft object have decayed by at least some tolerance `tol` (default is 1e-11). The time interval
+`dt` is determined automatically based on the frequency content in the DFT monitors.
+There are two optional parameters: a minimum run time `minimum_run_time` (default: 0) or a
+maximum run time `maximum_run_time` (no default).
+
+
+
```python
@@ -2734,7 +2754,6 @@ follows the `run` function (e.g., outputting fields).
-
Finally, another run function, useful for computing ω(**k**) band diagrams, is available via these `Simulation` methods:
@@ -7506,18 +7525,18 @@ fr = mp.FluxRegion(volume=mp.GDSII_vol(fname, layer, zmin, zmax))
```python
-def stop_when_fields_decayed(dt, c, pt, decay_by):
+def stop_when_fields_decayed(dt=None, c=None, pt=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 component `c` (e.g. `Ex`, etc.)
+or `until_after_sources` parameter, that examines the component `c` (e.g. `meep.Ex`, etc.)
at the point `pt` (a `Vector3`) and keeps running until its absolute value *squared*
has decayed by at least `decay_by` from its maximum previous value. In particular, it
-keeps incrementing the run time by `dT` (in Meep units) and checks the maximum value
+keeps incrementing the run time by `dt` (in Meep units) and checks the maximum value
over that time period — in this way, it won't be fooled just because the field
-happens to go through 0 at some instant.
+happens to go through zero at some instant.
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
diff --git a/doc/docs/Python_User_Interface.md.in b/doc/docs/Python_User_Interface.md.in
index 4fa2065a9..9affaf435 100644
--- a/doc/docs/Python_User_Interface.md.in
+++ b/doc/docs/Python_User_Interface.md.in
@@ -450,10 +450,10 @@ 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_dft_decayed @@
@@ stop_after_walltime @@
@@ stop_on_interrupt @@
-
Finally, another run function, useful for computing ω(**k**) band diagrams, is available via these `Simulation` methods:
@@ Simulation.run_k_points @@
diff --git a/python/simulation.py b/python/simulation.py
index e71c41ab0..568995705 100644
--- a/python/simulation.py
+++ b/python/simulation.py
@@ -4354,15 +4354,15 @@ def _to_appended(sim, todo):
return _to_appended
-def stop_when_fields_decayed(dt, c, pt, decay_by):
+def stop_when_fields_decayed(dt=None, c=None, pt=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 component `c` (e.g. `Ex`, etc.)
+ or `until_after_sources` parameter, that examines the component `c` (e.g. `meep.Ex`, etc.)
at the point `pt` (a `Vector3`) and keeps running until its absolute value *squared*
has decayed by at least `decay_by` from its maximum previous value. In particular, it
- keeps incrementing the run time by `dT` (in Meep units) and checks the maximum value
+ keeps incrementing the run time by `dt` (in Meep units) and checks the maximum value
over that time period — in this way, it won't be fooled just because the field
- happens to go through 0 at some instant.
+ happens to go through zero at some instant.
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
@@ -4370,6 +4370,9 @@ def stop_when_fields_decayed(dt, c, pt, decay_by):
[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 (c is None) or (pt is None) or (decay_by is None):
+ raise ValueError("dt, c, pt, and decay_by are all required.")
+
closure = {
'max_abs': 0,
'cur_max': 0,
@@ -4428,18 +4431,18 @@ def _stop(sim):
return _stop
-def stop_when_dft_decayed(tol, minimum_run_time=0, maximum_run_time=None):
+def stop_when_dft_decayed(tol=1e-11, minimum_run_time=0, maximum_run_time=None):
"""
- Return a `condition` function, suitable for passing to `sim.run()` as the `until`
- or `until_after_sources` parameter, that checks all of the Simulation's dft objects
- every `dt` timesteps, and stops the simulation once all the components and frequencies
- of *every* dft object have decayed by at least some tolerance, `tol`. The routine
- calculates the optimal `dt` to use based on the frequency content in the DFT monitors.
-
- Optionally the user can specify a minimum run time (`minimum_run_time`) or a maximum
- run time (`maximum_run_time`).
+ Return a `condition` function, suitable for passing to `Simulation.run` as the `until`
+ or `until_after_sources` parameter, that checks the `Simulation`'s dft objects every `dt`
+ timesteps, and stops the simulation once all the field components and frequencies of *every*
+ dft object have decayed by at least some tolerance `tol` (default is 1e-11). The time interval
+ `dt` is determined automatically based on the frequency content in the DFT monitors.
+ There are two optional parameters: a minimum run time `minimum_run_time` (default: 0) or a
+ maximum run time `maximum_run_time` (no default).
"""
- # Record data in closure so that we can persitently edit
+
+ # Record data in closure so that we can persistently edit
closure = {'previous_fields':0, 't0':0, 'dt':0, 'maxchange':0}
def _stop(_sim):
if _sim.fields.t == 0:
@@ -4457,11 +4460,11 @@ def _stop(_sim):
if previous_fields == 0:
closure['previous_fields'] = current_fields
return False
-
+
closure['previous_fields'] = current_fields
closure['t0'] = _sim.fields.t
- if mp.verbosity > 0:
- fmt = "DFT decay(t = {0:1.1f}): {1:0.4e}"
+ if mp.verbosity > 1:
+ fmt = "DFT fields decay(t = {0:0.2f}): {1:0.4e}"
print(fmt.format(_sim.meep_time(), np.real(change/closure['maxchange'])))
return (change/closure['maxchange']) <= tol and _sim.round_time() >= minimum_run_time