Note
Go to the end to download the full example code.
4.1. Helmholtz 2D homogeneous medium test
Test for propagation in 2D homogeneous medium.
import torch
import numpy as np
from time import time
import sys
sys.path.append(".")
from wavesim.helmholtzdomain import HelmholtzDomain # when number of domains is 1
from wavesim.multidomain import MultiDomain # for domain decomposition, when number of domains is >= 1
from wavesim.iteration import run_algorithm # to run the wavesim iteration
from wavesim.utilities import preprocess
# Parameters
wavelength = 1. # Wavelength in micrometers
pixel_size = wavelength/4 # Pixel size in wavelength units
# Size of simulation domain (in pixels in x, y, and z direction)
sim_size = np.array([50, 50, 1]) # Simulation size in micrometers
n_size = (sim_size * wavelength/pixel_size).astype(int)
n = np.ones(n_size, dtype=np.complex64) # Refractive index map
boundary_widths = 8 # Width of the boundary in pixels
# return permittivity (n²) with boundaries, and boundary_widths in format (ax0, ax1, ax2)
n, boundary_array = preprocess(n**2, boundary_widths) # permittivity is n², but uses the same variable n
# Source term. This way is more efficient than dense tensor
indices = torch.tensor([[int(v/2 - 1) + boundary_array[i] for i, v in enumerate(n_size)]]).T # Location: center of the domain
values = torch.tensor([1.0]) # Amplitude: 1
n_ext = tuple(np.array(n_size) + 2*boundary_array)
source = torch.sparse_coo_tensor(indices, values, n_ext, dtype=torch.complex64)
# Set up the domain operators (HelmholtzDomain() or MultiDomain() depending on number of domains)
# 1-domain, periodic boundaries (without wrapping correction)
periodic = (True, True, True) # periodic boundaries, wrapped field.
domain = HelmholtzDomain(permittivity=n, periodic=periodic, wavelength=wavelength)
# # OR. Uncomment to test domain decomposition
# periodic = (False, True, True) # wrapping correction
# domain = MultiDomain(permittivity=n, periodic=periodic, wavelength=wavelength,
# n_domains=(2, 1, 1))
# Run the wavesim iteration and get the computed field
start = time()
u_computed, iterations, residual_norm = run_algorithm(domain, source, max_iterations=2000)
end = time() - start
print(f'\nTime {end:2.2f} s; Iterations {iterations}; Residual norm {residual_norm:.3e}')
u_computed = u_computed.squeeze()[*([slice(boundary_widths, -boundary_widths)]*2)]