Skip to content

wcfrobert/wthisj

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

64 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation


logo

Punching Shear Calculation in Python

demo

Introduction

wthisj (what the heck is j?) is a python program that that performs punching shear calculations per ACI 318 and ACI 421.1R. Notable Features include:

  • Supports all column conditions (interior, edge, and corner)
  • Easily add stud rails (i.e. polygonal shear perimeters)
  • Easily add openings
  • Interactive visualization of shear stresses
  • Advanced features like principal axes rotation, consideration of moment due to centroid eccentricity, non-uniform depth shear perimeters, fast numerical approximation of $J_{cx}$ and $J_{cy}$ for any arbitrary sections.

Quick Start

Here's a quick start script found in main.py:

import wthisj

# initialize a critical shear section
column1 = wthisj.PunchingShearSection(col_width = 24,
                                      col_depth = 24,
                                      slab_avg_depth = 12,
                                      condition = "NW",
                                      overhang_x = 12,
                                      overhang_y = 12,
                                      studrail_length = 0)

# add as many openings as you want
column1.add_opening(xo=40, yo=-10, width=18, depth=20)

# preview geometry
column1.preview()

# calculate punching shear stress
results = column1.solve(Vz = -100,
                        Mx = 400,
                        My = 400,
                        consider_ecc=False,
                        auto_rotate=True, 
                        verbose=True)

# plot results (matplotlib)
column1.plot_results()

# plot results (plotly)
column1.plot_results_3D()
  • There are 9 possible conditions (1 interior, 4 edge, 4 corner), each represented using the cardinal directions on a compass (NW, N, NE, W, I, E, SW, S, SE), except "I" which stands for interior. For example, "NW" represents a top-left corner column.
  • Units should be in (KIPS, IN).
  • Sign convention for the applied forces should follows the right-hand rule. Note Vz should be negative unless you are checking uplift.
fig

Running the main quick start script will produce the following:

  • PunchingShearSection.preview() generates a preview of the critical shear perimeter, openings, columns, slab edge, as well as a tabulation of its geometric properties.
fig
  • PunchingShearSection.solve() runs an analysis and returns a result dataframe. The critical shear section is discretized numerically into many fibers, each row in the data frame represents one fiber. Key calculation results will be printed to console if verbose is set to True.
fig
fig
  • PunchingShearSection.plot_results() generates a shear stress contour plot + a concise calculation summary.
fig
  • PunchingShearSection.plot_results_3D() generates a interactive 3D visualization in html format - viewable in most web browsers.
fig

Installation

Option 1: Anaconda Python

Run main.py using your base Anaconda environment.

  1. Download Anaconda python
  2. Download this package (click the green "Code" button above or download the latest release)
  3. Open and run "main.py" in Anaconda's Spyder IDE.

The following packages are used:

  • Numpy
  • Pandas
  • Matplotlib
  • Plotly

Option 2: Regular Python

  1. Download this project to a folder of your choosing. You can use git, or download one of the tagged releases.
    git clone https://github.com/wcfrobert/wthisj.git
    
  2. Change directory into where you downloaded wthisj
    cd wthisj
    
  3. Create virtual environment
    py -m venv venv
    
  4. Activate virtual environment
    venv\Scripts\activate
    
  5. Install requirements
    pip install -r requirements.txt
    
  6. run wthisj
    py main.py
    

Pip install is also available.

pip install wthisj

Examples

Example #1 - Interior Condition

Here's a 24" x 24" interior column supporting a slab with 12" effective depth, subjected to 100 kips of shear, and 400 kip.in of unbalanced moment in the X-axis.

  • Unit should be in (kip, in)
  • User must provide the slab depth (NOT slab thickness) - with cover already subtracted.
  • Sign convention for the applied forces should follows the right-hand rule. Vz is almost always negative unless you are considering uplift
import wthisj
shear_section = wthisj.PunchingShearSection(col_width=24, 
                                            col_depth=24, 
                                            slab_avg_depth=12, 
                                            condition="I")
results = shear_section.solve(Vz = -100, Mx = 400, My = 0)
fig1 = shear_section.preview()
fig2 = shear_section.plot_results()
fig3 = shear_section.plot_results_3D()
fig

Example #2 - Edge Condition

