def step(self, action=0):
        self.time = op.getTime()
        assert (self.time < self.analysis_time)

        # 選ばれたアクションに応じて減衰定数を変化させる
        self.h = self.action[action]
        self.hs.append(self.h)
        self.beta_k = 2 * self.h / self.w0
        op.rayleigh(self.alpha_m, self.beta_k, self.beta_k_init,
                    self.beta_k_comm)

        op.analyze(1, self.dt)
        op.reactions()

        self.dis = op.nodeDisp(self.top_node, 1)
        self.vel = op.nodeVel(self.top_node, 1)
        self.acc = op.nodeAccel(self.top_node, 1)
        self.a_acc = self.acc + self.values[self.i]
        self.force = -op.nodeReaction(self.bot_node,
                                      1)  # Negative since diff node

        self.resp["time"].append(self.time)
        self.resp["dis"].append(self.dis)
        self.resp["vel"].append(self.vel)
        self.resp["acc"].append(self.acc)
        self.resp["a_acc"].append(self.a_acc)
        self.resp["force"].append(self.force)

        next_time = op.getTime()
        self.done = next_time >= self.analysis_time

        self.i_pre = self.i
        self.i += 1
        self.i_next = self.i + 1 if not self.done else self.i
        return self.reward, self.done
Beispiel #2
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
def get_inelastic_response(fb, asig, extra_time=0.0, xi=0.05, analysis_dt=0.001):
    """
    Run seismic analysis of a nonlinear FrameBuilding

    :param fb: FrameBuilding object
    :param asig: AccSignal object
    :param extra_time: float, additional analysis time after end of ground motion
    :param xi: damping ratio
    :param analysis_dt: time step to perform the analysis
    :return:
    """

    op.wipe()
    op.model('basic', '-ndm', 2, '-ndf', 3)  # 2 dimensions, 3 dof per node

    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):
            n_i = cc * 100 + ss
            nd["C%i-S%i" % (cc, ss)] = n_i
            op.node(n_i, 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)
                op.mass(n_i, node_mass)

    # 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):
            op.equalDOF(nd["C%i-S%i" % (1, ss)], nd["C%i-S%i" % (cc, ss)],  opc.X)

    # Fix all base nodes
    for cc in range(1, n_cols + 1):
        op.fix(nd["C%i-S%i" % (cc, 0)], opc.FIXED, opc.FIXED, opc.FIXED)

    # Coordinate transformation
    geo_tag = 1
    trans_args = []
    op.geomTransf("Linear", geo_tag, *[])

    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)

    # Define beams and columns

    md = OrderedDict()  # material dict
    sd = OrderedDict()  # section dict
    ed = OrderedDict()  # element dict
    # 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)
    for ss in range(fb.n_storeys):

        # set columns
        for cc in range(1, fb.n_cols + 1):
            ele_i = cc * 100 + ss
            md["C%i-S%i" % (cc, ss)] = ele_i
            sd["C%i-S%i" % (cc, ss)] = ele_i
            ed["C%i-S%i" % (cc, ss)] = ele_i
            mat_props = elastic_bilin(ei_columns[ss][cc - 1], 0.05 * ei_columns[ss][cc - 1], phi_y_col[ss][cc - 1])
            #print(opc.ELASTIC_BILIN, ele_i, *mat_props)
            op.uniaxialMaterial(opc.ELASTIC_BILIN, ele_i, *mat_props)
            # op.uniaxialMaterial("Elastic", ele_i, ei_columns[ss][cc - 1])

            node_numbers = [nd["C%i-S%i" % (cc, ss)], nd["C%i-S%i" % (cc, ss + 1)]]
            op.element(opc.ELASTIC_BEAM_COLUMN, ele_i,
                       *node_numbers,
                       a_columns[ss - 1][cc - 1],
                       e_conc,
                       i_columns[ss - 1][cc - 1],
                       geo_tag
                       )

        # Set beams
        for bb in range(1, fb.n_bays + 1):
            ele_i = bb * 10000 + ss
            md["B%i-S%i" % (bb, ss)] = ele_i
            sd["B%i-S%i" % (bb, ss)] = ele_i
            ed["B%i-S%i" % (bb, ss)] = ele_i
            mat_props = elastic_bilin(ei_beams[ss][bb - 1], 0.05 * ei_beams[ss][bb - 1], phi_y_beam[ss][bb - 1])
            op.uniaxialMaterial(opc.ELASTIC_BILIN, ele_i, *mat_props)
            # op.uniaxialMaterial("Elastic", ele_i, ei_beams[ss][bb - 1])
            node_numbers = [nd["C%i-S%i" % (bb, ss + 1)], nd["C%i-S%i" % (bb + 1, ss + 1)]]
            print((opc.BEAM_WITH_HINGES, ele_i,
                       *node_numbers,
                        sd["B%i-S%i" % (bb, ss)], l_hinge,
                       sd["B%i-S%i" % (bb, ss)], l_hinge,
                       sd["B%i-S%i" % (bb, ss)], geo_tag
                       ))
            # Old definition
            # op.element(opc.BEAM_WITH_HINGES, ele_i,
            #  *[nd["C%i-S%i" % (bb, ss - 1)], nd["C%i-S%i" % (bb + 1, ss)]],
            #  sd["B%i-S%i" % (bb, ss)], l_hinge,
            #  sd["B%i-S%i" % (bb, ss)], l_hinge,
            #  e_conc,
            #  a_beams[ss - 1][bb - 1],
            #  i_beams[ss - 1][bb - 1], geo_tag
            #  )
            # New definition
            # op.element(opc.BEAM_WITH_HINGES, ele_i,
            #            *node_numbers,
            #             sd["B%i-S%i" % (bb, ss)], l_hinge,
            #            sd["B%i-S%i" % (bb, ss)], l_hinge,
            #            sd["B%i-S%i" % (bb, ss)], geo_tag  # TODO: make this elastic
            #
            #            )

            # Elastic definition
            op.element(opc.ELASTIC_BEAM_COLUMN, ele_i,
                       *node_numbers,
                       a_beams[ss - 1][bb - 1],
                       e_conc,
                       i_beams[ss - 1][bb - 1],
                       geo_tag
                       )

    # Define the dynamic analysis
    load_tag_dynamic = 1
    pattern_tag_dynamic = 1

    values = list(-1 * asig.values)  # should be negative
    op.timeSeries('Path', load_tag_dynamic, '-dt', asig.dt, '-values', *values)
    op.pattern('UniformExcitation', pattern_tag_dynamic, opc.X, '-accel', load_tag_dynamic)

    # set damping based on first eigen mode
    angular_freq2 = op.eigen('-fullGenLapack', 1)
    if hasattr(angular_freq2, '__len__'):
        angular_freq2 = angular_freq2[0]
    angular_freq = angular_freq2 ** 0.5
    if isinstance(angular_freq, complex):
        raise ValueError("Angular frequency is complex, issue with stiffness or mass")
    alpha_m = 0.0
    beta_k = 2 * xi / angular_freq
    beta_k_comm = 0.0
    beta_k_init = 0.0
    period = angular_freq / 2 / np.pi
    print("period: ", period)

    op.rayleigh(alpha_m, beta_k, beta_k_init, beta_k_comm)

    # Run the dynamic analysis

    op.wipeAnalysis()

    op.algorithm('Newton')
    op.system('SparseGeneral')
    op.numberer('RCM')
    op.constraints('Transformation')
    op.integrator('Newmark', 0.5, 0.25)
    op.analysis('Transient')
    #op.test("NormDispIncr", 1.0e-1, 2, 0)
    tol = 1.0e-10
    iter = 10
    op.test('EnergyIncr', tol, iter, 0, 2)  # TODO: make this test work
    analysis_time = (len(values) - 1) * asig.dt + extra_time
    outputs = {
        "time": [],
        "rel_disp": [],
        "rel_accel": [],
        "rel_vel": [],
        "force": []
    }
    print("Analysis starting")
    while op.getTime() < analysis_time:
        curr_time = op.getTime()
        op.analyze(1, analysis_dt)
        outputs["time"].append(curr_time)
        outputs["rel_disp"].append(op.nodeDisp(nd["C%i-S%i" % (1, fb.n_storeys)], opc.X))
        outputs["rel_vel"].append(op.nodeVel(nd["C%i-S%i" % (1, fb.n_storeys)], opc.X))
        outputs["rel_accel"].append(op.nodeAccel(nd["C%i-S%i" % (1, fb.n_storeys)], opc.X))
        op.reactions()
        react = 0
        for cc in range(1, fb.n_cols):
            react += -op.nodeReaction(nd["C%i-S%i" % (cc, 0)], opc.X)
        outputs["force"].append(react)  # Should be negative since diff node
    op.wipe()
    for item in outputs:
        outputs[item] = np.array(outputs[item])

    return outputs
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
Beispiel #5
0
def analisis_opensees(path, permutaciones):  #helper, #win
    ops.wipe()

    # bucle para generar los x análisis
    for i in range(len(permutaciones)):

        perfil = str(permutaciones[i][0])
        nf = permutaciones[i][2]
        amort = permutaciones[i][3]
        den = permutaciones[i][4]
        vel = permutaciones[i][5]
        capas = len(permutaciones[i][6])
        nstep = permutaciones[i][30]
        dt = float(permutaciones[i][31])

        # creación de elementos
        sElemX = permutaciones[i][1]  # elementos en X
        sElemZ = permutaciones[i][46]  # espesor en Z

        # =============================================================================
        #         ######## geometría de la columna ######
        # =============================================================================
        # límite entre capas
        limite_capa = []
        anterior = 0
        for j in range(capas):
            espesor = permutaciones[i][8][j]

            limite_capa.append(espesor + anterior)
            anterior = limite_capa[j]
            print('Límite de capa: ' + str(limite_capa[j]))

        # creación de elementos y nodos en x
        nElemX = 1  # elementos en x
        nNodeX = 2 * nElemX + 1  # nodos en x

        # creación de elementos y nodos para z
        nElemZ = 1

        # creación de elementos y nodos en Y y totales
        nElemY = []  # elementos en y
        sElemY = []  # dimension en y
        nElemT = 0
        for j in range(capas):
            espesor = permutaciones[i][8][j]

            nElemY.append(2 * espesor)
            nElemT += nElemY[j]
            print('Elementos en capa ' + str(j + 1) + ': ' + str(nElemY[j]))
            sElemY.append(permutaciones[i][8][j] / nElemY[j])
            print('Tamaño de los elementos en capa ' + str(j + 1) + ': ' +
                  str(sElemY[j]) + '\n')

        # number of nodes in vertical direction in each layer
        nNodeY = []  # dimension en y
        nNodeT = 0
        s = 0
        for j in range(capas - 1):
            nNodeY.append(4 * nElemY[j])
            nNodeT += nNodeY[j]
            s += 1
            print('Nodos en capa ' + str(j + 1) + ': ' + str(nNodeY[j]))

        nNodeY.append(4 * (nElemY[-1] + 1))
        nNodeT += nNodeY[-1]
        print('Nodos en capa ' + str(s + 1) + ': ' + str(nNodeY[s]))
        print('Nodos totales: ' + str(nNodeT))

        #win.ui.progressBar.setValue(15)

        # =============================================================================
        #         ######### Crear nodos del suelo ##########
        # =============================================================================
        # creación de nodos de presión de poros
        ops.model('basic', '-ndm', 3, '-ndf', 4)

        with open(path + '/Post-proceso/' + perfil + '/ppNodesInfo.dat',
                  'w') as f:
            count = 0.0
            yCoord = 0.0
            nodos = []
            dryNode = []
            altura_nf = 10 - nf

            for k in range(capas):
                for j in range(0, int(nNodeY[k]), 4):
                    ops.node(j + count + 1, 0.0, yCoord, 0.0)
                    ops.node(j + count + 2, 0.0, yCoord, sElemZ)
                    ops.node(j + count + 3, sElemX, yCoord, sElemZ)
                    ops.node(j + count + 4, sElemX, yCoord, 0.0)

                    f.write(
                        str(int(j + count + 1)) + '\t' + str(0.0) + '\t' +
                        str(yCoord) + '\t' + str(0.0) + '\n')
                    f.write(
                        str(int(j + count + 2)) + '\t' + str(0.0) + '\t' +
                        str(yCoord) + '\t' + str(sElemZ) + '\n')
                    f.write(
                        str(int(j + count + 3)) + '\t' + str(sElemX) + '\t' +
                        str(yCoord) + '\t' + str(sElemZ) + '\n')
                    f.write(
                        str(int(j + count + 4)) + '\t' + str(sElemX) + '\t' +
                        str(yCoord) + '\t' + str(0.0) + '\n')

                    nodos.append(str(j + count + 1))
                    nodos.append(str(j + count + 2))
                    nodos.append(str(j + count + 3))
                    nodos.append(str(j + count + 4))

                    #designate node sobre la superficie de agua
                    if yCoord >= altura_nf:
                        dryNode.append(j + count + 1)
                        dryNode.append(j + count + 2)
                        dryNode.append(j + count + 3)
                        dryNode.append(j + count + 4)

                    yCoord = (yCoord + sElemY[k])

                count = (count + nNodeY[k])

        print("Finished creating all soil nodes...")

        # =============================================================================
        #         ####### Condiciones de contorno en la base de la columna #########
        # =============================================================================
        ops.fix(1, *[0, 1, 1, 0])
        ops.fix(2, *[0, 1, 1, 0])
        ops.fix(3, *[0, 1, 1, 0])
        ops.fix(4, *[0, 1, 1, 0])
        ops.equalDOF(1, 2, 1)
        ops.equalDOF(1, 3, 1)
        ops.equalDOF(1, 4, 1)

        print('Fin de creación de nodos de la base de la columna\n\n')

        # =============================================================================
        #         ####### Condiciones de contorno en los nudos restantes #########
        # =============================================================================

        count = 0
        for k in range(5, int(nNodeT + 1), 4):
            ops.equalDOF(k, k + 1, *[1, 2, 3])
            ops.equalDOF(k, k + 2, *[1, 2, 3])
            ops.equalDOF(k, k + 3, *[1, 2, 3])

        print('Fin de creación equalDOF para nodos de presión de poros\n\n')

        for j in range(len(dryNode)):
            ops.fix(dryNode[j], *[0, 0, 0, 1])

        print("Finished creating all soil boundary conditions...")

        # =============================================================================
        #         ####### crear elemento y material de suelo #########
        # =============================================================================

        cargas = []
        for j in range(capas):
            pendiente = permutaciones[i][9][j]
            slope = math.atan(pendiente / 100)

            tipo_suelo = permutaciones[i][6][j]
            rho = permutaciones[i][10][j]
            Gr = permutaciones[i][12][j]
            Br = permutaciones[i][13][j]
            fric = permutaciones[i][15][j]
            refpress = permutaciones[i][18][j]
            gmax = permutaciones[i][19][j]
            presscoef = permutaciones[i][20][j]
            surf = permutaciones[i][21][j]
            ev = permutaciones[i][22][j]
            cc1 = permutaciones[i][23][j]
            cc3 = permutaciones[i][24][j]
            cd1 = permutaciones[i][25][j]
            cd3 = permutaciones[i][26][j]
            ptang = permutaciones[i][27][j]
            coh = permutaciones[i][28][j]

            if tipo_suelo == 'No cohesivo':
                if float(surf) > 0:
                    ops.nDMaterial('PressureDependMultiYield02', j + 1, 3.0,
                                   rho, Gr, Br, fric, gmax, refpress,
                                   presscoef, ptang, cc1, cc3, cd1, cd3,
                                   float(surf), 5.0, 3.0, *[1.0, 0.0], ev,
                                   *[0.9, 0.02, 0.7, 101.0])
                else:
                    ops.nDMaterial('PressureDependMultiYield02', j + 1, 3.0,
                                   rho, Gr, Br, fric, gmax, refpress,
                                   presscoef, ptang, cc1, cc3, cd1, cd3,
                                   float(surf), *permutaciones[i][29][j], 5.0,
                                   3.0, *[1.0,
                                          0.0], ev, *[0.9, 0.02, 0.7, 101.0])

            cargas.append(
                [0.0, -9.81 * math.cos(slope), -9.81 * math.sin(slope)])

        print('Fin de la creación de material de suelo\n\n')

        #-----------------------------------------------------------------------------------------
        #  5. CREATE SOIL ELEMENTS
        #-----------------------------------------------------------------------------------------

        count = 0
        alpha = 1.5e-6

        with open(path + '/Post-proceso/' + perfil + '/ppElemInfo.dat',
                  'w') as f:
            # crear elemento de suelo
            for k in range(capas):
                for j in range(int(nElemY[k])):
                    nI = 4 * (j + count + 1) - 3
                    nJ = nI + 1
                    nK = nI + 2
                    nL = nI + 3
                    nM = nI + 4
                    nN = nI + 5
                    nO = nI + 6
                    nP = nI + 7
                    f.write(
                        str(j + count + 1) + '\t' + str(nI) + '\t' + str(nJ) +
                        '\t' + str(nK) + '\t' + str(nL) + '\t' + str(nM) +
                        '\t' + str(nN) + '\t' + str(nO) + '\t' + str(nP) +
                        '\n')

                    Bc = permutaciones[i][14][k]
                    ev = permutaciones[i][22][k]

                    ops.element('SSPbrickUP', (j + count + 1),
                                *[nI, nJ, nK, nL, nM, nN, nO,
                                  nP], (k + 1), float(Bc), 1.0, 1.0, 1.0, 1.0,
                                float(ev), alpha, cargas[k][0], cargas[k][1],
                                cargas[k][2])

                count = (count + int(nElemY[k]))
        print('Fin de la creación del elemento del suelo\n\n')

        #win.ui.progressBar.setValue(25)

        # =============================================================================
        #         ######### Amortiguamiento de Lysmer ##########
        # =============================================================================

        ops.model('basic', '-ndm', 3, '-ndf', 3)

        # definir nodos y coordenadas del amortiguamiento
        dashF = nNodeT + 1
        dashX = nNodeT + 2
        dashZ = nNodeT + 3

        ops.node(dashF, 0.0, 0.0, 0.0)
        ops.node(dashX, 0.0, 0.0, 0.0)
        ops.node(dashZ, 0.0, 0.0, 0.0)

        # definir restricciones para los nodos de amortiguamiento
        ops.fix(dashF, 1, 1, 1)
        ops.fix(dashX, 0, 1, 1)
        ops.fix(dashZ, 1, 1, 0)

        # definir equalDOF para el amortiguamiento en la base del suelo
        ops.equalDOF(1, dashX, 1)
        ops.equalDOF(1, dashZ, 3)

        print(
            'Fin de la creación de condiciones de contorno de los nodos de amortiguamiento\n\n'
        )

        # definir el material de amortiguamiento
        colArea = sElemX * sElemZ
        dashpotCoeff = vel * den * colArea
        ops.uniaxialMaterial('Viscous', capas + 1, dashpotCoeff, 1.0)

        # definir el elemento
        ops.element('zeroLength', nElemT + 1, *[dashF, dashX], '-mat',
                    capas + 1, '-dir', *[1])
        ops.element('zeroLength', nElemT + 2, *[dashF, dashZ], '-mat',
                    capas + 1, '-dir', *[3])

        print('Fin de la creación del elemento de amortiguamiento\n\n')

        #-----------------------------------------------------------------------------------------
        #  9. DEFINE ANALYSIS PARAMETERS
        #-----------------------------------------------------------------------------------------

        # amortiguamiento de Rayleigh
        # frecuencia menor
        omega1 = 2 * math.pi * 0.2
        # frecuencia mayor
        omega2 = 2 * math.pi * 20

        a0 = 2.0 * (amort / 100) * omega1 * omega2 / (omega1 + omega2)
        a1 = 2.0 * (amort / 100) / (omega1 + omega2)
        print('Coeficientes de amortiguamiento' + '\n' + 'a0: ' +
              format(a0, '.6f') + '\n' + 'a1: ' + format(a1, '.6f') + '\n\n')

        #win.ui.progressBar.setValue(35)

        # =============================================================================
        #         ######## Determinación de análisis estático #########
        # =============================================================================
        #---DETERMINE STABLE ANALYSIS TIME STEP USING CFL CONDITION
        # se determina a partir de un análisis transitorio de largo tiempo
        duration = nstep * dt

        # tamaño mínimo del elemento y velocidad máxima
        minSize = sElemY[0]
        vsMax = permutaciones[i][11][0]
        for j in range(1, capas):
            if sElemY[j] < minSize:
                minSize = sElemY[j]
            if permutaciones[i][11][j] > vsMax:
                vsMax = permutaciones[i][11][j]

        # trial analysis time step
        kTrial = minSize / (vsMax**0.5)

        # tiempo de análisis y pasos de tiempo
        if dt <= kTrial:
            nStep = nstep
            dT = dt
        else:
            nStep = int(math.floor(duration / kTrial) + 1)
            dT = duration / nStep

        print('Número de pasos en el análisis: ' + str(nStep) + '\n')
        print('Incremento de tiempo: ' + str(dT) + '\n\n')

        #----------------------------------------------------------------------------------------
        #  7. GRAVITY ANALYSIS
        #-----------------------------------------------------------------------------------------
        ops.model('basic', '-ndm', 3, '-ndf', 4)

        ops.updateMaterialStage('-material', int(k + 1), '-stage', 0)

        # algoritmo de análisis estático
        ops.constraints(permutaciones[i][32][0],
                        float(permutaciones[i][32][1]),
                        float(permutaciones[i][32][2]))
        ops.test(permutaciones[i][34][0], float(permutaciones[i][34][1]),
                 int(permutaciones[i][34][2]), int(permutaciones[i][34][3]))
        ops.algorithm(permutaciones[i][38][0])
        ops.numberer(permutaciones[i][33][0])
        ops.system(permutaciones[i][36][0])
        ops.integrator(permutaciones[i][35][0], float(permutaciones[i][35][1]),
                       float(permutaciones[i][35][2]))
        ops.analysis(permutaciones[i][37][0])

        print('Inicio de análisis estático elástico\n\n')

        ops.start()
        ops.analyze(20, 5.0e2)

        print('Fin de análisis estático elástico\n\n')

        #win.ui.progressBar.setValue(40)

        # update materials to consider plastic behavior

        # =============================================================================

        ops.updateMaterialStage('-material', int(k + 1), '-stage', 1)

        # =============================================================================

        # plastic gravity loading
        print('Inicio de análisis estático plástico\n\n')

        ok = ops.analyze(40, 5.0e-2)

        if ok != 0:
            error = 'Error de convergencia en análisis estático de modelo' + str(
                perfil) + '\n\n'
            print(error)

            break

        print('Fin de análisis estático plástico\n\n')

        #-----------------------------------------------------------------------------------------
        #  11. UPDATE ELEMENT PERMEABILITY VALUES FOR POST-GRAVITY ANALYSIS
        #-----------------------------------------------------------------------------------------

        ini = 1
        aum = 0
        sum = 0
        for j in range(capas):
            #Layer 3
            ops.setParameter(
                '-val', permutaciones[i][16][j],
                ['-eleRange',
                 int(ini + aum),
                 int(nElemY[j] + sum)], 'xPerm')
            ops.setParameter(
                '-val', permutaciones[i][17][j],
                ['-eleRange',
                 int(ini + aum),
                 int(nElemY[j] + sum)], 'yPerm')
            ops.setParameter(
                '-val', permutaciones[i][16][j],
                ['-eleRange',
                 int(ini + aum),
                 int(nElemY[j] + sum)], 'zPerm')

            ini = nElemY[j] + sum
            sum += nElemY[j]
            aum = 1

        print("Finished updating permeabilities for dynamic analysis...")

        # =============================================================================
        #         ########### Grabadores dinámicos ##########
        # =============================================================================

        ops.setTime(0.0)
        ops.wipeAnalysis()
        ops.remove('recorders')

        # tiempo de la grabadora
        recDT = 10 * dt
        path_acel = path + '/Post-proceso/' + perfil + '/dinamico/aceleraciones/'

        ops.recorder('Node', '-file', path_acel + 'accelerationx.out', '-time',
                     '-dT', recDT, '-node', *nodos, '-dof', 1, 'accel')

        print('Fin de creación de grabadores\n\n')
        #win.ui.progressBar.setValue(50)

        # =============================================================================
        #         ######### Determinación de análisis dinámico ##########
        # =============================================================================

        # objeto de serie temporal para el historial de fuerza

        path_vel = path + '/Pre-proceso/' + perfil + '/TREASISL2.txt'

        ops.timeSeries('Path', 1, '-dt', dt, '-filePath', path_vel, '-factor',
                       dashpotCoeff)

        ops.pattern('Plain', 10, 1)
        ops.load(1, *[1.0, 0.0, 0.0, 0.0])  #CAMBIO REALIZADO OJO

        print('Fin de creación de carga dinámica\n\n')

        # algoritmo de análisis dinámico
        ops.constraints(permutaciones[i][39][0],
                        float(permutaciones[i][39][1]),
                        float(permutaciones[i][39][2]))
        ops.test(permutaciones[i][41][0], float(permutaciones[i][41][1]),
                 int(permutaciones[i][41][2]), int(permutaciones[i][41][3]))
        ops.algorithm(permutaciones[i][45][0])
        ops.numberer(permutaciones[i][40][0])
        ops.system(permutaciones[i][43][0])
        ops.integrator(permutaciones[i][42][0], float(permutaciones[i][42][1]),
                       float(permutaciones[i][42][2]))
        ops.analysis(permutaciones[i][44][0])
        # =============================================================================
        #         ops.rayleigh(a0, a1, 0.0, 0.0)
        # =============================================================================

        print('Inicio de análisis dinámico\n\n')
        #win.ui.progressBar.setValue(85)
        ok = ops.analyze(nStep, dT)

        if ok != 0:
            error = 'Error de convergencia en análisis dinámico de modelo' + str(
                permutaciones[i][0]) + '\n\n'
            print(error)
            curTime = ops.getTime()
            mTime = curTime
            print('cursTime:' + str(curTime))
            curStep = (curTime / dT)
            print('cursStep:' + str(curStep))
            rStep = (nStep - curStep) * 2.0
            remStep = int(nStep - curStep) * 2.0
            print('remSTep:' + str(curStep))
            dT = (dT / 2)
            print('dT:' + str(dT))

            ops.analyze(remStep, dT)

            if ok != 0:
                error = 'Error de convergencia en análisis dinámico de modelo' + str(
                    permutaciones[i][0]) + '\n\n'
                print(error)
                curTime = ops.getTime()
                print('cursTime:' + str(curTime))
                curStep = (curTime - mTime) / dT
                print('cursStep:' + str(curStep))
                remStep = int(rStep - curStep) * 2
                print('remSTep:' + str(curStep))
                dT = (dT / 2)
                print('dT:' + str(dT))

                ops.analyze(remStep, dT)

        print('Fin de análisis dinámico\n\n')

    ops.wipe()
