diff --git a/.gitignore b/.gitignore index 3e6669a..151f2da 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,4 @@ localConfig.cmake python/**/*.cpp !python/doc python/doc/_build +python/doc/Tutorials/__* diff --git a/python/doc/Tutorials/Antenna_Tutorials.rst b/python/doc/Tutorials/Antenna_Tutorials.rst new file mode 100644 index 0000000..4a5927a --- /dev/null +++ b/python/doc/Tutorials/Antenna_Tutorials.rst @@ -0,0 +1,9 @@ +Antennas +-------- + +.. toctree:: + :maxdepth: 1 + + Simple_Patch_Antenna + Helical_Antenna + Bent_Patch_Antenna diff --git a/python/doc/Tutorials/Bent_Patch_Antenna.rst b/python/doc/Tutorials/Bent_Patch_Antenna.rst new file mode 100644 index 0000000..9582563 --- /dev/null +++ b/python/doc/Tutorials/Bent_Patch_Antenna.rst @@ -0,0 +1,35 @@ +Bent Patch Antenna +================== + +* Setup & Simulate a bent patch antenna using a cylindrical mesh + +Introduction +------------- +**This tutorial covers:** + +* Setup of a Bent Patch Antenna (see for comparison: :ref:`simple_patch_antenna`) +* setup of a *cylindrical FDTD mesh*. +* Calculate the S-Parameter and input impedance +* Calculate far-field pattern 2D/3D + + +Python Script +------------- +Get the latest version `from git `_. + +.. include:: ./__Bent_Patch_Antenna.txt + +Images +------------- +.. figure:: images/Bent_Patch.png + :width: 49% + :alt: alternate text + + 3D view of the Bent Patch Antenna (AppCSXCAD) + +.. figure:: images/Bent_Patch_Pattern.png + :width: 80% + :alt: Farfield pattern + + Farfield pattern on an xy- and xz-plane + diff --git a/python/doc/Tutorials/Helical_Antenna.rst b/python/doc/Tutorials/Helical_Antenna.rst new file mode 100644 index 0000000..6a94081 --- /dev/null +++ b/python/doc/Tutorials/Helical_Antenna.rst @@ -0,0 +1,32 @@ +Helical Antenna +=============== + +Introduction +------------- +**This tutorial covers:** + +* setup of a helix using the wire primitive +* setup a lumped feeding port (R_in = 120 Ohms) +* adding a near-field to far-field (nf2ff) box using an efficient subsampling +* calculate the S-Parameter of the antenna +* calculate and plot the far-field pattern + +Python Script +------------- +Get the latest version `from git `_. + +.. include:: ./__Helical_Antenna.txt + +Images +------------- +.. figure:: images/Helix_Ant.png + :width: 49% + :alt: alternate text + + 3D view of the Helical Antenna (AppCSXCAD) + +.. figure:: images/Helix_Ant_Pattern.png + :width: 49% + :alt: alternate text + + Far-Field pattern showing a right-handed circular polarization. diff --git a/python/doc/Tutorials/Intro_Tutorials.rst b/python/doc/Tutorials/Intro_Tutorials.rst new file mode 100644 index 0000000..f73b15d --- /dev/null +++ b/python/doc/Tutorials/Intro_Tutorials.rst @@ -0,0 +1,10 @@ +.. _intro_tutorials: + +Introductional Tutorials +------------------------ + + +.. toctree:: + + Rect_Waveguide + MSL_NotchFilter diff --git a/python/doc/Tutorials/MSL_NotchFilter.rst b/python/doc/Tutorials/MSL_NotchFilter.rst new file mode 100644 index 0000000..c84883e --- /dev/null +++ b/python/doc/Tutorials/MSL_NotchFilter.rst @@ -0,0 +1,27 @@ +Microstrip Notch Filter +======================= + + * A straight MSL line with a open-ended stub to create a simple microwave filter. + +Introduction +------------- +**This tutorial covers:** + + +* Setup a mircostrip line (MSL) and MSL port +* Apply an inhomogeneous mesh used for improved accuracy and simulation speed +* Calculate the S-Parameter of the filter + +Python Script +------------- +Get the latest version `from git `_. + +.. include:: ./__MSL_NotchFilter.txt + +Images +------------- +.. figure:: images/Notch_Filter_SPara.png + :width: 49% + :alt: S-Parameter over Frequency + + S-Parameter over Frequency diff --git a/python/doc/Tutorials/Rect_Waveguide.rst b/python/doc/Tutorials/Rect_Waveguide.rst new file mode 100644 index 0000000..8b955ac --- /dev/null +++ b/python/doc/Tutorials/Rect_Waveguide.rst @@ -0,0 +1,27 @@ +Rectangular Waveguide +===================== + + * A simple rectangular waveguide, showing the openEMS mode profile capabilities. + +Introduction +------------- +**This tutorial covers:** + +* Setup a mode profile excitation +* Create voltage and current probes using the mode profile +* Calculate the waveguide impedance and S-Parameter + + +Python Script +------------- +Get the latest version `from git `_. + +.. include:: ./__Rect_Waveguide.txt + +Images +------------- +.. figure:: images/Rect_WG_SPara.png + :width: 49% + :alt: S-Parameter over Frequency + + S-Parameter over Frequency diff --git a/python/doc/Tutorials/Simple_Patch_Antenna.rst b/python/doc/Tutorials/Simple_Patch_Antenna.rst new file mode 100644 index 0000000..4345c56 --- /dev/null +++ b/python/doc/Tutorials/Simple_Patch_Antenna.rst @@ -0,0 +1,42 @@ +.. _simple_patch_antenna: + +Simple Patch Antenna +==================== + +Introduction +------------ +A simple patch antenna for 2.4 GHz. + +**This tutorial covers:** + +* Setup a patch, substrate and ground. +* Setup of a lumped feeding port. +* Adding a near-field to far-field (nf2ff) recording box. +* Calculate the S-Parameter of the antenna. +* Calculate and plot the far-field pattern + +Python Script +------------- +Get the latest version `from git `_. + +.. include:: ./__Simple_Patch_Antenna.txt + +Images +------ +.. figure:: images/Simp_Patch_S11.png + :width: 49% + :alt: S11 over Frequency + + S-Parameter over Frequency + +.. figure:: images/Simp_Patch_Zin.png + :width: 49% + :alt: Input Impedance + + Antenna Input Impedance + +.. figure:: images/Simp_Patch_Pattern.png + :width: 49% + :alt: Farfield pattern + + Farfield pattern for the xy- and yz-plane. diff --git a/python/doc/Tutorials/images/Bent_Patch.png b/python/doc/Tutorials/images/Bent_Patch.png new file mode 100644 index 0000000..0beddf9 Binary files /dev/null and b/python/doc/Tutorials/images/Bent_Patch.png differ diff --git a/python/doc/Tutorials/images/Bent_Patch_Pattern.png b/python/doc/Tutorials/images/Bent_Patch_Pattern.png new file mode 100644 index 0000000..4fc3211 Binary files /dev/null and b/python/doc/Tutorials/images/Bent_Patch_Pattern.png differ diff --git a/python/doc/Tutorials/images/Bent_Patch_SPara.png b/python/doc/Tutorials/images/Bent_Patch_SPara.png new file mode 100644 index 0000000..d3970fb Binary files /dev/null and b/python/doc/Tutorials/images/Bent_Patch_SPara.png differ diff --git a/python/doc/Tutorials/images/Helix_Ant.png b/python/doc/Tutorials/images/Helix_Ant.png new file mode 100644 index 0000000..16c532c Binary files /dev/null and b/python/doc/Tutorials/images/Helix_Ant.png differ diff --git a/python/doc/Tutorials/images/Helix_Ant_Pattern.png b/python/doc/Tutorials/images/Helix_Ant_Pattern.png new file mode 100644 index 0000000..020aae1 Binary files /dev/null and b/python/doc/Tutorials/images/Helix_Ant_Pattern.png differ diff --git a/python/doc/Tutorials/images/Notch_Filter_SPara.png b/python/doc/Tutorials/images/Notch_Filter_SPara.png new file mode 100644 index 0000000..9d1d7be Binary files /dev/null and b/python/doc/Tutorials/images/Notch_Filter_SPara.png differ diff --git a/python/doc/Tutorials/images/Rect_WG_SPara.png b/python/doc/Tutorials/images/Rect_WG_SPara.png new file mode 100644 index 0000000..0c0ff27 Binary files /dev/null and b/python/doc/Tutorials/images/Rect_WG_SPara.png differ diff --git a/python/doc/Tutorials/images/Simp_Patch_Pattern.png b/python/doc/Tutorials/images/Simp_Patch_Pattern.png new file mode 100644 index 0000000..2cd90d4 Binary files /dev/null and b/python/doc/Tutorials/images/Simp_Patch_Pattern.png differ diff --git a/python/doc/Tutorials/images/Simp_Patch_S11.png b/python/doc/Tutorials/images/Simp_Patch_S11.png new file mode 100644 index 0000000..381bb3c Binary files /dev/null and b/python/doc/Tutorials/images/Simp_Patch_S11.png differ diff --git a/python/doc/Tutorials/images/Simp_Patch_Zin.png b/python/doc/Tutorials/images/Simp_Patch_Zin.png new file mode 100644 index 0000000..bbabe54 Binary files /dev/null and b/python/doc/Tutorials/images/Simp_Patch_Zin.png differ diff --git a/python/doc/Tutorials/index.rst b/python/doc/Tutorials/index.rst new file mode 100644 index 0000000..2262109 --- /dev/null +++ b/python/doc/Tutorials/index.rst @@ -0,0 +1,11 @@ +.. _tutorials: + +######### +Tutorials +######### + +.. toctree:: + :maxdepth: 2 + + Intro_Tutorials + Antenna_Tutorials diff --git a/python/doc/conf.py b/python/doc/conf.py index 455d910..b5c7b76 100644 --- a/python/doc/conf.py +++ b/python/doc/conf.py @@ -291,6 +291,7 @@ texinfo_documents = [ # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False +numpydoc_show_class_members = False # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'CSXCAD': ('http://openems.de/doc/CSXCAD/', None)} diff --git a/python/doc/convert_tutorials.py b/python/doc/convert_tutorials.py new file mode 100644 index 0000000..2996aac --- /dev/null +++ b/python/doc/convert_tutorials.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +""" +Created on Sat Sep 10 17:12:53 2016 + +@author: thorsten +""" + +import os +import glob + +DOC_DIR = os.path.dirname(__file__) +ROOT_DIR = os.path.join(DOC_DIR, '..') + +def main(): + in_path = os.path.join(ROOT_DIR, 'Tutorials') + + fns = glob.glob(os.path.join(in_path, '*.py')) + + for fn in fns: + bn = os.path.basename(fn) + out_fn = os.path.join(DOC_DIR, 'Tutorials', '__' + bn.replace('.py', '.txt')) + + in_code_block = False + in_ignore_block = False + out_fh = open(out_fn, 'w') + for line in open(fn, 'r'): + if in_ignore_block==False and line.startswith('"""'): + in_ignore_block = True + in_code_block = False + continue + elif in_ignore_block==True and line.startswith('"""'): + in_ignore_block = False + in_code_block = False + continue + elif in_ignore_block==True: + in_code_block = False + continue + elif line.startswith('# -*-'): + continue + elif not line.startswith('##'): + if not in_code_block: + if len(line.strip())==0: + continue + out_fh.write('\n.. code-block:: python\n\n') + in_code_block = True + out_fh.write(' ' + line) + elif line.startswith('###'): + if in_code_block: + out_fh.write('\n') + in_code_block = False + line = line.replace('#','').strip() + out_fh.write('**' + line + '**\n\n') +# out_fh.write('"'*len(line) + '\n') + elif line.startswith('##'): + if in_code_block: + out_fh.write('\n') + in_code_block = False + out_fh.write(line.replace('#','').strip() + '\n') + out_fh.close() + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/python/doc/index.rst b/python/doc/index.rst index 13aa7aa..f4cb9ea 100644 --- a/python/doc/index.rst +++ b/python/doc/index.rst @@ -9,9 +9,11 @@ Welcome to openEMS's documentation! Contents: .. toctree:: - :maxdepth: 4 + :maxdepth: 3 + + Tutorials/index + openEMS_API - openEMS Indices and tables ================== diff --git a/python/doc/nf2ff.rst b/python/doc/nf2ff.rst new file mode 100644 index 0000000..c4fbb01 --- /dev/null +++ b/python/doc/nf2ff.rst @@ -0,0 +1,16 @@ +.. _nf2ff: + +NF2FF +----- + +.. automodule:: openEMS.nf2ff + + NF2FF + ----- + .. autoclass:: nf2ff + :members: + + NF2FF Results + ----------------- + .. autoclass:: nf2ff_results + :members: diff --git a/python/doc/openEMS.rst b/python/doc/openEMS.rst index 1754eee..c73dece 100644 --- a/python/doc/openEMS.rst +++ b/python/doc/openEMS.rst @@ -1,59 +1,8 @@ -openEMS Python Interface -======================== +.. _openems: openEMS ------- -.. automodule:: openEMS.openEMS +.. automodule:: openEMS :members: openEMS :undoc-members: - -Ports ------ - -.. automodule:: openEMS.ports - - Port (Base Class) - ----------------- - .. autoclass:: Port - :members: - :show-inheritance: - - Lumped Port - ----------- - .. autoclass:: LumpedPort - :members: - :show-inheritance: - - MSL Port - -------- - .. autoclass:: MSLPort - :members: - :show-inheritance: - - Waveguide Port - -------------- - .. autoclass:: WaveguidePort - :members: - :show-inheritance: - - Rect Waveguide Port - ------------------- - .. autoclass:: RectWGPort - :members: - :show-inheritance: - -NF2FF ------ - -.. automodule:: openEMS.nf2ff - - NF2FF - ----- - .. autoclass:: nf2ff - :members: - - NF2FF Results - ----------------- - .. autoclass:: nf2ff_results - :members: diff --git a/python/doc/openEMS_API.rst b/python/doc/openEMS_API.rst new file mode 100644 index 0000000..ae7e381 --- /dev/null +++ b/python/doc/openEMS_API.rst @@ -0,0 +1,11 @@ +.. _openems_api: + +openEMS Python Interface +======================== + +.. toctree:: + + openEMS + ports + nf2ff + diff --git a/python/doc/ports.rst b/python/doc/ports.rst new file mode 100644 index 0000000..79971e6 --- /dev/null +++ b/python/doc/ports.rst @@ -0,0 +1,37 @@ +.. _ports: + +Ports +----- + +.. automodule:: openEMS.ports + + Port (Base Class) + ----------------- + .. autoclass:: Port + :members: + :show-inheritance: + + Lumped Port + ----------- + .. autoclass:: LumpedPort + :members: + :show-inheritance: + + MSL Port + -------- + .. autoclass:: MSLPort + :members: + :show-inheritance: + + Waveguide Port + -------------- + .. autoclass:: WaveguidePort + :members: + :show-inheritance: + + Rect Waveguide Port + ------------------- + .. autoclass:: RectWGPort + :members: + :show-inheritance: + diff --git a/python/openEMS/nf2ff.py b/python/openEMS/nf2ff.py index 6b428d4..6686659 100644 --- a/python/openEMS/nf2ff.py +++ b/python/openEMS/nf2ff.py @@ -23,6 +23,17 @@ from openEMS import _nf2ff from openEMS import utilities class nf2ff: + """ + Create an nf2ff recording box. The nf2ff can either record in time-domain + or frequency-domain. Further more certain directions and boundary condition + mirroring can be enabled/disabled. + + :param name: str -- Name for this recording box. + :param start/stop: (3,) array -- Box start/stop coordinates. + :param directions: (6,) bool array -- Enable/Disables directions. + :param mirror: (6,) int array -- 0 (Off), 1 (PEC) or 2 (PMC) boundary mirroring + :param frequency: array like -- List of frequencies (FD-domain recording) + """ def __init__(self, CSX, name, start, stop, **kw): self.CSX = CSX self.name = name @@ -81,6 +92,20 @@ class nf2ff: CSX.AddBox(self.h_dump, l_start, l_stop) def CalcNF2FF(self, sim_path, freq, theta, phi, center=[0,0,0], outfile=None, read_cached=True, verbose=0): + """ CalcNF2FF(sim_path, freq, theta, phi, center=[0,0,0], outfile=None, read_cached=True, verbose=0): + + Calculate the far-field after the simulation is done. + + :param sim_path: str -- Simulation path + :param freq: array like -- list of frequency for transformation + :param theta/phi: array like -- Theta/Phi angles to calculate the far-field + :param center: (3,) array -- phase center, must be inside the recording box + :param outfile: str -- File to save results in. (defaults to recording name) + :param read_cached: bool -- enable/disable read already existing results + :param verbose: int -- set verbose level + + :returns: nf2ff_results class instance + """ if np.isscalar(freq): freq = [freq] self.freq = freq @@ -119,6 +144,27 @@ class nf2ff: return result class nf2ff_results: + """ + nf2ff result class containing all results obtained by the nf2ff calculation. + Usueally returned from nf2ff.CalcNF2FF + + Available attributes: + + * `fn` : file name + * `theta`: theta angles + * `phi` : phi angles + * `r` : radius + * `freq` : frequencies + * `Dmax` : directivity over frequency + * `Prad` : total radiated power over frequency + + * `E_theta` : theta component of electric field over frequency/theta/phi + * `E_phi` : phi component of electric field over frequency/theta/phi + * `E_norm` : abs component of electric field over frequency/theta/phi + * `E_cprh` : theta component of electric field over frequency/theta/phi + * `E_cplh` : theta component of electric field over frequency/theta/phi + * `P_rad` : radiated power (S) over frequency/theta/phi + """ def __init__(self, fn): self.fn = fn h5_file = h5py.File(fn, 'r') diff --git a/python/openEMS/openEMS.pyx b/python/openEMS/openEMS.pyx index 24d3981..715c9d5 100644 --- a/python/openEMS/openEMS.pyx +++ b/python/openEMS/openEMS.pyx @@ -24,6 +24,30 @@ from . import ports, nf2ff cdef class openEMS: """ openEMS + This class is the main control class for the FDTD options and setup and + to run the final simulation. + + Examples + -------- + + >>> CSX = CSXCAD.ContinuousStructure() + >>> + >>> grid = CSX.GetGrid() + >>> grid.SetLines('x', np.arange(-50,50,1)) + >>> grid.SetLines('y', np.arange(-50,50,1)) + >>> grid.SetLines('z', np.arange(-2,2.1,1)) + >>> grid.SetDeltaUnit(1e-3) + >>> + >>> FDTD = openEMS(NrTS=1e4, EndCriteria=1e-4) + >>> + >>> FDTD.SetCSX(CSX) + >>> FDTD.SetBoundaryCond(['PML_8', 'PML_8', 'PML_8', 'PML_8', 'PEC', 'PEC']) + >>> FDTD.SetGaussExcite(0, 10e9) + >>> + >>> FDTD.AddLumpedPort(port_nr=1, R=50, start=[10, 0, -2], stop=[10, 0, 2], p_dir='z', excite=1) + >>> + >>> FDTD.Run(sim_path='/tmp/test') + :param NrTS: max. number of timesteps to simulate (e.g. default=1e9) :param EndCriteria: end criteria, e.g. 1e-5, simulations stops if energy has decayed by this value (<1e-4 is recommended, default=1e-5) :param MaxTime: max. real time in seconds to simulate @@ -300,7 +324,7 @@ cdef class openEMS: See Also -------- - CSXCAD.CSXCAD.ContinuousStructure + CSXCAD.ContinuousStructure """ self.__CSX = CSX self.thisptr.SetCSX(CSX.thisptr) diff --git a/python/openEMS/ports.py b/python/openEMS/ports.py index 56bc8fc..cc14da3 100644 --- a/python/openEMS/ports.py +++ b/python/openEMS/ports.py @@ -41,6 +41,19 @@ class UI_data: # Port Base-Class class Port: + """ + The port base class. + + :param CSX: Continuous Structure + :param port_nr: int -- port number + :param R: float -- port reference impedance, e.g. 50 (Ohms) + :param start, stop: (3,) array -- Start/Stop box coordinates + :param p_dir: int -- port direction + :param excite: float -- port excitation amplitude + :param priority: int -- priority of all contained primtives + :param PortNamePrefix: str -- a prefix for all ports-names + :param delay: float -- a positiv delay value to e.g. emulate a phase shift + """ def __init__(self, CSX, port_nr, start, stop, excite, **kw): self.CSX = CSX self.number = port_nr @@ -123,12 +136,9 @@ class LumpedPort(Port): """ The lumped port. - :param CSX: Continuous Structure - :param port_nr: int -- port number - :param R: float -- port reference impedance, e.g. 50 (Ohms) - :param start, stop: (3,) array -- Start/Stop box coordinates - :param p_dir: int -- port direction - :param excite: float -- port excitation amplitude + See Also + -------- + Port """ def __init__(self, CSX, port_nr, R, start, stop, exc_dir, excite=0, **kw): super(LumpedPort, self).__init__(CSX, port_nr=port_nr, start=start, stop=stop, excite=excite, **kw) @@ -175,6 +185,15 @@ class LumpedPort(Port): super(LumpedPort, self).CalcPort(sim_path, freq, ref_impedance, ref_plane_shift, signal_type) class MSLPort(Port): + """ + The microstrip transmission line port. + + :param prop_dir: int/str -- direction of propagation + + See Also + -------- + Port + """ def __init__(self, CSX, port_nr, metal_prop, start, stop, prop_dir, exc_dir, excite=0, **kw): super(MSLPort, self).__init__(CSX, port_nr=port_nr, start=start, stop=stop, excite=excite, **kw) self.exc_ny = CheckNyDir(exc_dir) @@ -287,6 +306,16 @@ class MSLPort(Port): self.Z_ref = np.sqrt(Et * dEt / (Ht * dHt)) class WaveguidePort(Port): + """ + Base class for any waveguide port. + + :param mode_name: str -- Mode name, e.g. TE11 or TM10 + + See Also + -------- + Port, RectWGPort + + """ def __init__(self, CSX, port_nr, start, stop, exc_dir, mode_name, excite=0, **kw): super(WaveguidePort, self).__init__(CSX, port_nr=port_nr, start=start, stop=stop, excite=excite, **kw) self.exc_ny = CheckNyDir(exc_dir) @@ -324,6 +353,9 @@ class WaveguidePort(Port): CSX.AddBox(i_probe, m_start, m_stop) def InitMode(self, wg_mode): + """ + Init/Define waveguide mode, e.g. TE11, TM10. + """ self.WG_mode = wg_mode assert len(self.WG_mode)==4, 'Invalid mode definition' self.unit = self.CSX.GetGrid().GetDeltaUnit() @@ -348,11 +380,24 @@ class WaveguidePort(Port): super(WaveguidePort, self).CalcPort(sim_path, freq, ref_impedance, ref_plane_shift, signal_type) class RectWGPort(WaveguidePort): + """ + Rectangular waveguide port. + + :param a,b: float -- Width/Height of rectangular waveguide port + + See Also + -------- + Port, WaveguidePort + + """ def __init__(self, CSX, port_nr, start, stop, exc_dir, a, b, mode_name, excite=0, **kw): self.WG_size = [a, b] super(RectWGPort, self).__init__(CSX, port_nr=port_nr, start=start, stop=stop, exc_dir=exc_dir, mode_name=mode_name, excite=excite, **kw) def InitMode(self, wg_mode): + """ + Init/Define rectangular waveguide mode, e.g. TE11, TM10. + """ super(RectWGPort, self).InitMode(wg_mode) assert self.TE, 'Currently only TE-modes are supported! Mode found: {}'.format(self.WG_mode)