line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Math::ODE; |
2
|
7
|
|
|
7
|
|
183751
|
use strict; |
|
7
|
|
|
|
|
17
|
|
|
7
|
|
|
|
|
235
|
|
3
|
|
|
|
|
|
|
|
4
|
7
|
|
|
7
|
|
7099
|
use Data::Dumper; |
|
7
|
|
|
|
|
95847
|
|
|
7
|
|
|
|
|
630
|
|
5
|
7
|
|
|
7
|
|
88
|
use Carp; |
|
7
|
|
|
|
|
19
|
|
|
7
|
|
|
|
|
10617
|
|
6
|
|
|
|
|
|
|
my $VERSION = '0.05_01'; |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
$Data::Dumper::Varname = "y"; |
9
|
|
|
|
|
|
|
$Data::Dumper::Indent = 1; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
sub evolve |
12
|
|
|
|
|
|
|
{ |
13
|
41
|
|
|
41
|
0
|
1999
|
my $self = shift; |
14
|
41
|
|
|
|
|
85
|
my ($F,$h,$t,$initial,$file) = map{ $self->{$_} } qw(ODE step t0 initial file); |
|
205
|
|
|
|
|
458
|
|
15
|
41
|
100
|
50
|
|
|
931
|
my $delim = $self->{csv} ? ',' : ($self->{delim} || $self->{delimeter} || " "); |
16
|
41
|
|
|
|
|
62
|
my $y; |
17
|
|
|
|
|
|
|
my $fh; |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
# don't clobber the initial condition in case we want to do multiple runs |
20
|
|
|
|
|
|
|
# with the same object |
21
|
41
|
|
|
|
|
105
|
@$y = @$initial; |
22
|
|
|
|
|
|
|
|
23
|
41
|
100
|
|
|
|
112
|
if( defined $file ){ |
24
|
4
|
50
|
|
|
|
524
|
open($fh,'>', $file) or croak "$file: $!"; |
25
|
|
|
|
|
|
|
} |
26
|
|
|
|
|
|
|
|
27
|
41
|
|
|
|
|
94
|
$self->_clear_values; |
28
|
|
|
|
|
|
|
|
29
|
41
|
|
|
|
|
134
|
while ( $t < $self->{tf} ){ |
30
|
|
|
|
|
|
|
# use Runge Kutta 4th order to step from $t to $t + $h |
31
|
12283
|
|
|
|
|
23269
|
$y = _RK4($self,$t,$y); |
32
|
|
|
|
|
|
|
|
33
|
12283
|
50
|
|
|
|
28730
|
warn "Exiting RK4 with t=$t ," . Dumper($y) . "\n" if( $self->{verbose} > 1 ); |
34
|
|
|
|
|
|
|
|
35
|
12283
|
|
|
|
|
22614
|
for my $i ( 0 .. $self->{N}-1 ){ |
36
|
|
|
|
|
|
|
# check for under/over flow |
37
|
22900
|
50
|
|
|
|
208918
|
next unless $y->[$i] =~ qr/nan|infinity/i; |
38
|
0
|
0
|
|
|
|
0
|
warn "Bailing out, over/under flow at t=$t,y->[$i] = $y->[$i]" if $self->{verbose}; |
39
|
0
|
|
|
|
|
0
|
return undef; |
40
|
|
|
|
|
|
|
} |
41
|
12283
|
|
|
|
|
17202
|
$t += $h; |
42
|
|
|
|
|
|
|
|
43
|
12283
|
|
|
|
|
17982
|
my $str = join $delim, map { sprintf "%0.12f", $_ } ($t, @$y); |
|
35183
|
|
|
|
|
147359
|
|
44
|
12283
|
|
|
|
|
18693
|
chop $str; |
45
|
|
|
|
|
|
|
|
46
|
12283
|
100
|
|
|
|
54385
|
if( defined $file ){ |
|
|
100
|
|
|
|
|
|
47
|
44
|
|
|
|
|
167
|
print $fh "$str\n"; |
48
|
|
|
|
|
|
|
} elsif ( ! $self->{keep_values} ) { |
49
|
11
|
|
|
|
|
1539
|
print "$str\n"; |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
} |
52
|
41
|
100
|
|
|
|
377
|
close $fh if defined $file; |
53
|
41
|
|
|
|
|
145
|
return $self; |
54
|
|
|
|
|
|
|
} |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
sub _RK4 |
57
|
|
|
|
|
|
|
{ |
58
|
|
|
|
|
|
|
# $t = dependent variable |
59
|
|
|
|
|
|
|
# $y = $N - vector of independent variables |
60
|
|
|
|
|
|
|
# $h = step size |
61
|
|
|
|
|
|
|
# $F = arrayref of coderefs of the equations to solve |
62
|
12283
|
|
|
12283
|
|
16204
|
my ($self,$t,$y) = @_; |
63
|
12283
|
|
|
|
|
15759
|
my $F = $self->{ODE}; |
64
|
12283
|
|
|
|
|
18067
|
my $h = $self->{step}; |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
## w vectors hold constants for equations |
67
|
|
|
|
|
|
|
## each $q holds a modified $y vector to feed to the next |
68
|
|
|
|
|
|
|
## for loop ( ie $y + $w1/2 , etc ... ) |
69
|
12283
|
|
|
|
|
12290
|
my (@w1,@w2,@w3,@w4,$q); |
70
|
12283
|
|
|
|
|
27338
|
my @indices = ( 0 .. $self->{N} - 1 ); |
71
|
|
|
|
|
|
|
|
72
|
12283
|
|
|
|
|
17696
|
map { $w1[$_] = $h * &{ $F->[$_] }($t,$y) } @indices; |
|
22900
|
|
|
|
|
70294
|
|
|
22900
|
|
|
|
|
49701
|
|
73
|
12283
|
|
|
|
|
59313
|
map { $q->[$_] = $y->[$_] + 0.5*$w1[$_] } @indices; |
|
22900
|
|
|
|
|
55109
|
|
74
|
|
|
|
|
|
|
|
75
|
12283
|
|
|
|
|
15095
|
map { $w2[$_] = $h * &{ $F->[$_] }($t + 0.5*$h,$q) } @indices; |
|
22900
|
|
|
|
|
63909
|
|
|
22900
|
|
|
|
|
53152
|
|
76
|
12283
|
|
|
|
|
57953
|
map { $q->[$_] = $y->[$_] + 0.5*$w2[$_] } @indices; |
|
22900
|
|
|
|
|
54091
|
|
77
|
|
|
|
|
|
|
|
78
|
12283
|
|
|
|
|
13657
|
map { $w3[$_] = $h * &{ $F->[$_] }($t + 0.5*$h,$q) } @indices; |
|
22900
|
|
|
|
|
60669
|
|
|
22900
|
|
|
|
|
47794
|
|
79
|
12283
|
|
|
|
|
54845
|
map { $q->[$_] = $y->[$_] + $w3[$_] } @indices; |
|
22900
|
|
|
|
|
43070
|
|
80
|
|
|
|
|
|
|
|
81
|
12283
|
|
|
|
|
13226
|
map { $w4[$_] = $h * &{ $F->[$_] }($t + $h,$q) } @indices; |
|
22900
|
|
|
|
|
60510
|
|
|
22900
|
|
|
|
|
46445
|
|
82
|
12283
|
|
|
|
|
77181
|
map { $y->[$_]+= ( $w1[$_] + 2 * $w2[$_] + 2 * $w3[$_] + $w4[$_])/6 } @indices; |
|
22900
|
|
|
|
|
60379
|
|
83
|
|
|
|
|
|
|
|
84
|
12283
|
|
|
|
|
26046
|
$self->_store_values( $t + $h, $y ); |
85
|
|
|
|
|
|
|
|
86
|
12283
|
|
|
|
|
39877
|
return $y; |
87
|
|
|
|
|
|
|
} |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
sub _store_values |
90
|
|
|
|
|
|
|
{ |
91
|
12283
|
|
|
12283
|
|
15377
|
my ($self,$t, $y) = @_; |
92
|
12283
|
100
|
|
|
|
28732
|
return unless $self->{keep_values}; |
93
|
12261
|
|
|
|
|
46669
|
my $s = sprintf '%0.12f', $t ; |
94
|
12261
|
|
|
|
|
12613
|
push @{ $self->{values}{$s} }, @$y; |
|
12261
|
|
|
|
|
57222
|
|
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
sub values_at |
98
|
|
|
|
|
|
|
{ |
99
|
12230
|
|
|
12230
|
0
|
18242
|
my ($self,$t, %args) = @_; |
100
|
12230
|
100
|
|
|
|
22333
|
if ($self->{keep_values}){ |
101
|
12229
|
|
|
|
|
11572
|
return @{ $self->{values}{sprintf('%0.12f',$t)} }; |
|
12229
|
|
|
|
|
76312
|
|
102
|
|
|
|
|
|
|
} else { |
103
|
1
|
|
|
|
|
151
|
warn "Values were not kept because keep_values was set to 0"; |
104
|
1
|
|
|
|
|
6
|
return; |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
} |
107
|
|
|
|
|
|
|
# because Math::ODE implements a 4th order Runge-Kutta method |
108
|
73
|
|
|
73
|
0
|
923
|
sub error { $_[0]->{step} ** 4 } |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
sub step |
111
|
|
|
|
|
|
|
{ |
112
|
0
|
|
|
0
|
0
|
0
|
my ($self,$step) = @_; |
113
|
0
|
0
|
|
|
|
0
|
if (defined $step){ |
114
|
0
|
0
|
0
|
|
|
0
|
croak "Stepsize must be strictly between zero and one" |
115
|
|
|
|
|
|
|
if ($step <= 0 || $step >= 1); |
116
|
|
|
|
|
|
|
|
117
|
0
|
|
|
|
|
0
|
$self->{step} = $step; |
118
|
|
|
|
|
|
|
} |
119
|
0
|
|
|
|
|
0
|
return $self->{step}; |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
sub initial |
123
|
|
|
|
|
|
|
{ |
124
|
0
|
|
|
0
|
0
|
0
|
my ($self, $initial) = @_; |
125
|
|
|
|
|
|
|
|
126
|
0
|
0
|
|
|
|
0
|
croak "not an array ref" unless ref $initial eq "ARRAY"; |
127
|
|
|
|
|
|
|
|
128
|
0
|
|
|
|
|
0
|
$self->{initial} = $initial; |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
sub _clear_values |
133
|
|
|
|
|
|
|
{ |
134
|
82
|
|
|
82
|
|
107
|
my $self = shift; |
135
|
82
|
|
|
|
|
177
|
$self->{values} = {} |
136
|
|
|
|
|
|
|
} |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
sub _init |
139
|
|
|
|
|
|
|
{ |
140
|
41
|
|
|
41
|
|
207
|
my ($self,%args) = @_; |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
# defaults |
143
|
41
|
|
|
|
|
114
|
$self->{keep_values} = 1; |
144
|
41
|
|
|
|
|
81
|
$self->{verbose} = 1; |
145
|
41
|
|
|
|
|
72
|
$self->{step} = 0.1; |
146
|
41
|
|
|
|
|
81
|
$self->{csv} = 0; |
147
|
41
|
|
50
|
|
|
52
|
$self->{N} = scalar( @{ $args{ODE} } ) || 1; |
148
|
|
|
|
|
|
|
|
149
|
41
|
|
|
|
|
257
|
@$self{keys %args} = values %args; |
150
|
|
|
|
|
|
|
|
151
|
41
|
|
|
|
|
147
|
$self->_clear_values; |
152
|
|
|
|
|
|
|
|
153
|
41
|
50
|
|
|
|
66
|
if( $self->{N} != scalar( @{ $args{initial } }) ){ |
|
41
|
|
|
|
|
133
|
|
154
|
0
|
|
|
|
|
0
|
croak "Must have same number of initial conditions as equations!"; |
155
|
|
|
|
|
|
|
} |
156
|
|
|
|
|
|
|
|
157
|
41
|
50
|
|
|
|
190
|
croak "Stepsize must be positive!" if $self->{step} <= 0; |
158
|
41
|
50
|
|
|
|
135
|
croak "\$self->t0 must be less than \$self->tf!" if $self->{t0} >= $self->{tf}; |
159
|
|
|
|
|
|
|
|
160
|
41
|
|
|
|
|
151
|
return $self; |
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
sub max_error |
164
|
|
|
|
|
|
|
{ |
165
|
36
|
|
|
36
|
0
|
274
|
my ($self, $sols) = @_; |
166
|
|
|
|
|
|
|
|
167
|
36
|
|
|
|
|
43
|
my $max_error = 0; |
168
|
|
|
|
|
|
|
|
169
|
36
|
|
|
|
|
40
|
for my $pt ( sort keys %{$self->{values}} ){ |
|
36
|
|
|
|
|
9158
|
|
170
|
12228
|
|
|
|
|
13659
|
my $k = 0; |
171
|
12228
|
|
|
|
|
21371
|
my @vals = $self->values_at($pt); |
172
|
|
|
|
|
|
|
|
173
|
12228
|
|
|
|
|
19180
|
for my $sol ( @$sols ) { |
174
|
15767
|
|
|
|
|
34818
|
my $res = abs( $vals[$k] - &$sol($pt) ); |
175
|
15767
|
100
|
|
|
|
83546
|
$max_error = $res if ($res > $max_error); |
176
|
15767
|
|
|
|
|
35639
|
$k++; |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
} |
179
|
36
|
|
|
|
|
1355
|
$max_error; |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
sub new { |
183
|
41
|
|
|
41
|
0
|
27304
|
my $class = shift; |
184
|
41
|
|
|
|
|
77
|
my $self = {}; |
185
|
41
|
|
|
|
|
117
|
bless $self, $class; |
186
|
41
|
|
|
|
|
135
|
$self->_init(@_); |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
42; |
190
|
|
|
|
|
|
|
__END__ |