!Cash-Karp Runge Kutta
La teoria

module cashkarp
    implicit none
    integer,private,parameter::q=selected_real_kind(14,300)
    real(kind=q),private,parameter::n11776=11776,n16632=16632,n6237=6237,&
        n24948=24948,n1540=1540,n3262=3262,n37800=37800,n4600=4600, &
        n44275=44275,n6831=6831,n8855=8855,n9361=9361,n38500=38500,&
        n20125=20125,n27648=27648,n10240=10240,n39550=39550,&
        n148600=148600,n94675=94675,n7479=7479,n96768=96768,&
        n2530=2530,n10=10
    real(kind=q),private,parameter::a21=n11776*n16632
    real(kind=q),private,parameter::a31=n11776*n6237,a32=3*n11776*n6237
    real(kind=q),private,parameter::a41=n11776*n24948,a42=-3*n11776*n24948,&
        a43=4*n11776*n24948
    real(kind=q),private,parameter::a51=-11*n11776*n1540,a52=135*n11776*n1540,&
        a53=-140*n11776*n1540,a54=70*n11776*n1540
    real(kind=q),private,parameter::a61=n8855*n3262,a62=n8855*n37800,&
        a63=n8855*n4600,a64=n8855*n44275,a65=n8855*n6831
    real(kind=q),private,parameter::b51=n10240*n9361,b53=n10240*n38500,&
        b54=n10240*n20125,b56=n10240*n27648
    real(kind=q),private,parameter::b41=n2530*n39550,b43=n2530*n148600,&
        b44=n2530*n94675,b45=n2530*n7479,b46=n2530*n96768
    real(kind=q),private,parameter::b31=344565760,b33=-362700800,b34=997427200
    real(kind=q),private,parameter::b21=-1468938240,b22=n10*244823040
    real(kind=q),private,parameter::b11=979292160
    real(kind=q),private,parameter::c2=195858432,c3=293787648,c4=587575296
    real(kind=q),private,parameter::c5=979292160,c6=856880640
    !
    real(kind=q),dimension(:),private,allocatable::yp,ytn  !  corrisponde a k1.
    real(kind=q),dimension(:),private,allocatable::k2,k3,k4,k5,k6
    integer,private::nv=0
    private:: esempio_cashkarp
    public:: program_test,xcontrollo,cash_karp,hermite,xscashkarp
    !
