def run(self): print 'Calculating synthesis dirty beam and clean beam...' # gather configuration uvmapgen = self.previous_results['uvmapgenerator'] cubeparams = self.previous_results['cubeparameters'] self.result['wn'] = wn = cubeparams['wn'] self.result['pixsize [rad]'] = pixsize = cubeparams['pixsize [rad]'] self.result['npix'] = npix = cubeparams['npix'] # spatial axes same for all wavelengths self.result['spatial axis [arcsec]'] = axis = \ cubeparams['spatial axis [arcsec]'] axis1 = co.Axis(data=-axis, title='RA offset', units='arcsec') axis2 = co.Axis(data=axis, title='Dec offset', units='arcsec') axis3 = co.Axis(data=wn, title='Frequency', units='cm-1') # calculate beam for each wavenumber in 'wn' rpix = npix / 2 mx, my = np.meshgrid(np.arange(-rpix, rpix), np.arange(-rpix, rpix)) self.result['dirty beam'] = collections.OrderedDict() self.result['clean beam'] = collections.OrderedDict() # calculate synthesis beams using pp jobs = {} wnmax = np.max(wn) for wavenum in wn: # # Submit pp calls to calculate_synthesis_beams to calculate # the results. indata = ( npix, pixsize, wavenum, uvmapgen['bxby'], ) jobs[wavenum] = self.job_server.submit(calculate_synthesis_beams, indata, (), ( 'numpy', 'fitgaussian', )) for wavenum in wn: if jobs[wavenum]() is None: raise Exception, 'calculate_synthesis_beams has failed' dirty_beam, clean_beam = jobs[wavenum]() image = co.Image(data=dirty_beam, axes=[axis1, axis2], title='Dirty Beam %06.4g cm-1' % wavenum) self.result['dirty beam'][wavenum] = image image = co.Image(data=clean_beam, axes=[axis1, axis2], title='Clean Beam %06.4g cm-1' % wavenum) self.result['clean beam'][wavenum] = image return self.result
def _create_point_source(self, xpos, ypos, skymodel, spatial_axis, frequency_axis, spectrum_function): """Create a point source. """ # calculate xpos, ypos in units of pixel - numpy arrays [row,col] nx = len(spatial_axis) colpos = float(nx - 1) * float(xpos - spatial_axis[0]) / ( spatial_axis[-1] - spatial_axis[0]) rowpos = float(nx - 1) * float(ypos - spatial_axis[0]) / ( spatial_axis[-1] - spatial_axis[0]) if colpos < 0 or colpos > (nx - 1) or rowpos < 0 or rowpos > (nx - 1): # point source is outside modelled area return # calculate fourier phase shift to move point at [0,0] to # [rowpos, colpos] shiftx = np.zeros([nx], np.complex) shiftx[:nx / 2] = np.arange(nx / 2, dtype=np.complex) shiftx[nx / 2:] = np.arange(-nx / 2, 0, dtype=np.complex) shiftx = np.exp((-2.0j * np.pi * colpos * shiftx) / float(nx)) shifty = np.zeros([nx], np.complex) shifty[:nx / 2] = np.arange(nx / 2, dtype=np.complex) shifty[nx / 2:] = np.arange(-nx / 2, 0, dtype=np.complex) shifty = np.exp((-2.0j * np.pi * rowpos * shifty) / float(nx)) shift = np.ones([nx, nx], np.complex) for j in range(nx): shift[j, :] *= shiftx for i in range(nx): shift[:, i] *= shifty # calculate spectrum spectrum = spectrum_function.calculate() # go through freq planes and add point source to each for iwn, wn in enumerate(frequency_axis): # create point in frequency space temp = np.zeros([nx, nx]) temp[0, 0] = spectrum[iwn] # 2d fft temp = np.fft.fft2(temp) # apply phase shift to move point to required offset temp *= shift # transform back temp = np.fft.ifft2(temp) # add to sky model skymodel[:, :, iwn] += temp # return spectrum axis = co.Axis(data=frequency_axis, title='wavenumber', units='cm-1') spectrum = co.Spectrum(data=spectrum, axis=axis, title='Source spectrum', units='W sr-1 m-2 Hz-1') return spectrum
def _create_gaussian_source(self, xpos, ypos, xwidth, ywidth, skymodel, spatial_axis, frequency_axis, spectrum_function): """Create a point source. """ # calculate spectrum spectrum = spectrum_function.calculate() # calculate Gaussian profile - a cunning use of array slicing found # on the web x = spatial_axis y = x[:, np.newaxis] profile = np.exp(-4 * np.log(2) * ((x - xpos)**2 / xwidth**2 + (y - ypos)**2 / ywidth**2)) # apodise profile[((x - xpos)**2 + (y - ypos)**2) > 16] = 0.0 # go through freq planes and add source to each for iwn, wn in enumerate(frequency_axis): # add to sky model skymodel[:, :, iwn] += profile * spectrum[iwn] # return spectrum axis = co.Axis(data=frequency_axis, title='wavenumber', units='cm-1') spectrum = co.Spectrum(data=spectrum, axis=axis, title='Source spectrum', units='W sr-1 m-2 Hz-1') return spectrum
def run(self): # print 'ReduceInterferogram.run' # namedtuple to hold UVspectrum result UVspectrum = collections.namedtuple('uvspectrum', [ 'scan_number', 'baseline_x', 'baseline_y', 'baseline_z', 'interferogram', 'opd', 'spectrum', 'wavenumber' ], verbose=False) # get observation list observe = self.previous_results['observe'] obs_timeline = observe['observed_timeline'] # Construct a dict holding lists of configurations for each # baseline observed baseline_configs = collections.defaultdict(list) observed_times = obs_timeline.keys() observed_times.sort() for t in observed_times: config = obs_timeline[t] b = (config.baseline_x, config.baseline_y, config.baseline_z) baseline_configs[b].append(config) baselines = baseline_configs.keys() baselines.sort() # for each baseline assemble config data into arrays baseline_scans = {} baseline_mean = {} baseline_uvspectrum = {} for b in baselines: data = [] smec_position = [] flag = [] for config in baseline_configs[b]: data.append(config.data) smec_position.append(config.smec_position) flag.append(config.flag) data = np.array(data) smec_position = np.array(smec_position) flag = np.array(flag) # identify scans within the data flow flag_sections = find_flagged_sections(flag) # extract scans from the data stream scans = [] mark = 0 for flag_section in flag_sections: interferogram = data[mark:flag_section[0]] opd = 100.0 * smec_position[mark:flag_section[0]] / \ self.smec_opd_to_mpd if len(interferogram): axis = co.Axis(data=opd, title='OPD', units='cm') scan = co.Spectrum(data=interferogram, flag=np.zeros(np.shape(interferogram), np.bool), axis=axis, title='scan interferogram', units='') scans.append(scan) mark = flag_section[1] + 1 # deal with last scan interferogram = data[mark:] opd = 100.0 * smec_position[mark:] / self.smec_opd_to_mpd if len(interferogram): axis = co.Axis(data=opd, title='OPD', units='cm') scan = co.Spectrum(data=interferogram, flag=np.zeros(np.shape(interferogram), np.bool), axis=axis, title='scan interferogram', units='') scans.append(scan) baseline_scans[b] = scans # now obtain mean of scans for each baseline interferogram_sum = np.zeros(np.shape(scans[0].data)) interferogram_n = np.zeros(np.shape(scans[0].data)) for scan in scans: # assume opd ranges are the same but sort them into # ascending order opd = scan.axis.data opd_sort = np.argsort(opd) interferogram_sum += scan.data[opd_sort] interferogram_n += 1.0 interferogram_mean = interferogram_sum / interferogram_n axis = co.Axis(data=opd[opd_sort], title='OPD', units='cm') scan_mean = co.Spectrum(data=interferogram_mean, flag=np.zeros(np.shape(interferogram_mean), np.bool), axis=axis, title='mean interferogram', units='') baseline_mean[b] = scan_mean # now convert interferogram to spectrum at this u-v position # first, shift interferogram so that 0 opd is at index 0 # must ask David N how to do this properly postzero = [] prezero = [] postzero_opd = [] prezero_opd = [] zero_opd_found = False for i, opd in enumerate(scan_mean.axis.data): if abs(opd) < 1e-10: zero_opd_found = True if not zero_opd_found: prezero.append(scan_mean.data[i]) prezero_opd.append(scan_mean.axis.data[i]) else: postzero.append(scan_mean.data[i]) postzero_opd.append(scan_mean.axis.data[i]) shifted = postzero + prezero shifted_opd = postzero_opd + prezero_opd shifted = np.array(shifted) shifted_opd = np.array(shifted_opd) # spectrum is complex spectrum = np.fft.fft(shifted) wavenumber = np.fft.fftfreq(n=spectrum.size, d=abs(shifted_opd[1] - shifted_opd[0])) # save it uvspectrum = UVspectrum(None, b[0], b[1], 0.0, shifted, shifted_opd, spectrum, wavenumber) baseline_uvspectrum[b] = uvspectrum self.result['baseline_scans'] = baseline_scans self.result['baseline_mean'] = baseline_mean self.result['baseline_uvspectrum'] = baseline_uvspectrum return self.result
def run(self): print 'DoubleFourier.run' # convert lengths to m. Wavenumbers leave as cm-1. fts = self.previous_results['fts'] fts_wn = fts['fts_wn'] fts_wn_truncated = fts['fts_wn_truncated'] opd_max = fts['opd_max'] / 100.0 fts_nsample = fts['ftsnsample'] vdrive = fts['vdrive'] / 100.0 delta_opd = fts['delta_opd'] delta_time = delta_opd / vdrive times = np.arange(int(fts_nsample), dtype=np.float) times *= delta_time opd_start = -((fts_nsample + 1) / 2) * delta_opd beamsgenerator = self.previous_results['beamsgenerator'] uvmapgenerator = self.previous_results['uvmapgenerator'] bxby = uvmapgenerator['bxby'] skygenerator = self.previous_results['skygenerator'] skymodel = skygenerator['sky model'] spatial_axis = self.result['spatial axis'] = skygenerator[ 'spatial axis'] self.result['frequency axis'] = fts_wn_truncated # assuming nx is even then transform has 0 freq at origin and [nx/2] is # Nyquist frequency. Nyq freq = 0.5 * Nyquist sampling freq. # Assume further that the fft is shifted so that 0 freq is at nx/2 nx = len(spatial_axis) spatial_freq_axis = np.arange(-nx / 2, nx / 2, dtype=np.float) sample_freq = (180.0 * 3600.0 / np.pi) / (spatial_axis[1] - spatial_axis[0]) spatial_freq_axis *= (sample_freq / nx) self.result['spatial frequency axis'] = spatial_freq_axis self.result['baseline interferograms'] = collections.OrderedDict() # for baseline in bxby: for baseline in bxby[:1]: print 'baseline', baseline measurement = np.zeros(np.shape(times)) opd = np.zeros(np.shape(times)) # FTS path diff and possibly baseline itself vary with time for tindex, t in enumerate(times): # calculate the sky that the system is observing at this # moment, incorporating various errors in turn. Be explicit # about the copy otherwise a reference is made instead. sky_now = skymodel.copy() # 1. baseline should be perp to centre of field. # If baseline is tilted then origin of sky map shifts. # (I think effect could be corrected by changing # FTS sample position to compensate.?) # for now assume 0 error but do full calculation for timing # purposes # perfect baseline is perpendicular to direction to 'centre' # on sky. Add errors in x,y,z - z towards sky 'centre' bz = 0.0 # convert bz to angular error blength = math.sqrt(baseline[0] * baseline[0] + baseline[1] * baseline[1]) bangle = (180.0 * 3600.0 / math.pi) * bz / blength bx_error = bangle * baseline[0] / blength by_error = bangle * baseline[1] / blength # calculate xpos, ypos in units of pixel - numpy arrays # [row,col] nx = len(spatial_axis) colpos = float(nx-1) * bx_error / \ (spatial_axis[-1] - spatial_axis[0]) rowpos = float(nx-1) * by_error / \ (spatial_axis[-1] - spatial_axis[0]) # calculate fourier phase shift to move point at [0,0] to # [rowpos, colpos] shiftx = np.zeros([nx], np.complex) shiftx[:nx / 2] = np.arange(nx / 2, dtype=np.complex) shiftx[nx / 2:] = np.arange(-nx / 2, 0, dtype=np.complex) shiftx = np.exp((-2.0j * np.pi * colpos * shiftx) / float(nx)) shifty = np.zeros([nx], np.complex) shifty[:nx / 2] = np.arange(nx / 2, dtype=np.complex) shifty[nx / 2:] = np.arange(-nx / 2, 0, dtype=np.complex) shifty = np.exp((-2.0j * np.pi * rowpos * shifty) / float(nx)) shift = np.ones([nx, nx], np.complex) for j in range(nx): shift[j, :] *= shiftx for i in range(nx): shift[:, i] *= shifty jobs = {} # go through freq planes and shift them for iwn, wn in enumerate(fts_wn_truncated): # submit jobs indata = ( sky_now[:, :, iwn], shift, ) jobs[wn] = self.job_server.submit(fftshift, indata, (), ('numpy', )) for iwn, wn in enumerate(fts_wn_truncated): # collect and store results temp = jobs[wn]() sky_now[:, :, iwn] = temp if t == times[0]: # take copy of array to fix a snapshot of it now self.result['sky at time 0'] = sky_now.copy() # 2. telescopes should be centred on centre of field # Telescopes collect flux from the 'sky' and pass # it to the FTS beam combiner. In doing this each # telescope multiplies the sky emission by its # amplitude beam response - always real but with # negative areas. Is this correct? Gives right # answer for 'no error' case. # multiply sky by amplitude beam 1 * amplitude beam 2 amp_beam_1 = beamsgenerator['primary amplitude beam'].data amp_beam_2 = beamsgenerator['primary amplitude beam'].data for iwn, wn in enumerate(fts_wn): # calculate shifted beams here # for now assume no errors and just use beam # calculated earlier pass # multiply sky by amplitude beams of 2 antennas sky_now *= amp_beam_1 * amp_beam_2 if t == times[0]: # take copy of array to fix a snapshot of it now self.result['sky*beams at time 0'] = sky_now.copy() # 3. baseline error revisited # derive baseline at this time # # Perhaps baseline should be a continuous function in # time, which would allow baselines that intentionally # smoothly vary (as in rotating tethered assembly) # and errors to be handled by one description. # # what follows assumes zero error baseline_error = 0.0 baseline_now = baseline + baseline_error fft_now = np.zeros(np.shape(sky_now), np.complex) spectrum = np.zeros(np.shape(fts_wn), np.complex) for iwn, wn in enumerate(fts_wn_truncated): # derive shift needed to place baseline at one of FFT coords # this depends on physical baseline and frequency baseline_now_lambdas = baseline_now * wn * 100.0 # calculate baseline position in units of pixels of FFTed # sky - numpy arrays [row,col] colpos = float(nx-1) * \ float(baseline_now_lambdas[0] - spatial_freq_axis[0]) / \ (spatial_freq_axis[-1] - spatial_freq_axis[0]) rowpos = float(nx-1) * \ float(baseline_now_lambdas[1] - spatial_freq_axis[0]) / \ (spatial_freq_axis[-1] - spatial_freq_axis[0]) colpos = 0.0 rowpos = 0.0 # calculate fourier phase shift to move point at [rowpos,colpos] to # [0,0] shiftx = np.zeros([nx], np.complex) shiftx[:nx / 2] = np.arange(nx / 2, dtype=np.complex) shiftx[nx / 2:] = np.arange(-nx / 2, 0, dtype=np.complex) shiftx = np.exp((-2.0j * np.pi * colpos * shiftx) / \ float(nx)) shifty = np.zeros([nx], np.complex) shifty[:nx / 2] = np.arange(nx / 2, dtype=np.complex) shifty[nx / 2:] = np.arange(-nx / 2, 0, dtype=np.complex) shifty = np.exp( (-2.0j * np.pi * rowpos * shifty) / float(nx)) shift = np.ones([nx, nx], np.complex) for j in range(nx): shift[j, :] *= shiftx for i in range(nx): shift[:, i] *= shifty # move centre of sky image to origin temp = np.fft.fftshift(sky_now[:, :, iwn]) # apply phase shift temp *= shift # 2d fft temp = np.fft.fft2(temp) fft_now[:, :, iwn] = temp # set amp/phase at this frequency spectrum[wn == fts_wn] = temp[0, 0] if t == times[0]: self.result['skyfft at time 0'] = fft_now.copy() axis = co.Axis(data=fts_wn, title='wavenumber', units='cm-1') temp = co.Spectrum(data=spectrum, axis=axis, title='Detected spectrum', units='W sr-1 m-2 Hz-1') self.result['skyfft spectrum at time 0'] = temp # 3. FTS sampling should be accurate # derive lag due to FTS path difference # 0 error for now if t == times[0]: # test interferogram # inverse fft of emission spectrum at this point reflected_spectrum = np.zeros([2 * (len(spectrum) - 1)], np.complex) reflected_spectrum[:len(spectrum)].real = spectrum reflected_spectrum[len(spectrum):].real = spectrum[-2:0:-1] temp = np.fft.ifft(reflected_spectrum) pos = np.fft.fftfreq(len(reflected_spectrum), d=fts_wn[1] - fts_wn[0]) # move 0 frequency to centre of array temp = np.fft.fftshift(temp) pos = np.fft.fftshift(pos) axis = co.Axis(data=pos, title='path difference', units='cm') temp = co.Spectrum(data=temp, axis=axis, title='Detected interferogram', units='') self.result['test FTS at time 0'] = temp mirror_error = 0.0 opd[tindex] = opd_start + (vdrive * t + mirror_error) opd_ipos = opd[tindex] / delta_opd # calculate shift needed to move point at opd to 0 nfreq = len(fts_wn) ndoublefreq = 2 * (nfreq - 1) shift = np.zeros([ndoublefreq], dtype=np.complex) shift[:nfreq] = np.arange(nfreq, dtype=np.complex) shift[nfreq:] = np.arange(nfreq, dtype=np.complex)[-2:0:-1] shift = np.exp((-2.0j * np.pi * opd_ipos * shift) / \ float(ndoublefreq)) # reflect spectrum about 0 to give unaliased version reflected_spectrum = np.zeros([2 * (len(spectrum) - 1)], np.complex) reflected_spectrum[:len(spectrum)].real = spectrum reflected_spectrum[len(spectrum):].real = spectrum[-2:0:-1] # apply phase shift and fft reflected_spectrum *= shift spectrum_fft = np.fft.ifft(reflected_spectrum) measurement[tindex] = spectrum_fft[0] # if np.array_equal(baseline, bxby[0]): # if 'skyfft check' not in self.result.keys(): # self.result['skyfft check'] = {} # temp = co.Spectrum(data=spectrum_fft, # title='Test interferogram', units='') # self.result['skyfft check'][t] = temp axis = co.Axis(data=np.array(opd), title='path difference', units='cm') temp = co.Spectrum(data=measurement, axis=axis, title='Detected interferogram', units='') self.result['baseline interferograms'][tuple(baseline)] = \ temp return self.result
def run(self): print 'Calculating beams' # gather configuration uvmapgen = self.previous_results['uvmapgenerator'] fts = self.previous_results['fts'] telescope = self.previous_results['loadparameters']['substages']\ ['Telescope'] # number of mirror positions sampled by FTS nsample = fts['ftsnsample'] fts_wn = fts['fts_wn'] fts_wn_truncated = fts['fts_wn_truncated'] # max pixel size (radians) that will fully sample the image. # Sampling freq is 2 * Nyquist freq. # So sampling freq = 2 * b_max / lambda_min. bmax = uvmapgen['bmax'] self.result['max baseline [m]'] = bmax lambda_min = 1.0 / (np.max(fts_wn) * 100.0) max_pixsize = lambda_min / (2.0 * bmax) self.result['max pixsize [rad]'] = max_pixsize # Number of pixels per beam # radius of first null of Airy disk is theta=1.22lambda/d. Number of # pixels is beam diameter/max_pixsize. Calculate this for the # longest wavelength that has flux (at wnmin), largest beam max_beam_radius = 1.22 * (1.0 / (np.min(fts_wn_truncated) * 100.0)) /\ telescope['Primary mirror diameter'] self.result['beam diam [rad]'] = 2.0 * max_beam_radius # go out to radius of first null rpix = int(max_beam_radius / max_pixsize) npix = 2 * rpix self.result['npix'] = npix # spatial axes same for all wavelengths axis = np.arange(-rpix, rpix, dtype=np.float) axis *= max_pixsize axis *= 206264.806247 self.result['spatial axis [arcsec]'] = axis axis1 = co.Axis(data=-axis, title='RA offset', units='arcsec') axis2 = co.Axis(data=axis, title='Dec offset', units='arcsec') axis3 = co.Axis(data=fts_wn_truncated, title='Frequency', units='cm-1') # calculate beams for each point on fts_wn_truncated # oversample uv grid by mult to minimise aliasing mult = 5 mx,my = np.meshgrid(np.arange(-mult*rpix, mult*rpix), np.arange(-mult*rpix, mult*rpix)) self.result['primary beam'] = collections.OrderedDict() self.result['primary amplitude beam'] = collections.OrderedDict() self.result['dirty beam'] = collections.OrderedDict() self.result['clean beam'] = collections.OrderedDict() wnmax = np.max(fts_wn_truncated) m1_diameter = telescope['Primary mirror diameter'] # first calculate primary beam for each wn jobs = {} for wn in fts_wn_truncated: # submit jobs indata = (npix, rpix, mult, mx, my, bmax, m1_diameter, wnmax, wn,) jobs[wn] = self.job_server.submit(calculate_primary_beam, indata, (), ('numpy',)) primary_amplitude_beam = np.zeros([npix,npix,len(fts_wn_truncated)], np.complex) for iwn,wn in enumerate(fts_wn_truncated): # collect and store results primary_intensity_beam, primary_amplitude_beam[:,:,iwn] = jobs[wn]() image = co.Image(data=primary_intensity_beam, axes=[axis1, axis2], title='Primary Beam %06.4g cm-1' % wn) self.result['primary beam'][wn] = image cube = co.Cube(data=primary_amplitude_beam, axes=[axis1, axis2, axis3], title='Amplitude Primary Beam %06.4g cm-1' % wn) self.result['primary amplitude beam'] = cube # second calculate dirty beam, same use of pp for wn in fts_wn_truncated: # # 1. uv plane covered by discrete points so should use a # slow dft rather than fft, which requires evenly spaced # samples. Perhaps a bit slow though as calcs done in Python # layer. # 2. Could use shift theorem to give even-spaced equivalent # of each discrete point, then use ffts. Equivalent to 1 # in speed. # 3. Oversample uv plane and accept small error in point # positions. Fastest but adequate? indata = (npix, rpix, mult, bmax, uvmapgen['bxby'], wnmax, wn,) jobs[wn] = self.job_server.submit(calculate_dirty_beam, indata, (), ('numpy','fitgaussian',)) # axes for dirty beam axis = np.arange(-rpix, rpix, dtype=np.float) axis *= (lambda_min / bmax) axis *= 206264.806247 axis1 = co.Axis(data=-axis, title='RA offset', units='arcsec') axis2 = co.Axis(data=axis, title='Dec offset', units='arcsec') for wn in fts_wn_truncated: dirty_beam,clean_beam = jobs[wn]() image = co.Image(data=dirty_beam, axes=[axis1, axis2], title='Dirty Beam %06.4g cm-1' % wn) self.result['dirty beam'][wn] = image image = co.Image(data=clean_beam, axes=[axis1, axis2], title='Clean Beam %06.4g cm-1' % wn) self.result['clean beam'][wn] = image return self.result
def run(self): print 'Calculating primary beams...' # gather configuration cubeparams = self.previous_results['cubeparameters'] self.result['wn'] = wn = cubeparams['wn'] self.result['pixsize [rad]'] = pixsize = cubeparams['pixsize [rad]'] self.result['npix'] = npix = cubeparams['npix'] telescope = self.previous_results['loadparameters']['substages']\ ['Telescope'] m1_diameter = telescope['Primary mirror diameter'] rpix = npix / 2 # spatial axes same for all wavelengths axis = np.arange(-rpix, rpix, dtype=np.float) axis *= pixsize axis = np.rad2deg(axis) * 3600.0 axis1 = co.Axis(data=-axis, title='RA offset', units='arcsec') axis2 = co.Axis(data=axis, title='Dec offset', units='arcsec') axis3 = co.Axis(data=wn, title='Frequency', units='cm-1') # calculate beams for each point on 'wn' jobs = {} for wavenum in wn: # submit jobs indata = ( npix, pixsize, m1_diameter, wavenum, self.nuv, ) jobs[wavenum] = self.job_server.submit(calculate_primary_beam, indata, (), ( 'numpy', 'math', 'zernike', )) # collect results self.result['primary beam'] = collections.OrderedDict() self.result['primary amplitude beam'] = collections.OrderedDict() primary_amplitude_beam = np.zeros([npix, npix, len(wn)], np.complex) for iwn, wavenum in enumerate(wn): if jobs[wavenum]() is None: raise Exception, 'calculate_primary_beams has failed' primary_amplitude_beam[:, :, iwn] = temp = jobs[wavenum]() primary_intensity_beam = (temp * np.conjugate(temp)).real image = co.Image(data=primary_intensity_beam, axes=[axis1, axis2], title='Primary Beam %06.4g cm-1' % wavenum) self.result['primary beam'][wavenum] = image cube = co.Cube(data=primary_amplitude_beam, axes=[axis1, axis2, axis3], title='Amplitude Primary Beam') self.result['primary amplitude beam'] = cube return self.result