Here's a 24" x 24" column at the edge of the slab with a 6" overhang beyond the column face. It is subjected to 100 kips of shear, and 400 kip.in of Y-axis unbalanced moment.

  • There are 9 possible conditions (1 interior, 4 edge, 4 corner), each represented using the cardinal directions on a compass (NW, N, NE, W, I, E, SW, S, SE) - except "I" which stands for interior. In this example, we use "W" to indicate an edge condition with the edge on the left. You can also specify edge condition with: "N", "W", "S", "E".
  • Again, pay attention to the signs and follow right-hand rule. In this case, the unbalanced moment exerts additional stress on the right side of the column.
import wthisj
shear_section = wthisj.PunchingShearSection(col_width=24, 
                                            col_depth=24, 
                                            slab_avg_depth=12, 
                                            condition="W")
results = shear_section.solve(Vz = -100, Mx = 0, My = 400)
fig = shear_section.plot_results()
fig

Example #3 - Corner Condition w/ Principal Axes Rotation

Here's a 24" x 24" column at a corner condition with 6" overhang beyond the column face. It is subjected to 100 kips of shear, and 400 kip.in of unbalanced moment in both the X and Y-axis.

  • You can specify corner condition with: "NW", "NE", "SW", "SE".
  • If you wish to apply moment one axis at a time, just specify zero values for either Mx and My .
  • Tell wthisj to rotate to principal axes by setting auto_rotate to True (which is the default).
import wthisj
shear_section = wthisj.PunchingShearSection(col_width=24, 
                                            col_depth=24, 
                                            slab_avg_depth=12, 
                                            condition="NW",
                                            overhang_x=6,
                                            overhang_y=6)
results = shear_section.solve(Vz = -100, Mx = 400, My = 400, auto_rotate = True)
fig = shear_section.plot_results()
fig
**Example #4 - Openings**

Let's repeat example #1, but this time, add an 12" x 24" opening 48 inches away to the right (measured from face of column to edge of opening).

  • The user can add as many openings as they want with PunchingShearSection.add_opening(). Note that the offset (x, y) is measured from the center of the column (always at 0,0) to the bottom left corner of the opening.
import wthisj
shear_section = wthisj.PunchingShearSection(col_width=24, 
                                            col_depth=24, 
                                            slab_avg_depth=12, 
                                            condition="I")
shear_section.add_opening(xo=60, yo =0, width=12, depth=24)
results = shear_section.solve(Vz = -100, Mx = 400, My = 0)
fig = shear_section.preview()
fig = shear_section.plot_results()
fig
fig

Example #5 - Polygonal Shear Perimeter (Stud Rails)

Repeat example #2, but this time, add 48" long stud rails on all three sides to create a polygonal shear section.

  • Creating polygonal shear perimeters is easy. Simply specify a non-zero value for thestudrail_length argument when initializing a shear section object.
  • Shear stress will be calculated for this polygonal perimeter. The internal failure plane that crosses the stud rail is not evaluated.
  • wthisj calculates gamma_vx and gamma_vy based on the recommendations provided in ACI 421.1R, the user may also opt to specify it themselves.
import wthisj
shear_section = wthisj.PunchingShearSection(col_width=24, 
                                            col_depth=24, 
                                            slab_avg_depth=12, 
                                            condition="W",
                                           	studrail_length = 48)
results = shear_section.solve(Vz = -100, Mx = 0, My = 400, 
                              gamma_vx = "auto", 
                              gamma_vy = 0.4,
                              consider_ecc=False)
fig = shear_section.plot_results()
fig

Example #6 - Centroid Offset Moment Adjustment

Repeat example #2, but this time, disable moment adjustment due to eccentricity. This adjustment is explained in detail the theoretical background and in ACI 421.1R. In short, the shear section centroid may be misaligned with the column centroid which can generate addition moment.

In some case, the user may be specifying the adjusted moment already, in which case please set consider_ecc to False

  • This feature is enabled by default.
  • This additional moment can be huge when stud rails are provided...
import wthisj
shear_section = wthisj.PunchingShearSection(col_width=24, 
                                            col_depth=24, 
                                            slab_avg_depth=12, 
                                            condition="W")
results = shear_section.solve(Vz = -100, Mx = 0, My = 1263, consider_ecc = False)
fig = shear_section.plot_results()
fig

Example #7 - Fully Custom Shear Perimeter

