def run(nchan, lwidth, inten): """ Method to generate spectra and inject Gaussians Parameters ---------- nchan : int Number of channels in the generated spectrum lwidth : float Width of the generated Gaussians in km/s inten : float The nominal peak intensity of the Gaussians """ # set up the dict to collect the results, key is the number of Gaussians to inject pats = {5: None, 10: None, 15:None, 20:None, 25:None, 30:None, 40:None, 50:None, 60:None, 70:None, 80:None, 90:None, 100:None} nl = pats.keys() nl.sort() # for each of the number of Gaussians for nlines in nl: print " ",nlines # generate frequency axis rms = 1.0 freq = np.arange(nchan, dtype=np.float64) center = int(nchan/2) for i in range(nchan): freq[i] = 100.0 + (float((i - nchan/2)) * 0.0001) # generate spectral and channel axes spec = np.zeros(nchan) chans = np.arange(nchan) # generate noise spec += np.random.normal(0.0, rms, nchan) # inject Gaussians for i in range(nlines): # randomly determine the peak position in channel space peak = int(random.random() * nchan) spec += utils.gaussian1D(freq, inten, freq[peak] + (random.random()/20), utils.veltofreq(lwidth, freq[peak])) # convert to Spectrum object spectrum = Spectrum(spec, freq, chans) # find the segments sfinder = SegmentFinder.SegmentFinder(spectrum=spectrum.spec(), freq=spectrum.freq(), method="ADMIT", minchan=3, maxgap=3, numsigma=4.0, iterate=True, nomean=True) seg, cut, noi, mean = sfinder.find() spectrum.set_noise(noi) # find the peaks args = {"spec" : spectrum.spec(), "y" : spectrum.freq(), "min_width" : 3} args["thresh"] = float(spectrum.noise() * 4.0) pks = getpeaks("PeakFinder", args, spectrum.spec(), seg, iterate=True) # find any patterns pats[nlines] = findpatterns(spectrum, pks, seg) return pats
def convert(self, chan=None, freq=None, velocity=None, spec=None, file=None, separator=None, restfreq=None, vlsr=None): """ Method to convert input data (either files or arrays) into a CubeSpectrum_BDP. If files are used then then the columns containing the frequency and the intensity must be given (channel numbers are optional). Any number of files can be given, but all spectra must have the same length as they are assumed to come from the same data source. Blank lines and lines starting with a comment '#' will be skipped, additionally any line with too few columns will be skipped. If arrays are used an input then both the frequency and intensity must be specified (the channel numbers are optional). Both lists and numpy arrays are accepted as inputs. Multidimmensional arrays are supported with the following parameters: + A single frequency list can be given to cover all input spectra, otherwise the shape of the frequency array must match that of the spectra + A single channel list can be given to cover all input spectra, otherwise the shape of the channel array must match that of the spectra + All spectra must have the same length If a channel array is not specified then one will be constructed with the following parameters: + The channel numbers will start at 0 (casa convention) + The first entry in the spectrum will be considered the first channel, regardless of whether the frequency array increases or decreases. Additionally, if there is velocity axis, but no frequency axis, a frequency axis can be constructed by specifying a rest frequency (restfreq), and vlsr. The convert method will return a single CubeSpectrum_BDP instance holding all input spectra along with an image of each. Parameters ---------- chan : array or int An array holding the channel numbers for the data, multidimmensional arrays are supported. If an integer is specified then it is the number of the column in the file which contains the channel numbers, column numbers are 1 based. Default: None freq : array An array holding the frequencies for the data, multidimmensional arrays are supported. If an integer is specified then it is the number of the column in the file which contains the frequencies, column numbers are 1 based. Default: None velocity : array An array holding the velocity for the data, multidimmensional arrays are supported. If an integer is specified then it is the number of the column in the file which contains the velcoties, column numbers are 1 based. If this parameter is specified then restfreq and vlsr must also be specified. Default: None spec : array An array holding the intesities of the data, multidimmensional arrays are supported. If an integer is specified then it is the number of the column in the file which contains the intensities, column numbers are 1 based. Default: None file : list or str A single file name or a list of file names to be read in for spectra. Default: None separator : str The column separator for reading in the data files. Default: None (any whitespace) restfreq : float The rest frequency to use to convert the spectra from velocity to frequency units. The rest frequency is in GHz. Default: None (no conversion done) vlsr : float The reference velocity for converting a velocity axis to frequency. The units are km/s. If this is not set then it is assumed that the vlsr is 0.0. Default: None Returns ------- CubeSpectrum_BDP instance containing all of the inpur spectra. """ self.restfreq = restfreq self.vlsr = vlsr # if a string was given as the file name then turn it into a list so it can be iterated over if isinstance(file, str): self.file = [file] else: self.file = file # do some error checking if isinstance(chan, np.ndarray) or isinstance(chan, list): if isinstance(chan, list): self.chan = np.array(chan) else: self.chan = copy.deepcopy(chan) self.chancol = -1 elif isinstance(chan, int): self.chancol = chan self.chan = None else: self.chancol = -1 self.chan = None if isinstance(freq, np.ndarray) or isinstance(freq, list): if isinstance(freq, list): self.freq = np.array(freq) else: self.freq = copy.deepcopy(freq) self.freqcol = -1 elif isinstance(freq, int): self.freqcol = freq self.freq = None else: self.freqcol = -1 self.freq = None if isinstance(velocity, np.ndarray) or isinstance(velocity, list): if isinstance(velocity, list): self.freq = np.array(velocity, dtype=np.float) else: self.freq = velocity.astype(np.float) for i, frq in enumerate(self.freq): self.freq[i] = self.restfreq + utils.veltofreq(frq - self.vlsr, self.restfreq) self.freqcol = -1 elif isinstance(velocity, int): self.velcol = velocity self.velocity = None else: self.velcol = -1 self.velocity = None if isinstance(spec, np.ndarray) or isinstance(spec, list): if isinstance(spec, list): self.spec = np.array(spec) else: self.spec = copy.deepcopy(spec) self.speccol = -1 elif isinstance(spec, int): self.speccol = spec self.spec = None else: self.speccol = -1 self.spec = None if isinstance(separator, str): self.separator = separator spectra = [] # read in the data from any files if self.file: for fl in self.file: spectra.append(self.getfile(fl)) else: # convert the input arrays singlefreq = False singlechan = False havechan = False # make sure they have the same shape or that the frequency array is 1D if self.spec.shape != self.freq.shape: if len(self.spec.shape) == 1 and len(self.freq.shape) != 1: raise Exception("Frequency axis and spectral axis do not have the same shape.") else: singlefreq = True # make sure they have the same shape or that the channel array is 1D if self.chan: havechan = True if self.spec.shape != self.chan.shape: if len(spec.shape) == 1 and len(self.chan.shape) != 1: raise Exception("Channel axis and spectral axis do not have the same shape.") else: singlechan = True # if the arrays are more than 1D, then go through each if len(self.spec.shape) > 1: for i in range(self.spec.shape[0]): spec = self.spec[i] if not havechan: chan = np.arange(len(spec)) elif singlechan: chan = self.chan else: chan = self.chan[i] if singlefreq: freq = self.freq else: freq = self.freq[i] spectra.append(Spectrum(spec=spec, freq=freq, chans=chan)) else: # construct the channel array if needed if not havechan: self.chan = np.arange(len(self.spec)) spectra.append(Spectrum(spec=self.spec, freq=self.freq, chans=self.chan)) first = True images = {} # make images from the spectra for i, spec in enumerate(spectra): data = (spec.chans(masked=False), spec.freq(masked=False), spec.spec(csub=False, masked=False)) if first: table = Table(columns=["channel", "frequency", "flux"], units=["number", "GHz", "Unknown"], data=np.column_stack(data), planes=["0"]) first = False else: table.addPlane(np.column_stack(data), "%i" % i) myplot = APlot(ptype=admit.PlotControl.PNG, pmode=admit.PlotControl.BATCH, abspath=os.getcwd()) myplot.plotter(spec.freq(masked=False), [spec.spec(csub=False, masked=False)], title="Spectrum %i" % i, figname="fig_%i" % i, xlab="Frequency", ylab="Intensity", thumbnail=True) # Why not use p1 as the key? images["fig%i" % i] = myplot.getFigure(figno=myplot.figno, relative=True) image = Image(images=images, description="Spectra") # construct the BDP bdp = CubeSpectrum_BDP(image=image, table=table) return bdp
def getfile(self, file): """ Method to read in a file and convert it to a Spectrum. Columns must already have been specified. Parameters ---------- file : str Name of the file to read in Returns ------- Spectrum instance containing the data read in from the file """ # do some consistency and integrity checking if self.freqcol is None and self.velcol is None: raise Exception("Either the frequency column or velocity column must be specified.") if not isinstance(self.chancol, int): raise Exception("chan parameter must be an int.") if not isinstance(self.freqcol, int) and self.freqcol is not None: raise Exception("freq parameter must be an int.") if not isinstance(self.velcol, int) and self.velcol is not None: raise Exception("velocity parameter must be an int.") if not isinstance(self.speccol, int): raise Exception("spec parameter must be an int.") #if self.freqcol < 0 and self.freqcol is not None: # raise Exception("The frequency column must be specified (i.e. freq=1)") if self.speccol < 0: raise Exception("The spectral column must be specified (i.e. spec=1)") print self.velcol if self.velcol >= 0: if self.restfreq is not None: if not isinstance(self.restfreq, float) and not isinstance(self.restfreq, int): raise Exception("Restfreq must be a float.") else: raise Exception("Restfreq must be specified.") if self.vlsr is None: print "vlsr was not specified, assuming it is 0.0" self.vlsr = 0.0 elif not isinstance(self.vlsr, float) and not isinstance(self.vlsr, int): raise Exception("vlsr must be a float") # find out the minimum number of columns to expect mincol = max(self.freqcol, self.chancol, self.speccol) # open the file and read it in fl = open(file, 'r') lines = fl.readlines() fl.close() # track line that are skipped for various reasons skipped = 0 comments = 0 blank = 0 freq = [] spec = [] chan = [] # go through each line for line in lines: # if the line is blank if len(line) < 1: blank += 1 continue # if the line is a comment if line.startswith("#"): comments += 1 continue # split the line up data = line.split(self.separator) # if there are not enough columns if len(data) < mincol: skipped += 1 continue # add the data to the arrays if self.velcol >= 0: freq.append(float(data[self.velcol - 1])) else: freq.append(float(data[self.freqcol - 1])) spec.append(float(data[self.speccol - 1])) if self.chancol > 0: chan.append(int(data[self.chancol - 1])) if self.velcol >= 0: for i, frq in enumerate(freq): freq[i] = self.restfreq + utils.veltofreq(self.vlsr - frq, self.restfreq) # if the was no channel column the generate it if len(chan) == 0: chan = range(len(spec)) # report what was found print "Imported %i lines from file %s" % (len(spec), file) if blank > 0: print "Skipped %i blank lines from file %s" % (blank, file) if skipped > 0: print "Skipped %i lines with too few columns from file %s" % (skipped, file) if comments > 0: print "Skipped %i commented lines from file %s" % (comments, file) if self.length == 0: self.length = len(spec) else: # if this spectrum is not the same length of the others if self.length != len(spec): raise Exception("Not all input spectra are the same length.") # convert to a Spectrum instance return Spectrum(spec=spec, freq=freq, chans=chan)
def convert(self, chan=None, freq=None, velocity=None, spec=None, file=None, separator=None, restfreq=None, vlsr=None): """ Method to convert input data (either files or arrays) into a CubeSpectrum_BDP. If files are used then then the columns containing the frequency and the intensity must be given (channel numbers are optional). Any number of files can be given, but all spectra must have the same length as they are assumed to come from the same data source. Blank lines and lines starting with a comment '#' will be skipped, additionally any line with too few columns will be skipped. If arrays are used an input then both the frequency and intensity must be specified (the channel numbers are optional). Both lists and numpy arrays are accepted as inputs. Multidimmensional arrays are supported with the following parameters: + A single frequency list can be given to cover all input spectra, otherwise the shape of the frequency array must match that of the spectra + A single channel list can be given to cover all input spectra, otherwise the shape of the channel array must match that of the spectra + All spectra must have the same length If a channel array is not specified then one will be constructed with the following parameters: + The channel numbers will start at 0 (casa convention) + The first entry in the spectrum will be considered the first channel, regardless of whether the frequency array increases or decreases. Additionally, if there is velocity axis, but no frequency axis, a frequency axis can be constructed by specifying a rest frequency (restfreq), and vlsr. The convert method will return a single CubeSpectrum_BDP instance holding all input spectra along with an image of each. Parameters ---------- chan : array or int An array holding the channel numbers for the data, multidimmensional arrays are supported. If an integer is specified then it is the number of the column in the file which contains the channel numbers, column numbers are 1 based. Default: None freq : array An array holding the frequencies for the data, multidimmensional arrays are supported. If an integer is specified then it is the number of the column in the file which contains the frequencies, column numbers are 1 based. Default: None velocity : array An array holding the velocity for the data, multidimmensional arrays are supported. If an integer is specified then it is the number of the column in the file which contains the velcoties, column numbers are 1 based. If this parameter is specified then restfreq and vlsr must also be specified. Default: None spec : array An array holding the intesities of the data, multidimmensional arrays are supported. If an integer is specified then it is the number of the column in the file which contains the intensities, column numbers are 1 based. Default: None file : list or str A single file name or a list of file names to be read in for spectra. Default: None separator : str The column separator for reading in the data files. Default: None (any whitespace) restfreq : float The rest frequency to use to convert the spectra from velocity to frequency units. The rest frequency is in GHz. Default: None (no conversion done) vlsr : float The reference velocity for converting a velocity axis to frequency. The units are km/s. If this is not set then it is assumed that the vlsr is 0.0. Default: None Returns ------- CubeSpectrum_BDP instance containing all of the inpur spectra. """ self.restfreq = restfreq self.vlsr = vlsr # if a string was given as the file name then turn it into a list so it can be iterated over if isinstance(file, str): self.file = [file] else: self.file = file # do some error checking if isinstance(chan, np.ndarray) or isinstance(chan, list): if isinstance(chan, list): self.chan = np.array(chan) else: self.chan = copy.deepcopy(chan) self.chancol = -1 elif isinstance(chan, int): self.chancol = chan self.chan = None else: self.chancol = -1 self.chan = None if isinstance(freq, np.ndarray) or isinstance(freq, list): if isinstance(freq, list): self.freq = np.array(freq) else: self.freq = copy.deepcopy(freq) self.freqcol = -1 elif isinstance(freq, int): self.freqcol = freq self.freq = None else: self.freqcol = -1 self.freq = None if isinstance(velocity, np.ndarray) or isinstance(velocity, list): if isinstance(velocity, list): self.freq = np.array(velocity, dtype=np.float) else: self.freq = velocity.astype(np.float) for i, frq in enumerate(self.freq): self.freq[i] = self.restfreq + utils.veltofreq( frq - self.vlsr, self.restfreq) self.freqcol = -1 elif isinstance(velocity, int): self.velcol = velocity self.velocity = None else: self.velcol = -1 self.velocity = None if isinstance(spec, np.ndarray) or isinstance(spec, list): if isinstance(spec, list): self.spec = np.array(spec) else: self.spec = copy.deepcopy(spec) self.speccol = -1 elif isinstance(spec, int): self.speccol = spec self.spec = None else: self.speccol = -1 self.spec = None if isinstance(separator, str): self.separator = separator spectra = [] # read in the data from any files if self.file: for fl in self.file: spectra.append(self.getfile(fl)) else: # convert the input arrays singlefreq = False singlechan = False havechan = False # make sure they have the same shape or that the frequency array is 1D if self.spec.shape != self.freq.shape: if len(self.spec.shape) == 1 and len(self.freq.shape) != 1: raise Exception( "Frequency axis and spectral axis do not have the same shape." ) else: singlefreq = True # make sure they have the same shape or that the channel array is 1D if self.chan: havechan = True if self.spec.shape != self.chan.shape: if len(spec.shape) == 1 and len(self.chan.shape) != 1: raise Exception( "Channel axis and spectral axis do not have the same shape." ) else: singlechan = True # if the arrays are more than 1D, then go through each if len(self.spec.shape) > 1: for i in range(self.spec.shape[0]): spec = self.spec[i] if not havechan: chan = np.arange(len(spec)) elif singlechan: chan = self.chan else: chan = self.chan[i] if singlefreq: freq = self.freq else: freq = self.freq[i] spectra.append(Spectrum(spec=spec, freq=freq, chans=chan)) else: # construct the channel array if needed if not havechan: self.chan = np.arange(len(self.spec)) spectra.append( Spectrum(spec=self.spec, freq=self.freq, chans=self.chan)) first = True images = {} # make images from the spectra for i, spec in enumerate(spectra): data = (spec.chans(masked=False), spec.freq(masked=False), spec.spec(csub=False, masked=False)) if first: table = Table(columns=["channel", "frequency", "flux"], units=["number", "GHz", "Unknown"], data=np.column_stack(data), planes=["0"]) first = False else: table.addPlane(np.column_stack(data), "%i" % i) myplot = APlot(ptype=admit.PlotControl.PNG, pmode=admit.PlotControl.BATCH, abspath=os.getcwd()) myplot.plotter(spec.freq(masked=False), [spec.spec(csub=False, masked=False)], title="Spectrum %i" % i, figname="fig_%i" % i, xlab="Frequency", ylab="Intensity", thumbnail=True) # Why not use p1 as the key? images["fig%i" % i] = myplot.getFigure(figno=myplot.figno, relative=True) image = Image(images=images, description="Spectra") # construct the BDP bdp = CubeSpectrum_BDP(image=image, table=table) return bdp
def getfile(self, file): """ Method to read in a file and convert it to a Spectrum. Columns must already have been specified. Parameters ---------- file : str Name of the file to read in Returns ------- Spectrum instance containing the data read in from the file """ # do some consistency and integrity checking if self.freqcol is None and self.velcol is None: raise Exception( "Either the frequency column or velocity column must be specified." ) if not isinstance(self.chancol, int): raise Exception("chan parameter must be an int.") if not isinstance(self.freqcol, int) and self.freqcol is not None: raise Exception("freq parameter must be an int.") if not isinstance(self.velcol, int) and self.velcol is not None: raise Exception("velocity parameter must be an int.") if not isinstance(self.speccol, int): raise Exception("spec parameter must be an int.") #if self.freqcol < 0 and self.freqcol is not None: # raise Exception("The frequency column must be specified (i.e. freq=1)") if self.speccol < 0: raise Exception( "The spectral column must be specified (i.e. spec=1)") print self.velcol if self.velcol >= 0: if self.restfreq is not None: if not isinstance(self.restfreq, float) and not isinstance( self.restfreq, int): raise Exception("Restfreq must be a float.") else: raise Exception("Restfreq must be specified.") if self.vlsr is None: print "vlsr was not specified, assuming it is 0.0" self.vlsr = 0.0 elif not isinstance(self.vlsr, float) and not isinstance( self.vlsr, int): raise Exception("vlsr must be a float") # find out the minimum number of columns to expect mincol = max(self.freqcol, self.chancol, self.speccol) # open the file and read it in fl = open(file, 'r') lines = fl.readlines() fl.close() # track line that are skipped for various reasons skipped = 0 comments = 0 blank = 0 freq = [] spec = [] chan = [] # go through each line for line in lines: # if the line is blank if len(line) < 1: blank += 1 continue # if the line is a comment if line.startswith("#"): comments += 1 continue # split the line up data = line.split(self.separator) # if there are not enough columns if len(data) < mincol: skipped += 1 continue # add the data to the arrays if self.velcol >= 0: freq.append(float(data[self.velcol - 1])) else: freq.append(float(data[self.freqcol - 1])) spec.append(float(data[self.speccol - 1])) if self.chancol > 0: chan.append(int(data[self.chancol - 1])) if self.velcol >= 0: for i, frq in enumerate(freq): freq[i] = self.restfreq + utils.veltofreq( self.vlsr - frq, self.restfreq) # if the was no channel column the generate it if len(chan) == 0: chan = range(len(spec)) # report what was found print "Imported %i lines from file %s" % (len(spec), file) if blank > 0: print "Skipped %i blank lines from file %s" % (blank, file) if skipped > 0: print "Skipped %i lines with too few columns from file %s" % ( skipped, file) if comments > 0: print "Skipped %i commented lines from file %s" % (comments, file) if self.length == 0: self.length = len(spec) else: # if this spectrum is not the same length of the others if self.length != len(spec): raise Exception("Not all input spectra are the same length.") # convert to a Spectrum instance return Spectrum(spec=spec, freq=freq, chans=chan)
def run(self): """Runs the task. Parameters ---------- None Returns ------- None """ self._summary = {} dt = utils.Dtime("CubeSpectrum") seed = self.getkey("seed") if seed <= 0: np.random.seed() else: np.random.seed(seed) #print "RANDOM.GET_STATE:",np.random.get_state() contin = self.getkey("contin") rms = 1.0 # not a user parameter, we do all spectra in S/N space f0 = self.getkey("freq") # central frequency in band df = self.getkey("delta") / 1000.0 # channel width (in GHz) nspectra = self.getkey("nspectra") taskargs = " contin=%f freq=%f delta=%f nspectra=%f " % (contin, f0, df, nspectra) spec = range(nspectra) dt.tag("start") if self.getkey("file") != "": print "READING spectrum from", self.getkey("file") (freq, spec[0]) = getspec(self.getkey("file")) nchan = len(freq) print "Spectrum %d chans from %f to %f: min/max = %f %f" % ( nchan, freq.min(), freq.max(), spec[0].min(), spec[0].max()) # @todo nspectra>1 not tested for i in range(1, nspectra): spec[i] = deepcopy(spec[0]) dt.tag("getspec") else: nchan = self.getkey("nchan") freq = np.arange(nchan, dtype=np.float64) center = int(nchan / 2) for i in range(nchan): freq[i] = f0 + (float((i - center)) * df) for i in range(nspectra): spec[i] = np.zeros(nchan) chans = np.arange(nchan) taskargs += " nchan = %d" % nchan for i in range(nspectra): if seed >= 0: spec[i] += np.random.normal(contin, rms, nchan) # print "MEAN/STD",spec[i].mean(),spec[i].std() lines = self.getkey("lines") sls = SpectralLineSearch(False) for item in self.getkey("transitions"): kw = { "include_only_nrao": True, "line_strengths": ["ls1", "ls2"], "energy_levels": ["el2", "el4"], "fel": True, "species": item[0] } results = sls.search(item[1][0], item[1][1], "off", **kw) # look at line strengths if len(results) > 0: mx = 0.0 indx = -1 for i in range(len(results)): if results[i].getkey("linestrength") > mx: indx = i mx = results[i].getkey("linestrength") for res in results: if mx > 0.0: lines.append([ item[2] * res.getkey("linestrength") / mx, res.getkey("frequency") + utils.veltofreq(item[4], res.getkey("frequency")), item[3] ]) else: lines.append([ item[2], res.getkey("frequency") + utils.veltofreq(item[4], res.getkey("frequency")), item[3] ]) for item in lines: for i in range(nspectra): spec[i] += utils.gaussian1D(freq, item[0], item[1], utils.veltofreq(item[2], item[1])) if self.getkey("hanning"): for i in range(nspectra): filter = Filter1D.Filter1D(spec[i], "hanning", **{"width": 3}) spec[i] = filter.run() dt.tag("hanning") center = int(nchan / 2) dt.tag("open") bdp_name = self.mkext("Genspec", "csp") b2 = CubeSpectrum_BDP(bdp_name) self.addoutput(b2) images = {} # png's accumulated for i in range(nspectra): sd = [] caption = "Generated Spectrum %d" % i # construct the Table for CubeSpectrum_BDP # @todo note data needs to be a tuple, later to be column_stack'd labels = ["channel", "frequency", "flux"] units = ["number", "GHz", ""] data = (chans, freq, spec[i]) # plane 0 : we are allowing a multiplane table, so the first plane is special if i == 0: table = Table(columns=labels, units=units, data=np.column_stack(data), planes=["0"]) else: table.addPlane(np.column_stack(data), "%d" % i) # example plot , one per position for now x = chans xlab = 'Channel' y = [spec[i]] sd.append(xlab) myplot = APlot(ptype=self._plot_type, pmode=self._plot_mode, abspath=self.dir()) ylab = 'Flux' p1 = "%s_%d" % (bdp_name, i) myplot.plotter(x, y, "", p1, xlab=xlab, ylab=ylab, thumbnail=True) # Why not use p1 as the key? ii = images["pos%d" % i] = myplot.getFigure(figno=myplot.figno, relative=True) thumbname = myplot.getThumbnail(figno=myplot.figno, relative=True) image = Image(images=images, description="CubeSpectrum") sd.extend([ii, thumbname, caption]) self.spec_description.append(sd) self._summary["spectra"] = SummaryEntry(self.spec_description, "GenerateSpectrum_AT", self.id(True), taskargs) dt.tag("table") b2.setkey("image", image) b2.setkey("table", table) b2.setkey("sigma", rms) b2.setkey("mean", contin) dt.tag("done") dt.end()
def findpatterns(spec, points, segments): """ Method to search for patterns in the peaks. Specifically it is looking for pairs of peaks that are the same distance apart (within the tolerance). These can be an indicator of rotation/infall/etc. Only two patterns are allowed to overlap. See the design documentation for specifics Parameters ---------- spec : array like The spectrum that is currently being worked on. points : numpy array Listing of peak points segments : list List of the segments for the current spectrum Returns ------- A Peaks class containing the spectra, segments, peaks and patterns """ #self.dt.tag("START PATTERN") # initialize the data class peaks = Peaks(spec=spec, segments=segments) delfrq = utils.veltofreq(650, spec.freq()[len(spec)/2]) maxsep = delfrq / spec.delta() ts = np.zeros(len(spec)).astype(float) ts[0] = 1. # make a copy of the input points which will be modified as groups are located singles = copy.deepcopy(points) # create a 2D array to catalog the distances between every peak diffs = np.zeros((len(points), len(points))) # calculate the distance between every peak #self.dt.tag("P0") for i in range(len(points)): for j in range(i + 1, len(points)): diffs[i, j] = abs(points[i] - points[j]) #self.dt.tag("P1") # look for pairs of peaks that are a common distance apart (within the # given tolerance) clusters = {} for i in range(len(points)): for j in range(i + 1, min(len(points), i + 2)): # get each distance one at a time and compare it to the rest diff = diffs[i, j] dlist = [] # if this is the first time this distance has been found first = True for k in range(i + 1): for l in range(k + 1, min(len(points), k + 2)): #print i, j, k, l # compare to all other points, skipping itself if (k == i and l == j) or diffs[k, l] < 3.0 / 3.0 \ or abs(spec.spec()[points[k]]) > 2.0 * abs(spec.spec()[points[l]])\ or abs(spec.spec()[points[l]]) > 2.0 * abs(spec.spec()[points[k]]): continue # if the distances from two pairs of points are close enough # add it to the list of clusters if maxsep > diffs[k, l] > 0.0 and (diff - 3.0 / 3.0 < diffs[k, l] < diff + 3.0 / 3.0): # if this is the first time for this distance then add # both to the list if first: dlist.append([i, j]) dlist.append([k, l]) # mark the current point as processed (i.e. set to 0.0) diffs[k, l] = 0.0 first = False # if groups of points were detected then add them to the dictionary if len(dlist) > 0: clusters[diff] = dlist #self.dt.tag("P2") # get the actual peak points rather then just indexes clens = {} for k, v in clusters.iteritems(): tl = [] for i in v: tl.append([points[i[0]], points[i[1]]]) clusters[k] = tl clens[k] = len(tl) # sort the list to make it easier to process clist = sorted(clens, key=clens.get) clist.reverse() spoints = [] for k in clist: for i in clusters[k]: if not i[0] in spoints: spoints.append(i[0]) if not i[1] in spoints: spoints.append(i[1]) # single spectral lines # spectral lines that appear to be in a pattern for p in spoints: l = set() for k in clist: v = clusters[k] # collect those that are very close together for i in v: if (p - 0.1 < i[0] < p + 0.1) or (p - 0.1 < i[1] < p + 0.1): l.add(k) # reduce each of these to a single instance if len(l) > 1: for i in clist: if i in l: l.remove(i) break for i in l: for j in range(len(clusters[i]) - 1, -1, -1): if (p - 0.1 < clusters[i][j][0] < p + 0.1) or \ (p - 0.1 < clusters[i][j][1] < p + 0.1): del clusters[i][j] if len(clusters[i]) < 2: del clusters[i] clist.remove(i) # remove any that appear multiple times counts = {} multi = {} for k, v in clusters.iteritems(): for i in v: if i[0] in counts: multi[i[0]].append(i) else: counts[i[0]] = 1 multi[i[0]] = [i] if i[1] in counts: multi[i[1]].append(i) else: counts[i[1]] = 1 multi[i[1]] = [i] for k, v in multi.iteritems(): ratios = {} r = [] if len(v) > 1: for i in v: temp = max(peaks.getspecs()[i[0]], peaks.getspecs()[i[1]]) / \ min(peaks.getspecs()[i[0]], peaks.getspecs()[i[1]]) r.append(temp) ratios[tuple(i)] = temp best = min(r, key=lambda x: abs(x - 1.0)) for k1, v1 in ratios.iteritems(): if best != v1: for k2 in clusters.keys(): try: clusters[k2].remove(list(k1)) except ValueError: pass remove = [] newcounts = {} counts = set() for k, v in clusters.iteritems(): #newcounts[k] = len(v) counts.add(len(v)) if len(counts) > 0: counts = sorted(counts) counts.reverse() counts = counts[0:min(2, len(counts))] for k, v in clusters.iteritems(): if not len(v) in counts: remove.append(k) continue else: newcounts[k] = len(v) for i in v: for j in i: try: singles.remove(j) except ValueError: pass for i in remove: del clusters[i] peaks.singles = singles peaks.pairs = clusters peaks.counts = newcounts return peaks
def run(self): """Runs the task. Parameters ---------- None Returns ------- None """ self._summary = {} dt = utils.Dtime("CubeSpectrum") seed = self.getkey("seed") if seed <= 0: np.random.seed() else: np.random.seed(seed) #print "RANDOM.GET_STATE:",np.random.get_state() contin = self.getkey("contin") rms = 1.0 # not a user parameter, we do all spectra in S/N space f0 = self.getkey("freq") # central frequency in band df = self.getkey("delta") / 1000.0 # channel width (in GHz) nspectra = self.getkey("nspectra") taskargs = " contin=%f freq=%f delta=%f nspectra=%f " % (contin,f0,df,nspectra) spec = range(nspectra) dt.tag("start") if self.getkey("file") != "": print "READING spectrum from",self.getkey("file") (freq, spec[0]) = getspec(self.getkey("file")) nchan = len(freq) print "Spectrum %d chans from %f to %f: min/max = %f %f" % (nchan, freq.min(), freq.max(), spec[0].min(), spec[0].max()) # @todo nspectra>1 not tested for i in range(1,nspectra): spec[i] = deepcopy(spec[0]) dt.tag("getspec") else: nchan = self.getkey("nchan") freq = np.arange(nchan, dtype=np.float64) center = int(nchan/2) for i in range(nchan): freq[i] = f0 + (float((i - center)) * df) for i in range(nspectra): spec[i] = np.zeros(nchan) chans = np.arange(nchan) taskargs += " nchan = %d" % nchan for i in range(nspectra): if seed >= 0: spec[i] += np.random.normal(contin, rms, nchan) # print "MEAN/STD",spec[i].mean(),spec[i].std() lines = self.getkey("lines") sls = SpectralLineSearch(False) for item in self.getkey("transitions"): kw = {"include_only_nrao" : True, "line_strengths": ["ls1", "ls2"], "energy_levels" : ["el2", "el4"], "fel" : True, "species" : item[0] } results = sls.search(item[1][0], item[1][1], "off", **kw) # look at line strengths if len(results) > 0: mx = 0.0 indx = -1 for i in range(len(results)): if results[i].getkey("linestrength") > mx: indx = i mx = results[i].getkey("linestrength") for res in results: if mx > 0.0: lines.append([item[2] * res.getkey("linestrength") / mx, res.getkey("frequency") + utils.veltofreq(item[4], res.getkey("frequency")), item[3]]) else: lines.append([item[2], res.getkey("frequency") + utils.veltofreq(item[4], res.getkey("frequency")), item[3]]) for item in lines: for i in range(nspectra): spec[i] += utils.gaussian1D(freq, item[0], item[1], utils.veltofreq(item[2], item[1])) if self.getkey("hanning"): for i in range(nspectra): filter = Filter1D.Filter1D(spec[i], "hanning", **{"width" : 3}) spec[i] = filter.run() dt.tag("hanning") center = int(nchan/2) dt.tag("open") bdp_name = self.mkext("Genspec","csp") b2 = CubeSpectrum_BDP(bdp_name) self.addoutput(b2) images = {} # png's accumulated for i in range(nspectra): sd = [] caption = "Generated Spectrum %d" % i # construct the Table for CubeSpectrum_BDP # @todo note data needs to be a tuple, later to be column_stack'd labels = ["channel" ,"frequency" ,"flux" ] units = ["number" ,"GHz" ,"" ] data = (chans ,freq ,spec[i] ) # plane 0 : we are allowing a multiplane table, so the first plane is special if i==0: table = Table(columns=labels,units=units,data=np.column_stack(data),planes=["0"]) else: table.addPlane(np.column_stack(data),"%d" % i) # example plot , one per position for now x = chans xlab = 'Channel' y = [spec[i]] sd.append(xlab) myplot = APlot(ptype=self._plot_type,pmode=self._plot_mode, abspath=self.dir()) ylab = 'Flux' p1 = "%s_%d" % (bdp_name,i) myplot.plotter(x,y,"",p1,xlab=xlab,ylab=ylab,thumbnail=True) # Why not use p1 as the key? ii = images["pos%d" % i] = myplot.getFigure(figno=myplot.figno,relative=True) thumbname = myplot.getThumbnail(figno=myplot.figno,relative=True) image = Image(images=images, description="CubeSpectrum") sd.extend([ii, thumbname, caption]) self.spec_description.append(sd) self._summary["spectra"] = SummaryEntry(self.spec_description,"GenerateSpectrum_AT",self.id(True), taskargs) dt.tag("table") b2.setkey("image",image) b2.setkey("table",table) b2.setkey("sigma",rms) b2.setkey("mean",contin) dt.tag("done") dt.end()