def _set_meshes(self, inputdata): """Set elements meshgrid data. Parameters ---------- inputdata : :class:`inputdata.InputData` An instance of problem with resolution information. """ # S-domain resolution NM, NS = self.configuration.NM, self.configuration.NS # Elements resolution NQ, NP = self.discretization # Image resolution NY, NX = inputdata.resolution dx = self.configuration.Lx / NX dy = self.configuration.Ly / NY # S-domain mesh xm, ym = cfg.get_coordinates_sdomain(self.configuration.Ro, self.configuration.NM) xms, yms = (np.reshape(np.tile(xm.reshape((-1, 1)), (1, NS)), (-1)), np.reshape(np.tile(ym.reshape((-1, 1)), (1, NS)), (-1))) # Image mesh at D-domain x, y = cfg.get_coordinates_ddomain(configuration=self.configuration, resolution=inputdata.resolution) self._u, self._v = x.reshape(-1), y.reshape(-1) self._du, self._dv = dx, dy # Elements meshgrid self._xpq, self._ypq = get_elements_mesh(NX, NY, dx, dy, NP, NQ) # Radius matrix self._R = np.zeros((NM * NS, self._u.size)) for i in range(NM * NS): self._R[i, :] = np.sqrt((xms[i] - self._u)**2 + (yms[i] - self._v)**2) # Bilinear trial function does not depend on field information if self.trial_function == TRIAL_BILINEAR: self._fij = bilinear(self._u.reshape((NY, NX)), self._v.reshape((NY, NX)), self._xpq.reshape((NQ, NP)), self._ypq.reshape((NQ, NP)))
def _compute_green_function(self, resolution): """Summarize method.""" NM = self.configuration.NM NY, NX = resolution N = NX * NY kb = self.configuration.kb xm, ym = cfg.get_coordinates_sdomain(self.configuration.Ro, NM) x, y = cfg.get_coordinates_ddomain(configuration=self.configuration, resolution=resolution) dx, dy = x[0, 1] - x[0, 0], y[1, 0] - y[0, 0] xm = np.tile(xm.reshape((-1, 1)), (1, N)) ym = np.tile(ym.reshape((-1, 1)), (1, N)) R = np.sqrt((xm - np.tile(np.reshape(x, (N, 1)).T, (NM, 1)))**2 + (ym - np.tile(np.reshape(y, (N, 1)).T, (NM, 1)))**2) an = np.sqrt(dx * dy / np.pi) self._GS = -1j * np.pi * kb * an / 2 * jv(1, kb * an) * hankel2( 0, kb * R)
def incident_field(self, resolution): """Compute the incident field matrix. Given the configuration information stored in the object, it computes the incident field matrix considering plane waves in different from different angles. Parameters ---------- resolution : 2-tuple The image size of D-domain in pixels (y and x). Returns ------- ei : :class:`numpy.ndarray` Incident field matrix. The rows correspond to the points in the image following `C`-order and the columns corresponds to the sources. """ NY, NX = resolution phi = cfg.get_angles(self.configuration.NS) x, y = cfg.get_coordinates_ddomain(configuration=self.configuration, resolution=resolution) kb = self.configuration.kb E0 = self.configuration.E0 if isinstance(kb, float) or isinstance(kb, complex): ei = E0*np.exp(-1j*kb*(x.reshape((-1, 1)) @ np.cos(phi.reshape((1, -1))) + y.reshape((-1, 1)) @ np.sin(phi.reshape((1, -1))))) else: ei = np.zeros((NX*NY, self.configuration.NS, kb.size), dtype=complex) for f in range(kb.size): ei[:, :, f] = E0*np.exp(-1j*kb[f]*(x.reshape((-1, 1)) @ np.cos(phi.reshape((1, -1))) + y.reshape((-1, 1)) @ np.sin(phi.reshape((1, -1)))) ) return ei
def solve(self, scenario, noise=None, PRINT_INFO=False, COMPUTE_SCATTERED_FIELD=True, SAVE_INTERN_FIELD=True): """Solve the forward problem. Parameters ---------- scenario : :class:`inputdata:InputData` An object describing the dielectric property map. PRINT_INFO : boolean, default: False Print iteration information. COMPUTE_INTERN_FIELD : boolean, default: True Compute the total field in D-domain. Return ------ es, et, ei : :class:`numpy:ndarray` Matrices with the scattered, total and incident field information. Examples -------- >>> solver = MoM_CG_FFT(configuration) >>> es, et, ei = solver.solve(scenario) >>> es, ei = solver.solve(scenario, COMPUTE_INTERN_FIELD=False) """ epsilon_r, sigma = super().solve(scenario) # Quick access for configuration variables NM = self.configuration.NM NS = self.configuration.NS Ro = self.configuration.Ro epsilon_rb = self.configuration.epsilon_rb sigma_b = self.configuration.sigma_b f = self.configuration.f omega = 2*np.pi*f kb = self.configuration.kb Lx, Ly = self.configuration.Lx, self.configuration.Ly NY, NX = scenario.resolution N = NX*NY xmin, xmax = cfg.get_bounds(Lx) ymin, ymax = cfg.get_bounds(Ly) dx, dy = Lx/NX, Ly/NY xm, ym = cfg.get_coordinates_sdomain(Ro, NM) x, y = cfg.get_coordinates_ddomain(dx=dx, dy=dy, xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax) ei = self.incident_field(scenario.resolution) scenario.ei = np.copy(ei) ei = np.conj(ei) if isinstance(f, float): MONO_FREQUENCY = True else: MONO_FREQUENCY = False NF = f.size deltasn = dx*dy # area of the cell an = np.sqrt(deltasn/np.pi) # radius of the equivalent circle Xr = get_contrast_map(epsilon_r, sigma, epsilon_rb, sigma_b, omega) # Using circular convolution [extended domain (2N-1)x(2N-1)] [xe, ye] = np.meshgrid(np.arange(xmin-(NX/2-1)*dx, xmax+NY/2*dx, dx), np.arange(ymin-(NY/2-1)*dy, ymax+NY/2*dy, dy)) Rmn = np.sqrt(xe**2 + ye**2) # distance between the cells G = self.__get_extended_matrix(Rmn, kb, an, NX, NY) b = np.copy(ei) if MONO_FREQUENCY: tic = time.time() et = np.zeros((N, NS), dtype=complex) niter = np.zeros(NS) error = np.zeros((self.MAX_IT, NS)) num_cores = multiprocessing.cpu_count() results = (Parallel(n_jobs=num_cores)(delayed(self.CG_FFT) (G, b[:, ns].reshape((-1, 1)), NX, NY, 1, Xr, self.MAX_IT, self.TOL, False) for ns in range(NS))) for ns in range(NS): et[:, ns] = results[ns][0].flatten() niter[ns] = results[ns][1] error[:, ns] = results[ns][2].flatten() time_cg_fft = time.time()-tic if PRINT_INFO: print('Execution time: %.2f' % time_cg_fft + ' [sec]') else: et = np.zeros((N, NS, NF), dtype=complex) niter = np.zeros(NF) error = np.zeros((self.MAX_IT, NF)) num_cores = multiprocessing.cpu_count() results = (Parallel(n_jobs=num_cores)(delayed(self.CG_FFT) (np.squeeze(G[:, :, nf]), np.squeeze(b[:, :, nf]), NX, NY, NS, np.squeeze(Xr[:, :, nf]), self.MAX_IT, self.TOL, False) for nf in range(NF))) for nf in range(NF): et[:, :, nf] = results[nf][0] niter[nf] = results[nf][1] error[:, nf] = results[nf][2] print('Frequency: %.3f ' % (f[nf]/1e9) + '[GHz] - ' + 'Number of iterations: %d - ' % (niter[nf]+1) + 'Error: %.3e' % error[int(niter[nf]), nf]) if SAVE_INTERN_FIELD: scenario.et = np.conj(et) if COMPUTE_SCATTERED_FIELD: GS = get_greenfunction(xm, ym, x, y, kb) if MONO_FREQUENCY: es = GS @ spr.dia_matrix((Xr.flatten(), 0), shape=(N, N)) @ et else: es = np.zeros((NM, NS, NF), dtype=complex) for nf in range(NF): aux = spr.dia_matrix((Xr[:, :, nf].flatten(), 0), shape=(N, N)) es[:, :, nf] = GS[:, :, nf] @ aux @ et[:, :, nf] if noise is not None and noise > 0: es = fwr.add_noise(es, noise) scenario.es = np.conj(es) return np.conj(et), np.conj(ei), np.conj(es) else: return np.conj(et), np.conj(ei)
def _set_meshes(self, inputdata): """Set elements meshgrid data. Parameters ---------- inputdata : :class:`inputdata.InputData` An instance of problem with resolution information. """ # Number of measurements and sources NM, NS = self.configuration.NM, self.configuration.NS # Discretization in S-domain NW, NZ = self.discretization[0], self.discretization[1] # Discretization in D-domain NP, NQ = self.discretization[3], self.discretization[2] # Image resolution NY, NX = inputdata.resolution dx, dy = self.configuration.Lx / NX, self.configuration.Ly / NY x, y = cfg.get_coordinates_ddomain(configuration=self.configuration, resolution=inputdata.resolution) self._u, self._v = x.reshape(-1), y.reshape(-1) self._du, self._dv = dx, dy # Interpolation condition: if S-space discretization is greater # than the number of measurements and sources. # If the discretization is smaller, than S-space is integrated # in the points of the original data. # Otherwise, the original data is interpolated in order to have # more integration points than elements. self._FLAG_INTERPOLATION = NW > NM or NZ > NS # If the original data have more information than discretization if not self._FLAG_INTERPOLATION: xm, ym = cfg.get_coordinates_sdomain(self.configuration.Ro, self.configuration.NM) self._xms = np.reshape(np.tile(xm.reshape((-1, 1)), (1, NS)), (-1)) self._yms = np.reshape(np.tile(ym.reshape((-1, 1)), (1, NS)), (-1)) self._phi_ms, self._theta_ms = np.meshgrid(cfg.get_angles(NS), cfg.get_angles(NM)) # If the original data have less information than discretization else: new_NM = self.constant_interpolation * NW new_NS = self.constant_interpolation * NZ xm, ym = cfg.get_coordinates_sdomain(self.configuration.Ro, new_NM) self._xms = np.reshape(np.tile(xm.reshape((-1, 1)), (1, new_NS)), (-1)) self._yms = np.reshape(np.tile(ym.reshape((-1, 1)), (1, new_NS)), (-1)) self._phi_ms, self._theta_ms = np.meshgrid(cfg.get_angles(new_NS), cfg.get_angles(new_NM)) self._phi_wz, self._theta_wz = np.meshgrid(cfg.get_angles(NZ), cfg.get_angles(NW)) self._dtheta = self._theta_ms[1, 0] - self._theta_ms[0, 0] self._dphi = self._phi_ms[0, 1] - self._phi_ms[0, 0] self._R = np.zeros((self._theta_ms.size, self._u.size)) for i in range(self._R.shape[0]): self._R[i, :] = np.sqrt((self._xms[i] - self._u)**2 + (self._yms[i] - self._v)**2) # Coordinates of D-domain elements self._xpq, self._ypq = clc.get_elements_mesh(NX, NY, dx, dy, NP, NQ) if self.basis_function == BASIS_BILINEAR: self._fij = clc.bilinear(self._u.reshape((NY, NX)), self._v.reshape((NY, NX)), self._xpq.reshape((NQ, NP)), self._ypq.reshape((NQ, NP))) self._gij = clc.bilinear(self._phi_ms, self._theta_ms, self._phi_wz, self._theta_wz) elif self.basis_function == BASIS_LEGENDRE: xmin, xmax = cfg.get_bounds(self.configuration.Lx) ymin, ymax = cfg.get_bounds(self.configuration.Ly) self._fij = legendre(self._u.reshape((NY, NX)), self._v.reshape((NY, NX)), NQ, NP, xmin, xmax, ymin, ymax) self._gij = legendre(self._phi_ms, self._theta_ms, NW, NZ, 0, 2 * np.pi, 0, 2 * np.pi)
def conductor_cylinder(self, scenario, radius_proportion=0.5, SAVE_INTERN_FIELD=True, SAVE_MAP=False): """Solve the forward problem. Parameters ---------- scenario : :class:`inputdata:InputData` An object describing the dielectric property map. PRINT_INFO : boolean, default: False Print iteration information. COMPUTE_INTERN_FIELD : boolean, default: True Compute the total field in D-domain. Return ------ es, et, ei : :class:`numpy:ndarray` Matrices with the scattered, total and incident field information. Examples -------- >>> solver = MoM_CG_FFT(configuration) >>> es, et, ei = solver.solve(scenario) >>> es, ei = solver.solve(scenario, COMPUTE_INTERN_FIELD=False) """ # Main constants omega = 2 * pi * self.configuration.f # Angular frequency [rad/s] kb = self.configuration.kb # Wavenumber of background [rad/m] a = radius_proportion * self.configuration.lambda_b # Sphere's radius thetal = cfg.get_angles(self.configuration.NS) thetam = cfg.get_angles(self.configuration.NM) # Summing coefficients n = np.arange(-self.NT, self.NT + 1) an = -jv(n, kb * a) / hankel2(n, kb * a) cn = np.zeros(n.size) # Mesh parameters x, y = cfg.get_coordinates_ddomain(configuration=self.configuration, resolution=scenario.resolution) # Incident field ei = self.incident_field(scenario.resolution) # Total field array et = compute_total_field(x, y, a, an, cn, self.NT, kb, 1., self.configuration.E0, thetal) # Map of parameters sigma = np.zeros(x.shape) sigma[x**2 + y**2 <= a**2] = 1e10 # Scatered field rho = self.configuration.Ro xm, ym = rho * np.cos(thetam), rho * np.sin(thetam) es = compute_scattered_field(xm, ym, an, kb, thetal, self.configuration.E0) if scenario.noise > 0: es = fwr.add_noise(es, scenario.noise) scenario.es = np.copy(es) scenario.ei = np.copy(ei) if SAVE_INTERN_FIELD: scenario.et = np.copy(et) if SAVE_MAP: scenario.sigma = np.copy(sigma) self.et = et self.ei = ei self.es = es self.epsilon_r = None self.sigma = sigma self.radius_proportion = radius_proportion self.problem = PERFECT_DIELECTRIC_PROBLEM
def dielectric_cylinder(self, scenario, radius_proportion=0.5, contrast=2., SAVE_INTERN_FIELD=True, SAVE_MAP=False): """Solve the forward problem. Parameters ---------- scenario : :class:`inputdata:InputData` An object describing the dielectric property map. PRINT_INFO : boolean, default: False Print iteration information. COMPUTE_INTERN_FIELD : boolean, default: True Compute the total field in D-domain. Return ------ es, et, ei : :class:`numpy:ndarray` Matrices with the scattered, total and incident field information. Examples -------- >>> solver = MoM_CG_FFT(configuration) >>> es, et, ei = solver.solve(scenario) >>> es, ei = solver.solve(scenario, COMPUTE_INTERN_FIELD=False) """ # Main constants omega = 2 * pi * self.configuration.f # Angular frequency [rad/s] epsilon_rd = cfg.get_relative_permittivity( contrast, self.configuration.epsilon_rb) epsd = epsilon_rd * epsilon_0 # Cylinder's permittivity [F/m] epsb = self.configuration.epsilon_rb * epsilon_0 mud = mu_0 # Cylinder's permeability [H/m] kb = self.configuration.kb # Wavenumber of background [rad/m] kd = omega * np.sqrt(mud * epsd) # Wavenumber of cylinder [rad/m] lambdab = 2 * pi / kb # Wavelength of background [m] a = radius_proportion * lambdab # Sphere's radius [m] thetal = cfg.get_angles(self.configuration.NS) thetam = cfg.get_angles(self.configuration.NM) # Summing coefficients an, cn = get_coefficients(self.NT, kb, kd, a, epsd, epsb) # Mesh parameters x, y = cfg.get_coordinates_ddomain(configuration=self.configuration, resolution=scenario.resolution) # Incident field ei = self.incident_field(scenario.resolution) # Total field array et = compute_total_field(x, y, a, an, cn, self.NT, kb, kd, self.configuration.E0, thetal) # Map of parameters epsilon_r, _ = get_map(x, y, a, self.configuration.epsilon_rb, epsilon_rd) # Scatered field rho = self.configuration.Ro xm, ym = rho * np.cos(thetam), rho * np.sin(thetam) es = compute_scattered_field(xm, ym, an, kb, thetal, self.configuration.E0) if scenario.noise > 0: es = fwr.add_noise(es, scenario.noise) scenario.es = np.copy(es) scenario.ei = np.copy(ei) if SAVE_INTERN_FIELD: scenario.et = np.copy(et) if SAVE_MAP: scenario.epsilon_r = np.copy(epsilon_r) self.et = et self.ei = ei self.es = es self.epsilon_r = epsilon_r self.sigma = None self.radius_proportion = radius_proportion self.contrast = contrast self.problem = PERFECT_DIELECTRIC_PROBLEM