in progress... multi-domain extractor started
[jalview.git] / forester / ruby / evoruby / lib / evo / io / parser / hmmscan_domain_extractor.rb
1 #
2 # = lib/evo/io/parser/hmmscan_domain_extractor.rb - HmmscanDomainExtractor class
3 #
4 # Copyright::    Copyright (C) 2017 Christian M. Zmasek
5 # License::      GNU Lesser General Public License (LGPL)
6 #
7 # Last modified: 2017/02/20
8
9 require 'lib/evo/util/constants'
10 require 'lib/evo/msa/msa_factory'
11 require 'lib/evo/io/msa_io'
12 require 'lib/evo/io/writer/fasta_writer'
13 require 'lib/evo/io/parser/fasta_parser'
14 require 'lib/evo/io/parser/hmmscan_parser'
15
16 module Evoruby
17   class HmmscanDomainExtractor
18
19     ADD_TO_CLOSE_PAIRS = 0
20     def initialize
21     end
22
23     # raises ArgumentError, IOError, StandardError
24     def parse( domain_id,
25       hmmscan_output,
26       fasta_sequence_file,
27       outfile,
28       passed_seqs_outfile,
29       failed_seqs_outfile,
30       e_value_threshold,
31       length_threshold,
32       add_position,
33       add_domain_number,
34       add_species,
35       min_linker,
36       log )
37
38       Util.check_file_for_readability( hmmscan_output )
39       Util.check_file_for_readability( fasta_sequence_file )
40       Util.check_file_for_writability( outfile + ".fasta" )
41       Util.check_file_for_writability( passed_seqs_outfile )
42       Util.check_file_for_writability( failed_seqs_outfile )
43
44       in_msa = nil
45       factory = MsaFactory.new()
46       in_msa = factory.create_msa_from_file( fasta_sequence_file, FastaParser.new() )
47
48       if ( in_msa == nil || in_msa.get_number_of_seqs() < 1 )
49         error_msg = "could not find fasta sequences in " + fasta_sequence_file
50         raise IOError, error_msg
51       end
52
53       out_msa = Msa.new
54
55       failed_seqs = Msa.new
56       passed_seqs = Msa.new
57       out_msa_pairs = nil
58       out_msa_isolated = nil
59       out_msa_singles = nil
60       out_msa_single_domains_protein_seqs = nil
61       out_msa_close_pairs_protein_seqs = nil
62       out_msa_close_pairs_only_protein_seqs = nil
63       out_msa_isolated_protein_seqs = nil
64       out_msa_isolated_only_protein_seqs = nil
65       out_msa_isolated_and_close_pair_protein_seqs = nil
66       if min_linker
67         out_msa_pairs = Msa.new
68         out_msa_isolated = Msa.new
69         out_msa_singles = Msa.new
70         out_msa_single_domains_protein_seqs = Msa.new
71         out_msa_close_pairs_protein_seqs = Msa.new
72         out_msa_close_pairs_only_protein_seqs = Msa.new
73         out_msa_isolated_protein_seqs = Msa.new
74         out_msa_isolated_only_protein_seqs = Msa.new
75         out_msa_isolated_and_close_pair_protein_seqs  = Msa.new
76       end
77
78       ld = Constants::LINE_DELIMITER
79
80       domain_pass_counter                = 0
81       domain_fail_counter                = 0
82       passing_domains_per_protein        = 0
83       proteins_with_failing_domains      = 0
84       domain_not_present_counter         = 0
85       protein_counter                    = 1
86       max_domain_copy_number_per_protein = -1
87       max_domain_copy_number_sequence    = ""
88       passing_target_length_sum          = 0
89       overall_target_length_sum          = 0
90       overall_target_length_min          = 10000000
91       overall_target_length_max          = -1
92       passing_target_length_min          = 10000000
93       passing_target_length_max          = -1
94
95       overall_target_ie_min          = 10000000
96       overall_target_ie_max          = -1
97       passing_target_ie_min          = 10000000
98       passing_target_ie_max          = -1
99
100       hmmscan_datas = []
101
102       hmmscan_parser = HmmscanParser.new( hmmscan_output )
103       results = hmmscan_parser.parse
104
105       prev_query = nil
106       saw_target = false
107
108       results.each do | r |
109
110         if ( prev_query != nil ) && ( r.query != prev_query )
111           protein_counter += 1
112           passing_domains_per_protein = 0
113           if !saw_target
114             log << domain_not_present_counter.to_s + ": " + prev_query.to_s + " lacks target domain" + ld
115             domain_not_present_counter += 1
116           end
117           saw_target = false
118         end
119
120         prev_query = r.query
121
122         if domain_id != r.model
123           next
124         end
125
126         saw_target = true
127
128         sequence  = r.query
129         number    = r.number
130         out_of    = r.out_of
131         env_from  = r.env_from
132         env_to    = r.env_to
133         i_e_value = r.i_e_value
134         prev_query = r.query
135
136         length = 1 + env_to - env_from
137
138         overall_target_length_sum += length
139         if length > overall_target_length_max
140           overall_target_length_max = length
141         end
142         if length < overall_target_length_min
143           overall_target_length_min = length
144         end
145
146         if i_e_value > overall_target_ie_max
147           overall_target_ie_max = i_e_value
148         end
149         if i_e_value < overall_target_ie_min
150           overall_target_ie_min = i_e_value
151         end
152
153         if ( ( ( e_value_threshold < 0.0 ) || ( i_e_value <= e_value_threshold ) ) &&
154         ( ( length_threshold <= 0 ) || ( length >= length_threshold.to_f ) ) )
155           hmmscan_datas << HmmsearchData.new( sequence, number, out_of, env_from, env_to, i_e_value )
156           passing_target_length_sum += length
157           passing_domains_per_protein += 1
158           if length > passing_target_length_max
159             passing_target_length_max = length
160           end
161           if length < passing_target_length_min
162             passing_target_length_min = length
163           end
164           if i_e_value > passing_target_ie_max
165             passing_target_ie_max = i_e_value
166           end
167           if i_e_value < passing_target_ie_min
168             passing_target_ie_min = i_e_value
169           end
170           if ( passing_domains_per_protein > max_domain_copy_number_per_protein )
171             max_domain_copy_number_sequence    = sequence
172             max_domain_copy_number_per_protein = passing_domains_per_protein
173           end
174         else # no pass
175           log << domain_fail_counter.to_s + ": " + sequence.to_s + " fails threshold(s)"
176           if ( ( e_value_threshold.to_f >= 0.0 ) && ( i_e_value > e_value_threshold ) )
177             log << " iE=" + i_e_value.to_s
178           end
179           if ( ( length_threshold.to_f > 0 ) && ( env_to - env_from + 1 ) < length_threshold.to_f )
180             le = env_to - env_from + 1
181             log << " l=" + le.to_s
182           end
183           log << ld
184           domain_fail_counter += 1
185         end
186
187         if number > out_of
188           error_msg = "number > out_of (this should not have happened)"
189           raise StandardError, error_msg
190         end
191
192         if number == out_of
193           if !hmmscan_datas.empty?
194             process_hmmscan_datas( hmmscan_datas,
195             in_msa,
196             add_position,
197             add_domain_number,
198             add_species,
199             out_msa,
200             out_msa_singles,
201             out_msa_pairs,
202             out_msa_isolated,
203             min_linker,
204             out_msa_single_domains_protein_seqs,
205             out_msa_close_pairs_protein_seqs,
206             out_msa_close_pairs_only_protein_seqs,
207             out_msa_isolated_protein_seqs,
208             out_msa_isolated_only_protein_seqs,
209             out_msa_isolated_and_close_pair_protein_seqs )
210             domain_pass_counter += hmmscan_datas.length
211             if passed_seqs.find_by_name_start( sequence, true ).length < 1
212               add_sequence( sequence, in_msa, passed_seqs )
213             else
214               error_msg = "this should not have happened"
215               raise StandardError, error_msg
216             end
217           else # no pass
218             if failed_seqs.find_by_name_start( sequence, true ).length < 1
219               add_sequence( sequence, in_msa, failed_seqs )
220               proteins_with_failing_domains += 1
221             else
222               error_msg = "this should not have happened"
223               raise StandardError, error_msg
224             end
225           end
226           hmmscan_datas.clear
227         end
228
229       end # results.each do | r |
230
231       if (prev_query != nil) && (!saw_target)
232         log << domain_not_present_counter.to_s + ": " + prev_query.to_s + " lacks target domain" + ld
233         domain_not_present_counter += 1
234       end
235
236       if domain_pass_counter < 1
237         error_msg = "no domain sequences were extracted"
238         raise IOError, error_msg
239       end
240
241       if ( domain_not_present_counter + passed_seqs.get_number_of_seqs + proteins_with_failing_domains ) != protein_counter
242         error_msg = "not present + passing + not passing != proteins in sequence (fasta) file (this should not have happened)"
243         raise StandardError, error_msg
244       end
245
246       puts
247       log << ld
248
249       log << ld
250       avg_pass = ( passing_target_length_sum / domain_pass_counter )
251       puts( "Passing target domain lengths: average: " + avg_pass.to_s  )
252       log << "Passing target domain lengths: average: " + avg_pass.to_s
253       log << ld
254       puts( "Passing target domain lengths: min-max: " + passing_target_length_min.to_s + " - "  + passing_target_length_max.to_s)
255       log << "Passing target domain lengths: min-max: " + passing_target_length_min.to_s + " - "  + passing_target_length_max.to_s
256       log << ld
257       puts( "Passing target domain iE:      min-max: " + passing_target_ie_min.to_s + " - "  + passing_target_ie_max.to_s)
258       log << "Passing target domain iE:      min-max: " + passing_target_ie_min.to_s + " - "  + passing_target_ie_max.to_s
259       log << ld
260       puts( "Passing target domains:            sum: " + domain_pass_counter.to_s  )
261       log << "Passing target domains:            sum: " + domain_pass_counter.to_s
262       log << ld
263       log << ld
264       puts
265       sum = domain_pass_counter + domain_fail_counter
266       avg_all = overall_target_length_sum / sum
267       puts( "All target domain lengths:     average: " + avg_all.to_s  )
268       log << "All target domain lengths:     average: " + avg_all.to_s
269       log << ld
270       puts( "All target domain lengths:     min-max: " + overall_target_length_min.to_s + " - "  + overall_target_length_max.to_s)
271       log << "All target domain lengths:     min-max: " + overall_target_length_min.to_s + " - "  + overall_target_length_max.to_s
272       log << ld
273       puts( "All target target domain iE:   min-max: " + overall_target_ie_min.to_s + " - "  + overall_target_ie_max.to_s)
274       log << "All target target domain iE:   min-max: " + overall_target_ie_min.to_s + " - "  + overall_target_ie_max.to_s
275       log << ld
276       puts( "All target domains:                sum: " + sum.to_s  )
277       log << "All target domains:                sum: " + sum.to_s
278
279       puts
280       puts( "Proteins with passing target domain(s): " + passed_seqs.get_number_of_seqs.to_s )
281       puts( "Proteins with no passing target domain: " + proteins_with_failing_domains.to_s )
282       puts( "Proteins with no target domain        : " + domain_not_present_counter.to_s )
283
284       log << ld
285       log << ld
286       puts
287       puts( "Max target domain copy number per protein: " + max_domain_copy_number_per_protein.to_s )
288       log << "Max target domain copy number per protein: " + max_domain_copy_number_per_protein.to_s
289       log << ld
290
291       if ( max_domain_copy_number_per_protein > 1 )
292         puts( "First target protein with this copy number: " + max_domain_copy_number_sequence )
293         log << "First target protein with this copy number: " + max_domain_copy_number_sequence
294         log << ld
295       end
296
297       write_msa( out_msa, outfile + ".fasta"  )
298       write_msa( passed_seqs, passed_seqs_outfile )
299       write_msa( failed_seqs, failed_seqs_outfile )
300
301       if out_msa_pairs
302         write_msa( out_msa_pairs, outfile + "_" + min_linker.to_s + ".fasta")
303       end
304
305       if out_msa_singles
306         write_msa( out_msa_singles, outfile + "_singles.fasta")
307       end
308
309       if out_msa_isolated
310         write_msa( out_msa_isolated, outfile + "_" + min_linker.to_s + "_isolated.fasta");
311       end
312
313       if out_msa_single_domains_protein_seqs
314         write_msa( out_msa_single_domains_protein_seqs, outfile + "_proteins_with_singles.fasta" )
315       end
316
317       if out_msa_close_pairs_protein_seqs
318         write_msa( out_msa_close_pairs_protein_seqs, outfile + "_" + min_linker.to_s + "_proteins_close_pairs.fasta" )
319       end
320
321       if out_msa_close_pairs_only_protein_seqs
322         write_msa( out_msa_close_pairs_only_protein_seqs, outfile + "_" + min_linker.to_s + "_proteins_with_close_pairs_only.fasta" )
323       end
324
325       if  out_msa_isolated_protein_seqs
326         write_msa(  out_msa_isolated_protein_seqs, outfile + "_" + min_linker.to_s + "_proteins_with_isolated_domains.fasta" )
327       end
328
329       if out_msa_isolated_only_protein_seqs
330         write_msa( out_msa_isolated_only_protein_seqs, outfile + "_" + min_linker.to_s + "_proteins_with_isolated_domains_only.fasta" )
331       end
332
333       if out_msa_isolated_and_close_pair_protein_seqs
334         write_msa( out_msa_isolated_and_close_pair_protein_seqs, outfile + "_" + min_linker.to_s + "_proteins_with_isolated_and_close_pairs.fasta" )
335       end
336
337       if min_linker
338         if ( out_msa_single_domains_protein_seqs.get_number_of_seqs +
339         out_msa_close_pairs_only_protein_seqs.get_number_of_seqs +
340         out_msa_isolated_only_protein_seqs.get_number_of_seqs +
341         out_msa_isolated_and_close_pair_protein_seqs.get_number_of_seqs ) != passed_seqs.get_number_of_seqs
342           error_msg = "this should not have happened"
343           raise StandardError, error_msg
344         end
345       end
346
347       log << ld
348       log << "passing target domains                       : " + domain_pass_counter.to_s + ld
349       log << "failing target domains                       : " + domain_fail_counter.to_s + ld
350       log << "proteins in sequence (fasta) file            : " + in_msa.get_number_of_seqs.to_s + ld
351       log << "proteins in hmmscan outputfile               : " + protein_counter.to_s + ld
352       log << "proteins with passing target domain(s)       : " + passed_seqs.get_number_of_seqs.to_s + ld
353       log << "proteins with no passing target domain       : " + proteins_with_failing_domains.to_s + ld
354       log << "proteins with no target domain               : " + domain_not_present_counter.to_s + ld
355       if min_linker
356         log << "min linker length                            : " + min_linker.to_s + ld
357         log << "single domains                               : " + out_msa_singles.get_number_of_seqs.to_s + ld
358         log << "domains in close pairs                       : " + (2 * out_msa_pairs.get_number_of_seqs).to_s + ld
359         log << "isolated domains                             : " + out_msa_isolated.get_number_of_seqs.to_s + ld
360         log << "proteins with single domains                 : " + out_msa_single_domains_protein_seqs.get_number_of_seqs.to_s + ld
361         log << "proteins with close pair domains             : " + out_msa_close_pairs_protein_seqs.get_number_of_seqs.to_s + ld
362         log << "proteins with close pair domains only        : " + out_msa_close_pairs_only_protein_seqs.get_number_of_seqs.to_s + ld
363         log << "proteins with isolated domains               : " + out_msa_isolated_protein_seqs.get_number_of_seqs.to_s + ld
364         log << "proteins with isolated domains only          : " + out_msa_isolated_only_protein_seqs.get_number_of_seqs.to_s + ld
365         log << "proteins with close pair and isolated domains: " + out_msa_isolated_and_close_pair_protein_seqs.get_number_of_seqs.to_s + ld
366       end
367
368       log << ld
369
370       return domain_pass_counter
371
372     end # parse
373
374     private
375
376     def write_msa( msa, filename )
377       io = MsaIO.new()
378       w = FastaWriter.new()
379       w.set_line_width( 60 )
380       w.clean( true )
381       begin
382         io.write_to_file( msa, filename, w )
383       rescue Exception
384         error_msg = "could not write to \"" + filename + "\""
385         raise IOError, error_msg
386       end
387     end
388
389     def add_sequence( sequence_name, in_msa, add_to_msa )
390       seqs = in_msa.find_by_name_start( sequence_name, true )
391       if ( seqs.length < 1 )
392         error_msg = "sequence \"" + sequence_name + "\" not found in sequence file"
393         raise StandardError, error_msg
394       end
395       if ( seqs.length > 1 )
396         error_msg = "sequence \"" + sequence_name + "\" not unique in sequence file"
397         raise StandardError, error_msg
398       end
399       seq = in_msa.get_sequence( seqs[ 0 ] )
400       add_to_msa.add_sequence( seq )
401     end
402
403     def process_hmmscan_datas( hmmscan_datas,
404       in_msa,
405       add_position,
406       add_domain_number,
407       add_species,
408       out_msa,
409       out_msa_singles,
410       out_msa_pairs,
411       out_msa_isolated,
412       min_linker,
413       out_msa_single_domains_protein_seqs,
414       out_msa_close_pairs_protein_seqs,
415       out_msa_close_pairs_only_protein_seqs,
416       out_msa_isolated_protein_seqs,
417       out_msa_isolated_only_protein_seqs,
418       out_msa_isolated_and_close_pair_protein_seqs )
419
420       actual_out_of = hmmscan_datas.size
421       saw_close_pair = false
422       saw_isolated = false
423
424       seq_name = ""
425       prev_seq_name = nil
426
427       hmmscan_datas.each_with_index do |hmmscan_data, index|
428         if hmmscan_data.number < ( index + 1 )
429           error_msg = "hmmscan_data.number < ( index + 1 ) (this should not have happened)"
430           raise StandardError, error_msg
431         end
432
433         seq_name =  hmmscan_data.seq_name
434
435         extract_domain( seq_name,
436         index + 1,
437         actual_out_of,
438         hmmscan_data.env_from,
439         hmmscan_data.env_to,
440         in_msa,
441         out_msa,
442         add_position,
443         add_domain_number,
444         add_species )
445
446         if min_linker
447           if actual_out_of == 1
448             extract_domain( seq_name,
449             1,
450             1,
451             hmmscan_data.env_from,
452             hmmscan_data.env_to,
453             in_msa,
454             out_msa_singles,
455             add_position,
456             add_domain_number,
457             add_species )
458             if out_msa_single_domains_protein_seqs.find_by_name_start( seq_name, true ).length < 1
459               add_sequence( seq_name, in_msa, out_msa_single_domains_protein_seqs )
460             else
461               error_msg = "this should not have happened"
462               raise StandardError, error_msg
463             end
464
465           else
466             first = index == 0
467             last = index == hmmscan_datas.length - 1
468
469             if ( ( first && ( ( hmmscan_datas[ index + 1 ].env_from - hmmscan_data.env_to ) > min_linker) )  ||
470             ( last && ( ( hmmscan_data.env_from - hmmscan_datas[ index - 1 ].env_to ) > min_linker ) ) ||
471             ( !first && !last &&  ( ( hmmscan_datas[ index + 1 ].env_from - hmmscan_data.env_to ) > min_linker ) &&
472             ( ( hmmscan_data.env_from - hmmscan_datas[ index - 1 ].env_to ) > min_linker ) ) )
473
474               extract_domain( seq_name,
475               index + 1,
476               actual_out_of,
477               hmmscan_data.env_from,
478               hmmscan_data.env_to,
479               in_msa,
480               out_msa_isolated,
481               add_position,
482               add_domain_number,
483               add_species )
484               saw_isolated = true
485
486             elsif !first
487
488               from = hmmscan_datas[ index - 1 ].env_from
489               to = hmmscan_data.env_to
490
491               if ADD_TO_CLOSE_PAIRS > 0
492                 from = from - ADD_TO_CLOSE_PAIRS
493                 if from < 1
494                   from = 1
495                 end
496                 to = to + ADD_TO_CLOSE_PAIRS
497                 temp_seqs = in_msa.find_by_name_start( seq_name, true )
498                 temp_seq = in_msa.get_sequence( temp_seqs[ 0 ] )
499                 if to >  temp_seq.get_length
500                   to =  temp_seq.get_length
501                 end
502               end
503
504               extract_domain( seq_name,
505               index.to_s  + "+" + ( index + 1 ).to_s,
506               actual_out_of,
507               from,
508               to,
509               in_msa,
510               out_msa_pairs,
511               add_position,
512               add_domain_number,
513               add_species )
514               saw_close_pair = true
515             end
516           end
517         end
518         if prev_seq_name && prev_seq_name != seq_name
519           error_msg = "this should not have happened"
520           raise StandardError, error_msg
521         end
522         prev_seq_name = seq_name
523       end # each
524       if saw_isolated
525         if out_msa_isolated_protein_seqs.find_by_name_start( seq_name, true ).length < 1
526           add_sequence( seq_name, in_msa, out_msa_isolated_protein_seqs )
527         else
528           error_msg = "this should not have happened"
529           raise StandardError, error_msg
530         end
531       end
532       if saw_close_pair
533         if out_msa_close_pairs_protein_seqs.find_by_name_start( seq_name, true ).length < 1
534           add_sequence( seq_name, in_msa, out_msa_close_pairs_protein_seqs )
535         else
536           error_msg = "this should not have happened"
537           raise StandardError, error_msg
538         end
539       end
540       if saw_close_pair && saw_isolated
541         if out_msa_isolated_and_close_pair_protein_seqs.find_by_name_start( seq_name, true ).length < 1
542           add_sequence( seq_name, in_msa, out_msa_isolated_and_close_pair_protein_seqs )
543         else
544           error_msg = "this should not have happened"
545           raise StandardError, error_msg
546         end
547       elsif saw_close_pair
548         if out_msa_close_pairs_only_protein_seqs.find_by_name_start( seq_name, true ).length < 1
549           add_sequence( seq_name, in_msa, out_msa_close_pairs_only_protein_seqs )
550         else
551           error_msg = "this should not have happened"
552           raise StandardError, error_msg
553         end
554       elsif saw_isolated
555         if out_msa_isolated_only_protein_seqs.find_by_name_start( seq_name, true ).length < 1
556           add_sequence( seq_name, in_msa, out_msa_isolated_only_protein_seqs )
557         else
558           error_msg = "this should not have happened"
559           raise StandardError, error_msg
560         end
561       end
562     end # def process_hmmscan_data
563
564     def extract_domain( sequence,
565       number,
566       out_of,
567       seq_from,
568       seq_to,
569       in_msa,
570       out_msa,
571       add_position,
572       add_domain_number,
573       add_species )
574       if number.is_a?( Fixnum ) && ( number < 1 || out_of < 1 || number > out_of )
575         error_msg = "number=" + number.to_s + ", out of=" + out_of.to_s
576         raise StandardError, error_msg
577       end
578       if seq_from < 1 || seq_to < 1 || seq_from >= seq_to
579         error_msg = "impossible: seq-from=" + seq_from.to_s + ", seq-to=" + seq_to.to_s
580         raise StandardError, error_msg
581       end
582       seqs = in_msa.find_by_name_start( sequence, true )
583       if seqs.length < 1
584         error_msg = "sequence \"" + sequence + "\" not found in sequence file"
585         raise IOError, error_msg
586       end
587       if seqs.length > 1
588         error_msg = "sequence \"" + sequence + "\" not unique in sequence file"
589         raise IOError, error_msg
590       end
591       # hmmscan is 1 based, whereas sequences are 0 bases in this package.
592       seq = in_msa.get_sequence( seqs[ 0 ] ).get_subsequence( seq_from - 1, seq_to - 1 )
593
594       orig_name = seq.get_name
595
596       seq.set_name( orig_name.split[ 0 ] )
597
598       if add_position
599         seq.set_name( seq.get_name + "_" + seq_from.to_s + "-" + seq_to.to_s )
600       end
601
602       if out_of != 1 && add_domain_number
603         seq.set_name( seq.get_name + "~" + number.to_s + "-" + out_of.to_s )
604       end
605
606       if add_species
607         a = orig_name.rindex "["
608         b = orig_name.rindex "]"
609         unless a && b
610           error_msg = "species not found in " + orig_name
611           raise StandardError, error_msg
612         end
613         species = orig_name[ a .. b ]
614         seq.set_name( seq.get_name + " " + species )
615       end
616       out_msa.add_sequence( seq )
617     end
618
619     def is_ignorable?( line )
620       return ( line !~ /[A-Za-z0-9-]/ || line =~/^#/ )
621     end
622
623   end # class HmmscanDomainExtractor
624
625   class HmmsearchData
626     def initialize( seq_name, number, out_of, env_from, env_to, i_e_value )
627       @seq_name = seq_name
628       @number = number
629       @out_of = out_of
630       @env_from = env_from
631       @env_to = env_to
632       @i_e_value = i_e_value
633     end
634     attr_reader :seq_name, :number, :out_of, :env_from, :env_to, :i_e_value
635   end
636
637 end # module Evoruby
638