0% found this document useful (0 votes)
142 views

Laxis: Netstat - A Netstat - Ab

This document provides instructions for using a Python wrapper to control a PLAXIS application via its HTTP REST API. It describes how to connect to a locally or remotely running PLAXIS instance, check the PLAXIS version, and provides an example that creates a simple project by defining a soil contour, borehole, soil layer, material, line load, mesh, and curve point.

Uploaded by

K. De'blckking
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
142 views

Laxis: Netstat - A Netstat - Ab

This document provides instructions for using a Python wrapper to control a PLAXIS application via its HTTP REST API. It describes how to connect to a locally or remotely running PLAXIS instance, check the PLAXIS version, and provides an example that creates a simple project by defining a soil contour, borehole, soil layer, material, line load, mesh, and curve point.

Uploaded by

K. De'blckking
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 18

APPENDIX G - PYTHON HTTP REST API WRAPPER

APPENDIX G - PYTHON HTTP REST API WRAPPER

The Python wrapper for the PLAXIS HTTP REST API hides the technicalities of dealing
with the HTTP communications behind a comfortable object-oriented wrapper.
Prerequisites:
• An editor for writing Python scripts in. This documentation assumes that the SciTE
editor will be used that comes with the PLAXIS installation.
• At least a very rudimentary knowledge of the Python language is recommended, as
this document makes no attempt to teach it. Good resources for this purpose are:
• Dive Into Python 3 (http://www.diveintopython3.net/)
• After Hours Programming
(http://www.afterhoursprogramming.com/tutorial/Python/Overview/)
• Think Python: How to Think Like a Computer Scientist
(http://greenteapress.com/thinkpython/html/index.html)
• Your firewall must not block the PLAXIS application from accessing the internet, nor
must it block other applications (in particular the python.exe executable) from talking
to the remote scripting server embedded inside the PLAXIS application.

Hint: Note that when using Python and other modules, users must do so under the
licensing conditions of those modules.
» The user can check the Python version delivered by PLAXIS by launching
the interactive interpreter from Expert menu.

G.1 STARTING THE SCRIPTING SERVER

• Start your PLAXIS application.


• Start a new project.
• Go to the Expert menu and select the Configure remote scripting server option. The
corresponding window pops up (Figure G.1). Depending on your machine, the
window may look slightly different.
• Ensure you find an available port and then start the server. Information about ports
currently in use can be retrieved in a Windows Command Prompt, with the following
commands (the second one may require administrator privileges):
netstat -a
netstat -ab

When using the remote scripting in an automated process, you can start the server
without manual interaction by launching your PLAXIS application with the AppServerPort
command line parameter. For example in order to start the server in PLAXIS 3D on port
21403:
c:\Program Files (x86)\Plaxis\PLAXIS 3D\Plaxis3DInput.exe
--AppServerPassword=mypassword --AppServerPort=21403

When the server is running, the main window of your PLAXIS application will reflect this

PLAXIS 3D 2018 | Reference Manual 471


REFERENCE MANUAL

Figure G.1 Configure remote scripting server window

information:

G.2 CONNECTING TO A LOCALLY RUNNING SCRIPTING SERVER USING SCITE

• Start SciTE from the Windows Start menu. It will be called 'Python editor for PLAXIS
3D'. This will present you with an empty document. Let us write a script that will start
a new project.
• First we must import the scripting library:
from plxscripting.easy import *

• Connect to the PLAXIS application (21403 is the port number, substitute yours as
applicable):
s_i, g_i = new_server('localhost', 21403, password="yourpassword")

The variable s is bound to an object representing the PLAXIS application. It is now


possible to control that from the interactive Python prompt.
• Start a new project as follows:
s_i.new()

Save this script somewhere on your hard drive. Make sure it ends with .py so SciTE
correctly identifies it as a Python script. Now we are able to run this script by going
to Tools and then clicking on Go or by pressing F5.
• A new project should now be visible in the PLAXIS application. If instead an error is
returned, check the following for the PLAXIS application you are trying to control:
• Is the application running?
• Does its title bar indicate that the server is active?
• Is the server active on the port you have specified when calling new_server?
You can also try a different port.
• Is your firewall blocking communications to the application?
• Is the password given to new_server the same as the one in the 'configure

472 Reference Manual | PLAXIS 3D 2018


APPENDIX G - PYTHON HTTP REST API WRAPPER

remote scripting server' dialog?

G.3 CONNECTING TO A REMOTE PLAXIS APPLICATION

Connecting to a PLAXIS application running on a computer other than your own requires
you to know the name or IP address of that machine. The IP address can be found by
typing in a Windows command prompt the following:
ipconfig | find "IPv4"

The result will give a set of four numbers separated by dots. E.g.:
IPv4 Address. . . . . . . . . . . : 10.4.2.178

In order to connect to this machine from within the same network, you can simply replace
'localhost' in the boilerplate connection code with the IP address you found, such as
'10.4.2.178'. For example:
>>> s_i, g_i = new_server('10.4.2.178', 21403, password="yourpassword")

If you want to connect to the same machine over the internet, you will need to set up
appropriate port forwarding rules in your router. Note that opening up your computer to
access from the network or even the internet carries significant security risks - do so only
if you have considered these risks.

G.4 IDENTIFYING THE TYPE AND VERSION OF PLAXIS

If there is a need to write codes that are usable under both programs PLAXIS 2D and
PLAXIS 3D, it is possible to check the type and version of PLAXIS software.
With sbeing a server-type object as returned by the new_server(...)method, the
following code must be valid:
>>> print(s.name, s.major_version, s.minor_version, s.is_2d, s.is_3d)
PLAXIS 2D 2018 0 True False
>>> if s.major_version + s.minor_version / 10 > 2020: # i.e. both are numbers
... print('We come from the future')
We come from the future
>>> from .const import PLAXIS_2D, PLAXIS_3D # later to be extended with MPM!
>>> if s.name == PLAXIS_2D:
... print('This is PLAXIS 2D')
... elif s.is_3d: # indicates whether the application is 2D or 3D *capable*
# (not whether it's the 2D or 3D *product*; 2D MPM will have is_2d=True!)
... print('This is 3D or 3D MPM')
... else:
... print('Unknown application!')
This is PLAXIS 2D

The following also work:


from easy import * # must implicitly import the app type constants
s, g = new_server('localhost', 10000)
if s.name == PLAXIS_3D:
print('Is PLAXIS 3D')

Drawing a rectangular/prismatic soil cluster in either 2D or 3D:


from plxscripting.easy import new_server
from math import isnan, nan

s_i, g_i = new_server() # works when launched from Expert menu

def get_number(msg):
nr = nan

PLAXIS 3D 2018 | Reference Manual 473


REFERENCE MANUAL

while isnan(nr):
try:
nr = float(input(msg + ': '))
except:
pass
return nr

width = get_number('Width')
height = get_number('Height')

if s_i.is_3d: # must also work for 3D MPM!


# get the depth of the cluster
depth = get_number('Depth')
make_coords = lambda x, z: (x, 0, z)
else:
make_coords = lambda x, y: (x, y)

coords = [(0, 0), (width, 0), (width, height), (0, height)]


result = g_i.polygon(*[make_coords(*pos) for pos in coords])[0]

if s_i.is_3d: # perform the extrusion and remove original polygon


volume = g_i.extrude(result, (0, depth, 0))[0]
g_i.delete(result)
result = volume

print('Created cluster: {}'.format(result.Name))

G.5 CREATING A SIMPLE PROJECT

All commands that are available in the PLAXIS command line are also available via the
Python wrapper. For more information, see the commands reference provided with your
PLAXIS application. Note that private commands in the application (i.e. commands
starting with a double underscore, __) are spelled without the double underscore when
used from Python. Commands that conflict with Python keywords should be spelled with
an extra trailing underscore in Python (e.g. import becomes import_). Identifiers
containing characters that are not part of the ASCII character set will be stripped of those
characters (e.g. 0 C becomes C).
• Start the interactive interpreter from PLAXIS 3D as described above. Now execute
the code displayed below. For the sake of completeness, the replies returned by the
different statements are included, but some of them are session-specific and will be
different for you.
• It is helpful while executing the Python code to keep an eye on the command line in
the PLAXIS application, in order to gain an understanding of how Python code maps
to commands.
• Keep also in mind that Python, unlike the PLAXIS command line, is case sensitive.
>>> s_i.new()
'OK'
>>> g_i.SoilContour.initializerectangular(0, 0, 10, 10)
'OK'
>>> g_i.borehole(0, 0)
<Borehole {0F389B97-EF0B-48A7-ACC6-E9CC9D3ADE8E}>
>>> g_i.soillayer(10)
<BoreholeVolume {260BC1DD-76F8-4780-AED8-18A5C88D8AAF}>
>>> material = g_i.soilmat()
>>> material.setproperties("SoilModel", "Linear elastic", "gammaUnsat", 16,
"gammaSat", 20, "Gref", 10000)
'Edited SoilMat_1'
>>> g_i.Soils[0].Material = material
>>> g_i.gotostructures()

474 Reference Manual | PLAXIS 3D 2018


APPENDIX G - PYTHON HTTP REST API WRAPPER

'OK'
>>> g_i.lineload((3, 5, 0), (7, 5, 0))
[<Point {C05BBBD3-2179-4D6D-844B-9C3E7CD44EC4}>,
<Point {F671BF99-1384-4BF5-81E8-DB1315414820}>,
<Line {A81EAC2D-8E8F-4A64-8ACB-8F3C72C47D8E}>,
<LineLoad {231DBD97-9E3A-4247-AD30-C6DB982FFDAC}>]
>>> g_i.gotomesh()
'OK'
>>> g_i.mesh(0.2)
'Generated 161 elements, 340 nodes'
>>> output_port = g_i.selectmeshpoints()
>>> s_o, g_o = new_server('localhost', output_port, password="yourpassword")
>>> g_o.addcurvepoint('node', g_o.Soil_1_1, (5, 5, 0))
'Added Node_A (5.0000, 5.0000, 0.0000)'
>>> g_o.update()
'OK'
>>> g_i.gotostages()
'OK'
>>> phase1 = g_i.phase(g_i.Phases[0])
>>> g_i.LineLoads[0].Active[phase1] = True
>>> g_i.calculate()
'OK'
>>> output_port = g_i.view(phase1)
>>> s_o, g_o = new_server('localhost', output_port, password="yourpassword")
>>> utot = g_o.getcurveresults(g_o.CurvePoints.Nodes.value[0],
g_o.Phases[1], g_o.Soil.Utot)
>>> print(utot)
9.68897384455302E-5
>>> g_i.save(r'c:\data\scripting_sample')
'Project saved as: c:\\data\\scripting_sample.P3D'

• The command line history on the application side looks as follows (excluding the
commands sent to Output):
0001> initializerectangular SoilContour 0 0 10 10
0002> borehole 0 0
0003> soillayer 10
0004> soilmat
0005> setproperties SoilMat_1 "SoilModel" "Linear elastic" "gammaUnsat" 16
"gammaSat" 20 "Gref" 10000
0006> set Soil_1.Material SoilMat_1
0007> gotostructures
0008> lineload (3 5 0) (7 5 0)
0009> gotomesh
0010> mesh 0.2
0011> selectmeshpoints
0012> gotostages
0013> phase InitialPhase
0014> set LineLoad_1_1.Active Phase_1 True
0015> calculate
0016> view Phase_1
0017> save "c:\data\scripting_sample"

G.6 CREATING STANDALONE SCRIPTS

In the examples above, we have looked at possibilities for using the API interactively from
a Python prompt. While this is very useful for testing and finding out how things work,
most scripts will be saved so they can be run again without having to retype it.
• In the SciTE editor make a new text file by going to File > New or by pressing
Ctrl+N. After that, you can type the code, save and run it using Tools > Go. A
Python script equivalent to the interactive session presented above looks as follows:
from plxscripting.easy import *

s_i, g_i = new_server('localhost', 21403, password="yourpassword")


s_i.new()

PLAXIS 3D 2018 | Reference Manual 475


REFERENCE MANUAL

g_i.SoilContour.initializerectangular(0, 0, 10, 10)


g_i.borehole(0, 0)
g_i.soillayer(10)
material = g_i.soilmat()
material.setproperties("SoilModel", "Linear elastic", "gammaUnsat", 16,
"gammaSat", 20, "Gref", 10000)
g_i.Soils[0].Material = material

g_i.gotostructures()
g_i.lineload((3, 5, 0), (7, 5, 0))

g_i.gotomesh()
g_i.mesh(0.2)

output_port = g_i.selectmeshpoints()
s_o, g_o = new_server('localhost', output_port, password="yourpassword")
g_o.addcurvepoint('node', g_o.Soil_1_1, (5, 5, 0))
g_o.update()

g_i.gotostages()
phase1 = g_i.phase(g_i.Phases[0])
g_i.LineLoads[0].Active[phase1] = True
g_i.calculate()

output_port = g_i.view(phase1)
s_o, g_o = new_server('localhost', output_port, password="yourpassword")
#In newer versions of the scripting layer it is possible to just write: g_o.CurvePoints.Nodes[0]
utot = g_o.getcurveresults(g_o.CurvePoints.Nodes.value[0], g_o.Phases[1], g_o.Soil.Utot)

print(utot)

g_i.save(r'c:\data\scripting_sample')

G.7 DISABLING THE USER INTERFACE

For some projects the overhead of updating the user interface may slow down the
execution of the script quite significantly. If you want maximum execution speed, launch
your PLAXIS application with the command parameter --NO_CONTROLLERS. For example:
Plaxis3DInput.exe --AppServerPort=21403 --NO_CONTROLLERS

The downside of this approach is that you will be unable to monitor progress or diagnose
problems visually. Instead, you will have to:
1. Stop the running script
2. Save the current project
3. Restart the application and open the saved project

G.8 PERFORMANCE AND LOGGING

By default the server object (typically referred to as s_i in these examples) implements a
caching system to reduce the number of calls by the script to the PLAXIS application,
thereby improving the overall performance of the system. It can be switched off using:
s_i.allow_caching = False

Additionally, a logging system is available in order to monitor the communications


between the Python script and the PLAXIS application. It can be toggled on or off using
the enable_logging()and disable_logging()methods of the server object:
s_i.enable_logging() # will write to TEMP\PlaxisScriptLogs\
s_i.disable_logging()

476 Reference Manual | PLAXIS 3D 2018


APPENDIX G - PYTHON HTTP REST API WRAPPER

G.9 INSTALLING THIRD-PARTY PYTHON MODULES

When the user want to install a third party package that is not already available in the
Python installation then you can do so by opening the command prompt from Python -
Expert - Command prompt or by selecting 'Console for PLAXIS 3D' from the PLAXIS
folder in the start menu. From there you can install new packages by either using conda
or pip.
For example, if you want to install the dropbox package you would type:
pip install dropbox

And if you want to install the cffi (C foreign function interface) package you would type:
conda install cffi

Note that not all packages that are available to pip are available to conda and some
complex packages such as numpy might be easier to install using conda than with pip.
Lastly installing new packages requires administrator rights on your PC.

G.10 EXTERNAL MANIPULATIONS DURING SCRIPT EXECUTION

While using Python scripts to manipulate the state of your PLAXIS application, it is
technically possible to interact with the same application in other ways as well (e.g. by
changing a project manually, or firing commands separately in an interpreter while the
main script is paused). This is very useful for troubleshooting.
In particular situations it may however lead to problems, in that internal data structures
built on the Python side have become obsolete due to actions outside of the script. The
main culprit is (re)opening a project while the script already has some internal
state/variables. Errors can look like this:
plxscripting.plx_scripting_exceptions.PlxScriptingError: Unsuccessful command:
GUID does not refer to object in registry: {F00F9FB0-B903-42BE-AE1F-
C7B8E200885D}

If you encounter such an error, simply stop the execution of your script and restart it.

G.10.1 SAMPLE APPLICATIONS


This section contains a few simple tasks that highlight different aspects of the Python
scripting wrapper. It is assumed PLAXIS 3D is running and the necessary connection
boilerplate code has been executed already.

Opening a project, removing all lines containing beams and saving again
s_i.open(r'c:\data\lines_with_beams.p3d')
lines = [b.Parent for b in g_i.Beams]
g_i.delete(*lines)
# alternatively (look at the difference in the command line):
# g_i.delete(lines)
g_i.save()

Reporting all points that are not used by lines and that have no features
points_in_lines = set()
for line in g_i.Lines[:]:
for point in line:
points_in_lines.add(point)

PLAXIS 3D 2018 | Reference Manual 477


REFERENCE MANUAL

points = set(g_i.Points)
points_no_lines = points - points_in_lines
points_to_report = []
for p in points_no_lines:
if len(p.UserFeatures.value) == 0:
points_to_report.append(p)
for p in points_to_report:
print(p.Name)

Creating an array, storing the results and performing actions on the


diagonal
# Code will work in any project, as it operates only on its own
# local results, regardless of what else is present in the project.
g_i.gotostructures()
point = g_i.point(0, 0, 0)
added_points = [point]
added_points.extend(g_i.arrayr(point, 6, (1, 0, 0), 6, (0, 1, 0), 6, (0, 0, 1)))
diagonal_points = []
for p in added_points:
if abs(p.x - p.y) < 1E-4 and abs(p.x - p.z) < 1E-4:
diagonal_points.append(p)

g_i.pointload(diagonal_points)

Lengthening all the embedded beams in a model


# Code below can be used to quickly generate a test model
## res = g_i.embeddedpile((0, 0, 0), (0, 0, -15))
## line = [item for item in res if item._plx_type=='Line'][0]
## g_i.arrayr(line, 5, (1, 0, 0), 5, (0, 1, 0))

def get_bottom_point(line):
first, second = line.First, line.Second
delta = second.z - first.z
if abs(delta) < 1E-4: # horizontal line
return None
if delta > 0: # Second point is higher -> move First down
return first
else: # First point is higher -> move Second down
return second

def lengthen_embedded_piles(extra_depth):
# start by selecting the lines which have embedded beams
pile_lines = [pile.Parent for pile in g_i.EmbeddedBeams[:]]

# we don't know whether the line is drawn top-to-bottom


# or bottom-to-top, so find out which point is lowest
points_to_move = []
for line in pile_lines:
bottom_point = get_bottom_point(line)
if bottom_point:
points_to_move.append(bottom_point)

g_i.move(points_to_move, (0, 0, -extra_depth))

lengthen_embedded_piles(2.5)

Making all plates have identical settings for each phase in staged
construction
refplate = g_i.Plates[0]
otherplates = g_i.Plates[1:]
for phase in g_i.Phases:
if refplate.Active[phase] is None: # phase not yet initialized
continue
for otherplate in otherplates:
otherplate.Material[phase] = refplate.Material[phase]
otherplate.Active[phase] = refplate.Active[phase]

478 Reference Manual | PLAXIS 3D 2018


APPENDIX G - PYTHON HTTP REST API WRAPPER

Create point loads on all corner points of all polygon-plates


# Code below can be used to quickly generate a test model
## g_i.plate((2, 6, 0), (2, 4, 0), (5, 4, 0), (5, 6, 0))
## g_i.surface((6, 6, 0), (6, 4, 0), (9, 4, 0))
## g_i.plate((3, 2, 0), (3, 0, 0), (5, 1, 0))
## g_i.surface((6, 2, 0), (6, 1, 0), (8, 1, 0), (8, 2, 0))

for plate in g_i.Plates[:]:


parent = plate.Parent.value
if parent._plx_type != 'Polygon': # skip non-polygons
continue

points = [p for p in parent]

# point positions are relative to the polygon's position


ref_pos = [parent.x, parent.y, parent.z]
for relative_point in points:
g_i.pointload([ref_pos[i] + relative_point[i] for i in range(3)])

Create safety phase after every consolidation phase


# Code below can be used to quickly generate a test model
## g_i.borehole(0, 0)
## g_i.soillayer(2)
## g_i.gotostages()
## phase = g_i.Phases[0]
## for i in range(10): # ten new consecutive phases
## phase = g_i.phase(phase)
## if i%2 == 1: # alternating consolidation phases
## phase.DeformCalcType = phase.DeformCalcType.consolidation

for phase in g_i.Phases[:]:


if phase.DeformCalcType == phase.DeformCalcType.consolidation:
newphase = g_i.phase(phase)
newphase.DeformCalcType = newphase.DeformCalcType.safety

Send an email using Gmail after calculations are finished


# A Gmail account is needed for this. Google Apps for Business accounts
# should work as well.
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

def opensession_gmail(username, password):


# Courtesy of http://segfault.in/2010/12/sending-gmail-from-python/
# Other email providers may require a different way of connecting.
# It may be necessary to enable access for less secure apps when
# using GMail, see https://www.google.com/settings/security/lesssecureapps
session = smtplib.SMTP('smtp.gmail.com', 587)
session.ehlo()
session.starttls()
session.ehlo()
session.login(username, password)
return session

def sendmail(smtp_session, sender_address, to_address, subject, body):


msg = MIMEMultipart()
msg['From'] = sender_address
msg['To'] = to_address
msg['Subject'] = subject
part = MIMEText('text', 'plain')
part.set_payload(body)
msg.attach(part)
smtp_session.sendmail(sender_address, to_address, msg.as_string())

def report_phases_by_kind(target_report, phases, kind, report_func):


if phases:
target_report.append(kind)
target_report.append('=' * len(kind))
for phase in phases:

PLAXIS 3D 2018 | Reference Manual 479


REFERENCE MANUAL

target_report.append(report_func(phase))
target_report.append('')

def phase_to_string(phase):
return "{} [{}]".format(phase.Identification, phase.Name)

def phase_to_string_with_error_code(phase):
return "{} [{}]. LogInfo: {}".format(
phase.Identification, phase.Name, phase.LogInfo)

def report_phases(phases_list):
successful_phases = []
failed_phases = []
uncalculated_phases = []
for phase in phases_list:
if phase.ShouldCalculate:
uncalculated_phases.append(phase)
elif phase.LogInfo == '0':
successful_phases.append(phase)
else:
failed_phases.append(phase)
report_lines = []
report_phases_by_kind(report_lines, successful_phases,
'Successful', phase_to_string)
report_phases_by_kind(report_lines, failed_phases,
'Failed', phase_to_string_with_error_code)
report_phases_by_kind(report_lines, uncalculated_phases,
'Skipped', phase_to_string)

return '\n'.join(report_lines)

def calculate_with_report():
phases_to_calculate = []

for phase in g_i.Phases[:]:


if phase.ShouldCalculate:
phases_to_calculate.append(phase)

res = g_i.calculate()
report = [
'Title: {}'.format(g_i.Project.Title),
'Location: {}'.format(g_i.Project.Filename),
'',
report_phases(phases_to_calculate),
'',
'Command feedback:',
res]

return '\n'.join(report)

message = calculate_with_report()

# change the account information below for your own account


username = '[email protected]'
password = 'abc123'
to_address = username # sending to ourselves, but can specify something else too

session = opensession_gmail(username, password)


sendmail(session, username, to_address,
'Plaxis calculation report',
message)

Executing commands specified in a file


# For debugging particular problems it may be interesting to execute
# commands directly (effectively bypassing the Python object wrapper)
# and parse feedback on demand. It may also have some performance
# benefits as the communication with the server is reduced. This way
# of communicating with the application also allows you to do your
# own error handling. The code below demonstrates several approaches
# to this task, examine the data that's printed out to better
# understand the differences.

480 Reference Manual | PLAXIS 3D 2018


APPENDIX G - PYTHON HTTP REST API WRAPPER

# Code below can be used to quickly generate a test model


s_i.new()
## from tempfile import NamedTemporaryFile
## import os
## f = NamedTemporaryFile(mode='w', delete=False)
## f.write('\n'.join(['bh 0 0', 'sl 2', 'beam 0 0 0 1 1 1']))
## filename = f.name

f = open(filename, 'r') # filename should be defined in advance


commands = [line.rstrip() for line in f.readlines()]
f.close()

# If you used the quickly generated test model, also enable


# the cleaning below:
## os.remove(filename)

# Run one command at a time and have the wrapper handle the response
# for you - not much extra control.
for command in commands:
print(s_i.call_and_handle_command(command))

# Run multiple commands at a time and have the wrapper handle


# the responses for you. There is less communication with the
# server than when executing one command at a time so it's
# potentially faster.
print(s_i.call_and_handle_commands(*commands))

# Run one command at a time and handle the response yourself. The
# response now consists of Python primitives representing the
# JSON communication rather than comfortable objects.
for command in commands:
print(s_i.call_commands(command))

# Run multiple commands at a time and handle the responses yourself.


print(s_i.call_commands(*commands))

# call_commands will not lead to an exception in case of invalid


# commands, unlike the other method calls presented.
print(s_i.call_commands('this is an invalid command'))

Creating a polar array of inclined piles


from math import radians, sin, cos

s_i.new()
g_i.gotostructures()
# input parameters
nr_piles = 4 # must be > 1
total_angle_deg = 180 # over which to distribute the piles, must be > 0 and <= 360
pile_length = 10
pile_inclination_deg = 15
array_center = (5, 5, 0)
array_radius = 10

# calculations
def rel_coords_to_absolute(rel_coords, radius, offset):
coords = [radius * coord for coord in rel_coords]
return [p[0] + p[1] for p in zip(coords, offset)]

if total_angle_deg == 360: # fencepost problem -> decrease angle


total_angle_deg = total_angle_deg / (nr_piles) * (nr_piles - 1)

pile_inclination_rad = radians(pile_inclination_deg)
array_bottom_radius = array_radius + \
pile_length * sin(pile_inclination_rad)
array_bottom_center = (array_center[0], array_center[1],
array_center[2] - pile_length * cos(pile_inclination_rad))
pile_coords = []
for i in range(nr_piles):
angle = radians(total_angle_deg * i / (nr_piles - 1))
rel_coords = [cos(angle), sin(angle), 0]

PLAXIS 3D 2018 | Reference Manual 481


REFERENCE MANUAL

top_coords = rel_coords_to_absolute(
rel_coords, array_radius, array_center)
bottom_coords = rel_coords_to_absolute(
rel_coords, array_bottom_radius, array_bottom_center)
pile_coords.append((top_coords, bottom_coords))

for coords_pair in pile_coords:


g_i.embeddedpile(*coords_pair)

Identifying to which PLAXIS connection is established


With s being a server-type object as returned by the new_server(...) method, the
following code must be valid:

>>> print(s.name, s.major_version, s.minor_version, s.is_2d, s.is_3d)


PLAXIS 2D 2018 0 True False
>>> if s.major_version + s.minor_version / 10 > 2020: # i.e. both are numbers
... print('We come from the future')
We come from the future
>>> from .const import PLAXIS_2D, PLAXIS_3D # later to be extended with MPM!
>>> if s.name == PLAXIS_2D:
... print('This is PLAXIS 2D')
... elif s.is_3d: # indicates whether the application is 2D or 3D *capable* (not whether it's the 2D or 3D *product*; 2
... print('This is 3D or 3D MPM')
... else:
... print('Unknown application!')
This is PLAXIS 2D

Using both Input and Output at the same time


# Sometimes you want to also automate the selection of history points
# or the retrieval of results in Output.
s_i, g_i = new_server('localhost', 10000, password="yourpassword")
s_i.open(r'C:\Users\x\Documents\PlaxisProjects\some_existing_project.p2dx')

# Calling the view command while Input has a running server will also start
# Output with a server and return the port that the new server uses.
# The commands preview, view and viewmesh will behave similarly.
output_port = g_i.selectmeshpoints()

# Connect to the Output server the same way we connect to the Input one.
s_o, g_o = new_server('localhost', output_port, password="yourpassword")

# Select a history point and close Output with the update command.
g_o.addcurvepoint('node', g_o.Soil_1_1, 0, 0)
g_o.update()

Computing a derived result from existing result types


This example shows how to compute a derived result starting from standard result types.
This code requires existing results to be processed.
import math

from plxscripting.easy import new_server

s_i, g_i = new_server('localhost', 10000, password='yourpassword')

last_phase = g_i.Phases[-1]
ux = g_i.getresults(last_phase, g_i.ResultTypes.Soil.Ux, 'node')
uy = g_i.getresults(last_phase, g_i.ResultTypes.Soil.Uy, 'node')

displacement_angles = [math.atan2(y, x) for x, y in zip(ux, uy)]

print(displacement_angles)

482 Reference Manual | PLAXIS 3D 2018


APPENDIX G - PYTHON HTTP REST API WRAPPER

Receiving an image in the scripting layer created by Output


This code requires existing results to be processed.
from plxscripting.easy import new_server

s_o, g_o = new_server('localhost', 10000, password='yourpassword')

newest_plot = g_o.Plots[-1]
newest_plot.ResultType = g_o.ResultTypes.Soil.Utot
newest_plot.PlotType = 'shadings'
newest_plot.Phase = g_o.Phases[-1]

#image_wrapper is an object that can save the created


#image or, if Pillow is installed, you can get the internal
#Pillow.Image object and use that.
image_wrapper = newest_plot.export(1600, 1200)

try:
from PIL import ImageFilter
pil_image = image_wrapper.image
new_image = pil_image.filter(ImageFilter.BLUR)
new_image.save("test.png")
except ImportError:
#Just save if we don't have Pillow
image_wrapper.save("test.png")

A class that can be used to quickly create simple projects


The following code demonstrates a possible approach to building a model template class,
that can be tweaked in subclasses in order to create model variants without duplicating
large amounts of code. Other examples in this document will build upon this class. Make
sure you save it as simple_project.py in the same directory where you save the examples
that need it.
import os
import subprocess

INPUT_SERVER_PORT = 10000
PLAXIS_PATH = r"C:\Program Files\Plaxis\PLAXIS 3D"
class SimpleProject(object):
"""
Class that provides a way to quickly setup a project for example purposes.
"""
def __init__(self, g_i):
from plxscripting.easy import new_server
self._new_server = new_server

args = [os.path.join(PLAXIS_PATH, "Plaxis3DInput.exe"),


"--AppServerPort={}".format(INPUT_SERVER_PORT)]
self._input_process = subprocess.Popen(args)

self._s_i, self._g_i = self._new_server(


'localhost', password="yourpassword", INPUT_SERVER_PORT, timeout=10.0)

def gather_results(self):
raise NotImplementedError("Override gather_results in subclass.")

def output_results(self):
raise NotImplementedError("Override output_results in subclass.")

def close_input(self):
self._input_process.kill()

@property
def g_i(self):
return self._g_i

def add_soil_layers(self):
raise NotImplementedError("Override add_soil_layers in subclass.")

PLAXIS 3D 2018 | Reference Manual 483


REFERENCE MANUAL

def apply_soil_material(self):
SAND_PARAMETERS = [
('MaterialName', 'Sand'),
('Colour', 10676870),
('SoilModel', 3), # Hardening soil
('DrainageType', 'Drained'),
('gammaUnsat', 17),
('gammaSat', 20),
('E50ref', 43000),
('EoedRef', 28000),
('EurRef', 129000),
('powerm', 0.5),
('cref', 1),
('phi', 34.0),
('psi', 4.0),
('nu', 0.2),
('Rinter', 0.7),
('K0NC', 0.5),
('OCR', 1.0),
('POP', 0.0)
]
sand = self._g_i.soilmat(*SAND_PARAMETERS)

for soil_layer in self._g_i.SoilLayers:


self._g_i.setmaterial(soil_layer, sand)

def add_structures(self):
pass # Not adding any plates is fine too.

def apply_plate_material(self):
DIAPHRAGM_WALL_PARAMETERS = [
('MaterialName', 'Wall'),
('Colour', 16711680),
('IsIsotropic', True),
('IsEndBearing', True),
('E1', 30000000),
('E2', 30000000),
('nu12', 0.15),
('d', 0.5),
('w', 15),
('G12', 13040000),
('G13', 13040000),
('G23', 13040000),
('RayleighAlpha', 0),
('RayleighBeta', 0)
]
diaphragm_wall_material = self._g_i.platemat(*DIAPHRAGM_WALL_PARAMETERS)

for plate in self._g_i.Plates:


self._g_i.setmaterial(plate, diaphragm_wall_material)

def mesh(self):
self._g_i.gotomesh()
self._g_i.mesh(0.4)

def select_curve_points(self):
pass # Not selecting any curve-points is fine too.

def configure_phases(self):
raise NotImplementedError("Override configure_phases in subclass.")

def make_project(self):
self._s_i.new()
self.add_soil_layers()
self.apply_soil_material()
self.add_structures()
self.apply_plate_material()
self.mesh()
self.select_curve_points()
self.configure_phases()
self._g_i.calculate()

484 Reference Manual | PLAXIS 3D 2018


APPENDIX G - PYTHON HTTP REST API WRAPPER

def run(project_class):
# Replace with the path to your PLAXIS installation.
project = project_class(r"c:\Program Files (x86)\Plaxis\PLAXIS 3D")
project.make_project()
project.gather_results()
project.output_results()
project.close_input()

Create a csv file from curve results


This example must be saved in the same directory as the simple_project.py module
created in a previous paragraph.
import sys
import csv

from simple_project import SimpleProject, run


class ExportCSVProject(SimpleProject):
def add_soil_layers(self):
self._g_i.SoilContour.initializerectangular(0, 0, 100, 30)

borehole = self._g_i.borehole(0,0)
self._g_i.soillayer(0)
self._g_i.setsoillayerlevel(borehole, 0, 30)
self._g_i.setsoillayerlevel(borehole, 1, 27)
self._g_i.soillayer(0)
self._g_i.setsoillayerlevel(borehole, 2, 15)
self._g_i.soillayer(0)
self._g_i.setsoillayerlevel(borehole, 3, 0)
borehole.Head = 23

def add_structures(self):
self._g_i.gotostructures()
self._g_i.plate(40, 0, 30, 40, 30, 30, 40, 30, 14, 40, 0, 14)
self._g_i.plate(60, 0, 30, 60, 30, 30, 60, 30, 14, 60, 0, 14)

for plate in self._g_i.Plates:


self._g_i.posinterface(plate.Parent)
self._g_i.neginterface(plate.Parent)

self._g_i.surfload(28, 0, 30, 28, 30, 30, 38, 30, 30)

def select_curve_points(self):
output_port = self._g_i.selectmeshpoints()
s_o, g_o = self._new_server('localhost', output_port, password="yourpassword")
g_o.addcurvepoint('node', 40, 0, 30)
g_o.update()

def configure_phases(self):
self._g_i.gotostages()

self._g_i.activate(self._g_i.SurfaceLoads, self._g_i.InitialPhase)
self._g_i.activate(self._g_i.Plates, self._g_i.InitialPhase)
self._g_i.activate(self._g_i.Interfaces, self._g_i.InitialPhase)

self._g_i.SoilPolygons[1], self._g_i.InitialPhase

surface_load = self._g_i.SurfaceLoads[0]

current_phase = self._g_i.InitialPhase
surface_load.sigz[current_phase] = 0.0

for i in range(1, 10):


current_phase = self._g_i.phase(current_phase)
self._g_i.setcurrentphase(current_phase)
surface_load.sigz[current_phase] = -10.0 * i

def gather_results(self):
output_port = self._g_i.view(self._g_i.InitialPhase)
s_o, g_o = self._new_server('localhost', output_port, password="yourpassword")

PLAXIS 3D 2018 | Reference Manual 485


REFERENCE MANUAL

self._results = []

# phaseID is only correct because we know the phases are in order!


for phaseID, phase in enumerate(g_o.Phases):
if phaseID == 0:
# Skip the initial phase and just write 0, because getcurveresults
# won't return any results for it.
self._results.append({
'phaseID': 0,
'displacement': 0
})
else:
displacement_string = g_o.getcurveresults(g_o.Nodes[0],
phase, g_o.ResultTypes.Soil.Utot)
self._results.append({
'phaseID': phaseID,
'displacement': displacement_string
})

g_o.update()

def output_results(self):
# Prevent excess newlines.
if sys.version_info.major == 2:
file_kwargs = { 'mode': 'wb' }
else:
file_kwargs = { 'mode': 'w', 'newline': '' }

# By default the csv writers will separate values with a comma and the
# period will be used as the decimal separator.
# However, this might be problematic when trying to import a csv file
# on a system that uses comma as the decimal separator.
# In these cases do the following:
# - import the locale modules and call: locale.setlocale(locale.LC_ALL, '')
# - get the correct separator with: locale.localeconv()['decimal_point']
# - replace the period in the output results with the correct separator
# - use a different csv dialect (see Python docs) so the comma isn't used
# as the column separator.
with open("results.csv", **file_kwargs) as csv_file:
writer = csv.DictWriter(csv_file, fieldnames=['phaseID', 'displacement'])
for row in self._results:
writer.writerow(row)

if __name__ == '__main__':
run(ExportCSVProject)

Create a combined image of the mesh for two different phases


This example must be saved in the same directory as the simple_project.py module
created in a previous paragraph.
from simple_project import SimpleProject, run

from PIL import Image, ImageFont, ImageDraw


class ExportPlotProject(SimpleProject):
def add_soil_layers(self):
self._g_i.borehole(0, 0)
self._g_i.soillayer(4)
def add_structures(self):
self._g_i.gotostructures()
self._g_i.plate(6, 0, 0, 6, 8, 0, 6, 8, -2, 6, 0, -2)

self._g_i.lineload(6, 0, 0, 6, 8, 0)
self._g_i.LineLoads[0].qz_start = -10
def configure_phases(self):
self._g_i.gotostages()
self._g_i.activate(self._g_i.Plates, self._g_i.InitialPhase)

486 Reference Manual | PLAXIS 3D 2018


APPENDIX G - PYTHON HTTP REST API WRAPPER

next_phase = self._g_i.phase(self._g_i.InitialPhase)
self._g_i.activate(self._g_i.LineLoads, next_phase)

def gather_results(self):
width, height = 800, 600

output_port = self._g_i.view(self._g_i.InitialPhase)
s_o, g_o = self._new_server('localhost', output_port, password="yourpassword")

# Hide everything except the mesh itself.


g_o.Plots[0].DrawFrame = False
g_o.Plots[0].DrawTitle = False
g_o.Plots[0].DrawLegend = False
g_o.Plots[0].DrawAxes = False
g_o.Plots[0].MeshSettings.Materials = True
g_o.Plots[0].MeshSettings.ElementContours = False
g_o.Plots[0].MeshSettings.PhreaticLevel = False

g_o.Plots[0].Phase = g_o.InitialPhase
self._initial_phase_image = g_o.Plots[0].export(width, height)

g_o.Plots[0].Phase = g_o.Phase_1
self._phase_1_image = g_o.Plots[0].export(width, height)

g_o.close()

def output_results(self):
# Get Pillow.Image objects.
inner_initial_image = self._initial_phase_image.image
inner_phase_1_image = self._phase_1_image.image

assert inner_initial_image.size == inner_phase_1_image.size

cropping_left = (0, 0,
inner_initial_image.width // 2, inner_initial_image.height)
left_image = inner_initial_image.crop(cropping_left)

cropping_right = (inner_phase_1_image.width // 2, 0,
inner_phase_1_image.width, inner_phase_1_image.height)
right_image = inner_phase_1_image.crop(cropping_right)

result_image = Image.new(
inner_initial_image.mode, inner_initial_image.size)
result_image.paste(left_image, cropping_left)
result_image.paste(right_image, cropping_right)

default_font = ImageFont.load_default()
drawing_context = ImageDraw.Draw(result_image)

initial_phase_ID = self._g_i.Phases[0].Identification.value
drawing_context.text((200, 160), initial_phase_ID,
font=default_font, fill=(0, 0, 0, 255))

phase_1_ID = self._g_i.Phases[1].Identification.value
after_text_size = default_font.getsize(phase_1_ID)
drawing_context.text((600 - after_text_size[0], 160),
phase_1_ID, font=default_font, fill=(0, 0, 0, 255))

result_image.save("plot_initial_vs_phase_1.png")

if __name__ == '__main__':
run(ExportPlotProject)

Create a matplotlib plot from a line cross-section


This example must be saved in the same directory as the simple_project.py module
created in a previous paragraph.
from simple_project import SimpleProject, run

from matplotlib import pyplot

PLAXIS 3D 2018 | Reference Manual 487


REFERENCE MANUAL

SAMPLE_COUNT = 16

class CrossSectionProject(SimpleProject):
def add_soil_layers(self):
self._g_i.borehole(0, 0)
self._g_i.soillayer(4)

def add_structures(self):
self._g_i.gotostructures()
self._g_i.surfload(5, 0, 0, 5, 8, 0, 7, 8, 0, 7, 0, 0)

def configure_phases(self):
self._g_i.gotostages()
self._g_i.activate(self._g_i.Plates, self._g_i.InitialPhase)

next_phase = self._g_i.phase(self._g_i.InitialPhase)
self._g_i.activate(self._g_i.SurfaceLoads, next_phase)

def gather_results(self):
start = (0.0, 4.0, 0.0)
end = (12.0, 4.0, 0.0)

output_port = self._g_i.view(self._g_i.Phases[-1])
s_o, g_o = self._new_server('localhost', output_port, password="yourpassword")

# Assumes that start and end contain floats, will not work correctly
# with integers in Python 2.
step = [(e - s) / (SAMPLE_COUNT - 1) for e, s in zip(end, start)]

self._results = []
for i in range(SAMPLE_COUNT):
position = (start[0] + i * step[0], start[1], start[2])
result_string = g_o.getsingleresult(g_o.Phases[-1],
g_o.ResultTypes.Soil.Utot, position)
# Check if position lies outside the mesh, it might make more sense
# to use 0 as the result for other result types.
if result_string == "not found":
raise Exception("Used getsingleresult for point outside mesh.")
self._results.append(float(result_string))
g_o.update()
def output_results(self):
pyplot.plot(range(len(self._results)), self._results, 'r')
pyplot.grid(True)
pyplot.show()
if __name__ == '__main__':
run(CrossSectionProject)

488 Reference Manual | PLAXIS 3D 2018

You might also like