Last active
February 19, 2021 17:28
-
-
Save rieder/abe1773776a9bb08503e06723d48ca66 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# -*- coding: utf-8 -*- | |
""" | |
Plot hydro and/or stars | |
""" | |
import os | |
import logging | |
import numpy | |
import copy | |
import argparse | |
import matplotlib | |
from matplotlib import pyplot | |
# import matplotlib.cm as cm | |
from amuse.datamodel import Particles | |
from amuse.io import read_set_from_file | |
from amuse.units import units, nbody_system | |
try: | |
from amuse.ext.mapper import MapHydro | |
except ImportError: | |
from mapper import MapHydro | |
logger = logging.getLogger(__name__) | |
def velocity_divergence(vx_field, vy_field, velocity_unit=units.kms): | |
# Return divergence of velocity fields | |
div = numpy.ufunc.reduce( | |
numpy.add, | |
[ | |
-numpy.gradient(vx_field.value_in(velocity_unit), axis=1), | |
-numpy.gradient(vy_field.value_in(velocity_unit), axis=0), | |
] | |
) | velocity_unit | |
return div | |
def plot_velocity( | |
ax, | |
maps, | |
x_axis="vx", | |
y_axis="vy", | |
velocity_unit=units.kms, | |
color='black', | |
normalise_by_counts=True, | |
style="streamplot", # or "quiver" | |
): | |
if normalise_by_counts: | |
vxmap = getattr(maps, x_axis) / maps.counts | |
vymap = getattr(maps, y_axis) / maps.counts | |
# vzmap = getattr(maps, z_axis) / maps.counts | |
x_range = numpy.arange( | |
maps.xmin, maps.xmax, maps.width/maps.bins[0] | |
) | |
y_range = numpy.arange( | |
maps.ymin, maps.ymax, maps.height/maps.bins[1] | |
) | |
vx_values = vxmap.value_in(velocity_unit) | |
vy_values = vymap.value_in(velocity_unit) | |
x_grid, y_grid = numpy.meshgrid(x_range, y_range) | |
if style == "streamplot": | |
return ax.streamplot( | |
x_grid, y_grid, | |
vx_values, vy_values, | |
color=color, | |
) | |
return ax.quiver( | |
x_grid, y_grid, | |
vx_values, vy_values, | |
color=color, | |
) | |
def plot_divergence( | |
ax, | |
maps, | |
x_axis="vx", | |
y_axis="vy", | |
contours=False, | |
normalise_by_counts=True, | |
vmin=None, | |
vmax=None, | |
): | |
x_range = numpy.arange( | |
maps.xmin.value_in(units.pc), maps.xmax.value_in(units.pc), | |
(maps.width / maps.bins[0]).value_in(units.pc), | |
) | |
y_range = numpy.arange( | |
maps.ymin.value_in(units.pc), maps.ymax.value_in(units.pc), | |
(maps.height / maps.bins[1]).value_in(units.pc), | |
) | |
x_grid, y_grid = numpy.meshgrid(x_range, y_range) | |
div = velocity_divergence( | |
getattr(maps, x_axis) / maps.counts, | |
getattr(maps, y_axis) / maps.counts, | |
) | |
if not normalise_by_counts: | |
div = div * maps.counts | |
if contours: | |
return ax.contour( | |
x_grid, y_grid, | |
div.value_in(units.kms), | |
levels=(-12, -4, 4, 12), | |
# colors=["blue", "red"], | |
# cmap="coolwarm", | |
cmap='bwr', | |
) | |
minmax = max(-div.min(), div.max()).value_in(units.kms) | |
if not vmin: | |
vmin = -minmax | |
if not vmax: | |
vmax = minmax | |
# minmax = 500 | |
# print(minmax) | |
return ax.imshow( | |
div.value_in(units.kms), | |
extent=maps.extent, | |
vmin=vmin, | |
vmax=vmax, | |
cmap='bwr', | |
origin='lower', | |
) | |
def plot_column_density( | |
ax, | |
maps, | |
unit_col_density=units.MSun * units.pc**-2, | |
vmin=-1, | |
vmax=4, | |
cmap='viridis', | |
): | |
cmap = copy.copy(matplotlib.cm.get_cmap(cmap)) | |
cmap.set_bad('k', alpha=1.0) | |
column_density = maps.column_density | |
logscale = numpy.log10(column_density.value_in(unit_col_density)) | |
return ax.imshow( | |
logscale, | |
extent=maps.extent, | |
vmin=vmin, | |
vmax=vmax, | |
origin='lower', | |
cmap=cmap, | |
) | |
def plot_temperature( | |
ax, | |
maps, | |
vmin=0, | |
vmax=5, | |
cmap="inferno", | |
): | |
cmap = copy.copy(matplotlib.cm.get_cmap(cmap)) | |
cmap.set_bad('k', alpha=1.0) | |
temperature_map = maps.temperature | |
logscale_temperature_map = numpy.log10( | |
temperature_map.value_in(units.K) | |
) | |
return ax.imshow( | |
logscale_temperature_map, | |
extent=maps.extent, | |
vmin=vmin, | |
vmax=vmax, | |
cmap=cmap, | |
origin="lower", | |
) | |
def plot_hydro_and_stars( | |
time, | |
maps, | |
title="", | |
plot="column density", | |
length_unit=units.parsec, | |
use_fresco=False, | |
image_size_scale=1, | |
starscale=1, | |
dpi=150, | |
vmin=None, | |
vmax=None, | |
sinks_color='red', | |
stars_color='white', | |
plot_velocities=False, | |
): | |
"Plot gas and stars" | |
left = 0.2 | |
bottom = 0.1 | |
right = 1 | |
top = 0.9 | |
# fig = pyplot.figure(figsize=(6, 5)) | |
image_size = image_size_scale * maps.bins | |
figwidth = image_size[0] / dpi / (right - left) | |
figheight = image_size[1] / dpi / (top - bottom) | |
figsize = (figwidth, figheight) | |
fig = pyplot.figure(figsize=figsize, dpi=dpi) | |
wspace = 0.5 | |
fig.subplots_adjust( | |
left=left, right=right, top=top, bottom=bottom, wspace=wspace) | |
ax = fig.add_subplot(1, 1, 1) | |
ax.set_title(plot) | |
if plot == "column density": | |
plot_column_density(ax, maps) | |
# if plot == "temperature": | |
# plot_temperature(ax, maps) | |
if plot == "divergence": | |
plot_divergence( | |
ax, | |
maps, | |
normalise_by_counts=True, | |
vmin=vmin, | |
vmax=vmax, | |
) | |
if plot == "mass flux": | |
plot_divergence( | |
ax, | |
maps, | |
normalise_by_counts=False, | |
vmin=vmin, | |
vmax=vmax, | |
) | |
if plot in ["stars", "stars colour"]: | |
ax.set_facecolor('black') | |
cmap = copy.copy(matplotlib.cm.get_cmap("coolwarm")) | |
if plot == "stars colour": | |
stars_color = maps.stars.mass.value_in(units.MSun) | |
if maps.sinks is not None: | |
if not maps.sinks.is_empty(): | |
# Scale sinks the same way as stars | |
s = starscale * 0.1 * ( | |
(maps.sinks.mass / (7 | units.MSun))**(3.5 / 2) | |
) | |
x = getattr(maps.sinks, maps.axes[0]).value_in(length_unit) | |
y = getattr(maps.sinks, maps.axes[1]).value_in(length_unit) | |
c = ( | |
"black" if plot in [ | |
"temperature", "divergence", "mass flux", | |
] | |
else sinks_color | |
) | |
ax.scatter(x, y, s=s, c=c, cmap=cmap, lw=0) | |
if maps.stars is not None: | |
if not maps.stars.is_empty(): | |
s = starscale * 0.1 * ( | |
(maps.stars.mass / (7 | units.MSun))**(3.5 / 2) | |
) | |
x = getattr(maps.stars, maps.axes[0]).value_in(length_unit) | |
y = getattr(maps.stars, maps.axes[1]).value_in(length_unit) | |
c = ( | |
"black" if plot in [ | |
"temperature", "divergence", "mass flux", | |
] | |
else stars_color | |
) | |
if plot == "stars colour": | |
s = starscale * maps.stars.mass.value_in(units.MSun) | |
if not use_fresco: | |
use_fresco = 0 | |
ax.scatter(x, y, s=s, c=c, cmap=cmap, lw=0, alpha=1-use_fresco) | |
if maps.stars is not None and use_fresco: | |
from amuse.ext.fresco.fresco import make_image | |
converter = nbody_system.nbody_to_si( | |
maps.stars.total_mass(), | |
maps.width, | |
) | |
stars = maps.stars.copy() | |
stars.x -= maps.offset[0] | |
stars.y -= maps.offset[1] | |
stars.z -= maps.offset[2] | |
gas = maps.gas.copy() | |
if gas is not None: | |
gas.x -= maps.offset[0] | |
gas.y -= maps.offset[1] | |
gas.z -= maps.offset[2] | |
fresco_image, vmax = make_image( | |
stars=stars, | |
gas=gas, | |
converter=converter, | |
image_width=maps.width, | |
image_size=maps.bins, | |
percentile=0.9995, | |
calc_temperature=True, | |
age=0 | units.Myr, | |
vmax=None, | |
sourcebands='ubvri', | |
zoom_factor=maps.bins[0]/2048, | |
psf_type='hubble', | |
psf_sigma=1.0, | |
return_vmax=True, | |
extinction=True, | |
) | |
ax.imshow( | |
fresco_image, | |
extent=maps.extent, | |
alpha=use_fresco, | |
origin='lower', | |
) | |
if plot_velocities: | |
plot_velocity( | |
ax, maps, | |
velocity_unit=units.kms, | |
color=( | |
"black" if plot in ["temperature", "divergence"] | |
else stars_color | |
), | |
# smoothing_length=100 | units.pc, | |
normalise_by_counts=True, | |
) | |
ax.set_xlim(maps.extent[0], maps.extent[1]) | |
ax.set_ylim(maps.extent[2], maps.extent[3]) | |
ax.set_xlabel("%s [%s]" % (maps.axes[0], length_unit)) | |
ax.set_ylabel("%s [%s]" % (maps.axes[1], length_unit)) | |
# pyplot.title(title) | |
fig.suptitle(title) | |
return fig, ax | |
def new_argument_parser(): | |
"Parse command line arguments" | |
parser = argparse.ArgumentParser() | |
parser.add_argument( | |
'-s', | |
dest='starsfilename', | |
default='', | |
help='file containing stars (optional) []', | |
) | |
parser.add_argument( | |
'-i', | |
dest='sinksfilename', | |
default='', | |
help='file containing sinks (optional) []', | |
) | |
parser.add_argument( | |
'-g', | |
dest='gasfilename', | |
default='', | |
help='file containing gas (optional) []', | |
) | |
parser.add_argument( | |
'-o', | |
dest='imagefilename', | |
default='test', | |
help='write image to this file [test]', | |
) | |
parser.add_argument( | |
'-n', | |
dest='bins', | |
default=800, | |
type=int, | |
help='number of bins (800)', | |
) | |
parser.add_argument( | |
'-x', | |
dest='x', | |
default=0, | |
type=float, | |
help='Central X coordinate (0 [pc])', | |
) | |
parser.add_argument( | |
'-y', | |
dest='y', | |
default=0, | |
type=float, | |
help='Central Y coordinate (0 [pc])', | |
) | |
parser.add_argument( | |
'-z', | |
dest='z', | |
default=0, | |
type=float, | |
help='Central Z coordinate (0 [pc])', | |
) | |
parser.add_argument( | |
'-w', | |
dest='w', | |
default=10, | |
type=float, | |
help='Width in pc (10)', | |
) | |
parser.add_argument( | |
'-d', | |
dest='thickness', | |
default=0, | |
type=float, | |
help='Thickness in pc (0)', | |
) | |
parser.add_argument( | |
'--com', | |
dest='use_com', | |
action='store_true', | |
default=False, | |
help='Center on center of mass [False]', | |
) | |
parser.add_argument( | |
'--fresco', | |
dest='fresco_fraction', | |
default=0, | |
type=float, | |
help='Fresco mix-in fraction (0-1)', | |
) | |
parser.add_argument( | |
'-X', | |
dest='x_axis', | |
default='x', | |
help='Horizontal axis ["x"]', | |
) | |
parser.add_argument( | |
'-Y', | |
dest='y_axis', | |
default='y', | |
help='Vertical axis ["y"]', | |
) | |
parser.add_argument( | |
'-Z', | |
dest='z_axis', | |
default='z', | |
help='Line-of-sight axis ["z"]', | |
) | |
parser.add_argument( | |
'--starscale', | |
dest='starscale', | |
type=float, | |
default=1, | |
help='Scale of stars and sinks (1)', | |
) | |
return parser.parse_args() | |
def main(): | |
o = new_argument_parser() | |
gasfilename = o.gasfilename | |
starsfilename = o.starsfilename | |
sinksfilename = o.sinksfilename | |
imagefilename = o.imagefilename | |
bins = o.bins | |
offset_x = o.x | units.pc | |
offset_y = o.y | units.pc | |
offset_z = o.z | units.pc | |
width = o.w | units.pc | |
x_axis = o.x_axis | |
y_axis = o.y_axis | |
z_axis = o.z_axis | |
starscale = o.starscale | |
dpi = 150 | |
fresco_fraction = 0 # fresco mix-in rate; 0 = not at all, 1 = only fresco | |
plots = [] | |
if os.path.isfile(starsfilename): | |
stars = read_set_from_file( | |
starsfilename, | |
"amuse", | |
) if starsfilename != "" else Particles() | |
else: | |
stars = Particles() | |
if os.path.isfile(sinksfilename): | |
sinks = read_set_from_file( | |
sinksfilename, | |
"amuse", | |
) if sinksfilename != "" else Particles() | |
else: | |
sinks = Particles() | |
if gasfilename: | |
gas = read_set_from_file( | |
gasfilename, | |
"amuse", | |
) | |
if hasattr(gas, "itype"): | |
gas = gas[gas.itype == 1] | |
# gas.h_smooth = gas.h | |
if hasattr(gas, "u"): | |
plots.append("temperature") | |
else: | |
gas = Particles() | |
if not gas.is_empty(): | |
plots += ["column density", "divergence", "mass flux"] | |
if hasattr(gas, 'u'): | |
plots.append("temperature") | |
if not stars.is_empty(): | |
plots += ["stars", "stars colour"] | |
if not hasattr(gas, "h_smooth"): | |
gas.h_smooth = 1 | units.pc | |
if not gas.is_empty(): | |
mtot = gas.total_mass() | |
com = mtot * gas.center_of_mass() | |
time = gas.get_timestamp() | |
else: | |
mtot = 0 | units.MSun | |
com = [0, 0, 0] | units.pc * units.MSun | |
time = False | |
if not sinks.is_empty(): | |
mtot += sinks.total_mass() | |
com += sinks.total_mass() * sinks.center_of_mass() | |
if not time: | |
time = sinks.get_timestamp() | |
if not stars.is_empty(): | |
mtot += stars.total_mass() | |
com += stars.total_mass() * stars.center_of_mass() | |
if not time: | |
time = stars.get_timestamp() | |
com = com / mtot | |
if o.use_com: | |
offset_x = com[0] | |
offset_y = com[1] | |
offset_z = com[2] | |
if time is None: | |
print('Unable to get timestamp, set time to 0 Myr.') | |
time = 0.0 | units.Myr | |
converter = nbody_system.nbody_to_si( | |
1 | units.pc, 1 | units.MSun, | |
) | |
width = o.w | units.pc | |
bins = o.bins | |
thickness = o.thickness | units.pc | |
if thickness > 0 | units.pc: | |
if z_axis == "x": | |
gas_slab = gas[(gas.x - offset_x)**2 < (thickness/2)**2] | |
elif z_axis == "y": | |
gas_slab = gas[(gas.y - offset_y)**2 < (thickness/2)**2] | |
else: # if z_axis == "z": | |
gas_slab = gas[(gas.z - offset_z)**2 < (thickness/2)**2] | |
else: | |
gas_slab = gas | |
maps = MapHydro(gas_slab, stars, sinks) | |
maps.width = width | |
maps.bins = bins | |
maps.offset = (offset_x, offset_y, offset_z) | |
maps.axes = (x_axis, y_axis, z_axis) | |
for plot in plots: | |
if plot == "density": | |
use_fresco = fresco_fraction | |
else: | |
use_fresco = 0 | |
figure, ax = plot_hydro_and_stars( | |
time, | |
maps, | |
title="time = %06.2f %s" % ( | |
time.value_in(units.Myr), | |
units.Myr, | |
), | |
plot=plot, | |
# colorbar=True, # causes weird interpolation | |
# alpha_sfe=0.02, | |
# stars_are_sinks=False, | |
use_fresco=use_fresco, | |
length_unit=units.parsec, | |
dpi=dpi, | |
starscale=starscale, | |
) | |
plot_cluster_locations = False | |
if plot_cluster_locations: | |
from find_clusters import find_clusters | |
clusters = find_clusters(stars, convert_nbody=converter,) | |
for cluster in clusters: | |
cluster_com = cluster.center_of_mass() | |
cluster_x = cluster_com[0].value_in(units.parsec) | |
cluster_y = cluster_com[1].value_in(units.parsec) | |
lagrangian = cluster.LagrangianRadii(converter) | |
lr90 = lagrangian[0][-2] | |
size = lr90.value_in(units.parsec) | |
# print("Circle with x, y, z: ", x, y, s) | |
circle = pyplot.Circle( | |
(cluster_x, cluster_y), size, color='r', fill=False, | |
) | |
ax.add_artist(circle) | |
pyplot.savefig( | |
plot.replace(" ", "_") | |
+ "-" + imagefilename + ".png", | |
dpi=dpi, | |
) | |
print( | |
"This plotting script is (c) Steven Rieder. Please cite \"Rieder et al. (in prep.)\" if you use it." | |
) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment