Bathe's Springs

March 2020, By Amir Hossein Namadchi

This is an OpenSeesPy simulation of a simple linear 3 DOF system introduced by Bathe and Noh. The model was used to demonstrate the ability of Bathe method in filtering out the unwanted artificial high-frequency responses. The effectiveness of numerical dissipation in a specific time integration algorithm could be evaluated by analyzing this benchmark model problem [1].

ModelProblem

import numpy as np
import opensees.openseespy as ops
import matplotlib.pyplot as plt

No units were defined in the original paper. So, I just assume the base units as follows:

## Units
m = 1               # Meters
KN = 1              # KiloNewtons
s = 1               # Seconds

kg = KN*(s**2)/m    # mass unit (derived)

Model specifications are defined as follows:

# Nodal mass values
m_1, m_2, m_3 = 0*kg, 1*kg, 1*kg

# Modulus of Elasticity
E_1, E_2 = (10.0**7)*(KN/m**2) , 1.0*(KN/m**2)

# Prescribed displacment @ node 1
d_p = lambda t: np.sin(1.2*t)*m
# Dynamic Analysis Parameters
dt = 0.2618
time = 10

Here’s the exact solution for comparison. It can be obtained using modal decomposition technique, considering only the lowest frequency plus the static correction [1].

d_2_ex = lambda t : (2.7273e-7)*np.sin(t) + np.sin(1.2*t)
d_3_ex = lambda t : (2.7273)*np.sin(t) - (2.2727)*np.sin(1.2*t)

v_2_ex = lambda t : (2.7273e-7)*np.cos(t) + 1.2*np.cos(1.2*t)
v_3_ex = lambda t : (2.7273)*np.cos(t) - 1.2*(2.2727)*np.cos(1.2*t)

a_2_ex = lambda t : -(2.7273e-7)*np.sin(t) - 1.2*1.2*np.sin(1.2*t)
a_3_ex = lambda t : -(2.7273)*np.sin(t) + 1.2*1.2*(2.2727)*np.sin(1.2*t)

Model Construction

Use ZeroLength elements to model the spings. Note that nodal masses are added via mass option of the node command.

def build_model():
    
    model = ops.Model(ndm=1, ndf=1)

    # Adding nodes
    for n, mass in enumerate([m_1,m_2,m_3]):
        model.node(n+1, 0.0, mass=mass)

    # Defining Material (spring stiffness)
    model.uniaxialMaterial('Elastic', 1, E_1)
    model.uniaxialMaterial('Elastic', 2, E_2)

    # Add springs
    model.element('ZeroLength', 1, *[1,2], '-mat', 1, '-dir', 1)
    model.element('ZeroLength', 2, *[2,3], '-mat', 2, '-dir', 1)

    print('Model built successfully!')
    return model

Dynamic Analysis

To use different time integration algorithms, the function do_dynamic_analysis is defined herein. Just pass the time, dt and integrator_params and run it multiple times with different integrators. Here, Newmark with $\gamma=0.5$ and $\beta=0.25$ (a non-dissipative algorithm) and the Bathe method, named as 'TRBDF2' (with asymptotic annihilation property) are used.

def do_dynamic_analysis(time, dt, integrator_params):
    
    model = build_model()
    time_domain = np.arange(0,time,dt)


    # Constraints
    model.timeSeries('Path', 1,
                    dt=dt,
                    values=np.vectorize(d_p)(time_domain),
                    time=time_domain)
    model.pattern('Plain', 1, 1)
    model.sp(1, 1, 1)

    # Analysis
    model.constraints('Transformation')
    model.numberer('Plain')
    model.system('ProfileSPD')
    model.algorithm('Linear')
    model.integrator(*integrator_params)
    model.analysis('Transient')

    time_lst =[0]           # list to hold time stations for plotting
    res_node_2 = [[0,0,0]]  # response params of node 2     
    res_node_3 = [[0,0,0]]  # response params of node 3

    for i in range(len(time_domain)):
        model.analyze(1, dt)
        time_lst.append(model.getTime())
        res_node_2.append([model.nodeDisp(2,1),
                           model.nodeVel(2,1),
                           model.nodeAccel(2,1)])
        res_node_3.append([model.nodeDisp(3,1),
                           model.nodeVel(3,1),
                           model.nodeAccel(3,1)])
    
    print('Done with ', integrator_params[0],'!')
    
    return {'time_list':np.array(time_lst),
            'Node 2': np.array(res_node_2), 
            'Node 3': np.array(res_node_3)}

Let’s perform analysis:

newmark = do_dynamic_analysis(time, dt,['Newmark',0.5,0.25])
bathe = do_dynamic_analysis(time, dt,['TRBDF2'])

Output:

Model built successfully!
Done with  Newmark !
Model built successfully!
Done with  TRBDF2 !

Visualization

plt.figure(figsize=(12,4))
plt.plot(np.arange(0, time, 0.2*dt),
         np.vectorize(v_2_ex)(np.arange(0,time,0.2*dt)),
         color = '#7D3C98', linewidth = 2, label = 'Exact')
plt.plot(newmark['time_list'], newmark['Node 2'][:,1],
         color = '#d62d20', linewidth=1.5, ls='--', label = 'Newmark (CAAM)' )
plt.plot(bathe['time_list'], bathe['Node 2'][:,1],
         color = '#008000', linewidth=1.75, label = 'Bathe (TRBDF2)', marker = 'o',ls=':')

plt.ylabel('Velocity of Node 2 (m/s)', {'fontstyle':'italic','size':14})
plt.xlabel('Time (sec)', {'fontstyle':'italic','size':14})
plt.xlim([0.0, time])
plt.ylim([-3, 3])
plt.grid()
plt.legend()
plt.yticks(fontsize = 14)
plt.xticks(fontsize = 14);

Output:

<Figure size 1200x400 with 1 Axes>
plt.figure(figsize=(12,4))
plt.plot(np.arange(0, time, 0.2*dt),
         np.vectorize(a_2_ex)(np.arange(0,time,0.2*dt)),
         color = '#7D3C98', linewidth = 2, label = 'Exact')
plt.plot(newmark['time_list'], newmark['Node 2'][:,2],
         color = '#d62d20', linewidth=1.5, ls='--', label = 'Newmark (CAAM)' )
plt.plot(bathe['time_list'], bathe['Node 2'][:,2],
         color = '#008000', linewidth=1.75, label = 'Bathe (TRBDF2)', marker = 'o',ls=':')

plt.ylabel('Acceleration of Node 2 m/s^2', {'fontstyle':'italic','size':14})
plt.xlabel('Time (sec)', {'fontstyle':'italic','size':14})
plt.xlim([0.0, time])
#plt.ylim([-30, 30])
plt.grid()
plt.legend()
plt.yticks(fontsize = 14)
plt.xticks(fontsize = 14);

Output:

<Figure size 1200x400 with 1 Axes>

Closure

As expected, non-dissipative methods like CAAM are unable to represent the exact response due to spurious high frequency oscillations. On the other hand, TRBDF2 scheme can effectively damp out these oscillations via algorithmic damping.

References

  • Bathe, K.J. and Noh, G., 2012. Insight into an implicit time integration scheme for structural dynamics. Computers & Structures, 98, pp.1-6.
  • Namadchi, A.H., Jandaghi, E. and Alamatian, J., 2020. A new model-dependent time integration scheme with effective numerical damping for dynamic analysis. Engineering with Computers, pp.1-16.