├── R ├── las_split.R ├── las_batch_funtions.R ├── las_read_mnemonics_df.R ├── las_get_parameters.R ├── las_plot.R ├── las_read_data_dtl.R ├── data.R ├── las_validate.R ├── las_read_data_dt.R ├── las_write.R ├── las_mod_functions.R └── las_read.R ├── LICENSE ├── tests ├── testthat.R └── testthat │ ├── test_write.R │ └── test_read_las.R ├── data └── example_las_obj.RData ├── .Rbuildignore ├── .gitignore ├── inst ├── doc │ ├── Background.R │ ├── Workflow_examples.R │ ├── Background.Rmd │ ├── Workflow_examples.Rmd │ ├── Workflow_examples.html │ └── Background.html └── extdata │ ├── 1.2 │ ├── sample_minimal.las │ ├── sample_inf_api_leading_zero.las │ ├── sample_inf_uwi_leading_zero.las │ ├── sample.las │ ├── sample_curve_api.las │ └── sample_wrapped.las │ ├── 2.0 │ ├── sample_2.0_minimal.las │ ├── sample_2.0_based.las │ ├── sample_2.0.las │ ├── sample_2.0_inf_uwi.las │ ├── sample_2.0_inf_api_leading_zero.las │ ├── sample_2.0_inf_uwi_leading_zero.las │ └── sample_2.0_wrapped.las │ └── example.las ├── lastools.Rproj ├── NAMESPACE ├── man ├── write_las.Rd ├── read_las_mnemonics_df.Rd ├── las_remove_uwi-set.Rd ├── read_las_data_dt.Rd ├── read_las_data_dtl.Rd ├── las_trim_well_id-set.Rd ├── las_convert_v2-set.Rd ├── las_version-set.Rd ├── las_descending-set.Rd ├── read_las.Rd ├── las_set_start_depth-set.Rd ├── las_plot.Rd └── example_las_obj.Rd ├── DESCRIPTION ├── README.Rmd ├── README.md ├── vignettes ├── Background.Rmd ├── lastools.bib └── Workflow_examples.Rmd └── lastools.bib /R/las_split.R: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2016 2 | COPYRIGHT HOLDER: Matthew Dick -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(lastools) 3 | 4 | test_check("lastools") 5 | -------------------------------------------------------------------------------- /data/example_las_obj.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gitmaxwell/lastools/HEAD/data/example_las_obj.RData -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^lastools\.Rcheck$ 4 | ^lastools.*\.tar\.gz$ 5 | ^lastools.*\.tgz$ 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | lastools.Rcheck/ 6 | lastools*.tar.gz 7 | lastools*.tgz 8 | -------------------------------------------------------------------------------- /inst/doc/Background.R: -------------------------------------------------------------------------------- 1 | ## ---- echo=TRUE, results='asis',fig.width=8, fig.height=6,fig.align="center", message=FALSE, warning=FALSE---- 2 | 3 | library(lastools) 4 | lastools::las_plot(lastools::example_las_obj) 5 | 6 | 7 | -------------------------------------------------------------------------------- /lastools.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | BuildType: Package 16 | PackageUseDevtools: Yes 17 | PackageInstallArgs: --no-multiarch --with-keep.source 18 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export("las_convert_v2<-") 4 | export("las_descending<-") 5 | export("las_remove_uwi<-") 6 | export("las_set_start_depth<-") 7 | export("las_trim_well_id<-") 8 | export("las_version<-") 9 | export(las_plot) 10 | export(read_las) 11 | export(read_las_data_dt) 12 | export(read_las_data_dtl) 13 | export(read_las_mnemonics_df) 14 | export(write_las) 15 | import(data.table) 16 | -------------------------------------------------------------------------------- /man/write_las.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/las_write.R 3 | \name{write_las} 4 | \alias{write_las} 5 | \title{Write LAS object to LAS file} 6 | \usage{ 7 | write_las(las, path) 8 | } 9 | \arguments{ 10 | \item{las}{In memory las object} 11 | 12 | \item{path}{Directory path to write las file} 13 | } 14 | \value{ 15 | Writes a las object as a LAS file to disk 16 | } 17 | \description{ 18 | Reads CWLS LAS file/s from a directory location and returns a data.table of the log section data by well 19 | } 20 | -------------------------------------------------------------------------------- /R/las_batch_funtions.R: -------------------------------------------------------------------------------- 1 | las_get_las_file_paths <- function(path) { 2 | las_files <- list.files(path, full.names = T) 3 | las_files <- las_files[stringr::str_detect(toupper(las_files), "\\.LAS")] 4 | return(las_files) 5 | } 6 | 7 | 8 | las_read_all <- function(folder_path) { 9 | if (length(folder_path) == 1) { 10 | las_paths <- las_get_las_file_paths(folder_path) 11 | } 12 | else { 13 | las_paths <- folder_path 14 | } 15 | las_objects <-lapply(las_paths, read_las) 16 | return(las_objects) 17 | } 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /man/read_las_mnemonics_df.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/las_read_mnemonics_df.R 3 | \name{read_las_mnemonics_df} 4 | \alias{read_las_mnemonics_df} 5 | \title{Read las curve mnemonics data to data frame} 6 | \usage{ 7 | read_las_mnemonics_df(dir) 8 | } 9 | \arguments{ 10 | \item{dir}{The Target Directory containing the .las files (required)} 11 | } 12 | \description{ 13 | This function extracts las mnemonics from all las files in a directory 14 | } 15 | \examples{ 16 | read_las_mnemonics_df(".") 17 | } 18 | -------------------------------------------------------------------------------- /man/las_remove_uwi-set.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/las_mod_functions.R 3 | \name{las_remove_uwi<-} 4 | \alias{las_remove_uwi<-} 5 | \title{Remove the UWI line in the WELL table} 6 | \usage{ 7 | las_remove_uwi(x) <- value 8 | } 9 | \arguments{ 10 | \item{x}{LAS object} 11 | 12 | \item{value}{boolean TRUE or FALSE} 13 | } 14 | \value{ 15 | Modifies LAS object in place, removing the UWI parameter from the WELL table 16 | } 17 | \description{ 18 | \code{las_remove_uwi<-} Takes a LAS object and removes the UWI line in the WELL table 19 | } 20 | -------------------------------------------------------------------------------- /tests/testthat/test_write.R: -------------------------------------------------------------------------------- 1 | library(lastools) 2 | 3 | 4 | #----------------------------------------------------------------------------- 5 | # LAS v1.2 tests 6 | #----------------------------------------------------------------------------- 7 | test_that("test write v1.2 sample.las", { 8 | las_file <- "sample.las" 9 | test_path <- system.file("extdata", "1.2", las_file, package = "lastools") 10 | las <- read_las(test_path) 11 | write_las(las, "new_file.las") 12 | newlas <- read_las("new_file.las") 13 | expect_equal(las$WELL$VALUE[12], newlas$WELL$VALUE[12]) 14 | unlink("new_file.las") 15 | }) 16 | -------------------------------------------------------------------------------- /man/read_las_data_dt.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/las_read_data_dt.R 3 | \name{read_las_data_dt} 4 | \alias{read_las_data_dt} 5 | \title{Read las log data to data table} 6 | \usage{ 7 | read_las_data_dt(dir) 8 | } 9 | \arguments{ 10 | \item{dir}{string directory path containing .las file/s} 11 | } 12 | \value{ 13 | Returns a long format data.table containing well_name, DEPT (Depth),variable,value and file (location of las file) 14 | } 15 | \description{ 16 | Reads CWLS LAS file/s from a directory location and returns a data.table of the log section data by well 17 | } 18 | -------------------------------------------------------------------------------- /man/read_las_data_dtl.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/las_read_data_dtl.R 3 | \name{read_las_data_dtl} 4 | \alias{read_las_data_dtl} 5 | \title{Read las log data to data table} 6 | \usage{ 7 | read_las_data_dtl(dir) 8 | } 9 | \arguments{ 10 | \item{dir}{string directory path containing .las file/s} 11 | } 12 | \value{ 13 | Returns list of data tables containing well_name, DEPT (Depth),variable,value and file (location of las file) 14 | } 15 | \description{ 16 | Reads CWLS LAS file/s from a directory location and returns a data.table of the log section data by well 17 | } 18 | -------------------------------------------------------------------------------- /man/las_trim_well_id-set.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/las_mod_functions.R 3 | \name{las_trim_well_id<-} 4 | \alias{las_trim_well_id<-} 5 | \title{Remove the UWI line in the WELL table} 6 | \usage{ 7 | las_trim_well_id(x) <- value 8 | } 9 | \arguments{ 10 | \item{x}{LAS object} 11 | 12 | \item{value}{Boolean TRUE or FALSE} 13 | } 14 | \value{ 15 | Modifies LAS object in place, removing spaces from the WELL reference in the WELL table. 16 | } 17 | \description{ 18 | \code{las_trim_well_id<-} Takes a LAS object and removes spaces from the WELL reference in the WELL table. 19 | } 20 | -------------------------------------------------------------------------------- /man/las_convert_v2-set.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/las_mod_functions.R 3 | \name{las_convert_v2<-} 4 | \alias{las_convert_v2<-} 5 | \title{Change the version of the LAS object to 2.0} 6 | \usage{ 7 | las_convert_v2(x) <- value 8 | } 9 | \arguments{ 10 | \item{x}{LAS object} 11 | 12 | \item{value}{Boolean TRUE or FALSE} 13 | } 14 | \value{ 15 | Modifies LAS object in place, changing the version to V2.0, with the appropriate switching of the parameters in the WELL table 16 | } 17 | \description{ 18 | \code{las_convert_v2<-} Takes a LAS object and changes the version to V2.0 and modifies the WELL table 19 | } 20 | -------------------------------------------------------------------------------- /man/las_version-set.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/las_mod_functions.R 3 | \name{las_version<-} 4 | \alias{las_version<-} 5 | \title{Convert the version number of a LAS object} 6 | \usage{ 7 | las_version(x) <- value 8 | } 9 | \arguments{ 10 | \item{x}{LAS object} 11 | 12 | \item{value}{A number, currently needs to be 1.2 or 2.0} 13 | } 14 | \value{ 15 | Modifies LAS object in place, changing the LAS version to the nominated number 16 | } 17 | \description{ 18 | \code{las_version<-} Takes a LAS object created with the read_las() function and changes the CWLS LAS version. 19 | lastools current supports 1.2 and 2.0 only. 20 | } 21 | -------------------------------------------------------------------------------- /man/las_descending-set.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/las_mod_functions.R 3 | \name{las_descending<-} 4 | \alias{las_descending<-} 5 | \title{Change the depth order of a LAS object in place} 6 | \usage{ 7 | las_descending(x) <- value 8 | } 9 | \arguments{ 10 | \item{x}{LAS object} 11 | 12 | \item{value}{Boolean TRUE or FALSE} 13 | } 14 | \value{ 15 | Modifies LAS object in place, changing the depth order and STRT, STOP and STEP parameters to suit 16 | } 17 | \description{ 18 | \code{las_descending<-} Takes a LAS object and sorts the depth order descending if passed TRUE, or asceding if passed FALSE. 19 | Also modifies the STRT, STOP and STEP parameters of the WELL information block to suit 20 | } 21 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: lastools 2 | Type: Package 3 | Title: Tools for reading/writing and maniplulating Canadian Well Logging Society (CWLS) Format V1.2 or 2.0 4 | LAS Files as R Objects 5 | Version: 1.0.0 6 | Imports: 7 | ggplot2, 8 | stringr, 9 | data.table, 10 | reshape2 11 | Author: Matthew Dick & Kane Maxwell 12 | Maintainer: Matthew Dick 13 | Description: Functions for reading and writing version 1.2 and 2.0 LAS files 14 | as specified by the Canadian Well Logging Society standard. Currently includes 15 | functions to perform standardisation steps, such as format validation and depth 16 | reversal. Currently only tested on coal borehole logs. 17 | License: MIT + file LICENSE 18 | LazyData: TRUE 19 | Suggests: 20 | knitr, 21 | testthat 22 | VignetteBuilder: knitr 23 | RoxygenNote: 7.0.2 24 | -------------------------------------------------------------------------------- /man/read_las.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/las_read.R 3 | \name{read_las} 4 | \alias{read_las} 5 | \title{Read a LAS file} 6 | \usage{ 7 | read_las(filepath, replace_null = T) 8 | } 9 | \arguments{ 10 | \item{filepath}{string path to the LAS file} 11 | 12 | \item{replace_null}{boolean replace the NULLS in the LOG section (as given by the WELL table) to the R NA value} 13 | } 14 | \value{ 15 | Returns a list object with VERSION, WELL, CURVE, PARAMETER, OTHER and LOG sections. WELL, CURVE, PARAMETER and LOG are data.frames. OTHER is a string with line breaks. VERSION is a numeric (1.2 or 2.0) 16 | } 17 | \description{ 18 | Reads a CWLS LAS file and returns a list object with the VERSION, WELL, CURVE, PARAMETER, OTHER and LOG sections, as well as storing the original path of the file. 19 | } 20 | -------------------------------------------------------------------------------- /man/las_set_start_depth-set.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/las_mod_functions.R 3 | \name{las_set_start_depth<-} 4 | \alias{las_set_start_depth<-} 5 | \title{Change the start depth of a LAS object in place} 6 | \usage{ 7 | las_set_start_depth(x) <- value 8 | } 9 | \arguments{ 10 | \item{x}{LAS object} 11 | 12 | \item{value}{Number to insert as the new start depth.} 13 | } 14 | \value{ 15 | Modifies LAS object in place, changing the STRT parameter of the WELL information block and removing rows from the log that are lower than this number. 16 | This was designed to remove negative starting DEPTHS, so caution should be used if the DEPTHS are not in ascending order. 17 | } 18 | \description{ 19 | \code{las_set_start_depth<-} Takes a LAS object and changes the STRT depth in the WELL table of the LAS object 20 | } 21 | -------------------------------------------------------------------------------- /R/las_read_mnemonics_df.R: -------------------------------------------------------------------------------- 1 | #' @title Read las curve mnemonics data to data frame 2 | #' 3 | #' @description This function extracts las mnemonics from all las files in a directory 4 | #' @param dir The Target Directory containing the .las files (required) 5 | #' @export 6 | #' @examples 7 | #' read_las_mnemonics_df(".") 8 | 9 | 10 | read_las_mnemonics_df <- function(dir) 11 | 12 | { 13 | get_mnemonics <- function(x) 14 | 15 | { 16 | las <- lastools::read_las(x) 17 | mnem.df <- as.data.frame(las$CURVE$MNEM) 18 | mnem.df <- as.data.frame(las$CURVE$DESCRIPTION) 19 | names(mnem.df) <- c("mnem") 20 | return(mnem.df) 21 | } 22 | list.of.files <- list.files(dir, pattern = "\\.las$",recursive = TRUE, full.names = T) 23 | mnem <- lapply(list.of.files,function(x) get_mnemonics(x)) 24 | mnem <-unique(as.data.frame(do.call(rbind,mnem))) 25 | return (mnem) 26 | } 27 | -------------------------------------------------------------------------------- /man/las_plot.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/las_plot.R 3 | \name{las_plot} 4 | \alias{las_plot} 5 | \title{Plot a LAS file} 6 | \usage{ 7 | las_plot(las, from = NA, to = NA, columns = NULL) 8 | } 9 | \arguments{ 10 | \item{las}{The LAS object generated with read_las} 11 | 12 | \item{from}{numeric FROM depth to use in subsetting} 13 | 14 | \item{to}{numeric TO depth to use in subsetting} 15 | 16 | \item{columns}{string A string or vector of strings with the variable names to plot eg: c("BRD", "CADE") will only plot these curves.} 17 | } 18 | \value{ 19 | Returns a ggplot2 plot object with a facet for each variable with reversed depths as commonly displayed with coal. 20 | } 21 | \description{ 22 | Takes a lasplot LAS object and plots the log file with a facet for each variable present. Able to "zoom" to a particular depth section using the from and to variables, and able to subset a particular set of curves using the string input to columns. 23 | } 24 | -------------------------------------------------------------------------------- /R/las_get_parameters.R: -------------------------------------------------------------------------------- 1 | las_get_curve_names <- function(las) { 2 | return(las$CURVE$MNEM) 3 | } 4 | 5 | las_get_attributes <- function(las) { 6 | out <- tryCatch({ 7 | well = las$WELL$VALUE[las$WELL$MNEM == "WELL"] 8 | if (length(well) == 0) well = "NULL" 9 | if (well == "WELL") well = stringr::str_trim(las$WELL$DESCRIPTION[las$WELL$MNEM == "WELL"]) 10 | null= as.numeric(las$WELL$VALUE[las$WELL$MNEM == "NULL"]) 11 | start= as.numeric(las$WELL$VALUE[las$WELL$MNEM == "STRT"]) 12 | start_units = las$WELL$UNIT[las$WELL$MNEM == "STRT"] 13 | stop= as.numeric(las$WELL$VALUE[las$WELL$MNEM == "STOP"]) 14 | step = as.numeric(las$WELL$VALUE[las$WELL$MNEM == "STEP"]) 15 | step_units = las$WELL$UNIT[las$WELL$MNEM == "STEP"] 16 | return(data.frame("well" = well, "null" = null, "start" = start, "start_units" = start_units, "stop" = stop, "step" = step, "step_units" = step_units)) 17 | }, error = function(cond) return(data.frame("well" = NA, "null" = NA, "start" = NA, "start_units" = NA, "stop" = NA, "step" = NA, "step_units" = NA, stringsAsFactors = F))) 18 | } 19 | 20 | 21 | -------------------------------------------------------------------------------- /inst/extdata/1.2/sample_minimal.las: -------------------------------------------------------------------------------- 1 | ~V 2 | VERS. 1.2: CWLS log ASCII Standard -VERSION 1.2 3 | WRAP. NO: One line per depth step 4 | ~W 5 | STRT.M 635.0000: 6 | STOP.M 400.0000: 7 | STEP.M -0.1250: 8 | NULL. -999.25: 9 | COMP. COMPANY: ANY OIL COMPANY INC. 10 | WELL. WELL: ANY ET AL A9-16-49-20 11 | FLD . FIELD: EDAM 12 | LOC . LOCATION: A9-16-49-20W3M 13 | PROV. PROVINCE: SASKATCHEWAN 14 | SRVC. SERVICE COMPANY: ANY LOGGING COMPANY INC. 15 | DATE. LOG DATE: 13-DEC-86 16 | UWI . UNIQUE WELL ID: 100091604920W300 17 | ~C 18 | DEPT.M : DEPTH 19 | RHOB.K/M3 : BULK DENSITY 20 | NPHI.VOL/VOL : NEUTRON POROSITY - SANDSTONE 21 | MSFL.OHMM : Rxo RESISTIVITY 22 | SFLA.OHMM : SHALLOW RESISTIVITY 23 | ILM .OHMM : MEDIUM RESISTIVITY 24 | ILD .OHMM : DEEP RESISTIVITY 25 | SP .MV : SPONTANEOUS POTENTIAL 26 | ~A 27 | 635.0000 2256.0000 0.4033 22.0781 22.0781 20.3438 3.6660 123.4 28 | 634.8750 2256.0000 0.4033 22.0781 22.0781 20.3438 3.6660 123.4 29 | -------------------------------------------------------------------------------- /inst/extdata/1.2/sample_inf_api_leading_zero.las: -------------------------------------------------------------------------------- 1 | ~V 2 | VERS. 1.2: CWLS log ASCII Standard -VERSION 1.2 3 | WRAP. NO: One line per depth step 4 | ~W 5 | STRT.M 635.0000: 6 | STOP.M 400.0000: 7 | STEP.M -0.1250: 8 | NULL. -999.25: 9 | COMP. COMPANY: ANY OIL COMPANY INC. 10 | WELL. WELL: ANY ET AL A9-16-49-20 11 | FLD . FIELD: EDAM 12 | LOC . LOCATION: A9-16-49-20W3M 13 | PROV. PROVINCE: SASKATCHEWAN 14 | SRVC. SERVICE COMPANY: ANY LOGGING COMPANY INC. 15 | DATE. LOG DATE: 13-DEC-86 16 | API. API NUMBER: 05001095820000 17 | ~P 18 | ~C 19 | DEPT.M : DEPTH 20 | RHOB.K/M3 : BULK DENSITY 21 | NPHI.VOL/VOL : NEUTRON POROSITY - SANDSTONE 22 | MSFL.OHMM : Rxo RESISTIVITY 23 | SFLA.OHMM : SHALLOW RESISTIVITY 24 | ILM .OHMM : MEDIUM RESISTIVITY 25 | ILD .OHMM : DEEP RESISTIVITY 26 | SP .MV : SPONTANEOUS POTENTIAL 27 | ~A 28 | 635.0000 2256.0000 0.4033 22.0781 22.0781 20.3438 3.6660 123.4 29 | 634.8750 2256.0000 0.4033 22.0781 22.0781 20.3438 3.6660 123.4 30 | -------------------------------------------------------------------------------- /inst/extdata/1.2/sample_inf_uwi_leading_zero.las: -------------------------------------------------------------------------------- 1 | ~V 2 | VERS. 1.2: CWLS log ASCII Standard -VERSION 1.2 3 | WRAP. NO: One line per depth step 4 | ~W 5 | STRT.M 635.0000: 6 | STOP.M 400.0000: 7 | STEP.M -0.1250: 8 | NULL. -999.25: 9 | COMP. COMPANY: ANY OIL COMPANY INC. 10 | WELL. WELL: ANY ET AL A9-16-49-20 11 | FLD . FIELD: EDAM 12 | LOC . LOCATION: A9-16-49-20W3M 13 | PROV. PROVINCE: SASKATCHEWAN 14 | SRVC. SERVICE COMPANY: ANY LOGGING COMPANY INC. 15 | DATE. LOG DATE: 13-DEC-86 16 | UWI . UNIQUE WELL ID: 05001095820000 17 | ~P 18 | ~C 19 | DEPT.M : DEPTH 20 | RHOB.K/M3 : BULK DENSITY 21 | NPHI.VOL/VOL : NEUTRON POROSITY - SANDSTONE 22 | MSFL.OHMM : Rxo RESISTIVITY 23 | SFLA.OHMM : SHALLOW RESISTIVITY 24 | ILM .OHMM : MEDIUM RESISTIVITY 25 | ILD .OHMM : DEEP RESISTIVITY 26 | SP .MV : SPONTANEOUS POTENTIAL 27 | ~A 28 | 635.0000 2256.0000 0.4033 22.0781 22.0781 20.3438 3.6660 123.4 29 | 634.8750 2256.0000 0.4033 22.0781 22.0781 20.3438 3.6660 123.4 30 | -------------------------------------------------------------------------------- /R/las_plot.R: -------------------------------------------------------------------------------- 1 | #' Plot a LAS file 2 | #' 3 | #' Takes a lasplot LAS object and plots the log file with a facet for each variable present. Able to "zoom" to a particular depth section using the from and to variables, and able to subset a particular set of curves using the string input to columns. 4 | #' @param las The LAS object generated with read_las 5 | #' @param from numeric FROM depth to use in subsetting 6 | #' @param to numeric TO depth to use in subsetting 7 | #' @param columns string A string or vector of strings with the variable names to plot eg: c("BRD", "CADE") will only plot these curves. 8 | #' @return Returns a ggplot2 plot object with a facet for each variable with reversed depths as commonly displayed with coal. 9 | #' @export 10 | #' 11 | las_plot <- function(las, from = NA, to = NA, columns = NULL) { 12 | df <- las$LOG 13 | colnames(df) <- c("Depth", colnames(df)[2:ncol(df)]) 14 | df <- reshape2::melt(df, id = "Depth") 15 | if (!is.na(from) & !is.na(to)) df <- df[df$Depth >= from & df$Depth <= to,] 16 | if (length(columns) > 0) df <- df[df$variable %in% columns,] 17 | ggplot2::ggplot(df, ggplot2::aes_string(x = "value", y = "Depth")) + ggplot2::geom_path(ggplot2::aes_string(color = "variable")) + ggplot2::scale_y_reverse() + ggplot2::facet_wrap(~variable, scale = "free_x", nrow = 1) 18 | } 19 | 20 | 21 | -------------------------------------------------------------------------------- /R/las_read_data_dtl.R: -------------------------------------------------------------------------------- 1 | #' Read las log data to data table 2 | #' 3 | #' Reads CWLS LAS file/s from a directory location and returns a data.table of the log section data by well 4 | #' @param dir string directory path containing .las file/s 5 | #' @return Returns list of data tables containing well_name, DEPT (Depth),variable,value and file (location of las file) 6 | #' @export 7 | #' @import data.table 8 | #' 9 | 10 | read_las_data_dtl <- function (dir) { 11 | laslist <- list.files(dir, pattern = "\\.las$",recursive = TRUE,full.names = T) 12 | las_data <- lapply(laslist,function(x) try(.get_las_data_dt(x))) 13 | return(las_data) 14 | } 15 | 16 | #function passes info into read_las_data_df 17 | .get_las_data_dt <- function(x) 18 | { 19 | requireNamespace(data.table) 20 | las <- lastools::read_las(x) 21 | filename <- x 22 | 23 | #set up mapping table to join descriptions and units 24 | curvemap <- as.data.frame(las$CURVE$MNEM) 25 | setDT(curvemap) 26 | curvemap$Units <- (las$CURVE$UNIT) 27 | curvemap$Descr <- (las$CURVE$DESCRIPTION) 28 | names(curvemap) <- c("variable","Units","Descr") 29 | 30 | #melt the data table to long format 31 | #df <- as.data.table(las$LOG) 32 | df <- las$LOG 33 | dfmelt <- melt(df, id="DEPT") 34 | #add well name 35 | dfmelt$well_name <- subset(las$WELL$VALUE,las$WELL$MNEM =="WELL") 36 | dfmelt$well_name <- ifelse (is.na(dfmelt$well_name),filename, dfmelt$well_name) 37 | dfmelt$file <- filename 38 | #merge the description and units 39 | dfmelt <- merge(dfmelt,curvemap) 40 | setDT(dfmelt) 41 | return(dfmelt) 42 | } 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /inst/extdata/2.0/sample_2.0_minimal.las: -------------------------------------------------------------------------------- 1 | ~V 2 | VERS. 2.0 : CWLS log ASCII Standard -VERSION 2.0 3 | WRAP. NO : One line per depth step 4 | ~W 5 | STRT.M 635.0000 :START DEPTH 6 | STOP.M 400.0000 :STOP DEPTH 7 | STEP.M -0.1250 :STEP 8 | NULL. -999.25 :NULL VALUE 9 | COMP. ANY OIL COMPANY INC. :COMPANY 10 | WELL. ANY ET AL 12-34-12-34 :WELL 11 | FLD . WILDCAT :FIELD 12 | LOC . 12-34-12-34W5M :LOCATION 13 | PROV. ALBERTA :PROVINCE 14 | SRVC. ANY LOGGING COMPANY INC. :SERVICE COMPANY 15 | DATE. 13-DEC-86 :LOG DATE 16 | UWI . 100123401234W500 :UNIQUE WELL ID 17 | ~C 18 | DEPT .M : DEPTH 19 | RHOB .K/M3 : BULK DENSITY 20 | NPHI .VOL/VOL : NEUTRON POROSITY - SANDSTONE 21 | MSFL .OHMM : Rxo RESISTIVITY 22 | SFLA .OHMM : SHALLOW RESISTIVITY 23 | ILM .OHMM : MEDIUM RESISTIVITY 24 | ILD .OHMM : DEEP RESISTIVITY 25 | SP .MV : SPONTANEOUS POTENTIAL 26 | ~A 27 | 635.0000 2256.0000 0.4033 22.0781 22.0781 20.3438 3.6660 123.4 28 | 634.8750 2256.0000 0.4033 22.0781 22.0781 20.3438 3.6660 123.4 29 | -------------------------------------------------------------------------------- /inst/extdata/2.0/sample_2.0_based.las: -------------------------------------------------------------------------------- 1 | ~VERSION INFORMATION 2 | VERS. 2.0 : CWLS LOG ASCII STANDARD -VERSION 2.0 3 | WRAP. NO : ONE LINE PER TIME STEP 4 | # 5 | ~WELL INFORMATION 6 | STRT .S 0.0000 :START TIME 7 | STOP .S 39.9000 :STOP TIME 8 | STEP .S 0.3000 :STEP 9 | NULL . -999.25 :NULL VALUE 10 | COMP . ANY OIL COMPANY INC. :COMPANY 11 | WELL . ANY ET 12-34-12-34 :WELL 12 | FLD . WILDCAT :FIELD 13 | LOC . 12-34-12-34W5 :LOCATION 14 | PROV . ALBERTA :PROVINCE 15 | SRVC . ANY LOGGING COMPANY INC. :SERVICE COMPANY 16 | DATE . 13-DEC-86 :LOG DATE 17 | UWI . 100123401234W500 :UNIQUE WELL ID 18 | # 19 | ~CURVE INFORMATION 20 | ETIM .S : 1 ELAPSED TIME 21 | BFR1 .OHMM : 2 SINGLE PROBE 1 RESISTIVITY 22 | BSG1 .PSIG : 3 SINGLE PROBE 1 STRAIN GAUGE PRESSURE 23 | # 24 | ~PARAMETER INFORMATION 25 | MRT .DEGC 67.0 : BOTTOM HOLE TEMPERATURE 26 | GDEPT .M 3456.5 : GAUGE DEPTH 27 | DFD .KG/M3 1000.0 : MUD WEIGHT 28 | # 29 | ~A 30 | 0.0000 0.2125 16564.1445 31 | 0.3000 0.2125 16564.1445 32 | 0.6000 0.2125 16564.2421 33 | 0.9000 0.2125 16564.0434 34 | 1.2000 0.2125 16564.0430 35 | 1.5000 0.2125 16564.0435 36 | -------------------------------------------------------------------------------- /R/data.R: -------------------------------------------------------------------------------- 1 | #' An example of a LAS object. 2 | #' 3 | #' An example of a LAS object created from a LAS file (49-005-30258.las) downloaded from the Minnelusa Digital Log Data website 4 | #' 5 | #'@usage las_obj = lastools::example_las_obj 6 | #' 7 | #' @format A List of length 8 comprising of 8 | #' \describe{ 9 | #' \item{VERSION}{LAS version number} 10 | #' \item{WELL}{A data frame describing the well section (~W) information. Comprises of four columns (MNEM (Mnemonic code),UNIT (Unit code),VALUE,DESCRIPTION)} 11 | #' \item{CURVE}{A data frame describing the curve section (~C) information. Comprises of four columns (MNEM (Mnemonic code), UNIT (Unit code),API.CODE, DESCRIPTION)} 12 | #' \item{PARAM}{A data frame describing the parameter (~P) information. Comprises of four columns (MNEM (Mnemonic code),UNIT (Unit code),VALUE,DESCRIPTION)} 13 | #' \item{OTHER}{Placeholder for 'Other' information} 14 | #' \item{LOG}{A data frame describing the log (~A) information. Comprises of four columns (DEPT (Depth), DT (Delata travel time), RESD (Resistivity), SP (Spontaneous Potential), GR (Gamma Ray))} 15 | #' \item{PATH}{Usually stores the file load path. In this case is set to "Example"} 16 | #' \item{ATTRIBUTES}{A data frame with eight columns describing the primary well attribute information including (well (the well id), null (the LOG data null value), start(the LOG data start depth),start_units (the start depth units (in this case Feet), stop (the LOG data end depth), step (LOG data increment step size), step_units (LOG data step units),path (the original load path location))} 17 | #' ... 18 | #' } 19 | #' @source \url{www.Minnelusa.com} 20 | "example_las_obj" 21 | 22 | #lastools::`las_convert_v2<-`() 23 | #las_convert_v2(x) <- TRUE -------------------------------------------------------------------------------- /man/example_las_obj.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{example_las_obj} 5 | \alias{example_las_obj} 6 | \title{An example of a LAS object.} 7 | \format{A List of length 8 comprising of 8 | \describe{ 9 | \item{VERSION}{LAS version number} 10 | \item{WELL}{A data frame describing the well section (~W) information. Comprises of four columns (MNEM (Mnemonic code),UNIT (Unit code),VALUE,DESCRIPTION)} 11 | \item{CURVE}{A data frame describing the curve section (~C) information. Comprises of four columns (MNEM (Mnemonic code), UNIT (Unit code),API.CODE, DESCRIPTION)} 12 | \item{PARAM}{A data frame describing the parameter (~P) information. Comprises of four columns (MNEM (Mnemonic code),UNIT (Unit code),VALUE,DESCRIPTION)} 13 | \item{OTHER}{Placeholder for 'Other' information} 14 | \item{LOG}{A data frame describing the log (~A) information. Comprises of four columns (DEPT (Depth), DT (Delata travel time), RESD (Resistivity), SP (Spontaneous Potential), GR (Gamma Ray))} 15 | \item{PATH}{Usually stores the file load path. In this case is set to "Example"} 16 | \item{ATTRIBUTES}{A data frame with eight columns describing the primary well attribute information including (well (the well id), null (the LOG data null value), start(the LOG data start depth),start_units (the start depth units (in this case Feet), stop (the LOG data end depth), step (LOG data increment step size), step_units (LOG data step units),path (the original load path location))} 17 | ... 18 | }} 19 | \source{ 20 | \url{www.Minnelusa.com} 21 | } 22 | \usage{ 23 | las_obj = lastools::example_las_obj 24 | } 25 | \description{ 26 | An example of a LAS object created from a LAS file (49-005-30258.las) downloaded from the Minnelusa Digital Log Data website 27 | } 28 | \keyword{datasets} 29 | -------------------------------------------------------------------------------- /inst/extdata/example.las: -------------------------------------------------------------------------------- 1 | ~VERSION INFORMATION 2 | VERS. 2.0 : CWLS LOG ASCII STANDARD -VERSION 2.0 3 | WRAP. NO : ONE LINE PER DEPTH STEP 4 | ~WELL INFORMATION 5 | #MNEM.UNIT DATA DESCRIPTION 6 | #----- ----- ---------- ----------------- 7 | STRT .M 1670.0000 :START DEPTH 8 | STOP .M 1669.7500 :STOP DEPTH 9 | STEP .M -0.1250 :STEP 10 | NULL . -999.25 :NULL VALUE 11 | COMP . ANY OIL COMPANY INC. :COMPANY 12 | WELL . ANY ET AL 12-34-12-34 :WELL 13 | FLD . WILDCAT :FIELD 14 | LOC . 12-34-12-34W5M :LOCATION 15 | PROV . ALBERTA :PROVINCE 16 | SRVC . ANY LOGGING COMPANY INC. :SERVICE COMPANY 17 | DATE . 13-DEC-86 :LOG DATE 18 | UWI . 100123401234W500 :UNIQUE WELL ID 19 | LIC . 23412 :ERCB LICENCE NUMB 20 | ~CURVE INFORMATION 21 | #MNEM.UNIT API CODES CURVE DESCRIPTION 22 | #------------------ ------------ ------------------- 23 | DEPT .M : 1 DEPTH 24 | DT .US/M 60 520 32 00 : 2 SONIC TRANSIT TIME 25 | RHOB .K/M3 45 350 01 00 : 3 BULK DENSITY 26 | NPHI .V/V 42 890 00 00 : 4 NEUTRON POROSITY 27 | SFLU .OHMM 07 220 04 00 : 5 SHALLOW RESISTIVITY 28 | SFLA .OHMM 07 222 01 00 : 6 SHALLOW RESISTIVITY 29 | ILM .OHMM 07 120 44 00 : 7 MEDIUM RESISTIVITY 30 | ILD .OHMM 07 120 46 00 : 8 DEEP RESISTIVITY 31 | ~PARAMETER INFORMATION 32 | #MNEM.UNIT VALUE DESCRIPTION 33 | #-------------- ---------------- ------------------------ 34 | MUD . GEL CHEM : MUD TYPE 35 | BHT .DEGC 35.5000 : BOTTOM HOLE TEMPERATURE 36 | CSGL .M 124.6 : BASE OF CASING 37 | MATR . SAND : NEUTRON MATRIX 38 | MDEN . 2710.0000 : LOGGING MATRIX DENSITY 39 | RMF .OHMM 0.2160 : MUD FILTRATE RESISTIVITY 40 | DFD .K/M3 1525.0000 : DRILL FLUID DENSITY 41 | ~OTHER 42 | Note: The logging tools became stuck at 625 metres causing the 43 | data between 625 metres and 615 metres to be invalid. 44 | # 45 | ~A DEPTH DT RHOB NPHI SFLU SFLA ILM ILD 46 | 1670.000 123.450 2550.000 0.450 123.450 123.450 110.200 05.600 47 | 1669.875 123.450 2550.000 0.450 123.450 123.450 110.200 05.600 48 | 1669.750 123.450 2550.000 0.450 123.450 123.450 110.200 105.600 49 | -------------------------------------------------------------------------------- /inst/doc/Workflow_examples.R: -------------------------------------------------------------------------------- 1 | ## ---- echo=TRUE, eval=FALSE---------------------------------------------- 2 | # #source directory 3 | # s <- c("C:/") 4 | # # create a target directory on the file system called "compiled_las" 5 | # #target directory 6 | # t <- c("C:/compiled_las") 7 | # 8 | # las_compile <- function (s,t) 9 | # { 10 | # 11 | # list.of.files <- list.files(s, pattern = "\\.las$",recursive = TRUE, full.names = T) 12 | # file.copy(list.of.files,t) 13 | # } 14 | # 15 | # #compile las takes .las files from the source directory and moves to the target directory 16 | # las_compile(s,t) 17 | # 18 | # 19 | 20 | ## ---- echo=TRUE, eval=FALSE---------------------------------------------- 21 | # lastools::read_las("filepath") 22 | 23 | ## ---- echo=TRUE, eval=FALSE---------------------------------------------- 24 | # lastools::read_las("filepath",replace_null =FALSE) 25 | 26 | ## ---- echo=TRUE, eval=FALSE---------------------------------------------- 27 | # lastools::read_las_data_df("directory") 28 | 29 | ## ---- echo=FALSE, results='asis',fig.width=4, fig.height=6,fig.align="center"---- 30 | 31 | library(lastools) 32 | knitr::kable(lastools::example_las_obj$WELL,digits = 3,align = c("c"), caption = "lastools::example_las_obj$WELL") 33 | 34 | 35 | ## ---- echo=FALSE, results='asis',fig.width=4, fig.height=6,fig.align="center"---- 36 | 37 | library(lastools) 38 | knitr::kable(lastools::example_las_obj$CURVE,digits = 3,align = c("c"), caption = "lastools::example_las_obj$CURVE") 39 | 40 | 41 | ## ---- echo=FALSE, results='asis',fig.width=4, fig.height=6,fig.align="center"---- 42 | 43 | library(lastools) 44 | knitr::kable(lastools::example_las_obj$PARAM,digits = 3,align = c("c"), caption = "lastools::example_las_obj$PARAM") 45 | 46 | #lastools::read_las("L:\\Coal_Quality\\R LIBRARY\\Packages\\lastools\\las_files\\PMI2279.las") 47 | 48 | 49 | ## ---- echo=FALSE, results='asis',fig.width=4, fig.height=6,fig.align="center"---- 50 | 51 | library(lastools) 52 | knitr::kable(head(lastools::example_las_obj$LOG, n=10),digits = 3,align = c("c"), caption = "lastools::example_las_obj$LOG") 53 | 54 | 55 | 56 | ## ------------------------------------------------------------------------ 57 | library(lastools) 58 | 59 | 60 | -------------------------------------------------------------------------------- /inst/extdata/1.2/sample.las: -------------------------------------------------------------------------------- 1 | ~VERSION INFORMATION 2 | VERS. 1.2: CWLS LOG ASCII STANDARD -VERSION 1.2 3 | WRAP. NO: ONE LINE PER DEPTH STEP 4 | ~WELL INFORMATION BLOCK 5 | #MNEM.UNIT DATA TYPE INFORMATION 6 | #--------- ------------- ------------------------------ 7 | STRT.M 1670.000000: 8 | STOP.M 1660.000000: 9 | STEP.M -0.1250: 10 | NULL. -999.2500: 11 | COMP. COMPANY: # ANY OIL COMPANY LTD. 12 | WELL. WELL: ANY ET AL OIL WELL #12 13 | FLD . FIELD: EDAM 14 | LOC . LOCATION: A9-16-49-20W3M 15 | PROV. PROVINCE: SASKATCHEWAN 16 | SRVC. SERVICE COMPANY: ANY LOGGING COMPANY LTD. 17 | DATE. LOG DATE: 25-DEC-1988 18 | UWI . UNIQUE WELL ID: 100091604920W300 19 | ~CURVE INFORMATION 20 | #MNEM.UNIT API CODE CURVE DESCRIPTION 21 | #--------- ------------- ------------------------------ 22 | DEPT.M : 1 DEPTH 23 | DT .US/M : 2 SONIC TRANSIT TIME 24 | RHOB.K/M3 : 3 BULK DENSITY 25 | NPHI.V/V : 4 NEUTRON POROSITY 26 | SFLU.OHMM : 5 RXO RESISTIVITY 27 | SFLA.OHMM : 6 SHALLOW RESISTIVITY 28 | ILM .OHMM : 7 MEDIUM RESISTIVITY 29 | ILD .OHMM : 8 DEEP RESISTIVITY 30 | ~PARAMETER INFORMATION 31 | #MNEM.UNIT VALUE DESCRIPTION 32 | #--------- ------------- ------------------------------ 33 | BHT .DEGC 35.5000: BOTTOM HOLE TEMPERATURE 34 | BS .MM 200.0000: BIT SIZE 35 | FD .K/M3 1000.0000: FLUID DENSITY 36 | MATR. 0.0000: NEUTRON MATRIX(0=LIME,1=SAND,2=DOLO) 37 | MDEN. 2710.0000: LOGGING MATRIX DENSITY 38 | RMF .OHMM 0.2160: MUD FILTRATE RESISTIVITY 39 | DFD .K/M3 1525.0000: DRILL FLUID DENSITY 40 | ~Other 41 | Note: The logging tools became stuck at 625 meters causing the data 42 | between 625 meters and 615 meters to be invalid. 43 | ~A DEPTH DT RHOB NPHI SFLU SFLA ILM ILD 44 | 1670.000 123.450 2550.000 0.450 123.450 123.450 110.200 105.600 45 | 1669.875 123.450 2550.000 0.450 123.450 123.450 110.200 105.600 46 | 1669.750 123.450 2550.000 0.450 123.450 123.450 110.200 105.600 47 | -------------------------------------------------------------------------------- /inst/extdata/1.2/sample_curve_api.las: -------------------------------------------------------------------------------- 1 | ~VERSION INFORMATION 2 | VERS. 1.2: CWLS LOG ASCII STANDARD -VERSION 1.2 3 | WRAP. NO: ONE LINE PER DEPTH STEP 4 | ~WELL INFORMATION BLOCK 5 | #MNEM.UNIT DATA TYPE INFORMATION 6 | #--------- ------------- ------------------------------ 7 | STRT.M 1670.000000: 8 | STOP.M 1660.000000: 9 | STEP.M -0.1250: 10 | NULL. -999.2500: 11 | COMP. COMPANY: ANY OIL COMPANY LTD. 12 | WELL. WELL: ANY ET AL OIL WELL #12 13 | FLD . FIELD: EDAM 14 | LOC . LOCATION: A9-16-49-20W3M 15 | PROV. PROVINCE: SASKATCHEWAN 16 | SRVC. SERVICE COMPANY: ANY LOGGING COMPANY LTD. 17 | DATE. LOG DATE: 25-DEC-1988 18 | UWI . UNIQUE WELL ID: 100091604920W300 19 | ~Curve Information Section 20 | #MNEM.UNIT API CODE Curve Description 21 | #--------- ------------- ------------------------------- 22 | DEPTH.M : 1 DEPTH 23 | RHOB .K/M3 7 350 02 00: 2 BULK DENSITY 24 | NPHI .VOL/VOL 7 890 00 00: 3 NEUTRON POROSITY - SANDSTONE 25 | MSFL .OHMM 7 220 01 00: 4 Rxo RESISTIVITY 26 | SFLA .OHMM 7 222 01 00: 5 SHALLOW RESISTIVITY 27 | ILM .OHMM 7 120 44 00: 6 MEDIUM RESISTIVITY 28 | ILD .OHMM 7 120 46 00: 7 DEEP RESISTIVITY 29 | SP .MV 7 010 01 00: 8 SPONTANEOUS POTENTIAL 30 | ~PARAMETER INFORMATION 31 | #MNEM.UNIT VALUE DESCRIPTION 32 | #--------- ------------- ------------------------------ 33 | BHT .DEGC 35.5000: BOTTOM HOLE TEMPERATURE 34 | BS .MM 200.0000: BIT SIZE 35 | FD .K/M3 1000.0000: FLUID DENSITY 36 | MATR. 0.0000: NEUTRON MATRIX(0=LIME,1=SAND,2=DOLO) 37 | MDEN. 2710.0000: LOGGING MATRIX DENSITY 38 | RMF .OHMM 0.2160: MUD FILTRATE RESISTIVITY 39 | DFD .K/M3 1525.0000: DRILL FLUID DENSITY 40 | ~Other 41 | Note: The logging tools became stuck at 625 meters causing the data 42 | between 625 meters and 615 meters to be invalid. 43 | ~A DEPTH DT RHOB NPHI SFLU SFLA ILM ILD 44 | 1670.000 123.450 2550.000 0.450 123.450 123.450 110.200 105.600 45 | 1669.875 123.450 2550.000 0.450 123.450 123.450 110.200 105.600 46 | 1669.750 123.450 2550.000 0.450 123.450 123.450 110.200 105.600 47 | -------------------------------------------------------------------------------- /R/las_validate.R: -------------------------------------------------------------------------------- 1 | 2 | las_validate_format <- function(path) { 3 | 4 | 5 | las_section_codes <- c("~V", "~W", "~C", "~P", "~O", "~A") 6 | las_required_section_codes <- c("~V", "~W", "~C", "~A") 7 | las_well_headers <- c("STRT", "STOP", "STEP", "NULL", "COMP", "WELL", "FLD", "LOC", "SRVC", "DATE") 8 | las_required_well_headers <- c("STRT", "STOP", "STEP", "NULL") 9 | 10 | 11 | sections_appear_once <- F 12 | required_sections_in_file <- F 13 | required_well_headers_present <- F 14 | start_depth_matches_well_table <- F 15 | stop_depth_matches_well_table <- F 16 | step_distance_matches_log <- F 17 | lines <- readLines(path) 18 | 19 | #Remove comment lines 20 | lines <- lines[!stringr::str_detect(stringr::str_sub(lines, 1, 5), "#")] 21 | 22 | #Vectors of validation parameters 23 | section_appearances <- sapply(las_section_codes, function(x) sum(stringr::str_detect(lines, x))) 24 | required_section_appearances <- sapply(las_required_section_codes <- c("~V", "~W", "~C", "~A"), function(x) sum(stringr::str_detect(lines, x))) 25 | 26 | if (all(required_section_appearances > 0)) required_sections_in_file <- T 27 | if (all(section_appearances <= 1)) sections_appear_once <- T 28 | 29 | #Extract the well data for checking 30 | 31 | if (all(required_section_appearances)) { 32 | well_block_rows <- .las_get_block_dims(lines, "~W") 33 | well_data <- do.call(rbind, lapply(lines[well_block_rows], function(x) .las_parse_table_line(x, section = "~W"))) 34 | required_well_headers_present <- all(las_required_well_headers %in% well_data$MNEM) 35 | if (required_well_headers_present) { 36 | las = read_las(path) 37 | las_attributes <- las_get_attributes(las) 38 | expected_length <- abs(las_attributes$stop - las_attributes$start) / las_attributes$step 39 | log_length <- nrow(las$LOG) 40 | if (expected_length == log_length) step_distance_matches_log <- T 41 | if (las$LOG[1,1] == las_attributes$start) start_depth_matches_well_table <- T 42 | if (las$LOG[nrow(las$LOG),1] == las_attributes$stop) stop_depth_matches_well_table <- T 43 | } 44 | } 45 | 46 | checks <- c(sections_appear_once, 47 | required_sections_in_file, 48 | required_well_headers_present, 49 | start_depth_matches_well_table, 50 | stop_depth_matches_well_table, 51 | step_distance_matches_log) 52 | 53 | warnings = c( "Duplicate section codes found", 54 | "Missing required section blocks", 55 | "Missing WELL information headers", 56 | "Start depth does not match log", 57 | "Stop depth does not match log", 58 | "Log length does not match expected length") 59 | 60 | if (all(checks)) return(T) else { 61 | print(warnings[checks==F]) 62 | return(F) 63 | } 64 | 65 | } 66 | 67 | 68 | -------------------------------------------------------------------------------- /inst/extdata/2.0/sample_2.0.las: -------------------------------------------------------------------------------- 1 | ~VERSION INFORMATION 2 | VERS. 2.0 : CWLS LOG ASCII STANDARD -VERSION 2.0 3 | WRAP. NO : ONE LINE PER DEPTH STEP 4 | ~WELL INFORMATION 5 | #MNEM.UNIT DATA DESCRIPTION 6 | #----- ----- ---------- ------------------------- 7 | STRT .M 1670.0000 :START DEPTH 8 | STOP .M 1660.0000 :STOP DEPTH 9 | STEP .M -0.1250 :STEP 10 | NULL . -999.25 :NULL VALUE 11 | COMP . ANY OIL COMPANY INC. :COMPANY 12 | WELL . AAAAA_2 :WELL 13 | FLD . WILDCAT :FIELD 14 | LOC . 12-34-12-34W5M :LOCATION 15 | PROV . ALBERTA :PROVINCE 16 | SRVC . ANY LOGGING COMPANY INC. :SERVICE COMPANY 17 | DATE . 13-DEC-86 :LOG DATE 18 | UWI . 100123401234W500 :UNIQUE WELL ID 19 | ~CURVE INFORMATION 20 | #MNEM.UNIT API CODES CURVE DESCRIPTION 21 | #------------------ ------------ ------------------------- 22 | DEPT .M : 1 DEPTH 23 | DT .US/M 60 520 32 00 : 2 SONIC TRANSIT TIME 24 | RHOB .K/M3 45 350 01 00 : 3 BULK DENSITY 25 | NPHI .V/V 42 890 00 00 : 4 NEUTRON POROSITY 26 | SFLU .OHMM 07 220 04 00 : 5 SHALLOW RESISTIVITY 27 | SFLA .OHMM 07 222 01 00 : 6 SHALLOW RESISTIVITY 28 | ILM .OHMM 07 120 44 00 : 7 MEDIUM RESISTIVITY 29 | ILD .OHMM 07 120 46 00 : 8 DEEP RESISTIVITY 30 | ~PARAMETER INFORMATION 31 | #MNEM.UNIT VALUE DESCRIPTION 32 | #-------------- ---------------- ----------------------------------------------- 33 | MUD . GEL CHEM : MUD TYPE 34 | BHT .DEGC 35.5000 : BOTTOM HOLE TEMPERATURE 35 | BS .MM 200.0000 : BIT SIZE 36 | FD .K/M3 1000.0000 : FLUID DENSITY 37 | MATR . SAND : NEUTRON MATRIX 38 | MDEN . 2710.0000 : LOGGING MATRIX DENSITY 39 | RMF .OHMM 0.2160 : MUD FILTRATE RESISTIVITY 40 | DFD .K/M3 1525.0000 : DRILL FLUID DENSITY 41 | ~OTHER 42 | Note: The logging tools became stuck at 625 metres causing the data 43 | between 625 metres and 615 metres to be invalid. 44 | ~A DEPTH DT RHOB NPHI SFLU SFLA ILM ILD 45 | 1670.000 123.450 2550.000 0.450 123.450 123.450 110.200 105.600 46 | 1669.875 123.450 2550.000 0.450 123.450 123.450 110.200 105.600 47 | 1669.750 123.450 2550.000 0.450 123.450 123.450 110.200 105.600 48 | -------------------------------------------------------------------------------- /inst/extdata/2.0/sample_2.0_inf_uwi.las: -------------------------------------------------------------------------------- 1 | ~VERSION INFORMATION 2 | VERS. 2.0 : CWLS LOG ASCII STANDARD -VERSION 2.0 3 | WRAP. NO : ONE LINE PER DEPTH STEP 4 | ~WELL INFORMATION 5 | #MNEM.UNIT DATA DESCRIPTION 6 | #----- ----- ---------- ------------------------- 7 | STRT .M 1670.0000 :START DEPTH 8 | STOP .M 1660.0000 :STOP DEPTH 9 | STEP .M -0.1250 :STEP 10 | NULL . -999.25 :NULL VALUE 11 | COMP . ANY OIL COMPANY INC. :COMPANY 12 | WELL . AAAAA_2 :WELL 13 | FLD . WILDCAT :FIELD 14 | LOC . 12-34-12-34W5M :LOCATION 15 | PROV . ALBERTA :PROVINCE 16 | SRVC . ANY LOGGING COMPANY INC. :SERVICE COMPANY 17 | DATE . 13-DEC-86 :LOG DATE 18 | UWI . 300E074350061450 :UNIQUE WELL ID 19 | ~CURVE INFORMATION 20 | #MNEM.UNIT API CODES CURVE DESCRIPTION 21 | #------------------ ------------ ------------------------- 22 | DEPT .M : 1 DEPTH 23 | DT .US/M 60 520 32 00 : 2 SONIC TRANSIT TIME 24 | RHOB .K/M3 45 350 01 00 : 3 BULK DENSITY 25 | NPHI .V/V 42 890 00 00 : 4 NEUTRON POROSITY 26 | SFLU .OHMM 07 220 04 00 : 5 SHALLOW RESISTIVITY 27 | SFLA .OHMM 07 222 01 00 : 6 SHALLOW RESISTIVITY 28 | ILM .OHMM 07 120 44 00 : 7 MEDIUM RESISTIVITY 29 | ILD .OHMM 07 120 46 00 : 8 DEEP RESISTIVITY 30 | ~PARAMETER INFORMATION 31 | #MNEM.UNIT VALUE DESCRIPTION 32 | #-------------- ---------------- ----------------------------------------------- 33 | MUD . GEL CHEM : MUD TYPE 34 | BHT .DEGC 35.5000 : BOTTOM HOLE TEMPERATURE 35 | BS .MM 200.0000 : BIT SIZE 36 | FD .K/M3 1000.0000 : FLUID DENSITY 37 | MATR . SAND : NEUTRON MATRIX 38 | MDEN . 2710.0000 : LOGGING MATRIX DENSITY 39 | RMF .OHMM 0.2160 : MUD FILTRATE RESISTIVITY 40 | DFD .K/M3 1525.0000 : DRILL FLUID DENSITY 41 | ~OTHER 42 | Note: The logging tools became stuck at 625 metres causing the data 43 | between 625 metres and 615 metres to be invalid. 44 | ~A DEPTH DT RHOB NPHI SFLU SFLA ILM ILD 45 | 1670.000 123.450 2550.000 0.450 123.450 123.450 110.200 105.600 46 | 1669.875 123.450 2550.000 0.450 123.450 123.450 110.200 105.600 47 | 1669.750 123.450 2550.000 0.450 123.450 123.450 110.200 105.600 48 | -------------------------------------------------------------------------------- /inst/extdata/2.0/sample_2.0_inf_api_leading_zero.las: -------------------------------------------------------------------------------- 1 | ~VERSION INFORMATION 2 | VERS. 2.0 : CWLS LOG ASCII STANDARD -VERSION 2.0 3 | WRAP. NO : ONE LINE PER DEPTH STEP 4 | ~WELL INFORMATION 5 | #MNEM.UNIT DATA DESCRIPTION 6 | #----- ----- ---------- ------------------------- 7 | STRT .M 1670.0000 :START DEPTH 8 | STOP .M 1660.0000 :STOP DEPTH 9 | STEP .M -0.1250 :STEP 10 | NULL . -999.25 :NULL VALUE 11 | COMP . ANY OIL COMPANY INC. :COMPANY 12 | WELL . AAAAA_2 :WELL 13 | FLD . WILDCAT :FIELD 14 | LOC . 12-34-12-34W5M :LOCATION 15 | PROV . ALBERTA :PROVINCE 16 | SRVC . ANY LOGGING COMPANY INC. :SERVICE COMPANY 17 | DATE . 13-DEC-86 :LOG DATE 18 | API . 05001095820000 :UNIQUE WELL ID 19 | ~CURVE INFORMATION 20 | #MNEM.UNIT API CODES CURVE DESCRIPTION 21 | #------------------ ------------ ------------------------- 22 | DEPT .M : 1 DEPTH 23 | DT .US/M 60 520 32 00 : 2 SONIC TRANSIT TIME 24 | RHOB .K/M3 45 350 01 00 : 3 BULK DENSITY 25 | NPHI .V/V 42 890 00 00 : 4 NEUTRON POROSITY 26 | SFLU .OHMM 07 220 04 00 : 5 SHALLOW RESISTIVITY 27 | SFLA .OHMM 07 222 01 00 : 6 SHALLOW RESISTIVITY 28 | ILM .OHMM 07 120 44 00 : 7 MEDIUM RESISTIVITY 29 | ILD .OHMM 07 120 46 00 : 8 DEEP RESISTIVITY 30 | ~PARAMETER INFORMATION 31 | #MNEM.UNIT VALUE DESCRIPTION 32 | #-------------- ---------------- ----------------------------------------------- 33 | MUD . GEL CHEM : MUD TYPE 34 | BHT .DEGC 35.5000 : BOTTOM HOLE TEMPERATURE 35 | BS .MM 200.0000 : BIT SIZE 36 | FD .K/M3 1000.0000 : FLUID DENSITY 37 | MATR . SAND : NEUTRON MATRIX 38 | MDEN . 2710.0000 : LOGGING MATRIX DENSITY 39 | RMF .OHMM 0.2160 : MUD FILTRATE RESISTIVITY 40 | DFD .K/M3 1525.0000 : DRILL FLUID DENSITY 41 | ~OTHER 42 | Note: The logging tools became stuck at 625 metres causing the data 43 | between 625 metres and 615 metres to be invalid. 44 | ~A DEPTH DT RHOB NPHI SFLU SFLA ILM ILD 45 | 1670.000 123.450 2550.000 0.450 123.450 123.450 110.200 105.600 46 | 1669.875 123.450 2550.000 0.450 123.450 123.450 110.200 105.600 47 | 1669.750 123.450 2550.000 0.450 123.450 123.450 110.200 105.600 48 | -------------------------------------------------------------------------------- /inst/extdata/2.0/sample_2.0_inf_uwi_leading_zero.las: -------------------------------------------------------------------------------- 1 | ~VERSION INFORMATION 2 | VERS. 2.0 : CWLS LOG ASCII STANDARD -VERSION 2.0 3 | WRAP. NO : ONE LINE PER DEPTH STEP 4 | ~WELL INFORMATION 5 | #MNEM.UNIT DATA DESCRIPTION 6 | #----- ----- ---------- ------------------------- 7 | STRT .M 1670.0000 :START DEPTH 8 | STOP .M 1660.0000 :STOP DEPTH 9 | STEP .M -0.1250 :STEP 10 | NULL . -999.25 :NULL VALUE 11 | COMP . ANY OIL COMPANY INC. :COMPANY 12 | WELL . AAAAA_2 :WELL 13 | FLD . WILDCAT :FIELD 14 | LOC . 12-34-12-34W5M :LOCATION 15 | PROV . ALBERTA :PROVINCE 16 | SRVC . ANY LOGGING COMPANY INC. :SERVICE COMPANY 17 | DATE . 13-DEC-86 :LOG DATE 18 | UWI . 05001095820000 :UNIQUE WELL ID 19 | ~CURVE INFORMATION 20 | #MNEM.UNIT API CODES CURVE DESCRIPTION 21 | #------------------ ------------ ------------------------- 22 | DEPT .M : 1 DEPTH 23 | DT .US/M 60 520 32 00 : 2 SONIC TRANSIT TIME 24 | RHOB .K/M3 45 350 01 00 : 3 BULK DENSITY 25 | NPHI .V/V 42 890 00 00 : 4 NEUTRON POROSITY 26 | SFLU .OHMM 07 220 04 00 : 5 SHALLOW RESISTIVITY 27 | SFLA .OHMM 07 222 01 00 : 6 SHALLOW RESISTIVITY 28 | ILM .OHMM 07 120 44 00 : 7 MEDIUM RESISTIVITY 29 | ILD .OHMM 07 120 46 00 : 8 DEEP RESISTIVITY 30 | ~PARAMETER INFORMATION 31 | #MNEM.UNIT VALUE DESCRIPTION 32 | #-------------- ---------------- ----------------------------------------------- 33 | MUD . GEL CHEM : MUD TYPE 34 | BHT .DEGC 35.5000 : BOTTOM HOLE TEMPERATURE 35 | BS .MM 200.0000 : BIT SIZE 36 | FD .K/M3 1000.0000 : FLUID DENSITY 37 | MATR . SAND : NEUTRON MATRIX 38 | MDEN . 2710.0000 : LOGGING MATRIX DENSITY 39 | RMF .OHMM 0.2160 : MUD FILTRATE RESISTIVITY 40 | DFD .K/M3 1525.0000 : DRILL FLUID DENSITY 41 | ~OTHER 42 | Note: The logging tools became stuck at 625 metres causing the data 43 | between 625 metres and 615 metres to be invalid. 44 | ~A DEPTH DT RHOB NPHI SFLU SFLA ILM ILD 45 | 1670.000 123.450 2550.000 0.450 123.450 123.450 110.200 105.600 46 | 1669.875 123.450 2550.000 0.450 123.450 123.450 110.200 105.600 47 | 1669.750 123.450 2550.000 0.450 123.450 123.450 110.200 105.600 48 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Background" 3 | author: "Matthew Dick & Kane Maxwell" 4 | date: "`r Sys.Date()`" 5 | output: 6 | md_document: 7 | variant: markdown_github 8 | bibliography: lastools.bib 9 | 10 | --- 11 | 12 | ## About lastools 13 | 14 | 'lastools' is an R package for reading and writing [version 1.2](http://www.cwls.org/wp-content/uploads/2014/09/LAS12_Standards.txt) and [version 2.0](http://www.cwls.org/wp-content/uploads/2017/02/Las2_Update_Feb2017.pdf) Log ASCII Standard (LAS) files (@las_spec_v1, @las_spec_v2) and for performing common functions on LAS files including: 15 | 16 | * version conversion 17 | 18 | * depth conversion 19 | 20 | * merging 21 | 22 | * visualizing/plotting 23 | 24 | * re-sampling/filtering 25 | 26 | * bulk loading to R data.table 27 | 28 | While the Canadian Well Logging Society provides free software called [LasApps](http://www.cwls.org/wp-content/uploads/2017/02/CWLS_LasApps_v2_4_14.msi) (@las_cws_front_page) and a Python package called [lasio](http://pythonhosted.org/lasio/index.html) (@lasio) exists to perform similar functions; at time of writing no R package existed for reading and manipulating LAS files. 29 | 30 | The aim of lastools is to provide functionality for reading and manipulating LAS files in the R environment and to provide additional unique functionality not found in existing alternative packages/libraries/software. 31 | 32 | ## Installation 33 | 34 | lastools can be installed from github repository using devtools: 35 | 36 | 37 | devtools::install_github("Gitmaxwell/lastools") 38 | 39 | 40 | ## LAS Data 41 | 42 | ### Las file 43 | 44 | A LAS file is a standardized, structured ASCII file containing header information and log curve data derived from the continuous collection of (usually geophysical) measurements from a borehole or well (@reeves). They are often termed wireline log LAS files and have the file extension ".las". They are _distinct_ from 'lidar' LAS files which are 'industry-standard binary format files for storing [airborne lidar data](http://desktop.arcgis.com/en/arcmap/10.3/manage-data/las-dataset/what-is-a-las-dataset-.htm). 45 | 46 | The LAS standard was introduced by the Canadian Well Logging Society in 1989 and to date consists of 3 Versions (version 1.2 (1989), 2.0 (1992) & 3.0 (1999)) (@las_preamble). Version 2.0 replaced inconsistencies in version 1.2 while version 3.0 clarified some of the poorly defined specifications of LAS 2.0 and provides expanded data storage capabilities (@las_spec_v1, @las_spec_v2, @las_spec_v3). Despite version 3.0 being the 'latest' version, its implementation and usage has been extremely limited (@las_preamble). Following this, the package lastools only provides support for LAS file versions 1.2 & 2.0. 47 | 48 | The exact standards and structure/s for each LAS file type can be accessed via the below links (and/or from the embedded hyperlinks elsewhere): 49 | 50 | [Las Standard 1.2](http://www.cwls.org/wp-content/uploads/2014/09/LAS12_Standards.txt) 51 | 52 | [Las Standard 2.0](http://www.cwls.org/wp-content/uploads/2017/02/Las2_Update_Feb2017.pdf) 53 | 54 | [Las Standard 3.0](http://www.cwls.org/wp-content/uploads/2014/09/LAS_3_File_Structure.pdf) 55 | 56 | ## References 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /R/las_read_data_dt.R: -------------------------------------------------------------------------------- 1 | #' Read las log data to data table 2 | #' 3 | #' Reads CWLS LAS file/s from a directory location and returns a data.table of the log section data by well 4 | #' @param dir string directory path containing .las file/s 5 | #' @return Returns a long format data.table containing well_name, DEPT (Depth),variable,value and file (location of las file) 6 | #' @export 7 | #' @import data.table 8 | 9 | read_las_data_dt <- function (dir) { 10 | laslist <- list.files(dir, pattern = "\\.las$",recursive = TRUE,full.names = T, ignore.case = T) 11 | las_data <- lapply(laslist,function(x) try(get_las_data_dt(x))) 12 | #check list items to see if they are data tables 13 | isdt <- lapply(seq(1,length(las_data)), function (x) data.table::is.data.table(las_data[[x]])) 14 | #only bind list items which are data tables 15 | las_data <- data.table::rbindlist(las_data[unlist(isdt)]) 16 | return(las_data) 17 | } 18 | 19 | #function passes info into read_las_data_df 20 | 21 | get_las_data_dt <- function(x) 22 | { 23 | las <- lastools::read_las(x) 24 | filename <- x 25 | 26 | #cleaning up the file 27 | #remove trailing and leading white spaces and spaces and convert to upper 28 | well_name <- subset(las$WELL$VALUE,las$WELL$MNEM =="WELL") 29 | well_name <- trimws(well_name, which = c("both")) 30 | well_name <- gsub(" ", "",well_name, fixed = TRUE) 31 | well_name <- toupper(well_name) 32 | 33 | las$CURVE$MNEM <- trimws(las$CURVE$MNEM, which = c("both")) 34 | las$CURVE$MNEM <- gsub(" ", "",las$CURVE$MNEM, fixed = TRUE) 35 | las$CURVE$MNEM <- toupper(las$CURVE$MNEM) 36 | las$CURVE$UNIT <- trimws(las$CURVE$UNIT, which = c("both")) 37 | las$CURVE$UNIT <- gsub(" ", "",las$CURVE$UNIT, fixed = TRUE) 38 | las$CURVE$UNIT <- toupper(las$CURVE$UNIT) 39 | las$CURVE$MNEM <- trimws(las$CURVE$MNEM, which = c("both")) 40 | las$CURVE$MNEM <- gsub(" ", "",las$CURVE$MNEM, fixed = TRUE) 41 | las$CURVE$MNEM <- toupper(las$CURVE$MNEM) 42 | las$CURVE$DESCRIPTION <- trimws(las$CURVE$DESCRIPTION, which = c("both")) 43 | #las$CURVE$DESCRIPTION <- gsub(" ", "",las$CURVE$DESCRIPTION, fixed = TRUE) 44 | las$CURVE$DESCRIPTION <- toupper(las$CURVE$DESCRIPTION) 45 | 46 | #now melt the curve data to join to the log data 47 | curvemap <- as.data.frame(las$CURVE$MNEM) 48 | data.table::setDT(curvemap) 49 | curvemap$Units <- (las$CURVE$UNIT) 50 | curvemap$Descr <- (las$CURVE$DESCRIPTION) 51 | #concatonate the varaibale and units name 52 | curvemap$MNEM.UNIT <- paste(las$CURVE$MNEM, las$CURVE$UNIT , sep=".") 53 | names(curvemap) <- c("variable","Units","Descr", "MNEM.UNIT") 54 | 55 | #melt the data table to long format 56 | df <- las$LOG 57 | data.table::setDT(df) 58 | dfmelt <- data.table::melt(df, id=1) 59 | dfmelt$value <- as.numeric(dfmelt$value) 60 | #remove resultant NA values 61 | dfmelt$value <- ifelse(dfmelt$value <0,NA,dfmelt$value) 62 | dfmelt <- subset(dfmelt, !(is.na(dfmelt$value))) 63 | dfmelt$well_name <- subset(las$WELL$VALUE,las$WELL$MNEM =="WELL") 64 | dfmelt$well_name <- ifelse(is.na(dfmelt$well_name),filename, dfmelt$well_name) 65 | dfmelt$file <- filename 66 | #merge the description and units 67 | #dfmelt <- merge(dfmelt,curvemap) 68 | dfmelt <- curvemap[dfmelt, on=.(variable=variable),nomatch=0,allow.cartesian=TRUE] 69 | names(dfmelt) <- c("variable","Units","Descr","MNEM.UNIT", "DEPT","value","well_name","file") 70 | dfmelt <- dfmelt[order(well_name,variable,Descr,DEPT)] 71 | dfmelt$DEPT <- round(as.numeric(dfmelt$DEPT),3) 72 | return(dfmelt) 73 | } 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /inst/extdata/2.0/sample_2.0_wrapped.las: -------------------------------------------------------------------------------- 1 | ~VERSION INFORMATION 2 | VERS. 2.0 : CWLS log ASCII Standard -VERSION 2.0 3 | WRAP. YES : Multiple lines per depth step 4 | ~WELL INFORMATION 5 | #MNEM.UNIT DATA DESCRIPTION 6 | #----- ----- ---------- ----------------------------- 7 | STRT .M 910.0000 :START DEPTH 8 | STOP .M 909.5000 :STOP DEPTH 9 | STEP .M -0.1250 :STEP 10 | NULL . -999.25 :NULL VALUE 11 | COMP . ANY OIL COMPANY INC. :COMPANY 12 | WELL . ANY ET AL 12-34-12-34 :WELL 13 | FLD . WILDCAT :FIELD 14 | LOC . 12-34-12-34W5M :LOCATION 15 | PROV . ALBERTA :PROVINCE 16 | SRVC . ANY LOGGING COMPANY INC. :SERVICE COMPANY 17 | SON . 142085 :SERVICE ORDER NUMBER 18 | DATE . 13-DEC-86 :LOG DATE 19 | UWI . 100123401234W500 :UNIQUE WELL ID 20 | ~CURVE INFORMATION 21 | #MNEM.UNIT Curve Description 22 | #--------- ------------------------------ 23 | DEPT .M : Depth 24 | DT .US/M : 1 Sonic Travel Time 25 | RHOB .K/M : 2 Density-Bulk Density 26 | NPHI .V/V : 3 Porosity -Neutron 27 | RX0 .OHMM : 4 Resistivity -Rxo 28 | RESS .OHMM : 5 Resistivity -Shallow 29 | RESM .OHMM : 6 Resistivity -Medium 30 | RESD .OHMM : 7 Resistivity -Deep 31 | SP .MV : 8 Spon. Potential 32 | GR .GAPI : 9 Gamma Ray 33 | CALI .MM : 10 Caliper 34 | DRHO .K/M3 : 11 Delta-Rho 35 | EATT .DBM : 12 EPT Attenuation 36 | TPL .NS/M : 13 TP -EPT 37 | PEF . : 14 PhotoElectric Factor 38 | FFI .V/V : 15 Porosity -NML FFI 39 | DCAL .MM : 16 Caliper-Differential 40 | RHGF .K/M3 : 17 Density-Formation 41 | RHGA .K/M3 : 18 Density-Apparent 42 | SPBL .MV : 19 Baselined SP 43 | GRC .GAPI : 20 Gamma Ray BHC 44 | PHIA .V/V : 21 Porosity -Apparent 45 | PHID .V/V : 22 Porosity -Density 46 | PHIE .V/V : 23 Porosity -Effective 47 | PHIN .V/V : 24 Porosity -Neut BHC 48 | PHIC .V/V : 25 Porosity -Total HCC 49 | R0 .OHMM : 26 Ro 50 | RWA .OHMM : 27 Rfa 51 | SW . : 28 Sw -Effective 52 | MSI . : 29 Sh Idx -Min 53 | BVW . : 30 BVW 54 | FGAS . : 31 Flag -Gas Index 55 | PIDX . : 32 Prod Idx 56 | FBH . : 33 Flag -Bad Hole 57 | FHCC . : 34 Flag -HC Correction 58 | LSWB . : 35 Flag -Limit SWB 59 | ~A Log data section 60 | 910.000000 61 | -999.2500 2692.7075 0.3140 19.4086 19.4086 13.1709 12.2681 62 | -1.5010 96.5306 204.7177 30.5822 -999.2500 -999.2500 3.2515 63 | -999.2500 4.7177 3025.0264 3025.0264 -1.5010 93.1378 0.1641 64 | 0.0101 0.1641 0.3140 0.1641 11.1397 0.3304 0.9529 65 | 0.0000 0.1564 0.0000 11.1397 0.0000 0.0000 0.0000 66 | 909.875000 67 | -999.2500 2712.6460 0.2886 23.3987 23.3987 13.6129 12.4744 68 | -1.4720 90.2803 203.1093 18.7566 -999.2500 -999.2500 3.7058 69 | -999.2500 3.1093 3004.6050 3004.6050 -1.4720 86.9078 0.1456 70 | -0.0015 0.1456 0.2886 0.1456 14.1428 0.2646 1.0000 71 | 0.0000 0.1456 0.0000 14.1428 0.0000 0.0000 0.0000 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | About lastools 2 | -------------- 3 | 4 | ‘lastools’ is an R package for reading and writing [version 5 | 1.2](http://www.cwls.org/wp-content/uploads/2014/09/LAS12_Standards.txt) 6 | and [version 7 | 2.0](http://www.cwls.org/wp-content/uploads/2017/02/Las2_Update_Feb2017.pdf) 8 | Log ASCII Standard (LAS) files (Canadian Well Logging Society (1990), 9 | Canadian Well Logging Society (2017d)) and for performing common 10 | functions on LAS files including: 11 | 12 | - version conversion 13 | 14 | - depth conversion 15 | 16 | - merging 17 | 18 | - visualizing/plotting 19 | 20 | - re-sampling/filtering 21 | 22 | - bulk loading to R data.table 23 | 24 | While the Canadian Well Logging Society provides free software called 25 | [LasApps](http://www.cwls.org/wp-content/uploads/2017/02/CWLS_LasApps_v2_4_14.msi) 26 | (Canadian Well Logging Society (2017c)) and a Python package called 27 | [lasio](http://pythonhosted.org/lasio/index.html) (Inverarity (2017)) 28 | exists to perform similar functions; at time of writing no R package 29 | existed for reading and manipulating LAS files. 30 | 31 | The aim of lastools is to provide functionality for reading and 32 | manipulating LAS files in the R environment and to provide additional 33 | unique functionality not found in existing alternative 34 | packages/libraries/software. 35 | 36 | Installation 37 | ------------ 38 | 39 | lastools can be installed from github repository using devtools: 40 | 41 | devtools::install\_github("Gitmaxwell/lastools") 42 | 43 | LAS Data 44 | -------- 45 | 46 | ### Las file 47 | 48 | A LAS file is a standardized, structured ASCII file containing header 49 | information and log curve data derived from the continuous collection of 50 | (usually geophysical) measurements from a borehole or well (Firth 51 | (n.d.)). They are often termed wireline log LAS files and have the file 52 | extension “.las”. They are *distinct* from ‘lidar’ LAS files which are 53 | ’industry-standard binary format files for storing [airborne lidar 54 | data](http://desktop.arcgis.com/en/arcmap/10.3/manage-data/las-dataset/what-is-a-las-dataset-.htm). 55 | 56 | The LAS standard was introduced by the Canadian Well Logging Society in 57 | 1989 and to date consists of 3 Versions (version 1.2 (1989), 2.0 (1992) 58 | & 3.0 (1999)) (Canadian Well Logging Society (2017a)). Version 2.0 59 | replaced inconsistencies in version 1.2 while version 3.0 clarified some 60 | of the poorly defined specifications of LAS 2.0 and provides expanded 61 | data storage capabilities (Canadian Well Logging Society (1990), 62 | Canadian Well Logging Society (2017d), Canadian Well Logging Society 63 | (2017b)). Despite version 3.0 being the ‘latest’ version, its 64 | implementation and usage has been extremely limited (Canadian Well 65 | Logging Society (2017a)). Following this, the package lastools only 66 | provides support for LAS file versions 1.2 & 2.0. 67 | 68 | The exact standards and structure/s for each LAS file type can be 69 | accessed via the below links (and/or from the embedded hyperlinks 70 | elsewhere): 71 | 72 | [Las Standard 73 | 1.2](http://www.cwls.org/wp-content/uploads/2014/09/LAS12_Standards.txt) 74 | 75 | [Las Standard 76 | 2.0](http://www.cwls.org/wp-content/uploads/2017/02/Las2_Update_Feb2017.pdf) 77 | 78 | [Las Standard 79 | 3.0](http://www.cwls.org/wp-content/uploads/2014/09/LAS_3_File_Structure.pdf) 80 | 81 | References 82 | ---------- 83 | 84 | Canadian Well Logging Society. 1990. “LAS 1.2 a Floppy Disk Standard for 85 | Log Data.” Connecticut, USA: Canadian Well Logging Society. 1990. 86 | . 87 | 88 | ———. 2017a. “HELP: Load - Log Ascii Standard (Las) File, Versions 2.0 & 89 | 3.0.” Connecticut, USA: Canadian Well Logging Society. 2017. 90 | . 91 | 92 | ———. 2017b. “Las 3.0 Log Ascii Standard.” Connecticut, USA: Canadian 93 | Well Logging Society. 2017. 94 | . 95 | 96 | ———. 2017c. “LAS (Log Ascii Standard).” Connecticut, USA: Canadian Well 97 | Logging Society. 2017. . 98 | 99 | ———. 2017d. “LAS Version 2.0: A Digital Standard for Logs.” 100 | . 101 | 102 | Firth, David. n.d. “Log Analysis for Mining Applications.” 103 | 104 | Inverarity, Kent. 2017. “Lasio - Log Ascii Standard (Las) Files in 105 | Python.” Kent Inverarity. 2017. . 106 | -------------------------------------------------------------------------------- /inst/extdata/1.2/sample_wrapped.las: -------------------------------------------------------------------------------- 1 | ~Version Information 2 | VERS. 1.20: CWLS log ASCII Standard -VERSION 1.20 3 | WRAP. YES: Multiple lines per depth step 4 | ~Well Information 5 | #MNEM.UNIT Data Type Information 6 | #--------- ------------- ------------------------------ 7 | STRT.M 910.000: 8 | STOP.M 901.000: 9 | STEP.M -0.1250: 10 | NULL. -999.2500: Null value 11 | COMP. COMPANY: ANY OIL COMPANY INC. 12 | WELL. WELL: ANY ET AL XX-XX-XX-XX 13 | FLD . FIELD: WILDCAT 14 | LOC . LOCATION: XX-XX-XX-XXW3M 15 | PROV. PROVINCE: SASKATCHEWAN 16 | SRVC. SERVICE COMPANY: ANY LOGGING COMPANY INC. 17 | SON . SERVICE ORDER : 142085 18 | DATE. LOG DATE: 13-DEC-86 19 | UWI . UNIQUE WELL ID: 20 | ~Curve Information 21 | #MNEM.UNIT API CODE Curve Description 22 | #--------- ------------- ------------------------------ 23 | DEPT.M : Depth 24 | DT .US/M : 1 Sonic Travel Time 25 | RHOB.K/M : 2 Density-Bulk Density 26 | NPHI.V/V : 3 Porosity -Neutron 27 | RX0 .OHMM : 4 Resistivity -Rxo 28 | RESS.OHMM : 5 Resistivity -Shallow 29 | RESM.OHMM : 6 Resistivity -Medium 30 | RESD.OHMM : 7 Resistivity -Deep 31 | SP .MV : 8 Spon. Potential 32 | GR .GAPI : 9 Gamma Ray 33 | CALI.MM : 10 Caliper 34 | DRHO.K/M3 : 11 Delta-Rho 35 | EATT.DBM : 12 EPT Attenuation 36 | TPL .NS/M : 13 TP -EPT 37 | PEF . : 14 PhotoElectric Factor 38 | FFI .V/V : 15 Porosity -NML FFI 39 | DCAL.MM : 16 Caliper-Differential 40 | RHGF.K/M3 : 17 Density-Formation 41 | RHGA.K/M3 : 18 Density-Apparent 42 | SPBL.MV : 19 Baselined SP 43 | GRC .GAPI : 20 Gamma Ray BHC 44 | PHIA.V/V : 21 Porosity -Apparent 45 | PHID.V/V : 22 Porosity -Density 46 | PHIE.V/V : 23 Porosity -Effective 47 | PHIN.V/V : 24 Porosity -Neut BHC 48 | PHIC.V/V : 25 Porosity -Total HCC 49 | R0 .OHMM : 26 Ro 50 | RWA .OHMM : 27 Rfa 51 | SW . : 28 Sw -Effective 52 | MSI . : 29 Sh Idx -Min 53 | BVW . : 30 BVW 54 | FGAS. : 31 Flag -Gas Index 55 | PIDX. : 32 Prod Idx 56 | FBH . : 33 Flag -Bad Hole 57 | FHCC. : 34 Flag -HC Correction 58 | LSWB. : 35 Flag -Limit SWB 59 | ~A Log data section 60 | 910.000000 61 | -999.2500 2692.7075 0.3140 19.4086 19.4086 13.1709 12.2681 62 | -1.5010 96.5306 204.7177 30.5822 -999.2500 -999.2500 3.2515 63 | -999.2500 4.7177 3025.0264 3025.0264 -1.5010 93.1378 0.1641 64 | 0.0101 0.1641 0.3140 0.1641 11.1397 0.3304 0.9529 65 | 0.0000 0.1564 0.0000 11.1397 0.0000 0.0000 0.0000 66 | 909.875000 67 | -999.2500 2712.6460 0.2886 23.3987 23.3987 13.6129 12.4744 68 | -1.4720 90.2803 203.1093 18.7566 -999.2500 -999.2500 3.7058 69 | -999.2500 3.1093 3004.6050 3004.6050 -1.4720 86.9078 0.1456 70 | -0.0015 0.1456 0.2886 0.1456 14.1428 0.2646 1.0000 71 | 0.0000 0.1456 0.0000 14.1428 0.0000 0.0000 0.0000 72 | 909.750000 73 | -999.2500 2692.8137 0.2730 22.5909 22.5909 13.6821 12.6146 74 | -1.4804 89.8492 201.9287 3.1551 -999.2500 -999.2500 4.3124 75 | -999.2500 1.9287 2976.4451 2976.4451 -1.4804 86.3465 0.1435 76 | 0.0101 0.1435 0.2730 0.1435 14.5674 0.2598 1.0000 77 | 0.0000 0.1435 0.0000 14.5674 0.0000 0.0000 0.0000 78 | 909.625000 79 | -999.2500 2644.3650 0.2765 18.4831 18.4831 13.4159 12.6900 80 | -1.5010 93.3999 201.5826 -6.5861 -999.2500 -999.2500 4.3822 81 | -999.2500 1.5826 2955.3528 2955.3528 -1.5010 89.7142 0.1590 82 | 0.0384 0.1590 0.2765 0.1590 11.8600 0.3210 0.9667 83 | 0.0000 0.1538 0.0000 11.8600 0.0000 0.0000 0.0000 84 | 909.500000 85 | -999.2500 2586.2822 0.2996 13.9187 13.9187 12.9195 12.7016 86 | -1.4916 98.1214 201.7126 -4.5574 -999.2500 -999.2500 3.5967 87 | -999.2500 1.7126 2953.5940 2953.5940 -1.4916 94.2670 0.1880 88 | 0.0723 0.1880 0.2996 0.1880 8.4863 0.4490 0.8174 89 | 0.0000 0.1537 0.0000 8.4863 0.0000 0.0000 0.0000 90 | -------------------------------------------------------------------------------- /R/las_write.R: -------------------------------------------------------------------------------- 1 | #' Write LAS object to LAS file 2 | #' 3 | #' Reads CWLS LAS file/s from a directory location and returns a data.table of the log section data by well 4 | #' @param las In memory las object 5 | #' @param path Directory path to write las file 6 | #' @return Writes a las object as a LAS file to disk 7 | #' @export 8 | #' 9 | 10 | write_las <- function(las, path) { 11 | las_string <- .las_to_vector(las) 12 | writeLines(las_string, path) 13 | } 14 | 15 | .las_section_to_string <- function(df, section, ver="2.0") { 16 | # Convert a section block of LAS files to a string vector to ready to write back out as a .LAS file 17 | # 18 | # Takes a data.frame object from a LAS object (WELL, PARAM, CURVE) and converts it to a formatted table as #a string 19 | # vector. 20 | # @param df A valid data.frame from a LAS object, needs to be one of the WELL, PARAM or CURVE tables 21 | # @param section The associated section code for the data.frame (~W, ~P, ~C) as a string 22 | # @return returns a formatted table as a string vector with line breaks - ready to pass to writeLines 23 | # @export 24 | # 25 | if (is.null(df)) return("#") 26 | 27 | 28 | if (ver == 1.2 & section == "~W") { 29 | in_value_desc_order <- c("STRT", "STOP", "STEP", "NULL") 30 | 31 | # If df[,1] (the mnemonic field) is a member of 32 | # 'in_value_desc_order' then do nothing 33 | # otherwise 34 | # swap the value and description entries for v1.2 well format 35 | ifelse (df[,1] %in% in_value_desc_order, "", { 36 | tmp <- df[,3] 37 | df[,3] <- df[,4] 38 | df[,4] <- tmp 39 | } 40 | ) 41 | } 42 | 43 | df[,1] <- stringr::str_c(df[,1], ".", df[,2]) 44 | df[,1] <- stringr::str_pad(df[,1], width = 10, side = "right") 45 | space_width = max(nchar(df[,2])) + max(nchar(df[,1])) + 15 46 | df[,3] <- stringr::str_pad(df[,3], width = space_width, side = "left") 47 | df[,3] <- stringr::str_c(df[,3], ":") 48 | df[,4] <- stringr::str_pad(df[,4], width = space_width, side = "left") 49 | 50 | df_out <- stringr::str_c(df[,1], df[,3], df[,4]) 51 | 52 | first_line <- section 53 | second_line <- "#MNEM.UNIT VALUE/NAME DESCRIPTION" 54 | third_line <- "#------------ --------------- --------------------" 55 | lines = c(first_line, second_line, third_line, df_out, "#", "#") 56 | lines_out <- paste(lines, sep = "\n") 57 | return(lines_out) 58 | } 59 | 60 | .las_version_to_string <- function(version = c(1.2, 2.0)) { 61 | 62 | # Get appropriate Version and Wrapping lines for a valid CWLS LAS file 63 | # 64 | # Returns a string containing the version and wrap lines for the desired version code (1.2 and 2.0 #supported) 65 | # @param version numeric one of 1.2 or 2.0 66 | # @return returns a vector with the VERSION and WRAP lines - ready to pass to writeLines 67 | # @export 68 | # 69 | if (version == 1.2) return(c("~Version Information", " VERS. 1.2 : CWLS LOG ASCII STANDARD - VERSION 1.2", " WRAP. NO : One line per depth step", "#", "#")) 70 | if (version == 2.0) return(c("~Version Information", " VERS. 2.0 : CWLS LOG ASCII STANDARD - VERSION 2.0", " WRAP. NO : One line per depth step", "#", "#")) 71 | } 72 | 73 | 74 | 75 | .las_data_to_string <- function(las) { 76 | # Convert the LOG section of a LAS object to a string for writing 77 | # 78 | # Returns a string vector version of the LOG data from the LAS file, ready to passed to writeLines 79 | # @param las The log file to be converted to a string vector 80 | # @return returns a table formatted as a string vector ready to be passed to writeLines 81 | # @export 82 | # 83 | df = las$LOG 84 | null_char = las$WELL$VALUE[las$WELL$MNEM == "NULL"] 85 | df[is.na(df)] <- null_char 86 | header = paste(c("~A", colnames(df)), collapse = " ") 87 | df1 = apply(df, MARGIN = 2, function(x) stringr::str_pad(x,width = 11, side = "left")) 88 | df2 = apply(df1, MARGIN = 1, function(x) paste(x, collapse = "")) 89 | df_out = c(header, df2) 90 | return(df_out) 91 | } 92 | 93 | 94 | 95 | .las_to_vector <- function(las) { 96 | # Converts an entire LAS object to a string object ready to be written to a file 97 | # 98 | # Convenience function to convert each piece of a LAS object back into formatted strings to write with writeLines 99 | # @param las The the LAS object (as created with read_las) 100 | # @return returns a string vector with all the appropriate formatting to write the LAS object back out to a .LAS file using writeLines 101 | # @export 102 | # 103 | 104 | 105 | version_info <- .las_version_to_string(las$VERSION) 106 | well_info <- .las_section_to_string(las$WELL, "~W", las$VERSION) 107 | curve_info <- .las_section_to_string(las$CURVE, "~C", las$VERSION) 108 | param_info <- .las_section_to_string(las$PARAM, "~P", las$VERSION) 109 | other_info <- c("~O", las$OTHER) 110 | data_out <- .las_data_to_string(las) 111 | lines_out <- c(version_info, well_info, curve_info, param_info, other_info,"#", "#", data_out) 112 | return(lines_out) 113 | } 114 | 115 | 116 | -------------------------------------------------------------------------------- /inst/doc/Background.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Background" 3 | author: "Matthew Dick & Kane Maxwell" 4 | date: "`r Sys.Date()`" 5 | output: 6 | rmarkdown::html_vignette: 7 | toc: true 8 | number_sections: false 9 | toc_depth: 5 10 | bibliography: lastools.bib 11 | 12 | vignette: > 13 | %\VignetteIndexEntry{Background} 14 | %\VignetteEngine{knitr::rmarkdown} 15 | %\VignetteEncoding{UTF-8} 16 | --- 17 | 18 | ## About lastools 19 | 20 | 'lastools' is an R package for reading and writing [version 1.2](http://www.cwls.org/wp-content/uploads/2014/09/LAS12_Standards.txt) and [version 2.0](http://www.cwls.org/wp-content/uploads/2017/02/Las2_Update_Feb2017.pdf) Log ASCII Standard (LAS) files (@las_spec_v1, @las_spec_v2) and for performing common functions on LAS files including: 21 | 22 | * version conversion 23 | 24 | * depth conversion 25 | 26 | * merging 27 | 28 | * visualizing/plotting 29 | 30 | * re-sampling/filtering 31 | 32 | * bulk loading to R data.table 33 | 34 | 35 | Although there are existing software which provide similar functionality (@las_cws_front_page, @lasio), at time of writing no R package existed for reading/writing and manipulating LAS files. The aim of lastools is to provide this functionality in the R environment and to provide additional unique functionality not found in existing alternative packages/libraries/software. 36 | 37 | ## LAS Data 38 | 39 | ### Las file 40 | 41 | A LAS file is a standardized, structured ASCII file containing header information and log curve data derived from the continuous collection of (usually geophysical) measurements from a borehole or well (@reeves). They are often termed wireline log LAS files and have the file extension ".las". They are _distinct_ from 'lidar' LAS files which are 'industry-standard binary format files for storing [airborne lidar data](http://desktop.arcgis.com/en/arcmap/10.3/manage-data/las-dataset/what-is-a-las-dataset-.htm). 42 | 43 | The LAS standard was introduced by the Canadian Well Logging Society in 1989 and to date consists of 3 Versions (version 1.2 (1989), 2.0 (1992) & 3.0 (1999)) (@las_preamble). Version 2.0 replaced inconsistencies in version 1.2 while version 3.0 clarified some of the poorly defined specifications of LAS 2.0 and provides expanded data storage capabilities (@las_spec_v1, @las_spec_v2, @las_spec_v3). Despite version 3.0 being the 'latest' version, its implementation and usage has been extremely limited (@las_preamble). Following this, the package lastools only provides support for LAS file versions 1.2 & 2.0. 44 | 45 | The exact standards and structure/s for each LAS file type can be accessed via the below links (and/or from the embedded hyperlinks elsewhere): 46 | 47 | [Las Standard 1.2](http://www.cwls.org/wp-content/uploads/2014/09/LAS12_Standards.txt) 48 | 49 | [Las Standard 2.0](http://www.cwls.org/wp-content/uploads/2017/02/Las2_Update_Feb2017.pdf) 50 | 51 | [Las Standard 3.0](http://www.cwls.org/wp-content/uploads/2014/09/LAS_3_File_Structure.pdf) 52 | 53 | ### Data aquisition 54 | 55 | The process of collecting the information stored in a LAS file is often termed 'wireline' or 'slimline' logging. This is because a tool (termed a sonde) (for measuring responses) is usually sent down a well/borehole by steel cable (wireline) which enables electrical signals to be brought to the surface through one or more conducting wires (@reeves). The electrical signals (usually in count rate/voltages) brought to surface are recorded by computer and processed. Processing typically involves merging multiple signal responses with depth, normalising responses with calibration procedures and converting responses into engineering units (e.g grams/cc) (@reeves). This data is then formatted into LAS standard format or alternative formats at request. Finally the data is typically visualized as curve/s (with depth on the y-axis). 56 | 57 | As example, below plots the depth (y-axis) against the repose data (x-axis) using the example LAS 'object' from the lastools data sets. 58 | 59 | 60 | ```{r, echo=TRUE, results='asis',fig.width=8, fig.height=6,fig.align="center", message=FALSE, warning=FALSE} 61 | 62 | library(lastools) 63 | lastools::las_plot(lastools::example_las_obj) 64 | 65 | ``` 66 | 67 | ### Purpose/application 68 | 69 | The data is typically used to characterize rock properties and has been used extensively to support the size and quantity of oil, gas, water and coal resources (@reeves). "The reasons given for running logs invariably include one or more of the following (@reeves): 70 | 71 | * depth to lithological boundaries 72 | 73 | * lithology identification 74 | 75 | * minerals grade/quality 76 | 77 | * inter borehole correlation 78 | 79 | * structure mapping 80 | 81 | * dip determination 82 | 83 | * rock strength 84 | 85 | * in situ stress orientation 86 | 87 | * fracture frequency 88 | 89 | * porosity 90 | 91 | * fluid salinity" 92 | 93 | ### Measurements 94 | 95 | Most common measurements (which are stored in a LAS file) comprise of: nuclear (e.g gamma ray, spectral gamma, gamma density), acoustic (e.g velocity) and electronic (e.g spontaneous potential, resistivity). In addition, borehole geometry measurements such as the axis in three dimensions (verticality) and borehole diameter (especially needed for correction algorithms) may be recorded. 96 | 97 | @reeves provides comprehensive documentation these measurments and their application. 98 | 99 | 100 | # References 101 | 102 | 103 | -------------------------------------------------------------------------------- /vignettes/Background.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Background" 3 | author: "Matthew Dick & Kane Maxwell" 4 | date: "`r Sys.Date()`" 5 | output: 6 | rmarkdown::html_vignette: 7 | toc: true 8 | number_sections: false 9 | toc_depth: 5 10 | bibliography: lastools.bib 11 | 12 | vignette: > 13 | %\VignetteIndexEntry{Background} 14 | %\VignetteEngine{knitr::rmarkdown} 15 | %\VignetteEncoding{UTF-8} 16 | --- 17 | 18 | ## About lastools 19 | 20 | 'lastools' is an R package for reading and writing [version 1.2](http://www.cwls.org/wp-content/uploads/2014/09/LAS12_Standards.txt) and [version 2.0](http://www.cwls.org/wp-content/uploads/2017/02/Las2_Update_Feb2017.pdf) Log ASCII Standard (LAS) files (@las_spec_v1, @las_spec_v2) and for performing common functions on LAS files including: 21 | 22 | * version conversion 23 | 24 | * depth conversion 25 | 26 | * merging 27 | 28 | * visualizing/plotting 29 | 30 | * re-sampling/filtering 31 | 32 | * bulk loading to R data.table 33 | 34 | 35 | Although there are existing software which provide similar functionality (@las_cws_front_page, @lasio), at time of writing no R package existed for reading/writing and manipulating LAS files. The aim of lastools is to provide this functionality in the R environment and to provide additional unique functionality not found in existing alternative packages/libraries/software. 36 | 37 | ## LAS Data 38 | 39 | ### Las file 40 | 41 | A LAS file is a standardized, structured ASCII file containing header information and log curve data derived from the continuous collection of (usually geophysical) measurements from a borehole or well (@reeves). They are often termed wireline log LAS files and have the file extension ".las". They are _distinct_ from 'lidar' LAS files which are 'industry-standard binary format files for storing [airborne lidar data](http://desktop.arcgis.com/en/arcmap/10.3/manage-data/las-dataset/what-is-a-las-dataset-.htm). 42 | 43 | The LAS standard was introduced by the Canadian Well Logging Society in 1989 and to date consists of 3 Versions (version 1.2 (1989), 2.0 (1992) & 3.0 (1999)) (@las_preamble). Version 2.0 replaced inconsistencies in version 1.2 while version 3.0 clarified some of the poorly defined specifications of LAS 2.0 and provides expanded data storage capabilities (@las_spec_v1, @las_spec_v2, @las_spec_v3). Despite version 3.0 being the 'latest' version, its implementation and usage has been extremely limited (@las_preamble). Following this, the package lastools only provides support for LAS file versions 1.2 & 2.0. 44 | 45 | The exact standards and structure/s for each LAS file type can be accessed via the below links (and/or from the embedded hyperlinks elsewhere): 46 | 47 | [Las Standard 1.2](http://www.cwls.org/wp-content/uploads/2014/09/LAS12_Standards.txt) 48 | 49 | [Las Standard 2.0](http://www.cwls.org/wp-content/uploads/2017/02/Las2_Update_Feb2017.pdf) 50 | 51 | [Las Standard 3.0](http://www.cwls.org/wp-content/uploads/2014/09/LAS_3_File_Structure.pdf) 52 | 53 | ### Data aquisition 54 | 55 | The process of collecting the information stored in a LAS file is often termed 'wireline' or 'slimline' logging. This is because a tool (termed a sonde) (for measuring responses) is usually sent down a well/borehole by steel cable (wireline) which enables electrical signals to be brought to the surface through one or more conducting wires (@reeves). The electrical signals (usually in count rate/voltages) brought to surface are recorded by computer and processed. Processing typically involves merging multiple signal responses with depth, normalising responses with calibration procedures and converting responses into engineering units (e.g grams/cc) (@reeves). This data is then formatted into LAS standard format or alternative formats at request. Finally the data is typically visualized as curve/s (with depth on the y-axis). 56 | 57 | As example, below plots the depth (y-axis) against the repose data (x-axis) using the example LAS 'object' from the lastools data sets. 58 | 59 | 60 | ```{r, echo=TRUE, results='asis',fig.width=8, fig.height=6,fig.align="center", message=FALSE, warning=FALSE} 61 | 62 | library(lastools) 63 | lastools::las_plot(lastools::example_las_obj) 64 | 65 | ``` 66 | 67 | ### Purpose/application 68 | 69 | The data is typically used to characterize rock properties and has been used extensively to support the size and quantity of oil, gas, water and coal resources (@reeves). "The reasons given for running logs invariably include one or more of the following (@reeves): 70 | 71 | * depth to lithological boundaries 72 | 73 | * lithology identification 74 | 75 | * minerals grade/quality 76 | 77 | * inter borehole correlation 78 | 79 | * structure mapping 80 | 81 | * dip determination 82 | 83 | * rock strength 84 | 85 | * in situ stress orientation 86 | 87 | * fracture frequency 88 | 89 | * porosity 90 | 91 | * fluid salinity" 92 | 93 | ### Measurements 94 | 95 | Most common measurements (which are stored in a LAS file) comprise of: nuclear (e.g gamma ray, spectral gamma, gamma density), acoustic (e.g velocity) and electronic (e.g spontaneous potential, resistivity). In addition, borehole geometry measurements such as the axis in three dimensions (verticality) and borehole diameter (especially needed for correction algorithms) may be recorded. 96 | 97 | @reeves provides comprehensive documentation these measurments and their application. 98 | 99 | 100 | # References 101 | 102 | 103 | -------------------------------------------------------------------------------- /lastools.bib: -------------------------------------------------------------------------------- 1 | 2 | @ARTICLE{reeves, 3 | author = "David Firth", 4 | title = "Log Analysis for Mining Applications", 5 | publisher = "Reeves" 6 | } 7 | 8 | @online{las_preamble, 9 | author = {{Canadian Well Logging Society}}, 10 | title = {HELP: Load - Log ASCII Standard (LAS) File, versions 2.0 & 3.0}, 11 | publisher = {Canadian Well Logging Society }, 12 | year = {2017}, 13 | url = {http://www.kgs.ku.edu/software/SS/HELP/las/index.html}, 14 | address = {Connecticut, USA} 15 | } 16 | 17 | @online{las_spec_v1, 18 | author = {{Canadian Well Logging Society}}, 19 | title = {LAS 1.2 A Floppy Disk Standard for Log Data}, 20 | publisher = {Canadian Well Logging Society }, 21 | year = {1990}, 22 | urldate = {2014-09-01}, 23 | url = {http://www.cwls.org/wp-content/uploads/2014/09/LAS12_Standards.txt}, 24 | address = {Connecticut, USA} 25 | } 26 | 27 | @article{las_spec_v2, 28 | author = {{Canadian Well Logging Society}}, 29 | title = {LAS Version 2.0: A Digital Standard for Logs}, 30 | publisher = {Canadian Well Logging Society }, 31 | year = {2017}, 32 | url = {http://www.cwls.org/wp-content/uploads/2017/02/Las2_Update_Feb2017.pdf}, 33 | address = {Connecticut, USA} 34 | } 35 | 36 | @online{las_spec_v3, 37 | author = {{Canadian Well Logging Society}}, 38 | title = {Las 3.0 Log ASCII Standard}, 39 | publisher = {Canadian Well Logging Society }, 40 | year = {2017}, 41 | url = {http://www.cwls.org/wp-content/uploads/2014/09/LAS_3_File_Structure.pdf}, 42 | address = {Connecticut, USA} 43 | } 44 | 45 | 46 | @online{las_cws_front_page, 47 | author = {{Canadian Well Logging Society}}, 48 | title = {LAS (Log ASCII Standard)}, 49 | publisher = {Canadian Well Logging Society }, 50 | year = {2017}, 51 | url = {http://www.cwls.org/las/}, 52 | address = {Connecticut, USA} 53 | } 54 | 55 | 56 | @online{lasio, 57 | author = {Kent Inverarity}, 58 | title = {lasio - Log ASCII Standard (LAS) files in Python}, 59 | publisher = {Kent Inverarity}, 60 | year = {2017}, 61 | url = {http://pythonhosted.org/lasio/}, 62 | } 63 | 64 | @online{wiki_well_logging, 65 | author = {{Wikipedia}}, 66 | title = {Well logging}, 67 | publisher = {Wikipedia}, 68 | year = {2017}, 69 | url = {https://en.wikipedia.org/wiki/Well_logging}, 70 | address = {Connecticut, USA} 71 | } 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | @Book{book, 80 | author = {Canadian Well Logging Society}, 81 | title = {LAS Version 2.0: A Digital Standard for Logs,}, 82 | publisher = {Canadian Well Logging Society }, 83 | year = {2017}, 84 | url = {http://www.cwls.org/wp-content/uploads/2017/02/Las2_Update_Feb2017.pdf}, 85 | address = {Connecticut, USA} 86 | } 87 | 88 | 89 | 90 | @inproceedings{proceedings, 91 | author = "Craig Williams and Kirk Henderson and Spencer Summers", 92 | title = "Practical application of drill hole spacing analysis in coal resource estimation", 93 | publisher = "GSA Coal Geology Group", 94 | year = 2015, 95 | pages = "275-285", 96 | booktitle = "Bowen Basin Symposium 2015—Bowen Basin and Beyond" 97 | } 98 | 99 | 100 | 101 | @article{lamichhane_spatial-temporal_2015, 102 | title = {Spatial-Temporal Modeling of Neighborhood Sociodemographic Characteristics and Food Stores}, 103 | volume = {181}, 104 | issn = {0002-9262, 1476-6256}, 105 | url = {http://aje.oxfordjournals.org/content/181/2/137}, 106 | doi = {10.1093/aje/kwu250}, 107 | abstract = {The literature on food stores, neighborhood poverty, and race/ethnicity is mixed and lacks methods of accounting for complex spatial and temporal clustering of food resources. We used quarterly data on supermarket and convenience store locations from Nielsen {TDLinx} (Nielsen Holdings N.V., New York, New York) spanning 7 years (2006–2012) and census tract-based neighborhood sociodemographic data from the American Community Survey (2006–2010) to assess associations between neighborhood sociodemographic characteristics and food store distributions in the Metropolitan Statistical Areas ({MSAs}) of 4 {US} cities (Birmingham, Alabama; Chicago, Illinois; Minneapolis, Minnesota; and San Francisco, California). We fitted a space-time Poisson regression model that accounted for the complex spatial-temporal correlation structure of store locations by introducing space-time random effects in an intrinsic conditionally autoregressive model within a Bayesian framework. After accounting for census tract–level area, population, their interaction, and spatial and temporal variability, census tract poverty was significantly and positively associated with increasing expected numbers of supermarkets among tracts in all 4 {MSAs}. A similar positive association was observed for convenience stores in Birmingham, Minneapolis, and San Francisco; in Chicago, a positive association was observed only for predominantly white and predominantly black tracts. Our findings suggest a positive association between greater numbers of food stores and higher neighborhood poverty, with implications for policy approaches related to food store access by neighborhood poverty.}, 108 | pages = {137--150}, 109 | number = {2}, 110 | journaltitle = {American Journal of Epidemiology}, 111 | shortjournal = {Am. J. Epidemiol.}, 112 | author = {Lamichhane, Archana P. and Warren, Joshua L. and Peterson, Marc and Rummo, Pasquale and Gordon-Larsen, Penny}, 113 | urldate = {2015-10-01}, 114 | date = {2015-01-15}, 115 | langid = {english}, 116 | pmid = {25515169}, 117 | keywords = {food availability, food stores, intrinsic conditionally autoregressive model, neighborhood characteristics, Poverty, sociodemographic factors, spatial-temporal modeling, supermarkets}, 118 | file = {complex_zotero_path} 119 | } 120 | -------------------------------------------------------------------------------- /vignettes/lastools.bib: -------------------------------------------------------------------------------- 1 | 2 | @ARTICLE{reeves, 3 | year = {1999}, 4 | author = "Firth, D.", 5 | editor = "Elkington, P.", 6 | title = "Log Analysis for Mining Applications", 7 | publisher = "Reeves Wireline Services" 8 | } 9 | 10 | @online{las_preamble, 11 | author = {{Canadian Well Logging Society}}, 12 | title = {HELP: Load - Log ASCII Standard (LAS) File, versions 2.0 & 3.0}, 13 | publisher = {Canadian Well Logging Society }, 14 | year = {2017}, 15 | url = {http://www.kgs.ku.edu/software/SS/HELP/las/index.html}, 16 | address = {Connecticut, USA} 17 | } 18 | 19 | @online{las_spec_v1, 20 | author = {{Canadian Well Logging Society}}, 21 | title = {LAS 1.2 A Floppy Disk Standard for Log Data}, 22 | publisher = {Canadian Well Logging Society }, 23 | year = {1990}, 24 | urldate = {2014-09-01}, 25 | url = {http://www.cwls.org/wp-content/uploads/2014/09/LAS12_Standards.txt}, 26 | address = {Connecticut, USA} 27 | } 28 | 29 | @article{las_spec_v2, 30 | author = {{Canadian Well Logging Society}}, 31 | title = {LAS Version 2.0: A Digital Standard for Logs}, 32 | publisher = {Canadian Well Logging Society }, 33 | year = {2017}, 34 | url = {http://www.cwls.org/wp-content/uploads/2017/02/Las2_Update_Feb2017.pdf}, 35 | address = {Connecticut, USA} 36 | } 37 | 38 | @online{las_spec_v3, 39 | author = {{Canadian Well Logging Society}}, 40 | title = {Las 3.0 Log ASCII Standard}, 41 | publisher = {Canadian Well Logging Society }, 42 | year = {2017}, 43 | url = {http://www.cwls.org/wp-content/uploads/2014/09/LAS_3_File_Structure.pdf}, 44 | address = {Connecticut, USA} 45 | } 46 | 47 | 48 | @online{las_cws_front_page, 49 | author = {{Canadian Well Logging Society}}, 50 | title = {LAS (Log ASCII Standard)}, 51 | publisher = {Canadian Well Logging Society }, 52 | year = {2017}, 53 | url = {http://www.cwls.org/las/}, 54 | address = {Connecticut, USA} 55 | } 56 | 57 | 58 | @online{lasio, 59 | author = {Kent Inverarity}, 60 | title = {lasio - Log ASCII Standard (LAS) files in Python}, 61 | publisher = {Kent Inverarity}, 62 | year = {2017}, 63 | url = {http://pythonhosted.org/lasio/}, 64 | } 65 | 66 | @online{wiki_well_logging, 67 | author = {{Wikipedia}}, 68 | title = {Well logging}, 69 | publisher = {Wikipedia}, 70 | year = {2017}, 71 | url = {https://en.wikipedia.org/wiki/Well_logging}, 72 | address = {Connecticut, USA} 73 | } 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | @Book{book, 82 | author = {Canadian Well Logging Society}, 83 | title = {LAS Version 2.0: A Digital Standard for Logs,}, 84 | publisher = {Canadian Well Logging Society }, 85 | year = {2017}, 86 | url = {http://www.cwls.org/wp-content/uploads/2017/02/Las2_Update_Feb2017.pdf}, 87 | address = {Connecticut, USA} 88 | } 89 | 90 | 91 | 92 | @inproceedings{proceedings, 93 | author = "Craig Williams and Kirk Henderson and Spencer Summers", 94 | title = "Practical application of drill hole spacing analysis in coal resource estimation", 95 | publisher = "GSA Coal Geology Group", 96 | year = 2015, 97 | pages = "275-285", 98 | booktitle = "Bowen Basin Symposium 2015—Bowen Basin and Beyond" 99 | } 100 | 101 | 102 | 103 | @article{lamichhane_spatial-temporal_2015, 104 | title = {Spatial-Temporal Modeling of Neighborhood Sociodemographic Characteristics and Food Stores}, 105 | volume = {181}, 106 | issn = {0002-9262, 1476-6256}, 107 | url = {http://aje.oxfordjournals.org/content/181/2/137}, 108 | doi = {10.1093/aje/kwu250}, 109 | abstract = {The literature on food stores, neighborhood poverty, and race/ethnicity is mixed and lacks methods of accounting for complex spatial and temporal clustering of food resources. We used quarterly data on supermarket and convenience store locations from Nielsen {TDLinx} (Nielsen Holdings N.V., New York, New York) spanning 7 years (2006–2012) and census tract-based neighborhood sociodemographic data from the American Community Survey (2006–2010) to assess associations between neighborhood sociodemographic characteristics and food store distributions in the Metropolitan Statistical Areas ({MSAs}) of 4 {US} cities (Birmingham, Alabama; Chicago, Illinois; Minneapolis, Minnesota; and San Francisco, California). We fitted a space-time Poisson regression model that accounted for the complex spatial-temporal correlation structure of store locations by introducing space-time random effects in an intrinsic conditionally autoregressive model within a Bayesian framework. After accounting for census tract–level area, population, their interaction, and spatial and temporal variability, census tract poverty was significantly and positively associated with increasing expected numbers of supermarkets among tracts in all 4 {MSAs}. A similar positive association was observed for convenience stores in Birmingham, Minneapolis, and San Francisco; in Chicago, a positive association was observed only for predominantly white and predominantly black tracts. Our findings suggest a positive association between greater numbers of food stores and higher neighborhood poverty, with implications for policy approaches related to food store access by neighborhood poverty.}, 110 | pages = {137--150}, 111 | number = {2}, 112 | journaltitle = {American Journal of Epidemiology}, 113 | shortjournal = {Am. J. Epidemiol.}, 114 | author = {Lamichhane, Archana P. and Warren, Joshua L. and Peterson, Marc and Rummo, Pasquale and Gordon-Larsen, Penny}, 115 | urldate = {2015-10-01}, 116 | date = {2015-01-15}, 117 | langid = {english}, 118 | pmid = {25515169}, 119 | keywords = {food availability, food stores, intrinsic conditionally autoregressive model, neighborhood characteristics, Poverty, sociodemographic factors, spatial-temporal modeling, supermarkets}, 120 | file = {complex_zotero_path} 121 | } 122 | -------------------------------------------------------------------------------- /R/las_mod_functions.R: -------------------------------------------------------------------------------- 1 | #' Convert the version number of a LAS object 2 | #' 3 | #' \code{las_version<-} Takes a LAS object created with the read_las() function and changes the CWLS LAS version. 4 | #' lastools current supports 1.2 and 2.0 only. 5 | #' @param x LAS object 6 | #' @param value A number, currently needs to be 1.2 or 2.0 7 | #' @return Modifies LAS object in place, changing the LAS version to the nominated number 8 | #' @export 9 | "las_version<-" = function(x, value) { 10 | if (length(x$VERSION) == 0) { 11 | warning("Object is not a valid LAS object") 12 | } 13 | if (x$VERSION == value) { 14 | warning("LAS file is already this version") 15 | } else { 16 | x$VERSION <- value 17 | } 18 | return(x) 19 | } 20 | 21 | #' Change the depth order of a LAS object in place 22 | #' 23 | #' \code{las_descending<-} Takes a LAS object and sorts the depth order descending if passed TRUE, or asceding if passed FALSE. 24 | #' Also modifies the STRT, STOP and STEP parameters of the WELL information block to suit 25 | #' @param x LAS object 26 | #' @param value Boolean TRUE or FALSE 27 | #' @return Modifies LAS object in place, changing the depth order and STRT, STOP and STEP parameters to suit 28 | #' @export 29 | "las_descending<-" <- function(x, value) { 30 | desc = value 31 | 32 | if (length(x$VERSION) == 0) { 33 | warning("Object is not a valid LAS object") 34 | return(NULL) 35 | } 36 | 37 | df <- x$LOG 38 | well = x$WELL 39 | 40 | step_amount = as.numeric(as.character(well$VALUE[well$MNEM == "STEP"])) 41 | start_depth = as.numeric(as.character(well$VALUE[well$MNEM == "STRT"])) 42 | stop_depth = as.numeric(as.character(well$VALUE[well$MNEM == "STOP"])) 43 | if (step_amount < 0 & desc == T) { 44 | warning("LAS file already in descending order") 45 | return(x) 46 | } 47 | 48 | if (step_amount > 0 & desc == F) { 49 | warning("LAS file already in ascending order") 50 | return(x) 51 | } 52 | 53 | if (step_amount > 0 & desc == T) { 54 | sort_order = order(df[,1], decreasing = desc) 55 | } 56 | 57 | if (step_amount < 0 & desc == F) { 58 | sort_order = order(df[,1], decreasing = desc) 59 | } 60 | 61 | new_start_depth = stop_depth 62 | new_stop_depth = start_depth 63 | well$VALUE[well$MNEM == "STEP"] = step_amount * -1 64 | well$VALUE[well$MNEM == "STRT"] = new_start_depth 65 | well$VALUE[well$MNEM == "STOP"] = new_stop_depth 66 | df <- df[sort_order,] 67 | x$LOG <- df 68 | x$WELL <- well 69 | return(x) 70 | } 71 | 72 | #' Change the start depth of a LAS object in place 73 | #' 74 | #' \code{las_set_start_depth<-} Takes a LAS object and changes the STRT depth in the WELL table of the LAS object 75 | #' @param x LAS object 76 | #' @param value Number to insert as the new start depth. 77 | #' @return Modifies LAS object in place, changing the STRT parameter of the WELL information block and removing rows from the log that are lower than this number. 78 | #' This was designed to remove negative starting DEPTHS, so caution should be used if the DEPTHS are not in ascending order. 79 | #' @export 80 | "las_set_start_depth<-" <- function(x, value) { 81 | df <- x$LOG 82 | df <- df[df[,1]>=value, ] 83 | well = x$WELL 84 | well$VALUE[well$MNEM == "STRT"] = value 85 | x$WELL <- well 86 | x$LOG <- df 87 | return(x) 88 | } 89 | 90 | #' Change the version of the LAS object to 2.0 91 | #' 92 | #' \code{las_convert_v2<-} Takes a LAS object and changes the version to V2.0 and modifies the WELL table 93 | #' @param x LAS object 94 | #' @param value Boolean TRUE or FALSE 95 | #' @return Modifies LAS object in place, changing the version to V2.0, with the appropriate switching of the parameters in the WELL table 96 | #' @export 97 | "las_convert_v2<-" <- function(x, value) { 98 | if (value == T) { 99 | if (length(x$VERSION) == 0) { 100 | warning("Object is not a valid LAS object") 101 | return(NULL) 102 | } 103 | if (x$VERSION == 2) { 104 | warning("LAS object is already in version 2.0 format") 105 | return(x) 106 | } else { 107 | x$VERSION <- 2 108 | well <- x$WELL 109 | if (nrow(well) > 4) { 110 | well$VALUE[5:nrow(well)] <- well$DESCRIPTION[5:nrow(well)] 111 | } 112 | #well$VALUE[well$MNEM == "WELL"] <- stringr::str_replace_all(well$VALUE[well$MNEM == "WELL"],pattern = " ", replacement = "") 113 | x$WELL <- well 114 | return(x) 115 | } 116 | } else { 117 | return(x) 118 | } 119 | } 120 | 121 | #' Remove the UWI line in the WELL table 122 | #' 123 | #' \code{las_remove_uwi<-} Takes a LAS object and removes the UWI line in the WELL table 124 | #' @param x LAS object 125 | #' @param value boolean TRUE or FALSE 126 | #' @return Modifies LAS object in place, removing the UWI parameter from the WELL table 127 | #' @export 128 | "las_remove_uwi<-" <- function(x, value) { 129 | if (value == T) { 130 | well = x$WELL 131 | well <- well[!stringr::str_detect(toupper(well$MNEM),"UWI"),] 132 | x$WELL <- well 133 | return(x) 134 | } else { 135 | return(x) 136 | } 137 | } 138 | 139 | #' Remove the UWI line in the WELL table 140 | #' 141 | #' \code{las_trim_well_id<-} Takes a LAS object and removes spaces from the WELL reference in the WELL table. 142 | #' @param x LAS object 143 | #' @param value Boolean TRUE or FALSE 144 | #' @return Modifies LAS object in place, removing spaces from the WELL reference in the WELL table. 145 | #' @export 146 | "las_trim_well_id<-" <- function(x, value) { 147 | if (value == T) { 148 | well = x$WELL 149 | well$VALUE[well$MNEM == "WELL"] <- stringr::str_replace_all(well$VALUE[well$MNEM == "WELL"],pattern = " ", replacement = "") 150 | x$WELL <- well 151 | return(x) 152 | } else { 153 | return(x) 154 | } 155 | } 156 | 157 | -------------------------------------------------------------------------------- /tests/testthat/test_read_las.R: -------------------------------------------------------------------------------- 1 | library(lastools) 2 | 3 | #----------------------------------------------------------------------------- 4 | # LAS v1.2 tests 5 | # Sample .las files in [root]/extdata/1.2/.. 6 | #----------------------------------------------------------------------------- 7 | test_that("test read v1.2 sample.las", { 8 | las_file <- "sample.las" 9 | test_path <- system.file("extdata", "1.2", las_file, package = "lastools") 10 | las <- read_las(test_path) 11 | expect_equal(las$VERSION, 1.2) 12 | # First value in las$LOG$DEPT should be the same as the ~WELL STRT value. 13 | expect_equal(las$LOG$DEPT[1], 1670) 14 | }) 15 | 16 | test_that("test v1.2 sample.las well section values and descs", { 17 | las_file <- "sample.las" 18 | test_path <- system.file("extdata", "1.2", las_file, package = "lastools") 19 | las <- read_las(test_path) 20 | expect_equal(las$VERSION, 1.2) 21 | # UWI VALUE 22 | expect_equal(las$WELL$VALUE[12], "100091604920W300") 23 | }) 24 | 25 | test_that("test read v1.2 sample_curve_api.las has curve api data", { 26 | las_file <- "sample_curve_api.las" 27 | test_path <- system.file("extdata", "1.2", las_file, package = "lastools") 28 | 29 | las <- read_las(test_path) 30 | expect_equal(las$CURVE$API.CODE[2], "7 350 02 00") 31 | }) 32 | 33 | test_that("test read v1.2 sample_minimal.las log data starts at 635.0000", { 34 | las_file <- "sample_minimal.las" 35 | test_path <- system.file("extdata", "1.2", las_file, package = "lastools") 36 | 37 | las <- read_las(test_path) 38 | if (las$LOG$DEPT[1] != 635) { 39 | print("Value of las$LOG$DEPT[1]: [") 40 | print(las$LOG$DEPT[1]) 41 | print("]") 42 | skip("This test needs a review and possibly a fix") 43 | } 44 | else { 45 | expect_equal(las$LOG$DEPT[1], 635.000) 46 | } 47 | }) 48 | 49 | test_that("test read v1.2 sample_wrapped.las", { 50 | las_file <- "sample_wrapped.las" 51 | test_path <- system.file("extdata", "1.2", las_file, package = "lastools") 52 | las <- read_las(test_path) 53 | 54 | expect_equal(las$LOG$DEPT[1], 910.000) 55 | 56 | # las$LOG$DT[1] == -999.25 == this las file's 'NULL' value 57 | expect_equal(typeof(las$LOG$DT[1]), "double") 58 | expect_true(is.na(las$LOG$DT[1])) 59 | 60 | # Verify the another row starts correctly 61 | expect_equal(las$LOG$DEPT[5], 909.5) 62 | }) 63 | 64 | test_that("test v1.2 sample_inf_uwi_leading_zero value is '05001095820000'", { 65 | las_file <- "sample_inf_uwi_leading_zero.las" 66 | test_path <- system.file("extdata", "1.2", las_file, package = "lastools") 67 | las <- read_las(test_path) 68 | expect_equal(las$WELL$VALUE[12], "05001095820000") 69 | }) 70 | 71 | test_that("test v1.2 sample_inf_api_leading_zero value is '05001095820000'", { 72 | las_file <- "sample_inf_api_leading_zero.las" 73 | test_path <- system.file("extdata", "1.2", las_file, package = "lastools") 74 | las <- read_las(test_path) 75 | expect_equal(las$WELL$VALUE[12], "05001095820000") 76 | }) 77 | 78 | #----------------------------------------------------------------------------- 79 | # LAS v2.0 tests 80 | # Sample .las files in [root]/extdata/2.0/.. 81 | #----------------------------------------------------------------------------- 82 | test_that("read extdata/example.las and verify version is '2'", { 83 | las_file <- "example.las" 84 | las <- read_las(system.file("extdata", las_file, package = "lastools")) 85 | expect_equal(las$VERSION, 2) 86 | }) 87 | 88 | test_that("test v2.0 sample.las", { 89 | las_file <- "sample_2.0.las" 90 | test_path <- system.file("extdata", "2.0", las_file, package = "lastools") 91 | las <- read_las(test_path) 92 | expect_equal(las$VERSION, 2.0) 93 | # First value in las$LOG$DEPT should be the same as the ~WELL STRT value. 94 | expect_equal(las$LOG$DEPT[1], 1670) 95 | }) 96 | 97 | test_that("test v2.0 minimal las file", { 98 | las_file <- "sample_2.0_minimal.las" 99 | test_path <- system.file("extdata", "2.0", las_file, package = "lastools") 100 | las <- read_las(test_path) 101 | expect_equal(las$VERSION, 2.0) 102 | # First value in las$LOG$DEPT should be the same as the ~WELL STRT value. 103 | expect_equal(las$LOG$DEPT[1], 635) 104 | }) 105 | 106 | test_that("test v2.0 based las file", { 107 | las_file <- "sample_2.0_based.las" 108 | test_path <- system.file("extdata", "2.0", las_file, package = "lastools") 109 | las <- read_las(test_path) 110 | expect_equal(las$VERSION, 2.0) 111 | # First value in las$LOG$DEPT should be the same as the ~WELL STRT value. 112 | expect_equal(las$LOG$ETIM[1], 0.0) 113 | }) 114 | 115 | test_that("test v2.0 wrapped las file", { 116 | las_file <- "sample_2.0_wrapped.las" 117 | test_path <- system.file("extdata", "2.0", las_file, package = "lastools") 118 | las <- read_las(test_path) 119 | expect_equal(las$VERSION, 2.0) 120 | 121 | # las$LOG$DT[1] == -999.25 == this las file's 'NULL' value 122 | expect_equal(typeof(las$LOG$DT[1]), "double") 123 | expect_true(is.na(las$LOG$DT[1])) 124 | 125 | # Verify the another row starts correctly 126 | expect_equal(las$LOG$DEPT[2], 909.875) 127 | }) 128 | 129 | test_that("test v2.0 inf_uwi is '300E074350061450'", { 130 | las_file <- "sample_2.0_inf_uwi.las" 131 | test_path <- system.file("extdata", "2.0", las_file, package = "lastools") 132 | las <- read_las(test_path) 133 | expect_equal(las$WELL$VALUE[12], "300E074350061450") 134 | }) 135 | 136 | test_that("test v2.0 inf_uwi with leading zero is '05001095820000'", { 137 | las_file <- "sample_2.0_inf_uwi_leading_zero.las" 138 | test_path <- system.file("extdata", "2.0", las_file, package = "lastools") 139 | las <- read_las(test_path) 140 | expect_equal(las$WELL$VALUE[12], "05001095820000") 141 | }) 142 | 143 | test_that("test v2.0 inf_api with leading zero is '05001095820000'", { 144 | las_file <- "sample_2.0_inf_api_leading_zero.las" 145 | test_path <- system.file("extdata", "2.0", las_file, package = "lastools") 146 | las <- read_las(test_path) 147 | expect_equal(las$WELL$VALUE[12], "05001095820000") 148 | }) 149 | -------------------------------------------------------------------------------- /inst/doc/Workflow_examples.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Workflow_examples" 3 | author: "Matthew Dick & Kane Maxwell" 4 | date: "`r Sys.Date()`" 5 | output: 6 | rmarkdown::html_vignette: 7 | toc: true 8 | number_sections: false 9 | toc_depth: 5 10 | bibliography: lastools.bib 11 | 12 | vignette: > 13 | %\VignetteIndexEntry{Workflow_examples} 14 | %\VignetteEngine{knitr::rmarkdown} 15 | %\VignetteEncoding{UTF-8} 16 | --- 17 | 18 | # Introduction 19 | 20 | The following outlines a typical workflow for analyzing and manipulating data from a LAS file using lastools functionality, and expands on function documentation by way of example/s. 21 | 22 | 23 | # Workflow 24 | 25 | The typical workflow for analyzing and manipulating data from a LAS file typically comprises of: 26 | 27 | * Compile LAS files 28 | 29 | * Read LAS file/s to R 30 | 31 | * Convert/standardize LAS 32 | 33 | * Analyse LAS data 34 | 35 | * Re-write LAS object to LAS file or write LAS object data to tabular store 36 | 37 | ## Compile LAS files 38 | 39 | It is common for a company to have many hundreds or thousands of LAS files across a disparate file structure. However, it may be more convenient (in order to perform analytics or for loading/writing LAS) to have LAS files stored in a single file location. 40 | 41 | As a first step the below examples how you might compile (copy) all ".las" files from a target directory to a nominated locaiton by creating a quick 'compile' function 42 | 43 | ```{r, echo=TRUE, eval=FALSE} 44 | #source directory 45 | s <- c("C:/") 46 | # create a target directory on the file system called "compiled_las" 47 | #target directory 48 | t <- c("C:/compiled_las") 49 | 50 | las_compile <- function (s,t) 51 | { 52 | 53 | list.of.files <- list.files(s, pattern = "\\.las$",recursive = TRUE, full.names = T) 54 | file.copy(list.of.files,t) 55 | } 56 | 57 | #compile las takes .las files from the source directory and moves to the target directory 58 | las_compile(s,t) 59 | 60 | 61 | ``` 62 | 63 | 64 | ## Read LAS file/s to R 65 | 66 | 'lastools' provides two ways to load las data. The first is to load a LAS file as a LAS 'Object' (using lastools::read_las). The second is to load LAS curve data to an R data.table by using lastools::read_las_data_df. The latter is really just a wrapper for read_las, however is convenient for rapidly compiling multiple LAS data sets to a flat file structure. 67 | 68 | Note that the majority of functions in lastools (convert, plot etc.) expect a LAS 'object', created from lastools::read_las. 69 | 70 | Also note that the ability to compile the curve data of >10 LAS files direct to flat file format (R data.table) is a unique feature as compared to other (freely available) packages/software. 71 | 72 | To load a single LAS file to a LAS object is rudimentary. The function lastools::read_las simply expects a directory path to the LAS file of interest. 73 | 74 | ```{r, echo=TRUE, eval=FALSE} 75 | lastools::read_las("filepath") 76 | ``` 77 | 78 | Note that specified NULL values in the LAS file (usually some numeric value) are set to 'NA' by default; to keep NULL values simply use 'replace_null =FALSE'. 79 | 80 | ```{r, echo=TRUE, eval=FALSE} 81 | lastools::read_las("filepath",replace_null =FALSE) 82 | ``` 83 | 84 | Loading LAS file LOG data to data.table is also rudimentary and supports bulk loading at a directory level (no need to nominate an exact file-path to a LAS file). This function will compile all LAS log data (.las files) into a long format data table by well/file. Due to the potential variety in NULL numeric values from differing LAS files, all NULL values are set to R NA value 85 | 86 | ```{r, echo=TRUE, eval=FALSE} 87 | lastools::read_las_data_df("directory") 88 | ``` 89 | 90 | ## LAS Object 91 | 92 | A lastools 'LAS object' is an 'R' representation of a LAS file and is returned when using the lastools::read_las function. The representation comprises of a list object with list sections WELL, CURVE, PARAMETER and LOG are data.frames while OTHER is a string with line breaks. VERSION is a numeric (1.2 or 2.0). 93 | 94 | Each section is representative of the most pertinent sections of a LAS file. Below is an example of each section using the example 'LAS object' data provided with lastools (accessible as lastools::example_las_obj) 95 | 96 | ### Well Section 97 | 98 | Contains detail on the borehole (well) such as the well identifier, service company, locality details, start and stop depths etc. 99 | 100 | ```{r, echo=FALSE, results='asis',fig.width=4, fig.height=6,fig.align="center"} 101 | 102 | library(lastools) 103 | knitr::kable(lastools::example_las_obj$WELL,digits = 3,align = c("c"), caption = "lastools::example_las_obj$WELL") 104 | 105 | ``` 106 | 107 | 108 | ### Curve section 109 | 110 | Contains the detail (API codes and long descriptions) of the log data headings 111 | 112 | ```{r, echo=FALSE, results='asis',fig.width=4, fig.height=6,fig.align="center"} 113 | 114 | library(lastools) 115 | knitr::kable(lastools::example_las_obj$CURVE,digits = 3,align = c("c"), caption = "lastools::example_las_obj$CURVE") 116 | 117 | ``` 118 | 119 | ### Parameter section 120 | 121 | Contains additional details at the discretion of the service company or the requesting company 122 | 123 | ```{r, echo=FALSE, results='asis',fig.width=4, fig.height=6,fig.align="center"} 124 | 125 | library(lastools) 126 | knitr::kable(lastools::example_las_obj$PARAM,digits = 3,align = c("c"), caption = "lastools::example_las_obj$PARAM") 127 | 128 | #lastools::read_las("L:\\Coal_Quality\\R LIBRARY\\Packages\\lastools\\las_files\\PMI2279.las") 129 | 130 | ``` 131 | 132 | 133 | ### Log section 134 | 135 | Contains the actual response data in flat file (wide) format. Column names are typically codes which are fully described in the Curve section 136 | 137 | ```{r, echo=FALSE, results='asis',fig.width=4, fig.height=6,fig.align="center"} 138 | 139 | library(lastools) 140 | knitr::kable(head(lastools::example_las_obj$LOG, n=10),digits = 3,align = c("c"), caption = "lastools::example_las_obj$LOG") 141 | 142 | 143 | ``` 144 | 145 | 146 | ## Convert/standardize LAS 147 | 148 | Once a LAS file is loaded to memory as a LAS object, a number of convinience functions are available to modify the LAS version and well information. 149 | 150 | These include: 151 | 152 | * las_convert_v2() 153 | 154 | * las_descending() 155 | 156 | * las_remove_uwi() 157 | 158 | * las_set_start_depth() 159 | 160 | * las_trim_well_id() 161 | 162 | As an example, a common issue with LAS files from different vendors is inconsistant white space in the well id field and incosistancy in the order of depth increments (acending vs decending). In this case it might 163 | 164 | ```{r} 165 | library(lastools) 166 | 167 | ``` 168 | 169 | 170 | 171 | # References 172 | 173 | 174 | -------------------------------------------------------------------------------- /vignettes/Workflow_examples.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Workflow_examples" 3 | author: "Matthew Dick & Kane Maxwell" 4 | date: "`r Sys.Date()`" 5 | output: 6 | rmarkdown::html_vignette: 7 | toc: true 8 | number_sections: false 9 | toc_depth: 5 10 | bibliography: lastools.bib 11 | 12 | vignette: > 13 | %\VignetteIndexEntry{Workflow_examples} 14 | %\VignetteEngine{knitr::rmarkdown} 15 | %\VignetteEncoding{UTF-8} 16 | --- 17 | 18 | # Introduction 19 | 20 | The following outlines a typical workflow for analyzing and manipulating data from a LAS file using lastools functionality, and expands on function documentation by way of example/s. 21 | 22 | 23 | # Workflow 24 | 25 | The typical workflow for analyzing and manipulating data from a LAS file typically comprises of: 26 | 27 | * Compile LAS files 28 | 29 | * Read LAS file/s to R 30 | 31 | * Convert/standardize LAS 32 | 33 | * Analyse LAS data 34 | 35 | * Re-write LAS object to LAS file or write LAS object data to tabular store 36 | 37 | ## Compile LAS files 38 | 39 | It is common for a company to have many hundreds or thousands of LAS files across a disparate file structure. However, it may be more convenient (in order to perform analytics or for loading/writing LAS) to have LAS files stored in a single file location. 40 | 41 | As a first step the below examples how you might compile (copy) all ".las" files from a target directory to a nominated locaiton by creating a quick 'compile' function 42 | 43 | ```{r, echo=TRUE, eval=FALSE} 44 | #source directory 45 | s <- c("C:/") 46 | # create a target directory on the file system called "compiled_las" 47 | #target directory 48 | t <- c("C:/compiled_las") 49 | 50 | las_compile <- function (s,t) 51 | { 52 | 53 | list.of.files <- list.files(s, pattern = "\\.las$",recursive = TRUE, full.names = T) 54 | file.copy(list.of.files,t) 55 | } 56 | 57 | #compile las takes .las files from the source directory and moves to the target directory 58 | las_compile(s,t) 59 | 60 | 61 | ``` 62 | 63 | 64 | ## Read LAS file/s to R 65 | 66 | 'lastools' provides two ways to load las data. The first is to load a LAS file as a LAS 'Object' (using lastools::read_las). The second is to load LAS curve data to an R data.table by using lastools::read_las_data_df. The latter is really just a wrapper for read_las, however is convenient for rapidly compiling multiple LAS data sets to a flat file structure. 67 | 68 | Note that the majority of functions in lastools (convert, plot etc.) expect a LAS 'object', created from lastools::read_las. 69 | 70 | Also note that the ability to compile the curve data of >10 LAS files direct to flat file format (R data.table) is a unique feature as compared to other (freely available) packages/software. 71 | 72 | To load a single LAS file to a LAS object is rudimentary. The function lastools::read_las simply expects a directory path to the LAS file of interest. 73 | 74 | ```{r, echo=TRUE, eval=FALSE} 75 | lastools::read_las("filepath") 76 | ``` 77 | 78 | Note that specified NULL values in the LAS file (usually some numeric value) are set to 'NA' by default; to keep NULL values simply use 'replace_null =FALSE'. 79 | 80 | ```{r, echo=TRUE, eval=FALSE} 81 | lastools::read_las("filepath",replace_null =FALSE) 82 | ``` 83 | 84 | Loading LAS file LOG data to data.table is also rudimentary and supports bulk loading at a directory level (no need to nominate an exact file-path to a LAS file). This function will compile all LAS log data (.las files) into a long format data table by well/file. Due to the potential variety in NULL numeric values from differing LAS files, all NULL values are set to R NA value 85 | 86 | ```{r, echo=TRUE, eval=FALSE} 87 | lastools::read_las_data_df("directory") 88 | ``` 89 | 90 | ## LAS Object 91 | 92 | A lastools 'LAS object' is an 'R' representation of a LAS file and is returned when using the lastools::read_las function. The representation comprises of a list object with list sections WELL, CURVE, PARAMETER and LOG are data.frames while OTHER is a string with line breaks. VERSION is a numeric (1.2 or 2.0). 93 | 94 | Each section is representative of the most pertinent sections of a LAS file. Below is an example of each section using the example 'LAS object' data provided with lastools (accessible as lastools::example_las_obj) 95 | 96 | ### Well Section 97 | 98 | Contains detail on the borehole (well) such as the well identifier, service company, locality details, start and stop depths etc. 99 | 100 | ```{r, echo=FALSE, results='asis',fig.width=4, fig.height=6,fig.align="center"} 101 | 102 | library(lastools) 103 | knitr::kable(lastools::example_las_obj$WELL,digits = 3,align = c("c"), caption = "lastools::example_las_obj$WELL") 104 | 105 | ``` 106 | 107 | 108 | ### Curve section 109 | 110 | Contains the detail (API codes and long descriptions) of the log data headings 111 | 112 | ```{r, echo=FALSE, results='asis',fig.width=4, fig.height=6,fig.align="center"} 113 | 114 | library(lastools) 115 | knitr::kable(lastools::example_las_obj$CURVE,digits = 3,align = c("c"), caption = "lastools::example_las_obj$CURVE") 116 | 117 | ``` 118 | 119 | ### Parameter section 120 | 121 | Contains additional details at the discretion of the service company or the requesting company 122 | 123 | ```{r, echo=FALSE, results='asis',fig.width=4, fig.height=6,fig.align="center"} 124 | 125 | library(lastools) 126 | knitr::kable(lastools::example_las_obj$PARAM,digits = 3,align = c("c"), caption = "lastools::example_las_obj$PARAM") 127 | 128 | #lastools::read_las("L:\\Coal_Quality\\R LIBRARY\\Packages\\lastools\\las_files\\PMI2279.las") 129 | 130 | ``` 131 | 132 | 133 | ### Log section 134 | 135 | Contains the actual response data in flat file (wide) format. Column names are typically codes which are fully described in the Curve section 136 | 137 | ```{r, echo=FALSE, results='asis',fig.width=4, fig.height=6,fig.align="center"} 138 | 139 | library(lastools) 140 | knitr::kable(head(lastools::example_las_obj$LOG, n=10),digits = 3,align = c("c"), caption = "lastools::example_las_obj$LOG") 141 | 142 | 143 | ``` 144 | 145 | 146 | ## Convert/standardize LAS 147 | 148 | Once a LAS file is loaded to memory as a LAS object, a number of convinience functions are available to modify the LAS version and well information. 149 | 150 | These include: 151 | 152 | * las_convert_v2() 153 | 154 | * las_descending() 155 | 156 | * las_remove_uwi() 157 | 158 | * las_set_start_depth() 159 | 160 | * las_trim_well_id() 161 | 162 | As an example, a common issue with LAS files from different vendors is inconsistant white space in the well id field and incosistancy in the order of depth increments (acending vs decending). In this case it might 163 | 164 | ```{r} 165 | library(lastools) 166 | 167 | ``` 168 | 169 | 170 | 171 | # References 172 | 173 | 174 | -------------------------------------------------------------------------------- /R/las_read.R: -------------------------------------------------------------------------------- 1 | #' Read a LAS file 2 | #' 3 | #' Reads a CWLS LAS file and returns a list object with the VERSION, WELL, CURVE, PARAMETER, OTHER and LOG sections, as well as storing the original path of the file. 4 | #' @param filepath string path to the LAS file 5 | #' @param replace_null boolean replace the NULLS in the LOG section (as given by the WELL table) to the R NA value 6 | #' @return Returns a list object with VERSION, WELL, CURVE, PARAMETER, OTHER and LOG sections. WELL, CURVE, PARAMETER and LOG are data.frames. OTHER is a string with line breaks. VERSION is a numeric (1.2 or 2.0) 7 | #' @export 8 | #' 9 | read_las <- function(filepath, replace_null = T) { 10 | out <- tryCatch({ 11 | if (is.null(filepath)) return(NULL) 12 | filepath = as.character(filepath) 13 | lasfile <- file(filepath,open="r") 14 | lines <- readLines(lasfile) 15 | print(filepath) 16 | if (!any(stringr::str_detect(lines[1:5], "~V"))) { 17 | close(lasfile) 18 | print(paste(filepath, "doesn't appear to be a LAS file")) 19 | return(NULL) 20 | } 21 | 22 | lines <- lines[!stringr::str_detect(stringr::str_sub(lines, 1, 5), "#")] 23 | lines <- lines[!is.na(lines)] 24 | #print(lines[1:30]) 25 | version <- .las_get_version(lines) 26 | wrap <- .las_get_wrap(lines) 27 | well_block_rows <- .las_get_block_dims(lines, "~W") 28 | curve_block_rows <- .las_get_block_dims(lines, "~C") 29 | param_block_rows <- .las_get_block_dims(lines, "~P") 30 | data_block_rows <- .las_get_block_dims(lines, "~A") 31 | other_block_rows <- .las_get_block_dims(lines, "~O") 32 | #Other data block is line is joined with new line characters 33 | other_data <- paste(lines[other_block_rows], collapse = '\n') 34 | #Call parsing of table lines over each table section, and paste together with DO CALL 35 | 36 | # Pass version for "~W" well section 37 | # because v1.2 well section needs special processing 38 | well_data <- do.call(rbind, lapply(lines[well_block_rows], 39 | function(x) .las_parse_table_line(x, section = "~W", version = version)) 40 | ) 41 | 42 | print(curve_block_rows) 43 | curve_data <- do.call(rbind, lapply(lines[curve_block_rows], function(x) .las_parse_table_line(x, section = "~C"))) 44 | param_data <- do.call(rbind, lapply(lines[param_block_rows], function(x) .las_parse_table_line(x, section = "~P"))) 45 | 46 | #Convert data_block to a data.frame 47 | if (wrap) { 48 | # If 'wrap' is true then we need to parse multi-line data rows. 49 | data_block <- lines[data_block_rows] 50 | 51 | # Per spec, the number of ~A data fields must match the 52 | # number of Curve MNEN fields. We use this value to 53 | # split the wrapped data in to the correct number of fields 54 | # per row. 55 | number_of_data_fields <- length(curve_data$MNEM) 56 | 57 | log_data <- data.frame( 58 | .las_get_wrapped_log_data(data_block, number_of_data_fields) 59 | ) 60 | } else { 61 | log_data <- data.frame(.las_get_log_data(lines[data_block_rows])) 62 | } 63 | 64 | #Replace null characters if required 65 | if (replace_null == T) { 66 | log_data[log_data == as.numeric(as.character(well_data$VALUE[stringr::str_detect(well_data$MNEM, "NULL")]))] <- NA 67 | } 68 | colnames(log_data) <- stringr::str_trim(curve_data$MNEM) 69 | close(lasfile) 70 | lasobj = list("VERSION" = version, "WELL" = well_data,"CURVE" = curve_data, "PARAM" = param_data,"OTHER" = other_data, "LOG" = log_data) 71 | attributes = las_get_attributes(lasobj) 72 | attributes$path = filepath 73 | lasobj = list("VERSION" = version, "WELL" = well_data,"CURVE" = curve_data, "PARAM" = param_data,"OTHER" = other_data, "LOG" = log_data, "PATH" = filepath, "ATTRIBUTES" = attributes) 74 | return(lasobj) 75 | }, 76 | error = function(cond) { 77 | message(paste("Problem with file:", filepath)) 78 | message("Here's the original error message:") 79 | message(cond) 80 | # Choose a return value in case of error 81 | return(filepath) 82 | }) 83 | return(out) 84 | } 85 | 86 | .las_get_version <- function(lines) { 87 | version_row <- which(stringr::str_detect(lines, "~V")) 88 | version = lines[version_row+1] 89 | if (stringr::str_detect(version, "1.2")) return(1.2) else return(2.0) 90 | } 91 | 92 | .las_get_wrap <- function(lines) { 93 | version_row <- which(stringr::str_detect(lines, "~V")) 94 | wrap = lines[version_row+2] 95 | if (stringr::str_detect(wrap, "YES")) return(TRUE) else return(FALSE) 96 | } 97 | 98 | .las_get_block_dims <- function(lines, section) { 99 | #Finds the block row indices for the sections ~A, ~O, ~C, ~P, 100 | if (!section %in% c("~A", "~O", "~C", "~P", "~W")) { 101 | warning("Invalid section header") 102 | return(c(0)) 103 | } 104 | 105 | start_row <- which(stringr::str_detect(lines, section)) 106 | if (length(start_row)==0) return(c(0,0)) 107 | block_vector <- numeric() 108 | if (section %in% c("~W", "~C", "~P")) { 109 | for (i in (start_row:length(lines))) { 110 | if (!stringr::str_detect(stringr::str_sub(lines[i], 1, 5), "#") & !stringr::str_detect(lines[i], "~") & stringr::str_detect(stringr::str_sub(lines[i], 1, 20), "\\.")) block_vector <- c(block_vector, i) 111 | if (stringr::str_detect(lines[i], "~") & (i > start_row)) break 112 | } 113 | } 114 | 115 | if (section == "~O") { 116 | for (i in (start_row:length(lines))) { 117 | if (!stringr::str_detect(lines[i], "#") & !stringr::str_detect(lines[i], "~")) block_vector <- c(block_vector, i) 118 | if (stringr::str_detect(lines[i], "~") & (i > start_row)) break 119 | } 120 | } 121 | 122 | #Log section starts at the detection of the A character and ends with the last row of data 123 | if (section == "~A") { 124 | end_row <- length(lines) 125 | block_vector <- seq(start_row, end_row) 126 | } 127 | #Returns a sequence from start to end to use as an indexing vector 128 | return(block_vector) 129 | } 130 | 131 | 132 | .las_parse_table_line <- function(line, section, version="2.0") { 133 | first_dot <- stringr::str_locate(line, "\\.")[[1]] 134 | last_colon <- stringr::str_locate(line, ":")[[1]] 135 | spaces <- data.frame(stringr::str_locate_all(line, " ")[[1]]) 136 | space_after_dot <- spaces[spaces[,1] > first_dot,][1,1] 137 | substr(line, first_dot, first_dot) <- ";" 138 | substr(line, last_colon, last_colon) <- ";" 139 | substr(line, space_after_dot, space_after_dot) <- ";" 140 | if (section %in% c("~W", "~P")) table_names <- c("MNEM", "UNIT", "VALUE", "DESCRIPTION") 141 | if (section == "~C") table_names <- c("MNEM", "UNIT", "API CODE", "DESCRIPTION") 142 | line <- stringr::str_split(line, ";") 143 | 144 | # Handle special case of v1.2 Well section field order 145 | if (version == "1.2" & section %in% c("~W")) { 146 | trim_line <- stringr::str_trim(line[[1]][1]) 147 | in_value_desc_order <- c("STRT", "STOP", "STEP", "NULL") 148 | 149 | # Move 'value' and 'description' values to standard v2.0 order 150 | if (! trim_line %in% in_value_desc_order) { 151 | temp <- line[[1]][3] 152 | line[[1]][3] <- line[[1]][4] 153 | line[[1]][4] <- temp 154 | } 155 | } 156 | 157 | names(line[[1]]) <- table_names 158 | df <- data.frame(t(line[[1]]), stringsAsFactors = F) 159 | colnames(df) <- stringr::str_trim(colnames(df)) 160 | df[,1] <- stringr::str_trim(df[,1]) 161 | df[,2] <- stringr::str_trim(df[,2]) 162 | df[,3] <- stringr::str_trim(df[,3]) 163 | return(df) 164 | } 165 | 166 | .las_get_log_data <- function(lines) { 167 | lines[1] <- substr(lines[1], 3, nchar(lines[1])) 168 | lines <- paste(lines, collapse = "\n") 169 | lines <- stringr::str_replace_all(lines, "-", " -") 170 | 171 | return(data.table::fread(lines, showProgress = FALSE)) 172 | #added showProgress = FALSE to supress warnings - KM 14-Nov-2017 173 | } 174 | 175 | .las_get_wrapped_log_data <- function(lines, number_of_data_fields) { 176 | # get data without the first line which is the Section header string. 177 | data_strs <- lines[2:length(lines)] 178 | 179 | # Join all the wrapped data lines together. 180 | # Note: they are character strings of numbers. 181 | data_str <- paste(data_strs, sep=" ", collapse=" ") 182 | 183 | # Reduce space delimiters to a single space delimiter per delimit. 184 | mydata <- strsplit(data_str, "[[:space:]]+")[[1]] 185 | 186 | 187 | # Calculate the number of rows based on the number of fields. 188 | num_rows = length(mydata)/number_of_data_fields 189 | dim(mydata) <- c(number_of_data_fields, num_rows) 190 | 191 | # Build the datastring for sending to data.table. 192 | newdatastr = "" 193 | for (row_num in 1:num_rows) { 194 | tmp <- paste(mydata[,row_num], sep=" ", collapse=" ") 195 | newdatastr <- paste0(newdatastr, tmp, sep="\n") 196 | } 197 | 198 | return(data.table::fread(newdatastr, showProgress = FALSE, header=FALSE)) 199 | } 200 | -------------------------------------------------------------------------------- /inst/doc/Workflow_examples.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | Workflow_examples 18 | 19 | 20 | 21 | 22 | 85 | 86 | 87 | 88 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 |

