diff --git a/src/matcha/distribution_m.f90 b/src/matcha/distribution_m.f90 index 5c132fe..41a3599 100644 --- a/src/matcha/distribution_m.f90 +++ b/src/matcha/distribution_m.f90 @@ -8,17 +8,18 @@ module distribution_m type distribution_t private - double precision, allocatable, dimension(:) :: vel_, cumulative_distribution_ + double precision, allocatable, dimension(:) :: vel_,cumulative_distribution_,angle_,cumulative_angle_distribution_ contains procedure :: cumulative_distribution + procedure :: cumulative_angle_distribution procedure :: velocities end type interface distribution_t - pure module function construct(sample_distribution) result(distribution) + pure module function construct(sample_distribution,sample_angle_distribution) result(distribution) implicit none - double precision, intent(in) :: sample_distribution(:,:) + double precision, intent(in) :: sample_distribution(:,:),sample_angle_distribution(:,:) type(distribution_t) distribution end function @@ -30,13 +31,20 @@ pure module function cumulative_distribution(self) result(my_cumulative_distribu implicit none class(distribution_t), intent(in) :: self double precision, allocatable :: my_cumulative_distribution(:) - end function + end function cumulative_distribution + + pure module function cumulative_angle_distribution(self) result(my_cumulative_angle_distribution) + implicit none + class(distribution_t), intent(in) :: self + double precision, allocatable :: my_cumulative_angle_distribution(:) + end function cumulative_angle_distribution + - pure module function velocities(self, speeds, directions) result(my_velocities) + pure module function velocities(self, speeds, angles, directions) result(my_velocities) !! Return the t_cell_collection_t object's velocity vectors implicit none class(distribution_t), intent(in) :: self - double precision, intent(in) :: speeds(:,:), directions(:,:,:) + double precision, intent(in) :: speeds(:,:),angles(:,:),directions(:,:,:) double precision, allocatable :: my_velocities(:,:,:) end function velocities diff --git a/src/matcha/distribution_s.F90 b/src/matcha/distribution_s.F90 index fff76fb..6369b61 100644 --- a/src/matcha/distribution_s.F90 +++ b/src/matcha/distribution_s.F90 @@ -2,7 +2,8 @@ ! Terms of use are as specified in LICENSE.tx submodule(distribution_m) distribution_s use intrinsic_array_m, only : intrinsic_array_t - use do_concurrent_m, only : do_concurrent_sampled_speeds, do_concurrent_my_velocities +! use do_concurrent_m, only : do_concurrent_sampled_speeds, do_concurrent_my_velocities + use do_concurrent_m, only : do_concurrent_sample, do_concurrent_my_velocities use assert_m, only : assert implicit none @@ -30,6 +31,20 @@ pure function monotonically_increasing(f) result(monotonic) intrinsic_array_t(distribution%cumulative_distribution_)) end associate + + call assert(all(sample_angle_distribution(:,2)>=0.D0), "distribution_t%construct: sample_distribution>=0.", & + intrinsic_array_t(sample_angle_distribution)) + + associate(nintervals => size(sample_angle_distribution,1)) + distribution%angle_ = [(sample_angle_distribution(i,1), i =1, nintervals)] ! Assign speeds to each distribution bin + distribution%cumulative_angle_distribution_ = [0.D0, [(sum(sample_angle_distribution(1:i,2)), i=1, nintervals)]] + + call assert(monotonically_increasing(distribution%cumulative_angle_distribution_), & + "distribution_t: monotonically_increasing(distribution%cumulative_distribution_)", & + intrinsic_array_t(distribution%cumulative_angle_distribution_)) + end associate + + end procedure construct module procedure cumulative_distribution @@ -37,36 +52,135 @@ pure function monotonically_increasing(f) result(monotonic) "distribution_t%cumulative_distribution: allocated(cumulative_distribution_)") my_cumulative_distribution = self%cumulative_distribution_ end procedure + + module procedure cumulative_angle_distribution + call assert(allocated(self%cumulative_angle_distribution_), & + "distribution_t%cumulative_angle_distribution: allocated(cumulative_angle_distribution_)") + my_cumulative_angle_distribution = self%cumulative_angle_distribution_ + end procedure + + module procedure velocities - double precision, allocatable :: sampled_speeds(:,:), dir(:,:,:) - integer step + double precision, allocatable :: sampled_speeds(:,:), sampled_angles(:,:), dir(:,:,:) + integer cell,step + + double precision dir_mag_ + + double precision pi,twopi + double precision a,b,c + double precision x1,y1,z1,x2,y2,z2,x3,y3,z3,vec1mag + double precision vxp,vyp,vzp,eps,vec3mag + double precision random_phi + double precision dot,adot,magv + call assert(allocated(self%cumulative_distribution_), & "distribution_t%cumulative_distribution: allocated(cumulative_distribution_)") call assert(allocated(self%vel_), "distribution_t%cumulative_distribution: allocated(vel_)") + call assert(allocated(self%cumulative_angle_distribution_), & + "distribution_t%cumulative_angle_distribution: allocated(cumulative_angle_distribution_)") + call assert(allocated(self%angle_), "distribution_t%cumulative_angle_distribution: allocated(angle_)") + ! Sample from the distribution - call do_concurrent_sampled_speeds(speeds, self%vel_, self%cumulative_distribution(), sampled_speeds) - - associate(nsteps => size(speeds,2)) - - ! Create unit vectors - dir = directions(:,1:nsteps,:) - - associate(dir_mag => sqrt(dir(:,:,1)**2 +dir(:,:,2)**2 + dir(:,:,3)**2)) - associate(dir_mag_ => merge(dir_mag, epsilon(dir_mag), dir_mag/=0.)) - dir(:,:,1) = dir(:,:,1)/dir_mag_ - dir(:,:,2) = dir(:,:,2)/dir_mag_ - dir(:,:,3) = dir(:,:,3)/dir_mag_ - end associate - end associate - - call do_concurrent_my_velocities(nsteps, dir, sampled_speeds, my_velocities) - + !call do_concurrent_sampled_speeds(speeds, self%vel_, self%cumulative_distribution(), sampled_speeds) + call do_concurrent_sample(speeds, self%vel_, self%cumulative_distribution(), sampled_speeds) + call do_concurrent_sample(angles, self%angle_, self%cumulative_angle_distribution(), sampled_angles) + + !print*,'sampled_angles = ',sampled_angles +! associate(nsteps => size(speeds,2)) +! +! ! Create unit vectors +! dir = directions(:,1:nsteps,:) +! +! associate(dir_mag => sqrt(dir(:,:,1)**2 +dir(:,:,2)**2 + dir(:,:,3)**2)) +! associate(dir_mag_ => merge(dir_mag, epsilon(dir_mag), dir_mag/=0.)) +! dir(:,:,1) = dir(:,:,1)/dir_mag_ +! dir(:,:,2) = dir(:,:,2)/dir_mag_ +! dir(:,:,3) = dir(:,:,3)/dir_mag_ +! end associate +! end associate +! +! call do_concurrent_my_velocities(nsteps, dir, sampled_speeds, my_velocities) +! +! end associate + + associate(ncells => size(speeds,1), nsteps => size(speeds,2)) + + allocate(my_velocities(ncells,nsteps,3)) + + pi = acos(-1.d0) + twopi = 2.d0*pi + eps = 1.d-12 + +! do cell = 1,ncells + do concurrent(cell = 1:ncells) + do step = 1,nsteps + random_phi = 2.d0*pi*directions(cell,step,1) + vxp = sampled_speeds(cell,step)*sin(sampled_angles(cell,step))*cos(random_phi) + vyp = sampled_speeds(cell,step)*sin(sampled_angles(cell,step))*sin(random_phi) + vzp = sampled_speeds(cell,step)*cos(sampled_angles(cell,step)) + if (step .eq. 1) then + my_velocities(cell,step,1) = vxp + my_velocities(cell,step,2) = vyp + my_velocities(cell,step,3) = vzp + else + x3 = my_velocities(cell,step-1,1) + y3 = my_velocities(cell,step-1,2) + z3 = my_velocities(cell,step-1,3) + vec3mag = sqrt(x3**2 + y3**2 + z3**2)+eps + x3 = x3/vec3mag + y3 = y3/vec3mag + z3 = z3/vec3mag + + if (abs(z3) .gt. eps) then + a = 0.d0 + b = 1.d0 + c = 0.d0 + else + a = 0.d0 + b = 0.d0 + c = 1.d0 + end if + + !curl(x3,y3,z3,a,b,c,x1,y1,z1) + x1 = y3*c - z3*b + y1 = z3*a - x3*c + z1 = x3*b - y3*a + vec1mag = sqrt(x1**2 + y1**2 + z1**2) + eps + x1 = x1/vec1mag + y1 = y1/vec1mag + z1 = z1/vec1mag + ! curl(x3,y3,z3,x1,y1,z1,x2,y2,z2) + x2 = y3*z1 - z3*y1 + y2 = z3*x1 - x3*z1 + z2 = x3*y1 - y3*x1 + + + my_velocities(cell,step,1) = vxp*x1 + vyp*x2 + vzp*x3 + my_velocities(cell,step,2) = vxp*y1 + vyp*y2 + vzp*y3 + my_velocities(cell,step,3) = vxp*z1 + vyp*z2 + vzp*z3 + + !magv = dsqrt(my_velocities(cell,step,1)**2 + & + ! my_velocities(cell,step,2)**2 + & + ! my_velocities(cell,step,3)**2) + !dot = (my_velocities(cell,step,1)*x3 + & + ! my_velocities(cell,step,2)*y3 + & + ! my_velocities(cell,step,3)*z3)/magv + !adot = acos(dot) + !print*,'adot = ',adot,sampled_angles(cell,step) + + end if + + end do + end do + end associate + + end procedure end submodule distribution_s diff --git a/src/matcha/do_concurrent_m.f90 b/src/matcha/do_concurrent_m.f90 index 21869c1..feaca52 100644 --- a/src/matcha/do_concurrent_m.f90 +++ b/src/matcha/do_concurrent_m.f90 @@ -3,10 +3,18 @@ module do_concurrent_m use t_cell_collection_m, only : t_cell_collection_t, t_cell_collection_bind_C_t implicit none private - public :: do_concurrent_sampled_speeds, do_concurrent_my_velocities, do_concurrent_k, do_concurrent_speeds + public :: do_concurrent_sample, do_concurrent_sampled_speeds, do_concurrent_my_velocities, do_concurrent_k + public :: do_concurrent_angles, do_concurrent_speeds public :: do_concurrent_output_distribution interface + + pure module subroutine do_concurrent_sample(rvalues, val, cumulative_distribution, sampled_values) bind(C) + implicit none + real(c_double), intent(in) :: rvalues(:,:), val(:), cumulative_distribution(:) + real(c_double), intent(out), allocatable :: sampled_values(:,:) + end subroutine + pure module subroutine do_concurrent_sampled_speeds(speeds, vel, cumulative_distribution, sampled_speeds) bind(C) implicit none @@ -40,7 +48,14 @@ module subroutine do_concurrent_speeds(history, speeds) bind(C) type(t_cell_collection_bind_C_t), intent(in) :: history(:) real(c_double), intent(out), allocatable :: speeds(:) end subroutine - + + module subroutine do_concurrent_angles(history, angles) bind(C) + implicit none + type(t_cell_collection_bind_C_t), intent(in) :: history(:) + real(c_double), intent(out), allocatable :: angles(:) + end subroutine + + end interface end module do_concurrent_m diff --git a/src/matcha/do_concurrent_s.f90 b/src/matcha/do_concurrent_s.f90 index 80c37e7..b62579a 100644 --- a/src/matcha/do_concurrent_s.f90 +++ b/src/matcha/do_concurrent_s.f90 @@ -3,7 +3,22 @@ implicit none contains - + + module procedure do_concurrent_sample + + integer cell, step + + associate(ncells => size(rvalues,1), nsteps => size(rvalues,2)) + allocate(sampled_values(ncells,nsteps)) + do concurrent(cell = 1:ncells, step = 1:nsteps) + associate(k => findloc(rvalues(cell,step) >= cumulative_distribution, value=.false., dim=1)-1) + sampled_values(cell,step) = val(k) + end associate + end do + end associate + + end procedure + module procedure do_concurrent_sampled_speeds integer cell, step @@ -68,14 +83,14 @@ real(c_double), pointer :: positions(:,:) real(c_double), allocatable :: x(:,:,:) - + associate(npositions => size(history), ncells => history(1)%positions_shape(1)) allocate(x(npositions,ncells,nspacedims)) do i=1,npositions - call c_f_pointer(history(i)%positions_ptr, positions, history(1)%positions_shape) - x(i,:,:) = positions + call c_f_pointer(history(i)%positions_ptr, positions, history(1)%positions_shape) + x(i,:,:) = positions end do associate(t => history%time) @@ -89,8 +104,46 @@ end associate end do end associate + end associate end procedure + + module procedure do_concurrent_angles + + integer i, j, k + integer, parameter :: nspacedims=3 + + real(c_double), pointer :: positions(:,:) + real(c_double), allocatable :: x(:,:,:) + real(c_double) v1mag,v2mag,eps + + eps = 1.d-16 + associate(npositions => size(history), ncells => history(1)%positions_shape(1)) + + allocate(x(npositions,ncells,nspacedims)) + + do i=1,npositions + call c_f_pointer(history(i)%positions_ptr, positions, history(1)%positions_shape) + x(i,:,:) = positions + end do + + allocate(angles(ncells*(npositions-2)) ) + do concurrent(i = 1:npositions-2, j = 1:ncells) + associate( & + v1 => x(i+1,j,:) - x(i,j,:), & + v2 => x(i+2,j,:) - x(i+1,j,:), & + ij => i + (j-1)*(npositions-2) & + ) + v1mag = sqrt(sum([(v1(k)**2, k=1,nspacedims)])) + v2mag = sqrt(sum([(v2(k)**2, k=1,nspacedims)])) + angles(ij) = acos( (v1(1)*v2(1)+v1(2)*v2(2)+v1(3)*v2(3))/(v1mag*v2mag+eps) ) + end associate + end do + + end associate + + end procedure + end submodule do_concurrent_s diff --git a/src/matcha/grid_m.f90 b/src/matcha/grid_m.f90 new file mode 100644 index 0000000..c18c9b2 --- /dev/null +++ b/src/matcha/grid_m.f90 @@ -0,0 +1,53 @@ +! Copyright (c), The Regents of the University of California +! Terms of use are as specified in LICENSE.txt + +module gridr_m + + use subdomain_m, only : subdomain_t + implicit none + + private + public :: gridr_t + + type gridr_t + private + contains + procedure :: gridparameters + procedure :: gradient + end type + + interface gridr_t + + pure module function construct() result(gridr) + implicit none + type(gridr_t) gridr + end function + + end interface + + interface + + pure module function gridparameters(self,gb,ge,ng,ndim) result(gridp) + implicit none + class(gridr_t), intent(in) :: self + integer, intent(in) :: ndim + integer, intent(in) :: ng(:) + double precision, intent(in) :: gb,ge + double precision, allocatable :: gridp(:) + end function gridparameters + + module function gradient(self, my_num_cells, ng, dx, gb, tconc, x, concentration_subgrid) result(gx) + implicit none + class(gridr_t), intent(in) :: self + integer, intent(in) :: my_num_cells + integer, intent(in) :: ng(:) + double precision, intent(in) :: dx(:) + double precision, intent(in) :: gb,tconc + double precision, intent(in) :: x(:,:) + double precision, allocatable :: gx(:,:) + type(subdomain_t) concentration_subgrid + end function gradient + + end interface + +end module gridr_m diff --git a/src/matcha/grid_s.f90 b/src/matcha/grid_s.f90 new file mode 100644 index 0000000..a2b622c --- /dev/null +++ b/src/matcha/grid_s.f90 @@ -0,0 +1,89 @@ +! Copyright (c), The Regents of the University of California +! Terms of use are as specified in LICENSE.tx +submodule(gridr_m) gridr_s + use subdomain_m, only : subdomain_t + +!#ifdef USE_CAFFEINE +! use caffeine_assert_m, only : assert +!#else +! use assert_m, only : assert +!#endif + + implicit none + +contains + + module procedure construct + end procedure construct + + module procedure gridparameters + + if (allocated(gridp)) deallocate(gridp) + allocate(gridp(2*ndim+1)) + + gridp(1) = (ge - gb)/dble(ng(1)) ! dx for 3D diffusion grid + gridp(2) = (ge - gb)/dble(ng(2)) ! dy for 3D diffusion grid + gridp(3) = (ge - gb)/dble(ng(3)) ! dz for 3D diffusion grid + + gridp(4) = 1.d0/(2.d0*gridp(1)) + gridp(5) = 1.d0/(2.d0*gridp(2)) + gridp(6) = 1.d0/(2.d0*gridp(3)) + + gridp(7) = 1.d0/(gridp(1)*gridp(2)*gridp(3)) ! Reciprocal of volume element in 3D diffusion grid + + end procedure gridparameters + + + module procedure gradient + integer i,j,k,ii,jj,kk + integer nxf,nxl,nxc + double precision, allocatable :: concentration(:,:,:) + double precision, allocatable :: grad(:,:,:,:) + + allocate(concentration(ng(1),ng(2),ng(3))) + allocate(grad(ng(1),ng(2),ng(3),3)) + + concentration = 0.d0 + grad = 0.d0 + + nxf = concentration_subgrid%nxfirst() + nxl = concentration_subgrid%nxlast() + nxc = concentration_subgrid%nx_cumulative() + + do concurrent(i = nxf:nxl, j = 2:ng(2)-1, k = 2:ng(3)-1) + concentration(i+nxc,j,k) = concentration_subgrid%s_(i,j,k) + end do + + + do i = 1,my_num_cells + ii = int((x(i,1) - gb)/dx(1)) + 1 + jj = int((x(i,2) - gb)/dx(2)) + 1 + kk = int((x(i,3) - gb)/dx(3)) + 1 + concentration(ii,jj,kk) = concentration(ii,jj,kk) + tconc + end do + + call co_sum(concentration) + + do concurrent(i = 2:ng(1)-1, j = 2:ng(2)-1, k = 2:ng(3)-1) + grad(i,j,k,1) = dx(4)*(concentration(i+1,j,k)-concentration(i-1,j,k)) + grad(i,j,k,2) = dx(5)*(concentration(i,j+1,k)-concentration(i,j-1,k)) + grad(i,j,k,3) = dx(6)*(concentration(i,j,k+1)-concentration(i,j,k-1)) + end do + + allocate(gx(my_num_cells,3)) + do concurrent(i = 1:my_num_cells) + ii = int((x(i,1) - gb)/dx(1)) + 1 + jj = int((x(i,2) - gb)/dx(2)) + 1 + kk = int((x(i,3) - gb)/dx(3)) + 1 + gx(i,:) = grad(ii,jj,kk,:) + end do + + do concurrent(i = nxf:nxl, j = 2:ng(2)-1, k = 2:ng(3)-1) + concentration_subgrid%s_(i,j,k) = concentration(i+nxc,j,k) + end do + + + + end procedure gradient + +end submodule gridr_s diff --git a/src/matcha/input_m.f90 b/src/matcha/input_m.f90 index 9d4a7c9..72c008a 100644 --- a/src/matcha/input_m.f90 +++ b/src/matcha/input_m.f90 @@ -8,17 +8,27 @@ module input_m type input_t private - integer :: num_cells_ = 6000, num_positions_ = 6000, num_dimensions_ = 3, num_intervals_ = 4 - double precision :: time_step_ = 0.1D0 - !double precision, allocatable :: sample_distribution_(:,:) - !allocate(sample_distribution_(num_intervals_,2)) + integer :: num_cells_ = 6000, num_positions_ = 6000, num_dimensions_ = 3 + integer :: num_intervals_ = 4, num_angle_intervals_ = 4, ngrid_ = 50 + + double precision :: time_step_ = 0.1d0 + double precision :: grid_begin_ = -500.d0, grid_end_ = 600.d0, cytokine_ = 1.d-6, gfac_ = 1.d-16 + double precision :: alpha_ = 1.d-10 contains procedure :: num_cells procedure :: num_positions procedure :: num_dimensions procedure :: num_intervals + procedure :: num_angle_intervals + procedure :: ngrid procedure :: time_step + procedure :: grid_begin + procedure :: grid_end + procedure :: cytokine + procedure :: gfac + procedure :: alpha procedure :: sample_distribution + procedure :: sample_angle_distribution end type interface @@ -46,19 +56,70 @@ pure module function num_intervals(self) result(n) class(input_t), intent(in) :: self integer n end function + + pure module function num_angle_intervals(self) result(n) + implicit none + class(input_t), intent(in) :: self + integer n + end function + + + pure module function ngrid(self) result(n) + implicit none + class(input_t), intent(in) :: self + integer n + end function pure module function time_step(self) result(dt) implicit none class(input_t), intent(in) :: self double precision dt end function time_step + + pure module function grid_begin(self) result(gg) + implicit none + class(input_t), intent(in) :: self + double precision gg + end function grid_begin + + pure module function grid_end(self) result(gg) + implicit none + class(input_t), intent(in) :: self + double precision gg + end function grid_end + + pure module function cytokine(self) result(gg) + implicit none + class(input_t), intent(in) :: self + double precision gg + end function cytokine + + pure module function gfac(self) result(gg) + implicit none + class(input_t), intent(in) :: self + double precision gg + end function gfac + pure module function alpha(self) result(gg) + implicit none + class(input_t), intent(in) :: self + double precision gg + end function alpha + + pure module function sample_distribution(self) result(empirical_distribution) implicit none class(input_t), intent(in) :: self double precision, allocatable :: empirical_distribution(:,:) end function sample_distribution - end interface + pure module function sample_angle_distribution(self) result(empirical_angle_distribution) + implicit none + class(input_t), intent(in) :: self + double precision, allocatable :: empirical_angle_distribution(:,:) + end function sample_angle_distribution + + + end interface end module input_m diff --git a/src/matcha/input_s.f90 b/src/matcha/input_s.f90 index 3492f24..643b952 100644 --- a/src/matcha/input_s.f90 +++ b/src/matcha/input_s.f90 @@ -21,10 +21,38 @@ n = self%num_intervals_ end procedure + module procedure num_angle_intervals + n = self%num_angle_intervals_ + end procedure + + module procedure ngrid + n = self%ngrid_ + end procedure + module procedure time_step dt = self%time_step_ end procedure + + module procedure grid_begin + gg = self%grid_begin_ + end procedure + + module procedure grid_end + gg = self%grid_end_ + end procedure + + module procedure cytokine + gg = self%cytokine_ + end procedure + module procedure gfac + gg = self%gfac_ + end procedure + + module procedure alpha + gg = self%alpha_ + end procedure + module procedure sample_distribution integer i,nintervals double precision speed_lower,speed_upper @@ -63,6 +91,48 @@ empirical_distribution(i,2) = probability(i) end do - end procedure + end procedure + + module procedure sample_angle_distribution + integer i,nintervals + double precision angle_lower,angle_upper + double precision range,pi,dangle,sumy + double precision angle_lower_bin + double precision angle_upper_bin + double precision angle_middle_bin + double precision pi + double precision, allocatable :: angles(:),probability(:) + nintervals = self%num_angle_intervals_ + + pi = acos(-1.d0) + angle_lower = 0.d0 + angle_upper = pi + range = angle_upper - angle_lower + pi = acos(-1.d0) + dangle = range/dble(nintervals) + allocate(angles(nintervals),probability(nintervals)) + + sumy = 0.d0 + do i = 1,nintervals + angle_lower_bin = angle_lower + dble(i-1)*dangle + angle_upper_bin = angle_lower + dble(i)*dangle + angle_middle_bin = 0.5d0*(angle_lower_bin + angle_upper_bin) + angles(i) = angle_middle_bin + probability(i) = 1.d0/dble(nintervals) ! Use uniform distribution + sumy = sumy + probability(i) + end do + + do i = 1,nintervals + probability(i) = probability(i)/sumy + end do + + allocate(empirical_angle_distribution(nintervals,2)) + + do i = 1,nintervals + empirical_angle_distribution(i,1) = angles(i) + empirical_angle_distribution(i,2) = probability(i) + end do + + end procedure end submodule input_s diff --git a/src/matcha/output_m.f90 b/src/matcha/output_m.f90 index abfa31f..7f09720 100644 --- a/src/matcha/output_m.f90 +++ b/src/matcha/output_m.f90 @@ -16,6 +16,7 @@ module output_m type(t_cell_collection_t), allocatable :: history_(:) contains procedure :: simulated_distribution + procedure :: simulated_distribution_angles procedure :: my_num_cells end type @@ -34,11 +35,21 @@ pure module function construct(input, history) result(output) interface module function simulated_distribution(self) result(output_distribution) + !result(output_distribution,output_angle_distribution) !! The result is a histogram calculated from the simulation output implicit none class(output_t), intent(in) :: self double precision, allocatable :: output_distribution(:,:) + end function + + module function simulated_distribution_angles(self) result(output_angle_distribution) + !result(output_distribution,output_angle_distribution) + !! The result is a histogram calculated from the simulation output + implicit none + class(output_t), intent(in) :: self + double precision, allocatable :: output_angle_distribution(:,:) end function + pure module function my_num_cells(self) result(num_cells) implicit none diff --git a/src/matcha/output_s.f90 b/src/matcha/output_s.f90 index 1bac989..6ba7089 100644 --- a/src/matcha/output_s.f90 +++ b/src/matcha/output_s.f90 @@ -1,7 +1,8 @@ ! Copyright (c), The Regents of the University of California ! Terms of use are as specified in LICENSE.txt submodule(output_m) output_s - use do_concurrent_m, only : do_concurrent_k, do_concurrent_output_distribution, do_concurrent_speeds + use do_concurrent_m, only : do_concurrent_k, do_concurrent_output_distribution, & + do_concurrent_speeds, do_concurrent_angles use t_cell_collection_m, only : t_cell_collection_bind_C_t use iso_c_binding, only : c_loc, c_double implicit none @@ -37,4 +38,28 @@ end procedure + module procedure simulated_distribution_angles + integer i + integer, allocatable :: ka(:) + real(c_double), allocatable, dimension(:) :: ang, angles + + integer, parameter :: speed=1, freq=2 ! subscripts for speeds and frequencies + + call do_concurrent_angles(t_cell_collection_bind_C_t(self%history_), angles) + + associate(emp_angle_distribution => self%input_%sample_angle_distribution()) + associate(nintervals_angle => size(emp_angle_distribution(:,1)), & + dangle_half => (emp_angle_distribution(2,speed)-emp_angle_distribution(1,speed))/2.d0) + ang = [emp_angle_distribution(1,speed) - dangle_half, & + [(emp_angle_distribution(i,speed) + dangle_half, i=1,nintervals_angle)]] + call do_concurrent_k(angles, ang, ka) + call do_concurrent_output_distribution(nintervals_angle, speed, freq, & + emp_angle_distribution, ka, output_angle_distribution) + output_angle_distribution(:,freq) = output_angle_distribution(:,freq)/sum(output_angle_distribution(:,freq)) + end associate + end associate + + end procedure + + end submodule output_s diff --git a/src/matcha/subdomain_m.f90 b/src/matcha/subdomain_m.f90 index 316193c..71596f9 100644 --- a/src/matcha/subdomain_m.f90 +++ b/src/matcha/subdomain_m.f90 @@ -5,8 +5,7 @@ module subdomain_m public :: subdomain_t type subdomain_t - private - real, allocatable :: s_(:,:) + double precision, allocatable :: s_(:,:,:) contains procedure, pass(self) :: define procedure, pass(self) :: step @@ -20,6 +19,13 @@ module subdomain_m generic :: assignment(=) => assign_and_sync procedure dx procedure dy + procedure dz + procedure mynx + procedure myny + procedure mynz + procedure nxfirst + procedure nxlast + procedure nx_cumulative procedure values end type @@ -27,45 +33,88 @@ module subdomain_m module subroutine define(side, boundary_val, internal_val, n, self) implicit none - real, intent(in) :: side, boundary_val, internal_val + double precision, intent(in) :: side, boundary_val, internal_val integer, intent(in) :: n !! number of grid points in each coordinate direction class(subdomain_t), intent(out) :: self end subroutine module subroutine step(alpha_dt, self) implicit none - real, intent(in) :: alpha_dt + double precision, intent(in) :: alpha_dt class(subdomain_t), intent(inout) :: self end subroutine pure module function values(self) result(my_values) implicit none class(subdomain_t), intent(in) :: self - real, allocatable :: my_values(:,:) + double precision, allocatable :: my_values(:,:,:) end function pure module function dx(self) result(my_dx) implicit none class(subdomain_t), intent(in) :: self - real my_dx + double precision my_dx end function pure module function dy(self) result(my_dy) implicit none class(subdomain_t), intent(in) :: self - real my_dy + double precision my_dy + end function + + pure module function dz(self) result(my_dz) + implicit none + class(subdomain_t), intent(in) :: self + double precision my_dz + end function + + pure module function mynx(self) result(my_mynx) + implicit none + class(subdomain_t), intent(in) :: self + integer my_mynx end function - pure module function laplacian(rhs) result(laplacian_rhs) + pure module function myny(self) result(my_myny) implicit none - class(subdomain_t), intent(in) :: rhs - type(subdomain_t) laplacian_rhs + class(subdomain_t), intent(in) :: self + integer my_myny end function + pure module function mynz(self) result(my_mynz) + implicit none + class(subdomain_t), intent(in) :: self + integer my_mynz + end function + + pure module function nxfirst(self) result(my_nxfirst) + implicit none + class(subdomain_t), intent(in) :: self + integer my_nxfirst + end function + + pure module function nxlast(self) result(my_nxlast) + implicit none + class(subdomain_t), intent(in) :: self + integer my_nxlast + end function + + pure module function nx_cumulative(self) result(my_nx_cumulative) + implicit none + class(subdomain_t), intent(in) :: self + integer my_nx_cumulative + end function + + + module function laplacian(rhs) result(laplacian_rhs) + implicit none + class(subdomain_t), intent(in) :: rhs + type(subdomain_t) laplacian_rhs + end function + pure module function multiply(lhs, rhs) result(product) implicit none class(subdomain_t), intent(in) :: rhs - real, intent(in) :: lhs + double precision, intent(in) :: lhs type(subdomain_t) product end function diff --git a/src/matcha/subdomain_s.f90 b/src/matcha/subdomain_s.f90 index 7dc2c30..b58f3bc 100644 --- a/src/matcha/subdomain_s.f90 +++ b/src/matcha/subdomain_s.f90 @@ -4,57 +4,85 @@ use intrinsic_array_m, only : intrinsic_array_t implicit none - real, allocatable :: halo_x(:,:)[:] + double precision, allocatable :: halo_x(:,:,:)[:] + integer mynxl,mynyl,mynzl integer, parameter :: west=1, east=2 type(data_partition_t) data_partition - real dx_, dy_ - integer my_nx, nx, ny, me, num_subdomains, my_internal_left, my_internal_right + double precision dx_, dy_, dz_ + integer mynx_,myny_,mynz_ + integer nxfirst_, nxlast_ + integer nx_cumulative_ + + integer :: nxbefore[*] + integer nx, me, num_subdomains, my_internal_left, my_internal_right contains module procedure define + integer i integer, parameter :: nx_boundaries = 2 nx = n - ny = nx - dx_ = side/(nx-1) - dy_ = dx_ + myny_ = n + mynz_ = n + + + dx_ = side/dble(nx-1) + dy_ = side/dble(myny_-1) + dz_ = side/dble(mynz_-1) + call assert(num_subdomains <= nx-nx_boundaries, & "subdomain_t%define: num_subdomains <= nx-nx_boundaries", intrinsic_array_t([nx, num_subdomains])) me = this_image() num_subdomains = num_images() call data_partition%define_partitions(nx) - my_nx = data_partition%last(me) - data_partition%first(me) + 1 + mynx_ = data_partition%last(me) - data_partition%first(me) + 1 + + nxbefore[me] = mynx_ + sync all + + nx_cumulative_ = 0 + do i = 1,me-1 + nx_cumulative_ = nx_cumulative_ + nxbefore[i] + end do if (allocated(self%s_)) deallocate(self%s_) - allocate(self%s_(my_nx, ny)) + allocate(self%s_(mynx_, myny_, mynz_)) my_internal_left = merge(2, 1, me==1) - my_internal_right = merge(my_nx-1, my_nx, me==num_subdomains) + my_internal_right = merge(mynx_ - 1, mynx_, me==num_subdomains) + + nxfirst_ = my_internal_left + nxlast_ = my_internal_right + + self%s_(my_internal_left:my_internal_right, 1,:) = boundary_val ! bottom subdomain boundary + self%s_(my_internal_left:my_internal_right, myny_,:) = boundary_val ! top subdomain boundary + + self%s_(my_internal_left:my_internal_right,:,1) = boundary_val ! bottom subdomain boundary + self%s_(my_internal_left:my_internal_right,:,mynz_) = boundary_val ! top subdomain boundary - self%s_(my_internal_left:my_internal_right, 1) = boundary_val ! bottom subdomain boundary - self%s_(my_internal_left:my_internal_right, ny) = boundary_val ! top subdomain boundary - self%s_(my_internal_left:my_internal_right, 2:ny-1) = internal_val ! internal points + self%s_(my_internal_left:my_internal_right, 2:myny_-1, 2:mynz_-1) = internal_val ! internal points if (me == 1) then - self%s_(1, 1:ny) = boundary_val ! left domain boundary + self%s_(1, 1:myny_, 1:mynz_) = boundary_val ! left domain boundary else - self%s_(1, 2:ny-1) = internal_val ! left subdomain boundary + self%s_(1, 2:myny_ - 1, 2:mynz_ - 1) = internal_val ! left subdomain boundary end if if (me == num_subdomains) then - self%s_(my_nx, 1:ny) = boundary_val ! right domain boundary + self%s_(mynx_, 1:myny_, 1:mynz_) = boundary_val ! right domain boundary else - self%s_(my_nx, 2:ny-1) = internal_val ! right subdomain boundary + self%s_(mynx_, 2:myny_ - 1, 2:mynz_ - 1) = internal_val ! right subdomain boundary end if if (allocated(halo_x)) deallocate(halo_x) - allocate(halo_x(west:east, ny)[*]) - if (me>1) halo_x(east,:)[me-1] = self%s_(1,:) - if (me1) halo_x(east,:,:)[me-1] = self%s_(1,:,:) + if (me0,"laplacian: rightmost subdomain too small") - do concurrent(j=2:ny-1) - laplacian_rhs%s_(i,j) = (rhs%s_(i-1,j) - 2*rhs%s_(i,j) + halo_right(j))/dx_**2 + & - (rhs%s_(i,j-1) - 2*rhs%s_(i,j) + rhs%s_(i,j+1))/dy_**2 + do concurrent(j=2:myny_-1, k=2:mynz_-1) + laplacian_rhs%s_(i,j,k) = (rhs%s_(i-1,j,k) - 2*rhs%s_(i,j,k) + halo_right(j,k))/dx_**2 + & + (rhs%s_(i,j-1,k) - 2*rhs%s_(i,j,k) + rhs%s_(i,j+1,k))/dy_**2 + & + (rhs%s_(i,j,k-1) - 2*rhs%s_(i,j,k) + rhs%s_(i,j,k+1))/dz_**2 end do - laplacian_rhs%s_(:, 1) = 0. - laplacian_rhs%s_(:,ny) = 0. - if (me==1) laplacian_rhs%s_(1,:) = 0. - if (me==num_subdomains) laplacian_rhs%s_(my_nx,:) = 0. + laplacian_rhs%s_(:, 1, :) = 0. + laplacian_rhs%s_(:,myny_, :) = 0. + laplacian_rhs%s_(:, :, 1) = 0. + laplacian_rhs%s_(:, :, mynz_) = 0. + if (me==1) laplacian_rhs%s_(1,:,:) = 0. + if (me==num_subdomains) laplacian_rhs%s_(mynx_,:,:) = 0. + end procedure module procedure multiply @@ -118,8 +181,8 @@ call assert(allocated(rhs%s_), "subdomain_t%assign_and_sync: allocated(rhs%s_)") sync all lhs%s_ = rhs%s_ - if (me>1) halo_x(east,:)[me-1] = rhs%s_(1,:) - if (me1) halo_x(east,:,:)[me-1] = rhs%s_(1,:,:) + if (me0,"laplacian: rightmost subdomain too small") - allocate(increment(my_nx,ny)) + allocate(increment(mynx_,myny_,mynz_)) call internal_points(increment) call edge_points(increment) @@ -151,56 +214,66 @@ contains subroutine internal_points(ds) - real, intent(inout) :: ds(:,:) - integer i, j - - do concurrent(i=my_internal_left+1:my_internal_right-1, j=2:ny-1) - ds(i,j) = alpha_dt*( & - (self%s_(i-1,j) - 2*self%s_(i,j) + self%s_(i+1,j))/dx_**2 + & - (self%s_(i,j-1) - 2*self%s_(i,j) + self%s_(i,j+1))/dy_**2 & + double precision, intent(inout) :: ds(:,:,:) + integer i, j, k + + do concurrent(i=my_internal_left+1:my_internal_right-1, j=2:myny_-1, k=2:mynz_-1) + ds(i,j,k) = alpha_dt*( & + (self%s_(i-1,j,k) - 2*self%s_(i,j,k) + self%s_(i+1,j,k))/dx_**2 + & + (self%s_(i,j-1,k) - 2*self%s_(i,j,k) + self%s_(i,j+1,k))/dy_**2 + & + (self%s_(i,j,k-1) - 2*self%s_(i,j,k) + self%s_(i,j,k+1))/dz_**2 & ) end do + end subroutine subroutine edge_points(ds) - real, intent(inout) :: ds(:,:) - real, allocatable :: halo_left(:), halo_right(:) - integer i, j + double precision, intent(inout) :: ds(:,:,:) + double precision, allocatable :: halo_left(:,:), halo_right(:,:) + integer i, j, k - halo_left = merge(halo_x(west,:), self%s_(1,:), me/=1) - halo_right = merge(halo_x(east,:), self%s_(my_nx,:), me/=num_subdomains) + halo_left = merge(halo_x(west,:,:), self%s_(1,:,:), me/=1) + halo_right = merge(halo_x(east,:,:), self%s_(mynx_,:,:), me/=num_subdomains) i = my_internal_left - do concurrent(j=2:ny-1) - ds(i,j) = alpha_dt*( & - (halo_left(j) - 2*self%s_(i,j) + self%s_(i+1,j))/dx_**2 + & - (self%s_(i,j-1) - 2*self%s_(i,j) + self%s_(i,j+1))/dy_**2 & + do concurrent(j=2:myny_-1,k=2:mynz_-1) + ds(i,j,k) = alpha_dt*( & + (halo_left(j,k) - 2*self%s_(i,j,k) + self%s_(i+1,j,k))/dx_**2 + & + (self%s_(i,j-1,k) - 2*self%s_(i,j,k) + self%s_(i,j+1,k))/dy_**2 + & + (self%s_(i,j,k-1) - 2*self%s_(i,j,k) + self%s_(i,j,k+1))/dz_**2 & ) end do i = my_internal_right - do concurrent(j=2:ny-1) - ds(i,j) = alpha_dt*( & - (self%s_(i-1,j) - 2*self%s_(i,j) + halo_right(j))/dx_**2 + & - (self%s_(i,j-1) - 2*self%s_(i,j) + self%s_(i,j+1))/dy_**2 & + do concurrent(j=2:myny_-1,k=2:mynz_-1) + ds(i,j,k) = alpha_dt*( & + (self%s_(i-1,j,k) - 2*self%s_(i,j,k) + halo_right(j,k))/dx_**2 + & + (self%s_(i,j-1,k) - 2*self%s_(i,j,k) + self%s_(i,j+1,k))/dy_**2 + & + (self%s_(i,j,k-1) - 2*self%s_(i,j,k) + self%s_(i,j,k+1))/dz_**2 & ) end do + + end subroutine subroutine apply_boundary_condition(ds) - real, intent(inout) :: ds(:,:) + double precision, intent(inout) :: ds(:,:,:) integer i, j - ds(:, 1) = 0. - ds(:,ny) = 0. - if (me==1) ds(1,:) = 0. - if (me==num_subdomains) ds(my_nx,:) = 0. + ds(:,1,:) = 0. + ds(:,myny_,:) = 0. + ds(:,:,1) = 0. + ds(:,:,mynz_) = 0. + if (me==1) ds(1,:,:) = 0. + if (me==num_subdomains) ds(mynx_,:,:) = 0. + end subroutine subroutine exchange_halo(s) - real, intent(in) :: s(:,:) - if (me>1) halo_x(east,:)[me-1] = s(1,:) - if (me1) halo_x(east,:,:)[me-1] = s(1,:,:) + if (me input%num_cells(), & - npositions => input%num_positions(), & - ndim => input%num_dimensions(), & - nintervals => input%num_intervals(), & - dt => input%time_step(), & - empirical_distribution => input%sample_distribution() & + ncells => input%num_cells(), & ! Number of T cells + npositions => input%num_positions(), & ! Number of time steps + ndim => input%num_dimensions(), & ! Normally 3 for 3D calculations + nintervals => input%num_intervals(), & ! Number of intervals in distributions + num_grid => input%ngrid(), & ! Number of grid cells in one-direction in 3D diffusion grid + dt => input%time_step(), & ! Time step (seconds) + gb => input%grid_begin(), & ! Lower spatial limit of 3D diffusion cubic grid (microns) + ge => input%grid_end(), & ! Upper spatial limit of 3D diffusion cubic grid (microns) + cytokinev => input%cytokine(), & ! Amount of protein deposited by T cell in grid + gfacv => input%gfac(), & ! Gradient speed factor + alpha => input%alpha(), & ! Diffusion coefficient + empirical_distribution => input%sample_distribution(), & + empirical_angle_distribution => input%sample_angle_distribution() & ) block double precision, parameter :: scale = 100.D0 double precision, allocatable :: random_positions(:,:), random_4vectors(:,:,:) + double precision, allocatable :: gdx(:) + double precision gxs type(distribution_t) distribution - integer, parameter :: nveldim = 4 + type(distribution_t) distribution_angle + type(gridr_t) gridr + integer, allocatable :: ng(:) + integer, parameter :: nveldim = 5 + integer i,j,k,ii,jj,kk integer step + double precision tconc + double precision dxdydz,dtmax,dta type(data_partition_t) data_partition + + double precision boundary_value, internal_value + type(subdomain_t) concentration_subgrid + + ! Subgrid is a subset of the complete grid owned by each processor. + ! The complete grid is divided in the x-direction + boundary_value = 0.d0 + internal_value = 0.d0 + call concentration_subgrid%define(side=ge-gb,boundary_val=boundary_value, & + internal_val=internal_value,n=num_grid) + + allocate(ng(ndim)) ! Number of grid cells in each direction of 3D diffusion grid + allocate(gdx(2*ndim+1)) ! Stores grid parameters + ng(:) = num_grid ! Set the number of cells in each direction of 3D diffusion grid to be the same + gridr = gridr_t() ! Complete grid + gdx = gridr%gridparameters(gb,ge,ng,ndim) + tconc = cytokinev*gfacv*gdx(7) + + dxdydz = 1.d0/gdx(1)**2 + 1.d0/gdx(2)**2 + 1.d0/gdx(3)**2 + dtmax = 0.5d0/(alpha*dxdydz) + dta = min(dt,dtmax) call data_partition%define_partitions(cardinality=ncells) @@ -40,22 +77,27 @@ associate(nsteps => npositions -1) allocate(random_4vectors(my_num_cells,nsteps,nveldim)) call random_number(random_4vectors) - distribution = distribution_t(empirical_distribution) + distribution = distribution_t(empirical_distribution,empirical_angle_distribution) - associate(random_speeds => random_4vectors(:,:,1), random_directions => random_4vectors(:,:,2:4)) - associate(v => distribution%velocities(random_speeds, random_directions)) - allocate(history(nsteps)) - history(1) = t_cell_collection_t(scale*random_positions, time=0.D0) - do step = 2, nsteps - associate(x => history(step-1)%positions(), t => history(step-1)%time()) - history(step) = t_cell_collection_t(x + v(:,step-1,:)*dt, t + dt) - end associate - end do + associate(random_speeds => random_4vectors(:,:,1), random_directions => random_4vectors(:,:,3:5)) + associate(random_angles => random_4vectors(:,:,2)) + associate(v => distribution%velocities(random_speeds,random_angles,random_directions)) + allocate(history(nsteps)) + history(1) = t_cell_collection_t(scale*random_positions, time=0.D0) + do step = 2, nsteps + associate(x => history(step-1)%positions(), t => history(step-1)%time()) + associate(gx => gridr%gradient(my_num_cells,ng,gdx,gb,tconc,x,concentration_subgrid)) + history(step) = t_cell_collection_t(x + (v(:,step-1,:) + gx(:,:))*dta, t + dta) + concentration_subgrid = concentration_subgrid + dta * alpha * .laplacian. concentration_subgrid + end associate + end associate + end do + end associate end associate end associate - end associate - end associate - end associate + end associate + end associate + end associate end block end associate diff --git a/test/matcha_test_m.f90 b/test/matcha_test_m.f90 index 2548a6c..1024b87 100644 --- a/test/matcha_test_m.f90 +++ b/test/matcha_test_m.f90 @@ -47,19 +47,24 @@ function compare_image_distributions() result(test_passes) type(output_t) output integer, parameter :: speed=1, freq=2 ! subscripts for speeds and frequencies - real, parameter :: tolerance = 1.D-02 + double precision, parameter :: tolerance = 1.D-02 associate(input => input_t()) output = output_t(input, matcha(input)) associate( & empirical_distribution => input%sample_distribution(), & - simulated_distribution => output%simulated_distribution() & - ) + simulated_distribution => output%simulated_distribution(), & + empirical_angle_distribution => input%sample_angle_distribution(), & + simulated_angle_distribution => output%simulated_distribution_angles() & + ) associate( & diffmax_speeds=> maxval(abs(empirical_distribution(:,speed)-simulated_distribution(:,speed))), & - diffmax_freqs => maxval(abs(empirical_distribution(:,freq)-simulated_distribution(:,freq))) & - ) - test_passes = (diffmax_freqs < tolerance) .and. (diffmax_speeds < tolerance) + diffmax_freqs => maxval(abs(empirical_distribution(:,freq)-simulated_distribution(:,freq))), & + diffmax_angles => maxval(abs(empirical_angle_distribution(:,speed)-simulated_angle_distribution(:,speed))), & + diffmax_freqs_angles => maxval(abs(empirical_angle_distribution(:,freq)-simulated_angle_distribution(:,freq))) & + ) + test_passes = (diffmax_freqs < tolerance) .and. (diffmax_speeds < tolerance) .and. & + (diffmax_angles < tolerance) .and. (diffmax_freqs_angles < tolerance) end associate end associate end associate @@ -69,27 +74,36 @@ function compare_global_distributions() result(test_passes) logical test_passes type(output_t) output double precision, allocatable :: simulated_distribution(:,:) + double precision, allocatable :: simulated_angle_distribution(:,:) integer num_cells integer, parameter :: speed=1, freq=2 ! subscripts for speeds and frequencies - real, parameter :: tolerance = 1.D-02 + double precision, parameter :: tolerance = 1.D-02 associate(input => input_t()) output = output_t(input, matcha(input)) - associate(empirical_distribution => input%sample_distribution()) - simulated_distribution = output%simulated_distribution() + associate(empirical_distribution => input%sample_distribution(), & + empirical_angle_distribution => input%sample_angle_distribution() ) + simulated_distribution = output%simulated_distribution() + simulated_angle_distribution = output%simulated_distribution_angles() num_cells = output%my_num_cells() simulated_distribution(:,freq) = num_cells*simulated_distribution(:,freq) + simulated_angle_distribution(:,freq) = num_cells*simulated_angle_distribution(:,freq) call co_sum(simulated_distribution(:,freq), result_image=1) + call co_sum(simulated_angle_distribution(:,freq), result_image=1) call co_sum(num_cells, result_image=1) if (this_image()/=1) then test_passes = .true. else simulated_distribution(:,freq) = simulated_distribution(:,freq)/dble(num_cells) + simulated_angle_distribution(:,freq) = simulated_angle_distribution(:,freq)/dble(num_cells) associate( & diffmax_speeds=> maxval(abs(empirical_distribution(:,speed)-simulated_distribution(:,speed))), & - diffmax_freqs => maxval(abs(empirical_distribution(:,freq)-simulated_distribution(:,freq))) & + diffmax_freqs => maxval(abs(empirical_distribution(:,freq)-simulated_distribution(:,freq))), & + diffmax_angles => maxval(abs(empirical_angle_distribution(:,speed)-simulated_angle_distribution(:,speed))), & + diffmax_freqs_angles => maxval(abs(empirical_angle_distribution(:,freq)-simulated_angle_distribution(:,freq))) & ) - test_passes = (diffmax_freqs < tolerance) .and. (diffmax_speeds < tolerance) + test_passes = (diffmax_freqs < tolerance) .and. (diffmax_speeds < tolerance) .and. & + (diffmax_angles < tolerance) .and. (diffmax_freqs_angles < tolerance) end associate end if end associate diff --git a/test/subdomain_test_m.f90 b/test/subdomain_test_m.f90 index b03530b..7190636 100644 --- a/test/subdomain_test_m.f90 +++ b/test/subdomain_test_m.f90 @@ -28,8 +28,8 @@ function results() result(test_results) type(test_result_t), allocatable :: test_results(:) test_results = test_result_t( & - [ character(len=len("computing a correctly shaped Laplacian for a 2D flat-topped, step-like plateau")) :: & - "computing a correctly shaped Laplacian for a 2D flat-topped, step-like plateau", & + [ character(len=len("computing a correctly shaped Laplacian for a 3D flat-topped, step-like plateau")) :: & + "computing a correctly shaped Laplacian for a 3D flat-topped, step-like plateau", & "reaching the correct steady state solution", & "functional pattern results matching procedural results" & ], & @@ -41,7 +41,7 @@ function results() result(test_results) end function subroutine output(v) - real, intent(in) :: v(:,:) + double precision, intent(in) :: v(:,:) integer j sync all critical @@ -55,52 +55,118 @@ subroutine output(v) function correctly_shaped_laplacian() result(test_passes) logical test_passes type(subdomain_t) f, laplacian_f - real, allocatable :: lap_f_vals(:,:) + double precision, allocatable :: lap_f_vals(:,:,:) - call f%define(side=1., boundary_val=1., internal_val=2., n=101) ! internally constant subdomain with a step down at the edges + double precision boundary_value,internal_value + + boundary_value = 1.d0 + internal_value = 2.d0 + call f%define(side=1.d0, boundary_val=boundary_value, & + internal_val=internal_value, n=51) ! internally constant subdomain with a step down at the edges laplacian_f = .laplacian. f lap_f_vals = laplacian_f%values() - associate(me => this_image(), n_subdomains => num_images(), nx => size(lap_f_vals,1), ny => size(lap_f_vals,2)) + associate(me => this_image(), n_subdomains => num_images(), & + nx => size(lap_f_vals,1), ny => size(lap_f_vals,2), & + nz => size(lap_f_vals,3) ) associate(first_zero_in_x => merge(3, 1, me==1), last_zero_in_x => merge(nx-2, nx, me==n_subdomains)) block - real, parameter :: tolerance = 1.0E-06 - integer, parameter :: left_adjacent = 2, bottom_adjacent = 2 - logical internally_zero, concave_down_edges, doubly_curved_left_corners, doubly_curved_right_corners - - associate(top_adjacent => ny - 1, right_adjacent => nx - 1) - internally_zero = all(abs(lap_f_vals(first_zero_in_x:last_zero_in_x, 3:ny-2)) < tolerance) - concave_down_edges = all( & - [lap_f_vals(left_adjacent:right_adjacent,bottom_adjacent), lap_f_vals(left_adjacent:right_adjacent,top_adjacent)] < 0& - ) - if (me==1) then - concave_down_edges = concave_down_edges .and. all([lap_f_vals(left_adjacent,bottom_adjacent:top_adjacent)] < 0) - associate( & - lower_left => lap_f_vals(left_adjacent,bottom_adjacent), & - middle_left => lap_f_vals(left_adjacent,bottom_adjacent+1:top_adjacent-1), & - upper_left => lap_f_vals(left_adjacent,top_adjacent) & - ) - doubly_curved_left_corners = & - all(abs(lower_left - 2.*middle_left) < tolerance) .and. all(abs(upper_left - 2.*middle_left) < tolerance) - end associate - end if - if (me==n_subdomains) then - concave_down_edges = concave_down_edges .and. all([lap_f_vals(right_adjacent,bottom_adjacent:top_adjacent)] < 0) - associate( & - lower_right => lap_f_vals(right_adjacent, bottom_adjacent), & - middle_right => lap_f_vals(right_adjacent, bottom_adjacent+1:top_adjacent-1), & - upper_right => lap_f_vals(right_adjacent, top_adjacent) & - ) - doubly_curved_right_corners = & - all(abs(lower_right - 2.*middle_right) < tolerance) .and. all(abs(upper_right - 2.*middle_right) < tolerance) - end associate - end if - test_passes = & - internally_zero .and. & - concave_down_edges .and. & - merge(doubly_curved_left_corners, .true., me==1) .and. & - merge(doubly_curved_right_corners, .true., me==n_subdomains) - end associate + double precision face_lap_x,face_lap_y,face_lap_z + double precision edge_lap_xy,edge_lap_xz,edge_lap_yz + double precision corner_lap + double precision dx_,dy_,dz_ + double precision boundary_lap + double precision, parameter :: tolerance = 1.0E-06 + integer, parameter :: left_adjacent = 2, bottom_adjacent = 2, y1 = 2, y2 = 2, z1 = 2, z2 = 2 + logical internally_zero + logical face_lap_x_left_check,face_lap_x_right_check,face_lap_y_check,face_lap_z_check + logical edge_lap_xy_left_check,edge_lap_xz_left_check + logical edge_lap_xy_right_check,edge_lap_xz_right_check + logical edge_lap_yz_check + logical corner_lap_left_check,corner_lap_right_check + + + internally_zero = all(abs(lap_f_vals(first_zero_in_x:last_zero_in_x, 3:ny-2, 3:nz-2)) < tolerance) + + dx_ = laplacian_f%dx() + dy_ = laplacian_f%dy() + dz_ = laplacian_f%dz() + + boundary_lap = (boundary_value - 2.0*internal_value + internal_value) + face_lap_x = boundary_lap/dx_**2 + face_lap_y = boundary_lap/dy_**2 + face_lap_z = boundary_lap/dz_**2 + + edge_lap_xy = face_lap_x + face_lap_y + edge_lap_xz = face_lap_x + face_lap_z + edge_lap_yz = face_lap_y + face_lap_z + + corner_lap = face_lap_x + face_lap_y + face_lap_z + + ! Check Laplacian on Faces + if (me .eq. 1) face_lap_x_left_check = all(abs(lap_f_vals(2,3:ny-2,3:nz-2)-face_lap_x) < tolerance) + if (me .eq. n_subdomains) face_lap_x_right_check = all(abs(lap_f_vals(nx-1,3:ny-2,3:nz-2)-face_lap_x) < tolerance) + face_lap_y_check = all( & + [abs(lap_f_vals(first_zero_in_x:last_zero_in_x,2,3:nz-2)-face_lap_y) < tolerance, & + abs(lap_f_vals(first_zero_in_x:last_zero_in_x,ny-1,3:nz-2)-face_lap_y) < tolerance ]) + face_lap_z_check = all( & + [abs(lap_f_vals(first_zero_in_x:last_zero_in_x,3:ny-2,2)-face_lap_z) < tolerance, & + abs(lap_f_vals(first_zero_in_x:last_zero_in_x,3:ny-2,nz-1)-face_lap_z) < tolerance ]) + + ! Check Laplacian on Edges + if (me .eq. 1) then + edge_lap_xy_left_check = all( & + [abs(lap_f_vals(2,2,3:nz-2)-edge_lap_xy) < tolerance, & + abs(lap_f_vals(2,ny-1,3:nz-2)-edge_lap_xy) < tolerance]) + edge_lap_xz_left_check = all( & + [abs(lap_f_vals(2,3:ny-2,2)-edge_lap_xz) < tolerance, & + abs(lap_f_vals(2,3:ny-2,nz-1)-edge_lap_xz) < tolerance]) + end if + if (me .eq. n_subdomains) then + edge_lap_xy_right_check = all( & + [abs(lap_f_vals(nx-1,2,3:nz-2)-edge_lap_xy) < tolerance, & + abs(lap_f_vals(nx-1,ny-1,3:nz-2)-edge_lap_xy) < tolerance]) + edge_lap_xz_right_check = all( & + [abs(lap_f_vals(nx-1,3:ny-2,2)-edge_lap_xz) < tolerance, & + abs(lap_f_vals(nx-1,3:ny-2,nz-1)-edge_lap_xz) < tolerance]) + end if + edge_lap_yz_check = all( & + [abs(lap_f_vals(first_zero_in_x:last_zero_in_x,2,2)-edge_lap_yz) < tolerance, & + abs(lap_f_vals(first_zero_in_x:last_zero_in_x,ny-1,2)-edge_lap_yz) < tolerance, & + abs(lap_f_vals(first_zero_in_x:last_zero_in_x,2,nz-1)-edge_lap_yz) < tolerance, & + abs(lap_f_vals(first_zero_in_x:last_zero_in_x,ny-1,nz-1)-edge_lap_yz) < tolerance]) + + + ! Check Laplacian on Corners + if (me .eq. 1) then + corner_lap_left_check = all( & + [abs(lap_f_vals(2,2,2)-corner_lap) < tolerance, & + abs(lap_f_vals(2,ny-1,2)-corner_lap) < tolerance, & + abs(lap_f_vals(2,2,nz-1)-corner_lap) < tolerance, & + abs(lap_f_vals(2,ny-1,nz-1)-corner_lap) < tolerance ]) + end if + if (me .eq. n_subdomains) then + corner_lap_right_check = all( & + [abs(lap_f_vals(nx-1,2,2)-corner_lap) < tolerance, & + abs(lap_f_vals(nx-1,ny-1,2)-corner_lap) < tolerance, & + abs(lap_f_vals(nx-1,2,nz-1)-corner_lap) < tolerance, & + abs(lap_f_vals(nx-1,ny-1,nz-1)-corner_lap) < tolerance ]) + end if + + test_passes = & + internally_zero .and. & + face_lap_y_check .and. & + face_lap_z_check .and. & + edge_lap_yz_check .and. & + merge(face_lap_x_left_check, .true., me==1) .and. & + merge(face_lap_x_right_check, .true., me==n_subdomains) .and. & + merge(edge_lap_xy_left_check, .true., me==1) .and. & + merge(edge_lap_xy_right_check, .true., me==n_subdomains) .and. & + merge(edge_lap_xz_left_check, .true., me==1) .and. & + merge(edge_lap_xz_right_check, .true., me==n_subdomains) .and. & + merge(corner_lap_left_check, .true., me==1) .and. & + merge(corner_lap_right_check, .true., me==n_subdomains) + end block end associate end associate @@ -110,16 +176,18 @@ function correctly_shaped_laplacian() result(test_passes) function correct_steady_state() result(test_passes) logical test_passes type(subdomain_t) T - real, parameter :: T_boundary = 1., T_initial = 2. + double precision, parameter :: T_boundary = 1., T_initial = 2. - call T%define(side=1., boundary_val=T_boundary, internal_val=T_initial, n=51) + call T%define(side=1.d0, boundary_val=T_boundary, internal_val=T_initial, n=51) ! spatially constant internal temperatuers with a step change at the boundaries block integer step integer, parameter :: steps = 5000 - real, parameter :: alpha = 1. - associate(dt => T%dx()*T%dy()/(4*alpha)) + double precision, parameter :: alpha = 1. + double precision dxdydz + dxdydz = 1.d0/T%dx()**2 + 1.d0/T%dy()**2 + 1.d0/T%dz()**2 + associate(dt => 0.5d0/(alpha*dxdydz)) do step = 1, steps T = T + dt * alpha * .laplacian. T end do @@ -127,11 +195,11 @@ function correct_steady_state() result(test_passes) end block block - real, parameter :: tolerance = 0.01, T_steady = T_boundary + double precision, parameter :: tolerance = 0.01, T_steady = T_boundary associate(T_values => T%values()) - associate(ny => size(T_values,2)) - associate( residual => T_values(:,2:ny-1) - T_steady) - test_passes = all(residual >= 0. .and. residual <= tolerance) + associate(ny => size(T_values,2),nz => size(T_values,3)) + associate( residual => T_values(:,2:ny-1,2:nz-1) - T_steady) + test_passes = all(residual >= 0.d0 .and. residual <= tolerance) end associate end associate end associate @@ -141,10 +209,10 @@ function correct_steady_state() result(test_passes) function functional_matches_procedural() result(test_passes) logical test_passes - real, parameter :: tolerance = 0.1 + double precision, parameter :: tolerance = 0.1d0 integer, parameter :: steps = 5000, n=51 - real, parameter :: alpha = 1. - real, parameter :: side=1., boundary_val=1., internal_val=2. + double precision, parameter :: alpha = 1.d0 + double precision, parameter :: side=1.d0, boundary_val=1.d0, internal_val=2.d0 associate( T_f => T_functional(), T_p => T_procedural()) associate(L_infinity_norm => maxval(abs(T_f - T_p))) @@ -155,13 +223,15 @@ function functional_matches_procedural() result(test_passes) contains function T_functional() - real, allocatable :: T_functional(:,:) + double precision dxdydz + double precision, allocatable :: T_functional(:,:,:) type(subdomain_t) T integer step call T%define(side, boundary_val, internal_val, n) - associate(dt => T%dx()*T%dy()/(4*alpha)) + dxdydz = 1.d0/T%dx()**2 + 1.d0/T%dy()**2 + 1.d0/T%dz()**2 + associate(dt => 0.5d0/(alpha*dxdydz)) do step = 1, steps T = T + dt * alpha * .laplacian. T end do @@ -171,13 +241,16 @@ function T_functional() end function function T_procedural() - real, allocatable :: T_procedural(:,:) + double precision dxdydz + double precision, allocatable :: T_procedural(:,:,:) type(subdomain_t) T integer step call T%define(side, boundary_val, internal_val, n) - associate(dt => T%dx()*T%dy()/(4*alpha)) + dxdydz = 1.d0/T%dx()**2 + 1.d0/T%dy()**2 + 1.d0/T%dz()**2 + + associate(dt => 0.5d0/(alpha*dxdydz)) do step = 1, steps call T%step(alpha*dt) end do