Surveys¶
Direct Current and Induced Polarization (DC/IP)¶
This object is meant to handle direct-current resistivity surveys. The survey object is made up of two curve entities defining the transmitter (current) and receiver (potential) electrodes. The following example shows how to generate a survey from scratch.
Current Electrode (sources)¶
The CurrentElectrode
entity defines the A-B dipole pairs used to inject current into the ground. It is a sub-class of the Curve
object, defined by vertices (poles) and cells (dipole segments). Here we generate four (4) parallel EW lines with eight dipoles per line.
[1]:
import numpy as np
import uuid
from geoh5py.workspace import Workspace
from geoh5py.objects import CurrentElectrode, PotentialElectrode
# Create a new project
workspace = Workspace("my_project.geoh5")
# Define the pole locations
n_poles = 9
n_lines = 2
x_loc, y_loc = np.meshgrid(np.linspace(0, 60, n_poles), np.linspace(-20, 20., n_lines))
vertices = np.c_[x_loc.ravel(), y_loc.ravel(), np.zeros_like(x_loc).ravel()]
# Assign a line ID to the poles (vertices)
parts = np.kron(np.arange(n_lines), np.ones(n_poles)).astype('int')
# Create the CurrentElectrode object
currents = CurrentElectrode.create(workspace, vertices=vertices, parts=parts)
At this stage the CurrentElectrode
object has segments (cells
) connecting all poles in series along line.
AB Cell ID¶
A key element of the DCIP survey objects is the ab_cell_id
property. This ReferenceData
contains the map referencing each cell of the CurrentElectrode
object to a unique A-B source identifier with name.
The utility function add_default_ab_cell_id
can help generate this map with a simple name string incrementor.
[2]:
currents.add_default_ab_cell_id()
print(currents.ab_cell_id.values)
print(currents.ab_cell_id.value_map.map)
[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16]
{0: 'Unknown', 1: '1', 2: '2', 3: '3', 4: '4', 5: '5', 6: '6', 7: '7', 8: '8', 9: '9', 10: '10', 11: '11', 12: '12', 13: '13', 14: '14', 15: '15', 16: '16'}
Users may wish to alter the value_map
names to carry forward specific survey information.
Note: The first entry {0:Unknown}
is a reserved field used by Geoscience ANALYST to flag unknown data entries.
In this specific case, every cell
on the curve corresponds to a unique dipole source current. For more complex survey configurations, users can edit the cell
property in order to define different combinations of connections between poles.
Potential Electrode (receivers)¶
The PotentialElectrode
object defines the M-N dipole pairs used to measure the electric potential (receivers). Just like the CurrentElectrode
, it is a sub-class of the Curve
object defined by vertices (poles) and cells (dipoles).
Although poles could be set independently on the CurrentElectrode
and PotentialElectrode
objects, here we re-uses the same locations for simplicity:
[3]:
potentials = PotentialElectrode.create(workspace, vertices=vertices)
The link between the sources CurrentElectrode
and the receivers PotentialElectrode
is established on creation with the current_electrodes
argument.
The same can also be done after instantiation as
[4]:
potentials.current_electrodes = currents
or equivalently
[5]:
currents.potential_electrodes = potentials
In all the above cases, the link between the two objects gets encoded in their respective metadata
.
[6]:
print(potentials.metadata == currents.metadata)
print(currents.metadata)
True
{'Current Electrodes': UUID('c29b42d7-afcd-4905-a758-72d124068d0d'), 'Potential Electrodes': UUID('5f060133-d569-4602-bb1d-6096f5bdad15')}
Next, we must define the receiver dipoles. The following routine generates a maximum of six (6) receivers dipoles per injection currents along line.
[7]:
N = 6
dipoles = []
current_id = []
for val in currents.ab_cell_id.values: # For each source dipole
cell_id = int(currents.ab_map[val]) - 1 # Python 0 indexing
line = currents.parts[currents.cells[cell_id, 0]]
for m_n in range(N):
dipole_ids = (currents.cells[cell_id, :] + 2 + m_n).astype("uint32") # Skip two poles
# Shorten the array as we get to the end of the line
if (
any(dipole_ids > (potentials.n_vertices - 1))
or any(currents.parts[dipole_ids] != line)
):
continue
dipoles += [dipole_ids] # Save the receiver id
current_id += [val] # Save the source id
potentials.cells = np.vstack(dipoles)
Finally, users need to create an association between each receiver dipole (M-N) to a dipole current (A-B). The mapping is done through the ab_cell_id
property of the PotentialElectrode
. An integer (ID) value must be assigned to each cell, corresponding to the AB Cell ID pairs stored on the associated CurrentElectrode
object.
[8]:
potentials.ab_cell_id = np.asarray(current_id, dtype="int32")
Note: The ab_cell_id
property of the CurrentElectrode
and PotentialElectrode
are two different ReferenceData
entities:
[9]:
print(potentials.ab_cell_id == currents.ab_cell_id)
False
but share the same DataType
that holds the map of unique source dipoles.
[10]:
print(potentials.ab_cell_id.entity_type == currents.ab_cell_id.entity_type)
True
This link between DataType
allows users to query the data by dipole sources and display the values as pseudo-section in Geoscience ANALYST.