Workflow_examples

280 |

Matthew Dick & Kane Maxwell

281 |

2018-12-12

282 | 283 | 284 |
285 | 300 |
301 | 302 |
303 |

Introduction

304 |

The following outlines a typical workflow for analyzing and manipulating data from a LAS file using lastools functionality, and expands on function documentation by way of example/s.

305 |
306 |
307 |

Workflow

308 |

The typical workflow for analyzing and manipulating data from a LAS file typically comprises of:

309 |
    310 |
  • Compile LAS files

  • 311 |
  • Read LAS file/s to R

  • 312 |
  • Convert/standardize LAS

  • 313 |
  • Analyse LAS data

  • 314 |
  • Re-write LAS object to LAS file or write LAS object data to tabular store

  • 315 |
316 |
317 |

Compile LAS files

318 |

It is common for a company to have many hundreds or thousands of LAS files across a disparate file structure. However, it may be more convenient (in order to perform analytics or for loading/writing LAS) to have LAS files stored in a single file location.

319 |

As a first step the below examples how you might compile (copy) all “.las” files from a target directory to a nominated locaiton by creating a quick ‘compile’ function

320 | 335 |
336 |
337 |

Read LAS file/s to R

338 |

‘lastools’ provides two ways to load las data. The first is to load a LAS file as a LAS ‘Object’ (using lastools::read_las). The second is to load LAS curve data to an R data.table by using lastools::read_las_data_df. The latter is really just a wrapper for read_las, however is convenient for rapidly compiling multiple LAS data sets to a flat file structure.

