source: trunk/beamsgenerator.py

Last change on this file was 67, checked in by JohnLightfoot, 10 years ago

added clean beams

File size: 8.4 KB
RevLine 
[17]1from __future__ import absolute_import
2
3import collections
4import numpy as np
[32]5import pp
[17]6
[32]7import common.commonobjects as co
[17]8
[43]9def calculate_dirty_beam(npix, rpix, mult, bmax, bxbylist, wnmax, wn):
[32]10 lamb = 1.0 / (wn * 100.0)
[43]11 lambda_min = 1.0 / (wnmax * 100.0)
[32]12 maxbx = bmax / lambda_min
[17]13
[32]14 uv = numpy.zeros([mult*npix,mult*npix])
[17]15
[32]16 for bxby in bxbylist:
17 ibx = int(round(((bxby[0] / lamb) / maxbx) * mult * rpix))
18 if ibx < 0:
19 ibx = mult*npix + ibx
20 iby = int(round(((bxby[1] / lamb) / maxbx) * mult * rpix))
21 if iby < 0:
22 iby = mult*npix + iby
23 uv[ibx,iby] = 1.0
24
25 temp = numpy.fft.fftshift(uv)
26 temp = numpy.fft.fft2(temp)
27 temp = numpy.fft.fftshift(temp)
28 temp = numpy.power(temp, 2)
29 # convert to real, should be real anway as FT of symmetric function
30 dirty_beam = numpy.abs(temp)
31 # and normalise
32 dirty_beam /= numpy.max(dirty_beam)
[17]33
[32]34 # truncate dirty beam pattern
35 dirty_beam = dirty_beam[
36 (mult-1)*rpix:(mult+1)*rpix, (mult-1)*rpix:(mult+1)*rpix]
[17]37
[67]38 # fit Gaussian to dirty beam
39 shape = numpy.shape(dirty_beam)
40 fitter = fitgaussian.FitGaussian()
41 dirty_centre = numpy.array(dirty_beam[shape[0]/2-5:shape[0]/2+5,
42 shape[1]/2-5:shape[1]/2+5])
43 p = fitter.fitgaussian(dirty_centre)
[17]44
[67]45 # clean beam
46 cp = (1.0, float(shape[0])/2.0, float(shape[1])/2.0, p[3], p[4], p[5])
47 rotgauss = fitter.gaussian(*cp)
48 bmax = numpy.max(dirty_beam)
49 clean_beam = numpy.fromfunction(rotgauss, numpy.shape(dirty_beam))
50
51 return dirty_beam, clean_beam
52
[32]53def calculate_primary_beam(npix, rpix, mult, mx, my, bmax, m1_diameter,
[43]54 wnmax, wn):
[32]55 # assume uniform illumination of primary. uv goes out to
56 # +- bmax / lambda_min, fill uv plane appropriate to primary
57 # size
58 mirror = numpy.ones([mult*npix,mult*npix])
[43]59 lambda_min = 1.0 / (wnmax * 100.0)
[32]60 lamb = 1.0 / (wn * 100.0)
61 mirror[(numpy.sqrt(mx*mx + my*my) / (mult*rpix)) *
62 (bmax / lambda_min) > (0.5 * m1_diameter / lamb)] = 0.0
63
64 # fftshift mirror to be centred at [0,0],
65 # do a 2d ftt of it,
66 # fftshift transform so that 0 freq is in centre of array
67 temp = numpy.fft.fftshift(mirror)
68 temp = numpy.fft.fft2(temp)
69 temp = numpy.fft.fftshift(temp)
70 # convert to real, should be real anyway as FT of symmetric function
71 # square to give intensity response
72 primary_amplitude_beam = numpy.real(temp)
73 primary_intensity_beam = numpy.power(numpy.abs(temp), 2)
74 # and normalise
75 primary_amplitude_beam /= numpy.max(primary_amplitude_beam)
76 primary_intensity_beam /= numpy.max(primary_intensity_beam)
77
78 # truncate primary beam pattern
79 primary_amplitude_beam = primary_amplitude_beam[
80 (mult-1)*rpix:(mult+1)*rpix,
81 (mult-1)*rpix:(mult+1)*rpix]
82 primary_intensity_beam = primary_intensity_beam[
83 (mult-1)*rpix:(mult+1)*rpix,
84 (mult-1)*rpix:(mult+1)*rpix]
85
86 return primary_intensity_beam, primary_amplitude_beam
87
88
[17]89class BeamsGenerator(object):
90 """Class to generate the primary beam(s) of the simulated observation.
91 """
92
93 def __init__(self, previous_results):
94 self.previous_results = previous_results
95 self.result = collections.OrderedDict()
96
[32]97 # start parallel python (pp), find the number of CPUS available
98 ppservers = ()
99 self.job_server = pp.Server(ppservers=ppservers)
100 print 'Sampler starting pp with %s workers' % \
101 self.job_server.get_ncpus()
102
[17]103 def run(self):
104 print 'BeamsGenerator.run'
105
106 uvmapgen = self.previous_results['uvmapgenerator']
107 fts = self.previous_results['fts']
108 telescope = self.previous_results['loadparameters']['substages']\
109 ['Telescope']
110
111 # number of mirror positions sampled by FTS
112 nsample = fts['ftsnsample']
113 fts_wn = fts['fts_wn']
[43]114 fts_wn_truncated = fts['fts_wn_truncated']
[17]115
116 # max pixel size (radians) that will fully sample the image.
[43]117 # Sampling freq is 2 * Nyquist freq.
118 # So sampling freq = 2 * b_max / lambda_min.
[17]119 bmax = uvmapgen['bmax']
120 self.result['max baseline [m]'] = bmax
[43]121
122 lambda_min = 1.0 / (np.max(fts_wn) * 100.0)
123 max_pixsize = lambda_min / (2.0 * bmax)
[17]124 self.result['max pixsize [rad]'] = max_pixsize
125
126 # Number of pixels per beam
127 # radius of first null of Airy disk is theta=1.22lambda/d. Number of
128 # pixels is beam diameter/max_pixsize. Calculate this for the
[43]129 # longest wavelength that has flux (at wnmin), largest beam
130
131 max_beam_radius = 1.22 * (1.0 / (np.min(fts_wn_truncated) * 100.0)) /\
[17]132 telescope['Primary mirror diameter']
133 self.result['beam diam [rad]'] = 2.0 * max_beam_radius
134
135 # go out to radius of first null
136 rpix = int(max_beam_radius / max_pixsize)
137 npix = 2 * rpix
138 self.result['npix'] = npix
139
140 # spatial axes same for all wavelengths
141 axis = np.arange(-rpix, rpix, dtype=np.float)
142 axis *= max_pixsize
143 axis *= 206264.806247
144 self.result['spatial axis [arcsec]'] = axis
[32]145 axis1 = co.Axis(data=-axis, title='RA offset', units='arcsec')
146 axis2 = co.Axis(data=axis, title='Dec offset', units='arcsec')
[43]147 axis3 = co.Axis(data=fts_wn_truncated, title='Frequency', units='cm-1')
[17]148
[43]149 # calculate beams for each point on fts_wn_truncated
[17]150 # oversample uv grid by mult to minimise aliasing
151 mult = 5
152 mx,my = np.meshgrid(np.arange(-mult*rpix, mult*rpix),
153 np.arange(-mult*rpix, mult*rpix))
154 self.result['primary beam'] = collections.OrderedDict()
155 self.result['primary amplitude beam'] = collections.OrderedDict()
156 self.result['dirty beam'] = collections.OrderedDict()
[67]157 self.result['clean beam'] = collections.OrderedDict()
[17]158
[43]159 wnmax = np.max(fts_wn_truncated)
[32]160 m1_diameter = telescope['Primary mirror diameter']
161
162 # first calculate primary beam for each wn
163 jobs = {}
[43]164 for wn in fts_wn_truncated:
[32]165 # submit jobs
[43]166 indata = (npix, rpix, mult, mx, my, bmax, m1_diameter, wnmax,
[32]167 wn,)
168 jobs[wn] = self.job_server.submit(calculate_primary_beam,
169 indata, (), ('numpy',))
[17]170
[43]171 primary_amplitude_beam = np.zeros([npix,npix,len(fts_wn_truncated)],
172 np.complex)
173 for iwn,wn in enumerate(fts_wn_truncated):
[32]174 # collect and store results
175 primary_intensity_beam, primary_amplitude_beam[:,:,iwn] = jobs[wn]()
176 image = co.Image(data=primary_intensity_beam, axes=[axis1, axis2],
[17]177 title='Primary Beam %06.4g cm-1' % wn)
178 self.result['primary beam'][wn] = image
179
[32]180 cube = co.Cube(data=primary_amplitude_beam, axes=[axis1, axis2, axis3],
181 title='Amplitude Primary Beam %06.4g cm-1' % wn)
182 self.result['primary amplitude beam'] = cube
[17]183
[32]184 # second calculate dirty beam, same use of pp
[43]185 for wn in fts_wn_truncated:
[17]186 #
187 # 1. uv plane covered by discrete points so should use a
188 # slow dft rather than fft, which requires evenly spaced
189 # samples. Perhaps a bit slow though as calcs done in Python
190 # layer.
191 # 2. Could use shift theorem to give even-spaced equivalent
192 # of each discrete point, then use ffts. Equivalent to 1
193 # in speed.
194 # 3. Oversample uv plane and accept small error in point
195 # positions. Fastest but adequate?
196
[43]197 indata = (npix, rpix, mult, bmax, uvmapgen['bxby'], wnmax, wn,)
[32]198 jobs[wn] = self.job_server.submit(calculate_dirty_beam,
[67]199 indata, (), ('numpy','fitgaussian',))
[17]200
201
[32]202 # axes for dirty beam
203 axis = np.arange(-rpix, rpix, dtype=np.float)
[43]204 axis *= (lambda_min / bmax)
[32]205 axis *= 206264.806247
206 axis1 = co.Axis(data=-axis, title='RA offset', units='arcsec')
207 axis2 = co.Axis(data=axis, title='Dec offset', units='arcsec')
208
[43]209 for wn in fts_wn_truncated:
[67]210 dirty_beam,clean_beam = jobs[wn]()
[32]211 image = co.Image(data=dirty_beam, axes=[axis1, axis2],
[17]212 title='Dirty Beam %06.4g cm-1' % wn)
213 self.result['dirty beam'][wn] = image
[67]214 image = co.Image(data=clean_beam, axes=[axis1, axis2],
215 title='Clean Beam %06.4g cm-1' % wn)
216 self.result['clean beam'][wn] = image
[17]217
218 return self.result
219
220 def __repr__(self):
221 return 'PrimaryBeamGenerator'
222
Note: See TracBrowser for help on using the repository browser.