Exercise: ParmEst#

In this exercise, you will practice using ParmEst to estimate four parameters (\(U_a\), \(U_b\), \(C_p^H\), \(C_p^S\)) in the TCLab model using the step test data. We previously estimated these parameters using the sine test data.

import sys

# If running on Google Colab, install Pyomo and Ipopt via IDAES
on_colab = "google.colab" in sys.modules
if on_colab:
    !wget "https://raw.githubusercontent.com/dowlinglab/pyomo-doe/main/notebooks/tclab_pyomo.py"
else:
    import os

    if "exercise_solutions" in os.getcwd():
        # Add the "notebooks" folder to the path
        # This is needed for running the solutions from a separate folder
        # You only need this if you run locally
        sys.path.append('../notebooks')

# import TCLab model, simulation, and data analysis functions
from tclab_pyomo import (
    TC_Lab_data,
    TC_Lab_experiment,
    extract_results,
    extract_plot_results,
)

# set default number of states in the TCLab model
number_tclab_states = 2

Load and explore experimental data (step test)#

import pandas as pd

if on_colab:
    file = "https://raw.githubusercontent.com/dowlinglab/pyomo-doe/main/data/tclab_step_test.csv"
else:
    file = '../data/tclab_step_test.csv'
df = pd.read_csv(file)
df.head()
Time T1 T2 Q1 Q2
0 0.00 22.84 22.84 50.0 0.0
1 1.00 22.84 22.84 50.0 0.0
2 2.01 23.16 22.84 50.0 0.0
3 3.02 22.84 22.84 50.0 0.0
4 4.01 22.84 22.84 50.0 0.0

Make two plots to visualize the temperature and heat power data as a function of time.

### BEGIN SOLUTION
ax = df.plot(x='Time', y=['T1', 'T2'], xlabel='Time (s)', ylabel='Temperature (°C)')
### END SOLUTION
../_images/22bb6e842955ea3f922517818f5d1d2467a981ab5811ff3097f933c71680d9d9.png
### BEGIN SOLUTION
ax = df.plot(x='Time', y=['Q1', 'Q2'], xlabel='Time (s)', ylabel='Heater Power (%)')
### END SOLUTION
../_images/1ec0c081b9f0a828f1857de54d84d1b3bf3e482c2cde0ba738e506f6c2990293.png

We’ll now store the data in this custom data class objective. This is a nice trick to help keep data organized, but it is NOT required to use ParmEst or Pyomo data. Alternatively, we could just use a pandas DataFrame.

tc_data = TC_Lab_data(
    name="Step Test for Heater 1",
    time=df['Time'].values,
    T1=df['T1'].values,
    u1=df['Q1'].values,
    P1=200,
    TS1_data=None,
    T2=df['T2'].values,
    u2=df['Q2'].values,
    P2=200,
    TS2_data=None,
    Tamb=df['T1'].values[0],
)

Our custom data class has a method to export the data as a Pandas Data Frame.

tc_data.to_data_frame().head()
time T1 u1 P1 TS1_data T2 u2 P2 TS2_data Tamb
0 0.00 22.84 50.0 200 None 22.84 0.0 200 None 22.84
1 1.00 22.84 50.0 200 None 22.84 0.0 200 None 22.84
2 2.01 23.16 50.0 200 None 22.84 0.0 200 None 22.84
3 3.02 22.84 50.0 200 None 22.84 0.0 200 None 22.84
4 4.01 22.84 50.0 200 None 22.84 0.0 200 None 22.84

Parameter estimation with ParmEst#

Now for the main event: performing nonlinear least squares with ParmEst.

import pyomo.contrib.parmest.parmest as parmest

# First, we define an Experiment object within parmest
### BEGIN SOLUTION
TC_Lab_sine_exp = TC_Lab_experiment(data=tc_data, number_of_states=number_tclab_states)
### END SOLUTION

# Since everything has been labeled properly in the Experiment object, we simply invoke
# parmest's Estimator function to estimate the parameters.
### BEGIN SOLUTION
pest = parmest.Estimator([TC_Lab_sine_exp, ], obj_function='SSE', tee=True)

obj, theta = pest.theta_est()
### END SOLUTION
Ipopt 3.13.2: 

******************************************************************************
This program contains Ipopt, a library for large-scale nonlinear optimization.
 Ipopt is released as open source code under the Eclipse Public License (EPL).
         For more information visit http://projects.coin-or.org/Ipopt

This version of Ipopt was compiled from source code available at
    https://github.com/IDAES/Ipopt as part of the Institute for the Design of
    Advanced Energy Systems Process Systems Engineering Framework (IDAES PSE
    Framework) Copyright (c) 2018-2019. See https://github.com/IDAES/idaes-pse.

This version of Ipopt was compiled using HSL, a collection of Fortran codes
    for large-scale scientific computation.  All technical papers, sales and
    publicity material resulting from use of the HSL codes within IPOPT must
    contain the following acknowledgement:
        HSL, a collection of Fortran codes for large-scale scientific
        computation. See http://www.hsl.rl.ac.uk.
