Skip to main content
PLAXIS Scripting

[ERSS] Exporting Wall Data

20 December 2023: Updated to v1.4, fixing errors for commas in phase names.

Download: #

OneDrive Link

Preamble: #

In a typical deep excavation analysis, the ERSS wall forces are required to be exported from PLAXIS for structural design.

Users must manually retrieve the plate forces from each phases and arrange them into a table for structural analysis and technical presentations. This is a slow and tedious process.

Function: #

The Python script is written to export all staged forces for walls (plate elements) in a model.

  1. Extract horizontal displacement ux, shear force Q and bending moment M across selected stages.
  2. Extract envelope of forces from a selected final stage.
  3. Create a table from the collected values.
  4. Save the table as a file on the desktop.

The table is created in a fixed format which allows for additional processing through a custom-made Excel file. An example Excel file is provided with the download link.

Special Features: #

The program preselects a number of keywords for retrieval of data. By setting clear identification in PLAXIS Input, the selection process can be automated.

For plates, preselection is made for keyword "Wall".

For stages, preselection is made for keywords "Exc", "Rem" and "Reinstate".

For final stage, preselection is made for keyword "Reinstate".

Output: #

By default, the results are presented as per below.

Illustration of Results

Using a custom-made Excel file, you can automate the plotting process. An example Excel file is included in the download link and the outputs are shown below.

Example Plot of Bending Moment Example Plot of Deflection

Script: #

import plxscripting.easy
import easygui
import os
import time

class VerticalPlateResultsGatherer():
    """ A class to retrieve plate calculation results for a number of phases """

    def __init__(self, g_o, phases, plate, finalphase):
        """
        Initialization of the VerticalPlateResultsGatherer class
        :param object g_o: the Plaxis Output global server object
        :param object phases: List of Plaxis Output phase objects to retrieve data for
        :param object plate: Plaxis Output plate object (single) to retrieve data for
        """
        self.g_o = g_o
        self.phases = phases
        self.plate = plate
        self.finalphase = finalphase

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

    def get_detailed_data(self):
        """ specifying which data results to retrieve """
        #_platetype = getattr(self.g_o.ResultTypes,"Plate")
        _resulttype = self.g_o.ResultTypes.Plate
        print("... reading plate data [{}]".format(self.plate.Name.value))

        # use general unit symbols:
        # L for length
        # F for force
        # T for time
        angle = u'\N{DEGREE SIGN}'
        self.add_new_detailed_data('Y Env {}'.format(self.finalphase.Name.value), _resulttype.Y, self.finalphase, unit="L")
        self.add_new_detailed_data('Nmin Env {}'.format(self.finalphase.Name.value), _resulttype.Nx_EnvelopeMin2D, self.finalphase, unit="F/L")
        self.add_new_detailed_data('Nmax Env {}'.format(self.finalphase.Name.value), _resulttype.Nx_EnvelopeMax2D, self.finalphase, unit="F/L")
        self.add_new_detailed_data('Qmin Env {}'.format(self.finalphase.Name.value), _resulttype.Q_EnvelopeMin2D, self.finalphase, unit="F/L")
        self.add_new_detailed_data('Qmax Env {}'.format(self.finalphase.Name.value), _resulttype.Q_EnvelopeMax2D, self.finalphase, unit="F/L")
        self.add_new_detailed_data('Mmin Env {}'.format(self.finalphase.Name.value), _resulttype.M_EnvelopeMin2D, self.finalphase, unit="F/L")
        self.add_new_detailed_data('Mmax Env {}'.format(self.finalphase.Name.value), _resulttype.M_EnvelopeMax2D, self.finalphase, unit="F/L")

        for phase in self.phases:
            self.add_new_detailed_data('Y {}'.format(phase.Name.value), _resulttype.Y, phase, unit="L")
            self.add_new_detailed_data('u_x {}'.format(phase.Name.value), _resulttype.Ux, phase, unit="L")
            self.add_new_detailed_data('Q {}'.format(phase.Name.value), _resulttype.Q2D, phase, unit="F/L")
            self.add_new_detailed_data('M {}'.format(phase.Name.value), _resulttype.M2D, phase, unit="F/L")

    def add_new_detailed_data(self, caption, resulttype, phase, unit=None, FEMtype="node"):
        """
        collects a specified result using Plaxis Output's getresult command
        :param str caption: the user defined caption of the retrieved result
        :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"
        """
        if caption not in self.data:
            self.header.append(caption)
            # 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[caption] = unit

            try:
                self.data[caption] = self.g_o.getresults(self.plate,phase,resulttype,FEMtype)
            except:
                self.data[caption] = []



