Octopus
derivatives.F90
Go to the documentation of this file.
1!! Copyright (C) 2002-2006 M. Marques, A. Castro, A. Rubio, G. Bertsch
2!!
3!! This program is free software; you can redistribute it and/or modify
4!! it under the terms of the GNU General Public License as published by
5!! the Free Software Foundation; either version 2, or (at your option)
6!! any later version.
7!!
8!! This program is distributed in the hope that it will be useful,
9!! but WITHOUT ANY WARRANTY; without even the implied warranty of
10!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11!! GNU General Public License for more details.
12!!
13!! You should have received a copy of the GNU General Public License
14!! along with this program; if not, write to the Free Software
15!! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16!! 02110-1301, USA.
17!!
18
19#include "global.h"
20
27
29 use accel_oct_m
31 use batch_oct_m
34 use debug_oct_m
40 use global_oct_m
41 use iso_c_binding
42 use, intrinsic :: iso_fortran_env
45 use math_oct_m
46 use mesh_oct_m
51 use parser_oct_m
53 use space_oct_m
61 use types_oct_m
62 use utils_oct_m
64
65 implicit none
66
67 private
68 public :: &
105
106
107 integer, parameter :: &
108 DER_BC_ZERO_F = 0, & !< function is zero at the boundaries
109 der_bc_zero_df = 1, &
110 der_bc_period = 2
111
112 integer, parameter, public :: &
113 DER_STAR = 1, &
114 der_variational = 2, &
115 der_cube = 3, &
116 der_starplus = 4, &
118
119 integer, parameter :: &
120 BLOCKING = 1, &
121 non_blocking = 2
122
125 type derivatives_t
126 ! Components are public by default
127 type(boundaries_t) :: boundaries
128 type(mesh_t), pointer :: mesh => null()
129 integer :: dim = 0
130 integer :: periodic_dim = 0
131 integer :: order = 0
132 integer :: stencil_type = 0
133
134
135 logical, private :: stencil_primitive_coordinates
136
137 class(coordinate_system_t), private, pointer :: coord_system
138 logical :: remove_zero_weight_points
139
140 real(real64), allocatable :: masses(:)
141
144 real(real64), private :: lapl_cutoff = m_zero
145
146 type(nl_operator_t), allocatable, private :: op(:)
148 type(nl_operator_t), pointer :: lapl => null()
149 type(nl_operator_t), pointer :: grad(:) => null()
150
151 integer, allocatable :: n_ghost(:)
152!#if defined(HAVE_MPI)
153 integer, private :: comm_method = 0
154!#endif
155 type(derivatives_t), pointer :: finer => null()
156 type(derivatives_t), pointer :: coarser => null()
157 type(transfer_table_t), pointer :: to_finer => null()
158 type(transfer_table_t), pointer :: to_coarser => null()
159 end type derivatives_t
160
163 private
164!#ifdef HAVE_MPI
165 type(par_vec_handle_batch_t) :: pv_h
166!#endif
167 type(derivatives_t), pointer :: der
168 type(nl_operator_t), pointer :: op
169 type(batch_t), pointer :: ff
170 type(batch_t), pointer :: opff
171 logical :: ghost_update
172 logical :: factor_present
173 real(real64) :: factor
175
176 type(accel_kernel_t) :: kernel_uvw_xyz, kernel_dcurl, kernel_zcurl
177
178contains
179
180 ! ---------------------------------------------------------
181 subroutine derivatives_init(der, namespace, space, coord_system, order)
182 type(derivatives_t), target, intent(inout) :: der
183 type(namespace_t), intent(in) :: namespace
184 class(space_t), intent(in) :: space
185 class(coordinate_system_t), target, intent(in) :: coord_system
186 integer, optional, intent(in) :: order
187
188 integer :: idir
189 integer :: default_stencil
190 logical :: stencil_primitive_coordinates
191 character(len=128) :: kernel_name
192
193 push_sub(derivatives_init)
194
195 ! Non-orthogonal curvilinear coordinates are currently not implemented
196 assert(.not. coord_system%local_basis .or. coord_system%orthogonal)
197
198 ! copy this value to my structure
199 der%dim = space%dim
200 der%periodic_dim = space%periodic_dim
201 der%coord_system => coord_system
203 !%Variable DerivativesStencil
204 !%Type integer
205 !%Default stencil_star
206 !%Section Mesh::Derivatives
207 !%Description
208 !% Decides what kind of stencil is used, <i>i.e.</i> which points, around
209 !% each point in the mesh, are the neighboring points used in the
210 !% expression of the differential operator.
211 !%
212 !% If curvilinear coordinates are to be used, then only the <tt>stencil_starplus</tt>
213 !% or the <tt>stencil_cube</tt> may be used. We only recommend the <tt>stencil_starplus</tt>,
214 !% since the cube typically needs far too much memory.
215 !%Option stencil_star 1
216 !% A star around each point (<i>i.e.</i>, only points on the axis).
217 !%Option stencil_variational 2
218 !% Same as the star, but with coefficients built in a different way.
219 !%Option stencil_cube 3
220 !% A cube of points around each point.
221 !%Option stencil_starplus 4
222 !% The star, plus a number of off-axis points.
223 !%Option stencil_stargeneral 5
224 !% The general star. Default for non-orthogonal grids.
225 !%End
226 default_stencil = der_star
227 if (coord_system%local_basis) default_stencil = der_starplus
228 if (.not. coord_system%orthogonal) default_stencil = der_stargeneral
229
230 call parse_variable(namespace, 'DerivativesStencil', default_stencil, der%stencil_type)
231
232 if (.not. varinfo_valid_option('DerivativesStencil', der%stencil_type)) then
233 call messages_input_error(namespace, 'DerivativesStencil')
234 end if
235 call messages_print_var_option("DerivativesStencil", der%stencil_type, namespace=namespace)
236
237 if (coord_system%local_basis .and. der%stencil_type < der_cube) call messages_input_error(namespace, 'DerivativesStencil')
238 ! The star stencils have no off-axis points, so they cannot represent the cross derivatives
239 ! of the Laplacian in non-orthogonal coordinates.
240 if (.not. coord_system%orthogonal .and. &
241 (der%stencil_type == der_star .or. der%stencil_type == der_variational)) then
242 call messages_input_error(namespace, 'DerivativesStencil', &
243 'stencil_star and stencil_variational cannot be used for non-orthogonal cells. Use stencil_stargeneral.')
244 end if
245 if (der%stencil_type == der_variational) then
246 !%Variable DerivativesLaplacianFilter
247 !%Type float
248 !%Default 1.0
249 !%Section Mesh::Derivatives
250 !%Description
251 !% Undocumented
252 !%End
253 call parse_variable(namespace, 'DerivativesLaplacianFilter', m_one, der%lapl_cutoff)
254 end if
255
256 !%Variable DerivativesOrder
257 !%Type integer
258 !%Default 4
259 !%Section Mesh::Derivatives
260 !%Description
261 !% This variable gives the discretization order for the approximation of
262 !% the differential operators. This means, basically, that
263 !% <tt>DerivativesOrder</tt> points are used in each positive/negative
264 !% spatial direction, <i>e.g.</i> <tt>DerivativesOrder = 1</tt> would give
265 !% the well-known three-point formula in 1D.
266 !% The number of points actually used for the Laplacian
267 !% depends on the stencil used. Let <math>O</math> = <tt>DerivativesOrder</tt>, and <math>d</math> = <tt>Dimensions</tt>.
268 !% <ul>
269 !% <li> <tt>stencil_star</tt>: <math>2 O d + 1</math>
270 !% <li> <tt>stencil_cube</tt>: <math>(2 O + 1)^d</math>
271 !% <li> <tt>stencil_starplus</tt>: <math>2 O d + 1 + n</math> with <i>n</i> being 8
272 !% in 2D and 24 in 3D.
273 !% </ul>
274 !%End
275 call parse_variable(namespace, 'DerivativesOrder', 4, der%order)
276 ! overwrite order if given as argument
277 if (present(order)) then
278 der%order = order
279 end if
280
281 !%Variable DerivativesRemoveZeroWeightPoints
282 !%Type logical
283 !%Default yes
284 !%Section Mesh::Derivatives
285 !%Description
286 !% By default, Octopus removes the zero-weight points in the stencils.
287 !% This debug variable gives the possibility to deactive this feature.
288 !%End
289 call parse_variable(namespace, 'DerivativesRemoveZeroWeightPoints', .true., der%remove_zero_weight_points)
290
291
292#ifdef HAVE_MPI
293 !%Variable ParallelizationOfDerivatives
294 !%Type integer
295 !%Default non_blocking
296 !%Section Execution::Parallelization
297 !%Description
298 !% This option selects how the communication of mesh boundaries is performed.
299 !%Option blocking 1
300 !% Blocking communication.
301 !%Option non_blocking 2
302 !% Communication is based on non-blocking point-to-point communication.
303 !%End
304
305 call parse_variable(namespace, 'ParallelizationOfDerivatives', non_blocking, der%comm_method)
306
307 if (.not. varinfo_valid_option('ParallelizationOfDerivatives', der%comm_method)) then
308 call messages_input_error(namespace, 'ParallelizationOfDerivatives')
309 end if
310
311 call messages_obsolete_variable(namespace, 'OverlapDerivatives', 'ParallelizationOfDerivatives')
312#endif
313
314 !%Variable StencilPrimitiveCoordinates
315 !%Type logical
316 !%Section Mesh::Derivatives
317 !%Description
318 !% This variable controls the method for generating the stencil weights for the Laplacian.
319 !% For the gradient, the weights are always computed for primitive coordinates.
320 !% If set to yes, primitive coordinates are used for the polynomials and the right-hand side
321 !% of the Laplacian is computed using the metric tensor and the trace of the Hessian.
322 !% If set to no, Cartesian coordinates are used for the polynomials and the right-hand side
323 !% of the Laplacian reduces to the Cartesian case.
324 !% For some non-orthogonal grids, using primitive coordinates is necessary because the polynomials
325 !% become linearly dependent, thus the corresponding matrix cannot be inverted. For some curvilinear
326 !% coordinate systems, using Cartesian coordinates is more accurate.
327 !%
328 !% By default, use primitve coordinates except for curvilinear meshes.
329 !%End
330 stencil_primitive_coordinates = .not. coord_system%local_basis
331 call parse_variable(namespace, 'StencilPrimitiveCoordinates', stencil_primitive_coordinates, der%stencil_primitive_coordinates)
332
333 ! if needed, der%masses should be initialized in modelmb_particles_init
334 safe_allocate(der%masses(1:space%dim))
335 der%masses = m_one
336
337 ! construct lapl and grad structures
338 safe_allocate(der%op(1:der%dim + 1))
339 der%grad => der%op
340 der%lapl => der%op(der%dim + 1)
341
342 call derivatives_get_stencil_lapl(der, der%lapl, space, coord_system)
344
345 ! find out how many ghost points we need in each dimension
346 safe_allocate(der%n_ghost(1:der%dim))
347 der%n_ghost(:) = 0
348 do idir = 1, der%dim
349 der%n_ghost(idir) = maxval(abs(der%lapl%stencil%points(idir, :)))
350 end do
351
352 nullify(der%coarser)
353 nullify(der%finer)
354 nullify(der%to_coarser)
355 nullify(der%to_finer)
356
357 if (accel_is_enabled()) then
358 ! Check if we can build the uvw_to_xyz kernel
359 select type (coord_system)
360 type is (cartesian_t)
361 ! In this case one does not need to call the kernel, so all is fine
362 class default
363 if (der%dim > 3) then
364 message(1) = "Calculation of derivatives on the GPU with dimension > 3 are only implemented for Cartesian coordinates."
365 call messages_fatal(1, namespace=namespace)
366 else
367 ! Each dimension adds a (source, ld, dest, ld) argument group
368 write(kernel_name, '(a,i1,a)') 'uvw_to_xyz<double, Dim::D', der%dim, &
369 repeat(', double*, int, double*, int', der%dim)//'>'
370 call accel_kernel_start_call(kernel_uvw_xyz, 'uvw_to_xyz.cu', trim(kernel_name))
371 end if
372 end select
373 call accel_kernel_build(kernel_dcurl, 'curl.cu', 'curl<double>')
374 call accel_kernel_build(kernel_zcurl, 'curl.cu', 'curl<complex<double>>')
375 end if
376
377 pop_sub(derivatives_init)
378 end subroutine derivatives_init
379
380
381 ! ---------------------------------------------------------
382 subroutine derivatives_end(der)
383 type(derivatives_t), intent(inout) :: der
384
385 integer :: idim
386
387 push_sub(derivatives_end)
388
389 assert(allocated(der%op))
390
391 do idim = 1, der%dim+1
392 call nl_operator_end(der%op(idim))
393 end do
394
395 safe_deallocate_a(der%masses)
396
397 safe_deallocate_a(der%n_ghost)
398
399 safe_deallocate_a(der%op)
400 nullify(der%lapl, der%grad)
401
402 nullify(der%coarser)
403 nullify(der%finer)
404 nullify(der%to_coarser)
405 nullify(der%to_finer)
406
407 call boundaries_end(der%boundaries)
408
409 pop_sub(derivatives_end)
410 end subroutine derivatives_end
411
412
413 ! ---------------------------------------------------------
414 subroutine derivatives_get_stencil_lapl(der, lapl, space, coord_system, name, order)
415 type(derivatives_t), intent(in) :: der
416 type(nl_operator_t), intent(inout) :: lapl
417 class(space_t), intent(in) :: space
418 class(coordinate_system_t), intent(in) :: coord_system
419 character(len=80), optional, intent(in) :: name
420 integer, optional, intent(in) :: order
421
422 character(len=80) :: name_
423 integer :: order_
424
426
427 name_ = optional_default(name, "Laplacian")
428 order_ = optional_default(order, der%order)
429
430 ! initialize nl operator
431 call nl_operator_init(lapl, name_, symm=op_symmetric)
432
433 ! create stencil
434 select case (der%stencil_type)
435 case (der_star, der_variational)
436 call stencil_star_get_lapl(lapl%stencil, der%dim, order_)
437 case (der_cube)
438 call stencil_cube_get_lapl(lapl%stencil, der%dim, order_)
439 case (der_starplus)
440 call stencil_starplus_get_lapl(lapl%stencil, der%dim, order_)
441 case (der_stargeneral)
442 call stencil_stargeneral_get_arms(lapl%stencil, space%dim, coord_system)
443 call stencil_stargeneral_get_lapl(lapl%stencil, der%dim, order_)
444 end select
445
447 end subroutine derivatives_get_stencil_lapl
448
449
450 ! ---------------------------------------------------------
452 subroutine derivatives_lapl_diag(der, lapl)
453 type(derivatives_t), intent(in) :: der
454 real(real64), contiguous, intent(out) :: lapl(:)
455
456 push_sub(derivatives_lapl_diag)
457
458 assert(ubound(lapl, dim=1) >= der%mesh%np)
459
460 ! the Laplacian is a real operator
461 call dnl_operator_operate_diag(der%lapl, lapl)
462
463 pop_sub(derivatives_lapl_diag)
464
465 end subroutine derivatives_lapl_diag
466
467
468 ! ---------------------------------------------------------
469 subroutine derivatives_get_stencil_grad(der)
470 type(derivatives_t), intent(inout) :: der
471
472
473 integer :: ii
474 character :: dir_label
475
477
478 assert(associated(der%grad))
479
480 ! initialize nl operator
481 do ii = 1, der%dim
482 dir_label = ' '
483 if (ii < 5) dir_label = index2axis(ii)
484
485 call nl_operator_init(der%grad(ii), "Gradient "//dir_label, symm=op_antisymmetric)
487 ! create stencil
488 select case (der%stencil_type)
489 case (der_star, der_variational)
490 call stencil_star_get_grad(der%grad(ii)%stencil, der%dim, ii, der%order)
491 case (der_cube)
492 call stencil_cube_get_grad(der%grad(ii)%stencil, der%dim, der%order)
493 case (der_starplus)
494 call stencil_starplus_get_grad(der%grad(ii)%stencil, der%dim, ii, der%order)
495 case (der_stargeneral)
496 ! use the simple star stencil
497 call stencil_star_get_grad(der%grad(ii)%stencil, der%dim, ii, der%order)
498 end select
499 end do
500
502
503 end subroutine derivatives_get_stencil_grad
504
505 ! ---------------------------------------------------------
551 subroutine derivatives_build(der, namespace, space, mesh, qvector, regenerate, verbose)
552 type(derivatives_t), intent(inout) :: der
553 type(namespace_t), intent(in) :: namespace
554 class(space_t), intent(in) :: space
555 class(mesh_t), target, intent(in) :: mesh
556 real(real64), contiguous, optional, intent(in) :: qvector(:)
558 logical, optional, intent(in) :: regenerate
559 logical, optional, intent(in) :: verbose
560
561 integer :: i
562 logical :: const_w_
563 integer :: np_zero_bc
564
565 push_sub(derivatives_build)
566
567 if (.not. optional_default(regenerate, .false.)) then
568 call boundaries_init(der%boundaries, namespace, space, mesh, qvector)
569 end if
570
571 assert(allocated(der%op))
572 assert(der%stencil_type >= der_star .and. der%stencil_type <= der_stargeneral)
573 assert(.not.(der%stencil_type == der_variational .and. mesh%use_curvilinear))
574
575 der%mesh => mesh ! make a pointer to the underlying mesh
576
577 const_w_ = .true.
578
579 ! need non-constant weights for curvilinear and scattering meshes
580 if (mesh%use_curvilinear) const_w_ = .false.
581
582 np_zero_bc = 0
583
584 if (optional_default(regenerate, .false.)) then
585 do i = 1, der%dim+1
586 call nl_operator_end(der%op(i))
587 safe_deallocate_a(der%op(i)%w)
588 end do
589 call derivatives_get_stencil_lapl(der, der%lapl, space, der%coord_system)
591 end if
592
593 ! build operators
594 do i = 1, der%dim+1
595 call nl_operator_build(space, mesh, der%op(i), der%mesh%np, const_w = const_w_, &
596 regenerate=regenerate)
597 np_zero_bc = max(np_zero_bc, nl_operator_np_zero_bc(der%op(i)))
598 end do
599
600 assert(np_zero_bc > mesh%np .and. np_zero_bc <= mesh%np_part)
601
602 select case (der%stencil_type)
603
604 case (der_star, der_starplus, der_stargeneral) ! Laplacian and gradient have different stencils
605 do i = 1, der%dim + 1
606
607 call derivatives_make_discretization(namespace, der, 1, i, der%op(i:i), space, verbose=verbose)
608 end do
609
610 case (der_cube)
611 ! gradients have the same stencils, so use one call to derivatives_make_discretization
612 ! to solve the linear equation once for several right-hand sides
613 call derivatives_make_discretization(namespace, der, der%dim, der%dim, der%op(:), space, verbose=verbose)
614 ! the polynomials are evaluated differently for the Laplacian
615 call derivatives_make_discretization(namespace, der, 1, der%dim+1, der%op(der%dim+1:der%dim+1), space, &
616 verbose=verbose)
617
618 case (der_variational)
619 ! the gradients use the star stencil
620 do i = 1, der%dim
621 call derivatives_make_discretization(namespace, der, 1, i, der%op(i:i), space, verbose=verbose)
622 end do
623 ! we have the explicit coefficients for the Laplacian
624 call stencil_variational_coeff_lapl(der%dim, der%order, mesh%spacing, der%lapl, alpha = der%lapl_cutoff)
625 ! finalize the Laplacian as done in derivatives_make_discretization for the other stencils
626 if (der%lapl%const_w) call nl_operator_build_symmetric_weights(der%lapl)
628 call nl_operator_update_gpu_buffers(der%lapl)
629 end select
630
631 pop_sub(derivatives_build)
632
633 end subroutine derivatives_build
634
635 ! ---------------------------------------------------------
636 subroutine stencil_pol_grad(stencil_type, dim, direction, order, polynomials)
637 integer, intent(in) :: stencil_type
638 integer, intent(in) :: dim
639 integer, intent(in) :: direction
640 integer, intent(in) :: order
641 integer, contiguous, intent(inout) :: polynomials(:, :)
642
643 select case (stencil_type)
644 case (der_star, der_stargeneral, der_variational)
645 call stencil_star_polynomials_grad(direction, order, polynomials)
646 case (der_starplus)
647 call stencil_starplus_pol_grad(dim, direction, order, polynomials)
648 case (der_cube)
649 ! same stencil for gradient and Laplacian in this case
650 call stencil_cube_polynomials_lapl(dim, order, polynomials)
651 end select
652 end subroutine stencil_pol_grad
653
654 ! ---------------------------------------------------------
655 subroutine stencil_pol_lapl(stencil_type, stencil, dim, order, polynomials)
656 integer, intent(in) :: stencil_type
657 type(stencil_t), intent(in) :: stencil
658 integer, intent(in) :: dim
659 integer, intent(in) :: order
660 integer, contiguous, intent(inout) :: polynomials(:, :)
661
662 select case (stencil_type)
663 case (der_star, der_variational)
664 ! the variational stencil uses the points of the star; a generic Laplacian (e.g. with a
665 ! different order) is built with the star finite-difference weights
666 call stencil_star_polynomials_lapl(dim, order, polynomials)
667 case (der_starplus)
668 call stencil_starplus_pol_lapl(dim, order, polynomials)
669 case (der_stargeneral)
670 call stencil_stargeneral_pol_lapl(stencil, dim, order, polynomials)
671 case (der_cube)
672 call stencil_cube_polynomials_lapl(dim, order, polynomials)
673 end select
674 end subroutine stencil_pol_lapl
675
676 ! ---------------------------------------------------------
677 subroutine get_rhs_lapl(coord_system, polynomials, chi, rhs, stencil_primitive_coordinates)
678 class(coordinate_system_t), intent(in) :: coord_system
679 integer, contiguous, intent(in) :: polynomials(:,:)
680 real(real64), contiguous, intent(in) :: chi(:)
681 real(real64), contiguous, intent(out) :: rhs(:)
682 logical, intent(in) :: stencil_primitive_coordinates
683
684 integer :: i, j, k, dim
685 real(real64) :: metric_inverse(1:size(polynomials, dim=1), 1:size(polynomials, dim=1))
686 real(real64) :: hessian_trace(1:size(polynomials, dim=1))
687 integer :: powers(0:2)
688
689 push_sub(get_rhs_lapl)
690
691 dim = size(polynomials, dim=1)
692
693 if (stencil_primitive_coordinates) then
694 ! inverse metric and trace of Hessian in primitive coordinates
695 metric_inverse = coord_system%metric_inverse(chi)
696 hessian_trace = coord_system%trace_hessian(chi)
697 else
698 ! inverse metric and trace of Hessian in Cartesian coordinates
699 metric_inverse = m_zero
700 do i = 1, dim
701 metric_inverse(i, i) = m_one
702 end do
703 hessian_trace = m_zero
704 end if
705
706 ! find right-hand side for operator
707 rhs(:) = m_zero
708 do j = 1, size(polynomials, dim=2)
709 ! count the powers of the polynomials
710 powers = 0
711 do i = 1, dim
712 if (polynomials(i, j) <= 2) then
713 powers(polynomials(i, j)) = powers(polynomials(i, j)) + 1
714 end if
715 end do
716
717 ! find all polynomials for which exactly one term is quadratic
718 ! for these, the Laplacian on the polynomial is 2*metric_inverse(i, i)
719 if (powers(2) == 1 .and. powers(0) == dim - 1) then
720 do i = 1, dim
721 if (polynomials(i, j) == 2) then
722 rhs(j) = m_two*metric_inverse(i, i)
723 end if
724 end do
725 end if
726 ! find all polynomials for which exactly two terms are linear
727 ! for these, the Laplacian on the polynomial is metric_inverse(i, k) + metric_inverse(k, i)
728 if (powers(1) == 2 .and. powers(0) == dim - 2) then
729 do i = 1, dim
730 if (polynomials(i, j) == 1) then
731 do k = i+1, dim
732 if (polynomials(k, j) == 1) then
733 rhs(j) = metric_inverse(i, k) + metric_inverse(k, i)
734 end if
735 end do
736 end if
737 end do
738 end if
739 ! find all polynomials for which exactly one term is linear
740 ! for these, the Laplacian on the polynomial is hessian_trace(i)
741 ! this term is only non-zero for curvilinear coordinates with a varying metric tensor
742 if (powers(1) == 1 .and. powers(0) == dim - 1) then
743 do i = 1, dim
744 if (polynomials(i, j) == 1) then
745 rhs(j) = hessian_trace(i)
746 end if
747 end do
748 end if
749 end do
750
751 pop_sub(get_rhs_lapl)
752 end subroutine get_rhs_lapl
753
754 ! ---------------------------------------------------------
755 subroutine get_rhs_grad(polynomials, dir, rhs)
756 integer, contiguous, intent(in) :: polynomials(:,:)
757 integer, intent(in) :: dir
758 real(real64), contiguous, intent(out) :: rhs(:)
759
760 integer :: j, k, dim
761 logical :: this_one
762
763 push_sub(get_rhs_grad)
764
765 dim = size(polynomials, dim=1)
766
767 ! find right-hand side for operator
768 rhs(:) = m_zero
769 do j = 1, size(polynomials, dim=2)
770 this_one = .true.
771 do k = 1, dim
772 if (k == dir .and. polynomials(k, j) /= 1) this_one = .false.
773 if (k /= dir .and. polynomials(k, j) /= 0) this_one = .false.
774 end do
775 if (this_one) rhs(j) = m_one
776 end do
777
778 pop_sub(get_rhs_grad)
779 end subroutine get_rhs_grad
780
781
782 ! ---------------------------------------------------------
783 subroutine derivatives_make_discretization(namespace, der, nderiv, ideriv, op, &
784 space, name, verbose, order)
785 type(namespace_t), intent(in) :: namespace
786 type(derivatives_t), intent(in) :: der
787 integer, intent(in) :: nderiv
788 integer, intent(in) :: ideriv
789 type(nl_operator_t), intent(inout) :: op(:)
790 type(space_t), intent(in) :: space
791 character(len=80), optional, intent(in) :: name
792 logical, optional, intent(in) :: verbose
793 integer, optional, intent(in) :: order
794
795 integer :: p, p_max, i, j, k, pow_max, order_
796 real(real64) :: x(der%dim)
797 real(real64), allocatable :: mat(:,:), sol(:,:), powers(:,:)
798 integer, allocatable :: pol(:,:)
799 real(real64), allocatable :: rhs(:,:)
800 character(len=80) :: name_
801
803
804 order_ = optional_default(order, der%order)
805
806 safe_allocate(rhs(1:op(1)%stencil%size, nderiv))
807 safe_allocate(pol(1:der%dim, 1:op(1)%stencil%size))
808 ! get polynomials
809 if (nderiv == 1) then
810 if (ideriv <= der%dim) then ! gradient
811 call stencil_pol_grad(der%stencil_type, der%dim, ideriv, order_, pol)
812 name_ = index2axis(ideriv) // "-gradient"
813 else ! Laplacian
814 call stencil_pol_lapl(der%stencil_type, op(1)%stencil, der%dim, order_, pol)
815 name_ = "Laplacian"
816 end if
817 else if (nderiv == der%dim) then
818 ! in this case, we compute the weights for the gradients at once
819 ! because they have the same stencils
820 ! this is only used for the cube stencil
821 name_ = "gradients"
822 call stencil_pol_grad(der%stencil_type, der%dim, 1, order_, pol)
823 else
824 message(1) = "Error: derivatives_make_discretization can only be called with nderiv = 1 or der%dim."
825 call messages_fatal(1)
826 end if
828 safe_allocate(mat(1:op(1)%stencil%size, 1:op(1)%stencil%size))
829 safe_allocate(sol(1:op(1)%stencil%size, 1:nderiv))
830
831 if (optional_default(verbose, .true.)) then
832 name_ = optional_default(name, name_)
833 message(1) = 'Info: Generating weights for finite-difference discretization of ' // trim(name_)
834 call messages_info(1, namespace=namespace)
835 end if
836
837 ! use to generate power lookup table
838 pow_max = maxval(pol)
839 safe_allocate(powers(1:der%dim, 0:pow_max))
840 powers(:,:) = m_zero
841 powers(:,0) = m_one
842
843 p_max = op(1)%np
844 if (op(1)%const_w) p_max = 1
845
846 do p = 1, p_max
847 ! get polynomials and right-hand side
848 ! we need the current position for the right-hand side for curvilinear coordinates
849 if (nderiv == 1) then
850 if (ideriv <= der%dim) then ! gradient
851 call get_rhs_grad(pol, ideriv, rhs(:,1))
852 name_ = index2axis(ideriv) // "-gradient"
853 else ! Laplacian
854 call get_rhs_lapl(der%mesh%coord_system, pol, der%mesh%chi(:, p), rhs(:,1), der%stencil_primitive_coordinates)
855 end if
856 else if (nderiv == der%dim) then
857 ! in this case, we compute the weights for the gradients at once
858 ! because they have the same stencils
859 do i = 1, der%dim
860 call get_rhs_grad(pol, i, rhs(:,i))
861 end do
862 end if
863
864 ! first polynomial is just a constant
865 mat(1,:) = m_one
866 ! i indexes the point in the stencil
867 do i = 1, op(1)%stencil%size
868 if (ideriv <= der%dim) then
869 ! for gradients, we always need to evaluate the polynomials in primitive coordinates
870 x = real(op(1)%stencil%points(:, i), real64)*der%mesh%spacing
871 else
872 ! for the Laplacian, we can evaluate the polynomials in primitive or Cartesian coordinates
873 if (der%stencil_primitive_coordinates) then
874 x = real(op(1)%stencil%points(:, i), real64)*der%mesh%spacing
875 else
876 x = der%mesh%x(:, p + op(1)%ri(i, op(1)%rimap(p))) - der%mesh%x(:, p)
877 end if
878 end if
879
880 ! NB: these masses are applied on the cartesian directions. Should add a check for non-orthogonal axes
881 x = x*sqrt(der%masses)
882
883 ! calculate powers
884 powers(:, 1) = x
885 do k = 2, pow_max
886 powers(:, k) = x*powers(:, k-1)
887 end do
888
889 ! generate the matrix
890 ! j indexes the polynomial being used
891 do j = 2, op(1)%stencil%size
892 mat(j, i) = powers(1, pol(1, j))
893 do k = 2, der%dim
894 mat(j, i) = mat(j, i)*powers(k, pol(k, j))
895 end do
896 end do
897 end do ! loop over i = point in stencil
898
899 ! linear problem to solve for derivative weights:
900 ! mat * sol = rhs
901 call lalg_linsyssolve(op(1)%stencil%size, nderiv, mat, rhs, sol)
902
903 ! for the cube stencil, all derivatives are calculated at once, so assign
904 ! the correct solution to each operator
905 do i = 1, nderiv
906 op(i)%w(:, p) = sol(:, i)
907 ! The central weight of an antisymmetric operator is zero by symmetry
908 if (op(i)%symmetry == op_antisymmetric .and. op(i)%stencil%center > 0) then
909 op(i)%w(op(i)%stencil%center, p) = m_zero
910 endif
911 end do
912
913 end do ! loop over points p
914
915 do i = 1, nderiv
916 if (der%remove_zero_weight_points) then
917 call nl_operator_remove_zero_weight_points(op(i), space, der%mesh)
918 end if
920 if (op(i)%const_w) then
922 end if
923 end do
924
925 ! In case of constant weights, we store the weights of the Laplacian on the GPU, as this
926 ! saves many unecessary transfers
927 do i = 1, nderiv
930 end do
931
932
933 safe_deallocate_a(mat)
934 safe_deallocate_a(sol)
935 safe_deallocate_a(powers)
936 safe_deallocate_a(pol)
937 safe_deallocate_a(rhs)
938
941
942#ifdef HAVE_MPI
943 ! ---------------------------------------------------------
944 logical function derivatives_overlap(this) result(overlap)
945 type(derivatives_t), intent(in) :: this
946
947 push_sub(derivatives_overlap)
948
949 overlap = this%comm_method /= blocking
950
951 pop_sub(derivatives_overlap)
952 end function derivatives_overlap
953#endif
954
955 ! ---------------------------------------------------------
956 subroutine derivatives_get_lapl(this, namespace, op, space, name, order)
957 type(derivatives_t), intent(in) :: this
958 type(namespace_t), intent(in) :: namespace
959 type(nl_operator_t), contiguous, intent(inout) :: op(:)
960 class(space_t), intent(in) :: space
961 character(len=80), intent(in) :: name
962 integer, intent(in) :: order
963
964 push_sub(derivatives_get_lapl)
965
966 call derivatives_get_stencil_lapl(this, op(1), space, this%mesh%coord_system, name, order)
967 call nl_operator_build(space, this%mesh, op(1), this%mesh%np, const_w = .not. this%mesh%use_curvilinear)
968 call derivatives_make_discretization(namespace, this, 1, this%dim+1, op(1:1), space, name, order=order)
969
970 pop_sub(derivatives_get_lapl)
971 end subroutine derivatives_get_lapl
972
973 ! ---------------------------------------------------------
986 function derivatives_get_inner_boundary_mask(this) result(mask)
987 type(derivatives_t), intent(in) :: this
988
989 logical :: mask(1:this%mesh%np)
990 integer :: ip, is, index
991
992 mask = .false.
993 ! Loop through all points in the grid
994 do ip = 1, this%mesh%np
995 ! For each of them, loop through all points in the stencil
996 do is = 1, this%lapl%stencil%size
997 ! Get the index of the point obtained as: grid_point + displament_due_to_stencil
998 index = nl_operator_get_index(this%lapl, is, ip)
999 ! Check whether the displaced point if outsude the grid. Is so, it belongs to the mask
1000 if (index > this%mesh%np + this%mesh%pv%np_ghost) then
1001 mask(ip) = .true.
1002 exit
1003 end if
1004 end do
1005 end do
1006
1008
1009 ! ---------------------------------------------------------
1014 real(real64) function derivatives_lapl_get_max_eigenvalue(this)
1015 type(derivatives_t), intent(in) :: this
1016
1017 integer :: i
1018
1019 push_sub(derivatives_lapl_get_max_eigenvalue)
1020
1021 derivatives_lapl_get_max_eigenvalue = m_zero
1022 if ((this%stencil_type == der_star .or. this%stencil_type == der_stargeneral) &
1023 .and. .not. this%mesh%use_curvilinear) then
1024 ! use Fourier transform of stencil evaluated at the maximum phase
1025 do i = 1, this%lapl%stencil%size
1026 derivatives_lapl_get_max_eigenvalue = derivatives_lapl_get_max_eigenvalue + &
1027 (-1)**maxval(abs(this%lapl%stencil%points(:, i)))*this%lapl%w(i, 1)
1028 end do
1029 derivatives_lapl_get_max_eigenvalue = abs(derivatives_lapl_get_max_eigenvalue)
1030 else
1031 ! use upper bound from continuum for other stencils
1032 do i = 1, this%dim
1033 derivatives_lapl_get_max_eigenvalue = derivatives_lapl_get_max_eigenvalue + &
1034 m_pi**2/this%mesh%spacing(i)**2
1035 end do
1036 end if
1037
1038 pop_sub(derivatives_lapl_get_max_eigenvalue)
1040
1041 subroutine derivates_set_coordinates_system(der, coord_system)
1042 type(derivatives_t), target, intent(inout) :: der
1043 class(coordinate_system_t), target, intent(in) :: coord_system
1044
1045 der%coord_system => coord_system
1047
1048#include "undef.F90"
1049#include "real.F90"
1050#include "derivatives_inc.F90"
1051
1052#include "undef.F90"
1053#include "complex.F90"
1054#include "derivatives_inc.F90"
1055
1056end module derivatives_oct_m
1057
1058!! Local Variables:
1059!! mode: f90
1060!! coding: utf-8
1061!! End:
double sqrt(double __x) __attribute__((__nothrow__
subroutine, public accel_kernel_start_call(this, file_name, kernel_name, flags)
Definition: accel.F90:1865
subroutine, public accel_kernel_build(this, file_name, kernel_name, flags)
Compile the program that contains a given kernel.
Definition: accel.F90:1823
pure logical function, public accel_is_enabled()
Definition: accel.F90:414
This module implements batches of mesh functions.
Definition: batch.F90:135
This module implements common operations on batches of mesh functions.
Definition: batch_ops.F90:118
Module implementing boundary conditions in Octopus.
Definition: boundaries.F90:124
subroutine, public boundaries_end(this)
Definition: boundaries.F90:430
subroutine, public boundaries_init(this, namespace, space, mesh, qvector)
initialize the boundary contitions
Definition: boundaries.F90:229
This module implements the curvilinear coordinates given in E.L. Briggs, D.J. Sullivan,...
This module implements the curvilinear coordinates given in F. Gygi and G. Galli, PRB 52 R2229 (1996)...
Definition: curv_gygi.F90:120
This module implements the curvilinear coordinates given in N. A. Modine, G. Zumbach,...
This module calculates the derivatives (gradients, Laplacians, etc.) of a function.
subroutine, public zderivatives_curl(der, ff, op_ff, ghost_update, set_bc)
apply the curl operator to a vector of mesh functions
subroutine, public dderivatives_perform(op, der, ff, op_ff, ghost_update, set_bc, factor)
apply a nl_operator to a mesh function
subroutine, public derivatives_lapl_diag(der, lapl)
Returns the diagonal elements of the Laplacian, needed for preconditioning.
integer, parameter, public der_cube
subroutine, public dderivatives_batch_curl_from_gradient(der, ffb, gradb)
calculate the curl from a batch and its gradient
subroutine derivatives_make_discretization(namespace, der, nderiv, ideriv, op, space, name, verbose, order)
subroutine, public derivatives_build(der, namespace, space, mesh, qvector, regenerate, verbose)
build the derivatives object:
subroutine, public dderivatives_batch_grad(der, ffb, opffb, ghost_update, set_bc, to_cartesian, factor)
apply the gradient to a batch of mesh functions
subroutine stencil_pol_grad(stencil_type, dim, direction, order, polynomials)
subroutine stencil_pol_lapl(stencil_type, stencil, dim, order, polynomials)
subroutine, public zderivatives_batch_curl_from_gradient(der, ffb, gradb)
calculate the curl from a batch and its gradient
subroutine, public zderivatives_batch_div(der, ffb, opffb, ghost_update, set_bc, to_cartesian)
calculate the divergence of a vector of batches
subroutine, public dderivatives_partial(der, ff, op_ff, dir, ghost_update, set_bc)
apply the partial derivative along dir to a mesh function
subroutine get_rhs_lapl(coord_system, polynomials, chi, rhs, stencil_primitive_coordinates)
subroutine, public dderivatives_batch_perform(op, der, ff, opff, ghost_update, set_bc, factor, async)
apply an operator to a bach of mesh functions
subroutine, public zderivatives_div(der, ff, op_ff, ghost_update, set_bc, to_cartesian)
apply the divergence operator to a vector of mesh functions
integer, parameter der_bc_period
boundary is periodic
subroutine, public dderivatives_batch_finish(handle, async)
apply a non-local operator to a batch (2nd part)
subroutine, public dderivatives_curl(der, ff, op_ff, ghost_update, set_bc)
apply the curl operator to a vector of mesh functions
subroutine derivatives_get_stencil_grad(der)
subroutine, public dderivatives_grad(der, ff, op_ff, ghost_update, set_bc, to_cartesian)
apply the gradient to a mesh function
subroutine derivatives_get_stencil_lapl(der, lapl, space, coord_system, name, order)
subroutine, public derivatives_init(der, namespace, space, coord_system, order)
subroutine, public derivatives_end(der)
integer, parameter der_bc_zero_df
first derivative of the function is zero
subroutine, public zderivatives_batch_grad(der, ffb, opffb, ghost_update, set_bc, to_cartesian, factor)
apply the gradient to a batch of mesh functions
subroutine, public dderivatives_batch_div(der, ffb, opffb, ghost_update, set_bc, to_cartesian)
calculate the divergence of a vector of batches
subroutine, public derivatives_get_lapl(this, namespace, op, space, name, order)
subroutine, public zderivatives_batch_perform(op, der, ff, opff, ghost_update, set_bc, factor, async)
apply an operator to a bach of mesh functions
subroutine, public dderivatives_batch_start(op, der, ff, opff, handle, ghost_update, set_bc, factor)
apply a non-local operator to a batch (1st part)
subroutine get_rhs_grad(polynomials, dir, rhs)
subroutine, public dderivatives_lapl(der, ff, op_ff, ghost_update, set_bc, factor)
apply the Laplacian to a mesh function
real(real64) function, public derivatives_lapl_get_max_eigenvalue(this)
Get maximum eigenvalue of discrete Laplacian. For the star and star_general stencils,...
subroutine, public dderivatives_batch_curl(der, ffb, ghost_update, set_bc)
apply the curl to a batch of mesh functions
subroutine, public zderivatives_grad(der, ff, op_ff, ghost_update, set_bc, to_cartesian)
apply the gradient to a mesh function
subroutine, public zderivatives_partial(der, ff, op_ff, dir, ghost_update, set_bc)
apply the partial derivative along dir to a mesh function
subroutine, public zderivatives_batch_start(op, der, ff, opff, handle, ghost_update, set_bc, factor)
apply a non-local operator to a batch (1st part)
logical function, dimension(1:this%mesh%np), public derivatives_get_inner_boundary_mask(this)
This function tells whether a point in the grid is contained in a layer of the width of the stencil b...
integer, parameter, public der_starplus
subroutine, public zderivatives_perform(op, der, ff, op_ff, ghost_update, set_bc, factor)
apply a nl_operator to a mesh function
subroutine, public zderivatives_batch_curl(der, ffb, ghost_update, set_bc)
apply the curl to a batch of mesh functions
integer, parameter, public der_variational
subroutine, public zderivatives_lapl(der, ff, op_ff, ghost_update, set_bc, factor)
apply the Laplacian to a mesh function
subroutine, public derivates_set_coordinates_system(der, coord_system)
subroutine, public zderivatives_batch_finish(handle, async)
apply a non-local operator to a batch (2nd part)
subroutine, public dderivatives_div(der, ff, op_ff, ghost_update, set_bc, to_cartesian)
apply the divergence operator to a vector of mesh functions
integer, parameter non_blocking
integer, parameter, public der_stargeneral
real(real64), parameter, public m_two
Definition: global.F90:202
real(real64), parameter, public m_zero
Definition: global.F90:200
real(real64), parameter, public m_pi
some mathematical constants
Definition: global.F90:198
real(real64), parameter, public m_one
Definition: global.F90:201
This module is intended to contain "only mathematical" functions and procedures.
Definition: math.F90:117
This module defines the meshes, which are used in Octopus.
Definition: mesh.F90:120
subroutine, public messages_obsolete_variable(namespace, name, rep)
Definition: messages.F90:1000
character(len=256), dimension(max_lines), public message
to be output by fatal, warning
Definition: messages.F90:162
subroutine, public messages_fatal(no_lines, only_root_writes, namespace)
Definition: messages.F90:410
subroutine, public messages_input_error(namespace, var, details, row, column)
Definition: messages.F90:691
subroutine, public messages_info(no_lines, iunit, debug_only, stress, all_nodes, namespace)
Definition: messages.F90:594
This module defines non-local operators.
subroutine, public dnl_operator_operate_diag(op, fo)
subroutine, public nl_operator_init(op, label, symm)
initialize an instance of a non-local operator by setting the label
subroutine, public nl_operator_build_symmetric_weights(op, max_size)
Builds (or rebuild) the necessary arrays for symmetric and antisymmetric stencils.
subroutine, public nl_operator_update_gpu_buffers(op)
subroutine, public nl_operator_output_weights(this)
subroutine, public nl_operator_end(op)
subroutine, public nl_operator_remove_zero_weight_points(op, space, mesh)
Removes the zero-weight points for constant weight stencils.
integer, parameter, public op_symmetric
subroutine, public nl_operator_build(space, mesh, op, np, const_w, regenerate)
Creates the nonlocal operators for the stencils used for finite differences.
integer pure function, public nl_operator_np_zero_bc(op)
integer, parameter, public op_antisymmetric
integer pure function, public nl_operator_get_index(op, is, ip)
subroutine, public nl_operator_allocate_gpu_buffers(op)
Some general things and nomenclature:
Definition: par_vec.F90:173
This module defines routines, generating operators for a cubic stencil.
subroutine, public stencil_cube_get_lapl(this, dim, order)
subroutine, public stencil_cube_polynomials_lapl(dim, order, pol)
subroutine, public stencil_cube_get_grad(this, dim, order)
This module defines stencils used in Octopus.
Definition: stencil.F90:137
This module defines routines, generating operators for a star stencil.
subroutine, public stencil_star_get_grad(this, dim, dir, order)
subroutine, public stencil_star_get_lapl(this, dim, order)
subroutine, public stencil_star_polynomials_grad(dir, order, pol)
subroutine, public stencil_star_polynomials_lapl(dim, order, pol)
This module defines routines, generating operators for a generalized star stencil.
subroutine, public stencil_stargeneral_get_arms(this, dim, coord_system)
Finds the direction of the arms of the star-general stencil as described in Natan et al....
subroutine, public stencil_stargeneral_pol_lapl(this, dim, order, pol)
subroutine, public stencil_stargeneral_get_lapl(this, dim, order)
This module defines routines, generating operators for a stencil consisting of a star and a cross....
subroutine, public stencil_starplus_pol_grad(dim, dir, order, pol)
subroutine, public stencil_starplus_pol_lapl(dim, order, pol)
subroutine, public stencil_starplus_get_lapl(this, dim, order)
subroutine, public stencil_starplus_get_grad(this, dim, dir, order)
Implements the variational discretization of the Laplacian as proposed by P. Maragakis,...
subroutine, public stencil_variational_coeff_lapl(dim, order, h, lapl, alpha)
This module is intended to contain simple general-purpose utility functions and procedures.
Definition: utils.F90:120
character pure function, public index2axis(idir)
Definition: utils.F90:211
abstract class to describe coordinate systems
handle to transfer data from the start() to finish() calls.
class representing derivatives
Describes mesh distribution to nodes.
Definition: mesh.F90:188
data type for non local operators
The class representing the stencil, which is used for non-local mesh operations.
Definition: stencil.F90:165
int true(void)