line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# Math::RungeKutta.pm |
2
|
|
|
|
|
|
|
######################################################################### |
3
|
|
|
|
|
|
|
# This Perl module is Copyright (c) 2002, Peter J Billam # |
4
|
|
|
|
|
|
|
# c/o P J B Computing, www.pjb.com.au # |
5
|
|
|
|
|
|
|
# # |
6
|
|
|
|
|
|
|
# This module is free software; you can redistribute it and/or # |
7
|
|
|
|
|
|
|
# modify it under the same terms as Perl itself. # |
8
|
|
|
|
|
|
|
######################################################################### |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
package Math::RungeKutta; |
11
|
1
|
|
|
1
|
|
656
|
no strict; no warnings; |
|
1
|
|
|
1
|
|
2
|
|
|
1
|
|
|
|
|
37
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
335
|
|
12
|
|
|
|
|
|
|
$VERSION = '1.07'; |
13
|
|
|
|
|
|
|
# gives a -w warning, but I'm afraid $VERSION .= ''; would confuse CPAN |
14
|
|
|
|
|
|
|
require Exporter; |
15
|
|
|
|
|
|
|
@ISA = qw(Exporter); |
16
|
|
|
|
|
|
|
@EXPORT = qw(rk2 rk4 rk4_auto rk4_auto_midpoint); |
17
|
|
|
|
|
|
|
@EXPORT_OK = qw(rk4_classical rk4_ralston arr2txt); |
18
|
|
|
|
|
|
|
%EXPORT_TAGS = (ALL => [@EXPORT,@EXPORT_OK]); |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
sub new { |
21
|
0
|
|
|
0
|
0
|
0
|
my $arg1 = shift; |
22
|
0
|
|
0
|
|
|
0
|
my $class = ref($arg1) || $arg1; # can be used as class or instance method |
23
|
0
|
|
|
|
|
0
|
my $self = {}; # ref to an empty hash |
24
|
0
|
|
|
|
|
0
|
bless $self, $class; |
25
|
0
|
|
|
|
|
0
|
$self->_initialise(); |
26
|
0
|
|
|
|
|
0
|
return $self; |
27
|
|
|
|
|
|
|
} |
28
|
|
|
|
|
|
|
|
29
|
32
|
|
|
32
|
1
|
660
|
sub rk2 { my ($ynref, $dydtref, $t, $dt) = @_; |
30
|
32
|
50
|
|
|
|
80
|
if (ref $dydtref ne 'CODE') { |
31
|
0
|
|
|
|
|
0
|
warn "Math::RungeKutta::rk2: 2nd arg must be a subroutine ref\n"; |
32
|
0
|
|
|
|
|
0
|
return (); |
33
|
|
|
|
|
|
|
} |
34
|
|
|
|
|
|
|
|
35
|
32
|
|
|
|
|
34
|
my $gamma = .75; # Ralston's minimisation of error bounds |
36
|
32
|
|
|
|
|
41
|
my $alpha = 0.5/$gamma; my $beta = 1.0-$gamma; |
|
32
|
|
|
|
|
37
|
|
37
|
32
|
|
|
|
|
40
|
my $alphadt=$alpha*$dt; my $betadt=$beta*$dt; my $gammadt=$gamma*$dt; |
|
32
|
|
|
|
|
34
|
|
|
32
|
|
|
|
|
34
|
|
38
|
32
|
100
|
|
|
|
79
|
if (ref $ynref eq 'ARRAY') { |
|
|
50
|
|
|
|
|
|
39
|
16
|
|
|
|
|
27
|
my $ny = $#$ynref; |
40
|
16
|
|
|
|
|
19
|
my @ynp1; $#ynp1 = $ny; |
|
16
|
|
|
|
|
34
|
|
41
|
16
|
|
|
|
|
17
|
my @dydtn; $#dydtn = $ny; |
|
16
|
|
|
|
|
28
|
|
42
|
16
|
|
|
|
|
18
|
my @ynpalpha; $#ynpalpha = $ny; # Gear calls this q |
|
16
|
|
|
|
|
23
|
|
43
|
16
|
|
|
|
|
17
|
my @dydtnpalpha; $#dydtnpalpha = $ny; |
|
16
|
|
|
|
|
30
|
|
44
|
16
|
|
|
|
|
22
|
@dydtn = &{$dydtref}($t, @$ynref); |
|
16
|
|
|
|
|
38
|
|
45
|
1
|
|
|
1
|
|
881
|
my $i; for ($i=$[; $i<=$ny; $i++) { |
|
1
|
|
|
|
|
409
|
|
|
1
|
|
|
|
|
5270
|
|
|
16
|
|
|
|
|
222
|
|
|
16
|
|
|
|
|
63
|
|
46
|
32
|
|
|
|
|
32
|
$ynpalpha[$i] = ${$ynref}[$i] + $alphadt*$dydtn[$i]; |
|
32
|
|
|
|
|
145
|
|
47
|
|
|
|
|
|
|
} |
48
|
16
|
|
|
|
|
27
|
@dydtnpalpha = &{$dydtref}($t+$alphadt, @ynpalpha); |
|
16
|
|
|
|
|
43
|
|
49
|
16
|
|
|
|
|
243
|
for ($i=$[; $i<=$ny; $i++) { |
50
|
32
|
|
|
|
|
115
|
$ynp1[$i] |
51
|
32
|
|
|
|
|
35
|
= ${$ynref}[$i]+$betadt*$dydtn[$i]+$gammadt*$dydtnpalpha[$i]; |
52
|
|
|
|
|
|
|
} |
53
|
16
|
|
|
|
|
107
|
return ($t+$dt, @ynp1); |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
} elsif (ref $ynref eq 'HASH') { |
56
|
16
|
|
|
|
|
17
|
my %ynp1; |
57
|
|
|
|
|
|
|
my %ynpalpha; # Gear calls this q |
58
|
16
|
|
|
|
|
28
|
my %dydtn = &{$dydtref}($t, %$ynref); |
|
16
|
|
|
|
|
36
|
|
59
|
16
|
|
|
|
|
168
|
foreach my $i (keys %$ynref) { |
60
|
32
|
|
|
|
|
29
|
$ynpalpha{$i} = ${$ynref}{$i} + $alphadt*$dydtn{$i}; |
|
32
|
|
|
|
|
90
|
|
61
|
|
|
|
|
|
|
} |
62
|
16
|
|
|
|
|
38
|
my %dydtnpalpha = &{$dydtref}($t+$alphadt, %ynpalpha); |
|
16
|
|
|
|
|
35
|
|
63
|
16
|
|
|
|
|
164
|
foreach my $i (keys %$ynref) { |
64
|
32
|
|
|
|
|
94
|
$ynp1{$i} |
65
|
32
|
|
|
|
|
31
|
= ${$ynref}{$i}+$betadt*$dydtn{$i}+$gammadt*$dydtnpalpha{$i}; |
66
|
|
|
|
|
|
|
} |
67
|
16
|
|
|
|
|
134
|
return ($t+$dt, %ynp1); |
68
|
|
|
|
|
|
|
} else { |
69
|
0
|
|
|
|
|
0
|
warn "Math::RungeKutta::rk2: 1st arg must be an arrayref or hashref\n"; |
70
|
0
|
|
|
|
|
0
|
return (); |
71
|
|
|
|
|
|
|
} |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
my @saved_k0; my %saved_k0; my $use_saved_k0 = 0; |
74
|
234
|
|
|
234
|
1
|
1346
|
sub rk4 { my ($ynref, $dydtref, $t, $dt) = @_; |
75
|
|
|
|
|
|
|
# The Runge-Kutta-Merson 5-function-evaluation 4th-order method |
76
|
|
|
|
|
|
|
# in the sine-cosine example, this seems to work as a 7th-order method ! |
77
|
234
|
50
|
|
|
|
608
|
if (ref $dydtref ne 'CODE') { |
78
|
0
|
|
|
|
|
0
|
warn "Math::RungeKutta::rk4: 2nd arg must be a subroutine ref\n"; |
79
|
0
|
|
|
|
|
0
|
return (); |
80
|
|
|
|
|
|
|
} |
81
|
234
|
100
|
|
|
|
521
|
if (ref $ynref eq 'ARRAY') { |
|
|
50
|
|
|
|
|
|
82
|
117
|
|
|
|
|
189
|
my $ny = $#$ynref; my $i; |
|
117
|
|
|
|
|
136
|
|
83
|
|
|
|
|
|
|
# @eta0 = @#ynref; |
84
|
117
|
|
|
|
|
518
|
my @k0; $#k0=$ny; |
85
|
117
|
100
|
|
|
|
199
|
if ($use_saved_k0) { @k0 = @saved_k0; |
|
66
|
|
|
|
|
159
|
|
86
|
51
|
|
|
|
|
71
|
} else { @k0 = &{$dydtref}($t, @$ynref); |
|
51
|
|
|
|
|
128
|
|
87
|
|
|
|
|
|
|
} |
88
|
117
|
|
|
|
|
1028
|
for ($i=$[; $i<=$ny; $i++) { $k0[$i] *= $dt; } |
|
234
|
|
|
|
|
589
|
|
89
|
117
|
|
|
|
|
113
|
my @eta1; $#eta1=$ny; |
|
117
|
|
|
|
|
215
|
|
90
|
117
|
|
|
|
|
383
|
for ($i=$[; $i<=$ny; $i++) { $eta1[$i] = ${$ynref}[$i] + $k0[$i]/3.0; } |
|
234
|
|
|
|
|
223
|
|
|
234
|
|
|
|
|
815
|
|
91
|
117
|
|
|
|
|
120
|
my @k1; $#k1=$ny; |
|
117
|
|
|
|
|
255
|
|
92
|
117
|
|
|
|
|
175
|
@k1 = &{$dydtref}($t + $dt/3.0, @eta1); |
|
117
|
|
|
|
|
294
|
|
93
|
117
|
|
|
|
|
1826
|
for ($i=$[; $i<=$ny; $i++) { $k1[$i] *= $dt; } |
|
234
|
|
|
|
|
502
|
|
94
|
117
|
|
|
|
|
120
|
my @eta2; $#eta2=$ny; |
|
117
|
|
|
|
|
285
|
|
95
|
117
|
|
|
|
|
119
|
my @k2; $#k2=$ny; |
|
117
|
|
|
|
|
196
|
|
96
|
117
|
|
|
|
|
391
|
for ($i=$[; $i<=$ny; $i++) { |
97
|
234
|
|
|
|
|
228
|
$eta2[$i] = ${$ynref}[$i] + ($k0[$i]+$k1[$i])/6.0; |
|
234
|
|
|
|
|
834
|
|
98
|
|
|
|
|
|
|
} |
99
|
117
|
|
|
|
|
189
|
@k2 = &{$dydtref}($t + $dt/3.0, @eta2); |
|
117
|
|
|
|
|
389
|
|
100
|
117
|
|
|
|
|
1698
|
for ($i=$[; $i<=$ny; $i++) { $k2[$i] *= $dt; } |
|
234
|
|
|
|
|
503
|
|
101
|
117
|
|
|
|
|
114
|
my @eta3; $#eta3=$ny; |
|
117
|
|
|
|
|
237
|
|
102
|
117
|
|
|
|
|
385
|
for ($i=$[; $i<=$ny; $i++) { |
103
|
234
|
|
|
|
|
225
|
$eta3[$i] = ${$ynref}[$i] + ($k0[$i]+3.0*$k2[$i])*0.125; |
|
234
|
|
|
|
|
825
|
|
104
|
|
|
|
|
|
|
} |
105
|
117
|
|
|
|
|
120
|
my @k3; $#k3=$ny; |
|
117
|
|
|
|
|
212
|
|
106
|
117
|
|
|
|
|
174
|
@k3 = &{$dydtref}($t+0.5*$dt, @eta3); |
|
117
|
|
|
|
|
279
|
|
107
|
117
|
|
|
|
|
1716
|
for ($i=$[; $i<=$ny; $i++) { $k3[$i] *= $dt; } |
|
234
|
|
|
|
|
526
|
|
108
|
117
|
|
|
|
|
106
|
my @eta4; $#eta4=$ny; |
|
117
|
|
|
|
|
225
|
|
109
|
117
|
|
|
|
|
391
|
for ($i=$[; $i<=$ny; $i++) { |
110
|
234
|
|
|
|
|
213
|
$eta4[$i] = ${$ynref}[$i] + ($k0[$i]-3.0*$k2[$i]+4.0*$k3[$i])*0.5; |
|
234
|
|
|
|
|
988
|
|
111
|
|
|
|
|
|
|
} |
112
|
117
|
|
|
|
|
116
|
my @k4; $#k4=$ny; |
|
117
|
|
|
|
|
203
|
|
113
|
117
|
|
|
|
|
170
|
@k4 = &{$dydtref}($t+$dt, @eta4); |
|
117
|
|
|
|
|
360
|
|
114
|
117
|
|
|
|
|
1839
|
for ($i=$[; $i<=$ny; $i++) { $k4[$i] *= $dt; } |
|
234
|
|
|
|
|
493
|
|
115
|
117
|
|
|
|
|
102
|
my @ynp1; $#ynp1 = $ny; |
|
117
|
|
|
|
|
11538
|
|
116
|
117
|
|
|
|
|
386
|
for ($i=$[; $i<=$ny; $i++) { |
117
|
234
|
|
|
|
|
223
|
$ynp1[$i] = ${$ynref}[$i] + ($k0[$i]+4.0*$k3[$i]+$k4[$i])/6.0; |
|
234
|
|
|
|
|
904
|
|
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
# Merson's method for error estimation, see Gear p85, only works |
120
|
|
|
|
|
|
|
# if F is linear, ie F = Ay + bt, so that eta4 has no 4th-order |
121
|
|
|
|
|
|
|
# errors. So in general step-doubling is the only way to do it. |
122
|
|
|
|
|
|
|
# Estimate error terms ... |
123
|
|
|
|
|
|
|
# if ($epsilon) { |
124
|
|
|
|
|
|
|
# my $errmax = 0; my $diff; |
125
|
|
|
|
|
|
|
# for ($i=$[; $i<=$ny; $i++) { |
126
|
|
|
|
|
|
|
# $diff = 0.2 * abs ($ynp1[$i] - $eta4[$i]); |
127
|
|
|
|
|
|
|
# if ($errmax < $diff) { $errmax = $diff; } |
128
|
|
|
|
|
|
|
# } |
129
|
|
|
|
|
|
|
# # print "errmax = $errmax\n"; # not much related to the actual error |
130
|
|
|
|
|
|
|
# } |
131
|
117
|
|
|
|
|
988
|
return ($t+$dt, @ynp1); |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
} elsif (ref $ynref eq 'HASH') { |
134
|
117
|
|
|
|
|
115
|
my %k0; |
135
|
117
|
100
|
|
|
|
171
|
if ($use_saved_k0) { %k0 = %saved_k0; |
|
66
|
|
|
|
|
188
|
|
136
|
51
|
|
|
|
|
100
|
} else { %k0 = &{$dydtref}($t, %$ynref); |
|
51
|
|
|
|
|
672
|
|
137
|
|
|
|
|
|
|
} |
138
|
117
|
|
|
|
|
726
|
foreach my $i (keys(%$ynref)) { $k0{$i} *= $dt; } |
|
234
|
|
|
|
|
393
|
|
139
|
117
|
|
|
|
|
202
|
my %eta1; |
140
|
117
|
|
|
|
|
203
|
foreach my $i (keys(%$ynref)) { $eta1{$i} = ${$ynref}{$i}+$k0{$i}/3.0; } |
|
234
|
|
|
|
|
226
|
|
|
234
|
|
|
|
|
739
|
|
141
|
117
|
|
|
|
|
327
|
my %k1 = &{$dydtref}($t + $dt/3.0, %eta1); |
|
117
|
|
|
|
|
362
|
|
142
|
117
|
|
|
|
|
1409
|
foreach my $i (keys(%$ynref)) { $k1{$i} *= $dt; } |
|
234
|
|
|
|
|
378
|
|
143
|
117
|
|
|
|
|
153
|
my %eta2; |
144
|
117
|
|
|
|
|
205
|
foreach my $i (keys(%$ynref)) { |
145
|
234
|
|
|
|
|
238
|
$eta2{$i} = ${$ynref}{$i} + ($k0{$i}+$k1{$i})/6.0; |
|
234
|
|
|
|
|
705
|
|
146
|
|
|
|
|
|
|
} |
147
|
117
|
|
|
|
|
252
|
my %k2 = &{$dydtref}($t + $dt/3.0, %eta2); |
|
117
|
|
|
|
|
347
|
|
148
|
117
|
|
|
|
|
1327
|
foreach my $i (keys(%$ynref)) { $k2{$i} *= $dt; } |
|
234
|
|
|
|
|
364
|
|
149
|
117
|
|
|
|
|
168
|
my %eta3; |
150
|
117
|
|
|
|
|
201
|
foreach my $i (keys(%$ynref)) { |
151
|
234
|
|
|
|
|
373
|
$eta3{$i} = ${$ynref}{$i} + ($k0{$i}+3.0*$k2{$i})*0.125; |
|
234
|
|
|
|
|
981
|
|
152
|
|
|
|
|
|
|
} |
153
|
117
|
|
|
|
|
269
|
my %k3 = &{$dydtref}($t+0.5*$dt, %eta3); |
|
117
|
|
|
|
|
347
|
|
154
|
117
|
|
|
|
|
1215
|
foreach my $i (keys(%$ynref)) { $k3{$i} *= $dt; } |
|
234
|
|
|
|
|
374
|
|
155
|
117
|
|
|
|
|
156
|
my %eta4; |
156
|
117
|
|
|
|
|
201
|
foreach my $i (keys(%$ynref)) { |
157
|
234
|
|
|
|
|
262
|
$eta4{$i} = ${$ynref}{$i} + ($k0{$i}-3.0*$k2{$i}+4.0*$k3{$i})*0.5; |
|
234
|
|
|
|
|
749
|
|
158
|
|
|
|
|
|
|
} |
159
|
117
|
|
|
|
|
294
|
my %k4 = &{$dydtref}($t+$dt, %eta4); |
|
117
|
|
|
|
|
275
|
|
160
|
117
|
|
|
|
|
1278
|
foreach my $i (keys(%$ynref)) { $k4{$i} *= $dt; } |
|
234
|
|
|
|
|
372
|
|
161
|
117
|
|
|
|
|
164
|
my %ynp1; |
162
|
117
|
|
|
|
|
195
|
foreach my $i (keys(%$ynref)) { |
163
|
234
|
|
|
|
|
321
|
$ynp1{$i} = ${$ynref}{$i} + ($k0{$i}+4.0*$k3{$i}+$k4{$i})/6.0; |
|
234
|
|
|
|
|
739
|
|
164
|
|
|
|
|
|
|
} |
165
|
117
|
|
|
|
|
1082
|
return ($t+$dt, %ynp1); |
166
|
|
|
|
|
|
|
} else { |
167
|
0
|
|
|
|
|
0
|
warn "Math::RungeKutta::rk4: 1st arg must be an arrayref or hashref\n"; |
168
|
0
|
|
|
|
|
0
|
return (); |
169
|
|
|
|
|
|
|
} |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
my $t; my $halfdt; |
172
|
|
|
|
|
|
|
my @y2; my %y2; # need to be remembered for the midpoint |
173
|
38
|
|
|
38
|
1
|
2265
|
sub rk4_auto { my $ynref=shift; my $dydtref=shift; $t=shift; |
|
38
|
|
|
|
|
123
|
|
|
38
|
|
|
|
|
45
|
|
174
|
38
|
|
|
|
|
53
|
my ($dt, $arg4) = @_; |
175
|
38
|
50
|
|
|
|
95
|
if (ref $dydtref ne 'CODE') { |
176
|
0
|
|
|
|
|
0
|
warn "Math::RungeKutta::rk4_auto: 2nd arg must be a subroutine ref\n"; |
177
|
0
|
|
|
|
|
0
|
return (); |
178
|
|
|
|
|
|
|
} |
179
|
38
|
50
|
|
|
|
71
|
if ($dt == 0) { $dt = 0.1; } |
|
0
|
|
|
|
|
0
|
|
180
|
38
|
100
|
|
|
|
95
|
if (ref $ynref eq 'ARRAY') { |
|
|
50
|
|
|
|
|
|
181
|
19
|
|
|
|
|
24
|
my @errors; my $epsilon; |
182
|
19
|
100
|
|
|
|
37
|
if (ref $arg4 eq 'ARRAY') { |
183
|
9
|
|
|
|
|
21
|
@errors = @$arg4; undef $epsilon; |
|
9
|
|
|
|
|
34
|
|
184
|
|
|
|
|
|
|
} else { |
185
|
10
|
|
|
|
|
14
|
$epsilon = abs $arg4; undef @errors; |
|
10
|
|
|
|
|
13
|
|
186
|
10
|
50
|
|
|
|
21
|
if (! $epsilon) { $epsilon = .0000001; } |
|
0
|
|
|
|
|
0
|
|
187
|
|
|
|
|
|
|
} |
188
|
19
|
|
|
|
|
31
|
my $ny = $#$ynref; my $i; |
|
19
|
|
|
|
|
23
|
|
189
|
19
|
|
|
|
|
129
|
my @y1; $#y1 = ny; |
190
|
19
|
|
|
|
|
44
|
$#y2 = ny; %y2 = (); |
|
19
|
|
|
|
|
34
|
|
191
|
19
|
|
|
|
|
19
|
my @y3; $#y3 = ny; |
|
19
|
|
|
|
|
47
|
|
192
|
19
|
|
|
|
|
50
|
$#saved_k0 = ny; @saved_k0 = &{$dydtref}($t, @$ynref); |
|
19
|
|
|
|
|
28
|
|
|
19
|
|
|
|
|
52
|
|
193
|
19
|
|
|
|
|
257
|
my $resizings = 0; |
194
|
19
|
|
|
|
|
23
|
my $highest_low_error = 0.1E-99; my $highest_low_dt = 0.0; |
|
19
|
|
|
|
|
22
|
|
195
|
19
|
|
|
|
|
18
|
my $lowest_high_error = 9.9E99; my $lowest_high_dt = 9.9E99; |
|
19
|
|
|
|
|
20
|
|
196
|
19
|
|
|
|
|
23
|
while (1) { |
197
|
33
|
|
|
|
|
40
|
$halfdt = 0.5 * $dt; my $dummy; |
|
33
|
|
|
|
|
31
|
|
198
|
33
|
|
|
|
|
33
|
$use_saved_k0 = 1; |
199
|
33
|
|
|
|
|
63
|
($dummy, @y1) = &rk4($ynref, $dydtref, $t, $dt); |
200
|
33
|
|
|
|
|
80
|
($dummy, @y2) = &rk4($ynref, $dydtref, $t, $halfdt); |
201
|
33
|
|
|
|
|
46
|
$use_saved_k0 = 0; |
202
|
33
|
|
|
|
|
83
|
($dummy, @y3) = &rk4(\@y2, $dydtref, $t+$halfdt, $halfdt); |
203
|
33
|
|
|
|
|
51
|
my $relative_error; |
204
|
33
|
100
|
|
|
|
80
|
if ($epsilon) { |
|
|
50
|
|
|
|
|
|
205
|
13
|
|
|
|
|
15
|
my $errmax = 0; my $diff; my $ymax = 0; |
|
13
|
|
|
|
|
13
|
|
|
13
|
|
|
|
|
16
|
|
206
|
13
|
|
|
|
|
46
|
for ($i=$[; $i<=$ny; $i++) { |
207
|
26
|
|
|
|
|
47
|
$diff = abs ($y1[$i]-$y3[$i]); |
208
|
26
|
100
|
|
|
|
54
|
if ($errmax < $diff) { $errmax = $diff; } |
|
18
|
|
|
|
|
23
|
|
209
|
26
|
100
|
|
|
|
26
|
if ($ymax < abs ${$ynref}[$i]) {$ymax = abs ${$ynref}[$i];} |
|
26
|
|
|
|
|
74
|
|
|
17
|
|
|
|
|
13
|
|
|
17
|
|
|
|
|
48
|
|
210
|
|
|
|
|
|
|
} |
211
|
13
|
|
|
|
|
61
|
$relative_error = $errmax/($epsilon*$ymax); |
212
|
|
|
|
|
|
|
} elsif (@errors) { |
213
|
20
|
|
|
|
|
32
|
$relative_error = 0.0; my $diff; |
|
20
|
|
|
|
|
19
|
|
214
|
20
|
|
|
|
|
75
|
for ($i=$[; $i<=$ny; $i++) { |
215
|
40
|
|
|
|
|
72
|
$diff = abs ($y1[$i]-$y3[$i]) / abs $errors[$i]; |
216
|
40
|
100
|
|
|
|
84
|
if ($relative_error < $diff) { $relative_error = $diff; } |
|
39
|
|
|
|
|
92
|
|
217
|
|
|
|
|
|
|
} |
218
|
0
|
|
|
|
|
0
|
} else { die |
219
|
|
|
|
|
|
|
"RungeKutta::rk4_auto: \$epsilon & \@errors both undefined\n"; |
220
|
|
|
|
|
|
|
} |
221
|
|
|
|
|
|
|
# Gear's correction assumes error is always in 5th-order terms :-( |
222
|
|
|
|
|
|
|
# $y1[$i] = (16.0*$y3{$i] - $y1[$i]) / 15.0; |
223
|
33
|
100
|
|
|
|
96
|
if ($relative_error < 0.60) { |
|
|
100
|
|
|
|
|
|
224
|
9
|
50
|
|
|
|
21
|
if ($dt > $highest_low_dt) { |
225
|
9
|
|
|
|
|
10
|
$highest_low_error = $relative_error; |
226
|
9
|
|
|
|
|
11
|
$highest_low_dt = $dt; |
227
|
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
} elsif ($relative_error > 1.67) { |
229
|
5
|
50
|
|
|
|
97
|
if ($dt < $lowest_high_dt) { |
230
|
5
|
|
|
|
|
9
|
$lowest_high_error = $relative_error; |
231
|
5
|
|
|
|
|
7
|
$lowest_high_dt = $dt; |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
} else { |
234
|
19
|
|
|
|
|
34
|
last; |
235
|
|
|
|
|
|
|
} |
236
|
14
|
100
|
100
|
|
|
58
|
if ($lowest_high_dt<9.8E99 && $highest_low_dt>1.0E-99) { # interp |
237
|
3
|
|
|
|
|
10
|
my $denom = log ($lowest_high_error/$highest_low_error); |
238
|
3
|
50
|
33
|
|
|
28
|
if ($highest_low_dt==0.0||$highest_low_error==0.0||$denom==0.0){ |
|
|
|
33
|
|
|
|
|
239
|
0
|
|
|
|
|
0
|
$dt = 0.5 * ($highest_low_dt+$lowest_high_dt); |
240
|
|
|
|
|
|
|
} else { |
241
|
3
|
|
|
|
|
27
|
$dt = $highest_low_dt * ( ($lowest_high_dt/$highest_low_dt) |
242
|
|
|
|
|
|
|
** ((log (1.0/$highest_low_error)) / $denom) ); |
243
|
|
|
|
|
|
|
} |
244
|
|
|
|
|
|
|
} else { |
245
|
11
|
|
|
|
|
70
|
my $adjust = $relative_error**(-0.2); # hope error is 5th-order ... |
246
|
11
|
100
|
|
|
|
24
|
if (abs $adjust > 2.0) { |
247
|
4
|
|
|
|
|
8
|
$dt *= 2.0; # prevent infinity if 4th-order is exact ... |
248
|
|
|
|
|
|
|
} else { |
249
|
7
|
|
|
|
|
12
|
$dt *= $adjust; |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
} |
252
|
14
|
|
|
|
|
13
|
$resizings++; |
253
|
14
|
50
|
33
|
|
|
319
|
if ($resizings>4 && $highest_low_dt>1.0E-99) { |
254
|
|
|
|
|
|
|
# hope a small step forward gets us out of this mess ... |
255
|
0
|
|
|
|
|
0
|
$dt = $highest_low_dt; $halfdt = 0.5 * $dt; |
|
0
|
|
|
|
|
0
|
|
256
|
0
|
|
|
|
|
0
|
$use_saved_k0 = 1; |
257
|
0
|
|
|
|
|
0
|
($dummy, @y2) = &rk4($ynref, $dydtref, $t, $halfdt); |
258
|
0
|
|
|
|
|
0
|
$use_saved_k0 = 0; |
259
|
0
|
|
|
|
|
0
|
($dummy, @y3) = &rk4(\@y2, $dydtref, $t+$halfdt, $halfdt); |
260
|
0
|
|
|
|
|
0
|
last; |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
} |
263
|
19
|
|
|
|
|
121
|
return ($t+$dt, $dt, @y3); |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
} elsif (ref $ynref eq 'HASH') { |
266
|
19
|
|
|
|
|
19
|
my %errors; my $epsilon; |
267
|
19
|
100
|
|
|
|
38
|
if (ref $arg4 eq 'HASH') { |
268
|
9
|
|
|
|
|
24
|
%errors = %$arg4; undef $epsilon; |
|
9
|
|
|
|
|
16
|
|
269
|
|
|
|
|
|
|
} else { |
270
|
10
|
|
|
|
|
11
|
$epsilon = abs $arg4; undef %errors; |
|
10
|
|
|
|
|
13
|
|
271
|
10
|
50
|
|
|
|
22
|
if (! $epsilon) { $epsilon = .0000001; } |
|
0
|
|
|
|
|
0
|
|
272
|
|
|
|
|
|
|
} |
273
|
19
|
|
|
|
|
18
|
my $i; my %y1; @y2 = (); my %y3; |
|
19
|
|
|
|
|
91
|
|
|
19
|
|
|
|
|
22
|
|
274
|
19
|
|
|
|
|
34
|
%saved_k0 = &{$dydtref}($t, %$ynref); |
|
19
|
|
|
|
|
50
|
|
275
|
19
|
|
|
|
|
260
|
my $resizings = 0; |
276
|
19
|
|
|
|
|
22
|
my $highest_low_error = 0.1E-99; my $highest_low_dt = 0.0; |
|
19
|
|
|
|
|
23
|
|
277
|
19
|
|
|
|
|
20
|
my $lowest_high_error = 9.9E99; my $lowest_high_dt = 9.9E99; |
|
19
|
|
|
|
|
21
|
|
278
|
19
|
|
|
|
|
18
|
while (1) { |
279
|
33
|
|
|
|
|
37
|
$halfdt = 0.5 * $dt; my $dummy; |
|
33
|
|
|
|
|
36
|
|
280
|
33
|
|
|
|
|
35
|
$use_saved_k0 = 1; |
281
|
33
|
|
|
|
|
59
|
($dummy, %y1) = &rk4($ynref, $dydtref, $t, $dt); |
282
|
33
|
|
|
|
|
89
|
($dummy, %y2) = &rk4($ynref, $dydtref, $t, $halfdt); |
283
|
33
|
|
|
|
|
65
|
$use_saved_k0 = 0; |
284
|
33
|
|
|
|
|
85
|
($dummy, %y3) = &rk4(\%y2, $dydtref, $t+$halfdt, $halfdt); |
285
|
33
|
|
|
|
|
62
|
my $relative_error; |
286
|
33
|
100
|
|
|
|
105
|
if ($epsilon) { |
|
|
50
|
|
|
|
|
|
287
|
13
|
|
|
|
|
14
|
my $errmax = 0; my $diff; my $ymax = 0; |
|
13
|
|
|
|
|
14
|
|
|
13
|
|
|
|
|
15
|
|
288
|
13
|
|
|
|
|
30
|
foreach $i (keys(%$ynref)) { |
289
|
26
|
|
|
|
|
48
|
$diff = abs ($y1{$i}-$y3{$i}); |
290
|
26
|
100
|
|
|
|
60
|
if ($errmax < $diff) { $errmax = $diff; } |
|
20
|
|
|
|
|
22
|
|
291
|
26
|
100
|
|
|
|
27
|
if ($ymax < abs ${$ynref}{$i}) {$ymax = abs ${$ynref}{$i};} |
|
26
|
|
|
|
|
82
|
|
|
15
|
|
|
|
|
15
|
|
|
15
|
|
|
|
|
31
|
|
292
|
|
|
|
|
|
|
} |
293
|
13
|
|
|
|
|
44
|
$relative_error = $errmax/($epsilon*$ymax); |
294
|
|
|
|
|
|
|
} elsif (%errors) { |
295
|
20
|
|
|
|
|
25
|
$relative_error = 0.0; my $diff; |
|
20
|
|
|
|
|
22
|
|
296
|
20
|
|
|
|
|
36
|
foreach $i (keys(%$ynref)) { |
297
|
40
|
|
|
|
|
106
|
$diff = abs ($y1{$i}-$y3{$i}) / abs $errors{$i}; |
298
|
40
|
100
|
|
|
|
152
|
if ($relative_error < $diff) { $relative_error = $diff; } |
|
31
|
|
|
|
|
61
|
|
299
|
|
|
|
|
|
|
} |
300
|
0
|
|
|
|
|
0
|
} else { die |
301
|
|
|
|
|
|
|
"RungeKutta::rk4_auto: \$epsilon & \%errors both undefined\n"; |
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
# Gear's correction assumes error is always in 5th-order terms :-( |
304
|
|
|
|
|
|
|
# $y1[$i] = (16.0*$y3{$i] - $y1[$i]) / 15.0; |
305
|
33
|
100
|
|
|
|
93
|
if ($relative_error < 0.60) { |
|
|
100
|
|
|
|
|
|
306
|
9
|
50
|
|
|
|
24
|
if ($dt > $highest_low_dt) { |
307
|
9
|
|
|
|
|
12
|
$highest_low_error = $relative_error; |
308
|
9
|
|
|
|
|
13
|
$highest_low_dt = $dt; |
309
|
|
|
|
|
|
|
} |
310
|
|
|
|
|
|
|
} elsif ($relative_error > 1.67) { |
311
|
5
|
50
|
|
|
|
22
|
if ($dt < $lowest_high_dt) { |
312
|
5
|
|
|
|
|
9
|
$lowest_high_error = $relative_error; |
313
|
5
|
|
|
|
|
6
|
$lowest_high_dt = $dt; |
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
} else { |
316
|
19
|
|
|
|
|
24
|
last; |
317
|
|
|
|
|
|
|
} |
318
|
14
|
100
|
100
|
|
|
52
|
if ($lowest_high_dt<9.8E99 && $highest_low_dt>1.0E-99) { # interp |
319
|
3
|
|
|
|
|
9
|
my $denom = log ($lowest_high_error/$highest_low_error); |
320
|
3
|
50
|
33
|
|
|
28
|
if ($highest_low_dt==0.0||$highest_low_error==0.0||$denom==0.0){ |
|
|
|
33
|
|
|
|
|
321
|
0
|
|
|
|
|
0
|
$dt = 0.5 * ($highest_low_dt+$lowest_high_dt); |
322
|
|
|
|
|
|
|
} else { |
323
|
3
|
|
|
|
|
12
|
$dt = $highest_low_dt * ( ($lowest_high_dt/$highest_low_dt) |
324
|
|
|
|
|
|
|
** ((log (1.0/$highest_low_error)) / $denom) ); |
325
|
|
|
|
|
|
|
} |
326
|
|
|
|
|
|
|
} else { |
327
|
11
|
|
|
|
|
29
|
my $adjust = $relative_error**(-0.2); # hope error is 5th-order ... |
328
|
11
|
100
|
|
|
|
23
|
if (abs $adjust > 2.0) { |
329
|
4
|
|
|
|
|
14
|
$dt *= 2.0; # prevent infinity if 4th-order is exact ... |
330
|
|
|
|
|
|
|
} else { |
331
|
7
|
|
|
|
|
11
|
$dt *= $adjust; |
332
|
|
|
|
|
|
|
} |
333
|
|
|
|
|
|
|
} |
334
|
14
|
|
|
|
|
15
|
$resizings++; |
335
|
14
|
50
|
33
|
|
|
35
|
if ($resizings>4 && $highest_low_dt>1.0E-99) { |
336
|
|
|
|
|
|
|
# hope a small step forward gets us out of this mess ... |
337
|
0
|
|
|
|
|
0
|
$dt = $highest_low_dt; $halfdt = 0.5 * $dt; |
|
0
|
|
|
|
|
0
|
|
338
|
0
|
|
|
|
|
0
|
$use_saved_k0 = 1; |
339
|
0
|
|
|
|
|
0
|
($dummy, %y2) = &rk4($ynref, $dydtref, $t, $halfdt); |
340
|
0
|
|
|
|
|
0
|
$use_saved_k0 = 0; |
341
|
0
|
|
|
|
|
0
|
($dummy, %y3) = &rk4(\%y2, $dydtref, $t+$halfdt, $halfdt); |
342
|
0
|
|
|
|
|
0
|
last; |
343
|
|
|
|
|
|
|
} |
344
|
|
|
|
|
|
|
} |
345
|
19
|
|
|
|
|
132
|
return ($t+$dt, $dt, %y3); |
346
|
|
|
|
|
|
|
|
347
|
0
|
|
|
|
|
0
|
} else { die |
348
|
|
|
|
|
|
|
"Math::RungeKutta::rk4_auto: 1st arg must be arrayref or hashref\n"; |
349
|
|
|
|
|
|
|
# return (); |
350
|
|
|
|
|
|
|
} |
351
|
|
|
|
|
|
|
} |
352
|
|
|
|
|
|
|
sub rk4_auto_midpoint { |
353
|
38
|
100
|
|
38
|
1
|
169
|
if (@y2) { return ($t+$halfdt, @y2); } else { return ($t+$halfdt, %y2); } |
|
19
|
|
|
|
|
60
|
|
|
19
|
|
|
|
|
83
|
|
354
|
|
|
|
|
|
|
} |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
# ---------------------- EXPORT_OK routines ---------------------- |
357
|
|
|
|
|
|
|
|
358
|
32
|
|
|
32
|
1
|
972
|
sub rk4_ralston { my ($ynref, $dydtref, $t, $dt) = @_; |
359
|
32
|
50
|
|
|
|
82
|
if (ref $dydtref ne 'CODE') { |
360
|
0
|
|
|
|
|
0
|
warn "RungeKutta::rk4_ralston: 2nd arg must be a subroutine ref\n"; |
361
|
0
|
|
|
|
|
0
|
return (); |
362
|
|
|
|
|
|
|
} |
363
|
|
|
|
|
|
|
# Ralston's minimisation of error bounds, see Gear p36 |
364
|
32
|
100
|
|
|
|
81
|
if (ref $ynref eq 'ARRAY') { |
|
|
50
|
|
|
|
|
|
365
|
16
|
|
|
|
|
22
|
my $ny = $#$ynref; my $i; |
|
16
|
|
|
|
|
20
|
|
366
|
16
|
|
|
|
|
20
|
my $alpha1=0.4; my $alpha2 = 0.4557372542; # = .875 - .1875*(sqrt 5); |
|
16
|
|
|
|
|
17
|
|
367
|
16
|
|
|
|
|
17
|
my @k0; $#k0=$ny; |
|
16
|
|
|
|
|
34
|
|
368
|
16
|
|
|
|
|
24
|
@k0 = &{$dydtref}($t, @$ynref); |
|
16
|
|
|
|
|
53
|
|
369
|
16
|
|
|
|
|
249
|
for ($i=$[; $i<=$ny; $i++) { $k0[$i] *= $dt; } |
|
32
|
|
|
|
|
77
|
|
370
|
16
|
|
|
|
|
15
|
my @k1; $#k1=$ny; |
|
16
|
|
|
|
|
30
|
|
371
|
16
|
|
|
|
|
54
|
for ($i=$[; $i<=$ny; $i++) { $k1[$i] = ${$ynref}[$i] + 0.4*$k0[$i]; } |
|
32
|
|
|
|
|
33
|
|
|
32
|
|
|
|
|
113
|
|
372
|
16
|
|
|
|
|
27
|
@k1 = &{$dydtref}($t + $alpha1*$dt, @k1); |
|
16
|
|
|
|
|
39
|
|
373
|
16
|
|
|
|
|
230
|
for ($i=$[; $i<=$ny; $i++) { $k1[$i] *= $dt; } |
|
32
|
|
|
|
|
144
|
|
374
|
16
|
|
|
|
|
20
|
my @k2; $#k2=$ny; |
|
16
|
|
|
|
|
30
|
|
375
|
16
|
|
|
|
|
55
|
for ($i=$[; $i<=$ny; $i++) { |
376
|
32
|
|
|
|
|
33
|
$k2[$i] = ${$ynref}[$i] + 0.2969776*$k0[$i] + 0.15875966*$k1[$i]; |
|
32
|
|
|
|
|
115
|
|
377
|
|
|
|
|
|
|
} |
378
|
16
|
|
|
|
|
28
|
@k2 = &{$dydtref}($t + $alpha2*$dt, @k2); |
|
16
|
|
|
|
|
41
|
|
379
|
16
|
|
|
|
|
236
|
for ($i=$[; $i<=$ny; $i++) { $k2[$i] *= $dt; } |
|
32
|
|
|
|
|
71
|
|
380
|
16
|
|
|
|
|
17
|
my @k3; $#k3=$ny; |
|
16
|
|
|
|
|
33
|
|
381
|
16
|
|
|
|
|
55
|
for ($i=$[; $i<=$ny; $i++) { |
382
|
32
|
|
|
|
|
32
|
$k3[$i] = ${$ynref}[$i] + 0.21810038*$k0[$i] - 3.0509647*$k1[$i] |
|
32
|
|
|
|
|
133
|
|
383
|
|
|
|
|
|
|
+ 3.83286432*$k2[$i]; |
384
|
|
|
|
|
|
|
} |
385
|
16
|
|
|
|
|
21
|
@k3 = &{$dydtref}($t+$dt, @k3); |
|
16
|
|
|
|
|
43
|
|
386
|
16
|
|
|
|
|
248
|
for ($i=$[; $i<=$ny; $i++) { $k3[$i] *= $dt; } |
|
32
|
|
|
|
|
69
|
|
387
|
16
|
|
|
|
|
16
|
my @ynp1; $#ynp1 = $ny; |
|
16
|
|
|
|
|
32
|
|
388
|
16
|
|
|
|
|
55
|
for ($i=$[; $i<=$ny; $i++) { |
389
|
32
|
|
|
|
|
32
|
$ynp1[$i] = ${$ynref}[$i] + 0.17476028*$k0[$i] |
|
32
|
|
|
|
|
126
|
|
390
|
|
|
|
|
|
|
- 0.55148053*$k1[$i] + 1.20553547*$k2[$i] + 0.17118478*$k3[$i]; |
391
|
|
|
|
|
|
|
} |
392
|
16
|
|
|
|
|
111
|
return ($t+$dt, @ynp1); |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
} elsif (ref $ynref eq 'HASH') { |
395
|
16
|
|
|
|
|
16
|
my $i; |
396
|
16
|
|
|
|
|
19
|
my $alpha1=0.4; my $alpha2 = 0.4557372542; # = .875 - .1875*(sqrt 5); |
|
16
|
|
|
|
|
17
|
|
397
|
16
|
|
|
|
|
27
|
my %k0 = &{$dydtref}($t, %$ynref); |
|
16
|
|
|
|
|
37
|
|
398
|
16
|
|
|
|
|
163
|
foreach $i (keys(%$ynref)) { $k0{$i} *= $dt; } |
|
32
|
|
|
|
|
56
|
|
399
|
16
|
|
|
|
|
23
|
my %k1; |
400
|
16
|
|
|
|
|
24
|
foreach $i (keys(%$ynref)) { $k1{$i} = ${$ynref}{$i} + 0.4*$k0{$i}; } |
|
32
|
|
|
|
|
27
|
|
|
32
|
|
|
|
|
104
|
|
401
|
16
|
|
|
|
|
34
|
%k1 = &{$dydtref}($t + $alpha1*$dt, %k1); |
|
16
|
|
|
|
|
38
|
|
402
|
16
|
|
|
|
|
167
|
foreach $i (keys(%$ynref)) { $k1{$i} *= $dt; } |
|
32
|
|
|
|
|
48
|
|
403
|
16
|
|
|
|
|
24
|
my %k2; |
404
|
16
|
|
|
|
|
23
|
foreach $i (keys(%$ynref)) { |
405
|
32
|
|
|
|
|
28
|
$k2{$i} = ${$ynref}{$i} + 0.2969776*$k0{$i} + 0.15875966*$k1{$i}; |
|
32
|
|
|
|
|
89
|
|
406
|
|
|
|
|
|
|
} |
407
|
16
|
|
|
|
|
45
|
%k2 = &{$dydtref}($t + $alpha2*$dt, %k2); |
|
16
|
|
|
|
|
37
|
|
408
|
16
|
|
|
|
|
163
|
foreach $i (keys(%$ynref)) { $k2{$i} *= $dt; } |
|
32
|
|
|
|
|
48
|
|
409
|
16
|
|
|
|
|
24
|
my %k3; |
410
|
16
|
|
|
|
|
24
|
foreach $i (keys(%$ynref)) { |
411
|
32
|
|
|
|
|
30
|
$k3{$i} = ${$ynref}{$i} + 0.21810038*$k0{$i} - 3.0509647*$k1{$i} |
|
32
|
|
|
|
|
101
|
|
412
|
|
|
|
|
|
|
+ 3.83286432*$k2{$i}; |
413
|
|
|
|
|
|
|
} |
414
|
16
|
|
|
|
|
30
|
%k3 = &{$dydtref}($t+$dt, %k3); |
|
16
|
|
|
|
|
38
|
|
415
|
16
|
|
|
|
|
165
|
foreach $i (keys(%$ynref)) { $k3{$i} *= $dt; } |
|
32
|
|
|
|
|
44
|
|
416
|
16
|
|
|
|
|
23
|
my %ynp1; |
417
|
16
|
|
|
|
|
28
|
foreach $i (keys(%$ynref)) { |
418
|
32
|
|
|
|
|
29
|
$ynp1{$i} = ${$ynref}{$i} + 0.17476028*$k0{$i} |
|
32
|
|
|
|
|
101
|
|
419
|
|
|
|
|
|
|
- 0.55148053*$k1{$i} + 1.20553547*$k2{$i} + 0.17118478*$k3{$i}; |
420
|
|
|
|
|
|
|
} |
421
|
16
|
|
|
|
|
113
|
return ($t+$dt, %ynp1); |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
} else { |
424
|
0
|
|
|
|
|
0
|
warn "Math::RungeKutta::rk4_ralston: 1st arg must be arrayref or hashref\n"; |
425
|
0
|
|
|
|
|
0
|
return (); |
426
|
|
|
|
|
|
|
} |
427
|
|
|
|
|
|
|
} |
428
|
32
|
|
|
32
|
1
|
921
|
sub rk4_classical { my ($ynref, $dydtref, $t, $dt) = @_; |
429
|
32
|
50
|
|
|
|
77
|
if (ref $dydtref ne 'CODE') { |
430
|
0
|
|
|
|
|
0
|
warn "RungeKutta::rk4_classical: 2nd arg must be subroutine ref\n"; |
431
|
0
|
|
|
|
|
0
|
return (); |
432
|
|
|
|
|
|
|
} |
433
|
|
|
|
|
|
|
# The Classical 4th-order Runge-Kutta Method, see Gear p35 |
434
|
32
|
100
|
|
|
|
75
|
if (ref $ynref eq 'ARRAY') { |
|
|
50
|
|
|
|
|
|
435
|
16
|
|
|
|
|
24
|
my $ny = $#$ynref; my $i; |
|
16
|
|
|
|
|
20
|
|
436
|
16
|
|
|
|
|
37
|
my @k0; $#k0=$ny; |
437
|
16
|
|
|
|
|
21
|
@k0 = &{$dydtref}($t, @$ynref); |
|
16
|
|
|
|
|
39
|
|
438
|
16
|
|
|
|
|
246
|
for ($i=$[; $i<=$ny; $i++) { $k0[$i] *= $dt; } |
|
32
|
|
|
|
|
79
|
|
439
|
16
|
|
|
|
|
21
|
my @eta1; $#eta1=$ny; |
|
16
|
|
|
|
|
32
|
|
440
|
16
|
|
|
|
|
54
|
for ($i=$[; $i<=$ny; $i++) { $eta1[$i] = ${$ynref}[$i] + 0.5*$k0[$i]; } |
|
32
|
|
|
|
|
27
|
|
|
32
|
|
|
|
|
113
|
|
441
|
16
|
|
|
|
|
17
|
my @k1; $#k1=$ny; |
|
16
|
|
|
|
|
32
|
|
442
|
16
|
|
|
|
|
26
|
@k1 = &{$dydtref}($t+0.5*$dt, @eta1); |
|
16
|
|
|
|
|
43
|
|
443
|
16
|
|
|
|
|
268
|
for ($i=$[; $i<=$ny; $i++) { $k1[$i] *= $dt; } |
|
32
|
|
|
|
|
69
|
|
444
|
16
|
|
|
|
|
15
|
my @eta2; $#eta2=$ny; |
|
16
|
|
|
|
|
33
|
|
445
|
16
|
|
|
|
|
53
|
for ($i=$[; $i<=$ny; $i++) { $eta2[$i] = ${$ynref}[$i] + 0.5*$k1[$i]; } |
|
32
|
|
|
|
|
31
|
|
|
32
|
|
|
|
|
105
|
|
446
|
16
|
|
|
|
|
17
|
my @k2; $#k2=$ny; |
|
16
|
|
|
|
|
29
|
|
447
|
16
|
|
|
|
|
23
|
@k2 = &{$dydtref}($t+0.5*$dt, @eta2); |
|
16
|
|
|
|
|
40
|
|
448
|
16
|
|
|
|
|
230
|
for ($i=$[; $i<=$ny; $i++) { $k2[$i] *= $dt; } |
|
32
|
|
|
|
|
69
|
|
449
|
16
|
|
|
|
|
14
|
my @eta3; $#eta3=$ny; |
|
16
|
|
|
|
|
61
|
|
450
|
16
|
|
|
|
|
55
|
for ($i=$[; $i<=$ny; $i++) { $eta3[$i] = ${$ynref}[$i] + $k2[$i]; } |
|
32
|
|
|
|
|
32
|
|
|
32
|
|
|
|
|
105
|
|
451
|
16
|
|
|
|
|
15
|
my @k3; $#k3=$ny; |
|
16
|
|
|
|
|
31
|
|
452
|
16
|
|
|
|
|
38
|
@k3 = &{$dydtref}($t+$dt, @eta3); |
|
16
|
|
|
|
|
41
|
|
453
|
16
|
|
|
|
|
246
|
for ($i=$[; $i<=$ny; $i++) { $k3[$i] *= $dt; } |
|
32
|
|
|
|
|
71
|
|
454
|
16
|
|
|
|
|
15
|
my @ynp1; $#ynp1 = $ny; |
|
16
|
|
|
|
|
32
|
|
455
|
16
|
|
|
|
|
54
|
for ($i=$[; $i<=$ny; $i++) { |
456
|
32
|
|
|
|
|
49
|
$ynp1[$i] = ${$ynref}[$i] + |
|
32
|
|
|
|
|
133
|
|
457
|
|
|
|
|
|
|
($k0[$i] + 2.0*$k1[$i] + 2.0*$k2[$i] + $k3[$i]) / 6.0; |
458
|
|
|
|
|
|
|
} |
459
|
16
|
|
|
|
|
148
|
return ($t+$dt, @ynp1); |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
} elsif (ref $ynref eq 'HASH') { |
462
|
16
|
|
|
|
|
28
|
my %k0 = &{$dydtref}($t, %$ynref); |
|
16
|
|
|
|
|
39
|
|
463
|
16
|
|
|
|
|
164
|
foreach my $i (keys(%$ynref)) { $k0{$i} *= $dt; } |
|
32
|
|
|
|
|
54
|
|
464
|
16
|
|
|
|
|
21
|
my %eta1; |
465
|
16
|
|
|
|
|
31
|
foreach $i (keys(%$ynref)) { $eta1{$i} = ${$ynref}{$i} + 0.5*$k0{$i}; } |
|
32
|
|
|
|
|
31
|
|
|
32
|
|
|
|
|
87
|
|
466
|
16
|
|
|
|
|
34
|
my %k1 = &{$dydtref}($t+0.5*$dt, %eta1); |
|
16
|
|
|
|
|
34
|
|
467
|
16
|
|
|
|
|
172
|
foreach $i (keys(%$ynref)) { $k1{$i} *= $dt; } |
|
32
|
|
|
|
|
53
|
|
468
|
16
|
|
|
|
|
19
|
my %eta2; |
469
|
16
|
|
|
|
|
30
|
foreach $i (keys(%$ynref)) { $eta2{$i} = ${$ynref}{$i} + 0.5*$k1{$i}; } |
|
32
|
|
|
|
|
38
|
|
|
32
|
|
|
|
|
78
|
|
470
|
16
|
|
|
|
|
36
|
my %k2 = &{$dydtref}($t+0.5*$dt, %eta2); |
|
16
|
|
|
|
|
41
|
|
471
|
16
|
|
|
|
|
193
|
foreach $i (keys(%$ynref)) { $k2{$i} *= $dt; } |
|
32
|
|
|
|
|
48
|
|
472
|
16
|
|
|
|
|
23
|
my %eta3; |
473
|
16
|
|
|
|
|
26
|
foreach $i (keys(%$ynref)) { $eta3{$i} = ${$ynref}{$i} + $k2{$i}; } |
|
32
|
|
|
|
|
31
|
|
|
32
|
|
|
|
|
75
|
|
474
|
16
|
|
|
|
|
30
|
my %k3 = &{$dydtref}($t+$dt, %eta3); |
|
16
|
|
|
|
|
38
|
|
475
|
16
|
|
|
|
|
152
|
foreach $i (keys(%$ynref)) { $k3{$i} *= $dt; } |
|
32
|
|
|
|
|
47
|
|
476
|
16
|
|
|
|
|
21
|
my %ynp1; |
477
|
16
|
|
|
|
|
30
|
foreach $i (keys(%$ynref)) { |
478
|
32
|
|
|
|
|
31
|
$ynp1{$i} = ${$ynref}{$i} + |
|
32
|
|
|
|
|
108
|
|
479
|
|
|
|
|
|
|
($k0{$i} + 2.0*$k1{$i} + 2.0*$k2{$i} + $k3{$i}) / 6.0; |
480
|
|
|
|
|
|
|
} |
481
|
16
|
|
|
|
|
124
|
return ($t+$dt, %ynp1); |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
} else { |
484
|
0
|
|
|
|
|
|
warn "Math::RungeKutta::rk4_classical: 1st arg must be arrayref or hashref\n"; |
485
|
0
|
|
|
|
|
|
return (); |
486
|
|
|
|
|
|
|
} |
487
|
|
|
|
|
|
|
} |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
# --------------------- infrastructure ---------------------- |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
sub arr2txt { # neat printing of arrays for debug use |
492
|
0
|
|
|
0
|
0
|
|
my @txt; foreach (@_) { push @txt, sprintf('%g',$_); } |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
493
|
0
|
|
|
|
|
|
return join (' ',@txt)."\n"; |
494
|
|
|
|
|
|
|
} |
495
|
|
|
|
|
|
|
my $flag; |
496
|
0
|
|
|
0
|
0
|
|
sub gaussn { my $standdev = $_[$[]; |
497
|
|
|
|
|
|
|
# returns normal distribution around 0.0 by the Box-Muller rules |
498
|
0
|
0
|
|
|
|
|
if (! $flag) { |
499
|
0
|
|
|
|
|
|
$a = sqrt(-2.0 * log(rand)); $b = 6.28318531 * rand; |
|
0
|
|
|
|
|
|
|
500
|
0
|
|
|
|
|
|
$flag = 1; return ($standdev * $a * sin($b)); |
|
0
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
} else { |
502
|
0
|
|
|
|
|
|
$flag = 0; return ($standdev * $a * cos($b)); |
|
0
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
} |
504
|
|
|
|
|
|
|
} |
505
|
|
|
|
|
|
|
1; |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
__END__ |