pad_zone = 0 verbose = True plot = True reparametrize = False slepian_r = 1.5 * M solver_type = 'spectral' # fourth or spectral coordinate_scheme = 'nufft' coordinate_tolerance = 1e-14 qfs_tolerance = 1e-14 # get heaviside function MOL = SlepianMollifier(slepian_r) # construct boundary bdy = GSB(c=star(nb, a=0.2, f=5)) if reparametrize: bdy = GSB(*arc_length_parameterize(bdy.x, bdy.y)) bh = bdy.dt * bdy.speed.min() # get number of gridpoints to roughly match boundary spacing ng = 2 * int(0.5 * 2.4 // bh) # construct a grid grid = Grid([-1.2, 1.2], ng, [-1.2, 1.2], ng, x_endpoints=[True, False], y_endpoints=[True, False]) # construct embedded boundary ebdy = EmbeddedBoundary(bdy, True, M, bh * 1, pad_zone=pad_zone,
if ngx == ngy: xmin = -1.0 xmax = 2.0 ymin = -1.5 ymax = ymin + (xmax - xmin) else: xmin = -1.0 xmax = 5.0 ymin = -1.5 ymax = ymin + (xmax - xmin) / 2 ################################################################################ # Get truth via just using Adams-Bashforth 2 on periodic domain bdy = GSB(c=star(nb, x=0.0, y=0.0, a=0.0, f=3)) bdy = GSB(*arc_length_parameterize(bdy.x, bdy.y)) bx = bdy.x by = bdy.y # generate a grid xv, h = np.linspace(xmin, xmax, ngx, endpoint=False, retstep=True) yv, _h = np.linspace(ymin, ymax, ngy, endpoint=False, retstep=True) if _h != h: raise Exception('Need grid to be isotropic') del _h x, y = np.meshgrid(xv, yv, indexing='ij') # fourier modes kvx = np.fft.fftfreq(ngx, h / (2 * np.pi)) kvy = np.fft.fftfreq(ngy, h / (2 * np.pi)) kvx[int(ngx / 2)] = 0.0 kvy[int(ngy / 2)] = 0.0
M = 16 pad_zone = 0 verbose = False plot = True reparametrize = True slepian_r = 2 * M solver_type = 'spectral' # fourth or spectral # get heaviside function MOL = SlepianMollifier(slepian_r) # construct boundary bdy1 = GSB(c=star(3 * nb, a=0.1, r=3, f=5)) bdy2 = GSB(c=squish(nb, x=-1.2, y=-0.7, b=0.4, rot=-np.pi / 4)) bdy3 = GSB(c=star(2 * nb, x=1, y=0.5, a=0.3, f=3)) if reparametrize: bdy1 = GSB(*arc_length_parameterize(bdy1.x, bdy1.y)) bdy2 = GSB(*arc_length_parameterize(bdy2.x, bdy2.y)) bdy3 = GSB(*arc_length_parameterize(bdy3.x, bdy3.y)) bh1 = bdy1.dt * bdy1.speed.min() bh2 = bdy2.dt * bdy2.speed.min() bh3 = bdy3.dt * bdy3.speed.min() bh = min(bh1, bh2, bh3) # get number of gridpoints to roughly match boundary spacing ng = 2 * int(0.5 * 6.4 // bh) # construct a grid grid = Grid([-3.0, 3.4], ng, [-3.2, 3.2], ng, x_endpoints=[True, False], y_endpoints=[True, False]) # construct embedded boundary
1 + np.cos(2 * np.pi * t)) # gradient function def gradient(f): fh = np.fft.fft2(f) fx = np.fft.ifft2(fh * ikx).real fy = np.fft.ifft2(fh * iky).real return fx, fy ################################################################################ # Get truth via just using Forward Euler on periodic domain bdy = GSB(c=star(nb, x=0.0, y=0.0, a=0.1, f=3)) bdy = GSB(*arc_length_parameterize(bdy.x, bdy.y)) bx = bdy.x by = bdy.y # generate a grid v, h = np.linspace(-1.5, 1.5, ng, endpoint=False, retstep=True) x, y = np.meshgrid(v, v, indexing='ij') # fourier modes kv = np.fft.fftfreq(ng, h / (2 * np.pi)) kv[int(ng / 2)] = 0.0 kx, ky = np.meshgrid(kv, kv, indexing='ij') ikx, iky = 1j * kx, 1j * ky # initial c field c0 = c0_function(x, y)
def generate(self, dt, fixed_grid=False): ebdyc = self.ebdyc ebdyc_old = self.ebdyc_old u, v = self.u, self.v uo, vo = self.uo, self.vo ux, uy, vx, vy = self.ux, self.uy, self.vx, self.vy uxo, uyo, vxo, vyo = self.uxo, self.uyo, self.vxo, self.vyo # interpolate the velocity ubs = ebdyc.interpolate_radial_to_boundary(u) vbs = ebdyc.interpolate_radial_to_boundary(v) # move all boundarys; generate new embedded boundaries new_ebdys = [] self.reparmed_ubs = [] self.reparmed_vbs = [] for ind, ebdy in enumerate(self.ebdyc): if False: # this is the ABF way # interpolate the velocity ub = ubs.bdy_value_list[ind] vb = vbs.bdy_value_list[ind] ubo_new_parm = self.ubos[ind] vbo_new_parm = self.vbos[ind] # move the boundary with Forward Euler bx = ebdy.bdy.x + 0.5 * dt * (3 * ub - ubo_new_parm) by = ebdy.bdy.y + 0.5 * dt * (3 * vb - vbo_new_parm) # repararmetrize the boundary bx, by, new_t = arc_length_parameterize( bx, by, filter_fraction=self.filter_fraction, return_t=True) # move these boundary values for velocity to the new parametrization self.reparmed_ubs.append( nufft_interpolation1d(new_t, np.fft.fft(ub))) self.reparmed_vbs.append( nufft_interpolation1d(new_t, np.fft.fft(vb))) # generate the new embedded boundary new_ebdy = ebdy.regenerate(bx, by) new_ebdys.append(new_ebdy) else: # this is the BDF way # interpolate the velocity ub = ubs.bdy_value_list[ind] vb = vbs.bdy_value_list[ind] ubo_new_parm = self.ubos[ind] vbo_new_parm = self.vbos[ind] # move the boundary with Forward Euler bx = ebdy.bdy.x + 0.5 * dt * (3 * ub - ubo_new_parm) by = ebdy.bdy.y + 0.5 * dt * (3 * vb - vbo_new_parm) # repararmetrize the boundary bx, by, new_t = arc_length_parameterize( bx, by, filter_fraction=self.filter_fraction, return_t=True) # move these boundary values for velocity to the new parametrization self.reparmed_ubs.append( nufft_interpolation1d(new_t, np.fft.fft(ub))) self.reparmed_vbs.append( nufft_interpolation1d(new_t, np.fft.fft(vb))) # generate the new embedded boundary new_ebdy = ebdy.regenerate(bx, by) new_ebdys.append(new_ebdy) new_ebdyc = EmbeddedBoundaryCollection(new_ebdys) # get dnager zone distance umax = np.sqrt(u * u + v * v).max() ddd = 2 * umax * dt # raise an exception if danger zone thicker than radial width if ddd > new_ebdyc[0].radial_width: raise Exception( 'Velocity is so fast that one timestep oversteps safety zones; reduce timestep.' ) # register the grid... if fixed_grid: new_ebdyc.register_grid(ebdyc.grid, danger_zone_distance=ddd) else: new_ebdyc.generate_grid(danger_zone_distance=ddd) # let's get the points that need to be interpolated to aap = new_ebdyc.pnar AP_key = ebdyc.register_points(aap.x, aap.y, dzl=new_ebdyc.danger_zone_list, gil=new_ebdyc.guess_ind_list) OAP_key = ebdyc_old.register_points(aap.x, aap.y, dzl=new_ebdyc.danger_zone_list, gil=new_ebdyc.guess_ind_list) # now we need to interpolate onto things AEP = ebdyc.registered_partitions[AP_key] OAEP = ebdyc_old.registered_partitions[OAP_key] # get departure points xd_all = np.zeros(aap.N) yd_all = np.zeros(aap.N) xD_all = np.zeros(aap.N) yD_all = np.zeros(aap.N) # advect those in the annulus c1n, c2n, c3n = AEP.get_Ns() oc1n, oc2n, oc3n = OAEP.get_Ns() # category 1 and 2 c1_2 = AEP.zone1_or_2 oc1_2 = OAEP.zone1_or_2 fc12 = np.logical_and(c1_2, oc1_2) fc12n = np.sum(fc12) # category 1 and 2 # NOTE: THESE INTERPOLATIONS CAN BE MADE FASTER BY EXPLOITING SHARED # GRIDPOINTS IF THAT IS ENFORCED IN GRID GENERATION # THIS IS NOT EXPLOITED, FOR THE TIME BEING uxh = ebdyc.interpolate_to_points(ux, aap.x, aap.y) uyh = ebdyc.interpolate_to_points(uy, aap.x, aap.y) vxh = ebdyc.interpolate_to_points(vx, aap.x, aap.y) vyh = ebdyc.interpolate_to_points(vy, aap.x, aap.y) uh = ebdyc.interpolate_to_points(u, aap.x, aap.y) vh = ebdyc.interpolate_to_points(v, aap.x, aap.y) uxoh = ebdyc_old.interpolate_to_points(uxo, aap.x, aap.y) uyoh = ebdyc_old.interpolate_to_points(uyo, aap.x, aap.y) vxoh = ebdyc_old.interpolate_to_points(vxo, aap.x, aap.y) vyoh = ebdyc_old.interpolate_to_points(vyo, aap.x, aap.y) uoh = ebdyc_old.interpolate_to_points(uo, aap.x, aap.y) voh = ebdyc_old.interpolate_to_points(vo, aap.x, aap.y) SLM = np.zeros([ fc12n, ] + [4, 4], dtype=float) SLR = np.zeros([ fc12n, ] + [ 4, ], dtype=float) # solve for departure points SLM[:, 0, 0] = uxh[fc12] SLM[:, 0, 1] = uyh[fc12] SLM[:, 0, 2] = 0.5 / dt SLM[:, 0, 3] = 0.0 SLM[:, 1, 0] = vxh[fc12] SLM[:, 1, 1] = vyh[fc12] SLM[:, 1, 2] = 0.0 SLM[:, 1, 3] = 0.5 / dt SLM[:, 2, 0] = 2.0 / dt + 3 * uxh[fc12] SLM[:, 2, 1] = 3 * uyh[fc12] SLM[:, 2, 2] = -uxoh[fc12] SLM[:, 2, 3] = -uyoh[fc12] SLM[:, 3, 0] = 3 * vxh[fc12] SLM[:, 3, 1] = 2.0 / dt + 3 * vyh[fc12] SLM[:, 3, 2] = -vxoh[fc12] SLM[:, 3, 3] = -vyoh[fc12] SLR[:, 0] = uh[fc12] SLR[:, 1] = vh[fc12] SLR[:, 2] = 3 * uh[fc12] - uoh[fc12] SLR[:, 3] = 3 * vh[fc12] - voh[fc12] OUT = np.linalg.solve(SLM, SLR) dx, dy, Dx, Dy = OUT[:, 0], OUT[:, 1], OUT[:, 2], OUT[:, 3] xd, yd = aap.x[fc12] - dx, aap.y[fc12] - dy xD, yD = aap.x[fc12] - Dx, aap.y[fc12] - Dy xd_all[fc12] = xd yd_all[fc12] = yd xD_all[fc12] = xD yD_all[fc12] = yD # categroy 3... this is the tricky one fc3n = aap.N - fc12n # print('Number of points in category 3 is:', fc3n) if fc3n > 0: for ind, (ebdy, ebdy_old) in enumerate(zip(ebdyc, ebdyc_old)): ub = ubs.bdy_value_list[ind] vb = vbs.bdy_value_list[ind] c3l = AEP.zone3l[ind] oc3l = OAEP.zone3l[ind] fc3l = np.unique(np.concatenate([c3l, oc3l])) th = ebdy.bdy.dt tk = ebdy.bdy.k def d1_der(f): return np.fft.ifft(np.fft.fft(f) * tk * 1j).real interp = lambda f: interp1d(0, 2 * np.pi, th, f, k=3, p=True) bx_interp = interp(ebdy.bdy.x) by_interp = interp(ebdy.bdy.y) bxs_interp = interp(d1_der(ebdy.bdy.x)) bys_interp = interp(d1_der(ebdy.bdy.y)) nx_interp = interp(ebdy.bdy.normal_x) ny_interp = interp(ebdy.bdy.normal_y) nxs_interp = interp(d1_der(ebdy.bdy.normal_x)) nys_interp = interp(d1_der(ebdy.bdy.normal_y)) urb = ebdy.interpolate_radial_to_boundary_normal_derivative( u[0]) vrb = ebdy.interpolate_radial_to_boundary_normal_derivative( v[0]) urrb = ebdy.interpolate_radial_to_boundary_normal_derivative2( u[0]) vrrb = ebdy.interpolate_radial_to_boundary_normal_derivative2( v[0]) ub_interp = interp(ub) vb_interp = interp(vb) urb_interp = interp(urb) vrb_interp = interp(vrb) urrb_interp = interp(urrb) vrrb_interp = interp(vrrb) ubs_interp = interp(d1_der(ub)) vbs_interp = interp(d1_der(vb)) urbs_interp = interp(d1_der(urb)) vrbs_interp = interp(d1_der(vrb)) urrbs_interp = interp(d1_der(urrb)) vrrbs_interp = interp(d1_der(vrrb)) old_bx_interp = interp(ebdy_old.bdy.x) old_by_interp = interp(ebdy_old.bdy.y) old_bxs_interp = interp(d1_der(ebdy_old.bdy.x)) old_bys_interp = interp(d1_der(ebdy_old.bdy.y)) old_nx_interp = interp(ebdy_old.bdy.normal_x) old_ny_interp = interp(ebdy_old.bdy.normal_y) old_nxs_interp = interp(d1_der(ebdy_old.bdy.normal_x)) old_nys_interp = interp(d1_der(ebdy_old.bdy.normal_y)) old_ub = ebdy_old.interpolate_radial_to_boundary(uo[0]) old_vb = ebdy_old.interpolate_radial_to_boundary(vo[0]) old_urb = ebdy_old.interpolate_radial_to_boundary_normal_derivative( uo[0]) old_vrb = ebdy_old.interpolate_radial_to_boundary_normal_derivative( vo[0]) old_urrb = ebdy_old.interpolate_radial_to_boundary_normal_derivative2( uo[0]) old_vrrb = ebdy_old.interpolate_radial_to_boundary_normal_derivative2( vo[0]) # i think the old parm is right, but should think about old_ub_interp = interp(old_ub) old_vb_interp = interp(old_vb) old_urb_interp = interp(old_urb) old_vrb_interp = interp(old_vrb) old_urrb_interp = interp(old_urrb) old_vrrb_interp = interp(old_vrrb) old_ubs_interp = interp(d1_der(old_ub)) old_vbs_interp = interp(d1_der(old_vb)) old_urbs_interp = interp(d1_der(old_urb)) old_vrbs_interp = interp(d1_der(old_vrb)) old_urrbs_interp = interp(d1_der(old_urrb)) old_vrrbs_interp = interp(d1_der(old_vrrb)) xx = aap.x[fc3l] yy = aap.y[fc3l] def objective(s, r, so, ro): f = np.empty([s.size, 4]) f[:, 0] = old_bx_interp(so) + ro * old_nx_interp( so) + 2 * dt * ub_interp(s) + 2 * dt * r * urb_interp( s) + dt * r**2 * urrb_interp(s) - xx f[:, 1] = old_by_interp(so) + ro * old_ny_interp( so) + 2 * dt * vb_interp(s) + 2 * dt * r * vrb_interp( s) + dt * r**2 * vrrb_interp(s) - yy f[:, 2] = bx_interp(s) + r * nx_interp( s ) + 1.5 * dt * ub_interp(s) + 1.5 * dt * r * urb_interp( s) + 0.75 * dt * r**2 * urrb_interp( s) - 0.5 * dt * old_ub_interp( so) - 0.5 * dt * ro * old_urb_interp( so) - 0.25 * dt * ro**2 * old_urrb_interp( so) - xx f[:, 3] = by_interp(s) + r * ny_interp( s ) + 1.5 * dt * vb_interp(s) + 1.5 * dt * r * vrb_interp( s) + 0.75 * dt * r**2 * vrrb_interp( s) - 0.5 * dt * old_vb_interp( so) - 0.5 * dt * ro * old_vrb_interp( so) - 0.25 * dt * ro**2 * old_vrrb_interp( so) - yy return f def Jac(s, r, so, ro): J = np.empty([s.size, 4, 4]) # derivative with respect to s J[:, 0, 0] = 2 * dt * ubs_interp(s) + 2 * dt * r * urbs_interp( s) + dt * r**2 * urrbs_interp(s) J[:, 1, 0] = 2 * dt * vbs_interp(s) + 2 * dt * r * vrbs_interp( s) + dt * r**2 * vrrbs_interp(s) J[:, 2, 0] = bxs_interp( s) + r * nxs_interp(s) + 1.5 * dt * ubs_interp( s) + 1.5 * dt * r * urbs_interp( s) + 0.75 * dt * r**2 * urrbs_interp(s) J[:, 3, 0] = bys_interp( s) + r * nys_interp(s) + 1.5 * dt * vbs_interp( s) + 1.5 * dt * r * vrbs_interp( s) + 0.75 * dt * r**2 * vrrbs_interp(s) # derivative with respect to r J[:, 0, 1] = 2 * dt * urb_interp(s) + 2 * dt * r * urrb_interp(s) J[:, 1, 1] = 2 * dt * vrb_interp(s) + 2 * dt * r * vrrb_interp(s) J[:, 2, 1] = nx_interp(s) + 1.5 * dt * urb_interp( s) + 1.5 * dt * r * urrb_interp(s) J[:, 3, 1] = ny_interp(s) + 1.5 * dt * vrb_interp( s) + 1.5 * dt * r * vrrb_interp(s) # derivative with respect to so J[:, 0, 2] = old_bxs_interp(so) + ro * old_nxs_interp(so) J[:, 1, 2] = old_bys_interp(so) + ro * old_nys_interp(so) J[:, 2, 2] = -0.5 * dt * old_ubs_interp( so) - 0.5 * dt * ro * old_urbs_interp( so) - 0.25 * dt * ro**2 * old_urrbs_interp(so) J[:, 3, 2] = -0.5 * dt * old_vbs_interp( so) - 0.5 * dt * ro * old_vrbs_interp( so) - 0.25 * dt * ro**2 * old_vrrbs_interp(so) # derivative with respect to ro J[:, 0, 3] = old_nx_interp(so) J[:, 1, 3] = old_ny_interp(so) J[:, 2, 3] = -0.5 * dt * old_urb_interp( so) - 0.5 * dt * ro * old_urrb_interp(so) J[:, 3, 3] = -0.5 * dt * old_vrb_interp( so) - 0.5 * dt * ro * old_vrrb_interp(so) return J # take as guess inds our s, r s = AEP.full_t[fc3l] r = AEP.full_r[fc3l] so = OAEP.full_t[fc3l] ro = OAEP.full_r[fc3l] # now solve for sd, rd res = objective(s, r, so, ro) mres1 = np.hypot(res[:, 0], res[:, 1]).max() mres2 = np.hypot(res[:, 2], res[:, 3]).max() mres = max(mres1, mres2) tol = 1e-12 while mres > tol: J = Jac(s, r, so, ro) d = np.linalg.solve(J, res) s -= d[:, 0] r -= d[:, 1] so -= d[:, 2] ro -= d[:, 3] res = objective(s, r, so, ro) mres1 = np.hypot(res[:, 0], res[:, 1]).max() mres2 = np.hypot(res[:, 2], res[:, 3]).max() mres = max(mres1, mres2) r_fail_1 = r.max() > 0.0 r_fail_2 = r.min() < -ebdy.radial_width ro_fail_1 = ro.max() > 0.0 ro_fail_2 = ro.min() < -ebdy_old.radial_width r_fail = r_fail_1 or r_fail_2 ro_fail = ro_fail_1 or ro_fail_2 fail = r_fail or ro_fail fail_amount = 0.0 if fail: if r_fail_1: fail_amount = max(fail_amount, r.max()) r[r > 0.0] = 0.0 if r_fail_2: fail_amount = max(fail_amount, (-r - ebdy.radial_width).max()) r[r < -ebdy.radial_width] = -ebdy.radial_width if ro_fail_1: fail_amount = max(fail_amount, ro.max()) ro[ro > 0.0] = 0.0 if ro_fail_2: fail_amount = max(fail_amount, (-ro - ebdy_old.radial_width).max()) ro[ro < -ebdy_old.radial_width] = -ebdy_old.radial_width # get the departure points xd = bx_interp(s) + nx_interp(s) * r yd = by_interp(s) + ny_interp(s) * r xD = old_bx_interp(so) + old_nx_interp(so) * ro yD = old_by_interp(so) + old_ny_interp(so) * ro xd_all[fc3l] = xd yd_all[fc3l] = yd xD_all[fc3l] = xD yD_all[fc3l] = yD self.new_ebdyc = new_ebdyc self.xd_all = xd_all self.yd_all = yd_all self.xD_all = xD_all self.yD_all = yD_all return self.new_ebdyc
def generate(self, dt, fixed_grid=False): """ If fixed_grid = True, reuse same grid Otherwise, generate new grid """ ebdyc = self.ebdyc u, v = self.u, self.v ux, uy, vx, vy = self.ux, self.uy, self.vx, self.vy # interpolate the velocity ubs = ebdyc.interpolate_radial_to_boundary(u) vbs = ebdyc.interpolate_radial_to_boundary(v) # move all boundarys; generate new embedded boundaries new_ebdys = [] self.reparmed_ubs = [] self.reparmed_vbs = [] for ind, ebdy in enumerate(ebdyc): # interpolate the velocity ub = ubs[ind] vb = vbs[ind] # move the boundary with Forward Euler bx = ebdy.bdy.x + dt*ub by = ebdy.bdy.y + dt*vb # repararmetrize the boundary bx, by, new_t = arc_length_parameterize(bx, by, filter_fraction=self.filter_fraction, return_t=True) # bx, by, new_t = bx, by, np.linspace(0, 2*np.pi, bx.size, endpoint=False) # bx, by, new_t = arc_length_parameterize(bx, by, return_t=True, filter_function=self.filter_function) # move these boundary values to the new parametrization # This is not necessary for this timestepper, but is used by other # timesteppers which use this as a startup! # SHOULD I SWITCH THIS TO NUFFT WHEN THAT IS BEING USED? self.reparmed_ubs.append(nufft_interpolation1d(new_t, np.fft.fft(ub))) self.reparmed_vbs.append(nufft_interpolation1d(new_t, np.fft.fft(vb))) # bu_interp = interp1d(0, 2*np.pi, ebdy.bdy.dt, ub, p=True) # bv_interp = interp1d(0, 2*np.pi, ebdy.bdy.dt, vb, p=True) # self.reparmed_ubs.append(bu_interp(new_t)) # self.reparmed_vbs.append(bv_interp(new_t)) # generate the new embedded boundary new_ebdy = ebdy.regenerate(bx, by) new_ebdys.append(new_ebdy) new_ebdyc = EmbeddedBoundaryCollection(new_ebdys) # get dnager zone distance umax = np.sqrt(u*u + v*v).max() ddd = 2*umax*dt # raise an exception if danger zone thicker than radial width if ddd > new_ebdyc[0].radial_width: raise Exception('Velocity is so fast that one timestep oversteps safety zones; reduce timestep.') # register the grid... if fixed_grid: new_ebdyc.register_grid(ebdyc.grid, danger_zone_distance=ddd) else: new_ebdyc.generate_grid(danger_zone_distance=ddd) # let's get the points that need to be interpolated to aap = new_ebdyc.pnar AP_key = ebdyc.register_points(aap.x, aap.y, dzl=new_ebdyc.danger_zone_list, gil=new_ebdyc.guess_ind_list) # now we need to interpolate onto things AEP = ebdyc.registered_partitions[AP_key] # get departure points xd_all = np.zeros(aap.N) yd_all = np.zeros(aap.N) c1n, c2n, c3n = AEP.get_Ns() # category 1 and 2 c1_2n = c1n + c2n c1_2 = AEP.zone1_or_2 uxh = ebdyc.interpolate_to_points(ux, aap.x, aap.y) uyh = ebdyc.interpolate_to_points(uy, aap.x, aap.y) vxh = ebdyc.interpolate_to_points(vx, aap.x, aap.y) vyh = ebdyc.interpolate_to_points(vy, aap.x, aap.y) uh = ebdyc.interpolate_to_points(u, aap.x, aap.y) vh = ebdyc.interpolate_to_points(v, aap.x, aap.y) SLM = np.zeros([c1_2n,] + [2,2], dtype=float) SLR = np.zeros([c1_2n,] + [2,], dtype=float) SLM[:,0,0] = 1 + dt*uxh[c1_2] SLM[:,0,1] = dt*uyh[c1_2] SLM[:,1,0] = dt*vxh[c1_2] SLM[:,1,1] = 1 + dt*vyh[c1_2] SLR[:,0] = dt*uh[c1_2] SLR[:,1] = dt*vh[c1_2] OUT = np.linalg.solve(SLM, SLR) xdt, ydt = OUT[:,0], OUT[:,1] xd, yd = aap.x[c1_2] - xdt, aap.y[c1_2] - ydt xd_all[c1_2] = xd yd_all[c1_2] = yd # categroy 3... this is the tricky one if c3n > 0: for ind, ebdy in enumerate(ebdyc): ub = ubs[ind] vb = vbs[ind] c3l = AEP.zone3l[ind] th = ebdy.bdy.dt # th = 2*np.pi/nb # tk = np.fft.fftfreq(nb, th/(2*np.pi)) tk = ebdy.bdy.k def d1_der(f): return np.fft.ifft(np.fft.fft(f)*tk*1j).real interp = lambda f: interp1d(0, 2*np.pi, th, f, k=3, p=True) bx_interp = interp(ebdy.bdy.x) by_interp = interp(ebdy.bdy.y) bxs_interp = interp(d1_der(ebdy.bdy.x)) bys_interp = interp(d1_der(ebdy.bdy.y)) nx_interp = interp(ebdy.bdy.normal_x) ny_interp = interp(ebdy.bdy.normal_y) nxs_interp = interp(d1_der(ebdy.bdy.normal_x)) nys_interp = interp(d1_der(ebdy.bdy.normal_y)) urb = ebdy.interpolate_radial_to_boundary_normal_derivative(u[ind]) vrb = ebdy.interpolate_radial_to_boundary_normal_derivative(v[ind]) ub_interp = interp(ub) vb_interp = interp(vb) urb_interp = interp(urb) vrb_interp = interp(vrb) ubs_interp = interp(d1_der(ub)) vbs_interp = interp(d1_der(vb)) urbs_interp = interp(d1_der(urb)) vrbs_interp = interp(d1_der(vrb)) xo = aap.x[c3l] yo = aap.y[c3l] def objective(s, r): f = np.empty([s.size, 2]) f[:,0] = bx_interp(s) + r*nx_interp(s) + dt*ub_interp(s) + dt*r*urb_interp(s) - xo f[:,1] = by_interp(s) + r*ny_interp(s) + dt*vb_interp(s) + dt*r*vrb_interp(s) - yo return f def Jac(s, r): J = np.empty([s.size, 2, 2]) J[:,0,0] = bxs_interp(s) + r*nxs_interp(s) + dt*ubs_interp(s) + dt*r*urbs_interp(s) J[:,1,0] = bys_interp(s) + r*nys_interp(s) + dt*vbs_interp(s) + dt*r*vrbs_interp(s) J[:,0,1] = nx_interp(s) + dt*urb_interp(s) J[:,1,1] = ny_interp(s) + dt*vrb_interp(s) return J # take as guess inds our s, r s = AEP.zone3t[ind] r = AEP.zone3r[ind] # now solve for sd, rd res = objective(s, r) mres = np.hypot(res[:,0], res[:,1]).max() tol = 1e-12 while mres > tol: J = Jac(s, r) d = np.linalg.solve(J, res) s -= d[:,0] r -= d[:,1] res = objective(s, r) mres = np.hypot(res[:,0], res[:,1]).max() # get the departure points xd = bx_interp(s) + nx_interp(s)*r yd = by_interp(s) + ny_interp(s)*r xd_all[c3l] = xd yd_all[c3l] = yd self.new_ebdyc = new_ebdyc self.xd_all = xd_all self.yd_all = yd_all return self.new_ebdyc