fieldplot.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  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, flow_total=11,
  106. pl=-1, draw_shell=False, outline_width=1, subplot_label=' '):
  107. E, H, S, scan, Sx, Sy = GetField(crossplane, npts, factor, x, m, pl)
  108. Er = np.absolute(E)
  109. Hr = np.absolute(H)
  110. try:
  111. from matplotlib import cm
  112. from matplotlib.colors import LogNorm
  113. if field_to_plot == 'Pabs':
  114. label = r'$\operatorname{Re}(E \times H^*)$'
  115. data = np.resize(np.linalg.norm(np.cross(E, np.conjugate(H)), axis=1).real, (npts, npts)).T
  116. elif field_to_plot == 'Eabs':
  117. label = r'$|E|$'
  118. Eabs = np.sqrt(Er[:, 0]**2 + Er[:, 1]**2 + Er[:, 2]**2)
  119. data = np.resize(Eabs, (npts, npts)).T
  120. elif field_to_plot == 'Habs':
  121. label = r'$|H|$'
  122. Habs = np.sqrt(Hr[:, 0]**2 + Hr[:, 1]**2 + Hr[:, 2]**2)
  123. Habs = 376.730313667*Habs # scale to free space impedance
  124. data = np.resize(Habs, (npts, npts)).T
  125. elif field_to_plot == 'angleEx':
  126. label = r'$arg(E_x)$'
  127. Eangle = np.angle(E[:, 0])/np.pi*180
  128. data = np.resize(Eangle, (npts, npts)).T
  129. elif field_to_plot == 'angleHy':
  130. label = r'$arg(H_y)$'
  131. Hangle = np.angle(H[:, 1])/np.pi*180
  132. data = np.resize(Hangle, (npts, npts)).T
  133. # Rescale to better show the axes
  134. scale = scan*WL/2.0/np.pi
  135. # Define scale ticks
  136. min_tick = np.amin(data[~np.isnan(data)])
  137. max_tick = np.amax(data[~np.isnan(data)])
  138. scale_ticks = np.linspace(min_tick, max_tick, 5)
  139. ax.set_title(label)
  140. # build a rectangle in axes coords
  141. ax.annotate(subplot_label, xy=(0.0, 1.1), xycoords='axes fraction', # fontsize=10,
  142. horizontalalignment='left', verticalalignment='top')
  143. # Interpolation can be 'nearest', 'bilinear' or 'bicubic'
  144. cax = ax.imshow(data, interpolation='nearest', cmap=cm.jet,
  145. origin='lower', vmin=min_tick, vmax=max_tick,
  146. extent=(min(scale), max(scale), min(scale), max(scale))
  147. # ,norm = LogNorm()
  148. )
  149. ax.axis("image")
  150. # Add colorbar
  151. cbar = fig.colorbar(cax, ticks=[a for a in scale_ticks], ax=ax)
  152. # vertically oriented colorbar
  153. if 'angle' in field_to_plot:
  154. cbar.ax.set_yticklabels(['%3.0f' % (a) for a in scale_ticks])
  155. else:
  156. cbar.ax.set_yticklabels(['%g' % (a) for a in scale_ticks])
  157. if crossplane == 'XZ':
  158. ax.set_xlabel('Z (%s)' % (WL_units))
  159. ax.set_ylabel('X (%s)' % (WL_units))
  160. elif crossplane == 'YZ':
  161. ax.set_xlabel('Z (%s)' % (WL_units))
  162. ax.set_ylabel('Y (%s)' % (WL_units))
  163. elif crossplane=='XYZ':
  164. ax.set_xlabel('Z (%s)' % (WL_units))
  165. ax.set_ylabel('Y(<0):X(>0) (%s)' % (WL_units))
  166. # draw a line to separate both planes
  167. ax.axhline(linewidth=outline_width, color='black')
  168. elif crossplane == 'XY':
  169. ax.set_xlabel('X (%s)' % (WL_units))
  170. ax.set_ylabel('Y (%s)' % (WL_units))
  171. if draw_shell:
  172. # Draw the nanoshell
  173. from matplotlib import patches
  174. from matplotlib.path import Path
  175. for xx in x:
  176. r = xx*WL/2.0/np.pi
  177. s1 = patches.Arc((0, 0), 2.0*r, 2.0*r, angle=0.0, zorder=1.8,
  178. theta1=0.0, theta2=360.0, linewidth=outline_width, color='black')
  179. ax.add_patch(s1)
  180. # Draw flow lines
  181. if (not crossplane == 'XY') and flow_total > 0:
  182. margin = 0.98
  183. points = np.vstack((margin*scale.min()*np.ones(flow_total),
  184. np.linspace(margin*scale.min(),
  185. margin*scale.max(), flow_total))).transpose()
  186. # Plot the streamlines with an appropriate colormap and arrow style
  187. ax.streamplot(scale, scale, Sx, Sy,
  188. start_points=points, integration_direction='both',
  189. density=20.0,
  190. linewidth=outline_width, color='white',
  191. arrowstyle='-|>', arrowsize=1.0)
  192. finally:
  193. terms, Qext, Qsca, Qabs, Qbk, Qpr, g, Albedo, S1, S2 = scattnlay(x, m)
  194. print("Qsca = " + str(Qsca))
  195. #