Concatenate produces null wave in procedure

Hello Everyone:

This should be simple but has me stumped.  Inside a procedure, I create a text wave to hold names of different waves that will be concatenated from subfolders:

    // list of waves to be concatenated for this experimenter (or condition).
    Make/FREE/T dataWaves = {"Re_data", "f_data", "f_calc"}
    Variable numWaves = numpnts( dataWaves)

Later I check to see if one of these waves exists, if not I then cycle over the subfolders concatenating the contained waves:

    Wave f_calc
       
    if (!WaveExists(f_calc))
        // - we must be at a top level datafolder (eg, Erken)
        //      need to cycle over subfolders & concatenate waves
        Variable numDFs = CountObjects(":",4)
        Variable n
       
        for (n=0; n<numDFs; n+=1)
            String expPath = GetIndexedObjName(":",4,n)
            SetDataFolder expPath
            for (i=0; i<numWaves; i+=1)
                String destWavePath = "::" + dataWaves[i]
                Concatenate/NP {$dataWaves[i]}, $destWavePath
            endfor
            SetDataFolder saveDF
        endfor
       
    endif

This works just as I intended.  All 3 data waves (f_calc, f_data and Re_data) are present and correctly filled with data from the subfolders.  But, when I try to use f_calc, it is null:

    Wave f_data
    Variable f_min = min(WaveMin(f_calc), WaveMin(f_data))

It has something to do with f_calc being referenced before it is created via the concatenate operation. I know that I must be making a simple error but...

NOTE: this was run using Igor 8.04 on Mac OS 11.6.1

Thanks for the help,

  Joe Kelly

 

Let us see all of the code.

I don't see where you are re-assigning WAVE f_calc anywhere.

Quote:
It has something to do with f_calc being referenced before it is created

Yes. Execute this for details:

DisplayHelpTopic "Put WAVE Declaration After Wave Is Created"

Thanks for looking at this.  The entire function is pasted below.  The reason that f_calc is declared before the concatenation is so that I can check for its existence.  Sometimes (depending on how experiments are organized in subfolders) the concatenation has already been done.

Thanks again,

   Joe

//
// - RunAll:    performs pressure drop calculations for all of the tests in the current
//                  datafolder, including any tests within subfolders.
//
//      Input Parameters:
//                          code        :   "PH" or "SAM"
//                          type        :   radial porosity treatment, either "Uniform", "White" or "Wall"
//                          model       :   "Ergun" or "KTA"
//
//      Output waves "dP_L" and "f_mod" are created in the appropriate data folders
//      with the code name and model appended (eg, dP_L_PH_Ergun).
//             
//      Graph of modified friction factor is created to show calculated values vs data.
//      Friction factor values are calculated for a range of Reynolds numbers and then  
//      interpolated to experimental value of Re_mod.
//
//      Provides statistical summary for code/model accuracy.
//
Function RunAll(code, type, model)
    String code             // either "PH" or "SAM"
    String type             // either "Uniform", "White" or "Wall"
    String model            // "Ergun" or "KTA"
   
    String saveDF = GetDataFolder(1)        // so we can return to current folder when finished

    // list of waves to be concatenated for this experimenter (or condition).
    Make/FREE/T dataWaves = {"Re_data", "f_data", "f_calc"}
    Variable numWaves = numpnts( dataWaves)
    Variable i
   
    // kill any destination waves that exist, otherwise the concatenation process
    //  will simply add the waves to the end of the existing ones.
    for (i=0; i<numWaves; i+=1)
        KillWaves/Z $dataWaves[i]
    endfor
   
    // run SAM or Pronghorn for all tests in this folder hierarchy
    doCalcs(saveDF, code, type, model)
   
    // gather up the data and calculation results
    getData(saveDF, type)

    Wave f_calc
       
    if (!WaveExists(f_calc))
        // - we must be at a top level datafolder (eg, Erken)
        //      need to cycle over subfolders & concatenate waves
        Variable numDFs = CountObjects(":",4)
        Variable n
       
        for (n=0; n<numDFs; n+=1)
            String expPath = GetIndexedObjName(":",4,n)
            SetDataFolder expPath
            for (i=0; i<numWaves; i+=1)
                String destWavePath = "::" + dataWaves[i]
                Concatenate/NP {$dataWaves[i]}, $destWavePath
            endfor
            SetDataFolder saveDF
        endfor
       
    endif
   
    // create graph for results of friction factor calcs:
    Wave f_data
    Variable f_min = min(WaveMin(f_calc), WaveMin(f_data))
    Variable f_max = max(WaveMax(f_calc), WaveMax(f_data))
    Make/O perf = {f_min,f_max}                     // provides data for perfect agreement line.

    Display/W=(159,44,1193,667) perf vs perf
    Label left "\\f01Calculated f\BM"
    Label bottom "\\f01Measured  f\BM"
    ModifyGraph lblMargin(left)=25,lblMargin(bottom)=11
    ModifyGraph standoff=0
    ModifyGraph tick=2,mirror=1
    ModifyGraph rgb=(0,0,0)
    ModifyGraph lsize=1.5
    ModifyGraph highTrip(left)=100000
    ModifyGraph highTrip(bottom)=100000
   
    String f_new = "f_" + code + "_" + type + "_" + model
    Duplicate/O f_calc, $f_new
   
    AppendToGraph $f_new vs f_data
    ModifyGraph mode($f_new)=3
    ModifyGraph marker($f_new)=2
    ModifyGraph mrkThick($f_new)=0.75
   
    // get error statistics
    Duplicate/FREE f_data, f_err            // tmp wave to hold relative error
    f_err = (f_calc - f_data)/f_data
   
    WaveStats/Q f_err       // get statistics for error wave
    String legendText = "\\f01\\JC Relative Error Statistics \r\\f00\\JL No. of data points: "
    legendText += num2str(V_npnts) + " \r Avg. Error: " + num2str(V_avg) + " \r RMS Error: " + num2str(V_rms)
    TextBox/C/N=textErr/A=RB legendText
   
    String name
    if (StringMatch(code, "PH"))
        name = "Pronghorn"
    elseif (StringMatch(code, "SAM"))
        name = "SAM"
    else
        name = ""
    endif
     
    legendText = "\\f01 Code: " + name + " \r Drag Model: " + model + " "
    TextBox/C/N=textCode/A=LT legendText
   
    KillWaves f_calc   
    SetDataFolder saveDF
End

 

My quick read says that when f_calc does not exist, it is also never created. If f_calc is created within doCalcs or getData, you might do better to return it explicitly from these functions rather than having it "hidden". Or, since you kill it at the end of this top level function anyway, why not just create it as a FREE wave to start?

First, if you declare a wave reference to a wave that my not exist, always use WAVE/Z instead of just WAVE. The /Z prevents the debugger from kicking in on a null wave reference. For details on this, execute:

DisplayHelpTopic "Runtime Lookup Failure And The Debugger"

You can solve the problem by adding this after the "endif" part of the "if (!WaveExists(f_calc))" block:

WAVE Re_data = $dataWaves[0]
WAVE f_data = $dataWaves[1]
WAVE f_calc = $dataWaves[2]

It is OK to redeclare a wave reference.

Here I did not use /Z because the waves are guaranteed to exist.

(Caveat: I don't understand what the Concatenate call is all about.)

 

 

THANKS!

I had forgotten (or never knew) that a wave reference can be redeclared.  Although I have used Igor since 1991, I only use it intermittently and so it is always a relearning experience.

Thanks again for a great product and support,

  Joe Kelly

PS - Thanks also to J. Weimer for looking at this.