class PlateResults():

    def __init__(self, g_o, phases=None, plates=None, finalphase = None):
        self.name = "PLAXIS 2D Plate Results Script"
        self.g_o = g_o
        self.phases = phases
        self.plates = plates
        self.finalphase = finalphase
        self.results = {}
        self.overheader = []

    def run(self):
        if not self.plates:
            self.ask_plates()
        print("... gathering phases")
        if not self.phases:
            self.ask_phases()
        if not self.finalphase:
            self.ask_final_phase()

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

        msgtxt = ""
        plateCount = 0

        for currentplate in self.plates:
            savepath = os.path.expanduser(saveDir + "/" + self.get_plate_screenname(currentplate) + ".csv")
            self.create_results_table(self.phases, currentplate, self.finalphase, savepath)

            plateCount = plateCount+1
            if (plateCount == 1):
               msgtxt= self.get_plate_screenname(currentplate)

            elif (plateCount == len(self.plates)):
                msgtxt = msgtxt + " and " + self.get_plate_screenname(currentplate)

            else:
                msgtxt = msgtxt + ", " + self.get_plate_screenname(currentplate)

        msgtxt = msgtxt + " 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:\nAdded preselect for plates (wall), phases (exc, rem, reinstate) and final phase (reinstate).\nModified save file location to a new folder in the Desktop.\n\n- Chen Teck, 2023',title='About',ok_button='OK')



    def ask_phases(self):
        """ asks the user to specify the phase to get the results for """
        if self.phases is None:
            phaselist = []
            choicelist = [self.get_phase_screenname(phase) for phase in g_o.Phases[:]]
            preselectlist = []
            for item in choicelist:
                if "exc" in item.lower() or "rem" in item.lower() or "reinstate" in item.lower():
                    preselectlist.append(choicelist.index(item))

            while not phaselist:
                msg = '[ERSS WALL] Select one or more phases to generate plate results for'
                phasechoices = easygui.multchoicebox(msg=msg,
                                                    title=self.name,
                                                    choices=choicelist,
                                                    preselect=preselectlist)
                if phasechoices:
                    for phase in g_o.Phases[:]:
                        if self.get_phase_screenname(phase) in phasechoices:
                            phaselist.append(phase)
                            self.overheader.append(phase.Identification.value.split('[')[0].strip())
            self.phases = phaselist[:]

    def ask_final_phase(self):
        if self.finalphase is None:
            fphasechoicelist = [self.get_phase_screenname(phase) for phase in g_o.Phases[:]]
            preselectfphase = None
            for item in fphasechoicelist:
                if "reinstate" in item.lower():
                    preselectfphase=fphasechoicelist.index(item)

            while not self.finalphase:
                msg = '[ERSS WALL] Select final phase for envelope of forces'
                fphasechoice = easygui.choicebox(msg=msg,title=self.name,choices=fphasechoicelist, preselect = preselectfphase)
                if fphasechoice:
                    for phase in g_o.Phases[:]:
                        if self.get_phase_screenname(phase) == fphasechoice:
                            self.finalphase = phase


    def get_phase_screenname(self, phase):
        return '{} - {}'.format(phase.Name.value, phase.Identification.value.split('[')[0].strip())

    def ask_plates(self):
        """ asks the user to specify the plates to get the results for """
        if self.plates is None:
            platelist = []
            platechoicelist = [self.get_plate_screenname(plate) for plate in g_o.Plates[:]]
            preselectplatelist = []
            for item in platechoicelist:
                if "wall" in item.lower():
                    preselectplatelist.append(platechoicelist.index(item))

            while not platelist:
                msg = '[ERSS WALL] Select one or more plates to generate results for'
                platechoices = easygui.multchoicebox(msg=msg,
                                                    title=self.name,
                                                    choices=platechoicelist,
                                                    preselect=preselectplatelist)
                if platechoices:
                    for plate in g_o.Plates[:]:
                        if self.get_plate_screenname(plate) in platechoices:
                            platelist.append(plate)
            self.plates = platelist[:]

    def get_plate_screenname(self, plate):
        return '{}'.format(plate.Name.value)

    def create_results_table(self, phases, plate, finalphase, savepath):
        """
        creates a full table with the results data for the given phase
        :param obj phase: Plaxis Output phase object to retrieve the data for
        :param str savepath: path to save the table as a csv
        """
        if plate not in self.results:
            self.results[plate] = VerticalPlateResultsGatherer(self.g_o, phases, plate, finalphase)
        _values = self.results[plate].data

        print("... generating table [{}]".format(plate.Name.value))

        tableinfo = [self.name]
        tableinfo.append("For plate: {}".format(plate.Name.value))

        tableheader = self.results[plate].header
        tablerows = [self.finalphase.Identification.value.split('[')[0].strip()+",,,,,,,"+",,,,".join(self.overheader)] #number of commas to edit if finalphase output changes
        tablerows.append(",".join(tableheader))
        # add units for the headers from self.results[phase].units
        headerunits = []
        for colname in tableheader:
            headerunits.append(self.results[plate].units[colname])
        tablerows.append(",".join(["[{}]".format(j) for j in headerunits]))

        # find the maximum length of data (per column of data)
        maxlength = 0
        for i in range(len(tableheader)):
            if len(_values[tableheader[i]]) > maxlength:
                maxlength = len(_values[tableheader[i]])

        for i in range(maxlength):
            rowdata = []
            for colname in tableheader:
                try:
                    rowdata.append(_values[colname][i])
                except:
                    rowdata.append("")
            tablerows.append(",".join(["{}".format(j) for j in rowdata]))
        self.save_table(tableinfo, tablerows, savepath)

    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()
    platedata = PlateResults(g_o)
    platedata.run()

