Pyomo.DoE: Exploratory Analysis#

import matplotlib.pyplot as plt

SMALL_SIZE = 14
MEDIUM_SIZE = 16
BIGGER_SIZE = 18

plt.rc('font', size=SMALL_SIZE)  # controls default text sizes
plt.rc('axes', titlesize=SMALL_SIZE)  # fontsize of the axes title
plt.rc('axes', labelsize=MEDIUM_SIZE)  # fontsize of the x and y labels
plt.rc('xtick', labelsize=SMALL_SIZE)  # fontsize of the tick labels
plt.rc('ytick', labelsize=SMALL_SIZE)  # fontsize of the tick labels
plt.rc('legend', fontsize=SMALL_SIZE)  # legend fontsize
plt.rc('figure', titlesize=BIGGER_SIZE)  # fontsize of the figure title
plt.rc('lines', linewidth=3)
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"

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

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

Load experimental data (sine test)#

import pandas as pd

if on_colab:
    file = "https://raw.githubusercontent.com/dowlinglab/pyomo-doe/main/data/tclab_sine_test_5min_period.csv"
else:
    file = '../data/tclab_sine_test_5min_period.csv'
df = pd.read_csv(file)
df.head()
Time T1 T2 Q1 Q2
0 0.00 22.2 22.2 50 0
1 1.01 22.2 22.2 51 0
2 2.01 22.2 22.2 52 0
3 3.00 22.2 22.2 53 0
4 4.01 22.2 22.2 54 0
ax = df.plot(x='Time', y=['T1', 'T2'], xlabel='Time (s)', ylabel='Temperature (°C)')
../_images/fbf2644f6f803238293870aec0099c1bb77225c820bcc42071fca6f36db6f2e6.png
ax = df.plot(x='Time', y=['Q1', 'Q2'], xlabel='Time (s)', ylabel='Heater Power (%)')
../_images/ab15113c9ac37b32ee9820b6e12aa73656cb2294c8fbe7117425cffc67afcbcb.png
# Here, we will induce a step size of 6 seconds, as to not give too many 
# degrees of freedom for experimental design.
skip = 6

# Create the data object considering the new control points every 6 seconds
tc_data = TC_Lab_data(
    name="Sine Wave Test for Heater 1",
    time=df['Time'].values[::skip],
    T1=df['T1'].values[::skip],
    u1=df['Q1'].values[::skip],
    P1=200,
    TS1_data=None,
    T2=df['T2'].values[::skip],
    u2=df['Q2'].values[::skip],
    P2=200,
    TS2_data=None,
    Tamb=df['T1'].values[0],
)

Analyze FIM with Pyomo.DoE at initial point (sine test)#

# Load Pyomo.DoE class
from pyomo.contrib.doe import DesignOfExperiments

from pyomo.environ import SolverFactory

# Copied from previous notebook
theta_values = {
    'Ua': 0.0417051733576387,
    'Ub': 0.009440714239773074,
    'inv_CpH': 0.1659093525658045,
    'inv_CpS': 5.8357556063605465,
}

Reusing the Experiment object used for parameter estimation#

Recall that we utilized the experiment object during parameter estimation. From the Experiment Abstraction notebook, we labeled the important sets:

  • experiment_inputs - The experimental design decisions (control variables \(u_2\) in this case)

  • experiment_outputs - The values measured during the experiment (temperature sensor data \(T_{S1}\) in this case)

  • measurement_error - The error associated with individual values measured during the experiment

  • unknown_parameters - Those parameters in the model that are estimated using the measured values during the experiment (heat transfer coefficients (\(U_a\) and \(U_b\)) and heat capacities (\(C^H_p\) and \(C^S_p\)) in this case)

This means we can use the same experiment object for optimal DoE! Therefore, we go about creating this object in the same way, except we will specify the values of the unknown parameters, \(\theta\).

# Create experiment object for design of experiments
doe_experiment = TC_Lab_experiment(data=tc_data, theta_initial=theta_values, number_of_states=number_tclab_states)

