1
! Copyright (C) 2010 Imperial College London and others.
3
! Please see the AUTHORS file in the main source directory for a full list
4
! of copyright holders.
7
! Applied Modelling and Computation Group
8
! Department of Earth Science and Engineering
9
! Imperial College London
11
! amcgsoftware@imperial.ac.uk
13
! This library is free software; you can redistribute it and/or
14
! modify it under the terms of the GNU Lesser General Public
15
! License as published by the Free Software Foundation,
16
! version 2.1 of the License.
18
! This library is distributed in the hope that it will be useful,
19
! but WITHOUT ANY WARRANTY; without even the implied warranty of
20
! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21
! Lesser General Public License for more details.
23
! You should have received a copy of the GNU Lesser General Public
24
! License along with this library; if not, write to the Free Software
25
! Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
35
use global_parameters, only: OPTION_PATH_LEN
38
use boundary_conditions
40
use transform_elements
42
use sparsity_patterns_meshes
49
public :: calculate_stream_function_multipath_2d
51
type(integer_vector), dimension(:), allocatable, save :: flux_face_list
52
real, dimension(:,:), allocatable, save :: flux_normal
54
integer, save :: last_adapt=-1
58
subroutine find_stream_paths(X, streamfunc)
59
!!< Find the paths through the mesh over which the velocity will be
60
!!< integrated to calculate the flux.
61
type(vector_field), intent(in) :: X
62
type(scalar_field), intent(inout) :: streamfunc
64
type(ilist) :: tmp_face_list
67
character(len=OPTION_PATH_LEN) :: option_path
68
real, dimension(2) :: start, end, dx, face_c
70
integer, dimension(:), pointer :: neigh
71
integer :: face, p, ele1, ele2, e2, i
74
bc_count=get_boundary_condition_count(streamfunc)
76
if(.not.(allocated(flux_face_list))) then
77
allocate(flux_face_list(bc_count))
78
CALL nullify(flux_face_list)
79
allocate(flux_normal(2, bc_count))
83
bc_loop: do i=1, bc_count
84
call get_boundary_condition(streamfunc, i, option_path=option_path)
86
if (have_option(trim(option_path)//"/primary_boundary")) then
87
! No path on the primary boundary.
89
if (associated(flux_face_list(i)%ptr)) then
90
deallocate(flux_face_list(i)%ptr)
92
allocate(flux_face_list(i)%ptr(0))
97
! We must be on a secondary boundary.
98
call get_option(trim(option_path)//"/secondary_boundary/primary_point"&
100
call get_option(trim(option_path)//"/secondary_boundary/secondary_point"&
105
dx2=dot_product(dx,dx)
107
do ele1=1, element_count(streamfunc)
108
neigh=>ele_neigh(X, ele1)
112
! Don't do boundaries
114
! Do each edge only once
117
!for parallel check that we own the node
118
if (.not.element_owned(streamfunc, ele1)) cycle
120
face=ele_face(streamfunc, ele1, ele2)
122
face_c=sum(face_val(X,face),2)/face_loc(X,face)
124
p=dot_product(face_c,dx)/dx2
126
! If the face is not within the limits of the line, don't do it.
127
if (p<0.or.p>1) cycle
129
c1=cross_product2(sum(ele_val(X,ele1),2)/ele_loc(X,ele1)-start, dx)
130
c2=cross_product2(sum(ele_val(X,ele2),2)/ele_loc(X,ele2)-start, dx)
132
if(C1<0 .and. C2>=0) then
134
else if (C1>=0 .and. C2<0) then
140
call insert(tmp_face_list, face)
146
if (associated(flux_face_list(i)%ptr)) then
147
deallocate(flux_face_list(i)%ptr)
149
allocate(flux_face_list(i)%ptr(tmp_face_list%length))
151
flux_face_list(i)%ptr=list2vector(tmp_face_list)
152
call flush_list(tmp_face_list)
154
! Work out the orthonormal to the line.
157
flux_normal(:,i)=(/-dx(2), dx(1)/)
161
end subroutine find_stream_paths
163
function boundary_value(X, U, bc_num)
164
!!< Calculate the value of the streamfunction on the boundary provided
165
!!< by integrating the velocity flux across a line between this
166
!!< boundary and the primary boundary.
167
real :: boundary_value
168
type(vector_field), intent(in) :: X, U
169
integer, intent(in) :: bc_num
175
do i=1, size(flux_face_list(bc_num)%ptr)
176
face=flux_face_list(bc_num)%ptr(i)
178
boundary_value=boundary_value + face_flux(face, X, U, bc_num)
182
! for parallel so each partitition calculates the bit of the flux that it owns and they sum along the boundary so they all have the correct bd. condition
183
call allsum(boundary_value)
187
function face_flux(face, X, U, bc_num)
189
integer, intent(in) :: face, bc_num
190
type(vector_field), intent(in) :: X, U
192
real, dimension(face_ngi(U, face)) :: detwei
193
real, dimension(U%dim,face_ngi(U, face)) :: normal, U_quad
196
call transform_facet_to_physical(X, face, detwei_f=detwei, normal=normal)
198
U_quad=face_val_at_quad(U,face)
202
do gi=1, size(detwei)
203
face_flux=face_flux+abs(dot_product(flux_normal(:,bc_num),normal(:,gi)))&
204
* dot_product(U_quad(:,gi),flux_normal(:,bc_num))&
208
end function face_flux
210
end function boundary_value
212
subroutine calculate_stream_function_multipath_2d(state, streamfunc)
213
!!< Calculate the stream function for a
214
type(state_type), intent(inout) :: state
215
type(scalar_field), intent(inout) :: streamfunc
217
integer :: i, ele, stat
218
type(vector_field), pointer :: X, U
219
type(csr_sparsity), pointer :: psi_sparsity
220
type(csr_matrix) :: psi_mat
221
type(scalar_field) :: rhs
223
type(scalar_field), pointer :: surface_field
225
integer :: mesh_movement
226
integer, save :: last_mesh_movement = -1
228
X => extract_vector_field(state, "Coordinate", stat)
229
if(present_and_nonzero(stat)) return
230
U => extract_vector_field(state, "Velocity", stat)
231
if(present_and_nonzero(stat)) return
234
FLExit("Streamfunction is only valid in 2d")
236
! No discontinuous stream functions.
237
if (continuity(streamfunc)<0) then
238
FLExit("Streamfunction must be a continuous field")
241
if (last_adapt<eventcount(EVENT_ADAPTIVITY)) then
242
last_adapt=eventcount(EVENT_ADAPTIVITY)
243
call find_stream_paths(X, streamfunc)
248
psi_mat = extract_csr_matrix(state, "StreamFunctionMatrix", stat = stat)
250
mesh_movement = eventcount(EVENT_MESH_MOVEMENT)
251
if(mesh_movement /= last_mesh_movement) then
256
psi_sparsity => get_csr_sparsity_firstorder(state, streamfunc%mesh, streamfunc%mesh)
258
call allocate(psi_mat, psi_sparsity, name="StreamFunctionMatrix")
261
call allocate(rhs, streamfunc%mesh, "StreamFunctionRHS")
264
do ele=1, element_count(streamfunc)
266
call calculate_streamfunc_ele(rhs, ele, X, U, psi_mat = psi_mat)
270
call insert(state, psi_mat, psi_mat%name)
274
call allocate(rhs, streamfunc%mesh, "StreamFunctionRHS")
277
do ele=1, element_count(streamfunc)
279
call calculate_streamfunc_ele(rhs, ele, X, U)
283
last_mesh_movement = eventcount(EVENT_MESH_MOVEMENT)
285
do i = 1, get_boundary_condition_count(streamfunc)
287
surface_field=>extract_surface_field(streamfunc, i, "value")
289
flux_val=boundary_value(X,U,i)
291
call set(surface_field, flux_val)
295
call zero(streamfunc)
297
call apply_dirichlet_conditions(psi_mat, rhs, streamfunc)
299
call petsc_solve(streamfunc, psi_mat, rhs)
302
call deallocate(psi_mat)
306
subroutine calculate_streamfunc_ele(rhs, ele, X, U, psi_mat)
307
type(scalar_field), intent(inout) :: rhs
308
type(vector_field), intent(in) :: X,U
309
integer, intent(in) :: ele
310
type(csr_matrix), optional, intent(inout) :: psi_mat
312
! Transformed gradient function for velocity.
313
real, dimension(ele_loc(U, ele), ele_ngi(U, ele), mesh_dim(U)) :: du_t
314
! Ditto for the stream function, psi
315
real, dimension(ele_loc(rhs, ele), ele_ngi(rhs, ele), mesh_dim(rhs))&
318
! Local vorticity_matrix
319
real, dimension(2, ele_loc(rhs, ele), ele_loc(U, ele)) ::&
322
real, dimension(ele_loc(rhs, ele)) :: lvorticity
324
! Variable transform times quadrature weights.
325
real, dimension(ele_ngi(U,ele)) :: detwei
327
type(element_type), pointer :: U_shape, psi_shape
328
integer, dimension(:), pointer :: psi_ele
331
U_shape=> ele_shape(U, ele)
332
psi_shape=> ele_shape(rhs, ele)
333
psi_ele=>ele_nodes(rhs, ele)
335
! Transform U derivatives and weights into physical space.
336
call transform_to_physical(X, ele, U_shape, dshape=du_t, detwei=detwei)
338
call transform_to_physical(X, ele, psi_shape, dshape=dpsi_t)
340
if(present(psi_mat)) then
341
call addto(psi_mat, psi_ele, psi_ele, &
342
dshape_dot_dshape(dpsi_t, dpsi_t, detwei))
345
lvorticity_mat=shape_curl_shape_2d(psi_shape, du_t, detwei)
349
lvorticity=lvorticity &
350
+matmul(lvorticity_mat(i,:,:), ele_val(U, i, ele))
353
call addto(rhs, psi_ele, -lvorticity)
355
end subroutine calculate_streamfunc_ele
357
end subroutine calculate_stream_function_multipath_2d
359
end module streamfunction