Class: HTS::Bcf

Inherits:
Hts
  • Object
show all
Includes:
Enumerable
Defined in:
lib/hts/bcf.rb,
lib/hts/bcf/info.rb,
lib/hts/bcf/errors.rb,
lib/hts/bcf/format.rb,
lib/hts/bcf/header.rb,
lib/hts/bcf/record.rb,
lib/hts/bcf/header_record.rb

Defined Under Namespace

Classes: Error, FieldError, Format, FormatDefinitionError, FormatError, FormatReadError, FormatTypeError, FormatUpdateError, Header, HeaderError, HeaderRecord, IndexError, Info, InfoError, InfoReadError, InfoTypeError, InfoUpdateError, InvalidBorrowedViewError, MissingIndexError, OpenError, QueryError, Record, SubsetError, UnknownSampleError, UnsupportedFormatOperationError, UnsupportedInfoOperationError

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(file_name, mode = "r", index: nil, threads: nil, build_index: false, subset: nil, samples: nil, unpack: :all) ⇒ Bcf

Returns a new instance of Bcf.



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/hts/bcf.rb', line 51

def initialize(file_name, mode = "r", index: nil, threads: nil, build_index: false,
               subset: nil, samples: nil, unpack: :all)
  raise "HTS::Bcf.new() does not take block; Please use HTS::Bcf.open() instead" if block_given?
  raise ArgumentError, "specify either samples: or subset:, not both" if samples && subset

  subset = samples unless samples.nil?
  @unpack = unpack.to_sym
  @max_unpack = resolve_max_unpack(@unpack)
  @file_name = file_name
  @index_name = index
  @mode = mode
  @nthreads = threads
  @index_load_attempted = false
  @native = Native::BcfFileHandle.open(@file_name, mode)
  set_threads(threads) if threads
  if subset && mode.start_with?("w")
    raise SubsetError,
          "Sample subsetting is only available when reading BCF/VCF files"
  end
  return if mode.start_with?("w")

  @read_header = Header.new(@native.read_header)
  @header = subset ? @read_header.subset(subset) : @read_header
  if build_index
    build_index(index)
    load_index(index)
  elsif index
    load_index(index)
  end
  @start_position = tell
rescue Errno::ENOENT
  raise OpenError, "Failed to open #{@file_name}"
end

Instance Attribute Details

#file_nameObject (readonly)

Returns the value of attribute file_name.



25
26
27
# File 'lib/hts/bcf.rb', line 25

def file_name
  @file_name
end

#headerObject

Returns the value of attribute header.



25
26
27
# File 'lib/hts/bcf.rb', line 25

def header
  @header
end

#index_nameObject (readonly)

Returns the value of attribute index_name.



25
26
27
# File 'lib/hts/bcf.rb', line 25

def index_name
  @index_name
end

#modeObject (readonly)

Returns the value of attribute mode.



25
26
27
# File 'lib/hts/bcf.rb', line 25

def mode
  @mode
end

#nthreadsObject (readonly)

Returns the value of attribute nthreads.



25
26
27
# File 'lib/hts/bcf.rb', line 25

def nthreads
  @nthreads
end

#unpackObject (readonly)

Returns the value of attribute unpack.



25
26
27
# File 'lib/hts/bcf.rb', line 25

def unpack
  @unpack
end

Class Method Details

.build_index(file_name, index_name = nil, min_shift = 14, threads = 0, verbose = true) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
# File 'lib/hts/bcf.rb', line 39

def self.build_index(file_name, index_name = nil, min_shift = 14, threads = 0, verbose = true)
  warn(index_name ? "Create index for #{file_name} to #{index_name}" : "Create index for #{file_name}") if verbose
  case Native::BcfFileHandle.build_index(file_name, index_name, min_shift, threads)
  when 0 then nil
  when -1 then raise IndexError, "Indexing failed for #{file_name}"
  when -2 then raise IndexError, "Opening #{file_name} failed while building the index"
  when -3 then raise IndexError, "#{file_name} is not in an indexable format"
  when -4 then raise IndexError, "Failed to create or save the index for #{file_name}"
  else raise IndexError, "Unknown index build error for #{file_name}"
  end
end

.filter_records(records, rid: nil, beg: nil, end_: nil, min_qual: nil, filter_id: nil) ⇒ Object



16
17
18
19
20
21
22
23
# File 'lib/hts/bcf.rb', line 16

def self.filter_records(records, rid: nil, beg: nil, end_: nil, min_qual: nil, filter_id: nil)
  Array(records).select do |record|
    (rid.nil? || record.rid == Integer(rid)) &&
      (beg.nil? || record.endpos > Integer(beg)) && (end_.nil? || record.pos < Integer(end_)) &&
      (min_qual.nil? || (!record.qual.nan? && record.qual >= Float(min_qual))) &&
      (filter_id.nil? || record.filter_id?(filter_id))
  end
end

.open(*args, **keywords) ⇒ Object



27
28
29
30
31
32
33
34
35
36
37
# File 'lib/hts/bcf.rb', line 27

def self.open(*args, **keywords)
  file = new(*args, **keywords)
  return file unless block_given?

  begin
    yield file
  ensure
    file.close
  end
  file
end

Instance Method Details

#build_index(index_name = nil, min_shift: 14, verbose: true) ⇒ Object



85
86
87
88
89
90
91
# File 'lib/hts/bcf.rb', line 85