# Create the design of experiments object using our experiment instance from above
TC_Lab_DoE = DesignOfExperiments(experiment=doe_experiment, 
                                 step=1e-2,
                                 scale_constant_value=1,
                                 scale_nominal_param_value=True, 
                                 tee=True,)

FIM = TC_Lab_DoE.compute_FIM(method='sequential')
results_summary(FIM)
======Results Summary======
Four design criteria log10() value:
A-optimality: 5.773228885932493
D-optimality: 12.308607163946094
E-optimality: -1.793074158088514
Modified E-optimality: 7.514721752736364

FIM:
 [[517225.40941304   1360.01262476 -66404.72541298  -1002.47319402]
 [  1360.01262476   5004.3737258   12379.2662576    5238.40389773]
 [-66404.72541298  12379.2662576   65481.16908635  14190.01468139]
 [ -1002.47319402   5238.40389773  14190.01468139   5526.94375493]]

eigenvalues:
 [5.26802218e+05 6.26035823e+04 3.83207978e+03 1.61037063e-02]

eigenvectors:
 [[-9.89752804e-01 -1.35949591e-01  4.36702406e-02 -7.52086327e-05]
 [ 8.63262440e-04 -2.26164575e-01 -6.85698047e-01 -6.91857665e-01]
 [ 1.42671125e-01 -9.31600001e-01  3.33329462e-01 -2.56487437e-02]
 [ 5.79584008e-03 -2.49977462e-01 -6.45602485e-01  7.21578207e-01]]

We can see the FIM of the sine wave experiment is rank deficient with one eigenvalue that is numerically zero. We see the corresponding eigenvector points in the direction of the 2nd and 4th parameters, U_b and inv_CpS. This means the experiment alone does not contain enough information to uniquely estimate all of the parameters if the model.

Why does it make sense that \(U_b C_p^S\) is difficult to estimate?

Define parameterized sine wave experiment#

Can we further optimize the sine wave test to improve the information content? Let’s define the parameterized sine wave as:

\[ u(t) = 50 + a \sin(\frac{2 \pi}{60 p} t) \]

where \(a\) is the amplitude (% power), \(p\) is the period (minutes), \(t\) is the time (seconds), and 60 is a conversion factor (seconds/minute).

Let’s look at the information content of \(p = 5\) minute and \(a = 50\) %.

# Create experiment object for design of experiments, now including the sine period and amplitude
sine_period = 5
sine_amplitude = 50

doe_experiment = TC_Lab_experiment(
    data=tc_data, 
    theta_initial=theta_values, 
    number_of_states=number_tclab_states, 
    sine_amplitude=sine_amplitude,
    sine_period=sine_period,
)

# Create the design of experiments object using our experiment instance from above
TC_Lab_DoE = DesignOfExperiments(experiment=doe_experiment, 
                                 step=1e-2,
                                 scale_constant_value=1,
                                 scale_nominal_param_value=True, 
                                 tee=True,)

FIM = TC_Lab_DoE.compute_FIM(method='sequential')

pyomo_doe_results = extract_plot_results(None, TC_Lab_DoE.compute_FIM_model)
../_images/9caeb9bf9dd17f8f2ca8fa722a1a041a80ef970943b5533707f12c0d0f0f2c52.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 5 minutes
u1_amplitude = 50 % power
 
results_summary(FIM)
======Results Summary======
Four design criteria log10() value:
A-optimality: 5.780415236175106
D-optimality: 12.321221800291308
E-optimality: -1.7900021808076725
Modified E-optimality: 7.519483927119054

FIM:
 [[526615.93104322   1269.39017779 -67742.74020183  -1135.81765702]
 [  1269.39017779   5002.22184096  12430.15508962   5238.07864931]
 [-67742.74020183  12430.15508962  65988.9240756   14256.31640107]
 [ -1135.81765702   5238.07864931  14256.31640107   5528.90185893]]

eigenvalues:
 [5.36391326e+05 6.29165915e+04 3.82804512e+03 1.62180195e-02]

