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
1=== modified file 'Makefile.in'
2--- Makefile.in 2012-09-19 11:09:17 +0000
3+++ Makefile.in 2012-10-23 15:04:25 +0000
4@@ -186,7 +186,9 @@
5 @echo " LD $(PROGRAM)"
6 @$(EVAL) $(LINKER) -o $(PROGRAM) main.o $(LIBS)
7
8-sw: bin/shallow_water
9+sw: bin/shallow_water
10+swm: bin/shallow_water_mimetic
11+test_sw: bin/test_sw_advection
12 be: bin/burgers_equation
13 hh: bin/hybridized_helmholtz_solver
14
15@@ -199,6 +201,22 @@
16 @$(EVAL) $(FLLINKER) -o bin/hybridized_helmholtz_solver main/Hybridized_Helmholtz_Solver.o $(LIBS)
17 @$(EVAL) $(FLLINKER) -o bin/shallow_water main/Shallow_Water.o $(LIBS)
18
19+bin/shallow_water_mimetic: fluidity_library main/Shallow_Water_Mimetic.F90
20+ @cd main; $(MAKE) Shallow_Water_Mimetic.o
21+ @echo "BUILD shallow_water_mimetic"
22+ @echo " MKDIR bin"
23+ @mkdir -p bin
24+ @echo " LD shallow_water_mimetic"
25+ @$(EVAL) $(FLLINKER) -o bin/shallow_water_mimetic main/Shallow_Water_Mimetic.o $(LIBS)
26+
27+bin/test_sw_advection: fluidity_library main/test_sw_advection.F90
28+ @cd main; $(MAKE) test_sw_advection.o
29+ @echo "BUILD test_sw_advection"
30+ @echo " MKDIR bin"
31+ @mkdir -p bin
32+ @echo " LD test_sw_advection"
33+ @$(EVAL) $(LINKER) -o bin/test_sw_advection main/test_sw_advection.o $(LIBS)
34+
35 bin/hybridized_helmholtz_solver: fluidity_library main/Hybridized_Helmholtz_Solver.F90
36 @cd main; $(MAKE) Hybridized_Helmholtz_Solver.o
37 @echo "BUILD hybridized_helmholtz_solver"
38
39=== modified file 'adjoint/Burgers_Control_Callbacks.F90'
40--- adjoint/Burgers_Control_Callbacks.F90 2011-07-07 16:30:31 +0000
41+++ adjoint/Burgers_Control_Callbacks.F90 2012-10-23 15:04:25 +0000
42@@ -34,7 +34,7 @@
43 use state_module
44 use python_state
45 use sparse_matrices_fields
46- use manifold_projections
47+ use manifold_tools
48 use mangle_dirichlet_rows_module
49
50 implicit none
51
52=== modified file 'adjoint/Makefile.dependencies'
53--- adjoint/Makefile.dependencies 2011-07-19 16:55:03 +0000
54+++ adjoint/Makefile.dependencies 2012-10-23 15:04:25 +0000
55@@ -70,10 +70,9 @@
56 Burgers_Control_Callbacks.o ../include/burgers_adjoint_controls.mod: \
57 Burgers_Control_Callbacks.F90 ../include/fdebug.h ../include/fields.mod \
58 ../include/global_parameters.mod \
59- ../include/mangle_dirichlet_rows_module.mod \
60- ../include/manifold_projections.mod ../include/python_state.mod \
61- ../include/sparse_matrices_fields.mod ../include/spud.mod \
62- ../include/state_module.mod
63+ ../include/mangle_dirichlet_rows_module.mod ../include/manifold_tools.mod \
64+ ../include/python_state.mod ../include/sparse_matrices_fields.mod \
65+ ../include/spud.mod ../include/state_module.mod
66
67 ../include/forward_main_loop.mod: Forward_Main_Loop.o
68 @true
69@@ -128,7 +127,7 @@
70 Shallow_Water_Adjoint_Callbacks.F90 ../include/adjoint_global_variables.mod \
71 ../include/fdebug.h ../include/fields.mod ../include/global_parameters.mod \
72 ../include/libadjoint_data_callbacks.mod ../include/mangle_options_tree.mod \
73- ../include/manifold_projections.mod ../include/populate_state_module.mod \
74+ ../include/manifold_tools.mod ../include/populate_state_module.mod \
75 ../include/sparse_matrices_fields.mod ../include/spud.mod \
76 ../include/state_module.mod
77
78@@ -140,7 +139,7 @@
79 ../include/shallow_water_adjoint_controls.mod: \
80 Shallow_Water_Control_Callbacks.F90 ../include/fdebug.h \
81 ../include/fields.mod ../include/global_parameters.mod \
82- ../include/manifold_projections.mod ../include/python_state.mod \
83+ ../include/manifold_tools.mod ../include/python_state.mod \
84 ../include/sparse_matrices_fields.mod ../include/spud.mod \
85 ../include/state_module.mod
86
87
88=== modified file 'adjoint/Shallow_Water_Adjoint_Callbacks.F90'
89--- adjoint/Shallow_Water_Adjoint_Callbacks.F90 2011-06-09 09:43:33 +0000
90+++ adjoint/Shallow_Water_Adjoint_Callbacks.F90 2012-10-23 15:04:25 +0000
91@@ -39,7 +39,7 @@
92 use spud, only: get_option
93 use adjoint_global_variables, only: adj_path_lookup
94 use mangle_options_tree, only: adjoint_field_path
95- use manifold_projections
96+ use manifold_tools
97 use global_parameters, only: running_adjoint
98 use populate_state_module
99 implicit none
100@@ -488,7 +488,7 @@
101 path = adjoint_field_path(path)
102 end if
103
104- call get_option(trim(path) // "/prognostic/temporal_discretisation/theta", theta)
105+ call get_option("/timestepping/theta", theta)
106 call get_option(trim(path) // "/prognostic/mean_layer_thickness", d0)
107
108 if (hermitian==ADJ_FALSE) then
109@@ -858,7 +858,7 @@
110
111 ierr = adj_dict_find(adj_path_lookup, "Fluid::LayerThickness", path)
112 call adj_chkierr(ierr)
113- call get_option(trim(path) // "/prognostic/temporal_discretisation/theta", theta)
114+ call get_option("/timestepping/theta",theta)
115 call get_option(trim(path) // "/prognostic/mean_layer_thickness", d0)
116
117 eta_mesh => extract_mesh(matrices, "LayerThicknessMesh")
118
119=== modified file 'adjoint/Shallow_Water_Control_Callbacks.F90'
120--- adjoint/Shallow_Water_Control_Callbacks.F90 2011-06-11 13:26:55 +0000
121+++ adjoint/Shallow_Water_Control_Callbacks.F90 2012-10-23 15:04:25 +0000
122@@ -34,7 +34,7 @@
123 use state_module
124 use python_state
125 use sparse_matrices_fields
126- use manifold_projections
127+ use manifold_tools
128
129 implicit none
130
131@@ -154,7 +154,7 @@
132 adj_vfield => extract_vector_field(states(state_id), "AdjointLocalVelocityDelta")
133 ! We need the AdjointVelocity for the option path only
134 adj_sfield2 => extract_scalar_field(states(state_id), "AdjointLayerThickness")
135- call get_option(trim(adj_sfield2%option_path) // "/prognostic/temporal_discretisation/theta", theta)
136+ call get_option("/timestepping/theta", theta)
137 call get_option(trim(adj_sfield2%option_path) // "/prognostic/mean_layer_thickness", d0)
138
139 big_mat => extract_block_csr_matrix(matrices, "InverseBigMatrix")
140
141=== modified file 'assemble/Advection_Diffusion_DG.F90'
142--- assemble/Advection_Diffusion_DG.F90 2012-10-11 17:52:01 +0000
143+++ assemble/Advection_Diffusion_DG.F90 2012-10-23 15:04:25 +0000
144@@ -1547,7 +1547,7 @@
145 end if
146 end if
147 end if
148-
149+
150 ! Add stabilisation to the advection term if requested by the user.
151 if (stabilisation_scheme==UPWIND) then
152 ! NOTE: U_nl_q may (or may not) have been modified by the grid velocity
153@@ -2603,7 +2603,6 @@
154 nnAdvection_in=shape_shape(T_shape, T_shape_2, &
155 & outer_advection_integral * detwei)
156
157-
158 end if
159 if (include_diffusion.or.have_buoyancy_adjustment_by_vertical_diffusion) then
160
161
162=== added file 'assemble/Advection_Local_DG.F90'
163--- assemble/Advection_Local_DG.F90 1970-01-01 00:00:00 +0000
164+++ assemble/Advection_Local_DG.F90 2012-10-23 15:04:25 +0000
165@@ -0,0 +1,2538 @@
166+! Copyright (C) 2006 Imperial College London and others.
167+!
168+! Please see the AUTHORS file in the main source directory for a full list
169+! of copyright holders.
170+!
171+! Prof. C Pain
172+! Applied Modelling and Computation Group
173+! Department of Earth Science and Engineering
174+! Imperial College London
175+!
176+! amcgsoftware@imperial.ac.uk
177+!
178+! This library is free software; you can redistribute it and/or
179+! modify it under the terms of the GNU Lesser General Public
180+! License as published by the Free Software Foundation,
181+! version 2.1 of the License.
182+!
183+! This library is distributed in the hope that it will be useful,
184+! but WITHOUT ANY WARRANTY; without even the implied warranty of
185+! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
186+! Lesser General Public License for more details.
187+!
188+! You should have received a copy of the GNU Lesser General Public
189+! License along with this library; if not, write to the Free Software
190+! Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
191+! USA
192+#include "fdebug.h"
193+
194+module advection_local_DG
195+ !!< This module contains the Discontinuous Galerkin form of the advection
196+ !!< equation when vector fields are represented in the local coordinates
197+ !!< from the Piola transformation.
198+ use elements
199+ use global_parameters, only:current_debug_level, OPTION_PATH_LEN
200+ use sparse_tools
201+ use fetools
202+ use dgtools
203+ use fields
204+ use fefields
205+ use state_module
206+ use shape_functions
207+ use global_numbering
208+ use transform_elements
209+ use vector_tools
210+ use fldebug
211+ use vtk_interfaces
212+ use Coordinates
213+ use Tensors
214+ use petsc_solve_state_module
215+ use spud
216+ use slope_limiters_dg
217+ use sparsity_patterns
218+ use sparse_matrices_fields
219+ use sparsity_patterns_meshes
220+ use Vector_Tools
221+ use manifold_tools
222+ use bubble_tools
223+ use diagnostic_fields, only: calculate_diagnostic_variable
224+ use global_parameters, only : FIELD_NAME_LEN
225+
226+ implicit none
227+
228+ ! Buffer for output messages.
229+ character(len=255), private :: message
230+
231+ private
232+ public solve_advection_dg_subcycle, solve_vector_advection_dg_subcycle,&
233+ & solve_advection_cg_tracer, compute_U_residual, get_pv
234+
235+ !parameters specifying vector upwind options
236+ integer, parameter :: VECTOR_UPWIND_EDGE=1, VECTOR_UPWIND_SPHERE=2
237+
238+ ! Local private control parameters. These are module-global parameters
239+ ! because it would be expensive and/or inconvenient to re-evaluate them
240+ ! on a per-element or per-face basis
241+ real :: dt
242+
243+ ! Whether to include various terms
244+ logical :: include_advection
245+ contains
246+
247+ subroutine solve_advection_dg_subcycle(field_name, state, velocity_name,&
248+ &continuity, flux)
249+ !!< Construct and solve the advection equation for the given
250+ !!< field using discontinuous elements.
251+
252+ !! Name of the field to be solved for.
253+ character(len=*), intent(in) :: field_name
254+ !! Collection of fields defining system state.
255+ type(state_type), intent(inout) :: state
256+ !! Optional velocity name
257+ character(len = *), intent(in) :: velocity_name
258+ !! Solve continuity equation as opposed to advection equation
259+ logical, intent(in), optional :: continuity
260+ !! Return a flux variable such that T-T_old=div(Flux)
261+ type(vector_field), intent(inout), optional :: Flux
262+
263+ !! Tracer to be solved for.
264+ type(scalar_field), pointer :: T, T_old, s_field
265+
266+ !! Coordinate and velocity fields
267+ type(vector_field), pointer :: X, U_nl
268+
269+ !! Velocity field in Cartesian coordinates for slope limiter
270+ type(vector_field) :: U_nl_cartesian
271+
272+ !! Change in T over one timestep.
273+ type(scalar_field) :: delta_T, delta_T_total
274+
275+ !! Sparsity of advection matrix.
276+ type(csr_sparsity), pointer :: sparsity
277+
278+ !! System matrix.
279+ type(csr_matrix) :: matrix, mass, inv_mass
280+
281+ !! Sparsity of mass matrix.
282+ type(csr_sparsity) :: mass_sparsity
283+
284+ !! Right hand side vector.
285+ type(scalar_field) :: rhs
286+
287+ !! Whether to invoke the slope limiter
288+ logical :: limit_slope
289+ !! Which limiter to use
290+ integer :: limiter
291+
292+ !! Number of advection subcycles.
293+ integer :: subcycles
294+ real :: max_courant_number
295+
296+ !! Flux reconstruction stuff:
297+ !! Upwind fluxes trace variable
298+ type(scalar_field) :: UpwindFlux
299+ !! Mesh where UpwindFlux lives
300+ type(mesh_type), pointer :: lambda_mesh
301+ !! Upwind Flux matrix
302+ type(csr_matrix) :: UpwindFluxMatrix
303+
304+ character(len=FIELD_NAME_LEN) :: limiter_name
305+ integer :: i, ele
306+ real :: meancheck
307+ ewrite(1,*) 'subroutine solve_advection_dg_subcycle'
308+
309+
310+ T=>extract_scalar_field(state, field_name)
311+ T_old=>extract_scalar_field(state, "Old"//field_name)
312+ X=>extract_vector_field(state, "Coordinate")
313+ U_nl=>extract_vector_field(state, velocity_name)
314+
315+ ewrite(2,*) 'UVALS in dg', maxval(abs(U_nl%val))
316+
317+ ! Reset T to value at the beginning of the timestep.
318+ call set(T, T_old)
319+ if(present(Flux)) then
320+ call zero(Flux)
321+ end if
322+
323+ sparsity => get_csr_sparsity_firstorder(state, T%mesh, T%mesh)
324+ call allocate(matrix, sparsity) ! Add data space to the sparsity
325+ ! pattern.
326+ call zero(matrix)
327+
328+ !Flux reconstruction allocations
329+ if(present(Flux)) then
330+ !Allocate trace variable for upwind fluxes
331+ lambda_mesh=>extract_mesh(state, "VelocityMeshTrace")
332+ call allocate(UpwindFlux,lambda_mesh, "UpwindFlux")
333+ call zero(UpwindFlux)
334+ !Allocate matrix that maps T values to upwind fluxes
335+ sparsity => get_csr_sparsity_firstorder(state,lambda_mesh,T%mesh)
336+ call allocate(UpwindFluxMatrix,sparsity)
337+ call zero(UpwindFluxMatrix)
338+ end if
339+
340+ mass_sparsity=make_sparsity_dg_mass(T%mesh)
341+ call allocate(mass, mass_sparsity)
342+ call zero(mass)
343+ call allocate(inv_mass, mass_sparsity)
344+ call zero(inv_mass)
345+
346+ ! Ensure delta_T inherits options from T.
347+ call allocate(delta_T, T%mesh, "delta_T")
348+ call zero(delta_T)
349+ delta_T%option_path = T%option_path
350+ if(present(Flux)) then
351+ call allocate(delta_T_total, T%mesh, "deltaT_total")
352+ call zero(delta_T_total)
353+ end if
354+ call allocate(rhs, T%mesh, trim(field_name)//" RHS")
355+ call zero(rhs)
356+
357+ if(present(Flux)) then
358+ call construct_advection_dg(matrix, rhs, field_name, state, &
359+ mass, velocity_name=velocity_name,continuity=continuity, &
360+ UpwindFlux=upwindflux, UpwindFluxMatrix=UpwindFluxMatrix)
361+ else
362+ call construct_advection_dg(matrix, rhs, field_name, state, &
363+ mass, velocity_name=velocity_name,continuity=continuity)
364+ end if
365+
366+ call get_dg_inverse_mass_matrix(inv_mass, mass)
367+
368+ ! Note that since dt is module global, these lines have to
369+ ! come after construct_advection_diffusion_dg.
370+ call get_option("/timestepping/timestep", dt)
371+
372+ if(have_option(trim(T%option_path)//&
373+ &"/prognostic/temporal_discretisation/discontinuous_galerkin/&
374+ &/number_advection_subcycles")) then
375+ call get_option(trim(T%option_path)//&
376+ &"/prognostic/temporal_discretisation/discontinuous_galerkin/&
377+ &/number_advection_subcycles", subcycles)
378+ else
379+ call get_option(trim(T%option_path)//&
380+ &"/prognostic/temporal_discretisation/discontinuous_galerkin/&
381+ &maximum_courant_number_per_subcycle", Max_Courant_number)
382+
383+ s_field => extract_scalar_field(state, "DG_CourantNumber")
384+ call calculate_diagnostic_variable(state, "DG_CourantNumber_Local", &
385+ & s_field)
386+ ewrite_minmax(s_field)
387+ subcycles = ceiling( maxval(s_field%val)/Max_Courant_number)
388+ call allmax(subcycles)
389+ ewrite(2,*) 'Number of subcycles for tracer eqn: ', subcycles
390+ end if
391+
392+ limit_slope=.false.
393+ if (have_option(trim(T%option_path)//"/prognostic/spatial_discretisation/&
394+ &discontinuous_galerkin/slope_limiter")) then
395+ limit_slope=.true.
396+
397+ call get_option(trim(T%option_path)//"/prognostic/spatial_discretisation/discontinuous_galerkin/slope_limiter/name",limiter_name)
398+
399+ select case(trim(limiter_name))
400+ case("Vertex_Based")
401+ limiter=LIMITER_VB
402+ case default
403+ FLAbort('No such limiter')
404+ end select
405+
406+ ! Note unsafe for mixed element meshes
407+ if (element_degree(T,1)==0) then
408+ FLExit("Slope limiters make no sense for degree 0 fields")
409+ end if
410+
411+ end if
412+
413+ if (limit_slope) then
414+ ! Filter wiggles from T
415+ call limit_vb_manifold(state,T,delta_T)
416+ if(present(flux)) then
417+ call zero(upwindflux)
418+ call mult(delta_T_total, mass, delta_T)
419+ call update_flux(Flux, Delta_T_total, UpwindFlux, Mass)
420+ end if
421+ end if
422+
423+ do i=1, subcycles
424+ ewrite(1,*) 'SUBCYCLE', i
425+
426+ ! dT = Advection * T
427+ call mult(delta_T, matrix, T)
428+ ! dT = dT + RHS
429+ call addto(delta_T, RHS, -1.0)
430+ if(present(Flux)) then
431+ if(maxval(abs(RHS%val))>1.0e-8) then
432+ FLAbort('Flux reconstruction doesn''t work if diffusion/bcs present at the moment')
433+ end if
434+ end if
435+ call scale(delta_T, -dt/subcycles)
436+ ! dT = M^(-1) dT
437+ if(present(flux)) then
438+ call mult(UpwindFlux, upwindfluxmatrix, T)
439+ call scale(UpwindFlux,-dt/subcycles)
440+ call update_flux(Flux, Delta_T, UpwindFlux, Mass)
441+ end if
442+ ! T = T + dt/s * dT
443+ call dg_apply_mass(inv_mass, delta_T)
444+ ewrite(2,*) 'Delta_T', maxval(abs(delta_T%val))
445+ call addto(T, delta_T)
446+ !Probably need to halo_update(Flux) as well
447+ call halo_update(T)
448+
449+ if (limit_slope) then
450+ ! Filter wiggles from T
451+ call limit_vb_manifold(state,T,delta_T)
452+ if(present(flux)) then
453+ call mult(delta_t_total, mass, delta_t)
454+ call zero(UpwindFlux)
455+ call update_flux(Flux, Delta_T_total, UpwindFlux, Mass)
456+ end if
457+ end if
458+ end do
459+
460+ if(present(Flux)) then
461+ if(have_option('/material_phase::Fluid/scalar_field::LayerThickness/p&
462+ &rognostic/spatial_discretisation/debug')) then
463+ do ele = 1, ele_count(Flux)
464+ call check_flux(Flux,T,T_old,X,mass,ele)
465+ end do
466+ end if
467+ call deallocate(UpwindFlux)
468+ call deallocate(UpwindFluxMatrix)
469+ call deallocate(delta_T_total)
470+ end if
471+
472+ call deallocate(delta_T)
473+ call deallocate(matrix)
474+ call deallocate(mass)
475+ call deallocate(inv_mass)
476+ call deallocate(mass_sparsity)
477+ call deallocate(rhs)
478+
479+ end subroutine solve_advection_dg_subcycle
480+
481+ subroutine check_flux(Flux,T,T_old,X,mass,ele)
482+ type(vector_field), intent(in) :: Flux, X
483+ type(scalar_field), intent(in) :: T,T_old
484+ integer, intent(in) :: ele
485+ type(csr_matrix), intent(in) :: mass
486+ !
487+ real, dimension(Flux%dim,ele_loc(Flux,ele)) :: Flux_vals
488+ real, dimension(ele_ngi(T,ele)) :: Div_Flux_gi
489+ real, dimension(ele_ngi(T,ele)) :: Delta_T_gi, T_gi
490+ real, dimension(ele_loc(T,ele)) :: Delta_T_rhs, Div_Flux_rhs,T_rhs
491+ integer :: dim1, loc
492+ real, dimension(ele_ngi(X,ele)) :: detwei
493+ real, dimension(mesh_dim(X), X%dim, ele_ngi(X,ele)) :: J_mat
494+ real :: residual
495+ Delta_T_gi = ele_val_at_quad(T,ele)-ele_val_at_quad(T_old,ele)
496+ T_gi = ele_val_at_quad(T,ele)
497+ Flux_vals = ele_val(Flux,ele)
498+
499+ Div_Flux_gi = 0.
500+ !dn is loc x ngi x dim
501+ do dim1 = 1, Flux%dim
502+ do loc = 1, ele_loc(Flux,ele)
503+ Div_Flux_gi = Div_Flux_gi + Flux%mesh%shape%dn(loc,:,dim1)*&
504+ &Flux_vals(dim1,loc)
505+ end do
506+ end do
507+
508+ call compute_jacobian(ele_val(X,ele), ele_shape(X,ele),&
509+ detwei=detwei,J=J_mat)
510+
511+ Delta_T_rhs = shape_rhs(ele_shape(T,ele),Delta_T_gi*detwei)
512+ T_rhs = shape_rhs(ele_shape(T,ele),T_gi*detwei)
513+ Div_Flux_rhs = shape_rhs(ele_shape(T,ele),Div_Flux_gi*&
514+ Flux%mesh%shape%quadrature%weight)
515+
516+ residual = &
517+ maxval(abs(Delta_T_rhs-Div_Flux_rhs))/max(1.0&
518+ &,maxval(abs(T_rhs)))
519+ if(residual>1.0e-6) then
520+ ewrite(2,*) residual, maxval(abs(T_rhs))
521+ FLExit('Flux residual error.')
522+ end if
523+
524+ end subroutine check_flux
525+
526+ subroutine update_flux(Flux, Delta_T, UpwindFlux, Mass)
527+ !! Subroutine to compute div-conforming Flux such that
528+ !! div Flux = Delta_T
529+ !! with Flux.n = UpwindFlux on element boundaries
530+ !! and then to add to Flux
531+ type(vector_field), intent(inout) :: Flux
532+ type(scalar_field), intent(in) :: Delta_T
533+ type(scalar_field), intent(in) :: UpwindFlux
534+ type(csr_matrix), intent(in) :: Mass
535+ !
536+ integer :: ele, i
537+ real :: Area
538+ integer, dimension(:), pointer :: T_ele
539+
540+ !Checking the Flux is in local representation
541+ assert(Flux%dim==mesh_dim(Flux))
542+
543+ do ele = 1, ele_count(Flux)
544+ T_ele => ele_nodes(delta_T,ele)
545+ area = 0.
546+ do i = 1, size(T_ele)
547+ area = area + &
548+ sum(row_val(Mass,t_ele(i)))
549+ end do
550+ call update_flux_ele(Flux, Delta_T, UpwindFlux,ele,area)
551+ end do
552+
553+ end subroutine update_flux
554+
555+ subroutine update_flux_ele(Flux, Delta_T, UpwindFlux,ele,area)
556+ !! Subroutine to compute div-conforming Flux such that
557+ !! div Flux = Delta_T
558+ !! with Flux.n = UpwindFlux on element boundaries
559+ type(vector_field), intent(inout) :: Flux
560+ type(scalar_field), intent(in) :: Delta_T
561+ type(scalar_field), intent(in) :: UpwindFlux
562+ integer, intent(in) :: ele
563+ real, intent(in) :: area
564+ !
565+ real, dimension(Flux%dim,ele_loc(Flux,ele)) :: Flux_vals
566+ real, dimension(ele_loc(Delta_T,ele)) :: Delta_T_vals
567+ real, dimension(Flux%dim*ele_loc(Flux,ele),&
568+ Flux%dim*ele_loc(Flux,ele)) :: Flux_mat
569+ real, dimension(Flux%dim*ele_loc(Flux,ele)) :: Flux_rhs
570+ integer :: row, ni, ele2, face, face2,floc, i, dim1, dim2
571+ integer, dimension(:), pointer :: neigh
572+ type(constraints_type), pointer :: flux_constraint
573+ type(element_type), pointer :: flux_shape, T_shape
574+ real, dimension(Flux%dim,ele_loc(Delta_T,ele),&
575+ &ele_loc(Flux,ele)) :: grad_mat
576+ real, dimension(ele_loc(Delta_t,ele)) :: delta_t_val
577+ real, dimension(ele_loc(flux,ele),ele_loc(flux,ele)) &
578+ &:: flux_mass_mat
579+ real, dimension(ele_loc(Delta_t,ele)) :: div_flux_val
580+ real, dimension(ele_ngi(Delta_t,ele)) :: div_flux_gi
581+ real :: total_flux, residual
582+ !! Need to set up and solve equation for flux DOFs
583+ !! Rows are as follows:
584+ !! All of the normal fluxes through edges, numbered according to
585+ !! UpwindFlux numbering
586+ !! Inner product of Flux with grad basis equaling gradient of delta_T
587+ !! plus boundary conditions
588+ !! Inner product of solution with curl basis equals zero
589+ !! inner product of solution with constraint basis equals zero
590+
591+ !! Debugging check:
592+ !! \int \Delta T dV = \int div Flux dV = \int Flux.n dS
593+ !! = \int \hat{Flux}.\hat{n dS}
594+ neigh => ele_neigh(Flux,ele)
595+ total_flux = 0.
596+ do ni = 1, size(neigh)
597+ ele2 = neigh(ni)
598+ face = ele_face(Flux,ele,ele2)
599+ if(ele2>0) then
600+ face2 = ele_face(Flux,ele2,ele)
601+ else
602+ face2 = face
603+ end if
604+ if(face>face2) then
605+ total_flux=total_flux+sum(face_val(UpwindFlux,face))
606+ else
607+ total_flux=total_flux-sum(face_val(UpwindFlux,face))
608+ end if
609+ end do
610+ residual = abs(total_flux-sum(ele_val(Delta_T,ele)))&
611+ &/max(1.0,maxval(ele_val(Delta_T,ele)))/area
612+ if(residual>1.0e-10) then
613+ ewrite(0,*) 'Residual = ', residual
614+ FLExit('Bad residual')
615+ end if
616+
617+ Flux_rhs = 0.
618+ Flux_mat = 0.
619+ flux_shape => ele_shape(flux,ele)
620+ T_shape => ele_shape(delta_t,ele)
621+ flux_constraint => flux%mesh%shape%constraints
622+
623+ row = 1
624+ !first do the face DOFs
625+ neigh => ele_neigh(Flux,ele)
626+ do ni = 1, size(neigh)
627+ ele2 = neigh(ni)
628+ face = ele_face(Flux,ele,ele2)
629+ if(ele2>0) then
630+ face2 = ele_face(Flux,ele2,ele)
631+ else
632+ face2 = face
633+ end if
634+ floc = face_loc(upwindflux,face)
635+
636+ call update_flux_face(&
637+ Flux,UpwindFlux,face,face2,&
638+ ele,ele2,Flux_mat(row:row+floc-1,:),&
639+ Flux_rhs(row:row+floc-1))
640+ row = row + floc
641+ end do
642+
643+ !Next the gradient basis
644+ !Start with \nabla\cdot F = \Delta T
645+ !Integrate with test function over element
646+ ! <\phi,Div F>_E = <\phi, \Delta T>_E
647+
648+ grad_mat = shape_dshape(T_shape, flux_shape%dn,&
649+ & T_shape%quadrature%weight)
650+ delta_t_val = ele_val(delta_t,ele)
651+ do i = 1, ele_loc(Delta_T,ele)-1
652+ do dim1 = 1, flux%dim
653+ Flux_mat(row,(dim1-1)*ele_loc(flux,ele)+1:dim1*ele_loc(flux,ele))&
654+ &=grad_mat(dim1,i,:)
655+ end do
656+ flux_rhs(row) = delta_t_val(i)
657+ row = row + 1
658+ end do
659+
660+ !Then the curl basis
661+ !Adding in the curl terms
662+ flux_mass_mat = shape_shape(flux_shape,flux_shape,T_shape%quadrature%weight)
663+ do i = 1, flux_constraint%n_curl_basis
664+ flux_mat(row,:) = 0.
665+ flux_rhs(row) = 0.
666+ do dim1 = 1, mesh_dim(flux)
667+ do dim2 = 1, mesh_dim(flux)
668+ flux_mat(&
669+ row,(dim2-1)*ele_loc(flux,ele)+1:dim2*ele_loc(flux,ele)) =&
670+ flux_mat(&
671+ row,(dim2-1)*ele_loc(flux,ele)+1:dim2*ele_loc(flux,ele)) +&
672+ matmul(flux_constraint%curl_basis(i,:,dim1),&
673+ flux_mass_mat)
674+ end do
675+ end do
676+ row = row + 1
677+ end do
678+
679+ !Then the constraints
680+ do i = 1, flux_constraint%n_constraints
681+ do dim1 = 1, mesh_dim(flux)
682+ flux_mat(&
683+ row,(dim1-1)*ele_loc(flux,ele)+1:&
684+ dim1*ele_loc(flux,ele)) =&
685+ flux_constraint%orthogonal(i,:,dim1)
686+ end do
687+ row = row + 1
688+ end do
689+
690+ call solve(flux_mat,flux_rhs)
691+
692+ do dim1 = 1, mesh_dim(flux)
693+ flux_vals(dim1,:) = &
694+ flux_rhs((dim1-1)*ele_loc(flux,ele)+1:dim1*ele_loc(flux,ele))
695+ end do
696+
697+ !A debugging check.
698+ !Computing < \phi, div F> in local coordinates
699+ !and comparing with <\phi, delta_T> in global coordinates
700+ !(works because of pullback formula)
701+
702+ !dn is loc x ngi x dim
703+ !Flux is dim x loc
704+ div_flux_gi = 0.
705+ do dim1 =1, size(flux_vals,1)
706+ do i=1,size(flux_vals,2)
707+ div_flux_gi = div_flux_gi + flux_vals(dim1,i)*&
708+ &flux_shape%dn(i,:,dim1)
709+ end do
710+ end do
711+ div_flux_val = shape_rhs(T_shape,div_flux_gi*T_shape%quadrature%weight)
712+ residual = maxval(abs(div_flux_val-delta_T_val))/max(1.0&
713+ &,maxval(abs(delta_T_val)))/area
714+ assert(residual<1.0e-10)
715+
716+ !Add the flux in
717+ call addto(flux,ele_nodes(flux,ele),flux_vals)
718+ end subroutine update_flux_ele
719+
720+ subroutine update_flux_face(&
721+ Flux,UpwindFlux,face,face2,&
722+ ele,ele2,Flux_mat_rows,Flux_rhs_rows)
723+ type(vector_field), intent(in) :: Flux
724+ type(scalar_Field), intent(in) :: UpwindFlux
725+ integer, intent(in) :: face,face2,ele,ele2
726+ real, dimension(face_loc(upwindflux,ele),&
727+ &mesh_dim(flux)*ele_loc(flux,ele)), &
728+ &intent(inout) :: flux_mat_rows
729+ real, dimension(flux%mesh%shape%constraints%n_face_basis), intent(inout)&
730+ &:: flux_rhs_rows
731+ !
732+ integer :: dim1, row, flux_dim1
733+ real, dimension(mesh_dim(flux), face_ngi(flux, face)) :: n_local
734+ real, dimension(face_ngi(flux,face)) :: detwei_f
735+ real :: weight
736+ type(element_type), pointer :: flux_face_shape, upwindflux_face_shape
737+ integer, dimension(face_loc(flux,face)) :: flux_face_nodes
738+ real, dimension(mesh_dim(flux),face_loc(upwindflux,face),&
739+ face_loc(flux,face)) :: face_mat
740+ !
741+ flux_face_shape=>face_shape(flux, face)
742+ upwindflux_face_shape=>face_shape(upwindflux, face)
743+ flux_face_nodes=face_local_nodes(flux, face)
744+
745+ !Get normal in local coordinates
746+ call get_local_normal(n_local,weight,flux,&
747+ &local_face_number(flux%mesh,face))
748+
749+ detwei_f = weight*flux_face_shape%quadrature%weight
750+ face_mat = shape_shape_vector(&
751+ upwindflux_face_shape,flux_face_shape,detwei_f,n_local)
752+ !Equation is:
753+ ! <\phi,Flux.n> = <\phi,UpwindFlux> for all trace test functions \phi.
754+
755+ do row = 1, size(flux_mat_rows,1)
756+ flux_mat_rows(row,:) = 0.
757+ do dim1 = 1,mesh_dim(flux)
758+ flux_dim1 = (dim1-1)*ele_loc(flux,ele)
759+ flux_mat_rows(row,flux_dim1+flux_face_nodes)&
760+ &=face_mat(dim1,row,:)
761+ end do
762+ end do
763+ !Need to adopt a sign convention:
764+ !If face>face_2
765+ !We store flux with normal pointing from face into face_2
766+ !Otherwise
767+ !We store flux with normal pointing from face_2 into face
768+ ! Outflow boundary integral.
769+ if(face>face2) then
770+ flux_rhs_rows = face_val(UpwindFlux,face)
771+ else
772+ flux_rhs_rows = -face_val(UpwindFlux,face)
773+ end if
774+ end subroutine update_flux_face
775+
776+ subroutine construct_advection_dg(big_m, rhs, field_name,&
777+ & state, mass, velocity_name, continuity,&
778+ & UpwindFlux, UpwindFluxMatrix)
779+ !!< Construct the advection equation for discontinuous elements in
780+ !!< acceleration form.
781+ !!<
782+ !!< If mass is provided then the mass matrix is not added into big_m or
783+ !!< rhs. It is instead returned as mass. This may be useful for testing
784+ !!< or for solving equations otherwise than in acceleration form.
785+
786+ !! Main advection matrix.
787+ type(csr_matrix), intent(inout) :: big_m
788+ !! Right hand side vector.
789+ type(scalar_field), intent(inout) :: rhs
790+
791+ !! Name of the field to be advected.
792+ character(len=*), intent(in) :: field_name
793+ !! Collection of fields defining system state.
794+ type(state_type), intent(inout) :: state
795+ !! Optional separate mass matrix.
796+ type(csr_matrix), intent(inout), optional :: mass
797+ !! Optional velocity name
798+ character(len = *), intent(in), optional :: velocity_name
799+ !! solve the continuity equation
800+ logical, intent(in), optional :: continuity
801+ !! Upwind flux field
802+ type(scalar_field), intent(in), optional :: UpwindFlux
803+ !! Upwind flux matrix
804+ type(csr_matrix), intent(inout), optional :: UpwindFluxMatrix
805+
806+ !! Position, and velocity fields.
807+ type(vector_field) :: X, U, U_nl
808+ !! Tracer to be solved for.
809+ type(scalar_field) :: T
810+
811+ !! Local velocity name
812+ character(len = FIELD_NAME_LEN) :: lvelocity_name
813+
814+ !! Element index
815+ integer :: ele
816+
817+ !! Status variable for field extraction.
818+ integer :: stat
819+
820+ ewrite(1,*) "Writing advection equation for "&
821+ &//trim(field_name)
822+
823+ ! These names are based on the CGNS SIDS.
824+ T=extract_scalar_field(state, field_name)
825+ X=extract_vector_field(state, "Coordinate")
826+
827+ if(present(velocity_name)) then
828+ lvelocity_name = velocity_name
829+ else
830+ lvelocity_name = "NonlinearVelocity"
831+ end if
832+
833+ if (.not.have_option(trim(T%option_path)//"/prognostic&
834+ &/spatial_discretisation/discontinuous_galerkin&
835+ &/advection_scheme/none")) then
836+ U_nl=extract_vector_field(state, lvelocity_name)
837+ call incref(U_nl)
838+ include_advection=.true.
839+ else
840+ ! Forcing a zero NonlinearVelocity will disable advection.
841+ U=extract_vector_field(state, "Velocity", stat)
842+ if (stat/=0) then
843+ FLExit("Oh dear, no velocity field. A velocity field is required for advection!")
844+ end if
845+ call allocate(U_nl, U%dim, U%mesh, "LocalNonlinearVelocity")
846+ call zero(U_nl)
847+ include_advection=.false.
848+ end if
849+
850+ assert(has_faces(X%mesh))
851+ assert(has_faces(T%mesh))
852+
853+ call zero(big_m)
854+ call zero(RHS)
855+ call zero(mass)
856+
857+ element_loop: do ele=1,element_count(T)
858+
859+ call construct_adv_element_dg(ele, big_m, rhs,&
860+ & X, T, U_nl, mass, continuity=continuity,&
861+ & upwindflux=upwindflux, upwindfluxmatrix=upwindfluxmatrix)
862+
863+ end do element_loop
864+
865+ ! Drop any extra field references.
866+
867+ call deallocate(U_nl)
868+
869+ end subroutine construct_advection_dg
870+
871+ subroutine construct_adv_element_dg(ele, big_m, rhs,&
872+ & X, T, U_nl, mass, continuity, upwindflux, upwindfluxmatrix)
873+ !!< Construct the advection_diffusion equation for discontinuous elements in
874+ !!< acceleration form.
875+ implicit none
876+ !! Index of current element
877+ integer :: ele
878+ !! Main advection matrix.
879+ type(csr_matrix), intent(inout) :: big_m
880+ !! Right hand side vector.
881+ type(scalar_field), intent(inout) :: rhs
882+ !! Optional separate mass matrix.
883+ type(csr_matrix), intent(inout) :: mass
884+
885+ !! Position and velocity.
886+ type(vector_field), intent(in) :: X, U_nl
887+
888+ type(scalar_field), intent(in) :: T
889+
890+ !! Upwind flux field
891+ type(scalar_field), intent(in), optional :: UpwindFlux
892+ !! Upwind flux matrix
893+ type(csr_matrix), intent(inout), optional :: UpwindFluxMatrix
894+
895+
896+ !! Solve the continuity equation rather than advection
897+ logical, intent(in), optional :: continuity
898+
899+ ! Bilinear forms.
900+ real, dimension(ele_loc(T,ele), ele_loc(T,ele)) :: &
901+ mass_mat
902+ real, dimension(ele_loc(T,ele), ele_loc(T,ele)) :: &
903+ Advection_mat, Ad_mat1, Ad_mat2
904+
905+ ! Local assembly matrices.
906+ real, dimension(ele_loc(T,ele), ele_loc(T,ele)) :: l_T_mat
907+ real, dimension(ele_loc(T,ele)) :: l_T_rhs
908+
909+ ! Local variables.
910+
911+ ! Neighbour element, face and neighbour face.
912+ integer :: ele_2, face, face_2
913+ ! Loops over faces.
914+ integer :: ni
915+
916+ ! Transform from local to physical coordinates.
917+ real, dimension(ele_ngi(X,ele)) :: detwei
918+ real, dimension(mesh_dim(U_nl), X%dim, ele_ngi(X,ele)) :: J_mat
919+
920+ ! Different velocities at quad points.
921+ real, dimension(U_nl%dim, ele_ngi(U_nl, ele)) :: U_quad
922+ real, dimension(U_nl%dim, ele_ngi(U_nl, ele)) :: U_nl_q
923+ real, dimension(ele_ngi(U_nl, ele)) :: U_nl_div_q
924+
925+ ! Node and shape pointers.
926+ integer, dimension(:), pointer :: T_ele
927+ type(element_type), pointer :: T_shape, U_shape
928+ ! Neighbours of this element.
929+ integer, dimension(:), pointer :: neigh
930+
931+ integer :: gi,i,j
932+
933+ !----------------------------------------------------------------------
934+ ! Establish local node lists
935+ !----------------------------------------------------------------------
936+
937+ T_ele=>ele_nodes(T,ele) ! Tracer node numbers
938+
939+ !----------------------------------------------------------------------
940+ ! Establish local shape functions
941+ !----------------------------------------------------------------------
942+
943+ T_shape=>ele_shape(T, ele)
944+ U_shape=>ele_shape(U_nl, ele)
945+
946+ !==========================
947+ ! Coordinates
948+ !==========================
949+
950+ ! Get J_mat
951+ call compute_jacobian(ele_val(X,ele), ele_shape(X,ele), detwei=detwei, J=J_mat)
952+
953+ !----------------------------------------------------------------------
954+ ! Construct bilinear forms.
955+ !----------------------------------------------------------------------
956+
957+ ! Element density matrix.
958+ ! /
959+ ! | T T dV
960+ ! /
961+ mass_mat = shape_shape(T_shape, T_shape, detwei)
962+
963+ if (include_advection) then
964+
965+ ! Advecting velocity at quadrature points.
966+ U_quad=ele_val_at_quad(U_nl,ele)
967+ U_nl_q=U_quad
968+
969+ U_nl_div_q=ele_div_at_quad(U_nl, ele, U_shape%dn)
970+
971+ ! Element advection matrix
972+ ! / /
973+ ! - | (grad phi dot U_nl) T dV -| phi ( div U_nl ) T dV
974+ ! / /
975+ Advection_mat = -dshape_dot_vector_shape(T_shape%dn, U_nl_q, T_shape,&
976+ & T_shape%quadrature%weight)
977+ if(.not.present_and_true(continuity)) then
978+ Advection_mat = Advection_mat&
979+ &-shape_shape(T_shape, T_shape, U_nl_div_q * T_shape&
980+ &%quadrature%weight)
981+ end if
982+ else
983+ Advection_mat=0.0
984+ end if
985+
986+ !----------------------------------------------------------------------
987+ ! Perform global assembly.
988+ !----------------------------------------------------------------------
989+
990+ l_T_rhs=0.0
991+
992+ ! Assemble matrix.
993+
994+ ! Advection.
995+ l_T_mat= Advection_mat
996+
997+ call addto(mass, t_ele, t_ele, mass_mat)
998+
999+ call addto(big_m, t_ele, t_ele, l_T_mat)
1000+
1001+ !-------------------------------------------------------------------
1002+ ! Interface integrals
1003+ !-------------------------------------------------------------------
1004+
1005+ neigh=>ele_neigh(T, ele)
1006+
1007+ neighbourloop: do ni=1,size(neigh)
1008+
1009+ !----------------------------------------------------------------------
1010+ ! Find the relevant faces.
1011+ !----------------------------------------------------------------------
1012+
1013+ ! These finding routines are outside the inner loop so as to allow
1014+ ! for local stack variables of the right size in
1015+ ! construct_add_diff_interface_dg.
1016+
1017+ ele_2=neigh(ni)
1018+
1019+ ! Note that although face is calculated on field U, it is in fact
1020+ ! applicable to any field which shares the same mesh topology.
1021+ face=ele_face(T, ele, ele_2)
1022+
1023+ if (ele_2>0) then
1024+ ! Internal faces.
1025+ face_2=ele_face(T, ele_2, ele)
1026+ else
1027+ ! External face.
1028+ face_2=face
1029+ end if
1030+
1031+ call construct_adv_interface_dg(ele, face, face_2,&
1032+ & big_m, rhs, X, T, U_nl, upwindflux, upwindfluxmatrix)
1033+
1034+ end do neighbourloop
1035+
1036+ end subroutine construct_adv_element_dg
1037+
1038+ subroutine construct_adv_interface_dg(ele, face, face_2, &
1039+ big_m, rhs, X, T, U_nl,upwindflux, upwindfluxmatrix)
1040+
1041+ !!< Construct the DG element boundary integrals on the ni-th face of
1042+ !!< element ele.
1043+ implicit none
1044+
1045+ integer, intent(in) :: ele, face, face_2
1046+ type(csr_matrix), intent(inout) :: big_m
1047+ type(scalar_field), intent(inout) :: rhs
1048+ ! We pass these additional fields to save on state lookups.
1049+ type(vector_field), intent(in) :: X, U_nl
1050+ type(scalar_field), intent(in) :: T
1051+ !! Upwind flux field
1052+ type(scalar_field), intent(in), optional :: UpwindFlux
1053+ !! Upwind flux matrix
1054+ type(csr_matrix), intent(inout), optional :: UpwindFluxMatrix
1055+
1056+ ! Face objects and numberings.
1057+ type(element_type), pointer :: T_shape, T_shape_2
1058+ integer, dimension(face_loc(T,face)) :: T_face
1059+ integer, dimension(face_loc(T,face_2)) :: T_face_2
1060+ integer, dimension(face_loc(U_nl,face)) :: U_face
1061+ integer, dimension(face_loc(U_nl,face_2)) :: U_face_2
1062+
1063+ ! Note that both sides of the face can be assumed to have the same
1064+ ! number of quadrature points.
1065+ real, dimension(U_nl%dim, face_ngi(U_nl, face)) :: n1, n2, normal, U_nl_q,&
1066+ & u_f_q, u_f2_q, div_u_f_q
1067+ logical, dimension(face_ngi(U_nl, face)) :: inflow
1068+ real, dimension(face_ngi(U_nl, face)) :: U_nl_q_dotn1, U_nl_q_dotn2, U_nl_q_dotn, income
1069+ ! Variable transform times quadrature weights.
1070+ real, dimension(face_ngi(T,face)) :: detwei
1071+ real, dimension(U_nl%dim, X%dim, face_ngi(T,face)) :: J
1072+ real, dimension(face_ngi(T,face)) :: inner_advection_integral, outer_advection_integral
1073+
1074+ ! Bilinear forms
1075+ real, dimension(face_loc(T,face),face_loc(T,face)) :: nnAdvection_out
1076+ real, dimension(face_loc(T,face),face_loc(T,face_2)) :: nnAdvection_in
1077+
1078+ ! Normal weights
1079+ real :: w1, w2
1080+ integer :: i
1081+
1082+ T_face=face_global_nodes(T, face)
1083+ T_shape=>face_shape(T, face)
1084+
1085+ T_face_2=face_global_nodes(T, face_2)
1086+ T_shape_2=>face_shape(T, face_2)
1087+
1088+ !Get normals
1089+ call get_local_normal(n1, w1, U_nl, local_face_number(U_nl%mesh,face))
1090+ call get_local_normal(n2, w2, U_nl, local_face_number(U_nl%mesh,face_2))
1091+
1092+ if (include_advection) then
1093+
1094+ ! Advecting velocity at quadrature points.
1095+ U_f_q = face_val_at_quad(U_nl, face)
1096+ U_f2_q = face_val_at_quad(U_nl, face_2)
1097+
1098+ u_nl_q_dotn1 = sum(U_f_q*w1*n1,1)
1099+ u_nl_q_dotn2 = -sum(U_f2_q*w2*n2,1)
1100+ U_nl_q_dotn=0.5*(u_nl_q_dotn1+u_nl_q_dotn2)
1101+
1102+ ! Inflow is true if the flow at this gauss point is directed
1103+ ! into this element.
1104+ inflow = u_nl_q_dotn<0.0
1105+ income = merge(1.0,0.0,inflow)
1106+
1107+ !----------------------------------------------------------------------
1108+ ! Construct bilinear forms.
1109+ !----------------------------------------------------------------------
1110+
1111+ ! Calculate outflow boundary integral.
1112+ ! can anyone think of a way of optimising this more to avoid
1113+ ! superfluous operations (i.e. multiplying things by 0 or 1)?
1114+
1115+ ! first the integral around the inside of the element
1116+ ! (this is the flux *out* of the element)
1117+ inner_advection_integral = (1.-income)*u_nl_q_dotn
1118+ nnAdvection_out=shape_shape(T_shape, T_shape, &
1119+ & inner_advection_integral*T_shape%quadrature%weight)
1120+
1121+ ! now the integral around the outside of the element
1122+ ! (this is the flux *in* to the element)
1123+ outer_advection_integral = income*u_nl_q_dotn
1124+ nnAdvection_in=shape_shape(T_shape, T_shape_2, &
1125+ & outer_advection_integral*T_shape%quadrature%weight)
1126+
1127+ end if
1128+
1129+ !----------------------------------------------------------------------
1130+ ! Perform global assembly.
1131+ !----------------------------------------------------------------------
1132+
1133+ ! Insert advection in matrix.
1134+ if (include_advection) then
1135+
1136+ ! Outflow boundary integral.
1137+ call addto(big_M, T_face, T_face,&
1138+ nnAdvection_out)
1139+
1140+ ! Inflow boundary integral.
1141+ call addto(big_M, T_face, T_face_2,&
1142+ nnAdvection_in)
1143+
1144+ if(present(UpwindFlux).and.present(UpwindFluxMatrix)) then
1145+ call construct_upwindflux_interface(upwindflux,upwindfluxmatrix)
1146+ end if
1147+ end if
1148+
1149+ contains
1150+ subroutine construct_upwindflux_interface(upwindflux,upwindfluxmatrix)
1151+ type(scalar_field), intent(in) :: upwindflux
1152+ type(csr_matrix), intent(inout) :: upwindfluxmatrix
1153+ !
1154+ ! Bilinear forms
1155+ real, dimension(face_loc(upwindflux,face),&
1156+ face_loc(T,face)) :: upwindflux_mat_in,upwindflux_mat_out
1157+ type(element_type), pointer :: flux_shape
1158+ integer, dimension(face_loc(upwindflux,face)) :: flux_face
1159+ integer :: sign
1160+
1161+ flux_shape=>face_shape(upwindflux, face)
1162+ flux_face=face_global_nodes(upwindflux, face)
1163+
1164+ upwindflux_mat_in=shape_shape(flux_shape, T_shape, &
1165+ income*u_nl_q_dotn*T_shape%quadrature%weight)
1166+ upwindflux_mat_out=shape_shape(flux_shape, T_shape, &
1167+ (1.0-income)*u_nl_q_dotn*T_shape%quadrature%weight)
1168+
1169+ !Need to adopt a sign convention:
1170+ !If face>face_2
1171+ !We store flux with normal pointing from face into face_2
1172+ !Otherwise
1173+ !We store flux with normal pointing from face_2 into face
1174+ ! Outflow boundary integral.
1175+
1176+ !inflow = u_nl_q_dotn<0.0
1177+ !income = 1 if(inflow) and 0 otherwise
1178+ if(face>face_2) then
1179+ sign = 1
1180+ else
1181+ sign = -1
1182+ end if
1183+
1184+ !Factor of 0.5 is because we visit from both sides
1185+ if(face.ne.face_2) then
1186+ call addto(upwindfluxmatrix, flux_face, T_face_2,&
1187+ 0.5*sign*upwindflux_mat_in)
1188+ end if
1189+ call addto(upwindfluxmatrix, flux_face, T_face,&
1190+ 0.5*sign*upwindflux_mat_out)
1191+
1192+ end subroutine construct_upwindflux_interface
1193+
1194+ end subroutine construct_adv_interface_dg
1195+
1196+ subroutine solve_advection_cg_tracer(Q,D,D_old,Flux,U_nl,QF,state)
1197+ !!< Solve the continuity equation for D*Q with Flux
1198+ !!< d/dt (D*Q) + div(Flux*Q) = diffusion terms.
1199+ !!< Done on the vorticity mesh
1200+ !!< Return a PV flux Q defined on the velocity mesh
1201+ !!< which is the projection of Flux*Q into the velocity space
1202+ !!< Note that Flux and Q contain a factor of dt for convenience.
1203+ type(state_type), intent(inout) :: state
1204+ type(scalar_field), intent(in),target :: D,D_old
1205+ type(scalar_field), intent(inout),target :: Q
1206+ type(vector_field), intent(in) :: Flux,U_nl
1207+ type(vector_field), intent(inout) :: QF
1208+ !
1209+ type(csr_sparsity), pointer :: Q_sparsity
1210+ type(csr_matrix) :: Adv_mat
1211+ type(scalar_field) :: Q_rhs, Qtest1,Qtest2
1212+ type(vector_field), pointer :: X, down
1213+ type(scalar_field), pointer :: Q_old
1214+ integer :: ele
1215+ real :: dt, t_theta, residual
1216+
1217+ ewrite(1,*) ' subroutine solve_advection_cg_tracer('
1218+
1219+ X=>extract_vector_field(state, "Coordinate")
1220+ down=>extract_vector_field(state, "GravityDirection")
1221+ Q_old=>extract_scalar_field(state, "Old"//trim(Q%name))
1222+ call get_option('/timestepping/timestep',dt)
1223+ call get_option('/timestepping/theta',t_theta)
1224+
1225+ !set up matrix and rhs
1226+ Q_sparsity => get_csr_sparsity_firstorder(state, Q%mesh, Q%mesh)
1227+ call allocate(adv_mat, Q_sparsity) ! Add data space to the sparsity
1228+ ! pattern.
1229+ call zero(adv_mat)
1230+ call allocate(Q_rhs,Q%mesh,trim(Q%name)//"RHS")
1231+ call zero(Q_rhs)
1232+ call zero(QF)
1233+
1234+ do ele = 1, ele_count(Q)
1235+ call construct_advection_cg_tracer_ele(Q_rhs,adv_mat,Q,D,D_old,Flux&
1236+ &,X,down,U_nl,dt,t_theta,ele)
1237+ end do
1238+
1239+ ewrite(2,*) 'Q_RHS', maxval(abs(Q_rhs%val))
1240+
1241+ call petsc_solve(Q,adv_mat,Q_rhs)
1242+
1243+ !! Compute the PV flux to pass to velocity equation
1244+ do ele = 1, ele_count(Q)
1245+ call construct_pv_flux_ele(QF,Q,Q_old,D,D_old,Flux,&
1246+ X,down,U_nl,t_theta,ele)
1247+ end do
1248+
1249+ if(have_option('/material_phase::Fluid/scalar_field::PotentialVorticity/&
1250+ &prognostic/debug')) then
1251+ call allocate(Qtest1,Q%mesh,trim(Q%name)//"test1")
1252+ call zero(Qtest1)
1253+ call allocate(Qtest2,Q%mesh,trim(Q%name)//"test2")
1254+ call zero(Qtest2)
1255+ do ele = 1, ele_count(Q)
1256+ call test_pv_flux_ele(Qtest1,Qtest2,QF,Q,Q_old,D,D_old,&
1257+ Flux,X,down,t_theta,ele)
1258+ end do
1259+ ewrite(2,*) 'Error = ', maxval(abs(Qtest2%val)), maxval(abs(Qtest1%val))
1260+ ewrite(2,*) 'Error = ', maxval(abs(Qtest1%val-Qtest2%val)), maxval(Qtest1%val)
1261+ residual = maxval(abs(Qtest1%val-Qtest2%val))/max(1.0&
1262+ &,maxval(abs(Qtest1%val)))
1263+ ewrite(2,*) 'residual from qtest', residual
1264+ assert(residual<1.0e-6)
1265+ ewrite(2,*) 'test passed'
1266+ call deallocate(Qtest1)
1267+ call deallocate(Qtest2)
1268+ end if
1269+
1270+ call deallocate(adv_mat)
1271+ call deallocate(Q_rhs)
1272+
1273+ ewrite(1,*) 'END subroutine solve_advection_cg_tracer('
1274+
1275+ end subroutine solve_advection_cg_tracer
1276+
1277+ subroutine construct_advection_cg_tracer_ele(Q_rhs,adv_mat,Q,D,D_old,Flux,&
1278+ & X,down,U_nl,dt,t_theta,ele)
1279+ type(scalar_field), intent(in) :: D,D_old,Q
1280+ type(scalar_field), intent(inout) :: Q_rhs
1281+ type(csr_matrix), intent(inout) :: Adv_mat
1282+ type(vector_field), intent(in) :: X, Flux, down,U_nl
1283+ integer, intent(in) :: ele
1284+ real, intent(in) :: dt, t_theta
1285+ !
1286+ real, dimension(ele_loc(Q,ele),ele_loc(Q,ele)) :: l_adv_mat,tmp_mat
1287+ real, dimension(ele_loc(Q,ele)) :: l_rhs
1288+ real, dimension(U_nl%dim,ele_ngi(U_nl,ele)) :: U_nl_gi
1289+ real, dimension(X%dim, ele_ngi(U_nl,ele)) :: U_cart_gi
1290+ real, dimension(Flux%dim,ele_ngi(Flux,ele)) :: Flux_gi
1291+ real, dimension(Flux%dim,FLux%dim,ele_ngi(Flux,ele)) :: Grad_Flux_gi,&
1292+ & tensor_gi
1293+ real, dimension(ele_ngi(X,ele)) :: detwei, Q_gi, D_gi, &
1294+ & D_old_gi,div_flux_gi, detwei_l, detJ
1295+ real, dimension(ele_loc(D,ele)) :: D_val
1296+ real, dimension(ele_loc(Q,ele)) :: Q_val
1297+ real, dimension(mesh_dim(X), X%dim, ele_ngi(X,ele)) :: J
1298+ type(element_type), pointer :: Q_shape, Flux_shape
1299+ integer :: loc,dim1,dim2,gi
1300+ real :: tau, alpha, tol, area, h,c_sc,disc_indicator
1301+ real, dimension(ele_ngi(X,ele),ele_ngi(X,ele)) :: Metric, MetricT
1302+ real, dimension(mesh_dim(X),ele_ngi(X,ele)) :: gradQ
1303+ type(element_type), pointer :: D_shape
1304+ !
1305+
1306+ ! Get J and detwei
1307+ call compute_jacobian(ele_val(X,ele), ele_shape(X,ele), detwei&
1308+ &=detwei, J=J, detJ=detJ)
1309+
1310+ D_shape => ele_shape(D,ele)
1311+ Q_shape => ele_shape(Q,ele)
1312+ Flux_shape => ele_shape(Flux,ele)
1313+ D_val = invert_pi_ele(ele_val(D,ele),D_shape,detwei)
1314+ D_gi = matmul(transpose(D_shape%n),D_val)
1315+ D_val = invert_pi_ele(ele_val(D_old,ele),D_shape,detwei)
1316+ D_old_gi = matmul(transpose(D_shape%n),D_val)
1317+ Q_gi = ele_val_at_quad(Q,ele)
1318+ Flux_gi = ele_val_at_quad(Flux,ele)
1319+ U_nl_gi = ele_val_at_quad(U_nl,ele)
1320+ Q_val = ele_val(Q,ele)
1321+
1322+ !Equations
1323+ ! D^{n+1} = D^n + \pi div Flux
1324+ ! If define \int_K\phi\hat{D}/J J dS = \int_K\phi D J dS, then
1325+ ! <\phi,\pi(\hat{D}/J)> = <\phi, D> for all \phi,
1326+ ! => \pi \hat{D} = D
1327+ ! \hat{D}^{n+1}/J = \hat{D}^n/J + div Flux
1328+
1329+ ! So (integrating by parts)
1330+ ! <\gamma, \hat{D}^{n+1}/J> =
1331+ ! <\gamma, D^n/J> - <\nabla\gamma, F>
1332+
1333+ ! and a consistent theta-method for Q is
1334+ ! <\gamma,\hat{D}^{n+1}Q^{n+1}/J> =
1335+ ! <\gamma, \hat{D}^nQ^n/J> -
1336+ ! \theta<\nabla\gamma,FQ^{n+1}>
1337+ ! -(1-\theta)<\nabla\gamma,FQ^n>
1338+
1339+ detwei_l = Q_shape%quadrature%weight
1340+ !Advection terms
1341+ !! <-grad gamma, FQ >
1342+ !! Can be evaluated locally using pullback
1343+ tmp_mat = dshape_dot_vector_shape(&
1344+ Q_shape%dn,Flux_gi,Q_shape,detwei_l)
1345+ l_adv_mat = t_theta*tmp_mat
1346+ l_rhs = -(1-t_theta)*matmul(tmp_mat,Q_val)
1347+
1348+ !Mass terms
1349+ !! <gamma, QD>
1350+ !! Requires transformed integral
1351+ tmp_mat = shape_shape(ele_shape(Q,ele),ele_shape(Q,ele),D_gi*detwei_l)
1352+ l_adv_mat = l_adv_mat + tmp_mat
1353+ tmp_mat = shape_shape(ele_shape(Q,ele),ele_shape(Q,ele),D_old_gi*detwei_l)
1354+ l_rhs = l_rhs + matmul(tmp_mat,Q_val)
1355+
1356+ !Laplacian filter options
1357+ if(have_option(trim(Q%option_path)//'/prognostic/spatial_discretisation/&
1358+ &continuous_galerkin/laplacian_filter')) then
1359+ FLExit('Laplacian filter not coded yet.')
1360+ end if
1361+ !Streamline upwinding options
1362+ if(have_option(trim(Q%option_path)//'/prognostic/spatial_discretisation/&
1363+ &continuous_galerkin/streamline_upwinding')) then
1364+ !! Replace test function \gamma with \gamma + \tau u\cdot\nabla\gamma
1365+ call get_option(trim(Q%option_path)//'/prognostic/spatial_discretisat&
1366+ &ion/continuous_galerkin/streamline_upwinding/alpha',alpha)
1367+ call get_option(trim(Q%option_path)//'/prognostic/spatial_discretisat&
1368+ &ion/continuous_galerkin/streamline_upwinding/tol',tol)
1369+
1370+ !Gradients of fluxes
1371+ Grad_flux_gi = ele_grad_at_quad(Flux,ele,Flux_shape%dn)
1372+ div_flux_gi = 0.
1373+ tensor_gi = 0.
1374+ do dim1 = 1, Flux%dim
1375+ div_flux_gi = div_flux_gi + grad_flux_gi(dim1,dim1,:)
1376+ do dim2 = 1, Flux%dim
1377+ tensor_gi(dim1,dim2,:) = U_nl_gi(dim1,:)*Flux_gi(dim2,:)
1378+ end do
1379+ end do
1380+ !real, dimension(mesh_dim(X), X%dim, ele_ngi(X,ele)) :: J
1381+ do gi = 1, ele_ngi(X,ele)
1382+ U_cart_gi(:,gi) = matmul(transpose(J(:,:,gi)),U_nl_gi(:,gi))/detJ(gi)
1383+ end do
1384+
1385+ if(maxval(sqrt(sum(U_cart_gi**2,1)))<tol) then
1386+ tau = 0.
1387+ else
1388+ area = sum(detwei)
1389+ h = sqrt(4*area/sqrt(3.))
1390+ tau = h*alpha/maxval(sqrt(sum(U_cart_gi**2,1)))
1391+ end if
1392+ !! Mass terms
1393+ !! tau < u . grad gamma, QD>
1394+ !! = tau < grad gamma, u QD>
1395+ !! can do locally because of pullback formula
1396+ tmp_mat = dshape_dot_vector_shape(&
1397+ Q_shape%dn,U_nl_gi,Q_shape,D_gi*detwei_l/detJ)
1398+ l_adv_mat = l_adv_mat + tau*tmp_mat
1399+ tmp_mat = dshape_dot_vector_shape(&
1400+ Q_shape%dn,U_nl_gi,Q_shape,D_old_gi*detwei_l/detJ)
1401+ l_rhs = l_rhs + tau*matmul(tmp_mat,Q_val)
1402+ !! Advection terms
1403+ !! tau < u. grad gamma, div (F Q)>
1404+ !! = tau <grad gamma, u (F.grad Q + Q div F)>
1405+ !! can do locally because of pullback formula
1406+ !! tensor = u outer F (F already contains factor of dt)
1407+ tmp_mat = dshape_dot_vector_shape(&
1408+ Q_shape%dn,U_nl_gi,Q_shape,div_flux_gi*detwei_l/detJ) &
1409+ + dshape_tensor_dshape(&
1410+ Q_shape%dn,tensor_gi,Q_shape%dn,detwei_l/detJ)
1411+ l_adv_mat = l_adv_mat - tau*t_theta*tmp_mat
1412+ l_rhs = l_rhs + tau*(1-t_theta)*matmul(tmp_mat,Q_val)
1413+ end if
1414+ !Discontinuity capturing options
1415+ if(have_option(trim(Q%option_path)//'/prognostic/spatial_discretisation/&
1416+ &continuous_galerkin/discontinuity_capturing')) then
1417+ ! call have_option(&
1418+ ! &trim(Q%option_path)//'/prognostic/spatial_discretisation/&
1419+ ! &continuous_galerkin/discontinuity_capturing/&
1420+ ! &scaling_coefficient',c_sc)
1421+
1422+ ! do gi=1,ele_ngi(Q,ele)
1423+ ! Metric(:,:,gi)=matmul(J(:,:,gi), transpose(J(:,:,gi)))/detJ(gi)
1424+ ! end do
1425+ ! MetricT(1,1,:) = MetricT(2,2,:)
1426+ ! MetricT(2,2,:) = MetricT(1,1,:)
1427+ ! MetricT(1,2,:) =-MetricT(2,1,:)
1428+ ! MetricT(2,1,:) =-MetricT(1,2,:)
1429+
1430+ ! area = sum(detwei)
1431+ ! D_gi = ele_val_at_quad(D_old,ele)
1432+ ! tmp_mat = dshape_tensor_dshape(Q_shape%dn, &
1433+ ! area*D_gi*MetricT, Q_shape%dn,&
1434+ ! Q_shape%quadrature%weight)
1435+ ! l_rhs = l_rhs - dt*c_sc*matmul(tmp_mat,Q_val)
1436+ FLAbort('not ready yet')
1437+ end if
1438+
1439+ call addto(Q_rhs,ele_nodes(Q_rhs,ele),l_rhs)
1440+ call addto(adv_mat,ele_nodes(Q_rhs,ele),ele_nodes(Q_rhs,ele),&
1441+ l_adv_mat)
1442+
1443+ end subroutine construct_advection_cg_tracer_ele
1444+
1445+ subroutine construct_pv_flux_ele(QFlux,Q,Q_old,D,D_old,&
1446+ Flux,X,down,U_nl,t_theta,ele)
1447+ type(scalar_field), intent(in) :: Q,Q_old,D,D_old
1448+ type(vector_field), intent(inout) :: QFlux
1449+ type(vector_field), intent(in) :: X, Flux, Down, U_nl
1450+ integer, intent(in) :: ele
1451+ real, intent(in) :: t_theta
1452+ !
1453+ real, dimension(mesh_dim(X),ele_loc(QFlux,ele)) :: QFlux_perp_rhs
1454+ real, dimension(Flux%dim,ele_ngi(Flux,ele)) :: Flux_gi, Flux_perp_gi,&
1455+ QFlux_gi
1456+ real, dimension(ele_ngi(X,ele)) :: Q_gi,Q_old_gi,D_gi,D_old_gi,Qbar_gi
1457+ real, dimension(ele_loc(D,ele)) :: D_val
1458+ real, dimension(X%dim, ele_ngi(Q, ele)) :: up_gi
1459+ real, dimension(mesh_dim(QFlux),mesh_dim(QFlux),ele_loc(QFlux,ele)&
1460+ &,ele_loc(QFlux,ele)) :: l_u_mat
1461+ real, dimension(mesh_dim(QFlux)*ele_loc(QFlux,ele),mesh_dim(QFlux)&
1462+ &*ele_loc(QFlux,ele)) :: solve_mat
1463+ real, dimension(ele_loc(Q,ele)) :: Q_test1_rhs, Q_test2_rhs
1464+ real, dimension(mesh_dim(Qflux)*ele_loc(QFlux,ele)) :: solve_rhs
1465+ real, dimension(mesh_dim(Qflux),ele_ngi(Qflux,ele)) :: &
1466+ contravariant_velocity_gi, velocity_perp_gi
1467+ type(element_type), pointer :: Q_shape, QFlux_shape, Flux_shape
1468+ integer :: loc, orientation,gi, dim1, dim2
1469+ real, dimension(mesh_dim(X), X%dim, ele_ngi(X,ele)) :: J
1470+ real, dimension(ele_ngi(X,ele)) :: detwei, detJ, detwei_l, div_flux_gi
1471+ real, dimension(mesh_dim(Q),ele_ngi(Q,ele)) :: gradQ_gi
1472+ real, dimension(Flux%dim,FLux%dim,ele_ngi(Flux,ele)) :: Grad_Flux_gi
1473+ real, dimension(U_nl%dim,ele_ngi(U_nl,ele)) :: U_nl_gi
1474+ real, dimension(X%dim,ele_ngi(U_nl,ele)) :: U_cart_gi
1475+ real :: residual, alpha, tol, tau, area, h
1476+ type(element_type), pointer :: D_shape
1477+ !
1478+
1479+ D_shape => ele_shape(D,ele)
1480+
1481+ call compute_jacobian(ele_val(X,ele), ele_shape(X,ele), J, detwei, detJ)
1482+
1483+ Q_shape => ele_shape(Q,ele)
1484+ Flux_shape => ele_shape(Flux,ele)
1485+ QFlux_shape => ele_shape(QFlux,ele)
1486+ Q_gi = ele_val_at_quad(Q,ele)
1487+ Q_old_gi = ele_val_at_quad(Q_old,ele)
1488+ Qbar_gi = t_theta*Q_gi + (1-t_theta)*Q_old_gi
1489+ D_val = invert_pi_ele(ele_val(D,ele),D_shape,detwei)
1490+ D_gi = matmul(transpose(D_shape%n),D_val)
1491+ D_val = invert_pi_ele(ele_val(D_old,ele),D_shape,detwei)
1492+ D_old_gi = matmul(transpose(D_shape%n),D_val)
1493+ Flux_gi = ele_val_at_quad(Flux,ele)
1494+ U_nl_gi = ele_val_at_quad(U_nl,ele)
1495+
1496+ detwei_l = Q_shape%quadrature%weight
1497+ up_gi = -ele_val_at_quad(down,ele)
1498+
1499+ call get_up_gi(X,ele,up_gi,orientation)
1500+
1501+ !Equations
1502+ ! <\nabla \gamma, (-v,u)> = <(\gamma_x, \gamma_y),(-v,u)>
1503+ ! = <(\gamma_y,-\gamma_x),( u,v)>
1504+ ! = <-\nabla^\perp\gamma,(u,v)>
1505+
1506+ ! and a consistent theta-method for Q is
1507+ ! <\gamma, D^{n+1}Q^{n+1}> = <\gamma, D^nQ^n> -
1508+ ! \theta<\nabla\gamma,FQ^{n+1}>
1509+ ! -(1-\theta)<\nabla\gamma,FQ^n>
1510+ ! So vorticity update is
1511+ ! <\gamma, \zeta^{n+1}> = <-\nabla^\perp\gamma,u^{n+1}>
1512+ != <-\nabla^\perp\gamma,u^n>
1513+ ! +\theta<-\nabla^\perp\gamma,(FQ^{n+1})^\perp>
1514+ ! +(1-\theta)<-\nabla^\perp\gamma,(FQ^n)^\perp>
1515+ ! taking w = -\nabla^\perp\gamma,
1516+ ! <w,u^{n+1}>=<w,u^n> + <w,F(\theta Q^{n+1}+(1-\theta)Q^n)^\perp>
1517+ ! <w,u^{n+1}>=<w,u^n> + <w,\bar{Q}^\perp>
1518+ ! We actually store the inner product with test function (FQ_rhs)
1519+ ! (avoids having to do a solve)
1520+
1521+ do gi = 1, ele_ngi(QFlux,ele)
1522+ QFlux_gi(:,gi) = Flux_gi(:,gi)*Qbar_gi(gi)
1523+ end do
1524+
1525+ !! Additional dissipative terms.
1526+ !! They all take the form
1527+ !! <\nabla gamma, G>
1528+ !! Which can be evaluated in local coordinates due to pullback magic
1529+
1530+ !Laplacian filter options
1531+ if(have_option(trim(Q%option_path)//'/prognostic/spatial_discretisation/&
1532+ &continuous_galerkin/laplacian_filter')) then
1533+ FLExit('Laplacian filter not coded yet.')
1534+ end if
1535+ !Streamline upwinding options
1536+ if(have_option(trim(Q%option_path)//'/prognostic/spatial_discretisation/&
1537+ &continuous_galerkin/streamline_upwinding')) then
1538+ !! Replace test function \gamma with \gamma + \tau u\cdot\nabla\gamma
1539+ call get_option(trim(Q%option_path)//'/prognostic/spatial_discretisat&
1540+ &ion/continuous_galerkin/streamline_upwinding/alpha',alpha)
1541+ call get_option(trim(Q%option_path)//'/prognostic/spatial_discretisat&
1542+ &ion/continuous_galerkin/streamline_upwinding/tol',tol)
1543+
1544+ !Gradients of fluxes
1545+ Grad_flux_gi = ele_grad_at_quad(Flux,ele,Flux_shape%dn)
1546+ div_flux_gi = 0.
1547+ do dim1 = 1, Flux%dim
1548+ div_flux_gi = div_flux_gi + grad_flux_gi(dim1,dim1,:)
1549+ end do
1550+ !real, dimension(mesh_dim(X), X%dim, ele_ngi(X,ele)) :: J
1551+ do gi = 1, ele_ngi(X,ele)
1552+ U_cart_gi(:,gi) = matmul(transpose(J(:,:,gi)),U_nl_gi(:,gi))/detJ(gi)
1553+ end do
1554+
1555+ if(maxval(sqrt(sum(U_cart_gi**2,1)))<tol) then
1556+ tau = 0.
1557+ else
1558+ area = sum(detwei)
1559+ h = sqrt(4*area/sqrt(3.))
1560+ tau = h*alpha/maxval(sqrt(sum(U_cart_gi**2,1)))
1561+ end if
1562+ !! Mass terms
1563+ !! tau < u . grad gamma, -(QD)^{n+1}+(QD)^n>
1564+ !! = tau < grad gamma, u(-(QD)^{n+1}+(QD)^n)>
1565+ !! can do locally because of pullback formula
1566+ !! Extra flux is tau u\Delta(QD)
1567+ !! minus sign from definition of vorticity
1568+ !! tau
1569+ do gi = 1, ele_ngi(Q,ele)
1570+ QFLux_gi(:,gi) = QFlux_gi(:,gi) + &
1571+ & tau*U_nl_gi(:,gi)*(Q_gi(gi)*D_gi(gi)-Q_old_gi(gi)&
1572+ &*D_old_gi(gi))/detJ(gi)
1573+
1574+ end do
1575+ !! Advection terms
1576+ !! tau < u. grad gamma, div (F\bar{Q})>
1577+ !! can do locally because of pullback formula
1578+ !! = <grad gamma, tau u (F.grad\bar{Q} + \bar{Q} div F)/det J>_{Ehat}
1579+ !! minus sign because of definition of vorticity
1580+
1581+ GradQ_gi = t_theta*ele_grad_at_quad(Q,ele,Q_shape%dn) + &
1582+ (1-t_theta)*ele_grad_at_quad(Q_old,ele,Q_shape%dn)
1583+
1584+ do gi = 1, ele_ngi(Q,ele)
1585+ QFlux_gi(:,gi) = QFlux_gi(:,gi) - tau*u_nl_gi(:,gi)*(&
1586+ & sum(Flux_gi(:,gi)*gradQ_gi(:,gi)) + &
1587+ & div_flux_gi(gi)*Qbar_gi(gi))/detJ(gi)
1588+ end do
1589+ end if
1590+ !Discontinuity capturing options
1591+ if(have_option(trim(Q%option_path)//'/prognostic/spatial_discretisation/&
1592+ &continuous_galerkin/discontinuity_capturing')) then
1593+ FLExit('Discontinuity capturing not coded yet.')
1594+ end if
1595+
1596+ !! Evaluate
1597+ !! < w, F^\perp > in local coordinates
1598+
1599+ Flux_perp_gi(1,:) = -orientation*QFlux_gi(2,:)
1600+ Flux_perp_gi(2,:) = orientation*QFlux_gi(1,:)
1601+
1602+ QFlux_perp_rhs = shape_vector_rhs(QFlux_shape,Flux_perp_gi,&
1603+ & QFlux_shape%quadrature%weight)
1604+
1605+ call set(QFlux,ele_nodes(QFlux,ele),QFlux_perp_rhs)
1606+
1607+ end subroutine construct_pv_flux_ele
1608+
1609+ subroutine test_pv_flux_ele(Qtest1,Qtest2,QFlux,Q,Q_old,D,D_old,&
1610+ Flux,X,down,t_theta,ele)
1611+ type(scalar_field), intent(in) :: Q,Q_old,D,D_old
1612+ type(scalar_field), intent(inout) :: Qtest1, Qtest2
1613+ type(vector_field), intent(in) :: X, Flux, Down,QFlux
1614+ integer, intent(in) :: ele
1615+ real, intent(in) :: t_theta
1616+ !
1617+ real, dimension(mesh_dim(X),ele_loc(QFlux,ele)) :: QFlux_perp_rhs
1618+ real, dimension(ele_ngi(X,ele)) :: Q_gi,Q_old_gi,D_gi,D_old_gi
1619+ real, dimension(Flux%dim,ele_ngi(Flux,ele)) :: Flux_gi, Flux_perp_gi
1620+ real, dimension(X%dim, ele_ngi(Q, ele)) :: up_gi
1621+ real, dimension(mesh_dim(QFlux), mesh_dim(QFlux), ele_ngi(QFlux,ele)) &
1622+ :: Metric, Metricf
1623+ real, dimension(mesh_dim(QFlux),mesh_dim(QFlux),ele_loc(QFlux,ele)&
1624+ &,ele_loc(QFlux,ele)) :: l_u_mat
1625+ real, dimension(mesh_dim(QFlux)*ele_loc(QFlux,ele),mesh_dim(QFlux)&
1626+ &*ele_loc(QFlux,ele)) :: solve_mat
1627+ real, dimension(ele_loc(Q,ele)) :: Q_test1_rhs, Q_test2_rhs
1628+ real, dimension(mesh_dim(Qflux)*ele_loc(QFlux,ele)) :: solve_rhs
1629+ real, dimension(mesh_dim(Qflux),ele_ngi(Qflux,ele)) :: &
1630+ contravariant_velocity_gi, velocity_perp_gi
1631+ type(element_type), pointer :: Q_shape, QFlux_shape,D_shape
1632+ integer :: loc, orientation,gi, dim1, dim2
1633+ real, dimension(mesh_dim(X), X%dim, ele_ngi(X,ele)) :: J
1634+ real, dimension(ele_ngi(X,ele)) :: detwei, detJ
1635+ real, dimension(X%dim, X%dim, ele_ngi(X,ele)) :: rot
1636+ real, dimension(ele_loc(D,ele)) :: D_val
1637+
1638+ D_shape => ele_shape(D,ele)
1639+ Q_shape => ele_shape(Q,ele)
1640+ QFlux_shape => ele_shape(QFlux,ele)
1641+ Q_gi = ele_val_at_quad(Q,ele)
1642+ Q_old_gi = ele_val_at_quad(Q_old,ele)
1643+
1644+ call compute_jacobian(ele_val(X,ele), ele_shape(X,ele), J, detwei, detJ)
1645+ D_val = invert_pi_ele(ele_val(D,ele),D_shape,detwei)
1646+ D_gi = matmul(transpose(D_shape%n),D_val)
1647+ D_val = invert_pi_ele(ele_val(D_old,ele),D_shape,detwei)
1648+ D_old_gi = matmul(transpose(D_shape%n),D_val)
1649+
1650+ Flux_gi = ele_val_at_quad(Flux,ele)
1651+ Qflux_perp_rhs = ele_val(Qflux,ele)
1652+
1653+ do gi = 1, ele_ngi(QFlux,ele)
1654+ Flux_gi(:,gi) = Flux_gi(:,gi)*(t_theta*Q_gi(gi) + &
1655+ (1-t_theta)*Q_old_gi(gi))
1656+ end do
1657+
1658+ up_gi = -ele_val_at_quad(down,ele)
1659+ call get_up_gi(X,ele,up_gi,orientation)
1660+
1661+ do gi=1, ele_ngi(QFlux,ele)
1662+ rot(1,:,gi)=(/0.,-up_gi(3,gi),up_gi(2,gi)/)
1663+ rot(2,:,gi)=(/up_gi(3,gi),0.,-up_gi(1,gi)/)
1664+ rot(3,:,gi)=(/-up_gi(2,gi),up_gi(1,gi),0./)
1665+ end do
1666+ do gi=1,ele_ngi(QFlux,ele)
1667+ Metric(:,:,gi)=matmul(J(:,:,gi), transpose(J(:,:,gi)))/detJ(gi)
1668+ Metricf(:,:,gi)=matmul(J(:,:,gi), &
1669+ matmul(rot(:,:,gi), transpose(J(:,:,gi))))/detJ(gi)
1670+ end do
1671+
1672+ l_u_mat = shape_shape_tensor(qflux_shape, qflux_shape, &
1673+ qflux_shape%quadrature%weight,Metric)
1674+ solve_mat = 0.
1675+ solve_rhs = 0.
1676+ do dim1 = 1, mesh_dim(qflux)
1677+ do dim2 = 1, mesh_dim(qflux)
1678+ solve_mat( (dim1-1)*ele_loc(qflux,ele)+1:dim1*ele_loc(qflux,ele),&
1679+ (dim2-1)*ele_loc(qflux,ele)+1:dim2*ele_loc(qflux,ele)) = &
1680+ l_u_mat(dim1,dim2,:,:)
1681+ end do
1682+ solve_rhs( (dim1-1)*ele_loc(qflux,ele)+1:dim1*ele_loc(qflux,ele))&
1683+ = QFlux_perp_rhs(dim1,:)
1684+ end do
1685+ call solve(solve_mat,solve_rhs)
1686+ !Don't need to include constraints since u eqn is
1687+ ! <w,Q^\perp> + <<[w],\lambda>> + \sum_i C_i\cdot w \Gamma_i = RHS
1688+ ! but if w=-\nabla^\perp\gamma then [w]=0 and C_i\cdot w=0.
1689+
1690+ do dim1 = 1, mesh_dim(qflux)
1691+ flux_perp_gi(dim1,:) = matmul(transpose(QFlux_shape%n),&
1692+ solve_rhs((dim1-1)*ele_loc(qflux,ele)+1:dim1*ele_loc(qflux&
1693+ &,ele)))
1694+ end do
1695+ !Now compute vorticity
1696+ do gi=1,ele_ngi(X,ele)
1697+ Metric(:,:,gi)=matmul(J(:,:,gi), transpose(J(:,:,gi)))/detJ(gi)
1698+ contravariant_velocity_gi(:,gi) = &
1699+ matmul(Metric(:,:,gi),Flux_perp_gi(:,gi))
1700+ end do
1701+
1702+ !!Q_test1_rhs contains -<\nabla\gamma,Q>
1703+
1704+ select case(mesh_dim(X))
1705+ case (2)
1706+ velocity_perp_gi(1,:) = -contravariant_velocity_gi(2,:)
1707+ velocity_perp_gi(2,:) = contravariant_velocity_gi(1,:)
1708+ Q_test1_rhs = dshape_dot_vector_rhs(Q%mesh%shape%dn, &
1709+ velocity_perp_gi,Q%mesh%shape%quadrature%weight)
1710+ Q_test1_rhs = q_test1_rhs*orientation
1711+ case default
1712+ FLAbort('Exterior derivative not implemented for given mesh dimension')
1713+ end select
1714+
1715+ !!Q_test2_rhs contains <\gamma,Q^{n+1}\tilde{D}^{n+1}-
1716+ !! Q^n\tilde{D}^n>
1717+
1718+ Q_test2_rhs = shape_rhs(ele_shape(Q,ele),(Q_gi*D_gi-Q_old_gi&
1719+ &*D_old_gi)*Q_shape%quadrature%weight)
1720+
1721+ call addto(Qtest1,ele_nodes(Q,ele),Q_test1_rhs)
1722+ call addto(Qtest2,ele_nodes(Q,ele),Q_test2_rhs)
1723+
1724+ end subroutine test_pv_flux_ele
1725+
1726+ subroutine get_PV(state,PV,velocity,D,path)
1727+ type(state_type), intent(inout) :: state
1728+ type(vector_field), intent(in) :: velocity
1729+ type(scalar_field), intent(inout) :: PV
1730+ type(scalar_field), intent(in) :: D
1731+ character(len=OPTION_PATH_LEN), optional :: path
1732+ !
1733+ type(vector_field), pointer :: X, down
1734+ type(scalar_field), pointer :: Coriolis
1735+ type(csr_matrix) :: pv_mass_matrix
1736+ type(csr_sparsity), pointer :: pv_mass_sparsity, curl_sparsity
1737+ type(scalar_field) :: pv_rhs
1738+ integer :: ele
1739+ !!DEbugging
1740+ integer ::dim1
1741+ real, dimension(3,3) :: X_val
1742+
1743+ ewrite(1,*) 'subroutine get_pv'
1744+
1745+ pv_mass_sparsity => &
1746+ get_csr_sparsity_firstorder(state, pv%mesh, pv%mesh)
1747+ call allocate(pv_mass_matrix,pv_mass_sparsity)
1748+ call zero(pv_mass_matrix)
1749+
1750+ X=>extract_vector_field(state, "Coordinate")
1751+ down=>extract_vector_field(state, "GravityDirection")
1752+ Coriolis=>extract_scalar_field(state, "Coriolis")
1753+
1754+ call allocate(pv_rhs, pv%mesh, 'PvRHS')
1755+ call zero(pv_rhs)
1756+
1757+ do ele = 1, ele_count(pv)
1758+ call assemble_pv_ele(pv_mass_matrix,pv_rhs,velocity,D,Coriolis,X&
1759+ &,down,ele)
1760+ end do
1761+
1762+ if(present(path)) then
1763+ call petsc_solve(pv,pv_mass_matrix,pv_rhs,option_path=path)
1764+ else
1765+ call petsc_solve(pv,pv_mass_matrix,pv_rhs)
1766+ end if
1767+
1768+ call deallocate(pv_mass_matrix)
1769+ call deallocate(pv_rhs)
1770+
1771+ end subroutine get_pv
1772+
1773+ subroutine assemble_PV_ele(pv_mass_matrix,pv_rhs,velocity,D,Coriolis,X&
1774+ &,down,ele)
1775+ !!Subroutine to compute the pv from a *local velocity* field
1776+ type(vector_field), intent(in) :: X, down, velocity
1777+ type(scalar_field), intent(inout) :: pv_rhs
1778+ type(scalar_field), intent(in) :: D, Coriolis
1779+ type(csr_matrix), intent(inout) :: pv_mass_matrix
1780+ integer, intent(in) :: ele
1781+ !
1782+ real, dimension(mesh_dim(X), X%dim, ele_ngi(X,ele)) :: J
1783+ real, dimension(ele_ngi(X,ele)) :: detwei, detJ, l_f, l_d
1784+ real, dimension(ele_loc(D,ele)) :: D_val
1785+ real, dimension(ele_loc(pv_rhs,ele),ele_loc(pv_rhs,ele))&
1786+ :: l_mass_mat
1787+ real, dimension(ele_loc(pv_rhs,ele))&
1788+ :: l_rhs
1789+ real, dimension(mesh_dim(velocity),ele_ngi(velocity,ele)) :: velocity_gi,&
1790+ contravariant_velocity_gi, velocity_perp_gi
1791+ real, dimension(X%dim, ele_ngi(X,ele)) :: up_gi
1792+ integer :: orientation, gi
1793+ real, dimension(mesh_dim(X), mesh_dim(X), ele_ngi(X,ele)) :: Metric
1794+ type(element_type), pointer :: D_shape
1795+ !
1796+
1797+ D_shape => ele_shape(D,ele)
1798+
1799+ up_gi = -ele_val_at_quad(down,ele)
1800+ call get_up_gi(X,ele,up_gi,orientation)
1801+
1802+ call compute_jacobian(ele_val(X,ele), ele_shape(X,ele), J, detwei, detJ)
1803+ D_val = ele_val(D,ele)
1804+ D_val = invert_pi_ele(D_val,D_shape,detwei)
1805+ l_d = matmul(transpose(D_shape%n),D_val)
1806+
1807+ l_mass_mat = shape_shape(ele_shape(pv_rhs,ele),&
1808+ ele_shape(pv_rhs,ele),l_d*d_shape%quadrature%weight)
1809+
1810+ velocity_gi = ele_val_at_quad(velocity,ele)
1811+ do gi=1,ele_ngi(X,ele)
1812+ Metric(:,:,gi)=matmul(J(:,:,gi), transpose(J(:,:,gi)))/detJ(gi)
1813+ contravariant_velocity_gi(:,gi) = &
1814+ matmul(Metric(:,:,gi),velocity_gi(:,gi))
1815+ end do
1816+
1817+ !Relative vorticity
1818+
1819+ ! pv = \nabla^\perp\cdot u = v_x - u_y
1820+ ! < \gamma, pv > = <\gamma, v_x - u_y> = <-\gamma_x,v>+<\gamma_y,u>
1821+ ! < -\nabla^\perp \gamma, J^TJ u/detJ> in local coordinates
1822+ ! < \nabla \gamma, (J^TJ u)^\perp/detJ> in local coordinates
1823+ ! requires us to know the orientation of the manifold
1824+
1825+ select case(mesh_dim(X))
1826+ case (2)
1827+ velocity_perp_gi(1,:) = -contravariant_velocity_gi(2,:)
1828+ velocity_perp_gi(2,:) = contravariant_velocity_gi(1,:)
1829+ l_rhs = dshape_dot_vector_rhs(pv_rhs%mesh%shape%dn, &
1830+ velocity_perp_gi,X%mesh%shape%quadrature%weight)
1831+ l_rhs = l_rhs*orientation
1832+ case default
1833+ FLAbort('Exterior derivative not implemented for given mesh dimension')
1834+ end select
1835+
1836+ ! Coriolis term
1837+ l_f = ele_val_at_quad(Coriolis,ele)
1838+ l_rhs = l_rhs + shape_rhs(ele_shape(pv_rhs,ele),l_f*detwei)
1839+
1840+ call addto(pv_mass_matrix,ele_nodes(pv_rhs,ele),&
1841+ ele_nodes(pv_rhs,ele),l_mass_mat)
1842+ call addto(pv_rhs,ele_nodes(pv_rhs,ele),l_rhs)
1843+
1844+ end subroutine assemble_pv_ele
1845+
1846+ function invert_pi_ele(D_val_in,D_shape,detwei) result (D_val_out)
1847+ real, intent(in), dimension(:) :: D_val_in
1848+ type(element_type), intent(in) :: D_shape
1849+ real, intent(in), dimension(:) :: detwei
1850+ real, dimension(size(D_val_in)) :: D_val_out !The result
1851+ !
1852+ real, dimension(size(D_val_in),size(D_val_in)) :: proj_mat, &
1853+ & mass_mat
1854+
1855+ !! \sum_i \phi_i \hat{D}_i w_i = \sum_i \phi_i D_i J_i w_i.
1856+
1857+ proj_mat = shape_shape(D_shape,D_shape,D_shape%quadrature%weight)
1858+ mass_mat = shape_shape(D_shape,D_shape,detwei)
1859+ D_val_out = matmul(mass_mat,D_val_in)
1860+ call solve(proj_mat,D_val_out)
1861+
1862+ end function invert_pi_ele
1863+
1864+ subroutine compute_U_residual(UResidual,oldU,oldD,newU,newD,PVFlux,state)
1865+ !!< Compute the residual in the U equation, given PVFlux computed
1866+ !!< from the PV advection equation. Note that the PVFlux has
1867+ !!< already been perped, and multiplied by test functions.
1868+ !!< Residual will be multiplied by test functions
1869+ type(vector_field), intent(inout) :: UResidual
1870+ type(vector_field), intent(in) :: oldU,newU,PVFlux
1871+ type(scalar_field), intent(in) :: oldD,newD
1872+ type(state_type), intent(inout) :: state
1873+ !
1874+ integer :: ele, stat
1875+ type(vector_field), pointer :: X
1876+ type(scalar_field), pointer :: Orography
1877+ real :: dt, theta,g
1878+ type(scalar_field), target :: l_orography
1879+ logical :: have_orography
1880+
1881+ ewrite(1,*) ' subroutine compute_U_residual('
1882+
1883+ X=>extract_vector_field(state, "Coordinate")
1884+ call get_option("/timestepping/timestep", dt)
1885+ call get_option("/timestepping/theta",theta)
1886+ call get_option("/physical_parameters/gravity/magnitude", g)
1887+
1888+ Orography=>extract_scalar_field(state, "Orography", stat)
1889+ have_orography = (stat==0)
1890+ if(.not.have_orography) then
1891+ call allocate(l_orography,X%mesh,"L_Orography")
1892+ orography => l_orography
1893+ call zero(l_orography)
1894+ end if
1895+
1896+ call zero(UResidual)
1897+ do ele = 1, ele_count(UResidual)
1898+ call compute_U_residual_ele(UResidual,oldU,oldD,newU,newD,PVFlux,&
1899+ orography,X,theta&
1900+ &,dt,g,ele)
1901+ end do
1902+
1903+ if(.not.have_orography) then
1904+ call deallocate(l_orography)
1905+ end if
1906+
1907+ ewrite(1,*) 'END subroutine compute_U_residual('
1908+
1909+ end subroutine compute_U_residual
1910+
1911+ subroutine compute_U_residual_ele(UResidual,oldU,oldD,newU,newD,PVFlux,&
1912+ orography,X,theta&
1913+ &,dt,g,ele)
1914+ type(vector_field), intent(inout) :: UResidual
1915+ type(vector_field), intent(in) :: oldU,newU,PVFlux,X
1916+ type(scalar_field), intent(in) :: oldD,newD,orography
1917+ real, intent(in) :: dt,theta,g
1918+ integer, intent(in) :: ele
1919+ !
1920+ real, dimension(ele_ngi(X,ele)) :: detwei, detJ
1921+ real, dimension(mesh_dim(X), X%dim, ele_ngi(X,ele)) :: J
1922+ real, dimension(mesh_dim(oldU), mesh_dim(oldU)) :: Metric
1923+ real, dimension(mesh_dim(oldU), ele_ngi(X,ele)) :: U_rhs, newU_rhs
1924+ real, dimension(ele_ngi(X,ele)) :: D_gi, newD_gi, D_bar_gi, K_bar_gi
1925+ real, dimension(mesh_dim(X), ele_ngi(X,ele)) :: U_gi,newU_gi
1926+ real, dimension(X%dim, ele_ngi(X,ele)) :: U_cart_gi,newU_cart_gi
1927+ real, dimension(mesh_dim(oldU),ele_loc(UResidual,ele)) :: UR_rhs
1928+ integer :: gi
1929+ type(element_type), pointer :: U_shape
1930+ real, dimension(ele_ngi(X,ele)) :: orography_gi
1931+
1932+ !r[w] = <w,u^{n+1}-u^n> - <w,FQ^\perp>
1933+ ! - dt*<div w, g\bar{h} + \bar{|u|^2/2}>
1934+
1935+ call compute_jacobian(ele_val(X,ele), ele_shape(X,ele), &
1936+ detwei=detwei, J=J, detJ=detJ)
1937+ U_shape=>ele_shape(oldU, ele)
1938+
1939+ !Get all the variables at the quadrature points
1940+ D_gi = ele_val_at_quad(oldD,ele)
1941+ orography_gi = ele_val_at_quad(orography,ele)
1942+ newD_gi = ele_val_at_quad(newD,ele)
1943+ U_gi = ele_val_at_quad(oldU,ele)
1944+ newU_gi = ele_val_at_quad(newU,ele)
1945+ U_rhs = 0.
1946+ newU_rhs = 0.
1947+ do gi=1,ele_ngi(oldU,ele)
1948+ Metric=matmul(J(:,:,gi), transpose(J(:,:,gi)))/detJ(gi)
1949+ U_rhs(:,gi) = matmul(Metric,U_gi(:,gi))
1950+ newU_rhs(:,gi) = matmul(Metric,newU_gi(:,gi))
1951+ end do
1952+ U_cart_gi = 0.
1953+ newU_cart_gi = 0.
1954+ do gi = 1, ele_ngi(oldU,ele)
1955+ U_cart_gi(:,gi) = matmul(transpose(J(:,:,gi)),U_gi(:,gi))/detJ(gi)
1956+ newU_cart_gi(:,gi) = matmul(transpose(J(:,:,gi)),newU_gi(:,gi))/detJ(gi)
1957+ end do
1958+
1959+ !!Residual is
1960+ !! U_new - (U_old + dt*Q^\perp - dt grad(g\bar{D} + \bar{K}))
1961+ !First the PV Flux (perped, contains factor of dt)
1962+ UR_rhs = -ele_val(PVFlux,ele)
1963+ !Now the time derivative term
1964+ UR_rhs = UR_rhs + shape_vector_rhs(U_shape,newU_rhs-U_rhs,&
1965+ U_shape%quadrature%weight)
1966+ !Now the gradient terms (done in local coordinates)
1967+ D_bar_gi = theta*newD_gi + (1-theta)*D_gi + orography_gi
1968+ K_bar_gi = 0.5*(theta*sum(newU_cart_gi**2,1)+(1-theta)*sum(U_cart_gi**2,1))
1969+ !integration by parts, so minus sign
1970+ UR_rhs = UR_rhs - dt * dshape_rhs(U_shape%dn,&
1971+ (g*D_bar_gi + K_bar_gi)*U_shape%quadrature%weight)
1972+
1973+ call set(UResidual,ele_nodes(UResidual,ele),UR_rhs)
1974+
1975+ end subroutine compute_U_residual_ele
1976+
1977+ subroutine solve_vector_advection_dg_subcycle(field_name, state, velocity_name)
1978+ !!< Construct and solve the advection equation for the given
1979+ !!< field using discontinuous elements.
1980+
1981+ !! Name of the field to be solved for.
1982+ character(len=*), intent(in) :: field_name
1983+ !! Collection of fields defining system state.
1984+ type(state_type), intent(inout) :: state
1985+ !! Optional velocity name
1986+ character(len = *), intent(in) :: velocity_name
1987+
1988+ !! Velocity to be solved for.
1989+ type(vector_field), pointer :: U, U_old, U_cartesian
1990+
1991+ !! Coordinate and advecting velocity fields
1992+ type(vector_field), pointer :: X, U_nl, down
1993+
1994+ !! Temporary velocity fields
1995+ type(vector_field) :: U_tmp, U_cartesian_tmp
1996+
1997+ !! Velocity components Cartesian coordinates for slope limiter
1998+ type(scalar_field) :: U_component
1999+
2000+ !! Change in U over one timestep.
2001+ type(vector_field) :: delta_U, delta_U_tmp
2002+
2003+ !! DG Courant number field
2004+ type(scalar_field), pointer :: s_field
2005+
2006+ !! Sparsity of advection matrix.
2007+ type(csr_sparsity), pointer :: sparsity
2008+
2009+ !! System matrices.
2010+ type(block_csr_matrix) :: A, L, mass_local, inv_mass_local, &
2011+ inv_mass_cartesian
2012+
2013+ !! Sparsity of mass matrix.
2014+ type(csr_sparsity) :: mass_sparsity
2015+
2016+ !! Right hand side vector.
2017+ type(vector_field) :: rhs
2018+
2019+ !! Whether to invoke the slope limiter
2020+ logical :: limit_slope
2021+ !! Which limiter to use
2022+ integer :: limiter
2023+
2024+ !! Number of advection subcycles.
2025+ integer :: subcycles
2026+ real :: max_courant_number
2027+
2028+ character(len=FIELD_NAME_LEN) :: limiter_name
2029+ integer :: i, j, dim, ele, dim1
2030+ integer :: upwinding_option
2031+
2032+ if(have_option('/material_phase::Fluid/vector_field::Velocity/prognostic&
2033+ &/spatial_discretisation/discontinuous_galerkin/advection_scheme/ed&
2034+ &ge_coordinates_upwind')) then
2035+ upwinding_option = VECTOR_UPWIND_EDGE
2036+ else
2037+ if(have_option('/material_phase::Fluid/vector_field::Velocity/prognostic&
2038+ &/spatial_discretisation/discontinuous_galerkin/advection_scheme/sphere_coordinates_upwind')) then
2039+ upwinding_option = VECTOR_UPWIND_SPHERE
2040+ else
2041+ FLAbort('Unknown upwinding option')
2042+ end if
2043+ end if
2044+
2045+ U=>extract_vector_field(state, field_name)
2046+ U_old=>extract_vector_field(state, "Old"//field_name)
2047+ X=>extract_vector_field(state, "Coordinate")
2048+ U_cartesian=>extract_vector_field(state, "Velocity")
2049+ down=>extract_vector_field(state, "GravityDirection")
2050+
2051+ dim=mesh_dim(U)
2052+
2053+ ! Reset U to value at the beginning of the timestep.
2054+ call set(U, U_old)
2055+
2056+ sparsity => get_csr_sparsity_firstorder(state, U%mesh, U%mesh)
2057+
2058+ ! Add data space to the sparsity pattern.
2059+ call allocate(A, sparsity, (/dim,X%dim/))
2060+ call zero(A)
2061+ call allocate(L, sparsity, (/dim,X%dim/))
2062+ call zero(L)
2063+
2064+ mass_sparsity=make_sparsity_dg_mass(U%mesh)
2065+ call allocate(mass_local, mass_sparsity, (/dim,dim/))
2066+ call zero(mass_local)
2067+ call allocate(inv_mass_local, mass_sparsity, (/dim,dim/))
2068+ call zero(inv_mass_local)
2069+ call allocate(inv_mass_cartesian, mass_sparsity, (/X%dim,X%dim/))
2070+ call zero(inv_mass_cartesian)
2071+
2072+ ! Ensure delta_U inherits options from U.
2073+ call allocate(delta_U, U%dim, U%mesh, "delta_U")
2074+ call zero(delta_U)
2075+ call allocate(delta_U_tmp, U%dim, U%mesh, "delta_U")
2076+ call zero(delta_U_tmp)
2077+ delta_U%option_path = U_cartesian%option_path
2078+ call allocate(rhs, U%dim, U%mesh, trim(field_name)//" RHS")
2079+ call zero(rhs)
2080+
2081+ ! allocate temp velocity fields
2082+ call allocate(U_tmp, U%dim, U%mesh, 'tmpU')
2083+ call zero(U_tmp)
2084+ call allocate(U_cartesian_tmp, U_cartesian%dim, U_cartesian%mesh, 'tmpUcartesian')
2085+ call zero(U_cartesian_tmp)
2086+
2087+ call construct_vector_advection_dg(A, L, mass_local, &
2088+ inv_mass_local, inv_mass_cartesian,&
2089+ rhs, field_name, state, upwinding_option, down, &
2090+ velocity_name=velocity_name)
2091+
2092+ ! Note that since dt is module global, these lines have to
2093+ ! come after construct_advection_diffusion_dg.
2094+ call get_option("/timestepping/timestep", dt)
2095+
2096+ if(have_option(trim(U_cartesian%option_path)//&
2097+ &"/prognostic/temporal_discretisation/discontinuous_galerkin/&
2098+ &/number_advection_subcycles")) then
2099+ call get_option(trim(U_cartesian%option_path)//&
2100+ &"/prognostic/temporal_discretisation/discontinuous_galerkin/&
2101+ &/number_advection_subcycles", subcycles)
2102+ else
2103+ call get_option(trim(U_cartesian%option_path)//&
2104+ &"/prognostic/temporal_discretisation/discontinuous_galerkin/&
2105+ &/maximum_courant_number_per_subcycle", Max_Courant_number)
2106+
2107+ s_field => extract_scalar_field(state, "DG_CourantNumber")
2108+ call calculate_diagnostic_variable(state, "DG_CourantNumber_Local", &
2109+ & s_field)
2110+ ewrite_minmax(s_field)
2111+
2112+ subcycles = ceiling( maxval(s_field%val)/Max_Courant_number)
2113+ call allmax(subcycles)
2114+ ewrite(2,*) 'Number of subcycles for velocity eqn: ', subcycles
2115+ end if
2116+
2117+ limit_slope=.false.
2118+ if (have_option(trim(U_cartesian%option_path)//&
2119+ "/prognostic/spatial_discretisation/&
2120+ &discontinuous_galerkin/slope_limiter")) then
2121+ limit_slope=.true.
2122+
2123+ ! Note unsafe for mixed element meshes
2124+ if (element_degree(U,1)==0) then
2125+ FLExit("Slope limiters make no sense for degree 0 fields")
2126+ end if
2127+
2128+ end if
2129+
2130+ do i=1, subcycles
2131+ call mult_t(U_cartesian_tmp, L, U)
2132+ call mult(U_cartesian, inv_mass_cartesian, U_cartesian_tmp)
2133+ ! dU = Advection * U
2134+ ! A maps from cartesian to local
2135+ call mult(delta_U_tmp, A, U_cartesian)
2136+ ! dU = dU + RHS
2137+ !------------------------------------------
2138+ !Is there anything in RHS?
2139+ call addto(delta_U_tmp, RHS, -1.0)
2140+ !------------------------------------------
2141+ ! dU = M^(-1) dU
2142+ call mult(delta_U, inv_mass_local, delta_U_tmp)
2143+ !------------------------------------------
2144+ !Do we need the below?
2145+ call mult_t(U_cartesian_tmp, L, delta_U)
2146+ call mult(U_cartesian, inv_mass_cartesian, U_cartesian_tmp)
2147+ !------------------------------------------
2148+
2149+ ! U = U + dt/s * dU
2150+ call addto(U, delta_U, scale=-dt/subcycles)
2151+ call halo_update(U)
2152+
2153+ if (limit_slope) then
2154+
2155+ call mult_t(U_cartesian_tmp, L, U)
2156+ call mult(U_cartesian, inv_mass_cartesian, U_cartesian_tmp)
2157+
2158+ !Really crap limiter
2159+ !Just limit each cartesian component
2160+ do dim1 = 1, U_cartesian%dim
2161+ u_component = extract_scalar_field(U_cartesian, dim1)
2162+ call limit_vb(state, U_component)
2163+ end do
2164+ !limiter=VECTOR_LIMITER_VB
2165+ !call limit_slope_dg(U_cartesian, state, limiter)
2166+
2167+ call mult(U_tmp, L, U_cartesian)
2168+ call mult(U, inv_mass_local, U_tmp)
2169+ end if
2170+
2171+ end do
2172+
2173+ call deallocate(delta_U)
2174+ call deallocate(A)
2175+ call deallocate(L)
2176+ call deallocate(mass_local)
2177+ call deallocate(inv_mass_local)
2178+ call deallocate(inv_mass_cartesian)
2179+ call deallocate(mass_sparsity)
2180+ call deallocate(rhs)
2181+
2182+ end subroutine solve_vector_advection_dg_subcycle
2183+
2184+ subroutine construct_vector_advection_dg(A, L, mass_local, inv_mass_local,&
2185+ & inv_mass_cartesian, rhs, field_name, &
2186+ & state, upwinding_option, down, velocity_name)
2187+ !!< Construct the advection equation for discontinuous elements in
2188+ !!< acceleration form.
2189+ !!<
2190+ !!< If mass is provided then the mass matrix is not added into big_m or
2191+ !!< rhs. It is instead returned as mass. This may be useful for testing
2192+ !!< or for solving equations otherwise than in acceleration form.
2193+
2194+ !! Main advection matrix.
2195+ type(block_csr_matrix), intent(inout) :: A, L
2196+ !! Mass matrices.
2197+ type(block_csr_matrix), intent(inout) :: mass_local, inv_mass_local, &
2198+ inv_mass_cartesian
2199+ !! Right hand side vector.
2200+ type(vector_field), intent(inout) :: rhs
2201+ type(vector_field), intent(in) :: down
2202+ !! Name of the field to be advected.
2203+ character(len=*), intent(in) :: field_name
2204+ !! Collection of fields defining system state.
2205+ type(state_type), intent(inout) :: state
2206+ integer, intent(in) :: upwinding_option
2207+
2208+ !! Optional velocity name
2209+ character(len = *), intent(in), optional :: velocity_name
2210+
2211+ !! Position, velocity and advecting velocity fields.
2212+ type(vector_field) :: X, U, U_nl
2213+
2214+ !! Local velocity name
2215+ character(len = FIELD_NAME_LEN) :: lvelocity_name
2216+
2217+ !! Element index
2218+ integer :: ele
2219+
2220+ !! Status variable for field extraction.
2221+ integer :: stat
2222+
2223+ ewrite(1,*) "Writing advection equation for "&
2224+ &//trim(field_name)
2225+
2226+ ! These names are based on the CGNS SIDS.
2227+ U=extract_vector_field(state, field_name)
2228+ X=extract_vector_field(state, "Coordinate")
2229+
2230+ if(present(velocity_name)) then
2231+ lvelocity_name = velocity_name
2232+ else
2233+ lvelocity_name = "NonlinearVelocity"
2234+ end if
2235+
2236+ U_nl=extract_vector_field(state, lvelocity_name)
2237+ call incref(U_nl)
2238+
2239+ assert(has_faces(X%mesh))
2240+ assert(has_faces(U%mesh))
2241+
2242+ call zero(A)
2243+ call zero(RHS)
2244+ call zero(mass_local)
2245+
2246+ element_loop: do ele=1,element_count(U)
2247+
2248+ call construct_vector_adv_element_dg(ele, A, L,&
2249+ & mass_local, inv_mass_local, inv_mass_cartesian, rhs,&
2250+ & X, U, U_nl, upwinding_option, down)
2251+
2252+ end do element_loop
2253+
2254+ ! Drop any extra field references.
2255+
2256+ call deallocate(U_nl)
2257+
2258+ end subroutine construct_vector_advection_dg
2259+
2260+ subroutine construct_vector_adv_element_dg(ele, A, L,&
2261+ & mass_local, inv_mass_local, inv_mass_cartesian, rhs,&
2262+ & X, U, U_nl, upwinding_option, down)
2263+ !!< Construct the advection_diffusion equation for discontinuous elements in
2264+ !!< acceleration form.
2265+ implicit none
2266+ !! Index of current element
2267+ integer :: ele
2268+ !! Main advection matrix.
2269+ type(block_csr_matrix), intent(inout) :: A
2270+ !! Transformation matrix
2271+ type(block_csr_matrix), intent(inout) :: L
2272+ !! Mass matrices.
2273+ type(block_csr_matrix), intent(inout) :: mass_local, inv_mass_local, &
2274+ inv_mass_cartesian
2275+ !! Right hand side vector.
2276+ type(vector_field), intent(inout) :: rhs
2277+
2278+ !! Position, velocity and advecting velocity.
2279+ type(vector_field), intent(in) :: X, U, U_nl, down
2280+
2281+ !! Upwinding option
2282+ integer, intent(in) :: upwinding_option
2283+
2284+ ! Bilinear forms.
2285+ real, dimension(mesh_dim(U),mesh_dim(U),ele_loc(U,ele),ele_loc(U,ele)) :: local_mass_mat
2286+ real, dimension(mesh_dim(U),X%dim,ele_loc(U,ele),ele_loc(U,ele)) :: l_mat
2287+ real, dimension(ele_loc(U,ele),ele_loc(U,ele)) :: cartesian_mass_mat
2288+ real, dimension(mesh_dim(U),X%dim,ele_loc(U,ele),ele_loc(U,ele)) :: &
2289+ Advection_mat
2290+
2291+ ! Local assembly matrices.
2292+ real, dimension(mesh_dim(U)*ele_loc(U,ele), mesh_dim(U)*ele_loc(U,ele)) :: l_mass, l_mass_cartesian
2293+
2294+ ! Local variables.
2295+
2296+ ! Neighbour element, face and neighbour face.
2297+ integer :: ele_2, face, face_2
2298+ ! Loops over faces.
2299+ integer :: ni
2300+
2301+ ! Transform from local to physical coordinates.
2302+ real, dimension(ele_ngi(X,ele)) :: detwei, detJ
2303+ real, dimension(mesh_dim(U), X%dim, ele_ngi(X,ele)) :: J, J_scaled
2304+ real, dimension(X%dim, mesh_dim(U), ele_ngi(X,ele)) :: pinvJ
2305+ real, dimension(ele_loc(U,ele), ele_ngi(X,ele), X%dim) :: dshape
2306+ real, dimension(mesh_dim(U), mesh_dim(U), ele_ngi(X,ele)) :: G
2307+
2308+ ! Different velocities at quad points.
2309+ real, dimension(U_nl%dim, ele_ngi(U_nl, ele)) :: U_nl_q
2310+ real, dimension(X%dim, ele_ngi(U_nl, ele)) :: U_cartesian_q
2311+ real, dimension(ele_ngi(U_nl, ele)) :: U_nl_div_q
2312+
2313+ ! Node and shape pointers.
2314+ integer, dimension(:), pointer :: U_ele
2315+ type(element_type), pointer :: U_shape, U_nl_shape
2316+ ! Neighbours of this element.
2317+ integer, dimension(:), pointer :: neigh
2318+
2319+ integer :: i, gi, dim, dim1, dim2, nloc, k
2320+
2321+ dim=mesh_dim(U)
2322+
2323+ !----------------------------------------------------------------------
2324+ ! Establish local node lists
2325+ !----------------------------------------------------------------------
2326+
2327+ U_ele=>ele_nodes(U,ele) ! Velocity node numbers
2328+
2329+ !----------------------------------------------------------------------
2330+ ! Establish local shape functions
2331+ !----------------------------------------------------------------------
2332+
2333+ U_shape=>ele_shape(U, ele)
2334+ U_nl_shape=>ele_shape(U_nl, ele)
2335+
2336+ !==========================
2337+ ! Coordinates
2338+ !==========================
2339+
2340+ ! Get J and detJ
2341+ call compute_jacobian(ele_val(X,ele), ele_shape(X,ele), detwei=detwei, J=J, detJ=detJ)
2342+
2343+ !----------------------------------------------------------------------
2344+ ! Construct bilinear forms.
2345+ !----------------------------------------------------------------------
2346+
2347+ ! Element mass matrix.
2348+ ! /
2349+ ! | W G U dV
2350+ ! /
2351+ do gi=1,ele_ngi(X,ele)
2352+ G(:,:,gi)=matmul(J(:,:,gi),transpose(J(:,:,gi)))/detJ(gi)
2353+ J_scaled(:,:,gi)=J(:,:,gi)/detJ(gi)
2354+ end do
2355+
2356+ local_mass_mat=shape_shape_tensor(u_shape, u_shape, &
2357+ u_shape%quadrature%weight, G)
2358+
2359+ cartesian_mass_mat=shape_shape(u_shape, u_shape, detwei)
2360+
2361+ ! Transformation matrix
2362+ l_mat=shape_shape_tensor(u_shape, u_shape, u_shape%quadrature%weight, J)
2363+
2364+ ! Advecting velocity at quadrature points.
2365+ U_nl_q=ele_val_at_quad(U_nl,ele)
2366+ U_nl_div_q=ele_div_at_quad(U_nl, ele, U_shape%dn)
2367+
2368+ ! WE HAVE INTEGRATED BY PARTS TWICE
2369+ ! Element advection matrix
2370+ ! /
2371+ ! | w . div (\bar{U} \tensor u )dV
2372+ ! /
2373+
2374+ ! becomes
2375+
2376+ ! /
2377+ ! | \phi_i (dx/d\xi)^T.\div_L (\bar{U}_L \tensor \phi_j)dV_L
2378+ ! /
2379+ ! underscore U indicates local coordinates
2380+
2381+ ! split into two terms
2382+
2383+ ! /
2384+ ! | \phi_i (dx/d\xi)^T(\div_L \bar{U}_L)\phi_j dV_L
2385+ ! /
2386+
2387+ ! and
2388+
2389+ ! /
2390+ ! | \phi_i (dx/d\xi)^T\bar{U}_L . grad_L \phi_j dV_L
2391+ ! /
2392+
2393+
2394+ U_cartesian_q=0.
2395+ do gi=1,ele_ngi(X,ele)
2396+ U_cartesian_q(:,gi)=matmul(transpose(J(:,:,gi)),U_nl_q(:,gi))
2397+ U_cartesian_q(:,gi)=U_cartesian_q(:,gi)/detJ(gi)
2398+ end do
2399+ Advection_mat = shape_vector_dot_dshape_tensor(U_shape, U_nl_q, U_shape&
2400+ &%dn, J_scaled, U_shape%quadrature%weight)
2401+
2402+ !----------------------------------------------------------------------
2403+ ! Perform global assembly.
2404+ !----------------------------------------------------------------------
2405+
2406+ ! Assemble matrices.
2407+
2408+ ! Return local mass separately.
2409+ do dim1 = 1,dim
2410+ do dim2 = 1,dim
2411+ call addto(mass_local, dim1, dim2, U_ele, U_ele, &
2412+ local_mass_mat(dim1,dim2,:,:))
2413+ end do
2414+ end do
2415+
2416+ ! calculate inverse_mass_local
2417+ nloc=ele_loc(U,ele)
2418+ do dim1 = 1,dim
2419+ do dim2 = 1,dim
2420+ l_mass(nloc*(dim1-1)+1:nloc*dim1, nloc*(dim2-1)+1:nloc*dim2)=&
2421+ local_mass_mat(dim1,dim2,:,:)
2422+ end do
2423+ end do
2424+
2425+ call invert(l_mass)
2426+
2427+ do dim1 = 1,dim
2428+ do dim2 = 1,dim
2429+ call addto(inv_mass_local, dim1, dim2, U_ele, U_ele, &
2430+ l_mass(nloc*(dim1-1)+1:nloc*dim1,nloc*(dim2-1)+1:nloc*dim2))
2431+ end do
2432+ end do
2433+
2434+ ! calculate inverse_mass_cartesian
2435+ call invert(cartesian_mass_mat)
2436+
2437+ do dim1 = 1,X%dim
2438+ call addto(inv_mass_cartesian, dim1, dim1, U_ele, U_ele, cartesian_mass_mat)
2439+ end do
2440+
2441+ ! advection matrix
2442+ do dim1 = 1,dim
2443+ do dim2 = 1,X%dim
2444+ call addto(A, dim1, dim2, U_ele, U_ele, Advection_mat(dim1,dim2,:,:))
2445+ end do
2446+ end do
2447+
2448+ ! transformation matrix
2449+ do dim1 = 1,dim
2450+ do dim2 = 1,X%dim
2451+ call addto(L, dim1, dim2, U_ele, U_ele, l_mat(dim1,dim2,:,:))
2452+ end do
2453+ end do
2454+
2455+ !-------------------------------------------------------------------
2456+ ! Interface integrals
2457+ !-------------------------------------------------------------------
2458+
2459+ neigh=>ele_neigh(U, ele)
2460+
2461+ neighbourloop: do ni=1,size(neigh)
2462+
2463+ !----------------------------------------------------------------------
2464+ ! Find the relevant faces.
2465+ !----------------------------------------------------------------------
2466+
2467+ ! These finding routines are outside the inner loop so as to allow
2468+ ! for local stack variables of the right size in
2469+ ! construct_add_diff_interface_dg.
2470+
2471+ ele_2=neigh(ni)
2472+
2473+ ! Note that although face is calculated on field U, it is in fact
2474+ ! applicable to any field which shares the same mesh topology.
2475+ face=ele_face(U, ele, ele_2)
2476+
2477+ if (ele_2>0) then
2478+ ! Internal faces.
2479+ face_2=ele_face(U, ele_2, ele)
2480+ else
2481+ ! External face.
2482+ face_2=face
2483+ end if
2484+
2485+ call construct_vector_adv_interface_dg(ele, ele_2, face, face_2,&
2486+ & A, rhs, X, U, U_nl, upwinding_option, down)
2487+
2488+ end do neighbourloop
2489+
2490+ end subroutine construct_vector_adv_element_dg
2491+
2492+ subroutine construct_vector_adv_interface_dg(ele, ele_2, face, face_2, &
2493+ & A, rhs, X, U, U_nl, upwinding_option, down)
2494+
2495+ !!< Construct the DG element boundary integrals on the ni-th face of
2496+ !!< element ele.
2497+ implicit none
2498+
2499+ integer, intent(in) :: ele, ele_2, face, face_2
2500+ type(block_csr_matrix), intent(inout) :: A
2501+ type(vector_field), intent(inout) :: rhs
2502+ ! We pass these additional fields to save on state lookups.
2503+ type(vector_field), intent(in) :: X, U, U_nl, down
2504+ integer, intent(in) :: upwinding_option
2505+
2506+ ! Face objects and numberings.
2507+ type(element_type), pointer :: U_shape, U_shape_2
2508+ integer, dimension(face_loc(U,face)) :: U_face, U_face_l
2509+ integer, dimension(face_loc(U,face_2)) :: U_face_2
2510+ integer, dimension(face_loc(U_nl,face)) :: U_nl_face
2511+ integer, dimension(face_loc(U_nl,face_2)) :: U_nl_face_2
2512+
2513+ ! Note that both sides of the face can be assumed to have the same
2514+ ! number of quadrature points.
2515+ real, dimension(U_nl%dim, face_ngi(U_nl, face)) :: n1_l, n2_l, U_nl_q,&
2516+ & U_nl_f_q, U_nl_f2_q
2517+ logical, dimension(face_ngi(U_nl, face)) :: inflow
2518+ real, dimension(face_ngi(U_nl, face)) :: U_nl_q_dotn1_l, U_nl_q_dotn2_l, U_nl_q_dotn_l, income
2519+ real, dimension(X%dim, face_ngi(X,face)) :: n1, n2, t11, t12, t21,t22,&
2520+ & pole_axis
2521+ real, dimension(X%dim, ele_ngi(X,ele)) :: up_gi, up_gi2
2522+ real, dimension(X%dim, face_ngI(X,face)) :: X_quad
2523+ real, dimension(X%dim, X%dim, face_ngi(X,face)) :: Btmp
2524+ real, dimension(mesh_dim(U), X%dim, face_ngi(X,face)) :: B
2525+ ! Variable transform times quadrature weights.
2526+ real, dimension(ele_ngi(X,ele)) :: detwei, detJ
2527+ real, dimension(face_ngi(X,face)) :: detwei_f, detJ_f
2528+ real, dimension(mesh_dim(U), X%dim, ele_ngi(X,ele)) :: J
2529+ real, dimension(mesh_dim(U), X%dim, face_ngi(X,face)) :: J_scaled
2530+ real, dimension(face_ngi(X,face)) :: inner_advection_integral, outer_advection_integral
2531+
2532+ ! Bilinear forms
2533+ real, dimension(mesh_dim(U),X%dim,face_loc(U,face),face_loc(U,face)) :: nnAdvection_out
2534+ real, dimension(mesh_dim(U),X%dim,face_loc(U,face),face_loc(U,face_2)) :: nnAdvection_in
2535+
2536+ ! normal weights
2537+ real :: w1, w2
2538+ integer :: dim, dim1, dim2, gi, i, k
2539+
2540+ dim=mesh_dim(U)
2541+
2542+ U_face=face_global_nodes(U, face)
2543+ U_shape=>face_shape(U, face)
2544+ X_quad = face_val_at_quad(X, face)
2545+
2546+ U_face_2=face_global_nodes(U, face_2)
2547+ U_shape_2=>face_shape(U, face_2)
2548+
2549+ !Unambiguously calculate the normal using the face with the higher
2550+ !face number. This is so that the normal is identical on both sides.
2551+
2552+ call get_local_normal(n1_l, w1, U, local_face_number(U%mesh,face))
2553+ call get_local_normal(n2_l, w2, U, local_face_number(U%mesh,face_2))
2554+
2555+ !----------------------------------------------------------------------
2556+ ! Construct element-wise quantities.
2557+ !----------------------------------------------------------------------
2558+
2559+ ! Advecting velocity at quadrature points.
2560+ U_nl_f_q = face_val_at_quad(U_nl, face)
2561+ U_nl_f2_q = face_val_at_quad(U_nl, face_2)
2562+
2563+ u_nl_q_dotn1_l = sum(U_nl_f_q*w1*n1_l,1)
2564+ u_nl_q_dotn2_l = -sum(U_nl_f2_q*w2*n2_l,1)
2565+ U_nl_q_dotn_l=0.5*(u_nl_q_dotn1_l+u_nl_q_dotn2_l)
2566+
2567+ ! Inflow is true if the flow at this gauss point is directed
2568+ ! into this element.
2569+ inflow = u_nl_q_dotn_l<0.0
2570+ income = merge(1.0,0.0,inflow)
2571+
2572+ ! Calculate tensor on face
2573+ if(ele_loc(X,ele).ne.3) then
2574+ ewrite(1,*) 'Nloc=',ele_loc(X,ele)
2575+ FLAbort('Hard coded for linear elements at the moment')
2576+ end if
2577+ call compute_jacobian(ele_val(X,ele), ele_shape(X,ele), detwei=detwei, J=J, detJ=detJ)
2578+ forall(gi=1:face_ngi(X,face))
2579+ J_scaled(:,:,gi)=J(:,:,1)/detJ(1)
2580+ end forall
2581+
2582+ select case(upwinding_option)
2583+ case (VECTOR_UPWIND_EDGE)
2584+ ! Get outward pointing normals in physical space for face1
2585+ ! inward pointing normals in physical space for face2
2586+ n1=get_face_normal_manifold(X, ele, face)
2587+ !needs checking
2588+ if(ele_2<0) then
2589+ ! external boundary
2590+ n2=n1
2591+ else
2592+ n2=-get_face_normal_manifold(X, ele_2, face_2)
2593+ end if
2594+ ! Form 'bending' tensor
2595+ ! This rotates the normal to face 2 into -normal from face 1
2596+ ! u_b = t(t.u_b) + n_b(n_b.u_b)
2597+ ! u_a = t(t.u_b) + n_a(n_b.u_b)
2598+ ! = u_b - n_b(n_b.u_b) + n_a(n_b.u_b)
2599+ ! = (I + (n_a-n_b)n_b^T)u_b == B u_b
2600+ forall(gi=1:face_ngi(X,face))
2601+ Btmp(:,:,gi)=outer_product(n1(:,gi)-n2(:,gi),n2(:,gi))
2602+ forall(i=1:size(Btmp,1)) Btmp(i,i,gi)=Btmp(i,i,gi)+1.0
2603+ B(:,:,gi)=matmul(J_scaled(:,:,gi),Btmp(:,:,gi))
2604+ end forall
2605+ case (VECTOR_UPWIND_SPHERE)
2606+ if(mesh_dim(X).ne.2) then
2607+ FLExit('Assumes 2d surface. Surface of the sphere, in fact.')
2608+ end if
2609+ !Get element normal
2610+ up_gi = -ele_val_at_quad(down,ele)
2611+ call get_up_gi(X,ele,up_gi)
2612+ up_gi2 = -ele_val_at_quad(down,ele_2)
2613+ call get_up_gi(X,ele_2,up_gi2)
2614+ ! Form 'bending' tensor
2615+ ! Coordinate system is:
2616+ ! t1: normal to local normal and (0,0,1)
2617+ ! t2: normal to t1 and local normal
2618+ ! u2 = t21(t21.u2) + t22(t22.u2)
2619+ ! u1 = t11(t21.u2) + t12(t22.u2)
2620+ ! = (t11 t21^T + t12 t22^T)u2
2621+
2622+ pole_axis = 0.
2623+ pole_axis(3,:) = 1.
2624+ btmp = 0.
2625+ do gi = 1, face_ngi(X,face)
2626+ t11(:,gi) = cross_product(pole_axis(:,gi),up_gi(:,1))
2627+ t11(:,gi) = t11(:,gi)/sqrt(sum(t11(:,gi)**2))
2628+ t12(:,gi) = cross_product(up_gi(:,1),t11(:,gi))
2629+ t21(:,gi) = cross_product(pole_axis(:,gi),up_gi2(:,1))
2630+ t21(:,gi) = t21(:,gi)/sqrt(sum(t21(:,gi)**2))
2631+ t22(:,gi) = cross_product(up_gi2(:,1),t21(:,gi))
2632+
2633+ forall(i=1:3,k=1:3)
2634+ Btmp(i,k,gi) = t11(i,gi)*t21(k,gi) + t12(i,gi)*t22(k,gi)
2635+ end forall
2636+ B(:,:,gi)=matmul(J_scaled(:,:,gi),Btmp(:,:,gi))
2637+ end do
2638+ case default
2639+ FLAbort('Unknown vector upwinding option')
2640+ end select
2641+
2642+ !----------------------------------------------------------------------
2643+ ! Construct bilinear forms.
2644+ !----------------------------------------------------------------------
2645+
2646+ ! Calculate outflow boundary integral.
2647+ ! can anyone think of a way of optimising this more to avoid
2648+ ! superfluous operations (i.e. multiplying things by 0 or 1)?
2649+
2650+ ! first the integral around the inside of the element
2651+ ! (this is the flux *out* of the element)
2652+ inner_advection_integral = -income*u_nl_q_dotn_l
2653+ nnAdvection_out=shape_shape_tensor(U_shape, U_shape, &
2654+ &inner_advection_integral*U_shape%quadrature%weight, J_scaled)
2655+
2656+ ! now the integral around the outside of the element
2657+ ! (this is the flux *in* to the element)
2658+ outer_advection_integral = income*u_nl_q_dotn_l
2659+ nnAdvection_in=shape_shape_tensor(U_shape, U_shape_2, &
2660+ &outer_advection_integral*U_shape%quadrature%weight, B)
2661+
2662+ !----------------------------------------------------------------------
2663+ ! Perform global assembly.
2664+ !----------------------------------------------------------------------
2665+
2666+ ! Insert advection in matrix.
2667+
2668+ ! Outflow boundary integral.
2669+ do dim1 = 1, dim
2670+ do dim2 = 1, X%dim
2671+ call addto(A, dim1, dim2, U_face, U_face, nnAdvection_out(dim1,dim2,:,:))
2672+ end do
2673+ end do
2674+
2675+ ! Inflow boundary integral.
2676+ do dim1 = 1, dim
2677+ do dim2 = 1, X%dim
2678+ call addto(A, dim1, dim2, U_face, U_face_2, nnAdvection_in(dim1,dim2,:,:))
2679+ end do
2680+ end do
2681+
2682+ contains
2683+
2684+ ! copy of outer_product in vector tools, but now as function
2685+ pure function outer_product(x, y) result (M)
2686+ !!< Give two column vectors x, y
2687+ !!< compute the matrix xy*
2688+
2689+ real, dimension(:), intent(in) :: x, y
2690+ real, dimension(size(x), size(y)) :: M
2691+ integer :: i, j
2692+
2693+ forall (i=1:size(x))
2694+ forall (j=1:size(y))
2695+ M(i, j) = x(i) * y(j)
2696+ end forall
2697+ end forall
2698+
2699+ end function outer_product
2700+
2701+ end subroutine construct_vector_adv_interface_dg
2702+
2703+end module advection_local_DG
2704
2705=== added file 'assemble/Bubble_tools.F90'
2706--- assemble/Bubble_tools.F90 1970-01-01 00:00:00 +0000
2707+++ assemble/Bubble_tools.F90 2012-10-23 15:04:25 +0000
2708@@ -0,0 +1,485 @@
2709+! Copyright (C) 2009 Imperial College London and others.
2710+!
2711+! Please see the AUTHORS file in the main source directory for a full list
2712+! of copyright holders.
2713+!
2714+! Prof. C Pain
2715+! Applied Modelling and Computation Group
2716+! Department of Earth Science and Engineering
2717+! Imperial College London
2718+!
2719+! amcgsoftware@imperial.ac.uk
2720+!
2721+! This library is free software; you can redistribute it and/or
2722+! modify it under the terms of the GNU Lesser General Public
2723+! License as published by the Free Software Foundation,
2724+! version 2.1 of the License.
2725+!
2726+! This library is distributed in the hope that it will be useful,
2727+! but WITHOUT ANY WARRANTY; without even the implied warranty of
2728+! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2729+! Lesser General Public License for more details.
2730+!
2731+! You should have received a copy of the GNU Lesser General Public
2732+! License along with this library; if not, write to the Free Software
2733+! Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
2734+! USA
2735+#include "fdebug.h"
2736+
2737+ module bubble_tools
2738+ use fields
2739+ use field_options
2740+ use fields_allocates
2741+ use vector_tools
2742+ use sparse_matrices_fields
2743+ use solvers
2744+ use quadrature
2745+ use sparsity_patterns_meshes
2746+ use element_numbering
2747+ use state_module
2748+ use shape_functions
2749+ use quadrature
2750+ use shape_functions
2751+ use fldebug_parameters
2752+ use ieee_arithmetic, only: ieee_quiet_nan, ieee_value
2753+ use spud
2754+ use vtk_interfaces
2755+ use adjacency_lists
2756+ use reference_counting
2757+ implicit none
2758+
2759+ private
2760+ public get_lumped_mass_p2b,&
2761+ & project_to_p2b_lumped, get_p2b_lumped_mass_quadrature, &
2762+ & bubble_field_to_vtk
2763+
2764+#include "../femtools/Reference_count_interface_mesh_type.F90"
2765+
2766+ contains
2767+
2768+ !! This is special cased because it is only used for mass lumping
2769+ subroutine get_p2b_lumped_mass_quadrature(quad)
2770+ type(quadrature_type), intent(inout) :: quad
2771+ !
2772+ real :: wv = 1.0/20., we = 2.0/15., wg = 9./20.
2773+
2774+ call allocate(quad, 3, 7, 3)
2775+ quad%dim = 2
2776+ quad%degree = 3
2777+ quad%weight = 0.5*(/wv,we,wv,we,we,wv,wg/)
2778+ quad%l(:,1) = (/1.0,0.5,0.0,0.5,0.0,0.,1./3./)
2779+ quad%l(:,2) = (/0.0,0.5,1.0,0.0,0.5,0.,1./3./)
2780+ quad%l(:,3) = (/0.0,0.0,0.0,0.5,0.5,1.,1./3./)
2781+ quad%family = 3!FAMILY_SIMPSONS
2782+
2783+ end subroutine get_p2b_lumped_mass_quadrature
2784+
2785+ !! This is special cased because of the special properties of the
2786+ !! p2b lumped mass (it is still 3rd order, and positive).
2787+ subroutine get_lumped_mass_p2b(state,mass,p2b_field)
2788+ type(csr_matrix), intent(inout) :: mass
2789+ type(state_type), intent(inout) :: state
2790+ type(scalar_field), intent(in) :: p2b_field
2791+ !
2792+ integer :: ele
2793+ type(vector_field), pointer :: X
2794+ real, dimension(7) :: N_vals
2795+ type(quadrature_type) :: Simpsons_quad
2796+ type(element_type) :: p2b_lumped_shape, X_lumped_shape
2797+
2798+ X=>extract_vector_field(state, "Coordinate")
2799+
2800+ if(p2b_field%mesh%shape%dim.ne.2) then
2801+ FLAbort('Only works for 2d meshes')
2802+ end if
2803+ if(p2b_field%mesh%shape%loc.ne.7) then
2804+ FLAbort('Expected p2 bubble mesh')
2805+ end if
2806+
2807+ call get_p2b_lumped_mass_quadrature(Simpsons_quad)
2808+ p2b_lumped_shape = make_element_shape_from_element(p2b_field%mesh%shape, &
2809+ quad=Simpsons_quad)
2810+ X_lumped_shape = make_element_shape_from_element(X%mesh%shape, &
2811+ quad=Simpsons_quad)
2812+
2813+ call zero(mass)
2814+
2815+ do ele = 1, ele_count(X)
2816+ call get_lumped_mass_p2b_ele(mass,p2b_field,p2b_lumped_shape,&
2817+ X,X_lumped_shape,&
2818+ ele)
2819+ end do
2820+
2821+ call deallocate(p2b_lumped_shape)
2822+ call deallocate(X_lumped_shape)
2823+ call deallocate(Simpsons_quad)
2824+
2825+ end subroutine get_lumped_mass_p2b
2826+
2827+ subroutine get_lumped_mass_p2b_ele(mass,p2b_field,p2b_lumped_shape,&
2828+ X,X_lumped_shape,ele)
2829+ type(csr_matrix), intent(inout) :: mass
2830+ type(scalar_field), intent(in) :: p2b_field
2831+ type(vector_field), intent(in) :: X
2832+ type(element_type), intent(in) :: p2b_lumped_shape,X_lumped_shape
2833+ integer, intent(in) :: ele
2834+ !
2835+ real, dimension(mesh_dim(X), X%dim, ele_ngi(X,ele)) :: J
2836+ real, dimension(ele_ngi(X,ele)) :: detwei
2837+ real, dimension(X_lumped_shape%ngi) :: detwei_l
2838+ real, dimension(ele_loc(p2b_field,ele),ele_loc(p2b_field,ele))&
2839+ :: l_mass_mat
2840+ real :: Area
2841+ integer :: loc
2842+ real, dimension(p2b_lumped_shape%ngi) :: weight_vals
2843+ call compute_jacobian(ele_val(X,ele), ele_shape(X,ele), J, detwei)
2844+ Area = sum(detwei)
2845+ call compute_jacobian(ele_val(X,ele), X_lumped_shape, J, detwei_l)
2846+ assert(abs(Area-sum(detwei_l))<1.0e-10)
2847+ l_mass_mat = shape_shape(p2b_lumped_shape,p2b_lumped_shape,&
2848+ detwei_l)
2849+ call addto(mass,ele_nodes(p2b_field,ele),&
2850+ ele_nodes(p2b_field,ele),l_mass_mat)
2851+
2852+ end subroutine get_lumped_mass_p2b_ele
2853+
2854+ !! This is special cased because of the special properties of the
2855+ !! p2b lumped mass (it is still 3rd order, and positivity preserving)
2856+ subroutine project_to_p2b_lumped(state,field,field_projected)
2857+ type(state_type), intent(inout) :: state
2858+ type(scalar_field), intent(in) :: field
2859+ type(scalar_field), intent(inout) :: field_projected
2860+ !
2861+ type(scalar_field) :: rhs
2862+ type(vector_field), pointer :: X
2863+ type(csr_sparsity), pointer :: mass_sparsity
2864+ type(csr_matrix) :: lumped_mass
2865+ integer :: ele
2866+ type(quadrature_type) :: Simpsons_quad
2867+ type(element_type) :: X_lumped_shape,&
2868+ & field_projected_lumped_shape, field_lumped_shape
2869+
2870+ if(field_projected%mesh%shape%dim.ne.2) then
2871+ FLAbort('Only works for 2d meshes')
2872+ end if
2873+ if(field_projected%mesh%shape%loc.ne.7) then
2874+ FLAbort('Expected p2 bubble mesh')
2875+ end if
2876+ X=>extract_vector_field(state, "Coordinate")
2877+
2878+ call get_p2b_lumped_mass_quadrature(Simpsons_quad)
2879+ field_projected_lumped_shape = make_element_shape_from_element( &
2880+ field_projected%mesh%shape,quad=Simpsons_quad)
2881+ field_lumped_shape = make_element_shape_from_element( &
2882+ field%mesh%shape,quad=Simpsons_quad)
2883+ X_lumped_shape = make_element_shape_from_element(X%mesh%shape, &
2884+ quad=Simpsons_quad)
2885+
2886+ mass_sparsity => get_csr_sparsity_firstorder(state, &
2887+ field_projected%mesh, field_projected%mesh)
2888+ call allocate(lumped_mass,mass_sparsity)
2889+ call zero(lumped_mass)
2890+ call allocate(rhs,field_projected%mesh,"ProjectionRHS")
2891+ call zero(rhs)
2892+
2893+ do ele = 1, ele_count(X)
2894+ call project_to_p2b_lumped_ele(rhs,lumped_mass,&
2895+ X,X_lumped_shape,field_lumped_shape,&
2896+ field_projected_lumped_shape,field,ele)
2897+ end do
2898+ call petsc_solve(field_projected,lumped_mass,rhs)
2899+ ewrite (2,*) maxval(abs(field_projected%val))
2900+ call deallocate(rhs)
2901+ call deallocate(lumped_mass)
2902+ end subroutine project_to_p2b_lumped
2903+
2904+ subroutine project_to_p2b_lumped_ele(rhs,lumped_mass,&
2905+ X,X_lumped_shape,field_lumped_shape,&
2906+ field_projected_lumped_shape,field,ele)
2907+ type(vector_field), intent(in) :: X
2908+ type(scalar_field), intent(in) :: field
2909+ type(scalar_field), intent(inout) :: rhs
2910+ type(csr_matrix), intent(inout) :: lumped_mass
2911+ integer, intent(in) :: ele
2912+ type(element_type), intent(in) :: X_lumped_shape,&
2913+ field_projected_lumped_shape, field_lumped_shape
2914+ !
2915+ real, dimension(ele_loc(rhs,ele)) :: l_rhs,field_gi
2916+ real, dimension(ele_loc(rhs,ele),ele_loc(rhs,ele)) :: &
2917+ & l_mass
2918+ real, dimension(ele_loc(field,ele)) :: field_vals
2919+ integer :: loc
2920+ real, dimension(mesh_dim(X), X%dim, ele_ngi(X,ele)) :: J
2921+ real, dimension(X_lumped_shape%ngi) :: detwei
2922+
2923+ call compute_jacobian(ele_val(X,ele), X_lumped_shape, J, detwei)
2924+
2925+ !Values of field at field DOFs
2926+ field_vals = ele_val(field,ele)
2927+ assert(size(field_gi)==size(detwei))
2928+ field_gi = matmul(transpose(field_lumped_shape%n),field_vals)
2929+ l_rhs = shape_rhs(field_projected_lumped_shape,&
2930+ field_gi*detwei)
2931+ l_mass = shape_shape(field_projected_lumped_shape,&
2932+ field_projected_lumped_shape,detwei)
2933+ call addto(rhs,ele_nodes(rhs,ele),l_rhs)
2934+ call addto(lumped_mass,ele_nodes(rhs,ele),&
2935+ ele_nodes(rhs,ele),l_mass)
2936+ end subroutine project_to_p2b_lumped_ele
2937+
2938+ subroutine bubble_field_to_vtk(state,sfields,filename,dump_num)
2939+ type(state_type), intent(inout) :: state
2940+ type(scalar_field), dimension(:), intent(in) :: sfields
2941+ character(len=*), intent(in) :: filename ! Base filename
2942+ integer, intent(in) :: dump_num
2943+ !
2944+ type(mesh_type), pointer :: lQuadMesh
2945+ type(mesh_type), target :: QuadMesh
2946+ type(vector_field) :: QuadX
2947+ type(scalar_field), dimension(size(sfields)) :: QuadSF
2948+ type(vector_field), pointer :: X
2949+ integer :: stat, i
2950+ type(mesh_type), pointer :: vertex_mesh
2951+
2952+ ewrite(1,*) 'subroutine bubble_field_to_vtk(state,sfield,filename,dump_num)'
2953+ do i = 1, size(sfields)
2954+ if(sfields(i)%mesh%shape%numbering%type.ne.ELEMENT_BUBBLE) then
2955+ FLExit('Only works for bubble functions.')
2956+ end if
2957+ end do
2958+ X => extract_vector_field(state,"Coordinate")
2959+ call find_linear_parent_mesh(state, X%mesh, vertex_mesh)
2960+
2961+ !Construct a quadrilateral mesh from the triangular one.
2962+ lQuadMesh => extract_mesh(state,"QuadMesh",stat)
2963+ if(stat/=0) then
2964+ call quadmesh_from_trimesh(QuadMesh,vertex_mesh,state)
2965+ lQuadMesh => QuadMesh
2966+ end if
2967+ !Construct a coordinate field on the quadrilateral mesh
2968+ call allocate(QuadX,X%dim,lQuadMesh,"QuadCoordinate")
2969+ call zero(QuadX)
2970+ call quadX_from_bubbleX(QuadX,X)
2971+ !Remap the bubble field to Q1 on the quad mesh
2972+ do i = 1, size(sfields)
2973+ call allocate(QuadSF(i),lQuadMesh,"Quad"//trim(sfields(i)%name))
2974+ call quadSF_from_bubbleSF(QuadSF(i),sfields(i))
2975+ end do
2976+ call vtk_write_fields(filename, dump_num, quadX, lQuadMesh, &
2977+ QuadSF)
2978+ call deallocate(QuadX)
2979+ do i = 1, size(sfields)
2980+ call deallocate(QuadSF(i))
2981+ end do
2982+
2983+ ewrite(1,*) 'END subroutine bubble_field_to_vtk(state,sfield,filename,dump_num)'
2984+ contains
2985+
2986+ subroutine quadmesh_from_trimesh(QuadMesh,Xmesh,state)
2987+ type(mesh_type), intent(inout) :: QuadMesh
2988+ type(mesh_type), intent(in) :: Xmesh
2989+ type(state_type), intent(inout) :: state
2990+ !
2991+ type(csr_sparsity) :: NNList, EEList
2992+ type(csr_matrix) :: edge_matrix
2993+ type(quadrature_type) :: quad
2994+ integer :: tri_edges, tri_nodes, tri_elements
2995+ integer :: ele, ni, nodecount, node, qele, qele_loc,ele2
2996+ integer, pointer, dimension(:) :: neigh, X_ele
2997+
2998+ tri_elements = Xmesh%elements
2999+ quadmesh%elements = 3*tri_elements
3000+ allocate(quadmesh%ndglno(quadmesh%elements*4))
3001+ quadmesh%ndglno=-666
3002+ call MakeLists_Mesh(Xmesh,NNList=NNlist,EEList=EEList)
3003+ call allocate(edge_matrix,EEList,type=CSR_INTEGER)
3004+ call zero(edge_matrix)
3005+ tri_edges=size(NNList%colm)/2
3006+ tri_nodes=Xmesh%nodes
3007+ quadmesh%nodes = 3*tri_elements+tri_edges+Xmesh%nodes
3008+
3009+ nodecount = Xmesh%nodes
3010+ do ele = 1, ele_count(Xmesh)
3011+ !! Element numbering in the three quads
3012+ !! ele_q = 3*(ele-1)+vnode where vnode is the local
3013+ !! node number of the node at the vertex
3014+ !!Local numbering in the three quads
3015+ !!triangle vertex node first, then edge node
3016+ !!on edge joining to vertex with next number in modulo arithmetic
3017+ !!then triangle centre, then other edge
3018+
3019+ !First do the vertex nodes (same numbering as Xmesh)
3020+ X_ele => ele_nodes(Xmesh,ele)
3021+ assert(size(X_ele)==3)
3022+ do qele_loc = 1,3
3023+ qele = 3*(ele-1)+qele_loc
3024+ quadmesh%ndglno(4*(qele-1)+1) = X_ele(qele_loc)
3025+ end do
3026+
3027+ !Then the edge nodes (only do if ele<ele2 or ele2<0)
3028+ neigh => ele_neigh(Xmesh,ele)
3029+ assert(size(neigh)==3)
3030+ do ni = 1, size(neigh)
3031+ ele2 = neigh(ni)
3032+ if(ele2.le.0 .or. ele<ele2) then
3033+ nodecount = nodecount + 1
3034+ node = nodecount
3035+ if(ele2>0) then
3036+ call set(edge_matrix,ele,ele2,node)
3037+ call set(edge_matrix,ele2,ele,node)
3038+ end if
3039+ else
3040+ node = val(edge_matrix,ele,ele2)
3041+ end if
3042+ assert(node>0)
3043+ select case(ni)
3044+ case (1)
3045+ qele = 3*(ele-1)+2
3046+ quadmesh%ndglno(4*(qele-1)+2)=node
3047+ qele = 3*(ele-1)+3
3048+ quadmesh%ndglno(4*(qele-1)+3)=node
3049+ case (2)
3050+ qele = 3*(ele-1)+3
3051+ quadmesh%ndglno(4*(qele-1)+2)=node
3052+ qele = 3*(ele-1)+1
3053+ quadmesh%ndglno(4*(qele-1)+3)=node
3054+ case (3)
3055+ qele = 3*(ele-1)+1
3056+ quadmesh%ndglno(4*(qele-1)+2)=node
3057+ qele = 3*(ele-1)+2
3058+ quadmesh%ndglno(4*(qele-1)+3)=node
3059+ end select
3060+ end do
3061+
3062+ !Then the triangle centre nodes
3063+ do qele = 3*(ele-1)+1,3*(ele-1)+3
3064+ nodecount = nodecount + 1
3065+ quadmesh%ndglno(4*(qele-1)+4)=nodecount
3066+ end do
3067+ end do
3068+ assert(all(quadmesh%ndglno>0))
3069+ assert(nodecount==quadmesh%nodes)
3070+
3071+ quadmesh%wrapped=.false.
3072+ quad = make_quadrature(4,2,degree=Xmesh%shape%quadrature%degree,&
3073+ family=Xmesh%shape%quadrature%family)
3074+ quadmesh%shape = make_element_shape(4,2,1,quad)
3075+ quadmesh%name = "QuadMesh"
3076+ quadmesh%continuity=0
3077+ quadmesh%periodic=.false.
3078+
3079+ allocate(quadmesh%adj_lists)
3080+ nullify(quadmesh%region_ids)
3081+ nullify(quadmesh%subdomain_mesh)
3082+ nullify(quadmesh%refcount) ! Hack for gfortran component initialisation
3083+ ! bug.
3084+
3085+ call addref(quadmesh)
3086+
3087+ !need to insert into state and deallocate
3088+ call insert(state,quadmesh,"QuadMesh")
3089+ call deallocate(quadmesh)
3090+ call deallocate(EEList)
3091+ call deallocate(NNList)
3092+ end subroutine quadmesh_from_trimesh
3093+
3094+ subroutine quadX_from_bubbleX(QuadX,X)
3095+ type(vector_field), intent(inout) :: QuadX
3096+ type(vector_field), intent(in) :: X
3097+ !
3098+ real, dimension(X%dim,ele_loc(X,1)) :: X_vals
3099+ real, dimension(X%dim,ele_loc(QuadX,1)) :: QX_vals
3100+ integer :: qele,dim1,ele
3101+
3102+ real, dimension(X%dim,3) :: X_lin
3103+
3104+ do ele = 1, ele_count(X)
3105+ X_vals = ele_val(X,ele)
3106+
3107+ !!Need to evaluate X at bubble node locations
3108+ select case(ele_loc(X,1))
3109+ case(3)
3110+ X_lin = X_vals
3111+ case(6)
3112+ X_lin(:,1) = X_vals(:,1)
3113+ X_lin(:,2) = X_vals(:,3)
3114+ X_lin(:,3) = X_vals(:,6)
3115+ case(10)
3116+ X_lin(:,1) = X_vals(:,1)
3117+ X_lin(:,2) = X_vals(:,4)
3118+ X_lin(:,3) = X_vals(:,10)
3119+ case default
3120+ FLExit('Dont know how to handle coordinate mesh')
3121+ end select
3122+
3123+ !! Quadrilateral #1
3124+ do dim1 = 1, X%dim
3125+ QX_vals(dim1,:) = &
3126+ &(/X_lin(dim1,1),0.5*(X_lin(dim1,1)+X_lin(dim1,2)),&
3127+ &0.5*(X_lin(dim1,1)+X_lin(dim1,3)),&
3128+ &sum(X_lin(dim1,:))/3/)
3129+ end do
3130+ qele = (ele-1)*3+1
3131+ call set(QuadX,ele_nodes(QuadX,qele),QX_vals)
3132+ !! Quadrilateral #2
3133+ do dim1 = 1, X%dim
3134+ QX_vals(dim1,:) = &
3135+ &(/X_lin(dim1,2),0.5*(X_lin(dim1,2)+X_lin(dim1,3)),&
3136+ &0.5*(X_lin(dim1,1)+X_lin(dim1,2)),&
3137+ &sum(X_lin(dim1,:))/3/)
3138+ end do
3139+ qele = (ele-1)*3+2
3140+ call set(QuadX,ele_nodes(QuadX,qele),QX_vals)
3141+ !! Quadrilateral #3
3142+ do dim1 = 1, X%dim
3143+ QX_vals(dim1,:) = &
3144+ &(/X_lin(dim1,3),0.5*(X_lin(dim1,3)+X_lin(dim1,1)),&
3145+ &0.5*(X_lin(dim1,2)+X_lin(dim1,3)),&
3146+ &sum(X_lin(dim1,:))/3/)
3147+ end do
3148+ qele = (ele-1)*3+3
3149+ call set(QuadX,ele_nodes(QuadX,qele),QX_vals)
3150+ end do
3151+
3152+ end subroutine quadX_from_bubbleX
3153+
3154+ subroutine quadSF_from_bubbleSF(QuadSF,sfield)
3155+ type(scalar_field), intent(inout) :: QuadSF
3156+ type(scalar_field), intent(in) :: sfield
3157+
3158+ !
3159+ real, dimension(ele_loc(sfield,1)) :: s_vals
3160+ real, dimension(ele_loc(QuadSF,1)) :: qs_vals
3161+ integer :: qele,ele
3162+ real, dimension(7) :: N_vals
3163+
3164+ !Basis functions evaluated at bubble node.
3165+ N_vals = eval_shape(ele_shape(sfield,1), (/1.0/3.0,1.0/3.0,1.0/3.0/))
3166+
3167+ do ele = 1, ele_count(sfield)
3168+ s_vals = ele_val(sfield,ele)
3169+ assert(size(s_vals)==7)
3170+ s_vals(7) = s_vals(7)*N_vals(7)
3171+ s_vals(7) = s_vals(7) + sum(s_vals(1:6)*N_vals(1:6))
3172+ !! Quadrilateral #1
3173+ QS_vals(:) = &
3174+ &(/s_vals(1),s_vals(2),s_vals(4),s_vals(7)/)
3175+ qele = (ele-1)*3+1
3176+ call set(QuadSF,ele_nodes(QuadSF,qele),QS_vals)
3177+ !! Quadrilateral #2
3178+ QS_vals(:) = &
3179+ &(/s_vals(3),s_vals(5),s_vals(2),s_vals(7)/)
3180+ qele = (ele-1)*3+2
3181+ call set(QuadSF,ele_nodes(QuadSF,qele),QS_vals)
3182+ !! Quadrilateral #3
3183+ QS_vals(:) = &
3184+ &(/s_vals(6),s_vals(4),s_vals(5),s_vals(7)/)
3185+ qele = (ele-1)*3+3
3186+ call set(QuadSF,ele_nodes(QuadSF,qele),QS_vals)
3187+ end do
3188+ end subroutine quadSF_from_bubbleSF
3189+ end subroutine bubble_field_to_vtk
3190+
3191+#include "../femtools/Reference_count_mesh_type.F90"
3192+
3193+ end module bubble_tools
3194
3195=== modified file 'assemble/Diagnostic_fields_wrapper.F90'
3196--- assemble/Diagnostic_fields_wrapper.F90 2012-09-18 08:03:27 +0000
3197+++ assemble/Diagnostic_fields_wrapper.F90 2012-10-23 15:04:25 +0000
3198@@ -79,7 +79,7 @@
3199 ! An array of submaterials of the current phase in state(istate).
3200 type(state_type), dimension(:), pointer :: submaterials
3201
3202- ewrite(1, *) "In calculate_diagnostic_variables"
3203+ ewrite(1, *) "In calculate_diagnostic_variables (Diagnostic_fields_wrapper)"
3204
3205 do i = 1, size(state)
3206
3207
3208=== modified file 'assemble/Hybridized_Helmholtz.F90'
3209--- assemble/Hybridized_Helmholtz.F90 2011-07-22 12:39:08 +0000
3210+++ assemble/Hybridized_Helmholtz.F90 2012-10-23 15:04:25 +0000
3211@@ -24,7 +24,6 @@
3212 ! License along with this library; if not, write to the Free Software
3213 ! Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
3214 ! USA
3215-
3216
3217 #include "fdebug.h"
3218
3219@@ -44,18 +43,213 @@
3220 use diagnostic_fields_wrapper
3221 use assemble_cmc
3222 use FUtils, only : real_vector, real_matrix
3223- use global_parameters, only: option_path_len
3224+ use global_parameters, only: option_path_len, PYTHON_FUNC_LEN
3225 use vector_tools, only: solve
3226- use manifold_projections
3227- implicit none
3228-
3229-contains
3230+ use manifold_tools
3231+ implicit none
3232+
3233+ !Variables belonging to the Newton solver
3234+ logical, private :: newton_initialised = .false.
3235+ !Cached Newton solver matrices
3236+ type(csr_matrix), private :: Newton_lambda_mat
3237+ type(block_csr_matrix), private :: Newton_continuity_mat
3238+ type(real_matrix), pointer, dimension(:), private &
3239+ &:: Newton_local_solver_cache=>null()
3240+ type(real_matrix), pointer, dimension(:), private &
3241+ &:: Newton_local_solver_rhs_cache=>null()
3242+
3243+contains
3244+
3245+ subroutine solve_hybridised_timestep_residual(state,newU,newD,&
3246+ UResidual, DResidual)
3247+
3248+ ! Subroutine to apply one Newton iteration to hybridised shallow
3249+ ! water equations
3250+ ! Written to cache all required matrices
3251+ implicit none
3252+ type(state_type), intent(inout) :: state
3253+ type(vector_field), intent(inout) :: newU !U at next timestep
3254+ type(scalar_field), intent(inout) :: newD !D at next timestep
3255+ type(vector_field), intent(in), optional :: UResidual
3256+ type(scalar_field), intent(in), optional :: DResidual
3257+ !
3258+ type(vector_field), pointer :: X, U, down, U_cart, U_old
3259+ type(scalar_field), pointer :: D,f, D_old
3260+ type(scalar_field) :: lambda, D_res, D_res2
3261+ type(vector_field) :: U_res, U_res2
3262+ type(scalar_field), target :: lambda_rhs, u_cpt
3263+ type(csr_sparsity) :: lambda_sparsity, continuity_sparsity
3264+ type(csr_matrix) :: continuity_block_mat
3265+ type(mesh_type), pointer :: lambda_mesh
3266+ real :: D0, dt, g, theta, tolerance
3267+ integer :: ele,i1, stat, dim1, dloc, uloc,mdim,n_constraints
3268+ logical :: have_constraint
3269+ character(len=OPTION_PATH_LEN) :: constraint_option_string
3270+
3271+ ewrite(1,*) 'solve_hybridised_timestep_residual(state)'
3272+
3273+ ewrite(1,*) 'TIMESTEPPING LAMBDA'
3274+
3275+ !get parameters
3276+ call get_option("/physical_parameters/gravity/magnitude", g)
3277+ call get_option("/timestepping/theta",theta)
3278+ call get_option("/material_phase::Fluid/scalar_field::LayerThickness/&
3279+ &prognostic/mean_layer_thickness",D0)
3280+ call get_option("/timestepping/timestep", dt)
3281+
3282+ !Pull the fields out of state
3283+ D=>extract_scalar_field(state, "LayerThickness")
3284+ f=>extract_scalar_field(state, "Coriolis")
3285+ U=>extract_vector_field(state, "LocalVelocity")
3286+ X=>extract_vector_field(state, "Coordinate")
3287+ down=>extract_vector_field(state, "GravityDirection")
3288+ U_cart => extract_vector_field(state, "Velocity")
3289+ U_old => extract_vector_field(state, "OldLocalVelocity")
3290+ D_old => extract_scalar_field(state, "OldLayerThickness")
3291+
3292+ !Allocate local field variables
3293+ lambda_mesh=>extract_mesh(state, "VelocityMeshTrace")
3294+ call allocate(lambda,lambda_mesh,name="LagrangeMultiplier")
3295+ call allocate(lambda_rhs,lambda%mesh,"LambdaRHS")
3296+ call zero(lambda_rhs)
3297+ call allocate(U_res,U%dim,U%mesh,"VelocityResidual")
3298+ call allocate(D_res,D%mesh,"LayerThicknessResidual")
3299+ call allocate(U_res2,U%dim,U%mesh,"VelocityResidual2")
3300+ call allocate(D_res2,D%mesh,"LayerThicknessResidual2")
3301+
3302+ if(.not.newton_initialised) then
3303+ !construct/extract sparsities
3304+ lambda_sparsity=get_csr_sparsity_firstorder(&
3305+ &state, lambda%mesh, lambda&
3306+ &%mesh)
3307+ continuity_sparsity=get_csr_sparsity_firstorder(state, u%mesh,&
3308+ & lambda%mesh)
3309+
3310+ !allocate matrices
3311+ call allocate(Newton_lambda_mat,lambda_sparsity)
3312+ call zero(Newton_lambda_mat)
3313+ call allocate(Newton_continuity_mat,continuity_sparsity,(/U%dim,1/))
3314+ call zero(Newton_continuity_mat)
3315+
3316+ mdim = mesh_dim(U)
3317+ have_constraint = &
3318+ &have_option(trim(U%mesh%option_path)//"/from_mesh/constraint_type")
3319+
3320+ allocate(newton_local_solver_cache(ele_count(U)))
3321+ allocate(newton_local_solver_rhs_cache(ele_count(U)))
3322+ do ele = 1, ele_count(D)
3323+ uloc = ele_loc(U,ele)
3324+ dloc = ele_loc(d,ele)
3325+ n_constraints = 0
3326+ if(have_constraint) then
3327+ n_constraints = ele_n_constraints(U,ele)
3328+ allocate(newton_local_solver_cache(ele)%ptr(&
3329+ mdim*uloc+dloc+n_constraints,&
3330+ mdim*uloc+dloc+n_constraints))
3331+ allocate(newton_local_solver_rhs_cache(ele)%ptr(&
3332+ mdim*uloc+dloc,mdim*uloc+dloc))
3333+ end if
3334+ end do
3335+
3336+ !Assemble matrices
3337+ do ele = 1, ele_count(D)
3338+ call assemble_newton_solver_ele(D,f,U,X,down,lambda_rhs,ele, &
3339+ &g,dt,theta,D0,&
3340+ &lambda_mat=Newton_lambda_mat,&
3341+ &continuity_mat=Newton_continuity_mat,&
3342+ &Local_solver_matrix=Newton_local_solver_cache(ele)%ptr,&
3343+ &Local_solver_rhs=Newton_local_solver_rhs_cache(ele)%ptr)
3344+ end do
3345+ newton_initialised = .true.
3346+ end if
3347+
3348+ if(present(DResidual).and.present(UResidual).and..not.&
3349+ have_option('/material_phase::Fluid/vector_field::Velocity/prognostic/wave_equation/fully_coupled/linear_debug')) then
3350+ !Set residuals from nonlinear input
3351+ call set(U_res,UResidual)
3352+ call set(D_res,DResidual)
3353+ else
3354+ !Compute residuals for linear equation
3355+ do ele = 1, ele_count(D)
3356+ call get_linear_residuals_ele(U_res,D_res,&
3357+ U_old,D_old,newU,newD,&
3358+ newton_local_solver_cache(ele)%ptr,&
3359+ newton_local_solver_rhs_cache(ele)%ptr,ele)
3360+ end do
3361+ ewrite(2,*) 'cjc U_res calc', sum(newU%val), maxval(abs(U_res%val))
3362+ ewrite(2,*) 'cjc D_res calc', sum(newD%val), maxval(abs(D_res%val))
3363+ end if
3364+
3365+ do ele = 1, ele_count(D)
3366+ call local_solve_residuals_ele(U_res,D_res,&
3367+ newton_local_solver_cache(ele)%ptr,ele)
3368+ end do
3369+
3370+ call mult_t(lambda_rhs,Newton_continuity_mat,U_res)
3371+ call scale(lambda_rhs,-1.0)
3372+ ewrite(2,*) 'LAMBDARHS', maxval(abs(lambda_rhs%val))
3373+
3374+ call zero(lambda)
3375+ !Solve the equations
3376+ call petsc_solve(lambda,newton_lambda_mat,lambda_rhs,&
3377+ option_path=trim(U_cart%mesh%option_path)//&
3378+ &"/from_mesh/constraint_type")
3379+ ewrite(2,*) 'LAMBDA', maxval(abs(lambda%val))
3380+
3381+ !Update new U and new D from lambda
3382+ !Compute residuals
3383+ if(present(DResidual).and.present(UResidual).and..not.&
3384+ have_option('/material_phase::Fluid/vector_field::Velocity/prognostic/wave_equation/fully_coupled/linear_debug')) then
3385+ call set(U_res,UResidual)
3386+ call set(D_res,DResidual)
3387+ else
3388+ do ele = 1, ele_count(D)
3389+ call get_linear_residuals_ele(U_res,D_res,&
3390+ U_old,D_old,newU,newD,&
3391+ newton_local_solver_cache(ele)%ptr,&
3392+ newton_local_solver_rhs_cache(ele)%ptr,ele)
3393+ end do
3394+ end if
3395+ ewrite(2,*) 'cjc U_res recalc', sum(newU%val), maxval(abs(U_res%val))
3396+ ewrite(2,*) 'cjc D_res recalc', sum(newD%val), maxval(abs(D_res%val))
3397+
3398+ call mult(U_res2,Newton_continuity_mat,lambda)
3399+ call addto(U_res,U_res2)
3400+ do ele = 1, ele_count(U)
3401+ call local_solve_residuals_ele(U_res,D_res,&
3402+ newton_local_solver_cache(ele)%ptr,ele)
3403+ end do
3404+
3405+ !Negative scaling occurs here.
3406+ call scale(U_res,-1.0)
3407+ call scale(D_res,-1.0)
3408+
3409+ ewrite(2,*) 'Delta U', maxval(abs(U_res%val))
3410+ ewrite(2,*) 'Delta D', maxval(abs(D_res%val))
3411+
3412+ call addto(newU,U_res)
3413+ call addto(newD,D_res)
3414+
3415+ !Deallocate local variables
3416+ call deallocate(lambda_rhs)
3417+ call deallocate(lambda)
3418+ call deallocate(U_res)
3419+ call deallocate(D_res)
3420+ call deallocate(U_res2)
3421+ call deallocate(D_res2)
3422+
3423+ ewrite(1,*) 'END solve_hybridised_timestep_residual(state)'
3424+
3425+ end subroutine solve_hybridised_timestep_residual
3426+
3427 subroutine solve_hybridized_helmholtz(state,D_rhs,U_Rhs,&
3428 &D_out,U_out,&
3429+ &dt_in,theta_in,&
3430 &compute_cartesian,&
3431- &check_continuity,output_dense,&
3432- &projection,poisson,u_rhs_local,&
3433- &solver_option_path)
3434+ &output_dense,&
3435+ &projection,poisson,&
3436+ &u_rhs_local)
3437+
3438 ! Subroutine to solve hybridized helmholtz equation
3439 ! If D_rhs (scalar pressure field) is present, then solve:
3440 ! <w,u> + <w,fu^\perp> - g <div w,d> + <<[w],d>> = <w,U_rhs>
3441@@ -63,7 +257,7 @@
3442 ! <<\gamma, [u]>> = 0
3443 ! (i.e. for unit testing)
3444 ! otherwise solve:
3445- ! <w,u> + dt*theta*<w,fu^\perp> - dt*theta*g <div w,d> + <<[w],d>> =
3446+ ! <w,u> + dt*theta*<w,fu^\perp> - dt*theta*g <div w,d> + <<[w],l>> =
3447 ! -dt*<w f(u^n)^\perp> + dt*g*<div w, d^n>
3448 ! <\phi,\eta> + dt*theta*<\phi,div u> = <\ph
3449 ! <<\gamma, [u]>> = 0
3450@@ -76,10 +270,10 @@
3451 type(vector_field), intent(inout), optional :: U_rhs
3452 type(scalar_field), intent(inout), optional :: D_out
3453 type(vector_field), intent(inout), optional :: U_out
3454+ real, intent(in), optional :: theta_in,dt_in
3455 logical, intent(in), optional :: compute_cartesian, &
3456- &check_continuity,output_dense, projection,poisson
3457+ &output_dense, projection,poisson
3458 logical, intent(in), optional :: u_rhs_local !means u_rhs is in local coords
3459- character(len=OPTION_PATH_LEN), intent(in), optional :: solver_option_path
3460 !
3461 type(vector_field), pointer :: X, U, down, U_cart
3462 type(scalar_field), pointer :: D,f, lambda_nc
3463@@ -89,21 +283,36 @@
3464 type(csr_matrix) :: lambda_mat, continuity_block_mat,continuity_block_mat1
3465 type(block_csr_matrix) :: continuity_mat
3466 type(mesh_type), pointer :: lambda_mesh
3467- real :: D0, dt, g, theta
3468+ real :: D0, dt, g, theta, tolerance
3469 integer :: ele,i1, stat, dim1
3470- logical :: l_compute_cartesian,l_check_continuity, l_output_dense
3471+ logical :: l_compute_cartesian, l_output_dense
3472 real, dimension(:,:), allocatable :: lambda_mat_dense
3473 character(len=OPTION_PATH_LEN) :: constraint_option_string
3474+ real :: u_max
3475+ real, dimension(:), allocatable :: weights
3476+ logical :: l_projection,l_poisson, pullback
3477
3478 ewrite(1,*) ' subroutine solve_hybridized_helmholtz('
3479
3480 l_compute_cartesian = .false.
3481 if(present(compute_cartesian)) l_compute_cartesian = compute_cartesian
3482- if(present(check_continuity)) l_check_continuity = check_continuity
3483- if(l_check_continuity) l_compute_cartesian = .true.
3484 l_output_dense = .false.
3485 if(present(output_dense)) l_output_dense = output_dense
3486
3487+ if(present(projection)) then
3488+ l_projection = projection
3489+ else
3490+ l_projection = .false.
3491+ end if
3492+ if(present(poisson)) then
3493+ l_poisson = poisson
3494+ end if
3495+ ewrite(2,*) 'Projection = ', l_projection
3496+ ewrite(2,*) 'Poisson = ', l_poisson
3497+ if(l_poisson.and.l_projection) then
3498+ FLAbort('Can''t do projection and poisson')
3499+ end if
3500+
3501 !Pull the fields out of state
3502 D=>extract_scalar_field(state, "LayerThickness")
3503 f=>extract_scalar_field(state, "Coriolis")
3504@@ -134,68 +343,86 @@
3505 !get parameters
3506 call get_option("/physical_parameters/gravity/magnitude", g)
3507 !theta
3508- call get_option("/material_phase::Fluid/scalar_field::LayerThickness/&
3509- &prognostic/temporal_discretisation/theta",theta)
3510+
3511+ if(present(theta_in)) then
3512+ theta = theta_in
3513+ else
3514+ call get_option("/timestepping/theta",theta)
3515+ end if
3516 !D0
3517 call get_option("/material_phase::Fluid/scalar_field::LayerThickness/&
3518- &p&
3519- &rognostic/mean_layer_thickness",D0)
3520- call get_option("/timestepping/timestep", dt)
3521-
3522+ &prognostic/mean_layer_thickness",D0)
3523+ if(present(dt_in)) then
3524+ dt = dt_in
3525+ else
3526+ call get_option("/timestepping/timestep", dt)
3527+ end if
3528+
3529+ !compute rescaling weights for local coordinates
3530+ allocate(weights(element_count(X)))
3531+ weights = 1.0
3532+ !call get_weights(X,weights)
3533+
3534+ pullback = have_option('/material_phase::Fluid/scalar_field::LayerThickn&
3535+ &ess/prognostic/spatial_discretisation/discontinuous_galerkin/wave_&
3536+ &equation/pullback')
3537+
3538 !Assemble matrices
3539 do ele = 1, ele_count(D)
3540 call assemble_hybridized_helmholtz_ele(D,f,U,X,down,ele, &
3541- &g,dt,theta,D0,lambda_mat=lambda_mat,&
3542+ &g,dt,theta,D0,pullback,weights(ele),lambda_mat=lambda_mat,&
3543 &lambda_rhs=lambda_rhs,D_rhs=D_rhs,U_rhs=U_rhs,&
3544 &continuity_mat=continuity_mat,&
3545- &projection=projection,poisson=poisson,&
3546+ &projection=l_projection,poisson=l_poisson,&
3547 &u_rhs_local=u_rhs_local)
3548 end do
3549
3550- ewrite(1,*) 'LAMBDARHS', maxval(abs(lambda_rhs%val))
3551-
3552+ ewrite(2,*) 'LAMBDARHS', maxval(abs(lambda_rhs%val))
3553+ call zero(lambda)
3554 !Solve the equations
3555- if(present(solver_option_path)) then
3556- call petsc_solve(lambda,lambda_mat,lambda_rhs,&
3557- option_path=solver_option_path)
3558- else
3559- call petsc_solve(lambda,lambda_mat,lambda_rhs,&
3560- option_path=trim(U_cart%mesh%option_path)//&
3561- &"/from_mesh/constraint_type")
3562- end if
3563- ewrite(1,*) 'LAMBDA', maxval(abs(lambda%val))
3564+ call petsc_solve(lambda,lambda_mat,lambda_rhs,&
3565+ option_path=trim(U_cart%mesh%option_path)//&
3566+ &"/from_mesh/constraint_type")
3567
3568 !Reconstruct U and D from lambda
3569 do ele = 1, ele_count(D)
3570 call reconstruct_u_d_ele(D,f,U,X,down,ele, &
3571- &g,dt,theta,D0,D_rhs=D_rhs,U_rhs=U_rhs,lambda=lambda,&
3572+ &g,dt,theta,D0,pullback,weights(ele),&
3573+ &D_rhs=D_rhs,U_rhs=U_rhs,lambda=lambda,&
3574 &D_out=D_out,U_out=U_out,&
3575- &projection=projection,poisson=poisson,&
3576+ &projection=l_projection,poisson=l_poisson,&
3577 &u_rhs_local=u_rhs_local)
3578 end do
3579
3580+ if(l_poisson.and.present(D_out)) then
3581+ call check_zero_level(D_out)
3582+ end if
3583+ ewrite(2,*) 'LAMBDA', maxval(abs(lambda%val))
3584+
3585 if(l_output_dense) then
3586 allocate(lambda_mat_dense(node_count(lambda),node_count(lambda)))
3587 lambda_mat_dense = dense(lambda_mat)
3588- ewrite(1,*) '-----------'
3589+ ewrite(2,*) '-----------'
3590 do i1 = 1, node_count(lambda)
3591- ewrite(1,*) lambda_mat_dense(i1,:)
3592+ ewrite(2,*) lambda_mat_dense(i1,:)
3593 end do
3594- ewrite(1,*) '-----------'
3595+ ewrite(2,*) '-----------'
3596 end if
3597
3598 if(l_compute_cartesian) then
3599 U_cart => extract_vector_field(state, "Velocity")
3600 if(present(U_out)) then
3601- call project_local_to_cartesian(X,U_out,U_cart)
3602+ call project_local_to_cartesian(X,U_out,U_cart,weights=weights)
3603 else
3604- call project_local_to_cartesian(X,U,U_cart)
3605+ call project_local_to_cartesian(X,U,U_cart,weights=weights)
3606 end if
3607 end if
3608- if(l_check_continuity) then
3609- ewrite(1,*) 'Checking continuity'
3610+
3611+ if(have_option('/geometry/mesh::VelocityMesh/check_continuity_matrix')) then
3612+ ewrite(2,*) 'Checking continuity'
3613
3614 call zero(lambda_rhs)
3615+ u_max = 0.
3616 do dim1 = 1,U%dim
3617 if(present(U_out)) then
3618 u_cpt = extract_scalar_field(U_out,dim1)
3619@@ -204,29 +431,264 @@
3620 end if
3621 continuity_block_mat = block(continuity_mat,dim1,1)
3622 call mult_T_addto(lambda_rhs,continuity_block_mat,u_cpt)
3623- ewrite(1,*) 'U, lambda',&
3624+ ewrite(2,*) 'U, lambda',&
3625 &maxval(u_cpt%val), maxval(abs(lambda_rhs%val))
3626+ u_max = u_max + maxval(abs(u_cpt%val))
3627 end do
3628- ewrite(1,*)'JUMPS MIN:MAX',minval(lambda_rhs%val),&
3629- &maxval(lambda_rhs%val)
3630- assert(maxval(abs(lambda_rhs%val))<1.0e-10)
3631-
3632- ewrite(1,*) 'D MAXABS', maxval(abs(D%val))
3633+ ewrite(2,*)'JUMPS MIN:MAX',minval(lambda_rhs%val),&
3634+ &maxval(lambda_rhs%val), u_max
3635+
3636+ call get_option('/geometry/mesh::VelocityMesh/check_continuity_matrix/tolerance',tolerance)
3637+ if(maxval(abs(lambda_rhs%val))/max(1.0,u_max/3.0)>tolerance) then
3638+ ewrite(-1,*) 'value =', maxval(abs(lambda_rhs%val))/max(1.0,u_max/3.0)
3639+ FLExit('Continuity matrix tolerance failure')
3640+ end if
3641+ end if
3642+
3643+ if(have_option('/geometry/mesh::VelocityMesh/check_continuity')) then
3644+ U_cart => extract_vector_field(state, "Velocity")
3645+ if(present(U_out)) then
3646+ call project_local_to_cartesian(X,U_out,U_cart,weights=weights)
3647+ else
3648+ call project_local_to_cartesian(X,U,U_cart,weights=weights)
3649+ end if
3650+ call get_option('/geometry/mesh::VelocityMesh/check_continuity/tolerance', tolerance)
3651 do ele = 1, ele_count(U)
3652- call check_continuity_ele(U_cart,X,ele)
3653+ call check_continuity_ele(U_cart,X,ele,tolerance)
3654 end do
3655 end if
3656
3657 call deallocate(lambda_mat)
3658+ call deallocate(continuity_mat)
3659 call deallocate(lambda_rhs)
3660 call deallocate(lambda)
3661+ deallocate(weights)
3662
3663 ewrite(1,*) 'END subroutine solve_hybridized_helmholtz'
3664
3665 end subroutine solve_hybridized_helmholtz
3666+
3667+ subroutine get_linear_residuals_ele(U_res,D_res,U,D,&
3668+ newU,newD,local_solver,local_solver_rhs,ele)
3669+ !Subroutine
3670+ type(vector_field), intent(inout) :: U_res,U,newU
3671+ type(scalar_field), intent(inout) :: D_res,D,newD
3672+ real, dimension(:,:), intent(in) :: local_solver, local_solver_rhs
3673+ integer, intent(in) :: ele
3674+ !
3675+ integer :: uloc, dloc, dim1, d_start, d_end, mdim
3676+ integer, dimension(mesh_dim(U)) :: U_start, U_end
3677+ real, dimension(mesh_dim(U)*ele_loc(U,ele)+ele_loc(D,ele))&
3678+ :: rhs_loc1,rhs_loc2
3679+ real, dimension(mesh_dim(U),ele_loc(U,ele)) :: U_val
3680+
3681+ !Calculate indices in a vector containing all the U and D dofs in
3682+ !element ele, First the u1 components, then the u2 components, then the
3683+ !D components are stored.
3684+ uloc = ele_loc(U_res,ele)
3685+ dloc = ele_loc(D_res,ele)
3686+ d_end = mesh_dim(U_res)*uloc+dloc
3687+ mdim = mesh_dim(U)
3688+ do dim1 = 1, mdim
3689+ u_start(dim1) = uloc*(dim1-1)+1
3690+ u_end(dim1) = uloc*dim1
3691+ end do
3692+ d_start = uloc*mdim + 1
3693+ d_end = uloc*mdim+dloc
3694+
3695+ U_val = ele_val(U,ele)
3696+ do dim1 = 1, mesh_dim(U)
3697+ rhs_loc1(u_start(dim1):u_end(dim1)) = u_val(dim1,:)
3698+ end do
3699+ rhs_loc1(d_start:d_end) = ele_val(D,ele)
3700+ rhs_loc1 = matmul(local_solver_rhs,rhs_loc1)
3701+ U_val = ele_val(newU,ele)
3702+ do dim1 = 1, mesh_dim(U)
3703+ rhs_loc2(u_start(dim1):u_end(dim1)) = u_val(dim1,:)
3704+ end do
3705+ rhs_loc2(d_start:d_end) = ele_val(newD,ele)
3706+ rhs_loc2 = matmul(local_solver(1:d_end,1:d_end),rhs_loc2)
3707+ rhs_loc2 = rhs_loc2-rhs_loc1
3708+ do dim1 = 1, mesh_dim(U)
3709+ call set(U_res,dim1,ele_nodes(U,ele),&
3710+ rhs_loc2(u_start(dim1):u_end(dim1)))
3711+ end do
3712+ call set(D_res,ele_nodes(D,ele),&
3713+ rhs_loc2(d_start:d_end))
3714+ end subroutine get_linear_residuals_ele
3715+
3716+ subroutine local_solve_residuals_ele(U_res,D_res,&
3717+ local_solver_matrix,ele)
3718+ type(vector_field), intent(inout) :: U_res
3719+ type(scalar_field), intent(inout) :: D_res
3720+ real, dimension(:,:), intent(in) :: local_solver_matrix
3721+ integer, intent(in) :: ele
3722+ !
3723+ real, dimension(mesh_dim(U_res)*ele_loc(U_res,ele)+ele_loc(D_res,ele)+&
3724+ &ele_n_constraints(U_res,ele)) :: rhs_loc
3725+ real, dimension(mesh_dim(U_res),ele_loc(U_res,ele)) :: U_val
3726+ integer :: uloc,dloc, d_start, d_end, dim1, mdim
3727+ integer, dimension(mesh_dim(U_res)) :: U_start, U_end
3728+
3729+ rhs_loc = 0.
3730+
3731+ !Calculate indices in a vector containing all the U and D dofs in
3732+ !element ele, First the u1 components, then the u2 components, then the
3733+ !D components are stored.
3734+ uloc = ele_loc(U_res,ele)
3735+ dloc = ele_loc(D_res,ele)
3736+ d_end = mesh_dim(U_res)*uloc+dloc
3737+ mdim = mesh_dim(U_res)
3738+ do dim1 = 1, mdim
3739+ u_start(dim1) = uloc*(dim1-1)+1
3740+ u_end(dim1) = uloc*dim1
3741+ end do
3742+ d_start = uloc*mdim + 1
3743+ d_end = uloc*mdim+dloc
3744+
3745+ U_val = ele_val(U_res,ele)
3746+ do dim1 = 1, mesh_dim(U_res)
3747+ rhs_loc(u_start(dim1):u_end(dim1)) = u_val(dim1,:)
3748+ end do
3749+ rhs_loc(d_start:d_end) = &
3750+ & ele_val(D_res,ele)
3751+
3752+ call solve(local_solver_matrix,Rhs_loc)
3753+ do dim1 = 1, mesh_dim(U_res)
3754+ call set(U_res,dim1,ele_nodes(U_res,ele),&
3755+ rhs_loc(u_start(dim1):u_end(dim1)))
3756+ end do
3757+ call set(D_res,ele_nodes(D_res,ele),&
3758+ rhs_loc(d_start:d_end))
3759+ end subroutine local_solve_residuals_ele
3760+
3761+ subroutine assemble_newton_solver_ele(D,f,U,X,down,lambda_rhs,ele, &
3762+ &g,dt,theta,D0,&
3763+ &lambda_mat,continuity_mat,&
3764+ &Local_solver_matrix,Local_solver_rhs)
3765+ implicit none
3766+ type(scalar_field), intent(in) :: D,f
3767+ type(scalar_field), intent(in) :: lambda_rhs
3768+ type(vector_field), intent(in) :: U,X,down
3769+ integer, intent(in) :: ele
3770+ real, intent(in) :: g,dt,theta,D0
3771+ type(csr_matrix), intent(inout) :: lambda_mat
3772+ type(block_csr_matrix), intent(inout) :: continuity_mat
3773+ real, dimension(:,:), intent(inout) ::&
3774+ & local_solver_matrix
3775+ real, dimension(:,:), intent(inout) ::&
3776+ & local_solver_rhs
3777+ !
3778+ real, allocatable, dimension(:,:),target :: &
3779+ &l_continuity_mat, l_continuity_mat2
3780+ real, allocatable, dimension(:,:) :: helmholtz_loc_mat
3781+ real, allocatable, dimension(:,:,:) :: continuity_face_mat
3782+ real, allocatable, dimension(:,:) :: scalar_continuity_face_mat
3783+ integer :: ni, face
3784+ integer, dimension(:), pointer :: neigh
3785+ type(element_type) :: U_shape
3786+ integer :: stat, d_start, d_end, dim1, mdim, uloc,dloc, lloc, iloc
3787+ integer, dimension(mesh_dim(U)) :: U_start, U_end
3788+ type(real_matrix), dimension(mesh_dim(U)) :: &
3789+ & continuity_mat_u_ptr
3790+ logical :: have_constraint
3791+ integer :: constraint_choice, n_constraints, constraints_start
3792+
3793+ !Get some sizes
3794+ lloc = ele_loc(lambda_rhs,ele)
3795+ mdim = mesh_dim(U)
3796+ uloc = ele_loc(U,ele)
3797+ dloc = ele_loc(d,ele)
3798+ U_shape = ele_shape(U,ele)
3799+
3800+ have_constraint = &
3801+ &have_option(trim(U%mesh%option_path)//"/from_mesh/constraint_type")
3802+ n_constraints = 0
3803+ if(have_constraint) then
3804+ n_constraints = ele_n_constraints(U,ele)
3805+ end if
3806+
3807+ !Calculate indices in a vector containing all the U and D dofs in
3808+ !element ele, First the u1 components, then the u2 components, then the
3809+ !D components are stored.
3810+ do dim1 = 1, mdim
3811+ u_start(dim1) = uloc*(dim1-1)+1
3812+ u_end(dim1) = uloc*dim1
3813+ end do
3814+ d_start = uloc*mdim + 1
3815+ d_end = uloc*mdim+dloc
3816+
3817+ !Get pointers to different parts of l_continuity_mat
3818+ allocate(l_continuity_mat(2*uloc+dloc+n_constraints,lloc))
3819+ do dim1= 1,mdim
3820+ continuity_mat_u_ptr(dim1)%ptr => &
3821+ & l_continuity_mat(u_start(dim1):u_end(dim1),:)
3822+ end do
3823+
3824+ ! ( M C -L)(u) (v)
3825+ ! ( -C^T N 0 )(h) = (j)
3826+ ! ( L^T 0 0 )(l) (0)
3827+ !
3828+ ! (u) (M C)^{-1}(v) (M C)^{-1}(L)
3829+ ! (h) = (-C^T N) (j) + (-C^T N) (0)(l)
3830+ ! so
3831+ ! (M C)^{-1}(L) (M C)^{-1}(v)
3832+ ! (L^T 0)(-C^T N) (0)=-(L^T 0)(-C^T N) (j)
3833+
3834+ !Get the local_solver matrix that obtains U and D from Lambda on the
3835+ !boundaries
3836+ call get_local_solver(local_solver_matrix,U,X,down,D,f,ele,&
3837+ & g,dt,theta,D0,pullback=.false.,weight=1.0,&
3838+ & have_constraint=have_constraint,&
3839+ & projection=.false.,poisson=.false.,&
3840+ & local_solver_rhs=local_solver_rhs)
3841+
3842+ !!!Construct the continuity matrix that multiplies lambda in
3843+ !!! the U equation
3844+ !allocate l_continuity_mat
3845+ l_continuity_mat = 0.
3846+ !get list of neighbours
3847+ neigh => ele_neigh(D,ele)
3848+ !calculate l_continuity_mat
3849+ do ni = 1, size(neigh)
3850+ face=ele_face(U, ele, neigh(ni))
3851+ allocate(continuity_face_mat(mdim,face_loc(U,face)&
3852+ &,face_loc(lambda_rhs,face)))
3853+ continuity_face_mat = 0.
3854+ call get_continuity_face_mat(continuity_face_mat,face,&
3855+ U,lambda_rhs)
3856+ do dim1 = 1, mdim
3857+ continuity_mat_u_ptr(dim1)%ptr(face_local_nodes(U,face),&
3858+ face_local_nodes(lambda_rhs,face))=&
3859+ continuity_mat_u_ptr(dim1)%ptr(face_local_nodes(U,face),&
3860+ face_local_nodes(lambda_rhs,face))+&
3861+ continuity_face_mat(dim1,:,:)
3862+ end do
3863+ do dim1 = 1, mdim
3864+ call addto(continuity_mat,dim1,1,face_global_nodes(U,face)&
3865+ &,face_global_nodes(lambda_rhs,face),&
3866+ &continuity_face_mat(dim1,:,:))
3867+ end do
3868+
3869+ deallocate(continuity_face_mat)
3870+ end do
3871+
3872+ !compute l_continuity_mat2 = inverse(local_solver)*l_continuity_mat
3873+ allocate(l_continuity_mat2(uloc*2+dloc+n_constraints,lloc))
3874+ l_continuity_mat2 = l_continuity_mat
3875+ call solve(local_solver_matrix,l_continuity_mat2)
3876+
3877+ !compute helmholtz_loc_mat
3878+ allocate(helmholtz_loc_mat(lloc,lloc))
3879+ helmholtz_loc_mat = matmul(transpose(l_continuity_mat),l_continuity_mat2)
3880+
3881+ !insert helmholtz_loc_mat into global lambda matrix
3882+ call addto(lambda_mat,ele_nodes(lambda_rhs,ele),&
3883+ ele_nodes(lambda_rhs,ele),helmholtz_loc_mat)
3884+ end subroutine assemble_newton_solver_ele
3885
3886 subroutine assemble_hybridized_helmholtz_ele(D,f,U,X,down,ele, &
3887- g,dt,theta,D0,lambda_mat,lambda_rhs,U_rhs,D_rhs,&
3888+ g,dt,theta,D0,pullback,weight,lambda_mat,lambda_rhs,U_rhs,D_rhs,&
3889 continuity_mat,projection,poisson,u_rhs_local)
3890 !subroutine to assemble hybridized helmholtz equation.
3891 !For assembly, must provide:
3892@@ -243,7 +705,8 @@
3893 type(vector_field), intent(in), optional :: U_rhs
3894 type(scalar_field), intent(in), optional :: D_rhs
3895 integer, intent(in) :: ele
3896- real, intent(in) :: g,dt,theta,D0
3897+ logical, intent(in) :: pullback
3898+ real, intent(in) :: g,dt,theta,D0,weight
3899 type(csr_matrix), intent(inout) :: lambda_mat
3900 type(block_csr_matrix), intent(inout), optional :: continuity_mat
3901 logical, intent(in), optional :: projection, poisson,u_rhs_local
3902@@ -259,7 +722,7 @@
3903 real, dimension(:),allocatable,target :: Rhs_loc
3904 real, dimension(:,:), allocatable :: local_solver_matrix, local_solver_rhs
3905 type(element_type) :: U_shape
3906- integer :: stat, d_start, d_end, dim1, mdim, uloc,dloc, lloc
3907+ integer :: stat, d_start, d_end, dim1, mdim, uloc,dloc, lloc, iloc
3908 integer, dimension(mesh_dim(U)) :: U_start, U_end
3909 type(real_vector), dimension(mesh_dim(U)) :: rhs_u_ptr
3910 real, dimension(:), pointer :: rhs_d_ptr
3911@@ -321,8 +784,15 @@
3912 !Get the local_solver matrix that obtains U and D from Lambda on the
3913 !boundaries
3914 call get_local_solver(local_solver_matrix,U,X,down,D,f,ele,&
3915- & g,dt,theta,D0,have_constraint,local_solver_rhs,&
3916- projection)
3917+ & g,dt,theta,D0,pullback,weight,have_constraint,&
3918+ & projection=projection,poisson=poisson,&
3919+ & local_solver_rhs=local_solver_rhs)
3920+ if(poisson.and.(ele==1)) then
3921+ !Fix the zero level of D
3922+ local_solver_matrix(d_start,:) = 0.
3923+ local_solver_matrix(:,d_start) = 0.
3924+ local_solver_matrix(d_start,d_start) = 1.
3925+ end if
3926
3927 !!!Construct the continuity matrix that multiplies lambda in
3928 !!! the U equation
3929@@ -343,13 +813,13 @@
3930 face_local_nodes(lambda_rhs,face))=&
3931 continuity_mat_u_ptr(dim1)%ptr(face_local_nodes(U,face),&
3932 face_local_nodes(lambda_rhs,face))+&
3933- continuity_face_mat(dim1,:,:)
3934+ weight*continuity_face_mat(dim1,:,:)
3935 end do
3936 if(present(continuity_mat)) then
3937 do dim1 = 1, mdim
3938 call addto(continuity_mat,dim1,1,face_global_nodes(U,face)&
3939 &,face_global_nodes(lambda_rhs,face),&
3940- &continuity_face_mat(dim1,:,:))
3941+ &weight*continuity_face_mat(dim1,:,:))
3942 end do
3943 end if
3944
3945@@ -368,7 +838,12 @@
3946 !construct lambda_rhs
3947 rhs_loc=0.
3948 lambda_rhs_loc = 0.
3949- call assemble_rhs_ele(Rhs_loc,D,U,X,ele,D_rhs,U_rhs,u_rhs_local)
3950+ call assemble_rhs_ele(Rhs_loc,D,U,X,ele,pullback,&
3951+ weight,D_rhs,U_rhs,u_rhs_local)
3952+ if(poisson.and.(ele==1)) then
3953+ !Fix the zero level of D
3954+ rhs_loc(d_start)=0.0
3955+ end if
3956 if(.not.(present(d_rhs).or.present(u_rhs)))then
3957 assert(.not.present_and_true(projection))
3958 assert(.not.present_and_true(poisson))
3959@@ -384,9 +859,9 @@
3960 ele_nodes(lambda_rhs,ele),helmholtz_loc_mat)
3961
3962 end subroutine assemble_hybridized_helmholtz_ele
3963-
3964+
3965 subroutine reconstruct_U_d_ele(D,f,U,X,down,ele, &
3966- g,dt,theta,D0,U_rhs,D_rhs,lambda,&
3967+ g,dt,theta,D0,pullback,weight,U_rhs,D_rhs,lambda,&
3968 &D_out,U_out,projection,poisson,u_rhs_local)
3969 !subroutine to reconstruct U and D having solved for lambda
3970 implicit none
3971@@ -399,12 +874,13 @@
3972 type(scalar_field), intent(inout), optional :: D_out
3973 type(vector_field), intent(inout), optional :: U_out
3974 integer, intent(in) :: ele
3975- real, intent(in) :: g,dt,theta,D0
3976+ logical, intent(in) :: pullback
3977+ real, intent(in) :: g,dt,theta,D0,weight
3978 logical, intent(in), optional :: projection, poisson,u_rhs_local
3979 !
3980 real, allocatable, dimension(:,:,:) :: continuity_face_mat
3981 integer :: ni, face
3982- integer, dimension(:), pointer :: neigh
3983+ integer, dimension(:), pointer :: neigh, d_nodes
3984 type(element_type) :: U_shape
3985 integer :: d_start, d_end, dim1, mdim, uloc,dloc,lloc
3986 integer, dimension(mesh_dim(U)) :: U_start, U_end
3987@@ -420,7 +896,7 @@
3988 logical :: have_constraint
3989 integer :: n_constraints, i1
3990 type(constraints_type), pointer :: constraints
3991- real :: constraint_check
3992+ real :: constraint_check, u_max
3993
3994 !Get some sizes
3995 lloc = ele_loc(lambda,ele)
3996@@ -460,12 +936,23 @@
3997 !Get the local_solver matrix that obtains U and D from Lambda on the
3998 !boundaries
3999 call get_local_solver(local_solver_matrix,U,X,down,D,f,ele,&
4000- & g,dt,theta,D0,have_constraint,&
4001- & local_solver_rhs=local_solver_rhs,projection=projection,&
4002- & poisson=poisson)
4003+ & g,dt,theta,D0,pullback,weight,have_constraint,&
4004+ & projection=projection,poisson=poisson,&
4005+ & local_solver_rhs=local_solver_rhs)
4006+ if(poisson.and.(ele==1)) then
4007+ !Fix the zero level of D
4008+ local_solver_matrix(d_start,:) = 0.
4009+ local_solver_matrix(:,d_start) = 0.
4010+ local_solver_matrix(d_start,d_start) = 1.
4011+ end if
4012
4013 !Construct the rhs sources for U from lambda
4014- call assemble_rhs_ele(Rhs_loc,D,U,X,ele,D_rhs,U_rhs,U_rhs_local)
4015+ call assemble_rhs_ele(Rhs_loc,D,U,X,ele,pullback,&
4016+ weight,D_rhs,U_rhs,U_rhs_local)
4017+ if(poisson.and.(ele==1)) then
4018+ !Fix the zero level of D
4019+ rhs_loc(d_start)=0.0
4020+ end if
4021 if(.not.(present(d_rhs).or.present(u_rhs)))then
4022 assert(.not.present_and_true(projection))
4023 assert(.not.present_and_true(poisson))
4024@@ -486,7 +973,7 @@
4025 do dim1 = 1, mdim
4026 rhs_u_ptr(dim1)%ptr(face_local_nodes(U,face)) = &
4027 & rhs_u_ptr(dim1)%ptr(face_local_nodes(U,face)) + &
4028- & matmul(continuity_face_mat(dim1,:,:),&
4029+ & weight*matmul(continuity_face_mat(dim1,:,:),&
4030 & face_val(lambda,face))
4031 end do
4032 deallocate(continuity_face_mat)
4033@@ -500,9 +987,6 @@
4034 ! (h) = (-C^T N) (j) + (-C^T N) (0)(l)
4035
4036 call solve(local_solver_matrix,Rhs_loc)
4037- rhs_loc = matmul(local_solver_matrix,rhs_loc)
4038- call solve(local_solver_matrix,Rhs_loc)
4039-
4040 do dim1 = 1, mdim
4041 U_solved(dim1,:) = rhs_loc(u_start(dim1):u_end(dim1))
4042 if(.not.(present_and_true(poisson))) then
4043@@ -513,7 +997,7 @@
4044 end if
4045 end if
4046 end do
4047-
4048+
4049 D_solved = rhs_loc(d_start:d_end)
4050 if(.not.(present_and_true(projection))) then
4051 if(present(D_out)) then
4052@@ -524,26 +1008,29 @@
4053 end if
4054
4055 !check that the constraints are satisfied
4056+ !this is just a rescaling so don't need to use weights
4057 if(have_constraint) then
4058 constraints => U%mesh%shape%constraints
4059 do i1 = 1, constraints%n_constraints
4060 constraint_check = 0.
4061+ u_max = 0.
4062 do dim1 = 1, mdim
4063+ u_max = max(u_max,maxval(abs(U_solved(dim1,:))))
4064 constraint_check = constraint_check + &
4065 & sum(U_solved(dim1,:)*constraints%orthogonal(i1,:,dim1))
4066 end do
4067- if(abs(constraint_check)>1.0e-8) then
4068- ewrite(1,*) 'Constraint check', constraint_check
4069+ if(abs(constraint_check)/(max(1.0,u_max))>1.0e-8) then
4070+ ewrite(2,*) 'Constraint check', constraint_check
4071 FLAbort('Constraint not enforced')
4072 end if
4073- end do
4074+ end do
4075 end if
4076
4077 end subroutine reconstruct_U_d_ele
4078
4079 subroutine get_local_solver(local_solver_matrix,U,X,down,D,f,ele,&
4080- & g,dt,theta,D0,have_constraint, &
4081- & local_solver_rhs,projection,poisson)
4082+ & g,dt,theta,D0,pullback,weight,have_constraint, &
4083+ & projection,poisson,local_solver_rhs)
4084 !Subroutine to get the matrix and rhs for obtaining U and D within
4085 !element ele from the lagrange multipliers on the boundaries.
4086 !This matrix-vector system is referred to as the "local solver" in the
4087@@ -554,15 +1041,16 @@
4088 implicit none
4089 !If projection is present and true, set dt to zero and just project U
4090 !into div-conforming space
4091- real, intent(in) :: g,dt,theta,D0
4092+ real, intent(in) :: g,dt,theta,D0,weight
4093 type(vector_field), intent(in) :: U,X,down
4094 type(scalar_field), intent(in) :: D,f
4095 integer, intent(in) :: ele
4096+ logical, intent(in) :: pullback
4097 real, dimension(:,:)&
4098 &, intent(inout) :: local_solver_matrix
4099 real, dimension(:,:)&
4100 &, intent(inout), optional :: local_solver_rhs
4101- logical, intent(in), optional :: projection, poisson
4102+ logical, intent(in) :: projection, poisson
4103 logical, intent(in) :: have_constraint
4104 !
4105 real, dimension(mesh_dim(U), X%dim, ele_ngi(U,ele)) :: J
4106@@ -583,12 +1071,20 @@
4107 integer, dimension(mesh_dim(U)) :: U_start, U_end
4108 type(constraints_type), pointer :: constraints
4109 integer :: i1, c_start, c_end
4110- real :: l_dt
4111+ real :: l_dt,l_theta,l_d0,detJ_bar
4112
4113- if(present_and_true(projection)) then
4114+ if(projection) then
4115 l_dt = 0.
4116+ l_theta = 0.
4117+ l_d0 = 1.
4118+ else if(poisson) then
4119+ l_dt = 1.
4120+ l_theta = 1.
4121+ l_d0 = 1.
4122 else
4123 l_dt = dt
4124+ l_theta = theta
4125+ l_d0 = d0
4126 end if
4127
4128 mdim = mesh_dim(U)
4129@@ -606,13 +1102,13 @@
4130 if(present(local_solver_rhs)) then
4131 local_solver_rhs = 0.
4132 end if
4133-
4134+
4135 u_shape=ele_shape(u, ele)
4136 D_shape=ele_shape(d, ele)
4137 D_ele => ele_nodes(D, ele)
4138 U_ele => ele_nodes(U, ele)
4139-
4140- if(present_and_true(projection)) then
4141+
4142+ if(projection.or.poisson) then
4143 f_gi = 0.
4144 else
4145 f_gi = ele_val_at_quad(f,ele)
4146@@ -625,6 +1121,7 @@
4147 !detwei is needed for pressure mass matrix
4148 call compute_jacobian(ele_val(X,ele), ele_shape(X,ele), J=J, &
4149 detwei=detwei, detJ=detJ)
4150+ detJ_bar = sum(detJ)/size(detJ)
4151
4152 !----construct local solver
4153 !metrics for velocity mass and coriolis matrices
4154@@ -643,51 +1140,71 @@
4155 !dt*theta*<\phi,div u> + D_0<\phi,h> = 0
4156
4157 !pressure mass matrix (done in global coordinates)
4158- local_solver_matrix(d_start:d_end,d_start:d_end)=&
4159- &shape_shape(d_shape,d_shape,detwei)
4160+ !not included in pressure solver
4161+ if(.not.poisson) then
4162+ if(pullback) then
4163+ local_solver_matrix(d_start:d_end,d_start:d_end)=&
4164+ &shape_shape(d_shape,d_shape,detJ_bar**2/detJ*&
4165+ D_shape%quadrature%weight)
4166+ else
4167+ local_solver_matrix(d_start:d_end,d_start:d_end)=&
4168+ &shape_shape(d_shape,d_shape,detwei)
4169+ end if
4170 if(present(local_solver_rhs)) then
4171- local_solver_rhs(d_start:d_end,d_start:d_end) = &
4172- shape_shape(d_shape,d_shape,detwei)
4173+ if(pullback) then
4174+ local_solver_rhs(d_start:d_end,d_start:d_end) = &
4175+ shape_shape(d_shape,d_shape,detJ_bar**2/detJ*&
4176+ D_shape%quadrature%weight)
4177+ else
4178+ local_solver_rhs(d_start:d_end,d_start:d_end) = &
4179+ shape_shape(d_shape,d_shape,detwei)
4180+ end if
4181 end if
4182+ end if
4183 !divergence matrix (done in local coordinates)
4184- l_div_mat = dshape_shape(u_shape%dn,d_shape,&
4185- &D_shape%quadrature%weight)
4186+ if(pullback) then
4187+ l_div_mat = dshape_shape(u_shape%dn,d_shape,&
4188+ &detJ_bar/detJ*D_shape%quadrature%weight)
4189+ else
4190+ l_div_mat = dshape_shape(u_shape%dn,d_shape,D_shape%quadrature%weight)
4191+ end if
4192 do dim1 = 1, mdim
4193 !pressure gradient term [integrated by parts so minus sign]
4194 local_solver_matrix(u_start(dim1):u_end(dim1),d_start:d_end)=&
4195- & -g*l_dt*theta*l_div_mat(dim1,:,:)
4196+ & -g*l_dt*l_theta*weight*l_div_mat(dim1,:,:)
4197 if(present(local_solver_rhs)) then
4198 local_solver_rhs(u_start(dim1):u_end(dim1),d_start:d_end)=&
4199- & -g*(theta-1.0)*l_dt*l_div_mat(dim1,:,:)
4200+ & -g*(l_theta-1.0)*l_dt*weight*l_div_mat(dim1,:,:)
4201 end if
4202 !divergence continuity term
4203 local_solver_matrix(d_start:d_end,u_start(dim1):u_end(dim1))=&
4204- & d0*l_dt*theta*transpose(l_div_mat(dim1,:,:))
4205+ & l_d0*l_dt*l_theta*weight*transpose(l_div_mat(dim1,:,:))
4206 if(present(local_solver_rhs)) then
4207 local_solver_rhs(d_start:d_end,u_start(dim1):u_end(dim1))=&
4208- & d0*(theta-1.0)*l_dt*transpose(l_div_mat(dim1,:,:))
4209+ & l_d0*(l_theta-1.0)*l_dt*weight*transpose(l_div_mat(dim1,:,:))
4210 end if
4211 end do
4212 !velocity mass matrix and Coriolis matrix (done in local coordinates)
4213 l_u_mat = shape_shape_tensor(u_shape, u_shape, &
4214- u_shape%quadrature%weight, Metric+l_dt*theta*Metricf)
4215+ u_shape%quadrature%weight, Metric+l_dt*l_theta*Metricf)
4216+
4217 do dim1 = 1, mdim
4218 do dim2 = 1, mdim
4219 local_solver_matrix(u_start(dim1):u_end(dim1),&
4220 u_start(dim2):u_end(dim2))=&
4221- & l_u_mat(dim1,dim2,:,:)
4222+ & weight**2*l_u_mat(dim1,dim2,:,:)
4223 end do
4224 end do
4225
4226 if(present(local_solver_rhs)) then
4227 l_u_mat = shape_shape_tensor(u_shape, u_shape, &
4228 u_shape%quadrature%weight, &
4229- Metric+(theta-1.0)*l_dt*Metricf)
4230+ Metric+(l_theta-1.0)*l_dt*Metricf)
4231 do dim1 = 1, mdim
4232 do dim2 = 1, mdim
4233 local_solver_rhs(u_start(dim1):u_end(dim1),&
4234 u_start(dim2):u_end(dim2))=&
4235- & l_u_mat(dim1,dim2,:,:)
4236+ & weight**2*l_u_mat(dim1,dim2,:,:)
4237 end do
4238 end do
4239 end if
4240@@ -708,52 +1225,6 @@
4241
4242 end subroutine get_local_solver
4243
4244- subroutine get_up_gi(X,ele,up_gi,orientation)
4245- !subroutine to replace up_gi with a normal to the surface
4246- !with the same orientation
4247- implicit none
4248- type(vector_field), intent(in) :: X
4249- integer, intent(in) :: ele
4250- real, dimension(X%dim,ele_ngi(X,ele)), intent(inout) :: up_gi
4251- integer, intent(out), optional :: orientation
4252- !
4253- real, dimension(mesh_dim(X), X%dim, ele_ngi(X,ele)) :: J
4254- integer :: gi
4255- real, dimension(X%dim,ele_ngi(X,ele)) :: normal_gi
4256- real, dimension(ele_ngi(X,ele)) :: orientation_gi
4257- real :: norm
4258-
4259- call compute_jacobian(ele_val(X,ele), ele_shape(X,ele), J=J)
4260-
4261- select case(mesh_dim(X))
4262- case (2)
4263- !Coriolis only makes sense for 2d surfaces embedded in 3d
4264- do gi = 1, ele_ngi(X,ele)
4265- normal_gi(:,gi) = cross_product(J(1,:,gi),J(2,:,gi))
4266- norm = sqrt(sum(normal_gi(:,gi)**2))
4267- normal_gi(:,gi) = normal_gi(:,gi)/norm
4268- end do
4269- do gi = 1, ele_ngi(X,ele)
4270- orientation_gi(gi) = dot_product(normal_gi(:,gi),up_gi(:,gi))
4271- end do
4272- if(any(abs(orientation_gi-orientation_gi(1))>1.0e-8)) then
4273- FLAbort('Nasty geometry problem')
4274- end if
4275- do gi = 1, ele_ngi(X,ele)
4276- up_gi(:,gi) = normal_gi(:,gi)*orientation_gi(gi)
4277- end do
4278- if(present(orientation)) then
4279- if(orientation_gi(1)>0.0) then
4280- orientation = 1
4281- else
4282- orientation = -1
4283- end if
4284- end if
4285- case default
4286- FLAbort('not implemented')
4287- end select
4288- end subroutine get_up_gi
4289-
4290 function get_orientation(X_val, up) result (orientation)
4291 !function compares the orientation of the element with the
4292 !up direction
4293@@ -853,73 +1324,6 @@
4294
4295 end subroutine get_scalar_continuity_face_mat
4296
4297- subroutine get_local_normal(norm,weight,U,face)
4298- !Function returns normal to face on local 2D element
4299- implicit none
4300- type(vector_field), intent(in) :: U
4301- integer, intent(in) :: face
4302- real, dimension(U%dim, face_ngi(U,face)), intent(out) :: norm
4303- real, intent(out) :: weight
4304-
4305- integer :: i
4306-
4307- select case(U%mesh%shape%numbering%family)
4308- case (FAMILY_SIMPLEX)
4309- if(U%dim==1) then
4310- if(face==1) then
4311- forall(i=1:face_ngi(U,face)) norm(1,i)=1.
4312- else if(face==2) then
4313- forall(i=1:face_ngi(U,face)) norm(1,i)=-1.
4314- else
4315- FLAbort('Funny face?')
4316- end if
4317- weight = 1.0
4318-
4319- else if(U%dim==2) then
4320- if(face==1) then
4321- forall(i=1:face_ngi(U,face)) norm(1:2,i)=(/-1.,0./)
4322- else if(face==2) then
4323- forall(i=1:face_ngi(U,face)) norm(1:2,i)=(/0.,-1./)
4324- else if(face==3) then
4325- forall(i=1:face_ngi(U,face)) norm(1:2,i)=(/1/sqrt(2.),1&
4326- &/sqrt(2.)/)
4327- else
4328- FLAbort('Funny face?')
4329- end if
4330-
4331- !Integral is taken on one of the edges of the local 2D element
4332- !This edge must be transformed to the local 1D element
4333- !to do numerical integration, with the following weight factors
4334- if(face==3) then
4335- weight = sqrt(2.)
4336- else
4337- weight = 1.0
4338- end if
4339-
4340- else
4341- FLAbort('Dimension not supported.')
4342- end if
4343- case (FAMILY_CUBE)
4344- if(U%dim==2) then
4345- if(face==1) then
4346- forall(i=1:face_ngi(U,face)) norm(1:2,i)=(/-1.,0./)
4347- else if(face==2) then
4348- forall(i=1:face_ngi(U,face)) norm(1:2,i)=(/ 1.,0./)
4349- else if(face==3) then
4350- forall(i=1:face_ngi(U,face)) norm(1:2,i)=(/0.,-1./)
4351- else if(face==4) then
4352- forall(i=1:face_ngi(U,face)) norm(1:2,i)=(/0.,1./)
4353- else
4354- FLAbort('Funny face?')
4355- end if
4356- weight = 1.0
4357- else
4358- FLAbort('Dimension not supported.')
4359- end if
4360- end select
4361-
4362- end subroutine get_local_normal
4363-
4364 subroutine compute_cartesian_ele(U_cart,U,X,ele)
4365 implicit none
4366 type(vector_field), intent(inout) :: U_cart
4367@@ -935,20 +1339,20 @@
4368 integer, dimension(mesh_dim(U)) :: U_start, U_end
4369 integer, dimension(:), pointer :: U_ele
4370 integer :: mdim, uloc, gi
4371- real, dimension(ele_ngi(U,ele)) :: detwei, detJ
4372+ real, dimension(ele_ngi(U,ele)) :: detwei, detJ
4373 real, dimension(mesh_dim(U), X%dim, ele_ngi(U,ele)) :: J
4374
4375- mdim = mesh_dim(U)
4376- uloc = ele_loc(U,ele)
4377- do dim1 = 1, mdim
4378- u_start(dim1) = uloc*(dim1-1)+1
4379- u_end(dim1) = uloc+dim1
4380+ mdim = mesh_dim(U)
4381+ uloc = ele_loc(U,ele)
4382+ do dim1 = 1, mdim
4383+ u_start(dim1) = uloc*(dim1-1)+1
4384+ u_end(dim1) = uloc+dim1
4385 end do
4386
4387 U_ele => ele_nodes(U, ele)
4388
4389- u_shape=ele_shape(u, ele)
4390- call compute_jacobian(ele_val(X,ele), ele_shape(X,ele), J=J, &
4391+ u_shape=ele_shape(u, ele)
4392+ call compute_jacobian(ele_val(X,ele),ele_shape(X,ele), J=J, &
4393 detwei=detwei,detJ=detJ)
4394
4395 local_u_gi = ele_val_at_quad(U,ele)
4396@@ -1002,15 +1406,16 @@
4397 u1 = face_val_at_quad(U,face)
4398 u2 = face_val_at_quad(U,face2)
4399 jump = maxval(abs(sum(u1*n1+u2*n2,1)))
4400- ewrite(1,*) jump
4401+ ewrite(2,*) jump
4402 assert(jump<1.0e-8)
4403
4404 end subroutine check_continuity_local_face
4405
4406- subroutine check_continuity_ele(U_cart,X,ele)
4407+ subroutine check_continuity_ele(U_cart,X,ele,tolerance)
4408 implicit none
4409 type(vector_field), intent(in) :: U_cart,X
4410 integer, intent(in) :: ele
4411+ real, intent(in) :: tolerance
4412 !
4413 integer, dimension(:), pointer :: neigh
4414 integer :: ni,face,ele2,face2
4415@@ -1024,18 +1429,20 @@
4416 else
4417 face2 = -1
4418 end if
4419- call check_continuity_face(U_cart,X,ele,ele2,face,face2)
4420+ call check_continuity_face(U_cart,X,ele,ele2,face,face2,tolerance)
4421 end do
4422 end subroutine check_continuity_ele
4423
4424- subroutine check_continuity_face(U_cart,X,ele,ele2,face,face2)
4425+ subroutine check_continuity_face(U_cart,X,ele,ele2,face,face2,tolerance)
4426 !subroutine to check the continuity of normal component
4427 !of velocity at quadrature points
4428 implicit none
4429 type(vector_field), intent(in) :: U_cart,X
4430 integer, intent(in) :: face,face2,ele,ele2
4431+ real, intent(in) :: tolerance
4432+ !
4433 real, dimension(X%dim, face_ngi(U_cart, face)) :: n1,n2
4434- real, dimension(X%dim, face_ngi(U_cart, face)) :: u1,u2,x1
4435+ real, dimension(X%dim, face_ngi(U_cart, face)) :: u1,u2,x1,x2
4436 real, dimension(face_ngi(U_cart, face)) :: jump_at_quad
4437 integer :: dim1
4438 !
4439@@ -1043,6 +1450,13 @@
4440 x1 = face_val_at_quad(X,face)
4441 if(ele2>0) then
4442 u2 = face_val_at_quad(U_cart,face2)
4443+ x2 = face_val_at_quad(X,face)
4444+ if(any(x1.ne.x2)) then
4445+ ewrite(0,*) 'Face 1 X', x1
4446+ ewrite(0,*) 'Face 2 X', x2
4447+ FLExit('Something wrong with mesh?')
4448+ end if
4449+
4450 else
4451 u2 = 0.
4452 end if
4453@@ -1054,98 +1468,22 @@
4454 n2 = -n1
4455 end if
4456 jump_at_quad = sum(n1*u1+n2*u2,1)
4457- if(maxval(abs(jump_at_quad))>1.0e-8) then
4458- ewrite(1,*) 'Jump at quadrature face, face2 =', jump_at_quad
4459- ewrite(1,*) 'ELE = ',ele,ele2
4460+ if(maxval(abs(jump_at_quad))>tolerance) then
4461+ ewrite(2,*) 'Jump at quadrature face, face2 =', jump_at_quad
4462+ ewrite(2,*) 'ELE = ',ele,ele2
4463 do dim1 = 1, X%dim
4464- ewrite(1,*) 'normal',dim1,n1(dim1,:)
4465- ewrite(1,*) 'normal',dim1,n2(dim1,:)
4466- ewrite(1,*) 'X',dim1,x1(dim1,:)
4467+ ewrite(2,*) 'normal',dim1,n1(dim1,:)
4468+ ewrite(2,*) 'normal',dim1,n2(dim1,:)
4469+ ewrite(2,*) 'X',dim1,x1(dim1,:)
4470 end do
4471- ewrite(1,*) 'n cpt1',sum(n1*u1,1)
4472- ewrite(1,*) 'n cpt2',sum(n2*u2,1)
4473- ewrite(1,*) jump_at_quad/max(maxval(abs(u1)),maxval(abs(u2)))
4474+ ewrite(2,*) 'n cpt1',sum(n1*u1,1)
4475+ ewrite(2,*) 'n cpt2',sum(n2*u2,1)
4476+ ewrite(2,*) jump_at_quad/max(maxval(abs(u1)),maxval(abs(u2)))
4477 FLAbort('stopping because of jumps')
4478 end if
4479
4480 end subroutine check_continuity_face
4481
4482- function get_face_normal_manifold(X,ele,face) result (normal)
4483- implicit none
4484- type(vector_field), intent(in) :: X
4485- integer, intent(in) :: ele, face
4486- real, dimension(X%dim,face_ngi(X,face)) :: normal
4487- !
4488- real, dimension(ele_ngi(X,ele)) :: detwei
4489- real, dimension(mesh_dim(X), X%dim, ele_ngi(X,ele)) :: J
4490- real, dimension(face_ngi(X,face)) :: detwei_f
4491- real, dimension(mesh_dim(X)-1, X%dim, face_ngi(X,face)) :: J_f
4492- real, dimension(ele_loc(X,ele),ele_loc(X,ele)) :: X_mass_mat
4493- real, dimension(mesh_dim(X), X%dim, ele_loc(X,ele)) :: J_loc
4494- real, dimension(ele_loc(X,ele)) :: J_loc_rhs
4495- real, dimension(mesh_dim(X), X%dim, face_ngi(X,face)) :: J_face_gi
4496- integer :: dim1, dim2, gi
4497- real, dimension(X%dim) :: ele_normal_gi, edge_tangent_gi, X_mid_ele,&
4498- &X_mid_face
4499- type(element_type) :: X_shape, X_face_shape
4500- real, dimension(X%dim,ele_loc(X,ele)) :: X_ele
4501- real, dimension(X%dim,face_loc(X,face)) :: X_face
4502-
4503- call compute_jacobian(ele_val(X,ele),ele_shape(X,ele), J=J, &
4504- detwei=detwei)
4505- call compute_jacobian(face_val(X,face),face_Shape(X,face), J=J_f, &
4506- detwei=detwei_f)
4507-
4508- !Jacobian can be expanded without error in X function space
4509- !so we map it to the basis function DOFs by projection
4510- X_shape = ele_shape(X,ele)
4511- X_face_shape = face_shape(X,face)
4512- X_mass_mat = shape_shape(X_shape,X_shape,detwei)
4513- do dim1 = 1, mesh_dim(X)
4514- do dim2 = 1, X%dim
4515- J_loc_rhs = shape_rhs(X_shape,J(dim1,dim2,:)*detwei)
4516- call solve(X_mass_mat,J_loc_rhs)
4517- J_loc(dim1,dim2,:) = J_loc_rhs
4518- end do
4519- end do
4520-
4521- do dim1 = 1, mesh_dim(X)
4522- do dim2 = 1, X%dim
4523- J_face_gi(dim1,dim2,:) = &
4524- & matmul(transpose(X_face_shape%n),&
4525- J_loc(dim1,dim2,face_local_nodes(X,face)))
4526- end do
4527- end do
4528-
4529- X_mid_ele = sum(ele_val(X,ele),2)/size(ele_val(X,ele),2)
4530- X_mid_face = sum(face_val(X,face),2)/size(face_val(X,face),2)
4531- select case(X%dim)
4532- case (3)
4533- select case (mesh_dim(X))
4534- case (2)
4535- do gi = 1, face_ngi(X,face)
4536- !Get normal to element e on face quad points
4537- ele_normal_gi = cross_product(J_face_gi(1,:,gi),&
4538- &J_face_gi(2,:,gi))
4539- ele_normal_gi = ele_normal_gi/(norm2(ele_normal_gi))
4540- !Get tangent to face f
4541- edge_tangent_gi = J_f(1,:,gi)
4542- edge_tangent_gi = edge_tangent_gi/norm2(edge_tangent_gi)
4543- !Compute normal to face f in manifold
4544- normal(:,gi) = cross_product(ele_normal_gi,edge_tangent_gi)
4545- if(dot_product(normal(:,gi),X_mid_face-X_mid_ele)<0) then
4546- normal(:,gi)=-normal(:,gi)
4547- end if
4548- end do
4549- case default
4550- FLAbort('dimension combination not implemented')
4551- end select
4552- case default
4553- FLAbort('dimension combination not implemented')
4554- end select
4555-
4556- end function get_face_normal_manifold
4557-
4558 subroutine reconstruct_lambda_nc(lambda,lambda_nc,X,ele)
4559 type(scalar_field), intent(in) :: lambda
4560 type(scalar_field), intent(inout) :: lambda_nc
4561@@ -1203,9 +1541,12 @@
4562 &shape_shape(lambda_nc_face_shape,lambda_nc_face_shape,detwei)
4563 end subroutine get_nc_rhs_face
4564
4565- subroutine assemble_rhs_ele(Rhs_loc,D,U,X,ele,D_rhs,U_rhs,u_rhs_local)
4566+ subroutine assemble_rhs_ele(Rhs_loc,D,U,X,ele,pullback,&
4567+ weight,D_rhs,U_rhs,u_rhs_local)
4568 implicit none
4569 integer, intent(in) :: ele
4570+ real, intent(in) :: weight
4571+ logical, intent(in) :: pullback
4572 type(scalar_field), intent(in), optional, target :: D_rhs
4573 type(vector_field), intent(in), optional, target :: U_rhs
4574 type(vector_field), intent(in) :: X,U
4575@@ -1226,6 +1567,7 @@
4576 type(scalar_field), pointer :: l_d_rhs
4577 type(vector_field), pointer :: l_u_rhs
4578 real, dimension(mesh_dim(U), mesh_dim(U)) :: Metric
4579+ real :: detJ_bar
4580
4581 !Get some sizes
4582 mdim = mesh_dim(U)
4583@@ -1245,6 +1587,7 @@
4584
4585 if(.not.(present(D_rhs).or.present(u_rhs))) then
4586 !We are in timestepping mode.
4587+ !This will be multiplied by the local_solver_rhs matrix later.
4588 rhs_loc(d_start:d_end) = ele_val(D,ele)
4589 u_rhs_loc = ele_val(U,ele)
4590 do dim1 = 1, mdim
4591@@ -1259,27 +1602,38 @@
4592
4593 call compute_jacobian(ele_val(X,ele), ele_shape(X,ele), J=J, &
4594 detJ=detJ,detwei=detwei)
4595+ detJ_bar = sum(detJ)/size(detJ)
4596
4597 Rhs_loc = 0.
4598 if(have_d_rhs) then
4599- Rhs_loc(d_start:d_end) = shape_rhs(ele_shape(D,ele),&
4600- &ele_val_at_quad(l_D_rhs,ele)*detwei)
4601+ if(pullback) then
4602+ Rhs_loc(d_start:d_end) = shape_rhs(ele_shape(D,ele),&
4603+ &ele_val_at_quad(l_D_rhs,ele)*&
4604+ &detJ_bar**2/detJ*u_shape%quadrature%weight)
4605+ else
4606+ Rhs_loc(d_start:d_end) = shape_rhs(ele_shape(D,ele),&
4607+ &ele_val_at_quad(l_D_rhs,ele)*detwei)
4608+ end if
4609 end if
4610 if(have_u_rhs) then
4611 if(present_and_true(u_rhs_local)) then
4612+ !Using local U, multiply by weight**2
4613 u_local_quad = ele_val_at_quad(l_u_rhs,ele)
4614 do gi=1,ele_ngi(U,ele)
4615 Metric=matmul(J(:,:,gi), transpose(J(:,:,gi)))&
4616 &/detJ(gi)
4617- u_local_quad(:,gi) = matmul(Metric,u_local_quad(:,gi))
4618+ u_local_quad(:,gi) = matmul(Metric,u_local_quad(:,gi))&
4619+ &*weight**2
4620 end do
4621 else
4622+ !Using cartesian U, multiply by weight
4623 allocate(u_cart_quad(l_U_rhs%dim,ele_ngi(X,ele)))
4624 u_cart_quad = ele_val_at_quad(l_u_rhs,ele)
4625 do gi = 1, ele_ngi(D,ele)
4626- !Don't divide by detJ as we can use weight instead of detwei
4627+ !Don't divide by detJ as we can use quadrature weight
4628+ !instead of detwei
4629 u_local_quad(:,gi) = matmul(J(:,:,gi)&
4630- &,u_cart_quad(:,gi))
4631+ &,u_cart_quad(:,gi))*weight
4632 end do
4633 end if
4634 U_rhs_loc = shape_vector_rhs(u_shape,&
4635@@ -1361,8 +1715,8 @@
4636 call compute_energy_ele(energy,U,D,X,D0,g,ele)
4637 end do
4638
4639- ewrite(1,*) 'Energy:= ', energy
4640- ewrite(1,*) 'Change in energy:= ', energy-old_energy
4641+ ewrite(2,*) 'Energy:= ', energy
4642+ ewrite(2,*) 'Percentage Change in energy:= ', (energy-old_energy)/energy
4643
4644 end subroutine compute_energy_hybridized
4645
4646@@ -1414,12 +1768,14 @@
4647 type(state_type), intent(inout) :: state
4648 !
4649 type(scalar_field), pointer :: D,psi,f
4650- type(scalar_field) :: D_rhs
4651+ type(scalar_field) :: D_rhs,tmp_field
4652 type(vector_field), pointer :: U_local,down,X, U_cart
4653- type(vector_field) :: Coriolis_term, Balance_eqn, tmp_field
4654- integer :: ele,dim1
4655- real :: g
4656- logical :: elliptic_method
4657+ type(vector_field) :: Coriolis_term, Balance_eqn, tmpV_field
4658+ integer :: ele,dim1,i1, stat
4659+ real :: g, D0
4660+ logical :: elliptic_method, pullback
4661+ real :: u_max, b_val, h_mean, area
4662+ real, dimension(:), allocatable :: weights
4663
4664 D=>extract_scalar_field(state, "LayerThickness")
4665 psi=>extract_scalar_field(state, "Streamfunction")
4666@@ -1429,133 +1785,135 @@
4667 X=>extract_vector_field(state, "Coordinate")
4668 down=>extract_vector_field(state, "GravityDirection")
4669 call get_option("/physical_parameters/gravity/magnitude", g)
4670- call allocate(tmp_field,mesh_dim(U_local), U_local%mesh, "tmp_field")
4671- call allocate(D_rhs,D%mesh,'BalancedSolverRHS')
4672+ call allocate(tmpV_field,mesh_dim(U_local), U_local%mesh, "tmpV_field")
4673+ call allocate(D_rhs,D%mesh,'BalancedSolverRHS')
4674+ call allocate(tmp_field,D%mesh,'TmpField')
4675 call allocate(Coriolis_term,mesh_dim(U_local),&
4676 U_local%mesh,"CoriolisTerm")
4677 call allocate(balance_eqn,mesh_dim(D),u_local%mesh,'BalancedEquation')
4678
4679+ !compute rescaling weights for local coordinates
4680+ allocate(weights(element_count(X)))
4681+ weights = 1.0
4682+ !call get_weights(X,weights)
4683+
4684 !STAGE 1: Set velocity from streamfunction
4685 do ele = 1, element_count(D)
4686 call set_local_velocity_from_streamfunction_ele(&
4687- &U_local,psi,down,X,ele)
4688+ &U_local,psi,down,X,ele,weights(ele))
4689 end do
4690
4691 !STAGE 1a: verify that velocity projects is div-conforming
4692- call project_local_to_cartesian(X,U_local,U_cart)
4693- do ele = 1, ele_count(U_local)
4694- call check_continuity_ele(U_cart,X,ele)
4695- end do
4696-
4697+ !call project_local_to_cartesian(X,U_local,U_cart,weights=weights)
4698+ !do ele = 1, ele_count(U_local)
4699+ ! call check_continuity_ele(U_cart,X,ele,tolerance=1.0e-8)
4700+ !end do
4701 !Stage 1b: verify that projection is idempotent
4702- ewrite(1,*) 'CHECKING CONTINUOUS', maxval(abs(u_local%val))
4703- call solve_hybridized_helmholtz(state,U_Rhs=U_local,&
4704- &U_out=tmp_field,&
4705- &compute_cartesian=.true.,&
4706- &check_continuity=.true.,projection=.true.,&
4707- &u_rhs_local=.true.)!verified that projection is idempotent
4708- assert(maxval(abs(U_local%val-tmp_field%val))<1.0e-8)
4709-
4710- elliptic_method = .false.
4711-
4712- if(elliptic_method) then
4713-
4714- !STAGE 2: Construct Coriolis term
4715+ ewrite(2,*) 'CHECKING CONTINUOUS', maxval(abs(u_local%val))
4716+ tmpV_field%val = U_local%val
4717+ call project_to_constrained_space(state,tmpV_field)
4718+ ewrite(2,*) maxval(abs(U_local%val-tmpV_field%val)), 'continuity'
4719+ u_max = 0.
4720+ do dim1 = 1, mesh_dim(D)
4721+ u_max = max(u_max,maxval(abs(U_local%val)))
4722+ end do
4723+ assert(maxval(abs(U_local%val-tmpV_field%val)/max(1.0,u_max))<1.0e-8)
4724+
4725+ if(have_option("/material_phase::Fluid/vector_field::Velocity/prognostic&
4726+ &/initial_condition::WholeMesh/balanced/elliptic_solver")) then
4727+ ewrite(2,*) 'Using elliptic solver'
4728+ !Construct Coriolis term
4729 call zero(Coriolis_term)
4730 do ele = 1, element_count(D)
4731+ !weights not needed since both sides of equation are multiplied
4732+ !by weight(ele)^2 in each element
4733 call set_coriolis_term_ele(Coriolis_term,f,down,U_local,X,ele)
4734- end do!checked!signs checked
4735-
4736- !STAGE 3: Project Coriolis term into div-conforming space
4737-
4738- !debugging bits - checking if it works with cartesian instead
4739- call project_local_to_cartesian(X,Coriolis_Term,U_cart)
4740- call solve_hybridized_helmholtz(state,U_Rhs=U_cart,&
4741- &U_out=tmp_field,&
4742- &compute_cartesian=.true.,output_dense=.false.,&
4743- &check_continuity=.true.,projection=.true.,&
4744- &u_rhs_local=.false.)
4745-
4746- ewrite(0,*) 'REMEMBER TO REMOVE DEBUGGING TESTS'
4747- call solve_hybridized_helmholtz(state,U_Rhs=Coriolis_term,&
4748- &U_out=tmp_field,&
4749- &compute_cartesian=.true.,&
4750- &check_continuity=.true.,projection=.true.,&
4751- &u_rhs_local=.true.)!verified that projection is idempotent
4752-
4753- !STAGE 4: Construct the RHS for the balanced layer depth equation
4754- call zero(D_rhs)
4755- do ele = 1, element_count(D)
4756- call set_geostrophic_balance_rhs_ele(D_rhs,Coriolis_term,ele)
4757- end do
4758-
4759- !STAGE 5: Solve Poisson equation for the balanced layer depth
4760- ewrite(0,*) 'REMEMBER ABOUT SETTING MEAN VALUE'
4761- ewrite(0,*) trim(u_cart%option_path)
4762-
4763- call solve_hybridized_helmholtz(state,D_rhs=D_rhs,&
4764- &compute_cartesian=.false.,&
4765- &check_continuity=.false.,Poisson=.true.,&
4766- &solver_option_path=trim(u_cart%option_path)//'/prognostic/initial_condition::WholeMesh/balanced')
4767-
4768- !STAGE 6: Check if we have a balanced solution
4769- !Can be done by projecting balance equation into div-conforming space
4770- !and checking that it is equal to zero
4771- !STAGE 6a: Project balance equation into DG space
4772- do ele = 1, element_count(D)
4773- call set_pressure_force_ele(balance_eqn,D,X,g,ele)
4774- end do
4775- call addto(balance_eqn,coriolis_term)
4776-
4777- !STAGE 6b: Project balance equation into div-conforming space
4778- call solve_hybridized_helmholtz(state,U_Rhs=balance_eqn,&
4779- &U_out=balance_eqn,&
4780- &compute_cartesian=.true.,&
4781- &check_continuity=.true.,projection=.true.,&
4782- &u_rhs_local=.true.)
4783-
4784- do dim1 = 1, mesh_dim(D)
4785- ewrite(1,*) 'Balance equation', maxval(abs(balance_eqn%val(dim1,:)))
4786- assert(maxval(abs(balance_eqn%val(dim1,:)))<1.0e-8)
4787- end do
4788-
4789+ end do
4790+
4791+ !Project Coriolis term into div-conforming space
4792+ call project_to_constrained_space(state,Coriolis_term)
4793+
4794+ !Calculate divergence of Coriolis term
4795+ call zero(d_rhs)
4796+
4797+ pullback = have_option('/material_phase::Fluid/scalar_field::LayerThickn&
4798+ &ess/prognostic/spatial_discretisation/discontinuous_galerkin/wave_&
4799+ &equation/pullback')
4800+
4801+ do ele = 1, element_count(D)
4802+ call compute_divergence_ele(Coriolis_term,d_rhs,X,ele,pullback)
4803+ end do
4804+
4805+ !Solve for balanced layer depth
4806+
4807+ ewrite(2,*) 'Solving elliptic problem for balanced layer depth.'
4808+ call solve_hybridized_helmholtz(state,d_Rhs=d_rhs,&
4809+ &U_out=tmpV_field,d_out=tmp_field,&
4810+ &compute_cartesian=.true.,&
4811+ &poisson=.true.,u_rhs_local=.true.)
4812+ D%val = tmp_field%val
4813 else
4814+ ewrite(2,*) 'Using streamfunction projection'
4815 !Project the streamfunction into pressure space
4816 do ele = 1, element_count(D)
4817 call project_streamfunction_for_balance_ele(D,psi,X,f,g,ele)
4818 end do
4819- ewrite(1,*) maxval(abs(D%val))
4820-
4821- !debugging tests
4822- call zero(Coriolis_term)
4823- do ele = 1, element_count(D)
4824- call set_coriolis_term_ele(Coriolis_term,f,down,U_local,X,ele)
4825- end do
4826- call zero(balance_eqn)
4827- do ele = 1, element_count(D)
4828- call set_pressure_force_ele(balance_eqn,D,X,g,ele)
4829- end do
4830- call addto(balance_eqn,coriolis_term,scale=1.0)
4831- ewrite(1,*) 'CJC b4',maxval(abs(balance_eqn%val)),&
4832- & maxval(abs(coriolis_term%val))
4833- !Project balance equation into div-conforming space
4834- call solve_hybridized_helmholtz(state,U_Rhs=balance_eqn,&
4835- &U_out=balance_eqn,&
4836- &compute_cartesian=.true.,&
4837- &check_continuity=.true.,projection=.true.,&
4838- &u_rhs_local=.true.)
4839-
4840- do dim1 = 1, mesh_dim(D)
4841- ewrite(1,*) 'Balance equation', maxval(abs(balance_eqn%val(dim1,:)))
4842- assert(maxval(abs(balance_eqn%val(dim1,:)))<1.0e-8)
4843- end do
4844 end if
4845- !Clean up after yourself
4846+
4847+ !Subtract off the mean part
4848+ !Didn't bother with pullback
4849+ h_mean = 0.
4850+ area = 0.
4851+ do ele = 1, element_count(D)
4852+ call assemble_mean_ele(D,X,h_mean,area,ele)
4853+ end do
4854+ h_mean = h_mean/area
4855+ D%val = D%val - h_mean
4856+ !Add back on the correct mean depth
4857+ call get_option("/material_phase::Fluid/scalar_field::LayerThickness/&
4858+ &prognostic/mean_layer_thickness",D0)
4859+ D%val = D%val + D0
4860+
4861+ !debugging tests
4862+ call zero(Coriolis_term)
4863+ do ele = 1, element_count(D)
4864+ !weights not needed since both sides of equation are multiplied
4865+ !by weight(ele)^2 in each element
4866+ call set_coriolis_term_ele(Coriolis_term,f,down,U_local,X,ele)
4867+ end do
4868+
4869+ call zero(balance_eqn)
4870+ do ele = 1, element_count(D)
4871+ call set_pressure_force_ele(balance_eqn,D,X,g,ele,weights(ele),&
4872+ pullback)
4873+ end do
4874+ call addto(balance_eqn,coriolis_term,scale=1.0)
4875+ ewrite(2,*) 'Project balance equation into div-conforming space'
4876+ ewrite(2,*) 'CJC b4',maxval(abs(balance_eqn%val)),&
4877+ & maxval(abs(coriolis_term%val))
4878+ b_val = maxval(abs(balance_eqn%val))
4879+ !Project balance equation into div-conforming space
4880+ call solve_hybridized_helmholtz(state,U_Rhs=balance_eqn,&
4881+ &U_out=balance_eqn,&
4882+ &compute_cartesian=.true.,&
4883+ &projection=.true.,&
4884+ &poisson=.false.,&
4885+ &u_rhs_local=.true.)
4886+
4887+ do dim1 = 1, mesh_dim(D)
4888+ ewrite(2,*) 'Balance equation', maxval(abs(balance_eqn%val(dim1,:)))/b_val
4889+ !assert(maxval(abs(balance_eqn%val(dim1,:)))/b_val<1.0e-8)
4890+ end do
4891+
4892+ !Clean up after yourself
4893 call deallocate(Coriolis_term)
4894 call deallocate(D_rhs)
4895 call deallocate(balance_eqn)
4896+ call deallocate(tmpV_field)
4897 call deallocate(tmp_field)
4898
4899+ deallocate(weights)
4900+
4901 end subroutine set_velocity_from_geostrophic_balance_hybridized
4902
4903 subroutine project_streamfunction_for_balance_ele(D,psi,X,f,g,ele)
4904@@ -1587,19 +1945,18 @@
4905
4906 end subroutine project_streamfunction_for_balance_ele
4907
4908- subroutine set_pressure_force_ele(force,D,X,g,ele)
4909+ subroutine set_pressure_force_ele(force,D,X,g,ele,weight,pullback)
4910 implicit none
4911 type(vector_field), intent(inout) :: force
4912 type(scalar_field), intent(in) :: D
4913 type(vector_field), intent(in) :: X
4914- real, intent(in) :: g
4915+ real, intent(in) :: g, weight
4916 integer, intent(in) :: ele
4917+ logical, intent(in) :: pullback
4918 !
4919 real, dimension(ele_ngi(D,ele)) :: D_gi
4920 real, dimension(mesh_dim(D),ele_loc(force,ele)) :: &
4921 & rhs_loc
4922- real, dimension(ele_loc(force,ele),ele_loc(force,ele)) :: &
4923- & l_mass_mat
4924 integer :: dim1,dim2,gi,uloc
4925 real, dimension(mesh_dim(force), X%dim, ele_ngi(force,ele)) :: J
4926 real, dimension(ele_ngi(force,ele)) :: detJ
4927@@ -1609,6 +1966,7 @@
4928 mesh_dim(force)*ele_loc(force,ele)) :: l_u_mat
4929 real, dimension(mesh_dim(force)*ele_loc(force,ele)) :: force_rhs
4930 type(element_type) :: force_shape
4931+ real :: detJ_bar
4932
4933 uloc = ele_loc(force,ele)
4934 force_shape = ele_shape(force,ele)
4935@@ -1618,11 +1976,14 @@
4936 Metric(:,:,gi)=matmul(J(:,:,gi), transpose(J(:,:,gi)))/detJ(gi)
4937 end do
4938
4939+ detJ_bar = sum(detJ)/size(detJ)
4940+
4941 D_gi = ele_val_at_quad(D,ele)
4942+ if(pullback) then
4943+ D_gi = D_gi*detJ_bar/detJ
4944+ end if
4945 rhs_loc = -g*dshape_rhs(force%mesh%shape%dn,&
4946 D_gi*D%mesh%shape%quadrature%weight)
4947- l_mass_mat = shape_shape(ele_shape(force,ele),ele_shape(force,ele),&
4948- &force%mesh%shape%quadrature%weight)
4949 do dim1 = 1, mesh_dim(force)
4950 force_rhs((dim1-1)*uloc+1:dim1*uloc) = rhs_loc(dim1,:)
4951 end do
4952@@ -1635,10 +1996,12 @@
4953 end do
4954 end do
4955
4956+ !LHS is multiplied by weights(ele)^2, RHS is multiplied by weights(ele)
4957+
4958 call solve(l_u_mat,force_rhs)
4959 do dim1= 1, mesh_dim(force)
4960 call set(force,dim1,ele_nodes(force,ele),&
4961- &force_rhs((dim1-1)*uloc+1:dim1*uloc))
4962+ &force_rhs((dim1-1)*uloc+1:dim1*uloc)/weight)
4963 end do
4964
4965 end subroutine set_pressure_force_ele
4966@@ -1709,7 +2072,6 @@
4967 up_gi = -ele_val_at_quad(down,ele)
4968
4969 call get_up_gi(X,ele,up_gi)
4970-
4971 coriolis_rhs = 0.
4972 l_u_mat = 0.
4973 !metrics for velocity mass and coriolis matrices
4974@@ -1747,15 +2109,16 @@
4975 end subroutine set_coriolis_term_ele
4976
4977 subroutine set_local_velocity_from_streamfunction_ele(&
4978- &U_local,psi,down,X,ele)
4979+ &U_local,psi,down,X,ele,weight)
4980 implicit none
4981 type(vector_field), intent(inout) :: U_local
4982 type(vector_field), intent(in) :: down,X
4983 type(scalar_field), intent(in) :: psi
4984 integer, intent(in) :: ele
4985+ real, intent(in) :: weight
4986 !
4987 real, dimension(ele_loc(psi,ele)) :: psi_loc
4988- real, dimension(mesh_dim(psi),ele_ngi(psi,ele)) :: dpsi_gi
4989+ real, dimension(mesh_dim(psi),ele_ngi(psi,ele)) :: dpsi_gi, Uloc_gi
4990 real, dimension(ele_ngi(psi,ele)) :: div_gi
4991 real, dimension(mesh_dim(U_local),ele_loc(U_local,ele)) :: U_loc
4992 real, dimension(ele_loc(U_local,ele),ele_loc(U_local,ele)) :: &
4993@@ -1764,6 +2127,8 @@
4994 integer :: dim1,gi,uloc
4995 real, dimension(X%dim, ele_ngi(X,ele)) :: up_gi
4996 integer :: orientation
4997+ real, dimension(mesh_dim(U_local), X%dim, ele_ngi(U_local,ele)) :: J
4998+ real, dimension(ele_ngi(X,ele)) :: detJ
4999
5000 uloc = ele_loc(U_local,ele)
The diff has been truncated for viewing.