def show_polarizer(center_polarizer=True): wavelength = 500e-9 angular_frequency = 2 * const.pi * const.c / wavelength source_amplitude = 1j * angular_frequency * const.mu_0 p_source = np.array([0, 1, 1]) / np.sqrt(2) # Set the sampling grid nb_samples = 2 * 1024 sample_pitch = wavelength / 8 boundary_thickness = 5e-6 x_range = utils.calc_ranges( nb_samples, sample_pitch, np.floor(1 + nb_samples / 2) * sample_pitch - boundary_thickness) # define the source source = -source_amplitude * sample_pitch * ( np.abs(x_range) < sample_pitch / 4) # point source at 0 source = p_source[:, np.newaxis] * source[np.newaxis, :] # define the medium eps_pol = np.eye(3, dtype=np.complex64) eps_pol[2, 2] = 1.0 + 0.1j rot_x = lambda a: np.array([[1, 0, 0], [0, np.cos( a), np.sin(a)], [0, -np.sin(a), np.cos(a)]], dtype=np.complex64) permittivity = np.tile( np.eye(3, dtype=np.complex64)[:, :, np.newaxis], [1, 1, nb_samples]) for pos_idx, pos in enumerate(x_range): if abs(pos - 30e-6) < 5e-6: permittivity[:, :, pos_idx] = eps_pol elif center_polarizer and abs(pos - 60e-6) < 5e-6: permittivity[:, :, pos_idx] = rot_x(-np.pi / 4) @ eps_pol @ rot_x( np.pi / 4) elif abs(pos - 90e-6) < 5e-6: permittivity[:, :, pos_idx] = rot_x(-np.pi / 2) @ eps_pol @ rot_x( np.pi / 2) dist_in_boundary = np.maximum( -(x_range - (x_range[0] + boundary_thickness)), x_range - (x_range[-1] - boundary_thickness)) / boundary_thickness for dim_idx in range(3): permittivity[dim_idx, dim_idx, ...] += (0.8j * np.maximum(0.0, dist_in_boundary) ) # absorbing boundary # # Impedance matched everywhere # permeability = 1.0 # # Non-impedance matched glass # permeability = np.ones((1, 1, len(x_range)), dtype=np.complex64) # permeability[:, :, (x_range < -1e-6) | (x_range > 26e-6)] = np.exp(0.2j) # absorbing boundary # permeability[:, :, (x_range >= 10e-6) & (x_range < 20e-6)] = 1.0 # permeability = bandpass_and_remove_gain(permeability, 2, x_range) # No magnetic component permeability = 1.0 # Prepare the display fig, axs = plt.subplots(3, 2, frameon=False, figsize=(12, 9)) abs_line = [] real_line = [] imag_line = [] for plot_idx in range(3): field_ax = axs[plot_idx][0] abs_line.append( field_ax.plot(x_range * 1e6, x_range * 0, color=[0, 0, 0])[0]) real_line.append( field_ax.plot(x_range * 1e6, x_range * 0, color=[0, 0.7, 0])[0]) imag_line.append( field_ax.plot(x_range * 1e6, x_range * 0, color=[1, 0, 0])[0]) field_ax.set_xlabel("x [$\mu$m]") field_ax.set_ylabel("$I_" + 'xyz'[plot_idx] + "$, $E_" + 'xyz'[plot_idx] + "$ [a.u.]") ax_m = axs[plot_idx][1] ax_m.plot(x_range[-1] * 2e6, 0, color=[0, 0, 0], label='I') ax_m.plot(x_range[-1] * 2e6, 0, color=[0, 0.7, 0], label='$E_{real}$') ax_m.plot(x_range[-1] * 2e6, 0, color=[1, 0, 0], label='$E_{imag}$') ax_m.plot(x_range * 1e6, permittivity[plot_idx, plot_idx].real, color=[0, 0, 1], label='$\epsilon_{real}$') ax_m.plot(x_range * 1e6, permittivity[plot_idx, plot_idx].imag, color=[0, 0.5, 0.5], label='$\epsilon_{imag}$') # ax_m.plot(x_range * 1e6, permeability[plot_idx, plot_idx].real, color=[0.5, 0.25, 0], label='$\mu_{real}$') # ax_m.plot(x_range * 1e6, permeability[plot_idx, plot_idx].imag, color=[0.5, 1, 0], label='$\mu_{imag}$') ax_m.set_xlabel('x [$\mu$m]') ax_m.set_ylabel('$\epsilon$, $\mu$') ax_m.set_xlim(x_range[[0, -1]] * 1e6) ax_m.legend(loc='upper right') plt.ion() # # Display the (intermediate) result # def display(s): E = s.E log.info("Total power: %0.3g", np.linalg.norm(E)**2) log.info("Displaying it %0.0f: error %0.1f%%" % (s.iteration, 100 * s.residue)) for plot_idx in range(3): ax = axs[plot_idx][0] field_to_display = E[plot_idx, :] max_val_to_display = np.maximum( np.max(np.abs(field_to_display)), np.finfo(field_to_display.dtype).eps) abs_line[plot_idx].set_ydata( np.abs(field_to_display)**2 / max_val_to_display) real_line[plot_idx].set_ydata(np.real(field_to_display)) imag_line[plot_idx].set_ydata(np.imag(field_to_display)) ax.set_ylim( np.array((-1, 1)) * np.maximum( np.max(np.abs(field_to_display)), np.max(abs(field_to_display)**2 / max_val_to_display)) * 1.05) figure_title = "$E_" + 'xyz'[ plot_idx] + "$ Iteration %d, " % s.iteration ax.set_title(figure_title) plt.draw() plt.pause(0.001) # # What to do after each iteration # def update_function(s): if np.mod(s.iteration, 100) == 0: log.info("Iteration %0.0f: rms error %0.1f%%" % (s.iteration, 100 * s.residue)) if np.mod(s.iteration, 100) == 0: display(s) return s.residue > 1e-5 and s.iteration < 1e4 # The actual work is done here: solution = macromax.solve(x_range, vacuum_wavelength=wavelength, source_distribution=source, epsilon=permittivity, mu=permeability, callback=update_function) # Show final result log.info('Displaying final result.') display(solution) plt.show()
def show_birefringence(): # # Medium settings # data_shape = np.array([128, 256]) * 2 wavelength = 500e-9 boundary_thickness = 3e-6 beam_diameter = 2.5e-6 k0 = 2 * np.pi / wavelength angular_frequency = const.c * k0 source_amplitude = 1j * angular_frequency * const.mu_0 sample_pitch = np.array([1, 1]) * wavelength / 8 ranges = utils.calc_ranges(data_shape, sample_pitch) incident_angle = 0 * np.pi / 180 def rot_Z(a): return np.array([[np.cos(a), -np.sin(a), 0], [np.sin(a), np.cos(a), 0], [0, 0, 1]]) incident_k = rot_Z(incident_angle) * k0 @ np.array([0, 1, 0]) p_source = rot_Z(incident_angle) @ np.array([1, 0, 1]) / np.sqrt( 2) # diagonally polarized beam source = -source_amplitude * np.exp( 1j * (incident_k[0] * ranges[0][:, np.newaxis] + incident_k[1] * ranges[1][np.newaxis, :])) # Aperture the incoming beam source = source * np.exp( -0.5 * (np.abs(ranges[1][np.newaxis, :] - (ranges[1][0] + boundary_thickness)) / wavelength)**2) source = source * np.exp(-0.5 * ( (ranges[0][:, np.newaxis] - ranges[0][int(len(ranges[0]) * 1 / 4)]) / (beam_diameter / 2))**2) source = p_source[:, np.newaxis, np.newaxis] * source[np.newaxis, ...] permittivity = np.tile( np.eye(3, dtype=np.complex128)[:, :, np.newaxis, np.newaxis], (1, 1, *data_shape)) # Add prism epsilon_crystal = rot_Z(-np.pi / 4) @ np.diag( (1.486, 1.658, 1.658))**2 @ rot_Z(np.pi / 4) permittivity[:, :, :, int(data_shape[1]*(1-5/8)/2)+np.arange(int(data_shape[1]*5/8))] = \ np.tile(epsilon_crystal[:, :, np.newaxis, np.newaxis], (1, 1, data_shape[0], int(data_shape[1]*5/8))) # Add boundary dist_in_boundary = np.maximum( np.maximum( 0.0, -(ranges[0][:, np.newaxis] - (ranges[0][0] + boundary_thickness))) + np.maximum( 0.0, ranges[0][:, np.newaxis] - (ranges[0][-1] - boundary_thickness)), np.maximum( 0.0, -(ranges[1][np.newaxis, :] - (ranges[1][0] + boundary_thickness))) + np.maximum( 0.0, ranges[1][np.newaxis, :] - (ranges[1][-1] - boundary_thickness))) weight_boundary = dist_in_boundary / boundary_thickness for dim_idx in range(3): permittivity[dim_idx, dim_idx, :, :] += -1.0 + ( 1.0 + 0.2j * weight_boundary) # boundary # Prepare the display fig, axs = plt.subplots(3, 2, frameon=False, figsize=(12, 9)) for ax in axs.ravel(): ax.set_xlabel('y [$\mu$m]') ax.set_ylabel('x [$\mu$m]') ax.set_aspect('equal') images = [ axs[dim_idx][0].imshow( utils.complex2rgb(np.zeros(data_shape), 1), extent=np.array([*ranges[1][[0, -1]], *ranges[0][[0, -1]]]) * 1e6, origin='lower') for dim_idx in range(3) ] axs[0][1].imshow( utils.complex2rgb(permittivity[0, 0], 1), extent=np.array([*ranges[1][[0, -1]], *ranges[0][[0, -1]]]) * 1e6, origin='lower') axs[2][1].imshow( utils.complex2rgb(source[0], 1), extent=np.array([*ranges[1][[0, -1]], *ranges[0][[0, -1]]]) * 1e6, origin='lower') axs[0][1].set_title('$\chi$') axs[1][1].axis('off') axs[2][1].set_title('source and S') mesh_ranges = [0, 1] for dim_idx in range(len(ranges)): mesh_ranges[dim_idx] = utils.to_dim(ranges[dim_idx].flatten(), len(ranges), axis=dim_idx) X, Y = np.meshgrid(mesh_ranges[1], mesh_ranges[0]) arrow_sep = np.array([1, 1], dtype=int) * 30 quiver = axs[2][1].quiver(X[::arrow_sep[0], ::arrow_sep[1]] * 1e6, Y[::arrow_sep[0], ::arrow_sep[1]] * 1e6, X[::arrow_sep[0], ::arrow_sep[1]] * 0, Y[::arrow_sep[0], ::arrow_sep[1]] * 0, pivot='mid', scale=1.0, scale_units='x', units='x', color=np.array([1, 0, 1, 0.5])) for dim_idx in range(3): for col_idx in range(2): axs[dim_idx][col_idx].autoscale(False, tight=True) # plt.show(block=True) # # Display the current solution # def display(s): log.info("Displaying iteration %d: error %0.1f%%" % (s.iteration, 100 * s.residue)) nb_dims = s.E.shape[0] for dim_idx in range(nb_dims): images[dim_idx].set_data(utils.complex2rgb(s.E[dim_idx], 1)) figure_title = '$E_' + 'xyz'[ dim_idx] + "$ it %d: rms error %0.1f%% " % (s.iteration, 100 * s.residue) axs[dim_idx][0].set_title(figure_title) S = s.S S /= np.sqrt(np.max(np.sum(np.abs(S)**2, axis=0))) / ( sample_pitch[0] * arrow_sep[0]) # Normalize U = S[0, ...] V = S[1, ...] quiver.set_UVC(V[::arrow_sep[0], ::arrow_sep[1]] * 1e6, U[::arrow_sep[0], ::arrow_sep[1]] * 1e6) plt.draw() plt.pause(0.001) # # Display the (intermediate) result # def update_function(s): if np.mod(s.iteration, 10) == 0: log.info("Iteration %0.0f: rms error %0.1f%%" % (s.iteration, 100 * s.residue)) if np.mod(s.iteration, 10) == 0: display(s) return s.residue > 1e-3 and s.iteration < 1e4 # The actual work is done here: start_time = time.time() solution = macromax.solve(ranges, vacuum_wavelength=wavelength, source_distribution=source, epsilon=permittivity, callback=update_function) log.info("Calculation time: %0.3fs." % (time.time() - start_time)) # Show final result log.info('Displaying final result.') display(solution) plt.show(block=True)
def show_scatterer(vectorial=True, anisotropic=True, scattering_layer=True): if not vectorial: anisotropic = False # # Medium settings # scale = 2 data_shape = np.array([128, 256]) * scale wavelength = 500e-9 medium_refractive_index = 1.0 # 1.4758, 2.7114 boundary_thickness = 2e-6 beam_diameter = 1.0e-6 * scale layer_thickness = 2.5e-6 * scale k0 = 2 * np.pi / wavelength angular_frequency = const.c * k0 source_amplitude = 1j * angular_frequency * const.mu_0 sample_pitch = np.array([1, 1]) * wavelength / 15 ranges = utils.calc_ranges(data_shape, sample_pitch) incident_angle = 0 * np.pi / 180 log.info('Calculating fields over a %0.1fum x %0.1fum area...' % tuple(data_shape * sample_pitch * 1e6)) def rot_Z(a): return np.array([[np.cos(a), -np.sin(a), 0], [np.sin(a), np.cos(a), 0], [0, 0, 1]]) incident_k = rot_Z(incident_angle) * k0 @ np.array([0, 1, 0]) p_source = rot_Z(incident_angle) @ np.array([1, 0, 1j]) / np.sqrt(2) source = -source_amplitude * np.exp( 1j * (incident_k[0] * ranges[0][:, np.newaxis] + incident_k[1] * ranges[1][np.newaxis, :])) # Aperture the incoming beam source = source * np.exp( -0.5 * (np.abs(ranges[1][np.newaxis, :] - (ranges[1][0] + boundary_thickness)) * medium_refractive_index / wavelength)**2) # source position source = source * np.exp(-0.5 * ( (ranges[0][:, np.newaxis] - ranges[0][int(len(ranges[0]) * 2 / 4)]) / (beam_diameter / 2))**2) # beam aperture source = source[np.newaxis, ...] if vectorial: source = source * p_source[:, np.newaxis, np.newaxis] # Place randomly oriented TiO2 particles permittivity, orientation, grain_pos, grain_rad, grain_dir = \ generate_random_layer(data_shape, sample_pitch, layer_thickness=layer_thickness, grain_mean=1e-6, grain_std=0.2e-6, normal_dim=1, birefringent=anisotropic, medium_refractive_index=medium_refractive_index, scattering_layer=scattering_layer) if not anisotropic: permittivity = permittivity[:1, :1, ...] log.info('Sample ready.') # for r, pos in zip(grain_rad, grain_pos): # plot_circle(plt, radius=r*1e6, origin=pos[::-1]*1e6) # epsilon_abs = np.abs(permittivity[0, 0]) - 1 # rgb_image = colors.hsv_to_rgb(np.stack((np.mod(direction / (2*np.pi),1), 1+0*direction, epsilon_abs), axis=2)) # plt.imshow(rgb_image, zorder=0, extent=utils.ranges2extent(*ranges)*1e6) # plt.axis('equal') # plt.pause(0.01) # plt.show(block=True) # Add boundary dist_in_boundary = np.maximum( np.maximum( 0.0, -(ranges[0][:, np.newaxis] - (ranges[0][0] + boundary_thickness))) + np.maximum( 0.0, ranges[0][:, np.newaxis] - (ranges[0][-1] - boundary_thickness)), np.maximum( 0.0, -(ranges[1][np.newaxis, :] - (ranges[1][0] + boundary_thickness))) + np.maximum( 0.0, ranges[1][np.newaxis, :] - (ranges[1][-1] - boundary_thickness))) weight_boundary = dist_in_boundary / boundary_thickness for dim_idx in range(permittivity.shape[0]): permittivity[dim_idx, dim_idx, :, :] += -1.0 + ( 1.0 + 0.5j * weight_boundary) # boundary # Prepare the display def add_circles_to_axes(axes): for r, pos in zip(grain_rad, grain_pos): circle = plt.Circle(pos[::-1] * 1e6, r * 1e6, edgecolor=np.array((1, 1, 1)) * 0.0, facecolor=None, alpha=0.25, fill=False, linewidth=1) axes.add_artist(circle) fig, axs = plt.subplots(3, 2, frameon=False, figsize=(12, 9), sharex=True, sharey=True) for ax in axs.ravel(): ax.set_xlabel('y [$\mu$m]') ax.set_ylabel('x [$\mu$m]') ax.set_aspect('equal') images = [ axs[dim_idx][0].imshow(utils.complex2rgb(np.zeros(data_shape), 1, inverted=True), extent=utils.ranges2extent(*ranges) * 1e6) for dim_idx in range(3) ] epsilon_abs = np.abs(permittivity[0, 0]) - 1 # rgb_image = colors.hsv_to_rgb(np.stack((np.mod(direction / (2*np.pi), 1), 1+0*direction, epsilon_abs), axis=2)) axs[0][1].imshow(utils.complex2rgb(epsilon_abs * np.exp(1j * orientation), normalization=True, inverted=True), zorder=0, extent=utils.ranges2extent(*ranges) * 1e6) add_circles_to_axes(axs[0][1]) axs[1][1].imshow(utils.complex2rgb(permittivity[0, 0], 1, inverted=True), extent=utils.ranges2extent(*ranges) * 1e6) axs[2][1].imshow(utils.complex2rgb(source[0], 1, inverted=True), extent=utils.ranges2extent(*ranges) * 1e6) axs[0][1].set_title('crystal axis orientation') axs[1][1].set_title('$\chi$') axs[2][1].set_title('source') # Display the medium without the boundaries for dim_idx in range(len(axs)): for col_idx in range(len(axs[dim_idx])): axs[dim_idx][col_idx].set_xlim( (ranges[1].flatten()[0] + boundary_thickness) * 1e6, (ranges[1].flatten()[-1] - boundary_thickness) * 1e6) axs[dim_idx][col_idx].set_ylim( (ranges[0].flatten()[0] + boundary_thickness) * 1e6, (ranges[0].flatten()[-1] - boundary_thickness) * 1e6) axs[dim_idx][col_idx].autoscale(False) # # Display the current solution # def display(s): log.info('Displaying iteration %d: error %0.1f%%' % (s.iteration, 100 * s.residue)) nb_dims = s.E.shape[0] for dim_idx in range(nb_dims): images[dim_idx].set_data( utils.complex2rgb(s.E[dim_idx], 1, inverted=True)) figure_title = '$E_' + 'xyz'[ dim_idx] + "$ it %d: rms error %0.1f%% " % (s.iteration, 100 * s.residue) add_circles_to_axes(axs[dim_idx][0]) axs[dim_idx][0].set_title(figure_title) plt.draw() plt.pause(0.001) # # Display progress and the (intermediate) result # residues = [] times = [] def update_function(s): # Log progress times.append(time.time()) residues.append(s.residue) if np.mod(s.iteration, 10) == 0: log.info("Iteration %0.0f: rms error %0.3f%%" % (s.iteration, 100 * s.residue)) if np.mod(s.iteration, 10) == 1: display(s) return s.residue > 1e-5 and s.iteration < 1e4 # The actual work is done here: start_time = time.time() solution = macromax.solve(ranges, vacuum_wavelength=wavelength, source_distribution=source, epsilon=permittivity, callback=update_function) log.info("Calculation time: %0.3fs." % (time.time() - start_time)) # Display how the method converged times = np.array(times) - start_time log.info("Calculation time: %0.3fs." % times[-1]) # Calculate total energy flow in propagation direction # forward_poynting_vector = np.sum(solution.S[1, :, :], axis=0) forward_E = np.mean(solution.E, axis=1) # average over dimension x forward_H = np.mean(solution.H, axis=1) # average over dimension x forward_poynting_vector = (0.5 / const.mu_0) * ParallelOperations.cross( forward_E, np.conj(forward_H)).real forward_poynting_vector = forward_poynting_vector[1, :] forward_poynting_vector_after_layer =\ forward_poynting_vector[(ranges[1] > layer_thickness / 2) & (ranges[1] < ranges[1][-1] - boundary_thickness)] forward_poynting_vector_after_layer = forward_poynting_vector_after_layer[ int(len(forward_poynting_vector_after_layer) / 2)] log.info('Forward Poynting vector: %g' % forward_poynting_vector_after_layer) fig_S = plt.figure(frameon=False, figsize=(12, 9)) ax_S = fig_S.add_subplot(111) ax_S.plot(ranges[1] * 1e6, forward_poynting_vector) ax_S.set_xlabel(r'$z [\mu m]$') ax_S.set_ylabel(r'$S_z$') # Show final result log.info('Displaying final result.') display(solution) plt.show(block=False) # Save the individual images log.info('Saving results to folder %s...' % os.getcwd()) plt.imsave('rutile_orientation.png', utils.complex2rgb(epsilon_abs * np.exp(1j * orientation), normalization=True, inverted=True), vmin=0.0, vmax=1.0, cmap=None, format='png', origin=None, dpi=600) for dim_idx in range(solution.E.shape[0]): plt.imsave('rutile_E%s.png' % chr(ord('x') + dim_idx), utils.complex2rgb(solution.E[dim_idx], 1, inverted=True), vmin=0.0, vmax=1.0, cmap=None, format='png', origin=None, dpi=600) # Save the figure plt.ioff() fig.savefig('rutile.svgz', bbox_inches='tight', format='svgz') plt.ion() return times, residues, forward_poynting_vector
def show_scatterer(vectorial=True): output_name = 'air_glass_air_2D' # # Medium settings # scale = 2 data_shape = np.array([256, 256]) * scale wavelength = 500e-9 medium_refractive_index = 1.0 # 1.4758, 2.7114 boundary_thickness = 2e-6 beam_diameter = 1.0e-6 * scale plate_thickness = 2.5e-6 * scale k0 = 2 * np.pi / wavelength angular_frequency = const.c * k0 source_amplitude = 1j * angular_frequency * const.mu_0 sample_pitch = np.array([1, 1]) * wavelength / 15 ranges = utils.calc_ranges(data_shape, sample_pitch) incident_angle = 30 * np.pi / 180 # incident_angle = np.arctan(1.5) # Brewster's angle log.info('Calculating fields over a %0.1fum x %0.1fum area...' % tuple(data_shape * sample_pitch * 1e6)) def rot_Z(a): return np.array([[np.cos(a), -np.sin(a), 0], [np.sin(a), np.cos(a), 0], [0, 0, 1]]) incident_k = rot_Z(incident_angle) * k0 @ np.array([1, 0, 0]) p_source = rot_Z(incident_angle) @ np.array([0, 1, 1j]) / np.sqrt(2) source = -source_amplitude * np.exp( 1j * (incident_k[0] * ranges[0][:, np.newaxis] + incident_k[1] * ranges[1][np.newaxis, :])) # Aperture the incoming beam # source = source * np.exp(-0.5*(np.abs(ranges[1][np.newaxis, :] - (ranges[1][0]+boundary_thickness)) # * medium_refractive_index / wavelength)**2) # source position source_pixel = data_shape[0] - int(boundary_thickness / sample_pitch[0]) source[:source_pixel, :] = 0 source[source_pixel + 1:, :] = 0 source = source * np.exp(-0.5 * ( (ranges[1][np.newaxis, :] - ranges[1][int(len(ranges[0]) * 1 / 4)]) / (beam_diameter / 2))**2) # beam aperture source = source[np.newaxis, ...] if vectorial: source = source * p_source[:, np.newaxis, np.newaxis] # define the glass plate refractive_index = 1.0 + 0.5 * np.ones(len(ranges[1]))[np.newaxis, :] * ( np.abs(ranges[0]) < plate_thickness / 2)[:, np.newaxis] permittivity = np.array(refractive_index**2, dtype=np.complex128) permittivity = permittivity[np.newaxis, np.newaxis, :, :] # Add boundary dist_in_boundary = np.maximum( np.maximum( 0.0, -(ranges[0][:, np.newaxis] - (ranges[0][0] + boundary_thickness))) + np.maximum( 0.0, ranges[0][:, np.newaxis] - (ranges[0][-1] - boundary_thickness)), np.maximum( 0.0, -(ranges[1][np.newaxis, :] - (ranges[1][0] + boundary_thickness))) + np.maximum( 0.0, ranges[1][np.newaxis, :] - (ranges[1][-1] - boundary_thickness))) weight_boundary = dist_in_boundary / boundary_thickness for dim_idx in range(permittivity.shape[0]): permittivity[dim_idx, dim_idx, :, :] += -1.0 + ( 1.0 + 0.5j * weight_boundary) # boundary # Prepare the display def add_rectangle_to_axes(axes): rectangle = plt.Rectangle(np.array( (ranges[1][0], -plate_thickness / 2)) * 1e6, (data_shape[1] * sample_pitch[1]) * 1e6, plate_thickness * 1e6, edgecolor=np.array((0, 1, 1, 0.25)), linewidth=1, fill=True, facecolor=np.array((0, 1, 1, 0.05))) axes.add_artist(rectangle) fig, axs = plt.subplots(2, 2, frameon=False, figsize=(12, 12), sharex=True, sharey=True) for ax in axs.ravel(): ax.set_xlabel('y [$\mu$m]') ax.set_ylabel('x [$\mu$m]') ax.set_aspect('equal') images = [ axs.flatten()[idx].imshow(utils.complex2rgb(np.zeros(data_shape), 1, inverted=True), extent=utils.ranges2extent(*ranges) * 1e6) for idx in range(4) ] axs[0][1].set_title('$||E||^2$') # Display the medium without the boundaries for idx in range(4): axs.flatten()[idx].set_xlim( (ranges[1].flatten()[0] + boundary_thickness) * 1e6, (ranges[1].flatten()[-1] - boundary_thickness) * 1e6) axs.flatten()[idx].set_ylim( (ranges[0].flatten()[0] + boundary_thickness) * 1e6, (ranges[0].flatten()[-1] - boundary_thickness) * 1e6) axs.flatten()[idx].autoscale(False) # # Display the current solution # def display(s): log.info('Displaying iteration %d: error %0.1f%%' % (s.iteration, 100 * s.residue)) nb_dims = s.E.shape[0] for dim_idx in range(nb_dims): images[dim_idx].set_data( utils.complex2rgb(s.E[dim_idx], 1, inverted=True)) figure_title = '$E_' + 'xyz'[ dim_idx] + "$ it %d: rms error %0.1f%% " % (s.iteration, 100 * s.residue) add_rectangle_to_axes(axs.flatten()[dim_idx]) axs.flatten()[dim_idx].set_title(figure_title) intensity = np.linalg.norm(s.E, axis=0) intensity /= np.max(intensity) intensity_rgb = np.concatenate( (intensity[:, :, np.newaxis], intensity[:, :, np.newaxis], intensity[:, :, np.newaxis]), axis=2) images[-1].set_data(intensity_rgb) add_rectangle_to_axes(axs.flatten()[-1]) axs.flatten()[3].set_title('I') plt.draw() plt.pause(0.001) # # Display progress and the (intermediate) result # residues = [] times = [] def update_function(s): # Log progress times.append(time.time()) residues.append(s.residue) if np.mod(s.iteration, 10) == 0: log.info("Iteration %0.0f: rms error %0.3f%%" % (s.iteration, 100 * s.residue)) if np.mod(s.iteration, 10) == 1: display(s) return s.residue > 1e-4 and s.iteration < 1e4 # The actual work is done here: start_time = time.time() solution = macromax.solve(ranges, vacuum_wavelength=wavelength, source_distribution=source, epsilon=permittivity, callback=update_function) log.info("Calculation time: %0.3fs." % (time.time() - start_time)) # Display how the method converged times = np.array(times) - start_time log.info("Calculation time: %0.3fs." % times[-1]) # Show final result log.info('Displaying final result.') display(solution) plt.show(block=False) # Save the individual images log.info('Saving results to folder %s...' % os.getcwd()) for dim_idx in range(solution.E.shape[0]): plt.imsave(output_name + '_E%s.png' % chr(ord('x') + dim_idx), utils.complex2rgb(solution.E[dim_idx], 1, inverted=True), vmin=0.0, vmax=1.0, cmap=None, format='png', origin=None, dpi=600) # Save the figure plt.ioff() fig.savefig(output_name + '.svgz', bbox_inches='tight', format='svgz') plt.ion() return times, residues
def show_air_glass_transition(impedance_matched=False, birefringent=False): wavelength = 488e-9 angular_frequency = 2 * const.pi * const.c / wavelength source_amplitude = 1j * angular_frequency * const.mu_0 p_source = np.array([0.0, 1.0, 1.0j]) # y-polarized # Set the sampling grid nb_samples = 1024 sample_pitch = wavelength / 16 x_range = sample_pitch * np.arange(nb_samples) - 5e-6 # define the source source = -source_amplitude * sample_pitch * ( np.abs(x_range) < sample_pitch / 4) # point source at 0 source = p_source[:, np.newaxis] * source[np.newaxis, :] # define the medium epsilon_material = np.array([1.5, 1.48, 1.5])**2 has_object = (x_range >= 10e-6) & (x_range < 200e-6) permittivity = np.ones((1, 1, len(x_range)), dtype=np.complex64) # absorbing boundary boundary_thickness = 5e-6 dist_in_boundary = np.maximum( 0.0, np.maximum(-(x_range - (x_range[0] + boundary_thickness)), x_range - (x_range[-1] - boundary_thickness)) / boundary_thickness) permittivity[:, :, :] += -1.0 + 1.0 + ( 0.5j * dist_in_boundary) # The first two dimensions are singleton dims if birefringent: permittivity = np.eye(3)[:, :, np.newaxis] * permittivity for dim_idx in range(3): permittivity[dim_idx, dim_idx, has_object] += epsilon_material[dim_idx] else: permittivity[:, :, has_object] += epsilon_material[0] # permittivity = bandpass_and_remove_gain(permittivity, 2, x_range, wavelength/2) if impedance_matched: # Impedance matched everywhere permeability = permittivity else: # Non-impedance matched glass permeability = 1.0 # The display function below would't expect a scalar # Prepare the display fig, ax = plt.subplots(2, 1, frameon=False, figsize=(12, 9), sharex=True) abs_line = ax[0].plot(x_range * 1e6, x_range * 0, color=[0, 0, 0])[0] poynting_line = ax[0].plot(x_range * 1e6, x_range * 0, color=[1, 0, 1])[0] energy_line = ax[0].plot(x_range * 1e6, x_range * 0, color=[0, 1, 1])[0] real_line = ax[0].plot(x_range * 1e6, x_range * 0, color=[0, 0.7, 0])[0] imag_line = ax[0].plot(x_range * 1e6, x_range * 0, color=[1, 0, 0])[0] ax[0].set_xlabel("x [$\mu$m]") ax[0].set_ylabel("E, S [a.u.]") ax[0].set_xlim(x_range[[0, -1]] * 1e6) ax[1].plot(x_range[-1] * 2e6, 0, color=[0, 0, 0], label='|E|') ax[1].plot(x_range[-1] * 2e6, 0, color=[1, 0, 1], label='S') ax[1].plot(x_range[-1] * 2e6, 0, color=[0, 1, 1], label='u') ax[1].plot(x_range[-1] * 2e6, 0, color=[0, 0.7, 0], label='$E_{real}$') ax[1].plot(x_range[-1] * 2e6, 0, color=[1, 0, 0], label='$E_{imag}$') ax[1].plot(x_range * 1e6, permittivity[0, 0].real, color=[0, 0, 1], linewidth=2.0, label='$\\epsilon_{real}$') ax[1].plot(x_range * 1e6, permittivity[0, 0].imag, color=[0, 0.5, 0.5], linewidth=2.0, label='$\\epsilon_{imag}$') if impedance_matched: ax[1].plot(x_range * 1e6, permeability[0, 0].real, color=[0.5, 0.25, 0], label='$\\mu_{real}$') ax[1].plot(x_range * 1e6, permeability[0, 0].imag, color=[0.5, 1, 0], label='$\\mu_{imag}$') ax[1].set_xlabel('x [$\mu$m]') ax[1].set_ylabel('$\epsilon$, $\mu$') ax[1].set_xlim(x_range[[0, -1]] * 1e6) ax[1].legend(loc='upper right') plt.ion() def display(s): E = s.E[1, :] H = s.H[2, :] S = s.S[0, :] u = s.energy_density log.info("1D: Displaying iteration %0.0f: error %0.1f%%" % (s.iteration, 100 * s.residue)) field_to_display = angular_frequency * E # The source is polarized along this dimension max_val_to_display = np.maximum(np.max(np.abs(field_to_display)), np.finfo(field_to_display.dtype).eps) poynting_normalization = np.max(np.abs(S)) / max_val_to_display energy_normalization = np.max(np.abs(u)) / max_val_to_display abs_line.set_ydata(np.abs(field_to_display)**2 / max_val_to_display) poynting_line.set_ydata(np.real(S) / poynting_normalization) energy_line.set_ydata(np.real(u) / energy_normalization) real_line.set_ydata(np.real(field_to_display)) imag_line.set_ydata(np.imag(field_to_display)) ax[0].set_ylim( np.array((-1, 1)) * np.maximum(np.max(np.abs(field_to_display)), np.max(abs(field_to_display)**2 / max_val_to_display)) * 1.05) figure_title = "Iteration %d, " % s.iteration ax[0].set_title(figure_title) plt.pause(0.001) # # What to do after each iteration # def update_function(s): if np.mod(s.iteration, 100) == 0: log.info("Iteration %0.0f: rms error %0.1f%%" % (s.iteration, 100 * s.residue)) if np.mod(s.iteration, 100) == 0: display(s) return s.residue > 1e-5 and s.iteration < 1e4 # The actual work is done here: start_time = time.time() solution = macromax.solve(x_range, vacuum_wavelength=wavelength, source_distribution=source, epsilon=permittivity, mu=permeability, callback=update_function) log.info("Calculation time: %0.3fs." % (time.time() - start_time)) # Show final result log.info('Displaying final result.') display(solution) plt.show(block=False)