You can also draw a highly customized shear perimeter. Here's a triangular one where one of the sides have a different depth than the other two.

  • Draw your own perimeter by turning auto_generate_perimeter to False, and using the .add_perimeter() method.
  • When analyzing a custom shear perimeter, the user must specify gamma_v themselves
import wthisj
shear_section = wthisj.PunchingShearSection(col_width=0, 
                                            col_depth=0, 
                                            slab_avg_depth=12, 
                                            condition="I",
                                           	auto_generate_perimeter = False)

shear_section.add_perimeter(start=(-18,-18), end=(-18,18), depth=12)
shear_section.add_perimeter(start=(-18,18), end=(18,0), depth=12)
shear_section.add_perimeter(start=(18,0), end=(-18,-18), depth=16)

results = shear_section.solve(Vz = -100, Mx = 400, My = 0, gamma_vx = 0.4, gamma_vy = 0.4)
fig2 = shear_section.plot_results()
fig

Theoretical Background

Link to theoretical background

Documentation

Here are all the public methods available to the user:

  • PunchingShearSection(col_width, col_height, slab_avg_depth, condition, overhang_x=0, overhang_y=0, studrail_length=0, auto_generate_perimeter=True, PATCH_SIZE=0.5)

  • PunchingShearSection.add_perimeter(start, end, depth)

  • PunchingShearSection.add_opening(xo, yo, width, depth)

  • PunchingShearSection.rotate(angle)

  • PunchingShearSection.solve(Vz, Mx, My, gamma_vx="auto", gamma_vy="auto", consider_ecc=False, auto_rotate=True, verbose=True)

  • PunchingShearSection.preview()

  • PunchingShearSection.plot_results(colormap="jet", cmin="auto", cmax="auto")

  • PunchingShearSection.plot_results_3D(colormap="jet", cmin="auto", cmax="auto", scale=10)

If you need guidance at any time, use the help() command to access method docstrings. For example, here is the output for help(wthisj.PunchingShearSection.add_opening)

fig

1.0 Define Shear Perimeter

PunchingShearSection(col_width, col_depth, slab_avg_depth, condition, overhang_x=0, overhang_y=0, studrail_length=0, auto_generate_perimeter=True, PATCH_SIZE=0.5) - Instantiate and return a PunchingShearSection object.

  • col_width: float

    • Column dimension along x-axis
  • col_depth: float

    • Column dimension along y-axis
  • slab_avg_depth: float

    • Slab depth from outermost compression fiber to outermost tension rebar. Use the average depth of two orthogonal slab directions.
  • condition: string

    • String used to specify interior, edge, or corner column condition. Valid inputs look like the cardinal directions on a compass.
    • "N", "S", "W", "E", "NW", "NE", "SW", "SE"
    • For example, "NW" is a corner condition with slab edge to the top left. "W" is an edge condition with slab edge on the left.
  • overhang_x: float (OPTIONAL)

    • Default = 0
    • Slab overhang dimension along the X-axis beyond column face.
    • Based on CRSI recommendations, overhang exceeding b/2 + d are treated as interior condition (where d is the slab depth, and b is the column dimension perpendicular to the edge).
  • overhang_y: float (OPTIONAL)

    • Default = 0
    • Slab overhang dimension along the Y-axis beyond column face.
    • Based on CRSI recommendations, overhang exceeding b/2 + d are treated as interior condition (where d is the slab depth, and b is the column dimension perpendicular to the edge).
  • studrail_length: float (OPTIONAL)

    • Default = 0
    • Stud rails may be added to expand the punching shear perimeter into a polygonal pattern. The exact geometry is described in ACI 318-19. We will assume stud rails always exist at the column corners, and that a minimum of two rails exist on each face. Parameters like spacing and number of stud rails are irrelevant here because all we care about is the perimeter geometry (wthisj does NOT calculate any shear capacities!)
  • auto_generate_perimeter: bool (OPTIONAL)

    • Default = True
    • Automatically generate the punching shear perimeter based on the arguments entered by the user above. Alternatively, the user may set this parameter to False, then draw each perimeter line manually using the .add_perimeter() method.
  • PATCH_SIZE: float (OPTIONAL)

    • Default = 0.5
    • By default, the shear perimeter is numerically discretized into 0.5" fibers. You can specify a smaller fiber size to improve accuracy. 0.5" is small enough for most cases.
