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__ |