diff --git a/doc/docs/Python_User_Interface.md b/doc/docs/Python_User_Interface.md index 2a739ab3f..aeeaf28c4 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: @@ -7491,18 +7510,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