From 8b36b2fa83276700da444a10d16eac042bb877e5 Mon Sep 17 00:00:00 2001 From: Christopher Hogan Date: Tue, 6 Nov 2018 13:32:51 -0600 Subject: [PATCH] Expose more ldos data (#581) * Expose more ldos data * Rename omega to freqs and make get_ldos_freqs a method of Ldos --- doc/docs/Python_User_Interface.md | 12 ++++++++---- python/meep.i | 31 ++++++++++++++++++++++++++++++- python/simulation.py | 15 ++++++++------- python/tests/ldos.py | 18 ++++++++++++++++++ 4 files changed, 64 insertions(+), 12 deletions(-) diff --git a/doc/docs/Python_User_Interface.md b/doc/docs/Python_User_Interface.md index b76f8960f..65b2a29c6 100644 --- a/doc/docs/Python_User_Interface.md +++ b/doc/docs/Python_User_Interface.md @@ -1180,13 +1180,17 @@ As `load_force_data`, but negates the Fourier-transformed fields after they are Meep can also calculate the LDOS (local density of states) spectrum, as described in [Tutorial/Local Density of States](Python_Tutorials/Local_Density_of_States.md). To do this, you simply pass the following step function to your `run` command: -**`dft_ldos(fcen, df, nfreq)`** +**`Ldos(fcen, df, nfreq)`** — -Compute the power spectrum of the sources (usually a single point dipole source), normalized to correspond to the LDOS, in a frequency bandwidth `df` centered at `fcen`, at `nfreq` frequency points. +Create an LDOS object with frequency bandwidth `df` centered at `fcen`, at `nfreq` frequency points. This can be passed to the `dft_ldos` step function below, and has the properties `freq_min`, `nfreq` and `dfreq`. -**`get_ldos_freqs(ldos)`** +**`freqs()`** — -Given an ldos object, returns a list of the frequencies that it is computing the spectrum for. +Method of `Ldos` that returns a list of the frequencies that this `Ldos` instance is computing the spectrum for. + +**`dft_ldos(fcen=None, df=None, nfreq=None, ldos=None)`** +— +Compute the power spectrum of the sources (usually a single point dipole source), normalized to correspond to the LDOS, in a frequency bandwidth `df` centered at `fcen`, at `nfreq` frequency points. One can also pass in an `ldos` created with `DftLdos` as `dft_ldos(ldos=my_ldos)`. The resulting spectrum is outputted as comma-delimited text, prefixed by `ldos:,`, and is also stored in the `ldos_data` variable of the `Simulation` object after the `run` is complete. diff --git a/python/meep.i b/python/meep.i index 6d3716816..567c7349a 100644 --- a/python/meep.i +++ b/python/meep.i @@ -1200,6 +1200,35 @@ void _get_eigenmode(meep::fields *f, double omega_src, meep::direction d, const bool verbose, double kdom[3]); #endif // HAVE_MPB +// Make omega members of meep::dft_ldos available as 'freq' in python +%extend meep::dft_ldos { + + double get_omega_min() { + return $self->omega_min; + } + double get_domega() { + return $self->domega; + } + int get_Nomega() { + return $self->Nomega; + } + %pythoncode %{ + def freqs(self): + import math + import numpy as np + start = self.omega_min / (2 * math.pi) + stop = start + (self.domega / (2 * math.pi)) * self.Nomega + return np.linspace(start, stop, num=self.Nomega, endpoint=False).tolist() + + __swig_getmethods__["freq_min"] = get_omega_min + __swig_getmethods__["nfreq"] = get_Nomega + __swig_getmethods__["dfreq"] = get_domega + if _newclass: freq_min = property(get_omega_min) + if _newclass: nfreq = property(get_Nomega) + if _newclass: dfreq = property(get_domega) + %} +} + extern boolean point_in_objectp(vector3 p, GEOMETRIC_OBJECT o); extern boolean point_in_periodic_objectp(vector3 p, GEOMETRIC_OBJECT o); void display_geometric_object_info(int indentby, GEOMETRIC_OBJECT o); @@ -1266,6 +1295,7 @@ kpoint_list get_eigenmode_coefficients_and_kpoints(meep::fields *f, meep::dft_fl ) from .simulation import ( Absorber, + Ldos, FluxRegion, ForceRegion, Harminv, @@ -1298,7 +1328,6 @@ kpoint_list get_eigenmode_coefficients_and_kpoints(meep::fields *f, meep::dft_fl get_force_freqs, get_forces, get_near2far_freqs, - get_ldos_freqs, in_point, in_volume, interpolate, diff --git a/python/simulation.py b/python/simulation.py index 8037f3867..0a50fb239 100644 --- a/python/simulation.py +++ b/python/simulation.py @@ -2160,14 +2160,15 @@ def output_sfield_p(sim): sim.output_component(mp.Sp) -def get_ldos_freqs(f): - start = f.omega_min / (2 * math.pi) - stop = start + (f.domega / (2 * math.pi)) * f.Nomega - return np.linspace(start, stop, num=f.Nomega, endpoint=False).tolist() +def Ldos(fcen, df, nfreq): + return mp._dft_ldos(fcen - df / 2, fcen + df / 2, nfreq) -def dft_ldos(fcen, df, nfreq): - ldos = mp._dft_ldos(fcen - df / 2, fcen + df / 2, nfreq) +def dft_ldos(fcen=None, df=None, nfreq=None, ldos=None): + if ldos is None: + if fcen is None or df is None or nfreq is None: + raise ValueError("Either fcen, df, and nfreq, or an Ldos is required for dft_ldos") + ldos = mp._dft_ldos(fcen - df / 2, fcen + df / 2, nfreq) def _ldos(sim, todo): if todo == 'step': @@ -2176,7 +2177,7 @@ def _ldos(sim, todo): sim.ldos_data = mp._dft_ldos_ldos(ldos) sim.ldos_Fdata = mp._dft_ldos_F(ldos) sim.ldos_Jdata = mp._dft_ldos_J(ldos) - display_csv(sim, 'ldos', zip(get_ldos_freqs(ldos), sim.ldos_data)) + display_csv(sim, 'ldos', zip(ldos.freqs(), sim.ldos_data)) return _ldos diff --git a/python/tests/ldos.py b/python/tests/ldos.py index de8d6472e..726e3cd11 100644 --- a/python/tests/ldos.py +++ b/python/tests/ldos.py @@ -1,3 +1,4 @@ +import math import unittest import meep as mp @@ -34,6 +35,23 @@ def test_ldos(self): self.assertAlmostEqual(self.sim.ldos_data[0], 1.011459560620368) + def test_ldos_user_object(self): + ldos = mp.Ldos(self.fcen, 0, 1) + self.sim.run( + mp.dft_ldos(ldos=ldos), + until_after_sources=mp.stop_when_fields_decayed(50, mp.Ez, mp.Vector3(), 1e-6) + ) + + self.assertAlmostEqual(self.sim.ldos_data[0], 1.011459560620368) + freqs = ldos.freqs() + self.assertEqual(ldos.freq_min, freqs[0] * 2 * math.pi) + self.assertEqual(ldos.nfreq, 1) + self.assertEqual(ldos.dfreq, 0) + + def test_invalid_dft_ldos(self): + with self.assertRaises(ValueError): + self.sim.run(mp.dft_ldos(mp.Ldos(self.fcen, 0, 1)), until=200) + if __name__ == '__main__': unittest.main()