Merge lp:~fluidity-core/fluidity/shallow-water-dev into lp:fluidity

Proposed by Dr Colin J Cotter
Status: Work in progress
Proposed branch: lp:~fluidity-core/fluidity/shallow-water-dev
Merge into: lp:fluidity
Diff against target: 68993 lines (has conflicts)
Text conflict in femtools/Makefile.dependencies
Text conflict in femtools/Makefile.in
Text conflict in femtools/doc/femtools_manual.pdf
Text conflict in main/Shallow_Water.F90
Text conflict in tests/Helmholtz-anisotropic-vector-mms-p1p1cg/MMS_A.flml
Text conflict in tests/Helmholtz-anisotropic-vector-mms-p1p1cg/MMS_B.flml
Text conflict in tests/Helmholtz-anisotropic-vector-mms-p1p1cg/MMS_C.flml
Text conflict in tests/Helmholtz-anisotropic-vector-mms-p1p1cg/MMS_D.flml
Text conflict in tests/Helmholtz-anisotropic-vector-mms-p1p1cg/MMS_E.flml
Text conflict in tests/data/cg_interpolation_A.flml
Text conflict in tests/optimality/dummy.swml
Contents conflict in tests/shallow_water_adjoint_default_controls_2d/adjoint_B.swml
Contents conflict in tests/shallow_water_adjoint_default_controls_2d/adjoint_C.swml
Contents conflict in tests/shallow_water_adjoint_default_controls_2d/adjoint_D.swml
Contents conflict in tests/shallow_water_adjoint_default_controls_2d/adjoint_E.swml
Text conflict in tests/shallow_water_adjoint_func_eval_2d/adjoint_template.swml
Contents conflict in tests/shallow_water_optimisation/adjoint_B.swml
Contents conflict in tests/shallow_water_optimisation/adjoint_C.swml
Contents conflict in tests/shallow_water_optimisation/adjoint_D.swml
Contents conflict in tests/shallow_water_optimisation/adjoint_E.swml
Text conflict in tests/shallow_water_optimisation/adjoint_template.swml
Text conflict in tests/shallow_water_optimisation_check_gradient_2d/adjoint_A.swml
To merge this branch: bzr merge lp:~fluidity-core/fluidity/shallow-water-dev
Reviewer Review Type Date Requested Status
Cian Wilson Needs Information
David Ham Pending
Review via email: mp+81751@code.launchpad.net

Description of the change

This shallow-water code includes the changes necessary to correctly interpolate vector fields from the sphere for BDFM1 elements, plus a suite of test cases.

To post a comment you must log in.
Revision history for this message
Cian Wilson (cwilson) wrote :

Hi Colin,

Only had a quick glance but can I check if these changes provide any new functionality to fluidity itself? I ask because you've changed mesh_options.rn[cg] and these options are now appearing in the fluidity schema too but I can't see how they're applicable to fluidity as I can only trace them back to the hybridized helmholtz solver. If they're not used in fluidity then I don't think they should appear in a piece of the schema that is visible within an flml but I may be missing something.

Unrelated to this merge but the parent option (geometry/mesh/from_mesh/constrain_type) for these new options is undocumented in the schema.

Cheers,
Cian

review: Needs Information
Revision history for this message
Dr Colin J Cotter (colin-cotter) wrote :

Hi Cian,
     These options are read in populate_state and make things happen in
Elements.F90 and various other places.

--cjc

On 09/11/11 16:17, Cian Wilson wrote:
> Review: Needs Information
>
> Hi Colin,
>
> Only had a quick glance but can I check if these changes provide any new functionality to fluidity itself? I ask because you've changed mesh_options.rn[cg] and these options are now appearing in the fluidity schema too but I can't see how they're applicable to fluidity as I can only trace them back to the hybridized helmholtz solver. If they're not used in fluidity then I don't think they should appear in a piece of the schema that is visible within an flml but I may be missing something.
>
> Unrelated to this merge but the parent option (geometry/mesh/from_mesh/constrain_type) for these new options is undocumented in the schema.
>
> Cheers,
> Cian

3579. By Dr Colin J Cotter

A few fixes for adjoint, plus tweaking some swmls.

Revision history for this message
Cian Wilson (cwilson) wrote :

Hi Colin,

Could you give me line numbers in fluidity code where this happens please?

When I open up diamond to set up a new flml I get option of setting:
/geometry/mesh/from_mesh/constraint_type/check_continuity
and
/geometry/mesh/from_mesh/constraint_type/check_continuity_matrix.

However, when I grep for where these options are used I get:

cwilson@uisce:shallow-water-dev$ grep -ir check_continuity * -l
assemble/Hybridized_Helmholtz.F90
schemas/mesh_options.rnc
schemas/mesh_options.rng
tests/bdfm1_sphere/linear_williamson2_projection.swml

None of which appear to be fluidity related to me so shouldn't be available in an flml.

Cheers,
Cian

> Hi Cian,
> These options are read in populate_state and make things happen in
> Elements.F90 and various other places.
>
> --cjc
>
> On 09/11/11 16:17, Cian Wilson wrote:
> > Review: Needs Information
> >
> > Hi Colin,
> >
> > Only had a quick glance but can I check if these changes provide any new
> functionality to fluidity itself? I ask because you've changed
> mesh_options.rn[cg] and these options are now appearing in the fluidity schema
> too but I can't see how they're applicable to fluidity as I can only trace
> them back to the hybridized helmholtz solver. If they're not used in fluidity
> then I don't think they should appear in a piece of the schema that is visible
> within an flml but I may be missing something.
> >
> > Unrelated to this merge but the parent option
> (geometry/mesh/from_mesh/constrain_type) for these new options is undocumented
> in the schema.
> >
> > Cheers,
> > Cian

review: Needs Information
Revision history for this message
Cian Wilson (cwilson) wrote :

I can see where the already existing parent option /geometry/mesh/from_mesh/constraint_type is used in Populate_State just not the child options you are adding with this merge.

The parent option does need some documentation in the schema however.

3580. By Patrick Farrell

Some XML fixes for Colin.

3581. By Patrick Farrell

Let's not put in a NonlinearVelocity in the adjoint run, for now.

3582. By Dr Colin J Cotter

Change place where option theta is looked for.

3583. By Dr Colin J Cotter

theta set.

3584. By Dr Colin J Cotter

Removing some dead tests and fixing some swmls.

Revision history for this message
Dr Colin J Cotter (colin-cotter) wrote :

Oh I see, fair point. I'll fix that.

all the best
--cjc

On 09/11/11 19:01, Cian Wilson wrote:
> I can see where the already existing parent option /geometry/mesh/from_mesh/constraint_type is used in Populate_State just not the child options you are adding with this merge.
>
> The parent option does need some documentation in the schema however.

3585. By Dr Colin J Cotter

Move continuity debugging options to shallow_water_options.rnc as
requested by Cian.

3586. By Dr Colin J Cotter

Merge from trunk.

3587. By Dr Colin J Cotter

Merged from the trunk.

3588. By Dr Colin J Cotter

Forgot that this file has been renamed.

Revision history for this message
David Ham (david-ham) wrote :

I think this looks OK. Make sure it is current with trunk and check that all the major functionality is tested.

3589. By Dr Colin J Cotter

Merged from trunk.

3590. By Dr Colin J Cotter

Uncommented code that I previously forgot to uncomment.

3591. By Dr Colin J Cotter

Get theta from correct place in adjoint callbacks.

3592. By Dr Colin J Cotter

Get theta from correct place in adjoint control callbacks too.

3593. By Dr Colin J Cotter

Some dead tests.

3594. By Dr Colin J Cotter

Remove pointer status from scalar field.

3595. By Dr Colin J Cotter

Don't output to the statfile twice at the end of the simulation.

This fixes shallow_water_adjoint_func_eval.

3596. By Dr Colin J Cotter

Merge from trunk.

3597. By Dr Colin J Cotter

Increased timesteps to 2 in test. This is not the solution, but I
can't work out why it doesn't work otherwise.

3598. By Dr Colin J Cotter

Need to calculate functional values before writing diagnostics.

3599. By Dr Colin J Cotter

Made makefiles and got a merge from the trunk.

3600. By Dr Colin J Cotter

Merge from trunk.

3601. By Dr Colin J Cotter

Error in merging makefiles.

3602. By Jemma Shipton

merging changes from Colin's bdfm1-nonlinear-sw branch

3603. By Jemma Shipton

get global numbering fix for quads from trunk

Unmerged revisions

3603. By Jemma Shipton

get global numbering fix for quads from trunk

3602. By Jemma Shipton

merging changes from Colin's bdfm1-nonlinear-sw branch

3601. By Dr Colin J Cotter

Error in merging makefiles.

3600. By Dr Colin J Cotter

Merge from trunk.

3599. By Dr Colin J Cotter

Made makefiles and got a merge from the trunk.

3598. By Dr Colin J Cotter

Need to calculate functional values before writing diagnostics.

3597. By Dr Colin J Cotter

Increased timesteps to 2 in test. This is not the solution, but I
can't work out why it doesn't work otherwise.

3596. By Dr Colin J Cotter

Merge from trunk.

3595. By Dr Colin J Cotter

Don't output to the statfile twice at the end of the simulation.

This fixes shallow_water_adjoint_func_eval.

3594. By Dr Colin J Cotter

