pymakeplots 0.2.0__py3-none-any.whl → 0.2.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pymakeplots/pymakeplots.py +225 -118
- {pymakeplots-0.2.0.dist-info → pymakeplots-0.2.2.dist-info}/METADATA +3 -3
- pymakeplots-0.2.2.dist-info/RECORD +9 -0
- {pymakeplots-0.2.0.dist-info → pymakeplots-0.2.2.dist-info}/WHEEL +1 -1
- pymakeplots-0.2.0.dist-info/RECORD +0 -9
- {pymakeplots-0.2.0.dist-info → pymakeplots-0.2.2.dist-info}/LICENSE.md +0 -0
- {pymakeplots-0.2.0.dist-info → pymakeplots-0.2.2.dist-info}/top_level.txt +0 -0
- {pymakeplots-0.2.0.dist-info → pymakeplots-0.2.2.dist-info}/zip-safe +0 -0
pymakeplots/pymakeplots.py
CHANGED
|
@@ -12,8 +12,8 @@ from mpl_toolkits.axes_grid1 import make_axes_locatable
|
|
|
12
12
|
from matplotlib.patches import Ellipse,Rectangle
|
|
13
13
|
from matplotlib import cm
|
|
14
14
|
from matplotlib.colors import ListedColormap, LinearSegmentedColormap
|
|
15
|
-
from matplotlib.offsetbox import AnchoredText
|
|
16
|
-
from mpl_toolkits.axes_grid1.anchored_artists import
|
|
15
|
+
from matplotlib.offsetbox import AnchoredText,AuxTransformBox, AnchoredOffsetbox
|
|
16
|
+
from mpl_toolkits.axes_grid1.anchored_artists import AnchoredSizeBar
|
|
17
17
|
from astropy.coordinates import ICRS
|
|
18
18
|
import matplotlib.gridspec as gridspec
|
|
19
19
|
from astropy.table import Table
|
|
@@ -23,6 +23,8 @@ import warnings
|
|
|
23
23
|
from spectral_cube import SpectralCube
|
|
24
24
|
from spectral_cube.utils import SpectralCubeWarning
|
|
25
25
|
warnings.filterwarnings(action='ignore', category=SpectralCubeWarning, append=True)
|
|
26
|
+
from scipy.stats import mode
|
|
27
|
+
|
|
26
28
|
|
|
27
29
|
def running_mean(x, N):
|
|
28
30
|
cumsum = np.cumsum(np.insert(x, 0, 0))
|
|
@@ -38,7 +40,7 @@ def rotateImage(img, angle, pivot):
|
|
|
38
40
|
|
|
39
41
|
|
|
40
42
|
class pymakeplots:
|
|
41
|
-
def __init__(self,cube_flat=None,pb=None,cube=None):
|
|
43
|
+
def __init__(self,cube_flat=None,pb=None,cube=None,rest_value=None):
|
|
42
44
|
self.galname=None
|
|
43
45
|
self.gal_distance=None
|
|
44
46
|
self.posang=None
|
|
@@ -66,6 +68,7 @@ class pymakeplots:
|
|
|
66
68
|
self.bardist=None
|
|
67
69
|
self.rmsfac=3
|
|
68
70
|
self.restfreq=None
|
|
71
|
+
self.repfreq=None
|
|
69
72
|
self.obj_ra=None
|
|
70
73
|
self.obj_dec=None
|
|
71
74
|
self.imagesize=None
|
|
@@ -78,29 +81,36 @@ class pymakeplots:
|
|
|
78
81
|
self.spatial_trim = None
|
|
79
82
|
self.maxvdisp=None
|
|
80
83
|
self.cliplevel=None
|
|
84
|
+
self.points2plot=None
|
|
85
|
+
self.pointingsra=None
|
|
86
|
+
self.pointingsdec=None
|
|
87
|
+
self.pointingsdiam=None
|
|
81
88
|
self.fits=False
|
|
82
89
|
self.pvdthick=5.
|
|
83
90
|
self.flipped=False
|
|
84
91
|
self.make_square=True
|
|
85
92
|
self.useallpixels = False
|
|
93
|
+
self.suppress_subbeam_artifacts=False
|
|
86
94
|
#self.wcs=None
|
|
87
95
|
|
|
88
96
|
if (cube != None)&(pb==None)&(cube_flat==None):
|
|
89
97
|
# only one cube given
|
|
90
|
-
self.input_cube_nopb(cube)
|
|
98
|
+
self.input_cube_nopb(cube,rest_value=rest_value)
|
|
91
99
|
|
|
92
100
|
if (cube != None)&(pb!=None):
|
|
93
101
|
# pbcorred cube and pb given
|
|
94
|
-
self.input_cube_pbcorr(cube,pb)
|
|
102
|
+
self.input_cube_pbcorr(cube,pb,rest_value=rest_value)
|
|
95
103
|
|
|
96
104
|
if (cube_flat != None)&(pb!=None):
|
|
97
105
|
# flat cube and pb given
|
|
98
106
|
if np.any(self.pbcorr_cube) == None: #check if the user gave all three cubes, in which case this call is redundant
|
|
99
|
-
self.input_cube_flat(cube_flat,pb)
|
|
107
|
+
self.input_cube_flat(cube_flat,pb,rest_value=rest_value)
|
|
100
108
|
|
|
101
109
|
if (cube != None)&(pb==None)&(cube_flat!=None):
|
|
102
110
|
# pbcorred cube and flat cube given
|
|
103
|
-
self.input_cube_pbcorr_and_flat(cube,cube_flat)
|
|
111
|
+
self.input_cube_pbcorr_and_flat(cube,cube_flat,rest_value=rest_value)
|
|
112
|
+
|
|
113
|
+
self.cube_for_param_guesses=self.flat_cube
|
|
104
114
|
|
|
105
115
|
def vsystrans_inv(self,val):
|
|
106
116
|
return val +self.vsys
|
|
@@ -124,21 +134,21 @@ class pymakeplots:
|
|
|
124
134
|
def beam_area(self):
|
|
125
135
|
return (np.pi*(self.bmaj/self.cellsize)*(self.bmin/self.cellsize))/(4*np.log(2))
|
|
126
136
|
|
|
127
|
-
def input_cube_pbcorr(self,path_to_pbcorr_cube,path_to_pb):
|
|
137
|
+
def input_cube_pbcorr(self,path_to_pbcorr_cube,path_to_pb,rest_value=None):
|
|
128
138
|
|
|
129
|
-
self.pbcorr_cube = self.read_primary_cube(path_to_pbcorr_cube)
|
|
139
|
+
self.pbcorr_cube = self.read_primary_cube(path_to_pbcorr_cube,rest_value=rest_value)
|
|
130
140
|
|
|
131
|
-
pb,hdr,_= self.read_in_a_cube(path_to_pb)
|
|
141
|
+
pb,hdr,_= self.read_in_a_cube(path_to_pb,rest_value=rest_value)
|
|
132
142
|
if self.flipped: pb=np.flip(pb,axis=2)
|
|
133
143
|
|
|
134
144
|
self.flat_cube = self.pbcorr_cube*pb
|
|
135
145
|
|
|
136
146
|
|
|
137
|
-
def input_cube_flat(self,path_to_flat_cube,path_to_pb):
|
|
147
|
+
def input_cube_flat(self,path_to_flat_cube,path_to_pb,rest_value=None):
|
|
138
148
|
|
|
139
|
-
self.flat_cube = self.read_primary_cube(path_to_flat_cube)
|
|
149
|
+
self.flat_cube = self.read_primary_cube(path_to_flat_cube,rest_value=rest_value)
|
|
140
150
|
|
|
141
|
-
pb,hdr,_= self.read_in_a_cube(path_to_pb)
|
|
151
|
+
pb,hdr,_= self.read_in_a_cube(path_to_pb,rest_value=rest_value)
|
|
142
152
|
if self.flipped: pb=np.flip(pb,axis=2)
|
|
143
153
|
|
|
144
154
|
self.pbcorr_cube = self.flat_cube.copy()*0.0
|
|
@@ -146,35 +156,41 @@ class pymakeplots:
|
|
|
146
156
|
|
|
147
157
|
|
|
148
158
|
|
|
149
|
-
def input_cube_nopb(self,path_to_cube):
|
|
159
|
+
def input_cube_nopb(self,path_to_cube,rest_value=None):
|
|
150
160
|
|
|
151
|
-
self.pbcorr_cube = self.read_primary_cube(path_to_cube)
|
|
161
|
+
self.pbcorr_cube = self.read_primary_cube(path_to_cube,rest_value=rest_value)
|
|
152
162
|
|
|
153
163
|
self.flat_cube = self.pbcorr_cube
|
|
154
164
|
|
|
155
165
|
|
|
156
166
|
|
|
157
|
-
def input_cube_pbcorr_and_flat(self,path_to_pbcorr_cube,path_to_flat_cube):
|
|
167
|
+
def input_cube_pbcorr_and_flat(self,path_to_pbcorr_cube,path_to_flat_cube,rest_value=None):
|
|
168
|
+
|
|
169
|
+
self.pbcorr_cube = self.read_primary_cube(path_to_pbcorr_cube,rest_value=rest_value)
|
|
158
170
|
|
|
159
|
-
self.
|
|
171
|
+
self.flat_cube,hdr,_ = self.read_in_a_cube(path_to_flat_cube,rest_value=rest_value)
|
|
160
172
|
|
|
161
|
-
self.flat_cube,hdr,_ = self.read_in_a_cube(path_to_flat_cube)
|
|
162
173
|
if self.flipped: self.flat_cube=np.flip(self.flat_cube,axis=2)
|
|
163
174
|
|
|
164
175
|
def smooth_mask(self,cube):
|
|
165
176
|
"""
|
|
166
177
|
Apply a Gaussian blur, using sigma = 4 in the velocity direction (seems to work best), to the uncorrected cube.
|
|
167
|
-
The mode 'nearest' seems to give the best results.
|
|
168
178
|
:return: (ndarray) mask to apply to the un-clipped cube
|
|
169
179
|
"""
|
|
170
180
|
sigma = 1.5 * self.bmaj / self.cellsize
|
|
171
181
|
smooth_cube = ndimage.uniform_filter(cube, size=[sigma, sigma,4], mode='constant') # mode='nearest'
|
|
172
182
|
newrms= self.rms_estimate(smooth_cube,0,1)
|
|
173
|
-
self.cliplevel=
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
183
|
+
self.cliplevel=self.rms*self.rmsfac
|
|
184
|
+
self.maskcliplevel=newrms*self.rmsfac
|
|
185
|
+
mask=(smooth_cube > self.maskcliplevel)
|
|
186
|
+
|
|
187
|
+
if self.suppress_subbeam_artifacts:
|
|
188
|
+
label,cnt=ndimage.label(mask)#.sum(axis=2))
|
|
189
|
+
hist,lab=np.histogram(label,bins=np.arange(cnt+1))
|
|
190
|
+
beampix=(self.bmaj*self.bmin)/(self.cellsize**2)
|
|
191
|
+
for thelabel in lab[0:-1][hist<(beampix*self.suppress_subbeam_artifacts)]:
|
|
192
|
+
mask[label == thelabel]=False
|
|
193
|
+
|
|
178
194
|
return mask
|
|
179
195
|
|
|
180
196
|
|
|
@@ -302,7 +318,8 @@ class pymakeplots:
|
|
|
302
318
|
return np.nanstd(cube[quarterx*1:3*quarterx,1*quartery:3*quartery,chanstart:chanend])
|
|
303
319
|
|
|
304
320
|
def get_header_coord_arrays(self,hdr):
|
|
305
|
-
|
|
321
|
+
|
|
322
|
+
|
|
306
323
|
cd1=self.spectralcube.wcs.pixel_scale_matrix[0,0]*3600
|
|
307
324
|
cd2=self.spectralcube.wcs.pixel_scale_matrix[1,1]*3600
|
|
308
325
|
x1=((np.arange(1,hdr['NAXIS1']+1)-(hdr['NAXIS1']//2))*cd1)# + hdr['CRVAL1']
|
|
@@ -311,37 +328,41 @@ class pymakeplots:
|
|
|
311
328
|
v1=self.spectralcube.spectral_axis.value
|
|
312
329
|
|
|
313
330
|
cd3= np.median(np.diff(v1))
|
|
331
|
+
|
|
314
332
|
|
|
315
333
|
return x1,y1,v1,np.abs(cd1),cd3
|
|
316
334
|
|
|
317
|
-
def read_in_a_cube(self,path):
|
|
318
|
-
|
|
335
|
+
def read_in_a_cube(self,path,rest_value=None,primary=False):
|
|
336
|
+
|
|
337
|
+
scube=SpectralCube.read(path).with_spectral_unit(u.km/u.s, velocity_convention='radio',rest_value=rest_value)
|
|
319
338
|
|
|
320
|
-
hdr=
|
|
321
|
-
cube = np.squeeze(
|
|
339
|
+
hdr=scube.header
|
|
340
|
+
cube = np.squeeze(scube.filled_data[:,:,:].T).value #squeeze to remove singular stokes axis if present
|
|
322
341
|
cube[np.isfinite(cube) == False] = 0.0
|
|
323
342
|
try:
|
|
324
|
-
beamtab=
|
|
343
|
+
beamtab=scube.beam
|
|
325
344
|
except:
|
|
326
345
|
try:
|
|
327
|
-
beamtab=
|
|
346
|
+
beamtab=scube.beams[np.floor(scube.beams.size/2).astype(int)]
|
|
328
347
|
except:
|
|
329
348
|
#try flipping them
|
|
330
349
|
try:
|
|
331
|
-
beamvals=[
|
|
350
|
+
beamvals=[scube.header['bmaj'],scube.header['bmin']]
|
|
332
351
|
beamtab=Beam(major=np.max(beamvals)*u.deg,minor=np.min(beamvals)*u.deg,pa=self.spectralcube.header['bpa']*u.deg)
|
|
333
352
|
except:
|
|
334
|
-
beamtab=False
|
|
335
|
-
|
|
353
|
+
beamtab=False
|
|
354
|
+
if primary:
|
|
355
|
+
self.spectralcube=scube
|
|
356
|
+
self.repfreq=np.median(self.spectralcube.with_spectral_unit(u.GHz).spectral_axis)
|
|
336
357
|
return cube, hdr, beamtab
|
|
337
358
|
|
|
338
359
|
|
|
339
360
|
|
|
340
|
-
def read_primary_cube(self,cube):
|
|
361
|
+
def read_primary_cube(self,cube,rest_value=None):
|
|
341
362
|
|
|
342
363
|
### read in cube ###
|
|
343
|
-
datacube,hdr,beam = self.read_in_a_cube(cube)
|
|
344
|
-
|
|
364
|
+
datacube,hdr,beam = self.read_in_a_cube(cube,rest_value=rest_value,primary=True)
|
|
365
|
+
|
|
345
366
|
self.bmaj=beam.major.to(u.arcsec).value
|
|
346
367
|
self.bmin=beam.minor.to(u.arcsec).value
|
|
347
368
|
self.bpa=beam.pa.value
|
|
@@ -351,15 +372,9 @@ class pymakeplots:
|
|
|
351
372
|
self.galname=hdr['OBJECT']
|
|
352
373
|
except:
|
|
353
374
|
self.galname="Galaxy"
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
375
|
+
|
|
357
376
|
self.xcoord,self.ycoord,self.vcoord,self.cellsize,self.dv = self.get_header_coord_arrays(hdr)
|
|
358
|
-
#breakpoint()
|
|
359
|
-
|
|
360
|
-
|
|
361
377
|
|
|
362
|
-
|
|
363
378
|
if self.dv < 0:
|
|
364
379
|
datacube = np.flip(datacube,axis=2)
|
|
365
380
|
self.dv*=(-1)
|
|
@@ -369,7 +384,15 @@ class pymakeplots:
|
|
|
369
384
|
|
|
370
385
|
self.rms= self.rms_estimate(datacube,self.linefree_chans_start,self.linefree_chans_end)
|
|
371
386
|
return datacube
|
|
372
|
-
|
|
387
|
+
|
|
388
|
+
def calc_offset(self,ra,dec):
|
|
389
|
+
refpos=SkyCoord(ra,dec)
|
|
390
|
+
xpix,ypix=self.spectralcube.wcs.celestial.world_to_pixel(refpos)
|
|
391
|
+
#self.xcentpix,self.ycentpix= xpix,ypix
|
|
392
|
+
xoffsetarc=np.interp(xpix,np.arange(self.xcoord.size),self.xcoord)
|
|
393
|
+
yoffsetarc=np.interp(ypix,np.arange(self.ycoord.size),self.ycoord)
|
|
394
|
+
return xpix,ypix,xoffsetarc,yoffsetarc
|
|
395
|
+
|
|
373
396
|
def prepare_cubes(self):
|
|
374
397
|
|
|
375
398
|
self.centskycoord=self.spectralcube.wcs.celestial.pixel_to_world(self.xcoord.size//2,self.ycoord.size//2).transform_to('icrs')
|
|
@@ -380,12 +403,19 @@ class pymakeplots:
|
|
|
380
403
|
if self.obj_dec == None:
|
|
381
404
|
self.obj_dec=self.y_skycent
|
|
382
405
|
|
|
383
|
-
refpos=SkyCoord(self.obj_ra*u.deg,self.obj_dec*u.deg)
|
|
384
|
-
xpix,ypix=self.spectralcube.wcs.celestial.world_to_pixel(refpos)
|
|
385
|
-
|
|
386
|
-
xoffsetarc=np.interp(xpix,np.arange(self.xcoord.size),self.xcoord)
|
|
387
|
-
yoffsetarc=np.interp(ypix,np.arange(self.ycoord.size),self.ycoord)
|
|
388
406
|
|
|
407
|
+
xpix,ypix,xoffsetarc,yoffsetarc=self.calc_offset(self.obj_ra*u.deg,self.obj_dec*u.deg)
|
|
408
|
+
self.xcentpix,self.ycentpix= xpix,ypix
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
### want to overplot the pointings
|
|
412
|
+
if self.pointingsra != None:
|
|
413
|
+
pointingposes=SkyCoord(self.pointingsra,self.pointingsdec,frame='icrs',unit=(u.hourangle,u.deg))
|
|
414
|
+
_,_,pointoffsetx,pointoffsety=self.calc_offset(pointingposes.ra,pointingposes.dec)
|
|
415
|
+
self.points2plot=np.zeros((len(pointoffsetx),3))
|
|
416
|
+
self.points2plot[:,0]=pointoffsetx-xoffsetarc
|
|
417
|
+
self.points2plot[:,1]=pointoffsety-yoffsetarc
|
|
418
|
+
self.points2plot[:,2]=self.pointingsdiam.value
|
|
389
419
|
|
|
390
420
|
self.clip_cube(xoffsetarc,yoffsetarc)
|
|
391
421
|
|
|
@@ -404,6 +434,8 @@ class pymakeplots:
|
|
|
404
434
|
if self.all_axes_physical:
|
|
405
435
|
self.xc=self.ang2kpctrans(self.xc)
|
|
406
436
|
self.yc=self.ang2kpctrans(self.yc)
|
|
437
|
+
if self.pointingsra != None:
|
|
438
|
+
self.points2plot=self.ang2kpctrans(self.points2plot)
|
|
407
439
|
|
|
408
440
|
|
|
409
441
|
|
|
@@ -441,7 +473,7 @@ class pymakeplots:
|
|
|
441
473
|
self.make_spec(axes=ax5,fits=fits)
|
|
442
474
|
|
|
443
475
|
### plotting PA on mom1
|
|
444
|
-
ypv=np.arange(-np.max(self.yc),np.max(self.yc),
|
|
476
|
+
ypv=np.arange(-np.max(self.yc),np.max(self.yc),self.cellsize)
|
|
445
477
|
xpv=ypv*0.0
|
|
446
478
|
ang=self.posang
|
|
447
479
|
c = np.cos(np.deg2rad(ang))
|
|
@@ -450,7 +482,6 @@ class pymakeplots:
|
|
|
450
482
|
y2 = s*xpv + c*ypv
|
|
451
483
|
ax2.scatter(0,0,facecolors='none',edgecolors='k')
|
|
452
484
|
ax2.plot(x2,y2,'k--')
|
|
453
|
-
#breakpoint()
|
|
454
485
|
|
|
455
486
|
###### make summary box
|
|
456
487
|
|
|
@@ -478,6 +509,7 @@ class pymakeplots:
|
|
|
478
509
|
|
|
479
510
|
if pdf:
|
|
480
511
|
plt.savefig(self.galname+"_allplots.pdf", bbox_inches = 'tight')
|
|
512
|
+
plt.close()
|
|
481
513
|
else:
|
|
482
514
|
plt.show()
|
|
483
515
|
|
|
@@ -561,19 +593,27 @@ class pymakeplots:
|
|
|
561
593
|
|
|
562
594
|
|
|
563
595
|
|
|
564
|
-
if self.chans2do == None:
|
|
565
|
-
# use the mask to try and guess the channels with signal.
|
|
566
|
-
mask_cumsum=np.nancumsum((self.pbcorr_cube > self.rmsfac*self.rms).sum(axis=0).sum(axis=0))
|
|
567
|
-
w_low,=np.where(mask_cumsum/np.max(mask_cumsum) < 0.02)
|
|
568
|
-
w_high,=np.where(mask_cumsum/np.max(mask_cumsum) > 0.98)
|
|
596
|
+
if np.any(self.chans2do == None):
|
|
569
597
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
598
|
+
clip=4.0
|
|
599
|
+
vwidth=4000
|
|
600
|
+
while vwidth > 1500:
|
|
601
|
+
clip+=0.1
|
|
602
|
+
# use the mask to try and guess the channels with signal.
|
|
603
|
+
mask_cumsum=np.nancumsum((self.cube_for_param_guesses > clip*self.rms).sum(axis=0).sum(axis=0))
|
|
604
|
+
w_low,=np.where(mask_cumsum/np.nanmax(mask_cumsum) < 0.05)
|
|
605
|
+
w_high,=np.where(mask_cumsum/np.nanmax(mask_cumsum) > 0.95)
|
|
606
|
+
|
|
607
|
+
if w_low.size ==0: w_low=np.array([0])
|
|
608
|
+
if w_high.size ==0: w_high=np.array([self.vcoord.size])
|
|
609
|
+
#breakpoint()
|
|
610
|
+
self.chans2do=[np.clip(np.max(w_low)-7,0,self.vcoord.size-1),np.clip(np.min(w_high)+7,0,self.vcoord.size-1)]
|
|
611
|
+
vwidth=self.vcoord[self.chans2do[1]]-self.vcoord[self.chans2do[0]]
|
|
612
|
+
#print(clip)
|
|
613
|
+
|
|
574
614
|
if self.vsys == None:
|
|
575
615
|
# use the cube to try and guess the vsys
|
|
576
|
-
self.vsys=((self.
|
|
616
|
+
self.vsys=((self.cube_for_param_guesses*(self.cube_for_param_guesses > self.rmsfac*self.rms)).sum(axis=0).sum(axis=0)*self.vcoord).sum()/((self.cube_for_param_guesses*(self.cube_for_param_guesses > self.rmsfac*self.rms)).sum(axis=0).sum(axis=0)).sum()
|
|
577
617
|
|
|
578
618
|
if self.imagesize != None:
|
|
579
619
|
if np.array(self.imagesize).size == 1:
|
|
@@ -587,7 +627,7 @@ class pymakeplots:
|
|
|
587
627
|
|
|
588
628
|
if self.spatial_trim == None:
|
|
589
629
|
|
|
590
|
-
mom0=(self.
|
|
630
|
+
mom0=(self.cube_for_param_guesses > self.rmsfac*self.rms).sum(axis=2)
|
|
591
631
|
mom0[mom0>0]=1
|
|
592
632
|
|
|
593
633
|
cumulative_x = np.nancumsum(mom0.sum(axis=1),dtype=float)
|
|
@@ -637,19 +677,16 @@ class pymakeplots:
|
|
|
637
677
|
return cb
|
|
638
678
|
|
|
639
679
|
|
|
640
|
-
def add_beam(self,ax):
|
|
680
|
+
def add_beam(self,ax):
|
|
681
|
+
aux_tr_box = AuxTransformBox(ax.transData)
|
|
682
|
+
|
|
641
683
|
if self.all_axes_physical:
|
|
642
|
-
|
|
643
|
-
loc='lower left', pad=0.5, borderpad=0.4,
|
|
644
|
-
frameon=False)
|
|
684
|
+
aux_tr_box.add_artist(Ellipse((0, 0), width=self.ang2kpctrans(self.bmaj), height=self.ang2kpctrans(self.bmin), angle=self.bpa+90,edgecolor='black',facecolor='none',linewidth=1.5))
|
|
645
685
|
else:
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
ae.ellipse.set_facecolor('none')
|
|
651
|
-
ae.ellipse.set_linewidth(1.5)
|
|
652
|
-
ax.add_artist(ae)
|
|
686
|
+
aux_tr_box.add_artist(Ellipse((0, 0), width=self.bmaj, height=self.bmin, angle=self.bpa+90,edgecolor='black',facecolor='none',linewidth=1.5))
|
|
687
|
+
box = AnchoredOffsetbox(child=aux_tr_box, loc='lower left', pad=0.5, borderpad=0.4,frameon=False)
|
|
688
|
+
ax.add_artist(box)
|
|
689
|
+
|
|
653
690
|
|
|
654
691
|
|
|
655
692
|
|
|
@@ -701,6 +738,29 @@ class pymakeplots:
|
|
|
701
738
|
|
|
702
739
|
|
|
703
740
|
self.add_beam(ax1)
|
|
741
|
+
|
|
742
|
+
if np.any(self.points2plot != None):
|
|
743
|
+
naca=0
|
|
744
|
+
nalma=0
|
|
745
|
+
for xp,yp,diam in self.points2plot:
|
|
746
|
+
if diam/np.min(self.points2plot[:,2])>1.7:
|
|
747
|
+
ls=':' #aca
|
|
748
|
+
if naca ==0:
|
|
749
|
+
label='ACA'
|
|
750
|
+
else:
|
|
751
|
+
label=None
|
|
752
|
+
naca+=1
|
|
753
|
+
else:
|
|
754
|
+
ls='--' #12m
|
|
755
|
+
if nalma ==0:
|
|
756
|
+
label='12m'
|
|
757
|
+
else:
|
|
758
|
+
label=None
|
|
759
|
+
nalma+=1
|
|
760
|
+
circle2 = plt.Circle((xp, yp), diam/2., color='k',ls=ls, fill=False,alpha=0.2,label=label)
|
|
761
|
+
ax1.add_patch(circle2)
|
|
762
|
+
ax1.legend(frameon=False,loc='upper left',fontsize='x-small',markerscale=0.5)
|
|
763
|
+
|
|
704
764
|
if self.make_square:
|
|
705
765
|
ax1.set_xlim(np.min([self.xc[0],self.yc[0]]),np.max([self.xc[-1],self.yc[-1]]))
|
|
706
766
|
ax1.set_ylim(np.min([self.xc[0],self.yc[0]]),np.max([self.xc[-1],self.yc[-1]]))
|
|
@@ -725,7 +785,7 @@ class pymakeplots:
|
|
|
725
785
|
mom1=mom0.copy()*np.nan
|
|
726
786
|
mom1[mom0 != 0.0] = (((self.pbcorr_cube_trim*self.mask_trim)*self.vcoord_trim).sum(axis=2))[mom0 != 0.0]/mom0[mom0 != 0.0]
|
|
727
787
|
|
|
728
|
-
|
|
788
|
+
|
|
729
789
|
vticks=np.linspace((-1)*np.ceil(np.max(np.abs(self.vcoord_trim-self.vsys))/10.)*10.,np.ceil(np.max(np.abs(self.vcoord_trim-self.vsys))/10.)*10.,5)
|
|
730
790
|
|
|
731
791
|
im1=ax1.contourf(self.xc,self.yc,mom1.T-self.vsys,levels=self.vcoord_trim-self.vsys,cmap=sauron,vmin=vticks[0],vmax=vticks[-1])
|
|
@@ -771,12 +831,13 @@ class pymakeplots:
|
|
|
771
831
|
mom2[i,j]=np.sqrt(np.sum(np.abs(self.pbcorr_cube_trim[i,j,:]*self.mask_trim[i,j,:]) * (self.vcoord_trim - mom1[i,j]) ** 2, axis=0) / np.sum(abs(self.pbcorr_cube_trim[i,j]*self.mask_trim[i,j,:]), axis=0))
|
|
772
832
|
|
|
773
833
|
if self.maxvdisp == None:
|
|
774
|
-
self.maxvdisp=np.ceil(np.clip(np.nanstd(mom2)*4,
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
834
|
+
self.maxvdisp=np.ceil(np.clip(np.nanstd(mom2)*4,20,np.nanmax(mom2))/10.)*10.
|
|
835
|
+
else:
|
|
836
|
+
self.maxvdisp=np.ceil(np.clip(np.nanstd(mom2)*4,20,self.maxvdisp)/10.)*10.
|
|
837
|
+
if np.isfinite(self.maxvdisp)==False:
|
|
838
|
+
self.maxvdisp=50.
|
|
839
|
+
#breakpoint()
|
|
778
840
|
mom2levs=np.linspace(0,self.maxvdisp,10)
|
|
779
|
-
|
|
780
841
|
im1=ax1.contourf(self.xc,self.yc,mom2.T,levels=mom2levs,cmap=sauron,vmax=self.maxvdisp)
|
|
781
842
|
|
|
782
843
|
if self.all_axes_physical:
|
|
@@ -796,7 +857,7 @@ class pymakeplots:
|
|
|
796
857
|
vticks=np.arange(0,5)*dvticks
|
|
797
858
|
|
|
798
859
|
cb=self.colorbar(im1,ticks=vticks)
|
|
799
|
-
cb.set_label(
|
|
860
|
+
cb.set_label('$\\sigma_{obs}$ (km s$^{-1}$)')
|
|
800
861
|
|
|
801
862
|
|
|
802
863
|
self.add_beam(ax1)
|
|
@@ -860,8 +921,8 @@ class pymakeplots:
|
|
|
860
921
|
newhdu.header['CUNIT2']=self.spectralcube.header['CUNIT2']
|
|
861
922
|
newhdu.header['BMAJ']=self.bmaj/3600.
|
|
862
923
|
newhdu.header['BMIN']=self.bmin/3600.
|
|
863
|
-
newhdu.header['BPA']=self.bpa
|
|
864
|
-
newhdu.header['MOMCLIP']=(self.
|
|
924
|
+
newhdu.header['BPA']=self.bpa
|
|
925
|
+
newhdu.header['MOMCLIP']=(self.maskcliplevel, self.bunit+' km/s')
|
|
865
926
|
newhdu.header['VSYS']=(self.vsys,'km/s')
|
|
866
927
|
newhdu.header['comment'] = 'Moment map created with pymakeplots'
|
|
867
928
|
|
|
@@ -881,7 +942,7 @@ class pymakeplots:
|
|
|
881
942
|
filename=self.galname+"_pvd.fits"
|
|
882
943
|
else:
|
|
883
944
|
filename=self.fits+"_pvd.fits"
|
|
884
|
-
|
|
945
|
+
|
|
885
946
|
newhdu = fits.PrimaryHDU(pvd)
|
|
886
947
|
newhdu.header['CRPIX1']=1
|
|
887
948
|
newhdu.header['CRVAL1']=xx[0]
|
|
@@ -895,10 +956,10 @@ class pymakeplots:
|
|
|
895
956
|
newhdu.header['CUNIT2']='km/s'
|
|
896
957
|
newhdu.header['BMAJ']=self.bmaj/3600.
|
|
897
958
|
newhdu.header['BMIN']=self.bmin/3600.
|
|
898
|
-
newhdu.header['BPA']=self.bpa
|
|
959
|
+
newhdu.header['BPA']=self.bpa
|
|
899
960
|
newhdu.header['PVDANGLE']=(self.posang,'deg')
|
|
900
961
|
newhdu.header['PVDTHICK']=(self.pvdthick,'pixels')
|
|
901
|
-
newhdu.header['MOMCLIP']=(self.
|
|
962
|
+
newhdu.header['MOMCLIP']=(self.maskcliplevel, self.bunit+' km/s')
|
|
902
963
|
newhdu.header['VSYS']=(self.vsys,'km/s')
|
|
903
964
|
newhdu.header['comment'] = 'Moment map created with pymakeplots'
|
|
904
965
|
newhdu.header['BUNIT'] = self.bunit+' km/s'
|
|
@@ -906,7 +967,7 @@ class pymakeplots:
|
|
|
906
967
|
newhdu.writeto(filename,overwrite=True)
|
|
907
968
|
|
|
908
969
|
def make_pvd(self,axes=None,fits=False,pdf=False):
|
|
909
|
-
|
|
970
|
+
self.fits=fits
|
|
910
971
|
if np.any(self.xc) == None:
|
|
911
972
|
self.prepare_cubes()
|
|
912
973
|
|
|
@@ -921,31 +982,67 @@ class pymakeplots:
|
|
|
921
982
|
if self.posang==None:
|
|
922
983
|
# try fitting the moment one to get the kinematic pa
|
|
923
984
|
if not self.silent: print("No position angle given, estimating using the observed moment one.")
|
|
924
|
-
mom0=(self.
|
|
985
|
+
mom0=(self.flat_cube_trim*self.mask_trim).sum(axis=2)
|
|
925
986
|
mom1=mom0.copy()*np.nan
|
|
926
|
-
mom1[mom0 != 0.0] = (((self.
|
|
927
|
-
mom1=mom1.T
|
|
987
|
+
mom1[mom0 != 0.0] = (((self.flat_cube_trim*self.mask_trim)*self.vcoord_trim).sum(axis=2))[mom0 != 0.0]/mom0[mom0 != 0.0]
|
|
988
|
+
mom1=mom1.T
|
|
928
989
|
|
|
929
|
-
|
|
930
|
-
#
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
xv, yv = np.meshgrid(x,y)
|
|
942
|
-
x,y,v = xv.flatten(),yv.flatten(),v.flatten()
|
|
990
|
+
# ### sigma clip
|
|
991
|
+
# mom1[np.abs(mom1/np.nanstd(mom1))>5]=np.nan
|
|
992
|
+
|
|
993
|
+
### select largest contigious structure
|
|
994
|
+
label,cnt=ndimage.label(self.mask_trim.sum(axis=2))
|
|
995
|
+
mode_label= mode(label[label>0])
|
|
996
|
+
|
|
997
|
+
hist,lab=np.histogram(label[label>0],bins=np.arange(cnt+1)+1)
|
|
998
|
+
beampix=(self.bmaj*self.bmin)/(self.cellsize**2)
|
|
999
|
+
|
|
1000
|
+
|
|
1001
|
+
if len(lab[0:-1][hist<beampix]) != len(lab[0:-1]):
|
|
943
1002
|
|
|
944
|
-
|
|
1003
|
+
## remove beam size artifacts
|
|
1004
|
+
for thelabel in lab[0:-1][hist<beampix]:
|
|
1005
|
+
mom1[label.T == thelabel]=np.nan
|
|
1006
|
+
|
|
1007
|
+
## keep biggest structures that contain >80% of remaining pixels
|
|
1008
|
+
remainlab=lab[0:-1][hist>beampix]
|
|
1009
|
+
remainhist=hist[hist>beampix]
|
|
1010
|
+
st=np.argsort(remainhist)
|
|
1011
|
+
for thelabel in remainlab[st][np.nancumsum(remainhist[st]/np.sum(remainhist)) < 0.2]:
|
|
1012
|
+
mom1[label.T == thelabel]=np.nan
|
|
1013
|
+
|
|
1014
|
+
else:
|
|
1015
|
+
mom1[label.T != mode_label.mode]=np.nan
|
|
945
1016
|
|
|
1017
|
+
#self.useallpixels=True
|
|
1018
|
+
|
|
1019
|
+
### remove median
|
|
1020
|
+
mom1-=np.nanmedian(mom1)
|
|
1021
|
+
|
|
1022
|
+
|
|
1023
|
+
|
|
1024
|
+
#breakpoint()
|
|
1025
|
+
# if the cube is small, use it directly to estimate posang. If its large, then interpolate down to keep runtime low.
|
|
1026
|
+
# if (self.pbcorr_cube_trim[:,:,0].size < 50*50) or (self.useallpixels):
|
|
1027
|
+
xv, yv = np.meshgrid(self.xc,self.yc)
|
|
1028
|
+
x,y,v = xv[np.isfinite(mom1)],yv[np.isfinite(mom1)],mom1[np.isfinite(mom1)]
|
|
1029
|
+
# else:
|
|
1030
|
+
# print("Downsampling the observed moment one in PA estimate for speed. Set `useallpixels` to override.")
|
|
1031
|
+
#mom1[np.isfinite(mom1) == False] = self.vsys
|
|
1032
|
+
#breakpoint()
|
|
1033
|
+
# interper = interpolate.RegularGridInterpolator((self.xc,self.yc),(mom1-self.vsys).T,bounds_error=False,fill_value=np.nan)
|
|
1034
|
+
# x=np.linspace(np.min(self.xc),np.max(self.xc),self.xc.size//2)
|
|
1035
|
+
# y=np.linspace(np.min(self.yc),np.max(self.yc),self.yc.size//2)
|
|
1036
|
+
# xv, yv = np.meshgrid(x,y)
|
|
1037
|
+
# v= interper((xv,yv))
|
|
1038
|
+
# x,y,v = xv.flatten(),yv.flatten(),v.flatten()
|
|
1039
|
+
|
|
1040
|
+
#breakpoint()
|
|
1041
|
+
self.posang,_,_ = fit_kinematic_pa(x[np.isfinite(v)],y[np.isfinite(v)],v[np.isfinite(v)],nsteps=36,plot=False,quiet=True)
|
|
1042
|
+
|
|
946
1043
|
if np.sin(np.deg2rad((self.posang+45)*2)) > 0:
|
|
947
1044
|
# do y axis cut
|
|
948
|
-
if np.
|
|
1045
|
+
if np.nansum(mom1[self.yc > 0,:])/(mom1[self.yc > 0,:]).size > np.nansum(mom1[self.yc < 0,:])/(mom1[self.yc < 0,:]).size:
|
|
949
1046
|
# posang should be gt 180
|
|
950
1047
|
if self.posang < 180: self.posang += 180
|
|
951
1048
|
else:
|
|
@@ -953,21 +1050,20 @@ class pymakeplots:
|
|
|
953
1050
|
if self.posang > 180: self.posang -= 180
|
|
954
1051
|
else:
|
|
955
1052
|
# do x axis cut
|
|
956
|
-
if np.
|
|
1053
|
+
if np.nansum(mom1[:,self.xc > 0])/(mom1[:,self.xc > 0]).size > np.nansum(mom1[:,self.xc < 0])/(mom1[:,self.xc < 0]).size:
|
|
957
1054
|
# posang should be gt 180
|
|
958
1055
|
if self.posang < 180: self.posang += 180
|
|
959
1056
|
else:
|
|
960
1057
|
# posang should be lt 180
|
|
961
1058
|
if self.posang > 180: self.posang -= 180
|
|
962
1059
|
if not self.silent: print("PA estimate (degrees): ",np.round(self.posang,1))
|
|
963
|
-
|
|
964
|
-
|
|
1060
|
+
|
|
965
1061
|
centpix_x=np.where(np.isclose(self.xc,0.0,atol=self.cellsize/1.9))[0]
|
|
966
1062
|
centpix_y=np.where(np.isclose(self.yc,0.0,atol=self.cellsize/1.9))[0]
|
|
967
|
-
|
|
1063
|
+
|
|
968
1064
|
|
|
969
1065
|
|
|
970
|
-
rotcube= rotateImage(self.pbcorr_cube_trim*self.mask_trim,90-self.posang,[
|
|
1066
|
+
rotcube= rotateImage(self.pbcorr_cube_trim*self.mask_trim,90-self.posang,[centpix_x[0],centpix_y[0]])
|
|
971
1067
|
|
|
972
1068
|
|
|
973
1069
|
pvd=rotcube[:,np.array(rotcube.shape[1]//2-self.pvdthick).astype(int):np.array(rotcube.shape[1]//2+self.pvdthick).astype(int),:].sum(axis=1)
|
|
@@ -998,34 +1094,44 @@ class pymakeplots:
|
|
|
998
1094
|
newcmp = ListedColormap(oldcmp(np.linspace(0.15, 1, 256)))
|
|
999
1095
|
|
|
1000
1096
|
|
|
1097
|
+
if np.nanmax(pvd) < self.cliplevel:
|
|
1098
|
+
contour_levels=np.linspace(np.nanmax(pvd)/2.,np.nanmax(pvd),10)
|
|
1099
|
+
if np.sum(contour_levels)==0:
|
|
1100
|
+
contour_levels=np.array([self.cliplevel,self.cliplevel+0.1])
|
|
1101
|
+
else:
|
|
1102
|
+
contour_levels=np.linspace(self.cliplevel,np.nanmax(pvd),10)
|
|
1001
1103
|
|
|
1002
|
-
axes.contourf(pvdaxis,vaxis,pvd.T,levels=
|
|
1003
|
-
axes.contour(pvdaxis,vaxis,pvd.T,levels=
|
|
1104
|
+
axes.contourf(pvdaxis,vaxis,pvd.T,levels=contour_levels,cmap=newcmp)
|
|
1105
|
+
axes.contour(pvdaxis,vaxis,pvd.T,levels=contour_levels,colors='black')
|
|
1004
1106
|
|
|
1005
1107
|
if self.all_axes_physical:
|
|
1006
1108
|
axes.set_xlabel('Offset (kpc)')
|
|
1109
|
+
secaxy = axes.secondary_xaxis('top', functions=(self.ang2kpctrans_inv, self.ang2kpctrans))
|
|
1110
|
+
secaxy.set_xlabel('Offset (")')
|
|
1007
1111
|
else:
|
|
1008
1112
|
axes.set_xlabel('Offset (")')
|
|
1009
|
-
|
|
1113
|
+
secaxy = axes.secondary_xaxis('top', functions=(self.ang2kpctrans, self.ang2kpctrans_inv))
|
|
1114
|
+
secaxy.set_xlabel('Offset (kpc)')
|
|
1115
|
+
|
|
1010
1116
|
axes.set_ylabel('Velocity (km s$^{-1}$)')
|
|
1011
1117
|
|
|
1012
|
-
|
|
1013
|
-
secaxy.set_xlabel('Offset (kpc)')
|
|
1118
|
+
|
|
1014
1119
|
|
|
1015
1120
|
secax = axes.secondary_yaxis('right', functions=(self.vsystrans, self.vsystrans_inv))
|
|
1016
1121
|
secax.set_ylabel(r'V$_{\rm offset}$ (km s$^{-1}$)')
|
|
1017
1122
|
|
|
1018
|
-
anchored_text = AnchoredText("PA: "+str(round(self.posang,1))+
|
|
1123
|
+
anchored_text = AnchoredText("PA: "+str(round(self.posang,1))+'$^{\\circ}$', loc=loc1,frameon=False)
|
|
1019
1124
|
axes.add_artist(anchored_text)
|
|
1020
1125
|
|
|
1021
1126
|
if self.gal_distance != None and not self.all_axes_physical:
|
|
1022
1127
|
self.scalebar(axes,loc=loc2)
|
|
1023
|
-
|
|
1128
|
+
|
|
1024
1129
|
if self.fits:
|
|
1025
1130
|
self.write_pvd_fits(pvdaxis,vaxis,pvd.T)
|
|
1026
1131
|
|
|
1027
1132
|
if pdf:
|
|
1028
1133
|
plt.savefig(self.galname+"_pvd.pdf", bbox_inches = 'tight')
|
|
1134
|
+
plt.close()
|
|
1029
1135
|
else:
|
|
1030
1136
|
if not outsideaxis: plt.show()
|
|
1031
1137
|
|
|
@@ -1097,6 +1203,7 @@ class pymakeplots:
|
|
|
1097
1203
|
|
|
1098
1204
|
if pdf:
|
|
1099
1205
|
plt.savefig(self.galname+"_spec.pdf", bbox_inches = 'tight')
|
|
1206
|
+
plt.close()
|
|
1100
1207
|
else:
|
|
1101
1208
|
if not outsideaxis: plt.show()
|
|
1102
1209
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pymakeplots
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Home-page: https://github.com/TimothyADavis/pymakeplots
|
|
5
5
|
Author: Timothy A. Davis
|
|
6
6
|
Author-email: DavisT@cardiff.ac.uk
|
|
@@ -12,8 +12,8 @@ Classifier: Operating System :: OS Independent
|
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE.md
|
|
14
14
|
Requires-Dist: numpy
|
|
15
|
-
Requires-Dist: matplotlib
|
|
16
|
-
Requires-Dist: scipy
|
|
15
|
+
Requires-Dist: matplotlib >3.3.1
|
|
16
|
+
Requires-Dist: scipy >=1.14.0
|
|
17
17
|
Requires-Dist: astropy
|
|
18
18
|
Requires-Dist: spectral-cube
|
|
19
19
|
Requires-Dist: radio-beam
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
pymakeplots/__init__.py,sha256=PLKnIm6rIKYjH_b6L9UBCFtv1lKt5GItdBKDbSOhuMU,87
|
|
2
|
+
pymakeplots/pymakeplots.py,sha256=_N8Wp2teEfr6W9SqBi3bb2WV3F7vxzfVhWDnhq_Qa1E,48843
|
|
3
|
+
pymakeplots/sauron_colormap.py,sha256=8APka-_L432wyICcHcySjk3qmjNOynb3iDjjlMuUTU0,4448
|
|
4
|
+
pymakeplots-0.2.2.dist-info/LICENSE.md,sha256=qtvmvajOPCad_5HMY5u49hldzBIXz7tbHbuGSG_HE5o,1077
|
|
5
|
+
pymakeplots-0.2.2.dist-info/METADATA,sha256=YlzzxZMuONkCuzkhi_5tHinfu6TsqW8MzdpB5jG_QTg,1847
|
|
6
|
+
pymakeplots-0.2.2.dist-info/WHEEL,sha256=Z4pYXqR_rTB7OWNDYFOm1qRk0RX6GFP2o8LgvP453Hk,91
|
|
7
|
+
pymakeplots-0.2.2.dist-info/top_level.txt,sha256=KgG17vI_D6BsiKRe_81UU5709tXc6Ha7gx1IwjA7ur8,12
|
|
8
|
+
pymakeplots-0.2.2.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
9
|
+
pymakeplots-0.2.2.dist-info/RECORD,,
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
pymakeplots/__init__.py,sha256=PLKnIm6rIKYjH_b6L9UBCFtv1lKt5GItdBKDbSOhuMU,87
|
|
2
|
-
pymakeplots/pymakeplots.py,sha256=-QBfg3BnC6I3RuGh9nFpfHXTQHNmGP8FsgboBCQ3cxk,43686
|
|
3
|
-
pymakeplots/sauron_colormap.py,sha256=8APka-_L432wyICcHcySjk3qmjNOynb3iDjjlMuUTU0,4448
|
|
4
|
-
pymakeplots-0.2.0.dist-info/LICENSE.md,sha256=qtvmvajOPCad_5HMY5u49hldzBIXz7tbHbuGSG_HE5o,1077
|
|
5
|
-
pymakeplots-0.2.0.dist-info/METADATA,sha256=-bZdjlHzVwuuEnhWLyWoCJeNtFUCVNymSPe-yhETRmo,1840
|
|
6
|
-
pymakeplots-0.2.0.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
|
|
7
|
-
pymakeplots-0.2.0.dist-info/top_level.txt,sha256=KgG17vI_D6BsiKRe_81UU5709tXc6Ha7gx1IwjA7ur8,12
|
|
8
|
-
pymakeplots-0.2.0.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
9
|
-
pymakeplots-0.2.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|