Octopus
helmholtz_decomposition_test.F90
Go to the documentation of this file.
1!! Copyright (C) 2019 R. Jestaedt, H. Appel, F. Bonafe, M. Oliveira, N. Tancogne-Dejean
2!! Copyright (C) 2022-2023 F. Troisi
3!!
4!! This program is free software; you can redistribute it and/or modify
5!! it under the terms of the GNU General Public License as published by
6!! the Free Software Foundation; either version 2, or (at your option)
7!! any later version.
8!!
9!! This program is distributed in the hope that it will be useful,
10!! but WITHOUT ANY WARRANTY; without even the implied warranty of
11!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12!! GNU General Public License for more details.
13!!
14!! You should have received a copy of the GNU General Public License
15!! along with this program; if not, write to the Free Software
16!! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17!! 02110-1301, USA.
18!!
19
20#include "global.h"
21
23
25 use blas_oct_m
26 use box_oct_m
28 use debug_oct_m
30 use global_oct_m
31 use grid_oct_m
33 use io_oct_m
35 use, intrinsic :: iso_fortran_env
37 use loct_oct_m
38 use math_oct_m
39 use mesh_oct_m
44 use parser_oct_m
47 use space_oct_m
48 use unit_oct_m
50
51 implicit none
52
53 private
54 public :: &
58
60 private
61 real(real64), allocatable :: total_field(:,:)
62 real(real64), allocatable :: trans_field(:,:)
63 real(real64), allocatable :: long_field(:,:)
64 real(real64), allocatable :: vector_potential(:,:)
65 real(real64), allocatable :: scalar_potential(:)
66 real(real64) :: abs_norm_self_consistency, abs_norm_long_field, abs_norm_trans_field, abs_norm_scal_pot, abs_norm_vec_pot
67 real(real64) :: rel_norm_self_consistency, rel_norm_long_field, rel_norm_trans_field, rel_norm_scal_pot, rel_norm_vec_pot
68 contains
69 procedure :: init => helmholtz_test_init
71 end type helmholtz_test_t
72
73contains
74
75 subroutine helmholtz_test_init(this, np_part, dim)
76 class(helmholtz_test_t), intent(inout) :: this
77 integer, intent(in) :: np_part
78 integer, intent(in) :: dim
79
80 push_sub(helmholtz_test_init)
81
82 ! Scalar potential
83 safe_allocate(this%scalar_potential(1:np_part))
84 this%scalar_potential = m_zero
85 ! Vector potential
86 safe_allocate(this%vector_potential(1:np_part, 1:dim))
87 this%vector_potential = m_zero
88 ! Total Field
89 safe_allocate(this%total_field(1:np_part, 1:dim))
90 this%total_field = m_zero
91 ! Transverse field
92 safe_allocate(this%trans_field(1:np_part, 1:dim))
93 this%trans_field = m_zero
94 ! Longitudinal field
95 safe_allocate(this%long_field(1:np_part, 1:dim))
96 this%long_field = m_zero
97
98 pop_sub(helmholtz_test_init)
99 end subroutine helmholtz_test_init
100
101 subroutine helmholtz_test_finalize(this)
102 type(helmholtz_test_t), intent(inout) :: this
103
105
106 safe_deallocate_a(this%total_field)
107 safe_deallocate_a(this%trans_field)
108 safe_deallocate_a(this%long_field)
109 safe_deallocate_a(this%vector_potential)
110 safe_deallocate_a(this%scalar_potential)
111
113 end subroutine helmholtz_test_finalize
114
115 subroutine hertzian_dipole_test(helmholtz, sys_grid, namespace, space)
116 class(helmholtz_decomposition_t), intent(inout) :: helmholtz
117 type(grid_t), intent(in) :: sys_grid
118 type(namespace_t), intent(in) :: namespace
119 class(space_t), intent(in) :: space
120
121 ! Fields
122 type(helmholtz_test_t) :: helm_analytical
123 type(helmholtz_test_t) :: helm_comp_test_1
124 type(helmholtz_test_t) :: helm_comp_test_2
125 ! Vector potential computed from the magnetic field. Surface correction is never needed for this test
126 type(helmholtz_test_t) :: helm_comp_vec_pot_from_trans_field
127
128 character(len=20) :: test_case
129
130 push_sub(hertzian_dipole_test)
131 assert(space%dim == 3)
132
133 ! Allocate and initialize the fields
134 call helm_analytical%init(sys_grid%np_part, sys_grid%box%dim)
135 call helm_comp_test_1%init(sys_grid%np_part, sys_grid%box%dim)
136 call helm_comp_vec_pot_from_trans_field%init(sys_grid%np_part, sys_grid%box%dim)
137 ! Define the field to be tested (Hertzian dipole).
138 ! Reference: The Helmholtz Decomposition and the Coulomb Gauge, Kirk T. McDonald, Joseph Henry Laboratories, Princeton (2008)
139 ! http://kirkmcd.princeton.edu/examples/helmholtz.pdf
140 call init_hertzian_dipole_field(helm_analytical, sys_grid)
141 call io_mkdir("hertzian_dipole_test", namespace=namespace)
142 ! Output analytical fields
143 call io_mkdir("hertzian_dipole_test/exact_fields", namespace=namespace)
144 call output_fields(sys_grid, helm_analytical, space, namespace, "hertzian_dipole_test/exact_fields")
145
146 ! TEST 1
147 ! Compute the transverse and longitudinal fields numerically
148 call helmholtz%get_trans_field(namespace, helm_comp_test_1%trans_field, total_field=helm_analytical%total_field, &
149 apply_boundary=.false.)
150 call helmholtz%get_long_field(namespace, helm_comp_test_1%long_field, total_field=helm_analytical%total_field, &
151 apply_boundary=.false.)
152 ! Compute the scalar and vector potential numerically
153 call helmholtz%get_vector_potential(namespace, helm_comp_test_1%vector_potential, total_field=helm_analytical%total_field, &
154 apply_boundary=.false.)
155 call helmholtz%get_scalar_potential(namespace, helm_comp_test_1%scalar_potential, helm_analytical%total_field, &
156 apply_boundary=.false.)
157 ! Output fields
158 test_case = "no_surf_corr"
159 if (helmholtz%compute_surface_correction) test_case = "with_surf_corr"
160 call io_mkdir("hertzian_dipole_test/" // trim(test_case), namespace=namespace)
161 call output_fields(sys_grid, helm_comp_test_1, space, namespace, "hertzian_dipole_test/" // trim(test_case))
162 call compute_norms(helmholtz, sys_grid, helm_analytical, helm_comp_test_1, namespace, "hertzian_dipole_test/"//trim(test_case))
164 ! TEST VECTOR POTENTIAL COMPUTED FROM THE MAGNETIC FIELD
165 call helmholtz%get_vector_potential(namespace, helm_comp_vec_pot_from_trans_field%vector_potential, &
166 trans_field=helm_analytical%trans_field, apply_boundary=.true.)
167 ! Output fields
168 test_case = "vec_pot_from_b_field"
169 call io_mkdir("hertzian_dipole_test/" // trim(test_case), namespace=namespace)
170 call output_fields(sys_grid, helm_comp_vec_pot_from_trans_field, space, namespace, "hertzian_dipole_test/" // trim(test_case))
171 call compute_norms(helmholtz, sys_grid, helm_analytical, helm_comp_vec_pot_from_trans_field, namespace, &
172 "hertzian_dipole_test/"//trim(test_case), apply_stencil_for_vec_pot=.true.)
173
174 ! TEST 2
175 ! If the TEST 1 was done with surface correction, then perform a second one without surface correction. This allows to test
176 ! that the surface correction actualy brings some improvements
177 if (helmholtz%compute_surface_correction) then
178 helmholtz%compute_surface_correction = .false.
179 call helm_comp_test_2%init(sys_grid%np_part, sys_grid%box%dim)
180 ! Compute the transverse and longitudinal fields numerically
181 call helmholtz%get_trans_field(namespace, helm_comp_test_2%trans_field, total_field=helm_analytical%total_field, &
182 apply_boundary=.false.)
183 call helmholtz%get_long_field(namespace, helm_comp_test_2%long_field, total_field=helm_analytical%total_field, &
184 apply_boundary=.false.)
185 ! Compute the scalar and vector potential numerically
186 call helmholtz%get_vector_potential(namespace, helm_comp_test_2%vector_potential, trans_field=helm_analytical%trans_field, &
187 apply_boundary=.false.)
188 call helmholtz%get_scalar_potential(namespace, helm_comp_test_2%scalar_potential, helm_analytical%total_field, &
189 apply_boundary=.false.)
190 ! Output fields
191 test_case = "no_surf_corr"
192 call io_mkdir("hertzian_dipole_test/" // trim(test_case), namespace=namespace)
193 call output_fields(sys_grid, helm_comp_test_2, space, namespace, "hertzian_dipole_test/" // trim(test_case))
194 call compute_norms(helmholtz,sys_grid,helm_analytical, helm_comp_test_2, namespace, "hertzian_dipole_test/"//trim(test_case))
195 ! After the test is done, reset the compute_surface_correction flag
196 helmholtz%compute_surface_correction = .true.
197 ! If we are here, we did a test with surface correction and one without. So we should test that with surface correction
198 ! we got better results
199 call compare_norms(helm_comp_test_1, helm_comp_test_2, namespace, "hertzian_dipole_test/")
200 end if
201
202 pop_sub(hertzian_dipole_test)
203 end subroutine hertzian_dipole_test
204
205 subroutine init_hertzian_dipole_field(this, sys_grid)
206 class(helmholtz_test_t), intent(inout) :: this
207 type(grid_t), intent(in) :: sys_grid
209 real(real64) :: lambda, omega, kk, rr, time, norm
210 real(real64) :: xx(sys_grid%box%dim), xx_origin(sys_grid%box%dim), pp(sys_grid%box%dim), cross_p(sys_grid%box%dim)
211 integer :: ip
212#ifndef NDEBUG
213 real(real64), parameter :: nearly_zero = 1e-14_real64
214#endif
215
217
218 ! Parameters that define the dipole
219 lambda = 1.55_real64
220 omega = m_two * m_pi * (p_c / lambda)
221 kk = m_two * m_pi / lambda
222 time = m_zero
223 pp = cos(omega * time)
224 xx_origin(2:sys_grid%box%dim) = m_zero
225 xx_origin(1) = 3.11_real64
226
227 do ip = 1, sys_grid%np_part
228 call mesh_r(sys_grid, ip, rr, coords = xx, origin = xx_origin)
229 if (rr >= sys_grid%spacing(1)) then
230 xx = xx / rr
231 ! Longitudinal field - equation 31 of reference paper
232 this%long_field(ip, :) = (m_three * dot_product(pp, xx) * xx - pp) * cos(omega*time)/(rr**3)
233 ! Transverse field - equation 32 of reference paper
234 cross_p = dcross_product(xx, pp)
235 this%trans_field(ip, :) = (kk**2) * cross_p * (cos(kk*rr - omega*time)/rr - sin(kk*rr - omega*time)/(kk * rr**2))
236 ! Vector potential - equation 53 of reference paper
237 this%vector_potential(ip, :) = kk * (pp - dot_product(pp, xx)*xx) * sin(kk*rr - omega*time)/rr
238 this%vector_potential(ip, :) = this%vector_potential(ip, :) * (pp - m_three*dot_product(pp, xx)*xx) * &
239 (cos(kk*rr - omega*time)/(rr**2) - (sin(kk*rr - omega*time) - sin(omega*time))/(kk * rr**3))
240 ! Scalar potential - equation 51 of reference paper
241 this%scalar_potential(ip) = dot_product(pp, xx) * cos(omega*time) / (rr**2)
242 ! Total field
243 this%total_field = this%trans_field + this%long_field
244 end if
245 end do
246 ! Check that the exact fields ere defined correctly
247 norm = dmf_nrm2(sys_grid, sys_grid%box%dim, this%total_field - this%trans_field - this%long_field)
248 assert(norm < nearly_zero)
249
251 end subroutine init_hertzian_dipole_field
252
253 subroutine gaussian_test(helmholtz, sys_grid, namespace, space)
254 class(helmholtz_decomposition_t), intent(inout) :: helmholtz
255 type(grid_t), intent(in) :: sys_grid
256 type(namespace_t), intent(in) :: namespace
257 class(space_t), intent(in) :: space
258
259 ! Fields
260 type(helmholtz_test_t) :: helm_analytical
261 type(helmholtz_test_t) :: helm_comp_test_1
262 type(helmholtz_test_t) :: helm_comp_test_2
263 ! Vector potential computed from the magnetic field. Surface correction is never needed for this test
264 type(helmholtz_test_t) :: helm_comp_vec_pot_from_trans_field
265
266 character(len=20) :: test_case
267
268 push_sub(gaussian_test)
269 assert(space%dim == 3)
270
271 ! Allocate and initialize the fields
272 call helm_analytical%init(sys_grid%np_part, sys_grid%box%dim)
273 call helm_comp_test_1%init(sys_grid%np_part, sys_grid%box%dim)
274 call helm_comp_vec_pot_from_trans_field%init(sys_grid%np_part, sys_grid%box%dim)
275
276 ! Define the field to be tested
277 ! https://gitlab.com/octopus-code/octopus-hackathons/-/blob/main/01-full-minimal-coupling-2022-07-04-2022-07-08/documentation/francesco/Helmholtz_Decomposition_Coulomb_Gauge.pdf
278 call init_gaussian_field(helm_analytical, sys_grid)
279 call io_mkdir("gaussian_field_test", namespace=namespace)
280 ! Output analytical fields
281 call io_mkdir("gaussian_field_test/exact_fields", namespace=namespace)
282 call output_fields(sys_grid, helm_analytical, space, namespace, "gaussian_field_test/exact_fields")
283
284 ! TEST 1
285 ! Compute the transverse and longitudinal fields numerically
286 call helmholtz%get_trans_field(namespace, helm_comp_test_1%trans_field, total_field=helm_analytical%total_field, &
287 apply_boundary=.false.)
288 call helmholtz%get_long_field(namespace, helm_comp_test_1%long_field, total_field=helm_analytical%total_field, &
289 apply_boundary=.false.)
290 ! Compute the scalar and vector potential numerically
291 call helmholtz%get_vector_potential(namespace, helm_comp_test_1%vector_potential, total_field=helm_analytical%total_field, &
292 apply_boundary=.false.)
293 call helmholtz%get_scalar_potential(namespace, helm_comp_test_1%scalar_potential, helm_analytical%total_field, &
294 apply_boundary=.false.)
295 ! Output fields
296 test_case = "no_surf_corr"
297 if (helmholtz%compute_surface_correction) test_case = "with_surf_corr"
298 call io_mkdir("gaussian_field_test/" // trim(test_case), namespace=namespace)
299 call output_fields(sys_grid, helm_comp_test_1, space, namespace, "gaussian_field_test/" // trim(test_case))
300 call compute_norms(helmholtz, sys_grid, helm_analytical, helm_comp_test_1, namespace, "gaussian_field_test/"//trim(test_case))
301
302 ! TEST VECTOR POTENTIAL COMPUTED FROM THE MAGNETIC FIELD
303 call helmholtz%get_vector_potential(namespace, helm_comp_vec_pot_from_trans_field%vector_potential, &
304 trans_field=helm_analytical%trans_field, apply_boundary=.true.)
305 ! Output fields
306 test_case = "vec_pot_from_b_field"
307 call io_mkdir("gaussian_field_test/" // trim(test_case), namespace=namespace)
308 call output_fields(sys_grid, helm_comp_vec_pot_from_trans_field, space, namespace, "gaussian_field_test/" // trim(test_case))
309 call compute_norms(helmholtz, sys_grid, helm_analytical, helm_comp_vec_pot_from_trans_field, namespace, &
310 "gaussian_field_test/"//trim(test_case), apply_stencil_for_vec_pot=.true.)
311
312 ! TEST 2
313 ! If the TEST 1 was done with surface correction, then perform a second one without surface correction. This allows to test
314 ! that the surface correction actualy brings some improvements
315 if (helmholtz%compute_surface_correction) then
316 helmholtz%compute_surface_correction = .false.
317 call helm_comp_test_2%init(sys_grid%np_part, sys_grid%box%dim)
318 ! Compute the transverse and longitudinal fields numerically
319 call helmholtz%get_trans_field(namespace, helm_comp_test_2%trans_field, total_field=helm_analytical%total_field, &
320 apply_boundary=.false.)
321 call helmholtz%get_long_field(namespace, helm_comp_test_2%long_field, total_field=helm_analytical%total_field, &
322 apply_boundary=.false.)
323 ! Compute the scalar and vector potential numerically
324 call helmholtz%get_vector_potential(namespace, helm_comp_test_2%vector_potential, helm_analytical%total_field, &
325 apply_boundary=.false.)
326 call helmholtz%get_scalar_potential(namespace, helm_comp_test_2%scalar_potential, helm_analytical%total_field, &
327 apply_boundary=.false.)
328 ! Output fields
329 test_case = "no_surf_corr"
330 call io_mkdir("gaussian_field_test/" // trim(test_case), namespace=namespace)
331 call output_fields(sys_grid, helm_comp_test_2, space, namespace, "gaussian_field_test/" // trim(test_case))
332 call compute_norms(helmholtz,sys_grid, helm_analytical, helm_comp_test_2, namespace, "gaussian_field_test/"//trim(test_case))
333 ! After the test is done, reset the compute_surface_correction flag
334 helmholtz%compute_surface_correction = .true.
335 ! If we are here, we did a test with surface correction and one without. So we should test that with surface correction
336 ! we got better results
337 call compare_norms(helm_comp_test_1, helm_comp_test_2, namespace, "gaussian_field_test/")
338 end if
339
340 pop_sub(gaussian_test)
341 end subroutine gaussian_test
342
343 subroutine init_gaussian_field(this, sys_grid)
344 class(helmholtz_test_t), intent(inout) :: this
345 type(grid_t), intent(in) :: sys_grid
347 real(real64) :: alpha, beta, a_prefactor, b_prefactor, ra, rb, norm
348 real(real64) :: aa(sys_grid%box%dim), a_origin(sys_grid%box%dim), bb(sys_grid%box%dim), b_origin(sys_grid%box%dim)
349 integer :: ip
350
351 push_sub(init_gaussian_field)
352
353 ! Parameters that define the gaussian
354 alpha = 0.3_real64
355 a_prefactor = 1.5_real64
356 a_origin = m_one / 10
357 beta = 0.3_real64
358 b_prefactor = 1.7_real64
359 b_origin = -m_one / 10
360
361 ! Define the field to be tested
362 do ip = 1, sys_grid%np_part
363 call mesh_r(sys_grid, ip, ra, coords = aa, origin = a_origin)
364 ! Longitudinal field
365 this%long_field(ip, :) = (2 * a_prefactor / (alpha**2)) * aa(:) * exp(-(ra / alpha)**2)
366 ! Analytical scalar potential
367 this%scalar_potential(ip) = a_prefactor * exp(-(ra / alpha)**2)
368
369 call mesh_r(sys_grid, ip, rb, coords = bb, origin = b_origin)
370 ! Transverse field
371 this%trans_field(ip, 1) = - (2 * b_prefactor / (beta**2)) * bb(1) * bb(3) * exp(-(rb / beta)**2)
372 this%trans_field(ip, 2) = - (2 * b_prefactor / (beta**2)) * bb(2) * bb(3) * exp(-(rb / beta)**2)
373 this%trans_field(ip, 3) = (2 * b_prefactor / (beta**2)) * (bb(1)**2 + bb(2)**2 - beta**2) * exp(-(rb / beta)**2)
374 ! Analytical vector potential
375 this%vector_potential(ip, 1) = b_prefactor * bb(2) * exp(-(rb / beta)**2)
376 this%vector_potential(ip, 2) = - b_prefactor * bb(1) * exp(-(rb / beta)**2)
377
378 ! Total field
379 this%total_field(ip, :) = this%long_field(ip, :) + this%trans_field(ip, :)
380 end do
381
382 ! Check that the exact fields ere defined correctly
383 norm = dmf_nrm2(sys_grid, sys_grid%box%dim, this%total_field - this%trans_field - this%long_field)
384 assert(norm < m_epsilon)
385
386 pop_sub(init_gaussian_field)
387 end subroutine init_gaussian_field
388
389 subroutine output_fields(sys_grid, test, space, namespace, path)
390 type(grid_t), intent(in) :: sys_grid
391 type(helmholtz_test_t), intent(in) :: test
392 class(space_t), intent(in) :: space
393 type(namespace_t), intent(in) :: namespace
394 character(len=*), intent(in) :: path
395
396 integer :: ierr
397
398 push_sub(output_fields)
399
400 ! Output the values of the fields on the grid
401 call io_function_output_vector (io_function_fill_how('PlaneZ'), ".", trim(path) // "/total_field", namespace, space, &
402 sys_grid, test%total_field, unit_one, ierr)
403 call io_function_output_vector (io_function_fill_how('PlaneZ'), ".", trim(path) // "/trans_field", namespace, space, &
404 sys_grid, test%trans_field, unit_one, ierr)
405 call io_function_output_vector (io_function_fill_how('PlaneZ'), ".", trim(path) // "/long_field", namespace, space, &
406 sys_grid, test%long_field, unit_one, ierr)
407 call io_function_output_vector (io_function_fill_how('PlaneZ'), ".", trim(path) // "/vector_potential", namespace, space, &
408 sys_grid, test%vector_potential, unit_one, ierr)
409 call dio_function_output (io_function_fill_how('PlaneZ'), ".", trim(path) // "/scalar_potential", namespace, space, &
410 sys_grid, test%scalar_potential, unit_one, ierr)
411
412 pop_sub(output_fields)
413 end subroutine output_fields
414
415 subroutine compute_norms(helmholtz, sys_grid, analytical, test, namespace, path, apply_stencil_for_vec_pot)
416 type(helmholtz_decomposition_t), intent(inout) :: helmholtz
417 type(grid_t), intent(in) :: sys_grid
418 type(helmholtz_test_t), intent(in) :: analytical
419 type(helmholtz_test_t), intent(inout) :: test
420 type(namespace_t), intent(in) :: namespace
421 character(len=*), intent(in) :: path
422 logical, optional, intent(in) :: apply_stencil_for_vec_pot
423
424 integer :: iunit, dim, np
425 real(real64), allocatable :: local_field(:,:)
426
427 push_sub(compute_norms)
428 safe_allocate(local_field(1:sys_grid%np, 1:sys_grid%box%dim))
429
430 dim = sys_grid%box%dim ! All boxes have the same dimension
431 np = sys_grid%np
432
433 ! First, compute the norms
434 ! Transverse field
435 local_field = analytical%trans_field(1:np,:)
436 call helmholtz%apply_inner_stencil_mask(local_field)
437 test%abs_norm_trans_field = dmf_nrm2(sys_grid, dim, test%trans_field(1:np,:) - local_field(1:np,:))
438 test%rel_norm_trans_field = test%abs_norm_trans_field / dmf_nrm2(sys_grid, dim, local_field(1:np, :))
439 ! Longitudinal field
440 local_field = analytical%long_field(1:np,:)
441 call helmholtz%apply_inner_stencil_mask(local_field)
442 test%abs_norm_long_field = dmf_nrm2(sys_grid, dim, test%long_field(1:np, :) - local_field(1:np, :))
443 test%rel_norm_long_field = test%abs_norm_long_field / dmf_nrm2(sys_grid, dim, local_field(1:np, :))
444 ! Vector potential
445 local_field = analytical%vector_potential(1:np,:)
446 if (optional_default(apply_stencil_for_vec_pot, .false.)) then
447 call helmholtz%apply_inner_stencil_mask(local_field)
448 end if
449 test%abs_norm_vec_pot = dmf_nrm2(sys_grid, dim, test%vector_potential(1:np,:) - local_field(1:np,:))
450 test%rel_norm_vec_pot = test%abs_norm_vec_pot / dmf_nrm2(sys_grid, dim, local_field(1:np, :))
451 ! Scalar potential
452 test%abs_norm_scal_pot = dmf_nrm2(sys_grid, test%scalar_potential(1:np) - analytical%scalar_potential(1:np))
453 test%rel_norm_scal_pot = test%abs_norm_scal_pot / dmf_nrm2(sys_grid, analytical%scalar_potential(1:np))
454 ! Self consistency
455 local_field = analytical%total_field(1:np,:)
456 call helmholtz%apply_inner_stencil_mask(local_field)
457 test%abs_norm_self_consistency = dmf_nrm2(sys_grid,dim,local_field(1:np,:)-test%trans_field(1:np,:)-test%long_field(1:np,:))
458 test%rel_norm_self_consistency = test%abs_norm_self_consistency / dmf_nrm2(sys_grid, dim, local_field(1:np, :))
459
460 safe_deallocate_a(local_field)
461
462 ! Output the norms to file
463 iunit = io_open(path // "/norms", namespace, action='write')
464 write(iunit, '(a,f19.13)') 'Transverse field test (abs.). = ', test%abs_norm_trans_field
465 write(iunit, '(a,f19.13)') 'Transverse field test (rel.). = ', test%rel_norm_trans_field
466 write(iunit, '(a,f19.13)') 'Longitudinal field test (abs.). = ', test%abs_norm_long_field
467 write(iunit, '(a,f19.13)') 'Longitudinal field test (rel.). = ', test%rel_norm_long_field
468 write(iunit, '(a,f19.13)') 'Vector potential test (abs.). = ', test%abs_norm_vec_pot
469 write(iunit, '(a,f19.13)') 'Vector potential test (rel.). = ', test%rel_norm_vec_pot
470 write(iunit, '(a,f19.13)') 'Scalar potential test (abs.). = ', test%abs_norm_scal_pot
471 write(iunit, '(a,f19.13)') 'Scalar potential test (rel.). = ', test%rel_norm_scal_pot
472 write(iunit, '(a,f19.13)') 'Self consistency test (abs.). = ', test%abs_norm_self_consistency
473 write(iunit, '(a,f19.13)') 'Self consistency test (rel.). = ', test%rel_norm_self_consistency
474
475 call io_close(iunit)
476
477 pop_sub(compute_norms)
478 end subroutine compute_norms
479
480 subroutine compare_norms(test_with_sc, test_no_sc, namespace, path)
481 type(helmholtz_test_t), intent(in) :: test_with_sc
482 type(helmholtz_test_t), intent(in) :: test_no_sc
483 type(namespace_t), intent(in) :: namespace
484 character(len=*), intent(in) :: path
485
486 integer :: iunit
487 character(len=24), parameter :: form = '(a,f19.13,a,f19.13)'
488
489 push_sub(compare_norms)
490
491 ! Output the norms to file
492 iunit = io_open(path // "norm_comparison", namespace, action='write')
493 ! Trans field
494 write(iunit, form) 'Transverse field test (abs.). No surf correction = ', test_no_sc%abs_norm_trans_field, &
495 " ;With surf correction = ", test_with_sc%abs_norm_trans_field
496 write(iunit, form) 'Transverse field test (rel.). No surf correction = ', test_no_sc%rel_norm_trans_field, &
497 " ;With surf correction = ", test_with_sc%rel_norm_trans_field
498 ! Long field
499 write(iunit, form) 'Longitudinal field test (abs.). No surf correction = ', test_no_sc%abs_norm_long_field, &
500 " ;With surf correction = ", test_with_sc%abs_norm_long_field
501 write(iunit, form) 'Longitudinal field test (rel.). No surf correction = ', test_no_sc%rel_norm_long_field, &
502 " ;With surf correction = ", test_with_sc%rel_norm_long_field
503 ! Vector potential
504 write(iunit, form) 'Vector potential test (abs.). No surf correction = ', test_no_sc%abs_norm_vec_pot, &
505 " ;With surf correction = ", test_with_sc%abs_norm_vec_pot
506 write(iunit, form) 'Vector potential test (rel.). No surf correction = ', test_no_sc%rel_norm_vec_pot, &
507 " ;With surf correction = ", test_with_sc%rel_norm_vec_pot
508 ! Scalar potential
509 write(iunit, form) 'Scalar potential test (abs.). No surf correction = ', test_no_sc%abs_norm_scal_pot, &
510 " ;With surf correction = ", test_with_sc%abs_norm_scal_pot
511 write(iunit, form) 'Scalar potential test (rel.). No surf correction = ', test_no_sc%rel_norm_scal_pot, &
512 " ;With surf correction = ", test_with_sc%rel_norm_scal_pot
513 ! Self consistency
514 write(iunit, form) 'Self consistency test (abs.). No surf correction = ', test_no_sc%abs_norm_self_consistency, &
515 " ;With surf correction = ", test_with_sc%abs_norm_self_consistency
516 write(iunit, form) 'Self consistency test (rel.). No surf correction = ', test_no_sc%rel_norm_self_consistency, &
517 " ;With surf correction = ", test_with_sc%rel_norm_self_consistency
518 call io_close(iunit)
519
520 ! Trans field
521 assert(test_with_sc%abs_norm_trans_field < test_no_sc%abs_norm_trans_field)
522 assert(test_with_sc%rel_norm_trans_field < test_no_sc%rel_norm_trans_field)
523 ! Long field
524 assert(test_with_sc%abs_norm_long_field < test_no_sc%abs_norm_long_field)
525 assert(test_with_sc%rel_norm_long_field < test_no_sc%rel_norm_long_field)
526 ! Vector potential
527 assert(test_with_sc%abs_norm_vec_pot < test_no_sc%abs_norm_vec_pot)
528 assert(test_with_sc%rel_norm_vec_pot < test_no_sc%rel_norm_vec_pot)
529 ! Scalar potential
530 assert(test_with_sc%abs_norm_scal_pot < test_no_sc%abs_norm_scal_pot)
531 assert(test_with_sc%rel_norm_scal_pot < test_no_sc%rel_norm_scal_pot)
532 ! Self Consistency
533 assert(test_with_sc%abs_norm_self_consistency < test_no_sc%abs_norm_self_consistency)
534 assert(test_with_sc%rel_norm_self_consistency < test_no_sc%rel_norm_self_consistency)
535
536 pop_sub(compare_norms)
537 end subroutine compare_norms
538
539#include "undef.F90"
540#include "complex.F90"
541
542#include "undef.F90"
543#include "real.F90"
544
546
547!! Local Variables:
548!! mode: f90
549!! coding: utf-8
550!! End:
double exp(double __x) __attribute__((__nothrow__
double sin(double __x) __attribute__((__nothrow__
double cos(double __x) __attribute__((__nothrow__
This module contains interfaces for BLAS routines You should not use these routines directly....
Definition: blas.F90:118
Module, implementing a factory for boxes.
This module calculates the derivatives (gradients, Laplacians, etc.) of a function.
real(real64), parameter, public m_two
Definition: global.F90:190
real(real64), parameter, public m_zero
Definition: global.F90:188
real(real64), parameter, public m_pi
some mathematical constants
Definition: global.F90:186
real(real64), parameter, public m_epsilon
Definition: global.F90:204
real(real64), parameter, public p_c
Electron gyromagnetic ratio, see Phys. Rev. Lett. 130, 071801 (2023)
Definition: global.F90:224
real(real64), parameter, public m_one
Definition: global.F90:189
real(real64), parameter, public m_three
Definition: global.F90:191
This module implements the underlying real-space grid.
Definition: grid.F90:117
The Helmholtz decomposition is intended to contain "only mathematical" functions and procedures to co...
Test suit for the Helmholtz decomposition module.
subroutine compare_norms(test_with_sc, test_no_sc, namespace, path)
subroutine helmholtz_test_init(this, np_part, dim)
subroutine init_gaussian_field(this, sys_grid)
subroutine, public gaussian_test(helmholtz, sys_grid, namespace, space)
subroutine init_hertzian_dipole_field(this, sys_grid)
subroutine compute_norms(helmholtz, sys_grid, analytical, test, namespace, path, apply_stencil_for_vec_pot)
subroutine, public hertzian_dipole_test(helmholtz, sys_grid, namespace, space)
subroutine output_fields(sys_grid, test, space, namespace, path)
subroutine, public dio_function_output(how, dir, fname, namespace, space, mesh, ff, unit, ierr, pos, atoms, grp, root)
Top-level IO routine for functions defined on the mesh.
integer(int64) function, public io_function_fill_how(where)
Use this function to quickly plot functions for debugging purposes: call dio_function_output(io_funct...
Definition: io.F90:114
subroutine, public io_close(iunit, grp)
Definition: io.F90:418
subroutine, public io_mkdir(fname, namespace, parents)
Definition: io.F90:311
integer function, public io_open(file, namespace, action, status, form, position, die, recl, grp)
Definition: io.F90:352
This module is intended to contain "only mathematical" functions and procedures.
Definition: math.F90:115
pure real(real64) function, dimension(1:3), public dcross_product(a, b)
Definition: math.F90:1859
This module defines various routines, operating on mesh functions.
This module defines the meshes, which are used in Octopus.
Definition: mesh.F90:118
pure subroutine, public mesh_r(mesh, ip, rr, origin, coords)
return the distance to the origin for a given grid point
Definition: mesh.F90:336
This module handles the communicators for the various parallelization strategies.
Definition: multicomm.F90:145
brief This module defines the class unit_t which is used by the unit_systems_oct_m module.
Definition: unit.F90:132
This module defines the unit system, used for input and output.
type(unit_t), public unit_one
some special units required for particular quantities
Description of the grid, containing information on derivatives, stencil, and symmetries.
Definition: grid.F90:168
int true(void)