******************************************************************************

This is Ipopt version 3.13.2, running with linear solver ma27.

Number of nonzeros in equality constraint Jacobian...:    15301
Number of nonzeros in inequality constraint Jacobian.:        0
Number of nonzeros in Lagrangian Hessian.............:     7203

Total number of variables............................:     3606
                     variables with only lower bounds:        0
                variables with lower and upper bounds:     1804
                     variables with only upper bounds:        0
Total number of equality constraints.................:     3602
Total number of inequality constraints...............:        0
        inequality constraints with only lower bounds:        0
   inequality constraints with lower and upper bounds:        0
        inequality constraints with only upper bounds:        0

iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
   0  5.1436530e+04 2.32e-01 1.31e+01  -1.0 0.00e+00    -  0.00e+00 0.00e+00   0
   1  1.5532093e+02 3.70e-02 1.52e+04  -1.0 8.79e+00    -  8.50e-01 1.00e+00f  1
   2  1.6227889e+02 2.30e-03 8.55e+03  -1.0 5.40e-01   2.0 9.90e-01 1.00e+00h  1
   3  3.7562775e+01 1.39e-03 4.30e+03  -1.0 4.74e-01    -  9.90e-01 1.00e+00f  1
   4  2.2919842e+01 1.98e-01 7.58e+02  -1.0 3.43e+00    -  9.97e-01 8.72e-01f  1
   5  2.4039376e+01 2.58e-03 1.26e+03  -1.0 5.10e-02   1.5 1.00e+00 1.00e+00h  1
   6  2.2841812e+01 2.56e-04 1.63e+02  -1.0 8.78e-02    -  1.00e+00 1.00e+00f  1
   7  2.2804825e+01 2.50e-05 1.15e+01  -1.0 2.53e-02    -  1.00e+00 1.00e+00h  1
   8  2.2806558e+01 9.64e-10 1.47e-03  -1.0 1.32e-04   1.0 1.00e+00 1.00e+00h  1
   9  2.2802018e+01 7.47e-06 4.57e-02  -2.5 3.52e-02    -  1.00e+00 1.00e+00f  1
iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
  10  2.2802049e+01 8.05e-12 2.04e-05  -2.5 5.50e-06   0.6 1.00e+00 1.00e+00h  1
  11  2.2802045e+01 4.14e-05 3.63e-03  -3.8 9.58e-02    -  1.00e+00 1.00e+00h  1
  12  2.2802045e+01 9.14e-05 2.48e-02  -3.8 9.18e+00    -  9.83e-01 1.17e-02h  7
  13  2.2802048e+01 2.02e-05 1.84e-03  -3.8 3.67e-01    -  1.00e+00 1.00e+00H  1
  14  2.2802047e+01 5.87e-10 2.29e-04  -3.8 8.33e-05   0.1 1.00e+00 1.00e+00h  1
  15  2.2802046e+01 4.49e-10 4.57e-05  -5.7 1.11e-04  -0.4 1.00e+00 1.00e+00h  1
  16  2.2802045e+01 1.77e-10 1.10e-05  -5.7 7.98e-05  -0.9 1.00e+00 1.00e+00h  1
  17  2.2802045e+01 1.33e-11 1.04e-06  -8.6 2.28e-05  -1.3 1.00e+00 1.00e+00h  1
  18  2.2802045e+01 1.36e-13 3.54e-08  -8.6 2.32e-06  -1.8 1.00e+00 1.00e+00h  1
  19  2.2802045e+01 1.59e-14 4.01e-10  -8.6 7.89e-08  -2.3 1.00e+00 1.00e+00h  1

Number of Iterations....: 19

                                   (scaled)                 (unscaled)
Objective...............:   2.2802045378652373e+01    2.2802045378652373e+01
Dual infeasibility......:   4.0072241098023035e-10    4.0072241098023035e-10
Constraint violation....:   1.5855372570428017e-14    1.5855372570428017e-14
Complementarity.........:   2.5059035596800626e-09    2.5059035596800626e-09
Overall NLP error.......:   2.5059035596800626e-09    2.5059035596800626e-09


Number of objective function evaluations             = 30
Number of objective gradient evaluations             = 20
Number of equality constraint evaluations            = 30
Number of inequality constraint evaluations          = 0
Number of equality constraint Jacobian evaluations   = 20
Number of inequality constraint Jacobian evaluations = 0
Number of Lagrangian Hessian evaluations             = 19
Total CPU secs in IPOPT (w/o function evaluations)   =      0.201
Total CPU secs in NLP function evaluations           =      0.006

EXIT: Optimal Solution Found.
parmest_regression_results = extract_plot_results(
    tc_data, pest.ef_instance
)
../_images/00b22f04e1ec9c5f0e80122eab2e940e6dce8aa728d8d457046f4d7ba5a6da20.png
Model parameters:
Ua = 0.041 Watts/degC
Ub = 0.0091 Watts/degC
CpH = 5.9527 Joules/degC
CpS = 0.1387 Joules/degC
 

Discussion: How do these results compare to our previous analysis?