Passing non-waveform X data to all-at-once fitfunc

I do X-ray diffraction experiments, and I'm trying to fit my experimental data to determine the best-fit values of lattice parameters "a", "b", and "c". The complication is that the dependent variable, "q", depend on the lattice parameters as well as the Miller lattice indices, "h", "k", and "l". However, there are a finite number of allowed values for each set of h, k, and l. The idea below is that I pass 4 experimentally observed dependent variables, and Igor tries all 715 (a "13 choose 4" problem) combinations of 4 sets of h, k, and l from the 13 I hard-coded. It does that fine. I'm having trouble, though, with fitting each a, b, and c during each iteration to the observed peaks. I thought I could use the all-at-once fitting function, but it appears that it does not accept non-waveform X data. I would much appreciate any thoughts on a work-around, an alternative solution, or a way to pass the X data I generate in the for-loops (the Miller lattice index sets) to the fitting function. Apologies if this is unclear; I'd be happy to provide additional details.

Function O70LatPar(q_expt)

    Wave q_expt // pass a wave of experimental data
    Variable i = 0, j = 0, m = 0, n = 0
    Variable index = 1
    Variable V_FitError = 4  // suppress progress window
 
    Make/O h = {1,0,0,1,1,1,0,1,2,2,2,0,1} // h, k, and l give 13 allowed reflection planes for orthorhombic lattice
    Make/O k = {1,2,0,1,3,1,4,3,0,2,2,4,3}
    Make/O l = {1,2,4,3,1,5,0,3,2,0,2,4,5}
    Make/O/D latpar = {30,40,70} // lattice parameters--a, b, and c--of unit cell; must find "best fit" values

    For (i=0; i<13; i+=1) // for loops solve "13 choose 4" combination problem; that is, all 715 unique combinations of lattice index sets are tried in the fit function
            For (j=0; j<i; j+=1)
                For (m=0; m<j; m+=1)
                        For (n=0; n<m; n+=1)
                     
                            Make/O latind = {{h[i], h[j], h[m], h[n]}, {k[i], k[j], k[m], k[n]}, {l[i], l[j], l[m], l[n]}}
                            Duplicate/O latind, $("latind"+num2istr(index))
                                                                 
                            FuncFit/N/Q/NTHR=0 fitting, latpar, q_expt
                            Duplicate/O latpar, $("fit"+num2istr(index))
                           
                            Print index, latind // just for debugging
                            index += 1
                               
                        Endfor
                Endfor
            Endfor
    Endfor

End

Function fitting(latpar, q_theo, latind) : FitFunc

    Wave latpar, q_theo, latind
   
    q_theo[0] = 2*pi*((latind[0]/latpar[0])^2 + (latind[4]/latpar[1])^2 + (latind[8]/latpar[2])^2)^0.5
    q_theo[1] = 2*pi*((latind[1]/latpar[0])^2 + (latind[5]/latpar[1])^2 + (latind[9]/latpar[2])^2)^0.5
    q_theo[2] = 2*pi*((latind[2]/latpar[0])^2 + (latind[6]/latpar[1])^2 + (latind[10]/latpar[2])^2)^0.5
    q_theo[3] = 2*pi*((latind[3]/latpar[0])^2 + (latind[7]/latpar[1])^2 + (latind[11]/latpar[2])^2)^0.5
   
    Print q_theo[0], q_theo[1], q_theo[2], q_theo[3] // just for debugging
   
End
You have used the same name for two different things. Perhaps you think they are the same thing.

In the main function O70LatPar(), you compute a wave "latind". In the function "fitting" you have used the name "latind" in the position of the independent variable wave.

In the fit function, the independent variable wave contains the X values corresponding to the Y values passed in the FuncFit command in the position in the command where you have q_expt. Since your FuncFit command has no /X flag, the independent variable (or X values) come from the wave scaling of q_expt. But you can associate a separate wave of X values using the /X flag:

FuncFit/N/Q/NTHR=0 fitting, latpar, q_expt/X=myXwave

The X wave must have the same number of points as the Y wave, so in this case, myXwave must have the same number of points as q_expt.

I feel like I haven't answered your question, though. Perhaps try again with some more details in your explanation.

John Weeks
WaveMetrics, Inc.
support@wavemetrics.com
Thanks for replying to my post. I'll offer a few more details, though, because unfortunately it's not going to work the way I'm approaching the fitting.

The wave "latind" is actually a matrix at the moment. It comprises 4 sets of h, k, and l. Think of it as vectors X0 through X3 (e.g., X0 = {h0, k0, l0}). So, I'm actually passing 12 points to "fitting", and "q_expt" has only 4 points.

Here's how I've approached the problem manually in Excel (I attached a screenshot):

I know what values of h, k, and l are allowed by nature. I make a (hopefully) good guess as to which observed peaks (i.e., values of q_expt) correspond to certain sets of h, k, and l. I then run a simple sum-of-squared errors analysis to calculate the values of a, b, and c. I then manually switch which observed q values go with which sets of h, k, and l and repeat the analysis. I'm attempting to automate this process in Igor.

Is there a direct analogue to Excel's solver functionality in Igor? My original intention was to write a simple function (sum of (q_theo - q_expt)^2) and use Optimize to find the minimum, but it appears you can't vary parameters this way.

Any additional input will of course be appreciated. At this point, I either need a way to send my h, k, l matrix to my current all-at-once fitting function, a way to automate Excel's solver functionality, or a way to split up the problem so I can use FuncFit.
O70 Lattice Parameter Calculator.jpg (34.1 KB)
I'm still confused...

But it sounds like you need to fit a solution for a, b, and c for each of several sets of h, k, l. So you need to do several fits, and you need to pass in values of hkl that will be treated as constants in any given fit.

The easiest way to pass in constants is to add them to the coefficient wave. So in your case, you would have a six-point coefficient wave with a, b, c, h, k, l. Since h, k, l are constants, you would pre-set them to the appropriate values and hold them during the fit.

A somewhat cleaner way to do it would be to use a structure fit function and pass the values of h, k, l as additional members of the structure. This eliminates the possibly error-prone requirement of holding the coefficients that correspond to h,k,l.

John Weeks
WaveMetrics, Inc.
support@wavemetrics.com