Smart Analysis

API :: opstool.anlys.SmartAnalyze

[1]:
import openseespy.opensees as ops
import opstool as opst

Reinforced Concrete Frame Pushover Analysis

Moddling

[2]:
def model():
    ops.wipe()
    ops.model("basic", "-ndm", 3, "-ndf", 6)
    width = 360.0
    height = 144.0
    ops.node(1, 0.0, 0.0, 0.0)
    ops.node(2, width, 0.0, 0.0)
    ops.node(3, 0.0, 0.0, height)
    ops.node(4, width, 0.0, height)
    ops.fix(1, 1, 1, 1, 1, 1, 1)
    ops.fix(2, 1, 1, 1, 1, 1, 1)

    ops.uniaxialMaterial("Concrete01", 1, -6.0, -0.004, -5.0, -0.014)
    ops.uniaxialMaterial("Concrete01", 2, -5.0, -0.002, 0.0, -0.006)

    fy = 60.0
    E = 30000.0
    ops.uniaxialMaterial("Steel01", 3, fy, E, 0.01)

    # Define cross-section for nonlinear columns
    # ------------------------------------------
    colWidth = 15
    colDepth = 24
    cover = 1.5
    As = 0.60  # area of no. 7 bars
    # some variables derived from the parameters
    y1 = colDepth / 2.0
    z1 = colWidth / 2.0

    ops.section("Fiber", 1, "-GJ", 1000000)
    ops.patch("rect", 1, 10, 10, cover - y1, cover - z1, y1 - cover, z1 - cover)
    # Create the concrete cover fibers (top, bottom, left, right)
    ops.patch("rect", 2, 11, 1, -y1, z1 - cover, y1, z1)
    ops.patch("rect", 2, 11, 1, -y1, -z1, y1, cover - z1)
    ops.patch("rect", 2, 1, 10, -y1, cover - z1, cover - y1, z1 - cover)
    ops.patch("rect", 2, 1, 10, y1 - cover, cover - z1, y1, z1 - cover)
    # Create the reinforcing fibers (left, middle, right)
    ops.layer("straight", 3, 5, As, y1 - cover, z1 - cover, y1 - cover, cover - z1)
    ops.layer("straight", 3, 2, As, 0.0, z1 - cover, 0.0, cover - z1)
    ops.layer("straight", 3, 5, As, cover - y1, z1 - cover, cover - y1, cover - z1)

    # Define column elements
    # ----------------------
    ops.geomTransf("PDelta", 1, -1, 0, 0)
    # Number of integration points along length of element
    np = 5
    # Lobatto integratoin
    ops.beamIntegration("Lobatto", 1, 1, np)
    eleType = "forceBeamColumn"
    ops.element(eleType, 1, 1, 3, 1, 1)
    ops.element(eleType, 2, 2, 4, 1, 1)

    # Define beam elment
    # -----------------------------
    ops.geomTransf("Linear", 2, 0.0, 0.0, 1.0)
    ops.element("elasticBeamColumn", 3, 3, 4, 360.0, 4030.0, 2015.0, 10000, 8640.0, 8640.0, 2)
[3]:
model()
opst.vis.pyvista.set_plot_props(notebook=True)  # you should not use
fig = opst.vis.pyvista.plot_model(show_local_axes=True)
fig.show(jupyter_backend="jupyterlab")
# fig.show()
OPSTOOL ::  Model data has been saved to _OPSTOOL_ODB/ModelData-None.nc!
../../_images/src_analysis_smart_analysis_6_1.png

Gravity analysis

