Are there any prebuilt operations to optimize the fit of a simulation to experimental data?
I have written a simulation (of a cyclic voltammetry experiment) that I am comparing to experimentally collected data. Both the simulation and experiment yield y data (current) plotted as a function of a common x data series. The simulation is based on a finite difference method for solving differential equations.
Currently, I am manually changing 2-4 input variables into the simulation to optimize the quality of "fit" between the simulation and data. I would like to write a function of code to optimize the input variables. I cannot use a built-in fitting function for this because I cannot linearize the simulation (i.e., I cannot write y as a function of x).
Are there any builtin Igor functions to perform this optimization? I attached the code of the simulation below. The simulation currently declares the parameters inside it, but that could easily be changed.
Thanks,
Chris
// VARIABLES I WANT TO OPTIMIZE
Variable C = 1E-6; // moles/cm^3, Initial concentration of R.
Variable D = 8E-7; // cm^2/s, Diffusion coefficient
Variable k0 = 5E-2; // cm/s, electrochemical rate constant.
// OTHER VARIABLES THAT ARE EXPERIMENT DEPENDENT BUT FIXED
Variable Ehigh = 0.6; // V, high potential of scan
Variable Elow = -0.1; // V, low potential of scan
Variable E0 = 0.25; // V, formal reduction potential of the compound
Variable n = 1; // unitless, number of electrons transferred
Variable alpha = 0.1; // unitless, alpha
Variable kc = 0; // 1/s, rate constant for degredation of species R
Variable A = pi*(0.3/2)^2; // cm^2, Electrode area
// PHYSICAL CONSTANTS
Variable T = 298.15; // Temp
Variable F = 96485; // C/mol, Faraday's constant
Variable R = 8.3145; // J/(mol*K) Ideal gas constant
Variable f_ = F/(R*T); // 1/V, normalized Faraday's constant to room temp.
// SIMULATION VARIABLES
Variable L = 2000; // Number of time points in the scans. Larger numbers give smoother CVs, but take longer.
Variable DM = 0.45; // Model diffusion coefficient (keep below 0.5)
Variable j = ceil(4.2*sqrt(L))+5; // Number of distance boxes from electrode surface.
// Specify the scan rates to simulate (in V/s)
// Note that if you change N, it will alter the number of current waves.
// You'll need to re-adjust the plot.
Make /O /N=(6) scan;
scan = {0.001, 0.005, 0.01, 0.050, 0.1, 1};
// This makes a 1-d array of the following variables as a function of scan rate.
// tk us the total time of the CV (based on the potential window and scan rate)
Make /O /N=(numpnts(scan)) tk = 2*(Ehigh-Elow)/scan; // units = s
// Dt is the time length of each time step.
Make /O /N=(numpnts(scan)) Dt = tk/L; // units = s
// Dx is the width of each distance step from the electrode surface.
Make /O /N=(numpnts(scan)) Dx = sqrt(D*Dt/DM); // units = cm
// ktk is the dimensionless kinetic parameter
Make /O /N=(numpnts(scan)) ktk = kc*tk;
// km is the normalized diemnsionless kinetic parameter
Make /O /N=(numpnts(scan)) km = ktk/L;
Variable h1,h2; // These variables are used for the following for lop.
Make /O /N=(L+1,numpnts(scan)) k; // k is the time index (time point)
Make /O /N=(L+1,numpnts(scan)) t_;// t_ is the simulation time in seconds.
for (h1=0; h1<(L+1); h1++) // scanning through all time points.
for (h2=0; h2<(numpnts(scan)); h2++) // scanning through each scan rate
k[h1][h2] = h1; // this sets the time point number.
t_[h1][h2] = Dt[h2]*k[h1][h2]; // this sets the actual amount of time at each time point number as a function of scan rate.
endfor
endfor
Make /O /N=(L/2) E1 = Elow + 2*(Ehigh-Elow)*x/L; // This is the potential of the first scan direction at each time step.
Make /O /N=(L/2+1) E2 = Ehigh - 2*(Ehigh-Elow)*x/L; // This is the potential of the second scan direction at each time point.
Concatenate /NP /O {E1,E2},Escan; // This combines the potentials of the forward and reverse scans.
Make /O /N=(L+1) Enorm = Escan*f_; // This normalizes the calculated potential to the unitless value.
Make /O /N=(L+1) kf = k0*exp(-alpha*n*(Enorm-E0*f_)); // The forward rate constant as a function of potential.
Make /O /N=(L+1) kr = k0*exp((1-alpha)*n*(Enorm-E0*f_)); // The reverse rate constant as a function of potential.
Make /O /N=(L+1,j,numpnts(scan)) Ox = 0; // The initial concentration of O in solution normalized to 1.
Make /O /N=(L+1,j,numpnts(scan)) Red = 1; // The initial concentration of R in solution normalized to 1.
Make /O /N=(L+1,numpnts(scan)) JO = 0; // The flux matrix.
Make /O /N=(l+1) Current; // A temporary holder to calculate the current from the flux.
Variable i1,i2,iv;
for (iv=0; iv<numpnts(scan); iv++) // for-loop for different scan rates.
for (i1=0; i1<l; i1++) // for-loop for time steps.
for (i2=2; i2<j-1; i2++) // for-loop for distance steps.
// These represent diffusion from cell to cell.
Ox[i1+1][i2][iv] = Ox[i1][i2][iv] + DM*(Ox[i1][i2+1][iv]+Ox[i1][i2-1][iv]-2*Ox[i1][i2][iv]);
Red[i1+1][i2][iv] = Red[i1][i2][iv] + DM*(Red[i1][i2+1][iv]+Red[i1][i2-1][iv]-2*Red[i1][i2][iv]) - km[iv]*Red[i1][i2][iv];
endfor
// This the flux or O<-> as a function of the electrode reaction.
JO[i1+1][iv] = (kf[i1+1][iv]*Ox[i1+1][2][iv] - kr[i1+1]*Red[i1+1][2][iv]) / (1 + Dx[iv]/D*(kf[i1+1][iv] + kr[i1+1][iv]));
// This changes the concentration of O and R based on the flux.
Ox[i1+1][1][iv] = Ox[i1+1][2][iv] - JO[i1+1][iv]*(Dx[iv]/D);
// The km term in the red equation is in case R is degrading via an EC mechanism.
Red[i1+1][1][iv] = Red[i1+1][2][iv] + JO[i1+1][iv]*(Dx[iv]/D) - km[iv]*Red[i1+1][1][iv];
// Calculates current based on the flux - converted to mA by multiplying by 1000.
Current[i1] = -n*F*A*C*JO[i1+1][iv];
endfor
// This is a quick-fix for naming each the current at each scan rate as a function of scan rate to make 1-d waves.
String current_name = "current"+num2str(iv);
// This copies the "current" waves to its permanant wave "current[scan rate number]"
Duplicate/O Current, $current_name
endfor
// Deleting all the extra waves created during the simulation.
killwaves Current, Dt, Dx, Enorm, E1, E2, JO, k, kf, km, kr, ktk, Ox, Red, tk, t_;
End
You could try Optimize I guess.
April 19, 2020 at 10:29 am - Permalink
There's too much code that does non-obvious things for me to really dig in and figure out what you're doing. When you say that you "cannot write y as a function of x" what does that mean? Your simulation is an implicit function? Are you solving a system of ODE's?
You also say, "The simulation is based on a finite difference method for solving differential equations." Perhaps you haven't found our built-in IntegrateODE? DisplayHelpTopic "Solving Differential Equations"
With some trouble you can write a user-defined fitting function for a system solved by IntegrateODE.
April 20, 2020 at 10:47 am - Permalink