# Problem using optimization for modeling

GROUPS:
 Paul McGill 3 Votes I'm trying use NMinimize to determine the parameters of a model, but I'm having problems using OutputResponse as part of the objective function. The model represents lithium-ion cells used to power an autonomous underwater vehicle. I can measure a real cell's voltage response to changes in output current, and I want to compare that output to the output of my model for a given set of model circuit parameters. If I can minimize the difference between the output of my model and the output of a real cell, then my model will be correct. In general, I'm trying to minimize the difference between two curves.I use OutputResponse to generate the output of my model. My problem is that OutputResponse can't simulate the response of a system that contains symbolic parameters. But my model parameters have to be symbolic so NMinimize can vary them. Here is my code (sorry for the long posting):Write the transfer function of the circuit that models the complex impedance of the cell.tf = (Rser + Rfast/((Rfast s Cfast) + 1) + Rslow/((Rslow s Cslow) + 1));Convert the tf into a transfer function model.tfm = TransferFunctionModel[{{tf}}, s];Generate a battery current profile.Ibatt = UnitStep[t - 1] - UnitStep[t - 5];Plot[Ibatt, {t, 0, 10}, Exclusions -> None]Assign values to the circuit parameters.vals = {Rser -> 0.1, Rfast -> 0.05, Cfast -> 10, Rslow -> 0.01, Cslow -> 100};Voc = 4.2;Find the output response for these circuit values and current profile.y = OutputResponse[tfm /. vals, Ibatt, {t, 10}];Plot[Voc - y, {t, 0, 10}]Define some different circuit values to get a different response and plot both responses.dvals = {Rser -> 0.06, Rfast -> 0.08, Cfast -> 15, Rslow -> 0.015, Cslow -> 50};dy = OutputResponse[tfm /. dvals, Ibatt, {t, 10}];Plot[{Voc - y, Voc - dy}, {t, 0, 10}]Find the area of the difference between the two responses.Plot[Abs[y - dy], {t, 0, 10}]NIntegrate[Abs[y - dy], {t, 0, 10}]{0.156543}I now have an objective function and can try modeling. Given only the first response curve as a template, find the circuit values that produced that response as a test of the modeling algorithm. Do this by minimizing the difference between the template and the output of a model whose circuit values are adjusted by NMinimize. In this case I already know the answer, but if it works then I can use measurements of an actual cell as my template. NMinimize[  NIntegrate[   Abs[    OutputResponse[tfm, Ibatt, {t, 0, 10}] - y   ], {t, 0, 10}  ], {Rser, Rfast, Cfast, Rslow, Cslow} ]  OutputResponse::symbs: Cannot simulate response of system StateSpaceModel[{{{0,1},{-(1/(Cfast Cslow Rfast Rslow)),-((Cfast Rfast+Cslow Rslow)/(Cfast Cslow Rfast Rslow))}},{{0},{1}},{{-(Rser/(Cfast Cslow Rfast Rslow))+(Rfast+Rser+Rslow)/(Cfast Cslow Rfast Rslow),-((Rser (Cfast Rfast+Cslow Rslow))/(Cfast Cslow Rfast Rslow))+(<<1>>+<<2>>+<<1>>)/<<1>>}},{{Rser}}},<<18>>->None] that contains symbolic parameter(s). >>As you can see, OutputResponse can't produce an output because it has symbolic parameters.How do I get NMinimize to set the values of the parameters before OutputResponse uses them? Will NMinimize work at all with this type of objective function?Thanks for any help,Paul
5 years ago
12 Replies
 Bill Simpson 3 Votes After evaluating all but your last block of code I look at the followingIn[14]:= tfmOut[14]= TransferFunctionModel[{{{Rfast + Rser + Rslow + Cfast*Rfast*Rser*s + Cfast*Rfast*Rslow*s + Cslow*Rfast*Rslow*s + Cslow*Rser*Rslow*s + Cfast*Cslow*Rfast*Rser*Rslow*s^2}}, (1 + Cfast*Rfast*s)*(1 + Cslow*Rslow*s)}, s]Thus I change your last block of code to be In[15]:= f[{Rse_?NumericQ, Rfa_?NumericQ, Cfa_?NumericQ, Rsl_?NumericQ, Csl_?NumericQ}] :=  (Print[{tfm /. {Rser -> Rse, Rfast -> Rfa, Cfast -> Cfa, Rslow -> Rsl, Cslow -> Csl}}]];    NIntegrate[Abs[OutputResponse[tfm/.{Rser->Rse,Rfast->Rfa,Cfast->Cfa,Rslow->Rsl,Cslow->Csl}, Ibatt, {t,0,10}]-y], {t,0,10}]  ); NMinimize[f[{Rser, Rfast, Cfast, Rslow, Cslow}], {Rser, Rfast, Cfast, Rslow, Cslow}]  During evaluation of In[16]:={TransferFunctionModel[{{{1.07688+0.15247*s-0.02527*s^2}},(1-0.07403*s)*(1+0.34557*s)},s]}  During evaluation of In[16]:= {TransferFunctionModel[{{{1.07688+0.15247*s-0.02527*s^2}},(1-0.07403*s)*(1+0.34557*s)},s]}During evaluation of In[16]:= NMinimize::nnum: The function value {1.19169*10^51} is not a number at{Cfast,Cslow,Rfast,Rser,Rslow} = {0.989531,0.284773,0.349234,0.987641,-0.259993}. >>Out[17]= NMinimize[f[{Rser, Rfast, Cfast, Rslow, Cslow}], {Rser, Rfast, Cfast, Rslow, Cslow}]I make some of those changes based on looking at your use of OutputResponse in a previous code block and the rest based on experimentation.By introducing a dummy function f and using ?NumericQ on each of the arguments I forced everything to be numeric to satisfy NIntegrate.By using :=(Print[],NIntegrate[]); I get a look at your integrand every time f is called.The error message from NMinimize blowing up may be because I've misused part of your code.Hopefully something in this will give you the needed clue to get to the next step.
5 years ago
 Shenghui Yang 4 Votes Though not recomended, you can still use NMinimize or FindMinimum in this way. Here is a piece of code that should work regardless of problematic numeric performance: Define the system and input: tf[Rser_, Rfast_, Cfast_, Rslow_, Cslow_, s_] := (Rser + Rfast/((Rfast s Cfast) + 1) + Rslow/((Rslow s Cslow) + 1)) tfm[Rser_, Rfast_, Cfast_, Rslow_, Cslow_, s_] :=    TransferFunctionModel[{{tf[Rser, Rfast, Cfast, Rslow, Cslow, s]}}, s] Ibatt = UnitStep[t - 1] - UnitStep[t - 5];Define responce y and responce f with parametery = OutputResponse[tfm[0.1, 0.05, 10, 0.01`, 100, t], Ibatt, {t, 10}]; f[Rser_?NumericQ, Rfast_?NumericQ, Cfast_?NumericQ, Rslow_?NumericQ, Cslow_?NumericQ] :=    First@NIntegrate[         Abs[OutputResponse[tfm[Rser, Rfast, Cfast, Rslow, Cslow, t],Ibatt, {t, 0, 10}] - y],         {t, 0, 10},         Method -> {"LocalAdaptive", "SymbolicProcessing" -> 0, Method -> {"TrapezoidalRule"}} ]You can use the optimization function here to find the close result: and use a dynamic variable to track the process: FindMinimum[         f[Rser, Rfast, Cfast, Rslow, Cslow],         {{Rser, 0.05}, {Rfast, 0.08}, {Cfast, 5}, {Rslow, 1/10}, {Cslow, 50}}, StepMonitor :> (x = Cslow)]Dynamic[x]The result I have for this set of value is not bad even though it halted due to poor convergence: During evaluation of In[30]:= FindMinimum::sdprec: Line search unable to find a sufficient decrease in the function value with MachinePrecision digit precision. >>Out[30]= {0.173594, {Rser -> 0.0555499, Rfast -> 0.0593808, Cfast -> 5., Rslow -> 0.0779124, Cslow -> 50.}}For this type of non-linear system, you may want to find some heuristical values first before you go ahead. Proper start value and boundaries for the parameters are very helpful in general.
5 years ago
 Paul McGill 2 Votes Bill - I really like your approach of using a dummy function. I don't understand how adding the pattern test to the function arguments forces them to be numbers. I thought that adding those qualifiers to function arguments tests the arguments and rejects them if they don't match, but it doesn't force them to be anything. Oh well, there's a lot about Mma I don't understand...I defined my dummy function as follows f[{Rse_?NumericQ, Rfa_?NumericQ, Cfa_?NumericQ, Rsl_?NumericQ, Csl_?NumericQ}] :=  (error =     NIntegrate[     Abs[OutputResponse[        tfm /. {Rser -> Rse, Rfast -> Rfa, Cfast -> Cfa, Rslow -> Rsl,           Cslow -> Csl}, Ibatt, {t, 0, 10}] - y     ], {t, 0, 10}    ];   Print[error, " ", Rser, " ", Rfast, " ", Cfast, " ", Rslow, " ", Cslow];)Unfortunately, when I run the minimization, even adding constraints, I getNMinimize[{f[{Rser, Rfast, Cfast, Rslow, Cslow}], Rser > 0,   Rser < 0.5, Rfast > 0, Rfast < 0.5, Cfast > 10, Cfast < 100,   Rslow > 0, Rslow < 0.5, Cslow > 50, Cslow < 200}, {Rser, Rfast, Cfast, Rslow, Cslow}]{1.74048} 0.485254 0.301249 93.0382 0.148942 135.54{1.74048} 0.485254 0.301249 93.0382 0.148942 135.54{1.74048} Rser Rfast Cfast Rslow CslowNMinimize::nnum: The function value Null is not a number at {Cfast,Cslow,Rfast,Rser,Rslow} = {93.0382,135.54,0.301249,0.485254,0.148942}. >>The good news is that NMinimize is working with numeric arguments. The bad news is that the arguments don't seem to change and then it fails after just two iterations. I'm not sure what's going on but will play with it some more. Thanks again for looking at it for me.Shenghui - Thank you also for taking the time to help with this. I'm going to try your suggestions tomorrow. Perhaps your method will work with constraints.
5 years ago
5 years ago
 Shenghui Yang 1 Vote @Bill, you are right:This often happens because something like NMinimize or Integrate might call your function first with just symbols instead of beginning with numeric values. The guess is that NMinimize or Integrate is "probing" or testing your function, to see if it can get a symbolic result back and save a lot of time testing with numerics.This is officially implemented via the HoldAll attribute of NMinimize and its siblings.
5 years ago
 Ilian Gachevski 3 Votes This is officially implemented via the HoldAll attribute of NMinimize and its siblings.NMinimize does not have the HoldAll attribute. FindMinimum does, but all that means is that the argument is not evaluated before being passed to function. Then, to quote the documentation, FindMinimum "first localizes the values of all variables, then evaluates f with the variables being symbolic, and then repeatedly evaluates the result numerically". The use of a black box objective function f (defined using _?NumericQ) prevents the symbolic evaluation step by keeping f unevaluated for non-numeric arguments.
5 years ago
5 years ago
 Todd Rowland 1 Vote It might be possible to get a close enough answer by searching the whole parameter space (in the spirit of Stephen Wolfram's "A New Kind of Science")This code will take a while, but it will finish in an overnight run.data = Table[f[{Rser, Rfast, Cfast, Rslow, Cslow}], {Rser, 0, .5, .1}, {Rfast, 0, .5, .1}, {Cfast, 10, 100, 10}, {Rslow, 0, .5, .1}, {Cslow, 50, 200, 30}]; {Min[data], Position[data, Min[data]]}That way you can get a better sense for what is going on by studying data, and also you get an answer with some feeling for its correctness.
5 years ago
 Paul McGill 1 Vote Progress update: I've managed to successfully run my test case, minimizing the error between my model output and my test output.Timing[NMinimize[{f[{Rser, Rfast, Cfast, Rslow, Cslow}],    0 < Rser < 0.5, 0 < Rfast < 0.5, 10 < Cfast < 100, 0 < Rslow < 0.5,    50 < Cslow < 200}, {Rser, Rfast, Cfast, Rslow, Cslow},   Method -> "DifferentialEvolution"]]{23648.140956, {0.00278763, {Rser -> 0.104464, Rfast -> 0.0426679,    Cfast -> 14.1031, Rslow -> 0.0126952, Cslow -> 50.}}}The process is pretty slow, taking over 6.5 hours to run, but at least it produced a decent result. Just to remind you, the correct values arevals = {Rser -> 0.1, Rfast -> 0.05, Cfast -> 10, Rslow -> 0.01,   Cslow -> 100}The resistor values are pretty close, but the capacitor values are 40% high and 50% low. However, if I compare the output of the optimized result with the correct output, it's not too bad.Plot[{Voc - y, Voc - yopt}, {t, 0, 10}]Plot[Abs[y - yopt], {t, 0, 10}, PlotRange -> All]Perhaps I can speed up the process using Todd's method above, which searches the whole parameter space. My algorithm was searching about two solutions per second. Todd's approach would need to look at 6 x 6 x 10 x 6 x 6 = 12,960 solutions, which would take about 1.8 hours. This might get me close to the optimum parameters, after which I could let NMinimize take over (assuming the parameter space is well enough behaved). This will be more important as a I attempt to model a real cell and don't know a priori what the correct model values are. I'll attempt to model some real test data next.