7.4.1. CLT Deflection Tables

CLT panels often have significant shear deflection because of shear deformation in their cross layers. There are some reasources that exist for single span clt, however, CLT is often used in multi-span conditions.

In this design example, a parametric study are run on a series of clt panels, and the outputs are used to create span tables. The parametric study evaluates shear by analyzing each panel as both a Euler beam, and timoshenko beam. Note that many helper functions are used from in the “parametricHelper” file, which has documentation for each function.

Also note that the actual pdf design reasource was created manually, using the .csv outputs from this study.

The first portion of the analysis creates the deflection tables for PRG-320 SPF sections. This is used to make the PRG deflection tables. Initially sections are loaded using section databases, then a set of spans and span lengths are set up.

sections    = c19.loadCltSections()
E1Sections  = ls.filterByName(sections, 'E1')
V2Sections  = ls.filterByName(sections, 'V2')

spfCLT = E1Sections + V2Sections
Nspans = [1, 2, 3]
Lspans = [2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 6, 6.5, 7, 7.5]

outPuts = {}
for panel in spfCLT:
    EI = panel.getEIs()
    GA = panel.getGAs()
    
    u = ph.runAllAnalysesPanel(Nspans, Lspans, panel)
    outPuts[panel.name] = ph.postProcessAnalysis(u)

ph.saveToFile(outPuts, Lspans, ind=1, baseName='prg')

The second portion of the analysis finds the ratio of total deflection (flexural + shear), to flexural deflection for panels with an abitary ratio of EI / GA.

EI0 = 8e6
ratios = [0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.5, 0.6, 0.7]
outPutsRatio = {}

for r in ratios:
    GA = EI0 /r    
    u = ph.runAllAnalyses(Nspans, Lspans, EI0, GA)
    outPutsRatio[r] = ph.postProcessAnalysis(u)

ph.saveToFile(outPutsRatio, Lspans, ind=3, baseName='modification')

Finally, plots of the section are made.

L = 5
Nspans = [1,2,3]
for Nspan in Nspans:
    fig, ax = ph.getBeamPlot(Nspan, L, V2Sections[1])

The full example is below:

"""
CLT panels often have significant shear deflection because of shear deformation
in their cross layers. There are some reasources that exist for single span
clt, however, CLT is often used in multi-span conditions.

In this design example, a parametric study are run on a series of clt panels, 
and the outputs are used to create span tables. The parametric study evaluates
shear by analyzing each panel as both a Euler beam, and timoshenko beam.
Note that many helper functions are used from in the "parametricHelper" file, 
which has documentation for each function.

Also note that the actual pdf design reasource was created manually, using the
.csv outputs from this study.
"""
import limitstates as ls
import limitstates.design.csa.o86.c19 as c19
import parametricHelpers as ph

# =============================================================================
# PRG panels
# =============================================================================

"""
The first portion of the analysis creates the deflection tables for PRG-320 SPF 
sections. This is used to make the PRG deflection tables.
Initially sections are loaded using section databases, then a set of spans and 
span lengths are set up.
 
"""

sections    = c19.loadCltSections()
E1Sections  = ls.filterByName(sections, 'E1')
V2Sections  = ls.filterByName(sections, 'V2')

spfCLT = E1Sections + V2Sections
Nspans = [1, 2, 3]
Lspans = [2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 6, 6.5, 7, 7.5]

outPuts = {}
for panel in spfCLT:
    EI = panel.getEIs()
    GA = panel.getGAs()
    
    u = ph.runAllAnalysesPanel(Nspans, Lspans, panel)
    outPuts[panel.name] = ph.postProcessAnalysis(u)

ph.saveToFile(outPuts, Lspans, ind=1, baseName='prg')


# =============================================================================
# Make The deflection modifcation tables
# =============================================================================
"""
An analysis is run on a arbitary set of CLT panels, where the ratio of EI/GA
is described in the ratio variable.
"""

EI0 = 8e6
ratios = [0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.5, 0.6, 0.7]
outPutsRatio = {}

for r in ratios:
    GA = EI0 /r    
    u = ph.runAllAnalyses(Nspans, Lspans, EI0, GA)
    outPutsRatio[r] = ph.postProcessAnalysis(u)

ph.saveToFile(outPutsRatio, Lspans, ind=3, baseName='modification')

# =============================================================================
# Make some figures that will go along with the tables
# =============================================================================

L = 5
Nspans = [1,2,3]
for Nspan in Nspans:
    fig, ax = ph.getBeamPlot(Nspan, L, V2Sections[1])