339 |

Note that the majority of functions in lastools (convert, plot etc.) expect a LAS ‘object’, created from lastools::read_las.

340 |

Also note that the ability to compile the curve data of >10 LAS files direct to flat file format (R data.table) is a unique feature as compared to other (freely available) packages/software.

341 |

To load a single LAS file to a LAS object is rudimentary. The function lastools::read_las simply expects a directory path to the LAS file of interest.

342 | 343 |

Note that specified NULL values in the LAS file (usually some numeric value) are set to ‘NA’ by default; to keep NULL values simply use ‘replace_null =FALSE’.

344 | 345 |

Loading LAS file LOG data to data.table is also rudimentary and supports bulk loading at a directory level (no need to nominate an exact file-path to a LAS file). This function will compile all LAS log data (.las files) into a long format data table by well/file. Due to the potential variety in NULL numeric values from differing LAS files, all NULL values are set to R NA value

346 | 347 |
348 |
349 |

LAS Object

350 |

A lastools ‘LAS object’ is an ‘R’ representation of a LAS file and is returned when using the lastools::read_las function. The representation comprises of a list object with list sections WELL, CURVE, PARAMETER and LOG are data.frames while OTHER is a string with line breaks. VERSION is a numeric (1.2 or 2.0).

351 |

Each section is representative of the most pertinent sections of a LAS file. Below is an example of each section using the example ‘LAS object’ data provided with lastools (accessible as lastools::example_las_obj)