# define a top-left corner column (24"x24") supporting a slab with rebar depth of 12". 
# Add 36" long stud rails on the inner faces. Slab overhang is 12" in both directions.
column1 = wthisj.PunchingShearSection(col_width = 24,
                                      col_deptht = 24,
                                      slab_avg_depth = 12,
                                      condition = "NW",
                                      overhang_x = 12,
                                      overhang_y = 12,
                                      studrail_length = 36)

Note

wthisj numerically approximates $I_x$ and $I_y$ by discretizing the perimeter into tiny 0.5 inch fibers, each fiber has an infinitesimal area (dA) which is then summed to approximate the moment of inertia integrals. The default fiber size is usually accurate enough; however, users may opt to reduce the fiber size further by changing the PATCH_SIZE argument when initializing a PunchingShearSection() object.

Note

If a PunchingShearSection() object has a large enough overhang_x or overhang_y, it will be automatically converted to an interior condition. The limit is explained in the theoretical background section.

2.0 Add Openings

PunchingShearSection.add_opening(xo, yo, width, depth) - Add a rectangular opening nearby. The column center is always located at (0,0). Specify bottom left corner of opening as well as opening size. You can add an arbitrary number of openings. A warning will be printed to console if the openings is further than 4h away because the specified opening can be ignored. This method modifies the PunchingShearSection object internally and does not return anything.

  • xo: float
    • x-offset from column center (0,0) to the bottom left corner of opening
  • yo: float
    • y-offset from column center (0,0) to the bottom left corner of opening
  • width: float
    • opening width
  • depth: float
    • opening height
# add a 18" x 20" opening with bottom-left corner located 80" left and 10" below the column center.
column1.add_opening(xo=80, yo=-10, width=18, depth=20)

3.0 Run Analysis

PunchingShearSection.solve(Vz, Mx, My, gamma_vx="auto", gamma_vy="auto", consider_ecc=True, auto_rotate=True, verbose=True) - Start analysis routine. Returns a dataframe where each row is a fiber within the shear perimeter, and the columns are the intermediate calculation results.

  • Vz: float
    • Applied shear force in KIPS. Should always be NEGATIVE unless you are checking uplift
  • Mx: float
    • Applied moment about the X-axis in KIP.IN.
  • My: float
    • Applied moment about the Y-axis in KIP.IN.
  • gamma_vx: float or string (OPTIONAL)
    • Percentage of X moment transferred to the column via shear. wthisj will automatically calculate this in accordance with ACI 421.1R. Or the user may enter a specific value of gamma_vx (e.g. 0.4)
  • gamma_vy: float or string (OPTIONAL)
    • Percentage of Y moment transferred to the column via shear. wthisj will automatically calculate this in accordance with ACI 421.1R. Or the user may enter a specific value of gamma_vy (e.g. 0.4)
  • consider_ecc: bool (OPTIONAL)
    • Whether or not to consider additional moment due to eccentricity between the column centroid and perimeter centroid. Defaults to True. Refer to the theory section for more info.
  • auto_rotate: bool (OPTIONAL)
    • Whether or not to auto-rotate geometry if it is not in principal orientation. Please note equilibrium is only maintained for sections in its principal orientation. Superposition of stress due to bi-axial moment is only valid when Ixy = 0. Refer to the theoretical background section for more info.
  • verbose: bool (OPTIONAL)
    • Whether or not to printout calculation result and other helpful messages. Default = True.
# Check punching shear stress for a perimeter subjected to 100 kips of shear, 400 kips.in of moment in both directions.
# Not rotating the section to principal orientation.
# Not considering additional moment due to eccentricity between column and perimeter centroid.
results = column1.solve(Vz = -100,
                        Mx = 400,
                        My = 400,
                        consider_ecc=False,
                        auto_rotate=False, 
                        verbose=True)

4.0 Preview Geometry

PunchingShearSection.preview() - Preview critical shear perimeter, openings, slab edges, and other surrounding contexts. Geometric properties like $b_o$ and $I_x$ are also shown. This method returns a matplotlib fig object.

  • No argument necessary.
# visualize what the shear perimeter looks like
fig1 = column1.preview()

5.0 Visualize Results - 2D

PunchingShearSection.plot_results(colormap="jet", cmin="auto", cmax="auto") - Visualize punching shear calculation results and stress contour. This method returns a matplotlib fig object.

  • colormap: string (OPTIONAL)
  • cmin: float or string (OPTIONAL)
    • specify min range for color mapping. Default = min(stress)
  • cmax: float or string (OPTIONAL)
    • specify max range for color mapping. Default = max(stress)