contains
    subroutine xcontrollo(tot)
        !   Se il valore di tot e' zero non ci sono errori evidenti.
        real(kind=q),intent(out)::tot
        tot=abs(a21-c2)
        tot=tot+abs(a31+a32-c3)
        tot=tot+abs(a41+a42+a43-c4)
        tot=tot+abs(a51+a52+a53+a54-c5)
        tot=tot+abs(a61+a62+a63+a64+a65-c6)
        tot=tot+abs(b51+b53+b54+b56-b11)
        tot=tot+abs(b41+b43+b44+b45+b46-b11)
        tot=tot+abs(b31+b33+b34-b11)
        tot=tot+abs(b21+b22-b11)
        return
    end subroutine xcontrollo
    !
    subroutine xscashkarp(y,nm,not_ok)
        real(kind=q),intent(in),dimension(1:)::y
        integer,optional,intent(in)::nm
        logical,intent(out)::not_ok
        not_ok=.false.
        if(nv==0) then
            nv=size(y,1)
            if(present(nm)) then
                nv=max(nv,nm)
            end if
            allocate(ytn(nv),k2(nv),k3(nv),k4(nv))
            allocate(k5(nv),k6(nv),yp(nv))
            ytn(:)=0.0_q
            k2(:)=0.0_q
            k3(:)=0.0_q
            k4(:)=0.0_q
            k5(:)=0.0_q
            k6(:)=0.0_q
            yp(:)=0.0_q
        else if(size(y,1)>size(ytn,1)) then
            not_ok=.true.            
        end if
        return
    end subroutine xscashkarp
    !
    subroutine cash_karp(yn,nv,y,hv,f,yt)
        !
        !  nv   rappresenta il numero di variabili da integrare e deve
        !       valere almeno 2 perche' la prima variabile e' quella
        !       indipendente.
        !  hv   rappresenta l'ampiezza del passo di integrazione
        !  y(:) rappresenta il vettore delle variabili a inizio passo.
        !  yn(:) rappresenta il vettore delle variabili a fine passo.
        !  yt(:) rappresenta il vettore delle derivate prime a inizio passo
        !       ma e' opzionale perche', se non viene dato, viene 
        !       calcolato internamente. 
        !  ytn(:) rappresenta il vettore delle derivate prime a fine passo, che
        !       viene calcolato solo se yt(:) e' passato alla subroutine.
        !  f(z,w) e' la subroutine che calcola il vettore delle derivate prime
        !       quando è noto il vettore delle variabili.
        !  yp(:) rappresenta il vettore delle variabili a fine passo ma 
        !       calcolato con una precisione minore. Se yp(:) differisce
        !       eccessivamente da yn(:) bisogna rifare il passo riducendo
        !       il valore di hv. Si noti che yp non è in argomento poiche'
        !       viene data come memoria del modulo.
        !
        integer,intent(in)::nv
        real(kind=q),intent(in),dimension(1:)::y
        real(kind=q),intent(in),dimension(1:),optional::yt
        real(kind=q),intent(in)::hv
        real(kind=q),intent(out),dimension(1:)::yn
        interface
            subroutine f(z,w)
                integer,parameter::q=selected_real_kind(15,300)
                real(kind=q),intent(in),dimension(1:)::w
                real(kind=q),intent(out),dimension(1:)::z
            end subroutine f
        end interface
        real(kind=q)::h
        h=hv/b11
        if(present(yt)) then
            ytn(1:nv)=yt(1:nv)
        else
            call f(ytn,y)
        end if
        yp(1:nv)=y(1:nv)+(h*a21)*ytn(1:nv)
        call f(k2,yp)
        yp(1:nv)=y(1:nv)+(h*a31)*ytn(1:nv)+(h*a32)*k2(1:nv)
        call f(k3,yp)
        yp(1:nv)=y(1:nv)+(h*a41)*ytn(1:nv)+(h*a42)*k2(1:nv)+&
                 (h*a43)*k3(1:nv)
        call f(k4,yp)
        yp(1:nv)=y(1:nv)+(h*a51)*ytn(1:nv)+(h*a52)*k2(1:nv)+&
                 (h*a53)*k3(1:nv)+(h*a54)*k4(1:nv)
        call f(k5,yp)
        yp(1:nv)=y(1:nv)+(h*a61)*ytn(1:nv)+(h*a62)*k2(1:nv)+&
                 (h*a63)*k3(1:nv)+(h*a64)*k4(1:nv)+(h*a65)*k5(1:nv)
        call f(k6,yp)
        yn=y !   per trascrivere eventuali dati oltre i primi nv.
        yn(1:nv)=yn(1:nv)+(h*b51)*ytn(1:nv)+(h*b53)*k3(1:nv)+&
                 (h*b54)*k4(1:nv)+(h*b56)*k6(1:nv)
        !  Valore meno preciso usabile solo per controllo:
        yp(1:nv)=y(1:nv)+(h*b41)*ytn(1:nv)+(h*b43)*k3(1:nv)+&
                 (h*b44)*k4(1:nv)+(h*b45)*k5(1:nv)+(h*b46)*k6(1:nv)
        if(present(yt)) then
            call f(ytn,yn)
        end if       
        return
    end subroutine cash_karp
    !
    subroutine hermite(yora,y,yt,yn,ytn,theta,n)
        !
        !   interpolazione cubica (di Hermite) 
        !
        integer,intent(in)::n
        real(kind=q),intent(in)::theta
        real(kind=q),intent(in),dimension(1:)::y,yt,yn,ytn
        real(kind=q),intent(out),dimension(1:)::yora
        real(kind=q):: h,d1,d2,d3,d4,ds,dsq,tq
        !
        !   La variabile indipendente deve essere la prima.
        !
        h=yn(1)-y(1)
        tq=theta*theta
        ds=theta-1.0_q
        dsq=ds*ds
        d1=dsq*(2.0_q*theta+1.0_q)
        d2=dsq*theta*h
        d3=tq*(3.0_q-2.0_q*theta)
        d4=tq*ds*h
        yora(1:n)=d1*y(1:n)+d2*yt(1:n)+d3*yn(1:n)+d4*ytn(1:n)
        return
    end subroutine hermite
    !
    subroutine esempio_cashkarp(z,w)
        !
        !   Il tempo e' ottenuto risolvendo l'equazione differenziale:
        !   `t = 1 e il valore del tempo e' memorizzato nella prima
        !   componente di w. Il vettore z rappresenta il valore
        !   delle derivate prime corrispondenti ai valori delle
        !   variabili memorizzate nel vettore w.
        !
        real(kind=q),intent(in),dimension(1:)::w
        real(kind=q),intent(out),dimension(1:)::z
        real(kind=q)::r32
        !
        if(w(1)>=0.0_q) then
            z(1)=1                  ! `t 
            z(2)=w(4)               ! `x
            z(3)=w(5)               ! `y
            r32=w(2)*w(2)+w(3)*w(3)     
            r32=1.0_q/(r32*sqrt(r32))
            z(4)=-w(2)*r32          ! `vx
            z(5)=-w(3)*r32          ! `vy
        else
            !
            !  Per poter inizializzare il calcolo , w(1) deve essere 
            !  negativo e il vettore z(:) rappresenta allora il vettore 
            !  delle variabili e non il vettore delle loro derivate prime.
            !  Al tempo w(1).lt.0 il valore assoluto di w(1) indica
            !  l'eccentricita' che deve essere maggiore di 0 e inferiore
            !  ad 1.
            !  Non e' ammessa una eccentricita' esattamente nulla perche'
            !  allora w(1) non sarebbe negativo ma qualsiasi valore pur
            !  piccolissimo va bene...
            !
            r32=min(0.999999_q,abs(w(1)))
            z(1)=0.0_q                         !  t=0
            z(2)=1-r32                         !  1-e
            z(3)=0.0_q                         
            z(4)=0.0_q
            z(5)=sqrt((1.0_q+r32)/(1.0_q-r32)) !  sqrt((1+e)/(1-e))
            write(unit=*,fmt="(1a,4f18.10)") "     ",z(2:)
        end if
        return
        !
        !   Questo esempio e' il problema del moto di un pianeta attorno al sole
        !   che occupa l'origine delle coordinate. Il semiasse maggiore e' sempre
        !   uno, se il moto e' ellittico ossia se l'eccentricita', mai negativa,
        !   risulta essere inferiore ad 1. Se valesse esattamente 1 il moto
        !   sarebbe parabolico. Se e indica l'eccentricita', allora a t=w(1)=0 
        !   bisogna imporre queste condizioni iniziali: z(1)=0, z(2)=1-e, z(3)=0, 
        !   z(4)=0, z(5)=sqrt((1+e)/(1-e)) ossia il moto inizia al perielio. 
        !
    end subroutine esempio_cashkarp
    !
    subroutine program_test(ece,passie,girie,posizioni)
        real(kind=q),intent(in),optional::ece
        integer,intent(in),optional::passie,girie
        real(kind=q),dimension(0:,1:),intent(out),optional::posizioni
        integer::passi,giri,j,kp
        real(kind=q)::ec,hv,t0,s,x
        real(kind=q),dimension(5)::y,yn,yt,yora
        logical::boh
        !----
        write(unit=*,fmt=*)"Esegue alcune prove di funzionamento",&
           " di questo modulo (module cashkarp)"
        write(unit=*,fmt=*)" "
        call xcontrollo(x)
        write(unit=*,fmt=*)"1) Se vale 0.0 ",&
            "i coefficienti sono forse giusti : ",x
        if(present(ece)) then
            ec=ece
        else
            write(unit=*,fmt="(a)",advance="no") &
               " Assegna l'eccentricita' dell'orbita "
            read(unit=*,fmt=*) ec
            ec=max(1.0e-30_q,min(0.999999_q,ec))
        end if
        if(present(passie)) then
            passi=passie
        else
            write(unit=*,fmt="(a)",advance="no") &
               " Assegna il numero di passi"
            read(unit=*,fmt=*) passi
        end if
        if(present(girie)) then
            giri=girie
        else
            write(unit=*,fmt="(a)",advance="no") &
               " Assegna il numero di giri"
            read(unit=*,fmt=*) giri
        end if
        !
        if(present(posizioni)) then
            posizioni=0.0_q
        end if
        !
        call xscashkarp(y,5,boh)
        if(boh) then
            write(unit=*,fmt=*)"Fallisce l'inizializzazione di cashkarp: invia e stop!"
            read(unit=*,fmt=*)
            stop
        end if
        !   hv*passi deve corrispondere ad un dodicesimo di giro ossia π/6
        hv=2.0_q*atan(1.0_q)/(3.0_q*max(1,passi))
        yn(:)=0
        yn(1)=-ec
        call esempio_cashkarp(y,yn)
         !  Ogni j=passi dovrebbe aver fatto un dodicesimo di giro.
        do kp=1,giri*12-12
            do j=1,passi
                call cash_karp(yn,5,y,hv,esempio_cashkarp)
                y=yn
            end do
        end do
        do kp=1,12
            do j=1,passi
                call cash_karp(yn,5,y,hv,esempio_cashkarp)
                y=yn
            end do
            write(unit=*,fmt="(i5,4f18.10)") kp,y(2:5)
        end do
        write(unit=*,fmt=*)"Ora il tempo vale:      ",y(1)
        t0=giri*atan(1.0_q)*8.0_q
        write(unit=*,fmt=*)"Il tempo voluto sarebbe:",t0
        read(unit=*,fmt=*)
        !  
        if(.not.present(posizioni)) then
            return
        end if
        !
        !   Esegue questi calcoli solo se deve fare una tabella con 
        !   le posizioni distanziate un centesimo di tempo ovvero
        !   un centesimo di radiante, dato che fa un giro in 6.28318....
        !
        call esempio_cashkarp(yt,y)
        call cash_karp(yn,5,y,hv,esempio_cashkarp,yt) 
        do kp=0,630
            procedo:do
                if(yn(1)>t0) then
                    exit procedo
                end if 
                y=yn
                yt(1:5)=ytn(1:5)
                call cash_karp(yn,5,y,hv,esempio_cashkarp,yt) 
            end do procedo
            s=(t0-y(1))/(yn(1)-y(1))
            call hermite(yora,y,yt,yn,ytn,s,5)
            posizioni(kp,1)=yora(2)
            posizioni(kp,2)=yora(3)
            !  read(unit=*,fmt=*)
            t0=t0+0.01_q
        end do
        return
    end subroutine program_test
    !
end module cashkarp
!
!

Per verificare scommenta il program seguente:

!
!program xcashkarp
!    use cashkarp
!    implicit none
!    call program_test()
!    write(unit=*,fmt="(1x,1a)",advance="no") "Un invio per finire: "
!    read(unit=*,fmt=*)
!    stop
!end program xcashkarp
!