Beispiel #6
0
# create numberer object
ops.numberer('Plain')

# create convergence test object
ops.test('PFEM', 1e-5, 1e-5, 1e-5, 1e-5, 1e-5, 1e-5, 10, 3, 1, 2)

# create algorithm object
ops.algorithm('Newton')

# create integrator object
ops.integrator('PFEM', 0.5, 0.25)

# create SOE object
ops.system('PFEM')
# ops.system('PFEM', '-mumps) Linux version can use mumps

# create analysis object
ops.analysis('PFEM', dtmax, dtmin, b2)

# analysis
while ops.getTime() < totaltime:

    # analysis
    if ops.analyze() < 0:
        break

    ops.remesh()

print("==========================================")
Beispiel #7
0
op.test('EnergyIncr', Tol, maxNumIter)
op.algorithm('ModifiedNewton')

NewmarkGamma = 0.5
NewmarkBeta = 0.25
op.integrator('Newmark', NewmarkGamma, NewmarkBeta)
op.analysis('Transient')

DtAnalysis = 0.01  # time-step Dt for lateral analysis
TmaxAnalysis = 10.0  # maximum duration of ground-motion analysis

Nsteps = int(TmaxAnalysis / DtAnalysis)

ok = op.analyze(Nsteps, DtAnalysis)

