def run():
    np = o3.mp.get_np()
    pid = o3.mp.get_pid()
    print(np)
    osi = o3.OpenSeesInstance(ndm=2, ndf=2, mp=True)
    mat = o3.uniaxial_material.Elastic(osi, 3000.0)
    nds = []
    if pid == 0:
        nds.append(o3.node.Node(osi, 0.0, 0.0, tag=0))
        nds.append(None)
        nds.append(None)
        nds.append(o3.node.Node(osi, 72.0, 96.0, tag=3))
        o3.Fix2DOF(osi, nds[0], o3.cc.FIXED, o3.cc.FIXED)
        ele = o3.element.Truss(osi, [nds[0], nds[3]], 10.0, mat)
        ts = o3.time_series.Linear(osi, 1)
        o3.pattern.Plain(osi, ts)
        o3.Load(osi, nds[3], [100.0, -50.0])

    else:
        nds.append(None)
        nds.append(o3.node.Node(osi, 144.0, 0.0, tag=1))
        nds.append(o3.node.Node(osi, 168.0, 0.0, tag=2))
        nds.append(o3.node.Node(osi, 72.0, 96.0, tag=3))
        o3.Fix2DOF(osi, nds[1], o3.cc.FIXED, o3.cc.FIXED)
        o3.Fix2DOF(osi, nds[2], o3.cc.FIXED, o3.cc.FIXED)
        o3.element.Truss(osi, [nds[1], nds[3]], 5.0, mat)
        o3.element.Truss(osi, [nds[2], nds[3]], 5.0, mat)

    o3.domain_change(osi)
    o3.constraints.Transformation(osi)
    o3.test_check.NormDispIncr(osi, tol=1.0e-6, max_iter=10, p_flag=0)
    o3.algorithm.Newton(osi)
    o3.numberer.apply_rcm(osi)
    o3.system.Mumps(osi)
    o3.integrator.LoadControl(osi, 0.1)
    o3.analysis.Static(osi)
    o3.analyze(osi, 10)

    print('Node 4: ', [o3.get_node_coords(osi, nds[3]), o3.get_node_disp(osi, nds[3])])

    o3.load_constant(osi)

    if pid == 0:
        o3.pattern.Plain(osi, ts)
        o3.Load(osi, nds[3], [1.0, 0.0])
    o3.domain_change(osi)
    o3.integrator.ParallelDisplacementControl(osi, nds[3], o3.cc.X, 0.1)
    o3.analyze(osi, 10)

    print('Node 4: ', [o3.get_node_coords(osi, nds[3]), o3.get_node_disp(osi, nds[3])])
    o3.stop(osi)
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)
示例#3
0
def test_ele_load_uniform():
    osi = o3.OpenSeesInstance(ndm=2, state=3)
    ele_len = 2.0
    coords = [[0, 0], [ele_len, 0]]
    ele_nodes = [o3.node.Node(osi, *coords[x]) for x in range(len(coords))]
    transf = o3.geom_transf.Linear2D(osi, [])
    ele = o3.element.ElasticBeamColumn2D(osi,
                                         ele_nodes=ele_nodes,
                                         area=1.0,
                                         e_mod=1.0,
                                         iz=1.0,
                                         transf=transf,
                                         mass=1.0,
                                         c_mass="string")

    for i, node in enumerate(ele_nodes):
        if i == 0:
            o3.Fix3DOF(osi, node, o3.cc.FIXED, o3.cc.FIXED, o3.cc.FIXED)
        else:
            o3.Fix3DOF(osi, node, o3.cc.FREE, o3.cc.FIXED, o3.cc.FIXED)

    ts_po = o3.time_series.Linear(osi, factor=1)
    o3.pattern.Plain(osi, ts_po)
    udl = 10.
    o3.EleLoad2DUniform(osi, ele, w_y=-udl)

    tol = 1.0e-4
    o3.constraints.Plain(osi)
    o3.numberer.RCM(osi)
    o3.system.BandGeneral(osi)
    o3.test_check.NormDispIncr(osi, tol, 6)
    o3.algorithm.Newton(osi)
    n_steps_gravity = 1
    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)
    opy.reactions()
    ele_loads = o3.get_ele_response(osi, ele, 'force')
    assert np.isclose(ele_loads[0], 0.0)
    assert np.isclose(ele_loads[1], udl * ele_len / 2)
    assert np.isclose(ele_loads[2], udl * ele_len**2 / 12)
    assert np.isclose(ele_loads[3], 0.0)
    assert np.isclose(ele_loads[4], udl * ele_len / 2)
    assert np.isclose(ele_loads[5], -udl * ele_len**2 / 12)
    assert np.isclose(o3.get_node_reaction(osi, ele_nodes[0], o3.cc.Y),
                      udl * ele_len / 2)
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():
    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()
