Skip to content

Design Approach

The primary way you run design in Songeya is by writing your own custom design logic. You write Python scripts that access analysis results, perform design calculations, and store the results back into the model. This provides full control over the design process. You can implement any design standard that is appropriate to you region and line of work.

Some checks may take time to set up, especially the first time. But this is no different from what engineers already do. Most engineers build spreadsheets that handle routine design checks. Songeya works the same way, with the added advantage that the bespoke design tools can be directly connected to the Finite Element model results. Since Python is easy to read, you can share your design scripts with other people, or use scripts written by others.

You do not need to set up anything to get started. The program already includes an embedded Python interpreter and a built-in code editor, so you can write and run scripts directly inside the application. The full API is documented here.

An example is shown below for designing a steel member under axial force according to EN 1993-1-1. This script is included with the software. To use it, save it as a file and place it in the designated scripts folder. Then assign it to the frames you want to check. When the design process is initiated, the script will pull the required data, perform the checks, and generate a report in Markdown format, which can easily be converted to other formats.


Example: Axially Loaded Steel Member As Per EN 1993-1-1

import numpy as np

# Everything you need to use to interact with the program is in the module imported below
import songeya

# Get the frame. The variable "name" is available in the global context and represents
# the name of the frame being checked when the script is executed
frame = songeya.model.get_steel_frame(name)

# Only proceed if the model has loads
if not len(songeya.model.list_load_combinations()) > 0:
    raise StopIteration("No loads found.")

# Supported section classes
HOT_ROLLED_I_SECTIONS = {
    "UB", "UC", "ASB", "W", "IPN", "RSJ", "M", "S", "HP"
}
HOLLOW_SECTIONS = {
    "CHS", "RHS", "SHS", "Sqr HSS", "Rect HSS",
    "Round HSS", "Pipe STD", "Pipe XS", "Pipe XXS"
}
ANGLE_SECTIONS = {"EA", "UA"}
OTHER_SECTIONS = {
    "PFC", "CH", "TUB", "TUC", "SQ", "SQR",
    "RB", "MT", "WT", "ST", "C", "MC"
}

# Get the section and other necessary properties
section = frame.section()
dimensions = section.dimensions()

# Section properties
A = section.A()
Iy = section.Iy()
Iz = section.Iz()

It = section.Jt()
Iw = section.Iw()

try:
    iz = section.iz()
except AttributeError:
    iz = section.iy()
iy = section.iy()

axis1 = "y"
axis2 = "z"
if section.family() in ["EA", "UA"]:
    # Angles buckle about minor axes
    Iy = section.Iu()
    Iz = section.Iv()
    iy = section.iu()
    iz = section.iv()
    axis1 = "u"
    axis2 = "v"

# Material
material = section.material()
fy = material.strength()
E = material.youngs_modulus()
G = material.shear_modulus()

gM0 = 1.0
gM1 = 1.0

fy_mpa = fy / 1e6
E_mpa = E / 1e6

L = frame.length()

def imperfection_factors(curve):
    # Table 6.1
    curves = {"a0": 0.13, "a": 0.21, "b": 0.34, "c": 0.49, "d": 0.76}
    return curves[curve]

def buckling_curve_for_compression():
    """
    Select buckling curves per EN 1993-1-1 Table 6.2.
    """
    family = section.family()
    dim = section.dimensions()
    if family in HOT_ROLLED_I_SECTIONS:
        h = dim["h"]
        tf = dim["tf"]
        b = dim["bb"]
        if h / b > 1.2:
            if tf < 0.04:
                yy = "a"
                zz = "b"
            else:
                yy = "b"
                zz = "c"
        else:
            if tf <= 0.1:
                yy = "b"
                zz = "c"
            else:
                yy = "d"
                zz = "d"
        return yy, zz
    elif family in HOLLOW_SECTIONS:
        return "a", "a"
    elif family in ["EA", "UA"]:
        return "b", "b"
    elif family in OTHER_SECTIONS:
        return "c", "c"
    else:
        raise TypeError("Section family is not supported.")