def build_index(index_name = nil, min_shift: 14, verbose: true)
  check_closed
  self.class.build_index(@file_name, index_name, min_shift, @nthreads || 0, verbose)
  @index_name = index_name
  @index_load_attempted = false
  self
end

#closeObject



105
# File 'lib/hts/bcf.rb', line 105

def close = @native&.close

#closed?Boolean

Returns:

  • (Boolean)


106
# File 'lib/hts/bcf.rb', line 106

def closed? = @native.nil? || @native.closed?

#collect_recordsObject



193
# File 'lib/hts/bcf.rb', line 193

def collect_records = each(copy: true).to_a

#each(copy: false, &block) ⇒ Object



218
# File 'lib/hts/bcf.rb', line 218

def each(copy: false, &block) = copy ? each_record_copy(&block) : each_record_reuse(&block)

#each_format(key) ⇒ Object



211
212
213
214
215
216
# File 'lib/hts/bcf.rb', line 211

def each_format(key)
  return to_enum(__method__, key) unless block_given?

  each { |record| yield record.format(key) }
  self
end

#each_info(key) ⇒ Object



204
205
206
207
208
209
# File 'lib/hts/bcf.rb', line 204

def each_info(key)
  return to_enum(__method__, key) unless block_given?

  each { |record| yield record.info(key) }
  self
end

#file_formatObject



107
# File 'lib/hts/bcf.rb', line 107

def file_format = @native.file_format

#file_format_versionObject



108
# File 'lib/hts/bcf.rb', line 108

def file_format_version = @native.file_format_version

#format(key = nil) ⇒ Object Also known as: format_array

Raises:

  • (NotImplementedError)


184
185
186
187
188
189
190
# File 'lib/hts/bcf.rb', line 184

def format(key = nil)
  check_closed
  raise NotImplementedError unless key

  position = tell
  map { |record| record.format(key) }.tap { seek(position) if position }
end

#index_loaded?Boolean

Returns:

  • (Boolean)


100
101
102
103
# File 'lib/hts/bcf.rb', line 100

def index_loaded?
  check_closed
  @native.index_loaded?
end

#info(key = nil) ⇒ Object Also known as: info_array

Raises:

  • (NotImplementedError)


175
176
177
178
179
180
181
# File 'lib/hts/bcf.rb', line 175

def info(key = nil)
  check_closed
  raise NotImplementedError unless key

  position = tell
  map { |record| record.info(key) }.tap { seek(position) if position }
end

#load_index(index_name = nil) ⇒ Object



93
94
95
96
97
98
# File 'lib/hts/bcf.rb', line 93

def load_index(index_name = nil)
  check_closed
  @index_name = index_name
  @index_load_attempted = true
  @native.load_index(index_name)
end

#nsamplesObject



156
157
158
159
# File 'lib/hts/bcf.rb', line 156

def nsamples
  check_closed
  header.nsamples
end

#query(region, beg = nil, end_ = nil, copy: false, &block) ⇒ Object



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/hts/bcf.rb', line 220

def query(region, beg = nil, end_ = nil, copy: false, &block)
  check_closed
  unless ensure_index_loaded
    raise MissingIndexError, "Index file is required to call the query method for #{@file_name}"
  end

  case region
  when Array
    raise ArgumentError, "beg and end must not be specified when region is an Array" unless beg.nil? && end_.nil?
    return to_enum(__method__, region, copy:) unless block

    region.each { |item| query(item, copy:, &block) }
    self
  else
    if beg && end_
      iterate_query(@native.query_interval(read_header_native, header.name2id(region), beg, end_), copy, region,
                    &block)
    elsif beg.nil? && end_.nil?
      iterate_query(@native.query_region(read_header_native, region), copy, region, &block)
    else
      raise ArgumentError, "beg and end must be specified together"
    end
  end
end

#rewindObject



112
113
114
115
116
117
118
119
# File 'lib/hts/bcf.rb', line 112

def rewind
  raise "Cannot rewind: no start position" unless @start_position

  result = seek(@start_position)
  raise "Failed to rewind: #{result}" if result.negative?

  tell
end

#samplesObject



161
162
163
164
# File 'lib/hts/bcf.rb', line 161

def samples
  check_closed
  header.samples
end

#seek(offset) ⇒ Object



109
# File 'lib/hts/bcf.rb', line 109

def seek(offset) = @native.seek(offset)

#set_threads(count = nil) ⇒ Object

Raises:

  • (TypeError)


121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/hts/bcf.rb', line 121

def set_threads(count = nil)
  if count.nil?
    require "etc"
    count = [Etc.nprocessors - 1, 1].max
  end
  raise TypeError unless count.is_a?(Integer)
  raise ArgumentError, "Number of threads must be positive" if count < 1
  raise "Failed to set number of threads: #{count}" if @native.set_threads(count).negative?

  @nthreads = count
  self
end

#tellObject



110
# File 'lib/hts/bcf.rb', line 110

def tell = @native.tell

#write(record) ⇒ Object Also known as: <<



147
148
149
150
151
152
153
# File 'lib/hts/bcf.rb', line 147

def write(record)
  check_closed
  result = @native.write(header.__send__(:native_handle), record.__send__(:native_handle))
  raise "Failed to write record" if result.negative?

  result
end

#write_header(header) ⇒ Object

Raises:



134
135
136
137
138
139
140
141
# File 'lib/hts/bcf.rb', line 134

def write_header(header)
  check_closed
  @header = header.dup
  result = @native.write_header(header.__send__(:native_handle))
  raise HeaderError, "Failed to write BCF header" if result.negative?

  result
end