~reducedmodelling/fluidity/ROM_Non-intrusive-ann

1 by hhiester
deleting initialisation as none of tools are used any longer.
1
!    Copyright (C) 2006 Imperial College London and others.
2
!    
3
!    Please see the AUTHORS file in the main source directory for a full list
4
!    of copyright holders.
5
!
6
!    Prof. C Pain
7
!    Applied Modelling and Computation Group
8
!    Department of Earth Science and Engineering
9
!    Imperial College London
10
!
2392 by tmb1
Changing isntances of Chris' person email address to the group
11
!    amcgsoftware@imperial.ac.uk
1 by hhiester
deleting initialisation as none of tools are used any longer.
12
!    
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.
17
!
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.
22
!
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
26
!    USA
27
28
#include "fdebug.h"
29
30
module differential_operator_diagnostics
31
1562 by maddison
Applying the usual Intel module bug workaround
32
! these 5 need to be on top and in this order, so as not to confuse silly old intel compiler 
33
  use quadrature
34
  use elements
35
  use sparse_tools
36
  use fields
37
  use state_module
38
!
1 by hhiester
deleting initialisation as none of tools are used any longer.
39
  use diagnostic_source_fields
773 by maddison
Adding a finite_element_divergence diagnostic algorithm. Bugfix in div algorithm and removing some invalid options from the shallow water schema
40
  use divergence_matrix_cg
1093 by maddison
Applying usual tricks to make the divergence matrix caching work with mesh movement
41
  use eventcounter
1 by hhiester
deleting initialisation as none of tools are used any longer.
42
  use field_derivatives
43
  use field_options
44
  use fldebug
825 by maddison
Adding finite_element_transpose diagnostic algorithm
45
  use geostrophic_pressure
1 by hhiester
deleting initialisation as none of tools are used any longer.
46
  use global_parameters, only : OPTION_PATH_LEN
773 by maddison
Adding a finite_element_divergence diagnostic algorithm. Bugfix in div algorithm and removing some invalid options from the shallow water schema
47
  use solvers
48
  use sparse_matrices_fields
49
  use sparsity_patterns_meshes
1 by hhiester
deleting initialisation as none of tools are used any longer.
50
  use spud
51
  use state_fields_module
52
53
  implicit none
54
  
55
  private
56
  
1488 by maddison
Perp diagnostic algorithm
57
  public :: calculate_grad, calculate_div, calculate_curl, calculate_perp, &
58
    & calculate_curl_2d, calculate_finite_element_divergence, &
993 by maddison
Adding some more options to the Vorticity and Vorticity2D diagnostics
59
    & calculate_finite_element_divergence_transpose, &
1 by hhiester
deleting initialisation as none of tools are used any longer.
60
    & calculate_scalar_advection, calculate_scalar_laplacian, &
61
    & calculate_vector_advection, calculate_vector_laplacian
62
  
63
contains
64
65
  subroutine calculate_grad(state, v_field)
66
    type(state_type), intent(in) :: state
67
    type(vector_field), intent(inout) :: v_field
68
    
69
    type(scalar_field), pointer :: source_field
70
    type(vector_field), pointer :: positions
71
    
72
    source_field => scalar_source_field(state, v_field)
73
    
74
    positions => extract_vector_field(state, "Coordinate")
75
    
1997 by skramer
Taking the derivative of a discontinuous field is not supported in most diagnostic algorithms. Checks for this however were missing and fluidity would happily provide you with the wrong answer. The following diagnostic algorithms do not work for discontinuous source fields and now give an appropriate error message:
76
    call check_source_mesh_derivative(source_field, "grad")
77
    
1 by hhiester
deleting initialisation as none of tools are used any longer.
78
    call grad(source_field, positions, v_field)
79
    
80
  end subroutine calculate_grad
81
82
  subroutine calculate_div(state, s_field)
83
    type(state_type), intent(in) :: state
84
    type(scalar_field), intent(inout) :: s_field
85
    
86
    type(vector_field), pointer :: positions, source_field
87
    
88
    source_field => vector_source_field(state, s_field)
89
    
90
    positions => extract_vector_field(state, "Coordinate")
91
    
1997 by skramer
Taking the derivative of a discontinuous field is not supported in most diagnostic algorithms. Checks for this however were missing and fluidity would happily provide you with the wrong answer. The following diagnostic algorithms do not work for discontinuous source fields and now give an appropriate error message:
92
    call check_source_mesh_derivative(source_field, "div")
93
    
1 by hhiester
deleting initialisation as none of tools are used any longer.
94
    call div(source_field, positions, s_field)
95
    
96
  end subroutine calculate_div
773 by maddison
Adding a finite_element_divergence diagnostic algorithm. Bugfix in div algorithm and removing some invalid options from the shallow water schema
97
  
98
  subroutine calculate_finite_element_divergence(state, s_field)
99
    type(state_type), intent(inout) :: state
100
    type(scalar_field), intent(inout) :: s_field
101
    
102
    character(len = OPTION_PATH_LEN) :: path
1092 by maddison
Add some caching to the finite_element_divergence diagnostic
103
    type(block_csr_matrix) :: ct_m
773 by maddison
Adding a finite_element_divergence diagnostic algorithm. Bugfix in div algorithm and removing some invalid options from the shallow water schema
104
    type(csr_sparsity), pointer :: divergence_sparsity
105
    type(csr_matrix), pointer :: mass
106
    type(scalar_field) :: ctfield, ct_rhs
107
    type(scalar_field), pointer :: masslump
108
    type(vector_field), pointer :: positions, source_field
109
    
110
    source_field => vector_source_field(state, s_field)
111
    path = trim(complete_field_path(s_field%option_path)) // "/algorithm"
1092 by maddison
Add some caching to the finite_element_divergence diagnostic
112
    