def flexural_buckling_resistance(L_y, L_z):
    """
    Flexural buckling resistance Nb,Rd per 6.3.1 (y-y and z-z axes).
    Returns (Nb_Rd, Nb_Rd_y, Nb_Rd_z, chi_y, chi_z, lambda_y, lambda_z, phi_y, phi_z).
    """

    lambda_1 = np.pi * np.sqrt(E / fy)

    lambda_y = (L_y / iy) / lambda_1
    lambda_z = (L_z / iz) / lambda_1

    curve_yy, curve_zz = buckling_curve_for_compression()
    alpha_y = imperfection_factors(curve_yy)
    alpha_z = imperfection_factors(curve_zz)

    phi_y = 0.5 * (1 + alpha_y * (lambda_y - 0.2) + lambda_y**2)
    phi_z = 0.5 * (1 + alpha_z * (lambda_z - 0.2) + lambda_z**2)
    chi_y = min(1.0, 1.0 / (phi_y + np.sqrt(max(phi_y**2 - lambda_y**2, 0))))
    chi_z = min(1.0, 1.0 / (phi_z + np.sqrt(max(phi_z**2 - lambda_z**2, 0))))

    Nb_Rd_y = chi_y * A * fy / gM1
    Nb_Rd_z = chi_z * A * fy / gM1
    Nb_Rd = min(Nb_Rd_y, Nb_Rd_z)

    return (
        Nb_Rd,
        Nb_Rd_y,
        Nb_Rd_z,
        curve_yy,
        curve_zz,
        alpha_y,
        alpha_z,
        chi_y,
        chi_z,
        lambda_y,
        lambda_z,
        phi_y,
        phi_z,
    )

# Number of positions along the member to check
n_points = 10
positions = [i * 0.999 * L / (n_points - 1) for i in range(n_points)]

# Add some common points
positions.extend((0.24 * L, 0.5 * L, 0.75 * L))

# Add points of discontinuity
disconts = frame.discontinuities()
positions.extend(disconts)
positions = sorted(set(positions))

# Utilisations
ratio = {}
ratio["MAX"] = 0
ratio["Combination"] = ""

forces = {}
buckling_y = {}
buckling_z = {}
buckling_Rd = {}

def find_segment(x, segment_points):
    """
    Returns (segment_index, x_start, x_end) for position x.
    segment_points must be sorted.
    """
    for i in range(len(segment_points) - 1):
        x0 = segment_points[i]
        x1 = segment_points[i + 1]
        if x0 <= x <= x1:
            return x0, x1
    return None, None


fully_restrained_y, braced_yy = frame.flexural_buckling_restraints(buckling_axis="Y")
fully_restrained_z, braced_zz = frame.flexural_buckling_restraints(buckling_axis="Z")

