| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Math::ODE; |
|
2
|
7
|
|
|
7
|
|
167641
|
use strict; |
|
|
7
|
|
|
|
|
15
|
|
|
|
7
|
|
|
|
|
181
|
|
|
3
|
7
|
|
|
7
|
|
35
|
use warnings; |
|
|
7
|
|
|
|
|
13
|
|
|
|
7
|
|
|
|
|
191
|
|
|
4
|
|
|
|
|
|
|
|
|
5
|
7
|
|
|
7
|
|
6537
|
use Data::Dumper; |
|
|
7
|
|
|
|
|
71855
|
|
|
|
7
|
|
|
|
|
431
|
|
|
6
|
7
|
|
|
7
|
|
44
|
use Carp; |
|
|
7
|
|
|
|
|
15
|
|
|
|
7
|
|
|
|
|
10874
|
|
|
7
|
|
|
|
|
|
|
my $VERSION = '0.07'; |
|
8
|
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
$Data::Dumper::Varname = "y"; |
|
10
|
|
|
|
|
|
|
$Data::Dumper::Indent = 1; |
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
sub evolve |
|
13
|
|
|
|
|
|
|
{ |
|
14
|
41
|
|
|
41
|
0
|
1343
|
my $self = shift; |
|
15
|
41
|
|
|
|
|
98
|
my ($F,$h,$t,$initial,$file) = map{ $self->{$_} } qw(ODE step t0 initial file); |
|
|
205
|
|
|
|
|
472
|
|
|
16
|
41
|
100
|
50
|
|
|
379
|
my $delim = $self->{csv} ? ',' : ($self->{delim} || $self->{delimeter} || " "); |
|
17
|
41
|
|
|
|
|
58
|
my $y; |
|
18
|
|
|
|
|
|
|
my $fh; |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
# don't clobber the initial condition in case we want to do multiple runs |
|
21
|
|
|
|
|
|
|
# with the same object |
|
22
|
41
|
|
|
|
|
110
|
@$y = @$initial; |
|
23
|
|
|
|
|
|
|
|
|
24
|
41
|
100
|
|
|
|
98
|
if( defined $file ){ |
|
25
|
4
|
50
|
|
|
|
531
|
open($fh,'>', $file) or croak "$file: $!"; |
|
26
|
|
|
|
|
|
|
} |
|
27
|
|
|
|
|
|
|
|
|
28
|
41
|
|
|
|
|
101
|
$self->_clear_values; |
|
29
|
|
|
|
|
|
|
|
|
30
|
41
|
|
|
|
|
150
|
while ( $t < $self->{tf} ){ |
|
31
|
|
|
|
|
|
|
# use Runge Kutta 4th order to step from $t to $t + $h |
|
32
|
12283
|
|
|
|
|
22622
|
$y = _RK4($self,$t,$y); |
|
33
|
|
|
|
|
|
|
|
|
34
|
12283
|
50
|
|
|
|
28815
|
warn "Exiting RK4 with t=$t ," . Dumper($y) . "\n" if( $self->{verbose} > 1 ); |
|
35
|
|
|
|
|
|
|
|
|
36
|
12283
|
|
|
|
|
25887
|
for my $i ( 0 .. $self->{N}-1 ){ |
|
37
|
|
|
|
|
|
|
# check for under/over flow |
|
38
|
22900
|
50
|
|
|
|
194960
|
next unless $y->[$i] =~ qr/nan|infinity/i; |
|
39
|
0
|
0
|
|
|
|
0
|
warn "Bailing out, over/under flow at t=$t,y->[$i] = $y->[$i]" if $self->{verbose}; |
|
40
|
0
|
|
|
|
|
0
|
return undef; |
|
41
|
|
|
|
|
|
|
} |
|
42
|
12283
|
|
|
|
|
15994
|
$t += $h; |
|
43
|
|
|
|
|
|
|
|
|
44
|
12283
|
|
|
|
|
18650
|
my $str = join $delim, map { sprintf $self->{format}, $_ } ($t, @$y); |
|
|
35183
|
|
|
|
|
159577
|
|
|
45
|
12283
|
|
|
|
|
21390
|
chop $str; |
|
46
|
|
|
|
|
|
|
|
|
47
|
12283
|
100
|
|
|
|
56470
|
if( defined $file ){ |
|
|
|
100
|
|
|
|
|
|
|
48
|
44
|
|
|
|
|
218
|
print $fh "$str\n"; |
|
49
|
|
|
|
|
|
|
} elsif ( ! $self->{keep_values} ) { |
|
50
|
11
|
|
|
|
|
97
|
print "$str\n"; |
|
51
|
|
|
|
|
|
|
} |
|
52
|
|
|
|
|
|
|
} |
|
53
|
41
|
100
|
|
|
|
319
|
close $fh if defined $file; |
|
54
|
41
|
|
|
|
|
152
|
return $self; |
|
55
|
|
|
|
|
|
|
} |
|
56
|
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
sub _RK4 |
|
58
|
|
|
|
|
|
|
{ |
|
59
|
|
|
|
|
|
|
# $t = dependent variable |
|
60
|
|
|
|
|
|
|
# $y = $N - vector of independent variables |
|
61
|
|
|
|
|
|
|
# $h = step size |
|
62
|
|
|
|
|
|
|
# $F = arrayref of coderefs of the equations to solve |
|
63
|
12283
|
|
|
12283
|
|
17788
|
my ($self,$t,$y) = @_; |
|
64
|
12283
|
|
|
|
|
16202
|
my $F = $self->{ODE}; |
|
65
|
12283
|
|
|
|
|
16529
|
my $h = $self->{step}; |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
## w vectors hold constants for equations |
|
68
|
|
|
|
|
|
|
## each $q holds a modified $y vector to feed to the next |
|
69
|
|
|
|
|
|
|
## for loop ( ie $y + $w1/2 , etc ... ) |
|
70
|
12283
|
|
|
|
|
13503
|
my (@w1,@w2,@w3,@w4,$q); |
|
71
|
12283
|
|
|
|
|
28512
|
my @indices = ( 0 .. $self->{N} - 1 ); |
|
72
|
|
|
|
|
|
|
|
|
73
|
12283
|
|
|
|
|
18907
|
map { $w1[$_] = $h * &{ $F->[$_] }($t,$y) } @indices; |
|
|
22900
|
|
|
|
|
61080
|
|
|
|
22900
|
|
|
|
|
51945
|
|
|
74
|
12283
|
|
|
|
|
60733
|
map { $q->[$_] = $y->[$_] + 0.5*$w1[$_] } @indices; |
|
|
22900
|
|
|
|
|
54463
|
|
|
75
|
|
|
|
|
|
|
|
|
76
|
12283
|
|
|
|
|
16129
|
map { $w2[$_] = $h * &{ $F->[$_] }($t + 0.5*$h,$q) } @indices; |
|
|
22900
|
|
|
|
|
62670
|
|
|
|
22900
|
|
|
|
|
50095
|
|
|
77
|
12283
|
|
|
|
|
54919
|
map { $q->[$_] = $y->[$_] + 0.5*$w2[$_] } @indices; |
|
|
22900
|
|
|
|
|
45225
|
|
|
78
|
|
|
|
|
|
|
|
|
79
|
12283
|
|
|
|
|
16522
|
map { $w3[$_] = $h * &{ $F->[$_] }($t + 0.5*$h,$q) } @indices; |
|
|
22900
|
|
|
|
|
62666
|
|
|
|
22900
|
|
|
|
|
49155
|
|
|
80
|
12283
|
|
|
|
|
53383
|
map { $q->[$_] = $y->[$_] + $w3[$_] } @indices; |
|
|
22900
|
|
|
|
|
43926
|
|
|
81
|
|
|
|
|
|
|
|
|
82
|
12283
|
|
|
|
|
15117
|
map { $w4[$_] = $h * &{ $F->[$_] }($t + $h,$q) } @indices; |
|
|
22900
|
|
|
|
|
58400
|
|
|
|
22900
|
|
|
|
|
48696
|
|
|
83
|
12283
|
|
|
|
|
53497
|
map { $y->[$_]+= ( $w1[$_] + 2 * $w2[$_] + 2 * $w3[$_] + $w4[$_])/6 } @indices; |
|
|
22900
|
|
|
|
|
58164
|
|
|
84
|
|
|
|
|
|
|
|
|
85
|
12283
|
|
|
|
|
27787
|
$self->_store_values( $t + $h, $y ); |
|
86
|
|
|
|
|
|
|
|
|
87
|
12283
|
|
|
|
|
40282
|
return $y; |
|
88
|
|
|
|
|
|
|
} |
|
89
|
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
sub _store_values |
|
91
|
|
|
|
|
|
|
{ |
|
92
|
12283
|
|
|
12283
|
|
16970
|
my ($self,$t, $y) = @_; |
|
93
|
12283
|
100
|
|
|
|
25636
|
return unless $self->{keep_values}; |
|
94
|
12261
|
|
|
|
|
50544
|
my $s = sprintf $self->{format}, $t ; |
|
95
|
12261
|
|
|
|
|
14299
|
push @{ $self->{values}{$s} }, @$y; |
|
|
12261
|
|
|
|
|
53018
|
|
|
96
|
|
|
|
|
|
|
} |
|
97
|
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
sub values_at |
|
99
|
|
|
|
|
|
|
{ |
|
100
|
12230
|
|
|
12230
|
0
|
19167
|
my ($self,$t, %args) = @_; |
|
101
|
12230
|
100
|
|
|
|
22477
|
if ($self->{keep_values}){ |
|
102
|
12229
|
|
|
|
|
12252
|
return @{ $self->{values}{sprintf($self->{format},$t)} }; |
|
|
12229
|
|
|
|
|
76329
|
|
|
103
|
|
|
|
|
|
|
} else { |
|
104
|
1
|
|
|
|
|
62
|
warn "Values were not kept because keep_values was set to 0"; |
|
105
|
1
|
|
|
|
|
6
|
return; |
|
106
|
|
|
|
|
|
|
} |
|
107
|
|
|
|
|
|
|
} |
|
108
|
|
|
|
|
|
|
# because Math::ODE implements a 4th order Runge-Kutta method |
|
109
|
73
|
|
|
73
|
0
|
987
|
sub error { $_[0]->{step} ** 4 } |
|
110
|
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
sub file |
|
112
|
|
|
|
|
|
|
{ |
|
113
|
1
|
|
|
1
|
0
|
9
|
my ($self,$target) = @_; |
|
114
|
|
|
|
|
|
|
|
|
115
|
1
|
50
|
|
|
|
4
|
if ($target) { |
|
116
|
0
|
|
|
|
|
0
|
$self->{file} = $target; |
|
117
|
|
|
|
|
|
|
} else { |
|
118
|
1
|
|
|
|
|
28
|
return $self->{file}; |
|
119
|
|
|
|
|
|
|
} |
|
120
|
|
|
|
|
|
|
} |
|
121
|
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
sub format |
|
123
|
|
|
|
|
|
|
{ |
|
124
|
4
|
|
|
4
|
0
|
8
|
my ($self,$format) = @_; |
|
125
|
|
|
|
|
|
|
|
|
126
|
4
|
100
|
|
|
|
19
|
$format ? $self->{format} = $format : return $self->{format}; |
|
127
|
|
|
|
|
|
|
} |
|
128
|
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
sub step |
|
130
|
|
|
|
|
|
|
{ |
|
131
|
0
|
|
|
0
|
0
|
0
|
my ($self,$step) = @_; |
|
132
|
0
|
0
|
|
|
|
0
|
if (defined $step){ |
|
133
|
0
|
0
|
0
|
|
|
0
|
croak "Stepsize must be strictly between zero and one" |
|
134
|
|
|
|
|
|
|
if ($step <= 0 || $step >= 1); |
|
135
|
|
|
|
|
|
|
|
|
136
|
0
|
|
|
|
|
0
|
$self->{step} = $step; |
|
137
|
|
|
|
|
|
|
} |
|
138
|
0
|
|
|
|
|
0
|
return $self->{step}; |
|
139
|
|
|
|
|
|
|
} |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
sub initial |
|
142
|
|
|
|
|
|
|
{ |
|
143
|
0
|
|
|
0
|
0
|
0
|
my ($self, $initial) = @_; |
|
144
|
|
|
|
|
|
|
|
|
145
|
0
|
0
|
|
|
|
0
|
croak "not an array ref" unless ref $initial eq "ARRAY"; |
|
146
|
|
|
|
|
|
|
|
|
147
|
0
|
|
|
|
|
0
|
$self->{initial} = $initial; |
|
148
|
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
} |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
sub _clear_values |
|
152
|
|
|
|
|
|
|
{ |
|
153
|
82
|
|
|
82
|
|
123
|
my $self = shift; |
|
154
|
|
|
|
|
|
|
$self->{values} = {} |
|
155
|
82
|
|
|
|
|
203
|
} |
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
sub _init |
|
158
|
|
|
|
|
|
|
{ |
|
159
|
41
|
|
|
41
|
|
211
|
my ($self,%args) = @_; |
|
160
|
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
# defaults |
|
162
|
41
|
|
|
|
|
122
|
$self->{keep_values} = 1; |
|
163
|
41
|
|
|
|
|
79
|
$self->{verbose} = 1; |
|
164
|
41
|
|
|
|
|
77
|
$self->{step} = 0.1; |
|
165
|
41
|
|
|
|
|
75
|
$self->{csv} = 0; |
|
166
|
41
|
|
50
|
|
|
48
|
$self->{N} = scalar( @{ $args{ODE} } ) || 1; |
|
167
|
41
|
|
|
|
|
88
|
$self->{format} = '%.12f'; |
|
168
|
|
|
|
|
|
|
|
|
169
|
41
|
|
|
|
|
280
|
@$self{keys %args} = values %args; |
|
170
|
|
|
|
|
|
|
|
|
171
|
41
|
|
|
|
|
168
|
$self->_clear_values; |
|
172
|
|
|
|
|
|
|
|
|
173
|
41
|
50
|
|
|
|
78
|
if( $self->{N} != scalar( @{ $args{initial } }) ){ |
|
|
41
|
|
|
|
|
158
|
|
|
174
|
0
|
|
|
|
|
0
|
croak "Must have same number of initial conditions as equations!"; |
|
175
|
|
|
|
|
|
|
} |
|
176
|
|
|
|
|
|
|
|
|
177
|
41
|
50
|
|
|
|
175
|
croak "Stepsize must be positive!" if $self->{step} <= 0; |
|
178
|
41
|
50
|
|
|
|
123
|
croak "\$self->t0 must be less than \$self->tf!" if $self->{t0} >= $self->{tf}; |
|
179
|
|
|
|
|
|
|
|
|
180
|
41
|
|
|
|
|
187
|
return $self; |
|
181
|
|
|
|
|
|
|
} |
|
182
|
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
sub max_error |
|
184
|
|
|
|
|
|
|
{ |
|
185
|
36
|
|
|
36
|
0
|
286
|
my ($self, $sols) = @_; |
|
186
|
|
|
|
|
|
|
|
|
187
|
36
|
|
|
|
|
50
|
my $max_error = 0; |
|
188
|
|
|
|
|
|
|
|
|
189
|
36
|
|
|
|
|
53
|
for my $pt ( sort keys %{$self->{values}} ){ |
|
|
36
|
|
|
|
|
8922
|
|
|
190
|
12228
|
|
|
|
|
13758
|
my $k = 0; |
|
191
|
12228
|
|
|
|
|
23468
|
my @vals = $self->values_at($pt); |
|
192
|
|
|
|
|
|
|
|
|
193
|
12228
|
|
|
|
|
21448
|
for my $sol ( @$sols ) { |
|
194
|
15767
|
|
|
|
|
33354
|
my $res = abs( $vals[$k] - &$sol($pt) ); |
|
195
|
15767
|
100
|
|
|
|
1967137
|
$max_error = $res if ($res > $max_error); |
|
196
|
15767
|
|
|
|
|
30097
|
$k++; |
|
197
|
|
|
|
|
|
|
} |
|
198
|
|
|
|
|
|
|
} |
|
199
|
36
|
|
|
|
|
821
|
$max_error; |
|
200
|
|
|
|
|
|
|
} |
|
201
|
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
sub new { |
|
203
|
41
|
|
|
41
|
0
|
25882
|
my $class = shift; |
|
204
|
41
|
|
|
|
|
90
|
my $self = {}; |
|
205
|
41
|
|
|
|
|
91
|
bless $self, $class; |
|
206
|
41
|
|
|
|
|
147
|
$self->_init(@_); |
|
207
|
|
|
|
|
|
|
} |
|
208
|
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
42; |
|
210
|
|
|
|
|
|
|
__END__ |