1093 by maddison
Applying usual tricks to make the divergence matrix caching work with mesh movement
113
    if(use_divergence_matrix_cache(state)) then
114
      ct_m = extract_block_csr_matrix(state, trim(s_field%name) // "DivergenceMatrix")
115
      call incref(ct_m)
1092 by maddison
Add some caching to the finite_element_divergence diagnostic
116
      ct_rhs = extract_scalar_field(state, trim(s_field%name) // "DivergenceRHS")
117
      call incref(ct_rhs)
118
    else
119
      positions => extract_vector_field(state, "Coordinate")
120
    
121
      divergence_sparsity => get_csr_sparsity_firstorder(state, s_field%mesh, source_field%mesh)
122
      call allocate(ct_m, divergence_sparsity, (/1, source_field%dim/), name = "DivergenceMatrix" )
123
      call allocate(ct_rhs, s_field%mesh, name = "CTRHS")
124
    
125
      call assemble_divergence_matrix_cg(ct_m, state, ct_rhs = ct_rhs, &
126
                                       & test_mesh = s_field%mesh, field = source_field, &
127
                                       & option_path = path)
128
      call insert(state, ct_m, trim(s_field%name) // "DivergenceMatrix")
129
      call insert(state, ct_rhs, trim(s_field%name) // "DivergenceRHS")
130
    end if
773 by maddison
Adding a finite_element_divergence diagnostic algorithm. Bugfix in div algorithm and removing some invalid options from the shallow water schema
131
1092 by maddison
Add some caching to the finite_element_divergence diagnostic
132
    call allocate(ctfield, s_field%mesh, name = "CTField")
133
    call mult(ctfield, ct_m, source_field)
773 by maddison
Adding a finite_element_divergence diagnostic algorithm. Bugfix in div algorithm and removing some invalid options from the shallow water schema
134
    call addto(ctfield, ct_rhs, -1.0)
135
    call deallocate(ct_rhs)
136
    
137
    if(have_option(trim(path) // "/lump_mass")) then
138
      masslump => get_lumped_mass(state, s_field%mesh)
139
      s_field%val = ctfield%val / masslump%val
140
    else
141
      mass => get_mass_matrix(state, s_field%mesh)  
142
      call petsc_solve(s_field, mass, ctfield, option_path = path)
143
    end if
144
1092 by maddison
Add some caching to the finite_element_divergence diagnostic
145
    call deallocate(ct_m)
773 by maddison
Adding a finite_element_divergence diagnostic algorithm. Bugfix in div algorithm and removing some invalid options from the shallow water schema
146
    call deallocate(ctfield)
1093 by maddison
Applying usual tricks to make the divergence matrix caching work with mesh movement
147
148
  contains
149
150
    function use_divergence_matrix_cache(state) result(use_cache)
151
      type(state_type), intent(in) :: state
152
      
153
      logical :: use_cache
154
155
      integer, save :: last_mesh_movement = -1
156
157
      if(has_block_csr_matrix(state, trim(s_field%name) // "DivergenceMatrix")) then
1132 by maddison
Adding some more diagnostics: vector sum, universal node numbering, and boundary condition option to finite element divergence transpose
158
        use_cache = (last_mesh_movement == eventcount(EVENT_MESH_MOVEMENT))
1093 by maddison
Applying usual tricks to make the divergence matrix caching work with mesh movement
159
      else
160
        use_cache = .false.
161
      end if
1132 by maddison
Adding some more diagnostics: vector sum, universal node numbering, and boundary condition option to finite element divergence transpose
162
      
163
      if(use_cache) then
164
        ewrite(2, *) "Using cached divergence matrix"
165
      else
166
        ewrite(2, *) "Not using cached divergence matrix"
167
        last_mesh_movement = eventcount(EVENT_MESH_MOVEMENT)
168
      end if
1093 by maddison
Applying usual tricks to make the divergence matrix caching work with mesh movement
169
170
    end function use_divergence_matrix_cache
773 by maddison
Adding a finite_element_divergence diagnostic algorithm. Bugfix in div algorithm and removing some invalid options from the shallow water schema
171
    
172
  end subroutine calculate_finite_element_divergence
825 by maddison
Adding finite_element_transpose diagnostic algorithm
173
  
174
  subroutine calculate_finite_element_divergence_transpose(state, v_field)
175
    type(state_type), intent(inout) :: state
176
    type(vector_field), intent(inout) :: v_field
177
    
1132 by maddison
Adding some more diagnostics: vector sum, universal node numbering, and boundary condition option to finite element divergence transpose
178
    character(len = FIELD_NAME_LEN) :: bcfield_name
825 by maddison
Adding finite_element_transpose diagnostic algorithm
179
    character(len = OPTION_PATH_LEN) :: path
180
    type(cmc_matrices) :: matrices
181
    type(scalar_field), pointer :: source_field
1132 by maddison
Adding some more diagnostics: vector sum, universal node numbering, and boundary condition option to finite element divergence transpose
182
    type(vector_field), pointer :: bcfield
825 by maddison
Adding finite_element_transpose diagnostic algorithm
183
    
184
    source_field => scalar_source_field(state, v_field)
185
    
186
    path = trim(complete_field_path(v_field%option_path)) // "/algorithm"
1132 by maddison
Adding some more diagnostics: vector sum, universal node numbering, and boundary condition option to finite element divergence transpose
187
    if(have_option(trim(path) // "/bc_field")) then
188
      call get_option(trim(path) // "/bc_field/name", bcfield_name)
189
      bcfield => extract_vector_field(state, bcfield_name)
190
      call allocate(matrices, state, v_field, source_field, bcfield = bcfield, option_path = path, add_cmc = .false.)
191
    else
192
      call allocate(matrices, state, v_field, source_field, option_path = path, add_cmc = .false.)
193
    end if
194
    
825 by maddison
Adding finite_element_transpose diagnostic algorithm
195
    call compute_conservative(matrices, v_field, source_field)
196
    call deallocate(matrices)
197
    
198
  end subroutine calculate_finite_element_divergence_transpose
1 by hhiester
deleting initialisation as none of tools are used any longer.
199
1488 by maddison
Perp diagnostic algorithm
200
  subroutine calculate_perp(state, v_field)
201
    type(state_type), intent(inout) :: state
202
    type(vector_field), intent(inout) :: v_field
203
        
204
    character(len = OPTION_PATH_LEN) :: path
205
    integer :: i
206
    type(csr_matrix), pointer :: mass
207
    type(scalar_field), pointer :: masslump
208
    type(scalar_field), pointer :: source_field
209
    type(vector_field) :: rhs
210
    type(vector_field), pointer :: positions
211
    
212
    write(1, *) "In calculate_perp"
213
        
214
    source_field => scalar_source_field(state, v_field)
215
    ewrite(2, *) "Calculating perp of field: " // trim(source_field%name)
216
    ewrite(2, *) "On mesh: " // trim(source_field%mesh%name)
217
    ewrite(2, *) "Diagnostic field: " // trim(v_field%name)
218
    ewrite(2, *) "On mesh: " // trim(v_field%mesh%name)
219
    
220
    if(v_field%dim /= 2) then
1795 by hhiester
FLAbort/Exit updates
221
      FLExit("Can only calculate perp in 2D")
1488 by maddison
Perp diagnostic algorithm
222
    end if
223
    
1997 by skramer
Taking the derivative of a discontinuous field is not supported in most diagnostic algorithms. Checks for this however were missing and fluidity would happily provide you with the wrong answer. The following diagnostic algorithms do not work for discontinuous source fields and now give an appropriate error message:
224
    call check_source_mesh_derivative(source_field, "perp")
225
    
1488 by maddison
Perp diagnostic algorithm
226
    positions => extract_vector_field(state, "Coordinate")    
227
    path = trim(complete_field_path(v_field%option_path)) // "/algorithm"
228
    
229
    if(have_option(trim(path) // "/lump_mass")) then
230
      masslump => get_lumped_mass(state, v_field%mesh)
231
      call zero(v_field)
232
      do i = 1, ele_count(v_field)
233
        call assemble_perp_ele(i, positions, source_field, v_field)
234
      end do
235
      do i = 1, v_field%dim
2445 by skramer
Changing the storage of vector_field from vfield%val(dim)%ptr(node) to vfield%val(dim,node).
236
        v_field%val(i,:) = v_field%val(i,:) / masslump%val
1488 by maddison
Perp diagnostic algorithm
237
      end do
238
    else
239
      select case(continuity(v_field))
240
        case(0)
241
          if(.not. have_option(trim(path) // "/solver")) then
1531 by maddison
Very minor diagnostic output / FLAbort corrections
242
            FLExit("For continuous perp, must supply solver options when not lumping mass")
1488 by maddison
Perp diagnostic algorithm
243
          end if
244
          mass => get_mass_matrix(state, v_field%mesh)
245
          call allocate(rhs, v_field%dim, v_field%mesh, "PerpRHS")
246
          call zero(rhs)
247
          do i = 1, ele_count(rhs)
248
            call assemble_perp_ele(i, positions, source_field, rhs)
249
          end do
250
          call petsc_solve(v_field, mass, rhs, option_path = path)
251
          call deallocate(rhs)
252
        case(-1)
253
          do i = 1, ele_count(v_field)
254
            call solve_perp_ele(i, positions, source_field, v_field)
255
          end do
256
        case default
257
          ewrite(-1, *) "For mesh continuity: ", continuity(v_field)
258
          FLAbort("Unrecognised mesh continuity")
259
      end select
260
    end if
261
    
262
  contains
263
  
264
    subroutine assemble_perp_ele(ele, positions, source, rhs)
265
      integer, intent(in) :: ele
266
      type(vector_field), intent(in) :: positions
267
      type(scalar_field), intent(in) :: source
268
      type(vector_field), intent(inout) :: rhs
269
      
270
      real, dimension(ele_ngi(positions, ele)) :: detwei
271
      real, dimension(ele_ngi(source, ele), positions%dim) :: grad_source_gi
272
      real, dimension(ele_loc(source, ele), ele_ngi(source, ele), positions%dim) :: dshape
273
      
274
      call transform_to_physical(positions, ele, ele_shape(source, ele), &
275
        & dshape = dshape, detwei = detwei)
276
      
277
      grad_source_gi = ele_grad_at_quad(source, ele, dshape)
278
      
279
      call addto(rhs, 1, ele_nodes(rhs, ele), &
280
        & shape_rhs(ele_shape(rhs, ele), grad_source_gi(:, 2) * detwei))
281
      call addto(rhs, 2, ele_nodes(rhs, ele), &
282
        & shape_rhs(ele_shape(rhs, ele), -grad_source_gi(:, 1) * detwei))
283
        
284
    end subroutine assemble_perp_ele
285
    
286
    subroutine solve_perp_ele(ele, positions, source, perp)
287
      integer, intent(in) :: ele
288
      type(vector_field), intent(in) :: positions
289
      type(scalar_field), intent(in) :: source
290
      type(vector_field), intent(inout) :: perp
291
      
292
      real, dimension(ele_ngi(positions, ele)) :: detwei
293
      real, dimension(ele_ngi(source, ele), positions%dim) :: grad_source_gi
294
      real, dimension(ele_loc(perp, ele), perp%dim) :: little_rhs
295
      real, dimension(ele_loc(perp, ele), ele_loc(perp, ele)) :: little_mass
296
      real, dimension(ele_loc(source, ele), ele_ngi(source, ele), positions%dim) :: dshape
297
      type(element_type), pointer :: shape
298
      
299
      call transform_to_physical(positions, ele, ele_shape(source, ele), &
300
        & dshape = dshape, detwei = detwei)
301
      
302
      grad_source_gi = ele_grad_at_quad(source, ele, dshape)
303
      
304
      shape => ele_shape(perp, ele)
305
      
306
      little_mass = shape_shape(shape, shape, detwei)
307
      little_rhs(:, 1) = shape_rhs(shape, grad_source_gi(:, 2) * detwei)
308
      little_rhs(:, 2) = shape_rhs(shape, -grad_source_gi(:, 1) * detwei)
309
        
310
      call solve(little_mass, little_rhs)
311
      
312
      call set(perp, ele_nodes(perp, ele), transpose(little_rhs))
313
      
314
    end subroutine solve_perp_ele
315
    
316
  end subroutine calculate_perp
317
993 by maddison
Adding some more options to the Vorticity and Vorticity2D diagnostics
318
  subroutine calculate_curl_2d(state, s_field)
319
    type(state_type), intent(inout) :: state
320
    type(scalar_field), intent(inout) :: s_field
321
    
322
    character(len = OPTION_PATH_LEN) :: path
323
    integer :: i
324
    type(csr_matrix), pointer :: mass
325
    type(scalar_field), pointer :: masslump
326
    type(scalar_field) :: rhs
327
    type(vector_field), pointer :: positions, source_field
328
    
329
    ewrite(1, *) "In calculate_curl_2d"
330
        
331
    source_field => vector_source_field(state, s_field)
332
    ewrite(2, *) "Calculating curl of field: " // trim(source_field%name)
333
    ewrite(2, *) "On mesh: " // trim(source_field%mesh%name)
334
    ewrite(2, *) "Diagnostic field: " // trim(s_field%name)
335
    ewrite(2, *) "On mesh: " // trim(s_field%mesh%name)
336
        
337
    if(source_field%dim /= 2) then
1795 by hhiester
FLAbort/Exit updates
338
      FLExit("Can only calculate 2D curl in 2D")
993 by maddison
Adding some more options to the Vorticity and Vorticity2D diagnostics
339
    end if
340
    
1997 by skramer
Taking the derivative of a discontinuous field is not supported in most diagnostic algorithms. Checks for this however were missing and fluidity would happily provide you with the wrong answer. The following diagnostic algorithms do not work for discontinuous source fields and now give an appropriate error message:
341
    call check_source_mesh_derivative(source_field, "curl_2d")
342
    
993 by maddison
Adding some more options to the Vorticity and Vorticity2D diagnostics
343
    positions => extract_vector_field(state, "Coordinate")    
344
    path = trim(complete_field_path(s_field%option_path)) // "/algorithm"
345
    if(have_option(trim(path) // "/lump_mass")) then
1071 by maddison
Removing sub-mesh lumping options for vorticity diagnostics
346
      masslump => get_lumped_mass(state, s_field%mesh)
999 by maddison
Adding a .steady_state diagnostics file, for steady state diagnostics. Also marking some diagnostic fields as "legacy".
347
      call zero(s_field)
1065 by maddison
Bugfix for lumped mass curl diagnostics
348
      do i = 1, ele_count(s_field)
999 by maddison
Adding a .steady_state diagnostics file, for steady state diagnostics. Also marking some diagnostic fields as "legacy".
349
        call assemble_curl_ele(i, positions, source_field, s_field)
993 by maddison
Adding some more options to the Vorticity and Vorticity2D diagnostics
350
      end do
999 by maddison
Adding a .steady_state diagnostics file, for steady state diagnostics. Also marking some diagnostic fields as "legacy".
351
      s_field%val = s_field%val / masslump%val
993 by maddison
Adding some more options to the Vorticity and Vorticity2D diagnostics
352
    else
353
      select case(continuity(s_field))
354
        case(0)
355
          if(.not. have_option(trim(path) // "/solver")) then
356
            FLExit("For continuous curl, must supply solver options when not lumping mass")
357
          end if
358
          mass => get_mass_matrix(state, s_field%mesh)
359
          call allocate(rhs, s_field%mesh, "CurlRHS")
360
          call zero(rhs)
361
          do i = 1, ele_count(rhs)
362
            call assemble_curl_ele(i, positions, source_field, rhs)
363
          end do
364
          call petsc_solve(s_field, mass, rhs, option_path = path)
365
          call deallocate(rhs)
366
        case(-1)
367
          do i = 1, ele_count(s_field)
368
            call solve_curl_ele(i, positions, source_field, s_field)
369
          end do
370
        case default
371
          ewrite(-1, *) "For mesh continuity: ", continuity(s_field)
372
          FLAbort("Unrecognised mesh continuity")
373
      end select
374
    end if
375
    
376
    ewrite_minmax(s_field%val)
377
    
378
    ewrite(1, *) "Exiting calculate_curl_2d"
379
    
380
  contains
381
  
382
    subroutine assemble_curl_ele(ele, positions, source, rhs)
383
      integer, intent(in) :: ele
384
      type(vector_field), intent(in) :: positions
385
      type(vector_field), intent(in) :: source
386
      type(scalar_field), intent(inout) :: rhs
387
      
388
      real, dimension(ele_ngi(positions, ele)) :: detwei
389
      real, dimension(ele_loc(source, ele), ele_ngi(source, ele), positions%dim) :: dshape
390
      
391
      call transform_to_physical(positions, ele, ele_shape(source, ele), &
392
        & dshape = dshape, detwei = detwei)
393
      
394
      call addto(rhs, ele_nodes(rhs, ele), &
395
        & shape_rhs(ele_shape(rhs, ele), &
396
        & ele_2d_curl_at_quad(source, ele, dshape) * detwei))
397
    
398
    end subroutine assemble_curl_ele
399
    
400
    subroutine solve_curl_ele(ele, positions, source, curl)
401
      integer, intent(in) :: ele
402
      type(vector_field), intent(in) :: positions
403
      type(vector_field), intent(in) :: source
404
      type(scalar_field), intent(inout) :: curl
405
      
406
      real, dimension(ele_ngi(positions, ele)) :: detwei
407
      real, dimension(ele_loc(curl, ele)) :: little_rhs
408
      real, dimension(ele_loc(curl, ele), ele_loc(curl, ele)) :: little_mass
409
      real, dimension(ele_loc(source, ele), ele_ngi(source, ele), positions%dim) :: dshape
410
      type(element_type), pointer :: shape
411
      
412
      call transform_to_physical(positions, ele, ele_shape(source, ele), &
413
        & dshape = dshape, detwei = detwei)
414
      
415
      shape => ele_shape(curl, ele)
416
      
417
      little_mass = shape_shape(shape, shape, detwei)
418
      little_rhs = shape_rhs(shape, ele_2d_curl_at_quad(source, ele, dshape) * detwei)
419
        
420
      call solve(little_mass, little_rhs)
421
      
422
      call set(curl, ele_nodes(curl, ele), little_rhs)
423
      
424
    end subroutine solve_curl_ele
425
    
426
  end subroutine calculate_curl_2d
427
1 by hhiester
deleting initialisation as none of tools are used any longer.
428
  subroutine calculate_curl(state, v_field)
993 by maddison
Adding some more options to the Vorticity and Vorticity2D diagnostics
429
    type(state_type), intent(inout) :: state
1 by hhiester
deleting initialisation as none of tools are used any longer.
430
    type(vector_field), intent(inout) :: v_field
431
    
993 by maddison
Adding some more options to the Vorticity and Vorticity2D diagnostics
432
    character(len = OPTION_PATH_LEN) :: path
433
    integer :: i
434
    type(csr_matrix), pointer :: mass
435
    type(scalar_field), pointer :: masslump
436
    type(vector_field) :: rhs
1 by hhiester
deleting initialisation as none of tools are used any longer.
437
    type(vector_field), pointer :: positions, source_field
438
    
993 by maddison
Adding some more options to the Vorticity and Vorticity2D diagnostics
439
    ewrite(1, *) "In calculate_curl"
440
    
1 by hhiester
deleting initialisation as none of tools are used any longer.
441
    source_field => vector_source_field(state, v_field)
993 by maddison
Adding some more options to the Vorticity and Vorticity2D diagnostics
442
    ewrite(2, *) "Calculating curl of field: " // trim(source_field%name)
443
    ewrite(2, *) "On mesh: " // trim(source_field%mesh%name)
444
    ewrite(2, *) "Diagnostic field: " // trim(v_field%name)
445
    ewrite(2, *) "On mesh: " // trim(v_field%mesh%name)
446
    
447
    if(source_field%dim /= 3) then
1795 by hhiester
FLAbort/Exit updates
448
      FLExit("Can only calculate curl in 3D")
993 by maddison
Adding some more options to the Vorticity and Vorticity2D diagnostics
449
    end if
450
    
1997 by skramer
Taking the derivative of a discontinuous field is not supported in most diagnostic algorithms. Checks for this however were missing and fluidity would happily provide you with the wrong answer. The following diagnostic algorithms do not work for discontinuous source fields and now give an appropriate error message:
451
    call check_source_mesh_derivative(source_field, "curl")
452
    
993 by maddison
Adding some more options to the Vorticity and Vorticity2D diagnostics
453
    positions => extract_vector_field(state, "Coordinate")    
454
    path = trim(complete_field_path(v_field%option_path)) // "/algorithm"
455
    if(have_option(trim(path) // "/lump_mass")) then
1071 by maddison
Removing sub-mesh lumping options for vorticity diagnostics
456
      masslump => get_lumped_mass(state, v_field%mesh)
999 by maddison
Adding a .steady_state diagnostics file, for steady state diagnostics. Also marking some diagnostic fields as "legacy".
457
      call zero(v_field)
1065 by maddison
Bugfix for lumped mass curl diagnostics
458
      do i = 1, ele_count(v_field)
999 by maddison
Adding a .steady_state diagnostics file, for steady state diagnostics. Also marking some diagnostic fields as "legacy".
459
        call assemble_curl_ele(i, positions, source_field, v_field)
993 by maddison
Adding some more options to the Vorticity and Vorticity2D diagnostics
460
      end do
461
      do i = 1, v_field%dim
2445 by skramer
Changing the storage of vector_field from vfield%val(dim)%ptr(node) to vfield%val(dim,node).
462
        v_field%val(i,:) = v_field%val(i,:) / masslump%val
993 by maddison
Adding some more options to the Vorticity and Vorticity2D diagnostics
463
      end do
464
    else
465
      select case(continuity(v_field))
466
        case(0)
467
          if(.not. have_option(trim(path) // "/solver")) then
468
            FLExit("For continuous curl, must supply solver options when not lumping mass")
469
          end if
470
          mass => get_mass_matrix(state, v_field%mesh)
471
          call allocate(rhs, v_field%dim, v_field%mesh, "CurlRHS")
472
          call zero(rhs)
473
          do i = 1, ele_count(rhs)
474
            call assemble_curl_ele(i, positions, source_field, rhs)
475
          end do
476
          call petsc_solve(v_field, mass, rhs, option_path = path)
477
          call deallocate(rhs)
478
        case(-1)
479
          do i = 1, ele_count(v_field)
480
            call solve_curl_ele(i, positions, source_field, v_field)
481
          end do
482
        case default
483
          ewrite(-1, *) "For mesh continuity: ", continuity(v_field)
484
          FLAbort("Unrecognised mesh continuity")
485
      end select
486
    end if
487
    
488
    do i = 1, v_field%dim
2445 by skramer
Changing the storage of vector_field from vfield%val(dim)%ptr(node) to vfield%val(dim,node).
489
      ewrite_minmax(v_field%val(i,:))
993 by maddison
Adding some more options to the Vorticity and Vorticity2D diagnostics
490
    end do
491
    
492
    ewrite(1, *) "Exiting calculate_curl"
493
    
494
  contains
495
  
496
    subroutine assemble_curl_ele(ele, positions, source, rhs)
497
      integer, intent(in) :: ele
498
      type(vector_field), intent(in) :: positions
499
      type(vector_field), intent(in) :: source
500
      type(vector_field), intent(inout) :: rhs
501
      
502
      real, dimension(ele_ngi(positions, ele)) :: detwei
503
      real, dimension(ele_loc(source, ele), ele_ngi(source, ele), positions%dim) :: dshape
504
      
505
      call transform_to_physical(positions, ele, ele_shape(source, ele), &
506
        & dshape = dshape, detwei = detwei)
507
      
508
      call addto(rhs, ele_nodes(rhs, ele), &
509
        & shape_vector_rhs(ele_shape(rhs, ele), &
510
        & ele_curl_at_quad(source, ele, dshape), detwei))
511
    
512
    end subroutine assemble_curl_ele
513
    
514
    subroutine solve_curl_ele(ele, positions, source, curl)
515
      integer, intent(in) :: ele
516
      type(vector_field), intent(in) :: positions
517
      type(vector_field), intent(in) :: source
518
      type(vector_field), intent(inout) :: curl
519
      
520
      real, dimension(ele_ngi(positions, ele)) :: detwei
521
      real, dimension(ele_loc(curl, ele), curl%dim) :: little_rhs
522
      real, dimension(ele_loc(curl, ele), ele_loc(curl, ele)) :: little_mass
523
      real, dimension(ele_loc(source, ele), ele_ngi(source, ele), positions%dim) :: dshape
524
      type(element_type), pointer :: shape
525
      
526
      call transform_to_physical(positions, ele, ele_shape(source, ele), &
527
        & dshape = dshape, detwei = detwei)
528
      
529
      shape => ele_shape(curl, ele)
530
      
531
      little_mass = shape_shape(shape, shape, detwei)
532
      little_rhs = transpose(shape_vector_rhs(shape, &
533
        & ele_curl_at_quad(source, ele, dshape), detwei))
534
        
535
      call solve(little_mass, little_rhs)
536
      
537
      call set(curl, ele_nodes(curl, ele), transpose(little_rhs))
538
      
539
    end subroutine solve_curl_ele
1 by hhiester
deleting initialisation as none of tools are used any longer.
540
    
541
  end subroutine calculate_curl
542
543
  subroutine calculate_scalar_advection(state, s_field)
544
    type(state_type), intent(in) :: state
545
    type(scalar_field), intent(inout) :: s_field
546
    
547
    integer :: stat
548
    type(scalar_field), pointer :: source_field
549
    type(vector_field), pointer :: positions, velocity
550
    
551
    source_field => scalar_source_field(state, s_field)
552
    
553
    velocity => extract_vector_field(state, "Velocity", stat = stat)
554
    if(stat /= 0) then
555
      ewrite(0, *) "For field " // trim(s_field%name)
556
      ewrite(0, *) "Warning: Calculating advection with no velocity"
557
      call zero(s_field)
558
      return
559
    end if
560
    
1997 by skramer
Taking the derivative of a discontinuous field is not supported in most diagnostic algorithms. Checks for this however were missing and fluidity would happily provide you with the wrong answer. The following diagnostic algorithms do not work for discontinuous source fields and now give an appropriate error message:
561
    call check_source_mesh_derivative(source_field, "scalar_advection")
562
    
1 by hhiester
deleting initialisation as none of tools are used any longer.
563
    positions => extract_vector_field(state, "Coordinate")
564
    
565
    call u_dot_nabla(velocity, source_field, positions, s_field)
566
    
567
  end subroutine calculate_scalar_advection
568
  
569
  subroutine calculate_vector_advection(state, v_field)
570
    type(state_type), intent(in) :: state
571
    type(vector_field), intent(inout) :: v_field
572
    
573
    integer :: stat
574
    type(vector_field), pointer :: positions, source_field, velocity
575
    
576
    source_field => vector_source_field(state, v_field)
577
    
578
    velocity => extract_vector_field(state, "Velocity", stat = stat)
579
    if(stat /= 0) then
580
      ewrite(0, *) "For field " // trim(v_field%name)
581
      ewrite(0, *) "Warning: Calculating advection with no velocity"
582
      call zero(v_field)
583
      return
584
    end if
585
    
1997 by skramer
Taking the derivative of a discontinuous field is not supported in most diagnostic algorithms. Checks for this however were missing and fluidity would happily provide you with the wrong answer. The following diagnostic algorithms do not work for discontinuous source fields and now give an appropriate error message:
586
    call check_source_mesh_derivative(source_field, "vector_advection")
587
    
1 by hhiester
deleting initialisation as none of tools are used any longer.
588
    positions => extract_vector_field(state, "Coordinate")
589
    
590
    call u_dot_nabla(velocity, source_field, positions, v_field)
591
    
592
  end subroutine calculate_vector_advection
593
  
594
  subroutine calculate_vector_laplacian(state, v_field)
595
    type(state_type), intent(inout) :: state
596
    type(vector_field), intent(inout) :: v_field
597
    
1555 by maddison
Add on the boundary term for scalar_laplacian and vector_laplacian algorithms
598
    integer :: i, j
599
    type(scalar_field) :: source_field_comp, v_field_comp
1 by hhiester
deleting initialisation as none of tools are used any longer.
600
    type(scalar_field), pointer :: masslump
601
    type(vector_field), pointer :: positions, source_field
602
      
603
    ewrite(1, *) "In calculate_vector_laplacian"
604
    ewrite(2, *) "Computing laplacian for field " // trim(v_field%name)
605
      
606
    source_field => vector_source_field(state, v_field)
607
    assert(ele_count(source_field) == ele_count(v_field))
608
    assert(source_field%dim == v_field%dim)
609
    do i = 1, source_field%dim
2445 by skramer
Changing the storage of vector_field from vfield%val(dim)%ptr(node) to vfield%val(dim,node).
610
      ewrite_minmax(source_field%val(i,:))
1 by hhiester
deleting initialisation as none of tools are used any longer.
611
    end do
1997 by skramer
Taking the derivative of a discontinuous field is not supported in most diagnostic algorithms. Checks for this however were missing and fluidity would happily provide you with the wrong answer. The following diagnostic algorithms do not work for discontinuous source fields and now give an appropriate error message:
612
      
613
    call check_source_mesh_derivative(source_field, "vector_laplacian")
1 by hhiester
deleting initialisation as none of tools are used any longer.
614
    
615
    positions => extract_vector_field(state, "Coordinate")
616
    assert(ele_count(positions) == ele_count(v_field))
617
    
618
    call zero(v_field)
619
    do i = 1, ele_count(v_field)
620
      call assemble_vector_laplacian_ele(i, v_field, positions, source_field)
621
    end do
622
    do i = 1, v_field%dim
1555 by maddison
Add on the boundary term for scalar_laplacian and vector_laplacian algorithms
623
      v_field_comp = extract_scalar_field(v_field, i)
624
      source_field_comp = extract_scalar_field(source_field, i)
625
      do j = 1, surface_element_count(v_field)
626
        ! This could be made more efficent by assembling all components at the
627
        ! same time
628
        call assemble_scalar_laplacian_face(j, v_field_comp, positions, source_field_comp)
629
      end do
630
    end do
631
    do i = 1, v_field%dim
2445 by skramer
Changing the storage of vector_field from vfield%val(dim)%ptr(node) to vfield%val(dim,node).
632
      ewrite_minmax(v_field%val(i,:))
1 by hhiester
deleting initialisation as none of tools are used any longer.
633
    end do
634
    
635
    masslump => get_lumped_mass(state, v_field%mesh)
636
    
637
    do i = 1, v_field%dim
2445 by skramer
Changing the storage of vector_field from vfield%val(dim)%ptr(node) to vfield%val(dim,node).
638
      v_field%val(i,:) = v_field%val(i,:) / masslump%val
1 by hhiester
deleting initialisation as none of tools are used any longer.
639
    end do
640
    do i = 1, v_field%dim
2445 by skramer
Changing the storage of vector_field from vfield%val(dim)%ptr(node) to vfield%val(dim,node).
641
      ewrite_minmax(v_field%val(i,:))
1 by hhiester
deleting initialisation as none of tools are used any longer.
642
    end do
643
    
644
    ewrite(1, *) "Exiting calculate_vector_laplacian"
645
        
646
  end subroutine calculate_vector_laplacian
647
  
648
  subroutine assemble_vector_laplacian_ele(ele, v_field, positions, source_field)
649
    integer, intent(in) :: ele
650
    type(vector_field), intent(inout) :: v_field
651
    type(vector_field), intent(in) :: positions
652
    type(vector_field), intent(in) :: source_field
653
    
654
    integer :: i, j
655
    integer, dimension(:), pointer :: element_nodes
656
    real, dimension(ele_ngi(v_field, ele)) :: detwei
657
    real, dimension(source_field%dim, ele_ngi(source_field, ele)) :: grad_gi
658
    real, dimension(ele_loc(source_field, ele), ele_ngi(source_field, ele), source_field%dim) :: dn_t
659
    
660
    assert(ele_ngi(positions, ele) == ele_ngi(v_field, ele))
661
    assert(ele_ngi(source_field, ele) == ele_ngi(v_field, ele))
662
        
663
    call transform_to_physical(positions, ele, ele_shape(source_field, ele), &
664
      & dshape = dn_t, detwei = detwei)
665
      
666
    element_nodes => ele_nodes(v_field, ele)
667
      
668
    do i = 1, source_field%dim
669
      do j = 1, source_field%dim
670
        grad_gi(j, :) = matmul(ele_val(source_field, i, ele), dn_t(:, :, j))
671
      end do
672
      
673
      call addto(v_field, i, element_nodes, -dshape_dot_vector_rhs(dn_t, grad_gi, detwei))
674
    end do
675
    
676
  end subroutine assemble_vector_laplacian_ele
677
  
1555 by maddison
Add on the boundary term for scalar_laplacian and vector_laplacian algorithms
678
  subroutine assemble_scalar_laplacian_face(face, s_field, positions, source_field)
679
    integer, intent(in) :: face
680
    type(scalar_field), intent(inout) :: s_field
681
    type(vector_field), intent(in) :: positions
682
    type(scalar_field), intent(in) :: source_field
683
    
684
    integer :: ele, lface, i, j
685
    real, dimension(face_ngi(s_field, face)) :: detwei, grad_sgi_n
686
    real, dimension(positions%dim, face_ngi(s_field, face)) :: grad_sgi, normal    
687
    real, dimension(positions%dim, positions%dim, ele_ngi(s_field, face_ele(s_field, face))) :: invj
688
    real, dimension(positions%dim, positions%dim, face_ngi(s_field, face)) :: invj_face
689
    real, dimension(ele_loc(s_field, face_ele(source_field, face)), face_ngi(s_field, face), positions%dim) :: dshape_face
690
    real, dimension(ele_loc(s_field, face_ele(source_field, face))) :: s_ele_val
691
    type(element_type) :: augmented_shape
692
    type(element_type), pointer :: fshape, source_shape, positions_shape
693
      
694
    ele = face_ele(s_field, face)
695
    lface = local_face_number(s_field, face)
696
      
697
    positions_shape => ele_shape(positions, ele)
698
    fshape => face_shape(s_field, face)
699
    
700
    source_shape => ele_shape(source_field, ele)
701
    if(associated(source_shape%dn_s)) then
702
      augmented_shape = source_shape
703
      call incref(augmented_shape)
704
    else
705
      augmented_shape = make_element_shape(positions_shape%loc, source_shape%dim, &
706
        & source_shape%degree, source_shape%quadrature, &
707
        & quad_s = fshape%quadrature)
708
    end if    
709
    
710
    call transform_facet_to_physical(positions, face, &
711
      & detwei_f = detwei, normal = normal)
712
    call compute_inverse_jacobian( &
713
      & ele_val(positions, ele), positions_shape, &
714
      & invj = invj)
715
    assert(positions_shape%degree == 1)
716
    assert(ele_numbering_family(positions_shape) == FAMILY_SIMPLEX)
717
    invj_face = spread(invj(:, :, 1), 3, size(invj_face, 3))
718
      
719
    dshape_face = eval_volume_dshape_at_face_quad(augmented_shape, lface, invj_face)
720
    call deallocate(augmented_shape)
721
    
722
    s_ele_val = ele_val(source_field, ele)
723
    forall(i = 1:positions%dim, j = 1:face_ngi(s_field, face))
724
      grad_sgi(i, j) = dot_product(s_ele_val, dshape_face(:, j, i))
725
    end forall
726
    
727
    do i = 1, face_ngi(s_field, face)
728
      grad_sgi_n = dot_product(grad_sgi(:, i), normal(:, i))
729
    end do
730
    
731
    call addto(s_field, face_global_nodes(s_field, face), shape_rhs(fshape, detwei * grad_sgi_n))
732
  
733
  end subroutine assemble_scalar_laplacian_face
734
  
1 by hhiester
deleting initialisation as none of tools are used any longer.
735
  subroutine calculate_scalar_laplacian(state, s_field)
736
    type(state_type), intent(inout) :: state
737
    type(scalar_field), intent(inout) :: s_field
738
    
739
    integer :: i
740
    type(scalar_field), pointer :: masslump, source_field
741
    type(vector_field), pointer :: positions
742
      
743
    ewrite(1, *) "In calculate_scalar_laplacian"
744
    ewrite(2, *) "Computing laplacian for field " // trim(s_field%name)
745
      
746
    source_field => scalar_source_field(state, s_field)
747
    assert(ele_count(source_field) == ele_count(s_field))
748
    assert(mesh_dim(source_field) == mesh_dim(s_field))
749
    ewrite_minmax(source_field%val)
750
    
1997 by skramer
Taking the derivative of a discontinuous field is not supported in most diagnostic algorithms. Checks for this however were missing and fluidity would happily provide you with the wrong answer. The following diagnostic algorithms do not work for discontinuous source fields and now give an appropriate error message:
751
    call check_source_mesh_derivative(source_field, "scalar_laplacian")
752
    
1 by hhiester
deleting initialisation as none of tools are used any longer.
753
    positions => extract_vector_field(state, "Coordinate")
754
    assert(ele_count(positions) == ele_count(s_field))
755
    
756
    call zero(s_field)
757
    do i = 1, ele_count(s_field)
758
      call assemble_scalar_laplacian_ele(i, s_field, positions, source_field)
759
    end do
1555 by maddison
Add on the boundary term for scalar_laplacian and vector_laplacian algorithms
760
    do i = 1, surface_element_count(s_field)
761
      call assemble_scalar_laplacian_face(i, s_field, positions, source_field)
762
    end do
1 by hhiester
deleting initialisation as none of tools are used any longer.
763
    ewrite_minmax(s_field%val)
764
    
765
    masslump => get_lumped_mass(state, s_field%mesh)
766
    
767
    s_field%val = s_field%val / masslump%val
768
    ewrite_minmax(s_field%val)
769
    
770
    ewrite(1, *) "Exiting calculate_scalar_laplacian"
771
        
772
  end subroutine calculate_scalar_laplacian
773
  
774
  subroutine assemble_scalar_laplacian_ele(ele, s_field, positions, source_field)
775
    integer, intent(in) :: ele
776
    type(scalar_field), intent(inout) :: s_field
777
    type(vector_field), intent(in) :: positions
778
    type(scalar_field), intent(in) :: source_field
779
    
780
    integer, dimension(:), pointer :: element_nodes
781
    real, dimension(ele_ngi(s_field, ele)) :: detwei
782
    real, dimension(ele_loc(source_field, ele), ele_ngi(source_field, ele), mesh_dim(source_field)) :: dn_t
783
    
784
    assert(ele_ngi(positions, ele) == ele_ngi(s_field, ele))
785
    assert(ele_ngi(source_field, ele) == ele_ngi(s_field, ele))
786
        
787
    call transform_to_physical(positions, ele, ele_shape(source_field, ele), &
788
      & dshape = dn_t, detwei = detwei)
789
      
790
    element_nodes => ele_nodes(s_field, ele)
791
      
792
    call addto(s_field, element_nodes, -dshape_dot_vector_rhs(dn_t, transpose(ele_grad_at_quad(source_field, ele, dn_t)), detwei))
793
    
794
  end subroutine assemble_scalar_laplacian_ele
795
796
end module differential_operator_diagnostics