fieldplot.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. #!/usr/bin/env python
  2. # -*- coding: UTF-8 -*-
  3. #
  4. # Copyright (C) 2009-2015 Ovidio Peña Rodríguez <ovidio@bytesfall.com>
  5. # Copyright (C) 2013-2015 Konstantin Ladutenko <kostyfisik@gmail.com>
  6. #
  7. # This file is part of python-scattnlay
  8. #
  9. # This program is free software: you can redistribute it and/or modify
  10. # it under the terms of the GNU General Public License as published by
  11. # the Free Software Foundation, either version 3 of the License, or
  12. # (at your option) any later version.
  13. #
  14. # This program is distributed in the hope that it will be useful,
  15. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. # GNU General Public License for more details.
  18. #
  19. # The only additional remark is that we expect that all publications
  20. # describing work using this software, or all commercial products
  21. # using it, cite the following reference:
  22. # [1] O. Pena and U. Pal, "Scattering of electromagnetic radiation by
  23. # a multilayered sphere," Computer Physics Communications,
  24. # vol. 180, Nov. 2009, pp. 2348-2354.
  25. #
  26. # You should have received a copy of the GNU General Public License
  27. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  28. # Several functions to plot field and streamlines (power flow lines).
  29. from scattnlay import fieldnlay, scattnlay
  30. import numpy as np
  31. ###############################################################################
  32. def GetCoords(crossplane, npts, factor, x):
  33. """
  34. crossplane: XZ, YZ, XY, or XYZ (half is XZ, half is YZ)
  35. npts: number of point in each direction
  36. factor: ratio of plotting size to outer size of the particle
  37. x: size parameters for particle layers
  38. """
  39. scan = np.linspace(-factor*x[-1], factor*x[-1], npts)
  40. zero = np.zeros(npts*npts, dtype = np.float64)
  41. if crossplane=='XZ':
  42. coordX, coordZ = np.meshgrid(scan, scan)
  43. coordX.resize(npts*npts)
  44. coordZ.resize(npts*npts)
  45. coordY = zero
  46. elif crossplane == 'YZ':
  47. coordY, coordZ = np.meshgrid(scan, scan)
  48. coordY.resize(npts*npts)
  49. coordZ.resize(npts*npts)
  50. coordX = zero
  51. elif crossplane == 'XY':
  52. coordX, coordY = np.meshgrid(scan, scan)
  53. coordX.resize(npts*npts)
  54. coordY.resize(npts*npts)
  55. coordZ = zero
  56. elif crossplane=='XYZ': # Upper half: XZ; Lower half: YZ
  57. coordX, coordZ = np.meshgrid(scan, scan)
  58. coordY, coordZ = np.meshgrid(scan, scan)
  59. coordX[:, scan<0] = 0
  60. coordY[:, scan>=0] = 0
  61. coordX.resize(npts*npts)
  62. coordY.resize(npts*npts)
  63. coordZ.resize(npts*npts)
  64. else:
  65. print("Unknown crossplane")
  66. import sys
  67. sys.exit()
  68. return coordX, coordY, coordZ, scan
  69. ###############################################################################
  70. def GetField(crossplane, npts, factor, x, m, pl):
  71. """
  72. crossplane: XZ, YZ, XY, or XYZ (half is XZ, half is YZ)
  73. npts: number of point in each direction
  74. factor: ratio of plotting size to outer size of the particle
  75. x: size parameters for particle layers
  76. m: relative index values for particle layers
  77. """
  78. coordX, coordY, coordZ, scan = GetCoords(crossplane, npts, factor, x)
  79. terms, E, H = fieldnlay(x, m, coordX, coordY, coordZ, pl=pl)
  80. if len(E.shape) > 2:
  81. E = E[0, :, :]
  82. H = H[0, :, :]
  83. S = np.cross(E, np.conjugate(H)).real
  84. print(S)
  85. if crossplane=='XZ':
  86. Sx = np.resize(S[:, 2], (npts, npts)).T
  87. Sy = np.resize(S[:, 0], (npts, npts)).T
  88. elif crossplane == 'YZ':
  89. Sx = np.resize(S[:, 2], (npts, npts)).T
  90. Sy = np.resize(S[:, 1], (npts, npts)).T
  91. elif crossplane == 'XY':
  92. Sx = np.resize(S[:, 1], (npts, npts)).T
  93. Sy = np.resize(S[:, 0], (npts, npts)).T
  94. elif crossplane=='XYZ': # Upper half: XZ; Lower half: YZ
  95. Sx = np.resize(S[:, 2], (npts, npts)).T
  96. Sy = np.resize(S[:, 0], (npts, npts)).T
  97. Sy[scan<0] = np.resize(S[:, 1], (npts, npts)).T[scan<0]
  98. else:
  99. print("Unknown crossplane")
  100. import sys
  101. sys.exit()
  102. return E, H, S, scan, Sx, Sy
  103. ###############################################################################
  104. def fieldplot(fig, ax, x, m, WL, comment='', WL_units=' ', crossplane='XZ',
  105. field_to_plot='Pabs', npts=101, factor=2.1,
  106. flow_total=11, density=20, minlength=0.1, maxlength=4.0,
  107. arrowstyle='-|>', arrowsize=1.0,
  108. pl=-1, draw_shell=False, outline_width=1, subplot_label=' '):
  109. E, H, S, scan, Sx, Sy = GetField(crossplane, npts, factor, x, m, pl)
  110. Er = np.absolute(E)
  111. Hr = np.absolute(H)
  112. try:
  113. from matplotlib import cm
  114. from matplotlib.colors import LogNorm
  115. if field_to_plot == 'Pabs':
  116. label = r'$\operatorname{Re}(E \times H^*)$'
  117. data = np.resize(np.linalg.norm(np.cross(E, np.conjugate(H)), axis=1).real, (npts, npts)).T
  118. elif field_to_plot == 'Eabs':
  119. label = r'$|E|$'
  120. Eabs = np.sqrt(Er[:, 0]**2 + Er[:, 1]**2 + Er[:, 2]**2)
  121. data = np.resize(Eabs, (npts, npts)).T
  122. elif field_to_plot == 'Habs':
  123. label = r'$|H|$'
  124. Habs = np.sqrt(Hr[:, 0]**2 + Hr[:, 1]**2 + Hr[:, 2]**2)
  125. Habs = 376.730313667*Habs # scale to free space impedance
  126. data = np.resize(Habs, (npts, npts)).T
  127. elif field_to_plot == 'angleEx':
  128. label = r'$arg(E_x)$'
  129. Eangle = np.angle(E[:, 0])/np.pi*180
  130. data = np.resize(Eangle, (npts, npts)).T
  131. elif field_to_plot == 'angleHy':
  132. label = r'$arg(H_y)$'
  133. Hangle = np.angle(H[:, 1])/np.pi*180
  134. data = np.resize(Hangle, (npts, npts)).T
  135. # Rescale to better show the axes
  136. scale = scan*WL/2.0/np.pi
  137. # Define scale ticks
  138. min_tick = np.amin(data[~np.isnan(data)])
  139. max_tick = np.amax(data[~np.isnan(data)])
  140. scale_ticks = np.linspace(min_tick, max_tick, 5)
  141. ax.set_title(label)
  142. # build a rectangle in axes coords
  143. ax.annotate(subplot_label, xy=(0.0, 1.1), xycoords='axes fraction', # fontsize=10,
  144. horizontalalignment='left', verticalalignment='top')
  145. # Interpolation can be 'nearest', 'bilinear' or 'bicubic'
  146. cax = ax.imshow(data, interpolation='nearest', cmap=cm.jet,
  147. origin='lower', vmin=min_tick, vmax=max_tick,
  148. extent=(min(scale), max(scale), min(scale), max(scale))
  149. # ,norm = LogNorm()
  150. )
  151. ax.axis("image")
  152. # Add colorbar
  153. cbar = fig.colorbar(cax, ticks=[a for a in scale_ticks], ax=ax)
  154. # vertically oriented colorbar
  155. if 'angle' in field_to_plot:
  156. cbar.ax.set_yticklabels(['%3.0f' % (a) for a in scale_ticks])
  157. else:
  158. cbar.ax.set_yticklabels(['%g' % (a) for a in scale_ticks])
  159. if crossplane == 'XZ':
  160. ax.set_xlabel('Z (%s)' % (WL_units))
  161. ax.set_ylabel('X (%s)' % (WL_units))
  162. elif crossplane == 'YZ':
  163. ax.set_xlabel('Z (%s)' % (WL_units))
  164. ax.set_ylabel('Y (%s)' % (WL_units))
  165. elif crossplane=='XYZ':
  166. ax.set_xlabel('Z (%s)' % (WL_units))
  167. ax.set_ylabel('Y(<0):X(>0) (%s)' % (WL_units))
  168. # draw a line to separate both planes
  169. ax.axhline(linewidth=outline_width, color='black')
  170. elif crossplane == 'XY':
  171. ax.set_xlabel('X (%s)' % (WL_units))
  172. ax.set_ylabel('Y (%s)' % (WL_units))
  173. if draw_shell:
  174. # Draw the nanoshell
  175. from matplotlib import patches
  176. from matplotlib.path import Path
  177. for xx in x:
  178. r = xx*WL/2.0/np.pi
  179. s1 = patches.Arc((0, 0), 2.0*r, 2.0*r, angle=0.0, zorder=1.8,
  180. theta1=0.0, theta2=360.0, linewidth=outline_width, color='black')
  181. ax.add_patch(s1)
  182. # Draw flow lines
  183. if (not crossplane == 'XY') and flow_total > 0:
  184. margin = 0.98
  185. points = np.vstack((margin*scale.min()*np.ones(flow_total),
  186. np.linspace(margin*scale.min(),
  187. margin*scale.max(), flow_total))).transpose()
  188. # Plot the streamlines with an appropriate colormap and arrow style
  189. ax.streamplot(scale, scale, Sx, Sy,
  190. start_points=points, integration_direction='both',
  191. density=density, minlength=minlength, maxlength=maxlength,
  192. linewidth=outline_width, color='white',
  193. arrowstyle=arrowstyle, arrowsize=arrowsize)
  194. finally:
  195. terms, Qext, Qsca, Qabs, Qbk, Qpr, g, Albedo, S1, S2 = scattnlay(x, m)
  196. print("Qsca = " + str(Qsca))
  197. #