for combo in songeya.model.list_load_combinations():
    for x in positions:

        # Determine the segment x falls in
        yy_start, yy_end = find_segment(x, braced_yy)
        zz_start, zz_end = find_segment(x, braced_zz)

        # Determine moments for the yy and zz segments
        yy_points = np.linspace(yy_start, yy_end, 20)
        zz_points = np.linspace(zz_start, zz_end, 20)

        # Find forces at current section
        NEd = frame.fx(x=x, combo_name=combo)  # - tension, + compression

        N_comp = max(NEd, 0.0)  # compression magnitude
        N_ten = max(-NEd, 0.0)  # tension magnitude

        NEd = abs(NEd)

        # Find section compression resistance
        L_y = yy_end - yy_start
        L_z = zz_end - zz_start
        if fully_restrained_y:
            L_y = 0
        if fully_restrained_z:
            L_z = 0
        nc_rd = A * fy / gM0  # This is for class 1,2 and 3.
        (
            nb_rd,
            nb_rd_y,
            nb_rd_z,
            curve_yy,
            curve_zz,
            alpha_y,
            alpha_z,
            chi_y,
            chi_z,
            lambda_y,
            lambda_z,
            phi_y,
            phi_z,
        ) = flexural_buckling_resistance(L_y, L_z)

        # Find tension resistance
        nt_rd = A * fy / gM0

        # Section checks
        UFS_NT = N_ten / nt_rd  # Nt,Ed/Nc,Rd
        UFS_NC = N_comp / nc_rd  # N,Ed/Nc,Rd
        UFB_NB = N_comp / nb_rd

        # Maximum utilisation ratio
        max_ratio = max([UFS_NT, UFS_NC, UFB_NB])

        if max_ratio < ratio["MAX"]:
            continue

        forces = {}
        forces["Nc,Ed"] = N_comp
        forces["Nt,Ed"] = N_ten

        # Buckling parameters about the y-axis
        buckling_y = {}
        buckling_y["Curve,y"] = curve_yy  # Buckling curve
        buckling_y["Lcr,y"] = L_y  # Effective buckling length
        buckling_y["Lam,y"] = L_y / iy  # Slenderness ratio
        buckling_y["Lam_nd,y"] = lambda_y  # Non-dimensional slenderness for buckling
        buckling_y["alpha,y"] = alpha_y  # Imperfection factor
        buckling_y["phi,y"] = phi_y  # Coefficient for calculation of X
        buckling_y["X,y"] = chi_y  # Reduction factor for buckling
        buckling_y["Ny,b,Rd"] = nb_rd_y  # Design buckling resistance

        # Buckling parameters about the z-axis
        buckling_z = {}
        buckling_z["Curve,z"] = curve_zz  # Buckling curve
        buckling_z["Lcr,z"] = L_z  # Effective buckling length
        buckling_z["Lam,z"] = L_z / iz  # Slenderness ratio
        buckling_z["Lam_nd,z"] = lambda_z  # Non-dimensional slenderness for buckling
        buckling_z["alpha,z"] = alpha_z  # Imperfection factor
        buckling_z["phi,z"] = phi_z  # Coefficient for calculation of X
        buckling_z["X,z"] = chi_z  # Reduction factor for buckling
        buckling_z["Nz,b,Rd"] = nb_rd_z  # Design buckling resistance

        buckling_Rd = {}
        buckling_Rd["Nc,Rd"] = nc_rd  # Design compression resistance
        buckling_Rd["Nt,Rd"] = nt_rd  # Tension resistance of member
        buckling_Rd["Nb,Rd"] = nb_rd  # Design buckling resistance of compression member

        # Section capacity
        ratio["UFS[Nc]"] = UFS_NC  # N,Ed/Nc,Rd
        ratio["UFS[Nt]"] = UFS_NT  # N,Ed/Nt,Rd

        # Global capacity
        ratio["UFB[Nb]"] = UFB_NB

        ratio["MAX"] = max_ratio
        ratio["Combination"] = combo

# Generate report
status = "Meets Criteria" if ratio.get("MAX", 0.0) <= 1.0 else "Fails Criteria"

