Skip to main content
PLAXIS Scripting

[ERSS] Exporting Strut Data

Download: #

For versions before 2024.1: OneDrive Link

For 2024.1: OneDrive Link

Preamble: #

In a typical deep excavation analysis, the strut forces are required to be exported from PLAXIS for structural strut designs.

Users must manually retrieve the strut forces from each phases and arrange them into a table for structural analysis and technical presentations.

Function: #

The Python script is written to export all forces of node-to-node anchors and fixed end anchors in a model.

  1. Extract all N values from anchors in the model, across all stages.
  2. Create a table from the collected values.
  3. Save the table as a file on the desktop.

Special Features: #

Many analyses utilises anchors for other members such as kingposts. For a cleaner export, you could name your struts with "Strut1", "Strut2" ... in your PLAXIS Input. The program will then only export for the named struts.

In the absence of any named struts, the program will export for all anchors.

Output: #

The default output is a csv file in the below format.

Example Strut Data Output

Through simple customization detailed in the last section, a user can get Nmin/Nmax if desired.

Example Full Strut Data Output

Script: #

import plxscripting.easy
import easygui
import os
import time

class OutputObjectResultsGatherer():
    """ A class to retrieve output objects calculation results for a number of phases """

    def __init__(self, g_o, phases, n2ns, feas):
        """
        Initialization of the outputObjectResultsGatherer class
        :param object g_o: the Plaxis Output global server object
        :param object phases: Plaxis Output phase object (list) to retrieve data for
        :param object n2ns, feas: Plaxis Output object (list) to retrieve data for
        """
        self.g_o = g_o
        self.phases = phases
        self.n2ns = n2ns
        self.feas = feas

        self.header = []
        self.data = {}
        self.units = {}
        self.get_detailed_data()

    def get_detailed_data(self):

        """ specifying which data results to retrieve """
        _resulttype_n2n = self.g_o.ResultTypes.NodeToNodeAnchor
        _resulttype_fea = self.g_o.ResultTypes.FixedEndAnchor
        print("... reading strut data [{}]")

        # use general unit symbols:
        # L for length
        # F for force
        # T for time

        for phase in self.phases:
            if self.n2ns:
                for n2n in self.n2ns:
                    self.add_new_detailed_data('N', _resulttype_n2n.AnchorForce2D, phase, n2n, unit="F")
                    #self.add_new_detailed_data('Nmin', _resulttype_n2n.AnchorForceMin2D, phase, n2n, unit="F")
                    #self.add_new_detailed_data('Nmax', _resulttype_n2n.AnchorForceMax2D, phase, n2n, unit="F")
            if self.feas:
                for fea in self.feas:
                    self.add_new_detailed_data('N', _resulttype_fea.AnchorForce2D, phase, fea, unit="F")
                    #self.add_new_detailed_data('Nmin', _resulttype_fea.AnchorForceMin2D, phase, fea, unit="F")
                    #self.add_new_detailed_data('Nmax', _resulttype_fea.AnchorForceMax2D, phase, fea, unit="F")


    def add_new_detailed_data(self, captionResultType, resulttype, phase, outputObject, unit=None, FEMtype="node"):
        """
        collects a specified result using Plaxis Output's getresult command
        :param object resulttype: the Plaxis Output ResultType object for the data to be retrieved
        :param str FEMtype: type of FEM element to retrieve data for: "node" or "stress point"
        """

        caption = captionResultType + ' {} {}'.format(outputObject.Name.value, phase.Name.value)
        headerCaption = captionResultType + ' {}'.format(outputObject.Name.value)

        if caption not in self.data:
            if headerCaption not in self.header:
                self.header.append(headerCaption)
            # add units
            if unit:
                unit = unit.replace("L", self.g_o.GeneralInfo.UnitLength.value)
                unit = unit.replace("F", self.g_o.GeneralInfo.UnitForce.value)
                unit = unit.replace("T", self.g_o.GeneralInfo.UnitTime.value)
            self.units[headerCaption] = unit

            try:
                resultsList = self.g_o.getresults(outputObject,phase,resulttype,FEMtype)
                self.data[caption] = resultsList[0]
                for N in resultsList:
                    print (caption + ": " + str(N) + unit)
                    if (self.data[caption] != N):
                        easygui.msgbox("Inspect " + caption + ", the results delivered multiple values for a single strut and phase.")
            except:
                self.data[caption] = ""


