LogIo.Net
by Petroware AS
LogIo.Net Documentation

The Petroware AS Log I/O module is a library for accessing (reading or writing) well log files such as DLIS, LAS2, LAS3, LIS, BIT, ASC, SPWLA or CSV.  Log I/O utilize high capacity memory mapped non-blocking I/O in order to get maximum performance for very large (GB) files.

Note that virtually all well logging data from the Norwegian continental shelf are QC'ed through the Log Studio™ application causing the library to get extremely well tested.

Log I/O is available for Java (logio.jar) and .Net (LogIo.dll). The libraries are self-contained, they have no external dependencies.

Programming examples

The programming examples below shows the .Net API using C#.


DLIS

Digital Log Interchange Standard (DLIS) is a digital well log format introduced as Recommended Practice 66 (RP 66) by the American Petroleum Institute in 1991. RP 66 exists in two versions, V1 and V2. V2 is not commonly used. DLIS is a binary (big-endian) data format.

DLIS is an old format in all possible ways. Available DLIS software is limited and to a large degree orphaned technology. The existence of programming tools for DLIS are absent. There is no easily accessible DLIS format description available, dialects exists, and DLIS is in general very hard to digest.

But still, DLIS is the main storage and communication medium for wellbore logging information. Since logging technology has evolved substantially since the introduction of the format, the amount of information contained may be vast; multi-GB volumes are commonplace.

Using Log I/O, reading and writing DLIS files becomes trivial. A coding example for reading a DLIS file is shown below. Note that a physical DLIS file consist of a file header and one or more logical DLIS files, each containing a number of sets (meta-data), one or more frames containing curve data, and possibly a set of encrypted records. A frame contains an index curve and a set of measurement curves. Each curve may be single- or multi-dimensional.

A complete example program for reading a DLIS file is shown below:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
namespace Test
{
// <summary>
// Class for testing the LogIo.Net DLIS read API.
// </summary>
public class DlisTest
{
public static void Main(string[] arguments)
{
FileInfo file = new FileInfo("path/to/file.DLIS");
//
// Write some meta information about the disk file
//
Console.WriteLine("File: " + file.FullName);
if (!file.Exists) {
Console.WriteLine("File not found.");
return;
}
Console.WriteLine("Size: " + file.Length);
//
// Read the DLIS file including bulk data. Report the performance.
//
DlisFileReader reader = new DlisFileReader(file);
Console.WriteLine();
Console.WriteLine("Reading...");
Stopwatch stopwatch = Stopwatch.StartNew();
IList<DlisFile> dlisFiles = reader.Read(true);
Console.WriteLine("Done in " + stopwatch.ElapsedMilliseconds + "ms.");
//
// Write the DLIS file header
//
Console.WriteLine();
Console.WriteLine("-- File header:");
Console.WriteLine(dlisFiles[0].GetHeader());
//
// Report number of subfiles. Loop over each of them.
//
Console.WriteLine();
Console.WriteLine("The file contains " + dlisFiles.Count + " subfile(s):");
foreach (DlisFile dlisFile in dlisFiles) {
//
// Report subfile ID
//
Console.WriteLine();
Console.WriteLine("File ID: " + dlisFile.GetFileId());
Console.WriteLine();
//
// Report the meta-data we can conveniently extract through DlisUtil
//
Console.WriteLine("-- Meta-data as reported by DlisUtil:");
Console.WriteLine("Well name.........: " + DlisUtil.GetWellName(dlisFile));
Console.WriteLine("Run number........: " + DlisUtil.GetRunNumber(dlisFile));
Console.WriteLine("Field name........: " + DlisUtil.GetFieldName(dlisFile));
Console.WriteLine("Producer name.....: " + DlisUtil.GetProducerName(dlisFile));
Console.WriteLine("Company...........: " + DlisUtil.GetCompany(dlisFile));
Console.WriteLine("Country...........: " + DlisUtil.GetCountry(dlisFile));
Console.WriteLine("Start date........: " + DlisUtil.GetStartDate(dlisFile));
Console.WriteLine("Bit size..........: " + DlisUtil.GetBitSize(dlisFile));
Console.WriteLine("MD................: " + DlisUtil.GetMd(dlisFile));
Console.WriteLine("Index type........: " + DlisUtil.GetIndexType(dlisFile));
Console.WriteLine("Header identifier.: " + DlisUtil.GetHeaderIdentifier(dlisFile));
double[] interval = DlisUtil.GetInterval(dlisFile);
Console.WriteLine("Interval..........: " + interval[0] + " - " + interval[1]);
//
// Report all the meta-data
//
Console.WriteLine();
Console.WriteLine("-- Meta-data details:");
Console.WriteLine();
Console.WriteLine("The subfile contains " + dlisFile.GetSets().Count + " set(s):");
Console.WriteLine();
foreach (DlisSet set in dlisFile.GetSets()) {
Console.WriteLine("Set type: " + set.GetSetType() + " (Name = " + set.GetName() + ")");
// This will write all the set content as a matrix
Console.WriteLine(set);
}
//
// Encrypted records
//
Console.WriteLine();
Console.WriteLine("-- The subfile contains " + dlisFile.GetEncryptedRecords().Count + " encrypted record(s).");
//
// Report number of frames in this sub file. Loop over each of them.
//
Console.WriteLine();
Console.WriteLine("-- Curve information:");
Console.WriteLine();
Console.WriteLine("The subfile contains " + dlisFile.GetFrames().Count + " frame(s):");
foreach (DlisFrame frame in dlisFile.GetFrames()) {
int nCurves = frame.GetCurves().Count;
int nValues = frame.GetCurves().Count > 0 ? frame.GetCurves()[0].GetNValues() : 0;
//
// Report frame ID and #curves and #values
//
Console.WriteLine();
Console.WriteLine("Frame " + frame.GetName() +
" (" + nCurves + " curves of " + nValues + " values each):");
Console.WriteLine();
//
// For each curve, report name, dimension, unit, type and description, ...
//
foreach (DlisCurve curve in frame.GetCurves()) {
Console.WriteLine(" " + curve.GetName() +
"[" + curve.GetNDimensions() + "], " +
curve.GetUnit() + ", " +
curve.GetValueType() +
"(" + curve.GetRepresentationCode() + "), " +
curve.GetDescription());
//
// ... and the first few values from the first couple of dimensions:
//
for (int dimension = 0; dimension < curve.GetNDimensions(); dimension++) {
Console.Write(" ");
for (int index = 0; index < curve.GetNValues(); index++) {
Console.Write(curve.GetValue(dimension, index) + "; ");
if (index == 2) break; // Write a few values only
}
Console.WriteLine("...");
if (dimension == 2) { // Write a few dimensions only
Console.WriteLine(" :");
break;
}
}
}
}
}
}
}
}


