def __init__(self, ebdyc, x, y, fix_r=True): self.grid = ebdyc.grid self.x_check = x self.y_check = y self.sh = x.shape self.x = x.ravel() self.y = y.ravel() self.size = self.x.size # zone 1 is physical and not inside an annulus zone1 = np.ones(self.size, dtype=bool) zone1_or_2 = np.ones(self.size, dtype=bool) # zone 2 is physical and inside an annulus # given as a list of indeces # the r, transformed r, and t values are also saved zone2l = [] zone2r = [] zone2_transfr = [] zone2t = [] # zone 3 is non-physical # given as list of indeces # with r and t values also saved zone3l = [] zone3r = [] zone3t = [] # loop over embedded boundaries for ind, ebdy in enumerate(ebdyc): code, interior, computed, full_r, full_t = ebdy.coordinate_mapper.classify(self.x, self.y) is_annulus = code == 1 is_phys_not_annulus = code == (3 if ebdy.interior else 2) is_phys = interior if ebdy.interior else ~interior zone1 = np.logical_and(zone1, is_phys_not_annulus) zone_1_or_2 = np.logical_and(zone1_or_2, is_phys) # get in annulus points zone2l.append(np.where(is_annulus)[0]) rhere = full_r[is_annulus] zone2r.append(rhere) zone2_transfr.append(ebdy.nufft_transform_r(rhere)) zone2t.append(full_t[is_annulus]) # i don't think there's a reason for zone3 to be a list? zone3 = ~zone_1_or_2 # save these away self.zone1 = zone1 self.zone1_or_2 = zone1_or_2 self.zone2l = zone2l self.zone2r = zone2r self.zone2_transfr = zone2_transfr self.zone2t = zone2t # get Ns self.zone1_N = int(np.sum(self.zone1)) self.zone2_Ns = [len(z2) for z2 in self.zone2l] self.zone2_N = int(np.sum(self.zone2_Ns)) self.zone3_N = int(np.sum(zone3)) # get transformed x's, y's for NUFFT interpolation (in zone1) self.x_transf = affine_transformation(self.x[self.zone1], self.grid.x_bounds[0], self.grid.x_bounds[1], 0.0, 2*np.pi, use_numexpr=True) self.y_transf = affine_transformation(self.y[self.zone1], self.grid.y_bounds[0], self.grid.y_bounds[1], 0.0, 2*np.pi, use_numexpr=True)
def register_grid(self, grid): """ Register a grid object grid (required): grid of type(Grid) from pybie2d (see pybie2d doc) """ self.grid = grid ######################################################################## # Locate grid points in annulus/curve and compute coordinates # get coordinates for points in radial region codes, interior, computed, r, t = self.coordinate_mapper( self.grid.xg, self.grid.yg, inner_coord=self.inner_radial_distance, outer_coord=self.outer_radial_distance, grid=False, **self.coordinate_search_kwargs ) # store interior / exterior bools self.grid_interior = interior self.grid_exterior = ~interior # compute those in the annulus ia = codes == 1 # reduce to those in the annulus r_small = r[ia] t_small = t[ia] self.grid_ia_r = r_small self.grid_ia_t = t_small wh = np.where(ia) self.grid_ia_xind = wh[0] self.grid_ia_yind = wh[1] # compute heaviside cutoff function lb = -self.heaviside_width if self.interior else self.heaviside_width grts = affine_transformation(self.grid_ia_r, lb, 0, -1, 1, use_numexpr=True) self.grid_to_radial_step = 1.0 - self.heaviside.step(grts) arts = affine_transformation(self.radial_rv, lb, 0, -1, 1, use_numexpr=True) self.radial_cutoff = self.heaviside.step(arts) # transform for NUFFTs self.grid_ia_r_transf = self.nufft_transform_r(self.grid_ia_r) self.interface_x_transf = affine_transformation(self.interface.x, self.grid.x_bounds[0], self.grid.x_bounds[1], 0.0, 2*np.pi, use_numexpr=True) self.interface_y_transf = affine_transformation(self.interface.y, self.grid.y_bounds[0], self.grid.y_bounds[1], 0.0, 2*np.pi, use_numexpr=True)
def ready_bump(self, bump_loc=None, bump_width=None): if bump_width == None: bump_width = self[0].radial_width if bump_loc == None: if self.bump_location is None: raise Exception('if ebdyc has no bump_location, need to give bump_loc') bump_loc = self.bump_location grr = np.hypot(self.grid.xg-bump_loc[0], self.grid.yg-bump_loc[1]) bumpy = self[0].heaviside.bump(affine_transformation(grr, 0, bump_width, 0, 1)) bumpy_int = self.grid_integral(bumpy) self.bumpy = bumpy / bumpy_int self.bumpy_readied = True
Jxy = uyr Jyx = vxr Jyy = 1+vyr det = Jxx*Jyy - Jxy*Jyx # get a function c c = c_func(xr, yr) # evaluate c at these new positions c2 = c_func(xr2, yr2) # get r, t coordinates for xr2, yr2 t, r = compute_local_coordinates(bdy.x, bdy.y, xr2, yr2, 1e-14, verbose=True) lb = -ebdy.radial_width ub = 0.0 starb = affine_transformation(r, lb, ub, 1.0, -1.0, use_numexpr=True) rtransf = np.arccos(starb) # interpolate from c2 back to (xr, yr) and check against c c2w = c2*det c2e = np.row_stack([c2w, c2w[::-1]]) ch = np.zeros([2*M,nb], dtype=complex, order='F') finufftpy.nufft2d1(xr2, yr2, c2e.astype(complex), -1, 1e-14, 2*M, nb, ch, modeord=1) cw = cc*det ch = np.zeros([n,n], dtype=complex, order='F') finufftpy.nufft2d1(xx, yy, cw.astype(complex), -1, 1e-14, n, n, ch, modeord=1) cr = np.fft.ifft2(ch). real
def __init__(self, ebdyc, x, y, fix_r=False, dzl=None, gil=None): """ ebdyc: embedded boundary collection x: x-coordinates of points to partition y: y-coordinates of points to partition fix_r: after finding coordinates, do we set any with r that would place them in the aphysical region to 0 (to lie on the boundary?) note: this should only be used if you're condifident all points are actually physical. in this case this is really being used to deal with very small errors from the Newton solver dzl: danger zone list: if available, a list of "danger zones" which give points for which coordinates should be computed if this is accurate enough, this will considerably speed up the calculation, as a near-points finder will not need to be called if it is not accurate, it may cause the coordinate solver to fail gil: guess index list: a list of "guess indeces", corresponding to the "danger zone list", giving initial guesses for the coordinate solving scheme """ self.grid = ebdyc.grid self.x_check = x self.y_check = y self.fix_r = fix_r self.dzl = dzl self.gil = gil self.sh = x.shape self.x = x.ravel() self.y = y.ravel() self.size = self.x.size if self.dzl is not None and self.gil is not None: self.has_danger_zone = [ dz is not None and gi is not None for dz, gi in zip(self.dzl, self.gil) ] else: self.has_danger_zone = [ None, ] * len(ebdyc) # zone 1 is physical and not inside an annulus zone1 = np.ones(self.size, dtype=bool) zone1_or_2 = np.ones(self.size, dtype=bool) # zone 2 is physical and inside an annulus # given as a list of indeces # the r, transformed r, and t values are also saved zone2l = [] zone2r = [] zone2_transfr = [] zone2t = [] # zone 3 is non-physical # given as list of indeces # with r and t values also saved zone3l = [] zone3r = [] zone3t = [] # holders for t, r for anything that we compute full_t = np.empty(self.size, dtype=float) full_r = np.empty(self.size, dtype=float) # holders for the one that has to interact with everything long_in_this_annulus = np.zeros(self.size, dtype=bool) # loop over embedded boundaries for ind, ebdy in enumerate(ebdyc): bx = ebdy.bdy.x by = ebdy.bdy.y width = ebdy.radial_width interior = ebdy.interior has_danger_zone = self.has_danger_zone[ind] if has_danger_zone: dz = self.dzl[ind] gi = self.gil[ind] else: dz, gi = points_near_curve_coarse(bx, by, self.x, self.y, width) gi = gi[dz] dz = np.where(dz)[0] # find coordinates for danger points check_x = self.x[dz] check_y = self.y[dz] res = compute_local_coordinates( bx, by, check_x, check_y, newton_tol=ebdy.coordinate_tolerance, interpolation_scheme=ebdy.coordinate_scheme, guess_ind=gi, verbose=False) t = res[0] r = res[1] # fix r values, if required if self.fix_r: ebdy.fix_r(r) full_t[dz] = t full_r[dz] = r # check on in annulus in_this_annulus, phys_not_in_this_annulus, \ exterior = ebdy.check_if_r_in_annulus(r) # this is a plot to check on how this looks... # assuming the points come from new_ebdyc if False: fig, ax = plt.subplots() ax.pcolormesh(grid.xg, grid.yg, ebdyc.phys) ax.plot(ebdyc[0].bdy.x, ebdyc[0].bdy.y, color='gray') ax.plot(ebdyc[0].interface.x, ebdyc[0].interface.y, color='gray') ax.plot(new_ebdyc[0].bdy.x, new_ebdyc[0].bdy.y, color='black') ax.plot(new_ebdyc[0].interface.x, new_ebdyc[0].interface.y, color='black') ax.scatter(check_x[in_this_annulus], check_y[in_this_annulus], color='white') ax.scatter(check_x[exterior], check_y[exterior], color='orange') ax.scatter(check_x[phys_not_in_this_annulus], check_y[phys_not_in_this_annulus], color='blue') # set indicators in zone1 for those owned by this boundary zone1[dz] = np.logical_and(zone1[dz], phys_not_in_this_annulus) zone1_or_2[dz] = np.logical_and(zone1_or_2[dz], np.logical_not(exterior)) # now save the required information in the zone2 and zone3 lists zone2l.append(dz[in_this_annulus]) rhere = r[in_this_annulus] zone2r.append(rhere) zone2_transfr.append(ebdy.nufft_transform_r(rhere)) zone2t.append(t[in_this_annulus]) zone3l.append(dz[exterior]) zone3r.append(r[exterior]) zone3t.append(t[exterior]) # plot to check on zone1 / zone1_or_2 if False: fig, ax = plt.subplots() ax.pcolormesh(grid.xg, grid.yg, ebdyc.phys) ax.plot(ebdyc[0].bdy.x, ebdyc[0].bdy.y, color='gray') ax.plot(ebdyc[0].interface.x, ebdyc[0].interface.y, color='gray') ax.plot(new_ebdyc[0].bdy.x, new_ebdyc[0].bdy.y, color='black') ax.plot(new_ebdyc[0].interface.x, new_ebdyc[0].interface.y, color='black') ax.scatter(self.x[zone1], self.y[zone1], color='white') nz1 = np.logical_not(zone1) ax.scatter(self.x[nz1], self.y[nz1], color='orange') fig, ax = plt.subplots() ax.pcolormesh(grid.xg, grid.yg, ebdyc.phys) ax.plot(ebdyc[0].bdy.x, ebdyc[0].bdy.y, color='gray') ax.plot(ebdyc[0].interface.x, ebdyc[0].interface.y, color='gray') ax.plot(new_ebdyc[0].bdy.x, new_ebdyc[0].bdy.y, color='black') ax.plot(new_ebdyc[0].interface.x, new_ebdyc[0].interface.y, color='black') ax.scatter(self.x[zone1_or_2], self.y[zone1_or_2], color='white') nz1_or_2 = np.logical_not(zone1_or_2) ax.scatter(self.x[nz1_or_2], self.y[nz1_or_2], color='orange') # save these away self.zone1 = zone1 self.zone1_or_2 = zone1_or_2 self.zone2l = zone2l self.zone2r = zone2r self.zone2_transfr = zone2_transfr self.zone2t = zone2t self.zone3l = zone3l self.zone3r = zone3r self.zone3t = zone3t self.full_t = full_t self.full_r = full_r # get Ns self.zone1_N = np.sum(self.zone1) self.zone2_Ns = [len(z2) for z2 in self.zone2l] self.zone3_Ns = [len(z3) for z3 in self.zone3l] self.zone2_N = np.sum(self.zone2_Ns) self.zone3_N = np.sum(self.zone3_Ns) print(self.zone1_N, self.zone2_N, self.zone3_N) # get transformed x's, y's for NUFFT interpolation (in zone1) self.x_transf = affine_transformation(self.x[self.zone1], self.grid.x_bounds[0], self.grid.x_bounds[1], 0.0, 2 * np.pi, use_numexpr=True) self.y_transf = affine_transformation(self.y[self.zone1], self.grid.y_bounds[0], self.grid.y_bounds[1], 0.0, 2 * np.pi, use_numexpr=True)
def nufft_transform_r(self, r): lb = -self.radial_width if self.interior else 0.0 ub = 0.0 if self.interior else self.radial_width return np.arccos(affine_transformation(r, lb, ub, 1.0, -1.0, use_numexpr=True))
def __init__(self, ebdyc, x, y, fix_r=False): """ ebdyc: embedded boundary collection x: x-coordinates of points to partition y: y-coordinates of points to partition fix_r: after finding coordinates, do we set any with r that would place them in the aphysical region to 0 (to lie on the boundary?) note: this should only be used if you're condifident all points are actually physical. in this case this is really being used to deal with very small errors from the Newton solver """ self.grid = ebdyc.grid self.x_check = x self.y_check = y self.fix_r = fix_r self.sh = x.shape self.x = x.ravel() self.y = y.ravel() self.size = self.x.size # zone 1 is physical and not inside an annulus zone1 = np.ones(self.size, dtype=bool) zone1_or_2 = np.ones(self.size, dtype=bool) # zone 2 is physical and inside an annulus # given as a list of indeces # the r, transformed r, and t values are also saved zone2l = [] zone2r = [] zone2_transfr = [] zone2t = [] # zone 3 is non-physical # given as list of indeces # with r and t values also saved zone3l = [] zone3r = [] zone3t = [] # holders for the one that has to interact with everything long_in_this_annulus = np.zeros(self.size, dtype=bool) # loop over embedded boundaries for ind, ebdy in enumerate(ebdyc): bx = ebdy.bdy.x by = ebdy.bdy.y width = ebdy.radial_width interior = ebdy.interior # find coordintes # full_r, full_t, have_coords, interior = ebdy.coordinate_mapper.classify(x, y) code, interior, computed, full_r, full_t = ebdy.coordinate_mapper.classify(x, y) have_coords = computed r = full_r[have_coords] t = full_t[have_coords] # fix r values, if required if self.fix_r: ebdy.fix_r(r) # check on in annulus in_this_annulus, phys_not_in_this_annulus, \ exterior = ebdy.check_if_r_in_annulus(r) # set indicators in zone1 for those owned by this boundary is_phys = interior if ebdy.interior else ~interior zone1 = np.logical_and(zone1, is_phys) zone1_or_2 = np.logical_and(zone1_or_2, is_phys) zone1[have_coords] = np.logical_and(zone1[have_coords], phys_not_in_this_annulus) zone1_or_2[have_coords] = np.logical_and(zone1_or_2[have_coords], np.logical_not(exterior)) # dz is just an old name from past routines dz = np.where(have_coords)[0] # zone 2 inds and coords zone2l.append(dz[in_this_annulus]) rhere = r[in_this_annulus] zone2r.append(rhere) zone2_transfr.append(ebdy.nufft_transform_r(rhere)) zone2t.append(t[in_this_annulus]) # zone 3 inds and coords zone3l.append(dz[exterior]) zone3r.append(r[exterior]) zone3t.append(t[exterior]) # save these away self.zone1 = zone1 self.zone1_or_2 = zone1_or_2 self.zone2l = zone2l self.zone2r = zone2r self.zone2_transfr = zone2_transfr self.zone2t = zone2t self.zone3l = zone3l self.zone3r = zone3r self.zone3t = zone3t self.full_t = full_t self.full_r = full_r # get Ns self.zone1_N = int(np.sum(self.zone1)) self.zone2_Ns = [len(z2) for z2 in self.zone2l] self.zone2_N = int(np.sum(self.zone2_Ns)) self.zone3_Ns = [len(z3) for z3 in self.zone3l] self.zone3_N = int(np.sum(self.zone3_Ns)) # get transformed x's, y's for NUFFT interpolation (in zone1) self.x_transf = affine_transformation(self.x[self.zone1], self.grid.x_bounds[0], self.grid.x_bounds[1], 0.0, 2*np.pi, use_numexpr=True) self.y_transf = affine_transformation(self.y[self.zone1], self.grid.y_bounds[0], self.grid.y_bounds[1], 0.0, 2*np.pi, use_numexpr=True)
def register_grid(self, grid, close, int_helper1, int_helper2, float_helper, bool_helper, index, danger_zone_distance=None, verbose=False): """ Register a grid object grid (required): grid of type(Grid) from pybie2d (see pybie2d doc) verbose (optional): bool, whether to pass verbose output onto gridpoints_near_curve """ self.grid = grid ######################################################################## # Locate grid points in annulus/curve and compute coordinates ddd = danger_zone_distance if danger_zone_distance is not None else 0.0 # find near points and corresponding coordinates out = gridpoints_near_curve_update( self.bdy.x, self.bdy.y, self.grid.xv, self.grid.yv, self.radial_width + ddd, index, close, int_helper1, int_helper2, float_helper, bool_helper, interpolation_scheme=self.coordinate_scheme, tol=self.coordinate_tolerance, verbose=verbose) self.near_curve_result = out # wrangle output from gridpoints_near_curve nclose = out[0] iax = out[1] iay = out[2] r = out[3] t = out[4] # actually in annulus ia_good, _, _ = self.check_if_r_in_annulus(r) # reduce to the good ones iax_small = iax[ia_good] iay_small = iay[ia_good] r_small = r[ia_good] t_small = t[ia_good] # save coordinate values for those points that are in annulus self.grid_ia_xind = iax_small self.grid_ia_yind = iay_small self.grid_ia_x = self.grid.xv[iax_small] self.grid_ia_y = self.grid.yv[iay_small] self.grid_ia_r = r_small self.grid_ia_t = t_small # for use in NUFFT interpolation self.grid_ia_r_transf = self.nufft_transform_r(self.grid_ia_r) self.interface_x_transf = affine_transformation(self.interface.x, self.grid.x_bounds[0], self.grid.x_bounds[1], 0.0, 2 * np.pi, use_numexpr=True) self.interface_y_transf = affine_transformation(self.interface.y, self.grid.y_bounds[0], self.grid.y_bounds[1], 0.0, 2 * np.pi, use_numexpr=True) ######################################################################## # Compute regularized Heaviside functions for coupling grids if not self.have_heavisides: lb = -self.heaviside_width if self.interior else self.heaviside_width grts = affine_transformation(self.grid_ia_r, lb, 0, -1, 1, use_numexpr=True) self.grid_to_radial_step = 1.0 - self.heaviside(grts) arts = affine_transformation(self.radial_rv, lb, 0, -1, 1, use_numexpr=True) self.radial_cutoff = self.heaviside(arts) # add these to kwargs for saving self.kwargs['grid_to_radial_step'] = self.grid_to_radial_step self.kwargs['radial_cutoff'] = self.radial_cutoff # get indeces for everything in danger zone if danger_zone_distance is not None: # actually in danger zone # should these only those on the phys side or any? I think any... # idz = np.logical_and(r <= ddd, r >= -self.radial_width-ddd) if self.interior \ # else np.logical_and(r >= -ddd, r <= self.radial_width+ddd) idz = np.logical_and(r <= ddd, r >= -self.radial_width-ddd) if self.interior \ else np.logical_and(r >= -ddd, r <= self.radial_width+ddd) # reduce to the good ones iax_small = iax[idz] iay_small = iay[idz] r_small = r[idz] t_small = t[idz] self.grid_in_danger_zone_x = iax_small self.grid_in_danger_zone_y = iay_small # get guess indeces for this gi = (t_small // self.bdy.dt).astype(int) self.grid_in_danger_zone_gi = gi