[4]:
def gravity_analysis():
    #  a parameter for the axial load
    P = 180.0  # 10% of axial capacity of columns

    # Create a Plain load pattern with a Linear TimeSeries
    ops.timeSeries("Linear", 1)
    ops.pattern("Plain", 1, 1)

    # Create nodal loads at nodes 3 & 4
    #    nd  FX,  FY, MZ
    ops.load(3, 0.0, 0.0, -P, 0.0, 0.0, 0.0)
    ops.load(4, 0.0, 0.0, -P, 0.0, 0.0, 0.0)

    # Start of analysis generation
    # ------------------------------
    ops.system("BandGeneral")
    ops.constraints("Transformation")
    ops.numberer("RCM")
    ops.test("NormDispIncr", 1.0e-12, 10, 3)
    ops.algorithm("Newton")
    ops.integrator("LoadControl", 0.1)
    ops.analysis("Static")
    ops.analyze(10)

Pushover analysis

[5]:
def pushover_load():
    ops.loadConst("-time", 0.0)
    # Define lateral loads
    # --------------------
    # Set some parameters
    H = 10.0  # Reference lateral load
    # Set lateral load pattern with a Linear TimeSeries
    ops.pattern("Plain", 2, 1)
    ops.load(3, H, 0.0, 0.0, 0.0, 0.0, 0.0)
    ops.load(4, H, 0.0, 0.0, 0.0, 0.0, 0.0)

Smart Analysis

No fixed number of analyses

[6]:
# Set some parameters
dU = 0.1  # Displacement increment
maxU = 45.0  # Max displacement
ok = 0
currentDisp = 0

model()
gravity_analysis()
pushover_load()

If tryAddTestTimes is turned on, it will first try to increase the number of test;

if tryAlterAlgoTypes is turned on, it will continue to try to change the iterative algorithm specified by the user;

Then, if it does not converge, it will try to automatically split the step size until the specified minimum step size minStep is reached.

If it still does not converge, the analysis is considered to have failed.

We can see that opensees throws out non-convergence information, but converges successfully when the number of iterations increases to 50.

[7]:
ODB = opst.post.CreateODB(odb_tag=1)  # Create an ODB object to store results

analysis = opst.anlys.SmartAnalyze(
    "Static",
    tryAddTestTimes=True,  # add test times to the analysis
    testIterTimesMore=[50, 100],
    tryAlterAlgoTypes=True,  # try different algorithms
    algoTypes=[40, 10, 20, 30],  # algorithm types to try
    minStep=1e-6,  # minimum step size for substepping
    debugMode=True,  # False for progress bar, True for debug info
    printPer=100,  # print every 100 steps
)

while ok == 0 and currentDisp < maxU:
    #  Perform the analysis one step at a time
    analysis.StaticAnalyze(node=3, dof=1, seg=dU)

    ODB.fetch_response_step()  # Fetch the response for the current step

    currentDisp = ops.nodeDisp(3, 1)

analysis.close()  # Close the analysis object, when analysis steps are unknown

ODB.save_response()

print("🎉 Analysis Completed Successfully 🎉")
>>> ▶️ SmartAnalyze: Setting algorithm to  KrylovNewton ...
>>> ✅ SmartAnalyze: progress 100 steps. Time consumption: 0.279 s.
>>> ✅ SmartAnalyze: progress 200 steps. Time consumption: 0.534 s.
WARNING: CTestEnergyIncr::test() - failed to converge
after: 10 iterations
 current EnergyIncr: 1.84355e-05 (max: 1e-10)   Norm deltaX: 0.000384464, Norm deltaR: 0.194756
AcceleratedNewton::solveCurrentStep() -The ConvergenceTest object failed in test()
StaticAnalysis::analyze() - the Algorithm failed at step: 0 with domain at load factor 2.89482
OpenSees > analyze failed, returned: -3 error flag
>>> ▶️ SmartAnalyze: Adding test times to 50.
>>> ✅ SmartAnalyze: progress 300 steps. Time consumption: 0.824 s.
>>> ✅ SmartAnalyze: progress 400 steps. Time consumption: 1.116 s.
WARNING: CTestEnergyIncr::test() - failed to converge
after: 10 iterations
 current EnergyIncr: 2.70125e-05 (max: 1e-10)   Norm deltaX: 0.000819596, Norm deltaR: 0.253889