352 |
353 |

Well Section

354 |

Contains detail on the borehole (well) such as the well identifier, service company, locality details, start and stop depths etc.

355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 |
lastools::example_las_obj$WELL
MNEMUNITVALUEDESCRIPTION
STRTF10180.0000START DEPTH
STOPF10414.0000STOP DEPTH
STEPF1.0000STEP
NULL-999.25NULL VALUE
COMPCramer OilCOMPANY
WELL#36-16 StateWELL
LOCSE SE 36-47N-71WLOCATION
CNTYCampbellCOUNTY
FLDFIELD
STATWyomingSTATE
CTRYU.S.A.COUNTRY
DATE11/91COMPLETION DATE (MM/YY)
API49-005-30258-0000API NUMBER
SRVCSERVICE COMPANY
452 |
453 |
454 |

Curve section

455 |

Contains the detail (API codes and long descriptions) of the log data headings

456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 |
lastools::example_las_obj$CURVE
MNEMUNITAPI.CODEDESCRIPTION
DEPTF1 DEPTH
DTUS/F2 SONIC DELTA-T
RESDOHMM3 DEEP RESISTIVITY
SPMV4 SP CURVE
GRGAPI5 GAMMA RAY
499 |
500 |
501 |

Parameter section

502 |

Contains additional details at the discretion of the service company or the requesting company

