diff --git a/src/auxval.f90 b/src/auxval.f90 index fd03d9c..990c31e 100644 --- a/src/auxval.f90 +++ b/src/auxval.f90 @@ -1,108 +1,116 @@ SUBROUTINE auxval ! USE constants USE basic USE fields USE beam ! ! Set auxiliary values ! IMPLICIT NONE ! INTEGER:: i !________________________________________________________________________________ IF(mpirank .eq. 0) WRITE(*,'(a/)') '=== Set auxiliary values ===' !________________________________________________________________________________ ! ! Total number of intervals nr=sum(nnr) ! Normalisation constants qsim=pi*(plasmadim(2)-plasmadim(1))*(plasmadim(4)**2-plasmadim(3)**2)*n0*elchar/nplasma msim=abs(qsim)/elchar*me vnorm=vlight tnorm=1/sqrt(abs(n0)*elchar*elchar/me/eps_0) rnorm=vnorm*tnorm bnorm=B0 enorm=vlight*bnorm phinorm=enorm*rnorm ! Normalised boundary conditions potinn=potinn/phinorm potout=potout/phinorm ! Characteristic frequencies and normalised volume omegac=qsim/msim*B0 omegap=sqrt(elchar**2*abs(n0)/me/eps_0) V=pi*(plasmadim(2)-plasmadim(1))*(plasmadim(4)**2-plasmadim(3)**2)/rnorm/rnorm/rnorm IF(mpirank .eq. 0) THEN IF(omegap.GT.omegac) THEN WRITE(*,'(a,3(1pe12.3))') 'omegap, omegac, omegap/omegac', omegap, omegac, omegap/omegac ELSE WRITE(*,'(a,3(1pe12.3))') 'omegap, omegac, omegac/omegap', omegap, omegac, omegac/omegap END IF END IF ! Construction of the mesh rgrid in r and zgrid in z and its normalisation CALL mesh rgrid=rgrid/rnorm zgrid=zgrid/rnorm dz=dz/rnorm dr=dr/rnorm ! Define Zindex bounds for the MPI process in the case of sequential run ALLOCATE( Zbounds(0:mpisize), Nplocs_all(0:mpisize-1)) Zbounds(0) = Zgrid(0) Zbounds(mpisize) = Zgrid(nz) IF(mpisize.gt.1) THEN leftproc=mpirank-1 - IF(mpirank .eq. 0) leftproc = mpisize-1 + IF(mpirank .eq. 0 .AND. partperiodic) THEN + leftproc = mpisize-1 + ELSE IF (mpirank .eq. 0) THEN + leftproc=-1 + END IF rightproc = mpirank+1 - IF(mpirank .eq. mpisize-1) rightproc = 0 + IF(mpirank .eq. mpisize-1 .AND. partperiodic) THEN + rightproc = 0 + ELSE IF (mpirank .eq. mpisize-1) THEN + rightproc=-1 + END IF ELSE leftproc=-1 rightproc=-1 END IF WRITE(*,*) "mpirank, left, right", mpirank, leftproc, rightproc ! Auxiliary vectors ALLOCATE(vec1((nz+1)*(nr+1)),vec2((nr+1)*(nz+1))) DO i=0,nr vec1(i*(nz+1)+1:(i+1)*(nz+1))=zgrid!(0:nz) vec2(i*(nz+1)+1:(i+1)*(nz+1))=rgrid(i) END DO ALLOCATE(partdensity(nz*nr)) ALLOCATE(fluidur(nz*nr)) ALLOCATE(fluiduz(nz*nr)) ALLOCATE(fluiduthet(nz*nr)) ALLOCATE(Presstens(6,nz*nr)) !________________________________________________________________________________ CONTAINS !________________________________________________________________________________ SUBROUTINE mesh USE basic, ONLY: zgrid, rgrid, nr, nnr, nz, dr, dz, lz, radii INTEGER :: j ALLOCATE(zgrid(0:nz),rgrid(0:nr)) dz=(lz(2)-lz(1))/nz DO j=0,nz zgrid(j)=j*dz+lz(1) END DO dr(1)=(radii(2)-radii(1))/nnr(1) DO j=0,nnr(1) rgrid(j)=radii(1)+j*dr(1) END DO dr(2)=(radii(3)-radii(2))/nnr(2) DO j=1,nnr(2) rgrid(nnr(1)+j)=radii(2)+j*dr(2) END DO dr(3)=(radii(4)-radii(3))/nnr(3) DO j=1,nnr(3) rgrid(nnr(1)+nnr(2)+j)=radii(3)+j*dr(3) END DO END SUBROUTINE mesh !________________________________________________________________________________ END SUBROUTINE auxval diff --git a/src/basic_mod.f90 b/src/basic_mod.f90 index ed1b91b..3280d35 100644 --- a/src/basic_mod.f90 +++ b/src/basic_mod.f90 @@ -1,306 +1,308 @@ MODULE basic ! USE hashtable USE constants USE bsplines USE mumps_bsplines USE futils USE mpihelper IMPLICIT NONE ! ! Basic module for time dependent problems ! CHARACTER(len=80) :: label1, label2, label3, label4 ! ! BASIC Namelist ! LOGICAL :: nlres = .FALSE. ! Restart flag LOGICAL :: nlsave = .TRUE. ! Checkpoint (save) flag LOGICAL :: newres=.FALSE. ! New result HDF5 file LOGICAL :: nlxg=.FALSE. ! Show graphical interface Xgrafix INTEGER :: nrun=1 ! Number of time steps to run DOUBLE PRECISION :: job_time=3600.0 ! Time allocated to this job in seconds DOUBLE PRECISION :: tmax=100000.0 ! Maximum simulation time DOUBLE PRECISION :: extra_time=60.0 ! Extra time allocated DOUBLE PRECISION :: dt=1 ! Time step DOUBLE PRECISION :: time=0 ! Current simulation time (Init from restart file) ! ! Other basic global vars and arrays ! INTEGER :: jobnum ! Job number INTEGER :: step ! Calculation step of this run INTEGER :: cstep=0 ! Current step number (Init from restart file) LOGICAL :: nlend ! Signal end of run INTEGER :: ierr ! Integer used for MPI INTEGER :: it0d=1, it2d=1, itparts=1 ! Number of iterations between 0d, 2d, particles, values writes to hdf5 INTEGER :: ittext=1 ! Number of iterations between text outputs in the console INTEGER :: itrestart=10000 ! Number of iterations between save of restart.h5 file INTEGER :: itgraph ! Number of iterations between graphical interface updates INTEGER :: mpirank ! MPIrank of the current processus INTEGER :: mpisize ! Size of the MPI_COMM_WORLD communicator INTEGER :: rightproc, leftproc ! Rank of next and previous processor in the z decomposition ! ! List of logical file units INTEGER :: lu_in = 90 ! File duplicated from STDIN INTEGER :: lu_stop = 91 ! stop file, see subroutine TESEND ! ! HDF5 file CHARACTER(len=64) :: resfile = "results.h5" ! Main result file CHARACTER(len=64) :: rstfile = "restart.h5" ! Restart file INTEGER :: fidres ! FID for resfile INTEGER :: fidrst ! FID for restart file TYPE(BUFFER_TYPE) :: hbuf0 ! Hashtable for 0d var ! ! Plasma parameters - LOGICAL :: nlPhis=.TRUE. ! Calculate self consistent electric field flag - LOGICAL :: nlclassical=.FALSE. ! If true, solves the equation of motion classicaly + LOGICAL :: nlPhis= .TRUE. ! Calculate self consistent electric field flag + LOGICAL :: nlclassical= .FALSE. ! If true, solves the equation of motion classicaly LOGICAL :: nlperiod(2)=(/.false.,.false./) ! Set periodic splines on or off + LOGICAL :: partperiodic= .TRUE. ! Sets if the particles boundary conditions are periodic or open INTEGER :: nplasma ! Number of superparticles INTEGER :: nblock ! Number of intervals in Z for stable distribution initialisation DOUBLE PRECISION :: potinn,potout ! Potential at inner and outer metalic walls DOUBLE PRECISION :: B0 ! Max magnitude of magnetic field DOUBLE PRECISION, allocatable :: Bz(:), Br(:) ! Magnetic field components DOUBLE PRECISION, allocatable :: Athet(:) ! Magnetic potential TYPE(spline2d) :: splrz ! Spline at r and z DOUBLE PRECISION, allocatable :: Ez(:), Er(:) ! Electric field components DOUBLE PRECISION, allocatable :: pot(:) ! Electro static potential DOUBLE PRECISION :: radii(4) ! Inner and outer radius of cylinder and radii of fine mesh region DOUBLE PRECISION :: plasmadim(4) ! Zmin Zmax Rmin Rmax values for plasma particle loading INTEGER :: distribtype=1 ! Type of distribution function used to load the particles !1: gaussian, 2: Stable as defined in 4.95 of Davidson DOUBLE PRECISION :: H0=0, P0=0 ! Initial value of Hamiltonian and canonical angular momentum ! for distribution 2 DOUBLE PRECISION :: lz(2) ! Length of cylinder in z direction DOUBLE PRECISION :: n0 ! Average density of physical plasma DOUBLE PRECISION, ALLOCATABLE:: partdensity(:) ! Number of particles per gridcell computed every it2d DOUBLE PRECISION, DIMENSION(:), ALLOCATABLE:: fluidur, fluiduz, fluiduthet ! Fluid velocities per gridcell computed every it2d DOUBLE PRECISION, DIMENSION(:,:), ALLOCATABLE:: Presstens ! Pressure tensor per gridcell computed every it2d INTEGER :: nz, nr, nnr(3) ! Number of intervals in z and r DOUBLE PRECISION :: dz, dr(3) ! Cell size in z and r for each region DOUBLE PRECISION, allocatable :: zgrid(:), rgrid(:) ! Nodes positions DOUBLE PRECISION :: bnorm,enorm,vnorm,tnorm,rnorm,phinorm ! Normalization constants DOUBLE PRECISION :: qsim ! Charge of superparticles DOUBLE PRECISION :: msim ! Mass of superparticles INTEGER :: femorder(2) ! FEM order INTEGER :: ngauss(2) ! Number of gauss points LOGICAL :: nlppform =.TRUE. ! Argument of set_spline INTEGER, SAVE :: nrank(2) ! Number of splines in both directions DOUBLE PRECISION, DIMENSION(:), ALLOCATABLE, SAVE:: rhs ! right hand side of the poisson equation solver DOUBLE PRECISION :: omegac ! Cyclotronic frequency DOUBLE PRECISION :: omegap ! Plasma frequency DOUBLE PRECISION :: V ! Normalised volume DOUBLE PRECISION :: temp ! Initial temperature of plasma DOUBLE PRECISION :: Rcurv ! Magnetic field curvature coefficient DOUBLE PRECISION :: Width ! Distance between two magnetic mirrors DOUBLE PRECISION, DIMENSION(:), ALLOCATABLE :: Zbounds ! Index of bounds for local processus in Z direction TYPE(BASICDATA) :: bdata CONTAINS ! !================================================================================ SUBROUTINE basic_data ! ! Define basic data ! use mpihelper IMPLICIT NONE ! ! Local vars and arrays CHARACTER(len=256) :: line, inputfilename ! NAMELIST /BASIC/ job_time, extra_time, nrun, tmax, dt, nlres, nlsave, newres, nlxg, & & nplasma, potinn, potout, B0, lz, n0, nz, nnr, femorder, ngauss, & & nlppform, plasmadim, radii, temp, Rcurv, width, it0d, it2d, itparts, ittext, & - & resfile, rstfile, itgraph, nlPhis, distribtype, nblock, nlclassical, H0, P0 + & resfile, rstfile, itgraph, nlPhis, distribtype, nblock, nlclassical, H0, P0, partperiodic !________________________________________________________________________________ ! 1. Process Standard Input File ! IF(mpirank .eq. 0) THEN IF(COMMAND_ARGUMENT_COUNT().NE.1)THEN WRITE(*,*)'ERROR, ONE COMMAND-LINE ARGUMENT REQUIRED, STOPPING' STOP ENDIF CALL GET_COMMAND_ARGUMENT(1,inputfilename) OPEN(UNIT=lu_in,FILE=trim(inputfilename)) !DO ! READ(*,'(a)', END=110) line ! WRITE(lu_in, '(a)') TRIM(line) !END DO !110 CONTINUE !REWIND(lu_in) !________________________________________________________________________________ ! 1. Label the run ! READ(lu_in,'(a)') label1 READ(lu_in,'(a)') label2 READ(lu_in,'(a)') label3 READ(lu_in,'(a)') label4 ! WRITE(*,'(12x,a/)') label1(1:len_trim(label1)) WRITE(*,'(12x,a/)') label2(1:len_trim(label2)) WRITE(*,'(12x,a/)') label3(1:len_trim(label3)) WRITE(*,'(12x,a/)') label4(1:len_trim(label4)) !________________________________________________________________________________ ! 2. Read in basic data specific to run ! READ(lu_in,basic) WRITE(*,basic) bdata=BASICDATA( nlres, nlsave, newres, nlxg, nlppform, nlPhis, nlclassical, & & nplasma, nz, it0d, it2d, itparts, itgraph, distribtype, nblock, & & nrun, job_time, extra_time, tmax, dt, potinn, potout, B0, n0, temp, & - & Rcurv, width, H0, P0, femorder, ngauss, nnr, lz, radii, plasmadim, resfile ) + & Rcurv, width, H0, P0, femorder, ngauss, nnr, lz, radii, plasmadim, resfile, partperiodic) END IF CALL init_mpitypes ! initialize all mpi types that will be needed in the simulation CALL MPI_Bcast(bdata, 1, basicdata_type, 0, MPI_COMM_WORLD, ierr) CALL readbdata ! END SUBROUTINE basic_data !================================================================================ SUBROUTINE daytim(str) ! ! Print date and time ! IMPLICIT NONE ! CHARACTER(len=*), INTENT(in) :: str ! ! Local vars and arrays CHARACTER(len=16) :: d, t, dat, functime !________________________________________________________________________________ ! CALL DATE_AND_TIME(d,t) dat=d(7:8) // '/' // d(5:6) // '/' // d(1:4) functime=t(1:2) // ':' // t(3:4) // ':' // t(5:10) WRITE(*,'(a,1x,a,1x,a)') str, dat(1:10), functime(1:12) ! END SUBROUTINE daytim !================================================================================ SUBROUTINE timera(cntrl, str, eltime) ! ! Timers (cntrl=0/1 to Init/Update) ! IMPLICIT NONE INTEGER, INTENT(in) :: cntrl CHARACTER(len=*), INTENT(in) :: str DOUBLE PRECISION, OPTIONAL, INTENT(out) :: eltime ! INTEGER, PARAMETER :: ncmax=128 INTEGER, SAVE :: icall=0, nc=0 DOUBLE PRECISION, SAVE :: startt0=0.0 CHARACTER(len=16), SAVE :: which(ncmax) INTEGER :: lstr, found, i DOUBLE PRECISION :: seconds DOUBLE PRECISION, DIMENSION(ncmax), SAVE :: startt = 0.0, endt = 0.0 !________________________________________________________________________________ IF( icall .EQ. 0 ) THEN icall = icall+1 startt0 = seconds() END IF lstr = LEN_TRIM(str) IF( lstr .GT. 0 ) found = loc(str) !________________________________________________________________________________ ! SELECT CASE (cntrl) ! CASE(-1) ! Current wall time IF( PRESENT(eltime) ) THEN eltime = seconds() - startt0 ELSE WRITE(*,'(/a,a,1pe10.3/)') "++ ", ' Wall time used so far = ', seconds() - startt0 END IF ! CASE(0) ! Init Timer IF( found .EQ. 0 ) THEN ! Called for the 1st time for 'str' nc = nc+1 which(nc) = str(1:lstr) found = nc END IF startt(found) = seconds() ! CASE(1) ! Update timer endt(found) = seconds() - startt(found) IF( PRESENT(eltime) ) THEN eltime = endt(found) ELSE WRITE(*,'(/a,a,1pe10.3/)') "++ "//str, ' wall clock time = ', endt(found) END IF ! CASE(2) ! Update and reset timer endt(found) = endt(found) + seconds() - startt(found) startt(found) = seconds() IF( PRESENT(eltime) ) THEN eltime = endt(found) END IF ! CASE(9) ! Display all timers IF( nc .GT. 0 ) THEN WRITE(*,'(a)') "Timer Summary" WRITE(*,'(a)') "=============" DO i=1,nc WRITE(*,'(a20,2x,2(1pe12.3))') TRIM(which(i))//":", endt(i) END DO END IF ! END SELECT ! CONTAINS INTEGER FUNCTION loc(str) CHARACTER(len=*), INTENT(in) :: str INTEGER :: i, ind loc = 0 DO i=1,nc ind = INDEX(which(i), str(1:lstr)) IF( ind .GT. 0 .AND. LEN_TRIM(which(i)) .EQ. lstr ) THEN loc = i EXIT END IF END DO END FUNCTION loc END SUBROUTINE timera !================================================================================ SUBROUTINE readbdata nlres = bdata%nlres nlsave = bdata%nlsave newres = bdata%newres nlxg = bdata%nlxg nlppform = bdata%nlppform nlPhis = bdata%nlPhis nlclassical = bdata%nlclassical nplasma = bdata%nplasma nz = bdata%nz it0d = bdata%it0d it2d = bdata%it2d itparts = bdata%itparts itgraph = bdata%itgraph distribtype = bdata%distribtype nblock = bdata%nblock nrun = bdata%nrun job_time = bdata%job_time extra_time = bdata%extra_time tmax = bdata%tmax dt = bdata%dt potinn = bdata%potinn potout = bdata%potout B0 = bdata%B0 n0 = bdata%n0 temp = bdata%temp Rcurv = bdata%Rcurv width = bdata%width H0 = bdata%H0 P0 = bdata%P0 femorder = bdata%femorder ngauss = bdata%ngauss nnr = bdata%nnr lz = bdata%lz radii = bdata%radii plasmadim = bdata%plasmadim resfile = bdata%resfile +partperiodic= bdata%partperiodic END SUBROUTINE readbdata !================================================================================ END MODULE basic diff --git a/src/beam_mod.f90 b/src/beam_mod.f90 index 1162483..65a54d4 100644 --- a/src/beam_mod.f90 +++ b/src/beam_mod.f90 @@ -1,1323 +1,1329 @@ !------------------------------------------------------------------------------ ! EPFL/Swiss Plasma Center !------------------------------------------------------------------------------ ! ! MODULE: beam ! !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> Module responsible for loading, advancing and computing the necessary diagnostics for the simulated particles. ! !------------------------------------------------------------------------------ MODULE beam ! USE constants USE mpi USE mpihelper USE basic, ONLY: mpirank, mpisize IMPLICIT NONE !! Stores the particles properties for the run. TYPE particles INTEGER :: Nptot !! Local number of simulated particles INTEGER, DIMENSION(:), ALLOCATABLE :: Rindex !! Index in the electric potential grid for the R direction INTEGER, DIMENSION(:), ALLOCATABLE :: Zindex !! Index in the electric potential grid for the Z direction INTEGER, DIMENSION(:), ALLOCATABLE :: partindex !! Index of the particle to be able to follow it when it goes from one MPI host to the other DOUBLE PRECISION, DIMENSION(:), ALLOCATABLE :: & & R,Z,THET, & & WZ, WR, & & pot, Er, Ez DOUBLE PRECISION, DIMENSION(:),POINTER:: & & UR, URold, & & UTHET, UTHETold, & & UZ, UZold, & & Gamma, Gammaold END TYPE particles ! TYPE(particles) :: parts ! Storage for all the particles SAVE :: parts TYPE(particle), DIMENSION(:), ALLOCATABLE:: rrecvpartbuff, lrecvpartbuff, rsendpartbuff, lsendpartbuff ! buffer to send and receive particle from left and right processes ! Diagnostics (scalars) DOUBLE PRECISION :: ekin ! Total kinetic energz (J) DOUBLE PRECISION :: epot ! Total potential energy (J) DOUBLE PRECISION :: etot, etot0 ! Current and Initial total energy (J) INTEGER, DIMENSION(7), SAVE :: ireducerequest=MPI_REQUEST_NULL ! INTEGER, DIMENSION(:), ALLOCATABLE :: Nplocs_all !F.B. get local numbers of particles LOGICAL:: collected=.false. !Stores if the particles data have been collected to root process this timestep ! CONTAINS !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> !> @brief !> Allocate the memory for the particles variable storing the particles quantities. ! !> @param[inout] p the particles variable needing to be allocated. !> @param[in] nparts the maximum number of particles that will be stored in this variable !--------------------------------------------------------------------------- SUBROUTINE creat_parts(p, nparts) TYPE(particles) :: p INTEGER, INTENT(in) :: nparts IF (.NOT. ALLOCATED(p%Z) ) THEN p%Nptot = nparts ALLOCATE(p%Z(nparts)) ALLOCATE(p%R(nparts)) ALLOCATE(p%THET(nparts)) ALLOCATE(p%WZ(nparts)) ALLOCATE(p%WR(nparts)) ALLOCATE(p%UR(nparts)) ALLOCATE(p%UZ(nparts)) ALLOCATE(p%UTHET(nparts)) ALLOCATE(p%URold(nparts)) ALLOCATE(p%UZold(nparts)) ALLOCATE(p%UTHETold(nparts)) ALLOCATE(p%Gamma(nparts)) ALLOCATE(p%Rindex(nparts)) ALLOCATE(p%Zindex(nparts)) ALLOCATE(p%partindex(nparts)) ALLOCATE(p%pot(nparts)) ALLOCATE(p%Er(nparts)) ALLOCATE(p%Ez(nparts)) ALLOCATE(p%GAMMAold(nparts)) parts%URold=0 parts%UZold=0 parts%UTHETold=0 parts%rindex=0 parts%zindex=0 parts%WR=0 parts%WZ=0 parts%UR=0 parts%UZ=0 parts%UTHET=0 parts%Z=0 parts%R=0 parts%THET=0 parts%Gamma=1 parts%Er=0 parts%Ez=0 parts%pot=0 parts%gammaold=1 END IF END SUBROUTINE creat_parts !______________________________________________________________________________ SUBROUTINE load_parts ! Loads the particles at the beginning of the simulation and create the parts variable if necessary USE basic, ONLY: nplasma, mpirank, ierr, distribtype, mpisize, nlclassical, Rcurv USE mpi INTEGER:: i DOUBLE PRECISION, DIMENSION(:), ALLOCATABLE :: VZ, VR, VTHET ALLOCATE(VZ(nplasma), VR(nplasma), VTHET(nplasma)) ! Select case to define the type of distribution SELECT CASE(distribtype) CASE(1) ! Gaussian distribution in V and uniform in R CALL loaduniformRZ(VR, VZ, VTHET) CASE(2) !Stable distribution from Davidson 4.95 p.119 CALL loadDavidson(VR, VZ, VTHET, lodunir) CASE(3) !Stable distribution from Davidson 4.95 p.119 but with distribution in R as 1/R**2 CALL loadDavidson(VR, VZ, VTHET, lodinvr) CASE(4) !Stable distribution from Davidson 4.95 p.119 but with gaussian distribution in R CALL loadDavidson(VR, VZ, VTHET, lodgausr) CASE(5) !Stable distribution from Davidson 4.95 p.119 with gaussian in V computed from v_th given by temp CALL loadDavidson(VR, VZ, VTHET, lodunir) CASE(6) ! Uniform distribution in R and Z and Gaussian distribution in V with Vz Random sampling) ! INTEGER, INTENT(in) :: nbase DOUBLE PRECISION, INTENT(out) :: y(:) INTEGER :: n, i ! n = SIZE(y) ! SELECT CASE (nbase) CASE(0) CALL RANDOM_NUMBER(y) CASE(1) DO i=1,n y(i) = (i-0.5_db)/n END DO CASE(2:) DO i=1,n y(i) = rev(nbase,i) END DO CASE default WRITE(*,'(a,i5)') 'Invalid value of NBASE =', nbase END SELECT ! END SUBROUTINE loduni !________________________________________________________________________________ SUBROUTINE lodgaus(nbase, y) ! ! Sample y from the Gauss distributrion ! DOUBLE PRECISION, INTENT(out) :: y(:) INTEGER, INTENT(in) :: nbase ! INTEGER :: n, i, iflag DOUBLE PRECISION :: r(SIZE(y)) DOUBLE PRECISION :: t ! DOUBLE PRECISION :: c0, c1, c2 DOUBLE PRECISION :: d1, d2, d3 DATA c0, c1, c2/2.515517_db, 0.802853_db, 0.010328_db/ DATA d1, d2, d3/1.432788_db, 0.189269_db, 0.001308_db/ ! n = SIZE(y) CALL loduni(nbase, r) r = 1.0E-5_db + 0.99998_db*r ! DO i=1,n iflag = 1 IF (r(i) .GT. 0.5_db) THEN r(i) = 1.0_db - r(i) iflag = -1 END IF t = SQRT(LOG(1.0_db/(r(i)*r(i)))) y(i) = t - (c0+c1*t+c2*t**2) / (1.0_db+d1*t+d2*t**2+d3*t**3) y(i) = y(i) * iflag END DO y = y - SUM(y)/REAL(n,db) END SUBROUTINE lodgaus !________________________________________________________________________________ SUBROUTINE lodinvr(nbase, y, ra, rb) ! ! Sample y from the distribution f=1/(r)H(r-ra)H(rb-r) for where H is the heavyside function ! DOUBLE PRECISION, INTENT(out) :: y(:) INTEGER, INTENT(in) :: nbase DOUBLE PRECISION, INTENT(in) :: rb, ra ! DOUBLE PRECISION :: r(SIZE(y)) ! CALL loduni(nbase, r) r=r*log(rb/ra) y=ra*exp(r) END SUBROUTINE lodinvr !________________________________________________________________________________ SUBROUTINE lodunir(nbase, y, ra, rb) DOUBLE PRECISION, INTENT(out) :: y(:) INTEGER, INTENT(in) :: nbase DOUBLE PRECISION, INTENT(in) :: rb, ra ! DOUBLE PRECISION :: r(SIZE(y)) ! CALL loduni(nbase, r) y=ra+(rb-ra)*r END SUBROUTINE lodunir !________________________________________________________________________________ SUBROUTINE lodgausr(nbase, y, ra, rb) DOUBLE PRECISION, INTENT(out) :: y(:) INTEGER, INTENT(in) :: nbase DOUBLE PRECISION, INTENT(in) :: rb, ra ! DOUBLE PRECISION :: r(SIZE(y)) ! CALL lodgaus(nbase, r) y=0.5*(rb+ra)+ 0.1*(rb-ra)*r END SUBROUTINE lodgausr !________________________________________________________________________________ REAL(db) FUNCTION rev(nbase,i) ! ! Return an element of the Hammersley's sequence ! INTEGER, INTENT(IN) :: nbase ! Base of the Hammersley's sequence (IN) INTEGER, INTENT(IN) :: i ! Index of the sequence (IN) ! ! Local vars INTEGER :: j1, j2 REAL(db) :: xs, xsi !----------------------------------------------------------------------- xs = 0._db xsi = 1.0_db j2 = i DO xsi = xsi/nbase j1 = j2/nbase xs = xs + (j2-nbase*j1)*xsi j2 = j1 IF( j2.LE.0 ) EXIT END DO rev = xs END FUNCTION rev !________________________________________________________________________________ !Modified Bessel functions of the first kind of the first order FUNCTION bessi1(x) DOUBLE PRECISION :: bessi1,x DOUBLE PRECISION :: ax DOUBLE PRECISION p1,p2,p3,p4,p5,p6,p7,q1,q2,q3,q4,q5,q6,q7,q8,q9,y SAVE p1,p2,p3,p4,p5,p6,p7,q1,q2,q3,q4,q5,q6,q7,q8,q9 DATA p1,p2,p3,p4,p5,p6,p7/0.5d0,0.87890594d0,0.51498869d0,0.15084934d0,0.2658733d-1,0.301532d-2,0.32411d-3/ DATA q1,q2,q3,q4,q5,q6,q7,q8,q9 /0.39894228d0, -0.3988024d-1, -0.362018d-2, 0.163801d-2,-0.1031555d-1, & & 0.2282967d-1, -0.2895312d-1, 0.1787654d-1,-0.420059d-2/ if (abs(x).lt.3.75) then y=(x/3.75)**2 bessi1=x*(p1+y*(p2+y*(p3+y*(p4+y*(p5+y*(p6+y*p7)))))) else ax=abs(x) y=3.75/ax bessi1=(exp(ax)/sqrt(ax))*(q1+y*(q2+y*(q3+y*(q4+y*(q5+y*(q6+y*(q7+y*(q8+y*q9)))))))) if(x.lt.0.)bessi1=-bessi1 endif return END FUNCTION bessi1 !________________________________________________________________________________ SUBROUTINE destroy_parts(p) TYPE(particles) :: p p%nptot=0 IF(ALLOCATED(p%Z)) DEALLOCATE(p%Z) IF(ALLOCATED(p%R)) DEALLOCATE(p%R) IF(ALLOCATED(p%THET)) DEALLOCATE(p%THET) IF(ALLOCATED(p%WZ)) DEALLOCATE(p%WZ) IF(ALLOCATED(p%WR)) DEALLOCATE(p%WR) IF(ASSOCIATED(p%UR)) DEALLOCATE(p%UR) IF(Associated(p%URold)) DEALLOCATE(p%URold) IF(Associated(p%UZ)) DEALLOCATE(p%UZ) IF(Associated(p%UZold)) DEALLOCATE(p%UZold) IF(Associated(p%UTHET)) DEALLOCATE(p%UTHET) IF(Associated(p%UTHETold)) DEALLOCATE(p%UTHETold) IF(Associated(p%Gamma)) DEALLOCATE(p%Gamma) IF(Associated(p%Gammaold)) DEALLOCATE(p%Gammaold) IF(ALLOCATED(p%Rindex)) DEALLOCATE(p%Rindex) IF(ALLOCATED(p%Zindex)) DEALLOCATE(p%Zindex) IF(ALLOCATED(p%partindex)) DEALLOCATE(p%partindex) END SUBROUTINE !________________________________________________________________________________ SUBROUTINE clean_beam ! CALL destroy_parts(parts) ! ! END SUBROUTINE clean_beam !________________________________________________________________________________ RECURSIVE SUBROUTINE quicksortparts(leftlimit, rightlimit) ! Sorts the particle according to their Z position using quicksort algorithm INTEGER,INTENT(IN):: leftlimit, rightlimit DOUBLE PRECISION:: pivot INTEGER::i, cnt, mid IF(leftlimit .ge. rightlimit) RETURN mid=(leftlimit+rightlimit)/2 IF(parts%Z(mid).lt.parts%Z(leftlimit)) CALL exchange_parts(leftlimit,mid) IF(parts%Z(rightlimit).lt.parts%Z(leftlimit)) CALL exchange_parts(leftlimit,rightlimit) IF(parts%Z(mid).lt.parts%Z(rightlimit)) CALL exchange_parts(rightlimit,mid) ! Store the pivot point for comparison pivot=parts%Z(rightlimit) cnt=leftlimit ! Move all parts with Z smaller than pivot to the left of pivot DO i=leftlimit, rightlimit IF(parts%Z(i) .le. pivot) THEN CALL exchange_parts(i,cnt) cnt=cnt+1 END IF END DO CALL quicksortparts(leftlimit,cnt-2) CALL quicksortparts(cnt,rightlimit) END SUBROUTINE quicksortparts !________________________________________________________________________________ SUBROUTINE swappointer( pointer1, pointer2) DOUBLE PRECISION, DIMENSION(:), POINTER, INTENT(inout):: pointer1, pointer2 DOUBLE PRECISION, DIMENSION(:), POINTER:: temppointer temppointer=>pointer1 pointer1=>pointer2 pointer2=>temppointer END SUBROUTINE swappointer !_______________________________________________________________________________ SUBROUTINE loaduniformRZ(VR,VZ,VTHET) USE basic, ONLY: plasmadim, rnorm, temp, vnorm USE constants, ONLY: me, kb, elchar DOUBLE PRECISION, DIMENSION(:), ALLOCATABLE, INTENT(INOUT)::VZ, VR, VTHET DOUBLE PRECISION:: vth ! Initial distribution in z with normalisation CALL loduni(1,parts%Z(:)) parts%Z(:)=(plasmadim(1)+(plasmadim(2)-plasmadim(1))*parts%Z(:))/rnorm ! Initial distribution in r with normalisation CALL loduni(2,parts%R(:)) parts%R=(plasmadim(3)+parts%R(:)*(plasmadim(4)-plasmadim(3)))/rnorm ! Initial velocities distribution vth=sqrt(3*kb*temp/me)/vnorm !thermal velocity CALL lodgaus(3,VZ(:)) CALL lodgaus(5,VR(:)) CALL lodgaus(7,VTHET(:)) VZ=VZ*vth VR=VR*vth VTHET=VTHET*vth END SUBROUTINE loaduniformRZ !_______________________________________________________________________________ SUBROUTINE loadDavidson(VR,VZ,VTHET,lodr) USE constants, ONLY: me, kb, elchar USE basic, ONLY: nplasma, rnorm, plasmadim, distribtype, H0, P0, Rcurv, B0, width, qsim, msim, vnorm, & & omegac, zgrid, nz, rnorm, V, n0, nblock, temp interface subroutine lodr(nbase,y,ra,rb) DOUBLE PRECISION, INTENT(out) :: y(:) INTEGER, INTENT(in) :: nbase DOUBLE PRECISION, INTENT(in) :: rb, ra end subroutine end interface DOUBLE PRECISION, DIMENSION(:), ALLOCATABLE, INTENT(INOUT)::VZ, VR, VTHET DOUBLE PRECISION, DIMENSION(:), ALLOCATABLE::ra, rb DOUBLE PRECISION :: r0, athetpos, deltar2, rg, zg, halfLz, Mirrorratio, Le, Pcomp, Acomp, gamma, VOL, vth INTEGER :: i, j, n, blockstart, blockend, addedpart, remainparts INTEGER, DIMENSION(:), ALLOCATABLE :: blocksize Allocate(ra(nblock),rb(nblock)) !r0=(plasmadim(4)+plasmadim(3))/2 r0=sqrt(2*H0/(me*omegac**2)) halfLz=(zgrid(nz)+zgrid(0))/2 MirrorRatio=(Rcurv-1)/(Rcurv+1) DO n=1,nblock ! Compute limits in radius and load radii for each part Le=(plasmadim(2)-plasmadim(1))/nblock*(n-0.5)-halfLz*rnorm+plasmadim(1) deltar2=1-MirrorRatio*cos(2*pi*Le/width) rb(n)=r0*(1+sqrt(1-abs(omegac)*P0*deltar2/H0))/deltar2 ra(n)=r0*(1-sqrt(1-abs(omegac)*P0*deltar2/H0))/deltar2 END DO VOL=SUM(2*pi*ra*(rb-ra)*(plasmadim(2)-plasmadim(1))/nblock) qsim=VOL*n0*elchar/nplasma msim=abs(qsim)/elchar*me V=VOL/rnorm**3 !H0=me*omegac**2*r0**2*0.5 gamma=1/sqrt(1-omegac**2*r0**2/vlight**2) !P0=H0/omegac blockstart=1 blockend=0 ALLOCATE(blocksize(nblock)) DO n=1,nblock blocksize(n)=nplasma/VOL*2*pi*ra(n)*(rb(n)-ra(n))*(plasmadim(2)-plasmadim(1))/nblock END DO remainparts=parts%Nptot-SUM(blocksize) addedpart=1 n=nblock/2 j=1 DO WHILE(remainparts .GT. 0) blocksize(n)=blocksize(n)+addedpart remainparts=remainparts-addedpart n=n+j j=-1*(j+SIGN(1,j)) END DO DO n=1,nblock blockstart=blockend+1 blockend=MIN(blockstart+blocksize(n)-1,parts%Nptot) ! Initial distribution in z with normalisation between magnetic mirrors CALL loduni(1,parts%Z(blockstart:blockend)) parts%Z(blockstart:blockend)=(plasmadim(2)-plasmadim(1))/rnorm/nblock*((n-1)+parts%Z(blockstart:blockend))+plasmadim(1)/rnorm CALL lodr(2,parts%R(blockstart:blockend),ra(n), rb(n)) parts%R(blockstart:blockend)=parts%R(blockstart:blockend)/rnorm END DO IF(distribtype .eq. 5) THEN ! Initial velocities distribution vth=sqrt(3*kb*temp/me)/vnorm !thermal velocity CALL lodgaus(3,VZ(:)) CALL lodgaus(5,VR(:)) CALL lodgaus(7,VTHET(:)) VZ=VZ*vth/4 VR=VR*8*vth VTHET=VTHET*8*vth ELSE ! Load velocities theta velocity ! Loading of r and z velocity is done in adapt_vinit to have ! access to parts%pot DO i=1,parts%Nptot ! Interpolation for Magnetic potential rg=parts%R(i)*rnorm zg=(parts%Z(i)-halfLz)*rnorm Athetpos=0.5*B0*(rg - width/pi*MirrorRatio*bessi1(2*pi*rg/width)*COS(2*pi*zg/width)) Pcomp=abs(P0/rg/me) Acomp=-SIGN(elchar/me*Athetpos,qsim) VTHET(i)=SIGN(MIN(abs(Pcomp+Acomp),sqrt(2*H0/me)),Pcomp+Acomp) END DO VTHET=VTHET/vnorm VZ=0._db VR=0._db END IF END SUBROUTINE loadDavidson END MODULE beam diff --git a/src/mpihelper_mod.f90 b/src/mpihelper_mod.f90 index 75f1e9d..c9b752f 100644 --- a/src/mpihelper_mod.f90 +++ b/src/mpihelper_mod.f90 @@ -1,140 +1,145 @@ MODULE mpihelper USE constants USE mpi IMPLICIT NONE TYPE BASICDATA LOGICAL :: nlres, nlsave, newres, nlxg, nlppform, nlPhis, nlclassical INTEGER :: nplasma, nz, it0d, it2d, itparts, & & itgraph, distribtype, nblock, nrun DOUBLE PRECISION :: job_time, extra_time, tmax, dt, & & potinn, potout, B0, n0, temp, & & Rcurv, width, H0, P0 INTEGER, DIMENSION(2) :: femorder, ngauss INTEGER, DIMENSION(3) :: nnr DOUBLE PRECISION, DIMENSION(2):: lz DOUBLE PRECISION, DIMENSION(4):: radii, plasmadim CHARACTER(len=64) :: resfile= "results.h5" + LOGICAL :: partperiodic END TYPE BASICDATA TYPE particle INTEGER :: Rindex, Zindex, partindex DOUBLE PRECISION :: R, Z, THET,& & UZ, UR, UTHET, & & Gamma END TYPE particle INTEGER, SAVE :: basicdata_type INTEGER, SAVE :: particle_type INTEGER, DIMENSION(:), ALLOCATABLE, SAVE :: rhscol_type INTEGER, SAVE:: rhsoverlap_type INTEGER, DIMENSION(:), ALLOCATABLE, SAVE:: rhsrequests, rhsoverlaprequests INTEGER:: rhscommtag=10, rhsoverlapcommtag = 15 DOUBLE PRECISION, DIMENSION(:), ALLOCATABLE, SAVE:: rhsoverlap CONTAINS !========================================================================= SUBROUTINE init_mpitypes CALL init_particlempi CALL init_basicdatampi END SUBROUTINE init_mpitypes !========================================================================= SUBROUTINE init_particlempi() INTEGER :: nblock = 10 INTEGER:: blocklength(10) INTEGER(kind=MPI_ADDRESS_KIND):: displs(10), displ0 INTEGER, DIMENSION(10) :: types TYPE(particle) :: part INTEGER:: ierr CALL mpi_get_address(part%Rindex, displs(1), ierr) CALL mpi_get_address(part%Zindex, displs(2), ierr) CALL mpi_get_address(part%partindex, displs(3), ierr) types(1:3)=MPI_INTEGER CALL mpi_get_address(part%R, displs(4), ierr) CALL mpi_get_address(part%Z, displs(5), ierr) CALL mpi_get_address(part%THET, displs(6), ierr) CALL mpi_get_address(part%UZ, displs(7), ierr) CALL mpi_get_address(part%UR, displs(8), ierr) CALL mpi_get_address(part%UTHET, displs(9), ierr) CALL mpi_get_address(part%GAMMA, displs(10), ierr) types(4:10)=MPI_DOUBLE_PRECISION blocklength(1:10) = 1 CALL mpi_get_address(part, displ0, ierr) displs=displs-displ0 CALL MPI_Type_create_struct(nblock, blocklength, displs, types, particle_type, ierr) CALL MPI_Type_commit(particle_type,ierr) END SUBROUTINE init_particlempi !========================================================================= SUBROUTINE init_basicdatampi() - INTEGER :: nblock = 36 - INTEGER:: blocklength(36) - INTEGER(kind=MPI_ADDRESS_KIND):: displs(36), displ0 - INTEGER, DIMENSION(36) :: types + INTEGER :: nblock = 37 + INTEGER:: blocklength(37) + INTEGER(kind=MPI_ADDRESS_KIND):: displs(37), displ0 + INTEGER, DIMENSION(37) :: types TYPE(BASICDATA) :: bdata INTEGER:: ierr CALL mpi_get_address(bdata%nlres, displs(1), ierr) CALL mpi_get_address(bdata%nlsave, displs(2), ierr) CALL mpi_get_address(bdata%newres, displs(3), ierr) CALL mpi_get_address(bdata%nlxg, displs(4), ierr) CALL mpi_get_address(bdata%nlppform, displs(5), ierr) CALL mpi_get_address(bdata%nlPhis, displs(6), ierr) CALL mpi_get_address(bdata%nlclassical, displs(7), ierr) types(1:7)=MPI_LOGICAL CALL mpi_get_address(bdata%nplasma, displs(8), ierr) CALL mpi_get_address(bdata%nz, displs(9), ierr) CALL mpi_get_address(bdata%it0d, displs(10), ierr) CALL mpi_get_address(bdata%it2d, displs(11), ierr) CALL mpi_get_address(bdata%itparts, displs(12), ierr) CALL mpi_get_address(bdata%itgraph, displs(13), ierr) CALL mpi_get_address(bdata%distribtype, displs(14), ierr) CALL mpi_get_address(bdata%nblock, displs(15), ierr) CALL mpi_get_address(bdata%nrun, displs(16), ierr) types(8:16)=MPI_INTEGER CALL mpi_get_address(bdata%job_time, displs(17), ierr) CALL mpi_get_address(bdata%extra_time, displs(18), ierr) CALL mpi_get_address(bdata%tmax, displs(19), ierr) CALL mpi_get_address(bdata%dt, displs(20), ierr) CALL mpi_get_address(bdata%potinn, displs(21), ierr) CALL mpi_get_address(bdata%potout, displs(22), ierr) CALL mpi_get_address(bdata%B0, displs(23), ierr) CALL mpi_get_address(bdata%n0, displs(24), ierr) CALL mpi_get_address(bdata%temp, displs(25), ierr) CALL mpi_get_address(bdata%Rcurv, displs(26), ierr) CALL mpi_get_address(bdata%width, displs(27), ierr) CALL mpi_get_address(bdata%H0, displs(28), ierr) CALL mpi_get_address(bdata%P0, displs(29), ierr) types(17:29)=MPI_DOUBLE_PRECISION blocklength(1:29) = 1 CALL mpi_get_address(bdata%femorder, displs(30), ierr) CALL mpi_get_address(bdata%ngauss, displs(31), ierr) CALL mpi_get_address(bdata%nnr, displs(32), ierr) types(30:32)=MPI_INTEGER blocklength(30:31) = 2 blocklength(32) = 3 CALL mpi_get_address(bdata%lz, displs(33), ierr) blocklength(33) = 2 CALL mpi_get_address(bdata%radii, displs(34), ierr) CALL mpi_get_address(bdata%plasmadim, displs(35), ierr) types(33:35)=MPI_DOUBLE_PRECISION blocklength(34:35) = 4 CALL mpi_get_address(bdata%resfile, displs(36), ierr) types(36)=MPI_CHARACTER blocklength(36) = 64 + CALL mpi_get_address(bdata%partperiodic, displs(37), ierr) + types(37)=MPI_LOGICAL + blocklength(37) = 1 + CALL mpi_get_address(bdata, displ0, ierr) displs=displs-displ0 CALL MPI_Type_create_struct(nblock, blocklength, displs, types, basicdata_type, ierr) CALL MPI_Type_commit(basicdata_type,ierr) END SUBROUTINE init_basicdatampi !========================================================================= END MODULE mpihelper