; docformat = 'rst' ;+ ; ; This is an example of testing the non-negative least-squares ; decomposition for a number of a randomly generated spectra, built ; around the functionality provided by the AmesPAHdbIDLSuite and ; should help confirm that the it has been properly installed. The ; source code is annotated to guide users and developers in the inner ; workings of the suite. ; ; Updated versions of the NASA Ames PAH IR Spectroscopic Database and ; more information can be found at: `www.astrochemistry.org/pahdb <https://www.astrochemistry.org/pahdb>`. ; ; :Examples: ; Call the procedure directly:: ; ; IDL> test_nnls ; ; :Author: ; Dr. Christiaan Boersma ; ; :Copyright: ; BSD licensed ; ; :History: ; Changes:: ; ; 05-16-2022 ; Define indices array and expand formatting. Christiaan Boersma. ; 08-19-2019 ; Documentation added. Christiaan Boersma. ;- ;+ ; Procedure performing a non-negative least-squares decomposition. ; ; :Returns: ; array (1D) of structures ; ; :Categories: ; Example ; ; :Private: ;- FUNCTION DO_NNLS COMPILE_OPT IDL2 ; avoid underflow messages !EXCEPT = 0 ; set number of spectra to co-add, the FWHM to be applied when ; convolving, the signal-to-noise ratio and spectral range nspectra = 20 fwhm = 20D sn = 100D xrange = [20D, 2.5D] ; read in the default database defined by the environement variable ; !AMESPAHDEFAULTDB or the system variable AMESPAHDEFAULTDB. use ; the keyword FILENAME if these have not been set pahdb = OBJ_NEW('AmesPAHdbIDLSuite') ; get the properties of all the PAHs in the database species = pahdb->getSpeciesByUID( -1 ) ; retrieve all unique identifiers uids = (species->Get()).data.uid ; count number of unique identifiers nuids = N_ELEMENTS(uids) ; create random selection of unique identifiers and avoid doubles n = nspectra indices = LONARR(nspectra) WHILE n GT 0 DO BEGIN indices[nspectra - n] = LONG(RANDOMU(seed, n) * nuids) indices = indices[SORT(indices)] u = UNIQ(indices) n = nspectra - N_ELEMENTS(u) indices[0] = indices[u] ENDWHILE selected_uids = uids[indices] ; get the transitions for the selected species transitions = pahdb->getTransitionsByUID(selected_uids) ; convolve the transitions in order to obtain a spectrum spectrum = transitions->Convolve(FWHM=fwhm, /Gaussian, XRange=1D4/xrange) ; create random weights selected_weights = REPLICATE({uid:0L, weight:0D}, nspectra) selected_weights.uid = selected_uids selected_weights.weight = RANDOMU(LONG(SYSTIME(1)), nspectra, /DOUBLE) ; co-add the spectra using the random weights coadd = spectrum->Coadd(Weights=selected_weights) ; obtain the spectrum for use outside the object coadd_s = coadd->Get() ; add some Gaussian noise noise = MAX(coadd_s.data.intensity) * RANDOMN(seed, N_ELEMENTS(coadd_s.data)) / sn coadd_s.data.intensity += noise ; clean up OBJ_DESTROY,[spectrum, transitions, species] ; retrieve all transitions from the database transitions = pahdb->getTransitionsByUID( -1 ) ; convolve the transitions in order to obtain a spectrum spectrum = transitions->Convolve(FWHM=fwhm, /Gaussian, XRange=1D4/xrange) ; fit the spectrum fit = spectrum->Fit(coadd_s.data.intensity) ; obtain the unique identifiers of the fitted species found_uids = fit->getUIDS() ; obtain the fitted weights found_weights = fit->getWeights() ; clean up OBJ_DESTROY,[fit, coadd, spectrum, transitions, pahdb] ; concat selected and found unique identifiers all_uids = [selected_uids, found_uids] ; sort all unique identifiers srt = SORT(all_uids) all_uids = all_uids[srt] ; keep only one unique identifier all_uids = all_uids[UNIQ(all_uids)] ; count all unique identifiers nall_uids = N_ELEMENTS(all_uids) ; output header PRINT,FORMAT='(A10,4X,A10)',"UID","weight" PRINT,FORMAT='(A0,X,A0,X,A0,X,A0)',"selected","found","selected","found" ; compare selected with found FOR i = 0, nall_uids - 1 DO BEGIN select = WHERE(selected_uids EQ all_uids[i], count) IF count EQ 0 THEN BEGIN selected_uid = '' selected_weight = '' ENDIF ELSE BEGIN selected_uid = STRING(FORMAT='(I4)', selected_uids[select]) select = WHERE(selected_weights.uid EQ all_uids[i]) selected_weight = STRING(FORMAT='(G7.2)', selected_weights[select].weight) ENDELSE select = WHERE(found_uids EQ all_uids[i], count) IF count EQ 0 THEN BEGIN found_uid = '' found_weight = '' ENDIF ELSE BEGIN found_uid = STRING(FORMAT='(I4)', found_uids[select]) select = WHERE(found_weights.uid EQ all_uids[i]) found_weight = STRING(FORMAT='(G7.2)', found_weights[select].weight) ENDELSE PRINT,FORMAT='(A3,4X,A3,4X,A7,4X,A7)',selected_uid,found_uid,selected_weight,found_weight ENDFOR points = {x:0D, y:0D} FOR i = 0, nspectra - 1 DO BEGIN select = WHERE(selected_uids[i] EQ found_uids , nselect) IF nselect EQ 0 THEN found_weight = 0D $ ELSE BEGIN select = WHERE(selected_uids[i] EQ found_weights.uid) found_weight = found_weights[select].weight ENDELSE points = [points, {x:selected_weights[i].weight, y:found_weight}] ENDFOR RETURN,points[1:*] END ;+ ; Procedure testing non-negative least-squares decomposition. ; ; :Categories: ; Example ; ;- PRO TEST_NNLS COMPILE_OPT IDL2 points = {x:0D, y:0D} FOR i = 0, 5 DO points = [points, DO_NNLS()] points = points[1:*] PLOT,[0,MAX(points.x)],[0,MAX(points.y)],XTITLE='input weights',YTITLE='output weights',/NODATA OPLOT,[0,1],[0,1],LINESTYLE=5,COLOR=2 OPLOT,points.x,points.y,PSYM=1 END