"""
Functions that aid with running the analysis shown in 3.1

"""
import pandas as pd
import planesections as ps
import numpy as np
import limitstates.design.csa.o86.c19 as c19


def runAllAnalysesPanel(Nspans:list[int], lengths:list[float], 
                        panel:c19.BeamColumnCltCsa19):
    """
    Runs a parametric analysis for the input number of spans and lengths
    for input panel.

    Parameters
    ----------
    Nspans : list[int]
        The list of span configurations to check.
    lengths : list[float]
        The list of span lengths to check.
    panel : BeamColumnCltCsa19
        The panel to use in the analysis.

    Returns
    -------
    uout : list[tuple]
        A list of deflections for a regular and timoshenko beam.

    """
    
    
    uout = {}
    for Nspan in Nspans:
        if Nspan not in uout:
            uout[Nspan] = []
        for L in lengths:
            uout[Nspan].append(runCLTAnalysisPanel(L, Nspan, panel))
            # uout.append(runCLTAnalysisPanel(L, Nspan, section))
            
    return uout


def runAllAnalyses(Nspans:list[int], lengths:list[float], EI:float, GA:float):
    """
    Runs a parametric analysis for a panel that uses the input EI and GA value.

    Parameters
    ----------
    Nspans : list[int]
        The list of span configurations to check.
    lengths : list[float]
        The list of span lengths to check.
    EI : float
        The EI for the panel. Units must be in a consistent set of units for 
        FEM that are also compatible with the length unit.
    GA : float
        The GA for the panel. Units must be in a consistent set of units for 
        FEM that are also compatible with the length unit.
        
    Returns
    -------
    uout : list[tuple]
        A list of deflections for a regular and timoshenko beam.

    """
    
    
    uout = {}
    for Nspan in Nspans:
        if Nspan not in uout:
            uout[Nspan] = []
        for L in lengths:
            uout[Nspan].append(runCLTAnalysis(L, Nspan, EI, GA))            
    return uout



def postProcessAnalysis(u:dict)->dict:
    """
    Post process the results by finding the shear deflection, flexure 
    deflection for the panel. Also returned is the percent shear is of total
    deflection, and how much deflection is increased by shear.

    Parameters
    ----------
    u : dict
        The input deflection, in the form of deflection[span] = list[(x1,x2)].

    Returns
    -------
    uOut : dict
        The output defection results, in the form:
            [[uShear1, uFlex1, shearPercent1, increaseFactor1],
             [uShear2, uFlex2, shearPercent2, increaseFactor2],...,]

    """

    uOut = {}
    
    for span in u:
        uTrial = u[span]
        uShear =  [item[0] - item[1] for item in uTrial]
        uFlex  =  [item[1] for item in uTrial]
   
    
        uShear = np.array(np.abs(uShear))
        uFlex  = np.array(np.abs(uFlex))
        
        shearPercent = uShear / (uShear + uFlex)
        increaseFactor = (uShear + uFlex) / uFlex
        
        uOut[span] = (uShear, uFlex, shearPercent, increaseFactor)

    return uOut




def saveToFile(u:dict, lengths:list[float], ind = 3, baseName = 'prg', ):
    """
    Inputs have form: 
        {panelType1: {span1: {r1, r2, r3, r4}, ...},
         panelType2: ....,} where 
        ri is a result for each length
    
    Outputs have the form:
        {span: {length: {r1, r2, r3, r4}}}, where 


    Parameters
    ----------
    u : dict
        The input postprocessed deflection file.
    lengths : list[float]
        A list of lengths.
    ind : int
        The index of the result to use.
        
    baseName : str
        The base name of the output file. A result is generated for each 
        span number
        

    Returns
    -------
    None.

    """
        

    uout = {}
    panelTypes = list(u.keys())
    
    for panelType in u:
        uSpan = u[panelType]
        
        for span in uSpan.keys():
            uLength = uSpan[span]
            
            # Intialize uout at the input span.
            if span not in uout:
                uout[span] = {}
                
            ii = 0
            for L in lengths:
                # Intialize uout at the input length.
                if L not in uout[span]:
                    uout[span][L] = []
                
                uout[span][L].append(uLength[ind][ii])
                ii+=1
            
            # uout[span][panelType] = 
    # Save rsults sorted by span.
    for span in uout:
        dictSpan = uout[span]
        df = pd.DataFrame(dictSpan, columns = lengths)
        df.index = panelTypes
        df.to_csv(f'{baseName}_{span}.csv')