eigenvectors:
 [[-9.89741606e-01 -1.36133505e-01  4.33499251e-02 -7.45344234e-05]
 [ 1.03341873e-03 -2.25549256e-01 -6.85896472e-01 -6.91861616e-01]
 [ 1.42740848e-01 -9.31879387e-01  3.32518176e-01 -2.56421659e-02]
 [ 5.96112603e-03 -2.49391279e-01 -6.45831616e-01  7.21574652e-01]]

This parameterization reproduced the original sine wave experiment. As expected, the FIM is still rank deficient.

Perform sensitivity analysis to sine wave frequency and period#

Now let’s perform a sensitivity analysis over \(p\) and \(a\). Pyomo.DoE includes functions to automate this analysis including visualization. In our experience, an exploratory analysis like this is especially helpful to develop intuition about the physical system and model.

# Design variable ranges as lists
quick_run = False

if not quick_run:
    # Larger sensitivity analysis with more detailed plots
    design_ranges = {
        "u1_period": [1, 2, 3, 4, 5, 6, 7, 8],
        "u1_amplitude": [15, 25, 35, 45],
    }
else:
    # Faster sensitivity analysis, good for debugging
    design_ranges = {"u1_period": [1, 2], "u1_amplitude": [20, 50]}

sensi_opt = "sequential_finite"
FIM_results = []
data_period = []
data_amplitude = []

count = 0
# Grid search
for period in design_ranges["u1_period"]:
    for amplitude in design_ranges["u1_amplitude"]:
        count += 1
        print("=======Iteration Number: {} =======".format(count))
        print("Design variable values for this iteration: (Period: {}, Amplitude: {})".format(period, amplitude))

        data_period.append(period)
        data_amplitude.append(amplitude)
        
        doe_experiment = TC_Lab_experiment(
            data=tc_data, 
            theta_initial=theta_values, 
            number_of_states=number_tclab_states, 
            sine_amplitude=amplitude,
            sine_period=period,
        )
        
        # Create the design of experiments object using our experiment instance from above
        TC_Lab_DoE = DesignOfExperiments(experiment=doe_experiment, 
                                         step=1e-2,
                                         scale_constant_value=1,
                                         scale_nominal_param_value=True, 
                                         tee=True,)
        
        FIM = TC_Lab_DoE.compute_FIM(method='sequential')
        
        pyomo_doe_results = extract_plot_results(None, TC_Lab_DoE.compute_FIM_model)

        FIM_results.append(FIM)
=======Iteration Number: 1 =======
Design variable values for this iteration: (Period: 1, Amplitude: 15)
../_images/1208d8731e9b34c2f132f56a816b3bf1471a6b99e0306d3d1f3216275741e5e4.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 1 minutes
u1_amplitude = 15 % power
 
=======Iteration Number: 2 =======
Design variable values for this iteration: (Period: 1, Amplitude: 25)
../_images/53248ac05258492b512cb74832f9e84549201d248d8a66340ab319fff195fed2.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 1 minutes
u1_amplitude = 25 % power
 
=======Iteration Number: 3 =======
Design variable values for this iteration: (Period: 1, Amplitude: 35)
../_images/d69f277783756c4cb4853e1acfd897e8de65592c13d6ead30a83016151f0501b.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 1 minutes
u1_amplitude = 35 % power
 
=======Iteration Number: 4 =======
Design variable values for this iteration: (Period: 1, Amplitude: 45)
../_images/73e0e2059b82e5a5edbb0c51a40cb229c07de3dace62410f078f4d6feff64917.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 1 minutes
u1_amplitude = 45 % power
 
=======Iteration Number: 5 =======
Design variable values for this iteration: (Period: 2, Amplitude: 15)
../_images/aa4c287cda3bcde3f130368b4a2e5e6e3c5db415be40317f183cf8bd4579ffc5.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 2 minutes
u1_amplitude = 15 % power
 
=======Iteration Number: 6 =======
Design variable values for this iteration: (Period: 2, Amplitude: 25)
../_images/47dad7911cdf3ed05fdd73801776220d54284fe07bcb9ad1a85a3497c18b3a1c.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 2 minutes
u1_amplitude = 25 % power
 