markdown_text = f"""
# Axial member design to EN 1993-1-1: {name}
## Load combination: {ratio['Combination']}
## Limitations

- Can only be used for steel grades S235, S275, S355 and S420
- Serviceability deflection limits are not checked
- Class 4 cross-sections are not handled
- Torsional nad torsional-flexural buckling are not checked. However, for columns in buildings, these are less onerous than flexural (Euler) buckling

## Material
| Property           | Value             | Reference | Description |
|--------------------|-------------------|-----------|-------------|
| Name               | S{int(fy_mpa)}    | -         | -           |
| Yield strength, fy | {fy_mpa:.1f} MPa  | -         | -           |
| Elastic modulus, E | {E_mpa:.0f} MPa   | -         | -           |
| Shear modulus, G   | {G / 1e9:.0f} MPa | -         | -           |
| gM0                | {gM0}             | 2.4       | Partial factor for resistance of cross-section |
| gM1                | {gM1}             | 2.4       | Partial factor for buckling resistance |

## Section properties
| Property | Value              | Description                         |
|----------|--------------------|-------------------------------------|
| A        | {A * 1e4:.1f} cm²  | Cross-sectional area                |
| I{axis1} | {Iy * 1e8:.0f} cm⁴ | Moment of inertia about major axis  |
| I{axis2} | {Iz * 1e8:.0f} cm⁴ | Moment of inertia about minor axis  |
| i{axis1} | {iy * 1e3:.0f} mm  | Radius of gyration about major axis |
| i{axis2} | {iz * 1e3:.0f} mm  | Radius of gyration about minor axis |

## Applied forces: {ratio['Combination']}
| Action    | Value                             |
|-----------|-----------------------------------|
| Nc,Ed     | {forces["Nc,Ed"] / 1e3:.1f} kN    |
| Nt,Ed     | {forces["Nt,Ed"] / 1e3:.1f} kN    |

## Flexural buckling about {axis1}-axis
| Parameter     | Value                                  | Formula                             | Reference   | Description |
|---------------|----------------------------------------|-------------------------------------|-------------|-------------|
| Curve         | {buckling_y["Curve,y"]}                | -                                   | Table 6.2   | Buckling curve
| Lcr           | {buckling_y["Lcr,y"]:.2f} m            | -                                   | -           | Effective buckling length
| Lam           | {buckling_y["Lam,y"]:.2f}              | Lcr/i{axis1}                        | -           | Slenderness ratio
| Lam_nd        | {buckling_y["Lam_nd,y"]:.3f}           | sqrt(A*fy/Ncr)                      | 6.3.1.2     | Non-dimensional slenderness for buckling
| alpha         | {buckling_y["alpha,y"]:.3f}            | -                                   | Table 6.1   | Imperfection factor
| phi           | {buckling_y["phi,y"]:.3f}              | 0.5*[1+alpha*(lam_nd-0.2)+lam_nd^2] | 6.3.1.2     | Coefficient for calculation of chi
| chi           | {buckling_y["X,y"]:.3f}                | 1/[phi+sqrt(phi^2-lam_nd^2)]        | 6.3.1.2     | Reduction factor for buckling
| N{axis1},b,Rd | {buckling_y["Ny,b,Rd"] * 1e-3:.1f} kN  | chi*A*fy/gM1                      | 6.3.1.1     | Design buckling resistance

## Flexural buckling about {axis2}-axis
| Parameter     | Value                                  | Formula                             | Reference   | Description |
|---------------|----------------------------------------|-------------------------------------|-------------|-------------|
| Curve         | {buckling_z["Curve,z"]}                | -                                   | Table 6.2   | Buckling curve
| Lcr           | {buckling_z["Lcr,z"]:.2f} m            | -                                   | -           | Effective buckling length
| Lam           | {buckling_z["Lam,z"]:.2f}              | Lcr/i{axis1}                        | -           | Slenderness ratio
| Lam_nd        | {buckling_z["Lam_nd,z"]:.3f}           | sqrt(A*fy/Ncr)                      | 6.3.1.2     | Non-dimensional slenderness for buckling
| alpha         | {buckling_z["alpha,z"]:.3f}            | -                                   | Table 6.1   | Imperfection factor
| phi           | {buckling_z["phi,z"]:.3f}              | 0.5*[1+alpha*(lam_nd-0.2)+lam_nd^2] | 6.3.1.2     | Coefficient for calculation of chi
| chi           | {buckling_z["X,z"]:.3f}                | 1/[phi+sqrt(phi^2-lam_nd^2)]        | 6.3.1.2     | Reduction factor for buckling
| N{axis2},b,Rd | {buckling_z["Nz,b,Rd"] * 1e-3:.1f} kN  | chi*A*fy/gM1                      | 6.3.1.1     | Design buckling resistance


## Resistances (EN 1993-1-1)
| Parameter | Value                               | Formula            | Reference | Description                         |
|-----------|-------------------------------------|--------------------|-----------|-------------------------------------|
| Nc,Rd     | {buckling_Rd["Nc,Rd"] / 1e3:.2f} kN | A * fy / gM0       | 6.2.4     | Design compression resistance of cross-section |
| Nt,Rd     | {buckling_Rd["Nt,Rd"] / 1e3:.2f} kN | A * fy / gM0       | 6.2.3     | Tension resistance of cross-section |
| Nb,Rd     | {buckling_Rd["Nb,Rd"] / 1e3:.2f} kN | chi * A * fy / gM1 | 6.3.1.1   | Design flexural buckling resistance |


## Utilisation ratios
| Check        | Value                  | Reference  | Description               |
|--------------|------------------------|------------|---------------------------|
| UFS[Nc]      | {ratio["UFS[Nc]"]:.3f} | 6.2.4      | NEd / Nc,Rd               |
| UFS[Nt]      | {ratio["UFS[Nt]"]:.3f} | 6.2.3      | NEd / Nt,Rd               |
| UFS[Nb]      | {ratio["UFB[Nb]"]:.3f} | 6.2.3      | NEd / Nb,Rd               |
| MAX          | {ratio["MAX"]:.3f}     | -          | Maximum utilisation ratio |

## Status
**{status}**

---

*Designed to EN 1993-1-1*  
*Generated via Songeya*
"""

frame.set_utilisation_ratio(ratio["MAX"])
if ratio["MAX"] > 1:
    frame.set_fails_criteria()
else:
    frame.set_meets_criteria()

frame.set_design_note(markdown_text)