def runCLTAnalysisPanel(L, Nspan, section):
    """
    Runs a CLT panel analysis, where the beam is analyzed as a timoshenko
    beam and euler beam, and returns the deflection for each.

    Units must be in a consistent set of units for FEM that are compatible 
    with the length unit.

    Parameters
    ----------
    L : TYPE
        THe length of the span.
    Nspan : TYPE
        The number of spans.
    section : TYPE
        THe CLT section used.


    Returns
    -------
    umaxTim : float
        The deflection of the beam as a timoshenko beam.
    umaxEul : float
        The deflection of the beam as a Euler beam.
    """

    EI = section.getEIs()
    GA = section.getGAs()

    return runCLTAnalysis(L, Nspan, EI, GA)



def runCLTAnalysis(L:float, Nspan:int, EI:float, GA:float):
    """
    Runs a CLT panel analysis, where the beam is analyzed as a timoshenko
    beam and euler beam, and returns the deflection for each.

    Units must be in a consistent set of units for FEM that are compatible 
    with the length unit.


    Parameters
    ----------
    L : float
        Units must be in a consistent set of units for FEM that are compatible 
        with the section EI/GA units.
    Nspan : int
        The number of spans in the analysis.
    EI : float
        Units must be in a consistent set of units for FEM that are compatible 
        with the length unit.
    GA : float
        Units must be in a consistent set of units for FEM that are compatible 
        with the length unit.

    Returns
    -------
    umaxTim : float
        The deflection of the beam as a timoshenko beam.
    umaxEul : float
        The deflection of the beam as a Euler beam.

    """

    xcoords     = np.linspace(0,1,101)*Nspan*L
    
    E = 9*10**9
    G = E
    Iz = EI / E
    Avx = GA / G
    
    psSection = ps.SectionBasic(E, G, Iz = Iz, Avx = Avx)
    beamTim = ps.TimoshenkoBeam(xcoords, section = psSection)
    beamEul = ps.EulerBeam(xcoords, section = psSection)
    
    pinned = [1,1,0]
    for ii in range(Nspan+1):
        beamTim.setFixity(ii*L, pinned)
    beamTim.addDistLoadVertical(0, L*Nspan, -1000)
    
    for ii in range(Nspan+1):
        beamEul.setFixity(ii*L, pinned)
    beamEul.addDistLoadVertical(0, L*Nspan, -1000)
    
    
    # Run the analysis
    analysisTim = ps.OpenSeesAnalyzer2D(beamTim)
    analysisTim.runAnalysis()
    umaxTim = float(min(ps.getDisp(beamTim, 1)[0]))
 
    
    # Run the analysis
    analysisTim = ps.OpenSeesAnalyzer2D(beamEul)
    analysisTim.runAnalysis()
    umaxEul = float(min(ps.getDisp(beamEul, 1)[0]))
    
    return umaxTim, umaxEul


def getBeamPlot(Nspan, L, section, dispScale = 100):
    """
    Creates an output beam plot.
    """
    
    EI = section.getEIs()
    GA = section.getGAs()
    
    
    xCoords     = np.linspace(0,1,101)*Nspan*L
    
    E = 9*10**9
    G = E
    Iz = EI / E
    Avx = GA / G
    
    psSection = ps.SectionBasic(E, G, Iz = Iz, Avx = Avx)
    beamTim = ps.TimoshenkoBeam(xCoords, section = psSection)
    
    pinned = [1,1,0]
    for ii in range(Nspan+1):
        beamTim.setFixity(ii*L, pinned)
    beamTim.addDistLoadVertical(0, L*Nspan, -1000)
    
    # Run the analysis
    analysisTim = ps.OpenSeesAnalyzer2D(beamTim)
    analysisTim.runAnalysis()
    disp, xCoordsOut = ps.getDisp(beamTim, 1)
    disp = [y*dispScale for y in disp]
    
    fig, ax = ps.plotBeamDiagram(beamTim)
    xplot =  ax.lines[-1].get_xydata()[:,0]
    
    dispScale =  (xplot[1] - xplot[0]) / (L*Nspan)
    xCoordsOut = [float(x)*dispScale for x in xCoordsOut]
    argMin = np.argmin(disp)
    
    ax.plot(xCoordsOut, disp)
    ax.scatter(xCoordsOut[argMin], disp[argMin], s=150, facecolors='none', edgecolors='r')
    
    return fig, ax