=======Iteration Number: 7 =======
Design variable values for this iteration: (Period: 2, Amplitude: 35)
../_images/28ade973eb3558a2447e33c76a45c02dc4b979f6e3d3ac2b1a330a5362aca55e.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 2 minutes
u1_amplitude = 35 % power
 
=======Iteration Number: 8 =======
Design variable values for this iteration: (Period: 2, Amplitude: 45)
../_images/ed3a9e265705587da33c16e5157d0ffdaf32025695917eb6253ab69b252d0209.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 2 minutes
u1_amplitude = 45 % power
 
=======Iteration Number: 9 =======
Design variable values for this iteration: (Period: 3, Amplitude: 15)
../_images/506418d6a2f1f2b7cb14fa794d94467c55c8816aa21226d6828624bc942823f7.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 3 minutes
u1_amplitude = 15 % power
 
=======Iteration Number: 10 =======
Design variable values for this iteration: (Period: 3, Amplitude: 25)
../_images/b39c42ea0e360f6cb0e4582f16196e780b19c87d790d94661001e8552e63836d.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 3 minutes
u1_amplitude = 25 % power
 
=======Iteration Number: 11 =======
Design variable values for this iteration: (Period: 3, Amplitude: 35)
../_images/903b18e36e2b8c4cb057bf21219dcc96ecbfc51013526b2c5f67763856648950.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 3 minutes
u1_amplitude = 35 % power
 
=======Iteration Number: 12 =======
Design variable values for this iteration: (Period: 3, Amplitude: 45)
../_images/7feb7785c27f730133b314faad8722a6f7a0f770d0ba3d40022df783e1182aaa.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 3 minutes
u1_amplitude = 45 % power
 
=======Iteration Number: 13 =======
Design variable values for this iteration: (Period: 4, Amplitude: 15)
../_images/8058d05f67728058a794ef62b53036da46405c07dcc9f4728f2f1f77829da67a.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 4 minutes
u1_amplitude = 15 % power
 
=======Iteration Number: 14 =======
Design variable values for this iteration: (Period: 4, Amplitude: 25)
../_images/ebb639a2816e8d2625ede7155fde378ff405c29e7a336627cad96bc38d3da0f3.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 4 minutes
u1_amplitude = 25 % power
 
=======Iteration Number: 15 =======
Design variable values for this iteration: (Period: 4, Amplitude: 35)
../_images/9215d0075b6860e2b928f52f054a6e8f799913406c185a2cdd80e7f46e39458c.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 4 minutes
u1_amplitude = 35 % power
 
=======Iteration Number: 16 =======
Design variable values for this iteration: (Period: 4, Amplitude: 45)
../_images/b09e109e2d9e8d525f888d29fdbe7dbc0d8d10f221685ac86496ab721ae56a4e.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 4 minutes
u1_amplitude = 45 % power
 
=======Iteration Number: 17 =======
Design variable values for this iteration: (Period: 5, Amplitude: 15)
../_images/355547dd114162c8e74369744168295ad38291c9f2a336a8bbebd3def471306d.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 5 minutes
u1_amplitude = 15 % power
 
=======Iteration Number: 18 =======
Design variable values for this iteration: (Period: 5, Amplitude: 25)
../_images/a52285cbe7c41d093f3c69cfa3d13862b9a3a0a6845d9e79a3378c87e3542910.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 5 minutes
u1_amplitude = 25 % power
 
=======Iteration Number: 19 =======
Design variable values for this iteration: (Period: 5, Amplitude: 35)
../_images/a71bc7c2ca8d3a2104233ee09f2bd21ead0cc06fff2aa37f50a8a04f1c0add91.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 5 minutes
u1_amplitude = 35 % power
 
=======Iteration Number: 20 =======
Design variable values for this iteration: (Period: 5, Amplitude: 45)
../_images/ee800c1c84797faac5cdaa495850b0ca1ebbc4860a0fc860a64afc16319e84c7.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 5 minutes
u1_amplitude = 45 % power
 