AcceleratedNewton::solveCurrentStep() -The ConvergenceTest object failed in test()
StaticAnalysis::analyze() - the Algorithm failed at step: 0 with domain at load factor 1.64367
OpenSees > analyze failed, returned: -3 error flag
>>> ▶️ SmartAnalyze: Adding test times to 50.
OPSTOOL ::  All responses data with _odb_tag = 1 saved in _OPSTOOL_ODB/RespStepData-1.nc!
🎉 Analysis Completed Successfully 🎉

Analysis of the fixed number of steps

[8]:
# Set some parameters
dU = 0.1  # Displacement increment
maxU = 45.0  # Max displacement

model()
gravity_analysis()
pushover_load()
[9]:
ODB = opst.post.CreateODB(odb_tag=2)  # Create ODB object

analysis = opst.anlys.SmartAnalyze(
    "Static",
    tryAddTestTimes=True,  # add test times to the analysis
    testIterTimesMore=[50, 100],
    tryAlterAlgoTypes=True,  # try different algorithms
    algoTypes=[40, 10, 20, 30],  # algorithm types to try
    minStep=1e-6,  # minimum step size for substepping
    debugMode=False,  # False for progress bar, True for debug info
)
segs = analysis.static_split([maxU], dU)

for seg in segs:
    analysis.StaticAnalyze(node=3, dof=1, seg=seg)
    ODB.fetch_response_step()  # fetch response for the current step
ODB.save_response()  # save response to ODB
🎉 SmartAnalyze: Successfully finished! Time consumption: 1.765 s. 🎉
OPSTOOL ::  All responses data with _odb_tag = 2 saved in _OPSTOOL_ODB/RespStepData-2.nc!

image22.png

Post-processing

[10]:
node_resp = opst.post.get_nodal_responses(odb_tag=1)
print(node_resp)
OPSTOOL ::  Loading all response data from _OPSTOOL_ODB/RespStepData-1.nc ...
<xarray.Dataset> Size: 538kB
Dimensions:             (time: 451, nodeTags: 4, DOFs: 6)
Coordinates:
  * nodeTags            (nodeTags) int32 16B 1 2 3 4
  * DOFs                (DOFs) <U2 48B 'UX' 'UY' 'UZ' 'RX' 'RY' 'RZ'
  * time                (time) float64 4kB 0.0 0.6715 1.179 ... 1.536 1.519
Data variables:
    disp                (time, nodeTags, DOFs) float64 87kB 0.0 ... 6.499e-15
    vel                 (time, nodeTags, DOFs) float64 87kB 0.0 0.0 ... 0.0 0.0
    accel               (time, nodeTags, DOFs) float64 87kB 0.0 0.0 ... 0.0 0.0
    reaction            (time, nodeTags, DOFs) float64 87kB 1.08e-15 ... -4.9...
    reactionIncInertia  (time, nodeTags, DOFs) float64 87kB 1.08e-15 ... -4.9...
    rayleighForces      (time, nodeTags, DOFs) float64 87kB 0.0 0.0 ... 0.0 0.0
    pressure            (time, nodeTags) float64 14kB 0.0 0.0 0.0 ... 0.0 0.0
Attributes:
    UX:       Displacement in X direction
    UY:       Displacement in Y direction
    UZ:       Displacement in Z direction
    RX:       Rotation about X axis
    RY:       Rotation about Y axis
    RZ:       Rotation about Z axis
[11]:
node3disp = node_resp["disp"].sel(nodeTags=3, DOFs="UX")
reaction = node_resp["reaction"].sel(DOFs="UX").sum(dim="nodeTags")
[12]:
import matplotlib.pyplot as plt

plt.plot(node3disp, -reaction, c="b")
plt.xlabel("Displacement")
plt.ylabel("Force")
plt.show()
../../_images/src_analysis_smart_analysis_24_0.png