Octopus
absorbing_boundaries.F90
Go to the documentation of this file.
1!! Copyright (C) 2015 U. De Giovannini
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#include "global.h"
19
21 use box_oct_m
26 use debug_oct_m
27 use global_oct_m
28 use grid_oct_m
30 use, intrinsic :: iso_fortran_env
31 use mesh_oct_m
34 use parser_oct_m
36 use space_oct_m
37 use unit_oct_m
40
41 implicit none
42
43 private
44 public :: &
48
50 private
51 integer, public :: abtype
52 real(real64), allocatable, public :: mf(:)
53 type(cube_function_t) :: cf
54 logical :: ab_user_def
55 real(real64), allocatable :: ab_ufn(:)
57
58 integer, public, parameter :: &
59 NOT_ABSORBING = 0, &
60 mask_absorbing = 1, &
62
63contains
64
65 ! ---------------------------------------------------------
66 subroutine absorbing_boundaries_init(this, namespace, space, gr)
67 type(absorbing_boundaries_t), intent(out) :: this
68 type(namespace_t), intent(in) :: namespace
69 class(space_t), intent(in) :: space
70 type(grid_t), intent(in) :: gr
71
72 integer :: ip
73 real(real64) :: bounds(space%dim, 2)
74 integer :: cols_abshape_block, imdim, maxdim
75
76 real(real64) :: xx(space%dim), rr
77 real(real64) :: ufn_re, ufn_im
78 character(len=1024) :: user_def_expr
79
80 real(real64), allocatable :: mf(:)
81 real(real64) :: abheight, abwidth, abwidth_def
82 type(block_t) :: blk
83
85
86 bounds = m_zero
87
88 this%ab_user_def = .false.
89
90 !%Variable AbsorbingBoundaries
91 !%Type integer
92 !%Default not_absorbing
93 !%Section Time-Dependent::Absorbing Boundaries
94 !%Description
95 !% To improve the quality of the spectra by avoiding the formation of
96 !% standing density waves, one can make the boundaries of the simulation
97 !% box absorbing.
98 !%Option not_absorbing 0
99 !% Reflecting boundaries.
100 !%Option mask 1
101 !% Absorbing boundaries with a mask function.
102 !%Option cap 2
103 !% Absorbing boundaries with a complex absorbing potential.
104 !%End
105 call parse_variable(namespace, 'AbsorbingBoundaries', not_absorbing, this%abtype)
106 if (.not. varinfo_valid_option('AbsorbingBoundaries', this%abtype)) then
107 call messages_input_error(namespace, 'AbsorbingBoundaries')
108 end if
109
110 if (this%abtype /= not_absorbing) then
111 call messages_print_with_emphasis(msg='Absorbing Boundaries', namespace=namespace)
112
113 !%Variable ABCapHeight
114 !%Type float
115 !%Default -0.2 a.u.
116 !%Section Time-Dependent::Absorbing Boundaries
117 !%Description
118 !% When <tt>AbsorbingBoundaries = cap</tt>, this is the height of the imaginary potential.
119 !%End
120 if (this%abtype == imaginary_absorbing) then
121 call parse_variable(namespace, 'ABCapHeight', -0.2_real64, abheight, units_inp%energy)
122 end if
123
124 !%Variable ABShape
125 !%Type block
126 !%Section Time-Dependent::Absorbing Boundaries
127 !%Description
128 !% Set the shape of the absorbing boundaries. Here you can set the inner
129 !% and outer bounds by setting the block as follows:
130 !%
131 !% <tt>%ABShape
132 !% <br>&nbsp;&nbsp; inner | outer | "user-defined"
133 !% <br>%</tt>
134 !%
135 !% The optional 3rd column is a user-defined expression for the absorbing
136 !% boundaries. For example, <math>r</math> creates a spherical absorbing zone for
137 !% coordinates with <math>{\tt inner} < r < {\tt outer}</math>, and <math>z</math> creates an
138 !% absorbing plane.
139 !% Note, values <tt>outer</tt> larger than the box size may lead in these cases to
140 !% unexpected reflection behaviours.
141 !% If no expression is given, the absorbing zone follows the edges of the
142 !% box (not valid for user-defined box).
143 !%End
145 cols_abshape_block = 0
146 if (parse_block(namespace, 'ABShape', blk) < 0) then
147 message(1) = "Input: ABShape not specified. Using default values for absorbing boundaries."
148 call messages_info(1, namespace=namespace)
150 select type (sb=>gr%box)
151 type is (box_sphere_t)
152 bounds(1,1) = sb%radius/m_two
153 bounds(1,2) = sb%radius
154 type is (box_parallelepiped_t)
155 bounds(:,1) = sb%half_length/m_two
156 bounds(:,2) = sb%half_length
157 type is (box_cylinder_t)
158 bounds(1,2) = sb%half_length
159 bounds(2,2) = sb%radius
160 bounds(1,1) = bounds(1,1)/m_two
161 bounds(2,1) = bounds(2,2)/m_two
162 class default
163 bounds(:,1) = m_zero
164 bounds(:,2) = 0.4_real64
165 end select
166 else
167 cols_abshape_block = parse_block_cols(blk, 0)
168
169 select case (cols_abshape_block)
170 case (2)
171 call parse_block_float(blk, 0, 0, bounds(1,1), units_inp%length)
172 call parse_block_float(blk, 0, 1, bounds(1,2), units_inp%length)
173 ! Apply single inner/outer pair to all dimensions.
174 bounds(:,1) = bounds(1,1)
175 bounds(:,2) = bounds(1,2)
176
177 select type (sb=>gr%box)
178 type is (box_sphere_t)
179 if (bounds(1,2) > sb%radius) then
180 bounds(1,2) = sb%radius
181 end if
182 message(1) = "Info: using spherical absorbing boundaries."
183
184 type is (box_parallelepiped_t)
185 do imdim = 1, space%dim
186 if (bounds(imdim,2) > sb%half_length(imdim)) then
187 bounds(imdim,2) = sb%half_length(imdim)
188 end if
189 end do
190 message(1) = "Info: using cubic absorbing boundaries."
191
192 type is (box_cylinder_t)
193 if (bounds(1,2) > sb%half_length) then
194 bounds(1,2) = sb%half_length
195 end if
196 if (bounds(2,2) > sb%radius) then
197 bounds(2,2) = sb%radius
198 end if
199 message(1) = "Info: using cylindrical absorbing boundaries."
200 end select
201 call messages_info(1, namespace=namespace)
202
203 case (3)
204 this%ab_user_def = .true.
205 safe_allocate(this%ab_ufn(1:gr%np))
206 this%ab_ufn = m_zero
207 call parse_block_float( blk, 0, 0, bounds(1,1), units_inp%length)
208 call parse_block_float( blk, 0, 1, bounds(1,2), units_inp%length)
209 call parse_block_string(blk, 0, 2, user_def_expr)
210 do ip = 1, gr%np
211 xx = units_from_atomic(units_inp%length, gr%x(:, ip))
212 rr = norm2(xx)
213 call parse_expression(ufn_re, ufn_im, space%dim, xx, rr, m_zero, user_def_expr)
214 this%ab_ufn(ip) = ufn_re
215 end do
216 message(1) = "Input: using user-defined function from expression:"
217 write(message(2),'(a,a)') ' F(x,y,z) = ', trim(user_def_expr)
218 call messages_info(2, namespace=namespace)
219 case default
220 message(1) = "Input: ABShape block must have at least 2 columns."
221 call messages_fatal(1, namespace=namespace)
222 end select
223
224 call parse_block_end(blk)
225 end if
226
227 !%Variable ABWidth
228 !%Type float
229 !%Section Time-Dependent::Absorbing Boundaries
230 !%Description
231 !% Specifies the boundary width. For a finer control over the absorbing boundary
232 !% shape use ABShape.
233 !%End
234 abwidth_def = bounds(1,2) - bounds(1,1)
235 call parse_variable(namespace, 'ABWidth', abwidth_def, abwidth, units_inp%length)
236 bounds(:, 1) = bounds(:, 2) - abwidth
237
238 select type (sb=>gr%box)
239 type is (box_sphere_t)
240 maxdim = 1
241 type is (box_cylinder_t)
242 maxdim = 2
243 class default
244 if (this%ab_user_def) then
245 maxdim = 1
246 else
247 maxdim = space%dim
248 end if
249 end select
250 write(message(1),'(a,2a,9f12.6)') &
251 " Absorbing boundary spans from [", trim(units_abbrev(units_inp%length)), '] =', &
252 (units_from_atomic(units_inp%length, bounds(imdim,1)), imdim=1,maxdim)
253 write(message(2),'(a,2a,9f12.6)') &
254 " to [", trim(units_abbrev(units_inp%length)), '] =', &
255 (units_from_atomic(units_inp%length, bounds(imdim,2)), imdim=1,maxdim)
256 call messages_info(2, namespace=namespace)
257
258 ! generate boundary function
259 safe_allocate(mf(1:gr%np))
260 call absorbing_boundaries_generate_mf(this, space, gr, bounds, mf)
261
262 ! mask or cap
263 safe_allocate(this%mf(1:gr%np))
264
265 select case (this%abtype)
266 case (mask_absorbing)
267 this%mf = m_one - mf
269 this%mf(:) = abheight * mf(:)
270 end select
271
272 if (debug%info) call absorbing_boundaries_write_info(this, gr, namespace, space)
273
274 call messages_print_with_emphasis(namespace=namespace)
275
276 safe_deallocate_a(mf)
277 end if
278
280 end subroutine absorbing_boundaries_init
281
282 ! ---------------------------------------------------------
283 subroutine absorbing_boundaries_end(this)
284 type(absorbing_boundaries_t), intent(inout) :: this
285
287
288 if (this%abtype /= not_absorbing) then
289 safe_deallocate_a(this%mf)
290 end if
291
293 end subroutine absorbing_boundaries_end
294
295 ! ---------------------------------------------------------
296 subroutine absorbing_boundaries_write_info(this, mesh, namespace, space)
297 type(absorbing_boundaries_t), intent(in) :: this
298 class(mesh_t), intent(in) :: mesh
299 type(namespace_t), intent(in) :: namespace
300 class(space_t), intent(in) :: space
301
302 integer :: err
303
304 if (space%dim < 3) return
305
307
308 if (this%abtype == mask_absorbing) then
309 call dio_function_output(io_function_fill_how("VTK"), "./td.general", "mask", namespace, space, mesh, &
310 this%mf(1:mesh%np), unit_one, err)
311 call dio_function_output(io_function_fill_how("PlaneZ"), "./td.general", "mask", namespace, space, mesh, &
312 this%mf(1:mesh%np), unit_one, err)
313 else if (this%abtype == imaginary_absorbing) then
314 call dio_function_output(io_function_fill_how("VTK"), "./td.general", "cap", namespace, space, mesh, &
315 this%mf(1:mesh%np), unit_one, err)
316 call dio_function_output(io_function_fill_how("PlaneZ"), "./td.general", "cap", namespace, space, mesh, &
317 this%mf(1:mesh%np), unit_one, err)
318 call dio_function_output(io_function_fill_how("PlaneX"), "./td.general", "cap", namespace, space, mesh, &
319 this%mf(1:mesh%np), unit_one, err)
320 call dio_function_output(io_function_fill_how("PlaneY"), "./td.general", "cap", namespace, space, mesh, &
321 this%mf(1:mesh%np), unit_one, err)
322 end if
323
326
327 ! ---------------------------------------------------------
328 subroutine absorbing_boundaries_generate_mf(this, space, gr, bounds, mf)
329 type(absorbing_boundaries_t), intent(inout) :: this
330 class(space_t), intent(in) :: space
331 type(grid_t), intent(in) :: gr
332 real(real64), intent(in) :: bounds(1:space%dim, 1:2)
333 real(real64), contiguous, intent(inout) :: mf(:)
334
335 integer :: ip, dir
336 real(real64) :: width(space%dim)
337 real(real64) :: xx(space%dim), rr, dd, ddv(space%dim), tmp(space%dim)
338
340
341 mf = m_zero
342
343 ! generate the boundaries on the mesh
344 width = bounds(:, 2) - bounds(:,1)
345
346 do ip = 1, gr%np
347 xx = gr%x(:, ip)
348
349 if (this%ab_user_def) then
350 dd = this%ab_ufn(ip) - bounds(1,1)
351 if (dd > m_zero) then
352 if (this%ab_ufn(ip) < bounds(1,2)) then
353 mf(ip) = sin(dd * m_pi / (m_two * (width(1))))**2
354 else
355 mf(ip) = m_one
356 end if
357 end if
358
359 else ! this%ab_user_def == .false.
360
361 select type (sb => gr%box)
362 type is (box_sphere_t)
363 rr = norm2(xx - sb%center)
364 dd = rr - bounds(1,1)
365 if (dd > m_zero) then
366 if (dd < width(1)) then
367 mf(ip) = sin(dd * m_pi / (m_two * (width(1))))**2
368 else
369 mf(ip) = m_one
370 end if
371 end if
372
373 type is (box_parallelepiped_t)
374 ! We are filling from the center opposite to the spherical case
375 tmp = m_one
376 mf(ip) = m_one
377 ddv = abs(xx - sb%center) - bounds(:, 1)
378 do dir = 1, space%dim
379 if (ddv(dir) > m_zero) then
380 if (ddv(dir) < width(dir)) then
381 tmp(dir) = m_one - sin(ddv(dir) * m_pi / (m_two * (width(dir))))**2
382 else
383 tmp(dir) = m_zero
384 end if
385 end if
386 mf(ip) = mf(ip) * tmp(dir)
387 end do
388 mf(ip) = m_one - mf(ip)
389
390 type is (box_cylinder_t)
391 rr = norm2(xx(2:space%dim) - sb%center(2:space%dim))
392 tmp = m_one
393 mf(ip) = m_one
394 ddv(1) = abs(xx(1) - sb%center(1)) - bounds(1,1)
395 ddv(2) = rr - bounds(2,1)
396 do dir = 1, 2
397 if (ddv(dir) > m_zero) then
398 if (ddv(dir) < width(dir)) then
399 tmp(dir) = m_one - sin(ddv(dir) * m_pi / (m_two * (width(dir))))**2
400 else
401 tmp(dir) = m_zero
402 end if
403 end if
404 mf(ip) = mf(ip) * tmp(dir)
405 end do
406 mf(ip) = m_one - mf(ip)
407
408 class default
409 ! Other box shapes are not implemented
410 assert(.false.)
411 end select
412 end if
413 end do
414
415 if (this%ab_user_def) then
416 safe_deallocate_a(this%ab_ufn)
417 end if
418
421
424!! Local Variables:
425!! mode: f90
426!! coding: utf-8
427!! End:
double sin(double __x) __attribute__((__nothrow__
integer, parameter, public imaginary_absorbing
subroutine absorbing_boundaries_write_info(this, mesh, namespace, space)
integer, parameter, public mask_absorbing
subroutine absorbing_boundaries_generate_mf(this, space, gr, bounds, mf)
subroutine, public absorbing_boundaries_end(this)
subroutine, public absorbing_boundaries_init(this, namespace, space, gr)
type(debug_t), save, public debug
Definition: debug.F90:156
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 implements the underlying real-space grid.
Definition: grid.F90:119
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...
This module defines the meshes, which are used in Octopus.
Definition: mesh.F90:120
subroutine, public messages_print_with_emphasis(msg, iunit, namespace)
Definition: messages.F90:898
character(len=512), private msg
Definition: messages.F90:167
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
subroutine, public parse_block_string(blk, l, c, res, convert_to_c)
Definition: parser.F90:818
integer function, public parse_block(namespace, name, blk, check_varinfo_)
Definition: parser.F90:623
brief This module defines the class unit_t which is used by the unit_systems_oct_m module.
Definition: unit.F90:134
character(len=20) pure function, public units_abbrev(this)
Definition: unit.F90:225
This module defines the unit system, used for input and output.
type(unit_system_t), public units_inp
the units systems for reading and writing
type(unit_t), public unit_one
some special units required for particular quantities
Class implementing a cylinder box. The cylinder axis is always along the first direction defined by t...
Class implementing a parallelepiped box. Currently this is restricted to a rectangular cuboid (all th...
Class implementing a spherical box.
Definition: box_sphere.F90:134
Description of the grid, containing information on derivatives, stencil, and symmetries.
Definition: grid.F90:171
Describes mesh distribution to nodes.
Definition: mesh.F90:187
int true(void)