Remove pointer status from scalar field.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'Makefile.in'
--- Makefile.in 2012-09-19 11:09:17 +0000
+++ Makefile.in 2012-10-23 15:04:25 +0000
@@ -186,7 +186,9 @@
186 @echo " LD $(PROGRAM)"186 @echo " LD $(PROGRAM)"
187 @$(EVAL) $(LINKER) -o $(PROGRAM) main.o $(LIBS)187 @$(EVAL) $(LINKER) -o $(PROGRAM) main.o $(LIBS)
188188
189sw: bin/shallow_water189sw: bin/shallow_water
190swm: bin/shallow_water_mimetic
191test_sw: bin/test_sw_advection
190be: bin/burgers_equation192be: bin/burgers_equation
191hh: bin/hybridized_helmholtz_solver193hh: bin/hybridized_helmholtz_solver
192194
@@ -199,6 +201,22 @@
199 @$(EVAL) $(FLLINKER) -o bin/hybridized_helmholtz_solver main/Hybridized_Helmholtz_Solver.o $(LIBS)201 @$(EVAL) $(FLLINKER) -o bin/hybridized_helmholtz_solver main/Hybridized_Helmholtz_Solver.o $(LIBS)
200 @$(EVAL) $(FLLINKER) -o bin/shallow_water main/Shallow_Water.o $(LIBS)202 @$(EVAL) $(FLLINKER) -o bin/shallow_water main/Shallow_Water.o $(LIBS)
201203
204bin/shallow_water_mimetic: fluidity_library main/Shallow_Water_Mimetic.F90
205 @cd main; $(MAKE) Shallow_Water_Mimetic.o
206 @echo "BUILD shallow_water_mimetic"
207 @echo " MKDIR bin"
208 @mkdir -p bin
209 @echo " LD shallow_water_mimetic"
210 @$(EVAL) $(FLLINKER) -o bin/shallow_water_mimetic main/Shallow_Water_Mimetic.o $(LIBS)
211
212bin/test_sw_advection: fluidity_library main/test_sw_advection.F90
213 @cd main; $(MAKE) test_sw_advection.o
214 @echo "BUILD test_sw_advection"
215 @echo " MKDIR bin"
216 @mkdir -p bin
217 @echo " LD test_sw_advection"
218 @$(EVAL) $(LINKER) -o bin/test_sw_advection main/test_sw_advection.o $(LIBS)
219
202bin/hybridized_helmholtz_solver: fluidity_library main/Hybridized_Helmholtz_Solver.F90220bin/hybridized_helmholtz_solver: fluidity_library main/Hybridized_Helmholtz_Solver.F90
203 @cd main; $(MAKE) Hybridized_Helmholtz_Solver.o221 @cd main; $(MAKE) Hybridized_Helmholtz_Solver.o
204 @echo "BUILD hybridized_helmholtz_solver"222 @echo "BUILD hybridized_helmholtz_solver"
205223
=== modified file 'adjoint/Burgers_Control_Callbacks.F90'
--- adjoint/Burgers_Control_Callbacks.F90 2011-07-07 16:30:31 +0000
+++ adjoint/Burgers_Control_Callbacks.F90 2012-10-23 15:04:25 +0000
@@ -34,7 +34,7 @@
34 use state_module34 use state_module
35 use python_state35 use python_state
36 use sparse_matrices_fields36 use sparse_matrices_fields
37 use manifold_projections37 use manifold_tools
38 use mangle_dirichlet_rows_module38 use mangle_dirichlet_rows_module
3939
40 implicit none40 implicit none
4141
=== modified file 'adjoint/Makefile.dependencies'
--- adjoint/Makefile.dependencies 2011-07-19 16:55:03 +0000
+++ adjoint/Makefile.dependencies 2012-10-23 15:04:25 +0000
@@ -70,10 +70,9 @@
70Burgers_Control_Callbacks.o ../include/burgers_adjoint_controls.mod: \70Burgers_Control_Callbacks.o ../include/burgers_adjoint_controls.mod: \
71 Burgers_Control_Callbacks.F90 ../include/fdebug.h ../include/fields.mod \71 Burgers_Control_Callbacks.F90 ../include/fdebug.h ../include/fields.mod \
72 ../include/global_parameters.mod \72 ../include/global_parameters.mod \
73 ../include/mangle_dirichlet_rows_module.mod \73 ../include/mangle_dirichlet_rows_module.mod ../include/manifold_tools.mod \
74 ../include/manifold_projections.mod ../include/python_state.mod \74 ../include/python_state.mod ../include/sparse_matrices_fields.mod \
75 ../include/sparse_matrices_fields.mod ../include/spud.mod \75 ../include/spud.mod ../include/state_module.mod
76 ../include/state_module.mod
7776
78../include/forward_main_loop.mod: Forward_Main_Loop.o77../include/forward_main_loop.mod: Forward_Main_Loop.o
79 @true78 @true
@@ -128,7 +127,7 @@
128 Shallow_Water_Adjoint_Callbacks.F90 ../include/adjoint_global_variables.mod \127 Shallow_Water_Adjoint_Callbacks.F90 ../include/adjoint_global_variables.mod \
129 ../include/fdebug.h ../include/fields.mod ../include/global_parameters.mod \128 ../include/fdebug.h ../include/fields.mod ../include/global_parameters.mod \
130 ../include/libadjoint_data_callbacks.mod ../include/mangle_options_tree.mod \129 ../include/libadjoint_data_callbacks.mod ../include/mangle_options_tree.mod \
131 ../include/manifold_projections.mod ../include/populate_state_module.mod \130 ../include/manifold_tools.mod ../include/populate_state_module.mod \
132 ../include/sparse_matrices_fields.mod ../include/spud.mod \131 ../include/sparse_matrices_fields.mod ../include/spud.mod \
133 ../include/state_module.mod132 ../include/state_module.mod
134133
@@ -140,7 +139,7 @@
140 ../include/shallow_water_adjoint_controls.mod: \139 ../include/shallow_water_adjoint_controls.mod: \
141 Shallow_Water_Control_Callbacks.F90 ../include/fdebug.h \140 Shallow_Water_Control_Callbacks.F90 ../include/fdebug.h \
142 ../include/fields.mod ../include/global_parameters.mod \141 ../include/fields.mod ../include/global_parameters.mod \
143 ../include/manifold_projections.mod ../include/python_state.mod \142 ../include/manifold_tools.mod ../include/python_state.mod \
144 ../include/sparse_matrices_fields.mod ../include/spud.mod \143 ../include/sparse_matrices_fields.mod ../include/spud.mod \
145 ../include/state_module.mod144 ../include/state_module.mod
146145
147146
=== modified file 'adjoint/Shallow_Water_Adjoint_Callbacks.F90'
--- adjoint/Shallow_Water_Adjoint_Callbacks.F90 2011-06-09 09:43:33 +0000
+++ adjoint/Shallow_Water_Adjoint_Callbacks.F90 2012-10-23 15:04:25 +0000
@@ -39,7 +39,7 @@
39 use spud, only: get_option39 use spud, only: get_option
40 use adjoint_global_variables, only: adj_path_lookup40 use adjoint_global_variables, only: adj_path_lookup
41 use mangle_options_tree, only: adjoint_field_path41 use mangle_options_tree, only: adjoint_field_path
42 use manifold_projections42 use manifold_tools
43 use global_parameters, only: running_adjoint43 use global_parameters, only: running_adjoint
44 use populate_state_module44 use populate_state_module
45 implicit none45 implicit none
@@ -488,7 +488,7 @@
488 path = adjoint_field_path(path)488 path = adjoint_field_path(path)
489 end if489 end if
490490
491 call get_option(trim(path) // "/prognostic/temporal_discretisation/theta", theta)491 call get_option("/timestepping/theta", theta)
492 call get_option(trim(path) // "/prognostic/mean_layer_thickness", d0)492 call get_option(trim(path) // "/prognostic/mean_layer_thickness", d0)
493493
494 if (hermitian==ADJ_FALSE) then494 if (hermitian==ADJ_FALSE) then
@@ -858,7 +858,7 @@
858858
859 ierr = adj_dict_find(adj_path_lookup, "Fluid::LayerThickness", path)859 ierr = adj_dict_find(adj_path_lookup, "Fluid::LayerThickness", path)
860 call adj_chkierr(ierr)860 call adj_chkierr(ierr)
861 call get_option(trim(path) // "/prognostic/temporal_discretisation/theta", theta)861 call get_option("/timestepping/theta",theta)
862 call get_option(trim(path) // "/prognostic/mean_layer_thickness", d0)862 call get_option(trim(path) // "/prognostic/mean_layer_thickness", d0)
863863
864 eta_mesh => extract_mesh(matrices, "LayerThicknessMesh")864 eta_mesh => extract_mesh(matrices, "LayerThicknessMesh")
865865
=== modified file 'adjoint/Shallow_Water_Control_Callbacks.F90'
--- adjoint/Shallow_Water_Control_Callbacks.F90 2011-06-11 13:26:55 +0000
+++ adjoint/Shallow_Water_Control_Callbacks.F90 2012-10-23 15:04:25 +0000
@@ -34,7 +34,7 @@
34 use state_module34 use state_module
35 use python_state35 use python_state
36 use sparse_matrices_fields36 use sparse_matrices_fields
37 use manifold_projections37 use manifold_tools
3838
39 implicit none39 implicit none
4040
@@ -154,7 +154,7 @@
154 adj_vfield => extract_vector_field(states(state_id), "AdjointLocalVelocityDelta") 154 adj_vfield => extract_vector_field(states(state_id), "AdjointLocalVelocityDelta")
155 ! We need the AdjointVelocity for the option path only155 ! We need the AdjointVelocity for the option path only
156 adj_sfield2 => extract_scalar_field(states(state_id), "AdjointLayerThickness")156 adj_sfield2 => extract_scalar_field(states(state_id), "AdjointLayerThickness")
157 call get_option(trim(adj_sfield2%option_path) // "/prognostic/temporal_discretisation/theta", theta)157 call get_option("/timestepping/theta", theta)
158 call get_option(trim(adj_sfield2%option_path) // "/prognostic/mean_layer_thickness", d0)158 call get_option(trim(adj_sfield2%option_path) // "/prognostic/mean_layer_thickness", d0)
159 159
160 big_mat => extract_block_csr_matrix(matrices, "InverseBigMatrix")160 big_mat => extract_block_csr_matrix(matrices, "InverseBigMatrix")
161161
=== modified file 'assemble/Advection_Diffusion_DG.F90'
--- assemble/Advection_Diffusion_DG.F90 2012-10-11 17:52:01 +0000
+++ assemble/Advection_Diffusion_DG.F90 2012-10-23 15:04:25 +0000
@@ -1547,7 +1547,7 @@
1547 end if1547 end if
1548 end if1548 end if
1549 end if1549 end if
1550 1550
1551 ! Add stabilisation to the advection term if requested by the user.1551 ! Add stabilisation to the advection term if requested by the user.
1552 if (stabilisation_scheme==UPWIND) then1552 if (stabilisation_scheme==UPWIND) then
1553 ! NOTE: U_nl_q may (or may not) have been modified by the grid velocity1553 ! NOTE: U_nl_q may (or may not) have been modified by the grid velocity
@@ -2603,7 +2603,6 @@
2603 nnAdvection_in=shape_shape(T_shape, T_shape_2, &2603 nnAdvection_in=shape_shape(T_shape, T_shape_2, &
2604 & outer_advection_integral * detwei) 2604 & outer_advection_integral * detwei)
26052605
2606
2607 end if2606 end if
2608 if (include_diffusion.or.have_buoyancy_adjustment_by_vertical_diffusion) then2607 if (include_diffusion.or.have_buoyancy_adjustment_by_vertical_diffusion) then
2609 2608
26102609
=== added file 'assemble/Advection_Local_DG.F90'
--- assemble/Advection_Local_DG.F90 1970-01-01 00:00:00 +0000
+++ assemble/Advection_Local_DG.F90 2012-10-23 15:04:25 +0000
@@ -0,0 +1,2538 @@
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!
11! amcgsoftware@imperial.ac.uk
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#include "fdebug.h"
28
29module advection_local_DG
30 !!< This module contains the Discontinuous Galerkin form of the advection
31 !!< equation when vector fields are represented in the local coordinates
32 !!< from the Piola transformation.
33 use elements
34 use global_parameters, only:current_debug_level, OPTION_PATH_LEN
35 use sparse_tools
36 use fetools
37 use dgtools
38 use fields
39 use fefields
40 use state_module
41 use shape_functions
42 use global_numbering
43 use transform_elements
44 use vector_tools
45 use fldebug
46 use vtk_interfaces
47 use Coordinates
48 use Tensors
49 use petsc_solve_state_module
50 use spud
51 use slope_limiters_dg
52 use sparsity_patterns
53 use sparse_matrices_fields
54 use sparsity_patterns_meshes
55 use Vector_Tools
56 use manifold_tools
57 use bubble_tools
58 use diagnostic_fields, only: calculate_diagnostic_variable
59 use global_parameters, only : FIELD_NAME_LEN
60
61 implicit none
62
63 ! Buffer for output messages.
64 character(len=255), private :: message
65
66 private
67 public solve_advection_dg_subcycle, solve_vector_advection_dg_subcycle,&
68 & solve_advection_cg_tracer, compute_U_residual, get_pv
69
70 !parameters specifying vector upwind options
71 integer, parameter :: VECTOR_UPWIND_EDGE=1, VECTOR_UPWIND_SPHERE=2
72
73 ! Local private control parameters. These are module-global parameters
74 ! because it would be expensive and/or inconvenient to re-evaluate them
75 ! on a per-element or per-face basis
76 real :: dt
77
78 ! Whether to include various terms
79 logical :: include_advection
80 contains
81
82 subroutine solve_advection_dg_subcycle(field_name, state, velocity_name,&
83 &continuity, flux)
84 !!< Construct and solve the advection equation for the given
85 !!< field using discontinuous elements.
86
87 !! Name of the field to be solved for.
88 character(len=*), intent(in) :: field_name
89 !! Collection of fields defining system state.
90 type(state_type), intent(inout) :: state
91 !! Optional velocity name
92 character(len = *), intent(in) :: velocity_name
93 !! Solve continuity equation as opposed to advection equation
94 logical, intent(in), optional :: continuity
95 !! Return a flux variable such that T-T_old=div(Flux)
96 type(vector_field), intent(inout), optional :: Flux
97
98 !! Tracer to be solved for.
99 type(scalar_field), pointer :: T, T_old, s_field
100
101 !! Coordinate and velocity fields
102 type(vector_field), pointer :: X, U_nl
103
104 !! Velocity field in Cartesian coordinates for slope limiter
105 type(vector_field) :: U_nl_cartesian
106
107 !! Change in T over one timestep.
108 type(scalar_field) :: delta_T, delta_T_total
109
110 !! Sparsity of advection matrix.
111 type(csr_sparsity), pointer :: sparsity
112
113 !! System matrix.
114 type(csr_matrix) :: matrix, mass, inv_mass
115
116 !! Sparsity of mass matrix.
117 type(csr_sparsity) :: mass_sparsity
118
119 !! Right hand side vector.
120 type(scalar_field) :: rhs
121
122 !! Whether to invoke the slope limiter
123 logical :: limit_slope
124 !! Which limiter to use
125 integer :: limiter
126
127 !! Number of advection subcycles.
128 integer :: subcycles
129 real :: max_courant_number
130
131 !! Flux reconstruction stuff:
132 !! Upwind fluxes trace variable
133 type(scalar_field) :: UpwindFlux
134 !! Mesh where UpwindFlux lives
135 type(mesh_type), pointer :: lambda_mesh
136 !! Upwind Flux matrix
137 type(csr_matrix) :: UpwindFluxMatrix
138
139 character(len=FIELD_NAME_LEN) :: limiter_name
140 integer :: i, ele
141 real :: meancheck
142 ewrite(1,*) 'subroutine solve_advection_dg_subcycle'
143
144
145 T=>extract_scalar_field(state, field_name)
146 T_old=>extract_scalar_field(state, "Old"//field_name)
147 X=>extract_vector_field(state, "Coordinate")
148 U_nl=>extract_vector_field(state, velocity_name)
149
150 ewrite(2,*) 'UVALS in dg', maxval(abs(U_nl%val))
151
152 ! Reset T to value at the beginning of the timestep.
153 call set(T, T_old)
154 if(present(Flux)) then
155 call zero(Flux)
156 end if
157
158 sparsity => get_csr_sparsity_firstorder(state, T%mesh, T%mesh)
159 call allocate(matrix, sparsity) ! Add data space to the sparsity
160 ! pattern.
161 call zero(matrix)
162
163 !Flux reconstruction allocations
164 if(present(Flux)) then
165 !Allocate trace variable for upwind fluxes
166 lambda_mesh=>extract_mesh(state, "VelocityMeshTrace")
167 call allocate(UpwindFlux,lambda_mesh, "UpwindFlux")
168 call zero(UpwindFlux)
169 !Allocate matrix that maps T values to upwind fluxes
170 sparsity => get_csr_sparsity_firstorder(state,lambda_mesh,T%mesh)
171 call allocate(UpwindFluxMatrix,sparsity)
172 call zero(UpwindFluxMatrix)
173 end if
174
175 mass_sparsity=make_sparsity_dg_mass(T%mesh)
176 call allocate(mass, mass_sparsity)
177 call zero(mass)
178 call allocate(inv_mass, mass_sparsity)
179 call zero(inv_mass)
180
181 ! Ensure delta_T inherits options from T.
182 call allocate(delta_T, T%mesh, "delta_T")
183 call zero(delta_T)
184 delta_T%option_path = T%option_path
185 if(present(Flux)) then
186 call allocate(delta_T_total, T%mesh, "deltaT_total")
187 call zero(delta_T_total)
188 end if
189 call allocate(rhs, T%mesh, trim(field_name)//" RHS")
190 call zero(rhs)
191
192 if(present(Flux)) then
193 call construct_advection_dg(matrix, rhs, field_name, state, &
194 mass, velocity_name=velocity_name,continuity=continuity, &
195 UpwindFlux=upwindflux, UpwindFluxMatrix=UpwindFluxMatrix)
196 else
197 call construct_advection_dg(matrix, rhs, field_name, state, &
198 mass, velocity_name=velocity_name,continuity=continuity)
199 end if
200
201 call get_dg_inverse_mass_matrix(inv_mass, mass)
202
203 ! Note that since dt is module global, these lines have to
204 ! come after construct_advection_diffusion_dg.
205 call get_option("/timestepping/timestep", dt)
206
207 if(have_option(trim(T%option_path)//&
208 &"/prognostic/temporal_discretisation/discontinuous_galerkin/&
209 &/number_advection_subcycles")) then
210 call get_option(trim(T%option_path)//&
211 &"/prognostic/temporal_discretisation/discontinuous_galerkin/&
212 &/number_advection_subcycles", subcycles)
213 else
214 call get_option(trim(T%option_path)//&
215 &"/prognostic/temporal_discretisation/discontinuous_galerkin/&
216 &maximum_courant_number_per_subcycle", Max_Courant_number)
217
218 s_field => extract_scalar_field(state, "DG_CourantNumber")
219 call calculate_diagnostic_variable(state, "DG_CourantNumber_Local", &
220 & s_field)
221 ewrite_minmax(s_field)
222 subcycles = ceiling( maxval(s_field%val)/Max_Courant_number)
223 call allmax(subcycles)
224 ewrite(2,*) 'Number of subcycles for tracer eqn: ', subcycles
225 end if
226
227 limit_slope=.false.
228 if (have_option(trim(T%option_path)//"/prognostic/spatial_discretisation/&
229 &discontinuous_galerkin/slope_limiter")) then
230 limit_slope=.true.
231
232 call get_option(trim(T%option_path)//"/prognostic/spatial_discretisation/discontinuous_galerkin/slope_limiter/name",limiter_name)
233
234 select case(trim(limiter_name))
235 case("Vertex_Based")
236 limiter=LIMITER_VB
237 case default
238 FLAbort('No such limiter')
239 end select
240
241 ! Note unsafe for mixed element meshes
242 if (element_degree(T,1)==0) then
243 FLExit("Slope limiters make no sense for degree 0 fields")
244 end if
245
246 end if
247
248 if (limit_slope) then
249 ! Filter wiggles from T
250 call limit_vb_manifold(state,T,delta_T)
251 if(present(flux)) then
252 call zero(upwindflux)
253 call mult(delta_T_total, mass, delta_T)
254 call update_flux(Flux, Delta_T_total, UpwindFlux, Mass)
255 end if
256 end if
257
258 do i=1, subcycles
259 ewrite(1,*) 'SUBCYCLE', i
260
261 ! dT = Advection * T
262 call mult(delta_T, matrix, T)
263 ! dT = dT + RHS
264 call addto(delta_T, RHS, -1.0)
265 if(present(Flux)) then
266 if(maxval(abs(RHS%val))>1.0e-8) then
267 FLAbort('Flux reconstruction doesn''t work if diffusion/bcs present at the moment')
268 end if
269 end if
270 call scale(delta_T, -dt/subcycles)
271 ! dT = M^(-1) dT
272 if(present(flux)) then
273 call mult(UpwindFlux, upwindfluxmatrix, T)
274 call scale(UpwindFlux,-dt/subcycles)
275 call update_flux(Flux, Delta_T, UpwindFlux, Mass)
276 end if
277 ! T = T + dt/s * dT
278 call dg_apply_mass(inv_mass, delta_T)
279 ewrite(2,*) 'Delta_T', maxval(abs(delta_T%val))
280 call addto(T, delta_T)
281 !Probably need to halo_update(Flux) as well
282 call halo_update(T)
283
284 if (limit_slope) then
285 ! Filter wiggles from T
286 call limit_vb_manifold(state,T,delta_T)
287 if(present(flux)) then
288 call mult(delta_t_total, mass, delta_t)
289 call zero(UpwindFlux)
290 call update_flux(Flux, Delta_T_total, UpwindFlux, Mass)
291 end if
292 end if
293 end do
294
295 if(present(Flux)) then
296 if(have_option('/material_phase::Fluid/scalar_field::LayerThickness/p&
297 &rognostic/spatial_discretisation/debug')) then
298 do ele = 1, ele_count(Flux)
299 call check_flux(Flux,T,T_old,X,mass,ele)
300 end do
301 end if
302 call deallocate(UpwindFlux)
303 call deallocate(UpwindFluxMatrix)
304 call deallocate(delta_T_total)
305 end if
306
307 call deallocate(delta_T)
308 call deallocate(matrix)
309 call deallocate(mass)
310 call deallocate(inv_mass)
311 call deallocate(mass_sparsity)
312 call deallocate(rhs)
313
314 end subroutine solve_advection_dg_subcycle
315
316 subroutine check_flux(Flux,T,T_old,X,mass,ele)
317 type(vector_field), intent(in) :: Flux, X
318 type(scalar_field), intent(in) :: T,T_old
319 integer, intent(in) :: ele
320 type(csr_matrix), intent(in) :: mass
321 !
322 real, dimension(Flux%dim,ele_loc(Flux,ele)) :: Flux_vals
323 real, dimension(ele_ngi(T,ele)) :: Div_Flux_gi
324 real, dimension(ele_ngi(T,ele)) :: Delta_T_gi, T_gi
325 real, dimension(ele_loc(T,ele)) :: Delta_T_rhs, Div_Flux_rhs,T_rhs
326 integer :: dim1, loc
327 real, dimension(ele_ngi(X,ele)) :: detwei
328 real, dimension(mesh_dim(X), X%dim, ele_ngi(X,ele)) :: J_mat
329 real :: residual
330 Delta_T_gi = ele_val_at_quad(T,ele)-ele_val_at_quad(T_old,ele)
331 T_gi = ele_val_at_quad(T,ele)
332 Flux_vals = ele_val(Flux,ele)
333
334 Div_Flux_gi = 0.
335 !dn is loc x ngi x dim
336 do dim1 = 1, Flux%dim
337 do loc = 1, ele_loc(Flux,ele)
338 Div_Flux_gi = Div_Flux_gi + Flux%mesh%shape%dn(loc,:,dim1)*&
339 &Flux_vals(dim1,loc)
340 end do
341 end do
342
343 call compute_jacobian(ele_val(X,ele), ele_shape(X,ele),&
344 detwei=detwei,J=J_mat)
345
346 Delta_T_rhs = shape_rhs(ele_shape(T,ele),Delta_T_gi*detwei)
347 T_rhs = shape_rhs(ele_shape(T,ele),T_gi*detwei)
348 Div_Flux_rhs = shape_rhs(ele_shape(T,ele),Div_Flux_gi*&
349 Flux%mesh%shape%quadrature%weight)
350
351 residual = &
352 maxval(abs(Delta_T_rhs-Div_Flux_rhs))/max(1.0&
353 &,maxval(abs(T_rhs)))
354 if(residual>1.0e-6) then
355 ewrite(2,*) residual, maxval(abs(T_rhs))
356 FLExit('Flux residual error.')
357 end if
358
359 end subroutine check_flux
360
361 subroutine update_flux(Flux, Delta_T, UpwindFlux, Mass)
362 !! Subroutine to compute div-conforming Flux such that
363 !! div Flux = Delta_T
364 !! with Flux.n = UpwindFlux on element boundaries
365 !! and then to add to Flux
366 type(vector_field), intent(inout) :: Flux
367 type(scalar_field), intent(in) :: Delta_T
368 type(scalar_field), intent(in) :: UpwindFlux
369 type(csr_matrix), intent(in) :: Mass
370 !
371 integer :: ele, i
372 real :: Area
373 integer, dimension(:), pointer :: T_ele
374
375 !Checking the Flux is in local representation
376 assert(Flux%dim==mesh_dim(Flux))
377
378 do ele = 1, ele_count(Flux)
379 T_ele => ele_nodes(delta_T,ele)
380 area = 0.
381 do i = 1, size(T_ele)
382 area = area + &
383 sum(row_val(Mass,t_ele(i)))
384 end do
385 call update_flux_ele(Flux, Delta_T, UpwindFlux,ele,area)
386 end do
387
388 end subroutine update_flux
389
390 subroutine update_flux_ele(Flux, Delta_T, UpwindFlux,ele,area)
391 !! Subroutine to compute div-conforming Flux such that
392 !! div Flux = Delta_T
393 !! with Flux.n = UpwindFlux on element boundaries
394 type(vector_field), intent(inout) :: Flux
395 type(scalar_field), intent(in) :: Delta_T
396 type(scalar_field), intent(in) :: UpwindFlux
397 integer, intent(in) :: ele
398 real, intent(in) :: area
399 !
400 real, dimension(Flux%dim,ele_loc(Flux,ele)) :: Flux_vals
401 real, dimension(ele_loc(Delta_T,ele)) :: Delta_T_vals
402 real, dimension(Flux%dim*ele_loc(Flux,ele),&
403 Flux%dim*ele_loc(Flux,ele)) :: Flux_mat
404 real, dimension(Flux%dim*ele_loc(Flux,ele)) :: Flux_rhs
405 integer :: row, ni, ele2, face, face2,floc, i, dim1, dim2
406 integer, dimension(:), pointer :: neigh
407 type(constraints_type), pointer :: flux_constraint
408 type(element_type), pointer :: flux_shape, T_shape
409 real, dimension(Flux%dim,ele_loc(Delta_T,ele),&
410 &ele_loc(Flux,ele)) :: grad_mat
411 real, dimension(ele_loc(Delta_t,ele)) :: delta_t_val
412 real, dimension(ele_loc(flux,ele),ele_loc(flux,ele)) &
413 &:: flux_mass_mat
414 real, dimension(ele_loc(Delta_t,ele)) :: div_flux_val
415 real, dimension(ele_ngi(Delta_t,ele)) :: div_flux_gi
416 real :: total_flux, residual
417 !! Need to set up and solve equation for flux DOFs
418 !! Rows are as follows:
419 !! All of the normal fluxes through edges, numbered according to
420 !! UpwindFlux numbering
421 !! Inner product of Flux with grad basis equaling gradient of delta_T
422 !! plus boundary conditions
423 !! Inner product of solution with curl basis equals zero
424 !! inner product of solution with constraint basis equals zero
425
426 !! Debugging check:
427 !! \int \Delta T dV = \int div Flux dV = \int Flux.n dS
428 !! = \int \hat{Flux}.\hat{n dS}
429 neigh => ele_neigh(Flux,ele)
430 total_flux = 0.
431 do ni = 1, size(neigh)
432 ele2 = neigh(ni)
433 face = ele_face(Flux,ele,ele2)
434 if(ele2>0) then
435 face2 = ele_face(Flux,ele2,ele)
436 else
437 face2 = face
438 end if
439 if(face>face2) then
440 total_flux=total_flux+sum(face_val(UpwindFlux,face))
441 else
442 total_flux=total_flux-sum(face_val(UpwindFlux,face))
443 end if
444 end do
445 residual = abs(total_flux-sum(ele_val(Delta_T,ele)))&
446 &/max(1.0,maxval(ele_val(Delta_T,ele)))/area
447 if(residual>1.0e-10) then
448 ewrite(0,*) 'Residual = ', residual
449 FLExit('Bad residual')
450 end if
451
452 Flux_rhs = 0.
453 Flux_mat = 0.
454 flux_shape => ele_shape(flux,ele)
455 T_shape => ele_shape(delta_t,ele)
456 flux_constraint => flux%mesh%shape%constraints
457
458 row = 1
459 !first do the face DOFs
460 neigh => ele_neigh(Flux,ele)
461 do ni = 1, size(neigh)
462 ele2 = neigh(ni)
463 face = ele_face(Flux,ele,ele2)
464 if(ele2>0) then
465 face2 = ele_face(Flux,ele2,ele)
466 else
467 face2 = face
468 end if
469 floc = face_loc(upwindflux,face)
470
471 call update_flux_face(&
472 Flux,UpwindFlux,face,face2,&
473 ele,ele2,Flux_mat(row:row+floc-1,:),&
474 Flux_rhs(row:row+floc-1))
475 row = row + floc
476 end do
477
478 !Next the gradient basis
479 !Start with \nabla\cdot F = \Delta T
480 !Integrate with test function over element
481 ! <\phi,Div F>_E = <\phi, \Delta T>_E
482
483 grad_mat = shape_dshape(T_shape, flux_shape%dn,&
484 & T_shape%quadrature%weight)
485 delta_t_val = ele_val(delta_t,ele)
486 do i = 1, ele_loc(Delta_T,ele)-1
487 do dim1 = 1, flux%dim
488 Flux_mat(row,(dim1-1)*ele_loc(flux,ele)+1:dim1*ele_loc(flux,ele))&
489 &=grad_mat(dim1,i,:)
490 end do
491 flux_rhs(row) = delta_t_val(i)
492 row = row + 1
493 end do
494
495 !Then the curl basis
496 !Adding in the curl terms
497 flux_mass_mat = shape_shape(flux_shape,flux_shape,T_shape%quadrature%weight)
498 do i = 1, flux_constraint%n_curl_basis
499 flux_mat(row,:) = 0.
500 flux_rhs(row) = 0.
501 do dim1 = 1, mesh_dim(flux)
502 do dim2 = 1, mesh_dim(flux)
503 flux_mat(&
504 row,(dim2-1)*ele_loc(flux,ele)+1:dim2*ele_loc(flux,ele)) =&
505 flux_mat(&
506 row,(dim2-1)*ele_loc(flux,ele)+1:dim2*ele_loc(flux,ele)) +&
507 matmul(flux_constraint%curl_basis(i,:,dim1),&
508 flux_mass_mat)
509 end do
510 end do
511 row = row + 1
512 end do
513
514 !Then the constraints
515 do i = 1, flux_constraint%n_constraints
516 do dim1 = 1, mesh_dim(flux)
517 flux_mat(&
518 row,(dim1-1)*ele_loc(flux,ele)+1:&
519 dim1*ele_loc(flux,ele)) =&
520 flux_constraint%orthogonal(i,:,dim1)
521 end do
522 row = row + 1
523 end do
524
525 call solve(flux_mat,flux_rhs)
526
527 do dim1 = 1, mesh_dim(flux)
528 flux_vals(dim1,:) = &
529 flux_rhs((dim1-1)*ele_loc(flux,ele)+1:dim1*ele_loc(flux,ele))
530 end do
531
532 !A debugging check.
533 !Computing < \phi, div F> in local coordinates
534 !and comparing with <\phi, delta_T> in global coordinates
535 !(works because of pullback formula)
536
537 !dn is loc x ngi x dim
538 !Flux is dim x loc
539 div_flux_gi = 0.
540 do dim1 =1, size(flux_vals,1)
541 do i=1,size(flux_vals,2)
542 div_flux_gi = div_flux_gi + flux_vals(dim1,i)*&
543 &flux_shape%dn(i,:,dim1)
544 end do
545 end do
546 div_flux_val = shape_rhs(T_shape,div_flux_gi*T_shape%quadrature%weight)
547 residual = maxval(abs(div_flux_val-delta_T_val))/max(1.0&
548 &,maxval(abs(delta_T_val)))/area
549 assert(residual<1.0e-10)
550
551 !Add the flux in
552 call addto(flux,ele_nodes(flux,ele),flux_vals)
553 end subroutine update_flux_ele
554
555 subroutine update_flux_face(&
556 Flux,UpwindFlux,face,face2,&
557 ele,ele2,Flux_mat_rows,Flux_rhs_rows)
558 type(vector_field), intent(in) :: Flux
559 type(scalar_Field), intent(in) :: UpwindFlux
560 integer, intent(in) :: face,face2,ele,ele2
561 real, dimension(face_loc(upwindflux,ele),&
562 &mesh_dim(flux)*ele_loc(flux,ele)), &
563 &intent(inout) :: flux_mat_rows
564 real, dimension(flux%mesh%shape%constraints%n_face_basis), intent(inout)&
565 &:: flux_rhs_rows
566 !
567 integer :: dim1, row, flux_dim1
568 real, dimension(mesh_dim(flux), face_ngi(flux, face)) :: n_local
569 real, dimension(face_ngi(flux,face)) :: detwei_f
570 real :: weight
571 type(element_type), pointer :: flux_face_shape, upwindflux_face_shape
572 integer, dimension(face_loc(flux,face)) :: flux_face_nodes
573 real, dimension(mesh_dim(flux),face_loc(upwindflux,face),&
574 face_loc(flux,face)) :: face_mat
575 !
576 flux_face_shape=>face_shape(flux, face)
577 upwindflux_face_shape=>face_shape(upwindflux, face)
578 flux_face_nodes=face_local_nodes(flux, face)
579
580 !Get normal in local coordinates
581 call get_local_normal(n_local,weight,flux,&
582 &local_face_number(flux%mesh,face))
583
584 detwei_f = weight*flux_face_shape%quadrature%weight
585 face_mat = shape_shape_vector(&
586 upwindflux_face_shape,flux_face_shape,detwei_f,n_local)
587 !Equation is:
588 ! <\phi,Flux.n> = <\phi,UpwindFlux> for all trace test functions \phi.
589
590 do row = 1, size(flux_mat_rows,1)
591 flux_mat_rows(row,:) = 0.
592 do dim1 = 1,mesh_dim(flux)
593 flux_dim1 = (dim1-1)*ele_loc(flux,ele)
594 flux_mat_rows(row,flux_dim1+flux_face_nodes)&
595 &=face_mat(dim1,row,:)
596 end do
597 end do
598 !Need to adopt a sign convention:
599 !If face>face_2
600 !We store flux with normal pointing from face into face_2
601 !Otherwise
602 !We store flux with normal pointing from face_2 into face
603 ! Outflow boundary integral.
604 if(face>face2) then
605 flux_rhs_rows = face_val(UpwindFlux,face)
606 else
607 flux_rhs_rows = -face_val(UpwindFlux,face)
608 end if
609 end subroutine update_flux_face
610
611 subroutine construct_advection_dg(big_m, rhs, field_name,&
612 & state, mass, velocity_name, continuity,&
613 & UpwindFlux, UpwindFluxMatrix)
614 !!< Construct the advection equation for discontinuous elements in
615 !!< acceleration form.
616 !!<
617 !!< If mass is provided then the mass matrix is not added into big_m or
618 !!< rhs. It is instead returned as mass. This may be useful for testing
619 !!< or for solving equations otherwise than in acceleration form.
620
621 !! Main advection matrix.
622 type(csr_matrix), intent(inout) :: big_m
623 !! Right hand side vector.
624 type(scalar_field), intent(inout) :: rhs
625
626 !! Name of the field to be advected.
627 character(len=*), intent(in) :: field_name
628 !! Collection of fields defining system state.
629 type(state_type), intent(inout) :: state
630 !! Optional separate mass matrix.
631 type(csr_matrix), intent(inout), optional :: mass
632 !! Optional velocity name
633 character(len = *), intent(in), optional :: velocity_name
634 !! solve the continuity equation
635 logical, intent(in), optional :: continuity
636 !! Upwind flux field
637 type(scalar_field), intent(in), optional :: UpwindFlux
638 !! Upwind flux matrix
639 type(csr_matrix), intent(inout), optional :: UpwindFluxMatrix
640
641 !! Position, and velocity fields.
642 type(vector_field) :: X, U, U_nl
643 !! Tracer to be solved for.
644 type(scalar_field) :: T
645
646 !! Local velocity name
647 character(len = FIELD_NAME_LEN) :: lvelocity_name
648
649 !! Element index
650 integer :: ele
651
652 !! Status variable for field extraction.
653 integer :: stat
654
655 ewrite(1,*) "Writing advection equation for "&
656 &//trim(field_name)
657
658 ! These names are based on the CGNS SIDS.
659 T=extract_scalar_field(state, field_name)
660 X=extract_vector_field(state, "Coordinate")
661
662 if(present(velocity_name)) then
663 lvelocity_name = velocity_name
664 else
665 lvelocity_name = "NonlinearVelocity"
666 end if
667
668 if (.not.have_option(trim(T%option_path)//"/prognostic&
669 &/spatial_discretisation/discontinuous_galerkin&
670 &/advection_scheme/none")) then
671 U_nl=extract_vector_field(state, lvelocity_name)
672 call incref(U_nl)
673 include_advection=.true.
674 else
675 ! Forcing a zero NonlinearVelocity will disable advection.
676 U=extract_vector_field(state, "Velocity", stat)
677 if (stat/=0) then
678 FLExit("Oh dear, no velocity field. A velocity field is required for advection!")
679 end if
680 call allocate(U_nl, U%dim, U%mesh, "LocalNonlinearVelocity")
681 call zero(U_nl)
682 include_advection=.false.
683 end if
684
685 assert(has_faces(X%mesh))
686 assert(has_faces(T%mesh))
687
688 call zero(big_m)
689 call zero(RHS)
690 call zero(mass)
691
692 element_loop: do ele=1,element_count(T)
693
694 call construct_adv_element_dg(ele, big_m, rhs,&
695 & X, T, U_nl, mass, continuity=continuity,&
696 & upwindflux=upwindflux, upwindfluxmatrix=upwindfluxmatrix)
697
698 end do element_loop
699
700 ! Drop any extra field references.
701
702 call deallocate(U_nl)
703
704 end subroutine construct_advection_dg
705
706 subroutine construct_adv_element_dg(ele, big_m, rhs,&
707 & X, T, U_nl, mass, continuity, upwindflux, upwindfluxmatrix)
708 !!< Construct the advection_diffusion equation for discontinuous elements in
709 !!< acceleration form.
710 implicit none
711 !! Index of current element
712 integer :: ele
713 !! Main advection matrix.
714 type(csr_matrix), intent(inout) :: big_m
715 !! Right hand side vector.
716 type(scalar_field), intent(inout) :: rhs
717 !! Optional separate mass matrix.
718 type(csr_matrix), intent(inout) :: mass
719
720 !! Position and velocity.
721 type(vector_field), intent(in) :: X, U_nl
722
723 type(scalar_field), intent(in) :: T
724
725 !! Upwind flux field
726 type(scalar_field), intent(in), optional :: UpwindFlux
727 !! Upwind flux matrix
728 type(csr_matrix), intent(inout), optional :: UpwindFluxMatrix
729
730
731 !! Solve the continuity equation rather than advection
732 logical, intent(in), optional :: continuity
733
734 ! Bilinear forms.
735 real, dimension(ele_loc(T,ele), ele_loc(T,ele)) :: &
736 mass_mat
737 real, dimension(ele_loc(T,ele), ele_loc(T,ele)) :: &
738 Advection_mat, Ad_mat1, Ad_mat2
739
740 ! Local assembly matrices.
741 real, dimension(ele_loc(T,ele), ele_loc(T,ele)) :: l_T_mat
742 real, dimension(ele_loc(T,ele)) :: l_T_rhs
743
744 ! Local variables.
745
746 ! Neighbour element, face and neighbour face.
747 integer :: ele_2, face, face_2
748 ! Loops over faces.
749 integer :: ni
750
751 ! Transform from local to physical coordinates.
752 real, dimension(ele_ngi(X,ele)) :: detwei
753 real, dimension(mesh_dim(U_nl), X%dim, ele_ngi(X,ele)) :: J_mat
754
755 ! Different velocities at quad points.
756 real, dimension(U_nl%dim, ele_ngi(U_nl, ele)) :: U_quad
757 real, dimension(U_nl%dim, ele_ngi(U_nl, ele)) :: U_nl_q
758 real, dimension(ele_ngi(U_nl, ele)) :: U_nl_div_q
759
760 ! Node and shape pointers.
761 integer, dimension(:), pointer :: T_ele
762 type(element_type), pointer :: T_shape, U_shape
763 ! Neighbours of this element.
764 integer, dimension(:), pointer :: neigh
765
766 integer :: gi,i,j
767
768 !----------------------------------------------------------------------
769 ! Establish local node lists
770 !----------------------------------------------------------------------
771
772 T_ele=>ele_nodes(T,ele) ! Tracer node numbers
773
774 !----------------------------------------------------------------------
775 ! Establish local shape functions
776 !----------------------------------------------------------------------
777
778 T_shape=>ele_shape(T, ele)
779 U_shape=>ele_shape(U_nl, ele)
780
781 !==========================
782 ! Coordinates
783 !==========================
784
785 ! Get J_mat
786 call compute_jacobian(ele_val(X,ele), ele_shape(X,ele), detwei=detwei, J=J_mat)
787
788 !----------------------------------------------------------------------
789 ! Construct bilinear forms.
790 !----------------------------------------------------------------------
791
792 ! Element density matrix.
793 ! /
794 ! | T T dV
795 ! /
796 mass_mat = shape_shape(T_shape, T_shape, detwei)
797
798 if (include_advection) then
799
800 ! Advecting velocity at quadrature points.
801 U_quad=ele_val_at_quad(U_nl,ele)
802 U_nl_q=U_quad
803
804 U_nl_div_q=ele_div_at_quad(U_nl, ele, U_shape%dn)
805
806 ! Element advection matrix
807 ! / /
808 ! - | (grad phi dot U_nl) T dV -| phi ( div U_nl ) T dV
809 ! / /
810 Advection_mat = -dshape_dot_vector_shape(T_shape%dn, U_nl_q, T_shape,&
811 & T_shape%quadrature%weight)
812 if(.not.present_and_true(continuity)) then
813 Advection_mat = Advection_mat&
814 &-shape_shape(T_shape, T_shape, U_nl_div_q * T_shape&
815 &%quadrature%weight)
816 end if
817 else
818 Advection_mat=0.0
819 end if
820
821 !----------------------------------------------------------------------
822 ! Perform global assembly.
823 !----------------------------------------------------------------------
824
825 l_T_rhs=0.0
826
827 ! Assemble matrix.
828
829 ! Advection.
830 l_T_mat= Advection_mat
831
832 call addto(mass, t_ele, t_ele, mass_mat)
833
834 call addto(big_m, t_ele, t_ele, l_T_mat)
835
836 !-------------------------------------------------------------------
837 ! Interface integrals
838 !-------------------------------------------------------------------
839
840 neigh=>ele_neigh(T, ele)
841
842 neighbourloop: do ni=1,size(neigh)
843
844 !----------------------------------------------------------------------
845 ! Find the relevant faces.
846 !----------------------------------------------------------------------
847
848 ! These finding routines are outside the inner loop so as to allow
849 ! for local stack variables of the right size in
850 ! construct_add_diff_interface_dg.
851
852 ele_2=neigh(ni)
853
854 ! Note that although face is calculated on field U, it is in fact
855 ! applicable to any field which shares the same mesh topology.
856 face=ele_face(T, ele, ele_2)
857
858 if (ele_2>0) then
859 ! Internal faces.
860 face_2=ele_face(T, ele_2, ele)
861 else
862 ! External face.
863 face_2=face
864 end if
865
866 call construct_adv_interface_dg(ele, face, face_2,&
867 & big_m, rhs, X, T, U_nl, upwindflux, upwindfluxmatrix)
868
869 end do neighbourloop
870
871 end subroutine construct_adv_element_dg
872
873 subroutine construct_adv_interface_dg(ele, face, face_2, &
874 big_m, rhs, X, T, U_nl,upwindflux, upwindfluxmatrix)
875
876 !!< Construct the DG element boundary integrals on the ni-th face of
877 !!< element ele.
878 implicit none
879
880 integer, intent(in) :: ele, face, face_2
881 type(csr_matrix), intent(inout) :: big_m
882 type(scalar_field), intent(inout) :: rhs
883 ! We pass these additional fields to save on state lookups.
884 type(vector_field), intent(in) :: X, U_nl
885 type(scalar_field), intent(in) :: T
886 !! Upwind flux field
887 type(scalar_field), intent(in), optional :: UpwindFlux
888 !! Upwind flux matrix
889 type(csr_matrix), intent(inout), optional :: UpwindFluxMatrix
890
891 ! Face objects and numberings.
892 type(element_type), pointer :: T_shape, T_shape_2
893 integer, dimension(face_loc(T,face)) :: T_face
894 integer, dimension(face_loc(T,face_2)) :: T_face_2
895 integer, dimension(face_loc(U_nl,face)) :: U_face
896 integer, dimension(face_loc(U_nl,face_2)) :: U_face_2
897
898 ! Note that both sides of the face can be assumed to have the same
899 ! number of quadrature points.
900 real, dimension(U_nl%dim, face_ngi(U_nl, face)) :: n1, n2, normal, U_nl_q,&
901 & u_f_q, u_f2_q, div_u_f_q
902 logical, dimension(face_ngi(U_nl, face)) :: inflow
903 real, dimension(face_ngi(U_nl, face)) :: U_nl_q_dotn1, U_nl_q_dotn2, U_nl_q_dotn, income
904 ! Variable transform times quadrature weights.
905 real, dimension(face_ngi(T,face)) :: detwei
906 real, dimension(U_nl%dim, X%dim, face_ngi(T,face)) :: J
907 real, dimension(face_ngi(T,face)) :: inner_advection_integral, outer_advection_integral
908
909 ! Bilinear forms
910 real, dimension(face_loc(T,face),face_loc(T,face)) :: nnAdvection_out
911 real, dimension(face_loc(T,face),face_loc(T,face_2)) :: nnAdvection_in
912
913 ! Normal weights
914 real :: w1, w2
915 integer :: i
916
917 T_face=face_global_nodes(T, face)
918 T_shape=>face_shape(T, face)
919
920 T_face_2=face_global_nodes(T, face_2)
921 T_shape_2=>face_shape(T, face_2)
922
923 !Get normals
924 call get_local_normal(n1, w1, U_nl, local_face_number(U_nl%mesh,face))
925 call get_local_normal(n2, w2, U_nl, local_face_number(U_nl%mesh,face_2))
926
927 if (include_advection) then
928
929 ! Advecting velocity at quadrature points.
930 U_f_q = face_val_at_quad(U_nl, face)
931 U_f2_q = face_val_at_quad(U_nl, face_2)
932
933 u_nl_q_dotn1 = sum(U_f_q*w1*n1,1)
934 u_nl_q_dotn2 = -sum(U_f2_q*w2*n2,1)
935 U_nl_q_dotn=0.5*(u_nl_q_dotn1+u_nl_q_dotn2)
936
937 ! Inflow is true if the flow at this gauss point is directed
938 ! into this element.
939 inflow = u_nl_q_dotn<0.0
940 income = merge(1.0,0.0,inflow)
941
942 !----------------------------------------------------------------------
943 ! Construct bilinear forms.
944 !----------------------------------------------------------------------
945
946 ! Calculate outflow boundary integral.
947 ! can anyone think of a way of optimising this more to avoid
948 ! superfluous operations (i.e. multiplying things by 0 or 1)?
949
950 ! first the integral around the inside of the element
951 ! (this is the flux *out* of the element)
952 inner_advection_integral = (1.-income)*u_nl_q_dotn
953 nnAdvection_out=shape_shape(T_shape, T_shape, &
954 & inner_advection_integral*T_shape%quadrature%weight)
955
956 ! now the integral around the outside of the element
957 ! (this is the flux *in* to the element)
958 outer_advection_integral = income*u_nl_q_dotn
959 nnAdvection_in=shape_shape(T_shape, T_shape_2, &
960 & outer_advection_integral*T_shape%quadrature%weight)
961
962 end if
963
964 !----------------------------------------------------------------------
965 ! Perform global assembly.
966 !----------------------------------------------------------------------
967
968 ! Insert advection in matrix.
969 if (include_advection) then
970
971 ! Outflow boundary integral.
972 call addto(big_M, T_face, T_face,&
973 nnAdvection_out)
974
975 ! Inflow boundary integral.
976 call addto(big_M, T_face, T_face_2,&
977 nnAdvection_in)
978
979 if(present(UpwindFlux).and.present(UpwindFluxMatrix)) then
980 call construct_upwindflux_interface(upwindflux,upwindfluxmatrix)
981 end if
982 end if
983
984 contains
985 subroutine construct_upwindflux_interface(upwindflux,upwindfluxmatrix)
986 type(scalar_field), intent(in) :: upwindflux
987 type(csr_matrix), intent(inout) :: upwindfluxmatrix
988 !
989 ! Bilinear forms
990 real, dimension(face_loc(upwindflux,face),&
991 face_loc(T,face)) :: upwindflux_mat_in,upwindflux_mat_out
992 type(element_type), pointer :: flux_shape
993 integer, dimension(face_loc(upwindflux,face)) :: flux_face
994 integer :: sign
995
996 flux_shape=>face_shape(upwindflux, face)
997 flux_face=face_global_nodes(upwindflux, face)
998
999 upwindflux_mat_in=shape_shape(flux_shape, T_shape, &
1000 income*u_nl_q_dotn*T_shape%quadrature%weight)
1001 upwindflux_mat_out=shape_shape(flux_shape, T_shape, &
1002 (1.0-income)*u_nl_q_dotn*T_shape%quadrature%weight)
1003
1004 !Need to adopt a sign convention:
1005 !If face>face_2
1006 !We store flux with normal pointing from face into face_2
1007 !Otherwise
1008 !We store flux with normal pointing from face_2 into face
1009 ! Outflow boundary integral.
1010
1011 !inflow = u_nl_q_dotn<0.0
1012 !income = 1 if(inflow) and 0 otherwise
1013 if(face>face_2) then
1014 sign = 1
1015 else
1016 sign = -1
1017 end if
1018
1019 !Factor of 0.5 is because we visit from both sides
1020 if(face.ne.face_2) then
1021 call addto(upwindfluxmatrix, flux_face, T_face_2,&
1022 0.5*sign*upwindflux_mat_in)
1023 end if
1024 call addto(upwindfluxmatrix, flux_face, T_face,&
1025 0.5*sign*upwindflux_mat_out)
1026
1027 end subroutine construct_upwindflux_interface
1028
1029 end subroutine construct_adv_interface_dg
1030
1031 subroutine solve_advection_cg_tracer(Q,D,D_old,Flux,U_nl,QF,state)
1032 !!< Solve the continuity equation for D*Q with Flux
1033 !!< d/dt (D*Q) + div(Flux*Q) = diffusion terms.
1034 !!< Done on the vorticity mesh
1035 !!< Return a PV flux Q defined on the velocity mesh
1036 !!< which is the projection of Flux*Q into the velocity space
1037 !!< Note that Flux and Q contain a factor of dt for convenience.
1038 type(state_type), intent(inout) :: state
1039 type(scalar_field), intent(in),target :: D,D_old
1040 type(scalar_field), intent(inout),target :: Q
1041 type(vector_field), intent(in) :: Flux,U_nl
1042 type(vector_field), intent(inout) :: QF
1043 !
1044 type(csr_sparsity), pointer :: Q_sparsity
1045 type(csr_matrix) :: Adv_mat
1046 type(scalar_field) :: Q_rhs, Qtest1,Qtest2
1047 type(vector_field), pointer :: X, down
1048 type(scalar_field), pointer :: Q_old
1049 integer :: ele
1050 real :: dt, t_theta, residual
1051
1052 ewrite(1,*) ' subroutine solve_advection_cg_tracer('
1053
1054 X=>extract_vector_field(state, "Coordinate")
1055 down=>extract_vector_field(state, "GravityDirection")
1056 Q_old=>extract_scalar_field(state, "Old"//trim(Q%name))
1057 call get_option('/timestepping/timestep',dt)
1058 call get_option('/timestepping/theta',t_theta)
1059
1060 !set up matrix and rhs
1061 Q_sparsity => get_csr_sparsity_firstorder(state, Q%mesh, Q%mesh)
1062 call allocate(adv_mat, Q_sparsity) ! Add data space to the sparsity
1063 ! pattern.
1064 call zero(adv_mat)
1065 call allocate(Q_rhs,Q%mesh,trim(Q%name)//"RHS")
1066 call zero(Q_rhs)
1067 call zero(QF)
1068
1069 do ele = 1, ele_count(Q)
1070 call construct_advection_cg_tracer_ele(Q_rhs,adv_mat,Q,D,D_old,Flux&
1071 &,X,down,U_nl,dt,t_theta,ele)
1072 end do
1073
1074 ewrite(2,*) 'Q_RHS', maxval(abs(Q_rhs%val))
1075
1076 call petsc_solve(Q,adv_mat,Q_rhs)
1077
1078 !! Compute the PV flux to pass to velocity equation
1079 do ele = 1, ele_count(Q)
1080 call construct_pv_flux_ele(QF,Q,Q_old,D,D_old,Flux,&
1081 X,down,U_nl,t_theta,ele)
1082 end do
1083
1084 if(have_option('/material_phase::Fluid/scalar_field::PotentialVorticity/&
1085 &prognostic/debug')) then
1086 call allocate(Qtest1,Q%mesh,trim(Q%name)//"test1")
1087 call zero(Qtest1)
1088 call allocate(Qtest2,Q%mesh,trim(Q%name)//"test2")
1089 call zero(Qtest2)
1090 do ele = 1, ele_count(Q)
1091 call test_pv_flux_ele(Qtest1,Qtest2,QF,Q,Q_old,D,D_old,&
1092 Flux,X,down,t_theta,ele)
1093 end do
1094 ewrite(2,*) 'Error = ', maxval(abs(Qtest2%val)), maxval(abs(Qtest1%val))
1095 ewrite(2,*) 'Error = ', maxval(abs(Qtest1%val-Qtest2%val)), maxval(Qtest1%val)
1096 residual = maxval(abs(Qtest1%val-Qtest2%val))/max(1.0&
1097 &,maxval(abs(Qtest1%val)))
1098 ewrite(2,*) 'residual from qtest', residual
1099 assert(residual<1.0e-6)
1100 ewrite(2,*) 'test passed'
1101 call deallocate(Qtest1)
1102 call deallocate(Qtest2)
1103 end if
1104
1105 call deallocate(adv_mat)
1106 call deallocate(Q_rhs)
1107
1108 ewrite(1,*) 'END subroutine solve_advection_cg_tracer('
1109
1110 end subroutine solve_advection_cg_tracer
1111
1112 subroutine construct_advection_cg_tracer_ele(Q_rhs,adv_mat,Q,D,D_old,Flux,&
1113 & X,down,U_nl,dt,t_theta,ele)
1114 type(scalar_field), intent(in) :: D,D_old,Q
1115 type(scalar_field), intent(inout) :: Q_rhs
1116 type(csr_matrix), intent(inout) :: Adv_mat
1117 type(vector_field), intent(in) :: X, Flux, down,U_nl
1118 integer, intent(in) :: ele
1119 real, intent(in) :: dt, t_theta
1120 !
1121 real, dimension(ele_loc(Q,ele),ele_loc(Q,ele)) :: l_adv_mat,tmp_mat
1122 real, dimension(ele_loc(Q,ele)) :: l_rhs
1123 real, dimension(U_nl%dim,ele_ngi(U_nl,ele)) :: U_nl_gi
1124 real, dimension(X%dim, ele_ngi(U_nl,ele)) :: U_cart_gi
1125 real, dimension(Flux%dim,ele_ngi(Flux,ele)) :: Flux_gi
1126 real, dimension(Flux%dim,FLux%dim,ele_ngi(Flux,ele)) :: Grad_Flux_gi,&
1127 & tensor_gi
1128 real, dimension(ele_ngi(X,ele)) :: detwei, Q_gi, D_gi, &
1129 & D_old_gi,div_flux_gi, detwei_l, detJ
1130 real, dimension(ele_loc(D,ele)) :: D_val
1131 real, dimension(ele_loc(Q,ele)) :: Q_val
1132 real, dimension(mesh_dim(X), X%dim, ele_ngi(X,ele)) :: J
1133 type(element_type), pointer :: Q_shape, Flux_shape
1134 integer :: loc,dim1,dim2,gi
1135 real :: tau, alpha, tol, area, h,c_sc,disc_indicator
1136 real, dimension(ele_ngi(X,ele),ele_ngi(X,ele)) :: Metric, MetricT
1137 real, dimension(mesh_dim(X),ele_ngi(X,ele)) :: gradQ
1138 type(element_type), pointer :: D_shape
1139 !
1140
1141 ! Get J and detwei
1142 call compute_jacobian(ele_val(X,ele), ele_shape(X,ele), detwei&
1143 &=detwei, J=J, detJ=detJ)
1144
1145 D_shape => ele_shape(D,ele)
1146 Q_shape => ele_shape(Q,ele)
1147 Flux_shape => ele_shape(Flux,ele)
1148 D_val = invert_pi_ele(ele_val(D,ele),D_shape,detwei)
1149 D_gi = matmul(transpose(D_shape%n),D_val)
1150 D_val = invert_pi_ele(ele_val(D_old,ele),D_shape,detwei)
1151 D_old_gi = matmul(transpose(D_shape%n),D_val)
1152 Q_gi = ele_val_at_quad(Q,ele)
1153 Flux_gi = ele_val_at_quad(Flux,ele)
1154 U_nl_gi = ele_val_at_quad(U_nl,ele)
1155 Q_val = ele_val(Q,ele)
1156
1157 !Equations
1158 ! D^{n+1} = D^n + \pi div Flux
1159 ! If define \int_K\phi\hat{D}/J J dS = \int_K\phi D J dS, then
1160 ! <\phi,\pi(\hat{D}/J)> = <\phi, D> for all \phi,
1161 ! => \pi \hat{D} = D
1162 ! \hat{D}^{n+1}/J = \hat{D}^n/J + div Flux
1163
1164 ! So (integrating by parts)
1165 ! <\gamma, \hat{D}^{n+1}/J> =
1166 ! <\gamma, D^n/J> - <\nabla\gamma, F>
1167
1168 ! and a consistent theta-method for Q is
1169 ! <\gamma,\hat{D}^{n+1}Q^{n+1}/J> =
1170 ! <\gamma, \hat{D}^nQ^n/J> -
1171 ! \theta<\nabla\gamma,FQ^{n+1}>
1172 ! -(1-\theta)<\nabla\gamma,FQ^n>
1173
1174 detwei_l = Q_shape%quadrature%weight
1175 !Advection terms
1176 !! <-grad gamma, FQ >
1177 !! Can be evaluated locally using pullback
1178 tmp_mat = dshape_dot_vector_shape(&
1179 Q_shape%dn,Flux_gi,Q_shape,detwei_l)
1180 l_adv_mat = t_theta*tmp_mat
1181 l_rhs = -(1-t_theta)*matmul(tmp_mat,Q_val)
1182
1183 !Mass terms
1184 !! <gamma, QD>
1185 !! Requires transformed integral
1186 tmp_mat = shape_shape(ele_shape(Q,ele),ele_shape(Q,ele),D_gi*detwei_l)
1187 l_adv_mat = l_adv_mat + tmp_mat
1188 tmp_mat = shape_shape(ele_shape(Q,ele),ele_shape(Q,ele),D_old_gi*detwei_l)
1189 l_rhs = l_rhs + matmul(tmp_mat,Q_val)
1190
1191 !Laplacian filter options
1192 if(have_option(trim(Q%option_path)//'/prognostic/spatial_discretisation/&
1193 &continuous_galerkin/laplacian_filter')) then
1194 FLExit('Laplacian filter not coded yet.')
1195 end if
1196 !Streamline upwinding options
1197 if(have_option(trim(Q%option_path)//'/prognostic/spatial_discretisation/&
1198 &continuous_galerkin/streamline_upwinding')) then
1199 !! Replace test function \gamma with \gamma + \tau u\cdot\nabla\gamma
1200 call get_option(trim(Q%option_path)//'/prognostic/spatial_discretisat&
1201 &ion/continuous_galerkin/streamline_upwinding/alpha',alpha)
1202 call get_option(trim(Q%option_path)//'/prognostic/spatial_discretisat&
1203 &ion/continuous_galerkin/streamline_upwinding/tol',tol)
1204
1205 !Gradients of fluxes
1206 Grad_flux_gi = ele_grad_at_quad(Flux,ele,Flux_shape%dn)
1207 div_flux_gi = 0.
1208 tensor_gi = 0.
1209 do dim1 = 1, Flux%dim
1210 div_flux_gi = div_flux_gi + grad_flux_gi(dim1,dim1,:)
1211 do dim2 = 1, Flux%dim
1212 tensor_gi(dim1,dim2,:) = U_nl_gi(dim1,:)*Flux_gi(dim2,:)
1213 end do
1214 end do
1215 !real, dimension(mesh_dim(X), X%dim, ele_ngi(X,ele)) :: J
1216 do gi = 1, ele_ngi(X,ele)
1217 U_cart_gi(:,gi) = matmul(transpose(J(:,:,gi)),U_nl_gi(:,gi))/detJ(gi)
1218 end do
1219
1220 if(maxval(sqrt(sum(U_cart_gi**2,1)))<tol) then
1221 tau = 0.
1222 else
1223 area = sum(detwei)
1224 h = sqrt(4*area/sqrt(3.))
1225 tau = h*alpha/maxval(sqrt(sum(U_cart_gi**2,1)))
1226 end if
1227 !! Mass terms
1228 !! tau < u . grad gamma, QD>
1229 !! = tau < grad gamma, u QD>
1230 !! can do locally because of pullback formula
1231 tmp_mat = dshape_dot_vector_shape(&
1232 Q_shape%dn,U_nl_gi,Q_shape,D_gi*detwei_l/detJ)
1233 l_adv_mat = l_adv_mat + tau*tmp_mat
1234 tmp_mat = dshape_dot_vector_shape(&
1235 Q_shape%dn,U_nl_gi,Q_shape,D_old_gi*detwei_l/detJ)
1236 l_rhs = l_rhs + tau*matmul(tmp_mat,Q_val)
1237 !! Advection terms
1238 !! tau < u. grad gamma, div (F Q)>
1239 !! = tau <grad gamma, u (F.grad Q + Q div F)>
1240 !! can do locally because of pullback formula
1241 !! tensor = u outer F (F already contains factor of dt)
1242 tmp_mat = dshape_dot_vector_shape(&
1243 Q_shape%dn,U_nl_gi,Q_shape,div_flux_gi*detwei_l/detJ) &
1244 + dshape_tensor_dshape(&
1245 Q_shape%dn,tensor_gi,Q_shape%dn,detwei_l/detJ)
1246 l_adv_mat = l_adv_mat - tau*t_theta*tmp_mat
1247 l_rhs = l_rhs + tau*(1-t_theta)*matmul(tmp_mat,Q_val)
1248 end if
1249 !Discontinuity capturing options
1250 if(have_option(trim(Q%option_path)//'/prognostic/spatial_discretisation/&
1251 &continuous_galerkin/discontinuity_capturing')) then
1252 ! call have_option(&
1253 ! &trim(Q%option_path)//'/prognostic/spatial_discretisation/&
1254 ! &continuous_galerkin/discontinuity_capturing/&
1255 ! &scaling_coefficient',c_sc)
1256
1257 ! do gi=1,ele_ngi(Q,ele)
1258 ! Metric(:,:,gi)=matmul(J(:,:,gi), transpose(J(:,:,gi)))/detJ(gi)
1259 ! end do
1260 ! MetricT(1,1,:) = MetricT(2,2,:)
1261 ! MetricT(2,2,:) = MetricT(1,1,:)
1262 ! MetricT(1,2,:) =-MetricT(2,1,:)
1263 ! MetricT(2,1,:) =-MetricT(1,2,:)
1264
1265 ! area = sum(detwei)
1266 ! D_gi = ele_val_at_quad(D_old,ele)
1267 ! tmp_mat = dshape_tensor_dshape(Q_shape%dn, &
1268 ! area*D_gi*MetricT, Q_shape%dn,&
1269 ! Q_shape%quadrature%weight)
1270 ! l_rhs = l_rhs - dt*c_sc*matmul(tmp_mat,Q_val)
1271 FLAbort('not ready yet')
1272 end if
1273
1274 call addto(Q_rhs,ele_nodes(Q_rhs,ele),l_rhs)
1275 call addto(adv_mat,ele_nodes(Q_rhs,ele),ele_nodes(Q_rhs,ele),&
1276 l_adv_mat)
1277
1278 end subroutine construct_advection_cg_tracer_ele
1279
1280 subroutine construct_pv_flux_ele(QFlux,Q,Q_old,D,D_old,&
1281 Flux,X,down,U_nl,t_theta,ele)
1282 type(scalar_field), intent(in) :: Q,Q_old,D,D_old
1283 type(vector_field), intent(inout) :: QFlux
1284 type(vector_field), intent(in) :: X, Flux, Down, U_nl
1285 integer, intent(in) :: ele
1286 real, intent(in) :: t_theta
1287 !
1288 real, dimension(mesh_dim(X),ele_loc(QFlux,ele)) :: QFlux_perp_rhs
1289 real, dimension(Flux%dim,ele_ngi(Flux,ele)) :: Flux_gi, Flux_perp_gi,&
1290 QFlux_gi
1291 real, dimension(ele_ngi(X,ele)) :: Q_gi,Q_old_gi,D_gi,D_old_gi,Qbar_gi
1292 real, dimension(ele_loc(D,ele)) :: D_val
1293 real, dimension(X%dim, ele_ngi(Q, ele)) :: up_gi
1294 real, dimension(mesh_dim(QFlux),mesh_dim(QFlux),ele_loc(QFlux,ele)&
1295 &,ele_loc(QFlux,ele)) :: l_u_mat
1296 real, dimension(mesh_dim(QFlux)*ele_loc(QFlux,ele),mesh_dim(QFlux)&
1297 &*ele_loc(QFlux,ele)) :: solve_mat
1298 real, dimension(ele_loc(Q,ele)) :: Q_test1_rhs, Q_test2_rhs
1299 real, dimension(mesh_dim(Qflux)*ele_loc(QFlux,ele)) :: solve_rhs
1300 real, dimension(mesh_dim(Qflux),ele_ngi(Qflux,ele)) :: &
1301 contravariant_velocity_gi, velocity_perp_gi
1302 type(element_type), pointer :: Q_shape, QFlux_shape, Flux_shape
1303 integer :: loc, orientation,gi, dim1, dim2
1304 real, dimension(mesh_dim(X), X%dim, ele_ngi(X,ele)) :: J
1305 real, dimension(ele_ngi(X,ele)) :: detwei, detJ, detwei_l, div_flux_gi
1306 real, dimension(mesh_dim(Q),ele_ngi(Q,ele)) :: gradQ_gi
1307 real, dimension(Flux%dim,FLux%dim,ele_ngi(Flux,ele)) :: Grad_Flux_gi
1308 real, dimension(U_nl%dim,ele_ngi(U_nl,ele)) :: U_nl_gi
1309 real, dimension(X%dim,ele_ngi(U_nl,ele)) :: U_cart_gi
1310 real :: residual, alpha, tol, tau, area, h
1311 type(element_type), pointer :: D_shape
1312 !
1313
1314 D_shape => ele_shape(D,ele)
1315
1316 call compute_jacobian(ele_val(X,ele), ele_shape(X,ele), J, detwei, detJ)
1317
1318 Q_shape => ele_shape(Q,ele)
1319 Flux_shape => ele_shape(Flux,ele)
1320 QFlux_shape => ele_shape(QFlux,ele)
1321 Q_gi = ele_val_at_quad(Q,ele)
1322 Q_old_gi = ele_val_at_quad(Q_old,ele)
1323 Qbar_gi = t_theta*Q_gi + (1-t_theta)*Q_old_gi
1324 D_val = invert_pi_ele(ele_val(D,ele),D_shape,detwei)
1325 D_gi = matmul(transpose(D_shape%n),D_val)
1326 D_val = invert_pi_ele(ele_val(D_old,ele),D_shape,detwei)
1327 D_old_gi = matmul(transpose(D_shape%n),D_val)
1328 Flux_gi = ele_val_at_quad(Flux,ele)
1329 U_nl_gi = ele_val_at_quad(U_nl,ele)
1330
1331 detwei_l = Q_shape%quadrature%weight
1332 up_gi = -ele_val_at_quad(down,ele)
1333
1334 call get_up_gi(X,ele,up_gi,orientation)
1335
1336 !Equations
1337 ! <\nabla \gamma, (-v,u)> = <(\gamma_x, \gamma_y),(-v,u)>
1338 ! = <(\gamma_y,-\gamma_x),( u,v)>
1339 ! = <-\nabla^\perp\gamma,(u,v)>
1340
1341 ! and a consistent theta-method for Q is
1342 ! <\gamma, D^{n+1}Q^{n+1}> = <\gamma, D^nQ^n> -
1343 ! \theta<\nabla\gamma,FQ^{n+1}>
1344 ! -(1-\theta)<\nabla\gamma,FQ^n>
1345 ! So vorticity update is
1346 ! <\gamma, \zeta^{n+1}> = <-\nabla^\perp\gamma,u^{n+1}>
1347 != <-\nabla^\perp\gamma,u^n>
1348 ! +\theta<-\nabla^\perp\gamma,(FQ^{n+1})^\perp>
1349 ! +(1-\theta)<-\nabla^\perp\gamma,(FQ^n)^\perp>
1350 ! taking w = -\nabla^\perp\gamma,
1351 ! <w,u^{n+1}>=<w,u^n> + <w,F(\theta Q^{n+1}+(1-\theta)Q^n)^\perp>
1352 ! <w,u^{n+1}>=<w,u^n> + <w,\bar{Q}^\perp>
1353 ! We actually store the inner product with test function (FQ_rhs)
1354 ! (avoids having to do a solve)
1355
1356 do gi = 1, ele_ngi(QFlux,ele)
1357 QFlux_gi(:,gi) = Flux_gi(:,gi)*Qbar_gi(gi)
1358 end do
1359
1360 !! Additional dissipative terms.
1361 !! They all take the form
1362 !! <\nabla gamma, G>
1363 !! Which can be evaluated in local coordinates due to pullback magic
1364
1365 !Laplacian filter options
1366 if(have_option(trim(Q%option_path)//'/prognostic/spatial_discretisation/&
1367 &continuous_galerkin/laplacian_filter')) then
1368 FLExit('Laplacian filter not coded yet.')
1369 end if
1370 !Streamline upwinding options
1371 if(have_option(trim(Q%option_path)//'/prognostic/spatial_discretisation/&
1372 &continuous_galerkin/streamline_upwinding')) then
1373 !! Replace test function \gamma with \gamma + \tau u\cdot\nabla\gamma
1374 call get_option(trim(Q%option_path)//'/prognostic/spatial_discretisat&
1375 &ion/continuous_galerkin/streamline_upwinding/alpha',alpha)
1376 call get_option(trim(Q%option_path)//'/prognostic/spatial_discretisat&
1377 &ion/continuous_galerkin/streamline_upwinding/tol',tol)
1378
1379 !Gradients of fluxes
1380 Grad_flux_gi = ele_grad_at_quad(Flux,ele,Flux_shape%dn)
1381 div_flux_gi = 0.
1382 do dim1 = 1, Flux%dim
1383 div_flux_gi = div_flux_gi + grad_flux_gi(dim1,dim1,:)
1384 end do
1385 !real, dimension(mesh_dim(X), X%dim, ele_ngi(X,ele)) :: J
1386 do gi = 1, ele_ngi(X,ele)
1387 U_cart_gi(:,gi) = matmul(transpose(J(:,:,gi)),U_nl_gi(:,gi))/detJ(gi)
1388 end do
1389
1390 if(maxval(sqrt(sum(U_cart_gi**2,1)))<tol) then
1391 tau = 0.
1392 else
1393 area = sum(detwei)
1394 h = sqrt(4*area/sqrt(3.))
1395 tau = h*alpha/maxval(sqrt(sum(U_cart_gi**2,1)))
1396 end if
1397 !! Mass terms
1398 !! tau < u . grad gamma, -(QD)^{n+1}+(QD)^n>
1399 !! = tau < grad gamma, u(-(QD)^{n+1}+(QD)^n)>
1400 !! can do locally because of pullback formula
1401 !! Extra flux is tau u\Delta(QD)
1402 !! minus sign from definition of vorticity
1403 !! tau
1404 do gi = 1, ele_ngi(Q,ele)
1405 QFLux_gi(:,gi) = QFlux_gi(:,gi) + &
1406 & tau*U_nl_gi(:,gi)*(Q_gi(gi)*D_gi(gi)-Q_old_gi(gi)&
1407 &*D_old_gi(gi))/detJ(gi)
1408
1409 end do
1410 !! Advection terms
1411 !! tau < u. grad gamma, div (F\bar{Q})>
1412 !! can do locally because of pullback formula
1413 !! = <grad gamma, tau u (F.grad\bar{Q} + \bar{Q} div F)/det J>_{Ehat}
1414 !! minus sign because of definition of vorticity
1415
1416 GradQ_gi = t_theta*ele_grad_at_quad(Q,ele,Q_shape%dn) + &
1417 (1-t_theta)*ele_grad_at_quad(Q_old,ele,Q_shape%dn)
1418
1419 do gi = 1, ele_ngi(Q,ele)
1420 QFlux_gi(:,gi) = QFlux_gi(:,gi) - tau*u_nl_gi(:,gi)*(&
1421 & sum(Flux_gi(:,gi)*gradQ_gi(:,gi)) + &
1422 & div_flux_gi(gi)*Qbar_gi(gi))/detJ(gi)
1423 end do
1424 end if
1425 !Discontinuity capturing options
1426 if(have_option(trim(Q%option_path)//'/prognostic/spatial_discretisation/&
1427 &continuous_galerkin/discontinuity_capturing')) then
1428 FLExit('Discontinuity capturing not coded yet.')
1429 end if
1430
1431 !! Evaluate
1432 !! < w, F^\perp > in local coordinates
1433
1434 Flux_perp_gi(1,:) = -orientation*QFlux_gi(2,:)
1435 Flux_perp_gi(2,:) = orientation*QFlux_gi(1,:)
1436
1437 QFlux_perp_rhs = shape_vector_rhs(QFlux_shape,Flux_perp_gi,&
1438 & QFlux_shape%quadrature%weight)
1439
1440 call set(QFlux,ele_nodes(QFlux,ele),QFlux_perp_rhs)
1441
1442 end subroutine construct_pv_flux_ele
1443
1444 subroutine test_pv_flux_ele(Qtest1,Qtest2,QFlux,Q,Q_old,D,D_old,&
1445 Flux,X,down,t_theta,ele)
1446 type(scalar_field), intent(in) :: Q,Q_old,D,D_old
1447 type(scalar_field), intent(inout) :: Qtest1, Qtest2
1448 type(vector_field), intent(in) :: X, Flux, Down,QFlux
1449 integer, intent(in) :: ele
1450 real, intent(in) :: t_theta
1451 !
1452 real, dimension(mesh_dim(X),ele_loc(QFlux,ele)) :: QFlux_perp_rhs
1453 real, dimension(ele_ngi(X,ele)) :: Q_gi,Q_old_gi,D_gi,D_old_gi
1454 real, dimension(Flux%dim,ele_ngi(Flux,ele)) :: Flux_gi, Flux_perp_gi
1455 real, dimension(X%dim, ele_ngi(Q, ele)) :: up_gi
1456 real, dimension(mesh_dim(QFlux), mesh_dim(QFlux), ele_ngi(QFlux,ele)) &
1457 :: Metric, Metricf
1458 real, dimension(mesh_dim(QFlux),mesh_dim(QFlux),ele_loc(QFlux,ele)&
1459 &,ele_loc(QFlux,ele)) :: l_u_mat
1460 real, dimension(mesh_dim(QFlux)*ele_loc(QFlux,ele),mesh_dim(QFlux)&
1461 &*ele_loc(QFlux,ele)) :: solve_mat
1462 real, dimension(ele_loc(Q,ele)) :: Q_test1_rhs, Q_test2_rhs
1463 real, dimension(mesh_dim(Qflux)*ele_loc(QFlux,ele)) :: solve_rhs
1464 real, dimension(mesh_dim(Qflux),ele_ngi(Qflux,ele)) :: &
1465 contravariant_velocity_gi, velocity_perp_gi
1466 type(element_type), pointer :: Q_shape, QFlux_shape,D_shape
1467 integer :: loc, orientation,gi, dim1, dim2
1468 real, dimension(mesh_dim(X), X%dim, ele_ngi(X,ele)) :: J
1469 real, dimension(ele_ngi(X,ele)) :: detwei, detJ
1470 real, dimension(X%dim, X%dim, ele_ngi(X,ele)) :: rot
1471 real, dimension(ele_loc(D,ele)) :: D_val
1472
1473 D_shape => ele_shape(D,ele)
1474 Q_shape => ele_shape(Q,ele)
1475 QFlux_shape => ele_shape(QFlux,ele)
1476 Q_gi = ele_val_at_quad(Q,ele)
1477 Q_old_gi = ele_val_at_quad(Q_old,ele)
1478
1479 call compute_jacobian(ele_val(X,ele), ele_shape(X,ele), J, detwei, detJ)
1480 D_val = invert_pi_ele(ele_val(D,ele),D_shape,detwei)
1481 D_gi = matmul(transpose(D_shape%n),D_val)
1482 D_val = invert_pi_ele(ele_val(D_old,ele),D_shape,detwei)
1483 D_old_gi = matmul(transpose(D_shape%n),D_val)
1484
1485 Flux_gi = ele_val_at_quad(Flux,ele)
1486 Qflux_perp_rhs = ele_val(Qflux,ele)
1487
1488 do gi = 1, ele_ngi(QFlux,ele)
1489 Flux_gi(:,gi) = Flux_gi(:,gi)*(t_theta*Q_gi(gi) + &
1490 (1-t_theta)*Q_old_gi(gi))
1491 end do
1492
1493 up_gi = -ele_val_at_quad(down,ele)
1494 call get_up_gi(X,ele,up_gi,orientation)
1495
1496 do gi=1, ele_ngi(QFlux,ele)
1497 rot(1,:,gi)=(/0.,-up_gi(3,gi),up_gi(2,gi)/)
1498 rot(2,:,gi)=(/up_gi(3,gi),0.,-up_gi(1,gi)/)
1499 rot(3,:,gi)=(/-up_gi(2,gi),up_gi(1,gi),0./)
1500 end do
1501 do gi=1,ele_ngi(QFlux,ele)
1502 Metric(:,:,gi)=matmul(J(:,:,gi), transpose(J(:,:,gi)))/detJ(gi)
1503 Metricf(:,:,gi)=matmul(J(:,:,gi), &
1504 matmul(rot(:,:,gi), transpose(J(:,:,gi))))/detJ(gi)
1505 end do
1506
1507 l_u_mat = shape_shape_tensor(qflux_shape, qflux_shape, &
1508 qflux_shape%quadrature%weight,Metric)
1509 solve_mat = 0.
1510 solve_rhs = 0.
1511 do dim1 = 1, mesh_dim(qflux)
1512 do dim2 = 1, mesh_dim(qflux)
1513 solve_mat( (dim1-1)*ele_loc(qflux,ele)+1:dim1*ele_loc(qflux,ele),&
1514 (dim2-1)*ele_loc(qflux,ele)+1:dim2*ele_loc(qflux,ele)) = &
1515 l_u_mat(dim1,dim2,:,:)
1516 end do
1517 solve_rhs( (dim1-1)*ele_loc(qflux,ele)+1:dim1*ele_loc(qflux,ele))&
1518 = QFlux_perp_rhs(dim1,:)
1519 end do
1520 call solve(solve_mat,solve_rhs)
1521 !Don't need to include constraints since u eqn is
1522 ! <w,Q^\perp> + <<[w],\lambda>> + \sum_i C_i\cdot w \Gamma_i = RHS
1523 ! but if w=-\nabla^\perp\gamma then [w]=0 and C_i\cdot w=0.
1524
1525 do dim1 = 1, mesh_dim(qflux)
1526 flux_perp_gi(dim1,:) = matmul(transpose(QFlux_shape%n),&
1527 solve_rhs((dim1-1)*ele_loc(qflux,ele)+1:dim1*ele_loc(qflux&
1528 &,ele)))
1529 end do
1530 !Now compute vorticity
1531 do gi=1,ele_ngi(X,ele)
1532 Metric(:,:,gi)=matmul(J(:,:,gi), transpose(J(:,:,gi)))/detJ(gi)
1533 contravariant_velocity_gi(:,gi) = &
1534 matmul(Metric(:,:,gi),Flux_perp_gi(:,gi))
1535 end do
1536
1537 !!Q_test1_rhs contains -<\nabla\gamma,Q>
1538
1539 select case(mesh_dim(X))
1540 case (2)
1541 velocity_perp_gi(1,:) = -contravariant_velocity_gi(2,:)
1542 velocity_perp_gi(2,:) = contravariant_velocity_gi(1,:)
1543 Q_test1_rhs = dshape_dot_vector_rhs(Q%mesh%shape%dn, &
1544 velocity_perp_gi,Q%mesh%shape%quadrature%weight)
1545 Q_test1_rhs = q_test1_rhs*orientation
1546 case default
1547 FLAbort('Exterior derivative not implemented for given mesh dimension')
1548 end select
1549
1550 !!Q_test2_rhs contains <\gamma,Q^{n+1}\tilde{D}^{n+1}-
1551 !! Q^n\tilde{D}^n>
1552
1553 Q_test2_rhs = shape_rhs(ele_shape(Q,ele),(Q_gi*D_gi-Q_old_gi&
1554 &*D_old_gi)*Q_shape%quadrature%weight)
1555
1556 call addto(Qtest1,ele_nodes(Q,ele),Q_test1_rhs)
1557 call addto(Qtest2,ele_nodes(Q,ele),Q_test2_rhs)
1558
1559 end subroutine test_pv_flux_ele
1560
1561 subroutine get_PV(state,PV,velocity,D,path)
1562 type(state_type), intent(inout) :: state
1563 type(vector_field), intent(in) :: velocity
1564 type(scalar_field), intent(inout) :: PV
1565 type(scalar_field), intent(in) :: D
1566 character(len=OPTION_PATH_LEN), optional :: path
1567 !
1568 type(vector_field), pointer :: X, down
1569 type(scalar_field), pointer :: Coriolis
1570 type(csr_matrix) :: pv_mass_matrix
1571 type(csr_sparsity), pointer :: pv_mass_sparsity, curl_sparsity
1572 type(scalar_field) :: pv_rhs
1573 integer :: ele
1574 !!DEbugging
1575 integer ::dim1
1576 real, dimension(3,3) :: X_val
1577
1578 ewrite(1,*) 'subroutine get_pv'
1579
1580 pv_mass_sparsity => &
1581 get_csr_sparsity_firstorder(state, pv%mesh, pv%mesh)
1582 call allocate(pv_mass_matrix,pv_mass_sparsity)
1583 call zero(pv_mass_matrix)
1584
1585 X=>extract_vector_field(state, "Coordinate")
1586 down=>extract_vector_field(state, "GravityDirection")
1587 Coriolis=>extract_scalar_field(state, "Coriolis")
1588
1589 call allocate(pv_rhs, pv%mesh, 'PvRHS')
1590 call zero(pv_rhs)
1591
1592 do ele = 1, ele_count(pv)
1593 call assemble_pv_ele(pv_mass_matrix,pv_rhs,velocity,D,Coriolis,X&
1594 &,down,ele)
1595 end do
1596
1597 if(present(path)) then
1598 call petsc_solve(pv,pv_mass_matrix,pv_rhs,option_path=path)
1599 else
1600 call petsc_solve(pv,pv_mass_matrix,pv_rhs)
1601 end if
1602
1603 call deallocate(pv_mass_matrix)
1604 call deallocate(pv_rhs)
1605
1606 end subroutine get_pv
1607
1608 subroutine assemble_PV_ele(pv_mass_matrix,pv_rhs,velocity,D,Coriolis,X&
1609 &,down,ele)
1610 !!Subroutine to compute the pv from a *local velocity* field
1611 type(vector_field), intent(in) :: X, down, velocity
1612 type(scalar_field), intent(inout) :: pv_rhs
1613 type(scalar_field), intent(in) :: D, Coriolis
1614 type(csr_matrix), intent(inout) :: pv_mass_matrix
1615 integer, intent(in) :: ele
1616 !
1617 real, dimension(mesh_dim(X), X%dim, ele_ngi(X,ele)) :: J
1618 real, dimension(ele_ngi(X,ele)) :: detwei, detJ, l_f, l_d
1619 real, dimension(ele_loc(D,ele)) :: D_val
1620 real, dimension(ele_loc(pv_rhs,ele),ele_loc(pv_rhs,ele))&
1621 :: l_mass_mat
1622 real, dimension(ele_loc(pv_rhs,ele))&
1623 :: l_rhs
1624 real, dimension(mesh_dim(velocity),ele_ngi(velocity,ele)) :: velocity_gi,&
1625 contravariant_velocity_gi, velocity_perp_gi
1626 real, dimension(X%dim, ele_ngi(X,ele)) :: up_gi
1627 integer :: orientation, gi
1628 real, dimension(mesh_dim(X), mesh_dim(X), ele_ngi(X,ele)) :: Metric
1629 type(element_type), pointer :: D_shape
1630 !
1631
1632 D_shape => ele_shape(D,ele)
1633
1634 up_gi = -ele_val_at_quad(down,ele)
1635 call get_up_gi(X,ele,up_gi,orientation)
1636
1637 call compute_jacobian(ele_val(X,ele), ele_shape(X,ele), J, detwei, detJ)
1638 D_val = ele_val(D,ele)
1639 D_val = invert_pi_ele(D_val,D_shape,detwei)
1640 l_d = matmul(transpose(D_shape%n),D_val)
1641
1642 l_mass_mat = shape_shape(ele_shape(pv_rhs,ele),&
1643 ele_shape(pv_rhs,ele),l_d*d_shape%quadrature%weight)
1644
1645 velocity_gi = ele_val_at_quad(velocity,ele)
1646 do gi=1,ele_ngi(X,ele)
1647 Metric(:,:,gi)=matmul(J(:,:,gi), transpose(J(:,:,gi)))/detJ(gi)
1648 contravariant_velocity_gi(:,gi) = &
1649 matmul(Metric(:,:,gi),velocity_gi(:,gi))
1650 end do
1651
1652 !Relative vorticity
1653
1654 ! pv = \nabla^\perp\cdot u = v_x - u_y
1655 ! < \gamma, pv > = <\gamma, v_x - u_y> = <-\gamma_x,v>+<\gamma_y,u>
1656 ! < -\nabla^\perp \gamma, J^TJ u/detJ> in local coordinates
1657 ! < \nabla \gamma, (J^TJ u)^\perp/detJ> in local coordinates
1658 ! requires us to know the orientation of the manifold
1659
1660 select case(mesh_dim(X))
1661 case (2)
1662 velocity_perp_gi(1,:) = -contravariant_velocity_gi(2,:)
1663 velocity_perp_gi(2,:) = contravariant_velocity_gi(1,:)
1664 l_rhs = dshape_dot_vector_rhs(pv_rhs%mesh%shape%dn, &
1665 velocity_perp_gi,X%mesh%shape%quadrature%weight)
1666 l_rhs = l_rhs*orientation
1667 case default
1668 FLAbort('Exterior derivative not implemented for given mesh dimension')
1669 end select
1670
1671 ! Coriolis term
1672 l_f = ele_val_at_quad(Coriolis,ele)
1673 l_rhs = l_rhs + shape_rhs(ele_shape(pv_rhs,ele),l_f*detwei)
1674
1675 call addto(pv_mass_matrix,ele_nodes(pv_rhs,ele),&
1676 ele_nodes(pv_rhs,ele),l_mass_mat)
1677 call addto(pv_rhs,ele_nodes(pv_rhs,ele),l_rhs)
1678
1679 end subroutine assemble_pv_ele
1680
1681 function invert_pi_ele(D_val_in,D_shape,detwei) result (D_val_out)
1682 real, intent(in), dimension(:) :: D_val_in
1683 type(element_type), intent(in) :: D_shape
1684 real, intent(in), dimension(:) :: detwei
1685 real, dimension(size(D_val_in)) :: D_val_out !The result
1686 !
1687 real, dimension(size(D_val_in),size(D_val_in)) :: proj_mat, &
1688 & mass_mat
1689
1690 !! \sum_i \phi_i \hat{D}_i w_i = \sum_i \phi_i D_i J_i w_i.
1691
1692 proj_mat = shape_shape(D_shape,D_shape,D_shape%quadrature%weight)
1693 mass_mat = shape_shape(D_shape,D_shape,detwei)
1694 D_val_out = matmul(mass_mat,D_val_in)
1695 call solve(proj_mat,D_val_out)
1696
1697 end function invert_pi_ele
1698
1699 subroutine compute_U_residual(UResidual,oldU,oldD,newU,newD,PVFlux,state)
1700 !!< Compute the residual in the U equation, given PVFlux computed
1701 !!< from the PV advection equation. Note that the PVFlux has
1702 !!< already been perped, and multiplied by test functions.
1703 !!< Residual will be multiplied by test functions
1704 type(vector_field), intent(inout) :: UResidual
1705 type(vector_field), intent(in) :: oldU,newU,PVFlux
1706 type(scalar_field), intent(in) :: oldD,newD
1707 type(state_type), intent(inout) :: state
1708 !
1709 integer :: ele, stat
1710 type(vector_field), pointer :: X
1711 type(scalar_field), pointer :: Orography
1712 real :: dt, theta,g
1713 type(scalar_field), target :: l_orography
1714 logical :: have_orography
1715
1716 ewrite(1,*) ' subroutine compute_U_residual('
1717
1718 X=>extract_vector_field(state, "Coordinate")
1719 call get_option("/timestepping/timestep", dt)
1720 call get_option("/timestepping/theta",theta)
1721 call get_option("/physical_parameters/gravity/magnitude", g)
1722
1723 Orography=>extract_scalar_field(state, "Orography", stat)
1724 have_orography = (stat==0)
1725 if(.not.have_orography) then
1726 call allocate(l_orography,X%mesh,"L_Orography")
1727 orography => l_orography
1728 call zero(l_orography)
1729 end if
1730
1731 call zero(UResidual)
1732 do ele = 1, ele_count(UResidual)
1733 call compute_U_residual_ele(UResidual,oldU,oldD,newU,newD,PVFlux,&
1734 orography,X,theta&
1735 &,dt,g,ele)
1736 end do
1737
1738 if(.not.have_orography) then
1739 call deallocate(l_orography)
1740 end if
1741
1742 ewrite(1,*) 'END subroutine compute_U_residual('
1743
1744 end subroutine compute_U_residual
1745
1746 subroutine compute_U_residual_ele(UResidual,oldU,oldD,newU,newD,PVFlux,&
1747 orography,X,theta&
1748 &,dt,g,ele)
1749 type(vector_field), intent(inout) :: UResidual
1750 type(vector_field), intent(in) :: oldU,newU,PVFlux,X
1751 type(scalar_field), intent(in) :: oldD,newD,orography
1752 real, intent(in) :: dt,theta,g
1753 integer, intent(in) :: ele
1754 !
1755 real, dimension(ele_ngi(X,ele)) :: detwei, detJ
1756 real, dimension(mesh_dim(X), X%dim, ele_ngi(X,ele)) :: J
1757 real, dimension(mesh_dim(oldU), mesh_dim(oldU)) :: Metric
1758 real, dimension(mesh_dim(oldU), ele_ngi(X,ele)) :: U_rhs, newU_rhs
1759 real, dimension(ele_ngi(X,ele)) :: D_gi, newD_gi, D_bar_gi, K_bar_gi
1760 real, dimension(mesh_dim(X), ele_ngi(X,ele)) :: U_gi,newU_gi
1761 real, dimension(X%dim, ele_ngi(X,ele)) :: U_cart_gi,newU_cart_gi
1762 real, dimension(mesh_dim(oldU),ele_loc(UResidual,ele)) :: UR_rhs
1763 integer :: gi
1764 type(element_type), pointer :: U_shape
1765 real, dimension(ele_ngi(X,ele)) :: orography_gi
1766
1767 !r[w] = <w,u^{n+1}-u^n> - <w,FQ^\perp>
1768 ! - dt*<div w, g\bar{h} + \bar{|u|^2/2}>
1769
1770 call compute_jacobian(ele_val(X,ele), ele_shape(X,ele), &
1771 detwei=detwei, J=J, detJ=detJ)
1772 U_shape=>ele_shape(oldU, ele)
1773
1774 !Get all the variables at the quadrature points
1775 D_gi = ele_val_at_quad(oldD,ele)
1776 orography_gi = ele_val_at_quad(orography,ele)
1777 newD_gi = ele_val_at_quad(newD,ele)
1778 U_gi = ele_val_at_quad(oldU,ele)
1779 newU_gi = ele_val_at_quad(newU,ele)
1780 U_rhs = 0.
1781 newU_rhs = 0.
1782 do gi=1,ele_ngi(oldU,ele)
1783 Metric=matmul(J(:,:,gi), transpose(J(:,:,gi)))/detJ(gi)
1784 U_rhs(:,gi) = matmul(Metric,U_gi(:,gi))
1785 newU_rhs(:,gi) = matmul(Metric,newU_gi(:,gi))
1786 end do
1787 U_cart_gi = 0.
1788 newU_cart_gi = 0.
1789 do gi = 1, ele_ngi(oldU,ele)
1790 U_cart_gi(:,gi) = matmul(transpose(J(:,:,gi)),U_gi(:,gi))/detJ(gi)
1791 newU_cart_gi(:,gi) = matmul(transpose(J(:,:,gi)),newU_gi(:,gi))/detJ(gi)
1792 end do
1793
1794 !!Residual is
1795 !! U_new - (U_old + dt*Q^\perp - dt grad(g\bar{D} + \bar{K}))
1796 !First the PV Flux (perped, contains factor of dt)
1797 UR_rhs = -ele_val(PVFlux,ele)
1798 !Now the time derivative term
1799 UR_rhs = UR_rhs + shape_vector_rhs(U_shape,newU_rhs-U_rhs,&
1800 U_shape%quadrature%weight)
1801 !Now the gradient terms (done in local coordinates)
1802 D_bar_gi = theta*newD_gi + (1-theta)*D_gi + orography_gi
1803 K_bar_gi = 0.5*(theta*sum(newU_cart_gi**2,1)+(1-theta)*sum(U_cart_gi**2,1))
1804 !integration by parts, so minus sign
1805 UR_rhs = UR_rhs - dt * dshape_rhs(U_shape%dn,&
1806 (g*D_bar_gi + K_bar_gi)*U_shape%quadrature%weight)
1807
1808 call set(UResidual,ele_nodes(UResidual,ele),UR_rhs)
1809
1810 end subroutine compute_U_residual_ele
1811
1812 subroutine solve_vector_advection_dg_subcycle(field_name, state, velocity_name)
1813 !!< Construct and solve the advection equation for the given
1814 !!< field using discontinuous elements.
1815
1816 !! Name of the field to be solved for.
1817 character(len=*), intent(in) :: field_name
1818 !! Collection of fields defining system state.
1819 type(state_type), intent(inout) :: state
1820 !! Optional velocity name
1821 character(len = *), intent(in) :: velocity_name
1822
1823 !! Velocity to be solved for.
1824 type(vector_field), pointer :: U, U_old, U_cartesian
1825
1826 !! Coordinate and advecting velocity fields
1827 type(vector_field), pointer :: X, U_nl, down
1828
1829 !! Temporary velocity fields
1830 type(vector_field) :: U_tmp, U_cartesian_tmp
1831
1832 !! Velocity components Cartesian coordinates for slope limiter
1833 type(scalar_field) :: U_component
1834
1835 !! Change in U over one timestep.
1836 type(vector_field) :: delta_U, delta_U_tmp
1837
1838 !! DG Courant number field
1839 type(scalar_field), pointer :: s_field
1840
1841 !! Sparsity of advection matrix.
1842 type(csr_sparsity), pointer :: sparsity
1843
1844 !! System matrices.
1845 type(block_csr_matrix) :: A, L, mass_local, inv_mass_local, &
1846 inv_mass_cartesian
1847
1848 !! Sparsity of mass matrix.
1849 type(csr_sparsity) :: mass_sparsity
1850
1851 !! Right hand side vector.
1852 type(vector_field) :: rhs
1853
1854 !! Whether to invoke the slope limiter
1855 logical :: limit_slope
1856 !! Which limiter to use
1857 integer :: limiter
1858
1859 !! Number of advection subcycles.
1860 integer :: subcycles
1861 real :: max_courant_number
1862
1863 character(len=FIELD_NAME_LEN) :: limiter_name
1864 integer :: i, j, dim, ele, dim1
1865 integer :: upwinding_option
1866
1867 if(have_option('/material_phase::Fluid/vector_field::Velocity/prognostic&
1868 &/spatial_discretisation/discontinuous_galerkin/advection_scheme/ed&
1869 &ge_coordinates_upwind')) then
1870 upwinding_option = VECTOR_UPWIND_EDGE
1871 else
1872 if(have_option('/material_phase::Fluid/vector_field::Velocity/prognostic&
1873 &/spatial_discretisation/discontinuous_galerkin/advection_scheme/sphere_coordinates_upwind')) then
1874 upwinding_option = VECTOR_UPWIND_SPHERE
1875 else
1876 FLAbort('Unknown upwinding option')
1877 end if
1878 end if
1879
1880 U=>extract_vector_field(state, field_name)
1881 U_old=>extract_vector_field(state, "Old"//field_name)
1882 X=>extract_vector_field(state, "Coordinate")
1883 U_cartesian=>extract_vector_field(state, "Velocity")
1884 down=>extract_vector_field(state, "GravityDirection")
1885
1886 dim=mesh_dim(U)
1887
1888 ! Reset U to value at the beginning of the timestep.
1889 call set(U, U_old)
1890
1891 sparsity => get_csr_sparsity_firstorder(state, U%mesh, U%mesh)
1892
1893 ! Add data space to the sparsity pattern.
1894 call allocate(A, sparsity, (/dim,X%dim/))
1895 call zero(A)
1896 call allocate(L, sparsity, (/dim,X%dim/))
1897 call zero(L)
1898
1899 mass_sparsity=make_sparsity_dg_mass(U%mesh)
1900 call allocate(mass_local, mass_sparsity, (/dim,dim/))
1901 call zero(mass_local)
1902 call allocate(inv_mass_local, mass_sparsity, (/dim,dim/))
1903 call zero(inv_mass_local)
1904 call allocate(inv_mass_cartesian, mass_sparsity, (/X%dim,X%dim/))
1905 call zero(inv_mass_cartesian)
1906
1907 ! Ensure delta_U inherits options from U.
1908 call allocate(delta_U, U%dim, U%mesh, "delta_U")
1909 call zero(delta_U)
1910 call allocate(delta_U_tmp, U%dim, U%mesh, "delta_U")
1911 call zero(delta_U_tmp)
1912 delta_U%option_path = U_cartesian%option_path
1913 call allocate(rhs, U%dim, U%mesh, trim(field_name)//" RHS")
1914 call zero(rhs)
1915
1916 ! allocate temp velocity fields
1917 call allocate(U_tmp, U%dim, U%mesh, 'tmpU')
1918 call zero(U_tmp)
1919 call allocate(U_cartesian_tmp, U_cartesian%dim, U_cartesian%mesh, 'tmpUcartesian')
1920 call zero(U_cartesian_tmp)
1921
1922 call construct_vector_advection_dg(A, L, mass_local, &
1923 inv_mass_local, inv_mass_cartesian,&
1924 rhs, field_name, state, upwinding_option, down, &
1925 velocity_name=velocity_name)
1926
1927 ! Note that since dt is module global, these lines have to
1928 ! come after construct_advection_diffusion_dg.
1929 call get_option("/timestepping/timestep", dt)
1930
1931 if(have_option(trim(U_cartesian%option_path)//&
1932 &"/prognostic/temporal_discretisation/discontinuous_galerkin/&
1933 &/number_advection_subcycles")) then
1934 call get_option(trim(U_cartesian%option_path)//&
1935 &"/prognostic/temporal_discretisation/discontinuous_galerkin/&
1936 &/number_advection_subcycles", subcycles)
1937 else
1938 call get_option(trim(U_cartesian%option_path)//&
1939 &"/prognostic/temporal_discretisation/discontinuous_galerkin/&
1940 &/maximum_courant_number_per_subcycle", Max_Courant_number)
1941
1942 s_field => extract_scalar_field(state, "DG_CourantNumber")
1943 call calculate_diagnostic_variable(state, "DG_CourantNumber_Local", &
1944 & s_field)
1945 ewrite_minmax(s_field)
1946
1947 subcycles = ceiling( maxval(s_field%val)/Max_Courant_number)
1948 call allmax(subcycles)
1949 ewrite(2,*) 'Number of subcycles for velocity eqn: ', subcycles
1950 end if
1951
1952 limit_slope=.false.
1953 if (have_option(trim(U_cartesian%option_path)//&
1954 "/prognostic/spatial_discretisation/&
1955 &discontinuous_galerkin/slope_limiter")) then
1956 limit_slope=.true.
1957
1958 ! Note unsafe for mixed element meshes
1959 if (element_degree(U,1)==0) then
1960 FLExit("Slope limiters make no sense for degree 0 fields")
1961 end if
1962
1963 end if
1964
1965 do i=1, subcycles
1966 call mult_t(U_cartesian_tmp, L, U)
1967 call mult(U_cartesian, inv_mass_cartesian, U_cartesian_tmp)
1968 ! dU = Advection * U
1969 ! A maps from cartesian to local
1970 call mult(delta_U_tmp, A, U_cartesian)
1971 ! dU = dU + RHS
1972 !------------------------------------------
1973 !Is there anything in RHS?
1974 call addto(delta_U_tmp, RHS, -1.0)
1975 !------------------------------------------
1976 ! dU = M^(-1) dU
1977 call mult(delta_U, inv_mass_local, delta_U_tmp)
1978 !------------------------------------------
1979 !Do we need the below?
1980 call mult_t(U_cartesian_tmp, L, delta_U)
1981 call mult(U_cartesian, inv_mass_cartesian, U_cartesian_tmp)
1982 !------------------------------------------
1983
1984 ! U = U + dt/s * dU
1985 call addto(U, delta_U, scale=-dt/subcycles)
1986 call halo_update(U)
1987
1988 if (limit_slope) then
1989
1990 call mult_t(U_cartesian_tmp, L, U)
1991 call mult(U_cartesian, inv_mass_cartesian, U_cartesian_tmp)
1992
1993 !Really crap limiter
1994 !Just limit each cartesian component
1995 do dim1 = 1, U_cartesian%dim
1996 u_component = extract_scalar_field(U_cartesian, dim1)
1997 call limit_vb(state, U_component)
1998 end do
1999 !limiter=VECTOR_LIMITER_VB
2000 !call limit_slope_dg(U_cartesian, state, limiter)
2001
2002 call mult(U_tmp, L, U_cartesian)
2003 call mult(U, inv_mass_local, U_tmp)
2004 end if
2005
2006 end do
2007
2008 call deallocate(delta_U)
2009 call deallocate(A)
2010 call deallocate(L)
2011 call deallocate(mass_local)
2012 call deallocate(inv_mass_local)
2013 call deallocate(inv_mass_cartesian)
2014 call deallocate(mass_sparsity)
2015 call deallocate(rhs)
2016
2017 end subroutine solve_vector_advection_dg_subcycle
2018
2019 subroutine construct_vector_advection_dg(A, L, mass_local, inv_mass_local,&
2020 & inv_mass_cartesian, rhs, field_name, &
2021 & state, upwinding_option, down, velocity_name)
2022 !!< Construct the advection equation for discontinuous elements in
2023 !!< acceleration form.
2024 !!<
2025 !!< If mass is provided then the mass matrix is not added into big_m or
2026 !!< rhs. It is instead returned as mass. This may be useful for testing
2027 !!< or for solving equations otherwise than in acceleration form.
2028
2029 !! Main advection matrix.
2030 type(block_csr_matrix), intent(inout) :: A, L
2031 !! Mass matrices.
2032 type(block_csr_matrix), intent(inout) :: mass_local, inv_mass_local, &
2033 inv_mass_cartesian
2034 !! Right hand side vector.
2035 type(vector_field), intent(inout) :: rhs
2036 type(vector_field), intent(in) :: down
2037 !! Name of the field to be advected.
2038 character(len=*), intent(in) :: field_name
2039 !! Collection of fields defining system state.
2040 type(state_type), intent(inout) :: state
2041 integer, intent(in) :: upwinding_option
2042
2043 !! Optional velocity name
2044 character(len = *), intent(in), optional :: velocity_name
2045
2046 !! Position, velocity and advecting velocity fields.
2047 type(vector_field) :: X, U, U_nl
2048
2049 !! Local velocity name
2050 character(len = FIELD_NAME_LEN) :: lvelocity_name
2051
2052 !! Element index
2053 integer :: ele
2054
2055 !! Status variable for field extraction.
2056 integer :: stat
2057
2058 ewrite(1,*) "Writing advection equation for "&
2059 &//trim(field_name)
2060
2061 ! These names are based on the CGNS SIDS.
2062 U=extract_vector_field(state, field_name)
2063 X=extract_vector_field(state, "Coordinate")
2064
2065 if(present(velocity_name)) then
2066 lvelocity_name = velocity_name
2067 else
2068 lvelocity_name = "NonlinearVelocity"
2069 end if
2070
2071 U_nl=extract_vector_field(state, lvelocity_name)
2072 call incref(U_nl)
2073
2074 assert(has_faces(X%mesh))
2075 assert(has_faces(U%mesh))
2076
2077 call zero(A)
2078 call zero(RHS)
2079 call zero(mass_local)
2080
2081 element_loop: do ele=1,element_count(U)
2082
2083 call construct_vector_adv_element_dg(ele, A, L,&
2084 & mass_local, inv_mass_local, inv_mass_cartesian, rhs,&
2085 & X, U, U_nl, upwinding_option, down)
2086
2087 end do element_loop
2088
2089 ! Drop any extra field references.
2090
2091 call deallocate(U_nl)
2092
2093 end subroutine construct_vector_advection_dg
2094
2095 subroutine construct_vector_adv_element_dg(ele, A, L,&
2096 & mass_local, inv_mass_local, inv_mass_cartesian, rhs,&
2097 & X, U, U_nl, upwinding_option, down)
2098 !!< Construct the advection_diffusion equation for discontinuous elements in
2099 !!< acceleration form.
2100 implicit none
2101 !! Index of current element
2102 integer :: ele
2103 !! Main advection matrix.
2104 type(block_csr_matrix), intent(inout) :: A
2105 !! Transformation matrix
2106 type(block_csr_matrix), intent(inout) :: L
2107 !! Mass matrices.
2108 type(block_csr_matrix), intent(inout) :: mass_local, inv_mass_local, &
2109 inv_mass_cartesian
2110 !! Right hand side vector.
2111 type(vector_field), intent(inout) :: rhs
2112
2113 !! Position, velocity and advecting velocity.
2114 type(vector_field), intent(in) :: X, U, U_nl, down
2115
2116 !! Upwinding option
2117 integer, intent(in) :: upwinding_option
2118
2119 ! Bilinear forms.
2120 real, dimension(mesh_dim(U),mesh_dim(U),ele_loc(U,ele),ele_loc(U,ele)) :: local_mass_mat
2121 real, dimension(mesh_dim(U),X%dim,ele_loc(U,ele),ele_loc(U,ele)) :: l_mat
2122 real, dimension(ele_loc(U,ele),ele_loc(U,ele)) :: cartesian_mass_mat
2123 real, dimension(mesh_dim(U),X%dim,ele_loc(U,ele),ele_loc(U,ele)) :: &
2124 Advection_mat
2125
2126 ! Local assembly matrices.
2127 real, dimension(mesh_dim(U)*ele_loc(U,ele), mesh_dim(U)*ele_loc(U,ele)) :: l_mass, l_mass_cartesian
2128
2129 ! Local variables.
2130
2131 ! Neighbour element, face and neighbour face.
2132 integer :: ele_2, face, face_2
2133 ! Loops over faces.
2134 integer :: ni
2135
2136 ! Transform from local to physical coordinates.
2137 real, dimension(ele_ngi(X,ele)) :: detwei, detJ
2138 real, dimension(mesh_dim(U), X%dim, ele_ngi(X,ele)) :: J, J_scaled
2139 real, dimension(X%dim, mesh_dim(U), ele_ngi(X,ele)) :: pinvJ
2140 real, dimension(ele_loc(U,ele), ele_ngi(X,ele), X%dim) :: dshape
2141 real, dimension(mesh_dim(U), mesh_dim(U), ele_ngi(X,ele)) :: G
2142
2143 ! Different velocities at quad points.
2144 real, dimension(U_nl%dim, ele_ngi(U_nl, ele)) :: U_nl_q
2145 real, dimension(X%dim, ele_ngi(U_nl, ele)) :: U_cartesian_q
2146 real, dimension(ele_ngi(U_nl, ele)) :: U_nl_div_q
2147
2148 ! Node and shape pointers.
2149 integer, dimension(:), pointer :: U_ele
2150 type(element_type), pointer :: U_shape, U_nl_shape
2151 ! Neighbours of this element.
2152 integer, dimension(:), pointer :: neigh
2153
2154 integer :: i, gi, dim, dim1, dim2, nloc, k
2155
2156 dim=mesh_dim(U)
2157
2158 !----------------------------------------------------------------------
2159 ! Establish local node lists
2160 !----------------------------------------------------------------------
2161
2162 U_ele=>ele_nodes(U,ele) ! Velocity node numbers
2163
2164 !----------------------------------------------------------------------
2165 ! Establish local shape functions
2166 !----------------------------------------------------------------------
2167
2168 U_shape=>ele_shape(U, ele)
2169 U_nl_shape=>ele_shape(U_nl, ele)
2170
2171 !==========================
2172 ! Coordinates
2173 !==========================
2174
2175 ! Get J and detJ
2176 call compute_jacobian(ele_val(X,ele), ele_shape(X,ele), detwei=detwei, J=J, detJ=detJ)
2177
2178 !----------------------------------------------------------------------
2179 ! Construct bilinear forms.
2180 !----------------------------------------------------------------------
2181
2182 ! Element mass matrix.
2183 ! /
2184 ! | W G U dV
2185 ! /
2186 do gi=1,ele_ngi(X,ele)
2187 G(:,:,gi)=matmul(J(:,:,gi),transpose(J(:,:,gi)))/detJ(gi)
2188 J_scaled(:,:,gi)=J(:,:,gi)/detJ(gi)
2189 end do
2190
2191 local_mass_mat=shape_shape_tensor(u_shape, u_shape, &
2192 u_shape%quadrature%weight, G)
2193
2194 cartesian_mass_mat=shape_shape(u_shape, u_shape, detwei)
2195
2196 ! Transformation matrix
2197 l_mat=shape_shape_tensor(u_shape, u_shape, u_shape%quadrature%weight, J)
2198
2199 ! Advecting velocity at quadrature points.
2200 U_nl_q=ele_val_at_quad(U_nl,ele)
2201 U_nl_div_q=ele_div_at_quad(U_nl, ele, U_shape%dn)
2202
2203 ! WE HAVE INTEGRATED BY PARTS TWICE
2204 ! Element advection matrix
2205 ! /
2206 ! | w . div (\bar{U} \tensor u )dV
2207 ! /
2208
2209 ! becomes
2210
2211 ! /
2212 ! | \phi_i (dx/d\xi)^T.\div_L (\bar{U}_L \tensor \phi_j)dV_L
2213 ! /
2214 ! underscore U indicates local coordinates
2215
2216 ! split into two terms
2217
2218 ! /
2219 ! | \phi_i (dx/d\xi)^T(\div_L \bar{U}_L)\phi_j dV_L
2220 ! /
2221
2222 ! and
2223
2224 ! /
2225 ! | \phi_i (dx/d\xi)^T\bar{U}_L . grad_L \phi_j dV_L
2226 ! /
2227
2228
2229 U_cartesian_q=0.
2230 do gi=1,ele_ngi(X,ele)
2231 U_cartesian_q(:,gi)=matmul(transpose(J(:,:,gi)),U_nl_q(:,gi))
2232 U_cartesian_q(:,gi)=U_cartesian_q(:,gi)/detJ(gi)
2233 end do
2234 Advection_mat = shape_vector_dot_dshape_tensor(U_shape, U_nl_q, U_shape&
2235 &%dn, J_scaled, U_shape%quadrature%weight)
2236
2237 !----------------------------------------------------------------------
2238 ! Perform global assembly.
2239 !----------------------------------------------------------------------
2240
2241 ! Assemble matrices.
2242
2243 ! Return local mass separately.
2244 do dim1 = 1,dim
2245 do dim2 = 1,dim
2246 call addto(mass_local, dim1, dim2, U_ele, U_ele, &
2247 local_mass_mat(dim1,dim2,:,:))
2248 end do
2249 end do
2250
2251 ! calculate inverse_mass_local
2252 nloc=ele_loc(U,ele)
2253 do dim1 = 1,dim
2254 do dim2 = 1,dim
2255 l_mass(nloc*(dim1-1)+1:nloc*dim1, nloc*(dim2-1)+1:nloc*dim2)=&
2256 local_mass_mat(dim1,dim2,:,:)
2257 end do
2258 end do
2259
2260 call invert(l_mass)
2261
2262 do dim1 = 1,dim
2263 do dim2 = 1,dim
2264 call addto(inv_mass_local, dim1, dim2, U_ele, U_ele, &
2265 l_mass(nloc*(dim1-1)+1:nloc*dim1,nloc*(dim2-1)+1:nloc*dim2))
2266 end do
2267 end do
2268
2269 ! calculate inverse_mass_cartesian
2270 call invert(cartesian_mass_mat)
2271
2272 do dim1 = 1,X%dim
2273 call addto(inv_mass_cartesian, dim1, dim1, U_ele, U_ele, cartesian_mass_mat)
2274 end do
2275
2276 ! advection matrix
2277 do dim1 = 1,dim
2278 do dim2 = 1,X%dim
2279 call addto(A, dim1, dim2, U_ele, U_ele, Advection_mat(dim1,dim2,:,:))
2280 end do
2281 end do
2282
2283 ! transformation matrix
2284 do dim1 = 1,dim
2285 do dim2 = 1,X%dim
2286 call addto(L, dim1, dim2, U_ele, U_ele, l_mat(dim1,dim2,:,:))
2287 end do
2288 end do
2289
2290 !-------------------------------------------------------------------
2291 ! Interface integrals
2292 !-------------------------------------------------------------------
2293
2294 neigh=>ele_neigh(U, ele)
2295
2296 neighbourloop: do ni=1,size(neigh)
2297
2298 !----------------------------------------------------------------------
2299 ! Find the relevant faces.
2300 !----------------------------------------------------------------------
2301
2302 ! These finding routines are outside the inner loop so as to allow
2303 ! for local stack variables of the right size in
2304 ! construct_add_diff_interface_dg.
2305
2306 ele_2=neigh(ni)
2307
2308 ! Note that although face is calculated on field U, it is in fact
2309 ! applicable to any field which shares the same mesh topology.
2310 face=ele_face(U, ele, ele_2)
2311
2312 if (ele_2>0) then
2313 ! Internal faces.
2314 face_2=ele_face(U, ele_2, ele)
2315 else
2316 ! External face.
2317 face_2=face
2318 end if
2319
2320 call construct_vector_adv_interface_dg(ele, ele_2, face, face_2,&
2321 & A, rhs, X, U, U_nl, upwinding_option, down)
2322
2323 end do neighbourloop
2324
2325 end subroutine construct_vector_adv_element_dg
2326
2327 subroutine construct_vector_adv_interface_dg(ele, ele_2, face, face_2, &
2328 & A, rhs, X, U, U_nl, upwinding_option, down)
2329
2330 !!< Construct the DG element boundary integrals on the ni-th face of
2331 !!< element ele.
2332 implicit none
2333
2334 integer, intent(in) :: ele, ele_2, face, face_2
2335 type(block_csr_matrix), intent(inout) :: A
2336 type(vector_field), intent(inout) :: rhs
2337 ! We pass these additional fields to save on state lookups.
2338 type(vector_field), intent(in) :: X, U, U_nl, down
2339 integer, intent(in) :: upwinding_option
2340
2341 ! Face objects and numberings.
2342 type(element_type), pointer :: U_shape, U_shape_2
2343 integer, dimension(face_loc(U,face)) :: U_face, U_face_l
2344 integer, dimension(face_loc(U,face_2)) :: U_face_2
2345 integer, dimension(face_loc(U_nl,face)) :: U_nl_face
2346 integer, dimension(face_loc(U_nl,face_2)) :: U_nl_face_2
2347
2348 ! Note that both sides of the face can be assumed to have the same
2349 ! number of quadrature points.
2350 real, dimension(U_nl%dim, face_ngi(U_nl, face)) :: n1_l, n2_l, U_nl_q,&
2351 & U_nl_f_q, U_nl_f2_q
2352 logical, dimension(face_ngi(U_nl, face)) :: inflow
2353 real, dimension(face_ngi(U_nl, face)) :: U_nl_q_dotn1_l, U_nl_q_dotn2_l, U_nl_q_dotn_l, income
2354 real, dimension(X%dim, face_ngi(X,face)) :: n1, n2, t11, t12, t21,t22,&
2355 & pole_axis
2356 real, dimension(X%dim, ele_ngi(X,ele)) :: up_gi, up_gi2
2357 real, dimension(X%dim, face_ngI(X,face)) :: X_quad
2358 real, dimension(X%dim, X%dim, face_ngi(X,face)) :: Btmp
2359 real, dimension(mesh_dim(U), X%dim, face_ngi(X,face)) :: B
2360 ! Variable transform times quadrature weights.
2361 real, dimension(ele_ngi(X,ele)) :: detwei, detJ
2362 real, dimension(face_ngi(X,face)) :: detwei_f, detJ_f
2363 real, dimension(mesh_dim(U), X%dim, ele_ngi(X,ele)) :: J
2364 real, dimension(mesh_dim(U), X%dim, face_ngi(X,face)) :: J_scaled
2365 real, dimension(face_ngi(X,face)) :: inner_advection_integral, outer_advection_integral
2366
2367 ! Bilinear forms
2368 real, dimension(mesh_dim(U),X%dim,face_loc(U,face),face_loc(U,face)) :: nnAdvection_out
2369 real, dimension(mesh_dim(U),X%dim,face_loc(U,face),face_loc(U,face_2)) :: nnAdvection_in
2370
2371 ! normal weights
2372 real :: w1, w2
2373 integer :: dim, dim1, dim2, gi, i, k
2374
2375 dim=mesh_dim(U)
2376
2377 U_face=face_global_nodes(U, face)
2378 U_shape=>face_shape(U, face)
2379 X_quad = face_val_at_quad(X, face)
2380
2381 U_face_2=face_global_nodes(U, face_2)
2382 U_shape_2=>face_shape(U, face_2)
2383
2384 !Unambiguously calculate the normal using the face with the higher
2385 !face number. This is so that the normal is identical on both sides.
2386
2387 call get_local_normal(n1_l, w1, U, local_face_number(U%mesh,face))
2388 call get_local_normal(n2_l, w2, U, local_face_number(U%mesh,face_2))
2389
2390 !----------------------------------------------------------------------
2391 ! Construct element-wise quantities.
2392 !----------------------------------------------------------------------
2393
2394 ! Advecting velocity at quadrature points.
2395 U_nl_f_q = face_val_at_quad(U_nl, face)
2396 U_nl_f2_q = face_val_at_quad(U_nl, face_2)
2397
2398 u_nl_q_dotn1_l = sum(U_nl_f_q*w1*n1_l,1)
2399 u_nl_q_dotn2_l = -sum(U_nl_f2_q*w2*n2_l,1)
2400 U_nl_q_dotn_l=0.5*(u_nl_q_dotn1_l+u_nl_q_dotn2_l)
2401
2402 ! Inflow is true if the flow at this gauss point is directed
2403 ! into this element.
2404 inflow = u_nl_q_dotn_l<0.0
2405 income = merge(1.0,0.0,inflow)
2406
2407 ! Calculate tensor on face
2408 if(ele_loc(X,ele).ne.3) then
2409 ewrite(1,*) 'Nloc=',ele_loc(X,ele)
2410 FLAbort('Hard coded for linear elements at the moment')
2411 end if
2412 call compute_jacobian(ele_val(X,ele), ele_shape(X,ele), detwei=detwei, J=J, detJ=detJ)
2413 forall(gi=1:face_ngi(X,face))
2414 J_scaled(:,:,gi)=J(:,:,1)/detJ(1)
2415 end forall
2416
2417 select case(upwinding_option)
2418 case (VECTOR_UPWIND_EDGE)
2419 ! Get outward pointing normals in physical space for face1
2420 ! inward pointing normals in physical space for face2
2421 n1=get_face_normal_manifold(X, ele, face)
2422 !needs checking
2423 if(ele_2<0) then
2424 ! external boundary
2425 n2=n1
2426 else
2427 n2=-get_face_normal_manifold(X, ele_2, face_2)
2428 end if
2429 ! Form 'bending' tensor
2430 ! This rotates the normal to face 2 into -normal from face 1
2431 ! u_b = t(t.u_b) + n_b(n_b.u_b)
2432 ! u_a = t(t.u_b) + n_a(n_b.u_b)
2433 ! = u_b - n_b(n_b.u_b) + n_a(n_b.u_b)
2434 ! = (I + (n_a-n_b)n_b^T)u_b == B u_b
2435 forall(gi=1:face_ngi(X,face))
2436 Btmp(:,:,gi)=outer_product(n1(:,gi)-n2(:,gi),n2(:,gi))
2437 forall(i=1:size(Btmp,1)) Btmp(i,i,gi)=Btmp(i,i,gi)+1.0
2438 B(:,:,gi)=matmul(J_scaled(:,:,gi),Btmp(:,:,gi))
2439 end forall
2440 case (VECTOR_UPWIND_SPHERE)
2441 if(mesh_dim(X).ne.2) then
2442 FLExit('Assumes 2d surface. Surface of the sphere, in fact.')
2443 end if
2444 !Get element normal
2445 up_gi = -ele_val_at_quad(down,ele)
2446 call get_up_gi(X,ele,up_gi)
2447 up_gi2 = -ele_val_at_quad(down,ele_2)
2448 call get_up_gi(X,ele_2,up_gi2)
2449 ! Form 'bending' tensor
2450 ! Coordinate system is:
2451 ! t1: normal to local normal and (0,0,1)
2452 ! t2: normal to t1 and local normal
2453 ! u2 = t21(t21.u2) + t22(t22.u2)
2454 ! u1 = t11(t21.u2) + t12(t22.u2)
2455 ! = (t11 t21^T + t12 t22^T)u2
2456
2457 pole_axis = 0.
2458 pole_axis(3,:) = 1.
2459 btmp = 0.
2460 do gi = 1, face_ngi(X,face)
2461 t11(:,gi) = cross_product(pole_axis(:,gi),up_gi(:,1))
2462 t11(:,gi) = t11(:,gi)/sqrt(sum(t11(:,gi)**2))
2463 t12(:,gi) = cross_product(up_gi(:,1),t11(:,gi))
2464 t21(:,gi) = cross_product(pole_axis(:,gi),up_gi2(:,1))
2465 t21(:,gi) = t21(:,gi)/sqrt(sum(t21(:,gi)**2))
2466 t22(:,gi) = cross_product(up_gi2(:,1),t21(:,gi))
2467
2468 forall(i=1:3,k=1:3)
2469 Btmp(i,k,gi) = t11(i,gi)*t21(k,gi) + t12(i,gi)*t22(k,gi)
2470 end forall
2471 B(:,:,gi)=matmul(J_scaled(:,:,gi),Btmp(:,:,gi))
2472 end do
2473 case default
2474 FLAbort('Unknown vector upwinding option')
2475 end select
2476
2477 !----------------------------------------------------------------------
2478 ! Construct bilinear forms.
2479 !----------------------------------------------------------------------
2480
2481 ! Calculate outflow boundary integral.
2482 ! can anyone think of a way of optimising this more to avoid
2483 ! superfluous operations (i.e. multiplying things by 0 or 1)?
2484
2485 ! first the integral around the inside of the element
2486 ! (this is the flux *out* of the element)
2487 inner_advection_integral = -income*u_nl_q_dotn_l
2488 nnAdvection_out=shape_shape_tensor(U_shape, U_shape, &
2489 &inner_advection_integral*U_shape%quadrature%weight, J_scaled)
2490
2491 ! now the integral around the outside of the element
2492 ! (this is the flux *in* to the element)
2493 outer_advection_integral = income*u_nl_q_dotn_l
2494 nnAdvection_in=shape_shape_tensor(U_shape, U_shape_2, &
2495 &outer_advection_integral*U_shape%quadrature%weight, B)
2496
2497 !----------------------------------------------------------------------
2498 ! Perform global assembly.
2499 !----------------------------------------------------------------------
2500
2501 ! Insert advection in matrix.
2502
2503 ! Outflow boundary integral.
2504 do dim1 = 1, dim
2505 do dim2 = 1, X%dim
2506 call addto(A, dim1, dim2, U_face, U_face, nnAdvection_out(dim1,dim2,:,:))
2507 end do
2508 end do
2509
2510 ! Inflow boundary integral.
2511 do dim1 = 1, dim
2512 do dim2 = 1, X%dim
2513 call addto(A, dim1, dim2, U_face, U_face_2, nnAdvection_in(dim1,dim2,:,:))
2514 end do
2515 end do
2516
2517 contains
2518
2519 ! copy of outer_product in vector tools, but now as function
2520 pure function outer_product(x, y) result (M)
2521 !!< Give two column vectors x, y
2522 !!< compute the matrix xy*
2523
2524 real, dimension(:), intent(in) :: x, y
2525 real, dimension(size(x), size(y)) :: M
2526 integer :: i, j
2527
2528 forall (i=1:size(x))
2529 forall (j=1:size(y))
2530 M(i, j) = x(i) * y(j)
2531 end forall
2532 end forall
2533
2534 end function outer_product
2535
2536 end subroutine construct_vector_adv_interface_dg
2537
2538end module advection_local_DG
02539
=== added file 'assemble/Bubble_tools.F90'
--- assemble/Bubble_tools.F90 1970-01-01 00:00:00 +0000
+++ assemble/Bubble_tools.F90 2012-10-23 15:04:25 +0000
@@ -0,0 +1,485 @@
1! Copyright (C) 2009 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!
11! amcgsoftware@imperial.ac.uk
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#include "fdebug.h"
28
29 module bubble_tools
30 use fields
31 use field_options
32 use fields_allocates
33 use vector_tools
34 use sparse_matrices_fields
35 use solvers
36 use quadrature
37 use sparsity_patterns_meshes
38 use element_numbering
39 use state_module
40 use shape_functions
41 use quadrature
42 use shape_functions
43 use fldebug_parameters
44 use ieee_arithmetic, only: ieee_quiet_nan, ieee_value
45 use spud
46 use vtk_interfaces
47 use adjacency_lists
48 use reference_counting
49 implicit none
50
51 private
52 public get_lumped_mass_p2b,&
53 & project_to_p2b_lumped, get_p2b_lumped_mass_quadrature, &
54 & bubble_field_to_vtk
55
56#include "../femtools/Reference_count_interface_mesh_type.F90"
57
58 contains
59
60 !! This is special cased because it is only used for mass lumping
61 subroutine get_p2b_lumped_mass_quadrature(quad)
62 type(quadrature_type), intent(inout) :: quad
63 !
64 real :: wv = 1.0/20., we = 2.0/15., wg = 9./20.
65
66 call allocate(quad, 3, 7, 3)
67 quad%dim = 2
68 quad%degree = 3
69 quad%weight = 0.5*(/wv,we,wv,we,we,wv,wg/)
70 quad%l(:,1) = (/1.0,0.5,0.0,0.5,0.0,0.,1./3./)
71 quad%l(:,2) = (/0.0,0.5,1.0,0.0,0.5,0.,1./3./)
72 quad%l(:,3) = (/0.0,0.0,0.0,0.5,0.5,1.,1./3./)
73 quad%family = 3!FAMILY_SIMPSONS
74
75 end subroutine get_p2b_lumped_mass_quadrature
76
77 !! This is special cased because of the special properties of the
78 !! p2b lumped mass (it is still 3rd order, and positive).
79 subroutine get_lumped_mass_p2b(state,mass,p2b_field)
80 type(csr_matrix), intent(inout) :: mass
81 type(state_type), intent(inout) :: state
82 type(scalar_field), intent(in) :: p2b_field
83 !
84 integer :: ele
85 type(vector_field), pointer :: X
86 real, dimension(7) :: N_vals
87 type(quadrature_type) :: Simpsons_quad
88 type(element_type) :: p2b_lumped_shape, X_lumped_shape
89
90 X=>extract_vector_field(state, "Coordinate")
91
92 if(p2b_field%mesh%shape%dim.ne.2) then
93 FLAbort('Only works for 2d meshes')
94 end if
95 if(p2b_field%mesh%shape%loc.ne.7) then
96 FLAbort('Expected p2 bubble mesh')
97 end if
98
99 call get_p2b_lumped_mass_quadrature(Simpsons_quad)
100 p2b_lumped_shape = make_element_shape_from_element(p2b_field%mesh%shape, &
101 quad=Simpsons_quad)
102 X_lumped_shape = make_element_shape_from_element(X%mesh%shape, &
103 quad=Simpsons_quad)
104
105 call zero(mass)
106
107 do ele = 1, ele_count(X)
108 call get_lumped_mass_p2b_ele(mass,p2b_field,p2b_lumped_shape,&
109 X,X_lumped_shape,&
110 ele)
111 end do
112
113 call deallocate(p2b_lumped_shape)
114 call deallocate(X_lumped_shape)
115 call deallocate(Simpsons_quad)
116
117 end subroutine get_lumped_mass_p2b
118
119 subroutine get_lumped_mass_p2b_ele(mass,p2b_field,p2b_lumped_shape,&
120 X,X_lumped_shape,ele)
121 type(csr_matrix), intent(inout) :: mass
122 type(scalar_field), intent(in) :: p2b_field
123 type(vector_field), intent(in) :: X
124 type(element_type), intent(in) :: p2b_lumped_shape,X_lumped_shape
125 integer, intent(in) :: ele
126 !
127 real, dimension(mesh_dim(X), X%dim, ele_ngi(X,ele)) :: J
128 real, dimension(ele_ngi(X,ele)) :: detwei
129 real, dimension(X_lumped_shape%ngi) :: detwei_l
130 real, dimension(ele_loc(p2b_field,ele),ele_loc(p2b_field,ele))&
131 :: l_mass_mat
132 real :: Area
133 integer :: loc
134 real, dimension(p2b_lumped_shape%ngi) :: weight_vals
135 call compute_jacobian(ele_val(X,ele), ele_shape(X,ele), J, detwei)
136 Area = sum(detwei)
137 call compute_jacobian(ele_val(X,ele), X_lumped_shape, J, detwei_l)
138 assert(abs(Area-sum(detwei_l))<1.0e-10)
139 l_mass_mat = shape_shape(p2b_lumped_shape,p2b_lumped_shape,&
140 detwei_l)
141 call addto(mass,ele_nodes(p2b_field,ele),&
142 ele_nodes(p2b_field,ele),l_mass_mat)
143
144 end subroutine get_lumped_mass_p2b_ele
145
146 !! This is special cased because of the special properties of the
147 !! p2b lumped mass (it is still 3rd order, and positivity preserving)
148 subroutine project_to_p2b_lumped(state,field,field_projected)
149 type(state_type), intent(inout) :: state
150 type(scalar_field), intent(in) :: field
151 type(scalar_field), intent(inout) :: field_projected
152 !
153 type(scalar_field) :: rhs
154 type(vector_field), pointer :: X
155 type(csr_sparsity), pointer :: mass_sparsity
156 type(csr_matrix) :: lumped_mass
157 integer :: ele
158 type(quadrature_type) :: Simpsons_quad
159 type(element_type) :: X_lumped_shape,&
160 & field_projected_lumped_shape, field_lumped_shape
161
162 if(field_projected%mesh%shape%dim.ne.2) then
163 FLAbort('Only works for 2d meshes')
164 end if
165 if(field_projected%mesh%shape%loc.ne.7) then
166 FLAbort('Expected p2 bubble mesh')
167 end if
168 X=>extract_vector_field(state, "Coordinate")
169
170 call get_p2b_lumped_mass_quadrature(Simpsons_quad)
171 field_projected_lumped_shape = make_element_shape_from_element( &
172 field_projected%mesh%shape,quad=Simpsons_quad)
173 field_lumped_shape = make_element_shape_from_element( &
174 field%mesh%shape,quad=Simpsons_quad)
175 X_lumped_shape = make_element_shape_from_element(X%mesh%shape, &
176 quad=Simpsons_quad)
177
178 mass_sparsity => get_csr_sparsity_firstorder(state, &
179 field_projected%mesh, field_projected%mesh)
180 call allocate(lumped_mass,mass_sparsity)
181 call zero(lumped_mass)
182 call allocate(rhs,field_projected%mesh,"ProjectionRHS")
183 call zero(rhs)
184
185 do ele = 1, ele_count(X)
186 call project_to_p2b_lumped_ele(rhs,lumped_mass,&
187 X,X_lumped_shape,field_lumped_shape,&
188 field_projected_lumped_shape,field,ele)
189 end do
190 call petsc_solve(field_projected,lumped_mass,rhs)
191 ewrite (2,*) maxval(abs(field_projected%val))
192 call deallocate(rhs)
193 call deallocate(lumped_mass)
194 end subroutine project_to_p2b_lumped
195
196 subroutine project_to_p2b_lumped_ele(rhs,lumped_mass,&
197 X,X_lumped_shape,field_lumped_shape,&
198 field_projected_lumped_shape,field,ele)
199 type(vector_field), intent(in) :: X
200 type(scalar_field), intent(in) :: field
201 type(scalar_field), intent(inout) :: rhs
202 type(csr_matrix), intent(inout) :: lumped_mass
203 integer, intent(in) :: ele
204 type(element_type), intent(in) :: X_lumped_shape,&
205 field_projected_lumped_shape, field_lumped_shape
206 !
207 real, dimension(ele_loc(rhs,ele)) :: l_rhs,field_gi
208 real, dimension(ele_loc(rhs,ele),ele_loc(rhs,ele)) :: &
209 & l_mass
210 real, dimension(ele_loc(field,ele)) :: field_vals
211 integer :: loc
212 real, dimension(mesh_dim(X), X%dim, ele_ngi(X,ele)) :: J
213 real, dimension(X_lumped_shape%ngi) :: detwei
214
215 call compute_jacobian(ele_val(X,ele), X_lumped_shape, J, detwei)
216
217 !Values of field at field DOFs
218 field_vals = ele_val(field,ele)
219 assert(size(field_gi)==size(detwei))
220 field_gi = matmul(transpose(field_lumped_shape%n),field_vals)
221 l_rhs = shape_rhs(field_projected_lumped_shape,&
222 field_gi*detwei)
223 l_mass = shape_shape(field_projected_lumped_shape,&
224 field_projected_lumped_shape,detwei)
225 call addto(rhs,ele_nodes(rhs,ele),l_rhs)
226 call addto(lumped_mass,ele_nodes(rhs,ele),&
227 ele_nodes(rhs,ele),l_mass)
228 end subroutine project_to_p2b_lumped_ele
229
230 subroutine bubble_field_to_vtk(state,sfields,filename,dump_num)
231 type(state_type), intent(inout) :: state
232 type(scalar_field), dimension(:), intent(in) :: sfields
233 character(len=*), intent(in) :: filename ! Base filename
234 integer, intent(in) :: dump_num
235 !
236 type(mesh_type), pointer :: lQuadMesh
237 type(mesh_type), target :: QuadMesh
238 type(vector_field) :: QuadX
239 type(scalar_field), dimension(size(sfields)) :: QuadSF
240 type(vector_field), pointer :: X
241 integer :: stat, i
242 type(mesh_type), pointer :: vertex_mesh
243
244 ewrite(1,*) 'subroutine bubble_field_to_vtk(state,sfield,filename,dump_num)'
245 do i = 1, size(sfields)
246 if(sfields(i)%mesh%shape%numbering%type.ne.ELEMENT_BUBBLE) then
247 FLExit('Only works for bubble functions.')
248 end if
249 end do
250 X => extract_vector_field(state,"Coordinate")
251 call find_linear_parent_mesh(state, X%mesh, vertex_mesh)
252
253 !Construct a quadrilateral mesh from the triangular one.
254 lQuadMesh => extract_mesh(state,"QuadMesh",stat)
255 if(stat/=0) then
256 call quadmesh_from_trimesh(QuadMesh,vertex_mesh,state)
257 lQuadMesh => QuadMesh
258 end if
259 !Construct a coordinate field on the quadrilateral mesh
260 call allocate(QuadX,X%dim,lQuadMesh,"QuadCoordinate")
261 call zero(QuadX)
262 call quadX_from_bubbleX(QuadX,X)
263 !Remap the bubble field to Q1 on the quad mesh
264 do i = 1, size(sfields)
265 call allocate(QuadSF(i),lQuadMesh,"Quad"//trim(sfields(i)%name))
266 call quadSF_from_bubbleSF(QuadSF(i),sfields(i))
267 end do
268 call vtk_write_fields(filename, dump_num, quadX, lQuadMesh, &
269 QuadSF)
270 call deallocate(QuadX)
271 do i = 1, size(sfields)
272 call deallocate(QuadSF(i))
273 end do
274
275 ewrite(1,*) 'END subroutine bubble_field_to_vtk(state,sfield,filename,dump_num)'
276 contains
277
278 subroutine quadmesh_from_trimesh(QuadMesh,Xmesh,state)
279 type(mesh_type), intent(inout) :: QuadMesh
280 type(mesh_type), intent(in) :: Xmesh
281 type(state_type), intent(inout) :: state
282 !
283 type(csr_sparsity) :: NNList, EEList
284 type(csr_matrix) :: edge_matrix
285 type(quadrature_type) :: quad
286 integer :: tri_edges, tri_nodes, tri_elements
287 integer :: ele, ni, nodecount, node, qele, qele_loc,ele2
288 integer, pointer, dimension(:) :: neigh, X_ele
289
290 tri_elements = Xmesh%elements
291 quadmesh%elements = 3*tri_elements
292 allocate(quadmesh%ndglno(quadmesh%elements*4))
293 quadmesh%ndglno=-666
294 call MakeLists_Mesh(Xmesh,NNList=NNlist,EEList=EEList)
295 call allocate(edge_matrix,EEList,type=CSR_INTEGER)
296 call zero(edge_matrix)
297 tri_edges=size(NNList%colm)/2
298 tri_nodes=Xmesh%nodes
299 quadmesh%nodes = 3*tri_elements+tri_edges+Xmesh%nodes
300
301 nodecount = Xmesh%nodes
302 do ele = 1, ele_count(Xmesh)
303 !! Element numbering in the three quads
304 !! ele_q = 3*(ele-1)+vnode where vnode is the local
305 !! node number of the node at the vertex
306 !!Local numbering in the three quads
307 !!triangle vertex node first, then edge node
308 !!on edge joining to vertex with next number in modulo arithmetic
309 !!then triangle centre, then other edge
310
311 !First do the vertex nodes (same numbering as Xmesh)
312 X_ele => ele_nodes(Xmesh,ele)
313 assert(size(X_ele)==3)
314 do qele_loc = 1,3
315 qele = 3*(ele-1)+qele_loc
316 quadmesh%ndglno(4*(qele-1)+1) = X_ele(qele_loc)
317 end do
318
319 !Then the edge nodes (only do if ele<ele2 or ele2<0)
320 neigh => ele_neigh(Xmesh,ele)
321 assert(size(neigh)==3)
322 do ni = 1, size(neigh)
323 ele2 = neigh(ni)
324 if(ele2.le.0 .or. ele<ele2) then
325 nodecount = nodecount + 1
326 node = nodecount
327 if(ele2>0) then
328 call set(edge_matrix,ele,ele2,node)
329 call set(edge_matrix,ele2,ele,node)
330 end if
331 else
332 node = val(edge_matrix,ele,ele2)
333 end if
334 assert(node>0)
335 select case(ni)
336 case (1)
337 qele = 3*(ele-1)+2
338 quadmesh%ndglno(4*(qele-1)+2)=node
339 qele = 3*(ele-1)+3
340 quadmesh%ndglno(4*(qele-1)+3)=node
341 case (2)
342 qele = 3*(ele-1)+3
343 quadmesh%ndglno(4*(qele-1)+2)=node
344 qele = 3*(ele-1)+1
345 quadmesh%ndglno(4*(qele-1)+3)=node
346 case (3)
347 qele = 3*(ele-1)+1
348 quadmesh%ndglno(4*(qele-1)+2)=node
349 qele = 3*(ele-1)+2
350 quadmesh%ndglno(4*(qele-1)+3)=node
351 end select
352 end do
353
354 !Then the triangle centre nodes
355 do qele = 3*(ele-1)+1,3*(ele-1)+3
356 nodecount = nodecount + 1
357 quadmesh%ndglno(4*(qele-1)+4)=nodecount
358 end do
359 end do
360 assert(all(quadmesh%ndglno>0))
361 assert(nodecount==quadmesh%nodes)
362
363 quadmesh%wrapped=.false.
364 quad = make_quadrature(4,2,degree=Xmesh%shape%quadrature%degree,&
365 family=Xmesh%shape%quadrature%family)
366 quadmesh%shape = make_element_shape(4,2,1,quad)
367 quadmesh%name = "QuadMesh"
368 quadmesh%continuity=0
369 quadmesh%periodic=.false.
370
371 allocate(quadmesh%adj_lists)
372 nullify(quadmesh%region_ids)
373 nullify(quadmesh%subdomain_mesh)
374 nullify(quadmesh%refcount) ! Hack for gfortran component initialisation
375 ! bug.
376
377 call addref(quadmesh)
378
379 !need to insert into state and deallocate
380 call insert(state,quadmesh,"QuadMesh")
381 call deallocate(quadmesh)
382 call deallocate(EEList)
383 call deallocate(NNList)
384 end subroutine quadmesh_from_trimesh
385
386 subroutine quadX_from_bubbleX(QuadX,X)
387 type(vector_field), intent(inout) :: QuadX
388 type(vector_field), intent(in) :: X
389 !
390 real, dimension(X%dim,ele_loc(X,1)) :: X_vals
391 real, dimension(X%dim,ele_loc(QuadX,1)) :: QX_vals
392 integer :: qele,dim1,ele
393
394 real, dimension(X%dim,3) :: X_lin
395
396 do ele = 1, ele_count(X)
397 X_vals = ele_val(X,ele)
398
399 !!Need to evaluate X at bubble node locations
400 select case(ele_loc(X,1))
401 case(3)
402 X_lin = X_vals
403 case(6)
404 X_lin(:,1) = X_vals(:,1)
405 X_lin(:,2) = X_vals(:,3)
406 X_lin(:,3) = X_vals(:,6)
407 case(10)
408 X_lin(:,1) = X_vals(:,1)
409 X_lin(:,2) = X_vals(:,4)
410 X_lin(:,3) = X_vals(:,10)
411 case default
412 FLExit('Dont know how to handle coordinate mesh')
413 end select
414
415 !! Quadrilateral #1
416 do dim1 = 1, X%dim
417 QX_vals(dim1,:) = &
418 &(/X_lin(dim1,1),0.5*(X_lin(dim1,1)+X_lin(dim1,2)),&
419 &0.5*(X_lin(dim1,1)+X_lin(dim1,3)),&
420 &sum(X_lin(dim1,:))/3/)
421 end do
422 qele = (ele-1)*3+1
423 call set(QuadX,ele_nodes(QuadX,qele),QX_vals)
424 !! Quadrilateral #2
425 do dim1 = 1, X%dim
426 QX_vals(dim1,:) = &
427 &(/X_lin(dim1,2),0.5*(X_lin(dim1,2)+X_lin(dim1,3)),&
428 &0.5*(X_lin(dim1,1)+X_lin(dim1,2)),&
429 &sum(X_lin(dim1,:))/3/)
430 end do
431 qele = (ele-1)*3+2
432 call set(QuadX,ele_nodes(QuadX,qele),QX_vals)
433 !! Quadrilateral #3
434 do dim1 = 1, X%dim
435 QX_vals(dim1,:) = &
436 &(/X_lin(dim1,3),0.5*(X_lin(dim1,3)+X_lin(dim1,1)),&
437 &0.5*(X_lin(dim1,2)+X_lin(dim1,3)),&
438 &sum(X_lin(dim1,:))/3/)
439 end do
440 qele = (ele-1)*3+3
441 call set(QuadX,ele_nodes(QuadX,qele),QX_vals)
442 end do
443
444 end subroutine quadX_from_bubbleX
445
446 subroutine quadSF_from_bubbleSF(QuadSF,sfield)
447 type(scalar_field), intent(inout) :: QuadSF
448 type(scalar_field), intent(in) :: sfield
449
450 !
451 real, dimension(ele_loc(sfield,1)) :: s_vals
452 real, dimension(ele_loc(QuadSF,1)) :: qs_vals
453 integer :: qele,ele
454 real, dimension(7) :: N_vals
455
456 !Basis functions evaluated at bubble node.
457 N_vals = eval_shape(ele_shape(sfield,1), (/1.0/3.0,1.0/3.0,1.0/3.0/))
458
459 do ele = 1, ele_count(sfield)
460 s_vals = ele_val(sfield,ele)
461 assert(size(s_vals)==7)
462 s_vals(7) = s_vals(7)*N_vals(7)
463 s_vals(7) = s_vals(7) + sum(s_vals(1:6)*N_vals(1:6))
464 !! Quadrilateral #1
465 QS_vals(:) = &
466 &(/s_vals(1),s_vals(2),s_vals(4),s_vals(7)/)
467 qele = (ele-1)*3+1
468 call set(QuadSF,ele_nodes(QuadSF,qele),QS_vals)
469 !! Quadrilateral #2
470 QS_vals(:) = &
471 &(/s_vals(3),s_vals(5),s_vals(2),s_vals(7)/)
472 qele = (ele-1)*3+2
473 call set(QuadSF,ele_nodes(QuadSF,qele),QS_vals)
474 !! Quadrilateral #3
475 QS_vals(:) = &
476 &(/s_vals(6),s_vals(4),s_vals(5),s_vals(7)/)
477 qele = (ele-1)*3+3
478 call set(QuadSF,ele_nodes(QuadSF,qele),QS_vals)
479 end do
480 end subroutine quadSF_from_bubbleSF
481 end subroutine bubble_field_to_vtk
482
483#include "../femtools/Reference_count_mesh_type.F90"
484
485 end module bubble_tools
0486
=== modified file 'assemble/Diagnostic_fields_wrapper.F90'
--- assemble/Diagnostic_fields_wrapper.F90 2012-09-18 08:03:27 +0000
+++ assemble/Diagnostic_fields_wrapper.F90 2012-10-23 15:04:25 +0000
@@ -79,7 +79,7 @@
79 ! An array of submaterials of the current phase in state(istate).79 ! An array of submaterials of the current phase in state(istate).
80 type(state_type), dimension(:), pointer :: submaterials80 type(state_type), dimension(:), pointer :: submaterials
81 81
82 ewrite(1, *) "In calculate_diagnostic_variables"82 ewrite(1, *) "In calculate_diagnostic_variables (Diagnostic_fields_wrapper)"
83 83
84 do i = 1, size(state)84 do i = 1, size(state)
8585
8686
=== modified file 'assemble/Hybridized_Helmholtz.F90'
--- assemble/Hybridized_Helmholtz.F90 2011-07-22 12:39:08 +0000
+++ assemble/Hybridized_Helmholtz.F90 2012-10-23 15:04:25 +0000
@@ -24,7 +24,6 @@
24 ! License along with this library; if not, write to the Free Software24 ! License along with this library; if not, write to the Free Software
25 ! Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-130725 ! Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
26 ! USA26 ! USA
27
2827
29#include "fdebug.h"28#include "fdebug.h"
3029
@@ -44,18 +43,213 @@
44 use diagnostic_fields_wrapper43 use diagnostic_fields_wrapper
45 use assemble_cmc44 use assemble_cmc
46 use FUtils, only : real_vector, real_matrix45 use FUtils, only : real_vector, real_matrix
47 use global_parameters, only: option_path_len46 use global_parameters, only: option_path_len, PYTHON_FUNC_LEN
48 use vector_tools, only: solve47 use vector_tools, only: solve
49 use manifold_projections48 use manifold_tools
50 implicit none49 implicit none
5150
52contains51 !Variables belonging to the Newton solver
52 logical, private :: newton_initialised = .false.
53 !Cached Newton solver matrices
54 type(csr_matrix), private :: Newton_lambda_mat
55 type(block_csr_matrix), private :: Newton_continuity_mat
56 type(real_matrix), pointer, dimension(:), private &
57 &:: Newton_local_solver_cache=>null()
58 type(real_matrix), pointer, dimension(:), private &
59 &:: Newton_local_solver_rhs_cache=>null()
60
61contains
62
63 subroutine solve_hybridised_timestep_residual(state,newU,newD,&
64 UResidual, DResidual)
65
66 ! Subroutine to apply one Newton iteration to hybridised shallow
67 ! water equations
68 ! Written to cache all required matrices
69 implicit none
70 type(state_type), intent(inout) :: state
71 type(vector_field), intent(inout) :: newU !U at next timestep
72 type(scalar_field), intent(inout) :: newD !D at next timestep
73 type(vector_field), intent(in), optional :: UResidual
74 type(scalar_field), intent(in), optional :: DResidual
75 !
76 type(vector_field), pointer :: X, U, down, U_cart, U_old
77 type(scalar_field), pointer :: D,f, D_old
78 type(scalar_field) :: lambda, D_res, D_res2
79 type(vector_field) :: U_res, U_res2
80 type(scalar_field), target :: lambda_rhs, u_cpt
81 type(csr_sparsity) :: lambda_sparsity, continuity_sparsity
82 type(csr_matrix) :: continuity_block_mat
83 type(mesh_type), pointer :: lambda_mesh
84 real :: D0, dt, g, theta, tolerance
85 integer :: ele,i1, stat, dim1, dloc, uloc,mdim,n_constraints
86 logical :: have_constraint
87 character(len=OPTION_PATH_LEN) :: constraint_option_string
88
89 ewrite(1,*) 'solve_hybridised_timestep_residual(state)'
90
91 ewrite(1,*) 'TIMESTEPPING LAMBDA'
92
93 !get parameters
94 call get_option("/physical_parameters/gravity/magnitude", g)
95 call get_option("/timestepping/theta",theta)
96 call get_option("/material_phase::Fluid/scalar_field::LayerThickness/&
97 &prognostic/mean_layer_thickness",D0)
98 call get_option("/timestepping/timestep", dt)
99
100 !Pull the fields out of state
101 D=>extract_scalar_field(state, "LayerThickness")
102 f=>extract_scalar_field(state, "Coriolis")
103 U=>extract_vector_field(state, "LocalVelocity")
104 X=>extract_vector_field(state, "Coordinate")
105 down=>extract_vector_field(state, "GravityDirection")
106 U_cart => extract_vector_field(state, "Velocity")
107 U_old => extract_vector_field(state, "OldLocalVelocity")
108 D_old => extract_scalar_field(state, "OldLayerThickness")
109
110 !Allocate local field variables
111 lambda_mesh=>extract_mesh(state, "VelocityMeshTrace")
112 call allocate(lambda,lambda_mesh,name="LagrangeMultiplier")
113 call allocate(lambda_rhs,lambda%mesh,"LambdaRHS")
114 call zero(lambda_rhs)
115 call allocate(U_res,U%dim,U%mesh,"VelocityResidual")
116 call allocate(D_res,D%mesh,"LayerThicknessResidual")
117 call allocate(U_res2,U%dim,U%mesh,"VelocityResidual2")
118 call allocate(D_res2,D%mesh,"LayerThicknessResidual2")
119
120 if(.not.newton_initialised) then
121 !construct/extract sparsities
122 lambda_sparsity=get_csr_sparsity_firstorder(&
123 &state, lambda%mesh, lambda&
124 &%mesh)
125 continuity_sparsity=get_csr_sparsity_firstorder(state, u%mesh,&
126 & lambda%mesh)
127
128 !allocate matrices
129 call allocate(Newton_lambda_mat,lambda_sparsity)
130 call zero(Newton_lambda_mat)
131 call allocate(Newton_continuity_mat,continuity_sparsity,(/U%dim,1/))
132 call zero(Newton_continuity_mat)
133
134 mdim = mesh_dim(U)
135 have_constraint = &
136 &have_option(trim(U%mesh%option_path)//"/from_mesh/constraint_type")
137
138 allocate(newton_local_solver_cache(ele_count(U)))
139 allocate(newton_local_solver_rhs_cache(ele_count(U)))
140 do ele = 1, ele_count(D)
141 uloc = ele_loc(U,ele)
142 dloc = ele_loc(d,ele)
143 n_constraints = 0
144 if(have_constraint) then
145 n_constraints = ele_n_constraints(U,ele)
146 allocate(newton_local_solver_cache(ele)%ptr(&
147 mdim*uloc+dloc+n_constraints,&
148 mdim*uloc+dloc+n_constraints))
149 allocate(newton_local_solver_rhs_cache(ele)%ptr(&
150 mdim*uloc+dloc,mdim*uloc+dloc))
151 end if
152 end do
153
154 !Assemble matrices
155 do ele = 1, ele_count(D)
156 call assemble_newton_solver_ele(D,f,U,X,down,lambda_rhs,ele, &
157 &g,dt,theta,D0,&
158 &lambda_mat=Newton_lambda_mat,&
159 &continuity_mat=Newton_continuity_mat,&
160 &Local_solver_matrix=Newton_local_solver_cache(ele)%ptr,&
161 &Local_solver_rhs=Newton_local_solver_rhs_cache(ele)%ptr)
162 end do
163 newton_initialised = .true.
164 end if
165
166 if(present(DResidual).and.present(UResidual).and..not.&
167 have_option('/material_phase::Fluid/vector_field::Velocity/prognostic/wave_equation/fully_coupled/linear_debug')) then
168 !Set residuals from nonlinear input
169 call set(U_res,UResidual)
170 call set(D_res,DResidual)
171 else
172 !Compute residuals for linear equation
173 do ele = 1, ele_count(D)
174 call get_linear_residuals_ele(U_res,D_res,&
175 U_old,D_old,newU,newD,&
176 newton_local_solver_cache(ele)%ptr,&
177 newton_local_solver_rhs_cache(ele)%ptr,ele)
178 end do
179 ewrite(2,*) 'cjc U_res calc', sum(newU%val), maxval(abs(U_res%val))
180 ewrite(2,*) 'cjc D_res calc', sum(newD%val), maxval(abs(D_res%val))
181 end if
182
183 do ele = 1, ele_count(D)
184 call local_solve_residuals_ele(U_res,D_res,&
185 newton_local_solver_cache(ele)%ptr,ele)
186 end do
187
188 call mult_t(lambda_rhs,Newton_continuity_mat,U_res)
189 call scale(lambda_rhs,-1.0)
190 ewrite(2,*) 'LAMBDARHS', maxval(abs(lambda_rhs%val))
191
192 call zero(lambda)
193 !Solve the equations
194 call petsc_solve(lambda,newton_lambda_mat,lambda_rhs,&
195 option_path=trim(U_cart%mesh%option_path)//&
196 &"/from_mesh/constraint_type")
197 ewrite(2,*) 'LAMBDA', maxval(abs(lambda%val))
198
199 !Update new U and new D from lambda
200 !Compute residuals
201 if(present(DResidual).and.present(UResidual).and..not.&
202 have_option('/material_phase::Fluid/vector_field::Velocity/prognostic/wave_equation/fully_coupled/linear_debug')) then
203 call set(U_res,UResidual)
204 call set(D_res,DResidual)
205 else
206 do ele = 1, ele_count(D)
207 call get_linear_residuals_ele(U_res,D_res,&
208 U_old,D_old,newU,newD,&
209 newton_local_solver_cache(ele)%ptr,&
210 newton_local_solver_rhs_cache(ele)%ptr,ele)
211 end do
212 end if
213 ewrite(2,*) 'cjc U_res recalc', sum(newU%val), maxval(abs(U_res%val))
214 ewrite(2,*) 'cjc D_res recalc', sum(newD%val), maxval(abs(D_res%val))
215
216 call mult(U_res2,Newton_continuity_mat,lambda)
217 call addto(U_res,U_res2)
218 do ele = 1, ele_count(U)
219 call local_solve_residuals_ele(U_res,D_res,&
220 newton_local_solver_cache(ele)%ptr,ele)
221 end do
222
223 !Negative scaling occurs here.
224 call scale(U_res,-1.0)
225 call scale(D_res,-1.0)
226
227 ewrite(2,*) 'Delta U', maxval(abs(U_res%val))
228 ewrite(2,*) 'Delta D', maxval(abs(D_res%val))
229
230 call addto(newU,U_res)
231 call addto(newD,D_res)
232
233 !Deallocate local variables
234 call deallocate(lambda_rhs)
235 call deallocate(lambda)
236 call deallocate(U_res)
237 call deallocate(D_res)
238 call deallocate(U_res2)
239 call deallocate(D_res2)
240
241 ewrite(1,*) 'END solve_hybridised_timestep_residual(state)'
242
243 end subroutine solve_hybridised_timestep_residual
244
53 subroutine solve_hybridized_helmholtz(state,D_rhs,U_Rhs,&245 subroutine solve_hybridized_helmholtz(state,D_rhs,U_Rhs,&
54 &D_out,U_out,&246 &D_out,U_out,&
247 &dt_in,theta_in,&
55 &compute_cartesian,&248 &compute_cartesian,&
56 &check_continuity,output_dense,&249 &output_dense,&
57 &projection,poisson,u_rhs_local,&250 &projection,poisson,&
58 &solver_option_path)251 &u_rhs_local)
252
59 ! Subroutine to solve hybridized helmholtz equation253 ! Subroutine to solve hybridized helmholtz equation
60 ! If D_rhs (scalar pressure field) is present, then solve:254 ! If D_rhs (scalar pressure field) is present, then solve:
61 ! <w,u> + <w,fu^\perp> - g <div w,d> + <<[w],d>> = <w,U_rhs>255 ! <w,u> + <w,fu^\perp> - g <div w,d> + <<[w],d>> = <w,U_rhs>
@@ -63,7 +257,7 @@
63 ! <<\gamma, [u]>> = 0257 ! <<\gamma, [u]>> = 0
64 ! (i.e. for unit testing)258 ! (i.e. for unit testing)
65 ! otherwise solve:259 ! otherwise solve:
66 ! <w,u> + dt*theta*<w,fu^\perp> - dt*theta*g <div w,d> + <<[w],d>> = 260 ! <w,u> + dt*theta*<w,fu^\perp> - dt*theta*g <div w,d> + <<[w],l>> =
67 ! -dt*<w f(u^n)^\perp> + dt*g*<div w, d^n>261 ! -dt*<w f(u^n)^\perp> + dt*g*<div w, d^n>
68 ! <\phi,\eta> + dt*theta*<\phi,div u> = <\ph262 ! <\phi,\eta> + dt*theta*<\phi,div u> = <\ph
69 ! <<\gamma, [u]>> = 0263 ! <<\gamma, [u]>> = 0
@@ -76,10 +270,10 @@
76 type(vector_field), intent(inout), optional :: U_rhs270 type(vector_field), intent(inout), optional :: U_rhs
77 type(scalar_field), intent(inout), optional :: D_out271 type(scalar_field), intent(inout), optional :: D_out
78 type(vector_field), intent(inout), optional :: U_out272 type(vector_field), intent(inout), optional :: U_out
273 real, intent(in), optional :: theta_in,dt_in
79 logical, intent(in), optional :: compute_cartesian, &274 logical, intent(in), optional :: compute_cartesian, &
80 &check_continuity,output_dense, projection,poisson275 &output_dense, projection,poisson
81 logical, intent(in), optional :: u_rhs_local !means u_rhs is in local coords276 logical, intent(in), optional :: u_rhs_local !means u_rhs is in local coords
82 character(len=OPTION_PATH_LEN), intent(in), optional :: solver_option_path
83 !277 !
84 type(vector_field), pointer :: X, U, down, U_cart278 type(vector_field), pointer :: X, U, down, U_cart
85 type(scalar_field), pointer :: D,f, lambda_nc279 type(scalar_field), pointer :: D,f, lambda_nc
@@ -89,21 +283,36 @@
89 type(csr_matrix) :: lambda_mat, continuity_block_mat,continuity_block_mat1283 type(csr_matrix) :: lambda_mat, continuity_block_mat,continuity_block_mat1
90 type(block_csr_matrix) :: continuity_mat284 type(block_csr_matrix) :: continuity_mat
91 type(mesh_type), pointer :: lambda_mesh285 type(mesh_type), pointer :: lambda_mesh
92 real :: D0, dt, g, theta286 real :: D0, dt, g, theta, tolerance
93 integer :: ele,i1, stat, dim1287 integer :: ele,i1, stat, dim1
94 logical :: l_compute_cartesian,l_check_continuity, l_output_dense288 logical :: l_compute_cartesian, l_output_dense
95 real, dimension(:,:), allocatable :: lambda_mat_dense289 real, dimension(:,:), allocatable :: lambda_mat_dense
96 character(len=OPTION_PATH_LEN) :: constraint_option_string290 character(len=OPTION_PATH_LEN) :: constraint_option_string
291 real :: u_max
292 real, dimension(:), allocatable :: weights
293 logical :: l_projection,l_poisson, pullback
97294
98 ewrite(1,*) ' subroutine solve_hybridized_helmholtz('295 ewrite(1,*) ' subroutine solve_hybridized_helmholtz('
99296
100 l_compute_cartesian = .false.297 l_compute_cartesian = .false.
101 if(present(compute_cartesian)) l_compute_cartesian = compute_cartesian298 if(present(compute_cartesian)) l_compute_cartesian = compute_cartesian
102 if(present(check_continuity)) l_check_continuity = check_continuity
103 if(l_check_continuity) l_compute_cartesian = .true.
104 l_output_dense = .false.299 l_output_dense = .false.
105 if(present(output_dense)) l_output_dense = output_dense300 if(present(output_dense)) l_output_dense = output_dense
106301
302 if(present(projection)) then
303 l_projection = projection
304 else
305 l_projection = .false.
306 end if
307 if(present(poisson)) then
308 l_poisson = poisson
309 end if
310 ewrite(2,*) 'Projection = ', l_projection
311 ewrite(2,*) 'Poisson = ', l_poisson
312 if(l_poisson.and.l_projection) then
313 FLAbort('Can''t do projection and poisson')
314 end if
315
107 !Pull the fields out of state316 !Pull the fields out of state
108 D=>extract_scalar_field(state, "LayerThickness")317 D=>extract_scalar_field(state, "LayerThickness")
109 f=>extract_scalar_field(state, "Coriolis")318 f=>extract_scalar_field(state, "Coriolis")
@@ -134,68 +343,86 @@
134 !get parameters343 !get parameters
135 call get_option("/physical_parameters/gravity/magnitude", g)344 call get_option("/physical_parameters/gravity/magnitude", g)
136 !theta345 !theta
137 call get_option("/material_phase::Fluid/scalar_field::LayerThickness/&346
138 &prognostic/temporal_discretisation/theta",theta)347 if(present(theta_in)) then
348 theta = theta_in
349 else
350 call get_option("/timestepping/theta",theta)
351 end if
139 !D0352 !D0
140 call get_option("/material_phase::Fluid/scalar_field::LayerThickness/&353 call get_option("/material_phase::Fluid/scalar_field::LayerThickness/&
141 &p&354 &prognostic/mean_layer_thickness",D0)
142 &rognostic/mean_layer_thickness",D0)355 if(present(dt_in)) then
143 call get_option("/timestepping/timestep", dt)356 dt = dt_in
144 357 else
358 call get_option("/timestepping/timestep", dt)
359 end if
360
361 !compute rescaling weights for local coordinates
362 allocate(weights(element_count(X)))
363 weights = 1.0
364 !call get_weights(X,weights)
365
366 pullback = have_option('/material_phase::Fluid/scalar_field::LayerThickn&
367 &ess/prognostic/spatial_discretisation/discontinuous_galerkin/wave_&
368 &equation/pullback')
369
145 !Assemble matrices370 !Assemble matrices
146 do ele = 1, ele_count(D)371 do ele = 1, ele_count(D)
147 call assemble_hybridized_helmholtz_ele(D,f,U,X,down,ele, &372 call assemble_hybridized_helmholtz_ele(D,f,U,X,down,ele, &
148 &g,dt,theta,D0,lambda_mat=lambda_mat,&373 &g,dt,theta,D0,pullback,weights(ele),lambda_mat=lambda_mat,&
149 &lambda_rhs=lambda_rhs,D_rhs=D_rhs,U_rhs=U_rhs,&374 &lambda_rhs=lambda_rhs,D_rhs=D_rhs,U_rhs=U_rhs,&
150 &continuity_mat=continuity_mat,&375 &continuity_mat=continuity_mat,&
151 &projection=projection,poisson=poisson,&376 &projection=l_projection,poisson=l_poisson,&
152 &u_rhs_local=u_rhs_local)377 &u_rhs_local=u_rhs_local)
153 end do378 end do
154379
155 ewrite(1,*) 'LAMBDARHS', maxval(abs(lambda_rhs%val))380 ewrite(2,*) 'LAMBDARHS', maxval(abs(lambda_rhs%val))
156381 call zero(lambda)
157 !Solve the equations382 !Solve the equations
158 if(present(solver_option_path)) then383 call petsc_solve(lambda,lambda_mat,lambda_rhs,&
159 call petsc_solve(lambda,lambda_mat,lambda_rhs,&384 option_path=trim(U_cart%mesh%option_path)//&
160 option_path=solver_option_path)385 &"/from_mesh/constraint_type")
161 else
162 call petsc_solve(lambda,lambda_mat,lambda_rhs,&
163 option_path=trim(U_cart%mesh%option_path)//&
164 &"/from_mesh/constraint_type")
165 end if
166 ewrite(1,*) 'LAMBDA', maxval(abs(lambda%val))
167386
168 !Reconstruct U and D from lambda387 !Reconstruct U and D from lambda
169 do ele = 1, ele_count(D)388 do ele = 1, ele_count(D)
170 call reconstruct_u_d_ele(D,f,U,X,down,ele, &389 call reconstruct_u_d_ele(D,f,U,X,down,ele, &
171 &g,dt,theta,D0,D_rhs=D_rhs,U_rhs=U_rhs,lambda=lambda,&390 &g,dt,theta,D0,pullback,weights(ele),&
391 &D_rhs=D_rhs,U_rhs=U_rhs,lambda=lambda,&
172 &D_out=D_out,U_out=U_out,&392 &D_out=D_out,U_out=U_out,&
173 &projection=projection,poisson=poisson,&393 &projection=l_projection,poisson=l_poisson,&
174 &u_rhs_local=u_rhs_local)394 &u_rhs_local=u_rhs_local)
175 end do395 end do
176396
397 if(l_poisson.and.present(D_out)) then
398 call check_zero_level(D_out)
399 end if
400 ewrite(2,*) 'LAMBDA', maxval(abs(lambda%val))
401
177 if(l_output_dense) then402 if(l_output_dense) then
178 allocate(lambda_mat_dense(node_count(lambda),node_count(lambda)))403 allocate(lambda_mat_dense(node_count(lambda),node_count(lambda)))
179 lambda_mat_dense = dense(lambda_mat)404 lambda_mat_dense = dense(lambda_mat)
180 ewrite(1,*) '-----------'405 ewrite(2,*) '-----------'
181 do i1 = 1, node_count(lambda)406 do i1 = 1, node_count(lambda)
182 ewrite(1,*) lambda_mat_dense(i1,:)407 ewrite(2,*) lambda_mat_dense(i1,:)
183 end do408 end do
184 ewrite(1,*) '-----------'409 ewrite(2,*) '-----------'
185 end if410 end if
186411
187 if(l_compute_cartesian) then412 if(l_compute_cartesian) then
188 U_cart => extract_vector_field(state, "Velocity")413 U_cart => extract_vector_field(state, "Velocity")
189 if(present(U_out)) then414 if(present(U_out)) then
190 call project_local_to_cartesian(X,U_out,U_cart)415 call project_local_to_cartesian(X,U_out,U_cart,weights=weights)
191 else416 else
192 call project_local_to_cartesian(X,U,U_cart)417 call project_local_to_cartesian(X,U,U_cart,weights=weights)
193 end if418 end if
194 end if419 end if
195 if(l_check_continuity) then420
196 ewrite(1,*) 'Checking continuity'421 if(have_option('/geometry/mesh::VelocityMesh/check_continuity_matrix')) then
422 ewrite(2,*) 'Checking continuity'
197423
198 call zero(lambda_rhs)424 call zero(lambda_rhs)
425 u_max = 0.
199 do dim1 = 1,U%dim426 do dim1 = 1,U%dim
200 if(present(U_out)) then427 if(present(U_out)) then
201 u_cpt = extract_scalar_field(U_out,dim1)428 u_cpt = extract_scalar_field(U_out,dim1)
@@ -204,29 +431,264 @@
204 end if431 end if
205 continuity_block_mat = block(continuity_mat,dim1,1)432 continuity_block_mat = block(continuity_mat,dim1,1)
206 call mult_T_addto(lambda_rhs,continuity_block_mat,u_cpt)433 call mult_T_addto(lambda_rhs,continuity_block_mat,u_cpt)
207 ewrite(1,*) 'U, lambda',&434 ewrite(2,*) 'U, lambda',&
208 &maxval(u_cpt%val), maxval(abs(lambda_rhs%val))435 &maxval(u_cpt%val), maxval(abs(lambda_rhs%val))
436 u_max = u_max + maxval(abs(u_cpt%val))
209 end do437 end do
210 ewrite(1,*)'JUMPS MIN:MAX',minval(lambda_rhs%val),&438 ewrite(2,*)'JUMPS MIN:MAX',minval(lambda_rhs%val),&
211 &maxval(lambda_rhs%val)439 &maxval(lambda_rhs%val), u_max
212 assert(maxval(abs(lambda_rhs%val))<1.0e-10)440
213 441 call get_option('/geometry/mesh::VelocityMesh/check_continuity_matrix/tolerance',tolerance)
214 ewrite(1,*) 'D MAXABS', maxval(abs(D%val))442 if(maxval(abs(lambda_rhs%val))/max(1.0,u_max/3.0)>tolerance) then
443 ewrite(-1,*) 'value =', maxval(abs(lambda_rhs%val))/max(1.0,u_max/3.0)
444 FLExit('Continuity matrix tolerance failure')
445 end if
446 end if
447
448 if(have_option('/geometry/mesh::VelocityMesh/check_continuity')) then
449 U_cart => extract_vector_field(state, "Velocity")
450 if(present(U_out)) then
451 call project_local_to_cartesian(X,U_out,U_cart,weights=weights)
452 else
453 call project_local_to_cartesian(X,U,U_cart,weights=weights)
454 end if
455 call get_option('/geometry/mesh::VelocityMesh/check_continuity/tolerance', tolerance)
215 do ele = 1, ele_count(U)456 do ele = 1, ele_count(U)
216 call check_continuity_ele(U_cart,X,ele)457 call check_continuity_ele(U_cart,X,ele,tolerance)
217 end do458 end do
218 end if459 end if
219 460
220 call deallocate(lambda_mat)461 call deallocate(lambda_mat)
462 call deallocate(continuity_mat)
221 call deallocate(lambda_rhs)463 call deallocate(lambda_rhs)
222 call deallocate(lambda)464 call deallocate(lambda)
465 deallocate(weights)
223466
224 ewrite(1,*) 'END subroutine solve_hybridized_helmholtz'467 ewrite(1,*) 'END subroutine solve_hybridized_helmholtz'
225468
226 end subroutine solve_hybridized_helmholtz469 end subroutine solve_hybridized_helmholtz
470
471 subroutine get_linear_residuals_ele(U_res,D_res,U,D,&
472 newU,newD,local_solver,local_solver_rhs,ele)
473 !Subroutine
474 type(vector_field), intent(inout) :: U_res,U,newU
475 type(scalar_field), intent(inout) :: D_res,D,newD
476 real, dimension(:,:), intent(in) :: local_solver, local_solver_rhs
477 integer, intent(in) :: ele
478 !
479 integer :: uloc, dloc, dim1, d_start, d_end, mdim
480 integer, dimension(mesh_dim(U)) :: U_start, U_end
481 real, dimension(mesh_dim(U)*ele_loc(U,ele)+ele_loc(D,ele))&
482 :: rhs_loc1,rhs_loc2
483 real, dimension(mesh_dim(U),ele_loc(U,ele)) :: U_val
484
485 !Calculate indices in a vector containing all the U and D dofs in
486 !element ele, First the u1 components, then the u2 components, then the
487 !D components are stored.
488 uloc = ele_loc(U_res,ele)
489 dloc = ele_loc(D_res,ele)
490 d_end = mesh_dim(U_res)*uloc+dloc
491 mdim = mesh_dim(U)
492 do dim1 = 1, mdim
493 u_start(dim1) = uloc*(dim1-1)+1
494 u_end(dim1) = uloc*dim1
495 end do
496 d_start = uloc*mdim + 1
497 d_end = uloc*mdim+dloc
498
499 U_val = ele_val(U,ele)
500 do dim1 = 1, mesh_dim(U)
501 rhs_loc1(u_start(dim1):u_end(dim1)) = u_val(dim1,:)
502 end do
503 rhs_loc1(d_start:d_end) = ele_val(D,ele)
504 rhs_loc1 = matmul(local_solver_rhs,rhs_loc1)
505 U_val = ele_val(newU,ele)
506 do dim1 = 1, mesh_dim(U)
507 rhs_loc2(u_start(dim1):u_end(dim1)) = u_val(dim1,:)
508 end do
509 rhs_loc2(d_start:d_end) = ele_val(newD,ele)
510 rhs_loc2 = matmul(local_solver(1:d_end,1:d_end),rhs_loc2)
511 rhs_loc2 = rhs_loc2-rhs_loc1
512 do dim1 = 1, mesh_dim(U)
513 call set(U_res,dim1,ele_nodes(U,ele),&
514 rhs_loc2(u_start(dim1):u_end(dim1)))
515 end do
516 call set(D_res,ele_nodes(D,ele),&
517 rhs_loc2(d_start:d_end))
518 end subroutine get_linear_residuals_ele
519
520 subroutine local_solve_residuals_ele(U_res,D_res,&
521 local_solver_matrix,ele)
522 type(vector_field), intent(inout) :: U_res
523 type(scalar_field), intent(inout) :: D_res
524 real, dimension(:,:), intent(in) :: local_solver_matrix
525 integer, intent(in) :: ele
526 !
527 real, dimension(mesh_dim(U_res)*ele_loc(U_res,ele)+ele_loc(D_res,ele)+&
528 &ele_n_constraints(U_res,ele)) :: rhs_loc
529 real, dimension(mesh_dim(U_res),ele_loc(U_res,ele)) :: U_val
530 integer :: uloc,dloc, d_start, d_end, dim1, mdim
531 integer, dimension(mesh_dim(U_res)) :: U_start, U_end
532
533 rhs_loc = 0.
534
535 !Calculate indices in a vector containing all the U and D dofs in
536 !element ele, First the u1 components, then the u2 components, then the
537 !D components are stored.
538 uloc = ele_loc(U_res,ele)
539 dloc = ele_loc(D_res,ele)
540 d_end = mesh_dim(U_res)*uloc+dloc
541 mdim = mesh_dim(U_res)
542 do dim1 = 1, mdim
543 u_start(dim1) = uloc*(dim1-1)+1
544 u_end(dim1) = uloc*dim1
545 end do
546 d_start = uloc*mdim + 1
547 d_end = uloc*mdim+dloc
548
549 U_val = ele_val(U_res,ele)
550 do dim1 = 1, mesh_dim(U_res)
551 rhs_loc(u_start(dim1):u_end(dim1)) = u_val(dim1,:)
552 end do
553 rhs_loc(d_start:d_end) = &
554 & ele_val(D_res,ele)
555
556 call solve(local_solver_matrix,Rhs_loc)
557 do dim1 = 1, mesh_dim(U_res)
558 call set(U_res,dim1,ele_nodes(U_res,ele),&
559 rhs_loc(u_start(dim1):u_end(dim1)))
560 end do
561 call set(D_res,ele_nodes(D_res,ele),&
562 rhs_loc(d_start:d_end))
563 end subroutine local_solve_residuals_ele
564
565 subroutine assemble_newton_solver_ele(D,f,U,X,down,lambda_rhs,ele, &
566 &g,dt,theta,D0,&
567 &lambda_mat,continuity_mat,&
568 &Local_solver_matrix,Local_solver_rhs)
569 implicit none
570 type(scalar_field), intent(in) :: D,f
571 type(scalar_field), intent(in) :: lambda_rhs
572 type(vector_field), intent(in) :: U,X,down
573 integer, intent(in) :: ele
574 real, intent(in) :: g,dt,theta,D0
575 type(csr_matrix), intent(inout) :: lambda_mat
576 type(block_csr_matrix), intent(inout) :: continuity_mat
577 real, dimension(:,:), intent(inout) ::&
578 & local_solver_matrix
579 real, dimension(:,:), intent(inout) ::&
580 & local_solver_rhs
581 !
582 real, allocatable, dimension(:,:),target :: &
583 &l_continuity_mat, l_continuity_mat2
584 real, allocatable, dimension(:,:) :: helmholtz_loc_mat
585 real, allocatable, dimension(:,:,:) :: continuity_face_mat
586 real, allocatable, dimension(:,:) :: scalar_continuity_face_mat
587 integer :: ni, face
588 integer, dimension(:), pointer :: neigh
589 type(element_type) :: U_shape
590 integer :: stat, d_start, d_end, dim1, mdim, uloc,dloc, lloc, iloc
591 integer, dimension(mesh_dim(U)) :: U_start, U_end
592 type(real_matrix), dimension(mesh_dim(U)) :: &
593 & continuity_mat_u_ptr
594 logical :: have_constraint
595 integer :: constraint_choice, n_constraints, constraints_start
596
597 !Get some sizes
598 lloc = ele_loc(lambda_rhs,ele)
599 mdim = mesh_dim(U)
600 uloc = ele_loc(U,ele)
601 dloc = ele_loc(d,ele)
602 U_shape = ele_shape(U,ele)
603
604 have_constraint = &
605 &have_option(trim(U%mesh%option_path)//"/from_mesh/constraint_type")
606 n_constraints = 0
607 if(have_constraint) then
608 n_constraints = ele_n_constraints(U,ele)
609 end if
610
611 !Calculate indices in a vector containing all the U and D dofs in
612 !element ele, First the u1 components, then the u2 components, then the
613 !D components are stored.
614 do dim1 = 1, mdim
615 u_start(dim1) = uloc*(dim1-1)+1
616 u_end(dim1) = uloc*dim1
617 end do
618 d_start = uloc*mdim + 1
619 d_end = uloc*mdim+dloc
620
621 !Get pointers to different parts of l_continuity_mat
622 allocate(l_continuity_mat(2*uloc+dloc+n_constraints,lloc))
623 do dim1= 1,mdim
624 continuity_mat_u_ptr(dim1)%ptr => &
625 & l_continuity_mat(u_start(dim1):u_end(dim1),:)
626 end do
627
628 ! ( M C -L)(u) (v)
629 ! ( -C^T N 0 )(h) = (j)
630 ! ( L^T 0 0 )(l) (0)
631 !
632 ! (u) (M C)^{-1}(v) (M C)^{-1}(L)
633 ! (h) = (-C^T N) (j) + (-C^T N) (0)(l)
634 ! so
635 ! (M C)^{-1}(L) (M C)^{-1}(v)
636 ! (L^T 0)(-C^T N) (0)=-(L^T 0)(-C^T N) (j)
637
638 !Get the local_solver matrix that obtains U and D from Lambda on the
639 !boundaries
640 call get_local_solver(local_solver_matrix,U,X,down,D,f,ele,&
641 & g,dt,theta,D0,pullback=.false.,weight=1.0,&
642 & have_constraint=have_constraint,&
643 & projection=.false.,poisson=.false.,&
644 & local_solver_rhs=local_solver_rhs)
645
646 !!!Construct the continuity matrix that multiplies lambda in
647 !!! the U equation
648 !allocate l_continuity_mat
649 l_continuity_mat = 0.
650 !get list of neighbours
651 neigh => ele_neigh(D,ele)
652 !calculate l_continuity_mat
653 do ni = 1, size(neigh)
654 face=ele_face(U, ele, neigh(ni))
655 allocate(continuity_face_mat(mdim,face_loc(U,face)&
656 &,face_loc(lambda_rhs,face)))
657 continuity_face_mat = 0.
658 call get_continuity_face_mat(continuity_face_mat,face,&
659 U,lambda_rhs)
660 do dim1 = 1, mdim
661 continuity_mat_u_ptr(dim1)%ptr(face_local_nodes(U,face),&
662 face_local_nodes(lambda_rhs,face))=&
663 continuity_mat_u_ptr(dim1)%ptr(face_local_nodes(U,face),&
664 face_local_nodes(lambda_rhs,face))+&
665 continuity_face_mat(dim1,:,:)
666 end do
667 do dim1 = 1, mdim
668 call addto(continuity_mat,dim1,1,face_global_nodes(U,face)&
669 &,face_global_nodes(lambda_rhs,face),&
670 &continuity_face_mat(dim1,:,:))
671 end do
672
673 deallocate(continuity_face_mat)
674 end do
675
676 !compute l_continuity_mat2 = inverse(local_solver)*l_continuity_mat
677 allocate(l_continuity_mat2(uloc*2+dloc+n_constraints,lloc))
678 l_continuity_mat2 = l_continuity_mat
679 call solve(local_solver_matrix,l_continuity_mat2)
680
681 !compute helmholtz_loc_mat
682 allocate(helmholtz_loc_mat(lloc,lloc))
683 helmholtz_loc_mat = matmul(transpose(l_continuity_mat),l_continuity_mat2)
684
685 !insert helmholtz_loc_mat into global lambda matrix
686 call addto(lambda_mat,ele_nodes(lambda_rhs,ele),&
687 ele_nodes(lambda_rhs,ele),helmholtz_loc_mat)
688 end subroutine assemble_newton_solver_ele
227 689
228 subroutine assemble_hybridized_helmholtz_ele(D,f,U,X,down,ele, &690 subroutine assemble_hybridized_helmholtz_ele(D,f,U,X,down,ele, &
229 g,dt,theta,D0,lambda_mat,lambda_rhs,U_rhs,D_rhs,&691 g,dt,theta,D0,pullback,weight,lambda_mat,lambda_rhs,U_rhs,D_rhs,&
230 continuity_mat,projection,poisson,u_rhs_local)692 continuity_mat,projection,poisson,u_rhs_local)
231 !subroutine to assemble hybridized helmholtz equation.693 !subroutine to assemble hybridized helmholtz equation.
232 !For assembly, must provide:694 !For assembly, must provide:
@@ -243,7 +705,8 @@
243 type(vector_field), intent(in), optional :: U_rhs705 type(vector_field), intent(in), optional :: U_rhs
244 type(scalar_field), intent(in), optional :: D_rhs706 type(scalar_field), intent(in), optional :: D_rhs
245 integer, intent(in) :: ele707 integer, intent(in) :: ele
246 real, intent(in) :: g,dt,theta,D0708 logical, intent(in) :: pullback
709 real, intent(in) :: g,dt,theta,D0,weight
247 type(csr_matrix), intent(inout) :: lambda_mat710 type(csr_matrix), intent(inout) :: lambda_mat
248 type(block_csr_matrix), intent(inout), optional :: continuity_mat711 type(block_csr_matrix), intent(inout), optional :: continuity_mat
249 logical, intent(in), optional :: projection, poisson,u_rhs_local712 logical, intent(in), optional :: projection, poisson,u_rhs_local
@@ -259,7 +722,7 @@
259 real, dimension(:),allocatable,target :: Rhs_loc722 real, dimension(:),allocatable,target :: Rhs_loc
260 real, dimension(:,:), allocatable :: local_solver_matrix, local_solver_rhs723 real, dimension(:,:), allocatable :: local_solver_matrix, local_solver_rhs
261 type(element_type) :: U_shape724 type(element_type) :: U_shape
262 integer :: stat, d_start, d_end, dim1, mdim, uloc,dloc, lloc725 integer :: stat, d_start, d_end, dim1, mdim, uloc,dloc, lloc, iloc
263 integer, dimension(mesh_dim(U)) :: U_start, U_end726 integer, dimension(mesh_dim(U)) :: U_start, U_end
264 type(real_vector), dimension(mesh_dim(U)) :: rhs_u_ptr727 type(real_vector), dimension(mesh_dim(U)) :: rhs_u_ptr
265 real, dimension(:), pointer :: rhs_d_ptr728 real, dimension(:), pointer :: rhs_d_ptr
@@ -321,8 +784,15 @@
321 !Get the local_solver matrix that obtains U and D from Lambda on the784 !Get the local_solver matrix that obtains U and D from Lambda on the
322 !boundaries785 !boundaries
323 call get_local_solver(local_solver_matrix,U,X,down,D,f,ele,&786 call get_local_solver(local_solver_matrix,U,X,down,D,f,ele,&
324 & g,dt,theta,D0,have_constraint,local_solver_rhs,&787 & g,dt,theta,D0,pullback,weight,have_constraint,&
325 projection)788 & projection=projection,poisson=poisson,&
789 & local_solver_rhs=local_solver_rhs)
790 if(poisson.and.(ele==1)) then
791 !Fix the zero level of D
792 local_solver_matrix(d_start,:) = 0.
793 local_solver_matrix(:,d_start) = 0.
794 local_solver_matrix(d_start,d_start) = 1.
795 end if
326796
327 !!!Construct the continuity matrix that multiplies lambda in 797 !!!Construct the continuity matrix that multiplies lambda in
328 !!! the U equation798 !!! the U equation
@@ -343,13 +813,13 @@
343 face_local_nodes(lambda_rhs,face))=&813 face_local_nodes(lambda_rhs,face))=&
344 continuity_mat_u_ptr(dim1)%ptr(face_local_nodes(U,face),&814 continuity_mat_u_ptr(dim1)%ptr(face_local_nodes(U,face),&
345 face_local_nodes(lambda_rhs,face))+&815 face_local_nodes(lambda_rhs,face))+&
346 continuity_face_mat(dim1,:,:)816 weight*continuity_face_mat(dim1,:,:)
347 end do817 end do
348 if(present(continuity_mat)) then818 if(present(continuity_mat)) then
349 do dim1 = 1, mdim819 do dim1 = 1, mdim
350 call addto(continuity_mat,dim1,1,face_global_nodes(U,face)&820 call addto(continuity_mat,dim1,1,face_global_nodes(U,face)&
351 &,face_global_nodes(lambda_rhs,face),&821 &,face_global_nodes(lambda_rhs,face),&
352 &continuity_face_mat(dim1,:,:))822 &weight*continuity_face_mat(dim1,:,:))
353 end do823 end do
354 end if824 end if
355825
@@ -368,7 +838,12 @@
368 !construct lambda_rhs838 !construct lambda_rhs
369 rhs_loc=0.839 rhs_loc=0.
370 lambda_rhs_loc = 0.840 lambda_rhs_loc = 0.
371 call assemble_rhs_ele(Rhs_loc,D,U,X,ele,D_rhs,U_rhs,u_rhs_local)841 call assemble_rhs_ele(Rhs_loc,D,U,X,ele,pullback,&
842 weight,D_rhs,U_rhs,u_rhs_local)
843 if(poisson.and.(ele==1)) then
844 !Fix the zero level of D
845 rhs_loc(d_start)=0.0
846 end if
372 if(.not.(present(d_rhs).or.present(u_rhs)))then847 if(.not.(present(d_rhs).or.present(u_rhs)))then
373 assert(.not.present_and_true(projection))848 assert(.not.present_and_true(projection))
374 assert(.not.present_and_true(poisson))849 assert(.not.present_and_true(poisson))
@@ -384,9 +859,9 @@
384 ele_nodes(lambda_rhs,ele),helmholtz_loc_mat)859 ele_nodes(lambda_rhs,ele),helmholtz_loc_mat)
385860
386 end subroutine assemble_hybridized_helmholtz_ele861 end subroutine assemble_hybridized_helmholtz_ele
387862
388 subroutine reconstruct_U_d_ele(D,f,U,X,down,ele, &863 subroutine reconstruct_U_d_ele(D,f,U,X,down,ele, &
389 g,dt,theta,D0,U_rhs,D_rhs,lambda,&864 g,dt,theta,D0,pullback,weight,U_rhs,D_rhs,lambda,&
390 &D_out,U_out,projection,poisson,u_rhs_local)865 &D_out,U_out,projection,poisson,u_rhs_local)
391 !subroutine to reconstruct U and D having solved for lambda866 !subroutine to reconstruct U and D having solved for lambda
392 implicit none867 implicit none
@@ -399,12 +874,13 @@
399 type(scalar_field), intent(inout), optional :: D_out874 type(scalar_field), intent(inout), optional :: D_out
400 type(vector_field), intent(inout), optional :: U_out875 type(vector_field), intent(inout), optional :: U_out
401 integer, intent(in) :: ele876 integer, intent(in) :: ele
402 real, intent(in) :: g,dt,theta,D0877 logical, intent(in) :: pullback
878 real, intent(in) :: g,dt,theta,D0,weight
403 logical, intent(in), optional :: projection, poisson,u_rhs_local879 logical, intent(in), optional :: projection, poisson,u_rhs_local
404 !880 !
405 real, allocatable, dimension(:,:,:) :: continuity_face_mat881 real, allocatable, dimension(:,:,:) :: continuity_face_mat
406 integer :: ni, face882 integer :: ni, face
407 integer, dimension(:), pointer :: neigh883 integer, dimension(:), pointer :: neigh, d_nodes
408 type(element_type) :: U_shape884 type(element_type) :: U_shape
409 integer :: d_start, d_end, dim1, mdim, uloc,dloc,lloc885 integer :: d_start, d_end, dim1, mdim, uloc,dloc,lloc
410 integer, dimension(mesh_dim(U)) :: U_start, U_end886 integer, dimension(mesh_dim(U)) :: U_start, U_end
@@ -420,7 +896,7 @@
420 logical :: have_constraint896 logical :: have_constraint
421 integer :: n_constraints, i1897 integer :: n_constraints, i1
422 type(constraints_type), pointer :: constraints898 type(constraints_type), pointer :: constraints
423 real :: constraint_check899 real :: constraint_check, u_max
424900
425 !Get some sizes901 !Get some sizes
426 lloc = ele_loc(lambda,ele)902 lloc = ele_loc(lambda,ele)
@@ -460,12 +936,23 @@
460 !Get the local_solver matrix that obtains U and D from Lambda on the936 !Get the local_solver matrix that obtains U and D from Lambda on the
461 !boundaries937 !boundaries
462 call get_local_solver(local_solver_matrix,U,X,down,D,f,ele,&938 call get_local_solver(local_solver_matrix,U,X,down,D,f,ele,&
463 & g,dt,theta,D0,have_constraint,&939 & g,dt,theta,D0,pullback,weight,have_constraint,&
464 & local_solver_rhs=local_solver_rhs,projection=projection,&940 & projection=projection,poisson=poisson,&
465 & poisson=poisson)941 & local_solver_rhs=local_solver_rhs)
942 if(poisson.and.(ele==1)) then
943 !Fix the zero level of D
944 local_solver_matrix(d_start,:) = 0.
945 local_solver_matrix(:,d_start) = 0.
946 local_solver_matrix(d_start,d_start) = 1.
947 end if
466948
467 !Construct the rhs sources for U from lambda949 !Construct the rhs sources for U from lambda
468 call assemble_rhs_ele(Rhs_loc,D,U,X,ele,D_rhs,U_rhs,U_rhs_local)950 call assemble_rhs_ele(Rhs_loc,D,U,X,ele,pullback,&
951 weight,D_rhs,U_rhs,U_rhs_local)
952 if(poisson.and.(ele==1)) then
953 !Fix the zero level of D
954 rhs_loc(d_start)=0.0
955 end if
469 if(.not.(present(d_rhs).or.present(u_rhs)))then956 if(.not.(present(d_rhs).or.present(u_rhs)))then
470 assert(.not.present_and_true(projection))957 assert(.not.present_and_true(projection))
471 assert(.not.present_and_true(poisson))958 assert(.not.present_and_true(poisson))
@@ -486,7 +973,7 @@
486 do dim1 = 1, mdim973 do dim1 = 1, mdim
487 rhs_u_ptr(dim1)%ptr(face_local_nodes(U,face)) = &974 rhs_u_ptr(dim1)%ptr(face_local_nodes(U,face)) = &
488 & rhs_u_ptr(dim1)%ptr(face_local_nodes(U,face)) + &975 & rhs_u_ptr(dim1)%ptr(face_local_nodes(U,face)) + &
489 & matmul(continuity_face_mat(dim1,:,:),&976 & weight*matmul(continuity_face_mat(dim1,:,:),&
490 & face_val(lambda,face))977 & face_val(lambda,face))
491 end do978 end do
492 deallocate(continuity_face_mat)979 deallocate(continuity_face_mat)
@@ -500,9 +987,6 @@
500 ! (h) = (-C^T N) (j) + (-C^T N) (0)(l)987 ! (h) = (-C^T N) (j) + (-C^T N) (0)(l)
501988
502 call solve(local_solver_matrix,Rhs_loc)989 call solve(local_solver_matrix,Rhs_loc)
503 rhs_loc = matmul(local_solver_matrix,rhs_loc)
504 call solve(local_solver_matrix,Rhs_loc)
505
506 do dim1 = 1, mdim990 do dim1 = 1, mdim
507 U_solved(dim1,:) = rhs_loc(u_start(dim1):u_end(dim1))991 U_solved(dim1,:) = rhs_loc(u_start(dim1):u_end(dim1))
508 if(.not.(present_and_true(poisson))) then992 if(.not.(present_and_true(poisson))) then
@@ -513,7 +997,7 @@
513 end if997 end if
514 end if998 end if
515 end do999 end do
516 1000
517 D_solved = rhs_loc(d_start:d_end)1001 D_solved = rhs_loc(d_start:d_end)
518 if(.not.(present_and_true(projection))) then1002 if(.not.(present_and_true(projection))) then
519 if(present(D_out)) then1003 if(present(D_out)) then
@@ -524,26 +1008,29 @@
524 end if1008 end if
5251009
526 !check that the constraints are satisfied1010 !check that the constraints are satisfied
1011 !this is just a rescaling so don't need to use weights
527 if(have_constraint) then1012 if(have_constraint) then
528 constraints => U%mesh%shape%constraints1013 constraints => U%mesh%shape%constraints
529 do i1 = 1, constraints%n_constraints1014 do i1 = 1, constraints%n_constraints
530 constraint_check = 0.1015 constraint_check = 0.
1016 u_max = 0.
531 do dim1 = 1, mdim1017 do dim1 = 1, mdim
1018 u_max = max(u_max,maxval(abs(U_solved(dim1,:))))
532 constraint_check = constraint_check + &1019 constraint_check = constraint_check + &
533 & sum(U_solved(dim1,:)*constraints%orthogonal(i1,:,dim1))1020 & sum(U_solved(dim1,:)*constraints%orthogonal(i1,:,dim1))
534 end do1021 end do
535 if(abs(constraint_check)>1.0e-8) then1022 if(abs(constraint_check)/(max(1.0,u_max))>1.0e-8) then
536 ewrite(1,*) 'Constraint check', constraint_check1023 ewrite(2,*) 'Constraint check', constraint_check
537 FLAbort('Constraint not enforced')1024 FLAbort('Constraint not enforced')
538 end if1025 end if
539 end do 1026 end do
540 end if1027 end if
5411028
542 end subroutine reconstruct_U_d_ele1029 end subroutine reconstruct_U_d_ele
5431030
544 subroutine get_local_solver(local_solver_matrix,U,X,down,D,f,ele,&1031 subroutine get_local_solver(local_solver_matrix,U,X,down,D,f,ele,&
545 & g,dt,theta,D0,have_constraint, &1032 & g,dt,theta,D0,pullback,weight,have_constraint, &
546 & local_solver_rhs,projection,poisson)1033 & projection,poisson,local_solver_rhs)
547 !Subroutine to get the matrix and rhs for obtaining U and D within1034 !Subroutine to get the matrix and rhs for obtaining U and D within
548 !element ele from the lagrange multipliers on the boundaries.1035 !element ele from the lagrange multipliers on the boundaries.
549 !This matrix-vector system is referred to as the "local solver" in the 1036 !This matrix-vector system is referred to as the "local solver" in the
@@ -554,15 +1041,16 @@
554 implicit none1041 implicit none
555 !If projection is present and true, set dt to zero and just project U 1042 !If projection is present and true, set dt to zero and just project U
556 !into div-conforming space1043 !into div-conforming space
557 real, intent(in) :: g,dt,theta,D01044 real, intent(in) :: g,dt,theta,D0,weight
558 type(vector_field), intent(in) :: U,X,down1045 type(vector_field), intent(in) :: U,X,down
559 type(scalar_field), intent(in) :: D,f1046 type(scalar_field), intent(in) :: D,f
560 integer, intent(in) :: ele1047 integer, intent(in) :: ele
1048 logical, intent(in) :: pullback
561 real, dimension(:,:)&1049 real, dimension(:,:)&
562 &, intent(inout) :: local_solver_matrix1050 &, intent(inout) :: local_solver_matrix
563 real, dimension(:,:)&1051 real, dimension(:,:)&
564 &, intent(inout), optional :: local_solver_rhs1052 &, intent(inout), optional :: local_solver_rhs
565 logical, intent(in), optional :: projection, poisson1053 logical, intent(in) :: projection, poisson
566 logical, intent(in) :: have_constraint1054 logical, intent(in) :: have_constraint
567 !1055 !
568 real, dimension(mesh_dim(U), X%dim, ele_ngi(U,ele)) :: J1056 real, dimension(mesh_dim(U), X%dim, ele_ngi(U,ele)) :: J
@@ -583,12 +1071,20 @@
583 integer, dimension(mesh_dim(U)) :: U_start, U_end1071 integer, dimension(mesh_dim(U)) :: U_start, U_end
584 type(constraints_type), pointer :: constraints1072 type(constraints_type), pointer :: constraints
585 integer :: i1, c_start, c_end1073 integer :: i1, c_start, c_end
586 real :: l_dt1074 real :: l_dt,l_theta,l_d0,detJ_bar
5871075
588 if(present_and_true(projection)) then1076 if(projection) then
589 l_dt = 0.1077 l_dt = 0.
1078 l_theta = 0.
1079 l_d0 = 1.
1080 else if(poisson) then
1081 l_dt = 1.
1082 l_theta = 1.
1083 l_d0 = 1.
590 else1084 else
591 l_dt = dt1085 l_dt = dt
1086 l_theta = theta
1087 l_d0 = d0
592 end if1088 end if
5931089
594 mdim = mesh_dim(U)1090 mdim = mesh_dim(U)
@@ -606,13 +1102,13 @@
606 if(present(local_solver_rhs)) then1102 if(present(local_solver_rhs)) then
607 local_solver_rhs = 0.1103 local_solver_rhs = 0.
608 end if1104 end if
609 1105
610 u_shape=ele_shape(u, ele)1106 u_shape=ele_shape(u, ele)
611 D_shape=ele_shape(d, ele)1107 D_shape=ele_shape(d, ele)
612 D_ele => ele_nodes(D, ele)1108 D_ele => ele_nodes(D, ele)
613 U_ele => ele_nodes(U, ele)1109 U_ele => ele_nodes(U, ele)
614 1110
615 if(present_and_true(projection)) then1111 if(projection.or.poisson) then
616 f_gi = 0.1112 f_gi = 0.
617 else1113 else
618 f_gi = ele_val_at_quad(f,ele)1114 f_gi = ele_val_at_quad(f,ele)
@@ -625,6 +1121,7 @@
625 !detwei is needed for pressure mass matrix1121 !detwei is needed for pressure mass matrix
626 call compute_jacobian(ele_val(X,ele), ele_shape(X,ele), J=J, &1122 call compute_jacobian(ele_val(X,ele), ele_shape(X,ele), J=J, &
627 detwei=detwei, detJ=detJ)1123 detwei=detwei, detJ=detJ)
1124 detJ_bar = sum(detJ)/size(detJ)
6281125
629 !----construct local solver1126 !----construct local solver
630 !metrics for velocity mass and coriolis matrices1127 !metrics for velocity mass and coriolis matrices
@@ -643,51 +1140,71 @@
643 !dt*theta*<\phi,div u> + D_0<\phi,h> = 0 1140 !dt*theta*<\phi,div u> + D_0<\phi,h> = 0
6441141
645 !pressure mass matrix (done in global coordinates)1142 !pressure mass matrix (done in global coordinates)
646 local_solver_matrix(d_start:d_end,d_start:d_end)=&1143 !not included in pressure solver
647 &shape_shape(d_shape,d_shape,detwei)1144 if(.not.poisson) then
1145 if(pullback) then
1146 local_solver_matrix(d_start:d_end,d_start:d_end)=&
1147 &shape_shape(d_shape,d_shape,detJ_bar**2/detJ*&
1148 D_shape%quadrature%weight)
1149 else
1150 local_solver_matrix(d_start:d_end,d_start:d_end)=&
1151 &shape_shape(d_shape,d_shape,detwei)
1152 end if
648 if(present(local_solver_rhs)) then1153 if(present(local_solver_rhs)) then
649 local_solver_rhs(d_start:d_end,d_start:d_end) = &1154 if(pullback) then
650 shape_shape(d_shape,d_shape,detwei)1155 local_solver_rhs(d_start:d_end,d_start:d_end) = &
1156 shape_shape(d_shape,d_shape,detJ_bar**2/detJ*&
1157 D_shape%quadrature%weight)
1158 else
1159 local_solver_rhs(d_start:d_end,d_start:d_end) = &
1160 shape_shape(d_shape,d_shape,detwei)
1161 end if
651 end if1162 end if
1163 end if
652 !divergence matrix (done in local coordinates)1164 !divergence matrix (done in local coordinates)
653 l_div_mat = dshape_shape(u_shape%dn,d_shape,&1165 if(pullback) then
654 &D_shape%quadrature%weight)1166 l_div_mat = dshape_shape(u_shape%dn,d_shape,&
1167 &detJ_bar/detJ*D_shape%quadrature%weight)
1168 else
1169 l_div_mat = dshape_shape(u_shape%dn,d_shape,D_shape%quadrature%weight)
1170 end if
655 do dim1 = 1, mdim1171 do dim1 = 1, mdim
656 !pressure gradient term [integrated by parts so minus sign]1172 !pressure gradient term [integrated by parts so minus sign]
657 local_solver_matrix(u_start(dim1):u_end(dim1),d_start:d_end)=&1173 local_solver_matrix(u_start(dim1):u_end(dim1),d_start:d_end)=&
658 & -g*l_dt*theta*l_div_mat(dim1,:,:)1174 & -g*l_dt*l_theta*weight*l_div_mat(dim1,:,:)
659 if(present(local_solver_rhs)) then1175 if(present(local_solver_rhs)) then
660 local_solver_rhs(u_start(dim1):u_end(dim1),d_start:d_end)=&1176 local_solver_rhs(u_start(dim1):u_end(dim1),d_start:d_end)=&
661 & -g*(theta-1.0)*l_dt*l_div_mat(dim1,:,:)1177 & -g*(l_theta-1.0)*l_dt*weight*l_div_mat(dim1,:,:)
662 end if1178 end if
663 !divergence continuity term1179 !divergence continuity term
664 local_solver_matrix(d_start:d_end,u_start(dim1):u_end(dim1))=&1180 local_solver_matrix(d_start:d_end,u_start(dim1):u_end(dim1))=&
665 & d0*l_dt*theta*transpose(l_div_mat(dim1,:,:))1181 & l_d0*l_dt*l_theta*weight*transpose(l_div_mat(dim1,:,:))
666 if(present(local_solver_rhs)) then1182 if(present(local_solver_rhs)) then
667 local_solver_rhs(d_start:d_end,u_start(dim1):u_end(dim1))=&1183 local_solver_rhs(d_start:d_end,u_start(dim1):u_end(dim1))=&
668 & d0*(theta-1.0)*l_dt*transpose(l_div_mat(dim1,:,:))1184 & l_d0*(l_theta-1.0)*l_dt*weight*transpose(l_div_mat(dim1,:,:))
669 end if1185 end if
670 end do1186 end do
671 !velocity mass matrix and Coriolis matrix (done in local coordinates)1187 !velocity mass matrix and Coriolis matrix (done in local coordinates)
672 l_u_mat = shape_shape_tensor(u_shape, u_shape, &1188 l_u_mat = shape_shape_tensor(u_shape, u_shape, &
673 u_shape%quadrature%weight, Metric+l_dt*theta*Metricf)1189 u_shape%quadrature%weight, Metric+l_dt*l_theta*Metricf)
1190
674 do dim1 = 1, mdim1191 do dim1 = 1, mdim
675 do dim2 = 1, mdim1192 do dim2 = 1, mdim
676 local_solver_matrix(u_start(dim1):u_end(dim1),&1193 local_solver_matrix(u_start(dim1):u_end(dim1),&
677 u_start(dim2):u_end(dim2))=&1194 u_start(dim2):u_end(dim2))=&
678 & l_u_mat(dim1,dim2,:,:)1195 & weight**2*l_u_mat(dim1,dim2,:,:)
679 end do1196 end do
680 end do1197 end do
6811198
682 if(present(local_solver_rhs)) then1199 if(present(local_solver_rhs)) then
683 l_u_mat = shape_shape_tensor(u_shape, u_shape, &1200 l_u_mat = shape_shape_tensor(u_shape, u_shape, &
684 u_shape%quadrature%weight, &1201 u_shape%quadrature%weight, &
685 Metric+(theta-1.0)*l_dt*Metricf)1202 Metric+(l_theta-1.0)*l_dt*Metricf)
686 do dim1 = 1, mdim1203 do dim1 = 1, mdim
687 do dim2 = 1, mdim1204 do dim2 = 1, mdim
688 local_solver_rhs(u_start(dim1):u_end(dim1),&1205 local_solver_rhs(u_start(dim1):u_end(dim1),&
689 u_start(dim2):u_end(dim2))=&1206 u_start(dim2):u_end(dim2))=&
690 & l_u_mat(dim1,dim2,:,:)1207 & weight**2*l_u_mat(dim1,dim2,:,:)
691 end do1208 end do
692 end do1209 end do
693 end if1210 end if
@@ -708,52 +1225,6 @@
708 1225
709 end subroutine get_local_solver1226 end subroutine get_local_solver
7101227
711 subroutine get_up_gi(X,ele,up_gi,orientation)
712 !subroutine to replace up_gi with a normal to the surface
713 !with the same orientation
714 implicit none
715 type(vector_field), intent(in) :: X
716 integer, intent(in) :: ele
717 real, dimension(X%dim,ele_ngi(X,ele)), intent(inout) :: up_gi
718 integer, intent(out), optional :: orientation
719 !
720 real, dimension(mesh_dim(X), X%dim, ele_ngi(X,ele)) :: J
721 integer :: gi
722 real, dimension(X%dim,ele_ngi(X,ele)) :: normal_gi
723 real, dimension(ele_ngi(X,ele)) :: orientation_gi
724 real :: norm
725
726 call compute_jacobian(ele_val(X,ele), ele_shape(X,ele), J=J)
727
728 select case(mesh_dim(X))
729 case (2)
730 !Coriolis only makes sense for 2d surfaces embedded in 3d
731 do gi = 1, ele_ngi(X,ele)
732 normal_gi(:,gi) = cross_product(J(1,:,gi),J(2,:,gi))
733 norm = sqrt(sum(normal_gi(:,gi)**2))
734 normal_gi(:,gi) = normal_gi(:,gi)/norm
735 end do
736 do gi = 1, ele_ngi(X,ele)
737 orientation_gi(gi) = dot_product(normal_gi(:,gi),up_gi(:,gi))
738 end do
739 if(any(abs(orientation_gi-orientation_gi(1))>1.0e-8)) then
740 FLAbort('Nasty geometry problem')
741 end if
742 do gi = 1, ele_ngi(X,ele)
743 up_gi(:,gi) = normal_gi(:,gi)*orientation_gi(gi)
744 end do
745 if(present(orientation)) then
746 if(orientation_gi(1)>0.0) then
747 orientation = 1
748 else
749 orientation = -1
750 end if
751 end if
752 case default
753 FLAbort('not implemented')
754 end select
755 end subroutine get_up_gi
756
757 function get_orientation(X_val, up) result (orientation)1228 function get_orientation(X_val, up) result (orientation)
758 !function compares the orientation of the element with the 1229 !function compares the orientation of the element with the
759 !up direction1230 !up direction
@@ -853,73 +1324,6 @@
8531324
854 end subroutine get_scalar_continuity_face_mat1325 end subroutine get_scalar_continuity_face_mat
8551326
856 subroutine get_local_normal(norm,weight,U,face)
857 !Function returns normal to face on local 2D element
858 implicit none
859 type(vector_field), intent(in) :: U
860 integer, intent(in) :: face
861 real, dimension(U%dim, face_ngi(U,face)), intent(out) :: norm
862 real, intent(out) :: weight
863
864 integer :: i
865
866 select case(U%mesh%shape%numbering%family)
867 case (FAMILY_SIMPLEX)
868 if(U%dim==1) then
869 if(face==1) then
870 forall(i=1:face_ngi(U,face)) norm(1,i)=1.
871 else if(face==2) then
872 forall(i=1:face_ngi(U,face)) norm(1,i)=-1.
873 else
874 FLAbort('Funny face?')
875 end if
876 weight = 1.0
877
878 else if(U%dim==2) then
879 if(face==1) then
880 forall(i=1:face_ngi(U,face)) norm(1:2,i)=(/-1.,0./)
881 else if(face==2) then
882 forall(i=1:face_ngi(U,face)) norm(1:2,i)=(/0.,-1./)
883 else if(face==3) then
884 forall(i=1:face_ngi(U,face)) norm(1:2,i)=(/1/sqrt(2.),1&
885 &/sqrt(2.)/)
886 else
887 FLAbort('Funny face?')
888 end if
889
890 !Integral is taken on one of the edges of the local 2D element
891 !This edge must be transformed to the local 1D element
892 !to do numerical integration, with the following weight factors
893 if(face==3) then
894 weight = sqrt(2.)
895 else
896 weight = 1.0
897 end if
898
899 else
900 FLAbort('Dimension not supported.')
901 end if
902 case (FAMILY_CUBE)
903 if(U%dim==2) then
904 if(face==1) then
905 forall(i=1:face_ngi(U,face)) norm(1:2,i)=(/-1.,0./)
906 else if(face==2) then
907 forall(i=1:face_ngi(U,face)) norm(1:2,i)=(/ 1.,0./)
908 else if(face==3) then
909 forall(i=1:face_ngi(U,face)) norm(1:2,i)=(/0.,-1./)
910 else if(face==4) then
911 forall(i=1:face_ngi(U,face)) norm(1:2,i)=(/0.,1./)
912 else
913 FLAbort('Funny face?')
914 end if
915 weight = 1.0
916 else
917 FLAbort('Dimension not supported.')
918 end if
919 end select
920
921 end subroutine get_local_normal
922
923 subroutine compute_cartesian_ele(U_cart,U,X,ele)1327 subroutine compute_cartesian_ele(U_cart,U,X,ele)
924 implicit none1328 implicit none
925 type(vector_field), intent(inout) :: U_cart1329 type(vector_field), intent(inout) :: U_cart
@@ -935,20 +1339,20 @@
935 integer, dimension(mesh_dim(U)) :: U_start, U_end1339 integer, dimension(mesh_dim(U)) :: U_start, U_end
936 integer, dimension(:), pointer :: U_ele1340 integer, dimension(:), pointer :: U_ele
937 integer :: mdim, uloc, gi1341 integer :: mdim, uloc, gi
938 real, dimension(ele_ngi(U,ele)) :: detwei, detJ1342 real, dimension(ele_ngi(U,ele)) :: detwei, detJ
939 real, dimension(mesh_dim(U), X%dim, ele_ngi(U,ele)) :: J1343 real, dimension(mesh_dim(U), X%dim, ele_ngi(U,ele)) :: J
9401344
941 mdim = mesh_dim(U)1345 mdim = mesh_dim(U)
942 uloc = ele_loc(U,ele)1346 uloc = ele_loc(U,ele)
943 do dim1 = 1, mdim1347 do dim1 = 1, mdim
944 u_start(dim1) = uloc*(dim1-1)+11348 u_start(dim1) = uloc*(dim1-1)+1
945 u_end(dim1) = uloc+dim11349 u_end(dim1) = uloc+dim1
946 end do1350 end do
9471351
948 U_ele => ele_nodes(U, ele)1352 U_ele => ele_nodes(U, ele)
9491353
950 u_shape=ele_shape(u, ele)1354 u_shape=ele_shape(u, ele)
951 call compute_jacobian(ele_val(X,ele), ele_shape(X,ele), J=J, &1355 call compute_jacobian(ele_val(X,ele),ele_shape(X,ele), J=J, &
952 detwei=detwei,detJ=detJ)1356 detwei=detwei,detJ=detJ)
9531357
954 local_u_gi = ele_val_at_quad(U,ele)1358 local_u_gi = ele_val_at_quad(U,ele)
@@ -1002,15 +1406,16 @@
1002 u1 = face_val_at_quad(U,face)1406 u1 = face_val_at_quad(U,face)
1003 u2 = face_val_at_quad(U,face2)1407 u2 = face_val_at_quad(U,face2)
1004 jump = maxval(abs(sum(u1*n1+u2*n2,1)))1408 jump = maxval(abs(sum(u1*n1+u2*n2,1)))
1005 ewrite(1,*) jump1409 ewrite(2,*) jump
1006 assert(jump<1.0e-8)1410 assert(jump<1.0e-8)
10071411
1008 end subroutine check_continuity_local_face1412 end subroutine check_continuity_local_face
10091413
1010 subroutine check_continuity_ele(U_cart,X,ele)1414 subroutine check_continuity_ele(U_cart,X,ele,tolerance)
1011 implicit none1415 implicit none
1012 type(vector_field), intent(in) :: U_cart,X1416 type(vector_field), intent(in) :: U_cart,X
1013 integer, intent(in) :: ele1417 integer, intent(in) :: ele
1418 real, intent(in) :: tolerance
1014 !1419 !
1015 integer, dimension(:), pointer :: neigh1420 integer, dimension(:), pointer :: neigh
1016 integer :: ni,face,ele2,face21421 integer :: ni,face,ele2,face2
@@ -1024,18 +1429,20 @@
1024 else1429 else
1025 face2 = -11430 face2 = -1
1026 end if1431 end if
1027 call check_continuity_face(U_cart,X,ele,ele2,face,face2)1432 call check_continuity_face(U_cart,X,ele,ele2,face,face2,tolerance)
1028 end do1433 end do
1029 end subroutine check_continuity_ele1434 end subroutine check_continuity_ele
10301435
1031 subroutine check_continuity_face(U_cart,X,ele,ele2,face,face2)1436 subroutine check_continuity_face(U_cart,X,ele,ele2,face,face2,tolerance)
1032 !subroutine to check the continuity of normal component1437 !subroutine to check the continuity of normal component
1033 !of velocity at quadrature points1438 !of velocity at quadrature points
1034 implicit none1439 implicit none
1035 type(vector_field), intent(in) :: U_cart,X1440 type(vector_field), intent(in) :: U_cart,X
1036 integer, intent(in) :: face,face2,ele,ele21441 integer, intent(in) :: face,face2,ele,ele2
1442 real, intent(in) :: tolerance
1443 !
1037 real, dimension(X%dim, face_ngi(U_cart, face)) :: n1,n21444 real, dimension(X%dim, face_ngi(U_cart, face)) :: n1,n2
1038 real, dimension(X%dim, face_ngi(U_cart, face)) :: u1,u2,x11445 real, dimension(X%dim, face_ngi(U_cart, face)) :: u1,u2,x1,x2
1039 real, dimension(face_ngi(U_cart, face)) :: jump_at_quad1446 real, dimension(face_ngi(U_cart, face)) :: jump_at_quad
1040 integer :: dim11447 integer :: dim1
1041 !1448 !
@@ -1043,6 +1450,13 @@
1043 x1 = face_val_at_quad(X,face)1450 x1 = face_val_at_quad(X,face)
1044 if(ele2>0) then1451 if(ele2>0) then
1045 u2 = face_val_at_quad(U_cart,face2)1452 u2 = face_val_at_quad(U_cart,face2)
1453 x2 = face_val_at_quad(X,face)
1454 if(any(x1.ne.x2)) then
1455 ewrite(0,*) 'Face 1 X', x1
1456 ewrite(0,*) 'Face 2 X', x2
1457 FLExit('Something wrong with mesh?')
1458 end if
1459
1046 else1460 else
1047 u2 = 0.1461 u2 = 0.
1048 end if1462 end if
@@ -1054,98 +1468,22 @@
1054 n2 = -n11468 n2 = -n1
1055 end if1469 end if
1056 jump_at_quad = sum(n1*u1+n2*u2,1)1470 jump_at_quad = sum(n1*u1+n2*u2,1)
1057 if(maxval(abs(jump_at_quad))>1.0e-8) then1471 if(maxval(abs(jump_at_quad))>tolerance) then
1058 ewrite(1,*) 'Jump at quadrature face, face2 =', jump_at_quad1472 ewrite(2,*) 'Jump at quadrature face, face2 =', jump_at_quad
1059 ewrite(1,*) 'ELE = ',ele,ele21473 ewrite(2,*) 'ELE = ',ele,ele2
1060 do dim1 = 1, X%dim1474 do dim1 = 1, X%dim
1061 ewrite(1,*) 'normal',dim1,n1(dim1,:)1475 ewrite(2,*) 'normal',dim1,n1(dim1,:)
1062 ewrite(1,*) 'normal',dim1,n2(dim1,:)1476 ewrite(2,*) 'normal',dim1,n2(dim1,:)
1063 ewrite(1,*) 'X',dim1,x1(dim1,:)1477 ewrite(2,*) 'X',dim1,x1(dim1,:)
1064 end do1478 end do
1065 ewrite(1,*) 'n cpt1',sum(n1*u1,1)1479 ewrite(2,*) 'n cpt1',sum(n1*u1,1)
1066 ewrite(1,*) 'n cpt2',sum(n2*u2,1)1480 ewrite(2,*) 'n cpt2',sum(n2*u2,1)
1067 ewrite(1,*) jump_at_quad/max(maxval(abs(u1)),maxval(abs(u2)))1481 ewrite(2,*) jump_at_quad/max(maxval(abs(u1)),maxval(abs(u2)))
1068 FLAbort('stopping because of jumps')1482 FLAbort('stopping because of jumps')
1069 end if1483 end if
1070 1484
1071 end subroutine check_continuity_face1485 end subroutine check_continuity_face
10721486
1073 function get_face_normal_manifold(X,ele,face) result (normal)
1074 implicit none
1075 type(vector_field), intent(in) :: X
1076 integer, intent(in) :: ele, face
1077 real, dimension(X%dim,face_ngi(X,face)) :: normal
1078 !
1079 real, dimension(ele_ngi(X,ele)) :: detwei
1080 real, dimension(mesh_dim(X), X%dim, ele_ngi(X,ele)) :: J
1081 real, dimension(face_ngi(X,face)) :: detwei_f
1082 real, dimension(mesh_dim(X)-1, X%dim, face_ngi(X,face)) :: J_f
1083 real, dimension(ele_loc(X,ele),ele_loc(X,ele)) :: X_mass_mat
1084 real, dimension(mesh_dim(X), X%dim, ele_loc(X,ele)) :: J_loc
1085 real, dimension(ele_loc(X,ele)) :: J_loc_rhs
1086 real, dimension(mesh_dim(X), X%dim, face_ngi(X,face)) :: J_face_gi
1087 integer :: dim1, dim2, gi
1088 real, dimension(X%dim) :: ele_normal_gi, edge_tangent_gi, X_mid_ele,&
1089 &X_mid_face
1090 type(element_type) :: X_shape, X_face_shape
1091 real, dimension(X%dim,ele_loc(X,ele)) :: X_ele
1092 real, dimension(X%dim,face_loc(X,face)) :: X_face
1093
1094 call compute_jacobian(ele_val(X,ele),ele_shape(X,ele), J=J, &
1095 detwei=detwei)
1096 call compute_jacobian(face_val(X,face),face_Shape(X,face), J=J_f, &
1097 detwei=detwei_f)
1098
1099 !Jacobian can be expanded without error in X function space
1100 !so we map it to the basis function DOFs by projection
1101 X_shape = ele_shape(X,ele)
1102 X_face_shape = face_shape(X,face)
1103 X_mass_mat = shape_shape(X_shape,X_shape,detwei)
1104 do dim1 = 1, mesh_dim(X)
1105 do dim2 = 1, X%dim
1106 J_loc_rhs = shape_rhs(X_shape,J(dim1,dim2,:)*detwei)
1107 call solve(X_mass_mat,J_loc_rhs)
1108 J_loc(dim1,dim2,:) = J_loc_rhs
1109 end do
1110 end do
1111
1112 do dim1 = 1, mesh_dim(X)
1113 do dim2 = 1, X%dim
1114 J_face_gi(dim1,dim2,:) = &
1115 & matmul(transpose(X_face_shape%n),&
1116 J_loc(dim1,dim2,face_local_nodes(X,face)))
1117 end do
1118 end do
1119
1120 X_mid_ele = sum(ele_val(X,ele),2)/size(ele_val(X,ele),2)
1121 X_mid_face = sum(face_val(X,face),2)/size(face_val(X,face),2)
1122 select case(X%dim)
1123 case (3)
1124 select case (mesh_dim(X))
1125 case (2)
1126 do gi = 1, face_ngi(X,face)
1127 !Get normal to element e on face quad points
1128 ele_normal_gi = cross_product(J_face_gi(1,:,gi),&
1129 &J_face_gi(2,:,gi))
1130 ele_normal_gi = ele_normal_gi/(norm2(ele_normal_gi))
1131 !Get tangent to face f
1132 edge_tangent_gi = J_f(1,:,gi)
1133 edge_tangent_gi = edge_tangent_gi/norm2(edge_tangent_gi)
1134 !Compute normal to face f in manifold
1135 normal(:,gi) = cross_product(ele_normal_gi,edge_tangent_gi)
1136 if(dot_product(normal(:,gi),X_mid_face-X_mid_ele)<0) then
1137 normal(:,gi)=-normal(:,gi)
1138 end if
1139 end do
1140 case default
1141 FLAbort('dimension combination not implemented')
1142 end select
1143 case default
1144 FLAbort('dimension combination not implemented')
1145 end select
1146
1147 end function get_face_normal_manifold
1148
1149 subroutine reconstruct_lambda_nc(lambda,lambda_nc,X,ele)1487 subroutine reconstruct_lambda_nc(lambda,lambda_nc,X,ele)
1150 type(scalar_field), intent(in) :: lambda1488 type(scalar_field), intent(in) :: lambda
1151 type(scalar_field), intent(inout) :: lambda_nc1489 type(scalar_field), intent(inout) :: lambda_nc
@@ -1203,9 +1541,12 @@
1203 &shape_shape(lambda_nc_face_shape,lambda_nc_face_shape,detwei)1541 &shape_shape(lambda_nc_face_shape,lambda_nc_face_shape,detwei)
1204 end subroutine get_nc_rhs_face1542 end subroutine get_nc_rhs_face
12051543
1206 subroutine assemble_rhs_ele(Rhs_loc,D,U,X,ele,D_rhs,U_rhs,u_rhs_local)1544 subroutine assemble_rhs_ele(Rhs_loc,D,U,X,ele,pullback,&
1545 weight,D_rhs,U_rhs,u_rhs_local)
1207 implicit none1546 implicit none
1208 integer, intent(in) :: ele1547 integer, intent(in) :: ele
1548 real, intent(in) :: weight
1549 logical, intent(in) :: pullback
1209 type(scalar_field), intent(in), optional, target :: D_rhs1550 type(scalar_field), intent(in), optional, target :: D_rhs
1210 type(vector_field), intent(in), optional, target :: U_rhs1551 type(vector_field), intent(in), optional, target :: U_rhs
1211 type(vector_field), intent(in) :: X,U1552 type(vector_field), intent(in) :: X,U
@@ -1226,6 +1567,7 @@
1226 type(scalar_field), pointer :: l_d_rhs1567 type(scalar_field), pointer :: l_d_rhs
1227 type(vector_field), pointer :: l_u_rhs1568 type(vector_field), pointer :: l_u_rhs
1228 real, dimension(mesh_dim(U), mesh_dim(U)) :: Metric1569 real, dimension(mesh_dim(U), mesh_dim(U)) :: Metric
1570 real :: detJ_bar
12291571
1230 !Get some sizes1572 !Get some sizes
1231 mdim = mesh_dim(U)1573 mdim = mesh_dim(U)
@@ -1245,6 +1587,7 @@
1245 1587
1246 if(.not.(present(D_rhs).or.present(u_rhs))) then1588 if(.not.(present(D_rhs).or.present(u_rhs))) then
1247 !We are in timestepping mode.1589 !We are in timestepping mode.
1590 !This will be multiplied by the local_solver_rhs matrix later.
1248 rhs_loc(d_start:d_end) = ele_val(D,ele)1591 rhs_loc(d_start:d_end) = ele_val(D,ele)
1249 u_rhs_loc = ele_val(U,ele)1592 u_rhs_loc = ele_val(U,ele)
1250 do dim1 = 1, mdim1593 do dim1 = 1, mdim
@@ -1259,27 +1602,38 @@
1259 1602
1260 call compute_jacobian(ele_val(X,ele), ele_shape(X,ele), J=J, &1603 call compute_jacobian(ele_val(X,ele), ele_shape(X,ele), J=J, &
1261 detJ=detJ,detwei=detwei)1604 detJ=detJ,detwei=detwei)
1605 detJ_bar = sum(detJ)/size(detJ)
1262 1606
1263 Rhs_loc = 0.1607 Rhs_loc = 0.
1264 if(have_d_rhs) then1608 if(have_d_rhs) then
1265 Rhs_loc(d_start:d_end) = shape_rhs(ele_shape(D,ele),&1609 if(pullback) then
1266 &ele_val_at_quad(l_D_rhs,ele)*detwei)1610 Rhs_loc(d_start:d_end) = shape_rhs(ele_shape(D,ele),&
1611 &ele_val_at_quad(l_D_rhs,ele)*&
1612 &detJ_bar**2/detJ*u_shape%quadrature%weight)
1613 else
1614 Rhs_loc(d_start:d_end) = shape_rhs(ele_shape(D,ele),&
1615 &ele_val_at_quad(l_D_rhs,ele)*detwei)
1616 end if
1267 end if1617 end if
1268 if(have_u_rhs) then1618 if(have_u_rhs) then
1269 if(present_and_true(u_rhs_local)) then1619 if(present_and_true(u_rhs_local)) then
1620 !Using local U, multiply by weight**2
1270 u_local_quad = ele_val_at_quad(l_u_rhs,ele)1621 u_local_quad = ele_val_at_quad(l_u_rhs,ele)
1271 do gi=1,ele_ngi(U,ele)1622 do gi=1,ele_ngi(U,ele)
1272 Metric=matmul(J(:,:,gi), transpose(J(:,:,gi)))&1623 Metric=matmul(J(:,:,gi), transpose(J(:,:,gi)))&
1273 &/detJ(gi)1624 &/detJ(gi)
1274 u_local_quad(:,gi) = matmul(Metric,u_local_quad(:,gi))1625 u_local_quad(:,gi) = matmul(Metric,u_local_quad(:,gi))&
1626 &*weight**2
1275 end do1627 end do
1276 else1628 else
1629 !Using cartesian U, multiply by weight
1277 allocate(u_cart_quad(l_U_rhs%dim,ele_ngi(X,ele)))1630 allocate(u_cart_quad(l_U_rhs%dim,ele_ngi(X,ele)))
1278 u_cart_quad = ele_val_at_quad(l_u_rhs,ele)1631 u_cart_quad = ele_val_at_quad(l_u_rhs,ele)
1279 do gi = 1, ele_ngi(D,ele)1632 do gi = 1, ele_ngi(D,ele)
1280 !Don't divide by detJ as we can use weight instead of detwei1633 !Don't divide by detJ as we can use quadrature weight
1634 !instead of detwei
1281 u_local_quad(:,gi) = matmul(J(:,:,gi)&1635 u_local_quad(:,gi) = matmul(J(:,:,gi)&
1282 &,u_cart_quad(:,gi))1636 &,u_cart_quad(:,gi))*weight
1283 end do1637 end do
1284 end if1638 end if
1285 U_rhs_loc = shape_vector_rhs(u_shape,&1639 U_rhs_loc = shape_vector_rhs(u_shape,&
@@ -1361,8 +1715,8 @@
1361 call compute_energy_ele(energy,U,D,X,D0,g,ele)1715 call compute_energy_ele(energy,U,D,X,D0,g,ele)
1362 end do1716 end do
13631717
1364 ewrite(1,*) 'Energy:= ', energy1718 ewrite(2,*) 'Energy:= ', energy
1365 ewrite(1,*) 'Change in energy:= ', energy-old_energy1719 ewrite(2,*) 'Percentage Change in energy:= ', (energy-old_energy)/energy
13661720
1367 end subroutine compute_energy_hybridized1721 end subroutine compute_energy_hybridized
13681722
@@ -1414,12 +1768,14 @@
1414 type(state_type), intent(inout) :: state1768 type(state_type), intent(inout) :: state
1415 !1769 !
1416 type(scalar_field), pointer :: D,psi,f1770 type(scalar_field), pointer :: D,psi,f
1417 type(scalar_field) :: D_rhs1771 type(scalar_field) :: D_rhs,tmp_field
1418 type(vector_field), pointer :: U_local,down,X, U_cart1772 type(vector_field), pointer :: U_local,down,X, U_cart
1419 type(vector_field) :: Coriolis_term, Balance_eqn, tmp_field1773 type(vector_field) :: Coriolis_term, Balance_eqn, tmpV_field
1420 integer :: ele,dim11774 integer :: ele,dim1,i1, stat
1421 real :: g1775 real :: g, D0
1422 logical :: elliptic_method1776 logical :: elliptic_method, pullback
1777 real :: u_max, b_val, h_mean, area
1778 real, dimension(:), allocatable :: weights
14231779
1424 D=>extract_scalar_field(state, "LayerThickness")1780 D=>extract_scalar_field(state, "LayerThickness")
1425 psi=>extract_scalar_field(state, "Streamfunction")1781 psi=>extract_scalar_field(state, "Streamfunction")
@@ -1429,133 +1785,135 @@
1429 X=>extract_vector_field(state, "Coordinate")1785 X=>extract_vector_field(state, "Coordinate")
1430 down=>extract_vector_field(state, "GravityDirection")1786 down=>extract_vector_field(state, "GravityDirection")
1431 call get_option("/physical_parameters/gravity/magnitude", g)1787 call get_option("/physical_parameters/gravity/magnitude", g)
1432 call allocate(tmp_field,mesh_dim(U_local), U_local%mesh, "tmp_field")1788 call allocate(tmpV_field,mesh_dim(U_local), U_local%mesh, "tmpV_field")
1433 call allocate(D_rhs,D%mesh,'BalancedSolverRHS')1789 call allocate(D_rhs,D%mesh,'BalancedSolverRHS')
1790 call allocate(tmp_field,D%mesh,'TmpField')
1434 call allocate(Coriolis_term,mesh_dim(U_local),&1791 call allocate(Coriolis_term,mesh_dim(U_local),&
1435 U_local%mesh,"CoriolisTerm")1792 U_local%mesh,"CoriolisTerm")
1436 call allocate(balance_eqn,mesh_dim(D),u_local%mesh,'BalancedEquation')1793 call allocate(balance_eqn,mesh_dim(D),u_local%mesh,'BalancedEquation')
14371794
1795 !compute rescaling weights for local coordinates
1796 allocate(weights(element_count(X)))
1797 weights = 1.0
1798 !call get_weights(X,weights)
1799
1438 !STAGE 1: Set velocity from streamfunction1800 !STAGE 1: Set velocity from streamfunction
1439 do ele = 1, element_count(D)1801 do ele = 1, element_count(D)
1440 call set_local_velocity_from_streamfunction_ele(&1802 call set_local_velocity_from_streamfunction_ele(&
1441 &U_local,psi,down,X,ele)1803 &U_local,psi,down,X,ele,weights(ele))
1442 end do1804 end do
14431805
1444 !STAGE 1a: verify that velocity projects is div-conforming1806 !STAGE 1a: verify that velocity projects is div-conforming
1445 call project_local_to_cartesian(X,U_local,U_cart)1807 !call project_local_to_cartesian(X,U_local,U_cart,weights=weights)
1446 do ele = 1, ele_count(U_local)1808 !do ele = 1, ele_count(U_local)
1447 call check_continuity_ele(U_cart,X,ele)1809 ! call check_continuity_ele(U_cart,X,ele,tolerance=1.0e-8)
1448 end do1810 !end do
1449
1450 !Stage 1b: verify that projection is idempotent1811 !Stage 1b: verify that projection is idempotent
1451 ewrite(1,*) 'CHECKING CONTINUOUS', maxval(abs(u_local%val))1812 ewrite(2,*) 'CHECKING CONTINUOUS', maxval(abs(u_local%val))
1452 call solve_hybridized_helmholtz(state,U_Rhs=U_local,&1813 tmpV_field%val = U_local%val
1453 &U_out=tmp_field,&1814 call project_to_constrained_space(state,tmpV_field)
1454 &compute_cartesian=.true.,&1815 ewrite(2,*) maxval(abs(U_local%val-tmpV_field%val)), 'continuity'
1455 &check_continuity=.true.,projection=.true.,&1816 u_max = 0.
1456 &u_rhs_local=.true.)!verified that projection is idempotent1817 do dim1 = 1, mesh_dim(D)
1457 assert(maxval(abs(U_local%val-tmp_field%val))<1.0e-8)1818 u_max = max(u_max,maxval(abs(U_local%val)))
14581819 end do
1459 elliptic_method = .false.1820 assert(maxval(abs(U_local%val-tmpV_field%val)/max(1.0,u_max))<1.0e-8)
14601821
1461 if(elliptic_method) then1822 if(have_option("/material_phase::Fluid/vector_field::Velocity/prognostic&
14621823 &/initial_condition::WholeMesh/balanced/elliptic_solver")) then
1463 !STAGE 2: Construct Coriolis term1824 ewrite(2,*) 'Using elliptic solver'
1825 !Construct Coriolis term
1464 call zero(Coriolis_term)1826 call zero(Coriolis_term)
1465 do ele = 1, element_count(D)1827 do ele = 1, element_count(D)
1828 !weights not needed since both sides of equation are multiplied
1829 !by weight(ele)^2 in each element
1466 call set_coriolis_term_ele(Coriolis_term,f,down,U_local,X,ele)1830 call set_coriolis_term_ele(Coriolis_term,f,down,U_local,X,ele)
1467 end do!checked!signs checked1831 end do
14681832
1469 !STAGE 3: Project Coriolis term into div-conforming space1833 !Project Coriolis term into div-conforming space
14701834 call project_to_constrained_space(state,Coriolis_term)
1471 !debugging bits - checking if it works with cartesian instead1835
1472 call project_local_to_cartesian(X,Coriolis_Term,U_cart)1836 !Calculate divergence of Coriolis term
1473 call solve_hybridized_helmholtz(state,U_Rhs=U_cart,&1837 call zero(d_rhs)
1474 &U_out=tmp_field,&1838
1475 &compute_cartesian=.true.,output_dense=.false.,&1839 pullback = have_option('/material_phase::Fluid/scalar_field::LayerThickn&
1476 &check_continuity=.true.,projection=.true.,&1840 &ess/prognostic/spatial_discretisation/discontinuous_galerkin/wave_&
1477 &u_rhs_local=.false.)1841 &equation/pullback')
14781842
1479 ewrite(0,*) 'REMEMBER TO REMOVE DEBUGGING TESTS'1843 do ele = 1, element_count(D)
1480 call solve_hybridized_helmholtz(state,U_Rhs=Coriolis_term,&1844 call compute_divergence_ele(Coriolis_term,d_rhs,X,ele,pullback)
1481 &U_out=tmp_field,&1845 end do
1482 &compute_cartesian=.true.,&1846
1483 &check_continuity=.true.,projection=.true.,&1847 !Solve for balanced layer depth
1484 &u_rhs_local=.true.)!verified that projection is idempotent1848
14851849 ewrite(2,*) 'Solving elliptic problem for balanced layer depth.'
1486 !STAGE 4: Construct the RHS for the balanced layer depth equation1850 call solve_hybridized_helmholtz(state,d_Rhs=d_rhs,&
1487 call zero(D_rhs)1851 &U_out=tmpV_field,d_out=tmp_field,&
1488 do ele = 1, element_count(D)1852 &compute_cartesian=.true.,&
1489 call set_geostrophic_balance_rhs_ele(D_rhs,Coriolis_term,ele)1853 &poisson=.true.,u_rhs_local=.true.)
1490 end do1854 D%val = tmp_field%val
1491
1492 !STAGE 5: Solve Poisson equation for the balanced layer depth
1493 ewrite(0,*) 'REMEMBER ABOUT SETTING MEAN VALUE'
1494 ewrite(0,*) trim(u_cart%option_path)
1495
1496 call solve_hybridized_helmholtz(state,D_rhs=D_rhs,&
1497 &compute_cartesian=.false.,&
1498 &check_continuity=.false.,Poisson=.true.,&
1499 &solver_option_path=trim(u_cart%option_path)//'/prognostic/initial_condition::WholeMesh/balanced')
1500
1501 !STAGE 6: Check if we have a balanced solution
1502 !Can be done by projecting balance equation into div-conforming space
1503 !and checking that it is equal to zero
1504 !STAGE 6a: Project balance equation into DG space
1505 do ele = 1, element_count(D)
1506 call set_pressure_force_ele(balance_eqn,D,X,g,ele)
1507 end do
1508 call addto(balance_eqn,coriolis_term)
1509
1510 !STAGE 6b: Project balance equation into div-conforming space
1511 call solve_hybridized_helmholtz(state,U_Rhs=balance_eqn,&
1512 &U_out=balance_eqn,&
1513 &compute_cartesian=.true.,&
1514 &check_continuity=.true.,projection=.true.,&
1515 &u_rhs_local=.true.)
1516
1517 do dim1 = 1, mesh_dim(D)
1518 ewrite(1,*) 'Balance equation', maxval(abs(balance_eqn%val(dim1,:)))
1519 assert(maxval(abs(balance_eqn%val(dim1,:)))<1.0e-8)
1520 end do
1521
1522 else1855 else
1856 ewrite(2,*) 'Using streamfunction projection'
1523 !Project the streamfunction into pressure space1857 !Project the streamfunction into pressure space
1524 do ele = 1, element_count(D)1858 do ele = 1, element_count(D)
1525 call project_streamfunction_for_balance_ele(D,psi,X,f,g,ele)1859 call project_streamfunction_for_balance_ele(D,psi,X,f,g,ele)
1526 end do1860 end do
1527 ewrite(1,*) maxval(abs(D%val))
1528
1529 !debugging tests
1530 call zero(Coriolis_term)
1531 do ele = 1, element_count(D)
1532 call set_coriolis_term_ele(Coriolis_term,f,down,U_local,X,ele)
1533 end do
1534 call zero(balance_eqn)
1535 do ele = 1, element_count(D)
1536 call set_pressure_force_ele(balance_eqn,D,X,g,ele)
1537 end do
1538 call addto(balance_eqn,coriolis_term,scale=1.0)
1539 ewrite(1,*) 'CJC b4',maxval(abs(balance_eqn%val)),&
1540 & maxval(abs(coriolis_term%val))
1541 !Project balance equation into div-conforming space
1542 call solve_hybridized_helmholtz(state,U_Rhs=balance_eqn,&
1543 &U_out=balance_eqn,&
1544 &compute_cartesian=.true.,&
1545 &check_continuity=.true.,projection=.true.,&
1546 &u_rhs_local=.true.)
1547
1548 do dim1 = 1, mesh_dim(D)
1549 ewrite(1,*) 'Balance equation', maxval(abs(balance_eqn%val(dim1,:)))
1550 assert(maxval(abs(balance_eqn%val(dim1,:)))<1.0e-8)
1551 end do
1552 end if1861 end if
1553 !Clean up after yourself1862
1863 !Subtract off the mean part
1864 !Didn't bother with pullback
1865 h_mean = 0.
1866 area = 0.
1867 do ele = 1, element_count(D)
1868 call assemble_mean_ele(D,X,h_mean,area,ele)
1869 end do
1870 h_mean = h_mean/area
1871 D%val = D%val - h_mean
1872 !Add back on the correct mean depth
1873 call get_option("/material_phase::Fluid/scalar_field::LayerThickness/&
1874 &prognostic/mean_layer_thickness",D0)
1875 D%val = D%val + D0
1876
1877 !debugging tests
1878 call zero(Coriolis_term)
1879 do ele = 1, element_count(D)
1880 !weights not needed since both sides of equation are multiplied
1881 !by weight(ele)^2 in each element
1882 call set_coriolis_term_ele(Coriolis_term,f,down,U_local,X,ele)
1883 end do
1884
1885 call zero(balance_eqn)
1886 do ele = 1, element_count(D)
1887 call set_pressure_force_ele(balance_eqn,D,X,g,ele,weights(ele),&
1888 pullback)
1889 end do
1890 call addto(balance_eqn,coriolis_term,scale=1.0)
1891 ewrite(2,*) 'Project balance equation into div-conforming space'
1892 ewrite(2,*) 'CJC b4',maxval(abs(balance_eqn%val)),&
1893 & maxval(abs(coriolis_term%val))
1894 b_val = maxval(abs(balance_eqn%val))
1895 !Project balance equation into div-conforming space
1896 call solve_hybridized_helmholtz(state,U_Rhs=balance_eqn,&
1897 &U_out=balance_eqn,&
1898 &compute_cartesian=.true.,&
1899 &projection=.true.,&
1900 &poisson=.false.,&
1901 &u_rhs_local=.true.)
1902
1903 do dim1 = 1, mesh_dim(D)
1904 ewrite(2,*) 'Balance equation', maxval(abs(balance_eqn%val(dim1,:)))/b_val
1905 !assert(maxval(abs(balance_eqn%val(dim1,:)))/b_val<1.0e-8)
1906 end do
1907
1908 !Clean up after yourself
1554 call deallocate(Coriolis_term)1909 call deallocate(Coriolis_term)
1555 call deallocate(D_rhs)1910 call deallocate(D_rhs)
1556 call deallocate(balance_eqn)1911 call deallocate(balance_eqn)
1912 call deallocate(tmpV_field)
1557 call deallocate(tmp_field)1913 call deallocate(tmp_field)
15581914
1915 deallocate(weights)
1916
1559 end subroutine set_velocity_from_geostrophic_balance_hybridized1917 end subroutine set_velocity_from_geostrophic_balance_hybridized
15601918
1561 subroutine project_streamfunction_for_balance_ele(D,psi,X,f,g,ele)1919 subroutine project_streamfunction_for_balance_ele(D,psi,X,f,g,ele)
@@ -1587,19 +1945,18 @@
15871945
1588 end subroutine project_streamfunction_for_balance_ele1946 end subroutine project_streamfunction_for_balance_ele
1589 1947
1590 subroutine set_pressure_force_ele(force,D,X,g,ele)1948 subroutine set_pressure_force_ele(force,D,X,g,ele,weight,pullback)
1591 implicit none1949 implicit none
1592 type(vector_field), intent(inout) :: force1950 type(vector_field), intent(inout) :: force
1593 type(scalar_field), intent(in) :: D1951 type(scalar_field), intent(in) :: D
1594 type(vector_field), intent(in) :: X1952 type(vector_field), intent(in) :: X
1595 real, intent(in) :: g1953 real, intent(in) :: g, weight
1596 integer, intent(in) :: ele1954 integer, intent(in) :: ele
1955 logical, intent(in) :: pullback
1597 !1956 !
1598 real, dimension(ele_ngi(D,ele)) :: D_gi1957 real, dimension(ele_ngi(D,ele)) :: D_gi
1599 real, dimension(mesh_dim(D),ele_loc(force,ele)) :: &1958 real, dimension(mesh_dim(D),ele_loc(force,ele)) :: &
1600 & rhs_loc1959 & rhs_loc
1601 real, dimension(ele_loc(force,ele),ele_loc(force,ele)) :: &
1602 & l_mass_mat
1603 integer :: dim1,dim2,gi,uloc1960 integer :: dim1,dim2,gi,uloc
1604 real, dimension(mesh_dim(force), X%dim, ele_ngi(force,ele)) :: J1961 real, dimension(mesh_dim(force), X%dim, ele_ngi(force,ele)) :: J
1605 real, dimension(ele_ngi(force,ele)) :: detJ1962 real, dimension(ele_ngi(force,ele)) :: detJ
@@ -1609,6 +1966,7 @@
1609 mesh_dim(force)*ele_loc(force,ele)) :: l_u_mat1966 mesh_dim(force)*ele_loc(force,ele)) :: l_u_mat
1610 real, dimension(mesh_dim(force)*ele_loc(force,ele)) :: force_rhs1967 real, dimension(mesh_dim(force)*ele_loc(force,ele)) :: force_rhs
1611 type(element_type) :: force_shape1968 type(element_type) :: force_shape
1969 real :: detJ_bar
16121970
1613 uloc = ele_loc(force,ele)1971 uloc = ele_loc(force,ele)
1614 force_shape = ele_shape(force,ele)1972 force_shape = ele_shape(force,ele)
@@ -1618,11 +1976,14 @@
1618 Metric(:,:,gi)=matmul(J(:,:,gi), transpose(J(:,:,gi)))/detJ(gi)1976 Metric(:,:,gi)=matmul(J(:,:,gi), transpose(J(:,:,gi)))/detJ(gi)
1619 end do1977 end do
16201978
1979 detJ_bar = sum(detJ)/size(detJ)
1980
1621 D_gi = ele_val_at_quad(D,ele)1981 D_gi = ele_val_at_quad(D,ele)
1982 if(pullback) then
1983 D_gi = D_gi*detJ_bar/detJ
1984 end if
1622 rhs_loc = -g*dshape_rhs(force%mesh%shape%dn,&1985 rhs_loc = -g*dshape_rhs(force%mesh%shape%dn,&
1623 D_gi*D%mesh%shape%quadrature%weight)1986 D_gi*D%mesh%shape%quadrature%weight)
1624 l_mass_mat = shape_shape(ele_shape(force,ele),ele_shape(force,ele),&
1625 &force%mesh%shape%quadrature%weight)
1626 do dim1 = 1, mesh_dim(force)1987 do dim1 = 1, mesh_dim(force)
1627 force_rhs((dim1-1)*uloc+1:dim1*uloc) = rhs_loc(dim1,:)1988 force_rhs((dim1-1)*uloc+1:dim1*uloc) = rhs_loc(dim1,:)
1628 end do1989 end do
@@ -1635,10 +1996,12 @@
1635 end do1996 end do
1636 end do1997 end do
16371998
1999 !LHS is multiplied by weights(ele)^2, RHS is multiplied by weights(ele)
2000
1638 call solve(l_u_mat,force_rhs)2001 call solve(l_u_mat,force_rhs)
1639 do dim1= 1, mesh_dim(force)2002 do dim1= 1, mesh_dim(force)
1640 call set(force,dim1,ele_nodes(force,ele),&2003 call set(force,dim1,ele_nodes(force,ele),&
1641 &force_rhs((dim1-1)*uloc+1:dim1*uloc))2004 &force_rhs((dim1-1)*uloc+1:dim1*uloc)/weight)
1642 end do2005 end do
16432006
1644 end subroutine set_pressure_force_ele2007 end subroutine set_pressure_force_ele
@@ -1709,7 +2072,6 @@
1709 up_gi = -ele_val_at_quad(down,ele)2072 up_gi = -ele_val_at_quad(down,ele)
17102073
1711 call get_up_gi(X,ele,up_gi)2074 call get_up_gi(X,ele,up_gi)
1712
1713 coriolis_rhs = 0.2075 coriolis_rhs = 0.
1714 l_u_mat = 0.2076 l_u_mat = 0.
1715 !metrics for velocity mass and coriolis matrices2077 !metrics for velocity mass and coriolis matrices
@@ -1747,15 +2109,16 @@
1747 end subroutine set_coriolis_term_ele2109 end subroutine set_coriolis_term_ele
1748 2110
1749 subroutine set_local_velocity_from_streamfunction_ele(&2111 subroutine set_local_velocity_from_streamfunction_ele(&
1750 &U_local,psi,down,X,ele)2112 &U_local,psi,down,X,ele,weight)
1751 implicit none2113 implicit none
1752 type(vector_field), intent(inout) :: U_local2114 type(vector_field), intent(inout) :: U_local
1753 type(vector_field), intent(in) :: down,X2115 type(vector_field), intent(in) :: down,X
1754 type(scalar_field), intent(in) :: psi2116 type(scalar_field), intent(in) :: psi
1755 integer, intent(in) :: ele2117 integer, intent(in) :: ele
2118 real, intent(in) :: weight
1756 !2119 !
1757 real, dimension(ele_loc(psi,ele)) :: psi_loc2120 real, dimension(ele_loc(psi,ele)) :: psi_loc
1758 real, dimension(mesh_dim(psi),ele_ngi(psi,ele)) :: dpsi_gi2121 real, dimension(mesh_dim(psi),ele_ngi(psi,ele)) :: dpsi_gi, Uloc_gi
1759 real, dimension(ele_ngi(psi,ele)) :: div_gi2122 real, dimension(ele_ngi(psi,ele)) :: div_gi
1760 real, dimension(mesh_dim(U_local),ele_loc(U_local,ele)) :: U_loc2123 real, dimension(mesh_dim(U_local),ele_loc(U_local,ele)) :: U_loc
1761 real, dimension(ele_loc(U_local,ele),ele_loc(U_local,ele)) :: &2124 real, dimension(ele_loc(U_local,ele),ele_loc(U_local,ele)) :: &
@@ -1764,6 +2127,8 @@
1764 integer :: dim1,gi,uloc2127 integer :: dim1,gi,uloc
1765 real, dimension(X%dim, ele_ngi(X,ele)) :: up_gi2128 real, dimension(X%dim, ele_ngi(X,ele)) :: up_gi
1766 integer :: orientation2129 integer :: orientation
2130 real, dimension(mesh_dim(U_local), X%dim, ele_ngi(U_local,ele)) :: J
2131 real, dimension(ele_ngi(X,ele)) :: detJ
17672132
1768 uloc = ele_loc(U_local,ele)2133 uloc = ele_loc(U_local,ele)
The diff has been truncated for viewing.