tCurrent = op.getTime()

# for gravity analysis, load control is fine, 0.1 is the load factor increment (http://opensees.berkeley.edu/wiki/index.php/Load_Control)

test = {
    1: 'NormDispIncr',
    2: 'RelativeEnergyIncr',
    4: 'RelativeNormUnbalance',
    5: 'RelativeNormDispIncr',
    6: 'NormUnbalance'
}
algorithm = {
    1: 'KrylovNewton',
    2: 'SecantNewton',
    4: 'RaphsonNewton',
    5: 'PeriodicNewton',
Beispiel #8
0
def run_sensitivity_analysis(ctrlNode,
                             dof,
                             baseNode,
                             SensParam,
                             steps=500,
                             IOflag=False):
    """
    Run load-control sensitivity analysis
    """
    ops.wipeAnalysis()
    start_time = time.time()

    title("Running Load-Control Sensitivity Analysis ...")

    ops.system("BandGeneral")
    ops.numberer("RCM")
    ops.constraints("Transformation")
    ops.test("NormDispIncr", 1.0E-12, 10, 3)
    ops.algorithm("Newton")  # KrylovNewton
    ops.integrator("LoadControl", 1 / steps)
    ops.analysis("Static")
    ops.sensitivityAlgorithm(
        "-computeAtEachStep"
    )  # automatically compute sensitivity at the end of each step

    outputs = {
        "time": np.array([]),
        "disp": np.array([]),
        "force": np.array([]),
    }

    for sens in SensParam:
        outputs[f"sensDisp_{sens}"] = np.array([]),

    for i in range(steps):
        ops.reactions()
        if IOflag:
            print(
                f"Single Cycle Response: Step #{i}, Node #{ctrlNode}: {ops.nodeDisp(ctrlNode, dof):.3f} {LunitTXT} / {-ops.nodeReaction(baseNode, dof):.2f} {FunitTXT}."
            )
        ops.analyze(1)
        tCurrent = ops.getTime()

        outputs["time"] = np.append(outputs["time"], tCurrent)
        outputs["disp"] = np.append(outputs["disp"],
                                    ops.nodeDisp(ctrlNode, dof))
        outputs["force"] = np.append(outputs["force"],
                                     -ops.nodeReaction(baseNode, dof))

        for sens in SensParam:
            # sensDisp(patternTag, paramTag)
            outputs[f"sensDisp_{sens}"] = np.append(
                outputs[f"sensDisp_{sens}"],
                ops.sensNodeDisp(ctrlNode, dof, sens))

    title("Sensitvity Analysis Completed!")
    print(
        f"Analysis elapsed time is {(time.time() - start_time):.3f} seconds.\n"
    )

    return outputs
ops.numberer('RCM')

# Create the integration scheme, the Newmark with alpha =0.5 and beta =.25
ops.integrator('Newmark', 0.5, 0.25)

# Create the analysis object
ops.analysis('Transient')

# Perform an eigenvalue analysis
numEigen = 2
eigenValues = ops.eigen(numEigen)
print("eigen values at start of transient:", eigenValues)

# set some variables
tFinal = nPts * dt
tCurrent = ops.getTime()
ok = 0

time = [tCurrent]
u3 = [0.0]

# Perform the transient analysis
while ok == 0 and tCurrent < tFinal:

    ok = ops.analyze(1, .01)

    # if the analysis fails try initial tangent iteration
    if ok != 0:
        print(
            "regular newton failed .. lets try an initail stiffness for this step"
        )
Beispiel #10
0
def run_dynamic_analysis_w_rayleigh_damping(dict_of_hinges,
                                            dict_of_disp_nodes,
                                            dict_of_rxn_nodes,
                                            zeta,
                                            initialOrTangent='initial',
                                            parent_dir=os.getcwd()):

    # remove any existing analysis data
    op.wipeAnalysis()

    # define a time series to add the ground motion
    op.timeSeries('Path', 2, '-dt', 0.01, '-filePath', 'BM68elc.acc',
                  '-factor', 3.0 * g)
    op.pattern('UniformExcitation', 2, 1, '-accel', 2)

    # uncomment/comment the code below to run bideirectional/unidirectional ground motion analysis
    #    op.timeSeries('Path', 3, '-dt', 0.01, '-filePath', 'BM68elc.acc', '-factor', 1.0*g)
    #    op.pattern('UniformExcitation', 3, 2, '-accel', 3)

    op.constraints('Transformation')
    op.numberer('RCM')
    op.system('UmfPack')
    #    op.test('NormDispIncr', 1e-2, 100000, 0, 0)
    op.test('EnergyIncr', 1e-4, 1e4, 0, 2)

    # available set of algorithms if the default fails
    backup_algos = {
        'Modified Newton w/ Initial Stiffness': ['ModifiedNewton', '-initial'],
        'Newton with Line Search': [
            'NewtonLineSearch', 'tol', 1e-3, 'maxIter', 1e5, 'maxEta', 10,
            'minEta', 1e-2
        ],
    }

    # define the default algorithm to be used for this analysis
    op.algorithm('KrylovNewton', 'maxDim', 3)

    # define the integrator to be used for this analysis from the set of available integrators in opensees
    alpha = 0.67
    op.integrator(
        'HHT',
        alpha)  # can be either of: ('HHT', alpha), ('Newmark', 0.5, 0.25)

    # define the type of analysis to be performed
    op.analysis('Transient')

    # obtain the modal analysis
    eigenValues = modal_response(numEigen)
    # get periods from the modal analsis
    periods = 2 * math.pi / np.sqrt(eigenValues)

    w1 = (eigenValues[0]**0.5)
    w2 = (eigenValues[0]**0.5) / 0.384
    a0 = zeta * 2 * w1 * w2 / (w1 + w2)
    a1 = zeta * 2 / (w1 + w2)

    print('\nRayleigh Damping Coefficients:')
    print(f'\talpha: {a0}')
    print(f'\tbeta : {a1}\n')

    if initialOrTangent == 'tangent':
        op.rayleigh(a0, a1, 0, 0)
    else:
        op.rayleigh(a0, 0, a1, 0)

    # setup to record analysis data
    setup_recorders(dict_of_disp_nodes, dict_of_rxn_nodes, dict_of_hinges,
                    initialOrTangent, parent_dir)

    total_run_time = 50  # seconds
    time_step = 0.01  # seconds
    total_num_of_steps = total_run_time / time_step

    # default initialization of constants to be used in the execution loop
    failed = 0
    time = 0
    algo = 'Krylov-Newton'
    pbar = tqdm(total=total_num_of_steps)

    # execution loop
    # this execution loop tries to use krylov-Newton until it works, when this fails it iterate through set of
    # algorithms until it finds a solution, if it is not able to find a solution with any algorithm, the loop ends.
    # Whereas if it is able to find a solution with any of the algorithms, it switches back to krylov-Newton
    while time <= total_run_time and failed == 0:
        failed = op.analyze(1, time_step)

        if failed:
            print(f'\n{algo} failed. Trying other algorithms...')

            for alg, algo_args in backup_algos.items():
                print(f'\nTrying {alg}...')
                op.algorithm(*algo_args)
                failed = op.analyze(1, time_step)

                if failed:
                    continue
                else:
                    algo = 'Krylov-Newton'
                    print(f'\n{alg} worked.\n\nMoving back to {algo}')
                    op.algorithm('KrylovNewton', 'maxDim', 3)
                    break

            print(''.center(100, '-'))

        pbar.update(1)
        time = op.getTime()

    op.wipe()

    # move output files to results directory
    list_of_out_files = [
        fname for fname in os.listdir() if fname.endswith('.out')
    ]
    for fname in list_of_out_files:
        shutil.move(os.path.join(os.getcwd(), fname),
                    os.path.join(parent_dir, fname))

    return periods, eigenValues
Beispiel #11
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:
    """

    opy.wipe()
    opy.model('basic', '-ndm', 2, '-ndf', 3)  # 2 dimensions, 3 dof per node

    # Establish nodes
    bot_node = 1
    top_node = 2
    opy.node(bot_node, 0., 0.)
    opy.node(top_node, 0., 0.)

    # Fix bottom node
    opy.fix(top_node, opc.FREE, opc.FIXED, opc.FIXED)
    opy.fix(bot_node, opc.FIXED, opc.FIXED, opc.FIXED)
    # Set out-of-plane DOFs to be slaved
    opy.equalDOF(1, 2, *[2, 3])

    # nodal mass (weight / g):
    opy.mass(top_node, mass, 0., 0.)

    # Define material
    bilinear_mat_tag = 1
    mat_type = "Steel01"
    mat_props = [f_yield, k_spring, r_post]
    opy.uniaxialMaterial(mat_type, bilinear_mat_tag, *mat_props)

    # Assign zero length element
    beam_tag = 1
    opy.element('zeroLength', beam_tag, bot_node, top_node, "-mat",
                bilinear_mat_tag, "-dir", 1, '-doRayleigh', 1)

    # Define the dynamic analysis
    load_tag_dynamic = 1
    pattern_tag_dynamic = 1

    values = list(-1 * motion)  # should be negative
    opy.timeSeries('Path', load_tag_dynamic, '-dt', dt, '-values', *values)
    opy.pattern('UniformExcitation', pattern_tag_dynamic, opc.X, '-accel',
                load_tag_dynamic)

    # set damping based on first eigen mode
    angular_freq2 = opy.eigen('-fullGenLapack', 1)
    if hasattr(angular_freq2, '__len__'):
        angular_freq2 = angular_freq2[0]
    angular_freq = angular_freq2**0.5
    alpha_m = 0.0
    beta_k = 2 * xi / angular_freq
    beta_k_comm = 0.0
    beta_k_init = 0.0

    opy.rayleigh(alpha_m, beta_k, beta_k_init, beta_k_comm)

    # Run the dynamic analysis

    opy.wipeAnalysis()

    opy.algorithm('Newton')
    opy.system('SparseGeneral')
    opy.numberer('RCM')
    opy.constraints('Transformation')
    opy.integrator('Newmark', 0.5, 0.25)
    opy.analysis('Transient')

    tol = 1.0e-10
    iterations = 10
    opy.test('EnergyIncr', tol, iterations, 0, 2)
    analysis_time = (len(values) - 1) * dt
    analysis_dt = 0.001
    outputs = {
        "time": [],
        "rel_disp": [],
        "rel_accel": [],
        "rel_vel": [],
        "force": []
    }

    while opy.getTime() < analysis_time:
        curr_time = opy.getTime()
        opy.analyze(1, analysis_dt)
        outputs["time"].append(curr_time)
        outputs["rel_disp"].append(opy.nodeDisp(top_node, 1))
        outputs["rel_vel"].append(opy.nodeVel(top_node, 1))
        outputs["rel_accel"].append(opy.nodeAccel(top_node, 1))
        opy.reactions()
        outputs["force"].append(
            -opy.nodeReaction(bot_node, 1))  # Negative since diff node
    opy.wipe()
    for item in outputs:
        outputs[item] = np.array(outputs[item])

    return outputs
def run_sensitivity_pushover_analysis(ctrlNode,
                                      baseNodes,
                                      dof,
                                      Dincr,
                                      max_disp,
                                      SensParam,
                                      IOflag=False):
    """
    Run pushover analysis with sensitivity
    """
    ops.wipeAnalysis()
    start_time = time.time()
    ops.loadConst("-time", 0.0)

    title("Running Displacement-Control Pushover Sensitivity Analysis ...")

    testType = "NormDispIncr"  # EnergyIncr
    tolInit = 1.0E-8
    iterInit = 10  # Set the initial Max Number of Iterations
    algorithmType = "Newton"  # Set the algorithm type

    ops.system("BandGeneral")
    ops.constraints("Transformation")
    ops.numberer("RCM")
    ops.test(testType, tolInit, iterInit)
    ops.algorithm(algorithmType)
    # Change the integration scheme to be displacement control
    #                                     node      dof  init Jd min max
    ops.integrator("DisplacementControl", ctrlNode, dof, Dincr)
    ops.analysis("Static")
    ops.sensitivityAlgorithm(
        "-computeAtEachStep"
    )  # automatically compute sensitivity at the end of each step

    if IOflag:
        print(
            f"Single Pushover: Push node {ctrlNode} to {max_disp} {LunitTXT}.\n"
        )

    # Set some parameters
    tCurrent = ops.getTime()
    currentStep = 1

    outputs = {
        "time": np.array([]),
        "disp": np.array([]),
        "force": np.array([]),
    }

    for sens in SensParam:
        outputs[f"sensLambda_{sens}"] = np.array([]),

    nodeList = []
    for node in baseNodes:
        nodeList.append(f"- ops.nodeReaction({node}, dof) ")

    nodeList = "".join(nodeList)
    currentDisp = ops.nodeDisp(ctrlNode, dof)
    ok = 0

    while ok == 0 and currentDisp < max_disp:
        ops.reactions()
        ok = ops.analyze(1)
        tCurrent = ops.getTime()
        currentDisp = ops.nodeDisp(ctrlNode, dof)

        if IOflag:
            print(
                f"Current displacement ==> {ops.nodeDisp(ctrlNode, dof):.3f} {LunitTXT}"
            )

        # if the analysis fails try initial tangent iteration
        if ok != 0:
            print("\n==> Trying relaxed convergence...")
            ops.test(testType, tolInit / 0.01, iterInit * 50)
            ok = ops.analyze(1)
            if ok == 0:
                print("==> that worked ... back to default analysis...\n")
            ops.test(testType, tolInit, iterInit)

        if ok != 0:
            print("\n==> Trying Newton with initial then current...")
            ops.test(testType, tolInit / 0.01, iterInit * 50)
            ops.algorithm("Newton", "-initialThenCurrent")
            ok = ops.analyze(1)
            if ok == 0:
                print("==> that worked ... back to default analysis...\n")
            ops.algorithm(algorithmType)
            ops.test(testType, tolInit, iterInit)

        if ok != 0:
            print("\n==> Trying ModifiedNewton with initial...")
            ops.test(testType, tolInit / 0.01, iterInit * 50)
            ops.algorithm("ModifiedNewton", "-initial")
            ok = ops.analyze(1)
            if ok == 0:
                print("==> that worked ... back to default analysis...\n")
            ops.algorithm(algorithmType)
            ops.test(testType, tolInit, iterInit)

        currentStep += 1
        tCurrent = ops.getTime()

        outputs["time"] = np.append(outputs["time"], tCurrent)
        outputs["disp"] = np.append(outputs["disp"],
                                    ops.nodeDisp(ctrlNode, dof))
        outputs["force"] = np.append(outputs["force"], eval(nodeList))

        for sens in SensParam:
            # sensLambda(patternTag, paramTag)
            outputs[f"sensLambda_{sens}"] = np.append(
                outputs[f"sensLambda_{sens}"], ops.sensLambda(1, sens))

    # Print a message to indicate if analysis completed succesfully or not
    if ok == 0:
        title("Pushover Analysis Completed Successfully!")
    else:
        title("Pushover Analysis Completed Failed!")

    print(
        f"Analysis elapsed time is {(time.time() - start_time):.3f} seconds.\n"
    )

    return outputs
Beispiel #13
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
    opy.fix(top_node.tag, o3.cc.FREE, o3.cc.FIXED, o3.cc.FIXED)
    opy.fix(bot_node.tag, o3.cc.FIXED, o3.cc.FIXED, o3.cc.FIXED)
    # Set out-of-plane DOFs to be slaved
    opy.equalDOF(top_node.tag, bot_node.tag, *[o3.cc.Y, o3.cc.ROTZ])

    # nodal mass (weight / g):
    opy.mass(top_node.tag, 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
    load_tag_dynamic = 1
    pattern_tag_dynamic = 1

    values = list(-1 * motion)  # should be negative
    opy.timeSeries('Path', load_tag_dynamic, '-dt', dt, '-values', *values)
    opy.pattern('UniformExcitation', pattern_tag_dynamic, o3.cc.X, '-accel',
                load_tag_dynamic)

    # set damping based on first eigen mode
    angular_freq2 = opy.eigen('-fullGenLapack', 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

    opy.wipeAnalysis()
    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": [],
        "rel_vel": [],
        "force": []
    }

    while opy.getTime() < analysis_time:
        curr_time = opy.getTime()
        opy.analyze(1, analysis_dt)
        outputs["time"].append(curr_time)
        outputs["rel_disp"].append(opy.nodeDisp(top_node.tag, o3.cc.X))
        outputs["rel_vel"].append(opy.nodeVel(top_node.tag, o3.cc.X))
        outputs["rel_accel"].append(opy.nodeAccel(top_node.tag, o3.cc.X))
        opy.reactions()
        outputs["force"].append(-opy.nodeReaction(
            bot_node.tag, o3.cc.X))  # Negative since diff node
    opy.wipe()
    for item in outputs:
        outputs[item] = np.array(outputs[item])

    return outputs
Beispiel #14
0
ops.algorithm('Linear')
ops.system('ProfileSPD')
ops.integrator('Newmark', 0.5, 0.25)
ops.analysis('Transient')

el_tags = ops.getEleTags()

nels = len(el_tags)

Eds = np.zeros((n_steps, nels, 6))
timeV = np.zeros(n_steps)

# transient analysis loop and collecting the data
for step in range(n_steps):
    ops.analyze(1, dt)
    timeV[step] = ops.getTime()
    # collect disp for element nodes
    for el_i, ele_tag in enumerate(el_tags):
        nd1, nd2 = ops.eleNodes(ele_tag)
        Eds[step, el_i, :] = [ops.nodeDisp(nd1)[0],
                              ops.nodeDisp(nd1)[1],
                              ops.nodeDisp(nd1)[2],
                              ops.nodeDisp(nd2)[0],
                              ops.nodeDisp(nd2)[1],
                              ops.nodeDisp(nd2)[2]]

# 1. animate the deformated shape
anim = opsv.anim_defo(Eds, timeV, sfac_a, interpFlag=1, xlim=[-1, 7],
                      ylim=[-1, 5], fig_wi_he=(30., 22.))

plt.show()
def test_sdofTransient():
    PI = 2.0 * asin(1.0)
    g = 386.4
    testOK = 0  # variable used to keep track of SUCCESS or FAILURE
    tol = 1.0e-2

    # procedure to build a linear model
    #   input args: K - desired stiffness
    #               periodStruct - desired structre period (used to compute mass)
    #               dampRatio (zeta) - desired damping ratio

    def buildModel(K, periodStruct, dampRatio):

        wn = 2.0 * PI / periodStruct
        m = K / (wn * wn)

        ops.wipe()
        ops.model('basic', '-ndm', 1, '-ndf', 1)

        ops.node(1, 0.)
        ops.node(2, 0., '-mass', m)

        ops.uniaxialMaterial('Elastic', 1, K)
        ops.element('zeroLength', 1, 1, 2, '-mat', 1, '-dir', 1)
        ops.fix(1, 1)

        # add damping using rayleigh damping on the mass term
        a0 = 2.0 * wn * dampRatio
        ops.rayleigh(a0, 0., 0., 0.)

    #
    # procedure to build a linear transient analysis
    #    input args: integrator command newmark

    def buildLinearAnalysis(gamma, beta):
        # do analysis
        ops.constraints('Plain')
        ops.numberer('Plain')
        ops.algorithm('Linear')
        ops.integrator('Newmark', gamma, beta)
        ops.system('ProfileSPD')
        ops.analysis('Transient')

    # Section 3.1 - Harmonic Vibrartion of Undamped Elastic SDOF System
    print("   - Undamped System Harmonic Exciatation (Section 3.1)")

    # harmonic force propertires
    P = 2.0
    periodForce = 5.0
    tFinal = 2.251 * periodForce

    # model properties
    periodStruct = 0.8
    K = 2.0

    # derived quantaties
    w = 2.0 * PI / periodForce
    wn = 2.0 * PI / periodStruct

    # build the model
    buildModel(K, periodStruct, 0.0)

    # add load pattern
    ops.timeSeries('Trig', 1, 0.0, 100.0 * periodForce, periodForce, '-factor',
                   P)
    ops.pattern('Plain', 1, 1)
    ops.load(2, 1.0)

    # build analysis
    buildLinearAnalysis(0.5, 1.0 / 6.0)

    # perform analysis, checking at every step
    dt = periodStruct / 1.0e4  # something small for accuracy
    tCurrent = 0.
    print(
        "\n  for 1000 time steps computing solution and checking against exact solution"
    )
    count = 0

    while tCurrent < tFinal:
        ops.analyze(1, dt)
        tCurrent = ops.getTime()
        uOpenSees = ops.nodeDisp(2, 1)
        uExact = P / K * 1.0 / (1 - (w * w) /
                                (wn * wn)) * (sin(w * tCurrent) -
                                              (w / wn) * sin(wn * tCurrent))

        if abs(uExact - uOpenSees) > tol:
            testOK = -1
            print("failed undamped harmonic> ", abs(uExact - uOpenSees),
                  "> tol at time tCurrent")
            tCurrent = tFinal

    print("\n  example results for last step at ", tCurrent, " (sec):")
    print('         OpenSees: ', uOpenSees, '     Exact:', uExact)

    if abs(uExact - uOpenSees) > tol:
        testOK = -1
        print("failed  undamped harmonic>", abs(uExact - uOpenSees), " ", tol)

    # Section 3.2 - Harmonic Vibrartion of Damped Elastic SDOF System
    print("\n\n   - Damped System Harmonic Excitation (Section 3.2)")

    dampRatio = 0.05

    # build the model
    buildModel(K, periodStruct, dampRatio)

    # add load pattern
    ops.timeSeries('Trig', 1, 0.0, 100.0 * periodForce, periodForce, '-factor',
                   P)
    ops.pattern('Plain', 1, 1)
    ops.load(2, 1.0)

    # build analysis
    buildLinearAnalysis(0.5, 1.0 / 6.0)

    # some variables needed in exact computation
    wd = wn * sqrt(1 - dampRatio * dampRatio)
    wwn2 = (w * w) / (wn * wn)
    det = (1.0 - wwn2) * (1 - wwn2) + 4.0 * dampRatio * dampRatio * wwn2
    ust = P / K

    C = ust / det * (1. - wwn2)
    D = ust / det * (-2. * dampRatio * w / wn)
    A = -D
    B = ust / det * (1.0 / wd) * ((-2. * dampRatio * w / wn) - w *
                                  (1.0 - wwn2))

    t = 0.0
    while t < tFinal:
        ops.analyze(1, dt)
        t = ops.getTime()
        uOpenSees = ops.nodeDisp(2, 1)
        uExact = exp(-dampRatio * wn * t) * (A * cos(wd * t) + B * sin(
            wd * t)) + C * sin(w * t) + D * cos(w * t)
        if abs(uExact - uOpenSees) > tol:
            testOk = -1
            print("failed  damped harmonic>", abs(uExact - uOpenSees), "> ",
                  tol, "at time ", t)
            t = tFinal

    print("\n  example results for last step at ", t, " (sec):")
    print('         OpenSees: ', uOpenSees, '     Exact:', uExact)

    if abs(uExact - uOpenSees) > tol:
        testOK = -1
        print("failed  damped harmonic>", abs(uExact - uOpenSees), " ", tol)

    # Section 6.4 - Earthquake Response of Linear System

    print("\n\n   - Earthquake Response (Section 6.4)\n")

    tol = 3.0e-2
    results = [2.67, 5.97, 7.47, 9.91, 7.47, 5.37]

    # read earthquake record, setting dt and nPts variables with data in te file elCentro.at2
    dt, nPts = ReadRecord('elCentro.at2', 'elCentro.dat')

    # print table header
    print('{:>15}{:>15}{:>15}{:>15}'.format('Period', 'dampRatio', 'OpenSees',
                                            'Exact'))

    # perform analysis for bunch of periods and damping ratio's
    counter = 0
    for (period, dampRatio) in [(0.5, 0.02), (1.0, 0.02), (2.0, 0.02),
                                (2.0, 0.0), (2.0, 0.02), (2.0, 0.05)]:
        # build the model
        buildModel(K, period, dampRatio)

        # add load pattern
        ops.timeSeries('Path', 1, '-filePath', 'elCentro.dat', '-dt', dt,
                       '-factor', g)
        ops.pattern('UniformExcitation', 1, 1, '-accel', 1)

        # build analysis
        buildLinearAnalysis(0.5, 1.0 / 6.0)

        maxU = 0.0
        for i in range(nPts):
            ops.analyze(1, dt)
            u = abs(ops.nodeDisp(2, 1))
            if u > maxU:
                maxU = u

        uExact = results[counter]
        print('{:>15.2f}{:>15.2f}{:>15.2f}{:>15.2f}'.format(
            period, dampRatio, maxU, uExact))

        if abs(maxU - uExact) > tol:
            testOK = -1
            print('failed earthquake record period: ', period, ' ', dampRatio,
                  ': ', dampRatio, ': ', maxU, ' ', uExact, ' ',
                  abs(uExact - maxU), ' ', tol)

        counter += 1

    assert testOK == 0
Beispiel #16
0
def run_nlth_analysis_on_sdof_ops_py(capacity_curve, gmr, damping,
                                     degradation):

    cap_df = pd.DataFrame(capacity_curve)
    if any(cap_df.duplicated()):
        warnings.warn(
            "Warning: Duplicated pairs have been found in capacity curve!")

    ops.wipe()
    ops.model('basic', '-ndm', 1, '-ndf', 1)

    d_cap = capacity_curve[:, 0]
    f_cap = capacity_curve[:, 1] * 9.81

    f_vec = np.zeros([5, 1])
    d_vec = np.zeros([5, 1])
    if len(f_cap) == 3:
        #bilinear curve
        f_vec[1] = f_cap[1]
        f_vec[4] = f_cap[-1]

        d_vec[1] = d_cap[1]
        d_vec[4] = d_cap[-1]

        d_vec[2] = d_vec[1] + (d_vec[4] - d_vec[1]) / 3
        d_vec[3] = d_vec[1] + 2 * ((d_vec[4] - d_vec[1]) / 3)

        f_vec[2] = np.interp(d_vec[2], d_cap, f_cap)
        f_vec[3] = np.interp(d_vec[3], d_cap, f_cap)

    elif len(f_cap) == 4:

        f_vec[1] = f_cap[1]
        f_vec[4] = f_cap[-1]

        d_vec[1] = d_cap[1]
        d_vec[4] = d_cap[-1]

        f_vec[2] = f_cap[2]
        d_vec[2] = d_cap[2]

        d_vec[3] = np.mean([d_vec[2], d_vec[-1]])
        f_vec[3] = np.interp(d_vec[3], d_cap, f_cap)

    elif len(f_cap) == 5:
        f_vec[1] = f_cap[1]
        f_vec[4] = f_cap[-1]

        d_vec[1] = d_cap[1]
        d_vec[4] = d_cap[-1]

        f_vec[2] = f_cap[2]
        d_vec[2] = d_cap[2]

        f_vec[3] = f_cap[3]
        d_vec[3] = d_cap[3]

    matTag_pinching = 10
    if degradation == True:
        matargs = [
            f_vec[1, 0], d_vec[1, 0], f_vec[2, 0], d_vec[2, 0], f_vec[3, 0],
            d_vec[3, 0], f_vec[4, 0], d_vec[4, 0], -1 * f_vec[1, 0],
            -1 * d_vec[1, 0], -1 * f_vec[2, 0], -1 * d_vec[2, 0],
            -1 * f_vec[3, 0], -1 * d_vec[3, 0], -1 * f_vec[4, 0],
            -1 * d_vec[4, 0], 0.5, 0.25, 0.05, 0.5, 0.25, 0.05, 0, 0.1, 0, 0,
            0.2, 0, 0.1, 0, 0, 0.2, 0, 0.4, 0, 0.4, 0.9, 10, 'energy'
        ]

    else:
        matargs = [
            f_vec[1, 0], d_vec[1, 0], f_vec[2, 0], d_vec[2, 0], f_vec[3, 0],
            d_vec[3, 0], f_vec[4, 0], d_vec[4, 0], -1 * f_vec[1, 0],
            -1 * d_vec[1, 0], -1 * f_vec[2, 0], -1 * d_vec[2, 0],
            -1 * f_vec[3, 0], -1 * d_vec[3, 0], -1 * f_vec[4, 0],
            -1 * d_vec[4, 0], 0.5, 0.25, 0.05, 0.5, 0.25, 0.05, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 'energy'
        ]

    ops.uniaxialMaterial('Pinching4', matTag_pinching, *matargs)
    matTag_minmax = int(matTag_pinching / 10)
    ops.uniaxialMaterial('MinMax', matTag_minmax, matTag_pinching, '-min',
                         -1 * d_vec[4, 0], '-max', d_vec[4, 0])

    mx = 1
    kx = f_vec[1, 0] / d_vec[1, 0]
    omega = np.sqrt(kx / mx)
    dt = gmr[1, 0] - gmr[0, 0]

    ops.node(1, 0)
    ops.node(2, 0, '-mass', float(mx))
    ops.fix(1, 1)
    ops.element('zeroLength', 1, 1, 2, "-mat", matTag_minmax, "-dir", 1,
                '-doRayleigh', 1)

    gmr_values = gmr[:, 1] * 9.81
    gmr_times = gmr[:, 0]
    ops.timeSeries('Path', 2, '-values', *gmr_values, '-time', *gmr_times)
    ops.pattern('UniformExcitation', 2, 1, '-accel', 2)

    ops.constraints('Plain')
    ops.numberer('RCM')
    ops.test('NormDispIncr', 1e-6, 50)
    ops.algorithm('Newton')
    ops.system('BandGeneral')
    ops.integrator('Newmark', 0.5, 0.25)
    ops.analysis('Transient')

    t_final = gmr[-1, 0]
    t_current = ops.getTime()
    ok = 0
    #betaKinit=2*damping/omega
    alphaM = 2 * damping * omega
    time = [t_current]
    disps = [0.0]
    accels = [0.0]
    #ops.rayleigh(0,0,betaKinit,0)
    ops.rayleigh(alphaM, 0, 0, 0)  # mass proportional damping
    while ok == 0 and t_current < t_final:
        ok = ops.analyze(1, dt)
        if ok != 0:
            print(
                "regular newton failed ... lets try an initail stiffness for this step"
            )
            ops.test('NormDispIncr', 1.0e-6, 100, 0)
            ops.algorithm('ModifiedNewton', '-initial')
            ok = ops.analyze(1, dt)
            if ok != 0:
                print("reducing dt by 10")
                ndt = dt / 10
                ok = ops.analyze(10, ndt)
            if ok == 0:
                print("that worked ... back to regular settings")
                ops.test('NormDispIncr', 1e-6, 50)
                ops.test('NormDispIncr', 1e-6, 50)
        t_current = ops.getTime()
        time.append(t_current)
        disps.append(ops.nodeDisp(2, 1))
        accels.append(ops.nodeAccel(2, 1))

    if ok != 0:
        print('NLTHA did not converge!')

    return time, disps, accels