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 loct_oct_m
46 use math_oct_m
47 use mesh_oct_m
53 use parser_oct_m
55 use space_oct_m
63 use types_oct_m
64 use utils_oct_m
66
67! debug purposes
68! use io_binary_oct_m
69! use io_function_oct_m
70! use io_oct_m
71! use unit_oct_m
72! use unit_system_oct_m
73
74 implicit none
75
76 private
77 public :: &
116
117
118 integer, parameter :: &
119 DER_BC_ZERO_F = 0, & !< function is zero at the boundaries
120 der_bc_zero_df = 1, &
121 der_bc_period = 2
122
123 integer, parameter, public :: &
124 DER_STAR = 1, &
125 der_variational = 2, &
126 der_cube = 3, &
127 der_starplus = 4, &
129
130 integer, parameter :: &
131 BLOCKING = 1, &
132 non_blocking = 2
133
136 type derivatives_t
137 ! Components are public by default
138 type(boundaries_t) :: boundaries
139 type(mesh_t), pointer :: mesh => null()
140 integer :: dim = 0
141 integer, private :: periodic_dim = 0
142 integer :: order = 0
143 integer :: stencil_type = 0
144
145
146 logical, private :: stencil_primitive_coordinates
147
148 class(coordinate_system_t), private, pointer :: coord_system
149 logical :: remove_zero_weight_points
150
151 real(real64), allocatable :: masses(:)
152
155 real(real64), private :: lapl_cutoff = m_zero
156
157 type(nl_operator_t), allocatable, private :: op(:)
159 type(nl_operator_t), pointer :: lapl => null()
160 type(nl_operator_t), pointer :: grad(:) => null()
161
162 integer, allocatable :: n_ghost(:)
163!#if defined(HAVE_MPI)
164 integer, private :: comm_method = 0
165!#endif
166 type(derivatives_t), pointer :: finer => null()
167 type(derivatives_t), pointer :: coarser => null()
168 type(transfer_table_t), pointer :: to_finer => null()
169 type(transfer_table_t), pointer :: to_coarser => null()
170 end type derivatives_t
171
174 private
175!#ifdef HAVE_MPI
176 type(par_vec_handle_batch_t) :: pv_h
177!#endif
178 type(derivatives_t), pointer :: der
179 type(nl_operator_t), pointer :: op
180 type(batch_t), pointer :: ff
181 type(batch_t), pointer :: opff
182 logical :: ghost_update
183 logical :: factor_present
184 real(real64) :: factor
186
187 type(accel_kernel_t) :: kernel_uvw_xyz, kernel_dcurl, kernel_zcurl
188
189contains
190
191 ! ---------------------------------------------------------
192 subroutine derivatives_init(der, namespace, space, coord_system, order)
193 type(derivatives_t), target, intent(inout) :: der
194 type(namespace_t), intent(in) :: namespace
195 class(space_t), intent(in) :: space
196 class(coordinate_system_t), target, intent(in) :: coord_system
197 integer, optional, intent(in) :: order
198
199 integer :: idir
200 integer :: default_stencil
201 character(len=40) :: flags
202 logical :: stencil_primitive_coordinates
203
204 push_sub(derivatives_init)
205
206 ! Non-orthogonal curvilinear coordinates are currently not implemented
207 assert(.not. coord_system%local_basis .or. coord_system%orthogonal)
208
209 ! copy this value to my structure
210 der%dim = space%dim
211 der%periodic_dim = space%periodic_dim
212 der%coord_system => coord_system
214 !%Variable DerivativesStencil
215 !%Type integer
216 !%Default stencil_star
217 !%Section Mesh::Derivatives
218 !%Description
219 !% Decides what kind of stencil is used, <i>i.e.</i> which points, around
220 !% each point in the mesh, are the neighboring points used in the
221 !% expression of the differential operator.
222 !%
223 !% If curvilinear coordinates are to be used, then only the <tt>stencil_starplus</tt>
224 !% or the <tt>stencil_cube</tt> may be used. We only recommend the <tt>stencil_starplus</tt>,
225 !% since the cube typically needs far too much memory.
226 !%Option stencil_star 1
227 !% A star around each point (<i>i.e.</i>, only points on the axis).
228 !%Option stencil_variational 2
229 !% Same as the star, but with coefficients built in a different way.
230 !%Option stencil_cube 3
231 !% A cube of points around each point.
232 !%Option stencil_starplus 4
233 !% The star, plus a number of off-axis points.
234 !%Option stencil_stargeneral 5
235 !% The general star. Default for non-orthogonal grids.
236 !%End
237 default_stencil = der_star
238 if (coord_system%local_basis) default_stencil = der_starplus
239 if (.not. coord_system%orthogonal) default_stencil = der_stargeneral
240
241 call parse_variable(namespace, 'DerivativesStencil', default_stencil, der%stencil_type)
242
243 if (.not. varinfo_valid_option('DerivativesStencil', der%stencil_type)) then
244 call messages_input_error(namespace, 'DerivativesStencil')
245 end if
246 call messages_print_var_option("DerivativesStencil", der%stencil_type, namespace=namespace)
247
248 if (coord_system%local_basis .and. der%stencil_type < der_cube) call messages_input_error(namespace, 'DerivativesStencil')
249 if (der%stencil_type == der_variational) then
250 !%Variable DerivativesLaplacianFilter
251 !%Type float
252 !%Default 1.0
253 !%Section Mesh::Derivatives
254 !%Description
255 !% Undocumented
256 !%End
257 call parse_variable(namespace, 'DerivativesLaplacianFilter', m_one, der%lapl_cutoff)
258 end if
260 !%Variable DerivativesOrder
261 !%Type integer
262 !%Default 4
263 !%Section Mesh::Derivatives
264 !%Description
265 !% This variable gives the discretization order for the approximation of
266 !% the differential operators. This means, basically, that
267 !% <tt>DerivativesOrder</tt> points are used in each positive/negative
268 !% spatial direction, <i>e.g.</i> <tt>DerivativesOrder = 1</tt> would give
269 !% the well-known three-point formula in 1D.
270 !% The number of points actually used for the Laplacian
271 !% depends on the stencil used. Let <math>O</math> = <tt>DerivativesOrder</tt>, and <math>d</math> = <tt>Dimensions</tt>.
272 !% <ul>
273 !% <li> <tt>stencil_star</tt>: <math>2 O d + 1</math>
274 !% <li> <tt>stencil_cube</tt>: <math>(2 O + 1)^d</math>
275 !% <li> <tt>stencil_starplus</tt>: <math>2 O d + 1 + n</math> with <i>n</i> being 8
276 !% in 2D and 24 in 3D.
277 !% </ul>
278 !%End
279 call parse_variable(namespace, 'DerivativesOrder', 4, der%order)
280 ! overwrite order if given as argument
281 if (present(order)) then
282 der%order = order
283 end if
284
285 !%Variable DerivativesRemoveZeroWeightPoints
286 !%Type logical
287 !%Default yes
288 !%Section Mesh::Derivatives
289 !%Description
290 !% By default, Octopus removes the zero-weight points in the stencils.
291 !% This debug variable gives the possibility to deactive this feature.
292 !%End
293 call parse_variable(namespace, 'DerivativesRemoveZeroWeightPoints', .true., der%remove_zero_weight_points)
294
295
296#ifdef HAVE_MPI
297 !%Variable ParallelizationOfDerivatives
298 !%Type integer
299 !%Default non_blocking
300 !%Section Execution::Parallelization
301 !%Description
302 !% This option selects how the communication of mesh boundaries is performed.
303 !%Option blocking 1
304 !% Blocking communication.
305 !%Option non_blocking 2
306 !% Communication is based on non-blocking point-to-point communication.
307 !%End
308
309 call parse_variable(namespace, 'ParallelizationOfDerivatives', non_blocking, der%comm_method)
310
311 if (.not. varinfo_valid_option('ParallelizationOfDerivatives', der%comm_method)) then
312 call messages_input_error(namespace, 'ParallelizationOfDerivatives')
313 end if
314
315 call messages_obsolete_variable(namespace, 'OverlapDerivatives', 'ParallelizationOfDerivatives')
316#endif
317
318 !%Variable StencilPrimitiveCoordinates
319 !%Type logical
320 !%Section Mesh::Derivatives
321 !%Description
322 !% This variable controls the method for generating the stencil weights for the Laplacian.
323 !% For the gradient, the weights are always computed for primitive coordinates.
324 !% If set to yes, primitive coordinates are used for the polynomials and the right-hand side
325 !% of the Laplacian is computed using the metric tensor and the trace of the Hessian.
326 !% If set to no, Cartesian coordinates are used for the polynomials and the right-hand side
327 !% of the Laplacian reduces to the Cartesian case.
328 !% For some non-orthogonal grids, using primitve coordinates is necessary becasuse the polynomials
329 !% become linearly dependent, thus the corresponding matrix cannot be inverted. For some curvilinear
330 !% coordinate systems, using Cartesian coordinates is more accurate.
331 !%
332 !% By default, use primitve coordinates except for curvilinear meshes.
333 !%End
334 stencil_primitive_coordinates = .not. coord_system%local_basis
335 call parse_variable(namespace, 'StencilPrimitiveCoordinates', stencil_primitive_coordinates, der%stencil_primitive_coordinates)
336
337 ! if needed, der%masses should be initialized in modelmb_particles_init
338 safe_allocate(der%masses(1:space%dim))
339 der%masses = m_one
340
341 ! construct lapl and grad structures
342 safe_allocate(der%op(1:der%dim + 1))
343 der%grad => der%op
344 der%lapl => der%op(der%dim + 1)
345
346 call derivatives_get_stencil_lapl(der, der%lapl, space, coord_system)
348
349 ! find out how many ghost points we need in each dimension
350 safe_allocate(der%n_ghost(1:der%dim))
351 der%n_ghost(:) = 0
352 do idir = 1, der%dim
353 der%n_ghost(idir) = maxval(abs(der%lapl%stencil%points(idir, :)))
354 end do
355
356 nullify(der%coarser)
357 nullify(der%finer)
358 nullify(der%to_coarser)
359 nullify(der%to_finer)
360
361 if (accel_is_enabled()) then
362 ! Check if we can build the uvw_to_xyz kernel
363 select type (coord_system)
364 type is (cartesian_t)
365 ! In this case one does not need to call the kernel, so all is fine
366 class default
367 if (der%dim > 3) then
368 message(1) = "Calculation of derivatives on the GPU with dimension > 3 are only implemented for Cartesian coordinates."
369 call messages_fatal(1, namespace=namespace)
370 else
371 write(flags, '(A,I1.1)') ' -DDIMENSION=', der%dim
372 call accel_kernel_build(kernel_uvw_xyz, 'uvw_to_xyz.cl', 'uvw_to_xyz', flags)
373 end if
374 end select
375 call accel_kernel_build(kernel_dcurl, 'curl.cl', 'dcurl', flags = '-DRTYPE_DOUBLE')
376 call accel_kernel_build(kernel_zcurl, 'curl.cl', 'zcurl', flags = '-DRTYPE_COMPLEX')
377 end if
378
379 pop_sub(derivatives_init)
380 end subroutine derivatives_init
381
382
383 ! ---------------------------------------------------------
384 subroutine derivatives_end(der)
385 type(derivatives_t), intent(inout) :: der
386
387 integer :: idim
388
389 push_sub(derivatives_end)
390
391 assert(allocated(der%op))
392
393 do idim = 1, der%dim+1
394 call nl_operator_end(der%op(idim))
395 end do
396
397 safe_deallocate_a(der%masses)
398
399 safe_deallocate_a(der%n_ghost)
400
401 safe_deallocate_a(der%op)
402 nullify(der%lapl, der%grad)
403
404 nullify(der%coarser)
405 nullify(der%finer)
406 nullify(der%to_coarser)
407 nullify(der%to_finer)
408
409 call boundaries_end(der%boundaries)
410
411 pop_sub(derivatives_end)
412 end subroutine derivatives_end
413
414
415 ! ---------------------------------------------------------
416 subroutine derivatives_get_stencil_lapl(der, lapl, space, coord_system, name, order)
417 type(derivatives_t), intent(in) :: der
418 type(nl_operator_t), intent(inout) :: lapl
419 class(space_t), intent(in) :: space
420 class(coordinate_system_t), intent(in) :: coord_system
421 character(len=80), optional, intent(in) :: name
422 integer, optional, intent(in) :: order
423
424 character(len=80) :: name_
425 integer :: order_
426
428
429 name_ = optional_default(name, "Laplacian")
430 order_ = optional_default(order, der%order)
431
432 ! initialize nl operator
433 call nl_operator_init(lapl, name_)
434
435 ! create stencil
436 select case (der%stencil_type)
437 case (der_star, der_variational)
438 call stencil_star_get_lapl(lapl%stencil, der%dim, order_)
439 case (der_cube)
440 call stencil_cube_get_lapl(lapl%stencil, der%dim, order_)
441 case (der_starplus)
442 call stencil_starplus_get_lapl(lapl%stencil, der%dim, order_)
443 case (der_stargeneral)
444 call stencil_stargeneral_get_arms(lapl%stencil, space%dim, coord_system)
445 call stencil_stargeneral_get_lapl(lapl%stencil, der%dim, order_)
446 end select
447
449 end subroutine derivatives_get_stencil_lapl
450
451
452 ! ---------------------------------------------------------
454 subroutine derivatives_lapl_diag(der, lapl)
455 type(derivatives_t), intent(in) :: der
456 real(real64), intent(out) :: lapl(:)
457
458 push_sub(derivatives_lapl_diag)
459
460 assert(ubound(lapl, dim=1) >= der%mesh%np)
461
462 ! the Laplacian is a real operator
463 call dnl_operator_operate_diag(der%lapl, lapl)
464
465 pop_sub(derivatives_lapl_diag)
466
467 end subroutine derivatives_lapl_diag
468
469
470 ! ---------------------------------------------------------
471 subroutine derivatives_get_stencil_grad(der)
472 type(derivatives_t), intent(inout) :: der
473
474
475 integer :: ii
476 character :: dir_label
477
479
480 assert(associated(der%grad))
481
482 ! initialize nl operator
483 do ii = 1, der%dim
484 dir_label = ' '
485 if (ii < 5) dir_label = index2axis(ii)
486
487 call nl_operator_init(der%grad(ii), "Gradient "//dir_label)
489 ! create stencil
490 select case (der%stencil_type)
491 case (der_star, der_variational)
492 call stencil_star_get_grad(der%grad(ii)%stencil, der%dim, ii, der%order)
493 case (der_cube)
494 call stencil_cube_get_grad(der%grad(ii)%stencil, der%dim, der%order)
495 case (der_starplus)
496 call stencil_starplus_get_grad(der%grad(ii)%stencil, der%dim, ii, der%order)
497 case (der_stargeneral)
498 ! use the simple star stencil
499 call stencil_star_get_grad(der%grad(ii)%stencil, der%dim, ii, der%order)
500 end select
501 end do
502
504
505 end subroutine derivatives_get_stencil_grad
506
507 ! ---------------------------------------------------------
553 subroutine derivatives_build(der, namespace, space, mesh, qvector, regenerate, verbose)
554 type(derivatives_t), intent(inout) :: der
555 type(namespace_t), intent(in) :: namespace
556 class(space_t), intent(in) :: space
557 class(mesh_t), target, intent(in) :: mesh
558 real(real64), optional, intent(in) :: qvector(:)
560 logical, optional, intent(in) :: regenerate
561 logical, optional, intent(in) :: verbose
562
563 integer :: i
564 logical :: const_w_
565 integer :: np_zero_bc
566
567 push_sub(derivatives_build)
568
569 if (.not. optional_default(regenerate, .false.)) then
570 call boundaries_init(der%boundaries, namespace, space, mesh, qvector)
571 end if
572
573 assert(allocated(der%op))
574 assert(der%stencil_type >= der_star .and. der%stencil_type <= der_stargeneral)
575 assert(.not.(der%stencil_type == der_variational .and. mesh%use_curvilinear))
576
577 der%mesh => mesh ! make a pointer to the underlying mesh
578
579 const_w_ = .true.
580
581 ! need non-constant weights for curvilinear and scattering meshes
582 if (mesh%use_curvilinear) const_w_ = .false.
583
584 np_zero_bc = 0
585
586 if (optional_default(regenerate, .false.)) then
587 do i = 1, der%dim+1
588 call nl_operator_end(der%op(i))
589 safe_deallocate_a(der%op(i)%w)
590 end do
591 call derivatives_get_stencil_lapl(der, der%lapl, space, der%coord_system)
593 end if
594
595 ! build operators
596 do i = 1, der%dim+1
597 call nl_operator_build(space, mesh, der%op(i), der%mesh%np, const_w = const_w_, &
598 regenerate=regenerate)
599 np_zero_bc = max(np_zero_bc, nl_operator_np_zero_bc(der%op(i)))
600 end do
601
602 assert(np_zero_bc > mesh%np .and. np_zero_bc <= mesh%np_part)
603
604 select case (der%stencil_type)
605
606 case (der_star, der_starplus, der_stargeneral) ! Laplacian and gradient have different stencils
607 do i = 1, der%dim + 1
608
609 call derivatives_make_discretization(namespace, der, 1, i, der%op(i:i), space, verbose=verbose)
610 end do
611
612 case (der_cube)
613 ! gradients have the same stencils, so use one call to derivatives_make_discretization
614 ! to solve the linear equation once for several right-hand sides
615 call derivatives_make_discretization(namespace, der, der%dim, der%dim, der%op(:), space, verbose=verbose)
616 ! the polynomials are evaluated differently for the Laplacian
617 call derivatives_make_discretization(namespace, der, 1, der%dim+1, der%op(der%dim+1:der%dim+1), space, &
618 verbose=verbose)
619
620 case (der_variational)
621 ! we have the explicit coefficients
622 call stencil_variational_coeff_lapl(der%dim, der%order, mesh%spacing, der%lapl, alpha = der%lapl_cutoff)
623 end select
624
626
627 end subroutine derivatives_build
628
629 ! ---------------------------------------------------------
630 subroutine stencil_pol_grad(stencil_type, dim, direction, order, polynomials)
631 integer, intent(in) :: stencil_type
632 integer, intent(in) :: dim
633 integer, intent(in) :: direction
634 integer, intent(in) :: order
635 integer, intent(inout) :: polynomials(:, :)
636
637 select case (stencil_type)
638 case (der_star, der_stargeneral)
639 call stencil_star_polynomials_grad(direction, order, polynomials)
640 case (der_starplus)
641 call stencil_starplus_pol_grad(dim, direction, order, polynomials)
642 case (der_cube)
643 ! same stencil for gradient and Laplacian in this case
644 call stencil_cube_polynomials_lapl(dim, order, polynomials)
645 end select
646 end subroutine stencil_pol_grad
647
648 ! ---------------------------------------------------------
649 subroutine stencil_pol_lapl(stencil_type, stencil, dim, order, polynomials)
650 integer, intent(in) :: stencil_type
651 type(stencil_t), intent(in) :: stencil
652 integer, intent(in) :: dim
653 integer, intent(in) :: order
654 integer, intent(inout) :: polynomials(:, :)
655
656 select case (stencil_type)
657 case (der_star)
658 call stencil_star_polynomials_lapl(dim, order, polynomials)
659 case (der_starplus)
660 call stencil_starplus_pol_lapl(dim, order, polynomials)
661 case (der_stargeneral)
662 call stencil_stargeneral_pol_lapl(stencil, dim, order, polynomials)
663 case (der_cube)
664 call stencil_cube_polynomials_lapl(dim, order, polynomials)
665 end select
666 end subroutine stencil_pol_lapl
667
668 ! ---------------------------------------------------------
669 subroutine get_rhs_lapl(coord_system, polynomials, chi, rhs, stencil_primitive_coordinates)
670 class(coordinate_system_t), intent(in) :: coord_system
671 integer, intent(in) :: polynomials(:,:)
672 real(real64), intent(in) :: chi(:)
673 real(real64), intent(out) :: rhs(:)
674 logical, intent(in) :: stencil_primitive_coordinates
675
676 integer :: i, j, k, dim
677 real(real64) :: metric_inverse(1:size(polynomials, dim=1), 1:size(polynomials, dim=1))
678 real(real64) :: hessian_trace(1:size(polynomials, dim=1))
679 integer :: powers(0:2)
680
681 push_sub(get_rhs_lapl)
682
683 dim = size(polynomials, dim=1)
684
685 if (stencil_primitive_coordinates) then
686 ! inverse metric and trace of Hessian in primitive coordinates
687 metric_inverse = coord_system%metric_inverse(chi)
688 hessian_trace = coord_system%trace_hessian(chi)
689 else
690 ! inverse metric and trace of Hessian in Cartesian coordinates
691 metric_inverse = m_zero
692 do i = 1, dim
693 metric_inverse(i, i) = m_one
694 end do
695 hessian_trace = m_zero
696 end if
697
698 ! find right-hand side for operator
699 rhs(:) = m_zero
700 do j = 1, size(polynomials, dim=2)
701 ! count the powers of the polynomials
702 powers = 0
703 do i = 1, dim
704 if (polynomials(i, j) <= 2) then
705 powers(polynomials(i, j)) = powers(polynomials(i, j)) + 1
706 end if
707 end do
708
709 ! find all polynomials for which exactly one term is quadratic
710 ! for these, the Laplacian on the polynomial is 2*metric_inverse(i, i)
711 if (powers(2) == 1 .and. powers(0) == dim - 1) then
712 do i = 1, dim
713 if (polynomials(i, j) == 2) then
714 rhs(j) = m_two*metric_inverse(i, i)
715 end if
716 end do
717 end if
718 ! find all polynomials for which exactly two terms are linear
719 ! for these, the Laplacian on the polynomial is metric_inverse(i, k) + metric_inverse(k, i)
720 if (powers(1) == 2 .and. powers(0) == dim - 2) then
721 do i = 1, dim
722 if (polynomials(i, j) == 1) then
723 do k = i+1, dim
724 if (polynomials(k, j) == 1) then
725 rhs(j) = metric_inverse(i, k) + metric_inverse(k, i)
726 end if
727 end do
728 end if
729 end do
730 end if
731 ! find all polynomials for which exactly one term is linear
732 ! for these, the Laplacian on the polynomial is hessian_trace(i)
733 ! this term is only non-zero for curvilinear coordinates with a varying metric tensor
734 if (powers(1) == 1 .and. powers(0) == dim - 1) then
735 do i = 1, dim
736 if (polynomials(i, j) == 1) then
737 rhs(j) = hessian_trace(i)
738 end if
739 end do
740 end if
741 end do
742
743 pop_sub(get_rhs_lapl)
744 end subroutine get_rhs_lapl
745
746 ! ---------------------------------------------------------
747 subroutine get_rhs_grad(polynomials, dir, rhs)
748 integer, intent(in) :: polynomials(:,:)
749 integer, intent(in) :: dir
750 real(real64), intent(out) :: rhs(:)
751
752 integer :: j, k, dim
753 logical :: this_one
754
755 push_sub(get_rhs_grad)
756
757 dim = size(polynomials, dim=1)
758
759 ! find right-hand side for operator
760 rhs(:) = m_zero
761 do j = 1, size(polynomials, dim=2)
762 this_one = .true.
763 do k = 1, dim
764 if (k == dir .and. polynomials(k, j) /= 1) this_one = .false.
765 if (k /= dir .and. polynomials(k, j) /= 0) this_one = .false.
766 end do
767 if (this_one) rhs(j) = m_one
768 end do
769
770 pop_sub(get_rhs_grad)
771 end subroutine get_rhs_grad
772
773
774 ! ---------------------------------------------------------
775 subroutine derivatives_make_discretization(namespace, der, nderiv, ideriv, op, &
776 space, name, verbose, order)
777 type(namespace_t), intent(in) :: namespace
778 type(derivatives_t), intent(in) :: der
779 integer, intent(in) :: nderiv
780 integer, intent(in) :: ideriv
781 type(nl_operator_t), intent(inout) :: op(:)
782 type(space_t), intent(in) :: space
783 character(len=80), optional, intent(in) :: name
784 logical, optional, intent(in) :: verbose
785 integer, optional, intent(in) :: order
786
787 integer :: p, p_max, i, j, k, pow_max, order_
788 real(real64) :: x(der%dim)
789 real(real64), allocatable :: mat(:,:), sol(:,:), powers(:,:)
790 integer, allocatable :: pol(:,:)
791 real(real64), allocatable :: rhs(:,:)
792 character(len=80) :: name_
793
795
796 order_ = optional_default(order, der%order)
797
798 safe_allocate(rhs(1:op(1)%stencil%size, nderiv))
799 safe_allocate(pol(1:der%dim, 1:op(1)%stencil%size))
800 ! get polynomials
801 if (nderiv == 1) then
802 if (ideriv <= der%dim) then ! gradient
803 call stencil_pol_grad(der%stencil_type, der%dim, ideriv, order_, pol)
804 name_ = index2axis(ideriv) // "-gradient"
805 else ! Laplacian
806 call stencil_pol_lapl(der%stencil_type, op(1)%stencil, der%dim, order_, pol)
807 name_ = "Laplacian"
808 end if
809 else if (nderiv == der%dim) then
810 ! in this case, we compute the weights for the gradients at once
811 ! because they have the same stencils
812 ! this is only used for the cube stencil
813 name_ = "gradients"
814 call stencil_pol_grad(der%stencil_type, der%dim, 1, order_, pol)
815 else
816 message(1) = "Error: derivatives_make_discretization can only be called with nderiv = 1 or der%dim."
817 call messages_fatal(1)
818 end if
820 safe_allocate(mat(1:op(1)%stencil%size, 1:op(1)%stencil%size))
821 safe_allocate(sol(1:op(1)%stencil%size, 1:nderiv))
822
823 if (optional_default(verbose, .true.)) then
824 name_ = optional_default(name, name_)
825 message(1) = 'Info: Generating weights for finite-difference discretization of ' // trim(name_)
826 call messages_info(1, namespace=namespace)
827 end if
828
829 ! use to generate power lookup table
830 pow_max = maxval(pol)
831 safe_allocate(powers(1:der%dim, 0:pow_max))
832 powers(:,:) = m_zero
833 powers(:,0) = m_one
834
835 p_max = op(1)%np
836 if (op(1)%const_w) p_max = 1
837
838 do p = 1, p_max
839 ! get polynomials and right-hand side
840 ! we need the current position for the right-hand side for curvilinear coordinates
841 if (nderiv == 1) then
842 if (ideriv <= der%dim) then ! gradient
843 call get_rhs_grad(pol, ideriv, rhs(:,1))
844 name_ = index2axis(ideriv) // "-gradient"
845 else ! Laplacian
846 call get_rhs_lapl(der%mesh%coord_system, pol, der%mesh%chi(:, p), rhs(:,1), der%stencil_primitive_coordinates)
847 end if
848 else if (nderiv == der%dim) then
849 ! in this case, we compute the weights for the gradients at once
850 ! because they have the same stencils
851 do i = 1, der%dim
852 call get_rhs_grad(pol, i, rhs(:,i))
853 end do
854 end if
855
856 ! first polynomial is just a constant
857 mat(1,:) = m_one
858 ! i indexes the point in the stencil
859 do i = 1, op(1)%stencil%size
860 if (ideriv <= der%dim) then
861 ! for gradients, we always need to evaluate the polynomials in primitive coordinates
862 x = real(op(1)%stencil%points(:, i), real64)*der%mesh%spacing
863 else
864 ! for the Laplacian, we can evaluate the polynomials in primitive or Cartesian coordinates
865 if (der%stencil_primitive_coordinates) then
866 x = real(op(1)%stencil%points(:, i), real64)*der%mesh%spacing
867 else
868 x = der%mesh%x(p + op(1)%ri(i, op(1)%rimap(p)), :) - der%mesh%x(p, :)
869 end if
870 end if
871
872 ! NB: these masses are applied on the cartesian directions. Should add a check for non-orthogonal axes
873 x = x*sqrt(der%masses)
874
875 ! calculate powers
876 powers(:, 1) = x
877 do k = 2, pow_max
878 powers(:, k) = x*powers(:, k-1)
879 end do
880
881 ! generate the matrix
882 ! j indexes the polynomial being used
883 do j = 2, op(1)%stencil%size
884 mat(j, i) = powers(1, pol(1, j))
885 do k = 2, der%dim
886 mat(j, i) = mat(j, i)*powers(k, pol(k, j))
887 end do
888 end do
889 end do ! loop over i = point in stencil
890
891 ! linear problem to solve for derivative weights:
892 ! mat * sol = rhs
893 call lalg_linsyssolve(op(1)%stencil%size, nderiv, mat, rhs, sol)
894
895 ! for the cube stencil, all derivatives are calculated at once, so assign
896 ! the correct solution to each operator
897 do i = 1, nderiv
898 op(i)%w(:, p) = sol(:, i)
899 end do
900
901 end do ! loop over points p
902
903 do i = 1, nderiv
904 if (der%remove_zero_weight_points) then
905 call nl_operator_remove_zero_weight_points(op(i), space, der%mesh)
906 end if
908 end do
909
910 ! In case of constant weights, we store the weights of the Laplacian on the GPU, as this
911 ! saves many unecessary transfers
912 do i = 1, nderiv
915 end do
916
917
918 safe_deallocate_a(mat)
919 safe_deallocate_a(sol)
920 safe_deallocate_a(powers)
921 safe_deallocate_a(pol)
922 safe_deallocate_a(rhs)
923
926
927#ifdef HAVE_MPI
928 ! ---------------------------------------------------------
929 logical function derivatives_overlap(this) result(overlap)
930 type(derivatives_t), intent(in) :: this
931
932 push_sub(derivatives_overlap)
933
934 overlap = this%comm_method /= blocking
935
936 pop_sub(derivatives_overlap)
937 end function derivatives_overlap
938#endif
939
940 ! ---------------------------------------------------------
941 subroutine derivatives_get_lapl(this, namespace, op, space, name, order)
942 type(derivatives_t), intent(in) :: this
943 type(namespace_t), intent(in) :: namespace
944 type(nl_operator_t), intent(inout) :: op(:)
945 class(space_t), intent(in) :: space
946 character(len=80), intent(in) :: name
947 integer, intent(in) :: order
948
949 push_sub(derivatives_get_lapl)
950
951 call derivatives_get_stencil_lapl(this, op(1), space, this%mesh%coord_system, name, order)
952 call nl_operator_build(space, this%mesh, op(1), this%mesh%np, const_w = .not. this%mesh%use_curvilinear)
953 call derivatives_make_discretization(namespace, this, 1, this%dim+1, op(1:1), space, name, order=order)
954
955 pop_sub(derivatives_get_lapl)
956 end subroutine derivatives_get_lapl
957
958 ! ---------------------------------------------------------
971 function derivatives_get_inner_boundary_mask(this) result(mask)
972 type(derivatives_t), intent(in) :: this
973
974 logical :: mask(1:this%mesh%np)
975 integer :: ip, is, index
976
977 mask = .false.
978 ! Loop through all points in the grid
979 do ip = 1, this%mesh%np
980 ! For each of them, loop through all points in the stencil
981 do is = 1, this%lapl%stencil%size
982 ! Get the index of the point obtained as: grid_point + displament_due_to_stencil
983 index = nl_operator_get_index(this%lapl, is, ip)
984 ! Check whether the displaced point if outsude the grid. Is so, it belongs to the mask
985 if (index > this%mesh%np + this%mesh%pv%np_ghost) then
986 mask(ip) = .true.
987 exit
988 end if
989 end do
990 end do
991
993
994 ! ---------------------------------------------------------
999 real(real64) function derivatives_lapl_get_max_eigenvalue(this)
1000 type(derivatives_t), intent(in) :: this
1001
1002 integer :: i
1003
1004 push_sub(derivatives_lapl_get_max_eigenvalue)
1005
1006 derivatives_lapl_get_max_eigenvalue = m_zero
1007 if ((this%stencil_type == der_star .or. this%stencil_type == der_stargeneral) &
1008 .and. .not. this%mesh%use_curvilinear) then
1009 ! use Fourier transform of stencil evaluated at the maximum phase
1010 do i = 1, this%lapl%stencil%size
1011 derivatives_lapl_get_max_eigenvalue = derivatives_lapl_get_max_eigenvalue + &
1012 (-1)**maxval(abs(this%lapl%stencil%points(:, i)))*this%lapl%w(i, 1)
1013 end do
1014 derivatives_lapl_get_max_eigenvalue = abs(derivatives_lapl_get_max_eigenvalue)
1015 else
1016 ! use upper bound from continuum for other stencils
1017 do i = 1, this%dim
1018 derivatives_lapl_get_max_eigenvalue = derivatives_lapl_get_max_eigenvalue + &
1019 m_pi**2/this%mesh%spacing(i)**2
1020 end do
1021 end if
1022
1023 pop_sub(derivatives_lapl_get_max_eigenvalue)
1025
1026 subroutine derivates_set_coordinates_system(der, coord_system)
1027 type(derivatives_t), target, intent(inout) :: der
1028 class(coordinate_system_t), target, intent(in) :: coord_system
1029
1030 der%coord_system => coord_system
1032
1033#include "undef.F90"
1034#include "real.F90"
1035#include "derivatives_inc.F90"
1036
1037#include "undef.F90"
1038#include "complex.F90"
1039#include "derivatives_inc.F90"
1040
1041end module derivatives_oct_m
1042
1043!! Local Variables:
1044!! mode: f90
1045!! coding: utf-8
1046!! End:
double sqrt(double __x) __attribute__((__nothrow__
subroutine, public accel_kernel_build(this, file_name, kernel_name, flags)
Definition: accel.F90:1340
pure logical function, public accel_is_enabled()
Definition: accel.F90:418
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 dderivatives_test(this, namespace, repetitions, min_blocksize, max_blocksize)
unit test for derivatives
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
subroutine, public zderivatives_test(this, namespace, repetitions, min_blocksize, max_blocksize)
unit test for derivatives
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:192
real(real64), parameter, public m_zero
Definition: global.F90:190
real(real64), parameter, public m_pi
some mathematical constants
Definition: global.F90:188
real(real64), parameter, public m_one
Definition: global.F90:191
This module is intended to contain "only mathematical" functions and procedures.
Definition: math.F90:117
This module defines various routines, operating on mesh functions.
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:1029
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:416
subroutine, public messages_input_error(namespace, var, details, row, column)
Definition: messages.F90:697
subroutine, public messages_info(no_lines, iunit, debug_only, stress, all_nodes, namespace)
Definition: messages.F90:600
This module defines non-local operators.
subroutine, public dnl_operator_operate_diag(op, fo)
subroutine, public nl_operator_init(op, label)
initialize an instance of a non-local operator by setting the label
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.
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 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:205
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:187
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)