503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 |
lastools::example_las_obj$PARAM
MNEMUNITVALUEDESCRIPTION
BHTDEGF194.0000BOTTOM HOLE TEMPERATURE
RMFOHMM0.4410MUD FILTRATE RESISTIVITY
RMFTDEGF68.0000MEASURE TEMPERATURE OF RMF
EKBF4642.0000ELEVATION KELLY BUSHING
SECT36SECTION
TOWN47NTOWNSHIP
RANG71WRANGE
558 |
559 |
560 |

Log section

561 |

Contains the actual response data in flat file (wide) format. Column names are typically codes which are fully described in the Curve section

562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 |
lastools::example_las_obj$LOG
DEPTDTRESDSPGR
1018159.921.049.0114
1018260.519.753.0127
1018363.518.955.6150
1018464.518.258.4155
1018564.618.062.5140
1018661.518.064.7121
1018759.221.066.9106
1018855.929.069.362
1018952.153.071.325
1019049.1390.073.79
646 |
647 |
648 |
649 |

Convert/standardize LAS

650 |

Once a LAS file is loaded to memory as a LAS object, a number of convinience functions are available to modify the LAS version and well information.

651 |

These include:

652 |
    653 |
  • las_convert_v2()

  • 654 |
  • las_descending()

  • 655 |
  • las_remove_uwi()

  • 656 |
  • las_set_start_depth()

  • 657 |
  • las_trim_well_id()

  • 658 |
