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 get_response(bd, dtype): """ 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) # define superstructure damping using rotational spring approach from Millen et al. (2017) to avoid double damping if dtype == 'rot_link': k_rot = bd.k_eff * (2.0 / 3)**2 * bd.h_eff**2 ss_rot_link_mat = o3.uniaxial_material.Elastic(osi, k_rot) sfi_link_ele = o3.element.TwoNodeLink(osi, [bot_ss_node, top_ss_node], mats=[ss_rot_link_mat], dir=[o3.cc.DOF2D_ROTZ], shear_dist=[0.1], p_delta_vals=[0., 0.]) print(sfi_link_ele.parameters) elif dtype == 'horz_link': cxx = bd.xi * 2 * np.sqrt(bd.mass_eff * bd.k_eff) cxx *= 0.01 ss_rot_link_mat = o3.uniaxial_material.Viscous(osi, cxx, alpha=1.) sfi_link_ele = o3.element.TwoNodeLink(osi, [bot_ss_node, top_ss_node], mats=[ss_rot_link_mat], dirs=[o3.cc.X]) else: 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) ts2 = o3.time_series.Path(osi, time=[0, 100, 1e10], values=[0., 1., 1.], factor=1) o3.pattern.Plain(osi, ts2, fact=1.) o3.SP(osi, top_ss_node, dof=o3.cc.X, dof_values=[0.2]) 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 = 80 analysis_dt = 0.01 # 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_force": o3.recorder.NodeToArrayCache(osi, top_ss_node, [o3.cc.DOF2D_X], 'reaction'), "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=[ 'basicForce' ]), # note that 'force' provides global force (includes link) } if dtype in ['rot_link', 'horz_link']: od['link_force'] = o3.recorder.ElementToArrayCache(osi, sfi_link_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'][:, 2] - od['col_forces'][:, 1]) / bd.h_eff od['col_moment'] = od['col_forces'][:, 1] od['col_moment_end'] = od['col_forces'][:, 2] od['hinge_rotation'] = od['chord_rots'][:, 1] del od['col_forces'] del od['chord_rots'] return od
def run_ts_custom_strain(mat, esig_v0, strains, osi=None, nu_dyn=None, target_d_inc=0.00001, k0=None, etype='newmark_explicit', handle='silent', verbose=0, opyfile=None, dss=False, plain_strain=True, min_n=10, nl=True): # if dss: # raise ValueError('dss option is not working') damp = 0.05 omega0 = 0.2 omega1 = 20.0 a1 = 2. * damp / (omega0 + omega1) a0 = a1 * omega0 * omega1 if osi is None: osi = o3.OpenSeesInstance(ndm=2, ndf=2) mat.build(osi) # Establish nodes h_ele = 1. nodes = [ o3.node.Node(osi, 0.0, 0.0), o3.node.Node(osi, h_ele, 0.0), o3.node.Node(osi, h_ele, h_ele), o3.node.Node(osi, 0.0, h_ele) ] # Fix bottom node o3.Fix2DOF(osi, nodes[0], o3.cc.FIXED, o3.cc.FIXED) if k0 is None: o3.Fix2DOF(osi, nodes[1], o3.cc.FIXED, o3.cc.FIXED) # Set out-of-plane DOFs to be slaved o3.EqualDOF(osi, nodes[2], nodes[3], [o3.cc.X, o3.cc.Y]) else: # control k0 with node forces o3.Fix2DOF(osi, nodes[1], o3.cc.FIXED, o3.cc.FREE) if plain_strain: oop = 'PlaneStrain' else: oop = 'PlaneStress' ele = o3.element.SSPquad(osi, nodes, mat, oop, 1, 0.0, 0.0) angular_freqs = np.array(o3.get_eigen(osi, solver='fullGenLapack', n=2)) ** 0.5 print('angular_freqs: ', angular_freqs) periods = 2 * np.pi / angular_freqs xi = 0.03 o3.ModalDamping(osi, [xi, xi]) print('periods: ', periods) 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.FullGeneral(osi) o3.integrator.Newmark(osi, gamma=0.5, beta=0.25) 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) o3.update_material_stage(osi, mat, stage=0) # dt = 0.00001 tload = 60 n_steps = tload / dt # Add static vertical pressure and stress bias time_series = o3.time_series.Path(osi, time=[0, tload, 1e10], values=[0, 1, 1]) o3.pattern.Plain(osi, time_series) # ts0 = o3.time_series.Linear(osi, factor=1) # o3.pattern.Plain(osi, ts0) if k0: o3.Load(osi, nodes[2], [-esig_v0 / 2, -esig_v0 / 2]) o3.Load(osi, nodes[3], [esig_v0 / 2, -esig_v0 / 2]) o3.Load(osi, nodes[1], [-esig_v0 / 2, 0]) # node 0 is fixed else: o3.Load(osi, nodes[2], [0, -esig_v0 / 2]) o3.Load(osi, nodes[3], [0, -esig_v0 / 2]) print('Apply init stress to elastic element') o3.analyze(osi, num_inc=n_steps, dt=dt) stresses = o3.get_ele_response(osi, ele, 'stress') print('init_stress0: ', stresses) o3.load_constant(osi, tload) if hasattr(mat, 'update_to_nonlinear') and nl: print('set to nonlinear') mat.update_to_nonlinear() o3.analyze(osi, 10000, dt=dt) # if not nl: # mat.update_to_linear() if nu_dyn is not None: mat.set_nu(nu_dyn, eles=[ele]) o3.analyze(osi, 10000, dt=dt) # o3.extensions.to_py_file(osi) stresses = o3.get_ele_response(osi, ele, 'stress') print('init_stress1: ', stresses) # Prepare for reading results exit_code = None stresses = o3.get_ele_response(osi, ele, 'stress') if dss: o3.gen_reactions(osi) force0 = o3.get_node_reaction(osi, nodes[2], o3.cc.DOF2D_X) force1 = o3.get_node_reaction(osi, nodes[3], o3.cc.DOF2D_X) # force2 = o3.get_node_reaction(osi, nodes[0], o3.cc.DOF2D_X) stress = [force1 + force0] strain = [o3.get_node_disp(osi, nodes[2], dof=o3.cc.DOF2D_X)] sxy_ind = None gxy_ind = None # iforce0 = o3.get_node_reaction(osi, nodes[0], o3.cc.DOF2D_X) # iforce1 = o3.get_node_reaction(osi, nodes[1], o3.cc.DOF2D_X) # iforce2 = o3.get_node_reaction(osi, nodes[2], o3.cc.DOF2D_X) # iforce3 = o3.get_node_reaction(osi, nodes[3], o3.cc.DOF2D_X) # print(iforce0, iforce1, iforce2, iforce3, stresses[2]) else: ro = o3.recorder.load_recorder_options() import pandas as pd df = pd.read_csv(ro) mat_type = ele.mat.type dfe = df[(df['mat'] == mat_type) & (df['form'] == oop)] df_sxy = dfe[dfe['recorder'] == 'stress'] outs = df_sxy['outs'].iloc[0].split('-') sxy_ind = outs.index('sxy') df_gxy = dfe[dfe['recorder'] == 'strain'] outs = df_gxy['outs'].iloc[0].split('-') gxy_ind = outs.index('gxy') stress = [stresses[sxy_ind]] cur_strains = o3.get_ele_response(osi, ele, 'strain') strain = [cur_strains[gxy_ind]] time_series = o3.time_series.Path(osi, time=[0, tload, 1e10], values=[0, 1, 1]) o3.pattern.Plain(osi, time_series) disps = list(np.array(strains) * 1) d_per_dt = 0.01 diff_disps = np.diff(disps, prepend=0) time_incs = np.abs(diff_disps) / d_per_dt approx_n_steps = time_incs / dt time_incs = np.where(approx_n_steps < 800, 800 * dt, time_incs) approx_n_steps = time_incs / dt assert min(approx_n_steps) >= 8, approx_n_steps curr_time = o3.get_time(osi) times = list(np.cumsum(time_incs) + curr_time) disps.append(disps[-1]) times.append(1e10) disps = list(disps) n_steps_p2 = int((times[-2] - curr_time) / dt) + 10 print('n_steps: ', n_steps_p2) times.insert(0, curr_time) disps.insert(0, 0.0) init_disp = o3.get_node_disp(osi, nodes[2], dof=o3.cc.X) disps = list(np.array(disps) + init_disp) ts0 = o3.time_series.Path(osi, time=times, values=disps, factor=1) pat0 = o3.pattern.Plain(osi, ts0) o3.SP(osi, nodes[2], dof=o3.cc.X, dof_values=[1]) o3.SP(osi, nodes[3], dof=o3.cc.X, dof_values=[1]) print('init_disp: ', init_disp) print('path -times: ', times) print('path -values: ', disps) v_eff = [stresses[1]] h_eff = [stresses[0]] time = [o3.get_time(osi)] for i in range(int(n_steps_p2 / 200)): print(i / (n_steps_p2 / 200)) fail = o3.analyze(osi, 200, dt=dt) o3.gen_reactions(osi) stresses = o3.get_ele_response(osi, ele, 'stress') v_eff.append(stresses[1]) h_eff.append(stresses[0]) if dss: o3.gen_reactions(osi) force0 = o3.get_node_reaction(osi, nodes[2], o3.cc.DOF2D_X) force1 = o3.get_node_reaction(osi, nodes[3], o3.cc.DOF2D_X) stress.append(force1 + force0) strain.append(o3.get_node_disp(osi, nodes[2], dof=o3.cc.DOF2D_X)) else: stress.append(stresses[sxy_ind]) cur_strains = o3.get_ele_response(osi, ele, 'strain') strain.append(cur_strains[gxy_ind]) time.append(o3.get_time(osi)) if fail: break return np.array(stress), np.array(strain)-init_disp, np.array(v_eff), np.array(h_eff), np.array(time), exit_code
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