Writing POD5 Files
The Writer struct creates new POD5 files with automatic signal compression.
Creating a Writer
1
2
3
4
5
6
7
8
9
10
11
12 | use escapepod_signal::{Writer, WriterOptions};
// Default options
let writer = Writer::create("output.pod5", WriterOptions::default())?;
// Custom options
let options = WriterOptions {
signal_compression: true,
signal_chunk_size: 102_400,
..Default::default()
};
let writer = Writer::create("output.pod5", options)?;
|
Adding Run Info
Every read references a run info entry. Add run info before adding reads:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | use escapepod_signal::RunInfoData;
use std::collections::HashMap;
let run_info = RunInfoData {
acquisition_id: "abc123-def456".to_string(),
acquisition_start_time: 1705320600000, // milliseconds since epoch
adc_max: 2047,
adc_min: -2048,
sample_rate: 4000,
context_tags: HashMap::new(),
tracking_id: {
let mut m = HashMap::new();
m.insert("flow_cell_id".to_string(), "FAK12345".to_string());
m
},
..Default::default()
};
let run_info_index = writer.add_run_info(run_info)?;
|
Adding Reads
Add reads with their signal data:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 | use escapepod_signal::{ReadData, EndReason};
use uuid::Uuid;
let read = ReadData {
read_id: Uuid::new_v4(),
read_number: 1,
start_sample: 0,
channel: 1,
well: 1,
pore_type: "not_set".to_string(),
calibration_offset: -240.0,
calibration_scale: 0.145,
median_before: 210.5,
end_reason: EndReason::SignalPositive,
end_reason_forced: false,
run_info_index,
num_minknow_events: 100,
num_samples: 50000,
..Default::default()
};
// Signal as raw ADC values
let signal: Vec<i16> = vec![/* ... */];
writer.add_read(read, &signal)?;
|
Finishing the File
Always call finish() to write the footer and finalize the file:
The file is not valid until finish() completes successfully.
Complete Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39 | use escapepod_signal::{Writer, WriterOptions, ReadData, RunInfoData, EndReason};
use uuid::Uuid;
use std::collections::HashMap;
fn write_pod5_file() -> Result<(), escapepod_signal::Error> {
let mut writer = Writer::create("output.pod5", WriterOptions::default())?;
// Add run info
let run_info = RunInfoData {
acquisition_id: "run-001".to_string(),
sample_rate: 4000,
adc_max: 2047,
adc_min: -2048,
..Default::default()
};
let run_idx = writer.add_run_info(run_info)?;
// Add some reads
for i in 0..10 {
let read = ReadData {
read_id: Uuid::new_v4(),
read_number: i + 1,
channel: ((i % 512) + 1) as u16,
well: ((i % 4) + 1) as u8,
run_info_index: run_idx,
num_samples: 10000,
end_reason: EndReason::SignalPositive,
..Default::default()
};
// Generate dummy signal
let signal: Vec<i16> = (0..10000).map(|j| ((j % 500) as i16) - 250).collect();
writer.add_read(read, &signal)?;
}
writer.finish()?;
Ok(())
}
|
WriterOptions
| Option |
Default |
Description |
signal_compression |
true |
Enable VBZ compression for signal |
signal_chunk_size |
102400 |
Max samples per signal chunk |
Signal Compression
By default, signal data is compressed using the VBZ codec:
- Delta encoding - Store differences between consecutive samples
- Zigzag encoding - Map signed integers to unsigned
- SVB16 - Variable-length encoding (1-2 bytes per sample)
- ZSTD - Final compression pass
This typically achieves 60-80% compression on nanopore signal data.
Copying Reads Between Files
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | use escapepod_signal::{Reader, Writer, WriterOptions};
let reader = Reader::open("input.pod5")?;
let mut writer = Writer::create("output.pod5", WriterOptions::default())?;
// Copy run infos
for run_info in reader.run_infos() {
writer.add_run_info(run_info.clone())?;
}
// Copy specific reads
for read in reader.reads() {
if should_include(&read) {
let signal = reader.get_signal(&read)?;
writer.add_read(read, &signal)?;
}
}
writer.finish()?;
|