# Storing borehole interval data (logs) in netcdf

I have a number of datasets that describe geological properties as intervals down a borehole (e.g. unit codes, sample results)

In a database, or a flat ASCII file, I would describe these with a from and a to column, and then have a column for each observation type.

What (if any) is the guidance for storing this kind of data that represents an interval within a netCDF. I've checked the CF Conventions, notably the section on discrete sampling geometries but haven't been able to find anything.

The more I think about it, intuitively, it seems like the wrong file format but maybe I'm wrong.

• For clarification: I mean variable XY has a value of 5.2 from 0m to 10m, a value of 4.9 from 10m to 20m, a value of ... from 20m to 30m and so on? Is it one borehole per file or several borehols per file? Do you need different time steps? Mar 26, 2020 at 8:15
• Your clarification is correct. With the added complication that there may be some intervals with no sampling information (although this could just have a nodata value). I would be open to one or multiple boreholes per file, but am primarily looking for existing best practices, so in a way, it would depend on that. Time steps are not essential but may come in handy. Mar 26, 2020 at 14:15
• The featureType profile seems to be reasonable for your case. To indicate the boundaries, you insert a variable z_bnds that contains the boundaries of the cells. I will post an answer later on or tomorrow. Mar 26, 2020 at 19:10

# tl;dr

I would suggest considering the borehole measurements as a vertical profile. In terms of the CF Convention it would be a featureType profile, which is described in the discrete sampling geometries section.

I didn't include all reference to the relevant sections in the CF Conventions below. Just comment if import things are missing.

# Example netCDF

netcdf borehole_01 {
dimensions:
z = 8 ;
profile = 1 ;
nv = 2 ;

variables:
int profile(profile) ;
profile:cf_role = "profile_id";
double time(profile);
time:standard_name = "time";
time:long_name = "time" ;
time:units = "days since 1970-01-01 00:00:00" ;
float lon(profile);
lon:standard_name = "longitude";
lon:long_name = "longitude" ;
lon:units = "degrees_east" ;
float lat(profile);
lat:standard_name = "latitude";
lat:long_name = "latitude" ;
lat:units = "degrees_north" ;
float z(z) ;
z:standard_name = "depth";
z:long_name = "depth below ..." ;
z:units = "m" ;
z:positive = "down" ;
z:axis = "Z" ;
z:bounds = "z_bnds" ;
float z_bnds(z, nv) ;
float temp(profile, z) ;
temp:standard_name = "soil_temperature" ;
temp:long_name = "soil_temperature" ;
temp:units = "K" ;
temp:coordinates = "time lon lat z" ;
temp:cell_methods = "z: mean" ;

// global attributes:
:featureType = "profile";

data:

z = 5, 15, 25, 45, 55, 70, 90, 150 ;

z_bnds =
0, 10,
10, 20,
20, 30,
40, 50,
50, 60,
60, 80,
80, 100,
100, 200 ;

temp = 279.4, 278.8, 278.3, 277.0, 277.1, 276.9, 277.0, 279.2 ;
}


# Explanation

The variable temp is an example for a data variable in the file.

## Basic depth information

Please see CF Conventions section 7.1 Cell Boundaries for details.

z_bnds provides the boundaries of intervals over which the temperature is averaged. The attribute bounds of variable z points to these boundary data. In this example, I wrote the average depth of each interval into the z variable. However, this is no requirement (as far as I know). It might be also the upper or lower depth of each interval or another depth in between. z_bnds automatically inherits all relevant attributs from z. Therefore, it has no own units and standard_name attributes.

As you can see in the data for z_bnds (or in the table below), it is possible to skip depth ranges (between depth invertal 3 and 4) and to have differently spaced intervals (e.g. interval 6 compared to previous intervals).

| **depth interval** | **minimum** | **maximum** |
|--------------------|-------------|-------------|
| 1                  | 0           | 10          |
| 2                  | 10          | 20          |
| 3                  | 20          | *30*        |
| 4                  | *40*        | 50          |
| 5                  | 50          | 60          |
| 6                  | *60*        | *80*        |
| 7                  | 80          | 100         |
| 8                  | 100         | 200         |


## Connecting data variables proberly to the vertical coordinate

Please see CF Conventions section 7.3 Cell Methods for details on the first paragraph and section 5.2 Two-Dimensional Latitude, Longitude, Coordinate Variables for details on the second paragraph.

The value z: mean of the attribute cell_methods of variable temp tells us that the temperature is an average value in z-direction over the interval provided in z_bnds. The latter (over the interval provided in z_bnds) is implicitely assumed.

The value time lon lat z of the coordinates attribute of variable temp indicates that the variables time, lon, lat and z provided temporal and spatial dimensions to each value of temp. Really proper software should be able to map each value of temp a the correspondinc time, lat and lon values via the dimension profile. However, implementing this properly is hard work. Therefore, most software package won't recognize it or might even have issues with it. I your software package throws errors, one first step should be to remove the attribute coordinates.

## Setting global attributes

Please see CF Conventions section Features and feature types for details

The global attribute featureType is mandatory when we have a discret sampling geometry.

## Several boreholes with different depth intervals

For details on coordinate variables and auxiliary coordinate variables please have a look into sections 1.2 Terminology and 5 Coordinate System

Via the dimension profile we could save several boreholes in one file. In the current situation, the samplings depths of each borehole would be the same. One could make the variable z and z_bnds dependent on profile to provide depth intervals for each borehole. However, then we needed to rename the variables z and z_bnds because a variable might only have the same name of a dimension, if the variable is onedimensional and only depends on this dimension (a coordiante variable). When a variable like the depth depends on two dimensions, it becomes and auxiliary coordinate variable and has to have a different names. Thus, we would have double depth(profile, z) instead of z(z) and double depth_bnds(profile, z, nv) instead of z_bnds(z, nv). The temp:coordiantes would have a value of time lat lon depth (because depth is the vertical coordinates) but temp:cell_methods would remain z: mean (because we calculate average in z-direction).

• Great answer. I wonder if there is a way to take advantage of the standard_name: thickness_of_soil_layer to make it more interoperable by using the conventions. The one example I was able to find (also in a soils context) did this: www2.nrel.colostate.edu/projects/irc/public/Documents/Software/…. However, I like that the bounds are explicitly stated the way you have it so they don't need to be recomputed. Mar 27, 2020 at 19:13
• @alex_danielssen Thanks for the feedback. thickness_of_soil_layer doesn't seem to be in the official standard name list but was used as standard_name in some data sets like this one. To be CF-compliant only standard names of the official standard name list are allowed to be used. One can use the long_name attribute to set a name, that might be a standard name but isn't. Mar 28, 2020 at 19:52
• New standard names can be proposed at the CF Conventions discussion GitHub repository. Mar 28, 2020 at 19:53
• Based on my personal feeling and on this dataset I think that thickness_of_soil_layer means something else than the thickness of several vertical layers in the ground. soil_layer means the layer where plants can grow -- where we actually have soil as described in its Wikipedia article. thickness_of_soil_layer is the thinkness of the layer in which plants etc. can grow/live. This variable should have no vertical dimension. Mar 28, 2020 at 19:59
• You are correct about thickness_of_soil_layer not being in the CF standard names, but I disagree about the usage of soil_layer in CF. Consider that the standard name frozen_water_content_of_soil_layer defines "Layer" as any layer with upper and lower boundaries that have constant values in some vertical coordinate. Also that Quantities defined for a soil layer must have a vertical coordinate variable. My interpretation of that dataset is that the layer dimension refers to the vertical index of vertically-stacked soil layers. Aug 11, 2020 at 21:04