def load_element(nu_init, nu_setp): esig_v0 = 100.0 osi = o3.OpenSeesInstance(ndm=2, ndf=2) mat = o3.nd_material.ElasticIsotropic(osi, 1.0e6, nu=nu_init) 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) o3.Fix2DOF(osi, nodes[1], o3.cc.FIXED, o3.cc.FIXED) o3.Fix2DOF(osi, nodes[2], o3.cc.FREE, o3.cc.FREE) o3.Fix2DOF(osi, nodes[3], o3.cc.FREE, o3.cc.FREE) # Set out-of-plane DOFs to be slaved o3.EqualDOF(osi, nodes[2], nodes[3], [o3.cc.X, o3.cc.Y]) ele = o3.element.SSPquad(osi, nodes, mat, 'PlaneStrain', 1, 0.0, 0.0) o3.constraints.Transformation(osi) o3.test_check.NormDispIncr(osi, tol=1.0e-3, max_iter=35, p_flag=0) o3.algorithm.Newton(osi) o3.numberer.RCM(osi) o3.system.FullGeneral(osi) o3.integrator.DisplacementControl(osi, nodes[2], o3.cc.DOF2D_Y, 0.005) # o3.rayleigh.Rayleigh(osi, a0, a1, 0.0, 0.0) o3.analysis.Static(osi) o3.set_parameter(osi, value=nu_setp, eles=[ele], args=['nu', 1]) # Add static vertical pressure and stress bias # time_series = o3.time_series.Path(osi, time=[0, 100, 1e10], values=[0, 1, 1]) # o3.pattern.Plain(osi, time_series) ts0 = o3.time_series.Linear(osi, factor=1) o3.pattern.Plain(osi, ts0) o3.Load(osi, nodes[2], [0, -esig_v0 / 2]) o3.Load(osi, nodes[3], [0, -esig_v0 / 2]) o3.analyze(osi, num_inc=100) stresses = o3.get_ele_response(osi, ele, 'stress') print('init_stress0: ', stresses)
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 set_material_state(self, value, ele=None, eles=None): from o3seespy import set_parameter if ele is not None: set_parameter(self.osi, value=value, eles=[ele], args=['materialState', 1]) if eles is not None: set_parameter(self.osi, value=value, eles=eles, args=['materialState', 1])
def set_nu(self, nu, ele=None, eles=None): from o3seespy import set_parameter if ele is not None: set_parameter(self.osi, value=nu, eles=[ele], args=['poissonRatio', 1]) if eles is not None: set_parameter(self.osi, value=nu, eles=eles, args=['poissonRatio', 1])
def run_2d_strain_driver_iso(osi, base_mat, esig_v0, disps, target_d_inc=0.00001, max_steps=10000, handle='silent', da_strain_max=0.05, max_cycles=200, srate=0.0001, esig_v_min=1.0, k0_init=1, verbose=0, cyc_lim_fail=True): if not np.isclose(k0_init, 1., rtol=0.05): raise ValueError(f'Only supports k0=1, current k0={k0_init:.3f}') max_steps_per_half_cycle = 50000 nodes = [ o3.node.Node(osi, 0.0, 0.0), o3.node.Node(osi, 1.0, 0.0), o3.node.Node(osi, 1.0, 1.0), o3.node.Node(osi, 0.0, 1.0) ] for node in nodes: o3.Fix2DOF(osi, node, 1, 1) mat = o3.nd_material.InitStressNDMaterial(osi, other=base_mat, init_stress=-esig_v0, n_dim=2) ele = o3.element.SSPquad(osi, nodes, mat, 'PlaneStrain', 1, 0.0, 0.0) # create analysis o3.constraints.Penalty(osi, 1.0e15, 1.0e15) o3.algorithm.Linear(osi) o3.numberer.RCM(osi) o3.system.FullGeneral(osi) o3.analysis.Static(osi) d_init = 0.0 d_max = 0.1 # element height is 1m max_time = (d_max - d_init) / srate ts0 = o3.time_series.Linear(osi, factor=1) o3.pattern.Plain(osi, ts0) o3.Load(osi, nodes[2], [1.0, 0.0]) o3.Load(osi, nodes[3], [1.0, 0.0]) o3.analyze(osi, 1) o3.set_parameter(osi, value=1, eles=[ele], args=['materialState']) o3.update_material_stage(osi, base_mat, 1) o3.analyze(osi, 1) exit_code = None # loop through the total number of cycles react = 0 strain = [0] stresses = o3.get_ele_response(osi, ele, 'stress') stress = [stresses[2]] v_eff = [stresses[1]] h_eff = [stresses[0]] d_incs = np.diff(disps, prepend=0) # orys = np.where(diffs >= 0, 1, -1) for i in range(len(disps)): d_inc_i = d_incs[i] if target_d_inc < abs(d_inc_i): n = int(abs(d_inc_i / target_d_inc)) d_step = d_inc_i / n else: n = 1 d_step = d_inc_i for j in range(n): o3.integrator.DisplacementControl(osi, nodes[2], o3.cc.DOF2D_X, -d_step) o3.Load(osi, nodes[2], [1.0, 0.0]) o3.Load(osi, nodes[3], [1.0, 0.0]) o3.analyze(osi, 1) o3.gen_reactions(osi) # react = o3.get_ele_response(osi, ele, 'force')[0] stresses = o3.get_ele_response(osi, ele, 'stress') v_eff.append(stresses[1]) h_eff.append(stresses[0]) force0 = o3.get_node_reaction(osi, nodes[0], o3.cc.DOF2D_X) force1 = o3.get_node_reaction(osi, nodes[1], o3.cc.DOF2D_X) stress.append(-force0 - force1) # stress.append(stresses[2]) end_strain = -o3.get_node_disp(osi, nodes[2], dof=o3.cc.DOF2D_X) strain.append(end_strain) return -np.array(stress), np.array(strain), np.array(v_eff), np.array(h_eff), exit_code
def run_2d_stress_driver(osi, base_mat, esig_v0, forces, d_step=0.001, max_steps=10000, handle='silent', da_strain_max=0.05, max_cycles=200, srate=0.0001, esig_v_min=1.0, k0_init=1, verbose=0, cyc_lim_fail=True): if k0_init != 1: raise ValueError('Only supports k0=1') max_steps_per_half_cycle = 50000 nodes = [ o3.node.Node(osi, 0.0, 0.0), o3.node.Node(osi, 1.0, 0.0), o3.node.Node(osi, 1.0, 1.0), o3.node.Node(osi, 0.0, 1.0) ] for node in nodes: o3.Fix2DOF(osi, node, 1, 1) mat = o3.nd_material.InitStressNDMaterial(osi, other=base_mat, init_stress=-esig_v0, n_dim=2) ele = o3.element.SSPquad(osi, nodes, mat, 'PlaneStrain', 1, 0.0, 0.0) # create analysis o3.constraints.Penalty(osi, 1.0e15, 1.0e15) o3.algorithm.Linear(osi) o3.numberer.RCM(osi) o3.system.FullGeneral(osi) o3.analysis.Static(osi) d_init = 0.0 d_max = 0.1 # element height is 1m max_time = (d_max - d_init) / srate ts0 = o3.time_series.Linear(osi, factor=1) o3.pattern.Plain(osi, ts0) o3.Load(osi, nodes[2], [1.0, 0.0]) o3.Load(osi, nodes[3], [1.0, 0.0]) o3.analyze(osi, 1) o3.set_parameter(osi, value=1, eles=[ele], args=['materialState']) o3.update_material_stage(osi, base_mat, 1) o3.analyze(osi, 1) exit_code = None print('hhh') # loop through the total number of cycles react = 0 strain = [0] stresses = o3.get_ele_response(osi, ele, 'stress') stress = [stresses[2]] v_eff = [stresses[1]] h_eff = [stresses[0]] diffs = np.diff(forces, prepend=0) orys = np.where(diffs >= 0, 1, -1) for i in range(len(forces)): print('i: ', i, d_step) ory = orys[i] o3.integrator.DisplacementControl(osi, nodes[2], o3.cc.DOF2D_X, -d_step * ory) o3.Load(osi, nodes[2], [ory * 1.0, 0.0]) o3.Load(osi, nodes[3], [ory * 1.0, 0.0]) for j in range(max_steps): if react * ory < forces[i] * ory: o3.analyze(osi, 1) else: print('reached!') break o3.gen_reactions(osi) # react = o3.get_ele_response(osi, ele, 'force')[0] stresses = o3.get_ele_response(osi, ele, 'stress') # print(stresses) tau = stresses[2] print(tau, forces[i], ory) react = -tau v_eff.append(stresses[1]) h_eff.append(stresses[0]) stress.append(tau) end_strain = -o3.get_node_disp(osi, nodes[2], dof=o3.cc.DOF2D_X) strain.append(end_strain) if j == max_steps - 1: if handle == 'silent': break if handle == 'warn': print(f'Target force not reached: force={react:.4g}, target: {forces[i]:.4g}') else: raise ValueError() return np.array(stress), np.array(strain), np.array(v_eff), np.array(h_eff), exit_code
def set_parameter(self, osi, pstr, value, ele, eles): from o3seespy import set_parameter if ele is not None: set_parameter(osi, value=value, eles=[ele], args=[pstr, 1]) if eles is not None: set_parameter(osi, value=value, eles=eles, args=[pstr, 1])