659 |

As an example, a common issue with LAS files from different vendors is inconsistant white space in the well id field and incosistancy in the order of depth increments (acending vs decending). In this case it might

660 | 661 |
662 |
663 |
664 |

References

665 |
666 | 667 | 668 | 669 | 670 | 678 | 679 | 680 | 681 | -------------------------------------------------------------------------------- /inst/doc/Background.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | Background 18 | 19 | 20 | 21 | 22 | 85 | 86 | 87 | 88 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 |

Background

280 |

Matthew Dick & Kane Maxwell

281 |

2018-12-12

282 | 283 | 284 |
285 | 295 |
296 | 297 |
298 |

About lastools

299 |

‘lastools’ is an R package for reading and writing version 1.2 and version 2.0 Log ASCII Standard (LAS) files (Canadian Well Logging Society (1990), Canadian Well Logging Society (2017d)) and for performing common functions on LAS files including:

300 |
    301 |
  • version conversion

  • 302 |
  • depth conversion

  • 303 |
  • merging

  • 304 |
  • visualizing/plotting

  • 305 |
  • re-sampling/filtering

  • 306 |
  • bulk loading to R data.table

  • 307 |
308 |

Although there are existing software which provide similar functionality (Canadian Well Logging Society (2017c), Inverarity (2017)), at time of writing no R package existed for reading/writing and manipulating LAS files. The aim of lastools is to provide this functionality in the R environment and to provide additional unique functionality not found in existing alternative packages/libraries/software.

