diff --git a/src/.depend b/src/.depend index 5676073..7d949a0 100644 --- a/src/.depend +++ b/src/.depend @@ -1,34 +1,35 @@ auxval.o : auxval.f90 beam_mod.o fields_mod.o basic_mod.o constants.o auxval__genmod.o : auxval__genmod.f90 basic_mod.o : basic_mod.f90 mpihelper_mod.o constants.o -beam_mod.o : beam_mod.f90 basic_mod.o mpihelper_mod.o constants.o +beam_mod.o : beam_mod.f90 basic_mod.o mpihelper_mod.o constants.o distrib_mod.o chkrst.o : chkrst.f90 fields_mod.o beam_mod.o basic_mod.o chkrst__genmod.o : chkrst__genmod.f90 constants.o : constants.f90 cp2bk__genmod.o : cp2bk__genmod.f90 diagnose.o : diagnose.f90 xg_mod.o beam_mod.o basic_mod.o diagnose__genmod.o : diagnose__genmod.f90 +distrib_mod.o : distrib_mod.f90 endrun.o : endrun.f90 fields_mod.o beam_mod.o basic_mod.o endrun__genmod.o : endrun__genmod.f90 energies.o : energies.f90 basic_mod.o fields_mod.o : fields_mod.f90 mpihelper_mod.o beam_mod.o basic_mod.o constants.o inital.o : inital.f90 mpihelper_mod.o fields_mod.o beam_mod.o basic_mod.o inital__genmod.o : inital__genmod.f90 main.o : main.f90 basic_mod.o mpihelper_mod.o : mpihelper_mod.f90 constants.o mv2bk.o : mv2bk.f90 mv2bk__genmod.o : mv2bk__genmod.f90 newrun.o : newrun.f90 newrun__genmod.o : newrun__genmod.f90 restart.o : restart.f90 restart__genmod.o : restart__genmod.f90 resume.o : resume.f90 sort_mod.o fields_mod.o basic_mod.o beam_mod.o resume__genmod.o : resume__genmod.f90 sort_mod.o : sort_mod.f90 beam_mod.o start.o : start.f90 basic_mod.o start__genmod.o : start__genmod.f90 stepon.o : stepon.f90 beam_mod.o fields_mod.o constants.o basic_mod.o stepon__genmod.o : stepon__genmod.f90 tesend.o : tesend.f90 basic_mod.o tesend__genmod.o : tesend__genmod.f90 xg_mod.o : xg_mod.f90 fields_mod.o beam_mod.o basic_mod.o constants.o diff --git a/src/Makefile b/src/Makefile index 7fb8dff..e647553 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,78 +1,78 @@ .DEFAULT_GOAL := all ifeq ($(PLATFORM),) $(error Please specify the env variable PLATFORM (mac, intel)) else $(info *** Using $(PLATFORM).mk ***) include $(PLATFORM).mk endif include .depend PROG = espic2d SRCS = main.f90 basic_mod.f90 newrun.f90 restart.f90 \ auxval.f90 inital.f90 resume.f90 start.f90 diagnose.f90 \ stepon.f90 tesend.f90 endrun.f90 chkrst.f90 mv2bk.f90 \ constants.f90 xg_mod.f90 fields_mod.f90 beam_mod.f90 \ - mpihelper_mod.f90 sort_mod.f90 + mpihelper_mod.f90 sort_mod.f90 distrib_mod.f90 SRCS_C = extra.c MKDIR_P = mkdir -p OUT_DIR = release F90FLAGS += -I$(BSPLINES)/include -I$(FUTILS)/include \ -I$(MUMPS)/include -I../src LDFLAGS += -L$(BSPLINES)/lib -L$(FUTILS)/lib -L${HDF5}/lib -L${HDF5}/lib \ -L$(MUMPS)/lib -L$(PARMETIS)/lib -L/usr/local/xgrafix_1.2/src-double LIBS += -lbsplines -lpppack -lfutils -lhdf5_fortran -lhdf5 -lz $(MUMPSLIBS) -lpputils2 \ -lXGF -lXGC -lX11 OBJS =${SRCS:.f90=.o} ${SRCS_F90:.F90=.o} ${SRCS_C:.c=.o} debug: F90FLAGS = $(DEBUGFLAGS) -I$(BSPLINES)/include -I$(FUTILS)/include \ -I$(MUMPS)/include debug: OUT_DIR=debug debug: $(OUT_DIR) all profile: F90FLAGS+=$(PROFILEFLAGS) profile: LDFLAGS+= $(PROFILEFLAGS) profile: OUT_DIR=profile profile: $(OUT_DIR) all .PHONY: directories all: directories $(PROG) $(PROG): $(OBJS) $(F90) $(LDFLAGS) $(F90FLAGS) -o $@ $(addprefix ./$(OUT_DIR)/,$(OBJS)) $(LIBS) mv *.mod ./$(OUT_DIR)/ tags: etags *.f90 clean: rm -f $(OBJS) *.mod distclean: clean rm -f $(PROG) *~ a.out *.o TAGS directories: ${OUT_DIR} ${OUT_DIR}: ${MKDIR_P} ${OUT_DIR} .SUFFIXES: $(SUFFIXES) .f90 .c .f90.o: $(F90) $(F90FLAGS) -c -o ./$(OUT_DIR)/$@ $< .c.o: $(CC) $(CCFLAGS) -c -o ./$(OUT_DIR)/$@ $< depend .depend: makedepf90 *.[fF]90 > .depend diff --git a/src/basic_mod.f90 b/src/basic_mod.f90 index 95f5a44..b35937e 100644 --- a/src/basic_mod.f90 +++ b/src/basic_mod.f90 @@ -1,318 +1,318 @@ 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 !< Number of iterations between 0d values writes to hdf5 INTEGER :: it2d=1 !< Number of iterations between 2d values writes to hdf5 INTEGER :: itparts=1 !< Number of iterations between 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 !< Rank of next processor in the z decomposition INTEGER :: leftproc !< Rank of 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 !< File ID for resfile INTEGER :: fidrst !< File ID 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 according to classical !! dynamics 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 macro-particles INTEGER :: nblock !< Number of slices in Z for stable distribution initialisation DOUBLE PRECISION :: potinn !< Electric potential at the inner metallic wall DOUBLE PRECISION :: potout !< Electric potential at the outer metallic wall DOUBLE PRECISION :: B0 !< Max magnitude of magnetic field DOUBLE PRECISION, allocatable :: Bz(:), Br(:) !< Magnetic field components DOUBLE PRECISION, allocatable :: Athet(:) !< Theta component of the magnetic vector 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 !< Initial value of Hamiltonian for distribution 2 DOUBLE PRECISION :: P0=0 !< Initial canonical angular momentum for distribution 2 DOUBLE PRECISION :: temprescale = -1.0 !< Factor used for temperature rescaling in case of a restart (<0 -> no rescaling) INTEGER :: samplefactor =-1 !< Factor used for the up-sampling of the particles number DOUBLE PRECISION :: lz(2) !< Lower and upper cylinder limits in z direction DOUBLE PRECISION :: n0 !< Physical plasma density parameter DOUBLE PRECISION, ASYNCHRONOUS, DIMENSION(:,:), ALLOCATABLE, SAVE:: moments !< Moments of the distribution function evaluated every it2d DOUBLE PRECISION, DIMENSION(:), ALLOCATABLE, SAVE:: rhs !< right hand side of the poisson equation solver DOUBLE PRECISION, DIMENSION(:), ALLOCATABLE, SAVE:: volume !< Volume covered by each spline for density calculation INTEGER :: nz !< Number of grid intervals in z INTEGER :: nr !< Total number of grid intervals in r INTEGER :: nnr(3) !< Number of grid intervals in r in each subdomain DOUBLE PRECISION :: dz !< Cell size in z DOUBLE PRECISION :: dr(3) !< Cell size in r for each region DOUBLE PRECISION, ALLOCATABLE :: zgrid(:) !< Nodes positions in longitudinal direction DOUBLE PRECISION, ALLOCATABLE :: rgrid(:) !< Nodes positions in radial direction 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 :: 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 INTEGER, DIMENSION(:), ALLOCATABLE :: Zbounds !< Index of bounds for local processus in Z direction - TYPE(BASICDATA) :: bdata + TYPE(BASICDATA) :: bdata !< Structure used for the mpi communication of the run parameters 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, partperiodic, & & temprescale, samplefactor !________________________________________________________________________________ ! 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)) !________________________________________________________________________________ ! 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, partperiodic, samplefactor) END IF ! Distribute run parameters to all MPI workers 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 IF(samplefactor .gt. 1 .and. .not. newres) THEN IF(mpirank.eq.0) WRITE(*,*)"To increase the number of particles, you need to create a new result file (set newres to 1)" CALL MPI_abort(MPI_COMM_WORLD,-1,ierr) END IF ! 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 samplefactor = bdata%samplefactor END SUBROUTINE readbdata !================================================================================ END MODULE basic diff --git a/src/beam_mod.f90 b/src/beam_mod.f90 index bae4781..d8a85c3 100644 --- a/src/beam_mod.f90 +++ b/src/beam_mod.f90 @@ -1,1620 +1,1477 @@ !------------------------------------------------------------------------------ ! EPFL/Swiss Plasma Center !------------------------------------------------------------------------------ ! ! MODULE: beam ! !> @author !> Guillaume Le Bars EPFL/SPC !> Patryk Kaminski EPFL/SPC !> Trach Minh Tran 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 + USE distrib 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 !< radial coordinates of the particles DOUBLE PRECISION, DIMENSION(:), ALLOCATABLE :: Z !< longitudinal coordinates of the particles DOUBLE PRECISION, DIMENSION(:), ALLOCATABLE :: THET !< azimuthal coordinates of the particles DOUBLE PRECISION, DIMENSION(:), ALLOCATABLE :: WZ !< axial radial relative distances to the left grid line DOUBLE PRECISION, DIMENSION(:), ALLOCATABLE :: WR !< radial relative distances to the bottom grid line DOUBLE PRECISION, DIMENSION(:), ALLOCATABLE :: pot !< Electric potential DOUBLE PRECISION, DIMENSION(:), ALLOCATABLE :: Er !< Radial Electric field DOUBLE PRECISION, DIMENSION(:), ALLOCATABLE :: Ez !< Axial electric field DOUBLE PRECISION, DIMENSION(:),POINTER:: UR !< normalized radial velocity at the current time step DOUBLE PRECISION, DIMENSION(:),POINTER:: URold !< normalized radial velocity at the previous time step DOUBLE PRECISION, DIMENSION(:),POINTER:: UTHET !< normalized azimuthal velocity at the current time step DOUBLE PRECISION, DIMENSION(:),POINTER:: UTHETold !< normalized azimuthal velocity at the previous time step DOUBLE PRECISION, DIMENSION(:),POINTER:: UZ !< normalized axial velocity at the current time step DOUBLE PRECISION, DIMENSION(:),POINTER:: UZold !< normalized axial velocity at the previous time step DOUBLE PRECISION, DIMENSION(:),POINTER:: Gamma !< Lorentz factor at the current time step DOUBLE PRECISION, DIMENSION(:),POINTER:: Gammaold !< Lorentz factor at the previous time step END TYPE particles ! TYPE(particles) :: parts !< Storage for all the particles SAVE :: parts TYPE(particle), DIMENSION(:), ALLOCATABLE:: rrecvpartbuff, lrecvpartbuff, rsendpartbuff, lsendpartbuff ! buffers to send and receive particle from left and right processes ! Diagnostics (scalars) DOUBLE PRECISION :: ekin=0 !< Total kinetic energz (J) DOUBLE PRECISION :: epot=0 !< Total potential energy (J) DOUBLE PRECISION :: etot=0 !< Current total energy (J) DOUBLE PRECISION :: etot0=0 !< Initial total energy (J) DOUBLE PRECISION :: loc_etot0=0 !< theoretical local total energy (J) INTEGER, DIMENSION(3), SAVE :: ireducerequest=MPI_REQUEST_NULL !< MPI requests used for the IREDUCE in the diagnostic routine ! INTEGER, DIMENSION(:), ALLOCATABLE :: Nplocs_all !< Array containing the local numbers of particles in each MPI process LOGICAL:: collected=.false. !< Stores if the particles data have been collected to MPI root process during 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%partindex=-1 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 !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> @brief Loads the particles at the beginning of the simulation and create the parts variable if necessary !--------------------------------------------------------------------------- SUBROUTINE load_parts USE basic, ONLY: nplasma, mpirank, ierr, distribtype, mpisize, nlclassical 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<V_perp to satisfy magnetic mirror trapping CALL loaduniformRZ(VR, VZ, VTHET) VZ = VZ/50 CASE DEFAULT IF (mpirank .eq. 0) WRITE(*,*) "Unknown type of distribution:", distribtype CALL MPI_Abort(MPI_COMM_WORLD, -1, ierr) END SELECT Do i=1,nplasma parts%partindex(i)=i END DO IF(nlclassical) THEN parts%Gamma=1 ELSE parts%Gamma=sqrt(1/(1-VR**2+VZ**2+VTHET**2)) END IF ! Normalization of the velocities parts%UR=parts%Gamma*VR parts%UZ=parts%Gamma*VZ parts%UTHET=parts%Gamma*VTHET DEALLOCATE(VZ, VR, VTHET) CALL localisation IF(mpisize .gt. 1) THEN CALL distribpartsonprocs END IF END SUBROUTINE load_parts !--------------------------------------------------------------------------- ! DESCRIPTION: !> @brief Checks for each particle if the z position is outside of the local/global simulation space. !> Depending on the boundary conditions, the leaving particles are sent to the correct neighbouring MPI process !> or deleted. ! !> @author Guillaume Le Bars EPFL/SPC !--------------------------------------------------------------------------- SUBROUTINE bound USE basic, ONLY: zgrid, nz, Zbounds, cstep, mpirank, partperiodic, step, leftproc, rightproc INTEGER :: i, rsendnbparts=0, lsendnbparts=0, nblostparts=0 INTEGER, DIMENSION(parts%Nptot) :: sendhole INTEGER, DIMENSION(parts%Nptot) :: losthole LOGICAL:: leftcomm, rightcomm rsendnbparts=0 lsendnbparts=0 nblostparts=0 losthole=0 sendhole=0 ! We communicate with the left processus leftcomm = leftproc .ne. -1 .or. partperiodic ! We communicate with the right processus rightcomm = rightproc .ne. -1 .or. partperiodic IF (parts%Nptot .NE. 0) THEN ! Boundary condition at z direction !$OMP PARALLEL DO DEFAULT(SHARED) PRIVATE(i) DO i=1,parts%Nptot IF(step .ne. 0) THEN ! If the particle is to the right of the local simulation space, it is sent to the right MPI process IF (parts%Z(i) .ge. zgrid(Zbounds(mpirank+1))) THEN !$OMP CRITICAL (nbparts) IF(rightcomm) THEN rsendnbparts=rsendnbparts+1 sendhole(lsendnbparts+rsendnbparts)=i ELSE !WRITE(*,*) "Particle:",i , " of process:", mpirank, "is out of bound in z" !WRITE(*,*) "!!!!!!!!!! Particle removed !!!!!!!!!!" nblostparts=nblostparts+1 losthole(nblostparts)=i END IF !$OMP END CRITICAL (nbparts) ! If the particle is to the left of the local simulation space, it is sent to the left MPI process ELSE IF (parts%Z(i) .lt. zgrid(Zbounds(mpirank))) THEN !$OMP CRITICAL (nbparts) IF(leftcomm) THEN lsendnbparts=lsendnbparts+1 sendhole(lsendnbparts+rsendnbparts)=-i ELSE !WRITE(*,*) "Particle:",i , " of process:", mpirank, "is out of bound in z" !WRITE(*,*) "!!!!!!!!!! Particle removed !!!!!!!!!!" nblostparts=nblostparts+1 losthole(nblostparts)=i END IF !$OMP END CRITICAL (nbparts) END IF END IF ! The periodic boundary conditions in z are here applied DO WHILE (parts%Z(i) .GT. zgrid(nz)) parts%Z(i) = parts%Z(i) - zgrid(nz) + zgrid(0) END DO DO WHILE (parts%Z(i) .LT. zgrid(0)) parts%Z(i) = parts%Z(i) + zgrid(nz) - zgrid(0) END DO END DO !$OMP END PARALLEL DO IF(mpisize .gt. 1 .and. step .ne. 0) THEN ! We send the particles leaving the local simulation space to the closest neighbour CALL particlescommunication(lsendnbparts, rsendnbparts, sendhole) END IF ! If the boundary conditions are not periodic, we delete the corresponding particles IF(nblostparts .gt. 0) THEN DO i=nblostparts,1,-1 CALL delete_part(losthole(i)) END DO END IF END IF END subroutine bound !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> @brief Compute the grid cell indices for each particle as well as the distance weight Wr, Wz. !--------------------------------------------------------------------------- SUBROUTINE localisation USE basic, ONLY: zgrid, rgrid, dz, nr, nnr, dr, rnorm INTEGER :: j,i, nblostparts=0 INTEGER, DIMENSION(parts%Nptot) :: losthole i=1 losthole=0 IF (parts%Nptot .NE. 0) THEN !$OMP PARALLEL DO DEFAULT(SHARED) PRIVATE(i) DO i=1,parts%Nptot parts%zindex(i)=(parts%Z(i)-zgrid(0))/dz IF (parts%R(i) .GT. rgrid(0) .AND. parts%R(i) .LT. rgrid(nnr(1))) THEN parts%rindex(i)=(parts%R(i)-rgrid(0))/dr(1) ELSE IF(parts%R(i) .GT. rgrid(nnr(1)) .AND. parts%R(i) .LT. rgrid(nnr(1)+nnr(2))) THEN parts%rindex(i)=(parts%R(i)-rgrid(nnr(1)))/dr(2)+nnr(1) ELSE IF(parts%R(i) .GT. rgrid(nnr(1)+nnr(2)) .AND. parts%R(i) .LT. rgrid(nr)) THEN parts%rindex(i)=(parts%R(i)-rgrid(nnr(1)+nnr(2)))/dr(3)+nnr(1)+nnr(2) ELSE ! If the particle is outside of the simulation space in the r direction, it is deleted. WRITE(*,*) "Particle:",i , " of process:", mpirank, "is out of bound in r:", parts%R(i)*rnorm, rgrid(0)*rnorm, rgrid(nr)*rnorm WRITE(*,*) "!!!!!!!!!! Particle removed !!!!!!!!!!" !$OMP CRITICAL (lostparts) nblostparts=nblostparts+1 losthole(nblostparts)=i !$OMP END CRITICAL (lostparts) END IF END DO !$OMP END PARALLEL DO IF(nblostparts.gt.0) THEN DO j=nblostparts,1,-1 CALL delete_part(losthole(j)) END DO END IF !$OMP PARALLEL DO SIMD DEFAULT(SHARED) DO i=1,parts%Nptot parts%WZ(i)=(parts%Z(i)-zgrid(parts%zindex(i)))/dz; parts%WR(i)=(parts%R(i)-rgrid(parts%rindex(i)))/(rgrid(parts%rindex(i)+1)-rgrid(parts%rindex(i))); END DO !$OMP END PARALLEL DO SIMD END IF END SUBROUTINE localisation !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> @brief General routine to compute the velocities at time t+1. !> This routine allows to treat the classical and relativistic case efficiently from a numerical standpoint, !> by using a pointer to the routine computing gamma. This avoid the nlclassical flag check on each particle. ! !--------------------------------------------------------------------------- SUBROUTINE comp_velocity ! ! Computes the new velocity of the particles due to Lorentz force ! USE basic, ONLY : nlclassical ! Store old Velocities CALL swappointer(parts%UZold, parts%UZ) CALL swappointer(parts%URold, parts%UR) CALL swappointer(parts%UTHETold, parts%UTHET) CALL swappointer(parts%Gammaold, parts%Gamma) IF (nlclassical) THEN CALL comp_velocity_fun(gamma_classical) ELSE CALL comp_velocity_fun(gamma_relativistic) END IF END SUBROUTINE comp_velocity !--------------------------------------------------------------------------- !> @author !> Patryk Kaminski EPFL/SPC !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> @brief Routine called by comp_velocity to compute the velocities at time t+1. !> This routine allows to treat the classical and relativistic case efficiently from a numerical standpoint, !> by using the routine computing gamma as an input. This avoid the nlclassical flag check on each particle. ! !> @param[in] gamma the function used to compute the value of the lorentz factor \f$\gamma\f$ !--------------------------------------------------------------------------- SUBROUTINE comp_velocity_fun(gamma) ! ! Computes the new velocity of the particles due to Lorentz force ! USE basic, ONLY : omegac, omegap, dt, tnorm, BZ, BR, nz interface subroutine gamma(gam, UZ, UR, UTHET) DOUBLE PRECISION, INTENT(IN):: UR,UZ,UTHET DOUBLE PRECISION, INTENT(OUT):: gam end subroutine end interface DOUBLE PRECISION :: tau DOUBLE PRECISION:: BRZ, BRR, ZBR, ZBZ, ZPR, ZPZ, ZPTHET, SQR, ZBZ2, ZBR2 INTEGER:: J1, J2, J3, J4 INTEGER:: i ! Normalized time increment tau=omegac/2/omegap*dt/tnorm IF (parts%Nptot .NE. 0) THEN !$OMP PARALLEL DO SIMD DEFAULT(SHARED) PRIVATE(J1,J2,J3,J4,BRZ, BRR, ZBR, ZBZ, ZPR, ZPZ, ZPTHET, SQR, ZBZ2, ZBR2) DO i=1,parts%Nptot J1=(parts%rindex(i))*(nz+1)+parts%zindex(i)+1 J2=(parts%rindex(i))*(nz+1)+parts%zindex(i)+2 J3=(parts%rindex(i)+1)*(nz+1)+parts%zindex(i)+1 J4=(parts%rindex(i)+1)*(nz+1)+parts%zindex(i)+2 ! Interpolation for magnetic field BRZ=(1-parts%WZ(i))*(1-parts%WR(i))*Bz(J4) & & +parts%WZ(i)*(1-parts%WR(i))*Bz(J3) & & +(1-parts%WZ(i))*parts%WR(i)*Bz(J2) & & +parts%WZ(i)*parts%WR(i)*Bz(J1) BRR=(1-parts%WZ(i))*(1-parts%WR(i))*Br(J4) & & +parts%WZ(i)*(1-parts%WR(i))*Br(J3) & & +(1-parts%WZ(i))*parts%WR(i)*Br(J2) & & +parts%WZ(i)*parts%WR(i)*Br(J1) ! First half of electric pulse parts%UZ(i)=parts%UZold(i)+parts%Ez(i)*tau parts%UR(i)=parts%URold(i)+parts%ER(i)*tau CALL gamma(parts%Gamma(i), parts%UZ(i), parts%UR(i), parts%UTHETold(i)) ! Rotation along magnetic field ZBZ=tau*BRZ/parts%Gamma(i) ZBR=tau*BRR/parts%Gamma(i) ZPZ=parts%UZ(i)-ZBR*parts%UTHETold(i) !u'_{z} ZPR=parts%UR(i)+ZBZ*parts%UTHETold(i) !u'_{r} ZPTHET=parts%UTHETold(i)+(ZBR*parts%UZ(i)-ZBZ*parts%UR(i)) !u'_{theta} SQR=1+ZBZ*ZBZ+ZBR*ZBR ZBZ2=2*ZBZ/SQR ZBR2=2*ZBR/SQR parts%UZ(i)=parts%UZ(i)-ZBR2*ZPTHET !u+_{z} parts%UR(i)=parts%UR(i)+ZBZ2*ZPTHET !u+_{r} parts%UTHET(i)=parts%UTHETold(i)+(ZBR2*ZPZ-ZBZ2*ZPR) !u+_{theta} ! Second half of acceleration parts%UZ(i)=parts%UZ(i)+parts%EZ(i)*tau parts%UR(i)=parts%UR(i)+parts%ER(i)*tau ! Final computation of the Lorentz factor CALL gamma(parts%Gamma(i), parts%UZ(i), parts%UR(i), parts%UTHETold(i)) END DO !$OMP END PARALLEL DO SIMD END IF collected=.false. END SUBROUTINE comp_velocity_fun !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> !> @brief Routine used to compute the lorentz factor \f$\gamma\f$ in the classical simulations. !> This routine systematically returns 1.0 to treat the system according to classical dynamic. ! !> @param[out] gamma the lorentz factor \f$\gamma\f$ !> @param[in] UZ \f$\gamma\beta_z=\gamma v_z/c\f$ the normalized particle longitudinal velocity !> @param[in] UR \f$\gamma\beta_r=\gamma v_r/c\f$ the normalized particle radial velocity !> @param[in] UTHET \f$\gamma\beta_\theta=\gamma v_\theta/c\f$ the normalized particle azimuthal velocity !--------------------------------------------------------------------------- ELEMENTAL SUBROUTINE gamma_classical(gamma, UZ, UR, UTHET) !$OMP declare simd(gamma_classical) DOUBLE PRECISION, INTENT(IN):: UR,UZ,UTHET DOUBLE PRECISION, INTENT(OUT):: gamma gamma=1.0 END SUBROUTINE gamma_classical !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> @brief Routine used to compute the lorentz factor \f$\gamma\f$ in the relativistic simulations. !> This routine computes the Lorentz factor \f$\gamma=\sqrt{1+\mathbf{\gamma\beta}^2}\f$ ! !> @param[out] gamma the lorentz factor \f$\gamma\f$ !> @param[in] UZ \f$\gamma\beta_z=\gamma v_z/c\f$ the normalized particle longitudinal velocity !> @param[in] UR \f$\gamma\beta_r=\gamma v_r/c\f$ the normalized particle radial velocity !> @param[in] UTHET \f$\gamma\beta_\theta=\gamma v_\theta/c\f$ the normalized particle azimuthal velocity !--------------------------------------------------------------------------- ELEMENTAL SUBROUTINE gamma_relativistic(gamma, UZ, UR, UTHET) !$OMP declare simd(gamma_relativistic) DOUBLE PRECISION, INTENT(IN):: UR,UZ,UTHET DOUBLE PRECISION, INTENT(OUT):: gamma gamma=sqrt(1+UZ**2+UR**2+UTHET**2) END SUBROUTINE !--------------------------------------------------------------------------- !> @author !> Patryk Kaminski EPFL/SPC !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> !> @brief Computes the particles position at time t+1 !> This routine computes the particles position at time t+1 according to the Bunemann algorithm. !--------------------------------------------------------------------------- SUBROUTINE push Use basic, ONLY: dt, tnorm DOUBLE PRECISION:: XP, YP, COSA, SINA, U1, U2, ALPHA INTEGER :: i IF (parts%Nptot .NE. 0) THEN !$OMP PARALLEL DO SIMD DEFAULT(SHARED) PRIVATE(XP, YP, COSA, SINA, U1, U2, ALPHA) DO i=1,parts%Nptot ! Local Cartesian coordinates XP=parts%R(i)+dt/tnorm*parts%UR(i)/parts%Gamma(i) YP=dt/tnorm*parts%UTHET(i)/parts%Gamma(i) ! Conversion to cylindrical coordiantes parts%Z(i)=parts%Z(i)+dt/tnorm*parts%UZ(i)/parts%Gamma(i) parts%R(i)=sqrt(XP**2+YP**2) ! Computation of the rotation angle IF (parts%R(i) .EQ. 0) THEN COSA=1 SINA=0 ALPHA=0 ELSE COSA=XP/parts%R(i) SINA=YP/parts%R(i) ALPHA=asin(SINA) END IF ! New azimuthal position parts%THET(i)=parts%THET(i)+ALPHA ! Velocity in rotated reference frame U1=COSA*parts%UR(i)+SINA*parts%UTHET(i) U2=-SINA*parts%UR(i)+COSA*parts%UTHET(i) parts%UR(i)=U1 parts%UTHET(i)=U2 END DO !$OMP END PARALLEL DO SIMD END IF collected=.false. END SUBROUTINE push !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> !> @brief Computes several diagnostic quantities !> This routine computes the total kinetic and electric potential energy. It also computes the first and second order !> moments of the distribution function ! !--------------------------------------------------------------------------- SUBROUTINE diagnostics ! ! Compute energies ! USE constants, ONLY: vlight USE basic, ONLY: qsim, phinorm, msim, cstep, nlclassical, nz, ierr, step, it0d, it2d, nlend, nr, itparts, nplasma, partperiodic, newres, Zbounds INTEGER :: i, gridindex INTEGER, DIMENSION(:) :: stat(3*MPI_STATUS_SIZE) CALL MPI_WAITALL(3, ireducerequest, stat, ierr) ! Reset the quantities ekin=0 epot=0 etot=0 ! Computation of the kinetic and potential energy as well as fluid velocities and density !$OMP PARALLEL DO SIMD REDUCTION(+:epot, ekin) DEFAULT(SHARED) DO i=1,parts%Nptot ! Potential energy epot=epot+0.5*qsim*parts%pot(i)*phinorm ! Kinetic energy IF(.not. nlclassical) THEN ekin=ekin+msim*vlight**2*(parts%Gamma(i)-1) ELSE ekin=ekin+0.5*msim*vlight**2*(abs(parts%URold(i)*parts%UR(i))+abs(parts%UZold(i)*parts%UZ(i))+abs(parts%UTHETold(i)*parts%UTHET(i))) END IF END DO !$OMP END PARALLEL DO SIMD ! Shift to Etot at cstep=1 (not valable yet at cstep=0!) IF(cstep.EQ.1 .or. (newres .AND. step .EQ. 1)) THEN ! Compute the local total energy loc_etot0 = epot+ekin END IF etot=loc_etot0 ! The computed energy is sent to the root process IF(mpisize .gt.1) THEN IF(mpirank .eq.0 ) THEN CALL MPI_IREDUCE(MPI_IN_PLACE, epot, 1, MPI_DOUBLE_PRECISION, MPI_SUM, & & 0, MPI_COMM_WORLD, ireducerequest(1), ierr) CALL MPI_IREDUCE(MPI_IN_PLACE, ekin, 1, MPI_DOUBLE_PRECISION, MPI_SUM, & & 0, MPI_COMM_WORLD, ireducerequest(2), ierr) CALL MPI_IREDUCE(etot, etot0, 1, MPI_DOUBLE_PRECISION, MPI_SUM, & & 0, MPI_COMM_WORLD, ireducerequest(3), ierr) ELSE CALL MPI_IREDUCE(epot, epot, 1, MPI_DOUBLE_PRECISION, MPI_SUM, & & 0, MPI_COMM_WORLD, ireducerequest(1), ierr) CALL MPI_IREDUCE(ekin, ekin, 1, MPI_DOUBLE_PRECISION, MPI_SUM, & & 0, MPI_COMM_WORLD, ireducerequest(2), ierr) CALL MPI_IREDUCE(etot, etot0, 1, MPI_DOUBLE_PRECISION, MPI_SUM, & & 0, MPI_COMM_WORLD, ireducerequest(3), ierr) END IF ELSE etot0=loc_etot0 END IF ! Wait for the communication of the energy to be finished CALL MPI_WAITALL(3, ireducerequest(1:3), stat(1:3*MPI_STATUS_SIZE), ierr) !CALL MPI_WAIT(ireducerequest(2), stat(MPI_STATUS_SIZE+1:2*MPI_STATUS_SIZE), ierr) ! Compute the total energy etot=epot+ekin ! Send the local number of particles on each node to the root process IF(modulo(step,it0d).eq. 0 .and. mpisize .gt. 1) THEN Nplocs_all(mpirank)=parts%Nptot IF(mpirank .eq.0 ) THEN CALL MPI_gather(MPI_IN_PLACE, 1, MPI_INTEGER, Nplocs_all, 1, MPI_INTEGER,& & 0, MPI_COMM_WORLD, ierr) ELSE CALL MPI_gather(Nplocs_all(mpirank), 1, MPI_INTEGER, Nplocs_all, 1, MPI_INTEGER,& & 0, MPI_COMM_WORLD, ierr) END IF IF(mpirank .eq. 0 .AND. partperiodic .and. INT(SUM(Nplocs_all)).ne. nplasma) THEN !WRITE(*,*) "Error particle lost on some processus" !CALL MPI_abort(MPI_COMM_WORLD,-1,ierr) END IF END IF !Calls the communications to send the particles data to the root process for diagnostic file every itparts time steps IF(mpisize .gt. 1 .and. (modulo(step,itparts) .eq. 0 .or. nlend)) THEN CALL collectparts END IF end subroutine diagnostics !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> @brief Collect the particles positions and velocities on the root process. !> If the collection has already been performed at this time step, the routine does nothing. ! !--------------------------------------------------------------------------- SUBROUTINE collectparts USE basic, ONLY: mpirank, mpisize, ierr INTEGER, DIMENSION(:), ALLOCATABLE :: displs INTEGER:: i IF(collected) RETURN ! exit subroutine if particles have already been collected during this time step IF(mpirank .ne. 0) THEN ALLOCATE(displs(0:mpisize-1)) displs=0 ! Send Particles informations to root process CALL MPI_Gatherv(parts%Z, Nplocs_all(mpirank), MPI_DOUBLE_PRECISION, parts%Z, Nplocs_all, displs,& & MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, ierr) CALL MPI_Gatherv(parts%R, Nplocs_all(mpirank), MPI_DOUBLE_PRECISION, parts%R, Nplocs_all, displs,& & MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, ierr) CALL MPI_Gatherv(parts%THET, Nplocs_all(mpirank), MPI_DOUBLE_PRECISION, parts%THET, Nplocs_all, displs,& & MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, ierr) CALL MPI_Gatherv(parts%UR, Nplocs_all(mpirank), MPI_DOUBLE_PRECISION, parts%UR, Nplocs_all, displs,& & MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, ierr) CALL MPI_Gatherv(parts%UZ, Nplocs_all(mpirank), MPI_DOUBLE_PRECISION, parts%UZ, Nplocs_all, displs, & & MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, ierr) CALL MPI_Gatherv(parts%UTHET, Nplocs_all(mpirank), MPI_DOUBLE_PRECISION, parts%UTHET, Nplocs_all, displs, & & MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, ierr) CALL MPI_Gatherv(parts%pot, Nplocs_all(mpirank), MPI_DOUBLE_PRECISION, parts%pot, Nplocs_all, displs, & & MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, ierr) CALL MPI_Gatherv(parts%Rindex, Nplocs_all(mpirank), MPI_INTEGER, parts%Rindex, Nplocs_all, displs, & & MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) CALL MPI_Gatherv(parts%Zindex, Nplocs_all(mpirank), MPI_INTEGER, parts%Zindex, Nplocs_all, displs, & & MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) CALL MPI_Gatherv(parts%partindex, Nplocs_all(mpirank), MPI_INTEGER, parts%partindex, Nplocs_all, displs, & & MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) ELSE ALLOCATE(displs(0:mpisize-1)) displs(0)=0 Do i=1,mpisize-1 displs(i)=displs(i-1)+Nplocs_all(i-1) END DO ! Receive particle information from all processes CALL MPI_Gatherv(MPI_IN_PLACE, Nplocs_all(mpirank), MPI_DOUBLE_PRECISION, parts%Z, Nplocs_all, displs,& & MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, ierr) CALL MPI_Gatherv(MPI_IN_PLACE, Nplocs_all(mpirank), MPI_DOUBLE_PRECISION, parts%R, Nplocs_all, displs,& & MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, ierr) CALL MPI_Gatherv(MPI_IN_PLACE, Nplocs_all(mpirank), MPI_DOUBLE_PRECISION, parts%THET, Nplocs_all, displs,& & MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, ierr) CALL MPI_Gatherv(MPI_IN_PLACE, Nplocs_all(mpirank), MPI_DOUBLE_PRECISION, parts%UR, Nplocs_all, displs,& & MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, ierr) CALL MPI_Gatherv(MPI_IN_PLACE, Nplocs_all(mpirank), MPI_DOUBLE_PRECISION, parts%UZ, Nplocs_all, displs, & & MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, ierr) CALL MPI_Gatherv(MPI_IN_PLACE, Nplocs_all(mpirank), MPI_DOUBLE_PRECISION, parts%UTHET, Nplocs_all, displs, & & MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, ierr) CALL MPI_Gatherv(MPI_IN_PLACE, Nplocs_all(mpirank), MPI_DOUBLE_PRECISION, parts%pot, Nplocs_all, displs, & & MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, ierr) CALL MPI_Gatherv(MPI_IN_PLACE, Nplocs_all(mpirank), MPI_INTEGER, parts%Rindex, Nplocs_all, displs, & & MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) CALL MPI_Gatherv(MPI_IN_PLACE, Nplocs_all(mpirank), MPI_INTEGER, parts%Zindex, Nplocs_all, displs, & & MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) CALL MPI_Gatherv(MPI_IN_PLACE, Nplocs_all(mpirank), MPI_INTEGER, parts%partindex, Nplocs_all, displs, & & MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) parts%partindex(sum(Nplocs_all)+1:)=-1 END IF collected=.TRUE. END SUBROUTINE collectparts !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> @brief Computes the velocities at time t-1/2 to keep the second order precision in time on the velocity. ! !--------------------------------------------------------------------------- SUBROUTINE adapt_vinit !! Computes the velocity at time -dt/2 from velocities computed at time 0 ! USE basic, ONLY : omegac, omegap, dt, tnorm, BZ, BR, nlclassical, phinorm, nz, distribtype, H0, vnorm DOUBLE PRECISION :: tau, BRZ, BRR, ZBR, ZBZ, ZPR, ZPZ, ZPTHET, & & SQR, ZBZ2, ZBR2, Vperp, v2 INTEGER :: J1, J2, J3, J4, i DOUBLE PRECISION, DIMENSION(:), ALLOCATABLE :: VZ, VR, VTHET ! In case Davidson distribution is used the longitudinal and radial velocities are adapted to take into account the ! electric potential. IF(distribtype .EQ. 2 .OR. distribtype .EQ. 3 .OR. distribtype .EQ. 4) THEN ALLOCATE(VR(parts%Nptot),VZ(parts%Nptot),VTHET(parts%Nptot)) CALL loduni(7,VZ) VZ=VZ*2*pi VTHET=parts%UTHET/parts%Gamma*vnorm DO i=1,parts%Nptot Vperp=sqrt(MAX(2*H0/me+2*elchar/me*parts%pot(i)*phinorm-VTHET(i)**2,0.0_db)) VR(i)=Vperp*sin(VZ(i)) VZ(i)=Vperp*cos(VZ(i)) IF(nlclassical) THEN parts%Gamma(i)=1 ELSE v2=VR(i)**2+VZ(i)**2+VTHET(i)**2 parts%Gamma(i)=sqrt(1/(1-v2/vnorm**2)) END IF parts%UR(i)=parts%Gamma(i)*VR(i)/vnorm parts%UZ(i)=parts%Gamma(i)*VZ(i)/vnorm parts%UTHET(i)=parts%Gamma(i)*VTHET(i)/vnorm END DO DEALLOCATE(VR,VZ,VTHET) END IF ! Normalised time increment tau=-omegac/2/omegap*dt/tnorm ! Store old Velocities CALL swappointer(parts%UZold, parts%UZ) CALL swappointer(parts%URold, parts%UR) CALL swappointer(parts%UTHETold, parts%UTHET) CALL swappointer(parts%Gammaold, parts%Gamma) IF (parts%Nptot .NE. 0) THEN !$OMP PARALLEL DO DEFAULT(SHARED) PRIVATE(i,J1,J2,J3,J4,BRZ, BRR, ZBR, ZBZ, ZPR, ZPZ, ZPTHET, SQR, ZBZ2, ZBR2) DO i=1,parts%Nptot ! Compute the particle linear indices for the magnetic field interpolation J1=(parts%rindex(i))*(nz+1)+parts%zindex(i)+1 J2=J1+1 J3=(parts%rindex(i)+1)*(nz+1)+parts%zindex(i)+1 J4=J3+1 ! Interpolation for magnetic field BRZ=(1-parts%WZ(i))*(1-parts%WR(i))*Bz(J4) & & +parts%WZ(i)*(1-parts%WR(i))*Bz(J3) & & +(1-parts%WZ(i))*parts%WR(i)*Bz(J2) & & +parts%WZ(i)*parts%WR(i)*Bz(J1) BRR=(1-parts%WZ(i))*(1-parts%WR(i))*Br(J4) & & +parts%WZ(i)*(1-parts%WR(i))*Br(J3) & & +(1-parts%WZ(i))*parts%WR(i)*Br(J2) & & +parts%WZ(i)*parts%WR(i)*Br(J1) ! Half inverse Rotation along magnetic field ZBZ=tau*BRZ/parts%Gammaold(i) ZBR=tau*BRR/parts%Gammaold(i) SQR=1+ZBZ*ZBZ+ZBR*ZBR ZPZ=(parts%UZold(i)-ZBR*parts%UTHETold(i))/SQR !u-_{z} ZPR=(parts%URold(i)+ZBZ*parts%UTHETold(i))/SQR !u-_{r} ZPTHET=parts%UTHETold(i)+(ZBR*parts%UZold(i)-ZBZ*parts%URold(i))/SQR !u-_{theta} parts%UZ(i)=ZPZ parts%UR(i)=ZPR parts%UTHET(i)=ZPTHET ! half of decceleration parts%UZ(i)=parts%UZ(i)+parts%Ez(i)*tau parts%UR(i)=parts%UR(i)+parts%Er(i)*tau IF(.not. nlclassical) THEN parts%Gamma(i)=sqrt(1+parts%UZ(i)**2+parts%UR(i)**2+parts%UTHET(i)**2) END IF END DO !$OMP END PARALLEL DO END IF END SUBROUTINE adapt_vinit !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> @brief In the case of MPI parallelism, computes the indices of the particle assigned to the current process. !--------------------------------------------------------------------------- SUBROUTINE distribpartsonprocs ! Computes the start and end indices for the Z boundaries on local processus ! Computes the particle indices from initial particle loading vector, that stay in current process USE basic, ONLY: nz, ierr, Zbounds INTEGER:: k DOUBLE PRECISION:: idealnbpartsperproc INTEGER:: totparts ! Total number of particle from zgrid(0) to zgrid(k) INTEGER:: partindexstart ! Starting index of local particle in the parts variable used later to move the local particles at the begining of the vector INTEGER:: partindexlast ! Ending index of local particle in the parts variable used later to move the local particles at the begining of the vector INTEGER, DIMENSION(0:nz):: partspercol ! Vector containing the number of particles between zgrid(n) and zgrid(n+1) INTEGER:: Zmin, Zmax ! Minimum and maximum indices of particles in Z direction INTEGER:: Zperproc, zindex partspercol=0 idealnbpartsperproc = FLOOR(REAL(parts%Nptot)/REAL(mpisize)) Zmin=MINVAL(parts%Zindex) Zmax=MAXVAL(parts%Zindex) Zperproc=(Zmax-Zmin)/mpisize DO k=1,parts%Nptot zindex=parts%Zindex(k) partspercol(zindex)=partspercol(zindex)+1 END DO DO k=1,mpisize-1 IF(k .lt. mpisize-1-MODULO(Zmax-Zmin,mpisize)) THEN Zbounds(k)=Zmin+k*Zperproc-1 ELSE Zbounds(k)=Zmin+k*Zperproc-1+k-mpisize+2+MODULO(Zmax-Zmin,mpisize) END IF END DO partindexstart=1 totparts=0 partindexlast=parts%Nptot !DO k=0,mpisize-2 ! Nplocs_all(k)=idealnbpartsperproc !END DO ! NPlocs_all(mpisize-1)=idealnbpartsperproc+MODULO(nplasma,mpisize) !DO k=1, mpisize-1 ! Zbounds(k)=parts%Z(INT(k*idealnbpartsperproc)) !END DO DO k=0,mpisize-1 Nplocs_all(k)=SUM(partspercol(Zbounds(k):Zbounds(k+1)-1)) END DO ! Store local end of particle vector partindexlast=SUM(Nplocs_all(0:mpirank)) ! Store local start of particle vector IF(mpirank .ne. 0) partindexstart=SUM(Nplocs_all(0:mpirank-1))+1 IF(mpirank .ne. 0) THEN DO k= partindexstart, partindexlast CALL move_part(k,k-partindexstart+1) END DO END IF IF(INT(SUM(Nplocs_all)) .ne. parts%Nptot) THEN WRITE(*,*) "Error in particle counting on proc:", mpirank, " local sum:", INT(SUM(Nplocs_all)), " Nptot:", parts%Nptot CALL MPI_Abort(MPI_COMM_WORLD, -1, ierr) END IF parts%Nptot=Nplocs_all(mpirank) WRITE(*,*) mpirank, " Zbounds: ", Zbounds(mpirank), Zbounds(mpirank+1), " indices ", partindexstart, partindexlast, " nptot", parts%Nptot END SUBROUTINE distribpartsonprocs !_______________________________________________________________________________ !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> !> @brief Manage the particle communication between neighbours. !> This routine is responsible to receive the incoming particles from the MPI neighbours and to send its outgoing !> particles to these neighbours ! !> @param [in] lsendnbparts number of particles to send to the left neighbour (mpirank-1) !> @param [in] rsendnbparts number of particles to send to the right neighbour (mpirank+1) !> @param [in] sendholes array containing the indices of the particle leaving the local domain in ascending order. If the index is positive, the particle goes to the right neigbour, and to the left neighbour if the index is negative !--------------------------------------------------------------------------- SUBROUTINE particlescommunication(lsendnbparts, rsendnbparts, sendholes) USE mpihelper, ONLY: particle_type USE basic, ONLY: rightproc, leftproc, ierr INTEGER, INTENT(in) :: lsendnbparts, rsendnbparts INTEGER, INTENT(in) :: sendholes(parts%Nptot) INTEGER, ASYNCHRONOUS :: rrecvnbparts=0, lrecvnbparts=0 INTEGER, DIMENSION(2), ASYNCHRONOUS :: sendrequest, recvrequest INTEGER, DIMENSION(2), ASYNCHRONOUS :: sendstatus(2*MPI_STATUS_SIZE), recvstatus(2*MPI_STATUS_SIZE) INTEGER :: lsentnbparts, rsentnbparts INTEGER :: lreceivednbparts, rreceivednbparts lsentnbparts=lsendnbparts rsentnbparts=rsendnbparts sendrequest=MPI_REQUEST_NULL recvrequest=MPI_REQUEST_NULL ! Send and receive the number of particles to exchange CALL MPI_IRECV(lrecvnbparts, 1, MPI_INTEGER, leftproc, 0, MPI_COMM_WORLD, recvrequest(1), ierr) CALL MPI_IRECV(rrecvnbparts, 1, MPI_INTEGER, rightproc, 0, MPI_COMM_WORLD, recvrequest(2), ierr) CALL MPI_ISEND(lsentnbparts, 1, MPI_INTEGER, leftproc, 0, MPI_COMM_WORLD, sendrequest(1), ierr) CALL MPI_ISEND(rsentnbparts, 1, MPI_INTEGER, rightproc, 0, MPI_COMM_WORLD, sendrequest(2), ierr) CALL MPI_Wait(recvrequest(1), recvstatus(1), ierr) CALL MPI_Wait(recvrequest(2), recvstatus(2), ierr) recvrequest=MPI_REQUEST_NULL lreceivednbparts=lrecvnbparts rreceivednbparts=rrecvnbparts ! Re/allocate enough memory to store the incoming particles IF( ALLOCATED(rrecvpartbuff) .AND. (size(rrecvpartbuff) .lt. rrecvnbparts) ) DEALLOCATE(rrecvpartbuff) IF(.not. ALLOCATED(rrecvpartbuff) .and. rrecvnbparts .gt. 0) ALLOCATE(rrecvpartbuff(INT(rreceivednbparts*1.3))) IF( ALLOCATED(lrecvpartbuff) .AND. (size(lrecvpartbuff) .lt. lrecvnbparts) ) DEALLOCATE(lrecvpartbuff) IF((.not. ALLOCATED(lrecvpartbuff) ).and. lrecvnbparts .gt. 0) ALLOCATE(lrecvpartbuff(INT(lreceivednbparts*1.3))) ! Receive particles from left and right processes to the corresponding buffers IF ( lrecvnbparts .gt. 0) THEN CALL MPI_IRECV(lrecvpartbuff, lreceivednbparts, particle_type, leftproc, 1, MPI_COMM_WORLD, recvrequest(1), ierr) END IF IF( rrecvnbparts .gt. 0) THEN CALL MPI_IRECV(rrecvpartbuff, rreceivednbparts, particle_type, rightproc, 1, MPI_COMM_WORLD, recvrequest(2), ierr) END IF ! Copy the leaving particles to the corresponding send buffers IF ( (lsendnbparts + rsendnbparts) .gt. 0) THEN CALL AddPartSendBuffers(lsendnbparts, rsendnbparts, sendholes) END IF CALL MPI_Wait(sendrequest(1), sendstatus(1), ierr) CALL MPI_Wait(sendrequest(2), sendstatus(MPI_STATUS_SIZE+1), ierr) ! Send the particles to the left and right neighbours IF( lsendnbparts .gt. 0) THEN CALL MPI_ISEND(lsendpartbuff, lsendnbparts, particle_type, leftproc, 1, MPI_COMM_WORLD, sendrequest(1), ierr) END IF IF( rsendnbparts .gt. 0) THEN CALL MPI_ISEND(rsendpartbuff, rsendnbparts, particle_type, rightproc, 1, MPI_COMM_WORLD, sendrequest(2), ierr) END IF ! Receive the incoming parts in the receive buffers IF ( lreceivednbparts .gt. 0) THEN CALL MPI_Wait(recvrequest(1), recvstatus(1), ierr) IF(ierr .ne. MPI_SUCCESS) THEN WRITE(*,*) "Error in particle reception on proc:", mpirank, " error code:", ierr, "status:", recvstatus(1) CALL MPI_Abort(MPI_COMM_WORLD, -1, ierr) END IF END IF IF ( rreceivednbparts .gt. 0) THEN CALL MPI_Wait(recvrequest(2), recvstatus(MPI_STATUS_SIZE+1), ierr) IF(ierr .ne. MPI_SUCCESS) THEN WRITE(*,*) "Error in particle reception on proc:", mpirank, " error code:", ierr, "status:", recvstatus(2) CALL MPI_Abort(MPI_COMM_WORLD, -1, ierr) END IF END IF ! Copy the incoming particles from the receive buffers to the simulation parts variable CALL Addincomingparts(rreceivednbparts, lreceivednbparts, lsendnbparts+rsendnbparts, sendholes) ! Wait for the outgoing particles to be fully received by the neighbours IF( lsendnbparts .gt. 0) THEN CALL MPI_Wait(sendrequest(1), sendstatus(1), ierr) END IF IF( rsendnbparts .gt. 0) THEN CALL MPI_Wait(sendrequest(2), sendstatus(2), ierr) END IF ! ! END SUBROUTINE particlescommunication !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> !> @brief Copy the particles from the receive buffers to the local simulation variable parts. !> The incoming particles will first be stored in the holes left by the outgoing particles, then they !> will be added at the end of the parts variable ! !> @param [in] rrecvnbparts number of particles received from the right neighbour (mpirank+1) !> @param [in] lrecvnbparts number of particles received from the left neighbour (mpirank-1) !> @param [in] sendnbparts total number of particles having left the local domain !> @param [in] sendholes array containing the indices of the particle having left the local domain in ascending order. !--------------------------------------------------------------------------- SUBROUTINE Addincomingparts(rrecvnbparts, lrecvnbparts, sendnbparts, sendholes) ! USE mpihelper INTEGER, INTENT(in) :: rrecvnbparts, lrecvnbparts, sendnbparts INTEGER, INTENT(in) :: sendholes(parts%Nptot) INTEGER k,partpos, partdiff ! computes if we received less particles than we sent partdiff=sendnbparts-rrecvnbparts-lrecvnbparts ! First import the particles coming from the right IF(rrecvnbparts .gt. 0) THEN Do k=1,rrecvnbparts IF(k .le. sendnbparts) THEN ! Fill the holes partpos=abs(sendholes(k)) ELSE ! Add at the end of parts parts%Nptot=parts%Nptot+1 partpos=parts%Nptot END IF CALL Insertincomingpart(rrecvpartbuff, k, partpos) END DO END IF ! Then import the particles coming from the left IF(lrecvnbparts .gt. 0) THEN Do k=1,lrecvnbparts IF(k+rrecvnbparts .le. sendnbparts) THEN partpos=abs(sendholes(k+rrecvnbparts)) ELSE parts%Nptot=parts%Nptot+1 partpos=parts%Nptot END IF CALL Insertincomingpart(lrecvpartbuff, k, partpos) END DO END IF ! If we received less particles than we sent, we fill the remaining holes with the particles from the end of the ! parts arrays IF(partdiff .gt. 0) THEN DO k=rrecvnbparts+lrecvnbparts+1, sendnbparts CALL move_part(parts%Nptot,abs(sendholes(k))) parts%Nptot = parts%Nptot-1 END DO END IF ! END SUBROUTINE Addincomingparts !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> !> @brief Copy one particle from the receive buffers to the local simulation variable parts. ! !> @param [in] buffer receive buffer containing the particles parameters to copy from !> @param [in] bufferindex particle index in the receive buffer !> @param [in] partsindex destination particle index in the local parts variable !--------------------------------------------------------------------------- SUBROUTINE Insertincomingpart(buffer, bufferindex, partsindex) USE mpihelper INTEGER, INTENT(in) :: bufferindex, partsindex TYPE(particle), DIMENSION(:), ALLOCATABLE, INTENT(in) :: buffer parts%partindex(partsindex) = buffer(bufferindex)%partindex parts%Rindex(partsindex) = buffer(bufferindex)%Rindex parts%Zindex(partsindex) = buffer(bufferindex)%Zindex parts%R(partsindex) = buffer(bufferindex)%R parts%Z(partsindex) = buffer(bufferindex)%Z parts%THET(partsindex) = buffer(bufferindex)%THET parts%UZ(partsindex) = buffer(bufferindex)%UZ parts%UR(partsindex) = buffer(bufferindex)%UR parts%UTHET(partsindex) = buffer(bufferindex)%UTHET parts%Gamma(partsindex) = buffer(bufferindex)%Gamma ! ! END SUBROUTINE Insertincomingpart !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> !> @brief Copy one particle from the local parts variable to the send buffer. ! !> @param [in] buffer send buffer to copy to !> @param [in] bufferindex particle index in the send buffer !> @param [in] partsindex origin particle index in the local parts variable !--------------------------------------------------------------------------- SUBROUTINE Insertsentpart(buffer, bufferindex, partsindex) USE mpihelper INTEGER, INTENT(in) :: bufferindex, partsindex TYPE(particle), DIMENSION(:), ALLOCATABLE, INTENT(inout) :: buffer buffer(bufferindex)%partindex = parts%partindex(partsindex) buffer(bufferindex)%Rindex = parts%Rindex(partsindex) buffer(bufferindex)%Zindex = parts%Zindex(partsindex) buffer(bufferindex)%R = parts%R(partsindex) buffer(bufferindex)%Z = parts%Z(partsindex) buffer(bufferindex)%THET = parts%THET(partsindex) buffer(bufferindex)%UZ = parts%UZ(partsindex) buffer(bufferindex)%UR = parts%UR(partsindex) buffer(bufferindex)%UTHET = parts%UTHET(partsindex) buffer(bufferindex)%Gamma = parts%Gamma(partsindex) ! END SUBROUTINE Insertsentpart !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> !> @brief Copy the particles from the local parts variable to the left and right send buffers. ! !> @param [in] lsendnbparts number of particles to send to the left neighbour (mpirank-1) !> @param [in] rsendnbparts number of particles to send to the right neighbour (mpirank+1) !> @param [in] sendholes array containing the indices of the particle leaving the local domain in ascending order. If the index is positive, the particle goes to the right neigbour, and to the left neighbour if the index is negative !--------------------------------------------------------------------------- SUBROUTINE AddPartSendBuffers(lsendnbparts, rsendnbparts, sendholes) ! USE mpihelper INTEGER, INTENT(in) :: lsendnbparts, rsendnbparts INTEGER, INTENT(in) :: sendholes(parts%Nptot) INTEGER:: partpos, k INTEGER:: lsendpos, rsendpos lsendpos=0 rsendpos=0 ! Verify if the buffers are big enough to store the outgoing particles IF( ALLOCATED(rsendpartbuff) .AND. size(rsendpartbuff) .lt. rsendnbparts ) DEALLOCATE(rsendpartbuff) IF( ALLOCATED(lsendpartbuff) .AND. size(lsendpartbuff) .lt. lsendnbparts ) DEALLOCATE(lsendpartbuff) ! Allocate the buffers if needed IF(.not. ALLOCATED(rsendpartbuff) .and. rsendnbparts .gt. 0) ALLOCATE(rsendpartbuff(INT(rsendnbparts*1.3))) IF(.not. ALLOCATED(lsendpartbuff) .and. lsendnbparts .gt. 0) ALLOCATE(lsendpartbuff(INT(lsendnbparts*1.3))) ! Loop over the outgoing particles and fill the correct send buffer Do k=lsendnbparts+rsendnbparts,1,-1 partpos=abs(sendholes(k)) IF(sendholes(k) .GT. 0) THEN rsendpos=rsendpos+1 CALL Insertsentpart(rsendpartbuff, rsendpos, partpos) ELSE IF(sendholes(k) .LT. 0) THEN lsendpos=lsendpos+1 CALL Insertsentpart(lsendpartbuff, lsendpos, partpos) END IF END DO ! ! END SUBROUTINE AddPartSendBuffers !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> @brief Exchange two particles in the parts variable. ! !> @param [in] index1 index in parts of the first particle to exchange. !> @param [in] index2 index in parts of the second particle to exchange. !--------------------------------------------------------------------------- SUBROUTINE exchange_parts(index1, index2) INTEGER, INTENT(IN) :: index1, index2 DOUBLE PRECISION:: R, Z, THET, UR, UZ, UTHET, Gamma INTEGER :: Rindex, Zindex, partindex !! Exchange particle at index1 with particle at index2 ! Store part at index1 in temporary value partindex= parts%partindex(index1) Gamma = parts%Gamma(index1) R = parts%R(index1) Z = parts%Z(index1) THET = parts%THET(index1) UR = parts%UR(index1) UTHET = parts%UTHET(index1) UZ = parts%UZ(index1) Rindex = parts%Rindex(index1) Zindex = parts%Zindex(index1) ! Move part at index2 in part at index 1 parts%partindex(index1)= parts%partindex(index2) parts%Gamma(index1) = parts%Gamma(index2) parts%R(index1) = parts%R(index2) parts%Z(index1) = parts%Z(index2) parts%THET(index1) = parts%THET(index2) parts%UR(index1) = parts%UR(index2) parts%UTHET(index1) = parts%UTHET(index2) parts%UZ(index1) = parts%UZ(index2) parts%Rindex(index1) = parts%Rindex(index2) parts%Zindex(index1) = parts%Zindex(index2) ! Move temporary values from part(index1) to part(index2) parts%partindex(index2)= partindex parts%Gamma(index2) = Gamma parts%R(index2) = R parts%Z(index2) = Z parts%THET(index2) = THET parts%UR(index2) = UR parts%UTHET(index2) = UTHET parts%UZ(index2) = UZ parts%Rindex(index2) = Rindex parts%Zindex(index2) = Zindex END SUBROUTINE exchange_parts + + SUBROUTINE add_created_part(buffer) + IMPLICIT NONE + TYPE(particle), DIMENSION(:), INTENT(in) :: buffer + INTEGER::i, nptotinit + nptotinit=parts%Nptot+1 + DO i=1,size(buffer,1) + ! if there is still space in the parts simulation buffer, copy the particle + IF(parts%Nptot .eq. size(parts%Z,1)) THEN + WRITE(*,*) "Particle buffer full: impossible to create part with maxwellian source" + RETURN + END IF + parts%Nptot=parts%Nptot+1 + CALL Insertincomingpart(buffer,i,parts%Nptot) + END DO + + ! Compute the energy added by these new particles for energy conservation diagnostic + IF(nptotinit .le. parts%Nptot) THEN + CALL getgrad(splrz, parts%Z(nptotinit:parts%Nptot), parts%R(nptotinit:parts%Nptot), & + & parts%pot(nptotinit:parts%Nptot), parts%EZ(nptotinit:parts%Nptot), parts%ER(nptotinit:parts%Nptot)) + parts%EZ(nptotinit:parts%Nptot)=-parts%Ez(nptotinit:parts%Nptot) + parts%ER(nptotinit:parts%Nptot)=-parts%ER(nptotinit:parts%Nptot) + + loc_etot0=loc_etot0+qsim*sum(parts%pot(nptotinit:parts%Nptot))*phinorm + IF(.not. nlclassical) THEN + loc_etot0=loc_etot0+sum(msim*vlight**2*(parts%Gamma(nptotinit:parts%Nptot)-1)) + ELSE + loc_etot0=loc_etot0+0.5*msim*vlight**2*sum(parts%UR(nptotinit:parts%Nptot)**2 & + & +parts%UZ(nptotinit:parts%Nptot)**2 & + & +parts%UTHET(nptotinit:parts%Nptot)**2) + END IF + END IF + END SUBROUTINE + !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> !> @brief Delete particle at given index removing its energy from the system ! !> @param [in] index index of particle to be deleted !--------------------------------------------------------------------------- SUBROUTINE delete_part(index) !! This will destroy particle at index USE constants, ONLY: vlight USE basic, ONLY: qsim, phinorm, msim, nlclassical INTEGER, INTENT(IN) :: index IF(index .le. parts%Nptot) THEN loc_etot0=loc_etot0-qsim*parts%pot(index)*phinorm IF(.not. nlclassical) THEN loc_etot0=loc_etot0-msim*vlight**2*(parts%Gamma(index)-1) ELSE loc_etot0=loc_etot0-0.5*msim*vlight**2*(abs(parts%URold(index)*parts%UR(index))+abs(parts%UZold(index)*parts%UZ(index))+abs(parts%UTHETold(index)*parts%UTHET(index))) END IF ! We fill the gap CALL move_part(parts%Nptot, index) parts%partindex(parts%Nptot)=-1 ! Reduce the total number of simulated parts parts%Nptot=parts%Nptot-1 END IF END SUBROUTINE delete_part !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> !> @brief Move particle with index sourceindex to particle with index destindex. !> !WARNING! This will destroy particle at destindex. ! !> @param [in] sourceindex index in parts of the particle to move. !> @param [in] destindex index in parts of the moved particle destination. !--------------------------------------------------------------------------- SUBROUTINE move_part(sourceindex, destindex) !! This will destroy particle at destindex INTEGER, INTENT(IN) :: destindex, sourceindex ! Move part at sourceindex in part at destindex parts%partindex(destindex)= parts%partindex(sourceindex) parts%Gamma(destindex) = parts%Gamma(sourceindex) parts%R(destindex) = parts%R(sourceindex) parts%Z(destindex) = parts%Z(sourceindex) parts%THET(destindex) = parts%THET(sourceindex) parts%UR(destindex) = parts%UR(sourceindex) parts%UTHET(destindex) = parts%UTHET(sourceindex) parts%UZ(destindex) = parts%UZ(sourceindex) parts%Rindex(destindex) = parts%Rindex(sourceindex) parts%Zindex(destindex) = parts%Zindex(sourceindex) END SUBROUTINE move_part - -!--------------------------------------------------------------------------- -!> @author -!> Trach Minh Tran EPFL/SPC -! -! DESCRIPTION: -!> -!> @brief Load a uniform distribution using the Hammersley's sequence (0<=y<=1). -!> (nbase = 0 => Random sampling) -! -!> @param [in] nbase Base of the Hammersley's sequence. (0 => Random) -!> @param [out] y array containing the random variables. -!--------------------------------------------------------------------------- - SUBROUTINE loduni(nbase, y) -! -! Load a uniform distribution using the Hammersley's sequence. -! (nbase = 0 => Random sampling) -! - INTEGER, INTENT(in) :: nbase - DOUBLE PRECISION, INTENT(inout) :: 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 - -!--------------------------------------------------------------------------- -!> @author -!> Trach Minh Tran EPFL/SPC -! -! DESCRIPTION: -!> -!> @brief Load a gaussian distribution using a uniform distribution according to the Hammersley's sequence. -!> (nbase = 0 => Random sampling) -! -!> @param [in] nbase Base of the Hammersley's sequence. (0 => Random) -!> @param [out] y array containing the random variables. -!--------------------------------------------------------------------------- - SUBROUTINE lodgaus(nbase, y) -! -! Sample y from the Gauss distributrion -! - DOUBLE PRECISION, INTENT(inout) :: 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 + 0.99998*r -! - DO i=1,n - iflag = 1 - IF (r(i) .GT. 0.5) THEN - r(i) = 1.0 - r(i) - iflag = -1 - END IF - t = SQRT(LOG(1.0/(r(i)*r(i)))) - y(i) = t - (c0+c1*t+c2*t**2) / (1.0+d1*t+d2*t**2+d3*t**3) - y(i) = y(i) * iflag - END DO - y = y - SUM(y)/REAL(n) - 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 lodlinr(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+sqrt(r)*(rb-ra) - END SUBROUTINE lodlinr - !________________________________________________________________________________ - 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 -!________________________________________________________________________________ - DOUBLE PRECISION 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 - DOUBLE PRECISION :: xs, xsi -!----------------------------------------------------------------------- - xs = 0.0 - xsi = 1.0 - 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 !________________________________________________________________________________ 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, 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, 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(4*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/deltar2*sqrt(1-P0*abs(omegac)/2/H0*deltar2+sqrt(1-P0*abs(omegac)/H0*deltar2)) ra(n)=r0/deltar2*sqrt(1-P0*abs(omegac)/2/H0*deltar2-sqrt(1-P0*abs(omegac)/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=P0/rg/me Acomp=-SIGN(elchar/me*Athetpos,qsim) VTHET(i)=SIGN(MIN(abs(Pcomp+Acomp),sqrt(2*H0/me)),Pcomp+Acomp) !VTHET(i)=Pcomp+Acomp END DO VTHET=VTHET/vnorm VZ=0._db VR=0._db END IF END SUBROUTINE loadDavidson !=============================================================================== SUBROUTINE Temp_rescale USE basic, ONLY: temprescale, nlclassical, nz ! INTEGER:: i, gridindex ! DOUBLE PRECISION :: vr, vz, vthet ! DO i=1,parts%Nptot ! gridindex=parts%zindex(i)+1+parts%rindex(i)*nz ! vr=parts%UR(i)/parts%Gamma(i)-fluidur(gridindex) ! vz=parts%UZ(i)/parts%Gamma(i)-fluiduz(gridindex) ! vthet=parts%UTHET(i)/parts%Gamma(i)-fluiduthet(gridindex) ! parts%UR(i)=vr*temprescale+fluidur(gridindex) ! parts%UTHET(i)=vthet*temprescale+fluiduthet(gridindex) ! parts%UZ(i)=vz*temprescale+fluiduz(gridindex) ! IF(nlclassical) THEN ! parts%Gamma(i)=1.0 ! ELSE ! parts%Gamma(i)=1/sqrt(1-(parts%UR(i)**2+parts%UTHET(i)**2+parts%UZ(i)**2)) ! END IF ! parts%UR(i)=parts%UR(i)*parts%Gamma(i) ! parts%UTHET(i)=parts%UTHET(i)*parts%Gamma(i) ! parts%UZ(i)=parts%UZ(i)*parts%Gamma(i) ! END DO END SUBROUTINE Temp_rescale SUBROUTINE upsample(samplefactor) USE basic, ONLY : nplasma, qsim, msim, dt, dr, dz INTEGER, INTENT(IN) ::samplefactor INTEGER:: i, j, currentindex DOUBLE PRECISION, DIMENSION(parts%Nptot) :: spreaddir ! random direction for the spread of each initial macro particle DOUBLE PRECISION :: dir ! Direction in which the particle is moved DOUBLE PRECISION :: dl ! Particle displacement used for ! Load and scale the direction angle for spreading the new particles CALL loduni(2, spreaddir) spreaddir=spreaddir*2*pi/samplefactor dl=min(dz,minval(dr))/100 DO i=1,parts%Nptot DO j=1,samplefactor-1 currentindex=parts%Nptot+(i-1)*(samplefactor-1)+j CALL move_part(i,currentindex) parts%partindex(currentindex)=currentindex dir = spreaddir(i)+2*pi*j/samplefactor parts%R(currentindex)=parts%R(currentindex) + dl*cos(dir) parts%Z(currentindex)=parts%Z(currentindex) + dl*sin(dir) END DO parts%partindex(i)=i parts%R(i)=parts%R(i) + dl*cos(spreaddir(i)) parts%Z(i)=parts%Z(i) + dl*sin(spreaddir(i)) END DO nplasma=nplasma*samplefactor qsim=qsim/samplefactor msim=msim/samplefactor parts%Nptot=parts%Nptot*samplefactor END SUBROUTINE upsample END MODULE beam diff --git a/src/distrib_mod.f90 b/src/distrib_mod.f90 new file mode 100644 index 0000000..f5aae59 --- /dev/null +++ b/src/distrib_mod.f90 @@ -0,0 +1,266 @@ +!------------------------------------------------------------------------------ +! EPFL/Swiss Plasma Center +!------------------------------------------------------------------------------ +! +! MODULE: distrib +! +!> @author +!> Guillaume Le Bars EPFL/SPC +!> Trach Minh Tran EPFL/SPC +! +! DESCRIPTION: +!> Contains the routines used to load several type of distribution functions +!------------------------------------------------------------------------------ + +MODULE distrib + ! + IMPLICIT NONE +contains + +!--------------------------------------------------------------------------- +!> @author +!> Trach Minh Tran EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief Load a uniform distribution using the Hammersley's sequence (0<=y<=1). +!> (nbase = 0 => Random sampling) +! +!> @param [in] nbase Base of the Hammersley's sequence. (0 => Random) +!> @param [out] y array containing the random variables. +!--------------------------------------------------------------------------- +SUBROUTINE loduni(nbase, y) + ! + ! Load a uniform distribution using the Hammersley's sequence. + ! (nbase = 0 => Random sampling) + ! + INTEGER, INTENT(in) :: nbase + DOUBLE PRECISION, INTENT(inout) :: 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 + + !--------------------------------------------------------------------------- + !> @author + !> Trach Minh Tran EPFL/SPC + ! + ! DESCRIPTION: + !> + !> @brief Load a gaussian distribution using a uniform distribution according to the Hammersley's sequence. + !> (nbase = 0 => Random sampling) + ! + !> @param [in] nbase Base of the Hammersley's sequence. (0 => Random) + !> @param [out] y array containing the random variables. + !--------------------------------------------------------------------------- + SUBROUTINE lodgaus(nbase, y) + ! + ! Sample y from the Gauss distributrion + ! + DOUBLE PRECISION, INTENT(inout) :: 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 + 0.99998*r + ! + DO i=1,n + iflag = 1 + IF (r(i) .GT. 0.5) THEN + r(i) = 1.0 - r(i) + iflag = -1 + END IF + t = SQRT(LOG(1.0/(r(i)*r(i)))) + y(i) = t - (c0+c1*t+c2*t**2) / (1.0+d1*t+d2*t**2+d3*t**3) + y(i) = y(i) * iflag + END DO + y = y - SUM(y)/REAL(n) + END SUBROUTINE lodgaus + !--------------------------------------------------------------------------- + !> @author + !> Guillaume Le Bars EPFL/SPC + ! + ! DESCRIPTION: + !> + !> @brief Load the array y according to a probability distribution function proportional to 1/r + !> between ra and rb + ! + !> @param [in] nbase Base of the Hammersley's sequence. (0 => Random) + !> @param [in] ra Lower limit for the values in y + !> @param [in] rb Upper limit for the values in y + !> @param [out] y array containing the random variables. + !--------------------------------------------------------------------------- + SUBROUTINE lodinvr(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) + r=r*log(rb/ra) + y=ra*exp(r) + END SUBROUTINE lodinvr + !--------------------------------------------------------------------------- + !> @author + !> Guillaume Le Bars EPFL/SPC + ! + ! DESCRIPTION: + !> + !> @brief Load the array y according to a probability distribution function proportional to r + !> between ra and rb + ! + !> @param [in] nbase Base of the Hammersley's sequence. (0 => Random) + !> @param [in] ra Lower limit for the values in y + !> @param [in] rb Upper limit for the values in y + !> @param [out] y array containing the random variables. + !--------------------------------------------------------------------------- + SUBROUTINE lodlinr(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+sqrt(r)*(rb-ra) + END SUBROUTINE lodlinr + !--------------------------------------------------------------------------- + !> @author + !> Guillaume Le Bars EPFL/SPC + ! + ! DESCRIPTION: + !> + !> @brief Load the array y according to a uniform probability distribution function + !> between ra and rb + ! + !> @param [in] nbase Base of the Hammersley's sequence. (0 => Random) + !> @param [in] ra Lower limit for the values in y + !> @param [in] rb Upper limit for the values in y + !> @param [out] y array containing the random variables. + !--------------------------------------------------------------------------- + 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 + !--------------------------------------------------------------------------- + !> @author + !> Guillaume Le Bars EPFL/SPC + ! + ! DESCRIPTION: + !> + !> @brief Load the array y according to a gaussian probability distribution function + !> around the mean of rb and ra and a sigma of (rb-ra)/10 + ! + !> @param [in] nbase Base of the Hammersley's sequence. (0 => Random) + !> @param [in] ra Lower limit for the values in y + !> @param [in] rb Upper limit for the values in y + !> @param [out] y array containing the random variables. + !--------------------------------------------------------------------------- + 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 + !--------------------------------------------------------------------------- + !> @author + !> Trach Minh Tran EPFL/SPC + ! + ! DESCRIPTION: + !> + !> @brief Return an element of the Hammersley's sequence + ! + !> @param [in] nbase Base of the Hammersley's sequence. (0 => Random) + !> @param [in] i Index of the sequence. + !> @param [out] rev Returned element of the Hammersley's sequence + !--------------------------------------------------------------------------- + DOUBLE PRECISION FUNCTION rev(nbase,i) + ! + 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 + DOUBLE PRECISION :: xs, xsi + !----------------------------------------------------------------------- + xs = 0.0 + xsi = 1.0 + 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 + !--------------------------------------------------------------------------- + !> @author + !> Trach Minh Tran EPFL/SPC + ! + ! DESCRIPTION: + !> + !> @brief Modified Bessel functions of the first kind of the first order 1 + ! + !--------------------------------------------------------------------------- + + 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 + +END MODULE distrib \ No newline at end of file