In [1]:
Copied!
from vcat import ImageData, ImageCube
#set important parameters
useDIFMAP=True
from vcat import ImageData, ImageCube
#set important parameters
useDIFMAP=True
2025-10-08 12:00:38,317 - INFO - vcat - Logging initialized. Log file: Console only. 2025-10-08 12:00:38,318 - INFO - vcat - No environment variable VCAT_CONFIG found, will use defaults. 2025-10-08 12:00:38,319 - INFO - vcat - Using DIFMAP path: /usr/local/difmap/
Thank you for using VCAT. Have fun with VLBI! If you are using this package please cite VCAT Team et al. 2025 ....
Loading the data¶
In [2]:
Copied!
#This notebook aims to explain the different alignment methods that are possible with the VCAT package
#We will start with two images:
#This is a U-band image which was already shifted by -5,5 manually
data_shifted=ImageData("../dataset_example/3C111_U_shifted/3C111_2014-05-08_15GHz.fits",
uvf_file="../dataset_example/3C111_U_shifted/3C111_2014-05-08_15GHz.uvf")
#The second dataset is a regular X band image
dataX=ImageData("../dataset_example/3C111_X_2014_05_08/3C111_X_2014_05_08.fits",
uvf_file="../dataset_example/3C111_X_2014_05_08/3C111_X_2014_05_08.uvf")
#Lets'plot both datasets as an ImageCube to have a look
im_cube=ImageCube([data_shifted,dataX])
im_cube.plot()
#This notebook aims to explain the different alignment methods that are possible with the VCAT package
#We will start with two images:
#This is a U-band image which was already shifted by -5,5 manually
data_shifted=ImageData("../dataset_example/3C111_U_shifted/3C111_2014-05-08_15GHz.fits",
uvf_file="../dataset_example/3C111_U_shifted/3C111_2014-05-08_15GHz.uvf")
#The second dataset is a regular X band image
dataX=ImageData("../dataset_example/3C111_X_2014_05_08/3C111_X_2014_05_08.fits",
uvf_file="../dataset_example/3C111_X_2014_05_08/3C111_X_2014_05_08.uvf")
#Lets'plot both datasets as an ImageCube to have a look
im_cube=ImageCube([data_shifted,dataX])
im_cube.plot()
Out[2]:
<vcat.plots.multi_fits_image.MultiFitsImage at 0x7229197379d0>
Alignment using cross-correlation¶
In [3]:
Copied!
#We will now try to align the shifted image to the second (unshifted) dataset
#we will use the cross-correlation method
method="cross_correlation" #(default setting)
#And we will also let the code determine a common pixel grid and common beam
auto_regrid=True
#Let's start the alignment, this will align data_shifted -> dataX
data_aligned=data_shifted.align(dataX,method=method,auto_regrid=auto_regrid,useDIFMAP=useDIFMAP)
#And plot the resulting image which is shifted and convolved with the common beam
data_aligned.plot()
#We will now try to align the shifted image to the second (unshifted) dataset
#we will use the cross-correlation method
method="cross_correlation" #(default setting)
#And we will also let the code determine a common pixel grid and common beam
auto_regrid=True
#Let's start the alignment, this will align data_shifted -> dataX
data_aligned=data_shifted.align(dataX,method=method,auto_regrid=auto_regrid,useDIFMAP=useDIFMAP)
#And plot the resulting image which is shifted and convolved with the common beam
data_aligned.plot()
2025-10-08 12:00:46,305 - INFO - vcat - Automatically regridding image to minimum pixelsize, smallest FOV and common beam 2025-10-08 12:00:47,175 - INFO - vcat - common beam calculated: [1.7770593660568255, 1.1265451854188613, -10.920121192749493] 2025-10-08 12:00:57,875 - INFO - vcat - will apply shift (x,y): [4.28000001011685 : -5.32000001257515] mas
Out[3]:
<vcat.plots.fits_image.FitsImage at 0x722994f29c40>
In [4]:
Copied!
#We regrid and restore the image cube with the same beam, FoV and pixel size to create the spectral index map
im_cube = ImageCube([data_aligned,dataX])
im_cube = im_cube.regrid(useDIFMAP=useDIFMAP) # automatically determines FoV and pixel size
im_cube = im_cube.restore(bmaj=dataX.beam_maj,bmin=dataX.beam_min,posa=dataX.beam_pa,useDIFMAP=useDIFMAP) # restore with larger beam
#Define the Frequencies to use for spectral index (will automatically pick the images closest to that frequency
freq1=8 #GHz
freq2=15 #GHz
#get the spectral index map
spix_map=im_cube.get_spectral_index_map(freq1,freq2,spix_vmin=-2,spix_vmax=3) # resetting colorbar limits
#The spix_map itself is also an ImageCube object and a copy of the higher frequency image with image.spix set to the spectral index map
spix_map.plot(plot_mode="spix",contour=True,xlim=[15,-5],ylim=[-5,10],do_colorbar=True,figsize=(5,4))
#We regrid and restore the image cube with the same beam, FoV and pixel size to create the spectral index map
im_cube = ImageCube([data_aligned,dataX])
im_cube = im_cube.regrid(useDIFMAP=useDIFMAP) # automatically determines FoV and pixel size
im_cube = im_cube.restore(bmaj=dataX.beam_maj,bmin=dataX.beam_min,posa=dataX.beam_pa,useDIFMAP=useDIFMAP) # restore with larger beam
#Define the Frequencies to use for spectral index (will automatically pick the images closest to that frequency
freq1=8 #GHz
freq2=15 #GHz
#get the spectral index map
spix_map=im_cube.get_spectral_index_map(freq1,freq2,spix_vmin=-2,spix_vmax=3) # resetting colorbar limits
#The spix_map itself is also an ImageCube object and a copy of the higher frequency image with image.spix set to the spectral index map
spix_map.plot(plot_mode="spix",contour=True,xlim=[15,-5],ylim=[-5,10],do_colorbar=True,figsize=(5,4))
2025-10-08 12:01:05,249 - INFO - vcat - Regridding images 2025-10-08 12:01:05,250 - INFO - vcat - Determining smallest pixel size and largest FoV from images for regridding Processing: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 2/2 [00:00<00:00, 33689.19it/s] Processing: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 2/2 [00:14<00:00, 7.01s/it] 2025-10-08 12:01:19,282 - INFO - vcat - Restoring images Processing: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 2/2 [00:14<00:00, 7.45s/it] 2025-10-08 12:01:34,179 - INFO - vcat - Image modifications completed. 2025-10-08 12:01:34,246 - INFO - vcat - Spectral index max(alpha)=4.642858505249023 - min(alpha)=-2.779508113861084 Cutoff -2<alpha<3
Out[4]:
<vcat.plots.multi_fits_image.MultiFitsImage at 0x722994d9a670>
Alignment using masked cross-correlation¶
In [5]:
Copied!
#Load the data again
#This is a U-band image which was already shifted by -5,5 manually
data_shifted=ImageData("../dataset_example/3C111_U_shifted/3C111_2014-05-08_15GHz.fits",
uvf_file="../dataset_example/3C111_U_shifted/3C111_2014-05-08_15GHz.uvf")
#The second dataset is a regular X band image
dataX=ImageData("../dataset_example/3C111_X_2014_05_08/3C111_X_2014_05_08.fits",
uvf_file="../dataset_example/3C111_X_2014_05_08/3C111_X_2014_05_08.uvf")
#We will mask the core region in both images with elliptical masks
e_maj = 4
e_min = 3
e_pa = -10
e_xoffset = -5.32
e_yoffset = 4.28
args_shifted = {
"e_args":[e_maj,e_min,e_pa],
"e_xoffset":e_xoffset,
"e_yoffset":e_yoffset
}
e_maj = 7
e_min = 5
e_pa = -10
e_xoffset = 0
e_yoffset = 0
args_X = {
"e_args":[e_maj,e_min,e_pa],
"e_xoffset":e_xoffset,
"e_yoffset":e_yoffset
}
data_shifted.masking(mask_type="ellipse", args=args_shifted)
dataX.masking(mask_type="ellipse", args=args_X)
#And plot the images with the masks applied to check them
im_cube=ImageCube([data_shifted,dataX])
im_cube.plot(plot_mask=True)
#Load the data again
#This is a U-band image which was already shifted by -5,5 manually
data_shifted=ImageData("../dataset_example/3C111_U_shifted/3C111_2014-05-08_15GHz.fits",
uvf_file="../dataset_example/3C111_U_shifted/3C111_2014-05-08_15GHz.uvf")
#The second dataset is a regular X band image
dataX=ImageData("../dataset_example/3C111_X_2014_05_08/3C111_X_2014_05_08.fits",
uvf_file="../dataset_example/3C111_X_2014_05_08/3C111_X_2014_05_08.uvf")
#We will mask the core region in both images with elliptical masks
e_maj = 4
e_min = 3
e_pa = -10
e_xoffset = -5.32
e_yoffset = 4.28
args_shifted = {
"e_args":[e_maj,e_min,e_pa],
"e_xoffset":e_xoffset,
"e_yoffset":e_yoffset
}
e_maj = 7
e_min = 5
e_pa = -10
e_xoffset = 0
e_yoffset = 0
args_X = {
"e_args":[e_maj,e_min,e_pa],
"e_xoffset":e_xoffset,
"e_yoffset":e_yoffset
}
data_shifted.masking(mask_type="ellipse", args=args_shifted)
dataX.masking(mask_type="ellipse", args=args_X)
#And plot the images with the masks applied to check them
im_cube=ImageCube([data_shifted,dataX])
im_cube.plot(plot_mask=True)
Out[5]:
<vcat.plots.multi_fits_image.MultiFitsImage at 0x722994a70b50>
In [6]:
Copied!
#We will now try to align the shifted image to the second (unshifted) dataset using the masks and the same settings
#Let's start the alignment, this will align data_shifted -> dataX
data_aligned=data_shifted.align(dataX,method=method,auto_regrid=auto_regrid,useDIFMAP=useDIFMAP)
#And plot the resulting image which is shifted and convolved with the common beam
data_aligned.plot()
#We will now try to align the shifted image to the second (unshifted) dataset using the masks and the same settings
#Let's start the alignment, this will align data_shifted -> dataX
data_aligned=data_shifted.align(dataX,method=method,auto_regrid=auto_regrid,useDIFMAP=useDIFMAP)
#And plot the resulting image which is shifted and convolved with the common beam
data_aligned.plot()
2025-10-08 12:01:48,512 - INFO - vcat - Automatically regridding image to minimum pixelsize, smallest FOV and common beam 2025-10-08 12:01:49,391 - INFO - vcat - common beam calculated: [1.7770593660568255, 1.1265451854188613, -10.920121192749493] 2025-10-08 12:02:00,801 - INFO - vcat - will apply shift (x,y): [4.68000001106235 : -5.16000001219695] mas
Out[6]:
<vcat.plots.fits_image.FitsImage at 0x722988be7880>
In [7]:
Copied!
#We regrid and restore the image cube with the same beam, FoV and pixel size to create the spectral index map
im_cube = ImageCube([data_aligned,dataX])
im_cube = im_cube.regrid(useDIFMAP=useDIFMAP) # automatically determines FoV and pixel size
im_cube = im_cube.restore(bmaj=dataX.beam_maj,bmin=dataX.beam_min,posa=dataX.beam_pa,useDIFMAP=useDIFMAP) # restore with larger beam
#Define the Frequencies to use for spectral index (will automatically pick the images closest to that frequency
freq1=8 #GHz
freq2=15 #GHz
#get the spectral index map
spix_map=im_cube.get_spectral_index_map(freq1,freq2,spix_vmin=-2,spix_vmax=3) # resetting colorbar limits
#The spix_map itself is also an ImageCube object and a copy of the higher frequency image with image.spix set to the spectral index map
spix_map.plot(plot_mode="spix",contour=True,xlim=[15,-5],ylim=[-5,10],do_colorbar=True,figsize=(5,4))
#We regrid and restore the image cube with the same beam, FoV and pixel size to create the spectral index map
im_cube = ImageCube([data_aligned,dataX])
im_cube = im_cube.regrid(useDIFMAP=useDIFMAP) # automatically determines FoV and pixel size
im_cube = im_cube.restore(bmaj=dataX.beam_maj,bmin=dataX.beam_min,posa=dataX.beam_pa,useDIFMAP=useDIFMAP) # restore with larger beam
#Define the Frequencies to use for spectral index (will automatically pick the images closest to that frequency
freq1=8 #GHz
freq2=15 #GHz
#get the spectral index map
spix_map=im_cube.get_spectral_index_map(freq1,freq2,spix_vmin=-2,spix_vmax=3) # resetting colorbar limits
#The spix_map itself is also an ImageCube object and a copy of the higher frequency image with image.spix set to the spectral index map
spix_map.plot(plot_mode="spix",contour=True,xlim=[15,-5],ylim=[-5,10],do_colorbar=True,figsize=(5,4))
2025-10-08 12:02:08,674 - INFO - vcat - Regridding images 2025-10-08 12:02:08,675 - INFO - vcat - Determining smallest pixel size and largest FoV from images for regridding Processing: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 2/2 [00:00<00:00, 41734.37it/s] Processing: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 2/2 [00:14<00:00, 7.23s/it] 2025-10-08 12:02:23,140 - INFO - vcat - Restoring images Processing: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 2/2 [00:15<00:00, 7.66s/it] 2025-10-08 12:02:38,455 - INFO - vcat - Image modifications completed. 2025-10-08 12:02:38,531 - INFO - vcat - Spectral index max(alpha)=1.495991587638855 - min(alpha)=-2.748716115951538 Cutoff -2<alpha<3
Out[7]:
<vcat.plots.multi_fits_image.MultiFitsImage at 0x7229891c06d0>
Alignment using the brightest pixel¶
In [8]:
Copied!
#Instead of using a 2d cross correlation, we can also align the images on the brightest pixel
#Let's load the data again from scratch (this is needed because align() also affects the image that it is called on
#This is a U-band image which was already shifted by -5,5 manually
data_shifted=ImageData("../dataset_example/3C111_U_shifted/3C111_2014-05-08_15GHz.fits",
uvf_file="../dataset_example/3C111_U_shifted/3C111_2014-05-08_15GHz.uvf")
#The second dataset is a regular X band image
dataX=ImageData("../dataset_example/3C111_X_2014_05_08/3C111_X_2014_05_08.fits",
uvf_file="../dataset_example/3C111_X_2014_05_08/3C111_X_2014_05_08.uvf")
#This time we will use the 'brightest' method
method="brightest" #align on brightest pixel
#And we will also let the code determine a common pixel grid and common beam
auto_regrid=True
#Let's start the alignment
data_aligned=data_shifted.align(dataX,method=method,auto_regrid=auto_regrid,useDIFMAP=useDIFMAP)
#And plot the resulting image which is shifted and convolved with the common beam
data_aligned.plot()
#Instead of using a 2d cross correlation, we can also align the images on the brightest pixel
#Let's load the data again from scratch (this is needed because align() also affects the image that it is called on
#This is a U-band image which was already shifted by -5,5 manually
data_shifted=ImageData("../dataset_example/3C111_U_shifted/3C111_2014-05-08_15GHz.fits",
uvf_file="../dataset_example/3C111_U_shifted/3C111_2014-05-08_15GHz.uvf")
#The second dataset is a regular X band image
dataX=ImageData("../dataset_example/3C111_X_2014_05_08/3C111_X_2014_05_08.fits",
uvf_file="../dataset_example/3C111_X_2014_05_08/3C111_X_2014_05_08.uvf")
#This time we will use the 'brightest' method
method="brightest" #align on brightest pixel
#And we will also let the code determine a common pixel grid and common beam
auto_regrid=True
#Let's start the alignment
data_aligned=data_shifted.align(dataX,method=method,auto_regrid=auto_regrid,useDIFMAP=useDIFMAP)
#And plot the resulting image which is shifted and convolved with the common beam
data_aligned.plot()
2025-10-08 12:02:49,557 - INFO - vcat - Automatically regridding image to minimum pixelsize, smallest FOV and common beam 2025-10-08 12:02:50,486 - INFO - vcat - common beam calculated: [1.7770593660568255, 1.1265451854188613, -10.920121192749493] 2025-10-08 12:03:01,342 - INFO - vcat - will apply shift (x,y): [5.08000001200785 : -4.68000001106235] mas
Out[8]:
<vcat.plots.fits_image.FitsImage at 0x722994a37c40>
In [9]:
Copied!
#We regrid and restore the image cube with the same beam, FoV and pixel size to create the spectral index map
im_cube = ImageCube([data_aligned,dataX])
im_cube = im_cube.regrid(useDIFMAP=useDIFMAP) # automatically determines FoV and pixel size
im_cube = im_cube.restore(bmaj=dataX.beam_maj,bmin=dataX.beam_min,posa=dataX.beam_pa,useDIFMAP=useDIFMAP) # restore with larger beam
#Define the Frequencies to use for spectral index (will automatically pick the images closest to that frequency
freq1=8 #GHz
freq2=15 #GHz
#get the spectral index map
spix_map=im_cube.get_spectral_index_map(freq1,freq2,spix_vmin=-2,spix_vmax=3) # resetting colorbar limits
#The spix_map itself is also an ImageCube object and a copy of the higher frequency image with image.spix set to the spectral index map
spix_map.plot(plot_mode="spix",contour=True,xlim=[15,-5],ylim=[-5,10],do_colorbar=True,figsize=(5,4))
#We regrid and restore the image cube with the same beam, FoV and pixel size to create the spectral index map
im_cube = ImageCube([data_aligned,dataX])
im_cube = im_cube.regrid(useDIFMAP=useDIFMAP) # automatically determines FoV and pixel size
im_cube = im_cube.restore(bmaj=dataX.beam_maj,bmin=dataX.beam_min,posa=dataX.beam_pa,useDIFMAP=useDIFMAP) # restore with larger beam
#Define the Frequencies to use for spectral index (will automatically pick the images closest to that frequency
freq1=8 #GHz
freq2=15 #GHz
#get the spectral index map
spix_map=im_cube.get_spectral_index_map(freq1,freq2,spix_vmin=-2,spix_vmax=3) # resetting colorbar limits
#The spix_map itself is also an ImageCube object and a copy of the higher frequency image with image.spix set to the spectral index map
spix_map.plot(plot_mode="spix",contour=True,xlim=[15,-5],ylim=[-5,10],do_colorbar=True,figsize=(5,4))
2025-10-08 12:03:08,712 - INFO - vcat - Regridding images 2025-10-08 12:03:08,713 - INFO - vcat - Determining smallest pixel size and largest FoV from images for regridding Processing: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 2/2 [00:00<00:00, 29537.35it/s] Processing: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 2/2 [00:14<00:00, 7.10s/it] 2025-10-08 12:03:22,908 - INFO - vcat - Restoring images Processing: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 2/2 [00:15<00:00, 7.53s/it] 2025-10-08 12:03:37,979 - INFO - vcat - Image modifications completed. 2025-10-08 12:03:38,055 - INFO - vcat - Spectral index max(alpha)=3.6160566806793213 - min(alpha)=-6.260083198547363 Cutoff -2<alpha<3
Out[9]:
<vcat.plots.multi_fits_image.MultiFitsImage at 0x72297d6e9580>
Alignment using modelfit components¶
In [10]:
Copied!
#The third option is to align the images based on modelfit components
#This requires to identify matching components between images first
#First let's load some new data again, this time including modelcomponents
#This is now the regular U-band data
dataU=ImageData("../dataset_example/3C111_U_2014_05_08/3C111_U_2014_05_08.fits",
model="../dataset_example/3C111_U_2014_05_08/3C111_U_2014_05_08.mfit", #can be modelfits .fits or .mod file
stokes_q="../dataset_example/3C111_U_2014_05_08/3C111_U_2014_05_08.qcln", #load polarization as well, we need it later
stokes_u="../dataset_example/3C111_U_2014_05_08/3C111_U_2014_05_08.ucln", #load polarization as well, we need it later
uvf_file="../dataset_example/3C111_U_2014_05_08/3C111_U_2014_05_08.uvf")
#The second dataset is a regular X band image
dataX=ImageData("../dataset_example/3C111_X_2014_05_08/3C111_X_2014_05_08.fits",
model="../dataset_example/3C111_X_2014_05_08/3C111_X_2014_05_08.mfit", #can be modelfits .fits or .mod file
stokes_q="../dataset_example/3C111_X_2014_05_08/3C111_X_2014_05_08.qcln", #load polarization as well, we need it later
stokes_u="../dataset_example/3C111_X_2014_05_08/3C111_X_2014_05_08.ucln", #load polarization as well, we need it later
uvf_file="../dataset_example/3C111_X_2014_05_08/3C111_X_2014_05_08.uvf")
#let's look at the data with modelfits displayed
#Lets'plot both datasets as an ImageCube to have a look
im_cube=ImageCube([dataU,dataX])
im_cube.plot(plot_model=True)
#The third option is to align the images based on modelfit components
#This requires to identify matching components between images first
#First let's load some new data again, this time including modelcomponents
#This is now the regular U-band data
dataU=ImageData("../dataset_example/3C111_U_2014_05_08/3C111_U_2014_05_08.fits",
model="../dataset_example/3C111_U_2014_05_08/3C111_U_2014_05_08.mfit", #can be modelfits .fits or .mod file
stokes_q="../dataset_example/3C111_U_2014_05_08/3C111_U_2014_05_08.qcln", #load polarization as well, we need it later
stokes_u="../dataset_example/3C111_U_2014_05_08/3C111_U_2014_05_08.ucln", #load polarization as well, we need it later
uvf_file="../dataset_example/3C111_U_2014_05_08/3C111_U_2014_05_08.uvf")
#The second dataset is a regular X band image
dataX=ImageData("../dataset_example/3C111_X_2014_05_08/3C111_X_2014_05_08.fits",
model="../dataset_example/3C111_X_2014_05_08/3C111_X_2014_05_08.mfit", #can be modelfits .fits or .mod file
stokes_q="../dataset_example/3C111_X_2014_05_08/3C111_X_2014_05_08.qcln", #load polarization as well, we need it later
stokes_u="../dataset_example/3C111_X_2014_05_08/3C111_X_2014_05_08.ucln", #load polarization as well, we need it later
uvf_file="../dataset_example/3C111_X_2014_05_08/3C111_X_2014_05_08.uvf")
#let's look at the data with modelfits displayed
#Lets'plot both datasets as an ImageCube to have a look
im_cube=ImageCube([dataU,dataX])
im_cube.plot(plot_model=True)
Out[10]:
<vcat.plots.multi_fits_image.MultiFitsImage at 0x722994a37370>
In [11]:
Copied!
#At the next stage, we need to identify model components with each other
#Let's say we want to identify the two largest components with id 1
#We can do this as follows:
import numpy as np
#get the components
comp1=dataU.components
comps2=dataX.components
#find largest component in both images
majs1=[]
for comp in comp1:
majs1.append(comp.maj)
majs2=[]
for comp in comps2:
majs2.append(comp.maj)
argmax1=np.argmax(majs1)
argmax2=np.argmax(majs2)
#assign the largest component the component number 42
dataU.components[argmax1].component_number=42
dataX.components[argmax2].component_number=42
#Now we can use this component to align both images
#Let's start the alignment
method="modelcomp"
comp_ids=42 #you can also use a list of comp_ids, if you have assigned multiple components, then the final shift will be the mean from all component shifts
#do the alignment, no regridding or beam convolution is needed, in principle
dataX_aligned=dataX.align(dataU,method=method,comp_ids=comp_ids,useDIFMAP=useDIFMAP)
#Let's plot the images again and zoom in close to the largest component to check the alignment
im_cube=ImageCube([dataU,dataX_aligned])
im_cube.plot(xlim=[10,5],ylim=[0,5],plot_model=True,plot_beam=False,title="")
#the component is now at the same position as the other component, we can also print their coordinates
print(dataU.components[argmax1].x,dataU.components[argmax1].y)
print(dataX_aligned.components[argmax2].x,dataX_aligned.components[argmax2].y)
#At the next stage, we need to identify model components with each other
#Let's say we want to identify the two largest components with id 1
#We can do this as follows:
import numpy as np
#get the components
comp1=dataU.components
comps2=dataX.components
#find largest component in both images
majs1=[]
for comp in comp1:
majs1.append(comp.maj)
majs2=[]
for comp in comps2:
majs2.append(comp.maj)
argmax1=np.argmax(majs1)
argmax2=np.argmax(majs2)
#assign the largest component the component number 42
dataU.components[argmax1].component_number=42
dataX.components[argmax2].component_number=42
#Now we can use this component to align both images
#Let's start the alignment
method="modelcomp"
comp_ids=42 #you can also use a list of comp_ids, if you have assigned multiple components, then the final shift will be the mean from all component shifts
#do the alignment, no regridding or beam convolution is needed, in principle
dataX_aligned=dataX.align(dataU,method=method,comp_ids=comp_ids,useDIFMAP=useDIFMAP)
#Let's plot the images again and zoom in close to the largest component to check the alignment
im_cube=ImageCube([dataU,dataX_aligned])
im_cube.plot(xlim=[10,5],ylim=[0,5],plot_model=True,plot_beam=False,title="")
#the component is now at the same position as the other component, we can also print their coordinates
print(dataU.components[argmax1].x,dataU.components[argmax1].y)
print(dataX_aligned.components[argmax2].x,dataX_aligned.components[argmax2].y)
2025-10-08 12:04:52,004 - INFO - vcat - will apply shift (x,y): [-5.002273974241688 : -2.2774519038648577] mas
2.2011784039932536e-06 1.0505120826564962e-06 2.201172947025043e-06 1.0505244745218079e-06
In [12]:
Copied!
#Now that the alignment has been carried out succesfully, we can also derive spectral index maps and rotation measure maps
#We will do this on the ImageCube object, assuming the maps were aligned correctly
#Let's first convolve them with a common beam
im_cube = im_cube.restore(useDIFMAP=useDIFMAP)
#Define the Frequencies to use for spectral index (will automatically pick the images closest to that frequency
freq1=8 #GHz
freq2=15 #GHz
#get the spectral index map
spix_map=im_cube.get_spectral_index_map(freq1,freq2)
#The spix_map itself is also an ImageCube object and a copy of the higher frequency image with image.spix set to the spectral index map
spix_map.plot(plot_mode="spix",contour=True,xlim=[15,-5],ylim=[-5,10],do_colorbar=True,figsize=(5,4))
#Now that the alignment has been carried out succesfully, we can also derive spectral index maps and rotation measure maps
#We will do this on the ImageCube object, assuming the maps were aligned correctly
#Let's first convolve them with a common beam
im_cube = im_cube.restore(useDIFMAP=useDIFMAP)
#Define the Frequencies to use for spectral index (will automatically pick the images closest to that frequency
freq1=8 #GHz
freq2=15 #GHz
#get the spectral index map
spix_map=im_cube.get_spectral_index_map(freq1,freq2)
#The spix_map itself is also an ImageCube object and a copy of the higher frequency image with image.spix set to the spectral index map
spix_map.plot(plot_mode="spix",contour=True,xlim=[15,-5],ylim=[-5,10],do_colorbar=True,figsize=(5,4))
2025-10-08 12:05:34,859 - INFO - vcat - Determining common beam... 2025-10-08 12:05:35,675 - INFO - vcat - common beam calculated: [1.7770593660568261, 1.1265451854188584, -10.920121192749422] 2025-10-08 12:05:35,676 - INFO - vcat - Restoring images Processing: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 2/2 [01:18<00:00, 39.45s/it] 2025-10-08 12:06:54,570 - INFO - vcat - Image modifications completed. 2025-10-08 12:06:54,586 - INFO - vcat - Spectral index max(alpha)=6.7717180252075195 - min(alpha)=-12.206295013427734 Cutoff -3<alpha<5
Out[12]:
<vcat.plots.multi_fits_image.MultiFitsImage at 0x7229716c7160>
Polarization maps and Rotation Measure¶
In [13]:
Copied!
#In a similar fashion we can also derive rotation measure maps, since we loaded polarization information earlier
#Let's look at the polarization again
im_cube.plot(plot_mode="lin_pol",plot_evpa=True,xlim=[15,-5],ylim=[-5,10])
#and now we derive the rotation measure map
rm_map=im_cube.get_rm_map(freq1,freq2,sigma_lim_pol=3,sigma_lim=3)
#plot it:
rm_map.plot(plot_mode="rm",contour=True,xlim=[15,-5],ylim=[-5,10],do_colorbar=True,figsize=(5,4))
#In a similar fashion we can also derive rotation measure maps, since we loaded polarization information earlier
#Let's look at the polarization again
im_cube.plot(plot_mode="lin_pol",plot_evpa=True,xlim=[15,-5],ylim=[-5,10])
#and now we derive the rotation measure map
rm_map=im_cube.get_rm_map(freq1,freq2,sigma_lim_pol=3,sigma_lim=3)
#plot it:
rm_map.plot(plot_mode="rm",contour=True,xlim=[15,-5],ylim=[-5,10],do_colorbar=True,figsize=(5,4))
Out[13]:
<vcat.plots.multi_fits_image.MultiFitsImage at 0x72295a362700>
In [ ]:
Copied!