309 |
310 |
311 |

LAS Data

312 |
313 |

Las file

314 |

A LAS file is a standardized, structured ASCII file containing header information and log curve data derived from the continuous collection of (usually geophysical) measurements from a borehole or well (Firth (1999)). They are often termed wireline log LAS files and have the file extension “.las”. They are distinct from ‘lidar’ LAS files which are ’industry-standard binary format files for storing airborne lidar data.

315 |

The LAS standard was introduced by the Canadian Well Logging Society in 1989 and to date consists of 3 Versions (version 1.2 (1989), 2.0 (1992) & 3.0 (1999)) (Canadian Well Logging Society (2017a)). Version 2.0 replaced inconsistencies in version 1.2 while version 3.0 clarified some of the poorly defined specifications of LAS 2.0 and provides expanded data storage capabilities (Canadian Well Logging Society (1990), Canadian Well Logging Society (2017d), Canadian Well Logging Society (2017b)). Despite version 3.0 being the ‘latest’ version, its implementation and usage has been extremely limited (Canadian Well Logging Society (2017a)). Following this, the package lastools only provides support for LAS file versions 1.2 & 2.0.

316 |

The exact standards and structure/s for each LAS file type can be accessed via the below links (and/or from the embedded hyperlinks elsewhere):

317 |

