|
| 1 | +# OBN Data Import |
| 2 | + |
| 3 | +This guide covers the `ObnReceiverGathers3D` template for importing Ocean Bottom Node (OBN) seismic data into MDIO. |
| 4 | + |
| 5 | +## Template Overview |
| 6 | + |
| 7 | +The `ObnReceiverGathers3D` template organizes data with the following dimensions: |
| 8 | + |
| 9 | +| Dimension | Description | |
| 10 | +| -------------- | ------------------------------------------------------------------------------------------ | |
| 11 | +| `component` | Sensor component (e.g., 1=X, 2=Y, 3=Z, 4=Hydrophone) | |
| 12 | +| `receiver` | Ocean bottom node receiver ID | |
| 13 | +| `shot_line` | Shot line identifier | |
| 14 | +| `gun` | Gun identifier for multi-gun sources | |
| 15 | +| `shot_index` | Calculated dense index for shots (see [Required Grid Overrides](#required-grid-overrides)) | |
| 16 | +| `time`/`depth` | Vertical sample axis | |
| 17 | + |
| 18 | +### Coordinates |
| 19 | + |
| 20 | +- **Logical coordinates**: `shot_point` (original values), `orig_field_record_num` |
| 21 | +- **Physical coordinates**: `group_coord_x`, `group_coord_y`, `source_coord_x`, `source_coord_y` |
| 22 | + |
| 23 | +```{note} |
| 24 | +The `shot_index` dimension is calculated (0 to N-1) from `shot_point` values during ingestion. Original `shot_point` values are preserved as a coordinate indexed by `(shot_line, gun, shot_index)`. |
| 25 | +``` |
| 26 | + |
| 27 | +## Required Grid Overrides |
| 28 | + |
| 29 | +### CalculateShotIndex (Required) |
| 30 | + |
| 31 | +The `CalculateShotIndex` grid override is **required** for the `ObnReceiverGathers3D` template. It calculates the `shot_index` dimension from `shot_point` values. Without this override, the import will fail with an error: |
| 32 | + |
| 33 | +> Required computed fields ['shot_index'] for template ObnReceiverGathers3D not found after grid overrides. |
| 34 | +
|
| 35 | +This override handles multi-gun acquisition where shot points are interleaved across guns, calculating a dense `shot_index` from sparse `shot_point` values: |
| 36 | + |
| 37 | +``` |
| 38 | +Before (interleaved shot_point): |
| 39 | + Gun 1: 1, 3, 5, 7, ... |
| 40 | + Gun 2: 2, 4, 6, 8, ... |
| 41 | +
|
| 42 | +After (dense shot_index): |
| 43 | + Gun 1: 0, 1, 2, 3, ... |
| 44 | + Gun 2: 0, 1, 2, 3, ... |
| 45 | +``` |
| 46 | + |
| 47 | +For `ObnReceiverGathers3D`, the override uses `shot_line` as the line field and requires `shot_line`, `gun`, and `shot_point` headers. |
| 48 | + |
| 49 | +## Special Behaviors |
| 50 | + |
| 51 | +### Component Synthesis |
| 52 | + |
| 53 | +When the SEG-Y spec does not include a `component` field, MDIO automatically synthesizes it with value `1` for all traces. This allows single-component data (e.g., hydrophone-only) to use the same template without modification. |
| 54 | + |
| 55 | +```{note} |
| 56 | +A warning is logged when component is synthesized: |
| 57 | +> SEG-Y headers do not contain 'component' field required by template 'ObnReceiverGathers3D'. |
| 58 | +> Synthesizing 'component' dimension with constant value 1 for all traces. |
| 59 | +``` |
| 60 | + |
| 61 | +## Usage |
| 62 | + |
| 63 | +### Basic Import |
| 64 | + |
| 65 | +```python |
| 66 | +from segy.schema import HeaderField |
| 67 | +from segy.standards import get_segy_standard |
| 68 | + |
| 69 | +from mdio import segy_to_mdio |
| 70 | +from mdio.builder.template_registry import get_template |
| 71 | + |
| 72 | +# Define SEG-Y header mapping |
| 73 | +obn_headers = [ |
| 74 | + HeaderField(name="orig_field_record_num", byte=9, format="int32"), |
| 75 | + HeaderField(name="receiver", byte=13, format="int32"), |
| 76 | + HeaderField(name="shot_point", byte=17, format="int32"), |
| 77 | + HeaderField(name="shot_line", byte=133, format="int16"), |
| 78 | + HeaderField(name="gun", byte=171, format="int16"), |
| 79 | + HeaderField(name="component", byte=189, format="int16"), |
| 80 | + HeaderField(name="coordinate_scalar", byte=71, format="int16"), |
| 81 | + HeaderField(name="source_coord_x", byte=73, format="int32"), |
| 82 | + HeaderField(name="source_coord_y", byte=77, format="int32"), |
| 83 | + HeaderField(name="group_coord_x", byte=81, format="int32"), |
| 84 | + HeaderField(name="group_coord_y", byte=85, format="int32"), |
| 85 | +] |
| 86 | + |
| 87 | +obn_spec = get_segy_standard(1.0).customize(trace_header_fields=obn_headers) |
| 88 | + |
| 89 | +segy_to_mdio( |
| 90 | + input_path="obn_data.sgy", |
| 91 | + output_path="obn_data.mdio", |
| 92 | + segy_spec=obn_spec, |
| 93 | + mdio_template=get_template("ObnReceiverGathers3D"), |
| 94 | + grid_overrides={"CalculateShotIndex": True}, |
| 95 | + overwrite=True, |
| 96 | +) |
| 97 | +``` |
| 98 | + |
| 99 | +### Single-Component Data |
| 100 | + |
| 101 | +For data without a `component` header field, simply omit it from the spec: |
| 102 | + |
| 103 | +```python |
| 104 | +# Same as above, but without the component field |
| 105 | +obn_headers = [ |
| 106 | + HeaderField(name="orig_field_record_num", byte=9, format="int32"), |
| 107 | + HeaderField(name="receiver", byte=13, format="int32"), |
| 108 | + HeaderField(name="shot_point", byte=17, format="int32"), |
| 109 | + HeaderField(name="shot_line", byte=133, format="int16"), |
| 110 | + HeaderField(name="gun", byte=171, format="int16"), |
| 111 | + # component omitted - will be synthesized |
| 112 | + HeaderField(name="coordinate_scalar", byte=71, format="int16"), |
| 113 | + HeaderField(name="source_coord_x", byte=73, format="int32"), |
| 114 | + HeaderField(name="source_coord_y", byte=77, format="int32"), |
| 115 | + HeaderField(name="group_coord_x", byte=81, format="int32"), |
| 116 | + HeaderField(name="group_coord_y", byte=85, format="int32"), |
| 117 | +] |
| 118 | +``` |
| 119 | + |
| 120 | +### Exploring the Data |
| 121 | + |
| 122 | +```python |
| 123 | +from mdio import open_mdio |
| 124 | + |
| 125 | +ds = open_mdio("obn_data.mdio") |
| 126 | + |
| 127 | +# View dimensions |
| 128 | +print(ds.sizes) |
| 129 | +# {'component': 4, 'receiver': 100, 'shot_line': 10, 'gun': 2, 'shot_index': 500, 'time': 2001} |
| 130 | + |
| 131 | +# Access original shot_point values (preserved as coordinate) |
| 132 | +print(ds["shot_point"].dims) # ('shot_line', 'gun', 'shot_index') |
| 133 | + |
| 134 | +# Select a receiver gather |
| 135 | +receiver_gather = ds.sel(receiver=150, component=4) |
| 136 | +receiver_gather["amplitude"].plot() |
| 137 | +``` |
| 138 | + |
| 139 | +## Required Header Fields |
| 140 | + |
| 141 | +| Field | Required | Notes | |
| 142 | +| ----------------------- | -------- | ----------------------------------- | |
| 143 | +| `receiver` | Yes | | |
| 144 | +| `shot_line` | Yes | | |
| 145 | +| `gun` | Yes | | |
| 146 | +| `shot_point` | Yes | | |
| 147 | +| `component` | No | Synthesized with value 1 if missing | |
| 148 | +| `coordinate_scalar` | Yes | | |
| 149 | +| `source_coord_x` | Yes | | |
| 150 | +| `source_coord_y` | Yes | | |
| 151 | +| `group_coord_x` | Yes | | |
| 152 | +| `group_coord_y` | Yes | | |
| 153 | +| `orig_field_record_num` | Yes | | |
| 154 | + |
| 155 | +## See Also |
| 156 | + |
| 157 | +- [Grid Overrides](grid_overrides.md) - All available grid overrides |
| 158 | +- [Template Registry](../template_registry.md) |
| 159 | +- [Quickstart Tutorial](../tutorials/quickstart.ipynb) |
0 commit comments