def rotate_velocities(self, particle_type: int, tilt: str = 'z', boost: unyt.unyt_array = None) -> unyt.array: velocities = self.data.subfind_particles[f'PartType{particle_type}'][ 'Velocity'] if boost is not None: velocities[:, 0] -= boost[0] velocities[:, 1] -= boost[1] velocities[:, 2] -= boost[2] vx, vy, vz = velocities.value.T if tilt == 'y': new_vel = np.vstack((vx, vz, vy)).T elif tilt == 'z': new_vel = np.vstack((vx, -vy, vz)).T elif tilt == 'x': new_vel = np.vstack((-vz, -vy, vx)).T elif tilt == 'faceon': face_on_rotation_matrix = rotation_matrix_from_vector( self.angular_momentum_hot_gas.value) new_vel = np.einsum('ijk,ik->ij', face_on_rotation_matrix, velocities.value) elif tilt == 'edgeon': edge_on_rotation_matrix = rotation_matrix_from_vector( self.angular_momentum_hot_gas.value, axis='y') new_vel = np.einsum('ijk,ik->ij', edge_on_rotation_matrix, velocities.value) return new_vel * velocities.units
def rotate_coordinates(self, particle_type: int, tilt: str = 'z') -> unyt.array: cop = self.data.subfind_tab.FOF.GroupCentreOfPotential coord = self.data.subfind_particles[f'PartType{particle_type}'][ 'Coordinates'] coord[:, 0] -= cop[0] coord[:, 1] -= cop[1] coord[:, 2] -= cop[2] x, y, z = coord.value.T if tilt == 'y': new_coord = np.vstack((x, z, y)).T elif tilt == 'z': new_coord = np.vstack((x, y, z)).T elif tilt == 'x': new_coord = np.vstack((z, y, x)).T elif tilt == 'faceon': face_on_rotation_matrix = rotation_matrix_from_vector( self.angular_momentum_hot_gas.value) new_coord = np.einsum('ijk,ik->ij', face_on_rotation_matrix, coord.value) elif tilt == 'edgeon': edge_on_rotation_matrix = rotation_matrix_from_vector( self.angular_momentum_hot_gas.value, axis='y') new_coord = np.einsum('ijk,ik->ij', edge_on_rotation_matrix, coord.value) new_coord *= coord.units # new_coord[:, 0] += cop[0] # new_coord[:, 1] += cop[1] # new_coord[:, 2] += cop[2] return new_coord
def calculate_integrated_quantities(data, ang_momentum, radius, mode): face_on_rotation_matrix = rotation_matrix_from_vector(ang_momentum) x, y, _ = np.matmul(face_on_rotation_matrix, data[:, :3].T) r = np.sqrt(x**2 + y**2) select = r <= radius surface = np.pi * radius**2 if mode == 0: m = data[select, 9] if mode == 1: m = data[select, 9] + data[select, 8] # If we have gas within rhalfMs if len(m) > 0: Sigma_gas = np.log10(np.sum(m) / surface) - 6.0 # Msun / pc^2 sfr = data[select, 10] sfr = sfr[sfr > 0] Sigma_SFR = np.log10(np.sum(sfr) / surface) # Msun / yr / kpc^2 else: Sigma_gas = -6 Sigma_SFR = -6 return Sigma_gas, Sigma_SFR
def get_rotation( image_attributes: ImageAttributes, galaxy_attributes: GalaxyAttributes ): """ Gets the rotation matrix and center if required. """ if image_attributes.projection == "faceon": return ( rotation_matrix_from_vector(galaxy_attributes.normal_vector), galaxy_attributes.center, ) elif image_attributes.projection == "edgeon": return ( rotation_matrix_from_vector(galaxy_attributes.normal_vector, "y"), galaxy_attributes.center, ) else: return None, None
def rotate(coord: np.ndarray, angular_momentum_hot_gas: np.ndarray, tilt: str = 'x'): if tilt == 'x': rotation_matrix = rotation_matrix_from_vector( np.array([1, 0, 0], dtype=np.float)) elif tilt == 'y': rotation_matrix = rotation_matrix_from_vector( np.array([0, 1, 0], dtype=np.float)) elif tilt == 'z': rotation_matrix = rotation_matrix_from_vector( np.array([0, 0, 1], dtype=np.float)) elif tilt == 'faceon': rotation_matrix = rotation_matrix_from_vector(angular_momentum_hot_gas) elif tilt == 'edgeon': rotation_matrix = rotation_matrix_from_vector(angular_momentum_hot_gas, axis='y') new_coord = np.matmul(rotation_matrix, coord.T).T return new_coord
def rotate_coordinates(self, particle_type: int, tilt: str = 'z') -> unyt.array: cop = self.data.read_catalogue_subfindtab('/FOF/GroupCentreOfPotential') coord = self.data.read_snapshot(f'PartType{particle_type}/Coordinates') coord[:, 0] -= cop[0] coord[:, 1] -= cop[1] coord[:, 2] -= cop[2] x, y, z = coord.T if tilt == 'y': new_coord = np.vstack((x, z, y)).T elif tilt == 'z': new_coord = np.vstack((x, y, z)).T elif tilt == 'x': new_coord = np.vstack((z, y, x)).T elif tilt == 'faceon': face_on_rotation_matrix = rotation_matrix_from_vector(self.angular_momentum_hot_gas) new_coord = np.einsum('ijk,ik->ij', face_on_rotation_matrix, coord) elif tilt == 'edgeon': edge_on_rotation_matrix = rotation_matrix_from_vector(self.angular_momentum_hot_gas, axis='y') new_coord = np.einsum('ijk,ik->ij', edge_on_rotation_matrix, coord) return new_coord
def KS_relation(data, ang_momentum, mode, method, size): image_diameter = 60 extent = [-30, 30] # kpc number_of_pixels = int(image_diameter / size + 1) face_on_rotation_matrix = rotation_matrix_from_vector(ang_momentum) if method == "grid": # Calculate the surface density maps using grid of pixel size partsDATA = data.copy() map_mass = project_gas(partsDATA, mode, number_of_pixels, extent, face_on_rotation_matrix) map_metals = integrate_metallicity_using_grid(partsDATA, number_of_pixels, extent, face_on_rotation_matrix) star_formation_rate_mask = partsDATA[:, 10] > 0.0 partsDATA = partsDATA[star_formation_rate_mask, :] map_SFR = project_gas(partsDATA, 2, number_of_pixels, extent, face_on_rotation_matrix) else: partsDATA = data.copy() map_mass = project_gas_with_azimuthal_average(partsDATA, mode, face_on_rotation_matrix, size) map_metals = project_metals_with_azimuthal_average( partsDATA, face_on_rotation_matrix, size) star_formation_rate_mask = np.where(partsDATA[:, 10] > 0.0)[0] partsDATA = partsDATA[star_formation_rate_mask, :] if len(star_formation_rate_mask) > 0: map_SFR = project_gas_with_azimuthal_average( partsDATA, 2, face_on_rotation_matrix, size) else: map_SFR = np.zeros(len(map_mass)) # Bounds map_SFR[map_SFR <= 0] = 1e-6 map_mass[map_mass <= 0] = 1e-6 surface_density = np.log10(map_mass.flatten()) # Msun / kpc^2 surface_density -= 6 # Msun / pc^2 SFR_surface_density = np.log10(map_SFR.flatten()) # Msun / yr / kpc^2 tgas = surface_density - SFR_surface_density + 6.0 return surface_density, SFR_surface_density, tgas, map_metals
def rotate_velocities(self, particle_type: int, tilt: str = 'z', boost = None) -> unyt.array: velocities = self.data.read_snapshot(f'PartType{particle_type}/Velocity') if boost is not None: velocities[:, 0] -= boost[0] velocities[:, 1] -= boost[1] velocities[:, 2] -= boost[2] vx, vy, vz = velocities.T if tilt == 'y': new_vel = np.vstack((vx, vz, vy)).T elif tilt == 'z': new_vel = np.vstack((vx, -vy, vz)).T elif tilt == 'x': new_vel = np.vstack((-vz, -vy, vx)).T elif tilt == 'faceon': face_on_rotation_matrix = rotation_matrix_from_vector(self.angular_momentum_hot_gas) new_vel = np.einsum('ijk,ik->ij', face_on_rotation_matrix, velocities) elif tilt == 'edgeon': edge_on_rotation_matrix = rotation_matrix_from_vector(self.angular_momentum_hot_gas, axis='y') new_vel = np.einsum('ijk,ik->ij', edge_on_rotation_matrix, velocities) return new_vel
def test_slice(filename): """ Checks that a slice of a single particle snapshot is invariant under rotations around the particle Parameters ---------- filename: str name of file providing metadata to copy """ # Start from the beginning, open the file output_filename = "single_particle.hdf5" create_single_particle_dataset(filename, output_filename) data = load(output_filename) # Compute rotation matrix for rotating around particle centre = data.gas.coordinates[0] rotate_vec = [0.5, 0.5, 0.5] matrix = rotation_matrix_from_vector(rotate_vec, axis="z") boxsize = data.metadata.boxsize z_range = boxsize[2] slice_z = centre[2] / z_range unrotated = slice_gas(data, resolution=1024, slice=slice_z, project="masses", parallel=True) rotated = slice_gas( data, resolution=1024, slice=slice_z, project="masses", rotation_center=centre, rotation_matrix=matrix, parallel=True, ) # Check that we didn't miss the particle assert unrotated.any() assert rotated.any() assert array_equal(rotated, unrotated) remove(output_filename)
def surface_ratios(data, ang_momentum, method, size): face_on_rotation_matrix = rotation_matrix_from_vector(ang_momentum) if method == "grid": image_diameter = 60 extent = [-30, 30] # kpc number_of_pixels = int(image_diameter / size + 1) # Calculate the maps using grid map_H2 = project_gas(data, 0, number_of_pixels, extent, face_on_rotation_matrix) map_HI = project_gas(data, 3, number_of_pixels, extent, face_on_rotation_matrix) else: # Calculate the maps using azimuthally-average shells map_H2 = project_gas_with_azimuthal_average(data, 0, face_on_rotation_matrix, size) map_HI = project_gas_with_azimuthal_average(data, 3, face_on_rotation_matrix, size) map_gas = map_H2 + map_HI # Bounds map_H2[map_H2 <= 0] = 1e-6 map_gas[map_gas <= 0] = 1e-6 H2_to_neutral_ratio = map_H2 / map_gas neutral_gas_surface_density = np.log10( map_gas.flatten()) # HI+H2 Msun / kpc^2 molecular_gas_surface_density = np.log10( map_H2.flatten()) # H2 Msun / kpc^2 neutral_gas_surface_density -= 6 # HI+H2 Msun / pc^2 molecular_gas_surface_density -= 6 # H2 Msun / pc^2 H2_to_neutral_ratio_density = np.log10( H2_to_neutral_ratio.flatten()) # no units return ( neutral_gas_surface_density, molecular_gas_surface_density, H2_to_neutral_ratio_density, )
def test_render(filename): """ Checks that a volume render of a single particle snapshot is invariant under rotations around the particle Parameters ---------- filename: str name of file providing metadata to copy """ # Start from the beginning, open the file output_filename = "single_particle.hdf5" create_single_particle_dataset(filename, output_filename) data = load(output_filename) # Compute rotation matrix for rotating around particle centre = data.gas.coordinates[0] rotate_vec = [0.5, 0.5, 0.5] matrix = rotation_matrix_from_vector(rotate_vec, axis="z") boxsize = data.metadata.boxsize unrotated = render_gas(data, resolution=256, project="masses", parallel=True) rotated = render_gas( data, resolution=256, project="masses", rotation_center=centre, rotation_matrix=matrix, parallel=True, ) assert array_equal(rotated, unrotated) remove(output_filename)
def rotation_align_with_vector(coordinates: np.ndarray, rotation_center: np.ndarray, vector: np.ndarray) -> np.ndarray: # Normalise vector for more reliable handling vector /= np.linalg.norm(vector) # Get the de-rotation matrix: # axis='z' is the default and corresponds to face-on (looking down z-axis) # axis='y' corresponds to edge-on (maximum rotational signal) rotation_matrix = rotation_matrix_from_vector(vector, axis='y') if rotation_center is not None: # Rotate co-ordinates as required x, y, z = np.matmul(rotation_matrix, (coordinates - rotation_center).T) x += rotation_center[0] y += rotation_center[1] z += rotation_center[2] else: x, y, z = coordinates.T return np.vstack((x, y, z)).T
def plot_galaxy_parts(partsDATA, parttype, ang_momentum, halo_data, index, output_path, simulation_name): # Plot ranges r_limit = 5 * halo_data.half_mass_radius_star[index] r_img = 30.0 if r_limit < r_img: r_img = r_limit xmin = -r_img ymin = -r_img xmax = r_img ymax = r_img pos_parts = partsDATA[:, 0:3].copy() face_on_rotation_matrix = rotation_matrix_from_vector(ang_momentum, axis="z") edge_on_rotation_matrix = rotation_matrix_from_vector(ang_momentum, axis="y") pos_face_on = np.matmul(face_on_rotation_matrix, pos_parts.T) pos_face_on = pos_face_on.T pos_edge_on = np.matmul(edge_on_rotation_matrix, pos_parts.T) pos_edge_on = pos_edge_on.T if parttype == 0: density = np.log10(partsDATA[:, 11]) if parttype == 4: density = np.log10(partsDATA[:, 8]) # Sort particles for better viewing arg_sort = np.argsort(density) density = density[arg_sort] pos_face_on = pos_face_on[arg_sort, :] pos_edge_on = pos_edge_on[arg_sort, :] denmin = np.min(density) denmax = np.max(density) rcParams.update(params) fig = plt.figure() ax = plt.subplot(1, 2, 1) if parttype == 4: title = "Stellar component" if parttype == 0: title = "Gas component" ax.set_title(title) ax.tick_params(labelleft=True, labelbottom=True, length=0) plt.xlabel("x [kpc]") plt.ylabel("y [kpc]") ax.set_xlim(xmin, xmax) ax.set_ylim(ymin, ymax) plt.scatter( pos_face_on[:, 0], pos_face_on[:, 1], c=density, alpha=1, s=10, vmin=denmin, vmax=denmax, cmap="magma", edgecolors="none", ) ax.autoscale(False) ax = plt.subplot(1, 2, 2) if parttype == 4: kappa = halo_data.kappa_co[index] mass = halo_data.log10_stellar_mass[index] ac = halo_data.axis_ca[index] cb = halo_data.axis_cb[index] ba = halo_data.axis_ba[index] radius = halo_data.half_mass_radius_star[index] title = r" $\kappa_{\mathrm{co}} = $%0.2f" % (kappa) title += " - $\log_{10}$ $M_{*}/M_{\odot} = $%0.2f" % (mass) title += " \n c/a = %0.2f," % (ac) title += " c/b = %0.2f," % (cb) title += " b/a = %0.2f" % (ba) title += "\n Stellar half mass radius %0.2f kpc" % (radius) if parttype == 0: kappa = halo_data.gas_kappa_co[index] mass = halo_data.log10_gas_mass[index] ac = halo_data.gas_axis_ca[index] cb = halo_data.gas_axis_cb[index] ba = halo_data.gas_axis_ba[index] radius = halo_data.half_mass_radius_star[index] title = r" $\kappa_{\mathrm{co}} = $%0.2f" % (kappa) title += " - $\log_{10}$ $M_{gas}/M_{\odot} = $%0.2f" % (mass) title += " \n c/a = %0.2f," % (ac) title += " c/b = %0.2f," % (cb) title += " b/a = %0.2f" % (ba) title += "\n Stellar half mass radius %0.2f kpc" % (radius) ax.set_title(title) ax.tick_params(labelleft=True, labelbottom=True, length=0) plt.xlabel("x [kpc]") plt.ylabel("z [kpc]") ax.set_xlim(xmin, xmax) ax.set_ylim(ymin, ymax) plt.scatter( pos_edge_on[:, 0], pos_edge_on[:, 1], c=density, alpha=1, s=10, vmin=denmin, vmax=denmax, cmap="magma", edgecolors="none", ) ax.autoscale(False) cbar_ax = fig.add_axes([0.86, 0.22, 0.018, 0.5]) cbar_ax.tick_params(labelsize=15) cb = plt.colorbar(ticks=[4, 6, 8, 10], cax=cbar_ax) cb.set_label(label=r"$\log_{10}$ $\rho$ [M$_{\odot}$/kpc$^{3}$]", labelpad=0.5) if parttype == 4: outfile = (f"{output_path}/galaxy_sparts_%i_" % (index) + simulation_name + ".png") if parttype == 0: outfile = f"{output_path}/galaxy_parts_%i_" % ( index) + simulation_name + ".png" fig.savefig(outfile, dpi=150) plt.close("all")
def plot_galaxy(parts_data, parttype, ang_momentum, halo_data, index, output_path, simulation_name): # partsDATA contains particles data and is structured as follow # [ (:3)Position[Mpc]: (0)X | (1)Y | (2)Z ] if parttype == 4: cmap = plt.cm.magma if parttype == 0: cmap = plt.cm.viridis pos_parts = parts_data[:, 0:3].copy() face_on_rotation_matrix = rotation_matrix_from_vector(ang_momentum, axis="z") edge_on_rotation_matrix = rotation_matrix_from_vector(ang_momentum, axis="y") pos_face_on = np.matmul(face_on_rotation_matrix, pos_parts.T) pos_face_on = pos_face_on.T pos_edge_on = np.matmul(edge_on_rotation_matrix, pos_parts.T) pos_edge_on = pos_edge_on.T hsml_parts = parts_data[:, 7] mass = parts_data[:, 3] r_limit = 5 * halo_data.half_mass_radius_star[index] r_img = 30.0 if r_limit < r_img: r_img = r_limit xmin = -r_img ymin = -r_img xmax = r_img ymax = r_img rcParams.update(params) fig = plt.figure() ax = plt.subplot(1, 2, 1) if parttype == 4: title = "Stellar component" if parttype == 0: title = "HI+H2 gas" ax.set_title(title) ###### plot one side ######################## qv = QuickView( pos_face_on, mass=mass, hsml=hsml_parts, logscale=True, plot=False, r="infinity", p=0, t=0, extent=[xmin, xmax, ymin, ymax], x=0, y=0, z=0, ) img = qv.get_image() ext = qv.get_extent() ax.tick_params(labelleft=True, labelbottom=True, length=0) plt.xlabel("x [kpc]") plt.ylabel("y [kpc]") ax.set_xlim(xmin, xmax) ax.set_ylim(ymin, ymax) img = get_normalized_image(img) ax.imshow(img, cmap=cmap, extent=ext) ax.autoscale(False) ###### plot another side ######################## ax = plt.subplot(1, 2, 2) if parttype == 4: kappa = halo_data.kappa_co[index] mass_galaxy = halo_data.log10_stellar_mass[index] ac = halo_data.axis_ca[index] cb = halo_data.axis_cb[index] ba = halo_data.axis_ba[index] title = r" $\kappa_{\mathrm{co}} = $%0.2f" % (kappa) title += " - $\log_{10}$ $M_{*}/M_{\odot} = $%0.2f" % (mass_galaxy) title += " \n c/a = %0.2f," % (ac) title += " c/b = %0.2f," % (cb) title += " b/a = %0.2f" % (ba) if parttype == 0: kappa = halo_data.gas_kappa_co[index] mass_galaxy = halo_data.log10_gas_mass[index] ac = halo_data.gas_axis_ca[index] cb = halo_data.gas_axis_cb[index] ba = halo_data.gas_axis_ba[index] title = r" $\kappa_{\mathrm{co}} = $%0.2f" % (kappa) title += " - $\log_{10}$ $M_{gas}/M_{\odot} = $%0.2f" % (mass_galaxy) title += " \n c/a = %0.2f," % (ac) title += " c/b = %0.2f," % (cb) title += " b/a = %0.2f" % (ba) ax.set_title(title) qv = QuickView( pos_edge_on, mass=mass, hsml=hsml_parts, logscale=True, plot=False, r="infinity", p=0, t=0, extent=[xmin, xmax, ymin, ymax], x=0, y=0, z=0, ) img = qv.get_image() ext = qv.get_extent() ax.tick_params(labelleft=True, labelbottom=True, length=0) plt.xlabel("x [kpc]") plt.ylabel("z [kpc]") ax.set_xlim(xmin, xmax) ax.set_ylim(ymin, ymax) img = get_normalized_image(img) ims = ax.imshow(img, cmap=cmap, extent=ext) ax.autoscale(False) cbar_ax = fig.add_axes([0.86, 0.22, 0.018, 0.5]) cbar_ax.tick_params(labelsize=15) cb = plt.colorbar(ims, ticks=[4, 6, 8, 10, 12], cax=cbar_ax) cb.set_label(label=r"$\log_{10}$ $\Sigma$ [M$_{\odot}$/kpc$^{2}$]", labelpad=0.5) if parttype == 0: outfile = f"{output_path}/galaxy_gas_%i_" % ( index) + simulation_name + ".png" if parttype == 4: outfile = f"{output_path}/galaxy_stars_%i_" % ( index) + simulation_name + ".png" fig.savefig(outfile, dpi=150) plt.close("all") return
def create_scatter( snapshot: SWIFTDataset, halo: Halo, image: Image, projection: Projection, resolution: int, ) -> unyt_array: """ Creates a projected image for a given image class, and snapshot. Parameters ---------- snapshot: SWIFTDataset, The opened dataset. halo: Halo Halo with properties to visualise image: Image Image class to visualise this time around projection: Projection Which projection to make resolution: int Image size along each axis. Returns ------- grid: unyt.unyt_array Output grid, in the requested units. """ region_given_r = lambda r: [ halo.position[0] - r, halo.position[0] + r, halo.position[1] - r, halo.position[1] + r, halo.position[2] - r, halo.position[2] + r, ] radius = image.get_radius(stellar_half_mass=halo.radius_100_kpc_star, r_200_crit=halo.radius_200_crit) particle_data = getattr(snapshot, image.particle_type, "gas") region = region_given_r(radius) rotation_center = None rotation_matrix = None # If the L vector is poorly constrained this will complain, # but we don't really care. with np.testing.suppress_warnings() as sup: sup.filter(RuntimeWarning) if projection == Projection.EDGE_ON: rotation_center = halo.position.to(particle_data.coordinates.units) rotation_matrix = rotation_matrix_from_vector(halo.L.v, "y") elif projection == Projection.FACE_ON: rotation_center = halo.position.to(particle_data.coordinates.units) rotation_matrix = rotation_matrix_from_vector(halo.L.v, "z") if hasattr(particle_data, "smoothing_lengths"): backend = "fast" else: backend = "histogram" common_attributes = dict( data=particle_data, boxsize=snapshot.metadata.boxsize, resolution=resolution, region=region, mask=None, rotation_matrix=rotation_matrix, rotation_center=rotation_center, parallel=False, backend=backend, ) mass_image = project_pixel_grid(project="masses", **common_attributes) if image.visualise == "projected_densities": # We're done! x_range = region[1] - region[0] y_range = region[3] - region[2] units = 1.0 / (x_range * y_range) units.convert_to_units(1.0 / (x_range.units * y_range.units)) units *= particle_data.masses.units grid = unyt_array(mass_image, units=units) else: # Need to make the complementary image. cache_name = f"_CACHE_MASSWEIGHTED_{image.visualise}" cache = getattr(particle_data, cache_name, None) particle_array = getattr(particle_data, image.visualise) if cache is None: cache = particle_array * particle_data.masses setattr( particle_data, cache_name, cache, ) weighted_image = project_pixel_grid(project=cache_name, **common_attributes) # Deal with zeroes: mass_image[mass_image == 0.0] = 1.0 # K * 1e10 Msun -> K * Msun for the 'units' internally. So we need # to reconstruct the true ratio, although this should be ideally the # same as particle_array.units. units = cache.units / particle_data.masses.units grid = unyt_array(weighted_image / mass_image, units=units) # Fill if required output_units = None unit_steal = [image.output_units, image.vmin, image.vmax, grid] while output_units is None: potential_unit = unit_steal.pop(0) if potential_unit is not None: output_units = potential_unit.units grid.convert_to_units(output_units) if image.fill_below is not None: mask = grid < image.fill_below.to(output_units) grid[mask] = image.fill_below.to(output_units) return grid
def render_luminosity_map( parts_data, luminosity, filtname, ang_momentum, halo_data, index, output_path, simulation_name, ): pos_parts = parts_data[:, 0:3].copy() face_on_rotation_matrix = rotation_matrix_from_vector(ang_momentum) edge_on_rotation_matrix = rotation_matrix_from_vector(ang_momentum, axis="y") pos_face_on = np.matmul(face_on_rotation_matrix, pos_parts.T) pos_face_on = pos_face_on.T pos_edge_on = np.matmul(edge_on_rotation_matrix, pos_parts.T) pos_edge_on = pos_edge_on.T hsml_parts = parts_data[:, 7] r_limit = 5 * halo_data.half_mass_radius_star[index] r_img = 30.0 if r_limit < r_img: r_img = r_limit xmin = -r_img ymin = -r_img xmax = r_img ymax = r_img rcParams.update(params) fig = plt.figure() ax = plt.subplot(1, 2, 1) ###### plot one side ######################## qv = QuickView( pos_face_on, mass=luminosity, hsml=hsml_parts, logscale=True, plot=False, r="infinity", p=0, t=0, extent=[xmin, xmax, ymin, ymax], x=0, y=0, z=0, ) img = qv.get_image() ext = qv.get_extent() ax.tick_params(labelleft=True, labelbottom=True, length=0) plt.xlabel("x [kpc]") plt.ylabel("y [kpc]") ax.set_xlim(xmin, xmax) ax.set_ylim(ymin, ymax) img = get_normalized_image(img, vmin=img.max() - 2.3) ax.imshow(img, cmap="magma", extent=ext, vmin=img.max() - 2.3) ax.autoscale(False) ###### plot another side ######################## ax = plt.subplot(1, 2, 2) qv = QuickView( pos_edge_on, mass=luminosity, hsml=hsml_parts, logscale=True, plot=False, r="infinity", p=90, t=0, extent=[xmin, xmax, ymin, ymax], x=0, y=0, z=0, ) img = qv.get_image() ext = qv.get_extent() ax.tick_params(labelleft=True, labelbottom=True, length=0) plt.xlabel("x [kpc]") plt.ylabel("z [kpc]") ax.set_xlim(xmin, xmax) ax.set_ylim(ymin, ymax) img = get_normalized_image(img, vmin=img.max() - 2.3) ims = ax.imshow(img, cmap="magma", vmin=img.max() - 2.3, extent=ext) ax.autoscale(False) cbar_ax = fig.add_axes([0.86, 0.22, 0.018, 0.5]) cbar_ax.tick_params(labelsize=15) cb = plt.colorbar(ims, ticks=[4, 6, 8, 10, 12], cax=cbar_ax) cb.set_label(label=r"$\log_{10}$ $\Sigma$ [Jy/kpc$^{2}$]", labelpad=0.5) outfile = (f"{output_path}/galaxy_%s_map_%i_" % (filtname, index) + simulation_name + ".png") fig.savefig(outfile, dpi=150) plt.close("all") return
def process_single_halo( self, zoom_obj: Zoom = None, path_to_snap: str = None, path_to_catalogue: str = None, mask_radius: Tuple[float, str] = (6, 'r500'), map_centre: Union[str, list, np.ndarray] = 'vr_centre_of_potential', temperature_range: Optional[tuple] = None, depth_offset: Optional[float] = None, return_type: Union[type, str] = 'class', inscribe_mask: bool = False, ): sw_data, vr_data = self.get_handles_from_zoom( zoom_obj, path_to_snap, path_to_catalogue, mask_radius_r500=15, ) map_centres_allowed = [ 'vr_centre_of_potential' ] if type(map_centre) is str and map_centre.lower() not in map_centres_allowed: raise AttributeError(( f"String-commands for `map_centre` only support " f"`vr_centre_of_potential`. Got {map_centre} instead." )) elif (type(map_centre) is list or type(map_centre) is np.ndarray) and len(map_centre) != 3: raise AttributeError(( f"List-commands for `map_centre` only support " f"length-3 lists. Got {map_centre} " f"(length {len(map_centre)}) instead." )) self.map_centre = map_centre centre_of_potential = [ vr_data.positions.xcminpot[0].to('Mpc') / vr_data.a, vr_data.positions.ycminpot[0].to('Mpc') / vr_data.a, vr_data.positions.zcminpot[0].to('Mpc') / vr_data.a ] if self.map_centre == 'vr_centre_of_potential': _xCen = centre_of_potential[0] _yCen = centre_of_potential[1] _zCen = centre_of_potential[2] elif type(self.map_centre) is list or type(self.map_centre) is np.ndarray: _xCen = self.map_centre[0] * Mpc / vr_data.a _yCen = self.map_centre[1] * Mpc / vr_data.a _zCen = self.map_centre[2] * Mpc / vr_data.a if xlargs.debug: print(f"Centre of potential: {[float(f'{i.v:.3f}') for i in centre_of_potential]} Mpc") print(f"Map centre: {[float(f'{i.v:.3f}') for i in [_xCen, _yCen, _zCen]]} Mpc") self.depth = _zCen / sw_data.metadata.boxsize[0] if depth_offset is not None: self.depth += depth_offset * Mpc / sw_data.metadata.boxsize[0] if xlargs.debug: percent = f"{depth_offset * Mpc / _zCen * 100:.1f}" print(( f"Imposing offset in slicing depth: {depth_offset:.2f} Mpc.\n" f"Percentage shift compared to centre: {percent} %" )) _r500 = vr_data.spherical_overdensities.r_500_rhocrit[0].to('Mpc') / vr_data.a if mask_radius[1] == 'r500': mask_radius_r500 = mask_radius[0] * _r500 else: mask_radius_r500 = unyt_quantity(mask_radius[0], units=mask_radius[1]) if inscribe_mask: mask_radius_r500 /= np.sqrt(3) region = [ _xCen - mask_radius_r500, _xCen + mask_radius_r500, _yCen - mask_radius_r500, _yCen + mask_radius_r500 ] if temperature_range is not None: temp_filter = np.where( (sw_data.gas.temperatures > temperature_range[0]) & (sw_data.gas.temperatures < temperature_range[1]) )[0] if xlargs.debug: percent = f"{len(temp_filter) / len(sw_data.gas.temperatures) * 100:.1f}" print(( f"Filtering particles by temperature: {temperature_range} K.\n" f"Total particles: {len(sw_data.gas.temperatures)}\n" f"Particles within bounds: {len(temp_filter)} = {percent} %" )) sw_data.gas.coordinates = sw_data.gas.coordinates[temp_filter] sw_data.gas.smoothing_lengths = sw_data.gas.smoothing_lengths[temp_filter] sw_data.gas.masses = sw_data.gas.masses[temp_filter] sw_data.gas.densities = sw_data.gas.densities[temp_filter] sw_data.gas.temperatures = sw_data.gas.temperatures[temp_filter] # Rotate about CoP if required center = [_xCen, _yCen, _zCen] rotate_vec = [0, 0, 1] matrix = rotation_matrix_from_vector(rotate_vec, axis='z') common_kwargs = dict( rotation_matrix=matrix, rotation_center=center, data=sw_data, resolution=self.resolution, parallel=self.parallel, region=region, slice=self.depth ) if self._project_quantity == 'entropies': number_density = (sw_data.gas.densities / mh).to('cm**-3') / mean_molecular_weight entropy = kb * sw_data.gas.temperatures / number_density ** (2 / 3) sw_data.gas.entropies_physical = entropy.to('keV*cm**2') gas_map = slice_gas(project='entropies_physical', **common_kwargs).to('keV*cm**2/Mpc**3') elif self._project_quantity == 'temperatures': sw_data.gas.mwtemps = sw_data.gas.masses * sw_data.gas.temperatures mass_weighted_temp_map = slice_gas(project='mwtemps', **common_kwargs) mass_map = slice_gas(project='masses', **common_kwargs) with np.errstate(divide='ignore', invalid='ignore'): gas_map = mass_weighted_temp_map / mass_map gas_map = gas_map.to('K') else: gas_map = slice_gas(project=self._project_quantity, **common_kwargs) units = gas_map.units gas_map = gas_map.value gas_map = np.ma.array( gas_map, mask=(gas_map <= 0.), fill_value=np.nan, copy=True, dtype=np.float64 ) output_values = [ gas_map, region, units, [_xCen, _yCen, _zCen], _r500, sw_data.metadata.z ] output_names = [ 'map', 'region', 'units', 'centre', 'r500', 'z' ] if return_type is tuple: output = tuple(output_values) elif return_type is dict: output = dict(zip(output_names, output_values)) elif return_type == 'class': OutputClass = namedtuple('OutputClass', output_names) output = OutputClass(*output_values) else: raise TypeError(f"Return type {return_type} not recognised.") return output