def restart(): ffp = 'db/truss_ops' osi = o3.OpenSeesInstance(3, restore=(ffp, 1)) # o3.ops.database('File', 'db/truss_ops') # o3.ops.restore(1) dd = restore_objs2dict(ffp) top_node = dd['top_node'] sf_eles = dd['sf_eles'] ts0 = o3.time_series.Linear(osi, factor=1) o3.pattern.Plain(osi, ts0) o3.Load(osi, top_node, [100, -500]) o3.constraints.Transformation(osi) o3.test_check.NormDispIncr(osi, tol=1.0e-6, max_iter=35, p_flag=0) o3.algorithm.Newton(osi) o3.numberer.RCM(osi) o3.system.FullGeneral(osi) n_steps_gravity = 15 d_gravity = 1. / n_steps_gravity o3.integrator.LoadControl(osi, d_gravity, num_iter=10) # o3.rayleigh.Rayleigh(osi, a0, a1, 0.0, 0.0) o3.analysis.Static(osi) o3r = o3.results.Results2D(cache_path=out_folder, dynamic=True) o3r.pseudo_dt = 0.1 o3r.start_recorders(osi, dt=0.1) nr = o3.recorder.NodeToArrayCache(osi, top_node, [o3.cc.DOF2D_X, o3.cc.DOF2D_Y], 'disp') er = o3.recorder.ElementToArrayCache(osi, sf_eles[0], arg_vals=['force']) for i in range(n_steps_gravity): o3.analyze(osi, num_inc=1) o3.load_constant(osi, time=0.0) import o3seespy.extensions o3.extensions.to_py_file(osi, 'ofile.py') print('init_disp: ', o3.get_node_disp(osi, top_node, o3.cc.DOF2D_Y)) print('init_disp: ', o3.get_node_disp(osi, top_node, o3.cc.DOF2D_Y)) print('init_disp: ', o3.get_node_disp(osi, top_node, o3.cc.DOF2D_Y)) o3.wipe(osi) o3r.save_to_cache() # o3r.coords = o3.get_all_node_coords(osi) # o3r.ele2node_tags = o3.get_all_ele_node_tags_as_dict(osi) data = nr.collect() edata = er.collect() # bf, sps = plt.subplots(nrows=2) # sps[0].plot(data[:, 0]) # sps[0].plot(data[:, 1]) # sps[1].plot(edata[:, 0]) # # sps[0].plot(data[1]) # plt.show() o3r.load_from_cache() o3plot.replot(o3r)
def run(): l = 100 osi = o3.OpenSeesInstance(ndm=1, ndf=1) nodes = [o3.node.Node(osi, 0.0), o3.node.Node(osi, l)] o3.Fix(osi, nodes[0], [o3.cc.X]) fy = 0.3 e0 = 200. eps_y = fy / e0 b = -0.01 mat = o3.uniaxial_material.Steel01(osi, fy=fy, e0=e0, b=b) area = 1000.0 truss = o3.element.Truss(osi, nodes, area, mat=mat) ts = o3.time_series.Linear(osi) pat = o3.pattern.Plain(osi, ts) o3.Load(osi, nodes[1], [1.0]) step_size = 0.01 dy = eps_y * l max_disp = 5 * dy n_steps = int(max_disp / step_size) ndr = o3.recorder.NodeToArrayCache(osi, nodes[1], dofs=[o3.cc.X], res_type='disp') efr = o3.recorder.ElementToArrayCache(osi, truss, arg_vals=['localForce']) o3.constraints.Plain(osi) o3.numberer.Plain(osi) o3.test.NormDispIncr(osi, tol=1.0e-6, max_iter=100, p_flag=0) o3.algorithm.Newton(osi) o3.system.BandGeneral(osi) o3.integrator.DisplacementControl(osi, nodes[1], o3.cc.X, step_size) o3.analysis.Static(osi) o3.analyze(osi, n_steps) o3.wipe(osi) disp = ndr.collect() force = efr.collect() plt.plot(disp, force) plt.show()
def run_analysis(asig, period, xi, f_yield, etype): # Load a ground motion # Define inelastic SDOF mass = 1.0 r_post = 0.0 # Initialise OpenSees instance osi = o3.OpenSeesInstance(ndm=2, state=0) # Establish nodes bot_node = o3.node.Node(osi, 0, 0) top_node = o3.node.Node(osi, 0, 0) # Fix bottom node o3.Fix3DOF(osi, top_node, o3.cc.FREE, o3.cc.FIXED, o3.cc.FIXED) o3.Fix3DOF(osi, bot_node, o3.cc.FIXED, o3.cc.FIXED, o3.cc.FIXED) # Set out-of-plane DOFs to be slaved o3.EqualDOF(osi, top_node, bot_node, [o3.cc.Y, o3.cc.ROTZ]) # nodal mass (weight / g): o3.Mass(osi, top_node, mass, 0., 0.) # Define material k_spring = 4 * np.pi**2 * mass / period**2 bilinear_mat = o3.uniaxial_material.Steel01(osi, fy=f_yield, e0=k_spring, b=r_post) # Assign zero length element, # Note: pass actual node and material objects into element o3.element.ZeroLength(osi, [bot_node, top_node], mats=[bilinear_mat], dirs=[o3.cc.DOF2D_X], r_flag=1) # Define the dynamic analysis # Define the dynamic analysis acc_series = o3.time_series.Path(osi, dt=asig.dt, values=-1 * asig.values) # should be negative o3.pattern.UniformExcitation(osi, dir=o3.cc.X, accel_series=acc_series) # set damping based on first eigen mode angular_freqs = np.array(o3.get_eigen(osi, solver='fullGenLapack', n=1))**0.5 beta_k = 2 * xi / angular_freqs[0] print('angular_freqs: ', angular_freqs) periods = 2 * np.pi / angular_freqs o3.rayleigh.Rayleigh(osi, alpha_m=0.0, beta_k=beta_k, beta_k_init=0.0, beta_k_comm=0.0) # Run the dynamic analysis o3.wipe_analysis(osi) # Run the dynamic analysis o3.constraints.Transformation(osi) o3.test_check.NormDispIncr(osi, tol=1.0e-6, max_iter=35, p_flag=0) o3.numberer.RCM(osi) if etype == 'implicit': o3.algorithm.Newton(osi) o3.system.SparseGeneral(osi) o3.integrator.Newmark(osi, gamma=0.5, beta=0.25) analysis_dt = 0.01 else: o3.algorithm.Linear(osi, factor_once=True) o3.system.FullGeneral(osi) if etype == 'newmark_explicit': o3.integrator.NewmarkExplicit(osi, gamma=0.6) explicit_dt = periods[0] / np.pi / 32 elif etype == 'central_difference': o3.integrator.CentralDifference(osi) o3.opy.integrator('HHTExplicit') explicit_dt = periods[0] / np.pi / 16 # 0.5 is a factor of safety elif etype == 'explicit_difference': o3.integrator.ExplicitDifference(osi) explicit_dt = periods[0] / np.pi / 32 else: raise ValueError(etype) print('explicit_dt: ', explicit_dt) analysis_dt = explicit_dt o3.analysis.Transient(osi) analysis_time = asig.time[-1] outputs = { "time": [], "rel_disp": [], "rel_accel": [], "rel_vel": [], "force": [] } while o3.get_time(osi) < analysis_time: o3.analyze(osi, 1, analysis_dt) curr_time = o3.get_time(osi) outputs["time"].append(curr_time) outputs["rel_disp"].append(o3.get_node_disp(osi, top_node, o3.cc.X)) outputs["rel_vel"].append(o3.get_node_vel(osi, top_node, o3.cc.X)) outputs["rel_accel"].append(o3.get_node_accel(osi, top_node, o3.cc.X)) o3.gen_reactions(osi) outputs["force"].append(-o3.get_node_reaction( osi, bot_node, o3.cc.X)) # Negative since diff node o3.wipe(osi) for item in outputs: outputs[item] = np.array(outputs[item]) return outputs
def run(out_folder): osi = o3.OpenSeesInstance(ndm=2, ndf=2, state=3) x_centre = 0.0 y_centre = 0.0 top_node = o3.node.Node(osi, x_centre, y_centre) fd_area = 1 fd_e_mod = 1e9 fd_iz = 1e6 top_nodes = [] bot_nodes = [] sf_eles = [] fd_eles = [] o3.Mass(osi, top_node, 10, 10) fy = 500 k = 1.0e4 b = 0.1 pro_params = [5, 0.925, 0.15] sf_mat = o3.uniaxial_material.SteelMPF(osi, fy, fy, k, b, b, params=pro_params) diff_pos = 0.5 depth = 1 bot_nodes.append(o3.node.Node(osi, x_centre - diff_pos, y_centre - depth)) o3.Fix2DOF(osi, bot_nodes[0], o3.cc.FIXED, o3.cc.FIXED) bot_nodes.append(o3.node.Node(osi, x_centre + diff_pos, y_centre - depth)) o3.Fix2DOF(osi, bot_nodes[1], o3.cc.FIXED, o3.cc.FIXED) top_nodes.append(o3.node.Node(osi, x_centre, y_centre)) sf_eles.append( o3.element.Truss(osi, [top_nodes[0], bot_nodes[0]], big_a=1.0, mat=sf_mat)) sf_eles.append( o3.element.Truss(osi, [top_nodes[0], bot_nodes[1]], big_a=1.0, mat=sf_mat)) o3.EqualDOF(osi, top_node, top_nodes[0], dofs=[o3.cc.DOF2D_X, o3.cc.DOF2D_Y]) ts0 = o3.time_series.Linear(osi, factor=1) o3.pattern.Plain(osi, ts0) o3.Load(osi, top_node, [100, -500]) o3.constraints.Transformation(osi) o3.test_check.NormDispIncr(osi, tol=1.0e-6, max_iter=35, p_flag=0) o3.algorithm.Newton(osi) o3.numberer.RCM(osi) o3.system.FullGeneral(osi) n_steps_gravity = 15 d_gravity = 1. / n_steps_gravity o3.integrator.LoadControl(osi, d_gravity, num_iter=10) # o3.rayleigh.Rayleigh(osi, a0, a1, 0.0, 0.0) o3.analysis.Static(osi) o3r = o3.results.Results2D(cache_path=out_folder, dynamic=True) o3r.pseudo_dt = 0.1 o3r.start_recorders(osi, dt=0.1) nr = o3.recorder.NodeToArrayCache(osi, top_node, [o3.cc.DOF2D_X, o3.cc.DOF2D_Y], 'disp') er = o3.recorder.ElementToArrayCache(osi, sf_eles[0], arg_vals=['force']) for i in range(n_steps_gravity): o3.analyze(osi, num_inc=1) o3.load_constant(osi, time=0.0) import o3seespy.extensions o3.extensions.to_py_file(osi, 'ofile.py') print('init_disp: ', o3.get_node_disp(osi, top_node, o3.cc.DOF2D_Y)) print('init_disp: ', o3.get_node_disp(osi, top_nodes[0], o3.cc.DOF2D_Y)) print('init_disp: ', o3.get_node_disp(osi, top_nodes[-1], o3.cc.DOF2D_Y)) o3.wipe(osi) o3r.save_to_cache() # o3r.coords = o3.get_all_node_coords(osi) # o3r.ele2node_tags = o3.get_all_ele_node_tags_as_dict(osi) data = nr.collect() edata = er.collect() # bf, sps = plt.subplots(nrows=2) # sps[0].plot(data[:, 0]) # sps[0].plot(data[:, 1]) # sps[1].plot(edata[:, 0]) # # sps[0].plot(data[1]) # plt.show() o3r.load_from_cache() o3plot.replot(o3r)
def run_analysis(etype, asig, use_modal_damping=0): osi = o3.OpenSeesInstance(ndm=2, ndf=3) nodes = [ o3.node.Node(osi, 0.0, 0.0), o3.node.Node(osi, 5.5, 0.0), o3.node.Node(osi, 0.0, 3.3), o3.node.Node(osi, 5.5, 3.3) ] o3.Mass2D(osi, nodes[2], 1e5, 1e5, 1e6) o3.Mass2D(osi, nodes[3], 1e5, 1e5, 1e6) o3.Fix3DOF(osi, nodes[0], o3.cc.FIXED, o3.cc.FIXED, o3.cc.FIXED) o3.Fix3DOF(osi, nodes[1], o3.cc.FIXED, o3.cc.FIXED, o3.cc.FIXED) steel_mat = o3.uniaxial_material.Steel01(osi, 300.0e6, 200.0e9, b=0.02) # o3.element.DispBeamColumn(osi, [nodes[2], nodes[3]], ) tran = o3.geom_transf.Linear2D(osi) e_mod = 200.0e9 iz = 1.0e-4 area = 0.01 # o3.element.ElasticBeamColumn2D(osi, [nodes[2], nodes[3]], 0.01, 200.0e9, iz=1.0e-4, transf=tran) ei = e_mod * iz ea = e_mod * area phi_y = 0.001 my = ei * phi_y print('my: ', my) mat = o3.uniaxial_material.ElasticBilin(osi, ei, 0.01 * ei, phi_y) mat_axial = o3.uniaxial_material.Elastic(osi, ea) top_sect = o3.section.Aggregator(osi, mats=[[mat_axial, o3.cc.P], [mat, o3.cc.M_Z]]) bot_sect = o3.section.Aggregator(osi, mats=[[mat_axial, o3.cc.P], [mat, o3.cc.M_Z]]) centre_sect = o3.section.Elastic2D(osi, e_mod, area, iz) lplas = 0.2 integ = o3.beam_integration.HingeMidpoint(osi, bot_sect, lplas, top_sect, lplas, centre_sect) beam = o3.element.ForceBeamColumn(osi, [nodes[2], nodes[3]], tran, integ) o3.element.ElasticBeamColumn2D(osi, [nodes[0], nodes[2]], 0.01, 200.0e9, iz=1.0e-4, transf=tran) o3.element.ElasticBeamColumn2D(osi, [nodes[1], nodes[3]], 0.01, 200.0e9, iz=1.0e-4, transf=tran) a_series = o3.time_series.Path(osi, dt=asig.dt, values=-1 * asig.values) # should be negative o3.pattern.UniformExcitation(osi, dir=o3.cc.X, accel_series=a_series) xi = 0.04 angular_freqs = np.array(o3.get_eigen(osi, n=4))**0.5 print('angular_freqs: ', angular_freqs) periods = 2 * np.pi / angular_freqs print('periods: ', periods) if use_modal_damping: # Does not support modal damping freqs = [0.5, 5] omega_1 = 2 * np.pi * freqs[0] omega_2 = 2 * np.pi * freqs[1] a0 = 2 * xi * omega_1 * omega_2 / (omega_1 + omega_2) a1 = 2 * xi / (omega_1 + omega_2) o3.rayleigh.Rayleigh(osi, a0, 0, a1, 0) else: o3.ModalDamping(osi, [xi]) o3.constraints.Transformation(osi) o3.test_check.NormDispIncr(osi, tol=1.0e-5, max_iter=35, p_flag=0) o3.numberer.RCM(osi) if use_modal_damping: o3_sys = o3.system.ProfileSPD # not sure why don't need to use FullGen here? since matrix is full? else: o3_sys = o3.system.ProfileSPD if etype == 'implicit': o3.algorithm.Newton(osi) o3_sys(osi) o3.integrator.Newmark(osi, gamma=0.5, beta=0.25) dt = 0.01 else: o3.algorithm.Linear(osi, factor_once=True) if etype == 'newmark_explicit': o3_sys(osi) o3.integrator.NewmarkExplicit(osi, gamma=0.5) explicit_dt = periods[-1] / np.pi / 4 elif etype == 'central_difference': o3_sys(osi) o3.integrator.CentralDifference(osi) explicit_dt = periods[-1] / np.pi / 4 # 0.5 is a factor of safety elif etype == 'explicit_difference': o3.system.Diagonal(osi) o3.integrator.ExplicitDifference(osi) explicit_dt = periods[-1] / np.pi / 4 else: raise ValueError(etype) print('explicit_dt: ', explicit_dt) dt = explicit_dt o3.analysis.Transient(osi) roof_disp = o3.recorder.NodeToArrayCache(osi, nodes[2], dofs=[o3.cc.X], res_type='disp') time = o3.recorder.TimeToArrayCache(osi) ele_resp = o3.recorder.ElementToArrayCache(osi, beam, arg_vals=['force']) ttotal = 10.0 o3.analyze(osi, int(ttotal / dt), dt) o3.wipe(osi) return time.collect(), roof_disp.collect(), ele_resp.collect()
def run_mz_triaxial(): """ This function runs an o3seespy equivalent of the ManzariDafalias triaxial compression example from https://opensees.berkeley.edu/wiki/index.php/Manzari_Dafalias_Material The intention is to demonstrate the compatibility between o3seespy and the Tcl version of OpenSees. """ damp = 0.1 omega0 = 0.0157 omega1 = 64.123 a1 = 2. * damp / (omega0 + omega1) a0 = a1 * omega0 * omega1 # Initialise OpenSees instance osi = o3.OpenSeesInstance(ndm=3, ndf=4, state=3) # Establish nodes n_coords = [ [1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 0.0], [1.0, 0.0, 1.0], [1.0, 1.0, 1.0], [0.0, 1.0, 1.0], [0.0, 0.0, 1.0] ] nm = [] for nc in n_coords: nm.append(o3.node.Node(osi, *nc)) o3.Fix4DOF(osi, nm[0], o3.cc.FREE, o3.cc.FIXED, o3.cc.FIXED, o3.cc.FIXED) o3.Fix4DOF(osi, nm[1], o3.cc.FREE, o3.cc.FREE, o3.cc.FIXED, o3.cc.FIXED) o3.Fix4DOF(osi, nm[2], o3.cc.FIXED, o3.cc.FREE, o3.cc.FIXED, o3.cc.FIXED) o3.Fix4DOF(osi, nm[3], o3.cc.FIXED, o3.cc.FIXED, o3.cc.FIXED, o3.cc.FIXED) o3.Fix4DOF(osi, nm[4], o3.cc.FREE, o3.cc.FIXED, o3.cc.FREE, o3.cc.FIXED) o3.Fix4DOF(osi, nm[5], o3.cc.FREE, o3.cc.FREE, o3.cc.FREE, o3.cc.FIXED) o3.Fix4DOF(osi, nm[6], o3.cc.FIXED, o3.cc.FREE, o3.cc.FREE, o3.cc.FIXED) o3.Fix4DOF(osi, nm[7], o3.cc.FIXED, o3.cc.FIXED, o3.cc.FREE, o3.cc.FIXED) # Define material p_conf = -300.0 # confinement stress dev_disp = -0.3 # deviatoric strain perm = 1.0e-10 # permeability e_curr = 0.8 # void ratio mzmod = o3.nd_material.ManzariDafalias(osi, g0=125, nu=0.05, e_init=0.8, m_c=1.25, c_c=0.712, lambda_c=0.019, e_0=0.934, ksi=0.7, p_atm=100, m_yield=0.01, h_0=7.05, c_h=0.968, n_b=1.1, a_0=0.704, n_d=3.5, z_max=4, c_z=600, den=1.42) water_bulk_mod = 2.2e6 f_den = 1.0 ele = o3.element.SSPbrickUP(osi, nm, mzmod, water_bulk_mod, f_den, perm, perm, perm, void=e_curr, alpha=1.5e-9, b1=0.0, b2=0.0, b3=0.0) all_stresses_cache = o3.recorder.ElementToArrayCache(osi, ele, arg_vals=['stress'], fname='stresses_03.txt') all_strains_cache = o3.recorder.ElementToArrayCache(osi, ele, arg_vals=['strain'], fname='strains_03.txt') nodes_cache = o3.recorder.NodesToArrayCache(osi, nm, dofs=[1, 2, 3], res_type='disp') o3.constraints.Penalty(osi, 1.0e18, 1.0e18) o3.test_check.NormDispIncr(osi, tol=1.0e-5, max_iter=20, p_flag=0) o3.algorithm.Newton(osi) o3.numberer.RCM(osi) o3.system.BandGeneral(osi) o3.integrator.Newmark(osi, gamma=0.5, beta=0.25) o3.rayleigh.Rayleigh(osi, a0, 0.0, a1, 0.0) o3.analysis.Transient(osi) # Add static vertical pressure and stress bias p_node = p_conf / 4.0 time_series = o3.time_series.Path(osi, time=[0, 10000, 1e10], values=[0, 1, 1]) o3.pattern.Plain(osi, time_series) o3.Load(osi, nm[0], [p_node, 0, 0, 0]) o3.Load(osi, nm[1], [p_node, p_node, 0, 0]) o3.Load(osi, nm[2], [0, p_node, 0, 0]) o3.Load(osi, nm[3], [0, 0, 0, 0]) o3.Load(osi, nm[4], [p_node, 0, p_node, 0]) o3.Load(osi, nm[5], [p_node, p_node, p_node, 0]) o3.Load(osi, nm[6], [0, p_node, p_node, 0]) o3.Load(osi, nm[7], [0, 0, p_node, 0]) o3.analyze(osi, num_inc=100, dt=100) o3.analyze(osi, 50, 100) # Close the drainage valves for node in nm: o3.remove_sp(osi, node, dof=4) o3.analyze(osi, 50, dt=100) z_vert = o3.get_node_disp(osi, nm[4], o3.cc.DOF3D_Z) l_values = [1, 1 + dev_disp / z_vert, 1 + dev_disp / z_vert] ts2 = o3.time_series.Path(osi, time=[20000, 1020000, 10020000], values=l_values, factor=1) o3.pattern.Plain(osi, ts2, fact=1.) o3.SP(osi, nm[4], dof=o3.cc.DOF3D_Z, dof_values=[z_vert]) o3.SP(osi, nm[5], dof=o3.cc.DOF3D_Z, dof_values=[z_vert]) o3.SP(osi, nm[6], dof=o3.cc.DOF3D_Z, dof_values=[z_vert]) o3.SP(osi, nm[7], dof=o3.cc.DOF3D_Z, dof_values=[z_vert]) o3.extensions.to_py_file(osi) dt = 100 num_step = 10000 rem_step = num_step def sub_step_analyze(dt, sub_step): loc_success = 0 if sub_step > 10: return -10 for i in range(1, 3): print(f'try dt = {dt}') loc_success = o3.analyze(osi, 1, dt) if loc_success != 0: loc_success = sub_step_analyze(dt / 2., sub_step + 1) if success == -1: print('Did not converge.') return loc_success else: if i == 1: print(f'Substep {sub_step}: Left side converged with dt = {dt}') else: print(f'Substep {sub_step}: Right side converged with dt = {dt}') return loc_success print('Start analysis') start_t = time.process_time() success = 0 while success != -10: sub_step = 0 success = o3.analyze(osi, rem_step, dt) if success == 0: print('Analysis Finished') break else: cur_time = o3.get_time(osi) print(f'Analysis failed at {cur_time} . Try substepping.') success = sub_step_analyze(dt / 2, sub_step + 1) cur_step = int((cur_time - 20000) / dt + 1) rem_step = int(num_step - cur_step) print(f'Current step: {cur_step}, Remaining steps: {rem_step}') end_t = time.process_time() print(f'loading analysis execution time: {end_t - start_t:.2f} seconds.') o3.wipe(osi) all_stresses = all_stresses_cache.collect() all_strains = all_strains_cache.collect() disps = nodes_cache.collect() return all_stresses, all_strains, disps pass
def run(show=0): # Load a ground motion record_filename = 'test_motion_dt0p01.txt' asig = eqsig.load_asig(ap.MODULE_DATA_PATH + 'gms/' + record_filename, m=0.5) # Define inelastic SDOF period = 1.0 xi = 0.05 mass = 1.0 f_yield = 1.5 # Reduce this to make it nonlinear r_post = 0.0 # Initialise OpenSees instance osi = o3.OpenSeesInstance(ndm=2, state=0) # Establish nodes bot_node = o3.node.Node(osi, 0, 0) top_node = o3.node.Node(osi, 0, 0) # Fix bottom node o3.Fix3DOF(osi, top_node, o3.cc.FREE, o3.cc.FIXED, o3.cc.FIXED) o3.Fix3DOF(osi, bot_node, o3.cc.FIXED, o3.cc.FIXED, o3.cc.FIXED) # Set out-of-plane DOFs to be slaved o3.EqualDOF(osi, top_node, bot_node, [o3.cc.Y, o3.cc.ROTZ]) # nodal mass (weight / g): o3.Mass(osi, top_node, mass, 0., 0.) # Define material k_spring = 4 * np.pi**2 * mass / period**2 bilinear_mat = o3.uniaxial_material.Steel01(osi, fy=f_yield, e0=k_spring, b=r_post) # Assign zero length element, # Note: pass actual node and material objects into element o3.element.ZeroLength(osi, [bot_node, top_node], mats=[bilinear_mat], dirs=[o3.cc.DOF2D_X], r_flag=1) # Define the dynamic analysis # Define the dynamic analysis acc_series = o3.time_series.Path(osi, dt=asig.dt, values=-1 * asig.values) # should be negative o3.pattern.UniformExcitation(osi, dir=o3.cc.X, accel_series=acc_series) # set damping based on first eigen mode angular_freq = o3.get_eigen(osi, solver='fullGenLapack', n=1)[0]**0.5 beta_k = 2 * xi / angular_freq o3.rayleigh.Rayleigh(osi, alpha_m=0.0, beta_k=beta_k, beta_k_init=0.0, beta_k_comm=0.0) # Run the dynamic analysis o3.wipe_analysis(osi) # Run the dynamic analysis o3.algorithm.Newton(osi) o3.system.SparseGeneral(osi) o3.numberer.RCM(osi) o3.constraints.Transformation(osi) o3.integrator.Newmark(osi, gamma=0.5, beta=0.25) o3.analysis.Transient(osi) o3.test_check.EnergyIncr(osi, tol=1.0e-10, max_iter=10) analysis_time = asig.time[-1] analysis_dt = 0.001 outputs = { "time": [], "rel_disp": [], "rel_accel": [], "rel_vel": [], "force": [] } while o3.get_time(osi) < analysis_time: o3.analyze(osi, 1, analysis_dt) curr_time = o3.get_time(osi) outputs["time"].append(curr_time) outputs["rel_disp"].append(o3.get_node_disp(osi, top_node, o3.cc.X)) outputs["rel_vel"].append(o3.get_node_vel(osi, top_node, o3.cc.X)) outputs["rel_accel"].append(o3.get_node_accel(osi, top_node, o3.cc.X)) o3.gen_reactions(osi) outputs["force"].append(-o3.get_node_reaction( osi, bot_node, o3.cc.X)) # Negative since diff node o3.wipe(osi) for item in outputs: outputs[item] = np.array(outputs[item]) if show: import matplotlib.pyplot as plt plt.plot(outputs['time'], outputs['rel_disp'], label='o3seespy') periods = np.array([period]) # Compare closed form elastic solution from eqsig import sdof resp_u, resp_v, resp_a = sdof.response_series(motion=asig.values, dt=asig.dt, periods=periods, xi=xi) plt.plot(asig.time, resp_u[0], ls='--', label='Elastic') plt.legend() plt.show()
def get_inelastic_response(mass, k_spring, f_yield, motion, dt, xi=0.05, r_post=0.0): """ Run seismic analysis of a nonlinear SDOF :param mass: SDOF mass :param k_spring: spring stiffness :param f_yield: yield strength :param motion: list, acceleration values :param dt: float, time step of acceleration values :param xi: damping ratio :param r_post: post-yield stiffness :return: """ osi = o3.OpenSeesInstance(ndm=2) # Establish nodes bot_node = o3.node.Node(osi, 0, 0) top_node = o3.node.Node(osi, 0, 0) # Fix bottom node o3.Fix3DOF(osi, top_node, o3.cc.FREE, o3.cc.FIXED, o3.cc.FIXED) o3.Fix3DOF(osi, bot_node, o3.cc.FIXED, o3.cc.FIXED, o3.cc.FIXED) # Set out-of-plane DOFs to be slaved o3.EqualDOF(osi, top_node, bot_node, [o3.cc.Y, o3.cc.DOF2D_ROTZ]) # nodal mass (weight / g): o3.Mass(osi, top_node, mass, 0., 0.) # Define material bilinear_mat = o3.uniaxial_material.Steel01(osi, fy=f_yield, e0=k_spring, b=r_post) # Assign zero length element, # Note: pass actual node and material objects into element o3.element.ZeroLength(osi, [bot_node, top_node], mats=[bilinear_mat], dirs=[o3.cc.DOF2D_X], r_flag=1) # Define the dynamic analysis values = list(-1 * motion) # should be negative acc_series = o3.time_series.Path(osi, dt, values) o3.pattern.UniformExcitation(osi, o3.cc.X, accel_series=acc_series) # set damping based on first eigen mode angular_freq2 = o3.get_eigen(osi, solver='fullGenLapack', n=1) if hasattr(angular_freq2, '__len__'): angular_freq2 = angular_freq2[0] angular_freq = angular_freq2**0.5 beta_k = 2 * xi / angular_freq o3.rayleigh.Rayleigh(osi, alpha_m=0.0, beta_k=beta_k, beta_k_init=0.0, beta_k_comm=0.0) # Run the dynamic analysis o3.wipe_analysis(osi) newmark_gamma = 0.5 newmark_beta = 0.25 o3.algorithm.Newton(osi) o3.constraints.Transformation(osi) o3.algorithm.Newton(osi) o3.numberer.RCM(osi) o3.system.SparseGeneral(osi) o3.integrator.Newmark(osi, newmark_gamma, newmark_beta) o3.analysis.Transient(osi) o3.test_check.EnergyIncr(osi, tol=1.0e-10, max_iter=10) analysis_time = (len(values) - 1) * dt analysis_dt = 0.001 outputs = {"time": [], "rel_disp": [], "rel_accel": [], "force": []} o3.record(osi) curr_time = o3.get_time(osi) while curr_time < analysis_time: outputs["time"].append(curr_time) outputs["rel_disp"].append(o3.get_node_disp(osi, top_node, o3.cc.X)) outputs["rel_accel"].append(o3.get_node_accel(osi, top_node, o3.cc.X)) o3.gen_reactions(osi) outputs["force"].append(-o3.get_node_reaction( osi, bot_node, o3.cc.X)) # Negative since diff node o3.analyze(osi, 1, analysis_dt) curr_time = o3.get_time(osi) o3.wipe(osi) for item in outputs: outputs[item] = np.array(outputs[item]) return outputs
def site_response(sp, asig, freqs=(0.5, 10), xi=0.03, analysis_dt=0.001, dy=0.5, analysis_time=None, outs=None, rec_dt=None, use_explicit=0): """ Run seismic analysis of a soil profile that has a compliant base Parameters ---------- sp: sfsimodels.SoilProfile object A soil profile asig: eqsig.AccSignal object An acceleration signal Returns ------- """ if analysis_time is None: analysis_time = asig.time[-1] if outs is None: outs = { 'ACCX': [0] } # Export the horizontal acceleration at the surface if rec_dt is None: rec_dt = analysis_dt osi = o3.OpenSeesInstance(ndm=2, ndf=2, state=3) assert isinstance(sp, sm.SoilProfile) sp.gen_split(props=['shear_vel', 'unit_mass'], target=dy) thicknesses = sp.split["thickness"] n_node_rows = len(thicknesses) + 1 node_depths = np.cumsum(sp.split["thickness"]) node_depths = np.insert(node_depths, 0, 0) ele_depths = (node_depths[1:] + node_depths[:-1]) / 2 unit_masses = sp.split["unit_mass"] / 1e3 grav = 9.81 omega_1 = 2 * np.pi * freqs[0] omega_2 = 2 * np.pi * freqs[1] a0 = 2 * xi * omega_1 * omega_2 / (omega_1 + omega_2) a1 = 2 * xi / (omega_1 + omega_2) k0 = 0.5 pois = k0 / (1 + k0) newmark_gamma = 0.5 newmark_beta = 0.25 ele_width = min(thicknesses) # Define nodes and set boundary conditions for simple shear deformation # Start at top and build down? sn = [[o3.node.Node(osi, 0, 0), o3.node.Node(osi, ele_width, 0)]] for i in range(1, n_node_rows): # Establish left and right nodes sn.append([ o3.node.Node(osi, 0, -node_depths[i]), o3.node.Node(osi, ele_width, -node_depths[i]) ]) # set x and y dofs equal for left and right nodes o3.EqualDOF(osi, sn[i][0], sn[i][1], [o3.cc.X, o3.cc.Y]) # Fix base nodes o3.Fix2DOF(osi, sn[-1][0], o3.cc.FREE, o3.cc.FIXED) o3.Fix2DOF(osi, sn[-1][1], o3.cc.FREE, o3.cc.FIXED) # Define dashpot nodes dashpot_node_l = o3.node.Node(osi, 0, -node_depths[-1]) dashpot_node_2 = o3.node.Node(osi, 0, -node_depths[-1]) o3.Fix2DOF(osi, dashpot_node_l, o3.cc.FIXED, o3.cc.FIXED) o3.Fix2DOF(osi, dashpot_node_2, o3.cc.FREE, o3.cc.FIXED) # define equal DOF for dashpot and soil base nodes o3.EqualDOF(osi, sn[-1][0], sn[-1][1], [o3.cc.X]) o3.EqualDOF(osi, sn[-1][0], dashpot_node_2, [o3.cc.X]) # define materials ele_thick = 1.0 # m soil_mats = [] prev_args = [] prev_kwargs = {} prev_sl_type = None eles = [] for i in range(len(thicknesses)): y_depth = ele_depths[i] sl_id = sp.get_layer_index_by_depth(y_depth) sl = sp.layer(sl_id) app2mod = {} if y_depth > sp.gwl: umass = sl.unit_sat_mass / 1e3 else: umass = sl.unit_dry_mass / 1e3 # Define material sl_class = o3.nd_material.ElasticIsotropic sl.e_mod = 2 * sl.g_mod * (1 + sl.poissons_ratio) / 1e3 app2mod['rho'] = 'unit_moist_mass' overrides = {'nu': sl.poissons_ratio, 'unit_moist_mass': umass} args, kwargs = o3.extensions.get_o3_kwargs_from_obj( sl, sl_class, custom=app2mod, overrides=overrides) changed = 0 if sl.type != prev_sl_type or len(args) != len(prev_args) or len( kwargs) != len(prev_kwargs): changed = 1 else: for j, arg in enumerate(args): if not np.isclose(arg, prev_args[j]): changed = 1 for pm in kwargs: if pm not in prev_kwargs or not np.isclose( kwargs[pm], prev_kwargs[pm]): changed = 1 if changed: mat = sl_class(osi, *args, **kwargs) prev_sl_type = sl.type prev_args = copy.deepcopy(args) prev_kwargs = copy.deepcopy(kwargs) soil_mats.append(mat) # def element nodes = [sn[i + 1][0], sn[i + 1][1], sn[i][1], sn[i][0]] # anti-clockwise eles.append( o3.element.SSPquad(osi, nodes, mat, o3.cc.PLANE_STRAIN, ele_thick, 0.0, -grav)) # eles.append(o3.element.Quad(osi, nodes, mat=mat, otype=o3.cc.PLANE_STRAIN, thick=ele_thick, pressure=0.0, rho=unit_masses[i], b2=grav)) # define material and element for viscous dampers base_sl = sp.layer(sp.n_layers) c_base = ele_width * base_sl.unit_dry_mass / 1e3 * sp.get_shear_vel_at_depth( sp.height) dashpot_mat = o3.uniaxial_material.Viscous(osi, c_base, alpha=1.) o3.element.ZeroLength(osi, [dashpot_node_l, dashpot_node_2], mats=[dashpot_mat], dirs=[o3.cc.DOF2D_X]) # Static analysis o3.constraints.Transformation(osi) o3.test_check.NormDispIncr(osi, tol=1.0e-4, max_iter=30, p_flag=0) o3.algorithm.Newton(osi) o3.numberer.RCM(osi) o3.system.ProfileSPD(osi) o3.integrator.Newmark(osi, newmark_gamma, newmark_beta) o3.analysis.Transient(osi) o3.analyze(osi, 40, 1.) for i in range(len(soil_mats)): if isinstance(soil_mats[i], o3.nd_material.PM4Sand) or isinstance( soil_mats[i], o3.nd_material.PressureIndependMultiYield): o3.update_material_stage(osi, soil_mats[i], 1) o3.analyze(osi, 50, 0.5) # reset time and analysis o3.set_time(osi, 0.0) o3.wipe_analysis(osi) ods = {} for otype in outs: if otype == 'ACCX': ods['ACCX'] = [] if isinstance(outs['ACCX'], str) and outs['ACCX'] == 'all': ods['ACCX'] = o3.recorder.NodesToArrayCache(osi, nodes=sn[:][0], dofs=[o3.cc.X], res_type='accel', dt=rec_dt) else: for i in range(len(outs['ACCX'])): ind = np.argmin(abs(node_depths - outs['ACCX'][i])) ods['ACCX'].append( o3.recorder.NodeToArrayCache(osi, node=sn[ind][0], dofs=[o3.cc.X], res_type='accel', dt=rec_dt)) if otype == 'TAU': ods['TAU'] = [] if isinstance(outs['TAU'], str) and outs['TAU'] == 'all': ods['TAU'] = o3.recorder.ElementsToArrayCache( osi, eles=eles, arg_vals=['stress'], dt=rec_dt) else: for i in range(len(outs['TAU'])): ind = np.argmin(abs(ele_depths - outs['TAU'][i])) ods['TAU'].append( o3.recorder.ElementToArrayCache(osi, ele=eles[ind], arg_vals=['stress'], dt=rec_dt)) if otype == 'STRS': ods['STRS'] = [] if isinstance(outs['STRS'], str) and outs['STRS'] == 'all': ods['STRS'] = o3.recorder.ElementsToArrayCache( osi, eles=eles, arg_vals=['strain'], dt=rec_dt) else: for i in range(len(outs['STRS'])): ind = np.argmin(abs(ele_depths - outs['STRS'][i])) ods['STRS'].append( o3.recorder.ElementToArrayCache(osi, ele=eles[ind], arg_vals=['strain'], dt=rec_dt)) # Define the dynamic analysis ts_obj = o3.time_series.Path(osi, dt=asig.dt, values=asig.velocity * 1, factor=c_base) o3.pattern.Plain(osi, ts_obj) o3.Load(osi, sn[-1][0], [1., 0.]) # Run the dynamic analysis if use_explicit: o3.system.FullGeneral(osi) # o3.algorithm.Newton(osi) o3.algorithm.Linear(osi) # o3.integrator.ExplicitDifference(osi) # o3.integrator.CentralDifference(osi) o3.integrator.NewmarkExplicit(osi, newmark_gamma) # also works else: o3.system.SparseGeneral(osi) o3.algorithm.Newton(osi) o3.integrator.Newmark(osi, newmark_gamma, newmark_beta) o3.numberer.RCM(osi) o3.constraints.Transformation(osi) n = 2 modal_damp = 0 omegas = np.array(o3.get_eigen(osi, n=n))**0.5 response_periods = 2 * np.pi / omegas print('response_periods: ', response_periods) if not modal_damp: o3.rayleigh.Rayleigh(osi, a0, a1, 0, 0) else: o3.ModalDamping(osi, [xi, xi]) o3.analysis.Transient(osi) o3.test_check.NormDispIncr(osi, tol=1.0e-6, max_iter=10) while o3.get_time(osi) < analysis_time: print(o3.get_time(osi)) if o3.analyze(osi, 1, analysis_dt): print('failed') break o3.wipe(osi) out_dict = {} for otype in ods: if isinstance(ods[otype], list): out_dict[otype] = [] for i in range(len(ods[otype])): out_dict[otype].append(ods[otype][i].collect()) out_dict[otype] = np.array(out_dict[otype]) else: out_dict[otype] = ods[otype].collect().T out_dict['time'] = np.arange(0, analysis_time, rec_dt) return out_dict
def get_response(bd, asig, dtype, l_ph): """ Compute the response of a nonlinear lollipop on a foundation with linear/nonlinear soil Units are N, m, s :param bd: SDOF building object :param asig: Acceleration signal object :return: """ osi = o3.OpenSeesInstance(ndm=2, state=3) # Establish nodes top_ss_node = o3.node.Node(osi, 0, bd.h_eff) bot_ss_node = o3.node.Node(osi, 0, 0) # Fix bottom node o3.Fix3DOF(osi, bot_ss_node, o3.cc.FIXED, o3.cc.FIXED, o3.cc.FIXED) # nodal mass (weight / g): o3.Mass(osi, top_ss_node, bd.mass_eff, 0.0, 0) # Define a column element with a plastic hinge at base transf = o3.geom_transf.Linear2D(osi, []) # can change for P-delta effects area = 1.0 e_mod = 200.0e9 iz = bd.k_eff * bd.h_eff ** 3 / (3 * e_mod) ele_nodes = [bot_ss_node, top_ss_node] # Superstructure element vert_ele = o3.element.ElasticBeamColumn2D(osi, ele_nodes, area=area, e_mod=e_mod, iz=iz, transf=transf) omega = 2 * np.pi / bd.t_fixed # define superstructure damping using rotational spring approach from Millen et al. (2017) to avoid double damping if dtype == 'rot_dashpot': cxx = bd.xi * 2 * np.sqrt(bd.mass_eff * bd.k_eff) equiv_c_rot = cxx * (2.0 / 3) ** 2 * bd.h_eff ** 2 ss_rot_dashpot_mat = o3.uniaxial_material.Viscous(osi, equiv_c_rot, alpha=1.) sfi_dashpot_ele = o3.element.TwoNodeLink(osi, [bot_ss_node, top_ss_node], mats=[ss_rot_dashpot_mat], dirs=[o3.cc.DOF2D_ROTZ]) elif dtype == 'horz_dashpot': cxx = bd.xi * 2 * np.sqrt(bd.mass_eff * bd.k_eff) ss_rot_dashpot_mat = o3.uniaxial_material.Viscous(osi, cxx, alpha=1.) sfi_dashpot_ele = o3.element.ZeroLength(osi, [bot_ss_node, top_ss_node], mats=[ss_rot_dashpot_mat], dirs=[o3.cc.X]) else: beta_k = 2 * bd.xi / omega o3.rayleigh.Rayleigh(osi, 0, 0, beta_k_init=beta_k, beta_k_comm=0.0) # Define the input motion for the dynamic analysis acc_series = o3.time_series.Path(osi, dt=asig.dt, values=-asig.values) # should be negative o3.pattern.UniformExcitation(osi, dir=o3.cc.X, accel_series=acc_series) print('loaded gm') o3.wipe_analysis(osi) o3.algorithm.Newton(osi) o3.system.SparseGeneral(osi) o3.numberer.RCM(osi) o3.constraints.Transformation(osi) o3.integrator.Newmark(osi, 0.5, 0.25) o3.analysis.Transient(osi) o3.test_check.NormDispIncr(osi, tol=1.0e-6, max_iter=10) analysis_time = asig.time[-1] analysis_dt = 0.001 # define outputs of analysis od = { "time": o3.recorder.TimeToArrayCache(osi), "rel_deck_disp": o3.recorder.NodeToArrayCache(osi, top_ss_node, [o3.cc.DOF2D_X], 'disp'), "deck_accel": o3.recorder.NodeToArrayCache(osi, top_ss_node, [o3.cc.DOF2D_X], 'accel'), "deck_rot": o3.recorder.NodeToArrayCache(osi, top_ss_node, [o3.cc.DOF2D_ROTZ], 'disp'), "chord_rots": o3.recorder.ElementToArrayCache(osi, vert_ele, arg_vals=['chordRotation']), "col_forces": o3.recorder.ElementToArrayCache(osi, vert_ele, arg_vals=['force']), } if dtype in ['rot_dashpot', 'horz_dashpot']: od['dashpot_force'] = o3.recorder.ElementToArrayCache(osi, sfi_dashpot_ele, arg_vals=['force']) o3.analyze(osi, int(analysis_time / analysis_dt), analysis_dt) o3.wipe(osi) for item in od: od[item] = od[item].collect() od['col_shear'] = -od['col_forces'][:, 0] od['col_moment'] = od['col_forces'][:, 2] od['hinge_rotation'] = od['chord_rots'][:, 1] od['hinge_rotation1'] = -od['chord_rots'][:, 2] del od['col_forces'] del od['chord_rots'] return od
def get_moment_curvature(axial_load=100., max_curve=0.001, num_incr=500): osi = o3.OpenSeesInstance(ndm=2, ndf=3, state=3) fc = 4.0 # e_mod = 57000.0 * np.sqrt(fc * 1000.0) / 1e3 conc_conf = o3.uniaxial_material.Concrete01(osi, fpc=-5.0, epsc0=-0.005, fpcu=-3.5, eps_u=-0.02) conc_unconf = o3.uniaxial_material.Concrete01(osi, fpc=-fc, epsc0=-0.002, fpcu=0.0, eps_u=-0.006) rebar = o3.uniaxial_material.Steel01(osi, fy=60.0, e0=30000.0, b=0.02) h = 18.0 b = 18.0 cover = 2.5 gj = 1.0E10 nf_core_y = 8 nf_core_z = 8 nf_cover_y = 10 nf_cover_z = 10 n_bars = 3 bar_area = 0.79 edge_y = h / 2.0 edge_z = b / 2.0 core_y = edge_y - cover core_z = edge_z - cover sect = o3.section.Fiber(osi, gj=gj) # define the core patch o3.patch.Quad(osi, conc_conf, nf_core_z, nf_core_y, # core, counter-clockwise (diagonals at corners) crds_i=[-core_y, core_z], crds_j=[-core_y, -core_z], crds_k=[core_y, -core_z], crds_l=[core_y, core_z]) o3.patch.Quad(osi, conc_unconf, 1, nf_cover_y, # right cover, counter-clockwise (diagonals at corners) crds_i=[-edge_y, edge_z], crds_j=[-core_y, core_z], crds_k=[core_y, core_z], crds_l=[edge_y, edge_z]) o3.patch.Quad(osi, conc_unconf, 1, nf_cover_y, # left cover crds_i=[-core_y, -core_z], crds_j=[-edge_y, -edge_z], crds_k=[edge_y, -edge_z], crds_l=[core_y, -core_z]) o3.patch.Quad(osi, conc_unconf, nf_cover_z, 1, # bottom cover crds_i=[-edge_y, edge_z], crds_j=[-edge_y, -edge_z], crds_k=[-core_y, -core_z], crds_l=[-core_y, core_z]) o3.patch.Quad(osi, conc_unconf, nf_cover_z, 1, # top cover crds_i=[core_y, core_z], crds_j=[core_y, -core_z], crds_k=[edge_y, -edge_z], crds_l=[edge_y, edge_z]) o3.layer.Straight(osi, rebar, n_bars, bar_area, start=[-core_y, core_z], end=[-core_y, -core_z]) o3.layer.Straight(osi, rebar, n_bars, bar_area, start=[core_y, core_z], end=[core_y, -core_z]) spacing_y = 2 * core_y / (n_bars - 1) remaining_bars = n_bars - 2 o3.layer.Straight(osi, rebar, remaining_bars, bar_area, start=[core_y - spacing_y, core_z], end=[-core_y + spacing_y, core_z]) o3.layer.Straight(osi, rebar, remaining_bars, bar_area, start=[core_y - spacing_y, -core_z], end=[-core_y + spacing_y, -core_z]) n1 = o3.node.Node(osi, 0.0, 0.0) n2 = o3.node.Node(osi, 0.0, 0.0) o3.Fix3DOF(osi, n1, 1, 1, 1) o3.Fix3DOF(osi, n2, 0, 1, 0) ele = o3.element.ZeroLengthSection(osi, [n1, n2], sect) nd = o3.recorder.NodeToArrayCache(osi, n2, dofs=[3], res_type='disp') nm = o3.recorder.NodeToArrayCache(osi, n1, dofs=[3], res_type='reaction') ts = o3.time_series.Constant(osi) o3.pattern.Plain(osi, ts) o3.Load(osi, n2, load_values=[axial_load, 0.0, 0.0]) o3.system.BandGeneral(osi) o3.numberer.Plain(osi) o3.constraints.Plain(osi) o3.test.NormUnbalance(osi, tol=1.0e-9, max_iter=10) o3.algorithm.Newton(osi) o3.integrator.LoadControl(osi, incr=0.0) o3.analysis.Static(osi) o3.analyze(osi, 1) # ts = o3.time_series.Linear(osi) o3.pattern.Plain(osi, ts) o3.Load(osi, n2, load_values=[0.0, 0.0, 1.0]) d_cur = max_curve / num_incr o3.integrator.DisplacementControl(osi, n2, o3.cc.DOF2D_ROTZ, d_cur, 1, d_cur, d_cur) o3.analyze(osi, num_incr) o3.wipe(osi) curvature = nd.collect() moment = -nm.collect() return moment, curvature
def _run_dyn_1d_site_response(region_based=None): osi = o3.OpenSeesInstance(ndm=2, ndf=2, state=3) # Establish nodes node_depths = np.arange(0, 5, 1) n_node_rows = len(node_depths) x_nodes = np.arange(0, 2, 1) nx = len(x_nodes) nd = {} for yy in range(0, len(node_depths)): for xx in range(nx): # Establish left and right nodes nd[f"X{xx}Y{yy}"] = o3.node.Node(osi, x_nodes[xx], -node_depths[yy]) # set x and y dofs equal for left and right nodes o3.EqualDOF(osi, nd[f"X0Y{yy}"], nd[f"X{nx - 1}Y{yy}"], [o3.cc.X]) # Fix base nodes for xx in range(nx): o3.Fix2DOF(osi, nd[f"X{xx}Y{n_node_rows -1}"], o3.cc.FIXED, o3.cc.FIXED) for yy in range(0, len(node_depths) - 1): for xx in range(nx): o3.Fix2DOF(osi, nd[f"X{xx}Y{yy}"], o3.cc.FREE, o3.cc.FIXED) vs = 150.0 rho = 1.8 g_mod = vs**2 * rho poissons_ratio = 0.3 e_mod = 2 * g_mod * (1 + poissons_ratio) ele_thick = 1.0 soil_mat = o3.nd_material.ElasticIsotropic(osi, e_mod=e_mod, nu=poissons_ratio, rho=rho) eles = [] for yy in range(0, len(node_depths) - 1): for xx in range(nx - 1): # def element nodes = [ nd[f"X{xx}Y{yy + 1}"], nd[f"X{xx + 1}Y{yy + 1}"], nd[f"X{xx + 1}Y{yy}"], nd[f"X{xx}Y{yy}"] ] eles.append( o3.element.SSPquad(osi, nodes, soil_mat, o3.cc.PLANE_STRAIN, ele_thick, 0.0, 0.0)) freqs = np.array([0.5, 15.0]) xi = 0.1 # high damping to see effects ang_f = np.pi / freqs alpha_m = xi * 2.0 * ang_f[0] * ang_f[1] / (ang_f[0] + ang_f[1] ) # mass proportional beta_k = xi * 2.0 / (ang_f[0] + ang_f[1]) # stiffness proportional if region_based == 'node': o3.region.NodeRegion(osi, nodes='all', rayleigh={ 'alpha_m': alpha_m, 'beta_k_init': beta_k }) if region_based == 'ele': o3.region.ElementRegion(osi, eles='all', rayleigh={ 'alpha_m': alpha_m, 'beta_k_init': beta_k }) else: o3.rayleigh.Rayleigh(osi, alpha_m=alpha_m, beta_k=0.0, beta_k_init=beta_k, beta_k_comm=0.0) # Define the dynamic analysis dt = 0.01 vals = np.sin(np.linspace(0, np.pi, 50)) acc_series = o3.time_series.Path(osi, dt=dt, values=-vals) # should be negative o3.pattern.UniformExcitation(osi, dir=o3.cc.X, accel_series=acc_series) # Run the dynamic analysis o3.wipe_analysis(osi) o3.algorithm.Newton(osi) o3.system.SparseGeneral(osi) o3.numberer.RCM(osi) o3.constraints.Transformation(osi) o3.integrator.Newmark(osi, 0.5, 0.25) o3.analysis.Transient(osi) o3.test_check.EnergyIncr(osi, tol=1.0e-3, max_iter=10) analysis_time = 1.0 analysis_dt = 0.001 rec_dt = 0.01 tn = o3.recorder.NodeToArrayCache(osi, nd["X0Y0"], [o3.cc.DOF2D_X], 'accel', dt=rec_dt) if region_based: o3.extensions.to_py_file(osi, 'wr.py') else: o3.extensions.to_py_file(osi, 'wor.py') curr_time = o3.get_time(osi) while curr_time < analysis_time: status = o3.analyze(osi, 1, analysis_dt) curr_time = o3.get_time(osi) if status != 0: print('Not ok') print(status) o3.wipe(osi) x_acc = tn.collect() return x_acc
def site_response(sp, asig, freqs=(0.5, 10), xi=0.03, dtype='rayleigh', analysis_dt=0.001, dy=0.5, analysis_time=None, outs=None, rec_dt=None): """ Run seismic analysis of a soil profile Parameters ---------- sp: sfsimodels.SoilProfile object A soil profile asig: eqsig.AccSignal object An acceleration signal Returns ------- """ if analysis_time is None: analysis_time = asig.time[-1] if outs is None: outs = { 'ACCX': [0] } # Export the horizontal acceleration at the surface if rec_dt is None: rec_dt = analysis_dt osi = o3.OpenSeesInstance(ndm=2, ndf=2, state=3) assert isinstance(sp, sm.SoilProfile) sp.gen_split(props=['shear_vel', 'unit_mass'], target=dy) thicknesses = sp.split["thickness"] n_node_rows = len(thicknesses) + 1 node_depths = np.cumsum(sp.split["thickness"]) node_depths = np.insert(node_depths, 0, 0) ele_depths = (node_depths[1:] + node_depths[:-1]) / 2 grav = 9.81 k0 = 0.5 pois = k0 / (1 + k0) newmark_gamma = 0.5 newmark_beta = 0.25 ele_width = min(thicknesses) # Define nodes and set boundary conditions for simple shear deformation # Start at top and build down? sn = [[o3.node.Node(osi, 0, 0), o3.node.Node(osi, ele_width, 0)]] for i in range(1, n_node_rows): # Establish left and right nodes sn.append([ o3.node.Node(osi, 0, -node_depths[i]), o3.node.Node(osi, ele_width, -node_depths[i]) ]) # set x and y dofs equal for left and right nodes o3.EqualDOF(osi, sn[i][0], sn[i][1], [o3.cc.X, o3.cc.Y]) # Fix base nodes o3.Fix2DOF(osi, sn[-1][0], o3.cc.FIXED, o3.cc.FIXED) o3.Fix2DOF(osi, sn[-1][1], o3.cc.FIXED, o3.cc.FIXED) # define materials ele_thick = 1.0 # m soil_mats = [] prev_pms = [0, 0, 0] eles = [] for i in range(len(thicknesses)): y_depth = ele_depths[i] sl_id = sp.get_layer_index_by_depth(y_depth) sl = sp.layer(sl_id) # Define material e_mod = 2 * sl.g_mod * (1 + sl.poissons_ratio) umass = sl.unit_dry_mass nu = sl.poissons_ratio pms = [e_mod, nu, umass] changed = 0 for pp in range(len(pms)): if not np.isclose(pms[pp], prev_pms[pp]): changed = 1 if changed: mat = o3.nd_material.ElasticIsotropic(osi, e_mod=e_mod, nu=nu, rho=umass) soil_mats.append(mat) # def element nodes = [sn[i + 1][0], sn[i + 1][1], sn[i][1], sn[i][0]] # anti-clockwise eles.append( o3.element.SSPquad(osi, nodes, mat, o3.cc.PLANE_STRAIN, ele_thick, 0.0, grav * umass)) # Static analysis o3.constraints.Transformation(osi) o3.test_check.NormDispIncr(osi, tol=1.0e-5, max_iter=30, p_flag=0) o3.algorithm.Newton(osi) o3.numberer.RCM(osi) o3.system.ProfileSPD(osi) o3.integrator.Newmark(osi, 5. / 6, 4. / 9) # include numerical damping o3.analysis.Transient(osi) o3.analyze(osi, 40, 1.) # reset time and analysis o3.set_time(osi, 0.0) o3.wipe_analysis(osi) ods = {} for otype in outs: if otype == 'ACCX': ods['ACCX'] = [] if isinstance(outs['ACCX'], str) and outs['ACCX'] == 'all': ods['ACCX'] = o3.recorder.NodesToArrayCache(osi, nodes=sn[:][0], dofs=[o3.cc.X], res_type='accel', dt=rec_dt) else: for i in range(len(outs['ACCX'])): ind = np.argmin(abs(node_depths - outs['ACCX'][i])) ods['ACCX'].append( o3.recorder.NodeToArrayCache(osi, node=sn[ind][0], dofs=[o3.cc.X], res_type='accel', dt=rec_dt)) # Run the dynamic analysis o3.algorithm.Newton(osi) o3.system.SparseGeneral(osi) o3.numberer.RCM(osi) o3.constraints.Transformation(osi) o3.integrator.Newmark(osi, newmark_gamma, newmark_beta) if dtype == 'rayleigh': omega_1 = 2 * np.pi * freqs[0] omega_2 = 2 * np.pi * freqs[1] a0 = 2 * xi * omega_1 * omega_2 / (omega_1 + omega_2) a1 = 2 * xi / (omega_1 + omega_2) o3.rayleigh.Rayleigh(osi, a0, a1, 0, 0) else: n = 20 omegas = np.array(o3.get_eigen(osi, n=n))**0.5 o3.ModalDamping(osi, [xi]) o3.analysis.Transient(osi) o3.test_check.NormDispIncr(osi, tol=1.0e-7, max_iter=10) # Define the dynamic analysis acc_series = o3.time_series.Path(osi, dt=asig.dt, values=asig.values) o3.pattern.UniformExcitation(osi, dir=o3.cc.X, accel_series=acc_series) while o3.get_time(osi) < analysis_time: print(o3.get_time(osi)) if o3.analyze(osi, 1, analysis_dt): print('failed') break o3.wipe(osi) out_dict = {} for otype in ods: if isinstance(ods[otype], list): out_dict[otype] = [] for i in range(len(ods[otype])): out_dict[otype].append(ods[otype][i].collect()) out_dict[otype] = np.array(out_dict[otype]) else: out_dict[otype] = ods[otype].collect().T out_dict['time'] = np.arange(0, analysis_time, rec_dt) return out_dict
def get_inelastic_response(fb, roof_drift_ratio=0.05, elastic=False, w_sfsi=False, out_folder=''): """ Run seismic analysis of a nonlinear FrameBuilding Units: Pa, N, m, s Parameters ---------- fb: sfsimodels.Frame2DBuilding object xi Returns ------- """ osi = o3.OpenSeesInstance(ndm=2, state=3) q_floor = 7.0e3 # Pa trib_width = fb.floor_length trib_mass_per_length = q_floor * trib_width / 9.8 # Establish nodes and set mass based on trib area # Nodes named as: C<column-number>-S<storey-number>, first column starts at C1-S0 = ground level left nd = OrderedDict() col_xs = np.cumsum(fb.bay_lengths) col_xs = np.insert(col_xs, 0, 0) n_cols = len(col_xs) sto_ys = fb.heights sto_ys = np.insert(sto_ys, 0, 0) for cc in range(1, n_cols + 1): for ss in range(fb.n_storeys + 1): nd[f"C{cc}-S{ss}"] = o3.node.Node(osi, col_xs[cc - 1], sto_ys[ss]) if ss != 0: if cc == 1: node_mass = trib_mass_per_length * fb.bay_lengths[0] / 2 elif cc == n_cols: node_mass = trib_mass_per_length * fb.bay_lengths[-1] / 2 else: node_mass = trib_mass_per_length * ( fb.bay_lengths[cc - 2] + fb.bay_lengths[cc - 1] / 2) o3.set_node_mass(osi, nd[f"C{cc}-S{ss}"], node_mass, 0., 0.) # Set all nodes on a storey to have the same displacement for ss in range(0, fb.n_storeys + 1): for cc in range(2, n_cols + 1): o3.set_equal_dof(osi, nd[f"C1-S{ss}"], nd[f"C{cc}-S{ss}"], o3.cc.X) # Fix all base nodes for cc in range(1, n_cols + 1): o3.Fix3DOF(osi, nd[f"C{cc}-S0"], o3.cc.FIXED, o3.cc.FIXED, o3.cc.FIXED) # Define material e_conc = 30.0e9 # kPa i_beams = 0.4 * fb.beam_widths * fb.beam_depths**3 / 12 i_columns = 0.5 * fb.column_widths * fb.column_depths**3 / 12 a_beams = fb.beam_widths * fb.beam_depths a_columns = fb.column_widths * fb.column_depths ei_beams = e_conc * i_beams ei_columns = e_conc * i_columns eps_yield = 300.0e6 / 200e9 phi_y_col = calc_yield_curvature(fb.column_depths, eps_yield) phi_y_beam = calc_yield_curvature(fb.beam_depths, eps_yield) # Define beams and columns # Columns named as: C<column-number>-S<storey-number>, first column starts at C1-S0 = ground floor left # Beams named as: B<bay-number>-S<storey-number>, first beam starts at B1-S1 = first storey left (foundation at S0) md = OrderedDict() # material dict sd = OrderedDict() # section dict ed = OrderedDict() # element dict for ss in range(fb.n_storeys): # set columns lp_i = 0.4 lp_j = 0.4 # plastic hinge length col_transf = o3.geom_transf.Linear2D( osi, ) # d_i=[0.0, lp_i], d_j=[0.0, -lp_j] for cc in range(1, fb.n_cols + 1): ele_str = f"C{cc}-S{ss}S{ss + 1}" if elastic: top_sect = o3.section.Elastic2D(osi, e_conc, a_columns[ss][cc - 1], i_columns[ss][cc - 1]) bot_sect = o3.section.Elastic2D(osi, e_conc, a_columns[ss][cc - 1], i_columns[ss][cc - 1]) else: m_cap = ei_columns[ss][cc - 1] * phi_y_col[ss][cc - 1] mat = o3.uniaxial_material.ElasticBilin( osi, ei_columns[ss][cc - 1], 0.05 * ei_columns[ss][cc - 1], 1 * phi_y_col[ss][cc - 1]) mat_axial = o3.uniaxial_material.Elastic( osi, e_conc * a_columns[ss][cc - 1]) top_sect = o3.section.Aggregator(osi, mats=[[mat_axial, o3.cc.P], [mat, o3.cc.M_Z]]) bot_sect = o3.section.Aggregator(osi, mats=[[mat_axial, o3.cc.P], [mat, o3.cc.M_Z]]) centre_sect = o3.section.Elastic2D(osi, e_conc, a_columns[ss][cc - 1], i_columns[ss][cc - 1]) sd[ele_str + "T"] = top_sect sd[ele_str + "B"] = bot_sect sd[ele_str + "C"] = centre_sect integ = o3.beam_integration.HingeMidpoint(osi, bot_sect, lp_i, top_sect, lp_j, centre_sect) bot_node = nd[f"C{cc}-S{ss}"] top_node = nd[f"C{cc}-S{ss + 1}"] ed[ele_str] = o3.element.ForceBeamColumn(osi, [bot_node, top_node], col_transf, integ) print('mc: ', ei_columns[ss][cc - 1] * phi_y_col[ss][cc - 1]) # Set beams lp_i = 0.4 lp_j = 0.4 beam_transf = o3.geom_transf.Linear2D(osi, ) for bb in range(1, fb.n_bays + 1): ele_str = f"C{bb}C{bb + 1}-S{ss + 1}" print('mb: ', ei_beams[ss][bb - 1] * phi_y_beam[ss][bb - 1]) print('phi_b: ', phi_y_beam[ss][bb - 1]) if elastic: left_sect = o3.section.Elastic2D(osi, e_conc, a_beams[ss][bb - 1], i_beams[ss][bb - 1]) right_sect = o3.section.Elastic2D(osi, e_conc, a_beams[ss][bb - 1], i_beams[ss][bb - 1]) else: m_cap = ei_beams[ss][bb - 1] * phi_y_beam[ss][bb - 1] # mat_flex = o3.uniaxial_material.ElasticBilin(osi, ei_beams[ss][bb - 1], 0.05 * ei_beams[ss][bb - 1], phi_y_beam[ss][bb - 1]) mat_flex = o3.uniaxial_material.Steel01(osi, m_cap, e0=ei_beams[ss][bb - 1], b=0.05) mat_axial = o3.uniaxial_material.Elastic( osi, e_conc * a_beams[ss][bb - 1]) left_sect = o3.section.Aggregator(osi, mats=[[mat_axial, o3.cc.P], [mat_flex, o3.cc.M_Z], [mat_flex, o3.cc.M_Y]]) right_sect = o3.section.Aggregator(osi, mats=[[mat_axial, o3.cc.P], [mat_flex, o3.cc.M_Z], [mat_flex, o3.cc.M_Y]]) centre_sect = o3.section.Elastic2D(osi, e_conc, a_beams[ss][bb - 1], i_beams[ss][bb - 1]) integ = o3.beam_integration.HingeMidpoint(osi, left_sect, lp_i, right_sect, lp_j, centre_sect) left_node = nd[f"C{bb}-S{ss + 1}"] right_node = nd[f"C{bb + 1}-S{ss + 1}"] ed[ele_str] = o3.element.ForceBeamColumn(osi, [left_node, right_node], beam_transf, integ) # Apply gravity loads gravity = 9.8 * 1e-2 # If true then load applied along beam g_beams = 0 # TODO: when this is true and analysis is inelastic then failure ts_po = o3.time_series.Linear(osi, factor=1) o3.pattern.Plain(osi, ts_po) for ss in range(1, fb.n_storeys + 1): print('ss:', ss) if g_beams: for bb in range(1, fb.n_bays + 1): ele_str = f"C{bb}C{bb + 1}-S{ss}" o3.EleLoad2DUniform(osi, ed[ele_str], -trib_mass_per_length * gravity) else: for cc in range(1, fb.n_cols + 1): if cc == 1 or cc == n_cols: node_mass = trib_mass_per_length * fb.bay_lengths[0] / 2 elif cc == n_cols: node_mass = trib_mass_per_length * fb.bay_lengths[-1] / 2 else: node_mass = trib_mass_per_length * ( fb.bay_lengths[cc - 2] + fb.bay_lengths[cc - 1] / 2) # This works o3.Load(osi, nd[f"C{cc}-S{ss}"], [0, -node_mass * gravity, 0]) tol = 1.0e-3 o3.constraints.Plain(osi) o3.numberer.RCM(osi) o3.system.BandGeneral(osi) o3.test_check.NormDispIncr(osi, tol, 10) o3.algorithm.Newton(osi) n_steps_gravity = 10 d_gravity = 1. / n_steps_gravity o3.integrator.LoadControl(osi, d_gravity, num_iter=10) o3.analysis.Static(osi) o3.analyze(osi, n_steps_gravity) o3.gen_reactions(osi) print('b1_int: ', o3.get_ele_response(osi, ed['C1C2-S1'], 'force')) print('c1_int: ', o3.get_ele_response(osi, ed['C1-S0S1'], 'force')) # o3.extensions.to_py_file(osi, 'po.py') o3.load_constant(osi, time=0.0) # Define the analysis # set damping based on first eigen mode angular_freq = o3.get_eigen(osi, solver='fullGenLapack', n=1)[0]**0.5 if isinstance(angular_freq, complex): raise ValueError( "Angular frequency is complex, issue with stiffness or mass") print('angular_freq: ', angular_freq) response_period = 2 * np.pi / angular_freq print('response period: ', response_period) # Run the analysis o3r = o3.results.Results2D() o3r.cache_path = out_folder o3r.dynamic = True o3r.pseudo_dt = 0.001 # since analysis is actually static o3.set_time(osi, 0.0) o3r.start_recorders(osi) # o3res.coords = o3.get_all_node_coords(osi) # o3res.ele2node_tags = o3.get_all_ele_node_tags_as_dict(osi) d_inc = 0.0001 o3.numberer.RCM(osi) o3.system.BandGeneral(osi) # o3.test_check.NormUnbalance(osi, 2, max_iter=10, p_flag=2) # o3.test_check.FixedNumIter(osi, max_iter=10) o3.test_check.NormDispIncr(osi, 0.002, 10, p_flag=0) o3.algorithm.Newton(osi) o3.integrator.DisplacementControl(osi, nd[f"C1-S{fb.n_storeys}"], o3.cc.X, d_inc) o3.analysis.Static(osi) d_max = 0.05 * fb.max_height # TODO: set to 5% print('d_max: ', d_max) # n_steps = int(d_max / d_inc) print("Analysis starting") print('int_disp: ', o3.get_node_disp(osi, nd[f"C1-S{fb.n_storeys}"], o3.cc.X)) # opy.recorder('Element', '-file', 'ele_out.txt', '-time', '-ele', 1, 'force') tt = 0 outputs = { 'h_disp': [], 'vb': [], 'REACT-C1C2-S1': [], 'REACT-C1-S0S1': [], } hd = 0 n_max = 2 n_cycs = 0 xs = fb.heights / fb.max_height # TODO: more sophisticated displacement profile ts_po = o3.time_series.Linear(osi, factor=1) o3.pattern.Plain(osi, ts_po) for i, xp in enumerate(xs): o3.Load(osi, nd[f"C1-S{i + 1}"], [xp, 0.0, 0]) # o3.analyze(osi, 2) # n_max = 0 time = [0] while n_cycs < n_max: print('n_cycles: ', n_cycs) for i in range(2): if i == 0: o3.integrator.DisplacementControl(osi, nd[f"C1-S{fb.n_storeys}"], o3.cc.X, d_inc) else: o3.integrator.DisplacementControl(osi, nd[f"C1-S{fb.n_storeys}"], o3.cc.X, -d_inc) while hd * (-1)**i < d_max: ok = o3.analyze(osi, 10) time.append(time[len(time) - 1] + 1) hd = o3.get_node_disp(osi, nd[f"C1-S{fb.n_storeys}"], o3.cc.X) outputs['h_disp'].append(hd) o3.gen_reactions(osi) vb = 0 for cc in range(1, fb.n_cols + 1): vb += o3.get_node_reaction(osi, nd[f"C{cc}-S0"], o3.cc.X) outputs['vb'].append(-vb) outputs['REACT-C1C2-S1'].append( o3.get_ele_response(osi, ed['C1C2-S1'], 'force')) outputs['REACT-C1-S0S1'].append( o3.get_ele_response(osi, ed['C1-S0S1'], 'force')) n_cycs += 1 o3.wipe(osi) o3r.save_to_cache() for item in outputs: outputs[item] = np.array(outputs[item]) print('complete') return outputs
def site_response(sp, asig, freqs=(0.5, 10), xi=0.03, analysis_dt=0.001, dy=0.5, analysis_time=None, outs=None, rec_dt=None, forder=1.0e3): """ Run seismic analysis of a soil profile Parameters ---------- sp: sfsimodels.SoilProfile object A soil profile asig: eqsig.AccSignal object An acceleration signal Returns ------- """ if analysis_time is None: analysis_time = asig.time[-1] if outs is None: outs = { 'ACCX': [0] } # Export the horizontal acceleration at the surface if rec_dt is None: rec_dt = analysis_dt osi = o3.OpenSeesInstance(ndm=2, ndf=2, state=3) assert isinstance(sp, sm.SoilProfile) sp.gen_split(props=['shear_vel', 'unit_mass'], target=dy) thicknesses = sp.split["thickness"] n_node_rows = len(thicknesses) + 1 node_depths = np.cumsum(sp.split["thickness"]) node_depths = np.insert(node_depths, 0, 0) ele_depths = (node_depths[1:] + node_depths[:-1]) / 2 grav = 9.81 omega_1 = 2 * np.pi * freqs[0] omega_2 = 2 * np.pi * freqs[1] a0 = 2 * xi * omega_1 * omega_2 / (omega_1 + omega_2) a1 = 2 * xi / (omega_1 + omega_2) k0 = 0.5 pois = k0 / (1 + k0) newmark_gamma = 0.5 newmark_beta = 0.25 ele_width = min(thicknesses) # Define nodes and set boundary conditions for simple shear deformation # Start at top and build down? sn = [[o3.node.Node(osi, 0, 0), o3.node.Node(osi, ele_width, 0)]] for i in range(1, n_node_rows): # Establish left and right nodes sn.append([ o3.node.Node(osi, 0, -node_depths[i]), o3.node.Node(osi, ele_width, -node_depths[i]) ]) if i != n_node_rows - 1: # set x and y dofs equal for left and right nodes o3.EqualDOF(osi, sn[i][0], sn[i][1], [o3.cc.X, o3.cc.Y]) # Fix base nodes o3.Fix2DOF(osi, sn[-1][0], o3.cc.FREE, o3.cc.FIXED) o3.Fix2DOF(osi, sn[-1][1], o3.cc.FREE, o3.cc.FIXED) o3.EqualDOF(osi, sn[-1][0], sn[-1][1], [o3.cc.X]) # Define dashpot nodes dashpot_node_1 = o3.node.Node(osi, 0, -node_depths[-1]) dashpot_node_2 = sn[-1][0] o3.Fix2DOF(osi, dashpot_node_1, o3.cc.FIXED, o3.cc.FIXED) # define materials ele_thick = 1.0 # m soil_mats = [] prev_args = [] prev_kwargs = {} prev_sl_type = None eles = [] for i in range(len(thicknesses)): y_depth = ele_depths[i] sl_id = sp.get_layer_index_by_depth(y_depth) sl = sp.layer(sl_id) app2mod = {} if y_depth > sp.gwl: umass = sl.unit_sat_mass / forder else: umass = sl.unit_dry_mass / forder p_atm = 101e3 / forder # Define material if sl.o3_type == 'pm4sand': sl_class = o3.nd_material.PM4Sand overrides = {'nu': pois, 'p_atm': p_atm, 'unit_moist_mass': umass} app2mod = sl.app2mod elif sl.o3_type == 'sdmodel': sl_class = o3.nd_material.StressDensity overrides = {'nu': pois, 'p_atm': p_atm, 'unit_moist_mass': umass} app2mod = sl.app2mod elif sl.o3_type == 'pimy': sl_class = o3.nd_material.PressureIndependMultiYield overrides = { 'nu': pois, 'p_atm': p_atm, 'rho': umass, 'nd': 2.0, 'g_mod_ref': sl.g_mod / forder, 'bulk_mod_ref': sl.bulk_mod / forder, 'peak_strain': 0.05, 'cohesion': sl.cohesion / forder, 'phi': sl.phi, 'p_ref': 101e3 / forder, 'd': 0.0, 'n_surf': 25 } else: sl_class = o3.nd_material.ElasticIsotropic sl.e_mod = 2 * sl.g_mod * (1 + sl.poissons_ratio) / forder app2mod['rho'] = 'unit_moist_mass' overrides = {'nu': sl.poissons_ratio, 'unit_moist_mass': umass} args, kwargs = o3.extensions.get_o3_kwargs_from_obj( sl, sl_class, custom=app2mod, overrides=overrides) changed = 0 if sl.type != prev_sl_type or len(args) != len(prev_args) or len( kwargs) != len(prev_kwargs): changed = 1 else: for j, arg in enumerate(args): if not np.isclose(arg, prev_args[j]): changed = 1 for pm in kwargs: if pm not in prev_kwargs or not np.isclose( kwargs[pm], prev_kwargs[pm]): changed = 1 if changed: mat = sl_class(osi, *args, **kwargs) prev_sl_type = sl.type prev_args = copy.deepcopy(args) prev_kwargs = copy.deepcopy(kwargs) soil_mats.append(mat) # def element nodes = [sn[i + 1][0], sn[i + 1][1], sn[i][1], sn[i][0]] # anti-clockwise eles.append( o3.element.SSPquad(osi, nodes, mat, o3.cc.PLANE_STRAIN, ele_thick, 0.0, -grav)) # Static analysis o3.constraints.Transformation(osi) o3.test_check.NormDispIncr(osi, tol=1.0e-5, max_iter=30, p_flag=0) o3.algorithm.Newton(osi) o3.numberer.RCM(osi) o3.system.ProfileSPD(osi) o3.integrator.Newmark(osi, 5. / 6, 4. / 9) # include numerical damping o3.analysis.Transient(osi) o3.analyze(osi, 40, 1.) for i, soil_mat in enumerate(soil_mats): if hasattr(soil_mat, 'update_to_nonlinear'): print('Update model to nonlinear') soil_mat.update_to_nonlinear() o3.analyze(osi, 50, 0.5) o3.extensions.to_py_file(osi, 'ofile_sra_pimy_og.py') # reset time and analysis o3.set_time(osi, 0.0) o3.wipe_analysis(osi) # define material and element for viscous dampers base_sl = sp.layer(sp.n_layers) c_base = ele_width * base_sl.unit_dry_mass / forder * sp.get_shear_vel_at_depth( sp.height) dashpot_mat = o3.uniaxial_material.Viscous(osi, c_base, alpha=1.) o3.element.ZeroLength(osi, [dashpot_node_1, dashpot_node_2], mats=[dashpot_mat], dirs=[o3.cc.DOF2D_X]) ods = {} for otype in outs: if otype == 'ACCX': ods['ACCX'] = [] if isinstance(outs['ACCX'], str) and outs['ACCX'] == 'all': ods['ACCX'] = o3.recorder.NodesToArrayCache(osi, nodes=sn[:][0], dofs=[o3.cc.X], res_type='accel', dt=rec_dt) else: for i in range(len(outs['ACCX'])): ind = np.argmin(abs(node_depths - outs['ACCX'][i])) ods['ACCX'].append( o3.recorder.NodeToArrayCache(osi, node=sn[ind][0], dofs=[o3.cc.X], res_type='accel', dt=rec_dt)) if otype == 'TAU': ods['TAU'] = [] if isinstance(outs['TAU'], str) and outs['TAU'] == 'all': ods['TAU'] = o3.recorder.ElementsToArrayCache( osi, eles=eles, arg_vals=['stress'], dt=rec_dt) else: for i in range(len(outs['TAU'])): ind = np.argmin(abs(ele_depths - outs['TAU'][i])) ods['TAU'].append( o3.recorder.ElementToArrayCache(osi, ele=eles[ind], arg_vals=['stress'], dt=rec_dt)) if otype == 'STRS': ods['STRS'] = [] if isinstance(outs['STRS'], str) and outs['STRS'] == 'all': ods['STRS'] = o3.recorder.ElementsToArrayCache( osi, eles=eles, arg_vals=['strain'], dt=rec_dt) else: for i in range(len(outs['STRS'])): ind = np.argmin(abs(ele_depths - outs['STRS'][i])) ods['STRS'].append( o3.recorder.ElementToArrayCache(osi, ele=eles[ind], arg_vals=['strain'], dt=rec_dt)) ods['time'] = o3.recorder.TimeToArrayCache(osi, dt=rec_dt) # Run the dynamic analysis o3.algorithm.Newton(osi) o3.system.SparseGeneral(osi) o3.numberer.RCM(osi) o3.constraints.Transformation(osi) o3.integrator.Newmark(osi, newmark_gamma, newmark_beta) o3.rayleigh.Rayleigh(osi, a0, a1, 0, 0) o3.analysis.Transient(osi) o3.test_check.EnergyIncr(osi, tol=1.0e-7, max_iter=10) # Define the dynamic analysis ts_obj = o3.time_series.Path(osi, dt=asig.dt, values=asig.velocity * 1, factor=c_base) o3.pattern.Plain(osi, ts_obj) o3.Load(osi, sn[-1][0], [1., 0.]) o3.analyze(osi, int(analysis_time / analysis_dt), analysis_dt) o3.wipe(osi) out_dict = {} for otype in ods: if isinstance(ods[otype], list): out_dict[otype] = [] for i in range(len(ods[otype])): out_dict[otype].append(ods[otype][i].collect()) out_dict[otype] = np.array(out_dict[otype]) else: out_dict[otype] = ods[otype].collect().T return out_dict
def get_response(bd, asig, l_ph): """ Compute the response of a nonlinear lollipop on a foundation with linear/nonlinear soil Units are N, m, s :param bd: SDOF building object :param asig: Acceleration signal object :return: """ osi = o3.OpenSeesInstance(ndm=2, state=3) # Establish nodes top_ss_node = o3.node.Node(osi, 0, bd.h_eff) bot_ss_node = o3.node.Node(osi, 0, 0) # Fix bottom node o3.Fix3DOF(osi, bot_ss_node, o3.cc.FIXED, o3.cc.FIXED, o3.cc.FIXED) # nodal mass (weight / g): o3.Mass(osi, top_ss_node, bd.mass_eff, 0.0, 0) # Define a column element with a plastic hinge at base transf = o3.geom_transf.Linear2D(osi, []) # can change for P-delta effects area = 1.0 e_mod = 200.0e9 iz = bd.k_eff * bd.h_eff**3 / (3 * e_mod) ele_nodes = [bot_ss_node, top_ss_node] # Superstructure element elastic_sect = o3.section.Elastic2D(osi, e_mod, area, iz) integ = o3.beam_integration.HingeMidpoint(osi, elastic_sect, l_ph, elastic_sect, l_ph, elastic_sect) vert_ele = o3.element.ForceBeamColumn(osi, ele_nodes, transf, integ) omega = 2 * np.pi / bd.t_fixed beta_k = 2 * bd.xi / omega o3.rayleigh.Rayleigh(osi, 0, 0, beta_k_init=beta_k, beta_k_comm=0.0) # Define the input motion for the dynamic analysis acc_series = o3.time_series.Path(osi, dt=asig.dt, values=-asig.values) # should be negative o3.pattern.UniformExcitation(osi, dir=o3.cc.X, accel_series=acc_series) print('loaded gm') o3.wipe_analysis(osi) o3.algorithm.Newton(osi) o3.system.SparseGeneral(osi) o3.numberer.RCM(osi) o3.constraints.Transformation(osi) o3.integrator.Newmark(osi, 0.5, 0.25) o3.analysis.Transient(osi) o3.test_check.NormDispIncr(osi, tol=1.0e-6, max_iter=10) analysis_time = asig.time[-1] analysis_dt = 0.001 # define outputs of analysis od = { "time": o3.recorder.TimeToArrayCache(osi), "rel_deck_disp": o3.recorder.NodeToArrayCache(osi, top_ss_node, [o3.cc.DOF2D_X], 'disp'), "deck_accel": o3.recorder.NodeToArrayCache(osi, top_ss_node, [o3.cc.DOF2D_X], 'accel'), "deck_rot": o3.recorder.NodeToArrayCache(osi, top_ss_node, [o3.cc.DOF2D_ROTZ], 'disp'), "chord_rots": o3.recorder.ElementToArrayCache(osi, vert_ele, arg_vals=['chordRotation']), "col_forces": o3.recorder.ElementToArrayCache(osi, vert_ele, arg_vals=['force']), } o3.analyze(osi, int(analysis_time / analysis_dt), analysis_dt) o3.wipe(osi) for item in od: od[item] = od[item].collect() od['col_shear'] = -od['col_forces'][:, 0] od['col_moment'] = od['col_forces'][:, 2] od['hinge_rotation'] = od['chord_rots'][:, 1] od['hinge_rotation1'] = -od['chord_rots'][:, 2] del od['col_forces'] del od['chord_rots'] return od
def gen_response(period, xi, asig, etype, fos_for_dt=None): # Define inelastic SDOF mass = 1.0 f_yield = 1.5 # Reduce this to make it nonlinear r_post = 0.0 # Initialise OpenSees instance osi = o3.OpenSeesInstance(ndm=2, state=0) # Establish nodes bot_node = o3.node.Node(osi, 0, 0) top_node = o3.node.Node(osi, 0, 0) # Fix bottom node o3.Fix3DOF(osi, top_node, o3.cc.FREE, o3.cc.FIXED, o3.cc.FIXED) o3.Fix3DOF(osi, bot_node, o3.cc.FIXED, o3.cc.FIXED, o3.cc.FIXED) # Set out-of-plane DOFs to be slaved o3.EqualDOF(osi, top_node, bot_node, [o3.cc.Y, o3.cc.ROTZ]) # nodal mass (weight / g): o3.Mass(osi, top_node, mass, 0., 0.) # Define material k_spring = 4 * np.pi**2 * mass / period**2 # bilinear_mat = o3.uniaxial_material.Steel01(osi, fy=f_yield, e0=k_spring, b=r_post) mat = o3.uniaxial_material.Elastic(osi, e_mod=k_spring) # Assign zero length element, # Note: pass actual node and material objects into element o3.element.ZeroLength(osi, [bot_node, top_node], mats=[mat], dirs=[o3.cc.DOF2D_X], r_flag=1) # Define the dynamic analysis # Define the dynamic analysis acc_series = o3.time_series.Path(osi, dt=asig.dt, values=-1 * asig.values) # should be negative o3.pattern.UniformExcitation(osi, dir=o3.cc.X, accel_series=acc_series) # set damping based on first eigen mode angular_freq = o3.get_eigen(osi, solver='fullGenLapack', n=1)[0]**0.5 period = 2 * np.pi / angular_freq beta_k = 2 * xi / angular_freq o3.rayleigh.Rayleigh(osi, alpha_m=0.0, beta_k=beta_k, beta_k_init=0.0, beta_k_comm=0.0) o3.set_time(osi, 0.0) # Run the dynamic analysis o3.wipe_analysis(osi) # Run the dynamic analysis o3.numberer.RCM(osi) o3.system.FullGeneral(osi) if etype == 'central_difference': o3.algorithm.Linear(osi, factor_once=True) o3.integrator.CentralDifference(osi) explicit_dt = 2 / angular_freq / fos_for_dt analysis_dt = explicit_dt elif etype == 'implicit': o3.algorithm.Newton(osi) o3.integrator.Newmark(osi, gamma=0.5, beta=0.25) analysis_dt = 0.001 else: raise ValueError() o3.constraints.Transformation(osi) o3.analysis.Transient(osi) o3.test_check.EnergyIncr(osi, tol=1.0e-10, max_iter=10) analysis_time = asig.time[-1] outputs = { "time": [], "rel_disp": [], "rel_accel": [], "rel_vel": [], "force": [] } rec_dt = 0.002 n_incs = int(analysis_dt / rec_dt) n_incs = 1 while o3.get_time(osi) < analysis_time: o3.analyze(osi, n_incs, analysis_dt) curr_time = o3.get_time(osi) outputs["time"].append(curr_time) outputs["rel_disp"].append(o3.get_node_disp(osi, top_node, o3.cc.X)) outputs["rel_vel"].append(o3.get_node_vel(osi, top_node, o3.cc.X)) outputs["rel_accel"].append(o3.get_node_accel(osi, top_node, o3.cc.X)) o3.gen_reactions(osi) outputs["force"].append(-o3.get_node_reaction( osi, bot_node, o3.cc.X)) # Negative since diff node o3.wipe(osi) for item in outputs: outputs[item] = np.array(outputs[item]) return outputs
def site_response(sp, asig, freqs=(0.5, 10), xi=0.03, dy=0.5, analysis_time=None, outs=None, rec_dt=None, etype='variable', forder=1.0): """ Run seismic analysis of a soil profile Parameters ---------- sp: sfsimodels.SoilProfile object A soil profile asig: eqsig.AccSignal object An acceleration signal Returns ------- """ if analysis_time is None: analysis_time = asig.time[-1] if outs is None: outs = { 'ACCX': [0] } # Export the horizontal acceleration at the surface osi = o3.OpenSeesInstance(ndm=2, ndf=2, state=3) assert isinstance(sp, sm.SoilProfile) sp.gen_split(props=['shear_vel', 'unit_mass'], target=dy) req_dt = min(sp.split["thickness"] / sp.split['shear_vel']) / 8 thicknesses = sp.split["thickness"] n_node_rows = len(thicknesses) + 1 node_depths = np.cumsum(sp.split["thickness"]) node_depths = np.insert(node_depths, 0, 0) ele_depths = (node_depths[1:] + node_depths[:-1]) / 2 unit_masses = sp.split["unit_mass"] / forder grav = 9.81 ele_width = min(thicknesses) total_soil_nodes = len(thicknesses) * 2 + 2 # Define nodes and set boundary conditions for simple shear deformation # Start at top and build down? sn = [[o3.node.Node(osi, 0, 0), o3.node.Node(osi, ele_width, 0)]] for i in range(1, n_node_rows): # Establish left and right nodes sn.append([ o3.node.Node(osi, 0, -node_depths[i]), o3.node.Node(osi, ele_width, -node_depths[i]) ]) # set x and y dofs equal for left and right nodes o3.EqualDOF(osi, sn[i][0], sn[i][1], [o3.cc.X, o3.cc.Y]) # Fix base nodes o3.Fix2DOF(osi, sn[-1][0], o3.cc.FIXED, o3.cc.FIXED) o3.Fix2DOF(osi, sn[-1][1], o3.cc.FIXED, o3.cc.FIXED) # define materials ele_thick = 1.0 # m soil_mats = [] eles = [] prev_id = -1 for i in range(len(thicknesses)): y_depth = ele_depths[i] sl_id = sp.get_layer_index_by_depth(y_depth) sl = sp.layer(sl_id) mat = sl.o3_mat if sl_id != prev_id: mat.build(osi) soil_mats.append(mat) prev_id = sl_id # def element nodes = [sn[i + 1][0], sn[i + 1][1], sn[i][1], sn[i][0]] # anti-clockwise eles.append( o3.element.SSPquad(osi, nodes, mat, o3.cc.PLANE_STRAIN, ele_thick, 0.0, -grav)) for i, soil_mat in enumerate(soil_mats): if hasattr(soil_mat, 'update_to_linear'): print('Update model to linear') soil_mat.update_to_linear() # Gravity analysis o3.constraints.Transformation(osi) o3.test_check.NormDispIncr(osi, tol=1.0e-5, max_iter=30, p_flag=0) o3.algorithm.Newton(osi) o3.numberer.RCM(osi) o3.system.ProfileSPD(osi) o3.integrator.Newmark(osi, 5. / 6, 4. / 9) # include numerical damping if etype == 'variable': o3.analysis.VariableTransient(osi) g_args = [0.5, 0.1, 2, 5] else: o3.analysis.Transient(osi) g_args = [0.5] fail = o3.analyze(osi, 40, *g_args) if fail: return for i, soil_mat in enumerate(soil_mats): if hasattr(soil_mat, 'update_to_nonlinear'): print('Update model to nonlinear') soil_mat.update_to_nonlinear() if o3.analyze(osi, 50, *g_args): print('Model failed') return print('finished nonlinear gravity analysis') # reset time and analysis o3.set_time(osi, 0.0) o3.wipe_analysis(osi) n = 10 # omegas = np.array(o3.get_eigen(osi, solver='fullGenLapack', n=n)) ** 0.5 # DO NOT USE fullGenLapack omegas = np.array(o3.get_eigen(osi, n=n))**0.5 periods = 2 * np.pi / omegas print('response_periods: ', periods) o3.constraints.Transformation(osi) o3.test_check.NormDispIncr(osi, tol=1.0e-8, max_iter=6, p_flag=0) o3.numberer.RCM(osi) o3.system.ProfileSPD(osi) # o3.algorithm.NewtonLineSearch(osi, 0.75) # o3.integrator.Newmark(osi, 0.5, 0.25) if etype == 'variable': # o3.analysis.VariableTransient(osi) o3.opy.analysis('VariableTransient') dyn_args = [20, 0.01, 0.0005, 0.01, 3] else: o3.analysis.Transient(osi) dyn_args = [20, 0.01] omega_1 = 2 * np.pi * freqs[0] omega_2 = 2 * np.pi * freqs[1] a0 = 2 * xi * omega_1 * omega_2 / (omega_1 + omega_2) a1 = 2 * xi / (omega_1 + omega_2) o3.rayleigh.Rayleigh(osi, a0, 0, a1, 0) o3.extensions.to_py_file(osi, f'opy_var_integration_{etype}.py') # o3.test_check.NormDispIncr(osi, tol=1.0e-7, max_iter=10) rec_dt = 0.001 ods = {} for otype in outs: if otype == 'ACCX': ods['ACCX'] = [] if isinstance(outs['ACCX'], str) and outs['ACCX'] == 'all': ods['ACCX'] = o3.recorder.NodesToArrayCache(osi, nodes=sn[:][0], dofs=[o3.cc.X], res_type='accel', dt=rec_dt) else: for i in range(len(outs['ACCX'])): ind = np.argmin(abs(node_depths - outs['ACCX'][i])) ods['ACCX'].append( o3.recorder.NodeToArrayCache(osi, node=sn[ind][0], dofs=[o3.cc.X], res_type='accel', dt=rec_dt)) if otype == 'TAU': ods['TAU'] = [] if isinstance(outs['TAU'], str) and outs['TAU'] == 'all': ods['TAU'] = o3.recorder.ElementsToArrayCache( osi, eles=eles, arg_vals=['stress'], dt=rec_dt) else: for i in range(len(outs['TAU'])): ind = np.argmin(abs(ele_depths - outs['TAU'][i])) ods['TAU'].append( o3.recorder.ElementToArrayCache(osi, ele=eles[ind], arg_vals=['stress'], dt=rec_dt)) if otype == 'STRS': ods['STRS'] = [] if isinstance(outs['STRS'], str) and outs['STRS'] == 'all': ods['STRS'] = o3.recorder.ElementsToArrayCache( osi, eles=eles, arg_vals=['strain'], dt=rec_dt) else: for i in range(len(outs['STRS'])): ind = np.argmin(abs(ele_depths - outs['STRS'][i])) ods['STRS'].append( o3.recorder.ElementToArrayCache(osi, ele=eles[ind], arg_vals=['strain'], dt=rec_dt)) ods['time'] = o3.recorder.TimeToArrayCache(osi, dt=rec_dt) acc_series = o3.time_series.Path(osi, dt=asig.dt, values=asig.values) o3.pattern.UniformExcitation(osi, dir=o3.cc.X, accel_series=acc_series) # Run the dynamic analysis inc = 1 o3.record(osi) while o3.get_time(osi) < analysis_time: print(o3.get_time(osi)) if o3.analyze(osi, *dyn_args): print('failed') break o3.wipe(osi) out_dict = {} for otype in ods: if isinstance(ods[otype], list): out_dict[otype] = [] for i in range(len(ods[otype])): out_dict[otype].append(ods[otype][i].collect()) out_dict[otype] = np.array(out_dict[otype]) else: out_dict[otype] = ods[otype].collect().T return out_dict
def site_response(sp, asig, linear=0): """ Run seismic analysis of a soil profile - example based on: http://opensees.berkeley.edu/wiki/index.php/Site_Response_Analysis_of_a_Layered_Soil_Column_(Total_Stress_Analysis) :param sp: sfsimodels.SoilProfile object A soil profile :param asig: eqsig.AccSignal object An acceleration signal :return: """ osi = o3.OpenSeesInstance(ndm=2, ndf=2, state=3) assert isinstance(sp, sm.SoilProfile) sp.gen_split(props=['shear_vel', 'unit_mass', 'cohesion', 'phi', 'bulk_mod', 'poissons_ratio', 'strain_peak']) thicknesses = sp.split["thickness"] n_node_rows = len(thicknesses) + 1 node_depths = np.cumsum(sp.split["thickness"]) node_depths = np.insert(node_depths, 0, 0) ele_depths = (node_depths[1:] + node_depths[:-1]) / 2 shear_vels = sp.split["shear_vel"] unit_masses = sp.split["unit_mass"] / 1e3 g_mods = unit_masses * shear_vels ** 2 poissons_ratio = sp.split['poissons_ratio'] youngs_mods = 2 * g_mods * (1 - poissons_ratio) bulk_mods = youngs_mods / (3 * (1 - 2 * poissons_ratio)) bulk_mods = sp.split['bulk_mod'] / 1e3 ref_pressure = 80.0 cohesions = sp.split['cohesion'] / 1e3 phis = sp.split['phi'] strain_peaks = sp.split['strain_peak'] grav = 9.81 damping = 0.03 omega_1 = 2 * np.pi * 0.5 omega_2 = 2 * np.pi * 10 a0 = 2 * damping * omega_1 * omega_2 / (omega_1 + omega_2) a1 = 2 * damping / (omega_1 + omega_2) newmark_gamma = 0.5 newmark_beta = 0.25 ele_width = min(thicknesses) total_soil_nodes = len(thicknesses) * 2 + 2 # Define nodes and set boundary conditions for simple shear deformation # Start at top and build down? nd = OrderedDict() nd["R0L"] = o3.node.Node(osi, 0, 0) # row 0 left nd["R0R"] = o3.node.Node(osi, ele_width, 0) for i in range(1, n_node_rows): # Establish left and right nodes nd[f"R{i}L"] = o3.node.Node(osi, 0, -node_depths[i]) nd[f"R{i}R"] = o3.node.Node(osi, ele_width, -node_depths[i]) # set x and y dofs equal for left and right nodes o3.EqualDOF(osi, nd[f"R{i}L"], nd[f"R{i}R"], [o3.cc.X, o3.cc.Y]) # Fix base nodes o3.Fix2DOF(osi, nd[f"R{n_node_rows - 1}L"], o3.cc.FREE, o3.cc.FIXED) o3.Fix2DOF(osi, nd[f"R{n_node_rows - 1}R"], o3.cc.FREE, o3.cc.FIXED) # Define dashpot nodes dashpot_node_l = o3.node.Node(osi, 0, -node_depths[-1]) dashpot_node_2 = o3.node.Node(osi, 0, -node_depths[-1]) o3.Fix2DOF(osi, dashpot_node_l, o3.cc.FIXED, o3.cc.FIXED) o3.Fix2DOF(osi, dashpot_node_2, o3.cc.FREE, o3.cc.FIXED) # define equal DOF for dashpot and soil base nodes o3.EqualDOF(osi, nd[f"R{n_node_rows - 1}L"], nd[f"R{n_node_rows - 1}R"], [o3.cc.X]) o3.EqualDOF(osi, nd[f"R{n_node_rows - 1}L"], dashpot_node_2, [o3.cc.X]) # define materials ele_thick = 1.0 # m soil_mats = [] strains = np.logspace(-6, -0.5, 16) ref_strain = 0.005 rats = 1. / (1 + (strains / ref_strain) ** 0.91) eles = [] for i in range(len(thicknesses)): if not linear: mat = o3.nd_material.PressureIndependMultiYield(osi, 2, unit_masses[i], g_mods[i], bulk_mods[i], cohesions[i], strain_peaks[i], phis[i], d=0.0, n_surf=16, strains=strains, ratios=rats) else: mat = o3.nd_material.ElasticIsotropic(osi, youngs_mods[i], poissons_ratio[i], rho=unit_masses[i]) soil_mats.append(mat) # def element nodes = [nd[f"R{i + 1}L"], nd[f"R{i + 1}R"], nd[f"R{i}R"], nd[f"R{i}L"]] ele = o3.element.Quad(osi, nodes, ele_thick, o3.cc.PLANE_STRAIN, mat, b2=grav * unit_masses[i]) eles.append(ele) # define material and element for viscous dampers c_base = ele_width * unit_masses[-1] * shear_vels[-1] dashpot_mat = o3.uniaxial_material.Viscous(osi, c_base, alpha=1.) o3.element.ZeroLength(osi, [dashpot_node_l, dashpot_node_2], mats=[dashpot_mat], dirs=[o3.cc.DOF2D_X]) # Static analysis o3.constraints.Transformation(osi) o3.test_check.NormDispIncr(osi, tol=1.0e-4, max_iter=30, p_flag=0) o3.algorithm.Newton(osi) o3.numberer.RCM(osi) o3.system.ProfileSPD(osi) o3.integrator.Newmark(osi, newmark_gamma, newmark_beta) o3.analysis.Transient(osi) o3.analyze(osi, 40, 1.) # for i in range(len(soil_mats)): # o3.update_material_stage(osi, soil_mats[i], 1) o3.analyze(osi, 50, 0.5) # reset time and analysis o3.set_time(osi, 0.0) o3.wipe_analysis(osi) na = o3.recorder.NodeToArrayCache(osi, node=nd["R0L"], dofs=[o3.cc.X], res_type='accel') es = o3.recorder.ElementsToArrayCache(osi, eles=eles, arg_vals=['stress']) # Define the dynamic analysis ts_obj = o3.time_series.Path(osi, dt=asig.dt, values=asig.velocity * -1, factor=c_base) o3.pattern.Plain(osi, ts_obj) o3.Load(osi, nd["R{0}L".format(n_node_rows - 1)], [1., 0.]) # Run the dynamic analysis o3.algorithm.Newton(osi) o3.system.SparseGeneral(osi) o3.numberer.RCM(osi) o3.constraints.Transformation(osi) o3.integrator.Newmark(osi, newmark_gamma, newmark_beta) o3.rayleigh.Rayleigh(osi, a0, a1, 0, 0) o3.analysis.Transient(osi) o3.test_check.EnergyIncr(osi, tol=1.0e-10, max_iter=10) analysis_time = asig.time[-1] analysis_dt = 0.01 # o3.extensions.to_py_file(osi) while o3.get_time(osi) < analysis_time: o3.analyze(osi, 1, analysis_dt) o3.wipe(osi) outputs = { "time": np.arange(0, analysis_time, analysis_dt), "rel_disp": [], "rel_accel": na.collect(), 'ele_stresses': es.collect() } return outputs
def get_elastic_response(mass, k_spring, motion, dt, xi=0.05, r_post=0.0): """ Run seismic analysis of a nonlinear SDOF :param mass: SDOF mass :param k_spring: spring stiffness :param motion: array_like, acceleration values :param dt: float, time step of acceleration values :param xi: damping ratio :param r_post: post-yield stiffness :return: """ osi = o3.OpenSeesInstance(ndm=2, state=3) height = 5. # Establish nodes bot_node = o3.node.Node(osi, 0, 0) top_node = o3.node.Node(osi, 0, height) # Fix bottom node o3.Fix3DOF(osi, top_node, o3.cc.FREE, o3.cc.FIXED, o3.cc.FREE) o3.Fix3DOF(osi, bot_node, o3.cc.FIXED, o3.cc.FIXED, o3.cc.FIXED) # Set out-of-plane DOFs to be slaved o3.EqualDOF(osi, top_node, bot_node, [o3.cc.Y]) # nodal mass (weight / g): o3.Mass(osi, top_node, mass, 0., 0.) # Define material transf = o3.geom_transf.Linear2D(osi, []) area = 1.0 e_mod = 1.0e6 iz = k_spring * height ** 3 / (3 * e_mod) ele_nodes = [bot_node, top_node] ele = o3.element.ElasticBeamColumn2D(osi, ele_nodes, area=area, e_mod=e_mod, iz=iz, transf=transf) # Define the dynamic analysis acc_series = o3.time_series.Path(osi, dt=dt, values=-motion) # should be negative o3.pattern.UniformExcitation(osi, dir=o3.cc.X, accel_series=acc_series) # set damping based on first eigen mode angular_freq = o3.get_eigen(osi, solver='fullGenLapack', n=1)[0] ** 0.5 response_period = 2 * np.pi / angular_freq print('response_period: ', response_period) beta_k = 2 * xi / angular_freq o3.rayleigh.Rayleigh(osi, alpha_m=0.0, beta_k=beta_k, beta_k_init=0.0, beta_k_comm=0.0) # Run the dynamic analysis o3.wipe_analysis(osi) o3.algorithm.Newton(osi) o3.system.SparseGeneral(osi) o3.numberer.RCM(osi) o3.constraints.Transformation(osi) o3.integrator.Newmark(osi, 0.5, 0.25) o3.analysis.Transient(osi) o3.extensions.to_py_file(osi, 'simple.py') o3.test_check.EnergyIncr(osi, tol=1.0e-10, max_iter=10) analysis_time = (len(motion) - 1) * dt analysis_dt = 0.001 outputs = { "time": [], "rel_disp": [], "rel_accel": [], "rel_vel": [], "force": [] } while o3.get_time(osi) < analysis_time: o3.analyze(osi, 1, analysis_dt) curr_time = o3.get_time(osi) outputs["time"].append(curr_time) outputs["rel_disp"].append(o3.get_node_disp(osi, top_node, o3.cc.X)) outputs["rel_vel"].append(o3.get_node_vel(osi, top_node, o3.cc.X)) outputs["rel_accel"].append(o3.get_node_accel(osi, top_node, o3.cc.X)) o3.gen_reactions(osi) outputs["force"].append(o3.get_ele_response(osi, ele, 'force')) o3.wipe(osi) for item in outputs: outputs[item] = np.array(outputs[item]) return outputs
def run_analysis(etype, asig, xi, sdt, use_modal_damping=0): osi = o3.OpenSeesInstance(ndm=2, ndf=3) nodes = [ o3.node.Node(osi, 0.0, 0.0), o3.node.Node(osi, 5.5, 0.0), o3.node.Node(osi, 0.0, 3.3), o3.node.Node(osi, 5.5, 3.3) ] o3.Mass2D(osi, nodes[2], 1e5, 1e5, 1e6) o3.Mass2D(osi, nodes[3], 1e5, 1e5, 1e6) o3.Fix3DOF(osi, nodes[0], o3.cc.FIXED, o3.cc.FIXED, o3.cc.FIXED) o3.Fix3DOF(osi, nodes[1], o3.cc.FIXED, o3.cc.FIXED, o3.cc.FIXED) steel_mat = o3.uniaxial_material.Steel01(osi, 300.0e6, 200.0e9, b=0.02) tran = o3.geom_transf.Linear2D(osi) o3.element.ElasticBeamColumn2D(osi, [nodes[2], nodes[3]], 0.01, 200.0e9, iz=1.0e-4, transf=tran) o3.element.ElasticBeamColumn2D(osi, [nodes[0], nodes[2]], 0.01, 200.0e9, iz=1.0e-4, transf=tran) o3.element.ElasticBeamColumn2D(osi, [nodes[1], nodes[3]], 0.01, 200.0e9, iz=1.0e-4, transf=tran) a_series = o3.time_series.Path(osi, dt=asig.dt, values=-1 * asig.values) # should be negative o3.pattern.UniformExcitation(osi, dir=o3.cc.X, accel_series=a_series) angular_freqs = np.array(o3.get_eigen(osi, n=5))**0.5 print('angular_freqs: ', angular_freqs) periods = 2 * np.pi / angular_freqs print('periods: ', periods) if use_modal_damping: o3.ModalDamping(osi, [xi]) else: freqs = [0.5, 5] omega_1 = 2 * np.pi * freqs[0] omega_2 = 2 * np.pi * freqs[1] a0 = 2 * xi * omega_1 * omega_2 / (omega_1 + omega_2) a1 = 2 * xi / (omega_1 + omega_2) o3.rayleigh.Rayleigh(osi, a0, 0, a1, 0) o3.constraints.Transformation(osi) o3.test_check.NormDispIncr(osi, tol=1.0e-5, max_iter=35, p_flag=0) o3.numberer.RCM(osi) if etype == 'implicit': o3.algorithm.Newton(osi) o3.system.FullGeneral(osi) o3.integrator.Newmark(osi, gamma=0.5, beta=0.25) dt = 0.01 else: o3.algorithm.Linear(osi) if etype == 'newmark_explicit': o3.system.FullGeneral(osi) o3.integrator.NewmarkExplicit(osi, gamma=0.5) explicit_dt = periods[-1] / np.pi / 8 elif etype == 'central_difference': if use_modal_damping: o3.system.FullGeneral(osi) explicit_dt = periods[ -1] / np.pi / 1.5 * sdt # 1.5 is a factor of safety for damping else: o3.system.ProfileSPD(osi) explicit_dt = periods[-1] / np.pi / 1.1 * sdt o3.integrator.CentralDifference(osi) elif etype == 'explicit_difference': o3.system.Diagonal(osi) o3.integrator.ExplicitDifference(osi) explicit_dt = periods[-1] / np.pi / 1.5 else: raise ValueError(etype) print('explicit_dt: ', explicit_dt) dt = explicit_dt o3.analysis.Transient(osi) roof_disp = o3.recorder.NodeToArrayCache(osi, nodes[2], dofs=[o3.cc.X], res_type='disp') time = o3.recorder.TimeToArrayCache(osi) ttotal = 15.0 while o3.get_time(osi) < ttotal: if o3.analyze(osi, 10, dt): print('failed') break ddisp = o3.get_node_disp(osi, nodes[2], dof=o3.cc.X) if abs(ddisp) > 0.1: print('Break') break o3.wipe(osi) return time.collect(), roof_disp.collect()
def test_init_o3(): osi = o3.OpenSeesInstance(ndm=2) o3.wipe(osi)
def get_inelastic_response(fb, asig, extra_time=0.0, xi=0.05, analysis_dt=0.001): """ Run seismic analysis of a nonlinear FrameBuilding Parameters ---------- fb: sfsimodels.Frame2DBuilding object asig: eqsig.AccSignal object extra_time xi analysis_dt Returns ------- """ osi = o3.OpenSeesInstance(ndm=2) q_floor = 10000. # kPa trib_width = fb.floor_length trib_mass_per_length = q_floor * trib_width / 9.8 # Establish nodes and set mass based on trib area # Nodes named as: C<column-number>-S<storey-number>, first column starts at C1-S0 = ground level left nd = OrderedDict() col_xs = np.cumsum(fb.bay_lengths) col_xs = np.insert(col_xs, 0, 0) n_cols = len(col_xs) sto_ys = fb.heights sto_ys = np.insert(sto_ys, 0, 0) for cc in range(1, n_cols + 1): for ss in range(fb.n_storeys + 1): nd[f"C{cc}-S{ss}"] = o3.node.Node(osi, col_xs[cc - 1], sto_ys[ss]) if ss != 0: if cc == 1: node_mass = trib_mass_per_length * fb.bay_lengths[0] / 2 elif cc == n_cols: node_mass = trib_mass_per_length * fb.bay_lengths[-1] / 2 else: node_mass = trib_mass_per_length * (fb.bay_lengths[cc - 2] + fb.bay_lengths[cc - 1] / 2) o3.set_node_mass(osi, nd[f"C{cc}-S{ss}"], node_mass, 0., 0.) # Set all nodes on a storey to have the same displacement for ss in range(0, fb.n_storeys + 1): for cc in range(1, n_cols + 1): o3.set_equal_dof(osi, nd[f"C1-S{ss}"], nd[f"C{cc}-S{ss}"], o3.cc.X) # Fix all base nodes for cc in range(1, n_cols + 1): o3.Fix3DOF(osi, nd[f"C{cc}-S0"], o3.cc.FIXED, o3.cc.FIXED, o3.cc.FIXED) # Coordinate transformation transf = o3.geom_transf.Linear2D(osi, []) l_hinge = fb.bay_lengths[0] * 0.1 # Define material e_conc = 30.0e6 i_beams = 0.4 * fb.beam_widths * fb.beam_depths ** 3 / 12 i_columns = 0.5 * fb.column_widths * fb.column_depths ** 3 / 12 a_beams = fb.beam_widths * fb.beam_depths a_columns = fb.column_widths * fb.column_depths ei_beams = e_conc * i_beams ei_columns = e_conc * i_columns eps_yield = 300.0e6 / 200e9 phi_y_col = calc_yield_curvature(fb.column_depths, eps_yield) phi_y_beam = calc_yield_curvature(fb.beam_depths, eps_yield) * 10 # TODO: re-evaluate # Define beams and columns # Columns named as: C<column-number>-S<storey-number>, first column starts at C1-S0 = ground floor left # Beams named as: B<bay-number>-S<storey-number>, first beam starts at B1-S1 = first storey left (foundation at S0) md = OrderedDict() # material dict sd = OrderedDict() # section dict ed = OrderedDict() # element dict for ss in range(fb.n_storeys): # set columns for cc in range(1, fb.n_cols + 1): lp_i = 0.4 lp_j = 0.4 # plastic hinge length ele_str = f"C{cc}-S{ss}S{ss+1}" top_sect = o3.section.Elastic2D(osi, e_conc, a_columns[ss][cc - 1], i_columns[ss][cc - 1]) bot_sect = o3.section.Elastic2D(osi, e_conc, a_columns[ss][cc - 1], i_columns[ss][cc - 1]) centre_sect = o3.section.Elastic2D(osi, e_conc, a_columns[ss][cc - 1], i_columns[ss][cc - 1]) sd[ele_str + "T"] = top_sect sd[ele_str + "B"] = bot_sect sd[ele_str + "C"] = centre_sect integ = o3.beam_integration.HingeMidpoint(osi, bot_sect, lp_i, top_sect, lp_j, centre_sect) bot_node = nd[f"C{cc}-S{ss}"] top_node = nd[f"C{cc}-S{ss+1}"] ed[ele_str] = o3.element.ForceBeamColumn(osi, [bot_node, top_node], transf, integ) # Set beams for bb in range(1, fb.n_bays + 1): lp_i = 0.5 lp_j = 0.5 ele_str = f"C{bb-1}C{bb}-S{ss}" mat = o3.uniaxial_material.ElasticBilin(osi, ei_beams[ss][bb - 1], 0.05 * ei_beams[ss][bb - 1], phi_y_beam[ss][bb - 1]) md[ele_str] = mat left_sect = o3.section.Uniaxial(osi, mat, quantity=o3.cc.M_Z) right_sect = o3.section.Uniaxial(osi, mat, quantity=o3.cc.M_Z) centre_sect = o3.section.Elastic2D(osi, e_conc, a_beams[ss][bb - 1], i_beams[ss][bb - 1]) integ = o3.beam_integration.HingeMidpoint(osi, left_sect, lp_i, right_sect, lp_j, centre_sect) left_node = nd[f"C{bb}-S{ss+1}"] right_node = nd[f"C{bb+1}-S{ss+1}"] ed[ele_str] = o3.element.ForceBeamColumn(osi, [left_node, right_node], transf, integ) # Define the dynamic analysis a_series = o3.time_series.Path(osi, dt=asig.dt, values=-1 * asig.values) # should be negative o3.pattern.UniformExcitation(osi, dir=o3.cc.X, accel_series=a_series) # set damping based on first eigen mode angular_freq_sqrd = o3.get_eigen(osi, solver='fullGenLapack', n=1) if hasattr(angular_freq_sqrd, '__len__'): angular_freq = angular_freq_sqrd[0] ** 0.5 else: angular_freq = angular_freq_sqrd ** 0.5 if isinstance(angular_freq, complex): raise ValueError("Angular frequency is complex, issue with stiffness or mass") beta_k = 2 * xi / angular_freq o3.rayleigh.Rayleigh(osi, alpha_m=0.0, beta_k=beta_k, beta_k_init=0.0, beta_k_comm=0.0) # Run the dynamic analysis o3.wipe_analysis(osi) o3.algorithm.Newton(osi) o3.system.SparseGeneral(osi) o3.numberer.RCM(osi) o3.constraints.Transformation(osi) o3.integrator.Newmark(osi, 0.5, 0.25) o3.analysis.Transient(osi) tol = 1.0e-4 iter = 4 o3.test_check.EnergyIncr(osi, tol, iter, 0, 2) analysis_time = (len(asig.values) - 1) * asig.dt + extra_time outputs = { "time": [], "rel_disp": [], "rel_accel": [], "rel_vel": [], "force": [], "ele_mom": [], "ele_curve": [], } print("Analysis starting") while o3.get_time(osi) < analysis_time: curr_time = opy.getTime() o3.analyze(osi, 1, analysis_dt) outputs["time"].append(curr_time) outputs["rel_disp"].append(o3.get_node_disp(osi, nd["C%i-S%i" % (1, fb.n_storeys)], o3.cc.X)) outputs["rel_vel"].append(o3.get_node_vel(osi, nd["C%i-S%i" % (1, fb.n_storeys)], o3.cc.X)) outputs["rel_accel"].append(o3.get_node_accel(osi, nd["C%i-S%i" % (1, fb.n_storeys)], o3.cc.X)) # outputs['ele_mom'].append(opy.eleResponse('-ele', [ed['B%i-S%i' % (1, 0)], 'basicForce'])) o3.gen_reactions(osi) react = 0 for cc in range(1, fb.n_cols): react += -o3.get_node_reaction(osi, nd["C%i-S%i" % (cc, 0)], o3.cc.X) outputs["force"].append(react) # Should be negative since diff node o3.wipe(osi) for item in outputs: outputs[item] = np.array(outputs[item]) return outputs
def site_response(sp, asig, freqs=(0.5, 10), xi=0.03, dy=0.5, analysis_time=None, outs=None, rec_dt=None, etype='implicit', forder=1.0): """ Run seismic analysis of a soil profile Parameters ---------- sp: sfsimodels.SoilProfile object A soil profile asig: eqsig.AccSignal object An acceleration signal Returns ------- """ if analysis_time is None: analysis_time = asig.time[-1] if outs is None: outs = {'ACCX': [0]} # Export the horizontal acceleration at the surface osi = o3.OpenSeesInstance(ndm=2, ndf=2, state=3) assert isinstance(sp, sm.SoilProfile) sp.gen_split(props=['shear_vel', 'unit_mass', 'g_mod', 'poissons_ratio'], target=dy) thicknesses = sp.split["thickness"] n_node_rows = len(thicknesses) + 1 node_depths = np.cumsum(sp.split["thickness"]) node_depths = np.insert(node_depths, 0, 0) ele_depths = (node_depths[1:] + node_depths[:-1]) / 2 rho = sp.split['unit_mass'] g_mod = sp.split['g_mod'] poi = sp.split['poissons_ratio'] lam = 2 * g_mod * poi / (1 - 2 * poi) mu = g_mod v_dil = np.sqrt((lam + 2 * mu) / rho) ele_h = sp.split['thickness'] dts = ele_h / v_dil min_dt = min(dts) print('min_dt: ', min_dt) grav = 9.81 ele_width = min(thicknesses) # Define nodes and set boundary conditions for simple shear deformation # Start at top and build down? sn = [[o3.node.Node(osi, 0, 0), o3.node.Node(osi, ele_width, 0)]] for i in range(1, n_node_rows): # Establish left and right nodes sn.append([o3.node.Node(osi, 0, -node_depths[i]), o3.node.Node(osi, ele_width, -node_depths[i])]) # set x and y dofs equal for left and right nodes o3.EqualDOF(osi, sn[i][0], sn[i][1], [o3.cc.X, o3.cc.Y]) # Fix base nodes o3.Fix2DOF(osi, sn[-1][0], o3.cc.FIXED, o3.cc.FIXED) o3.Fix2DOF(osi, sn[-1][1], o3.cc.FIXED, o3.cc.FIXED) # define materials ele_thick = 1.0 # m soil_mats = [] eles = [] prev_id = -1 for i in range(len(thicknesses)): y_depth = ele_depths[i] sl_id = sp.get_layer_index_by_depth(y_depth) sl = sp.layer(sl_id) mat = sl.o3_mat if sl_id != prev_id: mat.build(osi) soil_mats.append(mat) prev_id = sl_id # def element nodes = [sn[i+1][0], sn[i+1][1], sn[i][1], sn[i][0]] # anti-clockwise eles.append(o3.element.SSPquad(osi, nodes, mat, o3.cc.PLANE_STRAIN, ele_thick, 0.0, -grav)) for i, soil_mat in enumerate(soil_mats): if hasattr(soil_mat, 'update_to_linear'): print('Update model to linear') soil_mat.update_to_linear() # Gravity analysis o3.constraints.Transformation(osi) o3.test_check.NormDispIncr(osi, tol=1.0e-5, max_iter=30, p_flag=0) o3.algorithm.Newton(osi) o3.numberer.RCM(osi) o3.system.ProfileSPD(osi) o3.integrator.Newmark(osi, 5./6, 4./9) # include numerical damping o3.analysis.Transient(osi) o3.analyze(osi, 40, 1.) for i, soil_mat in enumerate(soil_mats): if hasattr(soil_mat, 'update_to_nonlinear'): print('Update model to nonlinear') soil_mat.update_to_nonlinear() if o3.analyze(osi, 50, 0.5): print('Model failed') return print('finished nonlinear gravity analysis') # reset time and analysis o3.set_time(osi, 0.0) o3.wipe_analysis(osi) n = 10 # omegas = np.array(o3.get_eigen(osi, solver='fullGenLapack', n=n)) ** 0.5 # DO NOT USE fullGenLapack omegas = np.array(o3.get_eigen(osi, n=n)) ** 0.5 periods = 2 * np.pi / omegas print('response_periods: ', periods) o3.constraints.Transformation(osi) o3.test_check.NormDispIncr(osi, tol=1.0e-6, max_iter=10) o3.numberer.RCM(osi) if etype == 'implicit': o3.system.ProfileSPD(osi) o3.algorithm.NewtonLineSearch(osi, 0.75) o3.integrator.Newmark(osi, 0.5, 0.25) dt = 0.001 else: o3.algorithm.Linear(osi) if etype == 'newmark_explicit': o3.system.ProfileSPD(osi) o3.integrator.NewmarkExplicit(osi, gamma=0.5) explicit_dt = min_dt / 1 elif etype == 'central_difference': o3.system.ProfileSPD(osi) o3.integrator.CentralDifference(osi) explicit_dt = min_dt / 8 elif etype == 'explicit_difference': o3.system.Diagonal(osi) o3.integrator.ExplicitDifference(osi) explicit_dt = min_dt / 2 # need reduced time step due to Rayleigh damping else: raise ValueError(etype) ndp = np.ceil(np.log10(explicit_dt)) if 0.5 * 10 ** ndp < explicit_dt: dt = 0.5 * 10 ** ndp elif 0.2 * 10 ** ndp < explicit_dt: dt = 0.2 * 10 ** ndp elif 0.1 * 10 ** ndp < explicit_dt: dt = 0.1 * 10 ** ndp else: raise ValueError(explicit_dt, 0.1 * 10 ** ndp) print('explicit_dt: ', explicit_dt, dt) use_modal_damping = 0 if not use_modal_damping: omega_1 = 2 * np.pi * freqs[0] omega_2 = 2 * np.pi * freqs[1] a0 = 2 * xi * omega_1 * omega_2 / (omega_1 + omega_2) a1 = 2 * xi / (omega_1 + omega_2) o3.rayleigh.Rayleigh(osi, a0, 0, a1, 0) else: o3.ModalDamping(osi, [xi]) o3.analysis.Transient(osi) o3.test_check.NormDispIncr(osi, tol=1.0e-7, max_iter=10) rec_dt = 0.001 ods = {} for otype in outs: if otype == 'ACCX': ods['ACCX'] = [] if isinstance(outs['ACCX'], str) and outs['ACCX'] == 'all': ods['ACCX'] = o3.recorder.NodesToArrayCache(osi, nodes=sn[:][0], dofs=[o3.cc.X], res_type='accel', dt=rec_dt) else: for i in range(len(outs['ACCX'])): ind = np.argmin(abs(node_depths - outs['ACCX'][i])) ods['ACCX'].append(o3.recorder.NodeToArrayCache(osi, node=sn[ind][0], dofs=[o3.cc.X], res_type='accel', dt=rec_dt)) if otype == 'TAU': ods['TAU'] = [] if isinstance(outs['TAU'], str) and outs['TAU'] == 'all': ods['TAU'] = o3.recorder.ElementsToArrayCache(osi, eles=eles, arg_vals=['stress'], dt=rec_dt) else: for i in range(len(outs['TAU'])): ind = np.argmin(abs(ele_depths - outs['TAU'][i])) ods['TAU'].append(o3.recorder.ElementToArrayCache(osi, ele=eles[ind], arg_vals=['stress'], dt=rec_dt)) if otype == 'STRS': ods['STRS'] = [] if isinstance(outs['STRS'], str) and outs['STRS'] == 'all': ods['STRS'] = o3.recorder.ElementsToArrayCache(osi, eles=eles, arg_vals=['strain'], dt=rec_dt) else: for i in range(len(outs['STRS'])): ind = np.argmin(abs(ele_depths - outs['STRS'][i])) ods['STRS'].append(o3.recorder.ElementToArrayCache(osi, ele=eles[ind], arg_vals=['strain'], dt=rec_dt)) ods['time'] = o3.recorder.TimeToArrayCache(osi, dt=rec_dt) acc_series = o3.time_series.Path(osi, dt=asig.dt, values=asig.values) o3.pattern.UniformExcitation(osi, dir=o3.cc.X, accel_series=acc_series) # Run the dynamic analysis inc = 1 if etype != 'implicit': inc = 10 o3.record(osi) while o3.get_time(osi) < analysis_time: print(o3.get_time(osi)) if o3.analyze(osi, inc, dt): print('failed') break o3.wipe(osi) out_dict = {} for otype in ods: if isinstance(ods[otype], list): out_dict[otype] = [] for i in range(len(ods[otype])): out_dict[otype].append(ods[otype][i].collect()) out_dict[otype] = np.array(out_dict[otype]) else: out_dict[otype] = ods[otype].collect().T return out_dict
def run_pm4sand_et(sl, csr, esig_v0=101.0e3, static_bias=0.0, n_lim=100, k0=0.5, strain_limit=0.03, strain_inc=5.0e-6, etype='implicit'): nu_init = k0 / (1 + k0) damp = 0.02 omega0 = 0.2 omega1 = 20.0 a1 = 2. * damp / (omega0 + omega1) a0 = a1 * omega0 * omega1 # Initialise OpenSees instance osi = o3.OpenSeesInstance(ndm=2, ndf=3, state=3) # Establish nodes h_ele = 1. bl_node = o3.node.Node(osi, 0, 0) br_node = o3.node.Node(osi, h_ele, 0) tr_node = o3.node.Node(osi, h_ele, h_ele) tl_node = o3.node.Node(osi, 0, h_ele) all_nodes = [bl_node, br_node, tr_node, tl_node] # Fix bottom node o3.Fix3DOF(osi, bl_node, o3.cc.FIXED, o3.cc.FIXED, o3.cc.FIXED) o3.Fix3DOF(osi, br_node, o3.cc.FIXED, o3.cc.FIXED, o3.cc.FIXED) o3.Fix3DOF(osi, tr_node, o3.cc.FREE, o3.cc.FREE, o3.cc.FIXED) o3.Fix3DOF(osi, tl_node, o3.cc.FREE, o3.cc.FREE, o3.cc.FIXED) # Set out-of-plane DOFs to be slaved o3.EqualDOF(osi, tr_node, tl_node, [o3.cc.X, o3.cc.Y]) # Define material pm4sand = o3.nd_material.PM4Sand(osi, sl.relative_density, sl.g0_mod, sl.h_po, sl.unit_sat_mass, 101.3, nu=nu_init) # Note water bulk modulus is irrelevant since constant volume test - so as soil skeleton contracts # the bulk modulus of the soil skeleton controls the change in effective stress water_bulk_mod = 2.2e6 ele = o3.element.SSPquadUP(osi, all_nodes, pm4sand, 1.0, water_bulk_mod, 1., sl.permeability, sl.permeability, sl.e_curr, alpha=1.0e-5, b1=0.0, b2=0.0) o3.constraints.Transformation(osi) o3.test_check.NormDispIncr(osi, tol=1.0e-6, max_iter=35, p_flag=0) o3.numberer.RCM(osi) omegas = np.array(o3.get_eigen(osi, n=1))**0.5 periods = 2 * np.pi / omegas periods = [0.001] if etype == 'implicit': o3.algorithm.Newton(osi) o3.system.FullGeneral(osi) o3.integrator.Newmark(osi, gamma=5. / 6, beta=4. / 9) dt = 0.01 else: o3.algorithm.Linear(osi, factor_once=True) o3.system.FullGeneral(osi) if etype == 'newmark_explicit': o3.integrator.NewmarkExplicit(osi, gamma=0.5) explicit_dt = periods[0] / np.pi / 8 elif etype == 'central_difference': o3.integrator.CentralDifference(osi) explicit_dt = periods[0] / np.pi / 16 # 0.5 is a factor of safety elif etype == 'hht_explicit': o3.integrator.HHTExplicit(osi, alpha=0.5) explicit_dt = periods[0] / np.pi / 8 elif etype == 'explicit_difference': o3.integrator.ExplicitDifference(osi) explicit_dt = periods[0] / np.pi / 4 else: raise ValueError(etype) print('explicit_dt: ', explicit_dt) dt = explicit_dt o3.analysis.Transient(osi) freqs = [0.5, 10] xi = 0.1 use_modal_damping = 0 if use_modal_damping: omega_1 = 2 * np.pi * freqs[0] omega_2 = 2 * np.pi * freqs[1] a0 = 2 * xi * omega_1 * omega_2 / (omega_1 + omega_2) a1 = 2 * xi / (omega_1 + omega_2) o3.rayleigh.Rayleigh(osi, a0, 0, a1, 0) else: o3.ModalDamping(osi, [xi]) o3.update_material_stage(osi, pm4sand, stage=0) # print('here1: ', o3.get_ele_response(osi, ele, 'stress'), esig_v0, csr) all_stresses_cache = o3.recorder.ElementToArrayCache(osi, ele, arg_vals=['stress']) all_strains_cache = o3.recorder.ElementToArrayCache(osi, ele, arg_vals=['strain']) nodes_cache = o3.recorder.NodesToArrayCache(osi, all_nodes, dofs=[1, 2, 3], res_type='disp') o3.recorder.NodesToFile(osi, 'node_disp.txt', all_nodes, dofs=[1, 2, 3], res_type='disp') # Add static vertical pressure and stress bias ttime = 30 time_series = o3.time_series.Path(osi, time=[0, ttime, 1e10], values=[0, 1, 1]) o3.pattern.Plain(osi, time_series) o3.Load(osi, tl_node, [0, -esig_v0 / 2, 0]) o3.Load(osi, tr_node, [0, -esig_v0 / 2, 0]) o3.analyze(osi, num_inc=int(ttime / dt) + 10, dt=dt) ts2 = o3.time_series.Path(osi, time=[ttime, 80000, 1e10], values=[1., 1., 1.], factor=1) o3.pattern.Plain(osi, ts2, fact=1.) y_vert = o3.get_node_disp(osi, tr_node, o3.cc.Y) o3.SP(osi, tl_node, dof=o3.cc.Y, dof_values=[y_vert]) o3.SP(osi, tr_node, dof=o3.cc.Y, dof_values=[y_vert]) # Close the drainage valves for node in all_nodes: o3.remove_sp(osi, node, dof=3) o3.analyze(osi, int(5 / dt), dt=dt) print('here3: ', o3.get_ele_response(osi, ele, 'stress'), esig_v0, csr) o3.update_material_stage(osi, pm4sand, stage=1) o3.set_parameter(osi, value=0, eles=[ele], args=['FirstCall', pm4sand.tag]) o3.analyze(osi, int(5 / dt), dt=dt) o3.set_parameter(osi, value=sl.poissons_ratio, eles=[ele], args=['poissonRatio', pm4sand.tag]) o3.extensions.to_py_file(osi) n_cyc = 0.0 target_strain = 1.1 * strain_limit target_disp = target_strain * h_ele limit_reached = 0 export = 1 while n_cyc < n_lim: print('n_cyc: ', n_cyc) h_disp = o3.get_node_disp(osi, tr_node, o3.cc.X) curr_time = o3.get_time(osi) steps = target_strain / strain_inc ts0 = o3.time_series.Path(osi, time=[curr_time, curr_time + steps, 1e10], values=[h_disp, target_disp, target_disp], factor=1) pat0 = o3.pattern.Plain(osi, ts0) o3.SP(osi, tr_node, dof=o3.cc.X, dof_values=[1.0]) curr_stress = o3.get_ele_response(osi, ele, 'stress')[2] if math.isnan(curr_stress): raise ValueError if export: o3.extensions.to_py_file(osi) export = 0 while curr_stress < (csr - static_bias) * esig_v0: o3.analyze(osi, int(0.1 / dt), dt=dt) curr_stress = o3.get_ele_response(osi, ele, 'stress')[2] h_disp = o3.get_node_disp(osi, tr_node, o3.cc.X) print(h_disp, target_disp) if h_disp >= target_disp: print('STRAIN LIMIT REACHED - on load') limit_reached = 1 break if limit_reached: break n_cyc += 0.25 print('load reversal, n_cyc: ', n_cyc) curr_time = o3.get_time(osi) o3.remove_load_pattern(osi, pat0) o3.remove(osi, ts0) o3.remove_sp(osi, tr_node, dof=o3.cc.X) # Reverse cycle steps = (h_disp + target_disp) / (strain_inc * h_ele) ts0 = o3.time_series.Path(osi, time=[curr_time, curr_time + steps, 1e10], values=[h_disp, -target_disp, -target_disp], factor=1) pat0 = o3.pattern.Plain(osi, ts0) o3.SP(osi, tr_node, dof=o3.cc.X, dof_values=[1.0]) i = 0 while curr_stress > -(csr + static_bias) * esig_v0: o3.analyze(osi, int(0.1 / dt), dt=dt) curr_stress = o3.get_ele_response(osi, ele, 'stress')[2] h_disp = o3.get_node_disp(osi, tr_node, o3.cc.X) if -h_disp >= target_disp: print('STRAIN LIMIT REACHED - on reverse') limit_reached = 1 break i += 1 if i > steps: break if limit_reached: break n_cyc += 0.5 print('reload, n_cyc: ', n_cyc) curr_time = o3.get_time(osi) o3.remove_load_pattern(osi, pat0) o3.remove(osi, ts0) o3.remove_sp(osi, tr_node, dof=o3.cc.X) # reload cycle steps = (-h_disp + target_disp) / (strain_inc * h_ele) ts0 = o3.time_series.Path(osi, time=[curr_time, curr_time + steps, 1e10], values=[h_disp, target_disp, target_disp], factor=1) pat0 = o3.pattern.Plain(osi, ts0) o3.SP(osi, tr_node, dof=o3.cc.X, dof_values=[1.0]) while curr_stress < static_bias * esig_v0: o3.analyze(osi, int(0.1 / dt), dt=dt) curr_stress = o3.get_ele_response(osi, ele, 'stress')[2] h_disp = o3.get_node_disp(osi, tr_node, o3.cc.X) if h_disp >= target_disp: print('STRAIN LIMIT REACHED - on reload') limit_reached = 1 break if limit_reached: break o3.remove_load_pattern(osi, pat0) o3.remove(osi, ts0) o3.remove_sp(osi, tr_node, dof=o3.cc.X) n_cyc += 0.25 o3.wipe(osi) all_stresses = all_stresses_cache.collect() all_strains = all_strains_cache.collect() disps = nodes_cache.collect() stress = all_stresses[:, 2] strain = all_strains[:, 2] ppt = all_stresses[:, 1] return stress, strain, ppt, disps pass
def run_analysis(etype, asig, use_modal_damping=0): osi = o3.OpenSeesInstance(ndm=2, ndf=3) nodes = [ o3.node.Node(osi, 0.0, 0.0), o3.node.Node(osi, 5.5, 0.0), o3.node.Node(osi, 0.0, 3.3), o3.node.Node(osi, 5.5, 3.3) ] o3.Mass2D(osi, nodes[2], 1e5, 1e5, 1e6) o3.Mass2D(osi, nodes[3], 1e5, 1e5, 1e6) o3.Fix3DOF(osi, nodes[0], o3.cc.FIXED, o3.cc.FIXED, o3.cc.FIXED) o3.Fix3DOF(osi, nodes[1], o3.cc.FIXED, o3.cc.FIXED, o3.cc.FIXED) steel_mat = o3.uniaxial_material.Steel01(osi, 300.0e6, 200.0e9, b=0.02) tran = o3.geom_transf.Linear2D(osi) o3.element.ElasticBeamColumn2D(osi, [nodes[2], nodes[3]], 0.01, 200.0e9, iz=1.0e-4, transf=tran) o3.element.ElasticBeamColumn2D(osi, [nodes[0], nodes[2]], 0.01, 200.0e9, iz=1.0e-4, transf=tran) o3.element.ElasticBeamColumn2D(osi, [nodes[1], nodes[3]], 0.01, 200.0e9, iz=1.0e-4, transf=tran) a_series = o3.time_series.Path(osi, dt=asig.dt, values=-1 * asig.values) # should be negative o3.pattern.UniformExcitation(osi, dir=o3.cc.X, accel_series=a_series) xi = 0.1 angular_freqs = np.array(o3.get_eigen( osi, n=5))**0.5 # There are vertical modes too! print('angular_freqs: ', angular_freqs) periods = 2 * np.pi / angular_freqs print('periods: ', periods) if use_modal_damping: # Does not support modal damping freqs = [0.5, 5] omega_1 = 2 * np.pi * freqs[0] omega_2 = 2 * np.pi * freqs[1] a0 = 2 * xi * omega_1 * omega_2 / (omega_1 + omega_2) a1 = 2 * xi / (omega_1 + omega_2) o3.rayleigh.Rayleigh(osi, a0, 0, a1, 0) else: o3.ModalDamping(osi, [xi]) o3.constraints.Transformation(osi) o3.test_check.NormDispIncr(osi, tol=1.0e-5, max_iter=35, p_flag=0) o3.numberer.RCM(osi) if use_modal_damping: o3_sys = o3.system.ProfileSPD # not sure why don't need to use FullGen here? since matrix is full? else: o3_sys = o3.system.ProfileSPD if etype == 'implicit': o3.algorithm.Newton(osi) o3_sys(osi) o3.integrator.Newmark(osi, gamma=0.5, beta=0.25) dt = 0.001 else: o3.algorithm.Linear(osi, factor_once=True) if etype == 'newmark_explicit': o3_sys(osi) o3.integrator.NewmarkExplicit(osi, gamma=0.5) explicit_dt = periods[-1] / np.pi / 2.01 elif etype == 'central_difference': o3_sys(osi) o3.integrator.CentralDifference(osi) if use_modal_damping: explicit_dt = periods[ -1] / np.pi / 1.5 # 1.1 is a factor of safety else: explicit_dt = periods[-1] / np.pi / 1.5 elif etype == 'explicit_difference': o3.system.Diagonal(osi) o3.integrator.ExplicitDifference(osi) explicit_dt = periods[-1] / np.pi / 1.6 else: raise ValueError(etype) print('explicit_dt: ', explicit_dt) dt = explicit_dt o3.analysis.Transient(osi) roof_disp = o3.recorder.NodeToArrayCache(osi, nodes[2], dofs=[o3.cc.X], res_type='disp') time = o3.recorder.TimeToArrayCache(osi) ttotal = 15.0 o3.analyze(osi, int(ttotal / dt), dt) o3.wipe(osi) return time.collect(), roof_disp.collect()