├── .gitignore ├── LICENSE ├── README.md ├── calcs.py ├── raw_data_example.png ├── raw_grapher.py ├── sbt_index.py └── sbt_profile_example.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2019, Coding Geologist 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CPT-Data-Visualisation 2 | Geotechnical Cone Penetration Testing Data Visualisation 3 | 4 | Reference Book on CPT techniques and analysis: T. Lunne , J.J.M Powell, P.K. Robertson: Cone Penetration Testing in Geotechnical Practice [Amazon Link](https://www.amazon.co.uk/Cone-Penetration-Testing-Geotechnical-Practice/dp/041923750X) 5 | 6 | Three Python scripts have been created to perform the calculations and data visualisation based on the techniques provided by Lunne et al 7 | and according to Geotechnical industry practice (British Standard: BS EN ISO 22476-1:2012) 8 | 9 | The Raw data is loaded into Python from *.xls files*, with four columns of data representing the required datasets. The first row of each of the columns saved within the excel files assumes a header with the standard [AGS4](https://www.ags.org.uk/) headers. 10 | 1) Test Depth (SCPT_DPTH). 11 | 2) Tip Resistance (SCPT_RES). 12 | 3) Cone Sleeve Friction (SCPT_FRES). 13 | 4) Measured Pore Pressure (U2 position, behind the tip) (SCPT_PWP2). 14 | 15 | Calculations: 16 | This is performed within the *calcs.py* script. The user should satisfy themselves that the assumptions used for the calculations are repreentative of the ground conditions. 17 | 18 | Graphing: 19 | The *raw_grapher.py* script has been setup to import the requried parameters for producing the standard tip resistance, sleeve friction and pore pressure plots. The resuting plots will be saved as a *.png file* within the specified root folder. 20 | 21 | Soil Behaviour Type Index: 22 | The *sbt_index.py* script reads the calculated *Ic - Soil Behavior Type Index* arrays and plots the data with the representative descriptions based on the industry standard guidance and Lunne et al. Two plots are saved as a *.png file* within the specified root folder, illustrating the test trace as well as an _interpreted_ soil column. 23 | -------------------------------------------------------------------------------- /calcs.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | from tkinter import Tk 4 | from tkinter.filedialog import askopenfilename 5 | ########################################################################################################################################################################## 6 | Tk().withdraw() 7 | file = askopenfilename(filetypes=[('EXCEL Files','*.xls')]) 8 | data_source = pd.read_excel(file,header=0,true_values=True) 9 | #data_source 10 | ########################################################################################################################################################################## 11 | #Data_Source 12 | z = data_source['SCPT_DPTH'] #Depth 13 | qc = data_source['SCPT_RES'] #Cone Resistance 14 | fs = data_source['SCPT_FRES'] #Sleeve Friction 15 | u2 = data_source['SCPT_PWP2'] #U2 pore pressure 16 | ########################################################################################################################################################################## 17 | #Assumptions 18 | title = 'CPT01' #CPT test number 19 | gwl = 3.5 #ground water level 20 | soil_den = 18 #soil density 21 | max_depth = 30 #Maximum depth to display plots 22 | ########################################################################################################################################################################## 23 | #Calculations 24 | u0 = [0] #Hydrostatic water pressure 25 | for i in np.arange(len(z)): 26 | if z[i] < gwl: 27 | u0.append(0) 28 | elif z[i] > gwl: 29 | u0.append((z[i] - gwl)*10) 30 | 31 | udl = (u2 - u0) #U_delta 32 | sig_vo = z * soil_den #Total Vertical Stress 33 | sig1_vo = sig_vo - u0 #Effective Vertical Stress 34 | np.seterr(divide='ignore') #Ignore math errors 35 | Rf = (fs/qc) * 100 #Friction Ratio 36 | qt = (qc*1000) + (0.21+u2) #qt kPa 37 | qn = qt - sig_vo #net qc 38 | Bq = udl/(qn) #Pore pressure ratio 39 | qtm = qt/1000 #qt MPa 40 | Su_15 = qn / 15 #Su Nkt = 15 41 | Su_15 = qn / 20 #Su Nkt = 20 42 | qt_norm = qn / sig1_vo #normalised qc 43 | fr_norm = ((fs * 1000) / qn) * 100 #normalised fs 44 | ic = np.sqrt(np.power(((3.47-(np.log10(qc/0.1)))),2) + np.power(((np.log10(Rf)+1.22)),2)) #Soil Behaviour Type Index 45 | ########################################################################################################################################################################## -------------------------------------------------------------------------------- /raw_data_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingeologist/CPT-Data-Visualisation/49326faf7b1e94b8b35eacd264edbfcc08e5a682/raw_data_example.png -------------------------------------------------------------------------------- /raw_grapher.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | from calcs import max_depth, z, qc, fs, u2, u0, Rf, Bq 3 | ########################################################################################################################################################################## 4 | #Initialise Figures 5 | fig = plt.figure(1,figsize=(20,20),dpi=300) 6 | ax1 = plt.subplot2grid((1,10),(0,0),rowspan=1,colspan=2) 7 | ax1_1 = ax1.twiny() 8 | ax2 = plt.subplot2grid((1,10),(0,4),rowspan=1,colspan=2) 9 | ax2_1 = ax2.twiny() 10 | ax3 = plt.subplot2grid((1,10),(0,8),rowspan=1,colspan=2) 11 | ax3_1 = ax3.twiny() 12 | ########################################################################################################################################################################## 13 | #Set axes limits 14 | ax1.set_xlim(0,40) 15 | ax1.set_ylim(max_depth,0) 16 | ax1.set_ylabel('Depth (m)') 17 | ax1.set_xlabel('qc (MPa)') 18 | ax1_1.set_xlim(0,1) 19 | ax1_1.set_ylim(max_depth,0) 20 | ax1_1.set_xlabel('fs (MPa)') 21 | 22 | ax2.set_xlim(0,10) 23 | ax2.set_ylim(max_depth,0) 24 | ax2.set_ylabel('Depth (m)') 25 | ax2.set_xlabel('Excess Pore Pressure (kPa)') 26 | ax2_1.set_xlim(0,300) 27 | ax2_1.set_ylim(max_depth,0) 28 | ax2_1.set_xlabel('Hydrostatic (kPa)') 29 | 30 | ax3.set_xlim(-10,10) 31 | ax3.set_ylim(max_depth,0) 32 | ax3.set_ylabel('Depth (m)') 33 | ax3.set_xlabel('Friction Ratio') 34 | ax3_1.set_xlim(-5,5) 35 | ax3_1.set_ylim(max_depth,0) 36 | ax3_1.set_xlabel('Pore Pressure Ratio') 37 | ########################################################################################################################################################################## 38 | #Plot axes grids 39 | ax1.grid(True) 40 | ax2.grid(True) 41 | ax3.grid(True) 42 | ########################################################################################################################################################################## 43 | #Data Plots 44 | ax1.plot(qc,z,'k-',lw=0.5) 45 | ax1_1.plot(fs,z,'r-',lw=0.5) 46 | 47 | ax2.plot(u2,z,'b-',lw=0.5) 48 | ax2_1.plot(u0,z,'r-',lw=0.5) 49 | 50 | ax3.plot(Rf,z,'k-',lw=0.5) 51 | ax3_1.plot(Bq,z,'b-',lw=0.5) 52 | ########################################################################################################################################################################## 53 | #Save and close figures 54 | fig.savefig('./raw_data.png',bboxinches='tight') 55 | plt.close(fig) 56 | ########################################################################################################################################################################## -------------------------------------------------------------------------------- /sbt_index.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import matplotlib.patches as mpatches 4 | from calcs import ic, max_depth, z 5 | ########################################################################################################################################################################## 6 | #Check Soil Behaviour Type 7 | sbt = [] 8 | for i in np.arange(len(ic)): 9 | 10 | if ic[i] > 3.6: 11 | sbt.append('SENS') 12 | elif ic[i] > 3: 13 | sbt.append('CLAY') 14 | elif ic[i] > 2.6: 15 | sbt.append('Silty CLAY') 16 | elif ic[i] > 2: 17 | sbt.append('Silty SAND + Sandy SILT') 18 | elif ic[i] > 1.4: 19 | sbt.append('Silty SAND') 20 | elif ic[i] > 1: 21 | sbt.append('SAND') 22 | else: 23 | sbt.append('Undefined') 24 | ########################################################################################################################################################################## 25 | #Initialise Figures 26 | fig2 = plt.figure(2,figsize=(20,20),dpi=300) 27 | ax4 = plt.subplot2grid((1,10),(0,0),rowspan=2,colspan=8) 28 | ax5 = plt.subplot2grid((1,10),(0,8),rowspan=1,colspan=2) 29 | ########################################################################################################################################################################## 30 | #Set axes limits 31 | ax4.set_ylim(max_depth,0) 32 | ax4.set_xlim(1,4) 33 | ax4.set_ylabel('Depth (m)') 34 | ax4.set_xlabel('SBT Ic') 35 | ax4.xaxis.set_ticks_position('none') 36 | 37 | ax5.set_ylim(max_depth,0) 38 | ax5.set_xlim(0,3.6) 39 | ax5.xaxis.set_ticks_position('none') 40 | ax5.yaxis.set_ticks_position('none') 41 | ax5.set_xticklabels([]) 42 | ax5.set_yticklabels([]) 43 | ########################################################################################################################################################################## 44 | #Soil Behaviour Type Legend 45 | sbt_legend_dict = {'SAND' : 'gold','Silty SAND' : 'tan','Silty SAND + Sandy SILT' : 'sandybrown','Silty CLAY' : 'palegreen','CLAY' : 'olivedrab','SENS' : 'royalblue'} 46 | sbtpatchList = [] 47 | for key in sbt_legend_dict: 48 | sbt_key = mpatches.Patch(color=sbt_legend_dict[key],label=key) 49 | sbtpatchList.append(sbt_key) 50 | ax4.legend(handles=sbtpatchList) 51 | ########################################################################################################################################################################## 52 | #SBT Legends 53 | ax4.fill([1,1,1.4,1.4],[0,max_depth,max_depth,0],'gold',alpha=0.2,edgecolor='r') 54 | ax4.fill([1.4,1.4,2,2],[0,max_depth,max_depth,0],'tan',alpha=0.2,edgecolor='r') 55 | ax4.fill([2,2,2.6,2.6],[0,max_depth,max_depth,0],'sandybrown',alpha=0.2,edgecolor='r') 56 | ax4.fill([2.6,2.6,3,3],[0,max_depth,max_depth,0],'palegreen',alpha=0.2,edgecolor='r') 57 | ax4.fill([3,3,3.6,3.6],[0,max_depth,max_depth,0],'olivedrab',alpha=0.2,edgecolor='r') 58 | ax4.fill([3.6,3.6,4,4],[0,max_depth,max_depth,0],'royalblue',alpha=0.2,edgecolor='r') 59 | ########################################################################################################################################################################## 60 | #SBT Profile 61 | 62 | for i in np.arange(len(ic)): 63 | 64 | if ic[i] > 3.6: 65 | ax5.plot([0,3.6],[z[i],z[i]],'royalblue',lw=4) 66 | elif ic[i] > 3: 67 | ax5.plot([0,3],[z[i],z[i]],'olivedrab',lw=4) 68 | elif ic[i] > 2.6: 69 | ax5.plot([0,2.6],[z[i],z[i]],'palegreen',lw=4) 70 | elif ic[i] > 2: 71 | ax5.plot([0,2],[z[i],z[i]],'sandybrown',lw=4) 72 | elif ic[i] > 1.4: 73 | ax5.plot([0,1.4],[z[i],z[i]],'tan',lw=4) 74 | elif ic[i] > 1: 75 | ax5.plot([0,1],[z[i],z[i]],'gold',lw=4) 76 | ########################################################################################################################################################################## 77 | #Plot axes grids 78 | ax4.grid(True) 79 | ########################################################################################################################################################################## 80 | #Data Plots 81 | ax4.plot(ic,z,'k-',lw=0.5) 82 | ########################################################################################################################################################################## 83 | #Save and close figures 84 | fig2.savefig('./sbt_profile.png',bboxinches='tight') 85 | plt.close(fig2) 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /sbt_profile_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingeologist/CPT-Data-Visualisation/49326faf7b1e94b8b35eacd264edbfcc08e5a682/sbt_profile_example.png --------------------------------------------------------------------------------