Customization #

1. Modifying the variables collected for each phase.

The variables defined for each phase are stated in the excerpt below. By inserting or removing lines from the following excerpt, you could retrieve other forms of data.

Original Excerpt:

        for phase in self.phases:
            self.add_new_detailed_data('Y {}'.format(phase.Name.value), _resulttype.Y, phase, unit="L")
            self.add_new_detailed_data('u_x {}'.format(phase.Name.value), _resulttype.Ux, phase, unit="L")
            self.add_new_detailed_data('Q {}'.format(phase.Name.value), _resulttype.Q2D, phase, unit="F/L")
            self.add_new_detailed_data('M {}'.format(phase.Name.value), _resulttype.M2D, phase, unit="F/L")

Example: Inserting lines to get Mmin and Mmax for each phase.

        for phase in self.phases:
            self.add_new_detailed_data('Y {}'.format(phase.Name.value), _resulttype.Y, phase, unit="L")
            self.add_new_detailed_data('u_x {}'.format(phase.Name.value), _resulttype.Ux, phase, unit="L")
            self.add_new_detailed_data('Q {}'.format(phase.Name.value), _resulttype.Q2D, phase, unit="F/L")
            self.add_new_detailed_data('M {}'.format(phase.Name.value), _resulttype.M2D, phase, unit="F/L")
            self.add_new_detailed_data('Mmin {}'.format(phase.Name.value), _resulttype.M_EnvelopeMin2D, phase, unit="F/L")
            self.add_new_detailed_data('Mmax {}'.format(phase.Name.value), _resulttype.M_EnvelopeMax2D, phase, unit="F/L")

Tip: The parameter for _resulttype can be found in 2D Output > Help > Command Reference > Output Program > Output objects reference > plate.

2. Modifying the variables collected for final phase.

Similar to above, by inserting or removing lines from the following excerpt, you could retrieve other forms of data from the final phase.

Original Excerpt:

self.add_new_detailed_data(
	"Y Env {}".format(self.finalphase.Name.value),
	_resulttype.Y,
	self.finalphase,
	(unit = "L")
);
self.add_new_detailed_data(
	"Nmin Env {}".format(self.finalphase.Name.value),
	_resulttype.Nx_EnvelopeMin2D,
	self.finalphase,
	(unit = "F/L")
);
self.add_new_detailed_data(
	"Nmax Env {}".format(self.finalphase.Name.value),
	_resulttype.Nx_EnvelopeMax2D,
	self.finalphase,
	(unit = "F/L")
);
self.add_new_detailed_data(
	"Qmin Env {}".format(self.finalphase.Name.value),
	_resulttype.Q_EnvelopeMin2D,
	self.finalphase,
	(unit = "F/L")
);
self.add_new_detailed_data(
	"Qmax Env {}".format(self.finalphase.Name.value),
	_resulttype.Q_EnvelopeMax2D,
	self.finalphase,
	(unit = "F/L")
);
self.add_new_detailed_data(
	"Mmin Env {}".format(self.finalphase.Name.value),
	_resulttype.M_EnvelopeMin2D,
	self.finalphase,
	(unit = "F/L")
);
self.add_new_detailed_data(
	"Mmax Env {}".format(self.finalphase.Name.value),
	_resulttype.M_EnvelopeMax2D,
	self.finalphase,
	(unit = "F/L")
);

Note that the above customization will increase the columns of the output file, and the original Excel file will not work. Either way, it is recommended to make your own Excel for your workflow.