# visualize shear stress plot. Use the "turbo" colormap instead 
# set cmax to 160 psi so that anything higher is colored red
fig2 = column1.plot_results(colormap="turbo", cmax=160)

6.0 Visualize Results - 3D

PunchingShearSection.plot_results_3D(colormap="jet", cmin="auto", cmax="auto", scale=10) - Visualize punching shear calculation results in an interactive 3D format using plotly. This method returns a plotly figure object.

  • colormap: string (OPTIONAL)
  • cmin: float or string (OPTIONAL)
    • specify min range for color mapping. Default = min(stress)
  • cmax: float or string (OPTIONAL)
    • specify max range for color mapping. Default = max(stress)
  • scale: float (OPTIONAL)
    • scaling factor used to adjust the size of vector plot. Default is 10. Which means the max vector is 10" in the Z-dimension.
# visualize shear stress. Use the "plasma" colormap
# change scale to 5 so the stress vectors are not too large.
fig3 = column1.plot_results_3D(colormap="plasma", scale=5)

7.0 Advanced Features

PunchingShearSection.add_perimeter(start, end, depth) - Draw a line of shear perimeter. This is an advanced feature for users who wish to draw highly customized perimeter, such as ones with non-uniform depths. This is not needed in most cases because the auto_generate_perimeter parameter is set to True during initialization, and a perimeter is automatically generated. This method modifies the PunchingShearSection object internally and does not return anything.

  • start: [float]
    • [x, y] coordinate of the start point
  • end: [float]
    • [x, y] coordinate of the end point
  • depth: float
    • slab depth along this line
# define a 18" x 18" column, but turn auto-generate perimeter off
column1 = wthisj.PunchingShearSection(width = 18,
                                      height = 18,
                                      slab_depth = 12,
                                      condition = "I",
                                      auto_generate_perimeter = False)

# draw a custom perimeter where one side has a slab depth of 6" rather than 12"
column1.add_perimeter(start=[-15,-15], end=[-15,15], depth=12)
column1.add_perimeter(start=[-15,15], end=[12,15], depth=12)
column1.add_perimeter(start=[12,15], end=[12,-15], depth=6)
column1.add_perimeter(start=[12,-15], end=[-15,-15], depth=12)

PunchingShearSection.rotate(angle) - Rotate the section by a specified angle. This is an advanced feature not needed in most cases because the auto_rotate argument in .solve() is set to True by default. In other words, sections will automatically be rotated to its principal orientation. Please note equilibrium is only maintained for sections in its principal orientation. Superposition of stress due to bi-axial moment is only valid when Ixy = 0. Refer to the theoretical background section for more info. This method modifies the PunchingShearSection object internally and does not return anything. In the backend, the entire geometry is rotated, rather than the moment vector, because the former is easier to implement.

  • angle: float
    • rotate shear perimeter by a specified DEGREE measured counter clockwise from the +X axis.
# rotate the column by 45 degrees counter-clockwise from +X axis.
column1.rotate(angle=45)

Assumptions and Limitations

  • Units should be in (KIPS, IN).
  • wthisj calculates $J_c$ using the recommendations in ACI 421.1R which differs (on the safe side) compared to ACI 318. Refer to section 5 of the Theoretical Background for more info.
  • wthisj works by discretizing the critical shear perimeter into tiny 0.5 inch fibers, each fiber has an infinitesimal area (dA) which is then summed to approximate $J_c$ and other parameters. The default fiber size is usually accurate enough; however, users may opt to reduce the fiber size further by changing the PATCH_SIZE argument when initializing a PunchingShearSection() object.
  • wthisj will only calculate shear stress demand. Please calculate concrete shear capacity yourself. It should also be noted that wthisj calculates demand using a very specific methodology applicable to the US design practice. Other countries have their own methods of calculating punching shear stress (e.g. EN 1992, fib Model Code 2010). Do NOT mix-and-match building codes when comparing demands to capacity.
  • This is not enterprise software. Please do NOT use it for work. Users assume full risk and responsibility for verifying that the results are accurate.

License

MIT License

Copyright (c) 2025 Robert Wang

About

Concrete punching shear calculation

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages