/*
 * lumped_li_battery_parameter_estimation.java
 */

import com.comsol.model.*;
import com.comsol.model.util.*;

/** Model exported on May 16 2026, 15:58 by COMSOL 6.4.0.422. */
public class lumped_li_battery_parameter_estimation {

  public static Model run() {
    Model model = ModelUtil.create("Model");

//    This tutorial consists of three parts. In the first part you will learn how to build a lumped battery model and run a simulation for a time-dependent battery current. In the second part you will perform parameter estimation using experimental data. In the third part, you will perform a prediction study using the optimized lumped parameter values that were obtained in the previous parameter estimation study, and compare with experimental data.
//    From the File menu, choose New.
//    In the New window, click Model Wizard.
//    In the Model Wizard window, click 0D.
//    In the Select Physics tree, select Electrochemistry > Batteries > Lumped Battery (lb).
//    Click Add.
//    Click Study.
//    In the Select Study tree, select General Studies > Time Dependent.
//    Click Done.

    model.component().create("comp1", true);

    model.component("comp1").physics().create("lb", "LumpedBattery");

    model.study().create("std1");
    model.study("std1").create("time", "Transient");

//    Import the model parameters from a text file.
//    In the Model Builder window, under Global Definitions, click Parameters 1.
//    In the Settings window for Parameters, locate the Parameters section.
//    Click Load from File.
//    Browse to the model's Application Library folder and double-click the file lumped_li_battery_parameter_estimation_parameters.txt.
//    To import content from file, use:
//    model.param().loadFile("FILENAME");
    model.param().set("eta_IR_1C", "10[mV]", "Ohmic overpotential at 1C, fitting parameter");
    model.param().set("invJ0", "1", "Inverse dimensionless charge exchange current, fitting parameter");
    model.param().set("tau", "1000[s]", "Diffusion time constant, fitting parameter");
    model.param().set("J0", "1/invJ0", "Dimensionless charge exchange current");
    model.param().set("Q_cell0", "12[A*h]", "Battery capacity");
    model.param().set("SOC_0", "0.3797", "Initial state of charge");
    model.param().set("T", "298.15[K]", "Temperature");

//    The battery current and the experimental cell voltage are time-dependent. Therefore you need to define these as variables. (The experimental cell voltage variable will only be used during postprocessing.)
//    In the Model Builder window, right-click Component 1 (comp1) > Definitions and choose Variables.

    model.component("comp1").variable().create("var1");

//    In the Settings window for Variables, locate the Variables section.
//    Click Load from File.
//    Browse to the model's Application Library folder and double-click the file lumped_li_battery_parameter_estimation_variables.txt.
//    To import content from file, use:
//    model.component("comp1").variable("var1").loadFile("FILENAME");
    model.component("comp1").variable("var1").set("I_cell_exp", "I_cell_exp(t)[A]", "Experimental cell current");
    model.component("comp1").variable("var1").set("E_cell_exp", "E_cell_exp(t)[V]", "Experimental cell voltage");

//    The expressions in the variable list are marked in orange, indicating unknown operators and functions. You will now proceed to add the missing interpolation function for the cell voltage and current versus time.
//    We will import the battery load and experimental cell voltage data as a table, and use this table both for defining time-dependent battery current and experimental voltage functions, as well as for the objective function used in the second part of the tutorial.
//    In the Model Builder window, expand the Results node.
//    Right-click Results > Tables and choose Table.

    model.result().table().create("tbl1", "Table");

//    In the Settings window for Table, type Load Cycle Data in the Label text field.

    model.result().table("tbl1").label("Load Cycle Data");

//    Locate the Data section.
//    Click Import.
//    Browse to the model's Application Library folder and double-click the file lumped_li_battery_parameter_estimation_E_I_vs_t_data.txt.

    model.result().table("tbl1").importData("lumped_li_battery_parameter_estimation_E_I_vs_t_data.txt");

//    The data file contains three different columns: Time, Current and Voltage.
//    In the Definitions toolbar, click Interpolation.

    model.component("comp1").func().create("int1", "Interpolation");

//    In the Settings window for Interpolation, type Interpolation - E and I vs. t in the Label text field.

    model.component("comp1").func("int1").label("Interpolation - E and I vs. t");

//    Locate the Definition section.
//    From the Data source list, select Result table.

    model.component("comp1").func("int1").set("source", "resultTable");

//    (Note that, instead of using the table, you could have imported the data file directly here too.)
//    Locate the Data Column Settings section.
//    In the table, click to select the cell at row number 1 and column number 1.
//    In the Unit text field, type s.

    model.component("comp1").func("int1").setIndex("argunit", "s", 0);

//    In the table, click to select the cell at row number 2 and column number 1.
//    In the Name text field, type E_cell_exp.

    model.component("comp1").func("int1").setEntry("funcnames", "col2", "E_cell_exp");

//    In the table, enter the following settings:

    model.component("comp1").func("int1").setEntry("columnType", "col3", "value");

//    In the Name text field, type I_cell_exp.

    model.component("comp1").func("int1").setEntry("funcnames", "col3", "I_cell_exp");

//    You will now start defining the battery model.
//    In the Model Builder window, under Component 1 (comp1), click Lumped Battery (lb).
//    In the Settings window for Lumped Battery, locate the Operation Mode section.
//    In the \[I_\textrm{app}\] text field, type I_cell_exp.

    model.component("comp1").physics("lb").prop("BatterySettings").set("I_app", "I_cell_exp");

//    Locate the Initial Capacity section.
//    In the \[Q_\textrm{cell,0}\] text field, type Q_cell0.

    model.component("comp1").physics("lb").prop("BatterySettings").set("Q_cell0", "Q_cell0");

//    Locate the Initial Cell Charge Distribution section.
//    In the \[\textrm{SOC}_\textrm{cell,0}\] text field, type SOC_0.

    model.component("comp1").physics("lb").prop("BatterySettings").set("SOC_cell0", "SOC_0");

//    <c>Q_cell0</c> and <c>SOC_0</c> were defined in the parameter text file you imported before.
//    (The <l>Battery volume</l> parameter is only used to calculate the heat source, in the unit W/m<sup>3</sup>, and is not needed in this model.)
//    Load the open circuit voltage data at the reference temperature from a text file. Note that in this model the reference temperature is same as the simulation temperature.
//    In the Model Builder window, under Component 1 (comp1) > Lumped Battery (lb), click Cell Equilibrium Potential 1.
//    In the Settings window for Cell Equilibrium Potential, locate the Open Circuit Voltage section.
//    Click Clear Table.

    model.component("comp1").physics("lb").feature("cep1").set("SOC_Eocv", new int[]{});
    model.component("comp1").physics("lb").feature("cep1").set("Eocv", new int[]{});

//    Note that it is important to clear the table before loading data from the text file.
//    Click Load from File.
//    Browse to the model's Application Library folder and double-click the file lumped_li_battery_parameter_estimation_E_OCP_data.txt.

    model.component("comp1").physics("lb").feature("cep1")
         .set("SOC_Eocv", new double[]{0, 0.01, 0.02, 0.030000000000000002, 0.04, 0.05, 0.060000000000000005, 0.07, 0.08, 0.09000000000000001, 0.1, 0.11, 0.12000000000000001, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18000000000000002, 0.19, 0.2, 0.21000000000000002, 0.22, 0.23, 0.24000000000000002, 0.25, 0.26, 0.27, 0.28, 0.29000000000000004, 0.3, 0.31, 0.32, 0.33, 0.34, 0.35000000000000003, 0.36000000000000004, 0.37, 0.38, 0.39, 0.4, 0.41000000000000003, 0.42000000000000004, 0.43, 0.44, 0.45, 0.46, 0.47000000000000003, 0.48000000000000004, 0.49000000000000005, 0.5, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.5700000000000001, 0.5800000000000001, 0.5900000000000001, 0.6, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.6900000000000001, 0.7000000000000001, 0.7100000000000001, 0.7200000000000001, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.8, 0.81, 0.8200000000000001, 0.8300000000000001, 0.8400000000000001, 0.8500000000000001, 0.86, 0.87, 0.88, 0.89, 0.9, 0.91, 0.92, 0.93, 0.9400000000000001, 0.9500000000000001, 0.9600000000000001, 0.9700000000000001, 0.9800000000000001, 0.9900000000000001, 1});
    model.component("comp1").physics("lb").feature("cep1")
         .set("Eocv", new double[]{1.3104190837885064, 2.2611069048882873, 2.8334213300309643, 3.1844362423678003, 3.393016295869783, 3.510875145406253, 3.5869463584273378, 3.6264514929037994, 3.6557565420023983, 3.6850615911009976, 3.6994551120045407, 3.7113345164250635, 3.7232139208455868, 3.7350933252661096, 3.746972729686633, 3.7588394760123167, 3.7669805471052284, 3.7751216181981406, 3.7832626892910524, 3.7914037603839645, 3.7995448314768763, 3.8076859025697884, 3.8158269736627, 3.8239680447556124, 3.832109115848524, 3.8402384951548894, 3.844926614135922, 3.8496147331169555, 3.8543028520979887, 3.8589909710790216, 3.863679090060055, 3.8683672090410877, 3.873055328022121, 3.877743447003154, 3.882431565984187, 3.8871182387192977, 3.891379235814907, 3.8956402329105164, 3.900108284697505, 3.9048921849677303, 3.910004858946812, 3.9155565350495363, 3.9213824207219656, 3.927676048105941, 3.9340438371441717, 3.94051108662678, 3.9470234377800457, 3.9533348394394214, 3.9592735197022777, 3.9650124251831564, 3.970366661250179, 3.975134457606617, 3.979408469784772, 3.9832924969139087, 3.986400783870235, 3.989209391104789, 3.9920179983393425, 3.994805300205646, 3.9971563232603926, 3.9995073463151387, 4.001858369369885, 4.004209392424632, 4.006560415479378, 4.008911438534124, 4.011262461588871, 4.014107842011973, 4.017168121841405, 4.020228401670838, 4.02328868150027, 4.0263489613297025, 4.029409241159136, 4.032469520988568, 4.03470071596249, 4.035374828848886, 4.036048941735283, 4.036723054621679, 4.037397167508075, 4.038071280394472, 4.038745393280867, 4.039419506167263, 4.039965701342635, 4.040511627548762, 4.041057553754889, 4.041603479961017, 4.042149406167145, 4.042695332373272, 4.0432412585794, 4.04546697590993, 4.048603777764223, 4.0517405796185155, 4.054877381472807, 4.0580141833271, 4.061150985181393, 4.064287787035685, 4.069434668295399, 4.079298736570241, 4.091601063364331, 4.110991916734893, 4.13395738036637, 4.167078533171239, 4.204442586248173});

//    In the \[T_\textrm{ref}\] text field, type T.

    model.component("comp1").physics("lb").feature("cep1").set("Tref", "T");

//    Note that in this node you may also add data for the temperature derivative of open circuit voltage, that is used to calculate the temperature dependence of the open circuit voltage. Additionally, this data is used in the calculation of the reversible (entropic) contribution and heat of mixing contribution to the total heat source. However, this data is not needed in this model.
//    Keep the default values for the <l>Voltage Losses</l> parameters for now, but enable also the concentration overpotential.
//    In the Model Builder window, click Voltage Losses 1.
//    In the Settings window for Voltage Losses, locate the Concentration Overpotential section.
//    Select the Include concentration overpotential checkbox.

    model.component("comp1").physics("lb").feature("vl1").set("IncludeConcentrationOverpotential", true);

//    The battery model is now ready for solving.
//    In the Model Builder window, click Study 1.
//    In the Settings window for Study, type Study 1 - Load Curve Simulation in the Label text field.

    model.study("std1").label("Study 1 - Load Curve Simulation");

//    In the Model Builder window, under Study 1 - Load Curve Simulation, click Step 1: Time Dependent.
//    In the Settings window for Time Dependent, locate the Study Settings section.
//    In the Output times text field, type range(0,1,300).

    model.study("std1").feature("time").set("tlist", "range(0,1,300)");

//    The above setting tells the solver to run a simulation for 300 s and store the solution every second.
//    From the Tolerance list, select User controlled.

    model.study("std1").feature("time").set("usertol", true);

//    In the Relative tolerance text field, type 0.001.

    model.study("std1").feature("time").set("rtol", 0.001);

//    In the Study toolbar, click Compute.

    model.study("std1").createAutoSequences("all");

    model.sol("sol1").runAll();

    model.result().create("pg1", "PlotGroup1D");
    model.result("pg1").label("Cell Potential and Load (lb)");
    model.result("pg1").set("ylabel", "Cell potential (V)");
    model.result("pg1").set("twoyaxes", true);
    model.result("pg1").set("legendpos", "lowerleft");
    model.result("pg1").set("smooth", "internal");
    model.result("pg1").feature().create("glob1", "Global");
    model.result("pg1").feature("glob1").set("expr", new String[]{"lb.E_cell"});
    model.result("pg1").feature("glob1").set("data", "parent");
    model.result("pg1").feature().create("glob2", "Global");
    model.result("pg1").feature("glob2").set("expr", new String[]{"lb.Eocv_cell"});
    model.result("pg1").feature("glob2").set("data", "parent");
    model.result("pg1").feature().create("glob3", "Global");
    model.result("pg1").feature("glob3").set("plotonsecyaxis", true);
    model.result("pg1").feature("glob3").set("expr", new String[]{"lb.I_cell"});
    model.result("pg1").feature("glob3").set("data", "parent");
    model.result().create("pg2", "PlotGroup1D");
    model.result("pg2").label("Cell State of Charge (lb)");
    model.result("pg2").set("twoyaxes", true);
    model.result("pg2").set("legendpos", "lowerleft");
    model.result("pg2").set("smooth", "internal");
    model.result("pg2").feature().create("glob1", "Global");
    model.result("pg2").feature("glob1").set("expr", new String[]{"lb.SOC_cell"});
    model.result("pg2").feature("glob1").set("data", "parent");
    model.result("pg2").feature().create("glob2", "Global");
    model.result("pg2").feature("glob2").set("plotonsecyaxis", true);
    model.result("pg2").feature("glob2").set("expr", new String[]{"lb.I_cell"});
    model.result("pg2").feature("glob2").set("data", "parent");
    model.result("pg1").run();

//    A number of plots were created by default. You will now modify the first plot to compare the modeled cell voltage with the experimental data.
//    In the Model Builder window, expand the Results > Cell Potential and Load (lb) node, then click Cell Potential and Load (lb).
//    In the Settings window for 1D Plot Group, type Cell Voltage in the Label text field.

    model.result("pg1").label("Cell Voltage");

//    Click to expand the Title section.
//    From the Title type list, select None.

    model.result("pg1").set("titletype", "none");

//    Locate the Plot Settings section.
//    Clear the Two y-axes checkbox.

    model.result("pg1").set("twoyaxes", false);

//    Locate the Legend section.
//    From the Position list, select Lower right.

    model.result("pg1").set("legendpos", "lowerright");
    model.result("pg1").run();

//    In the Model Builder window, click Global 1.
//    In the Settings window for Global, locate the y-Axis Data section.
//    In the table, enter the following settings:

    model.result("pg1").feature("glob1").setIndex("descr", "Modeled cell voltage", 0);
    model.result("pg1").run();

//    In the Model Builder window, click Global 3.
//    In the Settings window for Global, click Replace Expression in the upper-right corner of the y-Axis Data section.
//    From the menu, choose Component 1 (comp1) > Definitions > Variables > E_cell_exp - Experimental cell voltage - V.

    model.result("pg1").feature("glob3").set("expr", new String[]{"E_cell_exp"});
    model.result("pg1").feature("glob3").set("descr", new String[]{"Experimental cell voltage"});
    model.result("pg1").feature("glob3").set("unit", new String[]{"V"});
    model.result("pg1").run();

//    In the Model Builder window, click Cell Voltage.
//    In the Settings window for 1D Plot Group, locate the Axis section.
//    Select the Manual axis limits checkbox.

    model.result("pg1").set("axislimits", true);

//    In the x maximum text field, type 305.

    model.result("pg1").set("xmax", 305);

//    In the y minimum text field, type 3.35.

    model.result("pg1").set("ymin", 3.35);

//    In the y maximum text field, type 4.2.

    model.result("pg1").set("ymax", 4.2);

//    In the Cell Voltage toolbar, click Plot.

    model.result("pg1").run();
    model.result("pg2").run();

//    Also a state of charge versus time plot was created by default:
//    In the Model Builder window, click Cell State of Charge (lb).
//    Proceed as follows to create a plot that compares the different voltage losses in the model:
//    In the Results toolbar, click 1D Plot Group.

    model.result().create("pg3", "PlotGroup1D");
    model.result("pg3").run();

//    In the Settings window for 1D Plot Group, type Voltage Losses and Load in the Label text field.

    model.result("pg3").label("Voltage Losses and Load");

//    Right-click Voltage Losses and Load and choose Global.

    model.result("pg3").create("glob1", "Global");
    model.result("pg3").feature("glob1").set("markerpos", "datapoints");
    model.result("pg3").feature("glob1").set("linewidth", "preference");

//    In the Settings window for Global, click Replace Expression in the upper-right corner of the y-Axis Data section.
//    From the menu, choose Component 1 (comp1) > Lumped Battery > Overpotentials > lb.eta_ir - Ohmic overpotential - V.

    model.result("pg3").feature("glob1").set("expr", new String[]{"lb.eta_ir"});
    model.result("pg3").feature("glob1").set("descr", new String[]{"Ohmic overpotential"});
    model.result("pg3").feature("glob1").set("unit", new String[]{"V"});

//    Click Add Expression in the upper-right corner of the y-Axis Data section.
//    From the menu, choose Component 1 (comp1) > Lumped Battery > Overpotentials > lb.eta_act - Activation overpotential - V.

    model.result("pg3").feature("glob1").set("expr", new String[]{"lb.eta_ir", "lb.eta_act"});
    model.result("pg3").feature("glob1")
         .set("descr", new String[]{"Ohmic overpotential", "Activation overpotential"});

//    Click Add Expression in the upper-right corner of the y-Axis Data section.
//    From the menu, choose Component 1 (comp1) > Lumped Battery > Overpotentials > lb.eta_conc - Concentration overpotential - V.

    model.result("pg3").feature("glob1").set("expr", new String[]{"lb.eta_ir", "lb.eta_act", "lb.eta_conc"});
    model.result("pg3").feature("glob1")
         .set("descr", new String[]{"Ohmic overpotential", "Activation overpotential", "Concentration overpotential"});

//    Locate the x-Axis Data section.
//    From the Parameter list, select Expression.

    model.result("pg3").feature("glob1").set("xdata", "expr");

//    In the Expression text field, type t.

    model.result("pg3").feature("glob1").set("xdataexpr", "t");
    model.result("pg3").run();

//    In the Model Builder window, right-click Voltage Losses and Load and choose Global.

    model.result("pg3").create("glob2", "Global");
    model.result("pg3").feature("glob2").set("markerpos", "datapoints");
    model.result("pg3").feature("glob2").set("linewidth", "preference");

//    In the Settings window for Global, click Replace Expression in the upper-right corner of the y-Axis Data section.
//    From the menu, choose Component 1 (comp1) > Lumped Battery > lb.I_cell - Cell current - A.

    model.result("pg3").feature("glob2").set("expr", new String[]{"lb.I_cell"});
    model.result("pg3").feature("glob2").set("descr", new String[]{"Cell current"});
    model.result("pg3").feature("glob2").set("unit", new String[]{"A"});

//    Locate the x-Axis Data section.
//    From the Parameter list, select Expression.

    model.result("pg3").feature("glob2").set("xdata", "expr");

//    In the Expression text field, type t.

    model.result("pg3").feature("glob2").set("xdataexpr", "t");

//    Click to expand the Coloring and Style section.
//    Find the Line style subsection.
//    From the Line list, select Dotted.

    model.result("pg3").feature("glob2").set("linestyle", "dotted");

//    From the Color list, select Black.

    model.result("pg3").feature("glob2").set("linecolor", "black");
    model.result("pg3").run();

//    In the Model Builder window, click Voltage Losses and Load.
//    In the Settings window for 1D Plot Group, locate the Title section.
//    From the Title type list, select None.

    model.result("pg3").set("titletype", "none");

//    Locate the Plot Settings section.
//    Select the y-axis label checkbox.

    model.result("pg3").set("ylabelactive", true);

//    In the associated text field, type Overvoltage (V).

    model.result("pg3").set("ylabel", "Overvoltage (V)");

//    Select the Two y-axes checkbox.

    model.result("pg3").set("twoyaxes", true);

//    In the table, enter the following settings:

    model.result("pg3").setIndex("plotonsecyaxis", true, 1, 1);

//    Locate the Axis section.
//    Select the Manual axis limits checkbox.

    model.result("pg3").set("axislimits", true);

//    In the x maximum text field, type 305.

    model.result("pg3").set("xmax", 305);

//    In the y minimum text field, type -0.4.

    model.result("pg3").set("ymin", -0.4);

//    In the y maximum text field, type 0.2.

    model.result("pg3").set("ymax", 0.2);

//    In the Secondary y minimum text field, type -350.

    model.result("pg3").set("yminsec", -350);

//    In the Secondary y maximum text field, type 200.

    model.result("pg3").set("ymaxsec", 200);

//    Locate the Legend section.
//    From the Position list, select Lower left.

    model.result("pg3").set("legendpos", "lowerleft");

//    In the Voltage Losses and Load toolbar, click Plot.

    model.result("pg3").run();

//    In the Model Builder window, under Component 1 (comp1) > Lumped Battery (lb), click Voltage Losses 1.
//    In the Settings window for Voltage Losses, locate the Model Input section.
//    In the \[T\] text field, type T.

    model.component("comp1").physics("lb").feature("vl1").set("minput_temperature", "T");

//    Now change the default values for the voltage losses to use values defined in the <l>Parameters</l> node instead.
//    Locate the Ohmic Overpotential section.
//    In the \[\eta_\textrm{IR,1C}\] text field, type eta_IR_1C.

    model.component("comp1").physics("lb").feature("vl1").set("eta_ir1C", "eta_IR_1C");

//    Locate the Activation Overpotential section.
//    In the \[J_0\] text field, type J0.

    model.component("comp1").physics("lb").feature("vl1").set("J0", "J0");

//    Locate the Concentration Overpotential section.
//    In the \[\tau\] text field, type tau.

    model.component("comp1").physics("lb").feature("vl1").set("tau", "tau");

//    The first part of the tutorial is now complete. In the second part you will learn how to run an optimization solver to perform an estimation of the different voltage loss parameters. Start by setting up the optimization solver in a new study node.
//    In the Home toolbar, click Add Study to open the Add Study window.
//    Find the Studies subsection.
//    In the Select Study tree, select General Studies > Time Dependent.
//    Click Add Study in the window toolbar.

    model.study().create("std2");
    model.study("std2").create("time", "Transient");

//    In the Home toolbar, click Add Study to close the Add Study window.
//    In the Settings window for Study, type Study 2 - Parameter Estimation in the Label text field.

    model.study("std2").label("Study 2 - Parameter Estimation");

//    Locate the Study Settings section.
//    Clear the Generate default plots checkbox.

    model.study("std2").setGenPlots(false);

//    The <l>Parameter Estimation</l> study step is used to construct the objective function that is to be minimized by the optimization solver. The objective function in this case will equal the sum of the squared differences between the modeled and the experimental cell voltages, for all stored times in the data.
//    In the Study toolbar, click Optimization and choose Parameter Estimation.

    model.study("std2").create("lsqo", "LSQOptimization");

//    In the Settings window for Parameter Estimation, locate the Experimental Data section.
//    From the Data source list, select Result table.

    model.study("std2").feature("lsqo").set("source", "resultTable");

//    Note that you can also import a data file directly here instead of using the table.
//    Locate the Data Column Settings section.
//    In the table, click to select the cell at row number 2 and column number 3.
//    The <l>Model expression</l> tells what value in the model the data corresponds to.
//    In the Model expression text field, type comp1.lb.E_cell.

    model.study("std2").feature("lsqo").setEntry("modelExpression", "col2", "comp1.lb.E_cell");

//    In the Unit text field, type V.

    model.study("std2").feature("lsqo").setEntry("unit", "col2", "V");

//    In the table, enter the following settings:

    model.study("std2").feature("lsqo").setEntry("columnType", "col3", "none");

//    Now define what parameters (control variables) we should run the parameter estimation for:
//    Locate the Estimated Parameters section.
//    Click Add.

    model.study("std2").feature("lsqo").setIndex("pname", "eta_IR_1C", 0);
    model.study("std2").feature("lsqo").setIndex("initval", "10[mV]", 0);
    model.study("std2").feature("lsqo").setIndex("scale", 1, 0);
    model.study("std2").feature("lsqo").setIndex("lbound", "", 0);
    model.study("std2").feature("lsqo").setIndex("ubound", "", 0);
    model.study("std2").feature("lsqo").setIndex("punit", "", 0);
    model.study("std2").feature("lsqo").setIndex("pname", "eta_IR_1C", 0);
    model.study("std2").feature("lsqo").setIndex("initval", "10[mV]", 0);
    model.study("std2").feature("lsqo").setIndex("scale", 1, 0);
    model.study("std2").feature("lsqo").setIndex("lbound", "", 0);
    model.study("std2").feature("lsqo").setIndex("ubound", "", 0);
    model.study("std2").feature("lsqo").setIndex("punit", "", 0);

//     three times.

    model.study("std2").feature("lsqo").setIndex("pname", "invJ0", 1);
    model.study("std2").feature("lsqo").setIndex("initval", 1, 1);
    model.study("std2").feature("lsqo").setIndex("scale", 1, 1);
    model.study("std2").feature("lsqo").setIndex("lbound", "", 1);
    model.study("std2").feature("lsqo").setIndex("ubound", "", 1);
    model.study("std2").feature("lsqo").setIndex("punit", "", 1);
    model.study("std2").feature("lsqo").setIndex("pname", "invJ0", 1);
    model.study("std2").feature("lsqo").setIndex("initval", 1, 1);
    model.study("std2").feature("lsqo").setIndex("scale", 1, 1);
    model.study("std2").feature("lsqo").setIndex("lbound", "", 1);
    model.study("std2").feature("lsqo").setIndex("ubound", "", 1);
    model.study("std2").feature("lsqo").setIndex("punit", "", 1);
    model.study("std2").feature("lsqo").setIndex("pname", "tau", 2);
    model.study("std2").feature("lsqo").setIndex("initval", "1000[s]", 2);
    model.study("std2").feature("lsqo").setIndex("scale", 1, 2);
    model.study("std2").feature("lsqo").setIndex("lbound", "", 2);
    model.study("std2").feature("lsqo").setIndex("ubound", "", 2);
    model.study("std2").feature("lsqo").setIndex("punit", "", 2);
    model.study("std2").feature("lsqo").setIndex("pname", "tau", 2);
    model.study("std2").feature("lsqo").setIndex("initval", "1000[s]", 2);
    model.study("std2").feature("lsqo").setIndex("scale", 1, 2);
    model.study("std2").feature("lsqo").setIndex("lbound", "", 2);
    model.study("std2").feature("lsqo").setIndex("ubound", "", 2);
    model.study("std2").feature("lsqo").setIndex("punit", "", 2);

//    There should now be three control variables present in the table. In order to improve the optimization you need to provide suitable scales for these (the <l>Scale</l> column in the table).
//    In the table, enter the following settings:

    model.study("std2").feature("lsqo").setIndex("scale", 0.01, 0);
    model.study("std2").feature("lsqo").setIndex("scale", 1000, 2);

//    The Lower/Upper bound columns are not used in this model. They may be used to put bounds on the control variables during the optimization.
//    The <l>Levenberg-Marquardt</l> is suitable for global least-squares problems.
//    Locate the Parameter Estimation Method section.
//    From the Least-squares time/parameter list method list, select Use only least-squares data points.

    model.study("std2").feature("lsqo").set("lsqdatamethod", "lsq");

//    By adding probes for the fitting parameters (the control variables) you can monitor how these change during the optimization.
//    In the Model Builder window, right-click Component 1 (comp1) > Definitions and choose Global Variable Probe.

    model.component("comp1").probe().create("var1", "GlobalVariable");

//    In the Settings window for Global Variable Probe, locate the Expression section.
//    In the Expression text field, type eta_IR_1C.

    model.component("comp1").probe("var1").set("expr", "eta_IR_1C");

//    Right-click Definitions and choose Global Variable Probe.

    model.component("comp1").probe().create("var2", "GlobalVariable");

//    In the Settings window for Global Variable Probe, locate the Expression section.
//    In the Expression text field, type invJ0.

    model.component("comp1").probe("var2").set("expr", "invJ0");

//    Right-click Definitions and choose Global Variable Probe.

    model.component("comp1").probe().create("var3", "GlobalVariable");

//    In the Settings window for Global Variable Probe, locate the Expression section.
//    In the Expression text field, type tau.

    model.component("comp1").probe("var3").set("expr", "tau");

//    The parameter estimation problem is now ready for solving. Since the model will run multiple times in order to find the minimum of the objective function, this computation will take a little longer (about a minute) to run than the first study.
//    In the Study toolbar, click Compute.

    model.study("std2").createAutoSequences("all");

    model.component("comp1").probe("var1").genResult("none");
    model.component("comp1").probe("var2").genResult("none");
    model.component("comp1").probe("var3").genResult("none");

    model.study("std2").feature("lsqo").set("continuecontrolparams", new String[]{});
    model.study("std2").feature("lsqo").set("continuecontrolvals", new double[]{});
    model.study("std2").feature("lsqo").set("continuelagrangevals", new double[]{});
    model.study("std2").feature("lsqo").set("continuelagrangeparams", new String[]{});

    model.sol("sol2").runAll();

    model.study("std2").feature("lsqo").set("probewindow", "");

    model.result("pg1").run();

//    In the Model Builder window, under Results, click Cell Voltage.
//    In the Settings window for 1D Plot Group, locate the Data section.
//    From the Dataset list, select Study 2 - Parameter Estimation/Solution 2 (sol2).

    model.result("pg1").set("data", "dset2");

//    In the Cell Voltage toolbar, click Plot.

    model.result("pg1").run();
    model.result("pg3").run();

//    In the Model Builder window, click Voltage Losses and Load.
//    In the Settings window for 1D Plot Group, locate the Data section.
//    From the Dataset list, select Study 2 - Parameter Estimation/Solution 2 (sol2).

    model.result("pg3").set("data", "dset2");

    return model;
  }

  public static Model run2(Model model) {

//    In the Voltage Losses and Load toolbar, click Plot.

    model.result("pg3").run();

//    The cell voltage plot can be set as output while solving to monitor the optimization process in the graphics window during computation.
//    In the Model Builder window, under Study 2 - Parameter Estimation, click Parameter Estimation.
//    In the Settings window for Parameter Estimation, click to expand the Output section.
//    Select the Plot checkbox.

    model.study("std2").feature("lsqo").set("plot", true);

//    You may now try to recompute the solution to see how the experimental and model cell voltage curves approach each other during optimization.
//    The second part of the tutorial is now complete. The final part is to set up a new study for cell voltage prediction. Note that the previous two studies used a 300 s load cycle data. For the prediction study, a full load cycle with additional 300 s will be used. First, we will import the full load cycle data consisting of the battery load and experimental cell voltage data as a table, as before, and use this table for defining time-dependent battery current and experimental voltage functions. (Note that the initial 300 s of the full load cycle data is exactly identical to the load cycle data imported for the previous study).
//    In the Results toolbar, click Table.

    model.result().table().create("tbl4", "Table");

//    In the Settings window for Table, type Full Load Cycle Data in the Label text field.

    model.result().table("tbl4").label("Full Load Cycle Data");

//    Locate the Data section.
//    Click Import.
//    Browse to the model's Application Library folder and double-click the file lumped_li_battery_parameter_estimation_E_I_vs_t_fulldata.txt.

    model.result().table("tbl4").importData("lumped_li_battery_parameter_estimation_E_I_vs_t_fulldata.txt");

//    This data file also contains three different columns: Time, Current and Voltage, as before.
//    In the Definitions toolbar, click Interpolation.

    model.component("comp1").func().create("int2", "Interpolation");

//    In the Settings window for Interpolation, type Interpolation - E and I vs. t (full) in the Label text field.

    model.component("comp1").func("int2").label("Interpolation - E and I vs. t (full)");

//    Locate the Definition section.
//    From the Data source list, select Result table.

    model.component("comp1").func("int2").set("source", "resultTable");

//    From the Table from list, select Full Load Cycle Data.

    model.component("comp1").func("int2").set("resultTable", "tbl4");

//    Locate the Data Column Settings section.
//    In the table, click to select the cell at row number 1 and column number 1.
//    In the Unit text field, type s.

    model.component("comp1").func("int2").setIndex("argunit", "s", 0);

//    In the table, click to select the cell at row number 2 and column number 1.
//    In the Name text field, type E_cell_exp_full.

    model.component("comp1").func("int2").setEntry("funcnames", "col2", "E_cell_exp_full");

//    In the table, enter the following settings:

    model.component("comp1").func("int2").setEntry("columnType", "col3", "value");

//    In the Name text field, type I_cell_exp_full.

    model.component("comp1").func("int2").setEntry("funcnames", "col3", "I_cell_exp_full");

//    Next, we will define variables corresponding to the time-dependent battery current and the experimental cell voltage of the full load cycle. (The experimental cell voltage variable will only be used during postprocessing.)
//    In the Model Builder window, right-click Component 1 (comp1) > Definitions > Variables 1 and choose Duplicate.

    model.component("comp1").variable().duplicate("var2", "var1");

//    In the Settings window for Variables, locate the Variables section.
//    In the table, enter the following settings:

    model.component("comp1").variable("var2").set("I_cell_exp", "I_cell_exp_full(t)[A]");
    model.component("comp1").variable("var2").descr("I_cell_exp", "Experimental cell current - full load cycle");
    model.component("comp1").variable("var2").set("E_cell_exp", "E_cell_exp_full(t)[V]");
    model.component("comp1").variable("var2").descr("E_cell_exp", "Experimental cell voltage - full load cycle");

//    Add a new time-dependent study for prediction of the full 600 s load cycle. Modify the model configuration for this study step to disable <l>Variables1</l> (that consists of variables corresponding to the 300 s load cycle). Also, set up the study to use the optimized lumped parameter values from the previous parameter estimation study.
//    In the Home toolbar, click Add Study to open the Add Study window.
//    Find the Studies subsection.
//    In the Select Study tree, select General Studies > Time Dependent.
//    Click Add Study in the window toolbar.

    model.study().create("std3");
    model.study("std3").create("time", "Transient");

//    In the Home toolbar, click Add Study to close the Add Study window.
//    In the Settings window for Study, type Study 3 - Full Load Curve Prediction in the Label text field.

    model.study("std3").label("Study 3 - Full Load Curve Prediction");

//    In the Model Builder window, under Study 3 - Full Load Curve Prediction, click Step 1: Time Dependent.
//    In the Settings window for Time Dependent, locate the Study Settings section.
//    In the Output times text field, type range(0,1,600).

    model.study("std3").feature("time").set("tlist", "range(0,1,600)");

//    From the Tolerance list, select User controlled.

    model.study("std3").feature("time").set("usertol", true);

//    In the Relative tolerance text field, type 0.001.

    model.study("std3").feature("time").set("rtol", 0.001);

//    Locate the Physics and Variables Selection section.
//    Select the Modify model configuration for study step checkbox.

    model.study("std3").feature("time").set("useadvanceddisable", true);

//    In the tree, select Component 1 (comp1) > Definitions > Variables 1.
//    Click Disable.

    model.study("std3").feature("time").set("disabledvariables", new String[]{"var1"});

//    Click to expand the Values of Dependent Variables section.
//    Find the Initial values of variables solved for subsection.
//    From the Settings list, select User controlled.

    model.study("std3").feature("time").set("useinitsol", true);

//    Find the Values of variables not solved for subsection.
//    From the Settings list, select User controlled.

    model.study("std3").feature("time").set("usesol", true);

//    From the Method list, select Solution.

    model.study("std3").feature("time").set("notsolmethod", "sol");

//    From the Study list, select Study 2 - Parameter Estimation, Time Dependent.

    model.study("std3").feature("time").set("notstudy", "std2");

//    In the Model Builder window, click Study 3 - Full Load Curve Prediction.
//    In the Settings window for Study, locate the Study Settings section.
//    Clear the Generate default plots checkbox.

    model.study("std3").setGenPlots(false);

//    Before we compute the prediction study, it may be useful, for completeness, to update the model configuration for the previous two study steps to disable <l>Variables2</l> (that consists of variables corresponding to the 600 s full load cycle).
//    In the Model Builder window, under Study 1 - Load Curve Simulation, click Step 1: Time Dependent.
//    In the Settings window for Time Dependent, locate the Physics and Variables Selection section.
//    Select the Modify model configuration for study step checkbox.

    model.study("std1").feature("time").set("useadvanceddisable", true);

//    In the tree, select Component 1 (comp1) > Definitions > Variables 2.
//    Click Disable.

    model.study("std1").feature("time").set("disabledvariables", new String[]{"var2"});

//    In the Model Builder window, under Study 2 - Parameter Estimation, click Step 1: Time Dependent.
//    In the Settings window for Time Dependent, locate the Physics and Variables Selection section.
//    Select the Modify model configuration for study step checkbox.

    model.study("std2").feature("time").set("useadvanceddisable", true);

//    In the tree, select Component 1 (comp1) > Definitions > Variables 2.
//    Click Disable.

    model.study("std2").feature("time").set("disabledvariables", new String[]{"var2"});

//    In the Study toolbar, click Compute.

    model.study("std3").createAutoSequences("all");

    model.component("comp1").probe("var1").genResult("none");
    model.component("comp1").probe("var2").genResult("none");
    model.component("comp1").probe("var3").genResult("none");

    model.sol("sol3").runAll();

    model.result("pg1").run();

//    In the Model Builder window, right-click Cell Voltage and choose Duplicate.

    model.result().duplicate("pg5", "pg1");
    model.result("pg5").run();

//    In the Settings window for 1D Plot Group, type Cell Voltage: Full Cycle Prediction in the Label text field.

    model.result("pg5").label("Cell Voltage: Full Cycle Prediction");

//    Locate the Data section.
//    From the Dataset list, select Study 3 - Full Load Curve Prediction/Solution 3 (sol3).

    model.result("pg5").set("data", "dset4");

//    Locate the Axis section.
//    In the x maximum text field, type 610.

    model.result("pg5").set("xmax", 610);

//    Locate the Legend section.
//    From the Position list, select Lower left.

    model.result("pg5").set("legendpos", "lowerleft");
    model.result("pg5").run();

//    In the Model Builder window, expand the Cell Voltage: Full Cycle Prediction node, then click Global 1.
//    In the Settings window for Global, locate the y-Axis Data section.
//    In the table, enter the following settings:

    model.result("pg5").feature("glob1").setIndex("descr", "Predicted cell voltage", 0);

//    In the Cell Voltage: Full Cycle Prediction toolbar, click Plot.

    model.result("pg5").run();

//    Finally, you can set up global evaluations for calculating the root mean square error (RMSE) of the modeled cell voltage from the experimental values for all the three studies as follows:
//    In the Results toolbar, click Global Evaluation.

    model.result().numerical().create("gev4", "EvalGlobal");

//    In the Settings window for Global Evaluation, type Global Evaluation: RMSE - Study1 in the Label text field.

    model.result().numerical("gev4").label("Global Evaluation: RMSE - Study1");

//    Locate the Expressions section.
//    In the table, enter the following settings:

    model.result().numerical("gev4").setIndex("expr", "lb.E_cell-E_cell_exp", 0);

//    Locate the Data Series Operation section.
//    From the Transformation list, select RMS.

    model.result().numerical("gev4").set("dataseries", "rms");

//    Click Evaluate.

    model.result().table().create("tbl5", "Table");
    model.result().table("tbl5").comments("Global Evaluation: RMSE - Study1");
    model.result().numerical("gev4").set("table", "tbl5");
    model.result().numerical("gev4").setResult();

//    Right-click Global Evaluation: RMSE - Study1 and choose Duplicate.

    model.result().numerical().duplicate("gev5", "gev4");

//    In the Settings window for Global Evaluation, type Global Evaluation: RMSE - Study2 in the Label text field.

    model.result().numerical("gev5").label("Global Evaluation: RMSE - Study2");

//    Locate the Data section.
//    From the Dataset list, select Study 2 - Parameter Estimation/Solution 2 (sol2).

    model.result().numerical("gev5").set("data", "dset2");

//    Click Evaluate.

    model.result().numerical("gev5").set("table", "tbl5");
    model.result().numerical("gev5").appendResult();

//    Right-click Global Evaluation: RMSE - Study2 and choose Duplicate.

    model.result().numerical().duplicate("gev6", "gev5");

//    In the Settings window for Global Evaluation, type Global Evaluation: RMSE - Study3 in the Label text field.

    model.result().numerical("gev6").label("Global Evaluation: RMSE - Study3");

//    Locate the Data section.
//    From the Dataset list, select Study 3 - Full Load Curve Prediction/Solution 3 (sol3).

    model.result().numerical("gev6").set("data", "dset4");

//    You can choose only the latter half of the full load cycle to obtain the standard deviation of the prediction study.
//    From the Time selection list, select Manual.

    model.result().numerical("gev6").setIndex("looplevelinput", "manualindices", 0);

//    In the Time indices (1-601) text field, type range(302,1,601).

    model.result().numerical("gev6").setIndex("looplevelindices", "range(302,1,601)", 0);

//    Click Evaluate.

    model.result().numerical("gev6").set("table", "tbl5");
    model.result().numerical("gev6").appendResult();

    model.title("Parameter Estimation of a Time-Dependent Lumped Battery Model");

    model
         .description("This tutorial uses a \"black-box\" approach to define a battery model based on a small set of lumped parameters, assuming no knowledge of the internal structure or design of the battery electrodes, or choice of materials.\n\nThe input to the model is the battery capacity, the initial state of charge (SOC), and an open circuit voltage vs SOC curve, in combination with load cycle experimental data.\n\nThe lumped parameters are determined using the Parameter Estimation study step.");

    return model;
  }

  public static void main(String[] args) {
    Model model = run();
    run2(model);
  }

}
