Soil Layering for a Single Borehole with GIBR
Download: #
Preamble: #
This is a simple script for creating single borehole soil layering with GIBR information, with limited use cases.
I have used this script for bored tunnels analysis, which typically considers a single borehole only.
It also serves as a proof-of-concept for automating soil inputs in PLAXIS.
Function: #
The Python script is written to create soil layers and properties for a single borehole, given GIBR properties.
- Read a manually created GIBR table, considering variables SPT-N and upper limits.
- Create a single borehole based on variable depth.
- Create Drained MC soil properties based on variable SPT-N.
Example GIBR Input: #
An example GIBR input, based on a local (Singapore) project.
3 symbols are used beside numbers.
- N: represents SPT-N, which needs not be an integer.
- +: allows addition of variable SPT-N with constants.
- ^: sets an upper limit to the calculated number.
Example: The formula 3N+50^250 will input 3N+50 to a maximum of 250.
Example Borehole Input: #
An example borehole input, based on a local (Singapore) project. Northing and Easting are not required for this program to work.
Using the Script: #
A folder named "Soil Data" must be created on the Desktop.
A file containing the GIBR input should be saved as GIBR.csv in the folder. The rest of the files are to be borehole inputs.
Run the script on a new model. A selection screen will be created for user to choose a borehole.
Generated Model: #
The soil layering with the appropriate GIBR soil properties are generated based on the user's inputs.
Script: #
import plxscripting.easy
import easygui
import csv
import os
class MakeSoil():
def __init__(self, g_i):
self.g_i = g_i
def run(self):
#1. Code for Inputs
desktop_path = os.path.join(os.path.expanduser("~"), "Desktop")
folder_path = os.path.join(desktop_path, "Soil Data")
onedrive_desktop_path = os.path.join(os.path.expanduser("~"), "OneDrive\\Desktop")
onedrive_folder_path = os.path.join(onedrive_desktop_path, "Soil Data")
try:
selected_file = select_file_in_folder(folder_path)
except:
selected_file = select_file_in_folder(onedrive_folder_path)
folder_path = onedrive_folder_path
borehole_1 = g_i.borehole(0)
g_i.setproperties("UnitTime", "s")
g_i.SoilContour.initializerectangular(-75,0,75,10)
#1.1. Record GIBR
csv_GIBR_path = os.path.join(folder_path, "GIBR.csv")
# Initialize an empty list to store the dictionaries
GIBRData = []
# Open the CSV file and read it as a dictionary
with open(csv_GIBR_path, 'r', encoding='utf-8-sig') as csv_file: # Use utf-8-sig to strip off the BOM
# Create a CSV reader object
csv_reader = csv.DictReader(csv_file)
# Initialize a variable to keep track of the row number
row_number = 1
# Iterate through each row in the CSV file
for row in csv_reader:
# Check if it's the second row
if row_number == 1:
units = row # Save the second row as a dictionary with the name "units"
else:
# Append the row (dictionary) to the data list
GIBRData.append(row)
# Increment the row number
row_number += 1
#End of GIBR
#1.2. Read Soil Data and Generate the Soil Material
csv_soil_path = selected_file
g_i.soillayer(0)
g_i.Soillayers[0].Zones[0].Top.set(10)
# Open the CSV file and read it as a dictionary
with open(csv_soil_path, 'r', encoding='utf-8-sig') as csv_file: # Use utf-8-sig to strip off the BOM
# Create a CSV reader object
csv_reader = csv.reader(csv_file)
# Initialize a variable to keep track of the row number
row_number = 1
# Iterate through each row in the CSV file
for row in csv_reader:
if (row_number == 4):
g_i.Soillayers[0].Zones[0].Top.set(float(row[1]))
if row_number >6:
if(len(row[0].strip())!=0):
bottomOfLayer = float (row[0])
layer = row[1].strip()
spt = 0
try:
spt = float(row[2])
except ValueError:
spt = 0
#add the layers.
if(row_number != 7): #if not the first layer
g_i.soillayer(0) #add a layer
g_i.Soillayers[row_number-7].Zones[0].Bottom.set(bottomOfLayer) #set the bottom of the layer
for material in GIBRData:
if(material["Material"] == layer):
#add SPT name if soil is SPT dependent.
if(checkIfNDependent(material)):
layer = layer + " N = " + str(truncate_float_to_one_decimal_point(spt))
#check if soil exists.
soilExists = False
for existingMaterial in g_i.Materials:
if(existingMaterial.Identification == layer):
g_i.Soillayers[row_number-7].Soil.Material = existingMaterial
soilExists = True
break
if(not soilExists):
material1 = g_i.soilmat()
material1.setproperties(
"Identification",layer,
"SoilModel",2,
"gammaUnsat", parseGIBRData(material["Weight"], spt),
"gammaSat", parseGIBRData(material["Weight"], spt),
"Eref",1000*parseGIBRData(material["E"], spt),
"nu", parseGIBRData(material["v"], spt),
"cref", parseGIBRData(material["c"], spt),
"phi", parseGIBRData(material["phi"], spt),
"PermHorizontalPrimary", parseGIBRData(material["k"], spt),
"PermVertical", parseGIBRData(material["k"], spt),
"InterfaceStrengthDetermination", "Manual",
"Rinter", 0.67,
"K0Determination", "Manual",
"K0Primary", parseGIBRData(material["K0"], spt),
#"K0Primary", parseGIBRData(material["Ktunnel"], spt),
"Colour", round(parseGIBRData(material["Colour"], round(spt))),
)
g_i.Soillayers[row_number-7].Soil.Material = material1
print(row)
# Increment the row number
row_number += 1
selectionClose = easygui.buttonbox(msg="Soil profile was created based on "+ csv_soil_path+".", title='Close the window', choices = ["Close","About"])
if selectionClose == "About":
easygui.msgbox(msg='v1.0, released 11/11/2023\n\nChangelog:\nInitial Release.\n\n- Chen Teck, 2023',title='About',ok_button='OK')
def parseGIBRData(string, spt):
value = 0
if 'N' in string:
split_values = string.split('N')
try:
parsed_float = float(split_values[0].strip())
value = spt * parsed_float
except:
print("A term contains N but do not contain a prefix, please check.")
if '^' in string:
split_values = string.split('^')
try:
parsed_float = float(split_values[-1].strip())
if (value > parsed_float):
return parsed_float
else:
return value
except:
print("A term contains ^ but do not contain a suffix, please check.")
return value
if '+' in string:
split_values = string.split('+')
try:
parsed_float = float(split_values[-1].strip())
return value + parsed_float
except:
print("A term contains + but do not contain a suffix, please check.")
return value
else:
return value
else:
try:
value = float(string)
return value
except:
print("A term is unable to be parsed, please check.")
return value
def checkIfNDependent(dictionary):
char_to_check = 'N'
found = False
# Loop through each string in the list
for string in dictionary.values():
# Check if the character is in the string
if char_to_check in string:
found = True
break
return found
def truncate_float_to_one_decimal_point(value):
truncated_value = round(value, 1)
return int(truncated_value) if truncated_value.is_integer() else truncated_value
def select_file_in_folder(folder_path):
# Get a list of all files in the folder
files = os.listdir(folder_path)
filtered_list = [item for item in files if item != "GIBR.csv"]
# Display a choice box with the list of files
choice = easygui.choicebox("Select a borehole file:", "File Selection", filtered_list)
# If the user selects a file, return the full path to the selected file
if choice:
selected_file_path = os.path.join(folder_path, choice)
return selected_file_path
else:
return None
if __name__ == "__main__":
s_i, g_i = plxscripting.easy.new_server()
soil = MakeSoil(g_i)
soil.run()
Customization #
By default, the input K value refers to the K0 value in column F. For tunnel structural analysis, a Ktunnel could be used instead, which refers to a distortional factor.
Original Excerpt, considering K=K0
"K0Primary", parseGIBRData(material["K0"], spt),
#"K0Primary", parseGIBRData(material["Ktunnel"], spt),
If the intention is to use the Ktunnel, we can switch the '#' and the script will now run the other line.
Modified Excerpt:
#"K0Primary", parseGIBRData(material["K0"], spt),
"K0Primary", parseGIBRData(material["Ktunnel"], spt),
- Previous: [ERSS] Exporting Strut Data
- Next: PLAXIS 2024.1 Syntax Changes