line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Math::ODE; |
2
|
7
|
|
|
7
|
|
171574
|
use strict; |
|
7
|
|
|
|
|
15
|
|
|
7
|
|
|
|
|
188
|
|
3
|
7
|
|
|
7
|
|
36
|
use warnings; |
|
7
|
|
|
|
|
12
|
|
|
7
|
|
|
|
|
212
|
|
4
|
|
|
|
|
|
|
|
5
|
7
|
|
|
7
|
|
6584
|
use Data::Dumper; |
|
7
|
|
|
|
|
74228
|
|
|
7
|
|
|
|
|
489
|
|
6
|
7
|
|
|
7
|
|
54
|
use Carp; |
|
7
|
|
|
|
|
15
|
|
|
7
|
|
|
|
|
11161
|
|
7
|
|
|
|
|
|
|
my $VERSION = '0.06'; |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
$Data::Dumper::Varname = "y"; |
10
|
|
|
|
|
|
|
$Data::Dumper::Indent = 1; |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
sub evolve |
13
|
|
|
|
|
|
|
{ |
14
|
41
|
|
|
41
|
0
|
1221
|
my $self = shift; |
15
|
41
|
|
|
|
|
89
|
my ($F,$h,$t,$initial,$file) = map{ $self->{$_} } qw(ODE step t0 initial file); |
|
205
|
|
|
|
|
437
|
|
16
|
41
|
100
|
50
|
|
|
336
|
my $delim = $self->{csv} ? ',' : ($self->{delim} || $self->{delimeter} || " "); |
17
|
41
|
|
|
|
|
64
|
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
|
|
|
|
|
102
|
@$y = @$initial; |
23
|
|
|
|
|
|
|
|
24
|
41
|
100
|
|
|
|
97
|
if( defined $file ){ |
25
|
4
|
50
|
|
|
|
458
|
open($fh,'>', $file) or croak "$file: $!"; |
26
|
|
|
|
|
|
|
} |
27
|
|
|
|
|
|
|
|
28
|
41
|
|
|
|
|
88
|
$self->_clear_values; |
29
|
|
|
|
|
|
|
|
30
|
41
|
|
|
|
|
137
|
while ( $t < $self->{tf} ){ |
31
|
|
|
|
|
|
|
# use Runge Kutta 4th order to step from $t to $t + $h |
32
|
12283
|
|
|
|
|
22836
|
$y = _RK4($self,$t,$y); |
33
|
|
|
|
|
|
|
|
34
|
12283
|
50
|
|
|
|
28437
|
warn "Exiting RK4 with t=$t ," . Dumper($y) . "\n" if( $self->{verbose} > 1 ); |
35
|
|
|
|
|
|
|
|
36
|
12283
|
|
|
|
|
24358
|
for my $i ( 0 .. $self->{N}-1 ){ |
37
|
|
|
|
|
|
|
# check for under/over flow |
38
|
22900
|
50
|
|
|
|
186458
|
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
|
|
|
|
|
16612
|
$t += $h; |
43
|
|
|
|
|
|
|
|
44
|
12283
|
|
|
|
|
18650
|
my $str = join $delim, map { sprintf "%0.12f", $_ } ($t, @$y); |
|
35183
|
|
|
|
|
143553
|
|
45
|
12283
|
|
|
|
|
20778
|
chop $str; |
46
|
|
|
|
|
|
|
|
47
|
12283
|
100
|
|
|
|
52928
|
if( defined $file ){ |
|
|
100
|
|
|
|
|
|
48
|
44
|
|
|
|
|
189
|
print $fh "$str\n"; |
49
|
|
|
|
|
|
|
} elsif ( ! $self->{keep_values} ) { |
50
|
11
|
|
|
|
|
1641
|
print "$str\n"; |
51
|
|
|
|
|
|
|
} |
52
|
|
|
|
|
|
|
} |
53
|
41
|
100
|
|
|
|
281
|
close $fh if defined $file; |
54
|
41
|
|
|
|
|
145
|
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
|
|
17115
|
my ($self,$t,$y) = @_; |
64
|
12283
|
|
|
|
|
16238
|
my $F = $self->{ODE}; |
65
|
12283
|
|
|
|
|
16153
|
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
|
|
|
|
|
12852
|
my (@w1,@w2,@w3,@w4,$q); |
71
|
12283
|
|
|
|
|
26614
|
my @indices = ( 0 .. $self->{N} - 1 ); |
72
|
|
|
|
|
|
|
|
73
|
12283
|
|
|
|
|
17633
|
map { $w1[$_] = $h * &{ $F->[$_] }($t,$y) } @indices; |
|
22900
|
|
|
|
|
58375
|
|
|
22900
|
|
|
|
|
50248
|
|
74
|
12283
|
|
|
|
|
58508
|
map { $q->[$_] = $y->[$_] + 0.5*$w1[$_] } @indices; |
|
22900
|
|
|
|
|
54351
|
|
75
|
|
|
|
|
|
|
|
76
|
12283
|
|
|
|
|
15888
|
map { $w2[$_] = $h * &{ $F->[$_] }($t + 0.5*$h,$q) } @indices; |
|
22900
|
|
|
|
|
62043
|
|
|
22900
|
|
|
|
|
50355
|
|
77
|
12283
|
|
|
|
|
54839
|
map { $q->[$_] = $y->[$_] + 0.5*$w2[$_] } @indices; |
|
22900
|
|
|
|
|
46158
|
|
78
|
|
|
|
|
|
|
|
79
|
12283
|
|
|
|
|
15633
|
map { $w3[$_] = $h * &{ $F->[$_] }($t + 0.5*$h,$q) } @indices; |
|
22900
|
|
|
|
|
60216
|
|
|
22900
|
|
|
|
|
51710
|
|
80
|
12283
|
|
|
|
|
52881
|
map { $q->[$_] = $y->[$_] + $w3[$_] } @indices; |
|
22900
|
|
|
|
|
42543
|
|
81
|
|
|
|
|
|
|
|
82
|
12283
|
|
|
|
|
15418
|
map { $w4[$_] = $h * &{ $F->[$_] }($t + $h,$q) } @indices; |
|
22900
|
|
|
|
|
58302
|
|
|
22900
|
|
|
|
|
46766
|
|
83
|
12283
|
|
|
|
|
52058
|
map { $y->[$_]+= ( $w1[$_] + 2 * $w2[$_] + 2 * $w3[$_] + $w4[$_])/6 } @indices; |
|
22900
|
|
|
|
|
55042
|
|
84
|
|
|
|
|
|
|
|
85
|
12283
|
|
|
|
|
25844
|
$self->_store_values( $t + $h, $y ); |
86
|
|
|
|
|
|
|
|
87
|
12283
|
|
|
|
|
37450
|
return $y; |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
sub _store_values |
91
|
|
|
|
|
|
|
{ |
92
|
12283
|
|
|
12283
|
|
15938
|
my ($self,$t, $y) = @_; |
93
|
12283
|
100
|
|
|
|
24199
|
return unless $self->{keep_values}; |
94
|
12261
|
|
|
|
|
44342
|
my $s = sprintf '%0.12f', $t ; |
95
|
12261
|
|
|
|
|
13620
|
push @{ $self->{values}{$s} }, @$y; |
|
12261
|
|
|
|
|
48977
|
|
96
|
|
|
|
|
|
|
} |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
sub values_at |
99
|
|
|
|
|
|
|
{ |
100
|
12230
|
|
|
12230
|
0
|
18627
|
my ($self,$t, %args) = @_; |
101
|
12230
|
100
|
|
|
|
21344
|
if ($self->{keep_values}){ |
102
|
12229
|
|
|
|
|
11515
|
return @{ $self->{values}{sprintf('%0.12f',$t)} }; |
|
12229
|
|
|
|
|
70367
|
|
103
|
|
|
|
|
|
|
} else { |
104
|
1
|
|
|
|
|
140
|
warn "Values were not kept because keep_values was set to 0"; |
105
|
1
|
|
|
|
|
7
|
return; |
106
|
|
|
|
|
|
|
} |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
# because Math::ODE implements a 4th order Runge-Kutta method |
109
|
73
|
|
|
73
|
0
|
841
|
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
|
|
|
|
|
26
|
return $self->{file}; |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
sub step |
123
|
|
|
|
|
|
|
{ |
124
|
0
|
|
|
0
|
0
|
0
|
my ($self,$step) = @_; |
125
|
0
|
0
|
|
|
|
0
|
if (defined $step){ |
126
|
0
|
0
|
0
|
|
|
0
|
croak "Stepsize must be strictly between zero and one" |
127
|
|
|
|
|
|
|
if ($step <= 0 || $step >= 1); |
128
|
|
|
|
|
|
|
|
129
|
0
|
|
|
|
|
0
|
$self->{step} = $step; |
130
|
|
|
|
|
|
|
} |
131
|
0
|
|
|
|
|
0
|
return $self->{step}; |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
sub initial |
135
|
|
|
|
|
|
|
{ |
136
|
0
|
|
|
0
|
0
|
0
|
my ($self, $initial) = @_; |
137
|
|
|
|
|
|
|
|
138
|
0
|
0
|
|
|
|
0
|
croak "not an array ref" unless ref $initial eq "ARRAY"; |
139
|
|
|
|
|
|
|
|
140
|
0
|
|
|
|
|
0
|
$self->{initial} = $initial; |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
sub _clear_values |
145
|
|
|
|
|
|
|
{ |
146
|
82
|
|
|
82
|
|
142
|
my $self = shift; |
147
|
|
|
|
|
|
|
$self->{values} = {} |
148
|
82
|
|
|
|
|
177
|
} |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
sub _init |
151
|
|
|
|
|
|
|
{ |
152
|
41
|
|
|
41
|
|
178
|
my ($self,%args) = @_; |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
# defaults |
155
|
41
|
|
|
|
|
117
|
$self->{keep_values} = 1; |
156
|
41
|
|
|
|
|
91
|
$self->{verbose} = 1; |
157
|
41
|
|
|
|
|
63
|
$self->{step} = 0.1; |
158
|
41
|
|
|
|
|
64
|
$self->{csv} = 0; |
159
|
41
|
|
50
|
|
|
45
|
$self->{N} = scalar( @{ $args{ODE} } ) || 1; |
160
|
|
|
|
|
|
|
|
161
|
41
|
|
|
|
|
253
|
@$self{keys %args} = values %args; |
162
|
|
|
|
|
|
|
|
163
|
41
|
|
|
|
|
141
|
$self->_clear_values; |
164
|
|
|
|
|
|
|
|
165
|
41
|
50
|
|
|
|
70
|
if( $self->{N} != scalar( @{ $args{initial } }) ){ |
|
41
|
|
|
|
|
152
|
|
166
|
0
|
|
|
|
|
0
|
croak "Must have same number of initial conditions as equations!"; |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
|
169
|
41
|
50
|
|
|
|
172
|
croak "Stepsize must be positive!" if $self->{step} <= 0; |
170
|
41
|
50
|
|
|
|
119
|
croak "\$self->t0 must be less than \$self->tf!" if $self->{t0} >= $self->{tf}; |
171
|
|
|
|
|
|
|
|
172
|
41
|
|
|
|
|
189
|
return $self; |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
sub max_error |
176
|
|
|
|
|
|
|
{ |
177
|
36
|
|
|
36
|
0
|
246
|
my ($self, $sols) = @_; |
178
|
|
|
|
|
|
|
|
179
|
36
|
|
|
|
|
45
|
my $max_error = 0; |
180
|
|
|
|
|
|
|
|
181
|
36
|
|
|
|
|
46
|
for my $pt ( sort keys %{$self->{values}} ){ |
|
36
|
|
|
|
|
8066
|
|
182
|
12228
|
|
|
|
|
14069
|
my $k = 0; |
183
|
12228
|
|
|
|
|
22087
|
my @vals = $self->values_at($pt); |
184
|
|
|
|
|
|
|
|
185
|
12228
|
|
|
|
|
20817
|
for my $sol ( @$sols ) { |
186
|
15767
|
|
|
|
|
31901
|
my $res = abs( $vals[$k] - &$sol($pt) ); |
187
|
15767
|
100
|
|
|
|
2142055
|
$max_error = $res if ($res > $max_error); |
188
|
15767
|
|
|
|
|
28336
|
$k++; |
189
|
|
|
|
|
|
|
} |
190
|
|
|
|
|
|
|
} |
191
|
36
|
|
|
|
|
755
|
$max_error; |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
sub new { |
195
|
41
|
|
|
41
|
0
|
22698
|
my $class = shift; |
196
|
41
|
|
|
|
|
77
|
my $self = {}; |
197
|
41
|
|
|
|
|
80
|
bless $self, $class; |
198
|
41
|
|
|
|
|
130
|
$self->_init(@_); |
199
|
|
|
|
|
|
|
} |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
42; |
202
|
|
|
|
|
|
|
__END__ |