class OutputResults():

    def __init__(self, g_o, phases=None, n2ns = None, feas = None):
        self.name = "PLAXIS 2D Strut Results Script"
        self.g_o = g_o
        self.phases = g_o.Phases[:]


        n2nNamedList = []
        feaNamedList = []
        try:
            n2nList = g_o.NodeToNodeAnchors[:]
            for n2n in n2nList:
                if 'strut' in n2n.Name.value.lower():
                    n2nNamedList.append(n2n)
        except:
            n2nList = []
        try:
            feaList = g_o.FixedEndAnchors[:]
            for fea in feaList:
                if 'strut' in fea.Name.value.lower():
                    feaNamedList.append(fea)
            self.feas = g_o.FixedEndAnchors[:]
        except:
            feaList = []

        if n2nNamedList or feaNamedList:
            self.n2ns = n2nNamedList
            self.feas = feaNamedList
        else:
            self.n2ns = n2nList
            self.feas = feaList

    def run(self):

        saveDir = os.path.expanduser("~\\Desktop\\" + "PLAXIS Python Extracts\\"+time.strftime("%Y%m%d-%H%M%S"))
        os.makedirs(saveDir)

        savepath = saveDir + "\\strut_data.csv"
        self.create_results_table(self.phases, self.n2ns, self.feas, savepath)
        msgtxt = "Strut results are saved in\n"+ saveDir +"."

        selectionClose = easygui.buttonbox(msg=msgtxt, title='Close the window', choices = ["Close","About"])
        if selectionClose == "About":
            easygui.msgbox(msg='v1.2, released 28/05/2023\n\nChangelog:\nModified format to allow easier customization.\nModified save file location to a new folder in the Desktop.\n\n- Chen Teck, 2023',title='About',ok_button='OK')


    def create_results_table(self, phases, n2ns, feas, savepath):
        """
        creates a full table with the results data
        :param obj phases: Plaxis Output phase object list to retrieve the data for
        :param str savepath: path to save the table as a csv
        """
        results = OutputObjectResultsGatherer(self.g_o, phases, n2ns, feas)
        _values = results.data

        print("... generating table for struts")
        try:

            tableinfo = [self.name]
            tableinfo.append("For N2N/FE Anchors")

            tableheader = results.header

            tablerows = []
            tablerows.append("Identification,"+",".join(tableheader))

            # add units for the headers from self.results[phase].units
            headerunits = []
            for colname in tableheader:
                headerunits.append(results.units[colname])

            tablerows.append(",".join(["[{}]".format(j) for j in headerunits]))


            for phase in phases:
                rowData =  str(phase.Identification.value)
                rowData = rowData.replace(',', '')

                for header in tableheader:
                    rowData += ","
                    rowData += str(_values['{} {}'.format(header, phase.Name.value)])

                tablerows.append(rowData)

            self.save_table(tableinfo, tablerows, savepath)
        except Exception as e: print(e)

    def save_table(self, tableinfo, tablerows, savepath):

        full_table = "{}\n\n{}".format("\n".join(tableinfo), "\n".join(tablerows))
        overview = "\n".join(tableinfo)
        msginfo = 'Saving failed'

        if savepath:
            if full_table:
                try:
                    with open(savepath, 'w') as file:
                        file.write(full_table)
                    msginfo = 'Success, table is saved to desktop!'
                except:
                    msginfo = 'Failed to write to file, check if file is in use!'

        msgtxt = '{}\n\n{}'.format(overview, msginfo)
        print(msgtxt)


if __name__ == "__main__":
    s_o, g_o = plxscripting.easy.new_server()
    outputdata = OutputResults(g_o)
    outputdata.run()

Customization #

By default, the N value of the anchor, which represents the strut force in the current stage, is extracted from the model. Some engineers' workflow will prefer to work with Nmin, the maximum envelope strut compression force that has been utilized to the current stage.

'#' refers to a comment in the script and the script ignores it when running the program. The ability to extract Nmin and Nmax has been scripted in, and can be edited by simply adding or removing the '#' in the script.

Original Excerpt, that extracts only N:

        for phase in self.phases:
            if self.n2ns:
                for n2n in self.n2ns:
                    self.add_new_detailed_data('N', _resulttype_n2n.AnchorForce2D, phase, n2n, unit="F")
                    #self.add_new_detailed_data('Nmin', _resulttype_n2n.AnchorForceMin2D, phase, n2n, unit="F")
                    #self.add_new_detailed_data('Nmax', _resulttype_n2n.AnchorForceMax2D, phase, n2n, unit="F")
            if self.feas:
                for fea in self.feas:
                    self.add_new_detailed_data('N', _resulttype_fea.AnchorForce2D, phase, fea, unit="F")
                    #self.add_new_detailed_data('Nmin', _resulttype_fea.AnchorForceMin2D, phase, fea, unit="F")
                    #self.add_new_detailed_data('Nmax', _resulttype_fea.AnchorForceMax2D, phase, fea, unit="F")

If the intention is to add Nmin and Nmax to the table, we can remove the '#' and the script will now run these lines.

Modified Excerpt:

        for phase in self.phases:
            if self.n2ns:
                for n2n in self.n2ns:
                    self.add_new_detailed_data('N', _resulttype_n2n.AnchorForce2D, phase, n2n, unit="F")
                    self.add_new_detailed_data('Nmin', _resulttype_n2n.AnchorForceMin2D, phase, n2n, unit="F")
                    self.add_new_detailed_data('Nmax', _resulttype_n2n.AnchorForceMax2D, phase, n2n, unit="F")
            if self.feas:
                for fea in self.feas:
                    self.add_new_detailed_data('N', _resulttype_fea.AnchorForce2D, phase, fea, unit="F")
                    self.add_new_detailed_data('Nmin', _resulttype_fea.AnchorForceMin2D, phase, fea, unit="F")
                    self.add_new_detailed_data('Nmax', _resulttype_fea.AnchorForceMax2D, phase, fea, unit="F")

Note that n2n refers to node-to-node anchors and fea refers to fixed end anchors. When adding or removing which data to collect, you should make the change to both types.