00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 package require Tcl 8.4
00014 package require math::interpolate
00015 package provide math::calculus 0.7
00016
00017
00018
00019
00020 namespace ::math::calculus {
00021
00022 namespace import ::math::interpolate::neville
00023
00024 namespace import ::math::expectDouble ::math::expectInteger
00025
00026 namespace export \
00027 integral integralExpr integral2D integral3D \
00028 eulerStep heunStep rungeKuttaStep \
00029 boundaryValueSecondOrder solveTriDiagonal \
00030 newtonRaphson newtonRaphsonParameters
00031 namespace export \
00032 integral2D_2accurate integral3D_accurate
00033
00034 namespace export romberg romberg_infinity
00035 namespace export romberg_sqrtSingLower romberg_sqrtSingUpper
00036 namespace export romberg_powerLawLower romberg_powerLawUpper
00037 namespace export romberg_expLower romberg_expUpper
00038
00039 namespace export regula_falsi
00040
00041 variable nr_maxiter 20
00042 variable nr_tolerance 0.001
00043
00044 }
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058 ret ::math::calculus::integral ( type begin , type end , type nosteps , type func ) {
00059
00060 set delta [expr {($end-$begin)/double($nosteps)}]
00061 set hdelta [expr {$delta/2.0}]
00062 set result 0.0
00063 set xval $begin
00064 set func_end [uplevel 1 $func $xval]
00065 for { set i 1 } { $i <= $nosteps } { incr i } {
00066 set func_begin $func_end
00067 set func_middle [uplevel 1 $func [expr {$xval+$hdelta}]]
00068 set func_end [uplevel 1 $func [expr {$xval+$delta}]]
00069 set result [expr {$result+$func_begin+4.0*$func_middle+$func_end}]
00070
00071 set xval [expr {$begin+double($i)*$delta}]
00072 }
00073
00074 return [expr {$result*$delta/6.0}]
00075 }
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089 ret ::math::calculus::integralExpr ( type begin , type end , type nosteps , type expression ) {
00090
00091 set delta [expr {($end-$begin)/double($nosteps)}]
00092 set hdelta [expr {$delta/2.0}]
00093 set result 0.0
00094 set x $begin
00095 # FRINK: nocheck
00096 set func_end [expr $expression]
00097 for { set i 1 } { $i <= $nosteps } { incr i } {
00098 set func_begin $func_end
00099 set x [expr {$x+$hdelta}]
00100 # FRINK: nocheck
00101 set func_middle [expr $expression]
00102 set x [expr {$x+$hdelta}]
00103 # FRINK: nocheck
00104 set func_end [expr $expression]
00105 set result [expr {$result+$func_begin+4.0*$func_middle+$func_end}]
00106
00107 set x [expr {$begin+double($i)*$delta}]
00108 }
00109
00110 return [expr {$result*$delta/6.0}]
00111 }
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124 ret ::math::calculus::integral2D ( type xinterval , type yinterval , type func ) {
00125
00126 foreach { xbegin xend xnumber } $xinterval { break }
00127 foreach { ybegin yend ynumber } $yinterval { break }
00128
00129 set xdelta [expr {($xend-$xbegin)/double($xnumber)}]
00130 set ydelta [expr {($yend-$ybegin)/double($ynumber)}]
00131 set hxdelta [expr {$xdelta/2.0}]
00132 set hydelta [expr {$ydelta/2.0}]
00133 set result 0.0
00134 set dxdy [expr {$xdelta*$ydelta}]
00135 for { set j 0 } { $j < $ynumber } { incr j } {
00136 set y [expr {$ybegin+$hydelta+double($j)*$ydelta}]
00137 for { set i 0 } { $i < $xnumber } { incr i } {
00138 set x [expr {$xbegin+$hxdelta+double($i)*$xdelta}]
00139 set func_value [uplevel 1 $func $x $y]
00140 set result [expr {$result+$func_value}]
00141 }
00142 }
00143
00144 return [expr {$result*$dxdy}]
00145 }
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159 ret ::math::calculus::integral3D ( type xinterval , type yinterval , type zinterval , type func ) {
00160
00161 foreach { xbegin xend xnumber } $xinterval { break }
00162 foreach { ybegin yend ynumber } $yinterval { break }
00163 foreach { zbegin zend znumber } $zinterval { break }
00164
00165 set xdelta [expr {($xend-$xbegin)/double($xnumber)}]
00166 set ydelta [expr {($yend-$ybegin)/double($ynumber)}]
00167 set zdelta [expr {($zend-$zbegin)/double($znumber)}]
00168 set hxdelta [expr {$xdelta/2.0}]
00169 set hydelta [expr {$ydelta/2.0}]
00170 set hzdelta [expr {$zdelta/2.0}]
00171 set result 0.0
00172 set dxdydz [expr {$xdelta*$ydelta*$zdelta}]
00173 for { set k 0 } { $k < $znumber } { incr k } {
00174 set z [expr {$zbegin+$hzdelta+double($k)*$zdelta}]
00175 for { set j 0 } { $j < $ynumber } { incr j } {
00176 set y [expr {$ybegin+$hydelta+double($j)*$ydelta}]
00177 for { set i 0 } { $i < $xnumber } { incr i } {
00178 set x [expr {$xbegin+$hxdelta+double($i)*$xdelta}]
00179 set func_value [uplevel 1 $func $x $y $z]
00180 set result [expr {$result+$func_value}]
00181 }
00182 }
00183 }
00184
00185 return [expr {$result*$dxdydz}]
00186 }
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199 ret ::math::calculus::integral2D_accurate ( type xinterval , type yinterval , type func ) {
00200
00201 foreach { xbegin xend xnumber } $xinterval { break }
00202 foreach { ybegin yend ynumber } $yinterval { break }
00203
00204 set alpha [expr {sqrt(2.0/3.0)}]
00205 set minalpha [expr {-$alpha}]
00206 set dpoints [list $alpha 0.0 $minalpha 0.0 0.0 $alpha 0.0 $minalpha]
00207
00208 set xdelta [expr {($xend-$xbegin)/double($xnumber)}]
00209 set ydelta [expr {($yend-$ybegin)/double($ynumber)}]
00210 set hxdelta [expr {$xdelta/2.0}]
00211 set hydelta [expr {$ydelta/2.0}]
00212 set result 0.0
00213 set dxdy [expr {0.25*$xdelta*$ydelta}]
00214
00215 for { set j 0 } { $j < $ynumber } { incr j } {
00216 set y [expr {$ybegin+$hydelta+double($j)*$ydelta}]
00217 for { set i 0 } { $i < $xnumber } { incr i } {
00218 set x [expr {$xbegin+$hxdelta+double($i)*$xdelta}]
00219
00220 foreach {dx dy} $dpoints {
00221 set x1 [expr {$x+$dx}]
00222 set y1 [expr {$y+$dy}]
00223 set func_value [uplevel 1 $func $x1 $y1]
00224 set result [expr {$result+$func_value}]
00225 }
00226 }
00227 }
00228
00229 return [expr {$result*$dxdy}]
00230 }
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244 ret ::math::calculus::integral3D_accurate ( type xinterval , type yinterval , type zinterval , type func ) {
00245
00246 foreach { xbegin xend xnumber } $xinterval { break }
00247 foreach { ybegin yend ynumber } $yinterval { break }
00248 foreach { zbegin zend znumber } $zinterval { break }
00249
00250 set alpha [expr {sqrt(1.0/3.0)}]
00251 set minalpha [expr {-$alpha}]
00252
00253 set dpoints [list $alpha $alpha $alpha \
00254 $alpha $alpha $minalpha \
00255 $alpha $minalpha $alpha \
00256 $alpha $minalpha $minalpha \
00257 $minalpha $alpha $alpha \
00258 $minalpha $alpha $minalpha \
00259 $minalpha $minalpha $alpha \
00260 $minalpha $minalpha $minalpha ]
00261
00262 set xdelta [expr {($xend-$xbegin)/double($xnumber)}]
00263 set ydelta [expr {($yend-$ybegin)/double($ynumber)}]
00264 set zdelta [expr {($zend-$zbegin)/double($znumber)}]
00265 set hxdelta [expr {$xdelta/2.0}]
00266 set hydelta [expr {$ydelta/2.0}]
00267 set hzdelta [expr {$zdelta/2.0}]
00268 set result 0.0
00269 set dxdydz [expr {0.125*$xdelta*$ydelta*$zdelta}]
00270
00271 for { set k 0 } { $k < $znumber } { incr k } {
00272 set z [expr {$zbegin+$hzdelta+double($k)*$zdelta}]
00273 for { set j 0 } { $j < $ynumber } { incr j } {
00274 set y [expr {$ybegin+$hydelta+double($j)*$ydelta}]
00275 for { set i 0 } { $i < $xnumber } { incr i } {
00276 set x [expr {$xbegin+$hxdelta+double($i)*$xdelta}]
00277
00278 foreach {dx dy dz} $dpoints {
00279 set x1 [expr {$x+$dx}]
00280 set y1 [expr {$y+$dy}]
00281 set z1 [expr {$z+$dz}]
00282 set func_value [uplevel 1 $func $x1 $y1 $z1]
00283 set result [expr {$result+$func_value}]
00284 }
00285 }
00286 }
00287 }
00288
00289 return [expr {$result*$dxdydz}]
00290 }
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306 ret ::math::calculus::eulerStep ( type t , type tstep , type xvec , type func ) {
00307
00308 set xderiv [uplevel 1 $func $t [list $xvec]]
00309 set result {}
00310 foreach xv $xvec dx $xderiv {
00311 set xnew [expr {$xv+$tstep*$dx}]
00312 lappend result $xnew
00313 }
00314
00315 return $result
00316 }
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332 ret ::math::calculus::heunStep ( type t , type tstep , type xvec , type func ) {
00333
00334 #
00335 # Predictor step
00336 #
00337 set funcq [uplevel 1 namespace which -command $func]
00338 set xpred [eulerStep $t $tstep $xvec $funcq]
00339
00340 #
00341 # Corrector step
00342 #
00343 set tcorr [expr {$t+$tstep}]
00344 set xcorr [eulerStep $t $tstep $xpred $funcq]
00345
00346 set result {}
00347 foreach xv $xvec xc $xcorr {
00348 set xnew [expr {0.5*($xv+$xc)}]
00349 lappend result $xnew
00350 }
00351
00352 return $result
00353 }
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369 ret ::math::calculus::rungeKuttaStep ( type t , type tstep , type xvec , type func ) {
00370
00371 set funcq [uplevel 1 namespace which -command $func]
00372
00373 #
00374 # Four steps:
00375 # - k1 = tstep*func(t,x0)
00376 # - k2 = tstep*func(t+0.5*tstep,x0+0.5*k1)
00377 # - k3 = tstep*func(t+0.5*tstep,x0+0.5*k2)
00378 # - k4 = tstep*func(t+ tstep,x0+ k3)
00379 # - x1 = x0 + (k1+2*k2+2*k3+k4)/6
00380 #
00381 set tstep2 [expr {$tstep/2.0}]
00382 set tstep6 [expr {$tstep/6.0}]
00383
00384 set xk1 [$funcq $t $xvec]
00385 set xvec2 {}
00386 foreach x1 $xvec xv $xk1 {
00387 lappend xvec2 [expr {$x1+$tstep2*$xv}]
00388 }
00389 set xk2 [$funcq [expr {$t+$tstep2}] $xvec2]
00390
00391 set xvec3 {}
00392 foreach x1 $xvec xv $xk2 {
00393 lappend xvec3 [expr {$x1+$tstep2*$xv}]
00394 }
00395 set xk3 [$funcq [expr {$t+$tstep2}] $xvec3]
00396
00397 set xvec4 {}
00398 foreach x1 $xvec xv $xk3 {
00399 lappend xvec4 [expr {$x1+$tstep*$xv}]
00400 }
00401 set xk4 [$funcq [expr {$t+$tstep}] $xvec4]
00402
00403 set result {}
00404 foreach x0 $xvec k1 $xk1 k2 $xk2 k3 $xk3 k4 $xk4 {
00405 set dx [expr {$k1+2.0*$k2+2.0*$k3+$k4}]
00406 lappend result [expr {$x0+$dx*$tstep6}]
00407 }
00408
00409 return $result
00410 }
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435 ret ::math::calculus::boundaryValueSecondOrder (
00436 type coeff_, type func , type force_, type func , type leftbnd , type rightbnd , type nostep ) {
00437
00438 set coeffq [uplevel 1 namespace which -command $coeff_func]
00439 set forceq [uplevel 1 namespace which -command $force_func]
00440
00441 if { [llength $leftbnd] != 2 || [llength $rightbnd] != 2 } {
00442 error "Boundary condition(s) incorrect"
00443 }
00444 if { $nostep < 1 } {
00445 error "Number of steps must be larger/equal 1"
00446 }
00447
00448 #
00449 # Set up the matrix, as three different lists and the
00450 # righthand side as the fourth
00451 #
00452 set xleft [lindex $leftbnd 0]
00453 set xright [lindex $rightbnd 0]
00454 set xstep [expr {($xright-$xleft)/double($nostep)}]
00455
00456 set acoeff {}
00457 set bcoeff {}
00458 set ccoeff {}
00459 set dvalue {}
00460
00461 set x $xleft
00462 foreach {A B C} [$coeffq $x] { break }
00463
00464 set A1 [expr {$A/$xstep-0.5*$B}]
00465 set B1 [expr {$A/$xstep+0.5*$B+0.5*$C*$xstep}]
00466 set C1 0.0
00467
00468 for { set i 1 } { $i <= $nostep } { incr i } {
00469 set x [expr {$xleft+double($i)*$xstep}]
00470 if { [expr {abs($x)-0.5*abs($xstep)}] < 0.0 } {
00471 set x 0.0
00472 }
00473 foreach {A B C} [$coeffq $x] { break }
00474
00475 set A2 0.0
00476 set B2 [expr {$A/$xstep-0.5*$B+0.5*$C*$xstep}]
00477 set C2 [expr {$A/$xstep+0.5*$B}]
00478 lappend acoeff [expr {$A1+$A2}]
00479 lappend bcoeff [expr {-$B1-$B2}]
00480 lappend ccoeff [expr {$C1+$C2}]
00481 set A1 [expr {$A/$xstep-0.5*$B}]
00482 set B1 [expr {$A/$xstep+0.5*$B+0.5*$C*$xstep}]
00483 set C1 0.0
00484 }
00485 set xvec {}
00486 for { set i 0 } { $i < $nostep } { incr i } {
00487 set x [expr {$xleft+(0.5+double($i))*$xstep}]
00488 if { [expr {abs($x)-0.25*abs($xstep)}] < 0.0 } {
00489 set x 0.0
00490 }
00491 lappend xvec $x
00492 lappend dvalue [expr {$xstep*[$forceq $x]}]
00493 }
00494
00495 #
00496 # Substitute the boundary values
00497 #
00498 set A [lindex $acoeff 0]
00499 set D [lindex $dvalue 0]
00500 set D1 [expr {$D-$A*[lindex $leftbnd 1]}]
00501 set C [lindex $ccoeff end]
00502 set D [lindex $dvalue end]
00503 set D2 [expr {$D-$C*[lindex $rightbnd 1]}]
00504 set dvalue [concat $D1 [lrange $dvalue 1 end-1] $D2]
00505
00506 set yvec [solveTriDiagonal $acoeff $bcoeff $ccoeff $dvalue]
00507
00508 foreach x $xvec y $yvec {
00509 lappend result $x $y
00510 }
00511 return $result
00512 }
00513
00514
00515
00516
00517
00518
00519
00520
00521
00522
00523
00524
00525 ret ::math::calculus::solveTriDiagonal ( type acoeff , type bcoeff , type ccoeff , type dvalue ) {
00526
00527 set nostep [llength $acoeff]
00528 #
00529 # First step: Gauss-elimination
00530 #
00531 set B [lindex $bcoeff 0]
00532 set C [lindex $ccoeff 0]
00533 set D [lindex $dvalue 0]
00534 set bcoeff2 [list $B]
00535 set dvalue2 [list $D]
00536 for { set i 1 } { $i < $nostep } { incr i } {
00537 set A2 [lindex $acoeff $i]
00538 set B2 [lindex $bcoeff $i]
00539 set C2 [lindex $ccoeff $i]
00540 set D2 [lindex $dvalue $i]
00541 set ratab [expr {$A2/$B}]
00542 set B2 [expr {$B2-$ratab*$C}]
00543 set D2 [expr {$D2-$ratab*$D}]
00544 lappend bcoeff2 $B2
00545 lappend dvalue2 $D2
00546 set B $B2
00547 set D $D2
00548 }
00549
00550 #
00551 # Second step: substitution
00552 #
00553 set yvec {}
00554 set B [lindex $bcoeff2 end]
00555 set D [lindex $dvalue2 end]
00556 set y [expr {$D/$B}]
00557 for { set i [expr {$nostep-2}] } { $i >= 0 } { incr i -1 } {
00558 set yvec [concat $y $yvec]
00559 set B [lindex $bcoeff2 $i]
00560 set C [lindex $ccoeff $i]
00561 set D [lindex $dvalue2 $i]
00562 set y [expr {($D-$C*$y)/$B}]
00563 }
00564 set yvec [concat $y $yvec]
00565
00566 return $yvec
00567 }
00568
00569
00570
00571
00572
00573
00574
00575
00576
00577
00578
00579 ret ::math::calculus::newtonRaphson ( type func , type deriv , type initval ) {
00580 variable nr_maxiter
00581 variable nr_tolerance
00582
00583 set funcq [uplevel 1 namespace which -command $func]
00584 set derivq [uplevel 1 namespace which -command $deriv]
00585
00586 set value $initval
00587 set diff [expr {10.0*$nr_tolerance}]
00588
00589 for { set i 0 } { $i < $nr_maxiter } { incr i } {
00590 if { $diff < $nr_tolerance } {
00591 break
00592 }
00593
00594 set newval [expr {$value-[$funcq $value]/[$derivq $value]}]
00595 if { $value != 0.0 } {
00596 set diff [expr {abs($newval-$value)/abs($value)}]
00597 } else {
00598 set diff [expr {abs($newval-$value)}]
00599 }
00600 set value $newval
00601 }
00602
00603 return $newval
00604 }
00605
00606
00607
00608
00609
00610
00611
00612
00613
00614
00615 ret ::math::calculus::newtonRaphsonParameters ( type maxiter , type tolerance ) {
00616 variable nr_maxiter
00617 variable nr_tolerance
00618
00619 if { $maxiter > 0 } {
00620 set nr_maxiter $maxiter
00621 }
00622 if { $tolerance > 0 } {
00623 set nr_tolerance $tolerance
00624 }
00625 }
00626
00627
00628
00629
00630
00631
00632
00633
00634
00635
00636
00637
00638
00639
00640
00641
00642
00643
00644
00645
00646
00647
00648
00649
00650
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661
00662
00663
00664
00665
00666
00667
00668
00669
00670 ret ::math::calculus::midpoint ( type f , type a , type b , optional n =0 , optional s =0. ) {
00671
00672 if { $n == 0 } {
00673
00674 # First iteration. Simply evaluate the function at the midpoint
00675 # of the interval.
00676
00677 set cmd $f; lappend cmd [expr { 0.5 * ( $a + $b ) }]; set v [eval $cmd]
00678 return [expr { ( $b - $a ) * $v }]
00679
00680 } else {
00681
00682 # Subsequent iterations. We've divided the interval into
00683 # $it subintervals. Evaluate the function at the 1/3 and
00684 # 2/3 points of each subinterval. Then update the estimate
00685 # of the integral that we produced on the last step with
00686 # the new sum.
00687
00688 set it [expr { pow( 3, $n-1 ) }]
00689 set h [expr { ( $b - $a ) / ( 3. * $it ) }]
00690 set h2 [expr { $h + $h }]
00691 set x [expr { $a + 0.5 * $h }]
00692 set sum 0
00693 for { set j 0 } { $j < $it } { incr j } {
00694 set cmd $f; lappend cmd $x; set y [eval $cmd]
00695 set sum [expr { $sum + $y }]
00696 set x [expr { $x + $h2 }]
00697 set cmd $f; lappend cmd $x; set y [eval $cmd]
00698 set sum [expr { $sum + $y }]
00699 set x [expr { $x + $h}]
00700 }
00701 return [expr { ( $s + ( $b - $a ) * $sum / $it ) / 3. }]
00702
00703 }
00704 }
00705
00706
00707
00708
00709
00710
00711
00712
00713
00714
00715
00716
00717
00718
00719
00720
00721
00722
00723
00724
00725
00726
00727
00728
00729
00730
00731
00732
00733
00734
00735
00736
00737
00738
00739
00740
00741
00742
00743
00744
00745
00746
00747
00748
00749
00750
00751
00752
00753
00754
00755
00756
00757
00758 ret ::math::calculus::romberg ( type f , type a , type b , type args ) {
00759
00760 # Replace f with a context-independent version
00761
00762 set f [lreplace $f 0 0 [uplevel 1 [list namespace which [lindex $f 0]]]]
00763
00764 # Assign default parameters
00765
00766 array set params {
00767 -abserror 1.0e-10
00768 -degree 4
00769 -relerror 1.0e-6
00770 -maxiter 14
00771 }
00772
00773 # Extract parameters
00774
00775 if { ( [llength $args] % 2 ) != 0 } {
00776 return -code error -errorcode [list romberg wrongNumArgs] \
00777 "wrong \# args, should be\
00778 \"[lreplace [info level 0] 1 end \
00779 f x1 x2 ?-option value?...]\""
00780 }
00781 foreach { key value } $args {
00782 if { ![info exists params($key)] } {
00783 return -code error -errorcode [list romberg badoption $key] \
00784 "unknown option \"$key\",\
00785 should be -abserror, -degree, -relerror, or -maxiter"
00786 }
00787 set params($key) $value
00788 }
00789
00790 # Check params
00791
00792 if { ![string is double -strict $a] } {
00793 return -code error [expectDouble $a]
00794 }
00795 if { ![string is double -strict $b] } {
00796 return -code error [expectDouble $b]
00797 }
00798 if { ![string is double -strict $params(-abserror)] } {
00799 return -code error [expectDouble $params(-abserror)]
00800 }
00801 if { ![string is integer -strict $params(-degree)] } {
00802 return -code error [expectInteger $params(-degree)]
00803 }
00804 if { ![string is integer -strict $params(-maxiter)] } {
00805 return -code error [expectInteger $params(-maxiter)]
00806 }
00807 if { ![string is double -strict $params(-relerror)] } {
00808 return -code error [expectDouble $params(-relerror)]
00809 }
00810 foreach key {-abserror -degree -maxiter -relerror} {
00811 if { $params($key) <= 0 } {
00812 return -code error -errorcode [list romberg notPositive $key] \
00813 "$key must be positive"
00814 }
00815 }
00816 if { $params(-maxiter) <= $params(-degree) } {
00817 return -code error -errorcode [list romberg tooFewIter] \
00818 "-maxiter must be greater than -degree"
00819 }
00820
00821 # Create lists of step size and sum with the given number of steps.
00822
00823 set x [list]
00824 set y [list]
00825 set s 0; # Current best estimate of integral
00826 set indx end-$params(-degree)
00827 set pow3 1.; # Current step size (times b-a)
00828
00829 # Perform successive integrations, tripling the number of steps each time
00830
00831 for { set i 0 } { $i < $params(-maxiter) } { incr i } {
00832 set s [midpoint $f $a $b $i $s]
00833 lappend x $pow3
00834 lappend y $s
00835 set pow3 [expr { $pow3 / 9. }]
00836
00837 # Once $degree steps have been done, start Richardson extrapolation
00838 # to a zero step size.
00839
00840 if { $i >= $params(-degree) } {
00841 set x [lrange $x $indx end]
00842 set y [lrange $y $indx end]
00843 foreach {estimate err} [neville $x $y 0.] break
00844 if { $err < $params(-abserror)
00845 || $err < $params(-relerror) * abs($estimate) } {
00846 return [list $estimate $err]
00847 }
00848 }
00849 }
00850
00851 # If -maxiter iterations have been done, give up, and return
00852 # with the current error estimate.
00853
00854 return [list $estimate $err]
00855 }
00856
00857
00858
00859
00860
00861
00862
00863
00864
00865
00866
00867
00868
00869
00870
00871
00872
00873
00874
00875 ret ::math::calculus::u_infinity ( type f , type u ) {
00876 set cmd $f
00877 lappend cmd [expr { 1.0 / $u }]
00878 set y [eval $cmd]
00879 return [expr { $y / ( $u * $u ) }]
00880 }
00881
00882
00883
00884
00885
00886
00887
00888
00889
00890
00891
00892
00893
00894
00895
00896
00897
00898 ret ::math::calculus::romberg_infinity ( type f , type a , type b , type args ) {
00899 if { ![string is double -strict $a] } {
00900 return -code error [expectDouble $a]
00901 }
00902 if { ![string is double -strict $b] } {
00903 return -code error [expectDouble $b]
00904 }
00905 if { $a * $b <= 0. } {
00906 return -code error -errorcode {romberg_infinity cross-axis} \
00907 "limits of integration have opposite sign"
00908 }
00909 set f [lreplace $f 0 0 [uplevel 1 [list namespace which [lindex $f 0]]]]
00910 set f [list u_infinity $f]
00911 return [eval [linsert $args 0 \
00912 romberg $f [expr { 1.0 / $b }] [expr { 1.0 / $a }]]]
00913 }
00914
00915
00916
00917
00918
00919
00920
00921
00922
00923
00924
00925
00926
00927
00928
00929
00930
00931
00932
00933
00934 ret ::math::calculus::u_sqrtSingLower ( type f , type a , type u ) {
00935 set cmd $f
00936 lappend cmd [expr { $a + $u * $u }]
00937 set y [eval $cmd]
00938 return [expr { 2. * $u * $y }]
00939 }
00940
00941
00942
00943
00944
00945
00946
00947
00948
00949
00950
00951
00952
00953
00954
00955
00956
00957
00958
00959
00960 ret ::math::calculus::u_sqrtSingUpper ( type f , type b , type u ) {
00961 set cmd $f
00962 lappend cmd [expr { $b - $u * $u }]
00963 set y [eval $cmd]
00964 return [expr { 2. * $u * $y }]
00965 }
00966
00967
00968
00969
00970
00971
00972
00973
00974
00975
00976
00977
00978
00979
00980
00981
00982
00983 ret ::math::calculus::romberg_sqrtSingLower ( type f , type a , type b , type args ) {
00984 if { ![string is double -strict $a] } {
00985 return -code error [expectDouble $a]
00986 }
00987 if { ![string is double -strict $b] } {
00988 return -code error [expectDouble $b]
00989 }
00990 if { $a >= $b } {
00991 return -code error "limits of integration out of order"
00992 }
00993 set f [lreplace $f 0 0 [uplevel 1 [list namespace which [lindex $f 0]]]]
00994 set f [list u_sqrtSingLower $f $a]
00995 return [eval [linsert $args 0 \
00996 romberg $f 0 [expr { sqrt( $b - $a ) }]]]
00997 }
00998
00999
01000
01001
01002
01003
01004
01005
01006
01007
01008
01009
01010
01011
01012
01013
01014
01015 ret ::math::calculus::romberg_sqrtSingUpper ( type f , type a , type b , type args ) {
01016 if { ![string is double -strict $a] } {
01017 return -code error [expectDouble $a]
01018 }
01019 if { ![string is double -strict $b] } {
01020 return -code error [expectDouble $b]
01021 }
01022 if { $a >= $b } {
01023 return -code error "limits of integration out of order"
01024 }
01025 set f [lreplace $f 0 0 [uplevel 1 [list namespace which [lindex $f 0]]]]
01026 set f [list u_sqrtSingUpper $f $b]
01027 return [eval [linsert $args 0 \
01028 romberg $f 0. [expr { sqrt( $b - $a ) }]]]
01029 }
01030
01031
01032
01033
01034
01035
01036
01037
01038
01039
01040
01041
01042
01043
01044
01045
01046
01047
01048
01049
01050
01051
01052 ret ::math::calculus::u_powerLawLower ( type f , type gammaover1mgamma , type oneover1mgamma
01053 , type a , type u ) {
01054 set cmd $f
01055 lappend cmd [expr { $a + pow( $u, $oneover1mgamma ) }]
01056 set y [eval $cmd]
01057 return [expr { $y * pow( $u, $gammaover1mgamma ) }]
01058 }
01059
01060
01061
01062
01063
01064
01065
01066
01067
01068
01069
01070
01071
01072
01073
01074
01075
01076
01077
01078
01079
01080
01081
01082
01083
01084
01085
01086
01087
01088
01089
01090
01091
01092
01093
01094
01095
01096
01097
01098
01099
01100
01101
01102
01103
01104
01105
01106
01107
01108
01109
01110
01111
01112
01113
01114
01115
01116
01117 ret ::math::calculus::romberg_powerLawLower ( type gamma , type f , type a , type b , type args ) {
01118 if { ![string is double -strict $gamma] } {
01119 return -code error [expectDouble $gamma]
01120 }
01121 if { $gamma <= 0.0 || $gamma >= 1.0 } {
01122 return -code error -errorcode [list romberg gammaTooBig] \
01123 "gamma must lie in the interval (0,1)"
01124 }
01125 if { ![string is double -strict $a] } {
01126 return -code error [expectDouble $a]
01127 }
01128 if { ![string is double -strict $b] } {
01129 return -code error [expectDouble $b]
01130 }
01131 if { $a >= $b } {
01132 return -code error "limits of integration out of order"
01133 }
01134 set f [lreplace $f 0 0 [uplevel 1 [list namespace which [lindex $f 0]]]]
01135 set onemgamma [expr { 1. - $gamma }]
01136 set f [list u_powerLawLower $f \
01137 [expr { $gamma / $onemgamma }] \
01138 [expr { 1 / $onemgamma }] \
01139 $a]
01140
01141 set limit [expr { pow( $b - $a, $onemgamma ) }]
01142 set result {}
01143 foreach v [eval [linsert $args 0 romberg $f 0 $limit]] {
01144 lappend result [expr { $v / $onemgamma }]
01145 }
01146 return $result
01147
01148 }
01149
01150
01151
01152
01153
01154
01155
01156
01157
01158
01159
01160
01161
01162
01163
01164
01165
01166
01167
01168
01169
01170
01171 ret ::math::calculus::u_powerLawUpper ( type f , type gammaover1mgamma , type oneover1mgamma
01172 , type b , type u ) {
01173 set cmd $f
01174 lappend cmd [expr { $b - pow( $u, $oneover1mgamma ) }]
01175 set y [eval $cmd]
01176 return [expr { $y * pow( $u, $gammaover1mgamma ) }]
01177 }
01178
01179
01180
01181
01182
01183
01184
01185
01186
01187
01188
01189
01190
01191
01192
01193
01194
01195
01196
01197
01198
01199
01200
01201
01202
01203
01204
01205
01206
01207
01208
01209
01210
01211
01212
01213
01214
01215
01216
01217
01218
01219
01220
01221
01222
01223
01224
01225
01226
01227
01228
01229
01230
01231
01232
01233
01234
01235
01236 ret ::math::calculus::romberg_powerLawUpper ( type gamma , type f , type a , type b , type args ) {
01237 if { ![string is double -strict $gamma] } {
01238 return -code error [expectDouble $gamma]
01239 }
01240 if { $gamma <= 0.0 || $gamma >= 1.0 } {
01241 return -code error -errorcode [list romberg gammaTooBig] \
01242 "gamma must lie in the interval (0,1)"
01243 }
01244 if { ![string is double -strict $a] } {
01245 return -code error [expectDouble $a]
01246 }
01247 if { ![string is double -strict $b] } {
01248 return -code error [expectDouble $b]
01249 }
01250 if { $a >= $b } {
01251 return -code error "limits of integration out of order"
01252 }
01253 set f [lreplace $f 0 0 [uplevel 1 [list namespace which [lindex $f 0]]]]
01254 set onemgamma [expr { 1. - $gamma }]
01255 set f [list u_powerLawUpper $f \
01256 [expr { $gamma / $onemgamma }] \
01257 [expr { 1. / $onemgamma }] \
01258 $b]
01259
01260 set limit [expr { pow( $b - $a, $onemgamma ) }]
01261 set result {}
01262 foreach v [eval [linsert $args 0 romberg $f 0 $limit]] {
01263 lappend result [expr { $v / $onemgamma }]
01264 }
01265 return $result
01266
01267 }
01268
01269
01270
01271
01272
01273
01274
01275
01276
01277
01278
01279
01280
01281
01282
01283
01284
01285
01286
01287
01288 ret ::math::calculus::u_expUpper ( type f , type u ) {
01289 set cmd $f
01290 lappend cmd [expr { -log($u) }]
01291 set y [eval $cmd]
01292 return [expr { $y / $u }]
01293 }
01294
01295
01296
01297
01298
01299
01300
01301
01302
01303
01304
01305
01306
01307
01308
01309
01310
01311
01312
01313
01314 ret ::math::calculus::romberg_expUpper ( type f , type a , type b , type args ) {
01315 if { ![string is double -strict $a] } {
01316 return -code error [expectDouble $a]
01317 }
01318 if { ![string is double -strict $b] } {
01319 return -code error [expectDouble $b]
01320 }
01321 if { $a >= $b } {
01322 return -code error "limits of integration out of order"
01323 }
01324 set f [lreplace $f 0 0 [uplevel 1 [list namespace which [lindex $f 0]]]]
01325 set f [list u_expUpper $f]
01326 return [eval [linsert $args 0 \
01327 romberg $f [expr {exp(-$b)}] [expr {exp(-$a)}]]]
01328 }
01329
01330
01331
01332
01333
01334
01335
01336
01337
01338
01339
01340
01341
01342
01343
01344
01345
01346
01347
01348
01349 ret ::math::calculus::u_expLower ( type f , type u ) {
01350 set cmd $f
01351 lappend cmd [expr { log($u) }]
01352 set y [eval $cmd]
01353 return [expr { $y / $u }]
01354 }
01355
01356
01357
01358
01359
01360
01361
01362
01363
01364
01365
01366
01367
01368
01369
01370
01371
01372
01373
01374
01375 ret ::math::calculus::romberg_expLower ( type f , type a , type b , type args ) {
01376 if { ![string is double -strict $a] } {
01377 return -code error [expectDouble $a]
01378 }
01379 if { ![string is double -strict $b] } {
01380 return -code error [expectDouble $b]
01381 }
01382 if { $a >= $b } {
01383 return -code error "limits of integration out of order"
01384 }
01385 set f [lreplace $f 0 0 [uplevel 1 [list namespace which [lindex $f 0]]]]
01386 set f [list u_expLower $f]
01387 return [eval [linsert $args 0 \
01388 romberg $f [expr {exp($a)}] [expr {exp($b)}]]]
01389 }
01390
01391
01392
01393
01394
01395
01396
01397
01398
01399
01400
01401
01402
01403
01404
01405 ret ::math::calculus::regula_falsi ( type f , type xb , type xe , optional eps =1.0e-4 ) {
01406 if { $eps <= 0.0 } {
01407 return -code error "Relative error must be positive"
01408 }
01409
01410 set fb [$f $xb]
01411 set fe [$f $xe]
01412
01413 if { $fb * $fe > 0.0 } {
01414 return -code error "Interval must be chosen such that the \
01415 function has a different sign at the beginning than at the end"
01416 }
01417
01418 set max_error [expr {$eps * abs($xe-$xb)}]
01419 set interval [expr {abs($xe-$xb)}]
01420
01421 while { $interval > $max_error } {
01422 set coeff [expr {($fe-$fb)/($xe-$xb)}]
01423 set xi [expr {$xb-$fb/$coeff}]
01424 set fi [$f $xi]
01425
01426 if { $fi == 0.0 } {
01427 break
01428 }
01429 set diff1 [expr {abs($xe-$xi)}]
01430 set diff2 [expr {abs($xb-$xi)}]
01431 if { $diff1 > $diff2 } {
01432 set interval $diff2
01433 } else {
01434 set interval $diff1
01435 }
01436
01437 if { $fb*$fi < 0.0 } {
01438 set xe $xi
01439 set fe $fi
01440 } else {
01441 set xb $xi
01442 set fb $fi
01443 }
01444 }
01445
01446 return $xi
01447 }
01448