=======Iteration Number: 21 =======
Design variable values for this iteration: (Period: 6, Amplitude: 15)
../_images/3a8d4194fc31548471ccfa2ff16709bb6592001d1d228416b46396d01ba026ed.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 6 minutes
u1_amplitude = 15 % power
 
=======Iteration Number: 22 =======
Design variable values for this iteration: (Period: 6, Amplitude: 25)
../_images/8f65e8eb2faa736a462103656d2c6bb7ffb21d35f57ff45619d662d49dc0ad1a.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 6 minutes
u1_amplitude = 25 % power
 
=======Iteration Number: 23 =======
Design variable values for this iteration: (Period: 6, Amplitude: 35)
../_images/0aa7adb0b4cafc9061b0a8ee4a18e2e7266235f47276890972896d508b31fcb4.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 6 minutes
u1_amplitude = 35 % power
 
=======Iteration Number: 24 =======
Design variable values for this iteration: (Period: 6, Amplitude: 45)
../_images/f6ebb102d378503d9d0dcfe13d75b892e884d4959877f58d350d5d3d96882724.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 6 minutes
u1_amplitude = 45 % power
 
=======Iteration Number: 25 =======
Design variable values for this iteration: (Period: 7, Amplitude: 15)
../_images/cca0a103c3f33cae93c5582fa0f230bfc7faa18358f1f660cda8a1ec4d201688.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 7 minutes
u1_amplitude = 15 % power
 
=======Iteration Number: 26 =======
Design variable values for this iteration: (Period: 7, Amplitude: 25)
../_images/e5b3f5fb6611198c443dbf643313c5840b85beaec747b453e4e90b5687d885ed.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 7 minutes
u1_amplitude = 25 % power
 
=======Iteration Number: 27 =======
Design variable values for this iteration: (Period: 7, Amplitude: 35)
../_images/e66e346bc0ab60beac6235ce9fb96b396b690b25cd36fad1e86194af047d891b.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 7 minutes
u1_amplitude = 35 % power
 
=======Iteration Number: 28 =======
Design variable values for this iteration: (Period: 7, Amplitude: 45)
../_images/a51087d10f4e039b8ec241800730fd15c9efbb22337182d5cd4877938afe38fc.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 7 minutes
u1_amplitude = 45 % power
 
=======Iteration Number: 29 =======
Design variable values for this iteration: (Period: 8, Amplitude: 15)
../_images/866885fa097918c0c7611565b4f9a127b406a86e2085d314806da8a58012a17b.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 8 minutes
u1_amplitude = 15 % power
 
=======Iteration Number: 30 =======
Design variable values for this iteration: (Period: 8, Amplitude: 25)
../_images/ccfb681f6d581a940d0bd4fb65ef8c02fcd2209f3e411d1de556d87c30599443.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 8 minutes
u1_amplitude = 25 % power
 
=======Iteration Number: 31 =======
Design variable values for this iteration: (Period: 8, Amplitude: 35)
../_images/f664a43427c3141caf0256e2c40b8afff2dfdba6e6ef117c86a403cae2100b45.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 8 minutes
u1_amplitude = 35 % power
 
=======Iteration Number: 32 =======
Design variable values for this iteration: (Period: 8, Amplitude: 45)
../_images/7f651f73165df776bb07c66a99c7d7080a859127657d1ba668947fb382a3d9fb.png
Model parameters:
Ua = 0.0413 Watts/degC
Ub = 0.0093 Watts/degC
CpH = 6.0883 Joules/degC
CpS = 0.1731 Joules/degC
u1_period = 8 minutes
u1_amplitude = 45 % power
 

Visualize and interpret sensitivity analysis results#

import numpy as np

# Extract criteria from FIM
def get_FIM_metrics(result):
    eigenvalues, eigenvectors = np.linalg.eig(result)
    min_eig = min(eigenvalues)

    A_opt = np.log10(np.trace(result))
    D_opt = np.log10(np.linalg.det(result))
    E_opt = np.log10(min_eig)
    ME_opt = np.log10(np.linalg.cond(result))

    return A_opt, D_opt, E_opt, ME_opt

FIM_metrics = []