Creating DLIS files from scratch includes populating DlisFile instances with sets, frames and curves and writing it to a disk file using the DlisFileWriter class.

Note that the write API is by design low-level and there are many possible ways for a client to create an inconsistent DLIS file instance. The design is chosen for simplicity and flexibility. Each client is likely to implement one DLIS writer module anyway, and following the simple procedure below will ensure consistent DLIS instances:

// Create an empty DLIS file instance
DlisFile dlisFile = new DlisFile();
//
// Add the mandatory FILE-HEADER set
//
// Create the set
List<DlisComponent> fileHeaderAttributes = new List<DlisComponent>();
fileHeaderAttributes.Add(DlisComponent.NewAttributeComponent("SEQUENCE-NUMBER", DlisRepresentationCode.ASCII, null));
fileHeaderAttributes.Add(DlisComponent.NewAttributeComponent("ID", DlisRepresentationCode.ASCII, null));
DlisSet fileHeaderSet = new DlisSet("FILE-HEADER", null, fileHeaderAttributes);
// Create an object and add to the set
DlisComponent fileHeaderObject = DlisComponent.NewObjectComponent(0, "0");
List<DlisComponent> fileHeaderValues = new List<DlisComponent>();
fileHeaderValues.Add(DlisComponent.NewValueComponent("1", DlisRepresentationCode.ASCII, null)); // SEQUENCE-NUMBER
fileHeaderValues.Add(DlisComponent.NewValueComponent("logSetName", DlisRepresentationCode.ASCII, null)); // ID
fileHeaderSet.AddObject(fileHeaderObject, fileHeaderValues);
// Add set to file
dlisFile.AddSet(fileHeaderSet);
//
// Add the mandatory ORIGIN set
//
// Create set
List<DlisComponent> originAttributes = new List<DlisComponent>();
originAttributes.Add(DlisComponent.NewAttributeComponent("FILE-ID", DlisRepresentationCode.ASCII, null));
originAttributes.Add(DlisComponent.NewAttributeComponent("WELL-NAME", DlisRepresentationCode.ASCII, null));
originAttributes.Add(DlisComponent.NewAttributeComponent("FIELD-NAME", DlisRepresentationCode.ASCII, null));
originAttributes.Add(DlisComponent.NewAttributeComponent("COMPANY", DlisRepresentationCode.ASCII, null));
DlisSet originSet = new DlisSet("ORIGIN", null, originAttributes);
// Create an object and add to set
DlisComponent originObject = DlisComponent.NewObjectComponent(0, "DLIS_DEFINING_ORIGIN");
List<DlisComponent> originValues = new List<DlisComponent>();
originValues.Add(DlisComponent.NewValueComponent("fileId", DlisRepresentationCode.ASCII, null)); // FILE-ID
originValues.Add(DlisComponent.NewValueComponent("25/1-A-4", DlisRepresentationCode.ASCII, null)); // WELL-NAME
originValues.Add(DlisComponent.NewValueComponent("Frigg", DlisRepresentationCode.ASCII, null)); // FIELD-NAME
originValues.Add(DlisComponent.NewValueComponent("Mobil", DlisRepresentationCode.ASCII, null)); // COMPANY
originSet.AddObject(originObject, originValues);
// Add set to file
dlisFile.AddSet(originSet);
//
// Add the mandatory CHANNEL set describing all curves
//
// Create set
List<DlisComponent> channelAttributes = new List<DlisComponent>();
channelAttributes.Add(DlisComponent.NewAttributeComponent("LONG-NAME", DlisRepresentationCode.ASCII, null));
channelAttributes.Add(DlisComponent.NewAttributeComponent("PROPERTIES", DlisRepresentationCode.IDENT, null));
channelAttributes.Add(DlisComponent.NewAttributeComponent("REPRESENTATION-CODE", DlisRepresentationCode.USHORT, null));
channelAttributes.Add(DlisComponent.NewAttributeComponent("UNITS", DlisRepresentationCode.UNITS, null));
channelAttributes.Add(DlisComponent.NewAttributeComponent("DIMENSION", DlisRepresentationCode.UVARI, null));
DlisSet channelSet = new DlisSet("CHANNEL", null, channelAttributes);
// Create objects for each curve
DlisComponent channelObject1 = DlisComponent.NewObjectComponent(0, "MD");
List<DlisComponent> channelValues1 = new List<DlisComponent>();
channelValues1.Add(DlisComponent.NewValueComponent("Measured depth", DlisRepresentationCode.ASCII, null)); // LONG-NAME
channelValues1.Add(DlisComponent.NewValueComponent("BASIC", DlisRepresentationCode.IDENT, null)); // PROPERTIES
channelValues1.Add(DlisComponent.NewValueComponent(DlisRepresentationCode.FDOUBL, DlisRepresentationCode.USHORT, null)); // REPRESENTATION-CODE
channelValues1.Add(DlisComponent.NewValueComponent("m", DlisRepresentationCode.UNITS, null)); // UNITS
channelValues1.Add(DlisComponent.NewValueComponent(1, DlisRepresentationCode.UVARI, null)); // DIMENSION
channelSet.AddObject(channelObject1, channelValues1);
DlisComponent channelObject2 = DlisComponent.NewObjectComponent(0, "GR");
List<DlisComponent> channelValues2 = new List<DlisComponent>();
channelValues2.Add(DlisComponent.NewValueComponent("Gamma ray", DlisRepresentationCode.ASCII, null)); // LONG-NAME
channelValues2.Add(DlisComponent.NewValueComponent("BASIC", DlisRepresentationCode.IDENT, null)); // PROPERTIES
channelValues2.Add(DlisComponent.NewValueComponent(DlisRepresentationCode.FDOUBL, DlisRepresentationCode.USHORT, null)); // REPRESENTATION-CODE
channelValues2.Add(DlisComponent.NewValueComponent("gAPI", DlisRepresentationCode.UNITS, null)); // UNITS
channelValues2.Add(DlisComponent.NewValueComponent(1, DlisRepresentationCode.UVARI, null)); // DIMENSION
channelSet.AddObject(channelObject2, channelValues2);
// Add set to file
dlisFile.AddSet(channelSet);
//
// Add any other sets containing meta-data
//
:
:
//
// Add the mandatory FRAME set describing all the containing frames
// and what curves they contains
//
// Create set
List<DlisComponent> frameAttributes = new List<DlisComponent>();
frameAttributes.Add(DlisComponent.NewAttributeComponent("DESCRIPTION", DlisRepresentationCode.ASCII, null));
frameAttributes.Add(DlisComponent.NewAttributeComponent("CHANNELS", DlisRepresentationCode.OBNAME, null));
frameAttributes.Add(DlisComponent.NewAttributeComponent("INDEX-TYPE", DlisRepresentationCode.IDENT, null));
frameAttributes.Add(DlisComponent.NewAttributeComponent("DIRECTION", DlisRepresentationCode.IDENT, null));
frameAttributes.Add(DlisComponent.NewAttributeComponent("SPACING", DlisRepresentationCode.FDOUBL, "m"));
frameAttributes.Add(DlisComponent.NewAttributeComponent("INDEX-MIN", DlisRepresentationCode.FDOUBL, "m"));
frameAttributes.Add(DlisComponent.NewAttributeComponent("INDEX-MAX", DlisRepresentationCode.FDOUBL, "m"));
DlisSet frameSet = new DlisSet("FRAME", null, frameAttributes);
// Create a frame object
DlisComponent frameObject = DlisComponent.NewObjectComponent(0, "s10");
List<DlisComponent> frameValues = new List<DlisComponent>();
frameValues.Add(DlisComponent.NewValueComponent("", DlisRepresentationCode.ASCII, null)); // DESCRIPTION
frameValues.Add(DlisComponent.NewValueComponent(new List<object>() {"MD", "GR"}, DlisRepresentationCode.OBNAME, null)); // CHANNELS
frameValues.Add(DlisComponent.NewValueComponent("BOREHOLE-DEPTH", DlisRepresentationCode.IDENT, null)); // INDEX-TYPE
frameValues.Add(DlisComponent.NewValueComponent("INCREASING", DlisRepresentationCode.IDENT, null)); // DIRECTION
frameValues.Add(DlisComponent.NewValueComponent(0.5, DlisRepresentationCode.FDOUBL, "m")); // SPACING
frameValues.Add(DlisComponent.NewValueComponent(1450.0, DlisRepresentationCode.FDOUBL, "m")); // INDEX-MIN
frameValues.Add(DlisComponent.NewValueComponent(1451.0, DlisRepresentationCode.FDOUBL, "m")); // INDEX-MAX
frameSet.AddObject(frameObject, frameValues);
// Add set to file
dlisFile.AddSet(frameSet);
//
// Create one frame per frame objects in the FRAME set and populate with curves and data.
// The curves must already have been defined in the CHANNEL set above.
//
DlisFrame frame = new DlisFrame(frameSet, frameObject);
DlisCurve curve1 = new DlisCurve("MD", "m", "Measured depth", DlisRepresentationCode.FDOUBL, 1);
curve1.AddValue(1450.0);
curve1.AddValue(1450.5);
curve1.AddValue(1451.0);
frame.AddCurve(curve1);
DlisCurve curve2 = new DlisCurve("GR", "API", "Gamma ray", DlisRepresentationCode.FDOUBL, 1);
curve2.AddValue(46.89);
curve2.AddValue(30.65);
curve2.AddValue(43.02);
frame.AddCurve(curve2);
// Add frame to file
dlisFile.AddFrame(frame);
//
// Finally write the DLIS file instance to disk
DlisFileWriter fileWriter = new DlisFileWriter(new FileInfo("/path/to/file.DLIS");
fileWriter.Write(dlisFile);


LIS

Log Interchange Standard (LIS) is the predecessor of DLIS and was developed by Schlumberger in the late 1970s.

Like DLIS, LIS is a binary (big-endian) format. It it is based on the VAX binary information standard and has an even more awkward syntax than DLIS.

But still, LIS files are still being produced and immense volumes of historical logging information exists in this format. Log I/O is a convenient platform for being able to manage and maintain this information.

A physical LIS file consists of one or more logical LIS files, each containing a set of records (meta-data) of different types as well as an index curve and a set of measurement curves. Each curve may have one or several samples per depth measure, and in addition the curves may be single- or multi-dimensional.

A complete example program for reading a LIS file is shown below:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
namespace Test
{
// <summary>
// Class for testing the LogIo.Net LIS read API.
// </summary>
public class LisTest
{
public static void Main(string[] arguments)
{
FileInfo file = new FileInfo("path/to/file.LIS");
//
// Write some meta information about the disk file
//
Console.WriteLine("File: " + file.FullName);
if (!file.Exists) {
Console.WriteLine("File not found.");
return;
}
Console.WriteLine("Size: " + file.Length);
//
// Read the LIS file including bulk data. Report the performance.
//
LisFileReader reader = new LisFileReader(file);
Console.WriteLine();
Console.WriteLine("Reading...");
Stopwatch stopwatch = Stopwatch.StartNew();
IList<LisFile> lisFiles = reader.Read(true);
Console.WriteLine("Done in " + stopwatch.ElapsedMilliseconds + "ms.");
// Loop over all logical files
foreach (LisFile lisFile in lisFiles) {
//
// Report subfile name
//
Console.WriteLine();
Console.WriteLine("File name: " + lisFile.GetName());
Console.WriteLine();
//
// Report the meta-data we can conveniently extract through LisUtil
//
Console.WriteLine("-- Meta-data as reported by LisUtil:");
Console.WriteLine("Well name........: " + LisUtil.GetWellName(lisFile));
Console.WriteLine("Field name.......: " + LisUtil.GetFieldName(lisFile));
Console.WriteLine("Run number.......: " + LisUtil.GetRunNumber(lisFile));
Console.WriteLine("Company..........: " + LisUtil.GetCompany(lisFile));
Console.WriteLine("Service company..: " + LisUtil.GetServiceCompany(lisFile));
Console.WriteLine("Country..........: " + LisUtil.GetCountry(lisFile));
Console.WriteLine("Rig name.........: " + LisUtil.GetRigName(lisFile));
Console.WriteLine("Date.............: " + LisUtil.GetDate(lisFile));
Console.WriteLine("Bit size.........: " + LisUtil.GetBitSize(lisFile));
//
// Report all the meta-data
//
Console.WriteLine();
Console.WriteLine("-- Meta-data details:");
Console.WriteLine();
Console.WriteLine("The subfile contains " + lisFile.GetRecords().Count + " record(s):");
Console.WriteLine();
foreach (LisRecord in lisFile.GetRecord())
Console.WriteLine(record);
//
// Report #curves and #values
//
int nCurves = lisFile.GetCurves().Count;
int nValues = nCurves > 0 ? lisFile.GetCurves()[0].GetNValues() : 0;
Console.WriteLine();
Console.WriteLine("The file contains " + nCurves + " curves of " + nValues + " values each.");
Console.WriteLine();
//
// For each curve, report name, dimension, unit, type and description, ...
//
foreach (LisCurve curve in lisFile.GetCurves()) {
Console.WriteLine(" " + curve.GetName() +
"[" + curve.GetNDimensions() + "], " +
curve.GetUnit() + ", " +
curve.GetValueType() +
"(" + curve.GetRepresentationCode() + "), " +
curve.GetDescription());
//
// ... and the first few values from the first couple of dimensions:
//
for (int sample = 0; sample < curve.GetNSamples(), sample++) {
for (int dimension = 0; dimension < curve.GetNDimensions(); dimension++) {
Console.Write(" ");
for (int index = 0; index < curve.GetNValues(); index++) {
Console.Write(curve.GetValue(sample, dimension, index) + "; ");
if (index == 10) break; // Write a few values only
}
Console.WriteLine("...");
if (dimension == 10) { // Write a few dimensions only
Console.WriteLine(" :");
break;
}
}
}
}
}
}
}
}


Creating LIS files from scratch includes populating LisFile instances with records and curves and writing it to a disk file using the LisFileWriter class.

Note that the write API is by design low-level and there are many possible ways for a client to create an inconsistent LIS file instance. The design is chosen for simplicity and flexibility. Each client is likely to implement one LIS writer module anyway, and following the simple procedure below will ensure consistent LIS instances:

//
// Create an empty LisFile instance
//
LisFile lisFile = new LisFile();
//
// Add a reel header record
//
LisRecord reelHeaderRecord = new LisReelHeaderRecord("Serv", new DateTime(), "Orig", "Reel", 1, null, "Created by <name>");
lisFile.AddRecord(reelHeaderRecord);
//
// Add a tape header record
//
LisRecord tapeHeaderRecord = new LisTapeHeaderRecord("Serv", new DateTime(), "Orig", "Reel", 1, null, "Created by <name>");
lisFile.AddRecord(tapeHeaderRecord);
//
// Add a file header record
//
LisRecord fileHeaderRecord = new LisFileHeaderRecord("<name>", "ServID", "<version>", new DateTime(), 99999, "LI", null);
lisFile.AddRecord(fileHeaderRecord);
//
// Add a well site data record
//
LisWellSiteDataRecord wellSiteDataRecord = new LisWellSiteDataRecord("Well site data", new List<string>() {"Name", "Value", "Unit", "Description"});
wellSiteDataRecord.AddRow(new List<string>() {"WELL", "25/1-A-4", null, "Well name"});
wellSiteDataRecord.AddRow(new List<string>() {"FIELD", "Frigg", null, "Field name"});
wellSiteDataRecord.AddRow(new List<string>() {"MD", "2450.5", "m", "Measured depth"});
:
//
// Add the mandatory data format specification
//
TODO
//
// Add curves
//
TODO


LAS

The Log ASCII Standard (LAS) was created by the Canadian Well Logging Society (CWLS) in the late 1980s.

LAS popularity is due to its simple syntax and the fact that it contains human readable ASCII text. Consequently it is easier to work with than the black-box DLIS and LIS files. The ASCII format has a price however; It requires a lot more storage space than equivalent DLIS files, so LAS is not useful for very large log volumes. Also, the easily accessible text format combined with an ambiguous format description has caused many LAS dialects and semantic interpretations over the years.

Three different versions of LAS exists, 1.2, 2.0 and 3.0. Current version is 3.0, published in 2000, but version 2.0 is by far the most common. Log I/O supports all LAS versions, as well as converting between them. Note that LAS does not support multi-frame nor multi-dimensional curves like DLIS. LAS 3.0 supports multiple subfiles per physical disk file.

A complete example program for reading a LAS file is shown below:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
namespace Test
{
// <summary>
// Class for testing the LogIo.Net LAS read API.
// </summary>
public class LasTest
{
public static void Main(string[] arguments)
{
FileInfo file = new FileInfo("path/to/file.LAS");
//
// Write some meta information about the disk file
//
Console.WriteLine("File: " + file.FullName);
if (!file.Exists) {
Console.WriteLine("File not found.");
return;
}
Console.WriteLine("Size: " + file.Length);
//
// Read the LAS file including bulk data. Report the performance.
//
LasFileReader reader = new LasFileReader(file);
Console.WriteLine();
Console.WriteLine("Reading...");
Stopwatch stopwatch = Stopwatch.StartNew();
IList<LasFile> lasFiles = reader.Read(true);
double time = stopwatch.ElapsedMilliseconds;
double speed = Math.Round(file.Length / time / 1000.0);
Console.WriteLine("Done in " + time + "ms. (" + speed + "MB/s)");
//
// Report number of subfiles. Loop over each of them.
//
Console.WriteLine();
Console.WriteLine("The file contains " + lasFiles.Count + " subfile(s):");
foreach (LasFile lasFile in lasFiles) {
//
// Report subfile name
//
Console.WriteLine();
Console.WriteLine("File name: " + lasFile.GetName());
Console.WriteLine();
//
// Report the meta-data we can conveniently extract through LasUtil
//
Console.WriteLine("-- Meta-data as reported by LasUtil:");
Console.WriteLine("Well name.........: " + LasUtil.GetWellName(lasFile));
Console.WriteLine("Field name........: " + LasUtil.GetFieldName(lasFile));
Console.WriteLine("Rig name..........: " + LasUtil.GetRigName(lasFile));
Console.WriteLine("Company...........: " + LasUtil.GetCompany(lasFile));
Console.WriteLine("Service company...: " + LasUtil.GetServiceCompany(lasFile));
Console.WriteLine("Country...........: " + LasUtil.GetCountry(lasFile));
Console.WriteLine("Run number........: " + LasUtil.GetRunNumber(lasFile));
Console.WriteLine("Bit size..........: " + LasUtil.GetBitSize(lasFile));
Console.WriteLine("Date..............: " + LasUtil.GetDate(lasFile));
Console.WriteLine("MD................: " + LasUtil.GetMd(lasFile));
double?[] interval = LasUtil.GetInterval(lasFile);
if (interval != null)
Console.WriteLine("Interval..........: " + interval[0] + " - " + interval[1]);
// Loop over all curves
foreach (LasCurve curve in lasFile.GetCurves()) {
Console.WriteLine(" " + curve.GetName() +
curve.GetUnit() + ", " +
curve.GetValueType() +
curve.GetDescription());
//
// ... and the first few values
//
for (int index = 0; index < curve.GetNValues(); index++) {
Console.Write(curve.GetValue(index) + "; ");
if (index == 10) { // Write a few values only
Console.WriteLine("...");
break;
}
}
}
}
}
}
}


Creating LAS files from scratch includes populating LasFile instances with sections and curves.

The mandatory ~VERSION section will be auto-populated. The mandatory ~WELL section must be created by the client, but parameters associated with the curve data (STRT, STOP, STEP and NULL) will be auto-populated if  the section is present.

The optional parameter sections must be provided by the client.

The definition section(s) (~CURVE) will be auto-populated based on provided curves.

A complete example program for writing a LAS file is shown below:

using System;
namespace Test
{
// <summary>
// Class for testing the LogIo.Net LAS write API.
// </summary>
public class LasTest
{
public static void Main(string[] arguments)
{
//
// Create LasFile instance
//
LasFile lasFile = new LasFile("WLC_COMPOSITE", LasVersion.VERSION_2_0);
//
// Create the mandatory WELL section
//
LasSection wellSection = new LasSection("Well");
wellSection.AddRecord(new LasParameterRecord("WELL", null, "16/2-16", "Well name"));
wellSection.AddRecord(new LasParameterRecord("CTRY", null, "Norway", "Country"));
wellSection.AddRecord(new LasParameterRecord("COMP", null, "Lundin", "Company"));
wellSection.AddRecord(new LasParameterRecord("FLD", null, "Johan Sverdrup", "Field name"));
wellSection.AddRecord(new LasParameterRecord("SRVC", null, "Schlumberger", "Service company"));
lasFile.AddSection(wellSection);
//
// Create an optional parameter section
//
LasSection parameterSection = new LasSection("Parameters");
parameterSection.AddRecord(new LasParameterRecord("Rshl", "OHMM", "2.0000", "Resistivity shale"));
parameterSection.AddRecord(new LasParameterRecord("TLI", null, "149.5000", "Top Logged Interval"));
parameterSection.AddRecord(new LasParameterRecord("VshCutoff", "V/V", "0.2500", "Shale Volume Cutoff"));
lasFile.AddSection(parameterSection);
//
// Create and populate curves
//
LasCurve depthCurve = new LasCurve("DEPT", "m", "Measured depth", typeof(double));
depthCurve.AddValue(4350.00);
depthCurve.AddValue(4350.50);
depthCurve.AddValue(4351.00);
depthCurve.AddValue(4351.50);
depthCurve.AddValue(4352.00);
lasFile.AddCurve(depthCurve);
LasCurve gammaRayCurve = new LasCurve("GR", "gAPI", "Gamma ray", typeof(double));
gammaRayCurve.AddValue(12.3);
gammaRayCurve.AddValue(8.43);
gammaRayCurve.AddValue(null); // No-value
gammaRayCurve.AddValue(4.1);
gammaRayCurve.AddValue(7.29);
lasFile.AddCurve(gammaRayCurve);
//
// Write instance to disk
//
LasFileWriter.Write(new FileInfo("/path/to/file.LAS"), lasFile);
}
}
}

The program above will produce the following LAS file:

~Version
VERS. 2.0 : CWLS LAS ASCII Standard - Version 2.0
WRAP. NO : One line per depth step
~Well
WELL. 16/2-16 : Well name
CTRY. Norway : Country
COMP. Lundin : Company
FLD . Johan Sverdrup : Field name
SRVC. Schlumberger : Service company
STRT.m 4350.0 : Start depth
STOP.m 4352.0 : Stop depth
STEP.m 0.5 : Step
NULL. -999.25 : No-value
~Parameters
Rshl .OHMM 2.0000 : Resistivity shale
TLI . 149.5000 : Top Logged Interval
VshCutoff.V/V 0.2500 : Shale Volume Cutoff
~Curves
DEPT.m : Measured depth
GR .gAPI : Gamma ray
~Ascii
4350.0 12.30000
4350.5 8.43000
4351.0 -999.25
4351.5 4.10000
4352.0 7.29000


BIT

The Basic Information Tape (BIT) is a binary well log format created by Dresser Atlas in in the 1970's. Each physical BIT disk file can contain one or more logical BIT files. Each logical file is composed by a simple General Heading record followed by a number of Data records holding data for a maximum of 20 curves (not including the index channel of depth or time). All curve data is 4-byte floating point in the IBM System/360 representation.

The BIT format is no longer in common use, but vast amounts of historic logging data still exists in this format. The format is quite simple, but no public description of it is available.

A complete example program for reading a BIT file is shown below:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
namespace Test
{
// <summary>
// Class for testing the LogIo.Net BIT read API.
// </summary>
public class BitTest
{
public static void Main(string[] arguments)
{
FileInfo file = new FileInfo("path/to/file.BIT");
//
// Write some meta information about the disk file
//
Console.WriteLine("File: " + file.FullName);
if (!file.Exists) {
Console.WriteLine("File not found.");
return;
}
Console.WriteLine("Size: " + file.Length);
//
// Read the BIT file including bulk data. Report the performance.
//
BitFileReader reader = new BitFileReader(file);
Console.WriteLine();
Console.WriteLine("Reading...");
Stopwatch stopwatch = Stopwatch.StartNew();
IList<BitFile> bitFiles = reader.Read(true);
Console.WriteLine("Done in " + stopwatch.ElapsedMilliseconds + "ms.");
//
// Report number of subfiles. Loop over each of them.
//
Console.WriteLine();
Console.WriteLine("The file contains " + bitFiles.Count + " subfile(s):");
foreach (BitFile bitFile in bitFiles) {
//
// Report subfile name
//
Console.WriteLine();
Console.WriteLine("File name: " + bitFile.GetName());
Console.WriteLine();
//
// Get file header containing logging meta-data
//
LisGeneralHeadingRecord header = bitFile.GetHeader();
Console.WriteLine("Well name.....: " + header.GetWellName());
Console.WriteLine("Company.......: " + header.GetCompanyName());
Console.WriteLine("Time..........: " + header.GetTime();
//
// Loop over all curves
//
foreach (BitCurve curve in bitFile.GetCurves()) {
Console.WriteLine(" " + curve.GetName() + " " +
curve.GetQuantity() +
curve.GetUnit() + " " +
curve.GetValueType() + " " +
curve.GetDescription());
//
// ... and list the first few values
//
Console.Write(" ");
for (int index = 0; index < curve.GetNValues(); index++) {
Console.Write(curve.GetValue(index) + "; ");
if (index == 10) {
break;
Console.WriteLine("...");
}
}
}
}
}
}
}


Creating BIT files from scratch includes populating BitFile instances with a proper header and associated curve data.

A complete example program for writing a BIT file is shown below:

using System;
namespace Test
{
// <summary>
// Class for testing the LogIo.Net BIT write API.
// </summary>
public class BitTest
{
public static void Main(string[] arguments)
{
//
// Create BIT file with appropriate header
//
BitGeneralHeadingRecord header = new BitGeneralHeadingRecord(0.0,
"Halliburton",
"34/2-16B T2",
DateTime.Now,
new List<string>() {"GR", "NEU"},
startDepth,
4350.0,
4352.0,
0.5,
"",
"1");
header.setDepthUnit("m");
BitFile bitFile = new BitFile(header);
//
// Curve data
//
BitCurve gammaRayCurve = bitFile.GetCurves()[1];
gammaRayCurve.AddValue(12.3);
gammaRayCurve.AddValue(8.43);
gammaRayCurve.AddValue(null); // No-value
gammaRayCurve.AddValue(4.1);
gammaRayCurve.AddValue(7.29);
BitCurve neutronCurve = bitFile.GetCurves()[2];
neutronCurve.AddValue(0.89);
neutronCurve.AddValue(1.13);
neutronCurve.AddValue(2.32);
neutronCurve.AddValue(0.71);
neutronCurve.AddValue(0.99);
//
// Write instance to disk
//
BitFileWriter writer = new BitFileWriter(new FileInfo("/path/to/file.BIT"));
writer.Write(bitFile);
}
}
}