module m_vector_calculus use iso_fortran_env, only: stderr => error_unit use m_allocator, only: allocator_t, field_t use m_base_backend, only: base_backend_t use m_common, only: dp, DIR_X, DIR_Y, DIR_Z, & RDR_X2Y, RDR_X2Z, RDR_Y2X, RDR_Y2Z, RDR_Z2X, RDR_Z2Y use m_tdsops, only: tdsops_t implicit none type :: vector_calculus_t !! Defines vector calculus operators class(base_backend_t), pointer :: backend contains procedure :: curl procedure :: divergence_v2c procedure :: gradient_c2v procedure :: laplacian end type vector_calculus_t interface vector_calculus_t module procedure init end interface vector_calculus_t contains function init(backend) result(vector_calculus) implicit none class(base_backend_t), target, intent(inout) :: backend type(vector_calculus_t) :: vector_calculus vector_calculus%backend => backend end function init subroutine curl(self, o_i_hat, o_j_hat, o_k_hat, u, v, w, & x_der1st, y_der1st, z_der1st) !! Curl of a vector field (u, v, w). !! !! Evaluated at the data_loc defined by u, v, w fields. !! !! All the input and output fields are in DIR_X layout. implicit none class(vector_calculus_t) :: self !> Vector components of the output vector field Omega class(field_t), intent(inout) :: o_i_hat, o_j_hat, o_k_hat class(field_t), intent(in) :: u, v, w class(tdsops_t), intent(in) :: x_der1st, y_der1st, z_der1st class(field_t), pointer :: u_y, u_z, v_z, w_y, dwdy_y, dvdz_z, dvdz_x, & dudz_z, dudz_x, dudy_y, dudy_x if (o_i_hat%dir /= DIR_X .or. o_j_hat%dir /= DIR_X & .or. o_k_hat%dir /= DIR_X .or. u%dir /= DIR_X .or. v%dir /= DIR_X & .or. w%dir /= DIR_X) then error stop 'Error in curl input/output field %dirs: & &outputs and inputs must be in DIR_X layout.' end if ! omega_i_hat = dw/dy - dv/dz ! omega_j_hat = du/dz - dw/dx ! omega_k_hat = dv/dx - du/dy ! omega_i_hat ! dw/dy w_y => self%backend%allocator%get_block(DIR_Y) dwdy_y => self%backend%allocator%get_block(DIR_Y) call self%backend%reorder(w_y, w, RDR_X2Y) call self%backend%tds_solve(dwdy_y, w_y, y_der1st) call self%backend%reorder(o_i_hat, dwdy_y, RDR_Y2X) call self%backend%allocator%release_block(w_y) call self%backend%allocator%release_block(dwdy_y) ! dv/dz v_z => self%backend%allocator%get_block(DIR_Z) dvdz_z => self%backend%allocator%get_block(DIR_Z) call self%backend%reorder(v_z, v, RDR_X2Z) call self%backend%tds_solve(dvdz_z, v_z, z_der1st) dvdz_x => self%backend%allocator%get_block(DIR_X) call self%backend%reorder(dvdz_x, dvdz_z, RDR_Z2X) call self%backend%allocator%release_block(v_z) call self%backend%allocator%release_block(dvdz_z) ! omega_i_hat = dw/dy - dv/dz call self%backend%vecadd(-1._dp, dvdz_x, 1._dp, o_i_hat) call self%backend%allocator%release_block(dvdz_x) ! omega_j_hat ! du/dz u_z => self%backend%allocator%get_block(DIR_Z) dudz_z => self%backend%allocator%get_block(DIR_Z) call self%backend%reorder(u_z, u, RDR_X2Z) call self%backend%tds_solve(dudz_z, u_z, z_der1st) dudz_x => self%backend%allocator%get_block(DIR_X) call self%backend%reorder(dudz_x, dudz_z, RDR_Z2X) call self%backend%allocator%release_block(u_z) call self%backend%allocator%release_block(dudz_z) ! dw/dx call self%backend%tds_solve(o_j_hat, w, x_der1st) ! omega_j_hat = du/dz - dw/dx call self%backend%vecadd(1._dp, dudz_x, -1._dp, o_j_hat) call self%backend%allocator%release_block(dudz_x) ! omega_k_hat ! dv/dx call self%backend%tds_solve(o_k_hat, v, x_der1st) ! du/dy u_y => self%backend%allocator%get_block(DIR_Y) dudy_y => self%backend%allocator%get_block(DIR_Y) call self%backend%reorder(u_y, u, RDR_X2Y) call self%backend%tds_solve(dudy_y, u_y, y_der1st) dudy_x => self%backend%allocator%get_block(DIR_X) call self%backend%reorder(dudy_x, dudy_y, RDR_Y2X) call self%backend%allocator%release_block(u_y) call self%backend%allocator%release_block(dudy_y) ! omega_k_hat = dv/dx - du/dy call self%backend%vecadd(-1._dp, dudy_x, 1._dp, o_k_hat) call self%backend%allocator%release_block(dudy_x) end subroutine curl subroutine divergence_v2c(self, div_u, u, v, w, & x_stagder_v2c, x_interpl_v2c, & y_stagder_v2c, y_interpl_v2c, & z_stagder_v2c, z_interpl_v2c) !! Divergence of a vector field (u, v, w). !! !! Evaluated at the cell centers (data_loc=CELL) !! Input fields are at vertices (data_loc=VERT) !! !! Input fields are in DIR_X data layout. !! Output field is in DIR_Z data layout. implicit none class(vector_calculus_t) :: self class(field_t), intent(inout) :: div_u class(field_t), intent(in) :: u, v, w class(tdsops_t), intent(in) :: x_stagder_v2c, x_interpl_v2c, & y_stagder_v2c, y_interpl_v2c, & z_stagder_v2c, z_interpl_v2c class(field_t), pointer :: du_x, dv_x, dw_x, & u_y, v_y, w_y, du_y, dv_y, dw_y, & u_z, w_z, dw_z if (div_u%dir /= DIR_Z .or. u%dir /= DIR_X .or. v%dir /= DIR_X & .or. w%dir /= DIR_X) then error stop 'Error in divergence_v2c input/output field dirs: & &output must be in DIR_Z, inputs must be in DIR_X layout.' end if du_x => self%backend%allocator%get_block(DIR_X) dv_x => self%backend%allocator%get_block(DIR_X) dw_x => self%backend%allocator%get_block(DIR_X) ! Staggared der for u field in x ! Interpolation for v field in x ! Interpolation for w field in x call self%backend%tds_solve(du_x, u, x_stagder_v2c) call self%backend%tds_solve(dv_x, v, x_interpl_v2c) call self%backend%tds_solve(dw_x, w, x_interpl_v2c) ! request fields from the allocator u_y => self%backend%allocator%get_block(DIR_Y) v_y => self%backend%allocator%get_block(DIR_Y) w_y => self%backend%allocator%get_block(DIR_Y) ! reorder data from x orientation to y orientation call self%backend%reorder(u_y, du_x, RDR_X2Y) call self%backend%reorder(v_y, dv_x, RDR_X2Y) call self%backend%reorder(w_y, dw_x, RDR_X2Y) call self%backend%allocator%release_block(du_x) call self%backend%allocator%release_block(dv_x) call self%backend%allocator%release_block(dw_x) du_y => self%backend%allocator%get_block(DIR_Y) dv_y => self%backend%allocator%get_block(DIR_Y) dw_y => self%backend%allocator%get_block(DIR_Y) ! similar to the x direction, obtain derivatives in y. call self%backend%tds_solve(du_y, u_y, y_interpl_v2c) call self%backend%tds_solve(dv_y, v_y, y_stagder_v2c) call self%backend%tds_solve(dw_y, w_y, y_interpl_v2c) ! we don't need the velocities in y orientation any more, so release ! them to open up space. ! It is important that this doesn't actually deallocate any memory, ! it just makes the corresponding memory space available for use. call self%backend%allocator%release_block(u_y) call self%backend%allocator%release_block(v_y) call self%backend%allocator%release_block(w_y) ! just like in y direction, get some fields for the z derivatives. u_z => self%backend%allocator%get_block(DIR_Z) w_z => self%backend%allocator%get_block(DIR_Z) ! du_y = dv_y + du_y call self%backend%vecadd(1._dp, dv_y, 1._dp, du_y) ! reorder from y to z call self%backend%reorder(u_z, du_y, RDR_Y2Z) call self%backend%reorder(w_z, dw_y, RDR_Y2Z) ! release all the unnecessary blocks. call self%backend%allocator%release_block(du_y) call self%backend%allocator%release_block(dv_y) call self%backend%allocator%release_block(dw_y) dw_z => self%backend%allocator%get_block(DIR_Z) ! get the derivatives in z call self%backend%tds_solve(div_u, u_z, z_interpl_v2c) call self%backend%tds_solve(dw_z, w_z, z_stagder_v2c) ! div_u = div_u + dw_z call self%backend%vecadd(1._dp, dw_z, 1._dp, div_u) ! div_u array is in z orientation ! there is no need to keep velocities in z orientation around, so release call self%backend%allocator%release_block(u_z) call self%backend%allocator%release_block(w_z) call self%backend%allocator%release_block(dw_z) end subroutine divergence_v2c subroutine gradient_c2v(self, dpdx, dpdy, dpdz, p, & x_stagder_c2v, x_interpl_c2v, & y_stagder_c2v, y_interpl_c2v, & z_stagder_c2v, z_interpl_c2v) !! Gradient of a scalar field 'p'. !! !! Evaluated at the vertices (data_loc=VERT) !! Input field is at cell centers (data_loc=CELL) !! !! Input field is in DIR_Z data layout. !! Output fields (dpdx, dpdy, dpdz) are in DIR_X data layout. implicit none class(vector_calculus_t) :: self class(field_t), intent(inout) :: dpdx, dpdy, dpdz class(field_t), intent(in) :: p class(tdsops_t), intent(in) :: x_stagder_c2v, x_interpl_c2v, & y_stagder_c2v, y_interpl_c2v, & z_stagder_c2v, z_interpl_c2v class(field_t), pointer :: p_sxy_z, dpdz_sxy_z, & p_sxy_y, dpdz_sxy_y, & p_sx_y, dpdy_sx_y, dpdz_sx_y, & p_sx_x, dpdy_sx_x, dpdz_sx_x if (dpdx%dir /= DIR_X .or. dpdy%dir /= DIR_X .or. dpdz%dir /= DIR_X & .or. p%dir /= DIR_Z) then error stop 'Error in gradient_c2v input/output field dirs: & &outputs must be in DIR_X, input must be in DIR_Z layout.' end if p_sxy_z => self%backend%allocator%get_block(DIR_Z) dpdz_sxy_z => self%backend%allocator%get_block(DIR_Z) ! Staggared der for p field in z ! Interpolation for p field in z call self%backend%tds_solve(p_sxy_z, p, z_interpl_c2v) call self%backend%tds_solve(dpdz_sxy_z, p, z_stagder_c2v) ! request fields from the allocator p_sxy_y => self%backend%allocator%get_block(DIR_Y) dpdz_sxy_y => self%backend%allocator%get_block(DIR_Y) ! reorder data from z orientation to y orientation call self%backend%reorder(p_sxy_y, p_sxy_z, RDR_Z2Y) call self%backend%reorder(dpdz_sxy_y, dpdz_sxy_z, RDR_Z2Y) call self%backend%allocator%release_block(p_sxy_z) call self%backend%allocator%release_block(dpdz_sxy_z) p_sx_y => self%backend%allocator%get_block(DIR_Y) dpdy_sx_y => self%backend%allocator%get_block(DIR_Y) dpdz_sx_y => self%backend%allocator%get_block(DIR_Y) ! similar to the z direction, obtain derivatives in y. call self%backend%tds_solve(p_sx_y, p_sxy_y, y_interpl_c2v) call self%backend%tds_solve(dpdy_sx_y, p_sxy_y, y_stagder_c2v) call self%backend%tds_solve(dpdz_sx_y, dpdz_sxy_y, y_interpl_c2v) ! release memory call self%backend%allocator%release_block(p_sxy_y) call self%backend%allocator%release_block(dpdz_sxy_y) ! just like in y direction, get some fields for the x derivatives. p_sx_x => self%backend%allocator%get_block(DIR_X) dpdy_sx_x => self%backend%allocator%get_block(DIR_X) dpdz_sx_x => self%backend%allocator%get_block(DIR_X) ! reorder from y to x call self%backend%reorder(p_sx_x, p_sx_y, RDR_Y2X) call self%backend%reorder(dpdy_sx_x, dpdy_sx_y, RDR_Y2X) call self%backend%reorder(dpdz_sx_x, dpdz_sx_y, RDR_Y2X) ! release all the y directional fields. call self%backend%allocator%release_block(p_sx_y) call self%backend%allocator%release_block(dpdy_sx_y) call self%backend%allocator%release_block(dpdz_sx_y) ! get the derivatives in x call self%backend%tds_solve(dpdx, p_sx_x, x_stagder_c2v) call self%backend%tds_solve(dpdy, dpdy_sx_x, x_interpl_c2v) call self%backend%tds_solve(dpdz, dpdz_sx_x, x_interpl_c2v) ! release temporary x fields call self%backend%allocator%release_block(p_sx_x) call self%backend%allocator%release_block(dpdy_sx_x) call self%backend%allocator%release_block(dpdz_sx_x) end subroutine gradient_c2v subroutine laplacian(self, lapl_u, u, x_der2nd, y_der2nd, z_der2nd) !! Laplacian of a scalar field 'u'. !! !! Evaluated at the data_loc defined by the input u field !! !! Input and output fields are in DIR_X layout. implicit none class(vector_calculus_t) :: self class(field_t), intent(inout) :: lapl_u class(field_t), intent(in) :: u class(tdsops_t), intent(in) :: x_der2nd, y_der2nd, z_der2nd class(field_t), pointer :: u_y, d2u_y, u_z, d2u_z if (u%dir /= DIR_X .or. lapl_u%dir /= DIR_X) then error stop 'Error in laplacian input/output field %dirs: & &outputs and inputs must be in DIR_X layout.' end if ! d2u/dx2 call self%backend%tds_solve(lapl_u, u, x_der2nd) ! y directional temporary fields u_y => self%backend%allocator%get_block(DIR_Y) d2u_y => self%backend%allocator%get_block(DIR_Y) call self%backend%reorder(u_y, u, RDR_X2Y) ! d2u/dy2 call self%backend%tds_solve(d2u_y, u_y, y_der2nd) ! add the y derivative component into the result field call self%backend%sum_yintox(lapl_u, d2u_y) ! release y directional fields call self%backend%allocator%release_block(u_y) call self%backend%allocator%release_block(d2u_y) ! z directional temporary fields u_z => self%backend%allocator%get_block(DIR_Z) d2u_z => self%backend%allocator%get_block(DIR_Z) call self%backend%reorder(u_z, u, RDR_X2Z) ! d2u/dz2 call self%backend%tds_solve(d2u_z, u_z, z_der2nd) ! add the z derivative component into the result field call self%backend%sum_zintox(lapl_u, d2u_z) ! release z directional fields call self%backend%allocator%release_block(u_z) call self%backend%allocator%release_block(d2u_z) end subroutine laplacian end module m_vector_calculus