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 if (der%stencil_type == der_variational) then
239 !%Variable DerivativesLaplacianFilter
240 !%Type float
241 !%Default 1.0
242 !%Section Mesh::Derivatives
243 !%Description
244 !% Undocumented
245 !%End
246 call parse_variable(namespace, 'DerivativesLaplacianFilter', m_one, der%lapl_cutoff)
247 end if
249 !%Variable DerivativesOrder
250 !%Type integer
251 !%Default 4
252 !%Section Mesh::Derivatives
253 !%Description
254 !% This variable gives the discretization order for the approximation of
255 !% the differential operators. This means, basically, that
256 !% <tt>DerivativesOrder</tt> points are used in each positive/negative
257 !% spatial direction, <i>e.g.</i> <tt>DerivativesOrder = 1</tt> would give
258 !% the well-known three-point formula in 1D.
259 !% The number of points actually used for the Laplacian
260 !% depends on the stencil used. Let <math>O</math> = <tt>DerivativesOrder</tt>, and <math>d</math> = <tt>Dimensions</tt>.
261 !% <ul>
262 !% <li> <tt>stencil_star</tt>: <math>2 O d + 1</math>
263 !% <li> <tt>stencil_cube</tt>: <math>(2 O + 1)^d</math>
264 !% <li> <tt>stencil_starplus</tt>: <math>2 O d + 1 + n</math> with <i>n</i> being 8
265 !% in 2D and 24 in 3D.
266 !% </ul>
267 !%End
268 call parse_variable(namespace, 'DerivativesOrder', 4, der%order)
269 ! overwrite order if given as argument
270 if (present(order)) then
271 der%order = order
272 end if
273
274 !%Variable DerivativesRemoveZeroWeightPoints
275 !%Type logical
276 !%Default yes
277 !%Section Mesh::Derivatives
278 !%Description
279 !% By default, Octopus removes the zero-weight points in the stencils.
280 !% This debug variable gives the possibility to deactive this feature.
281 !%End
282 call parse_variable(namespace, 'DerivativesRemoveZeroWeightPoints', .true., der%remove_zero_weight_points)
283
284
285#ifdef HAVE_MPI
286 !%Variable ParallelizationOfDerivatives
287 !%Type integer
288 !%Default non_blocking
289 !%Section Execution::Parallelization
290 !%Description
291 !% This option selects how the communication of mesh boundaries is performed.
292 !%Option blocking 1
293 !% Blocking communication.
294 !%Option non_blocking 2
295 !% Communication is based on non-blocking point-to-point communication.
296 !%End
297
298 call parse_variable(namespace, 'ParallelizationOfDerivatives', non_blocking, der%comm_method)
299
300 if (.not. varinfo_valid_option('ParallelizationOfDerivatives', der%comm_method)) then
301 call messages_input_error(namespace, 'ParallelizationOfDerivatives')
302 end if
303
304 call messages_obsolete_variable(namespace, 'OverlapDerivatives', 'ParallelizationOfDerivatives')
305#endif
306
307 !%Variable StencilPrimitiveCoordinates
308 !%Type logical
309 !%Section Mesh::Derivatives
310 !%Description
311 !% This variable controls the method for generating the stencil weights for the Laplacian.
312 !% For the gradient, the weights are always computed for primitive coordinates.
313 !% If set to yes, primitive coordinates are used for the polynomials and the right-hand side
314 !% of the Laplacian is computed using the metric tensor and the trace of the Hessian.
315 !% If set to no, Cartesian coordinates are used for the polynomials and the right-hand side
316 !% of the Laplacian reduces to the Cartesian case.
317 !% For some non-orthogonal grids, using primitve coordinates is necessary becasuse the polynomials
318 !% become linearly dependent, thus the corresponding matrix cannot be inverted. For some curvilinear
319 !% coordinate systems, using Cartesian coordinates is more accurate.
320 !%
321 !% By default, use primitve coordinates except for curvilinear meshes.
322 !%End
323 stencil_primitive_coordinates = .not. coord_system%local_basis
324 call parse_variable(namespace, 'StencilPrimitiveCoordinates', stencil_primitive_coordinates, der%stencil_primitive_coordinates)
325
326 ! if needed, der%masses should be initialized in modelmb_particles_init
327 safe_allocate(der%masses(1:space%dim))
328 der%masses = m_one
329
330 ! construct lapl and grad structures
331 safe_allocate(der%op(1:der%dim + 1))
332 der%grad => der%op
333 der%lapl => der%op(der%dim + 1)
334
335 call derivatives_get_stencil_lapl(der, der%lapl, space, coord_system)
337
338 ! find out how many ghost points we need in each dimension
339 safe_allocate(der%n_ghost(1:der%dim))
340 der%n_ghost(:) = 0
341 do idir = 1, der%dim
342 der%n_ghost(idir) = maxval(abs(der%lapl%stencil%points(idir, :)))
343 end do
344
345 nullify(der%coarser)
346 nullify(der%finer)
347 nullify(der%to_coarser)
348 nullify(der%to_finer)
349
350 if (accel_is_enabled()) then
351 ! Check if we can build the uvw_to_xyz kernel
352 select type (coord_system)
353 type is (cartesian_t)
354 ! In this case one does not need to call the kernel, so all is fine
355 class default
356 if (der%dim > 3) then
357 message(1) = "Calculation of derivatives on the GPU with dimension > 3 are only implemented for Cartesian coordinates."
358 call messages_fatal(1, namespace=namespace)
359 else
360 ! Each dimension adds a (source, ld, dest, ld) argument group
361 write(kernel_name, '(a,i1,a)') 'uvw_to_xyz<double, Dim::D', der%dim, &
362 repeat(', double*, int, double*, int', der%dim)//'>'
363 call accel_kernel_start_call(kernel_uvw_xyz, 'uvw_to_xyz.cu', trim(kernel_name))
364 end if
365 end select
366 call accel_kernel_build(kernel_dcurl, 'curl.cu', 'curl<double>')
367 call accel_kernel_build(kernel_zcurl, 'curl.cu', 'curl<complex<double>>')
368 end if
369
370 pop_sub(derivatives_init)
371 end subroutine derivatives_init
372
373
374 ! ---------------------------------------------------------
375 subroutine derivatives_end(der)
376 type(derivatives_t), intent(inout) :: der
377
378 integer :: idim
379
380 push_sub(derivatives_end)
381
382 assert(allocated(der%op))
383
384 do idim = 1, der%dim+1
385 call nl_operator_end(der%op(idim))
386 end do
387
388 safe_deallocate_a(der%masses)
389
390 safe_deallocate_a(der%n_ghost)
391
392 safe_deallocate_a(der%op)
393 nullify(der%lapl, der%grad)
394
395 nullify(der%coarser)
396 nullify(der%finer)
397 nullify(der%to_coarser)
398 nullify(der%to_finer)
399
400 call boundaries_end(der%boundaries)
401
402 pop_sub(derivatives_end)
403 end subroutine derivatives_end
404
405
406 ! ---------------------------------------------------------
407 subroutine derivatives_get_stencil_lapl(der, lapl, space, coord_system, name, order)
408 type(derivatives_t), intent(in) :: der
409 type(nl_operator_t), intent(inout) :: lapl
410 class(space_t), intent(in) :: space
411 class(coordinate_system_t), intent(in) :: coord_system
412 character(len=80), optional, intent(in) :: name
413 integer, optional, intent(in) :: order
414
415 character(len=80) :: name_
416 integer :: order_
417
419
420 name_ = optional_default(name, "Laplacian")
421 order_ = optional_default(order, der%order)
422
423 ! initialize nl operator
424 call nl_operator_init(lapl, name_, symm=op_symmetric)
425
426 ! create stencil
427 select case (der%stencil_type)
428 case (der_star, der_variational)
429 call stencil_star_get_lapl(lapl%stencil, der%dim, order_)
430 case (der_cube)
431 call stencil_cube_get_lapl(lapl%stencil, der%dim, order_)
432 case (der_starplus)
433 call stencil_starplus_get_lapl(lapl%stencil, der%dim, order_)
434 case (der_stargeneral)
435 call stencil_stargeneral_get_arms(lapl%stencil, space%dim, coord_system)
436 call stencil_stargeneral_get_lapl(lapl%stencil, der%dim, order_)
437 end select
438
440 end subroutine derivatives_get_stencil_lapl
441
442
443 ! ---------------------------------------------------------
445 subroutine derivatives_lapl_diag(der, lapl)
446 type(derivatives_t), intent(in) :: der
447 real(real64), intent(out) :: lapl(:)
448
449 push_sub(derivatives_lapl_diag)
450
451 assert(ubound(lapl, dim=1) >= der%mesh%np)
452
453 ! the Laplacian is a real operator
454 call dnl_operator_operate_diag(der%lapl, lapl)
455
456 pop_sub(derivatives_lapl_diag)
457
458 end subroutine derivatives_lapl_diag
459
460
461 ! ---------------------------------------------------------
462 subroutine derivatives_get_stencil_grad(der)
463 type(derivatives_t), intent(inout) :: der
464
465
466 integer :: ii
467 character :: dir_label
468
470
471 assert(associated(der%grad))
472
473 ! initialize nl operator
474 do ii = 1, der%dim
475 dir_label = ' '
476 if (ii < 5) dir_label = index2axis(ii)
477
478 call nl_operator_init(der%grad(ii), "Gradient "//dir_label, symm=op_antisymmetric)
480 ! create stencil
481 select case (der%stencil_type)
482 case (der_star, der_variational)
483 call stencil_star_get_grad(der%grad(ii)%stencil, der%dim, ii, der%order)
484 case (der_cube)
485 call stencil_cube_get_grad(der%grad(ii)%stencil, der%dim, der%order)
486 case (der_starplus)
487 call stencil_starplus_get_grad(der%grad(ii)%stencil, der%dim, ii, der%order)
488 case (der_stargeneral)
489 ! use the simple star stencil
490 call stencil_star_get_grad(der%grad(ii)%stencil, der%dim, ii, der%order)
491 end select
492 end do
493
495
496 end subroutine derivatives_get_stencil_grad
497
498 ! ---------------------------------------------------------
544 subroutine derivatives_build(der, namespace, space, mesh, qvector, regenerate, verbose)
545 type(derivatives_t), intent(inout) :: der
546 type(namespace_t), intent(in) :: namespace
547 class(space_t), intent(in) :: space
548 class(mesh_t), target, intent(in) :: mesh
549 real(real64), optional, intent(in) :: qvector(:)
551 logical, optional, intent(in) :: regenerate
552 logical, optional, intent(in) :: verbose
553
554 integer :: i
555 logical :: const_w_
556 integer :: np_zero_bc
557
558 push_sub(derivatives_build)
559
560 if (.not. optional_default(regenerate, .false.)) then
561 call boundaries_init(der%boundaries, namespace, space, mesh, qvector)
562 end if
563
564 assert(allocated(der%op))
565 assert(der%stencil_type >= der_star .and. der%stencil_type <= der_stargeneral)
566 assert(.not.(der%stencil_type == der_variational .and. mesh%use_curvilinear))
567
568 der%mesh => mesh ! make a pointer to the underlying mesh
569
570 const_w_ = .true.
571
572 ! need non-constant weights for curvilinear and scattering meshes
573 if (mesh%use_curvilinear) const_w_ = .false.
574
575 np_zero_bc = 0
576
577 if (optional_default(regenerate, .false.)) then
578 do i = 1, der%dim+1
579 call nl_operator_end(der%op(i))
580 safe_deallocate_a(der%op(i)%w)
581 end do
582 call derivatives_get_stencil_lapl(der, der%lapl, space, der%coord_system)
584 end if
585
586 ! build operators
587 do i = 1, der%dim+1
588 call nl_operator_build(space, mesh, der%op(i), der%mesh%np, const_w = const_w_, &
589 regenerate=regenerate)
590 np_zero_bc = max(np_zero_bc, nl_operator_np_zero_bc(der%op(i)))
591 end do
592
593 assert(np_zero_bc > mesh%np .and. np_zero_bc <= mesh%np_part)
594
595 select case (der%stencil_type)
596
597 case (der_star, der_starplus, der_stargeneral) ! Laplacian and gradient have different stencils
598 do i = 1, der%dim + 1
599
600 call derivatives_make_discretization(namespace, der, 1, i, der%op(i:i), space, verbose=verbose)
601 end do
602
603 case (der_cube)
604 ! gradients have the same stencils, so use one call to derivatives_make_discretization
605 ! to solve the linear equation once for several right-hand sides
606 call derivatives_make_discretization(namespace, der, der%dim, der%dim, der%op(:), space, verbose=verbose)
607 ! the polynomials are evaluated differently for the Laplacian
608 call derivatives_make_discretization(namespace, der, 1, der%dim+1, der%op(der%dim+1:der%dim+1), space, &
609 verbose=verbose)
610
611 case (der_variational)
612 ! we have the explicit coefficients
613 call stencil_variational_coeff_lapl(der%dim, der%order, mesh%spacing, der%lapl, alpha = der%lapl_cutoff)
614 end select
615
617
618 end subroutine derivatives_build
619
620 ! ---------------------------------------------------------
621 subroutine stencil_pol_grad(stencil_type, dim, direction, order, polynomials)
622 integer, intent(in) :: stencil_type
623 integer, intent(in) :: dim
624 integer, intent(in) :: direction
625 integer, intent(in) :: order
626 integer, intent(inout) :: polynomials(:, :)
627
628 select case (stencil_type)
629 case (der_star, der_stargeneral)
630 call stencil_star_polynomials_grad(direction, order, polynomials)
631 case (der_starplus)
632 call stencil_starplus_pol_grad(dim, direction, order, polynomials)
633 case (der_cube)
634 ! same stencil for gradient and Laplacian in this case
635 call stencil_cube_polynomials_lapl(dim, order, polynomials)
636 end select
637 end subroutine stencil_pol_grad
638
639 ! ---------------------------------------------------------
640 subroutine stencil_pol_lapl(stencil_type, stencil, dim, order, polynomials)
641 integer, intent(in) :: stencil_type
642 type(stencil_t), intent(in) :: stencil
643 integer, intent(in) :: dim
644 integer, intent(in) :: order
645 integer, intent(inout) :: polynomials(:, :)
646
647 select case (stencil_type)
648 case (der_star)
649 call stencil_star_polynomials_lapl(dim, order, polynomials)
650 case (der_starplus)
651 call stencil_starplus_pol_lapl(dim, order, polynomials)
652 case (der_stargeneral)
653 call stencil_stargeneral_pol_lapl(stencil, dim, order, polynomials)
654 case (der_cube)
655 call stencil_cube_polynomials_lapl(dim, order, polynomials)
656 end select
657 end subroutine stencil_pol_lapl
658
659 ! ---------------------------------------------------------
660 subroutine get_rhs_lapl(coord_system, polynomials, chi, rhs, stencil_primitive_coordinates)
661 class(coordinate_system_t), intent(in) :: coord_system
662 integer, intent(in) :: polynomials(:,:)
663 real(real64), intent(in) :: chi(:)
664 real(real64), intent(out) :: rhs(:)
665 logical, intent(in) :: stencil_primitive_coordinates
666
667 integer :: i, j, k, dim
668 real(real64) :: metric_inverse(1:size(polynomials, dim=1), 1:size(polynomials, dim=1))
669 real(real64) :: hessian_trace(1:size(polynomials, dim=1))
670 integer :: powers(0:2)
671
672 push_sub(get_rhs_lapl)
673
674 dim = size(polynomials, dim=1)
675
676 if (stencil_primitive_coordinates) then
677 ! inverse metric and trace of Hessian in primitive coordinates
678 metric_inverse = coord_system%metric_inverse(chi)
679 hessian_trace = coord_system%trace_hessian(chi)
680 else
681 ! inverse metric and trace of Hessian in Cartesian coordinates
682 metric_inverse = m_zero
683 do i = 1, dim
684 metric_inverse(i, i) = m_one
685 end do
686 hessian_trace = m_zero
687 end if
688
689 ! find right-hand side for operator
690 rhs(:) = m_zero
691 do j = 1, size(polynomials, dim=2)
692 ! count the powers of the polynomials
693 powers = 0
694 do i = 1, dim
695 if (polynomials(i, j) <= 2) then
696 powers(polynomials(i, j)) = powers(polynomials(i, j)) + 1
697 end if
698 end do
699
700 ! find all polynomials for which exactly one term is quadratic
701 ! for these, the Laplacian on the polynomial is 2*metric_inverse(i, i)
702 if (powers(2) == 1 .and. powers(0) == dim - 1) then
703 do i = 1, dim
704 if (polynomials(i, j) == 2) then
705 rhs(j) = m_two*metric_inverse(i, i)
706 end if
707 end do
708 end if
709 ! find all polynomials for which exactly two terms are linear
710 ! for these, the Laplacian on the polynomial is metric_inverse(i, k) + metric_inverse(k, i)
711 if (powers(1) == 2 .and. powers(0) == dim - 2) then
712 do i = 1, dim
713 if (polynomials(i, j) == 1) then
714 do k = i+1, dim
715 if (polynomials(k, j) == 1) then
716 rhs(j) = metric_inverse(i, k) + metric_inverse(k, i)
717 end if
718 end do
719 end if
720 end do
721 end if
722 ! find all polynomials for which exactly one term is linear
723 ! for these, the Laplacian on the polynomial is hessian_trace(i)
724 ! this term is only non-zero for curvilinear coordinates with a varying metric tensor
725 if (powers(1) == 1 .and. powers(0) == dim - 1) then
726 do i = 1, dim
727 if (polynomials(i, j) == 1) then
728 rhs(j) = hessian_trace(i)
729 end if
730 end do
731 end if
732 end do
733
734 pop_sub(get_rhs_lapl)
735 end subroutine get_rhs_lapl
736
737 ! ---------------------------------------------------------
738 subroutine get_rhs_grad(polynomials, dir, rhs)
739 integer, intent(in) :: polynomials(:,:)
740 integer, intent(in) :: dir
741 real(real64), intent(out) :: rhs(:)
742
743 integer :: j, k, dim
744 logical :: this_one
745
746 push_sub(get_rhs_grad)
747
748 dim = size(polynomials, dim=1)
749
750 ! find right-hand side for operator
751 rhs(:) = m_zero
752 do j = 1, size(polynomials, dim=2)
753 this_one = .true.
754 do k = 1, dim
755 if (k == dir .and. polynomials(k, j) /= 1) this_one = .false.
756 if (k /= dir .and. polynomials(k, j) /= 0) this_one = .false.
757 end do
758 if (this_one) rhs(j) = m_one
759 end do
760
761 pop_sub(get_rhs_grad)
762 end subroutine get_rhs_grad
763
764
765 ! ---------------------------------------------------------
766 subroutine derivatives_make_discretization(namespace, der, nderiv, ideriv, op, &
767 space, name, verbose, order)
768 type(namespace_t), intent(in) :: namespace
769 type(derivatives_t), intent(in) :: der
770 integer, intent(in) :: nderiv
771 integer, intent(in) :: ideriv
772 type(nl_operator_t), intent(inout) :: op(:)
773 type(space_t), intent(in) :: space
774 character(len=80), optional, intent(in) :: name
775 logical, optional, intent(in) :: verbose
776 integer, optional, intent(in) :: order
777
778 integer :: p, p_max, i, j, k, pow_max, order_
779 real(real64) :: x(der%dim)
780 real(real64), allocatable :: mat(:,:), sol(:,:), powers(:,:)
781 integer, allocatable :: pol(:,:)
782 real(real64), allocatable :: rhs(:,:)
783 character(len=80) :: name_
784
786
787 order_ = optional_default(order, der%order)
788
789 safe_allocate(rhs(1:op(1)%stencil%size, nderiv))
790 safe_allocate(pol(1:der%dim, 1:op(1)%stencil%size))
791 ! get polynomials
792 if (nderiv == 1) then
793 if (ideriv <= der%dim) then ! gradient
794 call stencil_pol_grad(der%stencil_type, der%dim, ideriv, order_, pol)
795 name_ = index2axis(ideriv) // "-gradient"
796 else ! Laplacian
797 call stencil_pol_lapl(der%stencil_type, op(1)%stencil, der%dim, order_, pol)
798 name_ = "Laplacian"
799 end if
800 else if (nderiv == der%dim) then
801 ! in this case, we compute the weights for the gradients at once
802 ! because they have the same stencils
803 ! this is only used for the cube stencil
804 name_ = "gradients"
805 call stencil_pol_grad(der%stencil_type, der%dim, 1, order_, pol)
806 else
807 message(1) = "Error: derivatives_make_discretization can only be called with nderiv = 1 or der%dim."
808 call messages_fatal(1)
809 end if
811 safe_allocate(mat(1:op(1)%stencil%size, 1:op(1)%stencil%size))
812 safe_allocate(sol(1:op(1)%stencil%size, 1:nderiv))
813
814 if (optional_default(verbose, .true.)) then
815 name_ = optional_default(name, name_)
816 message(1) = 'Info: Generating weights for finite-difference discretization of ' // trim(name_)
817 call messages_info(1, namespace=namespace)
818 end if
819
820 ! use to generate power lookup table
821 pow_max = maxval(pol)
822 safe_allocate(powers(1:der%dim, 0:pow_max))
823 powers(:,:) = m_zero
824 powers(:,0) = m_one
825
826 p_max = op(1)%np
827 if (op(1)%const_w) p_max = 1
828
829 do p = 1, p_max
830 ! get polynomials and right-hand side
831 ! we need the current position for the right-hand side for curvilinear coordinates
832 if (nderiv == 1) then
833 if (ideriv <= der%dim) then ! gradient
834 call get_rhs_grad(pol, ideriv, rhs(:,1))
835 name_ = index2axis(ideriv) // "-gradient"
836 else ! Laplacian
837 call get_rhs_lapl(der%mesh%coord_system, pol, der%mesh%chi(:, p), rhs(:,1), der%stencil_primitive_coordinates)
838 end if
839 else if (nderiv == der%dim) then
840 ! in this case, we compute the weights for the gradients at once
841 ! because they have the same stencils
842 do i = 1, der%dim
843 call get_rhs_grad(pol, i, rhs(:,i))
844 end do
845 end if
846
847 ! first polynomial is just a constant
848 mat(1,:) = m_one
849 ! i indexes the point in the stencil
850 do i = 1, op(1)%stencil%size
851 if (ideriv <= der%dim) then
852 ! for gradients, we always need to evaluate the polynomials in primitive coordinates
853 x = real(op(1)%stencil%points(:, i), real64)*der%mesh%spacing
854 else
855 ! for the Laplacian, we can evaluate the polynomials in primitive or Cartesian coordinates
856 if (der%stencil_primitive_coordinates) then
857 x = real(op(1)%stencil%points(:, i), real64)*der%mesh%spacing
858 else
859 x = der%mesh%x(:, p + op(1)%ri(i, op(1)%rimap(p))) - der%mesh%x(:, p)
860 end if
861 end if
862
863 ! NB: these masses are applied on the cartesian directions. Should add a check for non-orthogonal axes
864 x = x*sqrt(der%masses)
865
866 ! calculate powers
867 powers(:, 1) = x
868 do k = 2, pow_max
869 powers(:, k) = x*powers(:, k-1)
870 end do
871
872 ! generate the matrix
873 ! j indexes the polynomial being used
874 do j = 2, op(1)%stencil%size
875 mat(j, i) = powers(1, pol(1, j))
876 do k = 2, der%dim
877 mat(j, i) = mat(j, i)*powers(k, pol(k, j))
878 end do
879 end do
880 end do ! loop over i = point in stencil
881
882 ! linear problem to solve for derivative weights:
883 ! mat * sol = rhs
884 call lalg_linsyssolve(op(1)%stencil%size, nderiv, mat, rhs, sol)
885
886 ! for the cube stencil, all derivatives are calculated at once, so assign
887 ! the correct solution to each operator
888 do i = 1, nderiv
889 op(i)%w(:, p) = sol(:, i)
890 end do
891
892 end do ! loop over points p
893
894 do i = 1, nderiv
895 if (der%remove_zero_weight_points) then
896 call nl_operator_remove_zero_weight_points(op(i), space, der%mesh)
897 end if
899 if (op(i)%const_w) then
901 end if
902 end do
903
904 ! In case of constant weights, we store the weights of the Laplacian on the GPU, as this
905 ! saves many unecessary transfers
906 do i = 1, nderiv
909 end do
910
911
912 safe_deallocate_a(mat)
913 safe_deallocate_a(sol)
914 safe_deallocate_a(powers)
915 safe_deallocate_a(pol)
916 safe_deallocate_a(rhs)
917
920
921#ifdef HAVE_MPI
922 ! ---------------------------------------------------------
923 logical function derivatives_overlap(this) result(overlap)
924 type(derivatives_t), intent(in) :: this
925
926 push_sub(derivatives_overlap)
927
928 overlap = this%comm_method /= blocking
929
930 pop_sub(derivatives_overlap)
931 end function derivatives_overlap
932#endif
933
934 ! ---------------------------------------------------------
935 subroutine derivatives_get_lapl(this, namespace, op, space, name, order)
936 type(derivatives_t), intent(in) :: this
937 type(namespace_t), intent(in) :: namespace
938 type(nl_operator_t), intent(inout) :: op(:)
939 class(space_t), intent(in) :: space
940 character(len=80), intent(in) :: name
941 integer, intent(in) :: order
942
943 push_sub(derivatives_get_lapl)
944
945 call derivatives_get_stencil_lapl(this, op(1), space, this%mesh%coord_system, name, order)
946 call nl_operator_build(space, this%mesh, op(1), this%mesh%np, const_w = .not. this%mesh%use_curvilinear)
947 call derivatives_make_discretization(namespace, this, 1, this%dim+1, op(1:1), space, name, order=order)
948
949 pop_sub(derivatives_get_lapl)
950 end subroutine derivatives_get_lapl
951
952 ! ---------------------------------------------------------
965 function derivatives_get_inner_boundary_mask(this) result(mask)
966 type(derivatives_t), intent(in) :: this
967
968 logical :: mask(1:this%mesh%np)
969 integer :: ip, is, index
970
971 mask = .false.
972 ! Loop through all points in the grid
973 do ip = 1, this%mesh%np
974 ! For each of them, loop through all points in the stencil
975 do is = 1, this%lapl%stencil%size
976 ! Get the index of the point obtained as: grid_point + displament_due_to_stencil
977 index = nl_operator_get_index(this%lapl, is, ip)
978 ! Check whether the displaced point if outsude the grid. Is so, it belongs to the mask
979 if (index > this%mesh%np + this%mesh%pv%np_ghost) then
980 mask(ip) = .true.
981 exit
982 end if
983 end do
984 end do
985
987
988 ! ---------------------------------------------------------
993 real(real64) function derivatives_lapl_get_max_eigenvalue(this)
994 type(derivatives_t), intent(in) :: this
995
996 integer :: i
997
998 push_sub(derivatives_lapl_get_max_eigenvalue)
999
1000 derivatives_lapl_get_max_eigenvalue = m_zero
1001 if ((this%stencil_type == der_star .or. this%stencil_type == der_stargeneral) &
1002 .and. .not. this%mesh%use_curvilinear) then
1003 ! use Fourier transform of stencil evaluated at the maximum phase
1004 do i = 1, this%lapl%stencil%size
1005 derivatives_lapl_get_max_eigenvalue = derivatives_lapl_get_max_eigenvalue + &
1006 (-1)**maxval(abs(this%lapl%stencil%points(:, i)))*this%lapl%w(i, 1)
1007 end do
1008 derivatives_lapl_get_max_eigenvalue = abs(derivatives_lapl_get_max_eigenvalue)
1009 else
1010 ! use upper bound from continuum for other stencils
1011 do i = 1, this%dim
1012 derivatives_lapl_get_max_eigenvalue = derivatives_lapl_get_max_eigenvalue + &
1013 m_pi**2/this%mesh%spacing(i)**2
1014 end do
1015 end if
1016
1017 pop_sub(derivatives_lapl_get_max_eigenvalue)
1019
1020 subroutine derivates_set_coordinates_system(der, coord_system)
1021 type(derivatives_t), target, intent(inout) :: der
1022 class(coordinate_system_t), target, intent(in) :: coord_system
1023
1024 der%coord_system => coord_system
1026
1027#include "undef.F90"
1028#include "real.F90"
1029#include "derivatives_inc.F90"
1030
1031#include "undef.F90"
1032#include "complex.F90"
1033#include "derivatives_inc.F90"
1034
1035end module derivatives_oct_m
1036
1037!! Local Variables:
1038!! mode: f90
1039!! coding: utf-8
1040!! End:
double sqrt(double __x) __attribute__((__nothrow__
subroutine, public accel_kernel_start_call(this, file_name, kernel_name, flags)
Definition: accel.F90:1749
subroutine, public accel_kernel_build(this, file_name, kernel_name, flags)
Compile the program that contains a given kernel.
Definition: accel.F90:1707
pure logical function, public accel_is_enabled()
Definition: accel.F90:395
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: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)