File Coverage

blib/lib/Deco/Dive.pm
Criterion Covered Total %
statement 149 153 97.3
branch 41 50 82.0
condition 7 12 58.3
subroutine 13 13 100.0
pod 8 8 100.0
total 218 236 92.3


line stmt bran cond sub pod time code
1             #######################################
2             # Module : Deco::Dive.pm
3             # Author : Jaap Voets
4             # Date : 27-05-2006
5             # $Revision$
6             #######################################
7             package Deco::Dive;
8              
9 4     4   92545 use strict;
  4         8  
  4         142  
10 4     4   18 use warnings;
  4         7  
  4         100  
11 4     4   21 use Carp;
  4         10  
  4         882  
12 4     4   5896 use Config::General;
  4         140748  
  4         348  
13 4     4   3319 use Deco::Tissue;
  4         16  
  4         6152  
14              
15             our $VERSION = '0.4';
16              
17             our @MODELS = ('haldane', 'padi', 'usnavy');
18              
19             # Constructor
20             sub new {
21 9     9 1 8977 my $class = shift;
22 9         44 my %args = @_;
23              
24 9         21 my $self = {};
25              
26             # the data points for the dive, both arrays
27 9         31 $self->{timepoints} = [];
28 9         20 $self->{depths} = [];
29              
30             # an array of tissues to use
31 9         19 $self->{tissues} = ();
32            
33             # super structure to remember all tissue info per timepoint
34 9         20 $self->{info} = {};
35              
36             # where can we find the config?
37 9   100     54 $self->{config_dir} = $args{configdir} || '.';
38              
39             # theoretical tissue model we'll be using
40 9         55 $self->{model} = '';
41 9         17 $self->{model_name} = '';
42 9         22 bless $self, $class;
43            
44 9         27 return $self;
45             }
46              
47             # load the dive profile data from a file
48             sub load_data_from_file {
49 2     2 1 8 my $self = shift;
50 2         6 my %opt = @_;
51              
52 2         4 my $file = $opt{file};
53 2 50       7 croak "No file specified, to load dive profile" unless $file;
54             # check whether the file exists
55 2 50       59 croak "File $file does not exist" unless ( -e $file);
56              
57             # field separator
58 2   50     41 my $sep = $opt{separator} || ';';
59 2   50     13 my $timefield = $opt{timefield} || '0';
60 2   50     13 my $depthfield = $opt{depthfield} || 1;
61 2   50     18 my $timefactor = $opt{timefactor} || 1; # factor to get each time point in seconds
62              
63 2         4 my (@times, @depths);
64 2 50       77 open (IN, $file) || croak "Can't open file $file for reading";
65 2         47 while (my $line = ) {
66 56         62 chomp($line);
67 56 50       108 next if $line =~ /^\s*#/; # skip comment lines
68 56 50       106 next if $line =~ /^\s+$/; # skip empty lines
69 56         179 my @fields = split(/$sep/, $line);
70 56         112 push @times, $timefactor * $fields[$timefield];
71 56         82 my $depth = $fields[$depthfield];
72 56 50       178 if ($depth < 0) {
73 0         0 $depth = -1 * $depth;
74             }
75 56         214 push @depths, $depth;
76             }
77 2         33 close(IN);
78            
79 2         14 $self->{depths} = \@depths;
80 2         13 $self->{timepoints} = \@times;
81            
82             }
83              
84             # set a time, depth point
85             sub point {
86 7     7 1 40 my $self = shift;
87 7         13 my ($time, $depth) = @_;
88 7         11 push @{ $self->{depths} }, $depth;
  7         17  
89 7         15 push @{ $self->{timepoints} }, $time;
  7         21  
90             }
91              
92             # pick a model and load the corresponding config
93             # this will create a list of tissues
94             # either specify a config file and read the model from there
95             # - or - specify a model and read in the default file
96             sub model {
97 17     17 1 2056 my $self = shift;
98 17         51 my %opt = @_;
99              
100 17         22 my ($config_file, $model);
101 17 100       54 if ( $opt{config} ) {
    100          
102 15         26 $config_file = $opt{config};
103             # model will be read from config
104             } elsif ( $opt{model} ) {
105 1         4 $model = lc( $opt{model} );
106 1         23 $config_file = $self->{config_dir} . "/$model.cnf";
107             } else {
108 1         23 croak "Please specify the config file or model to use!";
109             }
110              
111             # load the config
112 16         206 my $conf = new Config::General( -ConfigFile => $config_file, -LowerCaseNames => 1 );
113 15         80594 my %config = $conf->getall;
114            
115 15         192 $model = lc($config{model});
116              
117             # remember the model we use
118 15         49 $self->{model} = $model;
119 15         48 $self->{model_name} = $config{name};
120              
121 15 50       41 croak "Invalid model $model" unless grep { $_ eq $model } @MODELS;
  45         138  
122            
123             # cleanup first
124 15         37 $self->{tissues} = ();
125              
126             # create all the tissues
127 15         45 foreach my $num (keys %{ $config{tissue} }) {
  15         89  
128 175         894 $self->{tissues}[$num] = new Deco::Tissue( halftime => $config{tissue}{$num}{halftime},
129             M0 => $config{tissue}{$num}{m0},
130             deltaM => $config{tissue}{$num}{deltam} ,
131             nr => $num,
132             );
133             }
134            
135 15         537 return 1;
136             }
137              
138             # run the simulation
139             sub simulate {
140 9     9 1 90 my $self = shift;
141 9         23 my %opt = @_;
142              
143             # model passed to us takes precedence, if that is not present
144             # we see if the model was already set, otherwise we default to haldane
145 9   50     499 my $model = lc($opt{model}) || $self->{model} || 'haldane';
146 9 100       34 croak "Invalid model $model" unless grep { $_ eq $model } @MODELS;
  27         84  
147            
148             # first load the model
149 8         79 $self->model( model => $model, config => $self->{config_dir} . '/' . $model . '.cnf');
150            
151             # then check whether we loaded data
152 8 100       16 if ( scalar( @{ $self->{timepoints} } ) == 0 ) {
  8         40  
153 1         23 croak "No dive profile data present, forgot to call dive->load_data_from_file() ?";
154             }
155            
156             # step through all the timepoints & depths
157 7         15 my $i = 0;
158 7         15 my @times = @{ $self->{timepoints} };
  7         27  
159 7         14 my @depths = @{ $self->{depths} };
  7         55  
160 7         20 foreach my $time ( @times ) {
161             # get the corresponding depth
162 61         112 my $depth = $depths[$i];
163 61         83 $i++;
164              
165 61         80 my $nodeco_dive = 1000000;
166 61         95 my $leading_tissue_deco = '';
167              
168 61         71 my $safe_depth_dive = 0;
169 61         75 my $leading_tissue_depth = '';
170              
171             # loop over all the tissues
172 61         68 foreach my $tissue ( @{ $self->{tissues} } ) {
  61         205  
173 610 100       1372 next if ! defined $tissue;
174              
175 549         1334 my $num = $tissue->nr;
176              
177 549         1492 $tissue->point( $time, $depth );
178            
179             # we like to have
180             # no_deco time, is special, it can return - for not applicable
181 549         1302 my $nodeco = $tissue->nodeco_time();
182 549 100       1353 $nodeco = undef if $nodeco eq '-';
183 549         2242 $self->{info}->{$num}->{$time}->{nodeco_time} = $nodeco;
184 549 100       1038 if ($nodeco) {
185 165 100       402 if ($nodeco < $nodeco_dive) {
186 84         98 $nodeco_dive = $nodeco;
187 84         206 $leading_tissue_deco = $tissue->nr();
188             }
189             }
190              
191             # safe depth, meters, positive
192 549         1397 my $safe_depth = $tissue->safe_depth();
193 549         1570 $self->{info}->{$num}->{$time}->{safe_depth} = $safe_depth;
194 549 50       1084 if ($safe_depth > $safe_depth_dive) {
195 0         0 $safe_depth_dive = $safe_depth;
196 0         0 $leading_tissue_depth = $tissue->nr();
197             }
198              
199             # percentage filled compared to M0 pressure
200 549         1562 $self->{info}->{$num}->{$time}->{percentage} = $tissue->percentage();
201              
202             # internal pressure
203 549         2020 $self->{info}->{$num}->{$time}->{pressure} = $tissue->internalpressure();
204              
205             # OTU's
206 549         1524 $self->{info}->{$num}->{$time}->{pressure} = $tissue->calculate_otu();
207              
208             }
209 61 100       192 if ($nodeco_dive == 1000000) {
210 24         35 $nodeco_dive = '-';
211             }
212 61         266 $self->{info}->{dive}->{$time}->{nodeco} = $nodeco_dive;
213 61         154 $self->{info}->{dive}->{$time}->{leadingtissuedeco} = $leading_tissue_deco;
214 61         147 $self->{info}->{dive}->{$time}->{safedepth} = $safe_depth_dive;
215 61         237 $self->{info}->{dive}->{$time}->{leadingtissuedepth} = $leading_tissue_depth;
216              
217             }
218            
219             }
220              
221             # set gas fractions
222             sub gas {
223 2     2 1 41 my $self = shift;
224 2         9 my %gaslist = @_;
225             # just pass it off to each tissue
226 2         5 foreach my $tissue ( @{ $self->{tissues} } ) {
  2         53  
227 12 100       33 next if ! defined $tissue;
228              
229             # the tissue module will croak on setting wrong gas
230             # just let it bubble up to the calling script from here
231 10         43 $tissue->gas( %gaslist );
232             }
233             }
234              
235             # calculate the no-deco time for the dive
236             # this will be the smalles value of the nodeco times of
237             # the tissues of this model
238             #
239             # time is minutes, it takes the current depth and time of the tissue
240             # second return value is the tissue nr that gave the minimal nodeco_time
241             sub nodeco_time {
242 5     5 1 30 my $self = shift;
243             # loop over all the tissues
244 5         7 my $nodeco_time = 1000000; # start with absurd high value for easy comparing
245 5         6 my $tissue_nr = '';
246 5         6 foreach my $tissue ( @{ $self->{tissues} } ) {
  5         28  
247 50 100       91 next if ! defined $tissue;
248 45         110 my $time = $tissue->nodeco_time();
249 45 100       100 if ($time ne '-') {
250 37 100       106 if ($time < $nodeco_time) {
251 6         7 $nodeco_time = $time;
252 6         15 $tissue_nr = $tissue->nr();
253             }
254             }
255             }
256 5 50       18 if ($nodeco_time == 1000000) {
257 0         0 $nodeco_time = '-';
258             }
259 5         21 return ($nodeco_time, $tissue_nr);
260             }
261              
262             # return a tissue by number
263             sub tissue {
264 3     3 1 61 my $self =shift;
265 3         4 my $tissue_num = shift;
266            
267 3 100       18 croak "Please specify a tissue nr" unless defined $tissue_num;
268            
269 2         4 foreach my $tissue ( @{ $self->{tissues} } ) {
  2         7  
270 13 100       29 next if ! defined $tissue;
271 11 100       29 if ( $tissue->nr() == $tissue_num ) {
272 1         4 return $tissue;
273             }
274             }
275            
276             # if we make it to here the tissue is not known
277 1         21 croak "Tissue nr $tissue_num is unknown";
278             }
279             1;
280              
281              
282             __END__