示例#6
0
def run():
    osi = o3.OpenSeesInstance(ndm=1, ndf=1)
    mat = o3.uniaxial_material.Elastic(osi, 3000.0)

    n1 = o3.node.Node(osi, 0.0)
    n2 = o3.node.Node(osi, 72.0)

    o3.Fix1DOF(osi, n1, o3.cc.FIXED)

    o3.element.Truss(osi, [n1, n2], 10.0, mat)
    ts0 = o3.time_series.Linear(osi)
    o3.pattern.Plain(osi, ts0)
    o3.Load(osi, n1, [10])

    o3.constraints.Transformation(osi)
    o3.test_check.NormDispIncr(osi, tol=1.0e-6, max_iter=10)
    o3.numberer.RCM(osi)
    o3.system.ProfileSPD(osi)
    o3.algorithm.NewtonLineSearch(osi, 0.75)
    o3.integrator.Newmark(osi, 0.5, 0.25)

    o3.analysis.VariableTransient(osi)

    o3.analyze(osi, 5, 0.0001, 0.00001, 0.001, 10)
    time = o3.get_time(osi)
    print(f'time: ', o3.get_time(osi))
    approx_vtime = 0.0001 + 0.001  # One step at target, then one step at maximum
    assert 0.99 < time / approx_vtime < 1.01, (time, approx_vtime)
    o3.set_time(osi, 0.0)
    # Can still run a non-variable analysis - since analyze function has multiple dispatch.
    o3.analyze(osi, 5, 0.0001)
    time = o3.get_time(osi)
    print(f'time: ', o3.get_time(osi))
    approx_vtime = 0.0001 * 5  # variable transient is not active so time should be dt * 5
    # If variable transient is not active then time would be 0.0005
    assert 0.99 < time / approx_vtime < 1.01, (time, approx_vtime)
示例#7
0
 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