for i in FIM_results:
    FIM_metrics.append(get_FIM_metrics(i))

FIM_metrics_np = np.asarray(FIM_metrics)

# Make heat map
def plot_heatmap(data, title, y_label, x_label, colorbar_label):
    # set heatmap x,y ranges
    x_tick_labels = np.sort(np.unique(data[:, 0]))
    y_tick_labels = np.sort(np.unique(data[:, 1]))

    # optimality-values
    opt_vals = np.asarray(data[:, 2]).reshape(len(x_tick_labels), len(y_tick_labels))
    
    # Plot the colormap
    fig = plt.figure()

    # Plotting options
    ax = fig.add_subplot(111)
    params = {"mathtext.default": "regular"}
    plt.rcParams.update(params)

    # Plotting data
    ax.set_yticks(range(len(y_tick_labels)))
    ax.set_yticklabels(y_tick_labels)
    ax.set_ylabel(y_label)
    ax.set_xticks(range(len(x_tick_labels)))
    ax.set_xticklabels(x_tick_labels)
    ax.set_xlabel(x_label)
    im = ax.imshow(opt_vals.T, cmap=plt.cm.hot_r)
    ba = plt.colorbar(im)
    ba.set_label(colorbar_label)
    plt.title(title)

# X and Y axis labels
x_label = "Period [min]"
y_label = "Amplitude [% power]"

# Draw A-optimality figure
data_A = np.zeros((len(FIM_metrics), 3))
data_A[:, 0] = data_period
data_A[:, 1] = data_amplitude
data_A[:, 2] = FIM_metrics_np[:, 0]

plot_heatmap(data_A, "Sine Wave Test: A-optimality", y_label, x_label, "log10(trace(FIM))")

# Draw D-optimality figure
data_D = np.zeros((len(FIM_metrics), 3))
data_D[:, 0] = data_period
data_D[:, 1] = data_amplitude
data_D[:, 2] = FIM_metrics_np[:, 1]

plot_heatmap(data_D, "Sine Wave Test: D-optimality", y_label, x_label, "log10(det(FIM))")

# Draw E-optimality figure
data_E = np.zeros((len(FIM_metrics), 3))
data_E[:, 0] = data_period
data_E[:, 1] = data_amplitude
data_E[:, 2] = FIM_metrics_np[:, 2]

plot_heatmap(data_E, "Sine Wave Test: E-optimality", y_label, x_label, "log10(min-eig(FIM))")

# Draw ME-optimality figure
data_ME = np.zeros((len(FIM_metrics), 3))
data_ME[:, 0] = data_period
data_ME[:, 1] = data_amplitude
data_ME[:, 2] = FIM_metrics_np[:, 3]

plot_heatmap(data_ME, "Sine Wave Test: ME-optimality", y_label, x_label, "log10(cond(FIM))")
../_images/60dc23d65e704df92b61f6e77680d1321cc232659b2d6fa1102698b050525dfb.png ../_images/4b096cc9246ffae3a657db43997dc7783f1aa9337d640908f036be302430e67a.png ../_images/cba1a02b69530e82e60a96e39c5721b35582f32f681b23dbb251c26c5447b485.png ../_images/b2189675e5af0e697d55e8804f207f3c92358586ad724ccba5c63e1f8daffcfb.png

These heatmaps tell us a lot about the mathematical model for the TCLab system.

A-optimality (trace of FIM) is the largest with a long period (8 minutes) and large amplitude (45%). Why does this make sense? The long period means the heating and cooling is more gradual. This gives more information about the dynamics of the system, especially the lag in heat transfer between the two modeled thermal masses. Likewise, the larger amplitude ensures a larger temperature range is explored, which maximizes the ratio of the signal to noise (which is assumed to be i.i.d.)

The D-, E-, and modified-E optimality results are more nuanced. Even with the poorly identifiable parameter, we are still able to see that choosing a period of approximately 3 minutes is best for E- and ME-optimality, where D-optimality sees an optima at about 4-5 minutes of period.

One thing is consistent between all the optimality metrics: larger amplitude (toward 45%) results in more information.