#pragma rtGlobals=3 // Use modern global access method and strict wave access. #include "2015IcarttFormat_UWFPS" #include "2015GeneralMacros" Menu "UWFPS Analysis" SubMenu "Flights" "Load all instrument data from Twin Otter", LoadTwinOtterFlights() "Do all box model prep", DoAllBoxModelPrep() End SubMenu "STILT" SubMenu "Initial processing" "Convert STILT trajectories to ibw files", BatchConvertSTILTcsv() End "Load STILT trajectory matrices", LoadSTILTtrajs("") "Calculate distance to US Magnesium", CalculateDistToUSMag("") // "Bin the trajectory plume ages", BinTrajTimes("") "Plot trajectories", PlotTrajOnMap() "Get wind data from STILT", GetWindFromSTILT("") "Get wind data from MetProbe", MakeAvgWindSpdDir() SubMenu "Compare wind data" "Interpolate STILT onto MetProbe", InterpSTILTToTO() "Concatenate all flights", ConcatAllWindData() "Plot comparison - all flights", PlotWindComparison_OnePlot() "Plot comparison - flight by flight", PlotWindcomparison_AllFlights() End "Kill raw trajectories", KillTrajMatrices("") End SubMenu "Plumes" "Load plume indexes", LoadPlumeIndexes() "Get data in plumes", GaussFitPlumes() "Plot halogen plumes", PlotHalogenPlumes() "Plot other plumes", PlotOtherPlumes() "Get plume ages", GetPlumeAges() "Plot 3D flight track", Plot3DFlightTrack("") "Make master list of plumes", ConcatAllPlumes SubMenu "Get emissions" "Get emission fluxes", GetEmissionFluxes() "Make master list of emission fluxes", ConcatEmissionFluxes() "Average the emission data", AverageEmissionFluxes() "Plot emission plumes", PlotEmissionPlumes() End End SubMenu "Box modeling" "Load GS data for F0AM", LoadGSdataForF0AM() "Print VOC profile", PrintVOCProfile() SubMenu "Puff model" "Prepare puff box modeling input", PrepareBoxModelInput_Puff() "Make puff F0AM input files", SaveF0AMInputData_Puff() "Load F0AM output files", LoadModelOutput_Puff() "Process box modeling output", ProcessModelOutput_Puff() "Plot modeling results", PlotModelOutput_Puff() "Plot any species by plume", PlotSpeciesByPlume("", 0, 1) // "Plot family conc by plume", PlotFamilyConcByPlume(0) "Process rates - puff model, single species", ProcessModelRates_Puff("") "Process rates - puff model, family", ProcessModelFamilyRates("") "Plot top rates - puff", PlotTopRates_Puff("") "Plot top rates - puff, by plumes", PlotTopRatesByPlume_Puff("", 0) // "Calculate model-obs error", CalculateModelObsError("") End SubMenu "Last model" "Prepare last box modeling input", PrepareBoxModelInput_Last() "Make last F0AM input files", SaveF0AMInputData_Last() "Load last F0AM output files", LoadModelOutput_Last() "Process box modeling output", ProcessModelOutput_Last() "Plot start to finish changes", PlotModelChanges() "Process rates - last model, single species", ProcessModelRates_Last("") "Plot top rates - last model", PlotTopRatesByPlume_Last("") "Plot family conc", PlotFamilyConc_Last() End SubMenu "Generic" "Set it up", SetUpGeneric() "Load it", LoadGeneric() End SubMenu "Sensitivities" "Compare up to three puff models", CompareModels(1, "", "", "") "Compare model accuracy", CompareModelAccuracy(1, "", "", "") "Compare StartToFinish", CompareModelStartEnd(1, "", "", "") End End SubMenu "Ground sites" "Load and process UWFPS ground sites", LoadAndExtractUWFPSGroundSites() "Load and process historical GC-FID data", LoadAndExtractHistoricalData() "Compare VOC profiles", CompareVOCProfiles() End "Bring procedure file to front/F11", Bring_Procedure_Front() End //Version history: // v1 - This version comes from the UWFPS_Analysis_v13.ipf, which CCW used for analyzing the Oxtotal budget. Deleted all the functions not relevant // to the Twin Otter halogen analysis. // v2 - This version has an additional section of code starting at line 3522, written by WSC for analysis of the halogen plume emitted by the // US Magnesium plant on the western shore of the Great Salt Lake. Functions are also accessible in the UWFPS Analysis "US Mg Halogens" submenu. // v3 - CCW takes over from WSC after Hollings summer project is over, to finish this project. This is used to make some preliminary figures for the // 2021 Science for Solutions conference // v4 - Some additional analysis included, so that a full run through of all the modeling can be done. "Re-run" // v5 - Added code to automate the interaction with F0AM. Used to do test out various sensitivities for the 1/18 flight // v6 - Used to make the figures for the paper draft that went out to coauthors for the first time. Included some function // for making those figures // v7 - Used to make the figures for the initial submission to PNAS //********Load data***********************// Function Bring_Procedure_Front() String strWindows=StringFromList(0,WinList("UWFPS_Halogens_*",";","WIN:128")) DisplayProcedure/W=$strWindows End Function LoadTwinOtterFlights() //Prepare to load the necessary flights. Give the user the load options SetDataFolder root: String strLoadOptions = "Load all;Load one;Load night;Load photochem;Load one model day" Variable vLoadType Prompt vLoadType, "Which flights to load", popup, strLoadOptions DoPrompt "Select load option", vLoadType If(V_flag == 0) String strLoadType = "flt20170116;flt20170117_L1;flt20170117_L2;flt20170118_L1;flt20170118_L2;flt20170126_L1;flt20170126_L2;flt20170127_L1;flt20170127_L2;flt20170128_L1;flt20170128_L2;flt20170130_L1;flt20170130_L2;flt20170131_L1;flt20170131_L2;flt20170201;flt20170208_L1;flt20170208_L2;flt20170209;flt20170211_L1;flt20170211_L2;flt20170212_L1;flt20170212_L2" If(vLoadType == 1) //Loads all the Twin Otter flights strLoadType = "flt20170116;flt20170117_L1;flt20170117_L2;flt20170118_L1;flt20170118_L2;flt20170126_L1;flt20170126_L2;flt20170127_L1;flt20170127_L2;flt20170128_L1;flt20170128_L2;flt20170130_L1;flt20170130_L2;flt20170131_L1;flt20170131_L2;flt20170201;flt20170208_L1;flt20170208_L2;flt20170209;flt20170211_L1;flt20170211_L2;flt20170212_L1;flt20170212_L2" ElseIf(vLoadType == 2) //Loads a single flight (user defined) Variable vSingleFlight String strFlightOptions = "flt20170116;flt20170117_L1;flt20170117_L2;flt20170118_L1;flt20170118_L2;flt20170126_L1;flt20170126_L2;flt20170127_L1;flt20170127_L2;flt20170128_L1;flt20170128_L2;flt20170130_L1;flt20170130_L2;flt20170131_L1;flt20170131_L2;flt20170201;flt20170208_L1;flt20170208_L2;flt20170209;flt20170211_L1;flt20170211_L2;flt20170212_L1;flt20170212_L2" Prompt vSingleFlight, "Which one?", popup, strFlightOptions DoPrompt "Pick a flight", vSingleFlight strLoadType = StringFromList(vSingleFlight - 1, strFlightOptions) ElseIf(vLoadType == 3) //Loads just the night flights strLoadType = "flt20170208_L1;flt20170208_L2;flt20170211_L1;flt20170212_L1" ElseIf(vLoadType == 4) //Loads just the photochemically active flights strLoadType = "flt20170116;flt20170117_L2;flt20170118_L1;flt20170118_L2;flt20170126_L1;flt20170126_L2;flt20170127_L2;flt20170128_L1;flt20170130_L2;flt20170131_L1;flt20170131_L2" ElseIf(vLoadType == 5) Variable vSingleDay, iDex String strDayOptions = "20170116;20170117;20170118;20170126;20170127;20170128;20170131" Prompt vSingleDay, "Which one?", popup, strDayOptions DoPrompt "Pick a model day", vSingleDay String strList = "" For(iDex = 0; iDex < ItemsInList(strLoadType); iDex += 1) If(stringmatch(StringfromList(iDex, strLoadType), "flt" + StringFromList(vSingleDay - 1, strDayOptions) + "*") == 1) strList = AddListItem(StringfromList(iDex, strLoadType), strList, ";", inf) EndIf EndFor strLoadType = strList EndIf EndIf Variable vNumFlights = ItemsInList(strLoadType) Make/O/N=(vNumFlights)/T twFlightName = StringFromList(p, strLoadType) Make/O/N=(vNumFlights)/T twFlightData_Rev, twAMS_Rev, twNH3_Rev, twNOxCaRD_Rev, twUHSAS_Rev, twUWTOFCIMS1_Rev DoLoadICARTT() //Does the work of loading all flights in twFlightName ShortenUHSASNames() //Shortens the UHSAS names // LoadValleyBoundaryWaves() //Loads valley boundary waves LoadUtahMaps() //Loads useful maps MakeAdditionalFlightWaves() //Make Altitude (m agl), Ox, NOx, resets CIMS waves from ppt to ppb LoadOlderICARTT() //Loads previous ICARTT file for NOxCaRD and CIMS, so we can use that data CorrectNOxCaRDforHalogens() //Redo Dorothy's correction of the NO2 and NOy data for halogens, since she deleted some useful data in R1 CopyInHalogenOxides() //Copy the BrO and ClO waves into the main data folders End Function DoLoadICARTT() SetDataFolder root: PathInfo pICARTTbasepath //Get a symbolic path to where all the ICARTT files are If(V_flag == 0) NewPath/M="Select base path for ICARTT data\r"/Q pICARTTbasepath, "C:Users:cwomack:Documents:Research Projects:UWFPS:ICARTT Files" If(V_flag != 0) //If user cancels Abort EndIf PathInfo pICARTTbasepath EndIf Wave/T twFlightName Variable vNumTotalFlights = numpnts(twFlightName) Variable iFlightDex, iFileDex, vNumICARTTFiles String strCurrentFlight, strFileLocation, strFileList, strCurrentFile, strInstrumentID, strDateFromFile, strDateFromFolder, strRevision For(iFlightDex = 0; iFlightDex < vNumTotalFlights; iFlightDex += 1) //Loop through all flights strCurrentFlight = twFlightName[iFlightDex] //Get the flight name from twFlightName NewDataFolder/O/S root:$(strCurrentFlight) //Make a new data folder for this flight strFileLocation = S_path + strCurrentFlight //Set the new file location to the base path + the current folder's name NewPath/Q pFileLocation, strFileLocation //Set this to be the new path strFileList = IndexedFile(pFileLocation,-1,".ict") //Find all the ICARTT files in that folder vNumICARTTFiles = ItemsInList(strFileList) //Count the ICARTT files Printf "Loading %g ICARTT files from %s\r", vNumICARTTFiles, strFileLocation For(iFileDex = 0; iFileDex < vNumICARTTFiles; iFileDex += 1) //Loop through the ICARTT files strCurrentFile = StringFromList(iFileDex, strFileList) //Get the ICARTT file name strInstrumentID = StringFromList(0, strCurrentFile, "_") //Get instrument ID (the string before the first underscore) strDateFromFile = StringFromList(2, strCurrentFile, "_") //Get the date (the string between the second and third underscores) strDateFromFolder = strCurrentFlight[3,10] //Get the folder's date, and make sure the file is in the right folder If(StringMatch(strDateFromFile, strDateFromFolder) != 1) Abort "Found file entitled %s in folder entitled %s. Date mismatch, check for errors" EndIf strRevision = StringFromList(3, strCurrentFile, "_") //Get the revision number (the string after the third underscore) strRevision = ReplaceString(".ict", strRevision, "") Wave/T wInstrumID = $("root:tw" + strInstrumentID + "_Rev") //Pull up the instrument's revision wave and store RA, RB, etc. as the current revision wInstrumID[iFlightDex] = strRevision strCurrentFile = strFileLocation + ":" + strCurrentFile //Add the file location to the name LoadICARTT1001_UWFPS(strCurrentFile) //Load the file (using the special UWFPS versions of the ICARTT functions, below) EndFor KillPath/Z pFileLocation //Kill the previous loop's file location SetDataFolder root: EndFor End Function ShortenUHSASNames() //The UHSAS waves have 18- and 19-character names, which is fine now, but later on we make copies and need more space for suffixes //Shorten names to something still meaningful: NumConc_n, SurfConc_um2, VolConc_um3 SetDataFolder root: Wave/T twFlightName Variable vNumFlights = numpnts(twFlightName) Variable iFlightDex String sCurrentFlight For(iFlightDex = 0; iFlightDex < vNumFlights; iFlightDex += 1) sCurrentFlight = twFlightName[iFlightDex] SetDataFolder root:$(sCurrentFlight) Wave NumConcTotal_n_cm3, SurfConcTotal_um2_cm3, VolConcTotal_um3_cm3 If(WaveExists(NumConcTotal_n_cm3) == 1) Rename NumConcTotal_n_cm3 NumConc_n EndIf If(WaveExists(SurfConcTotal_um2_cm3) == 1) Rename SurfConcTotal_um2_cm3 SurfConc_um2 EndIf If(WaveExists(VolConcTotal_um3_cm3) == 1) Rename VolConcTotal_um3_cm3 VolConc_um3 EndIf EndFor Printf "Shortening UHSAS names\r" End Function LoadUtahMaps() //A function for loading all the Utah map Igor waves NewDataFolder/O/S root:UtahMaps //Make a new data folder to store them KillPath/Z pUtahMaps //If there is already a path, kill it // String sValleyBoundaries = "Macintosh HD:Users:wyndomchace:NOAA Research:Utah Map Data" //Optional. Comment this line out and it will give a dialog to choose folder String sValleyBoundaries = "C:Users:cwomack:Documents:Research Projects:UWFPS:UWFPS analysis tools:Utah Map Data" //Optional. Comment this line out and it will give a dialog to choose folder NewPath/Q/M="Select folder that contains the Utah map .ibw Igor waves you want to load" pUtahMaps, sValleyBoundaries //Ask the user to select the folder that contains the .ibw waves //NewPath/Q/M="Select folder that contains the Utah map .ibw Igor waves you want to load" pUtahMaps If(V_flag != 0) Abort EndIf PathInfo pUtahMaps Printf "Utah maps loaded from %s\r", S_path String sWaveList = IndexedFile(pUtahMaps, -1, ".ibw") //Make a list of .ibw waves available Variable vNumFiles = ItemsInList(sWaveList) //Count them Variable iDex String sCurrentFile If(strlen(sWaveList) != 0) //Loop through list of waves, loading each one For(iDex = 0; iDex < vNumFiles; iDex += 1) sCurrentFile = StringFromList(iDex, sWaveList) LoadWave/O/Q/H/P=pUtahMaps sCurrentFile EndFor EndIf //Igor is weird and the .ibw wave name isn't always the same as the wave name it loads. As a results // it loads the elevation and elevation_nolake waves as the same wave //So rename the one that loaded as the nolake one, and reload the one with the lake. Wave CO_UT_WY_Elevation Duplicate/O CO_UT_WY_Elevation CO_UT_WY_Elevation_NoLake LoadWave/O/Q/H/P=pUtahMaps "CO_UT_WY_Elevation" //Also, it's helpful to have a version of the elevation map that is just the area relevant to UWFPS, so make that here Variable vMaxLat = 42.5 Variable vMinLat = 39.5 Variable vMaxLon = -111.5 Variable vMinLon = -113 Duplicate/O/R=(vMinLon, vMaxLon)(vMaxLat, vMinLat) CO_UT_WY_Elevation CO_UT_WY_Elevation_UWFPS //The loaded Airport codes and lat/lon include some airports that we didn't go to. So make a UWFPS version, with the 8 airports we did go to, plus a two letter code I'll use later Wave Air_Lat, Air_Lon Make/O/N=8 Air_Lat_UWFPS, Air_Lon_UWFPS Make/O/N=8/T Air_Name_UWFPS Air_Name_UWFPS[0] = "SL" //Salt Lake International Air_Name_UWFPS[1] = "SV" //South Valley Air_Name_UWFPS[2] = "PR" //Provo Air_Name_UWFPS[3] = "SF" //Spanish Fork Air_Name_UWFPS[4] = "BO" //Bollinder Air_Name_UWFPS[5] = "OG" //Ogden Air_Name_UWFPS[6] = "BC" //Brigham City Air_Name_UWFPS[7] = "LO" //Logan Air_Lat_UWFPS[0] = Air_Lat[0] Air_Lat_UWFPS[1] = Air_Lat[1] Air_Lat_UWFPS[2] = Air_Lat[3] Air_Lat_UWFPS[3] = Air_Lat[4] Air_Lat_UWFPS[4] = Air_Lat[5] Air_Lat_UWFPS[5] = Air_Lat[9] Air_Lat_UWFPS[6] = Air_Lat[10] Air_Lat_UWFPS[7] = Air_Lat[11] Air_Lon_UWFPS[0] = Air_Lon[0] Air_Lon_UWFPS[1] = Air_Lon[1] Air_Lon_UWFPS[2] = Air_Lon[3] Air_Lon_UWFPS[3] = Air_Lon[4] Air_Lon_UWFPS[4] = Air_Lon[5] Air_Lon_UWFPS[5] = Air_Lon[9] Air_Lon_UWFPS[6] = Air_Lon[10] Air_Lon_UWFPS[7] = Air_Lon[11] SetDataFolder root: End Function MakeAdditionalFlightWaves() //Makes other waves that might be useful to us //Note: This version is for the halogens analysis, so includes fewer of the NOx, Ox waves than the original SetDataFolder root: Wave/T twFlightName Variable vNumFlights = numpnts(twFlightName) Variable iFlightDex, vNumTimePoints, iWaveDex, vNumWaves String sCurrentFlight, sCurrentWave, sNewWave, sListpptWaves For(iFlightDex = 0; iFlightDex < vNumFlights; iFlightDex += 1) sCurrentFlight = twFlightName[iFlightDex] SetDataFolder root:$sCurrentFlight //Convert CIMS ppt waves to ppbv, which just makes everything easier sListpptWaves = WaveList("*_ppt*", ";", "") vNumWaves = ItemsInList(sListpptWaves) For(iWaveDex = 0; iWaveDex < vNumWaves; iWaveDex += 1) sCurrentWave = StringFromList(iWaveDex, sListpptWaves) sNewWave = ReplaceString("_ppt", sCurrentWave, "_ppbv") Wave wCurrentWave = $sCurrentWave Rename wCurrentWave $sNewWave wCurrentWave /= 1000 EndFor //Calculate odd oxygen and nitrogen waves Wave O3_ppbv, NO2_ppbv, NO_ppbv, N2O5_ppbv, HNO3_ppbv Wave NOy_ppbv, ClNO2_ppbv, AMS_NO3, TO_Timewave_UTC Make/O/N=(numpnts(O3_ppbv)) NOx_ppbv, NOz_ppbv, Ox_ppbv//, Oy_ppbv, Oynight_ppbv, Oz_ppbv, Oznight_ppbv, NOzsum_ppbv, NOznight_ppbv NOx_ppbv = NO2_ppbv + NO_ppbv NOz_ppbv = NOy_ppbv - NOx_ppbv Ox_ppbv = O3_ppbv + NO2_ppbv //Calculate altitude above ground level Wave GndElev, GPSAlt Make/O/N=(numpnts(GPSAlt)) GPSAlt_mgl GPSAlt_mgl = GPSAlt - GndElev GPSAlt_mgl = GPSAlt_mgl[p] < 0 ? 0 : GPSAlt_mgl[p] //Hmm, it appears sometimes we went underground during missed approaches. Set these to 0, cause that probably didn't happen... EndFor SetDataFolder root: End Function LoadOlderICARTT() //Both NOxCaRD and the CIMS have some valuable information in previous ICARTT files. Load those into a subfolder SetDataFolder root: PathInfo pICARTTbasepath If(V_flag == 0) Abort "You must load data before overwriting it!" Endif String strBasePath = S_path + "Old ICARTT Files:RA and some R0:" Wave/T twFlightName Variable iFlightDex String strCurrentFlight For(iFlightDex = 0; iFlightDex < numpnts(twFlightName); iFlightDex +=1 ) strCurrentFlight = twFlightName[iFlightDex] //Get the flight name from twFlightName String strDate = StringFromList(0, ReplaceString("flt", strCurrentFlight, ""), "_") String strLeg = StringFromList(1, ReplaceString("flt", strCurrentFlight, ""), "_") If(strlen(strLeg) > 0) strLeg = "_" + strLeg EndIf //We had an R0 for every flight, so load that directly NewDataFolder/O/S root:$(strCurrentFlight):NOxCaRD_R0 //Make a new data folder for this flight LoadICARTT1001_UWFPS(strBasePath + strCurrentFlight + ":NOxCaRD_TO_" + strDate + "_R0" + strLeg + ".ict") //The CIMS had a variety of versions, so load the most recent in the "Old ICARTTs" file NewPath/Q pOldICARTTS, strBasePath + strCurrentFlight String strFileList = IndexedFile(pOldICARTTS, -1, ".ict") //Get all the ICARTT files KillPath/Z pOldICARTTS String strCIMSList = ListMatch(strFileList, "UWTOFCIMS1*") //Get just those that are CIMS ICARTTS If(strlen(strCIMSList) > 0) strCIMSList = SortList(strCIMSList, ";", 16) //Sort alphanumerically (i.e. RA, RB, R0, R1, etc.) String strCIMSFile = StringFromList(ItemsInList(strCIMSList) - 1, strCIMSList) //Get the last one in the list String strRevision = StringFromList(3, strCIMSFile, "_") //Pull the revision number strRevision = ReplaceString(".ict", strRevision, "") NewDataFolder/O/S $("root:" + strCurrentFlight + ":CIMS_" + strRevision) //Make a new data folder for this flight LoadICARTT1001_UWFPS(strBasePath + strCurrentFlight + ":UWTOFCIMS1_TO_" + strDate + "_" + strRevision + strLeg + ".ict") Else Printf "No previous CIMS ICARTT files found in %s\r", strCurrentFlight EndIf EndFor End Function CorrectNOxCaRDforHalogens() //Redo the correction of NOxCaRD data for halogens, since when Dorothy did it, she deleted a bunch of plumes near US Magnesium. SetDataFolder root: Wave/T twFlightName Variable iFlightDex String strCurrentFlight //These are the values from the JPL recommended cross sections Variable vXS_NO2_405 = 5.82e-19 //http://satellite.mpic.de/spectral_atlas/cross_sections/Nitrogen%20oxides/NO2_JPL-2010(2011)_294K_240-662.5nm(rec).txt Variable vXS_Cl2_405 = 1.56e-20 //Average of 400 and 410 nm Variable vXS_Br2_405 = 6.16e-19 Variable vXS_BrCl_405 = 2.54e-19 For(iFlightDex = 0; iFlightDex < numpnts(twFlightName); iFlightDex +=1 ) strCurrentFlight = twFlightName[iFlightDex] Wave wCl2 = $("root:" + strCurrentFlight + ":Cl2_ppbv") Wave wBr2 = $("root:" + strCurrentFlight + ":Br2_ppbv") Wave wBrCl = $("root:" + strCurrentFlight + ":BrCl_ppbv") Wave wNO2_R1 = $("root:" + strCurrentFlight + ":NO2_ppbv_R1") Wave wNO2_R0 = $("root:" + strCurrentFlight + ":NOxCaRD_R0:NO2_ppbv") Wave wNO2 = $("root:" + strCurrentFlight + ":NO2_ppbv") If(WaveExists(wNO2_R1) == 0) //i.e. if this correction hasn't already been made Make/O/N=(numpnts(wNO2)) $("root:" + strCurrentFlight + ":NO2_ppbv_R1")/WAVE=wNO2_R1 wNO2_R1 = wNO2 String strNote = note(wNO2) Note wNO2_R1, strNote wNO2 = wNO2_R0 - wCl2*(vXS_Cl2_405 / vXS_NO2_405) - wBr2*(vXS_Br2_405 / vXS_NO2_405) - wBrCl*(vXS_BrCl_405 / vXS_NO2_405)//Make correction EndIf //Repeat for NOy Wave wNOy_R1 = $("root:" + strCurrentFlight + ":NOy_ppbv_R1") Wave wNOy_R0 = $("root:" + strCurrentFlight + ":NOxCaRD_R0:NOy_ppbv") Wave wNOy = $("root:" + strCurrentFlight + ":NOy_ppbv") If(WaveExists(wNOy_R1) == 0) Make/O/N=(numpnts(wNOy)) $("root:" + strCurrentFlight + ":NOy_ppbv_R1")/WAVE=wNOy_R1 wNOy_R1 = wNOy strNote = note(wNOy) Note wNOy_R1, strNote wNOy = wNOy_R0 - wCl2*(vXS_Cl2_405 / vXS_NO2_405) - wBr2*(vXS_Br2_405 / vXS_NO2_405) - wBrCl*(vXS_BrCl_405 / vXS_NO2_405) EndIf //Repeat for NO If(StringMatch(strCurrentFlight, "flt20170127_L2") == 1) Wave wNO_R1 = $("root:" + strCurrentFlight + ":NO_ppbv_R1") Wave wNO_R0 = $("root:" + strCurrentFlight + ":NOxCaRD_R0:NO_ppbv") Wave wNO = $("root:" + strCurrentFlight + ":NO_ppbv") If(WaveExists(wNO_R1) == 0) Make/O/N=(numpnts(wNO)) $("root:" + strCurrentFlight + ":NO_ppbv_R1")/WAVE=wNO_R1 wNO_R1 = wNO strNote = note(wNO) Note wNO_R1, strNote wNO = wNO_R0 // wNO = wNO_R0 - wCl2*(vXS_Cl2_405 / vXS_NO2_405) - wBr2*(vXS_Br2_405 / vXS_NO2_405) - wBrCl*(vXS_BrCl_405 / vXS_NO2_405) EndIf EndIf //Now recalculate Ox and NOx Wave wNOx = $("root:" + strCurrentFlight + ":NOx_ppbv") Wave wOx = $("root:" + strCurrentFlight + ":Ox_ppbv") Wave wNOz = $("root:" + strCurrentFlight + ":NOz_ppbv") Wave wNO = $("root:" + strCurrentFlight + ":NO_ppbv") Wave wO3 = $("root:" + strCurrentFlight + ":O3_ppbv") wNOx = wNO + wNO2 wOx = wO3 + wNO2 wNOz = wNOy - wNOx EndFor End Function CopyInHalogenOxides() //Duplicate the uncalibrated ClO and BrO waves from the older ICARTT folder into the main flight folders SetDataFolder root: Wave/T twFlightName Variable iFlightDex String strCurrentFlight For(iFlightDex = 0; iFlightDex < numpnts(twFlightName); iFlightDex +=1 ) strCurrentFlight = twFlightName[iFlightDex] SetDataFolder $("root:" + strCurrentFlight) String strFolderList = DataFolderDir(1) // strFolderList = ReplaceString("FOLDERS:", strFolderList, "") Variable vStart = strsearch(strFolderList, "CIMS_R", 0) String strFolder = strFolderList[vStart, vStart + 6] SetDataFolder $("root:" + strCurrentFlight + ":" + strFolder) // String strTest = StringFromList(0, WaveList("ClO_*", ";", ""), ";") Wave wClO = $("root:" + strCurrentFlight + ":" + strFolder + ":" + StringFromList(0, WaveList("ClO_*", ";", ""), ";")) Wave wBrO = $("root:" + strCurrentFlight + ":" + strFolder + ":" + StringFromList(0, WaveList("BrO_*", ";", ""), ";")) Make/O/N=(numpnts(wClO)) $("root:" + strCurrentFlight + ":ClO_cps")/WAVE=wClO_cps wClO_cps = wClO String strNote = note(wClO) Note wClO_cps, strNote Make/O/N=(numpnts(wBrO)) $("root:" + strCurrentFlight + ":BrO_cps")/WAVE=wBrO_cps wBrO_cps = wBrO strNote = note(wBrO) Note wBrO_cps, strNote EndFor End //************Miscellaneous**********************// Function GetTakeoffLandingTimes() //Compile a list of takeoff and landing times for each flight. SetDataFolder root: Wave/T twFlightName Variable iFlightDex String strCurrentFlight Make/O/N=(numpnts(twFlightName))/D TakeoffTime_MST, LandingTime_MST SetScale d, 0, 0, "dat", TakeoffTime_MST, LandingTime_MST For(iFlightDex = 0; iFlightDex < numpnts(twFlightName); iFlightDex +=1 ) Wave wTime = $("root:" + twFlightName[iFlightDex] + ":TO_Timewave_MST") TakeoffTime_MST[iFlightDex] = wTime[0] LandingTime_MST[iFlightDex] = wTime[numpnts(wTime) - 1] EndFor End Function CountBinarySetToOne(wBinary) Wave wBinary Variable vNumPnts = numpnts(wBinary) Variable iDex Variable vNumOnes = 0 For(iDex = 0; iDex < vNumPnts; iDex += 1) If(wBinary[iDex] == 1) vNumOnes += 1 Do iDex += 1 While(wBinary[iDex] == 1 && iDex < vNumPnts - 1) EndIf EndFor Return vNumOnes End Function FillNaNsWithInterpData(wDataWave, wTimewave) Wave wDataWave, wTimewave Variable vStartNaN, vStopNaN, vSlope, vBaseValue, vBaseTime Do vStartNaN = FindFirstNaN(wDataWave, 0) If(vStartNaN == -1 || vStartNaN == numpnts(wDataWave)) Break EndIf vStopNaN = FindFirst(wDataWave, vStartNaN) If(vStartNaN == 0 && vStopNaN == numpnts(wTimewave)) Abort "All points are NaN. No interpolation is possible" ElseIf(vStartNaN == 0) wDataWave[vStartNaN, vStopNaN - 1] = wDataWave[vStopNaN] ElseIf(vStopNaN == numpnts(wTimewave)) wDataWave[vStartNaN, numpnts(wDataWave) - 1] = wDataWave[vStartNaN - 1] Else vSlope = (wDataWave[vStopNaN] - wDataWave[vStartNaN - 1])/(wTimewave[vStopNaN] - wTimewave[vStartNaN - 1]) vBaseValue = wDataWave[vStartNaN - 1] vBaseTime = wTimewave[vStartNaN - 1] wDataWave[vStartNaN, vStopNaN - 1] = vSlope * (wTimewave[p] - vBaseTime) + vBaseValue EndIf While(vStartNaN < numpnts(wDataWave)) End Function DistanceBtwnLatLon(Lat1, Lon1, Lat2, Lon2) variable Lat1, Lon1, Lat2, Lon2 //Function assumes north latitudes and west longitudes, both expressed as positive numbers in radians variable Angle, Distance variable Re = 6378 //radius of the earth in km Angle = acos(cos(lat1)*cos(lon1)*cos(lat2)*cos(lon2) + cos(lat1)*sin(lon1)*cos(lat2)*sin(lon2) +sin(lat1)*sin(lat2)) Distance = Re*Angle Return Distance End Function SZA_Calc(lat, lon, Year, twave, offset) //Taken from Arnold 2015 macros. Not rigorously checked. variable lat, lon, Year, twave, offset //lattitude, longitude in degrees, calendar year, local time in seconds since 1/1/04, offset in hours to UTC variable Day, Hour variable gam, b0, b1, b2, b3, b4, b5, b6, EQT,c0, c1, c2, c3, c4, delta, w Day = floor((twave + offset*3600 - date2secs(Year,1,1))/86400) //integer day number Hour = ((twave + offset*3600 - date2secs(Year, 1, 1) - Day*86400)/86400)*24 //decimal hour number //Calculate solar declination (+/- 23 degrees) according to empirical formula if (mod(Year, 4) == 0) //Test for leap year gam = 2*pi*(Day -1)/366 else gam = 2*pi*(Day -1)/365 endif b0 = 0.006918 b1 = -0.399912 b2 = 0.070257 b3 = -0.006758 b4 = 0.000907 b5 = -0.002697 b6 = 0.00148 delta = b0 + b1*cos(gam) + b2*sin(gam) + b3*cos(2*gam) + b4*sin(2*gam) + b5*cos(3*gam) + b6*sin(3*gam) //Calculate hour angle c0 = 7.5e-5 c1 = 1.868e-3 c2 = -0.032077 c3 = -0.014615 c4 = -0.040849 EQT = c0 + c1*cos(gam) + c2*sin(gam) + c3*cos(2*gam) + c4*sin(2*gam) w = pi*(Hour/12 - 1 + lon/180) + EQT //Solar Zenith Angle in degrees return (180/pi)*acos(sin(delta)*sin(lat*pi/180) + cos(delta)*cos(lat*pi/180)*cos(w)) End Function FilterBinaryWaveForSpikes(wWave, vMinPnts, vSpikeValue, vSetTo) //Function for getting rid of any short spikes in a binary wave (all values 1 or 0) //wWave = binary wave, which will be overwritten //vMinPnts = minimum number of points of continuous 1 or 0 values that must be exist to count as valid Wave wWave Variable vMinPnts, vSpikeValue, vSetTo Variable vNumpnts = numpnts(wWave) Variable iDex, vStart, vStop For(iDex = 0; iDex < vNumpnts; iDex += 1) If(wWave[iDex] == vSpikeValue) //Find where wave switches to 1 vStart = iDex Do iDex += 1 While(wWave[iDex] == vSpikeValue && iDex < vNumpnts - 1) //Find where wave switches to 0 If(iDex - vStart < vMinPnts) //If the difference between these points is smaller than vMinPnts, set all values to 0 wWave[vStart, iDex] = vSetTo EndIf EndIf EndFor End Function DoVectorsCross(vX, vY0, vY1, vXa, vXb, vYa, vYb) //Function for determining whether a vector crosses a vertical line //Vertical line has coordinates (vX, vY0) to (vX, vY1) //Vector has coordinates (vXa, vYa) to (vXb, vYb) //Two parts to this function: First, check where vXa and vXb are, relative to vX //If the are both smaller, or both larger, the vector cannot cross the vertical line, and function returns 0 //Second, if vXa and vXb are on opposite sides of vX, it's possible, but not guaranteed that the vector crosses the vertical line //So check by making an infinite line that is colinear with the vector (A*x + B*y + C = 0) where A = (vYb - vYa) and B = (vXa - vXb) //and C = vXb*vYa - vXa*vYb. Plug in (vX, vY0) and (vX, vY1) into this equation to find whether the result is postive (above the line) //or negative (below the line). If they have opposite signs, the vertical line must cross that vector. //See https://stackoverflow.com/questions/217578/how-can-i-determine-whether-a-2d-point-is-within-a-polygon for more details Variable vX, vY0, vY1, vXa, vXb, vYa, vYb Variable d1, d2 //Part one: check if vXa and vXb are on opposite sides of vX. If not, no cross occurs If(vXa < vX && vXb < vX) Return 0 ElseIf(vXa > vX && vXb > vX) Return 0 Else //Part two: Make the infinite line equation, and plug in both ends of the vertical line d1 = (vYb - vYa)*vX + (vXa - vXb)*vY1 + vXb*vYa - vXa*vYb d2 = (vYb - vYa)*vX + (vXa - vXb)*vY0 + vXb*vYa - vXa*vYb If(d1 > 0 && d2 > 0) //If both are positive or both negative, no cross occurs Return 0 ElseIf(d1 < 0 && d2 < 0) Return 0 Else //If they have opposite signs, check that the infinite line and vertical line aren't colinear d1 = (vY1 - vY0)*(vXa - vXb) If(abs(d1) < 1e-6) Return NaN Else Return 1 //If all other checks are false, then the vector crosses the vertical line! EndIf EndIf EndIf End Function AverageAMSDataInValley() SetDataFolder root: Wave/T twFlightName Variable vNumFlights = numpnts(twFlightName) Variable iFlightDex, iWaveDex String strWaves = "AMS_Chl;AMS_NH4;AMS_NO3;AMS_SO4;AMS_Org;AMS_Total;SurfConc_um2;NumConc_n;VolConc_um3;NOx_ppbv" For(iWaveDex = 0; iWaveDex < ItemsInList(strWaves); iWaveDex += 1) Make/O/N=(vNumFlights) $("root:" + StringFromList(iWaveDex, strWaves) + "_Avg_GSL") Make/O/N=(vNumFlights) $("root:" + StringFromList(iWaveDex, strWaves) + "_Avg_SLC") Make/O/N=(vNumFlights) $("root:" + StringFromList(iWaveDex, strWaves) + "_Ratio") EndFor Make/O/N=(vNumFlights) FlightNumber = p For(iFlightDex = 0; iFlightDex < vNumFlights; iFlightDex += 1) //Loop through data folders String sCurrentFlight = twFlightName[iFlightDex] SetDataFolder root: If(DataFolderExists(sCurrentFlight) == 0) //Make sure there is data for each flight Printf "Flight %s not found\r", sCurrentFlight Abort EndIf // NewDataFolder/O/S root:$(sCurrentFlight):A //Go into that folder Wave wFlag_GSL = $("root:" + sCurrentFlight + ":Flags:Flag_GSL_BL") Wave wFlag_SLC = $("root:" + sCurrentFlight + ":Flags:Flag_SLC_BL") For(iWaveDex = 0; iWaveDex < ItemsInList(strWaves); iWaveDex += 1) Wave wCurrentWave = $("root:" + sCurrentFlight + ":" + StringFromList(iWaveDex, strWaves)) Wave wGSLWave = $("root:" + StringFromList(iWaveDex, strWaves) + "_Avg_GSL") Wave wSLCWave = $("root:" + StringFromList(iWaveDex, strWaves) + "_Avg_SLC") Wave wRatioWave = $("root:" + StringFromList(iWaveDex, strWaves) + "_Ratio") Make/O/N=(numpnts(wCurrentWave))/FREE TempWave TempWave = wFlag_GSL[p] == 1 ? wCurrentWave[p] : NaN WaveStats/Q TempWave wGSLWave[iFlightDex] = V_avg TempWave = wFlag_SLC[p] == 1 ? wCurrentWave[p] : NaN WaveStats/Q TempWave wSLCWave[iFlightDex] = V_avg wRatioWave[iFlightDex] = wSLCWave[iFlightDex] / wGSLWave[iFlightDex] EndFor EndFor End Function GetIntegratedSolarRad() SetDataFolder root:AllFlights Wave Plume_All_Time, Plume_All_Age Wave Timewave_MST = root:GS:Timewave_MST Wave SR = root:GS:SR_interp Variable vNumPlumes = numpnts(Plume_All_Time) Make/O/N=(vNumPlumes) Plume_All_IntegSR Variable iDex For(iDex = 0; iDex < vNumPlumes; iDex += 1) Variable vStart = Plume_All_Time[iDex]-7*3600 - Plume_All_Age[iDex]*60 Variable vStop = Plume_All_Time[iDex] - 7*3600 Variable vStartDex = BinarySearch(Timewave_MST, vStart) + 1 Variable vStopDex = BinarySearch(Timewave_MST, vStop) Plume_All_IntegSR[iDex] = Sum(SR, vStartDex, vStopDex) EndFor End Function GetIntegratedSolarRadByFlight() SetDataFolder root: Wave Timewave_MST = root:GS:Timewave_MST Wave SR = root:GS:SR_interp Wave/T twFlightName Variable iFlightDex For(iFlightDex = 0; iFlightDex < numpnts(twFlightName); iFlightDex += 1) String strCurrentFlight = twFlightName[iFlightDex] SetDataFolder $("root:" + strCurrentFlight + ":Plumes") Wave Plume_Time_Gauss, Plume_Age Variable vNumPlumes = numpnts(Plume_Time_Gauss) Make/O/N=(vNumPlumes) Plume_IntegSR Variable iDex For(iDex = 0; iDex < vNumPlumes; iDex += 1) Variable vStart = Plume_Time_Gauss[iDex]-7*3600 - Plume_Age[iDex]*60 Variable vStop = Plume_Time_Gauss[iDex] - 7*3600 Variable vStartDex = BinarySearch(Timewave_MST, vStart) + 1 Variable vStopDex = BinarySearch(Timewave_MST, vStop) Plume_IntegSR[iDex] = Sum(SR, vStartDex, vStopDex) EndFor EndFor End Function DoAllBoxModelPrep() LoadSTILTtrajs("") CalculateDistToUSMag("") GetWindFromSTILT("") MakeAvgWindSpdDir() KillTrajMatrices("") LoadPlumeIndexes() GaussFitPlumes() GetPlumeAges() LoadGSdataForF0AM() PrepareBoxModelInput_Puff() PrintVOCProfile() End //*************Process STILT data*******************// //One time only functions for converting STILT files Function BatchConvertSTILTcsv() //Convert many STILT .csv files to their .ibw counterparts // String strTrajectories = "C:Users:cwomack:Documents:Research Projects:UWFPS:Halogens analysis:STILT" //Set folder where the .csv files are String strTrajectories = "D:UWFPS_STILT:" //Set folder where the .csv files are NewPath/O/Q pTrajectories, strTrajectories String strFolderList = IndexedDir(pTrajectories, -1, 0) Variable vNumFolders = ItemsInList(strFolderList) Variable iFolderDex For(iFolderDex = 0; iFolderDex < vNumFolders; iFolderDex += 1) Print "Currently converting files from " + StringFromList(iFolderDex, strFolderList) NewPath/O/Q pTrajectories, strTrajectories + StringFromList(iFolderDex, strFolderList) + ":CSVs" String strFileList = IndexedFile(pTrajectories, -1, ".csv") Variable iFileDex For(iFileDex = 0; iFileDex < ItemsInList(strFileList); iFileDex += 1) String strCurrentFile = strTrajectories + StringFromList(iFolderDex, strFolderList) + ":CSVs:" + StringFromList(iFileDex, strFileList) //Loop through files, converting each one ConvertSTILTcsvToIbw(strCurrentFile) EndFor EndFor End Function ConvertSTILTcsvToIbw(strCSVname) //Does the work of converting the STILT trajectory file from .csv (where it is a 2D matrix) to a 3D matrix, then saves it as a .ibw file. String strCSVname // Variable vStartTime = StartMSTimer NewDataFolder/O/S root:TempFolder //Since this will all get deleted anyway, put it in a temp folder LoadWave/Q/J/W/N/O/K=0 strCSVname String strFileName = S_filename Wave particle_indx, particle_time Variable vNumTraj = 200 //John ran the STILT model to output 200 different particle trajectories Variable vNumTimePoints = 1441 //He also ran it up to 24 hours. Mostly at 2 minute intervals, but occasionally at 1 minute, so make this matrix have 1-min resolution (plus slot for time = 0) Variable vNumPnts = vNumTraj * vNumTimePoints Variable iDex, iParamDex //Names of the 24 parameters we will save String strHeader_FullName = "particle_long;particle_lati;particle_zagl;particle_sigw;particle_tlgr;particle_zsfc;particle_icdx;particle_temp;particle_samt;particle_foot;particle_shtf;particle_tcld;particle_dmas;particle_dens;particle_rhfr;particle_sphu;particle_solw;particle_lcld;particle_zloc;particle_dswf;particle_wout;particle_mlht;particle_rain;particle_crai;" Variable vNumParams = ItemsInList(strHeader_FullName) //Make a master index list that shows where to insert each point in the loaded wave into the 3D matrix. Make/O/N=(numpnts(particle_indx))/FREE MasterIndex = (particle_indx[p] - 1) + particle_time[p]*-1*vNumTraj For(iParamDex = 0; iParamDex < vNumParams; iParamDex += 1) Wave wCurrentWave = $(StringFromList(iParamDex, strHeader_FullName)) Make/O/N=(vNumTraj * vNumTimePoints)/FREE TempWave = NaN For(iDex = 0; iDex < numpnts(MasterIndex); iDex += 1) TempWave[MasterIndex[iDex]] = wCurrentWave[iDex] //Make the fully expanded wave (at 1 minute resolution), from the loaded wave (at variable time resolution) EndFor Redimension/N=(vNumTraj, vNumTimePoints) TempWave //Reform it into a 200 * 1441 2D matrix If(iParamDex == 0) Make/O/N=(vNumTraj, vNumTimePoints) $("TrajMatrix_Full_" + StringFromList(0, strFileName, "_"))/WAVE=TrajMatrix_Full TrajMatrix_Full = TempWave Else Concatenate/NP=2 {TempWave}, TrajMatrix_Full //Then add it to the master matrix EndIf EndFor //Add a wave note to explain what was done, for future reference Wave/T runtime Wave receptor_lati, receptor_long, receptor_zagl String strWaveNote = "STILT trajectory file " + strFileName + ". Original output converted from .rds format to .csv, and then to Igor binary\n" strWaveNote += "Trajectory initiated from Lat = " + num2str(receptor_lati[0]) + ", Lon = " + num2str(receptor_long[0]) + ", Alt = " + num2str(receptor_zagl[0]) + " m agl, Time = " + runtime[0] + "UTC\n" strWaveNote += "Matrix has 200 rows (1 per trajectory), 1441 columns (1 per minute in simulation for 0 - 24 hours), and 24 layers, as listed in order below\n" strWaveNote += "long: particle longitude position [degrees]\n" strWaveNote += "lati: particle latitude position [degrees]\n" strWaveNote += "zagl: particle vertical position [m above-ground-level]\n" strWaveNote += "sigw: standard deviation of vertical velocity [m/s]\n" strWaveNote += "tlgr: Lagrangian decorrelation timescale [s]\n" strWaveNote += "zsfc: terrain height [m]\n" strWaveNote += "icdx: cloud index (1=updraft, 2=environment, 3=downdraft)\n" strWaveNote += "temp: air temperature at lowest model layer [K]\n" strWaveNote += "samt: amount of time that particle spends below 'VEGHT' [min]\n" strWaveNote += "foot: sensitivity of mixing ratio to surface fluxes [ppm/(micro-moles/m2/s)]\n" strWaveNote += "shtf: sensible heat flux [W/m2]\n" strWaveNote += "tcld: total cloud cover [%]\n" strWaveNote += "dmas: particle weight--changes due to mass violation [init value = 1.0]\n" strWaveNote += "dens: air density [kg/m3]\n" strWaveNote += "rhfr: relative humidity fraction [0~1.0]\n" strWaveNote += "sphu: specific humidity [g/g]\n" strWaveNote += "solw: soil moisture \n" strWaveNote += "lcld: low cloud cover [%]\n" strWaveNote += "zloc: limit of convection heights [m]\n" strWaveNote += "dswf: downward shortwave radiation [W/m2]\n" strWaveNote += "wout: Not indicated in readme file\n" strWaveNote += "mlht: Not indicated in readme file\n" strWaveNote += "rain: Not indicated in readme file\n" strWaveNote += "crai: Not indicated in readme file\n" Note TrajMatrix_Full, strWaveNote String strCSVLocation = ParseFilePath(1, strCSVname, ":", 1, 0) String strIBWLocation = ReplaceString("CSVs", strCSVLocation, "IgorBinary") String strSaveMatrix = strIBWLocation + "TrajMatrix_Full_" + StringFromList(0, strFileName, "_") + ".ibw" Save/O TrajMatrix_Full as strSaveMatrix //Make an abbreviated version of the matrix, which just has the lat, lon, altitude, and terrain information. Mostly this is all that is needed. Make/O/N=(vNumTraj, vNumTimePoints, 4) $("TrajMatrix_" + StringFromList(0, strFileName, "_"))/WAVE=TrajMatrix TrajMatrix[][][] = TrajMatrix_Full[p][q][r] TrajMatrix[][][3] = TrajMatrix_Full[p][q][5] strWaveNote = "STILT trajectory file " + strFileName + ". Original output converted from .rds format to .csv, and then to Igor binary\n" strWaveNote += "Only lat, lon, and altitude are saved in this version, to reduce matrix size\n" strWaveNote += "Trajectory initiated from Lat = " + num2str(receptor_lati[0]) + ", Lon = " + num2str(receptor_long[0]) + ", Alt = " + num2str(receptor_zagl[0]) + " m agl, Time = " + runtime[0] + "UTC\n" strWaveNote += "Matrix has 200 rows (1 per trajectory), 1441 columns (1 per minute in simulation for 0 - 24 hours), and 4 layers, as listed in order below\n" strWaveNote += "long: particle longitude position [degrees]\n" strWaveNote += "lati: particle latitude position [degrees]\n" strWaveNote += "zagl: particle vertical position [m above-ground-level]\n" strWaveNote += "zsfc: terrain height [m]\n" Note TrajMatrix, strWaveNote strSaveMatrix = strIBWLocation + "TrajMatrix_" + StringFromList(0, strFileName, "_") + ".ibw" Save/O TrajMatrix as strSaveMatrix KillDataFolder/Z root:TempFolder // Printf "Total time = %g sec\r", StopMSTimer(vStartTime) / 1e6 End //Load STILT trajectories for plume analysis Function LoadSTILTtrajs(strFlightName) //Loads the STILT trajectory matrices. These should be in a folder called STILT, and organized by day String strFlightName SetDataFolder root: If(strlen(strFlightName) > 0) Make/O/N=1/FREE/T twTempFlightName twTempFlightName[0] = strFlightName Else Wave/T twTempFlightName = twFlightName Endif Variable vNumFlights = numpnts(twTempFlightName) Make/O/N=1 wUSMag_Lat, wUSMag_Lon //Make wave to hold the information about where the US Magnesium plant is wUSMag_Lat[0] = 40.9155 wUSMag_Lon[0] = -112.7337 Variable iFlightDex, iFileDex For(iFlightDex = 0; iFlightDex < vNumFlights; iFlightDex += 1) //Cycle through the flight days String strFolder = twTempFlightName[iFlightDex] SetDataFolder root:$(strFolder) NewDataFolder/O/S root:$(strFolder):STILT String strTrajectories = "C:Users:cwomack:Documents:Research Projects:UWFPS:Halogens analysis:STILT:" + strFolder NewPath/O/Q pTrajectories, strTrajectories String strFileList = IndexedFile(pTrajectories, -1, ".ibw") //Get list of the trajectory .ibw files in this folder Make/O/N=(ItemsInList(strFileList)) Traj_Lat, Traj_Lon, Traj_Alt_magl Make/O/N=(ItemsInList(strFileList))/T twTraj_Name Make/O/N=(ItemsInList(strFileList))/D Traj_Time SetScale d, 0, 0, "dat", Traj_Time For(iFileDex = 0; iFileDex < ItemsInList(strFileList); iFileDex += 1) //Cycle through the list of files String strCurrentFile = StringFromList(iFileDex, strFileList) String strCurrentFolder = RemoveEnding(StringFromList(1, strCurrentFile, "_"), ".ibw") NewDataFolder/O/S root:$(strFolder):STILT:$("Rec" + strCurrentFolder) //Make a folder for each receptor point LoadWave/H/O/Q strTrajectories + ":" + strCurrentFile Wave wTrajMatrix_fullname = $StringFromList(0, S_wavenames) String strWaveNote = note(wTrajMatrix_fullname) Make/O/N=(DimSize(wTrajMatrix_fullname, 0), DimSize(wTrajMatrix_fullname, 1), DimSize(wTrajMatrix_fullname, 2)) $("TrajMatrix")/WAVE=wTrajMatrix//Make a new version that is just called TrajMatrix. This makes everything easier later. wTrajMatrix = wTrajMatrix_fullname Note wTrajMatrix, strWaveNote KillWaves/Z wTrajMatrix_fullname //Fill in the first point in the matrix with the receptor point Variable vStart = strsearch(strWaveNote, "2017", 0) Variable vStop = strsearch(strWaveNote, ".csv", 0) String strFileName = strWaveNote[vStart, vStop - 1] Variable vLon = str2num(StringFromList(1, strFileName, "_")) Variable vLat = str2num(StringFromList(2, strFileName, "_")) Variable vAlt = str2num(StringFromList(3, strFileName, "_")) wTrajMatrix[][0][0] = vLon wTrajMatrix[][0][1] = vLat wTrajMatrix[][0][2] = vAlt //Fill in some information about this receptor point into the full list Traj_Lon[iFileDex] = vLon Traj_Lat[iFileDex] = vLat Traj_Alt_magl[iFileDex] = vAlt Traj_Time[iFileDex] = Date2Secs(str2num(strCurrentFolder[0,3]), str2num(strCurrentFolder[4,5]), str2num(strCurrentFolder[6,7])) + str2num(strCurrentFolder[8,9])*3600 + str2num(strCurrentFolder[10,11])*60 + 30 twTraj_Name[iFileDex] = strCurrentFolder EndFor EndFor End Function CalculateDistToUSMag(strFlightName) String strFlightName SetDataFolder root: If(strlen(strFlightName) > 0) Make/O/N=1/FREE/T twTempFlightName twTempFlightName[0] = strFlightName Else Wave/T twTempFlightName = twFlightName Endif // Wave/T twFlightName Wave wUSMag_Lat, wUSMag_Lon Variable iFlightDex, iFolderDex, iPartDex, iDex Variable vLat_USMag_rad = wUSMag_Lat[0]*pi/180 Variable vLon_USMag_rad = wUSMag_Lon[0]*pi/180 Variable vNumPntsInCircle = 7 Variable vDeg_to_km = 111.317 Variable vCone = 25 Variable vHeightCutoff = 400 For(iFlightDex = 0; iFlightDex < numpnts(twTempFlightName); iFlightDex += 1) String strFlight = twTempFlightName[iFlightDex] SetDataFolder root:$(strFlight):STILT Wave/T twTraj_Name Variable vNumFolders = numpnts(twTraj_Name) Make/O/N=(vNumFolders) Traj_Threshold // Make/O/N=(vNumFolders) Traj_FracPassUSMag, Traj_Threshold For(iFolderDex = 0; iFolderDex < vNumFolders; iFolderDex += 1) String strCurrentFolder = "Rec" + twTraj_Name[iFolderDex] SetDataFolder root:$(strFlight):STILT:$(strCurrentFolder) Wave TrajMatrix Variable vNumTrajParts = DimSize(TrajMatrix, 0) Variable vNumTimePoints = DimSize(TrajMatrix,1) Make/O/N=(vNumTrajParts) ParticleClosestDist, ParticleClosestTime, ParticleAltAtClosestPass Make/O/N=(vNumTimePoints)/FREE TempTraj Make/O/N=(vNumTrajParts, vNumTimePoints) TrajDistUSMag // TrajDistUSMag[][] = 6378 * sqrt(2) * sqrt(1-(sin(vLat_USMag_rad)*sin(TrajMatrix[p][q][1]*pi/180)*cos(-TrajMatrix[p][q][0]*pi/180 + vLon_USMag_rad) + cos(vLat_USMag_rad)*cos(TrajMatrix[p][q][1]*pi/180))) TrajDistUSMag[][] = DistanceBtwnLatLon(vLat_USMag_rad, vLon_USMag_rad, TrajMatrix[p][q][1]*pi/180, TrajMatrix[p][q][0]*pi/180) Variable vRadius = TrajDistUSMag[0][0] Variable vCircum = vRadius * 2 * pi Variable vThreshold = vCircum / (360 / vCone) Variable vXSpacing = vThreshold / vDeg_to_km / vNumPntsInCircle Make/O/N=(4*vNumPntsInCircle) AcceptanceCircleLat, AcceptanceCircleLon AcceptanceCircleLat = vXSpacing*(round(p/2) - vNumPntsInCircle) + wUSMag_Lat[0] AcceptanceCircleLon = sqrt((AcceptanceCircleLat[0] - wUSMag_Lat[0])^2 - (AcceptanceCircleLat - wUSMag_Lat[0])^2)*(-1)^p + wUSMag_Lon[0] For(iPartDex = 0; iPartDex < vNumTrajParts; iPartDex += 1) TempTraj = TrajDistUSMag[iPartDex][p] WaveStats/Q TempTraj If(V_MinLoc > 10) ParticleClosestDist[iPartDex] = V_min ParticleClosestTime[iPartDex] = V_MinLoc ParticleAltAtClosestPass[iPartDex] = TrajMatrix[iPartDex][V_minLoc][2] Else iDex = V_minLoc Do iDex += 1 While(numtype(TrajMatrix[iPartDex][iDex][0]) == 2 && iDex < vNumTimePoints) Variable vUpperDex = iDex iDex = V_MinLoc If(V_minLoc > 0) Do iDex -= 1 While(numtype(TrajMatrix[iPartDex][iDex][0]) == 2 && iDex > 0) EndIf Variable vLowerDex = iDex Variable vInterpPnts = (vUpperDex - vLowerDex) / 0.1 + 1 Variable vNumPntsLower = (V_MinLoc - vLowerDex) / 0.1 Variable vNumPntsUpper = (vUpperDex - V_minLoc) / 0.1 Make/O/N=(vInterpPnts)/FREE TempLat, TempLon, TempAlt, TempDist If(vNumPntsLower > 0) TempLon[0, vNumPntsLower] = TrajMatrix[iPartDex][vLowerDex][0] + (TrajMatrix[iPartDex][V_minLoc][0] - TrajMatrix[iPartDex][vLowerDex][0])*p/vNumPntsLower TempLat[0, vNumPntsLower] = TrajMatrix[iPartDex][vLowerDex][1] + (TrajMatrix[iPartDex][V_minLoc][1] - TrajMatrix[iPartDex][vLowerDex][1])*p/vNumPntsLower TempAlt[0, vNumPntsLower] = TrajMatrix[iPartDex][vLowerDex][2] + (TrajMatrix[iPartDex][V_minLoc][2] - TrajMatrix[iPartDex][vLowerDex][2])*p/vNumPntsLower Else TempLon[0] = TrajMatrix[iPartDex][vLowerDex][0] TempLat[0] = TrajMatrix[iPartDex][vLowerDex][1] TempAlt[0] = TrajMatrix[iPartDex][vLowerDex][2] EndIf TempLon[vNumPntsLower + 1, vInterpPnts - 1] = TrajMatrix[iPartDex][V_minLoc][0] + (TrajMatrix[iPartDex][vUpperDex][0] - TrajMatrix[iPartDex][V_minLoc][0])*(p - vNumPntsLower)/vNumPntsUpper TempLat[vNumPntsLower + 1, vInterpPnts - 1] = TrajMatrix[iPartDex][V_minLoc][1] + (TrajMatrix[iPartDex][vUpperDex][1] - TrajMatrix[iPartDex][V_minLoc][1])*(p - vNumPntsLower)/vNumPntsUpper TempAlt[vNumPntsLower + 1, vInterpPnts - 1] = TrajMatrix[iPartDex][V_minLoc][2] + (TrajMatrix[iPartDex][vUpperDex][2] - TrajMatrix[iPartDex][V_minLoc][2])*(p - vNumPntsLower)/vNumPntsUpper TempDist = DistanceBtwnLatLon(vLat_USMag_rad, vLon_USMag_rad, TempLat[p]*pi/180, TempLon[p]*pi/180) WaveStats/Q TempDist ParticleClosestDist[iPartDex] = V_min ParticleClosestTime[iPartDex] = V_MinLoc * 0.1 ParticleAltAtClosestPass[iPartDex] = TempAlt[V_minLoc] EndIf EndFor // ParticleClosestTime = (ParticleClosestDist[p] < vThreshold) && (ParticleAltAtClosestPass[p] < vHeightCutoff) ? ParticleClosestTime[p] : NaN // ParticleClosestTime = (ParticleClosestDist[p] < 2) && ParticleAltAtClosestPass[p] < 200 ? ParticleClosestTime[p] : NaN // WaveStats/Q ParticleClosestTime // Traj_FracPassUSMag[iFolderDex] = V_npnts / 200 * 100 Traj_Threshold[iFolderDex] = vThreshold EndFor EndFor BinTrajTimes(strFlightName) End Function BinTrajTimes(strFlightName) String strFlightName SetDataFolder root: If(strlen(strFlightName) > 0) Make/O/N=1/FREE/T twTempFlightName twTempFlightName[0] = strFlightName Else Wave/T twTempFlightName = twFlightName Endif // Wave/T twFlightName Variable vNumFlights = numpnts(twTempFlightName) Variable iFlightDex For(iFlightDex = 0; iFlightDex < vNumFlights; iFlightDex += 1) String strFlight = twTempFlightName[iFlightDex] SetDataFolder root:$(strFlight):STILT Wave/T twTraj_Name Wave Traj_Threshold Variable vNumFolders = numpnts(twTraj_Name) Make/O/N=(vNumFolders) Traj_PlumeAge_Gauss = NaN Make/O/N=(vNumFolders) Traj_PlumeAge_Gauss_StDev = NaN Make/O/N=(vNumFolders) Traj_PlumeAge_Mean = NaN Make/O/N=(vNumFolders) Traj_PlumeAge_Mean_StDev = NaN Make/O/N=(vNumFolders) Traj_FracPassUSMag = NaN Variable iFolderDex, iTimeDex For(iFolderDex = 0; iFolderDex < vNumFolders; iFolderDex += 1) Wave wParticleTime = $("root:" + strFlight + ":STILT:Rec" + twTraj_Name[iFolderDex] + ":ParticleClosestTime") Wave wParticleDist = $("root:" + strFlight + ":STILT:Rec" + twTraj_Name[iFolderDex] + ":ParticleClosestDist") Wave wParticleAltAtClosestDist = $("root:" + strFlight + ":STILT:Rec" + twTraj_Name[iFolderDex] + ":ParticleAltAtClosestPass") WaveStats/Q wParticleTime Variable vNumNearTraj = V_npnts If(vNumNearTraj == 0) Make/O/N=0 $("root:" + strFlight + ":STILT:Rec" + twTraj_Name[iFolderDex] + ":TrajTimeBin")/WAVE=TrajTimeBin Make/O/N=0 $("root:" + strFlight + ":STILT:Rec" + twTraj_Name[iFolderDex] + ":TrajFreqBin")/WAVE=TrajFreqBin Else Variable vStart = V_Min Variable vStop = V_Max Variable vNumBins = 20 Variable vMinBinWidth = vStop > 10 ? 1 : 0.1 Variable vBinWidth = max(ceil((vStop - vStart)/vMinBinWidth/vNumBins)*vMinBinWidth, vMinBinWidth) // print iFolderDex, vStart, vStop, vBinWidth Make/O/N=(vNumBins) $("root:" + strFlight + ":STILT:Rec" + twTraj_Name[iFolderDex] + ":TrajTimeBin")/WAVE=TrajTimeBin Make/O/N=(vNumBins) $("root:" + strFlight + ":STILT:Rec" + twTraj_Name[iFolderDex] + ":TrajFreqBin_All")/WAVE=TrajFreqBin_All Make/O/N=(vNumBins) $("root:" + strFlight + ":STILT:Rec" + twTraj_Name[iFolderDex] + ":TrajFreqBin")/WAVE=TrajFreqBin TrajFreqBin_All = Nan TrajFreqBin = Nan TrajTimeBin = vStart + p*vBinWidth For(iTimeDex = 0; iTimeDex < vNumBins; iTimeDex += 1) Extract/FREE/O wParticleTime, TempWave, (wParticleTime >= TrajTimeBin[iTimeDex] && wParticleTime < TrajTimeBin[iTimeDex] + vBinWidth) TrajFreqBin_All[iTimeDex] = numpnts(TempWave) Extract/FREE/O wParticleTime, TempWave, (wParticleTime >= TrajTimeBin[iTimeDex] && wParticleTime < TrajTimeBin[iTimeDex] + vBinWidth && wParticleDist < Traj_Threshold[iFolderDex] && wParticleAltAtClosestDist < 400) TrajFreqBin[iTimeDex] = numpnts(TempWave) EndFor Make/O/N=(vNumBins) $("root:" + strFlight + ":STILT:Rec" + twTraj_Name[iFolderDex] + ":fit_TrajFreqBin")/WAVE=fit_TrajFreqBin fit_TrajFreqBin = 0 If(sum(TrajFreqBin) > 2) try CurveFit/Q gauss, TrajFreqBin/X=TrajTimeBin AbortOnRTE catch if(V_AbortCode == -4) Printf "Error during curve fit in %s, trajectory %g\r", strFlight, iFolderDex Variable CFerror = GetRTError(1) // 1 to clear the error Print GetErrMessage(CFerror) Endif endtry Wave W_coef Traj_PlumeAge_Gauss[iFolderDex] = W_coef[2] Traj_PlumeAge_Gauss_StDev[iFolderDex] = W_coef[3] fit_TrajFreqBin = W_coef[0] + W_coef[1]*exp(-((TrajTimeBin[p]-W_coef[2])/W_coef[3])^2) KillWaves/Z W_coef EndIf Make/O/N=(numpnts(wParticleTime))/FREE TempWave TempWave = (wParticleDist[p] < Traj_Threshold[iFolderDex] && wParticleAltAtClosestDist[p] < 400) ? wParticleTime[p] : NaN WaveStats/Q TempWave Traj_PlumeAge_Mean[iFolderDex] = V_avg Traj_PlumeAge_Mean_StDev[iFolderDex] = V_sdev Traj_FracPassUSMag[iFolderDex] = V_npnts / 200 * 100 EndIf EndFor // Traj_PlumeAge_Gauss = Traj_PlumeAge_Gauss[p] < Traj_PlumeAge_Gauss_StDev[p] ? NaN : Traj_PlumeAge_Gauss[p] EndFor End Function PlotTrajOnMap() SetDataFolder root: Wave/T twFlightName Wave wUSMag_Lat, wUSMag_Lon Variable vNumFlights = numpnts(twFlightName) Variable iFlightDex For(iFlightDex = 0; iFlightDex < vNumFlights; iFlightDex += 1) String strFlight = twFlightName[iFlightDex] SetDataFolder root:$(strFlight):STILT Wave/T twTraj_Name Variable vNumFolders = numpnts(twTraj_Name) Wave wLat = $("root:" + strFlight + ":GPSLat") Wave wLon = $("root:" + strFlight + ":GPSLon") Wave wLat_STILT = $("root:" + strFlight + ":STILT:Traj_Lat") Wave wLon_STILT = $("root:" + strFlight + ":STILT:Traj_Lon") Wave Traj_FracPassUSMag Wave/T twTraj_Name Variable vWhichRec = 0 Variable vWhichTrajSet = 0 Wave wTraj = $("root:" + strFlight + ":STILT:Rec" + twTraj_Name[vWhichRec] + ":TrajMatrix") Wave wAcceptanceCircleLat = $("root:" + strFlight + ":STILT:Rec" + twTraj_Name[vWhichRec] + ":AcceptanceCircleLat") Wave wAcceptanceCircleLon = $("root:" + strFlight + ":STILT:Rec" + twTraj_Name[vWhichRec] + ":AcceptanceCircleLon") Wave wTrajFreqBin = $("root:" + strFlight + ":STILT:Rec" + twTraj_Name[vWhichRec] + ":TrajFreqBin") Wave wTrajFreqBin_All = $("root:" + strFlight + ":STILT:Rec" + twTraj_Name[vWhichRec] + ":TrajFreqBin_All") Wave wTrajTimeBin = $("root:" + strFlight + ":STILT:Rec" + twTraj_Name[vWhichRec] + ":TrajTimeBin") Wave fit_TrajFreqBin = $("root:" + strFlight + ":STILT:Rec" + twTraj_Name[vWhichRec] + ":fit_TrajFreqBin") String strPlotName = strFlight + "_Traj" DoWindow/K/Z $strPlotName Display/K=1/W=(550, 100, 850, 600) as strPlotName DoWindow/C $strPlotName ControlBar 75 SetVariable SetVar_Rec_num,pos={20,5},size={95,18},bodyWidth=45,disable=0,title="Which receptor?",value=_NUM:vWhichRec,proc=Plotter_Update_Traj, limits={0,vNumFolders-1,1},fSize=11 SetVariable SetVar_Traj_num,pos={175,5},size={95,18},bodyWidth=45,disable=0,title="Which set of traj?",value=_NUM:vWhichTrajSet,proc=Plotter_Update_Traj, limits={0,9,1},fSize=11 SetVariable SetVar_Traj_datetime,pos={20,25},size={125,18},bodyWidth=95,disable=0,title="Traj date/time",value=_STR:twTraj_Name[vWhichRec],limits={0,inf,0},fSize=11 SetVariable SetVar_FracPass,pos={200,25},size={125,18},bodyWidth=55,disable=0,title="Perc. passing USMag",value=_NUM:Traj_FracPassUSMag[vWhichRec],limits={0,inf,0},fSize=11 CheckBox Check_ColorByAlt,pos={275,5},title="Color by altitude?", value = 0,proc=Plotter_Update_Traj_Color,fSize=11 Button Button_ResetAxes_Upper,pos={20,50},size={95,18},title="Reset upper",proc=Plotter_Reset_Axes,fSize=11 Button Button_ResetAxes_Lower,pos={175,50},size={95,18},title="Reset lower",proc=Plotter_Reset_Axes,fSize=11 AppendtoGraph/C=(0,0,0) wLat vs wLon AppendToGraph/C=(1,52428,52428) wLat_STILT vs wLon_STILT ModifyGraph mode(Traj_Lat) = 3, marker(Traj_Lat) = 19, msize(Traj_Lat) = 1 AppendImage root:UtahMaps:CO_UT_WY_Elevation_UWFPS //add terrain image, axes will rescale ModifyImage CO_UT_WY_Elevation_UWFPS ctab= {235,4328,Terrain256,0} //change terrain color Variable vNumTraj = 20 Variable iTrajDex For(iTrajDex = 0; iTrajDex < vNumTraj; iTrajDex += 1) String strTraceName = "Traj" + num2str(iTrajDex) AppendToGraph/C=(43690,43690,43690) wTraj[iTrajDex + vWhichTrajSet*20][][1]/TN=$strTraceName vs wTraj[iTrajDex + vWhichTrajSet*20][][0] ModifyGraph gaps($strTraceName) = 0 EndFor //Add color scale ColorScale/C/N=text0/A=MB vert=0,trace=Traj0, widthPct=50,height=10, "Height above ground level (m)" //Add USMag AppendToGraph wUSMag_Lat[0,0] vs wUSMag_Lon[0,0] ModifyGraph mode(wUSMag_Lat) = 3, marker(wUSMag_Lat) = 19 //Add circle of acceptance AppendtoGraph wAcceptanceCircleLat vs wAcceptanceCircleLon ModifyGraph mode(AcceptanceCircleLat) = 3, marker(AcceptanceCircleLat) = 19, msize(AcceptanceCircleLat) = 1, rgb(AcceptanceCircleLat) = (0,0,0) //Add plume age histogram AppendToGraph/L=L2/B=B2 wTrajFreqBin vs wTrajTimeBin AppendToGraph/L=L2/B=B2 wTrajFreqBin_All vs wTrajTimeBin AppendToGraph/L=L2/B=B2 fit_TrajFreqBin vs wTrajTimeBin ModifyGraph mode(TrajFreqBin)=5, mode(TrajFreqBin_All) = 5, hbFill(TrajFreqBin)=2 ModifyGraph axisEnab(left)={0,0.8},axisEnab(L2)={0.85,1},freePos(L2)={0,B2},freePos(B2)={0,L2} ModifyGraph mirror=2,standoff=0 SetAxis bottom, -113, -111.5 SetAxis left, 39.5, 42.5 EndFor End Function Plotter_Update_Traj(ctrlName, varNum, varStr, varName) : SetVariableControl String ctrlName Variable varNum String varStr String varName GetWindow/Z kwTopWin title String sPlotTitle = S_Value String strFlight = RemoveEnding(sPlotTitle, "_Traj") ControlInfo SetVar_Rec_num Variable vWhichRec=V_Value ControlInfo SetVar_Traj_num Variable vWhichTrajSet=V_Value ControlInfo Check_ColorByAlt Variable vColorByAlt = V_Value SetDataFolder root:$(strFlight):STILT Wave/T twTraj_Name Wave Traj_FracPassUSMag Wave wTraj = $("root:" + strFlight + ":STILT:Rec" + twTraj_Name[vWhichRec] + ":TrajMatrix") Wave wAcceptanceCircleLat = $("root:" + strFlight + ":STILT:Rec" + twTraj_Name[vWhichRec] + ":AcceptanceCircleLat") Wave wAcceptanceCircleLon = $("root:" + strFlight + ":STILT:Rec" + twTraj_Name[vWhichRec] + ":AcceptanceCircleLon") Wave wTrajFreqBin = $("root:" + strFlight + ":STILT:Rec" + twTraj_Name[vWhichRec] + ":TrajFreqBin") Wave wTrajFreqBin_All = $("root:" + strFlight + ":STILT:Rec" + twTraj_Name[vWhichRec] + ":TrajFreqBin_All") Wave wTrajTimeBin = $("root:" + strFlight + ":STILT:Rec" + twTraj_Name[vWhichRec] + ":TrajTimeBin") Wave fit_TrajFreqBin = $("root:" + strFlight + ":STILT:Rec" + twTraj_Name[vWhichRec] + ":fit_TrajFreqBin") Variable vNumTraj = 20 Variable iTrajDex For(iTrajDex = 0; iTrajDex < vNumTraj; iTrajDex += 1) String strTraceName = "Traj" + num2str(iTrajDex) ReplaceWave trace=$strTraceName, wTraj[iTrajDex + vWhichTrajSet*20][][1] ReplaceWave /X trace=$strTraceName, wTraj[iTrajDex + vWhichTrajSet*20][][0] If(vColorByAlt) ModifyGraph zColor($strTraceName)={wTraj[iTrajDex + vWhichTrajSet*20][][2],100,600,Rainbow,0} Else ModifyGraph ZColor($strTraceName) = 0, rgb($strTraceName) = (43690,43690,43690) EndIf ReplaceWave trace=AcceptanceCircleLat, wAcceptanceCircleLat ReplaceWave /X trace=AcceptanceCircleLat, wAcceptanceCircleLon ReplaceWave trace=TrajFreqBin, wTrajFreqBin ReplaceWave /X trace=TrajFreqBin, wTrajTimeBin ReplaceWave trace=fit_TrajFreqBin, fit_TrajFreqBin ReplaceWave /X trace=fit_TrajFreqBin, wTrajTimeBin ReplaceWave trace=TrajFreqBin_All, wTrajFreqBin_All ReplaceWave /X trace=TrajFreqBin_All, wTrajTimeBin EndFor SetVariable SetVar_Traj_datetime, value=_STR:twTraj_Name[vWhichRec] SetVariable SetVar_FracPass, value=_NUM:Traj_FracPassUSMag[vWhichRec] End Function Plotter_Update_Traj_Color(ctrlName, checked) : CheckBoxControl String ctrlName Variable checked // String varStr // String varName GetWindow/Z kwTopWin title String sPlotTitle = S_Value String strFlight = RemoveEnding(sPlotTitle, "_Traj") ControlInfo SetVar_Rec_num Variable vWhichRec=V_Value ControlInfo SetVar_Traj_num Variable vWhichTrajSet=V_Value ControlInfo Check_ColorByAlt Variable vColorByAlt = V_Value SetDataFolder root:$(strFlight):STILT Wave/T twTraj_Name // Wave Traj_FracPassUSMag Wave wTraj = $("root:" + strFlight + ":STILT:Rec" + twTraj_Name[vWhichRec] + ":TrajMatrix") Variable vNumTraj = 20 Variable iTrajDex For(iTrajDex = 0; iTrajDex < vNumTraj; iTrajDex += 1) String strTraceName = "Traj" + num2str(iTrajDex) // ReplaceWave trace=$strTraceName, wTraj[iTrajDex + vWhichTrajSet*20][][1] // ReplaceWave /X trace=$strTraceName, wTraj[iTrajDex + vWhichTrajSet*20][][0] If(vColorByAlt) ModifyGraph zColor($strTraceName)={wTraj[iTrajDex + vWhichTrajSet*20][][2],100,600,Rainbow,0} Else ModifyGraph ZColor($strTraceName) = 0, rgb($strTraceName) = (43690,43690,43690) EndIf EndFor // SetVariable SetVar_Traj_datetime, value=_STR:twTraj_Name[vWhichRec] // SetVariable SetVar_FracPass, value=_NUM:Traj_FracPassUSMag[vWhichRec] End Function Plotter_Reset_Axes(ctrlName) : ButtonControl String ctrlName If(StringMatch(ctrlName, "*upper*") == 1) SetAxis/A L2 SetAxis/A B2 ElseIf(StringMatch(ctrlName, "*lower") == 1) SetAxis bottom, -113, -111.5 SetAxis left, 39.5, 42.5 EndIf End Function KillTrajMatrices(strFlightName) //The trajectories are large, so if possible, delete the raw data after calculating the plume ages String strFlightName SetDataFolder root: If(strlen(strFlightName) > 0) Make/O/N=1/FREE/T twTempFlightName twTempFlightName[0] = strFlightName Else Wave/T twTempFlightName = twFlightName Endif Variable vNumFlights = numpnts(twTempFlightName) Variable iFlightDex, iFolderDex For(iFlightDex = 0; iFlightDex < vNumFlights; iFlightDex += 1) String strCurrentFlight = twTempFlightName[iFlightDex] Wave/T twTraj_Name = $("root:" + strCurrentFlight + ":STILT:twTraj_Name") Variable vNumFolders = numpnts(twTraj_Name) For(iFolderDex = 0; iFolderDex < vNumFolders; iFolderDex += 1) Wave wTrajMatrix = $("root:" + strCurrentFlight + ":STILT:Rec" + twTraj_Name[iFolderDex] + ":TrajMatrix") Wave wTrajDistance = $("root:" + strCurrentFlight + ":STILT:Rec" + twTraj_Name[iFolderDex] + ":TrajDistUSMag") KillWaves/Z wTrajMatrix, wTrajDistance EndFor EndFor End //Compare STILT winds to Met Probe winds Function MakeAvgWindSpdDir() //Average the MetProbe data to 1 minute, so it can be compared with the STILT-derived winds SetDataFolder root: Wave/T twFlightName Variable vNumFlights = numpnts(twFlightName) Variable iFlightDex, iTimeDex Variable vNumSecsToAvg = 60 //Number of seconds to average For(iFlightDex = 0; iFlightDex < vNumFlights; iFlightDex += 1) String strCurrentFlight = twFlightName[iFlightDex] Wave WindSpd = $("root:" + strCurrentFlight + ":WindSpd") Wave WindDir = $("root:" + strCurrentFlight + ":WindDir") Wave GPSLat = $("root:" + strCurrentFlight + ":GPSLat") Wave GPSLon = $("root:" + strCurrentFlight + ":GPSLon") Wave TO_Timewave_MST = $("root:" + strCurrentFlight + ":TO_Timewave_MST") Wave TO_Timewave_UTC = $("root:" + strCurrentFlight + ":TO_Timewave_UTC") Variable vNumTimePoints = numpnts(TO_Timewave_MST) Variable vNumAvgTimePnts = round((vNumTimePoints - 1) / vNumSecsToAvg) If(round(vNumAvgTimePnts) != vNumAvgTimePnts) Printf "Possible error! Non-even number of averaged time bins in %s\r", strCurrentFlight EndIf Make/O/N=(vNumAvgTimePnts)/D $("root:" + strCurrentFlight + ":TO_Timewave_Avg_MST")/WAVE=wTO_Timewave_Avg_MST Make/O/N=(vNumAvgTimePnts)/D $("root:" + strCurrentFlight + ":TO_Timewave_Avg_UTC")/WAVE=wTO_Timewave_Avg_UTC SetScale d, 0, 0, "dat", wTO_Timewave_Avg_MST, wTO_Timewave_Avg_UTC wTO_Timewave_Avg_MST = TO_Timewave_MST[0] + p*vNumSecsToAvg + (vNumSecsToAvg/2) wTO_Timewave_Avg_UTC = TO_Timewave_UTC[0] + p*vNumSecsToAvg + (vNumSecsToAvg/2) Make/O/N=(vNumAvgTimePnts) $("root:" + strCurrentFlight + ":WindSpd_Avg")/WAVE=WindSpd_Avg Make/O/N=(vNumAvgTimePnts) $("root:" + strCurrentFlight + ":WindDir_Avg")/WAVE=WindDir_Avg Make/O/N=(vNumAvgTimePnts) $("root:" + strCurrentFlight + ":GPSLat_Avg")/WAVE=GPSLat_Avg Make/O/N=(vNumAvgTimePnts) $("root:" + strCurrentFlight + ":GPSLon_Avg")/WAVE=GPSLon_Avg Make/O/N=(numpnts(WindDir))/FREE wEWvector, wNSvector wEWvector = -1*sin(WindDir*pi/180)*WindSpd wNSvector = -1*cos(WindDir*pi/180)*WindSpd For(iTimeDex = 0; iTimeDex < vNumAvgTimePnts; iTimeDex += 1) // WaveStats/Q/R=[iTimeDex*vNumSecsToAvg, (iTimeDex + 1)*vNumSecsToAvg - 1] WindSpd // WindSpd_Avg[iTimeDex] = V_avg GPSLat_Avg[iTimeDex] = GPSLat[(iTimeDex + 0.5)*vNumSecsToAvg] GPSLon_Avg[iTimeDex] = GPSLon[(iTimeDex + 0.5)*vNumSecsToAvg] WaveStats/Q/R=[iTimeDex*vNumSecsToAvg, (iTimeDex + 1)*vNumSecsToAvg - 1] wEWvector Variable vMeanEW = V_Avg WaveStats/Q/R=[iTimeDex*vNumSecsToAvg, (iTimeDex + 1)*vNumSecsToAvg - 1] wNSvector Variable vMeanNS = V_Avg Variable vArctan = atan2(vMeanEW,vMeanNS) Variable vFlow = vArctan < pi ? pi : -1*pi WindDir_Avg[iTimeDex] = (vArctan + vFlow)*180/pi WindSpd_Avg[iTimeDex] = sqrt(vMeanEW^2 + vMeanNS^2) EndFor Make/O/N=(numpnts(WindSpd_Avg),3) $("root:" + strCurrentFlight + ":WindBarbData")/WAVE=WindBarbData //creates empty barbDatawave with the appropriate dimensions (#rows,#columns) SetDimLabel 1,2,WindBarb,WindBarbData //1=dimNumber -> "columns"; 2=dimIndex -> the index=2 column; WindBarb is the name, must be used to set as barbs WindBarbData[][0] = 20 //Just set this as a constant WindBarbData[][1] = mod(-1*(WindDir_Avg[p]*pi/180 + pi/2),2*pi) WindBarbData[][2] = WindSpd_Avg[p] EndFor End Function GetWindFromSTILT(strFlightName) //Convert the first two points of each STILT trajectory into wind information, for comparing to the Met Probe String strFlightName SetDataFolder root: If(strlen(strFlightName) > 0) Make/O/N=1/FREE/T twTempFlightName twTempFlightName[0] = strFlightName Else Wave/T twTempFlightName = twFlightName Endif Variable vNumFlights = numpnts(twTempFlightName) Variable iFlightDex, iFolderDex, iTrajDex For(iFlightDex = 0; iFlightDex < vNumFlights; iFlightDex += 1) String strCurrentFlight = twTempFlightName[iFlightDex] SetDataFolder root:$(strCurrentFlight):STILT Wave/T twTraj_Name Variable vNumFolders = numpnts(twTraj_Name) Make/O/N=(vNumFolders) Traj_WindSpd, Traj_WindDir For(iFolderDex = 0; iFolderDex < vNumFolders; iFolderDex += 1) Wave wTraj = $("root:" + strCurrentFlight + ":STILT:Rec" + twTraj_Name[iFolderDex] + ":TrajMatrix") Variable vNumTrajectories = DimSize(wTraj, 0) Make/O/N=(vNumTrajectories)/FREE TempWindSpd, TempWindDir For(iTrajDex = 0; iTrajDex < vNumTrajectories; iTrajDex += 1) Variable vStart_Lat = wTraj[iTrajDex][0][1]*pi/180 Variable vStart_Lon = wTraj[iTrajDex][0][0]*pi/180 Variable iTimeDex = 0 Do iTimeDex += 1 While(numtype(wTraj[iTrajDex][iTimeDex][0]) == 2) Variable vStop_Lat = wTraj[iTrajDex][iTimeDex][1]*pi/180 Variable vStop_Lon = wTraj[iTrajDex][iTimeDex][0]*pi/180 // Variable vDist = 6378 * sqrt(2) * sqrt(1-(sin(vStart_Lat)*sin(vStop_Lat)*cos(-1*vStop_Lon + vStart_Lon) + cos(vStart_Lat)*cos(vStop_Lat))) Variable vDist = DistanceBtwnLatLon(vStart_Lat, vStart_Lon, vStop_Lat, vStop_Lon) // Variable vVertDist = 6378 * sqrt(2) * sqrt(1-(sin(vStart_Lat)*sin(vStop_Lat) + cos(vStart_Lat)*cos(vStop_Lat))) Variable vVertDist = DistanceBtwnLatLon(vStart_Lat, vStart_Lon, vStop_Lat, vStart_Lon) If(vStart_Lat > vStop_Lat) vVertDist *= -1 Endif Variable vDir = acos(vVertDist/vDist) If(vStart_Lon > vStop_Lon) vDir = 2*pi - vDir EndIf TempWindSpd[iTrajDex] = vDist / iTimeDex * 1000 / 60 TempWindDir[iTrajDex] = vDir EndFor Make/O/N=(numpnts(TempWindDir))/FREE wEWvector = -1*sin(TempWindDir)*TempWindSpd //determine East-west component unit vector Make/O/N=(numpnts(TempWindDir))/FREE wNSvector = -1*cos(TempWindDir)*TempWindSpd //determine north-south component unit vector WaveStats/Q wEWvector Variable vMeanEW = V_avg //average the east-west vectors WaveStats/Q wNSvector Variable vMeanNS = V_avg //average the north-south vectors Variable vArctan = atan2(vMeanEW,vMeanNS) //now recombine the averaged EW and NS components Variable vFlow = vArctan < pi ? pi : -1*pi Traj_WindSpd[iFolderDex] = sqrt(vMeanEW^2 + vMeanNS^2) Traj_WindDir[iFolderDex] = (vArctan + vFlow)*180/pi EndFor Make/O/N=(vNumFolders, 3) Traj_barbData SetDimLabel 1,2,WindBarb,Traj_barbData Traj_barbData[][0] = 20 Traj_barbData[][1] = mod(-1*(Traj_WindDir[p]*pi/180 + pi/2), 2*pi) Traj_barbData[][2] = Traj_WindSpd[p] EndFor End Function InterpSTILTToTO() //Make direct comparison between STILT and MetProbe. STILT is every 2-3 minutes, MetProbe has been averaged to 1 per minute //so interpolate the STILT onto the averaged MetProbe time wave SetDataFolder root: Wave/T twFlightName Variable vNumFlights = numpnts(twFlightName) Variable iFlightDex, iDex For(iFlightDex = 0; iFlightDex < vNumFlights; iFlightDex += 1) String strCurrentFlight = twFlightName[iFlightDex] Wave wTO_Timewave_Avg_UTC = $("root:" + strCurrentFlight + ":TO_Timewave_Avg_UTC") Wave wWindDir_Avg = $("root:" + strCurrentFlight + ":WindDir_Avg") Wave wTraj_WindSpd = $("root:" + strCurrentFlight + ":STILT:Traj_WindSpd") Wave wTraj_WindDir = $("root:" + strCurrentFlight + ":STILT:Traj_WindDir") Wave wTraj_Time = $("root:" + strCurrentFlight + ":STILT:Traj_Time") Make/O/N=(numpnts(wTO_Timewave_Avg_UTC)) $("root:" + strCurrentFlight + ":WindSpd_STILT")/WAVE=WindSpd_STILT Make/O/N=(numpnts(wTO_Timewave_Avg_UTC)) $("root:" + strCurrentFlight + ":WindDir_STILT")/WAVE=WindDir_STILT WindSpd_STILT = NaN WindDir_STILT = NaN For(iDex = 0; iDex < numpnts(wTraj_Time); iDex += 1) Variable vTime = BinarySearch(wTO_Timewave_Avg_UTC, wTraj_Time[iDex]) If(vTime > 0) WindSpd_STILT[vTime] = wTraj_WindSpd[iDex] WindDir_STILT[vTime] = wTraj_WindDir[iDex] EndIf EndFor //Make a wave showing difference in wind direction (to avoid 1 degree and 359 degrees from being treated as a major disagreement) Make/O/N=(numpnts(wTO_Timewave_Avg_UTC)) $("root:" + strCurrentFlight + ":WindDir_Difference")/WAVE=WindDir_Diff WindDir_Diff = WindDir_STILT - wWindDir_Avg WindDir_Diff = WindDir_Diff[p] > 180 ? 360 - WindDir_Diff[p] : WindDir_Diff[p] WindDir_Diff = WindDir_Diff[p] < -180 ? -360 - WindDir_Diff[p] : WindDir_Diff[p] //Then, bin that information Duplicate/O/FREE WindDir_Diff WindDir_Diff_Sorted Sort WindDir_Diff_Sorted, WindDir_Diff_Sorted Variable vBinSize = 10 Variable vNumBins = 360 / vBinSize + 1 Make/O/N=(vNumBins) $("root:" + strCurrentFlight + ":WindDir_Bin")/WAVE=WindDir_Bin Make/O/N=(vNumBins) $("root:" + strCurrentFlight + ":WindDir_Freq")/WAVE=WindDir_Freq WindDir_Bin = p*vBinSize - 180 For(iDex = 0; iDex < vNumBins; iDex += 1) Extract/FREE/O WindDir_Diff_Sorted, TempWave, (WindDir_Diff_Sorted >= WindDir_Bin[iDex] && WindDir_Diff_Sorted < WindDir_Bin[iDex] + vBinSize) WindDir_Freq[iDex] = numpnts(TempWave) EndFor EndFor End Function ConcatAllWindData() //Concatenate the wind data for all the flights, to have a better comparison NewDataFolder/O/S root:Wind Wave/T twFlightName = root:twFlightName Variable vNumFlights = numpnts(twFlightName) Variable iFlightDex Make/O/N=0 WindSpd_Avg_AllFlights, WindSpd_STILT_AllFlights, WindDir_Difference For(iFlightDex = 0; iFlightDex < vNumFlights; iFlightDex += 1) Wave wWindSpd_Avg = $("root:" + twFlightName[iFlightDex] + ":WindSpd_Avg") Wave wWindSpd_STILT = $("root:" + twFlightName[iFlightDex] + ":WindSpd_STILT") Wave wWindDir_Difference = $("root:" + twFlightName[iFlightDex] + ":WindDir_Difference") Concatenate/NP {wWindSpd_Avg}, WindSpd_Avg_AllFlights Concatenate/NP {wWindSpd_STILT}, WindSpd_STILT_AllFlights Concatenate/NP {wWindDir_Difference}, WindDir_Difference EndFor Duplicate/O/FREE WindDir_Difference WindDir_Difference_Sorted Sort WindDir_Difference_Sorted, WindDir_Difference_Sorted Variable vBinSize = 5 Variable vNumBins = 360 / vBinSize + 1 Make/O/N=(vNumBins) WindDir_Bin, WindDir_Freq WindDir_Bin = p*vBinSize - 180 Variable iDex For(iDex = 0; iDex < vNumBins; iDex += 1) Extract/FREE/O WindDir_Difference_Sorted, TempWave, (WindDir_Difference_Sorted >= WindDir_Bin[iDex] && WindDir_Difference_Sorted < WindDir_Bin[iDex] + vBinSize) WindDir_Freq[iDex] = numpnts(TempWave) EndFor End Function PlotWindcomparison_AllFlights() //Plot STILT vs MetProbe wind speed and direction histogram, one for each plot. Put all into a layout SetDataFolder root: Wave/T twFlightName = root:twFlightName Variable vNumFlights = numpnts(twFlightName) Variable iFlightDex, iDex //First, plot the wind speed comparison Make/O/N=2 OneToOne = p*25 String strLayoutName = "WindSpdCompare" DoWindow/K $strLayoutName NewLayout/N=$strLayoutName as strLayoutName LayoutPageAction/W=$strLayoutName appendPage LayoutPageAction/W=$strLayoutName Page=1 For(iFlightDex = 0; iFlightDex < vNumFlights; iFlightDex += 1) String strCurrentFlight = twFlightName[iFlightDex] String strGraphTitle = "WindSpdCompare_" + strCurrentFlight DoWindow/K $strGraphTitle Display as strGraphTitle DoWindow/C $strGraphTitle Wave WindSpd_Avg = $("root:" + strCurrentFlight + ":WindSpd_Avg") Wave WindSpd_STILT = $("root:" + strCurrentFlight + ":WindSpd_STILT") AppendToGraph WindSpd_STILT/TN=$strCurrentFlight vs WindSpd_Avg ModifyGraph mode($strCurrentFlight) = 3, marker($strCurrentFlight) = 19 AppendToGraph OneToOne vs OneToOne Label left "STILT 1-min avg wind speed" Label bottom "MetProbe 1-min avg wind speed" TextBox/C/N=text0/A=MT strCurrentFlight ModifyGraph mirror=2,standoff=0 Variable vPage = trunc(iFlightDex/12) + 1 AppendLayoutObject/W=$strLayoutName/PAGE=(vPage) graph $strGraphTitle EndFor For(iDex = 0; iDex < trunc(vNumFlights/12) + 1; iDex += 1) String strExecuteText = "Tile/A=(4,3)" LayoutPageAction/W=$strLayoutName Page=(iDex + 1) Execute strExecuteText Endfor //Then do it for the wind direction strLayoutName = "WindDirCompare" DoWindow/K $strLayoutName NewLayout/N=$strLayoutName as strLayoutName LayoutPageAction/W=$strLayoutName appendPage LayoutPageAction/W=$strLayoutName Page=1 For(iFlightDex = 0; iFlightDex < vNumFlights; iFlightDex += 1) strCurrentFlight = twFlightName[iFlightDex] strGraphTitle = "WindDirCompare_" + strCurrentFlight DoWindow/K $strGraphTitle Display as strGraphTitle DoWindow/C $strGraphTitle Wave WindDir_Freq = $("root:" + strCurrentFlight + ":WindDir_Freq") Wave WindDir_Bin = $("root:" + strCurrentFlight + ":WindDir_Bin") AppendToGraph WindDir_Freq/TN=$strCurrentFlight vs WindDir_Bin ModifyGraph mode($strCurrentFlight) = 5 Label left "Frequency distribution" Label bottom "Wind dir (STILT) - Wind dir (MetProbe)" ModifyGraph mirror=2,standoff=0 TextBox/C/N=text0/A=MT strCurrentFlight vPage = trunc(iFlightDex/12) + 1 AppendLayoutObject/W=$strLayoutName/PAGE=(vPage) graph $strGraphTitle EndFor For(iDex = 0; iDex < trunc(vNumFlights/12) + 1; iDex += 1) strExecuteText = "Tile/A=(4,3)" LayoutPageAction/W=$strLayoutName Page=(iDex + 1) Execute strExecuteText Endfor End Function PlotWindComparison_OnePlot() //Plot STILT vs MetProbe wind speed and direction histogram, for all flights in experiment SetDataFolder root:Wind Make/O/N=2 OneToOne = p*25 Wave WindSpd_Avg_AllFlights, WindSpd_STILT_AllFlights Wave WindDir_Freq, WindDir_Bin String sGraphTitle = "WindSpdCompare_All" DoWindow/K $sGraphTitle Display as sGraphTitle DoWindow/C $sGraphTitle AppendToGraph WindSpd_STILT_AllFlights vs WindSpd_Avg_AllFlights AppendToGraph OneToOne vs OneToOne Label left "STILT 1-min avg wind speed" Label bottom "MetProbe 1-min avg wind speed" ModifyGraph mode(WindSpd_STILT_AllFlights) = 3, marker(WindSpd_STILT_AllFlights) = 19 ModifyGraph mirror=2,standoff=0 sGraphTitle = "WindDirCompare_All" DoWindow/K $sGraphTitle Display as sGraphTitle DoWindow/C $sGraphTitle AppendToGraph WindDir_Freq vs WindDir_Bin Label left "Frequency distribution" Label bottom "Wind dir (STILT) - Wind dir (MetProbe)" ModifyGraph mode = 5 ModifyGraph mirror=2,standoff=0 End Function PlotWindBarbOnMap() SetDataFolder root: Wave/T twFlightName Variable vNumFlights = numpnts(twFlightName) Variable iFlightDex, iDex For(iFlightDex = 0; iFlightDex < vNumFlights; iFlightDex += 1) String strCurrentFlight = twFlightName[iFlightDex] Wave GPSLat = $("root:" + strCurrentFlight + ":GPSLat") Wave GPSLon = $("root:" + strCurrentFlight + ":GPSLon") Wave GPSLat_Avg = $("root:" + strCurrentFlight + ":GPSLat_Avg") Wave GPSLon_Avg = $("root:" + strCurrentFlight + ":GPSLon_Avg") Wave WindbarbData = $("root:" + strCurrentFlight + ":WindbarbData") Wave Traj_Lat = $("root:" + strCurrentFlight + ":STILT:Traj_Lat") Wave Traj_Lon = $("root:" + strCurrentFlight + ":STILT:Traj_Lon") Wave Traj_barbData = $("root:" + strCurrentFlight + ":STILT:Traj_barbData") String sGraphTitle = "SpatialPlot_FullFlight_" + strCurrentFlight //"Add Spatial Plot" in front of the flight name DoWindow/K $sGraphTitle Display/W=(550, 100, 900, 550) as sGraphTitle DoWindow/C $sGraphTitle AppendtoGraph/C=(0,0,0) GPSLat vs GPSLon //plot full flight path AppendtoGraph GPSLat_Avg vs GPSLon_Avg //plot full flight path AppendtoGraph/C=(0,0,0) Traj_Lat vs Traj_Lon //plot full flight path //add terrain image to background: AppendImage $("root:UtahMaps:CO_UT_WY_Elevation_UWFPS") //add terrain image, axes will rescale ModifyImage CO_UT_WY_Elevation_UWFPS ctab= {235,4328,Terrain256,0} //change terrain color ModifyGraph mode(Traj_Lat) = 3, mode(GPSLat_Avg) = 3 ModifyGraph arrowMarker(Traj_Lat) = {Traj_barbData, 1, 10, 1, 0} //turn on arrow mode on graph to show barbs ModifyGraph arrowMarker(GPSLat_Avg) = {WindbarbData, 1, 10, 1, 0} //turn on arrow mode on graph to show barbs ModifyGraph rgb(Traj_Lat)=(1,16019,65535) //Graph window titled by flight name EndFor End //******************************************** //Getting plume data from the Twin Otter observations Function LoadPlumeIndexes() //Loads all the plume index waves saved on my hard drive SetDataFolder root: String strTrajectories = "C:Users:cwomack:Documents:Research Projects:UWFPS:Halogens analysis:STILT:Plume_Indexes" NewPath/O/Q pPlumeIndexes, strTrajectories Wave/T twFlightName Variable vNumFlights = numpnts(twFlightName) Variable iFlightDex, iDex String strEmissionList = IndexedFile(pPlumeIndexes, -1, ".ibw") For(iFlightDex = 0; iFlightDex < vNumFlights; iFlightDex += 1) String strCurrentFlight = twFlightName[iFlightDex] NewDataFolder/O/S $("root:" + strCurrentFlight + ":Plumes") String strDate = strCurrentFlight[3, strlen(strCurrentFlight) - 1] LoadWave/Q/O/P=pPlumeIndexes "Plume_Start_" + strDate + ".ibw" LoadWave/Q/O/P=pPlumeIndexes "Plume_Stop_" + strDate + ".ibw" If(WhichListItem("UseInEmission_" + ReplaceString("flt", strCurrentFlight, "") + ".ibw", strEmissionList) != -1) //If available, also load the UseInEmission waves LoadWave/Q/O/P=pPlumeIndexes "UseInEmission_" + strDate + ".ibw" EndIf If(WhichListItem("Plume_ForceMethod_" + ReplaceString("flt", strCurrentFlight, "") + ".ibw", strEmissionList) != -1) //If available, also load the ForceMethod waves LoadWave/Q/O/P=pPlumeIndexes "Plume_ForceMethod_" + strDate + ".ibw" EndIf If(WhichListItem("Plume_UseInModel_" + ReplaceString("flt", strCurrentFlight, "") + ".ibw", strEmissionList) != -1) //If available, also load the ForceMethod waves LoadWave/Q/O/P=pPlumeIndexes "Plume_UseInModel_" + strDate + ".ibw" EndIf If(WhichListItem("Plume_WhichRun_" + ReplaceString("flt", strCurrentFlight, "") + ".ibw", strEmissionList) != -1) //If available, also load the ForceMethod waves LoadWave/Q/O/P=pPlumeIndexes "Plume_WhichRun_" + strDate + ".ibw" EndIf EndFor KillPath/Z pPlumeIndexes End Function GaussFitPlumes() //Using the plume_start and plume_stop waves, get observed data for each species in each plume SetDataFolder root: Wave/T twFlightName Variable vNumFlights = numpnts(twFlightName) Variable iFlightDex, iDex, iSpeciesDex String strSpeciesList = "Cl2;Br2;BrCl;Ox;BrNO2;ClNO2;HCl;HNO3;HOBr;HOCl;HONO;N2O5;O3;NO;NO2;SO2;NOx;BrO;ClO" //List of species we have observations for String strSuffix = "_ppbv;_ppbv;_ppbv;_ppbv;_ppbv;_ppbv;_ppbv;_ppbv;_ppbv;_ppbv;_ppbv;_ppbv;_ppbv;_ppbv;_ppbv;_ppbv;_ppbv;_cps;_cps" //List of species we have observations for String strAMS = "AMS_Chl;AMS_NO3" String strAMSConvertToppb = "0.64;0.36" //Note: previous version had 0.086 for pCl. Where did I get that?? For(iFlightDex = 0; iFlightDex < vNumFlights; iFlightDex += 1) String strCurrentFlight = twFlightName[iFlightDex] NewDataFolder/O/S root:$(strCurrentFlight):Plumes NewDataFolder/O root:$(strCurrentFlight):Plumes:GaussFits Wave Plume_Start = $("root:" + strCurrentFlight + ":Plumes:Plume_Start") Wave Plume_Stop = $("root:" + strCurrentFlight + ":Plumes:Plume_Stop") Wave TO_Timewave_UTC = $("root:" + strCurrentFlight + ":TO_Timewave_UTC") For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpeciesList); iSpeciesDex += 1) Make/O/N=(numpnts(Plume_Start)) $("root:" + strCurrentFlight + ":Plumes:Plume_" + StringFromList(iSpeciesDex, strSpeciesList) + "_Gauss") Make/O/N=(numpnts(Plume_Start)) $("root:" + strCurrentFlight + ":Plumes:Plume_" + StringFromList(iSpeciesDex, strSpeciesList) + "_95th") Endfor For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strAMS); iSpeciesDex += 1) Make/O/N=(numpnts(Plume_Start)) $("root:" + strCurrentFlight + ":Plumes:Plume_" + StringFromList(iSpeciesDex, strAMS) + "_Max") EndFor Make/O/N=(numpnts(Plume_Start))/D $("root:" + strCurrentFlight + ":Plumes:Plume_Time_UTC")/WAVE=Plume_Time_UTC Make/O/N=(numpnts(Plume_Start))/D $("root:" + strCurrentFlight + ":Plumes:Plume_Width")/WAVE=Plume_Width SetScale d, 0, 0, "dat", Plume_Time_UTC For(iDex = 0; iDex < numpnts(Plume_Start); iDex += 1) Variable vStart = Plume_Start[iDex] Variable vStop = Plume_Stop[iDex] For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpeciesList); iSpeciesDex += 1) String strCurrentSpecies = StringFromList(iSpeciesDex, strSpeciesList) String strCurrentSuffix = StringfromList(iSpeciesDex, strSuffix) Wave wSpecies_All = $("root:" + strCurrentFlight + ":" + strCurrentSpecies + strCurrentSuffix) Wave wSpecies_Gauss = $("root:" + strCurrentFlight + ":Plumes:Plume_" + strCurrentSpecies + "_Gauss") Wave wSpecies_95th = $("root:" + strCurrentFlight + ":Plumes:Plume_" + strCurrentSpecies + "_95th") //First, get the 95th percentile value within that range Duplicate/O/FREE/R=[vStart, vStop] wSpecies_All TempWave Sort TempWave, TempWave WaveTransform zapNaNs TempWave If(numpnts(TempWave) > 0) If(StringMatch(strCurrentSpecies, "O*") != 1) //for the non-O3 or Ox waves, get the highest 95th value Variable v95th = min(ceil(numpnts(TempWave)*0.95), numpnts(TempWave) - 1) Else v95th = max(floor(numpnts(TempWave)*0.05), 0) //For O3 and Ox, get the lowest 95th value EndIf wSpecies_95th[iDex] = TempWave[v95th] Else wSpecies_95th[iDex] = NaN EndIf //Then fit the data to a Gaussian Make/O/N=(vStop - vStart + 1)/D $("root:" + strCurrentFlight + ":Plumes:GaussFits:fit_" + StringFromList(iSpeciesDex, strSpeciesList) + "_Plume" + num2str(iDex))/WAVE=wFit SetScale/I x, TO_Timewave_UTC[vStart], TO_Timewave_UTC[vStop], wFit If(numpnts(TempWave) > 2) Variable V_AbortCode = 0 try CurveFit/Q/N=1 gauss, wSpecies_All[vStart, vStop]/X=TO_Timewave_UTC[vStart, vStop]; AbortOnRTE Wave W_coef catch If(V_AbortCode == -4) Printf "Error during curve fit for %s in flight %s, plume %g, :", StringFromList(iSpeciesDex, strSpeciesList), strCurrentFlight, iDex Variable CFerror = GetRTError(1) // 1 to clear the error Print GetErrMessage(CFerror) wFit = NaN wSpecies_Gauss[iDex] = NaN Endif endtry wFit = W_coef[0] + W_coef[1]*exp(-((x-W_coef[2])/W_coef[3])^2) If(V_AbortCode == 0) If(numtype(W_coef[2]) == 0 && W_coef[2] > TO_Timewave_UTC[vStart] && W_coef[2] < TO_Timewave_UTC[vStop]) wSpecies_Gauss[iDex] = W_coef[1] + W_coef[0] Else wSpecies_Gauss[iDex] = NaN EndIf If(iSpeciesDex == 0) Plume_Time_UTC[iDex] = W_Coef[2] Plume_Width[iDex] = W_Coef[3] Endif Else wSpecies_Gauss[iDex] = NaN EndIf Else wFit = NaN wSpecies_Gauss[iDex] = NaN Endif EndFor For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strAMS); iSpeciesDex += 1) strCurrentSpecies = StringFromList(iSpeciesDex, strAMS) Wave wSpecies_All = $("root:" + strCurrentFlight + ":" + strCurrentSpecies) Wave wSpecies_Max = $("root:" + strCurrentFlight + ":Plumes:Plume_" + strCurrentSpecies + "_Max") WaveStats/Q/R=[vStart, vStop] wSpecies_All wSpecies_Max[iDex] = V_max * str2num(StringFromList(iSpeciesDex, strAMSConvertToppb)) //Convert from ug/m3 to ppb EndFor EndFor EndFor End Function Plot3DFlightTrack(strFlightName) //Make a 3-D plot of the flight track, and color it by a certain wave (typically Cl2) String strFlightName SetDataFolder root: ColorTab2Wave YellowHot Wave M_Colors If(strlen(strFlightName) > 0) Make/O/N=1/FREE/T twTempFlightName twTempFlightName[0] = strFlightName Else Wave/T twTempFlightName = twFlightName Endif Variable iFlightDex For(iFlightDex = 0; iFlightDex < numpnts(twTempFlightName); iFlightDex += 1) String strCurrentFlight = twTempFlightName[iFlightDex] SetDataFolder $("root:" + strCurrentFlight) Wave GPSLat = $("root:" + strCurrentFlight + ":GPSLat") Wave GPSLon = $("root:" + strCurrentFlight + ":GPSLon") Wave GPSAlt = $("root:" + strCurrentFlight + ":GPSAlt") Variable vNumPnts = numpnts(GPSLat) Duplicate/O/FREE GPSLon TempGPSLon TempGPSLon *= -1 Concatenate/NP=1/O {GPSLat, TempGPSLon, GPSAlt}, GPSMatrix Make/O/N=(vNumPnts, 4) GPSColor = NaN Make/O/N=(vNumPnts, 3) GPSSize = 0 GPSColor[][3] = 0 Wave wPlume_Lat = $("root:" + strCurrentFlight + ":Plumes:Plume_Lat") Wave wPlume_Lon = $("root:" + strCurrentFlight + ":Plumes:Plume_Lon") Wave wPlume_Alt = $("root:" + strCurrentFlight + ":Plumes:Plume_Alt") Duplicate/O/FREE wPlume_Lon TempPlume_Lon TempPlume_Lon *= -1 Concatenate/NP=1/O {wPlume_Lat, TempPlume_Lon, wPlume_Alt}, GPSMatrix_Plume //USER DECIDE WHAT TO COLOR BY Wave wWave2Color = Cl2_ppbv Variable vColorMin = 0.005 Variable vColorMax = 50 // Variable vColorMax = WaveMax(wWave2Color) Make/O/N=(vNumPnts)/FREE TempColorScale TempColorScale = (log(wWave2Color[p]) - log(vColorMin)) / (log(vColorMax) - log(vColorMin)) TempColorScale = TempColorScale[p] < 0 ? 0 : TempColorScale[p] TempColorScale = TempColorScale[p] > 1 ? 1 : TempColorScale[p] Variable iDex For(iDex = 0; iDex < vNumPnts; iDex += 1) If(numtype(TempColorScale[iDex]) == 0) GPSColor[iDex][0] = M_Colors[TempColorScale[iDex]*99][0]/65535 GPSColor[iDex][1] = M_Colors[TempColorScale[iDex]*99][1]/65535 GPSColor[iDex][2] = M_Colors[TempColorScale[iDex]*99][2]/65535 GPSSize[iDex][0] = TempColorScale[iDex]/2 + 0.01 GPSSize[iDex][1] = TempColorScale[iDex]/2 + 0.01 GPSSize[iDex][2] = TempColorScale[iDex]/2 + 0.01 EndIf EndFor String strGizmoName = strCurrentFlight + "_3D" NewGizmo/N=$strGizmoName/T=strGizmoName AppendToGizmo Scatter=GPSMatrix,name=ColorTrack AppendToGizmo path=GPSMatrix,name=FlightTrack AppendToGizmo Scatter=GPSMatrix_Plume,name=PlumeLocs AppendToGizmo Axes=BoxAxes,name=axes0 ModifyGizmo ModifyObject=ColorTrack,objectType=scatter,property={ scatterColorType,1} ModifyGizmo ModifyObject=ColorTrack,objectType=scatter,property={ sizeType,1} ModifyGizmo ModifyObject=ColorTrack,objectType=scatter,property={ colorWave,GPSColor} ModifyGizmo ModifyObject=ColorTrack,objectType=scatter,property={ sizeWave,GPSSize} ModifyGizmo scalingOption=16 // ModifyGizmo setOuterBox={40.8,41.4,112.4,112.779,1288,2500} ModifyGizmo ModifyObject=axes0,objectType=Axes,property={ 0,ticks,2} ModifyGizmo ModifyObject=axes0,objectType=Axes,property={ 1,ticks,2} ModifyGizmo ModifyObject=axes0,objectType=Axes,property={ 2,ticks,2} ModifyGizmo ModifyObject=PlumeLocs,objectType=scatter,property={ Shape,5} ModifyGizmo ModifyObject=PlumeLocs,objectType=scatter,property={ size,0.1} ModifyGizmo ModifyObject=PlumeLocs,objectType=scatter,property={ color,0,0,0,1} ModifyGizmo setDisplayList=0, object=ColorTrack ModifyGizmo setDisplayList=1, object=FlightTrack ModifyGizmo setDisplayList=2, object=axes0 EndFor KillWaves/Z M_Colors End Function PlotHalogenPlumes() //Plot the halogen waves, along with the selected plumes, and include the trajectory ages SetDataFolder root: Wave/T twFlightName Variable vNumFlights = numpnts(twFlightName) Variable iFlightDex, iSpeciesDex, iPlumeDex ColorTab2Wave Rainbow Wave M_Colors String strSpeciesList = "GPSAlt;Cl2_ppbv;Br2_ppbv;BrCl_ppbv;Ox_ppbv" String strAxisToPlotOn = "GPSaltaxis;Cl2axis;Br2axis;BrClaxis;left;" Variable vNumSpecies = ItemsInList(strSpeciesList) + 2 For(iFlightDex = 0; iFlightDex < vNumFlights; iFlightDex += 1) String strCurrentFlight = twFlightName[iFlightDex] Wave wTimewave = $("root:" + strCurrentFlight + ":TO_Timewave_UTC") Wave Plume_Time = $("root:" + strCurrentFlight + ":Plumes:Plume_Time_UTC") //Call the trajectory waves that will be plotted Wave Traj_FracPassUSMag = $("root:" + strCurrentFlight + ":STILT:Traj_FracPassUSMag") Wave Traj_PlumeAge_Mean = $("root:" + strCurrentFlight + ":STILT:Traj_PlumeAge_Mean") Wave Traj_Time = $("root:" + strCurrentFlight + ":STILT:Traj_Time") String sGraphTitle = "HalogenPlumes_" + strCurrentFlight DoWindow/K $sGraphTitle Display/W=(20, 20, 550, 600) as sGraphTitle DoWindow/C $sGraphTitle //Add fraction of trajectories passing US Magnesium AppendToGraph/L=TrajFracaxis Traj_FracPassUSMag vs Traj_Time ModifyGraph axisEnab(TrajFracaxis)={0.14,0.27},freePos(TrajFracaxis)={0,kwFraction} Label TrajFracaxis "TrajFrac" ModifyGraph lblPosMode(TrajFracaxis)=2 ModifyGraph rgb(Traj_FracPassUSMag)=(29524,1,58982), mode(Traj_FracPassUSMag) = 3, marker(Traj_FracPassUSMag) = 19 //Add trajectory ages AppendToGraph/L=PlumeAgeaxis Traj_PlumeAge_Mean vs Traj_Time ModifyGraph axisEnab(PlumeAgeaxis)={0,0.13},freePos(PlumeAgeaxis)={0,kwFraction} Label PlumeAgeaxis "Plume Age" ModifyGraph lblPosMode(PlumeAgeaxis)=2 ModifyGraph rgb(Traj_PlumeAge_Mean)=(63030,0,65475), mode(Traj_PlumeAge_Mean) = 3, marker(Traj_PlumeAge_Mean) = 19 //Add each species in strSpeciesList For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpeciesList); iSpeciesDex += 1) Variable vStartAxis = (iSpeciesDex + 2) / vNumSpecies Variable vStopAxis = (iSpeciesDex + 3) / vNumSpecies Variable vColor = iSpeciesDex / ItemsInList(strSpeciesList) * 99 String strCurrentSpecies = StringFromList(iSpeciesDex, strSpeciesList) String strCurrentAxis = StringfromList(iSpeciesDex, strAxisToPlotOn) //First, add the full time series Wave wSpecies = $("root:" + strCurrentFlight + ":" + strCurrentSpecies) AppendToGraph/L=$strCurrentAxis wSpecies/TN=$strCurrentSpecies vs wTimewave ModifyGraph rgb($strCurrentSpecies) = (M_colors[vColor][0], M_Colors[vColor][1], M_Colors[vColor][2]) If(StringMatch(strCurrentSpecies, "GPSAlt") == 0) strCurrentSpecies = ReplaceString("_ppbv", strCurrentSpecies, "") //Then, add the 95th percentile value and the Gauss max value Wave Plume_Gauss = $("root:" + strCurrentFlight + ":Plumes:Plume_" + strCurrentSpecies + "_Gauss") Wave Plume_95th = $("root:" + strCurrentFlight + ":Plumes:Plume_" + strCurrentSpecies + "_95th") String strTraceName = strCurrentSpecies + "_Gauss" AppendToGraph/L=$strCurrentAxis/C=(0,0,0) Plume_Gauss/TN=$strTraceName vs Plume_Time ModifyGraph mode($strTraceName) = 3, marker($strTraceName) = 19 strTraceName = strCurrentSpecies + "_95th" AppendToGraph/L=$strCurrentAxis Plume_95th/TN=$strTraceName vs Plume_Time ModifyGraph mode($strTraceName) = 3, marker($strTraceName) = 19 EndIf ModifyGraph axisEnab($strCurrentAxis) = {vStartAxis, vStopAxis}, freePos($strCurrentAxis)={0,kwFraction} ModifyGraph lblPosMode($strCurrentAxis) = 2 Label $strCurrentAxis strCurrentSpecies EndFor //Finally, add the Gaussian fit itself For(iPlumeDex = 0; iPlumeDex < numpnts(Plume_Time); iPlumeDex += 1) For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpeciesList); iSpeciesDex += 1) strCurrentSpecies = ReplaceString("_ppbv", StringFromList(iSpeciesDex, strSpeciesList), "") If(StringMatch(strCurrentSpecies, "GPSAlt") == 0) Wave wFit = $("root:" + strCurrentFlight + ":Plumes:GaussFits:fit_" + strCurrentSpecies + "_Plume" + num2str(iPlumeDex)) AppendTograph/L=$(StringFromList(iSpeciesDex, strAxisToPlotOn))/C=(0,0,0) wFit EndIf EndFor EndFor EndFor KillWaves/Z M_colors End Function PlotOtherPlumes() //Plot the halogen waves, along with the selected plumes, and include the trajectory ages SetDataFolder root: Wave/T twFlightName Variable vNumFlights = numpnts(twFlightName) Variable iFlightDex, iSpeciesDex, iPlumeDex ColorTab2Wave Rainbow Wave M_Colors String strPlotOptions = "Other halogens;NOy species" Variable vPlotType Prompt vPlotType, "What to plot?", popup, strPlotOptions DoPrompt "Select plot type", vPlotType If(V_flag == 1) Abort Endif String strSpeciesList, strAxisToPlotOn, strSuffix If(vPlotType == 1) strSpeciesList = "BrNO2;ClNO2;HCl;HOBr;HOCl;BrO;ClO" strAxisToPlotOn = "BrNO2axis;ClNO2axis;HClaxis;HOBraxis;HOClaxis;BrOaxis;ClOaxis" strSuffix = "_ppbv;_ppbv;_ppbv;_ppbv;_ppbv;_cps;_cps" ElseIf(vPlotType == 2) strSpeciesList = "Ox;O3;NO;NO2;NOx;SO2;HNO3;HONO;N2O5" strAxisToPlotOn = "Oxaxis;Oxaxis;NOxaxis;NOxaxis;NOxaxis;SO2axis;HNO3axis;HONOaxis;N2O5axis" strSuffix = "_ppbv;_ppbv;_ppbv;_ppbv;_ppbv;_ppbv;_ppbv;_ppbv;_ppbv" EndIf Make/O/N=(ItemsInList(strAxisToPlotOn))/T/FREE twAxes = StringfromList(p, strAxisToPlotOn) FindDuplicates/RT=twAxes_noDups/FREE twAxes String strAxisToPlotOn_NoDups wfprintf strAxisToPlotOn_NoDups, "%s;", twAxes_noDups Variable vNumSpecies = ItemsInList(strSpeciesList) Variable vNumAxes = numpnts(twAxes_noDups) For(iFlightDex = 0; iFlightDex < 1; iFlightDex += 1) String strCurrentFlight = twFlightName[iFlightDex] Wave wTimewave = $("root:" + strCurrentFlight + ":TO_Timewave_UTC") Wave Plume_Time = $("root:" + strCurrentFlight + ":Plumes:Plume_Time_UTC") String sGraphTitle = "OtherPlumes_" + strCurrentFlight DoWindow/K $sGraphTitle Display/W=(20, 20, 550, 600) as sGraphTitle DoWindow/C $sGraphTitle //Add each species in strSpeciesList For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpeciesList); iSpeciesDex += 1) Variable vColor = iSpeciesDex / ItemsInList(strSpeciesList) * 99 String strCurrentSpecies = StringFromList(iSpeciesDex, strSpeciesList) String strCurrentAxis = StringfromList(iSpeciesDex, strAxisToPlotOn) String strCurrentSuffix = StringFromList(iSpeciesDex, strSuffix) Variable vAxisPosition = WhichListItem(strCurrentAxis, strAxisToPlotOn_NoDups) Variable vStartAxis = (vAxisPosition) / vNumAxes Variable vStopAxis = (vAxisPosition + 1) / vNumAxes //First, add the full time series Wave wSpecies = $("root:" + strCurrentFlight + ":" + strCurrentSpecies + strCurrentSuffix) AppendToGraph/L=$strCurrentAxis wSpecies/TN=$strCurrentSpecies vs wTimewave ModifyGraph rgb($strCurrentSpecies) = (M_colors[vColor][0], M_Colors[vColor][1], M_Colors[vColor][2]) //Then, add the 95th percentile value and the Gauss max value Wave Plume_Gauss = $("root:" + strCurrentFlight + ":Plumes:Plume_" + strCurrentSpecies + "_Gauss") Wave Plume_95th = $("root:" + strCurrentFlight + ":Plumes:Plume_" + strCurrentSpecies + "_95th") String strTraceName = strCurrentSpecies + "_Gauss" AppendToGraph/L=$strCurrentAxis/C=(0,0,0) Plume_Gauss/TN=$strTraceName vs Plume_Time ModifyGraph mode($strTraceName) = 3, marker($strTraceName) = 19 strTraceName = strCurrentSpecies + "_95th" AppendToGraph/L=$strCurrentAxis Plume_95th/TN=$strTraceName vs Plume_Time ModifyGraph mode($strTraceName) = 3, marker($strTraceName) = 19 ModifyGraph axisEnab($strCurrentAxis) = {vStartAxis, vStopAxis}, freePos($strCurrentAxis)={0,kwFraction} ModifyGraph lblPosMode($strCurrentAxis) = 2 Label $strCurrentAxis strCurrentSpecies EndFor //Finally, add the Gaussian fit itself For(iPlumeDex = 0; iPlumeDex < numpnts(Plume_Time); iPlumeDex += 1) For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpeciesList); iSpeciesDex += 1) strCurrentSpecies = ReplaceString("_ppbv", StringFromList(iSpeciesDex, strSpeciesList), "") If(StringMatch(strCurrentSpecies, "GPSAlt") == 0) Wave wFit = $("root:" + strCurrentFlight + ":Plumes:GaussFits:fit_" + strCurrentSpecies + "_Plume" + num2str(iPlumeDex)) AppendTograph/L=$(StringFromList(iSpeciesDex, strAxisToPlotOn))/C=(0,0,0) wFit EndIf EndFor EndFor EndFor KillWaves/Z M_colors End Function GetPlumeAges() //Approximates the plume ages in four different ways, depending on the nearby STILT trajectories SetDataFolder root: Wave wUSMag_Lat, wUSMag_Lon Wave/T twFlightName Variable vNumFlights = numpnts(twFlightName) Variable iFlightDex, iDex, iTimeDex For(iFlightDex = 0; iFlightDex < vNumFlights; iFlightDex += 1) String strCurrentFlight = twFlightName[iFlightDex] Wave Plume_Start = $("root:" + strCurrentFlight + ":Plumes:Plume_Start") Wave Plume_Stop = $("root:" + strCurrentFlight + ":Plumes:Plume_Stop") Wave Plume_Time_UTC = $("root:" + strCurrentFlight + ":Plumes:Plume_Time_UTC") Wave wForceMethod = $("root:" + strCurrentFlight + ":Plumes:Plume_ForceMethod") Wave GPSLat = $("root:" + strCurrentFlight + ":GPSLat") Wave GPSLon = $("root:" + strCurrentFlight + ":GPSLon") Wave GPSAlt = $("root:" + strCurrentFlight + ":GPSAlt") Wave WindSpd = $("root:" + strCurrentFlight + ":WindSpd") Wave TO_Timewave_UTC = $("root:" + strCurrentFlight + ":TO_Timewave_UTC") Wave Traj_PlumeAge_Mean = $("root:" + strCurrentFlight + ":STILT:Traj_PlumeAge_Mean") Wave Traj_FracPassUSMag = $("root:" + strCurrentFlight + ":STILT:Traj_FracPassUSMag") Wave Traj_Time = $("root:" + strCurrentFlight + ":STILT:Traj_Time") Wave Traj_WindSpd = $("root:" + strCurrentFlight + ":STILT:Traj_WindSpd") Make/O/N=(numpnts(GPSLat))/FREE DistToUSMag = DistanceBtwnLatLon(wUSMag_Lat[0]*pi/180, wUSMag_Lon[0]*pi/180, GPSLat[p]*pi/180, GPSLon[p]*pi/180) Make/O/N=(numpnts(Plume_Start)) $("root:" + strCurrentFlight + ":Plumes:Plume_Lat")/WAVE=Plume_Lat Make/O/N=(numpnts(Plume_Start)) $("root:" + strCurrentFlight + ":Plumes:Plume_Lon")/WAVE=Plume_Lon Make/O/N=(numpnts(Plume_Start)) $("root:" + strCurrentFlight + ":Plumes:Plume_Alt")/WAVE=Plume_Alt Make/O/N=(numpnts(Plume_Start)) $("root:" + strCurrentFlight + ":Plumes:Plume_Age_Min")/WAVE=Plume_Age_Min Make/O/N=(numpnts(Plume_Start)) $("root:" + strCurrentFlight + ":Plumes:Plume_Age_Method_1")/WAVE=Plume_Age_Method_1 Make/O/N=(numpnts(Plume_Start)) $("root:" + strCurrentFlight + ":Plumes:Plume_Age_Method_2")/WAVE=Plume_Age_Method_2 Make/O/N=(numpnts(Plume_Start)) $("root:" + strCurrentFlight + ":Plumes:Plume_Age_Method_3")/WAVE=Plume_Age_Method_3 Make/O/N=(numpnts(Plume_Start)) $("root:" + strCurrentFlight + ":Plumes:Plume_Age_Method_4")/WAVE=Plume_Age_Method_4 Make/O/N=(numpnts(Plume_Start)) $("root:" + strCurrentFlight + ":Plumes:Plume_Age_Method_5")/WAVE=Plume_Age_Method_5 Make/O/N=(numpnts(Plume_Start)) $("root:" + strCurrentFlight + ":Plumes:Plume_Method")/WAVE=Plume_Method Plume_Age_Min = NaN Plume_Age_Method_1 = NaN Plume_Age_Method_2 = NaN Plume_Age_Method_3 = NaN Plume_Age_Method_4 = NaN Plume_Age_Method_5 = NaN For(iDex = 0; iDex < numpnts(Plume_Start); iDex += 1) Variable vStart = Plume_Start[iDex] Variable vStop = Plume_Stop[iDex] WaveStats/Q/R=[vStart, vStop] GPSLat Plume_Lat[iDex] = V_avg WaveStats/Q/R=[vStart, vStop] GPSLon Plume_Lon[iDex] = V_avg WaveStats/Q/R=[vStart, vStop] GPSAlt Plume_Alt[iDex] = V_avg Variable vDistance = DistanceBtwnLatLon(wUSMag_Lat[0]*pi/180, wUSMag_Lon[0]*pi/180, Plume_Lat[iDex]*pi/180, Plume_Lon[iDex]*pi/180) WaveStats/Q/R=[vStart, vStop] WindSpd Variable vUpdraft = 1 // in m/s // Plume_Age_Method_5[iDex] = (vDistance / V_avg * 1000 + (Plume_Alt[iDex] - 1300) / vUpdraft) / 60 //Get the lower and upper indices of the STILT trajectories that lie within the plume. Variable vTimeStart = TO_Timewave_UTC[vStart] Variable vTimeStop = TO_Timewave_UTC[vStop] Variable vLowerDex = BinarySearch(Traj_Time, vTimeStart) Variable vUpperDex = BinarySearch(Traj_time, vTimeStop) If(vUpperDex != vLowerDex ) //If vUpperDex = vLowerDex, that means that there are no STILT trajectories within this plume Variable vStartTraj = vLowerDex + 1 //Get the first trajectory Variable vStopTraj = vUpperDex == -2 ? numpnts(Traj_Time) - 1 : vUpperDex //Get the last trajectory Variable vAvgPlumeAge = 0 Variable vNumToAvg = 0 For(iTimeDex = vStartTraj; iTimeDex <= vStopTraj; iTimeDex += 1) //Cycle through trajectories, averaging plume ages If(numtype(Traj_PlumeAge_Mean[iTimeDex]) == 0 && Traj_FracPassUSMag[iTimeDex] > 10) vAvgPlumeAge += Traj_PlumeAge_Mean[iTimeDex] vNumToAvg += 1 Endif EndFor If(vNumToAvg > 0) Plume_Age_Method_1[iDex] = vAvgPlumeAge / vNumToAvg //Method 1 = Average from 1 or more STILT trajectories in the plume. EndIf Variable vAvgWindSpd = 0 vNumToAvg = 0 For(iTimeDex = vStartTraj; iTimeDex <= vStopTraj; iTimeDex += 1) vAvgWindSpd += Traj_WindSpd[iTimeDex] vNumToAvg += 1 EndFor vAvgWindSpd /= vNumToAvg Plume_Age_Method_3[iDex] = vDistance / vAvgWindSpd * 1000 / 60 //Method 3 = Average the wind speed from 1 or more trajectories in the plume, get plume age from distance/speed EndIf Variable vNextLower = vLowerDex >= 0 ? vLowerDex : 0 Variable vNextHigher = vUpperDex >= 0 ? vUpperDex + 1 : (vUpperDex == -1 ? 0 : numpnts(Traj_Time) - 1) If(Traj_PlumeAge_Mean[vNextLower] > 0 && Traj_PlumeAge_Mean[vNextHigher] > 0) Plume_Age_Method_2[iDex] = (Traj_PlumeAge_Mean[vNextLower] + Traj_PlumeAge_Mean[vNextHigher]) / 2 //Method 2 = Average from next nearest STILT trajectories to the plume EndIf vAvgWindSpd = (Traj_WindSpd[vNextLower] + Traj_WindSpd[vNextHigher]) / 2 Plume_Age_Method_4[iDex] = vDistance / vAvgWindSpd * 1000 / 60 //Method 4 = Average with speed from next nearest trajectories Plume_Age_Method_5[iDex] = Plume_Age_Method_4[iDex] + 90 If(wForceMethod[iDex] == 0) If(numtype(Plume_Age_Method_1[iDex]) == 0) Plume_Age_Min[iDex] = Plume_Age_Method_1[iDex] Plume_Method[iDex] = 1 ElseIf(numtype(Plume_Age_Method_2[iDex]) == 0) Plume_Age_Min[iDex] = Plume_Age_Method_2[iDex] Plume_Method[iDex] = 2 ElseIf(numtype(Plume_Age_Method_3[iDex]) == 0) Plume_Age_Min[iDex] = Plume_Age_Method_3[iDex] Plume_Method[iDex] = 3 ElseIf(numtype(Plume_Age_Method_4[iDex]) == 0) Plume_Age_Min[iDex] = Plume_Age_Method_4[iDex] Plume_Method[iDex] = 4 EndIf Else Wave wMethodToUse = $("root:" + strCurrentFlight + ":Plumes:Plume_Age_Method_" + num2str(wForceMethod[iDex])) Plume_Age_Min[iDex] = wMethodToUse[iDex] Plume_Method[iDex] = wForceMethod[iDex] EndIf EndFor Make/O/N=(numpnts(Plume_Start))/D $("root:" + strCurrentFlight + ":Plumes:Plume_Time_MST")/WAVE=Plume_Time_MST SetScale d, 0, 0, "dat", Plume_Time_MST Make/O/N=(numpnts(Plume_Start)) $("root:" + strCurrentFlight + ":Plumes:Plume_HOD_Meas")/WAVE=Plume_HOD_Meas Make/O/N=(numpnts(Plume_Start)) $("root:" + strCurrentFlight + ":Plumes:Plume_HOD_Emit")/WAVE=Plume_HOD_Emit If(numpnts(Plume_Start) > 0) Plume_Time_MST = Plume_Time_UTC - 7*3600 Plume_HOD_Meas = str2num(StringFromList(0,Secs2Time(Plume_Time_MST[p], 3), ":")) + str2num(StringFromList(1,Secs2Time(Plume_Time_MST[p], 3), ":"))/60 Plume_HOD_Emit = Plume_Age_Min[p] > 0 ? Plume_HOD_Meas[p] - Plume_Age_Min[p]/60 : NaN EndIf EndFor End Function ConcatAllPlumes() //Takes all plumes and concatenates them into a master list SetDataFolder root: Wave/T twFlightName Variable vNumFlights = numpnts(twFlightName) Variable iFlightDex, iDex, iSpeciesDex String strSpeciesList = "Cl2;Br2;BrCl;Ox;BrNO2;ClNO2;HCl;HNO3;HOBr;HOCl;HONO;N2O5;O3;NO;NO2;SO2;NOx;BrO;ClO" NewDataFolder/O/S root:AllFlights Make/O/N=0/D Plume_All_Age_Min, Plume_All_Time_UTC, Plume_All_Time_MST, Plume_All_HOD_Meas, Plume_All_HOD_Emit SetScale d, 0, 0, "dat", Plume_All_Time_UTC, Plume_All_Time_MST For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpeciesList); iSpeciesDex += 1) Make/O/N=0 $("root:AllFlights:Plume_All_" + StringFromList(iSpeciesDex, strSpeciesList)) EndFor For(iFlightDex = 0; iFlightDex < vNumFlights; iFlightDex += 1) Wave wPlume_Age_Min = $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_Age_Min") Wave wPlume_Time_UTC = $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_Time_UTC") Wave wPlume_Time_MST = $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_Time_MST") Wave wPlume_HOD_Meas = $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_HOD_Meas") Wave wPlume_HOD_Emit = $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_HOD_Emit") Concatenate/NP {wPlume_Age_Min}, Plume_All_Age_Min Concatenate/NP {wPlume_Time_UTC}, Plume_All_Time_UTC Concatenate/NP {wPlume_Time_MST}, Plume_All_Time_MST Concatenate/NP {wPlume_HOD_Meas}, Plume_All_HOD_Meas Concatenate/NP {wPlume_HOD_Emit}, Plume_All_HOD_Emit For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpeciesList); iSpeciesDex += 1) Wave wPlume_Species = $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_" + StringFromList(iSpeciesDex, strSpeciesList) + "_95th") Wave wPlume_All = $("root:AllFlights:Plume_All_" + StringFromList(iSpeciesDex, strSpeciesList)) Concatenate/NP {wPlume_Species}, wPlume_All EndFor EndFor End Function GetHalogenRatios() SetDataFolder root: Wave Plume_All_Cl2, Plume_All_Br2, Plume_All_BrCl Make/O/N=(numpnts(Plume_all_Cl2)) Plume_Ratio_Cl2_Br2, Plume_Ratio_Cl2_BrCl, Plume_Ratio_BrCl_Br2, Plume_Number Plume_Ratio_Cl2_Br2 = Plume_All_Cl2 / Plume_All_Br2 Plume_Ratio_Cl2_BrCl = Plume_All_Cl2 / Plume_All_BrCl Plume_Ratio_BrCl_Br2 = Plume_All_BrCl / Plume_All_Br2 Plume_Number = p End Function GetPlumeWidthsInDistance() SetDataFolder root: Wave/T twFlightName Variable vNumFlights = numpnts(twFlightName) Variable iFlightDex, iPlumeDex // String strSpecies = "Cl2" For(iFlightDex = 0; iFlightDex < vNumFlights; iFlightDex += 1) Wave wPlume_Width = $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_Width") Wave Plume_Start = $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_Start") Wave Plume_Stop = $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_Stop") Make/O/N=(numpnts(Plume_Start)) $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_Width_Meters")/WAVE=wPlume_Width_Meters Make/O/N=(numpnts(Plume_Start)) $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_WindSpeed")/WAVE=wPlume_WindSpeed Wave WindSpd = $("root:" + twFlightName[iFlightDex] + ":WindSpd") Wave PlaneSpd = $("root:" + twFlightName[iFlightDex] + ":TrueAirSpd") // Printf "Flight: %s\r", twFlightName[iFlightDex] For(iPlumeDex = 0; iPlumeDex < numpnts(Plume_Start); iPlumeDex += 1) WaveStats/Q/R=[Plume_Start[iPlumeDex],Plume_Stop[iPlumeDex]] WindSpd wPlume_WindSpeed[iPlumeDex] = V_avg WaveStats/Q/R=[Plume_Start[iPlumeDex],Plume_Stop[iPlumeDex]] PlaneSpd wPlume_Width_Meters[iPlumeDex] = V_avg * wPlume_Width[iPlumeDex] // Printf "\tWidth = %g m, WindSpd = %g m/s\r", vPlumeWidth, vAvgWindSpd Endfor Endfor End Function PlotAllPlumes() SetDataFolder root: Wave wUSMag_Lat, wUSMag_Lon Wave/T twFlightName Wave wMap = root:UtahMaps:CO_UT_WY_Elevation_UWFPS Variable vNumFlights = numpnts(twFlightName) Variable iFlightDex, iDex String strPlotName = "AllPlumes_" + "Age" DoWindow/K $strPlotName Display as strPlotName DoWindow/C $strPlotName For(iFlightDex = 0; iFlightDex < vNumFlights; iFlightDex += 1) String strCurrentFlight = twFlightName[iFlightDex] String strDate = strCurrentFlight[0, strlen(strCurrentFlight) - 1] String strTraceName = "Ox_" + strDate Wave Plume_Lat = $("root:" + strCurrentFlight + ":Plumes:Plume_Lat") Wave Plume_Lon = $("root:" + strCurrentFlight + ":Plumes:Plume_Lon") Wave Plume_Age = $("root:" + strCurrentFlight + ":Plumes:Plume_Age") AppendToGraph Plume_Lat/TN=$strTraceName vs Plume_Lon ModifyGraph mode($strTraceName) = 3, marker($strTraceName) = 19 ModifyGraph zColor($strTraceName)={Plume_Age,0,1440,Rainbow,0} EndFor AppendImage wMap ModifyImage CO_UT_WY_Elevation_UWFPS ctab= {235,4328,Terrain256,0} //change terrain color End Function ExtractUsedPlumes() SetDataFolder root: Wave/T twFlightName Variable vNumFlights = numpnts(twFlightName) Variable iFlightDex, iDex Variable vTotalNumPlumes = 0 For(iFlightDex = 0; iFlightDex < vNumFlights; iFlightDex += 1) String strCurrentFlight = twFlightName[iFlightDex] Wave Plume_Lat = $("root:" + strCurrentFlight + ":Plumes:Plume_Lat") Wave Plume_Lon = $("root:" + strCurrentFlight + ":Plumes:Plume_Lon") Wave Plume_Cl2 = $("root:" + strCurrentFlight + ":Plumes:Plume_Cl2_95th") Wave Plume_O3 = $("root:" + strCurrentFlight + ":Plumes:Plume_O3_95th") Wave Plume_Ox = $("root:" + strCurrentFlight + ":Plumes:Plume_Ox_95th") Wave Plume_Age_min = $("root:" + strCurrentFlight + ":Plumes:Plume_Age_Min") Wave Plume_UseInModel = $("root:" + strCurrentFlight + ":Plumes:Plume_UseInModel") Variable vNumUsedPlumes = sum(Plume_UseInModel) vTotalNumPlumes += vNumUsedPlumes Make/O/N=(vNumUsedPlumes) $("root:" + strCurrentFlight + ":Plumes:Plume_Lat_Used")/WAVE=Plume_Lat_Used Make/O/N=(vNumUsedPlumes) $("root:" + strCurrentFlight + ":Plumes:Plume_Lon_Used")/WAVE=Plume_Lon_Used Make/O/N=(vNumUsedPlumes) $("root:" + strCurrentFlight + ":Plumes:Plume_Cl2_Used")/WAVE=Plume_Cl2_Used Make/O/N=(vNumUsedPlumes) $("root:" + strCurrentFlight + ":Plumes:Plume_O3_Used")/WAVE=Plume_O3_Used Make/O/N=(vNumUsedPlumes) $("root:" + strCurrentFlight + ":Plumes:Plume_Ox_Used")/WAVE=Plume_Ox_Used Make/O/N=(vNumUsedPlumes) $("root:" + strCurrentFlight + ":Plumes:Plume_Age_Used")/WAVE=Plume_Age_Used Variable vPlumeDex = 0 For(iDex = 0; iDex < numpnts(Plume_UseInModel); iDex += 1) If(Plume_UseInModel[iDex] == 1) Plume_Lat_Used[vPlumeDex] = Plume_Lat[iDex] Plume_Lon_Used[vPlumeDex] = Plume_Lon[iDex] Plume_Cl2_Used[vPlumeDex] = Plume_Cl2[iDex] Plume_O3_Used[vPlumeDex] = Plume_O3[iDex] Plume_Ox_Used[vPlumeDex] = Plume_Ox[iDex] Plume_Age_Used[vPlumeDex] = Plume_Age_min[iDex] / 60 vPlumeDex += 1 EndIf Endfor Endfor Printf "Total number of daytime plumes used: %g\n", vTotalNumPlumes End Function MakePlumeStartStopPlot() SetDataFolder root: Wave/T twFlightName Variable vNumFlights = numpnts(twFlightName) String strFlightList = "0116;0117_1;0117_2;0118;0126;0127;0128;0131" Variable iDex, vMinDex, vMaxDex, iFlightDex NewDataFolder/O root:PlumeStartStop For(iDex = 0; iDex < ItemsInList(strFlightList); iDex += 1) String strCurrentPlume = StringFromList(iDex, strFlightList) Make/O/N=2 $("root:PlumeStartStop:Plume_" + strCurrentPlume + "_Lat_StartStop")/WAVE=wPlume_Lat_StartStop Make/O/N=2 $("root:PlumeStartStop:Plume_" + strCurrentPlume + "_Lon_StartStop")/WAVE=wPlume_Lon_StartStop Variable vMinTime = 1e6 Variable vMaxTime = 0 String strRun = "1" String strFlightMin = "" String strFlightMax = "" If(strlen(StringFromList(1, strCurrentPlume, "_")) > 0) strRun = StringFromList(1, strCurrentPlume, "_") Endif For(iFlightDex = 0; iFlightDex < vNumFlights; iFlightDex += 1) If(StringMatch(twFlightName[iFlightDex], "*" + StringFromList(0,strCurrentPlume, "_") + "*") == 1) Wave wPlumeAge = $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_Age_Min") Wave wPlumeUse = $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_WhichRun") Make/O/N=(numpnts(wPlumeAge))/FREE TempWave TempWave = (wPlumeUse[p] == str2num(strRun) || wPlumeUse[p] == 0) ? wPlumeAge[p] : NaN WaveStats/Q TempWave If(V_min < vMinTime) vMinTime = V_min vMinDex = V_MinLoc strFlightMin = twFlightName[iFlightDex] Endif If(V_max > vMaxTime) vMaxTime = V_Max vMaxDex = V_maxLoc strFlightMax = twFlightName[iFlightDex] EndIf Endif EndFor Wave wPlume_Lat = $("root:" + strFlightMin + ":Plumes:Plume_Lat") Wave wPlume_Lon = $("root:" + strFlightMin + ":Plumes:Plume_Lon") wPlume_Lat_StartStop[0] = wPlume_Lat[vMinDex] wPlume_Lon_StartStop[0] = wPlume_Lon[vMinDex] Wave wPlume_Lat = $("root:" + strFlightMax + ":Plumes:Plume_Lat") Wave wPlume_Lon = $("root:" + strFlightMax + ":Plumes:Plume_Lon") wPlume_Lat_StartStop[1] = wPlume_Lat[vMaxDex] wPlume_Lon_StartStop[1] = wPlume_Lon[vMaxDex] EndFor End Function PlotPlumeStartStop() SetDataFolder root: String strFlightList = "0116;0117_1;0117_2;0118;0126;0127;0128;0131" // Wave/T twFlightName // Variable vNumFlights = numpnts(twFlightName) String strPlotName = "Plume_Start_and_Stop" DoWindow/K $strPlotName Display as strPlotName DoWindow/C $strPlotName Variable iFlightDex For(iFlightDex = 0; iFlightDex < ItemsInList(strFlightList); iFlightDex += 1) String strCurrentPlume = StringFromList(iFlightDex, strFlightList) Wave wPlume_Lat = $("root:PlumeStartStop:Plume_" + strCurrentPlume + "_Lat_StartStop") Wave wPlume_Lon = $("root:PlumeStartStop:Plume_" + strCurrentPlume + "_Lon_StartStop") // Wave wPlume_Lon = $("root:" + strCurrentFlight + ":Plumes:Plume_Lon") // Wave wPlume_Age = $("root:" + strCurrentFlight + ":Plumes:Plume_Age_min") // WaveStats/Q wPlume_Age // Variable vMin = V_minLoc // Variable vMax = V_maxLoc // Make/O/N=2 $("root:" + strCurrentFlight + ":Plumes:Plume_StartLat")/WAVE=wPlume_StartStopLat // Make/O/N=2 $("root:" + strCurrentFlight + ":Plumes:Plume_StartLon")/WAVE=wPlume_StartStopLon // wPlume_StartStopLat[0] = wPlume_Lat[vMin] // wPlume_StartStopLon[0] = wPlume_Lon[vMin] // wPlume_StartStopLat[1] = wPlume_Lat[vMax] // wPlume_StartStopLon[1] = wPlume_Lon[vMax] AppendToGraph wPlume_Lat/TN=$(strCurrentPlume) vs wPlume_Lon ModifyGraph mode($strCurrentPlume) = 4, marker($strCurrentPlume) = 19 EndFor Wave wElevation = root:UtahMaps:CO_UT_WY_Elevation_UWFPS AppendImage wElevation End //************************************************ //Work on nighttime plumes Function GetEmissionFluxes() //Calculate the emissions fluxes from the nighttime transects //Calculate all the emissions fluxes, then select the ones to use in the average later SetDataFolder root: Wave/T twFlightName String strSpeciesList = "Cl2;Br2;BrCl;HCl;NOx" //Species to calculate flux for String strSpeciesMolWeight = "70;160;115;36;30" Variable iPlumeDex, iFlightDex, iDex, iSpeciesDex For(iFlightDex = 0; iFlightDex < numpnts(twFlightName); iFlightDex += 1) SetDataFolder root:$(twFlightName[iFlightDex]) Wave wPlumeStart = $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_Start") Wave wPlumeStop = $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_Stop") Wave wTimewave = $("root:" + twFlightName[iFlightDex] + ":TO_Timewave_MST") Wave wTrueAirSpd = $("root:" + twFlightName[iFlightDex] + ":TrueAirSpd") Wave wAmbTemp = $("root:" + twFlightName[iFlightDex] + ":AmbTemp") Wave wStaticPrs = $("root:" + twFlightName[iFlightDex] + ":StaticPrs") Wave wWindSpd = $("root:" + twFlightName[iFlightDex] + ":WindSpd") For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpeciesList); iSpeciesDex += 1) //Cycle through each species Wave wSpecies = $("root:" + twFlightName[iFlightDex] + ":" + StringFromList(iSpeciesDex, strSpeciesList) + "_ppbv") Make/O/N=(numpnts(wPlumeStart)) $("root:" + twFlightName[iFlightDex] + ":Plumes:EmissionFlux_" + StringFromList(iSpeciesDex, strSpeciesList))/WAVE=wEmissionFlux For(iPlumeDex = 0; iPlumeDex < numpnts(wPlumeStart); iPlumeDex += 1) Variable vStart = wPlumeStart[iPlumeDex] Variable vStop = wPlumeStop[iPlumeDex] Wave wFit = $("root:" + twFlightName[iFlightDex] + ":Plumes:GaussFits:fit_" + StringFromList(iSpeciesDex, strSpeciesList) + "_Plume" + num2str(iPlumeDex)) WaveStats/Q wFit Variable vMax = round(V_maxrowLoc) Variable vUpper = vMax + 0.5 Variable vLower = vMax - 0.5 Variable vPrev = 0 Variable vArea = 0 Variable vSum = 0 For(iDex = vMax; iDex > 0; iDex -= 1) If(vUpper < numpnts(wFit)) Variable vConc = (wFit[iDex - 0.5] + wFit[vUpper])/2 //ppbv Variable vHeight = 1 // m vArea = (((vMax - iDex + 0.5)*wTrueAirSpd[iDex - 0.5 + vStart])^2)*pi * vHeight //m^3 If(iDex == vMax) vPrev = 0 Else vPrev = (((vMax - (iDex + 1) + 0.5)*wTrueAirSpd[(iDex + 1) - 0.5 + vStart])^2)*pi * vHeight EndIf vArea -= vPrev // vPrev = vArea vSum += vArea*vConc //(molec Cl2 / molec air)*m^3 vUpper += 1 EndIf EndFor Variable vDensity = (wStaticPrs[vMax + vStart]) / 0.083145 / (wAmbTemp[vMax + vStart] + 273.15) / 1e6 * 6.022e23 //molec/cm3 Variable vEmissionFlux = vSum * vDensity / 1e9 / 6.022e23 * str2num(StringFromList(iSpeciesDex, strSpeciesMolWeight)) * wWindSpd[vMax + vStart] *1e6 wEmissionFlux[iPlumeDex] = vEmissionFlux EndFor EndFor EndFor End Function ConcatEmissionFluxes() //Concatenate all the emission fluxes so that averages can be taken SetDataFolder root: Wave/T twFlightName Variable vNumFlights = numpnts(twFlightName) Variable iFlightDex, iDex, iSpeciesDex String strEmissionList = "Cl2;Br2;BrCl;HCl;NOx" NewDataFolder/O/S root:AllFlights Make/O/N=0 Plume_All_UseInEmission For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strEmissionList); iSpeciesDex += 1) Make/O/N=0 $("Emission_All_" + StringFromList(iSpeciesDex, strEmissionList)) EndFor For(iFlightDex = 0; iFlightDex < vNumFlights; iFlightDex += 1) Wave wUseInEmission = $("root:" + twFlightName[iFlightDex] + ":Plumes:UseInEmission") Concatenate/NP {wUseInEmission}, Plume_All_UseInEmission For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strEmissionList); iSpeciesDex += 1) Wave wEmission_Species = $("root:" + twFlightName[iFlightDex] + ":Plumes:EmissionFlux_" + StringFromList(iSpeciesDex, strEmissionList)) Wave wEmission_All = $("Emission_All_" + StringFromList(iSpeciesDex, strEmissionList)) Concatenate/NP {wEmission_Species}, wEmission_All EndFor EndFor End Function AverageEmissionFluxes() SetDataFolder root: NewDataFolder/O/S root:EmissionFlux String strEmissionList = "Cl2;Br2;BrCl;HCl;NOx" Variable iSpeciesDex Wave wUseInEmission = root:AllFlights:Plume_All_UseInEmission Variable vNumSpecies = ItemsInList(strEmissionList) Make/O/N=(vNumSpecies)/T twSpeciesList = StringFromList(p, strEmissionList) Make/O/N=(vNumSpecies) Emission_Avg, Emission_StDev, Emission_Lower, Emission_Upper For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strEmissionList); iSpeciesDex += 1) Wave wEmission_All = $("root:AllFlights:Emission_All_" + StringFromList(iSpeciesDex, strEmissionList)) Make/O/N=(numpnts(wEmission_All))/FREE TempEmission TempEmission = wUseInEmission[p] == 1 ? wEmission_All[p] : NaN WaveStats/Q TempEmission Emission_Avg[iSpeciesDex] = V_Avg Emission_StDev[iSpeciesDex] = V_sdev Emission_Lower[iSpeciesDex] = V_min Emission_Upper[iSpeciesDex] = V_max // Printf "%s emission = %g g/sec (sdev = %g)\r", StringFromList(iSpeciesDex, strEmissionList), V_avg, V_sdev Endfor End Function PlotEmissionPlumes() //Plot each plume used in the emission calculation, to check that the gaussian approximation makes sense SetDataFolder root: Wave/T twFlightName Variable vNumFlights = numpnts(twFlightName) String strEmissionList = "Cl2;Br2;BrCl;HCl;NOx" ColorTab2Wave Rainbow Wave M_colors Variable iFlightDex, iSpeciesDex, iPlumeDex Variable vPlumeNumber = 0 Wave wUseInEmission = $("root:AllFlights:Plume_All_UseInEmission") Wave wPlumeAges = $("root:AllFlights:Plume_All_Age_Min") For(iFlightDex = 0; iFlightDex < vNumFlights; iFlightDex += 1) Wave wPlume_Start = $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_Start") Wave wPlume_Stop = $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_Stop") Variable vNumPlumes = numpnts(wPlume_Start) For(iPlumeDex = 0; iPlumeDex < vNumPlumes; iPlumeDex += 1) String strPlotName = twFlightName[iFlightDex] + "_Plume_N" + num2str(vPlumeNumber) String strLegend = "" DoWindow/K/Z $strPlotName Display as strPlotName DoWindow/C $strPlotName Wave wTime = $("root:" + twFlightName[iFlightDex] + ":TO_Timewave_UTC") For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strEmissionList); iSpeciesDex += 1) String strSpecies = StringFromList(iSpeciesDex, strEmissionList) Variable vColor = iSpeciesDex / ItemsInList(strEmissionList) * 99 Wave wSpecies = $("root:" + twFlightName[iFlightDex] + ":" + strSpecies + "_ppbv") AppendToGraph wSpecies[wPlume_Start[iPlumeDex], wPlume_Stop[iPlumeDex]]/TN=$strSpecies vs wTime[wPlume_Start[iPlumeDex], wPlume_Stop[iPlumeDex]] ModifyGraph rgb($strSpecies) = (M_Colors[vColor][0], M_Colors[vColor][1], M_Colors[vColor][2]) ModifyGraph lsize($strSpecies) = 2 strLegend += "\\s(" + strSpecies + ") " + strSpecies + "\r" Wave wFit = $("root:" + twFlightName[iFlightDex] + ":Plumes:GaussFits:fit_" + strSpecies + "_Plume" + num2str(iPlumeDex)) If(WaveExists(wFit)) AppendTograph/C=(0,0,0) wFit EndIf Endfor String strText = twFlightName[iFlightDex] + "\rNight Plume #" + num2str(vPlumeNumber) strText += "\rPlume age = " + num2str(wPlumeAges[vPlumeNumber]) + " min" If(wUseInEmission[vPlumeNumber] == 1) strText += "\rUse in emission flux" Endif TextBox/C/N=text0/A=RT strText strLegend = RemoveEnding(strLegend, "\r") // strLegend = ReplaceString("2", strLegend, "\\B2\\M") Legend/C/N=text1/A=LT strLegend // ModifyGraph log(left) = 1 vPlumeNumber += 1 ModifyGraph mirror=2,standoff=0 Label left, "Mixing ratio (ppbv)" Label bottom, "Time (UTC)" EndFor EndFor End Function ConcatCIMSInterferences() SetDataFolder root: Wave/T twFlightName Variable vNumFlights = numpnts(twFlightName) Variable iSpeciesDex, iFlightDex String strSpecies = "Cl2;Br2;HOCl;HOBr" NewDataFolder/O/S root:AllFlights For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpecies); iSpeciesDex += 1) Make/O/N=0 $("Plume_All_" + StringFromList(iSpeciesDex, strSpecies)) EndFor For(iFlightDex = 0; iFlightDex < vNumFlights; iFlightDex += 1) For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpecies); iSpeciesDex += 1) Wave wPlume_Species = $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_" + StringFromList(iSpeciesDex, strSpecies) + "_95th") Wave wPlume_All = $("Plume_All_" + StringFromList(iSpeciesDex, strSpecies)) Concatenate/NP {wPlume_Species}, wPlume_All EndFor EndFor End Function MakeSubsetOfEmissionFluxes() SetDataFolder root: String strSpecies = "Cl2;Br2;BrCl;HCl;NOx" Variable iSpeciesDex Wave UseInEmission = root:Plume_All_UseInEmission Wave PlumeAge = root:Plume_All_Age Wave Plume_Cl2 = root:Plume_All_Cl2 Wave Emission_All_Cl2 = root:Emission_All_Cl2 NewDataFolder/O root:TrendsInFlux Make/O/N=(sum(UseInEmission)) root:TrendsInFlux:Plume_Subset_Age /WAVE=Plume_Subset_Age Make/O/N=(sum(UseInEmission)) root:TrendsInFlux:Plume_Subset_Cl2 /WAVE=Plume_Subset_Cl2 Make/O/N=(sum(UseInEmission)) root:TrendsInFlux:Emission_Subset_Cl2 /WAVE=Emission_Subset_Cl2 Variable iDex, vDex = 0 For(iDex = 0; iDex < numpnts(UseInEmission); iDex += 1) If(UseInEmission[iDex] == 1) Plume_Subset_Age[vDex] = PlumeAge[iDex] Plume_Subset_Cl2[vDex] = Plume_Cl2[iDex] Emission_Subset_Cl2[vDex] = Emission_All_Cl2[iDex] vDex += 1 EndIf EndFor End //***************Box modeling*********************// //Prepare model input Function LoadGSdataForF0AM() //Load just the ground site info that we will need for the box model Print "Loading UWFPS ground site ICARTT files" LoadGroundSiteICARTT() Print "Making standard time waves" MakeGroundSiteMSTTimewaves() MakeStandardGSTime() Print "Extracting VOC data" ExtractGSVOCData() //Needed for the VOC profile Print "Extracting PM2.5 data" ExtractGSPM25Data() //Needed for the scaling factor for GSL vs SLV ProcessSRData() //Needed for approximating the solar radiation on each day SetDataFolder root:GroundSites String strVOCPath = "C:Users:cwomack:Documents:Research Projects:UWFPS:Halogens Analysis:F0AM" NewPath/O pVOCpath, strVOCPath LoadWave/O/Q/P=pVOCpath "twVOCName" LoadWave/O/Q/P=pVOCpath "twVOCName_F0AM" LoadWave/O/Q/P=pVOCpath "VOC_scaling_factor_GRLWomack" LoadWave/O/Q/P=pVOCpath "VOC_scaling_factor_ESTGilman" End Function ProcessSRData() //Interpolate the solar radiation data from the Hawthorne site onto a normal time wave. SetDataFolder root:GroundSites:HW:TraceAndPM Wave Date_MST, SR Variable vStartTime = Date2Secs(2016,12,01) Duplicate/O Date_MST DateTime_MST DateTime_MST = Date_MST + vStartTime SetScale d, 0, 0, "dat", DateTime_MST Variable vStartInterp = Date2Secs(2017,01,16) Variable vStopInterp = Date2Secs(2017,02,13) Variable vNumPntsInterp = (vStopInterp - vStartInterp) / 60 Make/O/N=(vNumPntsInterp)/D Timewave_MST = vStartInterp + p*60 SetScale d, 0, 0, "dat", Timewave_MST Make/O/N=(vNumPntsInterp) SR_interp Interpolate2/T=1/I=3/X=Timewave_MST/Y=SR_interp DateTime_MST, SR End Function PrepareBoxModelInput_Puff() //Gathers the plume data into inputs for each of the puff box model runs SetDataFolder root: Wave/T twFlightName Wave wUSMag_Lat, wUSMag_Lon Variable vNumFlights = numpnts(twFlightName) Variable iFlightDex, iPlumeDex, iSpeciesDex //First, get the number of individual runs that will be done (usually 1 or 2) Variable vNumRuns = 0 For(iFlightDex = 0; iFlightDex < vNumFlights; iFlightDex += 1) Wave Plume_WhichRun = $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_WhichRun") vNumRuns = Max(WaveMax(Plume_WhichRun), vNumRuns) EndFor Variable iRunDex //List of observed species that will be eventually compared to the model String strSpeciesList = "Cl2;Br2;BrCl;BrNO2;ClNO2;HCl;HNO3;HOBr;HOCl;HONO;N2O5;O3;NO;NO2;SO2;BrO;ClO;NOx;Ox" String strAMSSpeciesList = "AMS_Chl;AMS_NO3" Variable vScalingFactor = GetSolarScalingFactor() //Get the jcorr scaling factor based on the Hawthorne SR measurement For(iRunDex = 1; iRunDex <= vNumRuns; iRunDex += 1) // String strFolder = "root:BoxModel_Run" + num2str(iRunDex) String strFolder = "root:BoxModel_Run" + num2str(iRunDex) + "_Puff" NewDataFolder/O/S $(strFolder) //Make waves to store the observations for each model run Make/O/N=0 $(strFolder + ":Obs_Age_sec")/WAVE=wObs_Age_Sec Make/O/N=0/T $(strFolder + ":twObs_Source")/WAVE=twObs_Source Make/O/N=0/D $(strFolder + ":Obs_Time_MST")/WAVE=wObs_Time_MST SetScale d, 0, 0, "dat", wObs_Time_MST Make/O/N=0 $(strFolder + ":Obs_P")/WAVE=wObs_P Make/O/N=0 $(strFolder + ":Obs_T")/WAVE=wObs_T Make/O/N=0 $(strFolder + ":Obs_RH")/WAVE=wObs_RH Make/O/N=0 $(strFolder + ":Obs_SZA")/WAVE=wObs_SZA Make/O/N=0 $(strFolder + ":Obs_Saerosol")/WAVE=wObs_Saerosol For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpeciesList); iSpeciesDex += 1) Make/O/N=0 $(strFolder + ":Obs_" + StringFromList(iSpeciesDex, strSpeciesList))/WAVE=wObs_Species EndFor For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strAMSSpeciesList); iSpeciesDex += 1) Make/O/N=0 $(strFolder + ":Obs_" + StringFromLIst(iSpeciesDex, strAMSSpeciesList))/WAVE=wObs_AMS_Species EndFor For(iFlightDex = 0; iFlightDex < vNumFlights; iFlightDex += 1) Wave Plume_Start = $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_Start") Wave Plume_Stop = $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_Stop") Wave wPlume_Age = $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_Age_Min") Wave wPlume_WhichRun = $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_WhichRun") Wave wPlume_Lat = $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_Lat") Wave wPlume_Lon = $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_Lon") Wave wPlume_Time_MST = $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_Time_MST") Wave wTemp = $("root:" + twFlightName[iFlightDex] + ":AmbTemp") Wave wPress = $("root:" + twFlightName[iFlightDex] + ":StaticPrs") Wave wRH = $("root:" + twFlightName[iFlightDex] + ":RelHumidity") Wave wAerSurfArea = $("root:" + twFlightName[iFlightDex] + ":SurfConc_um2") //Get the indices of the plumes that will be included in this model run Make/O/N=(numpnts(Plume_Start))/FREE TempOrder = p TempOrder = p TempOrder = (wPlume_WhichRun[p] == iRunDex) || (wPlume_WhichRun[p] == 0) ? p : NaN WaveTransform zapNaNs TempOrder //Get the name of the plume, in case it's helpful later Variable vNumTempPnts = numpnts(TempOrder) Make/O/N=(vNumTempPnts)/T/FREE TempSource TempSource = twFlightName[iFlightDex] + "_" + num2str(TempOrder[p]) Concatenate/NP/T {TempSource}, twObs_Source //Get the ages Make/O/N=(vNumTempPnts)/D/FREE Temp_Obs_Age_Sec Temp_Obs_Age_Sec = wPlume_Age[TempOrder[p]] * 60 Concatenate/NP {Temp_Obs_Age_Sec}, wObs_Age_Sec //Grab each of the species, but set any negatives to 0 For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpeciesList); iSpeciesDex += 1) Wave wSpecies_Gauss = $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_" + StringFromList(iSpeciesDex, strSpeciesList) + "_Gauss") Wave wSpecies_95th = $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_" + StringFromList(iSpeciesDex, strSpeciesList) + "_95th") Make/O/N=(vNumTempPnts)/FREE Temp_Obs_Species WAVE wObs_Species = $(strFolder + ":Obs_" + StringFromList(iSpeciesDex, strSpeciesList)) Temp_Obs_Species = numtype(wSpecies_Gauss[TempOrder[p]]) == 0 ? wSpecies_Gauss[TempOrder[p]] : wSpecies_95th[TempOrder[p]] Temp_Obs_Species = Temp_Obs_Species[p] < 0 ? 0 : Temp_Obs_Species[p] Concatenate/NP {Temp_Obs_Species}, wObs_Species EndFor //Grab the AMS waves, which weren't fit to a gaussian because the AMS ran at 0.1Hz For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strAMSSpeciesList); iSpeciesDex += 1) Wave wAMS_Max = $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_" + StringFromList(iSpeciesDex, strAMSSpeciesList) + "_Max") Wave wObs_AMS_Species = $(strFolder + ":Obs_" + StringFromList(iSpeciesDex, strAMSSpeciesList)) Make/O/N=(vNumTempPnts)/FREE Temp_Obs_Species = wAMS_Max[TempOrder[p]] Concatenate/NP {Temp_Obs_Species}, wObs_AMS_Species EndFor //Make a time wave Make/O/N=(vNumTempPnts)/D/FREE Temp_Obs_Time_MST Temp_Obs_Time_MST = wPlume_Time_MST[TempOrder[p]] Concatenate/NP {Temp_Obs_Time_MST}, wObs_Time_MST //Get the meteorological data for each transect Make/O/N=(vNumTempPnts)/FREE TempP, TempT, TempRH, TempSZA, TempSaerosol For(iPlumeDex = 0; iPlumeDex < vNumTempPnts; iPlumeDex += 1) Variable vStart = Plume_Start[TempOrder[iPlumeDex]] Variable vStop = Plume_Stop[TempOrder[iPlumeDex]] WaveStats/Q/R=[vStart,vStop] wPress TempP[iPlumeDex] = V_avg WaveStats/Q/R=[vStart,vStop] wTemp TempT[iPlumeDex] = V_avg + 273.15 WaveStats/Q/R=[vStart,vStop] wRH TempRH[iPlumeDex] = V_avg WaveStats/Q/R=[vStart,vStop] wAerSurfArea TempSaerosol[iPlumeDex] = V_avg Endfor Concatenate/NP {TempP}, wObs_P Concatenate/NP {TempT}, wObs_T Concatenate/NP {TempRH}, wObs_RH Concatenate/NP {TempSaerosol}, wObs_Saerosol TempSZA = SZA_Calc(wPlume_Lat[TempOrder[p]]*pi/180, wPlume_Lon[TempOrder[p]]*pi/180, 2017, Temp_Obs_Time_MST[p], 0) Concatenate/NP {TempSZA}, wObs_SZA EndFor Variable vNumModelPnts = numpnts(wObs_Time_MST) //Now sort the data by plume age For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpeciesList); iSpeciesDex += 1) Wave wObs_Species = $(strFolder + ":Obs_" + StringFromList(iSpeciesDex, strSpeciesList)) Sort wObs_Age_Sec, wObs_Species EndFor For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strAMSSpeciesList); iSpeciesDex += 1) Wave wObs_AMS_Species = $(strFolder + ":Obs_" + StringFromList(iSpeciesDex, strAMSSpeciesList)) Sort wObs_Age_Sec, wObs_AMS_Species EndFor Sort wObs_Age_Sec, wObs_Age_Sec, wObs_Time_MST, twObs_Source, wObs_P, wObs_T, wObs_RH, wObs_SZA //Make some additional helpful timewaves Make/O/N=(vNumModelPnts)/D $(strFolder + ":Emit_Time_MST")/WAVE=wEmit_Time_MST SetScale d, 0, 0, "dat", wEmit_Time_MST Make/O/N=(vNumModelPnts) $(strFolder + ":Obs_Age_sec_since_1st")/WAVE=wObs_Age_Sec_since_1st Make/O/N=(vNumModelPnts) $(strFolder + ":Obs_Age_min")/WAVE=wObs_Age_min wObs_Age_Sec_since_1st = wObs_Age_Sec - wObs_Age_Sec[0] wObs_Age_min = wObs_Age_Sec / 60 wEmit_Time_MST = wObs_Time_MST - wObs_Age_Sec Wave wTimeWaveForModel = $(strFolder + ":Obs_Age_sec") //Could also change this to time since first transect //Set the integration time to a number that won't be super long in F0AM Make/O/N=(vNumModelPnts) $(strFolder + ":Int_Time")/WAVE=wInt_Time wInt_Time = 10 //Default is 10 sec wInt_Time = wTimeWaveForModel[p] < 200 ? 1 : wInt_Time[p] //1 sec integration for <3 min plumes wInt_Time = wTimeWaveForModel[p] > 1000 ? 120 : wInt_Time[p] //2 min integration for >15 minute plumes wInt_Time = wTimeWaveForModel[p] > 10000 ? 300 : wInt_Time[p] //5 min integration for >3 hour plumes //Now make the Met waves for the F0AM puff model. Note that these are different from the observations //because these are going to be the met data along the lifetime of each puff, not the observation at the end //Use the data from the HW site to interpolate Wave wTemp_HW = root:GroundSites:HW:TraceAndPM:TEMP Wave wRH_HW = root:GroundSites:HW:TraceAndPM:RH Wave wTime_HW = root:GroundSites:HW:TraceAndPM:DateTime_MST For(iPlumeDex = 0; iPlumeDex < vNumModelPnts; iPlumeDex += 1) Variable vIntTime = wInt_Time[iPlumeDex] vStop = wTimeWaveForModel[iPlumeDex] //The is the stopping point for each "puff" Variable vNumpnts = ceil((vStop) / vIntTime) + 1 //Get the number of points based on total time and integration time Variable vTime_Meas = wObs_Time_MST[iPlumeDex] Variable vTime_Emit = wEmit_Time_MST[iPlumeDex] Variable vTime_Meas_interp = BinarySearchInterp(wTime_HW, vTime_Meas) //Find where to grab data from HW Variable vTime_Emit_interp = BinarySearchInterp(wTime_HW, vTime_Emit) Variable vSlope_T = (wTemp_HW[vTime_Meas_interp] - wTemp_HW[vTime_Emit_interp] ) / (vTime_Meas - vTime_Emit) Variable vSlope_RH = (wRH_HW[vTime_Meas_interp] - wRH_HW[vTime_Emit_interp] ) / (vTime_Meas - vTime_Emit) vSlope_RH = numtype(vSlope_RH) == 0 ? vSlope_RH : 0 vSlope_T = numtype(vSlope_T) == 0 ? vSlope_T : 0 Make/O/N=(vNumpnts)/D/FREE TempTime = -1*(vNumpnts - p)*vIntTime + wObs_Time_MST[iPlumeDex] Make/O/N=(vNumpnts) $(strFolder + ":Met_T_" + num2str(iPlumeDex))/Wave=wMet_T Make/O/N=(vNumpnts) $(strFolder + ":Met_P_" + num2str(iPlumeDex))/Wave=wMet_P Make/O/N=(vNumpnts) $(strFolder + ":Met_RH_" + num2str(iPlumeDex))/Wave=wMet_RH Make/O/N=(vNumpnts) $(strFolder + ":Met_SZA_" + num2str(iPlumeDex))/Wave=wMet_SZA Make/O/N=(vNumpnts) $(strFolder + ":Met_jcorr_" + num2str(iPlumeDex))/Wave=wMet_jcorr Make/O/N=(vNumpnts) $(strFolder + ":Met_Saerosol_" + num2str(iPlumeDex))/Wave=wMet_Saerosol wMet_P = wObs_P[iPlumeDex] //Since P isn't going to change much, just use a constant value at the stopping point wMet_T = (TempTime[p] - vTime_Emit) * vSlope_T + wObs_T[iPlumeDex] //Interpolate over the observed T at HW wMet_RH = (TempTime[p] - vTime_Emit) * vSlope_RH + wObs_RH[iPlumeDex] //Same with RH wMet_SZA = SZA_Calc(wUSMag_Lat[0]*pi/180, wUSMag_Lon[0]*pi/180, 2017, TempTime[p], 0) //Calculate SZA based on interpolated time wMet_jcorr = vScalingFactor //Use a constant value for each day WaveStats/Q wObs_Saerosol //Get an average aerosol surface area from entire plume wMet_Saerosol = V_avg * 1e-8//Convert from um2/cm3 to cm2/cm3 EndFor EndFor MakeObsFamilyWaves() End Function PrepareBoxModelInput_Last() //Generate model input for the single run style model //Note, must run the puff model first SetDataFolder root: Wave/T twFlightName Wave wUSMag_Lat, wUSMag_Lon Variable vNumFlights = numpnts(twFlightName) Variable iFlightDex, iSpeciesDex Wave wTemp_HW = root:GroundSites:HW:TraceAndPM:TEMP Wave wRH_HW = root:GroundSites:HW:TraceAndPM:RH Wave wTime_HW = root:GroundSites:HW:TraceAndPM:DateTime_MST //First, get the number of individual runs that will be done (usually 1 or 2) Variable vNumRuns = 0 For(iFlightDex = 0; iFlightDex < vNumFlights; iFlightDex += 1) Wave Plume_WhichRun = $("root:" + twFlightName[iFlightDex] + ":Plumes:Plume_WhichRun") vNumRuns = Max(WaveMax(Plume_WhichRun), vNumRuns) EndFor Variable iRunDex Variable vScalingFactor = GetSolarScalingFactor() //Get jcorr scaling factor based on which day of the campaign For(iRunDex = 1; iRunDex <= vNumRuns; iRunDex += 1) //Get some data from the puff model first Wave wObs_Time_MST = $("root:BoxModel_Run" + num2str(iRunDex) + "_Puff:Obs_Time_MST") Wave wEmit_Time_MST = $("root:BoxModel_Run" + num2str(iRunDex) + "_Puff:Emit_Time_MST") If(WaveExists(wObs_Time_MST) == 0) Abort "Must prepare puff model first!" EndIf Wave wObs_T = $("root:BoxModel_Run" + num2str(iRunDex) + "_Puff:Obs_T") Wave wObs_RH = $("root:BoxModel_Run" + num2str(iRunDex) + "_Puff:Obs_RH") Wave wObs_P = $("root:BoxModel_Run" + num2str(iRunDex) + "_Puff:Obs_P") Wave wObs_Saerosol = $("root:BoxModel_Run" + num2str(iRunDex) + "_Puff:Obs_Saerosol") Wave wTimeWaveForModel = $("root:BoxModel_Run" + num2str(iRunDex) + "_Puff:Obs_Age_sec") Variable vNumModelPnts = numpnts(wObs_Time_MST) //Then prepare the "last" model String strFolder_Last = "root:BoxModel_Run" + num2str(iRunDex) + "_Last" NewDataFolder/O $strFolder_Last Variable vLastRun = vNumModelPnts - 1 //Assume that we want to use the last plume as the last run Variable vIntTime = 10//Assume we want 10 sec integration time Variable vStop = wTimeWaveForModel[vLastRun] //The is the stopping point Variable vNumpnts = ceil((vStop) / vIntTime) + 1 Variable vTime_Meas = wObs_Time_MST[vLastRun] Variable vTime_Emit = wEmit_Time_MST[vLastRun] Variable vTime_Meas_interp = BinarySearchInterp(wTime_HW, vTime_Meas) //Find where to grab data from HW Variable vTime_Emit_interp = BinarySearchInterp(wTime_HW, vTime_Emit) Variable vSlope_T = (wTemp_HW[vTime_Meas_interp] - wTemp_HW[vTime_Emit_interp] ) / (vTime_Meas - vTime_Emit) Variable vSlope_RH = (wRH_HW[vTime_Meas_interp] - wRH_HW[vTime_Emit_interp] ) / (vTime_Meas - vTime_Emit) vSlope_RH = numtype(vSlope_RH) == 0 ? vSlope_RH : 0 vSlope_T = numtype(vSlope_T) == 0 ? vSlope_T : 0 Make/O/N=(vNumpnts)/D/FREE TempTime = -1*(vNumpnts - p)*vIntTime + wObs_Time_MST[vLastRun] Make/O/N=(vNumpnts) $(strFolder_Last + ":Met_T_Last")/Wave=wMet_T Make/O/N=(vNumpnts) $(strFolder_Last + ":Met_P_Last")/Wave=wMet_P Make/O/N=(vNumpnts) $(strFolder_Last + ":Met_RH_Last")/Wave=wMet_RH Make/O/N=(vNumpnts) $(strFolder_Last + ":Met_SZA_Last")/Wave=wMet_SZA Make/O/N=(vNumpnts) $(strFolder_Last + ":Met_jcorr_Last")/Wave=wMet_jcorr Make/O/N=(vNumpnts) $(strFolder_Last + ":Met_Saerosol_Last")/Wave=wMet_Saerosol wMet_P = wObs_P[vLastRun] //Since P isn't going to change much, just use a constant value at the stopping point wMet_T = (TempTime[p] - vTime_Emit) * vSlope_T + wObs_T[vLastRun] //Interpolate over the observed T at HW wMet_RH = (TempTime[p] - vTime_Emit) * vSlope_RH + wObs_RH[vLastRun] //Repeat with RH wMet_SZA = SZA_Calc(wUSMag_Lat[0]*pi/180, wUSMag_Lon[0]*pi/180, 2017, TempTime[p], 0) //Calculate SZA based on interpolated time wMet_jcorr = vScalingFactor WaveStats/Q wObs_Saerosol wMet_Saerosol = V_avg * 1e-8//Convert from um2/cm3 to cm2/cm3 EndFor End Function MakeObsFamilyWaves() //Makes the total observed halogen waves (Cly, Bry, as well as HCl + pCl and HNO3 + pNO3) for comparison to the puff model SetDataFolder root: String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "BoxModel_Run*Puff", ";") Variable vNumRuns = ItemsInList(strFolderList, ";") Wave/T twFlightName Variable iFlightDex, iRunDex, iSpeciesDex, iDex //Define the families, along with their multipliers, given by a number + "x" + species //Note these are just the members of the family for which we have observations. String strFamilies = "HClpCl=HCl,AMS_Chl" strFamilies += ";HNO3pNO3=HNO3,AMS_NO3" strFamilies += ";Bry=2xBr2,BrCl,BrNO2,HOBr" strFamilies += ";Cly=HCl,2xCl2,BrCl,ClNO2,HOCl,AMS_Chl" strFamilies += ";NOy=NO2,NO,HNO3,2xN2O5,BrNO2,ClNO2,HONO" strFamilies += ";Oxtotal=O3,NO2,3xN2O5,1.5xHNO3,1.5xAMS_NO3,ClNO2" For(iRunDex = 1; iRunDex <= vNumRuns; iRunDex += 1) String strFolder = "root:BoxModel_Run" + num2str(iRunDex) + "_Puff" Wave wObs_Age_Sec = $(strFolder + ":Obs_Age_sec") Variable vNumPnts = numpnts(wObs_Age_Sec) For(iDex = 0; iDex < ItemsInList(strFamilies, ";"); iDex += 1) String strFamilyName = StringFromList(0,StringFromList(iDex,strFamilies, ";"), "=") //Get family name String strFamilyMembers = StringFromList(1,StringFromList(iDex,strFamilies, ";"), "=") //Get family members Make/O/N=(vNumPnts) $(strFolder + ":Obs_" + strFamilyName)/WAVE=wObs_Family //Make wave to hold the summed family observations wObs_Family = 0 For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strFamilyMembers, ","); iSpeciesDex += 1) String strCurrentSpecies = StringFromList(iSpeciesDex, strFamilyMembers, ",") Variable vMultiplier = 1 If(StringMatch(strCurrentSpecies, "*x*") == 1) //Check to see if there is a multiplication factor vMultiplier = str2num(strCurrentSpecies[0]) strCurrentSpecies = StringFromList(1, strCurrentSpecies, "x") EndIf Wave wCurrentSpecies = $(strFolder + ":Obs_" + strCurrentSpecies) wObs_Family += (wCurrentSpecies * vMultiplier) EndFor EndFor EndFor End Function GetSolarScalingFactor() //Gets the scaling factor jcorr, based on the ratio of the measured SR on this data relative to the campaign maximum SR. SetDataFolder root: Wave/T twFlightName Variable iFlightDex Wave SR_interp = root:GroundSites:HW:TraceAndPM:SR_interp Wave SR_time = root:GroundSites:HW:TraceAndPM:Timewave_MST //First get the parameters for the campaign-wide highest SR day WaveStats/Q SR_interp Variable vMax = V_maxLoc //Find the maximum SR day (it's 2/12/17, spoiler alert) String strDate = Secs2Date(SR_time[vMax],-2) //Get the date, then turn into seconds Variable vStartTimeHighest = Date2Secs(str2num(StringFromList(0, strDate, "-")), str2num(StringFromList(1, strDate, "-")), str2num(StringFromList(2, strDate, "-"))) + 9*3600 FindLevel/Q SR_time, vStartTimeHighest Variable vHighestDex = V_LevelX //Then get the parameters for the current day Wave wObs_Time = $("root:" + twFlightName[0] + ":TO_Timewave_MST") strDate = Secs2Date(wObs_Time[0], -2) Variable vStartTimeCurrent = Date2Secs(str2num(StringFromList(0, strDate, "-")), str2num(StringFromList(1, strDate, "-")), str2num(StringFromList(2, strDate, "-"))) + 9*3600 FindLevel/Q SR_time vStartTimeCurrent Variable vCurrentDex = V_LevelX //Then make a ratio wave between the two days, between 9am and 5pm (i.e. 7 hours = 420 minutes) Make/O/N=(420)/FREE SR_ratio SR_ratio = SR_interp[vCurrentDex + p] / SR_interp[p + vHighestDex] SR_ratio = SR_interp[p + vCurrentDex] < 10 ? NaN : SR_ratio[p] //Take the average of this ratio to get the scaling factor WaveStats/Q SR_ratio Return V_avg End Function SaveF0AMInputData_Puff() //Make F0AM input waves and save them to the hard drive SetDataFolder root: Variable iPlumeDex, iSpeciesDex, iDex Wave/T twFlightName String strCurrentFlight = twFlightName[0] String strCurrentDate = ReplaceString("flt", StringFromList(0, strCurrentFlight, "_"), "") String strMetParams = "P;T;RH;SZA;jcorr;Saerosol" //Met parameters for the model String strSpeciesList = "Cl2;Br2;BrCl;HClpCl;O3;NO2;NO;HNO3;HONO;N2O5;ClNO2;BrNO2" //Species to initialize the model String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "BoxModel_Run*Puff", ";") Variable vNumRuns = ItemsInList(strFolderList, ";") String strBasePath = "C:Users:cwomack:Documents:Research Projects:UWFPS:Halogens Analysis:F0AM:" Variable iRunDex For(iRunDex = 1; iRunDex <= vNumRuns; iRunDex += 1) String strFolder = "root:BoxModel_Run" + num2str(iRunDex) + "_Puff" //Make the folders on my hard drive, both input and output, because I don't know to make Matlab do that String strPath = strBasePath + "OutputConc:" + strCurrentDate + ":" NewPath/O/Q/C pSavePath, strPath strPath = strBasePath + "InputDAQ:" + strCurrentDate + ":" NewPath/O/Q/C pSavePath, strPath strPath = strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":" NewPath/O/Q/C pSavePath, strPath strPath = strBasePath + "InputDAQ:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":" NewPath/O/Q/C pSavePath, strPath Wave wTimeWaveForModel = $(strFolder + ":Obs_Age_sec") //Save the plume age wave Save/G/M="\r\n"/O/P=pSavePath wTimeWaveForModel as "Obs_Time_MST.dat" Variable vNumPlumes = numpnts(wTimeWaveForModel) //Save the integration time wave Wave wInt_Time = $(strFolder + ":Int_Time") Save/G/M="\r\n"/O/P=pSavePath wInt_Time as "Int_Time.dat" //Save each of the species waves For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpeciesList); iSpeciesDex += 1) Wave wObs_Species = $(strFolder + ":Obs_" + StringFromList(iSpeciesDex, strSpeciesList)) Save/G/M="\r\n"/O/P=pSavePath wObs_Species as "Obs_" + StringFromList(iSpeciesDex, strSpeciesList) + ".dat" If(wObs_Species[0] < 0 || numtype(wObs_Species[0]) == 2) Printf "Possible error! %s has bad first value in run %g\r", StringFromList(iSpeciesDex, strSpeciesList), iRunDex EndIf EndFor //Save each of the met parameter waves, one per puff For(iPlumeDex = 0; iPlumeDex < vNumPlumes; iPlumeDex += 1) strPath = strBasePath + "InputDAQ:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":Plume_" + num2str(iPlumeDex) NewPath/O/Q/C pSavePath, strPath For(iDex = 0; iDex < ItemsInList(strMetParams); iDex += 1) Wave wMet = $(strFolder + ":Met_" + StringFromList(iDex, strMetParams) + "_" + num2str(iPlumeDex)) Save/G/M="\r\n"/O/P=pSavePath wMet as "Met_" + StringfromList(iDex, strMetParams) + ".dat" EndFor EndFor KillPath/Z pSavePath EndFor End Function SaveF0AMInputData_Last() //Make F0AM input waves and save them to the hard drive SetDataFolder root: Variable iPlumeDex, iSpeciesDex, iDex Wave/T twFlightName String strCurrentFlight = twFlightName[0] String strCurrentDate = ReplaceString("flt", StringFromList(0, strCurrentFlight, "_"), "") String strMetParams = "P;T;RH;SZA;jcorr;Saerosol" //Met parameters for the model String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "BoxModel_Run*Last", ";") Variable vNumRuns = ItemsInList(strFolderList, ";") String strBasePath = "C:Users:cwomack:Documents:Research Projects:UWFPS:Halogens Analysis:F0AM:" Variable iRunDex For(iRunDex = 1; iRunDex <= vNumRuns; iRunDex += 1) //Make the folders on my hard drive, both input and output, because I don't know to make Matlab do that String strPath = strBasePath + "OutputConc:" + strCurrentDate + ":" NewPath/O/Q/C pSavePath, strPath strPath = strBasePath + "InputDAQ:" + strCurrentDate + ":" NewPath/O/Q/C pSavePath, strPath strPath = strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":" NewPath/O/Q/C pSavePath, strPath strPath = strBasePath + "InputDAQ:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":" NewPath/O/Q/C pSavePath, strPath String strFolder = "root:BoxModel_Run" + num2str(iRunDex) + "_Last" //Then save the met parameter waves for the "last" model run strPath = strBasePath + "InputDAQ:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":Plume_Last" NewPath/O/Q/C pSavePath, strPath For(iDex = 0; iDex < ItemsInList(strMetParams); iDex += 1) Wave wMet = $(strFolder + ":Met_" + StringFromList(iDex, strMetParams) + "_Last") Save/G/M="\r\n"/O/P=pSavePath wMet as "Met_" + StringfromList(iDex, strMetParams) + ".dat" EndFor KillPath/Z pSavePath EndFor End Function PrintVOCProfile() //Print out a VOC profile for copying and pasting into a Matlab F0AM input file SetDataFolder root:GroundSites Wave/T twVOCName, twVOCName_F0AM Wave VOC_scaling_factor_GRLWomack, VOC_scaling_factor_ESTGilman Variable vOverall_Scaling = 0.5 // This is the scaling factor for the GSL vs SLC, to account for the pollution being a bit better over the GSL Variable vSensitivity_Scaling = 1 //This is an additional scaling factor for doing sensitivity tests Printf "Overall scaling factor = %g, sensitivity scaling factor = %g\r", vOverall_Scaling, vSensitivity_Scaling //These are the F0AM species that I want to include as initial and background VOCs String strF0AMVOCs = "CH3OH;C2H5OH;HCHO;CH3CHO;CH3COCH3;MEK;MACR;C2H5CHO;C3H7CHO;DIEK;HEX3ONE;HCOOH;CH3CO2H;BENZENE;TOLUENE;OXYL;" strF0AMVOCs += "TM124B;C2H6;C3H8;NC4H10;IC4H10;NC5H12;IC5H12;NC6H14;C2H4;C3H6;C2H2" // String strF0AMVOCs = "Methanol;Ethanol;HCHO;Acetaldehyde;Acetone;MEK;MVK_MACR;propanal;butanal;C5Ketones;C6Ketones;HCOOH;Acetic_Acid;BENZENE;TOLUENE;" // strF0AMVOCs += "C8Aromatics;C9Aromatics;Ethane;Propane;n_Butane;iso_Butane;n_Pentane;iso_Pentane;n_hexane;Ethylene;Propylene;Acetylene" Variable iRunDex = 1 //Just use the first run. I doubt the 1st vs 2nd runs are that different. Wave wObs_Time_MST = $("root:BoxModel_Run" + num2str(iRunDex) + "_Puff:Obs_Time_MST") Variable vStartTime = WaveMin(wObs_Time_MST) //Get start and stop times for this run Variable vStopTime = WaveMax(wObs_Time_MST) Variable vStartDex, vStopDex Variable iSpeciesDex Wave wTimewave_UU = root:GroundSites:GS_Timewave_MST_hr Wave wBenzene_UU = root:GroundSites:Benzene_ppbv_UU_hr If(vStartTime > Date2Secs(2017,01,26)) //The UU PTR was down during the first PCAP, so for the 1/16 - 1/18 flights, have to approximate a similar level during the 2nd PCAP vStartDex = BinarySearch(wTimewave_UU, vStartTime) vStopDex = BinarySearch(wTimewave_UU, vStopTime) ElseIf(vStartTime < Date2Secs(2017,01,17)) //For the 1/16 flight vStartDex = 302 vStopDex = 307 ElseIf(vStartTime < Date2Secs(2017,01,18)) //For the 1/17 flight vStartDex = 331 vStopDex = 335 ElseIf(vStartTime < Date2Secs(2017,01,19)) //For the 1/18 flight vStartDex = 368 vStopDex = 369 EndIf WaveStats/Q/R=[vStartDex, vStopDex] wBenzene_UU //Get the average benzene level during this time. Use this value to scale the others Variable vAvgBenzene = V_avg For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strF0AMVOCs); iSpeciesDex += 1) String strCurrentSpecies = StringFromList(iSpeciesDex, strF0AMVOCs) FindValue/TEXT=strCurrentSpecies/TXOP=4 twVOCName_F0AM If(V_Value != -1) String strPTRName = twVOCName[V_Value] Wave wSpecies_UU = $("root:GroundSites:" + strPTRName + "_ppbv_UU_hr") Printf "\t\t'%s'\t\t", twVOCName_F0AM[V_Value] If(WaveExists(wSpecies_UU)) //If it was measured during UWFPS, use direct measurement WaveStats/Q/R=[vStartDex, vStopDex] wSpecies_UU Printf "%g", V_avg*vOverall_Scaling*vSensitivity_Scaling ElseIf(numtype(VOC_scaling_factor_GRLWomack[V_Value]) == 0) //If not, but exists in the GRL scaling factor, use that Printf "%g", vAvgBenzene*VOC_scaling_factor_GRLWomack[V_Value]*vOverall_Scaling*vSensitivity_Scaling ElseIf(numtype(VOC_scaling_factor_ESTGilman[V_value]) == 0) //Otherwise, use Jessica's continental background scaling factor Printf "%g", vAvgBenzene*VOC_scaling_factor_ESTGilman[V_Value]*vOverall_Scaling*vSensitivity_Scaling EndIf Printf "\t\t0;...\n" EndIf EndFor End //Read in and process the puff model output Function LoadModelOutput_Puff() //Load the F0AM output for the puff model SetDataFolder root: String strBasePath = "C:Users:cwomack:Documents:Research Projects:UWFPS:Halogens Analysis:F0AM:" Wave/T twFlightName String strCurrentFlight = twFlightName[0] String strCurrentDate = ReplaceString("flt", StringFromList(0, strCurrentFlight, "_"), "") String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "BoxModel_Run*Puff", ";") Variable vNumRuns = ItemsInList(strFolderList, ";") Variable iRunDex, iPlumeDex, iSpeciesDex String strWholeLine Variable vLineLength, vRef For(iRunDex = 1; iRunDex <= vNumRuns; iRunDex += 1) String strFolder = "root:BoxModelOut_Run" + num2str(iRunDex) + "_Puff" NewDataFolder/O/S $(strFolder) Wave wInt_Time = $("root:BoxModel_Run" + num2str(iRunDex) + "_Puff:Int_Time") Variable vNumPlumes = numpnts(wInt_Time) //Get the names of the species (Cnames) Make/O/N=0/T twCnames, twRnames, twInitNames Open/R vRef as strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":Cnames.txt" Do //Reads each line from the template. If we need to change the line (usually means we need to change a path), then print the new line. Otherwise print the line from the template. FReadLine vRef, strWholeLine vLineLength = strlen(strWholeLine) If(vLineLength > 0) InsertPoints/M=0 numpnts(twCnames), 1, twCnames twCnames[numpnts(twCnames) - 1] = RemoveEnding(strWholeLine, "\r") EndIf While(vLineLength > 0) Close vRef //Get the names of the reactions (Rnames) Open/R vRef as strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":Rnames.txt" Do //Reads each line from the template. If we need to change the line (usually means we need to change a path), then print the new line. Otherwise print the line from the template. FReadLine vRef, strWholeLine vLineLength=strlen(strWholeLine) If(vLineLength > 0) InsertPoints/M=0 numpnts(twRnames), 1, twRnames twRnames[numpnts(twRnames) - 1] = RemoveEnding(strWholeLine, "\r") EndIf While(vLineLength > 0) Close vRef //Get the names of the reactions (twInitNames) Open/R vRef as strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":InitNames.txt" Do //Reads each line from the template. If we need to change the line (usually means we need to change a path), then print the new line. Otherwise print the line from the template. FReadLine vRef, strWholeLine vLineLength=strlen(strWholeLine) If(vLineLength > 0) InsertPoints/M=0 numpnts(twInitNames), 1, twInitNames twInitNames[numpnts(twInitNames) - 1] = RemoveEnding(strWholeLine, "\r") EndIf While(vLineLength > 0) Close vRef //Get the names of the speciated VOCs (twVOCNames) String strVOCs = "Acid;Alkene;Aromatic;Aldehyde;Alcohol;AlkoxyRadical;Ketone;PeroxyNitrate;PeroxyRadical;Peroxide;PeroxyAcid" For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strVOCs); iSpeciesDex += 1) Make/O/N=0/T $("tw" + StringFromList(iSpeciesDex, strVOCs))/WAVE=twVOCNames Open/R vRef as strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":" + StringFromList(iSpeciesDex, strVOCs) + "Names.txt" Do //Reads each line from the template. If we need to change the line (usually means we need to change a path), then print the new line. Otherwise print the line from the template. FReadLine vRef, strWholeLine vLineLength=strlen(strWholeLine) If(vLineLength > 0) InsertPoints/M=0 numpnts(twVOCNames), 1, twVOCNames twVOCNames[numpnts(twVOCNames) - 1] = RemoveEnding(strWholeLine, "\r") EndIf While(vLineLength > 0) Close vRef EndFor //Read in the indices of the RO2 species MLLoadWave/O/Q/Y=4/S=2/N=$("iRO2") strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":iRO2.mat" Wave iRO2 iRO2 -= 1 //Correct for the fact that Matlab is 1-indexed, while Igor is 0-indexed //Get the rates, concentrations, and dilution matrices //Create the time waves based on the age and the integration time For(iPlumeDex = 0; iPlumeDex < vNumPlumes; iPlumeDex += 1) MLLoadWave/O/Q/M=2/Y=4/S=2/N=$("Model_Conc_" + num2str(iPlumeDex)) strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":Conc_" + num2str(iPlumeDex) + ".mat" MLLoadWave/O/Q/M=2/Y=4/S=2/N=$("Model_Rate_" + num2str(iPlumeDex)) strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":Rate_" + num2str(iPlumeDex) + ".mat" MLLoadWave/O/Q/M=2/Y=4/S=2/N=$("Model_Dil_" + num2str(iPlumeDex)) strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":Dil_" + num2str(iPlumeDex) + ".mat" Wave Model_output = $(strFolder + ":Model_Conc_" + num2str(iPlumeDex)) Make/O/N=(DimSize(Model_output, 0)) $(strFolder + ":Model_time_" + num2str(iPlumeDex) + "_sec")/WAVE=Model_time_sec Make/O/N=(DimSize(Model_output, 0)) $(strFolder + ":Model_time_" + num2str(iPlumeDex) + "_min")/WAVE=Model_time_min Model_time_sec = p*wInt_Time[iPlumeDex] Model_time_min = Model_time_sec/60 NewDataFolder/O $(strFolder + ":Plume_" + num2str(iPlumeDex)) EndFor //Read in the initial concentration matrix MLLoadWave/O/Q/M=2/Y=4/S=2/N=$("Model_InitConc") strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":InitConc.mat" //Finally, load the matrix of stoichiometric coefficients for each reaction MLLoadWave/O/Q/M=2/Y=4/S=2/N=$("Stoic") strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":Stoic.mat" EndFor End Function ProcessModelOutput_Puff() //Do some processing of the F0AM output matrices loaded in LoadModelOutput SetDataFolder root: String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "BoxModel_Run*Puff", ";") Variable vNumRuns = ItemsInList(strFolderList, ";") Variable iRunDex, iSpeciesDex, iPlumeDex //List of modeled species to process String strSpecies = "Cl2;Br2;BrCl;BrNO2;ClNO2;HCl;HNO3;HOBr;HOCl;HONO;N2O5;O3;NO;NO2;SO2;BrO;ClO;Br;Cl;HBr;OH;HO2;RO2;CH4" strSpecies += ";I2;I;IO;HOI;HI" For(iRunDex = 1; iRunDex <= vNumRuns; iRunDex += 1) String strFolder = "root:BoxModelOut_Run" + num2str(iRunDex) + "_Puff" Wave/T twCnames = $(strFolder + ":twCnames") Wave wObs_Age_Sec = $("root:BoxModel_Run" + num2str(iRunDex) + "_Puff" + ":Obs_Age_sec") Variable vNumPlumes = numpnts(wObs_Age_Sec) Make/O/N=(vNumPlumes) $(strFolder + ":ZeroLine")/WAVE=ZeroLine ZeroLine = 0 //Cycle through each species in strSpecies, and get the right column from the Conc matrix For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpecies); iSpeciesDex += 1) String strCurrentSpecies = StringFromList(iSpeciesDex, strSpecies) FindValue/TEXT=(strCurrentSpecies)/TXOP=4 twCnames If(V_Value != -1) //If it exists in the model output, process it Variable vSpeciesDex = V_Value If(StringMatch(strCurrentSpecies, "HCl") == 1) //Because we want to treat HCL as HCl + pCl, change the name here strCurrentSpecies = "HClpCl" EndIf If(StringMatch(strCurrentSpecies, "HNO3") == 1) //Because we want to treat HNO3 as HNO3 + pNO3, change the name here strCurrentSpecies = "HNO3pNO3" EndIf //Make a wave to hold each concentration wave for the full puff model Make/O/N=(vNumPlumes) $(strFolder + ":Model_" + strCurrentSpecies)/WAVE=wModel_Species_Puff //Then get the data for each plume from the Conc matrix For(iPlumeDex = 0; iPlumeDex < vNumPlumes; iPlumeDex += 1) Wave Model_Conc = $(strFolder + ":Model_Conc_" + num2str(iPlumeDex)) Variable vNumTimePnts = DimSize(Model_Conc, 0) Make/O/N=(vNumTimePnts) $(strFolder + ":Plume_" + num2str(iPlumeDex) + ":Model_" + strCurrentSpecies + "_" + num2str(iPlumeDex))/WAVE=wModel_Species wModel_Species = Model_Conc[p][vSpeciesDex] wModel_Species_Puff[iPlumeDex] = wModel_Species[vNumTimePnts - 1] //Take the last point to build the "puff" model output If(iSpeciesDex == 0) Make/O/N=(vNumTimePnts) $(strFolder + ":Plume_" + num2str(iPlumeDex) + ":ZeroLine")/WAVE=wZeroLine wZeroLine = 0 EndIf EndFor EndIf EndFor EndFor ProcessModelFamilies_Puff() CalculateModelObsError("") End Function ProcessModelFamilies_Puff() //Process the families of species (Ox, NOx, total Bry, Cly, etc.) SetDataFolder root: String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "BoxModel_Run*Puff", ";") Variable vNumRuns = ItemsInList(strFolderList, ";") //Define the families, along with their multipliers, given by a number + "x" + species String strFamilies = "BRy=" strFamilies += ";CLy=" strFamilies += ";NOy=NO2,NO,1.5xHNO3pNO3,2xN2O5,BrNO2,ClNO2,HONO" strFamilies += ";Ox=O3,NO2" strFamilies += ";NOx=NO2,NO" strFamilies += ";Oxtotal=O3,NO2,3xN2O5,1.5xHNO3pNO3,ClNO2" strFamilies += ";HOx=OH,HO2" strFamilies += ";HG0=HG0" strFamilies += ";HGI=HGBR,HGOH,HGCL" strFamilies += ";HGII=HGBRO,HGOHO,HGCLO,HGBRONO,HGOHONO,HGCLONO,HGBR2,HGBROH,HGBRCL,HGOH2,HGOHCL,HGCL2,HGBRHO2,HGBR2O,HGBRCLO,HGOHHO2,HGOHBRO,HGOHCLO,HGCLHO2,HGCLBRO,HGCL2O" strFamilies += ";Iy=2xI2,ICl,IBr,HI,HOI,IO,I,OIO,2xI2O2,2xI2O3,2xI2O4,INO,INO2,INO3" Variable iSpeciesDex, iRunDex, iDex, iPlumeDex, iFamilyDex For(iRunDex = 1; iRunDex <= vNumRuns; iRunDex += 1) String strFolder = "root:BoxModelOut_Run" + num2str(iRunDex) + "_Puff" Wave/T twCnames = $(strFolder + ":twCnames") Wave wObs_Age_Sec = $("root:BoxModel_Run" + num2str(iRunDex) + "_Puff:Obs_Age_sec") Variable vNumPlumes = numpnts(wObs_Age_Sec) Variable vMultiplier For(iFamilyDex = 0; iFamilyDex < ItemsInList(strFamilies, ";"); iFamilyDex += 1) String strFamilyName = StringFromList(0, StringFromList(iFamilyDex, strFamilies, ";"), "=") String strFamilyMembers = StringFromList(1, StringFromList(iFamilyDex, strFamilies, ";"), "=") Make/O/N=(vNumPlumes) $(strFolder + ":Model_" + strFamilyName)/WAVE=wModel_Family_Puff wModel_Family_Puff = 0 For(iPlumeDex = 0; iPlumeDex < vNumPlumes; iPlumeDex += 1) Wave wModel_Time = $(strFolder + ":Model_time_" + num2str(iPlumeDex) + "_sec") Variable vNumTimePnts = numpnts(wModel_Time) Make/O/N=(vNumTimePnts) $(strFolder + ":Plume_" + num2str(iPlumeDex) + ":Model_" + strFamilyName + "_" + num2str(iPlumeDex))/WAVE=wModel_Family wModel_Family = 0 Wave Model_Conc = $(strFolder + ":Model_Conc_" + num2str(iPlumeDex)) If(strlen(strFamilyMembers) == 0) //For Bry and Cly, e.g., search twCnames for species with bromine or chlorine String strAtom = StringFromList(0, strFamilyName, "y") String strAllSpecies = "" For(iDex = 0; iDex < numpnts(twCnames); iDex += 1) If(StringMatch(twCnames[iDex], "*" + strAtom + "*") == 1) strAllSpecies += twCnames[iDex] + ";" wModel_Family += Model_Conc[p][iDex] * SelectNumber(StringMatch(twCnames[iDex], strAtom + "2") == 1, 1, 2) EndIf EndFor Note wModel_Family, strAllSpecies Else //For others, just add up the members of the family For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strFamilyMembers, ","); iSpeciesDex += 1) String strCurrentSpecies = StringFromList(iSpeciesDex, strFamilyMembers, ",") vMultiplier = 1 If(StringMatch(strCurrentSpecies, "*x*") == 1) vMultiplier = str2num(strCurrentSpecies[0]) strCurrentSpecies = StringFromList(1, strCurrentSpecies, "x") EndIf Wave wSpecies = $(strFolder + ":Plume_" + num2str(iPlumeDex) + ":Model_" + strCurrentSpecies + "_" + num2str(iPlumeDex)) If(WaveExists(wSpecies)) wModel_Family += wSpecies * vMultiplier Else FindValue/TEXT=strCurrentSpecies/TXOP=4 twCnames If(V_Value != -1) wModel_Family += Model_Conc[p][V_Value] * vMultiplier Else wModel_Family = 0 EndIf EndIf EndFor EndIf wModel_Family_Puff[iPlumeDex] = wModel_Family[vNumTimePnts - 1] EndFor EndFor EndFor End Function PlotModelOutput_Puff() SetDataFolder root: String strSpeciesToPlot = "Cl2;Br2;BrCl;HClpCl;O3;NO;NO2;HNO3pNO3;HONO;N2O5;BrNO2;ClNO2;HOBr;HOCl;BrO;ClO" String strSpeciesUnits = "ppb;ppb;ppb;ppb;ppb;ppb;ppb;ppb;ppb;ppb;ppb;ppb;ppb;ppb;cps;cps" String strSpeciesAxis = "log;log;log;log;lin;log;log;log;log;lin;log;log;log;log;lin;lin" String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "BoxModel_Run*Puff", ";") Variable vNumRuns = ItemsInList(strFolderList, ";") Variable iRunDex For(iRunDex = 1; iRunDex <= vNumRuns; iRunDex += 1) String strLayoutName = "Model_Output_Run" + num2str(iRunDex) String strFolder = "root:BoxModelOut_Run" + num2str(iRunDex) + "_Puff" Wave/T twCnames = $(strFolder + ":twCnames") DoWindow/K $strLayoutName NewLayout/N=$strLayoutName as strLayoutName Variable iSpeciesDex, vPage = 1, iDex, iPlumeDex Wave wObs_Age = $("root:BoxModel_Run" + num2str(iRunDex) + "_Puff:Obs_Age_min") For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpeciesToPlot); iSpeciesDex += 1) String strCurrentSpecies = StringfromList(iSpeciesDex, strSpeciesToPlot) Wave wObs_Species = $("root:BoxModel_Run" + num2str(iRunDex) + "_Puff:Obs_" + strCurrentSpecies) Wave wModel_Species = $(strFolder + ":Model_" + strCurrentSpecies) String strGraphTitle = "Model_Obs_Run" + num2str(iRunDex) + "_" + strCurrentSpecies DoWindow/K/Z $strGraphTitle Display as strGraphTitle DoWindow/C $strGraphTitle Appendtograph wModel_Species/TN=$(strCurrentSpecies + "_Model") vs wObs_Age If(stringmatch(StringfromList(iSpeciesDex, strSpeciesUnits), "ppb") == 1) Appendtograph wObs_Species/TN=$(strCurrentSpecies + "_Obs") vs wObs_Age ModifyGraph mirror = 2 ElseIf(stringmatch(StringfromList(iSpeciesDex, strSpeciesUnits), "cps") == 1) Appendtograph/R wObs_Species/TN=$(strCurrentSpecies + "_Obs") vs wObs_Age Label right "Obs " + strCurrentSpecies + " (cps)" EndIf // Variable vLowerBound = 1000 For(iPlumeDex = 0; iPlumeDex < numpnts(wObs_Species); iPlumeDex += 1) Wave mModel_Conc = $(strFolder + ":Model_conc_" + num2str(iPlumeDex)) Wave wModel_Time = $(strFolder + ":Model_time_" + num2str(iPlumeDex) + "_Min") If(StringMatch(strCurrentSpecies, "HClpCl") == 1) String strF0AMname = "HCL" ElseIf(StringMatch(strCurrentSpecies, "HNO3pNO3") == 1) strF0AMname = "HNO3" Else strF0AMname = strCurrentSpecies EndIf FindValue/TEXT=(strF0AMname)/TXOP=2 twCnames Variable vSpeciesDex = V_Value AppendToGraph mModel_Conc[][vSpeciesDex]/TN=$("Plume_" + num2str(iPlumeDex)) vs wModel_Time ModifyGraph lstyle($("Plume_" + num2str(iPlumeDex)))=3,rgb($("Plume_" + num2str(iPlumeDex)))=(48059,48059,48059) // WaveStats/Q/RMD=[][vSpeciesDex] mModel_Conc // vLowerBound = V_min < vLowerBound ? V_min : vLowerBound EndFor ModifyGraph mode($strCurrentSpecies + "_Model") = 4, mode($strCurrentSpecies + "_Obs") = 3, marker = 19 ModifyGraph rgb($(strCurrentSpecies + "_Model")) = (0,0,0) Label bottom "Plume age (min)" Label left strCurrentSpecies + " (" + StringfromList(iSpeciesDex, strSpeciesUnits) + ")" ModifyGraph standoff=0 TextBox/C/N=text0/A=RT strCurrentSpecies SetAxis bottom 0.1,* ModifyGraph log(left) = SelectNumber(StringMatch(StringFromList(iSpeciesDex, strSpeciesAxis), "log") == 1 , 0, 1) // vLowerBound = min(vLowerBound, WaveMin(wObs_Species)) // SetAxis left max(vLowerBound,1e-10), * ModifyGraph log(bottom) = 1 SetAxis bottom 5,* If(mod(iSpeciesDex, 8) == 0 && iSpeciesDex != 0) LayoutPageAction/W=$strLayoutName appendPage vPage += 1 EndIf AppendLayoutObject/W=$strLayoutName/PAGE=(vPage) graph $strGraphTitle EndFor For(iDex = 0; iDex < vPage; iDex += 1) String strExecuteText = "Tile/A=(4,2)" LayoutPageAction/W=$strLayoutName Page=(iDex + 1) Execute strExecuteText Endfor EndFor PlotModelFamilies_Puff() End Function PlotModelFamilies_Puff() //Plot each of the families in a stacked plot SetDataFolder root: ColorTab2Wave Rainbow Wave M_colors String strFamilies = "Bry=Br2,BrCl,BrNO2,HOBr,Br,HBr" strFamilies += ";Cly=Cl2,BrCl,ClNO2,HOCl,Cl,HClpCl" strFamilies += ";NOy=NO2,NO,HNO3pNO3,N2O5,BrNO2,ClNO2,HONO" strFamilies += ";Ox=O3,NO2" strFamilies += ";NOx=NO2,NO" strFamilies += ";Oxtotal=O3,NO2,N2O5,HNO3pNO3,ClNO2" String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "BoxModel_Run*Puff", ";") Variable vNumRuns = ItemsInList(strFolderList, ";") Variable iRunDex, iSpeciesDex, vColor, iFamilyDex String strFamilyName, strSpeciesInFamily, strPlotName, strLegend, strCurrentSpecies, strFamilyMembers For(iRunDex = 1; iRunDex <= vNumRuns; iRunDex += 1) String strFolder = "root:BoxModel_Run" + num2str(iRunDex) + "_Puff" String strFolderOut = "root:BoxModelOut_Run" + num2str(iRunDex) + "_Puff" Wave wObs_Age = $(strFolder + ":Obs_Age_min") Wave wZeroLine = $(strFolderOut + ":ZeroLine") For(iFamilyDex = 0; iFamilyDex < ItemsInList(strFamilies, ";"); iFamilyDex += 1) strFamilyName = StringFromList(0, StringFromList(iFamilyDex, strFamilies, ";"), "=") strFamilyMembers = StringFromList(1, StringFromList(iFamilyDex, strFamilies, ";"), "=") strPlotName = strFamilyName + "_family_Run" + num2str(iRunDex) DoWindow/K/Z $strPlotName Display as strPlotName DoWindow/C $strPlotName Wave wFamilyWave = $(strFolderOut + ":Model_" + strFamilyName) Wave wFamilyWave_Obs = $(strFolder + ":Obs_" + strFamilyName) AppendToGraph wFamilyWave/TN=$strFamilyName vs wObs_Age ModifyGraph rgb($strFamilyName) = (0,0,0), lsize($strFamilyName) = 3 strLegend = "\Z08\s(" + strFamilyName + ") " + strFamilyName + "\r" For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strFamilyMembers, ","); iSpeciesDex += 1) strCurrentSpecies = StringFromList(iSpeciesDex, strFamilyMembers, ",") vColor = iSpeciesDex / ItemsInList(strFamilyMembers, ",") * 99 Wave wSpecies = $(strFolderOut + ":Model_" + strCurrentSpecies) AppendToGraph wSpecies/TN=$strCurrentSpecies vs wObs_Age ModifyGraph hbFill($strCurrentSpecies)=5,toMode($strCurrentSpecies)=2,rgb($strCurrentSpecies)=(M_colors[vColor][0], M_colors[vColor][1], M_Colors[vColor][2]) ModifyGraph mode($strCurrentSpecies)=7 strLegend += "\s(" + strCurrentSpecies + ") " + strCurrentSpecies + "\r" EndFor AppendTograph/C=(0,0,0) wZeroLine vs wObs_Age AppendToGraph wFamilyWave_Obs/TN=$(strFamilyName + "_Obs") vs wObs_Age ModifyGraph rgb($(strFamilyName + "_Obs")) = (0,0,0), mode($(strFamilyName + "_Obs")) = 3, marker($(strFamilyName + "_Obs")) = 19 strLegend = RemoveEnding(strLegend, "\r") Legend/A=LT strLegend Label bottom "Model time (min)" Label left strFamilyName + " family (ppbv)" EndFor EndFor KillWaves M_Colors End Function PlotSpeciesByPlume(strSpecies, iPlumeDex, iRunDex) String strSpecies Variable iPlumeDex, iRunDex SetDataFolder root: If(strlen(strSpecies) == 0) Prompt strSpecies, "Species:" Prompt iPlumeDex, "Plume:" Prompt iRunDex, "Run:" DoPrompt "Select species and plume:", strSpecies, iPlumeDex, iRunDex EndIf Wave/T twCnames = $("root:BoxModelOut_Run" + num2str(iRunDex) + "_Puff:twCnames") FindValue/TEXT=strSpecies/TXOP=4 twCnames If(V_Value == -1) Print "Species not found" Abort EndIf Variable vSpeciesDex = V_Value Wave wModel_Conc = $("root:BoxModelOut_Run" + num2str(iRunDex) + "_Puff:Model_Conc_" + num2str(iPlumeDex)) Wave wModel_Time = $("root:BoxModelOut_Run" + num2str(iRunDex) + "_Puff:Model_Time_" + num2str(iPlumeDex) + "_min") String strPlotName = strSpecies + "_Plume" + num2str(iPlumeDex) DoWindow/K/Z $strPlotName Display as strPlotName DoWindow/C $strPlotName AppendToGraph wModel_Conc[][vSpeciesDex]/TN=$strSpecies vs wModel_Time Label bottom "Plume Age" Label left strSpecies + " (ppbv)" Legend/A=RT strSpecies + "\rPlume " + num2str(iPlumeDex) + "\rIndex=" + num2str(vSpeciesDex) End Function CalculateModelObsError(strDataFolder) String strDataFolder DFREF dfBase = $("root:" + strDataFolder) SetDataFolder root: // String strSpecies = "Cl2;Br2;BrCl;BrNO2;ClNO2;HClpCl;HNO3pNO3;HOBr;HOCl;HONO;N2O5;O3;NO;NO2" String strSpecies = "O3;Cl2;Br2;BrCl;HClpCl;NO;NO2;HONO;N2O5;HNO3pNO3;ClNO2;BrNO2;HOBr;HOCl" Variable vNumSpecies = ItemsInList(strSpecies) String strFolderList = DataFolderDir(1, dfBase) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "BoxModel_Run*Puff", ";") Variable vNumRuns = ItemsInList(strFolderList, ";") Variable iRunDex, iSpeciesDex For(iRunDex = 1; iRunDex <= vNumRuns; iRunDex += 1) String strFolderOut = "root:" + strDataFolder + "BoxModelOut_Run" + num2str(iRunDex) + "_Puff" String strFolderIn = "root:" + strDataFolder + "BoxModel_Run" + num2str(iRunDex) + "_Puff" Make/O/N=(vNumSpecies)/T $(strFolderOut + ":twObsSpecies")/WAVE=twObsSpecies twObsSpecies = StringFromList(p, strSpecies) Make/O/N=(vNumSpecies) $(strFolderOut + ":ModelObs_RMSE")/WAVE=RMSE Make/O/N=(vNumSpecies) $(strFolderOut + ":ModelObs_Bias")/WAVE=Bias Make/O/N=(vNumSpecies) $(strFolderOut + ":ModelObs_logSlope")/WAVE=LogSlope Make/O/N=(vNumSpecies) $(strFolderOut + ":ModelObs_logr2")/WAVE=Logr2 For(iSpeciesDex = 0; iSpeciesDex < vNumSpecies; iSpeciesDex += 1) String strCurrentSpecies = StringFromList(iSpeciesDex, strSpecies) Wave wModel_Species = $(strFolderOut + ":Model_" + strCurrentSpecies) Wave wObs_Species = $(strFolderIn + ":Obs_" + strCurrentSpecies) Make/O/N=(numpnts(wModel_Species))/FREE TempLogModel, TempLogObs TempLogModel = Log(wModel_Species) TempLogObs = Log(wObs_Species) WaveStats/Q wObs_Species If(V_npnts > 0) CurveFit/Q line wModel_Species /X=wObs_Species Wave W_Coef // Make/O/N=(numpnts(wModel_Species))/FREE TempRMSE // // TempRMSE = sqrt((wModel_Species - wObs_Species)^2 / numpnts(wModel_Species)) // TempRMSE = sqrt((wModel_Species - wObs_Species)^2 / wModel_Species) // RMSE[iSpeciesDex] = sum(TempRMSE) Bias[iSpeciesDex] = W_coef[1] RMSE[iSpeciesDex] = V_r2 CurveFit/Q line TempLogModel /X=TempLogObs LogSlope[iSpeciesDex] = W_Coef[1] Logr2[iSpeciesDex] = V_r2 Else Bias[iSpeciesDex] = NaN RMSE[iSpeciesDex] = NaN LogSlope[iSpeciesDex] = NaN Logr2[iSpeciesDex] = NaN EndIf Endfor Endfor End //Process the last model run Function LoadModelOutput_Last() //Load the F0AM output for the last model. Very similar to the puff model version SetDataFolder root: String strBasePath = "C:Users:cwomack:Documents:Research Projects:UWFPS:Halogens Analysis:F0AM:" Wave/T twFlightName String strCurrentFlight = twFlightName[0] String strCurrentDate = ReplaceString("flt", StringFromList(0, strCurrentFlight, "_"), "") String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "BoxModel_Run*Last", ";") Variable vNumRuns = ItemsInList(strFolderList, ";") Variable iRunDex, iPlumeDex, iSpeciesDex String strWholeLine Variable vLineLength, vRef For(iRunDex = 1; iRunDex <= vNumRuns; iRunDex += 1) String strFolder = "root:BoxModelOut_Run" + num2str(iRunDex) + "_Last" NewDataFolder/O/S $(strFolder) Wave wInt_Time = $("root:BoxModel_Run" + num2str(iRunDex) + "_Last" + ":Int_Time") Wave wObs_Age_Sec = $("root:BoxModel_Run" + num2str(iRunDex) + "_Last" + ":Obs_Age_sec") Variable vNumPlumes = numpnts(wInt_Time) //Get the names of the species (Cnames) Make/O/N=0/T twCnames, twRnames, twInitNames Open/R vRef as strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":Cnames.txt" Do //Reads each line from the template. If we need to change the line (usually means we need to change a path), then print the new line. Otherwise print the line from the template. FReadLine vRef, strWholeLine vLineLength = strlen(strWholeLine) If(vLineLength > 0) InsertPoints/M=0 numpnts(twCnames), 1, twCnames twCnames[numpnts(twCnames) - 1] = RemoveEnding(strWholeLine, "\r") EndIf While(vLineLength > 0) Close vRef //Get the names of the reactions (Rnames) Open/R vRef as strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":Rnames.txt" Do //Reads each line from the template. If we need to change the line (usually means we need to change a path), then print the new line. Otherwise print the line from the template. FReadLine vRef, strWholeLine vLineLength=strlen(strWholeLine) If(vLineLength > 0) InsertPoints/M=0 numpnts(twRnames), 1, twRnames twRnames[numpnts(twRnames) - 1] = RemoveEnding(strWholeLine, "\r") EndIf While(vLineLength > 0) Close vRef //Get the names of the reactions (twInitNames) Open/R vRef as strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":InitNames.txt" Do //Reads each line from the template. If we need to change the line (usually means we need to change a path), then print the new line. Otherwise print the line from the template. FReadLine vRef, strWholeLine vLineLength=strlen(strWholeLine) If(vLineLength > 0) InsertPoints/M=0 numpnts(twInitNames), 1, twInitNames twInitNames[numpnts(twInitNames) - 1] = RemoveEnding(strWholeLine, "\r") EndIf While(vLineLength > 0) Close vRef String strVOCs = "Acid;Alkene;Aromatic;Aldehyde;Alcohol;AlkoxyRadical;Ketone;PeroxyNitrate;PeroxyRadical;Peroxide;PeroxyAcid" For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strVOCs); iSpeciesDex += 1) Make/O/N=0/T $("tw" + StringFromList(iSpeciesDex, strVOCs))/WAVE=twVOCNames Open/R vRef as strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":" + StringFromList(iSpeciesDex, strVOCs) + "Names.txt" Do //Reads each line from the template. If we need to change the line (usually means we need to change a path), then print the new line. Otherwise print the line from the template. FReadLine vRef, strWholeLine vLineLength=strlen(strWholeLine) If(vLineLength > 0) InsertPoints/M=0 numpnts(twVOCNames), 1, twVOCNames twVOCNames[numpnts(twVOCNames) - 1] = RemoveEnding(strWholeLine, "\r") EndIf While(vLineLength > 0) Close vRef EndFor MLLoadWave/O/Q/Y=4/S=2/N=$("iRO2") strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":iRO2.mat" Wave iRO2 iRO2 -= 1 //Get the rates, concentrations, and dilution matrices //Create the time waves based on the age and the integration time MLLoadWave/O/Q/M=2/Y=4/S=2/N=$("Model_Conc_Last") strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":Conc_Last.mat" MLLoadWave/O/Q/M=2/Y=4/S=2/N=$("Model_Rate_Last") strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":Rate_Last.mat" MLLoadWave/O/Q/M=2/Y=4/S=2/N=$("Model_Dil_Last") strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":Dil_Last.mat" Wave Model_output = $(strFolder + ":Model_Conc_Last") Make/O/N=(DimSize(Model_output, 0)) $(strFolder + ":Model_time_Last_sec")/WAVE=Model_time_sec Make/O/N=(DimSize(Model_output, 0)) $(strFolder + ":Model_time_Last_min")/WAVE=Model_time_min Model_time_sec = p*10 Model_time_min = Model_time_sec/60 MLLoadWave/O/Q/M=2/Y=4/S=2/N=$("Model_InitConc") strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":InitConc.mat" //Finally, load the matrix of stoichiometric coefficients for each reaction MLLoadWave/O/Q/M=2/Y=4/S=2/N=$("Stoic") strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":Stoic.mat" EndFor End Function ProcessModelOutput_Last() //Process the last model output, and the families of species (Ox, NOx, total Bry, Cly, etc.) SetDataFolder root: String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "BoxModel_Run*Last", ";") Variable vNumRuns = ItemsInList(strFolderList, ";") String strSpecies = "Cl2;Br2;BrCl;BrNO2;ClNO2;HCl;HNO3;HOBr;HOCl;HONO;N2O5;O3;NO;NO2;SO2;BrO;ClO;Br;Cl;HBr;OH;HO2;RO2;CH4" strSpecies += ";I2;I;IO;HOI;HI" //Define the families, along with their multipliers, given by a number + "x" + species String strFamilies = "BRy=" strFamilies += ";CLy=" strFamilies += ";NOy=NO2,NO,1.5xHNO3pNO3,2xN2O5,BrNO2,ClNO2,HONO" strFamilies += ";Ox=O3,NO2" strFamilies += ";NOx=NO2,NO" strFamilies += ";Oxtotal=O3,NO2,3xN2O5,1.5xHNO3pNO3,ClNO2" strFamilies += ";HOx=OH,HO2" strFamilies += ";HOx=OH,HO2" strFamilies += ";elemHG0=HG0" strFamilies += ";HGI=HGBR,HGOH,HGCL" strFamilies += ";HGII=HGBRO,HGOHO,HGCLO,HGBRONO,HGOHONO,HGCLONO,HGBR2,HGBROH,HGBRCL,HGOH2,HGOHCL,HGCL2,HGBRHO2,HGBR2O,HGBRCLO,HGOHHO2,HGOHBRO,HGOHCLO,HGCLHO2,HGCLBRO,HGCL2O" strFamilies += ";HGy=HG0,HGI,HGII" strFamilies += ";Iy=2xI2,ICl,IBr,HI,HOI,IO,I,OIO,2xI2O2,2xI2O3,2xI2O4,INO,INO2,INO3" Variable iSpeciesDex, iRunDex, iDex, iPlumeDex, iFamilyDex For(iRunDex = 1; iRunDex <= vNumRuns; iRunDex += 1) String strFolder = "root:BoxModelOut_Run" + num2str(iRunDex) + "_Last" Wave/T twCnames = $(strFolder + ":twCnames") Wave wModel_Time = $(strFolder + ":Model_time_Last_sec") Wave Model_Conc = $(strFolder + ":Model_Conc_Last") Variable vNumTimePnts = numpnts(wModel_Time) Variable vMultiplier //First, grab the species from Model_Conc matrix For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpecies); iSpeciesDex += 1) String strCurrentSpecies = StringFromList(iSpeciesDex, strSpecies) FindValue/TEXT=(strCurrentSpecies)/TXOP=4 twCnames If(V_Value != -1) Variable vSpeciesDex = V_Value If(StringMatch(strCurrentSpecies, "HCl") == 1) //Because we want to treat HCL as HCl + pCl, change the name here strCurrentSpecies = "HClpCl" EndIf If(StringMatch(strCurrentSpecies, "HNO3") == 1) //Because we want to treat HCL as HCl + pCl, change the name here strCurrentSpecies = "HNO3pNO3" EndIf Make/O/N=(vNumTimePnts) $(strFolder + ":Model_" + strCurrentSpecies + "_Last")/WAVE=wModel_Species wModel_Species = Model_Conc[p][vSpeciesDex] EndIf EndFor Make/O/N=(vNumTimePnts) $(strFolder + ":ZeroLine")/WAVE=wZeroLine wZeroLine = 0 //Next, add up the members of families For(iFamilyDex = 0; iFamilyDex < ItemsInList(strFamilies, ";"); iFamilyDex += 1) String strFamilyName = StringFromList(0, StringFromList(iFamilyDex, strFamilies, ";"), "=") String strFamilyMembers = StringFromList(1, StringFromList(iFamilyDex, strFamilies, ";"), "=") Make/O/N=(vNumTimePnts) $(strFolder + ":Model_" + strFamilyName + "_Last")/WAVE=wModel_Family wModel_Family = 0 If(strlen(strFamilyMembers) == 0) String strAtom = StringFromList(0, strFamilyName, "y") String strAllSpecies = "" For(iDex = 0; iDex < numpnts(twCnames); iDex += 1) If(StringMatch(twCnames[iDex], "*" + strAtom + "*") == 1) strAllSpecies += twCnames[iDex] + ";" wModel_Family += Model_Conc[p][iDex] * SelectNumber(StringMatch(twCnames[iDex], strAtom + "2") == 1, 1, 2) EndIf EndFor Note wModel_Family, strAllSpecies Else For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strFamilyMembers, ","); iSpeciesDex += 1) strCurrentSpecies = StringFromList(iSpeciesDex, strFamilyMembers, ",") vMultiplier = 1 If(StringMatch(strCurrentSpecies, "*x*") == 1) vMultiplier = str2num(strCurrentSpecies[0]) strCurrentSpecies = StringFromList(1, strCurrentSpecies, "x") EndIf Wave wSpecies = $(strFolder + ":Model_" + strCurrentSpecies + "_Last") If(WaveExists(wSpecies)) wModel_Family += wSpecies * vMultiplier Else FindValue/TEXT=strCurrentSpecies/TXOP=4 twCnames If(V_value != -1) wModel_Family += Model_Conc[p][V_Value] * vMultiplier Else wModel_Family = 0 EndIf EndIf EndFor EndIf EndFor EndFor ProcessHalogenFamilies_Last() ProcessModelRO2Family_Last() ProcessModelVOCs_Last() GetChangeInModelSpecies() MakeChainLengths() End Function ProcessHalogenFamilies_Last() //Sort the halogenated VOCs into various categories, to help later on with the summing of VOCs SetDataFolder root: String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "BoxModel_Run*Last", ";") Variable vNumRuns = ItemsInList(strFolderList, ";") Variable iRunDex, iPlumeDex, iDex, iSpeciesDex //Note, these are hand-sorted. Could be some mistakes? // String strFamilyMembers_Br = "Br2;BrCl;BrO;Br;HBr;HOBr;BrNO2;BrNO3" //Standalone Br species String strFamilyMembers_Br = "BrNO3" //Standalone Br species strFamilyMembers_Br += ";BrAldehyde=CHOBR,BRPROPAL,BRETAL" //bromoaldehydes strFamilyMembers_Br += ";BrKetone=CH3COBR,CH2BRCOCH3,BRCOCH2OOH,BRCOCH2O,CH3COCBRO,CH3COCBRO2,BRCOCH2O2,CH3COBROOH" //bromoketones strFamilyMembers_Br += ";BrAlcohol=CH2BROH,BRIPROPOL,BRPROPOL,CH3CHBROH,CH2BRCH2OH" //bromoalcohols strFamilyMembers_Br += ";BrAcid=BRETO2H,CH3CHBRCOOH" //bromocarboxylicacids strFamilyMembers_Br += ";BrPeroxyAcid=BRETO3H,CH3CHBRCO3H" //bromoperoxyacids strFamilyMembers_Br += ";BrPeroxyNitrate=BRETPAN,BRPPN" //bromo peroxynitrates strFamilyMembers_Br += ";BrAPR=BRETO3,C2H5BRCO3" //bromo acyl peroxy radicsl strFamilyMembers_Br += ";BrAlkoxyRadical=CH2BRO,CH3CHBRO,CH2BRCH2O,CH3COCBRO,BRCOCH2O" //bromo alkoxy radicals strFamilyMembers_Br += ";BrPeroxide=CH3COBROOH,BRCOCH2OOH,CH2BROOH,BRPROPOOH,CH3CHBROOH,CH2BRCOOH" //bromoperoxids strFamilyMembers_Br += ";BrPeroxyRadical=CH2BRO2,BRPROPO2,IBRPROPO2,CH3CHBRO2,CH2BRCH2O2,CH3COCBRO2,BRCOCH2O2" //bromo peroxy radicals // String strFamilyMembers_Cl = "Cl2;BrCl;ClO;Cl;HCl;HOCl;ClNO2;ClNO3;CLOO;OCLO;CL2O2;CLNO" //Standalone Cl species String strFamilyMembers_Cl = "ClNO3;CLOO;OCLO;CL2O2;CLNO" //Standalone Cl species strFamilyMembers_Cl += ";ClAldehyde=CHOCL,CLPROPAL,CLETAL" //chloroaldehydes strFamilyMembers_Cl += ";ClKetone=CH3COCL,CH2CLCOCH3,CLCOCH2OOH,CLCOCH2O,CH3COCCLO,CH3COCCLO2,CLCOCH2O2,CH3COCLOOH" //chloroketones strFamilyMembers_Cl += ";ClAlcohol=CH2CLOH,CLIPROPOL,CLPROPOL,CH3CHCLOH,CH2CLCH2OH" //chloroalcohols strFamilyMembers_Cl += ";ClAcid=CLETO2H,CH3CHCLCOOH" //chlorocarboxylicacids strFamilyMembers_Cl += ";ClPeroxyAcid=CLETO3H,CH3CHCLCO3H" //chloroperoxyacids strFamilyMembers_Cl += ";ClPeroxyNitrate=CLETPAN,CLPPN" //chloro peroxynitrates strFamilyMembers_Cl += ";ClAPR=CLETO3,C2H5CLCO3" //chloro acyl peroxy radicsl strFamilyMembers_Cl += ";ClAlkoxyRadical=CH2CLO,CH3CHCLO,CH2CLCH2O,CH3COCCLO,CLCOCH2O" //chloro alkoxy radicals strFamilyMembers_Cl += ";ClPeroxide=CH3COCLOOH,CLCOCH2OOH,CH2CLOOH,CLPROPOOH,CH3CHCLOOH,CH2CLCOOH" //chloroperoxids strFamilyMembers_Cl += ";ClPeroxyRadical=CH2CLO2,CLPROPO2,ICLPROPO2,CH3CHCLO2,CH2CLCH2O2,CH3COCCLO2,CLCOCH2O2" //chloro peroxy radicals For(iRunDex = 1; iRunDex <= vNumRuns; iRunDex += 1) String strFolder = "root:BoxModelOut_Run" + num2str(iRunDex) + "_Last" Wave/T twCnames = $(strFolder + ":twCnames") Wave Model_Conc = $(strFolder + ":Model_Conc_Last") Wave Model_Time = $(strFolder + ":Model_Time_Last_min") Make/O/N=(numpnts(Model_Time)) $(strFolder + ":Model_Bry_OtherBr_Last")/WAVE=wOtherBr wOtherBr = 0 //First, cycle through the brominated VOCs For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strFamilyMembers_Br, ";"); iSpeciesDex += 1) String strCurrentSpecies = StringFromList(iSpeciesDex, strFamilyMembers_Br, ";") If(StringMatch(strCurrentSpecies, "*=*") == 0) //Get the other random Br species that aren't important enough to isolate, but still are there // Make/O/N=(numpnts(Model_Time)) $(strFolder + ":Model_" + strCurrentSpecies + "_Last")/WAVE=wSpecies FindValue/TEXT=strCurrentSpecies/TXOP=4 twCnames // wSpecies = Model_Conc[p][V_Value] wOtherBr += Model_Conc[p][V_Value] Else //Then add up the members of each family String strFamilyName = StringFromList(0, strCurrentSpecies, "=") String strSubMembers = StringFromList(1, strCurrentSpecies, "=") Make/O/N=(numpnts(Model_Time)) $(strFolder + ":Model_" + strFamilyName + "_Last")/WAVE=wSubSpecies wSubSpecies = 0 For(iDex = 0; iDex < ItemsInList(strSubMembers, ","); iDex += 1) String strSubSpecies = StringFromList(iDex, strSubMembers, ",") FindValue/TEXT=strSubSpecies/TXOP=4 twCnames wSubSpecies += Model_Conc[p][V_Value] EndFor wOtherBr += wSubSpecies EndIf EndFor //Repeat for the chlorinated VOCs Make/O/N=(numpnts(Model_Time)) $(strFolder + ":Model_Cly_OtherCl_Last")/WAVE=wOtherCl wOtherCl = 0 For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strFamilyMembers_Cl, ";"); iSpeciesDex += 1) strCurrentSpecies = StringFromList(iSpeciesDex, strFamilyMembers_Cl, ";") If(StringMatch(strCurrentSpecies, "*=*") == 0) // Make/O/N=(numpnts(Model_Time)) $(strFolder + ":Model_" + strCurrentSpecies + "_Last")/WAVE=wSpecies FindValue/TEXT=strCurrentSpecies/TXOP=4 twCnames // wSpecies = Model_Conc[p][V_Value] wOtherCl += Model_Conc[p][V_Value] Else strFamilyName = StringFromList(0, strCurrentSpecies, "=") strSubMembers = StringFromList(1, strCurrentSpecies, "=") Make/O/N=(numpnts(Model_Time)) $(strFolder + ":Model_" + strFamilyName + "_Last")/WAVE=wSubSpecies wSubSpecies = 0 For(iDex = 0; iDex < ItemsInList(strSubMembers, ","); iDex += 1) strSubSpecies = StringFromList(iDex, strSubMembers, ",") FindValue/TEXT=strSubSpecies/TXOP=4 twCnames wSubSpecies += Model_Conc[p][V_Value] EndFor wOtherCl += wSubSpecies EndIf EndFor EndFor End Function ProcessModelRO2Family_Last() SetDataFolder root: Variable vNumTopRO2 = 5 String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "BoxModel_Run*Last", ";") Variable vNumRuns = ItemsInList(strFolderList, ";") Variable iRunDex, iDex For(iRunDex = 1; iRunDex <= vNumRuns; iRunDex += 1) String strFolder = "root:BoxModelOut_Run" + num2str(iRunDex) + "_Last" Wave/T twCnames = $(strFolder + ":twCnames") // //Make RO2 species waves Wave iRO2 = $(strFolder + ":iRO2") Make/O/N=(numpnts(iRO2))/T $(strFolder + ":twRO2names")/WAVE=twRO2names twRO2names = twCnames[iRO2[p]] Wave wRO2 = $(strFolder + ":Model_RO2_Last") Wave Model_Conc = $(strFolder + ":Model_Conc_Last") Variable vNumTimePnts = DimSize(Model_Conc, 0) MatrixOp/O/FREE TempSum = SumCols(Model_Conc) Redimension/N=(numpnts(twCnames)) TempSum Make/O/N=(numpnts(iRO2))/FREE TempRO2, TempiRO2 TempiRO2 = iRO2[p] TempRO2 = TempSum[iRO2[p]] Sort/R TempRO2, TempRO2, TempiRO2 Make/O/N=(vNumTopRO2)/T $(strFolder + ":twTopRO2names_Last")/WAVE=twTopRO2names twTopRO2names = twCnames[TempiRO2[p]] Make/O/N=(vNumTimePnts) $(strFolder + ":Model_RO2_OtherRO2_Last")/WAVE=wRO2_OtherRO2 wRO2_OtherRO2 = wRO2 For(iDex = 0; iDex < numpnts(twTopRO2names); iDex += 1) Make/O/N=(vNumTimePnts) $(strFolder + ":Model_RO2_" + twTopRO2names[iDex] + "_Last")/WAVE=wRO2_Species FindValue/TEXT=twTopRO2names[iDex]/TXOP=2 twCnames Variable vSpeciesDex = V_Value wRO2_Species = Model_Conc[p][vSpeciesDex] wRO2_OtherRO2 -= wRO2_Species EndFor EndFor End Function ProcessModelVOCs_Last() SetDataFolder root: String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "BoxModel_Run*Last", ";") Variable vNumRuns = ItemsInList(strFolderList, ";") Variable iRunDex String strVOCclass = "Acid;Alcohol;Aldehyde;Alkene;AlkoxyRadical;Aromatic;Ketone;Peroxide;PeroxyAcid;PeroxyNitrate;PeroxyRadical" // String strHalVOCclass = "BrAcid;BrAlc;BrAld;;BrRO;;BrKet;BrROOH;BrPerAcid;BrPN;BrRO2" Variable iSpeciesDex, iDex For(iRunDex = 1; iRunDex <= vNumRuns; iRunDex += 1) String strFolderOut = "root:BoxModelOut_Run" + num2str(iRunDex) + "_Last" Wave/T twCnames = $(strFolderOut + ":twCnames") Wave Model_Conc = $(strFolderOut + ":Model_Conc_Last") For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strVOCclass); iSpeciesDex += 1) Wave/T twClassName = $(strFolderOut + ":tw" + StringFromList(iSpeciesDex, strVOCclass)) Make/O/N=(DimSize(Model_Conc,0)) $(strFolderOut + ":Model_VOC_" + StringFromList(iSpeciesDex, strVOCclass) + "_Last")/WAVE=wVOCclass wVOCclass = 0 For(iDex = 0; iDex < numpnts(twClassName); iDex += 1) FindValue/TEXT=twClassName[iDex]/TXOP=4 twCnames wVOCclass += Model_Conc[p][V_Value] EndFor Make/O/N=(DimSize(Model_Conc,0)) $(strFolderOut + ":Model_TotalVOC_" + StringFromList(iSpeciesDex, strVOCclass) + "_Last")/WAVE=wTotalVOCclass wTotalVOCclass = wVOCclass Wave wBrClass = $(strFolderOut + ":Model_Br" + StringFromList(iSpeciesDex, strVOCclass) + "_Last") If(WaveExists(wBrClass)) wTotalVOCclass += wBrClass EndIf Wave wClClass = $(strFolderOut + ":Model_Cl" + StringFromList(iSpeciesDex, strVOCclass) + "_Last") If(WaveExists(wClClass)) wTotalVOCclass += wBrClass EndIf EndFor //Then repeat for those species that have to be added up manually (i.e. no SMILES) String strAlkane = "C2H6;C3H8;NC4H10;IC4H10;NC5H12;IC5H12;NC6H14" Make/O/N=(DimSize(Model_Conc,0)) $(strFolderOut + ":Model_VOC_Alkane_Last")/WAVE=wVOCclass Make/O/N=(DimSize(Model_Conc,0)) $(strFolderOut + ":Model_TotalVOC_Alkane_Last")/WAVE=wTotalVOCclass wVOCclass = 0 For(iDex = 0; iDex < ItemsInList(strAlkane); iDex += 1) FindValue/TEXT=StringFromList(iDex,strAlkane)/TXOP=4 twCnames wVOCclass += Model_Conc[p][V_Value] EndFor wTotalVOCclass = wVOCclass String strAlkyne = "C2H2" Make/O/N=(DimSize(Model_Conc,0)) $(strFolderOut + ":Model_VOC_Alkyne_Last")/WAVE=wVOCclass Make/O/N=(DimSize(Model_Conc,0)) $(strFolderOut + ":Model_TotalVOC_Alkyne_Last")/WAVE=wTotalVOCclass wVOCclass = 0 For(iDex = 0; iDex < ItemsInList(strAlkyne); iDex += 1) FindValue/TEXT=StringFromList(iDex,strAlkyne)/TXOP=4 twCnames wVOCclass += Model_Conc[p][V_Value] EndFor wTotalVOCclass = wVOCclass EndFor End Function GetChangeInModelSpecies() //Figure out how much change there was in all the relevant species from the beginning to end of the model //Calculate this as both the change in concentration, and the fractional change SetDataFolder root: String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "BoxModel_Run*Last", ";") Variable vNumRuns = ItemsInList(strFolderList, ";") Variable iRunDex, iDex, iSpeciesDex String strSpecies = "O3;NO2;NO;HNO3pNO3;HONO;N2O5;" strSpecies += "Cl2;Br2;BrCl;HClpCl;HBr;HOCl;HOBr;ClNO2;BrNO2;" strSpecies += "CH4;TotalVOC_Alkane;TotalVOC_Alkene;TotalVOC_Alkyne;TotalVOC_Acid;TotalVOC_Aromatic;TotalVOC_Aldehyde;TotalVOC_Acid;" strSpecies += "TotalVOC_Alcohol;TotalVOC_AlkoxyRadical;TotalVOC_Ketone;TotalVOC_Peroxide;TotalVOC_PeroxyAcid;TotalVOC_PeroxyNitrate;TotalVOC_PeroxyRadical" For(iRunDex = 1; iRunDex <= vNumRuns; iRunDex += 1) String strFolder = "root:BoxModelOut_Run" + num2str(iRunDex) + "_Last" Wave twInitNames = $(strFolder + ":twInitNames") //Call up the wave of species that were included as initial concentrations Wave wModel_Init = $(strFolder + ":Model_InitConc") Make/O/N=(ItemsInList(strSpecies))/T $(strFolder + ":twModelSpecies")/WAVE=twModelSpecies Make/O/N=(ItemsInList(strSpecies)) $(strFolder + ":ChangeInConc")/WAVE=wModelSpecies_Conc Make/O/N=(ItemsInList(strSpecies)) $(strFolder + ":FracChangeInConc")/WAVE=wModelSpecies_FracConc Make/O/N=(ItemsInList(strSpecies)) $(strFolder + ":InitConc")/WAVE=wModelSpecies_InitConc twModelSpecies = StringFromList(p, strSpecies) For(iDex = 0; iDex < ItemsInList(strSpecies); iDex += 1) String strCurrentSpecies = StringFromList(iDex, strSpecies) FindValue/TEXT=strCurrentSpecies/TXOP=4 twInitNames Variable vDex = V_Value Wave wCurrentSpecies = $(strFolder + ":Model_" + strCurrentSpecies + "_Last") If(vDex != -1) //If it was one of the species that was initialized as non-zero wModelSpecies_InitConc[iDex] = wModel_Init[0][vDex] wModelSpecies_Conc[iDex] = (wCurrentSpecies[numpnts(wCurrentSpecies) - 1] - wModel_Init[0][vDex]) wModelSpecies_FracConc[iDex] = (wCurrentSpecies[numpnts(wCurrentSpecies) - 1]/wModel_Init[0][vDex]) - 1 Else If(StringMatch(strCurrentSpecies, "TotalVOC_Alkane") == 1) String strToAdd = "C2H6;C3H8;NC4H10;IC4H10;NC5H12;IC5H12;NC6H14" ElseIf(StringMatch(strCurrentSpecies, "TotalVOC_Alkyne") == 1) strToAdd = "C2H2" ElseIf(StringMatch(strCurrentSpecies, "TotalVOC_Alkene") == 1) strToAdd = "C2H4;C3H6" ElseIf(StringMatch(strCurrentSpecies, "TotalVOC_Aromatic") == 1) strToAdd = "BENZENE;TOLUENE;OXYL;TM124B" ElseIf(StringMatch(strCurrentSpecies, "TotalVOC_Acid") == 1) strToAdd = "HCOOH;CH3CO2H" ElseIf(StringMatch(strCurrentSpecies, "TotalVOC_Aldehyde") == 1) strToAdd = "HCHO;CH3CHO;C2H5CHO;C3H7CHO;MACR" ElseIf(StringMatch(strCurrentSpecies, "TotalVOC_Ketone") == 1) strToAdd = "CH3COCH3;MEK;DIEK;HEX3ONE" ElseIf(StringMatch(strCurrentSpecies, "TotalVOC_Alcohol") == 1) strToAdd = "CH3OH;C2H5OH" Else strToAdd = "" EndIf Variable vSum = 0 For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strToAdd); iSpeciesDex += 1) FindValue/TEXT=StringFromList(iSpeciesDex, strToAdd)/TXOP=4 twInitNames vSum += wModel_Init[0][V_Value] EndFor wModelSpecies_InitConc[iDex] = vSum wModelSpecies_Conc[iDex] = wCurrentSpecies[numpnts(wCurrentSpecies) - 1] - vSum //Calculate the end of the model - the initial concentration If(vSum == 0) wModelSpecies_FracConc[iDex] = NaN Else wModelSpecies_FracConc[iDex] = wModelSpecies_Conc[iDex] / vSum EndIf EndIf EndFor EndFor End Function PlotFamilyConc_Last() //Plot each of the families in a stacked plot SetDataFolder root: String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "BoxModel_Run*Last", ";") Variable vNumRuns = ItemsInList(strFolderList, ";") String strFamilies = "Bry=Br2,BrCl,BrNO2,HOBr,Br,HBr,Bry_OtherBr" strFamilies += ";Cly=Cl2,BrCl,ClNO2,HOCl,Cl,HClpCl,Cly_OtherCl" strFamilies += ";NOy=NO2,NO,HNO3pNO3,N2O5,BrNO2,ClNO2,HONO" strFamilies += ";Ox=O3,NO2" strFamilies += ";NOx=NO2,NO" strFamilies += ";Oxtotal=O3,NO2,N2O5,HNO3pNO3,ClNO2" strFamilies += ";HOx=OH,HO2" strFamilies += ";Iy=I2,ICl,IBr,HI,HOI,I,INO,INO2,INO3,I2O2,I2O3,I2O4" strFamilies += ";RO2=RO2_OtherRO2" Variable iRunDex For(iRunDex = 1; iRunDex <= vNumRuns; iRunDex += 1) String strFolder = "root:BoxModel_Run" + num2str(iRunDex) + "_Last" String strFolderOut = "root:BoxModelOut_Run" + num2str(iRunDex) + "_Last" Wave/T twTopRO2names = $(strFolderOut + ":twTopRO2names_Last") String strSpeciesInRO2 = "" wfprintf strSpeciesInRO2, ",RO2_%s", twTopRO2names strFamilies = ReplaceString(";RO2=RO2_OtherRO2", strFamilies, ";RO2=RO2_OtherRO2" + strSpeciesInRO2) ColorTab2Wave Rainbow Wave M_colors Variable iSpeciesDex, vColor, iFamilyDex String strFamilyName, strSpeciesInFamily, strPlotName, strLegend, strCurrentSpecies Wave wObs_Age = $(strFolder + ":Obs_Age_min") Wave Model_Time = $(strFolderOut + ":Model_time_Last_min") Wave wZeroLine = $(strFolderOut + ":ZeroLine") For(iFamilyDex = 0; iFamilyDex < ItemsInList(strFamilies, ";"); iFamilyDex += 1) strFamilyName = StringFromList(0, StringFromList(iFamilyDex, strFamilies, ";"), "=") strSpeciesInFamily = StringFromList(1, StringFromList(iFamilyDex, strFamilies, ";"), "=") strPlotName = strFamilyName + "_family_last" Wave wFamilyWave = $(strFolderOut + ":Model_" + strFamilyName + "_Last") WaveStats/Q wFamilyWave If(V_avg > 0) DoWindow/K/Z $strPlotName Display as strPlotName DoWindow/C $strPlotName AppendToGraph wFamilyWave/TN=$strFamilyName vs Model_Time ModifyGraph rgb($strFamilyName) = (0,0,0), lsize($strFamilyName) = 3 strLegend = "\Z08\s(" + strFamilyName + ") " + strFamilyName + "\r" For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpeciesInFamily, ","); iSpeciesDex += 1) strCurrentSpecies = StringFromList(iSpeciesDex, strSpeciesInFamily, ",") vColor = iSpeciesDex / ItemsInList(strSpeciesInFamily, ",") * 99 Wave wSpecies = $(strFolderOut + ":Model_" + strCurrentSpecies + "_Last") AppendToGraph wSpecies/TN=$strCurrentSpecies vs Model_Time ModifyGraph hbFill($strCurrentSpecies)=5,toMode($strCurrentSpecies)=2,rgb($strCurrentSpecies)=(M_colors[vColor][0], M_colors[vColor][1], M_Colors[vColor][2]) ModifyGraph mode($strCurrentSpecies)=7 strLegend += "\s(" + strCurrentSpecies + ") " + strCurrentSpecies + "\r" EndFor strLegend = RemoveEnding(strLegend, "\r") Legend/A=LT strLegend Label bottom "Model time (min)" Label left strFamilyName + " family (ppbv)" EndIF EndFor KillWaves M_Colors EndFor End Function PlotModelChanges() SetDataFolder root: ColorTab2Wave Rainbow Wave M_Colors //Get the total number of runs String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "BoxModel_Run*Last", ";") Variable vNumRuns = ItemsInList(strFolderList, ";") Variable iSpeciesDex, iRunDex, vColor String strSpecies, strPlotName, strLegend, strCurrentSpecies, strBkgd For(iRunDex = 1; iRunDex <= vNumRuns; iRunDex += 1) String strFolder = "root:BoxModelOut_Run" + num2str(iRunDex) + "_Last" Wave wTime = $(strFolder + ":Model_time_Last_min") Make/O/N=2 $(strFolder + ":Endpoint")/WAVE=Endpoint Endpoint = WaveMax(wTime)*1.1 Wave/T twModelSpecies = $(strFolder + ":twModelSpecies") Wave wInitConc = $(strFolder + ":InitConc") //First, compare the hydrocarbons strSpecies = "Alkane;Alkene;Alkyne;Aromatic;Alcohol" strPlotName = "Model_HCs_Run" + num2str(iRunDex) DoWindow/K/Z $strPlotName Display as strPlotName DoWindow/C $strPlotName strLegend = "" For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpecies); iSpeciesDex += 1) strCurrentSpecies = StringFromList(iSpeciesDex, strSpecies) vColor = iSpeciesDex / (ItemsInList(strSpecies)-1) * 99 Wave wConc = $(strFolder + ":Model_TotalVOC_" + strCurrentSpecies + "_Last") AppendToGraph wConc/TN=$(strCurrentSpecies) vs wTime ModifyGraph rgb($strCurrentSpecies) = (M_Colors[vColor][0], M_Colors[vColor][1], M_Colors[vColor][2]) strLegend += "\s(" + strCurrentSpecies + ") " + strCurrentSpecies + "\n" FindValue/TEXT=("TotalVOC_" + strCurrentSpecies)/TXOP=4 twModelSpecies Variable vDex = V_Value strBkgd = "Bkgd_" + strCurrentSpecies AppendToGraph wInitConc[vDex,vDex]/TN=$strBkgd vs Endpoint[0,0] ModifyGraph mode($strBkgd) = 3, marker($strBkgd) = 9, mrkThick($strBkgd) = 2 ModifyGraph rgb($strBkgd) = (M_Colors[vColor][0], M_Colors[vColor][1], M_Colors[vColor][2]) EndFor strLegend = RemoveEnding(strLegend, "\n") Legend/A=MT strLegend Label bottom "Model time (min)" Label left "Mixing ratio (ppbv)" ModifyGraph mirror=2,standoff=0 //Second, compare the OVOCs strSpecies = "Acid;Aldehyde;Ketone;Peroxide;PeroxyAcid;PeroxyNitrate;PeroxyRadical" strPlotName = "Model_OVOCs_Run" + num2str(iRunDex) DoWindow/K/Z $strPlotName Display as strPlotName DoWindow/C $strPlotName strLegend = "" For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpecies); iSpeciesDex += 1) strCurrentSpecies = StringFromList(iSpeciesDex, strSpecies) vColor = iSpeciesDex / (ItemsInList(strSpecies)-1) * 99 Wave wConc = $(strFolder + ":Model_TotalVOC_" + strCurrentSpecies + "_Last") AppendToGraph wConc/TN=$(strCurrentSpecies) vs wTime ModifyGraph rgb($strCurrentSpecies) = (M_Colors[vColor][0], M_Colors[vColor][1], M_Colors[vColor][2]) strLegend += "\s(" + strCurrentSpecies + ") " + strCurrentSpecies + "\n" FindValue/TEXT=("TotalVOC_" + strCurrentSpecies)/TXOP=4 twModelSpecies vDex = V_Value strBkgd = "Bkgd_" + strCurrentSpecies AppendToGraph wInitConc[vDex,vDex]/TN=$strBkgd vs Endpoint[0,0] ModifyGraph mode($strBkgd) = 3, marker($strBkgd) = 9, mrkThick($strBkgd) = 2 ModifyGraph rgb($strBkgd) = (M_Colors[vColor][0], M_Colors[vColor][1], M_Colors[vColor][2]) EndFor strLegend = RemoveEnding(strLegend, "\n") Legend/A=MT strLegend Label bottom "Model time (min)" Label left "Mixing ratio (ppbv)" ModifyGraph mirror=2,standoff=0 //Third, compare others strSpecies = "HNO3pNO3;N2O5;HONO;NO;NO2"//;HO2" strPlotName = "Model_NOy_Run" + num2str(iRunDex) DoWindow/K/Z $strPlotName Display as strPlotName DoWindow/C $strPlotName strLegend = "" For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpecies); iSpeciesDex += 1) strCurrentSpecies = StringFromList(iSpeciesDex, strSpecies) vColor = iSpeciesDex / (ItemsInList(strSpecies)-1) * 99 Wave wConc = $(strFolder + ":Model_" + strCurrentSpecies + "_Last") AppendToGraph wConc/TN=$(strCurrentSpecies) vs wTime ModifyGraph rgb($strCurrentSpecies) = (M_Colors[vColor][0], M_Colors[vColor][1], M_Colors[vColor][2]) strLegend += "\s(" + strCurrentSpecies + ") " + strCurrentSpecies + "\n" FindValue/TEXT=(strCurrentSpecies)/TXOP=4 twModelSpecies vDex = V_Value strBkgd = "Bkgd_" + strCurrentSpecies AppendToGraph wInitConc[vDex,vDex]/TN=$strBkgd vs Endpoint[0,0] ModifyGraph mode($strBkgd) = 3, marker($strBkgd) = 9, mrkThick($strBkgd) = 2 ModifyGraph rgb($strBkgd) = (M_Colors[vColor][0], M_Colors[vColor][1], M_Colors[vColor][2]) EndFor strLegend = RemoveEnding(strLegend, "\n") Legend/A=MT strLegend Label bottom "Model time (min)" Label left "Mixing ratio (ppbv)" ModifyGraph mirror=2,standoff=0 EndFor KillWaves/Z M_Colors End //Process the rates for the puff model Function ProcessModelRates_Puff(strCurrentSpecies) //Process the F0AM-derived rates for a given species //Based on the code written for F0AM4.2 by Glenn Wolfe String strCurrentSpecies SetDataFolder root: //If string is empty, prompt the user for the species name If(strlen(strCurrentSpecies) == 0) Prompt strCurrentSpecies, "Species:" DoPrompt "Enter species to process", strCurrentSpecies If(V_flag == 1 || strlen(strCurrentSpecies) == 0) Abort EndIf EndIf Variable vNumRates = 5 //This is the number of top rates to gather //Get the total number of runs String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "BoxModel_Run*Puff", ";") Variable vNumRuns = ItemsInList(strFolderList, ";") Variable iRunDex, iPlumeDex, iDex For(iRunDex = 1; iRunDex <= vNumRuns; iRunDex += 1) String strFolderIn = "root:BoxModel_Run" + num2str(iRunDex) + "_Puff" String strFolderOut = "root:BoxModelOut_Run" + num2str(iRunDex) + "_Puff" Wave Int_Time = $(strFolderIn + ":Int_Time") Wave/T twCnames = $(strFolderOut + ":twCnames") Wave/T twRnames = $(strFolderOut + ":twRnames") Variable vNumSpeciesTotal = numpnts(twCnames) Variable vNumReactionsTotal = numpnts(twRnames) Wave Stoic = $(strFolderOut + ":Stoic") //Adjustment for HCl If(StringMatch(strCurrentSpecies, "HClpCl") == 1) String strF0AMname = "HCL" ElseIf(StringMatch(strCurrentSpecies, "HNO3pNO3") == 1) strF0AMname = "HNO3" Else strF0AMname = strCurrentSpecies EndIf FindValue/TEXT=strF0AMname/TXOP=2 twCnames Variable vSpeciesDex = V_Value NewDataFolder/O/S $(strFolderOut + ":" + strCurrentSpecies) Make/O/N=(numpnts(Int_Time)) $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_Dil")/WAVE = wSpecies_Dil //Do some prep work. //First, retrieve the list of reactions that involve this species (i.e. those which have non-zero stoichiometric coefficients) Make/O/N=(vNumReactionsTotal)/FREE TempStoic, TempIndex, wSpecies_Stoic, wSpecies_Index TempStoic = Stoic[p][vSpeciesDex] TempIndex = p Extract/O TempStoic, wSpecies_Stoic, TempStoic != 0 Extract/O TempIndex, wSpecies_Index, TempStoic != 0 Variable vNumReactionsSpecies = numpnts(wSpecies_Stoic) //The number of reactions that involve the current species //Then, get a list of the equilibrium reactions, so they can later be summed up. Make/O/N=(vNumReactionsSpecies, vNumSpeciesTotal)/FREE Stoic_All //Get the stoichiometric coefficients for every species in the sublist of relevant reactions Stoic_All = Stoic[wSpecies_Index[p]][q] Make/O/N=0/FREE EqDexDir1, EqDexDir2 For(iDex = 0; iDex < vNumReactionsSpecies; iDex += 1) //Then cycle through every combination of reactions, looking for ones whose stoichiometric coefficients cancel out MatrixOp/O/FREE TempShiftMat = RotateRows(Stoic_All, -iDex) MatrixOp/O/FREE TempSumMat = Stoic_All + TempShiftMat MatrixOp/O/FREE TempSumSum = SumRows(abs(TempSumMat)) Redimension/N=(vNumReactionsSpecies - iDex) TempSumSum Extract/O/FREE/INDX TempSumSum, TempIndex, TempSumSum == 0 //Look for net zero reactions If(numpnts(TempIndex) > 0) //If there are any, then get rid of any duplicates, and then add it to the master list of eq. reactions Variable i = 0 Do Variable vDex = TempIndex[i] FindValue/V=(vDex)/T=0.1 EqDexDir1 If(V_Value != -1) DeletePoints i, 1, TempIndex i -= 1 EndIf i += 1 While(i < numpnts(TempIndex)) Concatenate/NP {TempIndex}, EqDexDir1 //EqDexDir1 is the index of one direction TempIndex += iDex Concatenate/NP {TempIndex}, EqDexDir2 //EqDexDir2 is the index of the corresponding opposite direction EndIf EndFor For(iPlumeDex = 0; iPlumeDex < numpnts(Int_Time); iPlumeDex += 1) Wave Model_Rate = $(strFolderOut + ":Model_Rate_" + num2str(iPlumeDex)) Variable vNumTimeSteps = DimSize(Model_Rate, 0) Variable vIntTime = Int_Time[iPlumeDex] vNumReactionsSpecies = numpnts(wSpecies_Stoic) //Grab the relevant rates for this species Make/O/N=(vNumTimeSteps, vNumReactionsSpecies) $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_Rates_" + num2str(iPlumeDex))/WAVE=wSpecies_Rates Make/O/N=(vNumReactionsSpecies)/T $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_Rnames_" + num2str(iPlumeDex))/WAVE=twSpecies_Rnames wSpecies_Rates = Model_Rate[p][wSpecies_Index[q]] * wSpecies_Stoic[q] twSpecies_Rnames = twRnames[wSpecies_Index[p]] //If there are equilibrium reactions to sum up, figure out what the net reaction is //then substitute the net forward and net reverse reactions in For(iDex = 0; iDex < numpnts(EqDexDir1); iDex += 1) Variable vDexDir1 = EqDexDir1[iDex] Variable vDexDir2 = EqDexDir2[iDex] Make/O/N=(DimSize(wSpecies_Rates, 0))/FREE TempRateDir1, TempRateDir2, TempNetFor, TempNetBack TempRateDir1 = wSpecies_Rates[p][vDexDir1] TempRateDir2 = wSpecies_Rates[p][vDexDir2] Variable vSumDir1 = Sum(TempRateDir1) Variable vSumDir2 = Sum(TempRateDir2) TempNetFor = (TempRateDir1[p] + TempRateDir2[p]) < 0 ? 0 : (TempRateDir1[p] + TempRateDir2[p]) TempNetBack = (TempRateDir1[p] + TempRateDir2[p]) > 0 ? 0 : (TempRateDir1[p] + TempRateDir2[p]) If(vSumDir1 > vSumDir2) wSpecies_Rates[][vDexDir1] = TempNetFor[p] wSpecies_Rates[][vDexDir2] = TempNetBack[p] Else wSpecies_Rates[][vDexDir1] = TempNetBack[p] wSpecies_Rates[][vDexDir2] = TempNetFor[p] EndIf Endfor //Sort in order of increasing total reactivity MatrixOp/O/FREE wSpecies_IntRates = SumCols(wSpecies_Rates) Redimension/N=(vNumReactionsSpecies) wSpecies_IntRates Make/O/N=(DimSize(wSpecies_Rates, 1), DimSize(wSpecies_Rates, 0))/FREE TempMat //Get the transpose so I can use SortColumns TempMat = wSpecies_Rates[q][p] SortColumns keyWaves = wSpecies_IntRates, sortWaves = TempMat wSpecies_Rates = TempMat[q][p] Sort wSpecies_IntRates, wSpecies_IntRates, twSpecies_Rnames Make/O/N=(vNumReactionsSpecies)/T/FREE twSpecies_reacts twSpecies_reacts = StringFromList(0, twSpecies_Rnames[p], " =") //Get rid of duplicates If(numpnts(twSpecies_reacts) > 1) FindDuplicates/FREE/INDX=DupDex twSpecies_reacts If(numpnts(DupDex) > 0) Do String strCurrentReaction = twSpecies_reacts[DupDex[0]] FindValue/TEXT=strCurrentReaction/TXOP=4 twSpecies_reacts Variable vOrig = V_value wSpecies_Rates[][vOrig] = wSpecies_Rates[p][vOrig] + wSpecies_Rates[p][DupDex[0]] DeletePoints/M=0 DupDex[0], 1, wSpecies_IntRates, twSpecies_Rnames, twSpecies_reacts DeletePoints/M=1 DupDex[0], 1, wSpecies_Rates DeletePoints/M=0 0, 1, DupDex DupDex -= 1 While(numpnts(DupDex) > 0) vNumReactionsSpecies = numpnts(twSpecies_Rnames) //Sort again MatrixOp/O/FREE wSpecies_IntRates = SumCols(wSpecies_Rates) Redimension/N=(vNumReactionsSpecies) wSpecies_IntRates Make/O/N=(DimSize(wSpecies_Rates, 1), DimSize(wSpecies_Rates, 0))/FREE TempMat TempMat = wSpecies_Rates[q][p] SortColumns keyWaves = wSpecies_IntRates, sortWaves = TempMat wSpecies_Rates = TempMat[q][p] Sort wSpecies_IntRates, wSpecies_IntRates, twSpecies_Rnames Make/O/N=(vNumReactionsSpecies)/T/FREE twSpecies_reacts twSpecies_reacts = StringFromList(0, twSpecies_Rnames[p], " =") EndIf EndIf //Get major loss reactions Make/O/N=(vNumReactionsSpecies)/FREE TempDex = p Extract/O TempDex, TempDex, wSpecies_IntRates < 0 Variable vNumLossReacts = numpnts(TempDex) Make/O/N=(vNumLossReacts)/T/FREE twSpecies_LossReactNames Make/O/N=(vNumTimeSteps, vNumLossReacts)/FREE wSpecies_LossRates Variable vNumTopLoss = min(vNumRates, vNumLossReacts) Variable vSumOtherLoss = vNumLossReacts <= vNumRates ? 0 : 1 Make/O/N=(vNumTimeSteps, vNumTopLoss + vSumOtherLoss) $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopLossRates_" + num2str(iPlumeDex))/WAVE=wSpecies_TopLossRates Make/O/N=(vNumTopLoss + vSumOtherLoss)/T $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopLossReactNames_" + num2str(iPlumeDex))/WAVE=twSpecies_TopLossReactNames If(vNumLossReacts > 0) twSpecies_LossReactNames = twSpecies_reacts[TempDex[p]] // wSpecies_LossRates = wSpecies_Rates[p][TempDex[q]] / vIntTime * 3600 wSpecies_LossRates = wSpecies_Rates[p][TempDex[q]] * 3600 wSpecies_TopLossRates = wSpecies_LossRates[p][q] twSpecies_TopLossReactNames = twSpecies_LossReactNames If(vSumOtherLoss == 1) Make/O/N=(vNumTimeSteps, vNumLossReacts - vNumTopLoss)/FREE TempMat TempMat = wSpecies_LossRates[p][q + vNumTopLoss] MatrixOp/O/FREE TempSumOther = Sumcols(TempMat^t) wSpecies_TopLossRates[][vNumTopLoss] = TempSumOther[0][p] twSpecies_TopLossReactNames[vNumTopLoss] = "Other" EndIf EndIf //Get major prodution reactions Make/O/N=(vNumReactionsSpecies)/FREE TempDex = p Extract/O TempDex, TempDex, wSpecies_IntRates > 0 Variable vNumProdReacts = numpnts(TempDex) Make/O/N=(vNumProdReacts)/T/FREE twSpecies_ProdReactNames Make/O/N=(vNumTimeSteps, vNumProdReacts)/FREE wSpecies_ProdRates Variable vNumTopProd = min(vNumRates, vNumProdReacts) Variable vSumOtherProd = vNumProdReacts <= vNumRates ? 0 : 1 Make/O/N=(vNumTimeSteps, vNumTopProd + vSumOtherProd) $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopProdRates_" + num2str(iPlumeDex))/WAVE=wSpecies_TopProdRates Make/O/N=(vNumTopProd + vSumOtherProd)/T $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopProdReactNames_" + num2str(iPlumeDex))/WAVE=twSpecies_TopProdReactNames If(vNumProdReacts > 0) twSpecies_ProdReactNames = twSpecies_reacts[TempDex[p]] // wSpecies_ProdRates = wSpecies_Rates[p][TempDex[q]] / vIntTime * 3600 wSpecies_ProdRates = wSpecies_Rates[p][TempDex[q]] * 3600 wSpecies_TopProdRates = wSpecies_ProdRates[p][DimSize(wSpecies_ProdRates,1) - 1 - q] twSpecies_TopProdReactNames = twSpecies_ProdReactNames[numpnts(twSpecies_ProdReactNames) - 1 - p] If(vSumOtherProd == 1) Make/O/N=(vNumTimeSteps, vNumProdReacts - vNumTopProd)/FREE TempMat TempMat = wSpecies_ProdRates[p][q] MatrixOp/O/FREE TempSumOther = Sumcols(TempMat^t) wSpecies_TopProdRates[][vNumTopProd] = TempSumOther[0][p] twSpecies_TopProdReactNames[vNumTopProd] = "Other" EndIf EndIf Wave wDil = $(strFolderOut + ":Model_Dil_" + num2str(iPlumeDex)) Make/O/N=(vNumTimeSteps)/FREE TempDil TempDil = wDil[p][vSpeciesDex] wSpecies_Dil[iPlumeDex] = sum(TempDil) EndFor EndFor CollateTopProdLoss(strCurrentSpecies) // PlotTopRates(strCurrentSpecies) End Function ProcessModelFamilyRates(strFamilyName) //Processes the net rates for a family of species String strFamilyName SetDataFolder root: If(strlen(strFamilyName) == 0) Prompt strFamilyName, "Family:" DoPrompt "Enter family to process", strFamilyName If(V_flag == 1 || strlen(strFamilyName) == 0) Abort EndIf EndIf String strSpeciesInFamily, strMultiplier If(StringMatch(strFamilyName, "Ox") == 1) strSpeciesInFamily = "O3;NO2" strMultiplier = "1;1" ElseIf(StringMatch(strFamilyName, "NOx") == 1) strSpeciesInFamily = "NO2;NO" strMultiplier = "1;1" ElseIf(StringMatch(strFamilyName, "HOx") == 1) strSpeciesInFamily = "OH;HO2" strMultiplier = "1;1" // ElseIf(StringMatch(strFamilyName, "Bry") == 1) //Note, Bry and Cly are not quite right, but they are conserved in the model, so I don't... // strSpeciesInFamily = "Br;Br2;BrCl;HBr;HOBr;BrO;BrNO2;Other_BR" //...think I will ever need to process the total family rates // strMultiplier = "1;2;1;1;1;1;1;1" // ElseIf(StringMatch(strFamilyName, "Cly") == 1) // strSpeciesInFamily = "Cl;Cl2;BrCl;HCl;HOCl;ClO;ClNO2;Other_CL" // strMultiplier = "1;2;1;1;1;1;1;1" ElseIf(StringMatch(strFamilyName, "RO2") == 1) Wave/T twRO2names = $("root:BoxModelOut_Run1_Puff:twRO2names") Make/O/FREE/N=(numpnts(twRO2names))/T Multiplier Multiplier = "1" strSpeciesInFamily = "" wfprintf strSpeciesInFamily, "%s;", twRO2names strMultiplier = "" wfprintf strMultiplier, "%s;", Multiplier Else Print "Family name not found!" Abort EndIf String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "BoxModel_Run*Puff", ";") Variable vNumRuns = ItemsInList(strFolderList, ";") Variable iRunDex, iPlumeDex, iDex, iSpeciesDex Variable vNumRates = 5 For(iRunDex = 1; iRunDex <= vNumRuns; iRunDex += 1) String strFolderIn = "root:BoxModel_Run" + num2str(iRunDex) + "_Puff" String strFolderOut = "root:BoxModelOut_Run" + num2str(iRunDex) + "_Puff" Wave Int_Time = $(strFolderIn + ":Int_Time") Wave/T twCnames = $(strFolderOut + ":twCnames") Wave/T twRnames = $(strFolderOut + ":twRnames") Wave Stoic = $(strFolderOut + ":Stoic") Variable vNumSpeciesTotal = numpnts(twCnames) Variable vNumReactionsTotal = numpnts(twRnames) NewDataFolder/O/S $(strFolderOut + ":" + strFamilyName) Make/O/N=(numpnts(Int_Time)) $(strFolderOut + ":" + strFamilyName + ":" + strFamilyName + "_Dil")/WAVE = wFamily_Dil wFamily_Dil = 0 For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpeciesInFamily); iSpeciesDex += 1) If(StringMatch(StringFromList(iSpeciesDex, strSpeciesInFamily), "Other_*") == 1) String strType = StringFromList(1, StringFromList(iSpeciesDex, strSpeciesInFamily), "_") strSpeciesInFamily = RemoveFromList("Other_" + strType, strSpeciesInFamily) strMultiplier = RemoveListItem(iSpeciesDex, strMultiplier) For(iDex = 0; iDex < numpnts(twCnames); iDex += 1) If(StringMatch(twCnames[iDex], "*" + strType + "*") == 1 && WhichListItem(twCnames[iDex], strSpeciesInFamily, ";", 0, 0) == -1) strSpeciesInFamily += twCnames[iDex] + ";" String strCname = twCnames[iDex] Variable vPos = strsearch(strCname, strType, 0) strCname = strCname[vPos + strlen(strType), vPos + strlen(strType)] Variable isNum = GrepString(strCname, "[0-9]") strMultiplier += SelectString(isNum, "1", strCname) + ";" EndIf EndFor EndIf EndFor Make/O/N=(vNumReactionsTotal)/FREE TempStoic, TempIndex, wSpecies_Index Make/O/N=0/FREE wFamily_Index, wFamily_Multiplier Make/O/N=(ItemsInList(strSpeciesInFamily))/FREE wSpeciesName_Index, wSpecies_Multiplier For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpeciesInFamily); iSpeciesDex += 1) String strCurrentSpecies = StringFromList(iSpeciesDex, strSpeciesInFamily) FindValue/TEXT=strCurrentSpecies/TXOP=2 twCnames Variable vSpeciesDex = V_Value wSpeciesName_Index[iSpeciesDex] = vSpeciesDex wSpecies_Multiplier[iSpeciesDex] = str2num(StringFromList(iSpeciesDex, strMultiplier)) //Retrieve the reactions that involve this species (i.e. non-zero stoichiometric coefficients) TempStoic = Stoic[p][vSpeciesDex] //* str2num(StringFromList(iSpeciesDex, strMultiplier)) TempIndex = p Extract/O TempIndex, wSpecies_Index, TempStoic != 0 Concatenate/NP {wSpecies_Index}, wFamily_Index EndFor Variable vNumReactionsSpecies = numpnts(wFamily_Index) //The number of reactions that involve the current species Make/O/N=(vNumReactionsSpecies, ItemsInList(strSpeciesInFamily))/FREE Stoic_All Stoic_All = Stoic[wFamily_Index[p]][wSpeciesName_Index[q]] * wSpecies_Multiplier[q] MatrixOp/O/FREE wFamily_Stoic = SumRows(Stoic_All) Redimension/N=(vNumReactionsSpecies) wFamily_Stoic Extract/O wFamily_Index, wFamily_Index, wFamily_Stoic != 0 Extract/O wFamily_Stoic, wFamily_Stoic, wFamily_Stoic != 0 //Sum up equilibrium reactions //First grab the indices of any reactions that are in equilibrium with another reaction vNumReactionsSpecies = numpnts(wFamily_Index) Make/O/N=(vNumReactionsSpecies, vNumSpeciesTotal)/FREE Stoic_All Stoic_All = Stoic[wFamily_Index[p]][q] Make/O/N=0/FREE EqDexDir1, EqDexDir2 For(iDex = 0; iDex < vNumReactionsSpecies; iDex += 1) MatrixOp/O/FREE TempShiftMat = RotateRows(Stoic_All, -iDex) MatrixOp/O/FREE TempSumMat = Stoic_All + TempShiftMat MatrixOp/O/FREE TempSumSum = SumRows(abs(TempSumMat)) Redimension/N=(vNumReactionsSpecies - iDex) TempSumSum Extract/O/FREE/INDX TempSumSum, TempIndex, TempSumSum == 0 If(numpnts(TempIndex) > 0) Variable i = 0 Do Variable vDex = TempIndex[i] FindValue/V=(vDex)/T=0.1 EqDexDir1 If(V_Value != -1) DeletePoints i, 1, TempIndex i -= 1 EndIf i += 1 While(i < numpnts(TempIndex)) Concatenate/NP {TempIndex}, EqDexDir1 TempIndex += iDex Concatenate/NP {TempIndex}, EqDexDir2 EndIf EndFor For(iPlumeDex = 0; iPlumeDex < numpnts(Int_Time); iPlumeDex += 1) Wave Model_Rate = $(strFolderOut + ":Model_Rate_" + num2str(iPlumeDex)) Variable vNumTimeSteps = DimSize(Model_Rate, 0) vNumReactionsSpecies = numpnts(wFamily_Index) Make/O/N=(vNumTimeSteps, vNumReactionsSpecies) $(strFolderOut + ":" + strFamilyName + ":" + strFamilyName + "_Rates_" + num2str(iPlumeDex))/WAVE=wSpecies_Rates Make/O/N=(vNumReactionsSpecies)/T $(strFolderOut + ":" + strFamilyName + ":" + strFamilyName + "_Rnames_" + num2str(iPlumeDex))/WAVE=twSpecies_Rnames wSpecies_Rates = Model_Rate[p][wFamily_Index[q]] * wFamily_Stoic[q] twSpecies_Rnames = twRnames[wFamily_Index[p]] //Then figure out what the net reaction is and substitute the net forward and net reverse reactions in For(iDex = 0; iDex < numpnts(EqDexDir1); iDex += 1) Variable vDexDir1 = EqDexDir1[iDex] Variable vDexDir2 = EqDexDir2[iDex] Make/O/N=(DimSize(wSpecies_Rates, 0))/FREE TempRateDir1, TempRateDir2, TempNetFor, TempNetBack TempRateDir1 = wSpecies_Rates[p][vDexDir1] TempRateDir2 = wSpecies_Rates[p][vDexDir2] Variable vSumDir1 = Sum(TempRateDir1) Variable vSumDir2 = Sum(TempRateDir2) TempNetFor = (TempRateDir1[p] + TempRateDir2[p]) < 0 ? 0 : (TempRateDir1[p] + TempRateDir2[p]) TempNetBack = (TempRateDir1[p] + TempRateDir2[p]) > 0 ? 0 : (TempRateDir1[p] + TempRateDir2[p]) If(vSumDir1 > vSumDir2) wSpecies_Rates[][vDexDir1] = TempNetFor[p] wSpecies_Rates[][vDexDir2] = TempNetBack[p] Else wSpecies_Rates[][vDexDir1] = TempNetBack[p] wSpecies_Rates[][vDexDir2] = TempNetFor[p] EndIf Endfor //Sort in order of increasing total reactivity MatrixOp/O/FREE wSpecies_IntRates = SumCols(wSpecies_Rates) Redimension/N=(vNumReactionsSpecies) wSpecies_IntRates Make/O/N=(DimSize(wSpecies_Rates, 1), DimSize(wSpecies_Rates, 0))/FREE TempMat //Get the transpose so I can use SortColumns TempMat = wSpecies_Rates[q][p] SortColumns keyWaves = wSpecies_IntRates, sortWaves = TempMat wSpecies_Rates = TempMat[q][p] Sort wSpecies_IntRates, wSpecies_IntRates, twSpecies_Rnames Make/O/N=(vNumReactionsSpecies)/T/FREE twSpecies_reacts twSpecies_reacts = StringFromList(0, twSpecies_Rnames[p], " =") //Get rid of duplicates If(numpnts(twSpecies_reacts) > 1) FindDuplicates/FREE/INDX=DupDex twSpecies_reacts If(numpnts(DupDex) > 0) Do String strCurrentReaction = twSpecies_reacts[DupDex[0]] FindValue/TEXT=strCurrentReaction twSpecies_reacts Variable vOrig = V_value wSpecies_Rates[][vOrig] = wSpecies_Rates[p][vOrig] + wSpecies_Rates[p][DupDex[0]] // DeletePoints/M=0 DupDex[0], 1, wSpecies_IntRates, twSpecies_Rnames, wSpecies_Stoic, wSpecies_Index, twSpecies_reacts DeletePoints/M=0 DupDex[0], 1, wSpecies_IntRates, twSpecies_Rnames, twSpecies_reacts DeletePoints/M=1 DupDex[0], 1, wSpecies_Rates DeletePoints/M=0 0, 1, DupDex DupDex -= 1 While(numpnts(DupDex) > 0) vNumReactionsSpecies = numpnts(twSpecies_Rnames) //Sort again MatrixOp/O/FREE wSpecies_IntRates = SumCols(wSpecies_Rates) Redimension/N=(vNumReactionsSpecies) wSpecies_IntRates Make/O/N=(DimSize(wSpecies_Rates, 1), DimSize(wSpecies_Rates, 0))/FREE TempMat TempMat = wSpecies_Rates[q][p] SortColumns keyWaves = wSpecies_IntRates, sortWaves = TempMat wSpecies_Rates = TempMat[q][p] Sort wSpecies_IntRates, wSpecies_IntRates, twSpecies_Rnames EndIf EndIf //Get major loss reactions Make/O/N=(vNumReactionsSpecies)/FREE TempDex = p Extract/O TempDex, TempDex, wSpecies_IntRates < 0 Variable vNumLossReacts = numpnts(TempDex) Make/O/N=(vNumLossReacts)/T/FREE twSpecies_LossReactNames Make/O/N=(vNumTimeSteps, vNumLossReacts)/FREE wSpecies_LossRates Variable vNumTopLoss = min(vNumRates, vNumLossReacts) Variable vSumOtherLoss = vNumLossReacts <= vNumRates ? 0 : 1 Make/O/N=(vNumTimeSteps, vNumTopLoss + vSumOtherLoss) $(strFolderOut + ":" + strFamilyName + ":" + strFamilyName + "_TopLossRates_" + num2str(iPlumeDex))/WAVE=wSpecies_TopLossRates Make/O/N=(vNumTopLoss + vSumOtherLoss)/T $(strFolderOut + ":" + strFamilyName + ":" + strFamilyName + "_TopLossReactNames_" + num2str(iPlumeDex))/WAVE=twSpecies_TopLossReactNames If(vNumLossReacts > 0) twSpecies_LossReactNames = twSpecies_reacts[TempDex[p]] wSpecies_LossRates = wSpecies_Rates[p][TempDex[q]] wSpecies_TopLossRates = wSpecies_LossRates[p][q] twSpecies_TopLossReactNames = twSpecies_LossReactNames If(vSumOtherLoss == 1) Make/O/N=(vNumTimeSteps, vNumLossReacts - vNumTopLoss)/FREE TempMat TempMat = wSpecies_LossRates[p][q + vNumTopLoss] MatrixOp/O/FREE TempSumOther = Sumcols(TempMat^t) wSpecies_TopLossRates[][vNumTopLoss] = TempSumOther[0][p] twSpecies_TopLossReactNames[vNumTopLoss] = "Other" EndIf EndIf //Get major prodution reactions Make/O/N=(vNumReactionsSpecies)/FREE TempDex = p Extract/O TempDex, TempDex, wSpecies_IntRates > 0 Variable vNumProdReacts = numpnts(TempDex) Make/O/N=(vNumProdReacts)/T/FREE twSpecies_ProdReactNames Make/O/N=(vNumTimeSteps, vNumProdReacts)/FREE wSpecies_ProdRates Variable vNumTopProd = min(vNumRates, vNumProdReacts) Variable vSumOtherProd = vNumProdReacts <= vNumRates ? 0 : 1 Make/O/N=(vNumTimeSteps, vNumTopProd + vSumOtherProd) $(strFolderOut + ":" + strFamilyName + ":" + strFamilyName + "_TopProdRates_" + num2str(iPlumeDex))/WAVE=wSpecies_TopProdRates Make/O/N=(vNumTopProd + vSumOtherProd)/T $(strFolderOut + ":" + strFamilyName + ":" + strFamilyName + "_TopProdReactNames_" + num2str(iPlumeDex))/WAVE=twSpecies_TopProdReactNames If(vNumProdReacts > 0) twSpecies_ProdReactNames = twSpecies_reacts[TempDex[p]] wSpecies_ProdRates = wSpecies_Rates[p][TempDex[q]] wSpecies_TopProdRates = wSpecies_ProdRates[p][DimSize(wSpecies_ProdRates,1) - 1 - q] twSpecies_TopProdReactNames = twSpecies_ProdReactNames[numpnts(twSpecies_ProdReactNames) - 1 - p] If(vSumOtherProd == 1) Make/O/N=(vNumTimeSteps, vNumProdReacts - vNumTopProd)/FREE TempMat TempMat = wSpecies_ProdRates[p][q] MatrixOp/O/FREE TempSumOther = Sumcols(TempMat^t) wSpecies_TopProdRates[][vNumTopProd] = TempSumOther[0][p] twSpecies_TopProdReactNames[vNumTopProd] = "Other" EndIf EndIf Wave wDil = $(strFolderOut + ":Model_Dil_" + num2str(iPlumeDex)) Make/O/N=(vNumTimeSteps)/FREE TempDil For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpeciesInFamily); iSpeciesDex += 1) vSpeciesDex = wSpeciesName_Index[iSpeciesDex] TempDil = wDil[p][vSpeciesDex] wFamily_Dil[iPlumeDex] += sum(TempDil) EndFor EndFor EndFor CollateTopProdLoss(strFamilyName) PlotTopRates_Puff(strFamilyName) End Function CollateTopProdLoss(strCurrentSpecies) String strCurrentSpecies SetDataFolder root: String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "BoxModel_Run*Puff", ";") Variable vNumRuns = ItemsInList(strFolderList, ";") Variable iRunDex, iPlumeDex, iDex For(iRunDex = 1; iRunDex <= vNumRuns; iRunDex += 1) String strFolderIn = "root:BoxModel_Run" + num2str(iRunDex) + "_Puff" String strFolderOut = "root:BoxModelOut_Run" + num2str(iRunDex) + "_Puff" Wave Int_Time = $(strFolderIn + ":Int_Time") Make/O/N=(numpnts(Int_Time)) $(strFolderOut + ":ZeroLine") Make/O/N=0/T/FREE twLoss_Concat, twProd_Concat For(iPlumeDex = 0; iPlumeDex < numpnts(Int_Time); iPlumeDex += 1) Wave/T wCurrentReacts = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopLossReactNames_" + num2str(iPlumeDex)) Concatenate/NP/T {wCurrentReacts}, twLoss_Concat Wave/T wCurrentReacts = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopProdReactNames_" + num2str(iPlumeDex)) Concatenate/NP/T {wCurrentReacts}, twProd_Concat EndFor If(numpnts(twLoss_Concat) > 0) If(numpnts(twLoss_Concat) > 1) FindDuplicates/RT=$(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopLossReactNames_All") twLoss_Concat Wave/T twSpecies_TopLossReacts_All = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopLossReactNames_All") Else Make/O/N=1/T $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopLossReactNames_All")/WAVE=twSpecies_TopLossReacts_All twSpecies_TopLossReacts_All = twLoss_Concat EndIf Make/O/N=(numpnts(twSpecies_TopLossReacts_All))/FREE TempSum For(iDex = 0; iDex < numpnts(twSpecies_TopLossReacts_All); iDex += 1) String strTempName = ReplaceString(" + ", twSpecies_TopLossReacts_All[iDex], "_") Make/O/N=(numpnts(Int_Time)) $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_Loss_" + strTempName)/Wave=wSpecies_Loss For(iPlumeDex = 0; iPlumeDex < numpnts(Int_Time); iPlumeDex += 1) Wave/T twSpecies_TopLossReactNames = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopLossReactNames_" + num2str(iPlumeDex)) Wave wSpecies_TopLossRates = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopLossRates_" + num2str(iPlumeDex)) MatrixOp/O/FREE wSpecies_TopLossInt = SumCols(wSpecies_TopLossRates) FindValue/TEXT = twSpecies_TopLossReacts_All[iDex] twSpecies_TopLossReactNames If(V_Value >= 0) wSpecies_Loss[iPlumeDex] = wSpecies_TopLossInt[0][V_value] * Int_Time[iPlumeDex] EndIf EndFor TempSum[iDex] = sum(wSpecies_Loss) EndFor Sort TempSum, TempSum, twSpecies_TopLossReacts_All For(iDex = 0; iDex < numpnts(twSpecies_TopLossReacts_All); iDex += 1) Make/O/N=(numpnts(Int_Time)) $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_LossReaction" + num2str(iDex))/Wave=wSpecies_LossReaction strTempName = ReplaceString(" + ", twSpecies_TopLossReacts_All[iDex], "_") Wave wSpecies_Loss = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_Loss_" + strTempName) wSpecies_LossReaction = wSpecies_Loss KillWaves/Z wSpecies_Loss EndFor EndIf If(numpnts(twProd_Concat) > 0) If(numpnts(twProd_Concat) > 1) FindDuplicates/RT=$(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopProdReactNames_All") twProd_Concat Wave/T twSpecies_TopProdReactNames_All = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopProdReactNames_All") Else Make/O/N=1/T $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopProdReactNames_All")/WAVE=twSpecies_TopProdReactNames_All twSpecies_TopProdReactNames_All = twProd_Concat EndIf Make/O/N=(numpnts(twSpecies_TopProdReactNames_All))/FREE TempSum For(iDex = 0; iDex < numpnts(twSpecies_TopProdReactNames_All); iDex += 1) strTempName = ReplaceString(" + ", twSpecies_TopProdReactNames_All[iDex], "_") Make/O/N=(numpnts(Int_Time)) $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_Prod_" + strTempName)/Wave=wSpecies_Prod For(iPlumeDex = 0; iPlumeDex < numpnts(Int_Time); iPlumeDex += 1) Wave/T twSpecies_TopProdReactNames = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopProdReactNames_" + num2str(iPlumeDex)) Wave wSpecies_TopProdRates = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopProdRates_" + num2str(iPlumeDex)) MatrixOp/O/FREE wSpecies_TopProdInt = SumCols(wSpecies_TopProdRates) FindValue/TEXT = twSpecies_TopProdReactNames_All[iDex] twSpecies_TopProdReactNames If(V_Value >= 0) wSpecies_Prod[iPlumeDex] = wSpecies_TopProdInt[0][V_value] * Int_Time[iPlumeDex] EndIf EndFor TempSum[iDex] = sum(wSpecies_Prod) EndFor Sort/R TempSum, TempSum, twSpecies_TopProdReactNames_All For(iDex = 0; iDex < numpnts(twSpecies_TopProdReactNames_All); iDex += 1) Make/O/N=(numpnts(Int_Time)) $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_ProdReaction" + num2str(iDex))/Wave=wSpecies_ProdReaction strTempName = ReplaceString(" + ", twSpecies_TopProdReactNames_All[iDex], "_") Wave wSpecies_Prod = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_Prod_" + strTempName) wSpecies_ProdReaction = wSpecies_Prod KillWaves/Z wSpecies_Prod EndFor EndIf EndFor End Function PlotTopRates_Puff(strCurrentSpecies) String strCurrentSpecies SetDataFolder root: //If string is empty, prompt the user for the species name If(strlen(strCurrentSpecies) == 0) Prompt strCurrentSpecies, "Species:" DoPrompt "Enter species to process", strCurrentSpecies If(V_flag == 1 || strlen(strCurrentSpecies) == 0) Abort EndIf EndIf ColorTab2Wave Rainbow Wave M_Colors String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "BoxModel_Run*Puff", ";") Variable vNumRuns = ItemsInList(strFolderList, ";") Variable iRunDex, iDex For(iRunDex = 1; iRunDex <= vNumRuns; iRunDex += 1) String strFolderOut = "root:BoxModelOut_Run" + num2str(iRunDex) + "_Puff" Wave/T twSpecies_TopLossReactNames_All = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopLossReactNames_All") Variable vNumLossReacts = numpnts(twSpecies_TopLossReactNames_All) Wave/T twSpecies_TopProdReactNames_All = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopProdReactNames_All") Variable vNumProdReacts = numpnts(twSpecies_TopProdReactNames_All) String strPlotName = "TotalReact_" + strCurrentSpecies + "_Run" + num2str(iRunDex) DoWindow/K $strPlotName Display as strPlotName DoWindow/C $strPlotName String strLossLegend = "" For(iDex = vNumLossReacts - 1; iDex >= 0; iDex -= 1) String strTraceName = "Loss" + num2str(iDex) strLossLegend = "\s(" + strTraceName + ") " + twSpecies_TopLossReactNames_All[iDex] + "\r" + strLossLegend Variable vColor = iDex / vNumLossReacts * 99 Wave wWave = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_LossReaction" + num2str(iDex)) AppendToGraph wWave/TN=$strTraceName ModifyGraph mode($strTraceName)=5,toMode($strTraceName)=2 ModifyGraph rgb($strTraceName) = (M_colors[vColor][0], M_Colors[vColor][1], M_Colors[vColor][2]), hbFill($strTraceName)=3 EndFor strLossLegend = RemoveEnding(strLossLegend, "\r") strLossLegend = "\Z08Top loss reactions:\n" + strLossLegend Wave ZeroLine = $(strFolderOut + ":ZeroLine") AppendTograph ZeroLine String strProdLegend = "" For(iDex = vNumProdReacts - 1; iDex >= 0; iDex -= 1) strTraceName = "Prod" + num2str(iDex) strProdLegend = "\s(" + strTraceName + ") " + twSpecies_TopProdReactNames_All[iDex] + "\r" + strProdLegend vColor = iDex / vNumProdReacts * 99 Wave wWave = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_ProdReaction" + num2str(iDex)) AppendToGraph wWave/TN=$strTraceName ModifyGraph mode($strTraceName)=5,toMode($strTraceName)=2 ModifyGraph rgb($strTraceName) = (M_colors[vColor][0], M_Colors[vColor][1], M_Colors[vColor][2]), hbFill($strTraceName)=3 EndFor strProdLegend = RemoveEnding(strProdLegend, "\r") strProdLegend = "\Z08Top prod reactions\r" + strProdLegend AppendTograph ZeroLine Wave wDil = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_Dil") AppendToGraph wDil/TN=Dilution ModifyGraph mode(Dilution) = 3, marker(Dilution) = 19, rgb(Dilution) = (0,0,0) EndFor Legend/A=LB/N=text0 strLossLegend Legend/A=LT/N=text1 strProdLegend Label bottom, "Plume number" Label left, strCurrentSpecies + " prod. rate (ppb/hr)" KillWaves/Z M_Colors End Function PlotTopRatesByPlume_Puff(strCurrentSpecies, iPlumeDex) String strCurrentSpecies Variable iPlumeDex SetDataFolder root: ColorTab2Wave Rainbow Wave M_Colors //If string is empty, prompt the user for the species name If(strlen(strCurrentSpecies) == 0) Prompt strCurrentSpecies, "Species:" Prompt iPlumeDex, "Plume number:" DoPrompt "Enter species to process", strCurrentSpecies, iPlumeDex If(V_flag == 1 || strlen(strCurrentSpecies) == 0) Abort EndIf EndIf String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "BoxModel_Run*Puff", ";") Variable vNumRuns = ItemsInList(strFolderList, ";") Variable iRunDex, iDex For(iRunDex = 1; iRunDex <= vNumRuns; iRunDex += 1) String strFolderOut = "root:BoxModelOut_Run" + num2str(iRunDex) + "_Puff" Wave/T twCnames = $(strFolderOut + ":twCnames") FindValue/TEXT=strCurrentSpecies/TXOP=2 twCnames Variable vSpeciesDex = V_Value Wave/T twSpecies_TopLossReactNames = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopLossReactNames_" + num2str(iPlumeDex)) Variable vNumLossReacts = numpnts(twSpecies_TopLossReactNames) Wave wSpecies_Loss = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopLossRates_" + num2str(iPlumeDex)) Wave/T twSpecies_TopProdReactNames = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopProdReactNames_" + num2str(iPlumeDex)) Variable vNumProdReacts = numpnts(twSpecies_TopProdReactNames) Wave wSpecies_Prod = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopProdRates_" + num2str(iPlumeDex)) Wave wModel_Time = $(strFolderOut + ":Model_Time_" + num2str(iPlumeDex) + "_min") String strPlotName = "TotalReact_" + strCurrentSpecies + "_Run" + num2str(iRunDex) + "_Plume" + num2str(iPlumeDex) DoWindow/K $strPlotName Display as strPlotName DoWindow/C $strPlotName String strLossLegend = "" For(iDex = vNumLossReacts - 1; iDex >= 0; iDex -= 1) String strTraceName = "Loss" + num2str(iDex) strLossLegend = "\s(" + strTraceName + ") " + twSpecies_TopLossReactNames[iDex] + "\r" + strLossLegend Variable vColor = iDex / vNumLossReacts * 99 // Wave wWave = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_LossReaction" + num2str(iDex)) AppendToGraph wSpecies_Loss[][iDex]/TN=$strTraceName vs wModel_Time ModifyGraph mode($strTraceName)=7,toMode($strTraceName)=2 ModifyGraph rgb($strTraceName) = (M_colors[vColor][0], M_Colors[vColor][1], M_Colors[vColor][2]), hbFill($strTraceName)=3 EndFor strLossLegend = RemoveEnding(strLossLegend, "\r") strLossLegend = "\Z08Top loss reactions:\n" + strLossLegend Wave ZeroLine = $(strFolderOut + ":Plume_" + num2str(iPlumeDex) + ":ZeroLine") AppendTograph ZeroLine vs wModel_Time String strProdLegend = "" For(iDex = vNumProdReacts - 1; iDex >= 0; iDex -= 1) strTraceName = "Prod" + num2str(iDex) strProdLegend = "\s(" + strTraceName + ") " + twSpecies_TopProdReactNames[iDex] + "\r" + strProdLegend vColor = iDex / vNumProdReacts * 99 // Wave wWave = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_ProdReaction" + num2str(iDex)) AppendToGraph wSpecies_Prod[][iDex]/TN=$strTraceName vs wModel_Time ModifyGraph mode($strTraceName)=7,toMode($strTraceName)=2 ModifyGraph rgb($strTraceName) = (M_colors[vColor][0], M_Colors[vColor][1], M_Colors[vColor][2]), hbFill($strTraceName)=3 EndFor strProdLegend = RemoveEnding(strProdLegend, "\r") strProdLegend = "\Z08Top prod reactions\r" + strProdLegend AppendTograph ZeroLine vs wModel_Time // Wave wDil = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_Dil") Wave wDil = $(strFolderOut + ":Model_Dil_" + num2str(iPlumeDex)) AppendToGraph wDil[][vSpeciesDex]/TN=Dilution vs wModel_Time ModifyGraph mode(Dilution) = 0, lstyle(Dilution) = 3, rgb(Dilution) = (0,0,0) EndFor Legend/A=LB/N=text0 strLossLegend Legend/A=LT/N=text1 strProdLegend Label bottom, "Time (min)" Label left, strCurrentSpecies + " prod. rate (ppb/hr)" ModifyGraph log(bottom)=1 KillWaves/Z M_Colors End //Process the rates for the last model Function ProcessModelRates_Last(strCurrentSpecies) //Process the F0AM-derived rates for a given species //Based on the code written for F0AM4.2 by Glenn Wolfe String strCurrentSpecies SetDataFolder root: //If string is empty, prompt the user for the species name If(strlen(strCurrentSpecies) == 0) Prompt strCurrentSpecies, "Species:" DoPrompt "Enter species to process", strCurrentSpecies If(V_flag == 1 || strlen(strCurrentSpecies) == 0) Abort EndIf EndIf Variable vNumRates = 5 //This is the number of top rates to gather //Get the total number of runs String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "BoxModel_Run*Last", ";") Variable vNumRuns = ItemsInList(strFolderList, ";") Variable iRunDex, iPlumeDex, iDex For(iRunDex = 1; iRunDex <= vNumRuns; iRunDex += 1) // String strFolderIn = "root:BoxModel_Run" + num2str(iRunDex) + "_Last" String strFolderOut = "root:BoxModelOut_Run" + num2str(iRunDex) + "_Last" // Wave Int_Time = $(strFolderIn + ":Int_Time") Wave/T twCnames = $(strFolderOut + ":twCnames") Wave/T twRnames = $(strFolderOut + ":twRnames") Variable vNumSpeciesTotal = numpnts(twCnames) Variable vNumReactionsTotal = numpnts(twRnames) Wave Stoic = $(strFolderOut + ":Stoic") //Adjustment for HCl If(StringMatch(strCurrentSpecies, "HClpCl") == 1) String strF0AMname = "HCL" ElseIf(StringMatch(strCurrentSpecies, "HNO3pNO3") == 1) strF0AMname = "HNO3" Else strF0AMname = strCurrentSpecies EndIf FindValue/TEXT=strF0AMname/TXOP=2 twCnames Variable vSpeciesDex = V_Value NewDataFolder/O/S $(strFolderOut + ":" + strCurrentSpecies) // Make/O/N=(numpnts(Int_Time)) $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_Dil")/WAVE = wSpecies_Dil //Do some prep work. //First, retrieve the list of reactions that involve this species (i.e. those which have non-zero stoichiometric coefficients) Make/O/N=(vNumReactionsTotal)/FREE TempStoic, TempIndex, wSpecies_Stoic, wSpecies_Index TempStoic = Stoic[p][vSpeciesDex] TempIndex = p Extract/O TempStoic, wSpecies_Stoic, TempStoic != 0 Extract/O TempIndex, wSpecies_Index, TempStoic != 0 Variable vNumReactionsSpecies = numpnts(wSpecies_Stoic) //The number of reactions that involve the current species //Then, get a list of the equilibrium reactions, so they can later be summed up. Make/O/N=(vNumReactionsSpecies, vNumSpeciesTotal)/FREE Stoic_All //Get the stoichiometric coefficients for every species in the sublist of relevant reactions Stoic_All = Stoic[wSpecies_Index[p]][q] Make/O/N=0/FREE EqDexDir1, EqDexDir2 For(iDex = 0; iDex < vNumReactionsSpecies; iDex += 1) //Then cycle through every combination of reactions, looking for ones whose stoichiometric coefficients cancel out MatrixOp/O/FREE TempShiftMat = RotateRows(Stoic_All, -iDex) MatrixOp/O/FREE TempSumMat = Stoic_All + TempShiftMat MatrixOp/O/FREE TempSumSum = SumRows(abs(TempSumMat)) Redimension/N=(vNumReactionsSpecies - iDex) TempSumSum Extract/O/FREE/INDX TempSumSum, TempIndex, TempSumSum == 0 //Look for net zero reactions If(numpnts(TempIndex) > 0) //If there are any, then get rid of any duplicates, and then add it to the master list of eq. reactions Variable i = 0 Do Variable vDex = TempIndex[i] FindValue/V=(vDex)/T=0.1 EqDexDir1 If(V_Value != -1) DeletePoints i, 1, TempIndex i -= 1 EndIf i += 1 While(i < numpnts(TempIndex)) Concatenate/NP {TempIndex}, EqDexDir1 //EqDexDir1 is the index of one direction TempIndex += iDex Concatenate/NP {TempIndex}, EqDexDir2 //EqDexDir2 is the index of the corresponding opposite direction EndIf EndFor // For(iPlumeDex = 0; iPlumeDex < numpnts(Int_Time); iPlumeDex += 1) Wave Model_Rate = $(strFolderOut + ":Model_Rate_Last") Variable vNumTimeSteps = DimSize(Model_Rate, 0) Variable vIntTime = 10 vNumReactionsSpecies = numpnts(wSpecies_Stoic) //Grab the relevant rates for this species Make/O/N=(vNumTimeSteps, vNumReactionsSpecies) $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_Rates_Last")/WAVE=wSpecies_Rates Make/O/N=(vNumReactionsSpecies)/T $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_Rnames_Last")/WAVE=twSpecies_Rnames wSpecies_Rates = Model_Rate[p][wSpecies_Index[q]] * wSpecies_Stoic[q] twSpecies_Rnames = twRnames[wSpecies_Index[p]] //If there are equilibrium reactions to sum up, figure out what the net reaction is //then substitute the net forward and net reverse reactions in For(iDex = 0; iDex < numpnts(EqDexDir1); iDex += 1) Variable vDexDir1 = EqDexDir1[iDex] Variable vDexDir2 = EqDexDir2[iDex] Make/O/N=(DimSize(wSpecies_Rates, 0))/FREE TempRateDir1, TempRateDir2, TempNetFor, TempNetBack TempRateDir1 = wSpecies_Rates[p][vDexDir1] TempRateDir2 = wSpecies_Rates[p][vDexDir2] Variable vSumDir1 = Sum(TempRateDir1) Variable vSumDir2 = Sum(TempRateDir2) TempNetFor = (TempRateDir1[p] + TempRateDir2[p]) < 0 ? 0 : (TempRateDir1[p] + TempRateDir2[p]) TempNetBack = (TempRateDir1[p] + TempRateDir2[p]) > 0 ? 0 : (TempRateDir1[p] + TempRateDir2[p]) If(vSumDir1 > vSumDir2) wSpecies_Rates[][vDexDir1] = TempNetFor[p] wSpecies_Rates[][vDexDir2] = TempNetBack[p] Else wSpecies_Rates[][vDexDir1] = TempNetBack[p] wSpecies_Rates[][vDexDir2] = TempNetFor[p] EndIf Endfor //Sort in order of increasing total reactivity MatrixOp/O/FREE wSpecies_IntRates = SumCols(wSpecies_Rates) Redimension/N=(vNumReactionsSpecies) wSpecies_IntRates Make/O/N=(DimSize(wSpecies_Rates, 1), DimSize(wSpecies_Rates, 0))/FREE TempMat //Get the transpose so I can use SortColumns TempMat = wSpecies_Rates[q][p] SortColumns keyWaves = wSpecies_IntRates, sortWaves = TempMat wSpecies_Rates = TempMat[q][p] Sort wSpecies_IntRates, wSpecies_IntRates, twSpecies_Rnames Make/O/N=(vNumReactionsSpecies)/T/FREE twSpecies_reacts twSpecies_reacts = StringFromList(0, twSpecies_Rnames[p], " =") //Get rid of duplicates If(numpnts(twSpecies_reacts) > 1) FindDuplicates/FREE/INDX=DupDex twSpecies_reacts If(numpnts(DupDex) > 0) Do String strCurrentReaction = twSpecies_reacts[DupDex[0]] FindValue/TEXT=strCurrentReaction/TXOP=4 twSpecies_reacts Variable vOrig = V_value wSpecies_Rates[][vOrig] = wSpecies_Rates[p][vOrig] + wSpecies_Rates[p][DupDex[0]] DeletePoints/M=0 DupDex[0], 1, wSpecies_IntRates, twSpecies_Rnames, twSpecies_reacts DeletePoints/M=1 DupDex[0], 1, wSpecies_Rates DeletePoints/M=0 0, 1, DupDex DupDex -= 1 While(numpnts(DupDex) > 0) vNumReactionsSpecies = numpnts(twSpecies_Rnames) //Sort again MatrixOp/O/FREE wSpecies_IntRates = SumCols(wSpecies_Rates) Redimension/N=(vNumReactionsSpecies) wSpecies_IntRates Make/O/N=(DimSize(wSpecies_Rates, 1), DimSize(wSpecies_Rates, 0))/FREE TempMat TempMat = wSpecies_Rates[q][p] SortColumns keyWaves = wSpecies_IntRates, sortWaves = TempMat wSpecies_Rates = TempMat[q][p] Sort wSpecies_IntRates, wSpecies_IntRates, twSpecies_Rnames Make/O/N=(vNumReactionsSpecies)/T/FREE twSpecies_reacts twSpecies_reacts = StringFromList(0, twSpecies_Rnames[p], " =") EndIf EndIf //Get major loss reactions Make/O/N=(vNumReactionsSpecies)/FREE TempDex = p Extract/O TempDex, TempDex, wSpecies_IntRates < 0 Variable vNumLossReacts = numpnts(TempDex) Make/O/N=(vNumLossReacts)/T/FREE twSpecies_LossReactNames Make/O/N=(vNumTimeSteps, vNumLossReacts)/FREE wSpecies_LossRates Variable vNumTopLoss = min(vNumRates, vNumLossReacts) Variable vSumOtherLoss = vNumLossReacts <= vNumRates ? 0 : 1 Make/O/N=(vNumTimeSteps, vNumTopLoss + vSumOtherLoss) $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopLossRates_Last")/WAVE=wSpecies_TopLossRates Make/O/N=(vNumTopLoss + vSumOtherLoss)/T $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopLossReactNames_Last")/WAVE=twSpecies_TopLossReactNames If(vNumLossReacts > 0) twSpecies_LossReactNames = twSpecies_reacts[TempDex[p]] // wSpecies_LossRates = wSpecies_Rates[p][TempDex[q]] / vIntTime * 3600 wSpecies_LossRates = wSpecies_Rates[p][TempDex[q]] * 3600 wSpecies_TopLossRates = wSpecies_LossRates[p][q] twSpecies_TopLossReactNames = twSpecies_LossReactNames If(vSumOtherLoss == 1) Make/O/N=(vNumTimeSteps, vNumLossReacts - vNumTopLoss)/FREE TempMat TempMat = wSpecies_LossRates[p][q + vNumTopLoss] MatrixOp/O/FREE TempSumOther = Sumcols(TempMat^t) wSpecies_TopLossRates[][vNumTopLoss] = TempSumOther[0][p] twSpecies_TopLossReactNames[vNumTopLoss] = "Other" EndIf EndIf //Get major prodution reactions Make/O/N=(vNumReactionsSpecies)/FREE TempDex = p Extract/O TempDex, TempDex, wSpecies_IntRates > 0 Variable vNumProdReacts = numpnts(TempDex) Make/O/N=(vNumProdReacts)/T/FREE twSpecies_ProdReactNames Make/O/N=(vNumTimeSteps, vNumProdReacts)/FREE wSpecies_ProdRates Variable vNumTopProd = min(vNumRates, vNumProdReacts) Variable vSumOtherProd = vNumProdReacts <= vNumRates ? 0 : 1 Make/O/N=(vNumTimeSteps, vNumTopProd + vSumOtherProd) $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopProdRates_Last")/WAVE=wSpecies_TopProdRates Make/O/N=(vNumTopProd + vSumOtherProd)/T $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopProdReactNames_Last")/WAVE=twSpecies_TopProdReactNames If(vNumProdReacts > 0) twSpecies_ProdReactNames = twSpecies_reacts[TempDex[p]] // wSpecies_ProdRates = wSpecies_Rates[p][TempDex[q]] / vIntTime * 3600 wSpecies_ProdRates = wSpecies_Rates[p][TempDex[q]] * 3600 wSpecies_TopProdRates = wSpecies_ProdRates[p][DimSize(wSpecies_ProdRates,1) - 1 - q] twSpecies_TopProdReactNames = twSpecies_ProdReactNames[numpnts(twSpecies_ProdReactNames) - 1 - p] If(vSumOtherProd == 1) Make/O/N=(vNumTimeSteps, vNumProdReacts - vNumTopProd)/FREE TempMat TempMat = wSpecies_ProdRates[p][q] MatrixOp/O/FREE TempSumOther = Sumcols(TempMat^t) wSpecies_TopProdRates[][vNumTopProd] = TempSumOther[0][p] twSpecies_TopProdReactNames[vNumTopProd] = "Other" EndIf EndIf EndFor End Function PlotTopRatesByPlume_Last(strCurrentSpecies) String strCurrentSpecies SetDataFolder root: //If string is empty, prompt the user for the species name If(strlen(strCurrentSpecies) == 0) Prompt strCurrentSpecies, "Species:" DoPrompt "Enter species to process", strCurrentSpecies If(V_flag == 1 || strlen(strCurrentSpecies) == 0) Abort EndIf EndIf ColorTab2Wave Rainbow Wave M_Colors String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "BoxModel_Run*Last", ";") Variable vNumRuns = ItemsInList(strFolderList, ";") Variable iRunDex, iDex For(iRunDex = 1; iRunDex <= vNumRuns; iRunDex += 1) String strFolderOut = "root:BoxModelOut_Run" + num2str(iRunDex) + "_Last" Wave/T twCnames = $(strFolderOut + ":twCnames") FindValue/TEXT=strCurrentSpecies/TXOP=2 twCnames Variable vSpeciesDex = V_Value Wave/T twSpecies_TopLossReactNames = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopLossReactNames_Last") Variable vNumLossReacts = numpnts(twSpecies_TopLossReactNames) Wave wSpecies_Loss = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopLossRates_Last") Wave/T twSpecies_TopProdReactNames = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopProdReactNames_Last") Variable vNumProdReacts = numpnts(twSpecies_TopProdReactNames) Wave wSpecies_Prod = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_TopProdRates_Last") Wave wModel_Time = $(strFolderOut + ":Model_Time_Last_min") String strPlotName = "TotalReact_" + strCurrentSpecies + "_Run" + num2str(iRunDex) + "_Last" DoWindow/K $strPlotName Display as strPlotName DoWindow/C $strPlotName String strLossLegend = "" For(iDex = vNumLossReacts - 1; iDex >= 0; iDex -= 1) String strTraceName = "Loss" + num2str(iDex) strLossLegend = "\s(" + strTraceName + ") " + twSpecies_TopLossReactNames[iDex] + "\r" + strLossLegend Variable vColor = iDex / vNumLossReacts * 99 // Wave wWave = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_LossReaction" + num2str(iDex)) AppendToGraph wSpecies_Loss[][iDex]/TN=$strTraceName vs wModel_Time ModifyGraph mode($strTraceName)=7,toMode($strTraceName)=2 ModifyGraph rgb($strTraceName) = (M_colors[vColor][0], M_Colors[vColor][1], M_Colors[vColor][2]), hbFill($strTraceName)=3 EndFor strLossLegend = RemoveEnding(strLossLegend, "\r") strLossLegend = "\Z08Top loss reactions:\n" + strLossLegend Wave ZeroLine = $(strFolderOut + ":ZeroLine") AppendTograph ZeroLine vs wModel_Time String strProdLegend = "" For(iDex = vNumProdReacts - 1; iDex >= 0; iDex -= 1) strTraceName = "Prod" + num2str(iDex) strProdLegend = "\s(" + strTraceName + ") " + twSpecies_TopProdReactNames[iDex] + "\r" + strProdLegend vColor = iDex / vNumProdReacts * 99 // Wave wWave = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_ProdReaction" + num2str(iDex)) AppendToGraph wSpecies_Prod[][iDex]/TN=$strTraceName vs wModel_Time ModifyGraph mode($strTraceName)=7,toMode($strTraceName)=2 ModifyGraph rgb($strTraceName) = (M_colors[vColor][0], M_Colors[vColor][1], M_Colors[vColor][2]), hbFill($strTraceName)=3 EndFor strProdLegend = RemoveEnding(strProdLegend, "\r") strProdLegend = "\Z08Top prod reactions\r" + strProdLegend AppendTograph ZeroLine vs wModel_Time // Wave wDil = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_Dil") Wave wDil = $(strFolderOut + ":Model_Dil_Last") AppendToGraph wDil[][vSpeciesDex]/TN=Dilution vs wModel_Time ModifyGraph mode(Dilution) = 0, lstyle(Dilution) = 3, rgb(Dilution) = (0,0,0) ModifyGraph muloffset(Dilution)={0,3600} Legend/A=LB/N=text0 strLossLegend Legend/A=LT/N=text1 strProdLegend Label bottom, "Time (min)" Label left, strCurrentSpecies + " prod. rate (ppb/hr)" ModifyGraph log(bottom)=1 EndFor KillWaves/Z M_Colors End Function MakeChainLengths() SetDataFolder root: //Get the total number of runs String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "BoxModel_Run*Last", ";") Variable vNumRuns = ItemsInList(strFolderList, ";") Variable iRunDex, iDex, iSpeciesDex String strSpecies = "BR;CL" For(iRunDex = 1; iRunDex <= vNumRuns; iRunDex += 1) String strFolderOut = "root:BoxModelOut_Run" + num2str(iRunDex) + "_Last" Wave Stoic = $(strFolderOut + ":Stoic") Wave/T twRnames = $(strFolderOut + ":twRnames") Wave/T twCnames = $(strFolderOut + ":twCnames") For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpecies); iSpeciesDex += 1) String strCurrentSpecies = StringFromLIst(iSpeciesDex, strSpecies) ProcessModelRates_Last(strCurrentSpecies) Wave/T twSubReactsNames = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_Rnames_Last") Wave wSubReacts = $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_Rates_Last") Make/O/N=(DimSize(wSubReacts, 0)) $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_Total_Prop_Last")/WAVE=wTotalProp Make/O/N=(DimSize(wSubReacts, 0)) $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_Total_Term_Last")/WAVE=wTotalTerm Make/O/N=(DimSize(wSubReacts, 0)) $(strFolderOut + ":" + strCurrentSpecies + ":" + strCurrentSpecies + "_ChainLength_Last")/WAVE=wChainLength wTotalProp = 0 wTotalTerm = 0 For(iDex = 0; iDex < numpnts(twSubReactsNames); iDex += 1) FindValue/TEXT=twSubReactsNames[iDex]/TXOP=4 twRnames Variable vReactDex = V_Value FindValue/TEXT=(strCurrentSpecies + "2")/TXOP=4 twCnames Variable vX2Dex = V_Value FindValue/TEXT=(strCurrentSpecies + "O")/TXOP=4 twCnames Variable vXODex = V_Value FindValue/TEXT=(strCurrentSpecies)/TXOP=4 twCnames Variable vXDex = V_Value If(Stoic[vReactDex][vXDex] == -1) If(Stoic[vReactDex][vX2Dex] == 1 || Stoic[vReactDex][vXODex] == 1) wTotalProp += wSubReacts[p][iDex] Else wTotalTerm += wSubReacts[p][iDex] EndIf EndIf EndFor wChainLength = wTotalProp / wTotalTerm EndFor EndFor End //Sensitivity tests Function CompareModels(vRun, strSuffix1, strSuffix2, strSuffix3) String strSuffix1, strSuffix2, strSuffix3 Variable vRun //If string is empty, prompt the user for the species name If(strlen(strSuffix1) == 0) Prompt vRun, "Run number:" Prompt strSuffix1, "Case 1:" Prompt strSuffix2, "Case 2:" Prompt strSuffix3, "Case 3:" DoPrompt "Enter cases to process", vRun, strSuffix1, strSuffix2, strSuffix3 If(V_flag == 1 || strlen(strSuffix1) == 0) Abort EndIf EndIf SetDataFolder root: String strSpeciesToPlot = "Cl2;Br2;BrCl;HClpCl;O3;NO;NO2;HNO3pNO3;HONO;N2O5;BrNO2;ClNO2;HOBr;HOCl;BrO;ClO" String strSpeciesUnits = "ppb;ppb;ppb;ppb;ppb;ppb;ppb;ppb;ppb;ppb;ppb;ppb;ppb;ppb;cps;cps" String strSpeciesAxis = "log;log;log;log;lin;log;log;log;log;lin;log;log;log;log;lin;lin" String strLayoutName = "Compare_Model_Run" + num2str(vRun) + "_" + strSuffix1 + "_" + strSuffix2 + "_" + strSuffix3 DoWindow/K $strLayoutName NewLayout/N=$strLayoutName as strLayoutName Variable iSpeciesDex, vPage = 1, iDex, iPlumeDex Wave wObs_Age_Suffix1 = $("root:Sens:" + strSuffix1 + ":BoxModel_Run" + num2str(vRun) + "_Puff:Obs_Age_min") Wave wObs_Age_Suffix2 = $("root:Sens:" + strSuffix2 + ":BoxModel_Run" + num2str(vRun) + "_Puff:Obs_Age_min") Wave wObs_Age_Suffix3 = $("root:Sens:" + strSuffix3 + ":BoxModel_Run" + num2str(vRun) + "_Puff:Obs_Age_min") For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpeciesToPlot); iSpeciesDex += 1) String strCurrentSpecies = StringfromList(iSpeciesDex, strSpeciesToPlot) Wave wObs_Species_Suffix1 = $("root:Sens:" + strSuffix1 + ":BoxModel_Run" + num2str(vRun) + "_Puff:Obs_" + strCurrentSpecies) Wave wObs_Species_Suffix2 = $("root:Sens:" + strSuffix2 + ":BoxModel_Run" + num2str(vRun) + "_Puff:Obs_" + strCurrentSpecies) Wave wObs_Species_Suffix3 = $("root:Sens:" + strSuffix3 + ":BoxModel_Run" + num2str(vRun) + "_Puff:Obs_" + strCurrentSpecies) Wave wModel_Species_Suffix1 = $("root:Sens:" + strSuffix1 + ":BoxModelOut_Run" + num2str(vRun) + "_Puff:Model_" + strCurrentSpecies) Wave wModel_Species_Suffix2 = $("root:Sens:" + strSuffix2 + ":BoxModelOut_Run" + num2str(vRun) + "_Puff:Model_" + strCurrentSpecies) Wave wModel_Species_Suffix3 = $("root:Sens:" + strSuffix3 + ":BoxModelOut_Run" + num2str(vRun) + "_Puff:Model_" + strCurrentSpecies) String strGraphTitle = "Model_Obs_Run" + num2str(vRun) + "_" + strCurrentSpecies + "_" + strSuffix1 + "_" + strSuffix2 + "_" + strSuffix3 DoWindow/K/Z $strGraphTitle Display as strGraphTitle DoWindow/C $strGraphTitle Appendtograph wModel_Species_Suffix1/TN=$(strCurrentSpecies + "_Model_" + strSuffix1) vs wObs_Age_Suffix1 Appendtograph wModel_Species_Suffix2/TN=$(strCurrentSpecies + "_Model_" + strSuffix2) vs wObs_Age_Suffix2 If(strlen(strSuffix3) > 0) Appendtograph wModel_Species_Suffix3/TN=$(strCurrentSpecies + "_Model_" + strSuffix3) vs wObs_Age_Suffix3 EndIf If(stringmatch(StringfromList(iSpeciesDex, strSpeciesUnits), "ppb") == 1) Appendtograph wObs_Species_Suffix1/TN=$(strCurrentSpecies + "_Obs_" + strSuffix1) vs wObs_Age_Suffix1 Appendtograph wObs_Species_Suffix2/TN=$(strCurrentSpecies + "_Obs_" + strSuffix2) vs wObs_Age_Suffix2 If(strlen(strSuffix3) > 0) Appendtograph wObs_Species_Suffix3/TN=$(strCurrentSpecies + "_Obs_" + strSuffix3) vs wObs_Age_Suffix3 EndIf ModifyGraph mirror = 2 ElseIf(stringmatch(StringfromList(iSpeciesDex, strSpeciesUnits), "cps") == 1) Appendtograph/R wObs_Species_Suffix1/TN=$(strCurrentSpecies + "_Obs_" + strSuffix1) vs wObs_Age_Suffix1 Appendtograph/R wObs_Species_Suffix2/TN=$(strCurrentSpecies + "_Obs_" + strSuffix2) vs wObs_Age_Suffix2 If(strlen(strSuffix3) > 0) Appendtograph/R wObs_Species_Suffix3/TN=$(strCurrentSpecies + "_Obs_" + strSuffix3) vs wObs_Age_Suffix3 EndIf Label right "Obs " + strCurrentSpecies + " (cps)" EndIf ModifyGraph rgb($(strCurrentSpecies + "_Model_" + strSuffix1)) = (0,0,0) ModifyGraph rgb($(strCurrentSpecies + "_Model_" + strSuffix2)) = (0,0,0) ModifyGraph mode($strCurrentSpecies + "_Model_" + strSuffix1) = 4, mode($strCurrentSpecies + "_Obs_" + strSuffix1) = 3 ModifyGraph marker($strCurrentSpecies + "_Model_" + strSuffix1) = 19, marker($strCurrentSpecies + "_Obs_" + strSuffix1) = 19 ModifyGraph mode($strCurrentSpecies + "_Model_" + strSuffix2) = 4, mode($strCurrentSpecies + "_Obs_" + strSuffix2) = 3 ModifyGraph marker($strCurrentSpecies + "_Model_" + strSuffix2) = 8, marker($strCurrentSpecies + "_Obs_" + strSuffix2) = 8 ModifyGraph lstyle($strCurrentSpecies + "_Model_" + strSuffix2) = 3 If(strlen(strSuffix3) > 0) ModifyGraph mode($strCurrentSpecies + "_Model_" + strSuffix3) = 4, mode($strCurrentSpecies + "_Obs_" + strSuffix3) = 3 ModifyGraph marker($strCurrentSpecies + "_Model_" + strSuffix3) = 0, marker($strCurrentSpecies + "_Obs_" + strSuffix3) = 0 ModifyGraph lstyle($strCurrentSpecies + "_Model_" + strSuffix3) = 9 ModifyGraph rgb($(strCurrentSpecies + "_Model_" + strSuffix3)) = (0,0,0) EndIf Label bottom "Plume age (min)" Label left strCurrentSpecies + " (" + StringfromList(iSpeciesDex, strSpeciesUnits) + ")" ModifyGraph standoff=0 String strLegend = "\Z08" + strCurrentSpecies + "\r\s(" + strCurrentSpecies + "_Model_" + strSuffix1 + ") " + strSuffix1 strLegend += "\r\s(" + strCurrentSpecies + "_Model_" + strSuffix2 + ") " + strSuffix2 If(strlen(strSuffix3) > 0) strLegend += "\r\s(" + strCurrentSpecies + "_Model_" + strSuffix3 + ") " + strSuffix3 EndIf TextBox/C/N=text0/A=LT strLegend ModifyGraph log(left) = SelectNumber(StringMatch(StringFromList(iSpeciesDex, strSpeciesAxis), "log") == 1 , 0, 1) ModifyGraph log(bottom) = 1 SetAxis bottom 1,* // vLowerBound = min(vLowerBound, WaveMin(wObs_Species_Suffix1)) // SetAxis left max(vLowerBound,1e-10), * If(mod(iSpeciesDex, 8) == 0 && iSpeciesDex != 0) LayoutPageAction/W=$strLayoutName appendPage vPage += 1 EndIf AppendLayoutObject/W=$strLayoutName/PAGE=(vPage) graph $strGraphTitle EndFor For(iDex = 0; iDex < vPage; iDex += 1) String strExecuteText = "Tile/A=(4,2)" LayoutPageAction/W=$strLayoutName Page=(iDex + 1) Execute strExecuteText Endfor End Function CompareModelAccuracy(vRun, strSuffix1, strSuffix2, strSuffix3) String strSuffix1, strSuffix2, strSuffix3 Variable vRun //If string is empty, prompt the user for the species name If(strlen(strSuffix1) == 0) Prompt vRun, "Run number:" Prompt strSuffix1, "Case 1:" Prompt strSuffix2, "Case 2:" Prompt strSuffix3, "Case 3:" DoPrompt "Enter cases to process", vRun, strSuffix1, strSuffix2, strSuffix3 If(V_flag == 1 || strlen(strSuffix1) == 0) Abort EndIf EndIf String strPlotName = "Compare_Model_Accuracy_" + strSuffix1 + "_" + strSuffix2 + "_" + strSuffix3 DoWindow/K/Z $strPlotName Display as strPlotName DoWindow/C $strPlotName Wave/T twObsSpecies_1 = $("root:Sens:" + strSuffix1 + ":BoxModelOut_Run" + num2str(vRun) + "_Puff:twObsSpecies") Wave/T twObsSpecies_2 = $("root:Sens:" + strSuffix2 + ":BoxModelOut_Run" + num2str(vRun) + "_Puff:twObsSpecies") Wave/T twObsSpecies_3 = $("root:Sens:" + strSuffix3 + ":BoxModelOut_Run" + num2str(vRun) + "_Puff:twObsSpecies") Wave wModelObsRMSE_1 = $("root:Sens:" + strSuffix1 + ":BoxModelOut_Run" + num2str(vRun) + "_Puff:ModelObs_RMSE") Wave wModelObsRMSE_2 = $("root:Sens:" + strSuffix2 + ":BoxModelOut_Run" + num2str(vRun) + "_Puff:ModelObs_RMSE") Wave wModelObsRMSE_3 = $("root:Sens:" + strSuffix3 + ":BoxModelOut_Run" + num2str(vRun) + "_Puff:ModelObs_RMSE") AppendToGraph/C=(54741,24158,0) wModelObsRMSE_1 vs twObsSpecies_1 AppendToGraph/C=(0,40606,29555) wModelObsRMSE_2 vs twObsSpecies_1 If(strlen(strSuffix3) > 0) AppendToGraph/C=(0,29298,45746) wModelObsRMSE_3 vs twObsSpecies_1 EndIf ModifyGraph tkLblRot(bottom)=60 SetAxis left 0,* End Function CompareModelStartEnd(vRun, strSuffix1, strSuffix2, strSuffix3) String strSuffix1, strSuffix2, strSuffix3 Variable vRun //If string is empty, prompt the user for the species name If(strlen(strSuffix1) == 0) Prompt vRun, "Run number:" Prompt strSuffix1, "Case 1:" Prompt strSuffix2, "Case 2:" Prompt strSuffix3, "Case 3:" DoPrompt "Enter cases to process", vRun, strSuffix1, strSuffix2, strSuffix3 If(V_flag == 1 || strlen(strSuffix1) == 0) Abort EndIf EndIf Wave/T twModelSpecies_1 = $("root:Sens:" + strSuffix1 + ":BoxModelOut_Run" + num2str(vRun) + "_Last:twModelSpecies") Wave ChangeInConc_1 = $("root:Sens:" + strSuffix1 + ":BoxModelOut_Run" + num2str(vRun) + "_Last:ChangeInConc") Wave ChangeInConc_2 = $("root:Sens:" + strSuffix2 + ":BoxModelOut_Run" + num2str(vRun) + "_Last:ChangeInConc") Wave ChangeInConc_3 = $("root:Sens:" + strSuffix3 + ":BoxModelOut_Run" + num2str(vRun) + "_Last:ChangeInConc") Wave FracChangeInConc_1 = $("root:Sens:" + strSuffix1 + ":BoxModelOut_Run" + num2str(vRun) + "_Last:FracChangeInConc") Wave FracChangeInConc_2 = $("root:Sens:" + strSuffix2 + ":BoxModelOut_Run" + num2str(vRun) + "_Last:FracChangeInConc") Wave FracChangeInConc_3 = $("root:Sens:" + strSuffix3 + ":BoxModelOut_Run" + num2str(vRun) + "_Last:FracChangeInConc") String strPlotName = "Compare_Model_Change_" + strSuffix1 + "_" + strSuffix2 + "_" + strSuffix3 DoWindow/K/Z $strPlotName Display as strPlotName DoWindow/C $strPlotName AppendToGraph/C=(54741,24158,0) ChangeInConc_1 vs twModelSpecies_1 AppendToGraph/C=(0,40606,29555) ChangeInConc_2 vs twModelSpecies_1 If(strlen(strSuffix3) > 0) AppendToGraph/C=(0,29298,45746) ChangeInConc_3 vs twModelSpecies_1 EndIf ModifyGraph tkLblRot(bottom)=60 strPlotName = "Compare_Model_FracChange_" + strSuffix1 + "_" + strSuffix2 + "_" + strSuffix3 DoWindow/K/Z $strPlotName Display as strPlotName DoWindow/C $strPlotName AppendToGraph/C=(54741,24158,0) FracChangeInConc_1 vs twModelSpecies_1 AppendToGraph/C=(0,40606,29555) FracChangeInConc_2 vs twModelSpecies_1 If(strlen(strSuffix3) > 0) AppendToGraph/C=(0,29298,45746) FracChangeInConc_3 vs twModelSpecies_1 EndIf ModifyGraph tkLblRot(bottom)=60 End //Compare all models Function ConcatenateModelAndObs() SetDataFolder root: //Get the total number of runs String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "Jan*", ";") strFolderList = SortList(strFolderList) Variable vNumFolders = ItemsInList(strFolderList, ";") String strSpecies = "Cl2;Br2;BrCl;HClpCl;Ox;NOy;HNO3pNO3;NOx" strSpecies += ";ClNO2;BrNO2;HONO;N2O5;HOCl;HOBr;HOBr_Upper;HOBr_Lower;HOCl_Upper;HOCl_Lower" Make/O/N=2 OneToOne = p*500 + 1e-12 Variable iFolderDex, iSpeciesDex String strRun For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpecies); iSpeciesDex += 1) String strCurrentSpecies = StringFromList(iSpeciesDex, strSpecies) Make/O/N=0 $("root:Model_" + strCurrentSpecies)/WAVE=wModel_Concat Make/O/N=0 $("root:Obs_" + strCurrentSpecies)/WAVE=wObs_Concat For(iFolderDex = 0; iFolderDex < vNumFolders; iFolderDex += 1) String strCurrentFolder = StringFromList(iFolderDex, strFolderList) If(StringMatch(strCurrentFolder, "*_*")) strRun = StringFromList(1, strCurrentFolder, "_") Else strRun = "1" EndIf Wave wModel = $("root:" + StringFromList(iFolderDex, strFolderList) + ":BoxModelOut_Run" + strRun + "_Puff:Model_" + strCurrentSpecies) Wave wObs = $("root:" + StringFromList(iFolderDex, strFolderList) + ":BoxModel_Run" + strRun + "_Puff:Obs_" + strCurrentSpecies) Concatenate/NP {wModel}, wModel_Concat Concatenate/NP {wObs}, wObs_Concat EndFor // CurveFit/Q/ODR=2/L=100 line, wModel_Concat/X=wObs_Concat/D // Wave W_Coef // Make/O/N= EndFor End //Make generic run Function SetUpGeneric() SetDataFolder root: Make/O/N=1 wUSMag_Lat, wUSMag_Lon //Make wave to hold the information about where the US Magnesium plant is wUSMag_Lat[0] = 40.9155 wUSMag_Lon[0] = -112.7337 String strRun = "1" String strFolder = "root:BoxModel_Run" + strRun + "_Last" NewDataFolder/O $strFolder Variable vNumHours = 6 //Number of hours to run Variable vStartHour = 9 //Starting hour, 8am Variable vStartTime = Date2Secs(2017,01,25) // Variable vStartTime = Date2Secs(2017,07,25) Variable vIntTime = 10 Variable vNumPnts = vNumHours*3600 / vIntTime Make/O/N=(vNumPnts)/D $(strFolder + ":Timewave_MST")/WAVE=Timewave_MST Timewave_MST = vStartTime + vStartHour*3600 + p*vIntTime Make/O/N=(vNumPnts) $(strFolder + ":Met_SZA_Last")/WAVE=wMet_SZA wMet_SZA = SZA_Calc(wUSMag_Lat[0]*pi/180, wUSMag_Lon[0]*pi/180, 2017, Timewave_MST[p], 0) //Calculate SZA based on interpolated time String strBasePath = "C:Users:cwomack:Documents:Research Projects:UWFPS:Halogens Analysis:F0AM:" String strPath = strBasePath + "OutputConc:General:" NewPath/O/Q/C pSavePath, strPath strPath = strBasePath + "InputDAQ:General:" NewPath/O/Q/C pSavePath, strPath strPath = strBasePath + "OutputConc:General:Run" + strRun + ":" NewPath/O/Q/C pSavePath, strPath strPath = strBasePath + "InputDAQ:General:Run" + strRun + ":" NewPath/O/Q/C pSavePath, strPath // String strFolder = "root:BoxModel_Run" + strRun + "_Last" //Then save the met parameter waves for the "last" model run strPath = strBasePath + "InputDAQ:General:Run" + strRun + ":Plume_Last" NewPath/O/Q/C pSavePath, strPath // For(iDex = 0; iDex < ItemsInList(strMetParams); iDex += 1) // Wave wMet = $(strFolder + ":Met_" + StringFromList(iDex, strMetParams) + "_Last") Save/G/M="\r\n"/O/P=pSavePath wMet_SZA as "Met_SZA.dat" // EndFor End Function LoadGeneric() //Load the F0AM output for the last model. Very similar to the puff model version SetDataFolder root: String strBasePath = "C:Users:cwomack:Documents:Research Projects:UWFPS:Halogens Analysis:F0AM:" // Wave/T twFlightName // String strCurrentFlight = twFlightName[0] String strCurrentDate = "General" String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "BoxModel_Run*Last", ";") Variable vNumRuns = ItemsInList(strFolderList, ";") Variable iRunDex, iPlumeDex, iSpeciesDex String strWholeLine Variable vLineLength, vRef For(iRunDex = 1; iRunDex <= vNumRuns; iRunDex += 1) String strFolder = "root:BoxModelOut_Run" + num2str(iRunDex) + "_Last" NewDataFolder/O/S $(strFolder) Wave wInt_Time = $("root:BoxModel_Run" + num2str(iRunDex) + "_Last" + ":Int_Time") Wave wObs_Age_Sec = $("root:BoxModel_Run" + num2str(iRunDex) + "_Last" + ":Obs_Age_sec") Variable vNumPlumes = numpnts(wInt_Time) //Get the names of the species (Cnames) Make/O/N=0/T twCnames, twRnames, twInitNames Open/R vRef as strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":Cnames.txt" Do //Reads each line from the template. If we need to change the line (usually means we need to change a path), then print the new line. Otherwise print the line from the template. FReadLine vRef, strWholeLine vLineLength = strlen(strWholeLine) If(vLineLength > 0) InsertPoints/M=0 numpnts(twCnames), 1, twCnames twCnames[numpnts(twCnames) - 1] = RemoveEnding(strWholeLine, "\r") EndIf While(vLineLength > 0) Close vRef //Get the names of the reactions (Rnames) Open/R vRef as strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":Rnames.txt" Do //Reads each line from the template. If we need to change the line (usually means we need to change a path), then print the new line. Otherwise print the line from the template. FReadLine vRef, strWholeLine vLineLength=strlen(strWholeLine) If(vLineLength > 0) InsertPoints/M=0 numpnts(twRnames), 1, twRnames twRnames[numpnts(twRnames) - 1] = RemoveEnding(strWholeLine, "\r") EndIf While(vLineLength > 0) Close vRef //Get the names of the reactions (twInitNames) Open/R vRef as strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":InitNames.txt" Do //Reads each line from the template. If we need to change the line (usually means we need to change a path), then print the new line. Otherwise print the line from the template. FReadLine vRef, strWholeLine vLineLength=strlen(strWholeLine) If(vLineLength > 0) InsertPoints/M=0 numpnts(twInitNames), 1, twInitNames twInitNames[numpnts(twInitNames) - 1] = RemoveEnding(strWholeLine, "\r") EndIf While(vLineLength > 0) Close vRef String strVOCs = "Acid;Alkene;Aromatic;Aldehyde;Alcohol;AlkoxyRadical;Ketone;PeroxyNitrate;PeroxyRadical;Peroxide;PeroxyAcid" For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strVOCs); iSpeciesDex += 1) Make/O/N=0/T $("tw" + StringFromList(iSpeciesDex, strVOCs))/WAVE=twVOCNames Open/R vRef as strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":" + StringFromList(iSpeciesDex, strVOCs) + "Names.txt" Do //Reads each line from the template. If we need to change the line (usually means we need to change a path), then print the new line. Otherwise print the line from the template. FReadLine vRef, strWholeLine vLineLength=strlen(strWholeLine) If(vLineLength > 0) InsertPoints/M=0 numpnts(twVOCNames), 1, twVOCNames twVOCNames[numpnts(twVOCNames) - 1] = RemoveEnding(strWholeLine, "\r") EndIf While(vLineLength > 0) Close vRef EndFor MLLoadWave/O/Q/Y=4/S=2/N=$("iRO2") strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":iRO2.mat" Wave iRO2 iRO2 -= 1 //Get the rates, concentrations, and dilution matrices //Create the time waves based on the age and the integration time MLLoadWave/O/Q/M=2/Y=4/S=2/N=$("Model_Conc_Last") strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":Conc_Last.mat" MLLoadWave/O/Q/M=2/Y=4/S=2/N=$("Model_Rate_Last") strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":Rate_Last.mat" MLLoadWave/O/Q/M=2/Y=4/S=2/N=$("Model_Dil_Last") strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":Dil_Last.mat" Wave Model_output = $(strFolder + ":Model_Conc_Last") Make/O/N=(DimSize(Model_output, 0)) $(strFolder + ":Model_time_Last_sec")/WAVE=Model_time_sec Make/O/N=(DimSize(Model_output, 0)) $(strFolder + ":Model_time_Last_min")/WAVE=Model_time_min Model_time_sec = p*10 Model_time_min = Model_time_sec/60 MLLoadWave/O/Q/M=2/Y=4/S=2/N=$("Model_InitConc") strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":InitConc.mat" //Finally, load the matrix of stoichiometric coefficients for each reaction MLLoadWave/O/Q/M=2/Y=4/S=2/N=$("Stoic") strBasePath + "OutputConc:" + strCurrentDate + ":Run" + num2str(iRunDex) + ":Stoic.mat" EndFor End //Making paper figures Function MakeReactivityFig() SetDataFolder root: ColorTab2Wave Rainbow Wave M_Colors String strSpecies = "O3;BR;CL" Variable iSpeciesDex, iRunDex = 1, iDex, vNumReacts, vColor Wave wModel_Time = $("root:BoxModelOut_Run" + num2str(iRunDex) + "_Last:Model_Time_Last_min") For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpecies); iSpeciesDex += 1) String strFolder = "root:BoxModelOut_Run" + num2str(iRunDex) + "_Last:" + StringFromList(iSpeciesDex, strSpecies) + ":" Wave wTopLoss = $(strFolder + StringFromList(iSpeciesDex, strSpecies) + "_TopLossRates_Last") Wave wChainLength = $(strFolder + StringFromList(iSpeciesDex, strSpecies) + "_ChainLength_Last") Wave/T twTopLossNames = $(strFolder + StringFromList(iSpeciesDex, strSpecies) + "_TopLossReactNames_Last") Make/O/N=(DimSize(wTopLoss, 0), DimSize(wTopLoss, 1)) $(strFolder + StringFromList(iSpeciesDex, strSpecies) + "_TopLossRates_Last_Neg")/WAVE=wTopLoss_Neg wTopLoss_Neg = -1*wTopLoss vNumReacts = DimSize(wTopLoss, 1) String strPlotName = "Losses_" + StringFromList(iSpeciesDex, strSpecies) DoWindow/K $strPlotName Display as strPlotName DoWindow/C $strPlotName String strLegend = "\Z08" String strReactName For(iDex = 0; iDex < vNumReacts; iDex += 1) String strTraceName = "React" + num2str(iDex) strReactName = twTopLossNames[iDex] strReactName = ReplaceString("BR", strReactName, "Br") strReactName = ReplaceString("CL", strReactName, "Cl") strReactName = ReplaceString("2", strReactName, "\B2\M\Z08") strReactName = ReplaceString("3", strReactName, "\B3\M\Z08") strReactName = ReplaceString("4", strReactName, "\B4\M\Z08") strReactName = ReplaceString("6", strReactName, "\B6\M\Z08") strLegend += "\s(" + strTraceName + ") " + strReactName + "\r" vColor = iDex / (vNumReacts - 1) * 99 AppendToGraph wTopLoss_Neg[][iDex]/TN=$strTraceName vs wModel_Time ModifyGraph rgb($strTraceName) = (M_Colors[vColor][0], M_Colors[vColor][1], M_Colors[vColor][2]) ModifyGraph mode($strTraceName)=7, hbFill($strTraceName)=2, toMode($strTraceName)=2 EndFor If(WaveExists(wChainLength)) AppendToGraph/L=L2/C=(0,0,0) wChainLength vs wModel_time ModifyGraph axisEnab(left)={0,0.8},axisEnab(L2)={0.82,1},freePos(L2)={0,bottom} Label L2, "Chain length" ModifyGraph lblPosMode(L2)=1 SetAxis L2, 0, * EndIf strLegend = RemoveEnding(strLegend, "\r") Legend/A=RT strLegend Label left, StringFromList(iSpeciesDex, strSpecies) + " loss (ppb/hr)" Label bottom, "Plume age (min)" ModifyGraph log(bottom) = 1, standoff=0, mirror = 2 EndFor KillWaves/Z M_Colors End Function PlotModelVsObs() SetDataFolder root: String strSpecies = "Cl2;Br2;BrCl;HClpCl;Ox;NOy" strSpecies += ";ClNO2;BrNO2;HONO;N2O5;HOCl;HOBr" String strLOD = "0.0004;0.0003;0.0002;0.256;0;0.1" //HClpCl LOD comes from 0.15 ug/m3 --> 0.096 ppb from AMS pCl and 0.16 ppb from CIMS HCl strLOD += ";0.0004;0.0001;0.2;0.0002;0.005;0.0018" String strMarkerOrder = "19;16;17;23;26;29;14;15" Make/O/N=(9,3) ColorPalatte ColorPalatte[0][0] = 54741; ColorPalatte[0][1] = 24158; ColorPalatte[0][2] = 0 ColorPalatte[1][0] = 59110; ColorPalatte[1][1] = 40863; ColorPalatte[1][2] = 0 ColorPalatte[2][0] = 61680; ColorPalatte[2][1] = 58596; ColorPalatte[2][2] = 16962 ColorPalatte[3][0] = 0; ColorPalatte[3][1] = 40606; ColorPalatte[3][2] = 29555 ColorPalatte[4][0] = 22102; ColorPalatte[4][1] = 46260; ColorPalatte[4][2] = 59881 ColorPalatte[5][0] = 0; ColorPalatte[5][1] = 29298; ColorPalatte[5][2] = 45746 ColorPalatte[6][0] = 52428; ColorPalatte[6][1] = 31097; ColorPalatte[6][2] = 42919 ColorPalatte[7][0] = 0; ColorPalatte[7][1] = 0; ColorPalatte[7][2] = 0 ColorPalatte[8][0] = 43690; ColorPalatte[8][1] = 43690; ColorPalatte[8][2] = 43690 Wave OneToOne ColorTab2Wave Rainbow Wave M_Colors String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "Jan*", ";") strFolderList = SortList(strFolderList) Variable vNumFolders = ItemsInList(strFolderList, ";") Variable iSpeciesDex, iFolderDex, vColor String strRun String strLegend = "\Z08" For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpecies); iSpeciesDex += 1) String strCurrentSpecies = StringFromList(iSpeciesDex, strSpecies) Wave wModel = $("root:Model_" + strCurrentSpecies) Wave wObs = $("root:Obs_" + strCurrentSpecies) Make/O/N=(numpnts(wModel))/FREE TempWave1 TempWave1 = wModel[p] == 0 ? NaN : wModel[p] WaveTransform zapNaNs TempWave1 Make/O/N=(numpnts(wObs))/FREE TempWave2 TempWave2 = wObs[p] == 0 ? NaN : wObs[p] WaveTransform zapNaNs TempWave2 Make/O/N=(numpnts(wModel))/FREE wMask wMask = wModel[p] <= str2num(StringFromList(iSpeciesDex, strLOD)) ? NaN : 1 Variable vLowerBound = Min(WaveMin(TempWave1), WaveMin(TempWave2)) Variable vUpperBound = Max(WaveMax(TempWave1), WaveMax(TempWave2)) String strPlotName = "ModelvObs_" + strCurrentSpecies DoWindow/K $strPlotName Display as strPlotName DoWindow/C $strPlotName Make/O/N=2 $("LOD_" + strCurrentSpecies)/WAVE=LOD LOD = str2num(StringFromList(iSpeciesDex, strLOD)) Make/O/N=2 ZeroLine = 1e-12 AppendToGraph LOD/TN=LOD_Model vs OneToOne AppendToGraph OneToOne/TN=LOD_Obs vs LOD AppendToGraph OneToOne/TN=ZeroLine vs ZeroLine ModifyGraph rgb(LOD_Model) = (56797,56797,56797), rgb(LOD_Obs)=(56797,56797,56797) ModifyGraph mode(LOD_Obs)=7,lstyle(LOD_Obs)=3//,hbFill(LOD_Obs)=5, toMode(LOD_Obs)=1 ModifyGraph mode(LOD_Model)=7,lstyle(LOD_Model)=3//,hbFill(LOD_Model)=5 AppendToGraph wModel/TN=$strCurrentSpecies vs wObs ModifyGraph mode($strCurrentSpecies) = 3, marker($strCurrentSpecies) = 19 AppendToGraph OneToOne vs OneToOne ModifyGraph rgb(OneToOne) = (0,0,0), lstyle(OneToOne) = 3 SetAxis left vLowerBound, vUpperBound SetAxis bottom vLowerBound, vUpperBound If(StringMatch(strCurrentSpecies, "Ox") == 0) Make/O/N=(numpnts(wModel))/FREE TempLogModel, TempLogObs TempLogModel = log(wModel) TempLogModel = wModel[p] < str2num(StringFromList(iSpeciesDex, strLOD)) ? NaN : TempLogModel[p] TempLogObs = log(wObs) Make/O/N=100 $("root:Model_" + strCurrentSpecies + "_fitX")/WAVE=wFitX Make/O/N=100 $("root:Model_" + strCurrentSpecies + "_fit")/WAVE=wFit WaveStats/Q TempLogModel wFitX = V_min + p*(V_max - V_min)/100 wFitX = 10^(wFitX[p]) CurveFit/Q line, TempLogModel/X=TempLogObs Variable vCoef = V_r2 Wave W_Coef wFit = 10^W_Coef[0]*wFitX[p]^(W_coef[1]) AppendToGraph/C=(0,0,0) wFit vs wFitX Else CurveFit/Q/L=800 line, wModel/M=wMask/X=wObs/D Wave W_Coef vCoef = V_r2 CurveFit/Q/L=800/ODR=2 line, wModel/M=wMask/X=wObs/D ModifyGraph rgb($("fit_Model_" + strCurrentSpecies)) = (0,0,0) EndIf String strLabelSpecies = ReplaceString("2", strCurrentSpecies, "\B2\M\Z08") strLabelSpecies = ReplaceString("5", strLabelSpecies, "\B5\M\Z08") strLabelSpecies = ReplaceString("x", strLabelSpecies, "\Bx\M\Z08") strLabelSpecies = ReplaceString("y", strLabelSpecies, "\By\M\Z08") strLabelSpecies = ReplaceString("p", strLabelSpecies, " + p") strLabelSpecies = ReplaceString("Jan", strLabelSpecies, "Jan ") strLabelSpecies = ReplaceString("_", strLabelSpecies, " #") String strTextBox = "\Z08" + strLabelSpecies + "\rSlope=" + num2str(round(W_Coef[1]*100)/100) + "\rr\S2\M\Z08=" + num2str(round(vCoef*100)/100) If(StringMatch(strCurrentSpecies, "Ox") == 0) ModifyGraph log = 1 EndIf For(iFolderDex = 0; iFolderDex < vNumFolders; iFolderDex += 1) String strCurrentFolder = StringFromList(iFolderDex, strFolderList) If(StringMatch(strCurrentFolder, "*_*")) strRun = StringFromList(1, strCurrentFolder, "_") Else strRun = "1" EndIf Wave wModel = $("root:" + StringFromList(iFolderDex, strFolderList) + ":BoxModelOut_Run" + strRun + "_Puff:Model_" + strCurrentSpecies) Wave wObs = $("root:" + StringFromList(iFolderDex, strFolderList) + ":BoxModel_Run" + strRun + "_Puff:Obs_" + strCurrentSpecies) AppendToGraph/C=(0,0,0) wModel/TN=$strCurrentFolder vs wObs ModifyGraph mode($strCurrentFolder) = 3//, marker($strCurrentFolder) = 19 ModifyGraph marker($strCurrentFolder) = str2num(StringFromList(iFolderDex, strMarkerOrder)) // vColor = iFolderDex / (vNumFolders - 1) * 99 ModifyGraph rgb($strCurrentFolder) = (ColorPalatte[iFolderDex][0], ColorPalatte[iFolderDex][1], ColorPalatte[iFolderDex][2]) If(iSpeciesDex == 0) strLegend += "\s(" + strCurrentFolder + ") " + strCurrentFolder + "\r" EndIf EndFor strLegend = RemoveEnding(strLegend, "\r") Legend/A=LT/F=0 strTextBox If(iSpeciesDex == 0) Legend/A=RB/F=0 strLegend EndIf RemoveFromGraph $strCurrentSpecies ModifyGraph mirror=2,standoff=0 Label left "Modeled " + strLabelSpecies + " (ppb)" Label bottom "Observed " + strLabelSpecies + " (ppb)" ModifyGraph lblMargin(left)=10,lblMargin(bottom)=5,tlOffset(left)=-1,tlOffset(bottom)=-6 ModifyGraph width=200,height=175 EndFor KillWaves/Z M_Colors End Function PlotModelVsObs_HOX() SetDataFolder root: String strSpecies = "HOCl;HOBr" String strLOD = "0.005;0.0018" String strMarkerOrder = "19;16;17;23;26;29;14;15" Make/O/N=(9,3) ColorPalatte ColorPalatte[0][0] = 54741; ColorPalatte[0][1] = 24158; ColorPalatte[0][2] = 0 ColorPalatte[1][0] = 59110; ColorPalatte[1][1] = 40863; ColorPalatte[1][2] = 0 ColorPalatte[2][0] = 61680; ColorPalatte[2][1] = 58596; ColorPalatte[2][2] = 16962 ColorPalatte[3][0] = 0; ColorPalatte[3][1] = 40606; ColorPalatte[3][2] = 29555 ColorPalatte[4][0] = 22102; ColorPalatte[4][1] = 46260; ColorPalatte[4][2] = 59881 ColorPalatte[5][0] = 0; ColorPalatte[5][1] = 29298; ColorPalatte[5][2] = 45746 ColorPalatte[6][0] = 52428; ColorPalatte[6][1] = 31097; ColorPalatte[6][2] = 42919 ColorPalatte[7][0] = 0; ColorPalatte[7][1] = 0; ColorPalatte[7][2] = 0 ColorPalatte[8][0] = 43690; ColorPalatte[8][1] = 43690; ColorPalatte[8][2] = 43690 Wave OneToOne ColorTab2Wave Rainbow Wave M_Colors String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "Jan*", ";") strFolderList = SortList(strFolderList) Variable vNumFolders = ItemsInList(strFolderList, ";") Variable iSpeciesDex, iFolderDex, vColor String strRun String strLegend = "\Z08" For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpecies); iSpeciesDex += 1) String strCurrentSpecies = StringFromList(iSpeciesDex, strSpecies) Wave wModel = $("root:Model_" + strCurrentSpecies) Wave wObs_Upper = $("root:Obs_" + strCurrentSpecies + "_Upper") Wave wObs_Lower = $("root:Obs_" + strCurrentSpecies + "_Lower") // Make/O/N=(numpnts(wModel))/FREE TempWave1 // TempWave1 = wModel[p] == 0 ? NaN : wModel[p] // WaveTransform zapNaNs TempWave1 // Make/O/N=(numpnts(wObs))/FREE TempWave2 // TempWave2 = wObs[p] == 0 ? NaN : wObs[p] // WaveTransform zapNaNs TempWave2 Make/O/N=(numpnts(wModel))/FREE wMask wMask = wModel[p] <= str2num(StringFromList(iSpeciesDex, strLOD)) ? NaN : 1 // Variable vLowerBound = Min(WaveMin(TempWave1), WaveMin(TempWave2)) // Variable vUpperBound = Max(WaveMax(TempWave1), WaveMax(TempWave2)) Make/O/N=2 $("LOD_" + strCurrentSpecies)/WAVE=LOD LOD = str2num(StringFromList(iSpeciesDex, strLOD)) Make/O/N=2 ZeroLine = 1e-12 //Make upper plot String strPlotName = "ModelvObs_" + strCurrentSpecies + "_Upper" DoWindow/K $strPlotName Display as strPlotName DoWindow/C $strPlotName AppendToGraph LOD/TN=LOD_Model vs OneToOne AppendToGraph OneToOne/TN=LOD_Obs vs LOD // AppendToGraph OneToOne/TN=ZeroLine vs ZeroLine ModifyGraph rgb(LOD_Model) = (56797,56797,56797), rgb(LOD_Obs)=(56797,56797,56797) ModifyGraph mode(LOD_Obs)=7,lstyle(LOD_Obs)=3//,hbFill(LOD_Obs)=5, toMode(LOD_Obs)=1 ModifyGraph mode(LOD_Model)=7,lstyle(LOD_Model)=3//,hbFill(LOD_Model)=5 AppendToGraph wModel/TN=$strCurrentSpecies vs wObs_Upper ModifyGraph mode($strCurrentSpecies) = 3, marker($strCurrentSpecies) = 19 AppendToGraph OneToOne vs OneToOne ModifyGraph rgb(OneToOne) = (0,0,0), lstyle(OneToOne) = 3 // SetAxis left vLowerBound, vUpperBound // SetAxis bottom vLowerBound, vUpperBound Make/O/N=(numpnts(wModel))/FREE TempLogModel, TempLogObs TempLogModel = log(wModel) TempLogModel = wModel[p] < str2num(StringFromList(iSpeciesDex, strLOD)) ? NaN : TempLogModel[p] TempLogObs = log(wObs_Upper) Make/O/N=100 $("root:Model_" + strCurrentSpecies + "_Upper_fitX")/WAVE=wFitX Make/O/N=100 $("root:Model_" + strCurrentSpecies + "_Upper_fit")/WAVE=wFit WaveStats/Q TempLogModel wFitX = V_min + p*(V_max - V_min)/100 wFitX = 10^(wFitX[p]) CurveFit/Q line, TempLogModel/X=TempLogObs Variable vCoef = V_r2 Wave W_Coef wFit = 10^W_Coef[0]*wFitX[p]^(W_coef[1]) AppendToGraph/C=(0,0,0) wFit vs wFitX String strLabelSpecies = strCurrentSpecies // String strLabelSpecies = ReplaceString("Jan", strCurrentSpecies, "Jan ") // strLabelSpecies = ReplaceString("_", strLabelSpecies, " #") String strTextBox = "\Z08" + strLabelSpecies + "\rSlope=" + num2str(round(W_Coef[1]*100)/100) + "\rr\S2\M\Z08=" + num2str(round(vCoef*100)/100) // If(StringMatch(strCurrentSpecies, "Ox") == 0) ModifyGraph log = 1 // EndIf For(iFolderDex = 0; iFolderDex < vNumFolders; iFolderDex += 1) String strCurrentFolder = StringFromList(iFolderDex, strFolderList) If(StringMatch(strCurrentFolder, "*_*")) strRun = StringFromList(1, strCurrentFolder, "_") Else strRun = "1" EndIf Wave wModel_Flight = $("root:" + StringFromList(iFolderDex, strFolderList) + ":BoxModelOut_Run" + strRun + "_Puff:Model_" + strCurrentSpecies) Wave wObs_Flight_Upper = $("root:" + StringFromList(iFolderDex, strFolderList) + ":BoxModel_Run" + strRun + "_Puff:Obs_" + strCurrentSpecies + "_Upper") AppendToGraph/C=(0,0,0) wModel_Flight/TN=$strCurrentFolder vs wObs_Flight_Upper ModifyGraph mode($strCurrentFolder) = 3//, marker($strCurrentFolder) = 19 ModifyGraph marker($strCurrentFolder) = str2num(StringFromList(iFolderDex, strMarkerOrder)) // vColor = iFolderDex / (vNumFolders - 1) * 99 ModifyGraph rgb($strCurrentFolder) = (ColorPalatte[iFolderDex][0], ColorPalatte[iFolderDex][1], ColorPalatte[iFolderDex][2]) If(iSpeciesDex == 0) strLegend += "\s(" + strCurrentFolder + ") " + strCurrentFolder + "\r" EndIf EndFor strLegend = RemoveEnding(strLegend, "\r") Legend/A=LT/F=0 strTextBox If(iSpeciesDex == 0) Legend/A=RB/F=0 strLegend EndIf RemoveFromGraph $strCurrentSpecies ModifyGraph mirror=2,standoff=0 Label left "Modeled " + strLabelSpecies + " (ppb)" Label bottom "Observed " + strLabelSpecies + " (ppb)" ModifyGraph lblMargin(left)=10,lblMargin(bottom)=5,tlOffset(left)=-1,tlOffset(bottom)=-6 ModifyGraph width=200,height=175 //Make lower plot strPlotName = "ModelvObs_" + strCurrentSpecies + "_Lower" DoWindow/K $strPlotName Display as strPlotName DoWindow/C $strPlotName AppendToGraph LOD/TN=LOD_Model vs OneToOne AppendToGraph OneToOne/TN=LOD_Obs vs LOD // AppendToGraph OneToOne/TN=ZeroLine vs ZeroLine ModifyGraph rgb(LOD_Model) = (56797,56797,56797), rgb(LOD_Obs)=(56797,56797,56797) ModifyGraph mode(LOD_Obs)=7,lstyle(LOD_Obs)=3//,hbFill(LOD_Obs)=5, toMode(LOD_Obs)=1 ModifyGraph mode(LOD_Model)=7,lstyle(LOD_Model)=3//,hbFill(LOD_Model)=5 AppendToGraph wModel/TN=$strCurrentSpecies vs wObs_Lower ModifyGraph mode($strCurrentSpecies) = 3, marker($strCurrentSpecies) = 19 AppendToGraph OneToOne vs OneToOne ModifyGraph rgb(OneToOne) = (0,0,0), lstyle(OneToOne) = 3 // SetAxis left vLowerBound, vUpperBound // SetAxis bottom vLowerBound, vUpperBound Make/O/N=(numpnts(wModel))/FREE TempLogModel, TempLogObs TempLogModel = log(wModel) TempLogModel = wModel[p] < str2num(StringFromList(iSpeciesDex, strLOD)) ? NaN : TempLogModel[p] TempLogObs = log(wObs_Lower) Make/O/N=100 $("root:Model_" + strCurrentSpecies + "_Lower_fitX")/WAVE=wFitX Make/O/N=100 $("root:Model_" + strCurrentSpecies + "_Lower_fit")/WAVE=wFit WaveStats/Q TempLogModel wFitX = V_min + p*(V_max - V_min)/100 wFitX = 10^(wFitX[p]) CurveFit/Q line, TempLogModel/X=TempLogObs vCoef = V_r2 Wave W_Coef wFit = 10^W_Coef[0]*wFitX[p]^(W_coef[1]) AppendToGraph/C=(0,0,0) wFit vs wFitX strLabelSpecies = strCurrentSpecies // String strLabelSpecies = ReplaceString("Jan", strCurrentSpecies, "Jan ") // strLabelSpecies = ReplaceString("_", strLabelSpecies, " #") strTextBox = "\Z08" + strLabelSpecies + "\rSlope=" + num2str(round(W_Coef[1]*100)/100) + "\rr\S2\M\Z08=" + num2str(round(vCoef*100)/100) // If(StringMatch(strCurrentSpecies, "Ox") == 0) ModifyGraph log = 1 // EndIf For(iFolderDex = 0; iFolderDex < vNumFolders; iFolderDex += 1) strCurrentFolder = StringFromList(iFolderDex, strFolderList) If(StringMatch(strCurrentFolder, "*_*")) strRun = StringFromList(1, strCurrentFolder, "_") Else strRun = "1" EndIf Wave wModel_Flight = $("root:" + StringFromList(iFolderDex, strFolderList) + ":BoxModelOut_Run" + strRun + "_Puff:Model_" + strCurrentSpecies) Wave wObs_Flight_Lower = $("root:" + StringFromList(iFolderDex, strFolderList) + ":BoxModel_Run" + strRun + "_Puff:Obs_" + strCurrentSpecies + "_Lower") AppendToGraph/C=(0,0,0) wModel_Flight/TN=$strCurrentFolder vs wObs_Flight_Lower ModifyGraph mode($strCurrentFolder) = 3//, marker($strCurrentFolder) = 19 ModifyGraph marker($strCurrentFolder) = str2num(StringFromList(iFolderDex, strMarkerOrder)) // vColor = iFolderDex / (vNumFolders - 1) * 99 ModifyGraph rgb($strCurrentFolder) = (ColorPalatte[iFolderDex][0], ColorPalatte[iFolderDex][1], ColorPalatte[iFolderDex][2]) // If(iSpeciesDex == 0) // strLegend += "\s(" + strCurrentFolder + ") " + strCurrentFolder + "\r" // EndIf EndFor // strLegend = RemoveEnding(strLegend, "\r") Legend/A=LT/F=0 strTextBox // If(iSpeciesDex == 0) // Legend/A=RB/F=0 strLegend // EndIf RemoveFromGraph $strCurrentSpecies ModifyGraph mirror=2,standoff=0 Label left "Modeled " + strLabelSpecies + " (ppb)" Label bottom "Observed " + strLabelSpecies + " (ppb)" ModifyGraph lblMargin(left)=10,lblMargin(bottom)=5,tlOffset(left)=-1,tlOffset(bottom)=-6 ModifyGraph width=200,height=175 EndFor KillWaves/Z M_Colors End Function CalculateHalVsNoHal(strSuffix1, strSuffix2) String strSuffix1, strSuffix2 SetDataFolder root: String strFolder = "root:Model_Comparison_" + strSuffix1 + "_" + strSuffix2 String strSpecies = "Alkane;Alkene;Alkyne;Alcohol;Aromatic;Acid;Aldehyde;Ketone;HO2;RO2;ROOH;HNO3pNO3" Make/O/N=(ItemsInList(strSpecies))/T $(strFolder + ":twVOCs")/WAVE=twVOCs twVOCs = StringFromList(p, strSpecies) Make/O/N=(ItemsInList(strSpecies)) $(strFolder + ":Enhancement")/WAVE=Enhancement Make/O/N=(ItemsInList(strSpecies)) $(strFolder + ":IncreasePercent")/WAVE=IncreasePercent Wave twPrimVOCs = $(strFolder + ":twPrimVOCs") Wave wPrimVOCs_Hal = $(strFolder + ":wPrimVOCs_" + strSuffix1) Wave wPrimVOCs_NoHal = $(strFolder + ":wPrimVOCs_" + strSuffix2) Variable iSpeciesDex For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpecies); iSpeciesDex += 1) Wave wHal = $(strFolder + ":w" + StringFromList(iSpeciesDex, strSpecies) + "_" + strSuffix1) Wave wNoHal = $(strFolder + ":w" + StringFromList(iSpeciesDex, strSpecies) + "_" + strSuffix2) If(WaveExists(wHal)) Enhancement[iSpeciesDex] = wHal[0] / wNoHal[0] IncreasePercent[iSpeciesDex] = 100*(wHal[0] - wNoHal[0]) / wNoHal[0] Else FindValue/TEXT=(StringFromList(iSpeciesDex, strSpecies))/TXOP=4 twPrimVOCs Enhancement[iSpeciesDex] = wPrimVOCs_Hal[V_Value] / wPrimVOCs_NoHal[V_Value] IncreasePercent[iSpeciesDex] = 100*(wPrimVOCs_Hal[V_Value] - wPrimVOCs_NoHal[V_Value]) / wPrimVOCs_NoHal[V_Value] EndIf EndFor End Function CompareChangeInVOCs(iRunDex, strCases) Variable iRunDex String strCases SetDataFolder root: ColorTab2Wave Rainbow Wave M_Colors Variable iDex, iSpeciesDex String strCasesFullNames = "_" For(iDex = 0; iDex < ItemsInList(strCases); iDex += 1) strCasesFullNames += StringFromList(iDex, strCases) + "_" EndFor strCasesFullNames = RemoveEnding(strCasesFullNames, "_") //First, compare the hydrocarbons String strSpecies = "Alkane;Alkene;Alkyne;Alcohol;Aromatic;Acid;Aldehyde;Ketone" String strNewFolder = "root:Model_Comparison" + strCasesFullNames NewDataFolder/O $(strNewFolder) Make/O/N=(ItemsInList(strSpecies))/T $(strNewFolder + ":twPrimVOCs")/WAVE=twPrimVOCs twPrimVOCs = StringFromList(p, strSpecies) For(iDex = 0; iDex < ItemsInList(strCases); iDex += 1) String strFolder = "root:Sens:" + StringFromList(iDex, strCases) + ":BoxModelOut_Run" + num2str(iRunDex) + "_Last" Make/O/N=(ItemsInList(strSpecies)) $(strNewFolder + ":wPrimVOCs_" + StringFromList(iDex, strCases))/WAVE=wPrimVOCs Wave/T twModelSpecies = $(strFolder + ":twModelSpecies") Wave ChangeInConc = $(strFolder + ":ChangeInConc") For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpecies); iSpeciesDex += 1) FindValue/TEXT=("TotalVOC_" + StringFromList(iSpeciesDex, strSpecies))/TXOP=4 twModelSpecies wPrimVOCs[iSpeciesDex] = ChangeInConc[V_Value] Endfor EndFor Make/O/N=1/T $(strNewFolder + ":twHO2")/WAVE=twHO2 Make/O/N=1/T $(strNewFolder + ":twRO2")/WAVE=twRO2 Make/O/N=1/T $(strNewFolder + ":twROOH")/WAVE=twROOH Make/O/N=1/T $(strNewFolder + ":twHNO3pNO3")/WAVE=twHNO3pNO3 twHO2 = "HO2" twRO2 = "RO2" twROOH = "ROOH" twHNO3pNO3 = "HNO3pNO3" For(iDex = 0; iDex < ItemsInList(strCases); iDex += 1) strFolder = "root:Sens:" + StringFromList(iDex, strCases) + ":BoxModelOut_Run" + num2str(iRunDex) + "_Last" Make/O/N=1 $(strNewFolder + ":wHO2_" + StringFromList(iDex, strCases))/WAVE=wHO2 Wave wConc = $(strFolder + ":Model_HO2_Last") wHO2[0] = wConc[numpnts(wConc) - 1] Make/O/N=1 $(strNewFolder + ":wRO2_" + StringFromList(iDex, strCases))/WAVE=wRO2 Wave wConc = $(strFolder + ":Model_TotalVOC_PeroxyRadical_Last") wRO2[0] = wConc[numpnts(wConc) - 1] Make/O/N=1 $(strNewFolder + ":wROOH_" + StringFromList(iDex, strCases))/WAVE=wROOH Wave wConc = $(strFolder + ":Model_TotalVOC_Peroxide_Last") wROOH[0] = wConc[numpnts(wConc) - 1] Make/O/N=1 $(strNewFolder + ":wHNO3pNO3_" + StringFromList(iDex, strCases))/WAVE=wHNO3pNO3 Wave wConc = $(strFolder + ":Model_HNO3pNO3_Last") wHNO3pNO3[0] = wConc[numpnts(wConc) - 1] EndFor //Plot it strSpecies = "HO2;RO2;ROOH;HNO3pNO3" Variable vColor For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strSpecies); iSpeciesDex += 1) String strPlotName = "ModelComp_" + StringFromList(iSpeciesDex, strSpecies) + strCasesFullNames Wave twSpecies = $(strNewFolder + ":tw" + StringFromList(iSpeciesDex, strSpecies)) DoWindow/K/Z $strPlotName Display/W=(25,25,175,250) as strPlotName DoWindow/C $strPlotName For(iDex = 0; iDex < ItemsInList(strCases); iDex += 1) vColor = iDex / (ItemsInList(strCases) - 1) * 99 Wave wChange = $(strNewFolder + ":w" + StringFromList(iSpeciesDex, strSpecies) + "_" + StringFromList(iDex, strCases)) AppendToGraph wChange/TN=$StringFromList(iDex,strCases) vs twSpecies ModifyGraph rgb($StringFromList(iDex, strCases)) = (M_Colors[vColor][0], M_Colors[vColor][1], M_Colors[vColor][2]) EndFor SetAxis left, 0, * Label left, "Production during model (ppbv)" ModifyGraph mirror=2,standoff=0 ModifyGraph lowTrip(left)=0.01 Endfor KillWaves M_Colors End Function MakeSensitivityFig() SetDataFolder root: String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "Model_Comparison_*", ";") strFolderList = RemoveFromList("Model_Comparison_Base_NoHal", strFolderList) // strFolderList = RemoveFromList("Model_Comparison_BaseWNOx_NoHalWNOx", strFolderList) strFolderList = SortList(strFolderList) Variable vNumFolders = ItemsInList(strFolderList, ";") String strPlotName = "Sens_Results" DoWindow/K/Z $strPlotName Display as strPlotName DoWindow/C $strPlotName Variable iFolderDex Wave/T twVOCs = $("root:Model_Comparison_Base_NoHal:twVOcs") Wave wEnhancement = $("root:Model_Comparison_Base_NoHal:Enhancement") Wave wIncrease = $("root:Model_Comparison_Base_NoHal:IncreasePercent") Variable vCountPairs = 0 Variable vOffset = -0.2 String strPairsList = "" // AppendToGraph wEnhancement vs twVOCs AppendToGraph wIncrease vs twVOCs For(iFolderDex = 0; iFolderDex < vNumFolders; iFolderDex += 1) String strCurrentFolder = StringFromList(iFolderDex, strFolderList) String strSuffix = StringFromList(2, strCurrentFolder, "_") strSuffix = ReplaceString("Base", strSuffix, "") // Wave wEnhancement = $("root:" + StringFromList(iFolderDex, strFolderList) + ":Enhancement") Wave wIncrease = $("root:" + StringFromList(iFolderDex, strFolderList) + ":IncreasePercent") // AppendTograph wEnhancement/TN=$strSuffix vs twVOCs AppendTograph wIncrease/TN=$strSuffix vs twVOCs ModifyGraph toMode($strSuffix)=-1, mode($strSuffix)=3 ModifyGraph offset($strSuffix) = {vOffset, 0} ModifyGraph marker($strSuffix) = 19 If(StringMatch(strSuffix, "*Down20*") == 1) ModifyGraph toMode($strSuffix)=1, mode($strSuffix)=8 vCountPairs += 1 vOffset -= 0.05 EndIf vOffset += 0.05 EndFor ModifyGraph log(left) = 1 SetAxis left 0.9,10 SetAxis bottom, 8, 12 End Function HalogenatedVOCs() SetDataFolder root: String strRun = "1" String strFolder = "root:BoxModelOut_Run" + strRun + "_Last" String strVOCclass = "Acid;Alcohol;Aldehyde;AlkoxyRadical;Ketone;Peroxide;PeroxyAcid;PeroxyNitrate;PeroxyRadical" String strLayoutName = "Layout_Brom_Chlor_VOCs" DoWindow/K $strLayoutName NewLayout/N=$strLayoutName as strLayoutName // DoWindow/C $strLayoutName Variable iDex Wave wTime = $(strFolder + ":Model_Time_Last_min") For(iDex = 0; iDex < ItemsInList(strVOCclass); iDex += 1) String strLegend = "" Wave wTotal = $(strFolder + ":Model_TotalVOC_" + StringFromList(iDex, strVOCclass) + "_Last") Wave wBr = $(strFolder + ":Model_Br" + StringFromList(iDex, strVOCclass) + "_Last") Wave wCl = $(strFolder + ":Model_Cl" + StringFromList(iDex, strVOCclass) + "_Last") String strPlotName = "Brom_Chlor_" + StringFromList(iDex, strVOCclass) strLegend += StringFromList(iDex, strVOCclass) + "\r" strLegend += "Br" + StringFromList(iDex, strVOCclass) + " = " + num2str(wBr[numpnts(wBr) - 1] * 1000) + " ppt\r" strLegend += "Cl" + StringFromList(iDex, strVOCclass) + " = " + num2str(wCl[numpnts(wCl) - 1] * 1000) + " ppt" DoWindow/K $strPlotName Display as strPlotName DoWindow/C $strPlotName AppendToGraph/C=(54741,24158,0) wTotal vs wTime AppendToGraph/C=(0,29298,45746) wBr vs wTime AppendToGraph/C=(59110,40863,0) wBr vs wTime Legend/A=LT strLegend ModifyGraph mode=7,hbFill=3,toMode=2 AppendLayoutObject/W=$strLayoutName graph $strPlotName EndFor // For(iDex = 0; iDex < trunc(vNumFlights/12) + 1; iDex += 1) String strExecuteText = "Tile/A=(4,3)" // LayoutPageAction/W=$strLayoutName Page=(iDex + 1) Execute strExecuteText // Endfor End Function GetLargestBrClVOCs() SetDataFolder root: String strRun = "1" String strFolder = "root:BoxModelOut_Run" + strRun + "_Last" Wave/T twCnames = $(strFolder + ":twCnames") Wave wModel_Conc = $(strFolder + ":Model_Conc_Last") Make/O/N=(DimSize(wModel_Conc, 1)) $(strFolder + ":FinalConc_Br")/WAVE=FinalConc_Br Make/O/N=(DimSize(wModel_Conc, 1)) $(strFolder + ":FinalConc_Cl")/WAVE=FinalConc_cl Make/O/N=(numpnts(twCnames))/T $(strFolder + ":twCnames_Br")/WAVE=twCnames_Br Make/O/N=(numpnts(twCnames))/T $(strFolder + ":twCnames_Cl")/WAVE=twCnames_Cl twCnames_Br = twCnames twCnames_Cl = twCnames FinalConc_Br = wModel_Conc[DimSize(wModel_Conc,0) - 1][p] FinalConc_Cl = wModel_Conc[DimSize(wModel_Conc,0) - 1][p] Variable iDex Sort/R FinalConc_Br, FinalConc_Br, FinalConc_Cl, twCnames_Br, twCnames_Cl For(iDex = 0; iDex < numpnts(twCnames_Br); iDex += 1) If((StringMatch(twCnames_Br[iDex], "*BR*") == 0) || FinalConc_Br[iDex] < 0.0001) DeletePoints iDex, 1, twCnames_Br, FinalConc_Br iDex -= 1 EndIf EndFor For(iDex = 0; iDex < numpnts(twCnames_Cl); iDex += 1) If((StringMatch(twCnames_Cl[iDex], "*CL*") == 0) || FinalConc_Cl[iDex] < 0.0001) DeletePoints iDex, 1, twCnames_Cl, FinalConc_Cl iDex -= 1 EndIf EndFor End Function EstimateHOXInterference() SetDataFolder root: String strFolderList = DataFolderDir(1) strFolderList = ReplaceString("FOLDERS:", strFolderList, "") strFolderList = ReplaceString(",", strFolderList, ";") strFolderList = ListMatch(strFolderList, "Jan*", ";") strFolderList = SortList(strFolderList) Variable vNumFolders = ItemsInList(strFolderList, ";") Variable iDex String strRun For(iDex = 0; iDex < vNumFolders; iDex += 1) String strCurrentFolder = StringFromList(iDex, strFolderList) If(StringMatch(strCurrentFolder, "*_*")) strRun = StringFromList(1, strCurrentFolder, "_") Else strRun = "1" EndIf String strFolder = "root:" + StringFromList(iDex, strFolderList) + ":BoxModel_Run" + strRun + "_Puff" Wave wHOBr = $(strFolder + ":Obs_HOBr") Wave wBr2 = $(strFolder + ":Obs_Br2") Make/O/N=(numpnts(wHOBr)) $(strFolder + ":Obs_HOBr_Upper")/WAVE=wHOBr_Upper Make/O/N=(numpnts(wHOBr)) $(strFolder + ":Obs_HOBr_Lower")/WAVE=wHOBr_Lower wHOBr_Upper = wHOBr + wBr2*0.03 wHOBr_Lower = wHOBr - wBr2*0.03 Wave wHOCl = $(strFolder + ":Obs_HOCl") Wave wCl2 = $(strFolder + ":Obs_Cl2") Make/O/N=(numpnts(wHOCl)) $(strFolder + ":Obs_HOCl_Upper")/WAVE=wHOCl_Upper Make/O/N=(numpnts(wHOCl)) $(strFolder + ":Obs_HOCl_Lower")/WAVE=wHOCl_Lower wHOCl_Upper = wHOCl + wCl2*0.01 wHOCl_Lower = wHOCl - wCl2*0.01 EndFor End //*************************************************************// // CCW code for processing ground site VOC data. Mostly taken from UWFPS_Analysis_v13.ipf Function LoadAndExtractUWFPSGroundSites() //Automated function for loading the UWFPS ground site and extracting and interpolating all the data that will be relevant for box modeling Print "Loading UWFPS ground site ICARTT files" LoadGroundSiteICARTT() Print "Loading Utah maps" LoadUtahMaps() Print "Making standard time waves" MakeGroundSiteMSTTimewaves() MakeStandardGSTime() Print "Extracting NOx and O3 data" ExtractGSNOxO3Data() Print "Extracting Picarro data" ExtractPicarroData() Print "Extracting other UU data" ExtractOtherUUData() Print "Extracting CIMS data" ExtractGSCIMSData() Print "Extracting AMS data" ExtractGSAMSData() Print "Extracting VOC data" ExtractGSVOCData() Print "Extracting PM2.5 data" ExtractGSPM25Data() Print "Extracting formaldehyde data" ExtractFormaldehydeData() MakeGSWindFlag() AverageBkgdVOCsUU() SetDataFolder root:GroundSites String strVOCPath = "C:Users:cwomack:Documents:Research Projects:UWFPS:Halogens Analysis:F0AM" NewPath/O pVOCpath, strVOCPath LoadWave/O/Q/P=pVOCpath "twVOCName" LoadWave/O/Q/P=pVOCpath "twVOCName_F0AM" LoadWave/O/Q/P=pVOCpath "VOC_scaling_factor_GRLWomack" End Function LoadGroundSiteICARTT() //Loads ICARTT files for the ground sites. All ICARTT files should be in one folder, but the name and location don't matter NewDataFolder/O root:GroundSites //Make new folder to hold ground site data MakeGroundSiteWaves() Wave/T twGroundSites Variable vNumGroundSites = numpnts(twGroundSites) //Get a symbolic path to where all the ICARTT files are KillPath/Z pICARTTbasepath String sICARTTBasePath = "C:Users:cwomack:Documents:Research Projects:UWFPS:ICARTT Files:Ground Sites" //Optional. Comment this line out and it will give a dialog to choose folder NewPath/O/M="Select base path for ICARTT data\r"/Q pICARTTbasepath, sICARTTBasePath //NewPath/O/M="Select base path for ICARTT data\r"/Q pICARTTbasepath If(V_flag != 0) //If user cancels Abort EndIf PathInfo pICARTTbasepath //Get a list of the ICARTT files to load String sListICARTTFiles = IndexedFile(pICARTTbasepath, -1, ".ict") Variable vNumICARTTFiles, iFileDex, vFirstUnderscore, vSecondUnderscore, vThirdUnderscore Variable vNumFiles = ItemsInList(sListICARTTFiles) If(vNumFiles == 0) Abort "No ground site data files found" EndIf Make/O/T/N=(vNumFiles) twStartDate, twRevision Variable iSiteDex String sCurrentGSite, sCurrentFile, sCurrentFolder, sMeasurement For(iSiteDex = 0; iSiteDex < vNumGroundSites; iSiteDex += 1) //First loop through the known ground sites and make a subfolder for each site sCurrentGSite = twGroundSites[iSiteDex] NewDataFolder/O root:GroundSites:$(sCurrentGSite) EndFor For(iFileDex = 0; iFileDex < vNumFiles; iFileDex += 1) //Then loop through available ICARTT file names sCurrentFile = StringFromList(iFileDex, sListICARTTFiles) vFirstUnderscore = strsearch(sCurrentFile, "_", 0) //Get some information from the file name. First, split it up according to where the underscores are vSecondUnderscore = strsearch(sCurrentFile, "_", vFirstUnderscore + 1) vThirdUnderscore = strsearch(sCurrentFile, "_", vSecondUnderscore + 1) sCurrentFolder = sCurrentFile[0, vSecondUnderscore - 1] sCurrentGSite = sCurrentFile[vFirstUnderscore + 1, vSecondUnderscore - 1] sMeasurement = sCurrentFile[0, vFirstUnderscore - 1] twStartDate[iFileDex] = sCurrentFile[vSecondUnderscore + 1, vThirdUnderscore - 1] //Get the date (the string between the second and third underscores) twRevision[iFileDex] = sCurrentFile[vThirdUnderscore + 1, vThirdUnderscore + 2] //Get the revision number (the string after the third underscore) If(StringMatch(sCurrentGSite, "AllSites") == 1) //If it was one of the All sites files, put it in its own folder NewDataFolder/O/S root:GroundSites:AllSites NewDataFolder/O/S root:GroundSites:AllSites:$(sMeasurement) ElseIf(DataFolderExists("root:GroundSites:" + sCurrentGSite) == 0) Continue Else NewDataFolder/O/S root:GroundSites:$(sCurrentGSite):$(sMeasurement) //Otherwise, put it in the appropriate ground site's folder EndIf sCurrentFile = S_path + sCurrentFile LoadICARTT1001_UWFPS(sCurrentFile) EndFor End Function MakeGroundSiteWaves() SetDataFolder root:GroundSites Make/T/O/N=6 twGroundSites Make/O/N=6 GroundSiteLat, GroundSiteLon, GroundSiteAlt //UU, L4 ground site coordinates from Google Maps. SM, HW, NP, LN from (http://www.airmonitoring.utah.gov/network/Counties.htm) converted to lat/lon using http://www.rcn.montana.edu/resources/converter.aspx twGroundSites[0] = "SM" //Smithfield, Cache Valley twGroundSites[1] = "L4" //Logan, Cache Valley twGroundSites[2] = "HW" //Hawthorne, SLC Valley twGroundSites[3] = "UU" //University of Utah, SLC Valley twGroundSites[4] = "NP" //North Provo, Utah Valley twGroundSites[5] = "LN" //Lindon, Utah Valley GroundSiteLat[0] = 41.842777 GroundSiteLat[1] = 41.758830 GroundSiteLat[2] = 40.734396 GroundSiteLat[3] = 40.766070 GroundSiteLat[4] = 40.253825 GroundSiteLat[5] = 40.339574 GroundSiteLon[0] = -111.85194784658663 GroundSiteLon[1] = -111.81497222222222 GroundSiteLon[2] = -111.87209989446377 GroundSiteLon[3] = -111.847702 GroundSiteLon[4] = -111.66331056516078 GroundSiteLon[5] = -111.71348066850189 GroundSiteAlt[0] = 1379 GroundSiteAlt[1] = 1379 //Assume it's the same as Smithfield GroundSiteAlt[2] = 1306 GroundSiteAlt[3] = 1483 //NOTE: Munkh's 2017 paper says the roof of the Browning building is 28 m above ground, which is 155 m above the valley floor, which I'm assuming is ~1300 m GroundSiteAlt[4] = 1402 GroundSiteAlt[5] = 1442 End Function MakeGroundSiteMSTTimewaves() //The different ground sites all had different names for the timewaves (typically Date_UTC or mid_sec_UTC). This is inconvenient, plus, I'd rather have MST, // so make a copy called DateTime_MST and change its time zone. SetDataFolder root:GroundSites Wave/T twGroundSites Variable vNumGroundSites = numpnts(twGroundSites) Variable iSiteDex, vNumFolders, iFolderDex String sCurrentGSite, sCurrentFolder For(iSiteDex = 0; iSiteDex < vNumGroundSites; iSiteDex += 1) //Cycle through ground sites sCurrentGSite = twGroundSites[iSiteDex] SetDataFolder root:GroundSites:$(sCurrentGSite) vNumFolders = CountObjects("", 4) For(iFolderDex = 0; iFolderDex < vNumFolders; iFolderDex += 1) //Cycle through instruments sCurrentFolder = GetIndexedObjName("", 4, iFolderDex) SetDataFolder root:GroundSites:$(sCurrentGSite):$(sCurrentFolder) Wave Date_UTC If(WaveExists(Date_UTC) == 1) //If there's a wave called Date_UTC, make a new wave Make/O/N=(numpnts(Date_UTC))/D DateTime_MST SetScale d, 0, 0, "dat", DateTime_MST DateTime_MST = Date_UTC - 7*3600 Else Wave mid_sec_UTC If(WaveExists(mid_sec_UTC) == 1) //If there's a wave called mid_sec_UTC, make a new wave Make/O/N=(numpnts(mid_sec_UTC))/D DateTime_MST DateTime_MST = mid_sec_UTC - 7*3600 SetScale d, 0, 0, "dat", DateTime_MST EndIf EndIf SetDataFolder root:GroundSites:$(sCurrentGSite) EndFor EndFor //Make date wave for the hourly and daily PM2.5 folders, which contain all the ground sites SetDataFolder root:GroundSites:AllSites:HourlyPM25 Wave Date_UTC Make/O/N=(numpnts(Date_UTC))/D DateTime_MST SetScale d, 0, 0, "dat", DateTime_MST DateTime_MST = Date_UTC - 7*3600 SetDataFolder root:GroundSites:AllSites:DailyPM25 Wave Date_MST Make/O/N=(numpnts(Date_MST))/D DateTime_MST SetScale d, 0, 0, "dat", DateTime_MST DateTime_MST = Date_MST + Date2Secs(2016,12,01) //Only wave in this folder counts seconds since 12.01.2016 End Function MakeStandardGSTime() //Since all the ground sites started and stopped at different times, make three master timewaves that span the whole month-long intensive, //spaced minutly, hourly, or daily so that all the ground site is more uniform SetDataFolder root:GroundSites Variable vGSStart = Date2Secs(2017, 01, 15) //Define start and stop of UWFPS campaign as Jan 15 at midnight to Feb 15 at midnight Variable vGSStop = Date2Secs(2017, 02, 16) //Get number of points for each wave Variable vNumTimePnts_min = (vGSStop - vGSStart) / 60 Variable vNumTimePnts_hr = (vGSStop - vGSStart) / 60 / 60 Variable vNumTimePnts_day = (vGSStop - vGSStart) / 60 / 60 / 24 Make/O/N=(vNumTimePnts_min)/D GS_Timewave_MST_min, GS_Timewave_MST_min_start, GS_Timewave_MST_min_stop Make/O/N=(vNumTimePnts_hr)/D GS_Timewave_MST_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop Make/O/N=(vNumTimePnts_day)/D GS_Timewave_MST_day, GS_Timewave_MST_day_start, GS_Timewave_MST_day_stop SetScale d, 0, 0, "dat", GS_Timewave_MST_day, GS_Timewave_MST_hr, GS_Timewave_MST_min SetScale d, 0, 0, "dat", GS_Timewave_MST_day_start, GS_Timewave_MST_hr_start, GS_Timewave_MST_min_start SetScale d, 0, 0, "dat", GS_Timewave_MST_day_stop, GS_Timewave_MST_hr_stop, GS_Timewave_MST_min_stop //Define each timewave relative to the start time (timestamps refer to the middle of the time bin) GS_Timewave_MST_min = vGSStart + p*60 + 30 GS_Timewave_MST_min_start = vGSStart + p*60 GS_Timewave_MST_min_stop = vGSStart + (p + 1)*60 GS_Timewave_MST_hr = vGSStart + p*60*60 + 30*60 GS_Timewave_MST_hr_start = vGSStart + p*60*60 GS_Timewave_MST_hr_stop = vGSStart + (p + 1)*60*60 GS_Timewave_MST_day = vGSStart + p*60*60*24 + 12*60*60 GS_Timewave_MST_day_start = vGSStart + p*60*60*24 GS_Timewave_MST_day_stop = vGSStart + (p + 1)*60*60*24 End Function MakeGSDayFlag() //Make a flag wave for day and night SetDataFolder root:GroundSites: Wave GS_Timewave_MST_hr, GS_Timewave_MST_min, GroundSiteLat, GroundSiteLon Make/O/N=(numpnts(GS_Timewave_MST_hr)) DayFlag_hr, SEA_hr Make/O/N=(numpnts(GS_Timewave_MST_min)) DayFlag_min, SEA_min Make/O/N=741 SEA, Flag_Day SEA_hr = 90 - SZA_Calc(GroundSiteLat[3], GroundSiteLon[3], 2017, GS_Timewave_MST_hr[p], 7) SEA_min = 90 - SZA_Calc(GroundSiteLat[3], GroundSiteLon[3], 2017, GS_Timewave_MST_min[p], 7) DayFlag_hr = SEA_hr[p] > 0 ? 1 : 0 DayFlag_min = SEA_min[p] > 0 ? 1 : 0 KillWaves/Z SEA_hr, SEA_day End Function ExtractGSNOxO3Data() //Take the NOx and O3 data from each ground site folder, and interpolate onto the master timewave SetDataFolder root:GroundSites Wave/T twGroundSites //Make temporary start and stop waves for averaging Wave GS_Timewave_MST_min, GS_Timewave_MST_min_start, GS_Timewave_MST_min_stop Wave GS_Timewave_MST_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop Variable vNumGroundSites = numpnts(twGroundSites) Variable iSiteDex, iFolderDex For(iSiteDex = 0; iSiteDex < vNumGroundSites; iSiteDex += 1) //Cycle through ground sites Variable vNumFolders = CountObjects("root:GroundSites:" + twGroundSites[iSiteDex], 4) //Cycle through subfolders (one for each measurement at the ground site) For(iFolderDex = 0; iFolderDex < vNumFolders; iFolderDex += 1) String sCurrentDataFolder = GetIndexedObjName("root:GroundSites:" + twGroundSites[iSiteDex], 4, iFolderDex) If(StringMatch(sCurrentDataFolder, "Trace*") == 1) //Look for subfolders called TraceAndPM or TracePMandMet. These have wave names without units, and are hourly Wave wO3_Raw = root:GroundSites:$(twGroundSites[iSiteDex]):$(sCurrentDataFolder):O3 Wave wNO_Raw = root:GroundSites:$(twGroundSites[iSiteDex]):$(sCurrentDataFolder):NO Wave wNO2_Raw = root:GroundSites:$(twGroundSites[iSiteDex]):$(sCurrentDataFolder):NO2 Wave wNOy_Raw = root:GroundSites:$(twGroundSites[iSiteDex]):$(sCurrentDataFolder):NOy Wave wCO_Raw = root:GroundSites:$(twGroundSites[iSiteDex]):$(sCurrentDataFolder):CO Wave wTime_Raw = root:GroundSites:$(twGroundSites[iSiteDex]):$(sCurrentDataFolder):DateTime_MST If(WaveExists(wO3_Raw) == 1) Make/O/N=(numpnts(GS_Timewave_MST_hr)) $("O3_ppbv_" + twGroundSites[iSiteDex] + "_hr")/WAVE=wO3_New DoAveragOnlyUsingStartStop(wTime_Raw, wO3_Raw, wO3_New, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) EndIf If(WaveExists(wNO_Raw) == 1) Make/O/N=(numpnts(GS_Timewave_MST_hr)) $("NO_ppbv_" + twGroundSites[iSiteDex] + "_hr")/WAVE=wNO_New DoAveragOnlyUsingStartStop(wTime_Raw, wNO_Raw, wNO_New, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) EndIf If(WaveExists(wNO2_Raw) == 1) Make/O/N=(numpnts(GS_Timewave_MST_hr)) $("NO2_ppbv_" + twGroundSites[iSiteDex] + "_hr")/WAVE=wNO2_New DoAveragOnlyUsingStartStop(wTime_Raw, wNO2_Raw, wNO2_New, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) EndIf If(WaveExists(wNOy_Raw) == 1) Make/O/N=(numpnts(GS_Timewave_MST_hr)) $("NOy_ppbv_" + twGroundSites[iSiteDex] + "_hr")/WAVE=wNOy_New DoAveragOnlyUsingStartStop(wTime_Raw, wNOy_Raw, wNOy_New, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) EndIf If(WaveExists(wCO_Raw) == 1) Make/O/N=(numpnts(GS_Timewave_MST_hr)) $("CO_ppbv_" + twGroundSites[iSiteDex] + "_hr")/WAVE=wCO_New DoAveragOnlyUsingStartStop(wTime_Raw, wCO_Raw, wCO_New, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) EndIf ElseIf(StringMatch(sCurrentDataFolder, "EPAORD") == 1) //Look for subfolders called EPAORD. These have wave names with units, and are taken at 1 min frequency Wave wO3_Raw = root:GroundSites:$(twGroundSites[iSiteDex]):$(sCurrentDataFolder):Ozone_ppbv Wave wNO_Raw = root:GroundSites:$(twGroundSites[iSiteDex]):$(sCurrentDataFolder):NO_ppbv Wave wNO2_Raw = root:GroundSites:$(twGroundSites[iSiteDex]):$(sCurrentDataFolder):NO2_ppbv Wave wNOy_Raw = root:GroundSites:$(twGroundSites[iSiteDex]):$(sCurrentDataFolder):NOy_ppbv Wave wTime_Raw = root:GroundSites:$(twGroundSites[iSiteDex]):$(sCurrentDataFolder):DateTime_MST If(WaveExists(wO3_Raw) == 1) Make/O/N=(numpnts(GS_Timewave_MST_min)) $("O3_ppbv_" + twGroundSites[iSiteDex] + "_min")/WAVE=wO3_New Make/O/N=(numpnts(GS_Timewave_MST_hr)) $("O3_ppbv_" + twGroundSites[iSiteDex] + "_hr")/WAVE=wO3_New DoAveragOnlyUsingStartStop(wTime_Raw, wO3_Raw, wO3_New, GS_Timewave_MST_min_start, GS_Timewave_MST_min_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wO3_Raw, wO3_New, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) EndIf If(WaveExists(wNO_Raw) == 1) Make/O/N=(numpnts(GS_Timewave_MST_min)) $("NO_ppbv_" + twGroundSites[iSiteDex] + "_min")/WAVE=wNO_New Make/O/N=(numpnts(GS_Timewave_MST_hr)) $("NO_ppbv_" + twGroundSites[iSiteDex] + "_hr")/WAVE=wNO_New DoAveragOnlyUsingStartStop(wTime_Raw, wNO_Raw, wNO_New, GS_Timewave_MST_min_start, GS_Timewave_MST_min_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wNO_Raw, wNO_New, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) EndIf If(WaveExists(wNO2_Raw) == 1) Make/O/N=(numpnts(GS_Timewave_MST_min)) $("NO2_ppbv_" + twGroundSites[iSiteDex] + "_min")/WAVE=wNO2_New Make/O/N=(numpnts(GS_Timewave_MST_hr)) $("NO2_ppbv_" + twGroundSites[iSiteDex] + "_hr")/WAVE=wNO2_New DoAveragOnlyUsingStartStop(wTime_Raw, wNO2_Raw, wNO2_New, GS_Timewave_MST_min_start, GS_Timewave_MST_min_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wNO2_Raw, wNO2_New, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) EndIf If(WaveExists(wNOy_Raw) == 1) Make/O/N=(numpnts(GS_Timewave_MST_min)) $("NOy_ppbv_" + twGroundSites[iSiteDex] + "_min")/WAVE=wNOy_New Make/O/N=(numpnts(GS_Timewave_MST_hr)) $("NOy_ppbv_" + twGroundSites[iSiteDex] + "_hr")/WAVE=wNOy_New DoAveragOnlyUsingStartStop(wTime_Raw, wNOy_Raw, wNOy_New, GS_Timewave_MST_min_start, GS_Timewave_MST_min_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wNOy_Raw, wNOy_New, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) EndIf EndIf EndFor EndFor End Function ExtractPicarroData() SetDataFolder root:GroundSites Wave GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop Wave wCH4_Raw = root:GroundSites:L4:Picarro1min:CH4 Wave wCO2_Raw = root:GroundSites:L4:Picarro1min:CO2 Wave wNH3_Raw = root:GroundSites:L4:Picarro1min:NH3 Wave wTime_Raw = root:GroundSites:L4:Picarro1min:DateTime_MST Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) CH4_ppbv_L4_hr Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) CO2_ppbv_L4_hr Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) NH3_ppbv_L4_hr DoAveragOnlyUsingStartStop(wTime_Raw, wCH4_Raw, CH4_ppbv_L4_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wCO2_Raw, CO2_ppbv_L4_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wNH3_Raw, NH3_ppbv_L4_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) CH4_ppbv_L4_hr *= 1000 CO2_ppbv_L4_hr *= 1000 End Function ExtractOtherUUData() SetDataFolder root:GroundSites Wave GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop Wave wTemp_Raw = root:GroundSites:UU:TracePMMet:T_C Wave wPress_Raw = root:GroundSites:UU:TracePMMet:Pressure Wave wRH_Raw = root:GroundSites:UU:TracePMMet:RH Wave wWindSpd_Raw = root:GroundSites:UU:TracePMMet:ws Wave wWindDir_Raw = root:GroundSites:UU:TracePMMet:wd //Wave wCO_Raw = root:GroundSites:UU:TracePMMet:CO Wave wTime_Raw = root:GroundSites:UU:TracePMMet:DateTime_MST Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) Temp_degC_UU_hr Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) Pressure_mbar_UU_hr Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) RH_percent_UU_hr Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) WindSpd_msec_UU_hr Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) WindDir_deg_UU_hr //Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) CO_ppbv_UU_hr DoAveragOnlyUsingStartStop(wTime_Raw, wTemp_Raw, Temp_degC_UU_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wPress_Raw, Pressure_mbar_UU_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wRH_Raw, RH_percent_UU_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wWindSpd_Raw, WindSpd_msec_UU_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wWindDir_Raw, WindDir_deg_UU_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) //DoAveragOnlyUsingStartStop(wTime_Raw, wCO_Raw, CO_ppbv_UU_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) End Function ExtractGSCIMSData() //Take CIMS data from the L4 CIMS instrument and interpolate onto master time wave //Note: CIMS data is timestamped with the beginning of the time bin (i.e. 12:00:00 = data from 12:00:00 to 12:00:59) SetDataFolder root:GroundSites //Make temporary time waves for averaging Wave GS_Timewave_MST_min, GS_Timewave_MST_min_start, GS_Timewave_MST_min_stop Wave GS_Timewave_MST_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop // Wave GS_Timewave_MST_min // Duplicate/O/FREE GS_Timewave_MST_min, GS_Timewave_MST_min_stop // GS_Timewave_MST_min_stop = GS_Timewave_MST_min + 60 - 1 //Declare raw CIMS data Wave wCH2O2_Raw = root:GroundSites:L4:EPACIMS:CH2O2_pptv Wave wClNO2_Raw = root:GroundSites:L4:EPACIMS:ClNO2_pptv Wave wCl2_Raw = root:GroundSites:L4:EPACIMS:Cl2_pptv Wave wHNO3_Raw = root:GroundSites:L4:EPACIMS:HNO3_pptv Wave wHONO_Raw = root:GroundSites:L4:EPACIMS:HONO_pptv Wave wH2O2_Raw = root:GroundSites:L4:EPACIMS:H2O2_pptv Wave wN2O5_Raw = root:GroundSites:L4:EPACIMS:N2O5_pptv Wave wTime_Raw = root:GroundSites:L4:EPACIMS:Start_MST //Make properly labeled new waves Make/O/N=(numpnts(GS_Timewave_MST_min)) CH2O2_ppbv_L4_min Make/O/N=(numpnts(GS_Timewave_MST_min)) ClNO2_ppbv_L4_min Make/O/N=(numpnts(GS_Timewave_MST_min)) Cl2_ppbv_L4_min Make/O/N=(numpnts(GS_Timewave_MST_min)) HNO3_ppbv_L4_min Make/O/N=(numpnts(GS_Timewave_MST_min)) HONO_ppbv_L4_min Make/O/N=(numpnts(GS_Timewave_MST_min)) H2O2_ppbv_L4_min Make/O/N=(numpnts(GS_Timewave_MST_min)) N2O5_ppbv_L4_min Make/O/N=(numpnts(GS_Timewave_MST_hr)) CH2O2_ppbv_L4_hr Make/O/N=(numpnts(GS_Timewave_MST_hr)) ClNO2_ppbv_L4_hr Make/O/N=(numpnts(GS_Timewave_MST_hr)) Cl2_ppbv_L4_hr Make/O/N=(numpnts(GS_Timewave_MST_hr)) HNO3_ppbv_L4_hr Make/O/N=(numpnts(GS_Timewave_MST_hr)) HONO_ppbv_L4_hr Make/O/N=(numpnts(GS_Timewave_MST_hr)) H2O2_ppbv_L4_hr Make/O/N=(numpnts(GS_Timewave_MST_hr)) N2O5_ppbv_L4_hr //Interpolate onto master time wave DoAveragOnlyUsingStartStop(wTime_Raw, wCH2O2_Raw, CH2O2_ppbv_L4_min, GS_Timewave_MST_min_start, GS_Timewave_MST_min_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wClNO2_Raw, ClNO2_ppbv_L4_min, GS_Timewave_MST_min_start, GS_Timewave_MST_min_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wCl2_Raw, Cl2_ppbv_L4_min, GS_Timewave_MST_min_start, GS_Timewave_MST_min_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wHNO3_Raw, HNO3_ppbv_L4_min, GS_Timewave_MST_min_start, GS_Timewave_MST_min_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wHONO_Raw, HONO_ppbv_L4_min, GS_Timewave_MST_min_start, GS_Timewave_MST_min_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wH2O2_Raw, H2O2_ppbv_L4_min, GS_Timewave_MST_min_start, GS_Timewave_MST_min_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wN2O5_Raw, N2O5_ppbv_L4_min, GS_Timewave_MST_min_start, GS_Timewave_MST_min_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wCH2O2_Raw, CH2O2_ppbv_L4_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wClNO2_Raw, ClNO2_ppbv_L4_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wCl2_Raw, Cl2_ppbv_L4_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wHNO3_Raw, HNO3_ppbv_L4_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wHONO_Raw, HONO_ppbv_L4_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wH2O2_Raw, H2O2_ppbv_L4_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wN2O5_Raw, N2O5_ppbv_L4_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) //Convert from pptv to ppbv CH2O2_ppbv_L4_min /= 1000 ClNO2_ppbv_L4_min /= 1000 Cl2_ppbv_L4_min /= 1000 HNO3_ppbv_L4_min /= 1000 HONO_ppbv_L4_min /= 1000 H2O2_ppbv_L4_min /= 1000 N2O5_ppbv_L4_min /= 1000 CH2O2_ppbv_L4_hr /= 1000 ClNO2_ppbv_L4_hr /= 1000 Cl2_ppbv_L4_hr /= 1000 HNO3_ppbv_L4_hr /= 1000 HONO_ppbv_L4_hr /= 1000 H2O2_ppbv_L4_hr /= 1000 N2O5_ppbv_L4_hr /= 1000 End Function ExtractGSAMSData() //Get AMS nitrate data from the L4 site and interpolate onto master time wave SetDataFolder root:GroundSites //Make temporary time waves for interpolation Wave GS_Timewave_MST_min_start, GS_Timewave_MST_min_stop Wave GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop //Declare raw data waves Wave wNO3_Raw = root:GroundSites:L4:AMS:NO3_CEadj Wave wTime_Raw = root:GroundSites:L4:AMS:L4_AMS_Time_MST Duplicate/O/FREE wTime_Raw Time_Mid Time_Mid = wTime_Raw - 2.5*60 //Ken Docherty says that the AMS timestamps refer to the end of the time bin. So make a temp wave to hold the timestamps in the middle of the bin //Make wave to hold averaged data Make/O/N=(numpnts(GS_Timewave_MST_min_start)) NO3_ugm3_L4_min Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) NO3_ugm3_L4_hr //Average to hourly data DoAveragOnlyUsingStartStop(Time_Mid, wNO3_Raw, NO3_ugm3_L4_min, GS_Timewave_MST_min_start, GS_Timewave_MST_min_stop) DoAveragOnlyUsingStartStop(Time_Mid, wNO3_Raw, NO3_ugm3_L4_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) Variable vConvFactor_ugm3_ppbv = 1e-12*(6.022e23/62)*(1e9/2.685e19) //Convert ug / m3 of NO3- to ppbv, using 62 g/mol molar mass, and number density at STP //Convert ug/m3 to ppbv, for easier comparison later Duplicate/O NO3_ugm3_L4_min NO3_ppbv_L4_min NO3_ppbv_L4_min = NO3_ugm3_L4_min * vConvFactor_ugm3_ppbv Duplicate/O NO3_ugm3_L4_hr NO3_ppbv_L4_hr NO3_ppbv_L4_hr = NO3_ugm3_L4_hr * vConvFactor_ugm3_ppbv End Function ExtractGSVOCData() //Extract the VOC data from the UU site, and interpolate onto master timewave SetDataFolder root:GroundSites //Make temporary time waves for averaging Wave GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop //Declare raw data waves Wave wAcetaldehyde_Raw = root:GroundSites:UU:HourlyVOC:Acetaldehyde Wave wAceticAcid_Raw = root:GroundSites:UU:HourlyVOC:Acetic_Acid_est Wave wAcetone_Raw = root:GroundSites:UU:HourlyVOC:Acetone Wave wAcetonitrile_Raw = root:GroundSites:UU:HourlyVOC:Acetonitrile Wave wBenzene_Raw = root:GroundSites:UU:HourlyVOC:Benzene Wave wFormaldehyde_Raw = root:GroundSites:UU:HourlyVOC:Formaldehyde_est Wave wFormic_Acid_Raw = root:GroundSites:UU:HourlyVOC:Formic_Acid_est Wave wIsoprene_Raw = root:GroundSites:UU:HourlyVOC:Isoprene Wave wMEK_Raw = root:GroundSites:UU:HourlyVOC:MEK Wave wMethanol_Raw = root:GroundSites:UU:HourlyVOC:Methanol Wave wMVK_MACR_Raw = root:GroundSites:UU:HourlyVOC:MVK_MACR Wave wC8Aromatics_Raw = root:GroundSites:UU:HourlyVOC:p_Xylene Wave wC9Aromatics_Raw = root:GroundSites:UU:HourlyVOC:X124_TMB Wave wC5Ketones_Raw = root:GroundSites:UU:HourlyVOC:X3_pentanone Wave wC6Ketones_Raw = root:GroundSites:UU:HourlyVOC:X3_hexanone Wave wToluene_Raw = root:GroundSites:UU:HourlyVOC:Toluene Wave wTime_Raw = root:GroundSites:UU:HourlyVOC:DateTime_MST //Make properly named waves to hold averaged data Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) Acetaldehyde_ppbv_UU_hr Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) Acetic_Acid_ppbv_UU_hr Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) Acetone_ppbv_UU_hr Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) Acetonitrile_ppbv_UU_hr Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) Benzene_ppbv_UU_hr Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) Formaldehyde_ppbv_UU_hr Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) Formic_Acid_ppbv_UU_hr Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) Isoprene_ppbv_UU_hr Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) MEK_ppbv_UU_hr Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) Methanol_ppbv_UU_hr Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) MVK_MACR_ppbv_UU_hr Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) C8Aromatics_ppbv_UU_hr Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) C9Aromatics_ppbv_UU_hr Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) C5Ketones_ppbv_UU_hr Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) C6Ketones_ppbv_UU_hr Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) Toluene_ppbv_UU_hr //Average to master timewave DoAveragOnlyUsingStartStop(wTime_Raw, wAcetaldehyde_Raw, Acetaldehyde_ppbv_UU_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wAceticAcid_Raw, Acetic_Acid_ppbv_UU_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wAcetone_Raw, Acetone_ppbv_UU_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wAcetonitrile_Raw, Acetonitrile_ppbv_UU_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wBenzene_Raw, Benzene_ppbv_UU_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wFormaldehyde_Raw, Formaldehyde_ppbv_UU_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wFormic_Acid_Raw, Formic_Acid_ppbv_UU_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wIsoprene_Raw, Isoprene_ppbv_UU_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wMEK_Raw, MEK_ppbv_UU_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wMethanol_Raw, Methanol_ppbv_UU_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wMVK_MACR_Raw, MVK_MACR_ppbv_UU_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wC8Aromatics_Raw, C8Aromatics_ppbv_UU_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wC9Aromatics_Raw, C9Aromatics_ppbv_UU_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wC5Ketones_Raw, C5Ketones_ppbv_UU_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wC6Ketones_Raw, C6Ketones_ppbv_UU_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wToluene_Raw, Toluene_ppbv_UU_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) End Function ExtractGSPM25Data() //Extract the PM2.5 data from all the ground sites. L4 is separate because it was done by the EPA SetDataFolder root:GroundSites //Make temporary time waves for averaging Wave GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop Wave GS_Timewave_MST_day_start, GS_Timewave_MST_day_stop Wave/T twGroundSites Variable vNumGroundSites = numpnts(twGroundSites) Variable iSiteDex Wave wTime_Raw = root:GroundSites:AllSites:HourlyPM25:DateTime_MST For(iSiteDex = 0; iSiteDex < vNumGroundSites; iSiteDex += 1) //Cycle through all ground sites Wave wPM_Raw = root:GroundSites:AllSites:HourlyPM25:$(twGroundSites[iSiteDex]) //Get raw data from the HourlyPM25 folder If(WaveExists(wPM_Raw) == 1) Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) $("PM25_ugm3_" + twGroundSites[iSiteDex] + "_hr")/WAVE=wPM_New //Average onto master timewave DoAveragOnlyUsingStartStop(wTime_Raw, wPM_Raw, wPM_New, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) Else Wave wPM_Raw = root:GroundSites:AllSites:HourlyPM25:LND //The LND site should be changed to LN Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) $("PM25_ugm3_LN_hr")/WAVE=wPM_New DoAveragOnlyUsingStartStop(wTime_Raw, wPM_Raw, wPM_New, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) EndIf EndFor //Repeat for Daily PM25 Wave wTime_Raw = root:GroundSites:AllSites:DailyPM25:DateTime_MST For(iSiteDex = 0; iSiteDex < vNumGroundSites; iSiteDex += 1) Wave wPM_Raw = root:GroundSites:AllSites:DailyPM25:$(twGroundSites[iSiteDex]) If(WaveExists(wPM_Raw) == 1) Make/O/N=(numpnts(GS_Timewave_MST_day_start)) $("PM25_ugm3_" + twGroundSites[iSiteDex] + "_day")/WAVE=wPM_New DoAveragOnlyUsingStartStop(wTime_Raw, wPM_Raw, wPM_New, GS_Timewave_MST_day_start, GS_Timewave_MST_day_stop) Else Wave wPM_Raw = root:GroundSites:AllSites:DailyPM25:LND Make/O/N=(numpnts(GS_Timewave_MST_day_start)) $("PM25_ugm3_LN_day")/WAVE=wPM_New DoAveragOnlyUsingStartStop(wTime_Raw, wPM_Raw, wPM_New, GS_Timewave_MST_day_start, GS_Timewave_MST_day_stop) EndIf EndFor Wave wPM_Raw = root:GroundSites:L4:EPAORD:PM2_5_ugm3 Wave wTime_Raw = root:Groundsites:L4:EPAORD:DateTime_MST Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) $("PM25_ugm3_L4_hr")/WAVE=wPM_New //Average onto master timewave DoAveragOnlyUsingStartStop(wTime_Raw, wPM_Raw, wPM_New, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) End Function ExtractFormaldehydeData() //Extract the formaldehyde from the VOC site from the EPA QCL instrument (different than the formaldehyde from the UU PTRMS) SetDataFolder root:GroundSites //Make temporary time wave for averaging Wave GS_Timewave_MST_min_start, GS_Timewave_MST_min_stop Wave GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop //Declare raw waves Wave wHCHO_Raw = root:GroundSites:UU:HCHO:HCHO_ppb Wave wHCOOH_Raw = root:GroundSites:UU:HCHO:HCOOH_ppb Wave wH2O_Raw = root:GroundSites:UU:HCHO:H2O_ppm Wave wTime_Raw = root:GroundSites:UU:HCHO:Time_Mid_MST //Make waves to hold averaged data Make/O/N=(numpnts(GS_Timewave_MST_min_start)) HCHO_ppbv_UU_min Make/O/N=(numpnts(GS_Timewave_MST_min_start)) HCOOH_ppbv_UU_min Make/O/N=(numpnts(GS_Timewave_MST_min_start)) H2O_ppbv_UU_min Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) HCHO_ppbv_UU_hr Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) HCOOH_ppbv_UU_hr Make/O/N=(numpnts(GS_Timewave_MST_hr_start)) H2O_ppbv_UU_hr //Average the raw data to the master time wave DoAveragOnlyUsingStartStop(wTime_Raw, wHCHO_Raw, HCHO_ppbv_UU_min, GS_Timewave_MST_min_start, GS_Timewave_MST_min_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wHCOOH_Raw, HCOOH_ppbv_UU_min, GS_Timewave_MST_min_start, GS_Timewave_MST_min_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wH2O_Raw, H2O_ppbv_UU_min, GS_Timewave_MST_min_start, GS_Timewave_MST_min_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wHCHO_Raw, HCHO_ppbv_UU_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wHCOOH_Raw, HCOOH_ppbv_UU_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) DoAveragOnlyUsingStartStop(wTime_Raw, wH2O_Raw, H2O_ppbv_UU_hr, GS_Timewave_MST_hr_start, GS_Timewave_MST_hr_stop) //Convert H2O from ppm to ppb, cause it's easier later. H2O_ppbv_UU_min *= 1000 H2O_ppbv_UU_hr *= 1000 End Function MakeGSWindFlag() //Flag the data when there were strong winds from the south and west SetDataFolder root:GroundSites: Wave GS_Timewave_MST_hr, GS_Timewave_MST_min, WindSpd_msec_UU_hr, WindDir_deg_UU_hr Make/O/N=(numpnts(GS_Timewave_MST_hr)) SouthWindFlag, WestWindFlag SouthWindFlag = WindSpd_msec_UU_hr[p] > 6 && WindDir_deg_UU_hr[p] < 240 ? 1 : 0 WestWindFlag = WindSpd_msec_UU_hr[p] > 4 && WindDir_deg_UU_hr[p] > 240 ? 1 : 0 End Function AverageBkgdVOCsUU() //Average the VOC data when the wind was strong from the west and south, or both SetDataFolder root:GroundSites Wave WestWindFlag, SouthWindFlag String strWavelist = WaveList("*ppbv_UU_hr", ";", "") Variable vNumWaves = ItemsInList(strWaveList) Make/O/N=(vNumWaves)/T twVOCName_UU Make/O/N=(vNumWaves) wVOC_Avg_UU_South, wVOC_Avg_UU_West, wVOC_Avg_UU Make/O/N=(vNumWaves) wVOC_StDev_UU_South, wVOC_StDev_UU_West, wVOC_StDev_UU Make/O/N=(vNumWaves) wVOC_X = p Variable iDex For(iDex = 0; iDex < vNumWaves; iDex += 1) String strCurrentName = StringFromList(iDex, strWaveList) Wave wCurrentWave = $(strCurrentName) String strSpecies = StringFromList(0, strCurrentName, "_") twVOCName_UU[iDex] = strSpecies Make/O/N=(numpnts(wCurrentWave))/FREE TempWave TempWave = WestWindFlag[p] == 1 ? wCurrentWave[p] : NaN WaveStats/Q TempWave wVOC_Avg_UU_West[iDex] = V_Avg wVOC_StDev_UU_West[iDex] = V_sdev TempWave = SouthWindFlag[p] == 1 ? wCurrentWave[p] : NaN WaveStats/Q TempWave wVOC_Avg_UU_South[iDex] = V_Avg wVOC_StDev_UU_South[iDex] = V_sdev TempWave = (SouthWindFlag[p] == 1 || WestWindFlag[p] == 1) ? wCurrentWave[p] : NaN WaveStats/Q TempWave wVOC_Avg_UU[iDex] = V_Avg wVOC_StDev_UU[iDex] = V_sdev EndFor End Function CompareVOCProfiles() SetDataFolder root: Wave/T twVOCName_UU = root:GroundSites:twVOCName_UU Wave wVOC_Avg_UU = root:GroundSites:wVOC_Avg_UU Wave wVOC_StDev_UU = root:GroundSites:wVOC_StDev_UU Wave/T twVOCName_GCFID = root:Historical:Winter_2013:twVOCName Wave wVOC_Avg_GCFID_2013 = root:Historical:Winter_2013:wVOC_Avg_GCFID Wave wVOC_Avg_GCFID_2014 = root:Historical:Winter_2014:wVOC_Avg_GCFID Wave wVOC_Avg_GCFID_2015 = root:Historical:Winter_2015:wVOC_Avg_GCFID Wave wVOC_StDev_GCFID_2013 = root:Historical:Winter_2013:wVOC_StDev_GCFID Wave wVOC_StDev_GCFID_2014 = root:Historical:Winter_2014:wVOC_StDev_GCFID Wave wVOC_StDev_GCFID_2015 = root:Historical:Winter_2015:wVOC_StDev_GCFID String strVOCs = "Methanol;Ethanol;HCHO;Acetaldehyde;Acetone;MEK;MVK;propanal;butanal;C5Ketones;C6Ketones;Acetic;HCOOH;Isoprene;Benzene;Toluene;C8Aromatics;C9Aromatics;"//PTR-MS strVOCs += "Styrene;m_Diethylbenzene;p_Diethylbenzene;"//C7 and C10 aromatics strVOCs += "Ethane;Propane;n_Butane;iso_Butane;n_Pentane;iso_Pentane;n_Hexane;n_Heptane;n_Octane;n_Nonane;n_Decane;n_Undecane;n_Dodecane;"//Simple alkanes strVOCs += "X2_2_Dimethylbutane;X2_3_Dimethylbutane;X2_Methylpentane;X3_methylpentane;X2_3_Dimethylpentane;X2_4_Dimethylpentane;X2_2_4_Trimethylpentane;X2_3_4_Trimethylpentane;X2_Methylhexane;X3_Methylhexane;X2_Methylheptane;X3_Methylheptane;" strVOCs += "Cyclopentane;Methylcyclopentane;Cyclohexane;Methylcyclohexane;"//Cycloalkanes strVOCs += "Ethylene;Propylene;X1_butene;trans_2_butene;cis_2_butene;X1_pentene;trans_2_pentene;cis_2_pentene;X1_Hexene;Acetylene;"//Alkenes and alkynes Variable vNumVOCs = ItemsInList(strVOCs) Make/O/N=(vNumVOCs)/T twVOCName = StringFromList(p, strVOCs) String strC8Aromatics = "Ethylbenzene;m_xylene;o_xylene;p_xylene" String strC9Aromatics = "iso_Propylbenzene;n_Propylbenzene;m_Ethyltoluene;o_Ethyltoluene;p_ethyltoluene;X1_3_5_trimethylbenzene;X1_2_4_trimethylbenzene;X1_2_3_trimethylbenzene" Make/O/N=(vNumVOCs) VOC_Avg_PTRMS_UWFPS, VOC_Avg_GCFID_2013, VOC_Avg_GCFID_2014, VOC_Avg_GCFID_2015, VOC_Avg_ContBkgd, VOC_X = p Make/O/N=(vNumVOCs) VOC_StDev_PTRMS_UWFPS, VOC_StDev_GCFID_2013, VOC_StDev_GCFID_2014, VOC_StDev_GCFID_2015 VOC_Avg_ContBkgd = {1.1, 0.12, NaN, 0.081, 0.58, 0.06, NaN, 0.031, 0.012, NaN, NaN, NaN, NaN, NaN, 0.078, 0.016, 0.007, 0.005, NaN, NaN, NaN, 1.6, 0.58, 0.11, 0.078, 0.028, 0.038, 0.014, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 0.01, 0.017, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 0.03, 0.0063, 0.0045, 0.052, 0.012, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 0.3} Variable iDex, iSpeciesDex Variable vSum_2013, vSum_2014, vSum_2015 Variable vStDev_2013, vStDev_2014, vStDev_2015 For(iDex = 0; iDex < vNumVOCs; iDex += 1) String strCurrentName = twVOCName[iDex] FindValue/TEXT=strCurrentName/TXOP=2 twVOCName_UU If(V_value != -1) VOC_Avg_PTRMS_UWFPS[iDex] = wVOC_Avg_UU[V_value] VOC_StDev_PTRMS_UWFPS[iDex] = wVOC_StDev_UU[V_value] Else VOC_Avg_PTRMS_UWFPS[iDex] = NaN VOC_StDev_PTRMS_UWFPS[iDex] = NaN EndIf FindValue/TEXT=strCurrentName/TXOP=2 twVOCName_GCFID If(V_value != -1) VOC_Avg_GCFID_2013[iDex] = wVOC_Avg_GCFID_2013[V_value] VOC_Avg_GCFID_2014[iDex] = wVOC_Avg_GCFID_2014[V_value] VOC_Avg_GCFID_2015[iDex] = wVOC_Avg_GCFID_2015[V_value] VOC_StDev_GCFID_2013[iDex] = wVOC_StDev_GCFID_2013[V_value] VOC_StDev_GCFID_2014[iDex] = wVOC_StDev_GCFID_2014[V_value] VOC_StDev_GCFID_2015[iDex] = wVOC_StDev_GCFID_2015[V_value] ElseIf(StringMatch(strCurrentName, "C8Aromatics") == 1) vSum_2013 = 0 vSum_2014 = 0 vSum_2015 = 0 vStDev_2013 = 0 vStDev_2014 = 0 vStDev_2015 = 0 For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strC8Aromatics); iSpeciesDex += 1) String strCurrentSpecies = StringFromList(iSpeciesDex, strC8Aromatics) FindValue/TEXT=strCurrentSpecies/TXOP=2 twVOCName_GCFID If(V_value != -1) vSum_2013 += wVOC_Avg_GCFID_2013[V_value] vSum_2014 += wVOC_Avg_GCFID_2014[V_value] vSum_2015 += wVOC_Avg_GCFID_2015[V_value] vStDev_2013 += wVOC_StDev_GCFID_2013[V_value]^2 vStDev_2014 += wVOC_StDev_GCFID_2014[V_value]^2 vStDev_2015 += wVOC_StDev_GCFID_2015[V_value]^2 EndIf EndFor VOC_Avg_GCFID_2013[iDex] = vSum_2013 VOC_Avg_GCFID_2014[iDex] = vSum_2014 VOC_Avg_GCFID_2015[iDex] = vSum_2015 VOC_StDev_GCFID_2013[iDex] = sqrt(vStDev_2013) VOC_StDev_GCFID_2014[iDex] = sqrt(vStDev_2014) VOC_StDev_GCFID_2015[iDex] = sqrt(vStDev_2015) ElseIf(StringMatch(strCurrentName, "C9Aromatics") == 1) vSum_2013 = 0 vSum_2014 = 0 vSum_2015 = 0 vStDev_2013 = 0 vStDev_2014 = 0 vStDev_2015 = 0 For(iSpeciesDex = 0; iSpeciesDex < ItemsInList(strC9Aromatics); iSpeciesDex += 1) strCurrentSpecies = StringFromList(iSpeciesDex, strC9Aromatics) FindValue/TEXT=strCurrentSpecies/TXOP=2 twVOCName_GCFID If(V_value != -1) vSum_2013 += wVOC_Avg_GCFID_2013[V_value] vSum_2014 += wVOC_Avg_GCFID_2014[V_value] vSum_2015 += wVOC_Avg_GCFID_2015[V_value] vStDev_2013 += wVOC_StDev_GCFID_2013[V_value]^2 vStDev_2014 += wVOC_StDev_GCFID_2014[V_value]^2 vStDev_2015 += wVOC_StDev_GCFID_2015[V_value]^2 EndIf EndFor VOC_Avg_GCFID_2013[iDex] = vSum_2013 VOC_Avg_GCFID_2014[iDex] = vSum_2014 VOC_Avg_GCFID_2015[iDex] = vSum_2015 VOC_StDev_GCFID_2013[iDex] = sqrt(vStDev_2013) VOC_StDev_GCFID_2014[iDex] = sqrt(vStDev_2014) VOC_StDev_GCFID_2015[iDex] = sqrt(vStDev_2015) Else VOC_Avg_GCFID_2013[iDex] = NaN VOC_Avg_GCFID_2014[iDex] = NaN VOC_Avg_GCFID_2015[iDex] = NaN VOC_StDev_GCFID_2013[iDex] = NaN VOC_StDev_GCFID_2014[iDex] = NaN VOC_StDev_GCFID_2015[iDex] = NaN EndIf EndFor End //*************** // CCW code for processing historical VOC data in the SLV. Mostly taken from DSMACC_Analysis_v8.ipf Function LoadAndExtractHistoricalData() LoadGCFIDData() LoadUDAQPMData() ConcatenateYearlyUDAQPMData() Variable iDex For(iDex = 2013; iDex <=2015; iDex += 1) MakeWinterWaves(iDex) MakeGCFIDWindFlag(iDex) AverageBkgdVOCsGCFID(iDex) EndFor End Function LoadGCFIDData() //Loads the GC-FID 2012-2015 historical data from Munkh/UDAQ NewDataFolder/O/S root:Historical NewDataFolder/O/S root:Historical:GCFID String sGCFIDFilePath = "C:Users:cwomack:Documents:Research Projects:UWFPS:Halogens Analysis:Historical SLC data from Munkh:voc_others.csv" Printf "Loading historical GC-FID data from %s\r", sGCFIDFilePath LoadWave/Q/J/D/W/O/K=0/L={0,0,0,1,1}/A sGCFIDFilePath //Load timewave as double precision LoadWave/Q/J/W/O/K=0/L={0,0,0,3,0}/A sGCFIDFilePath //Load remainder of data as single precision KillWaves/Z doy_x, doy_y, Lead, PM10, PM25 //These are not useful Wave DateW, pW Duplicate/O DateW Timewave_MST //Change names of date and pressure waves Duplicate/O pW Pressure KillWaves/Z DateW, pW End Function LoadUDAQPMData() //Loads the UDAQ 2012-2015 historical PM2.5 data from various ground sites NewDataFolder/O/S root:Historical NewDataFolder/O/S root:Historical:UDAQPM Printf "Loading historical UDAQ PM2.5 data from:\r" Variable iDex For(iDex = 2012; iDex <= 2017; iDex += 1) //Load years between 2012 and 2017, inclusive Printf "\tC:Users:cwomack:Documents:Research Projects:UWFPS:Halogens Analysis:Historical SLC data from Munkh:%g-PM2.5.xlsx\r", iDex XLLoadWave/Q/S=num2str(iDex) + "-PM2.5"/W=1/COLT="1D1N"/D/O "C:Users:cwomack:Documents:Research Projects:UWFPS:Halogens Analysis:Historical SLC data from Munkh:" + num2str(iDex) + "-PM2.5.xlsx" Wave X_Date Variable vNumPnts = numpnts(X_Date) //Count the timewave points to make sure all waves match it String sWaveList = WaveList("!*_201*", ";", "") //Get all waves loaded that are not already processed and dated. Variable vNumWaves = ItemsInList(sWaveList) Variable iWaveDex For(iWaveDex = 0; iWaveDex < vNumWaves; iWaveDex += 1) //Cycle through unprocessed waves String sCurrentWave = StringFromList(iWaveDex, sWaveList) Wave wCurrentWave = $sCurrentWave Variable vCurrentPnts = numpnts(wCurrentWave) //Make sure number of points match If(vCurrentPnts != vNumPnts) //If the don't, it's because XLLLoadWave leaves of blank spaces at the end of a column. InsertPoints vCurrentPnts, (vNumPnts - vCurrentPnts), wCurrentWave wCurrentWave[vCurrentPnts,*] = NaN EndIf String sNewName = ReplaceString("X_", sCurrentWave, "") + "_" + num2str(iDex) //Rename without the X_ Duplicate/O wCurrentWave $sNewName KillWaves/Z wCurrentWave EndFor EndFor ConcatenateYearlyUDAQPMData() End Function ConcatenateYearlyUDAQPMData() //Put together multiple years of UDAQ PM2.5 data for various sites. //Have to string together data from various sites at each valley, since sites weren't always continuously gathering data. //HW and HA are in the SLV, L4 and SM are in Cache, LN and NP are in Utah Valley. //Note: In the end, I didn't really end up using this data for much, so choices for what to use for each valley aren't that important right now. SetDataFolder root:Historical:UDAQPM //First concatenate the timewave Wave Date_2012, Date_2013, Date_2014, Date_2015, Date_2016, Date_2017 Concatenate/NP=0/O {Date_2012, Date_2013, Date_2014, Date_2015, Date_2016, Date_2017}, Timewave_MST //For Salt Lake Valley, use HW data if available, then HA if not. Wave HW_2012, HW_2013, HW_2015, HW_2016, HW_2017 WAve HA_2012, HA_2013, HA_2014, HA_2015, HA_2016, HA_2017 Concatenate/NP=0/O {HW_2012, HW_2013, HA_2014, HW_2015, HW_2016, HW_2017}, PM25_SLC Concatenate/NP=0/O {HA_2012, HA_2013, HA_2014, HA_2015, HA_2016, HA_2017}, PM25_SLC_HA PM25_SLC = numtype(PM25_SLC[p]) == 2 ? PM25_SLC_HA[p] : PM25_SLC[p] KillWaves/Z PM25_SLC_HA //For Cache valley, use L4 data if available, then SM if not. Wave L4_2012, L4_2013, L4_2014, L4_2015 Wave SM_2015, SM_2016, SM_2017 Concatenate/NP=0/O {L4_2012, L4_2013, L4_2014, L4_2015, SM_2016, SM_2017}, PM25_Cache Concatenate/NP=0/O {L4_2012, L4_2013, L4_2014, SM_2015, SM_2016, SM_2017}, PM25_Cache_SM PM25_Cache = numtype(PM25_Cache[p]) == 2 ? PM25_Cache_SM[p] : PM25_Cache[p] KillWaves/Z PM25_Cache_SM //For Utah Valley, use LN data if available, then NP if not. Wave LN_2012, LN_2013, LN_2014, LN_2015, LN_2016, LN_2017 Wave NP_2012, NP_2013, NP_2014, NP_2015, NP_2016, NP_2017 Concatenate/NP=0/O {LN_2012, LN_2013, LN_2014, LN_2015, LN_2016, LN_2017}, PM25_Utah Concatenate/NP=0/O {NP_2012, NP_2013, NP_2014, NP_2015, NP_2016, NP_2017}, PM25_Utah_NP PM25_Utah = numtype(PM25_Utah[p]) == 2 ? PM25_Utah_NP[p] : PM25_Utah[p] KillWaves/Z PM25_Utah_NP End Function MakeWinterWaves(vYear) //Isolate just the winter PM25, VOC, and NOx data from vYear (2013, 2014, and 2015) Variable vYear NewDataFolder/O/S root:Historical:$("Winter_" + num2str(vYear)) //Make new folder to hold the winter data Variable iWaveDex //Make timewave Variable vStartWinter = Date2Secs(vYear - 1, 12, 01) //Define the winter as Dec, Jan, Feb, and March. Variable vStopWinter = Date2Secs(vYear, 03, 01) Variable vNumPnts = (vStopWinter - vStartWinter) / 3600 Make/O/N=(vNumPnts)/D $("Timewave_MST_" + num2str(vYear))/WAVE=wTimewave SetScale d, 0, 0, "dat", wTimewave wTimewave = vStartWinter + p*3600 Wave wTimewave_VOC = root:Historical:GCFID:Timewave_MST //Define timewaves Wave wTimewave_PM25 = root:Historical:UDAQPM:Timewave_MST Wave wPM25_SLC = root:Historical:UDAQPM:PM25_SLC Wave wPM25_Cache = root:Historical:UDAQPM:PM25_Cache Wave wPM25_Utah = root:Historical:UDAQPM:PM25_Utah Make/O/N=(vNumPnts) Index_VOC, Index_PM25 //Because the raw timewaves sometimes have gaps in them... Index_VOC = BinarySearch(wTimewave_VOC, wTimewave[p]) //...make an index wave that maps the regularly spaced timewave onto the raw timestamps Index_PM25 = BinarySearch(wTimewave_PM25, wTimewave[p]) FindDuplicates/SN=(NaN)/SNDS=Index_VOC_NaNs Index_VOC //Make a copy that has NaNs where there are duplicates (i.e. where there were gaps in the raw timewaves) FindDuplicates/SN=(NaN)/SNDS=Index_PM25_NaNs Index_PM25 //Concatenate and flag PM2.5 data Make/O/N=(vNumPnts) $("PM25_SLC_" + num2str(vYear))/WAVE=wPM25_SLC_Year //Make evenly spaced PM25 waves for each valley Make/O/N=(vNumPnts) $("PM25_Cache_" + num2str(vYear))/WAVE=wPM25_Cache_Year Make/O/N=(vNumPnts) $("PM25_Utah_" + num2str(vYear))/WAVE=wPM25_Utah_Year wPM25_SLC_Year = wPM25_SLC[Index_PM25_NaNs[p]] wPM25_Cache_Year = wPM25_Cache[Index_PM25_NaNs[p]] wPM25_Utah_Year = wPM25_Utah[Index_PM25_NaNs[p]] Make/O/N=(vNumPnts) $("Flag_PCAP_" + num2str(vYear) + "_SLC")/WAVE=wFlag_PCAP_SLC //Make evenly spaced PCAP flags Make/O/N=(vNumPnts) $("Flag_PCAP_" + num2str(vYear) + "_Cache")/WAVE=wFlag_PCAP_Cache Make/O/N=(vNumPnts) $("Flag_PCAP_" + num2str(vYear) + "_Utah")/WAVE=wFlag_PCAP_Utah wFlag_PCAP_SLC = (wPM25_SLC_Year[p] >= 35) ? 1 : 0 //Set the flag to whenever the PM25 is above 35 wFlag_PCAP_Cache = (wPM25_Cache_Year[p] >= 35) ? 1 : 0 wFlag_PCAP_Utah = (wPM25_Utah_Year[p] >= 35) ? 1 : 0 FilterBinaryWaveForSpikes(wFlag_PCAP_SLC, 24, 0, 1) //Then filter for only when the PM25 is above 35 for 24 hours FilterBinaryWaveForSpikes(wFlag_PCAP_SLC, 24, 1, 0) FilterBinaryWaveForSpikes(wFlag_PCAP_Cache, 24, 0, 1) FilterBinaryWaveForSpikes(wFlag_PCAP_Cache, 24, 1, 0) FilterBinaryWaveForSpikes(wFlag_PCAP_Utah, 24, 0, 1) FilterBinaryWaveForSpikes(wFlag_PCAP_Utah, 24, 1, 0) Make/O/N=(vNumPnts) $("Flag_PCAP_" + num2str(vYear))/WAVE=wFlag_PCAP //Make a master PCAP flag for when at least 2 of the 3 valleys are in PCAP conditions Make/O/N=(vNumPnts)/FREE TempPCAP TempPCAP = wFlag_PCAP_SLC + wFlag_PCAP_Cache + wFlag_PCAP_Utah wFlag_PCAP = TempPCAP[p] >= 2 ? 1 : 0 FilterBinaryWaveForSpikes(wFlag_PCAP, 48, 0, 1) FilterBinaryWaveForSpikes(wFlag_PCAP, 24, 1, 0) //Concatenate the VOC data SetDataFolder root:Historical:GCFID Variable vNumPntsVOC = numpnts(wTimewave_VOC) String sWaveList = WaveList("*", ";", "TEXT:0,MINROWS:" + num2str(vNumPntsVOC - 1) + ",MAXROWS:" + num2str(vNumPntsVOC + 1)) Variable vNumWaves = ItemsInList(sWaveList) SetDataFolder root:Historical:$("Winter_" + num2str(vYear)) For(iWaveDex = 0; iWaveDex < vNumWaves; iWaveDex += 1) //Make evenly spaced VOC waves String sCurrentWave = StringFromList(iWaveDex, sWaveList) If(StringMatch(sCurrentWave, "*Timewave_MST*") == 0) Wave wCurrentWave = root:Historical:GCFID:$(sCurrentWave) Make/O/N=(vNumPnts) $(sCurrentWave + "_" + num2str(vYear))/WAVE=wNewWave wNewWave = numtype(Index_VOC_NaNs[p]) == 0 ? wCurrentWave[Index_VOC_NaNs[p]] : NaN EndIf EndFor //Correct ppm to ppb Wave wNO = $("NO_" + num2str(vYear)) Wave wNO2 = $("NO2_" + num2str(vYear)) Wave wNOx = $("NOx_" + num2str(vYear)) Wave wO3 = $("O3_" + num2str(vYear)) Wave wCO = $("CO_" + num2str(vYear)) wNO *= 1000 wNO2 *= 1000 wNOx *= 1000 wO3 *= 1000 wCO *= 1000 KillWaves/Z Index_VOC, Index_VOC_NaNs, Index_PM25, Index_PM25_NaNs End Function MakeGCFIDWindFlag(vYear) Variable vYear SetDataFolder root:Historical:$("Winter_" + num2str(vYear)) Wave wTimewave = $("Timewave_MST_" + num2str(vYear)) Wave wWindSpd = $("WS_" + num2str(vYear)) Wave wWindDir = $("WD_" + num2str(vYear)) Make/O/N=(numpnts(wTimewave)) $("SouthWindFlag_" + num2str(vYear))/WAVE=wSouthWindFlag Make/O/N=(numpnts(wTimewave)) $("WestWindFlag_" + num2str(vYear))/WAVE=wWestWindFlag Make/O/N=(numpnts(wTimewave)) $("NorthWindFlag_" + num2str(vYear))/WAVE=wNorthWindFlag wNorthWindFlag = wWindSpd[p] > 7 && wWindDir[p] < 130 ? 1 : 0 wSouthWindFlag = wWindSpd[p] > 7 && wWindDir[p] < 240 && wWindDir[p] > 130 ? 1 : 0 wWestWindFlag = wWindSpd[p] > 7 && wWindDir[p] > 240 ? 1 : 0 End Function AverageBkgdVOCsGCFID(vYear) Variable vYear SetDataFolder root:Historical:$("Winter_" + num2str(vYear)) Wave SouthWindFlag = $("SouthWindFlag_" + num2str(vYear)) Wave WestWindFlag = $("WestWindFlag_" + num2str(vYear)) Wave NorthWindFlag = $("NorthWindFlag_" + num2str(vYear)) String strWavelist = WaveList("!*Flag*", ";", "") String strWavesToRemove = WaveList("!*_" + num2str(vYear), ";", "") + WaveList("Time*", ";", "") strWaveList = RemoveFromList(strWavesToRemove, strWaveList) Variable vNumWaves = ItemsInList(strWaveList) Make/O/N=(vNumWaves)/T twVOCName Make/O/N=(vNumWaves) wVOC_Avg_GCFID_South, wVOC_Avg_GCFID_West, wVOC_Avg_GCFID_North, wVOC_Avg_GCFID Make/O/N=(vNumWaves) wVOC_StDev_GCFID_South, wVOC_StDev_GCFID_West, wVOC_StDev_GCFID_North, wVOC_StDev_GCFID Make/O/N=(vNumWaves) wVOC_X = p Variable iDex For(iDex = 0; iDex < vNumWaves; iDex += 1) String strCurrentName = StringFromList(iDex, strWaveList) Wave wCurrentWave = $(strCurrentName) String strSpecies = ReplaceString("_" + num2str(vYear), strCurrentName, "") twVOCName[iDex] = strSpecies Make/O/N=(numpnts(wCurrentWave))/FREE TempWave TempWave = WestWindFlag[p] == 1 ? wCurrentWave[p] : NaN WaveStats/Q TempWave wVOC_Avg_GCFID_West[iDex] = V_Avg wVOC_StDev_GCFID_West[iDex] = V_sdev TempWave = SouthWindFlag[p] == 1 ? wCurrentWave[p] : NaN WaveStats/Q TempWave wVOC_Avg_GCFID_South[iDex] = V_Avg wVOC_StDev_GCFID_South[iDex] = V_sdev TempWave = NorthWindFlag[p] == 1 ? wCurrentWave[p] : NaN WaveStats/Q TempWave wVOC_Avg_GCFID_North[iDex] = V_Avg wVOC_StDev_GCFID_North[iDex] = V_sdev TempWave = (SouthWindFlag[p] == 1 || WestWindFlag[p] == 1) ? wCurrentWave[p] : NaN WaveStats/Q TempWave wVOC_Avg_GCFID[iDex] = V_Avg wVOC_StDev_GCFID[iDex] = V_sdev EndFor End //Old code