def __init__(self, c, funcgen_tol, funcgen_order): # c: boundary coordinates, complex self.c = c self.cx = self.c.real.copy() self.cy = self.c.imag.copy() # get some useful boundary-related information self.n = self.c.size self.dt = 2 * np.pi / self.n self.ts = np.arange(self.n) * self.dt self.ik = np.fft.fftfreq(self.n, 1.0 / self.n) # get derivatives of this self.cp = fourier_derivative_1d(f=self.c, d=1, ik=self.ik) self.cpp = fourier_derivative_1d(f=self.c, d=2, ik=self.ik) # get direct fourier evaluator for this self.dirf_c = periodic_interp1d(self.c) self.dirf_cp = periodic_interp1d(self.cp) self.dirf_cpp = periodic_interp1d(self.cpp) # build function generator representations of these functions self.funcgen_c = FunctionGenerator(self.dirf_c, 0, 2 * np.pi, funcgen_tol, n=funcgen_order) self.funcgen_cp = FunctionGenerator(self.dirf_cp, 0, 2 * np.pi, funcgen_tol, n=funcgen_order) self.funcgen_cpp = FunctionGenerator(self.dirf_cpp, 0, 2 * np.pi, funcgen_tol, n=funcgen_order)
def compute_local_coordinates_nufft_old(cx, cy, x, y, newton_tol=1e-12, guess_ind=None, verbose=False, max_iterations=30): """ Find using the coordinates: x = X + r n_x y = Y + r n_y """ xshape = x.shape yshape = y.shape x = x.flatten() y = y.flatten() n = cx.shape[0] dt = 2 * np.pi / n ts = np.arange(n) * dt ik = 1j * np.fft.fftfreq(n, dt / (2 * np.pi)) # tangent vectors xp = fourier_derivative_1d(f=cx, d=1, ik=ik, out='f') yp = fourier_derivative_1d(f=cy, d=1, ik=ik, out='f') # speed sp = np.sqrt(xp * xp + yp * yp) isp = 1.0 / sp # unit tangent vectors tx = xp * isp ty = yp * isp # unit normal vectors nx = ty ny = -tx # derivatives of the normal vector nxp = fourier_derivative_1d(f=nx, d=1, ik=ik, out='f') nyp = fourier_derivative_1d(f=ny, d=1, ik=ik, out='f') # interpolation routines for the necessary objects if have_better_fourier: def interp(f): return _interp2(f) else: def interp(f): return _interp(np.fft.fft(f), x.size) nx_i = interp(nx) ny_i = interp(ny) nxp_i = interp(nxp) nyp_i = interp(nyp) x_i = interp(cx) y_i = interp(cy) xp_i = interp(xp) yp_i = interp(yp) # functions for coordinate transform and its jacobian def f(t, r): x = x_i(t) + r * nx_i(t) y = y_i(t) + r * ny_i(t) return x, y def Jac(t, r): dxdt = xp_i(t) + r * nxp_i(t) dydt = yp_i(t) + r * nyp_i(t) dxdr = nx_i(t) dydr = ny_i(t) J = np.zeros((t.shape[0], 2, 2), dtype=float) J[:, 0, 0] = dxdt J[:, 0, 1] = dydt J[:, 1, 0] = dxdr J[:, 1, 1] = dydr return J.transpose((0, 2, 1)) # brute force find of guess_inds if not provided (slow!) if guess_ind is None: xd = x - cx[:, None] yd = y - cy[:, None] dd = xd**2 + yd**2 guess_ind = dd.argmin(axis=0) # get starting points (initial guess for t and r) t = ts[guess_ind] cxg = cx[guess_ind] cyg = cy[guess_ind] xdg = x - cxg ydg = y - cyg r = np.sqrt(xdg**2 + ydg**2) # how about we re-sign r? nxg = nx[guess_ind] nyg = ny[guess_ind] xcr1 = cxg + r * nxg ycr1 = cyg + r * nyg d1 = np.hypot(xcr1 - x, ycr1 - y) xcr2 = cxg - r * nxg ycr2 = cyg - r * nyg d2 = np.hypot(xcr2 - x, ycr2 - y) better2 = d1 > d2 r[better2] *= -1 # begin Newton iteration xo, yo = f(t, r) remx = xo - x remy = yo - y rem = np.abs(np.sqrt(remx**2 + remy**2)).max() if verbose: print('Newton tol: {:0.2e}'.format(rem)) iteration = 0 while rem > newton_tol: J = Jac(t, r) delt = -np.linalg.solve(J, np.column_stack([remx, remy])) line_factor = 1.0 while True: t_new, r_new = t + line_factor * delt[:, 0], r + line_factor * delt[:, 1] try: xo, yo = f(t_new, r_new) remx = xo - x remy = yo - y rem_new = np.sqrt(remx**2 + remy**2).max() testit = True except: testit = False if testit and ((rem_new < (1 - 0.5 * line_factor) * rem) or line_factor < 1e-4): t = t_new # put theta back in [0, 2 pi] t[t < 0] += 2 * np.pi t[t > 2 * np.pi] -= 2 * np.pi r = r_new rem = rem_new break line_factor *= 0.5 if verbose: print('Newton tol: {:0.2e}'.format(rem)) iteration += 1 if iteration > max_iterations: raise Exception( 'Exceeded maximum number of iterations solving for coordinates .' ) return t, r
def compute_local_coordinates_polyi_centering(cx, cy, x, y, gi, newton_tol=1e-12, verbose=None, max_iterations=30): """ Find using the coordinates: x = X + r n_x y = Y + r n_y """ xshape = x.shape x = x.flatten() y = y.flatten() gi = gi.flatten() xsize = x.size n = cx.shape[0] dt = 2 * np.pi / n ts = np.arange(n) * dt ik = 1j * np.fft.fftfreq(n, dt / (2 * np.pi)) # tangent vectors xp = fourier_derivative_1d(f=cx, d=1, ik=ik, out='f') yp = fourier_derivative_1d(f=cy, d=1, ik=ik, out='f') # speed sp = np.sqrt(xp * xp + yp * yp) isp = 1.0 / sp # unit tangent vectors tx = xp * isp ty = yp * isp # unit normal vectors nx = ty ny = -tx # brute force find of guess_inds if not provided (slow!) guess_ind = np.empty(x.size, dtype=int) multi_guess_ind_finder_centering(cx, cy, x, y, guess_ind, gi, 10) # get starting points (initial guess for t and r) initial_ts = ts[guess_ind] # run the multi-newton solver t = _multi_newton(initial_ts, x, y, newton_tol, cx, cy, verbose, max_iterations) # need to determine the sign now X, Y = np.zeros_like(x), np.zeros_like(x) XP, YP = np.zeros_like(x), np.zeros_like(x) multi_polyi_p(cx, t, X, XP) multi_polyi_p(cy, t, Y, YP) ISP = 1.0 / np.sqrt(XP * XP + YP * YP) NX = YP / ISP NY = -XP / ISP r = np.hypot(X - x, Y - y) xe1 = X + r * NX ye1 = Y + r * NY err1 = np.hypot(xe1 - x, ye1 - y) xe2 = X - r * NX ye2 = Y - r * NY err2 = np.hypot(xe2 - x, ye2 - y) sign = (err1 < err2).astype(int) * 2 - 1 return t, r * sign
def compute_local_coordinates_nufft(cx, cy, x, y, newton_tol=1e-12, guess_ind=None, verbose=False, max_iterations=30): """ Find using the coordinates: x = X + r n_x y = Y + r n_y """ xshape = x.shape yshape = y.shape x = x.flatten() y = y.flatten() n = cx.shape[0] dt = 2 * np.pi / n ts = np.arange(n) * dt ik = 1j * np.fft.fftfreq(n, dt / (2 * np.pi)) # tangent vectors xp = fourier_derivative_1d(f=cx, d=1, ik=ik, out='f') yp = fourier_derivative_1d(f=cy, d=1, ik=ik, out='f') xpp = fourier_derivative_1d(f=cx, d=2, ik=ik, out='f') ypp = fourier_derivative_1d(f=cy, d=2, ik=ik, out='f') # speed sp = np.sqrt(xp * xp + yp * yp) isp = 1.0 / sp # unit tangent vectors tx = xp * isp ty = yp * isp # unit normal vectors nx = ty ny = -tx # interpolation routines for the necessary objects if have_better_fourier: def interp(f): return _interp2(f) else: def interp(f): return _interp(f, x.size) nc_i = interp(nx + 1j * ny) c_i = interp(cx + 1j * cy) cp_i = interp(xp + 1j * yp) cpp_i = interp(xpp + 1j * ypp) # function for computing (d^2)_s and its derivative def f(t, x, y): C = c_i(t) X = C.real Y = C.imag Cp = cp_i(t) Xp = Cp.real Yp = Cp.imag return Xp * (X - x) + Yp * (Y - y), X, Y, Xp, Yp def fp(t, x, y, X, Y, Xp, Yp): Cpp = cpp_i(t) Xpp = Cpp.real Ypp = Cpp.imag return Xpp * (X - x) + Ypp * (Y - y) + Xp * Xp + Yp * Yp # brute force find of guess_inds if not provided (slow!) if guess_ind is None: guess_ind = np.empty(x.size, dtype=int) multi_guess_ind_finder(cx, cy, x, y, guess_ind) # get starting guess t = ts[guess_ind] # begin Newton iteration rem, X, Y, Xp, Yp = f(t, x, y) mrem = np.abs(rem).max() if verbose: print('Newton tol: {:0.2e}'.format(mrem)) iteration = 0 while mrem > newton_tol: J = fp(t, x, y, X, Y, Xp, Yp) delt = -rem / J line_factor = 1.0 while True: t_new = t + line_factor * delt rem_new, X, Y, Xp, Yp = f(t_new, x, y) mrem_new = np.abs(rem_new).max() # try: # rem_new, X, Y, Xp, Yp = f(t_new, x, y) # mrem_new = np.abs(rem_new).max() # testit = True # except: # testit = False testit = True if testit and ((mrem_new < (1 - 0.5 * line_factor) * mrem) or line_factor < 1e-4): t = t_new # put theta back in [0, 2 pi] t[t < 0] += 2 * np.pi t[t > 2 * np.pi] -= 2 * np.pi rem = rem_new mrem = mrem_new break line_factor *= 0.5 if verbose: print('Newton tol: {:0.2e}'.format(mrem)) iteration += 1 if iteration > max_iterations: raise Exception( 'Exceeded maximum number of iterations solving for coordinates .' ) # need to determine the sign now C = c_i(t) X = C.real Y = C.imag if True: # use nx, ny from guess_inds to determine sign NX = nx[guess_ind] NY = ny[guess_ind] else: # interpolate to get these NC = nc_i(t) NX = NC.real NY = NC.imag r = np.hypot(X - x, Y - y) xe1 = X + r * NX ye1 = Y + r * NY err1 = np.hypot(xe1 - x, ye1 - y) xe2 = X - r * NX ye2 = Y - r * NY err2 = np.hypot(xe2 - x, ye2 - y) sign = (err1 < err2).astype(int) * 2 - 1 return t, r * sign
def compute_local_coordinates_polyi_old(cx, cy, x, y, newton_tol=1e-12, guess_ind=None, verbose=False, max_iterations=30): """ Find using the coordinates: x = X + r n_x y = Y + r n_y """ xshape = x.shape x = x.flatten() y = y.flatten() xsize = x.size n = cx.shape[0] dt = 2 * np.pi / n ts = np.arange(n) * dt ik = 1j * np.fft.fftfreq(n, dt / (2 * np.pi)) # tangent vectors xp = fourier_derivative_1d(f=cx, d=1, ik=ik, out='f') yp = fourier_derivative_1d(f=cy, d=1, ik=ik, out='f') # speed sp = np.sqrt(xp * xp + yp * yp) isp = 1.0 / sp # unit tangent vectors tx = xp * isp ty = yp * isp # unit normal vectors nx = ty ny = -tx # derivatives of the normal vector nxp = fourier_derivative_1d(f=nx, d=1, ik=ik, out='f') nyp = fourier_derivative_1d(f=ny, d=1, ik=ik, out='f') # brute force find of guess_inds if not provided (slow!) if guess_ind is None: xd = x - cx[:, None] yd = y - cy[:, None] dd = xd**2 + yd**2 guess_ind = dd.argmin(axis=0) # get starting points (initial guess for t and r) initial_ts = ts[guess_ind] cxg = cx[guess_ind] cyg = cy[guess_ind] xdg = x - cxg ydg = y - cyg initial_rs = np.sqrt(xdg**2 + ydg**2) # how about we re-sign r? nxg = nx[guess_ind] nyg = ny[guess_ind] xcr1 = cxg + initial_rs * nxg ycr1 = cyg + initial_rs * nyg d1 = np.hypot(xcr1 - x, ycr1 - y) xcr2 = cxg - initial_rs * nxg ycr2 = cyg - initial_rs * nyg d2 = np.hypot(xcr2 - x, ycr2 - y) better2 = d1 > d2 initial_rs[better2] *= -1 # run the multi-newton solver all_t, all_r = _multi_newton_old(initial_ts, initial_rs, x, y, newton_tol, cx, cy, nx, ny, xp, yp, nxp, nyp, verbose, max_iterations) return all_t, all_r