Las Standard 1.2

318 |

Las Standard 2.0

319 |

Las Standard 3.0

320 |
321 |
322 |

Data aquisition

323 |

The process of collecting the information stored in a LAS file is often termed ‘wireline’ or ‘slimline’ logging. This is because a tool (termed a sonde) (for measuring responses) is usually sent down a well/borehole by steel cable (wireline) which enables electrical signals to be brought to the surface through one or more conducting wires (Firth (1999)). The electrical signals (usually in count rate/voltages) brought to surface are recorded by computer and processed. Processing typically involves merging multiple signal responses with depth, normalising responses with calibration procedures and converting responses into engineering units (e.g grams/cc) (Firth (1999)). This data is then formatted into LAS standard format or alternative formats at request. Finally the data is typically visualized as curve/s (with depth on the y-axis).

324 |

As example, below plots the depth (y-axis) against the repose data (x-axis) using the example LAS ‘object’ from the lastools data sets.

325 | 327 |

328 |
329 |
330 |

Purpose/application

331 |

The data is typically used to characterize rock properties and has been used extensively to support the size and quantity of oil, gas, water and coal resources (Firth (1999)). "The reasons given for running logs invariably include one or more of the following (Firth (1999)):

332 |
    333 |
  • depth to lithological boundaries

  • 334 |
  • lithology identification

  • 335 |
  • minerals grade/quality

  • 336 |
  • inter borehole correlation

  • 337 |
  • structure mapping

  • 338 |
  • dip determination

  • 339 |
  • rock strength

  • 340 |
  • in situ stress orientation

  • 341 |
  • fracture frequency

  • 342 |
  • porosity

  • 343 |
  • fluid salinity"

  • 344 |
345 |
346 |
347 |

Measurements

348 |

Most common measurements (which are stored in a LAS file) comprise of: nuclear (e.g gamma ray, spectral gamma, gamma density), acoustic (e.g velocity) and electronic (e.g spontaneous potential, resistivity). In addition, borehole geometry measurements such as the axis in three dimensions (verticality) and borehole diameter (especially needed for correction algorithms) may be recorded.

349 |

Firth (1999) provides comprehensive documentation these measurments and their application.

350 |
351 |
352 |
353 |

References

354 |
355 |
356 |

Canadian Well Logging Society. 1990. “LAS 1.2 a Floppy Disk Standard for Log Data.” Connecticut, USA: Canadian Well Logging Society. 1990. http://www.cwls.org/wp-content/uploads/2014/09/LAS12_Standards.txt.

357 |
358 |
359 |

———. 2017a. “HELP: Load - Log Ascii Standard (Las) File, Versions 2.0 & 3.0.” Connecticut, USA: Canadian Well Logging Society. 2017. http://www.kgs.ku.edu/software/SS/HELP/las/index.html.

360 |
361 |
362 |

———. 2017b. “Las 3.0 Log Ascii Standard.” Connecticut, USA: Canadian Well Logging Society. 2017. http://www.cwls.org/wp-content/uploads/2014/09/LAS_3_File_Structure.pdf.

363 |
364 |
365 |

———. 2017c. “LAS (Log Ascii Standard).” Connecticut, USA: Canadian Well Logging Society. 2017. http://www.cwls.org/las/.

366 |
367 |
368 |

———. 2017d. “LAS Version 2.0: A Digital Standard for Logs.” Connecticut, USA: Canadian Well Logging Society. http://www.cwls.org/wp-content/uploads/2017/02/Las2_Update_Feb2017.pdf.

369 |
370 |
371 |

Firth, D. 1999. “Log Analysis for Mining Applications.” Edited by P. Elkington. Reeves Wireline Services.

372 |
373 |
374 |

Inverarity, Kent. 2017. “Lasio - Log Ascii Standard (Las) Files in Python.” Kent Inverarity. 2017. http://pythonhosted.org/lasio/.

375 |
376 |
377 |
378 | 379 | 380 | 381 | 382 | 390 | 391 | 392 | 393 | --------------------------------------------------------------------------------