示例#8
0
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 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_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_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
示例#12
0
def run_uniaxial_force_driver(osi,
                              mat_obj,
                              forces,
                              d_step=0.001,
                              max_steps=10000,
                              handle='silent'):
    """
    A Uniaxial material force-defined driver

    Parameters
    ----------
    osi: o3.OpenSeesInstance()
        An Opensees instance
    mat_obj: o3.uniaxial_material.UniaxialMaterialBase()
        An instance of uniaxial material
    forces: array_like
        Target forces
    d_step: float
        Displacement increment
    max_steps: int
        Maximum number of steps to take to achieve target force
    handle: str
        Behaviour if target force not reached, If 'silent' then  change to next target force,
        if 'warn' then print warning and go to next force,
        else raise error.

    Returns
    -------
    disp: array_like
        Actual displacements
    react: array_like
        Reactions at each displacement
    """
    left_node = o3.node.Node(osi, 0, 0)
    right_node = o3.node.Node(osi, 0, 0)
    o3.Fix1DOF(osi, left_node, o3.cc.FIXED)
    o3.Fix1DOF(osi, right_node, o3.cc.FREE)
    ele = o3.element.ZeroLength(osi, [left_node, right_node],
                                mats=[mat_obj],
                                dirs=[o3.cc.DOF2D_X],
                                r_flag=1)

    o3.constraints.Plain(osi)
    o3.numberer.RCM(osi)
    o3.system.BandGeneral(osi)
    o3.test_check.NormDispIncr(osi, 0.002, 10, p_flag=0)
    o3.algorithm.Newton(osi)
    o3.integrator.DisplacementControl(osi, right_node, o3.cc.X, 0.0001)
    o3.analysis.Static(osi)
    ts_po = o3.time_series.Linear(osi, factor=1)
    o3.pattern.Plain(osi, ts_po)
    o3.Load(osi, right_node, [1.0])

    react = 0
    disp = [0]
    reacts = [react]

    diffs = np.diff(forces, prepend=0)
    orys = np.where(diffs >= 0, 1, -1)
    for i in range(len(forces)):
        ory = orys[i]
        o3.integrator.DisplacementControl(osi, right_node, o3.cc.X,
                                          -d_step * ory)
        for j in range(max_steps):
            if react * ory < forces[i] * ory:
                o3.analyze(osi, 1)
            else:
                break
            o3.gen_reactions(osi)
            react = o3.get_ele_response(osi, ele, 'force')[0]
            reacts.append(react)
            end_disp = -o3.get_node_disp(osi, right_node, dof=o3.cc.X)
            disp.append(end_disp)
        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(disp), np.array(reacts)
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)
示例#14
0
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):
    """
    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)

    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)
    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])
    sn = np.array(sn)
    # 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 = []
    strains = np.logspace(-6, -0.5, 16)
    ref_strain = 0.005
    rats = 1. / (1 + (strains / ref_strain)**0.91)
    prev_args = []
    prev_kwargs = {}
    prev_sl_type = None
    eles = []
    tau_inds = []
    total_stress_inds = 0
    strs_inds = []
    total_strain_inds = 0
    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
        if sl.type == 'pm4sand':
            sl_class = o3.nd_material.PM4Sand
            overrides = {'nu': pois, 'p_atm': 101, 'unit_moist_mass': umass}
            app2mod = sl.app2mod
        elif sl.type == 'sdmodel':
            sl_class = o3.nd_material.StressDensity
            overrides = {'nu': pois, 'p_atm': 101, 'unit_moist_mass': umass}
            app2mod = sl.app2mod
        elif sl.type == 'pimy':
            sl_class = o3.nd_material.PressureIndependMultiYield
            overrides = {
                'nu': pois,
                'p_atm': 101,
                'rho': umass,
                'nd': 2.0,
                'g_mod_ref': sl.g_mod / 1e3,
                'bulk_mod_ref': sl.bulk_mod / 1e3,
                'cohesion': sl.cohesion / 1e3,
                'd': 0.0,
                # 'no_yield_surf': 20
            }
            tau_inds.append(total_stress_inds + 3)  # 4th
            total_stress_inds += 5
            strs_inds.append(total_strain_inds + 2)  # 3rd
            total_strain_inds += 3
        else:
            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}

        # opw.extensions.to_py_file(osi)
        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
        # ele = o3.element.Quad(osi, nodes, ele_thick, o3.cc.PLANE_STRAIN, mat, b2=grav * unit_masses[i])
        ele = o3.element.SSPquad(osi,
                                 nodes,
                                 mat,
                                 'PlaneStrain',
                                 ele_thick,
                                 0.0,
                                 b2=grav * unit_masses[i])
        eles.append(ele)

    # 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)

    # o3.recorder.NodeToFile(osi, 'sample_out.txt', node=nd["R0L"], dofs=[o3.cc.X], res_type='accel')
    na = o3.recorder.NodeToArrayCache(osi,
                                      node=sn[0][0],
                                      dofs=[o3.cc.X],
                                      res_type='accel')
    otypes = ['ACCX', 'TAU', 'STRS']
    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
    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)
    o3.extensions.to_py_file(osi)

    while opy.getTime() < analysis_time:
        print(opy.getTime())
        if o3.analyze(osi, 1, analysis_dt):
            print('failed')
            break
    opy.wipe()
    out_dict = {}
    for otype in ods:
        if isinstance(ods[otype], list):
            out_dict[otype] = []
            for i in range(len(ods[otype])):
                a = ods[otype][i].collect()
                out_dict[otype].append(
                    a[3::4])  # TODO: not working for generic case
            out_dict[otype] = np.array(out_dict[otype])
        else:
            if otype == 'TAU':
                a = ods[otype].collect()
                out_dict[otype] = a[:, tau_inds].T * 1e3
            elif otype == 'STRS':
                a = ods[otype].collect()
                out_dict[otype] = a[:, strs_inds].T
            else:
                out_dict[otype] = ods[otype].collect().T
    out_dict['time'] = np.arange(0, len(out_dict[otype][0])) * rec_dt

    return out_dict
示例#15
0
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 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
示例#18
0
def site_response(sp, dy=0.5, forder=1.0e3, static=0):
    """
    Run gravity analysis of a soil profile

    Parameters
    ----------
    sp: sfsimodels.SoilProfile object
        A soil profile

    Returns
    -------

    """

    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 = []
    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
        # Define material
        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))

    # Gravity analysis
    if not static:
        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)
    else:
        o3.domain_change(osi)
        o3.constraints.Transformation(osi)
        o3.test_check.NormDispIncr(osi, tol=1.0e-6, max_iter=10, p_flag=0)
        o3.algorithm.Newton(osi)
        o3.numberer.apply_rcm(osi)
        o3.system.Mumps(osi)
        o3.integrator.LoadControl(osi, 0.1)
        o3.analysis.Static(osi)
        o3.analyze(osi, 10)

    ys = []
    for nn in range(len(sn)):
        nd = sn[nn][0]
        ys.append(o3.get_node_disp(osi, nd, dof=o3.cc.Y))

    return np.array(ys)
def run(etype, xi):
    mass = 100.0
    k = 100.0
    omega = np.sqrt(k / mass)
    c = 2 * xi * k / omega
    osi = o3.OpenSeesInstance(ndm=1, ndf=1)
    mat = o3.uniaxial_material.Elastic(osi, k)
    dashpot_mat = o3.uniaxial_material.Viscous(osi, big_c=c, alpha=1.)

    n0 = o3.node.Node(osi, 0.0)
    n0a = o3.node.Node(osi, 0.0)
    n1 = o3.node.Node(osi, 0.0)
    n2 = o3.node.Node(osi, 0.0)

    o3.Mass2D(osi, n1, x_mass=mass)
    o3.Mass2D(osi, n2, x_mass=mass)
    o3.Fix1DOF(osi, n0, x=o3.cc.FIXED)

    o3.element.ZeroLength(osi, [n1, n2], mats=[mat], dirs=[o3.cc.X])
    if xi > 0.0:
        o3.element.ZeroLength(osi, [n0, n0a],
                              mats=[dashpot_mat],
                              dirs=[o3.cc.X])
    o3.EqualDOF(osi, n1, n0a, dofs=[o3.cc.X])
    ts0 = o3.time_series.Linear(osi)
    o3.pattern.Plain(osi, ts0)
    o3.Load(osi, n1, [10])
    min_dt = 2. / omega
    print('min_dt: ', min_dt)

    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 = min_dt
    else:
        o3.algorithm.Linear(osi, factor_once=True)
        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.FullGeneral(osi)
            o3.integrator.CentralDifference(osi)
            explicit_dt = min_dt / 1
        elif etype == 'explicit_difference':
            o3.system.Diagonal(osi)
            o3.integrator.ExplicitDifference(osi)
            explicit_dt = min_dt / 1
        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)

    o3.analysis.Transient(osi)
    ttotal = 150  # s
    inc = 2
    nn = int(ttotal / dt / inc)
    print(nn)
    time = []
    disp1 = []
    disp2 = []
    for i in range(nn):
        time.append(o3.get_time(osi))
        disp1.append(o3.get_node_disp(osi, n1, dof=o3.cc.X))
        disp2.append(o3.get_node_disp(osi, n2, dof=o3.cc.X))
        o3.analyze(osi, inc, dt)

    return time, disp1, disp2
def run(use_pload):
    # If pload=true then apply point load at end, else apply distributed load along beam

    osi = o3.OpenSeesInstance(ndm=2, state=3)

    ele_len = 4.0

    # Establish nodes
    left_node = o3.node.Node(osi, 0, 0)
    right_node = o3.node.Node(osi, ele_len, 0)

    # Fix bottom node
    o3.Fix3DOF(osi, left_node, o3.cc.FIXED, o3.cc.FIXED, o3.cc.FIXED)
    o3.Fix3DOF(osi, right_node, o3.cc.FREE, o3.cc.FREE, o3.cc.FREE)
    e_mod = 200.0e2
    i_sect = 0.1
    area = 0.5
    lp_i = 0.1
    lp_j = 0.1

    elastic = 0

    if elastic:
        left_sect = o3.section.Elastic2D(osi, e_mod, area, i_sect)
    else:
        m_cap = 14.80
        b = 0.05
        phi = m_cap / (e_mod * i_sect)
        # mat_flex = o3.uniaxial_material.Steel01(osi, m_cap, e0=e_mod * i_sect, b=b)
        mat_flex = o3.uniaxial_material.ElasticBilin(osi, e_mod * i_sect,
                                                     e_mod * i_sect * b, phi)
        mat_axial = o3.uniaxial_material.Elastic(osi, e_mod * area)
        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.Elastic2D(osi, e_mod, area, i_sect)
    centre_sect = o3.section.Elastic2D(osi, e_mod, area, i_sect)
    integ = o3.beam_integration.HingeMidpoint(osi, left_sect, lp_i, right_sect,
                                              lp_j, centre_sect)
    beam_transf = o3.geom_transf.Linear2D(osi, )
    ele = o3.element.ForceBeamColumn(osi, [left_node, right_node], beam_transf,
                                     integ)

    w_gloads = 1
    if w_gloads:
        # Apply gravity loads
        pload = 1.0 * ele_len
        udl = 2.0
        ts_po = o3.time_series.Linear(osi, factor=1)
        o3.pattern.Plain(osi, ts_po)
        if use_pload:
            o3.Load(osi, right_node, [0, -pload, 0])
        else:
            o3.EleLoad2DUniform(osi, ele, -udl)

        tol = 1.0e-3
        o3.constraints.Plain(osi)
        o3.numberer.RCM(osi)
        o3.system.BandGeneral(osi)
        n_steps_gravity = 10
        o3.integrator.LoadControl(osi, 1. / n_steps_gravity, num_iter=10)
        o3.test_check.NormDispIncr(osi, tol, 10)
        o3.algorithm.Linear(osi)
        o3.analysis.Static(osi)
        o3.analyze(osi, n_steps_gravity)
        o3.gen_reactions(osi)
        print('reactions: ', o3.get_ele_response(osi, ele, 'force')[:3])
        end_disp = o3.get_node_disp(osi, right_node, dof=o3.cc.Y)
        print(f'end_disp: {end_disp}')
        if use_pload:
            disp_expected = pload * ele_len**3 / (3 * e_mod * i_sect)
            print(f'v_expected: {pload}')
            print(f'm_expected: {pload * ele_len}')
            print(f'disp_expected: {disp_expected}')
        else:
            v_expected = udl * ele_len
            m_expected = udl * ele_len**2 / 2
            disp_expected = udl * ele_len**4 / (8 * e_mod * i_sect)
            print(f'v_expected: {v_expected}')
            print(f'm_expected: {m_expected}')
            print(f'disp_expected: {disp_expected}')

        # o3.extensions.to_py_file(osi, 'temp4.py')
    disp_load = 1
    if disp_load:
        # start displacement controlled
        d_inc = -0.01

        # opy.wipeAnalysis()
        o3.numberer.RCM(osi)
        o3.system.BandGeneral(osi)
        o3.test_check.NormUnbalance(osi, 2, max_iter=10, p_flag=0)
        # 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, right_node, o3.cc.Y, d_inc)
        o3.analysis.Static(osi)
        ts_po = o3.time_series.Linear(osi, factor=1)
        o3.pattern.Plain(osi, ts_po)
        o3.Load(osi, right_node, [0.0, 1.0, 0])
        ok = o3.analyze(osi, 10)
        end_disp = o3.get_node_disp(osi, right_node, dof=o3.cc.Y)
        print(f'end_disp: {end_disp}')
        r = o3.get_ele_response(osi, ele, 'force')[:3]
        print('reactions: ', r)
        k = r[1] / -end_disp
        print('k: ', k)
        k_elastic_expected = 1. / (ele_len**3 / (3 * e_mod * i_sect))
        print('k_elastic_expected: ', k_elastic_expected)
示例#21
0
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
示例#22
0
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
示例#23
0
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 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
示例#25
0
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
示例#26
0
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
示例#27
0
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 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()
示例#29
0
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
示例#30
0
def run_uniaxial_disp_driver(osi, mat_obj, disps, target_d_inc=1.0e-5):
    """
    A Uniaxial material displacement controlled driver

    Parameters
    ----------
    osi: o3.OpenSeesInstance()
        An Opensees instance
    mat_obj: o3.uniaxial_material.UniaxialMaterialBase()
        An instance of uniaxial material
    disps: array_like
        Target displacements
    target_d_inc: float
        Target displacement increment

    Returns
    -------
    disp: array_like
        Actual displacements
    react: array_like
        Reactions at each displacement
    """
    left_node = o3.node.Node(osi, 0, 0)
    right_node = o3.node.Node(osi, 0, 0)
    o3.Fix1DOF(osi, left_node, o3.cc.FIXED)
    o3.Fix1DOF(osi, right_node, o3.cc.FREE)
    ele = o3.element.ZeroLength(osi, [left_node, right_node],
                                mats=[mat_obj],
                                dirs=[o3.cc.DOF2D_X],
                                r_flag=1)

    disp = []
    react = []

    o3.constraints.Plain(osi)
    o3.numberer.RCM(osi)
    o3.system.BandGeneral(osi)
    o3.test_check.NormDispIncr(osi, 0.002, 10, p_flag=0)
    o3.algorithm.Newton(osi)
    o3.integrator.DisplacementControl(osi, right_node, o3.cc.X, -target_d_inc)
    o3.analysis.Static(osi)
    ts_po = o3.time_series.Linear(osi, factor=1)
    o3.pattern.Plain(osi, ts_po)
    o3.Load(osi, right_node, [1.0])

    disp.append(0)
    react.append(0)
    d_incs = np.diff(disps, prepend=0)
    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
        o3.integrator.DisplacementControl(osi, right_node, o3.cc.X, d_step)
        o3.analyze(osi, n)
        o3.gen_reactions(osi)
        react.append(o3.get_ele_response(osi, ele, 'force')[0])
        end_disp = -o3.get_node_disp(osi, right_node, dof=o3.cc.X)
        disp.append(end_disp)
    return np.array(disp), np.array(react)