Multi-Building Design Update of two Spring-Mass Systems¶

This Jupyter Notebook demonstrates how to use the joint Multi-Model Update with the example of two, previously trained 1D Spring-Mass Hybrid Models (see the notebook on Hybrid Digital Twinning here).

The main objective of the Multi-Building Updating is to calibrate uncertain parameters of simulation models across multiple buildings or experimental setups using shared measurement data. This ensemble-based calibration improves generalizability and robustness compared to single-structure updates.

System Overview¶

Both spring-mass systems follow Hooke’s Law and Newton’s Second Law:

$$ F = -k x $$

where:

  • ( k ) is the spring stiffness,
  • ( x ) is the displacement from equilibrium,
  • ( m ) is the mass of the attached object.

Analytical Solution¶

For an ideal undamped system, the displacement at a given time step is given by:

$$ u(T) = u_0 \cos \left( \sqrt{\frac{k}{m}} T \right) + \frac{v_0}{\sqrt{\frac{k}{m}}} \sin \left( \sqrt{\frac{k}{m}} T \right) $$

where:

  • ( u_0 ) is the initial displacement,
  • ( v_0 ) is the initial velocity,
  • ( k ) and ( m ) can change dynamically in the twinning process,
  • ( T = 10 ) is the single time step at which the system is evaluated.

Imports¶

In [1]:
import sys
import os
parent_dir = os.path.abspath("../../libraries/surrogate_modelling")
sys.path.insert(0, parent_dir)
In [2]:
import pickle
import pandas as pd
import utils
from multibuilding_update import JointManager

import warnings
warnings.filterwarnings("ignore", category=RuntimeWarning)
warnings.filterwarnings("ignore", category=FutureWarning)

Load models¶

In this example we simply load two previusly trained hybrid models of the two system. The chosen parameters of the models were:

  • ( m1 and m2 ) mass of the attached object and
  • ( k1 and k2 ) spring stiffness.

The models are connected through their m parameter. All parameters are defined with uniform distributions, both m1 and m2 between bounds [0.5, 2.5], while k1 and k2 between [1.73, 2.11] and [1.52, 2.0]

In [ ]:
#Load measurement data
z_spring1 = pd.read_csv('../data/spring_data/z_spring1_df.csv')
z_spring2 = pd.read_csv('../data/spring_data/z_spring2_df.csv')

#Load standard deviation of the measurement error
sigma_spring1 = pd.read_csv('../data/spring_data/sigma_spring1.csv')
sigma_spring2 = pd.read_csv('../data/spring_data/sigma_spring2.csv')

#Load surrogate models
with open('../data/spring_data/Spring1_model.sm', 'rb') as file:
    Spring1_model = pickle.load(file)
with open('../data/spring_data/Spring2_model.sm', 'rb') as file:
    Spring2_model = pickle.load(file)

Update¶

In [ ]:
#Define joint parameters
joint_parameters = {'m': ['m1', 'm2']}

#Define the joint manager
j = JointManager([Spring1_model, Spring2_model], joint_parameters)
print(j.Q.params)
{'m': U(0, 1), 'k1': U(1.73, 2.11), 'k2': U(1.52, 2.0)}
In [ ]:
#Update inputs
nwalkers=32
niter=200
nburn=1000

#Update
j.update([z_spring1, z_spring2], [sigma_spring1, sigma_spring2], nwalkers=nwalkers, niter=niter, nburn=nburn)
MCMC creating
Burning period
100%|██████████| 1000/1000 [04:06<00:00,  4.05it/s]
MCMC running
100%|██████████| 200/200 [00:55<00:00,  3.60it/s]
--- 302.52466464042664 seconds ---

In [ ]:
# Plot the MCMC sampling results along with the mean and MAP points for each model
figs = utils.plot_multibuilding_MCMC(j, map_point=True, model_names=['Spring1', 'Spring2'], formatted_param_names=[["$m_{1}$", "$k_{1}$"], ["$m_{2}$", "$k_{2}$"]])
No description has been provided for this image
No description has been provided for this image